toolroll 0.9.92 → 0.9.94
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 +87 -0
- package/dist/activity-line.d.ts +7 -4
- package/dist/activity-line.js +9 -7
- package/dist/agent-fence.d.ts +4 -0
- package/dist/agent-fence.js +4 -1
- 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 -3
- package/dist/assignment.js +16 -3
- 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 +2 -2
- package/dist/browser/workspace.js +26 -26
- package/dist/browser-crew.js +2 -2
- package/dist/browser-shell.d.ts +1 -1
- package/dist/browser-shell.js +2 -2
- package/dist/browser-workspace.d.ts +48 -7
- package/dist/browser-workspace.js +3 -3
- package/dist/builder-restart.d.ts +21 -0
- package/dist/builder-restart.js +50 -0
- package/dist/builder.d.ts +4 -0
- package/dist/builder.js +73 -25
- package/dist/chat-acceptance.js +1 -1
- package/dist/chat-actions.d.ts +12 -2
- package/dist/chat-actions.js +25 -2
- package/dist/chat-channel.js +11 -0
- package/dist/chat-decide.d.ts +7 -2
- package/dist/chat-decide.js +37 -9
- package/dist/chat-quiet.js +3 -3
- package/dist/chat-voice.d.ts +1 -1
- package/dist/chat-voice.js +2 -2
- package/dist/check-facts.d.ts +126 -0
- package/dist/check-facts.js +318 -0
- package/dist/check-failure.d.ts +22 -3
- package/dist/check-failure.js +57 -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-report.d.ts +7 -0
- package/dist/check-report.js +4 -3
- 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/cli-help.d.ts +4 -1
- package/dist/cli-help.js +47 -30
- package/dist/cli.js +16 -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 +2 -0
- package/dist/coding-provider.js +3 -2
- package/dist/coding-update.js +3 -2
- package/dist/coding-workspace.js +4 -4
- 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 +9 -0
- package/dist/contracts/chat-actions.js +2 -0
- package/dist/contracts/cli.d.ts +52 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +12 -5
- package/dist/contracts/console-api.js +12 -5
- package/dist/contracts/gateway-tools.d.ts +17 -0
- package/dist/contracts/gateway-tools.js +3 -1
- package/dist/contracts/lead-tools.d.ts +61 -0
- package/dist/contracts/lead-tools.js +10 -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/release-check.d.ts +25 -1
- package/dist/contracts/release-check.js +19 -0
- package/dist/contracts/storage.d.ts +14 -0
- package/dist/contracts/storage.js +3 -1
- package/dist/contracts/store-json.d.ts +2 -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/desktop-update.d.ts +2 -2
- package/dist/desktop-update.js +13 -9
- package/dist/dispatch.js +18 -15
- package/dist/dispose.js +8 -2
- package/dist/easy-start.js +2 -3
- package/dist/eval-hooks.d.ts +55 -0
- package/dist/eval-hooks.js +227 -4
- package/dist/evals.js +2 -1
- 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/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 +83 -0
- package/dist/flaky.js +264 -0
- package/dist/flow-card-task.js +3 -0
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +1 -1
- package/dist/github-import.d.ts +62 -0
- package/dist/github-import.js +146 -0
- package/dist/guides.js +15 -10
- package/dist/invoke.d.ts +5 -1
- package/dist/invoke.js +49 -14
- 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-context.js +1 -1
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +2 -2
- package/dist/lead-progress.js +3 -0
- package/dist/lead-status.d.ts +2 -0
- package/dist/lead-status.js +6 -2
- package/dist/lead-tools.js +51 -2
- package/dist/lead.js +1 -1
- 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 +11 -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/needs-you.d.ts +32 -13
- package/dist/needs-you.js +108 -49
- package/dist/nightly-github.d.ts +2 -0
- package/dist/nightly-github.js +4 -4
- package/dist/operate-remote-arguments.js +6 -1
- package/dist/operate-remote.js +3 -1
- package/dist/operate.d.ts +6 -1
- package/dist/operate.js +540 -70
- 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/planner.js +2 -1
- package/dist/process-liveness.d.ts +2 -0
- package/dist/process-liveness.js +61 -17
- package/dist/project-delete-ui.d.ts +7 -2
- package/dist/project-delete-ui.js +12 -1
- package/dist/project-delete.js +13 -2
- 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/proof.js +30 -5
- package/dist/provider-auth.js +1 -1
- package/dist/publish.js +9 -1
- package/dist/pull-request-flow.js +5 -0
- package/dist/push.d.ts +26 -3
- package/dist/push.js +55 -15
- package/dist/release-adapters.d.ts +9 -0
- package/dist/release-adapters.js +87 -1
- package/dist/release-approval.d.ts +191 -0
- package/dist/release-approval.js +382 -0
- package/dist/release-auto.js +26 -17
- package/dist/release-card.d.ts +74 -6
- package/dist/release-card.js +179 -23
- package/dist/release-carry.d.ts +7 -2
- package/dist/release-carry.js +39 -9
- package/dist/release-cli.d.ts +4 -0
- package/dist/release-cli.js +38 -23
- 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 +50 -1
- package/dist/release.js +118 -11
- 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 +29 -21
- 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 +6 -1
- package/dist/scope.js +22 -9
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +24 -18
- package/dist/server/chat.js +14 -12
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +26 -35
- package/dist/server/console.css +23 -0
- package/dist/server/guards.js +4 -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 +2 -2
- 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-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 +19 -0
- package/dist/server/render-release.js +84 -0
- package/dist/server/render-results.d.ts +2 -2
- package/dist/server/render-results.js +71 -73
- package/dist/server/render-tasks.d.ts +13 -2
- package/dist/server/render-tasks.js +156 -139
- package/dist/server/retired-routes.d.ts +17 -0
- package/dist/server/retired-routes.js +53 -0
- package/dist/server/route-table.d.ts +4 -1
- package/dist/server/route-table.js +52 -13
- package/dist/server/settings.js +129 -22
- package/dist/server/task-views.js +61 -26
- package/dist/server/tasks.d.ts +17 -6
- package/dist/server/tasks.js +273 -338
- package/dist/server/welcome.js +1 -1
- package/dist/settings-groups-ui.js +1 -1
- package/dist/setup-guide.d.ts +2 -2
- package/dist/stop-proof.d.ts +67 -0
- package/dist/stop-proof.js +295 -0
- 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 +148 -34
- package/dist/store.js +626 -177
- package/dist/surface.js +46 -5
- 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.js +4 -1
- package/dist/task-outcome-cli.d.ts +2 -0
- package/dist/task-outcome-cli.js +36 -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 +40 -49
- package/dist/teardown-git.d.ts +16 -0
- package/dist/teardown-git.js +63 -5
- package/dist/telegram-progress.js +5 -3
- package/dist/telegram-status.js +6 -4
- 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-ui.js +2 -2
- package/dist/toolroll-update.js +18 -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 +82 -70
- package/dist/work-summary.d.ts +2 -2
- package/dist/work-summary.js +2 -0
- package/dist/workspace-ui.d.ts +4 -2
- package/dist/workspace-ui.js +28 -22
- package/dist/worktree.d.ts +12 -3
- package/dist/worktree.js +68 -13
- package/package.json +1 -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
|
+
}
|