@ai-ecoverse/slicc-kernel 1.125.12 → 1.126.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.
@@ -1098,6 +1098,10 @@ var STALE = /* @__PURE__ */ new Set(["NotFoundError", "TypeMismatchError"]);
1098
1098
  var REPLACED_ATTEMPTS = 4;
1099
1099
  var RECONCILE_LISTINGS = 8;
1100
1100
  var EXECUTABLE = /\/node_modules\/(?:@[^/]+\/)?[^/]+\/bin\//;
1101
+ var RENAMES = "/.slicc-renames";
1102
+ function movable(handle) {
1103
+ return typeof handle.move === "function";
1104
+ }
1101
1105
  var ERRNO_BY_DOM_ERROR = {
1102
1106
  NotFoundError: "ENOENT",
1103
1107
  TypeMismatchError: "ENOTDIR",
@@ -2271,9 +2275,9 @@ var OpfsFs = class {
2271
2275
  }
2272
2276
  const now = Date.now();
2273
2277
  const moving = await this.movedOwner(source);
2274
- const carry = (entry) => ({
2278
+ const carry = (entry2) => ({
2275
2279
  ino: inodeOf(source.path),
2276
- ...occupied(entry),
2280
+ ...occupied(entry2),
2277
2281
  ...moving,
2278
2282
  path: destination,
2279
2283
  ctimeMs: now
@@ -2289,29 +2293,127 @@ var OpfsFs = class {
2289
2293
  const name2 = baseOf(destination);
2290
2294
  const swap = targetStat?.isDirectory === true;
2291
2295
  const staging = swap ? `.${name2}.${crypto.randomUUID()}` : name2;
2292
- this.forget(destination);
2293
- const timed2 = await this.explicitTimes(source.path);
2294
- const moved2 = await this.replacingTarget(
2295
- targetStat !== null,
2296
- () => this.place(source.handle, dir, staging)
2296
+ const copying = sourceStat.isDirectory && !movable(source.handle);
2297
+ const staged = resolvePath(parent.path, staging);
2298
+ const place = async () => {
2299
+ this.forget(destination);
2300
+ const timed2 = await this.explicitTimes(source.path);
2301
+ const first = copying ? await this.intend({ from: source.path, to: staged }) : void 0;
2302
+ const second = copying && swap ? await this.intend({ from: staged, to: destination, replace: true }) : void 0;
2303
+ const moved2 = await this.replacingTarget(
2304
+ targetStat !== null,
2305
+ () => this.place(source.handle, dir, staging)
2306
+ ).catch(async (err) => {
2307
+ if (first) await this.rollBack(staged, source.path, [first, second]);
2308
+ throw err;
2309
+ });
2310
+ if (swap) await this.rm(destination, { recursive: true });
2311
+ if (second) await this.intend({ from: staged, to: destination }, second);
2312
+ await this.meta.move(source.path, destination, carry, left);
2313
+ this.movePending(source.path, destination);
2314
+ if (!moved2) {
2315
+ await this.rm(source.path, { recursive: true });
2316
+ await this.meta.update(source.path, () => left);
2317
+ }
2318
+ if (first) await this.meta.update(first, () => void 0);
2319
+ let copied = !moved2;
2320
+ if (swap) {
2321
+ const hidden = await dir.getDirectoryHandle(staging);
2322
+ const placed = await this.place(hidden, dir, name2);
2323
+ if (!placed) await dir.removeEntry(staging, { recursive: true });
2324
+ copied ||= !placed;
2325
+ }
2326
+ if (second) await this.meta.update(second, () => void 0);
2327
+ if (copied) await this.restamp(destination, timed2);
2328
+ this.changed(source.path, destination);
2329
+ this.touch(Date.now(), parentOf3(source.path), parent.path);
2330
+ };
2331
+ if (!copying) return place();
2332
+ const { promise, resolve: resolve2 } = Promise.withResolvers();
2333
+ const entry = { from: source.path, to: destination, done: promise };
2334
+ this.renamingDirs.add(entry);
2335
+ try {
2336
+ await place();
2337
+ } finally {
2338
+ this.renamingDirs.delete(entry);
2339
+ resolve2();
2340
+ }
2341
+ }
2342
+ renamingDirs = /* @__PURE__ */ new Set();
2343
+ intents = 0;
2344
+ async intend(rename, at = `${RENAMES}/${crypto.randomUUID()}`) {
2345
+ const record = { ...rename, at: Date.now() * 1e3 + this.intents++ % 1e3 };
2346
+ await this.meta.update(at, () => ({ path: at, rename: record }));
2347
+ return at;
2348
+ }
2349
+ async rollBack(from, to, intents) {
2350
+ try {
2351
+ if (await this.real(from)) {
2352
+ const moved2 = await this.locate(from, false);
2353
+ const back = await this.locate(parentOf3(to), true);
2354
+ await this.copy(
2355
+ moved2.handle,
2356
+ back.handle,
2357
+ baseOf(to)
2358
+ );
2359
+ const [dir, name2] = await this.parent(from);
2360
+ await this.inDir(dir, (parent) => parent.removeEntry(name2, { recursive: true }));
2361
+ }
2362
+ this.forget(from);
2363
+ this.forget(to);
2364
+ for (const intent of intents) if (intent) await this.meta.update(intent, () => void 0);
2365
+ } catch {
2366
+ }
2367
+ }
2368
+ async finishRenames() {
2369
+ const intents = (await this.meta.under(RENAMES)).filter((entry) => entry.rename !== void 0).sort(
2370
+ (a, b) => a.rename.at - b.rename.at
2297
2371
  );
2298
- if (swap) await this.rm(destination, { recursive: true });
2299
- await this.meta.move(source.path, destination, carry, left);
2300
- this.movePending(source.path, destination);
2301
- if (!moved2) {
2302
- await this.rm(source.path, { recursive: true });
2303
- await this.meta.update(source.path, () => left);
2304
- }
2305
- let copied = !moved2;
2306
- if (swap) {
2307
- const staged = await dir.getDirectoryHandle(staging);
2308
- const placed = await this.place(staged, dir, name2);
2309
- if (!placed) await dir.removeEntry(staging, { recursive: true });
2310
- copied ||= !placed;
2311
- }
2312
- if (copied) await this.restamp(destination, timed2);
2313
- this.changed(source.path, destination);
2314
- this.touch(Date.now(), parentOf3(source.path), parent.path);
2372
+ for (const entry of intents) {
2373
+ const { from, to, replace } = entry.rename;
2374
+ const left = await this.real(from);
2375
+ if (left && replace && await this.real(to)) await this.rm(to, { recursive: true });
2376
+ if (left) {
2377
+ const source = await this.locate(from, false);
2378
+ const target = await this.locate(parentOf3(to), true);
2379
+ await this.copy(
2380
+ source.handle,
2381
+ target.handle,
2382
+ baseOf(to)
2383
+ );
2384
+ const [dir, name2] = await this.parent(from);
2385
+ await this.inDir(dir, (parent) => parent.removeEntry(name2, { recursive: true }));
2386
+ this.forget(from);
2387
+ this.forget(to);
2388
+ }
2389
+ if ((await this.meta.under(from)).length > 0) {
2390
+ await this.meta.move(from, to, (moved2) => ({ ...occupied(moved2), path: to }));
2391
+ }
2392
+ await this.meta.update(entry.path, () => void 0);
2393
+ }
2394
+ return intents.length;
2395
+ }
2396
+ async settledRename(path) {
2397
+ if (this.renamingDirs.size === 0 || typeof path !== "string") return;
2398
+ const at = normalizePath(path);
2399
+ for (let real; ; ) {
2400
+ const busy = this.renameAt(at) ?? (real === void 0 ? void 0 : this.renameAt(real));
2401
+ if (busy) {
2402
+ await busy.done;
2403
+ continue;
2404
+ }
2405
+ if (real !== void 0 || at === "/") return;
2406
+ const parent = await this.realpath(parentOf3(at), true).catch(() => parentOf3(at));
2407
+ real = resolvePath(parent, baseOf(at));
2408
+ if (real === at) return;
2409
+ }
2410
+ }
2411
+ renameAt(at) {
2412
+ for (const m of this.renamingDirs) {
2413
+ if (within2(at, m.from) || within2(at, m.to) || at === parentOf3(m.from) || at === parentOf3(m.to))
2414
+ return m;
2415
+ }
2416
+ return void 0;
2315
2417
  }
2316
2418
  async replacingTarget(replaces, op) {
2317
2419
  if (!replaces) return op();
@@ -2353,15 +2455,29 @@ var OpfsFs = class {
2353
2455
  () => false
2354
2456
  );
2355
2457
  if (moved2) return true;
2458
+ const before = this.fileMoves;
2356
2459
  try {
2357
2460
  await this.copy(handle, dir, name2);
2358
2461
  } catch (err) {
2359
- if (handle.kind === "directory") await dir.removeEntry(name2, { recursive: true });
2462
+ if (handle.kind === "directory" && this.fileMoves === before) {
2463
+ await dir.removeEntry(name2, { recursive: true });
2464
+ }
2360
2465
  throw translate(err, name2);
2361
2466
  }
2362
2467
  return false;
2363
2468
  }
2469
+ fileMoves = 0;
2364
2470
  async copy(handle, dir, name2) {
2471
+ if (handle.kind === "file" && movable(handle)) {
2472
+ const moved2 = await handle.move?.(dir, name2).then(
2473
+ () => true,
2474
+ () => false
2475
+ );
2476
+ if (moved2) {
2477
+ this.fileMoves++;
2478
+ return;
2479
+ }
2480
+ }
2365
2481
  if (handle.kind === "file") {
2366
2482
  const writable2 = await (await dir.getFileHandle(name2, { create: true })).createWritable();
2367
2483
  await writable2.write(await handle.getFile());
@@ -2498,6 +2614,7 @@ var OpfsFs = class {
2498
2614
  }
2499
2615
  async reconcile() {
2500
2616
  if (this.remote && await this.remote.peered()) return 0;
2617
+ await this.finishRenames();
2501
2618
  let removed = 0;
2502
2619
  const entries = await this.meta.all();
2503
2620
  const carried = new Set(entries.filter((e) => !e.vacated).map((e) => e.ino));
@@ -2528,7 +2645,9 @@ var READS = [
2528
2645
  "readdir",
2529
2646
  "readdirStat",
2530
2647
  "readlink",
2531
- "realpath"
2648
+ "realpath",
2649
+ "version",
2650
+ "readVersioned"
2532
2651
  ];
2533
2652
  var WRITES = [
2534
2653
  "writeFile",
@@ -2554,6 +2673,7 @@ for (const [names2, op] of [
2554
2673
  const inner = proto[name2];
2555
2674
  proto[name2] = async function(...args) {
2556
2675
  try {
2676
+ if (op === "read") await this.settledRename(args[0]);
2557
2677
  return await inner.apply(this, args);
2558
2678
  } catch (err) {
2559
2679
  throw translate(err, String(args[0]), op, false);
@@ -6699,7 +6819,7 @@ function satisfies(version, range2) {
6699
6819
  }
6700
6820
 
6701
6821
  // src/version.ts
6702
- var KERNEL_VERSION = true ? "1.125.12" : "0.0.0-dev";
6822
+ var KERNEL_VERSION = true ? "1.126.1" : "0.0.0-dev";
6703
6823
  var OS_RELEASE = "6.0.0-slicc";
6704
6824
  function procVersion() {
6705
6825
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -8331,10 +8451,10 @@ function tunnelRequestUrl(req, origin) {
8331
8451
  throw new HttpError(400, "malformed request target");
8332
8452
  }
8333
8453
  const hosts = fieldValues(req.headers, "host");
8334
- const hostOk = hosts.every(
8454
+ const hostOk2 = hosts.every(
8335
8455
  (h) => URL.canParse(`https://${h}`) && new URL(`https://${h}`).host === base.host
8336
8456
  );
8337
- if (url.origin !== base.origin || !hostOk) {
8457
+ if (url.origin !== base.origin || !hostOk2) {
8338
8458
  throw new HttpError(421, `this tunnel is for ${base.host}`);
8339
8459
  }
8340
8460
  return url.href;
@@ -17662,6 +17782,10 @@ function controlsOf(o, session, protocol, chain2, shape) {
17662
17782
  ...o.insecure ? insecureOf(session, chain2, shape) : { hostnameMatches: checked || !session.nameMismatch }
17663
17783
  };
17664
17784
  }
17785
+ function verdictOf(o, session) {
17786
+ if (o.bundled !== true || isIP(o.servername) || session.verifyResult !== 0) return false;
17787
+ return o.checkHostname !== false || !session.nameMismatch;
17788
+ }
17665
17789
  async function infoOf(o, session) {
17666
17790
  const flags2 = session.verifyResult;
17667
17791
  const code = verifyCode(flags2);
@@ -17674,7 +17798,9 @@ async function infoOf(o, session) {
17674
17798
  authorized: code === void 0,
17675
17799
  ...code ? { authorizationError: code, authorizationText: o.engine.verifyInfo(flags2).trim() } : {},
17676
17800
  peerCertificates: chain2,
17677
- ...controlsOf(o, session, protocol, chain2, shape)
17801
+ ...controlsOf(o, session, protocol, chain2, shape),
17802
+ verified: verdictOf(o, session),
17803
+ handshakeAt: Date.now()
17678
17804
  };
17679
17805
  }
17680
17806
  var FATAL_ALERT = -30592;
@@ -17792,6 +17918,7 @@ function sessionOf(info, servername) {
17792
17918
  return {
17793
17919
  path: "kernel",
17794
17920
  substitutes: false,
17921
+ verified: info.verified,
17795
17922
  protocol: info.protocol,
17796
17923
  alpnProtocol: info.alpnProtocol,
17797
17924
  authorized: info.authorized,
@@ -17905,7 +18032,8 @@ async function prepareWith(support, req, servername, cert) {
17905
18032
  insecure: req.insecure === true,
17906
18033
  ...req.sni === false ? { sni: false } : {},
17907
18034
  ...req.checkHostname === false ? { checkHostname: false } : {},
17908
- clientCertificate: cert !== void 0
18035
+ clientCertificate: cert !== void 0,
18036
+ bundled: own === void 0
17909
18037
  };
17910
18038
  let session;
17911
18039
  try {
@@ -22645,6 +22773,79 @@ function scrubStdio(fds, scrubber) {
22645
22773
  }
22646
22774
  }
22647
22775
 
22776
+ // src/kernel/sealed-session.ts
22777
+ var SESSION_WINDOW_MS = 6e4;
22778
+ var SESSION_PLACEHOLDERS = 16;
22779
+ var SESSION_PLACEHOLDER = /^fnox_proxy_[0-9a-f]{24}$/;
22780
+ var SESSION_ID = /^[A-Za-z0-9_-]{16,128}$/;
22781
+ var TOKEN5 = "[!#$%&'*+.^_`|~0-9a-z-]+";
22782
+ var HEADER = new RegExp(`^header:${TOKEN5}$`);
22783
+ var METHOD = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
22784
+ var TARGET = /^[\x21-\x7e]+$/;
22785
+ var EXIT = /^(page|proxy|tailscale|device:[\x21-\x7e]+)$/;
22786
+ var HOST = /^[a-z0-9.:-]+$/;
22787
+ var UNSAFE = /[\r\n\0]/;
22788
+ var POSITIONS = {
22789
+ http1: (position) => HEADER.test(position),
22790
+ smtp: (position) => position === "auth-plain" || position === "auth-login",
22791
+ pg: (position) => position === "password-cleartext"
22792
+ };
22793
+ function boundHost(servername) {
22794
+ const lower = servername.toLowerCase();
22795
+ return lower.endsWith(".") ? lower.slice(0, -1) : lower;
22796
+ }
22797
+ function headOk(f) {
22798
+ if (f.protocol !== "http1") return f.head === void 0;
22799
+ const head = f.head;
22800
+ return head !== void 0 && typeof head.method === "string" && METHOD.test(head.method) && typeof head.path === "string" && TARGET.test(head.path);
22801
+ }
22802
+ function placeholdersOk(list) {
22803
+ if (!Array.isArray(list) || list.length === 0 || list.length > SESSION_PLACEHOLDERS) return false;
22804
+ return new Set(list).size === list.length && list.every((p) => typeof p === "string" && SESSION_PLACEHOLDER.test(p));
22805
+ }
22806
+ function hostOk(host) {
22807
+ return typeof host === "string" && HOST.test(host) && boundHost(host) === host;
22808
+ }
22809
+ function sessionAskable(f, now) {
22810
+ if (!Object.hasOwn(POSITIONS, f.protocol) || typeof f.position !== "string") return false;
22811
+ if (!POSITIONS[f.protocol](f.position)) return false;
22812
+ if (typeof f.session !== "string" || !SESSION_ID.test(f.session)) return false;
22813
+ if (f.verified !== true || !hostOk(f.host) || typeof f.handshakeAt !== "number") return false;
22814
+ if (!Number.isInteger(f.port) || f.port < 1 || f.port > 65535) return false;
22815
+ const age = now - f.handshakeAt;
22816
+ if (!Number.isFinite(age) || age < 0 || age > SESSION_WINDOW_MS) return false;
22817
+ return typeof f.exit === "string" && EXIT.test(f.exit) && placeholdersOk(f.placeholders) && headOk(f);
22818
+ }
22819
+ function sessionRequest(f) {
22820
+ return {
22821
+ v: 1,
22822
+ op: "sessionMaterialize",
22823
+ caller: f.stamp,
22824
+ stamp: f.stamp,
22825
+ session: f.session,
22826
+ host: f.host,
22827
+ port: f.port,
22828
+ protocol: f.protocol,
22829
+ position: f.position,
22830
+ verified: true,
22831
+ handshakeAt: f.handshakeAt,
22832
+ exit: f.exit,
22833
+ placeholders: [...f.placeholders],
22834
+ ...f.head ? { head: { method: f.head.method, path: f.head.path } } : {}
22835
+ };
22836
+ }
22837
+ function sessionValues(reply, asked) {
22838
+ if (typeof reply !== "object" || reply === null || Array.isArray(reply)) return void 0;
22839
+ const { error, values } = reply;
22840
+ if (error !== void 0 || typeof values !== "object" || values === null) return void 0;
22841
+ const out = {};
22842
+ for (const ph of asked) {
22843
+ const value2 = values[ph];
22844
+ if (typeof value2 === "string" && value2.length > 0 && !UNSAFE.test(value2)) out[ph] = value2;
22845
+ }
22846
+ return Object.keys(out).length > 0 ? out : void 0;
22847
+ }
22848
+
22648
22849
  // src/kernel/sealed.ts
22649
22850
  var SEALED_MAX_BYTES = 64 * 1024;
22650
22851
  var SEALED_TIMEOUT_MS = 1e4;
@@ -22839,6 +23040,15 @@ var SealedBroker = class {
22839
23040
  };
22840
23041
  }
22841
23042
  async consume(fields) {
23043
+ const req = { ...fields, v: 1, op: "materializeConsume", caller: fields.stamp };
23044
+ return this.ask(req, swapOf);
23045
+ }
23046
+ async sessionMaterialize(fields, now = Date.now()) {
23047
+ if (!sessionAskable(fields, now)) return void 0;
23048
+ const req = sessionRequest(fields);
23049
+ return this.ask(req, (reply) => sessionValues(reply, req.placeholders));
23050
+ }
23051
+ async ask(req, parse) {
22842
23052
  const { hook } = this;
22843
23053
  if (!hook) return void 0;
22844
23054
  const control = new AbortController();
@@ -22846,14 +23056,8 @@ var SealedBroker = class {
22846
23056
  const stopped = new Promise((resolve2) => {
22847
23057
  control.signal.addEventListener("abort", () => resolve2(void 0), { once: true });
22848
23058
  });
22849
- const req = {
22850
- ...fields,
22851
- v: 1,
22852
- op: "materializeConsume",
22853
- caller: fields.stamp
22854
- };
22855
23059
  try {
22856
- const asked = Promise.resolve().then(() => hook(req, { signal: control.signal })).then(swapOf, () => void 0);
23060
+ const asked = Promise.resolve().then(() => hook(req, { signal: control.signal })).then(parse, () => void 0);
22857
23061
  return await Promise.race([asked, stopped]);
22858
23062
  } finally {
22859
23063
  clearTimeout(timer);