@ai-ecoverse/slicc-kernel 1.54.7 → 1.54.9

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.
@@ -725,7 +725,7 @@ var OpfsFs = class {
725
725
  const swap = targetStat?.isDirectory === true;
726
726
  const staging = swap ? `.${name2}.${crypto.randomUUID()}` : name2;
727
727
  this.forget(destination);
728
- const timed = await this.explicitTimes(source.path);
728
+ const timed2 = await this.explicitTimes(source.path);
729
729
  const moved2 = await this.place(source.handle, dir, staging);
730
730
  if (swap) await this.rm(destination, { recursive: true });
731
731
  await this.meta.move(source.path, destination, carry);
@@ -737,7 +737,7 @@ var OpfsFs = class {
737
737
  if (!placed) await dir.removeEntry(staging, { recursive: true });
738
738
  copied ||= !placed;
739
739
  }
740
- if (copied) await this.restamp(destination, timed);
740
+ if (copied) await this.restamp(destination, timed2);
741
741
  this.changed(source.path, destination);
742
742
  this.touch(parentOf2(source.path), parent.path);
743
743
  }
@@ -746,16 +746,16 @@ var OpfsFs = class {
746
746
  return handle.kind === "file" ? (await handle.getFile()).lastModified : 0;
747
747
  }
748
748
  async explicitTimes(path) {
749
- const timed = [];
749
+ const timed2 = [];
750
750
  for (const entry of await this.meta.under(path)) {
751
751
  if (entry.mtimeFor === void 0 || entry.link !== void 0) continue;
752
752
  const current = await this.modified(entry.path).catch(() => void 0);
753
- if (current === entry.mtimeFor) timed.push(entry.path.slice(path.length));
753
+ if (current === entry.mtimeFor) timed2.push(entry.path.slice(path.length));
754
754
  }
755
- return timed;
755
+ return timed2;
756
756
  }
