@ai-ecoverse/slicc-kernel 1.146.7 → 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
@@ -7785,7 +7785,7 @@ function satisfies(version, range2) {
7785
7785
  }
7786
7786
 
7787
7787
  // src/version.ts
7788
- var KERNEL_VERSION = true ? "1.146.7" : "0.0.0-dev";
7788
+ var KERNEL_VERSION = true ? "1.147.0" : "0.0.0-dev";
7789
7789
  var OS_RELEASE = "6.0.0-slicc";
7790
7790
  function procVersion() {
7791
7791
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -28859,6 +28859,8 @@ function linksOf2(req) {
28859
28859
  return req.links === void 0 ? {} : { links: req.links };
28860
28860
  }
28861
28861
  var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
28862
+ var SHIM_MARKER = /^\/\/ pnpm-wasm-shim=(\{.*\})$/gm;
28863
+ var SHIM_MAX = 64 * 1024;
28862
28864
  var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
28863
28865
  var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
28864
28866
  var WASM_MAGIC = [0, 97, 115, 109];
@@ -29550,7 +29552,10 @@ var Launcher = class {
29550
29552
  return { abi: "emscripten", glue, wasm, argv0: baseName2(argv0 || file), ...asked };
29551
29553
  }
29552
29554
  const owner = await this.glueOwner(glue, baseName2(file), scope);
29553
- 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
+ }
29554
29559
  return { ...targetOf(owner), ...asked, ...owner.glue === glue ? {} : { copy: glue } };
29555
29560
  }
29556
29561
  async ownOrRefused(path, name2, scope) {
@@ -29586,6 +29591,43 @@ var Launcher = class {
29586
29591
  }
29587
29592
  return void 0;
29588
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
+ }
29589
29631
  async glueOwner(glue, name2, scope) {
29590
29632
  const commands = [...(await this.commands(scope)).values()];
29591
29633
  const owners = commands.filter((c) => c.abi === "emscripten" && !c.script && c.glue === glue);
@@ -29681,7 +29723,10 @@ var Launcher = class {
29681
29723
  const found = await this.resolve(file, argv[0] ?? file, cwd, scope);
29682
29724
  const named = typeof argv0 === "string" && found && !found.fixed;
29683
29725
  const direct = named ? { ...found, argv0 } : found;
29684
- 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
+ }
29685
29730
  if (catalogOnly && !await this.scriptCommand(file, cwd, scope)) return void 0;
29686
29731
  const script = await this.interpreted(file, argv, cwd, scope);
29687
29732
  return script && { ...script, script: true };