@rivet-dev/agentos-core 0.0.0-integrate-dylib-into-main.815fcda → 0.0.0-main.2f99c77
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 +5 -8
- package/dist/agent-os.d.ts +176 -48
- package/dist/agent-os.js +829 -548
- package/dist/agentos-package.d.ts +29 -0
- package/dist/agentos-package.js +88 -0
- package/dist/base-filesystem.d.ts +2 -15
- package/dist/base-filesystem.js +23 -171
- package/dist/cron/types.d.ts +1 -1
- package/dist/default-software.d.ts +8 -0
- package/dist/default-software.js +10 -0
- package/dist/generated/AcpLimitsConfig.d.ts +4 -0
- package/dist/generated/AcpLimitsConfig.js +2 -0
- package/dist/generated/CreateVmConfig.d.ts +26 -0
- package/dist/generated/CreateVmConfig.js +1 -0
- package/dist/generated/FsPermissionRule.d.ts +6 -0
- package/dist/generated/FsPermissionRule.js +1 -0
- package/dist/generated/FsPermissionRuleSet.d.ts +6 -0
- package/dist/generated/FsPermissionRuleSet.js +1 -0
- package/dist/generated/FsPermissionScope.d.ts +3 -0
- package/dist/generated/FsPermissionScope.js +1 -0
- package/dist/generated/HttpLimitsConfig.d.ts +3 -0
- package/dist/generated/HttpLimitsConfig.js +2 -0
- package/dist/generated/JsModuleResolution.d.ts +1 -0
- package/dist/generated/JsModuleResolution.js +2 -0
- package/dist/generated/JsRuntimeConfig.d.ts +31 -0
- package/dist/generated/JsRuntimeConfig.js +1 -0
- package/dist/generated/JsRuntimeLimitsConfig.d.ts +11 -0
- package/dist/generated/JsRuntimeLimitsConfig.js +2 -0
- package/dist/generated/JsRuntimePlatform.d.ts +1 -0
- package/dist/generated/JsRuntimePlatform.js +2 -0
- package/dist/generated/MountPluginDescriptor.d.ts +4 -0
- package/dist/generated/MountPluginDescriptor.js +2 -0
- package/dist/generated/NativeRootFilesystemConfig.d.ts +5 -0
- package/dist/generated/NativeRootFilesystemConfig.js +1 -0
- package/dist/generated/PatternPermissionRule.d.ts +6 -0
- package/dist/generated/PatternPermissionRule.js +1 -0
- package/dist/generated/PatternPermissionRuleSet.d.ts +6 -0
- package/dist/generated/PatternPermissionRuleSet.js +1 -0
- package/dist/generated/PatternPermissionScope.d.ts +3 -0
- package/dist/generated/PatternPermissionScope.js +1 -0
- package/dist/generated/PermissionMode.d.ts +1 -0
- package/dist/generated/PermissionMode.js +2 -0
- package/dist/generated/PermissionsPolicy.d.ts +10 -0
- package/dist/generated/PermissionsPolicy.js +1 -0
- package/dist/generated/PluginLimitsConfig.d.ts +4 -0
- package/dist/generated/PluginLimitsConfig.js +2 -0
- package/dist/generated/PythonLimitsConfig.d.ts +6 -0
- package/dist/generated/PythonLimitsConfig.js +2 -0
- package/dist/generated/ResourceLimitsConfig.d.ts +24 -0
- package/dist/generated/ResourceLimitsConfig.js +2 -0
- package/dist/generated/RootFilesystemConfig.d.ts +9 -0
- package/dist/generated/RootFilesystemConfig.js +1 -0
- package/dist/generated/RootFilesystemEntry.d.ts +13 -0
- package/dist/generated/RootFilesystemEntry.js +1 -0
- package/dist/generated/RootFilesystemEntryEncoding.d.ts +1 -0
- package/dist/generated/RootFilesystemEntryEncoding.js +2 -0
- package/dist/generated/RootFilesystemEntryKind.d.ts +1 -0
- package/dist/generated/RootFilesystemEntryKind.js +2 -0
- package/dist/generated/RootFilesystemLowerDescriptor.d.ts +7 -0
- package/dist/generated/RootFilesystemLowerDescriptor.js +1 -0
- package/dist/generated/RootFilesystemMode.d.ts +1 -0
- package/dist/generated/RootFilesystemMode.js +2 -0
- package/dist/generated/ToolLimitsConfig.d.ts +10 -0
- package/dist/generated/ToolLimitsConfig.js +2 -0
- package/dist/generated/VmDnsConfig.d.ts +6 -0
- package/dist/generated/VmDnsConfig.js +2 -0
- package/dist/generated/VmLimitsConfig.d.ts +18 -0
- package/dist/generated/VmLimitsConfig.js +1 -0
- package/dist/generated/VmListenPolicyConfig.d.ts +5 -0
- package/dist/generated/VmListenPolicyConfig.js +2 -0
- package/dist/generated/WasmLimitsConfig.d.ts +7 -0
- package/dist/generated/WasmLimitsConfig.js +2 -0
- package/dist/host-dir-mount.d.ts +1 -1
- package/dist/host-tools-zod.js +27 -7
- package/dist/host-tools.js +9 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/layers.d.ts +2 -2
- package/dist/layers.js +19 -10
- package/dist/options-schema.d.ts +453 -0
- package/dist/options-schema.js +268 -0
- package/dist/packages.d.ts +17 -116
- package/dist/packages.js +35 -322
- package/dist/runtime-compat.d.ts +27 -11
- package/dist/runtime-compat.js +133 -147
- package/dist/runtime.d.ts +37 -7
- package/dist/sandbox.d.ts +112 -0
- package/dist/sandbox.js +264 -0
- package/dist/sidecar/agentos-protocol.d.ts +99 -1
- package/dist/sidecar/agentos-protocol.js +155 -18
- package/dist/sidecar/native-process-client.d.ts +6 -2
- package/dist/sidecar/native-process-client.js +8 -1
- package/dist/sidecar/permissions.d.ts +1 -1
- package/dist/sidecar/permissions.js +4 -4
- package/dist/sidecar/rpc-client.d.ts +86 -6
- package/dist/sidecar/rpc-client.js +388 -93
- package/dist/types.d.ts +10 -3
- package/package.json +40 -33
- package/dist/agents.d.ts +0 -63
- package/dist/agents.js +0 -42
- package/fixtures/AGENTOS_SYSTEM_PROMPT.md +0 -21
|
@@ -7,6 +7,30 @@ const PROTECTED_READ_ONLY_GUEST_ROOTS = ["/etc/agentos"];
|
|
|
7
7
|
const TRAILING_OUTPUT_DRAIN_INTERVAL_MS = 10;
|
|
8
8
|
const TRAILING_OUTPUT_DRAIN_MAX_MS = 250;
|
|
9
9
|
const TRAILING_OUTPUT_DRAIN_QUIET_TURNS = 2;
|
|
10
|
+
function shouldLogStructuredSidecarEvent(name) {
|
|
11
|
+
const normalized = name.toLowerCase();
|
|
12
|
+
return (normalized === "limit_warning" ||
|
|
13
|
+
normalized.startsWith("security.") ||
|
|
14
|
+
normalized.includes("warning") ||
|
|
15
|
+
normalized.includes("failed") ||
|
|
16
|
+
normalized.includes("error"));
|
|
17
|
+
}
|
|
18
|
+
function formatStructuredSidecarDetail(detail) {
|
|
19
|
+
const entries = Object.entries(detail);
|
|
20
|
+
if (entries.length === 0) {
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
return entries.map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(" ");
|
|
24
|
+
}
|
|
25
|
+
function logStructuredSidecarEvent(name, detail) {
|
|
26
|
+
if (!shouldLogStructuredSidecarEvent(name)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const formatted = formatStructuredSidecarDetail(detail);
|
|
30
|
+
console.warn(formatted
|
|
31
|
+
? `[agent-os] sidecar ${name}: ${formatted}`
|
|
32
|
+
: `[agent-os] sidecar ${name}`);
|
|
33
|
+
}
|
|
10
34
|
async function drainTrailingProcessOutputTurn(delayMs = 0) {
|
|
11
35
|
// Native-sidecar `process_output` events can lag one macrotask behind the
|
|
12
36
|
// terminal `process_exited` notification for very short-lived processes, and
|
|
@@ -142,7 +166,10 @@ function parseSimpleExecCommand(command) {
|
|
|
142
166
|
return tokens;
|
|
143
167
|
}
|
|
144
168
|
function canUseDirectExec(driver, commandName) {
|
|
145
|
-
return driver === "wasmvm" ||
|
|
169
|
+
return (driver === "wasmvm" ||
|
|
170
|
+
(driver === "node" && commandName === "node") ||
|
|
171
|
+
(driver === "python" &&
|
|
172
|
+
(commandName === "python" || commandName === "python3")));
|
|
146
173
|
}
|
|
147
174
|
function shellSingleQuote(value) {
|
|
148
175
|
if (value.length === 0) {
|
|
@@ -181,7 +208,17 @@ export class NativeSidecarKernelProxy {
|
|
|
181
208
|
client;
|
|
182
209
|
session;
|
|
183
210
|
vm;
|
|
211
|
+
ownsClient;
|
|
184
212
|
localMounts;
|
|
213
|
+
baseSidecarMounts;
|
|
214
|
+
permissions;
|
|
215
|
+
commandPermissions;
|
|
216
|
+
loopbackExemptPorts;
|
|
217
|
+
// Mutable: runtime `linkSoftware` appends via `registerLinkedPackage` so
|
|
218
|
+
// later mount reconfigures resend linked packages too, not just boot ones.
|
|
219
|
+
packages;
|
|
220
|
+
packagesMountAt;
|
|
221
|
+
toolShimCommands;
|
|
185
222
|
commandDrivers;
|
|
186
223
|
onWasmCommandResolved;
|
|
187
224
|
onDispose;
|
|
@@ -199,6 +236,7 @@ export class NativeSidecarKernelProxy {
|
|
|
199
236
|
zombieTimerCountRefresh = null;
|
|
200
237
|
disposed = false;
|
|
201
238
|
pumpError = null;
|
|
239
|
+
mountReconfigurePromise = null;
|
|
202
240
|
nextSyntheticPid = SYNTHETIC_PID_BASE;
|
|
203
241
|
eventPumpAbortController = new AbortController();
|
|
204
242
|
eventPump;
|
|
@@ -206,10 +244,20 @@ export class NativeSidecarKernelProxy {
|
|
|
206
244
|
this.client = options.client;
|
|
207
245
|
this.session = options.session;
|
|
208
246
|
this.vm = options.vm;
|
|
247
|
+
this.ownsClient = options.ownsClient ?? true;
|
|
209
248
|
this.env = { ...options.env };
|
|
210
249
|
this.cwd = options.cwd;
|
|
211
250
|
this.defaultExecCwd = options.defaultExecCwd;
|
|
212
251
|
this.localMounts = [...options.localMounts].sort((left, right) => right.path.length - left.path.length);
|
|
252
|
+
const localMountPaths = new Set(this.localMounts.map((mount) => mount.path));
|
|
253
|
+
this.baseSidecarMounts = options.sidecarMounts.filter((mount) => mount.plugin.id !== "js_bridge" ||
|
|
254
|
+
!localMountPaths.has(posixPath.normalize(mount.guestPath)));
|
|
255
|
+
this.permissions = options.permissions;
|
|
256
|
+
this.commandPermissions = options.commandPermissions;
|
|
257
|
+
this.loopbackExemptPorts = options.loopbackExemptPorts;
|
|
258
|
+
this.packages = options.packages ? [...options.packages] : [];
|
|
259
|
+
this.packagesMountAt = options.packagesMountAt;
|
|
260
|
+
this.toolShimCommands = options.toolShimCommands;
|
|
213
261
|
this.commandDrivers = buildCommandMap(options.commandGuestPaths);
|
|
214
262
|
this.onWasmCommandResolved = options.onWasmCommandResolved;
|
|
215
263
|
this.onDispose = options.onDispose;
|
|
@@ -233,12 +281,24 @@ export class NativeSidecarKernelProxy {
|
|
|
233
281
|
this.commandDrivers.set(name, "wasmvm");
|
|
234
282
|
}
|
|
235
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* Record a runtime-linked package (`linkSoftware`) so mount reconfigures
|
|
286
|
+
* resend it. Rust `configure_vm` rebuilds the whole VM configuration from
|
|
287
|
+
* each payload, so a linked package omitted here would be silently
|
|
288
|
+
* unprojected from `/opt/agentos` by the next `mountFs`/`unmountFs`.
|
|
289
|
+
*/
|
|
290
|
+
registerLinkedPackage(descriptor) {
|
|
291
|
+
if (!this.packages.some((pkg) => pkg.path === descriptor.path)) {
|
|
292
|
+
this.packages.push({ path: descriptor.path });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
236
295
|
async dispose() {
|
|
237
296
|
if (this.disposed) {
|
|
238
297
|
return;
|
|
239
298
|
}
|
|
240
299
|
this.disposed = true;
|
|
241
300
|
this.eventPumpAbortController.abort();
|
|
301
|
+
await this.mountReconfigurePromise?.catch(() => { });
|
|
242
302
|
const liveProcesses = [...this.trackedProcesses.values()].filter((entry) => entry.exitCode === null);
|
|
243
303
|
await Promise.allSettled(liveProcesses.map((entry) => this.signalProcess(entry, 15)));
|
|
244
304
|
await this.client.disposeVm(this.session, this.vm).catch(() => { });
|
|
@@ -251,9 +311,42 @@ export class NativeSidecarKernelProxy {
|
|
|
251
311
|
this.finishProcess(entry, 143);
|
|
252
312
|
}
|
|
253
313
|
}
|
|
254
|
-
|
|
314
|
+
// Only tear down the shared sidecar process when this proxy owns it. VMs
|
|
315
|
+
// leased from an `AgentOsSidecar` handle share one process, which is
|
|
316
|
+
// disposed when the handle is disposed.
|
|
317
|
+
if (this.ownsClient) {
|
|
318
|
+
await this.client.dispose().catch(() => { });
|
|
319
|
+
}
|
|
255
320
|
await this.eventPump.catch(() => { });
|
|
256
321
|
await this.onDispose?.().catch(() => { });
|
|
322
|
+
// Drop all per-VM tracking state so a disposed proxy retains nothing.
|
|
323
|
+
for (const entry of this.trackedProcesses.values()) {
|
|
324
|
+
entry.onStdout.clear();
|
|
325
|
+
entry.onStderr.clear();
|
|
326
|
+
}
|
|
327
|
+
this.trackedProcesses.clear();
|
|
328
|
+
this.trackedProcessesById.clear();
|
|
329
|
+
this.signalStates.clear();
|
|
330
|
+
this.signalRefreshes.clear();
|
|
331
|
+
this.localMounts.length = 0;
|
|
332
|
+
}
|
|
333
|
+
/** Test-only snapshot of the per-VM tracking collection sizes. */
|
|
334
|
+
__trackingSizesForTest() {
|
|
335
|
+
return {
|
|
336
|
+
trackedProcesses: this.trackedProcesses.size,
|
|
337
|
+
trackedProcessesById: this.trackedProcessesById.size,
|
|
338
|
+
signalStates: this.signalStates.size,
|
|
339
|
+
signalRefreshes: this.signalRefreshes.size,
|
|
340
|
+
localMounts: this.localMounts.length,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/** Test-only handle to a tracked entry so its listener Sets can be inspected. */
|
|
344
|
+
__trackedEntryForTest(pid) {
|
|
345
|
+
return this.trackedProcesses.get(pid);
|
|
346
|
+
}
|
|
347
|
+
/** Test-only join point for in-flight signal-state refreshes. */
|
|
348
|
+
async __awaitSignalRefreshesForTest() {
|
|
349
|
+
await Promise.allSettled([...this.signalRefreshes.values()]);
|
|
257
350
|
}
|
|
258
351
|
async exec(command, options) {
|
|
259
352
|
if (!this.commands.has("sh")) {
|
|
@@ -263,9 +356,6 @@ export class NativeSidecarKernelProxy {
|
|
|
263
356
|
const stderrChunks = [];
|
|
264
357
|
const effectiveCwd = options?.cwd ?? this.defaultExecCwd ?? this.cwd;
|
|
265
358
|
const parsedCommand = parseSimpleExecCommand(command);
|
|
266
|
-
const resolveExecPath = (targetPath) => targetPath.startsWith("/")
|
|
267
|
-
? posixPath.normalize(targetPath)
|
|
268
|
-
: posixPath.normalize(posixPath.join(effectiveCwd, targetPath));
|
|
269
359
|
const runAndCapture = async (proc, stdinOverride, readExitCode) => {
|
|
270
360
|
if (stdinOverride !== undefined) {
|
|
271
361
|
proc.writeStdin(stdinOverride);
|
|
@@ -301,43 +391,6 @@ export class NativeSidecarKernelProxy {
|
|
|
301
391
|
stderr: Buffer.concat(stderrChunks.map((chunk) => Buffer.from(chunk))).toString("utf8"),
|
|
302
392
|
};
|
|
303
393
|
};
|
|
304
|
-
if (parsedCommand &&
|
|
305
|
-
(parsedCommand[0] === "sh" || parsedCommand[0] === "/bin/sh") &&
|
|
306
|
-
parsedCommand[1] === "-c" &&
|
|
307
|
-
parsedCommand.length === 3) {
|
|
308
|
-
const shellScript = parsedCommand[2].trim();
|
|
309
|
-
const exitMatch = shellScript.match(/^exit(?:\s+(-?\d+))?$/);
|
|
310
|
-
if (exitMatch) {
|
|
311
|
-
return {
|
|
312
|
-
exitCode: Number.parseInt(exitMatch[1] ?? "0", 10),
|
|
313
|
-
stdout: "",
|
|
314
|
-
stderr: "",
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
return this.exec(parsedCommand[2], options);
|
|
318
|
-
}
|
|
319
|
-
if (parsedCommand &&
|
|
320
|
-
parsedCommand[0] === "chmod" &&
|
|
321
|
-
parsedCommand.length >= 3 &&
|
|
322
|
-
/^[0-7]{3,4}$/.test(parsedCommand[1] ?? "")) {
|
|
323
|
-
const mode = Number.parseInt(parsedCommand[1], 8);
|
|
324
|
-
for (const target of parsedCommand.slice(2)) {
|
|
325
|
-
await this.client.chmod(this.session, this.vm, resolveExecPath(target), mode);
|
|
326
|
-
}
|
|
327
|
-
return { exitCode: 0, stdout: "", stderr: "" };
|
|
328
|
-
}
|
|
329
|
-
if (parsedCommand &&
|
|
330
|
-
parsedCommand[0] === "stat" &&
|
|
331
|
-
parsedCommand.length === 4 &&
|
|
332
|
-
parsedCommand[1] === "-c" &&
|
|
333
|
-
parsedCommand[2] === "%a") {
|
|
334
|
-
const stat = await this.stat(resolveExecPath(parsedCommand[3]));
|
|
335
|
-
return {
|
|
336
|
-
exitCode: 0,
|
|
337
|
-
stdout: `${(stat.mode & 0o777).toString(8)}\n`,
|
|
338
|
-
stderr: "",
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
394
|
const parsedCommandDriver = parsedCommand
|
|
342
395
|
? this.commands.get(parsedCommand[0])
|
|
343
396
|
: undefined;
|
|
@@ -447,11 +500,15 @@ export class NativeSidecarKernelProxy {
|
|
|
447
500
|
processId,
|
|
448
501
|
command: spawnCommand,
|
|
449
502
|
args: spawnArgs,
|
|
450
|
-
driver: spawnCommand === "node"
|
|
503
|
+
driver: spawnCommand === "node"
|
|
504
|
+
? "node"
|
|
505
|
+
: spawnCommand === "python" || spawnCommand === "python3"
|
|
506
|
+
? "python"
|
|
507
|
+
: "wasmvm",
|
|
451
508
|
cwd: options?.cwd ?? this.cwd,
|
|
452
509
|
env: {
|
|
453
510
|
...(options?.env ?? {}),
|
|
454
|
-
...(options?.streamStdin ? {
|
|
511
|
+
...(options?.streamStdin ? { AGENTOS_KEEP_STDIN_OPEN: "1" } : {}),
|
|
455
512
|
},
|
|
456
513
|
startTime: Date.now(),
|
|
457
514
|
exitTime: null,
|
|
@@ -479,16 +536,16 @@ export class NativeSidecarKernelProxy {
|
|
|
479
536
|
pid,
|
|
480
537
|
writeStdin: (data) => {
|
|
481
538
|
if (entry.exitCode !== null) {
|
|
482
|
-
return;
|
|
539
|
+
return Promise.resolve();
|
|
483
540
|
}
|
|
484
541
|
entry.pendingStdin.push(data);
|
|
485
|
-
|
|
542
|
+
return this.flushPendingStdin(entry).catch((error) => {
|
|
486
543
|
this.handleBackgroundProcessError(entry, error);
|
|
487
544
|
});
|
|
488
545
|
},
|
|
489
546
|
closeStdin: () => {
|
|
490
547
|
entry.pendingCloseStdin = true;
|
|
491
|
-
|
|
548
|
+
return this.closeTrackedStdin(entry).catch((error) => {
|
|
492
549
|
this.handleBackgroundProcessError(entry, error);
|
|
493
550
|
});
|
|
494
551
|
},
|
|
@@ -532,7 +589,6 @@ export class NativeSidecarKernelProxy {
|
|
|
532
589
|
const args = options?.args ??
|
|
533
590
|
(command === "sh" || command === "/bin/sh" ? ["-i"] : []);
|
|
534
591
|
const synthesizePrompt = !options?.command && !options?.args;
|
|
535
|
-
const autoCloseExplicitCommandStdin = Boolean(options?.command) && !["sh", "/bin/sh", "bash"].includes(command);
|
|
536
592
|
const promptText = "sh-0.4$ ";
|
|
537
593
|
const textEncoder = new TextEncoder();
|
|
538
594
|
const textDecoder = new TextDecoder();
|
|
@@ -542,14 +598,18 @@ export class NativeSidecarKernelProxy {
|
|
|
542
598
|
.replace(/\u001b\[[0-9;]*m/g, "")
|
|
543
599
|
.replace(/^.*WARN could not retrieve pid for child process\n?/gm, "")
|
|
544
600
|
.replace(/^ProcessExitError:.*\n(?:\s+at .*\n)*/gm, "");
|
|
601
|
+
const sanitizeNativeShellOutput = (chunk) => {
|
|
602
|
+
const text = textDecoder.decode(chunk);
|
|
603
|
+
const sanitized = text.replace(/^.*WARN could not retrieve pid for child process\n?/gm, "");
|
|
604
|
+
return sanitized.length > 0 ? textEncoder.encode(sanitized) : null;
|
|
605
|
+
};
|
|
545
606
|
let bufferedInput = "";
|
|
546
607
|
let bufferedCommand = "";
|
|
547
608
|
let activeForegroundProcess = null;
|
|
548
|
-
let shellEnv = { ...(options?.env ?? {}) };
|
|
609
|
+
let shellEnv = { ...(options?.env ?? {}), AGENTOS_EXEC_TTY: "1" };
|
|
549
610
|
let shellCwd = options?.cwd ?? this.cwd;
|
|
550
611
|
let syntheticCommandQueue = Promise.resolve();
|
|
551
612
|
let promptTimer = null;
|
|
552
|
-
let closeStdinTimer = null;
|
|
553
613
|
let commandInFlight = false;
|
|
554
614
|
let syntheticCursorAtLineStart = true;
|
|
555
615
|
const syntheticPid = this.nextSyntheticPid++;
|
|
@@ -564,12 +624,6 @@ export class NativeSidecarKernelProxy {
|
|
|
564
624
|
promptTimer = null;
|
|
565
625
|
}
|
|
566
626
|
};
|
|
567
|
-
const clearCloseStdinTimer = () => {
|
|
568
|
-
if (closeStdinTimer !== null) {
|
|
569
|
-
clearTimeout(closeStdinTimer);
|
|
570
|
-
closeStdinTimer = null;
|
|
571
|
-
}
|
|
572
|
-
};
|
|
573
627
|
const normalizeSyntheticTerminalText = (text) => text.replace(/\r?\n/g, "\r\n");
|
|
574
628
|
const updateSyntheticCursor = (text) => {
|
|
575
629
|
if (!text) {
|
|
@@ -665,15 +719,36 @@ export class NativeSidecarKernelProxy {
|
|
|
665
719
|
}
|
|
666
720
|
return parsed;
|
|
667
721
|
};
|
|
668
|
-
const writeForegroundInput = (proc, data) => {
|
|
722
|
+
const writeForegroundInput = async (proc, data) => {
|
|
669
723
|
if (typeof data === "string") {
|
|
670
724
|
for (const character of data) {
|
|
671
|
-
proc.writeStdin(character);
|
|
725
|
+
await proc.writeStdin(character);
|
|
672
726
|
}
|
|
673
727
|
return;
|
|
674
728
|
}
|
|
675
729
|
for (const byte of data) {
|
|
676
|
-
proc.writeStdin(new Uint8Array([byte]));
|
|
730
|
+
await proc.writeStdin(new Uint8Array([byte]));
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
const appendSyntheticInput = (input) => {
|
|
734
|
+
for (const character of input) {
|
|
735
|
+
if (character === "\u0004") {
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
if (character === "\b" || character === "\u007f") {
|
|
739
|
+
if (bufferedInput.length > 0) {
|
|
740
|
+
bufferedInput = bufferedInput.slice(0, -1);
|
|
741
|
+
emitSyntheticTerminal("\b \b");
|
|
742
|
+
}
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
bufferedInput += character;
|
|
746
|
+
if (character === "\n") {
|
|
747
|
+
emitSyntheticTerminal("\n");
|
|
748
|
+
}
|
|
749
|
+
else if (character >= " ") {
|
|
750
|
+
emitSyntheticTerminal(character);
|
|
751
|
+
}
|
|
677
752
|
}
|
|
678
753
|
};
|
|
679
754
|
let onData = null;
|
|
@@ -685,7 +760,7 @@ export class NativeSidecarKernelProxy {
|
|
|
685
760
|
schedulePrompt(0);
|
|
686
761
|
return {
|
|
687
762
|
pid: syntheticPid,
|
|
688
|
-
write(data) {
|
|
763
|
+
async write(data) {
|
|
689
764
|
if (syntheticExitCode !== null) {
|
|
690
765
|
return;
|
|
691
766
|
}
|
|
@@ -696,13 +771,13 @@ export class NativeSidecarKernelProxy {
|
|
|
696
771
|
if (rawText.includes("\u0003")) {
|
|
697
772
|
const [beforeInterrupt] = rawText.split("\u0003");
|
|
698
773
|
if (beforeInterrupt) {
|
|
699
|
-
writeForegroundInput(activeForegroundProcess, beforeInterrupt);
|
|
774
|
+
await writeForegroundInput(activeForegroundProcess, beforeInterrupt);
|
|
700
775
|
}
|
|
701
776
|
emitSyntheticTerminal("^C\n");
|
|
702
777
|
activeForegroundProcess.kill(2);
|
|
703
778
|
return;
|
|
704
779
|
}
|
|
705
|
-
writeForegroundInput(activeForegroundProcess, data);
|
|
780
|
+
await writeForegroundInput(activeForegroundProcess, data);
|
|
706
781
|
return;
|
|
707
782
|
}
|
|
708
783
|
const rawText = typeof data === "string"
|
|
@@ -725,7 +800,7 @@ export class NativeSidecarKernelProxy {
|
|
|
725
800
|
finishSyntheticShell(0);
|
|
726
801
|
return;
|
|
727
802
|
}
|
|
728
|
-
|
|
803
|
+
appendSyntheticInput(text.replace(/\r\n/g, "\n").replace(/\r/g, "\n"));
|
|
729
804
|
while (true) {
|
|
730
805
|
const newlineIndex = bufferedInput.indexOf("\n");
|
|
731
806
|
if (newlineIndex < 0) {
|
|
@@ -735,7 +810,6 @@ export class NativeSidecarKernelProxy {
|
|
|
735
810
|
.slice(0, newlineIndex)
|
|
736
811
|
.replace(/\r$/, "");
|
|
737
812
|
bufferedInput = bufferedInput.slice(newlineIndex + 1);
|
|
738
|
-
emitSyntheticStdout(`${line}\n`);
|
|
739
813
|
const nextCommand = bufferedCommand
|
|
740
814
|
? `${bufferedCommand}\n${line}`
|
|
741
815
|
: line;
|
|
@@ -834,20 +908,33 @@ export class NativeSidecarKernelProxy {
|
|
|
834
908
|
};
|
|
835
909
|
}
|
|
836
910
|
const proc = this.spawn(command, args, {
|
|
837
|
-
env:
|
|
911
|
+
env: {
|
|
912
|
+
...(options?.env ?? {}),
|
|
913
|
+
...(options?.cols ? { COLUMNS: String(Math.trunc(options.cols)) } : {}),
|
|
914
|
+
...(options?.rows ? { LINES: String(Math.trunc(options.rows)) } : {}),
|
|
915
|
+
AGENTOS_EXEC_TTY: "1",
|
|
916
|
+
},
|
|
838
917
|
cwd: options?.cwd,
|
|
839
918
|
streamStdin: true,
|
|
840
919
|
onStdout: (chunk) => {
|
|
920
|
+
const sanitized = sanitizeNativeShellOutput(chunk);
|
|
921
|
+
if (!sanitized) {
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
841
924
|
for (const handler of stdoutHandlers) {
|
|
842
|
-
handler(
|
|
925
|
+
handler(sanitized);
|
|
843
926
|
}
|
|
844
927
|
if (commandInFlight) {
|
|
845
928
|
schedulePrompt(120);
|
|
846
929
|
}
|
|
847
930
|
},
|
|
848
931
|
onStderr: (chunk) => {
|
|
932
|
+
const sanitized = sanitizeNativeShellOutput(chunk);
|
|
933
|
+
if (!sanitized) {
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
849
936
|
for (const handler of stderrHandlers) {
|
|
850
|
-
handler(
|
|
937
|
+
handler(sanitized);
|
|
851
938
|
}
|
|
852
939
|
if (commandInFlight) {
|
|
853
940
|
schedulePrompt(120);
|
|
@@ -856,18 +943,11 @@ export class NativeSidecarKernelProxy {
|
|
|
856
943
|
});
|
|
857
944
|
return {
|
|
858
945
|
pid: proc.pid,
|
|
859
|
-
write(data) {
|
|
946
|
+
async write(data) {
|
|
860
947
|
if (synthesizePrompt) {
|
|
861
948
|
return;
|
|
862
949
|
}
|
|
863
|
-
proc.writeStdin(data);
|
|
864
|
-
if (autoCloseExplicitCommandStdin) {
|
|
865
|
-
clearCloseStdinTimer();
|
|
866
|
-
closeStdinTimer = setTimeout(() => {
|
|
867
|
-
closeStdinTimer = null;
|
|
868
|
-
proc.closeStdin();
|
|
869
|
-
}, 100);
|
|
870
|
-
}
|
|
950
|
+
await proc.writeStdin(data);
|
|
871
951
|
if (synthesizePrompt &&
|
|
872
952
|
typeof data === "string" &&
|
|
873
953
|
(data.includes("\n") || data.includes("\r"))) {
|
|
@@ -881,11 +961,18 @@ export class NativeSidecarKernelProxy {
|
|
|
881
961
|
set onData(handler) {
|
|
882
962
|
onData = handler;
|
|
883
963
|
},
|
|
884
|
-
resize() {
|
|
885
|
-
|
|
964
|
+
resize: (cols, rows) => {
|
|
965
|
+
const entry = this.trackedProcesses.get(proc.pid);
|
|
966
|
+
if (!entry || entry.exitCode !== null) {
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
void entry.startPromise
|
|
970
|
+
.then(() => this.client.resizePty(this.session, this.vm, entry.processId, Math.trunc(cols), Math.trunc(rows)))
|
|
971
|
+
.catch((error) => {
|
|
972
|
+
this.handleBackgroundProcessError(entry, error);
|
|
973
|
+
});
|
|
886
974
|
},
|
|
887
975
|
kill(signal) {
|
|
888
|
-
clearCloseStdinTimer();
|
|
889
976
|
clearPromptTimer();
|
|
890
977
|
proc.kill(signal);
|
|
891
978
|
},
|
|
@@ -985,12 +1072,41 @@ export class NativeSidecarKernelProxy {
|
|
|
985
1072
|
const entries = await this.client.readdir(this.session, this.vm, path);
|
|
986
1073
|
return [...new Set([...entries, ...this.mountedChildNames(path)])].sort((a, b) => a.localeCompare(b));
|
|
987
1074
|
}
|
|
1075
|
+
async readdirRecursive(path, options) {
|
|
1076
|
+
const local = this.resolveLocalMount(path);
|
|
1077
|
+
if (local) {
|
|
1078
|
+
return this.readdirRecursiveLocal(local.mount.fs, local.relativePath, options?.maxDepth);
|
|
1079
|
+
}
|
|
1080
|
+
return (await this.client.readdirRecursive(this.session, this.vm, path, options)).map((entry) => ({ ...entry, size: Number(entry.size) }));
|
|
1081
|
+
}
|
|
988
1082
|
async removeFile(path) {
|
|
989
1083
|
return this.dispatchWrite(path, (mount, relativePath) => mount.fs.removeFile(relativePath), () => this.client.removeFile(this.session, this.vm, path));
|
|
990
1084
|
}
|
|
991
1085
|
async removeDir(path) {
|
|
992
1086
|
return this.dispatchWrite(path, (mount, relativePath) => mount.fs.removeDir(relativePath), () => this.client.removeDir(this.session, this.vm, path));
|
|
993
1087
|
}
|
|
1088
|
+
async removePath(path, options) {
|
|
1089
|
+
return this.dispatchWrite(path, (mount, relativePath) => this.removePathLocal(mount.fs, relativePath, options?.recursive ?? false), () => this.client.removePath(this.session, this.vm, path, {
|
|
1090
|
+
recursive: options?.recursive ?? false,
|
|
1091
|
+
}));
|
|
1092
|
+
}
|
|
1093
|
+
async copyPath(fromPath, toPath, options) {
|
|
1094
|
+
const from = this.resolveLocalMount(fromPath);
|
|
1095
|
+
const to = this.resolveLocalMount(toPath);
|
|
1096
|
+
if (!!from !== !!to) {
|
|
1097
|
+
throw errnoError("EXDEV", "cross-device link not permitted");
|
|
1098
|
+
}
|
|
1099
|
+
if (from && to) {
|
|
1100
|
+
if (from.mount.path !== to.mount.path) {
|
|
1101
|
+
throw errnoError("EXDEV", "cross-device link not permitted");
|
|
1102
|
+
}
|
|
1103
|
+
this.assertLocalWritable(to.mount);
|
|
1104
|
+
return this.copyPathLocal(from.mount.fs, from.relativePath, to.relativePath, options?.recursive ?? false);
|
|
1105
|
+
}
|
|
1106
|
+
return this.client.copyPath(this.session, this.vm, fromPath, toPath, {
|
|
1107
|
+
recursive: options?.recursive ?? false,
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
994
1110
|
async rename(oldPath, newPath) {
|
|
995
1111
|
const from = this.resolveLocalMount(oldPath);
|
|
996
1112
|
const to = this.resolveLocalMount(newPath);
|
|
@@ -1006,19 +1122,93 @@ export class NativeSidecarKernelProxy {
|
|
|
1006
1122
|
}
|
|
1007
1123
|
return this.client.rename(this.session, this.vm, oldPath, newPath);
|
|
1008
1124
|
}
|
|
1125
|
+
async movePath(oldPath, newPath) {
|
|
1126
|
+
const from = this.resolveLocalMount(oldPath);
|
|
1127
|
+
const to = this.resolveLocalMount(newPath);
|
|
1128
|
+
if (!!from !== !!to) {
|
|
1129
|
+
throw errnoError("EXDEV", "cross-device link not permitted");
|
|
1130
|
+
}
|
|
1131
|
+
if (from && to) {
|
|
1132
|
+
if (from.mount.path !== to.mount.path) {
|
|
1133
|
+
throw errnoError("EXDEV", "cross-device link not permitted");
|
|
1134
|
+
}
|
|
1135
|
+
this.assertLocalWritable(from.mount);
|
|
1136
|
+
return from.mount.fs.rename(from.relativePath, to.relativePath);
|
|
1137
|
+
}
|
|
1138
|
+
return this.client.movePath(this.session, this.vm, oldPath, newPath);
|
|
1139
|
+
}
|
|
1009
1140
|
mountFs(path, driver, options) {
|
|
1010
1141
|
this.localMounts.unshift({
|
|
1011
1142
|
path: posixPath.normalize(path),
|
|
1012
1143
|
fs: driver,
|
|
1013
1144
|
readOnly: options?.readOnly ?? false,
|
|
1145
|
+
sidecarMount: options?.sidecarMount,
|
|
1014
1146
|
});
|
|
1015
1147
|
this.localMounts.sort((left, right) => right.path.length - left.path.length);
|
|
1148
|
+
// Resolves once the sidecar has the mount; a swallowed rejection here
|
|
1149
|
+
// used to turn reconfigure failures into silently missing guest mounts.
|
|
1150
|
+
// The local catch only guards callers that drop the promise — awaiting
|
|
1151
|
+
// callers still observe the rejection.
|
|
1152
|
+
const applied = this.reconfigureSidecarMounts();
|
|
1153
|
+
applied.catch(() => { });
|
|
1154
|
+
return applied;
|
|
1016
1155
|
}
|
|
1017
1156
|
unmountFs(path) {
|
|
1018
1157
|
const normalized = posixPath.normalize(path);
|
|
1019
1158
|
const index = this.localMounts.findIndex((mount) => mount.path === normalized);
|
|
1020
|
-
if (index
|
|
1021
|
-
|
|
1159
|
+
if (index < 0) {
|
|
1160
|
+
return Promise.resolve();
|
|
1161
|
+
}
|
|
1162
|
+
this.localMounts.splice(index, 1);
|
|
1163
|
+
const applied = this.reconfigureSidecarMounts();
|
|
1164
|
+
applied.catch(() => { });
|
|
1165
|
+
return applied;
|
|
1166
|
+
}
|
|
1167
|
+
desiredSidecarMounts() {
|
|
1168
|
+
return [
|
|
1169
|
+
...this.baseSidecarMounts,
|
|
1170
|
+
...this.localMounts.map((mount) => mount.sidecarMount ?? {
|
|
1171
|
+
guestPath: mount.path,
|
|
1172
|
+
readOnly: mount.readOnly,
|
|
1173
|
+
plugin: {
|
|
1174
|
+
id: "js_bridge",
|
|
1175
|
+
config: {},
|
|
1176
|
+
},
|
|
1177
|
+
}),
|
|
1178
|
+
];
|
|
1179
|
+
}
|
|
1180
|
+
reconfigureSidecarMounts() {
|
|
1181
|
+
const run = async () => {
|
|
1182
|
+
if (this.disposed) {
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
// Rust `configure_vm` rebuilds the whole VM configuration from this
|
|
1186
|
+
// payload, so resend the boot packages / tool shim commands too —
|
|
1187
|
+
// omitting them here strips the `/opt/agentos` projections and tool
|
|
1188
|
+
// shims from the VM as a side effect of a runtime mount change.
|
|
1189
|
+
await this.client.configureVm(this.session, this.vm, {
|
|
1190
|
+
mounts: this.desiredSidecarMounts(),
|
|
1191
|
+
permissions: this.permissions,
|
|
1192
|
+
commandPermissions: this.commandPermissions,
|
|
1193
|
+
loopbackExemptPorts: this.loopbackExemptPorts,
|
|
1194
|
+
packages: this.packages,
|
|
1195
|
+
packagesMountAt: this.packagesMountAt,
|
|
1196
|
+
toolShimCommands: this.toolShimCommands,
|
|
1197
|
+
});
|
|
1198
|
+
};
|
|
1199
|
+
const previous = this.mountReconfigurePromise ?? Promise.resolve();
|
|
1200
|
+
const next = previous.then(run, run);
|
|
1201
|
+
const tracked = next.finally(() => {
|
|
1202
|
+
if (this.mountReconfigurePromise === tracked) {
|
|
1203
|
+
this.mountReconfigurePromise = null;
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
this.mountReconfigurePromise = tracked;
|
|
1207
|
+
return tracked;
|
|
1208
|
+
}
|
|
1209
|
+
async waitForMountReconfigure() {
|
|
1210
|
+
if (this.mountReconfigurePromise) {
|
|
1211
|
+
await this.mountReconfigurePromise;
|
|
1022
1212
|
}
|
|
1023
1213
|
}
|
|
1024
1214
|
snapshotProcesses() {
|
|
@@ -1132,6 +1322,7 @@ export class NativeSidecarKernelProxy {
|
|
|
1132
1322
|
}
|
|
1133
1323
|
}
|
|
1134
1324
|
async startTrackedProcess(entry) {
|
|
1325
|
+
await this.waitForMountReconfigure();
|
|
1135
1326
|
const started = await this.client.execute(this.session, this.vm, {
|
|
1136
1327
|
processId: entry.processId,
|
|
1137
1328
|
command: entry.command,
|
|
@@ -1157,11 +1348,13 @@ export class NativeSidecarKernelProxy {
|
|
|
1157
1348
|
}
|
|
1158
1349
|
}
|
|
1159
1350
|
async runEventPump() {
|
|
1351
|
+
// Scope the pump to THIS VM's ownership so multiple proxies can share one
|
|
1352
|
+
// sidecar process: events for other VMs stay buffered for their own pumps
|
|
1353
|
+
// rather than being consumed (and dropped) here.
|
|
1354
|
+
const vmId = this.vm.vmId;
|
|
1160
1355
|
while (!this.disposed) {
|
|
1161
1356
|
try {
|
|
1162
|
-
const event = await this.client.waitForEvent(
|
|
1163
|
-
signal: this.eventPumpAbortController.signal,
|
|
1164
|
-
});
|
|
1357
|
+
const event = await this.client.waitForEvent((frame) => frame.ownership.scope === "vm" && frame.ownership.vm_id === vmId, undefined, { signal: this.eventPumpAbortController.signal });
|
|
1165
1358
|
if (event.payload.type === "process_output") {
|
|
1166
1359
|
const entry = this.trackedProcessesById.get(event.payload.process_id);
|
|
1167
1360
|
if (!entry) {
|
|
@@ -1188,8 +1381,11 @@ export class NativeSidecarKernelProxy {
|
|
|
1188
1381
|
continue;
|
|
1189
1382
|
}
|
|
1190
1383
|
void this.refreshProcessSnapshot().catch(() => { });
|
|
1191
|
-
this.signalRefreshes.delete(entry.pid);
|
|
1192
1384
|
this.finishProcess(entry, event.payload.exit_code);
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
if (event.payload.type === "structured") {
|
|
1388
|
+
logStructuredSidecarEvent(event.payload.name, event.payload.detail);
|
|
1193
1389
|
}
|
|
1194
1390
|
}
|
|
1195
1391
|
catch (error) {
|
|
@@ -1220,6 +1416,25 @@ export class NativeSidecarKernelProxy {
|
|
|
1220
1416
|
entry.exitTime = Date.now();
|
|
1221
1417
|
this.updateTrackedProcessSnapshot(entry);
|
|
1222
1418
|
entry.resolveWait(exitCode);
|
|
1419
|
+
// Release per-process tracking now that the process has terminated so these
|
|
1420
|
+
// maps/Sets don't grow without bound. Defer the release until trailing
|
|
1421
|
+
// output has drained: `wait()`'s drain and late `process_output` events
|
|
1422
|
+
// still need the entry + its listeners during the drain window. The exited
|
|
1423
|
+
// record lives on in `processes` for listing.
|
|
1424
|
+
void this.releaseProcessTrackingAfterDrain(entry);
|
|
1425
|
+
}
|
|
1426
|
+
async releaseProcessTrackingAfterDrain(entry) {
|
|
1427
|
+
try {
|
|
1428
|
+
await this.drainTrailingProcessOutput(entry);
|
|
1429
|
+
}
|
|
1430
|
+
finally {
|
|
1431
|
+
this.trackedProcesses.delete(entry.pid);
|
|
1432
|
+
this.trackedProcessesById.delete(entry.processId);
|
|
1433
|
+
this.signalRefreshes.delete(entry.pid);
|
|
1434
|
+
this.signalStates.delete(entry.pid);
|
|
1435
|
+
entry.onStdout.clear();
|
|
1436
|
+
entry.onStderr.clear();
|
|
1437
|
+
}
|
|
1223
1438
|
}
|
|
1224
1439
|
waitForTrackedProcess(entry) {
|
|
1225
1440
|
if (entry.exitCode !== null) {
|
|
@@ -1370,6 +1585,74 @@ export class NativeSidecarKernelProxy {
|
|
|
1370
1585
|
handler(stderr);
|
|
1371
1586
|
}
|
|
1372
1587
|
}
|
|
1588
|
+
async readdirRecursiveLocal(fs, path, maxDepth) {
|
|
1589
|
+
const results = [];
|
|
1590
|
+
const queue = [{ path, depth: 0 }];
|
|
1591
|
+
while (queue.length > 0) {
|
|
1592
|
+
const current = queue.shift();
|
|
1593
|
+
if (!current)
|
|
1594
|
+
break;
|
|
1595
|
+
for (const name of await fs.readDir(current.path)) {
|
|
1596
|
+
if (name === "." || name === "..")
|
|
1597
|
+
continue;
|
|
1598
|
+
const child = posixPath.join(current.path, name);
|
|
1599
|
+
const stat = await fs.lstat(child);
|
|
1600
|
+
results.push({
|
|
1601
|
+
name,
|
|
1602
|
+
path: child,
|
|
1603
|
+
isDirectory: stat.isDirectory,
|
|
1604
|
+
isSymbolicLink: stat.isSymbolicLink,
|
|
1605
|
+
size: stat.size,
|
|
1606
|
+
});
|
|
1607
|
+
if (stat.isDirectory &&
|
|
1608
|
+
!stat.isSymbolicLink &&
|
|
1609
|
+
(maxDepth === undefined || current.depth < maxDepth)) {
|
|
1610
|
+
queue.push({ path: child, depth: current.depth + 1 });
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
return results;
|
|
1615
|
+
}
|
|
1616
|
+
async removePathLocal(fs, path, recursive) {
|
|
1617
|
+
const stat = await fs.lstat(path);
|
|
1618
|
+
if (stat.isDirectory && !stat.isSymbolicLink) {
|
|
1619
|
+
if (recursive) {
|
|
1620
|
+
for (const name of await fs.readDir(path)) {
|
|
1621
|
+
if (name === "." || name === "..")
|
|
1622
|
+
continue;
|
|
1623
|
+
await this.removePathLocal(fs, posixPath.join(path, name), true);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
return fs.removeDir(path);
|
|
1627
|
+
}
|
|
1628
|
+
return fs.removeFile(path);
|
|
1629
|
+
}
|
|
1630
|
+
async copyPathLocal(fs, fromPath, toPath, recursive) {
|
|
1631
|
+
const stat = await fs.lstat(fromPath);
|
|
1632
|
+
if (stat.isSymbolicLink) {
|
|
1633
|
+
return fs.symlink(await fs.readlink(fromPath), toPath);
|
|
1634
|
+
}
|
|
1635
|
+
if (stat.isDirectory) {
|
|
1636
|
+
if (!recursive) {
|
|
1637
|
+
throw errnoError("EISDIR", "illegal operation on a directory");
|
|
1638
|
+
}
|
|
1639
|
+
await fs.mkdir(posixPath.dirname(toPath), { recursive: true });
|
|
1640
|
+
if (!(await fs.exists(toPath))) {
|
|
1641
|
+
await fs.createDir(toPath);
|
|
1642
|
+
}
|
|
1643
|
+
await fs.chmod(toPath, stat.mode);
|
|
1644
|
+
await fs.chown(toPath, stat.uid, stat.gid);
|
|
1645
|
+
for (const name of await fs.readDir(fromPath)) {
|
|
1646
|
+
if (name === "." || name === "..")
|
|
1647
|
+
continue;
|
|
1648
|
+
await this.copyPathLocal(fs, posixPath.join(fromPath, name), posixPath.join(toPath, name), true);
|
|
1649
|
+
}
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
await fs.writeFile(toPath, await fs.readFile(fromPath));
|
|
1653
|
+
await fs.chmod(toPath, stat.mode);
|
|
1654
|
+
await fs.chown(toPath, stat.uid, stat.gid);
|
|
1655
|
+
}
|
|
1373
1656
|
createFilesystemView(includeLocalMounts) {
|
|
1374
1657
|
return {
|
|
1375
1658
|
readFile: (path) => this.dispatchRead(path, (mount, relativePath) => mount.fs.readFile(relativePath), includeLocalMounts),
|
|
@@ -1580,7 +1863,8 @@ export class NativeSidecarKernelProxy {
|
|
|
1580
1863
|
}
|
|
1581
1864
|
return this.dispatchNativeRead(path);
|
|
1582
1865
|
}
|
|
1583
|
-
dispatchNativeRead(path) {
|
|
1866
|
+
async dispatchNativeRead(path) {
|
|
1867
|
+
await this.waitForMountReconfigure();
|
|
1584
1868
|
return this.client.readFile(this.session, this.vm, path);
|
|
1585
1869
|
}
|
|
1586
1870
|
async dispatchWrite(path, handler, nativeHandler, includeLocalMounts = true) {
|
|
@@ -1591,6 +1875,7 @@ export class NativeSidecarKernelProxy {
|
|
|
1591
1875
|
await handler(local.mount, local.relativePath);
|
|
1592
1876
|
return;
|
|
1593
1877
|
}
|
|
1878
|
+
await this.waitForMountReconfigure();
|
|
1594
1879
|
await nativeHandler();
|
|
1595
1880
|
}
|
|
1596
1881
|
resolveLocalMount(path) {
|
|
@@ -1666,6 +1951,10 @@ function buildCommandMap(commandGuestPaths) {
|
|
|
1666
1951
|
["node", "node"],
|
|
1667
1952
|
["npm", "node"],
|
|
1668
1953
|
["npx", "node"],
|
|
1954
|
+
// `python` / `python3` are served by the embedded Pyodide runtime,
|
|
1955
|
+
// mirroring how `node` is served by the embedded V8 runtime.
|
|
1956
|
+
["python", "python"],
|
|
1957
|
+
["python3", "python"],
|
|
1669
1958
|
]);
|
|
1670
1959
|
for (const name of commandGuestPaths.keys()) {
|
|
1671
1960
|
commands.set(name, "wasmvm");
|
|
@@ -1703,21 +1992,27 @@ function isMissingHostProcessError(error) {
|
|
|
1703
1992
|
function errnoError(code, message) {
|
|
1704
1993
|
return Object.assign(new Error(`${code}: ${message}`), { code });
|
|
1705
1994
|
}
|
|
1995
|
+
// VirtualStat is a numeric, Node-default-shaped view: u64 fields above
|
|
1996
|
+
// Number.MAX_SAFE_INTEGER lose precision here, same as Node's non-bigint
|
|
1997
|
+
// fs.stat on the host.
|
|
1706
1998
|
function toVirtualStat(stat) {
|
|
1707
1999
|
return {
|
|
1708
2000
|
mode: stat.mode,
|
|
1709
|
-
size: stat.size,
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
2001
|
+
size: Number(stat.size),
|
|
2002
|
+
sizeExact: stat.size,
|
|
2003
|
+
blocks: Number(stat.blocks),
|
|
2004
|
+
dev: Number(stat.dev),
|
|
2005
|
+
rdev: Number(stat.rdev),
|
|
1713
2006
|
isDirectory: stat.is_directory,
|
|
1714
2007
|
isSymbolicLink: stat.is_symbolic_link,
|
|
1715
2008
|
atimeMs: stat.atime_ms,
|
|
1716
2009
|
mtimeMs: stat.mtime_ms,
|
|
1717
2010
|
ctimeMs: stat.ctime_ms,
|
|
1718
2011
|
birthtimeMs: stat.birthtime_ms,
|
|
1719
|
-
ino: stat.ino,
|
|
1720
|
-
|
|
2012
|
+
ino: Number(stat.ino),
|
|
2013
|
+
inoExact: stat.ino,
|
|
2014
|
+
nlink: Number(stat.nlink),
|
|
2015
|
+
nlinkExact: stat.nlink,
|
|
1721
2016
|
uid: stat.uid,
|
|
1722
2017
|
gid: stat.gid,
|
|
1723
2018
|
};
|
|
@@ -1750,7 +2045,7 @@ function socketLookupKey(kind, request) {
|
|
|
1750
2045
|
path: request.path ?? null,
|
|
1751
2046
|
});
|
|
1752
2047
|
}
|
|
1753
|
-
export {
|
|
2048
|
+
export { NativeSidecarProcessClient, SidecarEventBufferOverflow, SidecarProcess, SidecarProcessError, SidecarProcessExited, } from "./native-process-client.js";
|
|
1754
2049
|
export class AgentOsSidecarVmHandle {
|
|
1755
2050
|
client;
|
|
1756
2051
|
sessionId;
|