@ai-ecoverse/slicc 1.38.0 → 1.39.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.
Files changed (2) hide show
  1. package/README.md +157 -19
  2. package/package.json +8 -8
package/README.md CHANGED
@@ -1,15 +1,17 @@
1
1
  # slicc-cli
2
2
 
3
- `slicc` is SLICC's command line for seven, the SLICC on `*.sliccy.ai`. It is one binary for macOS, Windows and Linux. It launches and links browsers, and shares a machine's network, compute and browsers with a running seven over a WebRTC link. `npx sliccy` and Sliccstart.app run this binary. It replaces slicc-node and slicc-swift.
3
+ `slicc` is SLICC's command line for seven, the SLICC on `*.sliccy.ai`. It is one binary for macOS, Windows and Linux. It launches and links browsers, and shares a machine's network, compute and browsers with a running seven over a WebRTC link. `npx sliccy` and Sliccstart.app run this binary. It replaces slicc-node and slicc-swift (see [Coming from slicc-node](#coming-from-slicc-node)).
4
4
 
5
- **Status:** being built. `slicc` (the default flow), `slicc attach` (with the `cdp` capability), `slicc <join-url> follow`, remote control with `prompt`, `watch` and `abort`, host folders with `--mount`, `slicc browsers`, `slicc launch`, `update`, `install`, `--version` and `--help` work so far, plus a hidden diagnostic, `slicc <join-url> follow --signal-only [--timeout 2m]`. It attaches through the tray hub, prints a summary of the leader's offer (its size, media sections, candidate count and DTLS fingerprint) and the ICE server URLs (credentials are never printed), and exits without answering. The link protocol is drafted alongside seven's link exit and moves into this README once both sides pass a shared test.
5
+ **Status:** being built. `slicc` (the default flow), `slicc attach` (with the `cdp` capability), `slicc open`, `slicc pair` and `slicc unpair` (seven in your everyday browser, without CDP), `slicc <join-url> follow`, remote control with `prompt`, `watch` and `abort`, host folders with `--mount`, `slicc browsers`, `slicc launch`, `update`, `install`, `--version` and `--help` work so far, plus a hidden diagnostic, `slicc <join-url> follow --signal-only [--timeout 2m]`. It attaches through the tray hub, prints a summary of the leader's offer (its size, media sections, candidate count and DTLS fingerprint) and the ICE server URLs (credentials are never printed), and exits without answering. The link protocol is drafted alongside seven's link exit and moves into this README once both sides pass a shared test.
6
6
 
7
7
  ## Commands (planned)
8
8
 
9
9
  | command | what it does |
10
10
  | --- | --- |
11
- | `slicc` | pick browsers in a small TUI; Enter launches or attaches to the best match, opens seven and links it (works now; see [The default](#the-default-slicc)) |
11
+ | `slicc` | pick browsers in a small TUI; Enter launches or attaches to the best match, opens seven and links it. With no SLICC profile and no CDP endpoint, it links seven in the default browser instead (works now; see [The default](#the-default-slicc)) |
12
12
  | `slicc attach --cdp <url>` | link a seven that is open in an already-running CDP browser (works now; see [Local setup](#local-setup-slicc-attach)) |
13
+ | `slicc open`, `slicc --default-browser`, `slicc pair` | link a seven in your everyday browser, without CDP: open or print a pairing URL (works now; see [Linking seven in your everyday browser](#linking-seven-in-your-everyday-browser)) |
14
+ | `slicc unpair [<fingerprint> \| --all]` | list or forget the sevens paired with `open` or `pair` |
13
15
  | `slicc launch` | launch a browser with CDP in a separate SLICC profile and print its CDP URL |
14
16
  | `slicc <join-url> follow [--exit] [--runner …] [--cdp <url>] [--mount <path>]` | follow a seven's tray from another machine and offer its network, compute (over SSH), browsers or folders (`--exit`, `--runner`, `--cdp` and `--mount` work now; see [Remote setup](#remote-setup-slicc-join-url-follow)) |
15
17
  | `slicc <join-url> prompt <text>`, `watch`, `abort` | send a prompt to seven's agent and stream its answer, follow the agent as it works, or abort its run; `--cdp <url>` instead of a join URL links locally (works now; see [Remote control](#remote-control-slicc-prompt-watch-and-abort)) |
@@ -19,11 +21,17 @@
19
21
 
20
22
  ## Install
21
23
 
24
+ ```
25
+ curl -fsSL https://get.sliccy.ai/slicc | sh
26
+ ```
27
+
28
+ This installs `slicc` on macOS and Linux; see [Install with curl](#install-with-curl). Options pass through to the script: `curl -fsSL https://get.sliccy.ai/slicc | sh -s -- --help`. If `get.sliccy.ai` can't be reached, use the GitHub release URL directly:
29
+
22
30
  ```
23
31
  curl -fsSL https://github.com/ai-ecoverse/slicc-cli/releases/latest/download/install.sh | sh
24
32
  ```
25
33
 
26
- This installs `slicc` on macOS and Linux; see [Install with curl](#install-with-curl). The alternatives:
34
+ The alternatives:
27
35
 
28
36
  - **npm:** `npm install -g @ai-ecoverse/slicc`, or `npx @ai-ecoverse/slicc`. The package is a small Node launcher; npm picks one of the platform packages `@ai-ecoverse/slicc-<os>-<arch>` (`darwin`, `linux`, `windows` × `amd64`, `arm64`) through `optionalDependencies` and their `os` and `cpu` fields. The launcher runs that binary with the same arguments, stdio and environment plus `SLICC_LAUNCHER=npm`, forwards `SIGTERM`, `SIGHUP`, `SIGQUIT` and `SIGUSR2` to it, and `SIGINT` too when stdin is not a terminal (a terminal's Ctrl-C already reaches the binary; on Windows the console delivers Ctrl-C and Ctrl-Break to both). It exits with the binary's exit code, or dies of the same signal. An install with `--omit=optional` has no binary, and the launcher says so. slicc-jerk comes the same way; fnox doesn't (see [Companion tools](#companion-tools)).
29
37
  - **GitHub releases:** every release of `ai-ecoverse/slicc-cli` carries `slicc-<os>-<arch>` (`.exe` on Windows) for all six targets, plus `install.sh` and `SHA256SUMS` (which lists `install.sh` too). See [Verifying a download](#verifying-a-download).
@@ -31,8 +39,15 @@ This installs `slicc` on macOS and Linux; see [Install with curl](#install-with-
31
39
 
32
40
  ### Install with curl
33
41
 
34
- `install.sh` is a POSIX `sh` script, attached to every release and kept in the repo root. It:
35
- - takes `VERSION` (default: the latest release; `1.2.3` or `v1.2.3`) and `INSTALL_DIR` (default: `$HOME/.local/bin`) from the environment, or `--version X` and `--dir D` as flags (`curl … | sh -s -- --version 1.2.3 --dir ~/bin`), and `--help`;
42
+ `install.sh` is a POSIX `sh` script, attached to every release and kept in the repo root. `https://get.sliccy.ai/slicc` redirects (`302`) to the latest release's copy, `https://github.com/ai-ecoverse/slicc-cli/releases/latest/download/install.sh`, so both one-liners run the same script:
43
+
44
+ ```
45
+ curl -fsSL https://get.sliccy.ai/slicc | sh
46
+ curl -fsSL https://github.com/ai-ecoverse/slicc-cli/releases/latest/download/install.sh | sh
47
+ ```
48
+
49
+ Options go after `sh -s --`, as in `curl -fsSL https://get.sliccy.ai/slicc | sh -s -- --help`. The script:
50
+ - takes `VERSION` (default: the latest release; `1.2.3` or `v1.2.3`) and `INSTALL_DIR` (default: `$HOME/.local/bin`) from the environment, or `--version X` and `--dir D` as flags (`curl -fsSL https://get.sliccy.ai/slicc | sh -s -- --version 1.2.3 --dir ~/bin`), and `--help`;
36
51
  - supports `Darwin` and `Linux` on `x86_64`/`amd64` and `arm64`/`aarch64` (an x86_64 shell under Rosetta gets `arm64`); anything else exits 1 and points to npm, or on Windows to the release's `.exe`;
37
52
  - downloads `slicc-<os>-<arch>` and `SHA256SUMS` from the release with `curl` or `wget`, checks the hash with `sha256sum` or `shasum -a 256`, and exits 1 without installing anything when the hash differs or `SHA256SUMS` has no line for the asset;
38
53
  - copies the binary to a temporary file in `INSTALL_DIR` and sets mode `755`; it exits 1 and leaves any installed `slicc` alone when that file's `--version` fails, doesn't print `slicc <version>`, or, with a version asked for, names another version;
@@ -118,9 +133,43 @@ Six's `--version`, `version`, `--profile` and `--mount` are seven flags too, and
118
133
  - **Not read:** `~/.slicc/secrets.env`, `~/.slicc/session-id`, `~/.slicc/logs/` and `~/.slicc/cloud-sessions.json`.
119
134
  - **Moved into seven:** `~/.slicc/crontab` and `~/.slicc/webhooks/` now live inside seven's kernel, not on the host.
120
135
  - **Update cache:** six's Go follower caches its update check in `<user cache dir>/slicc/update-check.json`, and this binary uses `update-check-seven.json` next to it. Both files have the same shape, so with one shared file, each binary would show the other's version and hold back the other's check.
121
- - **Kept by this binary:** `<config>/slicc/` holds `ssh_host_ed25519_key`, `tray-certs.json`, `tray-join.json` and `seven-profiles/`.
136
+ - **Kept by this binary:** `<config>/slicc/` holds `ssh_host_ed25519_key`, `tray-certs.json`, `tray-join.json`, `loopback-identity.json`, `loopback-peers.json` and `seven-profiles/`.
122
137
  - **Profiles:** six's Chrome profiles are `~/Library/Application Support/Slicc/profiles/browser-coding-agent-chrome[-<port>]` on macOS, `$XDG_STATE_HOME/slicc/profiles` on Linux and `%LOCALAPPDATA%\Slicc\profiles` on Windows (`chrome-launch.ts:119-148`). seven's live in `<config>/slicc/seven-profiles`, so a SLICC `--profile <name>` never opens six's logins. Only `--profile six` does, in place; see [Using SLICC six's Chrome profile](#using-slicc-sixs-chrome-profile). Discovery lists six's Chrome marked `six: true`, last, and never chooses it.
123
138
 
139
+ ## Coming from slicc-node
140
+
141
+ `npx @ai-ecoverse/slicc-node` and slicc-swift ran a local HTTP proxy, which seven reached on loopback with the URL and key from the launch URL's `#proxy=…&key=…` fragment. `slicc` links seven over WebRTC instead (see [Link](#link)). On this machine the link is set up over CDP, or with `slicc open` over a loopback pairing listener (see [Linking seven in your everyday browser](#linking-seven-in-your-everyday-browser)), and from another machine through the tray hub. There is no proxy port, and no key: `slicc open`'s `#slicc-pair=…` fragment carries a one-time token and the node's certificate fingerprint, and its listener only sets up links. The other listener is the [kernel services](#kernel-services) one, which carries the browser's own connections to `*.kernel.localhost` into the page (`--no-kernel` turns it off).
142
+
143
+ | slicc-node | `slicc` |
144
+ | --- | --- |
145
+ | `npx @ai-ecoverse/slicc-node`, which opens seven in the default browser | `slicc open` (or `slicc --default-browser`) does that, in any browser and without CDP (see [Linking seven in your everyday browser](#linking-seven-in-your-everyday-browser)). `npx sliccy@next`, or `slicc` from `curl -fsSL https://get.sliccy.ai/slicc \| sh` (see [Install with curl](#install-with-curl)). It launches or attaches to a Chromium browser over CDP and opens seven there (see [The default](#the-default-slicc)) |
146
+ | `--page <url>`, `--origin <url>` | `--page <url>`, `--origin <origin>` |
147
+ | `--port`, `--rotate-key`, `--ephemeral`, `--no-open` | not needed: there is no proxy port and no key. `slicc open` listens on 17117 or the next free port up to 17126, `slicc unpair` forgets paired sevens, and `slicc pair` doesn't open a browser |
148
+ | `--mount <path>[:<name>][:ro]` | the same `--mount` (see [Host folders](#host-folders)). In seven the folder is `<device>/<name>` (`mount -t hostfs lars-mbp/project /mnt/p`) rather than `<name>` |
149
+ | `--kernel-port <n>`, `--no-kernel` | the same flags (see [Kernel services](#kernel-services)) |
150
+ | `--cdp <url>`, the `/cdp` relay to a running browser | the [`cdp`](#cdp-browser-control) capability. The browser seven runs in is always listed. More come from `--browser` (with `--cdp <url>` for an endpoint) on `slicc`, `--cdp-extra <url>` on `attach`, and `--cdp <url>` on `follow`. `--cdp-extra` and `follow --cdp` must answer when they start |
151
+ | `--quiet` | no request log. `--plain` prints status lines, and `SLICC_DEBUG=1` logs each `http` stream's method, scheme and host |
152
+ | `/api/fetch-proxy` | `http`, `tcp`, `resolve` and `ws` streams under the [local policy](#network-policy): the internet, this machine and the LAN, but not the browsers' debugging ports or `slicc`'s own ports |
153
+ | `/api/hostfs/*`, tokens re-granted after a `403` | `fs` streams, with no tokens. In seven, a mount has no medium while the link is down, and fills in when the link is back |
154
+ | `/api/kernel-tunnel` | `kernel` streams into the page |
155
+ | `/auth/callback`, `/api/oauth-state`, `/api/oauth-result` | not needed on `https://<label>.sliccy.ai`, where seven signs in through the relay itself. A seven on another origin needs slicc-extension to sign in |
156
+ | `startProxy`, `launchUrl`, `persistentKey` (JS) | no JS API |
157
+
158
+ **Which browser.** slicc-node opened seven in the default browser, in any profile. `slicc` does that too when it finds no SLICC profile and no CDP endpoint: plain `slicc` then falls back to the first way below by itself, and `--no-fallback` turns that off (see [The default](#the-default-slicc)). `slicc` links seven in one of three ways:
159
+ - **Your everyday browser, any vendor:** `slicc open` (or `slicc --default-browser`) opens seven in the default browser and pairs it over loopback, with no CDP and no remote debugging. `slicc pair` prints the pairing URL for a seven that's already open. Once paired, that seven links again by itself after a reload or a restart of `slicc`. The link has the local policy, folders, kernel services and compute, but no `cdp`, since slicc controls no browser there (see [Linking seven in your everyday browser](#linking-seven-in-your-everyday-browser)).
160
+ - **A browser `slicc` controls over CDP**, with `slicc` itself or `slicc attach`, which also offers seven `cdp` for that browser:
161
+ - **by default,** a browser in a SLICC profile (see [`slicc launch`](#slicc-launch---browser-id---profile-name---url-url---headless---no-seven-flags---json)). Its seven is separate from the one in your everyday profile, since seven keeps its files per browser profile.
162
+ - **or a browser that is already running with CDP** (`--cdp`). That includes your everyday Chrome once its remote debugging is turned on at `chrome://inspect/#remote-debugging`. It is then listed as `wsOnly` (see [Browsers](#browsers)).
163
+ - **A seven on another machine**, in any browser, through the tray hub: in seven's Network panel, *Link a device*, then run `slicc <join-url> follow` on this machine (see [Remote setup](#remote-setup-slicc-join-url-follow)). That link has the remote policy (public addresses only, unless `--allow`) and no kernel services.
164
+
165
+ **Restarts.** slicc-node kept its key and port, so an open page reconnected after a restart. `slicc` sets the link up again instead:
166
+ - A browser that `slicc` launched runs in its own process group. It outlives a `slicc` that crashes or is killed.
167
+ - The next `slicc` finds that browser's seven tab and links it again, with `attached` and no `launched`. It finds the tab through the profile's `DevToolsActivePort`, or through `--cdp`. Given the same `--mount` and `--kernel-port`, the folders and kernel services come back too.
168
+ - Quitting `slicc` normally (`q`, Ctrl-C, `SIGTERM`) closes the browsers that run launched.
169
+ - Every new `slicc` process has a new `node` id, so seven sees a new link from the same machine.
170
+ - `attach` sets up again on every new seven context, and `follow` reconnects by itself, with the same device certificate.
171
+ - `slicc open` and `slicc pair` keep their certificate and port, so a paired page signals again after a restart, without a new pairing.
172
+
124
173
  ## Using SLICC six's Chrome profile
125
174
 
126
175
  `slicc --profile six` and `slicc launch --profile six` use SLICC six's own Chrome profile, with its logins, cookies and extensions, in place: it is never copied, moved or migrated. `--profile six:<port>` picks the profile six uses for serve port `<port>`. Nothing else ever chooses six's profile: not the default, `--yes`, `--json`, `--browser` or a `--cdp` URL.
@@ -230,7 +279,7 @@ Release builds outside npm and app bundles print a notice on stderr when a newer
230
279
  ## Telemetry
231
280
 
232
281
  Release builds (a plain `X.Y.Z` version, linked in or, for `go install`, from the module version) send operational telemetry through [go-optel](https://github.com/ai-ecoverse/go-optel) with the app id `slicc-cli`, sampled once per process (1 in 100 by default):
233
- - `enter`, with the subcommand as `source`, from a fixed list (`abort`, `attach`, `browsers`, `follow`, `launch`, `prompt`, `update`, `watch`, `default` for no command (no arguments, or only flags), `join` for a bare join URL, otherwise `unknown`). No other argument is ever sent.
282
+ - `enter`, with the subcommand as `source`, from a fixed list (`abort`, `attach`, `browsers`, `follow`, `install`, `launch`, `open`, `pair`, `prompt`, `unpair`, `update`, `watch`, `default` for no command (no arguments, or only flags), `join` for a bare join URL, otherwise `unknown`). No other argument is ever sent.
234
283
  - `error`, for operational failures, from a fixed set of sources (`update`, `signal`, `link`, `browser`, `ssh`, `cdp`). Messages pass through go-optel's sanitizer, which strips URLs and paths.
235
284
 
236
285
  `--version`, and any command line with `--help`, `-h` or `-help` (such as `slicc update --help`), send nothing, and development builds never configure a client.
@@ -247,6 +296,8 @@ Release builds (a plain `X.Y.Z` version, linked in or, for `go install`, from th
247
296
  | `OPTEL_RATE`, `OPTEL_DEBUG` | go-optel's sampling rate and beacon logging |
248
297
  | `SLICC_LAUNCHER` | set to `npm` by the npm launcher; removed from the environment that `slicc` passes on |
249
298
  | `SLICC_DISCOVERY` | test-only: limits browser discovery, see [Development](#development) |
299
+ | `SLICC_OPEN_COMMAND` | test-only: `slicc open` runs this program with the pairing URL as its only argument instead of opening the default browser; the integration tests always set it |
300
+ | `SLICC_LOOPBACK_PORTS` | test-only: `<first>-<last>`, the port range of the [loopback listener](#linking-seven-in-your-everyday-browser) instead of 17117-17126 |
250
301
  | `SLICC_USER_DATA_DIR`, `XDG_STATE_HOME` | read, as SLICC six reads them, only to find six's Chrome profile (see [Using SLICC six's Chrome profile](#using-slicc-sixs-chrome-profile)) |
251
302
 
252
303
  ## The default: `slicc`
@@ -254,11 +305,21 @@ Release builds (a plain `X.Y.Z` version, linked in or, for `go install`, from th
254
305
  ```
255
306
  slicc [--browser <id>]... [--cdp <url>]... [--page <url>] [--origin <origin>]... [--profile <name> | --profile six[:<port>]] [--headless]
256
307
  [--name <name>] [--kernel-port <n>] [--no-kernel] [--allow <rule>]... [--deny <rule>]... [--mount <path>[:<name>][:ro]]...
257
- [--yes] [--plain] [--json]
308
+ [--yes] [--plain] [--json] [--no-fallback] [--open] [--default-browser]
258
309
  [--runner <argv…> [--]] [--shell <argv…> [--]] [--eval <argv…> [--]] [--eval-quiet <dur>] [--sftp | --no-sftp]
259
310
  ```
260
311
 
261
- `slicc` with no command (`npx sliccy`) finds browsers, launches or attaches to the best one, opens seven in it and links it. It serves the link until it is told to stop: `q` or Ctrl-C in the dashboard, or `SIGINT` or `SIGTERM`. Then it says `bye` to seven, closes the browsers this run launched (as `slicc launch` does), leaves the browsers it attached to running, and exits 0. If the browser that hosts seven goes away, `slicc` closes the rest and exits 0 too. Setup failures (no browser, a launch or attach that fails) exit 1, and bad flags exit 2.
312
+ `slicc` with no command (`npx sliccy`) finds browsers, launches or attaches to the best one, opens seven in it and links it. It serves the link until it is told to stop: `q` or Ctrl-C in the dashboard, or `SIGINT` or `SIGTERM`. Then it says `bye` to seven, closes the browsers this run launched (as `slicc launch` does), leaves the browsers it attached to running, and exits 0. If the browser that hosts seven goes away, `slicc` closes the rest and exits 0 too. Setup failures (no browser, a launch or attach that fails) exit 1, and bad flags exit 2. With `--no-fallback`, a run where nothing can host seven says ``no browser to control: run `slicc open` to link seven in your everyday browser`` and exits 1.
313
+
314
+ **The fallback to your default browser.** Plain `slicc` with no SLICC profile and no CDP endpoint falls back to [linking seven in your everyday browser](#linking-seven-in-your-everyday-browser), as `slicc pair` does:
315
+ - **When:** no `--browser`, `--cdp` or `--profile` was given and `--no-fallback` wasn't, and discovery found no running CDP endpoint (SLICC six's Chrome doesn't count) and no installed Chromium browser with a profile in the SLICC profiles dir. A profile or endpoint, found or given, always wins, so the flow above runs instead.
316
+ - **What it says:** `No SLICC browser or debugging endpoint found, so seven opens in your default browser for pairing. Use --profile or --cdp to choose.` on stderr, in place of the picker and the dashboard. With `--json` it adds `{"schema":1,"event":"mode","mode":"loopback","reason":"no-browser"}` after `discovered`.
317
+ - **The pairing URL:** when stdin and stdout are both terminals, slicc opens it in the default browser. Otherwise it only prints it, unless `--open` is given. Without `--json` it goes to stdout. With `--json` it comes as `{"schema":1,"event":"pairing","url":"<pairing URL>"}`, and so does each new one.
318
+ - **`--json` events** in this mode, with the shapes of the other modes plus `mode: "loopback"` and `device` (the page's certificate fingerprint):
319
+ - `linked`: `peer?`, `caps` and `policy`, for each link that comes up;
320
+ - `link-lost`: `reason?` or `message?`, for each one that goes;
321
+ - `closed`, with `browsers: []`, last, when slicc exits.
322
+ - **Then** it serves loopback links with this run's `--page`, `--origin`, `--name`, `--allow`, `--deny`, `--kernel-port`, `--no-kernel`, `--no-jerk`, `--mount` and compute flags, until `SIGINT` or `SIGTERM`.
262
323
 
263
324
  **Discovery** is `slicc browsers`' discovery and ranking (see [Browsers](#browsers)), with each `--cdp` URL added and the `--page` origin counted as a seven origin.
264
325
 
@@ -281,6 +342,8 @@ The cdp list follows the browsers: an extra that stops answering is taken off th
281
342
  - `--profile <name>`: the SLICC profile for a launched browser that hosts seven. `--profile six` and `--profile six:<port>` use SLICC six's own Chrome profile instead, without the picker (see [Using SLICC six's Chrome profile](#using-slicc-sixs-chrome-profile)).
282
343
  - `--headless`: launch browsers without a window.
283
344
  - `--yes`, `--plain`: no picker. `--json`: NDJSON events, below.
345
+ - `--no-fallback`: never fall back to the default browser. slicc then behaves as it did before the fallback: it launches an installed browser in a new SLICC profile, or exits 1 with `no browser to control` when nothing can host seven (see above). `--open`: when falling back without a terminal, open the pairing URL in the default browser too.
346
+ - `--default-browser`: don't look for browsers; link a seven in the default browser instead, exactly as `slicc open` does (see [Linking seven in your everyday browser](#linking-seven-in-your-everyday-browser)).
284
347
  - A flag of SLICC six's points back to six and exits 2 (see [Coming from SLICC six](#coming-from-slicc-six)). Any other flag, or an argument, is an error.
285
348
 
286
349
  **The picker** comes up when stdin and stdout are both terminals and none of `--yes`, `--browser`, `--plain` or `--json` is given. It lists the ranking best first, one row each, with its state: `CDP :<port>` and `seven open` for a running endpoint, `installed · profile <name>` (or `new SLICC profile <name>` when the profile doesn't exist yet) for a browser slicc would launch, and `installed · Electron` for an Electron app. The first row is marked `best match`. A list longer than the terminal scrolls with the cursor.
@@ -308,6 +371,8 @@ The cdp list follows the browsers: an extra that stops answering is taken off th
308
371
  - `browser-gone`, `browser-back`: `browser`, `name`, `role`. A browser stopped answering and left the cdp list, or answers again and is back on it.
309
372
  - `closed`: `browsers`, the ids slicc closed (always a list). It is the last event of every run that got past choosing its browsers.
310
373
  - `error`: `message`, and `browser?` when closing that browser failed. A setup error ends the run with exit 1.
374
+ - `mode`: `mode` (`loopback`) and `reason` (`no-browser`), when the run [falls back](#the-default-slicc) to the default browser. Then come `pairing`, `linked` and `link-lost` events (each `linked` and `link-lost` with `mode` and `device`, the page's fingerprint), and `closed` with `browsers: []`.
375
+ - `pairing`: `url`, a pairing URL, after `mode`.
311
376
 
312
377
  ```
313
378
  {"schema":1,"event":"discovered","entries":[…]}
@@ -444,7 +509,7 @@ slicc attach --cdp <url> [--cdp-extra <url>]... [--origin <origin>]... [--signal
444
509
  - `--signal`: `cdp` (default) or `stdio`.
445
510
  - `--name`: the link's `name` and `host` in HELLO. The default name is `slicc on <host>`, with this machine's short host name, lower-cased.
446
511
  - `--allow`, `--deny`: policy rules on top of the local default (see [Network policy](#network-policy)). The `--cdp` and `--cdp-extra` ports and endpoint addresses are denied.
447
- - `--runner <argv…>`: offer compute over SSH (cap `ssh`, see [SSH server](#ssh-server)). Each command runs as `<argv…> <command>`. The argv is every word after `--runner` up to a lone `--` or the end of the command line, so `--runner bash -c` or `--runner docker exec -i box sh -c -- --name box`. An argv can't contain a lone `--` itself. `--runner=…` is refused. Without `--runner` the link has no `ssh` capability, and the flags below are refused.
512
+ - `--runner <argv…>`: offer compute over SSH (cap `ssh`, see [SSH server](#ssh-server)). Each command runs as `<argv…> <command>`. The argv is every word after `--runner` up to a lone `--` or the end of the command line, so `--runner bash -c` or `--runner docker exec -i box sh -c -- --name box`. An argv can't contain a lone `--` itself. `--runner=…` is refused. At start, slicc resolves the argv's first word as exec does (on `PATH`, or a path to an executable; a relative path is made absolute, since commands run in the home directory) and exits 2 before linking when it doesn't: `slicc: --runner <x>: program not found`, or, when the word holds whitespace (`--runner "bash -c"`), `slicc: --runner "bash -c": no program "bash -c"; did you mean --runner bash -c?`. Without `--runner` the link has no `ssh` capability, and the flags below are refused.
448
513
  - `--shell <argv…>`: the interactive form for `ssh` without a command, in the same argv syntax. The default is derived from the runner.
449
514
  - `--eval <argv…>` and `--eval-quiet <duration>`: the REPL behind the `slicc-eval` subsystem (for example `--eval python3 -i`), and the silence that ends one response (default `500ms`).
450
515
  - `--sftp`, `--no-sftp`: serve the `sftp` subsystem or not. The default is on only for a plain local shell as the runner.
@@ -482,6 +547,79 @@ slicc attach --cdp <url> [--cdp-extra <url>]... [--origin <origin>]... [--signal
482
547
  - Chrome's `<uuid>.local` candidates are resolved with mDNS in query-only mode.
483
548
  - The link is declared failed after 15 s without connectivity.
484
549
 
550
+ ## Linking seven in your everyday browser
551
+
552
+ ```
553
+ slicc open [--page <url>] [--origin <origin>]... [--name <name>] [--allow <rule>]... [--deny <rule>]...
554
+ [--kernel-port <n>] [--no-kernel] [--no-jerk] [--mount <path>[:<name>][:ro]]...
555
+ [--runner <argv…> [--]] [--shell <argv…> [--]] [--eval <argv…> [--]] [--eval-quiet <dur>] [--sftp | --no-sftp]
556
+ slicc pair [the same flags]
557
+ slicc --default-browser [the same flags]
558
+ slicc unpair [<fingerprint> | --all]
559
+ ```
560
+
561
+ Link spec §1.3. These commands link a seven that runs in the browser you use every day, any vendor's, without CDP or remote debugging, as slicc-node could. Only the setup is new: once the peer connection is up, the link is the same as for `slicc attach` (see [Link](#link)).
562
+
563
+ - **`slicc open`**, and `slicc --default-browser` (the same command), print the pairing URL on stdout and open it in the default browser: `open` on macOS, `xdg-open` on Linux, and `rundll32 url.dll,FileProtocolHandler` on Windows. If that fails, slicc says `could not open the default browser (…); open the URL above yourself` and goes on.
564
+ - **`slicc pair`** only prints the pairing URL, for a seven that's already open. Paste it into seven's address bar, or into its *Link this computer* field.
565
+ - **Plain `slicc`** falls back to this mode by itself when it finds no SLICC profile and no CDP endpoint (see [The default](#the-default-slicc)).
566
+ - Both serve links until `SIGINT` or `SIGTERM`, then exit 0. A listener that can't bind, or a missing config directory, exits 1, and bad flags exit 2. Status lines go to stderr, and `SLICC_DEBUG=1` adds diagnostics.
567
+ - **Flags** are as for `slicc attach`: `--origin`, `--name`, `--allow`, `--deny`, `--kernel-port`, `--no-kernel`, `--no-jerk`, `--mount` and the compute flags. `--page <url>` is the seven page to pair (default `https://seven.sliccy.ai/`), and its origin is accepted as a seven origin. `--default-browser` takes these flags too. The flags of `slicc` that pick or launch browsers (`--browser`, `--cdp`, `--profile`, `--headless`, `--yes`, `--plain`, `--json`) are errors here. A flag of SLICC six's points back to six, as for `slicc`.
568
+
569
+ **Identity.** The node's DTLS certificate (ECDSA P-256, valid for 20 years) lives in `<config>/slicc/loopback-identity.json` (mode `0600`, directory `0700`). The same file holds the port the node last listened on. It is minted on first use, and slicc says `new loopback identity for this computer: <name> (ab12…ef), fingerprint sha-256 AB:12:…`, or `this computer: …` on later runs. It is separate from the tray hub's certificates and survives restarts and updates. A file that exists but can't be read as an identity (another version, broken JSON or PEM) is left alone: slicc says ``<path> is not a loopback identity slicc can read; …`` and exits 1. Only a certificate within a day of expiring is replaced. Whenever slicc mints a new identity (none yet, or a replacement), it forgets every pin in `loopback-peers.json` right after writing it, since every seven has to pair with the new certificate anyway. A `loopback-peers.json` that can't be read is left alone the same way: pairings and `slicc unpair` fail with ``<path> is not a list of paired sevens slicc can read; …``, and signals are refused. `<name>` is `--name`, or this machine's short host name in lower case.
570
+
571
+ **The pairing URL** is `<page>#slicc-pair=<port>.<token>.<node-fp>`:
572
+ - `<port>` is the listener's port;
573
+ - `<token>` is 128 random bits in base64url (22 characters). It is good once, for 10 minutes;
574
+ - `<node-fp>` is the SHA-256 fingerprint of the node's certificate, in lower-case hex without colons.
575
+
576
+ The URL is the secret, and it carries the node's fingerprint, so a process that squats on the port can't pass for the node. slicc prints a new pairing URL each time a line arrives on stdin, so press Enter for a new one. When the newest URL expires unused, slicc says `the pairing URL expired; press Enter for a new one`. Older URLs stay good until their own 10 minutes are up. Pairing URLs only go to stdout; telemetry and logs never get them.
577
+
578
+ **The listener** binds `127.0.0.1:<port>`, plus `[::1]:<port>` when this machine has IPv6 loopback, and never `0.0.0.0`. The port is the one it used last time if that's still free, otherwise the first port in 17117–17126 where both addresses bind. The kernel port (`--kernel-port`, unless `--no-kernel`) is skipped. With none free, it says `no free port in 17117-17126 on 127.0.0.1` and exits 1. It serves exactly two requests:
579
+
580
+ | request | body | answer |
581
+ | --- | --- | --- |
582
+ | `POST /slicc/v1/pair` | `{ "token", "offer": "<sdp>" }` | `200` `{ "answer": "<sdp>", "node": "<per-process id>", "name": "<name>" }` |
583
+ | `POST /slicc/v1/signal` | `{ "offer": "<sdp>" }` | the same |
584
+
585
+ **The gate**, on every request, in this order. Anything that fails it gets `403` with an empty body:
586
+ 1. `Host` is `127.0.0.1:<port>`, `[::1]:<port>` or `localhost:<port>`, ignoring case, so DNS rebinding can't reach it.
587
+ 2. The request target is exactly `/slicc/v1/pair` or `/slicc/v1/signal`: no query, no trailing slash, no absolute form.
588
+ 3. There is exactly one `Origin`, and it is a seven origin under the same rule as CDP setup (`browser.IsSevenOrigin`): `https://<label>.sliccy.ai`, unless slicc-browsers' catalog lists the label in `seven.excludedLabels` or it starts with an entry of `seven.excludedLabelPrefixes`. The `--page` origin and each `--origin` count as seven origins too. `null` and missing origins fail.
589
+ 4. The method is `OPTIONS` (the preflight) or `POST`.
590
+ 5. A `POST` body is a JSON object of at most 64 KiB (65 536 bytes), and its `offer` holds exactly one SHA-256 fingerprint (one value, at the session or media level).
591
+
592
+ - **Preflight:** an `OPTIONS` that passes 1–3, asks for `Access-Control-Request-Method: POST`, and asks for no request header other than `Content-Type` gets `204` with `Access-Control-Allow-Origin: <that origin>` (never `*`), `Access-Control-Allow-Methods: POST`, `Access-Control-Allow-Headers: Content-Type`, `Access-Control-Allow-Private-Network: true`, `Access-Control-Max-Age: 600` and `Vary: Origin`. It never sends `Access-Control-Allow-Credentials`.
593
+ - **CORS on refusals:** a refusal after step 3 (a wrong method, body, token or pin) carries `Access-Control-Allow-Origin` and `Vary: Origin` as well, so seven can tell a `403` from a closed port. Earlier refusals carry no CORS headers. Every answer has `Cache-Control: no-store`.
594
+ - `Origin` can be forged by a process that isn't a browser, so the gate only filters browsers. Trust comes from the token or the pinned fingerprint.
595
+
596
+ **Pairing.**
597
+ - **Rate limit:** at most 10 pairing attempts a minute pass the gate. Past that, `/pair` is refused before the body is read, and slicc logs `refused a pairing: more than 10 a minute` (debug). Signals don't count.
598
+ - **The token:** the node checks it in constant time. A wrong, used or expired token burns nothing, still counts toward the limit, and gets `refused a pairing request from <origin>: the token is wrong, used or expired`. A matching token is burned at once, before the answer, even when the answer then fails.
599
+ - **The pin:** the node pins the offer's fingerprint with the request's `Origin` in `<config>/slicc/loopback-peers.json` (mode `0600`), as `{ "version": 1, "peers": { "<page-fp>": { "origin", "node", "firstSeen" } } }`, where `node` is the fingerprint of the node identity it was paired with. A new pairing of the same fingerprint replaces the entry. slicc says `paired with seven on <origin> (ab12…ef)`. Every change to the file, from this process or from `slicc unpair`, holds an exclusive OS file lock (`flock`, or `LockFileEx` on Windows) on `loopback-peers.json.lock` next to it for its read and write. Concurrent changes never overwrite each other, and since the OS releases the lock when a process exits, a crash leaves no stale lock. A change that can't get the lock within 5 s fails with `the loopback state is being changed by another slicc; try again`. The identity file is created under `loopback-identity.json.lock` the same way, so two first runs at once end up with one certificate.
600
+ - **Signals:** `/signal` is answered only for a pinned fingerprint whose origin equals the request's `Origin` (both normalized) and whose `node` is this node's current identity, so a pin that another `slicc` wrote under an older identity never counts. A `slicc` whose identity another one has since replaced can't change the pins either: its pairings and unlinks fail with `this slicc's loopback identity was replaced by another slicc; restart it`.
601
+ - **The answer:** non-trickled. The node answers through its link stack, waits until ICE gathering is complete (10 s at most, after which it refuses), and returns its SDP with every host candidate and `a=end-of-candidates`. The page checks that the answer's fingerprint is the one it pinned before it uses the answer.
602
+
603
+ **The link in this mode** is local (HELLO `mode: "local"`), with the node's loopback certificate:
604
+ - **Caps:** `net`, `http`, `credentialed`, `ws`, `unlink-loopback`, `fs` with `--mount`, `ssh` with `--runner`, `jerk` and `jerk-seal` unless `--no-jerk`, and kernel services on `--kernel-port` for a page that offers `kernel-in` (see [Kernel services](#kernel-services)). There is no `cdp`, since the node controls no browser, so a `cdp` OPEN gets `EOPNOTSUPP`. `jerk`'s `--seven-origin` is the pinned origin.
605
+ - **Policy:** the local default (see [Network policy](#network-policy)). The protected ports are 9222–9229, every port in a `DevToolsActivePort` file that browser discovery would read (default user-data dirs, SLICC profiles and six's profiles; files are only read, nothing is probed), the listener's port and the kernel port. Those files are read again every 5 s, and a new port is protected from then on as these are: on loopback and on every address of this machine. It isn't added to HELLO's `policy.deny`, which was sent already. So a kernel program can't reach a debugging port the user turned on.
606
+ - **ICE:** host candidates, loopback included, as for `slicc attach`.
607
+ - **Several sevens:** the node keeps one link per paired page fingerprint. A new offer for a fingerprint replaces that fingerprint's link only. Each link is an exit of its own, and kernel connections go to the page that linked last, as for `slicc attach`.
608
+ - **Restarts:** the node keeps its certificate and its port, and the page keeps its certificate and its pin. A restarted `slicc open` or `slicc pair` is reachable again at once, and a paired page signals again without a new token. Each process has a new `node` id, so seven keys the device on the fingerprint.
609
+
610
+ **Unlinking.**
611
+ - **From seven:** on a live link, seven opens `OPEN { "kind": "unlink" }` (cap `unlink-loopback`). The node answers OPENED `{}` and END, deletes that page's pin, closes that link only, and says `unlinked by seven on <origin> (ab12…ef); it must pair again to link`. It keeps running and keeps its certificate.
612
+ - **On the node:** `slicc unpair` lists the pins, one line each: `<page-fp> <origin> paired <time>`, or `no paired sevens`. `slicc unpair <fingerprint>` forgets one. A unique prefix works, ignoring colons and case. It says `forgot ab12…ef (<origin>)`, and exits 1 when nothing matches or more than one pin does. `slicc unpair --all` forgets every pin (`forgot <n> paired sevens`). A link that is up stays up until it drops, and a forgotten page gets `403` on its next signal. Once a link is down and its page is no longer pinned, the node lets go of everything it kept for that page.
613
+
614
+ **The page side**, as the test harness implements it (`test/integration/testdata/link/loopback.js`, the reference for seven):
615
+ 1. `RTCPeerConnection.generateCertificate({ name: "ECDSA", namedCurve: "P-256" })` once per origin, kept in IndexedDB (`slicc-loopback`, store `keys`, key `certificate`) and passed as `certificates` to every loopback `RTCPeerConnection`.
616
+ 2. On load, the page reads `#slicc-pair=<port>.<token>.<node-fp>`, strips it with `history.replaceState`, and asks the user to confirm. Only the confirmation sends the first loopback request.
617
+ 3. It creates the `slicc.link.v1` channel, sets the local offer, and waits for ICE gathering to complete.
618
+ 4. It posts `{ token, offer }` to `http://127.0.0.1:<port>/slicc/v1/pair` with `Content-Type: application/json`, `mode: "cors"` and `credentials: "omit"`.
619
+ 5. It accepts the answer only if its SHA-256 fingerprint is `<node-fp>`; otherwise it refuses the link (a squatter). Then it pins `{ fingerprint: <node-fp>, port, name }`.
620
+ 6. To link again, it posts `{ offer }` to `/slicc/v1/signal`, first on the remembered port, then on 17117–17126, and accepts only an answer with the pinned fingerprint. After a link loss it retries with backoff, from 1 s up to 30 s.
621
+ 7. To unlink, it opens `unlink` on the live link when HELLO lists `unlink-loopback`, then forgets its pin.
622
+
485
623
  ## Link
486
624
 
487
625
  One WebRTC peer connection. seven offers and the node answers. The node:
@@ -516,7 +654,7 @@ HELLO from `attach`: `{ "v": 1, "role": "node", "name", "host", "version", "mode
516
654
  - `addresses: []` means "not mine": the name doesn't exist (NXDOMAIN, or any other failure but a timeout), has no address of that family, or every address was dropped.
517
655
  - RESET codes: `ETIMEDOUT` when the lookup times out (30 s at most), and `EPROTO` for an OPEN without a name or with another `family`.
518
656
 
519
- **`http`** (cap `http`), page → node. slicc-node's raw fetch (its README, *Request* and *Response*) carried on a stream:
657
+ **`http`** (cap `http`), page → node. One HTTP request, fetched by the node without CORS, carried on a stream:
520
658
  - OPEN `{ "kind": "http" }`, answered OPENED `{}` at once.
521
659
  - The page sends a `u32` BE length, that many bytes of UTF-8 JSON `{ "url", "method", "headers": [[name, value], …] }` (at most 1 MiB), then the request body, then END.
522
660
  - The node sends a `u32` BE length, the JSON `{ "status", "statusText", "headers": [[name, value], …], "url" }`, then the body, then END. `url` is the request's.
@@ -608,7 +746,7 @@ Only browsers the user named are listed: the one seven runs in, `--cdp` and `--c
608
746
  - **OPENED:** `{ "product", "protocolVersion" }`, the `Browser` and `Protocol-Version` fields of `/json/version` (from `Browser.getVersion` for a `wsOnly` browser).
609
747
  - **RESET:** `ENOENT` for an id that isn't listed, `ECONNREFUSED` when the browser doesn't answer, `EPROTO` for an OPEN without `browser`.
610
748
 
611
- **Bytes**, both directions: a sequence of messages, each a `u32` BE length plus one UTF-8 CDP JSON message of at most 64 MiB (67 108 864 bytes). The node sends each page message as one WebSocket text message, and each text message from the browser as one stream message, after the filter below. A length over 64 MiB from the page, or a browser message over 64 MiB, is `RESET EMSGSIZE`, and a page END in the middle of a message is `RESET EPROTO`. Browser messages that start with `{"method":"Network.webSocketFrameReceived"` or `{"method":"Network.webSocketFrameSent"` are dropped: seven runs inside a browser it may debug, so those events would carry the CDP traffic itself and grow without bound (parity with slicc-node's `/cdp`).
749
+ **Bytes**, both directions: a sequence of messages, each a `u32` BE length plus one UTF-8 CDP JSON message of at most 64 MiB (67 108 864 bytes). The node sends each page message as one WebSocket text message, and each text message from the browser as one stream message, after the filter below. A length over 64 MiB from the page, or a browser message over 64 MiB, is `RESET EMSGSIZE`, and a page END in the middle of a message is `RESET EPROTO`. Browser messages that start with `{"method":"Network.webSocketFrameReceived"` or `{"method":"Network.webSocketFrameSent"` are dropped: seven runs inside a browser it may debug, so those events would carry the CDP traffic itself and grow without bound.
612
750
 
613
751
  **Closing.**
614
752
  - The browser closes the socket with a normal close (1000): the node sends END, then discards whatever the page still sends until its END.
@@ -650,7 +788,7 @@ Only browsers the user named are listed: the one seven runs in, `--cdp` and `--c
650
788
 
651
789
  **`browsers` updates** (page cap `browsers`), node → page. When the list changes while a link is up and the page's HELLO has `browsers` in `caps`, the node opens `OPEN { "kind": "browsers", "browsers": [ … ] }` on the control channel with the full current list in HELLO's shape. It replaces the previous list; there are no deltas. The page answers OPENED `{}` and END, and the node sends END. Updates are sent one at a time per link, a change that comes while one is in flight is sent after it, with the list as it is then, and an update the page doesn't finish within 10 s is reset with `ETIMEDOUT`. A change between the HELLO and the link coming up is sent as an update right away.
652
790
 
653
- `test/integration/testdata/link/link.js` is a page-side implementation of the link (framing, credit, both channel layouts, the violation rules, and `resolve` and raw-fetch helpers) that the integration tests run in Chrome against `slicc attach`. `cdp.js` next to it is a CDP client over `cdp` streams, `protect.js` probes the filter that keeps seven's origin out of reach, `fs.js` an op-shaped client for `fs` streams (`call`, `list`, `read`, `write`, `watch`), and `page.js` answers `browsers` and `mounts` updates. `page.js` keeps one link per `node` id from the hello. For tray links (no hello `node`) it replaces an older link with the same fingerprint only when HELLO's `node` matches, as seven does. `agent.js` is a fake agent behind the page's `agent` cap: the tests queue scripted runs, emit transcript events, hold and release a run, reset watch streams, and turn the cap off. Loaded with `?kernel`, the test page lists `kernel-in` and answers `kernel` OPENs with `kernel.js`, a small HTTP and WebSocket echo server in the page.
791
+ `test/integration/testdata/link/link.js` is a page-side implementation of the link (framing, credit, both channel layouts, the violation rules, and `resolve` and raw-fetch helpers) that the integration tests run in Chrome against `slicc attach`. `cdp.js` next to it is a CDP client over `cdp` streams, `protect.js` probes the filter that keeps seven's origin out of reach, `fs.js` an op-shaped client for `fs` streams (`call`, `list`, `read`, `write`, `watch`), `loopback.js` is the page side of [loopback signalling](#linking-seven-in-your-everyday-browser), and `page.js` answers `browsers` and `mounts` updates. Through `linkTest.loopback`, `page.js` pairs from the fragment (`confirm`), signals again (`signal`, `reconnect`), unlinks in-band (`unlink`) and posts raw requests (`post`), with an optional `?ports=<first>-<last>` scan range. `page.js` keeps one link per `node` id from the hello. For tray links (no hello `node`) it replaces an older link with the same fingerprint only when HELLO's `node` matches, as seven does. `agent.js` is a fake agent behind the page's `agent` cap: the tests queue scripted runs, emit transcript events, hold and release a run, reset watch streams, and turn the cap off. Loaded with `?kernel`, the test page lists `kernel-in` and answers `kernel` OPENs with `kernel.js`, a small HTTP and WebSocket echo server in the page.
654
792
 
655
793
  ### Kernel services
656
794
 
@@ -688,7 +826,7 @@ The browser's connection waits unread for `OPENED`.
688
826
 
689
827
  ## Host folders
690
828
 
691
- `--mount <path>[:<name>][:ro]` shares a folder of this machine with seven's kernel, which mounts it with its `hostfs` driver. It is repeatable and works on `slicc`, `slicc attach` and `slicc <join-url> follow`. This is slicc-node's hostfs (its README, *Host folders*), with the same operations and the same rules, carried on `fs` link streams instead of HTTP. There are no tokens: the link is the trust boundary, as for `ssh`, and seven decides which kernel mounts it creates. With `follow`, the folders go to the seven on the other machine.
829
+ `--mount <path>[:<name>][:ro]` shares a folder of this machine with seven's kernel, which mounts it with its `hostfs` driver. It is repeatable and works on `slicc`, `slicc attach` and `slicc <join-url> follow`. Each operation is an `fs` link stream (below). There are no tokens: the link is the trust boundary, as for `ssh`, and seven decides which kernel mounts it creates. With `follow`, the folders go to the seven on the other machine.
692
830
 
693
831
  **Export.**
694
832
  - A trailing `:ro` makes the folder read-only. After that, the text after the last `:` is the name if it isn't empty and has no `/` or `\`, so `C:\work` is a path and `C:\work:proj` is named `proj`. The default name is the folder's own.
@@ -713,7 +851,7 @@ The paths are read from slicc's own environment at start. A `--mount` of one of
713
851
  "caseInsensitive": true, "normalization": "nfd-insensitive", "ranges": true } }]
714
852
  ```
715
853
 
716
- - `capabilities` is slicc-node's grant answer, so seven hands it to the driver unchanged.
854
+ - `capabilities` is in the shape slicc-kernel's `hostfs` driver takes, so seven hands it to the driver unchanged.
717
855
  - `symlinks` and `chmod` are `false` on Windows and `true` elsewhere.
718
856
  - `caseInsensitive` is probed per folder on its own volume. The last component of the folder's path that has letters is looked up again with its case swapped, and the probe is `true` when that finds the same file. When no component has letters, it is `true` on macOS and Windows.
719
857
  - `normalization` is `nfd-insensitive` on macOS and `none` elsewhere.
@@ -731,7 +869,7 @@ The paths are read from slicc's own environment at start. A `--mount` of one of
731
869
  - The last component gets lstat semantics. The kernel follows symlinks itself, so `stat` reports a symlink as one, `list` on a symlink is `ENOTDIR`, and `open` on a symlink is `ELOOP`.
732
870
  - Operations that change the namespace run one at a time, and no other operation runs alongside them. The lock covers every link and every folder of the node, so no page can swap a directory for a symlink between the check and the use.
733
871
 
734
- **Operations**, with slicc-node's arguments and answers:
872
+ **Operations**, with their arguments and answers:
735
873
 
736
874
  | op | arguments | answer |
737
875
  | --- | --- | --- |
@@ -798,7 +936,7 @@ The watcher per OS:
798
936
 
799
937
  **Mount updates** (page cap `mounts`). When folders are added or removed while a link is up (`fs.Server.Add` and `Remove`, for the TUI), the node opens `OPEN { "kind": "mounts", "mounts": [ … ] }` on the control channel, with the full list in HELLO's shape. It replaces the previous list, and there are no deltas. The page answers OPENED `{}` and END, and the node sends END. Updates go one at a time per link, and one the page doesn't finish within 10 s is reset with `ETIMEDOUT`. A removed folder's handles close (`EBADF` if the name comes back), its operations are `ENOENT`, and a watch stream covering it ends with `RESET ENOENT: no such folder`. `--mount` alone never changes the set.
800
938
 
801
- **Errors.** The code is the POSIX name, `EIO` when there is none. The message is libuv's lower-case text for it (`no such file or directory`), or slicc-node's own for the cases it names (`path escapes the folder`, `read-only folder`, `file changed since it was opened`, `the folder root cannot be moved or removed`). It never contains a host path. Windows errors are mapped the way libuv maps them, for example `ERROR_ACCESS_DENIED` to `EPERM` and `ERROR_SHARING_VIOLATION` to `EBUSY`.
939
+ **Errors.** The code is the POSIX name, `EIO` when there is none. The message is libuv's lower-case text for it (`no such file or directory`), or slicc's own for the cases it names (`path escapes the folder`, `read-only folder`, `file changed since it was opened`, `the folder root cannot be moved or removed`). It never contains a host path. Windows errors are mapped the way libuv maps them, for example `ERROR_ACCESS_DENIED` to `EPERM` and `ERROR_SHARING_VIOLATION` to `EBUSY`.
802
940
 
803
941
  **Windows.**
804
942
  - Paths go through `\\?\` and `GetFinalPathNameByHandle`, so device names (`CON`, `NUL`) and 8.3 short names are plain names inside the folder.
@@ -1036,7 +1174,7 @@ A node on another machine sets up its link through the tray hub, the `slicc-tray
1036
1174
 
1037
1175
  Go 1.26 and Node ≥ 24 (for the lint tools). `npm run build` writes `bin/slicc`. `npm run lint` runs the slicc lint tools, `gofmt`, `go mod tidy -diff`, `go vet` and golangci-lint. `npm test` runs the integration tests in `test/integration/`, which build the binary and drive it. Unit tests stay next to the code and out of git (`npm run test:unit`). `go build` and `npm run build` link no version, so these binaries report `dev`. `npm run dist` cross-builds all six targets into `artifacts/release/` with `SHA256SUMS`, unsigned. `npm run npm:stage` then stages the npm packages into `dist/npm/`, and `npm run npm:check` packs them with `npm publish --dry-run`.
1038
1176
 
1039
- **Test names: `--hosts <file>`** (test-only, left out of `--help`). `slicc attach` and `slicc <join-url> follow` take a hosts file, so tests get deterministic names without public DNS. Each line is `<ip> <name> [<name>…]`, and `#` starts a comment. Names match case-insensitively, with or without a trailing dot, and a name on several lines gets every address, in file order. Wherever the node resolves a name (`resolve`, `tcp` by name, `http`, and the policy's [browser endpoints](#network-policy)), a name in the file gets the file's addresses of the requested family, possibly none, and never the system resolver's. Other names go to the system resolver. The policy applies afterwards as usual: `resolve` drops refused addresses, and a dial to a refused address is `EACCES`. `tcp` and `http` try the addresses in file order. A file that can't be read or parsed exits 2. A name mapped to a loopback address only helps the node's own dials, `http` and `tcp` by name: a seven kernel that gets a loopback address from `resolve` dials its own loopback, because loopback never goes out through an exit. For end-to-end exit tests, map test names to a non-loopback address of the machine.
1177
+ **Test names: `--hosts <file>`** (test-only, left out of `--help`). `slicc attach`, `slicc open`, `slicc pair` and `slicc <join-url> follow` take a hosts file, so tests get deterministic names without public DNS. Each line is `<ip> <name> [<name>…]`, and `#` starts a comment. Names match case-insensitively, with or without a trailing dot, and a name on several lines gets every address, in file order. Wherever the node resolves a name (`resolve`, `tcp` by name, `http`, and the policy's [browser endpoints](#network-policy)), a name in the file gets the file's addresses of the requested family, possibly none, and never the system resolver's. Other names go to the system resolver. The policy applies afterwards as usual: `resolve` drops refused addresses, and a dial to a refused address is `EACCES`. `tcp` and `http` try the addresses in file order. A file that can't be read or parsed exits 2. A name mapped to a loopback address only helps the node's own dials, `http` and `tcp` by name: a seven kernel that gets a loopback address from `resolve` dials its own loopback, because loopback never goes out through an exit. For end-to-end exit tests, map test names to a non-loopback address of the machine.
1040
1178
 
1041
1179
  Front ends and capabilities follow a node's links with `node.Node.Subscribe(func(node.Event)) (unsubscribe func())`. Events: `connecting` (an offer was answered), `linked` (the peer's HELLO arrived, in `Peer`), `link-down` (`Reason` when the node closed the link: seven's `bye` reason, `replaced by a new offer` or `slicc is exiting`; otherwise `Err` for a failure; a failed answer has no `Link`), and `stream-open` and `stream-close` for each page-opened stream, with its kind. Every event but a failed answer carries its `*link.Link`. A link's `link-down` comes before the next link's `connecting`, but its streams' `stream-close` can come after its `link-down`. Events are delivered synchronously on the node's and the link's goroutines, so a subscriber must return quickly and never block; one that needs to wait hands the event to its own goroutine.
1042
1180
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc",
3
- "version": "1.38.0",
3
+ "version": "1.39.0",
4
4
  "description": "SLICC's command line for seven: links a machine's network, compute and browsers to seven over WebRTC",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -18,12 +18,12 @@
18
18
  "node": ">=18"
19
19
  },
20
20
  "optionalDependencies": {
21
- "@ai-ecoverse/slicc-darwin-arm64": "1.38.0",
22
- "@ai-ecoverse/slicc-darwin-amd64": "1.38.0",
23
- "@ai-ecoverse/slicc-linux-arm64": "1.38.0",
24
- "@ai-ecoverse/slicc-linux-amd64": "1.38.0",
25
- "@ai-ecoverse/slicc-windows-arm64": "1.38.0",
26
- "@ai-ecoverse/slicc-windows-amd64": "1.38.0",
27
- "@ai-ecoverse/slicc-jerk": "^0.13.0"
21
+ "@ai-ecoverse/slicc-darwin-arm64": "1.39.0",
22
+ "@ai-ecoverse/slicc-darwin-amd64": "1.39.0",
23
+ "@ai-ecoverse/slicc-linux-arm64": "1.39.0",
24
+ "@ai-ecoverse/slicc-linux-amd64": "1.39.0",
25
+ "@ai-ecoverse/slicc-windows-arm64": "1.39.0",
26
+ "@ai-ecoverse/slicc-windows-amd64": "1.39.0",
27
+ "@ai-ecoverse/slicc-jerk": "^0.15.2"
28
28
  }
29
29
  }