@secure-exec/core 0.2.0-rc.1 → 0.2.0

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 (60) hide show
  1. package/dist/generated/isolate-runtime.d.ts +2 -2
  2. package/dist/generated/isolate-runtime.js +2 -2
  3. package/dist/index.d.ts +17 -4
  4. package/dist/index.js +10 -2
  5. package/dist/isolate-runtime/require-setup.js +1489 -239
  6. package/dist/isolate-runtime/setup-dynamic-import.js +31 -0
  7. package/dist/kernel/device-backend.d.ts +14 -0
  8. package/dist/kernel/device-backend.js +251 -0
  9. package/dist/kernel/device-layer.js +9 -0
  10. package/dist/kernel/file-lock.js +2 -3
  11. package/dist/kernel/index.d.ts +4 -4
  12. package/dist/kernel/index.js +3 -3
  13. package/dist/kernel/kernel.js +141 -122
  14. package/dist/kernel/mount-table.d.ts +75 -0
  15. package/dist/kernel/mount-table.js +353 -0
  16. package/dist/kernel/permissions.d.ts +9 -0
  17. package/dist/kernel/permissions.js +33 -1
  18. package/dist/kernel/proc-backend.d.ts +30 -0
  19. package/dist/kernel/proc-backend.js +428 -0
  20. package/dist/kernel/proc-layer.js +6 -0
  21. package/dist/kernel/process-table.d.ts +3 -1
  22. package/dist/kernel/process-table.js +23 -3
  23. package/dist/kernel/pty.d.ts +3 -2
  24. package/dist/kernel/pty.js +13 -2
  25. package/dist/kernel/socket-table.d.ts +7 -0
  26. package/dist/kernel/socket-table.js +99 -35
  27. package/dist/kernel/types.d.ts +45 -4
  28. package/dist/kernel/types.js +9 -0
  29. package/dist/kernel/vfs.d.ts +30 -2
  30. package/dist/kernel/vfs.js +19 -2
  31. package/dist/shared/api-types.d.ts +6 -0
  32. package/dist/shared/bridge-contract.d.ts +21 -3
  33. package/dist/shared/bridge-contract.js +2 -0
  34. package/dist/shared/console-formatter.js +8 -8
  35. package/dist/shared/global-exposure.js +95 -0
  36. package/dist/shared/in-memory-fs.d.ts +14 -59
  37. package/dist/shared/in-memory-fs.js +97 -597
  38. package/dist/shared/permissions.js +5 -0
  39. package/dist/test/block-store-conformance.d.ts +34 -0
  40. package/dist/test/block-store-conformance.js +251 -0
  41. package/dist/test/metadata-store-conformance.d.ts +37 -0
  42. package/dist/test/metadata-store-conformance.js +646 -0
  43. package/dist/test/vfs-conformance.d.ts +65 -0
  44. package/dist/test/vfs-conformance.js +842 -0
  45. package/dist/types.d.ts +1 -0
  46. package/dist/vfs/chunked-vfs.d.ts +66 -0
  47. package/dist/vfs/chunked-vfs.js +1290 -0
  48. package/dist/vfs/host-block-store.d.ts +19 -0
  49. package/dist/vfs/host-block-store.js +97 -0
  50. package/dist/vfs/memory-block-store.d.ts +16 -0
  51. package/dist/vfs/memory-block-store.js +45 -0
  52. package/dist/vfs/memory-metadata.d.ts +75 -0
  53. package/dist/vfs/memory-metadata.js +528 -0
  54. package/dist/vfs/sqlite-metadata.d.ts +91 -0
  55. package/dist/vfs/sqlite-metadata.js +582 -0
  56. package/dist/vfs/types.d.ts +210 -0
  57. package/dist/vfs/types.js +8 -0
  58. package/package.json +20 -1
  59. package/dist/kernel/inode-table.d.ts +0 -43
  60. package/dist/kernel/inode-table.js +0 -85
@@ -31,6 +31,7 @@
31
31
  var __dynamicImportConfig = globalThis.__runtimeDynamicImportConfig ?? {};
32
32
  var __fallbackReferrer = typeof __dynamicImportConfig.referrerPath === "string" && __dynamicImportConfig.referrerPath.length > 0 ? __dynamicImportConfig.referrerPath : "/";
33
33
  var __dynamicImportCache = /* @__PURE__ */ new Map();
