@ai-ecoverse/slicc-kernel 0.0.0 → 1.0.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.
@@ -0,0 +1,2978 @@
1
+ // src/fs/types.ts
2
+ function fsError(code, path) {
3
+ return Object.assign(new Error(`${code}: ${path}`), { code });
4
+ }
5
+ function normalizePath(path) {
6
+ const parts = [];
7
+ for (const part of path.split("/")) {
8
+ if (part === "" || part === ".") continue;
9
+ if (part === "..") parts.pop();
10
+ else parts.push(part);
11
+ }
12
+ return `/${parts.join("/")}`;
13
+ }
14
+ function resolvePath(base, path) {
15
+ return normalizePath(path.startsWith("/") ? path : `${base}/${path}`);
16
+ }
17
+
18
+ // src/fs/opfs.ts
19
+ var S_IFDIR = 16384;
20
+ var S_IFREG = 32768;
21
+ var PERM_MASK = 4095;
22
+ var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
23
+ var ERRNO_BY_DOM_ERROR = {
24
+ NotFoundError: "ENOENT",
25
+ TypeMismatchError: "ENOTDIR",
26
+ InvalidModificationError: "ENOTEMPTY",
27
+ NoModificationAllowedError: "EBUSY",
28
+ TypeError: "EINVAL"
29
+ };
30
+ function translate(err, path) {
31
+ if (typeof err?.code === "string") return err;
32
+ const name = err?.name;
33
+ return fsError(typeof name === "string" && ERRNO_BY_DOM_ERROR[name] || "EIO", path);
34
+ }
35
+ function split(path) {
36
+ return path.split("/").filter(Boolean);
37
+ }
38
+ function within(path, root) {
39
+ return path === root || path.startsWith(`${root}/`);
40
+ }
41
+ var OpfsFs = class {
42
+ modes = /* @__PURE__ */ new Map();
43
+ times = /* @__PURE__ */ new Map();
44
+ root;
45
+ constructor(root) {
46
+ this.root = root;
47
+ }
48
+ resolvePath(base, path) {
49
+ return resolvePath(base, path);
50
+ }
51
+ async dir(path, create = false) {
52
+ let dir = this.root;
53
+ try {
54
+ for (const part of split(path)) dir = await dir.getDirectoryHandle(part, { create });
55
+ } catch (err) {
56
+ throw translate(err, path);
57
+ }
58
+ return dir;
59
+ }
60
+ async parent(path) {
61
+ const parts = split(path);
62
+ const name = parts.pop();
63
+ if (name === void 0) throw fsError("EBUSY", path);
64
+ return [await this.dir(`/${parts.join("/")}`), name];
65
+ }
66
+ async handle(path) {
67
+ if (split(path).length === 0) return this.root;
68
+ const [dir, name] = await this.parent(path);
69
+ try {
70
+ return await dir.getFileHandle(name);
71
+ } catch (err) {
72
+ if (err?.name !== "TypeMismatchError") throw translate(err, path);
73
+ }
74
+ return dir.getDirectoryHandle(name);
75
+ }
76
+ mode(path, directory) {
77
+ const fallback = directory || EXECUTABLE.test(path) ? 493 : 420;
78
+ return (directory ? S_IFDIR : S_IFREG) | (this.modes.get(path) ?? fallback);
79
+ }
80
+ async stat(path) {
81
+ const handle = await this.handle(path);
82
+ const directory = handle.kind === "directory";
83
+ const file = directory ? void 0 : await handle.getFile();
84
+ return {
85
+ isFile: !directory,
86
+ isDirectory: directory,
87
+ isSymbolicLink: false,
88
+ size: file?.size ?? 0,
89
+ mode: this.mode(path, directory),
90
+ mtime: new Date(this.times.get(path) ?? file?.lastModified ?? 0)
91
+ };
92
+ }
93
+ lstat(path) {
94
+ return this.stat(path);
95
+ }
96
+ async exists(path) {
97
+ return this.handle(path).then(
98
+ () => true,
99
+ () => false
100
+ );
101
+ }
102
+ async readFileBuffer(path) {
103
+ const handle = await this.handle(path);
104
+ if (handle.kind === "directory") throw fsError("EISDIR", path);
105
+ return new Uint8Array(await (await handle.getFile()).arrayBuffer());
106
+ }
107
+ async readFile(path) {
108
+ return new TextDecoder().decode(await this.readFileBuffer(path));
109
+ }
110
+ async writeFile(path, content) {
111
+ const [dir, name] = await this.parent(path);
112
+ let file;
113
+ try {
114
+ file = await dir.getFileHandle(name, { create: true });
115
+ } catch (err) {
116
+ const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
117
+ throw code ? fsError(code, path) : translate(err, path);
118
+ }
119
+ const writable = await file.createWritable();
120
+ await writable.write(content);
121
+ await writable.close();
122
+ this.times.delete(path);
123
+ }
124
+ async readdir(path) {
125
+ const handle = await this.handle(path);
126
+ if (handle.kind !== "directory") throw fsError("ENOTDIR", path);
127
+ const names = [];
128
+ for await (const name of handle.keys()) names.push(name);
129
+ return names.sort();
130
+ }
131
+ async mkdir(path, options = {}) {
132
+ if (options.recursive) {
133
+ await this.dir(path, true);
134
+ return;
135
+ }
136
+ if (await this.exists(path)) throw fsError("EEXIST", path);
137
+ const [dir, name] = await this.parent(path);
138
+ await dir.getDirectoryHandle(name, { create: true });
139
+ }
140
+ async rm(path, options = {}) {
141
+ try {
142
+ const [dir, name] = await this.parent(path);
143
+ await dir.removeEntry(name, { recursive: options.recursive === true });
144
+ } catch (err) {
145
+ const error = translate(err, path);
146
+ if (options.force && error.code === "ENOENT") return;
147
+ throw error;
148
+ }
149
+ this.forget(path);
150
+ }
151
+ forget(path) {
152
+ for (const map of [this.modes, this.times]) {
153
+ for (const key of [...map.keys()]) if (within(key, path)) map.delete(key);
154
+ }
155
+ }
156
+ carry(from, to) {
157
+ for (const map of [this.modes, this.times]) {
158
+ for (const [key, value] of [...map]) {
159
+ if (!within(key, from)) continue;
160
+ map.delete(key);
161
+ map.set(to + key.slice(from.length), value);
162
+ }
163
+ }
164
+ }
165
+ async rename(from, to) {
166
+ if (from === to) return;
167
+ const source = await this.stat(from);
168
+ if (source.isDirectory && within(to, from)) throw fsError("EINVAL", to);
169
+ const target = await this.stat(to).catch(() => null);
170
+ if (target?.isDirectory && !source.isDirectory) throw fsError("EISDIR", to);
171
+ if (target && !target.isDirectory && source.isDirectory) throw fsError("ENOTDIR", to);
172
+ if (target?.isDirectory && (await this.readdir(to)).length > 0) {
173
+ throw fsError("ENOTEMPTY", to);
174
+ }
175
+ const [dir, name] = await this.parent(to);
176
+ const handle = await this.handle(from);
177
+ const swap = target?.isDirectory === true;
178
+ const staging = swap ? `.${name}.${crypto.randomUUID()}` : name;
179
+ const moved = await this.place(handle, dir, staging);
180
+ if (swap) await this.rm(to, { recursive: true });
181
+ this.carry(from, to);
182
+ if (!moved) await this.rm(from, { recursive: true });
183
+ if (!swap) return;
184
+ const staged = await dir.getDirectoryHandle(staging);
185
+ if (!await this.place(staged, dir, name)) await dir.removeEntry(staging, { recursive: true });
186
+ }
187
+ async place(handle, dir, name) {
188
+ const moved = await handle.move?.(dir, name).then(
189
+ () => true,
190
+ () => false
191
+ );
192
+ if (moved) return true;
193
+ try {
194
+ await this.copy(handle, dir, name);
195
+ } catch (err) {
196
+ if (handle.kind === "directory") await dir.removeEntry(name, { recursive: true });
197
+ throw translate(err, name);
198
+ }
199
+ return false;
200
+ }
201
+ async copy(handle, dir, name) {
202
+ if (handle.kind === "file") {
203
+ const writable = await (await dir.getFileHandle(name, { create: true })).createWritable();
204
+ await writable.write(await handle.getFile());
205
+ await writable.close();
206
+ return;
207
+ }
208
+ const target = await dir.getDirectoryHandle(name, { create: true });
209
+ for await (const child of handle.values()) await this.copy(child, target, child.name);
210
+ }
211
+ async symlink(_target, path) {
212
+ throw fsError("ENOSYS", path);
213
+ }
214
+ async readlink(path) {
215
+ await this.handle(path);
216
+ throw fsError("EINVAL", path);
217
+ }
218
+ async chmod(path, mode) {
219
+ await this.handle(path);
220
+ this.modes.set(path, mode & PERM_MASK);
221
+ }
222
+ async utimes(path, _atime, mtime) {
223
+ await this.handle(path);
224
+ this.times.set(path, mtime.getTime());
225
+ }
226
+ };
227
+
228
+ // src/commands.ts
229
+ var NAME = /^[A-Za-z0-9._+-]+$/;
230
+ var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
231
+ var PACKAGE = /\$\{package\}/g;
232
+ function inside(pkg, rel) {
233
+ if (typeof rel !== "string" || rel === "") return void 0;
234
+ const clean = rel.replace(/^\.\//, "");
235
+ if (clean.startsWith("/") || clean.split("/").includes("..")) return void 0;
236
+ return `${pkg}/${clean}`;
237
+ }
238
+ function envOf(pkg, raw) {
239
+ const env = {};
240
+ if (!raw || typeof raw !== "object") return env;
241
+ for (const [key, value] of Object.entries(raw)) {
242
+ if (ENV_KEY.test(key) && typeof value === "string") env[key] = value.replace(PACKAGE, pkg);
243
+ }
244
+ return env;
245
+ }
246
+ function argsOf(pkg, raw) {
247
+ if (!Array.isArray(raw) || raw.length === 0) return void 0;
248
+ if (!raw.every((arg) => typeof arg === "string")) return void 0;
249
+ return raw.map((arg) => arg.replace(PACKAGE, pkg));
250
+ }
251
+ function commandsOf(pkg, manifest) {
252
+ const slicc = manifest.slicc;
253
+ if (!slicc || typeof slicc !== "object" || (slicc.abi ?? "emscripten") !== "emscripten")
254
+ return [];
255
+ const entries = slicc.commands;
256
+ if (!entries || typeof entries !== "object") return [];
257
+ const shared = envOf(pkg, slicc.env);
258
+ const out = [];
259
+ for (const [name, raw] of Object.entries(entries)) {
260
+ if (!NAME.test(name) || name === "." || name === ".." || !raw || typeof raw !== "object") {
261
+ continue;
262
+ }
263
+ const glue = inside(pkg, raw.glue);
264
+ const wasm = inside(pkg, raw.wasm);
265
+ if ((raw.abi ?? "emscripten") !== "emscripten" || !glue || !wasm) continue;
266
+ const argv0 = typeof raw.argv0 === "string" && raw.argv0 ? raw.argv0 : name;
267
+ const args = argsOf(pkg, raw.args);
268
+ const env = { ...shared, ...envOf(pkg, raw.env) };
269
+ out.push({
270
+ name,
271
+ glue,
272
+ wasm,
273
+ argv0,
274
+ ...args ? { args } : {},
275
+ ...Object.keys(env).length > 0 ? { env } : {}
276
+ });
277
+ }
278
+ return out;
279
+ }
280
+ async function packages(fs, modules) {
281
+ const names = await fs.readdir(modules).catch(() => []);
282
+ const dirs = [];
283
+ for (const name of names) {
284
+ if (name.startsWith(".")) continue;
285
+ if (!name.startsWith("@")) {
286
+ dirs.push(`${modules}/${name}`);
287
+ continue;
288
+ }
289
+ for (const inner of await fs.readdir(`${modules}/${name}`).catch(() => [])) {
290
+ dirs.push(`${modules}/${name}/${inner}`);
291
+ }
292
+ }
293
+ return dirs;
294
+ }
295
+ async function scanCommands(fs, modules) {
296
+ const found = /* @__PURE__ */ new Map();
297
+ for (const pkg of await packages(fs, modules)) {
298
+ let manifest;
299
+ try {
300
+ manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
301
+ } catch {
302
+ continue;
303
+ }
304
+ for (const command of commandsOf(pkg, manifest)) {
305
+ if (!found.has(command.name)) found.set(command.name, command);
306
+ }
307
+ }
308
+ if (!found.has("sh") && found.has("bash")) {
309
+ found.set("sh", { ...found.get("bash"), name: "sh", argv0: "sh" });
310
+ }
311
+ return found;
312
+ }
313
+
314
+ // src/fs/commands.ts
315
+ var S_IFDIR2 = 16384;
316
+ var S_IFREG2 = 32768;
317
+ var COMMAND = /^\/(?:usr\/)?bin\/([^/]+)$/;
318
+ function missing(err) {
319
+ return err?.code === "ENOENT";
320
+ }
321
+ function synthetic(directory) {
322
+ return {
323
+ isFile: !directory,
324
+ isDirectory: directory,
325
+ isSymbolicLink: false,
326
+ size: 0,
327
+ mode: (directory ? S_IFDIR2 : S_IFREG2) | 493,
328
+ mtime: /* @__PURE__ */ new Date(0)
329
+ };
330
+ }
331
+ function withCommandDirs(fs, names) {
332
+ async function children(path) {
333
+ if (path === "/") return ["bin", "usr"];
334
+ if (path === "/usr") return ["bin"];
335
+ if (path === "/bin" || path === "/usr/bin") return [...await names()];
336
+ return void 0;
337
+ }
338
+ async function virtual(path) {
339
+ if (path === "/usr" || path === "/bin" || path === "/usr/bin") return synthetic(true);
340
+ const name = COMMAND.exec(path)?.[1];
341
+ return name !== void 0 && (await names()).has(name) ? synthetic(false) : void 0;
342
+ }
343
+ async function stat(path, real) {
344
+ try {
345
+ return await real(path);
346
+ } catch (err) {
347
+ const found = missing(err) ? await virtual(path) : void 0;
348
+ if (found) return found;
349
+ throw err;
350
+ }
351
+ }
352
+ return {
353
+ resolvePath: (base, path) => fs.resolvePath(base, path),
354
+ readFile: (path) => fs.readFile(path),
355
+ async readFileBuffer(path) {
356
+ try {
357
+ return await fs.readFileBuffer(path);
358
+ } catch (err) {
359
+ if (missing(err) && (await virtual(path))?.isFile) return new Uint8Array(0);
360
+ throw err;
361
+ }
362
+ },
363
+ writeFile: (path, content) => fs.writeFile(path, content),
364
+ async exists(path) {
365
+ return await fs.exists(path) || await virtual(path) !== void 0;
366
+ },
367
+ stat: (path) => stat(path, (p) => fs.stat(p)),
368
+ lstat: (path) => stat(path, (p) => fs.lstat(p)),
369
+ async readdir(path) {
370
+ const extra = await children(path);
371
+ if (!extra) return fs.readdir(path);
372
+ const real = await fs.readdir(path).catch((err) => {
373
+ if (missing(err)) return [];
374
+ throw err;
375
+ });
376
+ return [.../* @__PURE__ */ new Set([...real, ...extra])].sort();
377
+ },
378
+ mkdir: (path, options) => fs.mkdir(path, options),
379
+ rm: (path, options) => fs.rm(path, options),
380
+ rename: (from, to) => fs.rename(from, to),
381
+ symlink: (target, path) => fs.symlink(target, path),
382
+ readlink: (path) => fs.readlink(path),
383
+ chmod: (path, mode) => fs.chmod(path, mode),
384
+ utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime)
385
+ };
386
+ }
387
+
388
+ // src/kernel/pipe.ts
389
+ var PIPE_CAPACITY = 64 * 1024;
390
+ var PipeError = class extends Error {
391
+ code;
392
+ constructor(code) {
393
+ super(code);
394
+ this.code = code;
395
+ }
396
+ };
397
+ var KernelPipe = class {
398
+ chunks = [];
399
+ size = 0;
400
+ readers = 0;
401
+ writers = 0;
402
+ waiters = [];
403
+ capacity;
404
+ constructor(capacity = PIPE_CAPACITY) {
405
+ this.capacity = capacity;
406
+ }
407
+ get buffered() {
408
+ return this.size;
409
+ }
410
+ get readReady() {
411
+ return this.size > 0 || this.writers === 0;
412
+ }
413
+ get writeReady() {
414
+ return this.size < this.capacity || this.readers === 0;
415
+ }
416
+ get writersGone() {
417
+ return this.writers === 0;
418
+ }
419
+ get readersGone() {
420
+ return this.readers === 0;
421
+ }
422
+ openRead() {
423
+ this.readers += 1;
424
+ }
425
+ openWrite() {
426
+ this.writers += 1;
427
+ }
428
+ closeRead() {
429
+ if (this.readers > 0) this.readers -= 1;
430
+ if (this.readers === 0) {
431
+ this.chunks = [];
432
+ this.size = 0;
433
+ }
434
+ this.wake();
435
+ }
436
+ closeWrite() {
437
+ if (this.writers > 0) this.writers -= 1;
438
+ this.wake();
439
+ }
440
+ async read(max, signal) {
441
+ while (this.size === 0) {
442
+ if (this.writers === 0) return new Uint8Array(0);
443
+ await this.changed(signal);
444
+ }
445
+ const out = this.take(Math.min(max, this.size));
446
+ this.wake();
447
+ return out;
448
+ }
449
+ async peek(max, signal) {
450
+ while (this.size === 0) {
451
+ if (this.writers === 0) return new Uint8Array(0);
452
+ await this.changed(signal);
453
+ }
454
+ const out = new Uint8Array(Math.min(max, this.size));
455
+ let filled = 0;
456
+ for (const chunk of this.chunks) {
457
+ if (filled === out.length) break;
458
+ const count = Math.min(chunk.length, out.length - filled);
459
+ out.set(chunk.subarray(0, count), filled);
460
+ filled += count;
461
+ }
462
+ return out;
463
+ }
464
+ async write(bytes, signal) {
465
+ let offset = 0;
466
+ while (offset < bytes.length) {
467
+ if (this.readers === 0) throw new PipeError("EPIPE");
468
+ const room = this.capacity - this.size;
469
+ if (room === 0) {
470
+ try {
471
+ await this.changed(signal);
472
+ } catch (e) {
473
+ if (offset > 0) return offset;
474
+ throw e;
475
+ }
476
+ continue;
477
+ }
478
+ const n = Math.min(room, bytes.length - offset);
479
+ this.chunks.push(bytes.slice(offset, offset + n));
480
+ this.size += n;
481
+ offset += n;
482
+ this.wake();
483
+ }
484
+ return bytes.length;
485
+ }
486
+ take(n) {
487
+ const out = new Uint8Array(n);
488
+ let filled = 0;
489
+ while (filled < n) {
490
+ const head = this.chunks[0];
491
+ const count = Math.min(head.length, n - filled);
492
+ out.set(head.subarray(0, count), filled);
493
+ filled += count;
494
+ if (count === head.length) this.chunks.shift();
495
+ else this.chunks[0] = head.subarray(count);
496
+ }
497
+ this.size -= n;
498
+ return out;
499
+ }
500
+ changed(signal) {
501
+ if (signal?.aborted) return Promise.reject(new PipeError("EINTR"));
502
+ return new Promise((resolve, reject) => {
503
+ const onAbort = () => {
504
+ this.waiters = this.waiters.filter((w) => w !== waiter);
505
+ reject(new PipeError("EINTR"));
506
+ };
507
+ const waiter = () => {
508
+ signal?.removeEventListener("abort", onAbort);
509
+ resolve();
510
+ };
511
+ this.waiters.push(waiter);
512
+ signal?.addEventListener("abort", onAbort, { once: true });
513
+ });
514
+ }
515
+ wake() {
516
+ const waiters = this.waiters;
517
+ this.waiters = [];
518
+ for (const resolve of waiters) resolve();
519
+ }
520
+ };
521
+
522
+ // src/kernel/fd-table.ts
523
+ var KernelError = class extends Error {
524
+ code;
525
+ constructor(code) {
526
+ super(code);
527
+ this.code = code;
528
+ }
529
+ };
530
+ function pollFile(file) {
531
+ return file.poll?.() ?? { readable: !!file.read, writable: !!file.write, hangup: false };
532
+ }
533
+ var OpenFile = class {
534
+ refs = 1;
535
+ file;
536
+ constructor(file) {
537
+ this.file = file;
538
+ }
539
+ retain() {
540
+ this.refs += 1;
541
+ return this;
542
+ }
543
+ release() {
544
+ this.refs -= 1;
545
+ if (this.refs === 0) return this.file.close();
546
+ }
547
+ };
548
+ function openPipe(capacity) {
549
+ const pipe = new KernelPipe(capacity);
550
+ pipe.openRead();
551
+ pipe.openWrite();
552
+ return {
553
+ read: new OpenFile({
554
+ read: async (max, signal) => {
555
+ try {
556
+ return await pipe.read(max, signal);
557
+ } catch (e) {
558
+ throw new KernelError(e.code);
559
+ }
560
+ },
561
+ close: () => pipe.closeRead(),
562
+ poll: () => ({ readable: pipe.readReady, writable: false, hangup: pipe.writersGone }),
563
+ changed: (signal) => pipe.changed(signal)
564
+ }),
565
+ write: new OpenFile({
566
+ write: async (bytes, signal) => {
567
+ try {
568
+ return await pipe.write(bytes, signal);
569
+ } catch (e) {
570
+ throw new KernelError(e.code);
571
+ }
572
+ },
573
+ close: () => pipe.closeWrite(),
574
+ poll: () => ({ readable: false, writable: pipe.writeReady, hangup: pipe.readersGone }),
575
+ changed: (signal) => pipe.changed(signal)
576
+ })
577
+ };
578
+ }
579
+ function bytesSource(data) {
580
+ let offset = 0;
581
+ return new OpenFile({
582
+ read: async (max) => {
583
+ const out = data.subarray(offset, offset + max);
584
+ offset += out.length;
585
+ return out;
586
+ },
587
+ close: () => {
588
+ }
589
+ });
590
+ }
591
+ function sinkFile(onData) {
592
+ return new OpenFile({
593
+ write: async (bytes) => {
594
+ onData(bytes.slice());
595
+ return bytes.length;
596
+ },
597
+ close: () => {
598
+ }
599
+ });
600
+ }
601
+ function nullFile() {
602
+ return new OpenFile({
603
+ read: async () => new Uint8Array(0),
604
+ write: async (bytes) => bytes.length,
605
+ close: () => {
606
+ }
607
+ });
608
+ }
609
+ function deviceFile(device, access) {
610
+ const read = async (max) => {
611
+ if (device === "null") return new Uint8Array(0);
612
+ const out = new Uint8Array(max);
613
+ if (device === "urandom") {
614
+ for (let at = 0; at < max; at += 65536) {
615
+ crypto.getRandomValues(out.subarray(at, Math.min(max, at + 65536)));
616
+ }
617
+ }
618
+ return out;
619
+ };
620
+ return new OpenFile({
621
+ ...access !== "write" ? { read } : {},
622
+ ...access !== "read" ? { write: async (bytes) => bytes.length } : {},
623
+ seek: async () => 0,
624
+ heldMeta: { device, ...access ? { access } : {} },
625
+ close: () => {
626
+ }
627
+ });
628
+ }
629
+ function heldFile(meta) {
630
+ return new OpenFile({ held: true, ...meta ? { heldMeta: meta } : {}, close: () => {
631
+ } });
632
+ }
633
+ function kernelFdKind(file) {
634
+ if (file.held) return "held";
635
+ if (file.heldMeta && "device" in file.heldMeta) return "device";
636
+ if (file.tty) return "tty";
637
+ return file.seek ? "file" : "stream";
638
+ }
639
+ var FdTable = class _FdTable {
640
+ static MAX_FDS = 1024;
641
+ fds = /* @__PURE__ */ new Map();
642
+ cloexec = /* @__PURE__ */ new Set();
643
+ status = /* @__PURE__ */ new Map();
644
+ get(fd) {
645
+ const file = this.fds.get(fd);
646
+ if (!file) throw new KernelError("EBADF");
647
+ return file;
648
+ }
649
+ stdioTerminal() {
650
+ for (const fd of [0, 1, 2]) {
651
+ const tty = this.fds.get(fd)?.file.tty;
652
+ if (tty) return tty;
653
+ }
654
+ return void 0;
655
+ }
656
+ numbers() {
657
+ return [...this.fds.keys()].sort((a, b) => a - b);
658
+ }
659
+ has(fd) {
660
+ return this.fds.has(fd);
661
+ }
662
+ setCloseOnExec(fd) {
663
+ this.get(fd);
664
+ this.cloexec.add(fd);
665
+ }
666
+ clearCloseOnExec(fd) {
667
+ this.get(fd);
668
+ this.cloexec.delete(fd);
669
+ }
670
+ closesOnExec(fd) {
671
+ return this.cloexec.has(fd);
672
+ }
673
+ setStatusFlags(fd, flags) {
674
+ this.get(fd);
675
+ this.status.set(fd, flags);
676
+ }
677
+ statusFlags(fd) {
678
+ return this.status.get(fd);
679
+ }
680
+ install(file, min = 0) {
681
+ for (let fd = min; fd < _FdTable.MAX_FDS; fd++) {
682
+ if (!this.fds.has(fd)) {
683
+ this.fds.set(fd, file);
684
+ return fd;
685
+ }
686
+ }
687
+ void Promise.resolve(file.release()).catch(() => void 0);
688
+ throw new KernelError("EMFILE");
689
+ }
690
+ installAt(fd, file) {
691
+ if (fd < 0 || fd >= _FdTable.MAX_FDS) {
692
+ void Promise.resolve(file.release()).catch(() => void 0);
693
+ throw new KernelError("EBADF");
694
+ }
695
+ const previous = this.fds.get(fd);
696
+ this.fds.set(fd, file);
697
+ this.cloexec.delete(fd);
698
+ this.status.delete(fd);
699
+ void Promise.resolve(previous?.release()).catch(() => void 0);
700
+ }
701
+ dup(fd, min = 0) {
702
+ return this.install(this.get(fd).retain(), min);
703
+ }
704
+ dup2(oldFd, newFd) {
705
+ const file = this.get(oldFd);
706
+ if (oldFd !== newFd) this.installAt(newFd, file.retain());
707
+ return newFd;
708
+ }
709
+ close(fd) {
710
+ const file = this.get(fd);
711
+ this.fds.delete(fd);
712
+ this.cloexec.delete(fd);
713
+ this.status.delete(fd);
714
+ return file.release();
715
+ }
716
+ fork() {
717
+ const child = new _FdTable();
718
+ for (const [fd, file] of this.fds) child.fds.set(fd, file.retain());
719
+ for (const fd of this.cloexec) child.cloexec.add(fd);
720
+ for (const [fd, flags] of this.status) child.status.set(fd, flags);
721
+ return child;
722
+ }
723
+ async closeAll() {
724
+ const files = [...this.fds.values()];
725
+ this.fds.clear();
726
+ this.cloexec.clear();
727
+ this.status.clear();
728
+ await Promise.allSettled(files.map((file) => Promise.resolve().then(() => file.release())));
729
+ }
730
+ };
731
+
732
+ // src/kernel/children.ts
733
+ var SpawnError = class extends Error {
734
+ code;
735
+ constructor(code) {
736
+ super(code);
737
+ this.code = code;
738
+ }
739
+ };
740
+ function interrupted(signal) {
741
+ const { promise, reject } = Promise.withResolvers();
742
+ const fail = () => reject(new KernelError("EINTR"));
743
+ if (signal?.aborted) fail();
744
+ else signal?.addEventListener("abort", fail, { once: true });
745
+ return { promise, done: () => signal?.removeEventListener("abort", fail) };
746
+ }
747
+ function stoppedStatus(sig) {
748
+ return (sig & 255) << 8 | 127;
749
+ }
750
+ var CONTINUED_STATUS = 65535;
751
+ function waitStatus(code, termsig) {
752
+ return termsig ? termsig & 127 : (code & 255) << 8;
753
+ }
754
+ function concat(chunks) {
755
+ const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
756
+ let offset = 0;
757
+ for (const chunk of chunks) {
758
+ out.set(chunk, offset);
759
+ offset += chunk.length;
760
+ }
761
+ return out;
762
+ }
763
+ var ChildTable = class {
764
+ children = /* @__PURE__ */ new Map();
765
+ leftovers = /* @__PURE__ */ new Map();
766
+ onChildState;
767
+ onReap;
768
+ stateChanged = [];
769
+ watchers = /* @__PURE__ */ new Map();
770
+ parentFds;
771
+ spawner;
772
+ forker;
773
+ constructor(parentFds, spawner, forker) {
774
+ this.parentFds = parentFds;
775
+ this.spawner = spawner;
776
+ this.forker = forker;
777
+ }
778
+ async fork(state) {
779
+ if (!this.forker) throw new SpawnError("ENOSYS");
780
+ return this.track(state, this.parentFds.fork(), /* @__PURE__ */ new Map(), this.forker);
781
+ }
782
+ async spawn(req, stdio, inherit = []) {
783
+ if (!this.spawner) throw new SpawnError("ENOSYS");
784
+ const fds = new FdTable();
785
+ const captured = /* @__PURE__ */ new Map();
786
+ try {
787
+ for (const [n, slot] of stdio.entries()) fds.installAt(n, this.openSlot(slot, n, captured));
788
+ for (const slot of inherit) {
789
+ if (slot.fd <= 2) continue;
790
+ if ("device" in slot) {
791
+ fds.installAt(slot.fd, deviceFile(slot.device, slot.access));
792
+ continue;
793
+ }
794
+ fds.installAt(slot.fd, this.parentFds.get(slot.kernel).retain());
795
+ if (slot.flags !== void 0) fds.setStatusFlags(slot.fd, slot.flags);
796
+ }
797
+ } catch (e) {
798
+ await fds.closeAll();
799
+ throw e;
800
+ }
801
+ return this.track(req, fds, captured, this.spawner);
802
+ }
803
+ async track(req, fds, captured, start) {
804
+ let handle;
805
+ try {
806
+ handle = await start(req, fds);
807
+ } catch (e) {
808
+ await fds.closeAll();
809
+ throw e;
810
+ }
811
+ const child = { exited: handle.exited, termsig: handle.termsig, captured };
812
+ void handle.exited.then((code) => {
813
+ child.code = code;
814
+ this.watchers.delete(handle.pid);
815
+ this.onChildState?.();
816
+ });
817
+ handle.onState?.((state, sig) => {
818
+ child.stopReport = state === "stopped" ? sig : void 0;
819
+ child.continueReport = state === "continued";
820
+ this.watchers.get(handle.pid)?.(state, sig);
821
+ const waiters = this.stateChanged;
822
+ this.stateChanged = [];
823
+ for (const wake of waiters) wake();
824
+ this.onChildState?.();
825
+ });
826
+ this.children.set(handle.pid, child);
827
+ return handle.pid;
828
+ }
829
+ watch(pid, listener) {
830
+ if (this.children.has(pid)) this.watchers.set(pid, listener);
831
+ }
832
+ openSlot(slot, n, captured) {
833
+ if ("fd" in slot) return this.parentFds.get(slot.fd).retain();
834
+ if ("input" in slot) return bytesSource(slot.input);
835
+ if ("capture" in slot) {
836
+ const chunks = [];
837
+ captured.set(n, chunks);
838
+ return sinkFile((bytes) => chunks.push(bytes));
839
+ }
840
+ return nullFile();
841
+ }
842
+ async wait(pid, nohang, signal, flags = {}) {
843
+ let interrupt;
844
+ try {
845
+ for (; ; ) {
846
+ const candidates = this.candidates(pid, flags);
847
+ if (candidates.length === 0) throw new KernelError("ECHILD");
848
+ const done3 = candidates.find(([, child]) => child.code !== void 0);
849
+ if (done3) return this.reap(done3[0], done3[1].code);
850
+ const changed = this.stateReport(candidates, flags);
851
+ if (changed) return changed;
852
+ if (nohang) return [0, 0];
853
+ interrupt ??= interrupted(signal);
854
+ await this.nextChange(candidates, flags, interrupt.promise);
855
+ }
856
+ } finally {
857
+ interrupt?.done();
858
+ }
859
+ }
860
+ candidates(pid, flags) {
861
+ const all = [...this.children];
862
+ if (pid > 0) return all.filter(([p]) => p === pid);
863
+ const inGroup = flags.inGroup;
864
+ return pid === -1 || !inGroup ? all : all.filter(([p]) => inGroup(p));
865
+ }
866
+ async nextChange(candidates, flags, interrupt) {
867
+ let wake;
868
+ const stateChange = new Promise((resolve) => wake = resolve);
869
+ const watching = flags.untraced || flags.continued;
870
+ if (watching && wake) this.stateChanged.push(wake);
871
+ try {
872
+ await Promise.race([
873
+ ...candidates.map(([, child]) => child.exited),
874
+ ...watching ? [stateChange] : [],
875
+ interrupt
876
+ ]);
877
+ } finally {
878
+ this.stateChanged = this.stateChanged.filter((w) => w !== wake);
879
+ }
880
+ }
881
+ stateReport(candidates, flags) {
882
+ for (const [p, child] of candidates) {
883
+ if (flags.untraced && child.stopReport !== void 0) {
884
+ const sig = child.stopReport;
885
+ child.stopReport = void 0;
886
+ return [p, stoppedStatus(sig)];
887
+ }
888
+ if (flags.continued && child.continueReport) {
889
+ child.continueReport = false;
890
+ return [p, CONTINUED_STATUS];
891
+ }
892
+ }
893
+ return null;
894
+ }
895
+ reap(pid, code) {
896
+ const child = this.children.get(pid);
897
+ this.children.delete(pid);
898
+ this.onReap?.(pid);
899
+ if (child && child.captured.size > 0) this.leftovers.set(pid, child.captured);
900
+ return [pid, waitStatus(code, child?.termsig?.())];
901
+ }
902
+ pids() {
903
+ return [...this.children.keys()];
904
+ }
905
+ captured(pid, slot) {
906
+ const slots = this.leftovers.get(pid);
907
+ const chunks = slots?.get(slot);
908
+ if (!slots || !chunks) return new Uint8Array(0);
909
+ slots.delete(slot);
910
+ if (slots.size === 0) this.leftovers.delete(pid);
911
+ return concat(chunks);
912
+ }
913
+ };
914
+
915
+ // src/realm/sync-fs-token-registry.ts
916
+ var registry = /* @__PURE__ */ new Map();
917
+ function mintSyncFsToken(entry) {
918
+ const token = crypto.randomUUID();
919
+ registry.set(token, entry);
920
+ return token;
921
+ }
922
+ function resolveSyncFsToken(token) {
923
+ return registry.get(token) ?? null;
924
+ }
925
+ function revokeSyncFsToken(token) {
926
+ registry.delete(token);
927
+ }
928
+
929
+ // src/realm/sync-fs-wire.ts
930
+ var SYNC_FS_REQUEST_TIMEOUT_MS = 25e3;
931
+ function syncError(code, label) {
932
+ return Object.assign(new Error(`${code}: ${label}`), { code });
933
+ }
934
+
935
+ // src/realm/sync-fs-dispatch.ts
936
+ function toErrno(err) {
937
+ const message = err instanceof Error ? err.message : String(err);
938
+ const code = err?.code;
939
+ if (typeof code === "string" && /^E[A-Z]+$/.test(code)) {
940
+ return { ok: false, errno: code, message };
941
+ }
942
+ return { ok: false, errno: "EIO", message };
943
+ }
944
+ function statJson(s) {
945
+ return {
946
+ isDirectory: s.isDirectory,
947
+ isFile: s.isFile,
948
+ isSymbolicLink: s.isSymbolicLink,
949
+ size: s.size,
950
+ mode: s.mode,
951
+ mtimeMs: s.mtime.getTime()
952
+ };
953
+ }
954
+ async function readdirStat(fs, dir) {
955
+ const names = await fs.readdir(dir);
956
+ const base = dir === "/" ? "" : dir;
957
+ return Promise.all(
958
+ names.map(async (name) => {
959
+ try {
960
+ return [name, statJson(await fs.lstat(`${base}/${name}`))];
961
+ } catch {
962
+ return [name, null];
963
+ }
964
+ })
965
+ );
966
+ }
967
+ var done = { ok: true, kind: "void" };
968
+ function json(value) {
969
+ return { ok: true, kind: "json", json: value };
970
+ }
971
+ async function run(fs, path, req, cwd) {
972
+ switch (req.op) {
973
+ case "read":
974
+ return { ok: true, kind: "bytes", bytes: await fs.readFileBuffer(path) };
975
+ case "write":
976
+ await fs.writeFile(path, req.body ?? new Uint8Array(0));
977
+ return done;
978
+ case "exists":
979
+ return json(await fs.exists(path));
980
+ case "stat":
981
+ return json(statJson(await fs.stat(path)));
982
+ case "lstat":
983
+ return json(statJson(await fs.lstat(path)));
984
+ case "readdir":
985
+ return json(await fs.readdir(path));
986
+ case "readdir-stat":
987
+ return json(await readdirStat(fs, path));
988
+ case "mkdir":
989
+ await fs.mkdir(path, { recursive: true });
990
+ return done;
991
+ case "rm":
992
+ await fs.rm(path, { recursive: true });
993
+ return done;
994
+ case "rename":
995
+ await fs.rename(path, fs.resolvePath(cwd, req.arg2 ?? ""));
996
+ return done;
997
+ case "unlink":
998
+ if ((await fs.lstat(path)).isDirectory) throw syncError("EISDIR", path);
999
+ await fs.rm(path);
1000
+ return done;
1001
+ case "rmdir":
1002
+ if (!(await fs.lstat(path)).isDirectory) throw syncError("ENOTDIR", path);
1003
+ if ((await fs.readdir(path)).length > 0) throw syncError("ENOTEMPTY", path);
1004
+ await fs.rm(path, { recursive: true });
1005
+ return done;
1006
+ case "symlink":
1007
+ await fs.symlink(req.arg2 ?? "", path);
1008
+ return done;
1009
+ case "readlink":
1010
+ return json(await fs.readlink(path));
1011
+ case "chmod":
1012
+ await fs.chmod(path, req.mode ?? 0);
1013
+ return done;
1014
+ case "utimes":
1015
+ await fs.utimes(path, new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0));
1016
+ return done;
1017
+ default:
1018
+ return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
1019
+ }
1020
+ }
1021
+ async function dispatchSyncFs(req) {
1022
+ const entry = resolveSyncFsToken(req.token);
1023
+ if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
1024
+ const { fs, cwd } = entry;
1025
+ try {
1026
+ return await run(fs, fs.resolvePath(cwd, req.path), req, cwd);
1027
+ } catch (err) {
1028
+ return toErrno(err);
1029
+ }
1030
+ }
1031
+
1032
+ // src/realm/sync-sab-wire.ts
1033
+ var SAB_HEADER_I32 = 16;
1034
+ var SAB_HEADER_BYTES = SAB_HEADER_I32 * 4;
1035
+ var SAB_I_STATE = 0;
1036
+ var SAB_I_SEQ = 1;
1037
+ var SAB_I_STATUS = 2;
1038
+ var SAB_I_TOTAL = 3;
1039
+ var SAB_I_CHUNK = 4;
1040
+ var SAB_I_OFFSET = 5;
1041
+ var SAB_I_SIGNALS = 8;
1042
+ var SAB_I_TIMERS = 9;
1043
+ var SAB_STATE_READY = 2;
1044
+ var SAB_STATUS_BYTES = 1;
1045
+ var SAB_STATUS_JSON = 2;
1046
+ var SAB_STATUS_VOID = 3;
1047
+ var SAB_STATUS_ERRNO = 4;
1048
+ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
1049
+ var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
1050
+ var SYNC_SAB_REQ_MSG = "sync-sab-req";
1051
+ var SYNC_SAB_NEXT_MSG = "sync-sab-next";
1052
+ var encoder = new TextEncoder();
1053
+ var decoder = new TextDecoder();
1054
+ function encodeSabResult(result) {
1055
+ if (!result.ok) {
1056
+ return {
1057
+ status: SAB_STATUS_ERRNO,
1058
+ payload: encoder.encode(JSON.stringify({ errno: result.errno, message: result.message }))
1059
+ };
1060
+ }
1061
+ switch (result.kind) {
1062
+ case "bytes":
1063
+ return { status: SAB_STATUS_BYTES, payload: result.bytes };
1064
+ case "json":
1065
+ return { status: SAB_STATUS_JSON, payload: encoder.encode(JSON.stringify(result.json)) };
1066
+ default:
1067
+ return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
1068
+ }
1069
+ }
1070
+ function sabViews(sab) {
1071
+ if (sab.byteLength < SAB_MIN_BYTES) {
1072
+ throw new Error(`sync-sab: buffer too small (${sab.byteLength} < ${SAB_MIN_BYTES})`);
1073
+ }
1074
+ return {
1075
+ header: new Int32Array(sab, 0, SAB_HEADER_I32),
1076
+ window: new Uint8Array(sab, SAB_HEADER_BYTES)
1077
+ };
1078
+ }
1079
+
1080
+ // src/realm/sync-sab-responder.ts
1081
+ var PENDING_TTL_MS = SYNC_FS_REQUEST_TIMEOUT_MS + 5e3;
1082
+ function attachSyncSabResponder(port, sab, token, opts) {
1083
+ const views = sabViews(sab);
1084
+ const { header, window } = views;
1085
+ const pending = /* @__PURE__ */ new Map();
1086
+ let disposed = false;
1087
+ const { dispatch } = opts;
1088
+ function drop(id) {
1089
+ const entry = pending.get(id);
1090
+ if (!entry) return;
1091
+ clearTimeout(entry.timer);
1092
+ pending.delete(id);
1093
+ }
1094
+ function publish(id, entry, offset) {
1095
+ const chunk = Math.min(window.byteLength, entry.payload.byteLength - offset);
1096
+ if (chunk > 0) window.set(entry.payload.subarray(offset, offset + chunk));
1097
+ Atomics.store(header, SAB_I_STATUS, entry.status);
1098
+ Atomics.store(header, SAB_I_TOTAL, entry.payload.byteLength);
1099
+ Atomics.store(header, SAB_I_CHUNK, chunk);
1100
+ Atomics.store(header, SAB_I_OFFSET, offset);
1101
+ Atomics.store(header, SAB_I_SEQ, id);
1102
+ Atomics.store(header, SAB_I_STATE, SAB_STATE_READY);
1103
+ Atomics.notify(header, SAB_I_STATE);
1104
+ if (offset + chunk >= entry.payload.byteLength) drop(id);
1105
+ }
1106
+ function settle(id, result) {
1107
+ if (disposed) return;
1108
+ const { status, payload } = encodeSabResult(result);
1109
+ drop(id);
1110
+ const entry = {
1111
+ status,
1112
+ payload,
1113
+ timer: setTimeout(() => drop(id), PENDING_TTL_MS)
1114
+ };
1115
+ pending.set(id, entry);
1116
+ publish(id, entry, 0);
1117
+ }
1118
+ const handler = (event) => {
1119
+ const data = event.data;
1120
+ if (!data || typeof data.id !== "number") return;
1121
+ if (data.type === SYNC_SAB_NEXT_MSG) {
1122
+ const entry = pending.get(data.id);
1123
+ const offset = data.offset;
1124
+ if (!entry || typeof offset !== "number" || !Number.isInteger(offset) || offset < 0 || offset > entry.payload.byteLength) {
1125
+ settle(data.id, { ok: false, errno: "EIO", message: "sync-sab: bad continuation" });
1126
+ return;
1127
+ }
1128
+ publish(data.id, entry, offset);
1129
+ return;
1130
+ }
1131
+ if (data.type !== SYNC_SAB_REQ_MSG || !data.req || typeof data.req !== "object") return;
1132
+ const id = data.id;
1133
+ const body = data.req;
1134
+ const req = { ...body, token };
1135
+ let dispatched;
1136
+ try {
1137
+ dispatched = dispatch(req);
1138
+ } catch (err) {
1139
+ settle(id, {
1140
+ ok: false,
1141
+ errno: "EIO",
1142
+ message: err instanceof Error ? err.message : String(err)
1143
+ });
1144
+ return;
1145
+ }
1146
+ void dispatched.then(
1147
+ (result) => settle(id, result),
1148
+ (err) => settle(id, {
1149
+ ok: false,
1150
+ errno: "EIO",
1151
+ message: err instanceof Error ? err.message : String(err)
1152
+ })
1153
+ );
1154
+ };
1155
+ port.addEventListener("message", handler);
1156
+ return {
1157
+ dispose: () => {
1158
+ if (disposed) return;
1159
+ disposed = true;
1160
+ port.removeEventListener("message", handler);
1161
+ for (const id of [...pending.keys()]) drop(id);
1162
+ }
1163
+ };
1164
+ }
1165
+
1166
+ // src/kernel/signals.ts
1167
+ var SIG = {
1168
+ HUP: 1,
1169
+ INT: 2,
1170
+ QUIT: 3,
1171
+ ILL: 4,
1172
+ TRAP: 5,
1173
+ ABRT: 6,
1174
+ BUS: 7,
1175
+ FPE: 8,
1176
+ KILL: 9,
1177
+ USR1: 10,
1178
+ SEGV: 11,
1179
+ USR2: 12,
1180
+ PIPE: 13,
1181
+ ALRM: 14,
1182
+ TERM: 15,
1183
+ STKFLT: 16,
1184
+ CHLD: 17,
1185
+ CONT: 18,
1186
+ STOP: 19,
1187
+ TSTP: 20,
1188
+ TTIN: 21,
1189
+ TTOU: 22,
1190
+ URG: 23,
1191
+ XCPU: 24,
1192
+ XFSZ: 25,
1193
+ VTALRM: 26,
1194
+ PROF: 27,
1195
+ WINCH: 28,
1196
+ POLL: 29,
1197
+ PWR: 30,
1198
+ SYS: 31
1199
+ };
1200
+ var IGNORED_BY_DEFAULT = /* @__PURE__ */ new Set([SIG.CHLD, SIG.URG, SIG.WINCH]);
1201
+ var STOPPING = /* @__PURE__ */ new Set([SIG.STOP, SIG.TSTP, SIG.TTIN, SIG.TTOU]);
1202
+ function defaultAction(sig) {
1203
+ if (sig === SIG.CONT) return "continue";
1204
+ if (STOPPING.has(sig)) return "stop";
1205
+ return IGNORED_BY_DEFAULT.has(sig) ? "ignore" : "terminate";
1206
+ }
1207
+ function isSignal(sig) {
1208
+ return Number.isInteger(sig) && sig >= 1 && sig <= 31;
1209
+ }
1210
+ function sigbit(sig) {
1211
+ return 1 << sig;
1212
+ }
1213
+ var SIGNAL_BY_NAME = {
1214
+ SIGINT: SIG.INT,
1215
+ SIGTERM: SIG.TERM,
1216
+ SIGKILL: SIG.KILL,
1217
+ SIGSTOP: SIG.STOP,
1218
+ SIGCONT: SIG.CONT
1219
+ };
1220
+
1221
+ // src/kernel/tty.ts
1222
+ var ICRNL = 256;
1223
+ var IUTF8 = 16384;
1224
+ var OPOST = 1;
1225
+ var ONLCR = 4;
1226
+ var ISIG = 1;
1227
+ var ICANON = 2;
1228
+ var ECHO = 8;
1229
+ var ECHOE = 16;
1230
+ var ECHOCTL = 512;
1231
+ var NOFLSH = 128;
1232
+ var IEXTEN = 32768;
1233
+ var VINTR = 0;
1234
+ var VQUIT = 1;
1235
+ var VERASE = 2;
1236
+ var VKILL = 3;
1237
+ var VEOF = 4;
1238
+ var VSUSP = 10;
1239
+ var VWERASE = 14;
1240
+ function defaultTermios() {
1241
+ const cCc = new Array(32).fill(0);
1242
+ Object.assign(cCc, { [VINTR]: 3, [VQUIT]: 28, [VERASE]: 127, [VKILL]: 21 });
1243
+ Object.assign(cCc, { [VEOF]: 4, 6: 1, 8: 17, 9: 19, [VSUSP]: 26 });
1244
+ Object.assign(cCc, { 12: 18, 13: 15, [VWERASE]: 23, 15: 22 });
1245
+ return {
1246
+ c_iflag: ICRNL | 1024 | 8192 | IUTF8,
1247
+ c_oflag: OPOST | ONLCR,
1248
+ c_cflag: 15 | 48 | 128,
1249
+ c_lflag: ISIG | ICANON | ECHO | ECHOE | 32 | ECHOCTL | 2048 | IEXTEN,
1250
+ c_cc: cCc
1251
+ };
1252
+ }
1253
+ var encoder2 = new TextEncoder();
1254
+ function isContinuation(byte) {
1255
+ return (byte & 192) === 128;
1256
+ }
1257
+ var KernelTty = class {
1258
+ termios = defaultTermios();
1259
+ cols = 80;
1260
+ rows = 24;
1261
+ readable = [];
1262
+ line = [];
1263
+ waiters = [];
1264
+ hungUp = false;
1265
+ name;
1266
+ screen;
1267
+ signal;
1268
+ constructor(screen, signal) {
1269
+ this.screen = screen;
1270
+ this.signal = signal;
1271
+ }
1272
+ receive(bytes) {
1273
+ for (const byte of bytes) this.key(byte);
1274
+ this.wake();
1275
+ }
1276
+ resize(cols, rows) {
1277
+ this.setSize(cols, rows);
1278
+ this.signal(SIG.WINCH);
1279
+ }
1280
+ hangup() {
1281
+ this.hungUp = true;
1282
+ this.wake();
1283
+ }
1284
+ signalHangup() {
1285
+ this.signal(SIG.HUP);
1286
+ }
1287
+ setSize(cols, rows) {
1288
+ this.cols = cols;
1289
+ this.rows = rows;
1290
+ }
1291
+ tcgets() {
1292
+ return { ...this.termios, c_cc: [...this.termios.c_cc] };
1293
+ }
1294
+ tcsets(termios) {
1295
+ const wasCanonical = this.canonical;
1296
+ this.termios = { ...termios, c_cc: [...termios.c_cc] };
1297
+ if (wasCanonical && !this.canonical && this.line.length > 0) {
1298
+ this.readable.push(Uint8Array.from(this.line));
1299
+ this.line = [];
1300
+ this.wake();
1301
+ }
1302
+ }
1303
+ winsize() {
1304
+ return [this.rows, this.cols];
1305
+ }
1306
+ file() {
1307
+ return new OpenFile({
1308
+ read: (max, signal) => this.read(max, signal),
1309
+ write: async (bytes) => {
1310
+ if (this.hungUp) throw new KernelError("EIO");
1311
+ this.output(bytes);
1312
+ return bytes.length;
1313
+ },
1314
+ poll: () => ({
1315
+ readable: this.readable.length > 0 || this.hungUp,
1316
+ writable: true,
1317
+ hangup: this.hungUp
1318
+ }),
1319
+ changed: (signal) => this.changed(signal),
1320
+ close: () => {
1321
+ },
1322
+ tty: this
1323
+ });
1324
+ }
1325
+ get canonical() {
1326
+ return (this.termios.c_lflag & ICANON) !== 0;
1327
+ }
1328
+ cc(slot) {
1329
+ return this.termios.c_cc[slot] ?? 0;
1330
+ }
1331
+ key(input) {
1332
+ const { c_iflag, c_lflag } = this.termios;
1333
+ const byte = input === 13 && c_iflag & ICRNL ? 10 : input;
1334
+ if (c_lflag & ISIG && this.signalKey(byte)) return;
1335
+ if (!this.canonical) {
1336
+ this.readable.push(Uint8Array.of(byte));
1337
+ if (c_lflag & ECHO) this.echo(byte);
1338
+ return;
1339
+ }
1340
+ this.edit(byte);
1341
+ }
1342
+ signalKey(byte) {
1343
+ const sig = byte === this.cc(VINTR) ? SIG.INT : byte === this.cc(VQUIT) ? SIG.QUIT : byte === this.cc(VSUSP) ? SIG.TSTP : 0;
1344
+ if (!sig || byte === 0) return false;
1345
+ if (this.termios.c_lflag & ECHO) this.echo(byte);
1346
+ if (!(this.termios.c_lflag & NOFLSH)) {
1347
+ this.line = [];
1348
+ this.readable = [];
1349
+ }
1350
+ this.signal(sig);
1351
+ return true;
1352
+ }
1353
+ edit(byte) {
1354
+ const echo = (this.termios.c_lflag & ECHO) !== 0;
1355
+ if (byte === this.cc(VERASE) || byte === 8) {
1356
+ this.rubOut(this.lastCharLength());
1357
+ } else if (byte === this.cc(VKILL)) {
1358
+ this.rubOut(this.line.length);
1359
+ } else if (byte === this.cc(VWERASE)) {
1360
+ this.rubOut(this.lastWordLength());
1361
+ } else if (byte === this.cc(VEOF)) {
1362
+ this.readable.push(this.line.length > 0 ? Uint8Array.from(this.line) : null);
1363
+ this.line = [];
1364
+ } else if (byte === 10) {
1365
+ this.line.push(byte);
1366
+ if (echo) this.output(encoder2.encode("\n"));
1367
+ this.readable.push(Uint8Array.from(this.line));
1368
+ this.line = [];
1369
+ } else {
1370
+ this.line.push(byte);
1371
+ if (echo) this.echo(byte);
1372
+ }
1373
+ }
1374
+ lastWordLength() {
1375
+ let end = this.line.length;
1376
+ while (end > 0 && this.line[end - 1] === 32) end--;
1377
+ while (end > 0 && this.line[end - 1] !== 32) end--;
1378
+ return this.line.length - end;
1379
+ }
1380
+ lastCharLength() {
1381
+ const { line } = this;
1382
+ let n = Math.min(1, line.length);
1383
+ if (this.termios.c_iflag & IUTF8) {
1384
+ while (n < line.length && isContinuation(line[line.length - n] ?? 0)) n++;
1385
+ }
1386
+ return n;
1387
+ }
1388
+ rubOut(n) {
1389
+ const dropped = this.line.splice(this.line.length - n, n);
1390
+ const utf8 = (this.termios.c_iflag & IUTF8) !== 0;
1391
+ const cells = utf8 ? dropped.filter((b) => !isContinuation(b)).length : dropped.length;
1392
+ if (cells > 0 && this.termios.c_lflag & ECHO) {
1393
+ this.output(encoder2.encode("\b \b".repeat(cells)), false);
1394
+ }
1395
+ }
1396
+ echo(byte) {
1397
+ if (byte < 32 && byte !== 10 && byte !== 9 && this.termios.c_lflag & ECHOCTL) {
1398
+ this.output(Uint8Array.of(94, byte + 64), false);
1399
+ return;
1400
+ }
1401
+ this.output(Uint8Array.of(byte));
1402
+ }
1403
+ output(bytes, translate2 = true) {
1404
+ const { c_oflag } = this.termios;
1405
+ if (!translate2 || !(c_oflag & OPOST && c_oflag & ONLCR) || !bytes.includes(10)) {
1406
+ this.screen.write(bytes);
1407
+ return;
1408
+ }
1409
+ const out = [];
1410
+ for (const byte of bytes) {
1411
+ if (byte === 10) out.push(13);
1412
+ out.push(byte);
1413
+ }
1414
+ this.screen.write(Uint8Array.from(out));
1415
+ }
1416
+ async read(max, signal) {
1417
+ while (this.readable.length === 0) {
1418
+ if (this.hungUp) return new Uint8Array(0);
1419
+ await this.changed(signal);
1420
+ }
1421
+ const head = this.readable[0];
1422
+ if (head === null) {
1423
+ this.readable.shift();
1424
+ return new Uint8Array(0);
1425
+ }
1426
+ if (head === void 0) return new Uint8Array(0);
1427
+ const chunks = [];
1428
+ let n = 0;
1429
+ while (this.readable.length > 0 && n < max) {
1430
+ const next = this.readable[0];
1431
+ if (next === null || next === void 0) break;
1432
+ const take = next.subarray(0, max - n);
1433
+ chunks.push(take);
1434
+ n += take.length;
1435
+ if (take.length < next.length) this.readable[0] = next.subarray(take.length);
1436
+ else this.readable.shift();
1437
+ if (this.canonical) break;
1438
+ }
1439
+ const out = new Uint8Array(n);
1440
+ let at = 0;
1441
+ for (const chunk of chunks) {
1442
+ out.set(chunk, at);
1443
+ at += chunk.length;
1444
+ }
1445
+ return out;
1446
+ }
1447
+ changed(signal) {
1448
+ if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
1449
+ return new Promise((resolve, reject) => {
1450
+ const onAbort = () => {
1451
+ this.waiters = this.waiters.filter((w) => w !== waiter);
1452
+ reject(new KernelError("EINTR"));
1453
+ };
1454
+ const waiter = () => {
1455
+ signal?.removeEventListener("abort", onAbort);
1456
+ resolve();
1457
+ };
1458
+ this.waiters.push(waiter);
1459
+ signal?.addEventListener("abort", onAbort, { once: true });
1460
+ });
1461
+ }
1462
+ wake() {
1463
+ const waiters = this.waiters;
1464
+ this.waiters = [];
1465
+ for (const resolve of waiters) resolve();
1466
+ }
1467
+ };
1468
+
1469
+ // src/kernel/pty.ts
1470
+ var PtyPair = class {
1471
+ slave;
1472
+ locked = true;
1473
+ packet = false;
1474
+ queue = [];
1475
+ waiters = [];
1476
+ masterOpen = true;
1477
+ slaveRefs = 0;
1478
+ slaveSeen = false;
1479
+ index;
1480
+ onFree;
1481
+ constructor(index, signal, onFree) {
1482
+ this.index = index;
1483
+ this.onFree = onFree;
1484
+ this.slave = new KernelTty(
1485
+ { write: (bytes) => this.fromSlave(bytes) },
1486
+ (sig) => signal(this.slave, sig)
1487
+ );
1488
+ this.slave.name = `/dev/pts/${index}`;
1489
+ }
1490
+ master() {
1491
+ return new OpenFile({
1492
+ read: (max, signal) => this.readMaster(max, signal),
1493
+ write: async (bytes) => {
1494
+ this.slave.receive(bytes);
1495
+ return bytes.length;
1496
+ },
1497
+ poll: () => ({
1498
+ readable: this.queue.length > 0 || this.slaveGone,
1499
+ writable: true,
1500
+ hangup: this.slaveGone
1501
+ }),
1502
+ changed: (signal) => this.changed(signal),
1503
+ close: () => this.closeMaster(),
1504
+ pty: this
1505
+ });
1506
+ }
1507
+ openSlave() {
1508
+ if (!this.masterOpen || this.locked) throw new KernelError("EIO");
1509
+ const file = this.slave.file().file;
1510
+ this.slaveRefs += 1;
1511
+ this.slaveSeen = true;
1512
+ return new OpenFile({ ...file, close: () => this.closeSlave() });
1513
+ }
1514
+ get slaveGone() {
1515
+ return this.slaveSeen && this.slaveRefs === 0;
1516
+ }
1517
+ fromSlave(bytes) {
1518
+ if (!this.masterOpen) return;
1519
+ this.queue.push(bytes.slice());
1520
+ this.wake();
1521
+ }
1522
+ async readMaster(max, signal) {
1523
+ while (this.queue.length === 0) {
1524
+ if (this.slaveGone) throw new KernelError("EIO");
1525
+ await this.changed(signal);
1526
+ }
1527
+ const status = this.packet ? 1 : 0;
1528
+ const chunks = [];
1529
+ let n = 0;
1530
+ while (this.queue.length > 0 && n < max - status) {
1531
+ const next = this.queue[0];
1532
+ const take = next.subarray(0, max - status - n);
1533
+ chunks.push(take);
1534
+ n += take.length;
1535
+ if (take.length < next.length) this.queue[0] = next.subarray(take.length);
1536
+ else this.queue.shift();
1537
+ }
1538
+ const out = new Uint8Array(status + n);
1539
+ let at = status;
1540
+ for (const chunk of chunks) {
1541
+ out.set(chunk, at);
1542
+ at += chunk.length;
1543
+ }
1544
+ return out;
1545
+ }
1546
+ closeMaster() {
1547
+ this.masterOpen = false;
1548
+ this.queue = [];
1549
+ this.slave.hangup();
1550
+ this.slave.signalHangup();
1551
+ this.freeIfUnused();
1552
+ }
1553
+ closeSlave() {
1554
+ this.slaveRefs -= 1;
1555
+ this.wake();
1556
+ this.freeIfUnused();
1557
+ }
1558
+ freeIfUnused() {
1559
+ if (!this.masterOpen && this.slaveRefs === 0) this.onFree(this);
1560
+ }
1561
+ changed(signal) {
1562
+ if (signal?.aborted) return Promise.reject(new KernelError("EINTR"));
1563
+ return new Promise((resolve, reject) => {
1564
+ const onAbort = () => {
1565
+ this.waiters = this.waiters.filter((w) => w !== waiter);
1566
+ reject(new KernelError("EINTR"));
1567
+ };
1568
+ const waiter = () => {
1569
+ signal?.removeEventListener("abort", onAbort);
1570
+ resolve();
1571
+ };
1572
+ this.waiters.push(waiter);
1573
+ signal?.addEventListener("abort", onAbort, { once: true });
1574
+ });
1575
+ }
1576
+ wake() {
1577
+ const waiters = this.waiters;
1578
+ this.waiters = [];
1579
+ for (const resolve of waiters) resolve();
1580
+ }
1581
+ };
1582
+ var PtyTable = class {
1583
+ pairs = /* @__PURE__ */ new Map();
1584
+ signal;
1585
+ constructor(signal) {
1586
+ this.signal = signal;
1587
+ }
1588
+ open() {
1589
+ let index = 0;
1590
+ while (this.pairs.has(index)) index += 1;
1591
+ const pair = new PtyPair(index, this.signal, (p) => this.pairs.delete(p.index));
1592
+ this.pairs.set(index, pair);
1593
+ return { pair, master: pair.master() };
1594
+ }
1595
+ get(index) {
1596
+ const pair = this.pairs.get(index);
1597
+ if (!pair) throw new KernelError("ENOENT");
1598
+ return pair;
1599
+ }
1600
+ numbers() {
1601
+ return [...this.pairs.keys()].sort((a, b) => a - b);
1602
+ }
1603
+ };
1604
+ var PTY_OPS = [
1605
+ "pty-open",
1606
+ "pty-slave-open",
1607
+ "pty-number",
1608
+ "pty-lock",
1609
+ "pty-ctty",
1610
+ "pty-packet",
1611
+ "pty-list",
1612
+ "pty-winsz-set"
1613
+ ];
1614
+ function masterOf(ctx, fd) {
1615
+ const pair = ctx.fds.get(fd).file.pty;
1616
+ if (!pair) throw new KernelError("ENOTTY");
1617
+ return pair;
1618
+ }
1619
+ function terminalOf(ctx, fd) {
1620
+ const file = ctx.fds.get(fd).file;
1621
+ const tty = file.tty ?? file.pty?.slave;
1622
+ if (!tty) throw new KernelError("ENOTTY");
1623
+ return tty;
1624
+ }
1625
+ var json2 = (value) => ({ ok: true, kind: "json", json: value });
1626
+ var done2 = { ok: true, kind: "void" };
1627
+ function ptySyscall(req, ctx) {
1628
+ switch (req.op) {
1629
+ case "pty-open": {
1630
+ if (!ctx.ptys) throw new KernelError("ENOENT");
1631
+ return json2(ctx.fds.install(ctx.ptys.open().master, 3));
1632
+ }
1633
+ case "pty-slave-open": {
1634
+ if (!ctx.ptys) throw new KernelError("ENOENT");
1635
+ const pair = ctx.ptys.get(req.n);
1636
+ const fd = ctx.fds.install(pair.openSlave(), 3);
1637
+ if (!req.noctty) ctx.jobs?.acquireTerminal(ctx.pid, pair.slave);
1638
+ return json2(fd);
1639
+ }
1640
+ case "pty-number":
1641
+ return json2(masterOf(ctx, req.fd).index);
1642
+ case "pty-lock":
1643
+ masterOf(ctx, req.fd).locked = req.lock;
1644
+ return done2;
1645
+ case "pty-ctty": {
1646
+ const tty = terminalOf(ctx, req.fd);
1647
+ const leader = ctx.jobs?.getsid(ctx.pid, 0) === ctx.pid;
1648
+ if (leader && ctx.jobs?.controllingTerminal(ctx.pid) === tty) return done2;
1649
+ if (!ctx.jobs?.acquireTerminal(ctx.pid, tty)) throw new KernelError("EPERM");
1650
+ return done2;
1651
+ }
1652
+ case "pty-list":
1653
+ return json2(ctx.ptys?.numbers() ?? []);
1654
+ case "pty-packet":
1655
+ masterOf(ctx, req.fd).packet = req.on;
1656
+ return done2;
1657
+ case "pty-winsz-set":
1658
+ terminalOf(ctx, req.fd).resize(req.cols, req.rows);
1659
+ return done2;
1660
+ }
1661
+ }
1662
+
1663
+ // src/kernel/select.ts
1664
+ function ready(fds, read, write) {
1665
+ const hangup = /* @__PURE__ */ new Set();
1666
+ const pick = (list, dir) => list.filter((fd) => {
1667
+ const state = pollFile(fds.get(fd).file);
1668
+ if (state.hangup) hangup.add(fd);
1669
+ return state[dir] || state.hangup;
1670
+ });
1671
+ const out = { read: pick(read, "readable"), write: pick(write, "writable") };
1672
+ if (hangup.size > 0) out.hangup = [...hangup];
1673
+ return out;
1674
+ }
1675
+ function interruption(signal) {
1676
+ const { promise, reject } = Promise.withResolvers();
1677
+ const fail = () => reject(new KernelError("EINTR"));
1678
+ if (signal.aborted) fail();
1679
+ else signal.addEventListener("abort", fail, { once: true });
1680
+ return { promise, done: () => signal.removeEventListener("abort", fail) };
1681
+ }
1682
+ async function selectFds(fds, read, write, timeoutMs, interrupt) {
1683
+ const deadline = timeoutMs < 0 ? Number.POSITIVE_INFINITY : Date.now() + timeoutMs;
1684
+ const interrupted2 = interruption(interrupt);
1685
+ interrupted2.promise.catch(() => {
1686
+ });
1687
+ try {
1688
+ return await waitReady(fds, read, write, deadline, interrupted2.promise);
1689
+ } finally {
1690
+ interrupted2.done();
1691
+ }
1692
+ }
1693
+ async function waitReady(fds, read, write, deadline, interrupted2) {
1694
+ for (; ; ) {
1695
+ const now = ready(fds, read, write);
1696
+ if (now.read.length > 0 || now.write.length > 0 || Date.now() >= deadline) return now;
1697
+ const round = new AbortController();
1698
+ const changes = [.../* @__PURE__ */ new Set([...read, ...write])].flatMap((fd) => {
1699
+ const file = fds.get(fd).file;
1700
+ return file.changed === void 0 ? [] : [file.changed(round.signal).catch(() => void 0)];
1701
+ });
1702
+ let timer;
1703
+ const expiry = deadline === Number.POSITIVE_INFINITY ? [] : [new Promise((resolve) => timer = setTimeout(resolve, deadline - Date.now()))];
1704
+ try {
1705
+ await Promise.race([...changes, ...expiry, interrupted2]);
1706
+ } finally {
1707
+ round.abort();
1708
+ clearTimeout(timer);
1709
+ }
1710
+ }
1711
+ }
1712
+
1713
+ // src/kernel/vfs-file.ts
1714
+ var O_ACCMODE = 3;
1715
+ var O_WRONLY = 1;
1716
+ var O_RDWR = 2;
1717
+ var O_APPEND = 1024;
1718
+ var SEEK_SET = 0;
1719
+ var SEEK_CUR = 1;
1720
+ var SEEK_END = 2;
1721
+ var WRITEBACK_MS = 250;
1722
+ var WRITEBACK_COST_FACTOR = 10;
1723
+ function isMissing(err) {
1724
+ if (err?.code === "ENOENT") return true;
1725
+ return err instanceof Error && err.message.startsWith("ENOENT");
1726
+ }
1727
+ function within2(path, root) {
1728
+ return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
1729
+ }
1730
+ var VfsNode = class {
1731
+ data;
1732
+ length = 0;
1733
+ dirty = false;
1734
+ queue = Promise.resolve();
1735
+ writeBack;
1736
+ writeBackCost = 0;
1737
+ opens = 0;
1738
+ missing = false;
1739
+ fs;
1740
+ path;
1741
+ orphaned;
1742
+ constructor(fs, path, contents, orphaned = false, dirty = false) {
1743
+ this.fs = fs;
1744
+ this.path = path;
1745
+ this.orphaned = orphaned;
1746
+ if (contents !== void 0) this.data = new Uint8Array(contents);
1747
+ else if (orphaned) this.data = new Uint8Array(0);
1748
+ this.length = this.data?.length ?? 0;
1749
+ if (dirty && contents !== void 0) this.markDirty();
1750
+ }
1751
+ serial(op) {
1752
+ const next = this.queue.then(op);
1753
+ this.queue = next.catch(() => void 0);
1754
+ return next;
1755
+ }
1756
+ async load() {
1757
+ if (!this.data) {
1758
+ try {
1759
+ this.data = await this.fs.readFileBuffer(this.path);
1760
+ } catch (err) {
1761
+ if (!isMissing(err)) throw err;
1762
+ this.data = new Uint8Array(0);
1763
+ this.missing = true;
1764
+ }
1765
+ this.length = this.data.length;
1766
+ }
1767
+ return this.data;
1768
+ }
1769
+ replace(contents) {
1770
+ this.data = new Uint8Array(contents);
1771
+ this.length = this.data.length;
1772
+ this.missing = false;
1773
+ this.markDirty();
1774
+ }
1775
+ async materialize() {
1776
+ await this.load();
1777
+ if (!this.missing || this.orphaned) return;
1778
+ this.missing = false;
1779
+ await this.fs.writeFile(this.path, this.data?.slice(0, this.length) ?? new Uint8Array(0));
1780
+ }
1781
+ async size() {
1782
+ await this.load();
1783
+ return this.length;
1784
+ }
1785
+ async pread(max, at) {
1786
+ const bytes = await this.load();
1787
+ const n = Math.max(0, Math.min(max, this.length - at));
1788
+ return bytes.slice(at, at + n);
1789
+ }
1790
+ async pwrite(bytes, at) {
1791
+ await this.load();
1792
+ const buf = this.ensure(at + bytes.length);
1793
+ if (at > this.length) buf.fill(0, this.length, at);
1794
+ buf.set(bytes, at);
1795
+ this.length = Math.max(this.length, at + bytes.length);
1796
+ this.markDirty();
1797
+ return bytes.length;
1798
+ }
1799
+ async truncate(size) {
1800
+ await this.load();
1801
+ const buf = this.ensure(size);
1802
+ if (size > this.length) buf.fill(0, this.length, size);
1803
+ this.length = size;
1804
+ this.markDirty();
1805
+ }
1806
+ markDirty() {
1807
+ this.dirty = true;
1808
+ if (this.writeBack !== void 0 || this.orphaned) return;
1809
+ const delay = Math.max(WRITEBACK_MS, this.writeBackCost * WRITEBACK_COST_FACTOR);
1810
+ this.writeBack = setTimeout(() => {
1811
+ this.writeBack = void 0;
1812
+ this.serial(() => this.flush()).catch(() => void 0);
1813
+ }, delay);
1814
+ }
1815
+ ensure(need) {
1816
+ const cur = this.data ?? new Uint8Array(0);
1817
+ if (cur.length >= need) return cur;
1818
+ const grown = new Uint8Array(Math.max(need, cur.length * 2, 256));
1819
+ grown.set(cur.subarray(0, this.length));
1820
+ this.data = grown;
1821
+ return grown;
1822
+ }
1823
+ async flush() {
1824
+ if (!this.dirty || !this.data || this.orphaned) return;
1825
+ this.dirty = false;
1826
+ const started = performance.now();
1827
+ try {
1828
+ await this.fs.writeFile(this.path, this.data.slice(0, this.length));
1829
+ } catch (err) {
1830
+ this.dirty = true;
1831
+ throw err;
1832
+ }
1833
+ this.writeBackCost = performance.now() - started;
1834
+ }
1835
+ };
1836
+ var VfsNodes = class {
1837
+ byPath = /* @__PURE__ */ new Map();
1838
+ fs;
1839
+ constructor(fs) {
1840
+ this.fs = fs;
1841
+ }
1842
+ open(path) {
1843
+ let node = this.byPath.get(path);
1844
+ if (!node) {
1845
+ node = new VfsNode(this.fs, path);
1846
+ this.byPath.set(path, node);
1847
+ }
1848
+ node.opens++;
1849
+ return node;
1850
+ }
1851
+ adopt(path, contents, dirty) {
1852
+ let node = this.byPath.get(path);
1853
+ if (!node) {
1854
+ node = new VfsNode(this.fs, path, contents, false, dirty);
1855
+ this.byPath.set(path, node);
1856
+ } else if (dirty) {
1857
+ node.replace(contents);
1858
+ }
1859
+ node.opens++;
1860
+ return node;
1861
+ }
1862
+ closed(node) {
1863
+ node.opens--;
1864
+ if (node.opens === 0 && this.byPath.get(node.path) === node) this.byPath.delete(node.path);
1865
+ }
1866
+ async flush(path) {
1867
+ for (const [p, node] of this.byPath) if (within2(p, path)) await node.serial(() => node.flush());
1868
+ }
1869
+ async unlinking(path) {
1870
+ const node = this.byPath.get(path);
1871
+ if (node) await node.serial(() => node.load());
1872
+ }
1873
+ unlinked(path) {
1874
+ const node = this.byPath.get(path);
1875
+ if (!node) return;
1876
+ node.orphaned = true;
1877
+ this.byPath.delete(path);
1878
+ }
1879
+ renamed(from, to) {
1880
+ if (from === to) return;
1881
+ const moved = [];
1882
+ for (const [p, node] of this.byPath) {
1883
+ if (within2(p, from)) moved.push(node);
1884
+ else if (within2(p, to)) {
1885
+ node.orphaned = true;
1886
+ this.byPath.delete(p);
1887
+ }
1888
+ }
1889
+ for (const node of moved) {
1890
+ this.byPath.delete(node.path);
1891
+ node.path = to + node.path.slice(from.length);
1892
+ this.byPath.set(node.path, node);
1893
+ }
1894
+ }
1895
+ };
1896
+ function vfsFile(fs, opts, nodes) {
1897
+ const access = opts.flags & O_ACCMODE;
1898
+ const readable = access !== O_WRONLY;
1899
+ const writable = access === O_WRONLY || access === O_RDWR;
1900
+ const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true) : nodes.open(opts.path);
1901
+ let openError;
1902
+ const atOpen = (op) => {
1903
+ void node.serial(op).catch((err) => {
1904
+ openError ??= { err };
1905
+ });
1906
+ };
1907
+ if (opts.create) atOpen(() => node.materialize());
1908
+ if (opts.truncate) atOpen(() => node.truncate(0));
1909
+ const takeOpenError = () => {
1910
+ if (!openError) return;
1911
+ const { err } = openError;
1912
+ openError = void 0;
1913
+ throw err;
1914
+ };
1915
+ let offset = opts.position;
1916
+ const serial = (op) => node.serial(() => {
1917
+ takeOpenError();
1918
+ return op();
1919
+ });
1920
+ return new OpenFile({
1921
+ read: readable ? (max) => serial(async () => {
1922
+ const out = await node.pread(max, offset);
1923
+ offset += out.length;
1924
+ return out;
1925
+ }) : void 0,
1926
+ write: writable ? (bytes) => serial(async () => {
1927
+ if (opts.flags & O_APPEND) offset = await node.size();
1928
+ offset += await node.pwrite(bytes, offset);
1929
+ return bytes.length;
1930
+ }) : void 0,
1931
+ seek: (to, whence) => serial(async () => {
1932
+ let base = 0;
1933
+ if (whence === SEEK_CUR) base = offset;
1934
+ else if (whence === SEEK_END) base = await node.size();
1935
+ else if (whence !== SEEK_SET) throw new KernelError("EINVAL");
1936
+ if (base + to < 0) throw new KernelError("EINVAL");
1937
+ offset = base + to;
1938
+ return offset;
1939
+ }),
1940
+ pread: (max, at) => serial(() => {
1941
+ if (!readable) throw new KernelError("EBADF");
1942
+ return node.pread(max, at);
1943
+ }),
1944
+ pwrite: (bytes, at) => serial(() => {
1945
+ if (!writable) throw new KernelError("EBADF");
1946
+ return node.pwrite(bytes, at);
1947
+ }),
1948
+ resize: (size) => serial(() => {
1949
+ if (!writable) throw new KernelError("EBADF");
1950
+ return node.truncate(size);
1951
+ }),
1952
+ stat: () => serial(async () => ({
1953
+ path: node.path,
1954
+ size: await node.size(),
1955
+ ...node.orphaned ? { orphan: true } : {}
1956
+ })),
1957
+ flush: () => serial(() => node.flush()),
1958
+ close: () => node.serial(async () => {
1959
+ try {
1960
+ takeOpenError();
1961
+ await node.flush();
1962
+ } finally {
1963
+ nodes?.closed(node);
1964
+ }
1965
+ })
1966
+ });
1967
+ }
1968
+
1969
+ // src/kernel/process.ts
1970
+ var JOB_OPS = /* @__PURE__ */ new Set([
1971
+ "proc-setpgid",
1972
+ "proc-getpgid",
1973
+ "proc-getsid",
1974
+ "proc-setsid"
1975
+ ]);
1976
+ function isJobSyscall(req) {
1977
+ return JOB_OPS.has(req.op);
1978
+ }
1979
+ function isPtySyscall(req) {
1980
+ return req.op.startsWith("pty-");
1981
+ }
1982
+ function isVfsSyscall(req) {
1983
+ return req.op.startsWith("fd-path-") || req.op === "fd-pread" || req.op === "fd-pwrite" || req.op === "fd-resize" || req.op === "fd-vfs-stat";
1984
+ }
1985
+ function isFdSyscall(req) {
1986
+ return req.op.startsWith("fd-");
1987
+ }
1988
+ function isTtySyscall(req) {
1989
+ return req.op.startsWith("tty-");
1990
+ }
1991
+ var SYSCALL_OPS = /* @__PURE__ */ new Set([
1992
+ "fd-read",
1993
+ "fd-write",
1994
+ "fd-close",
1995
+ "fd-pipe",
1996
+ "fd-poll",
1997
+ "fd-open-vfs",
1998
+ "fd-seek",
1999
+ "fd-pread",
2000
+ "fd-pwrite",
2001
+ "fd-resize",
2002
+ "fd-vfs-stat",
2003
+ "fd-path-flush",
2004
+ "fd-path-unlinking",
2005
+ "fd-path-unlinked",
2006
+ "fd-path-renamed",
2007
+ "fd-select",
2008
+ "fd-info",
2009
+ "fd-dup",
2010
+ "fd-reserve",
2011
+ "fd-meta",
2012
+ "fd-setfl",
2013
+ "fd-cloexec",
2014
+ "fd-list",
2015
+ "proc-alarm",
2016
+ "fd-renumber",
2017
+ "fd-promote",
2018
+ "fd-open-tty",
2019
+ "tty-get",
2020
+ "tty-set",
2021
+ "tty-winsz",
2022
+ "fd-flush",
2023
+ "proc-spawn",
2024
+ "proc-wait",
2025
+ "proc-captured",
2026
+ "proc-fork",
2027
+ "proc-kill",
2028
+ "proc-exec",
2029
+ "proc-setpgid",
2030
+ "proc-getpgid",
2031
+ "proc-getsid",
2032
+ "proc-setsid",
2033
+ "tty-pgrp-get",
2034
+ "tty-pgrp-set",
2035
+ "sig-mask",
2036
+ "sig-pause",
2037
+ ...PTY_OPS
2038
+ ]);
2039
+ function isWasmSyscall(req) {
2040
+ const op = req.op;
2041
+ return typeof op === "string" && SYSCALL_OPS.has(op);
2042
+ }
2043
+ var MAX_READ = 1024 * 1024;
2044
+ var WasmProcess = class {
2045
+ exited = false;
2046
+ children;
2047
+ caught = 0;
2048
+ ignored = 0;
2049
+ interrupt = new AbortController();
2050
+ execChild;
2051
+ execTermsig;
2052
+ stopped = 0;
2053
+ stops = 0;
2054
+ resumed = Promise.resolve();
2055
+ wake;
2056
+ stateListeners = [];
2057
+ nodes;
2058
+ pid;
2059
+ fds;
2060
+ options;
2061
+ constructor(pid, fds, options) {
2062
+ this.pid = pid;
2063
+ this.fds = fds;
2064
+ this.options = options;
2065
+ this.children = new ChildTable(fds, options.spawner, options.forker);
2066
+ this.nodes = new VfsNodes(options.fs);
2067
+ this.children.onChildState = () => this.signal(SIG.CHLD);
2068
+ this.children.onReap = options.onReap;
2069
+ }
2070
+ signal(sig) {
2071
+ if (this.execChild !== void 0) {
2072
+ void Promise.resolve(this.options.kill?.(this.execChild, sig)).catch(() => void 0);
2073
+ return sig === SIG.KILL ? "terminate" : "forward";
2074
+ }
2075
+ if (sig === SIG.KILL) return "terminate";
2076
+ if (sig === SIG.CONT) this.cont();
2077
+ if (sig === SIG.STOP) return this.stop(sig);
2078
+ const bit = sigbit(sig);
2079
+ if (this.ignored & bit) return "ignore";
2080
+ if (!(this.caught & bit)) {
2081
+ const action = defaultAction(sig);
2082
+ return action === "stop" ? this.stop(sig) : action;
2083
+ }
2084
+ this.options.onPending?.(sig);
2085
+ const blocked = this.interrupt;
2086
+ this.interrupt = new AbortController();
2087
+ blocked.abort();
2088
+ return "deliver";
2089
+ }
2090
+ onState(listener) {
2091
+ this.stateListeners.push(listener);
2092
+ }
2093
+ stop(sig) {
2094
+ if (this.stopped) return "stop";
2095
+ this.stopped = sig;
2096
+ this.stops++;
2097
+ this.resumed = new Promise((resolve) => this.wake = resolve);
2098
+ const blocked = this.interrupt;
2099
+ this.interrupt = new AbortController();
2100
+ blocked.abort();
2101
+ for (const listener of this.stateListeners) listener("stopped", sig);
2102
+ return "stop";
2103
+ }
2104
+ cont() {
2105
+ if (!this.stopped) return;
2106
+ this.stopped = 0;
2107
+ this.wake?.();
2108
+ for (const listener of this.stateListeners) listener("continued", SIG.CONT);
2109
+ }
2110
+ async syscall(req) {
2111
+ for (; ; ) {
2112
+ await this.resumed;
2113
+ const stops = this.stops;
2114
+ const result = await this.dispatch(req);
2115
+ const restart = !result.ok && result.errno === "EINTR" && this.stops !== stops;
2116
+ if (restart && !this.options.hasPending?.()) continue;
2117
+ await this.resumed;
2118
+ return result;
2119
+ }
2120
+ }
2121
+ async dispatch(req) {
2122
+ try {
2123
+ if (isFdSyscall(req)) return await this.fdSyscall(req);
2124
+ if (isTtySyscall(req)) return this.ttySyscall(req);
2125
+ if (isJobSyscall(req)) return this.jobSyscall(req);
2126
+ if (isPtySyscall(req)) {
2127
+ const { ptys, jobs } = this.options;
2128
+ return ptySyscall(req, { pid: this.pid, fds: this.fds, ptys, jobs });
2129
+ }
2130
+ return await this.procSyscall(req);
2131
+ } catch (e) {
2132
+ if (e instanceof KernelError || e instanceof SpawnError) {
2133
+ return { ok: false, errno: e.code, message: e.code };
2134
+ }
2135
+ throw e;
2136
+ }
2137
+ }
2138
+ blockingSignal() {
2139
+ if (this.options.hasPending?.()) throw new KernelError("EINTR");
2140
+ return this.interrupt.signal;
2141
+ }
2142
+ async read(req) {
2143
+ const file = this.fds.get(req.fd).file;
2144
+ const read = req.peek ? file.peek : file.read;
2145
+ if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
2146
+ if (file.tty) this.checkForeground(file.tty);
2147
+ if (req.max <= 0) return new Uint8Array(0);
2148
+ const ready2 = pollFile(file).readable;
2149
+ if (!ready2 && req.nonblock) throw new KernelError("EAGAIN");
2150
+ const signal = ready2 ? this.interrupt.signal : this.blockingSignal();
2151
+ return read.call(file, Math.max(0, Math.min(req.max, MAX_READ)), signal);
2152
+ }
2153
+ async write(fd, body, nonblock = false) {
2154
+ const file = this.fds.get(fd).file;
2155
+ if (!file.write) throw new KernelError("EBADF");
2156
+ if (!pollFile(file).writable) {
2157
+ if (nonblock) throw new KernelError("EAGAIN");
2158
+ return file.write(body, this.blockingSignal());
2159
+ }
2160
+ if (nonblock) return file.write(body, AbortSignal.abort());
2161
+ return file.write(
2162
+ body,
2163
+ this.options.hasPending?.() ? AbortSignal.abort() : this.interrupt.signal
2164
+ );
2165
+ }
2166
+ openVfsFile(req) {
2167
+ return vfsFile(
2168
+ this.options.fs,
2169
+ {
2170
+ path: req.path,
2171
+ flags: req.flags,
2172
+ position: req.position,
2173
+ ...req.contents !== void 0 ? { contents: req.contents } : {},
2174
+ ...req.orphan ? { orphan: true } : {},
2175
+ ...req.truncate ? { truncate: true } : {},
2176
+ ...req.create ? { create: true } : {}
2177
+ },
2178
+ this.nodes
2179
+ );
2180
+ }
2181
+ async fdSyscall(req) {
2182
+ if (isVfsSyscall(req)) return this.vfsSyscall(req);
2183
+ switch (req.op) {
2184
+ case "fd-read":
2185
+ return { ok: true, kind: "bytes", bytes: await this.read(req) };
2186
+ case "fd-write":
2187
+ return { ok: true, kind: "json", json: await this.write(req.fd, req.body, req.nonblock) };
2188
+ case "fd-close":
2189
+ await Promise.resolve(this.fds.close(req.fd));
2190
+ return { ok: true, kind: "void" };
2191
+ case "fd-pipe": {
2192
+ const pipe = openPipe();
2193
+ const read = this.fds.install(pipe.read, 3);
2194
+ let write;
2195
+ try {
2196
+ write = this.fds.install(pipe.write, 3);
2197
+ } catch (e) {
2198
+ await Promise.resolve(this.fds.close(read));
2199
+ throw e;
2200
+ }
2201
+ return { ok: true, kind: "json", json: [read, write] };
2202
+ }
2203
+ case "fd-poll":
2204
+ return { ok: true, kind: "json", json: pollFile(this.fds.get(req.fd).file) };
2205
+ case "fd-open-vfs":
2206
+ return { ok: true, kind: "json", json: this.fds.install(this.openVfsFile(req), 3) };
2207
+ case "fd-info":
2208
+ return { ok: true, kind: "json", json: this.fdInfo(req.fd) };
2209
+ case "fd-list":
2210
+ return {
2211
+ ok: true,
2212
+ kind: "json",
2213
+ json: this.fds.numbers().map((fd) => ({ fd, ...this.fdInfo(fd) }))
2214
+ };
2215
+ case "fd-meta": {
2216
+ const file = this.fds.get(req.fd).file;
2217
+ if (!file.held) throw new KernelError("EBADF");
2218
+ file.heldMeta = req.meta;
2219
+ return { ok: true, kind: "void" };
2220
+ }
2221
+ case "fd-setfl":
2222
+ this.fds.setStatusFlags(req.fd, req.flags);
2223
+ return { ok: true, kind: "void" };
2224
+ case "fd-cloexec":
2225
+ if (req.on) this.fds.setCloseOnExec(req.fd);
2226
+ else this.fds.clearCloseOnExec(req.fd);
2227
+ return { ok: true, kind: "void" };
2228
+ case "fd-dup":
2229
+ return { ok: true, kind: "json", json: this.fds.dup(req.fd, req.min ?? 3) };
2230
+ case "fd-reserve":
2231
+ return { ok: true, kind: "json", json: this.reserve(req.fd, req.min, req.meta) };
2232
+ case "fd-promote":
2233
+ this.promote(req);
2234
+ return { ok: true, kind: "void" };
2235
+ case "fd-renumber":
2236
+ if (req.from !== req.to) {
2237
+ this.fds.dup2(req.from, req.to);
2238
+ if (!req.keep) await Promise.resolve(this.fds.close(req.from));
2239
+ }
2240
+ return { ok: true, kind: "void" };
2241
+ case "fd-open-tty": {
2242
+ const tty = req.name === void 0 ? this.controllingTerminal() : this.options.jobs?.terminalNamed(req.name);
2243
+ if (!tty) throw new KernelError("ENXIO");
2244
+ return { ok: true, kind: "json", json: this.fds.install(tty.file(), 3) };
2245
+ }
2246
+ case "fd-select": {
2247
+ const { read, write, timeoutMs } = req;
2248
+ const signal = this.blockingSignal();
2249
+ const selected = await selectFds(this.fds, read, write, timeoutMs, signal);
2250
+ return { ok: true, kind: "json", json: selected };
2251
+ }
2252
+ case "fd-seek": {
2253
+ const file = this.fds.get(req.fd).file;
2254
+ if (!file.seek) throw new KernelError("ESPIPE");
2255
+ return { ok: true, kind: "json", json: await file.seek(req.offset, req.whence) };
2256
+ }
2257
+ case "fd-flush": {
2258
+ const file = this.fds.get(req.fd).file;
2259
+ if (file.flush) await file.flush();
2260
+ return { ok: true, kind: "void" };
2261
+ }
2262
+ }
2263
+ }
2264
+ async vfsSyscall(req) {
2265
+ if ("path" in req || "from" in req) {
2266
+ const nodes = this.nodes;
2267
+ if (req.op === "fd-path-flush") await nodes.flush(req.path);
2268
+ else if (req.op === "fd-path-unlinking") await nodes.unlinking(req.path);
2269
+ else if (req.op === "fd-path-unlinked") nodes.unlinked(req.path);
2270
+ else if (req.op === "fd-path-renamed") nodes.renamed(req.from, req.to);
2271
+ return { ok: true, kind: "void" };
2272
+ }
2273
+ const file = this.fds.get(req.fd).file;
2274
+ if (!file.pread || !file.pwrite || !file.resize || !file.stat) throw new KernelError("ESPIPE");
2275
+ switch (req.op) {
2276
+ case "fd-pread":
2277
+ return {
2278
+ ok: true,
2279
+ kind: "bytes",
2280
+ bytes: await file.pread(Math.min(req.max, MAX_READ), req.offset)
2281
+ };
2282
+ case "fd-pwrite":
2283
+ return { ok: true, kind: "json", json: await file.pwrite(req.body, req.offset) };
2284
+ case "fd-resize":
2285
+ await file.resize(req.size);
2286
+ return { ok: true, kind: "void" };
2287
+ case "fd-vfs-stat":
2288
+ return { ok: true, kind: "json", json: await file.stat() };
2289
+ }
2290
+ }
2291
+ promote(req) {
2292
+ if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
2293
+ if (req.share !== void 0) {
2294
+ this.fds.dup2(req.share, req.fd);
2295
+ return;
2296
+ }
2297
+ if (req.path === void 0) throw new KernelError("EINVAL");
2298
+ const file = vfsFile(
2299
+ this.options.fs,
2300
+ {
2301
+ path: req.path,
2302
+ flags: req.flags ?? 0,
2303
+ position: req.position ?? 0,
2304
+ ...req.contents !== void 0 ? { contents: req.contents } : {},
2305
+ ...req.orphan ? { orphan: true } : {},
2306
+ ...req.dirty ? { dirty: true } : {}
2307
+ },
2308
+ this.nodes
2309
+ );
2310
+ this.fds.installAt(req.fd, file);
2311
+ }
2312
+ reserve(fd, min = 3, meta) {
2313
+ if (fd === void 0) return this.fds.install(heldFile(meta), Math.max(3, min));
2314
+ if (this.fds.has(fd)) throw new KernelError("EBADF");
2315
+ this.fds.installAt(fd, heldFile(meta));
2316
+ return fd;
2317
+ }
2318
+ fdInfo(fd) {
2319
+ const file = this.fds.get(fd).file;
2320
+ const flags = this.fds.statusFlags(fd);
2321
+ return {
2322
+ tty: file.tty !== void 0,
2323
+ kind: kernelFdKind(file),
2324
+ ...file.heldMeta ? { meta: file.heldMeta } : {},
2325
+ ...flags !== void 0 ? { flags } : {},
2326
+ ...this.fds.closesOnExec(fd) ? { cloexec: true } : {},
2327
+ ...file.tty?.name ? { name: file.tty.name } : file.pty ? { name: "/dev/ptmx" } : {}
2328
+ };
2329
+ }
2330
+ ttySyscall(req) {
2331
+ const tty = this.tty(req.fd);
2332
+ const jobs = this.options.jobs;
2333
+ switch (req.op) {
2334
+ case "tty-get":
2335
+ return { ok: true, kind: "json", json: tty.tcgets() };
2336
+ case "tty-set":
2337
+ tty.tcsets(req.termios);
2338
+ return { ok: true, kind: "void" };
2339
+ case "tty-winsz":
2340
+ return { ok: true, kind: "json", json: tty.winsize() };
2341
+ case "tty-pgrp-get":
2342
+ return { ok: true, kind: "json", json: jobs ? jobs.tcgetpgrp(tty, this.sid()) : this.pid };
2343
+ case "tty-pgrp-set":
2344
+ if (jobs) jobs.tcsetpgrp(this.pid, tty, req.pgrp);
2345
+ else if (req.pgrp !== this.pid) throw new KernelError("EPERM");
2346
+ return { ok: true, kind: "void" };
2347
+ }
2348
+ }
2349
+ checkForeground(tty) {
2350
+ const jobs = this.options.jobs;
2351
+ if (!jobs) return;
2352
+ const session = jobs.controllingTerminal(this.pid);
2353
+ if (session !== void 0 && session !== tty) return;
2354
+ const pgid = this.pgid();
2355
+ if (jobs.tcgetpgrp(tty, this.sid()) === pgid) return;
2356
+ if (this.ignored & sigbit(SIG.TTIN)) throw new KernelError("EIO");
2357
+ jobs.killGroup(pgid, SIG.TTIN);
2358
+ throw new KernelError("EINTR");
2359
+ }
2360
+ waitGroup(pid) {
2361
+ const jobs = this.options.jobs;
2362
+ if (!jobs || pid > 0 || pid === -1) return void 0;
2363
+ const group = pid === 0 ? this.pgid() : -pid;
2364
+ return (child) => (jobs.pgidOf(child) ?? this.pgid()) === group;
2365
+ }
2366
+ pgid() {
2367
+ return this.options.jobs?.getpgid(this.pid, 0) ?? this.pid;
2368
+ }
2369
+ sid() {
2370
+ return this.options.jobs?.getsid(this.pid, 0) ?? this.pid;
2371
+ }
2372
+ controllingTerminal() {
2373
+ const session = this.options.jobs?.controllingTerminal(this.pid);
2374
+ return session === void 0 ? this.fds.stdioTerminal() : session ?? void 0;
2375
+ }
2376
+ jobSyscall(req) {
2377
+ const jobs = this.options.jobs;
2378
+ const self = (pid) => {
2379
+ if (pid !== 0 && pid !== this.pid) throw new KernelError("ESRCH");
2380
+ return this.pid;
2381
+ };
2382
+ switch (req.op) {
2383
+ case "proc-setpgid":
2384
+ if (jobs) jobs.setpgid(this.pid, req.pid, req.pgid);
2385
+ else if (self(req.pid) !== (req.pgid || this.pid)) throw new KernelError("EPERM");
2386
+ return { ok: true, kind: "void" };
2387
+ case "proc-getpgid":
2388
+ return { ok: true, kind: "json", json: jobs?.getpgid(this.pid, req.pid) ?? self(req.pid) };
2389
+ case "proc-getsid":
2390
+ return { ok: true, kind: "json", json: jobs?.getsid(this.pid, req.pid) ?? self(req.pid) };
2391
+ case "proc-setsid":
2392
+ if (!jobs) throw new KernelError("EPERM");
2393
+ return { ok: true, kind: "json", json: jobs.setsid(this.pid) };
2394
+ }
2395
+ }
2396
+ tty(fd) {
2397
+ const file = this.fds.get(fd).file;
2398
+ const tty = file.tty ?? file.pty?.slave;
2399
+ if (!tty) throw new KernelError("ENOTTY");
2400
+ return tty;
2401
+ }
2402
+ async procSyscall(req) {
2403
+ switch (req.op) {
2404
+ case "proc-fork":
2405
+ return { ok: true, kind: "json", json: await this.children.fork(req.state) };
2406
+ case "proc-spawn": {
2407
+ const { file, argv, env, cwd, stdio, inherit } = req;
2408
+ const pid = await this.children.spawn({ file, argv, env, cwd }, stdio, inherit);
2409
+ return { ok: true, kind: "json", json: pid };
2410
+ }
2411
+ case "proc-wait": {
2412
+ const signal = req.nohang ? this.interrupt.signal : this.blockingSignal();
2413
+ const flags = {
2414
+ untraced: req.untraced,
2415
+ continued: req.continued,
2416
+ inGroup: this.waitGroup(req.pid)
2417
+ };
2418
+ const waited = await this.children.wait(req.pid, req.nohang, signal, flags);
2419
+ return { ok: true, kind: "json", json: waited };
2420
+ }
2421
+ case "proc-exec": {
2422
+ this.execChild = req.pid;
2423
+ this.options.jobs?.exec(this.pid, req.pid);
2424
+ this.children.watch(
2425
+ req.pid,
2426
+ (state, sig) => state === "stopped" ? this.stop(sig) : this.cont()
2427
+ );
2428
+ await this.fds.closeAll();
2429
+ try {
2430
+ const waited = await this.children.wait(req.pid, false);
2431
+ const termsig = waited[1] & 127;
2432
+ if (termsig) this.execTermsig = termsig;
2433
+ return { ok: true, kind: "json", json: waited };
2434
+ } finally {
2435
+ this.execChild = void 0;
2436
+ }
2437
+ }
2438
+ case "proc-kill":
2439
+ if (req.sig !== 0 && !isSignal(req.sig)) throw new KernelError("EINVAL");
2440
+ if (!await this.options.kill?.(req.pid === 0 ? -this.pgid() : req.pid, req.sig)) {
2441
+ throw new KernelError("ESRCH");
2442
+ }
2443
+ return { ok: true, kind: "void" };
2444
+ case "proc-alarm":
2445
+ this.setAlarm(req.sig, req.firstMs ?? req.ms, req.repeat ? req.ms : 0, req.timer);
2446
+ return { ok: true, kind: "void" };
2447
+ case "sig-mask":
2448
+ this.caught = req.caught;
2449
+ this.ignored = req.ignored;
2450
+ return { ok: true, kind: "void" };
2451
+ case "sig-pause":
2452
+ return this.pause();
2453
+ case "proc-captured":
2454
+ return { ok: true, kind: "bytes", bytes: this.children.captured(req.pid, req.slot) };
2455
+ }
2456
+ }
2457
+ pause() {
2458
+ const signal = this.blockingSignal();
2459
+ return new Promise((_, reject) => {
2460
+ signal.addEventListener("abort", () => reject(new KernelError("EINTR")), { once: true });
2461
+ });
2462
+ }
2463
+ alarm;
2464
+ alarmEvery;
2465
+ setAlarm(sig, first, every, timer) {
2466
+ if (!isSignal(sig)) throw new KernelError("EINVAL");
2467
+ this.clearAlarm();
2468
+ if (first <= 0) return;
2469
+ const fire = timer === void 0 ? () => this.options.raise?.(sig) : () => this.timerExpired(timer);
2470
+ this.alarm = setTimeout(() => {
2471
+ this.alarm = void 0;
2472
+ fire();
2473
+ if (every > 0) this.alarmEvery = setInterval(fire, every);
2474
+ }, first);
2475
+ }
2476
+ timerExpired(which) {
2477
+ this.options.onTimer?.(which);
2478
+ const blocked = this.interrupt;
2479
+ this.interrupt = new AbortController();
2480
+ blocked.abort();
2481
+ }
2482
+ clearAlarm() {
2483
+ clearTimeout(this.alarm);
2484
+ clearInterval(this.alarmEvery);
2485
+ this.alarm = void 0;
2486
+ this.alarmEvery = void 0;
2487
+ }
2488
+ async exit() {
2489
+ if (this.exited) return;
2490
+ this.exited = true;
2491
+ this.clearAlarm();
2492
+ for (const pid of this.children.pids()) this.options.onReap?.(pid);
2493
+ await this.fds.closeAll();
2494
+ }
2495
+ };
2496
+
2497
+ // src/kernel/protocol.ts
2498
+ var WASM_PROCESS_INIT = "wasm-process-init";
2499
+ var WASM_PROCESS_EXIT = "wasm-process-exit";
2500
+ var WASM_PROCESS_ERROR = "wasm-process-error";
2501
+
2502
+ // src/kernel/host.ts
2503
+ var CRASHED = 70;
2504
+ var descIds = /* @__PURE__ */ new WeakMap();
2505
+ var nextDescId = 1;
2506
+ function descId(file) {
2507
+ const id = descIds.get(file) ?? nextDescId++;
2508
+ descIds.set(file, id);
2509
+ return id;
2510
+ }
2511
+ function inheritedFds(fds) {
2512
+ return fds.numbers().filter((fd) => fd > 2 && !fds.get(fd).file.held).map((fd) => {
2513
+ const open = fds.get(fd);
2514
+ const flags = fds.statusFlags(fd);
2515
+ const kind = kernelFdKind(open.file);
2516
+ const meta = open.file.heldMeta;
2517
+ return {
2518
+ fd,
2519
+ kind,
2520
+ ...kind === "device" && meta && "device" in meta ? { device: meta } : {},
2521
+ ...flags !== void 0 ? { flags } : {},
2522
+ ...fds.closesOnExec(fd) ? { cloexec: true } : {},
2523
+ ...kind === "stream" ? { desc: descId(open) } : {}
2524
+ };
2525
+ });
2526
+ }
2527
+ function spawnWasmProcess(opts) {
2528
+ const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
2529
+ const header = new Int32Array(sab, 0, SAB_HEADER_I32);
2530
+ const process = new WasmProcess(opts.pid, opts.fds, {
2531
+ spawner: opts.spawner,
2532
+ forker: opts.forker,
2533
+ fs: opts.fs,
2534
+ kill: opts.kill,
2535
+ jobs: opts.jobs,
2536
+ ptys: opts.ptys,
2537
+ onReap: opts.onReap,
2538
+ onPending: (sig) => void Atomics.or(header, SAB_I_SIGNALS, sigbit(sig)),
2539
+ onTimer: (which) => void Atomics.or(header, SAB_I_TIMERS, 1 << which),
2540
+ hasPending: () => Atomics.load(header, SAB_I_SIGNALS) !== 0 || Atomics.load(header, SAB_I_TIMERS) !== 0,
2541
+ raise: (sig) => signal(sig)
2542
+ });
2543
+ const token = mintSyncFsToken({ fs: opts.fs, cwd: opts.cwd });
2544
+ const worker = opts.createWorker();
2545
+ const dispatch = async (req) => {
2546
+ if (isWasmSyscall(req)) return process.syscall(req);
2547
+ return dispatchSyncFs(req);
2548
+ };
2549
+ const responder = attachSyncSabResponder(worker, sab, token, { dispatch });
2550
+ let settle;
2551
+ const exited = new Promise((resolve) => settle = resolve);
2552
+ let done3 = false;
2553
+ let endedBy;
2554
+ const finish = (code, sig) => {
2555
+ if (done3) return;
2556
+ done3 = true;
2557
+ endedBy = sig ?? process.execTermsig;
2558
+ worker.removeEventListener("message", onMessage);
2559
+ worker.removeEventListener("error", onError);
2560
+ responder.dispose();
2561
+ revokeSyncFsToken(token);
2562
+ worker.terminate();
2563
+ void process.exit().then(
2564
+ () => settle(code),
2565
+ () => settle(code)
2566
+ );
2567
+ };
2568
+ const onMessage = (event) => {
2569
+ const data = event.data;
2570
+ if (data?.type === WASM_PROCESS_EXIT) {
2571
+ finish(typeof data.code === "number" ? data.code : CRASHED);
2572
+ } else if (data?.type === WASM_PROCESS_ERROR) {
2573
+ opts.onError?.(String(data.message));
2574
+ finish(CRASHED);
2575
+ }
2576
+ };
2577
+ const onError = (event) => {
2578
+ event.preventDefault();
2579
+ opts.onError?.(String(event.message ?? "worker error"));
2580
+ finish(CRASHED);
2581
+ };
2582
+ worker.addEventListener("message", onMessage);
2583
+ worker.addEventListener("error", onError);
2584
+ const init = {
2585
+ type: WASM_PROCESS_INIT,
2586
+ pid: opts.pid,
2587
+ program: opts.program,
2588
+ argv0: opts.argv0,
2589
+ args: opts.args,
2590
+ env: opts.env,
2591
+ cwd: opts.cwd,
2592
+ sab,
2593
+ ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
2594
+ ...opts.fork ? { fork: opts.fork } : { fds: inheritedFds(opts.fds) }
2595
+ };
2596
+ worker.postMessage(init, opts.fork ? [opts.fork.memory.buffer] : []);
2597
+ const signal = (sig) => {
2598
+ if (process.signal(sig) === "terminate") finish(sig === SIG.KILL ? 137 : 128 + sig, sig);
2599
+ };
2600
+ const kill = (code = 137) => finish(code, code > 128 && code < 160 ? code - 128 : void 0);
2601
+ return {
2602
+ pid: opts.pid,
2603
+ exited,
2604
+ kill,
2605
+ signal,
2606
+ termsig: () => endedBy,
2607
+ onState: (listener) => process.onState(listener)
2608
+ };
2609
+ }
2610
+
2611
+ // src/kernel/jobs.ts
2612
+ var JobTable = class {
2613
+ members = /* @__PURE__ */ new Map();
2614
+ foreground = /* @__PURE__ */ new Map();
2615
+ terminals = /* @__PURE__ */ new Map();
2616
+ add(pid, parentPid, signal, terminal) {
2617
+ const parent = parentPid === void 0 ? void 0 : this.members.get(parentPid);
2618
+ if (!parent && terminal) this.terminals.set(pid, terminal);
2619
+ const member = {
2620
+ pid,
2621
+ ppid: parent?.pid,
2622
+ pgid: parent?.pgid ?? pid,
2623
+ sid: parent?.sid ?? pid,
2624
+ signal
2625
+ };
2626
+ this.members.set(pid, member);
2627
+ return member;
2628
+ }
2629
+ remove(pid) {
2630
+ this.members.delete(pid);
2631
+ }
2632
+ exec(pid, child) {
2633
+ const member = this.members.get(child);
2634
+ if (member) member.execParent = pid;
2635
+ const execer = this.members.get(pid);
2636
+ if (execer) execer.execed = true;
2637
+ }
2638
+ pgidOf(pid) {
2639
+ return this.members.get(pid)?.pgid;
2640
+ }
2641
+ member(pid) {
2642
+ const member = this.members.get(pid);
2643
+ if (!member) throw new KernelError("ESRCH");
2644
+ return member;
2645
+ }
2646
+ setpgid(caller, pid, pgid) {
2647
+ const target = this.member(pid || caller);
2648
+ const group = pgid || target.pid;
2649
+ if (group < 0) throw new KernelError("EINVAL");
2650
+ const self = this.member(caller);
2651
+ if (target.pid !== caller) {
2652
+ if (target.ppid !== caller) throw new KernelError("ESRCH");
2653
+ if (target.execed) throw new KernelError("EACCES");
2654
+ }
2655
+ if (target.sid !== self.sid) throw new KernelError("EPERM");
2656
+ if (target.pid === target.sid) throw new KernelError("EPERM");
2657
+ const exists = [...this.members.values()].some((m) => m.pgid === group && m.sid === target.sid);
2658
+ if (group !== target.pid && !exists) throw new KernelError("EPERM");
2659
+ target.pgid = group;
2660
+ }
2661
+ getpgid(caller, pid) {
2662
+ return this.member(pid || caller).pgid;
2663
+ }
2664
+ getsid(caller, pid) {
2665
+ return this.member(pid || caller).sid;
2666
+ }
2667
+ setsid(pid) {
2668
+ const member = this.member(pid);
2669
+ if (member.pgid === pid) throw new KernelError("EPERM");
2670
+ member.sid = pid;
2671
+ member.pgid = pid;
2672
+ this.terminals.delete(pid);
2673
+ return pid;
2674
+ }
2675
+ terminalNamed(name) {
2676
+ const known = /* @__PURE__ */ new Set([...this.terminals.values(), ...this.foreground.keys()]);
2677
+ return [...known].find((tty) => tty.name === name);
2678
+ }
2679
+ controllingTerminal(pid) {
2680
+ const member = this.members.get(pid);
2681
+ return member && (this.terminals.get(member.sid) ?? null);
2682
+ }
2683
+ acquireTerminal(pid, tty) {
2684
+ const member = this.members.get(pid);
2685
+ if (!member || member.sid !== pid || this.terminals.has(pid)) return false;
2686
+ if ([...this.terminals.values()].includes(tty)) return false;
2687
+ this.terminals.set(pid, tty);
2688
+ this.foreground.set(tty, member.pgid);
2689
+ return true;
2690
+ }
2691
+ killGroup(pgid, sig) {
2692
+ const targets = [...this.members.values()].filter((m) => m.pgid === pgid);
2693
+ const pids = new Set(targets.map((m) => m.pid));
2694
+ if (sig !== 0) {
2695
+ for (const m of targets)
2696
+ if (m.execParent === void 0 || !pids.has(m.execParent)) m.signal(sig);
2697
+ }
2698
+ return targets.length > 0;
2699
+ }
2700
+ tcgetpgrp(tty, fallback) {
2701
+ return this.foreground.get(tty) ?? fallback;
2702
+ }
2703
+ tcsetpgrp(caller, tty, pgid) {
2704
+ const self = this.member(caller);
2705
+ const inSession = [...this.members.values()].some((m) => m.pgid === pgid && m.sid === self.sid);
2706
+ if (!inSession) throw new KernelError("EPERM");
2707
+ this.foreground.set(tty, pgid);
2708
+ }
2709
+ signalOwnedForeground(tty, sig) {
2710
+ const pgid = this.foreground.get(tty);
2711
+ if (pgid !== void 0) this.killGroup(pgid, sig);
2712
+ }
2713
+ signalForeground(tty, fallback, sig) {
2714
+ this.killGroup(this.tcgetpgrp(tty, fallback), sig);
2715
+ }
2716
+ };
2717
+
2718
+ // src/launcher.ts
2719
+ var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
2720
+ var SHEBANG_MAX = 256;
2721
+ var NOT_FOUND = 127;
2722
+ var encoder3 = new TextEncoder();
2723
+ function baseName(path) {
2724
+ return path.slice(path.lastIndexOf("/") + 1);
2725
+ }
2726
+ function modulePath(glue) {
2727
+ return glue.endsWith(".js") ? `${glue.slice(0, -3)}.wasm` : `${glue}.wasm`;
2728
+ }
2729
+ function targetOf(command) {
2730
+ return {
2731
+ glue: command.glue,
2732
+ wasm: command.wasm,
2733
+ argv0: command.argv0,
2734
+ ...command.args ? { prefix: command.args } : {},
2735
+ ...command.env ? { env: command.env } : {}
2736
+ };
2737
+ }
2738
+ function childHandle(handle) {
2739
+ return {
2740
+ pid: handle.pid,
2741
+ exited: handle.exited,
2742
+ termsig: handle.termsig,
2743
+ onState: (listener) => handle.onState(listener)
2744
+ };
2745
+ }
2746
+ function concat2(chunks) {
2747
+ const out = new Uint8Array(chunks.reduce((n, chunk) => n + chunk.length, 0));
2748
+ let offset = 0;
2749
+ for (const chunk of chunks) {
2750
+ out.set(chunk, offset);
2751
+ offset += chunk.length;
2752
+ }
2753
+ return out;
2754
+ }
2755
+ var Launcher = class {
2756
+ fs;
2757
+ base;
2758
+ createWorker;
2759
+ modulesDir;
2760
+ env;
2761
+ catalog;
2762
+ compiled = /* @__PURE__ */ new Map();
2763
+ processes = /* @__PURE__ */ new Map();
2764
+ zombies = /* @__PURE__ */ new Set();
2765
+ orphans = /* @__PURE__ */ new Set();
2766
+ jobs = new JobTable();
2767
+ ptys = new PtyTable((tty, sig) => this.jobs.signalOwnedForeground(tty, sig));
2768
+ nextPid = 1e3;
2769
+ constructor(options) {
2770
+ this.base = options.fs;
2771
+ this.createWorker = options.createWorker;
2772
+ this.modulesDir = options.modules ?? "/node_modules";
2773
+ this.env = options.env ?? {};
2774
+ this.fs = withCommandDirs(options.fs, async () => new Set((await this.commands()).keys()));
2775
+ }
2776
+ commands() {
2777
+ this.catalog ??= scanCommands(this.base, this.modulesDir);
2778
+ return this.catalog;
2779
+ }
2780
+ async resolve(file, argv0, cwd) {
2781
+ const name = COMMAND2.exec(file)?.[1] ?? (file.includes("/") ? void 0 : file);
2782
+ if (name !== void 0) {
2783
+ const command = (await this.commands()).get(name);
2784
+ return command && targetOf(command);
2785
+ }
2786
+ const glue = this.fs.resolvePath(cwd, file);
2787
+ const wasm = modulePath(glue);
2788
+ if (!await this.base.exists(glue) || !await this.base.exists(wasm)) return void 0;
2789
+ return { glue, wasm, argv0: baseName(argv0 || file) };
2790
+ }
2791
+ async interpreted(file, argv, cwd) {
2792
+ let head;
2793
+ try {
2794
+ head = (await this.base.readFileBuffer(this.fs.resolvePath(cwd, file))).subarray(
2795
+ 0,
2796
+ SHEBANG_MAX
2797
+ );
2798
+ } catch {
2799
+ return void 0;
2800
+ }
2801
+ if (head[0] !== 35 || head[1] !== 33) return void 0;
2802
+ const words = new TextDecoder().decode(head).slice(2).split("\n")[0].trim().split(/[ \t]+/);
2803
+ if (baseName(words[0]) === "env") words.shift();
2804
+ const [interp, ...rest] = words;
2805
+ const target = interp ? await this.resolve(interp, interp, cwd) : void 0;
2806
+ if (!target) return void 0;
2807
+ const arg = rest.join(" ");
2808
+ return {
2809
+ target,
2810
+ args: [...target.prefix ?? [], ...arg ? [arg] : [], file, ...argv.slice(1)]
2811
+ };
2812
+ }
2813
+ async plan(file, argv, cwd) {
2814
+ const direct = await this.resolve(file, argv[0] ?? file, cwd);
2815
+ if (direct) return { target: direct, args: [...direct.prefix ?? [], ...argv.slice(1)] };
2816
+ return this.interpreted(file, argv, cwd);
2817
+ }
2818
+ module(path) {
2819
+ return this.base.stat(path).then((st) => {
2820
+ const key = `${path}:${st.size}:${st.mtime.getTime()}`;
2821
+ let module = this.compiled.get(key);
2822
+ if (!module) {
2823
+ module = this.base.readFileBuffer(path).then((bytes) => WebAssembly.compile(bytes));
2824
+ this.compiled.set(key, module);
2825
+ module.catch(() => this.compiled.delete(key));
2826
+ }
2827
+ return module;
2828
+ });
2829
+ }
2830
+ async launch(planned, req) {
2831
+ const { target, args } = planned;
2832
+ let program;
2833
+ try {
2834
+ const [glue, module] = await Promise.all([
2835
+ this.base.readFile(target.glue),
2836
+ this.module(target.wasm)
2837
+ ]);
2838
+ program = { glue, module };
2839
+ } catch (err) {
2840
+ await req.fds.closeAll();
2841
+ throw err;
2842
+ }
2843
+ return this.start({
2844
+ ...req,
2845
+ program,
2846
+ argv0: target.argv0,
2847
+ args,
2848
+ env: { ...target.env, ...req.env }
2849
+ });
2850
+ }
2851
+ start(req) {
2852
+ const pid = this.nextPid++;
2853
+ const terminal = req.fds.stdioTerminal();
2854
+ const handle = spawnWasmProcess({
2855
+ pid,
2856
+ program: req.program,
2857
+ argv0: req.argv0,
2858
+ args: req.args,
2859
+ env: req.env,
2860
+ cwd: req.cwd,
2861
+ fds: req.fds,
2862
+ fs: this.fs,
2863
+ createWorker: this.createWorker,
2864
+ onError: req.report,
2865
+ spawner: this.spawner(pid, req.report),
2866
+ forker: this.forker(pid, req),
2867
+ kill: (target, sig) => this.kill(target, sig),
2868
+ jobs: this.jobs,
2869
+ ptys: this.ptys,
2870
+ onReap: (child) => this.reaped(child),
2871
+ ...req.fork ? { fork: req.fork } : {},
2872
+ ...req.ppid !== void 0 ? { ppid: req.ppid } : {}
2873
+ });
2874
+ this.processes.set(pid, handle);
2875
+ this.jobs.add(pid, req.ppid, (sig) => handle.signal(sig), terminal);
2876
+ void handle.exited.then(() => {
2877
+ this.processes.delete(pid);
2878
+ if (req.ppid === void 0 || this.orphans.delete(pid)) this.jobs.remove(pid);
2879
+ else this.zombies.add(pid);
2880
+ });
2881
+ return handle;
2882
+ }
2883
+ reaped(pid) {
2884
+ if (this.zombies.delete(pid)) this.jobs.remove(pid);
2885
+ else this.orphans.add(pid);
2886
+ }
2887
+ spawner(ppid, report) {
2888
+ return async (req, fds) => {
2889
+ const planned = await this.plan(req.file, req.argv, req.cwd);
2890
+ if (!planned) {
2891
+ await fds.closeAll();
2892
+ throw new SpawnError("ENOENT");
2893
+ }
2894
+ const handle = await this.launch(planned, { env: req.env, cwd: req.cwd, fds, report, ppid });
2895
+ return childHandle(handle);
2896
+ };
2897
+ }
2898
+ forker(ppid, parent) {
2899
+ return async (state, fds) => childHandle(this.start({ ...parent, cwd: state.cwd ?? parent.cwd, fds, ppid, fork: state }));
2900
+ }
2901
+ kill(pid, sig) {
2902
+ if (pid < 0) return this.jobs.killGroup(-pid, sig);
2903
+ const handle = this.processes.get(pid);
2904
+ if (!handle) return false;
2905
+ if (sig !== 0) handle.signal(sig);
2906
+ return true;
2907
+ }
2908
+ async run(argv, options = {}) {
2909
+ this.catalog = void 0;
2910
+ const cwd = this.fs.resolvePath("/", options.cwd ?? "/");
2911
+ const env = { PATH: "/usr/bin:/bin", HOME: "/", ...this.env, PWD: cwd, ...options.env };
2912
+ const out = [];
2913
+ const err = [];
2914
+ const collect = (chunks, tee) => (bytes) => {
2915
+ chunks.push(bytes);
2916
+ tee?.(bytes);
2917
+ };
2918
+ const stdout = collect(out, options.onStdout);
2919
+ const stderr = collect(err, options.onStderr);
2920
+ const report = (message) => stderr(encoder3.encode(`${message}
2921
+ `));
2922
+ const [file = ""] = argv;
2923
+ await this.base.mkdir(cwd, { recursive: true });
2924
+ const planned = await this.plan(file, argv, cwd);
2925
+ if (!planned) {
2926
+ report(`${file}: command not found`);
2927
+ return { status: NOT_FOUND, stdout: concat2(out), stderr: concat2(err) };
2928
+ }
2929
+ const fds = new FdTable();
2930
+ fds.installAt(0, options.stdin ? bytesSource(options.stdin) : nullFile());
2931
+ fds.installAt(1, sinkFile(stdout));
2932
+ fds.installAt(2, sinkFile(stderr));
2933
+ const handle = await this.launch(planned, { env, cwd, fds, report });
2934
+ const status = await handle.exited;
2935
+ return { status, stdout: concat2(out), stderr: concat2(err) };
2936
+ }
2937
+ };
2938
+
2939
+ // src/serve.ts
2940
+ function serveKernel(port, deps) {
2941
+ let launcher;
2942
+ const reply = (id, body) => port.postMessage({ id, ...body });
2943
+ async function handle(req) {
2944
+ if (req.op === "init") {
2945
+ launcher = (req.root ? Promise.resolve(req.root) : deps.storage()).then(
2946
+ (root) => new Launcher({
2947
+ fs: new OpfsFs(root),
2948
+ createWorker: deps.createWorker,
2949
+ ...req.modules ? { modules: req.modules } : {},
2950
+ ...req.env ? { env: req.env } : {}
2951
+ })
2952
+ );
2953
+ await launcher;
2954
+ return true;
2955
+ }
2956
+ if (!launcher) throw new Error("the kernel is not initialized");
2957
+ return (await launcher).run(req.argv, {
2958
+ ...req.options,
2959
+ onStdout: (bytes) => reply(req.id, { fd: 1, bytes }),
2960
+ onStderr: (bytes) => reply(req.id, { fd: 2, bytes })
2961
+ });
2962
+ }
2963
+ port.addEventListener("message", (event) => {
2964
+ const req = event.data;
2965
+ handle(req).then(
2966
+ (result) => reply(req.id, { result }),
2967
+ (err) => reply(req.id, { error: err instanceof Error ? err.message : String(err) })
2968
+ );
2969
+ });
2970
+ }
2971
+
2972
+ // src/kernel-worker.ts
2973
+ var processWorker = new URL("./process-worker.js", import.meta.url);
2974
+ serveKernel(globalThis, {
2975
+ storage: () => navigator.storage.getDirectory(),
2976
+ createWorker: () => new Worker(processWorker, { type: "module" })
2977
+ });
2978
+ //# sourceMappingURL=kernel-worker.js.map