@ai-ecoverse/slicc 1.21.0 → 1.23.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 +134 -10
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
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.
|
|
4
4
|
|
|
5
|
-
**Status:** being built. `slicc` (the default flow), `slicc attach` (with the `cdp` capability), `slicc <join-url> follow`, `slicc browsers`, `slicc launch`, `update`, `--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 <join-url> follow`, host folders with `--mount`, `slicc browsers`, `slicc launch`, `update`, `--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
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
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)) |
|
|
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
13
|
| `slicc launch` | launch a browser with CDP in a separate SLICC profile and print its CDP URL |
|
|
14
|
-
| `slicc <join-url> follow [--exit] [--runner …] [--cdp <url>]` | follow a seven's tray from another machine and offer its network, compute (over SSH) or
|
|
14
|
+
| `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
15
|
| `slicc browsers --json` | list running CDP endpoints and installed browsers, ranked |
|
|
16
16
|
| `slicc update [--check]` | replace this binary with the latest release (works now; see [Updates](#updates)) |
|
|
17
17
|
|
|
@@ -60,7 +60,9 @@ Release builds send operational telemetry through [go-optel](https://github.com/
|
|
|
60
60
|
|
|
61
61
|
```
|
|
62
62
|
slicc [--browser <id>]... [--cdp <url>]... [--page <url>] [--origin <origin>]... [--profile <name>] [--headless]
|
|
63
|
-
[--name <name>] [--kernel-port <n>] [--no-kernel] [--allow <rule>]... [--deny <rule>]... [--
|
|
63
|
+
[--name <name>] [--kernel-port <n>] [--no-kernel] [--allow <rule>]... [--deny <rule>]... [--mount <path>[:<name>][:ro]]...
|
|
64
|
+
[--yes] [--plain] [--json]
|
|
65
|
+
[--runner <argv…> [--]] [--shell <argv…> [--]] [--eval <argv…> [--]] [--eval-quiet <dur>] [--sftp | --no-sftp]
|
|
64
66
|
```
|
|
65
67
|
|
|
66
68
|
`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.
|
|
@@ -74,13 +76,15 @@ slicc [--browser <id>]... [--cdp <url>]... [--page <url>] [--origin <origin>]...
|
|
|
74
76
|
|
|
75
77
|
An Electron app can't host seven. Each further browser is an extra: an installed one is launched without a start page, and an endpoint is used as it is. Then the link is set up as for `slicc attach` (see [Local setup](#local-setup-slicc-attach)): over CDP with the browser that hosts seven, the first page tab on an allowed origin.
|
|
76
78
|
|
|
77
|
-
**What the link offers:** `net` (tcp, resolve), `http`, and `cdp` with the browser that hosts seven (`seven: true`) and every extra, under the local policy with all of their debugging ports protected; and kernel services on `--kernel-port` (default 80; `--no-kernel` turns them off) when seven offers `kernel-in` (see [Kernel services](#kernel-services)). seven gets full control of every listed browser.
|
|
79
|
+
**What the link offers:** `net` (tcp, resolve), `http`, and `cdp` with the browser that hosts seven (`seven: true`) and every extra, under the local policy with all of their debugging ports protected; and kernel services on `--kernel-port` (default 80; `--no-kernel` turns them off) when seven offers `kernel-in` (see [Kernel services](#kernel-services)); and `ssh` only with `--runner` (see [SSH server](#ssh-server)); and `fs` with each `--mount` folder (see [Host folders](#host-folders)). seven gets full control of every listed browser.
|
|
80
|
+
|
|
81
|
+
The cdp list follows the browsers: an extra that stops answering is taken off the list (`browser-gone`), and listed again with the same id when it answers on the same endpoint again (`browser-back`).
|
|
78
82
|
|
|
79
83
|
**Flags.**
|
|
80
84
|
- `--browser <id>`: an id from `slicc browsers` (an installed id such as `chrome`, or `cdp:<host>:<port>`), or a name, case-insensitively. Repeatable: the first hosts seven, the others are extras. Without it, the best match that can host seven.
|
|
81
85
|
- `--cdp <url>`: also consider this CDP endpoint, as `slicc browsers --cdp`.
|
|
82
86
|
- `--page <url>`: the seven page, default `https://seven.sliccy.ai/`. It must be `http` or `https`, and its origin is accepted as a seven origin.
|
|
83
|
-
- `--origin`, `--name`, `--allow`, `--deny`, `--kernel-port`, `--no-kernel`: as for `slicc attach`.
|
|
87
|
+
- `--origin`, `--name`, `--allow`, `--deny`, `--kernel-port`, `--no-kernel`, `--mount`, and the compute flags `--runner`, `--shell`, `--eval`, `--eval-quiet`, `--sftp`, `--no-sftp`: as for `slicc attach`. Without `--runner` there is no `ssh`.
|
|
84
88
|
- `--profile <name>`: the SLICC profile for a launched browser that hosts seven.
|
|
85
89
|
- `--headless`: launch browsers without a window.
|
|
86
90
|
- `--yes`, `--plain`: no picker. `--json`: NDJSON events, below.
|
|
@@ -97,9 +101,9 @@ An Electron app can't host seven. Each further browser is an extra: an installed
|
|
|
97
101
|
| Esc | back, in the profile chooser |
|
|
98
102
|
| `q`, Ctrl-C | quit; in the dashboard, close down as described above |
|
|
99
103
|
|
|
100
|
-
**The dashboard** follows: the linked browsers (launched or attached, their CDP port, hosts seven or extra), the seven tab, the link state (`waiting for seven`, `connecting`, `linked` with its uptime, or `reconnecting` after a link is lost, while slicc waits for seven to offer again), the capabilities offered, the policy summary, the kernel services URL, stream counts per kind (open and total, never contents), the last status lines, and the update notice, which then isn't printed on stderr. `NO_COLOR` turns colors off.
|
|
104
|
+
**The dashboard** follows: the linked browsers (launched or attached, their CDP port, hosts seven or extra, and `gone` while one doesn't answer), the seven tab, the link state (`waiting for seven`, `connecting`, `linked` with its uptime, or `reconnecting` after a link is lost, while slicc waits for seven to offer again), the capabilities offered, the policy summary, `compute ssh via <runner>` with `--runner`, the kernel services URL, stream counts per kind (open and total, never contents), the last status lines, and the update notice, which then isn't printed on stderr. `NO_COLOR` turns colors off.
|
|
101
105
|
|
|
102
|
-
**Plain mode** (no terminal, `--yes`, `--browser` or `--plain`): no picker and no dashboard, just the default, with status lines on stderr (`slicc: launched …`, `slicc: attached to …`, `slicc: linked with "<name>" in <tab>`, `slicc: link lost: …`, `slicc: closed <ids>`). It never writes ANSI escapes or cursor codes. `SLICC_DEBUG=1` adds diagnostics.
|
|
106
|
+
**Plain mode** (no terminal, `--yes`, `--browser` or `--plain`): no picker and no dashboard, just the default, with status lines on stderr (`slicc: launched …`, `slicc: attached to …`, `slicc: offering …; policy: …; compute: ssh via <runner>`, `slicc: linked with "<name>" in <tab>`, `slicc: link lost: …`, `slicc: <name> quit …`, `slicc: <name> is back …`, `slicc: closed <ids>`). It never writes ANSI escapes or cursor codes. `SLICC_DEBUG=1` adds diagnostics.
|
|
103
107
|
|
|
104
108
|
**`--json`** prints NDJSON events on stdout and nothing else there, for Sliccstart (`slicc --json --browser <id>`). Errors are also printed on stderr. Every event has `"schema": 1` and `event`. Fields marked `?` are left out when empty; new fields and events may be added, and existing ones keep their meaning.
|
|
105
109
|
- `discovered`: `entries`, the ranked list in the shape of `slicc browsers --json` (always a list).
|
|
@@ -107,6 +111,7 @@ An Electron app can't host seven. Each further browser is an extra: an installed
|
|
|
107
111
|
- `attached`: `browser`, `name`, `version?`, `role`, `launched?` (`true` when this run launched it), `cdp`, `webSocketDebuggerUrl?`. For `seven` slicc is connected to it over CDP; an `extra` is listed for `cdp`.
|
|
108
112
|
- `linked`: `peer?` (the name in seven's HELLO), `tab?` (the seven tab's URL), `caps`, `policy` (the summary).
|
|
109
113
|
- `link-lost`: `reason?` when slicc or seven closed the link (`slicc is exiting`, seven's `bye` reason, `replaced by a new offer`), or `message?` for a failure. seven sets the link up again after a reload, and a new `linked` follows.
|
|
114
|
+
- `browser-gone`, `browser-back`: `browser`, `name`, `role`. A browser stopped answering and left the cdp list, or answers again and is back on it.
|
|
110
115
|
- `closed`: `browsers`, the ids slicc closed (always a list). It is the last event of every run that got past choosing its browsers.
|
|
111
116
|
- `error`: `message`, and `browser?` when closing that browser failed. A setup error ends the run with exit 1.
|
|
112
117
|
|
|
@@ -219,6 +224,7 @@ Plain output: the CDP HTTP URL on stdout, status lines on stderr. With `--json`,
|
|
|
219
224
|
```
|
|
220
225
|
slicc attach --cdp <url> [--cdp-extra <url>]... [--origin <origin>]... [--signal cdp|stdio] [--name <name>] [--allow <rule>]... [--deny <rule>]...
|
|
221
226
|
[--runner <argv…> [--]] [--shell <argv…> [--]] [--eval <argv…> [--]] [--eval-quiet <dur>] [--sftp | --no-sftp] [--kernel-port <n>] [--no-kernel]
|
|
227
|
+
[--mount <path>[:<name>][:ro]]...
|
|
222
228
|
```
|
|
223
229
|
|
|
224
230
|
`attach` links a seven that is open in a running CDP browser and serves the link until it is interrupted (`SIGINT`, `SIGTERM`), or until stdin closes with `--signal stdio`. Status lines go to stderr, and `SLICC_DEBUG=1` adds diagnostics, pion's included. pion never writes to stderr itself.
|
|
@@ -234,6 +240,7 @@ slicc attach --cdp <url> [--cdp-extra <url>]... [--origin <origin>]... [--signal
|
|
|
234
240
|
- `--shell <argv…>`: the interactive form for `ssh` without a command, in the same argv syntax. The default is derived from the runner.
|
|
235
241
|
- `--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`).
|
|
236
242
|
- `--sftp`, `--no-sftp`: serve the `sftp` subsystem or not. The default is on only for a plain local shell as the runner.
|
|
243
|
+
- `--mount <path>[:<name>][:ro]`: share this folder with seven's kernel (cap `fs`, repeatable); see [Host folders](#host-folders).
|
|
237
244
|
- `--kernel-port`: the local port for [kernel services](#kernel-services) (1–65535, default `80`).
|
|
238
245
|
- `--no-kernel`: no kernel services; nothing listens.
|
|
239
246
|
|
|
@@ -276,7 +283,7 @@ One WebRTC peer connection. seven offers and the node answers. The node:
|
|
|
276
283
|
- **Resets with `EOPNOTSUPP`:** an OPEN for a kind it doesn't serve.
|
|
277
284
|
- **Closes the link:** an unknown frame type, a short or malformed frame (END with a payload, CREDIT that isn't 4 bytes), stream id 0, a reused id, an id with the wrong parity (page streams are odd, node streams even), or a second OPEN on a stream channel.
|
|
278
285
|
|
|
279
|
-
HELLO from `attach`: `{ "v": 1, "role": "node", "name", "host", "version", "mode": "local", "caps": ["net", "http", "cdp"], "routes": { "prefixes": [], "exit": true }, "policy": { "allow", "deny", "summary" }, "browsers": [ … ] }`. `cdp` and `browsers` are there when at least one browser is listed (see [`cdp`](#cdp-browser-control)). With `--runner`, `caps` adds `"ssh"` and HELLO adds `"sshHostKey": "ssh-ed25519 AAAA…"`, the host key in `authorized_keys` form, so the page can write `<host>.slicc.internal <key>` into the kernel's `known_hosts`.
|
|
286
|
+
HELLO from `attach`: `{ "v": 1, "role": "node", "name", "host", "version", "mode": "local", "caps": ["net", "http", "cdp"], "routes": { "prefixes": [], "exit": true }, "policy": { "allow", "deny", "summary" }, "browsers": [ … ] }`. `cdp` and `browsers` are there when at least one browser is listed (see [`cdp`](#cdp-browser-control)). With `--runner`, `caps` adds `"ssh"` and HELLO adds `"sshHostKey": "ssh-ed25519 AAAA…"`, the host key in `authorized_keys` form, so the page can write `<host>.slicc.internal <key>` into the kernel's `known_hosts`. With `--mount`, `caps` adds `"fs"` and HELLO adds `"mounts"` (see [Host folders](#host-folders)).
|
|
280
287
|
|
|
281
288
|
**`ssh`** (cap `ssh`), page → node, with `--runner` only:
|
|
282
289
|
- OPEN `{ "kind": "ssh" }`, OPENED `{}` at once. The stream's bytes are one SSH connection to the node's SSH server.
|
|
@@ -340,7 +347,7 @@ HELLO from `attach`: `{ "v": 1, "role": "node", "name", "host", "version", "mode
|
|
|
340
347
|
|
|
341
348
|
**`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.
|
|
342
349
|
|
|
343
|
-
`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, and `page.js` answers `browsers` updates. 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.
|
|
350
|
+
`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, `fs.js` an op-shaped client for `fs` streams (`call`, `list`, `read`, `write`, `watch`), and `page.js` answers `browsers` and `mounts` updates. 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.
|
|
344
351
|
|
|
345
352
|
### Kernel services
|
|
346
353
|
|
|
@@ -376,6 +383,119 @@ The browser's connection waits unread for `OPENED`.
|
|
|
376
383
|
|
|
377
384
|
**Security.** The `Host` check defends against DNS rebinding. Kernel services are reachable like any dev server on localhost: by any local process, and by any web page Chrome lets reach loopback. Use `--no-kernel` to keep them inside the page.
|
|
378
385
|
|
|
386
|
+
## Host folders
|
|
387
|
+
|
|
388
|
+
`--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.
|
|
389
|
+
|
|
390
|
+
**Export.**
|
|
391
|
+
- 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.
|
|
392
|
+
- The folder is resolved with `realpath` at start. A missing path or a file is skipped with `--mount <spec>: not an existing folder, skipping`, a name of `.` or `..` with `invalid name, skipping`, and a second folder under a name already used with `the name <name> is taken, skipping`.
|
|
393
|
+
- Names never reveal host paths, and neither does anything else on the link.
|
|
394
|
+
- Without a folder, the node doesn't advertise `fs`, and an `fs` OPEN gets `EOPNOTSUPP`.
|
|
395
|
+
|
|
396
|
+
**HELLO.** `caps` lists `fs`, and `"mounts"` lists the folders:
|
|
397
|
+
|
|
398
|
+
```json
|
|
399
|
+
"mounts": [{ "name": "project", "readonly": false,
|
|
400
|
+
"capabilities": { "maxIo": 16777216, "symlinks": true, "chmod": true,
|
|
401
|
+
"caseInsensitive": true, "normalization": "nfd-insensitive", "ranges": true } }]
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
- `capabilities` is slicc-node's grant answer, so seven hands it to the driver unchanged.
|
|
405
|
+
- `symlinks` and `chmod` are `false` on Windows and `true` elsewhere.
|
|
406
|
+
- `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.
|
|
407
|
+
- `normalization` is `nfd-insensitive` on macOS and `none` elsewhere.
|
|
408
|
+
- `ranges: true` says `setattr` takes `size`, so the kernel reads and writes in pages.
|
|
409
|
+
|
|
410
|
+
**Streams.** Each operation is one stream, `OPEN { "kind": "fs", "mount", "readonly"?, "op", …arguments }`. An OPEN is one frame, so at most 64 KiB.
|
|
411
|
+
- The node answers OPENED (followed by DATA for `list` and `read`), then END. The page sends END once it has OPENED.
|
|
412
|
+
- An error is `RESET <ERRNO>: <message>`, before OPENED or, for `list`, `read`, `write` and `watch`, after it.
|
|
413
|
+
- `readonly: true` on an OPEN makes that call `EROFS` if it writes, opens for writing or changes the namespace, even on a writable folder. A `:ro` folder is that way for every call.
|
|
414
|
+
- `read` and `list` may go on the control channel or on their own stream channel. Pages should give reads over 64 KiB their own channel. `write` and `watch` belong on their own channel.
|
|
415
|
+
|
|
416
|
+
**Paths** are relative to the folder, `/`-separated, with `""` (or a missing or `null` `path`) for the root. Empty and `.` components are ignored.
|
|
417
|
+
- `..` and a leading `/` are `EACCES`, and NUL is `EINVAL`. On Windows `\` and `:` are `EINVAL` too, which also rules out alternate data streams and drive letters.
|
|
418
|
+
- Every operation resolves the parent directory with `realpath`, and refuses it with `EACCES` when it lies outside the folder.
|
|
419
|
+
- 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`.
|
|
420
|
+
- 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.
|
|
421
|
+
|
|
422
|
+
**Operations**, with slicc-node's arguments and answers:
|
|
423
|
+
|
|
424
|
+
| op | arguments | answer |
|
|
425
|
+
| --- | --- | --- |
|
|
426
|
+
| `stat` | `path` | OPENED `attr` |
|
|
427
|
+
| `list` | `path` | OPENED `{}`, then NDJSON lines `{"name","attr"}` as DATA; an entry that vanishes meanwhile is left out |
|
|
428
|
+
| `mkdir` | `path` | `{}`; not recursive, mode `0777` minus the umask |
|
|
429
|
+
| `rmdir` | `path` | `{}` |
|
|
430
|
+
| `unlink` | `path` | `{}`; `EISDIR` for a directory |
|
|
431
|
+
| `rename` | `from`, `to` | `{}`, `rename(2)`; it replaces a file or an empty directory, a directory onto a non-empty one is `ENOTEMPTY`, a directory onto a file `ENOTDIR`, a file onto a directory `EISDIR` |
|
|
432
|
+
| `symlink` | `target`, `path` | `{}`; `target` is stored as given; `EPERM` on Windows |
|
|
433
|
+
| `readlink` | `path` | `{ "target" }`; `EINVAL` for anything but a symlink |
|
|
434
|
+
| `setattr` | `path`, `size?`, `mode?`, `mtime?` (ms) | `{}`; `size` truncates or extends with zeros, like `truncate(2)`, then `mode` and `mtime` apply. `size` on a directory is `EISDIR`, `size` or `mode` on a symlink is `EINVAL`, and `mtime` doesn't follow a symlink |
|
|
435
|
+
| `statfs` | | `{ "bsize", "blocks", "bfree", "bavail" }` of the folder's volume |
|
|
436
|
+
| `open` | `path`, `write?`, `create?`, `truncate?`, `exclusive?`, `mode?` | `{ "fh", "attr" }` |
|
|
437
|
+
| `read` | `fh`, `offset`, `size` (≤ `maxIo`), `ifMatch?` | OPENED `{ "etag", "size", "offset", "length" }`, then `length` bytes as DATA |
|
|
438
|
+
| `write` | `fh`, `offset` | OPENED `{}`; the page sends the bytes, then END, and the node ENDs once they are written |
|
|
439
|
+
| `release` | `fh` | `{ "attr" }` for a write handle, `{}` otherwise |
|
|
440
|
+
| `watch` | `mounts` (no `mount`) | OPENED `{}`, then NDJSON lines as DATA |
|
|
441
|
+
|
|
442
|
+
- `attr` is `{ "kind": "file"|"directory"|"symlink", "size", "mtime" (ms, fractional), "mode" (permission bits), "ino", "etag" }`.
|
|
443
|
+
- `etag` is `"<size>-<mtimeNs>-<ino>"`, quotes included, so `attr.etag` and `ifMatch` compare as plain strings.
|
|
444
|
+
- The folder's root can't be removed or renamed, or replaced by a rename (`EBUSY`).
|
|
445
|
+
- **Arguments:**
|
|
446
|
+
- `write`, `create`, `truncate`, `exclusive` and `readonly` count when they are truthy, as in JavaScript.
|
|
447
|
+
- Offsets and sizes are integers from 0 to 2^53−1, `mode` an integer up to `0o7777`, and `mtime` a number. `fh` is a positive integer, and any other `fh` is `EBADF`.
|
|
448
|
+
- A `path`, `from`, `to` or `target` that isn't a string is `EINVAL`, and so is any other argument of the wrong type, `null` included.
|
|
449
|
+
- An unknown `op`, or no `op`, is `EINVAL`. A `mount` that isn't exported is `ENOENT` with `no such folder`.
|
|
450
|
+
|
|
451
|
+
**Handles.** `open` without `write`, `create`, `truncate` or `exclusive` gives a name handle, which holds nothing open between calls. Any of those flags gives a write handle, which keeps the file open (read and write) until `release`.
|
|
452
|
+
- `create`, `exclusive` (with `create`), `truncate` and `mode` (default `0666` minus the umask) apply at `open`, and their errors come back there. A directory is `EISDIR` (`EEXIST` with `create` and `exclusive`), anything but a regular file `EINVAL`.
|
|
453
|
+
- Handles are numbered per link from 1. A link holds at most 4096 per folder, and the next `open` is `EMFILE`.
|
|
454
|
+
- An `fh` sent with another `mount` than the one it was opened on is `EBADF`, and so is one already released.
|
|
455
|
+
- When the link closes, its handles close. What was written stays, as with a crashed local process.
|
|
456
|
+
|
|
457
|
+
**Reads.** A read on a name handle opens the file again, through the path rules. When `ifMatch` is given and differs from the file's current etag, it is `ESTALE` with `file changed since it was opened`, so the kernel starts over instead of mixing two versions. A read on a write handle reads its descriptor and ignores `ifMatch`.
|
|
458
|
+
- OPENED carries the file's `etag` and `size` with the `offset` asked for and the `length` that follows. The read is short at EOF, and `length` is 0 past it.
|
|
459
|
+
- If the file shrinks while the bytes are being sent, the stream is `RESET ESTALE`.
|
|
460
|
+
|
|
461
|
+
**Writes happen in place,** like `open(2)` and `pwrite(2)`, in any order and with holes. Writing in place keeps hard links, extended attributes, ACLs and ownership. Programs that want atomic replacement write a temporary file and rename it, and those are operations too.
|
|
462
|
+
- Before OPENED, the node checks the call: `EROFS` for a read-only folder or `readonly: true`, `EINVAL` for a bad `offset`, `EBADF` for an unknown handle, one from another folder, or a name handle.
|
|
463
|
+
- Each DATA is written at its place before it is credited, so the stream window bounds the memory used. More than `maxIo` bytes in one call is `RESET EINVAL`, and what came before stays written.
|
|
464
|
+
- After the page's END and the last write, the node sends END. A page RESET leaves what was written.
|
|
465
|
+
|
|
466
|
+
**Watch.** `OPEN { "kind": "fs", "op": "watch", "mounts": ["project", …] }` streams NDJSON lines until the page sends END or the link closes:
|
|
467
|
+
|
|
468
|
+
```
|
|
469
|
+
{"mount":"project","paths":["src/a.ts","src"]}
|
|
470
|
+
{"mount":"project","all":true}
|
|
471
|
+
{"ping":1}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
- A missing or empty `mounts`, or one that isn't a list of strings, is `EINVAL`, and a name that isn't exported is `ENOENT`.
|
|
475
|
+
- `paths` name what changed and its parent directory (`""` for the root), coalesced over 50 ms. Past 256 paths the line is `all`, and so is a change the watcher can't name (the root itself, an event-queue overflow).
|
|
476
|
+
- A watcher that fails or can't start is retried every second. It sends one `all` when it is lost and one when it is back. A folder that is renamed away or removed is lost until it comes back.
|
|
477
|
+
- A ping goes out every 15 s.
|
|
478
|
+
- Changes seven made itself come back too.
|
|
479
|
+
- One watcher per folder serves every watch stream on every link, and it lingers 5 s after the last stream ends, so replacing a watch is cheap.
|
|
480
|
+
- A page that doesn't read gets its backlog folded into one `all` per folder instead of a growing queue.
|
|
481
|
+
|
|
482
|
+
The watcher per OS:
|
|
483
|
+
- **Linux:** inotify through fsnotify, one watch per directory, so the node walks the folder at start and adds every new directory. Symlinks aren't followed. A tree past `fs.inotify.max_user_watches` makes the watcher fail, which is then retried every second.
|
|
484
|
+
- **macOS:** kqueue through fsnotify, which holds one descriptor per watched directory and file. A very large tree can run out of descriptors in the same way.
|
|
485
|
+
- **Windows:** one `ReadDirectoryChangesW` on the folder with `bWatchSubtree`, which is what libuv does. A handle on every directory would stop Windows from renaming any of their parents.
|
|
486
|
+
|
|
487
|
+
**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.
|
|
488
|
+
|
|
489
|
+
**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`.
|
|
490
|
+
|
|
491
|
+
**Windows.**
|
|
492
|
+
- Paths go through `\\?\` and `GetFinalPathNameByHandle`, so device names (`CON`, `NUL`) and 8.3 short names are plain names inside the folder.
|
|
493
|
+
- Files are opened with `FILE_SHARE_DELETE` and without following reparse points.
|
|
494
|
+
- Symlinks and junctions both show as `kind: "symlink"`, `readlink` works on both, and `unlink` removes the link itself.
|
|
495
|
+
- `ino` is the file index, and `mode` is `0666`, or `0444` for a read-only file, plus `0111` for a directory. `setattr` `mode` sets or clears the read-only attribute.
|
|
496
|
+
- `unlink` clears the read-only attribute first, as `DeleteFile` would refuse otherwise.
|
|
497
|
+
- `rename` gives the POSIX answers above, replacing an empty directory itself.
|
|
498
|
+
|
|
379
499
|
## Network policy
|
|
380
500
|
|
|
381
501
|
A node checks every address it connects to for the link's `tcp`, `http` and `ssh` forwarding, and drops refused addresses from `resolve` answers. The check runs on each address actually connected, after DNS and for every Happy Eyeballs attempt, so a name can't rebind past it. A refusal is `EACCES`.
|
|
@@ -453,6 +573,7 @@ A command that can't start exits 127 with the reason on stderr.
|
|
|
453
573
|
```
|
|
454
574
|
slicc <join-url> follow [--exit] [--allow <rule>]... [--deny <rule>]... [--name <name>] [--cdp <url>]...
|
|
455
575
|
[--runner <argv…> [--]] [--shell <argv…> [--]] [--eval <argv…> [--]] [--eval-quiet <dur>] [--sftp | --no-sftp]
|
|
576
|
+
[--mount <path>[:<name>][:ro]]...
|
|
456
577
|
```
|
|
457
578
|
|
|
458
579
|
`follow` links this machine to a seven on another machine through the tray hub (see [Tray hub signaling](#tray-hub-signaling-remote-setup)). It serves the link until interrupted (`SIGINT`, `SIGTERM`) and reconnects when the link drops. Status lines go to stderr, and `SLICC_DEBUG=1` adds diagnostics. The join URL is never printed.
|
|
@@ -463,8 +584,9 @@ slicc <join-url> follow [--exit] [--allow <rule>]... [--deny <rule>]... [--name
|
|
|
463
584
|
- `--name`: the link's `name` and `host` in HELLO, as for `attach`.
|
|
464
585
|
- `--runner`, `--shell`, `--eval`, `--eval-quiet`, `--sftp`, `--no-sftp`: compute over SSH (cap `ssh`), exactly as for `attach`. `direct-tcpip` forwarding goes through the remote policy, so it reaches public addresses only unless `--allow` says otherwise. `--runner` works with or without `--exit`.
|
|
465
586
|
- `--cdp <url>`: let seven drive this browser over the link (repeatable, in any form `slicc browsers --cdp` takes; each must answer when `follow` starts). See [`cdp`](#cdp-browser-control), and its warning: the seven on the other machine gets full control of every browser listed. No browser is `seven: true`. Without `--cdp`, `follow` doesn't offer `cdp`, and a `cdp` OPEN is refused with `EOPNOTSUPP`. Each browser's debugging port and its own endpoint addresses are [denied](#network-policy) to `tcp`, `http` and `ssh` forwarding.
|
|
587
|
+
- `--mount <path>[:<name>][:ro]`: share this folder with the seven on the other machine (cap `fs`, repeatable); see [Host folders](#host-folders). It can read the folder, and change it unless `:ro`.
|
|
466
588
|
|
|
467
|
-
HELLO from `follow`: `{ "v": 1, "role": "node", "name", "host", "version", "mode": "remote", "caps", "routes": { "prefixes": [], "exit" }, "policy" }`. `caps` holds `net` and `http` with `--exit`, `ssh` with `--runner` (plus `sshHostKey`), `cdp` (and HELLO `browsers`) with `--cdp`, and always `join`.
|
|
589
|
+
HELLO from `follow`: `{ "v": 1, "role": "node", "name", "host", "version", "mode": "remote", "caps", "routes": { "prefixes": [], "exit" }, "policy" }`. `caps` holds `net` and `http` with `--exit`, `ssh` with `--runner` (plus `sshHostKey`), `cdp` (and HELLO `browsers`) with `--cdp`, `fs` (and HELLO `mounts`) with `--mount`, and always `join`.
|
|
468
590
|
|
|
469
591
|
**Identity.** The device is its DTLS certificate. `follow` keeps one ECDSA P-256 certificate per hub origin (`scheme://host:port`), valid for 20 years, in `<config>/slicc/tray-certs.json` (mode `0600`). Every link to that hub uses it, so the fingerprint stays the same across reconnects, restarts and updates. The first run on a hub mints it and says `new device, fingerprint sha-256 …`; later runs say `device, fingerprint …`. Every dial attaches with a fresh `controllerId`, which is only a routing handle.
|
|
470
592
|
|
|
@@ -509,6 +631,8 @@ Front ends and capabilities follow a node's links with `node.Node.Subscribe(func
|
|
|
509
631
|
|
|
510
632
|
In the same process, probe ports and dirs passed in explicitly (`Options.ProbePorts`, `Options.Home`, `Options.ProfilesDir`) are still used. `test/integration`'s `TestMain` sets `SLICC_DISCOVERY=isolated` and points `HOME`, `APPDATA` and the XDG dirs (and `USERPROFILE` and `LOCALAPPDATA` except on Windows, where a Chrome started with temporary ones never writes `DevToolsActivePort`) at a temporary directory for itself and every `slicc` it starts, so no test can reach the browsers of the machine it runs on. Tests opt in to more with a fake home or fake endpoints. The Chrome the harness starts itself gets the real environment.
|
|
511
633
|
|
|
634
|
+
On macOS, a headless Chrome started with `HOME` pointed at a temporary dir may never load a page (seen on a developer Mac, not on CI). So on macOS the test that has `slicc` launch Chrome for seven runs it with the real `HOME` and a unique `--profile`, which it deletes afterwards.
|
|
635
|
+
|
|
512
636
|
The real-Chrome tests in `test/integration/` launch headless, so they run the executable directly. A windowed launch on macOS goes through `open -n -W -a`, which CI doesn't cover: on GitHub's macOS runners, Chrome started that way never writes `DevToolsActivePort`.
|
|
513
637
|
|
|
514
638
|
## Releases
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.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,11 +18,11 @@
|
|
|
18
18
|
"node": ">=18"
|
|
19
19
|
},
|
|
20
20
|
"optionalDependencies": {
|
|
21
|
-
"@ai-ecoverse/slicc-darwin-arm64": "1.
|
|
22
|
-
"@ai-ecoverse/slicc-darwin-amd64": "1.
|
|
23
|
-
"@ai-ecoverse/slicc-linux-arm64": "1.
|
|
24
|
-
"@ai-ecoverse/slicc-linux-amd64": "1.
|
|
25
|
-
"@ai-ecoverse/slicc-windows-arm64": "1.
|
|
26
|
-
"@ai-ecoverse/slicc-windows-amd64": "1.
|
|
21
|
+
"@ai-ecoverse/slicc-darwin-arm64": "1.23.0",
|
|
22
|
+
"@ai-ecoverse/slicc-darwin-amd64": "1.23.0",
|
|
23
|
+
"@ai-ecoverse/slicc-linux-arm64": "1.23.0",
|
|
24
|
+
"@ai-ecoverse/slicc-linux-amd64": "1.23.0",
|
|
25
|
+
"@ai-ecoverse/slicc-windows-arm64": "1.23.0",
|
|
26
|
+
"@ai-ecoverse/slicc-windows-amd64": "1.23.0"
|
|
27
27
|
}
|
|
28
28
|
}
|