@ai-ecoverse/slicc-kernel 1.74.0 → 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.
@@ -2255,9 +2255,11 @@ var KernelPipe = class {
2255
2255
  // src/kernel/fd-table.ts
2256
2256
  var KernelError = class extends Error {
2257
2257
  code;
2258
- constructor(code) {
2258
+ detail;
2259
+ constructor(code, detail) {
2259
2260
  super(code);
2260
2261
  this.code = code;
2262
+ if (detail !== void 0) this.detail = detail;
2261
2263
  }
2262
2264
  };
2263
2265
  function pollFile(file) {
@@ -2400,6 +2402,7 @@ function kernelFdKind(file) {
2400
2402
  if (file.heldMeta && "device" in file.heldMeta) return "device";
2401
2403
  if (file.tty) return "tty";
2402
2404
  if (file.sealed) return "sealed";
2405
+ if (file.clipboard) return "clipboard";
2403
2406
  return file.seek ? "file" : "stream";
2404
2407
  }
2405
2408
  var FdTable = class _FdTable {
@@ -4190,6 +4193,203 @@ function serveClient(port, host) {
4190
4193
  return { detach, closed: ended.promise };
4191
4194
  }
4192
4195
 
4196
+ // src/kernel/clipboard.ts
4197
+ var CLIPBOARD_MAX_BYTES = 1024 * 1024;
4198
+ var CLIPBOARD_TIMEOUT_MS = 1e4;
4199
+ var encoder = new TextEncoder();
4200
+ var decoder = new TextDecoder("utf-8", { fatal: true });
4201
+ var READY = { readable: true, writable: true, hangup: false };
4202
+ function lineOf(err) {
4203
+ const text = err instanceof Error ? err.message : String(err);
4204
+ return (text.split("\n")[0] ?? "").slice(0, 200);
4205
+ }
4206
+ function clipboardFailure(err) {
4207
+ if (err instanceof KernelError) return err;
4208
+ const name2 = err?.name;
4209
+ return new KernelError(name2 === "NotAllowedError" ? "EACCES" : "EIO", lineOf(err));
4210
+ }
4211
+ function joined2(chunks, size) {
4212
+ if (chunks.length === 1) return chunks[0];
4213
+ const out = new Uint8Array(size);
4214
+ let at = 0;
4215
+ for (const chunk2 of chunks) {
4216
+ out.set(chunk2, at);
4217
+ at += chunk2.length;
4218
+ }
4219
+ return out;
4220
+ }
4221
+ async function interruptible(work, signal) {
4222
+ if (!signal) return work;
4223
+ if (signal.aborted) throw new KernelError("EINTR");
4224
+ let stop = () => {
4225
+ };
4226
+ const interrupted2 = new Promise((_, reject) => {
4227
+ stop = () => reject(new KernelError("EINTR"));
4228
+ signal.addEventListener("abort", stop, { once: true });
4229
+ });
4230
+ try {
4231
+ return await Promise.race([work, interrupted2]);
4232
+ } finally {
4233
+ signal.removeEventListener("abort", stop);
4234
+ }
4235
+ }
4236
+ var ClipboardChannel = class {
4237
+ stamp;
4238
+ access;
4239
+ broker;
4240
+ snapshot;
4241
+ taken;
4242
+ offset = 0;
4243
+ chunks = [];
4244
+ size = 0;
4245
+ wrote = false;
4246
+ failed = false;
4247
+ constructor(broker, access2, stamp) {
4248
+ this.broker = broker;
4249
+ this.access = access2;
4250
+ this.stamp = stamp;
4251
+ }
4252
+ get unread() {
4253
+ return this.taken === void 0 && this.snapshot === void 0;
4254
+ }
4255
+ get settled() {
4256
+ return this.taken !== void 0;
4257
+ }
4258
+ async read(max, signal) {
4259
+ if (this.access === "write") throw new KernelError("EBADF");
4260
+ if (!this.taken) {
4261
+ if (!this.snapshot) {
4262
+ const stamp = this.stamp;
4263
+ const asked = this.broker.ask(async (hook) => {
4264
+ const text = await hook.read(stamp);
4265
+ if (typeof text !== "string") throw new KernelError("EIO", "the clipboard gave no text");
4266
+ return encoder.encode(text);
4267
+ });
4268
+ this.snapshot = asked;
4269
+ asked.then(
4270
+ (bytes) => {
4271
+ if (this.snapshot === asked) this.taken = bytes;
4272
+ },
4273
+ () => {
4274
+ if (this.snapshot === asked) this.snapshot = void 0;
4275
+ }
4276
+ );
4277
+ }
4278
+ this.taken = await interruptible(this.snapshot, signal);
4279
+ }
4280
+ const out = this.taken.subarray(this.offset, this.offset + max);
4281
+ this.offset += out.length;
4282
+ return out;
4283
+ }
4284
+ write(bytes) {
4285
+ if (this.access === "read") throw new KernelError("EBADF");
4286
+ this.wrote = true;
4287
+ if (this.failed) throw new KernelError("EFBIG");
4288
+ if (this.size + bytes.length > CLIPBOARD_MAX_BYTES) {
4289
+ this.failed = true;
4290
+ this.chunks = [];
4291
+ this.size = 0;
4292
+ throw new KernelError("EFBIG");
4293
+ }
4294
+ if (bytes.length > 0) this.chunks.push(bytes.slice());
4295
+ this.size += bytes.length;
4296
+ return bytes.length;
4297
+ }
4298
+ poll() {
4299
+ return READY;
4300
+ }
4301
+ async close() {
4302
+ this.snapshot = void 0;
4303
+ this.taken = void 0;
4304
+ const commits = this.access === "write" || this.access === "both" && this.wrote;
4305
+ if (!commits || this.failed) return;
4306
+ const bytes = joined2(this.chunks, this.size);
4307
+ this.chunks = [];
4308
+ this.size = 0;
4309
+ let text;
4310
+ try {
4311
+ text = decoder.decode(bytes);
4312
+ } catch {
4313
+ throw new KernelError("EILSEQ", "the text is not valid UTF-8");
4314
+ }
4315
+ const stamp = this.stamp;
4316
+ await this.broker.ask(async (hook) => {
4317
+ await hook.write(text, stamp);
4318
+ });
4319
+ }
4320
+ };
4321
+ var ClipboardBroker = class {
4322
+ hook;
4323
+ users;
4324
+ timeoutMs;
4325
+ tagOf;
4326
+ constructor(hook, users, timeoutMs = CLIPBOARD_TIMEOUT_MS, tagOf = () => void 0) {
4327
+ this.hook = hook;
4328
+ this.users = users;
4329
+ this.timeoutMs = timeoutMs;
4330
+ this.tagOf = tagOf;
4331
+ }
4332
+ async ask(work) {
4333
+ const hook = this.hook;
4334
+ if (!hook) throw new KernelError("ENOTSUP", "no clipboard in this embedder");
4335
+ let timer;
4336
+ const late = new Promise((_, reject) => {
4337
+ timer = setTimeout(
4338
+ () => reject(new KernelError("EIO", "clipboard timed out")),
4339
+ this.timeoutMs
4340
+ );
4341
+ });
4342
+ const asked = Promise.resolve().then(() => work(hook)).catch((err) => {
4343
+ throw clipboardFailure(err);
4344
+ });
4345
+ try {
4346
+ return await Promise.race([asked, late]);
4347
+ } finally {
4348
+ clearTimeout(timer);
4349
+ }
4350
+ }
4351
+ open(access2, stamp) {
4352
+ const channel = new ClipboardChannel(this, access2, stamp);
4353
+ return new OpenFile({
4354
+ read: (max, signal) => channel.read(max, signal),
4355
+ write: async (bytes) => channel.write(bytes),
4356
+ poll: () => channel.poll(),
4357
+ close: () => channel.close(),
4358
+ clipboard: channel
4359
+ });
4360
+ }
4361
+ };
4362
+
4363
+ // src/clipboard-relay.ts
4364
+ function clipboardAsker(port) {
4365
+ const asks = /* @__PURE__ */ new Map();
4366
+ let next2 = 0;
4367
+ const ask = (op, stamp, text) => {
4368
+ const id2 = ++next2;
4369
+ const waiting = Promise.withResolvers();
4370
+ asks.set(id2, waiting);
4371
+ const timer = setTimeout(() => {
4372
+ if (asks.delete(id2)) waiting.reject(new Error("clipboard timed out"));
4373
+ }, CLIPBOARD_TIMEOUT_MS + 1e3);
4374
+ port.postMessage({ clipboardAsk: { id: id2, op, stamp, ...text !== void 0 ? { text } : {} } });
4375
+ return waiting.promise.finally(() => clearTimeout(timer));
4376
+ };
4377
+ const hook = {
4378
+ read: async (stamp) => await ask("read", stamp) ?? "",
4379
+ write: async (text, stamp) => {
4380
+ await ask("write", stamp, text);
4381
+ }
4382
+ };
4383
+ const answer = ({ id: id2, text, error }) => {
4384
+ const waiting = asks.get(id2);
4385
+ if (!waiting) return;
4386
+ asks.delete(id2);
4387
+ if (error) waiting.reject(Object.assign(new Error(error.message), { name: error.name }));
4388
+ else waiting.resolve(text);
4389
+ };
4390
+ return { hook, answer };
4391
+ }
4392
+
4193
4393
  // src/kernel/net/loopback-names.ts
4194
4394
  var HOST_LOOPBACK = "host.slicc.internal";
4195
4395
  var HOST_LOOPBACK_ADDRESS = [10, 0, 2, 2];
@@ -9191,20 +9391,20 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
9191
9391
  var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
9192
9392
  var SYNC_SAB_REQ_MSG = "sync-sab-req";
9193
9393
  var SYNC_SAB_NEXT_MSG = "sync-sab-next";
9194
- var encoder = new TextEncoder();
9195
- var decoder = new TextDecoder();
9394
+ var encoder2 = new TextEncoder();
9395
+ var decoder2 = new TextDecoder();
9196
9396
  function encodeSabResult(result) {
9197
9397
  if (!result.ok) {
9198
9398
  return {
9199
9399
  status: SAB_STATUS_ERRNO,
9200
- payload: encoder.encode(JSON.stringify({ errno: result.errno, message: result.message }))
9400
+ payload: encoder2.encode(JSON.stringify({ errno: result.errno, message: result.message }))
9201
9401
  };
9202
9402
  }
9203
9403
  switch (result.kind) {
9204
9404
  case "bytes":
9205
9405
  return { status: SAB_STATUS_BYTES, payload: result.bytes };
9206
9406
  case "json":
9207
- return { status: SAB_STATUS_JSON, payload: encoder.encode(JSON.stringify(result.json)) };
9407
+ return { status: SAB_STATUS_JSON, payload: encoder2.encode(JSON.stringify(result.json)) };
9208
9408
  default:
9209
9409
  return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
9210
9410
  }
@@ -10678,7 +10878,7 @@ function defaultTermios() {
10678
10878
  c_cc: cCc
10679
10879
  };
10680
10880
  }
10681
- var encoder2 = new TextEncoder();
10881
+ var encoder3 = new TextEncoder();
10682
10882
  function isContinuation(byte) {
10683
10883
  return (byte & 192) === 128;
10684
10884
  }
@@ -10800,7 +11000,7 @@ var KernelTty = class {
10800
11000
  this.line.push(byte);
10801
11001
  this.readable.push(Uint8Array.from(this.line));
10802
11002
  this.line = [];
10803
- if (echo) this.output(encoder2.encode("\n"));
11003
+ if (echo) this.output(encoder3.encode("\n"));
10804
11004
  } else {
10805
11005
  this.line.push(byte);
10806
11006
  if (echo) this.echo(byte);
@@ -10825,7 +11025,7 @@ var KernelTty = class {
10825
11025
  const utf82 = (this.termios.c_iflag & IUTF8) !== 0;
10826
11026
  const cells = utf82 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
10827
11027
  if (cells > 0 && this.termios.c_lflag & ECHO) {
10828
- this.output(encoder2.encode("\b \b".repeat(cells)), false);
11028
+ this.output(encoder3.encode("\b \b".repeat(cells)), false);
10829
11029
  }
10830
11030
  }
10831
11031
  echo(byte) {
@@ -11314,7 +11514,7 @@ async function socketSyscall(req, proc) {
11314
11514
  dialing?.catch(() => void 0);
11315
11515
  throw new KernelError("EINPROGRESS");
11316
11516
  }
11317
- if (dialing !== void 0) await interruptible(dialing, proc.blocking());
11517
+ if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
11318
11518
  return ok();
11319
11519
  }
11320
11520
  case "sock-shutdown":
@@ -11338,7 +11538,7 @@ async function socketSyscall(req, proc) {
11338
11538
  }
11339
11539
  }
11340
11540
  }
11341
- function interruptible(dialing, signal) {
11541
+ function interruptible2(dialing, signal) {
11342
11542
  return new Promise((resolve, reject) => {
11343
11543
  const abort = () => reject(new KernelError("EINTR"));
11344
11544
  signal.addEventListener("abort", abort, { once: true });
@@ -12005,6 +12205,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
12005
12205
  "fd-tty-names",
12006
12206
  "fd-tty-owner",
12007
12207
  "fd-open-sealed",
12208
+ "fd-open-clipboard",
12008
12209
  "tty-get",
12009
12210
  "tty-set",
12010
12211
  "tty-winsz",
@@ -12247,7 +12448,7 @@ var WasmProcess = class {
12247
12448
  return await this.procSyscall(req, cancelled);
12248
12449
  } catch (e) {
12249
12450
  if (e instanceof KernelError || e instanceof SpawnError) {
12250
- return { ok: false, errno: e.code, message: e.code };
12451
+ return { ok: false, errno: e.code, message: e.detail ?? e.code };
12251
12452
  }
12252
12453
  const code = e?.code;
12253
12454
  if (typeof code === "string" && /^E[A-Z]+$/.test(code)) {
@@ -12290,6 +12491,7 @@ var WasmProcess = class {
12290
12491
  const read = req.peek ? file.peek : file.read;
12291
12492
  if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
12292
12493
  if (file.tty) this.checkForeground(file.tty);
12494
+ if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
12293
12495
  if (req.max <= 0) return new Uint8Array(0);
12294
12496
  if (file instanceof KernelSocket && file.unconnected) throw new KernelError("ENOTCONN");
12295
12497
  const ready2 = pollFile(file).readable;
@@ -12297,6 +12499,13 @@ var WasmProcess = class {
12297
12499
  const signal = ready2 ? this.interrupt.signal : this.blockingSignal(cancelled);
12298
12500
  return read.call(file, Math.max(0, Math.min(req.max, MAX_READ)), signal);
12299
12501
  }
12502
+ async readClipboard(channel, req, cancelled) {
12503
+ if (channel.access === "write") throw new KernelError("EBADF");
12504
+ if (req.max <= 0) return new Uint8Array(0);
12505
+ if (channel.unread) channel.stamp = await this.clipboardStamp();
12506
+ const signal = channel.settled ? this.interrupt.signal : this.blockingSignal(cancelled);
12507
+ return channel.read(Math.min(req.max, MAX_READ), signal);
12508
+ }
12300
12509
  async write(fd, body, nonblock = false, cancelled) {
12301
12510
  const file = this.fds.get(fd).file;
12302
12511
  if (file.sealed) return file.sealed.send(body, await this.sealedCaller(), this.gone.signal);
@@ -12400,6 +12609,36 @@ var WasmProcess = class {
12400
12609
  if (!broker) throw new KernelError("ENOENT");
12401
12610
  return this.fds.install(broker.open(), 3);
12402
12611
  }
12612
+ async openClipboard(flags2) {
12613
+ const broker = this.options.clipboard;
12614
+ if (!broker) throw new KernelError("ENOENT");
12615
+ const mode = flags2 & WRITE_ACCESS;
12616
+ const access2 = mode === 0 ? "read" : mode === 1 ? "write" : "both";
12617
+ return this.fds.install(broker.open(access2, await this.clipboardStamp()), 3);
12618
+ }
12619
+ async clipboardStamp() {
12620
+ const shown = await this.options.identity?.();
12621
+ const broker = this.options.clipboard;
12622
+ const tag = broker.tagOf(this.pid);
12623
+ return {
12624
+ pid: shown?.pid ?? this.pid,
12625
+ uid: this.cred.euid,
12626
+ gid: this.cred.egid,
12627
+ users: broker.users,
12628
+ ...tag !== void 0 ? { tag } : {}
12629
+ };
12630
+ }
12631
+ async stampClipboards() {
12632
+ if (!this.options.clipboard) return;
12633
+ const open = this.fds.numbers().filter((fd) => this.fds.get(fd).file.clipboard);
12634
+ if (open.length === 0) return;
12635
+ const stamp = await this.clipboardStamp().catch(() => void 0);
12636
+ if (!stamp) return;
12637
+ for (const fd of open) {
12638
+ const channel = this.fds.get(fd).file.clipboard;
12639
+ if (channel) channel.stamp = stamp;
12640
+ }
12641
+ }
12403
12642
  async sealedCaller() {
12404
12643
  const shown = await this.options.identity?.();
12405
12644
  const broker = this.options.sealed;
@@ -12474,10 +12713,13 @@ var WasmProcess = class {
12474
12713
  kind: "json",
12475
12714
  json: await this.write(req.fd, req.body, req.nonblock, cancelled)
12476
12715
  };
12477
- case "fd-close":
12716
+ case "fd-close": {
12478
12717
  this.options.locks?.closed(this.pid, req.fd);
12718
+ const channel = this.fds.get(req.fd).file.clipboard;
12719
+ if (channel) channel.stamp = await this.clipboardStamp();
12479
12720
  await Promise.resolve(this.fds.close(req.fd));
12480
12721
  return { ok: true, kind: "void" };
12722
+ }
12481
12723
  case "fd-pipe": {
12482
12724
  const pipe = openPipe();
12483
12725
  const read = this.fds.install(pipe.read, 3);
@@ -12535,6 +12777,8 @@ var WasmProcess = class {
12535
12777
  return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
12536
12778
  case "fd-open-sealed":
12537
12779
  return { ok: true, kind: "json", json: this.openSealed() };
12780
+ case "fd-open-clipboard":
12781
+ return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
12538
12782
  case "fd-tty-owner":
12539
12783
  return { ok: true, kind: "json", json: this.terminalByName(req.name).owner ?? null };
12540
12784
  case "fd-select": {
@@ -12986,6 +13230,7 @@ var WasmProcess = class {
12986
13230
  this.options.locks?.release(this.pid);
12987
13231
  this.asyncOps.close();
12988
13232
  for (const pid of this.children.pids()) this.options.onReap?.(pid);
13233
+ await this.stampClipboards();
12989
13234
  await this.fds.closeAll();
12990
13235
  if (!this.options.nodes) this.options.openFiles?.delete(this.nodes);
12991
13236
  await this.options.http?.closeAll();
@@ -13168,6 +13413,7 @@ function processOptions(opts, header3, hooks, created3) {
13168
13413
  ...inherited(opts),
13169
13414
  ...opts.owners ? { owners: true } : {},
13170
13415
  ...opts.sealed ? { sealed: opts.sealed } : {},
13416
+ ...opts.clipboard ? { clipboard: opts.clipboard } : {},
13171
13417
  ...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
13172
13418
  ...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
13173
13419
  ...opts.identity ? { identity: opts.identity } : {},
@@ -13672,14 +13918,14 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
13672
13918
  "cancel",
13673
13919
  "materialize"
13674
13920
  ]);
13675
- var encoder3 = new TextEncoder();
13676
- var decoder2 = new TextDecoder("utf-8", { fatal: true });
13921
+ var encoder4 = new TextEncoder();
13922
+ var decoder3 = new TextDecoder("utf-8", { fatal: true });
13677
13923
  function parseSealed(bytes) {
13678
13924
  try {
13679
13925
  if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
13680
13926
  let parsed2;
13681
13927
  try {
13682
- parsed2 = JSON.parse(decoder2.decode(bytes));
13928
+ parsed2 = JSON.parse(decoder3.decode(bytes));
13683
13929
  } catch {
13684
13930
  throw new KernelError("EINVAL");
13685
13931
  }
@@ -13705,13 +13951,13 @@ function serialized(reply) {
13705
13951
  const text = JSON.stringify(reply);
13706
13952
  plain = text === void 0 ? void 0 : JSON.parse(text);
13707
13953
  } catch (err) {
13708
- return JSON.stringify(errorReply("EIO", lineOf(err)));
13954
+ return JSON.stringify(errorReply("EIO", lineOf2(err)));
13709
13955
  }
13710
13956
  if (!isRecord(plain)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
13711
13957
  const { v: _, ...rest2 } = plain;
13712
13958
  return JSON.stringify({ v: 1, ...rest2 });
13713
13959
  }
13714
- function lineOf(err) {
13960
+ function lineOf2(err) {
13715
13961
  const text = err instanceof Error ? err.message : String(err);
13716
13962
  return (text.split("\n")[0] ?? "").slice(0, 200);
13717
13963
  }
@@ -13741,7 +13987,7 @@ var SealedChannel = class {
13741
13987
  gone.removeEventListener("abort", withdraw);
13742
13988
  if (this.inflight !== flight) return;
13743
13989
  this.inflight = void 0;
13744
- if (reply) this.reply = encoder3.encode(serialized(reply));
13990
+ if (reply) this.reply = encoder4.encode(serialized(reply));
13745
13991
  });
13746
13992
  this.inflight = flight;
13747
13993
  return length2;
@@ -13755,7 +14001,7 @@ var SealedChannel = class {
13755
14001
  if (signal.aborted) return void 0;
13756
14002
  const asked = Promise.resolve().then(() => hook(req, { signal })).then(
13757
14003
  (reply2) => isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
13758
- (err) => errorReply("EIO", lineOf(err))
14004
+ (err) => errorReply("EIO", lineOf2(err))
13759
14005
  );
13760
14006
  const reply = await Promise.race([asked, stopped]);
13761
14007
  if (!signal.aborted) return reply;
@@ -14478,13 +14724,13 @@ var HostfsClient = class {
14478
14724
  const response = await this.send("/api/hostfs/watch", { method: "POST", signal });
14479
14725
  onOpen();
14480
14726
  const reader = response.body.getReader();
14481
- const decoder3 = new TextDecoder();
14727
+ const decoder4 = new TextDecoder();
14482
14728
  let buffered = "";
14483
14729
  try {
14484
14730
  for (; ; ) {
14485
14731
  const { done: done4, value } = await reader.read();
14486
14732
  if (done4) return;
14487
- buffered += decoder3.decode(value, { stream: true });
14733
+ buffered += decoder4.decode(value, { stream: true });
14488
14734
  let end = buffered.indexOf("\n");
14489
14735
  while (end >= 0) {
14490
14736
  const line = buffered.slice(0, end).trim();
@@ -15863,7 +16109,7 @@ var LAYOUT = [
15863
16109
  ];
15864
16110
  var LOAD_SAMPLE_MS = 5e3;
15865
16111
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
15866
- var encoder4 = new TextEncoder();
16112
+ var encoder5 = new TextEncoder();
15867
16113
  var USERS_MODES = ["off", "report", "enforce"];
15868
16114
  function usersMode(mode = "off") {
15869
16115
  if (USERS_MODES.includes(mode)) return mode;
@@ -16020,6 +16266,7 @@ var Launcher = class {
16020
16266
  usersMode;
16021
16267
  dac;
16022
16268
  sealed;
16269
+ clipboard;
16023
16270
  rootFs;
16024
16271
  fileWatches;
16025
16272
  nextPid = 1e3;
@@ -16030,6 +16277,12 @@ var Launcher = class {
16030
16277
  this.rootFs = options.fs;
16031
16278
  this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
16032
16279
  if (this.dac) this.dac.tagOf = (pid) => this.tagOf(pid);
16280
+ this.clipboard = new ClipboardBroker(
16281
+ options.clipboard,
16282
+ this.usersMode,
16283
+ void 0,
16284
+ (pid) => this.tagOf(pid)
16285
+ );
16033
16286
  this.sealed = new SealedBroker(
16034
16287
  options.sealed,
16035
16288
  this.usersMode,
@@ -16446,6 +16699,7 @@ var Launcher = class {
16446
16699
  cred: req.cred,
16447
16700
  ...this.owners ? { owners: true } : {},
16448
16701
  sealed: this.sealed,
16702
+ clipboard: this.clipboard,
16449
16703
  ...this.dac ? { dac: this.dac } : {},
16450
16704
  program: req.program,
16451
16705
  argv0: req.argv0,
@@ -17206,7 +17460,7 @@ var Launcher = class {
17206
17460
  },
17207
17461
  account
17208
17462
  );
17209
- const report = (message3) => options.onData(encoder4.encode(`${message3}\r
17463
+ const report = (message3) => options.onData(encoder5.encode(`${message3}\r
17210
17464
  `));
17211
17465
  const handle = await this.launch(planned, {
17212
17466
  env,
@@ -17243,7 +17497,7 @@ var Launcher = class {
17243
17497
  };
17244
17498
  const stdout = collect(out, options.onStdout);
17245
17499
  const stderr = collect(err, options.onStderr);
17246
- const report = (message3) => stderr(encoder4.encode(`${message3}
17500
+ const report = (message3) => stderr(encoder5.encode(`${message3}
17247
17501
  `));
17248
17502
  if (!planned) {
17249
17503
  report(`${file}: command not found`);
@@ -17431,13 +17685,14 @@ function usersOp(l, req) {
17431
17685
  if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
17432
17686
  return l.users.list();
17433
17687
  }
17434
- function hostOptions(req, port, sealed) {
17688
+ function hostOptions(req, port, sealed, clipboard) {
17435
17689
  return {
17436
17690
  ...req.hostname !== void 0 ? { hostname: req.hostname } : {},
17437
17691
  ...req.users !== void 0 ? { users: req.users } : {},
17438
17692
  ...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
17439
17693
  ...req.protect ? { protect: protectInit(req.protect) } : {},
17440
- ...req.sealed ? { sealed } : {}
17694
+ ...req.sealed ? { sealed } : {},
17695
+ ...req.clipboard ? { clipboard } : {}
17441
17696
  };
17442
17697
  }
17443
17698
  function policyAsker(port) {
@@ -17467,6 +17722,7 @@ function serveKernel(port, deps) {
17467
17722
  const reply = (id2, body, transfer) => port.postMessage({ id: id2, ...body }, transfer);
17468
17723
  const clients = /* @__PURE__ */ new Set();
17469
17724
  const sealed = sealedAsker(port);
17725
+ const clipboard = clipboardAsker(port);
17470
17726
  const approvals = sudoApprovals(port);
17471
17727
  const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
17472
17728
  let holding;
@@ -17531,7 +17787,7 @@ function serveKernel(port, deps) {
17531
17787
  ...deps.createDriverWorker ? { createDriverWorker: deps.createDriverWorker } : {},
17532
17788
  ...req.modules ? { modules: req.modules } : {},
17533
17789
  ...req.env ? { env: req.env } : {},
17534
- ...hostOptions(req, port, sealed.hook),
17790
+ ...hostOptions(req, port, sealed.hook, clipboard.hook),
17535
17791
  ...req.approve ? { approve: approvals.ask } : {},
17536
17792
  ...net,
17537
17793
  ...media ? { media } : {},
@@ -17600,6 +17856,8 @@ function serveKernel(port, deps) {
17600
17856
  port.addEventListener("message", (event) => {
17601
17857
  const sealedReply = event.data.sealedReply;
17602
17858
  if (sealedReply) return sealed.answer(sealedReply);
17859
+ const clipboardReply = event.data.clipboardReply;
17860
+ if (clipboardReply) return clipboard.answer(clipboardReply);
17603
17861
  const grant = event.data.hostfsGrant;
17604
17862
  if (grant) return granted2(grant);
17605
17863
  if (allowed(event.data)) return;