34
+ var __pathToFileURL = typeof globalThis.require === "function" ? globalThis.require("node:url").pathToFileURL ?? null : null;
34
35
  var __resolveDynamicImportPath = function(request, referrer) {
35
36
  if (!request.startsWith("./") && !request.startsWith("../") && !request.startsWith("/")) {
36
37
  return request;
@@ -88,5 +89,35 @@
88
89
  __dynamicImportCache.set(cacheKey, namespaceFallback);
89
90
  return Promise.resolve(namespaceFallback);
90
91
  };
92
+ var __importMetaResolveHandler = function(specifier, fromPath) {
93
+ const request = String(specifier);
94
+ const referrer = typeof fromPath === "string" && fromPath.length > 0 ? fromPath : __fallbackReferrer;
95
+ let resolved = null;
96
+ if (typeof globalThis._resolveModuleSync !== "undefined") {
97
+ resolved = globalThis._resolveModuleSync.applySync(
98
+ void 0,
99
+ [request, referrer, "import"]
100
+ );
101
+ }
102
+ if (resolved === null || resolved === void 0) {
103
+ resolved = globalThis._resolveModule.applySyncPromise(
104
+ void 0,
105
+ [request, referrer, "import"]
106
+ );
107
+ }
108
+ if (resolved === null) {
109
+ const err = new Error("Cannot find module '" + request + "'");
110
+ err.code = "MODULE_NOT_FOUND";
111
+ throw err;
112
+ }
113
+ if (resolved.startsWith("node:")) {
114
+ return resolved;
115
+ }
116
+ if (__pathToFileURL && resolved.startsWith("/")) {
117
+ return __pathToFileURL(resolved).href;
118
+ }
119
+ return resolved;
120
+ };
91
121
  __runtimeExposeCustomGlobal("__dynamicImport", __dynamicImportHandler);
122
+ __runtimeExposeCustomGlobal("__importMetaResolve", __importMetaResolveHandler);
92
123
  })();
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Device backend.
3
+ *
4
+ * Standalone VirtualFileSystem that handles device nodes.
5
+ * Receives relative paths (e.g. "null" not "/dev/null").
6
+ * Designed to be mounted at /dev via MountTable.
7
+ */
8
+ import type { VirtualFileSystem } from "./vfs.js";
9
+ /**
10
+ * Create a standalone device backend VFS.
11
+ * All paths are relative to /dev (e.g. "null", "zero", "pts/0").
12
+ * Mount at /dev via MountTable.
13
+ */
14
+ export declare function createDeviceBackend(): VirtualFileSystem;
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Device backend.
3
+ *
4
+ * Standalone VirtualFileSystem that handles device nodes.
5
+ * Receives relative paths (e.g. "null" not "/dev/null").
6
+ * Designed to be mounted at /dev via MountTable.
7
+ */
8
+ import { KernelError } from "./types.js";
9
+ const DEVICE_NAMES = new Set([
10
+ "null",
11
+ "zero",
12
+ "stdin",
13
+ "stdout",
14
+ "stderr",
15
+ "urandom",
16
+ "random",
17
+ "tty",
18
+ "console",
19
+ "full",
20
+ "ptmx",
21
+ ]);
22
+ const DEVICE_INO = {
23
+ null: 0xffff_0001,
24
+ zero: 0xffff_0002,
25
+ stdin: 0xffff_0003,
26
+ stdout: 0xffff_0004,
27
+ stderr: 0xffff_0005,
28
+ urandom: 0xffff_0006,
29
+ random: 0xffff_0007,
30
+ tty: 0xffff_0008,
31
+ console: 0xffff_0009,
32
+ full: 0xffff_000a,
33
+ ptmx: 0xffff_000b,
34
+ };
35
+ /** Device pseudo-directories that contain dynamic entries. */
36
+ const DEVICE_DIRS = new Set(["fd", "pts", "shm"]);
37
+ function isDeviceName(path) {
38
+ return (DEVICE_NAMES.has(path) || path.startsWith("fd/") || path.startsWith("pts/"));
39
+ }
40
+ function isDeviceDir(path) {
41
+ return path === "" || DEVICE_DIRS.has(path);
42
+ }
43
+ function deviceStat(path) {
44
+ const now = Date.now();
45
+ return {
46
+ mode: 0o666,
47
+ size: 0,
48
+ isDirectory: false,
49
+ isSymbolicLink: false,
50
+ atimeMs: now,
51
+ mtimeMs: now,
52
+ ctimeMs: now,
53
+ birthtimeMs: now,
54
+ ino: DEVICE_INO[path] ?? 0xffff_0000,
55
+ nlink: 1,
56
+ uid: 0,
57
+ gid: 0,
58
+ };
59
+ }
60
+ function dirStat(path) {
61
+ const now = Date.now();
62
+ return {
63
+ mode: 0o755,
64
+ size: 0,
65
+ isDirectory: true,
66
+ isSymbolicLink: false,
67
+ atimeMs: now,
68
+ mtimeMs: now,
69
+ ctimeMs: now,
70
+ birthtimeMs: now,
71
+ ino: DEVICE_INO[path] ?? 0xffff_0000,
72
+ nlink: 2,
73
+ uid: 0,
74
+ gid: 0,
75
+ };
76
+ }
77
+ const DEV_DIR_ENTRIES = [
78
+ { name: "null", isDirectory: false },
79
+ { name: "zero", isDirectory: false },
80
+ { name: "stdin", isDirectory: false },
81
+ { name: "stdout", isDirectory: false },
82
+ { name: "stderr", isDirectory: false },
83
+ { name: "urandom", isDirectory: false },
84
+ { name: "random", isDirectory: false },
85
+ { name: "tty", isDirectory: false },
86
+ { name: "console", isDirectory: false },
87
+ { name: "full", isDirectory: false },
88
+ { name: "ptmx", isDirectory: false },
89
+ { name: "fd", isDirectory: true },
90
+ { name: "pts", isDirectory: true },
91
+ { name: "shm", isDirectory: true },
92
+ ];
93
+ function randomBytes(length) {
94
+ const buf = new Uint8Array(length);
95
+ if (typeof globalThis.crypto?.getRandomValues === "function") {
96
+ globalThis.crypto.getRandomValues(buf);
97
+ }
98
+ else {
99
+ for (let i = 0; i < buf.length; i++) {
100
+ buf[i] = (Math.random() * 256) | 0;
101
+ }
102
+ }
103
+ return buf;
104
+ }
105
+ function notFound(path) {
106
+ throw new KernelError("ENOENT", `no such device: ${path}`);
107
+ }
108
+ /**
109
+ * Create a standalone device backend VFS.
110
+ * All paths are relative to /dev (e.g. "null", "zero", "pts/0").
111
+ * Mount at /dev via MountTable.
112
+ */
113
+ export function createDeviceBackend() {
114
+ const backend = {
115
+ async readFile(path) {
116
+ if (path === "null" || path === "full")
117
+ return new Uint8Array(0);
118
+ if (path === "zero")
119
+ return new Uint8Array(4096);
120
+ if (path === "urandom" || path === "random")
121
+ return randomBytes(4096);
122
+ if (path === "tty" || path === "console" || path === "ptmx")
123
+ return new Uint8Array(0);
124
+ if (path === "stdin" || path === "stdout" || path === "stderr")
125
+ return new Uint8Array(0);
126
+ notFound(path);
127
+ },
128
+ async pread(path, _offset, length) {
129
+ if (path === "null" || path === "full")
130
+ return new Uint8Array(0);
131
+ if (path === "zero")
132
+ return new Uint8Array(length);
133
+ if (path === "urandom" || path === "random")
134
+ return randomBytes(length);
135
+ if (path === "tty" || path === "console" || path === "ptmx")
136
+ return new Uint8Array(0);
137
+ if (path === "stdin" || path === "stdout" || path === "stderr")
138
+ return new Uint8Array(0);
139
+ notFound(path);
140
+ },
141
+ async readTextFile(path) {
142
+ const bytes = await this.readFile(path);
143
+ return new TextDecoder().decode(bytes);
144
+ },
145
+ async readDir(path) {
146
+ if (path === "")
147
+ return DEV_DIR_ENTRIES.map((e) => e.name);
148
+ if (DEVICE_DIRS.has(path))
149
+ return [];
150
+ notFound(path);
151
+ },
152
+ async readDirWithTypes(path) {
153
+ if (path === "")
154
+ return DEV_DIR_ENTRIES;
155
+ if (DEVICE_DIRS.has(path))
156
+ return [];
157
+ notFound(path);
158
+ },
159
+ async writeFile(path, _content) {
160
+ if (path === "full")
161
+ throw new KernelError("ENOSPC", "No space left on device");
162
+ if (DEVICE_NAMES.has(path) ||
163
+ path.startsWith("fd/") ||
164
+ path.startsWith("pts/")) {
165
+ return;
166
+ }
167
+ notFound(path);
168
+ },
169
+ async pwrite(path, _offset, _data) {
170
+ if (path === "full")
171
+ throw new KernelError("ENOSPC", "No space left on device");
172
+ if (DEVICE_NAMES.has(path) ||
173
+ path.startsWith("fd/") ||
174
+ path.startsWith("pts/")) {
175
+ return;
176
+ }
177
+ notFound(path);
178
+ },
179
+ async createDir(path) {
180
+ if (isDeviceDir(path))
181
+ return;
182
+ throw new KernelError("EPERM", "cannot create directory in /dev");
183
+ },
184
+ async mkdir(path, _options) {
185
+ if (isDeviceDir(path))
186
+ return;
187
+ throw new KernelError("EPERM", "cannot create directory in /dev");
188
+ },
189
+ async exists(path) {
190
+ return isDeviceName(path) || isDeviceDir(path);
191
+ },
192
+ async stat(path) {
193
+ if (isDeviceName(path))
194
+ return deviceStat(path);
195
+ if (isDeviceDir(path))
196
+ return dirStat(path);
197
+ notFound(path);
198
+ },
199
+ async removeFile(path) {
200
+ if (isDeviceName(path))
201
+ throw new KernelError("EPERM", "cannot remove device");
202
+ notFound(path);
203
+ },
204
+ async removeDir(path) {
205
+ if (isDeviceDir(path))
206
+ throw new KernelError("EPERM", "cannot remove device directory");
207
+ notFound(path);
208
+ },
209
+ async rename(_oldPath, _newPath) {
210
+ throw new KernelError("EPERM", "cannot rename device");
211
+ },
212
+ async realpath(path) {
213
+ if (isDeviceName(path) || isDeviceDir(path))
214
+ return path;
215
+ notFound(path);
216
+ },
217
+ async symlink(_target, _linkPath) {
218
+ throw new KernelError("EPERM", "cannot create symlink in /dev");
219
+ },
220
+ async readlink(path) {
221
+ notFound(path);
222
+ },
223
+ async lstat(path) {
224
+ return this.stat(path);
225
+ },
226
+ async link(_oldPath, _newPath) {
227
+ throw new KernelError("EPERM", "cannot link device");
228
+ },
229
+ async chmod(path, _mode) {
230
+ if (isDeviceName(path) || isDeviceDir(path))
231
+ return;
232
+ notFound(path);
233
+ },
234
+ async chown(path, _uid, _gid) {
235
+ if (isDeviceName(path) || isDeviceDir(path))
236
+ return;
237
+ notFound(path);
238
+ },
239
+ async utimes(path, _atime, _mtime) {
240
+ if (isDeviceName(path) || isDeviceDir(path))
241
+ return;
242
+ notFound(path);
243
+ },
244
+ async truncate(path, _length) {
245
+ if (isDeviceName(path) || isDeviceDir(path))
246
+ return;
247
+ notFound(path);
248
+ },
249
+ };
250
+ return backend;
251
+ }
@@ -160,6 +160,15 @@ export function createDeviceLayer(vfs) {
160
160
  return;
161
161
  return vfs.writeFile(path, content);
162
162
  },
