@ai-ecoverse/slicc-kernel 1.73.3 → 1.75.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.
package/dist/node.js CHANGED
@@ -736,9 +736,11 @@ var KernelPipe = class {
736
736
  // src/kernel/fd-table.ts
737
737
  var KernelError = class extends Error {
738
738
  code;
739
- constructor(code) {
739
+ detail;
740
+ constructor(code, detail) {
740
741
  super(code);
741
742
  this.code = code;
743
+ if (detail !== void 0) this.detail = detail;
742
744
  }
743
745
  };
744
746
  function pollFile(file) {
@@ -881,6 +883,7 @@ function kernelFdKind(file) {
881
883
  if (file.heldMeta && "device" in file.heldMeta) return "device";
882
884
  if (file.tty) return "tty";
883
885
  if (file.sealed) return "sealed";
886
+ if (file.clipboard) return "clipboard";
884
887
  return file.seek ? "file" : "stream";
885
888
  }
886
889
  var FdTable = class _FdTable {
@@ -6070,6 +6073,173 @@ var ChildTable = class {
6070
6073
  }
6071
6074
  };
6072
6075
 
6076
+ // src/kernel/clipboard.ts
6077
+ var CLIPBOARD_MAX_BYTES = 1024 * 1024;
6078
+ var CLIPBOARD_TIMEOUT_MS = 1e4;
6079
+ var encoder = new TextEncoder();
6080
+ var decoder = new TextDecoder("utf-8", { fatal: true });
6081
+ var READY = { readable: true, writable: true, hangup: false };
6082
+ function lineOf(err) {
6083
+ const text = err instanceof Error ? err.message : String(err);
6084
+ return (text.split("\n")[0] ?? "").slice(0, 200);
6085
+ }
6086
+ function clipboardFailure(err) {
6087
+ if (err instanceof KernelError) return err;
6088
+ const name2 = err?.name;
6089
+ return new KernelError(name2 === "NotAllowedError" ? "EACCES" : "EIO", lineOf(err));
6090
+ }
6091
+ function joined2(chunks2, size) {
6092
+ if (chunks2.length === 1) return chunks2[0];
6093
+ const out = new Uint8Array(size);
6094
+ let at = 0;
6095
+ for (const chunk2 of chunks2) {
6096
+ out.set(chunk2, at);
6097
+ at += chunk2.length;
6098
+ }
6099
+ return out;
6100
+ }
6101
+ async function interruptible(work, signal) {
6102
+ if (!signal) return work;
6103
+ if (signal.aborted) throw new KernelError("EINTR");
6104
+ let stop = () => {
6105
+ };
6106
+ const interrupted2 = new Promise((_, reject) => {
6107
+ stop = () => reject(new KernelError("EINTR"));
6108
+ signal.addEventListener("abort", stop, { once: true });
6109
+ });
6110
+ try {
6111
+ return await Promise.race([work, interrupted2]);
6112
+ } finally {
6113
+ signal.removeEventListener("abort", stop);
6114
+ }
6115
+ }
6116
+ var ClipboardChannel = class {
6117
+ stamp;
6118
+ access;
6119
+ broker;
6120
+ snapshot;
6121
+ taken;
6122
+ offset = 0;
6123
+ chunks = [];
6124
+ size = 0;
6125
+ wrote = false;
6126
+ failed = false;
6127
+ constructor(broker, access2, stamp) {
6128
+ this.broker = broker;
6129
+ this.access = access2;
6130
+ this.stamp = stamp;
6131
+ }
6132
+ get unread() {
6133
+ return this.taken === void 0 && this.snapshot === void 0;
6134
+ }
6135
+ get settled() {
6136
+ return this.taken !== void 0;
6137
+ }
6138
+ async read(max, signal) {
6139
+ if (this.access === "write") throw new KernelError("EBADF");
6140
+ if (!this.taken) {
6141
+ if (!this.snapshot) {
6142
+ const stamp = this.stamp;
6143
+ const asked = this.broker.ask(async (hook) => {
6144
+ const text = await hook.read(stamp);
6145
+ if (typeof text !== "string") throw new KernelError("EIO", "the clipboard gave no text");
6146
+ return encoder.encode(text);
6147
+ });
6148
+ this.snapshot = asked;
6149
+ asked.then(
6150
+ (bytes) => {
6151
+ if (this.snapshot === asked) this.taken = bytes;
6152
+ },
6153
+ () => {
6154
+ if (this.snapshot === asked) this.snapshot = void 0;
6155
+ }
6156
+ );
6157
+ }
6158
+ this.taken = await interruptible(this.snapshot, signal);
6159
+ }
6160
+ const out = this.taken.subarray(this.offset, this.offset + max);
6161
+ this.offset += out.length;
6162
+ return out;
6163
+ }
6164
+ write(bytes) {
6165
+ if (this.access === "read") throw new KernelError("EBADF");
6166
+ this.wrote = true;
6167
+ if (this.failed) throw new KernelError("EFBIG");
6168
+ if (this.size + bytes.length > CLIPBOARD_MAX_BYTES) {
6169
+ this.failed = true;
6170
+ this.chunks = [];
6171
+ this.size = 0;
6172
+ throw new KernelError("EFBIG");
6173
+ }
6174
+ if (bytes.length > 0) this.chunks.push(bytes.slice());
6175
+ this.size += bytes.length;
6176
+ return bytes.length;
6177
+ }
6178
+ poll() {
6179
+ return READY;
6180
+ }
6181
+ async close() {
6182
+ this.snapshot = void 0;
6183
+ this.taken = void 0;
6184
+ const commits = this.access === "write" || this.access === "both" && this.wrote;
6185
+ if (!commits || this.failed) return;
6186
+ const bytes = joined2(this.chunks, this.size);
6187
+ this.chunks = [];
6188
+ this.size = 0;
6189
+ let text;
6190
+ try {
6191
+ text = decoder.decode(bytes);
6192
+ } catch {
6193
+ throw new KernelError("EILSEQ", "the text is not valid UTF-8");
6194
+ }
6195
+ const stamp = this.stamp;
6196
+ await this.broker.ask(async (hook) => {
6197
+ await hook.write(text, stamp);
6198
+ });
6199
+ }
6200
+ };
6201
+ var ClipboardBroker = class {
6202
+ hook;
6203
+ users;
6204
+ timeoutMs;
6205
+ tagOf;
6206
+ constructor(hook, users, timeoutMs = CLIPBOARD_TIMEOUT_MS, tagOf = () => void 0) {
6207
+ this.hook = hook;
6208
+ this.users = users;
6209
+ this.timeoutMs = timeoutMs;
6210
+ this.tagOf = tagOf;
6211
+ }
6212
+ async ask(work) {
6213
+ const hook = this.hook;
6214
+ if (!hook) throw new KernelError("ENOTSUP", "no clipboard in this embedder");
6215
+ let timer;
6216
+ const late = new Promise((_, reject) => {
6217
+ timer = setTimeout(
6218
+ () => reject(new KernelError("EIO", "clipboard timed out")),
6219
+ this.timeoutMs
6220
+ );
6221
+ });
6222
+ const asked = Promise.resolve().then(() => work(hook)).catch((err) => {
6223
+ throw clipboardFailure(err);
6224
+ });
6225
+ try {
6226
+ return await Promise.race([asked, late]);
6227
+ } finally {
6228
+ clearTimeout(timer);
6229
+ }
6230
+ }
6231
+ open(access2, stamp) {
6232
+ const channel = new ClipboardChannel(this, access2, stamp);
6233
+ return new OpenFile({
6234
+ read: (max, signal) => channel.read(max, signal),
6235
+ write: async (bytes) => channel.write(bytes),
6236
+ poll: () => channel.poll(),
6237
+ close: () => channel.close(),
6238
+ clipboard: channel
6239
+ });
6240
+ }
6241
+ };
6242
+
6073
6243
  // src/mount/protocol.ts
6074
6244
  var DRIVER_PROTOCOL = [1, 2];
6075
6245
  function driverVersionError(theirs) {
@@ -7607,20 +7777,20 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
7607
7777
  var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
7608
7778
  var SYNC_SAB_REQ_MSG = "sync-sab-req";
7609
7779
  var SYNC_SAB_NEXT_MSG = "sync-sab-next";
7610
- var encoder = new TextEncoder();
7611
- var decoder = new TextDecoder();
7780
+ var encoder2 = new TextEncoder();
7781
+ var decoder2 = new TextDecoder();
7612
7782
  function encodeSabResult(result) {
7613
7783
  if (!result.ok) {
7614
7784
  return {
7615
7785
  status: SAB_STATUS_ERRNO,
7616
- payload: encoder.encode(JSON.stringify({ errno: result.errno, message: result.message }))
7786
+ payload: encoder2.encode(JSON.stringify({ errno: result.errno, message: result.message }))
7617
7787
  };
7618
7788
  }
7619
7789
  switch (result.kind) {
7620
7790
  case "bytes":
7621
7791
  return { status: SAB_STATUS_BYTES, payload: result.bytes };
7622
7792
  case "json":
7623
- return { status: SAB_STATUS_JSON, payload: encoder.encode(JSON.stringify(result.json)) };
7793
+ return { status: SAB_STATUS_JSON, payload: encoder2.encode(JSON.stringify(result.json)) };
7624
7794
  default:
7625
7795
  return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
7626
7796
  }
@@ -10442,7 +10612,7 @@ function defaultTermios() {
10442
10612
  c_cc: cCc
10443
10613
  };
10444
10614
  }
10445
- var encoder2 = new TextEncoder();
10615
+ var encoder3 = new TextEncoder();
10446
10616
  function isContinuation(byte) {
10447
10617
  return (byte & 192) === 128;
10448
10618
  }
@@ -10564,7 +10734,7 @@ var KernelTty = class {
10564
10734
  this.line.push(byte);
10565
10735
  this.readable.push(Uint8Array.from(this.line));
10566
10736
  this.line = [];
10567
- if (echo) this.output(encoder2.encode("\n"));
10737
+ if (echo) this.output(encoder3.encode("\n"));
10568
10738
  } else {
10569
10739
  this.line.push(byte);
10570
10740
  if (echo) this.echo(byte);
@@ -10589,7 +10759,7 @@ var KernelTty = class {
10589
10759
  const utf82 = (this.termios.c_iflag & IUTF8) !== 0;
10590
10760
  const cells = utf82 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
10591
10761
  if (cells > 0 && this.termios.c_lflag & ECHO) {
10592
- this.output(encoder2.encode("\b \b".repeat(cells)), false);
10762
+ this.output(encoder3.encode("\b \b".repeat(cells)), false);
10593
10763
  }
10594
10764
  }
10595
10765
  echo(byte) {
@@ -10939,7 +11109,14 @@ async function runChild(req, deps) {
10939
11109
  ];
10940
11110
  try {
10941
11111
  pid = await deps.children.spawn(
10942
- { file: req.file, argv: req.argv, env: req.env, cwd: req.cwd },
11112
+ {
11113
+ file: req.file,
11114
+ argv: req.argv,
11115
+ env: req.env,
11116
+ cwd: req.cwd,
11117
+ ...req.pgid !== void 0 ? { pgid: req.pgid } : {},
11118
+ ...req.session ? { session: req.session } : {}
11119
+ },
10943
11120
  stdio
10944
11121
  );
10945
11122
  } catch (e) {
@@ -11071,7 +11248,7 @@ async function socketSyscall(req, proc) {
11071
11248
  dialing?.catch(() => void 0);
11072
11249
  throw new KernelError("EINPROGRESS");
11073
11250
  }
11074
- if (dialing !== void 0) await interruptible(dialing, proc.blocking());
11251
+ if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
11075
11252
  return ok();
11076
11253
  }
11077
11254
  case "sock-shutdown":
@@ -11095,7 +11272,7 @@ async function socketSyscall(req, proc) {
11095
11272
  }
11096
11273
  }
11097
11274
  }
11098
- function interruptible(dialing, signal) {
11275
+ function interruptible2(dialing, signal) {
11099
11276
  return new Promise((resolve, reject) => {
11100
11277
  const abort = () => reject(new KernelError("EINTR"));
11101
11278
  signal.addEventListener("abort", abort, { once: true });
@@ -11762,6 +11939,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
11762
11939
  "fd-tty-names",
11763
11940
  "fd-tty-owner",
11764
11941
  "fd-open-sealed",
11942
+ "fd-open-clipboard",
11765
11943
  "tty-get",
11766
11944
  "tty-set",
11767
11945
  "tty-winsz",
@@ -11892,8 +12070,9 @@ var WasmProcess = class {
11892
12070
  return "deliver";
11893
12071
  }
11894
12072
  async spawn(req) {
11895
- const { file, argv, env, cwd, stdio, inherit: inherit2, exec } = req;
11896
- const spawned = { file, argv, env, cwd, ...exec ? { exec } : {} };
12073
+ const { file, argv, env, cwd, stdio, inherit: inherit2, exec, pgid, session } = req;
12074
+ const group = { ...pgid !== void 0 ? { pgid } : {}, ...session ? { session } : {} };
12075
+ const spawned = { file, argv, env, cwd, ...group, ...exec ? { exec } : {} };
11897
12076
  const pid = await this.children.spawn(spawned, stdio, inherit2);
11898
12077
  if (!exec) return pid;
11899
12078
  if (this.killed) void Promise.allSettled([this.options.kill?.(pid, SIG.KILL)]);
@@ -12003,7 +12182,7 @@ var WasmProcess = class {
12003
12182
  return await this.procSyscall(req, cancelled);
12004
12183
  } catch (e) {
12005
12184
  if (e instanceof KernelError || e instanceof SpawnError) {
12006
- return { ok: false, errno: e.code, message: e.code };
12185
+ return { ok: false, errno: e.code, message: e.detail ?? e.code };
12007
12186
  }
12008
12187
  const code = e?.code;
12009
12188
  if (typeof code === "string" && /^E[A-Z]+$/.test(code)) {
@@ -12046,6 +12225,7 @@ var WasmProcess = class {
12046
12225
  const read = req.peek ? file.peek : file.read;
12047
12226
  if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
12048
12227
  if (file.tty) this.checkForeground(file.tty);
12228
+ if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
12049
12229
  if (req.max <= 0) return new Uint8Array(0);
12050
12230
  if (file instanceof KernelSocket && file.unconnected) throw new KernelError("ENOTCONN");
12051
12231
  const ready2 = pollFile(file).readable;
@@ -12053,6 +12233,13 @@ var WasmProcess = class {
12053
12233
  const signal = ready2 ? this.interrupt.signal : this.blockingSignal(cancelled);
12054
12234
  return read.call(file, Math.max(0, Math.min(req.max, MAX_READ)), signal);
12055
12235
  }
12236
+ async readClipboard(channel, req, cancelled) {
12237
+ if (channel.access === "write") throw new KernelError("EBADF");
12238
+ if (req.max <= 0) return new Uint8Array(0);
12239
+ if (channel.unread) channel.stamp = await this.clipboardStamp();
12240
+ const signal = channel.settled ? this.interrupt.signal : this.blockingSignal(cancelled);
12241
+ return channel.read(Math.min(req.max, MAX_READ), signal);
12242
+ }
12056
12243
  async write(fd, body, nonblock = false, cancelled) {
12057
12244
  const file = this.fds.get(fd).file;
12058
12245
  if (file.sealed) return file.sealed.send(body, await this.sealedCaller(), this.gone.signal);
@@ -12156,6 +12343,36 @@ var WasmProcess = class {
12156
12343
  if (!broker) throw new KernelError("ENOENT");
12157
12344
  return this.fds.install(broker.open(), 3);
12158
12345
  }
12346
+ async openClipboard(flags2) {
12347
+ const broker = this.options.clipboard;
12348
+ if (!broker) throw new KernelError("ENOENT");
12349
+ const mode = flags2 & WRITE_ACCESS;
12350
+ const access2 = mode === 0 ? "read" : mode === 1 ? "write" : "both";
12351
+ return this.fds.install(broker.open(access2, await this.clipboardStamp()), 3);
12352
+ }
12353
+ async clipboardStamp() {
12354
+ const shown = await this.options.identity?.();
12355
+ const broker = this.options.clipboard;
12356
+ const tag = broker.tagOf(this.pid);
12357
+ return {
12358
+ pid: shown?.pid ?? this.pid,
12359
+ uid: this.cred.euid,
12360
+ gid: this.cred.egid,
12361
+ users: broker.users,
12362
+ ...tag !== void 0 ? { tag } : {}
12363
+ };
12364
+ }
12365
+ async stampClipboards() {
12366
+ if (!this.options.clipboard) return;
12367
+ const open = this.fds.numbers().filter((fd) => this.fds.get(fd).file.clipboard);
12368
+ if (open.length === 0) return;
12369
+ const stamp = await this.clipboardStamp().catch(() => void 0);
12370
+ if (!stamp) return;
12371
+ for (const fd of open) {
12372
+ const channel = this.fds.get(fd).file.clipboard;
12373
+ if (channel) channel.stamp = stamp;
12374
+ }
12375
+ }
12159
12376
  async sealedCaller() {
12160
12377
  const shown = await this.options.identity?.();
12161
12378
  const broker = this.options.sealed;
@@ -12230,10 +12447,13 @@ var WasmProcess = class {
12230
12447
  kind: "json",
12231
12448
  json: await this.write(req.fd, req.body, req.nonblock, cancelled)
12232
12449
  };
12233
- case "fd-close":
12450
+ case "fd-close": {
12234
12451
  this.options.locks?.closed(this.pid, req.fd);
12452
+ const channel = this.fds.get(req.fd).file.clipboard;
12453
+ if (channel) channel.stamp = await this.clipboardStamp();
12235
12454
  await Promise.resolve(this.fds.close(req.fd));
12236
12455
  return { ok: true, kind: "void" };
12456
+ }
12237
12457
  case "fd-pipe": {
12238
12458
  const pipe = openPipe();
12239
12459
  const read = this.fds.install(pipe.read, 3);
@@ -12291,6 +12511,8 @@ var WasmProcess = class {
12291
12511
  return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
12292
12512
  case "fd-open-sealed":
12293
12513
  return { ok: true, kind: "json", json: this.openSealed() };
12514
+ case "fd-open-clipboard":
12515
+ return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
12294
12516
  case "fd-tty-owner":
12295
12517
  return { ok: true, kind: "json", json: this.terminalByName(req.name).owner ?? null };
12296
12518
  case "fd-select": {
@@ -12742,6 +12964,7 @@ var WasmProcess = class {
12742
12964
  this.options.locks?.release(this.pid);
12743
12965
  this.asyncOps.close();
12744
12966
  for (const pid of this.children.pids()) this.options.onReap?.(pid);
12967
+ await this.stampClipboards();
12745
12968
  await this.fds.closeAll();
12746
12969
  if (!this.options.nodes) this.options.openFiles?.delete(this.nodes);
12747
12970
  await this.options.http?.closeAll();
@@ -12924,6 +13147,7 @@ function processOptions(opts, header4, hooks, created3) {
12924
13147
  ...inherited(opts),
12925
13148
  ...opts.owners ? { owners: true } : {},
12926
13149
  ...opts.sealed ? { sealed: opts.sealed } : {},
13150
+ ...opts.clipboard ? { clipboard: opts.clipboard } : {},
12927
13151
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
12928
13152
  ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
12929
13153
  ...opts.identity ? { identity: opts.identity } : {},
@@ -13187,6 +13411,9 @@ var JobTable = class {
13187
13411
  member.pgid = pgid;
13188
13412
  member.sid = sid;
13189
13413
  }
13414
+ lead(pid) {
13415
+ this.member(pid).pgid = this.idOf(pid);
13416
+ }
13190
13417
  pgidOf(pid) {
13191
13418
  return this.members.get(pid)?.pgid;
13192
13419
  }
@@ -13425,14 +13652,14 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
13425
13652
  "cancel",
13426
13653
  "materialize"
13427
13654
  ]);
13428
- var encoder3 = new TextEncoder();
13429
- var decoder2 = new TextDecoder("utf-8", { fatal: true });
13655
+ var encoder4 = new TextEncoder();
13656
+ var decoder3 = new TextDecoder("utf-8", { fatal: true });
13430
13657
  function parseSealed(bytes) {
13431
13658
  try {
13432
13659
  if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
13433
13660
  let parsed2;
13434
13661
  try {
13435
- parsed2 = JSON.parse(decoder2.decode(bytes));
13662
+ parsed2 = JSON.parse(decoder3.decode(bytes));
13436
13663
  } catch {
13437
13664
  throw new KernelError("EINVAL");
13438
13665
  }
@@ -13458,13 +13685,13 @@ function serialized(reply) {
13458
13685
  const text = JSON.stringify(reply);
13459
13686
  plain = text === void 0 ? void 0 : JSON.parse(text);
13460
13687
  } catch (err) {
13461
- return JSON.stringify(errorReply("EIO", lineOf(err)));
13688
+ return JSON.stringify(errorReply("EIO", lineOf2(err)));
13462
13689
  }
13463
13690
  if (!isRecord(plain)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
13464
13691
  const { v: _, ...rest2 } = plain;
13465
13692
  return JSON.stringify({ v: 1, ...rest2 });
13466
13693
  }
13467
- function lineOf(err) {
13694
+ function lineOf2(err) {
13468
13695
  const text = err instanceof Error ? err.message : String(err);
13469
13696
  return (text.split("\n")[0] ?? "").slice(0, 200);
13470
13697
  }
@@ -13494,7 +13721,7 @@ var SealedChannel = class {
13494
13721
  gone.removeEventListener("abort", withdraw);
13495
13722
  if (this.inflight !== flight) return;
13496
13723
  this.inflight = void 0;
13497
- if (reply) this.reply = encoder3.encode(serialized(reply));
13724
+ if (reply) this.reply = encoder4.encode(serialized(reply));
13498
13725
  });
13499
13726
  this.inflight = flight;
13500
13727
  return length2;
@@ -13508,7 +13735,7 @@ var SealedChannel = class {
13508
13735
  if (signal.aborted) return void 0;
13509
13736
  const asked = Promise.resolve().then(() => hook(req, { signal })).then(
13510
13737
  (reply2) => isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
13511
- (err) => errorReply("EIO", lineOf(err))
13738
+ (err) => errorReply("EIO", lineOf2(err))
13512
13739
  );
13513
13740
  const reply = await Promise.race([asked, stopped]);
13514
13741
  if (!signal.aborted) return reply;
@@ -14231,13 +14458,13 @@ var HostfsClient = class {
14231
14458
  const response = await this.send("/api/hostfs/watch", { method: "POST", signal });
14232
14459
  onOpen();
14233
14460
  const reader = response.body.getReader();
14234
- const decoder3 = new TextDecoder();
14461
+ const decoder4 = new TextDecoder();
14235
14462
  let buffered = "";
14236
14463
  try {
14237
14464
  for (; ; ) {
14238
14465
  const { done: done4, value } = await reader.read();
14239
14466
  if (done4) return;
14240
- buffered += decoder3.decode(value, { stream: true });
14467
+ buffered += decoder4.decode(value, { stream: true });
14241
14468
  let end = buffered.indexOf("\n");
14242
14469
  while (end >= 0) {
14243
14470
  const line = buffered.slice(0, end).trim();
@@ -15719,7 +15946,7 @@ var LAYOUT = [
15719
15946
  ];
15720
15947
  var LOAD_SAMPLE_MS = 5e3;
15721
15948
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
15722
- var encoder4 = new TextEncoder();
15949
+ var encoder5 = new TextEncoder();
15723
15950
  var USERS_MODES = ["off", "report", "enforce"];
15724
15951
  function usersMode(mode = "off") {
15725
15952
  if (USERS_MODES.includes(mode)) return mode;
@@ -15876,6 +16103,7 @@ var Launcher = class {
15876
16103
  usersMode;
15877
16104
  dac;
15878
16105
  sealed;
16106
+ clipboard;
15879
16107
  rootFs;
15880
16108
  fileWatches;
15881
16109
  nextPid = 1e3;
@@ -15886,6 +16114,12 @@ var Launcher = class {
15886
16114
  this.rootFs = options.fs;
15887
16115
  this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
15888
16116
  if (this.dac) this.dac.tagOf = (pid) => this.tagOf(pid);
16117
+ this.clipboard = new ClipboardBroker(
16118
+ options.clipboard,
16119
+ this.usersMode,
16120
+ void 0,
16121
+ (pid) => this.tagOf(pid)
16122
+ );
15889
16123
  this.sealed = new SealedBroker(
15890
16124
  options.sealed,
15891
16125
  this.usersMode,
@@ -16275,7 +16509,7 @@ var Launcher = class {
16275
16509
  start(req) {
16276
16510
  this.sampling();
16277
16511
  if (req.pgid !== void 0 && this.jobs.sessionOf(req.pgid) === void 0) {
16278
- throw fsError("ESRCH", `no process group ${req.pgid}`);
16512
+ throw new SpawnError("ESRCH");
16279
16513
  }
16280
16514
  const pid = this.nextPid++;
16281
16515
  const stack = this.stackFor(req.tag);
@@ -16302,6 +16536,7 @@ var Launcher = class {
16302
16536
  cred: req.cred,
16303
16537
  ...this.owners ? { owners: true } : {},
16304
16538
  sealed: this.sealed,
16539
+ clipboard: this.clipboard,
16305
16540
  ...this.dac ? { dac: this.dac } : {},
16306
16541
  program: req.program,
16307
16542
  argv0: req.argv0,
@@ -16350,6 +16585,8 @@ var Launcher = class {
16350
16585
  this.jobs.add(pid, req.ppid, (sig) => handle.signal(sig), terminal);
16351
16586
  if (req.exec && req.ppid !== void 0) this.jobs.exec(req.ppid, pid, true);
16352
16587
  if (req.pgid !== void 0) this.jobs.join(pid, req.pgid);
16588
+ if (req.group === "lead") this.jobs.lead(pid);
16589
+ if (req.group === "session") this.jobs.setsid(pid);
16353
16590
  this.described.set(pid, {
16354
16591
  argv: [req.argv0, ...req.args],
16355
16592
  tty: terminal?.name ?? null,
@@ -16511,6 +16748,11 @@ var Launcher = class {
16511
16748
  await fds.closeAll();
16512
16749
  throw new SpawnError("EACCES");
16513
16750
  }
16751
+ const group = this.childGroup(ppid, req);
16752
+ if (typeof group === "string") {
16753
+ await fds.closeAll();
16754
+ throw new SpawnError(group);
16755
+ }
16514
16756
  const cred = grant ? grant.cred : this.credOf(ppid);
16515
16757
  const scope = grant ? "" : await this.scopeOf(cred.euid);
16516
16758
  const planned = await this.plan(req.file, req.argv, req.cwd, scope);
@@ -16537,6 +16779,7 @@ var Launcher = class {
16537
16779
  umask: this.umaskOf(ppid),
16538
16780
  cred,
16539
16781
  tag: this.tagOf(ppid),
16782
+ ...group,
16540
16783
  ...exec
16541
16784
  });
16542
16785
  deciding?.attach();
@@ -16544,6 +16787,17 @@ var Launcher = class {
16544
16787
  return childHandle(handle);
16545
16788
  };
16546
16789
  }
16790
+ childGroup(ppid, req) {
16791
+ const { pgid, session } = req;
16792
+ if (pgid === void 0 && session === void 0) return {};
16793
+ const valid2 = pgid === void 0 || Number.isInteger(pgid) && pgid >= 0;
16794
+ if (req.exec || !valid2 || session !== void 0 && session !== true) return "EINVAL";
16795
+ if (session) return pgid === void 0 ? { group: "session" } : "EINVAL";
16796
+ if (pgid === 0) return { group: "lead" };
16797
+ const sid = this.jobs.sessionOf(pgid);
16798
+ if (sid === void 0) return "ESRCH";
16799
+ return sid === this.jobs.sidOf(ppid) ? { pgid } : "EPERM";
16800
+ }
16547
16801
  undecided(ppid) {
16548
16802
  const decided = Promise.withResolvers();
16549
16803
  return {
@@ -16574,7 +16828,7 @@ var Launcher = class {
16574
16828
  }, 0);
16575
16829
  }
16576
16830
  forker(ppid, parent) {
16577
- const { exec: _exec, ...image } = parent;
16831
+ const { exec: _exec, pgid: _pgid, group: _group, ...image } = parent;
16578
16832
  return async (state, fds) => childHandle(
16579
16833
  this.start({
16580
16834
  ...image,
@@ -17043,7 +17297,7 @@ var Launcher = class {
17043
17297
  },
17044
17298
  account
17045
17299
  );
17046
- const report = (message3) => options.onData(encoder4.encode(`${message3}\r
17300
+ const report = (message3) => options.onData(encoder5.encode(`${message3}\r
17047
17301
  `));
17048
17302
  const handle = await this.launch(planned, {
17049
17303
  env,
@@ -17080,7 +17334,7 @@ var Launcher = class {
17080
17334
  };
17081
17335
  const stdout = collect(out, options.onStdout);
17082
17336
  const stderr = collect(err, options.onStderr);
17083
- const report = (message3) => stderr(encoder4.encode(`${message3}
17337
+ const report = (message3) => stderr(encoder5.encode(`${message3}
17084
17338
  `));
17085
17339
  if (!planned) {
17086
17340
  report(`${file}: command not found`);
@@ -17617,7 +17871,7 @@ function dialStream(port, onClose) {
17617
17871
 
17618
17872
  // src/client/loopback-fetch.ts
17619
17873
  var OWN_HEADERS = /* @__PURE__ */ new Set(["host", "connection", "content-length", "transfer-encoding"]);
17620
- var encoder5 = new TextEncoder();
17874
+ var encoder6 = new TextEncoder();
17621
17875
  var latin12 = new TextDecoder("latin1");
17622
17876
  var ByteSource = class {
17623
17877
  buffered = new Uint8Array(0);
@@ -17628,14 +17882,14 @@ var ByteSource = class {
17628
17882
  async more() {
17629
17883
  const { done: done4, value } = await this.reader.read();
17630
17884
  if (done4) return false;
17631
- const joined2 = new Uint8Array(this.buffered.length + value.length);
17632
- joined2.set(this.buffered);
17633
- joined2.set(value, this.buffered.length);
17634
- this.buffered = joined2;
17885
+ const joined3 = new Uint8Array(this.buffered.length + value.length);
17886
+ joined3.set(this.buffered);
17887
+ joined3.set(value, this.buffered.length);
17888
+ this.buffered = joined3;
17635
17889
  return true;
17636
17890
  }
17637
17891
  async until(marker) {
17638
- const needle = encoder5.encode(marker);
17892
+ const needle = encoder6.encode(marker);
17639
17893
  for (; ; ) {
17640
17894
  const at = indexOf(this.buffered, needle);
17641
17895
  if (at >= 0) return this.take(at + needle.length);
@@ -17743,7 +17997,7 @@ async function requestBytes(request) {
17743
17997
  });
17744
17998
  if (body) lines2.push(`Content-Length: ${body.length}`);
17745
17999
  lines2.push("Connection: close", "", "");
17746
- const head = encoder5.encode(lines2.join("\r\n"));
18000
+ const head = encoder6.encode(lines2.join("\r\n"));
17747
18001
  const out = new Uint8Array(head.length + (body?.length ?? 0));
17748
18002
  out.set(head);
17749
18003
  if (body) out.set(body, head.length);
@@ -17797,8 +18051,8 @@ var KernelCallError = class extends Error {
17797
18051
  this.code = code;
17798
18052
  }
17799
18053
  };
17800
- var encoder6 = new TextEncoder();
17801
- var bytesOf2 = (data) => typeof data === "string" ? encoder6.encode(data) : data;
18054
+ var encoder7 = new TextEncoder();
18055
+ var bytesOf2 = (data) => typeof data === "string" ? encoder7.encode(data) : data;
17802
18056
  var UNAVAILABLE = {
17803
18057
  manualRedirects: false,
17804
18058
  encodedBodies: false,
@@ -18216,8 +18470,8 @@ function nodeWorker(file, live, closed) {
18216
18470
  }
18217
18471
  function textOf(on) {
18218
18472
  if (!on) return void 0;
18219
- const decoder3 = new TextDecoder();
18220
- return (bytes) => on(decoder3.decode(bytes, { stream: true }));
18473
+ const decoder4 = new TextDecoder();
18474
+ return (bytes) => on(decoder4.decode(bytes, { stream: true }));
18221
18475
  }
18222
18476
  async function createNodeKernel(options = {}) {
18223
18477
  const root = options.root ?? memoryRoot();
@@ -18252,6 +18506,7 @@ async function createNodeKernel(options = {}) {
18252
18506
  ...options.onDenial ? { onDenial: options.onDenial } : {},
18253
18507
  ...options.protect ? { protect: options.protect } : {},
18254
18508
  ...options.sealed ? { sealed: options.sealed } : {},
18509
+ ...options.clipboard ? { clipboard: options.clipboard } : {},
18255
18510
  ...options.approve ? { approve: options.approve } : {}
18256
18511
  });
18257
18512
  await launcher.prepare();
@@ -18260,7 +18515,7 @@ async function createNodeKernel(options = {}) {
18260
18515
  pending.add(reject);
18261
18516
  work().then(resolve, reject).finally(() => pending.delete(reject));
18262
18517
  });
18263
- const decoder3 = new TextDecoder();
18518
+ const decoder4 = new TextDecoder();
18264
18519
  return {
18265
18520
  root,
18266
18521
  async run(argv, opts = {}) {
@@ -18277,8 +18532,8 @@ async function createNodeKernel(options = {}) {
18277
18532
  );
18278
18533
  return {
18279
18534
  status: result.status,
18280
- stdout: decoder3.decode(result.stdout),
18281
- stderr: decoder3.decode(result.stderr)
18535
+ stdout: decoder4.decode(result.stdout),
18536
+ stderr: decoder4.decode(result.stderr)
18282
18537
  };
18283
18538
  },
18284
18539
  async openTerminal(argv, opts = {}) {