refactor: scaffold module registries and default-module layout

Additive change — existing code paths still run via inline fallbacks.
Prepares core for per-module extractions in PR #3 onward.

Four registries added with empty defaults:
  - delivery action handlers (delivery.ts)
  - router inbound gate (router.ts)
  - response dispatcher (index.ts)
  - MCP tool self-registration (container/agent-runner/src/mcp-tools/server.ts)

Default modules moved to src/modules/ for signaling:
  - src/modules/typing/       (extracted from delivery.ts)
  - src/modules/mount-security/ (moved from src/mount-security.ts)

Both are imported directly by core — no hook, no registry. Removal
requires editing core imports.

Migrator now keys applied rows by name (uniqueness) so module
migrations can pick arbitrary version numbers. Stored version column
is auto-assigned as an applied-order sequence.

sqlite_master guards added around core calls into module-owned tables
(user_roles, agent_destinations, pending_questions). No-ops today;
load-bearing after the owning modules are extracted.

MODULE-HOOK markers placed at scheduling's two skill-edit sites
(host-sweep.ts recurrence call, poll-loop.ts pre-task gate). PR #4
replaces the marked blocks when scheduling moves to its module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-18 14:46:19 +03:00
parent 1888ecc1e9
commit 4202041d0b
19 changed files with 480 additions and 234 deletions

View File

@@ -13,11 +13,12 @@ import { CONTAINER_IMAGE, DATA_DIR, GROUPS_DIR, IDLE_TIMEOUT, ONECLI_URL, TIMEZO
import { readContainerConfig, writeContainerConfig } from './container-config.js';
import { CONTAINER_RUNTIME_BIN, hostGatewayArgs, readonlyMountArgs, stopContainer } from './container-runtime.js';
import { getAgentGroup } from './db/agent-groups.js';
import { getDb, hasTable } from './db/connection.js';
import { getAdminsOfAgentGroup, getGlobalAdmins, getOwners } from './db/user-roles.js';
import { initGroupFilesystem } from './group-init.js';
import { stopTypingRefresh } from './delivery.js';
import { stopTypingRefresh } from './modules/typing/index.js';
import { log } from './log.js';
import { validateAdditionalMounts } from './mount-security.js';
import { validateAdditionalMounts } from './modules/mount-security/index.js';
// Provider host-side config barrel — each provider that needs host-side
// container setup self-registers on import.
import './providers/index.js';
@@ -286,10 +287,16 @@ async function buildContainerArgs(
// Users allowed to run admin commands (e.g. /clear) inside this container.
// Computed at wake time: owners + global admins + admins scoped to this
// agent group. Role changes take effect on next container spawn.
//
// Guarded: if the permissions module isn't installed, `user_roles`
// doesn't exist and the set stays empty — the formatter treats an
// empty admin set as permissionless (every sender is admin).
const adminUserIds = new Set<string>();
for (const r of getOwners()) adminUserIds.add(r.user_id);
for (const r of getGlobalAdmins()) adminUserIds.add(r.user_id);
for (const r of getAdminsOfAgentGroup(agentGroup.id)) adminUserIds.add(r.user_id);
if (hasTable(getDb(), 'user_roles')) {
for (const r of getOwners()) adminUserIds.add(r.user_id);
for (const r of getGlobalAdmins()) adminUserIds.add(r.user_id);
for (const r of getAdminsOfAgentGroup(agentGroup.id)) adminUserIds.add(r.user_id);
}
if (adminUserIds.size > 0) {
args.push('-e', `NANOCLAW_ADMIN_USER_IDS=${Array.from(adminUserIds).join(',')}`);
}