@rivet-dev/agentos-runtime-core 0.0.0-main.2f99c77 → 0.0.0-main.5ac5a14
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 +2 -2
- 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/binary.js +4 -3
- package/dist/descriptors.d.ts +2 -12
- package/dist/descriptors.js +10 -6
- package/dist/event-buffer.d.ts +14 -0
- package/dist/event-buffer.js +30 -1
- package/dist/frame-stream.d.ts +34 -0
- package/dist/frame-stream.js +121 -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/ExecutionLimitsConfig.d.ts +5 -0
- package/dist/generated/ExecutionLimitsConfig.js +2 -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 +16 -2
- package/dist/generated/VmSqliteDescriptor.d.ts +10 -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 +610 -2
- package/dist/generated-protocol.js +1534 -75
- package/dist/kernel-proxy.d.ts +4 -0
- package/dist/kernel-proxy.js +66 -34
- package/dist/native-client.d.ts +3 -3
- package/dist/native-client.js +43 -8
- 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 +13 -12
- package/dist/process.d.ts +5 -1
- package/dist/process.js +35 -8
- package/dist/protocol-client.d.ts +9 -2
- package/dist/protocol-client.js +127 -7
- 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 +100 -1
- package/dist/request-payloads.js +73 -2
- package/dist/response-payloads.d.ts +49 -1
- package/dist/response-payloads.js +51 -1
- package/dist/sidecar-errors.d.ts +22 -0
- package/dist/sidecar-errors.js +10 -0
- package/dist/sidecar-process.d.ts +20 -3
- package/dist/sidecar-process.js +104 -14
- package/dist/test-runtime.d.ts +25 -7
- package/dist/test-runtime.js +216 -69
- package/dist/vm-config.d.ts +5 -1
- package/package.json +16 -6
- 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.d.ts
CHANGED
|
@@ -4,11 +4,15 @@ import type { AuthenticatedSession, CreatedVm, SidecarProcess, SidecarMountDescr
|
|
|
4
4
|
export interface PlainMountConfig {
|
|
5
5
|
path: string;
|
|
6
6
|
driver: VirtualFileSystem;
|
|
7
|
+
guestSource?: string;
|
|
8
|
+
guestFstype?: string;
|
|
7
9
|
readOnly?: boolean;
|
|
8
10
|
}
|
|
9
11
|
export interface NativeMountConfig {
|
|
10
12
|
path: string;
|
|
11
13
|
plugin: NativeMountPluginDescriptor;
|
|
14
|
+
guestSource?: string;
|
|
15
|
+
guestFstype?: string;
|
|
12
16
|
readOnly?: boolean;
|
|
13
17
|
}
|
|
14
18
|
export declare function serializeMountConfigForSidecar(mount: PlainMountConfig | NativeMountConfig): SidecarMountDescriptor;
|
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,
|
|
@@ -22,7 +26,7 @@ export function serializeMountConfigForSidecar(mount) {
|
|
|
22
26
|
}
|
|
23
27
|
const SYNTHETIC_PID_BASE = 1_000_000;
|
|
24
28
|
const MISSING_EXIT_EVENT_GRACE_MS = 500;
|
|
25
|
-
const PROTECTED_READ_ONLY_GUEST_ROOTS = ["/etc/
|
|
29
|
+
const PROTECTED_READ_ONLY_GUEST_ROOTS = ["/etc/agentos"];
|
|
26
30
|
const TRAILING_OUTPUT_DRAIN_INTERVAL_MS = 10;
|
|
27
31
|
const TRAILING_OUTPUT_DRAIN_MAX_MS = 250;
|
|
28
32
|
const TRAILING_OUTPUT_DRAIN_QUIET_TURNS = 2;
|
|
@@ -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
|
|
@@ -477,14 +493,22 @@ export class NativeSidecarKernelProxy {
|
|
|
477
493
|
return;
|
|
478
494
|
}
|
|
479
495
|
entry.pendingKillSignal = signal;
|
|
480
|
-
void entry.startPromise
|
|
481
|
-
|
|
496
|
+
void entry.startPromise
|
|
497
|
+
.then(async () => {
|
|
498
|
+
if (entry.pendingKillSignal === null) {
|
|
482
499
|
return;
|
|
483
500
|
}
|
|
484
501
|
const pendingSignal = entry.pendingKillSignal;
|
|
485
502
|
entry.pendingKillSignal = null;
|
|
486
503
|
await this.signalProcess(entry, pendingSignal);
|
|
504
|
+
})
|
|
505
|
+
.catch((error) => {
|
|
506
|
+
this.handleBackgroundProcessError(entry, error);
|
|
487
507
|
});
|
|
508
|
+
if ((signal === 9 || signal === 15) &&
|
|
509
|
+
entry.exitCode === null) {
|
|
510
|
+
this.finishProcess(entry, 128 + signal);
|
|
511
|
+
}
|
|
488
512
|
},
|
|
489
513
|
wait: async () => {
|
|
490
514
|
const exitCode = await this.waitForTrackedProcess(entry);
|
|
@@ -506,7 +530,7 @@ export class NativeSidecarKernelProxy {
|
|
|
506
530
|
return proc;
|
|
507
531
|
}
|
|
508
532
|
openShell(options) {
|
|
509
|
-
const
|
|
533
|
+
const terminalHandlers = new Set();
|
|
510
534
|
const stderrHandlers = new Set();
|
|
511
535
|
const command = options?.command ?? "sh";
|
|
512
536
|
const args = options?.args ??
|
|
@@ -565,7 +589,7 @@ export class NativeSidecarKernelProxy {
|
|
|
565
589
|
const normalized = normalizeSyntheticTerminalText(text);
|
|
566
590
|
updateSyntheticCursor(normalized);
|
|
567
591
|
const chunk = textEncoder.encode(normalized);
|
|
568
|
-
for (const handler of
|
|
592
|
+
for (const handler of terminalHandlers) {
|
|
569
593
|
handler(chunk);
|
|
570
594
|
}
|
|
571
595
|
};
|
|
@@ -576,7 +600,7 @@ export class NativeSidecarKernelProxy {
|
|
|
576
600
|
const normalized = normalizeSyntheticTerminalText(text);
|
|
577
601
|
updateSyntheticCursor(normalized);
|
|
578
602
|
const chunk = textEncoder.encode(normalized);
|
|
579
|
-
for (const handler of
|
|
603
|
+
for (const handler of terminalHandlers) {
|
|
580
604
|
handler(chunk);
|
|
581
605
|
}
|
|
582
606
|
};
|
|
@@ -621,7 +645,7 @@ export class NativeSidecarKernelProxy {
|
|
|
621
645
|
commandInFlight = false;
|
|
622
646
|
const promptPrefix = syntheticCursorAtLineStart ? "" : "\r\n";
|
|
623
647
|
const promptChunk = textEncoder.encode(`${promptPrefix}${promptText}`);
|
|
624
|
-
for (const handler of
|
|
648
|
+
for (const handler of terminalHandlers) {
|
|
625
649
|
handler(promptChunk);
|
|
626
650
|
}
|
|
627
651
|
syntheticCursorAtLineStart = false;
|
|
@@ -658,7 +682,7 @@ export class NativeSidecarKernelProxy {
|
|
|
658
682
|
}
|
|
659
683
|
};
|
|
660
684
|
let onData = null;
|
|
661
|
-
|
|
685
|
+
terminalHandlers.add((data) => onData?.(data));
|
|
662
686
|
if (options?.onStderr) {
|
|
663
687
|
stderrHandlers.add(options.onStderr);
|
|
664
688
|
}
|
|
@@ -817,7 +841,7 @@ export class NativeSidecarKernelProxy {
|
|
|
817
841
|
cwd: options?.cwd,
|
|
818
842
|
streamStdin: true,
|
|
819
843
|
onStdout: (chunk) => {
|
|
820
|
-
for (const handler of
|
|
844
|
+
for (const handler of terminalHandlers) {
|
|
821
845
|
handler(chunk);
|
|
822
846
|
}
|
|
823
847
|
if (commandInFlight) {
|
|
@@ -825,6 +849,11 @@ export class NativeSidecarKernelProxy {
|
|
|
825
849
|
}
|
|
826
850
|
},
|
|
827
851
|
onStderr: (chunk) => {
|
|
852
|
+
// `onData` is the ordered PTY rendering stream. `onStderr` remains an
|
|
853
|
+
// optional channel-specific diagnostic tap and must not also be rendered.
|
|
854
|
+
for (const handler of terminalHandlers) {
|
|
855
|
+
handler(chunk);
|
|
856
|
+
}
|
|
828
857
|
for (const handler of stderrHandlers) {
|
|
829
858
|
handler(chunk);
|
|
830
859
|
}
|
|
@@ -878,13 +907,7 @@ export class NativeSidecarKernelProxy {
|
|
|
878
907
|
const stdin = process.stdin;
|
|
879
908
|
const stdout = process.stdout;
|
|
880
909
|
const { onData, ...shellOptions } = options ?? {};
|
|
881
|
-
const shell = this.openShell(
|
|
882
|
-
...shellOptions,
|
|
883
|
-
onStderr: shellOptions.onStderr ??
|
|
884
|
-
((data) => {
|
|
885
|
-
process.stderr.write(data);
|
|
886
|
-
}),
|
|
887
|
-
});
|
|
910
|
+
const shell = this.openShell(shellOptions);
|
|
888
911
|
const outputHandler = onData ??
|
|
889
912
|
((data) => {
|
|
890
913
|
stdout.write(data);
|
|
@@ -1128,15 +1151,6 @@ export class NativeSidecarKernelProxy {
|
|
|
1128
1151
|
if (entry.onStdout.size === 0 && entry.onStderr.size === 0) {
|
|
1129
1152
|
return;
|
|
1130
1153
|
}
|
|
1131
|
-
if (entry.exitViaEvent) {
|
|
1132
|
-
// The sidecar drains process output before it emits `process_exited`,
|
|
1133
|
-
// the stdio frame stream is FIFO, and this pump dispatches events in
|
|
1134
|
-
// order. Once the exit event reaches this entry, no trailing output can
|
|
1135
|
-
// follow it; one zero-delay turn is cheap insurance for same-tick
|
|
1136
|
-
// listener scheduling.
|
|
1137
|
-
await drainTrailingProcessOutputTurn(0);
|
|
1138
|
-
return;
|
|
1139
|
-
}
|
|
1140
1154
|
let observedGeneration = entry.outputGeneration;
|
|
1141
1155
|
let quietTurns = 0;
|
|
1142
1156
|
let delayMs = 0;
|
|
@@ -1307,9 +1321,27 @@ export class NativeSidecarKernelProxy {
|
|
|
1307
1321
|
return entry.waitWithFallbackPromise;
|
|
1308
1322
|
}
|
|
1309
1323
|
async signalProcess(entry, signal) {
|
|
1310
|
-
|
|
1324
|
+
const sidecarSignal = toSidecarSignalName(signal);
|
|
1325
|
+
let timedOut = false;
|
|
1326
|
+
const killPromise = this.client.killProcess(this.session, this.vm, entry.processId, sidecarSignal);
|
|
1311
1327
|
try {
|
|
1312
|
-
await
|
|
1328
|
+
await Promise.race([
|
|
1329
|
+
killPromise,
|
|
1330
|
+
new Promise((resolve) => setTimeout(() => {
|
|
1331
|
+
timedOut = true;
|
|
1332
|
+
resolve();
|
|
1333
|
+
}, 1000)),
|
|
1334
|
+
]);
|
|
1335
|
+
if (timedOut) {
|
|
1336
|
+
void killPromise.catch(() => { });
|
|
1337
|
+
}
|
|
1338
|
+
const hasGuestHandler = this.signalStates.get(entry.pid)?.handlers.has(signal) ?? false;
|
|
1339
|
+
if (entry.exitCode === null &&
|
|
1340
|
+
!hasGuestHandler &&
|
|
1341
|
+
LOCALLY_TERMINATING_SIGNALS.has(sidecarSignal) &&
|
|
1342
|
+
(entry.driver === "wasmvm" || timedOut)) {
|
|
1343
|
+
this.finishProcess(entry, 128 + signal);
|
|
1344
|
+
}
|
|
1313
1345
|
}
|
|
1314
1346
|
catch (error) {
|
|
1315
1347
|
if (isNoSuchProcessError(error) || isUnknownVmError(error)) {
|
|
@@ -1524,12 +1556,12 @@ export class NativeSidecarKernelProxy {
|
|
|
1524
1556
|
return this.client.pread(this.session, this.vm, path, offset, length);
|
|
1525
1557
|
},
|
|
1526
1558
|
pwrite: async (path, offset, data) => {
|
|
1527
|
-
const
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1559
|
+
const local = includeLocalMounts ? this.resolveLocalMount(path) : null;
|
|
1560
|
+
if (local) {
|
|
1561
|
+
this.assertLocalWritable(local.mount);
|
|
1562
|
+
return local.mount.fs.pwrite(local.relativePath, offset, data);
|
|
1563
|
+
}
|
|
1564
|
+
return this.client.pwrite(this.session, this.vm, path, offset, data);
|
|
1533
1565
|
},
|
|
1534
1566
|
};
|
|
1535
1567
|
}
|
package/dist/native-client.d.ts
CHANGED
|
@@ -1,9 +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 {
|
|
6
|
+
import type { SidecarProcessTransport } from "./sidecar-client.js";
|
|
7
7
|
export declare const DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY = 4096;
|
|
8
8
|
export declare const DEFAULT_SIDECAR_GRACEFUL_EXIT_MS = 5000;
|
|
9
9
|
export declare const DEFAULT_SIDECAR_FORCE_EXIT_MS = 2000;
|
package/dist/native-client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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
5
|
export const DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY = 4_096;
|
|
@@ -18,9 +18,19 @@ export class StdioSidecarProtocolClient {
|
|
|
18
18
|
this.gracefulExitMs = options.gracefulExitMs;
|
|
19
19
|
this.forceExitMs = options.forceExitMs;
|
|
20
20
|
this.disposedErrorMessage = options.disposedErrorMessage;
|
|
21
|
+
const transportOptions = this.sidecarProcess.combinedStdio
|
|
22
|
+
? {
|
|
23
|
+
stdin: this.child.stdin,
|
|
24
|
+
stdout: this.child.stdout,
|
|
25
|
+
combinedStdio: true,
|
|
26
|
+
}
|
|
27
|
+
: {
|
|
28
|
+
stdin: this.child.stdin,
|
|
29
|
+
stdout: this.child.stdout,
|
|
30
|
+
control: this.sidecarProcess.control,
|
|
31
|
+
};
|
|
21
32
|
this.protocolClient = new SidecarProtocolClient({
|
|
22
|
-
|
|
23
|
-
stdout: this.child.stdout,
|
|
33
|
+
...transportOptions,
|
|
24
34
|
eventBufferCapacity: options.eventBufferCapacity,
|
|
25
35
|
payloadCodec: options.payloadCodec,
|
|
26
36
|
silenceTimeoutMs: options.silenceTimeoutMs,
|
|
@@ -52,14 +62,15 @@ export class StdioSidecarProtocolClient {
|
|
|
52
62
|
});
|
|
53
63
|
}
|
|
54
64
|
static spawn(options = {}) {
|
|
65
|
+
const combinedStdio = typeof globalThis._childProcessSpawnStart !== "undefined";
|
|
55
66
|
return new StdioSidecarProtocolClient(StdioSidecarProcess.spawn({
|
|
56
67
|
command: options.command ?? resolvePublishedSidecarBinary(),
|
|
57
68
|
args: options.args ?? [],
|
|
58
69
|
cwd: options.cwd,
|
|
70
|
+
combinedStdio,
|
|
59
71
|
}), {
|
|
60
72
|
silenceTimeoutMs: options.silenceTimeoutMs,
|
|
61
|
-
eventBufferCapacity: options.eventBufferCapacity ??
|
|
62
|
-
DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY,
|
|
73
|
+
eventBufferCapacity: options.eventBufferCapacity ?? DEFAULT_SIDECAR_EVENT_BUFFER_CAPACITY,
|
|
63
74
|
gracefulExitMs: options.gracefulExitMs ?? DEFAULT_SIDECAR_GRACEFUL_EXIT_MS,
|
|
64
75
|
forceExitMs: options.forceExitMs ?? DEFAULT_SIDECAR_FORCE_EXIT_MS,
|
|
65
76
|
disposedErrorMessage: options.disposedErrorMessage ?? "sidecar client disposed",
|
|
@@ -79,6 +90,13 @@ export class StdioSidecarProtocolClient {
|
|
|
79
90
|
return await this.protocolClient.waitForEvent(matcher, timeoutMs, options);
|
|
80
91
|
}
|
|
81
92
|
async dispose() {
|
|
93
|
+
let shutdownError = null;
|
|
94
|
+
try {
|
|
95
|
+
await this.protocolClient.shutdown(this.disposedErrorMessage);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
shutdownError = error instanceof Error ? error : new Error(String(error));
|
|
99
|
+
}
|
|
82
100
|
this.protocolClient.failPermanently(new Error(this.disposedErrorMessage));
|
|
83
101
|
if (!this.child.stdin.destroyed) {
|
|
84
102
|
try {
|
|
@@ -88,6 +106,14 @@ export class StdioSidecarProtocolClient {
|
|
|
88
106
|
// Stdin may already be closing. The child exit watcher will catch up.
|
|
89
107
|
}
|
|
90
108
|
}
|
|
109
|
+
if (this.sidecarProcess.control && !this.sidecarProcess.control.destroyed) {
|
|
110
|
+
try {
|
|
111
|
+
this.sidecarProcess.control.end();
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// The control socket may already be closing with the child.
|
|
115
|
+
}
|
|
116
|
+
}
|
|
91
117
|
const exitCode = await this.sidecarProcess.waitForExit(this.gracefulExitMs);
|
|
92
118
|
if (exitCode === null) {
|
|
93
119
|
try {
|
|
@@ -117,11 +143,20 @@ export class StdioSidecarProtocolClient {
|
|
|
117
143
|
catch {
|
|
118
144
|
// Best effort. The child is gone so the descriptor will close on its own.
|
|
119
145
|
}
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
146
|
+
if (this.sidecarProcess.control) {
|
|
147
|
+
try {
|
|
148
|
+
this.sidecarProcess.control.destroy();
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Best effort. The child is gone so the descriptor will close on its own.
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (exitCode !== null && exitCode !== 0 && this.child.signalCode === null) {
|
|
123
155
|
throw new Error(`sidecar exited with code ${exitCode}\nstderr:\n${this.sidecarProcess.stderrText()}`);
|
|
124
156
|
}
|
|
157
|
+
if (shutdownError && this.child.exitCode !== 0) {
|
|
158
|
+
throw shutdownError;
|
|
159
|
+
}
|
|
125
160
|
}
|
|
126
161
|
failPermanently(error) {
|
|
127
162
|
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,22 +20,22 @@
|
|
|
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
|
|
38
|
-
* the directory a real `npm install
|
|
38
|
+
* the directory a real `npm install agentos` resolves: from the compiled
|
|
39
39
|
* `dist/node-runtime.js` it sits at `<package>/commands`. This is the analogue
|
|
40
40
|
* of how the sidecar binary ships inside `@rivet-dev/agentos-runtime-sidecar`.
|
|
41
41
|
*/
|
|
@@ -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) {
|
|
@@ -221,7 +222,7 @@ export class NodeRuntime {
|
|
|
221
222
|
* `node <file>`; it runs as standard ESM (top-level `await`, `import`).
|
|
222
223
|
*/
|
|
223
224
|
async exec(code, options = {}) {
|
|
224
|
-
const programPath = `/tmp/
|
|
225
|
+
const programPath = `/tmp/agentos-program-${nextProgramId++}.mjs`;
|
|
225
226
|
await this.kernel.writeFile(programPath, withBindingPreamble(code));
|
|
226
227
|
return this.runProgram(programPath, options);
|
|
227
228
|
}
|
|
@@ -327,7 +328,7 @@ export class NodeRuntime {
|
|
|
327
328
|
* ```
|
|
328
329
|
*/
|
|
329
330
|
async spawn(code, options = {}) {
|
|
330
|
-
const programPath = `/tmp/
|
|
331
|
+
const programPath = `/tmp/agentos-program-${nextProgramId++}.mjs`;
|
|
331
332
|
await this.kernel.writeFile(programPath, withBindingPreamble(code));
|
|
332
333
|
const proc = this.kernel.spawn("node", [programPath], {
|
|
333
334
|
env: options.env,
|
|
@@ -441,8 +442,8 @@ export class NodeRuntime {
|
|
|
441
442
|
*/
|
|
442
443
|
async run(code, options = {}) {
|
|
443
444
|
const id = nextProgramId++;
|
|
444
|
-
const resultPath = `/tmp/
|
|
445
|
-
const programPath = `/tmp/
|
|
445
|
+
const resultPath = `/tmp/agentos-result-${id}.json`;
|
|
446
|
+
const programPath = `/tmp/agentos-program-${id}.mjs`;
|
|
446
447
|
// Inject the __return helper as a module-level preamble, then the user
|
|
447
448
|
// code at module top level. Import declarations (preamble's and the
|
|
448
449
|
// user's) are hoisted, so __return is defined before the user's
|
|
@@ -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,19 +1,23 @@
|
|
|
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 {
|
|
5
6
|
command: string;
|
|
6
7
|
args?: string[];
|
|
7
8
|
cwd?: string;
|
|
9
|
+
combinedStdio?: boolean;
|
|
8
10
|
}
|
|
9
11
|
export declare class StdioSidecarProcess {
|
|
10
12
|
readonly child: ChildProcessWithoutNullStreams;
|
|
13
|
+
readonly control: Duplex | null;
|
|
14
|
+
readonly combinedStdio: boolean;
|
|
11
15
|
private readonly stderrChunks;
|
|
12
16
|
private readonly exitListeners;
|
|
13
17
|
private readonly errorListeners;
|
|
14
18
|
private constructor();
|
|
15
19
|
static spawn(options: StdioSidecarProcessSpawnOptions): StdioSidecarProcess;
|
|
16
|
-
static fromChild(child: ChildProcessWithoutNullStreams): StdioSidecarProcess;
|
|
20
|
+
static fromChild(child: ChildProcessWithoutNullStreams, control?: Duplex | null): StdioSidecarProcess;
|
|
17
21
|
onExit(handler: (error: SidecarProcessExited) => void): () => void;
|
|
18
22
|
onError(handler: (error: SidecarProcessError) => void): () => void;
|
|
19
23
|
stderrText(): string;
|
package/dist/process.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
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;
|
|
7
|
+
combinedStdio;
|
|
6
8
|
stderrChunks = [];
|
|
7
9
|
exitListeners = new Set();
|
|
8
10
|
errorListeners = new Set();
|
|
9
|
-
constructor(child) {
|
|
11
|
+
constructor(child, control) {
|
|
10
12
|
this.child = child;
|
|
13
|
+
this.control = control;
|
|
14
|
+
this.combinedStdio = control === null;
|
|
11
15
|
this.child.stderr.on("data", (chunk) => {
|
|
12
16
|
this.stderrChunks.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk));
|
|
13
17
|
});
|
|
@@ -30,13 +34,26 @@ export class StdioSidecarProcess {
|
|
|
30
34
|
});
|
|
31
35
|
}
|
|
32
36
|
static spawn(options) {
|
|
33
|
-
|
|
37
|
+
const combinedStdio = options.combinedStdio === true;
|
|
38
|
+
const child = spawn(options.command, options.args ?? [], {
|
|
34
39
|
cwd: options.cwd,
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
env: combinedStdio
|
|
41
|
+
? { ...process.env, AGENTOS_SIDECAR_COMBINED_STDIO: "1" }
|
|
42
|
+
: process.env,
|
|
43
|
+
stdio: combinedStdio
|
|
44
|
+
? ["pipe", "pipe", "pipe"]
|
|
45
|
+
: ["pipe", "pipe", "pipe", "pipe"],
|
|
46
|
+
});
|
|
47
|
+
try {
|
|
48
|
+
return new StdioSidecarProcess(child, combinedStdio ? null : requireControlStream(child));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
child.kill("SIGKILL");
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
37
54
|
}
|
|
38
|
-
static fromChild(child) {
|
|
39
|
-
return new StdioSidecarProcess(child);
|
|
55
|
+
static fromChild(child, control) {
|
|
56
|
+
return new StdioSidecarProcess(child, control === null ? null : (control ?? requireControlStream(child)));
|
|
40
57
|
}
|
|
41
58
|
onExit(handler) {
|
|
42
59
|
this.exitListeners.add(handler);
|
|
@@ -95,3 +112,13 @@ export class StdioSidecarProcess {
|
|
|
95
112
|
});
|
|
96
113
|
}
|
|
97
114
|
}
|
|
115
|
+
function requireControlStream(child) {
|
|
116
|
+
const stream = child.stdio[3];
|
|
117
|
+
if (!stream ||
|
|
118
|
+
typeof stream.write !== "function" ||
|
|
119
|
+
typeof stream.on !== "function" ||
|
|
120
|
+
typeof stream.read !== "function") {
|
|
121
|
+
throw new Error("sidecar process did not expose a full-duplex fd 3");
|
|
122
|
+
}
|
|
123
|
+
return stream;
|
|
124
|
+
}
|