@rivet-dev/agentos-runtime-core 0.0.0-codex-actor-plugin-contract-coverage.115ca8a → 0.0.0-fix-main-wasm-ownership.150c49a
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/commands/[ +0 -0
- package/commands/_stubs +0 -0
- package/commands/acl_tools +0 -0
- package/commands/arch +0 -0
- package/commands/attr +0 -0
- package/commands/awk +0 -0
- package/commands/b2sum +0 -0
- package/commands/base32 +0 -0
- package/commands/base64 +0 -0
- package/commands/basename +0 -0
- package/commands/basenc +0 -0
- package/commands/bash +0 -0
- package/commands/bc +0 -0
- package/commands/cat +0 -0
- package/commands/chacl +0 -0
- package/commands/chcon +0 -0
- package/commands/chgrp +0 -0
- package/commands/chmod +0 -0
- package/commands/chown +0 -0
- package/commands/chroot +0 -0
- package/commands/cksum +0 -0
- package/commands/cmp +0 -0
- package/commands/column +0 -0
- package/commands/comm +0 -0
- package/commands/cp +0 -0
- package/commands/curl +0 -0
- package/commands/cut +0 -0
- package/commands/date +0 -0
- package/commands/dd +0 -0
- package/commands/df +0 -0
- package/commands/diff +0 -0
- package/commands/dir +0 -0
- package/commands/dircolors +0 -0
- package/commands/dirname +0 -0
- package/commands/du +0 -0
- package/commands/echo +0 -0
- package/commands/egrep +0 -0
- package/commands/env +0 -0
- package/commands/envsubst +0 -0
- package/commands/expand +0 -0
- package/commands/expr +0 -0
- package/commands/factor +0 -0
- package/commands/false +0 -0
- package/commands/fd +0 -0
- package/commands/fgrep +0 -0
- package/commands/file +0 -0
- package/commands/find +0 -0
- package/commands/findmnt +0 -0
- package/commands/fmt +0 -0
- package/commands/fold +0 -0
- package/commands/getent +0 -0
- package/commands/getfacl +0 -0
- package/commands/getfattr +0 -0
- package/commands/git +0 -0
- package/commands/git-receive-pack +0 -0
- package/commands/git-remote-http +0 -0
- package/commands/git-remote-https +0 -0
- package/commands/git-upload-archive +0 -0
- package/commands/git-upload-pack +0 -0
- package/commands/grep +0 -0
- package/commands/groups +0 -0
- package/commands/gunzip +0 -0
- package/commands/gzip +0 -0
- package/commands/head +0 -0
- package/commands/hostid +0 -0
- package/commands/hostname +0 -0
- package/commands/http-test +0 -0
- package/commands/id +0 -0
- package/commands/install +0 -0
- package/commands/join +0 -0
- package/commands/jq +0 -0
- package/commands/kill +0 -0
- package/commands/link +0 -0
- package/commands/ln +0 -0
- package/commands/logname +0 -0
- package/commands/ls +0 -0
- package/commands/md5sum +0 -0
- package/commands/mkdir +0 -0
- package/commands/mkfifo +0 -0
- package/commands/mknod +0 -0
- package/commands/mktemp +0 -0
- package/commands/more +0 -0
- package/commands/mount +0 -0
- package/commands/mv +0 -0
- package/commands/nice +0 -0
- package/commands/nl +0 -0
- package/commands/nohup +0 -0
- package/commands/nproc +0 -0
- package/commands/numfmt +0 -0
- package/commands/od +0 -0
- package/commands/paste +0 -0
- package/commands/pathchk +0 -0
- package/commands/pinky +0 -0
- package/commands/printenv +0 -0
- package/commands/printf +0 -0
- package/commands/ptx +0 -0
- package/commands/pwd +0 -0
- package/commands/readlink +0 -0
- package/commands/realpath +0 -0
- package/commands/rev +0 -0
- package/commands/rg +0 -0
- package/commands/rm +0 -0
- package/commands/rmdir +0 -0
- package/commands/runas +0 -0
- package/commands/runcon +0 -0
- package/commands/sed +0 -0
- package/commands/seq +0 -0
- package/commands/setfacl +0 -0
- package/commands/setfattr +0 -0
- package/commands/sh +0 -0
- package/commands/sha1sum +0 -0
- package/commands/sha224sum +0 -0
- package/commands/sha256sum +0 -0
- package/commands/sha384sum +0 -0
- package/commands/sha512sum +0 -0
- package/commands/shred +0 -0
- package/commands/shuf +0 -0
- package/commands/sleep +0 -0
- package/commands/sort +0 -0
- package/commands/spawn-test-host +0 -0
- package/commands/split +0 -0
- package/commands/sqlite3 +0 -0
- package/commands/ssh +0 -0
- package/commands/ssh-keysign +0 -0
- package/commands/ssh-sk-helper +0 -0
- package/commands/stat +0 -0
- package/commands/stdbuf +0 -0
- package/commands/strings +0 -0
- package/commands/stty +0 -0
- package/commands/su +0 -0
- package/commands/sum +0 -0
- package/commands/sync +0 -0
- package/commands/tac +0 -0
- package/commands/tail +0 -0
- package/commands/tar +0 -0
- package/commands/tee +0 -0
- package/commands/test +0 -0
- package/commands/timeout +0 -0
- package/commands/touch +0 -0
- package/commands/tr +0 -0
- package/commands/tree +0 -0
- package/commands/true +0 -0
- package/commands/truncate +0 -0
- package/commands/tsort +0 -0
- package/commands/tty +0 -0
- package/commands/uname +0 -0
- package/commands/unexpand +0 -0
- package/commands/uniq +0 -0
- package/commands/unlink +0 -0
- package/commands/unzip +0 -0
- package/commands/uptime +0 -0
- package/commands/users +0 -0
- package/commands/vdir +0 -0
- package/commands/wc +0 -0
- package/commands/wget +0 -0
- package/commands/which +0 -0
- package/commands/who +0 -0
- package/commands/whoami +0 -0
- package/commands/xargs +0 -0
- package/commands/xattr_tools +0 -0
- package/commands/xfs_io +0 -0
- package/commands/xu +0 -0
- package/commands/yes +0 -0
- package/commands/yq +0 -0
- package/commands/zcat +0 -0
- package/commands/zip +0 -0
- package/dist/correlation.d.ts +1 -4
- package/dist/correlation.js +7 -10
- package/dist/descriptors.d.ts +2 -12
- package/dist/descriptors.js +10 -6
- package/dist/frame-rpc.d.ts +9 -4
- package/dist/frame-rpc.js +19 -2
- package/dist/frame-stream.d.ts +33 -0
- package/dist/frame-stream.js +113 -0
- package/dist/generated/AcpLimitsConfig.d.ts +16 -0
- package/dist/generated/BindingLimitsConfig.d.ts +10 -0
- package/dist/generated/CreateVmConfig.d.ts +4 -0
- package/dist/generated/Http2LimitsConfig.d.ts +12 -0
- package/dist/generated/Http2LimitsConfig.js +2 -0
- package/dist/generated/JsRuntimeLimitsConfig.d.ts +1 -0
- package/dist/generated/MountPluginDescriptor.d.ts +1 -1
- package/dist/generated/ProcessLimitsConfig.d.ts +7 -0
- package/dist/generated/ProcessLimitsConfig.js +2 -0
- package/dist/generated/ReactorLimitsConfig.d.ts +23 -0
- package/dist/generated/ReactorLimitsConfig.js +2 -0
- package/dist/generated/SqliteLimitsConfig.d.ts +3 -0
- package/dist/generated/SqliteLimitsConfig.js +2 -0
- package/dist/generated/TlsLimitsConfig.d.ts +3 -0
- package/dist/generated/TlsLimitsConfig.js +2 -0
- package/dist/generated/UdpLimitsConfig.d.ts +4 -0
- package/dist/generated/UdpLimitsConfig.js +2 -0
- package/dist/generated/VmGroupConfig.d.ts +5 -0
- package/dist/generated/VmGroupConfig.js +2 -0
- package/dist/generated/VmLimitsConfig.d.ts +14 -2
- package/dist/generated/VmSqliteDescriptor.d.ts +11 -0
- package/dist/generated/VmSqliteDescriptor.js +2 -0
- package/dist/generated/VmUserAccountConfig.d.ts +9 -0
- package/dist/generated/VmUserAccountConfig.js +2 -0
- package/dist/generated/VmUserConfig.d.ts +19 -0
- package/dist/generated/VmUserConfig.js +1 -0
- package/dist/generated/WasmLimitsConfig.d.ts +1 -0
- package/dist/generated-protocol.d.ts +60 -2
- package/dist/generated-protocol.js +166 -31
- package/dist/kernel-proxy.d.ts +5 -1
- package/dist/kernel-proxy.js +77 -39
- package/dist/native-client.d.ts +9 -5
- package/dist/native-client.js +41 -9
- package/dist/node-runtime-options-schema.d.ts +1 -1
- package/dist/node-runtime-options-schema.js +43 -1
- package/dist/node-runtime.d.ts +12 -3
- package/dist/node-runtime.js +8 -7
- package/dist/process.d.ts +3 -1
- package/dist/process.js +27 -8
- package/dist/protocol-client.d.ts +26 -4
- package/dist/protocol-client.js +164 -12
- package/dist/protocol-frames.d.ts +14 -3
- package/dist/protocol-frames.js +26 -1
- package/dist/protocol-schema.d.ts +1 -1
- package/dist/protocol-schema.js +1 -1
- package/dist/request-payloads.d.ts +4 -1
- package/dist/request-payloads.js +7 -2
- package/dist/response-payloads.d.ts +23 -2
- package/dist/response-payloads.js +34 -2
- package/dist/sidecar-errors.d.ts +37 -0
- package/dist/sidecar-errors.js +27 -0
- package/dist/sidecar-process.d.ts +20 -6
- package/dist/sidecar-process.js +79 -15
- package/dist/state.d.ts +6 -6
- package/dist/state.js +10 -11
- package/dist/test-runtime.d.ts +21 -6
- package/dist/test-runtime.js +129 -65
- package/dist/vm-config.d.ts +5 -1
- package/package.json +5 -3
- package/commands/codex +0 -0
- package/commands/codex-exec +0 -0
- package/commands/http_get +0 -0
- package/dist/generated/ToolLimitsConfig.d.ts +0 -10
- /package/dist/generated/{ToolLimitsConfig.js → BindingLimitsConfig.js} +0 -0
package/dist/kernel-proxy.js
CHANGED
|
@@ -4,6 +4,8 @@ export function serializeMountConfigForSidecar(mount) {
|
|
|
4
4
|
if ("driver" in mount) {
|
|
5
5
|
return {
|
|
6
6
|
guestPath: mount.path,
|
|
7
|
+
guestSource: mount.guestSource ?? "js_bridge",
|
|
8
|
+
guestFstype: mount.guestFstype ?? "js_bridge",
|
|
7
9
|
readOnly: mount.readOnly ?? false,
|
|
8
10
|
plugin: {
|
|
9
11
|
id: "js_bridge",
|
|
@@ -13,6 +15,8 @@ export function serializeMountConfigForSidecar(mount) {
|
|
|
13
15
|
}
|
|
14
16
|
return {
|
|
15
17
|
guestPath: mount.path,
|
|
18
|
+
guestSource: mount.guestSource ?? mount.plugin.id,
|
|
19
|
+
guestFstype: mount.guestFstype ?? mount.plugin.id,
|
|
16
20
|
readOnly: mount.readOnly ?? false,
|
|
17
21
|
plugin: {
|
|
18
22
|
id: mount.plugin.id,
|
|
@@ -70,6 +74,15 @@ const PREFERRED_SIGNAL_NAMES = [
|
|
|
70
74
|
"SIGEMT",
|
|
71
75
|
"SIGINFO",
|
|
72
76
|
];
|
|
77
|
+
const NON_TERMINATING_SIGNALS = new Set([
|
|
78
|
+
"0",
|
|
79
|
+
"SIGCHLD",
|
|
80
|
+
"SIGCONT",
|
|
81
|
+
"SIGSTOP",
|
|
82
|
+
"SIGURG",
|
|
83
|
+
"SIGWINCH",
|
|
84
|
+
]);
|
|
85
|
+
const LOCALLY_TERMINATING_SIGNALS = new Set(["SIGKILL", "SIGTERM"]);
|
|
73
86
|
const NON_CANONICAL_SIGNAL_NAMES = new Set([
|
|
74
87
|
"SIGCLD",
|
|
75
88
|
"SIGIOT",
|
|
@@ -262,7 +275,10 @@ export class NativeSidecarKernelProxy {
|
|
|
262
275
|
this.eventPumpAbortController.abort();
|
|
263
276
|
const liveProcesses = [...this.trackedProcesses.values()].filter((entry) => entry.exitCode === null);
|
|
264
277
|
await Promise.allSettled(liveProcesses.map((entry) => this.signalProcess(entry, 15)));
|
|
265
|
-
await
|
|
278
|
+
await Promise.race([
|
|
279
|
+
this.client.disposeVm(this.session, this.vm),
|
|
280
|
+
new Promise((resolve) => setTimeout(resolve, 1000)),
|
|
281
|
+
]).catch(() => { });
|
|
266
282
|
for (const entry of liveProcesses) {
|
|
267
283
|
if (entry.exitCode === null) {
|
|
268
284
|
// The sidecar dispose path already performs TERM/KILL escalation for any
|
|
@@ -478,13 +494,17 @@ export class NativeSidecarKernelProxy {
|
|
|
478
494
|
}
|
|
479
495
|
entry.pendingKillSignal = signal;
|
|
480
496
|
void entry.startPromise.then(async () => {
|
|
481
|
-
if (entry.
|
|
497
|
+
if (entry.pendingKillSignal === null) {
|
|
482
498
|
return;
|
|
483
499
|
}
|
|
484
500
|
const pendingSignal = entry.pendingKillSignal;
|
|
485
501
|
entry.pendingKillSignal = null;
|
|
486
502
|
await this.signalProcess(entry, pendingSignal);
|
|
487
503
|
});
|
|
504
|
+
if ((signal === 9 || signal === 15) &&
|
|
505
|
+
entry.exitCode === null) {
|
|
506
|
+
this.finishProcess(entry, 128 + signal);
|
|
507
|
+
}
|
|
488
508
|
},
|
|
489
509
|
wait: async () => {
|
|
490
510
|
const exitCode = await this.waitForTrackedProcess(entry);
|
|
@@ -506,7 +526,7 @@ export class NativeSidecarKernelProxy {
|
|
|
506
526
|
return proc;
|
|
507
527
|
}
|
|
508
528
|
openShell(options) {
|
|
509
|
-
const
|
|
529
|
+
const terminalHandlers = new Set();
|
|
510
530
|
const stderrHandlers = new Set();
|
|
511
531
|
const command = options?.command ?? "sh";
|
|
512
532
|
const args = options?.args ??
|
|
@@ -565,7 +585,7 @@ export class NativeSidecarKernelProxy {
|
|
|
565
585
|
const normalized = normalizeSyntheticTerminalText(text);
|
|
566
586
|
updateSyntheticCursor(normalized);
|
|
567
587
|
const chunk = textEncoder.encode(normalized);
|
|
568
|
-
for (const handler of
|
|
588
|
+
for (const handler of terminalHandlers) {
|
|
569
589
|
handler(chunk);
|
|
570
590
|
}
|
|
571
591
|
};
|
|
@@ -576,7 +596,7 @@ export class NativeSidecarKernelProxy {
|
|
|
576
596
|
const normalized = normalizeSyntheticTerminalText(text);
|
|
577
597
|
updateSyntheticCursor(normalized);
|
|
578
598
|
const chunk = textEncoder.encode(normalized);
|
|
579
|
-
for (const handler of
|
|
599
|
+
for (const handler of terminalHandlers) {
|
|
580
600
|
handler(chunk);
|
|
581
601
|
}
|
|
582
602
|
};
|
|
@@ -621,7 +641,7 @@ export class NativeSidecarKernelProxy {
|
|
|
621
641
|
commandInFlight = false;
|
|
622
642
|
const promptPrefix = syntheticCursorAtLineStart ? "" : "\r\n";
|
|
623
643
|
const promptChunk = textEncoder.encode(`${promptPrefix}${promptText}`);
|
|
624
|
-
for (const handler of
|
|
644
|
+
for (const handler of terminalHandlers) {
|
|
625
645
|
handler(promptChunk);
|
|
626
646
|
}
|
|
627
647
|
syntheticCursorAtLineStart = false;
|
|
@@ -658,7 +678,7 @@ export class NativeSidecarKernelProxy {
|
|
|
658
678
|
}
|
|
659
679
|
};
|
|
660
680
|
let onData = null;
|
|
661
|
-
|
|
681
|
+
terminalHandlers.add((data) => onData?.(data));
|
|
662
682
|
if (options?.onStderr) {
|
|
663
683
|
stderrHandlers.add(options.onStderr);
|
|
664
684
|
}
|
|
@@ -817,7 +837,7 @@ export class NativeSidecarKernelProxy {
|
|
|
817
837
|
cwd: options?.cwd,
|
|
818
838
|
streamStdin: true,
|
|
819
839
|
onStdout: (chunk) => {
|
|
820
|
-
for (const handler of
|
|
840
|
+
for (const handler of terminalHandlers) {
|
|
821
841
|
handler(chunk);
|
|
822
842
|
}
|
|
823
843
|
if (commandInFlight) {
|
|
@@ -825,6 +845,11 @@ export class NativeSidecarKernelProxy {
|
|
|
825
845
|
}
|
|
826
846
|
},
|
|
827
847
|
onStderr: (chunk) => {
|
|
848
|
+
// `onData` is the ordered PTY rendering stream. `onStderr` remains an
|
|
849
|
+
// optional channel-specific diagnostic tap and must not also be rendered.
|
|
850
|
+
for (const handler of terminalHandlers) {
|
|
851
|
+
handler(chunk);
|
|
852
|
+
}
|
|
828
853
|
for (const handler of stderrHandlers) {
|
|
829
854
|
handler(chunk);
|
|
830
855
|
}
|
|
@@ -878,13 +903,7 @@ export class NativeSidecarKernelProxy {
|
|
|
878
903
|
const stdin = process.stdin;
|
|
879
904
|
const stdout = process.stdout;
|
|
880
905
|
const { onData, ...shellOptions } = options ?? {};
|
|
881
|
-
const shell = this.openShell(
|
|
882
|
-
...shellOptions,
|
|
883
|
-
onStderr: shellOptions.onStderr ??
|
|
884
|
-
((data) => {
|
|
885
|
-
process.stderr.write(data);
|
|
886
|
-
}),
|
|
887
|
-
});
|
|
906
|
+
const shell = this.openShell(shellOptions);
|
|
888
907
|
const outputHandler = onData ??
|
|
889
908
|
((data) => {
|
|
890
909
|
stdout.write(data);
|
|
@@ -985,6 +1004,10 @@ export class NativeSidecarKernelProxy {
|
|
|
985
1004
|
}
|
|
986
1005
|
return this.client.rename(this.session, this.vm, oldPath, newPath);
|
|
987
1006
|
}
|
|
1007
|
+
// Test-runtime only: runtime mounts registered here stay host-side local
|
|
1008
|
+
// compat mounts and are not delivered to the sidecar. The production proxy
|
|
1009
|
+
// in @rivet-dev/agentos-core reconfigures sidecar mounts on every
|
|
1010
|
+
// mountFs/unmountFs.
|
|
988
1011
|
mountFs(path, driver, options) {
|
|
989
1012
|
this.localMounts.unshift({
|
|
990
1013
|
path: posixPath.normalize(path),
|
|
@@ -1124,15 +1147,6 @@ export class NativeSidecarKernelProxy {
|
|
|
1124
1147
|
if (entry.onStdout.size === 0 && entry.onStderr.size === 0) {
|
|
1125
1148
|
return;
|
|
1126
1149
|
}
|
|
1127
|
-
if (entry.exitViaEvent) {
|
|
1128
|
-
// The sidecar drains process output before it emits `process_exited`,
|
|
1129
|
-
// the stdio frame stream is FIFO, and this pump dispatches events in
|
|
1130
|
-
// order. Once the exit event reaches this entry, no trailing output can
|
|
1131
|
-
// follow it; one zero-delay turn is cheap insurance for same-tick
|
|
1132
|
-
// listener scheduling.
|
|
1133
|
-
await drainTrailingProcessOutputTurn(0);
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
1150
|
let observedGeneration = entry.outputGeneration;
|
|
1137
1151
|
let quietTurns = 0;
|
|
1138
1152
|
let delayMs = 0;
|
|
@@ -1303,9 +1317,27 @@ export class NativeSidecarKernelProxy {
|
|
|
1303
1317
|
return entry.waitWithFallbackPromise;
|
|
1304
1318
|
}
|
|
1305
1319
|
async signalProcess(entry, signal) {
|
|
1306
|
-
|
|
1320
|
+
const sidecarSignal = toSidecarSignalName(signal);
|
|
1321
|
+
let timedOut = false;
|
|
1322
|
+
const killPromise = this.client.killProcess(this.session, this.vm, entry.processId, sidecarSignal);
|
|
1307
1323
|
try {
|
|
1308
|
-
await
|
|
1324
|
+
await Promise.race([
|
|
1325
|
+
killPromise,
|
|
1326
|
+
new Promise((resolve) => setTimeout(() => {
|
|
1327
|
+
timedOut = true;
|
|
1328
|
+
resolve();
|
|
1329
|
+
}, 1000)),
|
|
1330
|
+
]);
|
|
1331
|
+
if (timedOut) {
|
|
1332
|
+
void killPromise.catch(() => { });
|
|
1333
|
+
}
|
|
1334
|
+
const hasGuestHandler = this.signalStates.get(entry.pid)?.handlers.has(signal) ?? false;
|
|
1335
|
+
if (entry.exitCode === null &&
|
|
1336
|
+
!hasGuestHandler &&
|
|
1337
|
+
LOCALLY_TERMINATING_SIGNALS.has(sidecarSignal) &&
|
|
1338
|
+
(entry.driver === "wasmvm" || timedOut)) {
|
|
1339
|
+
this.finishProcess(entry, 128 + signal);
|
|
1340
|
+
}
|
|
1309
1341
|
}
|
|
1310
1342
|
catch (error) {
|
|
1311
1343
|
if (isNoSuchProcessError(error) || isUnknownVmError(error)) {
|
|
@@ -1520,12 +1552,12 @@ export class NativeSidecarKernelProxy {
|
|
|
1520
1552
|
return this.client.pread(this.session, this.vm, path, offset, length);
|
|
1521
1553
|
},
|
|
1522
1554
|
pwrite: async (path, offset, data) => {
|
|
1523
|
-
const
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1555
|
+
const local = includeLocalMounts ? this.resolveLocalMount(path) : null;
|
|
1556
|
+
if (local) {
|
|
1557
|
+
this.assertLocalWritable(local.mount);
|
|
1558
|
+
return local.mount.fs.pwrite(local.relativePath, offset, data);
|
|
1559
|
+
}
|
|
1560
|
+
return this.client.pwrite(this.session, this.vm, path, offset, data);
|
|
1529
1561
|
},
|
|
1530
1562
|
};
|
|
1531
1563
|
}
|
|
@@ -1727,21 +1759,27 @@ function isMissingHostProcessError(error) {
|
|
|
1727
1759
|
function errnoError(code, message) {
|
|
1728
1760
|
return Object.assign(new Error(`${code}: ${message}`), { code });
|
|
1729
1761
|
}
|
|
1762
|
+
// VirtualStat is a numeric, Node-default-shaped view: u64 fields above
|
|
1763
|
+
// Number.MAX_SAFE_INTEGER lose precision here, same as Node's non-bigint
|
|
1764
|
+
// fs.stat on the host.
|
|
1730
1765
|
function toVirtualStat(stat) {
|
|
1731
1766
|
return {
|
|
1732
1767
|
mode: stat.mode,
|
|
1733
|
-
size: stat.size,
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1768
|
+
size: Number(stat.size),
|
|
1769
|
+
sizeExact: stat.size,
|
|
1770
|
+
blocks: Number(stat.blocks),
|
|
1771
|
+
dev: Number(stat.dev),
|
|
1772
|
+
rdev: Number(stat.rdev),
|
|
1737
1773
|
isDirectory: stat.is_directory,
|
|
1738
1774
|
isSymbolicLink: stat.is_symbolic_link,
|
|
1739
1775
|
atimeMs: stat.atime_ms,
|
|
1740
1776
|
mtimeMs: stat.mtime_ms,
|
|
1741
1777
|
ctimeMs: stat.ctime_ms,
|
|
1742
1778
|
birthtimeMs: stat.birthtime_ms,
|
|
1743
|
-
ino: stat.ino,
|
|
1744
|
-
|
|
1779
|
+
ino: Number(stat.ino),
|
|
1780
|
+
inoExact: stat.ino,
|
|
1781
|
+
nlink: Number(stat.nlink),
|
|
1782
|
+
nlinkExact: stat.nlink,
|
|
1745
1783
|
uid: stat.uid,
|
|
1746
1784
|
gid: stat.gid,
|
|
1747
1785
|
};
|
|
@@ -1774,4 +1812,4 @@ function socketLookupKey(kind, request) {
|
|
|
1774
1812
|
path: request.path ?? null,
|
|
1775
1813
|
});
|
|
1776
1814
|
}
|
|
1777
|
-
export {
|
|
1815
|
+
export { SidecarProcess, SidecarEventBufferOverflow, SidecarProcessError, SidecarProcessExited, } from "./sidecar-process.js";
|
package/dist/native-client.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
import type { LiveSidecarEventSelector } from "./event-buffer.js";
|
|
2
|
+
import type { LiveOwnershipScope } from "./ownership.js";
|
|
1
3
|
import { StdioSidecarProcess } from "./process.js";
|
|
2
|
-
import type { SidecarProcessTransport } from "./sidecar-client.js";
|
|
3
4
|
import type { LiveEventFrame, LiveResponseFrame, LiveSidecarRequestHandler, ProtocolFramePayloadCodec } from "./protocol-frames.js";
|
|
4
|
-
import type { LiveOwnershipScope } from "./ownership.js";
|
|
5
5
|
import type { LiveRequestPayload } from "./request-payloads.js";
|
|
6
|
-
import type {
|
|
7
|
-
export declare const DEFAULT_SIDECAR_FRAME_TIMEOUT_MS = 120000;
|
|
6
|
+
import type { SidecarProcessTransport } from "./sidecar-client.js";
|
|
8
7
|
export declare const DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY = 4096;
|
|
9
8
|
export declare const DEFAULT_SIDECAR_GRACEFUL_EXIT_MS = 5000;
|
|
10
9
|
export declare const DEFAULT_SIDECAR_FORCE_EXIT_MS = 2000;
|
|
@@ -12,12 +11,17 @@ export interface StdioSidecarProtocolClientSpawnOptions {
|
|
|
12
11
|
cwd?: string;
|
|
13
12
|
command?: string;
|
|
14
13
|
args?: string[];
|
|
15
|
-
frameTimeoutMs?: number;
|
|
16
14
|
eventBufferCapacity?: number;
|
|
17
15
|
gracefulExitMs?: number;
|
|
18
16
|
forceExitMs?: number;
|
|
19
17
|
disposedErrorMessage?: string;
|
|
20
18
|
payloadCodec?: ProtocolFramePayloadCodec;
|
|
19
|
+
/**
|
|
20
|
+
* Override the silence watchdog window (default 30s). Tests only — the
|
|
21
|
+
* window is a fixed protocol constant paired with the sidecar's 10s
|
|
22
|
+
* heartbeat cadence, not an operator tunable.
|
|
23
|
+
*/
|
|
24
|
+
silenceTimeoutMs?: number;
|
|
21
25
|
}
|
|
22
26
|
export declare class StdioSidecarProtocolClient implements SidecarProcessTransport {
|
|
23
27
|
readonly child: StdioSidecarProcess["child"];
|
package/dist/native-client.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { resolvePublishedSidecarBinary } from "./binary.js";
|
|
2
|
-
import { SidecarProcessExited, StdioSidecarProcess
|
|
2
|
+
import { SidecarProcessExited, StdioSidecarProcess } from "./process.js";
|
|
3
3
|
import { SidecarProtocolClient } from "./protocol-client.js";
|
|
4
4
|
import { registerSidecarProcessSpawnFactory } from "./sidecar-process.js";
|
|
5
|
-
export const DEFAULT_SIDECAR_FRAME_TIMEOUT_MS = 120_000;
|
|
6
5
|
export const DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY = 4_096;
|
|
7
6
|
export const DEFAULT_SIDECAR_GRACEFUL_EXIT_MS = 5_000;
|
|
8
7
|
export const DEFAULT_SIDECAR_FORCE_EXIT_MS = 2_000;
|
|
@@ -22,9 +21,21 @@ export class StdioSidecarProtocolClient {
|
|
|
22
21
|
this.protocolClient = new SidecarProtocolClient({
|
|
23
22
|
stdin: this.child.stdin,
|
|
24
23
|
stdout: this.child.stdout,
|
|
25
|
-
|
|
24
|
+
control: this.sidecarProcess.control,
|
|
26
25
|
eventBufferCapacity: options.eventBufferCapacity,
|
|
27
26
|
payloadCodec: options.payloadCodec,
|
|
27
|
+
silenceTimeoutMs: options.silenceTimeoutMs,
|
|
28
|
+
// A silent sidecar is dead or wedged; reap the process so it cannot
|
|
29
|
+
// linger as a zombie holding VM resources. The watchdog then rejects
|
|
30
|
+
// all in-flight requests with `SidecarSilenceTimeout`.
|
|
31
|
+
onSilenceExpired: () => {
|
|
32
|
+
try {
|
|
33
|
+
this.child.kill("SIGKILL");
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// The child may have exited between the check and the kill.
|
|
37
|
+
}
|
|
38
|
+
},
|
|
28
39
|
stderrText: () => this.sidecarProcess.stderrText(),
|
|
29
40
|
streamEndedError: () => this.sidecarProcess.currentExitError() ??
|
|
30
41
|
new SidecarProcessExited({
|
|
@@ -47,9 +58,8 @@ export class StdioSidecarProtocolClient {
|
|
|
47
58
|
args: options.args ?? [],
|
|
48
59
|
cwd: options.cwd,
|
|
49
60
|
}), {
|
|
50
|
-
|
|
51
|
-
eventBufferCapacity: options.eventBufferCapacity ??
|
|
52
|
-
DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY,
|
|
61
|
+
silenceTimeoutMs: options.silenceTimeoutMs,
|
|
62
|
+
eventBufferCapacity: options.eventBufferCapacity ?? DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY,
|
|
53
63
|
gracefulExitMs: options.gracefulExitMs ?? DEFAULT_SIDECAR_GRACEFUL_EXIT_MS,
|
|
54
64
|
forceExitMs: options.forceExitMs ?? DEFAULT_SIDECAR_FORCE_EXIT_MS,
|
|
55
65
|
disposedErrorMessage: options.disposedErrorMessage ?? "sidecar client disposed",
|
|
@@ -69,6 +79,13 @@ export class StdioSidecarProtocolClient {
|
|
|
69
79
|
return await this.protocolClient.waitForEvent(matcher, timeoutMs, options);
|
|
70
80
|
}
|
|
71
81
|
async dispose() {
|
|
82
|
+
let shutdownError = null;
|
|
83
|
+
try {
|
|
84
|
+
await this.protocolClient.shutdown(this.disposedErrorMessage);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
shutdownError = error instanceof Error ? error : new Error(String(error));
|
|
88
|
+
}
|
|
72
89
|
this.protocolClient.failPermanently(new Error(this.disposedErrorMessage));
|
|
73
90
|
if (!this.child.stdin.destroyed) {
|
|
74
91
|
try {
|
|
@@ -78,6 +95,14 @@ export class StdioSidecarProtocolClient {
|
|
|
78
95
|
// Stdin may already be closing. The child exit watcher will catch up.
|
|
79
96
|
}
|
|
80
97
|
}
|
|
98
|
+
if (!this.sidecarProcess.control.destroyed) {
|
|
99
|
+
try {
|
|
100
|
+
this.sidecarProcess.control.end();
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// The control socket may already be closing with the child.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
81
106
|
const exitCode = await this.sidecarProcess.waitForExit(this.gracefulExitMs);
|
|
82
107
|
if (exitCode === null) {
|
|
83
108
|
try {
|
|
@@ -107,11 +132,18 @@ export class StdioSidecarProtocolClient {
|
|
|
107
132
|
catch {
|
|
108
133
|
// Best effort. The child is gone so the descriptor will close on its own.
|
|
109
134
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
135
|
+
try {
|
|
136
|
+
this.sidecarProcess.control.destroy();
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Best effort. The child is gone so the descriptor will close on its own.
|
|
140
|
+
}
|
|
141
|
+
if (exitCode !== null && exitCode !== 0 && this.child.signalCode === null) {
|
|
113
142
|
throw new Error(`sidecar exited with code ${exitCode}\nstderr:\n${this.sidecarProcess.stderrText()}`);
|
|
114
143
|
}
|
|
144
|
+
if (shutdownError && this.child.exitCode !== 0) {
|
|
145
|
+
throw shutdownError;
|
|
146
|
+
}
|
|
115
147
|
}
|
|
116
148
|
failPermanently(error) {
|
|
117
149
|
this.protocolClient.failPermanently(error, {
|
|
@@ -114,4 +114,4 @@ export declare const nodeRuntimePermissionsSchema: z.ZodObject<{
|
|
|
114
114
|
* the Rust VM config.
|
|
115
115
|
*/
|
|
116
116
|
export declare const nodeRuntimeCreateOptionsSchema: z.ZodType<NodeRuntimeCreateOptions>;
|
|
117
|
-
export declare function parseNodeRuntimeCreateOptions(options
|
|
117
|
+
export declare function parseNodeRuntimeCreateOptions(options: NodeRuntimeCreateOptions): NodeRuntimeCreateOptions;
|
|
@@ -1,6 +1,46 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
const permissionModeSchema = z.enum(["allow", "deny"]);
|
|
3
3
|
const stringArray = z.array(z.string());
|
|
4
|
+
const vmIdSchema = z.number().int().min(0).max(0xffffffff);
|
|
5
|
+
const vmAccountNameSchema = z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.refine((value) => !/[:\s\0]/u.test(value), "Invalid account name");
|
|
9
|
+
const vmGuestPathSchema = z.string().startsWith("/");
|
|
10
|
+
const vmUserAccountSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
uid: vmIdSchema,
|
|
13
|
+
gid: vmIdSchema,
|
|
14
|
+
username: vmAccountNameSchema,
|
|
15
|
+
homedir: vmGuestPathSchema,
|
|
16
|
+
shell: vmGuestPathSchema,
|
|
17
|
+
gecos: z.string().optional(),
|
|
18
|
+
supplementaryGids: z.array(vmIdSchema).max(64),
|
|
19
|
+
})
|
|
20
|
+
.strict();
|
|
21
|
+
const vmGroupSchema = z
|
|
22
|
+
.object({
|
|
23
|
+
gid: vmIdSchema,
|
|
24
|
+
name: vmAccountNameSchema,
|
|
25
|
+
members: z.array(vmAccountNameSchema),
|
|
26
|
+
})
|
|
27
|
+
.strict();
|
|
28
|
+
const vmUserConfigSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
uid: vmIdSchema.optional(),
|
|
31
|
+
gid: vmIdSchema.optional(),
|
|
32
|
+
euid: vmIdSchema.optional(),
|
|
33
|
+
egid: vmIdSchema.optional(),
|
|
34
|
+
username: z.string().optional(),
|
|
35
|
+
homedir: z.string().optional(),
|
|
36
|
+
shell: z.string().optional(),
|
|
37
|
+
gecos: z.string().optional(),
|
|
38
|
+
groupName: z.string().optional(),
|
|
39
|
+
supplementaryGids: z.array(vmIdSchema).max(64).optional(),
|
|
40
|
+
accounts: z.array(vmUserAccountSchema).max(64).optional(),
|
|
41
|
+
groups: z.array(vmGroupSchema).max(128).optional(),
|
|
42
|
+
})
|
|
43
|
+
.strict();
|
|
4
44
|
const fsPermissionRuleSchema = z
|
|
5
45
|
.object({
|
|
6
46
|
mode: permissionModeSchema,
|
|
@@ -91,8 +131,10 @@ const bindingDefinitionSchema = z
|
|
|
91
131
|
*/
|
|
92
132
|
export const nodeRuntimeCreateOptionsSchema = z
|
|
93
133
|
.object({
|
|
134
|
+
filesystem: z.custom((value) => typeof value === "object" && value !== null, { message: "Expected caller-owned VirtualFileSystem object" }),
|
|
94
135
|
env: z.record(z.string(), z.string()).optional(),
|
|
95
136
|
cwd: z.string().optional(),
|
|
137
|
+
user: vmUserConfigSchema.optional(),
|
|
96
138
|
permissions: nodeRuntimePermissionsSchema.optional(),
|
|
97
139
|
commandsDir: z.string().optional(),
|
|
98
140
|
wasmCommandDirs: stringArray.optional(),
|
|
@@ -116,6 +158,6 @@ export const nodeRuntimeCreateOptionsSchema = z
|
|
|
116
158
|
jsRuntime: jsRuntimeSchema.optional(),
|
|
117
159
|
})
|
|
118
160
|
.strict();
|
|
119
|
-
export function parseNodeRuntimeCreateOptions(options
|
|
161
|
+
export function parseNodeRuntimeCreateOptions(options) {
|
|
120
162
|
return nodeRuntimeCreateOptionsSchema.parse(options);
|
|
121
163
|
}
|
package/dist/node-runtime.d.ts
CHANGED
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
* isolation boundary — no host escapes, no real Node.js builtins for guest
|
|
18
18
|
* work.
|
|
19
19
|
*/
|
|
20
|
-
import type { BindingDefinition, KernelBootTiming, Permissions, VirtualDirEntry } from "./test-runtime.js";
|
|
20
|
+
import type { BindingDefinition, KernelBootTiming, Permissions, VirtualDirEntry, VirtualFileSystem } from "./test-runtime.js";
|
|
21
21
|
import type { JsRuntimeConfig } from "./generated/JsRuntimeConfig.js";
|
|
22
|
+
import type { VmUserConfig } from "./generated/VmUserConfig.js";
|
|
22
23
|
import type { SidecarProcess } from "./sidecar-process.js";
|
|
23
|
-
export type { BindingDefinition,
|
|
24
|
+
export type { BindingDefinition, BindingExample, VirtualDirEntry, } from "./test-runtime.js";
|
|
24
25
|
export { resolveNodeRuntimeSidecarBinary } from "./test-runtime.js";
|
|
25
26
|
export type NodeRuntimeBootTimingPhase = KernelBootTiming["phase"] | "runtime_mount_wasm" | "runtime_mount_node" | "bindings";
|
|
26
27
|
export interface NodeRuntimeBootTiming {
|
|
@@ -50,10 +51,18 @@ export declare function resolveNodeRuntimeCommandsDir(explicit?: string): string
|
|
|
50
51
|
* `crates/vm-config/src/lib.rs::CreateVmConfig`.
|
|
51
52
|
*/
|
|
52
53
|
export interface NodeRuntimeCreateOptions {
|
|
54
|
+
/**
|
|
55
|
+
* Caller-owned filesystem used only by this low-level compatibility runtime.
|
|
56
|
+
* AgentOS clients do not create a TypeScript filesystem implicitly; normal
|
|
57
|
+
* product code should use the sidecar-owned VFS through `AgentOs`.
|
|
58
|
+
*/
|
|
59
|
+
filesystem: VirtualFileSystem;
|
|
53
60
|
/** Environment variables visible to guest processes. */
|
|
54
61
|
env?: Record<string, string>;
|
|
55
62
|
/** Initial working directory for guest processes. Defaults to `/workspace`. */
|
|
56
63
|
cwd?: string;
|
|
64
|
+
/** Initial virtual Linux credentials and account record. Defaults to `1000:1000` (`agentos`). */
|
|
65
|
+
user?: VmUserConfig;
|
|
57
66
|
/**
|
|
58
67
|
* Permission policy for the VM. Merged over a secure default that **denies
|
|
59
68
|
* network access** (guest code cannot reach the network until you opt in);
|
|
@@ -440,7 +449,7 @@ export declare class NodeRuntime {
|
|
|
440
449
|
* session, creates the VM with a bootstrapped root filesystem, mounts the
|
|
441
450
|
* shell and Node runtimes, and waits for the VM to report ready.
|
|
442
451
|
*/
|
|
443
|
-
static create(options
|
|
452
|
+
static create(options: NodeRuntimeCreateOptions): Promise<NodeRuntime>;
|
|
444
453
|
createResidentRunner(_options?: NodeRuntimeResidentRunnerOptions): Promise<NodeRuntimeResidentRunner>;
|
|
445
454
|
/**
|
|
446
455
|
* Run `code` as a guest Node program and capture its output.
|
package/dist/node-runtime.js
CHANGED
|
@@ -20,18 +20,18 @@
|
|
|
20
20
|
import { existsSync } from "node:fs";
|
|
21
21
|
import path from "node:path";
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
|
-
import {
|
|
23
|
+
import { createKernel, createNodeRuntime, createWasmVmRuntime, NodeFileSystem, } from "./test-runtime.js";
|
|
24
24
|
import { parseNodeRuntimeCreateOptions } from "./node-runtime-options-schema.js";
|
|
25
25
|
export { resolveNodeRuntimeSidecarBinary } from "./test-runtime.js";
|
|
26
26
|
/** Repository root, used to locate the in-repo WASM command build output. */
|
|
27
27
|
const REPO_ROOT = fileURLToPath(new URL("../../..", import.meta.url));
|
|
28
28
|
/**
|
|
29
29
|
* In-repo build output for the WASM coreutils/shell command binaries, produced
|
|
30
|
-
* by the Rust command build (`make -C
|
|
30
|
+
* by the Rust command build (`make -C toolchain wasm`). Only present in a
|
|
31
31
|
* developer checkout; preferred when it exists so local edits are picked up
|
|
32
32
|
* without re-vendoring.
|
|
33
33
|
*/
|
|
34
|
-
const REPO_COMMANDS_DIR = path.join(REPO_ROOT, "
|
|
34
|
+
const REPO_COMMANDS_DIR = path.join(REPO_ROOT, "toolchain/target/wasm32-wasip1/release/commands");
|
|
35
35
|
/**
|
|
36
36
|
* Commands vendored into the published `@rivet-dev/agentos-runtime-core` package by
|
|
37
37
|
* `scripts/copy-wasm-commands.mjs` (listed in `files` as `commands`). This is
|
|
@@ -128,14 +128,14 @@ export class NodeRuntime {
|
|
|
128
128
|
* session, creates the VM with a bootstrapped root filesystem, mounts the
|
|
129
129
|
* shell and Node runtimes, and waits for the VM to report ready.
|
|
130
130
|
*/
|
|
131
|
-
static async create(options
|
|
131
|
+
static async create(options) {
|
|
132
132
|
options = parseNodeRuntimeCreateOptions(options);
|
|
133
133
|
const commandsDir = resolveNodeRuntimeCommandsDir(options.commandsDir);
|
|
134
134
|
// Seed caller-provided files into the VM's in-memory filesystem before
|
|
135
135
|
// boot so they are part of the root filesystem snapshot the guest sees
|
|
136
136
|
// (e.g. projected npm packages or fixtures). The host filesystem is
|
|
137
137
|
// never exposed; only these bytes are copied in.
|
|
138
|
-
const filesystem =
|
|
138
|
+
const filesystem = options.filesystem;
|
|
139
139
|
if (options.files) {
|
|
140
140
|
for (const [filePath, content] of Object.entries(options.files)) {
|
|
141
141
|
await filesystem.writeFile(filePath, content);
|
|
@@ -183,6 +183,7 @@ export class NodeRuntime {
|
|
|
183
183
|
},
|
|
184
184
|
env: options.env,
|
|
185
185
|
cwd: options.cwd,
|
|
186
|
+
user: options.user,
|
|
186
187
|
sidecar: options.sidecar,
|
|
187
188
|
onBootTiming: (timing) => options.onBootTiming?.(timing),
|
|
188
189
|
loopbackExemptPorts: options.loopbackExemptPorts,
|
|
@@ -202,7 +203,7 @@ export class NodeRuntime {
|
|
|
202
203
|
// installed as guest commands the moment the VM is ready.
|
|
203
204
|
const bindings = options.bindings;
|
|
204
205
|
if (bindings && Object.keys(bindings).length > 0) {
|
|
205
|
-
await measureBootTiming("bindings", options.onBootTiming, () => kernel.
|
|
206
|
+
await measureBootTiming("bindings", options.onBootTiming, () => kernel.registerBindings(bindings));
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
catch (error) {
|
|
@@ -588,7 +589,7 @@ export class NodeRuntime {
|
|
|
588
589
|
* {@link NodeRuntime.create}) so the bindings are invocable.
|
|
589
590
|
*/
|
|
590
591
|
async registerBindings(bindings) {
|
|
591
|
-
await this.kernel.
|
|
592
|
+
await this.kernel.registerBindings(bindings);
|
|
592
593
|
}
|
|
593
594
|
/**
|
|
594
595
|
* Write a file into the VM's virtual filesystem, creating parent
|
package/dist/process.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ChildProcessWithoutNullStreams } from "node:child_process";
|
|
2
|
+
import type { Duplex } from "node:stream";
|
|
2
3
|
export { SidecarProcessError, SidecarProcessExited, } from "./sidecar-errors.js";
|
|
3
4
|
import { SidecarProcessError, SidecarProcessExited } from "./sidecar-errors.js";
|
|
4
5
|
export interface StdioSidecarProcessSpawnOptions {
|
|
@@ -8,12 +9,13 @@ export interface StdioSidecarProcessSpawnOptions {
|
|
|
8
9
|
}
|
|
9
10
|
export declare class StdioSidecarProcess {
|
|
10
11
|
readonly child: ChildProcessWithoutNullStreams;
|
|
12
|
+
readonly control: Duplex;
|
|
11
13
|
private readonly stderrChunks;
|
|
12
14
|
private readonly exitListeners;
|
|
13
15
|
private readonly errorListeners;
|
|
14
16
|
private constructor();
|
|
15
17
|
static spawn(options: StdioSidecarProcessSpawnOptions): StdioSidecarProcess;
|
|
16
|
-
static fromChild(child: ChildProcessWithoutNullStreams): StdioSidecarProcess;
|
|
18
|
+
static fromChild(child: ChildProcessWithoutNullStreams, control?: Duplex): StdioSidecarProcess;
|
|
17
19
|
onExit(handler: (error: SidecarProcessExited) => void): () => void;
|
|
18
20
|
onError(handler: (error: SidecarProcessError) => void): () => void;
|
|
19
21
|
stderrText(): string;
|
package/dist/process.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { spawn
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
2
|
export { SidecarProcessError, SidecarProcessExited, } from "./sidecar-errors.js";
|
|
3
|
-
import { SidecarProcessError, SidecarProcessExited
|
|
3
|
+
import { SidecarProcessError, SidecarProcessExited } from "./sidecar-errors.js";
|
|
4
4
|
export class StdioSidecarProcess {
|
|
5
5
|
child;
|
|
6
|
+
control;
|
|
6
7
|
stderrChunks = [];
|
|
7
8
|
exitListeners = new Set();
|
|
8
9
|
errorListeners = new Set();
|
|
9
|
-
constructor(child) {
|
|
10
|
+
constructor(child, control) {
|
|
10
11
|
this.child = child;
|
|
12
|
+
this.control = control;
|
|
11
13
|
this.child.stderr.on("data", (chunk) => {
|
|
12
14
|
this.stderrChunks.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk));
|
|
13
15
|
});
|
|
@@ -30,13 +32,20 @@ export class StdioSidecarProcess {
|
|
|
30
32
|
});
|
|
31
33
|
}
|
|
32
34
|
static spawn(options) {
|
|
33
|
-
|
|
35
|
+
const child = spawn(options.command, options.args ?? [], {
|
|
34
36
|
cwd: options.cwd,
|
|
35
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
36
|
-
})
|
|
37
|
+
stdio: ["pipe", "pipe", "pipe", "pipe"],
|
|
38
|
+
});
|
|
39
|
+
try {
|
|
40
|
+
return new StdioSidecarProcess(child, requireControlStream(child));
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
child.kill("SIGKILL");
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
37
46
|
}
|
|
38
|
-
static fromChild(child) {
|
|
39
|
-
return new StdioSidecarProcess(child);
|
|
47
|
+
static fromChild(child, control) {
|
|
48
|
+
return new StdioSidecarProcess(child, control ?? requireControlStream(child));
|
|
40
49
|
}
|
|
41
50
|
onExit(handler) {
|
|
42
51
|
this.exitListeners.add(handler);
|
|
@@ -95,3 +104,13 @@ export class StdioSidecarProcess {
|
|
|
95
104
|
});
|
|
96
105
|
}
|
|
97
106
|
}
|
|
107
|
+
function requireControlStream(child) {
|
|
108
|
+
const stream = child.stdio[3];
|
|
109
|
+
if (!stream ||
|
|
110
|
+
typeof stream.write !== "function" ||
|
|
111
|
+
typeof stream.on !== "function" ||
|
|
112
|
+
typeof stream.read !== "function") {
|
|
113
|
+
throw new Error("sidecar process did not expose a full-duplex fd 3");
|
|
114
|
+
}
|
|
115
|
+
return stream;
|
|
116
|
+
}
|