feat(db): move container config from filesystem to DB
Source of truth for container runtime config moves from
groups/<folder>/container.json to a new container_configs table.
The file becomes a materialized view written at spawn time.
- New container_configs table with scalar columns (provider, model,
effort, image_tag, assistant_name, max_messages_per_prompt) and
JSON columns (mcp_servers, packages_apt, packages_npm, skills,
additional_mounts)
- Startup backfill seeds DB from existing container.json files
- materializeContainerJson() replaces readContainerConfig + ensureRuntimeFields
- Self-mod handlers (install_packages, add_mcp_server) write to DB
- Provider cascade simplified: session -> container_configs -> 'claude'
- ncl groups config-{get,update,add-mcp-server,remove-mcp-server,
add-package,remove-package} custom operations
- restartAgentGroupContainers() helper for config change propagation
- Container side unchanged (still reads /workspace/agent/container.json)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
84
src/db/container-configs.ts
Normal file
84
src/db/container-configs.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { ContainerConfigRow } from '../types.js';
|
||||
import { getDb } from './connection.js';
|
||||
|
||||
export function getContainerConfig(agentGroupId: string): ContainerConfigRow | undefined {
|
||||
return getDb().prepare('SELECT * FROM container_configs WHERE agent_group_id = ?').get(agentGroupId) as
|
||||
| ContainerConfigRow
|
||||
| undefined;
|
||||
}
|
||||
|
||||
export function getAllContainerConfigs(): ContainerConfigRow[] {
|
||||
return getDb().prepare('SELECT * FROM container_configs').all() as ContainerConfigRow[];
|
||||
}
|
||||
|
||||
/** Insert a new config row. Caller must supply all JSON fields (use defaults for empty). */
|
||||
export function createContainerConfig(config: ContainerConfigRow): void {
|
||||
getDb()
|
||||
.prepare(
|
||||
`INSERT INTO container_configs (
|
||||
agent_group_id, provider, model, effort, image_tag, assistant_name,
|
||||
max_messages_per_prompt, skills, mcp_servers, packages_apt, packages_npm,
|
||||
additional_mounts, updated_at
|
||||
) VALUES (
|
||||
@agent_group_id, @provider, @model, @effort, @image_tag, @assistant_name,
|
||||
@max_messages_per_prompt, @skills, @mcp_servers, @packages_apt, @packages_npm,
|
||||
@additional_mounts, @updated_at
|
||||
)`,
|
||||
)
|
||||
.run(config);
|
||||
}
|
||||
|
||||
/** Create an empty config row with sensible defaults. Idempotent — no-ops if row exists. */
|
||||
export function ensureContainerConfig(agentGroupId: string): void {
|
||||
getDb()
|
||||
.prepare(
|
||||
`INSERT OR IGNORE INTO container_configs (agent_group_id, updated_at)
|
||||
VALUES (?, ?)`,
|
||||
)
|
||||
.run(agentGroupId, new Date().toISOString());
|
||||
}
|
||||
|
||||
/** Update scalar fields on a config row. Only touches fields present in `updates`. */
|
||||
export function updateContainerConfigScalars(
|
||||
agentGroupId: string,
|
||||
updates: Partial<
|
||||
Pick<
|
||||
ContainerConfigRow,
|
||||
'provider' | 'model' | 'effort' | 'image_tag' | 'assistant_name' | 'max_messages_per_prompt'
|
||||
>
|
||||
>,
|
||||
): void {
|
||||
const fields: string[] = [];
|
||||
const values: Record<string, unknown> = { agent_group_id: agentGroupId };
|
||||
|
||||
for (const [key, value] of Object.entries(updates)) {
|
||||
if (value !== undefined) {
|
||||
fields.push(`${key} = @${key}`);
|
||||
values[key] = value;
|
||||
}
|
||||
}
|
||||
if (fields.length === 0) return;
|
||||
|
||||
fields.push('updated_at = @updated_at');
|
||||
values.updated_at = new Date().toISOString();
|
||||
|
||||
getDb()
|
||||
.prepare(`UPDATE container_configs SET ${fields.join(', ')} WHERE agent_group_id = @agent_group_id`)
|
||||
.run(values);
|
||||
}
|
||||
|
||||
/** Overwrite a JSON column wholesale. Used for skills, mcp_servers, packages_*, additional_mounts. */
|
||||
export function updateContainerConfigJson(
|
||||
agentGroupId: string,
|
||||
column: 'skills' | 'mcp_servers' | 'packages_apt' | 'packages_npm' | 'additional_mounts',
|
||||
value: unknown,
|
||||
): void {
|
||||
const now = new Date().toISOString();
|
||||
getDb()
|
||||
.prepare(`UPDATE container_configs SET ${column} = ?, updated_at = ? WHERE agent_group_id = ?`)
|
||||
.run(JSON.stringify(value), now, agentGroupId);
|
||||
}
|
||||
|
||||
export function deleteContainerConfig(agentGroupId: string): void {
|
||||
getDb().prepare('DELETE FROM container_configs WHERE agent_group_id = ?').run(agentGroupId);
|
||||
}
|
||||
Reference in New Issue
Block a user