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:
gavrielc
2026-04-18 15:16:53 +03:00
parent a612c2ca24
commit a4573395d9
15 changed files with 666 additions and 404 deletions

View File

@@ -0,0 +1,37 @@
/**
* Approvals module — admin-gated self-modification and OneCLI credential flow.
*
* Registers:
* - Three delivery actions the container writes via self-mod MCP tools:
* install_packages, request_rebuild, add_mcp_server.
* - A response handler that claims `pending_approvals` rows (agent-initiated
* approvals) + OneCLI credential approvals (resolved via in-memory Promise).
* - An adapter-ready callback that starts the OneCLI manual-approval handler
* once the delivery adapter is set.
* - A shutdown callback that stops the OneCLI handler cleanly.
*/
import { registerDeliveryAction, onDeliveryAdapterReady } from '../../delivery.js';
import { registerResponseHandler, onShutdown } from '../../index.js';
import { handleAddMcpServer, handleInstallPackages, handleRequestRebuild } from './request-approval.js';
import { handleApprovalsResponse } from './response-handler.js';
import { startOneCLIApprovalHandler, stopOneCLIApprovalHandler } from './onecli-approvals.js';
registerDeliveryAction('install_packages', async (content, session) => {
await handleInstallPackages(content, session);
});
registerDeliveryAction('request_rebuild', async (content, session) => {
await handleRequestRebuild(content, session);
});
registerDeliveryAction('add_mcp_server', async (content, session) => {
await handleAddMcpServer(content, session);
});
registerResponseHandler(handleApprovalsResponse);
onDeliveryAdapterReady((adapter) => {
startOneCLIApprovalHandler(adapter);
});
onShutdown(() => {
stopOneCLIApprovalHandler();
});