diff --git a/plugins/reporting-governance/scripts/watchdog_auto_notify_orchestrator.mjs b/plugins/reporting-governance/scripts/watchdog_auto_notify_orchestrator.mjs index 02cfb09..688bae3 100644 --- a/plugins/reporting-governance/scripts/watchdog_auto_notify_orchestrator.mjs +++ b/plugins/reporting-governance/scripts/watchdog_auto_notify_orchestrator.mjs @@ -1,212 +1,5 @@ #!/usr/bin/env node -import path from 'node:path'; -import process from 'node:process'; -import { spawnSync } from 'node:child_process'; - -const ROOT_DIR = path.resolve(import.meta.dirname, '..'); -const DEFAULT_STATE_PATH = path.join(ROOT_DIR, 'memory', 'watchdog-state.json'); -const DEFAULT_EVIDENCE_DIR = path.join(ROOT_DIR, 'state', 'long-task-watchdog'); -const DEFAULT_EVENT_DIR = path.join(ROOT_DIR, 'state', 'long-task-watchdog-events'); -const DEFAULT_QUEUE_DIR = path.join(ROOT_DIR, 'state', 'operator-notify-queue'); -const DEFAULT_SPOOL_DIR = path.join(ROOT_DIR, 'state', 'operator-notify-dispatch-spool'); -const DEFAULT_RECEIPT_DIR = path.join(ROOT_DIR, 'state', 'operator-notify-bridge-receipts'); -const DEFAULT_WATCHDOG_SCRIPT = path.join(ROOT_DIR, 'scripts', 'long_task_watchdog.mjs'); -const DEFAULT_DISPATCHER_SCRIPT = path.join(ROOT_DIR, 'scripts', 'operator_notify_dispatcher.mjs'); -const DEFAULT_SUPERVISOR_SCRIPT = path.join(ROOT_DIR, 'scripts', 'operator_notify_bridge_supervisor.mjs'); - -function parseArgs(argv) { - const args = { - state: DEFAULT_STATE_PATH, - evidenceDir: DEFAULT_EVIDENCE_DIR, - eventDir: DEFAULT_EVENT_DIR, - queueDir: DEFAULT_QUEUE_DIR, - spoolDir: DEFAULT_SPOOL_DIR, - receiptDir: DEFAULT_RECEIPT_DIR, - watchdogScript: DEFAULT_WATCHDOG_SCRIPT, - dispatcherScript: DEFAULT_DISPATCHER_SCRIPT, - supervisorScript: DEFAULT_SUPERVISOR_SCRIPT, - senderCommand: null, - senderMode: null, - openclawBin: 'openclaw', - now: null, - compact: false, - writeState: false, - claim: false, - dryRun: false, - help: false, - }; - - for (let i = 0; i < argv.length; i += 1) { - const token = argv[i]; - if (token === '--compact') { args.compact = true; continue; } - if (token === '--write-state') { args.writeState = true; continue; } - if (token === '--claim') { args.claim = true; continue; } - if (token === '--dry-run') { args.dryRun = true; continue; } - if (token === '--help' || token === '-h') { args.help = true; continue; } - - const pairs = [ - ['--state', 'state'], - ['--evidence-dir', 'evidenceDir'], - ['--event-dir', 'eventDir'], - ['--queue-dir', 'queueDir'], - ['--spool-dir', 'spoolDir'], - ['--receipt-dir', 'receiptDir'], - ['--watchdog-script', 'watchdogScript'], - ['--dispatcher-script', 'dispatcherScript'], - ['--supervisor-script', 'supervisorScript'], - ['--sender-command', 'senderCommand'], - ['--sender-mode', 'senderMode'], - ['--openclaw-bin', 'openclawBin'], - ['--now', 'now'], - ]; - - let matched = false; - for (const [flag, key] of pairs) { - if (token === flag) { - args[key] = argv[i + 1] ?? args[key]; - i += 1; - matched = true; - break; - } - if (token.startsWith(`${flag}=`)) { - args[key] = token.slice(flag.length + 1) || args[key]; - matched = true; - break; - } - } - if (matched) continue; - } - - return args; -} - -function printHelp() { - process.stdout.write([ - 'Usage:', - ' node scripts/watchdog_auto_notify_orchestrator.mjs [--write-state] [--claim] [--dry-run] [--sender-command ] [--sender-mode shim|openclaw-cli] [--openclaw-bin ] [--now ] [--compact]', - '', - 'Runs the full watchdog auto-notify chain in order:', - ' runner -> queue -> dispatcher -> bridge -> sender -> ack|blocked', - '', - 'If --sender-mode is given and --sender-command is omitted, a sender-binding command is constructed automatically.', - ].join('\n') + '\n'); -} - -function buildSenderCommand(args) { - if (args.senderCommand) return args.senderCommand; - if (!args.senderMode) return null; - const cmd = [ - JSON.stringify(process.execPath), - JSON.stringify(path.join(ROOT_DIR, 'scripts', 'operator_notify_sender_binding.mjs')), - '--mode', JSON.stringify(args.senderMode), - '--openclaw-bin', JSON.stringify(args.openclawBin), - '--compact', - ]; - return cmd.join(' '); -} - -function runNodeScript(scriptPath, scriptArgs) { - return spawnSync(process.execPath, [scriptPath, ...scriptArgs], { - cwd: ROOT_DIR, - encoding: 'utf8', - }); -} - -function parseJsonOutput(label, result) { - const stdout = result.stdout ?? ''; - try { - return stdout.trim() ? JSON.parse(stdout) : null; - } catch (error) { - throw new Error(`${label} emitted non-JSON stdout: ${error instanceof Error ? error.message : String(error)}`); - } -} - -function ensureSuccess(label, result) { - if (result.status !== 0) { - throw new Error(`${label} failed with status ${result.status ?? 'null'}: ${(result.stderr ?? '').trim() || '(no stderr)'}`); - } -} - -function main() { - const args = parseArgs(process.argv.slice(2)); - if (args.help) { - printHelp(); - process.exit(0); - } - - const senderCommand = buildSenderCommand(args); - - try { - const watchdogArgs = [ - '--state', path.resolve(args.state), - '--evidence-dir', path.resolve(args.evidenceDir), - '--event-dir', path.resolve(args.eventDir), - '--notification-dir', path.resolve(args.queueDir), - '--compact', - ]; - if (args.writeState) watchdogArgs.push('--write-state'); - if (args.now) watchdogArgs.push('--now', args.now); - const watchdog = runNodeScript(path.resolve(args.watchdogScript), watchdogArgs); - ensureSuccess('watchdog runner', watchdog); - const watchdogPayload = parseJsonOutput('watchdog runner', watchdog); - - const dispatcherArgs = [ - '--queue-dir', path.resolve(args.queueDir), - '--spool-dir', path.resolve(args.spoolDir), - '--compact', - ]; - if (args.claim) dispatcherArgs.push('--claim'); - if (args.now) dispatcherArgs.push('--now', args.now); - const dispatcher = runNodeScript(path.resolve(args.dispatcherScript), dispatcherArgs); - ensureSuccess('dispatcher', dispatcher); - const dispatcherPayload = parseJsonOutput('dispatcher', dispatcher); - - const supervisorArgs = [ - '--queue-dir', path.resolve(args.queueDir), - '--spool-dir', path.resolve(args.spoolDir), - '--receipt-dir', path.resolve(args.receiptDir), - '--dispatcher-script', path.resolve(args.dispatcherScript), - '--compact', - ]; - if (args.dryRun) supervisorArgs.push('--dry-run'); - if (senderCommand) supervisorArgs.push('--sender-command', senderCommand); - if (args.now) supervisorArgs.push('--now', args.now); - const supervisor = runNodeScript(path.resolve(args.supervisorScript), supervisorArgs); - ensureSuccess('bridge supervisor', supervisor); - const supervisorPayload = parseJsonOutput('bridge supervisor', supervisor); - - const response = { - ok: true, - tool: 'watchdog_auto_notify_orchestrator', - version: 'mvp-v1', - now: args.now ?? null, - executionOrder: [ - 'runner', - 'queue', - 'dispatcher', - 'bridge', - senderCommand ? 'sender' : 'sender_unconfigured', - 'ack_or_blocked_or_pending', - ], - orchestration: { - script: path.resolve(import.meta.filename), - senderCommandConfigured: Boolean(senderCommand), - senderMode: args.senderMode ?? null, - dryRun: args.dryRun, - }, - result: { - watchdog: watchdogPayload?.result ?? null, - dispatcher: dispatcherPayload?.result ?? null, - supervisor: supervisorPayload?.result ?? null, - }, - }; - - process.stdout.write(`${JSON.stringify(response, null, args.compact ? 0 : 2)}\n`); - } catch (error) { - process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); - process.exit(1); - } -} +import { main } from '../src/adapters/orchestrator-cli.mjs'; main(); diff --git a/plugins/reporting-governance/src/adapters/index.mjs b/plugins/reporting-governance/src/adapters/index.mjs index 1d161fb..5dde3c6 100644 --- a/plugins/reporting-governance/src/adapters/index.mjs +++ b/plugins/reporting-governance/src/adapters/index.mjs @@ -3,6 +3,7 @@ export { runDispatcherAdapter } from './dispatcher.mjs'; export { runBridgeSupervisorAdapter } from './bridge-supervisor.mjs'; export { runSenderBindingAdapter } from './sender-binding.mjs'; export { runOrchestratorAdapter } from './orchestrator.mjs'; +export { parseOrchestratorCliArgs, formatOrchestratorHelp, runWatchdogAutoNotifyOrchestrator, runOrchestratorCli } from './orchestrator-cli.mjs'; export { createRuntimeBinding } from './runtime-binding.mjs'; export { loadDeploymentProfileArtifact, createDeploymentBindingContract } from '../storage/profile-artifact.mjs'; diff --git a/plugins/reporting-governance/src/adapters/orchestrator-cli.mjs b/plugins/reporting-governance/src/adapters/orchestrator-cli.mjs new file mode 100644 index 0000000..b0f9d9d --- /dev/null +++ b/plugins/reporting-governance/src/adapters/orchestrator-cli.mjs @@ -0,0 +1,225 @@ +import path from 'node:path'; +import process from 'node:process'; +import { fileURLToPath } from 'node:url'; +import { spawnSync } from 'node:child_process'; + +const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..'); +const DEFAULT_STATE_PATH = path.join(packageRoot, 'memory', 'watchdog-state.json'); +const DEFAULT_EVIDENCE_DIR = path.join(packageRoot, 'state', 'long-task-watchdog'); +const DEFAULT_EVENT_DIR = path.join(packageRoot, 'state', 'long-task-watchdog-events'); +const DEFAULT_QUEUE_DIR = path.join(packageRoot, 'state', 'operator-notify-queue'); +const DEFAULT_SPOOL_DIR = path.join(packageRoot, 'state', 'operator-notify-dispatch-spool'); +const DEFAULT_RECEIPT_DIR = path.join(packageRoot, 'state', 'operator-notify-bridge-receipts'); +const DEFAULT_WATCHDOG_SCRIPT = path.join(packageRoot, 'scripts', 'long_task_watchdog.mjs'); +const DEFAULT_DISPATCHER_SCRIPT = path.join(packageRoot, 'scripts', 'operator_notify_dispatcher.mjs'); +const DEFAULT_SUPERVISOR_SCRIPT = path.join(packageRoot, 'scripts', 'operator_notify_bridge_supervisor.mjs'); +const DEFAULT_SENDER_BINDING_SCRIPT = path.join(packageRoot, 'scripts', 'operator_notify_sender_binding.mjs'); + +export function parseOrchestratorCliArgs(argv) { + const args = { + state: DEFAULT_STATE_PATH, + evidenceDir: DEFAULT_EVIDENCE_DIR, + eventDir: DEFAULT_EVENT_DIR, + queueDir: DEFAULT_QUEUE_DIR, + spoolDir: DEFAULT_SPOOL_DIR, + receiptDir: DEFAULT_RECEIPT_DIR, + watchdogScript: DEFAULT_WATCHDOG_SCRIPT, + dispatcherScript: DEFAULT_DISPATCHER_SCRIPT, + supervisorScript: DEFAULT_SUPERVISOR_SCRIPT, + senderCommand: null, + senderMode: null, + openclawBin: 'openclaw', + now: null, + compact: false, + writeState: false, + claim: false, + dryRun: false, + help: false, + }; + + for (let i = 0; i < argv.length; i += 1) { + const token = argv[i]; + if (token === '--compact') { args.compact = true; continue; } + if (token === '--write-state') { args.writeState = true; continue; } + if (token === '--claim') { args.claim = true; continue; } + if (token === '--dry-run') { args.dryRun = true; continue; } + if (token === '--help' || token === '-h') { args.help = true; continue; } + + const pairs = [ + ['--state', 'state'], + ['--evidence-dir', 'evidenceDir'], + ['--event-dir', 'eventDir'], + ['--queue-dir', 'queueDir'], + ['--spool-dir', 'spoolDir'], + ['--receipt-dir', 'receiptDir'], + ['--watchdog-script', 'watchdogScript'], + ['--dispatcher-script', 'dispatcherScript'], + ['--supervisor-script', 'supervisorScript'], + ['--sender-command', 'senderCommand'], + ['--sender-mode', 'senderMode'], + ['--openclaw-bin', 'openclawBin'], + ['--now', 'now'], + ]; + + let matched = false; + for (const [flag, key] of pairs) { + if (token === flag) { + args[key] = argv[i + 1] ?? args[key]; + i += 1; + matched = true; + break; + } + if (token.startsWith(`${flag}=`)) { + args[key] = token.slice(flag.length + 1) || args[key]; + matched = true; + break; + } + } + if (matched) continue; + } + + return args; +} + +export function formatOrchestratorHelp({ invocation = 'node scripts/watchdog_auto_notify_orchestrator.mjs', description } = {}) { + return [ + 'Usage:', + ` ${invocation} [--write-state] [--claim] [--dry-run] [--sender-command ] [--sender-mode shim|openclaw-cli] [--openclaw-bin ] [--now ] [--compact]`, + '', + description ?? 'Runs the full watchdog auto-notify chain in order:', + ' runner -> queue -> dispatcher -> bridge -> sender -> ack|blocked', + '', + 'If --sender-mode is given and --sender-command is omitted, a sender-binding command is constructed automatically.', + ].join('\n'); +} + +export function printOrchestratorHelp(options = {}) { + process.stdout.write(`${formatOrchestratorHelp(options)}\n`); +} + +export function buildSenderCommand(args) { + if (args.senderCommand) return args.senderCommand; + if (!args.senderMode) return null; + const cmd = [ + JSON.stringify(process.execPath), + JSON.stringify(DEFAULT_SENDER_BINDING_SCRIPT), + '--mode', JSON.stringify(args.senderMode), + '--openclaw-bin', JSON.stringify(args.openclawBin), + '--compact', + ]; + return cmd.join(' '); +} + +export function runNodeScript(scriptPath, scriptArgs) { + return spawnSync(process.execPath, [scriptPath, ...scriptArgs], { + cwd: packageRoot, + encoding: 'utf8', + }); +} + +export function parseJsonOutput(label, result) { + const stdout = result.stdout ?? ''; + try { + return stdout.trim() ? JSON.parse(stdout) : null; + } catch (error) { + throw new Error(`${label} emitted non-JSON stdout: ${error instanceof Error ? error.message : String(error)}`); + } +} + +export function ensureSuccess(label, result) { + if (result.status !== 0) { + throw new Error(`${label} failed with status ${result.status ?? 'null'}: ${(result.stderr ?? '').trim() || '(no stderr)'}`); + } +} + +export function runWatchdogAutoNotifyOrchestrator(args) { + const senderCommand = buildSenderCommand(args); + + const watchdogArgs = [ + '--state', path.resolve(args.state), + '--evidence-dir', path.resolve(args.evidenceDir), + '--event-dir', path.resolve(args.eventDir), + '--notification-dir', path.resolve(args.queueDir), + '--compact', + ]; + if (args.writeState) watchdogArgs.push('--write-state'); + if (args.now) watchdogArgs.push('--now', args.now); + const watchdog = runNodeScript(path.resolve(args.watchdogScript), watchdogArgs); + ensureSuccess('watchdog runner', watchdog); + const watchdogPayload = parseJsonOutput('watchdog runner', watchdog); + + const dispatcherArgs = [ + '--queue-dir', path.resolve(args.queueDir), + '--spool-dir', path.resolve(args.spoolDir), + '--compact', + ]; + if (args.claim) dispatcherArgs.push('--claim'); + if (args.now) dispatcherArgs.push('--now', args.now); + const dispatcher = runNodeScript(path.resolve(args.dispatcherScript), dispatcherArgs); + ensureSuccess('dispatcher', dispatcher); + const dispatcherPayload = parseJsonOutput('dispatcher', dispatcher); + + const supervisorArgs = [ + '--queue-dir', path.resolve(args.queueDir), + '--spool-dir', path.resolve(args.spoolDir), + '--receipt-dir', path.resolve(args.receiptDir), + '--dispatcher-script', path.resolve(args.dispatcherScript), + '--compact', + ]; + if (args.dryRun) supervisorArgs.push('--dry-run'); + if (senderCommand) supervisorArgs.push('--sender-command', senderCommand); + if (args.now) supervisorArgs.push('--now', args.now); + const supervisor = runNodeScript(path.resolve(args.supervisorScript), supervisorArgs); + ensureSuccess('bridge supervisor', supervisor); + const supervisorPayload = parseJsonOutput('bridge supervisor', supervisor); + + return { + ok: true, + tool: 'watchdog_auto_notify_orchestrator', + version: 'mvp-v1', + now: args.now ?? null, + executionOrder: [ + 'runner', + 'queue', + 'dispatcher', + 'bridge', + senderCommand ? 'sender' : 'sender_unconfigured', + 'ack_or_blocked_or_pending', + ], + orchestration: { + script: path.resolve(import.meta.filename), + senderCommandConfigured: Boolean(senderCommand), + senderMode: args.senderMode ?? null, + dryRun: args.dryRun, + }, + result: { + watchdog: watchdogPayload?.result ?? null, + dispatcher: dispatcherPayload?.result ?? null, + supervisor: supervisorPayload?.result ?? null, + }, + }; +} + +export function runOrchestratorCli(argv = process.argv.slice(2), options = {}) { + const args = parseOrchestratorCliArgs(argv); + if (args.help) { + printOrchestratorHelp(options); + return 0; + } + + const payload = runWatchdogAutoNotifyOrchestrator(args); + process.stdout.write(`${JSON.stringify(payload, null, args.compact ? 0 : 2)}\n`); + return 0; +} + +export function main(argv = process.argv.slice(2), options = {}) { + try { + const exitCode = runOrchestratorCli(argv, options); + if (exitCode !== 0) process.exit(exitCode); + } catch (error) { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.exit(1); + } +} + +export { packageRoot }; diff --git a/plugins/reporting-governance/test/package-structure.test.mjs b/plugins/reporting-governance/test/package-structure.test.mjs index 8a5ee1e..a699c23 100644 --- a/plugins/reporting-governance/test/package-structure.test.mjs +++ b/plugins/reporting-governance/test/package-structure.test.mjs @@ -18,6 +18,7 @@ const requiredPaths = [ 'src/adapters/bridge-supervisor.mjs', 'src/adapters/sender-binding.mjs', 'src/adapters/orchestrator.mjs', + 'src/adapters/orchestrator-cli.mjs', 'src/storage', 'src/storage/profile-artifact.mjs', 'src/storage/decision-artifact.mjs', diff --git a/scripts/test_watchdog_auto_notify_orchestrator_shim_regression.mjs b/scripts/test_watchdog_auto_notify_orchestrator_shim_regression.mjs new file mode 100644 index 0000000..3ef9594 --- /dev/null +++ b/scripts/test_watchdog_auto_notify_orchestrator_shim_regression.mjs @@ -0,0 +1,127 @@ +#!/usr/bin/env node + +import assert from 'node:assert/strict'; +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import process from 'node:process'; +import { spawnSync } from 'node:child_process'; + +const ROOT_DIR = path.resolve(import.meta.dirname, '..'); +const REPO_SHIM = path.join(ROOT_DIR, 'scripts', 'watchdog_auto_notify_orchestrator.mjs'); +const PACKAGE_ENTRY = path.join(ROOT_DIR, 'plugins', 'reporting-governance', 'scripts', 'watchdog_auto_notify_orchestrator.mjs'); + +function createFixture() { + const fixtureRoot = mkdtempSync(path.join(tmpdir(), 'watchdog-auto-notify-shim-regression-')); + const statePath = path.join(fixtureRoot, 'watchdog-state.json'); + const evidenceDir = path.join(fixtureRoot, 'evidence'); + const eventDir = path.join(fixtureRoot, 'events'); + const queueDir = path.join(fixtureRoot, 'queue'); + const spoolDir = path.join(fixtureRoot, 'spool'); + const receiptDir = path.join(fixtureRoot, 'receipts'); + [evidenceDir, eventDir, queueDir, spoolDir, receiptDir].forEach((dir) => mkdirSync(dir, { recursive: true })); + + writeFileSync(statePath, `${JSON.stringify({ + version: 1, + watchdogs: [{ + id: 'reporting-governance-plugin-watchdog', + task: 'reporting-governance plugin spec development', + status: 'active', + ownerSessionKey: 'agent:coder:main', + reportChannel: 'telegram', + reportTarget: '864811879', + intervalMinutes: 10, + lastMilestoneAt: '2026-05-07T08:00:00.000Z', + lastAlertAt: null, + }], + }, null, 2)}\n`, 'utf8'); + + return { fixtureRoot, statePath, evidenceDir, eventDir, queueDir, spoolDir, receiptDir }; +} + +function run(script, args = []) { + const result = spawnSync(process.execPath, [script, ...args], { + cwd: ROOT_DIR, + encoding: 'utf8', + }); + return { + status: result.status, + stdout: result.stdout ?? '', + stderr: result.stderr ?? '', + }; +} + +const tests = []; +function test(name, fn) { tests.push({ name, fn }); } +function printResult(prefix, name, detail = '') { process.stdout.write(`${prefix} ${name}${detail ? ` ${detail}` : ''}\n`); } + +function normalizeStdout(stdout) { + const payload = JSON.parse(stdout); + delete payload.orchestration?.script; + return payload; +} + +function buildArgs(fixture) { + return [ + '--state', fixture.statePath, + '--evidence-dir', fixture.evidenceDir, + '--event-dir', fixture.eventDir, + '--queue-dir', fixture.queueDir, + '--spool-dir', fixture.spoolDir, + '--receipt-dir', fixture.receiptDir, + '--write-state', + '--sender-command', `node -e "process.stdout.write(JSON.stringify({state:'sent'}))"`, + '--now', '2026-05-07T08:20:00.000Z', + '--compact', + ]; +} + +test('repo-root shim forwards help text and exits like package entrypoint', () => { + const shim = run(REPO_SHIM, ['--help']); + const pkg = run(PACKAGE_ENTRY, ['--help']); + assert.equal(shim.status, 0); + assert.equal(pkg.status, 0); + assert.equal(shim.stderr, ''); + assert.equal(pkg.stderr, ''); + assert.equal(shim.stdout, pkg.stdout); +}); + +test('repo-root shim forwards args and preserves success payload semantics', () => { + const shimFixture = createFixture(); + const pkgFixture = createFixture(); + try { + const shim = run(REPO_SHIM, buildArgs(shimFixture)); + const pkg = run(PACKAGE_ENTRY, buildArgs(pkgFixture)); + assert.equal(shim.status, 0, shim.stderr); + assert.equal(pkg.status, 0, pkg.stderr); + assert.deepEqual(normalizeStdout(shim.stdout), normalizeStdout(pkg.stdout)); + } finally { + rmSync(shimFixture.fixtureRoot, { recursive: true, force: true }); + rmSync(pkgFixture.fixtureRoot, { recursive: true, force: true }); + } +}); + +test('repo-root shim preserves non-zero exit semantics from package core', () => { + const shim = run(REPO_SHIM, ['--watchdog-script', path.join(ROOT_DIR, 'missing-watchdog-script.mjs'), '--compact']); + const pkg = run(PACKAGE_ENTRY, ['--watchdog-script', path.join(ROOT_DIR, 'missing-watchdog-script.mjs'), '--compact']); + assert.equal(shim.status, 1); + assert.equal(pkg.status, 1); + assert.equal(shim.stdout, ''); + assert.equal(pkg.stdout, ''); + assert.equal(shim.stderr, pkg.stderr); +}); + +let failures = 0; +for (const { name, fn } of tests) { + try { + fn(); + printResult('ok', name); + } catch (error) { + failures += 1; + printResult('not ok', name, `- ${error instanceof Error ? error.message : String(error)}`); + } +} + +if (failures > 0) { + process.exit(1); +} diff --git a/scripts/watchdog_auto_notify_orchestrator.mjs b/scripts/watchdog_auto_notify_orchestrator.mjs index cce1877..a1514b1 100644 --- a/scripts/watchdog_auto_notify_orchestrator.mjs +++ b/scripts/watchdog_auto_notify_orchestrator.mjs @@ -1,94 +1,6 @@ #!/usr/bin/env node import process from 'node:process'; -import { runOrchestratorAdapter } from '../plugins/reporting-governance/src/adapters/orchestrator.mjs'; +import { main } from '../plugins/reporting-governance/src/adapters/orchestrator-cli.mjs'; -const cliArgs = parseCliArgs(process.argv.slice(2)); - -try { - const payload = runOrchestratorAdapter({ - compact: cliArgs.compact, - ...cliArgs, - }); - process.stdout.write(`${JSON.stringify(payload, null, cliArgs.compact ? 0 : 2)}\n`); -} catch (error) { - process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); - process.exit(1); -} - -function parseCliArgs(argv) { - const args = { - state: null, - evidenceDir: null, - eventDir: null, - queueDir: null, - spoolDir: null, - receiptDir: null, - watchdogScript: null, - dispatcherScript: null, - supervisorScript: null, - senderCommand: null, - senderMode: null, - openclawBin: 'openclaw', - now: null, - compact: false, - writeState: false, - claim: false, - dryRun: false, - help: false, - }; - - for (let i = 0; i < argv.length; i += 1) { - const token = argv[i]; - if (token === '--compact') { args.compact = true; continue; } - if (token === '--write-state') { args.writeState = true; continue; } - if (token === '--claim') { args.claim = true; continue; } - if (token === '--dry-run') { args.dryRun = true; continue; } - if (token === '--help' || token === '-h') { args.help = true; continue; } - - const pairs = [ - ['--state', 'state'], - ['--evidence-dir', 'evidenceDir'], - ['--event-dir', 'eventDir'], - ['--queue-dir', 'queueDir'], - ['--spool-dir', 'spoolDir'], - ['--receipt-dir', 'receiptDir'], - ['--watchdog-script', 'watchdogScript'], - ['--dispatcher-script', 'dispatcherScript'], - ['--supervisor-script', 'supervisorScript'], - ['--sender-command', 'senderCommand'], - ['--sender-mode', 'senderMode'], - ['--openclaw-bin', 'openclawBin'], - ['--now', 'now'], - ]; - - let matched = false; - for (const [flag, key] of pairs) { - if (token === flag) { - args[key] = argv[i + 1] ?? args[key]; - i += 1; - matched = true; - break; - } - if (token.startsWith(`${flag}=`)) { - args[key] = token.slice(flag.length + 1) || args[key]; - matched = true; - break; - } - } - if (matched) continue; - } - - if (args.help) { - process.stdout.write([ - 'Usage:', - ' node scripts/watchdog_auto_notify_orchestrator.mjs [--write-state] [--claim] [--dry-run] [--sender-command ] [--sender-mode shim|openclaw-cli] [--openclaw-bin ] [--now ] [--compact]', - '', - 'Repo-root shim that forwards to package-owned orchestrator implementation.', - 'Default runtime binding now resolves package script first; env/override can still point elsewhere.', - ].join('\n') + '\n'); - process.exit(0); - } - - return args; -} +main(process.argv.slice(2));