@loucompanion/forge-bridge 0.1.3-dev.8777774c67bc → 0.1.3-dev.88e93f53db5b
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/README.md +51 -0
- package/dist/bridge/bridge.base.d.ts +4 -1
- package/dist/bridge/bridge.service.d.ts +50 -3
- package/dist/bridge/bridge.service.js +730 -87
- package/dist/bridge/bridge.types.d.ts +63 -1
- package/dist/cleanup/cleanup.service.d.ts +14 -2
- package/dist/cleanup/cleanup.service.js +496 -3
- package/dist/cleanup/cleanup.types.d.ts +10 -0
- package/dist/cleanup/index.d.ts +1 -1
- package/dist/cleanup/index.js +1 -1
- package/dist/cleanup/internals.d.ts +2 -2
- package/dist/cleanup/internals.js +1 -1
- package/dist/cli/cli.help.js +37 -3
- package/dist/cli/cli.service.d.ts +30 -2
- package/dist/cli/cli.service.js +191 -27
- package/dist/cli/local-config.d.ts +4 -0
- package/dist/cli/local-config.js +41 -1
- package/dist/runtime/aspnet-runtime-prebuild.d.ts +36 -0
- package/dist/runtime/aspnet-runtime-prebuild.js +340 -0
- package/dist/runtime/attempt-node-runtime-launcher.d.ts +17 -0
- package/dist/runtime/attempt-node-runtime-launcher.js +105 -0
- package/dist/runtime/attempt-process-supervisor-registry.d.ts +20 -0
- package/dist/runtime/attempt-process-supervisor-registry.js +64 -0
- package/dist/runtime/attempt-process-supervisor.d.ts +97 -0
- package/dist/runtime/attempt-process-supervisor.js +384 -0
- package/dist/runtime/generated-output-reset.d.ts +10 -0
- package/dist/runtime/generated-output-reset.js +103 -0
- package/dist/runtime/host-listener-inspector.d.ts +46 -0
- package/dist/runtime/host-listener-inspector.js +349 -0
- package/dist/runtime/host-port-broker.d.ts +76 -0
- package/dist/runtime/host-port-broker.js +333 -0
- package/dist/runtime/host-runtime-contract.d.ts +47 -0
- package/dist/runtime/host-runtime-contract.js +700 -0
- package/dist/runtime/host-runtime-lease.d.ts +194 -0
- package/dist/runtime/host-runtime-lease.js +2138 -0
- package/dist/runtime/host-runtime-promotion.d.ts +23 -0
- package/dist/runtime/host-runtime-promotion.js +159 -0
- package/dist/runtime/memory-capacity.d.ts +68 -0
- package/dist/runtime/memory-capacity.js +121 -0
- package/dist/runtime/node-host-runtime-adapter.d.ts +54 -0
- package/dist/runtime/node-host-runtime-adapter.js +284 -0
- package/dist/runtime/playwright-host-runtime-adapter.d.ts +21 -0
- package/dist/runtime/playwright-host-runtime-adapter.js +225 -0
- package/dist/runtime/runtime-isolation-lease.d.ts +67 -0
- package/dist/runtime/runtime-isolation-lease.js +158 -0
- package/dist/runtime/workspace-resource-estimator.d.ts +17 -0
- package/dist/runtime/workspace-resource-estimator.js +174 -0
- package/dist/sandbox/image-policy.d.ts +3 -1
- package/dist/sandbox/image-policy.js +4 -2
- package/dist/sandbox/preflight.d.ts +49 -1
- package/dist/sandbox/preflight.js +313 -31
- package/dist/sandbox/process.js +125 -40
- package/dist/sandbox/repository-lock.js +41 -3
- package/dist/sandbox/runtime.d.ts +85 -4
- package/dist/sandbox/runtime.js +806 -126
- package/dist/sandbox/sandbox.types.d.ts +10 -0
- package/dist/sandbox/smoke.d.ts +76 -0
- package/dist/sandbox/smoke.js +380 -0
- package/dist/sandbox/worker-supervisor.js +1 -1
- package/dist/sandbox/worker.js +2 -5
- package/dist/sandbox/workspace.service.d.ts +1 -0
- package/dist/sandbox/workspace.service.js +28 -5
- package/dist/session/managed-attempt-tools.d.ts +18 -0
- package/dist/session/managed-attempt-tools.js +854 -0
- package/dist/session/managed-build-coordinator.d.ts +5 -0
- package/dist/session/managed-build-coordinator.js +105 -0
- package/dist/session/managed-build-receipt.d.ts +21 -0
- package/dist/session/managed-build-receipt.js +335 -0
- package/dist/session/phase-completion.artifacts.d.ts +17 -0
- package/dist/session/phase-completion.artifacts.js +233 -24
- package/dist/session/phase-completion.d.ts +11 -2
- package/dist/session/phase-completion.helpers.d.ts +5 -0
- package/dist/session/phase-completion.helpers.js +51 -10
- package/dist/session/phase-completion.js +12 -3
- package/dist/session/phase-completion.prompts.d.ts +3 -3
- package/dist/session/phase-completion.prompts.js +21 -6
- package/dist/session/phase-completion.runtime.d.ts +13 -1
- package/dist/session/phase-completion.runtime.js +189 -8
- package/dist/session/phase-completion.schemas.js +108 -3
- package/dist/session/phase-completion.validation.d.ts +4 -1
- package/dist/session/phase-completion.validation.js +151 -21
- package/dist/session/provider-cli/ci-repair-readiness.d.ts +14 -0
- package/dist/session/provider-cli/ci-repair-readiness.js +105 -0
- package/dist/session/provider-cli/provider-cli.artifacts.d.ts +3 -2
- package/dist/session/provider-cli/provider-cli.artifacts.js +65 -7
- package/dist/session/provider-cli/provider-cli.deterministic.js +47 -1
- package/dist/session/provider-cli/provider-cli.process.d.ts +23 -0
- package/dist/session/provider-cli/provider-cli.process.js +420 -32
- package/dist/session/provider-cli/provider-cli.prompts.d.ts +4 -1
- package/dist/session/provider-cli/provider-cli.prompts.js +99 -19
- package/dist/session/provider-cli/provider-cli.qa-sidecars.js +1 -0
- package/dist/session/provider-cli/provider-cli.runtime.d.ts +1 -1
- package/dist/session/provider-cli/provider-cli.runtime.js +6 -3
- package/dist/session/provider-cli/provider-cli.service.d.ts +20 -2
- package/dist/session/provider-cli/provider-cli.service.js +464 -102
- package/dist/session/provider-cli/provider-cli.types.d.ts +5 -0
- package/dist/session/provider-cli/provider-output-state.d.ts +2 -0
- package/dist/session/provider-cli/provider-output-state.js +91 -16
- package/dist/session/provider-cli/provider-usage.d.ts +7 -0
- package/dist/session/provider-cli/provider-usage.js +28 -5
- package/dist/session/session-redaction.d.ts +3 -0
- package/dist/session/session-redaction.js +38 -0
- package/dist/session/session.base.d.ts +6 -1
- package/dist/session/session.service.d.ts +8 -2
- package/dist/session/session.service.js +522 -41
- package/dist/session/session.types.d.ts +70 -0
- package/dist/session/session.types.js +76 -4
- package/dist/shared/format/format-bytes.d.ts +1 -0
- package/dist/shared/format/format-bytes.js +9 -0
- package/dist/shared/path/path.d.ts +1 -0
- package/dist/shared/path/path.js +21 -1
- package/dist/shared/process/host-process-recovery.d.ts +12 -0
- package/dist/shared/process/host-process-recovery.js +78 -0
- package/dist/shared/process/host-process-supervisor.d.ts +59 -0
- package/dist/shared/process/host-process-supervisor.js +330 -0
- package/dist/shared/process/process.d.ts +1 -0
- package/dist/shared/process/process.js +1 -0
- package/dist/shared/redaction/redaction.js +12 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worktree/artifact-injector.d.ts +3 -2
- package/dist/worktree/artifact-injector.js +43 -4
- package/dist/worktree/dependency-warmer.d.ts +28 -1
- package/dist/worktree/dependency-warmer.js +308 -9
- package/dist/worktree/index.d.ts +2 -1
- package/dist/worktree/index.js +1 -0
- package/dist/worktree/prior-phase-artifact-hydrator.d.ts +2 -0
- package/dist/worktree/prior-phase-artifact-hydrator.js +136 -0
- package/dist/worktree/worktree-paths.d.ts +4 -0
- package/dist/worktree/worktree-paths.js +30 -0
- package/dist/worktree/worktree.base.d.ts +3 -0
- package/dist/worktree/worktree.service.d.ts +9 -2
- package/dist/worktree/worktree.service.js +217 -48
- package/dist/worktree/worktree.types.d.ts +5 -1
- package/package.json +5 -3
- package/runtime/windows-job-supervisor.ps1 +444 -0
- package/runtime/windows-listener-owner.ps1 +33 -0
package/dist/sandbox/process.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { StringDecoder } from "node:string_decoder";
|
|
3
|
+
const terminationSigkillDelayMs = 5_000;
|
|
4
|
+
const forcedCloseSettleDelayMs = 5_000;
|
|
5
|
+
const expectedStdinCloseErrorCodes = new Set(["EPIPE", "ECONNRESET", "ERR_STREAM_DESTROYED"]);
|
|
6
|
+
function isExpectedStdinCloseError(error) {
|
|
7
|
+
return "code" in error
|
|
8
|
+
&& typeof error.code === "string"
|
|
9
|
+
&& expectedStdinCloseErrorCodes.has(error.code);
|
|
10
|
+
}
|
|
3
11
|
export async function runSandboxProcess(request) {
|
|
4
12
|
const limit = Math.max(1024, request.maxOutputBytes ?? 1024 * 1024);
|
|
5
13
|
return await new Promise((resolve, reject) => {
|
|
@@ -14,10 +22,15 @@ export async function runSandboxProcess(request) {
|
|
|
14
22
|
let stderr = Buffer.alloc(0);
|
|
15
23
|
let truncated = false;
|
|
16
24
|
let settled = false;
|
|
25
|
+
let terminating = false;
|
|
26
|
+
let discardStdoutLineCallbacks = false;
|
|
17
27
|
let stdoutText = "";
|
|
18
28
|
const stdoutDecoder = new StringDecoder("utf8");
|
|
19
29
|
let stdoutLineChain = Promise.resolve();
|
|
20
30
|
let stdoutLineError = null;
|
|
31
|
+
let timeout = null;
|
|
32
|
+
let sigkillTimer = null;
|
|
33
|
+
let forcedSettleTimer = null;
|
|
21
34
|
const append = (current, chunk) => {
|
|
22
35
|
if (current.length >= limit) {
|
|
23
36
|
truncated = true;
|
|
@@ -31,9 +44,87 @@ export async function runSandboxProcess(request) {
|
|
|
31
44
|
}
|
|
32
45
|
return Buffer.concat([current, chunk.subarray(0, remaining)]);
|
|
33
46
|
};
|
|
34
|
-
|
|
47
|
+
const appendStderrLine = (line) => {
|
|
48
|
+
stderr = append(stderr, Buffer.from(`\n${line}\n`));
|
|
49
|
+
};
|
|
50
|
+
const cleanup = () => {
|
|
51
|
+
if (timeout)
|
|
52
|
+
clearTimeout(timeout);
|
|
53
|
+
if (sigkillTimer)
|
|
54
|
+
clearTimeout(sigkillTimer);
|
|
55
|
+
if (forcedSettleTimer)
|
|
56
|
+
clearTimeout(forcedSettleTimer);
|
|
57
|
+
child.stdout.removeListener("data", onStdoutData);
|
|
58
|
+
child.stderr.removeListener("data", onStderrData);
|
|
59
|
+
child.removeListener("error", onProcessError);
|
|
60
|
+
child.removeListener("close", onProcessClose);
|
|
61
|
+
request.signal?.removeEventListener("abort", abort);
|
|
62
|
+
};
|
|
63
|
+
const settle = async (code, force) => {
|
|
64
|
+
if (settled)
|
|
65
|
+
return;
|
|
66
|
+
settled = true;
|
|
67
|
+
discardStdoutLineCallbacks = force;
|
|
68
|
+
cleanup();
|
|
69
|
+
if (stdoutLineError) {
|
|
70
|
+
reject(stdoutLineError);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (!force) {
|
|
74
|
+
if (request.onStdoutLine)
|
|
75
|
+
stdoutText += stdoutDecoder.end();
|
|
76
|
+
if (request.onStdoutLine && stdoutText) {
|
|
77
|
+
stdoutLineChain = stdoutLineChain.then(() => request.onStdoutLine(stdoutText));
|
|
78
|
+
}
|
|
79
|
+
await stdoutLineChain.catch(error => { stdoutLineError ??= error; });
|
|
80
|
+
if (stdoutLineError) {
|
|
81
|
+
reject(stdoutLineError);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
resolve({
|
|
86
|
+
exitCode: code ?? 1,
|
|
87
|
+
stdout: stdout.toString("utf8"),
|
|
88
|
+
stderr: stderr.toString("utf8"),
|
|
89
|
+
truncated
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
const forceSettle = (reason) => {
|
|
93
|
+
if (settled)
|
|
94
|
+
return;
|
|
95
|
+
appendStderrLine(reason);
|
|
96
|
+
void settle(1, true);
|
|
97
|
+
};
|
|
98
|
+
const killChild = (signal) => {
|
|
99
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
100
|
+
return;
|
|
101
|
+
try {
|
|
102
|
+
child.kill(signal);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// The process may have exited between timeout/abort and close. Still let the
|
|
106
|
+
// supervisor timers settle if close never arrives.
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const terminate = (reason) => {
|
|
110
|
+
if (settled || terminating)
|
|
111
|
+
return;
|
|
112
|
+
terminating = true;
|
|
113
|
+
appendStderrLine(reason);
|
|
114
|
+
killChild("SIGTERM");
|
|
115
|
+
sigkillTimer = setTimeout(() => {
|
|
116
|
+
killChild("SIGKILL");
|
|
117
|
+
forcedSettleTimer = setTimeout(() => forceSettle("process did not close after SIGKILL; forcing supervisor settlement"), forcedCloseSettleDelayMs);
|
|
118
|
+
forcedSettleTimer.unref?.();
|
|
119
|
+
}, terminationSigkillDelayMs);
|
|
120
|
+
sigkillTimer.unref?.();
|
|
121
|
+
};
|
|
122
|
+
const abort = () => terminate(String(request.signal?.reason ?? "process aborted"));
|
|
123
|
+
const onStdoutData = (chunk) => {
|
|
124
|
+
if (settled)
|
|
125
|
+
return;
|
|
35
126
|
stdout = append(stdout, chunk);
|
|
36
|
-
if (!request.onStdoutLine)
|
|
127
|
+
if (!request.onStdoutLine || settled)
|
|
37
128
|
return;
|
|
38
129
|
stdoutText += stdoutDecoder.write(chunk);
|
|
39
130
|
if (Buffer.byteLength(stdoutText, "utf8") > 1024 * 1024 && !stdoutText.includes("\n")) {
|
|
@@ -48,6 +139,8 @@ export async function runSandboxProcess(request) {
|
|
|
48
139
|
child.stdout.pause();
|
|
49
140
|
for (const line of lines) {
|
|
50
141
|
stdoutLineChain = stdoutLineChain.then(async () => {
|
|
142
|
+
if (discardStdoutLineCallbacks)
|
|
143
|
+
return;
|
|
51
144
|
if (Buffer.byteLength(line, "utf8") > 1024 * 1024)
|
|
52
145
|
throw new Error("sandbox process emitted an oversized stdout line");
|
|
53
146
|
await request.onStdoutLine(line);
|
|
@@ -62,57 +155,49 @@ export async function runSandboxProcess(request) {
|
|
|
62
155
|
if (!settled)
|
|
63
156
|
child.stdout.resume();
|
|
64
157
|
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
158
|
+
};
|
|
159
|
+
const onStderrData = (chunk) => {
|
|
160
|
+
if (!settled)
|
|
161
|
+
stderr = append(stderr, chunk);
|
|
162
|
+
};
|
|
163
|
+
const onProcessError = (error) => {
|
|
68
164
|
if (!settled) {
|
|
69
165
|
settled = true;
|
|
166
|
+
cleanup();
|
|
70
167
|
reject(error);
|
|
71
168
|
}
|
|
72
|
-
}
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
169
|
+
};
|
|
170
|
+
const onStdinError = (error) => {
|
|
171
|
+
if (isExpectedStdinCloseError(error))
|
|
75
172
|
return;
|
|
76
|
-
|
|
77
|
-
setTimeout(() => child.kill("SIGKILL"), 5_000).unref?.();
|
|
78
|
-
if (!settled) {
|
|
79
|
-
// Completion is still resolved by close so stderr remains available.
|
|
80
|
-
stderr = append(stderr, Buffer.from(`\n${reason}\n`));
|
|
81
|
-
}
|
|
173
|
+
onProcessError(error);
|
|
82
174
|
};
|
|
83
|
-
const
|
|
175
|
+
const onProcessClose = (code) => {
|
|
176
|
+
void settle(code, false);
|
|
177
|
+
};
|
|
178
|
+
child.stdout.on("data", onStdoutData);
|
|
179
|
+
child.stderr.on("data", onStderrData);
|
|
180
|
+
child.on("error", onProcessError);
|
|
181
|
+
child.on("close", onProcessClose);
|
|
182
|
+
// Keep this listener for the lifetime of stdin. A pipe error can be emitted
|
|
183
|
+
// after the child close event has settled the supervisor promise.
|
|
184
|
+
child.stdin?.on("error", onStdinError);
|
|
185
|
+
timeout = request.timeoutMs
|
|
84
186
|
? setTimeout(() => terminate(`process timeout after ${request.timeoutMs}ms`), request.timeoutMs)
|
|
85
187
|
: null;
|
|
86
188
|
timeout?.unref?.();
|
|
87
|
-
const abort = () => terminate(String(request.signal?.reason ?? "process aborted"));
|
|
88
189
|
request.signal?.addEventListener("abort", abort, { once: true });
|
|
89
|
-
if (request.
|
|
90
|
-
|
|
190
|
+
if (request.signal?.aborted) {
|
|
191
|
+
abort();
|
|
91
192
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
settled = true;
|
|
96
|
-
if (timeout)
|
|
97
|
-
clearTimeout(timeout);
|
|
98
|
-
request.signal?.removeEventListener("abort", abort);
|
|
99
|
-
if (request.onStdoutLine)
|
|
100
|
-
stdoutText += stdoutDecoder.end();
|
|
101
|
-
if (request.onStdoutLine && stdoutText) {
|
|
102
|
-
stdoutLineChain = stdoutLineChain.then(() => request.onStdoutLine(stdoutText));
|
|
193
|
+
if (request.input !== undefined) {
|
|
194
|
+
try {
|
|
195
|
+
child.stdin.end(request.input);
|
|
103
196
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
reject(stdoutLineError);
|
|
107
|
-
return;
|
|
197
|
+
catch (error) {
|
|
198
|
+
onStdinError(error instanceof Error ? error : new Error(String(error)));
|
|
108
199
|
}
|
|
109
|
-
|
|
110
|
-
exitCode: code ?? 1,
|
|
111
|
-
stdout: stdout.toString("utf8"),
|
|
112
|
-
stderr: stderr.toString("utf8"),
|
|
113
|
-
truncated
|
|
114
|
-
});
|
|
115
|
-
});
|
|
200
|
+
}
|
|
116
201
|
});
|
|
117
202
|
}
|
|
118
203
|
export async function requireSandboxProcess(request) {
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { constants as fsConstants } from "node:fs";
|
|
2
3
|
import fs from "node:fs/promises";
|
|
3
4
|
import path from "node:path";
|
|
5
|
+
import { resolvePathIdentity } from "../shared/path/path.js";
|
|
6
|
+
const LOCK_HEARTBEAT_INTERVAL_MS = 30_000;
|
|
7
|
+
const LOCK_STALE_AFTER_MS = 2 * 60_000;
|
|
4
8
|
export class RepositoryCoordinatorLock {
|
|
5
9
|
stateRoot;
|
|
6
10
|
constructor(stateRoot) {
|
|
7
11
|
this.stateRoot = stateRoot;
|
|
8
12
|
}
|
|
9
13
|
async withLock(repoPath, action, signal) {
|
|
10
|
-
const resolved =
|
|
11
|
-
const
|
|
14
|
+
const resolved = await resolvePathIdentity(repoPath);
|
|
15
|
+
const keyInput = process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
16
|
+
const key = createHash("sha256").update(keyInput).digest("hex");
|
|
12
17
|
const root = path.join(this.stateRoot, "repo-locks");
|
|
13
18
|
const target = path.join(root, `${key}.lock`);
|
|
14
19
|
const token = randomUUID();
|
|
@@ -41,10 +46,12 @@ export class RepositoryCoordinatorLock {
|
|
|
41
46
|
await abortableDelay(50, signal);
|
|
42
47
|
}
|
|
43
48
|
}
|
|
49
|
+
const heartbeat = startHeartbeat(target, token);
|
|
44
50
|
try {
|
|
45
51
|
return await action();
|
|
46
52
|
}
|
|
47
53
|
finally {
|
|
54
|
+
clearInterval(heartbeat);
|
|
48
55
|
const owner = await readOwner(target);
|
|
49
56
|
if (owner?.token === token)
|
|
50
57
|
await fs.rm(target, { force: true });
|
|
@@ -58,7 +65,7 @@ async function staleLock(target) {
|
|
|
58
65
|
const owner = await readOwner(target);
|
|
59
66
|
if (!owner || typeof owner.pid !== "number" || !Number.isInteger(owner.pid) || owner.pid <= 0)
|
|
60
67
|
return true;
|
|
61
|
-
if (Date.now() - stat.mtimeMs
|
|
68
|
+
if (Date.now() - stat.mtimeMs >= LOCK_STALE_AFTER_MS)
|
|
62
69
|
return true;
|
|
63
70
|
try {
|
|
64
71
|
process.kill(owner.pid, 0);
|
|
@@ -68,6 +75,37 @@ async function staleLock(target) {
|
|
|
68
75
|
return true;
|
|
69
76
|
}
|
|
70
77
|
}
|
|
78
|
+
function startHeartbeat(target, token) {
|
|
79
|
+
let refreshing = false;
|
|
80
|
+
const timer = setInterval(() => {
|
|
81
|
+
if (refreshing)
|
|
82
|
+
return;
|
|
83
|
+
refreshing = true;
|
|
84
|
+
void refreshHeartbeat(target, token).finally(() => { refreshing = false; });
|
|
85
|
+
}, LOCK_HEARTBEAT_INTERVAL_MS);
|
|
86
|
+
timer.unref?.();
|
|
87
|
+
return timer;
|
|
88
|
+
}
|
|
89
|
+
async function refreshHeartbeat(target, token) {
|
|
90
|
+
let handle = null;
|
|
91
|
+
try {
|
|
92
|
+
handle = await fs.open(target, fsConstants.O_RDWR | fsConstants.O_NOFOLLOW);
|
|
93
|
+
const stat = await handle.stat();
|
|
94
|
+
if (!stat.isFile() || stat.size > 16 * 1024)
|
|
95
|
+
return;
|
|
96
|
+
const owner = await handle.readFile("utf8").then(value => JSON.parse(value)).catch(() => null);
|
|
97
|
+
if (owner?.token !== token)
|
|
98
|
+
return;
|
|
99
|
+
const now = new Date();
|
|
100
|
+
await handle.utimes(now, now);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// A missing or replaced lock is no longer owned by this action.
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
await handle?.close().catch(() => undefined);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
71
109
|
async function readOwner(target) {
|
|
72
110
|
return await fs.readFile(target, "utf8").then(value => JSON.parse(value)).catch(() => null);
|
|
73
111
|
}
|
|
@@ -2,6 +2,7 @@ import type { BridgeServiceConfig } from "../bridge/bridge.types.js";
|
|
|
2
2
|
import type { SessionEventSink } from "../session/session.base.js";
|
|
3
3
|
import { type RunOnceDispatchPacket, type RunOnceResultPacket } from "../session/session.types.js";
|
|
4
4
|
import type { SandboxService, ValidatedSandboxContract } from "./sandbox.types.js";
|
|
5
|
+
import { type RuntimeIsolationLease } from "../runtime/runtime-isolation-lease.js";
|
|
5
6
|
export interface SandboxRuntime {
|
|
6
7
|
runOnce(dispatch: RunOnceDispatchPacket, sink: SessionEventSink, signal?: AbortSignal): Promise<RunOnceResultPacket>;
|
|
7
8
|
reap(options?: SandboxReapOptions): Promise<void>;
|
|
@@ -41,6 +42,28 @@ export interface SandboxRuntimeSnapshot {
|
|
|
41
42
|
readonly active: number;
|
|
42
43
|
readonly degraded: number;
|
|
43
44
|
}
|
|
45
|
+
export interface SandboxContainerMemoryPeak {
|
|
46
|
+
readonly containerId: string;
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly role: string;
|
|
49
|
+
readonly peakMemoryBytes: number;
|
|
50
|
+
}
|
|
51
|
+
export interface SandboxPeakMemoryTelemetry {
|
|
52
|
+
readonly schemaVersion: 1;
|
|
53
|
+
readonly source: "docker_stats_sampled";
|
|
54
|
+
readonly sampleIntervalMs: number;
|
|
55
|
+
readonly sampleCount: number;
|
|
56
|
+
readonly failedSampleCount: number;
|
|
57
|
+
readonly peakMemoryBytes: number;
|
|
58
|
+
readonly firstSampledAt: string;
|
|
59
|
+
readonly lastSampledAt: string;
|
|
60
|
+
readonly containers: readonly SandboxContainerMemoryPeak[];
|
|
61
|
+
}
|
|
62
|
+
export interface DockerStatsMemorySample {
|
|
63
|
+
readonly containerId: string;
|
|
64
|
+
readonly name: string;
|
|
65
|
+
readonly memoryBytes: number;
|
|
66
|
+
}
|
|
44
67
|
export interface SandboxPublicationGrantRequest {
|
|
45
68
|
readonly dispatchAttemptId: string;
|
|
46
69
|
readonly leaseId: string;
|
|
@@ -84,6 +107,7 @@ export declare class DockerSandboxRuntime implements SandboxRuntime {
|
|
|
84
107
|
private buildImageCore;
|
|
85
108
|
private runBuildStage;
|
|
86
109
|
private ensureBuildxBuilder;
|
|
110
|
+
private stopBuildxBuilder;
|
|
87
111
|
private initializeVolumes;
|
|
88
112
|
private inspectContainerState;
|
|
89
113
|
private initializeServiceSecrets;
|
|
@@ -102,9 +126,12 @@ export declare class DockerSandboxRuntime implements SandboxRuntime {
|
|
|
102
126
|
private readImageCacheRecord;
|
|
103
127
|
private collectExpiredImages;
|
|
104
128
|
private purgeRestrictedAuditArtifacts;
|
|
129
|
+
private inspectImageMetadata;
|
|
130
|
+
private startPeakMemorySampler;
|
|
105
131
|
private docker;
|
|
106
132
|
private dockerArgs;
|
|
107
133
|
}
|
|
134
|
+
export declare function managedBranchUpdateExpectedValue(current: string, expectedInput: string, imported: string): string;
|
|
108
135
|
export declare function publicationPushArgs(branch: string, resultCommitSha: string, inputCommitSha: string, remoteCommitSha: string, remote?: string): string[] | null;
|
|
109
136
|
export declare function validatePublicationCredential(credential: SandboxPublicationCredential, nowMs?: number): void;
|
|
110
137
|
export declare function publicationCredentialEnvironment(credential: SandboxPublicationCredential, environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
@@ -129,6 +156,7 @@ export declare function selectSandboxConfigRefs(executionPolicy: string, configu
|
|
|
129
156
|
readonly refs: readonly string[];
|
|
130
157
|
readonly approvedSha: string | null;
|
|
131
158
|
};
|
|
159
|
+
export declare function selectSandboxInputRefs(worktreeKey: string, baseBranch: string): readonly string[];
|
|
132
160
|
export declare function sandboxTimeLimits(environment?: NodeJS.ProcessEnv): {
|
|
133
161
|
readonly hardWallMs: number;
|
|
134
162
|
readonly idleMs: number;
|
|
@@ -144,8 +172,10 @@ export interface ImageCacheRecord {
|
|
|
144
172
|
readonly overlayTag: string;
|
|
145
173
|
readonly imageDigest: string;
|
|
146
174
|
readonly helperImageDigest: string;
|
|
175
|
+
readonly bridgeRuntimeDigest?: string;
|
|
147
176
|
readonly policyDigest: string;
|
|
148
177
|
}
|
|
178
|
+
export declare function sandboxWorkerFailureMessage(exitCode: number, stderr: string, containerError: string | null): string;
|
|
149
179
|
interface ResourceNames {
|
|
150
180
|
prefix: string;
|
|
151
181
|
container: string;
|
|
@@ -162,18 +192,30 @@ interface SandboxRuntimeVolume {
|
|
|
162
192
|
readonly name: string;
|
|
163
193
|
readonly role: string;
|
|
164
194
|
}
|
|
195
|
+
export declare function parseDockerMemoryBytes(value: string): number | null;
|
|
196
|
+
export declare function parseDockerStatsMemorySample(output: string): DockerStatsMemorySample[];
|
|
165
197
|
interface ActiveSandbox {
|
|
166
198
|
dispatchAttemptId: string;
|
|
167
199
|
fencingToken: string;
|
|
168
200
|
expiresAt: string;
|
|
169
201
|
controller: AbortController;
|
|
170
202
|
phase: "starting" | "active";
|
|
203
|
+
runtimeLease: RuntimeIsolationLease;
|
|
171
204
|
ledger: SandboxLedger | null;
|
|
172
205
|
leaseControlKey: string;
|
|
173
206
|
}
|
|
174
207
|
export declare function assertSandboxImportCurrent(expected: Pick<ActiveSandbox, "dispatchAttemptId" | "fencingToken" | "expiresAt">, current: Pick<ActiveSandbox, "dispatchAttemptId" | "fencingToken"> | undefined, signal: AbortSignal, nowMs?: number): void;
|
|
175
208
|
interface SandboxLedger {
|
|
176
|
-
schemaVersion:
|
|
209
|
+
schemaVersion: 3;
|
|
210
|
+
runtimeLease: RuntimeIsolationLease;
|
|
211
|
+
resources: ResourceNames;
|
|
212
|
+
labels: Record<string, string>;
|
|
213
|
+
includeCredentials?: boolean;
|
|
214
|
+
peakMemory?: SandboxPeakMemoryTelemetry;
|
|
215
|
+
}
|
|
216
|
+
interface LegacySandboxLedger {
|
|
217
|
+
schemaVersion: 2;
|
|
218
|
+
resourceLeaseId: string;
|
|
177
219
|
dispatchAttemptId: string;
|
|
178
220
|
fencingToken: string;
|
|
179
221
|
runnerInstanceId: string;
|
|
@@ -181,29 +223,68 @@ interface SandboxLedger {
|
|
|
181
223
|
resources: ResourceNames;
|
|
182
224
|
labels: Record<string, string>;
|
|
183
225
|
includeCredentials?: boolean;
|
|
226
|
+
peakMemory?: SandboxPeakMemoryTelemetry;
|
|
184
227
|
}
|
|
228
|
+
export declare function validSandboxLedger(value: unknown): boolean;
|
|
229
|
+
export declare function normalizeSandboxLedger(value: unknown): SandboxLedger | null;
|
|
185
230
|
export declare function resourceNames(attemptHex: string, perAttemptCaches?: readonly string[]): ResourceNames;
|
|
186
231
|
export declare function dockerObjectId(output: string, kind: "container" | "network"): string;
|
|
187
232
|
export declare function sandboxRuntimeVolumes(resources: ResourceNames, includeEgressPolicy?: boolean, includeCredentials?: boolean): SandboxRuntimeVolume[];
|
|
188
233
|
export declare function sandboxRuntimeCleanupVolumeNames(resources: ResourceNames, includeCredentials?: boolean): string[];
|
|
234
|
+
export declare function sandboxResourceLabels(dispatch: RunOnceDispatchPacket, image: string, contract: string): Record<string, string>;
|
|
189
235
|
export declare function mayReapManagedResource(labels: Readonly<Record<string, string>>, runnerId: string, liveAttempts: ReadonlySet<string>): boolean;
|
|
190
|
-
export declare function shouldReapSandboxLedger(ledger: Readonly<Pick<SandboxLedger, "dispatchAttemptId" | "fencingToken" | "runnerInstanceId" | "expiresAt">>, currentRunnerInstanceId: string, serverAttempt: SandboxAttemptSnapshot | undefined, snapshotAvailable: boolean, nowMs?: number): boolean;
|
|
191
|
-
export declare function
|
|
236
|
+
export declare function shouldReapSandboxLedger(ledger: Readonly<Pick<SandboxLedger, "runtimeLease">> | Readonly<Pick<LegacySandboxLedger, "dispatchAttemptId" | "fencingToken" | "runnerInstanceId" | "expiresAt">>, currentRunnerInstanceId: string, serverAttempt: SandboxAttemptSnapshot | undefined, snapshotAvailable: boolean, nowMs?: number): boolean;
|
|
237
|
+
export declare function bridgeRuntimeCacheMatches(record: Pick<ImageCacheRecord, "bridgeRuntimeDigest"> | null | undefined, expectedDigest: string, imageLabels: readonly (Readonly<Record<string, string>> | null | undefined)[]): boolean;
|
|
238
|
+
export declare function prepareOverlayBuildRoot(overlayRoot: string): Promise<void>;
|
|
239
|
+
export declare function extractOciLayoutArchive(archivePath: string, destination: string, signal?: AbortSignal): Promise<void>;
|
|
240
|
+
export declare function ociLayoutContextRef(layoutPath: string): string;
|
|
241
|
+
export declare function formatOciExtractionFailure(result: {
|
|
242
|
+
readonly stdout: string;
|
|
243
|
+
readonly stderr: string;
|
|
244
|
+
readonly truncated: boolean;
|
|
245
|
+
}): string;
|
|
246
|
+
export declare function readOciLayoutImageDigest(layoutRoot: string): Promise<string>;
|
|
247
|
+
export declare function removeExtractedOciLayout(layoutRoot: string): Promise<void>;
|
|
248
|
+
export declare function ociLayoutScanTarget(layoutPath: string): string;
|
|
249
|
+
export declare function agentOverlayDockerignore(): string;
|
|
250
|
+
export interface SandboxImageCacheKeyInput {
|
|
251
|
+
readonly contextDigest: string;
|
|
252
|
+
readonly contractDigest: string;
|
|
253
|
+
readonly platform: string;
|
|
254
|
+
readonly architecture: string;
|
|
255
|
+
readonly bridgeVersion: string;
|
|
256
|
+
readonly bridgeRuntimeDigest: string;
|
|
257
|
+
readonly toolingImage: string;
|
|
258
|
+
readonly codexVersion: string;
|
|
259
|
+
readonly policy: string;
|
|
260
|
+
readonly policyConfigurationDigest: string;
|
|
261
|
+
}
|
|
262
|
+
export declare function sandboxImageCacheKey(input: SandboxImageCacheKeyInput): string;
|
|
263
|
+
export declare function bridgeRuntimeContentDigest(packageRoot: string): Promise<string>;
|
|
264
|
+
export declare function agentOverlayDockerfile(toolingImage: string, projectContextName: string, installMode: BridgePackageInstallMode, codexVersion: string): string;
|
|
265
|
+
export type BridgePackageInstallMode = "ci" | "install";
|
|
266
|
+
export declare function copyBridgePackageManifests(packageRoot: string, bridgeRoot: string): Promise<BridgePackageInstallMode>;
|
|
267
|
+
export declare function bridgePackageInstallCommand(mode: BridgePackageInstallMode, codexVersion: string): string;
|
|
268
|
+
export declare function buildxBuildArgs(builder: string, metadataFile: string, tail: readonly string[], environment?: NodeJS.ProcessEnv, options?: {
|
|
269
|
+
readonly load?: boolean;
|
|
270
|
+
}): string[];
|
|
192
271
|
export declare function buildResourcePolicy(environment?: NodeJS.ProcessEnv): {
|
|
193
272
|
readonly nofile: number;
|
|
194
273
|
readonly sharedMemory: string;
|
|
195
274
|
readonly builderPidsLimit: number;
|
|
275
|
+
readonly builderMemoryBytes: number;
|
|
196
276
|
};
|
|
197
277
|
export declare function dedicatedBuildxBuilderName(runnerId: string, dockerContext: string | null | undefined): string;
|
|
198
278
|
export declare function dedicatedBuildxBuilderContainerName(builder: string): string;
|
|
199
279
|
export declare function selectImageGcCandidates(records: readonly ImageCacheRecord[], keepPerProject: number, ttlMs: number, nowMs: number): ImageCacheRecord[];
|
|
200
280
|
export declare function workerCreateArgs(dispatch: RunOnceDispatchPacket, contract: ValidatedSandboxContract, resources: ResourceNames, labels: Record<string, string>, image: string, securityOptions?: readonly string[]): string[];
|
|
281
|
+
export declare function validateSandboxMemoryReservation(dispatch: RunOnceDispatchPacket, contract: ValidatedSandboxContract): void;
|
|
201
282
|
export declare function cacheVolumeArgs(contract: ValidatedSandboxContract, resources: ResourceNames): string[];
|
|
202
283
|
export declare function proxyEnvironmentArgs(dispatch: RunOnceDispatchPacket, contract: ValidatedSandboxContract, resources: ResourceNames): string[];
|
|
203
284
|
export declare function egressProxyConfig(allowedHosts: readonly string[]): string;
|
|
204
285
|
export declare function serviceContainerArgs(service: SandboxService, image: string, resources: ResourceNames, labels: Record<string, string>, securityOptions: readonly string[], environment?: NodeJS.ProcessEnv): string[];
|
|
205
286
|
export declare function resolveSandboxSecurityOptions(executionPolicy: string, environment?: NodeJS.ProcessEnv): string[];
|
|
206
|
-
export declare function networkCreateArgs(dispatch: RunOnceDispatchPacket, environment?: NodeJS.ProcessEnv): string[];
|
|
287
|
+
export declare function networkCreateArgs(dispatch: RunOnceDispatchPacket, environment?: NodeJS.ProcessEnv, allowOpenPublicSandbox?: boolean): string[];
|
|
207
288
|
export declare function classifySandboxRuntimeFailure(value: string): {
|
|
208
289
|
readonly code: string;
|
|
209
290
|
readonly phase: string;
|