@secure-exec/core 0.2.1 → 0.3.0-rc.2

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.
Files changed (248) hide show
  1. package/README.md +5 -5
  2. package/dist/binary.d.ts +4 -0
  3. package/dist/binary.js +25 -0
  4. package/dist/bytes.d.ts +2 -0
  5. package/dist/bytes.js +6 -0
  6. package/dist/callbacks.d.ts +41 -0
  7. package/dist/callbacks.js +94 -0
  8. package/dist/cargo.d.ts +2 -0
  9. package/dist/cargo.js +142 -0
  10. package/dist/correlation.d.ts +10 -0
  11. package/dist/correlation.js +49 -0
  12. package/dist/descriptors.d.ts +34 -0
  13. package/dist/descriptors.js +37 -0
  14. package/dist/event-buffer.d.ts +90 -0
  15. package/dist/event-buffer.js +313 -0
  16. package/dist/ext.d.ts +7 -0
  17. package/dist/ext.js +13 -0
  18. package/dist/filesystem.d.ts +41 -0
  19. package/dist/filesystem.js +70 -0
  20. package/dist/frame-payload-codec.d.ts +8 -0
  21. package/dist/frame-payload-codec.js +14 -0
  22. package/dist/frame-rpc.d.ts +38 -0
  23. package/dist/frame-rpc.js +73 -0
  24. package/dist/frame-stream.d.ts +27 -0
  25. package/dist/frame-stream.js +99 -0
  26. package/dist/framing.d.ts +7 -0
  27. package/dist/framing.js +22 -0
  28. package/dist/generated/AcpLimitsConfig.d.ts +4 -0
  29. package/dist/generated/AcpLimitsConfig.js +2 -0
  30. package/dist/generated/CreateVmConfig.d.ts +19 -0
  31. package/dist/generated/FsPermissionRule.d.ts +6 -0
  32. package/dist/generated/FsPermissionRuleSet.d.ts +6 -0
  33. package/dist/generated/FsPermissionRuleSet.js +1 -0
  34. package/dist/generated/FsPermissionScope.d.ts +3 -0
  35. package/dist/generated/FsPermissionScope.js +1 -0
  36. package/dist/generated/HttpLimitsConfig.d.ts +3 -0
  37. package/dist/generated/HttpLimitsConfig.js +2 -0
  38. package/dist/generated/JsModuleResolution.d.ts +1 -0
  39. package/dist/generated/JsModuleResolution.js +2 -0
  40. package/dist/generated/JsRuntimeConfig.d.ts +26 -0
  41. package/dist/generated/JsRuntimeConfig.js +1 -0
  42. package/dist/generated/JsRuntimeLimitsConfig.d.ts +7 -0
  43. package/dist/generated/JsRuntimeLimitsConfig.js +2 -0
  44. package/dist/generated/JsRuntimePlatform.d.ts +1 -0
  45. package/dist/generated/JsRuntimePlatform.js +2 -0
  46. package/dist/generated/MountPluginDescriptor.d.ts +4 -0
  47. package/dist/generated/MountPluginDescriptor.js +2 -0
  48. package/dist/generated/NativeRootFilesystemConfig.d.ts +5 -0
  49. package/dist/generated/NativeRootFilesystemConfig.js +1 -0
  50. package/dist/generated/PatternPermissionRule.d.ts +6 -0
  51. package/dist/generated/PatternPermissionRule.js +1 -0
  52. package/dist/generated/PatternPermissionRuleSet.d.ts +6 -0
  53. package/dist/generated/PatternPermissionRuleSet.js +1 -0
  54. package/dist/generated/PatternPermissionScope.d.ts +3 -0
  55. package/dist/generated/PatternPermissionScope.js +1 -0
  56. package/dist/generated/PermissionMode.d.ts +1 -0
  57. package/dist/generated/PermissionMode.js +2 -0
  58. package/dist/generated/PermissionsPolicy.d.ts +10 -0
  59. package/dist/generated/PermissionsPolicy.js +1 -0
  60. package/dist/generated/PluginLimitsConfig.d.ts +4 -0
  61. package/dist/generated/PluginLimitsConfig.js +2 -0
  62. package/dist/generated/PythonLimitsConfig.d.ts +5 -0
  63. package/dist/generated/PythonLimitsConfig.js +2 -0
  64. package/dist/generated/ResourceLimitsConfig.d.ts +22 -0
  65. package/dist/generated/ResourceLimitsConfig.js +2 -0
  66. package/dist/generated/RootFilesystemConfig.d.ts +9 -0
  67. package/dist/generated/RootFilesystemConfig.js +1 -0
  68. package/dist/generated/RootFilesystemEntry.d.ts +13 -0
  69. package/dist/generated/RootFilesystemEntry.js +1 -0
  70. package/dist/generated/RootFilesystemEntryEncoding.d.ts +1 -0
  71. package/dist/generated/RootFilesystemEntryEncoding.js +2 -0
  72. package/dist/generated/RootFilesystemEntryKind.d.ts +1 -0
  73. package/dist/generated/RootFilesystemEntryKind.js +2 -0
  74. package/dist/generated/RootFilesystemLowerDescriptor.d.ts +7 -0
  75. package/dist/generated/RootFilesystemLowerDescriptor.js +1 -0
  76. package/dist/generated/RootFilesystemMode.d.ts +1 -0
  77. package/dist/generated/RootFilesystemMode.js +2 -0
  78. package/dist/generated/ToolLimitsConfig.d.ts +10 -0
  79. package/dist/generated/ToolLimitsConfig.js +2 -0
  80. package/dist/generated/VmDnsConfig.d.ts +6 -0
  81. package/dist/generated/VmDnsConfig.js +2 -0
  82. package/dist/generated/VmLimitsConfig.d.ts +18 -0
  83. package/dist/generated/VmLimitsConfig.js +1 -0
  84. package/dist/generated/VmListenPolicyConfig.d.ts +5 -0
  85. package/dist/generated/VmListenPolicyConfig.js +2 -0
  86. package/dist/generated/WasmLimitsConfig.d.ts +5 -0
  87. package/dist/generated/WasmLimitsConfig.js +2 -0
  88. package/dist/generated-protocol.d.ts +1037 -0
  89. package/dist/generated-protocol.js +2887 -0
  90. package/dist/index.d.ts +24 -62
  91. package/dist/index.js +24 -53
  92. package/dist/json.d.ts +2 -0
  93. package/dist/json.js +20 -0
  94. package/dist/kernel-proxy.d.ts +149 -0
  95. package/dist/kernel-proxy.js +1733 -0
  96. package/dist/native-client.d.ts +41 -0
  97. package/dist/native-client.js +124 -0
  98. package/dist/node-runtime.d.ts +490 -0
  99. package/dist/node-runtime.js +585 -0
  100. package/dist/numbers.d.ts +1 -0
  101. package/dist/numbers.js +8 -0
  102. package/dist/ownership.d.ts +18 -0
  103. package/dist/ownership.js +77 -0
  104. package/dist/permissions.d.ts +29 -0
  105. package/dist/permissions.js +68 -0
  106. package/dist/process.d.ts +35 -0
  107. package/dist/process.js +125 -0
  108. package/dist/protocol-client.d.ts +46 -0
  109. package/dist/protocol-client.js +180 -0
  110. package/dist/protocol-frames.d.ts +68 -0
  111. package/dist/protocol-frames.js +139 -0
  112. package/dist/protocol-maps.d.ts +28 -0
  113. package/dist/protocol-maps.js +217 -0
  114. package/dist/protocol-schema.d.ts +10 -0
  115. package/dist/protocol-schema.js +11 -0
  116. package/dist/request-payloads.d.ts +137 -0
  117. package/dist/request-payloads.js +210 -0
  118. package/dist/response-payloads.d.ts +107 -0
  119. package/dist/response-payloads.js +161 -0
  120. package/dist/sidecar-client.d.ts +242 -0
  121. package/dist/sidecar-client.js +797 -0
  122. package/dist/state.d.ts +40 -0
  123. package/dist/state.js +44 -0
  124. package/dist/test-runtime.d.ts +526 -0
  125. package/dist/test-runtime.js +2119 -0
  126. package/dist/vm-config.d.ts +31 -0
  127. package/dist/vm-config.js +1 -0
  128. package/fixtures/alpine-defaults.json +520 -0
  129. package/fixtures/base-filesystem.json +528 -0
  130. package/package.json +193 -115
  131. package/LICENSE +0 -191
  132. package/dist/bridge-setup.d.ts +0 -6
  133. package/dist/bridge-setup.js +0 -9
  134. package/dist/esm-compiler.d.ts +0 -18
  135. package/dist/esm-compiler.js +0 -72
  136. package/dist/fs-helpers.d.ts +0 -23
  137. package/dist/fs-helpers.js +0 -41
  138. package/dist/generated/isolate-runtime.d.ts +0 -19
  139. package/dist/generated/isolate-runtime.js +0 -21
  140. package/dist/generated/polyfills.d.ts +0 -82
  141. package/dist/generated/polyfills.js +0 -82
  142. package/dist/isolate-runtime/apply-custom-global-policy.js +0 -53
  143. package/dist/isolate-runtime/apply-timing-mitigation-freeze.js +0 -130
  144. package/dist/isolate-runtime/apply-timing-mitigation-off.js +0 -14
  145. package/dist/isolate-runtime/bridge-attach.js +0 -29
  146. package/dist/isolate-runtime/bridge-initial-globals.js +0 -385
  147. package/dist/isolate-runtime/eval-script-result.js +0 -8
  148. package/dist/isolate-runtime/global-exposure-helpers.js +0 -36
  149. package/dist/isolate-runtime/init-commonjs-module-globals.js +0 -28
  150. package/dist/isolate-runtime/override-process-cwd.js +0 -8
  151. package/dist/isolate-runtime/override-process-env.js +0 -8
  152. package/dist/isolate-runtime/require-setup.js +0 -4153
  153. package/dist/isolate-runtime/set-commonjs-file-globals.js +0 -36
  154. package/dist/isolate-runtime/set-stdin-data.js +0 -10
  155. package/dist/isolate-runtime/setup-dynamic-import.js +0 -123
  156. package/dist/isolate-runtime/setup-fs-facade.js +0 -87
  157. package/dist/kernel/command-registry.d.ts +0 -44
  158. package/dist/kernel/command-registry.js +0 -114
  159. package/dist/kernel/device-backend.d.ts +0 -14
  160. package/dist/kernel/device-backend.js +0 -251
  161. package/dist/kernel/device-layer.d.ts +0 -12
  162. package/dist/kernel/device-layer.js +0 -271
  163. package/dist/kernel/dns-cache.d.ts +0 -29
  164. package/dist/kernel/dns-cache.js +0 -52
  165. package/dist/kernel/fd-table.d.ts +0 -84
  166. package/dist/kernel/fd-table.js +0 -278
  167. package/dist/kernel/file-lock.d.ts +0 -34
  168. package/dist/kernel/file-lock.js +0 -122
  169. package/dist/kernel/host-adapter.d.ts +0 -50
  170. package/dist/kernel/host-adapter.js +0 -8
  171. package/dist/kernel/index.d.ts +0 -36
  172. package/dist/kernel/index.js +0 -34
  173. package/dist/kernel/kernel.d.ts +0 -9
  174. package/dist/kernel/kernel.js +0 -1415
  175. package/dist/kernel/mount-table.d.ts +0 -75
  176. package/dist/kernel/mount-table.js +0 -353
  177. package/dist/kernel/permissions.d.ts +0 -36
  178. package/dist/kernel/permissions.js +0 -150
  179. package/dist/kernel/pipe-manager.d.ts +0 -64
  180. package/dist/kernel/pipe-manager.js +0 -267
  181. package/dist/kernel/proc-backend.d.ts +0 -30
  182. package/dist/kernel/proc-backend.js +0 -428
  183. package/dist/kernel/proc-layer.d.ts +0 -11
  184. package/dist/kernel/proc-layer.js +0 -507
  185. package/dist/kernel/process-table.d.ts +0 -126
  186. package/dist/kernel/process-table.js +0 -651
  187. package/dist/kernel/pty.d.ts +0 -109
  188. package/dist/kernel/pty.js +0 -552
  189. package/dist/kernel/socket-table.d.ts +0 -312
  190. package/dist/kernel/socket-table.js +0 -1188
  191. package/dist/kernel/timer-table.d.ts +0 -54
  192. package/dist/kernel/timer-table.js +0 -108
  193. package/dist/kernel/types.d.ts +0 -541
  194. package/dist/kernel/types.js +0 -98
  195. package/dist/kernel/user.d.ts +0 -29
  196. package/dist/kernel/user.js +0 -35
  197. package/dist/kernel/vfs.d.ts +0 -82
  198. package/dist/kernel/vfs.js +0 -25
  199. package/dist/kernel/wait.d.ts +0 -45
  200. package/dist/kernel/wait.js +0 -112
  201. package/dist/kernel/wstatus.d.ts +0 -21
  202. package/dist/kernel/wstatus.js +0 -33
  203. package/dist/module-resolver.d.ts +0 -29
  204. package/dist/module-resolver.js +0 -314
  205. package/dist/package-bundler.d.ts +0 -41
  206. package/dist/package-bundler.js +0 -497
  207. package/dist/runtime-driver.d.ts +0 -66
  208. package/dist/shared/api-types.d.ts +0 -83
  209. package/dist/shared/bridge-contract.d.ts +0 -772
  210. package/dist/shared/bridge-contract.js +0 -169
  211. package/dist/shared/console-formatter.d.ts +0 -22
  212. package/dist/shared/console-formatter.js +0 -161
  213. package/dist/shared/constants.d.ts +0 -3
  214. package/dist/shared/constants.js +0 -3
  215. package/dist/shared/errors.d.ts +0 -16
  216. package/dist/shared/errors.js +0 -21
  217. package/dist/shared/esm-utils.d.ts +0 -28
  218. package/dist/shared/esm-utils.js +0 -97
  219. package/dist/shared/global-exposure.d.ts +0 -38
  220. package/dist/shared/global-exposure.js +0 -876
  221. package/dist/shared/in-memory-fs.d.ts +0 -16
  222. package/dist/shared/in-memory-fs.js +0 -115
  223. package/dist/shared/permissions.d.ts +0 -36
  224. package/dist/shared/permissions.js +0 -314
  225. package/dist/shared/require-setup.d.ts +0 -6
  226. package/dist/shared/require-setup.js +0 -9
  227. package/dist/test/block-store-conformance.d.ts +0 -34
  228. package/dist/test/block-store-conformance.js +0 -251
  229. package/dist/test/metadata-store-conformance.d.ts +0 -37
  230. package/dist/test/metadata-store-conformance.js +0 -646
  231. package/dist/test/vfs-conformance.d.ts +0 -65
  232. package/dist/test/vfs-conformance.js +0 -842
  233. package/dist/types.d.ts +0 -98
  234. package/dist/types.js +0 -6
  235. package/dist/vfs/chunked-vfs.d.ts +0 -66
  236. package/dist/vfs/chunked-vfs.js +0 -1290
  237. package/dist/vfs/host-block-store.d.ts +0 -19
  238. package/dist/vfs/host-block-store.js +0 -97
  239. package/dist/vfs/memory-block-store.d.ts +0 -16
  240. package/dist/vfs/memory-block-store.js +0 -45
  241. package/dist/vfs/memory-metadata.d.ts +0 -75
  242. package/dist/vfs/memory-metadata.js +0 -528
  243. package/dist/vfs/sqlite-metadata.d.ts +0 -91
  244. package/dist/vfs/sqlite-metadata.js +0 -582
  245. package/dist/vfs/types.d.ts +0 -210
  246. package/dist/vfs/types.js +0 -8
  247. /package/dist/{runtime-driver.js → generated/CreateVmConfig.js} +0 -0
  248. /package/dist/{shared/api-types.js → generated/FsPermissionRule.js} +0 -0
