@ai-ecoverse/slicc-kernel 1.179.0 → 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.
@@ -7518,7 +7518,7 @@ var init_version = __esm({
7518
7518
  "src/version.ts"() {
7519
7519
  "use strict";
7520
7520
  init_captured_globals();
7521
- KERNEL_VERSION = true ? "1.179.0" : "0.0.0-dev";
7521
+ KERNEL_VERSION = true ? "1.180.0" : "0.0.0-dev";
7522
7522
  OS_RELEASE = "6.0.0-slicc";
7523
7523
  }
7524
7524
  });
@@ -7517,7 +7517,7 @@ var init_version = __esm({
7517
7517
  "src/version.ts"() {
7518
7518
  "use strict";
7519
7519
  init_captured_globals();
7520
- KERNEL_VERSION = true ? "1.179.0" : "0.0.0-dev";
7520
+ KERNEL_VERSION = true ? "1.180.0" : "0.0.0-dev";
7521
7521
  OS_RELEASE = "6.0.0-slicc";
7522
7522
  }
7523
7523
  });
package/dist/node.js CHANGED
@@ -2751,6 +2751,82 @@ var KERNEL_SIGINFO = Object.freeze({
2751
2751
  status: 0
2752
2752
  });
2753
2753
 
2754
+ // src/kernel/trace.ts
2755
+ var TRACE_LIMIT = 4096;
2756
+ var TRACE_LIMIT_MAX = 65536;
2757
+ function traceLimit(value2) {
2758
+ if (value2 === void 0) return TRACE_LIMIT;
2759
+ if (!Number.isInteger(value2) || value2 < 1 || value2 > TRACE_LIMIT_MAX) {
2760
+ throw Object.assign(new Error(`a trace limit is an integer from 1 to ${TRACE_LIMIT_MAX}`), {
2761
+ code: "EINVAL"
2762
+ });
2763
+ }
2764
+ return value2;
2765
+ }
2766
+ var TraceHub = class {
2767
+ subs = /* @__PURE__ */ new Set();
2768
+ seq = 0;
2769
+ get idle() {
2770
+ return this.subs.size === 0;
2771
+ }
2772
+ subscribe(tag, viewer, deliver, limit = TRACE_LIMIT) {
2773
+ const sub = {
2774
+ tag,
2775
+ viewer: copyCred(viewer),
2776
+ limit,
2777
+ queue: [],
2778
+ dropped: 0,
2779
+ waiting: false,
2780
+ scheduled: false,
2781
+ deliver
2782
+ };
2783
+ this.subs.add(sub);
2784
+ return {
2785
+ ack: () => {
2786
+ sub.waiting = false;
2787
+ this.schedule(sub);
2788
+ },
2789
+ close: () => {
2790
+ this.subs.delete(sub);
2791
+ sub.queue = [];
2792
+ }
2793
+ };
2794
+ }
2795
+ wants(tags, cred) {
2796
+ if (!tags?.length || this.subs.size === 0) return false;
2797
+ for (const sub of this.subs) if (this.matches(sub, tags, cred)) return true;
2798
+ return false;
2799
+ }
2800
+ publish(tags, cred, draft) {
2801
+ if (!tags?.length) return;
2802
+ let event;
2803
+ for (const sub of this.subs) {
2804
+ if (!this.matches(sub, tags, cred)) continue;
2805
+ event ??= { ...draft, seq: ++this.seq };
2806
+ if (sub.queue.length >= sub.limit) sub.dropped++;
2807
+ else sub.queue.push(event);
2808
+ this.schedule(sub);
2809
+ }
2810
+ }
2811
+ matches(sub, tags, cred) {
2812
+ return tags.includes(sub.tag) && maySee(sub.viewer, cred);
2813
+ }
2814
+ schedule(sub) {
2815
+ if (sub.scheduled || sub.waiting) return;
2816
+ if (sub.queue.length === 0 && sub.dropped === 0) return;
2817
+ sub.scheduled = true;
2818
+ queueMicrotask(() => {
2819
+ sub.scheduled = false;
2820
+ if (!this.subs.has(sub)) return;
2821
+ const batch = { events: sub.queue, dropped: sub.dropped };
2822
+ sub.queue = [];
2823
+ sub.dropped = 0;
2824
+ sub.waiting = true;
2825
+ sub.deliver(batch);
2826
+ });
2827
+ }
2828
+ };
2829
+
2754
2830
  // src/kernel/net/http1.ts
