@ai-ecoverse/slicc-kernel 1.178.4 → 1.180.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.
@@ -7451,6 +7451,82 @@ var KERNEL_SIGINFO = Object.freeze({
7451
7451
  status: 0
7452
7452
  });
7453
7453
 
7454
+ // src/kernel/trace.ts
7455
+ var TRACE_LIMIT = 4096;
7456
+ var TRACE_LIMIT_MAX = 65536;
7457
+ function traceLimit(value2) {
7458
+ if (value2 === void 0) return TRACE_LIMIT;
7459
+ if (!Number.isInteger(value2) || value2 < 1 || value2 > TRACE_LIMIT_MAX) {
7460
+ throw Object.assign(new Error(`a trace limit is an integer from 1 to ${TRACE_LIMIT_MAX}`), {
7461
+ code: "EINVAL"
7462
+ });
7463
+ }
7464
+ return value2;
7465
+ }
7466
+ var TraceHub = class {
7467
+ subs = /* @__PURE__ */ new Set();
7468
+ seq = 0;
7469
+ get idle() {
7470
+ return this.subs.size === 0;
7471
+ }
7472
+ subscribe(tag, viewer, deliver, limit = TRACE_LIMIT) {
7473
+ const sub = {
7474
+ tag,
7475
+ viewer: copyCred(viewer),
7476
+ limit,
7477
+ queue: [],
7478
+ dropped: 0,
7479
+ waiting: false,
7480
+ scheduled: false,
7481
+ deliver
7482
+ };
7483
+ this.subs.add(sub);
7484
+ return {
7485
+ ack: () => {
7486
+ sub.waiting = false;
7487
+ this.schedule(sub);
7488
+ },
7489
+ close: () => {
7490
+ this.subs.delete(sub);
7491
+ sub.queue = [];
7492
+ }
7493
+ };
7494
+ }
7495
+ wants(tags, cred) {
7496
+ if (!tags?.length || this.subs.size === 0) return false;
7497
+ for (const sub of this.subs) if (this.matches(sub, tags, cred)) return true;
7498
+ return false;
7499
+ }
7500
+ publish(tags, cred, draft) {
7501
+ if (!tags?.length) return;
7502
+ let event;
7503
+ for (const sub of this.subs) {
7504
+ if (!this.matches(sub, tags, cred)) continue;
7505
+ event ??= { ...draft, seq: ++this.seq };
7506
+ if (sub.queue.length >= sub.limit) sub.dropped++;
7507
+ else sub.queue.push(event);
7508
+ this.schedule(sub);
7509
+ }
7510
+ }
7511
+ matches(sub, tags, cred) {
7512
+ return tags.includes(sub.tag) && maySee(sub.viewer, cred);
7513
+ }
7514
+ schedule(sub) {
7515
+ if (sub.scheduled || sub.waiting) return;
7516
+ if (sub.queue.length === 0 && sub.dropped === 0) return;
7517
+ sub.scheduled = true;
7518
+ queueMicrotask(() => {
7519
+ sub.scheduled = false;
7520
+ if (!this.subs.has(sub)) return;
7521
+ const batch = { events: sub.queue, dropped: sub.dropped };
7522
+ sub.queue = [];
7523
+ sub.dropped = 0;
7524
+ sub.waiting = true;
7525
+ sub.deliver(batch);
7526
+ });
7527
+ }
7528
+ };
7529
+
7454
7530
  // src/body-idle.ts
7455
7531
  var BODY_IDLE_MS = 5 * 60 * 1e3;
7456
7532
 
@@ -8130,7 +8206,7 @@ var UserDb = class {
8130
8206
  };
8131
8207
 
8132
8208
  // src/client/protocol.ts
