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:
@@ -31,3 +31,18 @@ export function closeDb(): void {
|
||||
_db?.close();
|
||||
_db = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a table exists. Used by core code that touches
|
||||
* module-owned tables so that an uninstalled module degrades silently
|
||||
* instead of raising SQLite errors. Cheap: a single indexed lookup on
|
||||
* sqlite_master. Results are not cached — a module install adds the
|
||||
* table at runtime (next service start), and callers may run before
|
||||
* or after that boundary.
|
||||
*/
|
||||
export function hasTable(db: Database.Database, name: string): boolean {
|
||||
const row = db.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name = ? LIMIT 1`).get(name) as
|
||||
| { '1': number }
|
||||
| undefined;
|
||||
return row !== undefined;
|
||||
}
|
||||
|
||||
@@ -32,29 +32,34 @@ export function runMigrations(db: Database.Database): void {
|
||||
name TEXT NOT NULL,
|
||||
applied TEXT NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_schema_version_name ON schema_version(name);
|
||||
`);
|
||||
|
||||
const currentVersion =
|
||||
(db.prepare('SELECT MAX(version) as v FROM schema_version').get() as { v: number | null })?.v ?? 0;
|
||||
|
||||
const pending = migrations.filter((m) => m.version > currentVersion);
|
||||
// Uniqueness is keyed on `name`, not `version`. This lets module
|
||||
// migrations (added later by install skills) pick arbitrary version
|
||||
// numbers without coordinating across modules. `version` stays on
|
||||
// the Migration object as an ordering hint within the barrel array;
|
||||
// the stored `version` column is auto-assigned at insert time as an
|
||||
// applied-order number.
|
||||
const applied = new Set<string>(
|
||||
(db.prepare('SELECT name FROM schema_version').all() as { name: string }[]).map((r) => r.name),
|
||||
);
|
||||
const pending = migrations.filter((m) => !applied.has(m.name));
|
||||
if (pending.length === 0) return;
|
||||
|
||||
log.info('Running migrations', {
|
||||
from: currentVersion,
|
||||
to: pending[pending.length - 1].version,
|
||||
count: pending.length,
|
||||
});
|
||||
log.info('Running migrations', { count: pending.length });
|
||||
|
||||
for (const m of pending) {
|
||||
db.transaction(() => {
|
||||
m.up(db);
|
||||
const next =
|
||||
(db.prepare('SELECT COALESCE(MAX(version), 0) + 1 AS v FROM schema_version').get() as { v: number }).v;
|
||||
db.prepare('INSERT INTO schema_version (version, name, applied) VALUES (?, ?, ?)').run(
|
||||
m.version,
|
||||
next,
|
||||
m.name,
|
||||
new Date().toISOString(),
|
||||
);
|
||||
})();
|
||||
log.info('Migration applied', { version: m.version, name: m.name });
|
||||
log.info('Migration applied', { name: m.name });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user