@namzu/sandbox 6.1.0 → 7.1.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/CHANGELOG.md +62 -0
- package/README.md +29 -1
- package/dist/backends/aci-standby-pool/index.d.ts.map +1 -1
- package/dist/backends/aci-standby-pool/index.js +163 -111
- package/dist/backends/aci-standby-pool/index.js.map +1 -1
- package/dist/backends/docker/index.d.ts.map +1 -1
- package/dist/backends/docker/index.js +212 -149
- package/dist/backends/docker/index.js.map +1 -1
- package/dist/backends/firecracker/index.d.ts.map +1 -1
- package/dist/backends/firecracker/index.js +154 -71
- package/dist/backends/firecracker/index.js.map +1 -1
- package/dist/backends/firecracker/protocol.d.ts +19 -27
- package/dist/backends/firecracker/protocol.d.ts.map +1 -1
- package/dist/backends/firecracker/protocol.js +57 -32
- package/dist/backends/firecracker/protocol.js.map +1 -1
- package/dist/backends/firecracker/transport.d.ts +26 -7
- package/dist/backends/firecracker/transport.d.ts.map +1 -1
- package/dist/backends/firecracker/transport.js +235 -72
- package/dist/backends/firecracker/transport.js.map +1 -1
- package/dist/backends/http-worker-client.d.ts +17 -0
- package/dist/backends/http-worker-client.d.ts.map +1 -0
- package/dist/backends/http-worker-client.js +173 -0
- package/dist/backends/http-worker-client.js.map +1 -0
- package/dist/backends/readiness.d.ts +43 -0
- package/dist/backends/readiness.d.ts.map +1 -0
- package/dist/backends/readiness.js +150 -0
- package/dist/backends/readiness.js.map +1 -0
- package/dist/backends/remote-execution-controller.d.ts +76 -0
- package/dist/backends/remote-execution-controller.d.ts.map +1 -0
- package/dist/backends/remote-execution-controller.js +294 -0
- package/dist/backends/remote-execution-controller.js.map +1 -0
- package/dist/egress/proxy.d.ts.map +1 -1
- package/dist/egress/proxy.js +20 -3
- package/dist/egress/proxy.js.map +1 -1
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/backends/aci-standby-pool/index.ts +199 -117
- package/src/backends/docker/index.ts +235 -175
- package/src/backends/firecracker/index.ts +163 -63
- package/src/backends/firecracker/protocol.ts +67 -33
- package/src/backends/firecracker/transport.ts +301 -77
- package/src/backends/http-worker-client.ts +230 -0
- package/src/backends/readiness.ts +173 -0
- package/src/backends/remote-execution-controller.ts +462 -0
- package/src/egress/proxy.ts +18 -3
- package/src/index.ts +27 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { withHint } from '@namzu/sdk';
|
|
2
|
+
import { RemoteCancellationUnsupportedError, RemoteCommandError, RemoteExecutionController, RemoteProtocolError, } from './remote-execution-controller.js';
|
|
3
|
+
function parseWorkerEvent(line) {
|
|
4
|
+
let parsed;
|
|
5
|
+
try {
|
|
6
|
+
parsed = JSON.parse(line);
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
throw new RemoteProtocolError(`worker emitted malformed NDJSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
10
|
+
}
|
|
11
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
12
|
+
throw new RemoteProtocolError('worker emitted an event without an object body');
|
|
13
|
+
}
|
|
14
|
+
const event = parsed;
|
|
15
|
+
if ((event.type === 'stdout_delta' || event.type === 'stderr_delta') &&
|
|
16
|
+
typeof event.data === 'string') {
|
|
17
|
+
return event;
|
|
18
|
+
}
|
|
19
|
+
if (event.type === 'error' && typeof event.error === 'string')
|
|
20
|
+
return event;
|
|
21
|
+
if (event.type === 'result' &&
|
|
22
|
+
Number.isFinite(event.exitCode) &&
|
|
23
|
+
typeof event.timedOut === 'boolean' &&
|
|
24
|
+
Number.isFinite(event.durationMs) &&
|
|
25
|
+
(event.signal === undefined || typeof event.signal === 'string') &&
|
|
26
|
+
(event.stdoutTruncated === undefined || typeof event.stdoutTruncated === 'boolean') &&
|
|
27
|
+
(event.stderrTruncated === undefined || typeof event.stderrTruncated === 'boolean')) {
|
|
28
|
+
return event;
|
|
29
|
+
}
|
|
30
|
+
throw new RemoteProtocolError(`worker emitted an invalid ${String(event.type)} event`);
|
|
31
|
+
}
|
|
32
|
+
async function readExecution(baseUrl, executionId, command, argv, opts, transportSignal) {
|
|
33
|
+
let response;
|
|
34
|
+
try {
|
|
35
|
+
response = await fetch(`${baseUrl}/execute`, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: { 'content-type': 'application/json' },
|
|
38
|
+
signal: transportSignal,
|
|
39
|
+
body: JSON.stringify({
|
|
40
|
+
...(executionId ? { executionId } : {}),
|
|
41
|
+
command,
|
|
42
|
+
args: argv ?? [],
|
|
43
|
+
cwd: opts?.cwd,
|
|
44
|
+
env: opts?.env,
|
|
45
|
+
timeoutMs: opts?.timeout,
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const cause = error instanceof Error ? error.cause : undefined;
|
|
51
|
+
const causeMessage = cause instanceof Error
|
|
52
|
+
? `${cause.message}${cause.code ? ` (${cause.code})` : ''}`
|
|
53
|
+
: cause
|
|
54
|
+
? String(cause)
|
|
55
|
+
: 'unknown';
|
|
56
|
+
throw withHint(new Error(`namzu-sandbox /execute fetch failed (baseUrl=${baseUrl}): ${error instanceof Error ? error.message : String(error)} — cause: ${causeMessage}`, { cause: error }), 'The worker was reachable when the sandbox started, so it has most likely exited, been killed, or become unreachable since. Check the container logs and runtime exit state.');
|
|
57
|
+
}
|
|
58
|
+
if (!response.ok || !response.body) {
|
|
59
|
+
throw new Error(`execute failed: HTTP ${response.status} ${await response.text()}`);
|
|
60
|
+
}
|
|
61
|
+
const decoder = new TextDecoder();
|
|
62
|
+
const reader = response.body.getReader();
|
|
63
|
+
let buffered = '';
|
|
64
|
+
let stdout = '';
|
|
65
|
+
let stderr = '';
|
|
66
|
+
let terminal;
|
|
67
|
+
let terminalCount = 0;
|
|
68
|
+
const consume = (rawLine) => {
|
|
69
|
+
if (!rawLine.trim())
|
|
70
|
+
return;
|
|
71
|
+
const event = parseWorkerEvent(rawLine);
|
|
72
|
+
if (terminalCount > 0) {
|
|
73
|
+
throw new RemoteProtocolError('worker emitted data after its terminal event');
|
|
74
|
+
}
|
|
75
|
+
if (event.type === 'stdout_delta') {
|
|
76
|
+
stdout += event.data;
|
|
77
|
+
opts?.onOutput?.({ stream: 'stdout', data: event.data });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (event.type === 'stderr_delta') {
|
|
81
|
+
stderr += event.data;
|
|
82
|
+
opts?.onOutput?.({ stream: 'stderr', data: event.data });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
terminalCount += 1;
|
|
86
|
+
if (event.type === 'error')
|
|
87
|
+
throw new RemoteCommandError(event.error);
|
|
88
|
+
terminal = event;
|
|
89
|
+
};
|
|
90
|
+
for (;;) {
|
|
91
|
+
const { value, done } = await reader.read();
|
|
92
|
+
if (done)
|
|
93
|
+
break;
|
|
94
|
+
buffered += decoder.decode(value, { stream: true });
|
|
95
|
+
let newline = buffered.indexOf('\n');
|
|
96
|
+
while (newline !== -1) {
|
|
97
|
+
consume(buffered.slice(0, newline));
|
|
98
|
+
buffered = buffered.slice(newline + 1);
|
|
99
|
+
newline = buffered.indexOf('\n');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
buffered += decoder.decode();
|
|
103
|
+
if (buffered.trim())
|
|
104
|
+
consume(buffered);
|
|
105
|
+
if (terminalCount !== 1 || !terminal) {
|
|
106
|
+
throw new RemoteProtocolError('worker response ended without exactly one result event');
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
exitCode: terminal.exitCode,
|
|
110
|
+
stdout,
|
|
111
|
+
stderr,
|
|
112
|
+
...(terminal.signal ? { signal: terminal.signal } : {}),
|
|
113
|
+
timedOut: terminal.timedOut,
|
|
114
|
+
durationMs: terminal.durationMs,
|
|
115
|
+
...(terminal.stdoutTruncated !== undefined
|
|
116
|
+
? { stdoutTruncated: terminal.stdoutTruncated }
|
|
117
|
+
: {}),
|
|
118
|
+
...(terminal.stderrTruncated !== undefined
|
|
119
|
+
? { stderrTruncated: terminal.stderrTruncated }
|
|
120
|
+
: {}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* A per-sandbox HTTP worker client. The controller caches protocol support for
|
|
125
|
+
* that worker and gives every v2 command an identity before it can be admitted.
|
|
126
|
+
*/
|
|
127
|
+
export class HttpWorkerClient {
|
|
128
|
+
controller;
|
|
129
|
+
constructor(baseUrl) {
|
|
130
|
+
const adapter = {
|
|
131
|
+
label: 'HTTP worker',
|
|
132
|
+
reserve: async (signal) => {
|
|
133
|
+
const response = await fetch(`${baseUrl}/executions/reserve`, {
|
|
134
|
+
method: 'POST',
|
|
135
|
+
signal,
|
|
136
|
+
});
|
|
137
|
+
if (response.status === 404) {
|
|
138
|
+
throw new RemoteCancellationUnsupportedError('This sandbox worker does not support the execution-cancellation lease protocol. Rebuild the worker image or standby-pool profile before passing SandboxExecOptions.signal; refusing rather than pretending cancellation is active.');
|
|
139
|
+
}
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
throw new Error(`execution reservation failed: HTTP ${response.status} ${await response.text()}`);
|
|
142
|
+
}
|
|
143
|
+
return await response.json();
|
|
144
|
+
},
|
|
145
|
+
cancel: async (executionId, signal) => {
|
|
146
|
+
const response = await fetch(`${baseUrl}/cancel`, {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
headers: { 'content-type': 'application/json' },
|
|
149
|
+
body: JSON.stringify({ executionId }),
|
|
150
|
+
signal,
|
|
151
|
+
});
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
throw new Error(`cancel failed: HTTP ${response.status} ${await response.text()}`);
|
|
154
|
+
}
|
|
155
|
+
return await response.json();
|
|
156
|
+
},
|
|
157
|
+
execute: async (executionId, command, argv, opts, signal) => await readExecution(baseUrl, executionId, command, argv, opts, signal),
|
|
158
|
+
};
|
|
159
|
+
this.controller = new RemoteExecutionController(adapter);
|
|
160
|
+
}
|
|
161
|
+
async exec(command, argv, opts) {
|
|
162
|
+
return await this.controller.exec(command, argv, opts);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Compatibility entry point for focused consumers. Sandbox backends keep one
|
|
167
|
+
* {@link HttpWorkerClient} per remote sandbox so capability state is not shared
|
|
168
|
+
* across peers and is not re-probed for every command.
|
|
169
|
+
*/
|
|
170
|
+
export async function execViaHttpWorker(baseUrl, command, argv, opts) {
|
|
171
|
+
return await new HttpWorkerClient(baseUrl).exec(command, argv, opts);
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=http-worker-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-worker-client.js","sourceRoot":"","sources":["../../src/backends/http-worker-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One owner for backend readiness clocks.
|
|
3
|
+
*
|
|
4
|
+
* A timestamp checked between retries is not a deadline when the thing inside
|
|
5
|
+
* a retry can remain pending. This owner races every foreign promise against
|
|
6
|
+
* the remaining operation budget and supplies a private signal so cooperative
|
|
7
|
+
* transports can release their sockets too.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Readiness failure teardown is deliberately a second, short budget. The
|
|
11
|
+
* health deadline has already expired, so pretending cleanup fits inside it
|
|
12
|
+
* either skips cleanup entirely or keeps `create()` pending without a bound.
|
|
13
|
+
*/
|
|
14
|
+
export declare const FAILURE_CLEANUP_GRACE_MS = 1000;
|
|
15
|
+
export interface ReadinessOptions {
|
|
16
|
+
readonly timeoutMs: number;
|
|
17
|
+
readonly pollIntervalMs: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class OperationDeadlineExpired extends Error {
|
|
20
|
+
readonly label: string;
|
|
21
|
+
constructor(label: string);
|
|
22
|
+
}
|
|
23
|
+
export declare function resolveReadinessOptions(owner: string, timeoutMs: number | undefined, pollIntervalMs: number | undefined, defaults: ReadinessOptions): ReadinessOptions;
|
|
24
|
+
export declare class OperationDeadline {
|
|
25
|
+
readonly label: string;
|
|
26
|
+
private readonly callerSignal?;
|
|
27
|
+
private readonly expiresAt;
|
|
28
|
+
constructor(timeoutMs: number, label: string, callerSignal?: AbortSignal | undefined);
|
|
29
|
+
remainingMs(): number;
|
|
30
|
+
run<T>(operation: (signal: AbortSignal) => Promise<T>): Promise<T>;
|
|
31
|
+
delay(maximumMs: number): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export declare function probeHttpHealth(url: string, signal: AbortSignal): Promise<{
|
|
34
|
+
readonly ok: boolean;
|
|
35
|
+
readonly status: number;
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Failure cleanup must never replace or indefinitely delay the primary
|
|
39
|
+
* readiness error. Cooperative cleanup observes the signal; an implementation
|
|
40
|
+
* that ignores it is still detached from the caller by the independent race.
|
|
41
|
+
*/
|
|
42
|
+
export declare function runFailureCleanup(cleanup: (signal: AbortSignal) => Promise<void>, graceMs?: number): Promise<void>;
|
|
43
|
+
//# sourceMappingURL=readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../../src/backends/readiness.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,OAAQ,CAAA;AAE7C,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAC/B;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAAb,KAAK,EAAE,MAAM;CAIlC;AAUD,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,QAAQ,EAAE,gBAAgB,GACxB,gBAAgB,CAQlB;AAED,qBAAa,iBAAiB;IAK5B,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;IAL/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;gBAGjC,SAAS,EAAE,MAAM,EACR,KAAK,EAAE,MAAM,EACL,YAAY,CAAC,EAAE,WAAW,YAAA;IAM5C,WAAW,IAAI,MAAM;IAIf,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IA6ClE,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAsB7C;AAED,wBAAsB,eAAe,CACpC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,WAAW,GACjB,OAAO,CAAC;IAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAY5D;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,EAC/C,OAAO,SAA2B,GAChC,OAAO,CAAC,IAAI,CAAC,CASf"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One owner for backend readiness clocks.
|
|
3
|
+
*
|
|
4
|
+
* A timestamp checked between retries is not a deadline when the thing inside
|
|
5
|
+
* a retry can remain pending. This owner races every foreign promise against
|
|
6
|
+
* the remaining operation budget and supplies a private signal so cooperative
|
|
7
|
+
* transports can release their sockets too.
|
|
8
|
+
*/
|
|
9
|
+
const MAX_NODE_TIMER_MS = 2_147_483_647;
|
|
10
|
+
/**
|
|
11
|
+
* Readiness failure teardown is deliberately a second, short budget. The
|
|
12
|
+
* health deadline has already expired, so pretending cleanup fits inside it
|
|
13
|
+
* either skips cleanup entirely or keeps `create()` pending without a bound.
|
|
14
|
+
*/
|
|
15
|
+
export const FAILURE_CLEANUP_GRACE_MS = 1_000;
|
|
16
|
+
export class OperationDeadlineExpired extends Error {
|
|
17
|
+
label;
|
|
18
|
+
constructor(label) {
|
|
19
|
+
super(`${label} deadline expired`);
|
|
20
|
+
this.label = label;
|
|
21
|
+
this.name = 'OperationDeadlineExpired';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function assertTimerValue(field, value) {
|
|
25
|
+
if (!Number.isSafeInteger(value) || value <= 0 || value > MAX_NODE_TIMER_MS) {
|
|
26
|
+
throw new RangeError(`${field} must be a positive safe integer no greater than ${MAX_NODE_TIMER_MS}; received ${String(value)}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function resolveReadinessOptions(owner, timeoutMs, pollIntervalMs, defaults) {
|
|
30
|
+
const resolved = {
|
|
31
|
+
timeoutMs: timeoutMs ?? defaults.timeoutMs,
|
|
32
|
+
pollIntervalMs: pollIntervalMs ?? defaults.pollIntervalMs,
|
|
33
|
+
};
|
|
34
|
+
assertTimerValue(`${owner}.readyTimeoutMs`, resolved.timeoutMs);
|
|
35
|
+
assertTimerValue(`${owner}.readyPollIntervalMs`, resolved.pollIntervalMs);
|
|
36
|
+
return resolved;
|
|
37
|
+
}
|
|
38
|
+
export class OperationDeadline {
|
|
39
|
+
label;
|
|
40
|
+
callerSignal;
|
|
41
|
+
expiresAt;
|
|
42
|
+
constructor(timeoutMs, label, callerSignal) {
|
|
43
|
+
this.label = label;
|
|
44
|
+
this.callerSignal = callerSignal;
|
|
45
|
+
assertTimerValue(`${label} timeout`, timeoutMs);
|
|
46
|
+
this.expiresAt = performance.now() + timeoutMs;
|
|
47
|
+
}
|
|
48
|
+
remainingMs() {
|
|
49
|
+
return Math.max(0, this.expiresAt - performance.now());
|
|
50
|
+
}
|
|
51
|
+
async run(operation) {
|
|
52
|
+
this.callerSignal?.throwIfAborted();
|
|
53
|
+
const remaining = this.remainingMs();
|
|
54
|
+
if (remaining <= 0)
|
|
55
|
+
throw new OperationDeadlineExpired(this.label);
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const expired = new OperationDeadlineExpired(this.label);
|
|
58
|
+
let timer;
|
|
59
|
+
let onCallerAbort;
|
|
60
|
+
const cancelled = this.callerSignal
|
|
61
|
+
? new Promise((_resolve, reject) => {
|
|
62
|
+
onCallerAbort = () => {
|
|
63
|
+
const reason = this.callerSignal?.reason;
|
|
64
|
+
controller.abort(reason);
|
|
65
|
+
reject(reason);
|
|
66
|
+
};
|
|
67
|
+
this.callerSignal?.addEventListener('abort', onCallerAbort, {
|
|
68
|
+
once: true,
|
|
69
|
+
});
|
|
70
|
+
})
|
|
71
|
+
: undefined;
|
|
72
|
+
const expiry = new Promise((_resolve, reject) => {
|
|
73
|
+
timer = setTimeout(() => {
|
|
74
|
+
controller.abort(expired);
|
|
75
|
+
reject(expired);
|
|
76
|
+
}, remaining);
|
|
77
|
+
});
|
|
78
|
+
const pending = Promise.resolve().then(() => operation(controller.signal));
|
|
79
|
+
try {
|
|
80
|
+
const value = await Promise.race(cancelled ? [pending, expiry, cancelled] : [pending, expiry]);
|
|
81
|
+
// Publication fence: a foreign promise that settles at the boundary
|
|
82
|
+
// does not get to turn an already-expired attempt into readiness.
|
|
83
|
+
if (this.callerSignal?.aborted)
|
|
84
|
+
throw this.callerSignal.reason;
|
|
85
|
+
if (controller.signal.aborted || this.remainingMs() <= 0) {
|
|
86
|
+
controller.abort(expired);
|
|
87
|
+
throw expired;
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
if (timer !== undefined)
|
|
93
|
+
clearTimeout(timer);
|
|
94
|
+
if (onCallerAbort)
|
|
95
|
+
this.callerSignal?.removeEventListener('abort', onCallerAbort);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async delay(maximumMs) {
|
|
99
|
+
await this.run((signal) => new Promise((resolve, reject) => {
|
|
100
|
+
if (signal.aborted) {
|
|
101
|
+
reject(signal.reason);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const finish = (err) => {
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
signal.removeEventListener('abort', abort);
|
|
107
|
+
if (err === undefined)
|
|
108
|
+
resolve();
|
|
109
|
+
else
|
|
110
|
+
reject(err);
|
|
111
|
+
};
|
|
112
|
+
const abort = () => {
|
|
113
|
+
finish(signal.reason);
|
|
114
|
+
};
|
|
115
|
+
const timer = setTimeout(() => finish(), maximumMs);
|
|
116
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export async function probeHttpHealth(url, signal) {
|
|
121
|
+
signal.throwIfAborted();
|
|
122
|
+
const response = await fetch(url, { signal });
|
|
123
|
+
const result = { ok: response.ok, status: response.status };
|
|
124
|
+
try {
|
|
125
|
+
await response.body?.cancel();
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// The status is already known. A body cancellation failure must not
|
|
129
|
+
// hide it; the owning deadline still aborts the transport if needed.
|
|
130
|
+
}
|
|
131
|
+
signal.throwIfAborted();
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Failure cleanup must never replace or indefinitely delay the primary
|
|
136
|
+
* readiness error. Cooperative cleanup observes the signal; an implementation
|
|
137
|
+
* that ignores it is still detached from the caller by the independent race.
|
|
138
|
+
*/
|
|
139
|
+
export async function runFailureCleanup(cleanup, graceMs = FAILURE_CLEANUP_GRACE_MS) {
|
|
140
|
+
const deadline = new OperationDeadline(graceMs, 'sandbox failure cleanup');
|
|
141
|
+
try {
|
|
142
|
+
await deadline.run(cleanup);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// The readiness failure remains primary. Promise.race installed a
|
|
146
|
+
// rejection observer on the losing cleanup promise, so a late failure
|
|
147
|
+
// cannot become an unhandled rejection.
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.js","sourceRoot":"","sources":["../../src/backends/readiness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { SandboxExecOptions, SandboxExecResult } from '@namzu/sdk';
|
|
2
|
+
export interface RemoteReservation {
|
|
3
|
+
readonly ok: true;
|
|
4
|
+
readonly protocolVersion: 2;
|
|
5
|
+
readonly executionId: string;
|
|
6
|
+
readonly leaseExpiresAt: number;
|
|
7
|
+
}
|
|
8
|
+
export interface RemoteTerminalMetadata {
|
|
9
|
+
readonly exitCode: number;
|
|
10
|
+
readonly timedOut: boolean;
|
|
11
|
+
readonly durationMs: number;
|
|
12
|
+
readonly signal?: string;
|
|
13
|
+
readonly stdoutTruncated?: boolean;
|
|
14
|
+
readonly stderrTruncated?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface RemoteCancellationAcknowledgement {
|
|
17
|
+
readonly ok: true;
|
|
18
|
+
readonly state: 'cancelled' | 'completed' | 'failed';
|
|
19
|
+
readonly started: boolean;
|
|
20
|
+
readonly result?: RemoteTerminalMetadata;
|
|
21
|
+
readonly error?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class RemoteCommandError extends Error {
|
|
24
|
+
}
|
|
25
|
+
export declare class RemoteProtocolError extends Error {
|
|
26
|
+
}
|
|
27
|
+
export declare class RemoteCancellationUnsupportedError extends Error {
|
|
28
|
+
}
|
|
29
|
+
export interface SandboxRetirementObservation {
|
|
30
|
+
readonly accepted: boolean;
|
|
31
|
+
readonly error?: Error;
|
|
32
|
+
}
|
|
33
|
+
export declare class RemoteCancellationUnknownError extends Error {
|
|
34
|
+
retirement?: SandboxRetirementObservation;
|
|
35
|
+
constructor(message: string, options?: ErrorOptions);
|
|
36
|
+
}
|
|
37
|
+
export declare class RemoteResultIncompleteError extends Error {
|
|
38
|
+
readonly acknowledgement: RemoteCancellationAcknowledgement;
|
|
39
|
+
constructor(message: string, acknowledgement: RemoteCancellationAcknowledgement, options?: ErrorOptions);
|
|
40
|
+
}
|
|
41
|
+
export interface RemoteExecutionAdapter<Context = undefined> {
|
|
42
|
+
readonly label: string;
|
|
43
|
+
reserve(signal: AbortSignal): Promise<unknown>;
|
|
44
|
+
cancel(executionId: string, signal: AbortSignal): Promise<unknown>;
|
|
45
|
+
execute(executionId: string | undefined, command: string, argv: string[] | undefined, opts: SandboxExecOptions | undefined, transportSignal: AbortSignal, context: Context | undefined): Promise<SandboxExecResult>;
|
|
46
|
+
}
|
|
47
|
+
export interface RemoteExecutionControllerOptions {
|
|
48
|
+
readonly controlRequestTimeoutMs?: number;
|
|
49
|
+
readonly cancelConfirmTimeoutMs?: number;
|
|
50
|
+
readonly resultDrainTimeoutMs?: number;
|
|
51
|
+
readonly executionObservationGraceMs?: number;
|
|
52
|
+
readonly defaultExecutionTimeoutMs?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* One state machine for remote command ownership. A v2 peer reserves every
|
|
56
|
+
* command, even when the caller supplied no signal, so transport loss can be
|
|
57
|
+
* reconciled by identity. Legacy execution is entered only after an explicit
|
|
58
|
+
* unsupported response from the peer and is fenced as unknown on any failure.
|
|
59
|
+
*/
|
|
60
|
+
export declare class RemoteExecutionController<Context = undefined> {
|
|
61
|
+
private readonly adapter;
|
|
62
|
+
private capability;
|
|
63
|
+
private unsupportedError;
|
|
64
|
+
private readonly controlRequestTimeoutMs;
|
|
65
|
+
private readonly cancelConfirmTimeoutMs;
|
|
66
|
+
private readonly resultDrainTimeoutMs;
|
|
67
|
+
private readonly executionObservationGraceMs;
|
|
68
|
+
private readonly defaultExecutionTimeoutMs;
|
|
69
|
+
constructor(adapter: RemoteExecutionAdapter<Context>, options?: RemoteExecutionControllerOptions);
|
|
70
|
+
exec(command: string, argv: string[] | undefined, opts: SandboxExecOptions | undefined, context?: Context): Promise<SandboxExecResult>;
|
|
71
|
+
private executeLegacy;
|
|
72
|
+
private requestCancellation;
|
|
73
|
+
private drainConfirmedExecution;
|
|
74
|
+
private executeReserved;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=remote-execution-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-execution-controller.d.ts","sourceRoot":"","sources":["../../src/backends/remote-execution-controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAWvE,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;IACjB,QAAQ,CAAC,eAAe,EAAE,CAAC,CAAA;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAC/B;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;IAClC,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAClC;AAED,MAAM,WAAW,iCAAiC;IACjD,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;IACjB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAA;IACpD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,sBAAsB,CAAA;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;CAAG;AAEhD,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AAEjD,qBAAa,kCAAmC,SAAQ,KAAK;CAAG;AAEhE,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CACtB;AAED,qBAAa,8BAA+B,SAAQ,KAAK;IACxD,UAAU,CAAC,EAAE,4BAA4B,CAAA;gBAE7B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAInD;AAED,qBAAa,2BAA4B,SAAQ,KAAK;IAGpD,QAAQ,CAAC,eAAe,EAAE,iCAAiC;gBAD3D,OAAO,EAAE,MAAM,EACN,eAAe,EAAE,iCAAiC,EAC3D,OAAO,CAAC,EAAE,YAAY;CAKvB;AAED,MAAM,WAAW,sBAAsB,CAAC,OAAO,GAAG,SAAS;IAC1D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9C,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAClE,OAAO,CACN,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,EAC1B,IAAI,EAAE,kBAAkB,GAAG,SAAS,EACpC,eAAe,EAAE,WAAW,EAC5B,OAAO,EAAE,OAAO,GAAG,SAAS,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAAA;CAC7B;AAED,MAAM,WAAW,gCAAgC;IAChD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAA;IACzC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAA;IACxC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IACtC,QAAQ,CAAC,2BAA2B,CAAC,EAAE,MAAM,CAAA;IAC7C,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAA;CAC3C;AA6ID;;;;;GAKG;AACH,qBAAa,yBAAyB,CAAC,OAAO,GAAG,SAAS;IAUxD,OAAO,CAAC,QAAQ,CAAC,OAAO;IATzB,OAAO,CAAC,UAAU,CAAwB;IAC1C,OAAO,CAAC,gBAAgB,CAAgD;IACxE,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAQ;IAChD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAQ;IAC/C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAQ;IAC7C,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAQ;IACpD,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAQ;gBAGhC,OAAO,EAAE,sBAAsB,CAAC,OAAO,CAAC,EACzD,OAAO,GAAE,gCAAqC;IAWzC,IAAI,CACT,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,EAC1B,IAAI,EAAE,kBAAkB,GAAG,SAAS,EACpC,OAAO,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,iBAAiB,CAAC;YAwCf,aAAa;YAyBb,mBAAmB;YAwBnB,uBAAuB;YAkBvB,eAAe;CA8F7B"}
|