@ai-ecoverse/slicc-kernel 1.178.3 → 1.179.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 +4 -3
- package/dist/driver-worker.js +5 -1
- package/dist/driver-worker.js.map +2 -2
- package/dist/index.js +5 -1
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +34 -18
- package/dist/kernel-worker.js.map +2 -2
- package/dist/node-driver-worker.js +5 -1
- package/dist/node-driver-worker.js.map +2 -2
- package/dist/node-process-worker.cjs +1 -1
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +38 -18
- package/dist/node.js.map +2 -2
- package/dist/process-worker.js +1 -1
- package/dist/types/client/attach.d.ts +3 -0
- package/dist/types/client/protocol.d.ts +2 -0
- package/dist/types/fs/dac.d.ts +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/launcher.d.ts +4 -0
- package/dist/types/node.d.ts +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -57,6 +57,7 @@ Runs `argv[0]` with `argv` as its arguments and resolves when it exits. `stdout`
|
|
|
57
57
|
| option | |
|
|
58
58
|
| --- | --- |
|
|
59
59
|
| `cwd` | working directory, default `/`; created in OPFS if missing. Under `users: 'enforce'`, a `cwd` the user can't search rejects with `code: 'EACCES'` (`cannot change to <cwd>: Permission denied`) and starts nothing, as a `chdir` there fails on Linux; `openTerminal` does the same. A program's spawn with its own `cwd` (`ctx.spawn`, `ctx.sync.run`, a WASIX `posix_spawn` chdir action) is checked the same way and fails with `EACCES`, while a child that inherits its parent's cwd keeps it unchecked, as on Linux |
|
|
60
|
+
| `cwdMissing` | `'create'` (the default) or `'error'`: what happens when `cwd` doesn't exist. `'create'` creates it, with its parents, as described for `cwd`. `'error'` rejects with `code: 'ENOENT'` (`cannot change to <cwd>: No such file or directory`) and starts nothing, as a `chdir` there fails on Linux. A caller that keeps directories in a list to run commands in, and so may name one that was removed since, should pass `'error'`: with `'create'`, its next command brings the removed directory back, empty ([#1808](https://github.com/ai-ecoverse/slicc-kernel/issues/1808)). Any other value rejects with a `TypeError`; `openTerminal` takes it too |
|
|
60
61
|
| `user` | the user to run as, by name or uid (see [Users](#users)); default `root`. An unknown user rejects with `code: 'ENOENT'` |
|
|
61
62
|
| `tag` | a string the kernel keeps with the process and every process it starts (fork, spawn and exec), and hands to `approve` with a [`sudo`](#sudo) request, so the embedder knows which agent asks; nothing a program does changes it. Anything but a string rejects with a `TypeError` |
|
|
62
63
|
| `env` | extra environment, over the user's defaults: `HOME`, `USER`, `LOGNAME`, `PNPM_HOME` and `PATH=$PNPM_HOME/bin:/usr/local/bin:/usr/bin:/bin` (see [Users](#users)), `TZ=<system time zone>` (see [Time zone](#time-zone)), and `PWD=<cwd>` |
|
|
@@ -69,7 +70,7 @@ Starts `argv` as the session leader on a new terminal and resolves once it is ru
|
|
|
69
70
|
|
|
70
71
|
| option | |
|
|
71
72
|
| --- | --- |
|
|
72
|
-
| `cwd`, `user`, `tag`, `env` | as for `run`; `TERM=xterm-256color` and `COLORTERM=truecolor` are added |
|
|
73
|
+
| `cwd`, `cwdMissing`, `user`, `tag`, `env` | as for `run`; `TERM=xterm-256color` and `COLORTERM=truecolor` are added |
|
|
73
74
|
| `cols`, `rows` | initial size, default 80 × 24, so `$COLUMNS` and `$LINES` are right before the first prompt |
|
|
74
75
|
| `onData` | called with each chunk of output as raw bytes (escape sequences included) |
|
|
75
76
|
|
|
@@ -176,7 +177,7 @@ Attaches to a kernel over a port from `kernel.connect()`, in any realm: a worker
|
|
|
176
177
|
|
|
177
178
|
| Client | |
|
|
178
179
|
|---|---|
|
|
179
|
-
| `spawn(argv, { cwd, user, env, stdin, pgid, onStdout, onStderr })` | runs over pipes and resolves with `{ pid, pgid, exited, ended, signal(name) }` once the process has started; `ended` resolves with `{ status, signal }`, `signal` named only when a signal ended the process, as for `kernel.run` (an older kernel never names one); output arrives in chunks, as bytes, and the kernel keeps no copy; every spawn leads its own process group, which `signal` (default `SIGTERM`) signals, unless `pgid` names an existing group to join (protocol 1.3): the process then joins that group and that group's session, as `setpgid` would (a client has no session of its own: each of its spawns and terminals leads one, and `setpgid` cannot cross sessions), and `signal` signals the whole group. The group must have been started by this client (a spawn or a terminal it opened), even if its leader has exited; an unknown group rejects with `code: 'ESRCH'`, another session's with `code: 'EPERM'`, and against a 1.2 kernel the client rejects with `code: 'ENOSYS'` before sending anything, as an older kernel would ignore the option; a program that cannot start rejects with `code: 'ENOENT'`. `user` runs it as another user, which only a root client may (see `kernel.connect`); against a 1.7 kernel the client rejects with `code: 'ENOSYS'` |
|
|
180
|
+
| `spawn(argv, { cwd, cwdMissing, user, env, stdin, pgid, onStdout, onStderr })` | runs over pipes and resolves with `{ pid, pgid, exited, ended, signal(name) }` once the process has started; `ended` resolves with `{ status, signal }`, `signal` named only when a signal ended the process, as for `kernel.run` (an older kernel never names one); output arrives in chunks, as bytes, and the kernel keeps no copy; every spawn leads its own process group, which `signal` (default `SIGTERM`) signals, unless `pgid` names an existing group to join (protocol 1.3): the process then joins that group and that group's session, as `setpgid` would (a client has no session of its own: each of its spawns and terminals leads one, and `setpgid` cannot cross sessions), and `signal` signals the whole group. The group must have been started by this client (a spawn or a terminal it opened), even if its leader has exited; an unknown group rejects with `code: 'ESRCH'`, another session's with `code: 'EPERM'`, and against a 1.2 kernel the client rejects with `code: 'ENOSYS'` before sending anything, as an older kernel would ignore the option; a program that cannot start rejects with `code: 'ENOENT'`. `user` runs it as another user, which only a root client may (see `kernel.connect`); against a 1.7 kernel the client rejects with `code: 'ENOSYS'`. `cwdMissing` is as for `kernel.run` (protocol 1.14); `run` and `openTerminal` take it too, and with `'error'` against a 1.13 kernel, which would create the directory, the client rejects with `code: 'ENOSYS'` before sending anything |
|
|
180
181
|
| `run(argv, options?)` | `spawn` and wait: `{ pid, status, signal, stdout, stderr }` as text, with `onStdout`/`onStderr` streaming text; `signal` as for `kernel.run` |
|
|
181
182
|
| `openTerminal(argv, options?)` | a pty session, as `kernel.openTerminal`, `ended` included |
|
|
182
183
|
| `ps()` | the kernel's processes: `{ pid, ppid, pgid, sid, argv, tty, started, state, rss, utime, stime, uid, gid }`, `state` being `'S'`, `'T'` (stopped) or `'Z'`, `rss` the process's resident memory in bytes (the size of its wasm memory, what `/proc/<pid>/status` shows as `VmRSS`; absent from kernels before 1.61), `utime`/`stime` the CPU time it has used, in milliseconds, in its program and in the kernel's calls (see [`/proc`](#proc); absent for a JS program and from kernels before 1.63) and `uid`/`gid` the effective ids (absent from a 1.7 kernel), and `tag` the process's tag (see `kernel.run`), when it has one, with `tags`, all of them in order, when a spawn added one (see `kernel.connect`), on rows the client's user may see as root or as the same uid. A client bound to a user sees only that user's processes |
|
|
@@ -1067,7 +1068,7 @@ With `users: 'enforce'`, the default, or `'report'`, files have owners, and [Per
|
|
|
1067
1068
|
- **Homes:** every `kernel.users.add` gives the user's home (only a real directory at `/home/<dir>`, not a symlink or a path through one) and everything in it without an owner of its own to the user, and sets the home's mode: `0700`, `0750` for a `scoop-` user, `2750` with `homeGroup`, and its `~/.local/share/pnpm`'s to `0700` (`0750` for a `scoop-` user). The embedder calls `add` for each user at boot, as it already does, so homes made before owners were on become the user's. That also means a user's own `chmod` or `chgrp` of its home lasts only until the next `add`, and `add` must get the same `homeGroup` every time.
|
|
1068
1069
|
- **`chown`, `lchown`, `fchown`:** Emscripten programs, and WASI programs through `slicc_fs` (see [File modes and the umask](#file-modes-and-the-umask)), change owners through the kernel, by the Linux rules: root may set any owner and group; a file's owner may set its group to one it belongs to, or leave it as it is; anything else fails with `EPERM`. `-1` keeps an id. A `chown` that sets an owner or group of a file clears its setuid bit, and its setgid bit if the file is group-executable, whoever calls it, root included, as on Linux. With `users: 'off'`, `chown` succeeds and changes nothing, as before.
|
|
1069
1070
|
- **What programs see:** `stat` shows the owners to Emscripten programs, whatever their libc reports for their own ids, so a program built before H1 sees real owners too, and its owner checks can fail. WASI programs read them with `slicc_fs.fd_owner` and `path_owner`. The commands in `/usr/bin` and `/bin` are root's, and a user's own commands in `~/.local/share/pnpm/bin` belong to the owner of the closest real directory above them, normally the user.
|
|
1070
|
-
- **Mounts:** the options `uid=` and `gid=` (decimal ids) give a mount its owner; without them it belongs to whoever mounted it: root for `kernel.mount`, `/etc/fstab` and a client, the process's effective ids for `mount(2)`, which then shows them in its options (a process that isn't root gets its own ids whatever `uid=` and `gid=` it passes, so it can't make a mount look root's). A mount whose driver keeps owners (`tmpfs`) starts with its root owned so, and its files get owners as on the root file system, `chown` included. On `hostfs`, `fsa` and any driver without `owners`, every file shows as the mount's owner and `chown` fails with `EPERM`, root's too, as on a FAT drive. An embedder that mounts a folder for a user (bios's linked and picked folders) passes that user's `uid` and `gid`, or the user's programs see root's files there, and git calls the repository's ownership dubious. Those mounts also take the FAT-style options `umask=`, `fmask=` and `dmask=` (octal, as on Linux `vfat`): when any is set, the kernel synthesises the permission bits that `stat` and DAC see from `0666` for files and `0777` for directories (and symlinks), less the mask (`fmask` for files, `dmask` for directories, and `umask` as the default for both). Without them, files stay `0644` and directories `0755` as before, so only the mount's `uid` may write. `umask=000` (or `fmask=000,dmask=000`) makes every file and directory world-writable under DAC, which is how a personal cone (another uid) can write a folder mounted as the template `cone` user. When a mask is set on a mount without a `chmod` capability (`fsa`), `chmod` is a no-op and is not owner-only, so a create that then sets the mode from another uid still succeeds. Without a mask, and on a mount that keeps real modes through `chmod` (`hostfs`), `chmod` still requires the mount's `uid` (or root). Drivers with `owners` (`tmpfs`) ignore the mask options: their modes stay real, and chmod still
|
|
1071
|
+
- **Mounts:** the options `uid=` and `gid=` (decimal ids) give a mount its owner; without them it belongs to whoever mounted it: root for `kernel.mount`, `/etc/fstab` and a client, the process's effective ids for `mount(2)`, which then shows them in its options (a process that isn't root gets its own ids whatever `uid=` and `gid=` it passes, so it can't make a mount look root's). A mount whose driver keeps owners (`tmpfs`) starts with its root owned so, and its files get owners as on the root file system, `chown` included. On `hostfs`, `fsa` and any driver without `owners`, every file shows as the mount's owner and `chown` fails with `EPERM`, root's too, as on a FAT drive. An embedder that mounts a folder for a user (bios's linked and picked folders) passes that user's `uid` and `gid`, or the user's programs see root's files there, and git calls the repository's ownership dubious. Those mounts also take the FAT-style options `umask=`, `fmask=` and `dmask=` (octal, as on Linux `vfat`): when any is set, the kernel synthesises the permission bits that `stat` and DAC see from `0666` for files and `0777` for directories (and symlinks), less the mask (`fmask` for files, `dmask` for directories, and `umask` as the default for both). Without them, files stay `0644` and directories `0755` as before, so only the mount's `uid` may write. `umask=000` (or `fmask=000,dmask=000`) makes every file and directory world-writable under DAC, which is how a personal cone (another uid) can write a folder mounted as the template `cone` user. When a mask is set on a mount without a `chmod` capability (`fsa`), `chmod` is a no-op and is not owner-only, so a create that then sets the mode from another uid still succeeds, and a non-owner who can write the file may set its times to an explicit value (`utimes` / `cp -p` / `tar x`), as on Linux `vfat` with a uid/umask mount; "now" times already needed only write access. Without a mask, and on a mount that keeps real modes through `chmod` (`hostfs`), `chmod` still requires the mount's `uid` (or root), and explicit times stay owner-only. Drivers with `owners` (`tmpfs`) ignore the mask options: their modes stay real, and chmod and explicit times still require the owner.
|
|
1071
1072
|
|
|
1072
1073
|
### Permissions
|
|
1073
1074
|
|
package/dist/driver-worker.js
CHANGED
|
@@ -824,7 +824,7 @@ async function loopbackFetch(dial, input, options) {
|
|
|
824
824
|
}
|
|
825
825
|
|
|
826
826
|
// src/client/protocol.ts
|
|
827
|
-
var PROTOCOL = [1,
|
|
827
|
+
var PROTOCOL = [1, 14];
|
|
828
828
|
function locksOf(scope = globalThis) {
|
|
829
829
|
const locks = scope.navigator?.locks;
|
|
830
830
|
return typeof locks?.request === "function" ? locks : void 0;
|
|
@@ -1059,6 +1059,8 @@ async function spawnOn(channel, argv, opts) {
|
|
|
1059
1059
|
});
|
|
1060
1060
|
if (opts.tag !== void 0) await since(channel, 12, "a tag per spawn", async () => {
|
|
1061
1061
|
});
|
|
1062
|
+
if (opts.cwdMissing === "error") await since(channel, 14, "cwdMissing: 'error'", async () => {
|
|
1063
|
+
});
|
|
1062
1064
|
const started = Promise.withResolvers();
|
|
1063
1065
|
let endedBy;
|
|
1064
1066
|
const options = { ...rest, ...stdin !== void 0 ? { stdin: bytesOf2(stdin) } : {} };
|
|
@@ -1118,6 +1120,8 @@ async function terminalOn(channel, argv, opts) {
|
|
|
1118
1120
|
});
|
|
1119
1121
|
if (opts.tag !== void 0) await since(channel, 12, "a tag per terminal", async () => {
|
|
1120
1122
|
});
|
|
1123
|
+
if (opts.cwdMissing === "error") await since(channel, 14, "cwdMissing: 'error'", async () => {
|
|
1124
|
+
});
|
|
1121
1125
|
let listener = onData ?? null;
|
|
1122
1126
|
const backlog = [];
|
|
1123
1127
|
const started = Promise.withResolvers();
|