757
- async restamp(path, timed) {
758
- for (const rest2 of timed) {
757
+ async restamp(path, timed2) {
758
+ for (const rest2 of timed2) {
759
759
  const mtimeFor = await this.modified(path + rest2);
760
760
  await this.meta.update(path + rest2, (entry) => entry && { ...entry, mtimeFor });
761
761
  }
@@ -1806,9 +1806,9 @@ var KernelSocket = class _KernelSocket {
1806
1806
  case SO_TYPE:
1807
1807
  return SOCK_STREAM;
1808
1808
  case SO_ERROR: {
1809
- const failed = this.failed;
1809
+ const failed2 = this.failed;
1810
1810
  this.failed = void 0;
1811
- return failed ? wasiErrno(failed) : 0;
1811
+ return failed2 ? wasiErrno(failed2) : 0;
1812
1812
  }
1813
1813
  case SO_ACCEPTCONN:
1814
1814
  return this.state === "listening" ? 1 : 0;
@@ -8022,6 +8022,7 @@ function* procWalk(path, follow2) {
8022
8022
  let done4 = [];
8023
8023
  let todo = path.split("/").filter((part) => part !== "");
8024
8024
  let hops = 0;
8025
+ let broken;
8025
8026
  while (todo.length > 0) {
8026
8027
  const part = todo.shift();
8027
8028
  if (part === ".") continue;
@@ -8031,19 +8032,31 @@ function* procWalk(path, follow2) {
8031
8032
  }
8032
8033
  const at = `/${[...done4, part].join("/")}`;
8033
8034
  const whole = normalize(`/${[...done4, part, ...todo].join("/")}`);
8034
- if (inProc(at) && inProc(whole)) return whole;
8035
+ if (inProc(at) && inProc(whole)) return broken ?? whole;
8035
8036
  if (!follow2 && todo.length === 0) return void 0;
8036
- const target = yield at;
8037
- if (target === null) return void 0;
8038
- if (target === void 0) done4.push(part);
8039
- else {
8037
+ const target = yield { at, check: todo.find((next2) => next2 !== ".") === ".." };
8038
+ if (typeof target === "string") {
8040
8039
  if (++hops > MAX_HOPS2) return void 0;
8041
- if (target.startsWith("/")) done4 = [];
8042
- todo = [...target.split("/").filter((part2) => part2 !== ""), ...todo];
8040
+ [done4, todo] = throughLink(target, done4, todo);
8041
+ } else {
8042
+ broken ??= refusalOf(target);
8043
+ done4.push(part);
8043
8044
  }
8044
8045
  }
8045
8046
  return void 0;
8046
8047
  }
8048
+ function failed(err) {
8049
+ const code = err?.code;
8050
+ return { refused: typeof code === "string" ? code : "ENOENT" };
8051
+ }
8052
+ function refusalOf(found) {
8053
+ if (found === true) return void 0;
8054
+ return found === false ? { refused: "ENOTDIR" } : found;
8055
+ }
8056
+ function throughLink(target, done4, todo) {
8057
+ const parts = target.split("/").filter((part) => part !== "");
8058
+ return [target.startsWith("/") ? [] : done4, [...parts, ...todo]];
8059
+ }
8047
8060
  async function procAlias(fs, path, follow2) {
8048
8061
  const { readlink, lstat } = fs;
8049
8062
  if (!readlink || !lstat) return void 0;
@@ -8055,7 +8068,10 @@ async function procAlias(fs, path, follow2) {
8055
8068
  const walk = procWalk(path, follow2);
8056
8069
  let step = walk.next();
8057
8070
  while (!step.done) {
8058
- const target = await readlink.call(fs, step.value).catch(() => void 0);
8071
+ const { at, check } = step.value;
8072
+ const link2 = await readlink.call(fs, at).catch(() => void 0);
8073
+ const isDir = (st) => st.isDirectory === true;
8074
+ const target = link2 ?? (check ? await lstat.call(fs, at).then(isDir, failed) : true);
8059
8075
  step = walk.next(target);
8060
8076
  }
8061
8077
  return step.value;
@@ -10101,6 +10117,8 @@ var WasmProcess = class {
10101
10117
  async openVfs(req) {
10102
10118
  if (inProc(req.path)) return this.openProc(req, req.path);
10103
10119
  const alias = req.orphan ? void 0 : await procAlias(this.options.fs, req.path, !req.exclusive);
10120
+ if (typeof alias === "object")
10121
+ throw Object.assign(new Error(alias.refused), { code: alias.refused });
10104
10122
  if (alias !== void 0) return this.openProc(req, alias);
10105
10123
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
10106
10124
  const fd = this.fds.install(heldFile(), 3);
@@ -11757,22 +11775,22 @@ async function mountFstab(lines2, mount, signal, retries = FSTAB_RETRIES, discar
11757
11775
  );
11758
11776
  }
11759
11777
  async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
11760
- let failed = invalid;
11778
+ let failed2 = invalid;
11761
11779
  for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
11762
11780
  try {
11763
11781
  const entry = await mount(spec);
11764
11782
  if (!signal.aborted) return { line, target: spec.target, entry };
11765
11783
  discard(entry);
11766
- failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
11784
+ failed2 = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
11767
11785
  break;
11768
11786
  } catch (err) {
11769
- failed = err;
11787
+ failed2 = err;
11770
11788
  if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
11771
11789
  await wait(retries[attempt], signal);
11772
11790
  }
11773
11791
  }
11774
- const code = codeOf3(failed);
11775
- return { line, target: spec.target, code, error: String(failed?.message ?? code) };
11792
+ const code = codeOf3(failed2);
11793
+ return { line, target: spec.target, code, error: String(failed2?.message ?? code) };
11776
11794
  }
11777
11795
 
11778
11796
  // src/mount/hostfs.ts
@@ -11800,11 +11818,24 @@ var HostfsClient = class {
11800
11818
  get maxIo() {
11801
11819
  return this.capabilities.maxIo ?? HOSTFS_IO;
11802
11820
  }
11803
- async send(path, init, handle = false, retried = false) {
11821
+ retired;
11822
+ stop = new AbortController();
11823
+ get gone() {
11824
+ return this.retired !== void 0;
11825
+ }
11826
+ retire(why) {
11827
+ this.retired ??= why;
11828
+ this.stop.abort();
11829
+ }
11830
+ async send(path, init, handle = false, retried = false, cleanup = false) {
11831
+ if (this.retired && !cleanup) throw fsError2("ENOTCONN", this.retired);
11832
+ const stop = cleanup ? void 0 : this.stop.signal;
11833
+ const signal = init.signal && stop ? AbortSignal.any([init.signal, stop]) : init.signal ?? stop;
11804
11834
  let response;
11805
11835
  try {
11806
11836
  response = await this.fetch(`${this.grant.url}${path}`, {
11807
11837
  ...init,
11838
+ ...signal ? { signal } : {},
11808
11839
  credentials: "omit",
11809
11840
  headers: {
11810
11841
  ...init.headers,
@@ -11841,6 +11872,20 @@ var HostfsClient = class {
11841
11872
  );
11842
11873
  return await response.json();
11843
11874
  }
11875
+ async cleanup(op, body) {
11876
+ const response = await this.send(
11877
+ "/api/hostfs",
11878
+ {
11879
+ method: "POST",
11880
+ headers: { "Content-Type": "application/json" },
11881
+ body: JSON.stringify({ op, ...body })
11882
+ },
11883
+ true,
11884
+ false,
11885
+ true
11886
+ );
11887
+ await response.body?.cancel();
11888
+ }
11844
11889
  async read(fh, offset, size, ifMatch) {
11845
11890
  const response = await this.send(
11846
11891
  "/api/hostfs",
@@ -11908,6 +11953,11 @@ function hostfsHandlers(current) {
11908
11953
  return fresh(remote);
11909
11954
  };
11910
11955
  const call = (op, body = {}) => current().call(op, body);
11956
+ const settle3 = (client, restore) => client.call("setattr", restore).catch((err) => {
11957
+ if (!client.gone || !client.cleanup) throw err;
11958
+ void client.cleanup("setattr", restore).catch(() => {
11959
+ });
11960
+ });
11911
11961
  async function writable(client, path, flags) {
11912
11962
  const { ifMatch: _, ...asked } = flags;
11913
11963
  const opening = () => client.call("open", { path: rel(path), ...asked });
@@ -11918,9 +11968,9 @@ function hostfsHandlers(current) {
11918
11968
  const mode = (await client.call("stat", { path: rel(path) })).mode ?? 0;
11919
11969
  if (mode & 128) throw err;
11920
11970
  const restore = { path: rel(path), mode };
11921
- await client.call("setattr", { path: rel(path), mode: mode | 128 });
11922
- const opened2 = await opening().catch(async (again) => {
11923
- await client.call("setattr", restore);
11971
+ const opened2 = await client.call("setattr", { path: rel(path), mode: mode | 128 }).then(opening).catch(async (again) => {
11972
+ await settle3(client, restore).catch(() => {
11973
+ });
11924
11974
  throw again;
11925
11975
  });
11926
11976
  return { opened: opened2, restore };
@@ -11975,7 +12025,7 @@ function hostfsHandlers(current) {
11975
12025
  try {
11976
12026
  await fresh(remote).client.call("release", { fh: remote.fh }, true);
11977
12027
  } finally {
11978
- if (remote.restore) await current().call("setattr", remote.restore);
12028
+ if (remote.restore) await settle3(current(), remote.restore);
11979
12029
  }
11980
12030
  },
11981
12031
  mkdir: async (path) => void await call("mkdir", { path: rel(path) }),
@@ -12105,6 +12155,73 @@ function invalidation(event) {
12105
12155
  return event.paths ?? true;
12106
12156
  }
12107
12157
  var PORT_REQUEST_MS = 6e4;
12158
+ var HOSTFS_CALL_MS = 2e4;
12159
+ var PORT_BEAT_MS = 1e3;
12160
+ var PORT_DEAD_MS = 1e4;
12161
+ var PORT_LINGER_MS = 1e3;
12162
+ function timed(transport, ms) {
12163
+ const inflight = /* @__PURE__ */ new Set();
12164
+ const guard = (name2, run2, late, expire) => new Promise((resolve, reject) => {
12165
+ let expired = false;
12166
+ const running = run2();
12167
+ inflight.add(running);
12168
+ const done4 = () => inflight.delete(running);
12169
+ running.then(done4, done4);
12170
+ const timer = setTimeout(() => {
12171
+ expired = true;
12172
+ const why = `the host folder did not answer ${name2} within ${ms} ms`;
12173
+ transport.retire?.(why);
12174
+ expire?.(running);
12175
+ reject(fsError2("ENOTCONN", why));
12176
+ }, ms);
12177
+ running.then(
12178
+ (value) => {
12179
+ clearTimeout(timer);
12180
+ if (expired) late?.(value);
12181
+ resolve(value);
12182
+ },
12183
+ (err) => {
12184
+ clearTimeout(timer);
12185
+ reject(err);
12186
+ }
12187
+ );
12188
+ });
12189
+ const cleanup = async (op, body, skip) => {
12190
+ await Promise.allSettled([...inflight].filter((work) => work !== skip));
12191
+ await transport.cleanup?.(op, body);
12192
+ };
12193
+ const release = (opened2) => {
12194
+ const fh = opened2?.fh;
12195
+ if (typeof fh === "number") void cleanup("release", { fh }).catch(() => {
12196
+ });
12197
+ };
12198
+ return {
12199
+ get capabilities() {
12200
+ return transport.capabilities;
12201
+ },
12202
+ get token() {
12203
+ return transport.token;
12204
+ },
12205
+ get maxIo() {
12206
+ return transport.maxIo;
12207
+ },
12208
+ get gone() {
12209
+ return transport.gone;
12210
+ },
12211
+ retire: (why) => transport.retire?.(why),
12212
+ ...transport.cleanup ? { cleanup } : {},
12213
+ call: (op, body, handle) => guard(
12214
+ op,
12215
+ () => transport.call(op, body, handle),
12216
+ op === "open" ? release : void 0,
12217
+ op === "release" ? (running) => void cleanup("release", body ?? {}, running).catch(() => {
12218
+ }) : void 0
12219
+ ),
12220
+ read: (fh, offset, size, ifMatch) => guard("read", () => transport.read(fh, offset, size, ifMatch)),
12221
+ write: (fh, offset, bytes) => guard("write", () => transport.write(fh, offset, bytes)),
12222
+ watch: (signal, onEvent, onOpen) => transport.watch(signal, onEvent, onOpen)
12223
+ };
12224
+ }
12108
12225
  var PORT_MS_PER_MIB = 1e3;
12109
12226
  var nextPortToken = 0;
12110
12227
  var PortTransport = class {
@@ -12115,13 +12232,25 @@ var PortTransport = class {
12115
12232
  waiting = /* @__PURE__ */ new Map();
12116
12233
  next = 0;
12117
12234
  dead;
12118
- constructor(grant, requestMs = PORT_REQUEST_MS) {
12235
+ beatMs;
12236
+ deadMs;
12237
+ lingerMs;
12238
+ beat;
12239
+ answers = false;
12240
+ pinged;
12241
+ constructor(grant, liveness = {}) {
12119
12242
  this.port = grant.port;
12120
- this.requestMs = requestMs;
12243
+ this.requestMs = liveness.requestMs ?? PORT_REQUEST_MS;
12244
+ this.beatMs = liveness.beatMs ?? PORT_BEAT_MS;
12245
+ this.deadMs = liveness.deadMs ?? PORT_DEAD_MS;
12246
+ this.lingerMs = liveness.lingerMs ?? PORT_LINGER_MS;
12121
12247
  this.capabilities = grant.capabilities ?? {};
12122
12248
  this.token = grant.token ?? `port-${++nextPortToken}`;
12123
12249
  this.port.addEventListener("message", (event) => {
12124
- this.waiting.get(event.data?.id)?.(event.data);
12250
+ if (event.data?.pong) {
12251
+ this.answers = true;
12252
+ this.pinged = void 0;
12253
+ } else this.waiting.get(event.data?.id)?.(event.data);
12125
12254
  });
12126
12255
  this.port.addEventListener("close", () => this.fail("the folder's port closed"));
12127
12256
  this.port.start();
@@ -12132,8 +12261,51 @@ var PortTransport = class {
12132
12261
  get gone() {
12133
12262
  return this.dead !== void 0;
12134
12263
  }
12264
+ retire(why) {
12265
+ this.fail(why);
12266
+ }
12267
+ ping() {
12268
+ if (this.pinged !== void 0) return;
12269
+ this.pinged = Date.now();
12270
+ try {
12271
+ this.port.postMessage({ id: 0, op: "ping" });
12272
+ } catch {
12273
+ }
12274
+ }
12275
+ watchBeat() {
12276
+ if (this.beat || this.dead) return;
12277
+ this.ping();
12278
+ this.beat = setInterval(() => {
12279
+ if (this.waiting.size === 0) {
12280
+ clearInterval(this.beat);
12281
+ this.beat = void 0;
12282
+ return;
12283
+ }
12284
+ if (this.answers && this.pinged !== void 0 && Date.now() - this.pinged > this.deadMs) {
12285
+ this.fail("the folder's port stopped answering");
12286
+ return;
12287
+ }
12288
+ this.ping();
12289
+ }, this.beatMs);
12290
+ }
12291
+ async cleanup(op, body) {
12292
+ if (this.closed) throw fsError2("ENOTCONN", "the folder's port is closed");
12293
+ this.port.postMessage({ id: 0, op: "cleanup", name: op, body });
12294
+ }
12295
+ closed = false;
12135
12296
  fail(why) {
12136
- if (this.dead === void 0) this.port.close();
12297
+ if (this.dead === void 0) {
12298
+ setTimeout(() => {
12299
+ try {
12300
+ this.port.postMessage({ id: 0, op: "bye" });
12301
+ } catch {
12302
+ }
12303
+ this.closed = true;
12304
+ this.port.close();
12305
+ }, this.lingerMs);
12306
+ }
12307
+ clearInterval(this.beat);
12308
+ this.beat = void 0;
12137
12309
  this.dead ??= why;
12138
12310
  for (const settle3 of [...this.waiting.values()])
12139
12311
  settle3({ id: 0, error: { code: "ENOTCONN", message: why } });
@@ -12150,6 +12322,7 @@ var PortTransport = class {
12150
12322
  },
12151
12323
  this.requestMs + Math.ceil(bytes / 1048576) * PORT_MS_PER_MIB
12152
12324
  );
12325
+ this.watchBeat();
12153
12326
  this.waiting.set(id2, (reply) => {
12154
12327
  clearTimeout(timer);
12155
12328
  this.waiting.delete(id2);
@@ -12196,6 +12369,7 @@ var PortTransport = class {
12196
12369
  }
12197
12370
  resolve();
12198
12371
  };
12372
+ this.watchBeat();
12199
12373
  this.waiting.set(id2, (reply) => {
12200
12374
  if (reply.open) return onOpen();
12201
12375
  if (reply.event) return onEvent(reply.event);
@@ -12213,10 +12387,10 @@ var PortTransport = class {
12213
12387
  }).finally(() => this.fail("the folder's watch ended"));
12214
12388
  }
12215
12389
  };
12216
- function grantConnect(hook, fetch2, requestMs) {
12390
+ function grantConnect(hook, fetch2, liveness = {}) {
12217
12391
  return async (source, options) => {
12218
12392
  const first = await hook(source, options);
12219
- if ("port" in first) return new PortTransport(first, requestMs);
12393
+ if ("port" in first) return new PortTransport(first, liveness);
12220
12394
  const grant = async () => {
12221
12395
  const again = await hook(source, options);
12222
12396
  if ("port" in again)
@@ -12236,7 +12410,10 @@ async function openHostfs(spec, connect, timing = {}) {
12236
12410
  const stop = new AbortController();
12237
12411
  let active;
12238
12412
  const handlers = hostfsHandlers(() => active);
12239
- const ranged2 = () => active?.capabilities.ranges === true ? { ranges: true } : {};
12413
+ const ranged2 = () => ({
12414
+ ...active?.capabilities.ranges === true ? { ranges: true } : {},
12415
+ ...active ? { maxIo: Math.min(active.maxIo, HOSTFS_IO) } : {}
12416
+ });
12240
12417
  const served = serveFilesystem(
12241
12418
  port2,
12242
12419
  { ...slot.handlers, mount: ranged2 },
@@ -12252,7 +12429,7 @@ async function openHostfs(spec, connect, timing = {}) {
12252
12429
  {
12253
12430
  connect: () => connect(spec.source, { readonly: readonly2 }),
12254
12431
  up(client) {
12255
- active = client;
12432
+ active = timed(client, timing.callMs ?? HOSTFS_CALL_MS);
12256
12433
  slot.insert(client, handlers);
12257
12434
  served.invalidate(true);
12258
12435
  },
@@ -13272,11 +13449,7 @@ var Launcher = class {
13272
13449
  throw fsError("ENODEV", "this kernel has no hostfs hook (createKernel({ hostfs }))");
13273
13450
  return openHostfs(
13274
13451
  spec,
13275
- grantConnect(
13276
- hostfs,
13277
- hostfsFetch ?? ((url, init) => fetch(url, init)),
13278
- hostfsTiming?.requestMs
13279
- ),
13452
+ grantConnect(hostfs, hostfsFetch ?? ((url, init) => fetch(url, init)), hostfsTiming),
13280
13453
  hostfsTiming
13281
13454
  );
13282
13455
  }