@ai-ecoverse/slicc-kernel 1.159.6 → 1.160.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/README.md +2 -0
- package/dist/kernel-worker.js +29 -3
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +45 -22
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +29 -3
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +45 -22
- package/dist/process-worker.js.map +2 -2
- package/dist/types/process/js/js-context.d.ts +4 -0
- package/dist/types/process/js/js-kernel.d.ts +3 -0
- package/dist/types/realm/sync-fs-wire.d.ts +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -423,6 +423,7 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
423
423
|
- `sync.runScript(source, { filename })` (#1541, slicc-jsh#977) runs `source` as a classic script in the program's own global scope, synchronously, as Node's `vm.runInThisContext` does: `let`, `const` and `class` land in the global lexical scope, `var` (in strict code too) on the global object, and stack frames are named `filename` (default `[eval]`). On the Node entry it returns `{ value, completion: true }` with the completion value, and an error the script throws is thrown as it is. In a browser, where process workers are module workers with no synchronous classic-script path, it returns `undefined`, so a runtime feature-detects it with `ctx.sync.runScript('1') !== undefined`. A non-string `source` or `filename` is a `TypeError` everywhere. The script sees exactly the globals the program sees, `import()` inside it is refused, and a `var`, function or `let` declaration can't replace a global the program can't assign: a non-configurable one stays as it is, and the only configurable, non-assignable ones (`crypto`, `navigator`) are ones the program can already redefine with `Object.defineProperty`.
|
|
424
424
|
- `sync.promiseState(promise)` (#1531, slicc-jsh#974) tells a native promise's state synchronously, as Node's `util.inspect` does: `{ state: 'pending' | 'fulfilled' | 'rejected', value }`. `value` is the very fulfillment value or rejection reason (`undefined` while pending), and probing doesn't handle a rejection. On the Node entry it reads the promise through an inspector session the process worker opens on first use, one per process and per thread; the probe runs no program code. Anything that isn't a native promise (a thenable, a proxy) is a `TypeError`. It answers `undefined` in a browser, where a worker has no inspector, on a Node built without one, once a program has changed `Object.prototype` or put a `toJSON` on a built-in prototype, and when the global object can't take a new property (`Object.preventExtensions(globalThis)`, or a frozen global).
|
|
425
425
|
- **Kernel frames in a program's stacks** (#1583): on the Node entry, the process worker's bundle names itself `slicc-kernel:/node-process-worker.js` (a `sourceURL`), so the kernel's own frames in an error a program sees (its own errors, a failed `ctx` call, `runScript`) show that name and not the host directory the kernel is installed in. The same holds in `CallSite.toString()` and `getScriptNameOrSourceURL()`, which a program's own `Error.prepareStackTrace` gets; `getFileName()` still returns the bundle's file URL. Line and column numbers are unchanged, so they still point into `dist/node-process-worker.js`. Don't run an embedder with `--enable-source-maps` if program-visible paths matter: Node then maps the kernel's frames back to the absolute paths of its sources. In a browser the frames keep the bundle's URL, because Chrome ignores `sourceURL` in a module worker loaded by URL.
|
|
426
|
+
- `module` (#1598, slicc-jsh#566) is a frozen `{ path }` with the command module's own path in the kernel's file system (e.g. `/usr/lib/node_modules/jsh/dist/jsh.mjs`), so a program can read its package's other files through `ctx.fs` on first use. It's the path the kernel found the package under, with symlinks left as they are (a pnpm-style `node_modules/<name>` link stays in it), and reads the same files through `ctx.fs`. It's a kernel path, never a host one, and a thread gets the same. It's absent for the kernel's built-in programs (`sudo`, `crontab`) and for a program run from inline source with no file.
|
|
426
427
|
- `memory()`, synchronous and without a kernel call, measures the calling isolate: each `thread` answers for its own (#1240, slicc-jsh#397). It returns a fresh plain object of numbers in bytes, each field only where it's measured: `heapUsed`, `heapTotal`, `heapLimit` (V8's heap size limit), `external`, `arrayBuffers` and `rss`. On the Node entry they come from V8's heap statistics and Node's memory usage, through functions the worker captured before its lockdown, so nothing reachable from the program leads back to `process` or `v8`. `rss` is the host process's resident set size, as a Node worker thread reports it in `process.memoryUsage().rss`: it isn't per process, but shared by every process and thread of the kernel, and by any other kernel in that host process (#1563, slicc-jsh#539). In a browser `memory()` answers `{}` for now, with no `rss`.
|
|
427
428
|
- **`thread`, kernel threads of the same process** ([#1094](https://github.com/ai-ecoverse/slicc-kernel/issues/1094)), the base for Node's `worker_threads` (tinypool, piscina, vitest's and jest's thread pools, eslint's concurrency). `thread.spawn({ data, transfer })` resolves with `{ tid, port, exited, terminate() }`.
|
|
428
429
|
- **What a thread is:** a new process worker in the **same process**. It has the same pid, descriptor table, cwd, umask, credentials and lockdown, and a sync lane of its own, so `sync` calls in one thread don't hold another.
|
|
@@ -1155,6 +1156,7 @@ printf '{"kind":"open.url","url":"%s"}\n' "$url" | cat > /dev/embedder
|
|
|
1155
1156
|
|
|
1156
1157
|
- **The request:** the first line is a JSON object with a `kind` (`a.b-c`: lower-case words with dots, at most 64 bytes) and options (every other key), at most 64 KiB. Everything written after it is the body, streamed to the page as it is written: a write waits while 1 MiB of it is unread by the page. A bad header fails that `write()` with `EINVAL`, and the description stays failed. A page that answers with bytes, a string or nothing, or cancels the body, before it has read all of it gets no more: the rest the program writes is dropped ([#1536](https://github.com/ai-ecoverse/slicc-kernel/issues/1536)), so a writer never waits on a body nobody reads. A page that answers with a stream keeps the body until that stream ends, so it must read or cancel the body: until it does, a writer past the 1 MiB window waits. The request goes to the page when its header line ends.
|
|
1157
1158
|
- **The reply:** reads return the page's reply in order, pulled as the program reads, and its end is end-of-file. Opened `O_RDWR`, the first `read()` ends the body. Opened `O_WRONLY` (nobody reads), the last close ends the body and waits for the page to finish, so `close()` returns its error (an Emscripten program's standard output follows `/dev/clipboard`'s exit rule, so `printf … | cat > /dev/embedder` fails with `cat: write error: …`). There is no timeout: a reply may wait for a prompt or a model download; run `timeout` for one.
|
|
1159
|
+
- **Who waits:** the process that does the last close waits for the reply, and only a signal to that process interrupts it. So put the open inside the command that `timeout` bounds: `timeout 12 sh -c 'cat request > /dev/embedder'`. In `timeout 12 cat request > /dev/embedder` the redirection is `timeout`'s own. `cat` writes the header and exits at once, so the timer never fires, and then `timeout` waits in its own last close, on its way out (coreutils closes its standard output), until the page answers.
|
|
1158
1160
|
- **Cancel:** a signal during a blocked read, write or `O_WRONLY` close fails it with `EINTR`, and the page's `signal` aborts with reason `interrupted`. Closing the description before the end of the reply aborts it with reason `closed`, and that is no error: it is how `hear` stops recording. A process that ends during a request aborts it too.
|
|
1159
1161
|
- **Errors:** the hook's error gives the errno: `NotAllowedError` or `AbortError` (a dismissed prompt, a refused permission) `EACCES`; `NotSupportedError` `ENOTSUP`; `NotFoundError` `ENOENT`; anything else `EIO`. An Emscripten or WASI program first gets `<argv0>: /dev/embedder: <first line of the message>` on standard error, as for `/dev/clipboard`; a JS program has it in the error. `request.progress(line)` from the page writes `<argv0>: <line>` to standard error the same way, at most 4 lines a second.
|
|
1160
1162
|
- **Who answers:** the hook of the client the process's session was started through, inherited by its children, forks and `exec`s: `createKernel({ requests })` (and `createNodeKernel`) for the page's own `run` and `openTerminal`, the hook a port was made with (`connect({ requests })`), or the hook its attached client brought (`connect({ requests: 'attach' })`, then `attachKernel(port, { requests })`). Nothing falls back to another client's hook, so a request from another tab's terminal never opens in this one. With no hook, or a kind the hook doesn't list, the request is `ENOTSUP` before the page sees it. A program has no way to register, replace or see a hook.
|
package/dist/kernel-worker.js
CHANGED
|
@@ -8236,7 +8236,7 @@ function satisfies(version, range2) {
|
|
|
8236
8236
|
}
|
|
8237
8237
|
|
|
8238
8238
|
// src/version.ts
|
|
8239
|
-
var KERNEL_VERSION = true ? "1.
|
|
8239
|
+
var KERNEL_VERSION = true ? "1.160.1" : "0.0.0-dev";
|
|
8240
8240
|
var OS_RELEASE = "6.0.0-slicc";
|
|
8241
8241
|
function procVersion() {
|
|
8242
8242
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -14932,7 +14932,8 @@ async function dispatchSyncFs(req) {
|
|
|
14932
14932
|
const entry = resolveSyncFsToken(req.token);
|
|
14933
14933
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
14934
14934
|
if (req.identity && req.op === "create") return created2(entry, req);
|
|
14935
|
-
if (!req.identity || !IDENTITY_OPS.has(req.op))
|
|
14935
|
+
if (!req.identity || !IDENTITY_OPS.has(req.op))
|
|
14936
|
+
return devsOf(entry, req, await dispatchOne(entry, req));
|
|
14936
14937
|
const { fs, cwd } = entry;
|
|
14937
14938
|
const path = fs.resolvePath(cwd, req.path);
|
|
14938
14939
|
const identified = async (held) => {
|
|
@@ -14950,6 +14951,30 @@ async function dispatchSyncFs(req) {
|
|
|
14950
14951
|
return toErrno(err);
|
|
14951
14952
|
}
|
|
14952
14953
|
}
|
|
14954
|
+
var DEV_PROBES = /* @__PURE__ */ new Set(["stat", "lstat", "read", "exists"]);
|
|
14955
|
+
async function devsOf(entry, req, result) {
|
|
14956
|
+
if (req.devs !== true || !DEV_PROBES.has(req.op)) return result;
|
|
14957
|
+
const absent2 = result.ok ? result.kind === "json" && result.json === false : result.errno === "ENOENT";
|
|
14958
|
+
if (!absent2) return result;
|
|
14959
|
+
const dev = await devTarget(entry, req, entry.fs.resolvePath(entry.cwd, req.path));
|
|
14960
|
+
if (dev === void 0) return result;
|
|
14961
|
+
return result.ok ? json2({ dev }) : { ...result, dev };
|
|
14962
|
+
}
|
|
14963
|
+
async function devTarget(entry, req, path) {
|
|
14964
|
+
const look = async (op, at) => {
|
|
14965
|
+
const r = await dispatchPath(entry, { token: req.token, op, path: at });
|
|
14966
|
+
if (!r.ok) throw new Error(r.errno);
|
|
14967
|
+
return r.json;
|
|
14968
|
+
};
|
|
14969
|
+
const resolved2 = await physicalPathAsync(
|
|
14970
|
+
{
|
|
14971
|
+
lstat: async (p) => await look("lstat", p),
|
|
14972
|
+
readlink: async (p) => String(await look("readlink", p))
|
|
14973
|
+
},
|
|
14974
|
+
path
|
|
14975
|
+
);
|
|
14976
|
+
return resolved2 !== path && (resolved2 === "/dev" || resolved2.startsWith("/dev/")) ? resolved2 : void 0;
|
|
14977
|
+
}
|
|
14953
14978
|
async function created2(entry, req) {
|
|
14954
14979
|
const result = await dispatchOne(entry, req);
|
|
14955
14980
|
if (!result.ok) return result;
|
|
@@ -15136,7 +15161,8 @@ function encodeSabResult(result) {
|
|
|
15136
15161
|
JSON.stringify({
|
|
15137
15162
|
errno: result.errno,
|
|
15138
15163
|
message: result.message,
|
|
15139
|
-
...result.direct ? { direct: true } : {}
|
|
15164
|
+
...result.direct ? { direct: true } : {},
|
|
15165
|
+
...result.dev !== void 0 ? { dev: result.dev } : {}
|
|
15140
15166
|
})
|
|
15141
15167
|
)
|
|
15142
15168
|
};
|