oh-my-super-agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/agents/advisor.md +15 -0
- package/.agents/agents/conductor.md +32 -0
- package/.agents/agents/dreamer.md +13 -0
- package/.agents/agents/executor.md +17 -0
- package/.agents/agents/grader.md +15 -0
- package/.agents/config/endurance-policy.json +8 -0
- package/.agents/config/oms-config.json +119 -0
- package/.agents/config/release-policy.json +12 -0
- package/.agents/config/secret-scan-policy.json +7 -0
- package/.agents/config/verification-plan.json +33 -0
- package/.agents/registry.json +11 -0
- package/.agents/rules/constitution.md +7 -0
- package/.agents/skills/conductor/SKILL.md +23 -0
- package/.agents/skills/dream/SKILL.md +14 -0
- package/.agents/skills/executor/SKILL.md +13 -0
- package/.agents/skills/oms/SKILL.md +52 -0
- package/.agents/skills/quota-aware/SKILL.md +12 -0
- package/.agents/skills/schedule/SKILL.md +13 -0
- package/.agents/skills/team-protocol/SKILL.md +20 -0
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/src/audit/run-ledger.d.ts +183 -0
- package/dist/src/audit/run-ledger.js +1655 -0
- package/dist/src/audit/run-ledger.js.map +1 -0
- package/dist/src/cli.d.ts +61 -0
- package/dist/src/cli.js +2096 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/github-queue-probe.d.ts +26 -0
- package/dist/src/commands/github-queue-probe.js +51 -0
- package/dist/src/commands/github-queue-probe.js.map +1 -0
- package/dist/src/commands/trace-fixture.d.ts +20 -0
- package/dist/src/commands/trace-fixture.js +72 -0
- package/dist/src/commands/trace-fixture.js.map +1 -0
- package/dist/src/conductor/activity.d.ts +46 -0
- package/dist/src/conductor/activity.js +224 -0
- package/dist/src/conductor/activity.js.map +1 -0
- package/dist/src/conductor/bridge.d.ts +53 -0
- package/dist/src/conductor/bridge.js +652 -0
- package/dist/src/conductor/bridge.js.map +1 -0
- package/dist/src/conductor/notifications.d.ts +59 -0
- package/dist/src/conductor/notifications.js +381 -0
- package/dist/src/conductor/notifications.js.map +1 -0
- package/dist/src/conductor/recovery.d.ts +29 -0
- package/dist/src/conductor/recovery.js +198 -0
- package/dist/src/conductor/recovery.js.map +1 -0
- package/dist/src/conductor/start.d.ts +73 -0
- package/dist/src/conductor/start.js +519 -0
- package/dist/src/conductor/start.js.map +1 -0
- package/dist/src/conductor/waiter.d.ts +88 -0
- package/dist/src/conductor/waiter.js +427 -0
- package/dist/src/conductor/waiter.js.map +1 -0
- package/dist/src/config.d.ts +9 -0
- package/dist/src/config.js +40 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/dispatch/advice-source.d.ts +61 -0
- package/dist/src/dispatch/advice-source.js +198 -0
- package/dist/src/dispatch/advice-source.js.map +1 -0
- package/dist/src/dispatch/advice-store.d.ts +41 -0
- package/dist/src/dispatch/advice-store.js +201 -0
- package/dist/src/dispatch/advice-store.js.map +1 -0
- package/dist/src/dispatch/advice.d.ts +34 -0
- package/dist/src/dispatch/advice.js +371 -0
- package/dist/src/dispatch/advice.js.map +1 -0
- package/dist/src/dispatch/authority.d.ts +124 -0
- package/dist/src/dispatch/authority.js +1202 -0
- package/dist/src/dispatch/authority.js.map +1 -0
- package/dist/src/dispatch/intent.d.ts +66 -0
- package/dist/src/dispatch/intent.js +71 -0
- package/dist/src/dispatch/intent.js.map +1 -0
- package/dist/src/dispatch/runtime-authority.d.ts +42 -0
- package/dist/src/dispatch/runtime-authority.js +52 -0
- package/dist/src/dispatch/runtime-authority.js.map +1 -0
- package/dist/src/doctor.d.ts +20 -0
- package/dist/src/doctor.js +85 -0
- package/dist/src/doctor.js.map +1 -0
- package/dist/src/domain/records.d.ts +44 -0
- package/dist/src/domain/records.js +138 -0
- package/dist/src/domain/records.js.map +1 -0
- package/dist/src/domain/role-policy.d.ts +67 -0
- package/dist/src/domain/role-policy.js +547 -0
- package/dist/src/domain/role-policy.js.map +1 -0
- package/dist/src/events/bus.d.ts +32 -0
- package/dist/src/events/bus.js +115 -0
- package/dist/src/events/bus.js.map +1 -0
- package/dist/src/events/kinds.d.ts +126 -0
- package/dist/src/events/kinds.js +14 -0
- package/dist/src/events/kinds.js.map +1 -0
- package/dist/src/evidence/immutable-file.d.ts +12 -0
- package/dist/src/evidence/immutable-file.js +141 -0
- package/dist/src/evidence/immutable-file.js.map +1 -0
- package/dist/src/evidence/store.d.ts +125 -0
- package/dist/src/evidence/store.js +1189 -0
- package/dist/src/evidence/store.js.map +1 -0
- package/dist/src/evidence/verdict.d.ts +17 -0
- package/dist/src/evidence/verdict.js +23 -0
- package/dist/src/evidence/verdict.js.map +1 -0
- package/dist/src/generation/agent.d.ts +5 -0
- package/dist/src/generation/agent.js +34 -0
- package/dist/src/generation/agent.js.map +1 -0
- package/dist/src/generation/frontmatter.d.ts +7 -0
- package/dist/src/generation/frontmatter.js +33 -0
- package/dist/src/generation/frontmatter.js.map +1 -0
- package/dist/src/github/adapter.d.ts +44 -0
- package/dist/src/github/adapter.js +592 -0
- package/dist/src/github/adapter.js.map +1 -0
- package/dist/src/github/dispatch-eligibility.d.ts +9 -0
- package/dist/src/github/dispatch-eligibility.js +94 -0
- package/dist/src/github/dispatch-eligibility.js.map +1 -0
- package/dist/src/github/projector.d.ts +23 -0
- package/dist/src/github/projector.js +181 -0
- package/dist/src/github/projector.js.map +1 -0
- package/dist/src/github/review.d.ts +39 -0
- package/dist/src/github/review.js +1450 -0
- package/dist/src/github/review.js.map +1 -0
- package/dist/src/github/sync.d.ts +21 -0
- package/dist/src/github/sync.js +518 -0
- package/dist/src/github/sync.js.map +1 -0
- package/dist/src/github/types.d.ts +134 -0
- package/dist/src/github/types.js +11 -0
- package/dist/src/github/types.js.map +1 -0
- package/dist/src/goals/artifact-store.d.ts +31 -0
- package/dist/src/goals/artifact-store.js +241 -0
- package/dist/src/goals/artifact-store.js.map +1 -0
- package/dist/src/goals/authority.d.ts +112 -0
- package/dist/src/goals/authority.js +621 -0
- package/dist/src/goals/authority.js.map +1 -0
- package/dist/src/goals/native-snapshot.d.ts +12 -0
- package/dist/src/goals/native-snapshot.js +53 -0
- package/dist/src/goals/native-snapshot.js.map +1 -0
- package/dist/src/goals/runtime-authority.d.ts +7 -0
- package/dist/src/goals/runtime-authority.js +20 -0
- package/dist/src/goals/runtime-authority.js.map +1 -0
- package/dist/src/health/release-assessment.d.ts +11 -0
- package/dist/src/health/release-assessment.js +83 -0
- package/dist/src/health/release-assessment.js.map +1 -0
- package/dist/src/health/release-policy.d.ts +16 -0
- package/dist/src/health/release-policy.js +40 -0
- package/dist/src/health/release-policy.js.map +1 -0
- package/dist/src/hooks/native.d.ts +30 -0
- package/dist/src/hooks/native.js +495 -0
- package/dist/src/hooks/native.js.map +1 -0
- package/dist/src/hooks/provider-runtime.d.ts +55 -0
- package/dist/src/hooks/provider-runtime.js +479 -0
- package/dist/src/hooks/provider-runtime.js.map +1 -0
- package/dist/src/hooks/provider.d.ts +102 -0
- package/dist/src/hooks/provider.js +358 -0
- package/dist/src/hooks/provider.js.map +1 -0
- package/dist/src/live/contact-journal.d.ts +73 -0
- package/dist/src/live/contact-journal.js +439 -0
- package/dist/src/live/contact-journal.js.map +1 -0
- package/dist/src/live-probes/gh-adapter.d.ts +28 -0
- package/dist/src/live-probes/gh-adapter.js +188 -0
- package/dist/src/live-probes/gh-adapter.js.map +1 -0
- package/dist/src/live-probes/github-queue.d.ts +1 -0
- package/dist/src/live-probes/github-queue.js +2 -0
- package/dist/src/live-probes/github-queue.js.map +1 -0
- package/dist/src/notifications/slack-request.d.ts +13 -0
- package/dist/src/notifications/slack-request.js +40 -0
- package/dist/src/notifications/slack-request.js.map +1 -0
- package/dist/src/outbox/processor.d.ts +27 -0
- package/dist/src/outbox/processor.js +63 -0
- package/dist/src/outbox/processor.js.map +1 -0
- package/dist/src/outbox/store.d.ts +158 -0
- package/dist/src/outbox/store.js +834 -0
- package/dist/src/outbox/store.js.map +1 -0
- package/dist/src/package-root.d.ts +1 -0
- package/dist/src/package-root.js +26 -0
- package/dist/src/package-root.js.map +1 -0
- package/dist/src/project.d.ts +12 -0
- package/dist/src/project.js +60 -0
- package/dist/src/project.js.map +1 -0
- package/dist/src/projection/generated-file.d.ts +15 -0
- package/dist/src/projection/generated-file.js +72 -0
- package/dist/src/projection/generated-file.js.map +1 -0
- package/dist/src/projection/project-workspace.d.ts +11 -0
- package/dist/src/projection/project-workspace.js +94 -0
- package/dist/src/projection/project-workspace.js.map +1 -0
- package/dist/src/projection/registry.d.ts +15 -0
- package/dist/src/projection/registry.js +59 -0
- package/dist/src/projection/registry.js.map +1 -0
- package/dist/src/projection/safe-path.d.ts +4 -0
- package/dist/src/projection/safe-path.js +106 -0
- package/dist/src/projection/safe-path.js.map +1 -0
- package/dist/src/providers/claude-interactive.d.ts +38 -0
- package/dist/src/providers/claude-interactive.js +287 -0
- package/dist/src/providers/claude-interactive.js.map +1 -0
- package/dist/src/providers/claude-structured-bridge.d.ts +32 -0
- package/dist/src/providers/claude-structured-bridge.js +215 -0
- package/dist/src/providers/claude-structured-bridge.js.map +1 -0
- package/dist/src/providers/claude-structured-completion.d.ts +23 -0
- package/dist/src/providers/claude-structured-completion.js +229 -0
- package/dist/src/providers/claude-structured-completion.js.map +1 -0
- package/dist/src/providers/claude-structured-pane.d.ts +23 -0
- package/dist/src/providers/claude-structured-pane.js +156 -0
- package/dist/src/providers/claude-structured-pane.js.map +1 -0
- package/dist/src/providers/claude-structured-store.d.ts +48 -0
- package/dist/src/providers/claude-structured-store.js +212 -0
- package/dist/src/providers/claude-structured-store.js.map +1 -0
- package/dist/src/providers/claude-structured.d.ts +56 -0
- package/dist/src/providers/claude-structured.js +250 -0
- package/dist/src/providers/claude-structured.js.map +1 -0
- package/dist/src/providers/codex-exec.d.ts +29 -0
- package/dist/src/providers/codex-exec.js +127 -0
- package/dist/src/providers/codex-exec.js.map +1 -0
- package/dist/src/providers/grading.d.ts +2 -0
- package/dist/src/providers/grading.js +20 -0
- package/dist/src/providers/grading.js.map +1 -0
- package/dist/src/providers/native-dispatch.d.ts +24 -0
- package/dist/src/providers/native-dispatch.js +34 -0
- package/dist/src/providers/native-dispatch.js.map +1 -0
- package/dist/src/providers/preflight.d.ts +65 -0
- package/dist/src/providers/preflight.js +256 -0
- package/dist/src/providers/preflight.js.map +1 -0
- package/dist/src/providers/process.d.ts +18 -0
- package/dist/src/providers/process.js +21 -0
- package/dist/src/providers/process.js.map +1 -0
- package/dist/src/queue/model.d.ts +37 -0
- package/dist/src/queue/model.js +51 -0
- package/dist/src/queue/model.js.map +1 -0
- package/dist/src/queue/service.d.ts +1 -0
- package/dist/src/queue/service.js +2 -0
- package/dist/src/queue/service.js.map +1 -0
- package/dist/src/reflections/store.d.ts +21 -0
- package/dist/src/reflections/store.js +60 -0
- package/dist/src/reflections/store.js.map +1 -0
- package/dist/src/reports/daily-trust.d.ts +11 -0
- package/dist/src/reports/daily-trust.js +98 -0
- package/dist/src/reports/daily-trust.js.map +1 -0
- package/dist/src/runs/controller.d.ts +187 -0
- package/dist/src/runs/controller.js +1182 -0
- package/dist/src/runs/controller.js.map +1 -0
- package/dist/src/runtime/composition.d.ts +9 -0
- package/dist/src/runtime/composition.js +35 -0
- package/dist/src/runtime/composition.js.map +1 -0
- package/dist/src/runtime/endurance-authorities.d.ts +44 -0
- package/dist/src/runtime/endurance-authorities.js +101 -0
- package/dist/src/runtime/endurance-authorities.js.map +1 -0
- package/dist/src/runtime/endurance-policy.d.ts +12 -0
- package/dist/src/runtime/endurance-policy.js +13 -0
- package/dist/src/runtime/endurance-policy.js.map +1 -0
- package/dist/src/runtime/lock.d.ts +63 -0
- package/dist/src/runtime/lock.js +577 -0
- package/dist/src/runtime/lock.js.map +1 -0
- package/dist/src/runtime/process-identity.d.ts +10 -0
- package/dist/src/runtime/process-identity.js +43 -0
- package/dist/src/runtime/process-identity.js.map +1 -0
- package/dist/src/runtime/quota.d.ts +130 -0
- package/dist/src/runtime/quota.js +590 -0
- package/dist/src/runtime/quota.js.map +1 -0
- package/dist/src/runtime/record-store.d.ts +45 -0
- package/dist/src/runtime/record-store.js +272 -0
- package/dist/src/runtime/record-store.js.map +1 -0
- package/dist/src/runtime/schema.d.ts +28 -0
- package/dist/src/runtime/schema.js +194 -0
- package/dist/src/runtime/schema.js.map +1 -0
- package/dist/src/runtime/stable-id.d.ts +3 -0
- package/dist/src/runtime/stable-id.js +27 -0
- package/dist/src/runtime/stable-id.js.map +1 -0
- package/dist/src/runtime/system-clock.d.ts +8 -0
- package/dist/src/runtime/system-clock.js +35 -0
- package/dist/src/runtime/system-clock.js.map +1 -0
- package/dist/src/schedule/service-plan.d.ts +58 -0
- package/dist/src/schedule/service-plan.js +327 -0
- package/dist/src/schedule/service-plan.js.map +1 -0
- package/dist/src/schedule/user-installation.d.ts +62 -0
- package/dist/src/schedule/user-installation.js +701 -0
- package/dist/src/schedule/user-installation.js.map +1 -0
- package/dist/src/schedule/user-plan.d.ts +47 -0
- package/dist/src/schedule/user-plan.js +286 -0
- package/dist/src/schedule/user-plan.js.map +1 -0
- package/dist/src/scheduler/minute.d.ts +31 -0
- package/dist/src/scheduler/minute.js +441 -0
- package/dist/src/scheduler/minute.js.map +1 -0
- package/dist/src/schemas/registry.d.ts +10 -0
- package/dist/src/schemas/registry.js +36 -0
- package/dist/src/schemas/registry.js.map +1 -0
- package/dist/src/security/secret-scan.d.ts +50 -0
- package/dist/src/security/secret-scan.js +384 -0
- package/dist/src/security/secret-scan.js.map +1 -0
- package/dist/src/sessions/registry.d.ts +89 -0
- package/dist/src/sessions/registry.js +443 -0
- package/dist/src/sessions/registry.js.map +1 -0
- package/dist/src/state.d.ts +27 -0
- package/dist/src/state.js +235 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/status/hud.d.ts +37 -0
- package/dist/src/status/hud.js +88 -0
- package/dist/src/status/hud.js.map +1 -0
- package/dist/src/team/plan.d.ts +31 -0
- package/dist/src/team/plan.js +184 -0
- package/dist/src/team/plan.js.map +1 -0
- package/dist/src/team/projection.d.ts +24 -0
- package/dist/src/team/projection.js +79 -0
- package/dist/src/team/projection.js.map +1 -0
- package/dist/src/tickets/admission.d.ts +34 -0
- package/dist/src/tickets/admission.js +169 -0
- package/dist/src/tickets/admission.js.map +1 -0
- package/dist/src/tickets/materializer.d.ts +1 -0
- package/dist/src/tickets/materializer.js +2 -0
- package/dist/src/tickets/materializer.js.map +1 -0
- package/dist/src/tickets/runtime-authority.d.ts +32 -0
- package/dist/src/tickets/runtime-authority.js +46 -0
- package/dist/src/tickets/runtime-authority.js.map +1 -0
- package/dist/src/tickets/store.d.ts +221 -0
- package/dist/src/tickets/store.js +2025 -0
- package/dist/src/tickets/store.js.map +1 -0
- package/dist/src/tracer/one-ticket.d.ts +58 -0
- package/dist/src/tracer/one-ticket.js +578 -0
- package/dist/src/tracer/one-ticket.js.map +1 -0
- package/dist/src/trust/assessment.d.ts +24 -0
- package/dist/src/trust/assessment.js +124 -0
- package/dist/src/trust/assessment.js.map +1 -0
- package/dist/src/trust/runs.d.ts +13 -0
- package/dist/src/trust/runs.js +46 -0
- package/dist/src/trust/runs.js.map +1 -0
- package/dist/src/types.d.ts +6 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/fs.d.ts +13 -0
- package/dist/src/utils/fs.js +188 -0
- package/dist/src/utils/fs.js.map +1 -0
- package/dist/src/verification/authority.d.ts +28 -0
- package/dist/src/verification/authority.js +153 -0
- package/dist/src/verification/authority.js.map +1 -0
- package/dist/src/verification/grade-source.d.ts +17 -0
- package/dist/src/verification/grade-source.js +30 -0
- package/dist/src/verification/grade-source.js.map +1 -0
- package/dist/src/verification/plan.d.ts +20 -0
- package/dist/src/verification/plan.js +128 -0
- package/dist/src/verification/plan.js.map +1 -0
- package/dist/src/verification/protected-runner.d.ts +50 -0
- package/dist/src/verification/protected-runner.js +171 -0
- package/dist/src/verification/protected-runner.js.map +1 -0
- package/dist/src/verification/result-gate-store.d.ts +72 -0
- package/dist/src/verification/result-gate-store.js +193 -0
- package/dist/src/verification/result-gate-store.js.map +1 -0
- package/dist/src/verification/result-gate.d.ts +41 -0
- package/dist/src/verification/result-gate.js +731 -0
- package/dist/src/verification/result-gate.js.map +1 -0
- package/dist/src/verification/runner.d.ts +38 -0
- package/dist/src/verification/runner.js +143 -0
- package/dist/src/verification/runner.js.map +1 -0
- package/dist/src/verification/sandbox.d.ts +16 -0
- package/dist/src/verification/sandbox.js +199 -0
- package/dist/src/verification/sandbox.js.map +1 -0
- package/dist/src/verify.d.ts +8 -0
- package/dist/src/verify.js +35 -0
- package/dist/src/verify.js.map +1 -0
- package/dist/src/watchdog/reconcile.d.ts +42 -0
- package/dist/src/watchdog/reconcile.js +440 -0
- package/dist/src/watchdog/reconcile.js.map +1 -0
- package/dist/src/workers/bridge.d.ts +35 -0
- package/dist/src/workers/bridge.js +426 -0
- package/dist/src/workers/bridge.js.map +1 -0
- package/dist/src/workers/delivery.d.ts +31 -0
- package/dist/src/workers/delivery.js +876 -0
- package/dist/src/workers/delivery.js.map +1 -0
- package/dist/src/workers/panes.d.ts +32 -0
- package/dist/src/workers/panes.js +212 -0
- package/dist/src/workers/panes.js.map +1 -0
- package/dist/src/workers/registry.d.ts +80 -0
- package/dist/src/workers/registry.js +351 -0
- package/dist/src/workers/registry.js.map +1 -0
- package/dist/src/workers/runtime.d.ts +44 -0
- package/dist/src/workers/runtime.js +21 -0
- package/dist/src/workers/runtime.js.map +1 -0
- package/dist/src/worktrees/manager.d.ts +35 -0
- package/dist/src/worktrees/manager.js +315 -0
- package/dist/src/worktrees/manager.js.map +1 -0
- package/docs/ARCHITECTURE.md +323 -0
- package/docs/PLAN.md +216 -0
- package/docs/REFERENCES.md +42 -0
- package/docs/ROADMAP.md +327 -0
- package/docs/SECURITY.md +242 -0
- package/docs/adr/0001-tracer-first-file-backed-certification.md +41 -0
- package/docs/adr/0002-conductor-first-typed-dispatch.md +102 -0
- package/docs/adr/0003-mandatory-pre-dispatch-advice.md +59 -0
- package/docs/compatibility/claude-code-2.1.207.md +25 -0
- package/loop/contract.md +102 -0
- package/package.json +54 -0
- package/schemas/advisor-evidence.schema.json +54 -0
- package/schemas/advisor-output.schema.json +31 -0
- package/schemas/conductor-activity-payload.schema.json +38 -0
- package/schemas/conductor-decision.schema.json +29 -0
- package/schemas/conductor-dispatch-intent.schema.json +71 -0
- package/schemas/conductor-dispatch-preparation.schema.json +46 -0
- package/schemas/conductor-dispatch-proposal.schema.json +82 -0
- package/schemas/conductor-goal-proposal.schema.json +30 -0
- package/schemas/conductor-manage-worker.schema.json +17 -0
- package/schemas/conductor-notification-payload.schema.json +39 -0
- package/schemas/conductor-process-result.schema.json +14 -0
- package/schemas/conductor-route.schema.json +51 -0
- package/schemas/conductor-start-team.schema.json +16 -0
- package/schemas/conductor-waiter-payload.schema.json +68 -0
- package/schemas/deterministic-check.schema.json +107 -0
- package/schemas/dispatch-acknowledgement.schema.json +15 -0
- package/schemas/dispatch-advice-payload.schema.json +64 -0
- package/schemas/dispatch-payload.schema.json +131 -0
- package/schemas/dispatch-result.schema.json +28 -0
- package/schemas/endurance-policy.schema.json +16 -0
- package/schemas/events/dispatch-delivery-requested.schema.json +17 -0
- package/schemas/events/github-observed.schema.json +28 -0
- package/schemas/events/native-hook-observed.schema.json +35 -0
- package/schemas/events/provider-observed.schema.json +64 -0
- package/schemas/events/run-heartbeat-due.schema.json +10 -0
- package/schemas/events/slack-notification-requested.schema.json +14 -0
- package/schemas/events/structured-role-completed.schema.json +54 -0
- package/schemas/events/ticket-resume-due.schema.json +28 -0
- package/schemas/events/watchdog-lease-expired.schema.json +14 -0
- package/schemas/events/watchdog-outbox-stalled.schema.json +12 -0
- package/schemas/events/watchdog-session-stalled.schema.json +11 -0
- package/schemas/evidence-record.schema.json +37 -0
- package/schemas/executor-result.schema.json +17 -0
- package/schemas/github-observation.schema.json +33 -0
- package/schemas/github-watermark.schema.json +66 -0
- package/schemas/goal-artifact.schema.json +19 -0
- package/schemas/goal-create-input.schema.json +14 -0
- package/schemas/goal-executor-result.schema.json +18 -0
- package/schemas/goal-payload.schema.json +95 -0
- package/schemas/grader-output.schema.json +33 -0
- package/schemas/lease-expiry.schema.json +19 -0
- package/schemas/live-disclosure.schema.json +16 -0
- package/schemas/live-outcome.schema.json +52 -0
- package/schemas/native-goal-snapshot.schema.json +27 -0
- package/schemas/provider-observed.schema.json +64 -0
- package/schemas/quota-observation.schema.json +72 -0
- package/schemas/reflection-evidence.schema.json +61 -0
- package/schemas/reflection.schema.json +13 -0
- package/schemas/regression-request.schema.json +54 -0
- package/schemas/release-assessment.schema.json +34 -0
- package/schemas/release-policy.schema.json +30 -0
- package/schemas/result-gate-payload.schema.json +244 -0
- package/schemas/review-delivery-payload.schema.json +42 -0
- package/schemas/role-policy.schema.json +111 -0
- package/schemas/run-abort-input.schema.json +11 -0
- package/schemas/run-completion-proof.schema.json +62 -0
- package/schemas/run-ledger-entry.schema.json +78 -0
- package/schemas/run-manifest.schema.json +74 -0
- package/schemas/run-start-intent.schema.json +74 -0
- package/schemas/runtime-record.schema.json +111 -0
- package/schemas/service-plan.schema.json +35 -0
- package/schemas/session-payload.schema.json +80 -0
- package/schemas/structured-role-read-request.schema.json +14 -0
- package/schemas/structured-role-request.schema.json +44 -0
- package/schemas/structured-role-result.schema.json +38 -0
- package/schemas/structured-role-submit-result.schema.json +15 -0
- package/schemas/team-checkpoint.schema.json +42 -0
- package/schemas/team-plan.schema.json +30 -0
- package/schemas/ticket-content.schema.json +38 -0
- package/schemas/ticket-queue-payload.schema.json +191 -0
- package/schemas/trust-report.schema.json +68 -0
- package/schemas/trust-run.schema.json +47 -0
- package/schemas/ultragoal-checkpoint.schema.json +49 -0
- package/schemas/user-schedule-installation-v1.schema.json +32 -0
- package/schemas/user-schedule-installation.schema.json +38 -0
- package/schemas/user-schedule-plan.schema.json +39 -0
- package/schemas/verification-plan-evidence.schema.json +37 -0
- package/schemas/worker-acknowledge-dispatch.schema.json +12 -0
- package/schemas/worker-payload.schema.json +47 -0
- package/schemas/worker-read-dispatch.schema.json +11 -0
- package/schemas/worker-record-dispatch-result.schema.json +14 -0
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,2096 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { existsSync, lstatSync, readdirSync, readSync, realpathSync } from "node:fs";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { createInterface } from "node:readline/promises";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { runConductorBridge } from "./conductor/bridge.js";
|
|
8
|
+
import { inspectConductorRuntimeBinding, requireStoredConductorAuthorization, startConductor, wakeConductorWithNotification, } from "./conductor/start.js";
|
|
9
|
+
import { recoverConductorNotification } from "./conductor/recovery.js";
|
|
10
|
+
import { waitForConductorNotification } from "./conductor/waiter.js";
|
|
11
|
+
import { normalizeProviderHook, SUPPORTED_PROVIDER_HOOK_VERSIONS, } from "./hooks/provider.js";
|
|
12
|
+
import { createDurableProviderBindingResolver, publishAndProjectProviderObservations, } from "./hooks/provider-runtime.js";
|
|
13
|
+
import { runGithubQueueProbeCommand, } from "./commands/github-queue-probe.js";
|
|
14
|
+
import { runTraceFixture } from "./commands/trace-fixture.js";
|
|
15
|
+
import { ingestNativeHook, projectNativeHook } from "./hooks/native.js";
|
|
16
|
+
import { reconcileMinute } from "./scheduler/minute.js";
|
|
17
|
+
import { installUserSchedule, readInstalledUserSchedulePlanBinding, removeUserSchedule, statusUserSchedule, } from "./schedule/user-installation.js";
|
|
18
|
+
import { generateUserSchedule } from "./schedule/user-plan.js";
|
|
19
|
+
import { createEnduranceAuthorities } from "./runtime/endurance-authorities.js";
|
|
20
|
+
import { linuxProcessIdentityIsLive, readLinuxProcessIdentity } from "./runtime/process-identity.js";
|
|
21
|
+
import { systemClockSample } from "./runtime/system-clock.js";
|
|
22
|
+
import { createHudSnapshot, renderHud } from "./status/hud.js";
|
|
23
|
+
import { reconcileWatchdog } from "./watchdog/reconcile.js";
|
|
24
|
+
import { runDoctor } from "./doctor.js";
|
|
25
|
+
import { readOmsConfig } from "./config.js";
|
|
26
|
+
import { projectClaudeStructuredRoleCompletion, recoverClaudeStructuredRoleCompletions, } from "./providers/claude-structured-completion.js";
|
|
27
|
+
import { assessRelease } from "./health/release-assessment.js";
|
|
28
|
+
import { createGoal, readGoal } from "./goals/authority.js";
|
|
29
|
+
import { normalizeGithubRemoteRepository, normalizeGithubSyncTarget } from "./github/adapter.js";
|
|
30
|
+
import { projectGithubSyncBatch } from "./github/projector.js";
|
|
31
|
+
import { syncGithubEventsOnce } from "./github/sync.js";
|
|
32
|
+
import { createReviewDeliveryService, OMS_REVIEW_REQUIRED_CHECK, } from "./github/review.js";
|
|
33
|
+
import { createRuntimeGoalAuthority } from "./goals/runtime-authority.js";
|
|
34
|
+
import { discloseLiveContact, LIVE_CONTACT_OPERATIONS, PRE_DISPATCH_ADVICE_OPERATION, readLiveDeliveryAuthorization, readPreDispatchAdviceAuthorization, } from "./live/contact-journal.js";
|
|
35
|
+
import { findPackageRoot } from "./package-root.js";
|
|
36
|
+
import { projectWorkspace } from "./projection/project-workspace.js";
|
|
37
|
+
import { initializeState, readStateSummary, resolveStateRoot } from "./state.js";
|
|
38
|
+
import { generateExecutorGroupManifest, persistExecutorGroupManifest } from "./team/plan.js";
|
|
39
|
+
import { errorMessage, isRecord, lstatIfExists, readJsonFileNoFollow, validateExistingDirectory } from "./utils/fs.js";
|
|
40
|
+
import { runVerification } from "./verify.js";
|
|
41
|
+
import { assertSchema, loadJsonSchema } from "./runtime/schema.js";
|
|
42
|
+
import { createStableId } from "./runtime/stable-id.js";
|
|
43
|
+
import { writeDailyTrustReport } from "./reports/daily-trust.js";
|
|
44
|
+
import { createRuntimeTicketAuthorities } from "./tickets/runtime-authority.js";
|
|
45
|
+
import { abortAuditRun, closeTimedAuditRun, conductorNext, listRunStatuses, readRunStatus, recoverRunControllerAuthority, startAuditRun, tickAuditRun, } from "./runs/controller.js";
|
|
46
|
+
import { createRuntimeDispatchAuthorities } from "./dispatch/runtime-authority.js";
|
|
47
|
+
import { createDispatchAdviceService } from "./dispatch/advice.js";
|
|
48
|
+
import { createWorkerDeliveryService } from "./workers/delivery.js";
|
|
49
|
+
import { runWorkerBridge } from "./workers/bridge.js";
|
|
50
|
+
import { runClaudeStructuredRoleBridge } from "./providers/claude-structured-bridge.js";
|
|
51
|
+
import { createRuntimeWorkerAuthorities } from "./workers/runtime.js";
|
|
52
|
+
import { createWorkerPaneAdapter } from "./workers/panes.js";
|
|
53
|
+
import { runProcess } from "./providers/process.js";
|
|
54
|
+
import { createResultGateService } from "./verification/result-gate.js";
|
|
55
|
+
const USAGE = `Usage: oms [command] [options]
|
|
56
|
+
|
|
57
|
+
With no command in an interactive terminal, OMS starts or attaches the conductor.
|
|
58
|
+
With no command outside a terminal, OMS prints this help and contacts no provider.
|
|
59
|
+
|
|
60
|
+
Commands:
|
|
61
|
+
init [--state-root PATH] Initialize local runtime state idempotently
|
|
62
|
+
doctor Run offline dependency and configuration checks
|
|
63
|
+
status [--state-root PATH] Print a deterministic local state summary
|
|
64
|
+
project Project rules, concrete agents, and skills from .agents
|
|
65
|
+
probe github-queue --repository OWNER/REPO --run-id ID [--confirm-live]
|
|
66
|
+
Disclose, then optionally contact GitHub and sync ready issues
|
|
67
|
+
trace fixture [--state-root PATH]
|
|
68
|
+
Run the offline one-ticket contributor fixture
|
|
69
|
+
hook ingest [--state-root PATH]
|
|
70
|
+
Ingest one bounded native-hook JSON object from stdin
|
|
71
|
+
hook provider --provider claude|codex --provider-version VERSION --dispatch-id ID --session-id ID
|
|
72
|
+
--state-root PATH --workspace-root PATH --config-root PATH
|
|
73
|
+
Project one pinned provider hook; completion is observational only
|
|
74
|
+
events sync github --repository OWNER/REPO [--state-root PATH] [--workspace-root PATH]
|
|
75
|
+
Run one typed, deduplicated GitHub observation pass
|
|
76
|
+
reconcile minute|watchdog [--state-root PATH] [--workspace-root PATH]
|
|
77
|
+
Run one idempotent reconciliation pass
|
|
78
|
+
schedule generate|install|status|remove --manager cron|systemd|launchd --repository OWNER/REPO
|
|
79
|
+
[--github-poll-seconds N] [--manager-executable PATH] [--state-root PATH]
|
|
80
|
+
Manage the exact user-scoped GitHub/watchdog schedule
|
|
81
|
+
run start (--until-empty | --for 12h|24h) --manager cron|systemd|launchd [--state-root PATH]
|
|
82
|
+
Start a non-certifying audit run; workers remain OMS-managed interactive
|
|
83
|
+
run tick --run-id ID [--state-root PATH]
|
|
84
|
+
Record one deterministic run/control-loop tick
|
|
85
|
+
run close --run-id ID [--state-root PATH]
|
|
86
|
+
Close a fully elapsed 12h/24h audit run; never certifies release
|
|
87
|
+
run abort --run-id ID --reason-file PATH [--state-root PATH]
|
|
88
|
+
Abort a run with one immutable bounded JSON reason
|
|
89
|
+
run recover-controller --generation-id ID --reason-file PATH [--state-root PATH]
|
|
90
|
+
Explicitly recover one crashed controller-lock generation
|
|
91
|
+
run status [--run-id ID] [--state-root PATH]
|
|
92
|
+
Read audit run status without launching providers
|
|
93
|
+
conductor start [--state-root PATH] [--repository OWNER/REPO --confirm-live-delivery]
|
|
94
|
+
Advanced recovery/state override for the conductor pane
|
|
95
|
+
conductor wait-event --session-id ID --hook-event SessionStart|Stop --provider-version VERSION [--state-root PATH]
|
|
96
|
+
Wait in a pinned asyncRewake hook; emits only one opaque notice ID
|
|
97
|
+
conductor next --run-id ID [--state-root PATH]
|
|
98
|
+
Persist the next opaque, read-only conductor route
|
|
99
|
+
goal create --file PATH [--state-root PATH]
|
|
100
|
+
Create one canonical goal from a bounded JSON file
|
|
101
|
+
goal status --goal-id ID [--state-root PATH]
|
|
102
|
+
Read canonical goal lifecycle status
|
|
103
|
+
worker deliver --event-id ID [--state-root PATH]
|
|
104
|
+
Deliver one accepted dispatch to its interactive pane
|
|
105
|
+
worker status --dispatch-id ID [--state-root PATH]
|
|
106
|
+
Read typed worker/session/pane status
|
|
107
|
+
worker park --dispatch-id ID --until TIME --reason-file PATH [--state-root PATH]
|
|
108
|
+
worker resume --dispatch-id ID [--state-root PATH]
|
|
109
|
+
worker stop --dispatch-id ID --reason-file PATH [--state-root PATH]
|
|
110
|
+
Manage one worker through typed OMS lifecycle commands
|
|
111
|
+
team start --dispatch-id ID [--dispatch-id ID...] [--state-root PATH]
|
|
112
|
+
Persist and start one OMS-native executor group
|
|
113
|
+
structured-role bridge --request-id ID --session-id UUID --state-root PATH --workspace-root PATH
|
|
114
|
+
Serve one two-tool fresh interactive Claude role bridge
|
|
115
|
+
trust report --date YYYY-MM-DD [--state-root PATH]
|
|
116
|
+
Write one immutable closed-day trust report
|
|
117
|
+
hud [--state-root PATH] Print one read-only runtime HUD snapshot
|
|
118
|
+
release status Print the offline negative-only release assessment
|
|
119
|
+
verify Run the offline package and workspace gate`;
|
|
120
|
+
export async function runCli(argv = process.argv.slice(2), context = {}) {
|
|
121
|
+
const packageRoot = context.packageRoot ?? findPackageRoot();
|
|
122
|
+
let workspaceRoot = resolve(context.workspaceRoot ?? context.cwd ?? process.cwd());
|
|
123
|
+
const interactive = context.isInteractive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
124
|
+
const automaticConductorStart = argv.length === 0 && interactive;
|
|
125
|
+
const command = automaticConductorStart ? "conductor" : argv[0];
|
|
126
|
+
let args = automaticConductorStart ? ["start"] : argv.slice(1);
|
|
127
|
+
try {
|
|
128
|
+
if (command === undefined || command === "help" || command === "-h" || command === "--help") {
|
|
129
|
+
return { ok: true, exitCode: 0, message: USAGE };
|
|
130
|
+
}
|
|
131
|
+
if (automaticConductorStart) {
|
|
132
|
+
if (!isOmsGitWorkspace(workspaceRoot)) {
|
|
133
|
+
if (!isGitWorkspace(workspaceRoot)) {
|
|
134
|
+
if (!isOmsGitWorkspace(packageRoot)) {
|
|
135
|
+
throw new Error("bare oms must run from the root of a Git repository");
|
|
136
|
+
}
|
|
137
|
+
workspaceRoot = packageRoot;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const authorizedStateRoot = discoverAuthorizedConductorStateRoot({
|
|
141
|
+
workspaceRoot,
|
|
142
|
+
env: context.env,
|
|
143
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
144
|
+
});
|
|
145
|
+
const stateRoot = authorizedStateRoot ?? resolveStateRoot({
|
|
146
|
+
workspaceRoot,
|
|
147
|
+
env: context.env,
|
|
148
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
149
|
+
});
|
|
150
|
+
const state = readStateSummary({
|
|
151
|
+
workspaceRoot,
|
|
152
|
+
override: stateRoot,
|
|
153
|
+
env: context.env,
|
|
154
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
155
|
+
});
|
|
156
|
+
const liveAuthorization = state.initialized ? readLiveDeliveryAuthorization(stateRoot) : undefined;
|
|
157
|
+
const adviceAuthorization = state.initialized ? readPreDispatchAdviceAuthorization(stateRoot) : undefined;
|
|
158
|
+
if (liveAuthorization && adviceAuthorization) {
|
|
159
|
+
args = ["start", "--state-root", stateRoot];
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
const requestAuthorization = context.requestBareLiveDeliveryAuthorization;
|
|
163
|
+
if (!requestAuthorization) {
|
|
164
|
+
throw new Error("bare oms requires the human authorization prompt before starting a conductor");
|
|
165
|
+
}
|
|
166
|
+
const suggestedRepository = liveAuthorization?.repository ?? inferBareGithubRepository({
|
|
167
|
+
workspaceRoot,
|
|
168
|
+
env: context.env,
|
|
169
|
+
runner: context.bareStartRunner ?? runProcess,
|
|
170
|
+
});
|
|
171
|
+
const confirmedRepository = await requestAuthorization({
|
|
172
|
+
state_root: stateRoot,
|
|
173
|
+
suggested_repository: suggestedRepository,
|
|
174
|
+
required_repository: liveAuthorization?.repository ?? null,
|
|
175
|
+
});
|
|
176
|
+
if (!confirmedRepository) {
|
|
177
|
+
throw new Error("live delivery authorization was not confirmed; no conductor was started");
|
|
178
|
+
}
|
|
179
|
+
const repository = normalizeGithubSyncTarget(confirmedRepository).repository;
|
|
180
|
+
const requiredRepository = liveAuthorization?.repository ?? suggestedRepository;
|
|
181
|
+
if (requiredRepository && repository !== requiredRepository) {
|
|
182
|
+
throw new Error(`live delivery authorization must match repository ${requiredRepository}`);
|
|
183
|
+
}
|
|
184
|
+
args = [
|
|
185
|
+
"start",
|
|
186
|
+
"--state-root",
|
|
187
|
+
stateRoot,
|
|
188
|
+
"--repository",
|
|
189
|
+
repository,
|
|
190
|
+
"--confirm-live-delivery",
|
|
191
|
+
];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const sourceRoot = hasOmsConfiguration(workspaceRoot) ? workspaceRoot : packageRoot;
|
|
195
|
+
switch (command) {
|
|
196
|
+
case "init": {
|
|
197
|
+
const override = parseStateRoot(args);
|
|
198
|
+
const stateRoot = initializeState({
|
|
199
|
+
workspaceRoot,
|
|
200
|
+
override,
|
|
201
|
+
env: context.env,
|
|
202
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
203
|
+
});
|
|
204
|
+
return { ok: true, exitCode: 0, message: `initialized: ${stateRoot}` };
|
|
205
|
+
}
|
|
206
|
+
case "status": {
|
|
207
|
+
const override = activeStateRootOverride({
|
|
208
|
+
workspaceRoot,
|
|
209
|
+
override: parseStateRoot(args),
|
|
210
|
+
env: context.env,
|
|
211
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
212
|
+
});
|
|
213
|
+
const summary = readStateSummary({
|
|
214
|
+
workspaceRoot,
|
|
215
|
+
override,
|
|
216
|
+
env: context.env,
|
|
217
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
218
|
+
});
|
|
219
|
+
return { ok: true, exitCode: 0, output: summary };
|
|
220
|
+
}
|
|
221
|
+
case "doctor": {
|
|
222
|
+
requireNoOptions("doctor", args);
|
|
223
|
+
const report = runDoctor({ root: sourceRoot });
|
|
224
|
+
return { ok: report.ok, exitCode: report.ok ? 0 : 1, message: report.lines.join("\n") };
|
|
225
|
+
}
|
|
226
|
+
case "project":
|
|
227
|
+
requireNoOptions("project", args);
|
|
228
|
+
return {
|
|
229
|
+
ok: true,
|
|
230
|
+
exitCode: 0,
|
|
231
|
+
message: `projected: ${projectWorkspace(sourceRoot, workspaceRoot).projected.join(" ")}`,
|
|
232
|
+
};
|
|
233
|
+
case "probe": {
|
|
234
|
+
const probe = parseGithubQueueProbeArgs(args);
|
|
235
|
+
const runProbe = context.githubQueueProbe ?? runGithubQueueProbeCommand;
|
|
236
|
+
const emitDisclosure = context.emitLiveDisclosure ?? ((disclosure, evidence) => {
|
|
237
|
+
process.stderr.write(`${JSON.stringify({ live_contact_disclosure: disclosure, evidence })}\n`);
|
|
238
|
+
});
|
|
239
|
+
const output = runProbe({
|
|
240
|
+
packageRoot,
|
|
241
|
+
configRoot: sourceRoot,
|
|
242
|
+
workspaceRoot,
|
|
243
|
+
stateRootOverride: probe.stateRoot,
|
|
244
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
245
|
+
env: context.env,
|
|
246
|
+
runId: probe.runId,
|
|
247
|
+
repository: probe.repository,
|
|
248
|
+
readyLabel: probe.readyLabel,
|
|
249
|
+
limit: probe.limit,
|
|
250
|
+
confirmLive: probe.confirmLive,
|
|
251
|
+
emitDisclosure,
|
|
252
|
+
});
|
|
253
|
+
return { ok: true, exitCode: 0, output };
|
|
254
|
+
}
|
|
255
|
+
case "trace": {
|
|
256
|
+
if (args[0] !== "fixture")
|
|
257
|
+
throw new Error("trace requires the explicit fixture subcommand");
|
|
258
|
+
const override = parseStateRoot(args.slice(1));
|
|
259
|
+
const traceFixture = context.traceFixture ?? runTraceFixture;
|
|
260
|
+
const output = traceFixture({
|
|
261
|
+
packageRoot,
|
|
262
|
+
configRoot: sourceRoot,
|
|
263
|
+
workspaceRoot,
|
|
264
|
+
stateRootOverride: override,
|
|
265
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
266
|
+
env: context.env,
|
|
267
|
+
});
|
|
268
|
+
if (output.state !== "verifying") {
|
|
269
|
+
throw new Error(`offline trace fixture stopped in ${output.state}`);
|
|
270
|
+
}
|
|
271
|
+
return { ok: true, exitCode: 0, output };
|
|
272
|
+
}
|
|
273
|
+
case "hook": {
|
|
274
|
+
const raw = context.readStdin ? context.readStdin() : readBoundedUtf8(0, 64 * 1024);
|
|
275
|
+
if (Buffer.byteLength(raw, "utf8") > 64 * 1024)
|
|
276
|
+
throw new Error("native hook input exceeds 65536 bytes");
|
|
277
|
+
const input = JSON.parse(raw);
|
|
278
|
+
if (args[0] === "ingest") {
|
|
279
|
+
const override = parseStateRoot(args.slice(1));
|
|
280
|
+
const stateRoot = requireInitializedState({ workspaceRoot, override, env: context.env, cwd: context.cwd ?? workspaceRoot });
|
|
281
|
+
const runtime = createEnduranceAuthorities({ packageRoot, configRoot: sourceRoot, stateRoot });
|
|
282
|
+
try {
|
|
283
|
+
const published = ingestNativeHook({ input, bus: runtime.eventBus, queue: runtime.tickets.queue });
|
|
284
|
+
const sampled = (context.clockSample ?? systemClockSample)();
|
|
285
|
+
const projected = await projectNativeHook({
|
|
286
|
+
eventId: published.event.event_id,
|
|
287
|
+
processedAt: sampled.wallTime,
|
|
288
|
+
bus: runtime.eventBus,
|
|
289
|
+
processor: runtime.outboxProcessor,
|
|
290
|
+
sessions: runtime.sessions,
|
|
291
|
+
quota: runtime.quota,
|
|
292
|
+
queue: runtime.tickets.queue,
|
|
293
|
+
packageRoot,
|
|
294
|
+
configRoot: sourceRoot,
|
|
295
|
+
stateRoot,
|
|
296
|
+
auditLedger: runtime.auditLedger,
|
|
297
|
+
});
|
|
298
|
+
const notification = runtime.notifications.assignProjected({
|
|
299
|
+
eventIds: [published.event.event_id],
|
|
300
|
+
projectorId: "native-hook-projector",
|
|
301
|
+
createdAt: sampled.wallTime,
|
|
302
|
+
});
|
|
303
|
+
return {
|
|
304
|
+
ok: true,
|
|
305
|
+
exitCode: 0,
|
|
306
|
+
output: {
|
|
307
|
+
event_id: published.event.event_id,
|
|
308
|
+
publish_status: published.status,
|
|
309
|
+
projection_status: projected.status,
|
|
310
|
+
notification_id: notification.record?.record_id ?? null,
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
finally {
|
|
315
|
+
runtime.close();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const provider = parseProviderHookArgs(args);
|
|
319
|
+
const providerWorkspace = validateExistingDirectory(provider.workspaceRoot, "provider hook workspace root");
|
|
320
|
+
const providerConfig = validateExistingDirectory(provider.configRoot, "provider hook config root");
|
|
321
|
+
const stateRoot = requireInitializedState({
|
|
322
|
+
workspaceRoot: providerWorkspace,
|
|
323
|
+
override: provider.stateRoot,
|
|
324
|
+
env: context.env,
|
|
325
|
+
cwd: providerWorkspace,
|
|
326
|
+
});
|
|
327
|
+
const endurance = createEnduranceAuthorities({ packageRoot, configRoot: providerConfig, stateRoot });
|
|
328
|
+
try {
|
|
329
|
+
const workers = createRuntimeWorkerAuthorities({
|
|
330
|
+
packageRoot,
|
|
331
|
+
configRoot: providerConfig,
|
|
332
|
+
workspaceRoot: providerWorkspace,
|
|
333
|
+
stateRoot,
|
|
334
|
+
actorId: "native-hook",
|
|
335
|
+
});
|
|
336
|
+
const bindings = createDurableProviderBindingResolver({
|
|
337
|
+
provider: provider.provider,
|
|
338
|
+
dispatch: workers.dispatch,
|
|
339
|
+
workers: workers.workers,
|
|
340
|
+
sessions: workers.sessions,
|
|
341
|
+
tickets: workers.tickets.lifecycle,
|
|
342
|
+
});
|
|
343
|
+
const sampled = (context.clockSample ?? systemClockSample)();
|
|
344
|
+
const observations = normalizeProviderHook({
|
|
345
|
+
packageRoot,
|
|
346
|
+
provider: provider.provider,
|
|
347
|
+
providerVersion: provider.providerVersion,
|
|
348
|
+
dispatchId: provider.dispatchId,
|
|
349
|
+
sessionId: provider.sessionId,
|
|
350
|
+
observedAt: sampled.wallTime,
|
|
351
|
+
input,
|
|
352
|
+
bindings,
|
|
353
|
+
});
|
|
354
|
+
await publishAndProjectProviderObservations({
|
|
355
|
+
observations,
|
|
356
|
+
processedAt: sampled.wallTime,
|
|
357
|
+
packageRoot,
|
|
358
|
+
configRoot: providerConfig,
|
|
359
|
+
bus: endurance.eventBus,
|
|
360
|
+
processor: endurance.outboxProcessor,
|
|
361
|
+
outbox: endurance.outbox,
|
|
362
|
+
notifications: endurance.notifications,
|
|
363
|
+
bindings,
|
|
364
|
+
workers: workers.workers,
|
|
365
|
+
sessions: workers.sessions,
|
|
366
|
+
});
|
|
367
|
+
const isCodexStop = provider.provider === "codex" && observations[0]?.hook_event === "Stop";
|
|
368
|
+
return { ok: true, exitCode: 0, ...(isCodexStop ? { output: {} } : {}) };
|
|
369
|
+
}
|
|
370
|
+
finally {
|
|
371
|
+
endurance.close();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
case "events": {
|
|
375
|
+
const events = parseEventsArgs(args);
|
|
376
|
+
const eventsWorkspace = events.workspaceRoot
|
|
377
|
+
? validateExistingDirectory(resolve(events.workspaceRoot), "GitHub sync workspace root")
|
|
378
|
+
: workspaceRoot;
|
|
379
|
+
const eventsConfig = existsSync(join(eventsWorkspace, ".agents")) ? eventsWorkspace : packageRoot;
|
|
380
|
+
const stateRoot = requireInitializedState({
|
|
381
|
+
workspaceRoot: eventsWorkspace,
|
|
382
|
+
override: events.stateRoot,
|
|
383
|
+
env: context.env,
|
|
384
|
+
cwd: eventsWorkspace,
|
|
385
|
+
});
|
|
386
|
+
const runtime = createEnduranceAuthorities({ packageRoot, configRoot: eventsConfig, stateRoot });
|
|
387
|
+
const sampled = (context.clockSample ?? systemClockSample)();
|
|
388
|
+
const target = normalizeGithubSyncTarget(events.repository);
|
|
389
|
+
const contacts = [];
|
|
390
|
+
let projection;
|
|
391
|
+
try {
|
|
392
|
+
const authorization = readLiveDeliveryAuthorization(stateRoot);
|
|
393
|
+
if (!authorization || authorization.repository !== target.repository) {
|
|
394
|
+
throw new Error("GitHub event sync lacks matching human-started live authorization");
|
|
395
|
+
}
|
|
396
|
+
const githubRunner = context.githubRunner ?? runProcess;
|
|
397
|
+
const disclosedRunner = (request) => {
|
|
398
|
+
contacts.push(discloseLiveContact({
|
|
399
|
+
stateRoot,
|
|
400
|
+
authorization,
|
|
401
|
+
operation: "github-event-sync",
|
|
402
|
+
contactSurface: "github-api-via-gh",
|
|
403
|
+
subjectId: createStableId("gh-sync-call", target.repository, sampled.wallTime, String(contacts.length)),
|
|
404
|
+
request: publicGithubSyncRequest(request),
|
|
405
|
+
disclosedAt: sampled.wallTime,
|
|
406
|
+
}));
|
|
407
|
+
return githubRunner(request);
|
|
408
|
+
};
|
|
409
|
+
const result = await syncGithubEventsOnce({
|
|
410
|
+
target,
|
|
411
|
+
cwd: eventsWorkspace,
|
|
412
|
+
watermarkRoot: join(stateRoot, "github-watermarks"),
|
|
413
|
+
locksRoot: join(stateRoot, "locks"),
|
|
414
|
+
env: context.env,
|
|
415
|
+
runner: disclosedRunner,
|
|
416
|
+
clock: () => sampled.wallTime,
|
|
417
|
+
project: async (batch) => {
|
|
418
|
+
projection = await projectGithubSyncBatch({
|
|
419
|
+
batch,
|
|
420
|
+
processedAt: sampled.wallTime,
|
|
421
|
+
packageRoot,
|
|
422
|
+
snapshotsRoot: join(stateRoot, "github-snapshots"),
|
|
423
|
+
bus: runtime.eventBus,
|
|
424
|
+
processor: runtime.outboxProcessor,
|
|
425
|
+
outbox: runtime.outbox,
|
|
426
|
+
notifications: runtime.notifications,
|
|
427
|
+
ticketMaterializer: runtime.tickets.materializer,
|
|
428
|
+
});
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
if (!projection)
|
|
432
|
+
throw new Error("GitHub sync completed without projection evidence");
|
|
433
|
+
return {
|
|
434
|
+
ok: true,
|
|
435
|
+
exitCode: 0,
|
|
436
|
+
output: {
|
|
437
|
+
repository: result.watermark.repository,
|
|
438
|
+
generation: result.watermark.generation,
|
|
439
|
+
observation_count: result.observations.length,
|
|
440
|
+
event_ids: projection.event_ids,
|
|
441
|
+
notification_id: projection.notification_id,
|
|
442
|
+
notification_ids: projection.notification_ids,
|
|
443
|
+
watermark_path: result.watermark_path,
|
|
444
|
+
contact_disclosure: contacts[0] ?? null,
|
|
445
|
+
contact_disclosures: contacts,
|
|
446
|
+
},
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
finally {
|
|
450
|
+
runtime.close();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
case "reconcile": {
|
|
454
|
+
const reconcile = parseReconcileArgs(args);
|
|
455
|
+
const reconcileWorkspace = resolve(reconcile.workspaceRoot ?? workspaceRoot);
|
|
456
|
+
const reconcileConfig = existsSync(join(reconcileWorkspace, ".agents")) ? reconcileWorkspace : packageRoot;
|
|
457
|
+
const stateRoot = requireInitializedState({
|
|
458
|
+
workspaceRoot: reconcileWorkspace,
|
|
459
|
+
override: reconcile.stateRoot,
|
|
460
|
+
env: context.env,
|
|
461
|
+
cwd: context.cwd ?? reconcileWorkspace,
|
|
462
|
+
});
|
|
463
|
+
const runtime = createEnduranceAuthorities({ packageRoot, configRoot: reconcileConfig, stateRoot });
|
|
464
|
+
try {
|
|
465
|
+
const at = (context.clockSample ?? systemClockSample)().wallTime;
|
|
466
|
+
const output = reconcile.kind === "minute"
|
|
467
|
+
? await reconcileMinute({
|
|
468
|
+
at,
|
|
469
|
+
bus: runtime.eventBus,
|
|
470
|
+
processor: runtime.outboxProcessor,
|
|
471
|
+
outbox: runtime.outbox,
|
|
472
|
+
queue: runtime.tickets.queue,
|
|
473
|
+
sessions: runtime.sessions,
|
|
474
|
+
quota: runtime.quota,
|
|
475
|
+
auditLedger: runtime.auditLedger,
|
|
476
|
+
packageRoot,
|
|
477
|
+
configRoot: reconcileConfig,
|
|
478
|
+
stateRoot,
|
|
479
|
+
})
|
|
480
|
+
: await reconcileWatchdog({
|
|
481
|
+
at,
|
|
482
|
+
bus: runtime.eventBus,
|
|
483
|
+
processor: runtime.outboxProcessor,
|
|
484
|
+
outbox: runtime.outbox,
|
|
485
|
+
queue: runtime.tickets.queue,
|
|
486
|
+
sessions: runtime.sessions,
|
|
487
|
+
workers: runtime.workers,
|
|
488
|
+
workerPanes: createWorkerPaneAdapter({ env: context.env }),
|
|
489
|
+
packageRoot,
|
|
490
|
+
configRoot: reconcileConfig,
|
|
491
|
+
stateRoot,
|
|
492
|
+
auditLedger: runtime.auditLedger,
|
|
493
|
+
stallMs: runtime.policy.outbox_stall_seconds * 1_000,
|
|
494
|
+
recoverStructuredRoleCompletions: () => {
|
|
495
|
+
const recovered = recoverClaudeStructuredRoleCompletions({ packageRoot, stateRoot });
|
|
496
|
+
return Object.freeze({
|
|
497
|
+
recoveredRequestIds: recovered.projections.map((projection) => projection.request_id),
|
|
498
|
+
failedRequestIds: recovered.failed_request_ids,
|
|
499
|
+
});
|
|
500
|
+
},
|
|
501
|
+
recoverConductor: () => {
|
|
502
|
+
const recover = context.conductorRecovery ?? recoverConductorNotification;
|
|
503
|
+
return recover({
|
|
504
|
+
at,
|
|
505
|
+
locksRoot: join(stateRoot, "locks"),
|
|
506
|
+
activity: runtime.conductorActivity,
|
|
507
|
+
notifications: runtime.notifications,
|
|
508
|
+
waiters: runtime.waiters,
|
|
509
|
+
ensureConductor: () => {
|
|
510
|
+
const launched = (context.conductorStart ?? startConductor)({
|
|
511
|
+
packageRoot,
|
|
512
|
+
configRoot: reconcileConfig,
|
|
513
|
+
workspaceRoot: reconcileWorkspace,
|
|
514
|
+
stateRoot,
|
|
515
|
+
executablePath: context.executablePath ?? resolveDefaultExecutable(packageRoot),
|
|
516
|
+
env: context.env,
|
|
517
|
+
attach: false,
|
|
518
|
+
resumeOnly: true,
|
|
519
|
+
});
|
|
520
|
+
const binding = inspectConductorRuntimeBinding({
|
|
521
|
+
workspaceRoot: reconcileWorkspace,
|
|
522
|
+
stateRoot,
|
|
523
|
+
sessionId: launched.claude_session_id,
|
|
524
|
+
env: context.env,
|
|
525
|
+
});
|
|
526
|
+
return Object.freeze({
|
|
527
|
+
action: launched.action,
|
|
528
|
+
binding: Object.freeze({
|
|
529
|
+
session_id: binding.session_id,
|
|
530
|
+
launch_generation: binding.launch_generation,
|
|
531
|
+
pane_id: binding.pane_id,
|
|
532
|
+
conductor_process: readLinuxProcessIdentity(binding.process_id),
|
|
533
|
+
}),
|
|
534
|
+
});
|
|
535
|
+
},
|
|
536
|
+
wakeConductor: (binding, notificationId) => wakeConductorWithNotification({
|
|
537
|
+
workspaceRoot: reconcileWorkspace,
|
|
538
|
+
stateRoot,
|
|
539
|
+
sessionId: binding.session_id,
|
|
540
|
+
launchGeneration: binding.launch_generation,
|
|
541
|
+
paneId: binding.pane_id,
|
|
542
|
+
conductorProcess: binding.conductor_process,
|
|
543
|
+
notificationId,
|
|
544
|
+
env: context.env,
|
|
545
|
+
}),
|
|
546
|
+
});
|
|
547
|
+
},
|
|
548
|
+
});
|
|
549
|
+
return { ok: true, exitCode: 0, output };
|
|
550
|
+
}
|
|
551
|
+
finally {
|
|
552
|
+
runtime.close();
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
case "schedule": {
|
|
556
|
+
const schedule = parseScheduleArgs(args);
|
|
557
|
+
const runtimeEnv = context.env ?? process.env;
|
|
558
|
+
const stateRoot = requireInitializedState({
|
|
559
|
+
workspaceRoot,
|
|
560
|
+
override: schedule.stateRoot,
|
|
561
|
+
env: context.env,
|
|
562
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
563
|
+
});
|
|
564
|
+
const installationsRoot = join(stateRoot, "schedule-installations");
|
|
565
|
+
const installedBinding = schedule.command === "generate"
|
|
566
|
+
? undefined
|
|
567
|
+
: readInstalledUserSchedulePlanBinding({ installationsRoot, packageRoot });
|
|
568
|
+
if (installedBinding && installedBinding.manager !== schedule.manager) {
|
|
569
|
+
throw new Error("recorded user schedule uses another manager");
|
|
570
|
+
}
|
|
571
|
+
const installedPlan = installedBinding?.schema_version === 2 ? installedBinding : undefined;
|
|
572
|
+
if (installedPlan) {
|
|
573
|
+
const requestedRepository = normalizeGithubSyncTarget(schedule.repository).repository;
|
|
574
|
+
if (installedPlan.state_root !== stateRoot)
|
|
575
|
+
throw new Error("recorded user schedule uses another state root");
|
|
576
|
+
if (installedPlan.repository !== requestedRepository)
|
|
577
|
+
throw new Error("recorded user schedule uses another repository");
|
|
578
|
+
if (schedule.githubPollSeconds !== undefined
|
|
579
|
+
&& schedule.githubPollSeconds !== installedPlan.github_poll_seconds)
|
|
580
|
+
throw new Error("recorded user schedule uses another GitHub poll interval");
|
|
581
|
+
}
|
|
582
|
+
const executablePath = installedPlan?.executable_path
|
|
583
|
+
?? resolve(context.executablePath ?? process.argv[1] ?? join(packageRoot, "bin", "oms"));
|
|
584
|
+
const generated = generateUserSchedule({
|
|
585
|
+
manager: schedule.manager,
|
|
586
|
+
executablePath,
|
|
587
|
+
environmentPath: installedPlan?.environment_path
|
|
588
|
+
?? (installedBinding?.schema_version === 1 ? dirname(executablePath) : runtimeEnv.PATH ?? ""),
|
|
589
|
+
stateRoot,
|
|
590
|
+
workspaceRoot: installedPlan?.workspace_root ?? workspaceRoot,
|
|
591
|
+
repository: installedPlan?.repository ?? schedule.repository,
|
|
592
|
+
githubPollSeconds: installedPlan?.github_poll_seconds ?? schedule.githubPollSeconds,
|
|
593
|
+
packageRoot,
|
|
594
|
+
});
|
|
595
|
+
if (schedule.command === "generate") {
|
|
596
|
+
return { ok: true, exitCode: 0, output: generated };
|
|
597
|
+
}
|
|
598
|
+
if (!runtimeEnv.HOME)
|
|
599
|
+
throw new Error(`schedule ${schedule.command} requires HOME`);
|
|
600
|
+
const lifecycle = {
|
|
601
|
+
generated,
|
|
602
|
+
packageRoot,
|
|
603
|
+
homeDirectory: runtimeEnv.HOME,
|
|
604
|
+
installationsRoot,
|
|
605
|
+
managerExecutablePath: schedule.managerExecutable ?? defaultManagerExecutable(schedule.manager),
|
|
606
|
+
env: runtimeEnv,
|
|
607
|
+
...(context.scheduleRunner ? { runner: context.scheduleRunner } : {}),
|
|
608
|
+
};
|
|
609
|
+
if (schedule.command === "install") {
|
|
610
|
+
return { ok: true, exitCode: 0, output: installUserSchedule(lifecycle) };
|
|
611
|
+
}
|
|
612
|
+
if (schedule.command === "status") {
|
|
613
|
+
return { ok: true, exitCode: 0, output: statusUserSchedule(lifecycle) };
|
|
614
|
+
}
|
|
615
|
+
return { ok: true, exitCode: 0, output: removeUserSchedule(lifecycle) };
|
|
616
|
+
}
|
|
617
|
+
case "run": {
|
|
618
|
+
const run = parseRunArgs(args);
|
|
619
|
+
const stateRoot = requireInitializedState({
|
|
620
|
+
workspaceRoot,
|
|
621
|
+
override: run.stateRoot,
|
|
622
|
+
env: context.env,
|
|
623
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
624
|
+
});
|
|
625
|
+
if (run.command === "start") {
|
|
626
|
+
const output = startAuditRun({
|
|
627
|
+
packageRoot,
|
|
628
|
+
configRoot: sourceRoot,
|
|
629
|
+
stateRoot,
|
|
630
|
+
workspaceRoot,
|
|
631
|
+
executablePath: resolve(context.executablePath ?? process.argv[1] ?? join(packageRoot, "bin", "oms")),
|
|
632
|
+
manager: run.manager,
|
|
633
|
+
runKind: run.runKind,
|
|
634
|
+
});
|
|
635
|
+
return { ok: true, exitCode: 0, output };
|
|
636
|
+
}
|
|
637
|
+
if (run.command === "tick") {
|
|
638
|
+
return {
|
|
639
|
+
ok: true,
|
|
640
|
+
exitCode: 0,
|
|
641
|
+
output: tickAuditRun({ packageRoot, configRoot: sourceRoot, stateRoot, runId: run.runId }),
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
if (run.command === "close") {
|
|
645
|
+
return {
|
|
646
|
+
ok: true,
|
|
647
|
+
exitCode: 0,
|
|
648
|
+
output: closeTimedAuditRun({ packageRoot, configRoot: sourceRoot, stateRoot, runId: run.runId }),
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
if (run.command === "abort") {
|
|
652
|
+
const inputPath = resolve(context.cwd ?? workspaceRoot, run.reasonFile);
|
|
653
|
+
const input = readJsonFileNoFollow(inputPath, 16 * 1024);
|
|
654
|
+
assertSchema(input, loadJsonSchema(join(packageRoot, "schemas", "run-abort-input.schema.json")), "run abort input");
|
|
655
|
+
return {
|
|
656
|
+
ok: true,
|
|
657
|
+
exitCode: 0,
|
|
658
|
+
output: abortAuditRun({
|
|
659
|
+
packageRoot,
|
|
660
|
+
configRoot: sourceRoot,
|
|
661
|
+
stateRoot,
|
|
662
|
+
runId: run.runId,
|
|
663
|
+
reason: input.reason,
|
|
664
|
+
}),
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
if (run.command === "recover-controller") {
|
|
668
|
+
const inputPath = resolve(context.cwd ?? workspaceRoot, run.reasonFile);
|
|
669
|
+
const input = readJsonFileNoFollow(inputPath, 16 * 1024);
|
|
670
|
+
assertSchema(input, loadJsonSchema(join(packageRoot, "schemas", "run-abort-input.schema.json")), "run recovery input");
|
|
671
|
+
return {
|
|
672
|
+
ok: true,
|
|
673
|
+
exitCode: 0,
|
|
674
|
+
output: recoverRunControllerAuthority({
|
|
675
|
+
packageRoot,
|
|
676
|
+
stateRoot,
|
|
677
|
+
expectedGenerationId: run.generationId,
|
|
678
|
+
reason: input.reason,
|
|
679
|
+
}),
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
return {
|
|
683
|
+
ok: true,
|
|
684
|
+
exitCode: 0,
|
|
685
|
+
output: run.runId
|
|
686
|
+
? readRunStatus({ packageRoot, configRoot: sourceRoot, stateRoot, runId: run.runId })
|
|
687
|
+
: listRunStatuses({ packageRoot, configRoot: sourceRoot, stateRoot }),
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
case "conductor": {
|
|
691
|
+
const conductor = parseConductorArgs(args);
|
|
692
|
+
if (conductor.command === "start") {
|
|
693
|
+
if (!interactive)
|
|
694
|
+
throw new Error("conductor start requires an interactive terminal");
|
|
695
|
+
const selectedStateRoot = resolveStateRoot({
|
|
696
|
+
workspaceRoot,
|
|
697
|
+
override: conductor.stateRoot,
|
|
698
|
+
env: context.env,
|
|
699
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
700
|
+
});
|
|
701
|
+
if (conductor.repository === undefined) {
|
|
702
|
+
const state = readStateSummary({
|
|
703
|
+
workspaceRoot,
|
|
704
|
+
override: selectedStateRoot,
|
|
705
|
+
env: context.env,
|
|
706
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
707
|
+
});
|
|
708
|
+
if (!state.initialized) {
|
|
709
|
+
throw new Error("conductor recovery requires an already authorized state root; run bare oms for first start");
|
|
710
|
+
}
|
|
711
|
+
requireStoredConductorAuthorization(selectedStateRoot, workspaceRoot);
|
|
712
|
+
}
|
|
713
|
+
const stateRoot = initializeState({
|
|
714
|
+
workspaceRoot,
|
|
715
|
+
override: selectedStateRoot,
|
|
716
|
+
env: context.env,
|
|
717
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
718
|
+
});
|
|
719
|
+
const runStart = context.conductorStart ?? startConductor;
|
|
720
|
+
return {
|
|
721
|
+
ok: true,
|
|
722
|
+
exitCode: 0,
|
|
723
|
+
output: runStart({
|
|
724
|
+
packageRoot,
|
|
725
|
+
configRoot: sourceRoot,
|
|
726
|
+
workspaceRoot,
|
|
727
|
+
stateRoot,
|
|
728
|
+
executablePath: context.executablePath ?? resolveDefaultExecutable(packageRoot),
|
|
729
|
+
env: context.env,
|
|
730
|
+
...(conductor.repository === undefined ? {} : {
|
|
731
|
+
liveDeliveryRepository: conductor.repository,
|
|
732
|
+
confirmLiveDelivery: true,
|
|
733
|
+
}),
|
|
734
|
+
}),
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
const stateRoot = requireInitializedState({
|
|
738
|
+
workspaceRoot,
|
|
739
|
+
override: conductor.stateRoot,
|
|
740
|
+
env: context.env,
|
|
741
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
742
|
+
});
|
|
743
|
+
if (conductor.command === "bridge") {
|
|
744
|
+
const serve = context.conductorBridge ?? runConductorBridge;
|
|
745
|
+
const createAdvice = context.dispatchAdvice ?? createDispatchAdviceService;
|
|
746
|
+
const createDelivery = context.workerDelivery ?? createWorkerDeliveryService;
|
|
747
|
+
let advice;
|
|
748
|
+
let delivery;
|
|
749
|
+
let resultGate;
|
|
750
|
+
let reviewDelivery;
|
|
751
|
+
const getAdvice = () => {
|
|
752
|
+
advice ??= createAdvice({
|
|
753
|
+
packageRoot,
|
|
754
|
+
configRoot: sourceRoot,
|
|
755
|
+
workspaceRoot,
|
|
756
|
+
stateRoot,
|
|
757
|
+
env: context.env,
|
|
758
|
+
});
|
|
759
|
+
return advice;
|
|
760
|
+
};
|
|
761
|
+
const getDelivery = () => {
|
|
762
|
+
delivery ??= createDelivery({
|
|
763
|
+
packageRoot,
|
|
764
|
+
configRoot: sourceRoot,
|
|
765
|
+
workspaceRoot,
|
|
766
|
+
stateRoot,
|
|
767
|
+
executablePath: context.executablePath ?? resolveDefaultExecutable(packageRoot),
|
|
768
|
+
env: context.env,
|
|
769
|
+
});
|
|
770
|
+
return delivery;
|
|
771
|
+
};
|
|
772
|
+
const getResultGate = () => {
|
|
773
|
+
resultGate ??= createResultGateService({
|
|
774
|
+
packageRoot,
|
|
775
|
+
configRoot: sourceRoot,
|
|
776
|
+
workspaceRoot,
|
|
777
|
+
stateRoot,
|
|
778
|
+
executablePath: context.executablePath ?? resolveDefaultExecutable(packageRoot),
|
|
779
|
+
env: context.env,
|
|
780
|
+
});
|
|
781
|
+
return resultGate;
|
|
782
|
+
};
|
|
783
|
+
const getReviewDelivery = () => {
|
|
784
|
+
reviewDelivery ??= createReviewDeliveryService({
|
|
785
|
+
packageRoot,
|
|
786
|
+
configRoot: sourceRoot,
|
|
787
|
+
workspaceRoot,
|
|
788
|
+
stateRoot,
|
|
789
|
+
requiredCheckName: OMS_REVIEW_REQUIRED_CHECK,
|
|
790
|
+
env: context.env,
|
|
791
|
+
});
|
|
792
|
+
return reviewDelivery;
|
|
793
|
+
};
|
|
794
|
+
await serve({
|
|
795
|
+
packageRoot,
|
|
796
|
+
configRoot: sourceRoot,
|
|
797
|
+
workspaceRoot,
|
|
798
|
+
stateRoot,
|
|
799
|
+
sessionId: conductor.sessionId,
|
|
800
|
+
actorId: "oms-conductor",
|
|
801
|
+
adviseDispatch: (intent) => getAdvice().advise(intent),
|
|
802
|
+
deliverAcceptedDispatch: (eventId) => getDelivery().deliver(eventId),
|
|
803
|
+
manageWorker: (request) => manageWorker(getDelivery(), request),
|
|
804
|
+
startTeam: (dispatchIds) => startExecutorGroup({
|
|
805
|
+
packageRoot,
|
|
806
|
+
configRoot: sourceRoot,
|
|
807
|
+
workspaceRoot,
|
|
808
|
+
stateRoot,
|
|
809
|
+
dispatchIds,
|
|
810
|
+
delivery: getDelivery(),
|
|
811
|
+
}),
|
|
812
|
+
processResult: (dispatchId) => {
|
|
813
|
+
const resultGateProgress = getResultGate().process(dispatchId);
|
|
814
|
+
return {
|
|
815
|
+
result_gate: resultGateProgress,
|
|
816
|
+
review_delivery: resultGateProgress.status === "completed"
|
|
817
|
+
? getReviewDelivery().advance(dispatchId, resultGateProgress)
|
|
818
|
+
: null,
|
|
819
|
+
};
|
|
820
|
+
},
|
|
821
|
+
input: process.stdin,
|
|
822
|
+
output: process.stdout,
|
|
823
|
+
});
|
|
824
|
+
return { ok: true, exitCode: 0 };
|
|
825
|
+
}
|
|
826
|
+
if (conductor.command === "observe") {
|
|
827
|
+
const raw = context.readStdin ? context.readStdin() : readBoundedUtf8(0, 64 * 1024);
|
|
828
|
+
const input = JSON.parse(raw);
|
|
829
|
+
const hook = parseConductorActivityHook(input, conductor.sessionId, conductor.hookEvent, workspaceRoot);
|
|
830
|
+
const binding = inspectConductorRuntimeBinding({
|
|
831
|
+
workspaceRoot,
|
|
832
|
+
stateRoot,
|
|
833
|
+
sessionId: conductor.sessionId,
|
|
834
|
+
env: context.env,
|
|
835
|
+
});
|
|
836
|
+
const runtime = createEnduranceAuthorities({ packageRoot, configRoot: sourceRoot, stateRoot });
|
|
837
|
+
try {
|
|
838
|
+
runtime.conductorActivity.observe({
|
|
839
|
+
conductorSessionId: binding.session_id,
|
|
840
|
+
launchGeneration: binding.launch_generation,
|
|
841
|
+
paneId: binding.pane_id,
|
|
842
|
+
conductorProcess: readLinuxProcessIdentity(binding.process_id),
|
|
843
|
+
providerVersion: conductor.providerVersion,
|
|
844
|
+
hookEvent: hook.hook_event_name,
|
|
845
|
+
sourceSha256: sha256Canonical(input),
|
|
846
|
+
observedAt: (context.clockSample ?? systemClockSample)().wallTime,
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
finally {
|
|
850
|
+
runtime.close();
|
|
851
|
+
}
|
|
852
|
+
return { ok: true, exitCode: 0 };
|
|
853
|
+
}
|
|
854
|
+
if (conductor.command === "consume-event") {
|
|
855
|
+
const raw = context.readStdin ? context.readStdin() : readBoundedUtf8(0, 64 * 1024);
|
|
856
|
+
const input = JSON.parse(raw);
|
|
857
|
+
const runtime = createEnduranceAuthorities({ packageRoot, configRoot: sourceRoot, stateRoot });
|
|
858
|
+
try {
|
|
859
|
+
const delivered = runtime.notifications.list().filter((record) => (record.state === "delivered"
|
|
860
|
+
&& record.payload.delivered_to_session_id === conductor.sessionId));
|
|
861
|
+
if (delivered.length > 1)
|
|
862
|
+
throw new Error("conductor has multiple delivered notifications");
|
|
863
|
+
if (delivered[0] && isMatchingConductorNotificationRead(input, conductor.sessionId, workspaceRoot, stateRoot, delivered[0].record_id)) {
|
|
864
|
+
runtime.notifications.consume({
|
|
865
|
+
notificationId: delivered[0].record_id,
|
|
866
|
+
sessionId: conductor.sessionId,
|
|
867
|
+
observedAt: (context.clockSample ?? systemClockSample)().wallTime,
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
finally {
|
|
872
|
+
runtime.close();
|
|
873
|
+
}
|
|
874
|
+
return { ok: true, exitCode: 0 };
|
|
875
|
+
}
|
|
876
|
+
if (conductor.command === "wait-event") {
|
|
877
|
+
const raw = context.readStdin ? context.readStdin() : readBoundedUtf8(0, 64 * 1024);
|
|
878
|
+
const hook = parseConductorWaitHook(JSON.parse(raw), conductor.sessionId, conductor.hookEvent, workspaceRoot);
|
|
879
|
+
const binding = inspectConductorRuntimeBinding({
|
|
880
|
+
workspaceRoot,
|
|
881
|
+
stateRoot,
|
|
882
|
+
sessionId: conductor.sessionId,
|
|
883
|
+
env: context.env,
|
|
884
|
+
});
|
|
885
|
+
const wait = context.conductorWait ?? waitForConductorNotification;
|
|
886
|
+
const runtime = createEnduranceAuthorities({ packageRoot, configRoot: sourceRoot, stateRoot });
|
|
887
|
+
let result;
|
|
888
|
+
try {
|
|
889
|
+
result = await wait({
|
|
890
|
+
waiterStore: runtime.waiters,
|
|
891
|
+
notificationStore: runtime.notifications,
|
|
892
|
+
arm: {
|
|
893
|
+
conductorSessionId: binding.session_id,
|
|
894
|
+
launchGeneration: binding.launch_generation,
|
|
895
|
+
paneId: binding.pane_id,
|
|
896
|
+
conductorProcess: readLinuxProcessIdentity(binding.process_id),
|
|
897
|
+
waiterProcess: readLinuxProcessIdentity(),
|
|
898
|
+
armedBy: hook.hook_event_name,
|
|
899
|
+
observedAt: (context.clockSample ?? systemClockSample)().wallTime,
|
|
900
|
+
},
|
|
901
|
+
canDeliver: () => runtime.conductorActivity.readIfExists(binding.session_id, binding.launch_generation)?.state === "idle",
|
|
902
|
+
conductorIsLive: linuxProcessIdentityIsLive,
|
|
903
|
+
onNotificationClaimed: (notificationId, observedAt) => {
|
|
904
|
+
runtime.conductorActivity.observe({
|
|
905
|
+
conductorSessionId: binding.session_id,
|
|
906
|
+
launchGeneration: binding.launch_generation,
|
|
907
|
+
paneId: binding.pane_id,
|
|
908
|
+
conductorProcess: readLinuxProcessIdentity(binding.process_id),
|
|
909
|
+
providerVersion: conductor.providerVersion,
|
|
910
|
+
hookEvent: "NotificationWake",
|
|
911
|
+
sourceSha256: createHash("sha256").update(notificationId, "utf8").digest("hex"),
|
|
912
|
+
observedAt,
|
|
913
|
+
});
|
|
914
|
+
},
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
finally {
|
|
918
|
+
runtime.close();
|
|
919
|
+
}
|
|
920
|
+
if (result.notificationId !== null) {
|
|
921
|
+
(context.writeStderr ?? ((value) => process.stderr.write(value)))(`${result.notificationId}\n`);
|
|
922
|
+
}
|
|
923
|
+
return { ok: result.exitCode !== 1, exitCode: result.exitCode };
|
|
924
|
+
}
|
|
925
|
+
return {
|
|
926
|
+
ok: true,
|
|
927
|
+
exitCode: 0,
|
|
928
|
+
output: conductorNext({
|
|
929
|
+
packageRoot,
|
|
930
|
+
configRoot: sourceRoot,
|
|
931
|
+
stateRoot,
|
|
932
|
+
runId: conductor.runId,
|
|
933
|
+
}),
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
case "structured-role": {
|
|
937
|
+
const structuredRole = parseStructuredRoleArgs(args);
|
|
938
|
+
const roleWorkspaceRoot = validateExistingDirectory(resolve(structuredRole.workspaceRoot), "structured-role workspace root");
|
|
939
|
+
const stateRoot = requireInitializedState({
|
|
940
|
+
workspaceRoot: roleWorkspaceRoot,
|
|
941
|
+
override: structuredRole.stateRoot,
|
|
942
|
+
env: context.env,
|
|
943
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
944
|
+
});
|
|
945
|
+
const serve = context.structuredRoleBridge ?? runClaudeStructuredRoleBridge;
|
|
946
|
+
await serve({
|
|
947
|
+
packageRoot,
|
|
948
|
+
stateRoot,
|
|
949
|
+
requestId: structuredRole.requestId,
|
|
950
|
+
sessionId: structuredRole.sessionId,
|
|
951
|
+
onResultSubmitted: (result) => {
|
|
952
|
+
projectClaudeStructuredRoleCompletion({
|
|
953
|
+
packageRoot,
|
|
954
|
+
stateRoot,
|
|
955
|
+
requestId: result.request_id,
|
|
956
|
+
});
|
|
957
|
+
},
|
|
958
|
+
onResultProjectionFailure: (requestId, error) => {
|
|
959
|
+
const write = context.writeStderr ?? ((value) => process.stderr.write(value));
|
|
960
|
+
const reason = errorMessage(error).trim().replace(/[\0\r\n]+/gu, " ").slice(0, 512) || "unknown projection error";
|
|
961
|
+
write(`oms: structured completion ${requestId} awaits watchdog recovery: ${reason}\n`);
|
|
962
|
+
},
|
|
963
|
+
input: process.stdin,
|
|
964
|
+
output: process.stdout,
|
|
965
|
+
});
|
|
966
|
+
return { ok: true, exitCode: 0 };
|
|
967
|
+
}
|
|
968
|
+
case "worker": {
|
|
969
|
+
const worker = parseWorkerArgs(args);
|
|
970
|
+
const workerWorkspaceRoot = worker.workspaceRoot
|
|
971
|
+
? validateExistingDirectory(resolve(worker.workspaceRoot), "worker workspace root")
|
|
972
|
+
: workspaceRoot;
|
|
973
|
+
const workerConfigRoot = worker.configRoot
|
|
974
|
+
? validateExistingDirectory(resolve(worker.configRoot), "worker configuration root")
|
|
975
|
+
: sourceRoot;
|
|
976
|
+
const stateRoot = requireInitializedState({
|
|
977
|
+
workspaceRoot: workerWorkspaceRoot,
|
|
978
|
+
override: worker.stateRoot,
|
|
979
|
+
env: context.env,
|
|
980
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
981
|
+
});
|
|
982
|
+
if (worker.command === "bridge") {
|
|
983
|
+
const serve = context.workerBridge ?? runWorkerBridge;
|
|
984
|
+
await serve({
|
|
985
|
+
packageRoot,
|
|
986
|
+
configRoot: workerConfigRoot,
|
|
987
|
+
workspaceRoot: workerWorkspaceRoot,
|
|
988
|
+
stateRoot,
|
|
989
|
+
dispatchId: worker.dispatchId,
|
|
990
|
+
sessionId: worker.sessionId,
|
|
991
|
+
cwd: context.cwd ?? process.cwd(),
|
|
992
|
+
env: context.env,
|
|
993
|
+
input: process.stdin,
|
|
994
|
+
output: process.stdout,
|
|
995
|
+
});
|
|
996
|
+
return { ok: true, exitCode: 0 };
|
|
997
|
+
}
|
|
998
|
+
const createDelivery = context.workerDelivery ?? createWorkerDeliveryService;
|
|
999
|
+
const delivery = createDelivery({
|
|
1000
|
+
packageRoot,
|
|
1001
|
+
configRoot: workerConfigRoot,
|
|
1002
|
+
workspaceRoot: workerWorkspaceRoot,
|
|
1003
|
+
stateRoot,
|
|
1004
|
+
executablePath: context.executablePath ?? resolveDefaultExecutable(packageRoot),
|
|
1005
|
+
env: context.env,
|
|
1006
|
+
});
|
|
1007
|
+
if (worker.command === "deliver") {
|
|
1008
|
+
return { ok: true, exitCode: 0, output: delivery.deliver(worker.eventId) };
|
|
1009
|
+
}
|
|
1010
|
+
if (worker.command === "status") {
|
|
1011
|
+
return { ok: true, exitCode: 0, output: delivery.observe(worker.dispatchId) };
|
|
1012
|
+
}
|
|
1013
|
+
if (worker.command === "resume") {
|
|
1014
|
+
return { ok: true, exitCode: 0, output: delivery.resume(worker.dispatchId) };
|
|
1015
|
+
}
|
|
1016
|
+
if (worker.command !== "park" && worker.command !== "stop")
|
|
1017
|
+
throw new Error("unsupported worker lifecycle command");
|
|
1018
|
+
const reason = readReasonInput(resolve(context.cwd ?? workspaceRoot, worker.reasonFile), packageRoot);
|
|
1019
|
+
return {
|
|
1020
|
+
ok: true,
|
|
1021
|
+
exitCode: 0,
|
|
1022
|
+
output: worker.command === "park"
|
|
1023
|
+
? delivery.park(worker.dispatchId, reason, worker.parkedUntil)
|
|
1024
|
+
: delivery.stop(worker.dispatchId, reason),
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
case "goal": {
|
|
1028
|
+
const goal = parseGoalArgs(args);
|
|
1029
|
+
const stateRoot = requireInitializedState({
|
|
1030
|
+
workspaceRoot,
|
|
1031
|
+
override: goal.stateRoot,
|
|
1032
|
+
env: context.env,
|
|
1033
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
1034
|
+
});
|
|
1035
|
+
const runtime = createRuntimeGoalAuthority({ packageRoot, stateRoot });
|
|
1036
|
+
if (goal.command === "create") {
|
|
1037
|
+
const inputPath = resolve(context.cwd ?? workspaceRoot, goal.file);
|
|
1038
|
+
const input = readJsonFileNoFollow(inputPath, 128 * 1024);
|
|
1039
|
+
assertSchema(input, loadJsonSchema(join(packageRoot, "schemas", "goal-create-input.schema.json")), "goal create input");
|
|
1040
|
+
const candidate = input;
|
|
1041
|
+
const created = createGoal(runtime.authority, {
|
|
1042
|
+
objective: candidate.objective,
|
|
1043
|
+
stoppingCondition: candidate.stopping_condition,
|
|
1044
|
+
repositoryScope: workspaceRoot,
|
|
1045
|
+
leaderId: candidate.leader_id,
|
|
1046
|
+
createdAt: candidate.created_at,
|
|
1047
|
+
});
|
|
1048
|
+
return { ok: true, exitCode: 0, output: goalSummary(created) };
|
|
1049
|
+
}
|
|
1050
|
+
return { ok: true, exitCode: 0, output: goalSummary(readGoal(runtime.authority, goal.goalId)) };
|
|
1051
|
+
}
|
|
1052
|
+
case "team": {
|
|
1053
|
+
const team = parseTeamArgs(args);
|
|
1054
|
+
const stateRoot = requireInitializedState({
|
|
1055
|
+
workspaceRoot,
|
|
1056
|
+
override: team.stateRoot,
|
|
1057
|
+
env: context.env,
|
|
1058
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
1059
|
+
});
|
|
1060
|
+
const createDelivery = context.workerDelivery ?? createWorkerDeliveryService;
|
|
1061
|
+
const delivery = createDelivery({
|
|
1062
|
+
packageRoot,
|
|
1063
|
+
configRoot: sourceRoot,
|
|
1064
|
+
workspaceRoot,
|
|
1065
|
+
stateRoot,
|
|
1066
|
+
executablePath: context.executablePath ?? resolveDefaultExecutable(packageRoot),
|
|
1067
|
+
env: context.env,
|
|
1068
|
+
});
|
|
1069
|
+
return {
|
|
1070
|
+
ok: true,
|
|
1071
|
+
exitCode: 0,
|
|
1072
|
+
output: startExecutorGroup({
|
|
1073
|
+
packageRoot,
|
|
1074
|
+
configRoot: sourceRoot,
|
|
1075
|
+
workspaceRoot,
|
|
1076
|
+
stateRoot,
|
|
1077
|
+
dispatchIds: team.dispatchIds,
|
|
1078
|
+
delivery,
|
|
1079
|
+
}),
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
case "trust": {
|
|
1083
|
+
const trust = parseTrustArgs(args);
|
|
1084
|
+
const stateRoot = requireInitializedState({
|
|
1085
|
+
workspaceRoot,
|
|
1086
|
+
override: trust.stateRoot,
|
|
1087
|
+
env: context.env,
|
|
1088
|
+
cwd: context.cwd ?? workspaceRoot,
|
|
1089
|
+
});
|
|
1090
|
+
const tickets = createRuntimeTicketAuthorities({
|
|
1091
|
+
packageRoot,
|
|
1092
|
+
configRoot: sourceRoot,
|
|
1093
|
+
stateRoot,
|
|
1094
|
+
actorId: "oms-system",
|
|
1095
|
+
});
|
|
1096
|
+
return {
|
|
1097
|
+
ok: true,
|
|
1098
|
+
exitCode: 0,
|
|
1099
|
+
output: writeDailyTrustReport({
|
|
1100
|
+
evidenceStore: tickets.evidenceStore,
|
|
1101
|
+
utcDate: trust.utcDate,
|
|
1102
|
+
packageRoot: sourceRoot,
|
|
1103
|
+
}),
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
case "hud": {
|
|
1107
|
+
const override = parseStateRoot(args);
|
|
1108
|
+
const stateRoot = requireInitializedState({ workspaceRoot, override, env: context.env, cwd: context.cwd ?? workspaceRoot });
|
|
1109
|
+
const runtime = createEnduranceAuthorities({ packageRoot, configRoot: sourceRoot, stateRoot });
|
|
1110
|
+
try {
|
|
1111
|
+
const snapshot = createHudSnapshot({
|
|
1112
|
+
queue: runtime.tickets.queue,
|
|
1113
|
+
sessions: runtime.sessions,
|
|
1114
|
+
quota: runtime.quota,
|
|
1115
|
+
outbox: runtime.outbox,
|
|
1116
|
+
workers: runtime.workers,
|
|
1117
|
+
conductorActivity: runtime.conductorActivity,
|
|
1118
|
+
conductorProcessIsLive: linuxProcessIdentityIsLive,
|
|
1119
|
+
});
|
|
1120
|
+
return { ok: true, exitCode: 0, message: renderHud(snapshot), output: snapshot };
|
|
1121
|
+
}
|
|
1122
|
+
finally {
|
|
1123
|
+
runtime.close();
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
case "release": {
|
|
1127
|
+
if (args[0] !== "status")
|
|
1128
|
+
throw new Error("release requires the status subcommand");
|
|
1129
|
+
requireNoOptions("release status", args.slice(1));
|
|
1130
|
+
return { ok: true, exitCode: 0, output: assessRelease(packageRoot) };
|
|
1131
|
+
}
|
|
1132
|
+
case "verify": {
|
|
1133
|
+
requireNoOptions("verify", args);
|
|
1134
|
+
const lines = context.verify
|
|
1135
|
+
? context.verify(packageRoot, workspaceRoot, sourceRoot)
|
|
1136
|
+
: runVerification(packageRoot, { workspaceRoot, configRoot: sourceRoot });
|
|
1137
|
+
return { ok: true, exitCode: 0, message: lines.join("\n") };
|
|
1138
|
+
}
|
|
1139
|
+
default:
|
|
1140
|
+
throw new Error(`command not implemented in M0: ${command}`);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
catch (error) {
|
|
1144
|
+
return { ok: false, exitCode: 1, message: `oms: ${errorMessage(error)}` };
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
function hasOmsConfiguration(root) {
|
|
1148
|
+
const configPath = join(root, ".agents", "config", "oms-config.json");
|
|
1149
|
+
const entry = lstatIfExists(configPath);
|
|
1150
|
+
if (!entry)
|
|
1151
|
+
return false;
|
|
1152
|
+
if (!entry.isFile() || entry.isSymbolicLink()) {
|
|
1153
|
+
throw new Error(`OMS configuration must be a non-symbolic regular file: ${configPath}`);
|
|
1154
|
+
}
|
|
1155
|
+
return true;
|
|
1156
|
+
}
|
|
1157
|
+
function isOmsGitWorkspace(root) {
|
|
1158
|
+
if (!hasOmsConfiguration(root))
|
|
1159
|
+
return false;
|
|
1160
|
+
readOmsConfig(root);
|
|
1161
|
+
return isGitWorkspace(root);
|
|
1162
|
+
}
|
|
1163
|
+
function isGitWorkspace(root) {
|
|
1164
|
+
const gitPath = join(root, ".git");
|
|
1165
|
+
const entry = lstatIfExists(gitPath);
|
|
1166
|
+
if (!entry)
|
|
1167
|
+
return false;
|
|
1168
|
+
if (entry.isSymbolicLink() || (!entry.isDirectory() && !entry.isFile())) {
|
|
1169
|
+
throw new Error(`OMS Git marker must be a non-symbolic file or directory: ${gitPath}`);
|
|
1170
|
+
}
|
|
1171
|
+
return true;
|
|
1172
|
+
}
|
|
1173
|
+
function discoverAuthorizedConductorStateRoot(options) {
|
|
1174
|
+
const env = options.env ?? process.env;
|
|
1175
|
+
if (env.OMS_STATE_ROOT)
|
|
1176
|
+
return undefined;
|
|
1177
|
+
const defaultRoot = join(options.workspaceRoot, ".oms");
|
|
1178
|
+
if (!existsSync(defaultRoot))
|
|
1179
|
+
return undefined;
|
|
1180
|
+
const rootEntry = lstatSync(defaultRoot);
|
|
1181
|
+
if (!rootEntry.isDirectory() || rootEntry.isSymbolicLink()) {
|
|
1182
|
+
throw new Error("OMS requires .oms to be a non-symbolic directory");
|
|
1183
|
+
}
|
|
1184
|
+
const candidates = [
|
|
1185
|
+
defaultRoot,
|
|
1186
|
+
...readdirSync(defaultRoot, { withFileTypes: true })
|
|
1187
|
+
.filter((entry) => entry.isDirectory())
|
|
1188
|
+
.map((entry) => join(defaultRoot, entry.name)),
|
|
1189
|
+
];
|
|
1190
|
+
const authorized = candidates.filter((candidate) => {
|
|
1191
|
+
const authorizationPath = join(candidate, "conductors", "live-delivery-authorization.json");
|
|
1192
|
+
if (!existsSync(authorizationPath))
|
|
1193
|
+
return false;
|
|
1194
|
+
const state = readStateSummary({
|
|
1195
|
+
workspaceRoot: options.workspaceRoot,
|
|
1196
|
+
override: candidate,
|
|
1197
|
+
env,
|
|
1198
|
+
cwd: options.cwd,
|
|
1199
|
+
});
|
|
1200
|
+
if (!state.initialized)
|
|
1201
|
+
throw new Error(`authorized OMS state is not initialized: ${candidate}`);
|
|
1202
|
+
return readLiveDeliveryAuthorization(candidate) !== undefined;
|
|
1203
|
+
});
|
|
1204
|
+
if (authorized.length > 1) {
|
|
1205
|
+
throw new Error("OMS found multiple repository-authorized state roots; set OMS_STATE_ROOT explicitly");
|
|
1206
|
+
}
|
|
1207
|
+
return authorized[0];
|
|
1208
|
+
}
|
|
1209
|
+
export function printResult(result) {
|
|
1210
|
+
if (result.output !== undefined)
|
|
1211
|
+
process.stdout.write(`${JSON.stringify(result.output, null, 2)}\n`);
|
|
1212
|
+
if (result.message) {
|
|
1213
|
+
const stream = result.ok ? process.stdout : process.stderr;
|
|
1214
|
+
stream.write(`${result.message}\n`);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
1218
|
+
const result = await runCli(argv, {
|
|
1219
|
+
requestBareLiveDeliveryAuthorization: requestBareLiveDeliveryAuthorization,
|
|
1220
|
+
});
|
|
1221
|
+
printResult(result);
|
|
1222
|
+
process.exitCode = result.exitCode;
|
|
1223
|
+
}
|
|
1224
|
+
export async function requestBareLiveDeliveryAuthorization(request, io = {}) {
|
|
1225
|
+
const expectedRepository = request.required_repository ?? request.suggested_repository;
|
|
1226
|
+
const exampleRepository = expectedRepository ?? "OWNER/REPO";
|
|
1227
|
+
const input = io.input ?? process.stdin;
|
|
1228
|
+
const output = io.output ?? process.stdout;
|
|
1229
|
+
output.write([
|
|
1230
|
+
"OMS requires one human confirmation before its first live conductor session.",
|
|
1231
|
+
`Repository: ${expectedRepository ?? "not detected; enter the actual OWNER/REPO"}`,
|
|
1232
|
+
"Provider spend: subscription",
|
|
1233
|
+
"GitHub spend: none",
|
|
1234
|
+
`Allowed operations: ${[...LIVE_CONTACT_OPERATIONS, PRE_DISPATCH_ADVICE_OPERATION].join(", ")}`,
|
|
1235
|
+
"OMS cannot merge, release, publish, or modify branch protection.",
|
|
1236
|
+
].join("\n") + "\n");
|
|
1237
|
+
const terminal = createInterface({ input, output });
|
|
1238
|
+
try {
|
|
1239
|
+
const prompt = expectedRepository
|
|
1240
|
+
? `Type \"authorize ${exampleRepository}\" to continue: `
|
|
1241
|
+
: "Type \"authorize OWNER/REPO\" with the actual repository to continue: ";
|
|
1242
|
+
const answer = await Promise.race([
|
|
1243
|
+
terminal.question(prompt),
|
|
1244
|
+
new Promise((resolveClosed) => terminal.once("close", () => resolveClosed(null))),
|
|
1245
|
+
]);
|
|
1246
|
+
if (answer === null)
|
|
1247
|
+
return null;
|
|
1248
|
+
const match = /^authorize ([^\s]+)$/u.exec(answer.trim());
|
|
1249
|
+
return match?.[1] ?? null;
|
|
1250
|
+
}
|
|
1251
|
+
finally {
|
|
1252
|
+
terminal.close();
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
function inferBareGithubRepository(options) {
|
|
1256
|
+
const result = options.runner({
|
|
1257
|
+
command: "git",
|
|
1258
|
+
args: ["remote", "get-url", "--push", "origin"],
|
|
1259
|
+
cwd: options.workspaceRoot,
|
|
1260
|
+
env: bareGitEnvironment(options.env ?? process.env),
|
|
1261
|
+
timeoutMs: 10_000,
|
|
1262
|
+
maxBufferBytes: 64 * 1024,
|
|
1263
|
+
});
|
|
1264
|
+
if (result.error || result.status !== 0 || result.signal !== null)
|
|
1265
|
+
return null;
|
|
1266
|
+
try {
|
|
1267
|
+
return normalizeGithubRemoteRepository(result.stdout.trim());
|
|
1268
|
+
}
|
|
1269
|
+
catch {
|
|
1270
|
+
return null;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
function bareGitEnvironment(source) {
|
|
1274
|
+
const env = {};
|
|
1275
|
+
for (const key of ["PATH", "HOME", "TMPDIR", "TMP", "TEMP", "LANG", "LC_ALL", "SYSTEMROOT"]) {
|
|
1276
|
+
if (source[key] !== undefined)
|
|
1277
|
+
env[key] = source[key];
|
|
1278
|
+
}
|
|
1279
|
+
env.GIT_TERMINAL_PROMPT = "0";
|
|
1280
|
+
env.GIT_CONFIG_NOSYSTEM = "1";
|
|
1281
|
+
env.GIT_CONFIG_GLOBAL = "/dev/null";
|
|
1282
|
+
return env;
|
|
1283
|
+
}
|
|
1284
|
+
function parseStateRoot(args) {
|
|
1285
|
+
let value;
|
|
1286
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1287
|
+
const argument = args[index];
|
|
1288
|
+
if (argument !== "--state-root")
|
|
1289
|
+
throw new Error(`unknown option: ${argument ?? ""}`);
|
|
1290
|
+
const next = args[index + 1];
|
|
1291
|
+
if (!next)
|
|
1292
|
+
throw new Error("--state-root requires a path");
|
|
1293
|
+
value = next;
|
|
1294
|
+
index += 1;
|
|
1295
|
+
}
|
|
1296
|
+
return value;
|
|
1297
|
+
}
|
|
1298
|
+
function parseProviderHookArgs(args) {
|
|
1299
|
+
if (args[0] !== "provider")
|
|
1300
|
+
throw new Error("hook requires ingest or provider");
|
|
1301
|
+
const values = new Map();
|
|
1302
|
+
const allowed = new Set([
|
|
1303
|
+
"--provider",
|
|
1304
|
+
"--provider-version",
|
|
1305
|
+
"--dispatch-id",
|
|
1306
|
+
"--session-id",
|
|
1307
|
+
"--state-root",
|
|
1308
|
+
"--workspace-root",
|
|
1309
|
+
"--config-root",
|
|
1310
|
+
]);
|
|
1311
|
+
for (let index = 1; index < args.length; index += 2) {
|
|
1312
|
+
const option = args[index];
|
|
1313
|
+
const value = args[index + 1];
|
|
1314
|
+
if (!option || !allowed.has(option))
|
|
1315
|
+
throw new Error(`unknown option: ${option ?? ""}`);
|
|
1316
|
+
if (values.has(option))
|
|
1317
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1318
|
+
if (!value || value.startsWith("--"))
|
|
1319
|
+
throw new Error(`${option} requires a value`);
|
|
1320
|
+
values.set(option, value);
|
|
1321
|
+
}
|
|
1322
|
+
const provider = values.get("--provider");
|
|
1323
|
+
if (provider !== "claude" && provider !== "codex")
|
|
1324
|
+
throw new Error("hook provider requires --provider claude|codex");
|
|
1325
|
+
const providerVersion = values.get("--provider-version");
|
|
1326
|
+
if (providerVersion !== SUPPORTED_PROVIDER_HOOK_VERSIONS[provider]) {
|
|
1327
|
+
throw new Error(`unsupported ${provider} provider version`);
|
|
1328
|
+
}
|
|
1329
|
+
const dispatchId = values.get("--dispatch-id");
|
|
1330
|
+
const sessionId = values.get("--session-id");
|
|
1331
|
+
const stateRoot = values.get("--state-root");
|
|
1332
|
+
const workspaceRoot = values.get("--workspace-root");
|
|
1333
|
+
const configRoot = values.get("--config-root");
|
|
1334
|
+
if (!dispatchId || !sessionId || !stateRoot || !workspaceRoot || !configRoot) {
|
|
1335
|
+
throw new Error("hook provider requires dispatch, session, state, workspace, and config bindings");
|
|
1336
|
+
}
|
|
1337
|
+
return { provider, providerVersion, dispatchId, sessionId, stateRoot, workspaceRoot, configRoot };
|
|
1338
|
+
}
|
|
1339
|
+
function requireNoOptions(command, args) {
|
|
1340
|
+
if (args.length > 0)
|
|
1341
|
+
throw new Error(`${command} takes no options`);
|
|
1342
|
+
}
|
|
1343
|
+
function resolveDefaultExecutable(packageRoot) {
|
|
1344
|
+
const sourceShim = join(packageRoot, "bin", "oms");
|
|
1345
|
+
if (existsSync(sourceShim))
|
|
1346
|
+
return realpathSync(sourceShim);
|
|
1347
|
+
const entrypoint = process.argv[1];
|
|
1348
|
+
if (!entrypoint)
|
|
1349
|
+
throw new Error("unable to resolve the installed OMS executable");
|
|
1350
|
+
return realpathSync(entrypoint);
|
|
1351
|
+
}
|
|
1352
|
+
export function readBoundedUtf8(fileDescriptor, maxBytes) {
|
|
1353
|
+
if (!Number.isSafeInteger(fileDescriptor) || fileDescriptor < 0)
|
|
1354
|
+
throw new Error("invalid bounded input descriptor");
|
|
1355
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1)
|
|
1356
|
+
throw new Error("invalid bounded input limit");
|
|
1357
|
+
const chunks = [];
|
|
1358
|
+
let total = 0;
|
|
1359
|
+
while (true) {
|
|
1360
|
+
const chunk = Buffer.allocUnsafe(Math.min(8 * 1024, maxBytes - total + 1));
|
|
1361
|
+
const count = readSync(fileDescriptor, chunk, 0, chunk.byteLength, null);
|
|
1362
|
+
if (count === 0)
|
|
1363
|
+
break;
|
|
1364
|
+
total += count;
|
|
1365
|
+
if (total > maxBytes)
|
|
1366
|
+
throw new Error(`native hook input exceeds ${maxBytes} bytes`);
|
|
1367
|
+
chunks.push(chunk.subarray(0, count));
|
|
1368
|
+
}
|
|
1369
|
+
return Buffer.concat(chunks, total).toString("utf8");
|
|
1370
|
+
}
|
|
1371
|
+
function requireInitializedState(options) {
|
|
1372
|
+
const summary = readStateSummary({
|
|
1373
|
+
...options,
|
|
1374
|
+
override: activeStateRootOverride(options),
|
|
1375
|
+
});
|
|
1376
|
+
if (!summary.initialized)
|
|
1377
|
+
throw new Error(`runtime state is not initialized: ${summary.state_root}`);
|
|
1378
|
+
return summary.state_root;
|
|
1379
|
+
}
|
|
1380
|
+
function activeStateRootOverride(options) {
|
|
1381
|
+
return options.override ?? discoverAuthorizedConductorStateRoot(options);
|
|
1382
|
+
}
|
|
1383
|
+
function parseReconcileArgs(args) {
|
|
1384
|
+
const kind = args[0];
|
|
1385
|
+
if (kind !== "minute" && kind !== "watchdog")
|
|
1386
|
+
throw new Error("reconcile requires minute or watchdog");
|
|
1387
|
+
let stateRoot;
|
|
1388
|
+
let workspaceRoot;
|
|
1389
|
+
const seen = new Set();
|
|
1390
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1391
|
+
const option = args[index];
|
|
1392
|
+
if (option === "--scheduled") {
|
|
1393
|
+
if (seen.has(option))
|
|
1394
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1395
|
+
seen.add(option);
|
|
1396
|
+
continue;
|
|
1397
|
+
}
|
|
1398
|
+
if (option !== "--state-root" && option !== "--workspace-root")
|
|
1399
|
+
throw new Error(`unknown option: ${option ?? ""}`);
|
|
1400
|
+
if (seen.has(option))
|
|
1401
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1402
|
+
seen.add(option);
|
|
1403
|
+
const value = args[index + 1];
|
|
1404
|
+
if (!value || value.startsWith("--"))
|
|
1405
|
+
throw new Error(`${option} requires a path`);
|
|
1406
|
+
if (option === "--state-root")
|
|
1407
|
+
stateRoot = value;
|
|
1408
|
+
if (option === "--workspace-root")
|
|
1409
|
+
workspaceRoot = value;
|
|
1410
|
+
index += 1;
|
|
1411
|
+
}
|
|
1412
|
+
return { kind, stateRoot, workspaceRoot };
|
|
1413
|
+
}
|
|
1414
|
+
function parseEventsArgs(args) {
|
|
1415
|
+
if (args[0] !== "sync" || args[1] !== "github")
|
|
1416
|
+
throw new Error("events requires sync github");
|
|
1417
|
+
let repository;
|
|
1418
|
+
let stateRoot;
|
|
1419
|
+
let workspaceRoot;
|
|
1420
|
+
const seen = new Set();
|
|
1421
|
+
for (let index = 2; index < args.length; index += 1) {
|
|
1422
|
+
const option = args[index];
|
|
1423
|
+
if (option !== "--repository" && option !== "--state-root" && option !== "--workspace-root") {
|
|
1424
|
+
throw new Error(`unknown option: ${option ?? ""}`);
|
|
1425
|
+
}
|
|
1426
|
+
if (seen.has(option))
|
|
1427
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1428
|
+
seen.add(option);
|
|
1429
|
+
const value = args[index + 1];
|
|
1430
|
+
if (!value || value.startsWith("--"))
|
|
1431
|
+
throw new Error(`${option} requires a value`);
|
|
1432
|
+
if (option === "--repository")
|
|
1433
|
+
repository = value;
|
|
1434
|
+
if (option === "--state-root")
|
|
1435
|
+
stateRoot = value;
|
|
1436
|
+
if (option === "--workspace-root")
|
|
1437
|
+
workspaceRoot = value;
|
|
1438
|
+
index += 1;
|
|
1439
|
+
}
|
|
1440
|
+
if (!repository)
|
|
1441
|
+
throw new Error("events sync github requires --repository");
|
|
1442
|
+
return { repository, stateRoot, workspaceRoot };
|
|
1443
|
+
}
|
|
1444
|
+
function parseScheduleArgs(args) {
|
|
1445
|
+
const command = args[0];
|
|
1446
|
+
if (command !== "generate" && command !== "install" && command !== "status" && command !== "remove") {
|
|
1447
|
+
throw new Error("schedule requires generate, install, status, or remove");
|
|
1448
|
+
}
|
|
1449
|
+
let manager;
|
|
1450
|
+
let repository;
|
|
1451
|
+
let githubPollSeconds;
|
|
1452
|
+
let managerExecutable;
|
|
1453
|
+
let stateRoot;
|
|
1454
|
+
const seen = new Set();
|
|
1455
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1456
|
+
const option = args[index];
|
|
1457
|
+
if (option !== "--manager"
|
|
1458
|
+
&& option !== "--repository"
|
|
1459
|
+
&& option !== "--github-poll-seconds"
|
|
1460
|
+
&& option !== "--manager-executable"
|
|
1461
|
+
&& option !== "--state-root") {
|
|
1462
|
+
throw new Error(`unknown option: ${option ?? ""}`);
|
|
1463
|
+
}
|
|
1464
|
+
if (seen.has(option))
|
|
1465
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1466
|
+
seen.add(option);
|
|
1467
|
+
const value = args[index + 1];
|
|
1468
|
+
if (!value || value.startsWith("--"))
|
|
1469
|
+
throw new Error(`${option} requires a value`);
|
|
1470
|
+
if (option === "--manager") {
|
|
1471
|
+
if (value !== "cron" && value !== "systemd" && value !== "launchd")
|
|
1472
|
+
throw new Error("invalid service manager");
|
|
1473
|
+
manager = value;
|
|
1474
|
+
}
|
|
1475
|
+
if (option === "--repository")
|
|
1476
|
+
repository = value;
|
|
1477
|
+
if (option === "--github-poll-seconds") {
|
|
1478
|
+
if (!/^[1-9][0-9]{1,3}$/u.test(value))
|
|
1479
|
+
throw new Error("invalid GitHub poll interval");
|
|
1480
|
+
githubPollSeconds = Number(value);
|
|
1481
|
+
}
|
|
1482
|
+
if (option === "--manager-executable")
|
|
1483
|
+
managerExecutable = value;
|
|
1484
|
+
if (option === "--state-root")
|
|
1485
|
+
stateRoot = value;
|
|
1486
|
+
index += 1;
|
|
1487
|
+
}
|
|
1488
|
+
if (!manager || !repository)
|
|
1489
|
+
throw new Error(`schedule ${command} requires --manager and --repository`);
|
|
1490
|
+
return { command, manager, repository, githubPollSeconds, managerExecutable, stateRoot };
|
|
1491
|
+
}
|
|
1492
|
+
function defaultManagerExecutable(manager) {
|
|
1493
|
+
if (manager === "systemd")
|
|
1494
|
+
return "/usr/bin/systemctl";
|
|
1495
|
+
if (manager === "launchd")
|
|
1496
|
+
return "/bin/launchctl";
|
|
1497
|
+
return "/usr/bin/crontab";
|
|
1498
|
+
}
|
|
1499
|
+
function parseRunArgs(args) {
|
|
1500
|
+
const command = args[0];
|
|
1501
|
+
if (command !== "start" && command !== "tick" && command !== "close" && command !== "abort" && command !== "recover-controller" && command !== "status") {
|
|
1502
|
+
throw new Error("run requires start, tick, close, abort, recover-controller, or status");
|
|
1503
|
+
}
|
|
1504
|
+
let runId;
|
|
1505
|
+
let stateRoot;
|
|
1506
|
+
let manager;
|
|
1507
|
+
let runKind;
|
|
1508
|
+
let reasonFile;
|
|
1509
|
+
let generationId;
|
|
1510
|
+
const seen = new Set();
|
|
1511
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1512
|
+
const option = args[index];
|
|
1513
|
+
if (!option)
|
|
1514
|
+
throw new Error("invalid empty run option");
|
|
1515
|
+
if (seen.has(option))
|
|
1516
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1517
|
+
seen.add(option);
|
|
1518
|
+
if (option === "--until-empty") {
|
|
1519
|
+
if (command !== "start")
|
|
1520
|
+
throw new Error("--until-empty is valid only for run start");
|
|
1521
|
+
if (runKind)
|
|
1522
|
+
throw new Error("run start requires exactly one terminal mode");
|
|
1523
|
+
runKind = "until-empty";
|
|
1524
|
+
continue;
|
|
1525
|
+
}
|
|
1526
|
+
if (!["--for", "--generation-id", "--manager", "--run-id", "--reason-file", "--state-root"].includes(option)) {
|
|
1527
|
+
throw new Error(`unknown option: ${option}`);
|
|
1528
|
+
}
|
|
1529
|
+
const value = args[index + 1];
|
|
1530
|
+
if (!value || value.startsWith("--"))
|
|
1531
|
+
throw new Error(`${option} requires a value`);
|
|
1532
|
+
index += 1;
|
|
1533
|
+
if (option === "--state-root")
|
|
1534
|
+
stateRoot = value;
|
|
1535
|
+
if (option === "--run-id")
|
|
1536
|
+
runId = value;
|
|
1537
|
+
if (option === "--reason-file")
|
|
1538
|
+
reasonFile = value;
|
|
1539
|
+
if (option === "--generation-id")
|
|
1540
|
+
generationId = value;
|
|
1541
|
+
if (option === "--for") {
|
|
1542
|
+
if (command !== "start")
|
|
1543
|
+
throw new Error("--for is valid only for run start");
|
|
1544
|
+
if (runKind)
|
|
1545
|
+
throw new Error("run start requires exactly one terminal mode");
|
|
1546
|
+
if (value !== "12h" && value !== "24h")
|
|
1547
|
+
throw new Error("run --for requires 12h or 24h");
|
|
1548
|
+
runKind = value;
|
|
1549
|
+
}
|
|
1550
|
+
if (option === "--manager") {
|
|
1551
|
+
if (command !== "start")
|
|
1552
|
+
throw new Error("--manager is valid only for run start");
|
|
1553
|
+
if (value !== "cron" && value !== "systemd" && value !== "launchd")
|
|
1554
|
+
throw new Error("invalid service manager");
|
|
1555
|
+
manager = value;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
if (command === "start") {
|
|
1559
|
+
if (!runKind)
|
|
1560
|
+
throw new Error("run start requires --until-empty or --for 12h|24h");
|
|
1561
|
+
if (!manager)
|
|
1562
|
+
throw new Error("run start requires --manager");
|
|
1563
|
+
if (runId || reasonFile || generationId)
|
|
1564
|
+
throw new Error("run start accepts only its terminal mode, manager, and state root");
|
|
1565
|
+
return { command, runKind, manager, stateRoot };
|
|
1566
|
+
}
|
|
1567
|
+
if (manager || runKind)
|
|
1568
|
+
throw new Error(`run ${command} does not accept a terminal mode or manager`);
|
|
1569
|
+
if (command === "recover-controller") {
|
|
1570
|
+
if (runId || !generationId || !reasonFile) {
|
|
1571
|
+
throw new Error("run recover-controller requires --generation-id and --reason-file and does not accept --run-id");
|
|
1572
|
+
}
|
|
1573
|
+
return { command, generationId, reasonFile, stateRoot };
|
|
1574
|
+
}
|
|
1575
|
+
if (generationId)
|
|
1576
|
+
throw new Error(`run ${command} does not accept --generation-id`);
|
|
1577
|
+
if (command === "abort") {
|
|
1578
|
+
if (!runId || !reasonFile)
|
|
1579
|
+
throw new Error("run abort requires --run-id and --reason-file");
|
|
1580
|
+
return { command, runId, reasonFile, stateRoot };
|
|
1581
|
+
}
|
|
1582
|
+
if (reasonFile)
|
|
1583
|
+
throw new Error(`run ${command} does not accept --reason-file`);
|
|
1584
|
+
if (command === "tick" || command === "close") {
|
|
1585
|
+
if (!runId)
|
|
1586
|
+
throw new Error(`run ${command} requires --run-id`);
|
|
1587
|
+
return { command, runId, stateRoot };
|
|
1588
|
+
}
|
|
1589
|
+
return { command, runId, stateRoot };
|
|
1590
|
+
}
|
|
1591
|
+
function parseConductorArgs(args) {
|
|
1592
|
+
const command = args[0];
|
|
1593
|
+
if (command !== "start"
|
|
1594
|
+
&& command !== "bridge"
|
|
1595
|
+
&& command !== "observe"
|
|
1596
|
+
&& command !== "consume-event"
|
|
1597
|
+
&& command !== "wait-event"
|
|
1598
|
+
&& command !== "next") {
|
|
1599
|
+
throw new Error("conductor requires start, bridge, observe, consume-event, wait-event, or next");
|
|
1600
|
+
}
|
|
1601
|
+
let runId;
|
|
1602
|
+
let sessionId;
|
|
1603
|
+
let stateRoot;
|
|
1604
|
+
let hookEvent;
|
|
1605
|
+
let providerVersion;
|
|
1606
|
+
let repository;
|
|
1607
|
+
let confirmLiveDelivery = false;
|
|
1608
|
+
const seen = new Set();
|
|
1609
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1610
|
+
const option = args[index];
|
|
1611
|
+
if (option === "--confirm-live-delivery") {
|
|
1612
|
+
if (seen.has(option))
|
|
1613
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1614
|
+
seen.add(option);
|
|
1615
|
+
confirmLiveDelivery = true;
|
|
1616
|
+
continue;
|
|
1617
|
+
}
|
|
1618
|
+
if (option !== "--run-id"
|
|
1619
|
+
&& option !== "--session-id"
|
|
1620
|
+
&& option !== "--state-root"
|
|
1621
|
+
&& option !== "--hook-event"
|
|
1622
|
+
&& option !== "--provider-version"
|
|
1623
|
+
&& option !== "--repository") {
|
|
1624
|
+
throw new Error(`unknown option: ${option ?? ""}`);
|
|
1625
|
+
}
|
|
1626
|
+
if (seen.has(option))
|
|
1627
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1628
|
+
seen.add(option);
|
|
1629
|
+
const value = args[index + 1];
|
|
1630
|
+
if (!value || value.startsWith("--"))
|
|
1631
|
+
throw new Error(`${option} requires a value`);
|
|
1632
|
+
if (option === "--run-id")
|
|
1633
|
+
runId = value;
|
|
1634
|
+
if (option === "--session-id")
|
|
1635
|
+
sessionId = value;
|
|
1636
|
+
if (option === "--state-root")
|
|
1637
|
+
stateRoot = value;
|
|
1638
|
+
if (option === "--hook-event") {
|
|
1639
|
+
if (!["SessionStart", "UserPromptSubmit", "Stop", "StopFailure", "SessionEnd"].includes(value)) {
|
|
1640
|
+
throw new Error("invalid conductor hook event");
|
|
1641
|
+
}
|
|
1642
|
+
hookEvent = value;
|
|
1643
|
+
}
|
|
1644
|
+
if (option === "--provider-version") {
|
|
1645
|
+
if (value !== SUPPORTED_PROVIDER_HOOK_VERSIONS.claude)
|
|
1646
|
+
throw new Error("unsupported conductor provider version");
|
|
1647
|
+
providerVersion = value;
|
|
1648
|
+
}
|
|
1649
|
+
if (option === "--repository")
|
|
1650
|
+
repository = value;
|
|
1651
|
+
index += 1;
|
|
1652
|
+
}
|
|
1653
|
+
if (command === "start") {
|
|
1654
|
+
if (runId || sessionId || hookEvent || providerVersion) {
|
|
1655
|
+
throw new Error("conductor start accepts only --state-root and paired live-delivery options");
|
|
1656
|
+
}
|
|
1657
|
+
if (confirmLiveDelivery !== (repository !== undefined)) {
|
|
1658
|
+
throw new Error("conductor start live delivery requires --repository and --confirm-live-delivery together");
|
|
1659
|
+
}
|
|
1660
|
+
return { command, stateRoot, repository };
|
|
1661
|
+
}
|
|
1662
|
+
if (repository || confirmLiveDelivery)
|
|
1663
|
+
throw new Error(`conductor ${command} does not accept live-delivery options`);
|
|
1664
|
+
if (command === "bridge") {
|
|
1665
|
+
if (!sessionId || runId || hookEvent || providerVersion)
|
|
1666
|
+
throw new Error("conductor bridge requires --session-id and does not accept --run-id");
|
|
1667
|
+
return { command, sessionId, stateRoot };
|
|
1668
|
+
}
|
|
1669
|
+
if (command === "observe") {
|
|
1670
|
+
if (!sessionId || !hookEvent || !providerVersion || runId) {
|
|
1671
|
+
throw new Error("conductor observe requires --session-id, --hook-event, and --provider-version");
|
|
1672
|
+
}
|
|
1673
|
+
return { command, sessionId, hookEvent, providerVersion, stateRoot };
|
|
1674
|
+
}
|
|
1675
|
+
if (command === "consume-event") {
|
|
1676
|
+
if (!sessionId || !providerVersion || runId || hookEvent) {
|
|
1677
|
+
throw new Error("conductor consume-event requires --session-id and --provider-version");
|
|
1678
|
+
}
|
|
1679
|
+
return { command, sessionId, providerVersion, stateRoot };
|
|
1680
|
+
}
|
|
1681
|
+
if (command === "wait-event") {
|
|
1682
|
+
if (!sessionId || !providerVersion || (hookEvent !== "SessionStart" && hookEvent !== "Stop") || runId) {
|
|
1683
|
+
throw new Error("conductor wait-event requires --session-id, SessionStart|Stop, and --provider-version");
|
|
1684
|
+
}
|
|
1685
|
+
return { command, sessionId, hookEvent, providerVersion, stateRoot };
|
|
1686
|
+
}
|
|
1687
|
+
if (!runId || sessionId || hookEvent || providerVersion)
|
|
1688
|
+
throw new Error("conductor next requires --run-id and does not accept --session-id");
|
|
1689
|
+
return { command, runId, stateRoot };
|
|
1690
|
+
}
|
|
1691
|
+
function parseConductorWaitHook(value, expectedSessionId, expectedEvent, workspaceRoot) {
|
|
1692
|
+
if (!isRecord(value))
|
|
1693
|
+
throw new Error("conductor waiter hook input must be an object");
|
|
1694
|
+
if (value.session_id !== expectedSessionId || value.hook_event_name !== expectedEvent) {
|
|
1695
|
+
throw new Error("conductor waiter hook binding mismatch");
|
|
1696
|
+
}
|
|
1697
|
+
if (typeof value.cwd !== "string" || resolve(value.cwd) !== workspaceRoot) {
|
|
1698
|
+
throw new Error("conductor waiter hook workspace mismatch");
|
|
1699
|
+
}
|
|
1700
|
+
return Object.freeze({ session_id: expectedSessionId, hook_event_name: expectedEvent });
|
|
1701
|
+
}
|
|
1702
|
+
function parseConductorActivityHook(value, expectedSessionId, expectedEvent, workspaceRoot) {
|
|
1703
|
+
if (!isRecord(value))
|
|
1704
|
+
throw new Error("conductor activity hook input must be an object");
|
|
1705
|
+
if (value.session_id !== expectedSessionId || value.hook_event_name !== expectedEvent) {
|
|
1706
|
+
throw new Error("conductor activity hook binding mismatch");
|
|
1707
|
+
}
|
|
1708
|
+
if (typeof value.cwd !== "string" || resolve(value.cwd) !== workspaceRoot) {
|
|
1709
|
+
throw new Error("conductor activity hook workspace mismatch");
|
|
1710
|
+
}
|
|
1711
|
+
return Object.freeze({ session_id: expectedSessionId, hook_event_name: expectedEvent });
|
|
1712
|
+
}
|
|
1713
|
+
function isMatchingConductorNotificationRead(value, expectedSessionId, workspaceRoot, stateRoot, notificationId) {
|
|
1714
|
+
if (!isRecord(value) || value.session_id !== expectedSessionId || value.hook_event_name !== "PostToolUse") {
|
|
1715
|
+
throw new Error("conductor read hook binding mismatch");
|
|
1716
|
+
}
|
|
1717
|
+
if (typeof value.cwd !== "string" || resolve(value.cwd) !== workspaceRoot || value.tool_name !== "Read") {
|
|
1718
|
+
throw new Error("conductor read hook context mismatch");
|
|
1719
|
+
}
|
|
1720
|
+
if (!isRecord(value.tool_input) || typeof value.tool_input.file_path !== "string")
|
|
1721
|
+
return false;
|
|
1722
|
+
const candidate = resolve(value.tool_input.file_path);
|
|
1723
|
+
const expected = join(realpathSync.native(stateRoot), "conductor-notifications", `${notificationId}.json`);
|
|
1724
|
+
if (candidate !== expected)
|
|
1725
|
+
return false;
|
|
1726
|
+
if (!existsSync(candidate))
|
|
1727
|
+
return false;
|
|
1728
|
+
const entry = lstatSync(candidate);
|
|
1729
|
+
if (entry.isSymbolicLink() || !entry.isFile())
|
|
1730
|
+
return false;
|
|
1731
|
+
return realpathSync.native(candidate) === expected;
|
|
1732
|
+
}
|
|
1733
|
+
function sha256Canonical(value) {
|
|
1734
|
+
return createHash("sha256").update(canonicalJson(value), "utf8").digest("hex");
|
|
1735
|
+
}
|
|
1736
|
+
function publicGithubSyncRequest(request) {
|
|
1737
|
+
return {
|
|
1738
|
+
command: request.command,
|
|
1739
|
+
args: [...request.args],
|
|
1740
|
+
cwd_sha256: createHash("sha256").update(request.cwd, "utf8").digest("hex"),
|
|
1741
|
+
input_sha256: request.input === undefined
|
|
1742
|
+
? null
|
|
1743
|
+
: createHash("sha256").update(request.input, "utf8").digest("hex"),
|
|
1744
|
+
timeout_ms: request.timeoutMs,
|
|
1745
|
+
max_buffer_bytes: request.maxBufferBytes,
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
function canonicalJson(value) {
|
|
1749
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
1750
|
+
return JSON.stringify(value);
|
|
1751
|
+
if (typeof value === "number") {
|
|
1752
|
+
if (!Number.isFinite(value))
|
|
1753
|
+
throw new Error("hook input contains a non-finite number");
|
|
1754
|
+
return JSON.stringify(value);
|
|
1755
|
+
}
|
|
1756
|
+
if (Array.isArray(value))
|
|
1757
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
1758
|
+
if (isRecord(value)) {
|
|
1759
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
|
|
1760
|
+
}
|
|
1761
|
+
throw new Error("hook input must contain only JSON values");
|
|
1762
|
+
}
|
|
1763
|
+
function parseGoalArgs(args) {
|
|
1764
|
+
const command = args[0];
|
|
1765
|
+
if (command !== "create" && command !== "status")
|
|
1766
|
+
throw new Error("goal requires create or status");
|
|
1767
|
+
let file;
|
|
1768
|
+
let goalId;
|
|
1769
|
+
let stateRoot;
|
|
1770
|
+
const seen = new Set();
|
|
1771
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1772
|
+
const option = args[index];
|
|
1773
|
+
if (option !== "--file" && option !== "--goal-id" && option !== "--state-root") {
|
|
1774
|
+
throw new Error(`unknown option: ${option ?? ""}`);
|
|
1775
|
+
}
|
|
1776
|
+
if (seen.has(option))
|
|
1777
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1778
|
+
seen.add(option);
|
|
1779
|
+
const value = args[index + 1];
|
|
1780
|
+
if (!value || value.startsWith("--"))
|
|
1781
|
+
throw new Error(`${option} requires a value`);
|
|
1782
|
+
if (option === "--file")
|
|
1783
|
+
file = value;
|
|
1784
|
+
if (option === "--goal-id")
|
|
1785
|
+
goalId = value;
|
|
1786
|
+
if (option === "--state-root")
|
|
1787
|
+
stateRoot = value;
|
|
1788
|
+
index += 1;
|
|
1789
|
+
}
|
|
1790
|
+
if (command === "create") {
|
|
1791
|
+
if (!file || goalId)
|
|
1792
|
+
throw new Error("goal create requires --file and does not accept --goal-id");
|
|
1793
|
+
return { command, file, stateRoot };
|
|
1794
|
+
}
|
|
1795
|
+
if (!goalId || file)
|
|
1796
|
+
throw new Error("goal status requires --goal-id and does not accept --file");
|
|
1797
|
+
return { command, goalId, stateRoot };
|
|
1798
|
+
}
|
|
1799
|
+
function parseStructuredRoleArgs(args) {
|
|
1800
|
+
if (args[0] !== "bridge")
|
|
1801
|
+
throw new Error("structured-role requires the bridge subcommand");
|
|
1802
|
+
let requestId;
|
|
1803
|
+
let sessionId;
|
|
1804
|
+
let stateRoot;
|
|
1805
|
+
let workspaceRoot;
|
|
1806
|
+
const seen = new Set();
|
|
1807
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1808
|
+
const option = args[index];
|
|
1809
|
+
if (option !== "--request-id" && option !== "--session-id" && option !== "--state-root" && option !== "--workspace-root") {
|
|
1810
|
+
throw new Error(`unknown option: ${option ?? ""}`);
|
|
1811
|
+
}
|
|
1812
|
+
if (seen.has(option))
|
|
1813
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1814
|
+
seen.add(option);
|
|
1815
|
+
const value = args[index + 1];
|
|
1816
|
+
if (!value || value.startsWith("--"))
|
|
1817
|
+
throw new Error(`${option} requires a value`);
|
|
1818
|
+
if (option === "--request-id")
|
|
1819
|
+
requestId = value;
|
|
1820
|
+
if (option === "--session-id")
|
|
1821
|
+
sessionId = value;
|
|
1822
|
+
if (option === "--state-root")
|
|
1823
|
+
stateRoot = value;
|
|
1824
|
+
if (option === "--workspace-root")
|
|
1825
|
+
workspaceRoot = value;
|
|
1826
|
+
index += 1;
|
|
1827
|
+
}
|
|
1828
|
+
if (!requestId || !sessionId || !stateRoot || !workspaceRoot) {
|
|
1829
|
+
throw new Error("structured-role bridge requires --request-id, --session-id, --state-root, and --workspace-root");
|
|
1830
|
+
}
|
|
1831
|
+
return { command: "bridge", requestId, sessionId, stateRoot, workspaceRoot };
|
|
1832
|
+
}
|
|
1833
|
+
function parseWorkerArgs(args) {
|
|
1834
|
+
const command = args[0];
|
|
1835
|
+
if (command !== "deliver"
|
|
1836
|
+
&& command !== "status"
|
|
1837
|
+
&& command !== "park"
|
|
1838
|
+
&& command !== "resume"
|
|
1839
|
+
&& command !== "stop"
|
|
1840
|
+
&& command !== "bridge") {
|
|
1841
|
+
throw new Error("worker requires deliver, status, park, resume, stop, or bridge");
|
|
1842
|
+
}
|
|
1843
|
+
let eventId;
|
|
1844
|
+
let dispatchId;
|
|
1845
|
+
let sessionId;
|
|
1846
|
+
let stateRoot;
|
|
1847
|
+
let workspaceRoot;
|
|
1848
|
+
let configRoot;
|
|
1849
|
+
let parkedUntil;
|
|
1850
|
+
let reasonFile;
|
|
1851
|
+
const seen = new Set();
|
|
1852
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1853
|
+
const option = args[index];
|
|
1854
|
+
if (!option || ![
|
|
1855
|
+
"--event-id",
|
|
1856
|
+
"--dispatch-id",
|
|
1857
|
+
"--session-id",
|
|
1858
|
+
"--state-root",
|
|
1859
|
+
"--workspace-root",
|
|
1860
|
+
"--config-root",
|
|
1861
|
+
"--until",
|
|
1862
|
+
"--reason-file",
|
|
1863
|
+
].includes(option))
|
|
1864
|
+
throw new Error(`unknown option: ${option ?? ""}`);
|
|
1865
|
+
if (seen.has(option))
|
|
1866
|
+
throw new Error(`duplicate option: ${option}`);
|
|
1867
|
+
seen.add(option);
|
|
1868
|
+
const value = args[index + 1];
|
|
1869
|
+
if (!value || value.startsWith("--"))
|
|
1870
|
+
throw new Error(`${option} requires a value`);
|
|
1871
|
+
if (option === "--event-id")
|
|
1872
|
+
eventId = value;
|
|
1873
|
+
if (option === "--dispatch-id")
|
|
1874
|
+
dispatchId = value;
|
|
1875
|
+
if (option === "--session-id")
|
|
1876
|
+
sessionId = value;
|
|
1877
|
+
if (option === "--state-root")
|
|
1878
|
+
stateRoot = value;
|
|
1879
|
+
if (option === "--workspace-root")
|
|
1880
|
+
workspaceRoot = value;
|
|
1881
|
+
if (option === "--config-root")
|
|
1882
|
+
configRoot = value;
|
|
1883
|
+
if (option === "--until")
|
|
1884
|
+
parkedUntil = value;
|
|
1885
|
+
if (option === "--reason-file")
|
|
1886
|
+
reasonFile = value;
|
|
1887
|
+
index += 1;
|
|
1888
|
+
}
|
|
1889
|
+
if (command === "deliver") {
|
|
1890
|
+
if (!eventId || dispatchId || sessionId || parkedUntil || reasonFile)
|
|
1891
|
+
throw new Error("worker deliver requires only --event-id");
|
|
1892
|
+
return { command, eventId, stateRoot, workspaceRoot, configRoot };
|
|
1893
|
+
}
|
|
1894
|
+
if (!dispatchId || eventId)
|
|
1895
|
+
throw new Error(`worker ${command} requires --dispatch-id`);
|
|
1896
|
+
if (command === "bridge") {
|
|
1897
|
+
if (!sessionId || !stateRoot || !workspaceRoot || !configRoot || parkedUntil || reasonFile) {
|
|
1898
|
+
throw new Error("worker bridge requires --dispatch-id, --session-id, --state-root, --workspace-root, and --config-root");
|
|
1899
|
+
}
|
|
1900
|
+
return { command, dispatchId, sessionId, stateRoot, workspaceRoot, configRoot };
|
|
1901
|
+
}
|
|
1902
|
+
if (sessionId)
|
|
1903
|
+
throw new Error(`worker ${command} does not accept --session-id`);
|
|
1904
|
+
if (command === "park") {
|
|
1905
|
+
if (!parkedUntil || !reasonFile)
|
|
1906
|
+
throw new Error("worker park requires --until and --reason-file");
|
|
1907
|
+
return { command, dispatchId, parkedUntil, reasonFile, stateRoot, workspaceRoot, configRoot };
|
|
1908
|
+
}
|
|
1909
|
+
if (command === "stop") {
|
|
1910
|
+
if (!reasonFile || parkedUntil)
|
|
1911
|
+
throw new Error("worker stop requires --reason-file and does not accept --until");
|
|
1912
|
+
return { command, dispatchId, reasonFile, stateRoot, workspaceRoot, configRoot };
|
|
1913
|
+
}
|
|
1914
|
+
if (parkedUntil || reasonFile)
|
|
1915
|
+
throw new Error(`worker ${command} does not accept --until or --reason-file`);
|
|
1916
|
+
return { command, dispatchId, stateRoot, workspaceRoot, configRoot };
|
|
1917
|
+
}
|
|
1918
|
+
function parseTeamArgs(args) {
|
|
1919
|
+
if (args[0] !== "start")
|
|
1920
|
+
throw new Error("team requires the start subcommand");
|
|
1921
|
+
const dispatchIds = [];
|
|
1922
|
+
let stateRoot;
|
|
1923
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1924
|
+
const option = args[index];
|
|
1925
|
+
if (option !== "--dispatch-id" && option !== "--state-root")
|
|
1926
|
+
throw new Error(`unknown option: ${option ?? ""}`);
|
|
1927
|
+
const value = args[index + 1];
|
|
1928
|
+
if (!value || value.startsWith("--"))
|
|
1929
|
+
throw new Error(`${option} requires a value`);
|
|
1930
|
+
if (option === "--dispatch-id")
|
|
1931
|
+
dispatchIds.push(value);
|
|
1932
|
+
else {
|
|
1933
|
+
if (stateRoot)
|
|
1934
|
+
throw new Error("duplicate option: --state-root");
|
|
1935
|
+
stateRoot = value;
|
|
1936
|
+
}
|
|
1937
|
+
index += 1;
|
|
1938
|
+
}
|
|
1939
|
+
if (dispatchIds.length < 1 || dispatchIds.length > 20)
|
|
1940
|
+
throw new Error("team start requires from 1 to 20 --dispatch-id values");
|
|
1941
|
+
return { dispatchIds, stateRoot };
|
|
1942
|
+
}
|
|
1943
|
+
function manageWorker(delivery, request) {
|
|
1944
|
+
switch (request.action) {
|
|
1945
|
+
case "observe":
|
|
1946
|
+
return delivery.observe(request.dispatch_id);
|
|
1947
|
+
case "resume":
|
|
1948
|
+
return delivery.resume(request.dispatch_id);
|
|
1949
|
+
case "park":
|
|
1950
|
+
if (request.reason === null || request.parked_until === null)
|
|
1951
|
+
throw new Error("park requires reason and parked_until");
|
|
1952
|
+
return delivery.park(request.dispatch_id, request.reason, request.parked_until);
|
|
1953
|
+
case "stop":
|
|
1954
|
+
if (request.reason === null)
|
|
1955
|
+
throw new Error("stop requires reason");
|
|
1956
|
+
return delivery.stop(request.dispatch_id, request.reason);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
function startExecutorGroup(options) {
|
|
1960
|
+
const runtime = createRuntimeDispatchAuthorities({
|
|
1961
|
+
packageRoot: options.packageRoot,
|
|
1962
|
+
configRoot: options.configRoot,
|
|
1963
|
+
workspaceRoot: options.workspaceRoot,
|
|
1964
|
+
stateRoot: options.stateRoot,
|
|
1965
|
+
actorId: "oms-conductor",
|
|
1966
|
+
});
|
|
1967
|
+
const generated = generateExecutorGroupManifest({
|
|
1968
|
+
dispatchIds: options.dispatchIds,
|
|
1969
|
+
dispatchAuthority: runtime.dispatch,
|
|
1970
|
+
teamPlansRoot: join(options.stateRoot, "team-plans"),
|
|
1971
|
+
packageRoot: options.packageRoot,
|
|
1972
|
+
});
|
|
1973
|
+
const persisted = persistExecutorGroupManifest({ generated });
|
|
1974
|
+
const workers = generated.plan.dispatch_ids.map((dispatchId) => {
|
|
1975
|
+
const eventId = runtime.dispatch.read(dispatchId).payload.delivery_event_id;
|
|
1976
|
+
if (!eventId)
|
|
1977
|
+
throw new Error(`accepted Team dispatch has no delivery event: ${dispatchId}`);
|
|
1978
|
+
return options.delivery.deliver(eventId);
|
|
1979
|
+
});
|
|
1980
|
+
return Object.freeze({
|
|
1981
|
+
status: persisted.status,
|
|
1982
|
+
path: persisted.path,
|
|
1983
|
+
digest: generated.digest,
|
|
1984
|
+
manifest: generated.plan,
|
|
1985
|
+
workers,
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1988
|
+
function readReasonInput(path, packageRoot) {
|
|
1989
|
+
const input = readJsonFileNoFollow(path, 16 * 1024);
|
|
1990
|
+
assertSchema(input, loadJsonSchema(join(packageRoot, "schemas", "run-abort-input.schema.json")), "worker reason input");
|
|
1991
|
+
return input.reason;
|
|
1992
|
+
}
|
|
1993
|
+
function parseTrustArgs(args) {
|
|
1994
|
+
if (args[0] !== "report")
|
|
1995
|
+
throw new Error("trust requires the report subcommand");
|
|
1996
|
+
let utcDate;
|
|
1997
|
+
let stateRoot;
|
|
1998
|
+
const seen = new Set();
|
|
1999
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
2000
|
+
const option = args[index];
|
|
2001
|
+
if (option !== "--date" && option !== "--state-root")
|
|
2002
|
+
throw new Error(`unknown option: ${option ?? ""}`);
|
|
2003
|
+
if (seen.has(option))
|
|
2004
|
+
throw new Error(`duplicate option: ${option}`);
|
|
2005
|
+
seen.add(option);
|
|
2006
|
+
const value = args[index + 1];
|
|
2007
|
+
if (!value || value.startsWith("--"))
|
|
2008
|
+
throw new Error(`${option} requires a value`);
|
|
2009
|
+
if (option === "--date")
|
|
2010
|
+
utcDate = value;
|
|
2011
|
+
if (option === "--state-root")
|
|
2012
|
+
stateRoot = value;
|
|
2013
|
+
index += 1;
|
|
2014
|
+
}
|
|
2015
|
+
if (!utcDate)
|
|
2016
|
+
throw new Error("trust report requires --date");
|
|
2017
|
+
return { utcDate, stateRoot };
|
|
2018
|
+
}
|
|
2019
|
+
function goalSummary(goal) {
|
|
2020
|
+
return Object.freeze({
|
|
2021
|
+
goal_id: goal.record_id,
|
|
2022
|
+
state: goal.state,
|
|
2023
|
+
leader_id: goal.actor,
|
|
2024
|
+
version: goal.version,
|
|
2025
|
+
created_at: goal.payload.created_at,
|
|
2026
|
+
updated_at: goal.payload.updated_at,
|
|
2027
|
+
park_reason: goal.payload.park_reason,
|
|
2028
|
+
release: "not_stable",
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
2031
|
+
function parseGithubQueueProbeArgs(args) {
|
|
2032
|
+
if (args[0] !== "github-queue")
|
|
2033
|
+
throw new Error("probe requires the github-queue subcommand");
|
|
2034
|
+
let runId;
|
|
2035
|
+
let repository;
|
|
2036
|
+
let readyLabel;
|
|
2037
|
+
let limit;
|
|
2038
|
+
let stateRoot;
|
|
2039
|
+
let confirmLive = false;
|
|
2040
|
+
const seen = new Set();
|
|
2041
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
2042
|
+
const argument = args[index];
|
|
2043
|
+
if (!argument)
|
|
2044
|
+
throw new Error("invalid empty probe option");
|
|
2045
|
+
if (seen.has(argument))
|
|
2046
|
+
throw new Error(`duplicate option: ${argument}`);
|
|
2047
|
+
seen.add(argument);
|
|
2048
|
+
if (argument === "--confirm-live") {
|
|
2049
|
+
confirmLive = true;
|
|
2050
|
+
continue;
|
|
2051
|
+
}
|
|
2052
|
+
if (!["--run-id", "--repository", "--ready-label", "--limit", "--state-root"].includes(argument)) {
|
|
2053
|
+
throw new Error(`unknown option: ${argument}`);
|
|
2054
|
+
}
|
|
2055
|
+
const value = args[index + 1];
|
|
2056
|
+
if (!value || value.startsWith("--"))
|
|
2057
|
+
throw new Error(`${argument} requires a value`);
|
|
2058
|
+
index += 1;
|
|
2059
|
+
if (argument === "--run-id")
|
|
2060
|
+
runId = value;
|
|
2061
|
+
if (argument === "--repository")
|
|
2062
|
+
repository = value;
|
|
2063
|
+
if (argument === "--ready-label")
|
|
2064
|
+
readyLabel = value;
|
|
2065
|
+
if (argument === "--state-root")
|
|
2066
|
+
stateRoot = value;
|
|
2067
|
+
if (argument === "--limit") {
|
|
2068
|
+
if (!/^[1-9][0-9]{0,2}$/.test(value))
|
|
2069
|
+
throw new Error("--limit must be an integer from 1 to 100");
|
|
2070
|
+
limit = Number(value);
|
|
2071
|
+
if (limit > 100)
|
|
2072
|
+
throw new Error("--limit must be an integer from 1 to 100");
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
if (!runId)
|
|
2076
|
+
throw new Error("probe github-queue requires --run-id");
|
|
2077
|
+
if (!repository)
|
|
2078
|
+
throw new Error("probe github-queue requires --repository");
|
|
2079
|
+
return { runId, repository, readyLabel, limit, stateRoot, confirmLive };
|
|
2080
|
+
}
|
|
2081
|
+
function isEntrypoint() {
|
|
2082
|
+
const argv1 = process.argv[1];
|
|
2083
|
+
if (!argv1)
|
|
2084
|
+
return false;
|
|
2085
|
+
if (import.meta.url === pathToFileURL(argv1).href)
|
|
2086
|
+
return true;
|
|
2087
|
+
try {
|
|
2088
|
+
return import.meta.url === pathToFileURL(realpathSync(argv1)).href;
|
|
2089
|
+
}
|
|
2090
|
+
catch {
|
|
2091
|
+
return false;
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
if (isEntrypoint())
|
|
2095
|
+
await main();
|
|
2096
|
+
//# sourceMappingURL=cli.js.map
|