diff --git a/src/container-runner.ts b/src/container-runner.ts index c3dce4d..bc54632 100644 --- a/src/container-runner.ts +++ b/src/container-runner.ts @@ -15,12 +15,7 @@ import { getAgentGroup } from './db/agent-groups.js'; import { getMessagingGroup } from './db/messaging-groups.js'; import { log } from './log.js'; import { validateAdditionalMounts } from './mount-security.js'; -import { - markContainerIdle, - markContainerRunning, - markContainerStopped, - sessionDir, -} from './session-manager.js'; +import { markContainerIdle, markContainerRunning, markContainerStopped, sessionDir } from './session-manager.js'; import type { AgentGroup, Session } from './types.js'; const onecli = new OneCLI({ url: ONECLI_URL }); diff --git a/src/container-runtime.test.ts b/src/container-runtime.test.ts index 80eb46e..47d9744 100644 --- a/src/container-runtime.test.ts +++ b/src/container-runtime.test.ts @@ -100,10 +100,10 @@ describe('cleanupOrphans', () => { expect(mockExecSync).toHaveBeenNthCalledWith(3, `${CONTAINER_RUNTIME_BIN} stop -t 1 nanoclaw-group2-222`, { stdio: 'pipe', }); - expect(log.info).toHaveBeenCalledWith( - 'Stopped orphaned containers', - { count: 2, names: ['nanoclaw-group1-111', 'nanoclaw-group2-222'] }, - ); + expect(log.info).toHaveBeenCalledWith('Stopped orphaned containers', { + count: 2, + names: ['nanoclaw-group1-111', 'nanoclaw-group2-222'], + }); }); it('does nothing when no orphans exist', () => { @@ -140,9 +140,9 @@ describe('cleanupOrphans', () => { cleanupOrphans(); // should not throw expect(mockExecSync).toHaveBeenCalledTimes(3); - expect(log.info).toHaveBeenCalledWith( - 'Stopped orphaned containers', - { count: 2, names: ['nanoclaw-a-1', 'nanoclaw-b-2'] }, - ); + expect(log.info).toHaveBeenCalledWith('Stopped orphaned containers', { + count: 2, + names: ['nanoclaw-a-1', 'nanoclaw-b-2'], + }); }); }); diff --git a/src/delivery.ts b/src/delivery.ts index d74df75..35a41c2 100644 --- a/src/delivery.ts +++ b/src/delivery.ts @@ -131,9 +131,9 @@ async function deliverSessionMessages(session: Session): Promise { try { await deliverMessage(msg, session, inDb); // Track delivery in inbound.db (host-owned) — not outbound.db - inDb.prepare("INSERT OR IGNORE INTO delivered (message_out_id, delivered_at) VALUES (?, datetime('now'))").run( - msg.id, - ); + inDb + .prepare("INSERT OR IGNORE INTO delivered (message_out_id, delivered_at) VALUES (?, datetime('now'))") + .run(msg.id); resetContainerIdleTimer(session.id); } catch (err) { log.error('Failed to deliver message', { messageId: msg.id, sessionId: session.id, err }); @@ -249,9 +249,7 @@ async function handleSystemAction( const recurrence = (content.recurrence as string) || null; // Compute next even seq for host-owned inbound.db - const maxSeq = ( - inDb.prepare('SELECT COALESCE(MAX(seq), 0) AS m FROM messages_in').get() as { m: number } - ).m; + const maxSeq = (inDb.prepare('SELECT COALESCE(MAX(seq), 0) AS m FROM messages_in').get() as { m: number }).m; const nextSeq = maxSeq < 2 ? 2 : maxSeq + 2 - (maxSeq % 2); inDb @@ -276,7 +274,9 @@ async function handleSystemAction( case 'cancel_task': { const taskId = content.taskId as string; inDb - .prepare("UPDATE messages_in SET status = 'completed' WHERE id = ? AND kind = 'task' AND status IN ('pending', 'paused')") + .prepare( + "UPDATE messages_in SET status = 'completed' WHERE id = ? AND kind = 'task' AND status IN ('pending', 'paused')", + ) .run(taskId); log.info('Task cancelled', { taskId }); break; diff --git a/src/host-core.test.ts b/src/host-core.test.ts index 9dc711e..1378589 100644 --- a/src/host-core.test.ts +++ b/src/host-core.test.ts @@ -104,7 +104,9 @@ describe('session manager', () => { const outPath = outboundDbPath('ag-1', 'sess-test'); expect(fs.existsSync(outPath)).toBe(true); const outDb = new Database(outPath); - const outTables = outDb.prepare("SELECT name FROM sqlite_master WHERE type='table'").all() as Array<{ name: string }>; + const outTables = outDb.prepare("SELECT name FROM sqlite_master WHERE type='table'").all() as Array<{ + name: string; + }>; expect(outTables.map((t) => t.name)).toContain('messages_out'); expect(outTables.map((t) => t.name)).toContain('processing_ack'); outDb.close(); diff --git a/src/host-sweep.ts b/src/host-sweep.ts index 5bd877e..22583a8 100644 --- a/src/host-sweep.ts +++ b/src/host-sweep.ts @@ -115,9 +115,7 @@ function syncProcessingAcks(inDb: Database.Database, outDb: Database.Database): if (completed.length === 0) return; // Batch-update messages_in status for completed/failed messages - const updateStmt = inDb.prepare( - "UPDATE messages_in SET status = 'completed' WHERE id = ? AND status != 'completed'", - ); + const updateStmt = inDb.prepare("UPDATE messages_in SET status = 'completed' WHERE id = ? AND status != 'completed'"); inDb.transaction(() => { for (const { message_id } of completed) { updateStmt.run(message_id); @@ -148,9 +146,9 @@ function detectStaleContainers( if (heartbeatAge < STALE_THRESHOLD_MS) return; // Container is alive // Heartbeat is stale — check for stuck processing entries - const processing = outDb - .prepare("SELECT message_id FROM processing_ack WHERE status = 'processing'") - .all() as Array<{ message_id: string }>; + const processing = outDb.prepare("SELECT message_id FROM processing_ack WHERE status = 'processing'").all() as Array<{ + message_id: string; + }>; if (processing.length === 0) return; @@ -200,9 +198,7 @@ async function handleRecurrence(inDb: Database.Database, session: Session): Prom const newId = `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; // Host uses even seq numbers - const maxSeq = ( - inDb.prepare('SELECT COALESCE(MAX(seq), 0) AS m FROM messages_in').get() as { m: number } - ).m; + const maxSeq = (inDb.prepare('SELECT COALESCE(MAX(seq), 0) AS m FROM messages_in').get() as { m: number }).m; const nextSeq = maxSeq < 2 ? 2 : maxSeq + 2 - (maxSeq % 2); inDb @@ -210,7 +206,17 @@ async function handleRecurrence(inDb: Database.Database, session: Session): Prom `INSERT INTO messages_in (id, seq, kind, timestamp, status, process_after, recurrence, platform_id, channel_type, thread_id, content) VALUES (?, ?, ?, datetime('now'), 'pending', ?, ?, ?, ?, ?, ?)`, ) - .run(newId, nextSeq, msg.kind, nextRun, msg.recurrence, msg.platform_id, msg.channel_type, msg.thread_id, msg.content); + .run( + newId, + nextSeq, + msg.kind, + nextRun, + msg.recurrence, + msg.platform_id, + msg.channel_type, + msg.thread_id, + msg.content, + ); // Remove recurrence from the completed message so it doesn't spawn again inDb.prepare('UPDATE messages_in SET recurrence = NULL WHERE id = ?').run(msg.id); diff --git a/src/mount-security.ts b/src/mount-security.ts index cea550a..ba2b6f8 100644 --- a/src/mount-security.ts +++ b/src/mount-security.ts @@ -76,7 +76,10 @@ export function loadMountAllowlist(): MountAllowlist | null { if (!fs.existsSync(MOUNT_ALLOWLIST_PATH)) { // Do NOT cache this as an error — file may be created later without restart. // Only parse/structural errors are permanently cached. - log.warn('Mount allowlist not found - additional mounts will be BLOCKED. Create the file to enable additional mounts.', { path: MOUNT_ALLOWLIST_PATH }); + log.warn( + 'Mount allowlist not found - additional mounts will be BLOCKED. Create the file to enable additional mounts.', + { path: MOUNT_ALLOWLIST_PATH }, + ); return null; } @@ -101,12 +104,19 @@ export function loadMountAllowlist(): MountAllowlist | null { allowlist.blockedPatterns = mergedBlockedPatterns; cachedAllowlist = allowlist; - log.info('Mount allowlist loaded successfully', { path: MOUNT_ALLOWLIST_PATH, allowedRoots: allowlist.allowedRoots.length, blockedPatterns: allowlist.blockedPatterns.length }); + log.info('Mount allowlist loaded successfully', { + path: MOUNT_ALLOWLIST_PATH, + allowedRoots: allowlist.allowedRoots.length, + blockedPatterns: allowlist.blockedPatterns.length, + }); return cachedAllowlist; } catch (err) { allowlistLoadError = err instanceof Error ? err.message : String(err); - log.error('Failed to load mount allowlist - additional mounts will be BLOCKED', { path: MOUNT_ALLOWLIST_PATH, error: allowlistLoadError }); + log.error('Failed to load mount allowlist - additional mounts will be BLOCKED', { + path: MOUNT_ALLOWLIST_PATH, + error: allowlistLoadError, + }); return null; } } @@ -287,7 +297,10 @@ export function validateMount(mount: AdditionalMount, isMain: boolean): MountVal } else if (!allowedRoot.allowReadWrite) { // Root doesn't allow read-write effectiveReadonly = true; - log.info('Mount forced to read-only - root does not allow read-write', { mount: mount.hostPath, root: allowedRoot.path }); + log.info('Mount forced to read-only - root does not allow read-write', { + mount: mount.hostPath, + root: allowedRoot.path, + }); } else { // Read-write allowed effectiveReadonly = false; @@ -333,9 +346,20 @@ export function validateAdditionalMounts( readonly: result.effectiveReadonly!, }); - log.debug('Mount validated successfully', { group: groupName, hostPath: result.realHostPath, containerPath: result.resolvedContainerPath, readonly: result.effectiveReadonly, reason: result.reason }); + log.debug('Mount validated successfully', { + group: groupName, + hostPath: result.realHostPath, + containerPath: result.resolvedContainerPath, + readonly: result.effectiveReadonly, + reason: result.reason, + }); } else { - log.warn('Additional mount REJECTED', { group: groupName, requestedPath: mount.hostPath, containerPath: mount.containerPath, reason: result.reason }); + log.warn('Additional mount REJECTED', { + group: groupName, + requestedPath: mount.hostPath, + containerPath: mount.containerPath, + reason: result.reason, + }); } } diff --git a/src/session-manager.ts b/src/session-manager.ts index f24f620..20e4562 100644 --- a/src/session-manager.ts +++ b/src/session-manager.ts @@ -139,9 +139,7 @@ export function writeSessionMessage( try { // Host uses even seq numbers, container uses odd — prevents collisions // across the two-DB boundary without cross-DB coordination. - const maxSeq = ( - db.prepare('SELECT COALESCE(MAX(seq), 0) AS m FROM messages_in').get() as { m: number } - ).m; + const maxSeq = (db.prepare('SELECT COALESCE(MAX(seq), 0) AS m FROM messages_in').get() as { m: number }).m; const nextSeq = maxSeq < 2 ? 2 : maxSeq + 2 - (maxSeq % 2); // next even db.prepare(