2755
2831
  var HttpError = class extends Error {
2756
2832
  status;
@@ -3711,7 +3787,7 @@ function satisfies(version, range2) {
3711
3787
  }
3712
3788
 
3713
3789
  // src/version.ts
3714
- var KERNEL_VERSION = true ? "1.179.0" : "0.0.0-dev";
3790
+ var KERNEL_VERSION = true ? "1.180.0" : "0.0.0-dev";
3715
3791
  var OS_RELEASE = "6.0.0-slicc";
3716
3792
  function procVersion() {
3717
3793
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -25973,6 +26049,9 @@ var JobTable = class {
25973
26049
  idOf(pid) {
25974
26050
  return this.members.get(pid)?.shownAs ?? pid;
25975
26051
  }
26052
+ execParentOf(pid) {
26053
+ return this.members.get(pid)?.execParent;
26054
+ }
25976
26055
  byId(id2) {
25977
26056
  for (const member of this.members.values()) if (member.shownAs === id2) return member;
25978
26057
  return this.member(id2);
@@ -34180,10 +34259,12 @@ var Launcher = class {
34180
34259
  });
34181
34260
  const fit = await (handle.ready ?? true);
34182
34261
  if (fit) {
34262
+ this.traceAdmitted(handle.pid, true);
34183
34263
  await fds.closeAll();
34184
34264
  return handle;
34185
34265
  }
34186
34266
  await handle.exited;
34267
+ this.traceAdmitted(handle.pid, false);
34187
34268
  await this.memories.refused().catch(async (err) => {
34188
34269
  await fds.closeAll();
34189
34270
  throw err;
@@ -34314,10 +34395,12 @@ var Launcher = class {
34314
34395
  if (req.group === "lead") this.jobs.lead(pid);
34315
34396
  if (req.group === "session") this.jobs.setsid(pid);
34316
34397
  this.described.set(pid, describe(req, terminal?.name ?? null));
34398
+ this.traceStarted(pid, req);
34317
34399
  handle.onState(
34318
34400
  (state) => state === "stopped" ? this.stopped.add(pid) : this.stopped.delete(pid)
34319
34401
  );
34320
- void handle.exited.then(() => {
34402
+ void handle.exited.then((status) => {
34403
+ this.traceEnded(pid, handle, status);
34321
34404
  this.cwds.release(this.cwdSlots.get(pid));
34322
34405
  this.cwdSlots.delete(pid);
34323
34406
  this.cwdPaths.delete(pid);
@@ -34432,6 +34515,71 @@ var Launcher = class {
34432
34515
  imageTimes = /* @__PURE__ */ new Map();
34433
34516
  childTimes = /* @__PURE__ */ new Map();
34434
34517
  stopped = /* @__PURE__ */ new Set();
34518
+ traces = new TraceHub();
34519
+ traceHeld = /* @__PURE__ */ new Map();
34520
+ replacedImages = /* @__PURE__ */ new Set();
34521
+ traceStarted(pid, req) {
34522
+ const replaces = req.exec ? req.ppid : void 0;
34523
+ if (req.quietOom) this.traceHeld.set(pid, { replaces, events: [] });
34524
+ else if (replaces !== void 0) this.replacedImages.add(replaces);
34525
+ this.traceEmit(pid, () => {
34526
+ const { pid: shown2, ppid } = this.identityOf(pid);
34527
+ const described2 = this.described.get(pid);
34528
+ const { argv, started, tty: _tty, exeId: _exeId, ...exe } = described2;
34529
+ return {
34530
+ type: req.fork ? "fork" : "exec",
34531
+ pid: shown2,
34532
+ ppid,
34533
+ pgid: this.jobs.pgidOf(pid),
34534
+ argv: [...argv],
34535
+ ...exe,
34536
+ uid: this.credOf(pid).euid,
34537
+ at: started,
34538
+ ...req.exec ? { replaced: true } : {}
34539
+ };
34540
+ });
34541
+ }
34542
+ traceEnded(pid, handle, status) {
34543
+ if (this.replacedImages.delete(pid)) return;
34544
+ this.traceEmit(pid, () => ({
34545
+ type: "exit",
34546
+ pid: this.jobs.idOf(pid),
34547
+ status,
34548
+ ...signalField(handle.termsig()),
34549
+ ...this.chainTimes(pid),
34550
+ at: Date.now()
34551
+ }));
34552
+ }
34553
+ traceEmit(pid, build) {
34554
+ if (this.traces.idle) return;
34555
+ const tags = this.tagsOf(pid);
34556
+ const cred = this.credOf(pid);
34557
+ if (!this.traces.wants(tags, cred)) return;
34558
+ const held = this.traceHeld.get(pid);
34559
+ if (held) held.events.push({ tags, cred: copyCred(cred), draft: build() });
34560
+ else this.traces.publish(tags, cred, build());
34561
+ }
34562
+ traceAdmitted(pid, fit) {
34563
+ const held = this.traceHeld.get(pid);
34564
+ if (!held) return;
34565
+ this.traceHeld.delete(pid);
34566
+ if (!fit) return;
34567
+ if (held.replaces !== void 0) this.replacedImages.add(held.replaces);
34568
+ for (const { tags, cred, draft } of held.events) this.traces.publish(tags, cred, draft);
34569
+ }
34570
+ chainTimes(pid) {
34571
+ let utime = 0;
34572
+ let stime = 0;
34573
+ let timed2 = false;
34574
+ for (let at = pid; at !== void 0; at = this.jobs.execParentOf(at)) {
34575
+ const t = this.timesAt(at);
34576
+ if (!t) continue;
34577
+ timed2 = true;
34578
+ utime += t.utime;
34579
+ stime += t.stime;
34580
+ }
34581
+ return timed2 ? { utime, stime } : {};
34582
+ }
34435
34583
  stateOf(pid) {
34436
34584
  if (!this.processes.has(pid)) return "Z";
34437
34585
  return this.stopped.has(pid) ? "T" : "S";
@@ -36112,7 +36260,7 @@ function serveTransport(peer, transport, {
36112
36260
  }
36113
36261
 
36114
36262
  // src/client/protocol.ts
36115
- var PROTOCOL = [1, 14];
36263
+ var PROTOCOL = [1, 15];
36116
36264
  var FS_METHODS = [
36117
36265
  "readFile",
36118
36266
  "writeFile",
@@ -36279,9 +36427,21 @@ async function runAs(l, self, requested) {
36279
36427
  function clientCred(self) {
36280
36428
  return self ? credOf(self) : copyCred(ROOT);
36281
36429
  }
36430
+ var MAX_SPAWN_TAGS = 8;
36431
+ function spawnTags(value2) {
36432
+ if (!Array.isArray(value2)) {
36433
+ tagOption(value2);
36434
+ return value2 === void 0 ? [] : [value2];
36435
+ }
36436
+ if (value2.length > MAX_SPAWN_TAGS) {
36437
+ throw new TypeError(`a spawn adds at most ${MAX_SPAWN_TAGS} tags`);
36438
+ }
36439
+ for (const each of value2) tagOption(each ?? 0);
36440
+ return value2;
36441
+ }
36282
36442
  async function runTagged(l, self, options, tag, session = -1) {
36283
- tagOption(options.tag);
36284
- const tags = withTag(withTag(void 0, tag), options.tag);
36443
+ let tags = withTag(void 0, tag);
36444
+ for (const added of spawnTags(options.tag)) tags = withTag(tags, added);
36285
36445
  return {
36286
36446
  user: await runAs(l, self, options.user),
36287
36447
  tag,
@@ -36412,6 +36572,34 @@ async function watchOp(l, req, watches, send2, g) {
36412
36572
  watches.set(req.id, unwatch);
36413
36573
  return { result: req.id };
36414
36574
  }
36575
+ function traceRegistry(send2) {
36576
+ const traces = /* @__PURE__ */ new Map();
36577
+ return {
36578
+ answer(l, self, req) {
36579
+ if (req.op === "untrace") {
36580
+ traces.get(req.trace)?.close();
36581
+ return { result: traces.delete(req.trace) };
36582
+ }
36583
+ if (typeof req.tag !== "string" || req.tag === "") {
36584
+ throw fsError("EINVAL", "trace: a tag is a non-empty string");
36585
+ }
36586
+ const limit = traceLimit(req.limit);
36587
+ const id2 = req.id;
36588
+ const deliver = ({ events, dropped: dropped2 }) => send2({ trace: id2, events, ...dropped2 > 0 ? { dropped: dropped2 } : {} });
36589
+ traces.set(id2, l.traces.subscribe(req.tag, credOf(self), deliver, limit));
36590
+ return { result: id2 };
36591
+ },
36592
+ ack(data) {
36593
+ if (typeof data.traceAck !== "number") return false;
36594
+ traces.get(data.traceAck)?.ack();
36595
+ return true;
36596
+ },
36597
+ close() {
36598
+ for (const trace of traces.values()) trace.close();
36599
+ traces.clear();
36600
+ }
36601
+ };
36602
+ }
36415
36603
  var TRAITS_WATCH = -1;
36416
36604
  function traitsWatch(launcher, transport, send2) {
36417
36605
  return launcher.onTraits(() => send2({ traits: transport.traits }));
@@ -36438,6 +36626,21 @@ function requestBinding(send2, port) {
36438
36626
  close: () => unbind?.()
36439
36627
  };
36440
36628
  }
36629
+ function answered(pending, req, reply, detach) {
36630
+ pending.then(
36631
+ ({ result, signal, transfer }) => {
36632
+ reply(req.id, { result, ...signal ? { signal } : {} }, transfer);
36633
+ if (req.op === "detach") detach(req.kill === true);
36634
+ },
36635
+ (err) => {
36636
+ const code = err?.code;
36637
+ reply(req.id, {
36638
+ error: err instanceof Error ? err.message : String(err),
36639
+ ...typeof code === "string" ? { code } : {}
36640
+ });
36641
+ }
36642
+ );
36643
+ }
36441
36644
  function serveClient(port, host) {
36442
36645
  let state = "new";
36443
36646
  let self;
@@ -36453,6 +36656,7 @@ function serveClient(port, host) {
36453
36656
  };
36454
36657
  const reply = (id2, body, transfer) => send2({ id: id2, ...body }, transfer);
36455
36658
  const hosts = hostRegistries(send2, ended.promise, port);
36659
+ const traces = traceRegistry(send2);
36456
36660
  function detach(kill = false, bye = "the slicc-kernel detached this client") {
36457
36661
  if (state === "closed") return;
36458
36662
  send2({ bye });
@@ -36460,6 +36664,7 @@ function serveClient(port, host) {
36460
36664
  net?.close();
36461
36665
  for (const unwatch of watches.values()) unwatch();
36462
36666
  watches.clear();
36667
+ traces.close();
36463
36668
  hosts.close();
36464
36669
  for (const session of terminals.values()) session.close();
36465
36670
  terminals.clear();
@@ -36567,6 +36772,9 @@ function serveClient(port, host) {
36567
36772
  case "serve-facade":
36568
36773
  case "unserve-facade":
36569
36774
  return { result: hosts.answer(l, req) };
36775
+ case "trace":
36776
+ case "untrace":
36777
+ return traces.answer(l, self, req);
36570
36778
  case "detach":
36571
36779
  return { result: true };
36572
36780
  default:
@@ -36590,20 +36798,9 @@ function serveClient(port, host) {
36590
36798
  net?.answer(data);
36591
36799
  return;
36592
36800
  }
36801
+ if (traces.ack(data)) return;
36593
36802
  const req = data;
36594
- handle(req).then(
36595
- ({ result, signal, transfer }) => {
36596
- reply(req.id, { result, ...signal ? { signal } : {} }, transfer);
36597
- if (req.op === "detach") detach(req.kill === true);
36598
- },
36599
- (err) => {
36600
- const code = err?.code;
36601
- reply(req.id, {
36602
- error: err instanceof Error ? err.message : String(err),
36603
- ...typeof code === "string" ? { code } : {}
36604
- });
36605
- }
36606
- );
36803
+ answered(handle(req), req, reply, detach);
36607
36804
  });
36608
36805
  port.addEventListener("close", () => detach());
36609
36806
  port.start?.();
@@ -37532,6 +37729,7 @@ var Channel = class {
37532
37729
  failure;
37533
37730
  pending = /* @__PURE__ */ new Map();
37534
37731
  watching = /* @__PURE__ */ new Map();
37732
+ tracing = /* @__PURE__ */ new Map();
37535
37733
  cdpHooks = /* @__PURE__ */ new Map();
37536
37734
  cdpOpen = /* @__PURE__ */ new Set();
37537
37735
  facadeHooks = /* @__PURE__ */ new Map();
@@ -37599,6 +37797,10 @@ var Channel = class {
37599
37797
  this.watching.get(data.watch)?.(data.change);
37600
37798
  return;
37601
37799
  }
37800
+ if (data.trace !== void 0 && data.id === void 0) {
37801
+ this.traced(data.trace, data.events ?? [], data.dropped ?? 0);
37802
+ return;
37803
+ }
37602
37804
  if (data.bye !== void 0) {
37603
37805
  this.end(new KernelGoneError(data.bye));
37604
37806
  return;
@@ -37627,6 +37829,23 @@ var Channel = class {
37627
37829
  if (reply.error !== void 0) call.reject(new KernelCallError(reply.error, reply.code));
37628
37830
  else call.resolve(reply.result);
37629
37831
  }
37832
+ traced(trace, events, dropped2) {
37833
+ const listener = this.tracing.get(trace);
37834
+ if (!listener) return;
37835
+ const ack = () => {
37836
+ if (this.tracing.has(trace)) this.post({ traceAck: trace });
37837
+ };
37838
+ let consumed;
37839
+ try {
37840
+ consumed = listener({ events, dropped: dropped2 });
37841
+ } finally {
37842
+ if (typeof consumed?.then === "function") {
37843
+ void Promise.resolve(consumed).then(ack, ack);
37844
+ } else {
37845
+ ack();
37846
+ }
37847
+ }
37848
+ }
37630
37849
  openCdp({ cdpOpen: { registration, ...request }, port }) {
37631
37850
  const hook = registration === void 0 ? void 0 : this.cdpHooks.get(registration);
37632
37851
  if (!hook || this.failure) {
@@ -37689,6 +37908,29 @@ async function serveCdpOn(channel, hook, options) {
37689
37908
  }
37690
37909
  };
37691
37910
  }
37911
+ async function traceOn(channel, tag, onEvents, options) {
37912
+ if (typeof onEvents !== "function") throw new TypeError("trace: onEvents must be a function");
37913
+ await since(channel, 15, "trace", async () => {
37914
+ });
37915
+ const { id: id2, done: done4 } = channel.request({
37916
+ op: "trace",
37917
+ tag,
37918
+ ...options.limit !== void 0 ? { limit: options.limit } : {}
37919
+ });
37920
+ channel.tracing.set(id2, onEvents);
37921
+ try {
37922
+ await done4;
37923
+ } catch (error) {
37924
+ channel.tracing.delete(id2);
37925
+ throw error;
37926
+ }
37927
+ return {
37928
+ close: () => {
37929
+ if (!channel.tracing.delete(id2)) return;
37930
+ void channel.call({ op: "untrace", trace: id2 }).catch(() => void 0);
37931
+ }
37932
+ };
37933
+ }
37692
37934
  async function dialOn(channel, { port, host }) {
37693
37935
  const req = { op: "dial", port, ...host ? { host } : {} };
37694
37936
  return dialStream(await since(channel, 4, "dial", () => channel.call(req)));
@@ -37702,6 +37944,8 @@ async function spawnOn(channel, argv, opts) {
37702
37944
  });
37703
37945
  if (opts.tag !== void 0) await since(channel, 12, "a tag per spawn", async () => {
37704
37946
  });
37947
+ if (Array.isArray(opts.tag)) await since(channel, 15, "several tags per spawn", async () => {
37948
+ });
37705
37949
  if (opts.cwdMissing === "error") await since(channel, 14, "cwdMissing: 'error'", async () => {
37706
37950
  });
37707
37951
  const started = Promise.withResolvers();
@@ -37763,6 +38007,10 @@ async function terminalOn(channel, argv, opts) {
37763
38007
  });
37764
38008
  if (opts.tag !== void 0) await since(channel, 12, "a tag per terminal", async () => {
37765
38009
  });
38010
+ if (Array.isArray(opts.tag)) {
38011
+ await since(channel, 15, "several tags per terminal", async () => {
38012
+ });
38013
+ }
37766
38014
  if (opts.cwdMissing === "error") await since(channel, 14, "cwdMissing: 'error'", async () => {
37767
38015
  });
37768
38016
  let listener = onData ?? null;
@@ -37922,6 +38170,7 @@ async function attachKernel(port, options = {}) {
37922
38170
  channel.traitsListeners.add(listener);
37923
38171
  return () => channel.traitsListeners.delete(listener);
37924
38172
  },
38173
+ trace: (tag, onEvents, options2 = {}) => traceOn(channel, tag, onEvents, options2),
37925
38174
  async close(o = {}) {
37926
38175
  if (channel.failure) return;
37927
38176
  await channel.call({ op: "detach", ...o.kill ? { kill: true } : {} }).catch(() => void 0);