refactor: dual-instance setup, remove dead container code

- Make STORE_DIR, DATA_DIR, GROUPS_DIR configurable via env vars
  (NANOCLAW_STORE_DIR, NANOCLAW_DATA_DIR, NANOCLAW_GROUPS_DIR)
  for running two instances from one codebase
- Remove broken 'both' agent type sequential loop from processGroupMessages
- Delete dead code: container-runtime, mount-security, credential-proxy,
  Dockerfiles, and related config constants (~1,085 lines removed)
- Add codex instance launchd plist template
This commit is contained in:
Eyejoker
2026-03-11 00:50:03 +09:00
parent 955a2901c8
commit 57cafc19f8
15 changed files with 84 additions and 1269 deletions

View File

@@ -1,123 +0,0 @@
/**
* Container runtime abstraction for NanoClaw.
* All runtime-specific logic lives here so swapping runtimes means changing one file.
*/
import { execSync } from 'child_process';
import { logger } from './logger.js';
/** The container runtime binary name. */
export const CONTAINER_RUNTIME_BIN = 'container';
/**
* Hostname containers use to reach the host machine.
* Apple Container VMs access the host via the default gateway (192.168.64.1).
*/
export const CONTAINER_HOST_GATEWAY = '192.168.64.1';
/**
* Address the credential proxy binds to on the host.
* Apple Container VMs reach the host via 192.168.64.1, so we must bind to all interfaces.
*/
export const PROXY_BIND_HOST = '0.0.0.0';
/**
* CLI args needed for the container to resolve the host gateway.
* Apple Container provides host networking natively on macOS — no extra args needed.
*/
export function hostGatewayArgs(): string[] {
return [];
}
/** Returns CLI args for a readonly bind mount. */
export function readonlyMountArgs(
hostPath: string,
containerPath: string,
): string[] {
return [
'--mount',
`type=bind,source=${hostPath},target=${containerPath},readonly`,
];
}
/** Returns the shell command to stop a container by name. */
export function stopContainer(name: string): string {
return `${CONTAINER_RUNTIME_BIN} stop ${name}`;
}
/** Ensure the container runtime is running, starting it if needed. */
export function ensureContainerRuntimeRunning(): void {
try {
execSync(`${CONTAINER_RUNTIME_BIN} system status`, { stdio: 'pipe' });
logger.debug('Container runtime already running');
} catch {
logger.info('Starting container runtime...');
try {
execSync(`${CONTAINER_RUNTIME_BIN} system start`, {
stdio: 'pipe',
timeout: 30000,
});
logger.info('Container runtime started');
} catch (err) {
logger.error({ err }, 'Failed to start container runtime');
console.error(
'\n╔════════════════════════════════════════════════════════════════╗',
);
console.error(
'║ FATAL: Container runtime failed to start ║',
);
console.error(
'║ ║',
);
console.error(
'║ Agents cannot run without a container runtime. To fix: ║',
);
console.error(
'║ 1. Ensure Apple Container is installed ║',
);
console.error(
'║ 2. Run: container system start ║',
);
console.error(
'║ 3. Restart NanoClaw ║',
);
console.error(
'╚════════════════════════════════════════════════════════════════╝\n',
);
throw new Error('Container runtime is required but failed to start');
}
}
}
/** Kill orphaned NanoClaw containers from previous runs. */
export function cleanupOrphans(): void {
try {
const output = execSync(`${CONTAINER_RUNTIME_BIN} ls --format json`, {
stdio: ['pipe', 'pipe', 'pipe'],
encoding: 'utf-8',
});
const containers: { status: string; configuration: { id: string } }[] =
JSON.parse(output || '[]');
const orphans = containers
.filter(
(c) =>
c.status === 'running' && c.configuration.id.startsWith('nanoclaw-'),
)
.map((c) => c.configuration.id);
for (const name of orphans) {
try {
execSync(stopContainer(name), { stdio: 'pipe' });
} catch {
/* already stopped */
}
}
if (orphans.length > 0) {
logger.info(
{ count: orphans.length, names: orphans },
'Stopped orphaned containers',
);
}
} catch (err) {
logger.warn({ err }, 'Failed to clean up orphaned containers');
}
}