toolroll 0.9.93 → 0.9.95
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/CHANGELOG.md +79 -0
- package/dist/activity-line.d.ts +7 -4
- package/dist/activity-line.js +9 -7
- package/dist/agent-fence.d.ts +10 -0
- package/dist/agent-fence.js +6 -3
- package/dist/approval-authority.d.ts +7 -1
- package/dist/approval-authority.js +11 -2
- package/dist/approval-password.d.ts +31 -0
- package/dist/approval-password.js +26 -0
- package/dist/assignment-status.d.ts +1 -1
- package/dist/assignment-ui.js +3 -1
- package/dist/assignment.js +5 -0
- package/dist/attest.d.ts +3 -0
- package/dist/attest.js +10 -0
- package/dist/boot-identity.d.ts +2 -1
- package/dist/boot-identity.js +6 -4
- package/dist/bounded-spawn.d.ts +57 -0
- package/dist/bounded-spawn.js +220 -0
- package/dist/browser/terminal.js +42 -42
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +32 -32
- package/dist/browser-crew.js +2 -0
- package/dist/browser-shell.d.ts +1 -1
- package/dist/browser-shell.js +2 -2
- package/dist/browser-workspace.d.ts +50 -2
- package/dist/browser-workspace.js +3 -3
- package/dist/build-review.js +2 -0
- package/dist/builder-restart.d.ts +21 -0
- package/dist/builder-restart.js +50 -0
- package/dist/builder.d.ts +11 -2
- package/dist/builder.js +234 -101
- package/dist/card-catalog.d.ts +209 -0
- package/dist/card-catalog.js +133 -0
- package/dist/card-cli.d.ts +43 -0
- package/dist/card-cli.js +44 -0
- package/dist/card-render.d.ts +26 -0
- package/dist/card-render.js +223 -0
- package/dist/card-sources.d.ts +68 -0
- package/dist/card-sources.js +234 -0
- package/dist/chat-acceptance.js +1 -1
- package/dist/chat-actions.d.ts +106 -9
- package/dist/chat-actions.js +473 -46
- package/dist/chat-approval.d.ts +13 -0
- package/dist/chat-approval.js +39 -9
- package/dist/chat-card-part.d.ts +11 -0
- package/dist/chat-card-part.js +18 -0
- package/dist/chat-card.d.ts +46 -0
- package/dist/chat-card.js +99 -0
- package/dist/chat-channel.d.ts +2 -0
- package/dist/chat-channel.js +37 -14
- package/dist/chat-controls.d.ts +4 -0
- package/dist/chat-controls.js +4 -0
- package/dist/chat-decide.d.ts +10 -16
- package/dist/chat-decide.js +79 -62
- package/dist/chat-delivery.js +13 -4
- package/dist/chat-flow.d.ts +0 -27
- package/dist/chat-flow.js +31 -2
- package/dist/chat-quiet.js +4 -4
- package/dist/chat-task-actions.d.ts +3 -2
- package/dist/chat-task-actions.js +19 -5
- package/dist/chat-terms.d.ts +14 -2
- package/dist/chat-terms.js +35 -0
- package/dist/chat-voice.d.ts +1 -1
- package/dist/chat-voice.js +2 -2
- package/dist/check-facts.d.ts +3 -1
- package/dist/check-facts.js +57 -11
- package/dist/check-failure.d.ts +24 -3
- package/dist/check-failure.js +89 -13
- package/dist/check-levels-ui.d.ts +4 -0
- package/dist/check-levels-ui.js +10 -1
- package/dist/check-load.d.ts +85 -0
- package/dist/check-load.js +147 -0
- package/dist/check-only-standing.d.ts +36 -0
- package/dist/check-only-standing.js +39 -0
- package/dist/check-only.d.ts +15 -0
- package/dist/check-only.js +99 -0
- package/dist/check-repair.d.ts +47 -0
- package/dist/check-repair.js +240 -0
- package/dist/check-report.d.ts +2 -0
- package/dist/check-report.js +1 -1
- package/dist/check-stamp.d.ts +46 -0
- package/dist/check-stamp.js +93 -0
- package/dist/checkout-cleanup.d.ts +36 -5
- package/dist/checkout-cleanup.js +166 -14
- package/dist/claim.d.ts +6 -0
- package/dist/claim.js +13 -2
- package/dist/cli-help.d.ts +4 -1
- package/dist/cli-help.js +49 -30
- package/dist/cli.js +15 -1
- package/dist/code-extract.d.ts +44 -0
- package/dist/code-extract.js +88 -0
- package/dist/code-map-acts.d.ts +24 -0
- package/dist/code-map-acts.js +53 -0
- package/dist/code-map-admission.d.ts +52 -0
- package/dist/code-map-admission.js +295 -0
- package/dist/code-map-build.d.ts +63 -0
- package/dist/code-map-build.js +415 -0
- package/dist/code-map-cli.d.ts +153 -0
- package/dist/code-map-cli.js +103 -0
- package/dist/code-map-main.d.ts +1 -0
- package/dist/code-map-main.js +48 -0
- package/dist/code-map-read.d.ts +106 -0
- package/dist/code-map-read.js +171 -0
- package/dist/code-map-report.d.ts +37 -0
- package/dist/code-map-report.js +95 -0
- package/dist/code-map-shape.d.ts +172 -0
- package/dist/code-map-shape.js +327 -0
- package/dist/code-map-store.d.ts +136 -0
- package/dist/code-map-store.js +233 -0
- package/dist/coding-provider.d.ts +3 -1
- package/dist/coding-provider.js +12 -7
- package/dist/coding-update.js +3 -2
- package/dist/coding-workspace.d.ts +1 -1
- package/dist/coding-workspace.js +8 -8
- package/dist/console-chrome.js +2 -2
- package/dist/containment.d.ts +5 -0
- package/dist/containment.js +14 -1
- package/dist/contracts/chat-actions.d.ts +92 -0
- package/dist/contracts/chat-actions.js +20 -0
- package/dist/contracts/chat-card.d.ts +91 -0
- package/dist/contracts/chat-card.js +54 -0
- package/dist/contracts/chat-content.d.ts +191 -0
- package/dist/contracts/chat-content.js +4 -0
- package/dist/contracts/cli.d.ts +52 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +12 -3
- package/dist/contracts/console-api.js +12 -3
- package/dist/contracts/flow-propose.d.ts +8 -1
- package/dist/contracts/flow-propose.js +3 -1
- package/dist/contracts/flow-send.d.ts +126 -0
- package/dist/contracts/flow-send.js +3 -0
- package/dist/contracts/gateway-tools.d.ts +17 -0
- package/dist/contracts/gateway-tools.js +3 -1
- package/dist/contracts/lead-tools.d.ts +234 -0
- package/dist/contracts/lead-tools.js +25 -1
- package/dist/contracts/project-knowledge.d.ts +38 -5
- package/dist/contracts/project-knowledge.js +11 -5
- package/dist/contracts/project-memory.d.ts +3 -1
- package/dist/contracts/project-memory.js +3 -2
- package/dist/contracts/storage.d.ts +14 -0
- package/dist/contracts/storage.js +3 -1
- package/dist/contracts/store-json.d.ts +18 -0
- package/dist/contracts/youiyoux-request.d.ts +1 -1
- package/dist/contracts/youiyoux-request.js +1 -1
- package/dist/desktop-access.js +6 -2
- package/dist/discord-chat.js +14 -0
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +25 -16
- package/dist/dispose.d.ts +19 -0
- package/dist/dispose.js +55 -9
- package/dist/easy-start.js +2 -3
- package/dist/eval-oracle.js +4 -1
- package/dist/evals.js +2 -1
- package/dist/evidence-pair.d.ts +53 -0
- package/dist/evidence-pair.js +128 -0
- package/dist/exhaustion.d.ts +17 -4
- package/dist/exhaustion.js +44 -4
- package/dist/export.js +1 -1
- package/dist/failing-first.d.ts +31 -0
- package/dist/failing-first.js +44 -0
- package/dist/fallback-schema.d.ts +8 -0
- package/dist/fallback-schema.js +40 -0
- package/dist/fallback.d.ts +138 -0
- package/dist/fallback.js +605 -0
- package/dist/fast-start.d.ts +136 -0
- package/dist/fast-start.js +431 -0
- package/dist/fast-terms.d.ts +9 -0
- package/dist/fast-terms.js +23 -0
- package/dist/fenced-check-coverage.d.ts +62 -0
- package/dist/fenced-check-coverage.js +54 -0
- package/dist/findings.d.ts +224 -0
- package/dist/findings.js +449 -0
- package/dist/first-run.js +1 -1
- package/dist/flaky-ci.d.ts +31 -0
- package/dist/flaky-ci.js +149 -0
- package/dist/flaky-cli.d.ts +60 -0
- package/dist/flaky-cli.js +60 -0
- package/dist/flaky.d.ts +75 -9
- package/dist/flaky.js +255 -0
- package/dist/fleet-schema.d.ts +17 -0
- package/dist/fleet-schema.js +135 -0
- package/dist/flow-browser.js +2 -2
- package/dist/flow-card-task.js +3 -0
- package/dist/flow-code.d.ts +2 -2
- package/dist/flow-code.js +9 -5
- package/dist/flow-result-card.d.ts +22 -0
- package/dist/flow-result-card.js +55 -0
- package/dist/flow-send.js +12 -9
- package/dist/flow-steps.js +1 -1
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +2 -2
- package/dist/flow-unattended.d.ts +26 -0
- package/dist/flow-unattended.js +12 -3
- package/dist/flows-ui.js +10 -2
- package/dist/follow-through-brief.d.ts +12 -0
- package/dist/follow-through-brief.js +31 -0
- package/dist/follow-through-cli.d.ts +23 -0
- package/dist/follow-through-cli.js +153 -0
- package/dist/git-incident.d.ts +16 -0
- package/dist/git-incident.js +69 -6
- package/dist/github-import.d.ts +62 -0
- package/dist/github-import.js +146 -0
- package/dist/guides.js +15 -10
- package/dist/handover-note.d.ts +33 -0
- package/dist/handover-note.js +106 -0
- package/dist/invoke.d.ts +25 -2
- package/dist/invoke.js +157 -58
- package/dist/job-object-helper.ps1 +10 -3
- package/dist/keymap.d.ts +41 -0
- package/dist/keymap.js +51 -0
- package/dist/knowledge-cli.js +1 -1
- package/dist/knowledge-ui.d.ts +21 -4
- package/dist/knowledge-ui.js +75 -7
- package/dist/laptop-background.d.ts +16 -0
- package/dist/laptop-background.js +108 -2
- package/dist/lead-cli.js +26 -24
- package/dist/lead-context.js +1 -1
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +3 -2
- package/dist/lead-doors.d.ts +4 -0
- package/dist/lead-doors.js +2 -2
- package/dist/lead-progress.js +5 -0
- package/dist/lead-status.d.ts +2 -0
- package/dist/lead-status.js +6 -2
- package/dist/lead-tools.js +162 -34
- package/dist/lead.js +2 -2
- package/dist/lesson-notes.d.ts +81 -0
- package/dist/lesson-notes.js +336 -0
- package/dist/limits-ui.js +1 -1
- package/dist/local-owner.d.ts +1 -1
- package/dist/local-owner.js +16 -0
- package/dist/machine-check.d.ts +16 -0
- package/dist/machine-check.js +26 -0
- package/dist/machine-identity.d.ts +75 -0
- package/dist/machine-identity.js +170 -0
- package/dist/mcp-core.js +11 -1
- package/dist/memory-cli.d.ts +300 -1
- package/dist/memory-cli.js +62 -9
- package/dist/memory-pass.d.ts +16 -0
- package/dist/memory-pass.js +67 -3
- package/dist/memory-tidy.d.ts +181 -0
- package/dist/memory-tidy.js +523 -0
- package/dist/memory-use.d.ts +35 -0
- package/dist/memory-use.js +109 -0
- package/dist/merge-checklist.d.ts +55 -0
- package/dist/merge-checklist.js +182 -0
- package/dist/modes.d.ts +6 -6
- package/dist/modes.js +3 -1
- package/dist/needs-you.d.ts +31 -9
- package/dist/needs-you.js +110 -41
- package/dist/nightly-github.d.ts +2 -0
- package/dist/nightly-github.js +4 -4
- package/dist/operate-remote-arguments.js +7 -1
- package/dist/operate-remote.js +3 -1
- package/dist/operate.d.ts +6 -1
- package/dist/operate.js +565 -64
- package/dist/phase-routing.js +2 -2
- package/dist/plain-schema.d.ts +23 -0
- package/dist/plain-schema.js +94 -0
- package/dist/plain-words.d.ts +20 -0
- package/dist/plain-words.js +66 -0
- package/dist/plan-login.d.ts +14 -0
- package/dist/plan-login.js +58 -0
- package/dist/planner.d.ts +1 -1
- package/dist/planner.js +4 -2
- package/dist/process-liveness.d.ts +2 -0
- package/dist/process-liveness.js +61 -17
- package/dist/project-delete-ui.d.ts +19 -2
- package/dist/project-delete-ui.js +29 -1
- package/dist/project-delete.js +14 -1
- package/dist/project-knowledge.d.ts +18 -11
- package/dist/project-knowledge.js +39 -9
- package/dist/project-learning.d.ts +2 -1
- package/dist/project-learning.js +15 -45
- package/dist/project-memory.d.ts +5 -0
- package/dist/project-memory.js +22 -2
- package/dist/project-tools.d.ts +4 -4
- package/dist/project-tools.js +23 -7
- package/dist/proof.js +30 -5
- package/dist/protected-storage-look.d.ts +1 -0
- package/dist/protected-storage-look.js +13 -0
- package/dist/protected-storage.d.ts +70 -39
- package/dist/protected-storage.js +196 -147
- package/dist/provider-auth.js +1 -1
- package/dist/provider-capture.d.ts +34 -0
- package/dist/provider-capture.js +53 -0
- package/dist/publish.js +9 -1
- package/dist/pull-request-flow.js +5 -0
- package/dist/push.d.ts +18 -2
- package/dist/push.js +40 -16
- package/dist/release-adapters.d.ts +9 -0
- package/dist/release-adapters.js +76 -0
- package/dist/release-auto.js +13 -13
- package/dist/release-card.d.ts +1 -2
- package/dist/release-card.js +11 -0
- package/dist/release-carry.d.ts +7 -2
- package/dist/release-carry.js +49 -10
- package/dist/release-cli.d.ts +4 -0
- package/dist/release-cli.js +29 -22
- package/dist/release-coverage.js +1 -1
- package/dist/release-next.d.ts +48 -0
- package/dist/release-next.js +161 -0
- package/dist/release.d.ts +20 -1
- package/dist/release.js +85 -9
- package/dist/remote-destination.d.ts +36 -0
- package/dist/remote-destination.js +217 -0
- package/dist/remote-main.d.ts +48 -0
- package/dist/remote-main.js +84 -5
- package/dist/repository-context.d.ts +3 -2
- package/dist/repository-context.js +112 -70
- package/dist/restart-certification.d.ts +1 -0
- package/dist/restart-certification.js +8 -6
- package/dist/result-acts.d.ts +27 -19
- package/dist/result-acts.js +36 -25
- package/dist/result-follow-ups.d.ts +2 -2
- package/dist/result-follow-ups.js +18 -4
- package/dist/result-review.d.ts +1 -1
- package/dist/route-change-check.d.ts +4 -0
- package/dist/route-change-check.js +13 -0
- package/dist/route-change.d.ts +22 -0
- package/dist/route-change.js +89 -0
- package/dist/route-hooks.js +3 -2
- package/dist/route-results.d.ts +6 -1
- package/dist/route-results.js +14 -5
- package/dist/run-closeout.d.ts +1 -1
- package/dist/run-closeout.js +7 -5
- package/dist/runner-process.js +3 -2
- package/dist/runner.js +2 -1
- package/dist/scope.d.ts +28 -4
- package/dist/scope.js +92 -28
- package/dist/scout.d.ts +1 -1
- package/dist/scout.js +2 -1
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +26 -18
- package/dist/server/chat.js +16 -14
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +26 -35
- package/dist/server/console.css +73 -0
- package/dist/server/guards.js +3 -0
- package/dist/server/home.d.ts +6 -0
- package/dist/server/home.js +21 -13
- package/dist/server/http.js +1 -1
- package/dist/server/kit.js +1 -1
- package/dist/server/merge-ready-view.d.ts +10 -0
- package/dist/server/merge-ready-view.js +30 -0
- package/dist/server/pages-assets.d.ts +1 -0
- package/dist/server/pages-assets.js +8 -5
- package/dist/server/pages.js +103 -82
- package/dist/server/render-chat.js +16 -3
- package/dist/server/render-code-map.d.ts +30 -0
- package/dist/server/render-code-map.js +137 -0
- package/dist/server/render-flaky.d.ts +20 -0
- package/dist/server/render-flaky.js +59 -0
- package/dist/server/render-follow-through.d.ts +11 -0
- package/dist/server/render-follow-through.js +68 -0
- package/dist/server/render-history.js +1 -1
- package/dist/server/render-new-task.d.ts +26 -1
- package/dist/server/render-new-task.js +30 -5
- package/dist/server/render-pages.d.ts +1 -175
- package/dist/server/render-pages.js +38 -313
- package/dist/server/render-projects.d.ts +2 -1
- package/dist/server/render-projects.js +9 -7
- package/dist/server/render-release.d.ts +2 -0
- package/dist/server/render-release.js +3 -3
- package/dist/server/render-results.d.ts +2 -0
- package/dist/server/render-results.js +68 -68
- package/dist/server/render-tasks.d.ts +16 -2
- package/dist/server/render-tasks.js +177 -141
- package/dist/server/retired-routes.d.ts +17 -0
- package/dist/server/retired-routes.js +53 -0
- package/dist/server/route-table.d.ts +3 -0
- package/dist/server/route-table.js +48 -12
- package/dist/server/settings.js +166 -34
- package/dist/server/task-views.js +66 -26
- package/dist/server/tasks.d.ts +2 -6
- package/dist/server/tasks.js +250 -321
- package/dist/server/welcome.js +1 -1
- package/dist/settings-groups-ui.d.ts +1 -0
- package/dist/settings-groups-ui.js +4 -2
- package/dist/settings-kit.js +3 -2
- package/dist/settings-proofs.js +3 -1
- package/dist/slack-chat.js +14 -1
- package/dist/steer-mailbox.d.ts +8 -1
- package/dist/steer-mailbox.js +164 -6
- package/dist/stop-proof.js +6 -4
- package/dist/storage-sweep-process.js +1 -1
- package/dist/storage-sweep.d.ts +5 -3
- package/dist/storage-sweep.js +30 -9
- package/dist/storage-ui.js +28 -17
- package/dist/storage.d.ts +27 -2
- package/dist/storage.js +39 -2
- package/dist/store.d.ts +89 -7
- package/dist/store.js +386 -114
- package/dist/surface.js +67 -6
- package/dist/sweep-quarantine.d.ts +1 -0
- package/dist/sweep-quarantine.js +6 -4
- package/dist/tamper-scan.d.ts +4 -0
- package/dist/tamper-scan.js +90 -0
- package/dist/task-aliases.d.ts +5 -0
- package/dist/task-aliases.js +9 -0
- package/dist/task-control.d.ts +2 -1
- package/dist/task-control.js +4 -1
- package/dist/task-outcome-cli.d.ts +2 -0
- package/dist/task-outcome-cli.js +17 -17
- package/dist/task-sizing.d.ts +5 -0
- package/dist/task-sizing.js +2 -0
- package/dist/task-source.d.ts +7 -0
- package/dist/task-source.js +14 -0
- package/dist/task-status.d.ts +9 -0
- package/dist/task-status.js +45 -49
- package/dist/teardown-git.d.ts +18 -0
- package/dist/teardown-git.js +105 -21
- package/dist/telegram-lead.js +23 -6
- package/dist/telegram-progress.js +5 -3
- package/dist/telegram-status.js +6 -4
- package/dist/telegram.js +12 -1
- package/dist/test-infra.d.ts +8 -0
- package/dist/test-infra.js +18 -0
- package/dist/tidy.js +1 -1
- package/dist/toolroll-update.js +19 -5
- package/dist/tui-contract.d.ts +24 -0
- package/dist/tui-source.js +67 -8
- package/dist/tui.js +51 -51
- package/dist/vanished-retry.d.ts +30 -0
- package/dist/vanished-retry.js +174 -0
- package/dist/work-index.d.ts +2 -0
- package/dist/work-index.js +95 -66
- package/dist/work-summary.d.ts +1 -1
- package/dist/work-summary.js +7 -2
- package/dist/workspace-ui.d.ts +7 -3
- package/dist/workspace-ui.js +38 -23
- package/dist/worktree.d.ts +12 -3
- package/dist/worktree.js +91 -14
- package/package.json +2 -1
- package/dist/server/render-next.d.ts +0 -63
- package/dist/server/render-next.js +0 -109
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One bounded child process tree, for work on a repository's untrusted bytes (docs/plans/code-map.md §1). What is
|
|
3
|
+
* guaranteed depends on the platform, and the result says which:
|
|
4
|
+
*
|
|
5
|
+
* Linux with a delegated cgroup, Windows the OS object (containment.ts, policy `preferred`) holds the memory limit,
|
|
6
|
+
* and dies with the worker. `enforced: "system"`.
|
|
7
|
+
* macOS, Linux without a cgroup no hard memory or descendant bound exists. Memory and disk are sampled every
|
|
8
|
+
* 250 ms; a burst between samples can pass the limit before the kill, and a
|
|
9
|
+
* helper that left the process group can survive it. `enforced: "checked"`.
|
|
10
|
+
*
|
|
11
|
+
* `RLIMIT_FSIZE` applies to every POSIX spawn. The whole group is killed with SIGKILL on any limit, and again when the
|
|
12
|
+
* root exits, so nothing the root started outlives it.
|
|
13
|
+
*/
|
|
14
|
+
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
15
|
+
import { lstatSync, readdirSync, rmSync } from "node:fs";
|
|
16
|
+
import { join, relative, isAbsolute } from "node:path";
|
|
17
|
+
import { createContainer, effectiveContainment, probeContainmentCapability } from "./containment.js";
|
|
18
|
+
const SAMPLE_MS = 250;
|
|
19
|
+
const PIDS = 64;
|
|
20
|
+
/** Applies the file-size limit (bash counts it in 1024-byte blocks, POSIX shells in 512), then becomes the target with
|
|
21
|
+
* argv untouched. */
|
|
22
|
+
const PRELUDE = 'if [ "$1" != none ]; then if [ -n "$BASH_VERSION" ]; then b=$(($1/1024)); else b=$(($1/512)); fi; ulimit -f "$b" 2>/dev/null || exit 126; fi\nshift\nexec "$@"';
|
|
23
|
+
let containment = null;
|
|
24
|
+
const preferred = () => containment ??= effectiveContainment("preferred", probeContainmentCapability());
|
|
25
|
+
/** Tests: forget the probed machine. */
|
|
26
|
+
export function resetBoundedContainmentForTests() { containment = null; }
|
|
27
|
+
/** A process's own start time, as `ps` reports it: what tells a live process from a reused pid. */
|
|
28
|
+
export function processStartedAt(pid) {
|
|
29
|
+
if (process.platform === "win32")
|
|
30
|
+
return null;
|
|
31
|
+
try {
|
|
32
|
+
return execFileSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", timeout: 2_000, stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function processGroupOf(pid) {
|
|
39
|
+
if (process.platform === "win32")
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
const group = Number(execFileSync("ps", ["-o", "pgid=", "-p", String(pid)], { encoding: "utf8", timeout: 2_000, stdio: ["ignore", "pipe", "ignore"] }).trim());
|
|
43
|
+
return Number.isInteger(group) && group > 1 ? group : null;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function groupRss(pgid) {
|
|
50
|
+
return new Promise(done => {
|
|
51
|
+
execFile("ps", ["-A", "-o", "pgid=,rss="], { encoding: "utf8", timeout: 2_000, maxBuffer: 4 * 1024 * 1024 }, (error, stdout) => {
|
|
52
|
+
if (error !== null) {
|
|
53
|
+
done(null);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
let kilobytes = 0;
|
|
57
|
+
for (const line of stdout.split("\n")) {
|
|
58
|
+
const [group, rss] = line.trim().split(/\s+/);
|
|
59
|
+
if (Number(group) === pgid)
|
|
60
|
+
kilobytes += Number(rss) || 0;
|
|
61
|
+
}
|
|
62
|
+
done(kilobytes * 1024);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** The folder's total size, without following a link. */
|
|
67
|
+
export function folderBytes(folder) {
|
|
68
|
+
let total = 0;
|
|
69
|
+
const walk = (dir) => {
|
|
70
|
+
let names;
|
|
71
|
+
try {
|
|
72
|
+
names = readdirSync(dir);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
for (const name of names) {
|
|
78
|
+
const path = join(dir, name);
|
|
79
|
+
try {
|
|
80
|
+
const held = lstatSync(path);
|
|
81
|
+
if (held.isDirectory())
|
|
82
|
+
walk(path);
|
|
83
|
+
else
|
|
84
|
+
total += held.size;
|
|
85
|
+
}
|
|
86
|
+
catch { /* a temporary pack gone between the list and the look */ }
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
walk(folder);
|
|
90
|
+
return total;
|
|
91
|
+
}
|
|
92
|
+
const killGroup = (pgid) => { try {
|
|
93
|
+
process.kill(-pgid, "SIGKILL");
|
|
94
|
+
}
|
|
95
|
+
catch { /* gone */ } };
|
|
96
|
+
export const boundedRun = async (file, args, options) => {
|
|
97
|
+
const windows = process.platform === "win32";
|
|
98
|
+
const effective = preferred();
|
|
99
|
+
const made = createContainer(effective, options.label);
|
|
100
|
+
const container = "container" in made ? made.container : null;
|
|
101
|
+
let enforced = "checked";
|
|
102
|
+
if (container !== null && options.memoryBytes !== undefined && container.limit?.({ memoryBytes: options.memoryBytes, pids: PIDS }) === true)
|
|
103
|
+
enforced = "system";
|
|
104
|
+
const target = windows ? { file, args: [...args] } : { file: "/bin/sh", args: ["-c", PRELUDE, "so-bounded", options.fileBytes === undefined ? "none" : String(options.fileBytes), file, ...args] };
|
|
105
|
+
const launch = container?.launch(target.file, target.args) ?? null;
|
|
106
|
+
const child = spawn(launch?.file ?? target.file, launch?.args ?? target.args, {
|
|
107
|
+
cwd: options.cwd, env: options.env, shell: false, windowsHide: true, detached: !windows,
|
|
108
|
+
stdio: ["pipe", "pipe", "pipe", ...(launch?.extraStdio ?? [])],
|
|
109
|
+
});
|
|
110
|
+
const stdout = [];
|
|
111
|
+
let stdoutBytes = 0, stderr = "", killed = null, peakBytes = 0, sampling = false;
|
|
112
|
+
const pid = child.pid;
|
|
113
|
+
const kill = (why) => {
|
|
114
|
+
killed ??= why;
|
|
115
|
+
if (pid !== undefined && !windows)
|
|
116
|
+
killGroup(pid);
|
|
117
|
+
if (container !== null)
|
|
118
|
+
void container.kill();
|
|
119
|
+
else if (windows) {
|
|
120
|
+
try {
|
|
121
|
+
child.kill("SIGKILL");
|
|
122
|
+
}
|
|
123
|
+
catch { /* gone */ }
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
if (pid !== undefined) {
|
|
127
|
+
try {
|
|
128
|
+
options.onStart?.({ pid, pgid: pid, startedAt: processStartedAt(pid) });
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
kill("time");
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
if (launch !== null) {
|
|
135
|
+
const attached = await launch.attach(child);
|
|
136
|
+
if (!attached.ok)
|
|
137
|
+
kill("time");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
child.stdout?.on("data", (chunk) => {
|
|
141
|
+
stdoutBytes += chunk.length;
|
|
142
|
+
if (stdoutBytes > options.maxStdout) {
|
|
143
|
+
kill("output");
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
stdout.push(chunk);
|
|
147
|
+
});
|
|
148
|
+
child.stderr?.on("data", (chunk) => { if (stderr.length < options.maxStderr)
|
|
149
|
+
stderr += chunk.toString("utf8").slice(0, options.maxStderr - stderr.length); });
|
|
150
|
+
child.stdin?.on("error", () => { });
|
|
151
|
+
child.stdin?.end(options.stdin);
|
|
152
|
+
const timer = setTimeout(() => kill("time"), options.timeoutMs);
|
|
153
|
+
const sampler = setInterval(() => {
|
|
154
|
+
if (sampling || pid === undefined)
|
|
155
|
+
return;
|
|
156
|
+
sampling = true;
|
|
157
|
+
void (async () => {
|
|
158
|
+
try {
|
|
159
|
+
if (options.memoryBytes !== undefined && enforced === "checked" && !windows) {
|
|
160
|
+
const rss = await groupRss(pid);
|
|
161
|
+
if (rss !== null) {
|
|
162
|
+
peakBytes = Math.max(peakBytes, rss);
|
|
163
|
+
if (rss > options.memoryBytes)
|
|
164
|
+
kill("memory");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (options.diskBytes !== undefined && options.folder !== undefined && folderBytes(options.folder) > options.diskBytes)
|
|
168
|
+
kill("disk");
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
sampling = false;
|
|
172
|
+
}
|
|
173
|
+
})();
|
|
174
|
+
}, options.sampleMs ?? SAMPLE_MS);
|
|
175
|
+
const finished = await new Promise(done => {
|
|
176
|
+
let settled = false;
|
|
177
|
+
const settle = (code, signal) => { if (!settled) {
|
|
178
|
+
settled = true;
|
|
179
|
+
done({ code, signal });
|
|
180
|
+
} };
|
|
181
|
+
child.once("error", () => settle(127, null));
|
|
182
|
+
child.once("close", (code, signal) => settle(code, signal));
|
|
183
|
+
// A helper that left the group can hold the pipes open past the root's exit: the root's exit is the answer.
|
|
184
|
+
child.once("exit", (code, signal) => {
|
|
185
|
+
if (pid !== undefined && !windows)
|
|
186
|
+
killGroup(pid);
|
|
187
|
+
setTimeout(() => { child.stdout?.destroy(); child.stderr?.destroy(); settle(code, signal); }, 2_000).unref?.();
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
clearTimeout(timer);
|
|
191
|
+
clearInterval(sampler);
|
|
192
|
+
if (container !== null) {
|
|
193
|
+
await container.kill(2_000);
|
|
194
|
+
container.release();
|
|
195
|
+
}
|
|
196
|
+
return { code: finished.code, signal: finished.signal, stdout: Buffer.concat(stdout), stderr, killed, enforced, peakBytes };
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* What a crashed build left: its group is killed only if the leader is still the process recorded (the pid and its start
|
|
200
|
+
* time, so a reused pid isn't killed), and its folder goes if it sits under `root`. On Linux the cgroup janitor has
|
|
201
|
+
* done this already.
|
|
202
|
+
*/
|
|
203
|
+
export function cleanupLeftover(record, root) {
|
|
204
|
+
let killed = false, removed = false;
|
|
205
|
+
if (record.pid !== null && record.pgid !== null && record.startedAt !== null && processStartedAt(record.pid) === record.startedAt && processGroupOf(record.pid) === record.pgid) {
|
|
206
|
+
killGroup(record.pgid);
|
|
207
|
+
killed = true;
|
|
208
|
+
}
|
|
209
|
+
if (record.folder !== null) {
|
|
210
|
+
const inner = relative(root, record.folder);
|
|
211
|
+
if (inner !== "" && !inner.startsWith("..") && !isAbsolute(inner)) {
|
|
212
|
+
try {
|
|
213
|
+
rmSync(record.folder, { recursive: true, force: true });
|
|
214
|
+
removed = true;
|
|
215
|
+
}
|
|
216
|
+
catch { /* the next takeover tries again */ }
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return { killed, removed };
|
|
220
|
+
}
|