@@ -0,0 +1,2119 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import * as fsSync from "node:fs";
3
+ import * as fs from "node:fs/promises";
4
+ import * as path from "node:path";
5
+ import * as posixPath from "node:path/posix";
6
+ import { fileURLToPath } from "node:url";
7
+ import { NATIVE_SIDECAR_FRAME_TIMEOUT_MS, NativeSidecarKernelProxy, NativeSidecarProcessClient, serializeMountConfigForSidecar, } from "./kernel-proxy.js";
8
+ import { resolvePublishedSidecarBinary } from "./binary.js";
9
+ import { findCargoBinary, resolveCargoBinary } from "./cargo.js";
10
+ export const AF_INET = 2;
11
+ export const AF_UNIX = 1;
12
+ export const SOCK_STREAM = 1;
13
+ export const SOCK_DGRAM = 2;
14
+ export const SIGTERM = 15;
15
+ const S_IFREG = 0o100000;
16
+ const S_IFDIR = 0o040000;
17
+ const S_IFLNK = 0o120000;
18
+ const MAX_SYMLINK_DEPTH = 40;
19
+ const KERNEL_COMMAND_STUB = "#!/bin/sh\n# kernel command stub\n";
20
+ const NODE_RUNTIME_BOOTSTRAP_COMMANDS = ["node", "npm", "npx"];
21
+ const KERNEL_POSIX_BOOTSTRAP_DIRS = [
22
+ "/dev",
23
+ "/proc",
24
+ "/tmp",
25
+ "/bin",
26
+ "/lib",
27
+ "/sbin",
28
+ "/boot",
29
+ "/etc",
30
+ "/root",
31
+ "/run",
32
+ "/srv",
33
+ "/sys",
34
+ "/opt",
35
+ "/mnt",
36
+ "/media",
37
+ "/home",
38
+ "/home/user",
39
+ "/usr",
40
+ "/usr/bin",
41
+ "/usr/games",
42
+ "/usr/include",
43
+ "/usr/lib",
44
+ "/usr/libexec",
45
+ "/usr/man",
46
+ "/usr/local",
47
+ "/usr/local/bin",
48
+ "/usr/sbin",
49
+ "/usr/share",
50
+ "/usr/share/man",
51
+ "/var",
52
+ "/var/cache",
53
+ "/var/empty",
54
+ "/var/lib",
55
+ "/var/lock",
56
+ "/var/log",
57
+ "/var/run",
58
+ "/var/spool",
59
+ "/var/tmp",
60
+ ];
61
+ const REPO_ROOT = fileURLToPath(new URL("../../..", import.meta.url));
62
+ const SIDECAR_BINARY = path.join(REPO_ROOT, "target/debug/secure-exec-sidecar");
63
+ const SIDECAR_BUILD_INPUTS = [
64
+ path.join(REPO_ROOT, "Cargo.toml"),
65
+ path.join(REPO_ROOT, "Cargo.lock"),
66
+ path.join(REPO_ROOT, "crates/bridge"),
67
+ path.join(REPO_ROOT, "crates/execution"),
68
+ path.join(REPO_ROOT, "crates/kernel"),
69
+ path.join(REPO_ROOT, "crates/sidecar"),
70
+ ];
71
+ let ensuredSidecarBinary = null;
72
+ export class KernelError extends Error {
73
+ code;
74
+ constructor(code, message) {
75
+ super(message.startsWith(`${code}:`) ? message : `${code}: ${message}`);
76
+ this.name = "KernelError";
77
+ this.code = code;
78
+ }
79
+ }
80
+ function normalizePath(inputPath) {
81
+ if (!inputPath)
82
+ return "/";
83
+ let normalized = inputPath.startsWith("/") ? inputPath : `/${inputPath}`;
84
+ normalized = normalized.replace(/\/+/g, "/");
85
+ if (normalized.length > 1 && normalized.endsWith("/")) {
86
+ normalized = normalized.slice(0, -1);
87
+ }
88
+ const parts = normalized.split("/");
89
+ const resolved = [];
90
+ for (const part of parts) {
91
+ if (part === "" || part === ".")
92
+ continue;
93
+ if (part === "..") {
94
+ resolved.pop();
95
+ continue;
96
+ }
97
+ resolved.push(part);
98
+ }
99
+ return resolved.length === 0 ? "/" : `/${resolved.join("/")}`;
100
+ }
101
+ function dirnameVirtual(inputPath) {
102
+ const normalized = normalizePath(inputPath);
103
+ if (normalized === "/")
104
+ return "/";
105
+ const parts = normalized.split("/").filter(Boolean);
106
+ return parts.length <= 1 ? "/" : `/${parts.slice(0, -1).join("/")}`;
107
+ }
108
+ let nextInode = 1;
109
+ export class InMemoryFileSystem {
110
+ entries = new Map();
111
+ constructor() {
112
+ this.entries.set("/", this.newDirectory());
113
+ }
114
+ async readFile(targetPath) {
115
+ const entry = this.resolveEntry(targetPath);
116
+ if (!entry || entry.type !== "file") {
117
+ throw errnoError("ENOENT", `open '${targetPath}'`);
118
+ }
119
+ entry.atimeMs = Date.now();
120
+ return entry.data;
121
+ }
122
+ async readTextFile(targetPath) {
123
+ return new TextDecoder().decode(await this.readFile(targetPath));
124
+ }
125
+ async readDir(targetPath) {
126
+ return (await this.readDirWithTypes(targetPath)).map((entry) => entry.name);
127
+ }
128
+ async readDirWithTypes(targetPath) {
129
+ const resolved = this.resolvePath(targetPath);
130
+ const entry = this.entries.get(resolved);
131
+ if (!entry || entry.type !== "dir") {
132
+ throw errnoError("ENOENT", `scandir '${targetPath}'`);
133
+ }
134
+ const prefix = resolved === "/" ? "/" : `${resolved}/`;
135
+ const output = new Map();
136
+ for (const [entryPath, candidate] of this.entries) {
137
+ if (!entryPath.startsWith(prefix))
138
+ continue;
139
+ const rest = entryPath.slice(prefix.length);
140
+ if (!rest || rest.includes("/"))
141
+ continue;
142
+ output.set(rest, {
143
+ name: rest,
144
+ isDirectory: candidate.type === "dir",
145
+ isSymbolicLink: candidate.type === "symlink",
146
+ });
147
+ }
148
+ return [...output.values()];
149
+ }
150
+ async writeFile(targetPath, content) {
151
+ const normalized = normalizePath(targetPath);
152
+ await this.mkdir(dirnameVirtual(normalized), { recursive: true });
153
+ const data = typeof content === "string" ? new TextEncoder().encode(content) : content;
154
+ const existing = this.entries.get(normalized);
155
+ if (existing?.type === "file") {
156
+ existing.data = data;
157
+ existing.mtimeMs = Date.now();
158
+ existing.ctimeMs = Date.now();
159
+ return;
160
+ }
161
+ const now = Date.now();
162
+ this.entries.set(normalized, {
163
+ type: "file",
164
+ data,
165
+ mode: S_IFREG | 0o644,
166
+ uid: 0,
167
+ gid: 0,
168
+ nlink: 1,
169
+ ino: nextInode++,
170
+ atimeMs: now,
171
+ mtimeMs: now,
172
+ ctimeMs: now,
173
+ birthtimeMs: now,
174
+ });
175
+ }
176
+ async createDir(targetPath) {
177
+ const normalized = normalizePath(targetPath);
178
+ if (!this.entries.has(dirnameVirtual(normalized))) {
179
+ throw errnoError("ENOENT", `mkdir '${targetPath}'`);
180
+ }
181
+ if (!this.entries.has(normalized)) {
182
+ this.entries.set(normalized, this.newDirectory());
183
+ }
184
+ }
185
+ async mkdir(targetPath, options) {
186
+ const normalized = normalizePath(targetPath);
187
+ if (options?.recursive === false) {
188
+ return this.createDir(normalized);
189
+ }
190
+ let current = "";
191
+ for (const part of normalized.split("/").filter(Boolean)) {
192
+ current += `/${part}`;
193
+ if (!this.entries.has(current)) {
194
+ this.entries.set(current, this.newDirectory());
195
+ }
196
+ }
197
+ }
198
+ async exists(targetPath) {
199
+ try {
200
+ return this.entries.has(this.resolvePath(targetPath));
201
+ }
202
+ catch {
203
+ return false;
204
+ }
205
+ }
206
+ async stat(targetPath) {
207
+ const entry = this.resolveEntry(targetPath);
208
+ if (!entry)
209
+ throw errnoError("ENOENT", `stat '${targetPath}'`);
210
+ return this.toStat(entry);
211
+ }
212
+ async removeFile(targetPath) {
213
+ const resolved = this.resolvePath(targetPath);
214
+ const entry = this.entries.get(resolved);
215
+ if (!entry || entry.type === "dir") {
216
+ throw errnoError("ENOENT", `unlink '${targetPath}'`);
217
+ }
218
+ this.entries.delete(resolved);
219
+ }
220
+ async removeDir(targetPath) {
221
+ const resolved = this.resolvePath(targetPath);
222
+ if (resolved === "/") {
223
+ throw errnoError("EPERM", "operation not permitted");
224
+ }
225
+ const entry = this.entries.get(resolved);
226
+ if (!entry || entry.type !== "dir") {
227
+ throw errnoError("ENOENT", `rmdir '${targetPath}'`);
228
+ }
229
+ const prefix = `${resolved}/`;
230
+ for (const key of this.entries.keys()) {
231
+ if (key.startsWith(prefix)) {
232
+ throw errnoError("ENOTEMPTY", `directory not empty '${targetPath}'`);
233
+ }
234
+ }
235
+ this.entries.delete(resolved);
236
+ }
237
+ async rename(oldPath, newPath) {
238
+ const oldResolved = this.resolvePath(oldPath);
239
+ const newResolved = normalizePath(newPath);
240
+ const entry = this.entries.get(oldResolved);
241
+ if (!entry)
242
+ throw errnoError("ENOENT", `rename '${oldPath}'`);
243
+ if (!this.entries.has(dirnameVirtual(newResolved))) {
244
+ throw errnoError("ENOENT", `rename '${newPath}'`);
245
+ }
246
+ if (entry.type !== "dir") {
247
+ this.entries.set(newResolved, entry);
248
+ this.entries.delete(oldResolved);
249
+ return;
250
+ }
251
+ const prefix = `${oldResolved}/`;
252
+ const moved = [];
253
+ for (const candidate of this.entries) {
254
+ if (candidate[0] === oldResolved || candidate[0].startsWith(prefix)) {
255
+ moved.push(candidate);
256
+ }
257
+ }
258
+ for (const [candidatePath] of moved) {
259
+ this.entries.delete(candidatePath);
260
+ }
261
+ for (const [candidatePath, candidate] of moved) {
262
+ const nextPath = candidatePath === oldResolved
263
+ ? newResolved
264
+ : `${newResolved}${candidatePath.slice(oldResolved.length)}`;
265
+ this.entries.set(nextPath, candidate);
266
+ }
267
+ }
268
+ async realpath(targetPath) {
269
+ return this.resolvePath(targetPath);
270
+ }
271
+ async symlink(target, linkPath) {
272
+ const normalized = normalizePath(linkPath);
273
+ if (this.entries.has(normalized)) {
274
+ throw errnoError("EEXIST", `symlink '${linkPath}'`);
275
+ }
276
+ const now = Date.now();
277
+ this.entries.set(normalized, {
278
+ type: "symlink",
279
+ target,
280
+ mode: S_IFLNK | 0o777,
281
+ uid: 0,
282
+ gid: 0,
283
+ nlink: 1,
284
+ ino: nextInode++,
285
+ atimeMs: now,
286
+ mtimeMs: now,
287
+ ctimeMs: now,
288
+ birthtimeMs: now,
289
+ });
290
+ }
291
+ async readlink(targetPath) {
292
+ const normalized = normalizePath(targetPath);
293
+ const entry = this.entries.get(normalized);
294
+ if (!entry || entry.type !== "symlink") {
295
+ throw errnoError("ENOENT", `readlink '${targetPath}'`);
296
+ }
297
+ return entry.target;
298
+ }
299
+ async lstat(targetPath) {
300
+ const entry = this.entries.get(normalizePath(targetPath));
301
+ if (!entry)
302
+ throw errnoError("ENOENT", `lstat '${targetPath}'`);
303
+ return this.toStat(entry);
304
+ }
305
+ async link(oldPath, newPath) {
306
+ const entry = this.resolveEntry(oldPath);
307
+ if (!entry || entry.type !== "file") {
308
+ throw errnoError("ENOENT", `link '${oldPath}'`);
309
+ }
310
+ const normalized = normalizePath(newPath);
311
+ if (this.entries.has(normalized)) {
312
+ throw errnoError("EEXIST", `link '${newPath}'`);
313
+ }
314
+ entry.nlink += 1;
315
+ this.entries.set(normalized, entry);
316
+ }
317
+ async chmod(targetPath, mode) {
318
+ const entry = this.resolveEntry(targetPath);
319
+ if (!entry)
320
+ throw errnoError("ENOENT", `chmod '${targetPath}'`);
321
+ const typeBits = mode & 0o170000;
322
+ entry.mode =
323
+ typeBits === 0 ? (entry.mode & 0o170000) | (mode & 0o7777) : mode;
324
+ entry.ctimeMs = Date.now();
325
+ }
326
+ async chown(targetPath, uid, gid) {
327
+ const entry = this.resolveEntry(targetPath);
328
+ if (!entry)
329
+ throw errnoError("ENOENT", `chown '${targetPath}'`);
330
+ entry.uid = uid;
331
+ entry.gid = gid;
332
+ entry.ctimeMs = Date.now();
333
+ }
334
+ async utimes(targetPath, atime, mtime) {
335
+ const entry = this.resolveEntry(targetPath);
336
+ if (!entry)
337
+ throw errnoError("ENOENT", `utimes '${targetPath}'`);
338
+ entry.atimeMs = atime;
339
+ entry.mtimeMs = mtime;
340
+ entry.ctimeMs = Date.now();
341
+ }
342
+ async truncate(targetPath, length) {
343
+ const entry = this.resolveEntry(targetPath);
344
+ if (!entry || entry.type !== "file") {
345
+ throw errnoError("ENOENT", `truncate '${targetPath}'`);
346
+ }
347
+ if (length < entry.data.length) {
348
+ entry.data = entry.data.slice(0, length);
349
+ }
350
+ else if (length > entry.data.length) {
351
+ const expanded = new Uint8Array(length);
352
+ expanded.set(entry.data);
353
+ entry.data = expanded;
354
+ }
355
+ entry.mtimeMs = Date.now();
356
+ entry.ctimeMs = Date.now();
357
+ }
358
+ async pread(targetPath, offset, length) {
359
+ const entry = this.resolveEntry(targetPath);
360
+ if (!entry || entry.type !== "file") {
361
+ throw errnoError("ENOENT", `open '${targetPath}'`);
362
+ }
363
+ if (offset >= entry.data.length)
364
+ return new Uint8Array(0);
365
+ return entry.data.slice(offset, Math.min(offset + length, entry.data.length));
366
+ }
367
+ async pwrite(targetPath, offset, data) {
368
+ const entry = this.resolveEntry(targetPath);
369
+ if (!entry || entry.type !== "file") {
370
+ throw errnoError("ENOENT", `open '${targetPath}'`);
371
+ }
372
+ const nextSize = Math.max(entry.data.length, offset + data.length);
373
+ const updated = new Uint8Array(nextSize);
374
+ updated.set(entry.data);
375
+ updated.set(data, offset);
376
+ entry.data = updated;
377
+ entry.mtimeMs = Date.now();
378
+ entry.ctimeMs = Date.now();
379
+ }
380
+ resolvePath(targetPath, depth = 0) {
381
+ if (depth > MAX_SYMLINK_DEPTH) {
382
+ throw errnoError("ELOOP", `too many symbolic links '${targetPath}'`);
383
+ }
384
+ const normalized = normalizePath(targetPath);
385
+ const entry = this.entries.get(normalized);
386
+ if (!entry)
387
+ return normalized;
388
+ if (entry.type === "symlink") {
389
+ const target = entry.target.startsWith("/")
390
+ ? entry.target
391
+ : `${dirnameVirtual(normalized)}/${entry.target}`;
392
+ return this.resolvePath(target, depth + 1);
393
+ }
394
+ return normalized;
395
+ }
396
+ resolveEntry(targetPath) {
397
+ return this.entries.get(this.resolvePath(targetPath));
398
+ }
399
+ newDirectory() {
400
+ const now = Date.now();
401
+ return {
402
+ type: "dir",
403
+ mode: S_IFDIR | 0o755,
404
+ uid: 0,
405
+ gid: 0,
406
+ nlink: 2,
407
+ ino: nextInode++,
408
+ atimeMs: now,
409
+ mtimeMs: now,
410
+ ctimeMs: now,
411
+ birthtimeMs: now,
412
+ };
413
+ }
414
+ toStat(entry) {
415
+ const size = entry.type === "file" ? entry.data.length : 4096;
416
+ return {
417
+ mode: entry.mode,
418
+ size,
419
+ blocks: size === 0 ? 0 : Math.ceil(size / 512),
420
+ dev: 1,
421
+ rdev: 0,
422
+ isDirectory: entry.type === "dir",
423
+ isSymbolicLink: entry.type === "symlink",
424
+ atimeMs: entry.atimeMs,
425
+ mtimeMs: entry.mtimeMs,
426
+ ctimeMs: entry.ctimeMs,
427
+ birthtimeMs: entry.birthtimeMs,
428
+ ino: entry.ino,
429
+ nlink: entry.nlink,
430
+ uid: entry.uid,
431
+ gid: entry.gid,
432
+ };
433
+ }
434
+ }
435
+ export function createInMemoryFileSystem() {
436
+ return new InMemoryFileSystem();
437
+ }
438
+ export class NodeFileSystem {
439
+ rootPath;
440
+ constructor(options) {
441
+ this.rootPath = fsSync.realpathSync(options.root);
442
+ }
443
+ normalizeTarget(targetPath) {
444
+ const normalized = normalizePath(targetPath).replace(/^\/+/, "");
445
+ const resolved = path.resolve(this.rootPath, normalized);
446
+ if (resolved !== this.rootPath &&
447
+ !resolved.startsWith(`${this.rootPath}${path.sep}`)) {
448
+ throw errnoError("EACCES", `path escapes root '${targetPath}'`);
449
+ }
450
+ return resolved;
451
+ }
452
+ toStat(stat) {
453
+ const posixStat = stat;
454
+ return {
455
+ mode: stat.mode,
456
+ size: stat.size,
457
+ blocks: posixStat.blocks ?? (stat.size === 0 ? 0 : Math.ceil(stat.size / 512)),
458
+ dev: posixStat.dev ?? 1,
459
+ rdev: posixStat.rdev ?? 0,
460
+ isDirectory: stat.isDirectory(),
461
+ isSymbolicLink: stat.isSymbolicLink(),
462
+ atimeMs: Math.trunc(stat.atimeMs),
463
+ mtimeMs: Math.trunc(stat.mtimeMs),
464
+ ctimeMs: Math.trunc(stat.ctimeMs),
465
+ birthtimeMs: Math.trunc(stat.birthtimeMs),
466
+ ino: stat.ino,
467
+ nlink: stat.nlink,
468
+ uid: stat.uid,
469
+ gid: stat.gid,
470
+ };
471
+ }
472
+ async readFile(targetPath) {
473
+ return new Uint8Array(await fs.readFile(this.normalizeTarget(targetPath)));
474
+ }
475
+ async readTextFile(targetPath) {
476
+ return fs.readFile(this.normalizeTarget(targetPath), "utf8");
477
+ }
478
+ async readDir(targetPath) {
479
+ return fs.readdir(this.normalizeTarget(targetPath));
480
+ }
481
+ async readDirWithTypes(targetPath) {
482
+ const entries = await fs.readdir(this.normalizeTarget(targetPath), {
483
+ withFileTypes: true,
484
+ });
485
+ return entries.map((entry) => ({
486
+ name: entry.name,
487
+ isDirectory: entry.isDirectory(),
488
+ isSymbolicLink: entry.isSymbolicLink(),
489
+ }));
490
+ }
491
+ async writeFile(targetPath, content) {
492
+ const resolved = this.normalizeTarget(targetPath);
493
+ await fs.mkdir(path.dirname(resolved), { recursive: true });
494
+ await fs.writeFile(resolved, content);
495
+ }
496
+ async createDir(targetPath) {
497
+ await fs.mkdir(this.normalizeTarget(targetPath));
498
+ }
499
+ async mkdir(targetPath, options) {
500
+ await fs.mkdir(this.normalizeTarget(targetPath), {
501
+ recursive: options?.recursive ?? true,
502
+ });
503
+ }
504
+ async exists(targetPath) {
505
+ try {
506
+ await fs.access(this.normalizeTarget(targetPath));
507
+ return true;
508
+ }
509
+ catch {
510
+ return false;
511
+ }
512
+ }
513
+ async stat(targetPath) {
514
+ return this.toStat(await fs.stat(this.normalizeTarget(targetPath)));
515
+ }
516
+ async removeFile(targetPath) {
517
+ await fs.unlink(this.normalizeTarget(targetPath));
518
+ }
519
+ async removeDir(targetPath) {
520
+ await fs.rmdir(this.normalizeTarget(targetPath));
521
+ }
522
+ async rename(oldPath, newPath) {
523
+ const nextPath = this.normalizeTarget(newPath);
524
+ await fs.mkdir(path.dirname(nextPath), { recursive: true });
525
+ await fs.rename(this.normalizeTarget(oldPath), nextPath);
526
+ }
527
+ async realpath(targetPath) {
528
+ const real = await fs.realpath(this.normalizeTarget(targetPath));
529
+ const relative = path.relative(this.rootPath, real);
530
+ return relative ? `/${relative.split(path.sep).join("/")}` : "/";
531
+ }
532
+ async symlink(target, linkPath) {
533
+ const resolvedLink = this.normalizeTarget(linkPath);
534
+ await fs.mkdir(path.dirname(resolvedLink), { recursive: true });
535
+ await fs.symlink(target, resolvedLink);
536
+ }
537
+ async readlink(targetPath) {
538
+ return fs.readlink(this.normalizeTarget(targetPath));
539
+ }
540
+ async lstat(targetPath) {
541
+ return this.toStat(await fs.lstat(this.normalizeTarget(targetPath)));
542
+ }
543
+ async link(oldPath, newPath) {
544
+ await fs.link(this.normalizeTarget(oldPath), this.normalizeTarget(newPath));
545
+ }
546
+ async chmod(targetPath, mode) {
547
+ await fs.chmod(this.normalizeTarget(targetPath), mode);
548
+ }
549
+ async chown(targetPath, uid, gid) {
550
+ await fs.chown(this.normalizeTarget(targetPath), uid, gid);
551
+ }
552
+ async utimes(targetPath, atime, mtime) {
553
+ await fs.utimes(this.normalizeTarget(targetPath), atime / 1000, mtime / 1000);
554
+ }
555
+ async truncate(targetPath, length) {
556
+ await fs.truncate(this.normalizeTarget(targetPath), length);
557
+ }
558
+ async pread(targetPath, offset, length) {
559
+ const handle = await fs.open(this.normalizeTarget(targetPath), "r");
560
+ try {
561
+ const buffer = Buffer.alloc(length);
562
+ const { bytesRead } = await handle.read(buffer, 0, length, offset);
563
+ return new Uint8Array(buffer.buffer, buffer.byteOffset, bytesRead);
564
+ }
565
+ finally {
566
+ await handle.close();
567
+ }
568
+ }
569
+ async pwrite(targetPath, offset, data) {
570
+ const handle = await fs.open(this.normalizeTarget(targetPath), "r+");
571
+ try {
572
+ await handle.write(data, 0, data.length, offset);
573
+ }
574
+ finally {
575
+ await handle.close();
576
+ }
577
+ }
578
+ }
579
+ function permissionAllows(mode) {
580
+ return mode !== "deny";
581
+ }
582
+ function globMatches(pattern, value) {
583
+ const escaped = pattern.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
584
+ const expression = escaped.replace(/\*/g, ".*");
585
+ return new RegExp(`^${expression}$`).test(value);
586
+ }
587
+ function envPolicyAllows(policy, name) {
588
+ if (policy === undefined) {
589
+ return true;
590
+ }
591
+ if (typeof policy === "string") {
592
+ return permissionAllows(policy);
593
+ }
594
+ let mode = policy.default ?? "deny";
595
+ for (const rule of policy.rules) {
596
+ const operationsMatch = !rule.operations ||
597
+ rule.operations.length === 0 ||
598
+ rule.operations.includes("read");
599
+ const patternsMatch = !rule.patterns ||
600
+ rule.patterns.length === 0 ||
601
+ rule.patterns.some((pattern) => globMatches(pattern, name));
602
+ if (operationsMatch && patternsMatch) {
603
+ mode = rule.mode;
604
+ }
605
+ }
606
+ return permissionAllows(mode);
607
+ }
608
+ export const allowAllFs = "allow";
609
+ export const allowAllNetwork = "allow";
610
+ export const allowAllChildProcess = "allow";
611
+ export const allowAllProcess = "allow";
612
+ export const allowAllEnv = "allow";
613
+ export const allowAll = {
614
+ fs: allowAllFs,
615
+ network: allowAllNetwork,
616
+ childProcess: allowAllChildProcess,
617
+ process: allowAllProcess,
618
+ env: allowAllEnv,
619
+ };
620
+ function normalizeFsPermissionScope(scope) {
621
+ if (scope === undefined || typeof scope === "string") {
622
+ return scope;
623
+ }
624
+ return {
625
+ ...scope,
626
+ rules: scope.rules.map((rule) => ({
627
+ mode: rule.mode,
628
+ operations: rule.operations ?? [],
629
+ paths: rule.paths ?? [],
630
+ })),
631
+ };
632
+ }
633
+ function normalizePatternPermissionScope(scope) {
634
+ if (scope === undefined || typeof scope === "string") {
635
+ return scope;
636
+ }
637
+ return {
638
+ ...scope,
639
+ rules: scope.rules.map((rule) => ({
640
+ mode: rule.mode,
641
+ operations: rule.operations ?? [],
642
+ patterns: rule.patterns ?? [],
643
+ })),
644
+ };
645
+ }
646
+ function normalizePermissionsPolicy(permissions) {
647
+ if (!permissions) {
648
+ return undefined;
649
+ }
650
+ return {
651
+ fs: normalizeFsPermissionScope(permissions.fs),
652
+ network: normalizePatternPermissionScope(permissions.network),
653
+ childProcess: normalizePatternPermissionScope(permissions.childProcess),
654
+ process: normalizePatternPermissionScope(permissions.process),
655
+ env: normalizePatternPermissionScope(permissions.env),
656
+ tool: normalizePatternPermissionScope(permissions.tool),
657
+ };
658
+ }
659
+ export function filterEnv(env, permissions) {
660
+ const input = env ?? {};
661
+ if (!permissions?.env)
662
+ return { ...input };
663
+ const output = {};
664
+ for (const [name, value] of Object.entries(input)) {
665
+ if (envPolicyAllows(permissions.env, name)) {
666
+ output[name] = value;
667
+ }
668
+ }
669
+ return output;
670
+ }
671
+ export function createProcessScopedFileSystem(filesystem) {
672
+ return filesystem;
673
+ }
674
+ export async function exists(filesystem, targetPath) {
675
+ return filesystem.exists(targetPath);
676
+ }
677
+ export async function stat(filesystem, targetPath) {
678
+ return filesystem.stat(targetPath);
679
+ }
680
+ export async function rename(filesystem, oldPath, newPath) {
681
+ return filesystem.rename(oldPath, newPath);
682
+ }
683
+ export async function readDirWithTypes(filesystem, targetPath) {
684
+ return filesystem.readDirWithTypes(targetPath);
685
+ }
686
+ export async function mkdir(filesystem, targetPath, options) {
687
+ return filesystem.mkdir(targetPath, options);
688
+ }
689
+ export function createNodeHostCommandExecutor() {
690
+ return {
691
+ spawn() {
692
+ throw new Error("createNodeHostCommandExecutor is not supported on the native runtime path");
693
+ },
694
+ };
695
+ }
696
+ export function createKernelCommandExecutor(kernel) {
697
+ return {
698
+ spawn(command, args, options) {
699
+ return kernel.spawn(command, args, options);
700
+ },
701
+ };
702
+ }
703
+ export function createKernelVfsAdapter(kernelVfs) {
704
+ return kernelVfs;
705
+ }
706
+ export function createHostFallbackVfs(base) {
707
+ return base;
708
+ }
709
+ export function isPrivateIp(host) {
710
+ return (host === "localhost" ||
711
+ host === "127.0.0.1" ||
712
+ host.startsWith("10.") ||
713
+ host.startsWith("192.168.") ||
714
+ /^172\.(1[6-9]|2\d|3[0-1])\./.test(host));
715
+ }
716
+ export function createNodeHostNetworkAdapter() {
717
+ return createDefaultNetworkAdapter();
718
+ }
719
+ export function createDefaultNetworkAdapter() {
720
+ return {
721
+ async fetch(url, options) {
722
+ const response = await globalThis.fetch(url, {
723
+ method: options?.method ?? "GET",
724
+ headers: options?.headers,
725
+ body: options?.body,
726
+ });
727
+ const headers = {};
728
+ response.headers.forEach((value, key) => {
729
+ headers[key] = value;
730
+ });
731
+ return {
732
+ ok: response.ok,
733
+ status: response.status,
734
+ statusText: response.statusText,
735
+ headers,
736
+ body: await response.text(),
737
+ url: response.url,
738
+ redirected: response.redirected,
739
+ };
740
+ },
741
+ async dnsLookup(hostname) {
742
+ return { address: hostname, family: hostname.includes(":") ? 6 : 4 };
743
+ },
744
+ async httpRequest(url, options) {
745
+ const response = await globalThis.fetch(url, {
746
+ method: options?.method ?? "GET",
747
+ headers: options?.headers,
748
+ body: options?.body,
749
+ });
750
+ const headers = {};
751
+ response.headers.forEach((value, key) => {
752
+ headers[key] = value;
753
+ });
754
+ return {
755
+ status: response.status,
756
+ statusText: response.statusText,
757
+ headers,
758
+ body: await response.text(),
759
+ url: response.url,
760
+ };
761
+ },
762
+ };
763
+ }
764
+ export function createNodeDriver(options = {}) {
765
+ return {
766
+ filesystem: options.filesystem,
767
+ network: options.networkAdapter,
768
+ commandExecutor: options.commandExecutor,
769
+ permissions: options.permissions,
770
+ runtime: {
771
+ process: options.processConfig ?? {},
772
+ os: options.osConfig ?? {},
773
+ },
774
+ };
775
+ }
776
+ export class NodeExecutionDriver {
777
+ options;
778
+ network;
779
+ constructor(options) {
780
+ this.options = options;
781
+ this.network = options.system.network;
782
+ }
783
+ async exec() {
784
+ throw new Error("NodeExecutionDriver is not available after the native runtime migration");
785
+ }
786
+ async run() {
787
+ throw new Error("NodeExecutionDriver is not available after the native runtime migration");
788
+ }
789
+ dispose() {
790
+ void this.options;
791
+ }
792
+ async terminate() { }
793
+ }
794
+ export class NodeRuntime extends NodeExecutionDriver {
795
+ }
796
+ export function createNodeRuntimeDriverFactory() {
797
+ return {
798
+ createRuntimeDriver(options) {
799
+ return new NodeRuntime(options);
800
+ },
801
+ };
802
+ }
803
+ export class ModuleAccessFileSystem extends NodeFileSystem {
804
+ }
805
+ export const WASMVM_COMMANDS = Object.freeze([
806
+ "sh",
807
+ "bash",
808
+ "grep",
809
+ "egrep",
810
+ "fgrep",
811
+ "rg",
812
+ "sed",
813
+ "awk",
814
+ "jq",
815
+ "yq",
816
+ "find",
817
+ "fd",
818
+ "cat",
819
+ "chmod",
820
+ "column",
821
+ "cp",
822
+ "dd",
823
+ "diff",
824
+ "du",
825
+ "expr",
826
+ "file",
827
+ "head",
828
+ "ln",
829
+ "logname",
830
+ "ls",
831
+ "mkdir",
832
+ "mktemp",
833
+ "mv",
834
+ "pathchk",
835
+ "rev",
836
+ "rm",
837
+ "sleep",
838
+ "sort",
839
+ "split",
840
+ "stat",
841
+ "strings",
842
+ "tac",
843
+ "tail",
844
+ "test",
845
+ "[",
846
+ "touch",
847
+ "tree",
848
+ "tsort",
849
+ "whoami",
850
+ "gzip",
851
+ "gunzip",
852
+ "zcat",
853
+ "tar",
854
+ "zip",
855
+ "unzip",
856
+ "sqlite3",
857
+ "curl",
858
+ "wget",
859
+ "make",
860
+ "git",
861
+ "git-remote-http",
862
+ "git-remote-https",
863
+ "env",
864
+ "envsubst",
865
+ "nice",
866
+ "nohup",
867
+ "stdbuf",
868
+ "timeout",
869
+ "xargs",
870
+ "base32",
871
+ "base64",
872
+ "basenc",
873
+ "basename",
874
+ "comm",
875
+ "cut",
876
+ "dircolors",
877
+ "dirname",
878
+ "echo",
879
+ "expand",
880
+ "factor",
881
+ "false",
882
+ "fmt",
883
+ "fold",
884
+ "join",
885
+ "nl",
886
+ "numfmt",
887
+ "od",
888
+ "paste",
889
+ "printenv",
890
+ "printf",
891
+ "ptx",
892
+ "seq",
893
+ "shuf",
894
+ "tr",
895
+ "true",
896
+ "unexpand",
897
+ "uniq",
898
+ "wc",
899
+ "yes",
900
+ "b2sum",
901
+ "cksum",
902
+ "md5sum",
903
+ "sha1sum",
904
+ "sha224sum",
905
+ "sha256sum",
906
+ "sha384sum",
907
+ "sha512sum",
908
+ "sum",
909
+ "link",
910
+ "pwd",
911
+ "readlink",
912
+ "realpath",
913
+ "rmdir",
914
+ "shred",
915
+ "tee",
916
+ "truncate",
917
+ "unlink",
918
+ "arch",
919
+ "date",
920
+ "nproc",
921
+ "uname",
922
+ "dir",
923
+ "vdir",
924
+ "hostname",
925
+ "hostid",
926
+ "more",
927
+ "sync",
928
+ "tty",
929
+ "chcon",
930
+ "runcon",
931
+ "chgrp",
932
+ "chown",
933
+ "chroot",
934
+ "df",
935
+ "groups",
936
+ "id",
937
+ "install",
938
+ "kill",
939
+ "mkfifo",
940
+ "mknod",
941
+ "pinky",
942
+ "who",
943
+ "users",
944
+ "uptime",
945
+ "stty",
946
+ "codex",
947
+ "codex-exec",
948
+ ]);
949
+ export const DEFAULT_FIRST_PARTY_TIERS = Object.freeze({
950
+ sh: "full",
951
+ bash: "full",
952
+ env: "full",
953
+ timeout: "full",
954
+ xargs: "full",
955
+ nice: "full",
956
+ nohup: "full",
957
+ stdbuf: "full",
958
+ make: "full",
959
+ codex: "full",
960
+ "codex-exec": "full",
961
+ git: "full",
962
+ "git-remote-http": "full",
963
+ "git-remote-https": "full",
964
+ grep: "read-only",
965
+ egrep: "read-only",
966
+ fgrep: "read-only",
967
+ rg: "read-only",
968
+ cat: "read-only",
969
+ head: "read-only",
970
+ tail: "read-only",
971
+ wc: "read-only",
972
+ sort: "read-only",
973
+ uniq: "read-only",
974
+ diff: "read-only",
975
+ find: "read-only",
976
+ fd: "read-only",
977
+ tree: "read-only",
978
+ file: "read-only",
979
+ du: "read-only",
980
+ ls: "read-only",
981
+ dir: "read-only",
982
+ vdir: "read-only",
983
+ strings: "read-only",
984
+ stat: "read-only",
985
+ rev: "read-only",
986
+ column: "read-only",
987
+ cut: "read-only",
988
+ tr: "read-only",
989
+ paste: "read-only",
990
+ join: "read-only",
991
+ fold: "read-only",
992
+ expand: "read-only",
993
+ nl: "read-only",
994
+ od: "read-only",
995
+ comm: "read-only",
996
+ basename: "read-only",
997
+ dirname: "read-only",
998
+ realpath: "read-only",
999
+ readlink: "read-only",
1000
+ pwd: "read-only",
1001
+ echo: "read-only",
1002
+ envsubst: "read-only",
1003
+ printf: "read-only",
1004
+ true: "read-only",
1005
+ false: "read-only",
1006
+ yes: "read-only",
1007
+ seq: "read-only",
1008
+ test: "read-only",
1009
+ "[": "read-only",
1010
+ expr: "read-only",
1011
+ factor: "read-only",
1012
+ date: "read-only",
1013
+ uname: "read-only",
1014
+ nproc: "read-only",
1015
+ whoami: "read-only",
1016
+ id: "read-only",
1017
+ groups: "read-only",
1018
+ base64: "read-only",
1019
+ md5sum: "read-only",
1020
+ sha256sum: "read-only",
1021
+ tac: "read-only",
1022
+ tsort: "read-only",
1023
+ curl: "full",
1024
+ wget: "full",
1025
+ sqlite3: "read-write",
1026
+ });
1027
+ class NativeRuntimeDescriptor {
1028
+ kind;
1029
+ name;
1030
+ commands;
1031
+ commandDirs;
1032
+ constructor(kind, name, commands, commandDirs) {
1033
+ this.kind = kind;
1034
+ this.name = name;
1035
+ this.commands = commands;
1036
+ this.commandDirs = commandDirs;
1037
+ }
1038
+ }
1039
+ function normalizeCommandLookup(command) {
1040
+ return path.posix.basename(command);
1041
+ }
1042
+ function isWasmBinaryFile(filePath) {
1043
+ try {
1044
+ const header = fsSync.readFileSync(filePath, { encoding: null });
1045
+ return (header.length >= 4 &&
1046
+ header[0] === 0x00 &&
1047
+ header[1] === 0x61 &&
1048
+ header[2] === 0x73 &&
1049
+ header[3] === 0x6d);
1050
+ }
1051
+ catch {
1052
+ return false;
1053
+ }
1054
+ }
1055
+ function discoverWasmCommandEntries(commandDirs) {
1056
+ const discovered = [];
1057
+ const seen = new Set();
1058
+ commandDirs.forEach((commandDir, dirOffset) => {
1059
+ let entries;
1060
+ try {
1061
+ entries = fsSync
1062
+ .readdirSync(commandDir)
1063
+ .sort((left, right) => left.localeCompare(right));
1064
+ }
1065
+ catch {
1066
+ return;
1067
+ }
1068
+ for (const entry of entries) {
1069
+ if (entry.startsWith("."))
1070
+ continue;
1071
+ if (seen.has(entry))
1072
+ continue;
1073
+ const fullPath = path.join(commandDir, entry);
1074
+ if (isWasmBinaryFile(fullPath)) {
1075
+ seen.add(entry);
1076
+ discovered.push({
1077
+ name: entry,
1078
+ hostPath: fullPath,
1079
+ dirOffset,
1080
+ });
1081
+ continue;
1082
+ }
1083
+ try {
1084
+ const realPath = fsSync.realpathSync(fullPath);
1085
+ if (isWasmBinaryFile(realPath)) {
1086
+ seen.add(entry);
1087
+ discovered.push({
1088
+ name: entry,
1089
+ hostPath: fullPath,
1090
+ dirOffset,
1091
+ });
1092
+ }
1093
+ }
1094
+ catch { }
1095
+ }
1096
+ });
1097
+ return discovered;
1098
+ }
1099
+ class WasmVmRuntimeDescriptor {
1100
+ kind = "wasmvm";
1101
+ name = "wasmvm";
1102
+ commands = [];
1103
+ commandDirs;
1104
+ _commandPaths = new Map();
1105
+ _moduleCache = new Map();
1106
+ commandDirOffsets = new Map();
1107
+ constructor(options) {
1108
+ this.commandDirs =
1109
+ options.commandDirs && options.commandDirs.length > 0
1110
+ ? [...options.commandDirs]
1111
+ : undefined;
1112
+ if (options.commandDirs && options.commandDirs.length > 0) {
1113
+ this.refreshDiscovery();
1114
+ return;
1115
+ }
1116
+ this.commands.push(...WASMVM_COMMANDS);
1117
+ if (options.wasmBinaryPath) {
1118
+ console.warn("createWasmVmRuntime({ wasmBinaryPath }) is deprecated; use commandDirs instead.");
1119
+ }
1120
+ }
1121
+ init(_kernel) {
1122
+ if (this.commandDirs && this.commandDirs.length > 0) {
1123
+ this.refreshDiscovery();
1124
+ }
1125
+ }
1126
+ tryResolve(command) {
1127
+ if (!this.commandDirs || this.commandDirs.length === 0) {
1128
+ return false;
1129
+ }
1130
+ const normalized = normalizeCommandLookup(command);
1131
+ if (this._commandPaths.has(normalized)) {
1132
+ return true;
1133
+ }
1134
+ this.refreshDiscovery();
1135
+ return this._commandPaths.has(normalized);
1136
+ }
1137
+ getGuestCommandPaths(startIndex) {
1138
+ const guestPaths = new Map();
1139
+ for (const [name] of this._commandPaths) {
1140
+ const dirOffset = this.commandDirOffsets.get(name);
1141
+ if (dirOffset === undefined) {
1142
+ continue;
1143
+ }
1144
+ guestPaths.set(name, `/__secure_exec/commands/${startIndex + dirOffset}/${name}`);
1145
+ }
1146
+ return guestPaths;
1147
+ }
1148
+ recordModuleExecution(command) {
1149
+ const normalized = normalizeCommandLookup(command);
1150
+ if (this._commandPaths.has(normalized) ||
1151
+ (!this.commandDirs || this.commandDirs.length === 0) &&
1152
+ this.commands.includes(normalized)) {
1153
+ this._moduleCache.set(normalized, true);
1154
+ }
1155
+ }
1156
+ refreshDiscovery() {
1157
+ if (!this.commandDirs || this.commandDirs.length === 0) {
1158
+ return;
1159
+ }
1160
+ const discovered = discoverWasmCommandEntries(this.commandDirs);
1161
+ this.commands.length = 0;
1162
+ this._commandPaths.clear();
1163
+ this.commandDirOffsets.clear();
1164
+ for (const entry of discovered) {
1165
+ this.commands.push(entry.name);
1166
+ this._commandPaths.set(entry.name, entry.hostPath);
1167
+ this.commandDirOffsets.set(entry.name, entry.dirOffset);
1168
+ }
1169
+ }
1170
+ }
1171
+ export function createWasmVmRuntime(options = {}) {
1172
+ return new WasmVmRuntimeDescriptor(options);
1173
+ }
1174
+ export function createNodeRuntime() {
1175
+ return new NativeRuntimeDescriptor("node", "node", ["node", "npm", "npx"]);
1176
+ }
1177
+ function latestMtimeMs(targetPath) {
1178
+ try {
1179
+ const stats = fsSync.statSync(targetPath);
1180
+ if (!stats.isDirectory()) {
1181
+ return stats.mtimeMs;
1182
+ }
1183
+ let latest = stats.mtimeMs;
1184
+ for (const entry of fsSync.readdirSync(targetPath)) {
1185
+ latest = Math.max(latest, latestMtimeMs(path.join(targetPath, entry)));
1186
+ }
1187
+ return latest;
1188
+ }
1189
+ catch {
1190
+ return 0;
1191
+ }
1192
+ }
1193
+ function sidecarBinaryNeedsBuild() {
1194
+ if (!fsSync.existsSync(SIDECAR_BINARY)) {
1195
+ return true;
1196
+ }
1197
+ const binaryMtime = latestMtimeMs(SIDECAR_BINARY);
1198
+ return SIDECAR_BUILD_INPUTS.some((inputPath) => latestMtimeMs(inputPath) > binaryMtime);
1199
+ }
1200
+ function ensureNativeSidecarBinary() {
1201
+ // A published install has no in-repo Cargo workspace to build from: resolve
1202
+ // the prebuilt platform binary (or the SECURE_EXEC_SIDECAR_BIN override).
1203
+ if (process.env.SECURE_EXEC_SIDECAR_BIN ||
1204
+ !fsSync.existsSync(path.join(REPO_ROOT, "Cargo.toml"))) {
1205
+ return resolvePublishedSidecarBinary();
1206
+ }
1207
+ if (ensuredSidecarBinary &&
1208
+ fsSync.existsSync(ensuredSidecarBinary) &&
1209
+ !sidecarBinaryNeedsBuild()) {
1210
+ return ensuredSidecarBinary;
1211
+ }
1212
+ if (sidecarBinaryNeedsBuild()) {
1213
+ const cargoBinary = findCargoBinary();
1214
+ if (cargoBinary) {
1215
+ execFileSync(cargoBinary, ["build", "-q", "-p", "secure-exec-sidecar"], {
1216
+ cwd: REPO_ROOT,
1217
+ stdio: "pipe",
1218
+ });
1219
+ }
1220
+ else if (!fsSync.existsSync(SIDECAR_BINARY)) {
1221
+ execFileSync(resolveCargoBinary(), ["build", "-q", "-p", "secure-exec-sidecar"], {
1222
+ cwd: REPO_ROOT,
1223
+ stdio: "pipe",
1224
+ });
1225
+ }
1226
+ }
1227
+ ensuredSidecarBinary = SIDECAR_BINARY;
1228
+ return ensuredSidecarBinary;
1229
+ }
1230
+ function createBootstrapEntries(commandNames) {
1231
+ const entries = [
1232
+ {
1233
+ path: "/",
1234
+ kind: "directory",
1235
+ mode: 0o755,
1236
+ uid: 0,
1237
+ gid: 0,
1238
+ },
1239
+ ...KERNEL_POSIX_BOOTSTRAP_DIRS.map((entryPath) => ({
1240
+ path: entryPath,
1241
+ kind: "directory",
1242
+ mode: 0o755,
1243
+ uid: 0,
1244
+ gid: 0,
1245
+ })),
1246
+ {
1247
+ path: "/usr/bin/env",
1248
+ kind: "file",
1249
+ mode: 0o644,
1250
+ uid: 0,
1251
+ gid: 0,
1252
+ content: "",
1253
+ encoding: "utf8",
1254
+ },
1255
+ ];
1256
+ for (const command of [...new Set(commandNames)].sort((left, right) => left.localeCompare(right))) {
1257
+ entries.push({
1258
+ path: `/bin/${command}`,
1259
+ kind: "file",
1260
+ mode: 0o755,
1261
+ uid: 0,
1262
+ gid: 0,
1263
+ content: KERNEL_COMMAND_STUB,
1264
+ encoding: "utf8",
1265
+ });
1266
+ }
1267
+ return entries;
1268
+ }
1269
+ function mergeRootFilesystemEntries(baseEntries, overrideEntries) {
1270
+ const merged = new Map();
1271
+ for (const entry of baseEntries) {
1272
+ merged.set(entry.path, entry);
1273
+ }
1274
+ for (const entry of overrideEntries) {
1275
+ merged.set(entry.path, entry);
1276
+ }
1277
+ return [...merged.values()];
1278
+ }
1279
+ async function snapshotFilesystemEntries(filesystem, targetPath = "/", output = [], options) {
1280
+ const passthroughDirectories = options?.passthroughDirectories;
1281
+ const passthroughDirectory = passthroughDirectories?.has(targetPath) ?? false;
1282
+ const statInfo = targetPath === "/" || passthroughDirectory
1283
+ ? await filesystem.stat(targetPath)
1284
+ : await filesystem.lstat(targetPath);
1285
+ if (statInfo.isSymbolicLink) {
1286
+ output.push({
1287
+ path: targetPath,
1288
+ kind: "symlink",
1289
+ mode: statInfo.mode,
1290
+ uid: statInfo.uid,
1291
+ gid: statInfo.gid,
1292
+ target: await filesystem.readlink(targetPath),
1293
+ });
1294
+ return output;
1295
+ }
1296
+ if (statInfo.isDirectory) {
1297
+ output.push({
1298
+ path: targetPath,
1299
+ kind: "directory",
1300
+ mode: statInfo.mode,
1301
+ uid: statInfo.uid,
1302
+ gid: statInfo.gid,
1303
+ });
1304
+ if (passthroughDirectory) {
1305
+ return output;
1306
+ }
1307
+ const children = (await filesystem.readDirWithTypes(targetPath))
1308
+ .map((entry) => entry.name)
1309
+ .filter((name) => name !== "." && name !== "..")
1310
+ .sort((left, right) => left.localeCompare(right));
1311
+ for (const child of children) {
1312
+ const childPath = targetPath === "/"
1313
+ ? posixPath.join("/", child)
1314
+ : posixPath.join(targetPath, child);
1315
+ await snapshotFilesystemEntries(filesystem, childPath, output, options);
1316
+ }
1317
+ return output;
1318
+ }
1319
+ output.push({
1320
+ path: targetPath,
1321
+ kind: "file",
1322
+ mode: statInfo.mode,
1323
+ uid: statInfo.uid,
1324
+ gid: statInfo.gid,
1325
+ content: Buffer.from(await filesystem.readFile(targetPath)).toString("base64"),
1326
+ encoding: "base64",
1327
+ });
1328
+ return output;
1329
+ }
1330
+ async function materializeSnapshotEntriesIntoVm(client, session, vm, entries) {
1331
+ for (const entry of entries) {
1332
+ if (entry.path === "/") {
1333
+ continue;
1334
+ }
1335
+ if (entry.kind === "directory") {
1336
+ await client.mkdir(session, vm, entry.path, { recursive: true });
1337
+ }
1338
+ else if (entry.kind === "file") {
1339
+ await client.writeFile(session, vm, entry.path, decodeRootFilesystemEntryContent(entry));
1340
+ }
1341
+ else {
1342
+ await client.symlink(session, vm, entry.target ?? "", entry.path);
1343
+ continue;
1344
+ }
1345
+ if (typeof entry.mode === "number") {
1346
+ await client.chmod(session, vm, entry.path, entry.mode);
1347
+ }
1348
+ if (typeof entry.uid === "number" && typeof entry.gid === "number") {
1349
+ await client.chown(session, vm, entry.path, entry.uid, entry.gid);
1350
+ }
1351
+ }
1352
+ }
1353
+ function decodeRootFilesystemEntryContent(entry) {
1354
+ const content = entry.content ?? "";
1355
+ if (entry.encoding === "base64") {
1356
+ return new Uint8Array(Buffer.from(content, "base64"));
1357
+ }
1358
+ return new TextEncoder().encode(content);
1359
+ }
1360
+ const NODE_FILESYSTEM_ROOT_PASSTHROUGH_DIRS = ["node_modules"];
1361
+ function planNodeFilesystemPassthroughMounts(filesystem, existingMounts) {
1362
+ if (!(filesystem instanceof NodeFileSystem)) {
1363
+ return {
1364
+ mounts: [],
1365
+ passthroughDirectories: new Set(),
1366
+ };
1367
+ }
1368
+ const passthroughDirectories = new Set();
1369
+ const existingGuestPaths = new Set(existingMounts.map((mount) => mount.path));
1370
+ const mounts = [];
1371
+ for (const directoryName of NODE_FILESYSTEM_ROOT_PASSTHROUGH_DIRS) {
1372
+ const guestPath = normalizePath(`/${directoryName}`);
1373
+ const hostPath = path.join(filesystem.rootPath, directoryName);
1374
+ let statInfo;
1375
+ try {
1376
+ statInfo = fsSync.statSync(hostPath);
1377
+ }
1378
+ catch {
1379
+ continue;
1380
+ }
1381
+ if (!statInfo.isDirectory()) {
1382
+ continue;
1383
+ }
1384
+ passthroughDirectories.add(guestPath);
1385
+ if (existingGuestPaths.has(guestPath)) {
1386
+ continue;
1387
+ }
1388
+ mounts.push({
1389
+ path: guestPath,
1390
+ fs: new NodeFileSystem({ root: hostPath }),
1391
+ readOnly: true,
1392
+ });
1393
+ }
1394
+ return {
1395
+ mounts,
1396
+ passthroughDirectories,
1397
+ };
1398
+ }
1399
+ function collectGuestCommandPaths(commandDirs, startIndex = 0) {
1400
+ const guestPaths = new Map();
1401
+ for (const entry of discoverWasmCommandEntries(commandDirs)) {
1402
+ if (!guestPaths.has(entry.name)) {
1403
+ guestPaths.set(entry.name, `/__secure_exec/commands/${startIndex + entry.dirOffset}/${entry.name}`);
1404
+ }
1405
+ }
1406
+ return guestPaths;
1407
+ }
1408
+ async function ensureCommandStubs(proxy, commands) {
1409
+ const rootView = proxy.createRootView();
1410
+ for (const command of commands) {
1411
+ const stubPath = `/bin/${command}`;
1412
+ await rootView.writeFile(stubPath, KERNEL_COMMAND_STUB);
1413
+ await rootView.chmod(stubPath, 0o755);
1414
+ }
1415
+ }
1416
+ class DeferredFileSystem {
1417
+ getFilesystem;
1418
+ constructor(getFilesystem) {
1419
+ this.getFilesystem = getFilesystem;
1420
+ }
1421
+ filesystem() {
1422
+ const filesystem = this.getFilesystem();
1423
+ if (!filesystem) {
1424
+ throw new Error("kernel filesystem is not ready; mount a runtime first");
1425
+ }
1426
+ return filesystem;
1427
+ }
1428
+ readFile(path) {
1429
+ return this.filesystem().readFile(path);
1430
+ }
1431
+ readTextFile(path) {
1432
+ return this.filesystem().readTextFile(path);
1433
+ }
1434
+ readDir(path) {
1435
+ return this.filesystem().readDir(path);
1436
+ }
1437
+ readDirWithTypes(path) {
1438
+ return this.filesystem().readDirWithTypes(path);
1439
+ }
1440
+ writeFile(path, content) {
1441
+ return this.filesystem().writeFile(path, content);
1442
+ }
1443
+ createDir(path) {
1444
+ return this.filesystem().createDir(path);
1445
+ }
1446
+ mkdir(path, options) {
1447
+ return this.filesystem().mkdir(path, options);
1448
+ }
1449
+ exists(path) {
1450
+ return this.filesystem().exists(path);
1451
+ }
1452
+ stat(path) {
1453
+ return this.filesystem().stat(path);
1454
+ }
1455
+ removeFile(path) {
1456
+ return this.filesystem().removeFile(path);
1457
+ }
1458
+ removeDir(path) {
1459
+ return this.filesystem().removeDir(path);
1460
+ }
1461
+ rename(oldPath, newPath) {
1462
+ return this.filesystem().rename(oldPath, newPath);
1463
+ }
1464
+ realpath(path) {
1465
+ return this.filesystem().realpath(path);
1466
+ }
1467
+ symlink(target, linkPath) {
1468
+ return this.filesystem().symlink(target, linkPath);
1469
+ }
1470
+ readlink(path) {
1471
+ return this.filesystem().readlink(path);
1472
+ }
1473
+ lstat(path) {
1474
+ return this.filesystem().lstat(path);
1475
+ }
1476
+ link(oldPath, newPath) {
1477
+ return this.filesystem().link(oldPath, newPath);
1478
+ }
1479
+ chmod(path, mode) {
1480
+ return this.filesystem().chmod(path, mode);
1481
+ }
1482
+ chown(path, uid, gid) {
1483
+ return this.filesystem().chown(path, uid, gid);
1484
+ }
1485
+ utimes(path, atime, mtime) {
1486
+ return this.filesystem().utimes(path, atime, mtime);
1487
+ }
1488
+ truncate(path, length) {
1489
+ return this.filesystem().truncate(path, length);
1490
+ }
1491
+ pread(path, offset, length) {
1492
+ return this.filesystem().pread(path, offset, length);
1493
+ }
1494
+ pwrite(path, offset, data) {
1495
+ return this.filesystem().pwrite(path, offset, data);
1496
+ }
1497
+ }
1498
+ const VIRTUAL_FILESYSTEM_METHOD_NAMES = [
1499
+ "readFile",
1500
+ "readTextFile",
1501
+ "readDir",
1502
+ "readDirWithTypes",
1503
+ "writeFile",
1504
+ "createDir",
1505
+ "mkdir",
1506
+ "exists",
1507
+ "stat",
1508
+ "removeFile",
1509
+ "removeDir",
1510
+ "rename",
1511
+ "realpath",
1512
+ "symlink",
1513
+ "readlink",
1514
+ "lstat",
1515
+ "link",
1516
+ "chmod",
1517
+ "chown",
1518
+ "utimes",
1519
+ "truncate",
1520
+ "pread",
1521
+ "pwrite",
1522
+ ];
1523
+ const LIVE_FILESYSTEM_SYNC_CHUNK_SIZE = 512 * 1024;
1524
+ function topLevelSyncRoot(targetPath) {
1525
+ const normalized = normalizePath(targetPath);
1526
+ const [first] = normalized.split("/").filter(Boolean);
1527
+ return first ? `/${first}` : "/";
1528
+ }
1529
+ function collectLiveFilesystemSyncRoots(entries) {
1530
+ const roots = new Set();
1531
+ for (const entry of entries) {
1532
+ if (entry.path === "/") {
1533
+ continue;
1534
+ }
1535
+ roots.add(topLevelSyncRoot(entry.path));
1536
+ }
1537
+ return [...roots].sort((left, right) => left.localeCompare(right));
1538
+ }
1539
+ async function callBoundFilesystemMethod(methods, method, ...args) {
1540
+ const delegate = methods[method];
1541
+ if (!delegate) {
1542
+ throw new Error(`filesystem method ${method} is unavailable`);
1543
+ }
1544
+ return (await delegate(...args));
1545
+ }
1546
+ async function ensureBoundParentDirectory(methods, targetPath) {
1547
+ const parent = dirnameVirtual(targetPath);
1548
+ if (parent === targetPath) {
1549
+ return;
1550
+ }
1551
+ await callBoundFilesystemMethod(methods, "mkdir", parent, { recursive: true });
1552
+ }
1553
+ async function syncLiveFilesystemToBoundMethods(live, methods, paths) {
1554
+ for (const targetPath of [...new Set(paths.map(normalizePath))].sort((left, right) => left.localeCompare(right))) {
1555
+ if (!(await live.exists(targetPath).catch(() => false))) {
1556
+ continue;
1557
+ }
1558
+ await syncLiveFilesystemPathToBoundMethods(live, methods, targetPath);
1559
+ }
1560
+ }
1561
+ async function syncLiveFilesystemPathToBoundMethods(live, methods, targetPath) {
1562
+ const stat = targetPath === "/" ? await live.stat(targetPath) : await live.lstat(targetPath);
1563
+ if (stat.isSymbolicLink) {
1564
+ await ensureBoundParentDirectory(methods, targetPath);
1565
+ await callBoundFilesystemMethod(methods, "removeFile", targetPath).catch(() => { });
1566
+ await callBoundFilesystemMethod(methods, "symlink", await live.readlink(targetPath), targetPath);
1567
+ return;
1568
+ }
1569
+ if (stat.isDirectory) {
1570
+ await callBoundFilesystemMethod(methods, "mkdir", targetPath, {
1571
+ recursive: true,
1572
+ });
1573
+ const children = (await live.readDirWithTypes(targetPath))
1574
+ .map((entry) => entry.name)
1575
+ .filter((name) => name !== "." && name !== "..")
1576
+ .sort((left, right) => left.localeCompare(right));
1577
+ for (const child of children) {
1578
+ await syncLiveFilesystemPathToBoundMethods(live, methods, targetPath === "/" ? posixPath.join("/", child) : posixPath.join(targetPath, child));
1579
+ }
1580
+ return;
1581
+ }
1582
+ await ensureBoundParentDirectory(methods, targetPath);
1583
+ await callBoundFilesystemMethod(methods, "writeFile", targetPath, new Uint8Array(0));
1584
+ for (let offset = 0; offset < stat.size; offset += LIVE_FILESYSTEM_SYNC_CHUNK_SIZE) {
1585
+ const chunk = await live.pread(targetPath, offset, Math.min(LIVE_FILESYSTEM_SYNC_CHUNK_SIZE, stat.size - offset));
1586
+ if (chunk.length === 0) {
1587
+ break;
1588
+ }
1589
+ await callBoundFilesystemMethod(methods, "pwrite", targetPath, offset, chunk);
1590
+ }
1591
+ }
1592
+ function bindLiveFilesystem(target, getFilesystem) {
1593
+ const fallback = {};
1594
+ for (const method of VIRTUAL_FILESYSTEM_METHOD_NAMES) {
1595
+ const candidate = target[method];
1596
+ if (typeof candidate === "function") {
1597
+ fallback[method] = candidate.bind(target);
1598
+ }
1599
+ }
1600
+ for (const method of VIRTUAL_FILESYSTEM_METHOD_NAMES) {
1601
+ target[method] = (...args) => {
1602
+ const filesystem = getFilesystem();
1603
+ const delegate = filesystem
1604
+ ? filesystem[method].bind(filesystem)
1605
+ : fallback[method];
1606
+ if (!delegate) {
1607
+ throw new Error(`kernel filesystem is not ready; mount a runtime before calling ${method}()`);
1608
+ }
1609
+ return delegate(...args);
1610
+ };
1611
+ }
1612
+ return {
1613
+ async syncFromLive(paths) {
1614
+ const filesystem = getFilesystem();
1615
+ if (!filesystem) {
1616
+ return;
1617
+ }
1618
+ await syncLiveFilesystemToBoundMethods(filesystem, fallback, paths);
1619
+ },
1620
+ restore() {
1621
+ for (const [method, delegate] of Object.entries(fallback)) {
1622
+ target[method] = delegate;
1623
+ }
1624
+ },
1625
+ };
1626
+ }
1627
+ class NativeKernel {
1628
+ options;
1629
+ env;
1630
+ cwd;
1631
+ commands = new Map();
1632
+ processes = new Map();
1633
+ socketTable;
1634
+ processTable;
1635
+ timerTable = {};
1636
+ vfs;
1637
+ client = null;
1638
+ session = null;
1639
+ vm = null;
1640
+ proxy = null;
1641
+ rootFilesystem = null;
1642
+ readyPromise = null;
1643
+ liveFilesystemBinding;
1644
+ liveFilesystemSyncRoots = [];
1645
+ pendingLocalMounts = [];
1646
+ mountedCommandDirs = [];
1647
+ mountedRuntimeDrivers = [];
1648
+ runtimeDriverCommandDirStarts = new Map();
1649
+ loopbackExemptPorts;
1650
+ // Host tools registered with the VM, keyed by the callback key the sidecar
1651
+ // sends back on a host_callback request (the tool name). Installed lazily on
1652
+ // the first registerHostTools call.
1653
+ hostToolHandlers = new Map();
1654
+ hostToolRequestHandlerInstalled = false;
1655
+ constructor(options) {
1656
+ this.options = options;
1657
+ this.env = { ...(options.env ?? {}) };
1658
+ this.cwd = options.cwd ?? "/home/user";
1659
+ this.socketTable = {
1660
+ hasHostNetworkAdapter: () => Boolean(options.hostNetworkAdapter),
1661
+ findListener: (request) => this.proxy?.findListener(request) ?? null,
1662
+ findBoundUdp: (request) => this.proxy?.findBoundUdp(request) ?? null,
1663
+ };
1664
+ this.processTable = {
1665
+ getSignalState: (pid) => this.proxy?.getSignalState(pid) ?? {
1666
+ handlers: new Map(),
1667
+ },
1668
+ };
1669
+ this.loopbackExemptPorts = [...(options.loopbackExemptPorts ?? [])];
1670
+ for (const mount of options.mounts ?? []) {
1671
+ this.pendingLocalMounts.push({
1672
+ path: normalizePath(mount.path),
1673
+ fs: mount.fs,
1674
+ readOnly: mount.readOnly ?? false,
1675
+ });
1676
+ }
1677
+ this.vfs = new DeferredFileSystem(() => this.rootFilesystem);
1678
+ this.liveFilesystemBinding = bindLiveFilesystem(this.options.filesystem, () => this.rootFilesystem);
1679
+ }
1680
+ get zombieTimerCount() {
1681
+ return this.proxy?.zombieTimerCount ?? 0;
1682
+ }
1683
+ async mount(driver) {
1684
+ await this.ensureReady();
1685
+ if (!this.proxy || !this.client || !this.session || !this.vm) {
1686
+ throw new Error("kernel is not ready");
1687
+ }
1688
+ await driver.init?.(this);
1689
+ if (driver.kind === "node") {
1690
+ for (const command of driver.commands) {
1691
+ this.commands.set(command, "node");
1692
+ }
1693
+ this.mountedRuntimeDrivers.push(driver);
1694
+ await ensureCommandStubs(this.proxy, driver.commands);
1695
+ return;
1696
+ }
1697
+ const commandDirs = driver.commandDirs ?? [];
1698
+ if (commandDirs.length === 0) {
1699
+ for (const command of driver.commands) {
1700
+ this.commands.set(command, "wasmvm");
1701
+ }
1702
+ this.mountedRuntimeDrivers.push(driver);
1703
+ await ensureCommandStubs(this.proxy, driver.commands);
1704
+ return;
1705
+ }
1706
+ const startIndex = this.mountedCommandDirs.length;
1707
+ const newGuestPaths = driver.getGuestCommandPaths?.(startIndex) ??
1708
+ collectGuestCommandPaths(commandDirs, startIndex);
1709
+ const allCommandDirs = [...this.mountedCommandDirs, ...commandDirs];
1710
+ const sidecarMounts = allCommandDirs.map((commandDir, index) => serializeMountConfigForSidecar({
1711
+ path: `/__secure_exec/commands/${index}`,
1712
+ readOnly: true,
1713
+ plugin: {
1714
+ id: "host_dir",
1715
+ config: {
1716
+ hostPath: commandDir,
1717
+ readOnly: true,
1718
+ },
1719
+ },
1720
+ }));
1721
+ const localMounts = this.pendingLocalMounts.map((mount) => mount.fs instanceof NodeFileSystem
1722
+ ? serializeMountConfigForSidecar({
1723
+ path: mount.path,
1724
+ readOnly: mount.readOnly,
1725
+ plugin: {
1726
+ id: "host_dir",
1727
+ config: {
1728
+ hostPath: mount.fs.rootPath,
1729
+ readOnly: mount.readOnly,
1730
+ },
1731
+ },
1732
+ })
1733
+ : serializeMountConfigForSidecar({
1734
+ path: mount.path,
1735
+ driver: mount.fs,
1736
+ readOnly: mount.readOnly,
1737
+ }));
1738
+ await this.client.configureVm(this.session, this.vm, {
1739
+ mounts: [...localMounts, ...sidecarMounts],
1740
+ loopbackExemptPorts: this.loopbackExemptPorts,
1741
+ });
1742
+ this.proxy.registerCommandGuestPaths(newGuestPaths);
1743
+ this.mountedCommandDirs.push(...commandDirs);
1744
+ this.mountedRuntimeDrivers.push(driver);
1745
+ this.runtimeDriverCommandDirStarts.set(driver, startIndex);
1746
+ for (const command of newGuestPaths.keys()) {
1747
+ this.commands.set(command, "wasmvm");
1748
+ }
1749
+ await ensureCommandStubs(this.proxy, newGuestPaths.keys());
1750
+ }
1751
+ async dispose() {
1752
+ await this.readyPromise?.catch(() => { });
1753
+ let syncError;
1754
+ if (this.options.syncFilesystemOnDispose !== false &&
1755
+ this.rootFilesystem &&
1756
+ !(this.options.filesystem instanceof NodeFileSystem)) {
1757
+ try {
1758
+ await this.liveFilesystemBinding.syncFromLive(this.liveFilesystemSyncRoots);
1759
+ }
1760
+ catch (error) {
1761
+ syncError = error;
1762
+ }
1763
+ }
1764
+ try {
1765
+ await this.proxy?.dispose().catch(() => { });
1766
+ }
1767
+ finally {
1768
+ this.proxy = null;
1769
+ this.rootFilesystem = null;
1770
+ this.client = null;
1771
+ this.session = null;
1772
+ this.vm = null;
1773
+ this.liveFilesystemBinding.restore();
1774
+ }
1775
+ if (syncError) {
1776
+ throw syncError;
1777
+ }
1778
+ }
1779
+ async exec(command, options) {
1780
+ await this.ensureReady();
1781
+ if (!this.proxy) {
1782
+ throw new Error("kernel is not ready");
1783
+ }
1784
+ return this.proxy.exec(command, options);
1785
+ }
1786
+ spawn(command, args, options) {
1787
+ if (!this.proxy) {
1788
+ throw new Error("kernel is not ready; await kernel.mount(...) first");
1789
+ }
1790
+ const normalized = normalizeCommandLookup(command);
1791
+ const knownCommand = this.commands.has(command) || this.commands.has(normalized);
1792
+ if (!knownCommand && !this.tryResolveMountedCommand(command)) {
1793
+ throw new Error(`ENOENT: command not found: ${command}`);
1794
+ }
1795
+ const proc = this.proxy.spawn(command, args, options);
1796
+ const syncProcessSnapshot = () => {
1797
+ const snapshot = this.proxy?.processes.get(proc.pid);
1798
+ if (!snapshot) {
1799
+ return;
1800
+ }
1801
+ this.processes.set(proc.pid, {
1802
+ ...snapshot,
1803
+ args: [...snapshot.args],
1804
+ });
1805
+ };
1806
+ syncProcessSnapshot();
1807
+ return {
1808
+ pid: proc.pid,
1809
+ writeStdin(data) {
1810
+ proc.writeStdin(data);
1811
+ },
1812
+ closeStdin() {
1813
+ proc.closeStdin();
1814
+ },
1815
+ kill(signal) {
1816
+ proc.kill(signal);
1817
+ syncProcessSnapshot();
1818
+ },
1819
+ async wait() {
1820
+ const exitCode = await proc.wait();
1821
+ syncProcessSnapshot();
1822
+ return exitCode;
1823
+ },
1824
+ get exitCode() {
1825
+ return proc.exitCode;
1826
+ },
1827
+ };
1828
+ }
1829
+ openShell(options) {
1830
+ if (!this.proxy) {
1831
+ throw new Error("kernel is not ready; await kernel.mount(...) first");
1832
+ }
1833
+ return this.proxy.openShell(options);
1834
+ }
1835
+ async connectTerminal(options) {
1836
+ await this.ensureReady();
1837
+ if (!this.proxy) {
1838
+ throw new Error("kernel is not ready");
1839
+ }
1840
+ return this.proxy.connectTerminal(options);
1841
+ }
1842
+ mountFs(mountPath, filesystem, options) {
1843
+ if (!this.proxy) {
1844
+ this.pendingLocalMounts.push({
1845
+ path: normalizePath(mountPath),
1846
+ fs: filesystem,
1847
+ readOnly: options?.readOnly ?? false,
1848
+ });
1849
+ return;
1850
+ }
1851
+ this.proxy.mountFs(mountPath, filesystem, options);
1852
+ }
1853
+ unmountFs(mountPath) {
1854
+ this.proxy?.unmountFs(mountPath);
1855
+ }
1856
+ async readFile(targetPath) {
1857
+ await this.ensureReady();
1858
+ return this.proxy.readFile(targetPath);
1859
+ }
1860
+ async vmFetch(request) {
1861
+ await this.ensureReady();
1862
+ return this.proxy.vmFetch(request);
1863
+ }
1864
+ async registerHostTools(tools) {
1865
+ await this.ensureReady();
1866
+ if (!this.client || !this.session || !this.vm) {
1867
+ throw new Error("kernel is not ready");
1868
+ }
1869
+ // Install the dispatcher once. It routes every host_callback request the
1870
+ // sidecar emits to the matching registered handler and replies with a
1871
+ // host_callback_result frame.
1872
+ if (!this.hostToolRequestHandlerInstalled) {
1873
+ this.client.setSidecarRequestHandler((request) => this.dispatchHostToolRequest(request));
1874
+ this.hostToolRequestHandlerInstalled = true;
1875
+ }
1876
+ for (const [name, tool] of Object.entries(tools)) {
1877
+ this.hostToolHandlers.set(name, tool.handler);
1878
+ const definition = {
1879
+ description: tool.description,
1880
+ inputSchema: tool.inputSchema,
1881
+ ...(tool.timeoutMs !== undefined ? { timeoutMs: tool.timeoutMs } : {}),
1882
+ ...(tool.examples && tool.examples.length > 0
1883
+ ? {
1884
+ examples: tool.examples.map((example) => ({
1885
+ description: example.description,
1886
+ input: example.input,
1887
+ })),
1888
+ }
1889
+ : {}),
1890
+ };
1891
+ // Register each tool as its own single-tool toolkit so the guest can
1892
+ // invoke it directly by name (or by any caller-provided alias). The
1893
+ // sidecar exposes the toolkit name as a guest command; the single
1894
+ // callback carries the tool's schema and gates the `tool` permission.
1895
+ await this.client.registerHostCallbacks(this.session, this.vm, {
1896
+ name,
1897
+ description: tool.description,
1898
+ commandAliases: [name, ...(tool.commandAliases ?? [])],
1899
+ callbacks: { [name]: definition },
1900
+ });
1901
+ this.commands.set(name, "wasmvm");
1902
+ for (const alias of tool.commandAliases ?? []) {
1903
+ this.commands.set(alias, "wasmvm");
1904
+ }
1905
+ }
1906
+ }
1907
+ async dispatchHostToolRequest(request) {
1908
+ const { payload } = request;
1909
+ if (payload.type !== "host_callback") {
1910
+ throw new Error(`unsupported sidecar request for host tools: ${payload.type}`);
1911
+ }
1912
+ // Callback keys arrive as `${toolkit}:${tool}` for toolkit invocations and
1913
+ // as the bare command name otherwise. The toolkit name and tool name are
1914
+ // the same here, so the registered tool name is the segment after the last
1915
+ // colon (or the whole key when no colon is present).
1916
+ const callbackKey = payload.callback_key;
1917
+ const toolName = callbackKey.includes(":")
1918
+ ? callbackKey.slice(callbackKey.lastIndexOf(":") + 1)
1919
+ : callbackKey;
1920
+ const handler = this.hostToolHandlers.get(toolName) ??
1921
+ this.hostToolHandlers.get(callbackKey);
1922
+ if (!handler) {
1923
+ return {
1924
+ type: "host_callback_result",
1925
+ invocation_id: payload.invocation_id,
1926
+ error: `no host tool registered for ${callbackKey}`,
1927
+ };
1928
+ }
1929
+ try {
1930
+ const result = await handler(payload.input);
1931
+ return {
1932
+ type: "host_callback_result",
1933
+ invocation_id: payload.invocation_id,
1934
+ result: result === undefined ? null : result,
1935
+ };
1936
+ }
1937
+ catch (error) {
1938
+ return {
1939
+ type: "host_callback_result",
1940
+ invocation_id: payload.invocation_id,
1941
+ error: error instanceof Error ? error.message : String(error),
1942
+ };
1943
+ }
1944
+ }
1945
+ async writeFile(targetPath, content) {
1946
+ await this.ensureReady();
1947
+ return this.proxy.writeFile(targetPath, content);
1948
+ }
1949
+ async mkdir(targetPath) {
1950
+ await this.ensureReady();
1951
+ return this.proxy.mkdir(targetPath);
1952
+ }
1953
+ async readdir(targetPath) {
1954
+ await this.ensureReady();
1955
+ return this.proxy.readdir(targetPath);
1956
+ }
1957
+ async stat(targetPath) {
1958
+ await this.ensureReady();
1959
+ return this.proxy.stat(targetPath);
1960
+ }
1961
+ async exists(targetPath) {
1962
+ await this.ensureReady();
1963
+ return this.proxy.exists(targetPath);
1964
+ }
1965
+ async removeFile(targetPath) {
1966
+ await this.ensureReady();
1967
+ return this.proxy.removeFile(targetPath);
1968
+ }
1969
+ async removeDir(targetPath) {
1970
+ await this.ensureReady();
1971
+ return this.proxy.removeDir(targetPath);
1972
+ }
1973
+ async rename(oldPath, newPath) {
1974
+ await this.ensureReady();
1975
+ return this.proxy.rename(oldPath, newPath);
1976
+ }
1977
+ tryResolveMountedCommand(command) {
1978
+ const normalized = normalizeCommandLookup(command);
1979
+ for (const driver of this.mountedRuntimeDrivers) {
1980
+ if (!driver.tryResolve?.(command)) {
1981
+ continue;
1982
+ }
1983
+ this.commands.set(normalized, driver.kind);
1984
+ if (driver.kind === "wasmvm" && this.proxy) {
1985
+ const startIndex = this.runtimeDriverCommandDirStarts.get(driver);
1986
+ if (startIndex !== undefined) {
1987
+ const guestPaths = driver.getGuestCommandPaths?.(startIndex);
1988
+ if (guestPaths?.has(normalized)) {
1989
+ this.proxy.registerCommandGuestPaths(new Map([[normalized, guestPaths.get(normalized)]]));
1990
+ }
1991
+ }
1992
+ }
1993
+ return true;
1994
+ }
1995
+ return false;
1996
+ }
1997
+ recordModuleExecution(command) {
1998
+ for (const driver of this.mountedRuntimeDrivers) {
1999
+ driver.recordModuleExecution?.(command);
2000
+ }
2001
+ }
2002
+ async ensureReady() {
2003
+ if (!this.readyPromise) {
2004
+ this.readyPromise = this.initialize();
2005
+ }
2006
+ return this.readyPromise;
2007
+ }
2008
+ async initialize() {
2009
+ const createVmEnv = { ...this.env };
2010
+ const requestedPermissions = this.options.permissions;
2011
+ const bootstrapPermissions = requestedPermissions
2012
+ ? normalizePermissionsPolicy(allowAll)
2013
+ : undefined;
2014
+ if (this.loopbackExemptPorts.length > 0) {
2015
+ createVmEnv.SECURE_EXEC_LOOPBACK_EXEMPT_PORTS = JSON.stringify(this.loopbackExemptPorts);
2016
+ }
2017
+ const rootPassthroughPlan = planNodeFilesystemPassthroughMounts(this.options.filesystem, this.pendingLocalMounts);
2018
+ const snapshotEntries = await snapshotFilesystemEntries(this.options.filesystem, "/", [], {
2019
+ passthroughDirectories: rootPassthroughPlan.passthroughDirectories,
2020
+ });
2021
+ this.liveFilesystemSyncRoots =
2022
+ collectLiveFilesystemSyncRoots(snapshotEntries);
2023
+ const rootFilesystem = {
2024
+ mode: "ephemeral",
2025
+ disableDefaultBaseLayer: true,
2026
+ lowers: [
2027
+ {
2028
+ kind: "snapshot",
2029
+ entries: mergeRootFilesystemEntries(createBootstrapEntries([...NODE_RUNTIME_BOOTSTRAP_COMMANDS]), snapshotEntries).map((entry) => ({
2030
+ ...entry,
2031
+ executable: entry.executable ?? false,
2032
+ })),
2033
+ },
2034
+ ],
2035
+ bootstrapEntries: [],
2036
+ };
2037
+ const client = NativeSidecarProcessClient.spawn({
2038
+ cwd: REPO_ROOT,
2039
+ command: ensureNativeSidecarBinary(),
2040
+ args: [],
2041
+ frameTimeoutMs: NATIVE_SIDECAR_FRAME_TIMEOUT_MS,
2042
+ });
2043
+ const session = await client.authenticateAndOpenSession();
2044
+ const vm = await client.createVm(session, {
2045
+ runtime: "java_script",
2046
+ config: {
2047
+ env: createVmEnv,
2048
+ rootFilesystem,
2049
+ ...(bootstrapPermissions ? { permissions: bootstrapPermissions } : {}),
2050
+ loopbackExemptPorts: this.loopbackExemptPorts,
2051
+ },
2052
+ });
2053
+ await client.waitForEvent({
2054
+ type: "vm_lifecycle",
2055
+ ownership: {
2056
+ scope: "vm",
2057
+ connection_id: session.connectionId,
2058
+ session_id: session.sessionId,
2059
+ vm_id: vm.vmId,
2060
+ },
2061
+ state: "ready",
2062
+ }, 10_000);
2063
+ if (requestedPermissions && snapshotEntries.length > 1) {
2064
+ await materializeSnapshotEntriesIntoVm(client, session, vm, snapshotEntries);
2065
+ }
2066
+ if (rootPassthroughPlan.mounts.length > 0) {
2067
+ this.pendingLocalMounts.push(...rootPassthroughPlan.mounts);
2068
+ }
2069
+ if (this.pendingLocalMounts.length > 0 ||
2070
+ this.loopbackExemptPorts.length > 0 ||
2071
+ requestedPermissions) {
2072
+ await client.configureVm(session, vm, {
2073
+ mounts: this.pendingLocalMounts.map((mount) => mount.fs instanceof NodeFileSystem
2074
+ ? serializeMountConfigForSidecar({
2075
+ path: mount.path,
2076
+ readOnly: mount.readOnly,
2077
+ plugin: {
2078
+ id: "host_dir",
2079
+ config: {
2080
+ hostPath: mount.fs.rootPath,
2081
+ readOnly: mount.readOnly,
2082
+ },
2083
+ },
2084
+ })
2085
+ : serializeMountConfigForSidecar({
2086
+ path: mount.path,
2087
+ driver: mount.fs,
2088
+ readOnly: mount.readOnly,
2089
+ })),
2090
+ permissions: requestedPermissions,
2091
+ loopbackExemptPorts: this.loopbackExemptPorts,
2092
+ });
2093
+ }
2094
+ const proxy = new NativeSidecarKernelProxy({
2095
+ client,
2096
+ session,
2097
+ vm,
2098
+ env: this.env,
2099
+ cwd: this.cwd,
2100
+ defaultExecCwd: this.options.cwd === undefined ? "/home/user" : this.cwd,
2101
+ localMounts: this.pendingLocalMounts,
2102
+ commandGuestPaths: new Map(),
2103
+ onWasmCommandResolved: (command) => {
2104
+ this.recordModuleExecution(command);
2105
+ },
2106
+ });
2107
+ this.client = client;
2108
+ this.session = session;
2109
+ this.vm = vm;
2110
+ this.proxy = proxy;
2111
+ this.rootFilesystem = proxy.createRootView();
2112
+ }
2113
+ }
2114
+ export function createKernel(options) {
2115
+ return new NativeKernel(options);
2116
+ }
2117
+ function errnoError(code, message) {
2118
+ return new KernelError(code, `${code}: ${message}`);
2119
+ }