@ai-ecoverse/slicc-kernel 1.145.1 → 1.146.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
@@ -448,7 +448,7 @@ The module's default export (or its `main` export) is called with a context and
448
448
  - `wait(pid, { untraced })` resolves with `{ status }` once a child ends, or `{ status: 128 + signal, signal }` when a signal ended it, without blocking the program; with `untraced: true` it also resolves when the child stops, with `{ status: 128 + signal, signal, stopped: true }`, as `waitpid` with `WUNTRACED` (a child's `wait({ untraced })` is the same), and options that aren't an object are `EINVAL`; `kill(pid, signal)` signals any process, by number or as `'SIGTERM'` (the default);
449
449
  - `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;
450
450
  - `crontab({ action, user?, text? })`, what the [`crontab`](#cron) command calls: `action` is `'list'`, `'remove'` or `'install'` (with the table as `text`), on the caller's own table or, for an effective uid of 0, on `user`'s. It resolves with `{ text }` (the table for `list`, `''` otherwise) or `{ error }`, the message to print. A malformed call is `EINVAL`;
451
- - `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`;
451
+ - `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`). `sync.net.listen` takes the same options and gives the same listener, bound and listening when it returns, so its `port` is known at once, as Node's `listen()` needs ([#1398](https://github.com/ai-ecoverse/slicc-kernel/issues/1398)); it needs the sync lane. 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`;
452
452
  - `net.connect({ host, port, tls: { servername, alpn, rejectUnauthorized, ca, path, cert, key, passphrase, sni, checkHostname } })` takes one of two paths. By default it's **A1, TLS through the transport** ([#914](https://github.com/ai-ecoverse/slicc-kernel/issues/914)), described first below. For a destination the uplink routes it can also be **A2, a TLS handshake in the kernel** ([#922](https://github.com/ai-ecoverse/slicc-kernel/issues/922)), described after it. `conn.tls.path` (`'transport'` or `'kernel'`) and `conn.tls.substitutes` say which one was taken.
453
453
  - **What it does:** there's no handshake in the kernel. The connection carries plain HTTP/1.1 (and WebSocket upgrades) to the realm proxy's HTTP path for the origin `https://<servername or host>[:port]`, exactly as a `CONNECT` through `127.0.0.1:3128` does after the kernel terminates its TLS. The transport (the page's `fetch` or WebSocket, or the uplink's) makes the real TLS connection and verifies it. So `https.request` over a socket, keep-alive agents, `agent-base` and `wss://` clients work with the transport's semantics. That includes sealed placeholders in headers, which reach the transport exactly as from `ctx.fetch`.
454
454
  - **The `tls` property:** the connection's `tls` is `{ path: 'transport', substitutes: true, protocol: 'transport', alpnProtocol: 'http/1.1', authorized: true, servername }`. `authorized` means the transport verified the server. There's no peer certificate chain and no TLS version of the kernel's own, so don't read it as a real handshake's result.
@@ -7728,7 +7728,7 @@ function satisfies(version, range2) {
7728
7728
  }
7729
7729
 
7730
7730
  // src/version.ts
7731
- var KERNEL_VERSION = true ? "1.145.1" : "0.0.0-dev";
7731
+ var KERNEL_VERSION = true ? "1.146.0" : "0.0.0-dev";
7732
7732
  var OS_RELEASE = "6.0.0-slicc";
7733
7733
  function procVersion() {
7734
7734
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -6800,7 +6800,7 @@ var KERNEL_VERSION, OS_RELEASE;
6800
6800
  var init_version = __esm({
6801
6801
  "src/version.ts"() {
6802
6802
  "use strict";
6803
- KERNEL_VERSION = true ? "1.145.1" : "0.0.0-dev";
6803
+ KERNEL_VERSION = true ? "1.146.0" : "0.0.0-dev";
6804
6804
  OS_RELEASE = "6.0.0-slicc";
6805
6805
  }
6806
6806
  });
@@ -7715,7 +7715,7 @@ function port(n) {
7715
7715
  if (!Number.isInteger(n) || n < 0 || n > 65535) throw new JsCallError("EINVAL", `port ${n}`);
7716
7716
  return n;
7717
7717
  }
7718
- function netOps(kernel, io) {
7718
+ function netOps(kernel, io, sync) {
7719
7719
  const name2 = async (fd, peer) => addressOf(await kernel.json({ op: "sock-name", fd, peer }));
7720
7720
  const connection = async (fd) => {
7721
7721
  const [local3, remote] = [await name2(fd, false), await name2(fd, true)];
@@ -7830,14 +7830,29 @@ function netOps(kernel, io) {
7830
7830
  });
7831
7831
  return [...r.ok && r.kind === "json" ? r.json : []];
7832
7832
  };
7833
+ const inetAt = (options) => ({
7834
+ family: "inet",
7835
+ host: options.host ?? "127.0.0.1",
7836
+ port: port(options.port ?? 0)
7837
+ });
7838
+ const listenSync = (options = {}) => {
7839
+ const lane = sync;
7840
+ const addr = inetAt(options);
7841
+ const fd = lane.json({ op: "sock-open", domain: "inet" });
7842
+ try {
7843
+ lane.call({ op: "sock-bind", fd, addr });
7844
+ lane.call({ op: "sock-listen", fd, backlog: options.backlog ?? 128 });
7845
+ return listener(fd, addressOf(lane.json({ op: "sock-name", fd, peer: false })));
7846
+ } catch (err) {
7847
+ lane.raw({ op: "fd-close", fd });
7848
+ throw err;
7849
+ }
7850
+ };
7833
7851
  return {
7852
+ sync: { listen: listenSync },
7834
7853
  resolve: lookup2,
7835
7854
  listen: (options = {}) => opened2(async (fd) => {
7836
- const addr = {
7837
- family: "inet",
7838
- host: options.host ?? "127.0.0.1",
7839
- port: port(options.port ?? 0)
7840
- };
7855
+ const addr = inetAt(options);
7841
7856
  await kernel.call({ op: "sock-bind", fd, addr });
7842
7857
  await kernel.call({ op: "sock-listen", fd, backlog: options.backlog ?? 128 });
7843
7858
  return listener(fd, await name2(fd, false));
@@ -15393,6 +15408,14 @@ async function vfsOpen(kernel, abs, options, statOf, io, whole) {
15393
15408
  handles.set(fd, handle.invalidate);
15394
15409
  return handle.file;
15395
15410
  }
15411
+ function setcredOn(syncKernel) {
15412
+ return (change) => {
15413
+ if (typeof change !== "object" || change === null || Array.isArray(change)) {
15414
+ throw new JsCallError("EINVAL", "setcred");
15415
+ }
15416
+ return syncKernel.json({ op: "proc-setcred", change: { ...change } });
15417
+ };
15418
+ }
15396
15419
  function watchingSyncOps(kernel, syncKernel, shared) {
15397
15420
  const ops = syncOps(syncKernel, shared);
15398
15421
  const watch = (path, options, listener) => {
@@ -15537,6 +15560,7 @@ function createContext(o) {
15537
15560
  run: (options) => runSync(syncKernel, { env: o.env, cwd: o.cwd, resolve: resolve2, signal: signalNumber }, options),
15538
15561
  spawn: (options) => spawnSync(options),
15539
15562
  wasi: { create: wasi.create },
15563
+ net: { listen: (options) => syncNet.listen(options) },
15540
15564
  exit: (status2 = 0) => {
15541
15565
  exiting ??= status2 & 255;
15542
15566
  wasi.closeAll();
@@ -15545,16 +15569,12 @@ function createContext(o) {
15545
15569
  kill: killer({ self: o.pid, kernel, syncKernel, lane, writes, modes: terminal.modes }),
15546
15570
  cred: () => syncKernel.json({ op: "proc-cred" }),
15547
15571
  umask: (mask) => syncKernel.json(umaskRequest(mask)),
15548
- setcred(change) {
15549
- if (typeof change !== "object" || change === null || Array.isArray(change)) {
15550
- throw new JsCallError("EINVAL", "setcred");
15551
- }
15552
- return syncKernel.json({ op: "proc-setcred", change: { ...change } });
15553
- }
15572
+ setcred: setcredOn(syncKernel)
15554
15573
  };
15555
15574
  const io = { read: read2, send: writeAll, close: (fd) => ctx.close(fd) };
15556
15575
  const defaults = { env: o.env, cwd: o.cwd, signal: signalNumber };
15557
15576
  const { spawnSync, ...children } = childOps(kernel, io, defaults, syncKernel);
15577
+ const { sync: syncNet, ...net } = netOps(kernel, io, syncKernel);
15558
15578
  const strays = o.strays ?? { exception: /* @__PURE__ */ new Set(), rejection: /* @__PURE__ */ new Set() };
15559
15579
  const ctx = {
15560
15580
  kernel: KERNEL_INFO,
@@ -15602,7 +15622,7 @@ function createContext(o) {
15602
15622
  }),
15603
15623
  crontab: async (call2) => await kernel.json({ ...call2, op: "proc-crontab" }),
15604
15624
  ...children,
15605
- net: netOps(kernel, io),
15625
+ net,
15606
15626
  websocket: websocketOp(kernel),
15607
15627
  tty: terminal.tty,
15608
15628
  exit,