toolroll 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1113 -0
- package/THIRD_PARTY_NOTICES.md +449 -0
- package/dist/accent-colors.d.ts +48 -0
- package/dist/accent-colors.js +122 -0
- package/dist/action-ledger.d.ts +26 -0
- package/dist/action-ledger.js +75 -0
- package/dist/agent-fence.d.ts +42 -0
- package/dist/agent-fence.js +183 -0
- package/dist/agentconfig.d.ts +216 -0
- package/dist/agentconfig.js +454 -0
- package/dist/api-tokens.d.ts +16 -0
- package/dist/api-tokens.js +27 -0
- package/dist/approval-policy.d.ts +69 -0
- package/dist/approval-policy.js +137 -0
- package/dist/approval-rules-ui.d.ts +25 -0
- package/dist/approval-rules-ui.js +39 -0
- package/dist/assignment-adapters.d.ts +180 -0
- package/dist/assignment-adapters.js +239 -0
- package/dist/assignment-brief.d.ts +71 -0
- package/dist/assignment-brief.js +129 -0
- package/dist/assignment-delivery.d.ts +56 -0
- package/dist/assignment-delivery.js +160 -0
- package/dist/assignment-presentation.d.ts +26 -0
- package/dist/assignment-presentation.js +80 -0
- package/dist/assignment-status.d.ts +62 -0
- package/dist/assignment-status.js +152 -0
- package/dist/assignment-ui.d.ts +71 -0
- package/dist/assignment-ui.js +103 -0
- package/dist/assignment.d.ts +222 -0
- package/dist/assignment.js +399 -0
- package/dist/attest.d.ts +56 -0
- package/dist/attest.js +153 -0
- package/dist/backend.d.ts +97 -0
- package/dist/backend.js +166 -0
- package/dist/backup-ui.d.ts +22 -0
- package/dist/backup-ui.js +59 -0
- package/dist/backup.d.ts +84 -0
- package/dist/backup.js +421 -0
- package/dist/beads.d.ts +34 -0
- package/dist/beads.js +135 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +22 -0
- package/dist/board.d.ts +150 -0
- package/dist/board.js +210 -0
- package/dist/boot-identity.d.ts +63 -0
- package/dist/boot-identity.js +99 -0
- package/dist/browser/THIRD_PARTY_NOTICES.txt +2295 -0
- package/dist/browser/workspace.css +4 -0
- package/dist/browser/workspace.js +225 -0
- package/dist/browser-crew.d.ts +11 -0
- package/dist/browser-crew.js +50 -0
- package/dist/browser-shell.d.ts +8 -0
- package/dist/browser-shell.js +60 -0
- package/dist/browser-workspace.d.ts +780 -0
- package/dist/browser-workspace.js +47 -0
- package/dist/budget-alerts.d.ts +19 -0
- package/dist/budget-alerts.js +46 -0
- package/dist/builder.d.ts +370 -0
- package/dist/builder.js +3262 -0
- package/dist/capscan.d.ts +35 -0
- package/dist/capscan.js +113 -0
- package/dist/chat-acceptance.d.ts +71 -0
- package/dist/chat-acceptance.js +120 -0
- package/dist/chat-actions.d.ts +265 -0
- package/dist/chat-actions.js +1273 -0
- package/dist/chat-channel.d.ts +109 -0
- package/dist/chat-channel.js +652 -0
- package/dist/chat-continuity.d.ts +32 -0
- package/dist/chat-continuity.js +317 -0
- package/dist/chat-controls.d.ts +113 -0
- package/dist/chat-controls.js +53 -0
- package/dist/chat-delivery-state.d.ts +159 -0
- package/dist/chat-delivery-state.js +309 -0
- package/dist/chat-delivery.d.ts +38 -0
- package/dist/chat-delivery.js +693 -0
- package/dist/chat-display.d.ts +1 -0
- package/dist/chat-display.js +18 -0
- package/dist/chat-evidence.d.ts +123 -0
- package/dist/chat-evidence.js +207 -0
- package/dist/chat-flow.d.ts +44 -0
- package/dist/chat-flow.js +215 -0
- package/dist/chat-inbox.d.ts +57 -0
- package/dist/chat-inbox.js +123 -0
- package/dist/chat-polish.d.ts +16 -0
- package/dist/chat-polish.js +135 -0
- package/dist/chat-review.d.ts +46 -0
- package/dist/chat-review.js +98 -0
- package/dist/chat-rooms.d.ts +123 -0
- package/dist/chat-rooms.js +216 -0
- package/dist/chat-task-actions.d.ts +44 -0
- package/dist/chat-task-actions.js +79 -0
- package/dist/check-progress.d.ts +51 -0
- package/dist/check-progress.js +290 -0
- package/dist/child-database.d.ts +12 -0
- package/dist/child-database.js +30 -0
- package/dist/claim.d.ts +688 -0
- package/dist/claim.js +1740 -0
- package/dist/cli.d.ts +137 -0
- package/dist/cli.js +1461 -0
- package/dist/codex-limits.d.ts +15 -0
- package/dist/codex-limits.js +145 -0
- package/dist/coding-context.d.ts +55 -0
- package/dist/coding-context.js +92 -0
- package/dist/coding-handoff.d.ts +57 -0
- package/dist/coding-handoff.js +277 -0
- package/dist/coding-provider.d.ts +72 -0
- package/dist/coding-provider.js +424 -0
- package/dist/coding-shipping-ui.d.ts +4 -0
- package/dist/coding-shipping-ui.js +12 -0
- package/dist/coding-types.d.ts +62 -0
- package/dist/coding-types.js +1 -0
- package/dist/coding-ui.d.ts +17 -0
- package/dist/coding-ui.js +364 -0
- package/dist/coding-update.d.ts +15 -0
- package/dist/coding-update.js +149 -0
- package/dist/coding-workspace.d.ts +124 -0
- package/dist/coding-workspace.js +768 -0
- package/dist/container-state.d.ts +5 -0
- package/dist/container-state.js +62 -0
- package/dist/containment.d.ts +197 -0
- package/dist/containment.js +559 -0
- package/dist/contest.d.ts +318 -0
- package/dist/contest.js +753 -0
- package/dist/control-setup.d.ts +35 -0
- package/dist/control-setup.js +40 -0
- package/dist/control-ui.d.ts +29 -0
- package/dist/control-ui.js +53 -0
- package/dist/controller-service.d.ts +1 -0
- package/dist/controller-service.js +19 -0
- package/dist/controller-supervisor.d.ts +30 -0
- package/dist/controller-supervisor.js +104 -0
- package/dist/converse.d.ts +304 -0
- package/dist/converse.js +849 -0
- package/dist/coordinator-proposals.d.ts +29 -0
- package/dist/coordinator-proposals.js +137 -0
- package/dist/coordinator.d.ts +186 -0
- package/dist/coordinator.js +447 -0
- package/dist/credentials-ui.d.ts +25 -0
- package/dist/credentials-ui.js +48 -0
- package/dist/daemon.d.ts +209 -0
- package/dist/daemon.js +604 -0
- package/dist/decision.d.ts +120 -0
- package/dist/decision.js +388 -0
- package/dist/demo.d.ts +55 -0
- package/dist/demo.js +992 -0
- package/dist/desktop-access.d.ts +28 -0
- package/dist/desktop-access.js +88 -0
- package/dist/desktop-bundle.d.ts +16 -0
- package/dist/desktop-bundle.js +74 -0
- package/dist/desktop-host.d.ts +54 -0
- package/dist/desktop-host.js +508 -0
- package/dist/desktop-update-gate.d.ts +12 -0
- package/dist/desktop-update-gate.js +91 -0
- package/dist/desktop-update-recovery.d.ts +14 -0
- package/dist/desktop-update-recovery.js +243 -0
- package/dist/desktop-update.d.ts +110 -0
- package/dist/desktop-update.js +740 -0
- package/dist/discord-api.d.ts +20 -0
- package/dist/discord-api.js +138 -0
- package/dist/discord-chat.d.ts +16 -0
- package/dist/discord-chat.js +381 -0
- package/dist/discord-settings.d.ts +7 -0
- package/dist/discord-settings.js +75 -0
- package/dist/discord.d.ts +10 -0
- package/dist/discord.js +169 -0
- package/dist/discover.d.ts +75 -0
- package/dist/discover.js +150 -0
- package/dist/dispatch.d.ts +100 -0
- package/dist/dispatch.js +311 -0
- package/dist/dispose.d.ts +161 -0
- package/dist/dispose.js +803 -0
- package/dist/email-settings.d.ts +34 -0
- package/dist/email-settings.js +50 -0
- package/dist/envelope.d.ts +47 -0
- package/dist/envelope.js +105 -0
- package/dist/evidence-pack.d.ts +185 -0
- package/dist/evidence-pack.js +258 -0
- package/dist/evidence.d.ts +345 -0
- package/dist/evidence.js +782 -0
- package/dist/exec.d.ts +285 -0
- package/dist/exec.js +1841 -0
- package/dist/exhaustion.d.ts +85 -0
- package/dist/exhaustion.js +141 -0
- package/dist/export-ui.d.ts +4 -0
- package/dist/export-ui.js +17 -0
- package/dist/export.d.ts +48 -0
- package/dist/export.js +305 -0
- package/dist/flow-actions.d.ts +77 -0
- package/dist/flow-actions.js +203 -0
- package/dist/flow-code.d.ts +50 -0
- package/dist/flow-code.js +159 -0
- package/dist/flow-draft.d.ts +38 -0
- package/dist/flow-draft.js +80 -0
- package/dist/flow-engine.d.ts +66 -0
- package/dist/flow-engine.js +416 -0
- package/dist/flow-insights.d.ts +95 -0
- package/dist/flow-insights.js +149 -0
- package/dist/flow-live.d.ts +24 -0
- package/dist/flow-live.js +150 -0
- package/dist/flow-people.d.ts +24 -0
- package/dist/flow-people.js +91 -0
- package/dist/flow-replies.d.ts +48 -0
- package/dist/flow-replies.js +113 -0
- package/dist/flow-scripts.d.ts +38 -0
- package/dist/flow-scripts.js +73 -0
- package/dist/flow-secrets.d.ts +13 -0
- package/dist/flow-secrets.js +45 -0
- package/dist/flow-sort.d.ts +82 -0
- package/dist/flow-sort.js +153 -0
- package/dist/flow-steps.d.ts +40 -0
- package/dist/flow-steps.js +408 -0
- package/dist/flow-triggers.d.ts +244 -0
- package/dist/flow-triggers.js +959 -0
- package/dist/flows-ui.d.ts +20 -0
- package/dist/flows-ui.js +175 -0
- package/dist/flows.d.ts +229 -0
- package/dist/flows.js +968 -0
- package/dist/fonts.d.ts +19 -0
- package/dist/fonts.js +19 -0
- package/dist/gaps.d.ts +35 -0
- package/dist/gaps.js +101 -0
- package/dist/gate-failure.d.ts +40 -0
- package/dist/gate-failure.js +66 -0
- package/dist/git.d.ts +54 -0
- package/dist/git.js +94 -0
- package/dist/google-mail.d.ts +58 -0
- package/dist/google-mail.js +151 -0
- package/dist/grant.d.ts +136 -0
- package/dist/grant.js +238 -0
- package/dist/graph.d.ts +164 -0
- package/dist/graph.js +383 -0
- package/dist/guides.d.ts +22 -0
- package/dist/guides.js +363 -0
- package/dist/held.d.ts +150 -0
- package/dist/held.js +799 -0
- package/dist/invoke.d.ts +112 -0
- package/dist/invoke.js +780 -0
- package/dist/issues.d.ts +41 -0
- package/dist/issues.js +131 -0
- package/dist/job-object-helper.ps1 +252 -0
- package/dist/jsonl-discriminants.d.ts +11 -0
- package/dist/jsonl-discriminants.js +93 -0
- package/dist/keys.d.ts +104 -0
- package/dist/keys.js +229 -0
- package/dist/kits-ui.d.ts +17 -0
- package/dist/kits-ui.js +46 -0
- package/dist/kits.d.ts +86 -0
- package/dist/kits.js +215 -0
- package/dist/knowledge-cli.d.ts +101 -0
- package/dist/knowledge-cli.js +92 -0
- package/dist/knowledge-ui.d.ts +24 -0
- package/dist/knowledge-ui.js +40 -0
- package/dist/lead-context.d.ts +8 -0
- package/dist/lead-context.js +56 -0
- package/dist/lead-follow.d.ts +22 -0
- package/dist/lead-follow.js +167 -0
- package/dist/lead-status.d.ts +75 -0
- package/dist/lead-status.js +285 -0
- package/dist/ledger-chain.d.ts +46 -0
- package/dist/ledger-chain.js +187 -0
- package/dist/ledger-csv.d.ts +4 -0
- package/dist/ledger-csv.js +11 -0
- package/dist/ledger-view.d.ts +24 -0
- package/dist/ledger-view.js +58 -0
- package/dist/limits-ui.d.ts +26 -0
- package/dist/limits-ui.js +67 -0
- package/dist/link.d.ts +72 -0
- package/dist/link.js +217 -0
- package/dist/live.d.ts +96 -0
- package/dist/live.js +366 -0
- package/dist/liveness.d.ts +30 -0
- package/dist/liveness.js +42 -0
- package/dist/log.d.ts +7 -0
- package/dist/log.js +25 -0
- package/dist/mailbox.d.ts +57 -0
- package/dist/mailbox.js +150 -0
- package/dist/maintenance.d.ts +11 -0
- package/dist/maintenance.js +35 -0
- package/dist/mate-cli.d.ts +52 -0
- package/dist/mate-cli.js +345 -0
- package/dist/mate-contract.d.ts +10 -0
- package/dist/mate-contract.js +30 -0
- package/dist/mate-doors.d.ts +69 -0
- package/dist/mate-doors.js +548 -0
- package/dist/mate-progress.d.ts +29 -0
- package/dist/mate-progress.js +121 -0
- package/dist/mate-tools.d.ts +94 -0
- package/dist/mate-tools.js +1878 -0
- package/dist/mate.d.ts +92 -0
- package/dist/mate.js +435 -0
- package/dist/mcp-connect.d.ts +103 -0
- package/dist/mcp-connect.js +252 -0
- package/dist/mcp.d.ts +27 -0
- package/dist/mcp.js +651 -0
- package/dist/memory-cli.d.ts +466 -0
- package/dist/memory-cli.js +111 -0
- package/dist/memory-pass.d.ts +160 -0
- package/dist/memory-pass.js +409 -0
- package/dist/metrics.d.ts +3 -0
- package/dist/metrics.js +56 -0
- package/dist/mobile-viewport.d.ts +3 -0
- package/dist/mobile-viewport.js +37 -0
- package/dist/model-catalog.d.ts +118 -0
- package/dist/model-catalog.js +376 -0
- package/dist/models-cli.d.ts +102 -0
- package/dist/models-cli.js +66 -0
- package/dist/models-ui.d.ts +34 -0
- package/dist/models-ui.js +68 -0
- package/dist/modes.d.ts +80 -0
- package/dist/modes.js +182 -0
- package/dist/monitoring-settings.d.ts +44 -0
- package/dist/monitoring-settings.js +173 -0
- package/dist/monitoring-ui.d.ts +20 -0
- package/dist/monitoring-ui.js +51 -0
- package/dist/monitoring.d.ts +34 -0
- package/dist/monitoring.js +221 -0
- package/dist/names.d.ts +67 -0
- package/dist/names.js +102 -0
- package/dist/observations.d.ts +40 -0
- package/dist/observations.js +211 -0
- package/dist/oidc.d.ts +71 -0
- package/dist/oidc.js +182 -0
- package/dist/onboard.d.ts +108 -0
- package/dist/onboard.js +325 -0
- package/dist/openrouter-models.d.ts +30 -0
- package/dist/openrouter-models.js +120 -0
- package/dist/operate.d.ts +118 -0
- package/dist/operate.js +11321 -0
- package/dist/peek-cli.d.ts +97 -0
- package/dist/peek-cli.js +215 -0
- package/dist/peek.d.ts +134 -0
- package/dist/peek.js +578 -0
- package/dist/phase-routing.d.ts +307 -0
- package/dist/phase-routing.js +658 -0
- package/dist/plan-auto.d.ts +14 -0
- package/dist/plan-auto.js +92 -0
- package/dist/plan.d.ts +186 -0
- package/dist/plan.js +401 -0
- package/dist/planner-source.d.ts +251 -0
- package/dist/planner-source.js +460 -0
- package/dist/planner.d.ts +75 -0
- package/dist/planner.js +992 -0
- package/dist/policy-ui.d.ts +20 -0
- package/dist/policy-ui.js +41 -0
- package/dist/policy.d.ts +111 -0
- package/dist/policy.js +231 -0
- package/dist/prepared-evidence.d.ts +16 -0
- package/dist/prepared-evidence.js +96 -0
- package/dist/pricing.d.ts +45 -0
- package/dist/pricing.js +77 -0
- package/dist/principal.d.ts +42 -0
- package/dist/principal.js +82 -0
- package/dist/probe.d.ts +46 -0
- package/dist/probe.js +79 -0
- package/dist/process-custody.d.ts +17 -0
- package/dist/process-custody.js +91 -0
- package/dist/process-liveness.d.ts +10 -0
- package/dist/process-liveness.js +81 -0
- package/dist/process-recovery-anchor.d.ts +75 -0
- package/dist/process-recovery-anchor.js +188 -0
- package/dist/process-recovery-coalition.d.ts +25 -0
- package/dist/process-recovery-coalition.js +105 -0
- package/dist/process-recovery-eligibility.d.ts +178 -0
- package/dist/process-recovery-eligibility.js +170 -0
- package/dist/process-recovery-native.d.ts +172 -0
- package/dist/process-recovery-native.js +592 -0
- package/dist/process-recovery-provenance.d.ts +163 -0
- package/dist/process-recovery-provenance.js +292 -0
- package/dist/process-recovery-services.d.ts +56 -0
- package/dist/process-recovery-services.js +191 -0
- package/dist/process-recovery-settlement.d.ts +32 -0
- package/dist/process-recovery-settlement.js +132 -0
- package/dist/process-recovery.d.ts +41 -0
- package/dist/process-recovery.js +91 -0
- package/dist/process-tree.d.ts +41 -0
- package/dist/process-tree.js +264 -0
- package/dist/project-access.d.ts +14 -0
- package/dist/project-access.js +34 -0
- package/dist/project-cli.d.ts +18 -0
- package/dist/project-cli.js +104 -0
- package/dist/project-delete-ui.d.ts +22 -0
- package/dist/project-delete-ui.js +48 -0
- package/dist/project-delete.d.ts +55 -0
- package/dist/project-delete.js +413 -0
- package/dist/project-knowledge.d.ts +111 -0
- package/dist/project-knowledge.js +241 -0
- package/dist/project-learning.d.ts +100 -0
- package/dist/project-learning.js +438 -0
- package/dist/project-memory.d.ts +81 -0
- package/dist/project-memory.js +264 -0
- package/dist/project-skills.d.ts +152 -0
- package/dist/project-skills.js +660 -0
- package/dist/project-tools.d.ts +219 -0
- package/dist/project-tools.js +796 -0
- package/dist/project.d.ts +61 -0
- package/dist/project.js +125 -0
- package/dist/prompt.d.ts +22 -0
- package/dist/prompt.js +72 -0
- package/dist/proof.d.ts +513 -0
- package/dist/proof.js +1140 -0
- package/dist/proposal.d.ts +82 -0
- package/dist/proposal.js +210 -0
- package/dist/provider-connection.d.ts +22 -0
- package/dist/provider-connection.js +98 -0
- package/dist/provider-limits.d.ts +35 -0
- package/dist/provider-limits.js +95 -0
- package/dist/provider.d.ts +279 -0
- package/dist/provider.js +935 -0
- package/dist/publish.d.ts +107 -0
- package/dist/publish.js +650 -0
- package/dist/pulls.d.ts +118 -0
- package/dist/pulls.js +240 -0
- package/dist/push.d.ts +95 -0
- package/dist/push.js +353 -0
- package/dist/quality.d.ts +8 -0
- package/dist/quality.js +6 -0
- package/dist/recipe-ui.d.ts +18 -0
- package/dist/recipe-ui.js +152 -0
- package/dist/recipes.d.ts +81 -0
- package/dist/recipes.js +332 -0
- package/dist/remote.d.ts +67 -0
- package/dist/remote.js +122 -0
- package/dist/render.d.ts +64 -0
- package/dist/render.js +587 -0
- package/dist/report-summary.d.ts +13 -0
- package/dist/report-summary.js +15 -0
- package/dist/repos.d.ts +65 -0
- package/dist/repos.js +277 -0
- package/dist/repository-context-ui.d.ts +2 -0
- package/dist/repository-context-ui.js +10 -0
- package/dist/repository-context.d.ts +67 -0
- package/dist/repository-context.js +376 -0
- package/dist/restart-certification.d.ts +134 -0
- package/dist/restart-certification.js +226 -0
- package/dist/result-actions.d.ts +28 -0
- package/dist/result-actions.js +270 -0
- package/dist/result-completion.d.ts +18 -0
- package/dist/result-completion.js +66 -0
- package/dist/result-review.d.ts +213 -0
- package/dist/result-review.js +430 -0
- package/dist/retention-ui.d.ts +12 -0
- package/dist/retention-ui.js +39 -0
- package/dist/retention.d.ts +72 -0
- package/dist/retention.js +288 -0
- package/dist/review-context.d.ts +243 -0
- package/dist/review-context.js +1043 -0
- package/dist/review-evidence.d.ts +33 -0
- package/dist/review-evidence.js +56 -0
- package/dist/reviewer.d.ts +97 -0
- package/dist/reviewer.js +202 -0
- package/dist/routine.d.ts +228 -0
- package/dist/routine.js +764 -0
- package/dist/runner.d.ts +266 -0
- package/dist/runner.js +399 -0
- package/dist/scan.d.ts +32 -0
- package/dist/scan.js +92 -0
- package/dist/scope.d.ts +695 -0
- package/dist/scope.js +1176 -0
- package/dist/scout-report.d.ts +42 -0
- package/dist/scout-report.js +108 -0
- package/dist/scout.d.ts +69 -0
- package/dist/scout.js +351 -0
- package/dist/serve.d.ts +313 -0
- package/dist/serve.js +21776 -0
- package/dist/session-brief.d.ts +8 -0
- package/dist/session-brief.js +24 -0
- package/dist/session-cli.d.ts +13 -0
- package/dist/session-cli.js +284 -0
- package/dist/session-contract.d.ts +183 -0
- package/dist/session-contract.js +122 -0
- package/dist/session-http.d.ts +19 -0
- package/dist/session-http.js +154 -0
- package/dist/session-server.d.ts +12 -0
- package/dist/session-server.js +37 -0
- package/dist/session-service.d.ts +22 -0
- package/dist/session-service.js +157 -0
- package/dist/setup-guide.d.ts +39 -0
- package/dist/setup-guide.js +93 -0
- package/dist/sign-in-guard.d.ts +45 -0
- package/dist/sign-in-guard.js +74 -0
- package/dist/skills-ui.d.ts +11 -0
- package/dist/skills-ui.js +58 -0
- package/dist/skills.d.ts +97 -0
- package/dist/skills.js +276 -0
- package/dist/slack-api.d.ts +55 -0
- package/dist/slack-api.js +229 -0
- package/dist/slack-chat.d.ts +28 -0
- package/dist/slack-chat.js +413 -0
- package/dist/slack-settings.d.ts +7 -0
- package/dist/slack-settings.js +75 -0
- package/dist/slack-state.d.ts +13 -0
- package/dist/slack-state.js +9 -0
- package/dist/slack.d.ts +13 -0
- package/dist/slack.js +167 -0
- package/dist/spend-ui.d.ts +28 -0
- package/dist/spend-ui.js +95 -0
- package/dist/spend.d.ts +142 -0
- package/dist/spend.js +313 -0
- package/dist/sqlite-runtime.d.ts +3 -0
- package/dist/sqlite-runtime.js +6 -0
- package/dist/sso-settings.d.ts +33 -0
- package/dist/sso-settings.js +103 -0
- package/dist/sso-ui.d.ts +22 -0
- package/dist/sso-ui.js +39 -0
- package/dist/storage.d.ts +15 -0
- package/dist/storage.js +84 -0
- package/dist/store.d.ts +7754 -0
- package/dist/store.js +23279 -0
- package/dist/structured-output.d.ts +58 -0
- package/dist/structured-output.js +103 -0
- package/dist/style-asset.d.ts +7 -0
- package/dist/style-asset.js +41 -0
- package/dist/subscription-chat.d.ts +46 -0
- package/dist/subscription-chat.js +275 -0
- package/dist/summary.d.ts +33 -0
- package/dist/summary.js +71 -0
- package/dist/supervisor.mjs +297 -0
- package/dist/surface.d.ts +63 -0
- package/dist/surface.js +352 -0
- package/dist/sync.d.ts +94 -0
- package/dist/sync.js +279 -0
- package/dist/task-composer.d.ts +13 -0
- package/dist/task-composer.js +52 -0
- package/dist/task-control.d.ts +165 -0
- package/dist/task-control.js +214 -0
- package/dist/task-outcome-cli.d.ts +10 -0
- package/dist/task-outcome-cli.js +92 -0
- package/dist/task-text.d.ts +30 -0
- package/dist/task-text.js +41 -0
- package/dist/team-cli.d.ts +15 -0
- package/dist/team-cli.js +618 -0
- package/dist/team-contract.d.ts +92 -0
- package/dist/team-contract.js +1 -0
- package/dist/team-http.d.ts +17 -0
- package/dist/team-http.js +144 -0
- package/dist/team-leads.d.ts +81 -0
- package/dist/team-leads.js +565 -0
- package/dist/team-runtime.d.ts +27 -0
- package/dist/team-runtime.js +264 -0
- package/dist/team-ui.d.ts +3 -0
- package/dist/team-ui.js +9 -0
- package/dist/team-updates.d.ts +8 -0
- package/dist/team-updates.js +57 -0
- package/dist/teammate-admin.d.ts +53 -0
- package/dist/teammate-admin.js +111 -0
- package/dist/teammate-desk.d.ts +60 -0
- package/dist/teammate-desk.js +154 -0
- package/dist/teammate-memory.d.ts +64 -0
- package/dist/teammate-memory.js +196 -0
- package/dist/teammate-question.d.ts +59 -0
- package/dist/teammate-question.js +175 -0
- package/dist/teammate-tools.d.ts +108 -0
- package/dist/teammate-tools.js +271 -0
- package/dist/teammate-week.d.ts +68 -0
- package/dist/teammate-week.js +132 -0
- package/dist/teammate-work.d.ts +76 -0
- package/dist/teammate-work.js +280 -0
- package/dist/teammates-ui.d.ts +20 -0
- package/dist/teammates-ui.js +166 -0
- package/dist/teammates.d.ts +180 -0
- package/dist/teammates.js +253 -0
- package/dist/teams-api.d.ts +33 -0
- package/dist/teams-api.js +204 -0
- package/dist/teams-chat.d.ts +26 -0
- package/dist/teams-chat.js +215 -0
- package/dist/teams-settings.d.ts +12 -0
- package/dist/teams-settings.js +57 -0
- package/dist/teams.d.ts +21 -0
- package/dist/teams.js +124 -0
- package/dist/telegram-flow.d.ts +49 -0
- package/dist/telegram-flow.js +110 -0
- package/dist/telegram-mate.d.ts +137 -0
- package/dist/telegram-mate.js +707 -0
- package/dist/telegram-progress.d.ts +14 -0
- package/dist/telegram-progress.js +129 -0
- package/dist/telegram-settings.d.ts +9 -0
- package/dist/telegram-settings.js +36 -0
- package/dist/telegram-status.d.ts +75 -0
- package/dist/telegram-status.js +265 -0
- package/dist/telegram-team.d.ts +85 -0
- package/dist/telegram-team.js +359 -0
- package/dist/telegram.d.ts +230 -0
- package/dist/telegram.js +1931 -0
- package/dist/templates.d.ts +65 -0
- package/dist/templates.js +118 -0
- package/dist/tool-launcher.d.ts +1 -0
- package/dist/tool-launcher.js +101 -0
- package/dist/tools-ui.d.ts +30 -0
- package/dist/tools-ui.js +40 -0
- package/dist/transitions-recipes.d.ts +1 -0
- package/dist/transitions-recipes.js +203 -0
- package/dist/tree-proof.d.ts +34 -0
- package/dist/tree-proof.js +58 -0
- package/dist/verification-evidence.d.ts +44 -0
- package/dist/verification-evidence.js +238 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +10 -0
- package/dist/webhooks.d.ts +91 -0
- package/dist/webhooks.js +283 -0
- package/dist/work-index.d.ts +68 -0
- package/dist/work-index.js +384 -0
- package/dist/work-summary.d.ts +57 -0
- package/dist/work-summary.js +116 -0
- package/dist/workspace-motion.d.ts +4 -0
- package/dist/workspace-motion.js +133 -0
- package/dist/workspace-revision.d.ts +19 -0
- package/dist/workspace-revision.js +92 -0
- package/dist/workspace-ui.d.ts +204 -0
- package/dist/workspace-ui.js +411 -0
- package/dist/worktree-notices.d.ts +13 -0
- package/dist/worktree-notices.js +17 -0
- package/dist/worktree.d.ts +249 -0
- package/dist/worktree.js +720 -0
- package/package.json +122 -0
- package/scripts/canary-assertions.mjs +42 -0
- package/scripts/crash-canary.mjs +253 -0
- package/scripts/fixtures/crash-process.mjs +74 -0
- package/scripts/fixtures/pilot-scenarios.mjs +85 -0
- package/scripts/fixtures/restart-service.mjs +27 -0
- package/scripts/launchd-certification.mjs +143 -0
- package/scripts/pilot.mjs +121 -0
- package/scripts/proof-preflight.mjs +140 -0
- package/scripts/provider-canary.mjs +379 -0
- package/scripts/recovery-canary.mjs +135 -0
- package/scripts/restart-certification.mjs +98 -0
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A machine that may be given work.
|
|
3
|
+
*
|
|
4
|
+
* Auth is here from the first commit, and that is a deliberate ordering rather
|
|
5
|
+
* than diligence for its own sake. A control plane that dispatches work to
|
|
6
|
+
* unauthenticated runners for six months grows six months of code that assumes
|
|
7
|
+
* the caller is whoever it says it is, and retrofitting identity into that is
|
|
8
|
+
* how the retrofit gets a hole in it. It costs almost nothing to do now.
|
|
9
|
+
*
|
|
10
|
+
* **The token is never stored.** Registration mints one, prints it once, and
|
|
11
|
+
* keeps only a hash. There is no command that recovers it, because a control
|
|
12
|
+
* plane able to hand back a runner's credential is one whose database is worth
|
|
13
|
+
* stealing — and because the same rule governs secrets everywhere else in this
|
|
14
|
+
* design: the control plane holds metadata, the runner holds values.
|
|
15
|
+
*
|
|
16
|
+
* Liveness is separate from a lease expiring, and the distinction matters at
|
|
17
|
+
* 3am. A lease expires because time passed; a runner is dead because it
|
|
18
|
+
* stopped saying otherwise. Reaping on the first tells you a task is free
|
|
19
|
+
* again. Reaping on the second tells you a *machine* is gone, and everything
|
|
20
|
+
* it was holding — claims and worktrees alike — needs recovering together.
|
|
21
|
+
*/
|
|
22
|
+
import type { Store, Mutation } from "./store.js";
|
|
23
|
+
import { type ProviderId } from "./provider.js";
|
|
24
|
+
import type { ReadinessObservation } from "./phase-routing.js";
|
|
25
|
+
/** A runner's repo binding, in the same canonical form task filing uses —
|
|
26
|
+
* the claim gate compares these by equality, so both sides must resolve
|
|
27
|
+
* through one convention (MCP gateway spec v6, the runner tuple). */
|
|
28
|
+
export declare function canonicalRepos(repos: readonly string[]): string[];
|
|
29
|
+
export type Runner = {
|
|
30
|
+
name: string;
|
|
31
|
+
host: string;
|
|
32
|
+
/** How many tasks it will hold at once. */
|
|
33
|
+
capacity: number;
|
|
34
|
+
repos: string[];
|
|
35
|
+
agents: string[];
|
|
36
|
+
registeredAt: string;
|
|
37
|
+
heartbeatAt: string;
|
|
38
|
+
retiredAt: string | null;
|
|
39
|
+
/** The queue column's theme note (v19) — operator prose, display only. */
|
|
40
|
+
queueNote?: string | null;
|
|
41
|
+
};
|
|
42
|
+
export type Registration = {
|
|
43
|
+
runner: Runner;
|
|
44
|
+
token: string;
|
|
45
|
+
/** What the previous holder of this name was still holding, taken back. */
|
|
46
|
+
reclaimed: Reclaim | null;
|
|
47
|
+
};
|
|
48
|
+
export type AuthResult = {
|
|
49
|
+
ok: true;
|
|
50
|
+
runner: Runner;
|
|
51
|
+
} | {
|
|
52
|
+
ok: false;
|
|
53
|
+
reason: "unknown" | "bad-token" | "retired" | "expired";
|
|
54
|
+
};
|
|
55
|
+
/** v101: a runner's token lasts a year from its registration; registering again (every start does) renews it. */
|
|
56
|
+
export declare const RUNNER_TOKEN_MS: number;
|
|
57
|
+
/**
|
|
58
|
+
* How long a runner may go quiet before it is presumed gone.
|
|
59
|
+
*
|
|
60
|
+
* Comfortably more than any sensible heartbeat interval: declaring a busy
|
|
61
|
+
* machine dead because one check-in was slow costs more than waiting another
|
|
62
|
+
* minute for a machine that really is.
|
|
63
|
+
*/
|
|
64
|
+
export declare const DEFAULT_LIVENESS_MS: number;
|
|
65
|
+
export type RegisterOptions = {
|
|
66
|
+
name: string;
|
|
67
|
+
host: string;
|
|
68
|
+
capacity?: number;
|
|
69
|
+
repos?: readonly string[];
|
|
70
|
+
agents?: readonly string[];
|
|
71
|
+
now: Date;
|
|
72
|
+
/** Injected so a test can assert on a token it chose. */
|
|
73
|
+
newToken?: () => string;
|
|
74
|
+
mutation?: Mutation;
|
|
75
|
+
};
|
|
76
|
+
export declare function register(store: Store, options: RegisterOptions): Registration;
|
|
77
|
+
/**
|
|
78
|
+
* Whether this caller is who it says it is.
|
|
79
|
+
*
|
|
80
|
+
* Compared in constant time. The comparison is between hashes rather than
|
|
81
|
+
* secrets, so the practical risk is slight — but a timing-variable equality on
|
|
82
|
+
* a credential path is the kind of thing that gets copied into somewhere it
|
|
83
|
+
* matters, and there is no reason to leave the example lying around.
|
|
84
|
+
*/
|
|
85
|
+
export declare function authenticate(store: Store, name: string, token: string): AuthResult;
|
|
86
|
+
/**
|
|
87
|
+
* "I am still here." Authenticated AND ATOMIC (arc 2 findings 25/33): the
|
|
88
|
+
* hash is verified and the heartbeat stamped in one write transaction, so
|
|
89
|
+
* a takeover that rotates the credential between the check and the touch
|
|
90
|
+
* cannot receive a stale incarnation's pulse — after rotation commits, the
|
|
91
|
+
* very next heartbeat refuses, and the caller must treat that as fatal.
|
|
92
|
+
*/
|
|
93
|
+
export declare function heartbeat(store: Store, name: string, token: string, now: Date): AuthResult;
|
|
94
|
+
/**
|
|
95
|
+
* Extend a live runner's project binding using that runner's own credential.
|
|
96
|
+
*
|
|
97
|
+
* `up` owns both the credential and the registry watcher, so a project the
|
|
98
|
+
* operator admitted through its console can join without a second operator
|
|
99
|
+
* ceremony or a restart. Authentication and the additive write share one
|
|
100
|
+
* transaction: a replaced process cannot widen its successor's binding.
|
|
101
|
+
*/
|
|
102
|
+
export declare function addRunnerReposAuthed(store: Store, args: {
|
|
103
|
+
name: string;
|
|
104
|
+
token: string;
|
|
105
|
+
repos: readonly string[];
|
|
106
|
+
}, now: Date): AuthResult;
|
|
107
|
+
/**
|
|
108
|
+
* The one runner-name rule (arc 2 finding 13): nonempty, control-free, at
|
|
109
|
+
* most 60 characters — the console form's rule, now shared by every door.
|
|
110
|
+
*/
|
|
111
|
+
export declare const RUNNER_NAME_MAX = 60;
|
|
112
|
+
/** The most tasks one worker may be set to run at once (the console's register form says the same). */
|
|
113
|
+
export declare const RUNNER_CAPACITY_MAX = 64;
|
|
114
|
+
/** A capacity as typed: a whole number from 1 to RUNNER_CAPACITY_MAX, or null. */
|
|
115
|
+
export declare function parseRunnerCapacity(value: string): number | null;
|
|
116
|
+
export declare function validRunnerName(name: string): boolean;
|
|
117
|
+
/** A generated base name: lowercase [a-z0-9-], collapsed, trimmed. The
|
|
118
|
+
* suffix budget (finding 24) is the CALLER's arithmetic — truncate the
|
|
119
|
+
* base to `RUNNER_NAME_MAX - suffix.length` before appending. */
|
|
120
|
+
export declare function normalizeRunnerName(raw: string): string;
|
|
121
|
+
/**
|
|
122
|
+
* The atomic take-or-refuse door (arc 2 findings 1/15/16/26/31): ONE write
|
|
123
|
+
* transaction that proves the name is free — absent, retired, or dead by
|
|
124
|
+
* the same predicate recovery uses, with NO unexpired watch lease — then
|
|
125
|
+
* finishes every open run the dead holder left (requeueing what no newer
|
|
126
|
+
* live claim owns), reclaims, and rotates. Two racers serialize: one wins,
|
|
127
|
+
* the other gets the typed refusal.
|
|
128
|
+
*/
|
|
129
|
+
export declare function registerRunnerIfIdle(store: Store, options: RegisterOptions): (Registration & {
|
|
130
|
+
ok: true;
|
|
131
|
+
recoveredRuns: number;
|
|
132
|
+
}) | {
|
|
133
|
+
ok: false;
|
|
134
|
+
reason: "runner-alive" | "watch-live";
|
|
135
|
+
detail: string;
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* The authenticated lease door (arc 2 findings 15/26): credential verified
|
|
139
|
+
* and lease acquired/renewed in the SAME transaction — after a takeover
|
|
140
|
+
* rotates the hash, a stale incarnation can neither acquire nor renew. A
|
|
141
|
+
* takeover of an expired lease recovers the superseded incarnation INSIDE
|
|
142
|
+
* this transaction, so no crash can separate "B owns the lease" from "A
|
|
143
|
+
* was recovered".
|
|
144
|
+
*/
|
|
145
|
+
export declare function acquireWatchLeaseAuthed(store: Store, args: {
|
|
146
|
+
runner: string;
|
|
147
|
+
token: string;
|
|
148
|
+
repo: string;
|
|
149
|
+
owner: string;
|
|
150
|
+
ttlMs: number;
|
|
151
|
+
}, now: Date): {
|
|
152
|
+
ok: true;
|
|
153
|
+
generation: number;
|
|
154
|
+
superseded: string | null;
|
|
155
|
+
recovered: number;
|
|
156
|
+
} | {
|
|
157
|
+
ok: false;
|
|
158
|
+
reason: "unknown" | "bad-token" | "retired" | "expired" | "watch-busy" | "unauthorized-repo";
|
|
159
|
+
holder?: string;
|
|
160
|
+
until?: string;
|
|
161
|
+
};
|
|
162
|
+
/** The renewal, equally authenticated: a false answer is FATAL to the
|
|
163
|
+
* caller — a loop that keeps admitting work after losing its lease or its
|
|
164
|
+
* credential is the exact hole the doors exist to close. */
|
|
165
|
+
export declare function heartbeatWatchLeaseAuthed(store: Store, args: {
|
|
166
|
+
runner: string;
|
|
167
|
+
token: string;
|
|
168
|
+
repo: string;
|
|
169
|
+
owner: string;
|
|
170
|
+
ttlMs: number;
|
|
171
|
+
}, now: Date): boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Retirement fenced by THIS holder's credential (arc 2 finding 28): a
|
|
174
|
+
* successor that already rotated makes the predecessor's cleanup a typed
|
|
175
|
+
* no-op instead of retiring the winner.
|
|
176
|
+
*/
|
|
177
|
+
export declare function retireRunnerIfCurrent(store: Store, name: string, token: string, now: Date): {
|
|
178
|
+
ok: true;
|
|
179
|
+
} | {
|
|
180
|
+
ok: false;
|
|
181
|
+
reason: "unknown" | "bad-token" | "retired" | "expired";
|
|
182
|
+
};
|
|
183
|
+
export declare function isAlive(runner: Runner, now: Date, livenessMs?: number): boolean;
|
|
184
|
+
/** Runners that have stopped saying they are there. */
|
|
185
|
+
export declare function deadRunners(store: Store, now: Date, livenessMs?: number): Runner[];
|
|
186
|
+
/** What a re-registration takes back from the previous holder of its name. */
|
|
187
|
+
export type Reclaim = {
|
|
188
|
+
runner: string;
|
|
189
|
+
/** Lease ids released because the machine holding them is gone. */
|
|
190
|
+
claims: string[];
|
|
191
|
+
/** Worktree paths handed back to the pool. */
|
|
192
|
+
worktrees: string[];
|
|
193
|
+
};
|
|
194
|
+
/** What one reconcile pass took back from a proven-dead runner. */
|
|
195
|
+
export type Recovery = Reclaim & {
|
|
196
|
+
/** Run ids finished as failed/interrupted because the machine that opened
|
|
197
|
+
* them is gone — including runs whose claim a reaper or the runner itself
|
|
198
|
+
* had already released (P0.1a). */
|
|
199
|
+
runs: number[];
|
|
200
|
+
/** Built-in task ids returned to the queue because no newer live lease
|
|
201
|
+
* owned them once the dead runner's rows were settled. */
|
|
202
|
+
requeued: string[];
|
|
203
|
+
};
|
|
204
|
+
/** Whether a pass found anything at all to take back. */
|
|
205
|
+
export declare function recoveredAnything(one: Recovery): boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Take back everything a dead runner was holding.
|
|
208
|
+
*
|
|
209
|
+
* Claims, open runs, and worktrees together, in one pass, because they are
|
|
210
|
+
* halves of the same fact: the machine is gone. Recovering one and not the
|
|
211
|
+
* others leaves a task dispatchable with its working copy still checked out
|
|
212
|
+
* to a process that no longer exists — which fails later, further away, and
|
|
213
|
+
* looks like a git problem rather than a scheduling one — or a run record
|
|
214
|
+
* that stays open forever after its lease was released and a successor
|
|
215
|
+
* finished the work (P0.1a: the real run 1501).
|
|
216
|
+
*
|
|
217
|
+
* Runs settle through the same transactional walk the takeover door uses
|
|
218
|
+
* (`recoverRunnerWork`), AFTER this runner's claims are released: the walk
|
|
219
|
+
* finishes every open run recorded against the runner outside a live held
|
|
220
|
+
* session as failed/interrupted, and requeues its task only when no newer
|
|
221
|
+
* live lease owns it. A finished outcome is never rewritten; a successor's
|
|
222
|
+
* claim, run, worktree, and task state are somebody else's rows and stay
|
|
223
|
+
* exactly as they were.
|
|
224
|
+
*
|
|
225
|
+
* Worktrees are handed back **unverified**. Nobody watched what the dead
|
|
226
|
+
* process was doing when it stopped, so the directory on disk is a claim about
|
|
227
|
+
* the past rather than a description of the present, and something has to look
|
|
228
|
+
* before it is reused.
|
|
229
|
+
*/
|
|
230
|
+
export declare function recoverDead(store: Store, now: Date, livenessMs?: number): Recovery[];
|
|
231
|
+
export declare function hashToken(token: string): string;
|
|
232
|
+
/**
|
|
233
|
+
* What ONE machine can say about a provider without spending a token:
|
|
234
|
+
* the binary answers `--version`; where a cheap identity probe exists
|
|
235
|
+
* (codex, openrouter: `login status`) whether it is logged in; where a key
|
|
236
|
+
* env var is required (openrouter) whether it is present; and, for an
|
|
237
|
+
* attested provider (gemini), whether the installed version is inside the
|
|
238
|
+
* range this build proved. Claude has no non-spending login check, so an
|
|
239
|
+
* installed claude is UNKNOWN — stated, never upgraded to ready.
|
|
240
|
+
*
|
|
241
|
+
* Every answer carries the probe's own words. The observation is the
|
|
242
|
+
* runner's and is recorded under its name only through the authenticated
|
|
243
|
+
* road below; a runner never reports for another machine.
|
|
244
|
+
*/
|
|
245
|
+
export type ReadinessProbe = (file: string, args: readonly string[], options?: {
|
|
246
|
+
timeoutMs?: number;
|
|
247
|
+
omitEnv?: readonly string[];
|
|
248
|
+
}) => Promise<{
|
|
249
|
+
code: number;
|
|
250
|
+
stdout: string;
|
|
251
|
+
stderr: string;
|
|
252
|
+
notFound: boolean;
|
|
253
|
+
timedOut: boolean;
|
|
254
|
+
}>;
|
|
255
|
+
export declare function observeProviderReadiness(probe: ReadinessProbe, env?: Record<string, string | undefined>, providers?: readonly ProviderId[]): Promise<Omit<ReadinessObservation, "runner" | "observedAt">[]>;
|
|
256
|
+
/**
|
|
257
|
+
* Record a runner's observations under its OWN authenticated name: the
|
|
258
|
+
* credential is verified and the rows written in one transaction, so a
|
|
259
|
+
* replaced incarnation cannot report for its successor, and no caller can
|
|
260
|
+
* report for a machine it does not hold the token of.
|
|
261
|
+
*/
|
|
262
|
+
export declare function reportProviderReadinessAuthed(store: Store, args: {
|
|
263
|
+
name: string;
|
|
264
|
+
token: string;
|
|
265
|
+
observations: readonly Omit<ReadinessObservation, "runner" | "observedAt">[];
|
|
266
|
+
}, now: Date): AuthResult;
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A machine that may be given work.
|
|
3
|
+
*
|
|
4
|
+
* Auth is here from the first commit, and that is a deliberate ordering rather
|
|
5
|
+
* than diligence for its own sake. A control plane that dispatches work to
|
|
6
|
+
* unauthenticated runners for six months grows six months of code that assumes
|
|
7
|
+
* the caller is whoever it says it is, and retrofitting identity into that is
|
|
8
|
+
* how the retrofit gets a hole in it. It costs almost nothing to do now.
|
|
9
|
+
*
|
|
10
|
+
* **The token is never stored.** Registration mints one, prints it once, and
|
|
11
|
+
* keeps only a hash. There is no command that recovers it, because a control
|
|
12
|
+
* plane able to hand back a runner's credential is one whose database is worth
|
|
13
|
+
* stealing — and because the same rule governs secrets everywhere else in this
|
|
14
|
+
* design: the control plane holds metadata, the runner holds values.
|
|
15
|
+
*
|
|
16
|
+
* Liveness is separate from a lease expiring, and the distinction matters at
|
|
17
|
+
* 3am. A lease expires because time passed; a runner is dead because it
|
|
18
|
+
* stopped saying otherwise. Reaping on the first tells you a task is free
|
|
19
|
+
* again. Reaping on the second tells you a *machine* is gone, and everything
|
|
20
|
+
* it was holding — claims and worktrees alike — needs recovering together.
|
|
21
|
+
*/
|
|
22
|
+
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
23
|
+
import { resolve } from "node:path";
|
|
24
|
+
import { canonicalProject } from "./project.js";
|
|
25
|
+
import { ALL_CREDENTIAL_ENV, PROVIDER_IDS, inspectionOf } from "./provider.js";
|
|
26
|
+
import { attestationOf, versionInRange } from "./attest.js";
|
|
27
|
+
/** A runner's repo binding, in the same canonical form task filing uses —
|
|
28
|
+
* the claim gate compares these by equality, so both sides must resolve
|
|
29
|
+
* through one convention (MCP gateway spec v6, the runner tuple). */
|
|
30
|
+
export function canonicalRepos(repos) {
|
|
31
|
+
return repos.map(repo => canonicalProject(repo) ?? resolve(repo));
|
|
32
|
+
}
|
|
33
|
+
/** v101: a runner's token lasts a year from its registration; registering again (every start does) renews it. */
|
|
34
|
+
export const RUNNER_TOKEN_MS = 365 * 86_400_000;
|
|
35
|
+
/**
|
|
36
|
+
* How long a runner may go quiet before it is presumed gone.
|
|
37
|
+
*
|
|
38
|
+
* Comfortably more than any sensible heartbeat interval: declaring a busy
|
|
39
|
+
* machine dead because one check-in was slow costs more than waiting another
|
|
40
|
+
* minute for a machine that really is.
|
|
41
|
+
*/
|
|
42
|
+
export const DEFAULT_LIVENESS_MS = 3 * 60_000;
|
|
43
|
+
export function register(store, options) {
|
|
44
|
+
const { name, host, capacity = 1, repos = [], agents = [], now, newToken = mintToken, mutation = {}, } = options;
|
|
45
|
+
const token = newToken();
|
|
46
|
+
/**
|
|
47
|
+
* Anything the previous holder of this name still had is taken back first.
|
|
48
|
+
*
|
|
49
|
+
* Registering under a name is a new process saying it exists; whatever the
|
|
50
|
+
* last one was holding, it is not holding now. Skipping this is worse than
|
|
51
|
+
* it sounds: the fresh `heartbeat_at` makes the old identity look alive
|
|
52
|
+
* again, so the reaper walks straight past its abandoned claims and
|
|
53
|
+
* worktrees and they are stranded for good.
|
|
54
|
+
*/
|
|
55
|
+
const previous = store.getRunner(name);
|
|
56
|
+
const reclaimed = previous === null
|
|
57
|
+
? null
|
|
58
|
+
: {
|
|
59
|
+
runner: name,
|
|
60
|
+
claims: store.releaseClaimsOf(name, now),
|
|
61
|
+
worktrees: store.releaseWorktreesOf(name, now),
|
|
62
|
+
};
|
|
63
|
+
const proposed = {
|
|
64
|
+
name,
|
|
65
|
+
host,
|
|
66
|
+
capacity,
|
|
67
|
+
repos: canonicalRepos(repos),
|
|
68
|
+
agents: [...agents],
|
|
69
|
+
registeredAt: now.toISOString(),
|
|
70
|
+
heartbeatAt: now.toISOString(),
|
|
71
|
+
retiredAt: null,
|
|
72
|
+
};
|
|
73
|
+
// The store assigns the incarnation while holding its write transaction.
|
|
74
|
+
// Doing it from the `previous` snapshot above is racy: two registrars can
|
|
75
|
+
// read the same predecessor and otherwise mint the same generation.
|
|
76
|
+
const runner = store.saveRunner(proposed, hashToken(token), mutation);
|
|
77
|
+
return {
|
|
78
|
+
runner,
|
|
79
|
+
token,
|
|
80
|
+
reclaimed: reclaimed !== null && (reclaimed.claims.length > 0 || reclaimed.worktrees.length > 0)
|
|
81
|
+
? reclaimed
|
|
82
|
+
: null,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Whether this caller is who it says it is.
|
|
87
|
+
*
|
|
88
|
+
* Compared in constant time. The comparison is between hashes rather than
|
|
89
|
+
* secrets, so the practical risk is slight — but a timing-variable equality on
|
|
90
|
+
* a credential path is the kind of thing that gets copied into somewhere it
|
|
91
|
+
* matters, and there is no reason to leave the example lying around.
|
|
92
|
+
*/
|
|
93
|
+
export function authenticate(store, name, token) {
|
|
94
|
+
const found = store.getRunner(name);
|
|
95
|
+
if (found === null)
|
|
96
|
+
return { ok: false, reason: "unknown" };
|
|
97
|
+
if (found.runner.retiredAt !== null)
|
|
98
|
+
return { ok: false, reason: "retired" };
|
|
99
|
+
if (Date.now() - Date.parse(found.runner.registeredAt) > RUNNER_TOKEN_MS)
|
|
100
|
+
return { ok: false, reason: "expired" };
|
|
101
|
+
return sameDigest(found.credentialHash, hashToken(token))
|
|
102
|
+
? { ok: true, runner: found.runner }
|
|
103
|
+
: { ok: false, reason: "bad-token" };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* "I am still here." Authenticated AND ATOMIC (arc 2 findings 25/33): the
|
|
107
|
+
* hash is verified and the heartbeat stamped in one write transaction, so
|
|
108
|
+
* a takeover that rotates the credential between the check and the touch
|
|
109
|
+
* cannot receive a stale incarnation's pulse — after rotation commits, the
|
|
110
|
+
* very next heartbeat refuses, and the caller must treat that as fatal.
|
|
111
|
+
*/
|
|
112
|
+
export function heartbeat(store, name, token, now) {
|
|
113
|
+
return store.transact(() => {
|
|
114
|
+
const auth = authenticate(store, name, token);
|
|
115
|
+
if (!auth.ok)
|
|
116
|
+
return auth;
|
|
117
|
+
store.touchRunner(name, now);
|
|
118
|
+
return { ok: true, runner: { ...auth.runner, heartbeatAt: now.toISOString() } };
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Extend a live runner's project binding using that runner's own credential.
|
|
123
|
+
*
|
|
124
|
+
* `up` owns both the credential and the registry watcher, so a project the
|
|
125
|
+
* operator admitted through its console can join without a second operator
|
|
126
|
+
* ceremony or a restart. Authentication and the additive write share one
|
|
127
|
+
* transaction: a replaced process cannot widen its successor's binding.
|
|
128
|
+
*/
|
|
129
|
+
export function addRunnerReposAuthed(store, args, now) {
|
|
130
|
+
return store.transact(() => {
|
|
131
|
+
const auth = authenticate(store, args.name, args.token);
|
|
132
|
+
if (!auth.ok)
|
|
133
|
+
return auth;
|
|
134
|
+
const repos = [...new Set([...auth.runner.repos, ...canonicalRepos(args.repos)])].sort();
|
|
135
|
+
const bound = store.bindRunnerRepos(args.name, repos, now);
|
|
136
|
+
if (!bound.ok)
|
|
137
|
+
return { ok: false, reason: bound.reason };
|
|
138
|
+
store.touchRunner(args.name, now);
|
|
139
|
+
return {
|
|
140
|
+
ok: true,
|
|
141
|
+
runner: { ...auth.runner, repos: bound.repos, heartbeatAt: now.toISOString() },
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The one runner-name rule (arc 2 finding 13): nonempty, control-free, at
|
|
147
|
+
* most 60 characters — the console form's rule, now shared by every door.
|
|
148
|
+
*/
|
|
149
|
+
export const RUNNER_NAME_MAX = 60;
|
|
150
|
+
/** The most tasks one worker may be set to run at once (the console's register form says the same). */
|
|
151
|
+
export const RUNNER_CAPACITY_MAX = 64;
|
|
152
|
+
/** A capacity as typed: a whole number from 1 to RUNNER_CAPACITY_MAX, or null. */
|
|
153
|
+
export function parseRunnerCapacity(value) {
|
|
154
|
+
const capacity = Number(value.trim());
|
|
155
|
+
return value.trim() !== "" && Number.isInteger(capacity) && capacity >= 1 && capacity <= RUNNER_CAPACITY_MAX ? capacity : null;
|
|
156
|
+
}
|
|
157
|
+
export function validRunnerName(name) {
|
|
158
|
+
if (name === "" || name.length > RUNNER_NAME_MAX)
|
|
159
|
+
return false;
|
|
160
|
+
for (const char of name) {
|
|
161
|
+
const code = char.codePointAt(0) ?? 0;
|
|
162
|
+
if (code < 0x20 || code === 0x7f)
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
/** A generated base name: lowercase [a-z0-9-], collapsed, trimmed. The
|
|
168
|
+
* suffix budget (finding 24) is the CALLER's arithmetic — truncate the
|
|
169
|
+
* base to `RUNNER_NAME_MAX - suffix.length` before appending. */
|
|
170
|
+
export function normalizeRunnerName(raw) {
|
|
171
|
+
const collapsed = raw
|
|
172
|
+
.toLowerCase()
|
|
173
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
174
|
+
.replace(/-+/g, "-")
|
|
175
|
+
.replace(/^-|-$/g, "");
|
|
176
|
+
return (collapsed === "" ? "worker" : collapsed).slice(0, RUNNER_NAME_MAX);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* The atomic take-or-refuse door (arc 2 findings 1/15/16/26/31): ONE write
|
|
180
|
+
* transaction that proves the name is free — absent, retired, or dead by
|
|
181
|
+
* the same predicate recovery uses, with NO unexpired watch lease — then
|
|
182
|
+
* finishes every open run the dead holder left (requeueing what no newer
|
|
183
|
+
* live claim owns), reclaims, and rotates. Two racers serialize: one wins,
|
|
184
|
+
* the other gets the typed refusal.
|
|
185
|
+
*/
|
|
186
|
+
export function registerRunnerIfIdle(store, options) {
|
|
187
|
+
const { name, now } = options;
|
|
188
|
+
return store.transact(() => {
|
|
189
|
+
const existing = store.getRunner(name);
|
|
190
|
+
if (existing !== null && existing.runner.retiredAt === null && isAlive(existing.runner, now)) {
|
|
191
|
+
return {
|
|
192
|
+
ok: false,
|
|
193
|
+
reason: "runner-alive",
|
|
194
|
+
detail: `${name} heartbeated ${existing.runner.heartbeatAt} — it looks alive`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const lease = store.liveWatchLeaseOf(name, now);
|
|
198
|
+
if (lease !== null) {
|
|
199
|
+
return {
|
|
200
|
+
ok: false,
|
|
201
|
+
reason: "watch-live",
|
|
202
|
+
detail: `${name} holds a live watch lease on ${lease.repo} until ${lease.until}`,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
// Recovery BEFORE reclaim (finding 16): the open-run evidence must be
|
|
206
|
+
// read while it still exists; register()'s releaseClaimsOf would
|
|
207
|
+
// otherwise hide it from every later recovery.
|
|
208
|
+
const recoveredRuns = existing === null ? 0 : store.recoverRunnerWork(name, now).runs.length;
|
|
209
|
+
// A restart keeps the capacity an operator set; only an explicit one changes it.
|
|
210
|
+
const registration = register(store, { ...options, capacity: options.capacity ?? existing?.runner.capacity ?? 1 });
|
|
211
|
+
return { ok: true, ...registration, recoveredRuns };
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* The authenticated lease door (arc 2 findings 15/26): credential verified
|
|
216
|
+
* and lease acquired/renewed in the SAME transaction — after a takeover
|
|
217
|
+
* rotates the hash, a stale incarnation can neither acquire nor renew. A
|
|
218
|
+
* takeover of an expired lease recovers the superseded incarnation INSIDE
|
|
219
|
+
* this transaction, so no crash can separate "B owns the lease" from "A
|
|
220
|
+
* was recovered".
|
|
221
|
+
*/
|
|
222
|
+
export function acquireWatchLeaseAuthed(store, args, now) {
|
|
223
|
+
return store.transact(() => {
|
|
224
|
+
const auth = authenticate(store, args.runner, args.token);
|
|
225
|
+
if (!auth.ok)
|
|
226
|
+
return { ok: false, reason: auth.reason };
|
|
227
|
+
// Watching a repo is a runner road like any other (MCP spec v6,
|
|
228
|
+
// round-4 finding 1): membership is proven in the same transaction
|
|
229
|
+
// as the lease, before any repo activity rides it.
|
|
230
|
+
if (!auth.runner.repos.includes(canonicalProject(args.repo) ?? resolve(args.repo))) {
|
|
231
|
+
return { ok: false, reason: "unauthorized-repo" };
|
|
232
|
+
}
|
|
233
|
+
const got = store.acquireWatchLease(args.runner, args.repo, args.owner, args.ttlMs, now);
|
|
234
|
+
if (!got.ok)
|
|
235
|
+
return { ok: false, reason: "watch-busy", holder: got.holder, until: got.until };
|
|
236
|
+
const recovered = got.superseded === null ? 0 : store.recoverIncarnation(args.runner, got.superseded, now);
|
|
237
|
+
return { ok: true, generation: got.generation, superseded: got.superseded, recovered };
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
/** The renewal, equally authenticated: a false answer is FATAL to the
|
|
241
|
+
* caller — a loop that keeps admitting work after losing its lease or its
|
|
242
|
+
* credential is the exact hole the doors exist to close. */
|
|
243
|
+
export function heartbeatWatchLeaseAuthed(store, args, now) {
|
|
244
|
+
return store.transact(() => {
|
|
245
|
+
const auth = authenticate(store, args.runner, args.token);
|
|
246
|
+
if (!auth.ok)
|
|
247
|
+
return false;
|
|
248
|
+
const renewed = store.heartbeatWatchLease(args.runner, args.repo, args.owner, args.ttlMs, now);
|
|
249
|
+
if (renewed)
|
|
250
|
+
store.touchRunner(args.runner, now);
|
|
251
|
+
return renewed;
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Retirement fenced by THIS holder's credential (arc 2 finding 28): a
|
|
256
|
+
* successor that already rotated makes the predecessor's cleanup a typed
|
|
257
|
+
* no-op instead of retiring the winner.
|
|
258
|
+
*/
|
|
259
|
+
export function retireRunnerIfCurrent(store, name, token, now) {
|
|
260
|
+
return store.transact(() => {
|
|
261
|
+
const auth = authenticate(store, name, token);
|
|
262
|
+
if (!auth.ok)
|
|
263
|
+
return { ok: false, reason: auth.reason };
|
|
264
|
+
store.retireRunner(name, now);
|
|
265
|
+
return { ok: true };
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
export function isAlive(runner, now, livenessMs = DEFAULT_LIVENESS_MS) {
|
|
269
|
+
if (runner.retiredAt !== null)
|
|
270
|
+
return false;
|
|
271
|
+
const last = Date.parse(runner.heartbeatAt);
|
|
272
|
+
if (Number.isNaN(last))
|
|
273
|
+
return false;
|
|
274
|
+
return now.getTime() - last < livenessMs;
|
|
275
|
+
}
|
|
276
|
+
/** Runners that have stopped saying they are there. */
|
|
277
|
+
export function deadRunners(store, now, livenessMs = DEFAULT_LIVENESS_MS) {
|
|
278
|
+
return store.listRunners().filter(runner => !isAlive(runner, now, livenessMs));
|
|
279
|
+
}
|
|
280
|
+
/** Whether a pass found anything at all to take back. */
|
|
281
|
+
export function recoveredAnything(one) {
|
|
282
|
+
return one.claims.length > 0 || one.worktrees.length > 0 || one.runs.length > 0 || one.requeued.length > 0;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Take back everything a dead runner was holding.
|
|
286
|
+
*
|
|
287
|
+
* Claims, open runs, and worktrees together, in one pass, because they are
|
|
288
|
+
* halves of the same fact: the machine is gone. Recovering one and not the
|
|
289
|
+
* others leaves a task dispatchable with its working copy still checked out
|
|
290
|
+
* to a process that no longer exists — which fails later, further away, and
|
|
291
|
+
* looks like a git problem rather than a scheduling one — or a run record
|
|
292
|
+
* that stays open forever after its lease was released and a successor
|
|
293
|
+
* finished the work (P0.1a: the real run 1501).
|
|
294
|
+
*
|
|
295
|
+
* Runs settle through the same transactional walk the takeover door uses
|
|
296
|
+
* (`recoverRunnerWork`), AFTER this runner's claims are released: the walk
|
|
297
|
+
* finishes every open run recorded against the runner outside a live held
|
|
298
|
+
* session as failed/interrupted, and requeues its task only when no newer
|
|
299
|
+
* live lease owns it. A finished outcome is never rewritten; a successor's
|
|
300
|
+
* claim, run, worktree, and task state are somebody else's rows and stay
|
|
301
|
+
* exactly as they were.
|
|
302
|
+
*
|
|
303
|
+
* Worktrees are handed back **unverified**. Nobody watched what the dead
|
|
304
|
+
* process was doing when it stopped, so the directory on disk is a claim about
|
|
305
|
+
* the past rather than a description of the present, and something has to look
|
|
306
|
+
* before it is reused.
|
|
307
|
+
*/
|
|
308
|
+
export function recoverDead(store, now, livenessMs = DEFAULT_LIVENESS_MS) {
|
|
309
|
+
const recovered = [];
|
|
310
|
+
for (const candidate of deadRunners(store, now, livenessMs)) {
|
|
311
|
+
// Death is re-proved inside the transaction that acts on it. The snapshot
|
|
312
|
+
// above is a survey of a moving world: a runner whose pulse lands between
|
|
313
|
+
// the survey and the release is alive, and releasing its claims anyway
|
|
314
|
+
// would take work from a machine that just said "still here" — the exact
|
|
315
|
+
// loss the heartbeat exists to prevent.
|
|
316
|
+
const recovery = store.transact(() => {
|
|
317
|
+
const fresh = store.getRunner(candidate.name);
|
|
318
|
+
if (fresh === null || isAlive(fresh.runner, now, livenessMs))
|
|
319
|
+
return null;
|
|
320
|
+
// Claims and worktrees first, so the report names every path handed
|
|
321
|
+
// back; the run walk's own per-task worktree release then finds
|
|
322
|
+
// nothing left to do here.
|
|
323
|
+
const requeued = [];
|
|
324
|
+
const claims = store.releaseClaimsOf(candidate.name, now, requeued);
|
|
325
|
+
const worktrees = store.releaseWorktreesOf(candidate.name, now);
|
|
326
|
+
const settled = store.recoverRunnerWork(candidate.name, now);
|
|
327
|
+
return { runner: candidate.name, claims, worktrees, runs: settled.runs, requeued: [...new Set([...requeued, ...settled.requeued])] };
|
|
328
|
+
});
|
|
329
|
+
if (recovery !== null)
|
|
330
|
+
recovered.push(recovery);
|
|
331
|
+
}
|
|
332
|
+
return recovered;
|
|
333
|
+
}
|
|
334
|
+
/** 32 bytes of randomness, urlsafe, so it survives being pasted anywhere. */
|
|
335
|
+
function mintToken() {
|
|
336
|
+
return randomBytes(32).toString("base64url");
|
|
337
|
+
}
|
|
338
|
+
export function hashToken(token) {
|
|
339
|
+
return createHash("sha256").update(token, "utf8").digest("hex");
|
|
340
|
+
}
|
|
341
|
+
function sameDigest(left, right) {
|
|
342
|
+
const a = Buffer.from(left, "utf8");
|
|
343
|
+
const b = Buffer.from(right, "utf8");
|
|
344
|
+
// timingSafeEqual throws on a length mismatch, which is itself a leak of
|
|
345
|
+
// sorts; equal-length hex digests make that unreachable in practice, and the
|
|
346
|
+
// guard keeps it from throwing if one is ever malformed.
|
|
347
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
348
|
+
}
|
|
349
|
+
const READINESS_PROBE_TIMEOUT_MS = 5_000;
|
|
350
|
+
export async function observeProviderReadiness(probe, env = process.env, providers = PROVIDER_IDS) {
|
|
351
|
+
return Promise.all(providers.map(async (provider) => {
|
|
352
|
+
const facts = inspectionOf(provider);
|
|
353
|
+
const version = await probe(facts.binary, ["--version"], { timeoutMs: READINESS_PROBE_TIMEOUT_MS, omitEnv: ALL_CREDENTIAL_ENV });
|
|
354
|
+
if (version.notFound) {
|
|
355
|
+
return { provider, state: "unavailable", reason: `\`${facts.binary}\` is not installed on this runner's PATH`, probe: "version" };
|
|
356
|
+
}
|
|
357
|
+
if (version.timedOut || version.code !== 0) {
|
|
358
|
+
return { provider, state: "unavailable", reason: `\`${facts.binary} --version\` ${version.timedOut ? "timed out" : `exited ${version.code}`}`, probe: "version" };
|
|
359
|
+
}
|
|
360
|
+
const installed = version.stdout.trim().split("\n")[0] ?? "";
|
|
361
|
+
const range = attestationOf(provider);
|
|
362
|
+
if (range !== null && !versionInRange(installed, range)) {
|
|
363
|
+
return { provider, state: "unavailable", reason: `installed ${facts.binary} ${installed || "(unversioned)"} is outside this build's attested range ${range.floor}–${range.ceiling}`, probe: "version" };
|
|
364
|
+
}
|
|
365
|
+
if (facts.requiresEnv !== null && (env[facts.requiresEnv] ?? "") === "") {
|
|
366
|
+
return { provider, state: "unavailable", reason: `${facts.requiresEnv} is absent from this runner's environment`, probe: "key" };
|
|
367
|
+
}
|
|
368
|
+
// A key-driven provider (openrouter rides the codex harness on an API
|
|
369
|
+
// key) is ready on key PRESENCE — presence, never authorization — and
|
|
370
|
+
// needs no harness login. A login-driven one asks the harness.
|
|
371
|
+
if (facts.requiresEnv !== null) {
|
|
372
|
+
return { provider, state: "ready", reason: `installed (${installed}); ${facts.requiresEnv} present (presence, not authorization)`, probe: "key" };
|
|
373
|
+
}
|
|
374
|
+
if (facts.identityProbe !== null) {
|
|
375
|
+
const identity = await probe(facts.binary, [...facts.identityProbe], { timeoutMs: READINESS_PROBE_TIMEOUT_MS, omitEnv: ALL_CREDENTIAL_ENV });
|
|
376
|
+
if (identity.code !== 0) {
|
|
377
|
+
return { provider, state: "unavailable", reason: `\`${facts.binary} ${facts.identityProbe.join(" ")}\` says not logged in`, probe: "identity" };
|
|
378
|
+
}
|
|
379
|
+
const who = identity.stdout.trim().split("\n")[0] ?? "";
|
|
380
|
+
return { provider, state: "ready", reason: `installed (${installed}); ${who === "" ? "logged in" : who}`, probe: "identity" };
|
|
381
|
+
}
|
|
382
|
+
return { provider, state: "unknown", reason: `installed (${installed}); no non-spending login check exists — a real run is the proof`, probe: "version" };
|
|
383
|
+
}));
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Record a runner's observations under its OWN authenticated name: the
|
|
387
|
+
* credential is verified and the rows written in one transaction, so a
|
|
388
|
+
* replaced incarnation cannot report for its successor, and no caller can
|
|
389
|
+
* report for a machine it does not hold the token of.
|
|
390
|
+
*/
|
|
391
|
+
export function reportProviderReadinessAuthed(store, args, now) {
|
|
392
|
+
return store.transact(() => {
|
|
393
|
+
const auth = authenticate(store, args.name, args.token);
|
|
394
|
+
if (!auth.ok)
|
|
395
|
+
return auth;
|
|
396
|
+
store.recordProviderReadiness(args.name, args.observations, now);
|
|
397
|
+
return auth;
|
|
398
|
+
});
|
|
399
|
+
}
|