()
| 40 | * Returns the set of fields that were populated, for logging. |
| 41 | */ |
| 42 | export function bootstrapFromEnv(): string[] { |
| 43 | const applied: string[] = []; |
| 44 | |
| 45 | const apiUrl = process.env.COREBRAIN_API_URL; |
| 46 | const apiKey = process.env.COREBRAIN_API_KEY; |
| 47 | const gwName = process.env.COREBRAIN_GATEWAY_NAME; |
| 48 | const gwDesc = process.env.COREBRAIN_GATEWAY_DESCRIPTION; |
| 49 | const defaultWorkspace = process.env.COREBRAIN_DEFAULT_WORKSPACE; |
| 50 | |
| 51 | // --- core.auth --- |
| 52 | // auth requires both url and apiKey, so we only persist when the merge |
| 53 | // would yield a complete pair (env-supplied or already on disk). |
| 54 | const config = getConfig(); |
| 55 | const mergedUrl = config.auth?.url ?? apiUrl; |
| 56 | const mergedApiKey = config.auth?.apiKey ?? apiKey; |
| 57 | const addedUrl = !config.auth?.url && !!apiUrl; |
| 58 | const addedApiKey = !config.auth?.apiKey && !!apiKey; |
| 59 | if ((addedUrl || addedApiKey) && mergedUrl && mergedApiKey) { |
| 60 | updateConfig({auth: {url: mergedUrl, apiKey: mergedApiKey}}); |
| 61 | if (addedUrl) applied.push('COREBRAIN_API_URL'); |
| 62 | if (addedApiKey) applied.push('COREBRAIN_API_KEY'); |
| 63 | } |
| 64 | |
| 65 | // --- preferences.gateway --- |
| 66 | const prefs = getPreferences(); |
| 67 | const existing = prefs.gateway ?? {pid: 0, startedAt: 0}; |
| 68 | |
| 69 | // Local id — internal key for the gateway record. Webapp assigns its own |
| 70 | // ID at registration time; this is just to satisfy the existing |
| 71 | // "configured" check in the start command. |
| 72 | const needsId = !existing.id; |
| 73 | const namePatch = gwName && !existing.name ? {name: gwName} : {}; |
| 74 | const descPatch = |
| 75 | gwDesc && !existing.description ? {description: gwDesc} : {}; |
| 76 | |
| 77 | // Security key. Without this every authed request returns 401, so make |
| 78 | // sure a fresh container always boots with one. Three cases: |
| 79 | // 1) Env var supplied → use it (user controls the value via Railway/Fly env). |
| 80 | // 2) Already on disk → keep it. |
| 81 | // 3) Neither → generate, persist the hash, print the raw key |
| 82 | // so the user can paste it into the webapp's |
| 83 | // "Register gateway" dialog. |
| 84 | const envKey = process.env.COREBRAIN_GATEWAY_SECURITY_KEY; |
| 85 | let securityKeyPatch: {securityKeyHash?: string} = {}; |
| 86 | let printRawKey: string | null = null; |
| 87 | if (envKey && !existing.securityKeyHash) { |
| 88 | securityKeyPatch.securityKeyHash = hashKey(envKey); |
| 89 | applied.push('COREBRAIN_GATEWAY_SECURITY_KEY'); |
| 90 | } else if (!envKey && !existing.securityKeyHash) { |
| 91 | printRawKey = generateSecurityKey(); |
| 92 | securityKeyPatch.securityKeyHash = hashKey(printRawKey); |
| 93 | applied.push('generated-security-key'); |
| 94 | } |
| 95 | |
| 96 | // Default slots: browser/coding/files/exec on. Only applied to a fresh |
| 97 | // gateway record — once a slot has any explicit setting (even `false`), |
| 98 | // we don't override it. |
| 99 | const slotsPatch: {slots?: GatewaySlots} = {}; |
no test coverage detected