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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a machine gate failed, read from its sealed receipt, log and candidate
|
|
3
|
+
* inventory — so the bounded repair loop does not spend a code-writing
|
|
4
|
+
* attempt on a failure the change cannot have caused.
|
|
5
|
+
*
|
|
6
|
+
* Two classes are conservative enough to skip a repair draft:
|
|
7
|
+
* - the whole command timed out before any test failed, and
|
|
8
|
+
* - every failing test is a per-test timeout inside a file the candidate
|
|
9
|
+
* did not touch.
|
|
10
|
+
* An ordinary assertion failure anywhere — including in an untouched file,
|
|
11
|
+
* whose imports the change may have broken — stays repairable. When the
|
|
12
|
+
* failing files cannot be read from the log with certainty, the failure
|
|
13
|
+
* stays repairable too: never silence a real failure by parsing badly.
|
|
14
|
+
*/
|
|
15
|
+
import type { VerifyCommandFacts } from "./proof.js";
|
|
16
|
+
export type GateFailureClass = {
|
|
17
|
+
kind: "repairable";
|
|
18
|
+
} | {
|
|
19
|
+
kind: "gate-timed-out";
|
|
20
|
+
timeoutMs: number | null;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "untouched-test-timeout";
|
|
23
|
+
files: string[];
|
|
24
|
+
};
|
|
25
|
+
export type FailingTest = {
|
|
26
|
+
file: string;
|
|
27
|
+
timedOut: boolean;
|
|
28
|
+
};
|
|
29
|
+
/** The failing tests a Vitest log names, or null when the log's own failed
|
|
30
|
+
* count and its FAIL blocks disagree (or no count was printed at all). */
|
|
31
|
+
export declare function failingTestsOf(log: string): FailingTest[] | null;
|
|
32
|
+
export declare function classifyGateFailure(result: VerifyCommandFacts, log: string, changedPaths: readonly string[] | null, timeoutMs?: number | null): GateFailureClass;
|
|
33
|
+
/** What happened, in one sentence, for every notice about this failure. */
|
|
34
|
+
export declare function gateFailureSummary(failure: Exclude<GateFailureClass, {
|
|
35
|
+
kind: "repairable";
|
|
36
|
+
}>): string;
|
|
37
|
+
/** Plain words for the person who has to act. */
|
|
38
|
+
export declare function describeGateFailure(failure: Exclude<GateFailureClass, {
|
|
39
|
+
kind: "repairable";
|
|
40
|
+
}>, taskId: string): string;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const ANSI = /\x1b\[[0-9;]*[A-Za-z]/g;
|
|
2
|
+
const FAIL_LINE = /^\s*FAIL\s+(\S+?)(?:\s+>\s.*)?\s*$/;
|
|
3
|
+
const TESTS_SUMMARY = /^\s*Tests\s+(\d+)\s+failed\b/;
|
|
4
|
+
const TIMED_OUT = /\b(?:Test|Hook) timed out in \d+ms\b/;
|
|
5
|
+
/** The failing tests a Vitest log names, or null when the log's own failed
|
|
6
|
+
* count and its FAIL blocks disagree (or no count was printed at all). */
|
|
7
|
+
export function failingTestsOf(log) {
|
|
8
|
+
const lines = log.replace(ANSI, "").split(/\r?\n/);
|
|
9
|
+
const failures = [];
|
|
10
|
+
let reported = null;
|
|
11
|
+
let open = null;
|
|
12
|
+
for (const line of lines) {
|
|
13
|
+
const summary = TESTS_SUMMARY.exec(line);
|
|
14
|
+
if (summary) {
|
|
15
|
+
reported = Number(summary[1]);
|
|
16
|
+
open = null;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const fail = FAIL_LINE.exec(line);
|
|
20
|
+
if (fail) {
|
|
21
|
+
open = { file: fail[1], timedOut: false };
|
|
22
|
+
failures.push(open);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (open !== null && TIMED_OUT.test(line))
|
|
26
|
+
open.timedOut = true;
|
|
27
|
+
}
|
|
28
|
+
if (reported === null || reported !== failures.length)
|
|
29
|
+
return null;
|
|
30
|
+
return failures;
|
|
31
|
+
}
|
|
32
|
+
/** Vitest prints paths relative to its root; the candidate inventory lists
|
|
33
|
+
* repository paths. Treat a failing file as touched when any changed path is
|
|
34
|
+
* the same path or ends with it. */
|
|
35
|
+
const touched = (file, changed) => changed.some(path => path === file || path.endsWith(`/${file}`) || file.endsWith(`/${path}`));
|
|
36
|
+
export function classifyGateFailure(result, log, changedPaths, timeoutMs = null) {
|
|
37
|
+
if (!result.configured)
|
|
38
|
+
return { kind: "repairable" };
|
|
39
|
+
if (!result.ran) {
|
|
40
|
+
return result.failure === "timed-out" || result.failure === "retry-timed-out" ? { kind: "gate-timed-out", timeoutMs } : { kind: "repairable" };
|
|
41
|
+
}
|
|
42
|
+
if (result.exitCode === 0 || changedPaths === null)
|
|
43
|
+
return { kind: "repairable" };
|
|
44
|
+
const failures = failingTestsOf(log);
|
|
45
|
+
if (failures === null || failures.length === 0)
|
|
46
|
+
return { kind: "repairable" };
|
|
47
|
+
if (failures.every(one => one.timedOut && !touched(one.file, changedPaths))) {
|
|
48
|
+
return { kind: "untouched-test-timeout", files: [...new Set(failures.map(one => one.file))].sort() };
|
|
49
|
+
}
|
|
50
|
+
return { kind: "repairable" };
|
|
51
|
+
}
|
|
52
|
+
/** What happened, in one sentence, for every notice about this failure. */
|
|
53
|
+
export function gateFailureSummary(failure) {
|
|
54
|
+
if (failure.kind === "gate-timed-out") {
|
|
55
|
+
const limit = failure.timeoutMs === null ? "its time limit" : `${Math.round(failure.timeoutMs / 1000)} s`;
|
|
56
|
+
return `The project check ran out of time (${limit}) before any test failed, so the code was not shown to be wrong.`;
|
|
57
|
+
}
|
|
58
|
+
return `The project check failed only because ${failure.files.join(", ")} timed out, and this change did not touch ${failure.files.length === 1 ? "that file" : "those files"}.`;
|
|
59
|
+
}
|
|
60
|
+
/** Plain words for the person who has to act. */
|
|
61
|
+
export function describeGateFailure(failure, taskId) {
|
|
62
|
+
const next = failure.kind === "gate-timed-out"
|
|
63
|
+
? `Raise the check's time limit or shorten the suite, then run \`toolroll task regate ${taskId}\` to check the same commit again.`
|
|
64
|
+
: `Run \`toolroll task regate ${taskId}\` to check the same commit again, or fix the slow test outside this task first.`;
|
|
65
|
+
return `${gateFailureSummary(failure)} No repair task was filed. ${next}`;
|
|
66
|
+
}
|
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsers for git plumbing output.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions over strings — no child_process, no fs. Shelling out lives in
|
|
5
|
+
* the caller, so the part most worth testing needs no fixture repo. This is also
|
|
6
|
+
* why discovery can claim to be read-only: nothing here can reach a repository.
|
|
7
|
+
*/
|
|
8
|
+
export type Worktree = {
|
|
9
|
+
path: string;
|
|
10
|
+
head: string;
|
|
11
|
+
/** null when in detached HEAD — which is how treehouse leases worktrees. */
|
|
12
|
+
branch: string | null;
|
|
13
|
+
detached: boolean;
|
|
14
|
+
bare: boolean;
|
|
15
|
+
locked: boolean;
|
|
16
|
+
prunable: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type TrackingState = {
|
|
19
|
+
ahead: number;
|
|
20
|
+
behind: number;
|
|
21
|
+
/** Upstream existed and was deleted — the merged-and-cleaned-up case. */
|
|
22
|
+
gone: boolean;
|
|
23
|
+
};
|
|
24
|
+
export type Branch = {
|
|
25
|
+
name: string;
|
|
26
|
+
track: TrackingState;
|
|
27
|
+
/** Opaque ISO-8601 from git, passed through untouched. Never reformatted here. */
|
|
28
|
+
committedAt: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Parse `git worktree list --porcelain`.
|
|
32
|
+
*
|
|
33
|
+
* Records are separated by blank lines; each is a sequence of `key value` lines
|
|
34
|
+
* where `detached`, `bare`, `locked` and `prunable` are bare flags that may carry
|
|
35
|
+
* a trailing reason we deliberately discard.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseWorktrees(porcelain: string): Worktree[];
|
|
38
|
+
/**
|
|
39
|
+
* Parse the `%(upstream:track)` field: `""`, `"[gone]"`, `"[ahead 2]"`,
|
|
40
|
+
* `"[behind 5]"`, or `"[ahead 2, behind 5]"`.
|
|
41
|
+
*
|
|
42
|
+
* Unrecognized input yields a zeroed state rather than throwing — a tracking
|
|
43
|
+
* field we cannot read must never take down discovery of the repo around it.
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseTrack(field: string): TrackingState;
|
|
46
|
+
/**
|
|
47
|
+
* Parse tab-separated `git for-each-ref` output:
|
|
48
|
+
*
|
|
49
|
+
* %(refname:short)\t%(upstream:track)\t%(committerdate:iso8601-strict)
|
|
50
|
+
*
|
|
51
|
+
* Tabs rather than spaces because a ref name may not contain a tab, but the
|
|
52
|
+
* tracking field always does.
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseBranches(output: string): Branch[];
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsers for git plumbing output.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions over strings — no child_process, no fs. Shelling out lives in
|
|
5
|
+
* the caller, so the part most worth testing needs no fixture repo. This is also
|
|
6
|
+
* why discovery can claim to be read-only: nothing here can reach a repository.
|
|
7
|
+
*/
|
|
8
|
+
const HEADS_PREFIX = "refs/heads/";
|
|
9
|
+
const emptyTrack = () => ({ ahead: 0, behind: 0, gone: false });
|
|
10
|
+
/**
|
|
11
|
+
* Parse `git worktree list --porcelain`.
|
|
12
|
+
*
|
|
13
|
+
* Records are separated by blank lines; each is a sequence of `key value` lines
|
|
14
|
+
* where `detached`, `bare`, `locked` and `prunable` are bare flags that may carry
|
|
15
|
+
* a trailing reason we deliberately discard.
|
|
16
|
+
*/
|
|
17
|
+
export function parseWorktrees(porcelain) {
|
|
18
|
+
return porcelain
|
|
19
|
+
.split(/\n\s*\n/)
|
|
20
|
+
.map(block => block.trim())
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.map(parseWorktreeBlock)
|
|
23
|
+
.filter((wt) => wt !== null);
|
|
24
|
+
}
|
|
25
|
+
function parseWorktreeBlock(block) {
|
|
26
|
+
const lines = block.split("\n");
|
|
27
|
+
const path = readValue(lines, "worktree");
|
|
28
|
+
if (path === null)
|
|
29
|
+
return null;
|
|
30
|
+
const branchRef = readValue(lines, "branch");
|
|
31
|
+
return {
|
|
32
|
+
path,
|
|
33
|
+
head: readValue(lines, "HEAD") ?? "",
|
|
34
|
+
branch: branchRef === null ? null : stripHeadsPrefix(branchRef),
|
|
35
|
+
detached: hasFlag(lines, "detached"),
|
|
36
|
+
bare: hasFlag(lines, "bare"),
|
|
37
|
+
locked: hasFlag(lines, "locked"),
|
|
38
|
+
prunable: hasFlag(lines, "prunable"),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** First value for `key`, or null when absent. Values may contain spaces. */
|
|
42
|
+
function readValue(lines, key) {
|
|
43
|
+
const prefix = `${key} `;
|
|
44
|
+
const line = lines.find(l => l.startsWith(prefix));
|
|
45
|
+
return line === undefined ? null : line.slice(prefix.length).trim();
|
|
46
|
+
}
|
|
47
|
+
/** Flags appear bare (`detached`) or with a trailing reason (`locked why`). */
|
|
48
|
+
function hasFlag(lines, flag) {
|
|
49
|
+
return lines.some(l => l === flag || l.startsWith(`${flag} `));
|
|
50
|
+
}
|
|
51
|
+
function stripHeadsPrefix(ref) {
|
|
52
|
+
return ref.startsWith(HEADS_PREFIX) ? ref.slice(HEADS_PREFIX.length) : ref;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Parse the `%(upstream:track)` field: `""`, `"[gone]"`, `"[ahead 2]"`,
|
|
56
|
+
* `"[behind 5]"`, or `"[ahead 2, behind 5]"`.
|
|
57
|
+
*
|
|
58
|
+
* Unrecognized input yields a zeroed state rather than throwing — a tracking
|
|
59
|
+
* field we cannot read must never take down discovery of the repo around it.
|
|
60
|
+
*/
|
|
61
|
+
export function parseTrack(field) {
|
|
62
|
+
const trimmed = field.trim();
|
|
63
|
+
if (trimmed === "")
|
|
64
|
+
return emptyTrack();
|
|
65
|
+
if (trimmed === "[gone]")
|
|
66
|
+
return { ahead: 0, behind: 0, gone: true };
|
|
67
|
+
return {
|
|
68
|
+
ahead: readCount(trimmed, "ahead"),
|
|
69
|
+
behind: readCount(trimmed, "behind"),
|
|
70
|
+
gone: false,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function readCount(field, key) {
|
|
74
|
+
const match = field.match(new RegExp(`${key} (\\d+)`));
|
|
75
|
+
return match === null ? 0 : Number(match[1]);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Parse tab-separated `git for-each-ref` output:
|
|
79
|
+
*
|
|
80
|
+
* %(refname:short)\t%(upstream:track)\t%(committerdate:iso8601-strict)
|
|
81
|
+
*
|
|
82
|
+
* Tabs rather than spaces because a ref name may not contain a tab, but the
|
|
83
|
+
* tracking field always does.
|
|
84
|
+
*/
|
|
85
|
+
export function parseBranches(output) {
|
|
86
|
+
return output
|
|
87
|
+
.split("\n")
|
|
88
|
+
.filter(line => line.trim() !== "")
|
|
89
|
+
.map(parseBranchLine);
|
|
90
|
+
}
|
|
91
|
+
function parseBranchLine(line) {
|
|
92
|
+
const [name = "", track = "", committedAt = ""] = line.split("\t");
|
|
93
|
+
return { name: name.trim(), track: parseTrack(track), committedAt: committedAt.trim() };
|
|
94
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export declare const GOOGLE_AUTH = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
2
|
+
export declare const GOOGLE_TOKEN = "https://oauth2.googleapis.com/token";
|
|
3
|
+
/** Full mail access is what IMAP and SMTP sign-in needs; "email" says which address it is. */
|
|
4
|
+
export declare const GOOGLE_SCOPE = "https://mail.google.com/ openid email";
|
|
5
|
+
export declare const GOOGLE_CALLBACK = "/settings/google/callback";
|
|
6
|
+
export type GoogleMail = {
|
|
7
|
+
clientId: string;
|
|
8
|
+
clientSecret: string;
|
|
9
|
+
refreshToken: string | null;
|
|
10
|
+
address: string | null;
|
|
11
|
+
};
|
|
12
|
+
export declare function readGoogleMail(dir: string | null): GoogleMail | null;
|
|
13
|
+
/** Connected: a refresh token and the address it belongs to. */
|
|
14
|
+
export declare function googleConnected(dir: string | null): {
|
|
15
|
+
address: string;
|
|
16
|
+
} | null;
|
|
17
|
+
/** Save the OAuth client from Settings; an empty secret keeps the saved one. A new client forgets the old connection. */
|
|
18
|
+
export declare function saveGoogleClient(dir: string, input: {
|
|
19
|
+
clientId: unknown;
|
|
20
|
+
clientSecret: unknown;
|
|
21
|
+
}): {
|
|
22
|
+
ok: true;
|
|
23
|
+
} | {
|
|
24
|
+
ok: false;
|
|
25
|
+
message: string;
|
|
26
|
+
};
|
|
27
|
+
export type GoogleVisit = {
|
|
28
|
+
state: string;
|
|
29
|
+
verifier: string;
|
|
30
|
+
redirectUri: string;
|
|
31
|
+
by: string;
|
|
32
|
+
expires: number;
|
|
33
|
+
};
|
|
34
|
+
/** Where to send the person to consent, and what to remember to check their return against. */
|
|
35
|
+
export declare function googleConsent(client: GoogleMail, redirectUri: string, by: string, now?: number): {
|
|
36
|
+
url: string;
|
|
37
|
+
visit: GoogleVisit;
|
|
38
|
+
};
|
|
39
|
+
/** The one-time code from Google's redirect, exchanged for a refresh token and saved. */
|
|
40
|
+
export declare function finishGoogleConsent(dir: string, visit: GoogleVisit, code: string, fetcher: typeof fetch): Promise<{
|
|
41
|
+
ok: true;
|
|
42
|
+
address: string;
|
|
43
|
+
} | {
|
|
44
|
+
ok: false;
|
|
45
|
+
message: string;
|
|
46
|
+
}>;
|
|
47
|
+
/** A fresh access token for IMAP and SMTP (kept in memory until a minute before it ends). */
|
|
48
|
+
export declare function googleAccessToken(dir: string | null, fetcher: typeof fetch, now?: number): Promise<{
|
|
49
|
+
ok: true;
|
|
50
|
+
token: string;
|
|
51
|
+
address: string;
|
|
52
|
+
} | {
|
|
53
|
+
ok: false;
|
|
54
|
+
said: string;
|
|
55
|
+
permanent: boolean;
|
|
56
|
+
}>;
|
|
57
|
+
/** Disconnect: Google forgets the grant (best effort), and the file goes. */
|
|
58
|
+
export declare function disconnectGoogle(dir: string, fetcher: typeof fetch): Promise<void>;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Google account for email (v89): sign in with Google instead of an app
|
|
3
|
+
* password, for Send email steps and Inbox triggers alike.
|
|
4
|
+
*
|
|
5
|
+
* The person makes an OAuth client in their own Google Cloud project (a
|
|
6
|
+
* "Web application", with this console's callback as its redirect address)
|
|
7
|
+
* and pastes its ID and secret into Settings → Email. Connecting sends them
|
|
8
|
+
* to Google's consent screen; Google sends them back here with a one-time
|
|
9
|
+
* code (checked against a state and a PKCE verifier made for that visit),
|
|
10
|
+
* which becomes a refresh token. The client secret and refresh token live
|
|
11
|
+
* in a 0600 file beside the database, never in a column, a log or a page.
|
|
12
|
+
* Mail itself goes over IMAP and SMTP with XOAUTH2, so reading and sending
|
|
13
|
+
* are the same code as for any other mail server.
|
|
14
|
+
*/
|
|
15
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
16
|
+
import { chmodSync, existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
const FILE = "google-mail.json";
|
|
19
|
+
export const GOOGLE_AUTH = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
20
|
+
export const GOOGLE_TOKEN = "https://oauth2.googleapis.com/token";
|
|
21
|
+
const GOOGLE_REVOKE = "https://oauth2.googleapis.com/revoke";
|
|
22
|
+
/** Full mail access is what IMAP and SMTP sign-in needs; "email" says which address it is. */
|
|
23
|
+
export const GOOGLE_SCOPE = "https://mail.google.com/ openid email";
|
|
24
|
+
export const GOOGLE_CALLBACK = "/settings/google/callback";
|
|
25
|
+
export function readGoogleMail(dir) {
|
|
26
|
+
if (dir === null || !existsSync(join(dir, FILE)))
|
|
27
|
+
return null;
|
|
28
|
+
try {
|
|
29
|
+
const raw = JSON.parse(readFileSync(join(dir, FILE), "utf8"));
|
|
30
|
+
if (typeof raw["clientId"] !== "string" || typeof raw["clientSecret"] !== "string")
|
|
31
|
+
return null;
|
|
32
|
+
return { clientId: raw["clientId"], clientSecret: raw["clientSecret"], refreshToken: typeof raw["refreshToken"] === "string" ? raw["refreshToken"] : null, address: typeof raw["address"] === "string" ? raw["address"] : null };
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Connected: a refresh token and the address it belongs to. */
|
|
39
|
+
export function googleConnected(dir) {
|
|
40
|
+
const saved = readGoogleMail(dir);
|
|
41
|
+
return saved?.refreshToken && saved.address ? { address: saved.address } : null;
|
|
42
|
+
}
|
|
43
|
+
function write(dir, value) {
|
|
44
|
+
writeFileSync(join(dir, FILE), JSON.stringify(value), { mode: 0o600 });
|
|
45
|
+
chmodSync(join(dir, FILE), 0o600);
|
|
46
|
+
}
|
|
47
|
+
/** Save the OAuth client from Settings; an empty secret keeps the saved one. A new client forgets the old connection. */
|
|
48
|
+
export function saveGoogleClient(dir, input) {
|
|
49
|
+
const clientId = typeof input.clientId === "string" ? input.clientId.trim() : "";
|
|
50
|
+
if (!/^[0-9]{6,20}-[a-z0-9]{8,64}\.apps\.googleusercontent\.com$/.test(clientId))
|
|
51
|
+
return { ok: false, message: "That doesn't look like a Google OAuth client ID. It ends in .apps.googleusercontent.com." };
|
|
52
|
+
const before = readGoogleMail(dir);
|
|
53
|
+
const clientSecret = typeof input.clientSecret === "string" && input.clientSecret.trim() !== "" ? input.clientSecret.trim() : before?.clientId === clientId ? before.clientSecret : "";
|
|
54
|
+
if (!/^[A-Za-z0-9_-]{16,80}$/.test(clientSecret))
|
|
55
|
+
return { ok: false, message: "Paste the client secret from the same OAuth client." };
|
|
56
|
+
write(dir, { clientId, clientSecret, refreshToken: before?.clientId === clientId ? before.refreshToken : null, address: before?.clientId === clientId ? before.address : null });
|
|
57
|
+
return { ok: true };
|
|
58
|
+
}
|
|
59
|
+
/** Where to send the person to consent, and what to remember to check their return against. */
|
|
60
|
+
export function googleConsent(client, redirectUri, by, now = Date.now()) {
|
|
61
|
+
const state = randomBytes(24).toString("base64url"), verifier = randomBytes(48).toString("base64url");
|
|
62
|
+
const challenge = createHash("sha256").update(verifier).digest("base64url");
|
|
63
|
+
const url = `${GOOGLE_AUTH}?${new URLSearchParams({
|
|
64
|
+
client_id: client.clientId, redirect_uri: redirectUri, response_type: "code", scope: GOOGLE_SCOPE,
|
|
65
|
+
access_type: "offline", prompt: "consent", include_granted_scopes: "true", state, code_challenge: challenge, code_challenge_method: "S256",
|
|
66
|
+
})}`;
|
|
67
|
+
return { url, visit: { state, verifier, redirectUri, by, expires: now + 10 * 60_000 } };
|
|
68
|
+
}
|
|
69
|
+
const post = async (fetcher, url, form) => {
|
|
70
|
+
const response = await fetcher(url, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded", accept: "application/json" }, body: new URLSearchParams(form), signal: AbortSignal.timeout(20_000) });
|
|
71
|
+
let body = {};
|
|
72
|
+
try {
|
|
73
|
+
body = await response.json();
|
|
74
|
+
}
|
|
75
|
+
catch { /* an empty or broken answer */ }
|
|
76
|
+
return { status: response.status, body };
|
|
77
|
+
};
|
|
78
|
+
/** The address an ID token names; it came straight from Google's token endpoint over TLS. */
|
|
79
|
+
function addressOf(idToken) {
|
|
80
|
+
if (typeof idToken !== "string")
|
|
81
|
+
return null;
|
|
82
|
+
try {
|
|
83
|
+
const claims = JSON.parse(Buffer.from(idToken.split(".")[1] ?? "", "base64url").toString("utf8"));
|
|
84
|
+
return typeof claims["email"] === "string" && claims["email_verified"] !== false ? claims["email"] : null;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** The one-time code from Google's redirect, exchanged for a refresh token and saved. */
|
|
91
|
+
export async function finishGoogleConsent(dir, visit, code, fetcher) {
|
|
92
|
+
const client = readGoogleMail(dir);
|
|
93
|
+
if (client === null)
|
|
94
|
+
return { ok: false, message: "Save the Google OAuth client first." };
|
|
95
|
+
let answer;
|
|
96
|
+
try {
|
|
97
|
+
answer = await post(fetcher, GOOGLE_TOKEN, { code, client_id: client.clientId, client_secret: client.clientSecret, redirect_uri: visit.redirectUri, grant_type: "authorization_code", code_verifier: visit.verifier });
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return { ok: false, message: "Couldn't reach Google. Try connecting again." };
|
|
101
|
+
}
|
|
102
|
+
const refresh = answer.body["refresh_token"], scope = String(answer.body["scope"] ?? "");
|
|
103
|
+
if (answer.status !== 200 || typeof refresh !== "string")
|
|
104
|
+
return { ok: false, message: `Google didn't connect it${typeof answer.body["error_description"] === "string" ? `: ${String(answer.body["error_description"]).slice(0, 160)}` : "."}` };
|
|
105
|
+
if (!scope.split(" ").includes("https://mail.google.com/"))
|
|
106
|
+
return { ok: false, message: "Google didn't allow mail access. Connect again and tick the box to read, compose, send and delete mail." };
|
|
107
|
+
const address = addressOf(answer.body["id_token"]);
|
|
108
|
+
if (address === null)
|
|
109
|
+
return { ok: false, message: "Google didn't say which address this is. Connect again." };
|
|
110
|
+
write(dir, { ...client, refreshToken: refresh, address });
|
|
111
|
+
cached.delete(client.clientId);
|
|
112
|
+
return { ok: true, address };
|
|
113
|
+
}
|
|
114
|
+
const cached = new Map();
|
|
115
|
+
/** A fresh access token for IMAP and SMTP (kept in memory until a minute before it ends). */
|
|
116
|
+
export async function googleAccessToken(dir, fetcher, now = Date.now()) {
|
|
117
|
+
const saved = readGoogleMail(dir);
|
|
118
|
+
if (saved?.refreshToken == null || saved.address === null)
|
|
119
|
+
return { ok: false, said: "The Google account isn't connected. Connect it in Settings → Email.", permanent: true };
|
|
120
|
+
const hit = cached.get(saved.clientId);
|
|
121
|
+
if (hit !== undefined && hit.refresh === saved.refreshToken && hit.until > now)
|
|
122
|
+
return { ok: true, token: hit.token, address: saved.address };
|
|
123
|
+
let answer;
|
|
124
|
+
try {
|
|
125
|
+
answer = await post(fetcher, GOOGLE_TOKEN, { client_id: saved.clientId, client_secret: saved.clientSecret, refresh_token: saved.refreshToken, grant_type: "refresh_token" });
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return { ok: false, said: "Couldn't reach Google to sign in.", permanent: false };
|
|
129
|
+
}
|
|
130
|
+
const token = answer.body["access_token"];
|
|
131
|
+
if (answer.status !== 200 || typeof token !== "string") {
|
|
132
|
+
// invalid_grant: the person removed access, or the client changed. It won't fix itself.
|
|
133
|
+
const lost = answer.body["error"] === "invalid_grant";
|
|
134
|
+
return { ok: false, said: lost ? "Google no longer accepts this connection. Connect the account again in Settings → Email." : "Google didn't sign in just now.", permanent: lost };
|
|
135
|
+
}
|
|
136
|
+
cached.set(saved.clientId, { token, refresh: saved.refreshToken, until: now + Math.max(60, Number(answer.body["expires_in"]) || 3600) * 1000 - 60_000 });
|
|
137
|
+
return { ok: true, token, address: saved.address };
|
|
138
|
+
}
|
|
139
|
+
/** Disconnect: Google forgets the grant (best effort), and the file goes. */
|
|
140
|
+
export async function disconnectGoogle(dir, fetcher) {
|
|
141
|
+
const saved = readGoogleMail(dir);
|
|
142
|
+
if (saved?.refreshToken) {
|
|
143
|
+
try {
|
|
144
|
+
await fetcher(`${GOOGLE_REVOKE}?${new URLSearchParams({ token: saved.refreshToken })}`, { method: "POST", signal: AbortSignal.timeout(10_000) });
|
|
145
|
+
}
|
|
146
|
+
catch { /* the file goes either way */ }
|
|
147
|
+
}
|
|
148
|
+
if (saved !== null)
|
|
149
|
+
cached.delete(saved.clientId);
|
|
150
|
+
rmSync(join(dir, FILE), { force: true });
|
|
151
|
+
}
|
package/dist/grant.d.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission to write to somebody else's tracker.
|
|
3
|
+
*
|
|
4
|
+
* Everything up to here reads. This is the line, and it is drawn deliberately
|
|
5
|
+
* wide of where it needs to be: discovery indexes everything it can see, and
|
|
6
|
+
* Toolroll drives only what has been handed over, one repository and one
|
|
7
|
+
* backend at a time, by a person, in an act that shows its own consequences
|
|
8
|
+
* first.
|
|
9
|
+
*
|
|
10
|
+
* The grant is not a boolean. A yes/no flag would answer "may I write here"
|
|
11
|
+
* and leave every interesting question — write *what*, to *which* tasks, with
|
|
12
|
+
* *whose* credentials, visible to *whom* — to whatever the agent decided at
|
|
13
|
+
* 3am. So the record carries all of them, and the check is against the whole
|
|
14
|
+
* thing.
|
|
15
|
+
*
|
|
16
|
+
* Two defaults are load-bearing.
|
|
17
|
+
*
|
|
18
|
+
* **Only tasks we created or were given.** An operator enrolling a repository
|
|
19
|
+
* with four hundred open issues is not volunteering all four hundred to an
|
|
20
|
+
* unattended agent, and reading enrollment that way is how a tool ends up
|
|
21
|
+
* closing somebody's backlog overnight.
|
|
22
|
+
*
|
|
23
|
+
* **Closing is not included.** Creating a task is additive and reversible;
|
|
24
|
+
* transitioning one we own is ours to do; closing work somebody else filed is
|
|
25
|
+
* neither, so it is off unless explicitly granted. Widening takes another
|
|
26
|
+
* human action, which is the design's rule and not a nicety.
|
|
27
|
+
*
|
|
28
|
+
* Absence of a grant is denial. There is no implicit, inherited, or default
|
|
29
|
+
* permission anywhere in this module — an unknown repository is refused for
|
|
30
|
+
* the same reason an unknown lease is: silence is not consent.
|
|
31
|
+
*/
|
|
32
|
+
import { type ExecResult, type RunOptions } from "./exec.js";
|
|
33
|
+
/**
|
|
34
|
+
* What a write actually does, split by how much it can hurt.
|
|
35
|
+
*
|
|
36
|
+
* `close` is separate from `transition` because "mark the task I am working on
|
|
37
|
+
* as done" and "close the issue somebody filed in March" are the same verb to
|
|
38
|
+
* a database and completely different acts to a person.
|
|
39
|
+
*/
|
|
40
|
+
export type MutationClass = "create" | "transition" | "edge" | "hold" | "close" | "comment";
|
|
41
|
+
export declare const MUTATION_CLASSES: readonly MutationClass[];
|
|
42
|
+
/** Granted unless asked otherwise; `close` is the deliberate omission. */
|
|
43
|
+
export declare const DEFAULT_MUTATIONS: readonly MutationClass[];
|
|
44
|
+
/**
|
|
45
|
+
* Which tasks the grant covers. `ours` means the ones Toolroll created or
|
|
46
|
+
* was explicitly given — the design's default, and the one that keeps an
|
|
47
|
+
* enrolment from becoming an amnesty over an entire backlog.
|
|
48
|
+
*/
|
|
49
|
+
export type TaskSelector = "ours" | "all";
|
|
50
|
+
export type BackendGrant = {
|
|
51
|
+
repo: string;
|
|
52
|
+
backend: string;
|
|
53
|
+
/** Exact paths, or `owner/name` for a GitHub tracker. Never a wildcard. */
|
|
54
|
+
paths: string[];
|
|
55
|
+
mutations: MutationClass[];
|
|
56
|
+
selector: TaskSelector;
|
|
57
|
+
/** Named, never held: the value lives on the runner. */
|
|
58
|
+
credentialScope: string | null;
|
|
59
|
+
/** Whether these writes will show up in `git status` and in history. */
|
|
60
|
+
observedByGit: boolean;
|
|
61
|
+
grantedAt: string;
|
|
62
|
+
grantedBy: string;
|
|
63
|
+
/** External dispatch (v20): a SEPARATE authority from tracker writes —
|
|
64
|
+
* "this plane will BUILD what this tracker nominates, under scopes
|
|
65
|
+
* approved here". Never in any default; its own explicit yes. */
|
|
66
|
+
dispatch?: boolean;
|
|
67
|
+
/** The one exact owner/name this dispatch grant binds. Required when
|
|
68
|
+
* dispatch is true (enforced in saveGrant). */
|
|
69
|
+
remoteRepo?: string | null;
|
|
70
|
+
/** This plane's identity for the tracker-side marker — detection of a
|
|
71
|
+
* second plane, never a distributed lease. Minted at grant creation. */
|
|
72
|
+
planeId?: string | null;
|
|
73
|
+
/** Fail-closed dispatch block: set by marker verification and sync
|
|
74
|
+
* failures; null = clear. Admission refuses while set. */
|
|
75
|
+
dispatchBlocked?: "pending-marker" | "unreachable" | "foreign" | "missing" | "multiple-or-malformed" | null;
|
|
76
|
+
dispatchBlockedAt?: string | null;
|
|
77
|
+
dispatchBlockedDetail?: string | null;
|
|
78
|
+
};
|
|
79
|
+
/** Where a task came from, which is what `selector` discriminates on. */
|
|
80
|
+
export type TaskOrigin = "ours" | "theirs";
|
|
81
|
+
export type WriteRequest = {
|
|
82
|
+
repo: string;
|
|
83
|
+
backend: string;
|
|
84
|
+
mutation: MutationClass;
|
|
85
|
+
origin: TaskOrigin;
|
|
86
|
+
/** The file or repository being written, when the backend has one. */
|
|
87
|
+
path?: string;
|
|
88
|
+
};
|
|
89
|
+
export type Verdict = {
|
|
90
|
+
ok: true;
|
|
91
|
+
} | {
|
|
92
|
+
ok: false;
|
|
93
|
+
reason: DenialReason;
|
|
94
|
+
message: string;
|
|
95
|
+
};
|
|
96
|
+
/** Stable tokens: an agent branches on these, a person reads the message. */
|
|
97
|
+
export type DenialReason = "no-grant" | "mutation" | "selector" | "path";
|
|
98
|
+
/**
|
|
99
|
+
* Whether this write is inside the grant.
|
|
100
|
+
*
|
|
101
|
+
* Written as a pure function over the whole grant so the policy can be read in
|
|
102
|
+
* one place and tested without a database, a repository, or a network — the
|
|
103
|
+
* three things that make a permission check hard to trust.
|
|
104
|
+
*/
|
|
105
|
+
export declare function permits(grant: BackendGrant | null, request: WriteRequest): Verdict;
|
|
106
|
+
export type Runner = (file: string, args: readonly string[], options?: RunOptions) => Promise<ExecResult>;
|
|
107
|
+
export type ProposeOptions = {
|
|
108
|
+
repo: string;
|
|
109
|
+
backend: string;
|
|
110
|
+
paths: string[];
|
|
111
|
+
mutations?: readonly MutationClass[];
|
|
112
|
+
selector?: TaskSelector;
|
|
113
|
+
credentialScope?: string | null;
|
|
114
|
+
grantedBy?: string;
|
|
115
|
+
now: Date;
|
|
116
|
+
runner?: Runner;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Build the grant that would be created, including the one fact an operator
|
|
120
|
+
* cannot easily check for themselves: whether these writes will be visible.
|
|
121
|
+
*
|
|
122
|
+
* A backend that stores its data in a tracked directory means every task
|
|
123
|
+
* transition shows up in `git status` and eventually in history — which is
|
|
124
|
+
* either exactly what somebody wants, or a nasty surprise on their next
|
|
125
|
+
* commit. It is asked of git rather than assumed, because the answer differs
|
|
126
|
+
* per repository and the guess would be wrong half the time.
|
|
127
|
+
*/
|
|
128
|
+
export declare function proposeGrant(options: ProposeOptions): Promise<BackendGrant>;
|
|
129
|
+
/**
|
|
130
|
+
* The grant, in the words an operator needs before agreeing to it — what may
|
|
131
|
+
* be touched, what may be done, to which tasks, and whether anyone else will
|
|
132
|
+
* see it happen.
|
|
133
|
+
*/
|
|
134
|
+
export declare function describeGrant(grant: BackendGrant): string[];
|
|
135
|
+
/** What is not granted, which is the half people forget to read. */
|
|
136
|
+
export declare function describeWithheld(grant: BackendGrant): string[];
|