@ai-ecoverse/slicc-kernel 1.54.8 → 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
  }
@@ -11818,11 +11818,24 @@ var HostfsClient = class {
11818
11818
  get maxIo() {
11819
11819
  return this.capabilities.maxIo ?? HOSTFS_IO;
11820
11820
  }
11821
- 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;
11822
11834
  let response;
11823
11835
  try {
11824
11836
  response = await this.fetch(`${this.grant.url}${path}`, {
11825
11837
  ...init,
11838
+ ...signal ? { signal } : {},
11826
11839
  credentials: "omit",
11827
11840
  headers: {
11828
11841
  ...init.headers,
@@ -11859,6 +11872,20 @@ var HostfsClient = class {
11859
11872
  );
11860
11873
  return await response.json();
11861
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
+ }
11862
11889
  async read(fh, offset, size, ifMatch) {
11863
11890
  const response = await this.send(
11864
11891
  "/api/hostfs",
@@ -11926,6 +11953,11 @@ function hostfsHandlers(current) {
11926
11953
  return fresh(remote);
11927
11954
  };
11928
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
+ });
11929
11961
  async function writable(client, path, flags) {
11930
11962
  const { ifMatch: _, ...asked } = flags;
11931
11963
  const opening = () => client.call("open", { path: rel(path), ...asked });
@@ -11936,9 +11968,9 @@ function hostfsHandlers(current) {
11936
11968
  const mode = (await client.call("stat", { path: rel(path) })).mode ?? 0;
11937
11969
  if (mode & 128) throw err;
11938
11970
  const restore = { path: rel(path), mode };
11939
- await client.call("setattr", { path: rel(path), mode: mode | 128 });
11940
- const opened2 = await opening().catch(async (again) => {
11941
- 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
+ });
11942
11974
  throw again;
11943
11975
  });
11944
11976
  return { opened: opened2, restore };
@@ -11993,7 +12025,7 @@ function hostfsHandlers(current) {
11993
12025
  try {
11994
12026
  await fresh(remote).client.call("release", { fh: remote.fh }, true);
11995
12027
  } finally {
11996
- if (remote.restore) await current().call("setattr", remote.restore);
12028
+ if (remote.restore) await settle3(current(), remote.restore);
11997
12029
  }
11998
12030
  },
11999
12031
  mkdir: async (path) => void await call("mkdir", { path: rel(path) }),
@@ -12123,6 +12155,73 @@ function invalidation(event) {
12123
12155
  return event.paths ?? true;
12124
12156
  }
12125
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
+ }
12126
12225
  var PORT_MS_PER_MIB = 1e3;
12127
12226
  var nextPortToken = 0;
12128
12227
  var PortTransport = class {
@@ -12133,13 +12232,25 @@ var PortTransport = class {
12133
12232
  waiting = /* @__PURE__ */ new Map();
12134
12233
  next = 0;
12135
12234
  dead;
12136
- constructor(grant, requestMs = PORT_REQUEST_MS) {
12235
+ beatMs;
12236
+ deadMs;
12237
+ lingerMs;
12238
+ beat;
12239
+ answers = false;
12240
+ pinged;
12241
+ constructor(grant, liveness = {}) {
12137
12242
  this.port = grant.port;
12138
- 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;
12139
12247
  this.capabilities = grant.capabilities ?? {};
12140
12248
  this.token = grant.token ?? `port-${++nextPortToken}`;
12141
12249
  this.port.addEventListener("message", (event) => {
12142
- 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);
12143
12254
  });
12144
12255
  this.port.addEventListener("close", () => this.fail("the folder's port closed"));
12145
12256
  this.port.start();
@@ -12150,8 +12261,51 @@ var PortTransport = class {
12150
12261
  get gone() {
12151
12262
  return this.dead !== void 0;
12152
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;
12153
12296
  fail(why) {
12154
- 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;
12155
12309
  this.dead ??= why;
12156
12310
  for (const settle3 of [...this.waiting.values()])
12157
12311
  settle3({ id: 0, error: { code: "ENOTCONN", message: why } });
@@ -12168,6 +12322,7 @@ var PortTransport = class {
12168
12322
  },
12169
12323
  this.requestMs + Math.ceil(bytes / 1048576) * PORT_MS_PER_MIB
12170
12324
  );
12325
+ this.watchBeat();
12171
12326
  this.waiting.set(id2, (reply) => {
12172
12327
  clearTimeout(timer);
12173
12328
  this.waiting.delete(id2);
@@ -12214,6 +12369,7 @@ var PortTransport = class {
12214
12369
  }
12215
12370
  resolve();
12216
12371
  };
12372
+ this.watchBeat();
12217
12373
  this.waiting.set(id2, (reply) => {
12218
12374
  if (reply.open) return onOpen();
12219
12375
  if (reply.event) return onEvent(reply.event);
@@ -12231,10 +12387,10 @@ var PortTransport = class {
12231
12387
  }).finally(() => this.fail("the folder's watch ended"));
12232
12388
  }
12233
12389
  };
12234
- function grantConnect(hook, fetch2, requestMs) {
12390
+ function grantConnect(hook, fetch2, liveness = {}) {
12235
12391
  return async (source, options) => {
12236
12392
  const first = await hook(source, options);
12237
- if ("port" in first) return new PortTransport(first, requestMs);
12393
+ if ("port" in first) return new PortTransport(first, liveness);
12238
12394
  const grant = async () => {
12239
12395
  const again = await hook(source, options);
12240
12396
  if ("port" in again)
@@ -12254,7 +12410,10 @@ async function openHostfs(spec, connect, timing = {}) {
12254
12410
  const stop = new AbortController();
12255
12411
  let active;
12256
12412
  const handlers = hostfsHandlers(() => active);
12257
- 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
+ });
12258
12417
  const served = serveFilesystem(
12259
12418
  port2,
12260
12419
  { ...slot.handlers, mount: ranged2 },
@@ -12270,7 +12429,7 @@ async function openHostfs(spec, connect, timing = {}) {
12270
12429
  {
12271
12430
  connect: () => connect(spec.source, { readonly: readonly2 }),
12272
12431
  up(client) {
12273
- active = client;
12432
+ active = timed(client, timing.callMs ?? HOSTFS_CALL_MS);
12274
12433
  slot.insert(client, handlers);
12275
12434
  served.invalidate(true);
12276
12435
  },
@@ -13290,11 +13449,7 @@ var Launcher = class {
13290
13449
  throw fsError("ENODEV", "this kernel has no hostfs hook (createKernel({ hostfs }))");
13291
13450
  return openHostfs(
13292
13451
  spec,
13293
- grantConnect(
13294
- hostfs,
13295
- hostfsFetch ?? ((url, init) => fetch(url, init)),
13296
- hostfsTiming?.requestMs
13297
- ),
13452
+ grantConnect(hostfs, hostfsFetch ?? ((url, init) => fetch(url, init)), hostfsTiming),
13298
13453
  hostfsTiming
13299
13454
  );
13300
13455
  }