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,559 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OS process containment (docs/PROCESS_CONTAINMENT_PLAN.md).
|
|
3
|
+
*
|
|
4
|
+
* The stop contract before this module was OBSERVATIONAL: a provider's
|
|
5
|
+
* tree is scanned every 500 ms, and a double fork can reparent between
|
|
6
|
+
* scans. This module adds an OS boundary where the OS has one, and says
|
|
7
|
+
* exactly what it has where it has none:
|
|
8
|
+
*
|
|
9
|
+
* linux a delegated cgroup v2 directory per spawn. The target is
|
|
10
|
+
* placed in it BEFORE it executes (the `sh` prelude below writes
|
|
11
|
+
* its own pid, then execs the target with argv verbatim), so a
|
|
12
|
+
* setsid/double-fork helper is born inside and dies with
|
|
13
|
+
* `cgroup.kill`. Emptiness is the kernel's own `cgroup.events`
|
|
14
|
+
* populated flag — never a transport exit. A detached janitor
|
|
15
|
+
* kills the cgroup when this worker dies.
|
|
16
|
+
* win32 a Job Object with kill-on-close and no breakaway, owned by a
|
|
17
|
+
* PowerShell helper OUTSIDE the job that creates the target
|
|
18
|
+
* suspended, assigns it, then resumes it. The helper's death —
|
|
19
|
+
* including this worker's death, which closes its stdin — closes
|
|
20
|
+
* the handle and the job kills everything in it.
|
|
21
|
+
* darwin nothing. There is no supported cgroup/Job Object equivalent;
|
|
22
|
+
* NOTE_TRACK/NOTE_CHILD have been unsupported since 10.5 and
|
|
23
|
+
* launchd's AbandonProcessGroup only covers a process group. The
|
|
24
|
+
* observational mode stays, is reported as exactly that, and a
|
|
25
|
+
* REQUIRED policy refuses before any target executes.
|
|
26
|
+
*
|
|
27
|
+
* Three policies: `observed` (the behaviour every existing installation
|
|
28
|
+
* has), `preferred` (native when this machine can, observed — and said
|
|
29
|
+
* so — when it cannot), `required` (native or a refusal; never a silent
|
|
30
|
+
* downgrade). The policy is decided ONCE per controller process from the
|
|
31
|
+
* operator's flag or environment and pinned; nothing a child supplies and
|
|
32
|
+
* no corrupt value can weaken a pinned requirement.
|
|
33
|
+
*/
|
|
34
|
+
import { spawn } from "node:child_process";
|
|
35
|
+
import { randomBytes } from "node:crypto";
|
|
36
|
+
import { existsSync, mkdirSync, readFileSync, rmdirSync, writeFileSync } from "node:fs";
|
|
37
|
+
import { captureCgroupIdentity, cgroupEmptiness, containerEmptiness } from "./container-state.js";
|
|
38
|
+
import { createServer } from "node:net";
|
|
39
|
+
import { dirname, join } from "node:path";
|
|
40
|
+
import { fileURLToPath } from "node:url";
|
|
41
|
+
import { envValue } from "./names.js";
|
|
42
|
+
export const CONTAINMENT_POLICIES = ["observed", "preferred", "required"];
|
|
43
|
+
/** The environment name the controller reads when no flag names a policy. */
|
|
44
|
+
export const CONTAINMENT_ENV = "TOOLROLL_CONTAINMENT";
|
|
45
|
+
/**
|
|
46
|
+
* Parse an operator's policy word. Absent means `observed` — every
|
|
47
|
+
* existing installation keeps its behaviour. Anything present must be
|
|
48
|
+
* one of the three words exactly: a corrupt value is a refusal, never a
|
|
49
|
+
* quiet fall back to observed (which would turn a requirement off).
|
|
50
|
+
*/
|
|
51
|
+
export function parseContainmentPolicy(raw) {
|
|
52
|
+
if (raw === undefined || raw === null)
|
|
53
|
+
return { ok: true, policy: "observed", source: "default" };
|
|
54
|
+
const word = raw.trim().toLowerCase();
|
|
55
|
+
if (CONTAINMENT_POLICIES.includes(word)) {
|
|
56
|
+
return { ok: true, policy: word, source: "explicit" };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
problem: `containment policy ${JSON.stringify(raw)} is not one of ${CONTAINMENT_POLICIES.join(", ")} — nothing is assumed from an unreadable value`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const DARWIN_DETAIL = "macOS has no supported cgroup or Job Object equivalent (kqueue NOTE_TRACK/NOTE_CHILD have been unsupported since 10.5; launchd AbandonProcessGroup covers only a process group) — containment stays observational here; for native containment run this runner on a Linux VM or Linux runner with a delegated cgroup v2 (or on Windows)";
|
|
64
|
+
/** The cgroup path of this process from /proc/self/cgroup (`0::/path`). */
|
|
65
|
+
export function ownCgroupPath(selfCgroup) {
|
|
66
|
+
for (const line of selfCgroup.split("\n")) {
|
|
67
|
+
const match = /^0::(\/.*)$/.exec(line.trim());
|
|
68
|
+
if (match !== null)
|
|
69
|
+
return match[1] ?? null;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* What this machine can do, established by DOING it: on Linux, a child
|
|
75
|
+
* cgroup is created and removed inside this process's own cgroup; a
|
|
76
|
+
* mount that is not v2, a cgroup that is not delegated to this user, or
|
|
77
|
+
* an unreadable /proc all answer unavailable with the reason. Nothing
|
|
78
|
+
* here can be overridden by an environment variable into a claim the OS
|
|
79
|
+
* did not make.
|
|
80
|
+
*/
|
|
81
|
+
export function probeContainmentCapability(options = {}) {
|
|
82
|
+
const platform = options.platform ?? process.platform;
|
|
83
|
+
if (platform === "linux")
|
|
84
|
+
return probeCgroup2(options);
|
|
85
|
+
if (platform === "win32")
|
|
86
|
+
return probeJobObject(options);
|
|
87
|
+
if (platform === "darwin")
|
|
88
|
+
return { platform, backend: null, available: false, detail: DARWIN_DETAIL };
|
|
89
|
+
return { platform, backend: null, available: false, detail: `${platform} has no native containment backend in this build — containment stays observational` };
|
|
90
|
+
}
|
|
91
|
+
function probeCgroup2(options) {
|
|
92
|
+
const mount = options.cgroupMount ?? "/sys/fs/cgroup";
|
|
93
|
+
const platform = "linux";
|
|
94
|
+
if (!existsSync(join(mount, "cgroup.controllers"))) {
|
|
95
|
+
return { platform, backend: "cgroup2", available: false, detail: `${mount} is not a cgroup v2 (unified) hierarchy — native containment needs cgroup2; the legacy v1 layout is not supported` };
|
|
96
|
+
}
|
|
97
|
+
let selfCgroup;
|
|
98
|
+
try {
|
|
99
|
+
selfCgroup = options.selfCgroup ?? readFileSync("/proc/self/cgroup", "utf8");
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return { platform, backend: "cgroup2", available: false, detail: `/proc/self/cgroup could not be read (${error.message}) — this process's cgroup is unknown` };
|
|
103
|
+
}
|
|
104
|
+
const own = ownCgroupPath(selfCgroup);
|
|
105
|
+
if (own === null) {
|
|
106
|
+
return { platform, backend: "cgroup2", available: false, detail: "/proc/self/cgroup names no unified (0::) cgroup for this process" };
|
|
107
|
+
}
|
|
108
|
+
const root = join(mount, own);
|
|
109
|
+
const probe = join(root, `so-probe-${process.pid}-${randomBytes(4).toString("hex")}`);
|
|
110
|
+
try {
|
|
111
|
+
mkdirSync(probe);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
const code = error.code ?? "error";
|
|
115
|
+
return {
|
|
116
|
+
platform,
|
|
117
|
+
backend: "cgroup2",
|
|
118
|
+
available: false,
|
|
119
|
+
cgroupRoot: root,
|
|
120
|
+
detail: `cgroup ${own} is not delegated to this user (${code} creating a child cgroup) — delegate it (systemd: a user session with Delegate=yes, or chown a leaf cgroup to this uid and start the controller inside it) or run under observed containment`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const hasKill = existsSync(join(probe, "cgroup.kill"));
|
|
124
|
+
try {
|
|
125
|
+
rmdirSync(probe);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// A probe cgroup that cannot be removed is empty and harmless; the
|
|
129
|
+
// capability is still real. It is named so a person can remove it.
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
platform,
|
|
133
|
+
backend: "cgroup2",
|
|
134
|
+
available: hasKill,
|
|
135
|
+
cgroupRoot: root,
|
|
136
|
+
cgroupKill: hasKill,
|
|
137
|
+
detail: hasKill ? `delegated cgroup v2 at ${own} with cgroup.kill` : `cgroup v2 at ${own} lacks atomic cgroup.kill — Linux 5.14+ with writable cgroup.kill is required`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
export function jobObjectHelperPath() {
|
|
141
|
+
return fileURLToPath(new URL("./job-object-helper.ps1", import.meta.url));
|
|
142
|
+
}
|
|
143
|
+
function probeJobObject(options) {
|
|
144
|
+
const platform = "win32";
|
|
145
|
+
const helper = options.helperPath ?? jobObjectHelperPath();
|
|
146
|
+
if (!existsSync(helper)) {
|
|
147
|
+
return { platform, backend: "job-object", available: false, detail: `the Job Object helper is missing at ${helper} — reinstall the package` };
|
|
148
|
+
}
|
|
149
|
+
const powershell = options.powershellPath ?? findPowershell();
|
|
150
|
+
if (powershell === null) {
|
|
151
|
+
return { platform, backend: "job-object", available: false, detail: "powershell.exe was not found on PATH or in System32 — the Job Object helper needs Windows PowerShell 5.1 or later" };
|
|
152
|
+
}
|
|
153
|
+
return { platform, backend: "job-object", available: true, helper, powershell, detail: `Job Object (kill-on-close, no breakaway) through ${helper}` };
|
|
154
|
+
}
|
|
155
|
+
function findPowershell() {
|
|
156
|
+
const root = process.env["SystemRoot"] ?? process.env["WINDIR"] ?? "C:\\Windows";
|
|
157
|
+
const candidates = [join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")];
|
|
158
|
+
for (const dir of (process.env["PATH"] ?? "").split(";")) {
|
|
159
|
+
if (dir !== "")
|
|
160
|
+
candidates.push(join(dir, "powershell.exe"), join(dir, "pwsh.exe"));
|
|
161
|
+
}
|
|
162
|
+
for (const candidate of candidates)
|
|
163
|
+
if (existsSync(candidate))
|
|
164
|
+
return candidate;
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
/** The truthful composition of what was asked and what the OS offers. */
|
|
168
|
+
export function effectiveContainment(policy, capability) {
|
|
169
|
+
if (policy === "observed")
|
|
170
|
+
return { policy, capability, mode: "observed", downgraded: false, refusal: null };
|
|
171
|
+
if (capability.available)
|
|
172
|
+
return { policy, capability, mode: "native", downgraded: false, refusal: null };
|
|
173
|
+
if (policy === "preferred")
|
|
174
|
+
return { policy, capability, mode: "observed", downgraded: true, refusal: null };
|
|
175
|
+
return {
|
|
176
|
+
policy,
|
|
177
|
+
capability,
|
|
178
|
+
mode: "observed",
|
|
179
|
+
downgraded: false,
|
|
180
|
+
refusal: `native process containment is required but unavailable on this ${capability.platform} runner: ${capability.detail}. No provider, setup or check process runs until containment is available or the policy is set to observed/preferred explicitly`,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/** One-line status words for `daemon status`, `up`, and the certificate. */
|
|
184
|
+
export function describeContainment(effective) {
|
|
185
|
+
const backend = effective.capability.backend ?? "none";
|
|
186
|
+
if (effective.refusal !== null)
|
|
187
|
+
return `containment: REQUIRED but unavailable (${backend}) — refusing spawns: ${effective.capability.detail}`;
|
|
188
|
+
if (effective.mode === "native")
|
|
189
|
+
return `containment: native ${backend} (${effective.policy}) — ${effective.capability.detail}`;
|
|
190
|
+
if (effective.downgraded)
|
|
191
|
+
return `containment: observed (preferred native, ${backend} unavailable) — ${effective.capability.detail}`;
|
|
192
|
+
return `containment: observed (${effective.policy}) — ${effective.capability.detail}`;
|
|
193
|
+
}
|
|
194
|
+
/** The status envelope's shape: no paths a person did not already know, no secrets. */
|
|
195
|
+
export function containmentStatus(effective) {
|
|
196
|
+
return {
|
|
197
|
+
policy: effective.policy,
|
|
198
|
+
mode: effective.mode,
|
|
199
|
+
backend: effective.capability.backend,
|
|
200
|
+
available: effective.capability.available,
|
|
201
|
+
downgraded: effective.downgraded,
|
|
202
|
+
refusal: effective.refusal,
|
|
203
|
+
detail: effective.capability.detail,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// ---- the pinned policy --------------------------------------------------------
|
|
207
|
+
let pinned = null;
|
|
208
|
+
/**
|
|
209
|
+
* Pin this process's containment ONCE, from the operator's own word. A
|
|
210
|
+
* second pin may only tighten: once `required` stands nothing pins it
|
|
211
|
+
* weaker — the only way down is a new process started with a new word.
|
|
212
|
+
*/
|
|
213
|
+
export function pinContainment(effective) {
|
|
214
|
+
if (pinned !== null && rank(effective.policy) < rank(pinned.policy)) {
|
|
215
|
+
throw new Error(`containment is pinned at ${pinned.policy} for this process; ${effective.policy} would weaken it`);
|
|
216
|
+
}
|
|
217
|
+
pinned = effective;
|
|
218
|
+
return effective;
|
|
219
|
+
}
|
|
220
|
+
function rank(policy) {
|
|
221
|
+
return policy === "required" ? 2 : policy === "preferred" ? 1 : 0;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Resolve the policy from a flag word, then the environment, then the
|
|
225
|
+
* default, and pin it. A corrupt word is a refusal to start, never a
|
|
226
|
+
* default: the caller reports `problem` and exits.
|
|
227
|
+
*/
|
|
228
|
+
export function resolveContainment(flagWord, env = process.env) {
|
|
229
|
+
const parsed = parseContainmentPolicy(flagWord ?? envValue(env, "CONTAINMENT"));
|
|
230
|
+
if (!parsed.ok)
|
|
231
|
+
return parsed;
|
|
232
|
+
return { ok: true, effective: pinContainment(effectiveContainment(parsed.policy, probeContainmentCapability())) };
|
|
233
|
+
}
|
|
234
|
+
/** What every containable spawn consults. Unpinned = observed, exactly as before this module. */
|
|
235
|
+
export function currentContainment() {
|
|
236
|
+
if (pinned === null)
|
|
237
|
+
pinned = effectiveContainment("observed", probeContainmentCapability());
|
|
238
|
+
return pinned;
|
|
239
|
+
}
|
|
240
|
+
/** Tests only. */
|
|
241
|
+
export function resetContainmentForTests() {
|
|
242
|
+
pinned = null;
|
|
243
|
+
}
|
|
244
|
+
let factoryOverride = null;
|
|
245
|
+
/** Tests: substitute the OS object (a scripted or file-backed stand-in). */
|
|
246
|
+
export function overrideContainerFactoryForTests(factory) {
|
|
247
|
+
factoryOverride = factory;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Make the OS object for one spawn, or say why not. `null` when the
|
|
251
|
+
* effective mode is observed; a `refused` when native containment was
|
|
252
|
+
* demanded and this spawn cannot have it — the caller must not spawn.
|
|
253
|
+
*/
|
|
254
|
+
export function createContainer(effective, label) {
|
|
255
|
+
if (effective.refusal !== null)
|
|
256
|
+
return { refused: effective.refusal };
|
|
257
|
+
if (effective.mode !== "native")
|
|
258
|
+
return { container: null };
|
|
259
|
+
try {
|
|
260
|
+
if (factoryOverride !== null)
|
|
261
|
+
return { container: factoryOverride(effective, label) };
|
|
262
|
+
if (effective.capability.backend === "cgroup2")
|
|
263
|
+
return { container: cgroup2Container(effective.capability, label) };
|
|
264
|
+
if (effective.capability.backend === "job-object")
|
|
265
|
+
return { container: jobObjectContainer(effective.capability, label) };
|
|
266
|
+
return { refused: `no native backend for ${effective.capability.platform}` };
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
return { refused: `native containment could not be established for this spawn (${error instanceof Error ? error.message : String(error)}) — nothing ran` };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
273
|
+
/**
|
|
274
|
+
* The cgroup2 prelude: join, confirm on fd 3, close it, exec the target
|
|
275
|
+
* with argv exactly as given (positional parameters, never re-parsed).
|
|
276
|
+
* A join that fails exits 126 before the target can run.
|
|
277
|
+
*/
|
|
278
|
+
export const CGROUP2_PRELUDE = 'echo "$$" > "$1/cgroup.procs" || exit 126\nprintf "attached\\n" >&3 || exit 126\nIFS= read -r permit <&3 || exit 126\n[ "$permit" = go ] || exit 126\nexec 3>&-\nshift\nexec "$@"';
|
|
279
|
+
/** The janitor: waits for this worker's pipe to close, then kills and removes the cgroup. */
|
|
280
|
+
export const CGROUP2_JANITOR = [
|
|
281
|
+
'd="$1"',
|
|
282
|
+
'printf "ready\\n"',
|
|
283
|
+
"cat >/dev/null 2>&1",
|
|
284
|
+
'echo 1 > "$d/cgroup.kill" 2>/dev/null',
|
|
285
|
+
"i=0",
|
|
286
|
+
'while [ "$i" -lt 100 ] && [ -d "$d" ]; do',
|
|
287
|
+
' if /usr/bin/grep -qx "populated 0" "$d/cgroup.events" 2>/dev/null; then rmdir "$d" 2>/dev/null && break; fi',
|
|
288
|
+
" i=$((i+1)); sleep 0.1",
|
|
289
|
+
"done",
|
|
290
|
+
"exit 0",
|
|
291
|
+
].join("\n");
|
|
292
|
+
export function safeContainerLabel(label) {
|
|
293
|
+
return label.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "run";
|
|
294
|
+
}
|
|
295
|
+
/** A cgroup v2 leaf under the delegated root. Exported for the native test's direct use. */
|
|
296
|
+
export function cgroup2Container(capability, label, mountOverride) {
|
|
297
|
+
const root = mountOverride ?? capability.cgroupRoot;
|
|
298
|
+
if (root === undefined)
|
|
299
|
+
throw new Error("the cgroup2 capability names no delegated root");
|
|
300
|
+
const dir = join(root, `so-${safeContainerLabel(label)}-${randomBytes(6).toString("hex")}`);
|
|
301
|
+
mkdirSync(dir);
|
|
302
|
+
let identity;
|
|
303
|
+
try {
|
|
304
|
+
identity = captureCgroupIdentity(dir);
|
|
305
|
+
// Exercise the exact kernel operation while the object is still empty.
|
|
306
|
+
writeFileSync(join(dir, "cgroup.kill"), "1");
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
try {
|
|
310
|
+
rmdirSync(dir);
|
|
311
|
+
}
|
|
312
|
+
catch { }
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
const populated = () => {
|
|
316
|
+
const state = cgroupEmptiness(dir, identity);
|
|
317
|
+
return state === "empty" ? false : state === "populated" ? true : null;
|
|
318
|
+
};
|
|
319
|
+
const killNow = () => { try {
|
|
320
|
+
writeFileSync(join(dir, "cgroup.kill"), "1");
|
|
321
|
+
}
|
|
322
|
+
catch { } };
|
|
323
|
+
const janitor = spawn("/bin/sh", ["-c", CGROUP2_JANITOR, "so-janitor", dir], { detached: true, stdio: ["pipe", "pipe", "ignore"] });
|
|
324
|
+
const ready = new Promise(resolve => {
|
|
325
|
+
const timer = setTimeout(() => resolve(false), 5_000);
|
|
326
|
+
const done = (ok) => { clearTimeout(timer); resolve(ok); };
|
|
327
|
+
janitor.stdout?.once("data", chunk => done(String(chunk) === "ready\n"));
|
|
328
|
+
janitor.once("error", () => done(false));
|
|
329
|
+
janitor.once("exit", () => { done(false); killNow(); });
|
|
330
|
+
});
|
|
331
|
+
janitor.stdin?.on("error", () => { });
|
|
332
|
+
janitor.unref();
|
|
333
|
+
const waitEmpty = async (timeoutMs) => {
|
|
334
|
+
const deadline = Date.now() + timeoutMs;
|
|
335
|
+
for (;;) {
|
|
336
|
+
if (populated() === false)
|
|
337
|
+
return true;
|
|
338
|
+
if (Date.now() >= deadline)
|
|
339
|
+
return false;
|
|
340
|
+
await sleep(20);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
return {
|
|
344
|
+
backend: "cgroup2",
|
|
345
|
+
id: dir,
|
|
346
|
+
identity,
|
|
347
|
+
launch(file, args) {
|
|
348
|
+
return {
|
|
349
|
+
file: "/bin/sh",
|
|
350
|
+
args: ["-c", CGROUP2_PRELUDE, "so-contain", dir, file, ...args],
|
|
351
|
+
extraStdio: ["pipe"],
|
|
352
|
+
attach(child) {
|
|
353
|
+
return new Promise(resolve => {
|
|
354
|
+
const channel = child.stdio[3];
|
|
355
|
+
let settled = false;
|
|
356
|
+
const timer = setTimeout(() => settle({ ok: false, detail: "containment admission timed out" }), 6_000);
|
|
357
|
+
const settle = (outcome) => {
|
|
358
|
+
if (settled)
|
|
359
|
+
return;
|
|
360
|
+
settled = true;
|
|
361
|
+
clearTimeout(timer);
|
|
362
|
+
if (!outcome.ok) {
|
|
363
|
+
channel?.destroy();
|
|
364
|
+
killNow();
|
|
365
|
+
}
|
|
366
|
+
resolve(outcome);
|
|
367
|
+
};
|
|
368
|
+
if (!channel) {
|
|
369
|
+
settle({ ok: false, detail: "containment admission channel missing" });
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
let seen = "";
|
|
373
|
+
channel.on("data", (chunk) => {
|
|
374
|
+
seen += String(chunk);
|
|
375
|
+
if (seen.length > 64) {
|
|
376
|
+
settle({ ok: false, detail: "invalid containment admission" });
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (seen === "attached\n")
|
|
380
|
+
void ready.then(ok => {
|
|
381
|
+
if (settled)
|
|
382
|
+
return;
|
|
383
|
+
if (!ok || janitor.exitCode !== null || janitor.signalCode !== null) {
|
|
384
|
+
settle({ ok: false, detail: "containment cleanup guardian unavailable" });
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
// onSpawn already durably recorded custody. EOF before this
|
|
388
|
+
// authorization leaves the target unexecuted inside the cgroup.
|
|
389
|
+
channel.write("go\n", error => settle(error ? { ok: false, detail: String(error) } : { ok: true }));
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
channel.on("end", () => settle({ ok: false, detail: "containment admission closed before authorization" }));
|
|
393
|
+
channel.on("error", error => settle({ ok: false, detail: String(error) }));
|
|
394
|
+
child.on("error", error => settle({ ok: false, detail: String(error) }));
|
|
395
|
+
});
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
},
|
|
399
|
+
async kill(timeoutMs = 5_000) {
|
|
400
|
+
killNow();
|
|
401
|
+
return waitEmpty(timeoutMs);
|
|
402
|
+
},
|
|
403
|
+
populated,
|
|
404
|
+
waitEmpty,
|
|
405
|
+
release() {
|
|
406
|
+
try {
|
|
407
|
+
rmdirSync(dir);
|
|
408
|
+
}
|
|
409
|
+
catch { /* still populated: the janitor or recovery finishes it */ }
|
|
410
|
+
try {
|
|
411
|
+
janitor?.stdin?.end();
|
|
412
|
+
}
|
|
413
|
+
catch { /* already gone */ }
|
|
414
|
+
},
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* A Windows Job Object owned by a PowerShell helper outside the job. The
|
|
419
|
+
* helper connects to a named pipe this process listens on and reports
|
|
420
|
+
* `attached` only after CreateProcess(CREATE_SUSPENDED) + AssignProcess
|
|
421
|
+
* ToJobObject + ResumeThread; `failed <detail>` means the target never
|
|
422
|
+
* ran. `kill` asks for TerminateJobObject over the same pipe and waits
|
|
423
|
+
* for the helper's `empty` reply. Physical Windows is where this runs;
|
|
424
|
+
* the CI windows job exercises it.
|
|
425
|
+
*/
|
|
426
|
+
export function jobObjectContainer(capability, label) {
|
|
427
|
+
if (capability.helper === undefined || capability.powershell === undefined)
|
|
428
|
+
throw new Error("the Job Object capability names no helper");
|
|
429
|
+
const name = `so-${safeContainerLabel(label)}-${randomBytes(32).toString("hex")}`;
|
|
430
|
+
const pipe = `\\\\.\\pipe\\${name}`;
|
|
431
|
+
const id = `Global\\${name}`;
|
|
432
|
+
const identity = "job-object-v1";
|
|
433
|
+
let connection = null;
|
|
434
|
+
let state = "pending";
|
|
435
|
+
let failure = null;
|
|
436
|
+
let authorized = false;
|
|
437
|
+
let cancelled = false;
|
|
438
|
+
let helperChild = null;
|
|
439
|
+
const fail = (detail) => { failure ??= detail; if (state !== "empty")
|
|
440
|
+
state = "lost"; connection?.destroy(); };
|
|
441
|
+
const authorize = () => {
|
|
442
|
+
if (authorized && state === "ready")
|
|
443
|
+
connection?.write(cancelled ? "kill\n" : "go\n");
|
|
444
|
+
};
|
|
445
|
+
const server = createServer(socket => {
|
|
446
|
+
if (connection !== null || failure !== null) {
|
|
447
|
+
socket.destroy();
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
connection = socket;
|
|
451
|
+
socket.setEncoding("utf8");
|
|
452
|
+
let buffered = "";
|
|
453
|
+
socket.on("data", chunk => {
|
|
454
|
+
buffered += String(chunk);
|
|
455
|
+
if (buffered.length > 4096) {
|
|
456
|
+
fail("oversized containment control frame");
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
let cut;
|
|
460
|
+
while ((cut = buffered.indexOf("\n")) !== -1) {
|
|
461
|
+
const line = buffered.slice(0, cut);
|
|
462
|
+
buffered = buffered.slice(cut + 1);
|
|
463
|
+
if (line === "ready" && state === "pending") {
|
|
464
|
+
state = "ready";
|
|
465
|
+
authorize();
|
|
466
|
+
}
|
|
467
|
+
else if (line === "attached" && state === "ready" && authorized)
|
|
468
|
+
state = "attached";
|
|
469
|
+
else if (line === "empty") {
|
|
470
|
+
state = "empty";
|
|
471
|
+
// The helper may have a pending control read. Close now, on its
|
|
472
|
+
// kernel empty proof, so .NET disposal never waits for an EOF that
|
|
473
|
+
// transport `close` would otherwise delay until after helper exit.
|
|
474
|
+
socket.destroy();
|
|
475
|
+
}
|
|
476
|
+
else if (line.startsWith("failed "))
|
|
477
|
+
fail(line.slice(7));
|
|
478
|
+
else {
|
|
479
|
+
fail("invalid containment control sequence");
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
socket.on("error", error => fail(String(error)));
|
|
485
|
+
socket.on("close", () => { if (state !== "empty")
|
|
486
|
+
fail("containment helper lost its control channel"); });
|
|
487
|
+
});
|
|
488
|
+
server.on("error", error => fail(String(error)));
|
|
489
|
+
server.listen(pipe);
|
|
490
|
+
const populated = () => {
|
|
491
|
+
if (state === "empty")
|
|
492
|
+
return false;
|
|
493
|
+
if (state === "attached")
|
|
494
|
+
return true;
|
|
495
|
+
if (state !== "lost")
|
|
496
|
+
return null;
|
|
497
|
+
const evidence = containerEmptiness("job-object", id, process.platform, identity);
|
|
498
|
+
return evidence === "empty" ? false : evidence === "populated" ? true : null;
|
|
499
|
+
};
|
|
500
|
+
const waitEmpty = async (timeoutMs) => {
|
|
501
|
+
const deadline = performance.now() + timeoutMs;
|
|
502
|
+
do {
|
|
503
|
+
if (populated() === false)
|
|
504
|
+
return true;
|
|
505
|
+
await sleep(25);
|
|
506
|
+
} while (performance.now() < deadline);
|
|
507
|
+
return false;
|
|
508
|
+
};
|
|
509
|
+
return {
|
|
510
|
+
backend: "job-object", id, identity,
|
|
511
|
+
launch(file, args) {
|
|
512
|
+
return {
|
|
513
|
+
file: capability.powershell,
|
|
514
|
+
args: ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", capability.helper, "-Pipe", pipe, "-Encoded", Buffer.from(JSON.stringify([file, ...args]), "utf8").toString("base64"), "-JobName", id],
|
|
515
|
+
extraStdio: [],
|
|
516
|
+
async attach(child) {
|
|
517
|
+
// Called only after the durable onSpawn callback succeeded.
|
|
518
|
+
helperChild = child;
|
|
519
|
+
authorized = true;
|
|
520
|
+
authorize();
|
|
521
|
+
child.once("error", error => fail(String(error)));
|
|
522
|
+
const deadline = performance.now() + 15_000;
|
|
523
|
+
while (state !== "attached" && state !== "empty" && failure === null && child.exitCode === null && child.signalCode === null && performance.now() < deadline)
|
|
524
|
+
await sleep(20);
|
|
525
|
+
if ((state === "attached" || state === "empty") && failure === null)
|
|
526
|
+
return { ok: true };
|
|
527
|
+
fail(failure ?? "containment helper admission timed out or exited");
|
|
528
|
+
return { ok: false, detail: failure };
|
|
529
|
+
},
|
|
530
|
+
};
|
|
531
|
+
},
|
|
532
|
+
async kill(timeoutMs = 5_000) {
|
|
533
|
+
cancelled = true;
|
|
534
|
+
try {
|
|
535
|
+
connection?.write("kill\n");
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
fail("containment control channel unavailable");
|
|
539
|
+
}
|
|
540
|
+
if (await waitEmpty(timeoutMs))
|
|
541
|
+
return true;
|
|
542
|
+
// A nonresponsive helper is still our own ChildProcess. Closing its
|
|
543
|
+
// actual handle triggers KILL_ON_JOB_CLOSE; saved IDs remain query-only.
|
|
544
|
+
if (helperChild && helperChild.exitCode === null && helperChild.signalCode === null)
|
|
545
|
+
helperChild.kill("SIGKILL");
|
|
546
|
+
state = "lost";
|
|
547
|
+
return waitEmpty(timeoutMs);
|
|
548
|
+
},
|
|
549
|
+
populated, waitEmpty,
|
|
550
|
+
release() {
|
|
551
|
+
connection?.end();
|
|
552
|
+
server.close();
|
|
553
|
+
},
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
/** Where the Windows helper lives beside this module (dist or src). */
|
|
557
|
+
export function containmentModuleDir() {
|
|
558
|
+
return dirname(fileURLToPath(import.meta.url));
|
|
559
|
+
}
|