@ai-ecoverse/slicc-kernel 1.13.0 → 1.15.0

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;
@@ -1168,9 +1169,28 @@ async function packages(fs, modules) {
1168
1169
  }
1169
1170
  return dirs;
1170
1171
  }
1172
+ async function pnpmGlobalRoots(fs, home) {
1173
+ const roots = [];
1174
+ const global = `${home}/global`;
1175
+ for (const version of (await fs.readdir(global).catch(() => [])).sort()) {
1176
+ for (const group of (await fs.readdir(`${global}/${version}`).catch(() => [])).sort()) {
1177
+ const dir = `${global}/${version}/${group}`;
1178
+ const st = await fs.lstat(dir).catch(() => void 0);
1179
+ if (st?.isDirectory && await fs.exists(`${dir}/node_modules`))
1180
+ roots.push(`${dir}/node_modules`);
1181
+ }
1182
+ }
1183
+ return roots;
1184
+ }
1185
+ async function packagesIn(fs, roots) {
1186
+ const dirs = [];
1187
+ for (const root of typeof roots === "string" ? [roots] : roots)
1188
+ dirs.push(...await packages(fs, root));
1189
+ return dirs;
1190
+ }
1171
1191
  async function scanCommands(fs, modules) {
1172
1192
  const found = /* @__PURE__ */ new Map();
1173
- for (const pkg of await packages(fs, modules)) {
1193
+ for (const pkg of await packagesIn(fs, modules)) {
1174
1194
  let manifest;
1175
1195
  try {
1176
1196
  manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
@@ -1189,7 +1209,7 @@ async function scanCommands(fs, modules) {
1189
1209
  }
1190
1210
  async function scanFilesystems(fs, modules) {
1191
1211
  const found = /* @__PURE__ */ new Map();
1192
- for (const pkg of await packages(fs, modules)) {
1212
+ for (const pkg of await packagesIn(fs, modules)) {
1193
1213
  let manifest;
1194
1214
  try {
1195
1215
  manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
@@ -5891,6 +5911,157 @@ function serveFilesystem(port, handlers, capabilities = {}) {
5891
5911
  return { invalidate: (paths) => reply({ invalidate: paths }) };
5892
5912
  }
5893
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: 16749 };
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
+
5894
6065
  // src/mount/connection.ts
5895
6066
  var DRIVER_TIMEOUT_MS = 3e4;
5896
6067
  function unref(timer) {
@@ -6034,14 +6205,16 @@ var MountTable = class {
6034
6205
  this.deps = deps;
6035
6206
  }
6036
6207
  list() {
6037
- return [...this.mounts.values()].map(({ type, source, target, options, state, error }) => ({
6038
- type,
6039
- source,
6040
- target,
6041
- options: { ...options },
6042
- state,
6043
- ...error ? { error } : {}
6044
- }));
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
+ );
6045
6218
  }
6046
6219
  locate(path) {
6047
6220
  let best;
@@ -6112,7 +6285,8 @@ var MountTable = class {
6112
6285
  maxFile: options.maxfile !== void 0 ? parseSize(options.maxfile) : caps.maxFile ?? 0,
6113
6286
  attrs: /* @__PURE__ */ new Map(),
6114
6287
  lists: /* @__PURE__ */ new Map(),
6115
- dispose: () => opened2.dispose()
6288
+ dispose: () => opened2.dispose(),
6289
+ ...opened2.present ? { present: opened2.present } : {}
6116
6290
  };
6117
6291
  mounted = mount;
6118
6292
  this.mounts.set(target, mount);
@@ -6749,6 +6923,7 @@ function foreignImports(bytes) {
6749
6923
  }
6750
6924
 
6751
6925
  // src/launcher.ts
6926
+ var PNPM_HOME = "/home/.local/share/pnpm";
6752
6927
  var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
6753
6928
  var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
6754
6929
  var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
@@ -6838,8 +7013,13 @@ var Launcher = class {
6838
7013
  ca;
6839
7014
  transport;
6840
7015
  watchers = new FsWatchers();
7016
+ pnpmHome;
6841
7017
  mounts;
6842
7018
  createDriverWorker;
7019
+ media;
7020
+ onMountPending;
7021
+ removable = /* @__PURE__ */ new Map();
7022
+ inserted = /* @__PURE__ */ new Map();
6843
7023
  openFiles = /* @__PURE__ */ new Set();
6844
7024
  held = /* @__PURE__ */ new Set();
6845
7025
  nextPid = 1e3;
@@ -6854,6 +7034,9 @@ var Launcher = class {
6854
7034
  this.base = this.watchers.wrap(this.mounts.wrap(options.fs));
6855
7035
  this.createWorker = options.createWorker;
6856
7036
  this.createDriverWorker = options.createDriverWorker;
7037
+ this.media = options.media ?? memoryMedia();
7038
+ this.onMountPending = options.onMountPending;
7039
+ this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
6857
7040
  this.modulesDir = options.modules ?? "/node_modules";
6858
7041
  this.env = { ...networkEnv(), ...options.env };
6859
7042
  this.ca = kernelCa(options.caStore ?? memoryCaStore());
@@ -6864,11 +7047,17 @@ var Launcher = class {
6864
7047
  ca: this.ca
6865
7048
  });
6866
7049
  this.fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
7050
+ this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
7051
+ this.catalog = void 0;
7052
+ });
6867
7053
  }
6868
7054
  commands() {
6869
- this.catalog ??= scanCommands(this.base, this.modulesDir);
7055
+ this.catalog ??= this.roots().then((roots) => scanCommands(this.base, roots));
6870
7056
  return this.catalog;
6871
7057
  }
7058
+ async roots() {
7059
+ return [this.modulesDir, ...await pnpmGlobalRoots(this.base, this.pnpmHome)];
7060
+ }
6872
7061
  async resolve(file, argv0, cwd) {
6873
7062
  const name2 = COMMAND2.exec(file)?.[1] ?? (file.includes("/") ? void 0 : file);
6874
7063
  if (name2 !== void 0) {
@@ -7110,7 +7299,8 @@ var Launcher = class {
7110
7299
  if (sig !== 0) handle.signal(sig);
7111
7300
  return true;
7112
7301
  }
7113
- async driver(type, _spec) {
7302
+ async driver(type, spec) {
7303
+ if (type === "fsa") return this.fsaDriver(spec);
7114
7304
  if (type === "tmpfs") {
7115
7305
  const { port1, port2 } = new MessageChannel();
7116
7306
  serveFilesystem(port2, tmpfs(), { symlinks: true, chmod: true, attrTtl: 0 });
@@ -7122,7 +7312,7 @@ var Launcher = class {
7122
7312
  }
7123
7313
  };
7124
7314
  }
7125
- const module = (await scanFilesystems(this.base, this.modulesDir)).get(type);
7315
+ const module = (await scanFilesystems(this.base, await this.roots())).get(type);
7126
7316
  if (!module) throw fsError("ENODEV", `unknown file system type ${type}`);
7127
7317
  if (!this.createDriverWorker)
7128
7318
  throw fsError("ENODEV", `this kernel cannot start ${type} drivers`);
@@ -7151,8 +7341,47 @@ var Launcher = class {
7151
7341
  })
7152
7342
  };
7153
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
+ }
7154
7368
  mount(spec) {
7155
- 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);
7156
7385
  }
7157
7386
  umount(target) {
7158
7387
  this.mounts.umount(target);
@@ -7162,7 +7391,14 @@ var Launcher = class {
7162
7391
  await writeCaFile(this.base, this.ca).catch(() => void 0);
7163
7392
  }
7164
7393
  environment(cwd, extra) {
7165
- return { PATH: "/usr/bin:/bin", HOME: "/home", ...this.env, PWD: cwd, ...extra };
7394
+ return {
7395
+ PATH: `/usr/bin:/bin:${this.pnpmHome}/bin`,
7396
+ HOME: "/home",
7397
+ PNPM_HOME: this.pnpmHome,
7398
+ ...this.env,
7399
+ PWD: cwd,
7400
+ ...extra
7401
+ };
7166
7402
  }
7167
7403
  async starting(argv, dir) {
7168
7404
  this.catalog = void 0;
@@ -7520,13 +7756,13 @@ async function holdLock(locks) {
7520
7756
  if (!locks) return { release() {
7521
7757
  } };
7522
7758
  const name2 = `slicc-kernel-client:${crypto.randomUUID()}`;
7523
- const granted = Promise.withResolvers();
7759
+ const granted2 = Promise.withResolvers();
7524
7760
  const held = Promise.withResolvers();
7525
7761
  void locks.request(name2, () => {
7526
- granted.resolve();
7762
+ granted2.resolve();
7527
7763
  return held.promise;
7528
7764
  });
7529
- await granted.promise;
7765
+ await granted2.promise;
7530
7766
  return { name: name2, release: () => held.resolve() };
7531
7767
  }
7532
7768
  function concat6(chunks2) {