@ai-ecoverse/slicc-kernel 1.143.0 → 1.143.2
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 -2
- package/dist/kernel-worker.js +63 -16
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +63 -16
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +1 -1
- package/dist/types/fs/watch.d.ts +13 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -375,7 +375,7 @@ The module's default export (or its `main` export) is called with a context and
|
|
|
375
375
|
- `'change'`: an existing entry's data or metadata changed (a write, `truncate`, `chmod`, `chown`, `utimes`). A write that creates a file gives `'rename'` and then `'change'`, as on Linux.
|
|
376
376
|
- `filename` is the entry's name relative to the watched directory (`sub/a.txt` when recursive), or the file's basename for a file watch.
|
|
377
377
|
- **Overflow:** a watch holds 1,000 events between reads. Beyond that, the rest are dropped, and `{ overflow: true }` follows the ones kept.
|
|
378
|
-
- **The watched path itself:**
|
|
378
|
+
- **The watched path itself:** like inotify, a watch follows the file or directory it was opened on, not its name. When a directory above it is renamed, the watch stays on the moved path with no event, and later events keep the name it was opened with. When the path itself is renamed, the watch gets one `{ type: 'rename', filename, moved: true }` and stays on the new path. Only a removal ends it: `rm` of the path or a directory above it, or a rename of something else over it gives one `'rename'` with its own name, then `{ ended: true }`. Renames are followed as this kernel makes them ([#1356](https://github.com/ai-ecoverse/slicc-kernel/issues/1356)). `close()` is safe at any time and idempotent, and nothing is delivered after it returns.
|
|
379
379
|
- **Errors at watch time:** a path the program may not read (under `users`) is `EACCES`, and one that doesn't exist is `ENOENT`. Permission is checked first, so a path under a directory the program can't search is `EACCES` whether or not it exists. A process holds at most 8,192 watches; past that, `ENOSPC`.
|
|
380
380
|
- **Permissions per event:** under `users`, a program only hears about paths it may reach. An event about `P` below the watched directory is delivered only if the program's user can search every directory down to `P`'s parent and read that parent, as Node's recursive watch would need in order to add a watch there. The check runs when the program takes the events, so another user's `0700` directory stays silent until it's opened up. A consequence is that events inside a directory that is gone by the time the program takes them are dropped: after `rm -r sub` the program sees `rename sub` but not `sub/a`. A watcher should rescan on a directory's `rename`, as Node users do anyway.
|
|
381
381
|
- **Who counts as a writer:** every process in the kernel, the embedder (`kernel.writeFile` and the like) and the mounts that report changes. A `hostfs` change from the host comes as `'change'`, since the host doesn't say which kind it was.
|
|
@@ -1292,7 +1292,7 @@ The tools in `tools/cert-review/`:
|
|
|
1292
1292
|
- and record, which re-hashes the candidate's artifact against the pack job's sha256.
|
|
1293
1293
|
- **The verdict** goes into the commit status on the pull request's head and a comment on the pull request. Certified is green. A FAIL is red with the line, and the pull request needs a fix. **INCOMPLETE** (a job that didn't succeed, no verdict, a suite without results) is red with `re-run the failed jobs`, never a silent pass. Main moving on after a review doesn't trigger another one; a push does.
|
|
1294
1294
|
- **Merge groups only look up.** On `merge_group`, the job lists the group's pull requests (its merge commits) and requires, for each one, a green status on the head being merged, or no behaviour change. Otherwise it's red, naming the pull request. Then it adds the group's downstream verdict. The group's `ci / test` covers the combined tree.
|
|
1295
|
-
- **Downstream, once per merge group:** the `merge group pack` job packs the group's tree with `main`'s tools (`x.y.z-group.<run>`), and `downstream bios canary` calls slicc-bios's `canary.yml` at a pinned full sha (`
|
|
1295
|
+
- **Downstream, once per merge group:** the `merge group pack` job packs the group's tree with `main`'s tools (`x.y.z-group.<run>`), and `downstream bios canary` calls slicc-bios's `canary.yml` at a pinned full sha (`42cdf3a8…`, never a tag) with that tarball, bound by artifact id, digest and sha256, on the `sweep`, `guard-prompt`, `guard-lagging`, `k4-upgrade`, `k4-agent-modes` and `link-serve` shards, each run once, on the runner the `canary runner` job picks: `.github/runner.mjs`'s `decide()` from `main`, as `ci.yml` uses it, gives `vars.CERT_CANARY_RUNNER` only to a merge group whose entries are all same-repo PRs, and `ubuntu-latest` otherwise, without the variable, or if the decider fails (bios honours a runner only for merge groups and pushes to main in ai-ecoverse, so rolling back is unsetting the variable). The verdict line names the shards, or their count past 32 characters (`6 shards`), so a status description (140 characters) keeps the reason. `downstream.mjs` judges its result (`downstreamOf` in `lib.mjs`), and the lookup requires it. The `merge group` job reads that result as `result.json` from the canary's own artifact, downloaded by the call's `artifact-id` with `digest-mismatch: error`, not from the `result` output: that output can be up to 512 KB, and one environment variable holds 128 KB, so passing it that way stopped the step from starting at all (`Argument list too long`, runs 39166647175 and 39168823799). A missing or unreadable artifact leaves no result, so the group is INCOMPLETE:
|
|
1296
1296
|
- **PASS:** the candidate passes, `downstream: PASS bios … N pass`;
|
|
1297
1297
|
- **FAIL:** a regression, or a failing test (by the counts, even without its rows) is **not certified**, and the group is red;
|
|
1298
1298
|
- **INCOMPLETE** blocks too: no result, a result from another bios than the pinned one, a missing shard, a shard that exited without a failing test, a refused candidate, 0 tests run, or no test passed;
|
package/dist/kernel-worker.js
CHANGED
|
@@ -1279,6 +1279,7 @@ var escapeRoot = (name2) => name2.replace(/[%:]/g, (c) => `%${c.charCodeAt(0).to
|
|
|
1279
1279
|
var LEGACY_RENAME_LOCK = /^slicc-rename:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
1280
1280
|
var PAIRED = /* @__PURE__ */ new Set(["rename", "link", "copyFile"]);
|
|
1281
1281
|
var LISTINGS = /* @__PURE__ */ new Set(["readdir", "readdirStat"]);
|
|
1282
|
+
var REMOVALS = /* @__PURE__ */ new Set(["rm", "rename"]);
|
|
1282
1283
|
var RETRY = /* @__PURE__ */ Symbol("retry");
|
|
1283
1284
|
var ACK_SAMPLES = 4096;
|
|
1284
1285
|
var INTENT_POLL_MS = 5;
|
|
@@ -2757,10 +2758,11 @@ var OpfsFs = class {
|
|
|
2757
2758
|
const running = [...this.inflight];
|
|
2758
2759
|
const reached = await Promise.all(
|
|
2759
2760
|
running.map(async (op) => {
|
|
2760
|
-
|
|
2761
|
+
const near = (at) => touches(at, trees, true, op.removing === true);
|
|
2762
|
+
if (op.paths.some(near)) return true;
|
|
2761
2763
|
for (const at of op.paths) {
|
|
2762
2764
|
const forms = await this.resolvedForms(at);
|
|
2763
|
-
if (forms.some(
|
|
2765
|
+
if (forms.some(near)) return true;
|
|
2764
2766
|
}
|
|
2765
2767
|
return false;
|
|
2766
2768
|
})
|
|
@@ -2777,10 +2779,10 @@ var OpfsFs = class {
|
|
|
2777
2779
|
})
|
|
2778
2780
|
);
|
|
2779
2781
|
}
|
|
2780
|
-
peerAt(at, listing) {
|
|
2782
|
+
peerAt(at, listing, removing = false) {
|
|
2781
2783
|
for (const move of this.peerMoves.values()) {
|
|
2782
2784
|
if (this.ownLocks.has(move.lock)) continue;
|
|
2783
|
-
if (touches(at, move.trees, listing)) return move;
|
|
2785
|
+
if (touches(at, move.trees, listing, removing)) return move;
|
|
2784
2786
|
}
|
|
2785
2787
|
return void 0;
|
|
2786
2788
|
}
|
|
@@ -2815,11 +2817,11 @@ var OpfsFs = class {
|
|
|
2815
2817
|
this.peerWaits.set(move.lock, wait2);
|
|
2816
2818
|
return wait2;
|
|
2817
2819
|
}
|
|
2818
|
-
async peerSettled(at, listing) {
|
|
2820
|
+
async peerSettled(at, listing, removing) {
|
|
2819
2821
|
await this.lookForRenames();
|
|
2820
2822
|
for (let reals; ; ) {
|
|
2821
2823
|
const forms = [at, ...reals ?? []];
|
|
2822
|
-
const move = forms.map((form) => this.peerAt(form, listing)).find(Boolean);
|
|
2824
|
+
const move = forms.map((form) => this.peerAt(form, listing, removing)).find(Boolean);
|
|
2823
2825
|
if (move) {
|
|
2824
2826
|
this.peerWaited++;
|
|
2825
2827
|
await this.peerMoved(move);
|
|
@@ -2857,13 +2859,14 @@ var OpfsFs = class {
|
|
|
2857
2859
|
async peerOp(name2, args, inner) {
|
|
2858
2860
|
const paths = (name2 === "symlink" ? args.slice(1, 2) : PAIRED.has(name2) ? args.slice(0, 2) : args.slice(0, 1)).filter((path) => typeof path === "string").map(normalizePath);
|
|
2859
2861
|
const listing = LISTINGS.has(name2);
|
|
2862
|
+
const removing = REMOVALS.has(name2);
|
|
2860
2863
|
const checked = [...paths];
|
|
2861
2864
|
while (this.amongPeers()) {
|
|
2862
2865
|
checked.length = 0;
|
|
2863
|
-
for (const at of paths) checked.push(...await this.peerSettled(at, listing));
|
|
2864
|
-
if (!checked.some((at) => this.peerAt(at, listing))) break;
|
|
2866
|
+
for (const at of paths) checked.push(...await this.peerSettled(at, listing, removing));
|
|
2867
|
+
if (!checked.some((at) => this.peerAt(at, listing, removing))) break;
|
|
2865
2868
|
}
|
|
2866
|
-
const busy = { paths: checked, release: () => void 0 };
|
|
2869
|
+
const busy = { paths: checked, removing, release: () => void 0 };
|
|
2867
2870
|
const done5 = () => {
|
|
2868
2871
|
this.inflight.delete(busy);
|
|
2869
2872
|
busy.waiting?.resolve();
|
|
@@ -3288,9 +3291,9 @@ var OpfsFs = class {
|
|
|
3288
3291
|
function nothing() {
|
|
3289
3292
|
return void 0;
|
|
3290
3293
|
}
|
|
3291
|
-
function touches(at, trees, listing = true) {
|
|
3294
|
+
function touches(at, trees, listing = true, removing = false) {
|
|
3292
3295
|
return trees.some(
|
|
3293
|
-
([from, to]) => within2(at, from) || within2(at, to) || listing && (at === parentOf3(from) || at === parentOf3(to))
|
|
3296
|
+
([from, to]) => within2(at, from) || within2(at, to) || listing && (at === parentOf3(from) || at === parentOf3(to)) || removing && (at === "/" || within2(from, at) || within2(to, at))
|
|
3294
3297
|
);
|
|
3295
3298
|
}
|
|
3296
3299
|
function byIntent(a, b) {
|
|
@@ -7595,7 +7598,7 @@ function satisfies(version, range2) {
|
|
|
7595
7598
|
}
|
|
7596
7599
|
|
|
7597
7600
|
// src/version.ts
|
|
7598
|
-
var KERNEL_VERSION = true ? "1.143.
|
|
7601
|
+
var KERNEL_VERSION = true ? "1.143.2" : "0.0.0-dev";
|
|
7599
7602
|
var OS_RELEASE = "6.0.0-slicc";
|
|
7600
7603
|
function procVersion() {
|
|
7601
7604
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -10506,7 +10509,7 @@ function route(watch, type, path) {
|
|
|
10506
10509
|
}
|
|
10507
10510
|
function routed(watch, root, type, path) {
|
|
10508
10511
|
if (path === root || type === "rename" && root !== "/" && under3(root, path)) {
|
|
10509
|
-
noted(watch, { type, filename: baseOf3(root) });
|
|
10512
|
+
noted(watch, { type, filename: watch.names.get(root) ?? baseOf3(root) });
|
|
10510
10513
|
if (type === "rename") noted(watch, { ended: true });
|
|
10511
10514
|
return true;
|
|
10512
10515
|
}
|
|
@@ -10516,6 +10519,11 @@ function routed(watch, root, type, path) {
|
|
|
10516
10519
|
} else return false;
|
|
10517
10520
|
return true;
|
|
10518
10521
|
}
|
|
10522
|
+
function remapped(root, from, to) {
|
|
10523
|
+
if (root === from) return to;
|
|
10524
|
+
if (from !== "/" && root.startsWith(`${from}/`)) return to + root.slice(from.length);
|
|
10525
|
+
return void 0;
|
|
10526
|
+
}
|
|
10519
10527
|
var FsWatchers = class {
|
|
10520
10528
|
watchers = /* @__PURE__ */ new Set();
|
|
10521
10529
|
typed = /* @__PURE__ */ new Set();
|
|
@@ -10550,6 +10558,7 @@ var FsWatchers = class {
|
|
|
10550
10558
|
const other = alias === void 0 ? main : normalizePath(alias);
|
|
10551
10559
|
const watch = {
|
|
10552
10560
|
roots: other === main ? [main] : [main, other],
|
|
10561
|
+
names: /* @__PURE__ */ new Map(),
|
|
10553
10562
|
recursive,
|
|
10554
10563
|
notes: [],
|
|
10555
10564
|
overflowed: false,
|
|
@@ -10599,6 +10608,35 @@ var FsWatchers = class {
|
|
|
10599
10608
|
for (const watch of [...this.typed]) route(watch, type, at);
|
|
10600
10609
|
}
|
|
10601
10610
|
}
|
|
10611
|
+
moved(from, to) {
|
|
10612
|
+
const source = normalizePath(from);
|
|
10613
|
+
const target = normalizePath(to);
|
|
10614
|
+
const followed2 = /* @__PURE__ */ new Set();
|
|
10615
|
+
for (const watch of this.typed) {
|
|
10616
|
+
const [main] = watch.roots;
|
|
10617
|
+
const roots = watch.roots.map((root) => remapped(root, source, target) ?? root);
|
|
10618
|
+
if (roots.every((root, i) => root === watch.roots[i])) continue;
|
|
10619
|
+
followed2.add(watch);
|
|
10620
|
+
const names2 = /* @__PURE__ */ new Map();
|
|
10621
|
+
watch.roots.forEach((root, i) => {
|
|
10622
|
+
names2.set(roots[i], watch.names.get(root) ?? baseOf3(root));
|
|
10623
|
+
});
|
|
10624
|
+
watch.names = names2;
|
|
10625
|
+
watch.roots = [...new Set(roots)];
|
|
10626
|
+
if (main === source) {
|
|
10627
|
+
noted(watch, { type: "rename", filename: names2.get(target), moved: true });
|
|
10628
|
+
}
|
|
10629
|
+
}
|
|
10630
|
+
return followed2;
|
|
10631
|
+
}
|
|
10632
|
+
renamed(from, to) {
|
|
10633
|
+
const followed2 = this.moved(from, to);
|
|
10634
|
+
if (this.typed.size === 0) return;
|
|
10635
|
+
for (const path of [from, to]) {
|
|
10636
|
+
const at = normalizePath(path);
|
|
10637
|
+
for (const watch of [...this.typed]) if (!followed2.has(watch)) route(watch, "rename", at);
|
|
10638
|
+
}
|
|
10639
|
+
}
|
|
10602
10640
|
changed(...paths) {
|
|
10603
10641
|
this.report(paths);
|
|
10604
10642
|
this.emit("change", paths);
|
|
@@ -10702,7 +10740,16 @@ var FsWatchers = class {
|
|
|
10702
10740
|
}
|
|
10703
10741
|
},
|
|
10704
10742
|
rm: reshaping(after(fs.rm.bind(fs), (path) => [path], "rename")),
|
|
10705
|
-
rename: reshaping(
|
|
10743
|
+
rename: reshaping(async (from, to) => {
|
|
10744
|
+
let done5 = false;
|
|
10745
|
+
try {
|
|
10746
|
+
await fs.rename(from, to);
|
|
10747
|
+
done5 = true;
|
|
10748
|
+
} finally {
|
|
10749
|
+
this.report([from, to]);
|
|
10750
|
+
if (done5) this.renamed(from, to);
|
|
10751
|
+
}
|
|
10752
|
+
}),
|
|
10706
10753
|
link: reshaping(
|
|
10707
10754
|
after(
|
|
10708
10755
|
(from, to, follow2) => fs.link(from, to, follow2),
|
|
@@ -13577,7 +13624,7 @@ async function guardAndResolve(entry, path, req) {
|
|
|
13577
13624
|
entry: { ...entry, fs: noLinks }
|
|
13578
13625
|
};
|
|
13579
13626
|
}
|
|
13580
|
-
var
|
|
13627
|
+
var REMOVALS2 = /* @__PURE__ */ new Set(["unlink", "rmdir", "rm"]);
|
|
13581
13628
|
var CREATIONS = /* @__PURE__ */ new Set([
|
|
13582
13629
|
"write",
|
|
13583
13630
|
"mkdir",
|
|
@@ -13623,7 +13670,7 @@ async function dispatchPath(entry, req) {
|
|
|
13623
13670
|
const { fs, cwd } = entry;
|
|
13624
13671
|
try {
|
|
13625
13672
|
const path = fs.resolvePath(cwd, req.path);
|
|
13626
|
-
const device =
|
|
13673
|
+
const device = REMOVALS2.has(req.op) ? devRemoval(path, entry.cred?.()) : void 0;
|
|
13627
13674
|
if (device) return { ok: false, errno: device, message: `${path}: a device entry stays` };
|
|
13628
13675
|
const made2 = await newDevName(entry, path, req);
|
|
13629
13676
|
if (made2) return made2;
|