refactor(modules): extract approvals + interactive as registry-based modules
Phase 2 / PR #3 of the module refactor. Moves the approval and interactive- question flows out of core and into src/modules/, wired through the response dispatcher and delivery action registries. New modules: - src/modules/interactive/ — registers a response handler that claims pending_questions rows, writes question_response to the session DB, wakes the container. createPendingQuestion call stays inline in delivery.ts (guarded by hasTable) per plan. - src/modules/approvals/ — registers 3 delivery actions (install_packages, request_rebuild, add_mcp_server), a response handler for pending_approvals (including OneCLI action fall-through), an adapter-ready hook that boots the OneCLI manual-approval handler, and a shutdown hook that stops it. OneCLI implementation (src/onecli-approvals.ts) moves into the module. Core lifecycle hooks added (narrow, not registries): - onDeliveryAdapterReady(cb) in delivery.ts — fires when setDeliveryAdapter runs (or immediately if already set). Used by approvals for OneCLI boot. - onShutdown(cb) in index.ts — fires on SIGTERM/SIGINT. Used by approvals for OneCLI teardown. - getDeliveryAdapter() getter in delivery.ts — for live-flow adapter access in registered delivery actions. Core shrinks: delivery.ts 911 → 665 lines, index.ts 405 → 224 lines. dispatchResponse now logs "Unclaimed response" instead of falling through to an inline handler — the inline fallback moved into the two modules. Migration files renamed to the module-<name>-<short>.ts convention: - 003-pending-approvals.ts → module-approvals-pending-approvals.ts - 007-pending-approvals-title-options.ts → module-approvals-title-options.ts Migration.name fields unchanged so existing DBs treat them as already-applied. Degradation verified: emptying the modules barrel builds clean and 137/137 tests pass. Actions would log "Unknown system action"; button clicks would log "Unclaimed response". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
40
src/db/migrations/module-approvals-title-options.ts
Normal file
40
src/db/migrations/module-approvals-title-options.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { Migration } from './index.js';
|
||||
|
||||
/**
|
||||
* Retroactive schema fix: earlier migration 003 was edited after it had
|
||||
* already been applied in the wild, adding `title` and `options_json`
|
||||
* columns to its CREATE TABLE statement. Installs that ran 003 before the
|
||||
* edit don't have those columns, and `createPendingApproval` (which
|
||||
* inserts into both) fails with "no such column" at runtime.
|
||||
*
|
||||
* This migration adds the missing columns via ALTER TABLE so old installs
|
||||
* catch up. On a fresh install that runs 003 at its current definition,
|
||||
* the ALTER statements will fail harmlessly (column already exists) and
|
||||
* we swallow the error per-column.
|
||||
*/
|
||||
// Retains the original `name` ('pending-approvals-title-options') so
|
||||
// existing DBs that already recorded this migration don't re-run it. The
|
||||
// module- prefix lives on the filename / export identifier only.
|
||||
export const moduleApprovalsTitleOptions: Migration = {
|
||||
version: 7,
|
||||
name: 'pending-approvals-title-options',
|
||||
up(db) {
|
||||
const addIfMissing = (col: string, sql: string): void => {
|
||||
try {
|
||||
db.exec(sql);
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
if (msg.includes('duplicate column') || msg.includes('already exists')) {
|
||||
// Fresh install — column already added by the current 003
|
||||
// definition. Nothing to do.
|
||||
return;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
void col;
|
||||
};
|
||||
|
||||
addIfMissing('title', `ALTER TABLE pending_approvals ADD COLUMN title TEXT NOT NULL DEFAULT ''`);
|
||||
addIfMissing('options_json', `ALTER TABLE pending_approvals ADD COLUMN options_json TEXT NOT NULL DEFAULT '[]'`);
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user