@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,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The TUI's one control-plane action (review-loop P18 commit 4): dispatch
|
|
3
|
+
* a task to a connected agent.
|
|
4
|
+
*
|
|
5
|
+
* Trust boundary: this opens a short-lived, authenticated observer
|
|
6
|
+
* connection -- the SAME wire pattern `hub-observer.ts`'s long-lived
|
|
7
|
+
* `HubObserver` uses (contracts `./handshake`'s hello/challenge/proof
|
|
8
|
+
* frames, the #686 precedent), reusing its exported helpers rather than
|
|
9
|
+
* re-deriving the handshake. It sends exactly one `fleet.dispatch` frame,
|
|
10
|
+
* waits for exactly one `fleet.dispatch.result`, and closes -- no
|
|
11
|
+
* reconnect, no history, no long-lived state, because a TUI keystroke is
|
|
12
|
+
* a single request, not a subscription.
|
|
13
|
+
*
|
|
14
|
+
* `fleet.dispatch` here is NOT `@kya-os/compute`'s HTTP `fleet-mcp`
|
|
15
|
+
* surface (`packages/compute/src/didcomm/fleet-mcp.ts`), which is bearer-
|
|
16
|
+
* token/org-scoped and has no notion of a per-agent signing key. It is
|
|
17
|
+
* the wire-level message `packages/compute/src/didcomm/hub.ts` already
|
|
18
|
+
* handles directly on an admitted OBSERVER socket
|
|
19
|
+
* (`ws.on("message", ...)` inside `transport.onObserver`): a JSON frame
|
|
20
|
+
* `{ type: "fleet.dispatch", taskId, toDid, description, context?,
|
|
21
|
+
* timeoutMs? }`, answered with `{ type: "fleet.dispatch.result", taskId,
|
|
22
|
+
* status, response?, error?, ... }`. Reusing this existing hub-side
|
|
23
|
+
* handler is why this file adds nothing to `@kya-os/contracts` or
|
|
24
|
+
* `@kya-os/compute` -- the wire shape already exists and this PR's
|
|
25
|
+
* `Package:` line is `@kya-os/cli` alone.
|
|
26
|
+
*
|
|
27
|
+
* The hub proposes, the target's on-disk command allowlist (P17, epic
|
|
28
|
+
* decision 10) disposes: a refusal comes back as an ordinary
|
|
29
|
+
* `fleet.dispatch.result` with a non-"completed" status, which the caller
|
|
30
|
+
* renders as a refusal, not an error (see `AgentsView`'s handling of
|
|
31
|
+
* `status === "refused"`).
|
|
32
|
+
*
|
|
33
|
+
* @module cli/orchestrator/hub-dispatch
|
|
34
|
+
*/
|
|
35
|
+
import { randomUUID } from "node:crypto";
|
|
36
|
+
import { readFileSync } from "node:fs";
|
|
37
|
+
import WebSocket from "ws";
|
|
38
|
+
import { DIDCommChallengeSchema, DIDCommProofSchema } from "@kya-os/contracts/handshake";
|
|
39
|
+
import { buildObserverHello, signChallenge, isPlaintextNonLoopback, LOOPBACK_HOSTS, } from "./hub-observer.js";
|
|
40
|
+
import { signingKeyPath } from "../utils/dco/paths.js";
|
|
41
|
+
import { parseEd25519SshPrivateKeyPem } from "../utils/dco/ssh-key.js";
|
|
42
|
+
import { readHubConfig } from "../utils/hub-config.js";
|
|
43
|
+
const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10000;
|
|
44
|
+
const DEFAULT_DISPATCH_TIMEOUT_MS = 30000;
|
|
45
|
+
/**
|
|
46
|
+
* Opens one observer connection, authenticates if asked to, sends one
|
|
47
|
+
* `fleet.dispatch` frame for `targetDid`/`command`, and resolves with its
|
|
48
|
+
* `fleet.dispatch.result` -- or rejects on a connection/handshake/timeout
|
|
49
|
+
* failure. Always closes the socket before settling, on every path.
|
|
50
|
+
*/
|
|
51
|
+
export function dispatchTask(config, targetDid, command) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const ws = (config.wsFactory ?? ((endpoint) => new WebSocket(endpoint)))(config.endpoint);
|
|
54
|
+
const taskId = randomUUID().slice(0, 8);
|
|
55
|
+
let settled = false;
|
|
56
|
+
let handshakeDone = false;
|
|
57
|
+
let dispatchTimer;
|
|
58
|
+
const finish = (outcome) => {
|
|
59
|
+
if (settled)
|
|
60
|
+
return;
|
|
61
|
+
settled = true;
|
|
62
|
+
clearTimeout(handshakeTimer);
|
|
63
|
+
if (dispatchTimer)
|
|
64
|
+
clearTimeout(dispatchTimer);
|
|
65
|
+
try {
|
|
66
|
+
ws.close();
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
/* already closing/closed */
|
|
70
|
+
}
|
|
71
|
+
if (outcome instanceof Error)
|
|
72
|
+
reject(outcome);
|
|
73
|
+
else
|
|
74
|
+
resolve(outcome);
|
|
75
|
+
};
|
|
76
|
+
const handshakeTimer = setTimeout(() => {
|
|
77
|
+
finish(new Error(`hub at ${config.endpoint} did not complete the observer handshake in time`));
|
|
78
|
+
}, config.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
79
|
+
ws.on("open", () => {
|
|
80
|
+
try {
|
|
81
|
+
ws.send(JSON.stringify(buildObserverHello(config.identity)));
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
finish(err instanceof Error ? err : new Error(String(err)));
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
ws.on("message", (raw) => {
|
|
88
|
+
void handleFrame(raw.toString());
|
|
89
|
+
});
|
|
90
|
+
async function handleFrame(text) {
|
|
91
|
+
let msg;
|
|
92
|
+
try {
|
|
93
|
+
const parsed = JSON.parse(text);
|
|
94
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
95
|
+
return;
|
|
96
|
+
msg = parsed;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return; // a frame we cannot parse is not a frame we can act on
|
|
100
|
+
}
|
|
101
|
+
const type = msg["type"];
|
|
102
|
+
if (type === "challenge") {
|
|
103
|
+
const challenge = DIDCommChallengeSchema.safeParse(msg);
|
|
104
|
+
if (!challenge.success || !config.identity)
|
|
105
|
+
return;
|
|
106
|
+
try {
|
|
107
|
+
const jws = await signChallenge(config.identity, challenge.data.nonce);
|
|
108
|
+
ws.send(JSON.stringify(DIDCommProofSchema.parse({ type: "proof", jws })));
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
finish(err instanceof Error ? err : new Error(String(err)));
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// "snapshot" is the hub's readiness signal (same as HubObserver):
|
|
116
|
+
// the handshake is complete exactly once this arrives. Only send
|
|
117
|
+
// the dispatch frame on the FIRST snapshot -- a hub that (unusually)
|
|
118
|
+
// sent more than one must not trigger a second dispatch.
|
|
119
|
+
if (type === "snapshot" && !handshakeDone) {
|
|
120
|
+
handshakeDone = true;
|
|
121
|
+
clearTimeout(handshakeTimer);
|
|
122
|
+
try {
|
|
123
|
+
ws.send(JSON.stringify({
|
|
124
|
+
type: "fleet.dispatch",
|
|
125
|
+
taskId,
|
|
126
|
+
toDid: targetDid,
|
|
127
|
+
description: command,
|
|
128
|
+
timeoutMs: config.dispatchTimeoutMs ?? DEFAULT_DISPATCH_TIMEOUT_MS,
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
finish(err instanceof Error ? err : new Error(String(err)));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
dispatchTimer = setTimeout(() => {
|
|
136
|
+
finish(new Error("dispatch timed out waiting for a result"));
|
|
137
|
+
}, config.dispatchTimeoutMs ?? DEFAULT_DISPATCH_TIMEOUT_MS);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (type === "fleet.dispatch.result" && msg["taskId"] === taskId) {
|
|
141
|
+
const status = typeof msg["status"] === "string" ? msg["status"] : "unknown";
|
|
142
|
+
finish({
|
|
143
|
+
taskId,
|
|
144
|
+
status,
|
|
145
|
+
response: typeof msg["response"] === "string" ? msg["response"] : undefined,
|
|
146
|
+
error: typeof msg["error"] === "string" ? msg["error"] : undefined,
|
|
147
|
+
// Allowlist, not a blacklist: `status` here is whatever the
|
|
148
|
+
// target agent's own task-response body reports, forwarded
|
|
149
|
+
// verbatim by the hub (dispatchAndWait in
|
|
150
|
+
// packages/compute/src/didcomm/hub.ts has no fixed status enum
|
|
151
|
+
// and no "failed" handling of its own). A blacklist of known
|
|
152
|
+
// failure strings reads any UNANTICIPATED one -- including the
|
|
153
|
+
// agent-reported "failed" itself -- as success. "completed" is
|
|
154
|
+
// the one value both that hub code and fleet-server's HubClient
|
|
155
|
+
// (packages/fleet-server/src/hub-client.ts, the other existing
|
|
156
|
+
// consumer of this same fleet.dispatch.result message) treat as
|
|
157
|
+
// an unambiguous success; that HubClient collapses everything
|
|
158
|
+
// else to a failure the same way.
|
|
159
|
+
ok: status === "completed",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
ws.on("error", (err) => finish(err));
|
|
164
|
+
ws.on("close", (code, reason) => {
|
|
165
|
+
if (settled)
|
|
166
|
+
return;
|
|
167
|
+
finish(new Error(`hub closed the connection (${code}${reason.length ? `: ${reason.toString()}` : ""})`));
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Is this endpoint loopback-only? Mirrors `isPlaintextNonLoopback`'s host
|
|
173
|
+
* set but answers a different question: that function asks "is this
|
|
174
|
+
* unencrypted AND remote" (a warn-but-allow case); this asks "is this
|
|
175
|
+
* unreachable from anywhere but this machine" (the ONLY case where
|
|
176
|
+
* connecting with no identity at all -- `--anonymous`-equivalent -- is
|
|
177
|
+
* appropriate for a write action like dispatch).
|
|
178
|
+
*/
|
|
179
|
+
export function isLoopbackEndpoint(endpoint) {
|
|
180
|
+
try {
|
|
181
|
+
return LOOPBACK_HOSTS.has(new URL(endpoint).hostname.toLowerCase());
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Builds the identity used to authenticate the dispatch connection, from
|
|
189
|
+
* the profile whose row is focused in the TUI. Every profile's key
|
|
190
|
+
* lives under `~/.mcpi/keys/<slug>` in the SSH-signing format
|
|
191
|
+
* `dco setup` writes (see `signing-key-writer.ts`); profiles never
|
|
192
|
+
* persist a raw seed on disk, so the seed is recovered from that SSH
|
|
193
|
+
* key via the same parser `dco fix` already uses to sign a fix
|
|
194
|
+
* attestation from the same file (`parseEd25519SshPrivateKeyPem`) rather
|
|
195
|
+
* than adding a second place that stores key material.
|
|
196
|
+
*
|
|
197
|
+
* Authenticating AS the profile being dispatched to -- rather than some
|
|
198
|
+
* separate "which of my identities is primary" concept, which does not
|
|
199
|
+
* exist anywhere else in this codebase -- is a deliberate scope
|
|
200
|
+
* decision: every profile under `~/.mcpi/keys/` belongs to the one
|
|
201
|
+
* operator running this TUI, so proving control of the row already in
|
|
202
|
+
* view is sufficient authorization and needs no new concept.
|
|
203
|
+
*
|
|
204
|
+
* Undefined if the key file is missing or not in the format this
|
|
205
|
+
* codebase writes -- the caller falls back to loopback-only anonymous
|
|
206
|
+
* dispatch in that case, matching `orchestrator daemon`'s own
|
|
207
|
+
* `--anonymous` precedent.
|
|
208
|
+
*/
|
|
209
|
+
export function resolveProfileIdentity(profile) {
|
|
210
|
+
try {
|
|
211
|
+
const pem = readFileSync(signingKeyPath(profile.slug), "utf-8");
|
|
212
|
+
const { seed } = parseEd25519SshPrivateKeyPem(pem);
|
|
213
|
+
return { did: profile.did, privateKey: seed.toString("base64") };
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The TUI-facing entry point: resolve the hub and the dispatching
|
|
221
|
+
* identity, dispatch, and reduce every failure mode (no hub configured,
|
|
222
|
+
* no usable key on a non-loopback hub, a transport/handshake error) to
|
|
223
|
+
* the same `DispatchOutcome` shape the agents view renders -- so the UI
|
|
224
|
+
* never has to distinguish "the wire call threw" from "the wire call
|
|
225
|
+
* returned an unhappy status".
|
|
226
|
+
*/
|
|
227
|
+
export async function dispatchToProfile(profile, command, wsFactory) {
|
|
228
|
+
const hubConfig = readHubConfig();
|
|
229
|
+
if (!hubConfig) {
|
|
230
|
+
return { ok: false, status: "error", detail: "no hub configured (~/.mcpi/hub.json is absent)" };
|
|
231
|
+
}
|
|
232
|
+
const warning = isPlaintextNonLoopback(hubConfig.endpoint)
|
|
233
|
+
? `${hubConfig.endpoint} is plaintext ws:// to a non-loopback host -- the DID, public key, and signed proof are sent unencrypted.`
|
|
234
|
+
: undefined;
|
|
235
|
+
const identity = resolveProfileIdentity(profile);
|
|
236
|
+
if (!identity && !isLoopbackEndpoint(hubConfig.endpoint)) {
|
|
237
|
+
return {
|
|
238
|
+
ok: false,
|
|
239
|
+
status: "error",
|
|
240
|
+
detail: `no usable signing key for ${profile.slug}, and ${hubConfig.endpoint} is not loopback`,
|
|
241
|
+
warning,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
const result = await dispatchTask({ endpoint: hubConfig.endpoint, identity, wsFactory }, profile.did, command);
|
|
246
|
+
return { ok: result.ok, status: result.status, detail: result.error ?? result.response, warning };
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
return {
|
|
250
|
+
ok: false,
|
|
251
|
+
status: "error",
|
|
252
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
253
|
+
warning,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=hub-dispatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-dispatch.js","sourceRoot":"","sources":["../../src/orchestrator/hub-dispatch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A coordination hub, observed from outside its process.
|
|
3
|
+
*
|
|
4
|
+
* This is what makes the daemon host pluggable. `OrchestratorDaemon` takes a
|
|
5
|
+
* structural `EventSource` -- `onEvent` plus `getRecentEvents` -- rather than the
|
|
6
|
+
* concrete hub, so anything that can supply those two methods can drive it. In
|
|
7
|
+
* process, the hub itself supplies them. Here they come over a WebSocket, which
|
|
8
|
+
* lets the daemon run wherever the operator already runs things and attach to
|
|
9
|
+
* whichever hub they already point their agents at.
|
|
10
|
+
*
|
|
11
|
+
* The CLI deliberately does not depend on @kya-os/compute: compute is PID 1 in a
|
|
12
|
+
* Fly worker, this is a published CLI. So the observer protocol is spoken
|
|
13
|
+
* directly, against the wire format in @kya-os/contracts/handshake rather than
|
|
14
|
+
* a copy of it. (Not `/proof`: both a `proof.ts` and a `proof/index.ts` exist
|
|
15
|
+
* there, and Node and tsc disagree about which one that subpath means.)
|
|
16
|
+
*
|
|
17
|
+
* ## Why connect() waits for the snapshot
|
|
18
|
+
*
|
|
19
|
+
* `getRecentEvents` is synchronous, because in-process it reads an array. Over a
|
|
20
|
+
* socket the history arrives in a frame, so `connect()` does not resolve until
|
|
21
|
+
* that frame lands. A daemon started before it arrives would reconcile against
|
|
22
|
+
* an empty window and silently skip the recovery a restart exists to perform --
|
|
23
|
+
* the failure mode is a quiet no-op, which is the kind that survives review.
|
|
24
|
+
*
|
|
25
|
+
* @module cli/orchestrator/hub-observer
|
|
26
|
+
*/
|
|
27
|
+
import { type ObserverHello } from "@kya-os/contracts/handshake";
|
|
28
|
+
import { type EventSource, type HubEventLike } from "./events.js";
|
|
29
|
+
/** The identity fields this needs out of `.mcpi/identity.json`. */
|
|
30
|
+
export interface ObserverIdentity {
|
|
31
|
+
did: string;
|
|
32
|
+
/** Raw Ed25519 seed, base64 — the shape identity.json stores. */
|
|
33
|
+
privateKey: string;
|
|
34
|
+
}
|
|
35
|
+
export interface HubObserverConfig {
|
|
36
|
+
/** e.g. `ws://127.0.0.1:18821`, normally from DIDCOMM_HUB_ENDPOINT. */
|
|
37
|
+
endpoint: string;
|
|
38
|
+
/** Omit to observe anonymously, which only a loopback hub will accept. */
|
|
39
|
+
identity?: ObserverIdentity;
|
|
40
|
+
/** How long to wait for the snapshot before giving up on a connection. */
|
|
41
|
+
handshakeTimeoutMs?: number;
|
|
42
|
+
/** Reconnect backoff ceiling. */
|
|
43
|
+
maxBackoffMs?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Read an identity file, returning undefined rather than throwing.
|
|
47
|
+
*
|
|
48
|
+
* A missing or malformed identity is a normal state -- the operator may not have
|
|
49
|
+
* run `dco setup` -- and it only matters if the hub demands authentication. The
|
|
50
|
+
* hub's rejection message says far more about what is wrong than a parse error
|
|
51
|
+
* raised here would.
|
|
52
|
+
*/
|
|
53
|
+
export declare function readObserverIdentity(path: string): ObserverIdentity | undefined;
|
|
54
|
+
/** The public JWK the hub needs to bind a did:key to the presented key. */
|
|
55
|
+
export declare function publicJwkFor(identity: ObserverIdentity): string;
|
|
56
|
+
/**
|
|
57
|
+
* The `observe` hello frame for an optional identity -- shared by every
|
|
58
|
+
* observer-wire client (this file's long-lived `HubObserver` and
|
|
59
|
+
* `hub-dispatch.ts`'s short-lived one-shot client), so the two cannot
|
|
60
|
+
* silently drift on what an anonymous vs. authenticated hello looks like.
|
|
61
|
+
*/
|
|
62
|
+
export declare function buildObserverHello(identity?: ObserverIdentity): ObserverHello;
|
|
63
|
+
/** Sign the hub's challenge, per the wire format in contracts. */
|
|
64
|
+
export declare function signChallenge(identity: ObserverIdentity, nonce: string): Promise<string>;
|
|
65
|
+
/**
|
|
66
|
+
* Validate a raw `challenge` frame and, if well-formed, answer it: sign its
|
|
67
|
+
* nonce and build the wire `proof` frame to send back. Returns `undefined`
|
|
68
|
+
* for a frame that fails `DIDCommChallengeSchema` (not this caller's to
|
|
69
|
+
* handle -- the same "ignore, don't fail" choice both callers already made
|
|
70
|
+
* before this was factored out). Every caller of the observer
|
|
71
|
+
* `observe`/`challenge`/`proof` handshake shares this exact step -- {@link
|
|
72
|
+
* HubObserver}'s long-lived `connect()` and the one-shot
|
|
73
|
+
* `github-credential-anchor-sender.ts` -- so it lives here once rather than
|
|
74
|
+
* being reimplemented per caller.
|
|
75
|
+
*/
|
|
76
|
+
export declare function respondToObserverChallenge(identity: ObserverIdentity, rawChallenge: unknown): Promise<string | undefined>;
|
|
77
|
+
/**
|
|
78
|
+
* Hosts that need no transport encryption because nothing off this machine
|
|
79
|
+
* can reach them. Both `::1` and `[::1]` are listed because `URL#hostname`
|
|
80
|
+
* keeps an IPv6 literal's brackets rather than stripping them.
|
|
81
|
+
*/
|
|
82
|
+
export declare const LOOPBACK_HOSTS: Set<string>;
|
|
83
|
+
/**
|
|
84
|
+
* Is this an unencrypted `ws://` endpoint pointed somewhere other than this
|
|
85
|
+
* machine?
|
|
86
|
+
*
|
|
87
|
+
* A loopback hub genuinely does not need TLS -- nothing but this machine can
|
|
88
|
+
* see the socket. Anything else sends the DID, the public key, and the signed
|
|
89
|
+
* proof of possession over a transport anyone on the path can read; `wss://`
|
|
90
|
+
* is the production default for exactly this reason, and a plain `ws://` to a
|
|
91
|
+
* remote host is far more likely a dev config that escaped dev than a
|
|
92
|
+
* deliberate choice. An endpoint this cannot even parse is left alone: it
|
|
93
|
+
* fails at `connect()`, with a clearer error than anything said here.
|
|
94
|
+
*/
|
|
95
|
+
export declare function isPlaintextNonLoopback(endpoint: string): boolean;
|
|
96
|
+
export declare class HubObserver implements EventSource {
|
|
97
|
+
private readonly config;
|
|
98
|
+
private ws;
|
|
99
|
+
private handlers;
|
|
100
|
+
private history;
|
|
101
|
+
private closedByUs;
|
|
102
|
+
private backoffMs;
|
|
103
|
+
/**
|
|
104
|
+
* Has a handshake ever completed?
|
|
105
|
+
*
|
|
106
|
+
* Reconnecting is only right after a connection that WORKED. Retrying a first
|
|
107
|
+
* failure means a misconfigured or unauthorised hub is retried forever in the
|
|
108
|
+
* background while the caller has already been told it failed -- a daemon that
|
|
109
|
+
* looks alive and records nothing, which is the outcome connect() rejects to
|
|
110
|
+
* avoid.
|
|
111
|
+
*/
|
|
112
|
+
private hasConnected;
|
|
113
|
+
/** The pending reconnect. Held so close() can cancel it, not merely ignore it. */
|
|
114
|
+
private reconnectTimer;
|
|
115
|
+
/**
|
|
116
|
+
* Serializes `handleFrame` across messages.
|
|
117
|
+
*
|
|
118
|
+
* `ws.on("message")` fires and forgets (`void this.handleFrame(...)`), which
|
|
119
|
+
* was harmless while every frame finished synchronously within one tick.
|
|
120
|
+
* Retrying a replay's failed write does not: it `await`s a `setTimeout`, and
|
|
121
|
+
* without this queue a live frame arriving during that wait ran a SECOND,
|
|
122
|
+
* concurrent `handleFrame` -- pushing onto `this.history` right before the
|
|
123
|
+
* still-in-flight snapshot handler clobbered it with `this.history = window`,
|
|
124
|
+
* and dispatching that live event ahead of the gap it was meant to follow
|
|
125
|
+
* (review finding on #686). Chaining onto this instead makes every frame
|
|
126
|
+
* fully finish before the next one starts, socket-wide.
|
|
127
|
+
*/
|
|
128
|
+
private frameQueue;
|
|
129
|
+
constructor(config: HubObserverConfig);
|
|
130
|
+
/** Queue a frame behind whatever is still being processed for this socket. */
|
|
131
|
+
private enqueueFrame;
|
|
132
|
+
/**
|
|
133
|
+
* Connect, authenticate if asked to, and resolve once the snapshot has landed.
|
|
134
|
+
*
|
|
135
|
+
* Rejects rather than retrying: a first connection that cannot authenticate is
|
|
136
|
+
* a configuration problem, and retrying it forever would leave a daemon that
|
|
137
|
+
* looks alive and records nothing. Reconnection after a WORKING connection is
|
|
138
|
+
* a different case and is handled silently below.
|
|
139
|
+
*/
|
|
140
|
+
connect(): Promise<void>;
|
|
141
|
+
private handleFrame;
|
|
142
|
+
/**
|
|
143
|
+
* Hand one event to every handler, once.
|
|
144
|
+
*
|
|
145
|
+
* Shared by live delivery and reconnect replay (through {@link
|
|
146
|
+
* dispatchReplayed}) so the two cannot diverge -- a replay that skipped a
|
|
147
|
+
* handler would recover the journal and not the ticket status, which reads
|
|
148
|
+
* as the reactor having run and disagreed. Returns whether every handler
|
|
149
|
+
* succeeded, so a caller that can retry (replay) knows whether it needs to.
|
|
150
|
+
*
|
|
151
|
+
* This is now the ONLY place a handler's failure is caught. The handlers
|
|
152
|
+
* themselves (`registerJournalWriter`, `registerReviewReactor`) deliberately
|
|
153
|
+
* do not swallow their own write errors -- a handler that always reports
|
|
154
|
+
* success by catching internally makes `allOk` a lie, which is what made
|
|
155
|
+
* `dispatchReplayed`'s retry dead code against the real handlers even though
|
|
156
|
+
* it worked against a mock that threw directly.
|
|
157
|
+
*/
|
|
158
|
+
private dispatch;
|
|
159
|
+
/**
|
|
160
|
+
* Dispatch one event from a reconnect's gap replay, retrying instead of
|
|
161
|
+
* letting a write failure disappear it.
|
|
162
|
+
*
|
|
163
|
+
* All handlers together, every attempt, not just the ones that failed: safe
|
|
164
|
+
* because journal ids are content-derived and verdicts are keyed on their
|
|
165
|
+
* subject, so re-dispatching an event a handler already wrote collapses
|
|
166
|
+
* rather than double-applying. SQLite's own `busy_timeout` already absorbs a
|
|
167
|
+
* same-instant lock collision between two writers; this covers whatever
|
|
168
|
+
* survives that -- a slower disk, any other transient failure -- during the
|
|
169
|
+
* one window where a dropped write has no later chance to be recovered.
|
|
170
|
+
*/
|
|
171
|
+
private dispatchReplayed;
|
|
172
|
+
/**
|
|
173
|
+
* Schedule the next attempt.
|
|
174
|
+
*
|
|
175
|
+
* The timer is deliberately NOT unref'd, and that is the whole point. While
|
|
176
|
+
* reconnecting there is no socket, and in `kya-os orchestrator daemon` the
|
|
177
|
+
* socket is the only handle keeping the process alive -- the liveness beat is
|
|
178
|
+
* unref'd and the command holds no stdin. An unref'd timer here therefore let
|
|
179
|
+
* the process exit during the exact window it was supposed to be recovering
|
|
180
|
+
* in: a hub restart quietly killed the daemon, with no error and no exit
|
|
181
|
+
* message. This timer stands in for the socket, so it has to hold the loop
|
|
182
|
+
* exactly as the socket does.
|
|
183
|
+
*
|
|
184
|
+
* The rule the two now share: the observer owns the daemon's liveness while
|
|
185
|
+
* it is attached or trying to be, and everything else (the beat, the signal
|
|
186
|
+
* handlers) is incidental and stays unref'd.
|
|
187
|
+
*/
|
|
188
|
+
private scheduleReconnect;
|
|
189
|
+
/**
|
|
190
|
+
* Is a reconnect scheduled -- i.e. is this observer still holding the process
|
|
191
|
+
* open on purpose? Distinguishes a cancelled timer from a merely neutered one,
|
|
192
|
+
* which is invisible from the outside and is the difference between a stopped
|
|
193
|
+
* daemon exiting and hanging.
|
|
194
|
+
*/
|
|
195
|
+
hasPendingReconnect(): boolean;
|
|
196
|
+
onEvent(handler: (event: HubEventLike) => void): () => void;
|
|
197
|
+
getRecentEvents(limit?: number): HubEventLike[];
|
|
198
|
+
/** Stop observing. Idempotent, and suppresses the reconnect. */
|
|
199
|
+
close(): void;
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=hub-observer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hub-observer.d.ts","sourceRoot":"","sources":["../../src/orchestrator/hub-observer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,OAAO,EAML,KAAK,aAAa,EACnB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAsB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAEtF,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAkB/E;AAED,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAG/D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,CAAC,EAAE,gBAAgB,GAAG,aAAa,CAK7E;AAED,kEAAkE;AAClE,wBAAsB,aAAa,CACjC,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,gBAAgB,EAC1B,YAAY,EAAE,OAAO,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAK7B;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,aAAsD,CAAC;AAElF;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQhE;AAsBD,qBAAa,WAAY,YAAW,WAAW;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,EAAE,CAAwB;IAClC,OAAO,CAAC,QAAQ,CAA4C;IAC5D,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAO;IACxB;;;;;;;;OAQG;IACH,OAAO,CAAC,YAAY,CAAS;IAC7B,kFAAkF;IAClF,OAAO,CAAC,cAAc,CAA4C;IAClE;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,UAAU,CAAoC;gBAE1C,MAAM,EAAE,iBAAiB;IAIrC,8EAA8E;IAC9E,OAAO,CAAC,YAAY;IAUpB;;;;;;;OAOG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAyEV,WAAW;IA8FzB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,QAAQ;IAkBhB;;;;;;;;;;;OAWG;YACW,gBAAgB;IAa9B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,iBAAiB;IAmBzB;;;;;OAKG;IACH,mBAAmB,IAAI,OAAO;IAI9B,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI;IAO3D,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE;IAI/C,gEAAgE;IAChE,KAAK,IAAI,IAAI;CAYd"}
|