@kya-os/cli 1.8.1 → 1.8.3
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/dist/commands/agent.d.ts +35 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +321 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/build.js +2 -2
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/check.js +3 -3
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/checkpoint-init.d.ts +1 -1
- package/dist/commands/checkpoint-init.d.ts.map +1 -1
- package/dist/commands/checkpoint-init.js +8 -41
- package/dist/commands/checkpoint-init.js.map +1 -1
- package/dist/commands/claim.js +2 -2
- package/dist/commands/claim.js.map +1 -1
- package/dist/commands/dco.d.ts +59 -0
- package/dist/commands/dco.d.ts.map +1 -1
- package/dist/commands/dco.js +280 -5
- package/dist/commands/dco.js.map +1 -1
- package/dist/commands/dev.js +1 -1
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +18 -24
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/env.js +6 -6
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/identity.js +3 -3
- package/dist/commands/identity.js.map +1 -1
- package/dist/commands/init-stages.d.ts +141 -0
- package/dist/commands/init-stages.d.ts.map +1 -0
- package/dist/commands/init-stages.js +684 -0
- package/dist/commands/init-stages.js.map +1 -0
- package/dist/commands/init.d.ts +46 -16
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +229 -279
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/orchestrator.d.ts +18 -0
- package/dist/commands/orchestrator.d.ts.map +1 -0
- package/dist/commands/orchestrator.js +251 -0
- package/dist/commands/orchestrator.js.map +1 -0
- package/dist/commands/register.d.ts +1 -1
- package/dist/commands/register.d.ts.map +1 -1
- package/dist/commands/register.js +35 -7
- package/dist/commands/register.js.map +1 -1
- package/dist/commands/review.d.ts +48 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +220 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/rotate.js +4 -4
- package/dist/commands/rotate.js.map +1 -1
- package/dist/commands/score.d.ts +15 -0
- package/dist/commands/score.d.ts.map +1 -0
- package/dist/commands/score.js +471 -0
- package/dist/commands/score.js.map +1 -0
- package/dist/commands/start.js +3 -3
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.js +1 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/verify.js +6 -6
- package/dist/commands/verify.js.map +1 -1
- package/dist/dco.d.ts +4 -0
- package/dist/dco.d.ts.map +1 -0
- package/dist/dco.js +23 -0
- package/dist/dco.js.map +1 -0
- package/dist/index.js +52 -9
- package/dist/orchestrator/daemon.d.ts +137 -0
- package/dist/orchestrator/daemon.d.ts.map +1 -0
- package/dist/orchestrator/daemon.js +242 -0
- package/dist/orchestrator/daemon.js.map +1 -0
- package/dist/orchestrator/db/dispatches.d.ts +108 -0
- package/dist/orchestrator/db/dispatches.d.ts.map +1 -0
- package/dist/orchestrator/db/dispatches.js +122 -0
- package/dist/orchestrator/db/dispatches.js.map +1 -0
- package/dist/orchestrator/db/index.d.ts +50 -0
- package/dist/orchestrator/db/index.d.ts.map +1 -0
- package/dist/orchestrator/db/index.js +116 -0
- package/dist/orchestrator/db/index.js.map +1 -0
- package/dist/orchestrator/db/journal.d.ts +93 -0
- package/dist/orchestrator/db/journal.d.ts.map +1 -0
- package/dist/orchestrator/db/journal.js +91 -0
- package/dist/orchestrator/db/journal.js.map +1 -0
- package/dist/orchestrator/db/liveness.d.ts +97 -0
- package/dist/orchestrator/db/liveness.d.ts.map +1 -0
- package/dist/orchestrator/db/liveness.js +105 -0
- package/dist/orchestrator/db/liveness.js.map +1 -0
- package/dist/orchestrator/db/mission.d.ts +78 -0
- package/dist/orchestrator/db/mission.d.ts.map +1 -0
- package/dist/orchestrator/db/mission.js +99 -0
- package/dist/orchestrator/db/mission.js.map +1 -0
- package/dist/orchestrator/db/schema.d.ts +14 -0
- package/dist/orchestrator/db/schema.d.ts.map +1 -0
- package/dist/orchestrator/db/schema.js +169 -0
- package/dist/orchestrator/db/schema.js.map +1 -0
- package/dist/orchestrator/db/tickets.d.ts +147 -0
- package/dist/orchestrator/db/tickets.d.ts.map +1 -0
- package/dist/orchestrator/db/tickets.js +238 -0
- package/dist/orchestrator/db/tickets.js.map +1 -0
- package/dist/orchestrator/db/verdicts.d.ts +187 -0
- package/dist/orchestrator/db/verdicts.d.ts.map +1 -0
- package/dist/orchestrator/db/verdicts.js +197 -0
- package/dist/orchestrator/db/verdicts.js.map +1 -0
- package/dist/orchestrator/events.d.ts +69 -0
- package/dist/orchestrator/events.d.ts.map +1 -0
- package/dist/orchestrator/events.js +20 -0
- package/dist/orchestrator/events.js.map +1 -0
- package/dist/orchestrator/github-credential-anchor-sender.d.ts +159 -0
- package/dist/orchestrator/github-credential-anchor-sender.d.ts.map +1 -0
- package/dist/orchestrator/github-credential-anchor-sender.js +313 -0
- package/dist/orchestrator/github-credential-anchor-sender.js.map +1 -0
- package/dist/orchestrator/hub-dispatch.d.ts +120 -0
- package/dist/orchestrator/hub-dispatch.d.ts.map +1 -0
- package/dist/orchestrator/hub-dispatch.js +257 -0
- package/dist/orchestrator/hub-dispatch.js.map +1 -0
- package/dist/orchestrator/hub-observer.d.ts +201 -0
- package/dist/orchestrator/hub-observer.d.ts.map +1 -0
- package/dist/orchestrator/hub-observer.js +474 -0
- package/dist/orchestrator/hub-observer.js.map +1 -0
- package/dist/orchestrator/journal-writer.d.ts +80 -0
- package/dist/orchestrator/journal-writer.d.ts.map +1 -0
- package/dist/orchestrator/journal-writer.js +113 -0
- package/dist/orchestrator/journal-writer.js.map +1 -0
- package/dist/orchestrator/mcp/server.d.ts +136 -0
- package/dist/orchestrator/mcp/server.d.ts.map +1 -0
- package/dist/orchestrator/mcp/server.js +582 -0
- package/dist/orchestrator/mcp/server.js.map +1 -0
- package/dist/orchestrator/mcp/stdio.d.ts +22 -0
- package/dist/orchestrator/mcp/stdio.d.ts.map +1 -0
- package/dist/orchestrator/mcp/stdio.js +51 -0
- package/dist/orchestrator/mcp/stdio.js.map +1 -0
- package/dist/orchestrator/review.d.ts +131 -0
- package/dist/orchestrator/review.d.ts.map +1 -0
- package/dist/orchestrator/review.js +322 -0
- package/dist/orchestrator/review.js.map +1 -0
- package/dist/tui/app.d.ts +33 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +71 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/components.d.ts +110 -0
- package/dist/tui/components.d.ts.map +1 -0
- package/dist/tui/components.js +118 -0
- package/dist/tui/components.js.map +1 -0
- package/dist/tui/index.d.ts +30 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +95 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/keymap.d.ts +33 -0
- package/dist/tui/keymap.d.ts.map +1 -0
- package/dist/tui/keymap.js +49 -0
- package/dist/tui/keymap.js.map +1 -0
- package/dist/tui/nav.d.ts +29 -0
- package/dist/tui/nav.d.ts.map +1 -0
- package/dist/tui/nav.js +39 -0
- package/dist/tui/nav.js.map +1 -0
- package/dist/tui/sanitize.d.ts +25 -0
- package/dist/tui/sanitize.d.ts.map +1 -0
- package/dist/tui/sanitize.js +36 -0
- package/dist/tui/sanitize.js.map +1 -0
- package/dist/tui/theme.d.ts +32 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +63 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/views/agents.d.ts +67 -0
- package/dist/tui/views/agents.d.ts.map +1 -0
- package/dist/tui/views/agents.js +231 -0
- package/dist/tui/views/agents.js.map +1 -0
- package/dist/tui/views/index.d.ts +93 -0
- package/dist/tui/views/index.d.ts.map +1 -0
- package/dist/tui/views/index.js +150 -0
- package/dist/tui/views/index.js.map +1 -0
- package/dist/tui/views/registry.d.ts +22 -0
- package/dist/tui/views/registry.d.ts.map +1 -0
- package/dist/tui/views/registry.js +110 -0
- package/dist/tui/views/registry.js.map +1 -0
- package/dist/utils/agent/backoff.d.ts +31 -0
- package/dist/utils/agent/backoff.d.ts.map +1 -0
- package/dist/utils/agent/backoff.js +51 -0
- package/dist/utils/agent/backoff.js.map +1 -0
- package/dist/utils/agent/markers.d.ts +87 -0
- package/dist/utils/agent/markers.d.ts.map +1 -0
- package/dist/utils/agent/markers.js +147 -0
- package/dist/utils/agent/markers.js.map +1 -0
- package/dist/utils/agent/review.d.ts +67 -0
- package/dist/utils/agent/review.d.ts.map +1 -0
- package/dist/utils/agent/review.js +305 -0
- package/dist/utils/agent/review.js.map +1 -0
- package/dist/utils/agent/spawn-agent.d.ts +117 -0
- package/dist/utils/agent/spawn-agent.d.ts.map +1 -0
- package/dist/utils/agent/spawn-agent.js +177 -0
- package/dist/utils/agent/spawn-agent.js.map +1 -0
- package/dist/utils/agent/task.d.ts +101 -0
- package/dist/utils/agent/task.d.ts.map +1 -0
- package/dist/utils/agent/task.js +357 -0
- package/dist/utils/agent/task.js.map +1 -0
- package/dist/utils/agent/verify.d.ts +47 -0
- package/dist/utils/agent/verify.d.ts.map +1 -0
- package/dist/utils/agent/verify.js +144 -0
- package/dist/utils/agent/verify.js.map +1 -0
- package/dist/utils/agent-detector.d.ts +48 -0
- package/dist/utils/agent-detector.d.ts.map +1 -0
- package/dist/utils/agent-detector.js +74 -0
- package/dist/utils/agent-detector.js.map +1 -0
- package/dist/utils/agent-profile-stage.d.ts +70 -0
- package/dist/utils/agent-profile-stage.d.ts.map +1 -0
- package/dist/utils/agent-profile-stage.js +153 -0
- package/dist/utils/agent-profile-stage.js.map +1 -0
- package/dist/utils/agent-profiles.d.ts +27 -0
- package/dist/utils/agent-profiles.d.ts.map +1 -0
- package/dist/utils/agent-profiles.js +61 -0
- package/dist/utils/agent-profiles.js.map +1 -0
- package/dist/utils/checkpoint-api.d.ts +1 -1
- package/dist/utils/checkpoint-api.js +1 -1
- package/dist/utils/checkpoint-env.d.ts +1 -1
- package/dist/utils/checkpoint-env.js +2 -2
- package/dist/utils/checkpoint-env.js.map +1 -1
- package/dist/utils/checkpoint-scaffold.d.ts +1 -1
- package/dist/utils/checkpoint-scaffold.js +1 -1
- package/dist/utils/cli-kit/browser.d.ts +24 -0
- package/dist/utils/cli-kit/browser.d.ts.map +1 -0
- package/dist/utils/cli-kit/browser.js +58 -0
- package/dist/utils/cli-kit/browser.js.map +1 -0
- package/dist/utils/cli-kit/claim-polling.d.ts +18 -0
- package/dist/utils/cli-kit/claim-polling.d.ts.map +1 -0
- package/dist/utils/{claim-polling.js → cli-kit/claim-polling.js} +19 -26
- package/dist/utils/cli-kit/claim-polling.js.map +1 -0
- package/dist/utils/cli-kit/diagnostics.d.ts +9 -0
- package/dist/utils/cli-kit/diagnostics.d.ts.map +1 -0
- package/dist/utils/cli-kit/diagnostics.js +11 -0
- package/dist/utils/cli-kit/diagnostics.js.map +1 -0
- package/dist/utils/cli-kit/fs-safe.d.ts +14 -0
- package/dist/utils/cli-kit/fs-safe.d.ts.map +1 -0
- package/dist/utils/cli-kit/fs-safe.js +20 -0
- package/dist/utils/cli-kit/fs-safe.js.map +1 -0
- package/dist/utils/cli-kit/index.d.ts +15 -0
- package/dist/utils/cli-kit/index.d.ts.map +1 -0
- package/dist/utils/cli-kit/index.js +10 -0
- package/dist/utils/cli-kit/index.js.map +1 -0
- package/dist/utils/cli-kit/json-file.d.ts +21 -0
- package/dist/utils/cli-kit/json-file.d.ts.map +1 -0
- package/dist/utils/cli-kit/json-file.js +51 -0
- package/dist/utils/cli-kit/json-file.js.map +1 -0
- package/dist/utils/cli-kit/marker-block.d.ts +26 -0
- package/dist/utils/cli-kit/marker-block.d.ts.map +1 -0
- package/dist/utils/cli-kit/marker-block.js +61 -0
- package/dist/utils/cli-kit/marker-block.js.map +1 -0
- package/dist/utils/cli-kit/paths.d.ts +15 -0
- package/dist/utils/cli-kit/paths.d.ts.map +1 -0
- package/dist/utils/cli-kit/paths.js +21 -0
- package/dist/utils/cli-kit/paths.js.map +1 -0
- package/dist/utils/cli-kit/secure-storage.d.ts +14 -0
- package/dist/utils/cli-kit/secure-storage.d.ts.map +1 -0
- package/dist/utils/cli-kit/secure-storage.js +20 -0
- package/dist/utils/cli-kit/secure-storage.js.map +1 -0
- package/dist/utils/cli-kit/step-report.d.ts +16 -0
- package/dist/utils/cli-kit/step-report.d.ts.map +1 -0
- package/dist/utils/cli-kit/step-report.js +36 -0
- package/dist/utils/cli-kit/step-report.js.map +1 -0
- package/dist/utils/dco/agent-metadata.d.ts +1 -1
- package/dist/utils/dco/agent-metadata.js +1 -1
- package/dist/utils/dco/agent-metadata.js.map +1 -1
- package/dist/utils/dco/claude-config.d.ts +18 -14
- package/dist/utils/dco/claude-config.d.ts.map +1 -1
- package/dist/utils/dco/claude-config.js +31 -97
- package/dist/utils/dco/claude-config.js.map +1 -1
- package/dist/utils/dco/dispatch.d.ts +134 -0
- package/dist/utils/dco/dispatch.d.ts.map +1 -0
- package/dist/utils/dco/dispatch.js +399 -0
- package/dist/utils/dco/dispatch.js.map +1 -0
- package/dist/utils/dco/fix.d.ts +122 -0
- package/dist/utils/dco/fix.d.ts.map +1 -0
- package/dist/utils/dco/fix.js +560 -0
- package/dist/utils/dco/fix.js.map +1 -0
- package/dist/utils/dco/git-config.d.ts.map +1 -1
- package/dist/utils/dco/git-config.js +5 -3
- package/dist/utils/dco/git-config.js.map +1 -1
- package/dist/utils/dco/github.d.ts +101 -0
- package/dist/utils/dco/github.d.ts.map +1 -1
- package/dist/utils/dco/github.js +133 -0
- package/dist/utils/dco/github.js.map +1 -1
- package/dist/utils/dco/paths.d.ts +1 -8
- package/dist/utils/dco/paths.d.ts.map +1 -1
- package/dist/utils/dco/paths.js +3 -12
- package/dist/utils/dco/paths.js.map +1 -1
- package/dist/utils/dco/render.d.ts +1 -1
- package/dist/utils/dco/render.d.ts.map +1 -1
- package/dist/utils/dco/render.js +4 -21
- package/dist/utils/dco/render.js.map +1 -1
- package/dist/utils/dco/resolve-agent.d.ts +54 -0
- package/dist/utils/dco/resolve-agent.d.ts.map +1 -0
- package/dist/utils/dco/resolve-agent.js +164 -0
- package/dist/utils/dco/resolve-agent.js.map +1 -0
- package/dist/utils/dco/setup.d.ts +10 -7
- package/dist/utils/dco/setup.d.ts.map +1 -1
- package/dist/utils/dco/setup.js +264 -200
- package/dist/utils/dco/setup.js.map +1 -1
- package/dist/utils/dco/signing-key-writer.d.ts +40 -0
- package/dist/utils/dco/signing-key-writer.d.ts.map +1 -0
- package/dist/utils/dco/signing-key-writer.js +76 -0
- package/dist/utils/dco/signing-key-writer.js.map +1 -0
- package/dist/utils/dco/ssh-key.d.ts +50 -0
- package/dist/utils/dco/ssh-key.d.ts.map +1 -1
- package/dist/utils/dco/ssh-key.js +129 -8
- package/dist/utils/dco/ssh-key.js.map +1 -1
- package/dist/utils/dco/status.d.ts +1 -1
- package/dist/utils/dco/status.d.ts.map +1 -1
- package/dist/utils/dco/status.js +3 -3
- package/dist/utils/dco/status.js.map +1 -1
- package/dist/utils/dco/verify.d.ts +170 -0
- package/dist/utils/dco/verify.d.ts.map +1 -0
- package/dist/utils/dco/verify.js +550 -0
- package/dist/utils/dco/verify.js.map +1 -0
- package/dist/utils/dependency-installer.d.ts +37 -0
- package/dist/utils/dependency-installer.d.ts.map +1 -0
- package/dist/utils/dependency-installer.js +32 -0
- package/dist/utils/dependency-installer.js.map +1 -0
- package/dist/utils/github-credential-stage.d.ts +87 -0
- package/dist/utils/github-credential-stage.d.ts.map +1 -0
- package/dist/utils/github-credential-stage.js +168 -0
- package/dist/utils/github-credential-stage.js.map +1 -0
- package/dist/utils/github-credential.d.ts +160 -0
- package/dist/utils/github-credential.d.ts.map +1 -0
- package/dist/utils/github-credential.js +308 -0
- package/dist/utils/github-credential.js.map +1 -0
- package/dist/utils/gitignore.d.ts +17 -0
- package/dist/utils/gitignore.d.ts.map +1 -0
- package/dist/utils/gitignore.js +40 -0
- package/dist/utils/gitignore.js.map +1 -0
- package/dist/utils/hub-config.d.ts +21 -0
- package/dist/utils/hub-config.d.ts.map +1 -0
- package/dist/utils/hub-config.js +28 -0
- package/dist/utils/hub-config.js.map +1 -0
- package/dist/utils/identity-heal.d.ts +16 -0
- package/dist/utils/identity-heal.d.ts.map +1 -0
- package/dist/utils/identity-heal.js +37 -0
- package/dist/utils/identity-heal.js.map +1 -0
- package/dist/utils/init/pipeline.d.ts +74 -0
- package/dist/utils/init/pipeline.d.ts.map +1 -0
- package/dist/utils/init/pipeline.js +96 -0
- package/dist/utils/init/pipeline.js.map +1 -0
- package/dist/utils/poll.d.ts +15 -1
- package/dist/utils/poll.d.ts.map +1 -1
- package/dist/utils/poll.js +6 -3
- package/dist/utils/poll.js.map +1 -1
- package/dist/utils/register-agent-identity.d.ts +67 -0
- package/dist/utils/register-agent-identity.d.ts.map +1 -0
- package/dist/utils/register-agent-identity.js +34 -0
- package/dist/utils/register-agent-identity.js.map +1 -0
- package/dist/utils/review/did-web-key.d.ts +34 -0
- package/dist/utils/review/did-web-key.d.ts.map +1 -0
- package/dist/utils/review/did-web-key.js +104 -0
- package/dist/utils/review/did-web-key.js.map +1 -0
- package/dist/utils/review/load-registry.d.ts +33 -0
- package/dist/utils/review/load-registry.d.ts.map +1 -0
- package/dist/utils/review/load-registry.js +62 -0
- package/dist/utils/review/load-registry.js.map +1 -0
- package/dist/utils/review/verdict-verify.d.ts +43 -0
- package/dist/utils/review/verdict-verify.d.ts.map +1 -0
- package/dist/utils/review/verdict-verify.js +53 -0
- package/dist/utils/review/verdict-verify.js.map +1 -0
- package/dist/utils/review-registry-writer.d.ts +52 -0
- package/dist/utils/review-registry-writer.d.ts.map +1 -0
- package/dist/utils/review-registry-writer.js +79 -0
- package/dist/utils/review-registry-writer.js.map +1 -0
- package/dist/utils/should-install-dependency.d.ts +17 -0
- package/dist/utils/should-install-dependency.d.ts.map +1 -0
- package/dist/utils/should-install-dependency.js +25 -0
- package/dist/utils/should-install-dependency.js.map +1 -0
- package/dist/utils/tool-readiness.d.ts +57 -0
- package/dist/utils/tool-readiness.d.ts.map +1 -0
- package/dist/utils/tool-readiness.js +114 -0
- package/dist/utils/tool-readiness.js.map +1 -0
- package/dist/utils/which.d.ts +2 -0
- package/dist/utils/which.d.ts.map +1 -0
- package/dist/utils/which.js +22 -0
- package/dist/utils/which.js.map +1 -0
- package/dist/version.d.ts +15 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +29 -0
- package/dist/version.js.map +1 -0
- package/package.json +19 -6
- package/dist/utils/claim-polling.d.ts +0 -21
- package/dist/utils/claim-polling.d.ts.map +0 -1
- package/dist/utils/claim-polling.js.map +0 -1
- package/dist/utils/dco/fs-safe.d.ts +0 -14
- package/dist/utils/dco/fs-safe.d.ts.map +0 -1
- package/dist/utils/dco/fs-safe.js +0 -20
- package/dist/utils/dco/fs-safe.js.map +0 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch store (ORC-W1 PR-2).
|
|
3
|
+
*
|
|
4
|
+
* One row per task handed to a worker. Its job in PR-2 is correlation: it is
|
|
5
|
+
* how an incoming hub event, which knows a taskId, becomes a journal entry
|
|
6
|
+
* attached to the right ticket.
|
|
7
|
+
*
|
|
8
|
+
* @module orchestrator/db/dispatches
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { type Ledger } from "./index.js";
|
|
12
|
+
export declare const DispatchStatusSchema: z.ZodEnum<["dispatched", "in-progress", "completed", "failed"]>;
|
|
13
|
+
export type DispatchStatus = z.infer<typeof DispatchStatusSchema>;
|
|
14
|
+
export declare const DispatchRowSchema: z.ZodObject<{
|
|
15
|
+
task_id: z.ZodString;
|
|
16
|
+
ticket_id: z.ZodString;
|
|
17
|
+
worker_did: z.ZodString;
|
|
18
|
+
repo: z.ZodNullable<z.ZodString>;
|
|
19
|
+
branch: z.ZodNullable<z.ZodString>;
|
|
20
|
+
pr_number: z.ZodNullable<z.ZodNumber>;
|
|
21
|
+
status: z.ZodEnum<["dispatched", "in-progress", "completed", "failed"]>;
|
|
22
|
+
dispatched_at: z.ZodString;
|
|
23
|
+
completed_at: z.ZodNullable<z.ZodString>;
|
|
24
|
+
}, "strip", z.ZodTypeAny, {
|
|
25
|
+
status: "failed" | "completed" | "in-progress" | "dispatched";
|
|
26
|
+
repo: string | null;
|
|
27
|
+
ticket_id: string;
|
|
28
|
+
task_id: string;
|
|
29
|
+
worker_did: string;
|
|
30
|
+
branch: string | null;
|
|
31
|
+
pr_number: number | null;
|
|
32
|
+
dispatched_at: string;
|
|
33
|
+
completed_at: string | null;
|
|
34
|
+
}, {
|
|
35
|
+
status: "failed" | "completed" | "in-progress" | "dispatched";
|
|
36
|
+
repo: string | null;
|
|
37
|
+
ticket_id: string;
|
|
38
|
+
task_id: string;
|
|
39
|
+
worker_did: string;
|
|
40
|
+
branch: string | null;
|
|
41
|
+
pr_number: number | null;
|
|
42
|
+
dispatched_at: string;
|
|
43
|
+
completed_at: string | null;
|
|
44
|
+
}>;
|
|
45
|
+
export type DispatchRow = z.infer<typeof DispatchRowSchema>;
|
|
46
|
+
export interface RecordDispatchInput {
|
|
47
|
+
taskId: string;
|
|
48
|
+
ticketId: string;
|
|
49
|
+
workerDid: string;
|
|
50
|
+
repo?: string;
|
|
51
|
+
branch?: string;
|
|
52
|
+
prNumber?: number;
|
|
53
|
+
}
|
|
54
|
+
export declare function recordDispatch(db: Ledger, input: RecordDispatchInput): DispatchRow;
|
|
55
|
+
export declare function getDispatch(db: Ledger, taskId: string): DispatchRow | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* The ticket a task belongs to, or null when this task was never dispatched
|
|
58
|
+
* through the ledger.
|
|
59
|
+
*
|
|
60
|
+
* Null is an ordinary answer, not an error: the hub is multi-tenant and emits
|
|
61
|
+
* events for work this orchestrator did not start. The caller records the
|
|
62
|
+
* observation with no ticket rather than guessing at one.
|
|
63
|
+
*/
|
|
64
|
+
export declare function findTicketIdByTaskId(db: Ledger, taskId: string): string | null;
|
|
65
|
+
/**
|
|
66
|
+
* Find the ticket behind a pull request.
|
|
67
|
+
*
|
|
68
|
+
* The only correlation available for review events. `pr_quorum_reached` carries
|
|
69
|
+
* `repo`/`prNumber`/`headSha` and no task id at all, and `review_verdict`'s task
|
|
70
|
+
* id is optional AND belongs to the reviewer's task rather than the dispatch
|
|
71
|
+
* that produced the branch. Without this, every quorum event lands in the
|
|
72
|
+
* journal with a null ticket, which is why the Review lens could show that
|
|
73
|
+
* something was approved but not what.
|
|
74
|
+
*
|
|
75
|
+
* `idx_dispatches_pr` already indexed (repo, pr_number) with no reader; this is
|
|
76
|
+
* the reader it was waiting for.
|
|
77
|
+
*
|
|
78
|
+
* Newest dispatch wins. A PR reopened after a failed first attempt has two
|
|
79
|
+
* dispatch rows, and the live one is the recent one.
|
|
80
|
+
*
|
|
81
|
+
* Ties break on `rowid` DESC, meaning last-inserted, because `dispatched_at`
|
|
82
|
+
* has millisecond resolution and a retry loop can record two dispatches inside
|
|
83
|
+
* one millisecond. Tiebreaking on `task_id` instead would order them
|
|
84
|
+
* alphabetically, which silently returns whichever id happens to sort lower --
|
|
85
|
+
* the older attempt as often as not.
|
|
86
|
+
*
|
|
87
|
+
* Null is an ordinary answer for the same reason as {@link findTicketIdByTaskId}:
|
|
88
|
+
* the hub is multi-tenant and reviews PRs this orchestrator never dispatched.
|
|
89
|
+
*/
|
|
90
|
+
export declare function findTicketIdByPr(db: Ledger, repo: string, prNumber: number): string | null;
|
|
91
|
+
export declare function setDispatchStatus(db: Ledger, taskId: string, status: DispatchStatus): DispatchRow;
|
|
92
|
+
/**
|
|
93
|
+
* Read dispatches, newest first, optionally capped.
|
|
94
|
+
*
|
|
95
|
+
* The cap is applied in SQL rather than by the caller slicing the result.
|
|
96
|
+
* Dispatches are never deleted, so this table only grows: a cap enforced after
|
|
97
|
+
* the read bounds the RESPONSE while still asking SQLite to materialise every
|
|
98
|
+
* dispatch ever recorded, which is the opposite of what a row cap is for. The
|
|
99
|
+
* ticket and journal readers already capped in SQL for this reason; this one was
|
|
100
|
+
* the outlier.
|
|
101
|
+
*
|
|
102
|
+
* `task_id` breaks ties because `dispatched_at` is an ISO timestamp and not
|
|
103
|
+
* unique -- two dispatches recorded in the same millisecond would otherwise
|
|
104
|
+
* order arbitrarily, and an arbitrary order at the cap boundary decides which
|
|
105
|
+
* row is visible at all.
|
|
106
|
+
*/
|
|
107
|
+
export declare function listDispatches(db: Ledger, limit?: number): DispatchRow[];
|
|
108
|
+
//# sourceMappingURL=dispatches.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatches.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/db/dispatches.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAEjD,eAAO,MAAM,oBAAoB,iEAK/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAU5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,GAAG,WAAW,CAelF;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAI/E;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI9E;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW1F;AAED,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,GACrB,WAAW,CAUb;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAKxE"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch store (ORC-W1 PR-2).
|
|
3
|
+
*
|
|
4
|
+
* One row per task handed to a worker. Its job in PR-2 is correlation: it is
|
|
5
|
+
* how an incoming hub event, which knows a taskId, becomes a journal entry
|
|
6
|
+
* attached to the right ticket.
|
|
7
|
+
*
|
|
8
|
+
* @module orchestrator/db/dispatches
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { rowCap } from "./index.js";
|
|
12
|
+
export const DispatchStatusSchema = z.enum([
|
|
13
|
+
"dispatched",
|
|
14
|
+
"in-progress",
|
|
15
|
+
"completed",
|
|
16
|
+
"failed",
|
|
17
|
+
]);
|
|
18
|
+
export const DispatchRowSchema = z.object({
|
|
19
|
+
task_id: z.string(),
|
|
20
|
+
ticket_id: z.string(),
|
|
21
|
+
worker_did: z.string(),
|
|
22
|
+
repo: z.string().nullable(),
|
|
23
|
+
branch: z.string().nullable(),
|
|
24
|
+
pr_number: z.number().nullable(),
|
|
25
|
+
status: DispatchStatusSchema,
|
|
26
|
+
dispatched_at: z.string(),
|
|
27
|
+
completed_at: z.string().nullable(),
|
|
28
|
+
});
|
|
29
|
+
export function recordDispatch(db, input) {
|
|
30
|
+
db.prepare(`INSERT INTO dispatches
|
|
31
|
+
(task_id, ticket_id, worker_did, repo, branch, pr_number, status, dispatched_at)
|
|
32
|
+
VALUES (?, ?, ?, ?, ?, ?, 'dispatched', ?)`).run(input.taskId, input.ticketId, input.workerDid, input.repo ?? null, input.branch ?? null, input.prNumber ?? null, new Date().toISOString());
|
|
33
|
+
return getDispatch(db, input.taskId);
|
|
34
|
+
}
|
|
35
|
+
export function getDispatch(db, taskId) {
|
|
36
|
+
const row = db.prepare(`SELECT * FROM dispatches WHERE task_id = ?`).get(taskId);
|
|
37
|
+
if (row === undefined || row === null)
|
|
38
|
+
return undefined;
|
|
39
|
+
return DispatchRowSchema.parse(row);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The ticket a task belongs to, or null when this task was never dispatched
|
|
43
|
+
* through the ledger.
|
|
44
|
+
*
|
|
45
|
+
* Null is an ordinary answer, not an error: the hub is multi-tenant and emits
|
|
46
|
+
* events for work this orchestrator did not start. The caller records the
|
|
47
|
+
* observation with no ticket rather than guessing at one.
|
|
48
|
+
*/
|
|
49
|
+
export function findTicketIdByTaskId(db, taskId) {
|
|
50
|
+
const row = db.prepare(`SELECT ticket_id FROM dispatches WHERE task_id = ?`).get(taskId);
|
|
51
|
+
if (row === undefined || row === null)
|
|
52
|
+
return null;
|
|
53
|
+
return z.object({ ticket_id: z.string() }).parse(row).ticket_id;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Find the ticket behind a pull request.
|
|
57
|
+
*
|
|
58
|
+
* The only correlation available for review events. `pr_quorum_reached` carries
|
|
59
|
+
* `repo`/`prNumber`/`headSha` and no task id at all, and `review_verdict`'s task
|
|
60
|
+
* id is optional AND belongs to the reviewer's task rather than the dispatch
|
|
61
|
+
* that produced the branch. Without this, every quorum event lands in the
|
|
62
|
+
* journal with a null ticket, which is why the Review lens could show that
|
|
63
|
+
* something was approved but not what.
|
|
64
|
+
*
|
|
65
|
+
* `idx_dispatches_pr` already indexed (repo, pr_number) with no reader; this is
|
|
66
|
+
* the reader it was waiting for.
|
|
67
|
+
*
|
|
68
|
+
* Newest dispatch wins. A PR reopened after a failed first attempt has two
|
|
69
|
+
* dispatch rows, and the live one is the recent one.
|
|
70
|
+
*
|
|
71
|
+
* Ties break on `rowid` DESC, meaning last-inserted, because `dispatched_at`
|
|
72
|
+
* has millisecond resolution and a retry loop can record two dispatches inside
|
|
73
|
+
* one millisecond. Tiebreaking on `task_id` instead would order them
|
|
74
|
+
* alphabetically, which silently returns whichever id happens to sort lower --
|
|
75
|
+
* the older attempt as often as not.
|
|
76
|
+
*
|
|
77
|
+
* Null is an ordinary answer for the same reason as {@link findTicketIdByTaskId}:
|
|
78
|
+
* the hub is multi-tenant and reviews PRs this orchestrator never dispatched.
|
|
79
|
+
*/
|
|
80
|
+
export function findTicketIdByPr(db, repo, prNumber) {
|
|
81
|
+
const row = db
|
|
82
|
+
.prepare(`SELECT ticket_id FROM dispatches
|
|
83
|
+
WHERE repo = ? AND pr_number = ?
|
|
84
|
+
ORDER BY dispatched_at DESC, rowid DESC
|
|
85
|
+
LIMIT 1`)
|
|
86
|
+
.get(repo, prNumber);
|
|
87
|
+
if (row === undefined || row === null)
|
|
88
|
+
return null;
|
|
89
|
+
return z.object({ ticket_id: z.string() }).parse(row).ticket_id;
|
|
90
|
+
}
|
|
91
|
+
export function setDispatchStatus(db, taskId, status) {
|
|
92
|
+
const completedAt = status === "completed" || status === "failed" ? new Date().toISOString() : null;
|
|
93
|
+
const info = db
|
|
94
|
+
.prepare(`UPDATE dispatches SET status = ?, completed_at = ? WHERE task_id = ?`)
|
|
95
|
+
.run(status, completedAt, taskId);
|
|
96
|
+
if (info.changes === 0) {
|
|
97
|
+
throw new Error(`orchestrator: cannot update unknown dispatch ${taskId}`);
|
|
98
|
+
}
|
|
99
|
+
return getDispatch(db, taskId);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Read dispatches, newest first, optionally capped.
|
|
103
|
+
*
|
|
104
|
+
* The cap is applied in SQL rather than by the caller slicing the result.
|
|
105
|
+
* Dispatches are never deleted, so this table only grows: a cap enforced after
|
|
106
|
+
* the read bounds the RESPONSE while still asking SQLite to materialise every
|
|
107
|
+
* dispatch ever recorded, which is the opposite of what a row cap is for. The
|
|
108
|
+
* ticket and journal readers already capped in SQL for this reason; this one was
|
|
109
|
+
* the outlier.
|
|
110
|
+
*
|
|
111
|
+
* `task_id` breaks ties because `dispatched_at` is an ISO timestamp and not
|
|
112
|
+
* unique -- two dispatches recorded in the same millisecond would otherwise
|
|
113
|
+
* order arbitrarily, and an arbitrary order at the cap boundary decides which
|
|
114
|
+
* row is visible at all.
|
|
115
|
+
*/
|
|
116
|
+
export function listDispatches(db, limit) {
|
|
117
|
+
const rows = db
|
|
118
|
+
.prepare(`SELECT * FROM dispatches ORDER BY dispatched_at DESC, task_id DESC LIMIT ?`)
|
|
119
|
+
.all(rowCap(limit));
|
|
120
|
+
return z.array(DispatchRowSchema).parse(rows);
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=dispatches.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatches.js","sourceRoot":"","sources":["../../../src/orchestrator/db/dispatches.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAe,MAAM,YAAY,CAAC;AAEjD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC;IACzC,YAAY;IACZ,aAAa;IACb,WAAW;IACX,QAAQ;CACT,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,MAAM,EAAE,oBAAoB;IAC5B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;IACzB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAYH,MAAM,UAAU,cAAc,CAAC,EAAU,EAAE,KAA0B;IACnE,EAAE,CAAC,OAAO,CACR;;gDAE4C,CAC7C,CAAC,GAAG,CACH,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,SAAS,EACf,KAAK,CAAC,IAAI,IAAI,IAAI,EAClB,KAAK,CAAC,MAAM,IAAI,IAAI,EACpB,KAAK,CAAC,QAAQ,IAAI,IAAI,EACtB,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CACzB,CAAC;IACF,OAAO,WAAW,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,CAAE,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAU,EAAE,MAAc;IACpD,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjF,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACxD,OAAO,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,EAAU,EAAE,MAAc;IAC7D,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,oDAAoD,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACzF,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnD,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAU,EAAE,IAAY,EAAE,QAAgB;IACzE,MAAM,GAAG,GAAG,EAAE;SACX,OAAO,CACN;;;gBAGU,CACX;SACA,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvB,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnD,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,EAAU,EACV,MAAc,EACd,MAAsB;IAEtB,MAAM,WAAW,GACf,MAAM,KAAK,WAAW,IAAI,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAClF,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,sEAAsE,CAAC;SAC/E,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IACpC,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,gDAAgD,MAAM,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,WAAW,CAAC,EAAE,EAAE,MAAM,CAAE,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU,EAAE,KAAc;IACvD,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CAAC,4EAA4E,CAAC;SACrF,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtB,OAAO,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ledger open + schema application.
|
|
3
|
+
*
|
|
4
|
+
* Lives inside `@kya-os/cli` rather than in a package of its own. The last
|
|
5
|
+
* internal package extracted from this CLI (`@kya-os/cli-kit`) had exactly one
|
|
6
|
+
* consumer and a `workspace:*` dependency that blocked publishing, and was
|
|
7
|
+
* folded back in. One consumer does not justify a package.
|
|
8
|
+
*
|
|
9
|
+
* @module cli/orchestrator/db
|
|
10
|
+
*/
|
|
11
|
+
import type BetterSqlite3 from "better-sqlite3";
|
|
12
|
+
export type Ledger = BetterSqlite3.Database;
|
|
13
|
+
/**
|
|
14
|
+
* How long a writer waits on SQLite's own lock before giving up, in ms.
|
|
15
|
+
*
|
|
16
|
+
* Without this, a second connection's write against an already-locked
|
|
17
|
+
* database gets `SQLITE_BUSY` back IMMEDIATELY -- not after a wait, on the
|
|
18
|
+
* very first attempt -- and every caller here (the journal writer, the
|
|
19
|
+
* liveness beat, a reconnect's replay) treats that as a genuine failure
|
|
20
|
+
* rather than momentary contention. `busy_timeout` makes SQLite retry
|
|
21
|
+
* acquiring the lock internally for this long before it reports busy, which
|
|
22
|
+
* is what turns a same-instant collision between two writers into a queued
|
|
23
|
+
* write instead of a dropped one.
|
|
24
|
+
*/
|
|
25
|
+
export declare const LEDGER_BUSY_TIMEOUT_MS = 5000;
|
|
26
|
+
/**
|
|
27
|
+
* Turn an optional caller cap into a SQL `LIMIT` value.
|
|
28
|
+
*
|
|
29
|
+
* SQLite reads ANY negative LIMIT as "no limit", which is what makes the omitted
|
|
30
|
+
* case a one-statement affair -- but it also means a caller who passes a bad cap
|
|
31
|
+
* gets the whole table back. `-1` meaning unlimited is a deliberate sentinel;
|
|
32
|
+
* `-5` arriving from a caller's arithmetic is a bug, and answering it with every
|
|
33
|
+
* row is the exact failure a row cap exists to prevent, reached by asking for
|
|
34
|
+
* fewer rows.
|
|
35
|
+
*
|
|
36
|
+
* Shared by every capped reader so the sentinel is written once and the
|
|
37
|
+
* validation cannot be present in one store and missing in the next.
|
|
38
|
+
*/
|
|
39
|
+
export declare function rowCap(limit: number | undefined): number;
|
|
40
|
+
/** Per-user state directory for the orchestrator ledger. */
|
|
41
|
+
export declare function stateDir(): string;
|
|
42
|
+
/** Default on-disk location of the ledger. */
|
|
43
|
+
export declare function defaultLedgerPath(): string;
|
|
44
|
+
/**
|
|
45
|
+
* Open (creating if absent) the ledger and apply the schema.
|
|
46
|
+
*
|
|
47
|
+
* Pass `":memory:"` for tests.
|
|
48
|
+
*/
|
|
49
|
+
export declare function openLedger(path?: string): Ledger;
|
|
50
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/db/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAC;AAGhD,MAAM,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC;AAI5C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AA0B5C;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAMxD;AAED,4DAA4D;AAC5D,wBAAgB,QAAQ,IAAI,MAAM,CAOjC;AAED,8CAA8C;AAC9C,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAA4B,GAAG,MAAM,CA0BrE"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ledger open + schema application.
|
|
3
|
+
*
|
|
4
|
+
* Lives inside `@kya-os/cli` rather than in a package of its own. The last
|
|
5
|
+
* internal package extracted from this CLI (`@kya-os/cli-kit`) had exactly one
|
|
6
|
+
* consumer and a `workspace:*` dependency that blocked publishing, and was
|
|
7
|
+
* folded back in. One consumer does not justify a package.
|
|
8
|
+
*
|
|
9
|
+
* @module cli/orchestrator/db
|
|
10
|
+
*/
|
|
11
|
+
import { createRequire } from "node:module";
|
|
12
|
+
import { mkdirSync } from "node:fs";
|
|
13
|
+
import { dirname, join } from "node:path";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { LEDGER_SCHEMA } from "./schema.js";
|
|
16
|
+
const require_ = createRequire(import.meta.url);
|
|
17
|
+
/**
|
|
18
|
+
* How long a writer waits on SQLite's own lock before giving up, in ms.
|
|
19
|
+
*
|
|
20
|
+
* Without this, a second connection's write against an already-locked
|
|
21
|
+
* database gets `SQLITE_BUSY` back IMMEDIATELY -- not after a wait, on the
|
|
22
|
+
* very first attempt -- and every caller here (the journal writer, the
|
|
23
|
+
* liveness beat, a reconnect's replay) treats that as a genuine failure
|
|
24
|
+
* rather than momentary contention. `busy_timeout` makes SQLite retry
|
|
25
|
+
* acquiring the lock internally for this long before it reports busy, which
|
|
26
|
+
* is what turns a same-instant collision between two writers into a queued
|
|
27
|
+
* write instead of a dropped one.
|
|
28
|
+
*/
|
|
29
|
+
export const LEDGER_BUSY_TIMEOUT_MS = 5000;
|
|
30
|
+
/**
|
|
31
|
+
* Load the SQLite driver on first use.
|
|
32
|
+
*
|
|
33
|
+
* `better-sqlite3` is an OPTIONAL dependency, and loaded lazily so it stays
|
|
34
|
+
* that way in practice. It is the only native addon `@kya-os/cli` pulls in,
|
|
35
|
+
* and the overwhelming majority of this CLI's users run `init`, `register` or
|
|
36
|
+
* `dco` and never touch the orchestrator at all. A top-level import would make
|
|
37
|
+
* every one of them download or compile it, and would break the entire CLI on
|
|
38
|
+
* any platform lacking a prebuild, rather than just the commands that need a
|
|
39
|
+
* database.
|
|
40
|
+
*/
|
|
41
|
+
function loadDriver() {
|
|
42
|
+
try {
|
|
43
|
+
return require_("better-sqlite3");
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
throw new Error("Orchestrator commands need the optional 'better-sqlite3' dependency, " +
|
|
47
|
+
"which is not installed or failed to build. Install it with " +
|
|
48
|
+
"`npm install better-sqlite3`. " +
|
|
49
|
+
`Underlying error: ${err instanceof Error ? err.message : String(err)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Turn an optional caller cap into a SQL `LIMIT` value.
|
|
54
|
+
*
|
|
55
|
+
* SQLite reads ANY negative LIMIT as "no limit", which is what makes the omitted
|
|
56
|
+
* case a one-statement affair -- but it also means a caller who passes a bad cap
|
|
57
|
+
* gets the whole table back. `-1` meaning unlimited is a deliberate sentinel;
|
|
58
|
+
* `-5` arriving from a caller's arithmetic is a bug, and answering it with every
|
|
59
|
+
* row is the exact failure a row cap exists to prevent, reached by asking for
|
|
60
|
+
* fewer rows.
|
|
61
|
+
*
|
|
62
|
+
* Shared by every capped reader so the sentinel is written once and the
|
|
63
|
+
* validation cannot be present in one store and missing in the next.
|
|
64
|
+
*/
|
|
65
|
+
export function rowCap(limit) {
|
|
66
|
+
if (limit === undefined)
|
|
67
|
+
return -1;
|
|
68
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
69
|
+
throw new Error(`orchestrator: limit must be a positive integer, got ${String(limit)}`);
|
|
70
|
+
}
|
|
71
|
+
return limit;
|
|
72
|
+
}
|
|
73
|
+
/** Per-user state directory for the orchestrator ledger. */
|
|
74
|
+
export function stateDir() {
|
|
75
|
+
const override = process.env.KYA_OS_ORCHESTRATOR_STATE_DIR;
|
|
76
|
+
if (override)
|
|
77
|
+
return override;
|
|
78
|
+
const xdg = process.env.XDG_STATE_HOME;
|
|
79
|
+
return xdg
|
|
80
|
+
? join(xdg, "kya-os", "orchestrator")
|
|
81
|
+
: join(homedir(), ".local", "state", "kya-os", "orchestrator");
|
|
82
|
+
}
|
|
83
|
+
/** Default on-disk location of the ledger. */
|
|
84
|
+
export function defaultLedgerPath() {
|
|
85
|
+
return join(stateDir(), "ledger.db");
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Open (creating if absent) the ledger and apply the schema.
|
|
89
|
+
*
|
|
90
|
+
* Pass `":memory:"` for tests.
|
|
91
|
+
*/
|
|
92
|
+
export function openLedger(path = defaultLedgerPath()) {
|
|
93
|
+
if (path !== ":memory:") {
|
|
94
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
const Database = loadDriver();
|
|
97
|
+
const db = new Database(path);
|
|
98
|
+
// See LEDGER_BUSY_TIMEOUT_MS. Meaningless for a private `:memory:` database
|
|
99
|
+
// (no other connection can ever reach it), but harmless to set regardless --
|
|
100
|
+
// one pragma applied unconditionally beats a conditional whose only purpose
|
|
101
|
+
// is to skip a no-op.
|
|
102
|
+
db.pragma(`busy_timeout = ${LEDGER_BUSY_TIMEOUT_MS}`);
|
|
103
|
+
// WAL so a reader (a TUI, another CLI invocation) never blocks the writer.
|
|
104
|
+
// An in-memory database cannot journal to a file, and asking it to silently
|
|
105
|
+
// leaves journal_mode at "memory", so only set it where it means something.
|
|
106
|
+
if (path !== ":memory:") {
|
|
107
|
+
db.pragma("journal_mode = WAL");
|
|
108
|
+
}
|
|
109
|
+
// The schema leans on foreign keys (superseded_by, dispatches.ticket_id).
|
|
110
|
+
// SQLite defaults this OFF per-connection, so forgetting it turns every
|
|
111
|
+
// referential guarantee into a comment.
|
|
112
|
+
db.pragma("foreign_keys = ON");
|
|
113
|
+
db.exec(LEDGER_SCHEMA);
|
|
114
|
+
return db;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/orchestrator/db/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Journal store (ORC-W1 PR-2).
|
|
3
|
+
*
|
|
4
|
+
* The journal records what the daemon OBSERVED, written from protocol events
|
|
5
|
+
* rather than from a model narrating its own behaviour. That distinction is
|
|
6
|
+
* the whole reason the daemon is the single writer.
|
|
7
|
+
*
|
|
8
|
+
* @module orchestrator/db/journal
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { type Ledger } from "./index.js";
|
|
12
|
+
import type { HubEventLike } from "../events.js";
|
|
13
|
+
export declare const JournalRowSchema: z.ZodObject<{
|
|
14
|
+
id: z.ZodString;
|
|
15
|
+
ticket_id: z.ZodNullable<z.ZodString>;
|
|
16
|
+
event_type: z.ZodString;
|
|
17
|
+
agent_did: z.ZodNullable<z.ZodString>;
|
|
18
|
+
agent_name: z.ZodNullable<z.ZodString>;
|
|
19
|
+
data: z.ZodString;
|
|
20
|
+
observed_at: z.ZodString;
|
|
21
|
+
}, "strip", z.ZodTypeAny, {
|
|
22
|
+
data: string;
|
|
23
|
+
id: string;
|
|
24
|
+
ticket_id: string | null;
|
|
25
|
+
event_type: string;
|
|
26
|
+
agent_did: string | null;
|
|
27
|
+
agent_name: string | null;
|
|
28
|
+
observed_at: string;
|
|
29
|
+
}, {
|
|
30
|
+
data: string;
|
|
31
|
+
id: string;
|
|
32
|
+
ticket_id: string | null;
|
|
33
|
+
event_type: string;
|
|
34
|
+
agent_did: string | null;
|
|
35
|
+
agent_name: string | null;
|
|
36
|
+
observed_at: string;
|
|
37
|
+
}>;
|
|
38
|
+
export type JournalRow = z.infer<typeof JournalRowSchema>;
|
|
39
|
+
/**
|
|
40
|
+
* Deterministic id for an event, so replaying it is a no-op.
|
|
41
|
+
*
|
|
42
|
+
* Hub events carry no id of their own, and the daemon replays a window of
|
|
43
|
+
* recent events on every boot to recover what it slept through. Without a
|
|
44
|
+
* content-derived key each restart would duplicate that window, and the
|
|
45
|
+
* journal's value is that you can count things in it.
|
|
46
|
+
*
|
|
47
|
+
* Derived from the fields that identify an occurrence rather than from
|
|
48
|
+
* arrival order, so the same event seen live and again on replay collapses to
|
|
49
|
+
* one row.
|
|
50
|
+
*/
|
|
51
|
+
export declare function journalEventId(event: HubEventLike): string;
|
|
52
|
+
export interface AppendJournalInput {
|
|
53
|
+
event: HubEventLike;
|
|
54
|
+
ticketId?: string | null;
|
|
55
|
+
/**
|
|
56
|
+
* Override the content-derived id, for a row that records an ACT rather than
|
|
57
|
+
* an observation.
|
|
58
|
+
*
|
|
59
|
+
* The default id is a hash of the event's content, which makes a replayed hub
|
|
60
|
+
* observation collapse to one row -- correct, and the reason the journal can
|
|
61
|
+
* be counted. It is wrong for something the system DID. Two identical status
|
|
62
|
+
* transitions in the same millisecond are two distinct acts, and content
|
|
63
|
+
* addressing silently dropped the second while its ticket write committed,
|
|
64
|
+
* leaving state that the evidence beside it disagreed with.
|
|
65
|
+
*
|
|
66
|
+
* So: observations are keyed by what they are, acts are keyed by being a
|
|
67
|
+
* separate occurrence. Callers recording an act pass a fresh id.
|
|
68
|
+
*/
|
|
69
|
+
id?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Append an observation. Idempotent: a replayed event updates nothing and
|
|
73
|
+
* inserts nothing, because its id is derived from its content.
|
|
74
|
+
*
|
|
75
|
+
* Returns true when the row was new, which lets a caller distinguish "first
|
|
76
|
+
* time seen" from "replayed" without a second query.
|
|
77
|
+
*/
|
|
78
|
+
export declare function appendJournal(db: Ledger, input: AppendJournalInput): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Recent entries, newest first.
|
|
81
|
+
*
|
|
82
|
+
* Ties on `observed_at` break on `rowid`, i.e. insertion order, matching the
|
|
83
|
+
* dispatch reader. `id` used to serve as the tiebreak, which read as ordered
|
|
84
|
+
* only because content-addressed ids made same-millisecond duplicates collapse
|
|
85
|
+
* in the first place. Once acts are keyed per occurrence they genuinely coexist
|
|
86
|
+
* within a millisecond, and ordering them by id is uuid order -- no order at
|
|
87
|
+
* all. An audit trail that lists three transitions in the wrong sequence is
|
|
88
|
+
* worse than one that lists two, because it reads as authoritative.
|
|
89
|
+
*/
|
|
90
|
+
export declare function listJournal(db: Ledger, limit?: number): JournalRow[];
|
|
91
|
+
export declare function listJournalForTicket(db: Ledger, ticketId: string, limit?: number): JournalRow[];
|
|
92
|
+
export declare function countJournal(db: Ledger): number;
|
|
93
|
+
//# sourceMappingURL=journal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/db/journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;EAQ3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAS1D;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;;;;;;;;;;OAaG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAkB5E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,UAAU,EAAE,CAKjE;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAM,GACV,UAAU,EAAE,CASd;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAG/C"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Journal store (ORC-W1 PR-2).
|
|
3
|
+
*
|
|
4
|
+
* The journal records what the daemon OBSERVED, written from protocol events
|
|
5
|
+
* rather than from a model narrating its own behaviour. That distinction is
|
|
6
|
+
* the whole reason the daemon is the single writer.
|
|
7
|
+
*
|
|
8
|
+
* @module orchestrator/db/journal
|
|
9
|
+
*/
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { rowCap } from "./index.js";
|
|
13
|
+
export const JournalRowSchema = z.object({
|
|
14
|
+
id: z.string(),
|
|
15
|
+
ticket_id: z.string().nullable(),
|
|
16
|
+
event_type: z.string(),
|
|
17
|
+
agent_did: z.string().nullable(),
|
|
18
|
+
agent_name: z.string().nullable(),
|
|
19
|
+
data: z.string(),
|
|
20
|
+
observed_at: z.string(),
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Deterministic id for an event, so replaying it is a no-op.
|
|
24
|
+
*
|
|
25
|
+
* Hub events carry no id of their own, and the daemon replays a window of
|
|
26
|
+
* recent events on every boot to recover what it slept through. Without a
|
|
27
|
+
* content-derived key each restart would duplicate that window, and the
|
|
28
|
+
* journal's value is that you can count things in it.
|
|
29
|
+
*
|
|
30
|
+
* Derived from the fields that identify an occurrence rather than from
|
|
31
|
+
* arrival order, so the same event seen live and again on replay collapses to
|
|
32
|
+
* one row.
|
|
33
|
+
*/
|
|
34
|
+
export function journalEventId(event) {
|
|
35
|
+
const canonical = JSON.stringify([
|
|
36
|
+
event.type,
|
|
37
|
+
event.timestamp,
|
|
38
|
+
event.agentDid,
|
|
39
|
+
event.agentName,
|
|
40
|
+
event.data,
|
|
41
|
+
]);
|
|
42
|
+
return createHash("sha256").update(canonical).digest("hex").slice(0, 32);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Append an observation. Idempotent: a replayed event updates nothing and
|
|
46
|
+
* inserts nothing, because its id is derived from its content.
|
|
47
|
+
*
|
|
48
|
+
* Returns true when the row was new, which lets a caller distinguish "first
|
|
49
|
+
* time seen" from "replayed" without a second query.
|
|
50
|
+
*/
|
|
51
|
+
export function appendJournal(db, input) {
|
|
52
|
+
const { event } = input;
|
|
53
|
+
const info = db
|
|
54
|
+
.prepare(`INSERT OR IGNORE INTO journal
|
|
55
|
+
(id, ticket_id, event_type, agent_did, agent_name, data, observed_at)
|
|
56
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
|
57
|
+
.run(input.id ?? journalEventId(event), input.ticketId ?? null, event.type, event.agentDid || null, event.agentName || null, JSON.stringify(event.data ?? {}), new Date(event.timestamp).toISOString());
|
|
58
|
+
return info.changes === 1;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Recent entries, newest first.
|
|
62
|
+
*
|
|
63
|
+
* Ties on `observed_at` break on `rowid`, i.e. insertion order, matching the
|
|
64
|
+
* dispatch reader. `id` used to serve as the tiebreak, which read as ordered
|
|
65
|
+
* only because content-addressed ids made same-millisecond duplicates collapse
|
|
66
|
+
* in the first place. Once acts are keyed per occurrence they genuinely coexist
|
|
67
|
+
* within a millisecond, and ordering them by id is uuid order -- no order at
|
|
68
|
+
* all. An audit trail that lists three transitions in the wrong sequence is
|
|
69
|
+
* worse than one that lists two, because it reads as authoritative.
|
|
70
|
+
*/
|
|
71
|
+
export function listJournal(db, limit = 100) {
|
|
72
|
+
const rows = db
|
|
73
|
+
.prepare(`SELECT * FROM journal ORDER BY observed_at DESC, rowid DESC LIMIT ?`)
|
|
74
|
+
.all(rowCap(limit));
|
|
75
|
+
return z.array(JournalRowSchema).parse(rows);
|
|
76
|
+
}
|
|
77
|
+
export function listJournalForTicket(db, ticketId, limit = 100) {
|
|
78
|
+
// Bounded like listJournal, and validated like it too. An unbounded branch
|
|
79
|
+
// here means a caller's validated cap is silently ignored the moment it also
|
|
80
|
+
// passes a ticket id, which is the shape a cap-bypass usually takes: not a
|
|
81
|
+
// missing check, a second path that forgot it.
|
|
82
|
+
const rows = db
|
|
83
|
+
.prepare(`SELECT * FROM journal WHERE ticket_id = ? ORDER BY observed_at DESC, rowid DESC LIMIT ?`)
|
|
84
|
+
.all(ticketId, rowCap(limit));
|
|
85
|
+
return z.array(JournalRowSchema).parse(rows);
|
|
86
|
+
}
|
|
87
|
+
export function countJournal(db) {
|
|
88
|
+
const row = db.prepare(`SELECT COUNT(*) AS n FROM journal`).get();
|
|
89
|
+
return z.object({ n: z.number() }).parse(row).n;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../../../src/orchestrator/db/journal.ts"],"names":[],"mappings":"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"}
|