@ai-ecoverse/slicc-kernel 1.95.0 → 1.95.2
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 +3 -1
- package/dist/kernel-worker.js +12 -7
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +3 -3
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +12 -7
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +3 -3
- package/dist/process-worker.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -335,7 +335,7 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
335
335
|
- `sudo(args)`, what the [`sudo`](#sudo) command calls: `args` are its arguments after `sudo`, and it resolves with `{ pid }`, the command the kernel started as the program's child, to `wait` for; `{ run }`, an argv to run as the caller (with `users: 'off'`); `{ list }`, the text of `sudo -l`; or `{ error, errno }`, the message to print;
|
|
336
336
|
- `net`: `listen({ port, host, backlog })` and `connect({ host, port, signal })` (`host` defaults to `127.0.0.1`) on the kernel's loopback, the same sockets WASIX programs use, so a JS server is reached by other processes, by the page as `<port>.kernel.localhost` (`loopbackFetch`, `dial`) and by attached clients. A listener has its `port` (as bound, so port `0` picks a free one), `accept({ signal })`, `close()` and `for await` over its connections. A connection has `local` and `remote` addresses, `readable` and `writable` web streams (closing the writable shuts down the sending side, so the peer reads the end), and `close()`. The loopback is IPv4 (`127.0.0.1` by default, or `0.0.0.0`), so an IPv6 host such as `::1` fails with `EADDRNOTAVAIL` for both `listen` and `connect`, and binding and connecting fail as they would for a WASIX program (`EADDRINUSE`, `ECONNREFUSED`, `EADDRNOTAVAIL`). A blocked accept, read or write waits without holding anything in the kernel, so aborting it or closing the socket leaves nothing behind. A write to a connection the peer closed fails with `EPIPE` and no `SIGPIPE`;
|
|
337
337
|
- `websocket(url, { protocols, headers, signal })`, a WebSocket over the kernel's transport (its `websocket`, so `fetchTransport({ webSocket: true })` in a browser), shaped like `WebSocketStream`. It resolves once the socket is open, with `url`, the `protocol` the far end chose (always one the program offered: a transport that hands back a socket with another fails the open with `EPROTO`, while browsers and Node's `WebSocket`, which `fetchTransport()` uses, fail such a handshake themselves without saying why, so the open fails with `ECONNREFUSED`), a `readable` of messages (`string` for text, `Uint8Array` for binary), a `writable` for sending them, `closed` (the `{ code, reason }` of the close, from either side, whether or not the program reads) and `close({ code, reason })`. An `http(s):` URL is taken as `ws(s):`. Sending waits while more than 1 MiB is buffered. A signal aborts the open, and after it the socket. A transport without WebSockets rejects with a `TypeError` whose `cause.code` is `ENOTSUP`. One that cannot connect rejects with the `code` its error carries (an errno, such as `EAI_NONAME`, `ECONNREFUSED`, `ETIMEDOUT` or `EPROTO`), else `ECONNREFUSED`. `ws://host.slicc.internal:port` reaches the real machine's loopback, as it does through the proxy;
|
|
338
|
-
- `fetch(input, init)`, the web's `fetch` over the kernel's transport, the one the proxy uses, so the embedder's transport, its WebSocket and proxy settings and the kernel's routes apply. The response body streams as the program reads it. The request body is read whole first, since a transport takes it whole (streaming it is [#238](https://github.com/ai-ecoverse/slicc-kernel/issues/238)), and one larger than the transport's `maxRequestBody` is answered `413`, as through the proxy. A signal aborts the request at any stage. Redirects follow the fetch rules: at most 20, a 301 or 302 after a `POST` and a 303 turn into a `GET` without a body, `Authorization` is dropped across origins, and `redirect: 'manual'` returns the redirect itself. On a transport that follows redirects on its own (`fetchTransport()` in a browser) only `'follow'` works: `'manual'` and `'error'` reject with a `TypeError` whose `cause.code` is `ENOTSUP`, since such a transport never shows the program a redirect. A status the transport chose comes back as a response whose body is the reason. An unreachable host, or no transport at all, rejects with a `TypeError` whose `cause.code` is `ECONNREFUSED` or `ENETUNREACH`, as Node's `fetch` does. A `data:` URL is answered by `fetch` itself, as the fetch standard decodes it, without a transport: for any method, a `HEAD` without a body, and its type parsed and serialized as a MIME type (lowercase, no spaces, invalid parameters dropped), as Node's `fetch` answers; any other scheme than `http:` and `https:` rejects with a `TypeError` whose `cause.code` is `EPROTONOSUPPORT`, at the start as for a redirect target. The global `fetch` stays withheld;
|
|
338
|
+
- `fetch(input, init)`, the web's `fetch` over the kernel's transport, the one the proxy uses, so the embedder's transport, its WebSocket and proxy settings and the kernel's routes apply. The response body streams as the program reads it. The request body is read whole first, since a transport takes it whole (streaming it is [#238](https://github.com/ai-ecoverse/slicc-kernel/issues/238)), and one larger than the transport's `maxRequestBody` is answered `413`, as through the proxy. A signal aborts the request at any stage. Redirects follow the fetch rules: at most 20, a 301 or 302 after a `POST` and a 303 turn into a `GET` without a body, `Authorization` is dropped across origins, and `redirect: 'manual'` returns the redirect itself. On a transport that follows redirects on its own (`fetchTransport()` in a browser) only `'follow'` works: `'manual'` and `'error'` reject with a `TypeError` whose `cause.code` is `ENOTSUP`, since such a transport never shows the program a redirect. A status the transport chose comes back as a response whose body is the reason. An unreachable host, or no transport at all, rejects with a `TypeError` whose `cause.code` is `ECONNREFUSED` or `ENETUNREACH`, as Node's `fetch` does. A `data:` URL is answered by `fetch` itself, as the fetch standard decodes it, without a transport: for any method, a `HEAD` without a body, and its type parsed and serialized as a MIME type (lowercase, no spaces, invalid parameters dropped), as Node's `fetch` answers; any other scheme than `http:` and `https:` rejects with a `TypeError` whose `cause.code` is `EPROTONOSUPPORT`, at the start as for a redirect target. A URL on one of the fetch standard's bad ports (25, 6000 and the rest) rejects too, as Node's `fetch` does: a `TypeError` `fetch failed` whose `cause` is `bad port` with `code` `EACCES`, at the start and as a redirect target. The kernel's own loopback is exempt. The global `fetch` stays withheld;
|
|
339
339
|
- **`fetch` and `websocket` to the kernel's own loopback:** a URL whose host is `127.0.0.0/8`, `0.0.0.0`, `[::1]`, `localhost` or `*.localhost` goes to the kernel's own loopback, as a guest's sockets do (`<port>.kernel.localhost` takes its port from the name). So it reaches whatever listens on that port in the kernel, the CDP facade at `SLICC_CDP_URL` and its `/json` included. It never goes to the transport: with nothing listening, it rejects with a `TypeError` whose `cause.code` is `ECONNREFUSED`, with or without a transport. The kernel speaks HTTP/1.1 and WebSocket (RFC 6455) to the listener itself, and `fetch` handles the redirects and compressed bodies it answers, as for any server. `https:` and `wss:` there reject with `ENOTSUP`. The listener sees a connection from `127.0.0.1` whose `SO_PEERCRED` is the program's pid, uid and gid, as if the program had connected itself. `host.slicc.internal` still goes to the transport as the real machine's loopback, for `fetch` too, under the transport's policy (`nodeTransport({ hostLoopback })`). A redirect from any other URL into the kernel's loopback rejects with `cause.code` `EACCES`, so a server elsewhere can't steer a program's request to the kernel's own servers; `redirect: 'manual'` still returns it;
|
|
340
340
|
- `tty`, for programs on a terminal: `getAttr(fd)` and `setAttr(fd, termios)` read and set its termios (`{ c_iflag, c_oflag, c_cflag, c_lflag, c_cc }`, Linux values), `setRaw(fd, raw)` switches raw mode on as libuv (and Node's `setRawMode`) does (no echo, no line editing, no `^C` signal, each key read as it comes) and off again to the termios it had before (per terminal: after raw mode on fds 0 and 1 of one terminal, turning it off on either restores the termios from before the first), `size(fd)` answers `{ columns, rows }`, and `foreground(fd)` and `setForeground(fd, pgrp)` get and set the foreground process group, under the same job-control rules as other programs. A window resize arrives as `SIGWINCH` through `signals`. A terminal the program left raw is set back when the program ends, whether it returns, exits or throws (a program killed by a signal cannot do so). On a descriptor that is not a terminal, each call fails with `ENOTTY`;
|
|
341
341
|
- `exit(status)`, which ends the program once its pending writes are done;
|
|
@@ -826,6 +826,8 @@ The Biome, TypeScript, lefthook, Renovate and CI configuration comes from [slicc
|
|
|
826
826
|
|
|
827
827
|
`test/integration/fixtures/dlfork/` is a WASIX PIE program (`dlfork.c`) that `dlopen`s a side module (`side.c`, built as `lib/libdlside.so`), forks, and has the child read the module's data and call through function pointers taken before the fork, for the fork-after-`dlopen` test. It is built with wasix-sysroot 2025.9.30-28's legacy-EH PIC variant and homescoop's pinned wasixcc, then Asyncify for `proc_fork`: `INSTALL_WASIXCC=<homescoop>/scripts/install-wasixcc.sh WASIX_SYSROOT_28=<unpacked -28 package> test/integration/fixtures/dlfork/build.sh`. `bin/dlfork.wasm` is about 800 KB, far bigger than the other fixtures: wasixcc links a position-independent main with `--whole-archive --export-all`, so side modules can find any libc symbol, and neither `--gc-sections` nor a narrower export list (`--no-export-dynamic --export=…`) takes that out.
|
|
828
828
|
|
|
829
|
+
`test/integration/fixtures/linktest/` is a WASIX program (`linktest.c`) that calls `linkat(AT_FDCWD, from, AT_FDCWD, to, AT_SYMLINK_FOLLOW)` and prints the new name's type, whether it shares the target's inode, and its link count, for `link-follow.test.mjs`. Coreutils' `ln -L` (Emscripten) takes another path, so the WASI program covers `path_link` with the follow flag. Build it with homescoop's pinned wasixcc and wasix-sysroot 2025.9.30-28: `INSTALL_WASIXCC=<homescoop>/scripts/install-wasixcc.sh WASIX_SYSROOT_28=<unpacked -28 package> test/integration/fixtures/linktest/build.sh`.
|
|
830
|
+
|
|
829
831
|
`tools/big-file.mjs [bytes]` is a manual check, not part of any suite. It writes a file of that size (64 MiB by default) through a tmpfs mount with the unit-test launcher, reads it back, and prints the peak RSS and its ratio to the file size. A 256 MiB file peaks at about 1.8 times its size. Before ranged I/O (#127), a 1.1 GiB file peaked at 18 GB of RSS. **Do not run it past 1 GiB on a workstation.** It refuses anything over 256 MiB unless `SLICC_BIG_FILE_I_HAVE_THE_RAM=1` is set. Measure a small file and extrapolate instead. The unit suite checks a 32 MiB file the same way, and allows a fixed 192 MiB for garbage V8 has yet to collect, which dominates at that size.
|
|
830
832
|
|
|
831
833
|
Releases are cut by semantic-release on every push to `main`, in two jobs, and only on `main`. The first job runs on the bench pool, so it starts within a minute of the push, before `main` has moved on. It computes the version, tags the commit and creates the GitHub release, with `npmPublish: false`. A run that starts after `main` has moved skips; the run for the newer commit releases it. The second job runs on a GitHub-hosted runner, because npm's trusted publishing and provenance accept no self-hosted runner. It publishes the tagged commit with `tools/release-recover.mjs`. However long it waits for a runner, it publishes exactly the commit the run started on, so the provenance, the tag and the package's `gitHead` agree. For any version tagged at HEAD, `tools/release-recover.mjs` pushes the tag if origin lacks it, publishes the version if npm lacks it (with retries), and creates its GitHub release if that is missing too. So a failure after the version is tagged (a rejected push, a failed `npm publish`) doesn't strand it, and re-running the Release workflow completes it the same way.
|
package/dist/kernel-worker.js
CHANGED
|
@@ -8467,7 +8467,7 @@ var FreshModules = class {
|
|
|
8467
8467
|
},
|
|
8468
8468
|
link: async (from, to, follow2) => {
|
|
8469
8469
|
await fs.link(from, to, follow2);
|
|
8470
|
-
const source = follow2 && fs.realpath ? await fs.realpath(from, true) : from;
|
|
8470
|
+
const source = follow2 && fs.realpath ? await fs.realpath(from, true).catch(() => from) : from;
|
|
8471
8471
|
const fresh2 = this.fresh.get(normalizePath(source));
|
|
8472
8472
|
if (fresh2) this.fresh.set(normalizePath(to), fresh2);
|
|
8473
8473
|
}
|
|
@@ -10415,11 +10415,16 @@ var MountFs = class _MountFs {
|
|
|
10415
10415
|
}
|
|
10416
10416
|
async realpath(path, follow2) {
|
|
10417
10417
|
const { realpath: realpath2 } = this.base;
|
|
10418
|
-
if (this.at(path)
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10418
|
+
if (!this.at(path) && realpath2) {
|
|
10419
|
+
const real2 = await realpath2.call(this.base, path, follow2).catch((err) => {
|
|
10420
|
+
if (redirectOf(err)) return void 0;
|
|
10421
|
+
throw err;
|
|
10422
|
+
});
|
|
10423
|
+
if (real2 !== void 0 && !this.at(real2)) return real2;
|
|
10424
|
+
}
|
|
10425
|
+
const real = follow2 ? await realTarget(this, normalizePath(path)) : await this.named(path);
|
|
10426
|
+
if (this.rejectLinks && real !== normalizePath(path)) throw errnoError("ELOOP", path);
|
|
10427
|
+
await this.lstat(real);
|
|
10423
10428
|
return real;
|
|
10424
10429
|
}
|
|
10425
10430
|
async hasOwner(path) {
|
|
@@ -11723,7 +11728,7 @@ function mountTable(mounts = []) {
|
|
|
11723
11728
|
}
|
|
11724
11729
|
|
|
11725
11730
|
// src/version.ts
|
|
11726
|
-
var KERNEL_VERSION = true ? "1.95.
|
|
11731
|
+
var KERNEL_VERSION = true ? "1.95.2" : "0.0.0-dev";
|
|
11727
11732
|
var OS_RELEASE = "6.0.0-slicc";
|
|
11728
11733
|
function procVersion() {
|
|
11729
11734
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|