/** * One-time backfill: seed `container_configs` rows from existing * `groups//container.json` files and `agent_groups.agent_provider`. * * Runs after migrations, before channel adapters start. Idempotent — skips * groups that already have a config row. */ import fs from 'fs'; import path from 'path'; import { GROUPS_DIR } from './config.js'; import type { McpServerConfig, AdditionalMountConfig } from './container-config.js'; import { getAllAgentGroups } from './db/agent-groups.js'; import { getContainerConfig, createContainerConfig } from './db/container-configs.js'; import { log } from './log.js'; import type { ContainerConfigRow } from './types.js'; interface LegacyContainerJson { mcpServers?: Record; packages?: { apt?: string[]; npm?: string[] }; imageTag?: string; additionalMounts?: AdditionalMountConfig[]; skills?: string[] | 'all'; provider?: string; assistantName?: string; maxMessagesPerPrompt?: number; } export function backfillContainerConfigs(): void { const groups = getAllAgentGroups(); let backfilled = 0; for (const group of groups) { // Skip if already has a config row if (getContainerConfig(group.id)) continue; // Read legacy container.json from disk const filePath = path.join(GROUPS_DIR, group.folder, 'container.json'); let legacy: LegacyContainerJson = {}; if (fs.existsSync(filePath)) { try { legacy = JSON.parse(fs.readFileSync(filePath, 'utf8')) as LegacyContainerJson; } catch (err) { log.warn('Backfill: failed to parse container.json, using defaults', { folder: group.folder, err: String(err), }); } } // DB agent_provider wins over file provider (matches old cascade) const provider = group.agent_provider || legacy.provider || null; const row: ContainerConfigRow = { agent_group_id: group.id, provider, model: null, effort: null, image_tag: legacy.imageTag ?? null, assistant_name: legacy.assistantName ?? null, max_messages_per_prompt: legacy.maxMessagesPerPrompt ?? null, skills: JSON.stringify(legacy.skills ?? 'all'), mcp_servers: JSON.stringify(legacy.mcpServers ?? {}), packages_apt: JSON.stringify(legacy.packages?.apt ?? []), packages_npm: JSON.stringify(legacy.packages?.npm ?? []), additional_mounts: JSON.stringify(legacy.additionalMounts ?? []), cli_scope: 'group', updated_at: new Date().toISOString(), }; createContainerConfig(row); backfilled++; } if (backfilled > 0) { log.info('Backfilled container_configs from disk', { count: backfilled }); } }