@ai-ecoverse/slicc-kernel 1.106.3 → 1.107.1

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.
@@ -19,9 +19,9 @@ var PathMap = class {
19
19
  keys() {
20
20
  return this.values.keys();
21
21
  }
22
- set(path, value) {
22
+ set(path, value2) {
23
23
  if (!this.values.has(path)) this.link(path);
24
- this.values.set(path, value);
24
+ this.values.set(path, value2);
25
25
  return this;
26
26
  }
27
27
  delete(path) {
@@ -136,9 +136,9 @@ var MemoryMeta = class {
136
136
  function descendants(path) {
137
137
  return IDBKeyRange.bound(`${path}/`, `${path}0`, false, true);
138
138
  }
139
- function settle(transaction, value) {
139
+ function settle(transaction, value2) {
140
140
  return new Promise((resolve2, reject) => {
141
- transaction.oncomplete = () => resolve2(value());
141
+ transaction.oncomplete = () => resolve2(value2());
142
142
  transaction.onabort = () => reject(transaction.error ?? new Error("metadata transaction aborted"));
143
143
  });
144
144
  }
@@ -834,8 +834,8 @@ var ProtectedFs = class {
834
834
  for (let at = fs; at && at !== Object.prototype; at = Object.getPrototypeOf(at)) {
835
835
  for (const name2 of Object.getOwnPropertyNames(at)) {
836
836
  if (name2 === "constructor" || Object.hasOwn(view2, name2)) continue;
837
- const value = fs[name2];
838
- view2[name2] = typeof value === "function" ? value.bind(fs) : value;
837
+ const value2 = fs[name2];
838
+ view2[name2] = typeof value2 === "function" ? value2.bind(fs) : value2;
839
839
  }
840
840
  }
841
841
  return Object.assign(view2, own);
@@ -965,8 +965,8 @@ function view(target, overrides) {
965
965
  if (keys.has(prop)) return overrides[prop];
966
966
  return Reflect.get(t, prop, receiver);
967
967
  },
968
- set(t, prop, value) {
969
- return Reflect.set(t, prop, value);
968
+ set(t, prop, value2) {
969
+ return Reflect.set(t, prop, value2);
970
970
  },
971
971
  getPrototypeOf(t) {
972
972
  return Reflect.getPrototypeOf(t);
@@ -2160,9 +2160,9 @@ function memoryMedia() {
2160
2160
  put: async (id2, handle) => void handles.set(id2, handle)
2161
2161
  };
2162
2162
  }
2163
- function settle2(request2, value) {
2163
+ function settle2(request2, value2) {
2164
2164
  return new Promise((resolve2, reject) => {
2165
- request2.onsuccess = () => resolve2(value());
2165
+ request2.onsuccess = () => resolve2(value2());
2166
2166
  request2.onerror = () => reject(request2.error);
2167
2167
  });
2168
2168
  }
@@ -2718,12 +2718,12 @@ function userCred(uid, gid, groups = [gid]) {
2718
2718
  function copyCred(cred) {
2719
2719
  return { ...cred, groups: [...cred.groups] };
2720
2720
  }
2721
- function id(value) {
2722
- if (value === void 0 || value === -1 || value === 4294967295) return void 0;
2723
- if (!Number.isInteger(value) || value < 0 || value > MAX_ID) {
2724
- throw fsError("EINVAL", `not a user or group id: ${String(value)}`);
2721
+ function id(value2) {
2722
+ if (value2 === void 0 || value2 === -1 || value2 === 4294967295) return void 0;
2723
+ if (!Number.isInteger(value2) || value2 < 0 || value2 > MAX_ID) {
2724
+ throw fsError("EINVAL", `not a user or group id: ${String(value2)}`);
2725
2725
  }
2726
- return value;
2726
+ return value2;
2727
2727
  }
2728
2728
  function changeCred(cur, change) {
2729
2729
  if (typeof change !== "object" || change === null || Array.isArray(change)) {
@@ -2734,10 +2734,10 @@ function changeCred(cur, change) {
2734
2734
  for (const keys of [UIDS, GIDS]) {
2735
2735
  const own = keys.map((key) => cur[key]);
2736
2736
  for (const key of keys) {
2737
- const value = id(change[key]);
2738
- if (value === void 0) continue;
2739
- if (!privileged && !own.includes(value)) throw fsError("EPERM", `${key} ${value}`);
2740
- next3[key] = value;
2737
+ const value2 = id(change[key]);
2738
+ if (value2 === void 0) continue;
2739
+ if (!privileged && !own.includes(value2)) throw fsError("EPERM", `${key} ${value2}`);
2740
+ next3[key] = value2;
2741
2741
  }
2742
2742
  }
2743
2743
  if (change.groups !== void 0) {
@@ -3348,11 +3348,11 @@ function ipv6Groups(text) {
3348
3348
  } catch {
3349
3349
  return void 0;
3350
3350
  }
3351
- const [head, tail] = canonical.split("::");
3351
+ const [head, tail2] = canonical.split("::");
3352
3352
  const part = (s) => s ? s.split(":").map((h) => Number.parseInt(h, 16)) : [];
3353
3353
  const front = part(head);
3354
- const back = part(tail);
3355
- return tail === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
3354
+ const back = part(tail2);
3355
+ return tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
3356
3356
  }
3357
3357
  function toBits(values, width) {
3358
3358
  return values.reduce((acc, v) => acc << BigInt(width) | BigInt(v), 0n);
@@ -3834,8 +3834,8 @@ var KernelSocket = class _KernelSocket {
3834
3834
  }
3835
3835
  return this.options.get(`${level}:${name2}`) ?? 0;
3836
3836
  }
3837
- setOption(level, name2, value) {
3838
- this.options.set(`${level}:${name2}`, value);
3837
+ setOption(level, name2, value2) {
3838
+ this.options.set(`${level}:${name2}`, value2);
3839
3839
  }
3840
3840
  close() {
3841
3841
  const was = this.state;
@@ -5668,15 +5668,15 @@ function concat(chunks, total = chunks.reduce((n, c) => n + c.length, 0)) {
5668
5668
  }
5669
5669
  return out;
5670
5670
  }
5671
- function safeValue(value) {
5672
- return !/[\r\n\0]/.test(value);
5671
+ function safeValue(value2) {
5672
+ return !/[\r\n\0]/.test(value2);
5673
5673
  }
5674
5674
  function responseHead(status, reason, headers) {
5675
5675
  const phrase = /^[\t\x20-\x7e\x80-\xff]*$/.test(reason) ? reason : "";
5676
5676
  let head = `HTTP/1.1 ${status} ${phrase}\r
5677
5677
  `;
5678
- for (const [name2, value] of headers) {
5679
- if (TOKEN.test(name2) && safeValue(value)) head += `${name2}: ${value}\r
5678
+ for (const [name2, value2] of headers) {
5679
+ if (TOKEN.test(name2) && safeValue(value2)) head += `${name2}: ${value2}\r
5680
5680
  `;
5681
5681
  }
5682
5682
  return latin1Bytes(`${head}\r
@@ -5989,12 +5989,12 @@ function localV4(a, b) {
5989
5989
  return a === 127 || a === 0 || a === 169 && b === 254;
5990
5990
  }
5991
5991
  function hextets(address) {
5992
- const [head, tail, extra] = address.split("::");
5992
+ const [head, tail2, extra] = address.split("::");
5993
5993
  if (extra !== void 0) return void 0;
5994
5994
  const part = (text) => text ? text.split(":").map((h) => Number.parseInt(h, 16)) : [];
5995
5995
  const front = part(head);
5996
- const back = part(tail);
5997
- const groups = tail === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
5996
+ const back = part(tail2);
5997
+ const groups = tail2 === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
5998
5998
  return groups.length === 8 && groups.every((g) => g >= 0 && g <= 65535) ? groups : void 0;
5999
5999
  }
6000
6000
  function localV6(address) {
@@ -6551,8 +6551,8 @@ function name(dn) {
6551
6551
  rdns.push(set(seq(oid("2.5.4.3"), utf8(dn.commonName))));
6552
6552
  return seq(...rdns);
6553
6553
  }
6554
- function extension(id2, critical, value) {
6555
- return critical ? seq(oid(id2), TRUE, octets(value)) : seq(oid(id2), octets(value));
6554
+ function extension(id2, critical, value2) {
6555
+ return critical ? seq(oid(id2), TRUE, octets(value2)) : seq(oid(id2), octets(value2));
6556
6556
  }
6557
6557
  function children(der) {
6558
6558
  const out = [];
@@ -6609,11 +6609,11 @@ function ipv6(host) {
6609
6609
  if (!host.includes(":") || !/^[0-9A-Fa-f:.]+$/.test(host)) return void 0;
6610
6610
  const v4 = /(\d{1,3}(?:\.\d{1,3}){3})$/.exec(host);
6611
6611
  let text = host;
6612
- const tail = [];
6612
+ const tail2 = [];
6613
6613
  if (v4) {
6614
6614
  const bytes = ipv4(v4[1]);
6615
6615
  if (!bytes) return void 0;
6616
- tail.push(bytes[0] << 8 | bytes[1], bytes[2] << 8 | bytes[3]);
6616
+ tail2.push(bytes[0] << 8 | bytes[1], bytes[2] << 8 | bytes[3]);
6617
6617
  text = host.slice(0, -v4[1].length).replace(/:$/, host.endsWith(`::${v4[1]}`) ? ":" : "");
6618
6618
  }
6619
6619
  const halves = text.split("::");
@@ -6623,13 +6623,13 @@ function ipv6(host) {
6623
6623
  const back = halves.length === 2 ? groups(halves[1]) : [];
6624
6624
  if (![...head, ...back].every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g))) return void 0;
6625
6625
  const words2 = [...head, ...back].map((g) => Number.parseInt(g, 16));
6626
- const missing4 = 8 - words2.length - tail.length;
6626
+ const missing4 = 8 - words2.length - tail2.length;
6627
6627
  if (halves.length === 2 ? missing4 < 1 : missing4 !== 0) return void 0;
6628
6628
  const all = [
6629
6629
  ...head.map((g) => Number.parseInt(g, 16)),
6630
6630
  ...new Array(halves.length === 2 ? missing4 : 0).fill(0),
6631
6631
  ...back.map((g) => Number.parseInt(g, 16)),
6632
- ...tail
6632
+ ...tail2
6633
6633
  ];
6634
6634
  return Uint8Array.from(all.flatMap((w) => [w >> 8, w & 255]));
6635
6635
  }
@@ -7556,8 +7556,8 @@ Connection: close\r
7556
7556
  out.set(bytes, head.length);
7557
7557
  return out;
7558
7558
  }
7559
- function json(value) {
7560
- return respond(200, `${JSON.stringify(value, null, 2)}
7559
+ function json(value2) {
7560
+ return respond(200, `${JSON.stringify(value2, null, 2)}
7561
7561
  `, "application/json; charset=UTF-8");
7562
7562
  }
7563
7563
  var CdpFacade = class {
@@ -7955,9 +7955,9 @@ function comparator(token) {
7955
7955
  function set2(text) {
7956
7956
  const trimmed = text.trim().replace(/(<=|>=|<|>|=|\^|~>?)\s+/g, "$1");
7957
7957
  if (trimmed === "") return ANY;
7958
- const range = /^(\S+)\s+-\s+(\S+)$/.exec(trimmed);
7959
- if (range) {
7960
- const [from, to] = [partial(range[1]), partial(range[2])];
7958
+ const range2 = /^(\S+)\s+-\s+(\S+)$/.exec(trimmed);
7959
+ if (range2) {
7960
+ const [from, to] = [partial(range2[1]), partial(range2[2])];
7961
7961
  return from && to ? hyphen(from, to) : void 0;
7962
7962
  }
7963
7963
  const out = [];
@@ -7976,16 +7976,16 @@ function holds(v, [op, w]) {
7976
7976
  if (op === ">=") return c >= 0;
7977
7977
  return c === 0;
7978
7978
  }
7979
- function satisfies(version, range) {
7979
+ function satisfies(version, range2) {
7980
7980
  const v = parseVersion(version);
7981
7981
  if (!v) return void 0;
7982
- const sets = range.split("||").map(set2);
7982
+ const sets = range2.split("||").map(set2);
7983
7983
  if (sets.some((s) => s === void 0)) return void 0;
7984
7984
  return sets.some((s) => s.every((c) => holds(v, c)));
7985
7985
  }
7986
7986
 
7987
7987
  // src/version.ts
7988
- var KERNEL_VERSION = true ? "1.106.3" : "0.0.0-dev";
7988
+ var KERNEL_VERSION = true ? "1.107.1" : "0.0.0-dev";
7989
7989
  var OS_RELEASE = "6.0.0-slicc";
7990
7990
  function procVersion() {
7991
7991
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -8009,19 +8009,19 @@ function inside(pkg, rel2) {
8009
8009
  function envOf(pkg, raw) {
8010
8010
  const env = {};
8011
8011
  if (!raw || typeof raw !== "object") return env;
8012
- for (const [key, value] of Object.entries(raw)) {
8012
+ for (const [key, value2] of Object.entries(raw)) {
8013
8013
  if (!ENV_KEY.test(key)) continue;
8014
- if (typeof value === "string") env[key] = value.replace(PACKAGE, pkg);
8015
- else if (value === null) env[key] = null;
8014
+ if (typeof value2 === "string") env[key] = value2.replace(PACKAGE, pkg);
8015
+ else if (value2 === null) env[key] = null;
8016
8016
  }
8017
8017
  return env;
8018
8018
  }
8019
8019
  function envFields(merged2) {
8020
8020
  const env = {};
8021
8021
  const unset = [];
8022
- for (const [key, value] of Object.entries(merged2)) {
8023
- if (value === null) unset.push(key);
8024
- else env[key] = value;
8022
+ for (const [key, value2] of Object.entries(merged2)) {
8023
+ if (value2 === null) unset.push(key);
8024
+ else env[key] = value2;
8025
8025
  }
8026
8026
  return {
8027
8027
  ...Object.keys(env).length > 0 ? { env } : {},
@@ -8100,9 +8100,9 @@ function commandsOf(pkg, manifest) {
8100
8100
  async function withPackagePaths(fs, pkg, command) {
8101
8101
  if (!command.env) return command;
8102
8102
  const env = {};
8103
- for (const [key, value] of Object.entries(command.env)) {
8104
- const path = !value.startsWith("/") && value.includes("/") && inside(pkg, value);
8105
- env[key] = path && await fs.exists(path) ? path : value;
8103
+ for (const [key, value2] of Object.entries(command.env)) {
8104
+ const path = !value2.startsWith("/") && value2.includes("/") && inside(pkg, value2);
8105
+ env[key] = path && await fs.exists(path) ? path : value2;
8106
8106
  }
8107
8107
  return { ...command, env };
8108
8108
  }
@@ -8153,7 +8153,7 @@ function physicalResolver(fs) {
8153
8153
  const known = /* @__PURE__ */ new Map();
8154
8154
  const pending = /* @__PURE__ */ new Set();
8155
8155
  let hops = 0;
8156
- const step = async (path) => {
8156
+ const step2 = async (path) => {
8157
8157
  if (path === "/") return { path, file: false };
8158
8158
  const slash = path.lastIndexOf("/");
8159
8159
  const parent = await resolve2(path.slice(0, slash) || "/");
@@ -8170,7 +8170,7 @@ function physicalResolver(fs) {
8170
8170
  let found = known.get(path);
8171
8171
  if (!found) {
8172
8172
  pending.add(path);
8173
- found = step(path).finally(() => pending.delete(path));
8173
+ found = step2(path).finally(() => pending.delete(path));
8174
8174
  known.set(path, found);
8175
8175
  }
8176
8176
  return found;
@@ -8205,15 +8205,15 @@ async function binCommandsOf(physical, pkg, manifest, taken) {
8205
8205
  }
8206
8206
  function engineRefusal(manifest, kernel) {
8207
8207
  const engines = manifest.engines;
8208
- const range = engines && typeof engines === "object" ? engines["slicc-kernel"] : void 0;
8208
+ const range2 = engines && typeof engines === "object" ? engines["slicc-kernel"] : void 0;
8209
8209
  const running = parseVersion(kernel);
8210
- if (typeof range !== "string" || !running || running.slice(0, 3).every((n) => n === 0)) {
8210
+ if (typeof range2 !== "string" || !running || running.slice(0, 3).every((n) => n === 0)) {
8211
8211
  return void 0;
8212
8212
  }
8213
- const fits = satisfies(kernel, range);
8213
+ const fits = satisfies(kernel, range2);
8214
8214
  if (fits) return void 0;
8215
8215
  const who = `${String(manifest.name ?? "?")}@${String(manifest.version ?? "?")}`;
8216
- return fits === false ? `${who} needs slicc-kernel ${range}; this kernel is ${kernel}` : `${who} has an engines.slicc-kernel range this kernel can't read: ${JSON.stringify(range)}`;
8216
+ return fits === false ? `${who} needs slicc-kernel ${range2}; this kernel is ${kernel}` : `${who} has an engines.slicc-kernel range this kernel can't read: ${JSON.stringify(range2)}`;
8217
8217
  }
8218
8218
  var PACKAGE_NAME = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
8219
8219
  function replacesOf(manifest) {
@@ -8344,6 +8344,795 @@ function binfmtOf(binfmts, path) {
8344
8344
  return best === void 0 ? void 0 : binfmts.get(best);
8345
8345
  }
8346
8346
 
8347
+ // src/fs/unlinked.ts
8348
+ var UNLINKED_DIR = ".slicc-unlinked";
8349
+ function webLocks() {
8350
+ return globalThis.navigator?.locks;
8351
+ }
8352
+ var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
8353
+ var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
8354
+ var lockName = (instance) => `slicc-unlinked:${instance}`;
8355
+ var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
8356
+ var PathGate = class {
8357
+ tails = /* @__PURE__ */ new Map();
8358
+ async acquire(path) {
8359
+ const prev = this.tails.get(path) ?? Promise.resolve();
8360
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
8361
+ const tail2 = prev.then(() => promise);
8362
+ this.tails.set(path, tail2);
8363
+ await prev;
8364
+ return () => {
8365
+ resolve2();
8366
+ if (this.tails.get(path) === tail2) this.tails.delete(path);
8367
+ };
8368
+ }
8369
+ async run(path, op) {
8370
+ const release = await this.acquire(path);
8371
+ try {
8372
+ return await op();
8373
+ } finally {
8374
+ release();
8375
+ }
8376
+ }
8377
+ };
8378
+ var UnlinkedKeeper = class {
8379
+ fs;
8380
+ holders;
8381
+ rootOf;
8382
+ locks;
8383
+ busy;
8384
+ waiting = /* @__PURE__ */ new Set();
8385
+ owner;
8386
+ instance;
8387
+ gate = new PathGate();
8388
+ queue = Promise.resolve();
8389
+ count = 0;
8390
+ links = /* @__PURE__ */ new Map();
8391
+ linked = /* @__PURE__ */ new Map();
8392
+ dirOwner;
8393
+ constructor(fs, holders, rootOf, options = {}) {
8394
+ const { locks, owner } = options;
8395
+ this.dirOwner = options.dirOwner;
8396
+ this.fs = fs;
8397
+ this.holders = holders;
8398
+ this.rootOf = rootOf;
8399
+ this.locks = locks;
8400
+ this.busy = options.busy ?? (() => false);
8401
+ this.owner = owner;
8402
+ this.instance = owner ? `${owner.id}~${crypto.randomUUID()}` : crypto.randomUUID();
8403
+ void locks?.request(lockName(this.instance), () => new Promise(() => void 0));
8404
+ }
8405
+ serial(op) {
8406
+ const next3 = this.queue.then(op);
8407
+ this.queue = next3.catch(() => void 0);
8408
+ return next3;
8409
+ }
8410
+ wrap(fs = this.fs) {
8411
+ const keeper = this;
8412
+ const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
8413
+ const counted2 = (st) => {
8414
+ const hidden = keeper.links.get(`${st.dev ?? 0}:${st.ino}`) ?? 0;
8415
+ return hidden === 0 || st.nlink === void 0 ? st : { ...st, nlink: Math.max(0, st.nlink - hidden) };
8416
+ };
8417
+ return {
8418
+ ...fs,
8419
+ links: (path) => mayLink(fs, path),
8420
+ stat: async (path) => counted2(await fs.stat(path)),
8421
+ lstat: async (path) => counted2(await fs.lstat(path)),
8422
+ async rm(path, options) {
8423
+ const target = fs.resolvePath("/", path);
8424
+ const pinned = await keeper.pin(target);
8425
+ if (pinned.moved.has(target)) pinned.commit();
8426
+ else await keeper.settle(pinned, () => fs.rm(path, options));
8427
+ },
8428
+ async rename(from, to) {
8429
+ const target = fs.resolvePath("/", to);
8430
+ if (target === fs.resolvePath("/", from)) return fs.rename(from, to);
8431
+ const pinned = await keeper.pin(target);
8432
+ await keeper.settle(pinned, () => fs.rename(from, to));
8433
+ },
8434
+ readdir: async (path) => visible(await fs.readdir(path)),
8435
+ readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted2(st)])
8436
+ };
8437
+ }
8438
+ async settle(pinned, op) {
8439
+ try {
8440
+ await op();
8441
+ } catch (err) {
8442
+ await pinned.rollback();
8443
+ throw err;
8444
+ }
8445
+ pinned.commit();
8446
+ }
8447
+ async pin(target) {
8448
+ const held = /* @__PURE__ */ new Map();
8449
+ for (const holder of this.holders) {
8450
+ if (holder.owns(target)) continue;
8451
+ for (const path of holder.held()) {
8452
+ if (!within4(path, target) || holder.isKept(path)) continue;
8453
+ held.set(path, [...held.get(path) ?? [], holder]);
8454
+ }
8455
+ }
8456
+ const paths = [...held.keys()].sort();
8457
+ const releases = [];
8458
+ for (const path of paths) releases.push(await this.gate.acquire(path));
8459
+ const hidden = /* @__PURE__ */ new Map();
8460
+ for (const path of paths) {
8461
+ const at = await this.hide(path);
8462
+ if (at !== void 0) hidden.set(path, at);
8463
+ }
8464
+ const done4 = () => {
8465
+ for (const release of releases) release();
8466
+ };
8467
+ const files = /* @__PURE__ */ new Map();
8468
+ for (const [path, at] of hidden) {
8469
+ const holders = held.get(path);
8470
+ const file = this.keptFile(at, { refs: holders.length + 1 });
8471
+ files.set(path, file);
8472
+ for (const holder of holders) holder.keep(path, file);
8473
+ }
8474
+ return {
8475
+ moved: new Set(hidden.keys()),
8476
+ commit: () => {
8477
+ for (const file of files.values()) file.release();
8478
+ done4();
8479
+ },
8480
+ rollback: async () => {
8481
+ for (const [path, file] of files) {
8482
+ for (const holder of held.get(path)) holder.unkeep(path, file);
8483
+ await this.serial(async () => {
8484
+ await Promise.allSettled([this.fs.rename(file.hidden, path)]);
8485
+ this.unlinkedName(file.hidden);
8486
+ await this.tidy(parentOf5(file.hidden));
8487
+ });
8488
+ }
8489
+ done4();
8490
+ }
8491
+ };
8492
+ }
8493
+ keptFile(hidden, entry) {
8494
+ return {
8495
+ hidden,
8496
+ release: () => {
8497
+ if (--entry.refs === 0) void this.serial(() => this.drop(hidden));
8498
+ }
8499
+ };
8500
+ }
8501
+ hide(path) {
8502
+ return this.serial(async () => {
8503
+ const dir = dirOf(this.rootOf(path));
8504
+ const hidden = `${dir}/${this.instance}.${++this.count}`;
8505
+ try {
8506
+ const owner = this.dirOwner;
8507
+ await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
8508
+ await this.fs.rename(path, hidden);
8509
+ await this.linkedName(hidden);
8510
+ return hidden;
8511
+ } catch {
8512
+ await this.tidy(dir);
8513
+ return void 0;
8514
+ }
8515
+ });
8516
+ }
8517
+ closed(path) {
8518
+ if (this.waiting.delete(path)) void this.serial(() => this.drop(path));
8519
+ }
8520
+ async drop(hidden) {
8521
+ if (this.busy(hidden)) {
8522
+ this.waiting.add(hidden);
8523
+ return;
8524
+ }
8525
+ await this.fs.rm(hidden).catch(() => void 0);
8526
+ this.unlinkedName(hidden);
8527
+ await this.tidy(parentOf5(hidden));
8528
+ }
8529
+ async linkedName(hidden) {
8530
+ if (!mayLink(this.fs, hidden)) return;
8531
+ const st = await this.fs.lstat(hidden).catch(() => void 0);
8532
+ if (st?.nlink === void 0) return;
8533
+ const id2 = `${st.dev ?? 0}:${st.ino}`;
8534
+ this.linked.set(hidden, id2);
8535
+ this.links.set(id2, (this.links.get(id2) ?? 0) + 1);
8536
+ }
8537
+ unlinkedName(hidden) {
8538
+ const id2 = this.linked.get(hidden);
8539
+ if (id2 === void 0) return;
8540
+ this.linked.delete(hidden);
8541
+ const left = (this.links.get(id2) ?? 1) - 1;
8542
+ if (left > 0) this.links.set(id2, left);
8543
+ else this.links.delete(id2);
8544
+ }
8545
+ async tidy(dir) {
8546
+ const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
8547
+ if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
8548
+ }
8549
+ sweep(root) {
8550
+ const query = this.locks?.query?.bind(this.locks);
8551
+ const owner = this.owner;
8552
+ if (!query && !owner) return Promise.resolve();
8553
+ return this.serial(async () => {
8554
+ const dir = dirOf(root);
8555
+ const names = await this.fs.readdir(dir).catch(() => void 0);
8556
+ if (!names) return;
8557
+ const held = query && new Set(((await query()).held ?? []).map((lock) => lock.name));
8558
+ for (const name2 of names) {
8559
+ const id2 = name2.slice(0, name2.lastIndexOf("."));
8560
+ const at = id2.indexOf("~");
8561
+ const dead = id2 !== this.instance && (at < 0 ? held !== void 0 && !held.has(lockName(id2)) : owner?.alive(id2.slice(0, at)) === false);
8562
+ if (dead) await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
8563
+ }
8564
+ await this.tidy(dir);
8565
+ });
8566
+ }
8567
+ };
8568
+
8569
+ // src/cron/clock.ts
8570
+ var systemCronClock = {
8571
+ now: () => Date.now(),
8572
+ sleep(until, signal) {
8573
+ if (signal.aborted) return Promise.resolve();
8574
+ return new Promise((resolve2) => {
8575
+ const done4 = () => {
8576
+ clearTimeout(timer);
8577
+ signal.removeEventListener("abort", done4);
8578
+ resolve2();
8579
+ };
8580
+ const timer = setTimeout(done4, Math.max(0, until - Date.now()));
8581
+ timer.unref?.();
8582
+ signal.addEventListener("abort", done4, { once: true });
8583
+ });
8584
+ }
8585
+ };
8586
+
8587
+ // src/cron/schedule.ts
8588
+ var MISSED_CAP = 1e5;
8589
+ var MACROS = {
8590
+ "@yearly": "0 0 1 1 *",
8591
+ "@annually": "0 0 1 1 *",
8592
+ "@monthly": "0 0 1 * *",
8593
+ "@weekly": "0 0 * * 0",
8594
+ "@daily": "0 0 * * *",
8595
+ "@midnight": "0 0 * * *",
8596
+ "@hourly": "0 * * * *"
8597
+ };
8598
+ var MONTHS = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
8599
+ var WEEKDAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
8600
+ var FIELDS = [
8601
+ { name: "minute", min: 0, max: 59 },
8602
+ { name: "hour", min: 0, max: 23 },
8603
+ { name: "day of month", min: 1, max: 31 },
8604
+ { name: "month", min: 1, max: 12, names: MONTHS, offset: 1 },
8605
+ { name: "day of week", min: 0, max: 7, names: WEEKDAYS }
8606
+ ];
8607
+ function value(text, field4) {
8608
+ const named = field4.names?.indexOf(text.toLowerCase()) ?? -1;
8609
+ if (named >= 0) return named + (field4.offset ?? 0);
8610
+ if (!/^\d+$/.test(text)) throw new Error(`${field4.name}: "${text}" is not a number`);
8611
+ const number = Number(text);
8612
+ if (number < field4.min || number > field4.max)
8613
+ throw new Error(`${field4.name}: ${number} is outside ${field4.min}-${field4.max}`);
8614
+ return number;
8615
+ }
8616
+ function range(part, field4) {
8617
+ if (part === "*") return [field4.min, field4.max];
8618
+ const [from, to, extra] = part.split("-");
8619
+ if (extra !== void 0) throw new Error(`${field4.name}: "${part}" is not a range`);
8620
+ const start2 = value(from, field4);
8621
+ const end = to === void 0 ? start2 : value(to, field4);
8622
+ if (end < start2) throw new Error(`${field4.name}: range ${part} runs backwards`);
8623
+ return [start2, end];
8624
+ }
8625
+ function step(text, field4) {
8626
+ if (text === void 0) return 1;
8627
+ if (!/^\d+$/.test(text) || Number(text) === 0)
8628
+ throw new Error(`${field4.name}: step "${text}" must be a positive number`);
8629
+ return Number(text);
8630
+ }
8631
+ function field2(text, spec) {
8632
+ const out = /* @__PURE__ */ new Set();
8633
+ for (const part of text.split(",")) {
8634
+ if (part === "") throw new Error(`${spec.name}: empty list item`);
8635
+ const [base, every, extra] = part.split("/");
8636
+ if (extra !== void 0) throw new Error(`${spec.name}: "${part}" has two steps`);
8637
+ const [start2, end] = range(base, spec);
8638
+ const stride = step(every, spec);
8639
+ const last = every !== void 0 && !base.includes("-") ? spec.max : end;
8640
+ for (let n = start2; n <= last; n += stride) out.add(n);
8641
+ }
8642
+ return out;
8643
+ }
8644
+ function parseSchedule(expression) {
8645
+ const trimmed = expression.trim();
8646
+ const text = MACROS[trimmed.toLowerCase()] ?? trimmed;
8647
+ if (text.startsWith("@")) throw new Error(`unknown schedule ${trimmed}`);
8648
+ const parts2 = text.split(/\s+/);
8649
+ if (parts2.length !== 5)
8650
+ throw new Error(
8651
+ `"${trimmed}" needs 5 fields (minute hour day month weekday), not ${parts2.length}`
8652
+ );
8653
+ const [minutes, hours, days, months, weekdays] = parts2.map(
8654
+ (part, at) => field2(part, FIELDS[at])
8655
+ );
8656
+ if (weekdays.delete(7)) weekdays.add(0);
8657
+ return {
8658
+ minutes,
8659
+ hours,
8660
+ days,
8661
+ months,
8662
+ weekdays,
8663
+ anyDay: parts2[2].startsWith("*"),
8664
+ anyWeekday: parts2[4].startsWith("*")
8665
+ };
8666
+ }
8667
+ function parseWhen(expression) {
8668
+ if (expression.trim().toLowerCase() === "@reboot") return { reboot: true };
8669
+ return { reboot: false, schedule: parseSchedule(expression) };
8670
+ }
8671
+ var LOCAL = {
8672
+ year: (d) => d.getFullYear(),
8673
+ month: (d) => d.getMonth(),
8674
+ date: (d) => d.getDate(),
8675
+ day: (d) => d.getDay(),
8676
+ hours: (d) => d.getHours(),
8677
+ minutes: (d) => d.getMinutes(),
8678
+ setMonth: (d, month) => d.setMonth(month, 1),
8679
+ setDate: (d, date) => d.setDate(date),
8680
+ setHours: (d, hours) => d.setHours(hours, 0, 0, 0),
8681
+ setMinutes: (d, minutes) => d.setMinutes(minutes, 0, 0)
8682
+ };
8683
+ var UTC = {
8684
+ year: (d) => d.getUTCFullYear(),
8685
+ month: (d) => d.getUTCMonth(),
8686
+ date: (d) => d.getUTCDate(),
8687
+ day: (d) => d.getUTCDay(),
8688
+ hours: (d) => d.getUTCHours(),
8689
+ minutes: (d) => d.getUTCMinutes(),
8690
+ setMonth: (d, month) => d.setUTCMonth(month, 1),
8691
+ setDate: (d, date) => d.setUTCDate(date),
8692
+ setHours: (d, hours) => d.setUTCHours(hours, 0, 0, 0),
8693
+ setMinutes: (d, minutes) => d.setUTCMinutes(minutes, 0, 0)
8694
+ };
8695
+ function dayMatches(schedule, cal, date) {
8696
+ const day = schedule.days.has(cal.date(date));
8697
+ const weekday = schedule.weekdays.has(cal.day(date));
8698
+ if (schedule.anyDay || schedule.anyWeekday) return day && weekday;
8699
+ return day || weekday;
8700
+ }
8701
+ var LIMIT_YEARS = 8;
8702
+ function nextFire(schedule, after, utc = false) {
8703
+ const cal = utc ? UTC : LOCAL;
8704
+ const date = new Date(Math.floor(after / 6e4) * 6e4 + 6e4);
8705
+ const limit = cal.year(date) + LIMIT_YEARS;
8706
+ while (cal.year(date) <= limit) {
8707
+ if (!schedule.months.has(cal.month(date) + 1)) {
8708
+ cal.setMonth(date, cal.month(date) + 1);
8709
+ cal.setHours(date, 0);
8710
+ } else if (!dayMatches(schedule, cal, date)) {
8711
+ cal.setDate(date, cal.date(date) + 1);
8712
+ cal.setHours(date, 0);
8713
+ } else if (!schedule.hours.has(cal.hours(date))) {
8714
+ cal.setHours(date, cal.hours(date) + 1);
8715
+ } else if (!schedule.minutes.has(cal.minutes(date))) {
8716
+ cal.setMinutes(date, cal.minutes(date) + 1);
8717
+ } else return date.getTime();
8718
+ }
8719
+ return null;
8720
+ }
8721
+ function missedFires(schedule, from, until, utc = false, cap = MISSED_CAP) {
8722
+ let count2 = 0;
8723
+ let last = null;
8724
+ let at = from;
8725
+ while (count2 < cap) {
8726
+ at = nextFire(schedule, at, utc);
8727
+ if (at === null || at > until) return { count: count2, last, more: false };
8728
+ last = at;
8729
+ count2++;
8730
+ }
8731
+ const next3 = nextFire(schedule, last, utc);
8732
+ return { count: count2, last, more: next3 !== null && next3 <= until };
8733
+ }
8734
+
8735
+ // src/cron/table.ts
8736
+ var SYSTEM_CRONTAB = "/etc/crontab";
8737
+ var SPOOL_DIR = "/var/spool/cron/crontabs";
8738
+ var ENV_LINE = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/;
8739
+ var NAME_LINE = /^#\s*name:\s*(.*?)\s*$/;
8740
+ var JOB_NAME = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/;
8741
+ var USER_NAME = /^[a-z_][a-z0-9_.-]*$/;
8742
+ function unquote(text) {
8743
+ const trimmed = text.trim();
8744
+ const quote = trimmed[0];
8745
+ if ((quote === '"' || quote === "'") && trimmed.length > 1 && trimmed.endsWith(quote))
8746
+ return trimmed.slice(1, -1);
8747
+ return trimmed;
8748
+ }
8749
+ function hashId(text) {
8750
+ let a = 2166136261;
8751
+ let b = 16777619;
8752
+ for (let i = 0; i < text.length; i++) {
8753
+ const c = text.charCodeAt(i);
8754
+ a = Math.imul(a ^ c, 16777619);
8755
+ b = Math.imul(b ^ c, 1540483477);
8756
+ }
8757
+ const hex3 = (n) => (n >>> 0).toString(16).padStart(8, "0");
8758
+ return hex3(a) + hex3(b);
8759
+ }
8760
+ function split2(text, count2) {
8761
+ const words2 = [];
8762
+ let rest2 = text;
8763
+ for (let i = 0; i < count2; i++) {
8764
+ const match = /^(\S+)\s+/.exec(rest2);
8765
+ if (!match) return void 0;
8766
+ words2.push(match[1]);
8767
+ rest2 = rest2.slice(match[0].length);
8768
+ }
8769
+ return [words2, rest2.trim()];
8770
+ }
8771
+ function jobOf(content, pending, ctx) {
8772
+ const fields = content.startsWith("@") ? 1 : 5;
8773
+ const parts2 = split2(content, fields + (ctx.system ? 1 : 0));
8774
+ if (!parts2 || parts2[1] === "") throw new Error("no command");
8775
+ const [words2, command] = parts2;
8776
+ const schedule = words2.slice(0, fields).join(" ");
8777
+ const when = parseWhen(schedule);
8778
+ const user = ctx.system ? words2[fields] : void 0;
8779
+ if (user !== void 0 && !USER_NAME.test(user)) throw new Error(`bad user name ${user}`);
8780
+ if (pending !== void 0 && !JOB_NAME.test(pending))
8781
+ throw new Error(`bad job name "${pending}"`);
8782
+ if (pending !== void 0 && ctx.names.has(pending))
8783
+ throw new Error(`job name ${pending} is taken`);
8784
+ const hashed = hashId(`${ctx.path}
8785
+ ${user ?? ""}
8786
+ ${schedule}
8787
+ ${command}`);
8788
+ let id2 = pending ?? hashed;
8789
+ for (let n = 2; pending === void 0 && ctx.names.has(id2); n++) id2 = `${hashed}.${n}`;
8790
+ ctx.names.add(id2);
8791
+ return {
8792
+ id: id2,
8793
+ ...pending !== void 0 ? { name: pending } : {},
8794
+ schedule,
8795
+ when,
8796
+ ...user !== void 0 ? { user } : {},
8797
+ command
8798
+ };
8799
+ }
8800
+ function parseCrontab(text, path, system = false) {
8801
+ const jobs = [];
8802
+ const errors = [];
8803
+ const env = {};
8804
+ const ctx = { path, system, names: /* @__PURE__ */ new Set() };
8805
+ let name2;
8806
+ for (const [index, raw] of text.split("\n").entries()) {
8807
+ const line = index + 1;
8808
+ const content = raw.trim();
8809
+ const named = NAME_LINE.exec(content);
8810
+ if (named) {
8811
+ name2 = named[1];
8812
+ continue;
8813
+ }
8814
+ const pending = name2;
8815
+ name2 = void 0;
8816
+ if (content === "" || content.startsWith("#")) continue;
8817
+ const assign = ENV_LINE.exec(content);
8818
+ if (assign) {
8819
+ env[assign[1]] = unquote(assign[2]);
8820
+ continue;
8821
+ }
8822
+ try {
8823
+ jobs.push({ line, ...jobOf(content, pending, ctx), env: { ...env } });
8824
+ } catch (err) {
8825
+ errors.push({ line, message: err.message });
8826
+ }
8827
+ }
8828
+ return { jobs, errors };
8829
+ }
8830
+
8831
+ // src/cron/service.ts
8832
+ var CRON_DIR = "/var/spool/cron";
8833
+ var STAMP_PATH = `${CRON_DIR}/stamp`;
8834
+ var LOG_DIR = "/var/log/cron";
8835
+ var ERR_LOG = `${LOG_DIR}/cron.err`;
8836
+ var LOG_CAP = 256 * 1024;
8837
+ var JOBS_PER_USER = 16;
8838
+ var WAKE_MS = 6e4;
8839
+ var BACK_JUMP_MS = 12e4;
8840
+ var DEFAULT_SHELL = "/bin/bash";
8841
+ var encoder2 = new TextEncoder();
8842
+ var EMPTY_STAMP = JSON.stringify({ jobs: {}, reboot: {} });
8843
+ var messageOf = (err) => err?.message ?? String(err);
8844
+ var keyOf2 = ({ source, job }) => `${source} ${job.id}`;
8845
+ function tail(bytes, cap = LOG_CAP) {
8846
+ return bytes.length > cap ? bytes.slice(bytes.length - cap) : bytes;
8847
+ }
8848
+ function concat3(parts2) {
8849
+ const out = new Uint8Array(parts2.reduce((n, part) => n + part.length, 0));
8850
+ let at = 0;
8851
+ for (const part of parts2) {
8852
+ out.set(part, at);
8853
+ at += part.length;
8854
+ }
8855
+ return out;
8856
+ }
8857
+ var Output = class {
8858
+ parts = [];
8859
+ size = 0;
8860
+ push(bytes) {
8861
+ this.parts.push(bytes.slice());
8862
+ this.size += bytes.length;
8863
+ if (this.size > LOG_CAP * 2) {
8864
+ const kept = tail(concat3(this.parts));
8865
+ this.parts = [kept];
8866
+ this.size = kept.length;
8867
+ }
8868
+ }
8869
+ bytes() {
8870
+ return tail(concat3(this.parts));
8871
+ }
8872
+ };
8873
+ function readStamp(text) {
8874
+ try {
8875
+ const parsed2 = JSON.parse(text ?? "");
8876
+ const jobs = parsed2.jobs && typeof parsed2.jobs === "object" ? parsed2.jobs : {};
8877
+ const reboot = parsed2.reboot && typeof parsed2.reboot === "object" ? parsed2.reboot : {};
8878
+ return { jobs: { ...jobs }, reboot: { ...reboot } };
8879
+ } catch {
8880
+ return { jobs: {}, reboot: {} };
8881
+ }
8882
+ }
8883
+ var CronService = class {
8884
+ host;
8885
+ clock;
8886
+ stopping = new AbortController();
8887
+ gate = new PathGate();
8888
+ running = /* @__PURE__ */ new Map();
8889
+ reported = /* @__PURE__ */ new Map();
8890
+ wake = new AbortController();
8891
+ started;
8892
+ rebooted = false;
8893
+ constructor(host) {
8894
+ this.host = host;
8895
+ this.clock = host.clock ?? systemCronClock;
8896
+ }
8897
+ start() {
8898
+ this.started ??= this.begin();
8899
+ return this.started;
8900
+ }
8901
+ stop() {
8902
+ this.stopping.abort();
8903
+ this.wake.abort();
8904
+ }
8905
+ async begin() {
8906
+ await this.host.ready?.().catch(() => void 0);
8907
+ if (this.stopping.signal.aborted) return;
8908
+ const { locks } = this.host;
8909
+ const fire = () => this.fire().catch((err) => {
8910
+ console.warn(`slicc-kernel cron: stopped: ${messageOf(err)}`);
8911
+ });
8912
+ if (!locks) return fire();
8913
+ await locks.request(this.host.lock, { signal: this.stopping.signal }, fire).catch(() => void 0);
8914
+ }
8915
+ poke = () => this.wake.abort();
8916
+ async fire() {
8917
+ await this.provision();
8918
+ const unwatch = await this.host.watch([SYSTEM_CRONTAB, SPOOL_DIR], this.poke);
8919
+ try {
8920
+ while (!this.stopping.signal.aborted) await this.pass();
8921
+ } finally {
8922
+ unwatch();
8923
+ }
8924
+ }
8925
+ async pass() {
8926
+ let until;
8927
+ try {
8928
+ until = await this.tick();
8929
+ } catch (err) {
8930
+ await this.error(`cron: ${messageOf(err)}`);
8931
+ until = this.clock.now() + WAKE_MS;
8932
+ }
8933
+ if (this.stopping.signal.aborted) return;
8934
+ if (this.wake.signal.aborted) this.wake = new AbortController();
8935
+ await this.clock.sleep(until, this.wake.signal);
8936
+ }
8937
+ async provision() {
8938
+ const { fs, owners } = this.host;
8939
+ for (const [dir, mode] of [
8940
+ [CRON_DIR, 493],
8941
+ [SPOOL_DIR, 448],
8942
+ [LOG_DIR, 493]
8943
+ ]) {
8944
+ if (await fs.exists(dir)) continue;
8945
+ await fs.mkdir(dir, { recursive: true });
8946
+ if (owners) await fs.chown?.(dir, { uid: 0, gid: 0, mode, follow: false });
8947
+ }
8948
+ }
8949
+ async tick() {
8950
+ const { fs } = this.host;
8951
+ const loaded = await this.load();
8952
+ const stamp = readStamp(await fs.readFile(STAMP_PATH).catch(() => void 0));
8953
+ const now = this.clock.now();
8954
+ const utc = this.clock.utc === true;
8955
+ const due2 = [];
8956
+ const keys = /* @__PURE__ */ new Set();
8957
+ let next3 = now + WAKE_MS;
8958
+ for (const entry of loaded) {
8959
+ const key = keyOf2(entry);
8960
+ keys.add(key);
8961
+ const { when } = entry.job;
8962
+ if (when.reboot) {
8963
+ if (this.rebooted || stamp.reboot[key] === this.host.bootId) continue;
8964
+ stamp.reboot[key] = this.host.bootId;
8965
+ due2.push({ entry, due: now, missed: 0 });
8966
+ continue;
8967
+ }
8968
+ const last = stamp.jobs[key];
8969
+ if (typeof last !== "number" || last > now + BACK_JUMP_MS) stamp.jobs[key] = now;
8970
+ else {
8971
+ const missed = missedFires(when.schedule, last, now, utc);
8972
+ if (missed.last !== null) {
8973
+ stamp.jobs[key] = now;
8974
+ due2.push({ entry, due: missed.last, missed: missed.count - 1 });
8975
+ }
8976
+ }
8977
+ const coming = nextFire(when.schedule, Math.max(now, stamp.jobs[key]), utc);
8978
+ if (coming !== null && coming < next3) next3 = coming;
8979
+ }
8980
+ this.rebooted = true;
8981
+ for (const map of [stamp.jobs, stamp.reboot])
8982
+ for (const key of Object.keys(map)) if (!keys.has(key)) delete map[key];
8983
+ await this.writeStamp(stamp);
8984
+ for (const each of due2) void this.spawn(each);
8985
+ return next3;
8986
+ }
8987
+ async writeStamp(stamp) {
8988
+ const { fs, owners } = this.host;
8989
+ const text = JSON.stringify(stamp);
8990
+ const old = await fs.readFile(STAMP_PATH).catch(() => void 0);
8991
+ if (old === text || old === void 0 && text === EMPTY_STAMP) return;
8992
+ const fresh2 = `${STAMP_PATH}.new`;
8993
+ const owner = owners ? { uid: 0, gid: 0, mode: 384 } : void 0;
8994
+ await fs.writeFile(fresh2, text, owner);
8995
+ await fs.rename(fresh2, STAMP_PATH);
8996
+ }
8997
+ async load() {
8998
+ const { fs } = this.host;
8999
+ const out = [];
9000
+ const system = await this.table(SYSTEM_CRONTAB, 0, true);
9001
+ for (const job of system?.jobs ?? []) {
9002
+ const account = await this.host.lookup(job.user);
9003
+ if (account) out.push({ source: "system", account, job });
9004
+ }
9005
+ const names = await fs.readdir(SPOOL_DIR).catch(() => []);
9006
+ for (const name2 of names.sort()) {
9007
+ if (name2.startsWith(".") || name2.endsWith("~") || name2.endsWith(".new")) continue;
9008
+ const path = `${SPOOL_DIR}/${name2}`;
9009
+ const account = await this.host.lookup(name2);
9010
+ if (!account) {
9011
+ await this.once(path, "no user", [`${path}: skipped: there is no user ${name2}`]);
9012
+ continue;
9013
+ }
9014
+ const table = await this.table(path, account.uid, false);
9015
+ for (const job of table?.jobs ?? []) out.push({ source: name2, account, job });
9016
+ }
9017
+ return out;
9018
+ }
9019
+ async table(path, uid, system) {
9020
+ const { fs, owners } = this.host;
9021
+ const st = await fs.lstat(path).catch(() => void 0);
9022
+ if (!st) {
9023
+ this.reported.delete(path);
9024
+ return void 0;
9025
+ }
9026
+ const trusted = st.isFile && (!owners || (st.uid === uid || st.uid === 0) && (st.mode & 18) === 0);
9027
+ if (!trusted) {
9028
+ const who = uid === 0 ? "root" : "its user or root";
9029
+ const why = `${path}: skipped: it must be a file of ${who}, writable by its owner only`;
9030
+ await this.once(path, "untrusted", [why]);
9031
+ return void 0;
9032
+ }
9033
+ const text = await fs.readFile(path).catch(() => "");
9034
+ const table = parseCrontab(text, path, system);
9035
+ const errors = table.errors.map(({ line, message: message3 }) => `${path}:${line}: ${message3}`);
9036
+ for (const job of table.jobs) {
9037
+ if (job.user !== void 0 && !await this.host.lookup(job.user))
9038
+ errors.push(`${path}:${job.line}: there is no user ${job.user}`);
9039
+ }
9040
+ await this.once(path, text, errors);
9041
+ return table;
9042
+ }
9043
+ async once(path, version, lines2) {
9044
+ if (this.reported.get(path) === version) return;
9045
+ this.reported.set(path, version);
9046
+ for (const line of lines2) await this.error(line);
9047
+ }
9048
+ async error(line) {
9049
+ if (this.stopping.signal.aborted) return;
9050
+ const stamp = new Date(this.clock.now()).toISOString();
9051
+ await this.append(ERR_LOG, encoder2.encode(`${stamp} ${line}
9052
+ `), 0, 0).catch(() => void 0);
9053
+ }
9054
+ async append(path, bytes, uid, gid) {
9055
+ const { fs, owners } = this.host;
9056
+ await this.gate.run(path, async () => {
9057
+ const dir = path.slice(0, path.lastIndexOf("/"));
9058
+ if (!await fs.lstat(dir).catch(() => void 0)) {
9059
+ await fs.mkdir(dir, { recursive: true });
9060
+ if (owners) await fs.chown?.(dir, { uid: 0, gid: 0, mode: 493, follow: false });
9061
+ }
9062
+ const st = await fs.lstat(path).catch(() => void 0);
9063
+ if (owners) await this.trustLog(dir, path, st, uid);
9064
+ const old = st ? await fs.readFileBuffer(path) : void 0;
9065
+ const next3 = tail(old ? concat3([old, bytes]) : bytes);
9066
+ const owner = owners ? { uid, gid, mode: 384 } : void 0;
9067
+ await fs.writeFile(path, next3, old === void 0 ? owner : void 0);
9068
+ });
9069
+ }
9070
+ async trustLog(dir, path, st, uid) {
9071
+ const parent = await this.host.fs.lstat(dir);
9072
+ if (!parent.isDirectory || parent.isSymbolicLink || parent.uid !== 0)
9073
+ throw new Error(`${path} not written: ${dir} must be a directory of root`);
9074
+ if (st && (!st.isFile || st.isSymbolicLink || st.uid !== uid && st.uid !== 0))
9075
+ throw new Error(`${path} not written: it must be a file of uid ${uid} or root`);
9076
+ }
9077
+ logOf({ account, job }) {
9078
+ return normalizePath(`${LOG_DIR}/${account.name}/${job.id}.log`);
9079
+ }
9080
+ async spawn({ entry, due: due2, missed }) {
9081
+ const { account, job } = entry;
9082
+ const log = this.logOf(entry);
9083
+ const iso = new Date(due2).toISOString();
9084
+ const count2 = this.running.get(account.uid) ?? 0;
9085
+ if (count2 >= JOBS_PER_USER) {
9086
+ const why = `${JOBS_PER_USER} cron jobs of ${account.name} are running`;
9087
+ await this.append(
9088
+ log,
9089
+ encoder2.encode(`--- ${iso} skipped: ${why} ---
9090
+ `),
9091
+ account.uid,
9092
+ account.gid
9093
+ ).catch((err) => this.error(messageOf(err)));
9094
+ await this.error(`${account.name}/${job.id}: run due ${iso} skipped: ${why}`);
9095
+ return;
9096
+ }
9097
+ this.running.set(account.uid, count2 + 1);
9098
+ const output = new Output();
9099
+ let ended;
9100
+ try {
9101
+ const cwd = await this.host.fs.exists(account.home) ? account.home : "/";
9102
+ const shell = job.env.SHELL ?? DEFAULT_SHELL;
9103
+ ended = await this.host.run({
9104
+ account,
9105
+ argv: [shell, "-c", job.command],
9106
+ cwd,
9107
+ env: {
9108
+ SHELL: shell,
9109
+ ...job.env,
9110
+ SLICC_CRON: "1",
9111
+ SLICC_CRON_JOB: job.id,
9112
+ SLICC_CRON_DUE: iso,
9113
+ SLICC_CRON_MISSED: String(missed)
9114
+ },
9115
+ output: (bytes) => output.push(bytes)
9116
+ });
9117
+ } catch (err) {
9118
+ output.push(encoder2.encode(`cron: ${messageOf(err)}
9119
+ `));
9120
+ ended = { status: 127 };
9121
+ } finally {
9122
+ this.running.set(account.uid, (this.running.get(account.uid) ?? 1) - 1);
9123
+ }
9124
+ if (this.stopping.signal.aborted) return;
9125
+ const exit = ended.signal ? `signal=${ended.signal}` : `exit=${ended.status}`;
9126
+ const header3 = encoder2.encode(`--- ${iso} ${exit} missed=${missed} ---
9127
+ `);
9128
+ const body = output.bytes();
9129
+ const newline = body.length > 0 && body[body.length - 1] !== 10 ? encoder2.encode("\n") : new Uint8Array();
9130
+ await this.append(log, concat3([header3, body, newline]), account.uid, account.gid).catch(
9131
+ (err) => this.error(messageOf(err))
9132
+ );
9133
+ }
9134
+ };
9135
+
8347
9136
  // src/facade/types.ts
8348
9137
  var FACADE_NAME = /^[a-z][a-z0-9-]*$/;
8349
9138
  function facadeEnvName(name2) {
@@ -8872,7 +9661,7 @@ var DEFAULT_UMASK = 18;
8872
9661
  function isModule(bytes) {
8873
9662
  return MAGIC.every((b, i) => bytes[i] === b);
8874
9663
  }
8875
- function within4(path, dir) {
9664
+ function within5(path, dir) {
8876
9665
  return path.startsWith(dir === "/" ? "/" : `${dir}/`);
8877
9666
  }
8878
9667
  var FreshModules = class {
@@ -8896,10 +9685,10 @@ var FreshModules = class {
8896
9685
  const target = normalizePath(to);
8897
9686
  if (source === target) return;
8898
9687
  for (const key of [...this.fresh.keys()]) {
8899
- if (key === target || within4(key, target)) this.fresh.delete(key);
9688
+ if (key === target || within5(key, target)) this.fresh.delete(key);
8900
9689
  }
8901
9690
  for (const [key, fresh2] of [...this.fresh]) {
8902
- if (key !== source && !within4(key, source)) continue;
9691
+ if (key !== source && !within5(key, source)) continue;
8903
9692
  this.fresh.delete(key);
8904
9693
  this.fresh.set(target + key.slice(source.length), fresh2);
8905
9694
  }
@@ -8941,7 +9730,7 @@ var FreshModules = class {
8941
9730
  await fs.rm(path, options);
8942
9731
  const at = normalizePath(path);
8943
9732
  for (const key of [...this.fresh.keys()]) {
8944
- if (key === at || within4(key, at)) this.fresh.delete(key);
9733
+ if (key === at || within5(key, at)) this.fresh.delete(key);
8945
9734
  }
8946
9735
  },
8947
9736
  rename: async (from, to) => {
@@ -8971,12 +9760,12 @@ var FreshModules = class {
8971
9760
  return new Proxy(fs, {
8972
9761
  get(target, key, receiver) {
8973
9762
  if (Object.hasOwn(overrides, key)) return overrides[key];
8974
- const value = Reflect.get(target, key, receiver);
8975
- if (typeof value !== "function") return value;
8976
- let method = bound2.get(value);
9763
+ const value2 = Reflect.get(target, key, receiver);
9764
+ if (typeof value2 !== "function") return value2;
9765
+ let method = bound2.get(value2);
8977
9766
  if (method === void 0) {
8978
- method = value.bind(target);
8979
- bound2.set(value, method);
9767
+ method = value2.bind(target);
9768
+ bound2.set(value2, method);
8980
9769
  }
8981
9770
  return method;
8982
9771
  }
@@ -9031,228 +9820,6 @@ async function physicalPathAsync(fs, path) {
9031
9820
  return `/${walk2.done.join("/")}`;
9032
9821
  }
9033
9822
 
9034
- // src/fs/unlinked.ts
9035
- var UNLINKED_DIR = ".slicc-unlinked";
9036
- function webLocks() {
9037
- return globalThis.navigator?.locks;
9038
- }
9039
- var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
9040
- var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
9041
- var lockName = (instance) => `slicc-unlinked:${instance}`;
9042
- var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
9043
- var PathGate = class {
9044
- tails = /* @__PURE__ */ new Map();
9045
- async acquire(path) {
9046
- const prev = this.tails.get(path) ?? Promise.resolve();
9047
- const { promise, resolve: resolve2 } = Promise.withResolvers();
9048
- const tail = prev.then(() => promise);
9049
- this.tails.set(path, tail);
9050
- await prev;
9051
- return () => {
9052
- resolve2();
9053
- if (this.tails.get(path) === tail) this.tails.delete(path);
9054
- };
9055
- }
9056
- async run(path, op) {
9057
- const release = await this.acquire(path);
9058
- try {
9059
- return await op();
9060
- } finally {
9061
- release();
9062
- }
9063
- }
9064
- };
9065
- var UnlinkedKeeper = class {
9066
- fs;
9067
- holders;
9068
- rootOf;
9069
- locks;
9070
- busy;
9071
- waiting = /* @__PURE__ */ new Set();
9072
- owner;
9073
- instance;
9074
- gate = new PathGate();
9075
- queue = Promise.resolve();
9076
- count = 0;
9077
- links = /* @__PURE__ */ new Map();
9078
- linked = /* @__PURE__ */ new Map();
9079
- dirOwner;
9080
- constructor(fs, holders, rootOf, options = {}) {
9081
- const { locks, owner } = options;
9082
- this.dirOwner = options.dirOwner;
9083
- this.fs = fs;
9084
- this.holders = holders;
9085
- this.rootOf = rootOf;
9086
- this.locks = locks;
9087
- this.busy = options.busy ?? (() => false);
9088
- this.owner = owner;
9089
- this.instance = owner ? `${owner.id}~${crypto.randomUUID()}` : crypto.randomUUID();
9090
- void locks?.request(lockName(this.instance), () => new Promise(() => void 0));
9091
- }
9092
- serial(op) {
9093
- const next3 = this.queue.then(op);
9094
- this.queue = next3.catch(() => void 0);
9095
- return next3;
9096
- }
9097
- wrap(fs = this.fs) {
9098
- const keeper = this;
9099
- const visible = (names) => names.filter((name2) => name2 !== UNLINKED_DIR);
9100
- const counted2 = (st) => {
9101
- const hidden = keeper.links.get(`${st.dev ?? 0}:${st.ino}`) ?? 0;
9102
- return hidden === 0 || st.nlink === void 0 ? st : { ...st, nlink: Math.max(0, st.nlink - hidden) };
9103
- };
9104
- return {
9105
- ...fs,
9106
- links: (path) => mayLink(fs, path),
9107
- stat: async (path) => counted2(await fs.stat(path)),
9108
- lstat: async (path) => counted2(await fs.lstat(path)),
9109
- async rm(path, options) {
9110
- const target = fs.resolvePath("/", path);
9111
- const pinned = await keeper.pin(target);
9112
- if (pinned.moved.has(target)) pinned.commit();
9113
- else await keeper.settle(pinned, () => fs.rm(path, options));
9114
- },
9115
- async rename(from, to) {
9116
- const target = fs.resolvePath("/", to);
9117
- if (target === fs.resolvePath("/", from)) return fs.rename(from, to);
9118
- const pinned = await keeper.pin(target);
9119
- await keeper.settle(pinned, () => fs.rename(from, to));
9120
- },
9121
- readdir: async (path) => visible(await fs.readdir(path)),
9122
- readdirStat: async (path) => (await fs.readdirStat(path)).filter(([name2]) => name2 !== UNLINKED_DIR).map(([name2, st]) => [name2, st && counted2(st)])
9123
- };
9124
- }
9125
- async settle(pinned, op) {
9126
- try {
9127
- await op();
9128
- } catch (err) {
9129
- await pinned.rollback();
9130
- throw err;
9131
- }
9132
- pinned.commit();
9133
- }
9134
- async pin(target) {
9135
- const held = /* @__PURE__ */ new Map();
9136
- for (const holder of this.holders) {
9137
- if (holder.owns(target)) continue;
9138
- for (const path of holder.held()) {
9139
- if (!within5(path, target) || holder.isKept(path)) continue;
9140
- held.set(path, [...held.get(path) ?? [], holder]);
9141
- }
9142
- }
9143
- const paths = [...held.keys()].sort();
9144
- const releases = [];
9145
- for (const path of paths) releases.push(await this.gate.acquire(path));
9146
- const hidden = /* @__PURE__ */ new Map();
9147
- for (const path of paths) {
9148
- const at = await this.hide(path);
9149
- if (at !== void 0) hidden.set(path, at);
9150
- }
9151
- const done4 = () => {
9152
- for (const release of releases) release();
9153
- };
9154
- const files = /* @__PURE__ */ new Map();
9155
- for (const [path, at] of hidden) {
9156
- const holders = held.get(path);
9157
- const file = this.keptFile(at, { refs: holders.length + 1 });
9158
- files.set(path, file);
9159
- for (const holder of holders) holder.keep(path, file);
9160
- }
9161
- return {
9162
- moved: new Set(hidden.keys()),
9163
- commit: () => {
9164
- for (const file of files.values()) file.release();
9165
- done4();
9166
- },
9167
- rollback: async () => {
9168
- for (const [path, file] of files) {
9169
- for (const holder of held.get(path)) holder.unkeep(path, file);
9170
- await this.serial(async () => {
9171
- await Promise.allSettled([this.fs.rename(file.hidden, path)]);
9172
- this.unlinkedName(file.hidden);
9173
- await this.tidy(parentOf5(file.hidden));
9174
- });
9175
- }
9176
- done4();
9177
- }
9178
- };
9179
- }
9180
- keptFile(hidden, entry) {
9181
- return {
9182
- hidden,
9183
- release: () => {
9184
- if (--entry.refs === 0) void this.serial(() => this.drop(hidden));
9185
- }
9186
- };
9187
- }
9188
- hide(path) {
9189
- return this.serial(async () => {
9190
- const dir = dirOf(this.rootOf(path));
9191
- const hidden = `${dir}/${this.instance}.${++this.count}`;
9192
- try {
9193
- const owner = this.dirOwner;
9194
- await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
9195
- await this.fs.rename(path, hidden);
9196
- await this.linkedName(hidden);
9197
- return hidden;
9198
- } catch {
9199
- await this.tidy(dir);
9200
- return void 0;
9201
- }
9202
- });
9203
- }
9204
- closed(path) {
9205
- if (this.waiting.delete(path)) void this.serial(() => this.drop(path));
9206
- }
9207
- async drop(hidden) {
9208
- if (this.busy(hidden)) {
9209
- this.waiting.add(hidden);
9210
- return;
9211
- }
9212
- await this.fs.rm(hidden).catch(() => void 0);
9213
- this.unlinkedName(hidden);
9214
- await this.tidy(parentOf5(hidden));
9215
- }
9216
- async linkedName(hidden) {
9217
- if (!mayLink(this.fs, hidden)) return;
9218
- const st = await this.fs.lstat(hidden).catch(() => void 0);
9219
- if (st?.nlink === void 0) return;
9220
- const id2 = `${st.dev ?? 0}:${st.ino}`;
9221
- this.linked.set(hidden, id2);
9222
- this.links.set(id2, (this.links.get(id2) ?? 0) + 1);
9223
- }
9224
- unlinkedName(hidden) {
9225
- const id2 = this.linked.get(hidden);
9226
- if (id2 === void 0) return;
9227
- this.linked.delete(hidden);
9228
- const left = (this.links.get(id2) ?? 1) - 1;
9229
- if (left > 0) this.links.set(id2, left);
9230
- else this.links.delete(id2);
9231
- }
9232
- async tidy(dir) {
9233
- const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
9234
- if (left.length === 0) await this.fs.rm(dir, { recursive: true }).catch(() => void 0);
9235
- }
9236
- sweep(root) {
9237
- const query = this.locks?.query?.bind(this.locks);
9238
- const owner = this.owner;
9239
- if (!query && !owner) return Promise.resolve();
9240
- return this.serial(async () => {
9241
- const dir = dirOf(root);
9242
- const names = await this.fs.readdir(dir).catch(() => void 0);
9243
- if (!names) return;
9244
- const held = query && new Set(((await query()).held ?? []).map((lock) => lock.name));
9245
- for (const name2 of names) {
9246
- const id2 = name2.slice(0, name2.lastIndexOf("."));
9247
- const at = id2.indexOf("~");
9248
- const dead = id2 !== this.instance && (at < 0 ? held !== void 0 && !held.has(lockName(id2)) : owner?.alive(id2.slice(0, at)) === false);
9249
- if (dead) await this.fs.rm(`${dir}/${name2}`, { recursive: true }).catch(() => void 0);
9250
- }
9251
- await this.tidy(dir);
9252
- });
9253
- }
9254
- };
9255
-
9256
9823
  // src/fs/watch.ts
9257
9824
  var WATCH_BATCH_LIMIT = 1e3;
9258
9825
  var under3 = (path, dir) => dir === "/" || path.startsWith(`${dir}/`);
@@ -9393,8 +9960,8 @@ var FsWatchers = class {
9393
9960
  return new Proxy(fs, {
9394
9961
  get(target, key, receiver) {
9395
9962
  if (Object.hasOwn(overrides, key)) return overrides[key];
9396
- const value = Reflect.get(target, key, receiver);
9397
- return typeof value === "function" ? value.bind(target) : value;
9963
+ const value2 = Reflect.get(target, key, receiver);
9964
+ return typeof value2 === "function" ? value2.bind(target) : value2;
9398
9965
  }
9399
9966
  });
9400
9967
  }
@@ -9451,7 +10018,7 @@ function captureFile(onData, control) {
9451
10018
  close: () => control.ended()
9452
10019
  });
9453
10020
  }
9454
- function concat3(chunks) {
10021
+ function concat4(chunks) {
9455
10022
  const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
9456
10023
  let offset = 0;
9457
10024
  for (const chunk2 of chunks) {
@@ -9621,7 +10188,7 @@ var ChildTable = class {
9621
10188
  if (!slots2 || !chunks) return new Uint8Array(0);
9622
10189
  slots2.delete(slot2);
9623
10190
  if (slots2.size === 0) this.leftovers.delete(pid);
9624
- return concat3(chunks);
10191
+ return concat4(chunks);
9625
10192
  }
9626
10193
  };
9627
10194
 
@@ -9635,12 +10202,12 @@ async function argvDigest(argv) {
9635
10202
  const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
9636
10203
  return [...hash].map((b) => b.toString(16).padStart(2, "0")).join("");
9637
10204
  }
9638
- function isStrings(value) {
9639
- return Array.isArray(value) && value.every((item) => typeof item === "string");
10205
+ function isStrings(value2) {
10206
+ return Array.isArray(value2) && value2.every((item) => typeof item === "string");
9640
10207
  }
9641
- function stringRecord(value) {
9642
- if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
9643
- const entries = Object.entries(value);
10208
+ function stringRecord(value2) {
10209
+ if (typeof value2 !== "object" || value2 === null || Array.isArray(value2)) return void 0;
10210
+ const entries = Object.entries(value2);
9644
10211
  return entries.every(([, v]) => typeof v === "string") ? Object.fromEntries(entries) : void 0;
9645
10212
  }
9646
10213
  var TicketBook = class {
@@ -9800,14 +10367,14 @@ var DriverConnection = class _DriverConnection {
9800
10367
  var S_ISGID4 = 1024;
9801
10368
  var ID = /^\d{1,10}$/;
9802
10369
  var MAX_ID2 = 4294967294;
9803
- function idOption(value) {
9804
- return value === void 0 ? 0 : Number(value);
10370
+ function idOption(value2) {
10371
+ return value2 === void 0 ? 0 : Number(value2);
9805
10372
  }
9806
10373
  function checkIds(options) {
9807
10374
  for (const key of ["uid", "gid"]) {
9808
- const value = options[key];
9809
- if (value === void 0) continue;
9810
- if (!ID.test(value) || Number(value) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value}`);
10375
+ const value2 = options[key];
10376
+ if (value2 === void 0) continue;
10377
+ if (!ID.test(value2) || Number(value2) > MAX_ID2) throw errnoError("EINVAL", `${key}=${value2}`);
9811
10378
  }
9812
10379
  }
9813
10380
  var MAX_LINKS7 = 40;
@@ -10021,7 +10588,7 @@ function statOf(attr2, path, mount) {
10021
10588
  } : {}
10022
10589
  };
10023
10590
  }
10024
- function concat4(chunks, total) {
10591
+ function concat5(chunks, total) {
10025
10592
  if (chunks.length === 1 && chunks[0]?.length === total) return chunks[0];
10026
10593
  const out = new Uint8Array(total);
10027
10594
  let at = 0;
@@ -10528,7 +11095,7 @@ var MountFs = class _MountFs {
10528
11095
  } finally {
10529
11096
  await mount.conn.call({ op: "release", fh });
10530
11097
  }
10531
- return concat4(chunks, total);
11098
+ return concat5(chunks, total);
10532
11099
  }
10533
11100
  async writeFile(path, content, owner, redirects = 0) {
10534
11101
  const real = await this.follow(path, 0, true);
@@ -10623,7 +11190,7 @@ var MountFs = class _MountFs {
10623
11190
  } finally {
10624
11191
  await mount.conn.call({ op: "release", fh });
10625
11192
  }
10626
- return concat4(chunks, total);
11193
+ return concat5(chunks, total);
10627
11194
  }
10628
11195
  fits(mount, real, end) {
10629
11196
  if (mount.maxFile > 0 && end > mount.maxFile) {
@@ -11036,7 +11603,7 @@ var MountFs = class _MountFs {
11036
11603
  await mount.conn.call({
11037
11604
  op: "setattr",
11038
11605
  path: rel2,
11039
- ...Object.fromEntries(attrs.filter(([, value]) => value !== void 0))
11606
+ ...Object.fromEntries(attrs.filter(([, value2]) => value2 !== void 0))
11040
11607
  });
11041
11608
  }
11042
11609
  async chmod(path, mode, redirects = 0) {
@@ -11215,8 +11782,8 @@ async function storageUsage() {
11215
11782
  }
11216
11783
  var PLAIN_DIR_MODE = 493;
11217
11784
  var done = { ok: true, kind: "void" };
11218
- function json2(value) {
11219
- return { ok: true, kind: "json", json: value };
11785
+ function json2(value2) {
11786
+ return { ok: true, kind: "json", json: value2 };
11220
11787
  }
11221
11788
  function ownerFor2(entry, mode) {
11222
11789
  const cred = entry.cred?.();
@@ -11775,9 +12342,9 @@ function storeBlocked(header3, [low, high]) {
11775
12342
  }
11776
12343
  var anyBlocked = (mask) => mask !== void 0 && (mask[0] !== 0 || mask[1] !== 0);
11777
12344
  var U32 = 2 ** 32;
11778
- function storeU53(header3, lo, hi, value) {
11779
- Atomics.store(header3, hi, Math.floor(value / U32));
11780
- Atomics.store(header3, lo, value % U32 | 0);
12345
+ function storeU53(header3, lo, hi, value2) {
12346
+ Atomics.store(header3, hi, Math.floor(value2 / U32));
12347
+ Atomics.store(header3, lo, value2 % U32 | 0);
11781
12348
  }
11782
12349
  var SAB_STATE_READY = 2;
11783
12350
  var SAB_STATUS_BYTES = 1;
@@ -11788,20 +12355,20 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
11788
12355
  var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
11789
12356
  var SYNC_SAB_REQ_MSG = "sync-sab-req";
11790
12357
  var SYNC_SAB_NEXT_MSG = "sync-sab-next";
11791
- var encoder2 = new TextEncoder();
12358
+ var encoder3 = new TextEncoder();
11792
12359
  var decoder2 = new TextDecoder();
11793
12360
  function encodeSabResult(result) {
11794
12361
  if (!result.ok) {
11795
12362
  return {
11796
12363
  status: SAB_STATUS_ERRNO,
11797
- payload: encoder2.encode(JSON.stringify({ errno: result.errno, message: result.message }))
12364
+ payload: encoder3.encode(JSON.stringify({ errno: result.errno, message: result.message }))
11798
12365
  };
11799
12366
  }
11800
12367
  switch (result.kind) {
11801
12368
  case "bytes":
11802
12369
  return { status: SAB_STATUS_BYTES, payload: result.bytes };
11803
12370
  case "json":
11804
- return { status: SAB_STATUS_JSON, payload: encoder2.encode(JSON.stringify(result.json)) };
12371
+ return { status: SAB_STATUS_JSON, payload: encoder3.encode(JSON.stringify(result.json)) };
11805
12372
  default:
11806
12373
  return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
11807
12374
  }
@@ -11909,14 +12476,14 @@ function attachSyncSabResponder(port, sab, token, opts) {
11909
12476
 
11910
12477
  // src/kernel/cpu-clock.ts
11911
12478
  var SYS_CAP_US = 2e4;
11912
- var LOCAL = SAB_CLOCK_OFFSET;
12479
+ var LOCAL2 = SAB_CLOCK_OFFSET;
11913
12480
  var BLOCKED = 0;
11914
12481
  var IDLE = 1;
11915
12482
  function epochMicros() {
11916
12483
  return Math.floor((performance.timeOrigin + performance.now()) * 1e3);
11917
12484
  }
11918
12485
  function slots(sab) {
11919
- return new BigInt64Array(sab, LOCAL, SAB_CLOCK_I64);
12486
+ return new BigInt64Array(sab, LOCAL2, SAB_CLOCK_I64);
11920
12487
  }
11921
12488
  var rawWait = Atomics.wait.bind(Atomics);
11922
12489
  function localIdle(sab, now) {
@@ -12291,14 +12858,14 @@ var KernelDgram = class {
12291
12858
  }
12292
12859
  return this.options.get(`${level}:${name2}`) ?? 0;
12293
12860
  }
12294
- setOption(level, name2, value) {
12295
- if (level === SOL_SOCKET2 && name2 === SO_BROADCAST && value !== 0) {
12861
+ setOption(level, name2, value2) {
12862
+ if (level === SOL_SOCKET2 && name2 === SO_BROADCAST && value2 !== 0) {
12296
12863
  throw new KernelError("EOPNOTSUPP");
12297
12864
  }
12298
12865
  if (level === IPPROTO_IP && name2 >= IP_MULTICAST_FIRST && name2 <= IP_MULTICAST_LAST) {
12299
12866
  throw new KernelError("ENOPROTOOPT");
12300
12867
  }
12301
- this.options.set(`${level}:${name2}`, value);
12868
+ this.options.set(`${level}:${name2}`, value2);
12302
12869
  }
12303
12870
  close() {
12304
12871
  if (this.closed) return;
@@ -12557,7 +13124,7 @@ var PATH_OPS = new Set(SYNC_FS_OPS);
12557
13124
  var EARLY_WHOLE_FILE = 64 * 1024;
12558
13125
 
12559
13126
  // src/process/process-fds.ts
12560
- var field2 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
13127
+ var field3 = (text) => (text || "none").replace(/[\s\\]/g, (c) => `\\${c.charCodeAt(0).toString(8).padStart(3, "0")}`);
12561
13128
  var pinnedRoot;
12562
13129
  var rootKind = () => globalThis.navigator?.storage ? "opfs" : "memory";
12563
13130
  function mountTable(mounts = []) {
@@ -12568,7 +13135,7 @@ function mountTable(mounts = []) {
12568
13135
  if (m.state !== "ok") options.push(m.state);
12569
13136
  const access2 = options.includes("ro") ? [] : ["rw"];
12570
13137
  lines2.push(
12571
- `${field2(m.source)} ${field2(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
13138
+ `${field3(m.source)} ${field3(m.target)} ${m.type} ${[...access2, ...options].join(",")} 0 0`
12572
13139
  );
12573
13140
  }
12574
13141
  return `${lines2.join("\n")}
@@ -12724,7 +13291,7 @@ function memoryInfo(source, used = 0) {
12724
13291
  ["SReclaimable", kb(0)],
12725
13292
  ["SwapTotal", kb(0)],
12726
13293
  ["SwapFree", kb(0)]
12727
- ].map(([key, value]) => `${`${key}:`.padEnd(16)}${value.padStart(12)}`).concat("").join("\n");
13294
+ ].map(([key, value2]) => `${`${key}:`.padEnd(16)}${value2.padStart(12)}`).concat("").join("\n");
12728
13295
  }
12729
13296
  function systemFile(name2, listing, now, memory) {
12730
13297
  if (name2 === "version") return procVersion();
@@ -12979,15 +13546,15 @@ async function procAlias(fs, path, follow2) {
12979
13546
  );
12980
13547
  if (!physical) return void 0;
12981
13548
  const walk2 = procWalk(path, follow2);
12982
- let step = walk2.next();
12983
- while (!step.done) {
12984
- const { at, check } = step.value;
13549
+ let step2 = walk2.next();
13550
+ while (!step2.done) {
13551
+ const { at, check } = step2.value;
12985
13552
  const link2 = await readlink.call(fs, at).catch(() => void 0);
12986
13553
  const isDir = (st) => st.isDirectory === true;
12987
13554
  const target = link2 ?? (check ? await lstat.call(fs, at).then(isDir, failed) : true);
12988
- step = walk2.next(target);
13555
+ step2 = walk2.next(target);
12989
13556
  }
12990
- return step.value;
13557
+ return step2.value;
12991
13558
  }
12992
13559
 
12993
13560
  // src/kernel/eventfd.ts
@@ -13033,23 +13600,23 @@ var EventFd = class _EventFd {
13033
13600
  blocked(signal);
13034
13601
  await this.changes.wait(signal);
13035
13602
  }
13036
- const value = this.semaphore ? 1n : this.count;
13037
- this.count -= value;
13603
+ const value2 = this.semaphore ? 1n : this.count;
13604
+ this.count -= value2;
13038
13605
  this.changes.wake();
13039
13606
  const out = new Uint8Array(_EventFd.SIZE);
13040
- new DataView(out.buffer).setBigUint64(0, value, true);
13607
+ new DataView(out.buffer).setBigUint64(0, value2, true);
13041
13608
  return out;
13042
13609
  }
13043
13610
  async write(bytes, signal) {
13044
13611
  if (bytes.length < _EventFd.SIZE) throw new KernelError("EINVAL");
13045
- const value = new DataView(bytes.buffer, bytes.byteOffset, _EventFd.SIZE).getBigUint64(0, true);
13046
- if (value > EVENTFD_MAX) throw new KernelError("EINVAL");
13047
- while (EVENTFD_MAX - this.count < value) {
13612
+ const value2 = new DataView(bytes.buffer, bytes.byteOffset, _EventFd.SIZE).getBigUint64(0, true);
13613
+ if (value2 > EVENTFD_MAX) throw new KernelError("EINVAL");
13614
+ while (EVENTFD_MAX - this.count < value2) {
13048
13615
  blocked(signal);
13049
13616
  await this.changes.wait(signal);
13050
13617
  }
13051
- this.count += value;
13052
- if (value > 0n) this.changes.wake();
13618
+ this.count += value2;
13619
+ if (value2 > 0n) this.changes.wake();
13053
13620
  return _EventFd.SIZE;
13054
13621
  }
13055
13622
  poll() {
@@ -13336,10 +13903,10 @@ var LockTable = class {
13336
13903
  }
13337
13904
  set(key, owner, kind, start2, end) {
13338
13905
  if (kind !== "unlock" && this.holder(key, owner, { key, kind, start: start2, end })) return false;
13339
- const range = { start: start2, end };
13906
+ const range2 = { start: start2, end };
13340
13907
  const kept = [];
13341
13908
  for (const lock of this.records.get(key) ?? []) {
13342
- if (lock.owner !== owner || !overlaps(lock, range)) kept.push(lock);
13909
+ if (lock.owner !== owner || !overlaps(lock, range2)) kept.push(lock);
13343
13910
  else {
13344
13911
  if (lock.start < start2) kept.push({ ...lock, end: start2 });
13345
13912
  if (lock.end > end) kept.push({ ...lock, start: end });
@@ -13350,13 +13917,13 @@ var LockTable = class {
13350
13917
  this.wake(key);
13351
13918
  return true;
13352
13919
  }
13353
- async wait(key, owner, kind, range, blocking) {
13354
- const want = { key, kind, ...range };
13920
+ async wait(key, owner, kind, range2, blocking) {
13921
+ const want = { key, kind, ...range2 };
13355
13922
  let signal;
13356
13923
  for (; ; ) {
13357
13924
  const holder = this.holder(key, owner, want);
13358
13925
  if (!holder) {
13359
- this.set(key, owner, kind, range.start, range.end);
13926
+ this.set(key, owner, kind, range2.start, range2.end);
13360
13927
  return;
13361
13928
  }
13362
13929
  if (this.waitsOn(holder.owner, owner)) throw new KernelError("EDEADLK");
@@ -13479,7 +14046,7 @@ var HOST_OPS = [
13479
14046
  "async-settle",
13480
14047
  "async-close"
13481
14048
  ];
13482
- var json3 = (value) => ({ ok: true, kind: "json", json: value });
14049
+ var json3 = (value2) => ({ ok: true, kind: "json", json: value2 });
13483
14050
  var done2 = { ok: true, kind: "void" };
13484
14051
  var KEPT_OPS = /* @__PURE__ */ new Set(["fd-write"]);
13485
14052
  var unknownSettled = false;
@@ -13509,9 +14076,9 @@ var AsyncOps = class {
13509
14076
  );
13510
14077
  return id2;
13511
14078
  }
13512
- resolve(value) {
14079
+ resolve(value2) {
13513
14080
  const id2 = this.hold();
13514
- this.complete(id2, json3(value));
14081
+ this.complete(id2, json3(value2));
13515
14082
  return id2;
13516
14083
  }
13517
14084
  hold() {
@@ -13656,19 +14223,19 @@ async function recordLock2(req, ctx) {
13656
14223
  locks.dropFile(await fileKey(target, "close"), owner);
13657
14224
  return done2;
13658
14225
  }
13659
- const range = lockRange(req.start, req.len);
14226
+ const range2 = lockRange(req.start, req.len);
13660
14227
  const key = await fileKey(target, req.kind, req.op === "lk-set" && req.fd !== void 0);
13661
14228
  if (req.op === "lk-get") {
13662
- const held = locks.holder(key, owner, { key, kind: req.kind, ...range });
14229
+ const held = locks.holder(key, owner, { key, kind: req.kind, ...range2 });
13663
14230
  if (!held) return json3(null);
13664
14231
  const len = held.end === Number.POSITIVE_INFINITY ? 0 : held.end - held.start;
13665
14232
  return json3({ kind: held.kind, pid: held.owner, start: held.start, len });
13666
14233
  }
13667
14234
  if (req.kind !== "unlock" && req.wait) {
13668
- await locks.wait(key, owner, req.kind, range, ctx.blocking);
14235
+ await locks.wait(key, owner, req.kind, range2, ctx.blocking);
13669
14236
  return done2;
13670
14237
  }
13671
- if (!locks.set(key, owner, req.kind, range.start, range.end)) throw new KernelError("EAGAIN");
14238
+ if (!locks.set(key, owner, req.kind, range2.start, range2.end)) throw new KernelError("EAGAIN");
13672
14239
  return done2;
13673
14240
  }
13674
14241
 
@@ -13704,13 +14271,13 @@ function errno(code, message3) {
13704
14271
  function requestHead(method, url, headers, extra, first = []) {
13705
14272
  if (!TOKEN3.test(method)) throw errno("EINVAL", `method ${method}`);
13706
14273
  const lines2 = [`${method} ${url.pathname}${url.search} HTTP/1.1`, `host: ${url.host}`];
13707
- for (const [name2, value] of first) lines2.push(`${name2}: ${value}`);
13708
- for (const [name2, value] of headers) {
14274
+ for (const [name2, value2] of first) lines2.push(`${name2}: ${value2}`);
14275
+ for (const [name2, value2] of headers) {
13709
14276
  if (OWN_HEADERS.has(name2.toLowerCase())) continue;
13710
- if (!TOKEN3.test(name2) || !SAFE_VALUE.test(value)) throw errno("EINVAL", `header ${name2}`);
13711
- lines2.push(`${name2}: ${value}`);
14277
+ if (!TOKEN3.test(name2) || !SAFE_VALUE.test(value2)) throw errno("EINVAL", `header ${name2}`);
14278
+ lines2.push(`${name2}: ${value2}`);
13712
14279
  }
13713
- for (const [name2, value] of extra) lines2.push(`${name2}: ${value}`);
14280
+ for (const [name2, value2] of extra) lines2.push(`${name2}: ${value2}`);
13714
14281
  return latin1Bytes(`${lines2.join("\r\n")}\r
13715
14282
  \r
13716
14283
  `);
@@ -14487,11 +15054,11 @@ function waitFor(lookup2, signal) {
14487
15054
  reject(new KernelError("EINTR"));
14488
15055
  };
14489
15056
  signal?.addEventListener("abort", abort, { once: true });
14490
- void lookup2.work.then((value) => {
15057
+ void lookup2.work.then((value2) => {
14491
15058
  if (signal?.aborted) return;
14492
15059
  lookup2.waiters--;
14493
15060
  signal?.removeEventListener("abort", abort);
14494
- resolve2(value);
15061
+ resolve2(value2);
14495
15062
  });
14496
15063
  });
14497
15064
  }
@@ -14528,7 +15095,7 @@ function defaultTermios() {
14528
15095
  c_cc: cCc
14529
15096
  };
14530
15097
  }
14531
- var encoder3 = new TextEncoder();
15098
+ var encoder4 = new TextEncoder();
14532
15099
  function isContinuation(byte) {
14533
15100
  return (byte & 192) === 128;
14534
15101
  }
@@ -14651,7 +15218,7 @@ var KernelTty = class {
14651
15218
  this.line.push(byte);
14652
15219
  this.readable.push(Uint8Array.from(this.line));
14653
15220
  this.line = [];
14654
- if (echo) this.output(encoder3.encode("\n"));
15221
+ if (echo) this.output(encoder4.encode("\n"));
14655
15222
  } else {
14656
15223
  this.line.push(byte);
14657
15224
  if (echo) this.echo(byte);
@@ -14676,7 +15243,7 @@ var KernelTty = class {
14676
15243
  const utf83 = (this.termios.c_iflag & IUTF8) !== 0;
14677
15244
  const cells = utf83 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
14678
15245
  if (cells > 0 && this.termios.c_lflag & ECHO) {
14679
- this.output(encoder3.encode("\b \b".repeat(cells)), false);
15246
+ this.output(encoder4.encode("\b \b".repeat(cells)), false);
14680
15247
  }
14681
15248
  }
14682
15249
  echo(byte) {
@@ -14928,7 +15495,7 @@ function terminalOf(ctx, fd) {
14928
15495
  if (!tty) throw new KernelError("ENOTTY");
14929
15496
  return tty;
14930
15497
  }
14931
- var json4 = (value) => ({ ok: true, kind: "json", json: value });
15498
+ var json4 = (value2) => ({ ok: true, kind: "json", json: value2 });
14932
15499
  var done3 = { ok: true, kind: "void" };
14933
15500
  function sizeKey(rows, cols) {
14934
15501
  return `${rows}x${cols}`;
@@ -15144,8 +15711,8 @@ var Semaphore = class {
15144
15711
  gid;
15145
15712
  mode;
15146
15713
  waiters = [];
15147
- constructor(value, uid, gid, mode) {
15148
- this.value = value;
15714
+ constructor(value2, uid, gid, mode) {
15715
+ this.value = value2;
15149
15716
  this.uid = uid;
15150
15717
  this.gid = gid;
15151
15718
  this.mode = mode;
@@ -15770,10 +16337,10 @@ var VfsNodes = class {
15770
16337
  async onEntry(path, op) {
15771
16338
  const key = await this.entryKey(path);
15772
16339
  const run2 = (this.entries.get(key) ?? Promise.resolve()).then(op);
15773
- const tail = run2.catch(() => void 0);
15774
- this.entries.set(key, tail);
15775
- void tail.then(() => {
15776
- if (this.entries.get(key) === tail) this.entries.delete(key);
16340
+ const tail2 = run2.catch(() => void 0);
16341
+ this.entries.set(key, tail2);
16342
+ void tail2.then(() => {
16343
+ if (this.entries.get(key) === tail2) this.entries.delete(key);
15777
16344
  });
15778
16345
  return run2;
15779
16346
  }
@@ -16398,11 +16965,11 @@ var MAX_READ = 1024 * 1024;
16398
16965
  var WRITE_ACCESS = 3;
16399
16966
  var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
16400
16967
  var DEFAULT_UMASK2 = 18;
16401
- function words(value) {
16402
- if (!Array.isArray(value) || !value.every((w) => typeof w === "string")) {
16968
+ function words(value2) {
16969
+ if (!Array.isArray(value2) || !value2.every((w) => typeof w === "string")) {
16403
16970
  throw new KernelError("EINVAL");
16404
16971
  }
16405
- return [...value];
16972
+ return [...value2];
16406
16973
  }
16407
16974
  var ESCAPES = { " ": "\\t", "\n": "\\n", "\r": "\\r" };
16408
16975
  function clipboardLine(text) {
@@ -18375,7 +18942,7 @@ function replayingHops(inner, heldMs = HELD_MS) {
18375
18942
  clearTimeout(entry.timer);
18376
18943
  void entry.final.cancel().catch(() => void 0);
18377
18944
  };
18378
- const keyOf2 = (owner, method, url) => `${owner} ${method} ${url}`;
18945
+ const keyOf3 = (owner, method, url) => `${owner} ${method} ${url}`;
18379
18946
  const hold = (key, chain2) => {
18380
18947
  drop(key);
18381
18948
  const mine = [...held.keys()].filter((k) => held.get(k)?.owner === chain2.owner);
@@ -18394,7 +18961,7 @@ function replayingHops(inner, heldMs = HELD_MS) {
18394
18961
  const method = followedWith(hop.status, chain2.method);
18395
18962
  const away = target.origin !== new URL(hop.url).origin;
18396
18963
  const print = away ? unauthorized(chain2.fingerprint) : chain2.fingerprint;
18397
- hold(keyOf2(chain2.owner, method, target.href), {
18964
+ hold(keyOf3(chain2.owner, method, target.href), {
18398
18965
  ...chain2,
18399
18966
  method,
18400
18967
  fingerprint: print,
@@ -18408,7 +18975,7 @@ function replayingHops(inner, heldMs = HELD_MS) {
18408
18975
  },
18409
18976
  ...inner.websocket ? { websocket: inner.websocket.bind(inner) } : {},
18410
18977
  async fetch(request2, owner = KERNEL) {
18411
- const key = keyOf2(owner, request2.method, request2.url);
18978
+ const key = keyOf3(owner, request2.method, request2.url);
18412
18979
  const waiting = held.get(key);
18413
18980
  if (waiting && waiting.fingerprint === fingerprint(request2.headers)) {
18414
18981
  held.delete(key);
@@ -18433,10 +19000,10 @@ function replayingHops(inner, heldMs = HELD_MS) {
18433
19000
  var FRAGMENT = 16;
18434
19001
  var MIN_VALUE = 8;
18435
19002
  var MIN_BASE64 = 8;
18436
- var encoder4 = new TextEncoder();
19003
+ var encoder5 = new TextEncoder();
18437
19004
  var STD = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
18438
19005
  function utf82(text) {
18439
- return encoder4.encode(text);
19006
+ return encoder5.encode(text);
18440
19007
  }
18441
19008
  function base642(bytes, padded = true) {
18442
19009
  let out = "";
@@ -18468,30 +19035,30 @@ function embedded(bytes, offset) {
18468
19035
  const last = Math.floor(8 * (offset + bytes.length) / 6);
18469
19036
  return encoded.slice(first, last);
18470
19037
  }
18471
- function unicode(value, upper) {
19038
+ function unicode(value2, upper) {
18472
19039
  let out = "";
18473
- for (let i = 0; i < value.length; i++) {
18474
- const unit = value.charCodeAt(i);
19040
+ for (let i = 0; i < value2.length; i++) {
19041
+ const unit = value2.charCodeAt(i);
18475
19042
  if (unit === 34) out += '\\"';
18476
19043
  else if (unit === 92) out += "\\\\";
18477
19044
  else if (unit < 32 || unit > 126) {
18478
19045
  const code = unit.toString(16).padStart(4, "0");
18479
19046
  out += `\\u${upper ? code.toUpperCase() : code}`;
18480
- } else out += value[i];
19047
+ } else out += value2[i];
18481
19048
  }
18482
19049
  return out;
18483
19050
  }
18484
- function percent(value) {
19051
+ function percent(value2) {
18485
19052
  let component;
18486
19053
  try {
18487
- component = encodeURIComponent(value);
19054
+ component = encodeURIComponent(value2);
18488
19055
  } catch {
18489
19056
  return [];
18490
19057
  }
18491
19058
  const lower = component.replace(/%[0-9A-F]{2}/g, (escape) => escape.toLowerCase());
18492
19059
  const plus = component.replace(/%20/g, "+");
18493
19060
  let form = "";
18494
- for (const byte of utf82(value)) {
19061
+ for (const byte of utf82(value2)) {
18495
19062
  const char = String.fromCharCode(byte);
18496
19063
  if (/[A-Za-z0-9*\-._]/.test(char)) form += char;
18497
19064
  else if (byte === 32) form += "+";
@@ -18499,8 +19066,8 @@ function percent(value) {
18499
19066
  }
18500
19067
  return [component, lower, plus, form];
18501
19068
  }
18502
- function html(value, apostrophe) {
18503
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, apostrophe);
19069
+ function html(value2, apostrophe) {
19070
+ return value2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, apostrophe);
18504
19071
  }
18505
19072
  function encodings(bytes) {
18506
19073
  const standard = [base642(bytes), base642(bytes, false)];
@@ -18510,9 +19077,9 @@ function encodings(bytes) {
18510
19077
  }
18511
19078
  return [...standard, ...standard.map(urlSafe)];
18512
19079
  }
18513
- function shapes(value) {
18514
- const raw = utf82(value);
18515
- const seen = /* @__PURE__ */ new Set([value]);
19080
+ function shapes(value2) {
19081
+ const raw = utf82(value2);
19082
+ const seen = /* @__PURE__ */ new Set([value2]);
18516
19083
  const out = [];
18517
19084
  if (raw.length < FRAGMENT) out.push({ bytes: raw, form: "raw" });
18518
19085
  for (let offset = 0; raw.length >= FRAGMENT && offset + FRAGMENT <= raw.length; offset++) {
@@ -18523,17 +19090,17 @@ function shapes(value) {
18523
19090
  seen.add(text);
18524
19091
  out.push({ bytes: utf82(text), form });
18525
19092
  };
18526
- const json5 = JSON.stringify(value).slice(1, -1);
19093
+ const json5 = JSON.stringify(value2).slice(1, -1);
18527
19094
  for (const text of [
18528
19095
  json5,
18529
19096
  json5.replace(/\//g, "\\/"),
18530
- unicode(value, false),
18531
- unicode(value, true)
19097
+ unicode(value2, false),
19098
+ unicode(value2, true)
18532
19099
  ]) {
18533
19100
  add(text, "json");
18534
19101
  }
18535
- for (const text of percent(value)) add(text, "percent");
18536
- for (const text of [html(value, "&#39;"), html(value, "&#x27;")]) add(text, "html");
19102
+ for (const text of percent(value2)) add(text, "percent");
19103
+ for (const text of [html(value2, "&#39;"), html(value2, "&#x27;")]) add(text, "html");
18537
19104
  for (const text of encodings(raw)) add(text, "base64");
18538
19105
  add(hex2(raw), "hex");
18539
19106
  add(hex2(raw).toUpperCase(), "hex");
@@ -18684,15 +19251,15 @@ function resolve(c, input, start2, end, source) {
18684
19251
  growing: false
18685
19252
  };
18686
19253
  }
18687
- const value = c.values[source.secret];
19254
+ const value2 = c.values[source.secret];
18688
19255
  let stop = end;
18689
19256
  let at = source.offset + FRAGMENT;
18690
- while (stop < input.length && at < value.length && input[stop] === value[at]) {
19257
+ while (stop < input.length && at < value2.length && input[stop] === value2[at]) {
18691
19258
  stop++;
18692
19259
  at++;
18693
19260
  }
18694
- const form = source.offset === 0 && at === value.length ? "raw" : "fragment";
18695
- const growing = stop === input.length && at < value.length;
19261
+ const form = source.offset === 0 && at === value2.length ? "raw" : "fragment";
19262
+ const growing = stop === input.length && at < value2.length;
18696
19263
  return { start: start2, end: stop, secret: source.secret, form, rank: RANK[form], growing };
18697
19264
  }
18698
19265
  function keep(best, candidate) {
@@ -18740,7 +19307,7 @@ function count(c, hits, candidate) {
18740
19307
  hit.count += 1;
18741
19308
  hits.set(key, hit);
18742
19309
  }
18743
- function concat5(parts2) {
19310
+ function concat6(parts2) {
18744
19311
  let length2 = 0;
18745
19312
  for (const part of parts2) length2 += part.length;
18746
19313
  const out = new Uint8Array(length2);
@@ -18782,7 +19349,7 @@ function pass(c, input, final) {
18782
19349
  pos = candidate.end;
18783
19350
  }
18784
19351
  parts2.push(input.subarray(pos, cut));
18785
- return { output: concat5(parts2), cut, hits };
19352
+ return { output: concat6(parts2), cut, hits };
18786
19353
  }
18787
19354
  function merge(into, from) {
18788
19355
  for (const [key, hit] of from) {
@@ -18825,7 +19392,7 @@ function compile(secrets, options = {}) {
18825
19392
  };
18826
19393
  return {
18827
19394
  push(chunk2) {
18828
- pending = concat5([pending, chunk2]);
19395
+ pending = concat6([pending, chunk2]);
18829
19396
  return run2(false);
18830
19397
  },
18831
19398
  idle() {
@@ -18845,9 +19412,9 @@ function compile(secrets, options = {}) {
18845
19412
  // src/kernel/scrub-file.ts
18846
19413
  var SCRUB_IDLE_MS = 50;
18847
19414
  var SCRUB_NONBLOCK_MAX = 64 * 1024;
18848
- var encoder5 = new TextEncoder();
19415
+ var encoder6 = new TextEncoder();
18849
19416
  function scrubberOf(secrets) {
18850
- const usable = secrets.filter((s) => encoder5.encode(s.value).length >= MIN_VALUE);
19417
+ const usable = secrets.filter((s) => encoder6.encode(s.value).length >= MIN_VALUE);
18851
19418
  return usable.length > 0 ? compile(usable, { idleMs: SCRUB_IDLE_MS }) : void 0;
18852
19419
  }
18853
19420
  async function writeAll3(file, bytes) {
@@ -18865,7 +19432,7 @@ function interrupted2(err) {
18865
19432
  function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18866
19433
  const file = inner.file;
18867
19434
  const stream = scrubber.stream();
18868
- let tail = Promise.resolve();
19435
+ let tail2 = Promise.resolve();
18869
19436
  let pending = 0;
18870
19437
  let busy = false;
18871
19438
  let timer;
@@ -18884,7 +19451,7 @@ function scrubbed(inner, scrubber, idleMs = SCRUB_IDLE_MS) {
18884
19451
  const later = (bytes) => {
18885
19452
  if (bytes.length === 0) return;
18886
19453
  pending += 1;
18887
- tail = tail.then(() => writeAll3(file, bytes)).catch((err) => {
19454
+ tail2 = tail2.then(() => writeAll3(file, bytes)).catch((err) => {
18888
19455
  if (failed3 === void 0) failed3 = err;
18889
19456
  }).finally(() => {
18890
19457
  pending -= 1;
@@ -19035,7 +19602,7 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
19035
19602
  "cancel",
19036
19603
  "materialize"
19037
19604
  ]);
19038
- var encoder6 = new TextEncoder();
19605
+ var encoder7 = new TextEncoder();
19039
19606
  var decoder3 = new TextDecoder("utf-8", { fatal: true });
19040
19607
  function parseSealed(bytes) {
19041
19608
  try {
@@ -19064,8 +19631,8 @@ function materializable(req) {
19064
19631
  function errorReply(code, message3) {
19065
19632
  return { v: 1, error: { code, message: message3 } };
19066
19633
  }
19067
- function isRecord(value) {
19068
- return typeof value === "object" && value !== null && !Array.isArray(value);
19634
+ function isRecord(value2) {
19635
+ return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
19069
19636
  }
19070
19637
  function serialized(reply) {
19071
19638
  let plain2;
@@ -19116,7 +19683,7 @@ var SealedChannel = class {
19116
19683
  await this.broker.tickets.record(req, reply, caller.pid);
19117
19684
  if (this.inflight !== flight) return;
19118
19685
  this.inflight = void 0;
19119
- if (reply) this.reply = encoder6.encode(serialized(reply));
19686
+ if (reply) this.reply = encoder7.encode(serialized(reply));
19120
19687
  });
19121
19688
  this.inflight = flight;
19122
19689
  return length2;
@@ -19708,8 +20275,8 @@ function umountCall(target, flags2) {
19708
20275
  var FSTAB_PATH = "/etc/fstab";
19709
20276
  var FSTAB_RETRIES = [1e3, 4e3, 16e3];
19710
20277
  var FINAL = /* @__PURE__ */ new Set(["EINVAL", "EBUSY", "EPERM", "EACCES"]);
19711
- function unescape(field3) {
19712
- return field3.replace(
20278
+ function unescape(field4) {
20279
+ return field4.replace(
19713
20280
  /\\([0-7]{3})/g,
19714
20281
  (_, octal) => String.fromCharCode(Number.parseInt(octal, 8))
19715
20282
  );
@@ -19905,9 +20472,9 @@ var HostfsClient = class {
19905
20472
  let buffered = "";
19906
20473
  try {
19907
20474
  for (; ; ) {
19908
- const { done: done4, value } = await reader.read();
20475
+ const { done: done4, value: value2 } = await reader.read();
19909
20476
  if (done4) return;
19910
- buffered += decoder4.decode(value, { stream: true });
20477
+ buffered += decoder4.decode(value2, { stream: true });
19911
20478
  let end = buffered.indexOf("\n");
19912
20479
  while (end >= 0) {
19913
20480
  const line = buffered.slice(0, end).trim();
@@ -20169,10 +20736,10 @@ function timed(transport, ms) {
20169
20736
  reject(fsError2("ENOTCONN", why));
20170
20737
  }, ms);
20171
20738
  running.then(
20172
- (value) => {
20739
+ (value2) => {
20173
20740
  clearTimeout(timer);
20174
- if (expired) late?.(value);
20175
- resolve2(value);
20741
+ if (expired) late?.(value2);
20742
+ resolve2(value2);
20176
20743
  },
20177
20744
  (err) => {
20178
20745
  clearTimeout(timer);
@@ -20484,9 +21051,9 @@ var ChunkedBytes = class {
20484
21051
  if (size < this.size) {
20485
21052
  const keep2 = Math.ceil(size / CHUNK3);
20486
21053
  this.chunks.length = Math.min(this.chunks.length, keep2);
20487
- const tail = this.chunks[keep2 - 1];
21054
+ const tail2 = this.chunks[keep2 - 1];
20488
21055
  const from = size - (keep2 - 1) * CHUNK3;
20489
- if (tail && from < tail.length) tail.fill(0, from);
21056
+ if (tail2 && from < tail2.length) tail2.fill(0, from);
20490
21057
  }
20491
21058
  this.size = size;
20492
21059
  }
@@ -20501,8 +21068,8 @@ var ChunkedBytes = class {
20501
21068
  };
20502
21069
 
20503
21070
  // src/mount/tmpfs.ts
20504
- function idOption2(value) {
20505
- return value !== void 0 && /^\d+$/.test(value) ? Number(value) : 0;
21071
+ function idOption2(value2) {
21072
+ return value2 !== void 0 && /^\d+$/.test(value2) ? Number(value2) : 0;
20506
21073
  }
20507
21074
  function created2(ino, kind, mode, from, owner) {
20508
21075
  const now = Date.now();
@@ -20737,7 +21304,7 @@ function importedMemory(bytes) {
20737
21304
  const count2 = u32();
20738
21305
  for (let i = 0; i < count2; i++) {
20739
21306
  const module = name2();
20740
- const field3 = name2();
21307
+ const field4 = name2();
20741
21308
  const kind = bytes[at++];
20742
21309
  if (kind === 0) u32();
20743
21310
  else if (kind === 1) {
@@ -20745,7 +21312,7 @@ function importedMemory(bytes) {
20745
21312
  limits();
20746
21313
  } else if (kind === 2) {
20747
21314
  const l = limits();
20748
- return { module, name: field3, ...l };
21315
+ return { module, name: field4, ...l };
20749
21316
  } else if (kind === 3) at += 2;
20750
21317
  else if (kind === 4) {
20751
21318
  at++;
@@ -20836,12 +21403,12 @@ function readImports(r, types) {
20836
21403
  const foreign = {};
20837
21404
  for (let i = r.u32(); i > 0; i--) {
20838
21405
  const module = r.name();
20839
- const field3 = r.name();
21406
+ const field4 = r.name();
20840
21407
  const kind = r.byte();
20841
21408
  if (kind !== 0) skipImport(r, kind);
20842
21409
  else {
20843
21410
  const type = types[r.u32()] ?? "other";
20844
- if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[field3] = type;
21411
+ if (!RESERVED_NAMESPACES.has(module)) (foreign[module] ??= {})[field4] = type;
20845
21412
  }
20846
21413
  }
20847
21414
  return foreign;
@@ -21354,7 +21921,7 @@ var LAYOUT = [
21354
21921
  ];
21355
21922
  var LOAD_SAMPLE_MS = 5e3;
21356
21923
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
21357
- var encoder7 = new TextEncoder();
21924
+ var encoder8 = new TextEncoder();
21358
21925
  var USERS_MODES = ["off", "report", "enforce"];
21359
21926
  function usersMode(mode = "off") {
21360
21927
  if (USERS_MODES.includes(mode)) return mode;
@@ -21414,10 +21981,10 @@ function shebangCommand(words2) {
21414
21981
  const arg2 = words2.slice(1).join(" ");
21415
21982
  return { interp: words2[0], args: arg2 ? [arg2] : [] };
21416
21983
  }
21417
- const split2 = words2[1] === "-S";
21418
- const [interp, ...rest2] = words2.slice(split2 ? 2 : 1);
21984
+ const split3 = words2[1] === "-S";
21985
+ const [interp, ...rest2] = words2.slice(split3 ? 2 : 1);
21419
21986
  const arg = rest2.join(" ");
21420
- return { interp, args: split2 ? rest2 : arg ? [arg] : [] };
21987
+ return { interp, args: split3 ? rest2 : arg ? [arg] : [] };
21421
21988
  }
21422
21989
  function interpreter(head) {
21423
21990
  const words2 = shebang(head);
@@ -21428,9 +21995,9 @@ function interpreter(head) {
21428
21995
  }
21429
21996
  function expandDefaults(defaults, env) {
21430
21997
  const out = {};
21431
- for (const [key, value] of Object.entries(defaults ?? {})) {
21998
+ for (const [key, value2] of Object.entries(defaults ?? {})) {
21432
21999
  let missing4 = false;
21433
- const expanded = value.replace(ENV_REFERENCE, (_, name2) => {
22000
+ const expanded = value2.replace(ENV_REFERENCE, (_, name2) => {
21434
22001
  const set3 = Object.hasOwn(env, name2) ? env[name2] : void 0;
21435
22002
  if (set3 === void 0) missing4 = true;
21436
22003
  return set3 ?? "";
@@ -21472,7 +22039,7 @@ function childHandle(handle) {
21472
22039
  onState: (listener) => handle.onState(listener)
21473
22040
  };
21474
22041
  }
21475
- function concat6(chunks) {
22042
+ function concat7(chunks) {
21476
22043
  const out = new Uint8Array(chunks.reduce((n, chunk2) => n + chunk2.length, 0));
21477
22044
  let offset = 0;
21478
22045
  for (const chunk2 of chunks) {
@@ -21577,6 +22144,8 @@ var Launcher = class {
21577
22144
  fileWatches;
21578
22145
  nextPid = 1e3;
21579
22146
  boot = Date.now();
22147
+ bootId = crypto.randomUUID();
22148
+ cron;
21580
22149
  terminals = 0;
21581
22150
  constructor(options) {
21582
22151
  this.usersMode = usersMode(options.users);
@@ -21707,6 +22276,7 @@ var Launcher = class {
21707
22276
  approve: options.approve,
21708
22277
  timeoutMs: options.sudoTimeoutMs
21709
22278
  });
22279
+ this.cron = this.cronService(options.cron);
21710
22280
  void this.unlinked.sweep("/");
21711
22281
  this.watchers.watch(
21712
22282
  [this.modulesDir, this.pnpmHome, ...this.appended],
@@ -21718,6 +22288,38 @@ var Launcher = class {
21718
22288
  this.resetCatalogs();
21719
22289
  });
21720
22290
  }
22291
+ cronService(options = {}) {
22292
+ return new CronService({
22293
+ fs: this.fs,
22294
+ owners: this.owners,
22295
+ lock: options.lock ?? "slicc-kernel-cron",
22296
+ bootId: this.bootId,
22297
+ ...options.clock ? { clock: options.clock } : {},
22298
+ locks: cronLocks(),
22299
+ ready: () => this.fstab,
22300
+ lookup: (name2) => this.users.lookup(name2),
22301
+ watch: async (paths, notify) => {
22302
+ const local = this.watchers.watch(paths, { recursive: true }, () => notify());
22303
+ const remote = await this.watchFiles(paths, notify);
22304
+ return () => {
22305
+ local();
22306
+ remote();
22307
+ };
22308
+ },
22309
+ run: async ({ account, argv, cwd, env, output }) => {
22310
+ const ended = await this.run(argv, {
22311
+ user: account,
22312
+ cwd,
22313
+ env,
22314
+ login: true,
22315
+ collect: false,
22316
+ onStdout: output,
22317
+ onStderr: output
22318
+ });
22319
+ return { status: ended.status, ...signalField(ended.signal) };
22320
+ }
22321
+ });
22322
+ }
21721
22323
  get owners() {
21722
22324
  return this.usersMode !== "off";
21723
22325
  }
@@ -22077,10 +22679,10 @@ var Launcher = class {
22077
22679
  if (!swap) throw new SpawnError("EACCES");
22078
22680
  const secrets = [];
22079
22681
  for (const [name2, placeholder] of Object.entries(swap.placeholders)) {
22080
- const value = swap.values[name2];
22081
- if (value === void 0 || env[name2] !== placeholder) continue;
22082
- env[name2] = value;
22083
- secrets.push({ name: name2, value, placeholder });
22682
+ const value2 = swap.values[name2];
22683
+ if (value2 === void 0 || env[name2] !== placeholder) continue;
22684
+ env[name2] = value2;
22685
+ secrets.push({ name: name2, value: value2, placeholder });
22084
22686
  }
22085
22687
  const scrub = scrubberOf(secrets);
22086
22688
  return { env, pid, cwd: booked.cwd, ...scrub ? { scrub } : {} };
@@ -22187,7 +22789,7 @@ var Launcher = class {
22187
22789
  if (hit !== void 0) return hit;
22188
22790
  const revocations = this.revocations;
22189
22791
  const loading2 = load();
22190
- const found = loading2.then(({ value }) => value);
22792
+ const found = loading2.then(({ value: value2 }) => value2);
22191
22793
  cache.set(key, found);
22192
22794
  const drop = () => {
22193
22795
  if (cache.get(key) === found) cache.delete(key);
@@ -23054,6 +23656,7 @@ var Launcher = class {
23054
23656
  for (const holds2 of this.held) holds2.revoke(at);
23055
23657
  }
23056
23658
  unmountAll() {
23659
+ this.cron.stop();
23057
23660
  this.booting.abort();
23058
23661
  const targets = this.mounts.list().map((m) => m.target);
23059
23662
  for (const target of targets.sort((a, b) => b.length - a.length)) this.umount(target, true);
@@ -23120,7 +23723,9 @@ var Launcher = class {
23120
23723
  if (this.owners) await this.stampLayout();
23121
23724
  if (this.owners) await this.sudoer.provision();
23122
23725
  await writeCaFile(this.base, this.ca).catch(() => void 0);
23726
+ await this.cron.provision().catch(() => void 0);
23123
23727
  this.fstab = this.mountFstab();
23728
+ void this.cron.start();
23124
23729
  }
23125
23730
  async mountFstab() {
23126
23731
  const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
@@ -23142,9 +23747,9 @@ var Launcher = class {
23142
23747
  }
23143
23748
  );
23144
23749
  }
23145
- environment(cwd, extra, account) {
23750
+ environment(cwd, extra, account, login = false) {
23146
23751
  const env = {
23147
- ...this.env,
23752
+ ...login ? networkEnv(this.hostname) : this.env,
23148
23753
  ...userEnv(account, this.pnpmHome),
23149
23754
  PWD: cwd,
23150
23755
  ...extra
@@ -23296,7 +23901,7 @@ var Launcher = class {
23296
23901
  },
23297
23902
  account
23298
23903
  );
23299
- const report = (message3) => options.onData(encoder7.encode(`${message3}\r
23904
+ const report = (message3) => options.onData(encoder8.encode(`${message3}\r
23300
23905
  `));
23301
23906
  const handle = await this.launch(planned, {
23302
23907
  env,
@@ -23324,7 +23929,7 @@ var Launcher = class {
23324
23929
  tagOption(options.tag);
23325
23930
  const account = await this.account(options.user);
23326
23931
  const { cwd, file, planned } = await this.starting(argv, options.cwd, account, options.tag);
23327
- const env = this.environment(cwd, options.env, account);
23932
+ const env = this.environment(cwd, options.env, account, options.login);
23328
23933
  const out = [];
23329
23934
  const err = [];
23330
23935
  const keep2 = options.collect !== false;
@@ -23334,13 +23939,13 @@ var Launcher = class {
23334
23939
  };
23335
23940
  const stdout = collect(out, options.onStdout);
23336
23941
  const stderr = collect(err, options.onStderr);
23337
- const report = (message3) => stderr(encoder7.encode(`${message3}
23942
+ const report = (message3) => stderr(encoder8.encode(`${message3}
23338
23943
  `));
23339
23944
  if (!planned) {
23340
23945
  const directory = await this.directory(file, this.fs.resolvePath(cwd, file));
23341
23946
  report(`${file}: ${directory ? "Is a directory" : "command not found"}`);
23342
23947
  const status2 = directory ? NOT_EXECUTABLE : NOT_FOUND;
23343
- return { status: status2, stdout: concat6(out), stderr: concat6(err) };
23948
+ return { status: status2, stdout: concat7(out), stderr: concat7(err) };
23344
23949
  }
23345
23950
  const fds = new FdTable();
23346
23951
  fds.installAt(0, options.stdin ? bytesSource(options.stdin) : deviceFile("null"));
@@ -23358,9 +23963,12 @@ var Launcher = class {
23358
23963
  options.onStarted?.(handle.pid);
23359
23964
  const status = await handle.exited;
23360
23965
  const signal = handle.termsig();
23361
- return { status, ...signal ? { signal } : {}, stdout: concat6(out), stderr: concat6(err) };
23966
+ return { status, ...signal ? { signal } : {}, stdout: concat7(out), stderr: concat7(err) };
23362
23967
  }
23363
23968
  };
23969
+ function cronLocks() {
23970
+ return globalThis.navigator?.locks;
23971
+ }
23364
23972
 
23365
23973
  // src/sealed-relay.ts
23366
23974
  function sealedAsker(port) {
@@ -23557,6 +24165,7 @@ function usersOp(l, req) {
23557
24165
  }
23558
24166
  function hostOptions(req, port, sealed, clipboard, lookup2) {
23559
24167
  return {
24168
+ cron: { lock: `slicc-kernel-cron:${req.metadata ?? META_DB}` },
23560
24169
  ...req.hostname !== void 0 ? { hostname: req.hostname } : {},
23561
24170
  ...req.users !== void 0 ? { users: req.users } : {},
23562
24171
  ...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},