@almadar/shell-node 0.0.0-stage → 0.1.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,2219 @@
1
+ import { installNodeHostPorts } from './chunk-23YAOI6P.js';
2
+ import { loadNativeKernelModule, resolveNativeAddon, loadKernelModule, loadHttpKernel } from './chunk-UBZT5LDW.js';
3
+ import { workspaceOwnerSegment } from './chunk-BWMLDDCL.js';
4
+ import { mkdirSync, realpathSync, mkdtempSync, writeFileSync, rmSync, constants as constants$1 } from 'fs';
5
+ import { constants, homedir, tmpdir, loadavg, uptime, machine, version, release, hostname, type, userInfo } from 'os';
6
+ import { dirname, join, resolve } from 'path';
7
+ import { spawn } from 'child_process';
8
+ import { createRequire } from 'module';
9
+ import { createHttpPorts, NativeMemoryPersistenceAdapter, kernelOsGrants, InFlightCalls, KernelEngine, dispatchInbound, anchoredGrants, expandAnchor } from '@almadar/shell-runtime';
10
+ import { resolveOrbBinary } from '@almadar/core/behavior-package';
11
+ import { tickEventKey } from '@almadar/core';
12
+ import { lstat, chmod, stat, readlink, symlink, link, unlink, rename, copyFile, open, mkdir, readdir, rm, rmdir, access, chown } from 'fs/promises';
13
+ import { randomBytes } from 'crypto';
14
+ import { lookup } from 'dns/promises';
15
+ import { createSocket } from 'dgram';
16
+ import { isIP, createServer, createConnection } from 'net';
17
+ import { isatty } from 'tty';
18
+ import { createLogger } from '@almadar/logger';
19
+
20
+ var OsFailure = class extends Error {
21
+ constructor(error) {
22
+ super(error.message);
23
+ this.error = error;
24
+ }
25
+ error;
26
+ };
27
+ function osFailure(kind, op, what) {
28
+ return new OsFailure({ kind, message: `${op}: ${what}` });
29
+ }
30
+ function denied(op, what) {
31
+ return osFailure("denied", op, what);
32
+ }
33
+ function invalid(op, what) {
34
+ return osFailure("invalid", op, what);
35
+ }
36
+ function invalidArgument(op, i, expected) {
37
+ return invalid(op, `argument ${i + 1} must be ${expected}`);
38
+ }
39
+ function argument(args, i, op, expected) {
40
+ if (i >= args.length) return fail(invalid(op, `missing argument ${i + 1} (${expected})`));
41
+ return args[i];
42
+ }
43
+ function fail(failure2) {
44
+ throw failure2;
45
+ }
46
+ function argOpt(args, i) {
47
+ return i < args.length && args[i] !== null ? args[i] : void 0;
48
+ }
49
+ function argStr(args, i, op) {
50
+ const value = argument(args, i, op, "a string");
51
+ return typeof value === "string" ? value : fail(invalidArgument(op, i, "a string"));
52
+ }
53
+ function argI64(args, i, op) {
54
+ const value = argument(args, i, op, "an integer");
55
+ if (typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && Math.abs(value) < 9007199254740992) return value;
56
+ return fail(invalidArgument(op, i, "an integer"));
57
+ }
58
+ function argBool(args, i, op) {
59
+ const value = argument(args, i, op, "a boolean");
60
+ return typeof value === "boolean" ? value : fail(invalidArgument(op, i, "a boolean"));
61
+ }
62
+ var BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
63
+ function decodeStandardBase64(text) {
64
+ const symbols = text.endsWith("==") ? text.length - 2 : text.endsWith("=") ? text.length - 1 : text.length;
65
+ for (let i = 0; i < text.length; i += 1) {
66
+ const code = text.charCodeAt(i);
67
+ if (i >= symbols) continue;
68
+ if (BASE64_ALPHABET.indexOf(text[i]) === -1) return { failure: `Invalid byte ${code}, offset ${i}.` };
69
+ }
70
+ const padding = text.length - symbols;
71
+ const remainder = symbols % 4;
72
+ if (remainder === 1) return { failure: `Invalid input length: ${symbols}` };
73
+ if (text.length % 4 !== 0 || remainder === 0 && padding !== 0 || remainder === 2 && padding !== 2 || remainder === 3 && padding !== 1) {
74
+ return { failure: "Invalid padding" };
75
+ }
76
+ if (remainder === 2 || remainder === 3) {
77
+ const last = BASE64_ALPHABET.indexOf(text[symbols - 1]);
78
+ const spare = remainder === 2 ? 15 : 3;
79
+ if ((last & spare) !== 0) return { failure: `Invalid last symbol ${text.charCodeAt(symbols - 1)}, offset ${symbols - 1}.` };
80
+ }
81
+ return { bytes: Buffer.from(text, "base64") };
82
+ }
83
+ function argBytes(args, i, op) {
84
+ const decoded = decodeStandardBase64(argStr(args, i, op));
85
+ if ("failure" in decoded) throw invalid(op, `argument ${i + 1} is not standard base64: ${decoded.failure}`);
86
+ return decoded.bytes;
87
+ }
88
+ function bytesValue(bytes) {
89
+ return Buffer.from(bytes).toString("base64");
90
+ }
91
+ var ERRNO = constants.errno;
92
+ var POSIX_TEXT = {
93
+ EPERM: "Operation not permitted",
94
+ ENOENT: "No such file or directory",
95
+ ESRCH: "No such process",
96
+ EINTR: "Interrupted system call",
97
+ EIO: "Input/output error",
98
+ E2BIG: "Argument list too long",
99
+ EBADF: "Bad file descriptor",
100
+ EAGAIN: "Resource temporarily unavailable",
101
+ ENOMEM: "Cannot allocate memory",
102
+ EACCES: "Permission denied",
103
+ EBUSY: "Device or resource busy",
104
+ EEXIST: "File exists",
105
+ EXDEV: "Invalid cross-device link",
106
+ ENOTDIR: "Not a directory",
107
+ EISDIR: "Is a directory",
108
+ EINVAL: "Invalid argument",
109
+ EMFILE: "Too many open files",
110
+ EFBIG: "File too large",
111
+ ENOSPC: "No space left on device",
112
+ ESPIPE: "Illegal seek",
113
+ EROFS: "Read-only file system",
114
+ EMLINK: "Too many links",
115
+ EPIPE: "Broken pipe",
116
+ ENAMETOOLONG: "File name too long",
117
+ ENOTEMPTY: "Directory not empty",
118
+ ELOOP: "Too many levels of symbolic links",
119
+ EADDRINUSE: "Address already in use",
120
+ EADDRNOTAVAIL: "Cannot assign requested address",
121
+ ENETDOWN: "Network is down",
122
+ ENETUNREACH: "Network is unreachable",
123
+ ECONNABORTED: "Software caused connection abort",
124
+ ECONNRESET: "Connection reset by peer",
125
+ ENOTCONN: "Transport endpoint is not connected",
126
+ ETIMEDOUT: "Connection timed out",
127
+ ECONNREFUSED: "Connection refused",
128
+ EHOSTUNREACH: "No route to host",
129
+ EINPROGRESS: "Operation now in progress"
130
+ };
131
+ var DARWIN_TEXT = {
132
+ EXDEV: "Cross-device link",
133
+ EBUSY: "Resource busy",
134
+ ENOTCONN: "Socket is not connected",
135
+ ETIMEDOUT: "Operation timed out"
136
+ };
137
+ var TextIoError = class extends Error {
138
+ constructor(code, text) {
139
+ super(text);
140
+ this.code = code;
141
+ }
142
+ code;
143
+ };
144
+ function errnoError(code) {
145
+ const error = new Error(code);
146
+ error.code = code;
147
+ return error;
148
+ }
149
+ function errnoText(error) {
150
+ if (error instanceof TextIoError) return error.message;
151
+ const code = errnoCode(error);
152
+ const number = code === void 0 ? void 0 : ERRNO[code];
153
+ const text = code === void 0 ? void 0 : (process.platform === "darwin" ? DARWIN_TEXT[code] : void 0) ?? POSIX_TEXT[code];
154
+ if (number === void 0 || text === void 0) return error.message;
155
+ return `${text} (os error ${number})`;
156
+ }
157
+ function errnoCode(error) {
158
+ const raised = error;
159
+ return raised.code;
160
+ }
161
+ function fsKind(code) {
162
+ switch (code) {
163
+ case "ENOENT":
164
+ return "not-found";
165
+ case "EEXIST":
166
+ return "exists";
167
+ case "EACCES":
168
+ case "EPERM":
169
+ return "denied";
170
+ case "ETIMEDOUT":
171
+ return "timeout";
172
+ case "EINVAL":
173
+ case "EISDIR":
174
+ case "ENOTDIR":
175
+ case "ENAMETOOLONG":
176
+ case "ELOOP":
177
+ case "EBADF":
178
+ return "invalid";
179
+ default:
180
+ return "io";
181
+ }
182
+ }
183
+ function processKind(code) {
184
+ switch (code) {
185
+ case "ENOENT":
186
+ return "not-found";
187
+ case "EACCES":
188
+ case "EPERM":
189
+ return "denied";
190
+ case "EINVAL":
191
+ return "invalid";
192
+ default:
193
+ return "io";
194
+ }
195
+ }
196
+ function netKind(code) {
197
+ switch (code) {
198
+ case "ENOENT":
199
+ return "not-found";
200
+ case "EACCES":
201
+ case "EPERM":
202
+ return "denied";
203
+ case "ETIMEDOUT":
204
+ case "EAGAIN":
205
+ case "EWOULDBLOCK":
206
+ case "EINPROGRESS":
207
+ return "timeout";
208
+ case "ECONNRESET":
209
+ case "ECONNABORTED":
210
+ case "EPIPE":
211
+ case "ENOTCONN":
212
+ return "closed";
213
+ case "EADDRINUSE":
214
+ case "EEXIST":
215
+ return "exists";
216
+ case "EINVAL":
217
+ return "invalid";
218
+ default:
219
+ return "io";
220
+ }
221
+ }
222
+ function processFailure(op, error) {
223
+ return osFailure(processKind(errnoCode(error)), op, errnoText(error));
224
+ }
225
+ function netFailure(op, error) {
226
+ return osFailure(netKind(errnoCode(error)), op, errnoText(error));
227
+ }
228
+ var ByteSource = class {
229
+ constructor(stream, highWater) {
230
+ this.stream = stream;
231
+ this.highWater = highWater;
232
+ stream.on("data", (chunk) => {
233
+ this.chunks.push(chunk);
234
+ this.held += chunk.length;
235
+ if (this.held > this.highWater) stream.pause();
236
+ this.notify();
237
+ });
238
+ stream.on("end", () => {
239
+ this.ended = true;
240
+ this.notify();
241
+ });
242
+ stream.on("error", (error) => {
243
+ this.failure = error;
244
+ this.notify();
245
+ });
246
+ stream.on("close", () => {
247
+ this.ended = true;
248
+ this.notify();
249
+ });
250
+ }
251
+ stream;
252
+ highWater;
253
+ chunks = [];
254
+ held = 0;
255
+ ended = false;
256
+ failure;
257
+ wake;
258
+ queue = Promise.resolve();
259
+ notify() {
260
+ const wake = this.wake;
261
+ this.wake = void 0;
262
+ wake?.();
263
+ }
264
+ /** One read at a time, as the native pipe lock does: a read that has not ended holds the next one back. */
265
+ take(want, timeoutMs) {
266
+ const run = this.queue.then(() => this.takeNow(want, timeoutMs));
267
+ this.queue = run.then(() => void 0, () => void 0);
268
+ return run;
269
+ }
270
+ /** At most `want` bytes of one read: what is held, else what arrives; empty at end of stream; `'timeout'` after `timeoutMs`. */
271
+ async takeNow(want, timeoutMs) {
272
+ if (want === 0) return Buffer.alloc(0);
273
+ const deadline = timeoutMs === void 0 ? void 0 : Date.now() + timeoutMs;
274
+ for (; ; ) {
275
+ if (this.held > 0) return this.slice(want);
276
+ if (this.failure !== void 0) throw this.failure;
277
+ if (this.ended) return Buffer.alloc(0);
278
+ const left = deadline === void 0 ? void 0 : deadline - Date.now();
279
+ if (left !== void 0 && left <= 0) return "timeout";
280
+ await new Promise((resolve2) => {
281
+ const timer = left === void 0 ? void 0 : setTimeout(resolve2, left);
282
+ this.wake = () => {
283
+ if (timer !== void 0) clearTimeout(timer);
284
+ resolve2();
285
+ };
286
+ });
287
+ }
288
+ }
289
+ slice(want) {
290
+ const parts = [];
291
+ let need = want;
292
+ while (need > 0 && this.chunks.length > 0) {
293
+ const head = this.chunks[0];
294
+ if (head.length <= need) {
295
+ parts.push(head);
296
+ need -= head.length;
297
+ this.chunks.shift();
298
+ } else {
299
+ parts.push(head.subarray(0, need));
300
+ this.chunks[0] = head.subarray(need);
301
+ need = 0;
302
+ }
303
+ }
304
+ const taken = Buffer.concat(parts);
305
+ this.held -= taken.length;
306
+ if (this.held <= this.highWater) this.stream.resume();
307
+ return taken;
308
+ }
309
+ destroy() {
310
+ this.stream.destroy();
311
+ }
312
+ };
313
+ function writeAll(stream, data, timeoutMs) {
314
+ return new Promise((resolve2, reject) => {
315
+ const timer = setTimeout(() => reject(errnoError("EAGAIN")), timeoutMs);
316
+ stream.write(data, (error) => {
317
+ if (timer !== void 0) clearTimeout(timer);
318
+ if (error) reject(error);
319
+ else resolve2();
320
+ });
321
+ });
322
+ }
323
+ var Completions = class {
324
+ live = /* @__PURE__ */ new Set();
325
+ /** `false` when `key` already has a completion pending. */
326
+ begin(key) {
327
+ if (this.live.has(key)) return false;
328
+ this.live.add(key);
329
+ return true;
330
+ }
331
+ /** Drop the pending completion of `key`; `false` when none is pending. */
332
+ cancel(key) {
333
+ return this.live.delete(key);
334
+ }
335
+ /** Take the right to deliver `key`'s completion: `false` when it was cancelled first. */
336
+ claim(key) {
337
+ return this.live.delete(key);
338
+ }
339
+ clear() {
340
+ this.live.clear();
341
+ }
342
+ };
343
+ var FIRST_HANDLE_ID = 3;
344
+ var OsState = class {
345
+ constructor(anchors = {}) {
346
+ this.anchors = anchors;
347
+ }
348
+ anchors;
349
+ next = FIRST_HANDLE_ID;
350
+ fds = /* @__PURE__ */ new Map();
351
+ socks = /* @__PURE__ */ new Map();
352
+ children = /* @__PURE__ */ new Map();
353
+ env = /* @__PURE__ */ new Map();
354
+ completions = new Completions();
355
+ cwd;
356
+ allocate() {
357
+ const id = this.next;
358
+ this.next += 1;
359
+ return id;
360
+ }
361
+ close() {
362
+ this.completions.clear();
363
+ for (const entry of this.children.values()) entry.child.kill("SIGKILL");
364
+ this.children.clear();
365
+ this.env.clear();
366
+ for (const entry of this.fds.values()) closeFd(entry);
367
+ this.fds.clear();
368
+ this.cwd = void 0;
369
+ for (const entry of this.socks.values()) closeSock(entry);
370
+ this.socks.clear();
371
+ }
372
+ };
373
+ function closeFd(entry) {
374
+ if (entry.kind === "file") void entry.handle.close();
375
+ else if (entry.kind === "child-stdin") entry.stream.end();
376
+ else entry.source.destroy();
377
+ }
378
+ function closeSock(entry) {
379
+ if (entry.kind === "stream") entry.socket.destroy();
380
+ else if (entry.kind === "listener") entry.server.close();
381
+ else entry.socket.close();
382
+ }
383
+ function byteOrder(a, b) {
384
+ return Buffer.compare(Buffer.from(a), Buffer.from(b));
385
+ }
386
+
387
+ // src/os-node-fs.ts
388
+ var MAX_BYTES = 64 * 1024 * 1024;
389
+ var MAX_LINKS = 40;
390
+ var CHUNK = 1024 * 1024;
391
+ function kindOf(entry) {
392
+ if (entry.isSymbolicLink()) return "symlink";
393
+ if (entry.isDirectory()) return "dir";
394
+ if (entry.isFile()) return "file";
395
+ return "other";
396
+ }
397
+ function millis(ns) {
398
+ let seconds = ns / 1000000000n;
399
+ let nanos = ns % 1000000000n;
400
+ if (nanos < 0n) {
401
+ nanos += 1000000000n;
402
+ seconds -= 1n;
403
+ }
404
+ return Number(seconds) * 1e3 + Number(nanos) / 1e6;
405
+ }
406
+ function statValue(entry) {
407
+ return {
408
+ kind: kindOf(entry),
409
+ size: Number(entry.size),
410
+ mode: Number(entry.mode & 0o7777n),
411
+ uid: Number(entry.uid),
412
+ gid: Number(entry.gid),
413
+ atime: millis(entry.atimeNs),
414
+ mtime: millis(entry.mtimeNs),
415
+ ctime: millis(entry.ctimeNs)
416
+ };
417
+ }
418
+ function lexical(path) {
419
+ const kept = [];
420
+ for (const part of path.split("/")) {
421
+ if (part === "" || part === ".") continue;
422
+ if (part === "..") {
423
+ if (kept.length === 0) return void 0;
424
+ kept.pop();
425
+ continue;
426
+ }
427
+ kept.push(part);
428
+ }
429
+ return `/${kept.join("/")}`;
430
+ }
431
+ function joinPath(directory, part) {
432
+ return directory === "/" ? `/${part}` : `${directory}/${part}`;
433
+ }
434
+ function parentOf(path) {
435
+ const cut = path.lastIndexOf("/");
436
+ return cut <= 0 ? "/" : path.slice(0, cut);
437
+ }
438
+ function ancestorsOf(path) {
439
+ const out = [];
440
+ let at = path;
441
+ for (; ; ) {
442
+ out.push(at);
443
+ if (at === "/") return out;
444
+ at = parentOf(at);
445
+ }
446
+ }
447
+ function components(path) {
448
+ return path.split("/").filter((part) => part !== "" && part !== ".");
449
+ }
450
+ async function resolveReal(path, followLast) {
451
+ const queue = components(path);
452
+ let current = "/";
453
+ let links = 0;
454
+ while (queue.length > 0) {
455
+ const part = queue.shift() ?? "";
456
+ if (part === "..") {
457
+ current = parentOf(current);
458
+ continue;
459
+ }
460
+ const next = joinPath(current, part);
461
+ const entry = await lstat(next).catch((raised) => raised);
462
+ if (entry instanceof Error) {
463
+ if (errnoCode(entry) === "ENOENT") {
464
+ current = next;
465
+ continue;
466
+ }
467
+ throw entry;
468
+ }
469
+ if (entry.isSymbolicLink() && (followLast || queue.length > 0)) {
470
+ links += 1;
471
+ if (links > MAX_LINKS) throw errnoError("ELOOP");
472
+ const target = await readlink(next);
473
+ if (target.startsWith("/")) current = "/";
474
+ queue.unshift(...components(target));
475
+ } else {
476
+ current = next;
477
+ }
478
+ }
479
+ return current;
480
+ }
481
+ function modeName(mode) {
482
+ return mode.charAt(0).toUpperCase() + mode.slice(1);
483
+ }
484
+ var FS_OPS = /* @__PURE__ */ new Set([
485
+ "os/read-file",
486
+ "os/write-file",
487
+ "os/append-file",
488
+ "os/open",
489
+ "os/close",
490
+ "os/read",
491
+ "os/write",
492
+ "os/seek",
493
+ "os/truncate",
494
+ "os/fsync",
495
+ "os/copy",
496
+ "os/rename",
497
+ "os/remove",
498
+ "os/link",
499
+ "os/symlink",
500
+ "os/readlink",
501
+ "os/watch-files",
502
+ "os/mkdir",
503
+ "os/rmdir",
504
+ "os/list-dir",
505
+ "os/glob",
506
+ "os/mktemp",
507
+ "os/realpath",
508
+ "os/cwd",
509
+ "os/chdir",
510
+ "os/stat",
511
+ "os/lstat",
512
+ "os/access",
513
+ "os/exists",
514
+ "os/chmod",
515
+ "os/chown",
516
+ "os/utimes"
517
+ ]);
518
+ function isFsOp(op) {
519
+ return FS_OPS.has(op);
520
+ }
521
+ var FsOps = class {
522
+ constructor(state, grants) {
523
+ this.state = state;
524
+ this.grants = grants;
525
+ }
526
+ state;
527
+ grants;
528
+ failure(op, kind, what) {
529
+ return osFailure(kind, op, what);
530
+ }
531
+ io(op, path, error) {
532
+ return this.failure(op, fsKind(errnoCode(error)), `${path}: ${errnoText(error)}`);
533
+ }
534
+ allow(op, path, mode) {
535
+ if (!this.grants.allows({ need: "fs", path, mode })) {
536
+ throw denied(op, `${path} is not granted for ${modeName(mode)}`);
537
+ }
538
+ }
539
+ async locate(op, raw, mode, followLast) {
540
+ if (raw === "") throw invalid(op, "empty path");
541
+ const absolute = this.absolute(op, raw);
542
+ const norm = lexical(absolute);
543
+ if (norm === void 0) throw denied(op, `${raw} climbs above the root`);
544
+ this.allow(op, norm, mode);
545
+ const real = await resolveReal(norm, followLast).catch((raised) => {
546
+ throw this.io(op, norm, raised);
547
+ });
548
+ if (real !== norm && !this.grants.allows({ need: "fs", path: real, mode })) {
549
+ throw denied(op, `${norm} resolves to ${real}, outside the grant`);
550
+ }
551
+ return { norm, real };
552
+ }
553
+ absolute(op, raw) {
554
+ const anchored = expandAnchor(this.state.anchors, raw);
555
+ if (anchored.kind === "anchored") return anchored.path;
556
+ if (anchored.kind === "unknown") throw invalid(op, anchored.message);
557
+ if (raw.startsWith("/")) return raw;
558
+ const cwd = this.state.cwd;
559
+ if (cwd === void 0) throw invalid(op, `${raw} is relative and no working directory is set (os/chdir)`);
560
+ return `${cwd}/${raw}`;
561
+ }
562
+ require(op, at, mode) {
563
+ this.allow(op, at.norm, mode);
564
+ if (at.real !== at.norm) this.allow(op, at.real, mode);
565
+ }
566
+ async path(op, raw, mode, followLast) {
567
+ return (await this.locate(op, raw, mode, followLast)).real;
568
+ }
569
+ async missing(op, at) {
570
+ const found = await lstat(at.real).catch((raised) => raised);
571
+ if (!(found instanceof Error)) return false;
572
+ if (errnoCode(found) === "ENOENT") return true;
573
+ throw this.io(op, at.real, found);
574
+ }
575
+ async writeTarget(op, raw) {
576
+ const at = await this.locate(op, raw, "write", true);
577
+ if (await this.missing(op, at)) this.require(op, at, "create");
578
+ return at.real;
579
+ }
580
+ modeArg(op, args, i) {
581
+ if (argOpt(args, i) === void 0) return void 0;
582
+ const mode = argI64(args, i, op);
583
+ if (mode < 0 || mode > 4095) throw invalid(op, "mode must be within 0..=0o7777");
584
+ return mode;
585
+ }
586
+ fdEntry(op, fd) {
587
+ const entry = this.state.fds.get(fd);
588
+ if (entry === void 0) throw osFailure("closed", op, `fd ${fd} is not open`);
589
+ return entry;
590
+ }
591
+ fileEntry(op, fd) {
592
+ const entry = this.fdEntry(op, fd);
593
+ if (entry.kind !== "file") throw invalid(op, `fd ${fd} is a pipe, not a file`);
594
+ return entry;
595
+ }
596
+ fdIo(op, raised) {
597
+ return this.io(op, "fd", raised);
598
+ }
599
+ /** `None` of the native `answer`: an `os/close` of a handle this table does not own. */
600
+ closeOwned(args) {
601
+ const fd = argI64(args, 0, "os/close");
602
+ const entry = this.state.fds.get(fd);
603
+ if (fd < FIRST_HANDLE_ID || entry === void 0) return false;
604
+ this.state.fds.delete(fd);
605
+ closeFd(entry);
606
+ return true;
607
+ }
608
+ async run(op, args) {
609
+ switch (op) {
610
+ case "os/read-file":
611
+ return this.readFile(op, args);
612
+ case "os/write-file":
613
+ return this.writeFile(op, args, false);
614
+ case "os/append-file":
615
+ return this.writeFile(op, args, true);
616
+ case "os/open":
617
+ return this.open(op, args);
618
+ case "os/read":
619
+ return this.read(op, args);
620
+ case "os/write":
621
+ return this.write(op, args);
622
+ case "os/seek":
623
+ return this.seek(op, args);
624
+ case "os/truncate":
625
+ return this.truncate(op, args);
626
+ case "os/fsync": {
627
+ const entry = this.fileEntry(op, argI64(args, 0, op));
628
+ await entry.handle.sync().catch((raised) => {
629
+ throw this.fdIo(op, raised);
630
+ });
631
+ return {};
632
+ }
633
+ case "os/copy": {
634
+ const from = await this.path(op, argStr(args, 0, op), "read", true);
635
+ const to = await this.writeTarget(op, argStr(args, 1, op));
636
+ await copyFile(from, to).catch((raised) => {
637
+ throw this.io(op, from, raised);
638
+ });
639
+ return {};
640
+ }
641
+ case "os/rename": {
642
+ const from = await this.path(op, argStr(args, 0, op), "remove", false);
643
+ const to = await this.locate(op, argStr(args, 1, op), "create", false);
644
+ if (!await this.missing(op, to)) this.require(op, to, "remove");
645
+ await rename(from, to.real).catch((raised) => {
646
+ throw this.io(op, from, raised);
647
+ });
648
+ return {};
649
+ }
650
+ case "os/remove": {
651
+ const path = await this.path(op, argStr(args, 0, op), "remove", false);
652
+ const entry = await lstat(path).catch((raised) => {
653
+ throw this.io(op, path, raised);
654
+ });
655
+ if (entry.isDirectory()) throw invalid(op, `${path} is a directory; use os/rmdir`);
656
+ await unlink(path).catch((raised) => {
657
+ throw this.io(op, path, raised);
658
+ });
659
+ return {};
660
+ }
661
+ case "os/link": {
662
+ const target = await this.path(op, argStr(args, 0, op), "write", true);
663
+ const path = await this.path(op, argStr(args, 1, op), "create", false);
664
+ await link(target, path).catch((raised) => {
665
+ throw this.io(op, path, raised);
666
+ });
667
+ return {};
668
+ }
669
+ case "os/symlink": {
670
+ const target = argStr(args, 0, op);
671
+ const path = await this.path(op, argStr(args, 1, op), "create", false);
672
+ await symlink(target, path).catch((raised) => {
673
+ throw this.io(op, path, raised);
674
+ });
675
+ return {};
676
+ }
677
+ case "os/readlink": {
678
+ const path = await this.path(op, argStr(args, 0, op), "read", false);
679
+ const target = await readlink(path).catch((raised) => {
680
+ throw this.io(op, path, raised);
681
+ });
682
+ return { target };
683
+ }
684
+ case "os/watch-files":
685
+ throw this.failure(op, "unsupported", "streaming host not implemented");
686
+ case "os/mkdir":
687
+ return this.mkdir(op, args);
688
+ case "os/rmdir":
689
+ return this.rmdir(op, args);
690
+ case "os/list-dir":
691
+ return this.listDir(op, args);
692
+ case "os/glob":
693
+ return this.glob(op, args);
694
+ case "os/mktemp":
695
+ return this.mktemp(op, args);
696
+ case "os/realpath": {
697
+ const path = await this.path(op, argStr(args, 0, op), "read", true);
698
+ await lstat(path).catch((raised) => {
699
+ throw this.io(op, path, raised);
700
+ });
701
+ return { path };
702
+ }
703
+ case "os/cwd": {
704
+ const cwd = this.state.cwd;
705
+ if (cwd === void 0) throw invalid(op, "no working directory is set (os/chdir)");
706
+ return { path: cwd };
707
+ }
708
+ case "os/chdir": {
709
+ const path = await this.path(op, argStr(args, 0, op), "read", true);
710
+ const entry = await stat(path).catch((raised) => {
711
+ throw this.io(op, path, raised);
712
+ });
713
+ if (!entry.isDirectory()) throw invalid(op, `${path} is not a directory`);
714
+ this.state.cwd = path;
715
+ return {};
716
+ }
717
+ case "os/stat":
718
+ case "os/lstat": {
719
+ const follow = op === "os/stat";
720
+ const path = await this.path(op, argStr(args, 0, op), "read", follow);
721
+ const read = follow ? stat : lstat;
722
+ const entry = await read(path, { bigint: true }).catch((raised) => {
723
+ throw this.io(op, path, raised);
724
+ });
725
+ return statValue(entry);
726
+ }
727
+ case "os/access":
728
+ return this.access(op, args);
729
+ case "os/exists": {
730
+ const path = await this.path(op, argStr(args, 0, op), "read", true);
731
+ const present = await stat(path).then(() => true, () => false);
732
+ return { ok: present };
733
+ }
734
+ case "os/chmod": {
735
+ const path = await this.path(op, argStr(args, 0, op), "meta", true);
736
+ const mode = this.modeArg(op, args, 1);
737
+ if (mode === void 0) throw invalid(op, "missing mode");
738
+ await chmod(path, mode).catch((raised) => {
739
+ throw this.io(op, path, raised);
740
+ });
741
+ return {};
742
+ }
743
+ case "os/chown":
744
+ return this.chown(op, args);
745
+ case "os/utimes":
746
+ return this.utimes(op, args);
747
+ default:
748
+ throw this.failure(op, "unsupported", "not a file operation");
749
+ }
750
+ }
751
+ async readFile(op, args) {
752
+ const path = await this.path(op, argStr(args, 0, op), "read", true);
753
+ const handle = await open(path, "r").catch((raised) => {
754
+ throw this.io(op, path, raised);
755
+ });
756
+ const parts = [];
757
+ let total = 0;
758
+ try {
759
+ while (total < MAX_BYTES + 1) {
760
+ const chunk = Buffer.allocUnsafe(Math.min(CHUNK, MAX_BYTES + 1 - total));
761
+ const { bytesRead } = await handle.read(chunk, 0, chunk.length, null);
762
+ if (bytesRead === 0) break;
763
+ parts.push(chunk.subarray(0, bytesRead));
764
+ total += bytesRead;
765
+ }
766
+ } catch (raised) {
767
+ throw this.io(op, path, raised instanceof Error ? raised : new Error(String(raised)));
768
+ } finally {
769
+ await handle.close();
770
+ }
771
+ if (total > MAX_BYTES) {
772
+ throw invalid(op, `${path} exceeds the ${MAX_BYTES}-byte (64 MiB) per-call cap; use os/open and os/read`);
773
+ }
774
+ return { data: bytesValue(Buffer.concat(parts)) };
775
+ }
776
+ async writeFile(op, args, append) {
777
+ const path = await this.writeTarget(op, argStr(args, 0, op));
778
+ const data = argBytes(args, 1, op);
779
+ const mode = this.modeArg(op, args, 2);
780
+ const flags = constants$1.O_WRONLY | constants$1.O_CREAT | (append ? constants$1.O_APPEND : constants$1.O_TRUNC);
781
+ const handle = await open(path, flags, mode).catch((raised) => {
782
+ throw this.io(op, path, raised);
783
+ });
784
+ try {
785
+ let written = 0;
786
+ while (written < data.length) {
787
+ const { bytesWritten } = await handle.write(data, written, data.length - written, null);
788
+ written += bytesWritten;
789
+ }
790
+ } catch (raised) {
791
+ throw this.io(op, path, raised instanceof Error ? raised : new Error(String(raised)));
792
+ } finally {
793
+ await handle.close();
794
+ }
795
+ return { bytes: data.length };
796
+ }
797
+ parseFlags(op, flags) {
798
+ const exclusive = flags.endsWith("x");
799
+ const base = exclusive ? flags.slice(0, -1) : flags;
800
+ let parsed;
801
+ switch (base) {
802
+ case "r":
803
+ parsed = { read: true, write: false, append: false, create: false, truncate: false };
804
+ break;
805
+ case "r+":
806
+ parsed = { read: true, write: true, append: false, create: false, truncate: false };
807
+ break;
808
+ case "w":
809
+ parsed = { read: false, write: true, append: false, create: true, truncate: true };
810
+ break;
811
+ case "w+":
812
+ parsed = { read: true, write: true, append: false, create: true, truncate: true };
813
+ break;
814
+ case "a":
815
+ parsed = { read: false, write: true, append: true, create: true, truncate: false };
816
+ break;
817
+ case "a+":
818
+ parsed = { read: true, write: true, append: true, create: true, truncate: false };
819
+ break;
820
+ default:
821
+ throw invalid(op, `flags ${JSON.stringify(flags)} must be one of r r+ w w+ a a+, optionally followed by x`);
822
+ }
823
+ if (exclusive && !parsed.create) throw invalid(op, "the x flag needs w or a");
824
+ return { ...parsed, exclusive };
825
+ }
826
+ async open(op, args) {
827
+ const raw = argStr(args, 0, op);
828
+ const flags = this.parseFlags(op, argStr(args, 1, op));
829
+ const mode = this.modeArg(op, args, 2);
830
+ const at = await this.locate(op, raw, flags.write ? "write" : "read", true);
831
+ if (flags.read && flags.write) this.require(op, at, "read");
832
+ if (flags.create && (flags.exclusive || await this.missing(op, at))) this.require(op, at, "create");
833
+ let access2 = constants$1.O_RDONLY;
834
+ if (flags.write) access2 = flags.read ? constants$1.O_RDWR : constants$1.O_WRONLY;
835
+ let numeric = access2;
836
+ if (flags.append) numeric |= constants$1.O_APPEND;
837
+ if (flags.truncate) numeric |= constants$1.O_TRUNC;
838
+ if (flags.exclusive) numeric |= constants$1.O_CREAT | constants$1.O_EXCL;
839
+ else if (flags.create) numeric |= constants$1.O_CREAT;
840
+ const handle = await open(at.real, numeric, mode).catch((raised) => {
841
+ throw this.io(op, at.real, raised);
842
+ });
843
+ const fd = this.state.allocate();
844
+ this.state.fds.set(fd, { kind: "file", handle, position: 0, append: flags.append });
845
+ return { fd };
846
+ }
847
+ async read(op, args) {
848
+ return (await this.readLater(op, args)())();
849
+ }
850
+ /** `os/read` after its checks; a keyed ask under a route runs the returned work in the background (EXEC-QUEUE-10). */
851
+ readLater(op, args) {
852
+ const fd = argI64(args, 0, op);
853
+ const count = argI64(args, 1, op);
854
+ if (count < 0 || count > MAX_BYTES) {
855
+ throw invalid(op, `count must be within 0..=${MAX_BYTES} (64 MiB per-call cap)`);
856
+ }
857
+ const entry = this.fdEntry(op, fd);
858
+ if (entry.kind === "child-stdin") throw invalid(op, `fd ${fd} is write-only`);
859
+ return async () => {
860
+ let data;
861
+ if (entry.kind === "file") {
862
+ const parts = [];
863
+ let total = 0;
864
+ try {
865
+ while (total < count) {
866
+ const chunk = Buffer.allocUnsafe(Math.min(CHUNK, count - total));
867
+ const { bytesRead } = await entry.handle.read(chunk, 0, chunk.length, entry.position);
868
+ if (bytesRead === 0) break;
869
+ parts.push(chunk.subarray(0, bytesRead));
870
+ total += bytesRead;
871
+ entry.position += bytesRead;
872
+ }
873
+ } catch (raised) {
874
+ throw this.fdIo(op, raised instanceof Error ? raised : new Error(String(raised)));
875
+ }
876
+ data = Buffer.concat(parts);
877
+ } else {
878
+ try {
879
+ const taken = await entry.source.take(count, void 0);
880
+ data = taken === "timeout" ? Buffer.alloc(0) : taken;
881
+ } catch (raised) {
882
+ throw this.fdIo(op, raised instanceof Error ? raised : new Error(String(raised)));
883
+ }
884
+ }
885
+ const answer = { data: bytesValue(data), eof: count > 0 && data.length === 0 };
886
+ return () => answer;
887
+ };
888
+ }
889
+ async write(op, args) {
890
+ const fd = argI64(args, 0, op);
891
+ const data = argBytes(args, 1, op);
892
+ const entry = this.fdEntry(op, fd);
893
+ if (entry.kind === "file") {
894
+ try {
895
+ let written = 0;
896
+ while (written < data.length) {
897
+ const position = entry.append ? null : entry.position;
898
+ const { bytesWritten } = await entry.handle.write(data, written, data.length - written, position);
899
+ written += bytesWritten;
900
+ if (!entry.append) entry.position += bytesWritten;
901
+ }
902
+ if (entry.append) entry.position = (await entry.handle.stat()).size;
903
+ } catch (raised) {
904
+ throw this.fdIo(op, raised instanceof Error ? raised : new Error(String(raised)));
905
+ }
906
+ } else if (entry.kind === "child-stdin") {
907
+ const { stream } = entry;
908
+ await new Promise((resolve2, reject) => {
909
+ stream.write(data, (error) => error ? reject(error) : resolve2());
910
+ }).catch((raised) => {
911
+ throw this.fdIo(op, raised);
912
+ });
913
+ } else {
914
+ throw invalid(op, `fd ${fd} is read-only`);
915
+ }
916
+ return { bytes: data.length };
917
+ }
918
+ async seek(op, args) {
919
+ const fd = argI64(args, 0, op);
920
+ const offset = argI64(args, 1, op);
921
+ const whence = argStr(args, 2, op);
922
+ if (whence !== "set" && whence !== "cur" && whence !== "end") {
923
+ throw invalid(op, `whence ${JSON.stringify(whence)} must be set, cur or end`);
924
+ }
925
+ if (whence === "set" && offset < 0) throw invalid(op, "a set offset cannot be negative");
926
+ const entry = this.fileEntry(op, fd);
927
+ let target = offset;
928
+ if (whence === "cur") target = entry.position + offset;
929
+ if (whence === "end") {
930
+ const size = (await entry.handle.stat().catch((raised) => {
931
+ throw this.fdIo(op, raised);
932
+ })).size;
933
+ target = size + offset;
934
+ }
935
+ if (target < 0) throw this.fdIo(op, errnoError("EINVAL"));
936
+ entry.position = target;
937
+ return { offset: target };
938
+ }
939
+ async truncate(op, args) {
940
+ const size = argI64(args, 1, op);
941
+ if (size < 0) throw invalid(op, "size cannot be negative");
942
+ if (typeof args[0] === "number") {
943
+ const entry = this.fileEntry(op, argI64(args, 0, op));
944
+ await entry.handle.truncate(size).catch((raised) => {
945
+ throw this.fdIo(op, raised);
946
+ });
947
+ return {};
948
+ }
949
+ const path = await this.path(op, argStr(args, 0, op), "write", true);
950
+ const handle = await open(path, constants$1.O_WRONLY).catch((raised) => {
951
+ throw this.io(op, path, raised);
952
+ });
953
+ try {
954
+ await handle.truncate(size);
955
+ } catch (raised) {
956
+ throw this.io(op, path, raised instanceof Error ? raised : new Error(String(raised)));
957
+ } finally {
958
+ await handle.close();
959
+ }
960
+ return {};
961
+ }
962
+ async mkdir(op, args) {
963
+ const at = await this.locate(op, argStr(args, 0, op), "create", false);
964
+ const recursive = argOpt(args, 1) === void 0 ? false : argBool(args, 1, op);
965
+ const mode = this.modeArg(op, args, 2);
966
+ if (recursive) {
967
+ for (const ancestor of ancestorsOf(at.real)) {
968
+ const present = await lstat(ancestor).then(() => true, () => false);
969
+ if (!present) this.allow(op, ancestor, "create");
970
+ }
971
+ }
972
+ await mkdir(at.real, { recursive, mode }).catch((raised) => {
973
+ throw this.io(op, at.real, raised);
974
+ });
975
+ return {};
976
+ }
977
+ async requireTreeRemovable(op, directory) {
978
+ const entries = await readdir(directory, { withFileTypes: true }).catch((raised) => {
979
+ throw this.io(op, directory, raised);
980
+ });
981
+ for (const entry of entries) {
982
+ const path = joinPath(directory, entry.name);
983
+ this.allow(op, path, "remove");
984
+ if (entry.isDirectory()) await this.requireTreeRemovable(op, path);
985
+ }
986
+ }
987
+ async rmdir(op, args) {
988
+ const path = await this.path(op, argStr(args, 0, op), "remove", false);
989
+ const recursive = argOpt(args, 1) === void 0 ? false : argBool(args, 1, op);
990
+ const entry = await lstat(path).catch((raised) => {
991
+ throw this.io(op, path, raised);
992
+ });
993
+ if (!entry.isDirectory()) throw invalid(op, `${path} is not a directory; use os/remove`);
994
+ if (recursive) {
995
+ await this.requireTreeRemovable(op, path);
996
+ await rm(path, { recursive: true }).catch((raised) => {
997
+ throw this.io(op, path, raised);
998
+ });
999
+ } else {
1000
+ await rmdir(path).catch((raised) => {
1001
+ throw this.io(op, path, raised);
1002
+ });
1003
+ }
1004
+ return {};
1005
+ }
1006
+ async listDir(op, args) {
1007
+ const path = await this.path(op, argStr(args, 0, op), "read", true);
1008
+ const found = await readdir(path, { withFileTypes: true }).catch((raised) => {
1009
+ throw this.io(op, path, raised);
1010
+ });
1011
+ const entries = found.map((entry) => ({ name: entry.name, kind: kindOf(entry) })).sort((a, b) => byteOrder(a.name, b.name) || byteOrder(a.kind, b.kind));
1012
+ return { entries };
1013
+ }
1014
+ async glob(op, args) {
1015
+ const raw = argStr(args, 0, op);
1016
+ if (raw === "") throw invalid(op, "empty pattern");
1017
+ const pattern = lexical(this.absolute(op, raw));
1018
+ if (pattern === void 0) throw denied(op, `${raw} climbs above the root`);
1019
+ const fixed = [];
1020
+ let rest = 0;
1021
+ let wild = false;
1022
+ let deep = false;
1023
+ for (const part of components(pattern)) {
1024
+ if (wild || part.includes("*") || part.includes("?")) {
1025
+ wild = true;
1026
+ rest += 1;
1027
+ deep = deep || part === "**";
1028
+ } else {
1029
+ fixed.push(part);
1030
+ }
1031
+ }
1032
+ const prefix = `/${fixed.join("/")}`;
1033
+ this.allow(op, prefix, "read");
1034
+ const found = [];
1035
+ await this.consider(prefix, pattern, found);
1036
+ if (wild) await this.walk(prefix, 1, deep ? void 0 : rest, pattern, found);
1037
+ const sorted = [...new Set(found)].sort(byteOrder);
1038
+ return { paths: sorted };
1039
+ }
1040
+ async consider(path, pattern, out) {
1041
+ if (!this.grants.globMatches(pattern, path) || !this.grants.allows({ need: "fs", path, mode: "read" })) return;
1042
+ const real = await resolveReal(path, true).catch((raised) => raised);
1043
+ if (real instanceof Error) return;
1044
+ if (real !== path && !this.grants.allows({ need: "fs", path: real, mode: "read" })) return;
1045
+ if (await lstat(path).then(() => true, () => false)) out.push(path);
1046
+ }
1047
+ async walk(directory, level, maxLevel, pattern, out) {
1048
+ const entries = await readdir(directory, { withFileTypes: true }).catch((raised) => raised);
1049
+ if (entries instanceof Error) return;
1050
+ const children = entries.map((entry) => ({ path: joinPath(directory, entry.name), isDir: entry.isDirectory() })).sort((a, b) => byteOrder(a.path, b.path));
1051
+ for (const child of children) {
1052
+ await this.consider(child.path, pattern, out);
1053
+ if (child.isDir && (maxLevel === void 0 || level < maxLevel)) {
1054
+ await this.walk(child.path, level + 1, maxLevel, pattern, out);
1055
+ }
1056
+ }
1057
+ }
1058
+ async mktemp(op, args) {
1059
+ const prefix = argStr(args, 0, op);
1060
+ if (prefix.includes("/") || prefix.includes("\0") || prefix === ".." || prefix === ".") {
1061
+ throw invalid(op, "prefix must be a plain file-name prefix");
1062
+ }
1063
+ const base = argOpt(args, 1) === void 0 ? process.env.TMPDIR ?? "/tmp" : argStr(args, 1, op);
1064
+ const at = await this.locate(op, base, "create", true);
1065
+ for (let attempt = 0; attempt < 64; attempt += 1) {
1066
+ const candidate = joinPath(at.real, `${prefix}${randomBytes(8).toString("hex")}`);
1067
+ const handle = await open(candidate, constants$1.O_WRONLY | constants$1.O_CREAT | constants$1.O_EXCL, 384).catch((raised) => raised);
1068
+ if (handle instanceof Error) {
1069
+ if (errnoCode(handle) === "EEXIST") continue;
1070
+ throw this.io(op, candidate, handle);
1071
+ }
1072
+ await handle.close();
1073
+ return { path: candidate };
1074
+ }
1075
+ throw this.failure(op, "io", "could not find an unused temporary name");
1076
+ }
1077
+ async access(op, args) {
1078
+ const path = await this.path(op, argStr(args, 0, op), "read", true);
1079
+ let flags = constants$1.F_OK;
1080
+ if (argOpt(args, 1) !== void 0) {
1081
+ const mode = argStr(args, 1, op);
1082
+ if (mode === "exists") flags = constants$1.F_OK;
1083
+ else if (mode === "read") flags = constants$1.R_OK;
1084
+ else if (mode === "write") flags = constants$1.W_OK;
1085
+ else if (mode === "exec" || mode === "execute") flags = constants$1.X_OK;
1086
+ else if (mode !== "" && [...mode].every((letter) => "rwx".includes(letter))) {
1087
+ flags = 0;
1088
+ for (const letter of mode) {
1089
+ flags |= letter === "r" ? constants$1.R_OK : letter === "w" ? constants$1.W_OK : constants$1.X_OK;
1090
+ }
1091
+ } else {
1092
+ throw invalid(op, `mode ${JSON.stringify(mode)} must be exists, read, write, exec or letters of rwx`);
1093
+ }
1094
+ }
1095
+ const outcome = await access(path, flags).then(() => void 0, (raised) => raised);
1096
+ if (outcome === void 0) return { ok: true };
1097
+ const code = errnoCode(outcome);
1098
+ if (code === "ENOENT" || code === "EACCES" || code === "EPERM") return { ok: false };
1099
+ throw this.io(op, path, outcome);
1100
+ }
1101
+ async chown(op, args) {
1102
+ const path = await this.path(op, argStr(args, 0, op), "meta", true);
1103
+ const id = (i) => {
1104
+ const value = argI64(args, i, op);
1105
+ if (value === -1) return -1;
1106
+ if (value < 0 || value >= 4294967295) throw invalid(op, "uid and gid must be -1 or a valid id");
1107
+ return value;
1108
+ };
1109
+ const uid = id(1);
1110
+ const gid = id(2);
1111
+ await chown(path, uid, gid).catch((raised) => {
1112
+ throw this.io(op, path, raised);
1113
+ });
1114
+ return {};
1115
+ }
1116
+ async utimes(op, args) {
1117
+ const path = await this.path(op, argStr(args, 0, op), "meta", true);
1118
+ const accessed = new Date(argI64(args, 1, op));
1119
+ const modified = new Date(argI64(args, 2, op));
1120
+ if (Number.isNaN(accessed.getTime())) throw invalid(op, "atime is out of range");
1121
+ if (Number.isNaN(modified.getTime())) throw invalid(op, "mtime is out of range");
1122
+ const handle = await open(path, "r").catch((raised) => {
1123
+ throw this.io(op, path, raised);
1124
+ });
1125
+ try {
1126
+ await handle.utimes(accessed, modified);
1127
+ } catch (raised) {
1128
+ throw this.io(op, path, raised instanceof Error ? raised : new Error(String(raised)));
1129
+ } finally {
1130
+ await handle.close();
1131
+ }
1132
+ return {};
1133
+ }
1134
+ };
1135
+ var IO_TIMEOUT_MS = 5e3;
1136
+ var CONNECT_TIMEOUT_MS = 1e4;
1137
+ var MAX_RECV = 64 * 1024 * 1024;
1138
+ var MAX_DATAGRAM = 65535;
1139
+ var SOCKET_HIGH_WATER = 1024 * 1024;
1140
+ var NET_OPS = /* @__PURE__ */ new Set([
1141
+ "os/tcp-connect",
1142
+ "os/tcp-listen",
1143
+ "os/send",
1144
+ "os/recv",
1145
+ "os/udp-bind",
1146
+ "os/sendto",
1147
+ "os/recvfrom",
1148
+ "os/dns-resolve",
1149
+ "os/shutdown",
1150
+ "os/watch-port"
1151
+ ]);
1152
+ function isNetOp(op) {
1153
+ return NET_OPS.has(op);
1154
+ }
1155
+ var LOOKUP_FAILED = process.platform === "darwin" ? "nodename nor servname provided, or not known" : "Name or service not known";
1156
+ function hostArg(args, i, op) {
1157
+ const name = argStr(args, i, op).replace(/^\[+/, "").replace(/\]+$/, "");
1158
+ if (name === "") throw osFailure("invalid", op, "the host is empty");
1159
+ return name;
1160
+ }
1161
+ function portArg(args, i, op) {
1162
+ const port = argI64(args, i, op);
1163
+ if (port < 0 || port > 65535) throw osFailure("invalid", op, "the port must be 0 to 65535");
1164
+ return port;
1165
+ }
1166
+ function countArg(args, i, op) {
1167
+ const count = argI64(args, i, op);
1168
+ if (count < 0) throw osFailure("invalid", op, "the count must not be negative");
1169
+ return count;
1170
+ }
1171
+ function peerText(address, port, family) {
1172
+ return family === "IPv6" ? `[${address}]:${port}` : `${address}:${port}`;
1173
+ }
1174
+ function streamFailure(op, error) {
1175
+ const code = errnoCode(error);
1176
+ return netFailure(op, code !== void 0 && code.startsWith("ERR_STREAM") ? errnoError("EPIPE") : error);
1177
+ }
1178
+ var NetOps = class {
1179
+ constructor(state, grants) {
1180
+ this.state = state;
1181
+ this.grants = grants;
1182
+ }
1183
+ state;
1184
+ grants;
1185
+ checkGrant(op, name, port, kind) {
1186
+ if (!this.grants.allows({ need: "net", host: name, port, kinds: [kind] })) {
1187
+ throw denied(op, `${name}:${port} is not granted`);
1188
+ }
1189
+ }
1190
+ async resolve(op, name) {
1191
+ const found = await lookup(name, { all: true, verbatim: true }).catch((raised) => raised);
1192
+ if (found instanceof Error) {
1193
+ const text = errnoCode(found) === "ENOTFOUND" ? `failed to lookup address information: ${LOOKUP_FAILED}` : found.message;
1194
+ throw osFailure("not-found", op, `${name} did not resolve: ${text}`);
1195
+ }
1196
+ if (found.length === 0) throw osFailure("not-found", op, `${name} has no addresses`);
1197
+ return found.map((entry) => ({ address: entry.address, family: entry.family === 6 ? 6 : 4 }));
1198
+ }
1199
+ async outbound(op, name) {
1200
+ const literal = isIP(name);
1201
+ if (literal !== 0) return [{ address: name, family: literal === 6 ? 6 : 4 }];
1202
+ const addresses = await this.resolve(op, name);
1203
+ const closed = addresses.find((entry) => !this.grants.isPublicAddress(entry.address));
1204
+ if (closed !== void 0) throw denied(op, `${name} resolves to ${closed.address}, not a public address`);
1205
+ return addresses;
1206
+ }
1207
+ streamEntry(op, id) {
1208
+ const entry = this.state.socks.get(id);
1209
+ if (entry === void 0) throw osFailure("closed", op, "not an open socket");
1210
+ if (entry.kind !== "stream") throw osFailure("invalid", op, "not a connected stream socket");
1211
+ return entry;
1212
+ }
1213
+ datagramEntry(op, id) {
1214
+ const entry = this.state.socks.get(id);
1215
+ if (entry === void 0) throw osFailure("closed", op, "not an open socket");
1216
+ if (entry.kind !== "datagram") throw osFailure("invalid", op, "not a datagram socket");
1217
+ return entry;
1218
+ }
1219
+ /** `None` of the native `answer`: an `os/close` of a handle this table does not own. */
1220
+ closeOwned(args) {
1221
+ const id = argI64(args, 0, "os/close");
1222
+ const entry = this.state.socks.get(id);
1223
+ if (entry === void 0) return false;
1224
+ this.state.socks.delete(id);
1225
+ closeSock(entry);
1226
+ return true;
1227
+ }
1228
+ async run(op, args) {
1229
+ switch (op) {
1230
+ case "os/tcp-listen":
1231
+ throw invalid(op, "a stream needs a key and a call site");
1232
+ case "os/tcp-connect":
1233
+ return this.tcpConnect(op, args);
1234
+ case "os/send":
1235
+ return this.send(op, args);
1236
+ case "os/recv":
1237
+ return this.recv(op, args);
1238
+ case "os/udp-bind":
1239
+ return this.udpBind(op, args);
1240
+ case "os/sendto":
1241
+ return this.sendto(op, args);
1242
+ case "os/recvfrom":
1243
+ return this.recvfrom(op, args);
1244
+ case "os/dns-resolve":
1245
+ return this.dnsResolve(op, args);
1246
+ case "os/shutdown":
1247
+ return this.shutdown(op, args);
1248
+ default:
1249
+ throw osFailure("unsupported", op, "streaming host not implemented");
1250
+ }
1251
+ }
1252
+ /**
1253
+ * The keyed listening stream of `os/tcp-listen` (native `listen`): binds, answers `{ sock, address, port }`, then hands
1254
+ * each accepted connection to `accept` as `{ sock, peer }`; a connection no one takes is closed. Closing the listener's
1255
+ * handle stops listening.
1256
+ */
1257
+ async listen(op, args, accept, fail2) {
1258
+ const name = hostArg(args, 0, op);
1259
+ const port = portArg(args, 1, op);
1260
+ this.checkGrant(op, name, port, "listen");
1261
+ const literal = isIP(name);
1262
+ const [address] = literal !== 0 ? [{ address: name, family: literal === 6 ? 6 : 4 }] : await this.resolve(op, name);
1263
+ if (address === void 0) throw osFailure("not-found", op, `${name} did not resolve`);
1264
+ const server = createServer();
1265
+ await new Promise((resolve2, reject) => {
1266
+ server.once("error", reject);
1267
+ server.listen({ host: address.address, port }, () => {
1268
+ server.off("error", reject);
1269
+ resolve2();
1270
+ });
1271
+ }).catch((raised) => {
1272
+ throw netFailure(op, raised);
1273
+ });
1274
+ const id = this.state.allocate();
1275
+ this.state.socks.set(id, { kind: "listener", server });
1276
+ server.on("connection", (socket) => {
1277
+ if (!this.state.socks.has(id)) {
1278
+ socket.destroy();
1279
+ return;
1280
+ }
1281
+ const conn = this.state.allocate();
1282
+ this.state.socks.set(conn, { kind: "stream", socket, source: new ByteSource(socket, SOCKET_HIGH_WATER), shut: false });
1283
+ if (!accept({ sock: conn, peer: peerOf(socket) })) {
1284
+ this.state.socks.delete(conn);
1285
+ socket.destroy();
1286
+ }
1287
+ });
1288
+ server.on("error", (raised) => {
1289
+ if (this.state.socks.delete(id)) server.close();
1290
+ fail2(netFailure(op, raised).error);
1291
+ });
1292
+ const bound = server.address();
1293
+ if (bound === null || typeof bound === "string") throw osFailure("io", op, "the listener has no address");
1294
+ return { sock: id, address: bound.address, port: bound.port };
1295
+ }
1296
+ /** Stop the listener `id` (the stream's cancel): its handle closes and no connection is accepted after. */
1297
+ stopListening(id) {
1298
+ const entry = this.state.socks.get(id);
1299
+ if (entry?.kind !== "listener") return;
1300
+ this.state.socks.delete(id);
1301
+ entry.server.close();
1302
+ }
1303
+ connectOnce(address, port) {
1304
+ return new Promise((resolve2, reject) => {
1305
+ const socket = createConnection({ host: address.address, port, autoSelectFamily: false });
1306
+ const timer = setTimeout(() => {
1307
+ socket.destroy();
1308
+ reject(new TextIoError("ETIMEDOUT", "connection timed out"));
1309
+ }, CONNECT_TIMEOUT_MS);
1310
+ socket.once("connect", () => {
1311
+ clearTimeout(timer);
1312
+ resolve2(socket);
1313
+ });
1314
+ socket.once("error", (error) => {
1315
+ clearTimeout(timer);
1316
+ reject(error);
1317
+ });
1318
+ });
1319
+ }
1320
+ async tcpConnect(op, args) {
1321
+ const name = hostArg(args, 0, op);
1322
+ const port = portArg(args, 1, op);
1323
+ this.checkGrant(op, name, port, "tcp");
1324
+ let last;
1325
+ for (const address of await this.outbound(op, name)) {
1326
+ const outcome = await this.connectOnce(address, port).catch((raised) => raised);
1327
+ if (outcome instanceof Error) {
1328
+ last = outcome;
1329
+ continue;
1330
+ }
1331
+ const id = this.state.allocate();
1332
+ this.state.socks.set(id, { kind: "stream", socket: outcome, source: new ByteSource(outcome, SOCKET_HIGH_WATER), shut: false });
1333
+ return { sock: id };
1334
+ }
1335
+ if (last === void 0) throw osFailure("io", op, "no address to connect to");
1336
+ throw netFailure(op, last);
1337
+ }
1338
+ async send(op, args) {
1339
+ return (await this.sendLater(op, args)())();
1340
+ }
1341
+ /** `os/send` after its checks; a keyed ask under a route writes in the background (EXEC-QUEUE-12). The write is queued before the first await, so the socket's write queue keeps sends in call order. */
1342
+ sendLater(op, args) {
1343
+ const id = argI64(args, 0, op);
1344
+ const data = argBytes(args, 1, op);
1345
+ const entry = this.streamEntry(op, id);
1346
+ if (entry.shut) throw netFailure(op, errnoError("EPIPE"));
1347
+ return async () => {
1348
+ await writeAll(entry.socket, data, IO_TIMEOUT_MS).catch((raised) => {
1349
+ throw streamFailure(op, raised);
1350
+ });
1351
+ const answer = { bytes: data.length };
1352
+ return () => answer;
1353
+ };
1354
+ }
1355
+ async recv(op, args) {
1356
+ return (await this.recvLater(op, args)())();
1357
+ }
1358
+ /** `os/recv` after its checks; a keyed ask under a route reads in the background (EXEC-QUEUE-10). */
1359
+ recvLater(op, args) {
1360
+ const id = argI64(args, 0, op);
1361
+ const want = Math.min(countArg(args, 1, op), MAX_RECV);
1362
+ const entry = this.streamEntry(op, id);
1363
+ return async () => {
1364
+ if (entry.shut) {
1365
+ const ended = { data: bytesValue(Buffer.alloc(0)), eof: want > 0 };
1366
+ return () => ended;
1367
+ }
1368
+ const taken = await entry.source.take(want, IO_TIMEOUT_MS).catch((raised) => {
1369
+ throw netFailure(op, raised);
1370
+ });
1371
+ if (taken === "timeout") throw netFailure(op, errnoError("EAGAIN"));
1372
+ const answer = { data: bytesValue(taken), eof: taken.length === 0 && want > 0 };
1373
+ return () => answer;
1374
+ };
1375
+ }
1376
+ bindOnce(address, port) {
1377
+ return new Promise((resolve2, reject) => {
1378
+ const socket = createSocket(address.family === 6 ? "udp6" : "udp4");
1379
+ const entry = { kind: "datagram", socket, inbox: [], failure: void 0, wake: void 0 };
1380
+ socket.on("message", (data, info) => {
1381
+ entry.inbox.push({ data, peer: peerText(info.address, info.port, info.family) });
1382
+ entry.wake?.();
1383
+ });
1384
+ socket.on("error", (error) => {
1385
+ entry.failure = error;
1386
+ entry.wake?.();
1387
+ reject(error);
1388
+ });
1389
+ socket.once("listening", () => resolve2(entry));
1390
+ socket.bind(port, address.address);
1391
+ });
1392
+ }
1393
+ async udpBind(op, args) {
1394
+ const name = hostArg(args, 0, op);
1395
+ const port = portArg(args, 1, op);
1396
+ this.checkGrant(op, name, port, "udp");
1397
+ let last;
1398
+ for (const address of await this.resolve(op, name)) {
1399
+ const bound = await this.bindOnce(address, port).catch((raised) => raised);
1400
+ if (bound instanceof Error) {
1401
+ last = bound;
1402
+ continue;
1403
+ }
1404
+ const id = this.state.allocate();
1405
+ this.state.socks.set(id, bound);
1406
+ return { sock: id };
1407
+ }
1408
+ throw last === void 0 ? osFailure("io", op, "no address to bind") : netFailure(op, last);
1409
+ }
1410
+ async sendto(op, args) {
1411
+ const id = argI64(args, 0, op);
1412
+ const data = argBytes(args, 1, op);
1413
+ const name = hostArg(args, 2, op);
1414
+ const port = portArg(args, 3, op);
1415
+ this.checkGrant(op, name, port, "udp");
1416
+ const entry = this.datagramEntry(op, id);
1417
+ let last;
1418
+ for (const address of await this.outbound(op, name)) {
1419
+ const sent = await new Promise((resolve2) => {
1420
+ entry.socket.send(data, port, address.address, (error) => resolve2(error ?? void 0));
1421
+ });
1422
+ if (sent === void 0) return { bytes: data.length };
1423
+ last = sent;
1424
+ }
1425
+ throw last === void 0 ? osFailure("io", op, "no address to send to") : netFailure(op, last);
1426
+ }
1427
+ async nextDatagram(entry) {
1428
+ const deadline = Date.now() + IO_TIMEOUT_MS;
1429
+ for (; ; ) {
1430
+ const queued = entry.inbox.shift();
1431
+ if (queued !== void 0) return queued;
1432
+ if (entry.failure !== void 0) return entry.failure;
1433
+ const left = deadline - Date.now();
1434
+ if (left <= 0) return errnoError("EAGAIN");
1435
+ await new Promise((resolve2) => {
1436
+ const timer = setTimeout(resolve2, left);
1437
+ entry.wake = () => {
1438
+ clearTimeout(timer);
1439
+ resolve2();
1440
+ };
1441
+ });
1442
+ }
1443
+ }
1444
+ async recvfrom(op, args) {
1445
+ return (await this.recvfromLater(op, args)())();
1446
+ }
1447
+ /** `os/recvfrom` after its checks; a keyed ask under a route reads in the background (EXEC-QUEUE-10). */
1448
+ recvfromLater(op, args) {
1449
+ const id = argI64(args, 0, op);
1450
+ const want = Math.min(countArg(args, 1, op), MAX_RECV, MAX_DATAGRAM);
1451
+ const entry = this.datagramEntry(op, id);
1452
+ return async () => {
1453
+ const received = await this.nextDatagram(entry);
1454
+ if (received instanceof Error) throw netFailure(op, received);
1455
+ const answer = { data: bytesValue(received.data.subarray(0, want)), peer: received.peer };
1456
+ return () => answer;
1457
+ };
1458
+ }
1459
+ async dnsResolve(op, args) {
1460
+ const name = hostArg(args, 0, op);
1461
+ let family;
1462
+ if (argOpt(args, 1) !== void 0) {
1463
+ family = argI64(args, 1, op);
1464
+ if (family !== 4 && family !== 6) throw invalid(op, "the family must be 4 or 6");
1465
+ }
1466
+ if (!this.grants.allows({ need: "net", host: name, port: 0, kinds: ["tcp", "udp"] })) {
1467
+ throw denied(op, `${name}:0 is not granted`);
1468
+ }
1469
+ const seen = [];
1470
+ for (const entry of await this.resolve(op, name)) {
1471
+ const wanted = family === void 0 || family === entry.family;
1472
+ if (wanted && !seen.includes(entry.address)) seen.push(entry.address);
1473
+ }
1474
+ if (seen.length === 0) throw osFailure("not-found", op, `${name} has no address of that family`);
1475
+ return { addresses: seen };
1476
+ }
1477
+ shutdown(op, args) {
1478
+ const id = argI64(args, 0, op);
1479
+ const entry = this.streamEntry(op, id);
1480
+ if (entry.socket.destroyed) throw netFailure(op, errnoError("ENOTCONN"));
1481
+ entry.socket.end();
1482
+ entry.shut = true;
1483
+ return {};
1484
+ }
1485
+ };
1486
+ function peerOf(socket) {
1487
+ const host = socket.remoteAddress ?? "";
1488
+ const port = socket.remotePort ?? 0;
1489
+ return isIP(host) === 6 ? `[${host}]:${port}` : `${host}:${port}`;
1490
+ }
1491
+ var PROCESS_OPS = /* @__PURE__ */ new Set([
1492
+ "os/spawn",
1493
+ "os/wait",
1494
+ "os/kill",
1495
+ "os/pid",
1496
+ "os/ppid",
1497
+ "os/watch-process",
1498
+ "os/watch-signal",
1499
+ "os/getenv",
1500
+ "os/setenv",
1501
+ "os/unsetenv",
1502
+ "os/environ",
1503
+ "os/watch-env",
1504
+ "os/stdin-read",
1505
+ "os/stdout-write",
1506
+ "os/stderr-write",
1507
+ "os/isatty",
1508
+ "os/tty-size"
1509
+ ]);
1510
+ function isProcessOp(op) {
1511
+ return PROCESS_OPS.has(op);
1512
+ }
1513
+ var PIPE_HIGH_WATER = 1024 * 1024;
1514
+ var SIGNALS = constants.signals;
1515
+ var SIGNAL_SET = ["TERM", "KILL", "INT", "HUP", "QUIT", "USR1", "USR2", "STOP", "CONT"].map(
1516
+ (name) => [name, SIGNALS[`SIG${name}`]]
1517
+ );
1518
+ function validEnvName(name) {
1519
+ return name !== "" && !name.includes("=") && !name.includes("\0");
1520
+ }
1521
+ function isJsonRecord(value) {
1522
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1523
+ }
1524
+ var ProcessOps = class {
1525
+ constructor(state, grants) {
1526
+ this.state = state;
1527
+ this.grants = grants;
1528
+ }
1529
+ state;
1530
+ grants;
1531
+ lookupEnv(name) {
1532
+ if (this.state.env.has(name)) return this.state.env.get(name) ?? null;
1533
+ return process.env[name] ?? null;
1534
+ }
1535
+ envGranted(op, name) {
1536
+ if (!this.grants.allows({ need: "env", name })) throw denied(op, `environment ${name} is not granted`);
1537
+ }
1538
+ stdioMode(options, key, kind) {
1539
+ const op = "os/spawn";
1540
+ const given = options?.[key];
1541
+ let mode = "null";
1542
+ if (given === void 0 || given === null) {
1543
+ mode = "null";
1544
+ } else if (typeof given === "string") {
1545
+ if (given !== "inherit" && given !== "pipe" && given !== "null") {
1546
+ throw invalid(op, `${key} must be inherit, pipe or null, got ${given}`);
1547
+ }
1548
+ mode = given;
1549
+ } else {
1550
+ throw invalid(op, `${key} must be a string`);
1551
+ }
1552
+ if (mode === "inherit" && !this.grants.allows({ need: "stdio", kind })) {
1553
+ throw denied(op, `inheriting ${key} is not granted`);
1554
+ }
1555
+ return mode;
1556
+ }
1557
+ async run(op, args) {
1558
+ switch (op) {
1559
+ case "os/spawn":
1560
+ return this.spawn(args);
1561
+ case "os/wait":
1562
+ return this.wait(args);
1563
+ case "os/kill":
1564
+ return this.kill(args);
1565
+ case "os/pid":
1566
+ return { pid: process.pid };
1567
+ case "os/ppid":
1568
+ return { pid: process.ppid };
1569
+ case "os/getenv": {
1570
+ const name = argStr(args, 0, op);
1571
+ this.envGranted(op, name);
1572
+ return { value: this.lookupEnv(name) };
1573
+ }
1574
+ case "os/setenv":
1575
+ case "os/unsetenv":
1576
+ return this.setenv(op, args, op === "os/setenv");
1577
+ case "os/environ":
1578
+ return this.environ();
1579
+ case "os/stdout-write":
1580
+ return this.writeStdio(op, args, false);
1581
+ case "os/stderr-write":
1582
+ return this.writeStdio(op, args, true);
1583
+ case "os/isatty":
1584
+ return { ok: isatty(this.stdFd(op, args)) };
1585
+ case "os/tty-size":
1586
+ return this.ttySize(op, args);
1587
+ case "os/watch-process":
1588
+ case "os/watch-signal":
1589
+ case "os/watch-env":
1590
+ throw osFailure("unsupported", op, "streaming host not implemented");
1591
+ default:
1592
+ throw osFailure("unsupported", op, "the Node os host does not answer this operation yet (G-ORB-308)");
1593
+ }
1594
+ }
1595
+ async spawn(args) {
1596
+ const op = "os/spawn";
1597
+ const command = argStr(args, 0, op);
1598
+ const listed = args[1];
1599
+ if (!Array.isArray(listed) || !listed.every((item) => typeof item === "string")) {
1600
+ throw invalid(op, "argument 2 must be a list of strings");
1601
+ }
1602
+ const argv = listed.filter((item) => typeof item === "string");
1603
+ const given = argOpt(args, 2);
1604
+ if (given !== void 0 && !isJsonRecord(given)) throw invalid(op, "argument 3 must be an object");
1605
+ const options = given === void 0 ? void 0 : given;
1606
+ if (options !== void 0) {
1607
+ for (const key of Object.keys(options)) {
1608
+ if (!["cwd", "env", "stdin", "stdout", "stderr", "detached"].includes(key)) throw invalid(op, `unknown option ${key}`);
1609
+ }
1610
+ }
1611
+ if (!this.grants.allows({ need: "exec", command })) throw denied(op, `command ${command} is not granted`);
1612
+ const wantedCwd = options?.cwd;
1613
+ let cwd;
1614
+ if (wantedCwd === void 0 || wantedCwd === null) {
1615
+ cwd = void 0;
1616
+ } else if (typeof wantedCwd === "string") {
1617
+ const anchored = expandAnchor(this.state.anchors, wantedCwd);
1618
+ if (anchored.kind === "unknown") throw invalid(op, anchored.message);
1619
+ const dir = anchored.kind === "anchored" ? anchored.path : wantedCwd;
1620
+ if (!this.grants.allows({ need: "fs", path: dir, mode: "read" })) {
1621
+ throw denied(op, `cwd ${wantedCwd} is not granted for read`);
1622
+ }
1623
+ cwd = dir;
1624
+ } else {
1625
+ throw invalid(op, "cwd must be a string");
1626
+ }
1627
+ const stdinMode = this.stdioMode(options, "stdin", "in");
1628
+ const stdoutMode = this.stdioMode(options, "stdout", "out");
1629
+ const stderrMode = this.stdioMode(options, "stderr", "err");
1630
+ const wantedDetached = options?.detached;
1631
+ if (wantedDetached !== void 0 && wantedDetached !== null && typeof wantedDetached !== "boolean") {
1632
+ throw invalid(op, "detached must be a boolean");
1633
+ }
1634
+ const detached = wantedDetached === true;
1635
+ if (detached && [stdinMode, stdoutMode, stderrMode].includes("pipe")) throw invalid(op, "a detached child takes no piped stream");
1636
+ const vars = {};
1637
+ for (const [name, value] of this.state.env) {
1638
+ if (value !== null) vars[name] = value;
1639
+ }
1640
+ const wantedEnv = options?.env;
1641
+ if (wantedEnv === void 0 || wantedEnv === null) ; else if (isJsonRecord(wantedEnv)) {
1642
+ for (const [name, value] of Object.entries(wantedEnv)) {
1643
+ if (!validEnvName(name)) throw invalid(op, `bad environment name ${name}`);
1644
+ if (typeof value === "string" && !value.includes("\0")) {
1645
+ vars[name] = value;
1646
+ } else if (value === null) {
1647
+ this.envGranted(op, name);
1648
+ const found = this.lookupEnv(name);
1649
+ if (found !== null) vars[name] = found;
1650
+ else delete vars[name];
1651
+ } else {
1652
+ throw invalid(op, `environment ${name} must be a string or null`);
1653
+ }
1654
+ }
1655
+ } else {
1656
+ throw invalid(op, "env must be an object");
1657
+ }
1658
+ const modes = [stdinMode, stdoutMode, stderrMode].map((mode) => mode === "inherit" ? "inherit" : mode === "pipe" ? "pipe" : "ignore");
1659
+ let child;
1660
+ try {
1661
+ child = spawn(command, argv, { cwd, env: vars, stdio: [modes[0], modes[1], modes[2]], detached });
1662
+ } catch (raised) {
1663
+ const error = raised instanceof Error ? raised : new Error(String(raised));
1664
+ if (errnoCode(error) === "ERR_INVALID_ARG_VALUE") throw invalid(op, "nul byte found in provided data");
1665
+ throw processFailure(op, error);
1666
+ }
1667
+ const exit = new Promise((resolve2) => {
1668
+ child.once("exit", (code, signal) => resolve2({ code, signal: signal === null ? null : SIGNALS[signal] ?? null }));
1669
+ });
1670
+ const started = await new Promise((resolve2) => {
1671
+ child.once("spawn", () => resolve2(void 0));
1672
+ child.once("error", (error) => resolve2(error));
1673
+ });
1674
+ if (started !== void 0) throw processFailure(op, started);
1675
+ child.on("error", () => void 0);
1676
+ if (detached) child.unref();
1677
+ const pid = child.pid ?? 0;
1678
+ const entry = { child, exit, exited: false };
1679
+ void exit.then(() => {
1680
+ entry.exited = true;
1681
+ });
1682
+ const fields = { pid };
1683
+ if (child.stdin !== null) {
1684
+ const id = this.state.allocate();
1685
+ this.state.fds.set(id, { kind: "child-stdin", stream: child.stdin });
1686
+ fields.stdin = id;
1687
+ }
1688
+ if (child.stdout !== null) {
1689
+ const id = this.state.allocate();
1690
+ this.state.fds.set(id, { kind: "child-stdout", source: new ByteSource(child.stdout, PIPE_HIGH_WATER) });
1691
+ fields.stdout = id;
1692
+ }
1693
+ if (child.stderr !== null) {
1694
+ const id = this.state.allocate();
1695
+ this.state.fds.set(id, { kind: "child-stderr", source: new ByteSource(child.stderr, PIPE_HIGH_WATER) });
1696
+ fields.stderr = id;
1697
+ }
1698
+ this.state.children.set(pid, entry);
1699
+ return fields;
1700
+ }
1701
+ async wait(args) {
1702
+ return (await this.waitLater(args)())();
1703
+ }
1704
+ /** `os/wait` after its checks; a keyed ask under a route watches the child in the background (EXEC-QUEUE-10). */
1705
+ waitLater(args) {
1706
+ const op = "os/wait";
1707
+ const pid = argI64(args, 0, op);
1708
+ const entry = this.state.children.get(pid);
1709
+ if (entry === void 0) throw osFailure("closed", op, `pid ${pid} is not a spawned process`);
1710
+ return async () => {
1711
+ const info = await entry.exit;
1712
+ return () => {
1713
+ if (!this.state.children.delete(pid)) throw osFailure("closed", op, `pid ${pid} was already reaped`);
1714
+ const fields = {};
1715
+ if (info.code !== null) fields.code = info.code;
1716
+ if (info.signal !== null) fields.signal = info.signal;
1717
+ return fields;
1718
+ };
1719
+ };
1720
+ }
1721
+ signalNumber(value) {
1722
+ const op = "os/kill";
1723
+ let found;
1724
+ if (typeof value === "string") found = SIGNAL_SET.find(([name]) => name === value)?.[1];
1725
+ else if (typeof value === "number" && Number.isInteger(value)) found = SIGNAL_SET.find(([, number]) => number === value)?.[1];
1726
+ if (found === void 0) {
1727
+ throw invalid(op, "signal must be one of TERM, KILL, INT, HUP, QUIT, USR1, USR2, STOP, CONT (or its number)");
1728
+ }
1729
+ return found;
1730
+ }
1731
+ kill(args) {
1732
+ const op = "os/kill";
1733
+ const pid = argI64(args, 0, op);
1734
+ const signal = this.signalNumber(args.length > 1 ? args[1] : null);
1735
+ const entry = this.state.children.get(pid);
1736
+ if (entry === void 0) throw osFailure("closed", op, `pid ${pid} is not a spawned process`);
1737
+ if (pid < -2147483648 || pid > 2147483647) throw invalid(op, "pid out of range");
1738
+ if (entry.exited) return {};
1739
+ try {
1740
+ process.kill(pid, signal);
1741
+ } catch (raised) {
1742
+ throw processFailure(op, raised instanceof Error ? raised : new Error(String(raised)));
1743
+ }
1744
+ return {};
1745
+ }
1746
+ setenv(op, args, set) {
1747
+ const name = argStr(args, 0, op);
1748
+ this.envGranted(op, name);
1749
+ if (!validEnvName(name)) throw invalid(op, `bad environment name ${name}`);
1750
+ let entry = null;
1751
+ if (set) {
1752
+ const value = argStr(args, 1, op);
1753
+ if (value.includes("\0")) throw invalid(op, "value contains a NUL byte");
1754
+ entry = value;
1755
+ }
1756
+ this.state.env.set(name, entry);
1757
+ return {};
1758
+ }
1759
+ environ() {
1760
+ const names = new Set(Object.keys(process.env).filter((name) => this.grants.allows({ need: "env", name })));
1761
+ for (const [name, value] of this.state.env) {
1762
+ if (value !== null && this.grants.allows({ need: "env", name })) names.add(name);
1763
+ else if (value === null) names.delete(name);
1764
+ }
1765
+ return { names: [...names].sort((a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b))) };
1766
+ }
1767
+ async writeStdio(op, args, toStderr) {
1768
+ const data = argBytes(args, 0, op);
1769
+ if (!this.grants.allows({ need: "stdio", kind: toStderr ? "err" : "out" })) throw denied(op, "stdio is not granted");
1770
+ const stream = toStderr ? process.stderr : process.stdout;
1771
+ await new Promise((resolve2, reject) => {
1772
+ stream.write(data, (error) => error ? reject(error) : resolve2());
1773
+ }).catch((raised) => {
1774
+ throw processFailure(op, raised);
1775
+ });
1776
+ return { bytes: data.length };
1777
+ }
1778
+ stdFd(op, args) {
1779
+ const fd = argI64(args, 0, op);
1780
+ let kind;
1781
+ if (fd === 0) kind = "in";
1782
+ else if (fd === 1) kind = "out";
1783
+ else if (fd === 2) kind = "err";
1784
+ else throw osFailure("unsupported", op, "only fds 0, 1 and 2 are supported");
1785
+ if (!this.grants.allows({ need: "stdio", kind })) throw denied(op, "stdio is not granted");
1786
+ return fd;
1787
+ }
1788
+ ttySize(op, args) {
1789
+ const fd = this.stdFd(op, args);
1790
+ if (!isatty(fd)) throw osFailure("io", op, `fd ${fd} is not a terminal`);
1791
+ if (fd === 0) throw osFailure("unsupported", op, "the Node os host does not read the size of a terminal on fd 0 yet (G-ORB-308)");
1792
+ const stream = fd === 1 ? process.stdout : process.stderr;
1793
+ const [cols, rows] = stream.getWindowSize();
1794
+ return { rows, cols };
1795
+ }
1796
+ };
1797
+ var SYS_KIND = {
1798
+ "os/uid": "identity",
1799
+ "os/gid": "identity",
1800
+ "os/username": "identity",
1801
+ "os/group-of": "identity",
1802
+ "os/hostname": "identity",
1803
+ "os/uname": "identity",
1804
+ "os/cpu-usage": "cpu",
1805
+ "os/uptime": "cpu",
1806
+ "os/load-avg": "cpu",
1807
+ "os/memory-info": "memory",
1808
+ "os/disk-usage": "disk",
1809
+ "os/mounts": "disk",
1810
+ "os/disk-io": "disk",
1811
+ "os/network-interfaces": "network",
1812
+ "os/network-stats": "network",
1813
+ "os/network-connected": "network",
1814
+ "os/default-gateway": "network",
1815
+ "os/battery-status": "power-state",
1816
+ "os/ac-power": "power-state",
1817
+ "os/thermal-zones": "power-state"
1818
+ };
1819
+ var DESKTOP_KIND = {
1820
+ "os/notify": "notify",
1821
+ "os/speak": "speak",
1822
+ "os/brightness-get": "brightness",
1823
+ "os/brightness-set": "brightness",
1824
+ "os/volume-get": "volume",
1825
+ "os/volume-set": "volume",
1826
+ "os/volume-mute": "volume",
1827
+ "os/suspend": "power",
1828
+ "os/hibernate": "power"
1829
+ };
1830
+ function isSysOp(op) {
1831
+ return op === "os/clock-monotonic" || op === "os/watch-cron" || op in SYS_KIND || op in DESKTOP_KIND;
1832
+ }
1833
+ var ORIGIN = process.hrtime.bigint();
1834
+ var SysOps = class {
1835
+ constructor(grants) {
1836
+ this.grants = grants;
1837
+ }
1838
+ grants;
1839
+ run(op) {
1840
+ const sys = SYS_KIND[op];
1841
+ if (sys !== void 0 && !this.grants.allows({ need: "sys", kind: sys })) {
1842
+ throw denied(op, `the sys grant ${sys} is missing`);
1843
+ }
1844
+ const desktop = DESKTOP_KIND[op];
1845
+ if (desktop !== void 0 && !this.grants.allows({ need: "desktop", kind: desktop })) {
1846
+ throw denied(op, `the desktop grant ${desktop} is missing`);
1847
+ }
1848
+ switch (op) {
1849
+ case "os/clock-monotonic":
1850
+ return { nanos: Number(process.hrtime.bigint() - ORIGIN) };
1851
+ case "os/uid":
1852
+ return { uid: process.getuid?.() ?? -1 };
1853
+ case "os/gid":
1854
+ return { gid: process.getgid?.() ?? -1 };
1855
+ case "os/username":
1856
+ return { username: userInfo().username };
1857
+ case "os/hostname":
1858
+ return { hostname: hostname() };
1859
+ case "os/uname":
1860
+ return { sysname: type(), nodename: hostname(), release: release(), version: version(), machine: machine() };
1861
+ case "os/uptime":
1862
+ return { seconds: uptime() };
1863
+ case "os/load-avg": {
1864
+ const [one, five, fifteen] = loadavg();
1865
+ return { one, five, fifteen };
1866
+ }
1867
+ case "os/watch-cron":
1868
+ throw osFailure("unsupported", op, "streaming host not implemented");
1869
+ default:
1870
+ throw osFailure("unsupported", op, "the Node os host does not answer this operation yet (G-ORB-308)");
1871
+ }
1872
+ }
1873
+ };
1874
+ var log = createLogger("almadar:host:os");
1875
+ var DEVICE_OPS = /* @__PURE__ */ new Set(["os/mmio-read", "os/mmio-write", "os/irq-wait", "os/irq-ack"]);
1876
+ function failureOf(op, raised) {
1877
+ if (raised instanceof OsFailure) return raised.error;
1878
+ return { kind: "io", message: `${op}: ${raised instanceof Error ? raised.message : String(raised)}` };
1879
+ }
1880
+ var NodeOsHost = class {
1881
+ constructor(given, anchors) {
1882
+ this.given = given;
1883
+ this.anchors = anchors;
1884
+ this.state = new OsState(anchors);
1885
+ const grants = anchoredGrants(given, anchors);
1886
+ this.fs = new FsOps(this.state, grants);
1887
+ this.process = new ProcessOps(this.state, grants);
1888
+ this.net = new NetOps(this.state, grants);
1889
+ this.sys = new SysOps(grants);
1890
+ }
1891
+ given;
1892
+ anchors;
1893
+ state;
1894
+ fs;
1895
+ process;
1896
+ net;
1897
+ sys;
1898
+ placed = false;
1899
+ /** The listener handle of each open stream, by its key. */
1900
+ streams = /* @__PURE__ */ new Map();
1901
+ /** The call's arguments with every anchored part resolved by the kernel, the program's anchors placed on the first call. */
1902
+ resolved(op, args) {
1903
+ if (!this.placed) {
1904
+ for (const problem of this.given.anchor(this.anchors)) log.warn("anchor:grants-nothing", { at: problem.path, message: problem.message });
1905
+ this.placed = true;
1906
+ }
1907
+ const resolved = this.given.resolveArgs(op, args);
1908
+ if ("error" in resolved) throw new OsFailure(resolved.error);
1909
+ return resolved.ok;
1910
+ }
1911
+ async dispatch(op, given) {
1912
+ const args = this.resolved(op, given);
1913
+ if (op === "os/close") {
1914
+ if (this.fs.closeOwned(args) || this.net.closeOwned(args)) return {};
1915
+ throw osFailure("closed", op, "not an open handle");
1916
+ }
1917
+ if (isFsOp(op)) return this.fs.run(op, args);
1918
+ if (isProcessOp(op)) return this.process.run(op, args);
1919
+ if (isNetOp(op)) return this.net.run(op, args);
1920
+ if (isSysOp(op)) return this.sys.run(op);
1921
+ if (DEVICE_OPS.has(op)) throw osFailure("unsupported", op, "this host has no devices");
1922
+ throw osFailure("unsupported", op, "this host does not implement the operation yet");
1923
+ }
1924
+ async answer(op, args) {
1925
+ try {
1926
+ return { ok: await this.dispatch(op, args) };
1927
+ } catch (raised) {
1928
+ return { error: failureOf(op, raised) };
1929
+ }
1930
+ }
1931
+ deferral(op, args) {
1932
+ switch (op) {
1933
+ case "os/wait":
1934
+ return this.process.waitLater(args);
1935
+ case "os/read":
1936
+ return this.fs.readLater(op, args);
1937
+ case "os/send":
1938
+ return this.net.sendLater(op, args);
1939
+ case "os/recv":
1940
+ return this.net.recvLater(op, args);
1941
+ case "os/recvfrom":
1942
+ return this.net.recvfromLater(op, args);
1943
+ default:
1944
+ throw osFailure("unsupported", op, "this operation does not defer its completion");
1945
+ }
1946
+ }
1947
+ /** `Completions::run_later`: the checks fail in place, the wait runs in the background and its outcome goes to `deliver` unless the key is cancelled first. */
1948
+ defer(op, args, key, deliver) {
1949
+ let blocking;
1950
+ try {
1951
+ blocking = this.deferral(op, this.resolved(op, args));
1952
+ } catch (raised) {
1953
+ return { error: failureOf(op, raised) };
1954
+ }
1955
+ if (!this.state.completions.begin(key)) return { error: failureOf(op, invalid(op, `key ${key} already has a completion pending`)) };
1956
+ const finish = (build) => {
1957
+ if (!this.state.completions.claim(key)) return;
1958
+ try {
1959
+ deliver({ ok: build() });
1960
+ } catch (raised) {
1961
+ deliver({ error: failureOf(op, raised) });
1962
+ }
1963
+ };
1964
+ blocking().then(finish, (raised) => finish(() => {
1965
+ throw raised;
1966
+ }));
1967
+ return { ok: null };
1968
+ }
1969
+ /** A keyed stream (EXEC-QUEUE-12): `os/tcp-listen` binds and answers in place, then each connection reaches `message`. */
1970
+ async stream(op, args, key, message, fail2) {
1971
+ try {
1972
+ if (op !== "os/tcp-listen") throw osFailure("unsupported", op, "this host streams only os/tcp-listen");
1973
+ if (this.streams.has(key)) throw invalid(op, `key ${key} already has a stream open`);
1974
+ const answer = await this.net.listen(op, this.resolved(op, args), message, (error) => {
1975
+ this.streams.delete(key);
1976
+ fail2(error);
1977
+ });
1978
+ this.streams.set(key, answer.sock);
1979
+ return { ok: answer };
1980
+ } catch (raised) {
1981
+ return { error: failureOf(op, raised) };
1982
+ }
1983
+ }
1984
+ cancel(key) {
1985
+ const listener = this.streams.get(key);
1986
+ if (listener !== void 0) {
1987
+ this.streams.delete(key);
1988
+ this.net.stopListening(listener);
1989
+ return true;
1990
+ }
1991
+ return this.state.completions.cancel(key);
1992
+ }
1993
+ close() {
1994
+ this.state.close();
1995
+ }
1996
+ };
1997
+ function processAnchors() {
1998
+ const env = {};
1999
+ for (const [name, value] of Object.entries(process.env)) if (value !== void 0) env[name] = value;
2000
+ return { home: homedir(), start: process.cwd(), exeDir: dirname(process.execPath), env };
2001
+ }
2002
+ function createNodeOsHost(grants, options = {}) {
2003
+ return new NodeOsHost(grants, options.anchors ?? processAnchors());
2004
+ }
2005
+
2006
+ // src/kernel-program.ts
2007
+ function resolveKernelDir(from = process.cwd()) {
2008
+ const named = process.env.ALMADAR_KERNEL_DIR;
2009
+ if (named !== void 0 && named !== "") return named;
2010
+ const entry = createRequire(join(resolve(from), "resolve.js")).resolve("@almadar/kernel");
2011
+ return dirname(dirname(entry));
2012
+ }
2013
+ function emitPayload(run, event, read) {
2014
+ const found = run.emits.find((emit) => emit.event === event);
2015
+ return found === void 0 ? void 0 : read(found.payload ?? {});
2016
+ }
2017
+ async function resolveProgramFile(path, cwd = dirname(resolve(path)), orb = resolveOrbBinary(cwd)) {
2018
+ const absolute = resolve(path);
2019
+ return new Promise((done, fail2) => {
2020
+ const child = spawn(orb, ["resolve", absolute], { cwd, stdio: ["ignore", "pipe", "pipe"] });
2021
+ const out = [];
2022
+ const err = [];
2023
+ child.stdout.on("data", (chunk) => out.push(chunk));
2024
+ child.stderr.on("data", (chunk) => err.push(chunk));
2025
+ child.on("error", fail2);
2026
+ child.on("close", (code) => {
2027
+ if (code !== 0) {
2028
+ fail2(new Error(`orb resolve ${absolute} exited ${code}: ${Buffer.concat(err).toString("utf8")}`));
2029
+ return;
2030
+ }
2031
+ const text = Buffer.concat(out).toString("utf8");
2032
+ done(JSON.parse(text.slice(text.indexOf("{"))));
2033
+ });
2034
+ });
2035
+ }
2036
+ var KernelProgram = class _KernelProgram {
2037
+ constructor(engine, os, schema, http, persistence) {
2038
+ this.engine = engine;
2039
+ this.os = os;
2040
+ this.schema = schema;
2041
+ this.http = http;
2042
+ this.persistence = persistence;
2043
+ }
2044
+ engine;
2045
+ os;
2046
+ schema;
2047
+ http;
2048
+ persistence;
2049
+ static async open(schema, options) {
2050
+ const languageFrom = { languageFrom: options.languageFrom };
2051
+ const native = options.engine === "native";
2052
+ const addon = native ? await loadNativeKernelModule(options.nativeAddon ?? resolveNativeAddon(options.languageFrom), languageFrom) : void 0;
2053
+ const kernelDir = addon === void 0 ? options.kernelDir ?? resolveKernelDir() : "";
2054
+ const kernel = addon ?? await loadKernelModule(kernelDir, languageFrom);
2055
+ installNodeHostPorts();
2056
+ const http = createHttpPorts({
2057
+ kernel: addon !== void 0 ? async () => addon : () => loadHttpKernel(kernelDir, languageFrom),
2058
+ ...options.http,
2059
+ egress: schema.egress ?? []
2060
+ });
2061
+ const persistence = new NativeMemoryPersistenceAdapter(new kernel.MemoryStore());
2062
+ for (const [entityType, rows] of Object.entries(options.seed ?? {})) {
2063
+ for (const row of rows) await persistence.writeWhere({ kind: "upsert", entityType, data: row });
2064
+ }
2065
+ let engine;
2066
+ const requireEngine = () => {
2067
+ if (engine === void 0) throw new Error("KernelProgram: an os ask arrived before the program opened");
2068
+ return engine;
2069
+ };
2070
+ const os = options.os === true ? createNodeOsHost(kernelOsGrants(kernel, requireEngine)) : void 0;
2071
+ const ports = {
2072
+ persistence,
2073
+ callService: async () => null,
2074
+ programEffect: () => void 0,
2075
+ inFlightCalls: new InFlightCalls(),
2076
+ servicePorts: (caller, _user, message) => ({ caller, message, inputs: () => [], readableEntities: () => [], dispatchInput: async () => {
2077
+ throw new Error("KernelProgram has no input channel");
2078
+ }, read: async () => [] }),
2079
+ deliverLive: () => void 0,
2080
+ deferCallOutcome: () => void 0,
2081
+ http,
2082
+ ...os !== void 0 ? { os } : {}
2083
+ };
2084
+ engine = KernelEngine.open(kernel, schema, ports);
2085
+ return new _KernelProgram(engine, os, schema, http, persistence);
2086
+ }
2087
+ /** Fire `event` at the orbital as `user` (the program's `@user`), wait for its whole cascade (every `http` ask included), and return what it emitted. */
2088
+ async dispatch(orbital, event, payload = {}, targetTrait, user) {
2089
+ const response = await this.engine.evaluate(
2090
+ orbital,
2091
+ { event, payload, ...targetTrait !== void 0 ? { targetTrait } : {}, ...user !== void 0 ? { user } : {} },
2092
+ { orbital, ...user !== void 0 ? { user } : {} }
2093
+ );
2094
+ return {
2095
+ success: response.success,
2096
+ emits: response.emittedEvents,
2097
+ rejections: response.rejections ?? [],
2098
+ ...response.error !== void 0 ? { error: response.error } : {}
2099
+ };
2100
+ }
2101
+ /** Fire `trait`'s tick `name` once, as a host timer would; the kernel evaluates the tick's guard and effects. */
2102
+ async tick(orbital, trait, name) {
2103
+ const response = await this.engine.evaluate(orbital, { event: tickEventKey(name), runTick: { trait, tick: name }, targetTrait: trait }, { orbital });
2104
+ return {
2105
+ success: response.success,
2106
+ emits: response.emittedEvents,
2107
+ rejections: response.rejections ?? [],
2108
+ ...response.error !== void 0 ? { error: response.error } : {}
2109
+ };
2110
+ }
2111
+ /** Deliver an `/api/hooks/<name>` request to the program and return the answer it gave (EXEC-INBOUND-1). */
2112
+ async deliverInbound(name, delivery) {
2113
+ return dispatchInbound(this.schema, name, delivery, (orbital, request) => this.engine.evaluate(orbital, request, { orbital }), (credential) => this.http.secrets.get(credential, void 0));
2114
+ }
2115
+ /** The rows the host's in-memory store holds for `entity` (empty when the native kernel keeps its own `store`). */
2116
+ async rows(entity) {
2117
+ return this.persistence.list(entity);
2118
+ }
2119
+ close() {
2120
+ this.os?.close();
2121
+ this.engine.dispose();
2122
+ }
2123
+ };
2124
+
2125
+ // src/program/kernel-workspace-host.ts
2126
+ var GCS_ENDPOINT = "https://storage.googleapis.com";
2127
+ var WORKSPACE_HMAC_KEY_ID = "ALMADAR_WORKSPACE_HMAC_KEY_ID";
2128
+ var WORKSPACE_HMAC_SECRET = "ALMADAR_WORKSPACE_HMAC_SECRET";
2129
+ function workspaceBucketFromEnv(env) {
2130
+ const name = env.ALMADAR_WORKSPACE_BUCKET;
2131
+ if (name === void 0 || name === "") return void 0;
2132
+ return { name, endpoint: GCS_ENDPOINT, region: "auto", accessKeyId: WORKSPACE_HMAC_KEY_ID, secretAccessKey: WORKSPACE_HMAC_SECRET };
2133
+ }
2134
+ var ORBITAL = "Workspaces";
2135
+ var TRAIT = `${ORBITAL}WorkspaceClient`;
2136
+ function workspacesRoot(projectRoot) {
2137
+ return join(projectRoot, "workspaces");
2138
+ }
2139
+ function programText(root, bucket) {
2140
+ const egress = bucket === void 0 ? "" : `
2141
+ egress: [${JSON.stringify(bucket.endpoint)}],`;
2142
+ return `app kernel-workspace-host [
2143
+ version: "1.0.0",
2144
+ description: "The program host's workspaces, run by std-workspace.",${egress}
2145
+ fs: { ${JSON.stringify(`${root}/**`)}: [read, write, create, remove] },
2146
+ exec: ["git"]
2147
+ ]
2148
+ uses Ws from "std/behaviors/std-workspace"
2149
+ orbital ${ORBITAL} = Ws.orbitals.WorkspaceOrbital {${bucket === void 0 ? `
2150
+ traits { WorkspaceClient { config { root: ${JSON.stringify(root)} } } }` : `
2151
+ traits {
2152
+ WorkspaceClient { config { root: ${JSON.stringify(root)}, store: bucket, bucket: ${JSON.stringify(bucket.name)} } }
2153
+ WorkspaceStore { config { endpoint: ${JSON.stringify(bucket.endpoint)}, region: ${JSON.stringify(bucket.region)}, bucket: ${JSON.stringify(bucket.name)}, accessKeyId: ${JSON.stringify(bucket.accessKeyId)}, secretAccessKey: ${JSON.stringify(bucket.secretAccessKey)} } }
2154
+ }`}
2155
+ }
2156
+ `;
2157
+ }
2158
+ function failure(run, failed, what) {
2159
+ const detail = emitPayload(run, failed, (p) => typeof p.message === "string" ? p.message : "");
2160
+ return new Error(`workspace ${what} failed: ${detail ?? run.error ?? "no answer"}`);
2161
+ }
2162
+ function snapshots(payload) {
2163
+ const rows = payload.snapshots;
2164
+ if (!Array.isArray(rows)) return [];
2165
+ return rows.flatMap(
2166
+ (row) => typeof row === "object" && row !== null && !Array.isArray(row) && typeof row.id === "string" && typeof row.timestamp === "number" && typeof row.reason === "string" ? [{ id: row.id, timestamp: row.timestamp, reason: row.reason }] : []
2167
+ );
2168
+ }
2169
+ function kernelWorkspaceHost(options) {
2170
+ const root = workspacesRoot(options.projectRoot);
2171
+ let program;
2172
+ let tail = Promise.resolve();
2173
+ const open2 = () => {
2174
+ program ??= (async () => {
2175
+ mkdirSync(root, { recursive: true });
2176
+ const granted = realpathSync(root);
2177
+ const dir = mkdtempSync(join(tmpdir(), "almadar-workspace-host-"));
2178
+ try {
2179
+ const file = join(dir, "workspace-host.lolo");
2180
+ writeFileSync(file, programText(granted, options.bucket));
2181
+ const schema = await resolveProgramFile(file, options.projectRoot, options.orb);
2182
+ return await KernelProgram.open(schema, { ...options.kernel, languageFrom: options.projectRoot, os: true });
2183
+ } finally {
2184
+ rmSync(dir, { recursive: true, force: true });
2185
+ }
2186
+ })();
2187
+ return program;
2188
+ };
2189
+ const run = (principal, event, payload, read) => {
2190
+ const next = tail.then(async () => read(await (await open2()).dispatch(ORBITAL, event, payload, TRAIT, { id: principal })));
2191
+ tail = next.catch(() => void 0);
2192
+ return next;
2193
+ };
2194
+ return {
2195
+ root,
2196
+ async open(workspace, context) {
2197
+ const principal = context.principal;
2198
+ if (principal === void 0 || principal === "") throw new Error("A workspace needs a signed-in caller");
2199
+ return {
2200
+ workDir: join(root, workspaceOwnerSegment(principal), workspaceOwnerSegment(workspace)),
2201
+ writeFile: (path, content) => run(principal, "WORKSPACE_WRITE_FILE", { workspace, path, content }, (r) => {
2202
+ if (emitPayload(r, "WORKSPACE_FILE_WRITTEN", (p) => p.written) !== true) throw failure(r, "WORKSPACE_WRITE_FAILED", `write of ${path}`);
2203
+ }),
2204
+ snapshot: (reason) => run(principal, "WORKSPACE_SNAPSHOT", { workspace, reason }, (r) => {
2205
+ const taken = emitPayload(r, "WORKSPACE_SNAPSHOT_TAKEN", (p) => ({ committed: p.committed === true, oid: typeof p.oid === "string" ? p.oid : "" }));
2206
+ if (taken === void 0) throw failure(r, "WORKSPACE_SNAPSHOT_FAILED", "snapshot");
2207
+ return taken.committed ? { oid: taken.oid } : null;
2208
+ }),
2209
+ listSnapshots: (opts) => run(principal, "WORKSPACE_HISTORY", { workspace, ...opts?.limit !== void 0 ? { limit: opts.limit } : {} }, (r) => {
2210
+ const found = emitPayload(r, "WORKSPACE_HISTORY_LOADED", snapshots);
2211
+ if (found === void 0) throw failure(r, "WORKSPACE_HISTORY_FAILED", "history");
2212
+ return found;
2213
+ })
2214
+ };
2215
+ }
2216
+ };
2217
+ }
2218
+
2219
+ export { GCS_ENDPOINT, KernelProgram, WORKSPACE_HMAC_KEY_ID, WORKSPACE_HMAC_SECRET, createNodeOsHost, emitPayload, kernelWorkspaceHost, processAnchors, resolveKernelDir, resolveProgramFile, workspaceBucketFromEnv, workspacesRoot };