8133
- var PROTOCOL = [1, 13];
8209
+ var PROTOCOL = [1, 15];
8134
8210
  var FS_METHODS = [
8135
8211
  "readFile",
8136
8212
  "writeFile",
@@ -8297,9 +8373,21 @@ async function runAs(l, self, requested) {
8297
8373
  function clientCred(self) {
8298
8374
  return self ? credOf(self) : copyCred(ROOT);
8299
8375
  }
8376
+ var MAX_SPAWN_TAGS = 8;
8377
+ function spawnTags(value2) {
8378
+ if (!Array.isArray(value2)) {
8379
+ tagOption(value2);
8380
+ return value2 === void 0 ? [] : [value2];
8381
+ }
8382
+ if (value2.length > MAX_SPAWN_TAGS) {
8383
+ throw new TypeError(`a spawn adds at most ${MAX_SPAWN_TAGS} tags`);
8384
+ }
8385
+ for (const each of value2) tagOption(each ?? 0);
8386
+ return value2;
8387
+ }
8300
8388
  async function runTagged(l, self, options, tag, session = -1) {
8301
- tagOption(options.tag);
8302
- const tags = withTag(withTag(void 0, tag), options.tag);
8389
+ let tags = withTag(void 0, tag);
8390
+ for (const added of spawnTags(options.tag)) tags = withTag(tags, added);
8303
8391
  return {
8304
8392
  user: await runAs(l, self, options.user),
8305
8393
  tag,
@@ -8430,6 +8518,34 @@ async function watchOp(l, req, watches, send2, g) {
8430
8518
  watches.set(req.id, unwatch);
8431
8519
  return { result: req.id };
8432
8520
  }
8521
+ function traceRegistry(send2) {
8522
+ const traces = /* @__PURE__ */ new Map();
8523
+ return {
8524
+ answer(l, self, req) {
8525
+ if (req.op === "untrace") {
8526
+ traces.get(req.trace)?.close();
8527
+ return { result: traces.delete(req.trace) };
8528
+ }
8529
+ if (typeof req.tag !== "string" || req.tag === "") {
8530
+ throw fsError("EINVAL", "trace: a tag is a non-empty string");
8531
+ }
8532
+ const limit = traceLimit(req.limit);
8533
+ const id2 = req.id;
8534
+ const deliver = ({ events, dropped: dropped2 }) => send2({ trace: id2, events, ...dropped2 > 0 ? { dropped: dropped2 } : {} });
8535
+ traces.set(id2, l.traces.subscribe(req.tag, credOf(self), deliver, limit));
8536
+ return { result: id2 };
8537
+ },
8538
+ ack(data) {
8539
+ if (typeof data.traceAck !== "number") return false;
8540
+ traces.get(data.traceAck)?.ack();
8541
+ return true;
8542
+ },
8543
+ close() {
8544
+ for (const trace of traces.values()) trace.close();
8545
+ traces.clear();
8546
+ }
8547
+ };
8548
+ }
8433
8549
  var TRAITS_WATCH = -1;
8434
8550
  function traitsWatch(launcher, transport, send2) {
8435
8551
  return launcher.onTraits(() => send2({ traits: transport.traits }));
@@ -8456,6 +8572,21 @@ function requestBinding(send2, port) {
8456
8572
  close: () => unbind?.()
8457
8573
  };
8458
8574
  }
8575
+ function answered(pending, req, reply, detach) {
8576
+ pending.then(
8577
+ ({ result, signal, transfer }) => {
8578
+ reply(req.id, { result, ...signal ? { signal } : {} }, transfer);
8579
+ if (req.op === "detach") detach(req.kill === true);
8580
+ },
8581
+ (err) => {
8582
+ const code = err?.code;
8583
+ reply(req.id, {
8584
+ error: err instanceof Error ? err.message : String(err),
8585
+ ...typeof code === "string" ? { code } : {}
8586
+ });
8587
+ }
8588
+ );
8589
+ }
8459
8590
  function serveClient(port, host) {
8460
8591
  let state = "new";
8461
8592
  let self;
@@ -8471,6 +8602,7 @@ function serveClient(port, host) {
8471
8602
  };
8472
8603
  const reply = (id2, body, transfer) => send2({ id: id2, ...body }, transfer);
8473
8604
  const hosts = hostRegistries(send2, ended.promise, port);
8605
+ const traces = traceRegistry(send2);
8474
8606
  function detach(kill = false, bye = "the slicc-kernel detached this client") {
8475
8607
  if (state === "closed") return;
8476
8608
  send2({ bye });
@@ -8478,6 +8610,7 @@ function serveClient(port, host) {
8478
8610
  net?.close();
8479
8611
  for (const unwatch of watches.values()) unwatch();
8480
8612
  watches.clear();
8613
+ traces.close();
8481
8614
  hosts.close();
8482
8615
  for (const session of terminals.values()) session.close();
8483
8616
  terminals.clear();
@@ -8585,6 +8718,9 @@ function serveClient(port, host) {
8585
8718
  case "serve-facade":
8586
8719
  case "unserve-facade":
8587
8720
  return { result: hosts.answer(l, req) };
8721
+ case "trace":
8722
+ case "untrace":
8723
+ return traces.answer(l, self, req);
8588
8724
  case "detach":
8589
8725
  return { result: true };
8590
8726
  default:
@@ -8608,20 +8744,9 @@ function serveClient(port, host) {
8608
8744
  net?.answer(data);
8609
8745
  return;
8610
8746
  }
8747
+ if (traces.ack(data)) return;
8611
8748
  const req = data;
8612
- handle(req).then(
8613
- ({ result, signal, transfer }) => {
8614
- reply(req.id, { result, ...signal ? { signal } : {} }, transfer);
8615
- if (req.op === "detach") detach(req.kill === true);
8616
- },
8617
- (err) => {
8618
- const code = err?.code;
8619
- reply(req.id, {
8620
- error: err instanceof Error ? err.message : String(err),
8621
- ...typeof code === "string" ? { code } : {}
8622
- });
8623
- }
8624
- );
8749
+ answered(handle(req), req, reply, detach);
8625
8750
  });
8626
8751
  port.addEventListener("close", () => detach());
8627
8752
  port.start?.();
@@ -8779,7 +8904,7 @@ function satisfies(version, range2) {
8779
8904
  }
8780
8905
 
8781
8906
  // src/version.ts
8782
- var KERNEL_VERSION = true ? "1.178.4" : "0.0.0-dev";
8907
+ var KERNEL_VERSION = true ? "1.180.0" : "0.0.0-dev";
8783
8908
  var OS_RELEASE = "6.0.0-slicc";
8784
8909
  function procVersion() {
8785
8910
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -30069,6 +30194,9 @@ var JobTable = class {
30069
30194
  idOf(pid) {
30070
30195
  return this.members.get(pid)?.shownAs ?? pid;
30071
30196
  }
30197
+ execParentOf(pid) {
30198
+ return this.members.get(pid)?.execParent;
30199
+ }
30072
30200
  byId(id2) {
30073
30201
  for (const member of this.members.values()) if (member.shownAs === id2) return member;
30074
30202
  return this.member(id2);
@@ -33714,6 +33842,11 @@ function usersMode(mode = "off") {
33714
33842
  function tagOption(tag) {
33715
33843
  if (tag !== void 0 && typeof tag !== "string") throw new TypeError("a tag must be a string");
33716
33844
  }
33845
+ function cwdMissingOption(value2) {
33846
+ if (value2 === void 0 || value2 === "create") return "create";
33847
+ if (value2 === "error") return "error";
33848
+ throw new TypeError("cwdMissing must be 'create' or 'error'");
33849
+ }
33717
33850
  function withTag(tags, tag) {
33718
33851
  if (tag === void 0 || tags?.includes(tag)) return tags;
33719
33852
  return [...tags ?? [], tag];
@@ -35278,10 +35411,12 @@ var Launcher = class {
35278
35411
  });
35279
35412
  const fit = await (handle.ready ?? true);
35280
35413
  if (fit) {
35414
+ this.traceAdmitted(handle.pid, true);
35281
35415
  await fds.closeAll();
35282
35416
  return handle;
35283
35417
  }
35284
35418
  await handle.exited;
35419
+ this.traceAdmitted(handle.pid, false);
35285
35420
  await this.memories.refused().catch(async (err) => {
35286
35421
  await fds.closeAll();
35287
35422
  throw err;
@@ -35412,10 +35547,12 @@ var Launcher = class {
35412
35547
  if (req.group === "lead") this.jobs.lead(pid);
35413
35548
  if (req.group === "session") this.jobs.setsid(pid);
35414
35549
  this.described.set(pid, describe(req, terminal?.name ?? null));
35550
+ this.traceStarted(pid, req);
35415
35551
  handle.onState(
35416
35552
  (state) => state === "stopped" ? this.stopped.add(pid) : this.stopped.delete(pid)
35417
35553
  );
35418
- void handle.exited.then(() => {
35554
+ void handle.exited.then((status) => {
35555
+ this.traceEnded(pid, handle, status);
35419
35556
  this.cwds.release(this.cwdSlots.get(pid));
35420
35557
  this.cwdSlots.delete(pid);
35421
35558
  this.cwdPaths.delete(pid);
@@ -35530,6 +35667,71 @@ var Launcher = class {
35530
35667
  imageTimes = /* @__PURE__ */ new Map();
35531
35668
  childTimes = /* @__PURE__ */ new Map();
35532
35669
  stopped = /* @__PURE__ */ new Set();
35670
+ traces = new TraceHub();
35671
+ traceHeld = /* @__PURE__ */ new Map();
35672
+ replacedImages = /* @__PURE__ */ new Set();
35673
+ traceStarted(pid, req) {
35674
+ const replaces = req.exec ? req.ppid : void 0;
35675
+ if (req.quietOom) this.traceHeld.set(pid, { replaces, events: [] });
35676
+ else if (replaces !== void 0) this.replacedImages.add(replaces);
35677
+ this.traceEmit(pid, () => {
35678
+ const { pid: shown2, ppid } = this.identityOf(pid);
35679
+ const described2 = this.described.get(pid);
35680
+ const { argv, started, tty: _tty, exeId: _exeId, ...exe } = described2;
35681
+ return {
35682
+ type: req.fork ? "fork" : "exec",
35683
+ pid: shown2,
35684
+ ppid,
35685
+ pgid: this.jobs.pgidOf(pid),
35686
+ argv: [...argv],
35687
+ ...exe,
35688
+ uid: this.credOf(pid).euid,
35689
+ at: started,
35690
+ ...req.exec ? { replaced: true } : {}
35691
+ };
35692
+ });
35693
+ }
35694
+ traceEnded(pid, handle, status) {
35695
+ if (this.replacedImages.delete(pid)) return;
35696
+ this.traceEmit(pid, () => ({
35697
+ type: "exit",
35698
+ pid: this.jobs.idOf(pid),
35699
+ status,
35700
+ ...signalField(handle.termsig()),
35701
+ ...this.chainTimes(pid),
35702
+ at: Date.now()
35703
+ }));
35704
+ }
35705
+ traceEmit(pid, build) {
35706
+ if (this.traces.idle) return;
35707
+ const tags = this.tagsOf(pid);
35708
+ const cred = this.credOf(pid);
35709
+ if (!this.traces.wants(tags, cred)) return;
35710
+ const held = this.traceHeld.get(pid);
35711
+ if (held) held.events.push({ tags, cred: copyCred(cred), draft: build() });
35712
+ else this.traces.publish(tags, cred, build());
35713
+ }
35714
+ traceAdmitted(pid, fit) {
35715
+ const held = this.traceHeld.get(pid);
35716
+ if (!held) return;
35717
+ this.traceHeld.delete(pid);
35718
+ if (!fit) return;
35719
+ if (held.replaces !== void 0) this.replacedImages.add(held.replaces);
35720
+ for (const { tags, cred, draft } of held.events) this.traces.publish(tags, cred, draft);
35721
+ }
35722
+ chainTimes(pid) {
35723
+ let utime = 0;
35724
+ let stime = 0;
35725
+ let timed2 = false;
35726
+ for (let at = pid; at !== void 0; at = this.jobs.execParentOf(at)) {
35727
+ const t = this.timesAt(at);
35728
+ if (!t) continue;
35729
+ timed2 = true;
35730
+ utime += t.utime;
35731
+ stime += t.stime;
35732
+ }
35733
+ return timed2 ? { utime, stime } : {};
35734
+ }
35533
35735
  stateOf(pid) {
35534
35736
  if (!this.processes.has(pid)) return "Z";
35535
35737
  return this.stopped.has(pid) ? "T" : "S";
@@ -36592,7 +36794,7 @@ var Launcher = class {
36592
36794
  const kept = keepingOpen(fs, nodes, (path) => this.changes.publish(path));
36593
36795
  return this.swaps.wrap(unlinked.wrap(kept));
36594
36796
  }
36595
- async starting(argv, dir, account, tags, base) {
36797
+ async starting(argv, dir, account, tags, base, missing6 = "create") {
36596
36798
  const tag = tags?.[0];
36597
36799
  await this.swaps.settled();
36598
36800
  this.resetCatalogs();
@@ -36601,8 +36803,13 @@ var Launcher = class {
36601
36803
  const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
36602
36804
  await this.enterable(this.execGuard(credOf(account), 0, tag), cwd);
36603
36805
  const file = await this.execFile(argv[0] ?? "", cwd, this.execGuard(credOf(account), 0, tag));
36604
- if (!await this.base.exists(cwd))
36806
+ if (!await this.base.exists(cwd)) {
36807
+ if (missing6 === "error")
36808
+ throw Object.assign(new Error(`cannot change to ${cwd}: No such file or directory`), {
36809
+ code: "ENOENT"
36810
+ });
36605
36811
  await this.stackFor(tags, base).fs.mkdir(cwd, { recursive: true });
36812
+ }
36606
36813
  const plan = async () => {
36607
36814
  const found = await this.plan(file, argv, cwd, scope);
36608
36815
  await this.mayExec(credOf(account), { file, cwd, pid: 0, tag }, found);
@@ -36613,6 +36820,7 @@ var Launcher = class {
36613
36820
  }
36614
36821
  async openTerminal(argv, options) {
36615
36822
  const tagged = tagsOption(options);
36823
+ const missing6 = cwdMissingOption(options.cwdMissing);
36616
36824
  const { tags } = tagged;
36617
36825
  const { account, tty: ttyGroup } = await this.terminalAccount(options.user);
36618
36826
  const { cwd, file, planned } = await this.starting(
@@ -36620,7 +36828,8 @@ var Launcher = class {
36620
36828
  options.cwd,
36621
36829
  account,
36622
36830
  tags,
36623
- tagged.tagBase
36831
+ tagged.tagBase,
36832
+ missing6
36624
36833
  );
36625
36834
  if (!planned) throw new Error(`${file}: command not found`);
36626
36835
  let leader = 0;
@@ -36672,6 +36881,7 @@ var Launcher = class {
36672
36881
  }
36673
36882
  async run(argv, options = {}) {
36674
36883
  const tagged = tagsOption(options);
36884
+ const missing6 = cwdMissingOption(options.cwdMissing);
36675
36885
  const { tags } = tagged;
36676
36886
  const account = await this.account(options.user);
36677
36887
  const { cwd, file, planned } = await this.starting(
@@ -36679,7 +36889,8 @@ var Launcher = class {
36679
36889
  options.cwd,
36680
36890
  account,
36681
36891
  tags,
36682
- tagged.tagBase
36892
+ tagged.tagBase,
36893
+ missing6
36683
36894
  );
36684
36895
  const env = this.environment(cwd, options.env, account, options.login);
36685
36896
  const out = [];
@@ -37047,7 +37258,7 @@ function sudoApprovals(port) {
37047
37258
  }
37048
37259
  };
37049
37260
  }
37050
- function answered(data, remote, uplink, approvals) {
37261
+ function answered2(data, remote, uplink, approvals) {
37051
37262
  if (data.sudoAnswer) approvals.answer(data.sudoAnswer);
37052
37263
  else if (data.net !== void 0) remote?.receive(data);
37053
37264
  else if (typeof data.uplink === "string") uplink?.receive(data);
@@ -37332,7 +37543,7 @@ function serveKernel(port, deps) {
37332
37543
  const grant = event.data.hostfsGrant;
37333
37544
  if (grant) return granted2(grant);
37334
37545
  if (allowed(event.data)) return;
37335
- if (answered(event.data, remote, uplink, approvals)) return;
37546
+ if (answered2(event.data, remote, uplink, approvals)) return;
37336
37547
  const req = event.data;
37337
37548
  handle(req).then(
37338
37549
  (result) => reply(req.id, { result }, transferOf2(req, result)),