@ai-ecoverse/slicc-kernel 1.6.0 → 1.6.1
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 -4
- package/dist/kernel-worker.js +17 -1
- package/dist/kernel-worker.js.map +4 -4
- package/dist/process-worker.js +81 -5
- package/dist/process-worker.js.map +3 -3
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -14,8 +14,8 @@ const { status, stdout, stderr } = await kernel.run(['bash', '-c', 'echo hi > he
|
|
|
14
14
|
## Requirements
|
|
15
15
|
|
|
16
16
|
- **The page must be cross-origin isolated**: serve it with `Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: require-corp` (or `credentialless`). Processes block on `Atomics.wait` over a `SharedArrayBuffer`, which only exists in isolated contexts. `createKernel` throws if `crossOriginIsolated` is false.
|
|
17
|
-
- **The kernel lives in a dedicated worker owned by the page.** A `SharedWorker` is never cross-origin isolated in Chromium (and has no `Worker` constructor), so it cannot host the kernel. `createKernel` starts `dist/kernel-worker.js`; that worker owns OPFS and starts one nested dedicated worker (`dist/process-worker.js`) per process.
|
|
18
|
-
- The page's CSP must allow `eval
|
|
17
|
+
- **The kernel lives in a dedicated worker owned by the page.** A `SharedWorker` is never cross-origin isolated in Chromium (and has no `Worker` constructor), so it cannot host the kernel. `createKernel` starts `dist/kernel-worker.js`; that worker owns OPFS and starts one nested dedicated worker (`dist/process-worker.js`) per process. The kernel worker reads `process-worker.js` once when it starts and runs every process and thread from that copy (a `blob:` URL), so updating the package in place while a kernel runs does not mix versions: the next page load switches both.
|
|
18
|
+
- The page's CSP must allow `eval`, since each process evaluates its program's Emscripten glue with `new Function`, and `blob:` workers (`worker-src 'self' blob:`, or the `script-src` it falls back to), since processes run from the kernel's pinned copy of `process-worker.js`.
|
|
19
19
|
|
|
20
20
|
`dist/` is plain ESM with no bare imports, and the workers are referenced with `new URL('./….js', import.meta.url)`, so the files work when served as-is (for example from OPFS through a service worker). Whatever serves them must send the COOP/COEP headers on the page and a compatible `Cross-Origin-Resource-Policy` on the scripts.
|
|
21
21
|
|
|
@@ -137,7 +137,7 @@ The proxy answers `200` with `Content-Type: application/vnd.slicc.raw-fetch`: a
|
|
|
137
137
|
|
|
138
138
|
## Filesystem
|
|
139
139
|
|
|
140
|
-
`/` is the OPFS root. Each process mounts the top-level directories that exist when it starts (plus `/usr` and `/bin`), so files in them are shared by all processes and visible through the OPFS API as soon as the process that wrote them has closed them; `run` resolves only after that. `createKernel` creates `/tmp` and `/home` in OPFS, so they are shared too.
|
|
140
|
+
`/` is the OPFS root. Each process mounts the top-level directories that exist when it starts (plus `/usr` and `/bin`), so files in them are shared by all processes and visible through the OPFS API as soon as the process that wrote them has closed them; `run` resolves only after that. `createKernel` creates `/tmp` and `/home` in OPFS, so they are shared too. Files directly in `/`, and directories created there after a process started, are OPFS as well, the same for Emscripten and WASI programs; only `/dev` and `/proc` live in each process's memory. File contents are buffered per open file and written back on close, `fsync` and exit; metadata operations (`mkdir`, `rename`, `rm`, …) go straight to OPFS. The kernel worker is the only writer.
|
|
141
141
|
|
|
142
142
|
Directories are renamed with `FileSystemHandle.move()` where available, else by copy and delete.
|
|
143
143
|
|
|
@@ -167,7 +167,7 @@ The filesystem is our own rather than [ZenFS](https://github.com/zen-fs/core) (w
|
|
|
167
167
|
The kernel is ported from SLICC's `packages/webapp/src/kernel/` with the browser-specific VFS replaced by an OPFS one:
|
|
168
168
|
|
|
169
169
|
- `src/kernel/`: the kernel side, per process (`host.ts` starts the worker and answers its syscalls in `process.ts`) and shared tables (descriptors, pipes, children, jobs, signals, terminals and pseudo-terminals, `select`).
|
|
170
|
-
- `src/process/`: the runtime inside each process worker. It evaluates the Emscripten glue, mounts the live VFS, routes descriptors through the kernel and implements `fork` by copying the whole linear memory into a new worker (Asyncify). `src/process/wasi/` runs WASI and WASIX programs.
|
|
170
|
+
- `src/process/`: the runtime inside each process worker. It evaluates the Emscripten glue, mounts the live VFS, routes descriptors through the kernel and implements `fork` by copying the whole linear memory into a new worker (Asyncify). `fork` is the only call allowed to suspend through Asyncify: an `fsync` that Emscripten made asynchronous is answered synchronously by the file's own stream instead. `src/process/wasi/` runs WASI and WASIX programs.
|
|
171
171
|
- `src/realm/`: the synchronous bridge (`SharedArrayBuffer` + `Atomics.wait`) and the live Emscripten filesystem on top of it.
|
|
172
172
|
- `src/fs/`: the OPFS filesystem and the virtual command directories.
|
|
173
173
|
- `src/launcher.ts`, `src/commands.ts`, `src/serve.ts`, `src/index.ts`: command resolution, the kernel worker protocol and the page API. An embedder that uses `Launcher` directly, without `createKernel`, calls `await launcher.prepare()` first: it creates `/tmp` and `/home` and writes the CA certificate.
|
package/dist/kernel-worker.js
CHANGED
|
@@ -177,6 +177,22 @@ function openMeta(name2, factory) {
|
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
// src/pin.ts
|
|
181
|
+
function pinScript(url) {
|
|
182
|
+
try {
|
|
183
|
+
const request2 = new XMLHttpRequest();
|
|
184
|
+
request2.open("GET", url.href, false);
|
|
185
|
+
request2.send();
|
|
186
|
+
if (request2.status !== 200) return url.href;
|
|
187
|
+
const source = `${request2.responseText}
|
|
188
|
+
//# sourceURL=${url.href}
|
|
189
|
+
`;
|
|
190
|
+
return URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
|
|
191
|
+
} catch {
|
|
192
|
+
return url.href;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
180
196
|
// src/fs/types.ts
|
|
181
197
|
function fsError(code, path) {
|
|
182
198
|
return Object.assign(new Error(`${code}: ${path}`), { code });
|
|
@@ -5978,7 +5994,7 @@ function serveKernel(port, deps) {
|
|
|
5978
5994
|
}
|
|
5979
5995
|
|
|
5980
5996
|
// src/kernel-worker.ts
|
|
5981
|
-
var processWorker = new URL("./process-worker.js", import.meta.url);
|
|
5997
|
+
var processWorker = pinScript(new URL("./process-worker.js", import.meta.url));
|
|
5982
5998
|
serveKernel(globalThis, {
|
|
5983
5999
|
storage: () => navigator.storage.getDirectory(),
|
|
5984
6000
|
createWorker: () => new Worker(processWorker, { type: "module" }),
|