clawdie-ai/setup/secrets.ts

194 lines
5.9 KiB
TypeScript

import { randomBytes } from 'crypto';
import fs from 'fs';
import {
defaultMemoryDbName,
defaultMemoryDbUser,
defaultSkillsDbName,
defaultSkillsDbUser,
} from '../src/db-identifiers.js';
import {
ensureEnvFile,
extractEnvValue,
writeEnvLine,
} from './profile.js';
export interface SplitBrainSecrets {
envFile: string;
agentName: string;
subnetBase: string;
dbHost: string;
skillsDbUser: string;
skillsDbName: string;
memoryDbUser: string;
memoryDbName: string;
postgresAdminPassword: string;
skillsDbPassword: string;
memoryDbPassword: string;
strapiDbPassword: string;
strapiAppKeys: string;
strapiApiTokenSalt: string;
strapiAdminJwtSecret: string;
strapiTransferTokenSalt: string;
strapiJwtSecret: string;
skillsDbUrl: string;
memoryDbUrl: string;
}
export interface ScreenshotSecrets {
envFile: string;
screenshotsUser: string;
screenshotsPassword: string;
userCreated: boolean;
passwordCreated: boolean;
}
function generateSecret(bytes = 24): string {
return randomBytes(bytes).toString('base64url');
}
function generateStrapiAppKeys(): string {
return Array.from({ length: 4 }, () => generateSecret(24)).join(',');
}
function currentEnvValue(content: string, key: string): string | null {
const value = extractEnvValue(content, key);
return value && value.trim() ? value.trim() : null;
}
function buildPostgresUrl(
user: string,
password: string,
host: string,
database: string,
): string {
return `postgresql://${encodeURIComponent(user)}:${encodeURIComponent(password)}@${host}:5432/${encodeURIComponent(database)}`;
}
function writeIfMissing(envFile: string, key: string, value: string): void {
const content = fs.readFileSync(envFile, 'utf-8');
if (!currentEnvValue(content, key)) {
writeEnvLine(envFile, key, value);
}
}
export function ensureSplitBrainSecrets(projectRoot: string): SplitBrainSecrets {
const envFile = ensureEnvFile(projectRoot);
let content = fs.readFileSync(envFile, 'utf-8');
const agentName = (
currentEnvValue(content, 'AGENT_NAME') || 'clawdie'
).toLowerCase();
const subnetBase =
currentEnvValue(content, 'AGENT_SUBNET_BASE') ||
currentEnvValue(content, 'WARDEN_SUBNET_BASE') ||
'10.0.0';
const dbHost =
currentEnvValue(content, 'WARDEN_DB_IP') ||
`${subnetBase}.3`;
const skillsDbUser =
currentEnvValue(content, 'SKILLS_DB_USER') || defaultSkillsDbUser(agentName);
const skillsDbName =
currentEnvValue(content, 'SKILLS_DB_NAME') || defaultSkillsDbName(agentName);
const memoryDbUser =
currentEnvValue(content, 'MEMORY_DB_USER') || defaultMemoryDbUser(agentName);
const memoryDbName =
currentEnvValue(content, 'MEMORY_DB_NAME') || defaultMemoryDbName(agentName);
writeIfMissing(envFile, 'POSTGRES_ADMIN_PASSWORD', generateSecret(24));
writeIfMissing(envFile, 'SKILLS_DB_PASSWORD', generateSecret(24));
writeIfMissing(envFile, 'MEMORY_DB_PASSWORD', generateSecret(24));
writeIfMissing(envFile, 'STRAPI_DB_PASSWORD', generateSecret(24));
writeIfMissing(envFile, 'STRAPI_APP_KEYS', generateStrapiAppKeys());
writeIfMissing(envFile, 'STRAPI_API_TOKEN_SALT', generateSecret(24));
writeIfMissing(envFile, 'STRAPI_ADMIN_JWT_SECRET', generateSecret(24));
writeIfMissing(envFile, 'STRAPI_TRANSFER_TOKEN_SALT', generateSecret(24));
writeIfMissing(envFile, 'STRAPI_JWT_SECRET', generateSecret(24));
content = fs.readFileSync(envFile, 'utf-8');
const postgresAdminPassword =
currentEnvValue(content, 'POSTGRES_ADMIN_PASSWORD') || generateSecret(24);
const skillsDbPassword =
currentEnvValue(content, 'SKILLS_DB_PASSWORD') || generateSecret(24);
const memoryDbPassword =
currentEnvValue(content, 'MEMORY_DB_PASSWORD') || generateSecret(24);
const strapiDbPassword =
currentEnvValue(content, 'STRAPI_DB_PASSWORD') || generateSecret(24);
const strapiAppKeys =
currentEnvValue(content, 'STRAPI_APP_KEYS') || generateStrapiAppKeys();
const strapiApiTokenSalt =
currentEnvValue(content, 'STRAPI_API_TOKEN_SALT') || generateSecret(24);
const strapiAdminJwtSecret =
currentEnvValue(content, 'STRAPI_ADMIN_JWT_SECRET') || generateSecret(24);
const strapiTransferTokenSalt =
currentEnvValue(content, 'STRAPI_TRANSFER_TOKEN_SALT') || generateSecret(24);
const strapiJwtSecret =
currentEnvValue(content, 'STRAPI_JWT_SECRET') || generateSecret(24);
const skillsDbUrl = buildPostgresUrl(
skillsDbUser,
skillsDbPassword,
dbHost,
skillsDbName,
);
const memoryDbUrl = buildPostgresUrl(
memoryDbUser,
memoryDbPassword,
dbHost,
memoryDbName,
);
writeEnvLine(envFile, 'SKILLS_DB_URL', skillsDbUrl);
writeEnvLine(envFile, 'MEMORY_DB_URL', memoryDbUrl);
return {
envFile,
agentName,
subnetBase,
dbHost,
skillsDbUser,
skillsDbName,
memoryDbUser,
memoryDbName,
postgresAdminPassword,
skillsDbPassword,
memoryDbPassword,
strapiDbPassword,
strapiAppKeys,
strapiApiTokenSalt,
strapiAdminJwtSecret,
strapiTransferTokenSalt,
strapiJwtSecret,
skillsDbUrl,
memoryDbUrl,
};
}
export function ensureScreenshotSecrets(projectRoot: string): ScreenshotSecrets {
const envFile = ensureEnvFile(projectRoot);
const content = fs.readFileSync(envFile, 'utf-8');
const agentName = (
currentEnvValue(content, 'AGENT_NAME') || 'clawdie'
).toLowerCase();
const existingUser = currentEnvValue(content, 'SCREENSHOTS_USER');
const existingPassword = currentEnvValue(content, 'SCREENSHOTS_PASSWORD');
const screenshotsUser = existingUser || agentName;
const screenshotsPassword = existingPassword || generateSecret(18);
if (!existingUser) {
writeEnvLine(envFile, 'SCREENSHOTS_USER', screenshotsUser);
}
if (!existingPassword) {
writeEnvLine(envFile, 'SCREENSHOTS_PASSWORD', screenshotsPassword);
}
return {
envFile,
screenshotsUser,
screenshotsPassword,
userCreated: !existingUser,
passwordCreated: !existingPassword,
};
}