@ai-ecoverse/slicc-kernel 1.137.0 → 1.138.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 +2 -2
- package/dist/kernel-worker.js +21 -6
- package/dist/kernel-worker.js.map +2 -2
- package/dist/meta.js.map +1 -1
- package/dist/node-process-worker.js +1 -1
- package/dist/node.js +21 -6
- package/dist/node.js.map +2 -2
- package/dist/process-worker.js +1 -1
- package/dist/types/fs/notice.d.ts +1 -0
- package/dist/types/fs/opfs.d.ts +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1149,7 +1149,7 @@ OPFS stays authoritative: an entry is consulted only for a path that exists in O
|
|
|
1149
1149
|
|
|
1150
1150
|
**Stamping files written to OPFS:** `import { writeMetadata } from '@ai-ecoverse/slicc-kernel/meta'` and `await writeMetadata(root, [{ path, mode?, uid?, gid? }], { metadata?, indexedDB? })` is the supported way for an embedder to give files it wrote straight to OPFS a mode and an owner, also while no kernel runs (an installer before the first boot). `root` is the directory handle that becomes `/`, each `path` is absolute (`EINVAL` otherwise) and must exist there (`ENOENT` otherwise), `mode` is the permission bits (`0` to `0o7777`), and `uid` and `gid` come together or not at all (`EINVAL` otherwise). Entries for the same path (after normalization) are merged in order, a later field winning. All entries are written in one IndexedDB transaction, to the database a kernel on that root reads (`metadata`, default `slicc-kernel`), keeping what else a record holds and setting its change time (`ctime`) to now, and a running kernel on the same origin drops what it cached of them. The record format is the kernel's own and changes with it, so use this rather than writing the database. The kernel gives its layout paths (see [Permissions](#permissions)) an owner and mode only when they have no owner yet, so a path stamped here with `uid` and `gid` keeps them.
|
|
1151
1151
|
|
|
1152
|
-
**Telling running kernels about files written to OPFS:** a writer outside the kernel (an installer worker, a service worker, the page) that changes files on a root a kernel may be running on calls `import { postFsNotice } from '@ai-ecoverse/slicc-kernel/meta'` and `postFsNotice({ paths?, trees? }, { metadata? })` **after** each write has committed: after `close()` of its writable, `removeEntry` or `move()` has resolved. `paths` are absolute paths written, created or removed. List a created or removed name's parent directory too, so listings refresh. `trees` are absolute directories whose whole subtree changed, for a recursive replace or a sweep. The notice goes on the BroadcastChannel `slicc-kernel-dirs:<metadata>` (default `slicc-kernel-dirs:slicc-kernel`), the one kernels on that root already use between themselves, as `{ paths, trees }`. One notice per batch of writes is enough, if it's posted after the last of them. A kernel that receives it drops its cached lookups, handles and misses for those paths (for a tree, everything under it), tells its open files to reread, and publishes the paths to its programs' caches. A tree marks every path changed for them, so a running program's next look at any path goes to the kernel. **The residual window:** a reader can still see the old state between the write's commit and the notice's delivery (a message, usually well under a millisecond, but not bounded). A writer that runs while no kernel is up needs no notice: a kernel's caches start empty when it boots. **Without a notice** a kernel that is alone on its root keeps what it cached: a running program's stats of a path stay as they were for as long as the program runs, and the kernel's own directory handles for up to a second. So a writer that doesn't post notices must not change files a running kernel uses ([#1156](https://github.com/ai-ecoverse/slicc-kernel/issues/1156)).
|
|
1152
|
+
**Telling running kernels about files written to OPFS:** a writer outside the kernel (an installer worker, a service worker, the page) that changes files on a root a kernel may be running on calls `import { postFsNotice } from '@ai-ecoverse/slicc-kernel/meta'` and `postFsNotice({ paths?, trees? }, { metadata? })` **after** each write has committed: after `close()` of its writable, `removeEntry` or `move()` has resolved. `paths` are absolute paths written, created or removed. List a created or removed name's parent directory too, so listings refresh. `trees` are absolute directories whose whole subtree changed, for a recursive replace or a sweep. The notice goes on the BroadcastChannel `slicc-kernel-dirs:<metadata>` (default `slicc-kernel-dirs:slicc-kernel`), the one kernels on that root already use between themselves, as `{ paths, trees }`. One notice per batch of writes is enough, if it's posted after the last of them. A kernel that receives it drops its cached lookups, handles and misses for those paths (for a tree, everything under it), tells its open files to reread, and publishes the paths to its programs' caches. A tree marks every path changed for them, so a running program's next look at any path goes to the kernel. **The residual window:** a reader can still see the old state between the write's commit and the notice's delivery (a message, usually well under a millisecond, but not bounded). A writer that runs while no kernel is up needs no notice: a kernel's caches start empty when it boots. A notice for a path that exists as a regular file, listed in `paths` (a `trees` entry refreshes caches but stamps no times, since a tree can hold thousands of files), also gives it a new modification time, before running programs are told of the change: the notice's arrival, with the change time along, unless its modification time is already later (an explicit future one is kept, with its change time), as a write does on Linux, recorded once per burst of notices for the same file ([#1275](https://github.com/ai-ecoverse/slicc-kernel/issues/1275)). That matters because Chrome can keep a file's OPFS `lastModified` across an append through `createWritable({ keepExistingData: true })`, and the kernel reports `lastModified` for files it didn't write, so `-nt` and make wouldn't see the change. A notice without a real change still moves the time, so a make may rebuild once. An outside write that posts no notice keeps OPFS's time. Notices between kernels (the array form they post for their own writes) don't stamp: those kernels record their times themselves. That's part of the channel's contract: kernels post their own change notices on the dirs channel as a plain array of paths, and only the `{ paths, trees }` object form that `postFsNotice` sends counts as an outside writer's. A kernel must keep posting arrays there, which `fs-notice-page.test.mjs`'s peer row checks. **Without a notice** a kernel that is alone on its root keeps what it cached: a running program's stats of a path stay as they were for as long as the program runs, and the kernel's own directory handles for up to a second. So a writer that doesn't post notices must not change files a running kernel uses ([#1156](https://github.com/ai-ecoverse/slicc-kernel/issues/1156)).
|
|
1153
1153
|
|
|
1154
1154
|
The database is `slicc-kernel` (or the `metadata` option), schema version 1, with one object store, `entries`, keyed by absolute path (`keyPath: 'path'`) and an index `dir` on the parent directory of symlinks. A record looks like `{ path, mode?, atimeMs?, mtimeMs?, mtimeFor?, ctimeMs?, ino?, link?, dir?, uid?, gid?, heir?, writes?, epoch? }`, where `mtimeFor` is the OPFS `lastModified` an explicit `mtimeMs` belongs to, `uid`, `gid` and `heir` (`[uid, gid]`) are the owners of [File owners](#file-owners), `writes` counts the writes kernels with `integrity` made to a protected file (see [Protected paths](#protected-paths)), whose `lastModified` OPFS leaves alone for a write through a sync access handle, and `epoch`, on the entry of `/`, counts their renames and symlinks at or above a protected path. A future schema bumps the version and migrates in `onupgradeneeded`; an older kernel then closes its connection. The sidecar is keyed by path from the `root` handle, so use a different `metadata` name for each distinct root.
|
|
1155
1155
|
|
|
@@ -1277,7 +1277,7 @@ The tools in `tools/cert-review/`:
|
|
|
1277
1277
|
- and record, which re-hashes the candidate's artifact against the pack job's sha256.
|
|
1278
1278
|
- **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.
|
|
1279
1279
|
- **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.
|
|
1280
|
-
- **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 (`
|
|
1280
|
+
- **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 (`fcaa9a23…`, 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:
|
|
1281
1281
|
- **PASS:** the candidate passes, `downstream: PASS bios … N pass`;
|
|
1282
1282
|
- **FAIL:** a regression, or a failing test (by the counts, even without its rows) is **not certified**, and the group is red;
|
|
1283
1283
|
- **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
|
@@ -358,7 +358,7 @@ function noticeOf(data) {
|
|
|
358
358
|
if (Array.isArray(data)) return { paths: data.filter((p) => typeof p === "string"), trees: [] };
|
|
359
359
|
const notice = data ?? {};
|
|
360
360
|
const strings = (list) => Array.isArray(list) ? list.filter((p) => typeof p === "string") : [];
|
|
361
|
-
return { paths: strings(notice.paths), trees: strings(notice.trees) };
|
|
361
|
+
return { paths: strings(notice.paths), trees: strings(notice.trees), outside: true };
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
// src/fs/types.ts
|
|
@@ -1307,9 +1307,11 @@ var OpfsFs = class {
|
|
|
1307
1307
|
for (const path of [...c.paths, ...c.trees]) this.forget(path);
|
|
1308
1308
|
});
|
|
1309
1309
|
this.channel.onmessage = ({ data }) => {
|
|
1310
|
-
const { paths, trees } = noticeOf(data);
|
|
1310
|
+
const { paths, trees, outside } = noticeOf(data);
|
|
1311
1311
|
for (const path of [...paths, ...trees]) this.forget(normalizePath(path));
|
|
1312
|
-
remote?.emit({ paths, trees });
|
|
1312
|
+
const tell = () => remote?.emit({ paths, trees });
|
|
1313
|
+
if (!outside) tell();
|
|
1314
|
+
else void this.noticed(paths.map(normalizePath), Date.now()).finally(tell);
|
|
1313
1315
|
};
|
|
1314
1316
|
this.channel.unref?.();
|
|
1315
1317
|
}
|
|
@@ -1768,6 +1770,14 @@ var OpfsFs = class {
|
|
|
1768
1770
|
this.fileTimes.set(target, Date.now());
|
|
1769
1771
|
this.scheduleFileTimes();
|
|
1770
1772
|
}
|
|
1773
|
+
async noticed(paths, at) {
|
|
1774
|
+
for (const path of new Set(paths)) {
|
|
1775
|
+
const st = await this.lstat(path).catch(() => void 0);
|
|
1776
|
+
if (!st?.isFile || st.mtime.getTime() >= at) continue;
|
|
1777
|
+
this.fileTimes.set(path, Math.max(at, this.fileTimes.get(path) ?? 0));
|
|
1778
|
+
}
|
|
1779
|
+
this.scheduleFileTimes();
|
|
1780
|
+
}
|
|
1771
1781
|
scheduleFileTimes() {
|
|
1772
1782
|
if (this.flushing.file || this.fileTimes.size === 0) return;
|
|
1773
1783
|
this.flushing.file = setTimeout(() => {
|
|
@@ -7115,7 +7125,7 @@ function satisfies(version, range2) {
|
|
|
7115
7125
|
}
|
|
7116
7126
|
|
|
7117
7127
|
// src/version.ts
|
|
7118
|
-
var KERNEL_VERSION = true ? "1.
|
|
7128
|
+
var KERNEL_VERSION = true ? "1.138.0" : "0.0.0-dev";
|
|
7119
7129
|
var OS_RELEASE = "6.0.0-slicc";
|
|
7120
7130
|
function procVersion() {
|
|
7121
7131
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -15750,10 +15760,15 @@ var TlsEngine = class {
|
|
|
15750
15760
|
const m = this.clientModule();
|
|
15751
15761
|
if (servername.includes("\0")) throw new TlsError(-3, "tls engine: a server name has no NUL");
|
|
15752
15762
|
const wire = alpnWire(alpn);
|
|
15753
|
-
const
|
|
15763
|
+
const bytes = new TextEncoder().encode(servername);
|
|
15764
|
+
const name2 = this.put(bytes);
|
|
15754
15765
|
try {
|
|
15755
15766
|
const at = store?.ptr ?? 0;
|
|
15756
|
-
const
|
|
15767
|
+
const named = m._tls_client_new_name;
|
|
15768
|
+
const ptr = this.withBytes(
|
|
15769
|
+
wire,
|
|
15770
|
+
(p) => named ? named(at, name2, bytes.length, p, wire.length, flags2) : m._tls_client_new(at, name2, p, wire.length, flags2)
|
|
15771
|
+
);
|
|
15757
15772
|
if (!ptr) throw this.error(m._tls_last_error(), "tls client");
|
|
15758
15773
|
return new TlsSession(this, m, ptr);
|
|
15759
15774
|
} finally {
|