@ai-ecoverse/slicc-kernel 1.14.0 → 1.15.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.
package/dist/node.js CHANGED
@@ -585,7 +585,8 @@ var ERRNO_BY_DOM_ERROR = {
585
585
  TypeMismatchError: "ENOTDIR",
586
586
  InvalidModificationError: "ENOTEMPTY",
587
587
  NoModificationAllowedError: "EBUSY",
588
- TypeError: "EINVAL"
588
+ TypeError: "EINVAL",
589
+ NotAllowedError: "EACCES"
589
590
  };
590
591
  function translate(err, path) {
591
592
  if (typeof err?.code === "string") return err;
@@ -5910,6 +5911,157 @@ function serveFilesystem(port, handlers, capabilities = {}) {
5910
5911
  return { invalidate: (paths) => reply({ invalidate: paths }) };
5911
5912
  }
5912
5913
 
5914
+ // src/mount/fsa.ts
5915
+ var FSA_CAPABILITIES = { listingStats: true };
5916
+ var codeOf = (err) => err?.code;
5917
+ var denied = (err) => codeOf(err) === "EACCES" || err?.name === "NotAllowedError";
5918
+ function attrOf(st) {
5919
+ return {
5920
+ kind: st.isDirectory ? "directory" : st.isSymbolicLink ? "symlink" : "file",
5921
+ size: st.size,
5922
+ mtime: st.mtime.getTime(),
5923
+ mode: st.mode,
5924
+ ino: st.ino
5925
+ };
5926
+ }
5927
+ function kernelFsHandlers(fs) {
5928
+ const handles = /* @__PURE__ */ new Map();
5929
+ let nextFh = 0;
5930
+ const handle = (fh) => {
5931
+ const found = handles.get(fh);
5932
+ if (!found) throw fsError2("EBADF", `handle ${fh}`);
5933
+ return found;
5934
+ };
5935
+ async function existing(path) {
5936
+ try {
5937
+ return await fs.stat(path);
5938
+ } catch (err) {
5939
+ if (codeOf(err) === "ENOENT") return void 0;
5940
+ throw err;
5941
+ }
5942
+ }
5943
+ return {
5944
+ getattr: async (path) => attrOf(await fs.lstat(path)),
5945
+ readdir: async (path) => (await fs.readdirStat(path)).flatMap(([name2, st]) => {
5946
+ if (!st) return [];
5947
+ const attr = attrOf(st);
5948
+ return [{ name: name2, kind: attr.kind, attr }];
5949
+ }),
5950
+ async open(path, flags) {
5951
+ const st = await existing(path);
5952
+ if (st && flags.exclusive) throw fsError2("EEXIST", path);
5953
+ if (st?.isDirectory && flags.write) throw fsError2("EISDIR", path);
5954
+ if (!st && !flags.create) throw fsError2("ENOENT", path);
5955
+ const data = st && !flags.truncate ? await fs.readFileBuffer(path) : new Uint8Array(0);
5956
+ handles.set(++nextFh, { path, data, length: data.length, dirty: !st || flags.truncate });
5957
+ return nextFh;
5958
+ },
5959
+ async read(fh, offset, size) {
5960
+ const h = handle(fh);
5961
+ return h.data.slice(offset, Math.min(h.length, offset + size));
5962
+ },
5963
+ async write(fh, offset, bytes) {
5964
+ const h = handle(fh);
5965
+ const end = offset + bytes.length;
5966
+ if (end > h.data.length) {
5967
+ const grown = new Uint8Array(Math.max(end, h.data.length * 2));
5968
+ grown.set(h.data.subarray(0, h.length));
5969
+ h.data = grown;
5970
+ }
5971
+ h.data.set(bytes, offset);
5972
+ h.length = Math.max(h.length, end);
5973
+ h.dirty = true;
5974
+ },
5975
+ async release(fh) {
5976
+ const h = handle(fh);
5977
+ handles.delete(fh);
5978
+ if (h.dirty) await fs.writeFile(h.path, h.data.slice(0, h.length));
5979
+ },
5980
+ mkdir: (path) => fs.mkdir(path),
5981
+ async rmdir(path) {
5982
+ if (!(await fs.lstat(path)).isDirectory) throw fsError2("ENOTDIR", path);
5983
+ if ((await fs.readdir(path)).length > 0) throw fsError2("ENOTEMPTY", path);
5984
+ await fs.rm(path);
5985
+ },
5986
+ async unlink(path) {
5987
+ if ((await fs.lstat(path)).isDirectory) throw fsError2("EISDIR", path);
5988
+ await fs.rm(path);
5989
+ },
5990
+ rename: (from, to) => fs.rename(from, to),
5991
+ symlink: (target, path) => fs.symlink(target, path),
5992
+ readlink: (path) => fs.readlink(path),
5993
+ async setattr(path, change) {
5994
+ if (change.mode !== void 0) await fs.chmod(path, change.mode);
5995
+ if (change.mtime !== void 0) {
5996
+ await fs.utimes(path, new Date(change.mtime), new Date(change.mtime));
5997
+ }
5998
+ }
5999
+ };
6000
+ }
6001
+ var EMPTY_ROOT = { kind: "directory", size: 0, mtime: 0, mode: 16877 };
6002
+ function granted(handle) {
6003
+ if (!handle.queryPermission) return Promise.resolve(true);
6004
+ return handle.queryPermission({ mode: "readwrite" }).then(
6005
+ (state) => state === "granted",
6006
+ () => false
6007
+ );
6008
+ }
6009
+ function removableMedium(onLost, open = (handle) => new OpfsFs(handle)) {
6010
+ let current;
6011
+ const absent = () => fsError2("ENOMEDIUM", "No medium found");
6012
+ function wrap(pick) {
6013
+ return async (...args) => {
6014
+ const medium = current;
6015
+ if (!medium) throw absent();
6016
+ try {
6017
+ return await pick(medium.handlers)(...args);
6018
+ } catch (err) {
6019
+ if (!denied(err) || await granted(medium.handle)) throw err;
6020
+ if (current === medium) {
6021
+ current = void 0;
6022
+ onLost(medium.handle);
6023
+ }
6024
+ throw absent();
6025
+ }
6026
+ };
6027
+ }
6028
+ const getattr = wrap((h) => h.getattr);
6029
+ const readdir = wrap((h) => h.readdir);
6030
+ return {
6031
+ handlers: {
6032
+ getattr: (path) => current || path !== "/" ? getattr(path) : Promise.resolve(EMPTY_ROOT),
6033
+ readdir: (path) => current || path !== "/" ? readdir(path) : Promise.resolve([]),
6034
+ open: wrap((h) => h.open),
6035
+ read: wrap((h) => h.read),
6036
+ write: wrap((h) => h.write),
6037
+ release: wrap((h) => h.release),
6038
+ mkdir: wrap((h) => h.mkdir),
6039
+ rmdir: wrap((h) => h.rmdir),
6040
+ unlink: wrap((h) => h.unlink),
6041
+ rename: wrap((h) => h.rename),
6042
+ symlink: wrap((h) => h.symlink),
6043
+ readlink: wrap((h) => h.readlink),
6044
+ setattr: wrap((h) => h.setattr)
6045
+ },
6046
+ present: () => current !== void 0,
6047
+ insert(handle) {
6048
+ current = { handle, handlers: kernelFsHandlers(open(handle)) };
6049
+ },
6050
+ eject() {
6051
+ current = void 0;
6052
+ }
6053
+ };
6054
+ }
6055
+
6056
+ // src/mount/media.ts
6057
+ function memoryMedia() {
6058
+ const handles = /* @__PURE__ */ new Map();
6059
+ return {
6060
+ get: async (id) => handles.get(id),
6061
+ put: async (id, handle) => void handles.set(id, handle)
6062
+ };
6063
+ }
6064
+
5913
6065
  // src/mount/connection.ts
5914
6066
  var DRIVER_TIMEOUT_MS = 3e4;
5915
6067
  function unref(timer) {
@@ -6053,14 +6205,16 @@ var MountTable = class {
6053
6205
  this.deps = deps;
6054
6206
  }
6055
6207
  list() {
6056
- return [...this.mounts.values()].map(({ type, source, target, options, state, error }) => ({
6057
- type,
6058
- source,
6059
- target,
6060
- options: { ...options },
6061
- state,
6062
- ...error ? { error } : {}
6063
- }));
6208
+ return [...this.mounts.values()].map(
6209
+ ({ type, source, target, options, state, error, present }) => ({
6210
+ type,
6211
+ source,
6212
+ target,
6213
+ options: { ...options },
6214
+ state: state === "ok" && present?.() === false ? "nomedium" : state,
6215
+ ...error ? { error } : {}
6216
+ })
6217
+ );
6064
6218
  }
6065
6219
  locate(path) {
6066
6220
  let best;
@@ -6131,7 +6285,8 @@ var MountTable = class {
6131
6285
  maxFile: options.maxfile !== void 0 ? parseSize(options.maxfile) : caps.maxFile ?? 0,
6132
6286
  attrs: /* @__PURE__ */ new Map(),
6133
6287
  lists: /* @__PURE__ */ new Map(),
6134
- dispose: () => opened2.dispose()
6288
+ dispose: () => opened2.dispose(),
6289
+ ...opened2.present ? { present: opened2.present } : {}
6135
6290
  };
6136
6291
  mounted = mount;
6137
6292
  this.mounts.set(target, mount);
@@ -6861,6 +7016,10 @@ var Launcher = class {
6861
7016
  pnpmHome;
6862
7017
  mounts;
6863
7018
  createDriverWorker;
7019
+ media;
7020
+ onMountPending;
7021
+ removable = /* @__PURE__ */ new Map();
7022
+ inserted = /* @__PURE__ */ new Map();
6864
7023
  openFiles = /* @__PURE__ */ new Set();
6865
7024
  held = /* @__PURE__ */ new Set();
6866
7025
  nextPid = 1e3;
@@ -6875,6 +7034,8 @@ var Launcher = class {
6875
7034
  this.base = this.watchers.wrap(this.mounts.wrap(options.fs));
6876
7035
  this.createWorker = options.createWorker;
6877
7036
  this.createDriverWorker = options.createDriverWorker;
7037
+ this.media = options.media ?? memoryMedia();
7038
+ this.onMountPending = options.onMountPending;
6878
7039
  this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
6879
7040
  this.modulesDir = options.modules ?? "/node_modules";
6880
7041
  this.env = { ...networkEnv(), ...options.env };
@@ -7138,7 +7299,8 @@ var Launcher = class {
7138
7299
  if (sig !== 0) handle.signal(sig);
7139
7300
  return true;
7140
7301
  }
7141
- async driver(type, _spec) {
7302
+ async driver(type, spec) {
7303
+ if (type === "fsa") return this.fsaDriver(spec);
7142
7304
  if (type === "tmpfs") {
7143
7305
  const { port1, port2 } = new MessageChannel();
7144
7306
  serveFilesystem(port2, tmpfs(), { symlinks: true, chmod: true, attrTtl: 0 });
@@ -7179,8 +7341,47 @@ var Launcher = class {
7179
7341
  })
7180
7342
  };
7181
7343
  }
7344
+ async fsaDriver(spec) {
7345
+ const { target, source } = spec;
7346
+ const ask = (handle) => this.onMountPending?.({ target, source, ...handle ? { handle } : {} });
7347
+ const { port1, port2 } = new MessageChannel();
7348
+ const medium = removableMedium((handle) => {
7349
+ served.invalidate(true);
7350
+ ask(handle);
7351
+ });
7352
+ const served = serveFilesystem(port2, medium.handlers, FSA_CAPABILITIES);
7353
+ const id = source.slice("fsa:".length);
7354
+ this.removable.set(target, { id, medium, served });
7355
+ const stored = this.inserted.get(id) ?? await this.media.get(id);
7356
+ if (stored && await granted(stored)) medium.insert(stored);
7357
+ else ask(stored);
7358
+ return {
7359
+ port: port1,
7360
+ present: () => medium.present(),
7361
+ dispose: () => {
7362
+ this.removable.delete(target);
7363
+ port1.close();
7364
+ port2.close();
7365
+ }
7366
+ };
7367
+ }
7182
7368
  mount(spec) {
7183
- return this.mounts.mount(spec, this.fs);
7369
+ if (spec.type !== "fsa" || spec.source.startsWith("fsa:"))
7370
+ return this.mounts.mount(spec, this.fs);
7371
+ return this.mounts.mount({ ...spec, source: `fsa:${crypto.randomUUID()}` }, this.fs);
7372
+ }
7373
+ async insert(target, handle, source) {
7374
+ const at = normalizePath(target);
7375
+ const slot = this.removable.get(at);
7376
+ if (!slot) throw fsError("EINVAL", `${at} is not a removable mount`);
7377
+ if (source !== void 0 && source !== `fsa:${slot.id}`) {
7378
+ throw fsError("EINVAL", `${at} is no longer the drive ${source}`);
7379
+ }
7380
+ if (!await granted(handle)) throw fsError("EACCES", `${at}: no permission for this folder`);
7381
+ this.inserted.set(slot.id, handle);
7382
+ await this.media.put(slot.id, handle);
7383
+ slot.medium.insert(handle);
7384
+ slot.served.invalidate(true);
7184
7385
  }
7185
7386
  umount(target) {
7186
7387
  this.mounts.umount(target);
@@ -7555,13 +7756,13 @@ async function holdLock(locks) {
7555
7756
  if (!locks) return { release() {
7556
7757
  } };
7557
7758
  const name2 = `slicc-kernel-client:${crypto.randomUUID()}`;
7558
- const granted = Promise.withResolvers();
7759
+ const granted2 = Promise.withResolvers();
7559
7760
  const held = Promise.withResolvers();
7560
7761
  void locks.request(name2, () => {
7561
- granted.resolve();
7762
+ granted2.resolve();
7562
7763
  return held.promise;
7563
7764
  });
7564
- await granted.promise;
7765
+ await granted2.promise;
7565
7766
  return { name: name2, release: () => held.resolve() };
7566
7767
  }
7567
7768
  function concat6(chunks2) {