/** * Strips environment-specific fields from config JSON before transferring * between local and platform environments (teleport/restore). * * Fields removed or reset: * - `ingress.publicBaseUrl` → set to `""` * - `ingress.enabled` → deleted * - `ingress.publicBaseUrlManagedBy` → deleted * - `telegram.registeredWebhookUrl` → deleted * - `daemon` → deleted entirely * - `skills.load.extraDirs` → set to `[]` * - `hostBrowser.cdpInspect.desktopAuto` → deleted **only when the source * either relies on the schema default or explicitly sets * `enabled: true`**. An explicit `enabled: false` is preserved so a * platform→local teleport doesn't silently re-enable auto-attach * against the user's opt-out. * * `logFile.dir` is intentionally *not* stripped: the logger's container * fallback (`util/logger.ts#resolveLogDir`) already redirects to the * default log dir with a warning when the configured path can't be * created, and stripping `dir` would disable rotating file logging * entirely because `lifecycle.ts` gates `initLogger` on a truthy * `config.logFile.dir`. */ export function sanitizeConfigForTransfer(configJson: string): string { let config: Record; try { const parsed = JSON.parse(configJson); if ( typeof parsed !== "object" || parsed === null || Array.isArray(parsed) ) { return configJson; } config = parsed; } catch { return configJson; } // Strip ingress environment-specific fields if (config.ingress && typeof config.ingress === "object") { const ingress = config.ingress as Record; ingress.publicBaseUrl = ""; delete ingress.enabled; delete ingress.publicBaseUrlManagedBy; } // Strip the recorded Telegram webhook URL. It records where *this* // deployment pointed Telegram, so carrying it across a teleport would have // the destination compare its own registration against the source's address // and report a mismatch on a channel that is fine. Absent, the health sweep // reports unverified until the destination reconciles and records its own. if (config.telegram && typeof config.telegram === "object") { const telegram = config.telegram as Record; delete telegram.registeredWebhookUrl; } // Strip daemon entirely delete config.daemon; // Strip skills.load.extraDirs if (config.skills && typeof config.skills === "object") { const skills = config.skills as Record; if (skills.load && typeof skills.load === "object") { const load = skills.load as Record; load.extraDirs = []; } } // Strip hostBrowser.cdpInspect.desktopAuto — the auto-attach-to-Chrome // behavior is gated on a macOS-originated turn; preserving a // source-host-derived `enabled: true` inside a Linux managed pod's // config is misleading and brittle. Preserve an explicit // `enabled: false` opt-out, though — the schema default is `true`, // so unconditionally stripping this subobject would re-enable // auto-attach after a platform→local teleport. if (config.hostBrowser && typeof config.hostBrowser === "object") { const hostBrowser = config.hostBrowser as Record; if (hostBrowser.cdpInspect && typeof hostBrowser.cdpInspect === "object") { const cdpInspect = hostBrowser.cdpInspect as Record; const desktopAuto = cdpInspect.desktopAuto; const isExplicitOptOut = desktopAuto !== null && typeof desktopAuto === "object" && !Array.isArray(desktopAuto) && (desktopAuto as Record).enabled === false; if (!isExplicitOptOut) { delete cdpInspect.desktopAuto; } } } return JSON.stringify(config, null, 2) + "\n"; }