@ai-ecoverse/slicc-kernel 1.146.6 → 1.147.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/README.md CHANGED
@@ -239,6 +239,8 @@ A command's name is any single path component without whitespace or control char
239
239
 
240
240
  **Variants by environment** ([homescoop#357](https://github.com/ai-ecoverse/homescoop/issues/357)): one package can carry more than one build of a wasm command and choose between them by the environment. `"variants": [{ "env": { "PYTHON_FORK": "true" }, "wasm": "bin/python-fork.wasm" }]` on a command entry (Emscripten or WASI) is matched in order against the environment the new process gets, at spawn and at exec. Every key must equal its value exactly, and the first match runs its `wasm`; no match runs the command's own `wasm`. This holds however the kernel reaches the entry: by name, by its path (`/usr/bin/python3`, as `sys.executable` gives it to `subprocess` and `multiprocessing`), or through a symlink to that path (a venv's `bin/python`). So `PYTHON_FORK=true python3`, `PYTHON_FORK=true exec /usr/bin/python3` and a child that inherits `PYTHON_FORK=true` all get the variant, and `PYTHON_FORK=1` doesn't. Commands of one package that share a build (`python`, `python3`, `pip`, `pip3`) each carry the same `variants`. A variant replaces only the module: the `abi`, the glue, `env`, `argv0`, `argv0Path` and the `/usr/bin` entry stay the command's. For an Emscripten command that means a variant swaps only the `.wasm` and keeps the command's JS glue, so it must be built against that same glue; a WASI or WASIX command has no glue, so there's no such constraint. Only the chosen module is read, checked and compiled, and compiled modules are cached by its path. A read or `stat` of `/usr/bin/<name>` still shows the default `wasm`. A `variants` that isn't an array, or has an entry without an `env` object of strings and a `wasm` inside the package, is ignored as a whole, with one warning per command, and the command runs its `wasm`. An older kernel ignores the field.
241
241
 
242
+ **A pnpm bin of a kernel command** ([#1453](https://github.com/ai-ecoverse/slicc-kernel/issues/1453)): a `#!/usr/bin/env node` (or `#!/usr/bin/node`) script run by its path, whose package lists the same name in `slicc.commands`, runs that kernel command, even when a `node` command exists. This is the bin shim `pnpm add -g` writes into `$PNPM_HOME/bin`, which names its target in a `// pnpm-wasm-shim={"target":…}` line; a script without that line is run as a script, so a same-named file in a package that differs from its glue still doesn't run as the command (#899). The shim's target, with its links resolved, must lie in a package installed directly under one of the command roots, and the name must be the catalog's command from that same package, so a shim only ever runs a command the caller could run by name, with the same checks; with `integrity` on, the shim itself is verified as a source. So `pnpm add -g <pkg> && <cmd>` works in one line.
243
+
242
244
  A package can also name a command that runs files by their suffix, as `binfmt_misc` does: `"slicc": { "binfmt": { ".jsh": "jsh" } }`. Executing a file that has no `#!` line and is not a wasm module, whose name ends in a mapped suffix, runs that command with the file's path as invoked and the arguments after it, as a `#!` line naming the command would; a `#!` line still wins. The longest matching suffix wins, the whole name is never a suffix (`.jsh` alone is not one), and of two packages that map one suffix, the one whose name sorts first wins, as for commands; the kernel warns once (`console.warn`) when another package maps that suffix to a different command. A suffix starts with a dot and the command must be a name the catalog resolves when the file runs.
243
245
 
244
246
  ### WASI programs
@@ -3221,13 +3221,7 @@ var OpfsFs = class {
3221
3221
  }
3222
3222
  }
3223
3223
  async place(handle, dir, name2, copies) {
3224
- const move = handle.move;
3225
- if (move) this.others++;
3226
- const moved2 = await move?.call(handle, dir, name2).then(
3227
- () => true,
3228
- () => false
3229
- );
3230
- if (moved2) return true;
3224
+ if (await this.moved(handle, dir, name2)) return true;
3231
3225
  const before = this.fileMoves;
3232
3226
  try {
3233
3227
  await this.copy(handle, dir, name2, copies);
@@ -3240,6 +3234,23 @@ var OpfsFs = class {
3240
3234
  return false;
3241
3235
  }
3242
3236
  fileMoves = 0;
3237
+ async moved(handle, dir, name2) {
3238
+ const move = handle.move;
3239
+ if (!move) return false;
3240
+ for (let attempt = 0; ; attempt++) {
3241
+ this.others++;
3242
+ const failed3 = await move.call(handle, dir, name2).then(
3243
+ () => void 0,
3244
+ (err) => ({ err })
3245
+ );
3246
+ if (!failed3) return true;
3247
+ const error = failed3.err?.name;
3248
+ if (handle.kind !== "file" || !LOCKED.has(error)) return false;
3249
+ const delay = SYNC_BACKOFF_MS[attempt];
3250
+ if (delay === void 0) throw fsError("EBUSY", name2);
3251
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
3252
+ }
3253
+ }
3243
3254
  async copy(handle, dir, name2, copies, rest2 = "") {
3244
3255
  if (handle.kind === "file" && movable(handle)) {
3245
3256
  this.others++;
@@ -7774,7 +7785,7 @@ function satisfies(version, range2) {
7774
7785
  }
7775
7786
 
7776
7787
  // src/version.ts
7777
- var KERNEL_VERSION = true ? "1.146.6" : "0.0.0-dev";
7788
+ var KERNEL_VERSION = true ? "1.147.0" : "0.0.0-dev";
7778
7789
  var OS_RELEASE = "6.0.0-slicc";
7779
7790
  function procVersion() {
7780
7791
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -28848,6 +28859,8 @@ function linksOf2(req) {
28848
28859
  return req.links === void 0 ? {} : { links: req.links };
28849
28860
  }
28850
28861
  var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
28862
+ var SHIM_MARKER = /^\/\/ pnpm-wasm-shim=(\{.*\})$/gm;
28863
+ var SHIM_MAX = 64 * 1024;
28851
28864
  var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
28852
28865
  var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
28853
28866
  var WASM_MAGIC = [0, 97, 115, 109];
@@ -29539,7 +29552,10 @@ var Launcher = class {
29539
29552
  return { abi: "emscripten", glue, wasm, argv0: baseName2(argv0 || file), ...asked };
29540
29553
  }
29541
29554
  const owner = await this.glueOwner(glue, baseName2(file), scope);
29542
- if (!owner) return void 0;
29555
+ if (!owner) {
29556
+ const shim = await this.shimOwner(glue, baseName2(file), scope);
29557
+ return shim && { ...targetOf(shim), ...asked, shim: glue };
29558
+ }
29543
29559
  return { ...targetOf(owner), ...asked, ...owner.glue === glue ? {} : { copy: glue } };
29544
29560
  }
29545
29561
  async ownOrRefused(path, name2, scope) {
@@ -29575,6 +29591,43 @@ var Launcher = class {
29575
29591
  }
29576
29592
  return void 0;
29577
29593
  }
29594
+ async shimOwner(script, name2, scope) {
29595
+ const command = (await this.commands(scope)).get(name2);
29596
+ if (!command || command.script || command.origin?.script) return void 0;
29597
+ const target = await this.shimTarget(script);
29598
+ if (target === void 0) return void 0;
29599
+ const pkg = await this.installedPackage(await physicalPathAsync(this.base, target), scope);
29600
+ if (!pkg) return void 0;
29601
+ const glue = command.origin?.glue ?? command.glue;
29602
+ const own = await physicalPathAsync(this.base, glue).catch(() => void 0);
29603
+ return own?.startsWith(`${pkg}/`) ? command : void 0;
29604
+ }
29605
+ async shimTarget(script) {
29606
+ const st = await this.base.stat(script).catch(() => void 0);
29607
+ if (!st?.isFile || st.size > SHIM_MAX) return void 0;
29608
+ const text = await this.base.readFile(script).catch(() => "");
29609
+ const marker = [...text.matchAll(SHIM_MARKER)].at(-1)?.[1];
29610
+ if (marker === void 0) return void 0;
29611
+ let target;
29612
+ try {
29613
+ target = JSON.parse(marker).target;
29614
+ } catch {
29615
+ return void 0;
29616
+ }
29617
+ if (typeof target !== "string" || target === "") return void 0;
29618
+ const dir = await physicalPathAsync(this.base, script.slice(0, script.lastIndexOf("/")) || "/");
29619
+ return this.fs.resolvePath(dir, target);
29620
+ }
29621
+ async installedPackage(real, scope) {
29622
+ for (const root of await this.roots(scope)) {
29623
+ const top = await physicalPathAsync(this.base, root).catch(() => void 0);
29624
+ if (!top || !real.startsWith(`${top}/`)) continue;
29625
+ const [first = "", second] = real.slice(top.length + 1).split("/");
29626
+ const pkg = first.startsWith("@") && second ? `${top}/${first}/${second}` : `${top}/${first}`;
29627
+ if (pkg !== real && await this.base.exists(`${pkg}/package.json`)) return pkg;
29628
+ }
29629
+ return void 0;
29630
+ }
29578
29631
  async glueOwner(glue, name2, scope) {
29579
29632
  const commands = [...(await this.commands(scope)).values()];
29580
29633
  const owners = commands.filter((c) => c.abi === "emscripten" && !c.script && c.glue === glue);
@@ -29670,7 +29723,10 @@ var Launcher = class {
29670
29723
  const found = await this.resolve(file, argv[0] ?? file, cwd, scope);
29671
29724
  const named = typeof argv0 === "string" && found && !found.fixed;
29672
29725
  const direct = named ? { ...found, argv0 } : found;
29673
- if (direct) return { target: direct, args: [...direct.prefix ?? [], ...argv.slice(1)] };
29726
+ if (direct) {
29727
+ const shim = direct.shim ? { sources: [direct.shim], asked: direct.asked ? { [direct.shim]: direct.asked } : {} } : {};
29728
+ return { target: direct, args: [...direct.prefix ?? [], ...argv.slice(1)], ...shim };
29729
+ }
29674
29730
  if (catalogOnly && !await this.scriptCommand(file, cwd, scope)) return void 0;
29675
29731
  const script = await this.interpreted(file, argv, cwd, scope);
29676
29732
  return script && { ...script, script: true };