163
+ async pwrite(path, offset, data) {
164
+ if (path === "/dev/full")
165
+ throw new KernelError("ENOSPC", "No space left on device");
166
+ if (path === "/dev/null" || path === "/dev/zero" || path === "/dev/urandom"
167
+ || path === "/dev/random" || path === "/dev/tty" || path === "/dev/console"
168
+ || path === "/dev/ptmx")
169
+ return;
170
+ return vfs.pwrite(path, offset, data);
171
+ },
163
172
  async createDir(path) {
164
173
  if (isDeviceDir(path))
165
174
  return;
@@ -12,7 +12,6 @@ export const LOCK_SH = 1;
12
12
  export const LOCK_EX = 2;
13
13
  export const LOCK_UN = 8;
14
14
  export const LOCK_NB = 4;
15
- const FLOCK_WAIT_TIMEOUT_MS = 30_000;
16
15
  export class FileLockManager {
17
16
  /** path -> lock state */
18
17
  locks = new Map();
@@ -40,8 +39,8 @@ export class FileLockManager {
40
39
  if (nonBlocking) {
41
40
  throw new KernelError("EAGAIN", "resource temporarily unavailable");
42
41
  }
43
- // Bound each wait so callers can re-check lock state without hanging forever.
44
- const handle = state.waiters.enqueue(FLOCK_WAIT_TIMEOUT_MS);
42
+ // Wait indefinitely until an unlock wakes this waiter.
43
+ const handle = state.waiters.enqueue();
45
44
  try {
46
45
  await handle.wait();
47
46
  }
@@ -6,21 +6,21 @@
6
6
  * same kernel instance.
7
7
  */
8
8
  export { createKernel } from "./kernel.js";
9
- export type { Kernel, KernelOptions, KernelInterface, ExecOptions, ExecResult, SpawnOptions, ManagedProcess, RuntimeDriver, ProcessContext, DriverProcess, ProcessEntry, ProcessInfo, FDStat, FileDescription, FDEntry, Pipe, Permissions, PermissionDecision, PermissionCheck, FsAccessRequest, NetworkAccessRequest, ChildProcessAccessRequest, EnvAccessRequest, KernelErrorCode, SignalDisposition, SignalHandler, ProcessSignalState, Termios, TermiosCC, OpenShellOptions, ShellHandle, ConnectTerminalOptions, } from "./types.js";
10
- export { KernelError, defaultTermios } from "./types.js";
9
+ export type { FsMount, Kernel, KernelOptions, KernelInterface, KernelLogger, ExecOptions, ExecResult, SpawnOptions, ManagedProcess, RuntimeDriver, ProcessContext, DriverProcess, ProcessEntry, ProcessInfo, FDStat, FileDescription, FDEntry, Pipe, Permissions, PermissionDecision, PermissionCheck, FsAccessRequest, NetworkAccessRequest, ChildProcessAccessRequest, EnvAccessRequest, KernelErrorCode, SignalDisposition, SignalHandler, ProcessSignalState, Termios, TermiosCC, OpenShellOptions, ShellHandle, ConnectTerminalOptions, } from "./types.js";
10
+ export { KernelError, defaultTermios, noopKernelLogger } from "./types.js";
11
11
  export type { VirtualFileSystem, VirtualDirEntry, VirtualStat, } from "./vfs.js";
12
12
  export { FDTableManager, ProcessFDTable } from "./fd-table.js";
13
13
  export { ProcessTable } from "./process-table.js";
14
14
  export { createDeviceLayer } from "./device-layer.js";
15
15
  export { createProcLayer, createProcessScopedFileSystem, resolveProcSelfPath, } from "./proc-layer.js";
16
+ export { createProcBackend } from "./proc-backend.js";
17
+ export type { ProcBackendOptions } from "./proc-backend.js";
16
18
  export { PipeManager } from "./pipe-manager.js";
17
19
  export { PtyManager } from "./pty.js";
18
20
  export type { LineDisciplineConfig } from "./pty.js";
19
21
  export { CommandRegistry } from "./command-registry.js";
20
22
  export { FileLockManager, LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB } from "./file-lock.js";
21
23
  export { WaitHandle, WaitQueue } from "./wait.js";
22
- export { InodeTable } from "./inode-table.js";
23
- export type { Inode } from "./inode-table.js";
24
24
  export { TimerTable } from "./timer-table.js";
25
25
  export type { KernelTimer, TimerTableOptions } from "./timer-table.js";
26
26
  export { DnsCache } from "./dns-cache.js";
@@ -7,19 +7,19 @@
7
7
  */
8
8
  // Kernel factory
9
9
  export { createKernel } from "./kernel.js";
10
- // Structured kernel error and termios defaults
11
- export { KernelError, defaultTermios } from "./types.js";
10
+ // Structured kernel error, termios defaults, and no-op logger
11
+ export { KernelError, defaultTermios, noopKernelLogger } from "./types.js";
12
12
  // Kernel components (for direct use / testing)
13
13
  export { FDTableManager, ProcessFDTable } from "./fd-table.js";
14
14
  export { ProcessTable } from "./process-table.js";
15
15
  export { createDeviceLayer } from "./device-layer.js";
16
16
  export { createProcLayer, createProcessScopedFileSystem, resolveProcSelfPath, } from "./proc-layer.js";
17
+ export { createProcBackend } from "./proc-backend.js";
17
18
  export { PipeManager } from "./pipe-manager.js";
18
19
  export { PtyManager } from "./pty.js";
19
20
  export { CommandRegistry } from "./command-registry.js";
20
21
  export { FileLockManager, LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB } from "./file-lock.js";
21
22
  export { WaitHandle, WaitQueue } from "./wait.js";
22
- export { InodeTable } from "./inode-table.js";
23
23
  export { TimerTable } from "./timer-table.js";
24
24
  export { DnsCache } from "./dns-cache.js";
25
25
  export { UserManager } from "./user.js";