@ricsam/r5d-worker 0.0.111 → 0.0.112
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 +11 -6
- package/dist/cjs/command-launcher.cjs +345 -0
- package/dist/cjs/inline-output.cjs +62 -0
- package/dist/cjs/main.cjs +149 -102
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/supervisor.cjs +1 -1
- package/dist/mjs/command-launcher.mjs +316 -0
- package/dist/mjs/inline-output.mjs +37 -0
- package/dist/mjs/main.mjs +151 -103
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/supervisor.mjs +1 -1
- package/dist/types/command-launcher.d.ts +61 -0
- package/dist/types/inline-output.d.ts +3 -0
- package/dist/types/supervisor-launcher-cleanup-fixture.d.ts +1 -0
- package/package.json +1 -1
- package/dist/cjs/job-resources.cjs +0 -335
- package/dist/mjs/job-resources.mjs +0 -293
- package/dist/types/job-resources.d.ts +0 -61
package/README.md
CHANGED
|
@@ -71,16 +71,21 @@ worker exec macos bun test
|
|
|
71
71
|
|
|
72
72
|
Agent shell commands receive no stdin by default: prompts read immediate EOF instead of hanging. A command started with `interactive: true` is spawned with a writable stdin pipe, and the agent's `shell_write` tool delivers input through the `exec_stdin` protocol message. The worker acknowledges each write with an `operation_result` carrying the delivered byte count, closes stdin on an explicit `eof`, on cancellation, on timeout, and at process exit, and advertises this support through the `execStdinV1` capability in its hello message. Servers refuse interactive runs and stdin writes for workers without the capability, so this is a protocol-compatible release that uses the ordinary worker update flow. Delivered stdin is audited server-side in the run's `process-runs/<runId>/stdin.log`.
|
|
73
73
|
|
|
74
|
-
###
|
|
74
|
+
### Optional command launcher
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
By default the worker launches commands and terminals directly on every platform. It imposes no memory limit, concurrency limit, or OS setup requirement. The operating system may terminate a command or the worker itself under memory pressure; a killed process alone is not evidence of an OOM. Inline RPC output is limited to 8 MiB per stream to bound worker-side buffering; use streaming execution for larger output.
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Operators may set `R5D_WORKER_COMMAND_LAUNCHER` to a JSON argv array, such as `["node", "/opt/worker/launcher.mjs", "/etc/worker/policy.json"]`. The worker starts this trusted executable once after acquiring credential authority and keeps it alive across websocket reconnects. Configuration is read from the worker's environment, never from command message environments, and the controller inherits only basic non-secret host environment fields. Arguments are passed literally without shell splitting.
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
The launcher owns admission, OS isolation, budgets, and diagnostic evidence. Its JSONL protocol uses `{requestId, method, ...fields}` requests and `{requestId, result}` or `{requestId, error}` replies:
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
- `initialize` returns `{}` once ready; failure prevents command admission.
|
|
83
|
+
- `acquire` receives `{id, kind, workspaceEffect?, sessionId?}` and returns `{argvPrefix: string[]}`. The worker prepends this argv to the command or terminal shell without interpreting it.
|
|
84
|
+
- `cancel` receives `{id}` and irreversibly revokes queued or acquired work. `release` receives `{id}` and acknowledges only after cleanup. Both must be idempotent, including release before a delayed acquisition response.
|
|
85
|
+
- `diagnose` receives `{id}` and returns `{message?: string}` with launcher-specific evidence. The worker does not infer a memory failure from an exit signal.
|
|
83
86
|
|
|
84
|
-
|
|
87
|
+
Requests must be processed concurrently so cancellation and cleanup cannot wait behind queued admission. Controller RPCs are bounded (10 seconds, or 90 seconds for acquisition); external policy must reject excessive queue waits within that bound. Controller loss or protocol failure stops command admission, preserves uncertain outcomes for recovery, reaps ordinary children, and restarts the runtime through its supervisor. It never falls back to direct spawning. Shutdown closes controller stdin; the controller must reap its jobs before exiting.
|
|
88
|
+
|
|
89
|
+
Cancellation is checked again immediately before spawn. Transient disconnects preserve commands and terminals; expiry of the five-minute execution lease cancels agent work and closes terminals. File operations, communication recovery, and cancellation do not enter launcher admission. Internal worker maintenance subprocesses are also outside this hook. Deployment-specific limits and service protection are documented in [Worker deployment resources](../../docs/worker-deployment-resources.md).
|
|
85
90
|
|
|
86
91
|
When the connected `r5d-browser` requests a port forward, the worker opens each relayed connection only to `127.0.0.1` on the requested worker port. Browser-side and worker-side ports may differ. The worker never opens a public listener, and a disconnected worker leaves the browser's long-lived mapping unavailable until the same worker label reconnects.
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var command_launcher_exports = {};
|
|
20
|
+
__export(command_launcher_exports, {
|
|
21
|
+
CommandLaunchCancelledError: () => CommandLaunchCancelledError,
|
|
22
|
+
CommandLauncherUnavailableError: () => CommandLauncherUnavailableError,
|
|
23
|
+
WorkerCommandLauncher: () => WorkerCommandLauncher,
|
|
24
|
+
commandLauncherEnvironment: () => commandLauncherEnvironment,
|
|
25
|
+
createWorkerCommandLauncher: () => createWorkerCommandLauncher,
|
|
26
|
+
parseCommandLauncher: () => parseCommandLauncher
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(command_launcher_exports);
|
|
29
|
+
var import_node_child_process = require("node:child_process");
|
|
30
|
+
class CommandLauncherUnavailableError extends Error {
|
|
31
|
+
constructor(message, cause) {
|
|
32
|
+
super(message, { cause });
|
|
33
|
+
this.name = "CommandLauncherUnavailableError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class CommandLaunchCancelledError extends Error {
|
|
37
|
+
constructor() {
|
|
38
|
+
super("Command cancelled before it started");
|
|
39
|
+
this.name = "CommandLaunchCancelledError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function parseCommandLauncher(configuration) {
|
|
43
|
+
if (configuration === void 0) return void 0;
|
|
44
|
+
let argv;
|
|
45
|
+
try {
|
|
46
|
+
argv = JSON.parse(configuration);
|
|
47
|
+
} catch {
|
|
48
|
+
throw new Error("R5D_WORKER_COMMAND_LAUNCHER must be a JSON argv array");
|
|
49
|
+
}
|
|
50
|
+
if (!Array.isArray(argv) || argv.length === 0 || argv.some((part) => typeof part !== "string" || part.includes("\0")) || !argv[0])
|
|
51
|
+
throw new Error("R5D_WORKER_COMMAND_LAUNCHER must be a nonempty JSON argv array without NUL characters");
|
|
52
|
+
return argv;
|
|
53
|
+
}
|
|
54
|
+
function commandLauncherEnvironment(source = process.env) {
|
|
55
|
+
const result = {};
|
|
56
|
+
for (const [key, value] of Object.entries(source)) {
|
|
57
|
+
if (/^(PATH|HOME|USER|LOGNAME|TMP|TMPDIR|TEMP|SHELL|SYSTEMROOT|COMSPEC|PATHEXT|LANG|LC_[A-Z_]+)$/i.test(key)) result[key] = value;
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
class WorkerCommandLauncher {
|
|
62
|
+
constructor(options = {}) {
|
|
63
|
+
this.options = options;
|
|
64
|
+
this.mode = options.argv ? "external" : "direct";
|
|
65
|
+
if (!options.argv) {
|
|
66
|
+
this.exited = Promise.resolve();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
this.child = (0, import_node_child_process.spawn)(options.argv[0], options.argv.slice(1), {
|
|
70
|
+
env: options.env ?? commandLauncherEnvironment(),
|
|
71
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
72
|
+
});
|
|
73
|
+
this.exited = new Promise((resolve) => {
|
|
74
|
+
this.child.once("close", (code, signal) => {
|
|
75
|
+
if (!this.closing) this.fail(new Error(`Configured command launcher exited (${signal ?? code ?? "unknown"})`));
|
|
76
|
+
resolve();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
this.child.once("error", (error) => this.fail(error));
|
|
80
|
+
this.child.stdin.on("error", (error) => {
|
|
81
|
+
if (!this.closing) this.fail(error);
|
|
82
|
+
});
|
|
83
|
+
let buffered = "";
|
|
84
|
+
this.child.stdout.setEncoding("utf8");
|
|
85
|
+
this.child.stdout.on("data", (data) => {
|
|
86
|
+
buffered += data;
|
|
87
|
+
if (Buffer.byteLength(buffered) > 1024 * 1024) {
|
|
88
|
+
this.fail(new Error("Command launcher response exceeded the protocol limit"));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
while (buffered.includes("\n")) {
|
|
92
|
+
const newline = buffered.indexOf("\n");
|
|
93
|
+
const line = buffered.slice(0, newline);
|
|
94
|
+
buffered = buffered.slice(newline + 1);
|
|
95
|
+
try {
|
|
96
|
+
this.receive(JSON.parse(line));
|
|
97
|
+
} catch (error) {
|
|
98
|
+
this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
this.child.stderr.setEncoding("utf8");
|
|
104
|
+
this.child.stderr.on("data", (data) => options.onStderr?.(data));
|
|
105
|
+
}
|
|
106
|
+
options;
|
|
107
|
+
mode;
|
|
108
|
+
child;
|
|
109
|
+
pending = /* @__PURE__ */ new Map();
|
|
110
|
+
launches = /* @__PURE__ */ new Map();
|
|
111
|
+
exited;
|
|
112
|
+
failure;
|
|
113
|
+
closing = false;
|
|
114
|
+
initialized = false;
|
|
115
|
+
closePromise;
|
|
116
|
+
sequence = 0;
|
|
117
|
+
async initialize() {
|
|
118
|
+
this.assertHealthy();
|
|
119
|
+
if (this.initialized) return;
|
|
120
|
+
if (this.child) {
|
|
121
|
+
try {
|
|
122
|
+
await this.rpc("initialize");
|
|
123
|
+
} catch (error) {
|
|
124
|
+
this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
125
|
+
await this.close();
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
this.initialized = true;
|
|
130
|
+
}
|
|
131
|
+
get activeIds() {
|
|
132
|
+
return [...this.launches.keys()];
|
|
133
|
+
}
|
|
134
|
+
async acquire(request) {
|
|
135
|
+
this.assertHealthy();
|
|
136
|
+
if (!this.initialized) throw new Error("Command launcher is not initialized");
|
|
137
|
+
request.assertAdmission();
|
|
138
|
+
if (this.launches.has(request.id)) throw new Error(`Duplicate command launch identity: ${request.id}`);
|
|
139
|
+
let rejectAdmission;
|
|
140
|
+
const cancellation = new Promise((_resolve, reject) => {
|
|
141
|
+
rejectAdmission = reject;
|
|
142
|
+
});
|
|
143
|
+
void cancellation.catch(() => {
|
|
144
|
+
});
|
|
145
|
+
const record = { request, revoked: false, released: false, rejectAdmission };
|
|
146
|
+
this.launches.set(request.id, record);
|
|
147
|
+
let prefix = [];
|
|
148
|
+
let admissionTimer;
|
|
149
|
+
try {
|
|
150
|
+
if (this.child) {
|
|
151
|
+
const { assertAdmission: _assertAdmission, ...fields } = request;
|
|
152
|
+
const acquisition = this.rpc("acquire", fields, this.options.acquisitionTimeoutMs ?? 9e4).then(async (result) => {
|
|
153
|
+
if (record.revoked) {
|
|
154
|
+
await this.releaseRecord(record);
|
|
155
|
+
throw new CommandLaunchCancelledError();
|
|
156
|
+
}
|
|
157
|
+
if (!Array.isArray(result.argvPrefix) || result.argvPrefix.some((part) => typeof part !== "string" || part.includes("\0")))
|
|
158
|
+
throw this.fail(new Error("Command launcher acquire returned an invalid argvPrefix"));
|
|
159
|
+
return result.argvPrefix;
|
|
160
|
+
});
|
|
161
|
+
admissionTimer = setInterval(() => {
|
|
162
|
+
try {
|
|
163
|
+
request.assertAdmission();
|
|
164
|
+
} catch (error) {
|
|
165
|
+
record.revoked = true;
|
|
166
|
+
record.rejectAdmission(error);
|
|
167
|
+
void this.cancel(request.id).catch(() => {
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}, 100);
|
|
171
|
+
prefix = await Promise.race([acquisition, cancellation]);
|
|
172
|
+
}
|
|
173
|
+
request.assertAdmission();
|
|
174
|
+
this.assertHealthy();
|
|
175
|
+
if (record.revoked) throw new CommandLaunchCancelledError();
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (!this.failure && !this.closing) await this.releaseRecord(record);
|
|
178
|
+
throw error;
|
|
179
|
+
} finally {
|
|
180
|
+
if (admissionTimer) clearInterval(admissionTimer);
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
wrap: (argv) => {
|
|
184
|
+
this.assertHealthy();
|
|
185
|
+
request.assertAdmission();
|
|
186
|
+
if (record.revoked) throw new CommandLaunchCancelledError();
|
|
187
|
+
if (record.released) throw new Error("Command launch lease is already released");
|
|
188
|
+
if (!argv.length) throw new Error("Command argv must not be empty");
|
|
189
|
+
return [...prefix, ...argv];
|
|
190
|
+
},
|
|
191
|
+
cancel: () => this.cancel(request.id),
|
|
192
|
+
diagnose: async () => {
|
|
193
|
+
this.assertHealthy();
|
|
194
|
+
if (!this.child) return void 0;
|
|
195
|
+
try {
|
|
196
|
+
const result = await this.rpc("diagnose", { id: request.id });
|
|
197
|
+
if (result.message !== void 0 && typeof result.message !== "string")
|
|
198
|
+
throw new Error("Command launcher returned an invalid diagnostic");
|
|
199
|
+
return result.message;
|
|
200
|
+
} catch (error) {
|
|
201
|
+
throw this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
release: () => this.releaseRecord(record)
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
async cancel(id) {
|
|
208
|
+
const record = this.launches.get(id);
|
|
209
|
+
if (!record || record.released) return;
|
|
210
|
+
record.revoked = true;
|
|
211
|
+
record.rejectAdmission(new CommandLaunchCancelledError());
|
|
212
|
+
if (this.child) {
|
|
213
|
+
try {
|
|
214
|
+
await this.rpc("cancel", { id });
|
|
215
|
+
} catch (error) {
|
|
216
|
+
throw this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async cancelSession(sessionId) {
|
|
221
|
+
await this.cancelMatching((_id, ownerSessionId) => ownerSessionId === sessionId);
|
|
222
|
+
}
|
|
223
|
+
async cancelMatching(matches) {
|
|
224
|
+
const results = await Promise.allSettled(
|
|
225
|
+
[...this.launches].filter(([id, record]) => matches(id, record.request.sessionId)).map(([id]) => this.cancel(id))
|
|
226
|
+
);
|
|
227
|
+
const errors = results.flatMap((result) => result.status === "rejected" ? [result.reason] : []);
|
|
228
|
+
if (errors.length) throw new AggregateError(errors, "Command launcher cancellation was incomplete");
|
|
229
|
+
}
|
|
230
|
+
close() {
|
|
231
|
+
return this.closePromise ??= (async () => {
|
|
232
|
+
this.closing = true;
|
|
233
|
+
for (const record of this.launches.values()) {
|
|
234
|
+
record.revoked = true;
|
|
235
|
+
record.rejectAdmission(new CommandLaunchCancelledError());
|
|
236
|
+
}
|
|
237
|
+
this.rejectPending(new CommandLauncherUnavailableError("Command launcher is shutting down"));
|
|
238
|
+
this.child?.stdin.end();
|
|
239
|
+
const exited = await this.waitForExit(6e3);
|
|
240
|
+
if (!exited) {
|
|
241
|
+
this.child?.kill("SIGKILL");
|
|
242
|
+
await this.waitForExit(1e3);
|
|
243
|
+
}
|
|
244
|
+
})();
|
|
245
|
+
}
|
|
246
|
+
async waitForExit(timeoutMs) {
|
|
247
|
+
let timer;
|
|
248
|
+
try {
|
|
249
|
+
return await Promise.race([
|
|
250
|
+
this.exited.then(() => true),
|
|
251
|
+
new Promise((resolve) => {
|
|
252
|
+
timer = setTimeout(() => resolve(false), timeoutMs);
|
|
253
|
+
})
|
|
254
|
+
]);
|
|
255
|
+
} finally {
|
|
256
|
+
if (timer) clearTimeout(timer);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
releaseRecord(record) {
|
|
260
|
+
return record.release ??= (async () => {
|
|
261
|
+
record.revoked = true;
|
|
262
|
+
if (this.child) {
|
|
263
|
+
try {
|
|
264
|
+
await this.rpc("release", { id: record.request.id });
|
|
265
|
+
} catch (error) {
|
|
266
|
+
throw this.fail(error instanceof Error ? error : new Error(String(error)));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
record.released = true;
|
|
270
|
+
this.launches.delete(record.request.id);
|
|
271
|
+
})();
|
|
272
|
+
}
|
|
273
|
+
assertHealthy() {
|
|
274
|
+
if (this.failure) throw this.failure;
|
|
275
|
+
if (this.closing) throw new CommandLauncherUnavailableError("Command launcher is shutting down");
|
|
276
|
+
}
|
|
277
|
+
rpc(method, fields = {}, timeoutMs = this.options.requestTimeoutMs ?? 1e4) {
|
|
278
|
+
try {
|
|
279
|
+
this.assertHealthy();
|
|
280
|
+
} catch (error) {
|
|
281
|
+
return Promise.reject(error);
|
|
282
|
+
}
|
|
283
|
+
const requestId = String(++this.sequence);
|
|
284
|
+
return new Promise((resolve, reject) => {
|
|
285
|
+
const timer = setTimeout(() => this.fail(new Error(`Command launcher ${method} request timed out`)), timeoutMs);
|
|
286
|
+
this.pending.set(requestId, { resolve, reject, timer });
|
|
287
|
+
this.child.stdin.write(`${JSON.stringify({ requestId, method, ...fields })}
|
|
288
|
+
`, (error) => {
|
|
289
|
+
if (error) this.fail(error);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
receive(value) {
|
|
294
|
+
if (!value || typeof value !== "object") throw new Error("Invalid command launcher response");
|
|
295
|
+
const response = value;
|
|
296
|
+
if (typeof response.requestId !== "string") throw new Error("Command launcher response is missing requestId");
|
|
297
|
+
const request = this.pending.get(response.requestId);
|
|
298
|
+
if (!request) throw new Error("Command launcher returned an unknown requestId");
|
|
299
|
+
this.pending.delete(response.requestId);
|
|
300
|
+
clearTimeout(request.timer);
|
|
301
|
+
if (typeof response.error === "string") {
|
|
302
|
+
request.reject(new Error(response.error));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (!response.result || typeof response.result !== "object" || Array.isArray(response.result)) {
|
|
306
|
+
const error = new Error("Command launcher response is missing result");
|
|
307
|
+
request.reject(error);
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
request.resolve(response.result);
|
|
311
|
+
}
|
|
312
|
+
rejectPending(error) {
|
|
313
|
+
for (const request of this.pending.values()) {
|
|
314
|
+
clearTimeout(request.timer);
|
|
315
|
+
request.reject(error);
|
|
316
|
+
}
|
|
317
|
+
this.pending.clear();
|
|
318
|
+
}
|
|
319
|
+
fail(error) {
|
|
320
|
+
if (this.failure || this.closing) return this.failure ?? error;
|
|
321
|
+
this.failure = new CommandLauncherUnavailableError(`Command launcher unavailable: ${error.message}`, error);
|
|
322
|
+
for (const record of this.launches.values()) {
|
|
323
|
+
record.revoked = true;
|
|
324
|
+
record.rejectAdmission(this.failure);
|
|
325
|
+
}
|
|
326
|
+
this.rejectPending(this.failure);
|
|
327
|
+
this.child?.kill("SIGTERM");
|
|
328
|
+
this.options.onFailure?.(this.failure, this.activeIds);
|
|
329
|
+
return this.failure;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async function createWorkerCommandLauncher(options = {}) {
|
|
333
|
+
const launcher = new WorkerCommandLauncher({ ...options, argv: parseCommandLauncher(options.configuration) });
|
|
334
|
+
await launcher.initialize();
|
|
335
|
+
return launcher;
|
|
336
|
+
}
|
|
337
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
338
|
+
0 && (module.exports = {
|
|
339
|
+
CommandLaunchCancelledError,
|
|
340
|
+
CommandLauncherUnavailableError,
|
|
341
|
+
WorkerCommandLauncher,
|
|
342
|
+
commandLauncherEnvironment,
|
|
343
|
+
createWorkerCommandLauncher,
|
|
344
|
+
parseCommandLauncher
|
|
345
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var inline_output_exports = {};
|
|
20
|
+
__export(inline_output_exports, {
|
|
21
|
+
WORKER_INLINE_OUTPUT_MAX_BYTES: () => WORKER_INLINE_OUTPUT_MAX_BYTES,
|
|
22
|
+
collectWorkerInlineOutput: () => collectWorkerInlineOutput
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(inline_output_exports);
|
|
25
|
+
const WORKER_INLINE_OUTPUT_MAX_BYTES = 8 * 1024 * 1024;
|
|
26
|
+
async function collectWorkerInlineOutput(stream, maxBytes = WORKER_INLINE_OUTPUT_MAX_BYTES, signal) {
|
|
27
|
+
if (!stream) return "";
|
|
28
|
+
signal?.throwIfAborted();
|
|
29
|
+
const reader = stream.getReader();
|
|
30
|
+
const abort = () => {
|
|
31
|
+
void reader.cancel().catch(() => {
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
35
|
+
const decoder = new TextDecoder();
|
|
36
|
+
const parts = [];
|
|
37
|
+
let bytes = 0;
|
|
38
|
+
try {
|
|
39
|
+
while (true) {
|
|
40
|
+
const next = await reader.read();
|
|
41
|
+
signal?.throwIfAborted();
|
|
42
|
+
if (next.done) break;
|
|
43
|
+
bytes += next.value.byteLength;
|
|
44
|
+
if (bytes > maxBytes) {
|
|
45
|
+
void reader.cancel().catch(() => {
|
|
46
|
+
});
|
|
47
|
+
throw new Error(`Inline command output exceeded ${maxBytes} bytes. Use streaming execution for large output.`);
|
|
48
|
+
}
|
|
49
|
+
parts.push(decoder.decode(next.value, { stream: true }));
|
|
50
|
+
}
|
|
51
|
+
parts.push(decoder.decode());
|
|
52
|
+
return parts.join("");
|
|
53
|
+
} finally {
|
|
54
|
+
signal?.removeEventListener("abort", abort);
|
|
55
|
+
reader.releaseLock();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
59
|
+
0 && (module.exports = {
|
|
60
|
+
WORKER_INLINE_OUTPUT_MAX_BYTES,
|
|
61
|
+
collectWorkerInlineOutput
|
|
62
|
+
});
|