refactor: shared source — replace per-group agent-runner copies with single RO mount
Replace the per-group agent-runner-src copy model with a single shared read-only mount. Source and skills are now RO + shared; personality, config, working files, and Claude state stay RW + per-group. Key changes: - Mount container/agent-runner/src/ RO at /app/src (all groups share one copy) - Mount container/skills/ RO at /app/skills; per-group skill selection via symlinks in .claude-shared/skills/ based on container.json "skills" field - Mount container.json as nested RO bind on top of RW group dir - Move all NANOCLAW_* env vars to container.json (runner reads at startup) - New runner config.ts module replaces process.env reads - Move command gate (filtered/admin) from container to host router - Dockerfile: remove source COPY, split CLI installs (claude-code last), move agent-runner deps above CLIs for better layer caching - Add writeOutboundDirect for router denial responses - Design doc at docs/shared-src.md Not included (follow-up): DB migration to drop agent_provider columns, cleanup of orphaned agent-runner-src directories. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,8 @@
|
||||
/**
|
||||
* Per-group container config, stored as a plain JSON file at
|
||||
* `groups/<folder>/container.json`. Replaces the former
|
||||
* `agent_groups.container_config` DB column.
|
||||
*
|
||||
* Shape:
|
||||
* {
|
||||
* mcpServers: { [name]: { command, args, env } }
|
||||
* packages: { apt: string[], npm: string[] }
|
||||
* imageTag?: string // set by buildAgentGroupImage on rebuild
|
||||
* additionalMounts?: Array<{hostPath, containerPath, readonly}>
|
||||
* }
|
||||
* `groups/<folder>/container.json`. Mounted read-only inside the container
|
||||
* at `/workspace/agent/container.json` — the runner reads it at startup but
|
||||
* cannot modify it. Config changes go through the self-mod approval flow.
|
||||
*
|
||||
* All fields are optional — a missing file or a partial file both resolve
|
||||
* to sensible defaults. Writes are atomic-enough (write-then-rename is not
|
||||
@@ -38,6 +31,18 @@ export interface ContainerConfig {
|
||||
packages: { apt: string[]; npm: string[] };
|
||||
imageTag?: string;
|
||||
additionalMounts: AdditionalMountConfig[];
|
||||
/** Which skills to enable — array of skill names or "all" (default). */
|
||||
skills: string[] | 'all';
|
||||
/** Agent provider name (e.g. "claude", "opencode"). Default: "claude". */
|
||||
provider?: string;
|
||||
/** Agent group display name (used in transcript archiving). */
|
||||
groupName?: string;
|
||||
/** Assistant display name (used in system prompt / responses). */
|
||||
assistantName?: string;
|
||||
/** Agent group ID — set by the host, read by the runner. */
|
||||
agentGroupId?: string;
|
||||
/** Max messages per prompt. Falls back to code default if unset. */
|
||||
maxMessagesPerPrompt?: number;
|
||||
}
|
||||
|
||||
function emptyConfig(): ContainerConfig {
|
||||
@@ -45,6 +50,7 @@ function emptyConfig(): ContainerConfig {
|
||||
mcpServers: {},
|
||||
packages: { apt: [], npm: [] },
|
||||
additionalMounts: [],
|
||||
skills: 'all',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,6 +77,12 @@ export function readContainerConfig(folder: string): ContainerConfig {
|
||||
},
|
||||
imageTag: raw.imageTag,
|
||||
additionalMounts: raw.additionalMounts ?? [],
|
||||
skills: raw.skills ?? 'all',
|
||||
provider: raw.provider,
|
||||
groupName: raw.groupName,
|
||||
assistantName: raw.assistantName,
|
||||
agentGroupId: raw.agentGroupId,
|
||||
maxMessagesPerPrompt: raw.maxMessagesPerPrompt,
|
||||
};
|
||||
} catch (err) {
|
||||
console.error(`[container-config] failed to parse ${p}: ${String(err)}`);
|
||||
|
||||
Reference in New Issue
Block a user