@ai-ecoverse/slicc-kernel 1.22.0 → 1.22.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 +2 -1
- package/dist/kernel-worker.js +60 -22
- package/dist/kernel-worker.js.map +2 -2
- package/dist/node.js +62 -23
- package/dist/node.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,7 +118,7 @@ const { status, stdout } = await kernel.run(['bash', '-c', 'echo hi'], { cwd: '/
|
|
|
118
118
|
kernel.terminate();
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
`createNodeKernel({ root, modules, env, network, worker, processMounts })` takes the options of `createKernel` except `metadata
|
|
121
|
+
`createNodeKernel({ root, modules, env, network, worker, processMounts, fstabRetries })` takes the options of `createKernel` except `metadata`, and `fstabRetries`, the delays in ms between tries of a failing `/etc/fstab` line (default `[1000, 4000, 16000]`). `root` is an in-memory directory by default (`memoryRoot()` makes another), and POSIX metadata stays in memory. Processes and threads run on `worker_threads`. The kernel has `run`, `openTerminal` and `terminate` as above, plus `root`, `writeFile(path, data)` (creating the parent directories) and `readFile(path)` to put files in place and read results. `nodeTransport()` is `fetchTransport()` with Node's `fetch`, which no CORS binds (`crossOrigin: 'any'`). `connect()` and `attachKernel` work as in the browser: the port is a `worker_threads` `MessagePort`, which a worker thread can attach with, and `terminate()` ends every attached client.
|
|
122
122
|
|
|
123
123
|
## Commands
|
|
124
124
|
|
|
@@ -230,6 +230,7 @@ project /mnt/project hostfs ro 0 0
|
|
|
230
230
|
|
|
231
231
|
- **Boot never waits for it:** the lines are mounted in the background once the kernel has started, all at once, so a slow source holds up nothing.
|
|
232
232
|
- **Retries:** a line that fails is tried again after 1, 4 and 16 seconds (a driver package installed meanwhile, a mount point created later), except when it cannot succeed (`EINVAL`, `EBUSY`, `EPERM`, `EACCES`). `terminate()` stops the retries.
|
|
233
|
+
- **Failures are listed:** until it mounts, a line is in `kernel.mounts()` with state `pending`, and once it gives up with state `failed`, each with the last `error`. `/proc/mounts` and `mount` show the state in its options, as `none /mnt/bad nosuchfs rw,failed 0 0`. Unmounting a failed line's target takes it off the list. A line whose target another line mounted is not listed.
|
|
233
234
|
- **No user involvement:** an `fsa` line names a drive by its id: it comes up with the folder it had while its permission holds, and otherwise with no medium, asking the page for the folder through `onMountPending`, as any `fsa` mount does. With `media: false` the drive finds its folder only within the same kernel. With the default `media`, an `fsa` line reads its folder handle back from IndexedDB at boot, which crashes Chrome in an Incognito or other off-the-record profile ([#95](https://github.com/ai-ecoverse/slicc-kernel/issues/95)); a page cannot reliably tell it runs in one, so an embedder that cannot rule it out passes `media: false`, which keeps `fsa` lines in `/etc/fstab` safe. A `hostfs` line waits only for its first connection, and comes up with no medium if the proxy is not there yet.
|
|
234
235
|
|
|
235
236
|
### Removable media: `fsa`
|
package/dist/kernel-worker.js
CHANGED
|
@@ -4128,6 +4128,7 @@ var MountTable = class {
|
|
|
4128
4128
|
mounts = /* @__PURE__ */ new Map();
|
|
4129
4129
|
devices = 0;
|
|
4130
4130
|
pending = /* @__PURE__ */ new Set();
|
|
4131
|
+
notes = /* @__PURE__ */ new Map();
|
|
4131
4132
|
deps;
|
|
4132
4133
|
constructor(deps) {
|
|
4133
4134
|
this.deps = deps;
|
|
@@ -4136,7 +4137,7 @@ var MountTable = class {
|
|
|
4136
4137
|
return this.mounts.get(target);
|
|
4137
4138
|
}
|
|
4138
4139
|
list() {
|
|
4139
|
-
|
|
4140
|
+
const mounted = [...this.mounts.values()].map(
|
|
4140
4141
|
({ type, source, target, options, state, error, present }) => ({
|
|
4141
4142
|
type,
|
|
4142
4143
|
source,
|
|
@@ -4146,6 +4147,19 @@ var MountTable = class {
|
|
|
4146
4147
|
...error ? { error } : {}
|
|
4147
4148
|
})
|
|
4148
4149
|
);
|
|
4150
|
+
const noted = [...this.notes.values()].filter((note) => !this.mounts.has(note.target));
|
|
4151
|
+
return [...mounted, ...noted.map((note) => ({ ...note, options: { ...note.options } }))];
|
|
4152
|
+
}
|
|
4153
|
+
note(spec, state, error) {
|
|
4154
|
+
const target = normalizePath(spec.target);
|
|
4155
|
+
this.notes.set(target, {
|
|
4156
|
+
type: spec.type,
|
|
4157
|
+
source: spec.source,
|
|
4158
|
+
target,
|
|
4159
|
+
options: { ...spec.options },
|
|
4160
|
+
state,
|
|
4161
|
+
...error ? { error } : {}
|
|
4162
|
+
});
|
|
4149
4163
|
}
|
|
4150
4164
|
locate(path) {
|
|
4151
4165
|
let best;
|
|
@@ -4222,13 +4236,19 @@ var MountTable = class {
|
|
|
4222
4236
|
};
|
|
4223
4237
|
mounted = mount;
|
|
4224
4238
|
this.mounts.set(target, mount);
|
|
4239
|
+
this.notes.delete(target);
|
|
4225
4240
|
return this.list().find((m) => m.target === target);
|
|
4226
4241
|
}
|
|
4242
|
+
unnote(target) {
|
|
4243
|
+
this.notes.delete(normalizePath(target));
|
|
4244
|
+
}
|
|
4227
4245
|
umount(target, detach = false) {
|
|
4228
4246
|
const at = normalizePath(target);
|
|
4229
4247
|
const mount = this.mounts.get(at);
|
|
4248
|
+
if (!mount && this.notes.delete(at)) return;
|
|
4230
4249
|
if (!mount) throw errnoError("EINVAL", `${at} is not mounted`);
|
|
4231
4250
|
if (!detach && this.deps.busy(at)) throw errnoError("EBUSY", `${at} has open files`);
|
|
4251
|
+
this.notes.delete(at);
|
|
4232
4252
|
this.mounts.delete(at);
|
|
4233
4253
|
mount.conn.close();
|
|
4234
4254
|
mount.dispose();
|
|
@@ -7717,28 +7737,34 @@ function wait(ms, signal) {
|
|
|
7717
7737
|
});
|
|
7718
7738
|
}
|
|
7719
7739
|
async function mountFstab(lines, mount, signal, retries = FSTAB_RETRIES, discard = () => {
|
|
7740
|
+
}, settled = () => {
|
|
7720
7741
|
}) {
|
|
7721
7742
|
return Promise.all(
|
|
7722
|
-
lines.map(async (
|
|
7723
|
-
|
|
7724
|
-
|
|
7725
|
-
|
|
7726
|
-
const entry = await mount(spec);
|
|
7727
|
-
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
7728
|
-
discard(entry);
|
|
7729
|
-
failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
|
|
7730
|
-
break;
|
|
7731
|
-
} catch (err) {
|
|
7732
|
-
failed = err;
|
|
7733
|
-
if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
|
|
7734
|
-
await wait(retries[attempt], signal);
|
|
7735
|
-
}
|
|
7736
|
-
}
|
|
7737
|
-
const code = codeOf3(failed);
|
|
7738
|
-
return { line, target: spec.target, code, error: String(failed?.message ?? code) };
|
|
7743
|
+
lines.map(async (each) => {
|
|
7744
|
+
const result = await mountLine(each, mount, signal, retries, discard);
|
|
7745
|
+
settled(each, result);
|
|
7746
|
+
return result;
|
|
7739
7747
|
})
|
|
7740
7748
|
);
|
|
7741
7749
|
}
|
|
7750
|
+
async function mountLine({ line, spec, invalid }, mount, signal, retries, discard) {
|
|
7751
|
+
let failed = invalid;
|
|
7752
|
+
for (let attempt = 0; !invalid && attempt <= retries.length && !signal.aborted; attempt++) {
|
|
7753
|
+
try {
|
|
7754
|
+
const entry = await mount(spec);
|
|
7755
|
+
if (!signal.aborted) return { line, target: spec.target, entry };
|
|
7756
|
+
discard(entry);
|
|
7757
|
+
failed = Object.assign(new Error("the kernel stopped"), { code: "ECANCELED" });
|
|
7758
|
+
break;
|
|
7759
|
+
} catch (err) {
|
|
7760
|
+
failed = err;
|
|
7761
|
+
if (FINAL.has(codeOf3(err)) || attempt === retries.length) break;
|
|
7762
|
+
await wait(retries[attempt], signal);
|
|
7763
|
+
}
|
|
7764
|
+
}
|
|
7765
|
+
const code = codeOf3(failed);
|
|
7766
|
+
return { line, target: spec.target, code, error: String(failed?.message ?? code) };
|
|
7767
|
+
}
|
|
7742
7768
|
|
|
7743
7769
|
// src/mount/hostfs.ts
|
|
7744
7770
|
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
@@ -8541,6 +8567,7 @@ var Launcher = class {
|
|
|
8541
8567
|
onMountPending;
|
|
8542
8568
|
removable = /* @__PURE__ */ new Map();
|
|
8543
8569
|
hostfs;
|
|
8570
|
+
fstabRetries;
|
|
8544
8571
|
inserted = /* @__PURE__ */ new Map();
|
|
8545
8572
|
openFiles = /* @__PURE__ */ new Set();
|
|
8546
8573
|
held = /* @__PURE__ */ new Set();
|
|
@@ -8562,6 +8589,7 @@ var Launcher = class {
|
|
|
8562
8589
|
this.media = options.media ?? memoryMedia();
|
|
8563
8590
|
this.onMountPending = options.onMountPending;
|
|
8564
8591
|
this.hostfs = options;
|
|
8592
|
+
this.fstabRetries = options.fstabRetries ?? FSTAB_RETRIES;
|
|
8565
8593
|
this.processMounts = options.processMounts ?? true;
|
|
8566
8594
|
this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
|
|
8567
8595
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
@@ -8995,12 +9023,22 @@ var Launcher = class {
|
|
|
8995
9023
|
}
|
|
8996
9024
|
async mountFstab() {
|
|
8997
9025
|
const text = await this.base.readFile(FSTAB_PATH).catch(() => "");
|
|
9026
|
+
const lines = parseFstab(text);
|
|
9027
|
+
for (const { spec } of lines) this.mounts.note(spec, "pending");
|
|
8998
9028
|
return mountFstab(
|
|
8999
|
-
|
|
9000
|
-
(spec) => this.mount(spec)
|
|
9029
|
+
lines,
|
|
9030
|
+
(spec) => this.mount(spec).catch((err) => {
|
|
9031
|
+
this.mounts.note(spec, "pending", err?.message ?? String(err));
|
|
9032
|
+
throw err;
|
|
9033
|
+
}),
|
|
9001
9034
|
this.booting.signal,
|
|
9002
|
-
|
|
9003
|
-
(entry) => this.umount(entry.target, true)
|
|
9035
|
+
this.fstabRetries,
|
|
9036
|
+
(entry) => this.umount(entry.target, true),
|
|
9037
|
+
({ spec }, result) => {
|
|
9038
|
+
if (!result.code || result.code === "ECANCELED" || this.booting.signal.aborted) {
|
|
9039
|
+
this.mounts.unnote(spec.target);
|
|
9040
|
+
} else this.mounts.note(spec, "failed", result.error);
|
|
9041
|
+
}
|
|
9004
9042
|
);
|
|
9005
9043
|
}
|
|
9006
9044
|
environment(cwd, extra) {
|