@ai-ecoverse/slicc-kernel 1.54.8 → 1.55.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 +7 -4
- package/dist/index.js +40 -10
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +326 -76
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node.js +366 -86
- package/dist/node.js.map +3 -3
- package/package.json +1 -1
package/dist/node.js
CHANGED
|
@@ -1693,6 +1693,7 @@ var LAST_UID = 6e4;
|
|
|
1693
1693
|
var USERS_GROUP = "users";
|
|
1694
1694
|
var HOME_DIR = /^\/home\/[^/]+$/;
|
|
1695
1695
|
var SYSTEM_PNPM_HOME = "/usr/local/share/pnpm";
|
|
1696
|
+
var PNPM_CONFIG = "minimumReleaseAge: 1440\n";
|
|
1696
1697
|
var isId = (text) => text !== void 0 && /^\d+$/.test(text);
|
|
1697
1698
|
function lines(text) {
|
|
1698
1699
|
return text.split("\n").filter((line, i, all) => line !== "" || i < all.length - 1);
|
|
@@ -1748,6 +1749,7 @@ function userEnv(account, system) {
|
|
|
1748
1749
|
USER: account.name,
|
|
1749
1750
|
LOGNAME: account.name,
|
|
1750
1751
|
PNPM_HOME: pnpm,
|
|
1752
|
+
NPM_CONFIG_PREFIX: account.uid === 0 ? "/usr/local" : `${account.home}/.local`,
|
|
1751
1753
|
PATH: `${pnpm}/bin:/usr/local/bin:/usr/bin:/bin`
|
|
1752
1754
|
};
|
|
1753
1755
|
}
|
|
@@ -1772,6 +1774,18 @@ var UserDb = class {
|
|
|
1772
1774
|
group: parseGroup(await this.read(GROUP, DEFAULT_GROUP))
|
|
1773
1775
|
};
|
|
1774
1776
|
}
|
|
1777
|
+
async provision(row) {
|
|
1778
|
+
const pnpm = `${row.home}/.local/share/pnpm`;
|
|
1779
|
+
if (!await this.fs.exists(pnpm)) {
|
|
1780
|
+
await this.fs.mkdir(pnpm, { recursive: true });
|
|
1781
|
+
await this.fs.chmod(pnpm, row.name.startsWith("scoop-") ? 488 : 448);
|
|
1782
|
+
}
|
|
1783
|
+
const config = `${row.home}/.config/pnpm/config.yaml`;
|
|
1784
|
+
if (!await this.fs.exists(config)) {
|
|
1785
|
+
await this.fs.mkdir(`${row.home}/.config/pnpm`, { recursive: true });
|
|
1786
|
+
await this.fs.writeFile(config, PNPM_CONFIG);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1775
1789
|
async commit(passwd, group, then) {
|
|
1776
1790
|
const before = [await this.read(PASSWD, DEFAULT_PASSWD), await this.read(GROUP, DEFAULT_GROUP)];
|
|
1777
1791
|
try {
|
|
@@ -1845,6 +1859,7 @@ var UserDb = class {
|
|
|
1845
1859
|
const groups = parsed(group);
|
|
1846
1860
|
const found = users.find((r) => r.name === name2);
|
|
1847
1861
|
if (found) {
|
|
1862
|
+
if (found.uid >= FIRST_UID && found.uid <= LAST_UID) await this.provision(found);
|
|
1848
1863
|
remember();
|
|
1849
1864
|
return this.account(found, groups);
|
|
1850
1865
|
}
|
|
@@ -1863,6 +1878,7 @@ var UserDb = class {
|
|
|
1863
1878
|
const fresh = !await this.fs.exists(home);
|
|
1864
1879
|
await this.fs.mkdir(home, { recursive: true });
|
|
1865
1880
|
try {
|
|
1881
|
+
await this.provision(row);
|
|
1866
1882
|
await this.fs.mkdir("/etc", { recursive: true });
|
|
1867
1883
|
await this.commit(formatPasswd(passwd), formatGroup(group));
|
|
1868
1884
|
} catch (err) {
|
|
@@ -2749,7 +2765,7 @@ var OpfsFs = class {
|
|
|
2749
2765
|
const parent = await this.locate(parentOf2(path), true);
|
|
2750
2766
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
2751
2767
|
await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
|
|
2752
|
-
this.touch(parent.path);
|
|
2768
|
+
this.touch(parent.path, `${parent.path === "/" ? "" : parent.path}/${baseOf(path)}`);
|
|
2753
2769
|
}
|
|
2754
2770
|
async rm(path, options = {}) {
|
|
2755
2771
|
let found;
|
|
@@ -2813,7 +2829,7 @@ var OpfsFs = class {
|
|
|
2813
2829
|
const swap = targetStat?.isDirectory === true;
|
|
2814
2830
|
const staging = swap ? `.${name2}.${crypto.randomUUID()}` : name2;
|
|
2815
2831
|
this.forget(destination);
|
|
2816
|
-
const
|
|
2832
|
+
const timed2 = await this.explicitTimes(source.path);
|
|
2817
2833
|
const moved2 = await this.place(source.handle, dir, staging);
|
|
2818
2834
|
if (swap) await this.rm(destination, { recursive: true });
|
|
2819
2835
|
await this.meta.move(source.path, destination, carry);
|
|
@@ -2825,7 +2841,7 @@ var OpfsFs = class {
|
|
|
2825
2841
|
if (!placed) await dir.removeEntry(staging, { recursive: true });
|
|
2826
2842
|
copied ||= !placed;
|
|
2827
2843
|
}
|
|
2828
|
-
if (copied) await this.restamp(destination,
|
|
2844
|
+
if (copied) await this.restamp(destination, timed2);
|
|
2829
2845
|
this.changed(source.path, destination);
|
|
2830
2846
|
this.touch(parentOf2(source.path), parent.path);
|
|
2831
2847
|
}
|
|
@@ -2834,16 +2850,16 @@ var OpfsFs = class {
|
|
|
2834
2850
|
return handle.kind === "file" ? (await handle.getFile()).lastModified : 0;
|
|
2835
2851
|
}
|
|
2836
2852
|
async explicitTimes(path) {
|
|
2837
|
-
const
|
|
2853
|
+
const timed2 = [];
|
|
2838
2854
|
for (const entry of await this.meta.under(path)) {
|
|
2839
2855
|
if (entry.mtimeFor === void 0 || entry.link !== void 0) continue;
|
|
2840
2856
|
const current = await this.modified(entry.path).catch(() => void 0);
|
|
2841
|
-
if (current === entry.mtimeFor)
|
|
2857
|
+
if (current === entry.mtimeFor) timed2.push(entry.path.slice(path.length));
|
|
2842
2858
|
}
|
|
2843
|
-
return
|
|
2859
|
+
return timed2;
|
|
2844
2860
|
}
|
|
2845
|
-
async restamp(path,
|
|
2846
|
-
for (const rest2 of
|
|
2861
|
+
async restamp(path, timed2) {
|
|
2862
|
+
for (const rest2 of timed2) {
|
|
2847
2863
|
const mtimeFor = await this.modified(path + rest2);
|
|
2848
2864
|
await this.meta.update(path + rest2, (entry) => entry && { ...entry, mtimeFor });
|
|
2849
2865
|
}
|
|
@@ -4156,17 +4172,22 @@ async function followLinks(fs, path) {
|
|
|
4156
4172
|
}
|
|
4157
4173
|
return current;
|
|
4158
4174
|
}
|
|
4159
|
-
function withCommandDirs(fs, names) {
|
|
4175
|
+
function withCommandDirs(fs, names, userDir = async () => void 0) {
|
|
4160
4176
|
async function children2(path) {
|
|
4161
4177
|
if (path === "/") return ["bin", "usr"];
|
|
4162
4178
|
if (path === "/usr") return ["bin"];
|
|
4163
4179
|
if (path === "/bin" || path === "/usr/bin") return [...await names()];
|
|
4164
|
-
|
|
4180
|
+
const own = await userDir(path);
|
|
4181
|
+
return own?.size ? [...own] : void 0;
|
|
4165
4182
|
}
|
|
4166
4183
|
async function virtual(path) {
|
|
4167
4184
|
if (path === "/usr" || path === "/bin" || path === "/usr/bin") return synthetic(true, path);
|
|
4168
4185
|
const name2 = COMMAND.exec(path)?.[1];
|
|
4169
|
-
|
|
4186
|
+
if (name2 !== void 0) return (await names()).has(name2) ? synthetic(false, path) : void 0;
|
|
4187
|
+
if ((await userDir(path))?.size) return synthetic(true, path);
|
|
4188
|
+
const slash = path.lastIndexOf("/");
|
|
4189
|
+
const own = await userDir(path.slice(0, slash));
|
|
4190
|
+
return own?.has(path.slice(slash + 1)) ? synthetic(false, path) : void 0;
|
|
4170
4191
|
}
|
|
4171
4192
|
async function linked(path) {
|
|
4172
4193
|
return await virtual(path) ?? virtual(await followLinks(fs, path));
|
|
@@ -11573,11 +11594,24 @@ var HostfsClient = class {
|
|
|
11573
11594
|
get maxIo() {
|
|
11574
11595
|
return this.capabilities.maxIo ?? HOSTFS_IO;
|
|
11575
11596
|
}
|
|
11576
|
-
|
|
11597
|
+
retired;
|
|
11598
|
+
stop = new AbortController();
|
|
11599
|
+
get gone() {
|
|
11600
|
+
return this.retired !== void 0;
|
|
11601
|
+
}
|
|
11602
|
+
retire(why) {
|
|
11603
|
+
this.retired ??= why;
|
|
11604
|
+
this.stop.abort();
|
|
11605
|
+
}
|
|
11606
|
+
async send(path, init, handle = false, retried = false, cleanup = false) {
|
|
11607
|
+
if (this.retired && !cleanup) throw fsError2("ENOTCONN", this.retired);
|
|
11608
|
+
const stop = cleanup ? void 0 : this.stop.signal;
|
|
11609
|
+
const signal = init.signal && stop ? AbortSignal.any([init.signal, stop]) : init.signal ?? stop;
|
|
11577
11610
|
let response;
|
|
11578
11611
|
try {
|
|
11579
11612
|
response = await this.fetch(`${this.grant.url}${path}`, {
|
|
11580
11613
|
...init,
|
|
11614
|
+
...signal ? { signal } : {},
|
|
11581
11615
|
credentials: "omit",
|
|
11582
11616
|
headers: {
|
|
11583
11617
|
...init.headers,
|
|
@@ -11614,6 +11648,20 @@ var HostfsClient = class {
|
|
|
11614
11648
|
);
|
|
11615
11649
|
return await response.json();
|
|
11616
11650
|
}
|
|
11651
|
+
async cleanup(op, body) {
|
|
11652
|
+
const response = await this.send(
|
|
11653
|
+
"/api/hostfs",
|
|
11654
|
+
{
|
|
11655
|
+
method: "POST",
|
|
11656
|
+
headers: { "Content-Type": "application/json" },
|
|
11657
|
+
body: JSON.stringify({ op, ...body })
|
|
11658
|
+
},
|
|
11659
|
+
true,
|
|
11660
|
+
false,
|
|
11661
|
+
true
|
|
11662
|
+
);
|
|
11663
|
+
await response.body?.cancel();
|
|
11664
|
+
}
|
|
11617
11665
|
async read(fh, offset, size, ifMatch) {
|
|
11618
11666
|
const response = await this.send(
|
|
11619
11667
|
"/api/hostfs",
|
|
@@ -11681,6 +11729,11 @@ function hostfsHandlers(current) {
|
|
|
11681
11729
|
return fresh(remote);
|
|
11682
11730
|
};
|
|
11683
11731
|
const call = (op, body = {}) => current().call(op, body);
|
|
11732
|
+
const settle = (client, restore) => client.call("setattr", restore).catch((err) => {
|
|
11733
|
+
if (!client.gone || !client.cleanup) throw err;
|
|
11734
|
+
void client.cleanup("setattr", restore).catch(() => {
|
|
11735
|
+
});
|
|
11736
|
+
});
|
|
11684
11737
|
async function writable(client, path, flags) {
|
|
11685
11738
|
const { ifMatch: _, ...asked } = flags;
|
|
11686
11739
|
const opening = () => client.call("open", { path: rel(path), ...asked });
|
|
@@ -11691,9 +11744,9 @@ function hostfsHandlers(current) {
|
|
|
11691
11744
|
const mode = (await client.call("stat", { path: rel(path) })).mode ?? 0;
|
|
11692
11745
|
if (mode & 128) throw err;
|
|
11693
11746
|
const restore = { path: rel(path), mode };
|
|
11694
|
-
await client.call("setattr", { path: rel(path), mode: mode | 128 })
|
|
11695
|
-
|
|
11696
|
-
|
|
11747
|
+
const opened2 = await client.call("setattr", { path: rel(path), mode: mode | 128 }).then(opening).catch(async (again) => {
|
|
11748
|
+
await settle(client, restore).catch(() => {
|
|
11749
|
+
});
|
|
11697
11750
|
throw again;
|
|
11698
11751
|
});
|
|
11699
11752
|
return { opened: opened2, restore };
|
|
@@ -11748,7 +11801,7 @@ function hostfsHandlers(current) {
|
|
|
11748
11801
|
try {
|
|
11749
11802
|
await fresh(remote).client.call("release", { fh: remote.fh }, true);
|
|
11750
11803
|
} finally {
|
|
11751
|
-
if (remote.restore) await current()
|
|
11804
|
+
if (remote.restore) await settle(current(), remote.restore);
|
|
11752
11805
|
}
|
|
11753
11806
|
},
|
|
11754
11807
|
mkdir: async (path) => void await call("mkdir", { path: rel(path) }),
|
|
@@ -11878,6 +11931,73 @@ function invalidation(event) {
|
|
|
11878
11931
|
return event.paths ?? true;
|
|
11879
11932
|
}
|
|
11880
11933
|
var PORT_REQUEST_MS = 6e4;
|
|
11934
|
+
var HOSTFS_CALL_MS = 2e4;
|
|
11935
|
+
var PORT_BEAT_MS = 1e3;
|
|
11936
|
+
var PORT_DEAD_MS = 1e4;
|
|
11937
|
+
var PORT_LINGER_MS = 1e3;
|
|
11938
|
+
function timed(transport, ms) {
|
|
11939
|
+
const inflight = /* @__PURE__ */ new Set();
|
|
11940
|
+
const guard = (name2, run2, late, expire) => new Promise((resolve, reject) => {
|
|
11941
|
+
let expired = false;
|
|
11942
|
+
const running = run2();
|
|
11943
|
+
inflight.add(running);
|
|
11944
|
+
const done4 = () => inflight.delete(running);
|
|
11945
|
+
running.then(done4, done4);
|
|
11946
|
+
const timer = setTimeout(() => {
|
|
11947
|
+
expired = true;
|
|
11948
|
+
const why = `the host folder did not answer ${name2} within ${ms} ms`;
|
|
11949
|
+
transport.retire?.(why);
|
|
11950
|
+
expire?.(running);
|
|
11951
|
+
reject(fsError2("ENOTCONN", why));
|
|
11952
|
+
}, ms);
|
|
11953
|
+
running.then(
|
|
11954
|
+
(value) => {
|
|
11955
|
+
clearTimeout(timer);
|
|
11956
|
+
if (expired) late?.(value);
|
|
11957
|
+
resolve(value);
|
|
11958
|
+
},
|
|
11959
|
+
(err) => {
|
|
11960
|
+
clearTimeout(timer);
|
|
11961
|
+
reject(err);
|
|
11962
|
+
}
|
|
11963
|
+
);
|
|
11964
|
+
});
|
|
11965
|
+
const cleanup = async (op, body, skip) => {
|
|
11966
|
+
await Promise.allSettled([...inflight].filter((work) => work !== skip));
|
|
11967
|
+
await transport.cleanup?.(op, body);
|
|
11968
|
+
};
|
|
11969
|
+
const release = (opened2) => {
|
|
11970
|
+
const fh = opened2?.fh;
|
|
11971
|
+
if (typeof fh === "number") void cleanup("release", { fh }).catch(() => {
|
|
11972
|
+
});
|
|
11973
|
+
};
|
|
11974
|
+
return {
|
|
11975
|
+
get capabilities() {
|
|
11976
|
+
return transport.capabilities;
|
|
11977
|
+
},
|
|
11978
|
+
get token() {
|
|
11979
|
+
return transport.token;
|
|
11980
|
+
},
|
|
11981
|
+
get maxIo() {
|
|
11982
|
+
return transport.maxIo;
|
|
11983
|
+
},
|
|
11984
|
+
get gone() {
|
|
11985
|
+
return transport.gone;
|
|
11986
|
+
},
|
|
11987
|
+
retire: (why) => transport.retire?.(why),
|
|
11988
|
+
...transport.cleanup ? { cleanup } : {},
|
|
11989
|
+
call: (op, body, handle) => guard(
|
|
11990
|
+
op,
|
|
11991
|
+
() => transport.call(op, body, handle),
|
|
11992
|
+
op === "open" ? release : void 0,
|
|
11993
|
+
op === "release" ? (running) => void cleanup("release", body ?? {}, running).catch(() => {
|
|
11994
|
+
}) : void 0
|
|
11995
|
+
),
|
|
11996
|
+
read: (fh, offset, size, ifMatch) => guard("read", () => transport.read(fh, offset, size, ifMatch)),
|
|
11997
|
+
write: (fh, offset, bytes) => guard("write", () => transport.write(fh, offset, bytes)),
|
|
11998
|
+
watch: (signal, onEvent, onOpen) => transport.watch(signal, onEvent, onOpen)
|
|
11999
|
+
};
|
|
12000
|
+
}
|
|
11881
12001
|
var PORT_MS_PER_MIB = 1e3;
|
|
11882
12002
|
var nextPortToken = 0;
|
|
11883
12003
|
var PortTransport = class {
|
|
@@ -11888,13 +12008,25 @@ var PortTransport = class {
|
|
|
11888
12008
|
waiting = /* @__PURE__ */ new Map();
|
|
11889
12009
|
next = 0;
|
|
11890
12010
|
dead;
|
|
11891
|
-
|
|
12011
|
+
beatMs;
|
|
12012
|
+
deadMs;
|
|
12013
|
+
lingerMs;
|
|
12014
|
+
beat;
|
|
12015
|
+
answers = false;
|
|
12016
|
+
pinged;
|
|
12017
|
+
constructor(grant, liveness = {}) {
|
|
11892
12018
|
this.port = grant.port;
|
|
11893
|
-
this.requestMs = requestMs;
|
|
12019
|
+
this.requestMs = liveness.requestMs ?? PORT_REQUEST_MS;
|
|
12020
|
+
this.beatMs = liveness.beatMs ?? PORT_BEAT_MS;
|
|
12021
|
+
this.deadMs = liveness.deadMs ?? PORT_DEAD_MS;
|
|
12022
|
+
this.lingerMs = liveness.lingerMs ?? PORT_LINGER_MS;
|
|
11894
12023
|
this.capabilities = grant.capabilities ?? {};
|
|
11895
12024
|
this.token = grant.token ?? `port-${++nextPortToken}`;
|
|
11896
12025
|
this.port.addEventListener("message", (event) => {
|
|
11897
|
-
|
|
12026
|
+
if (event.data?.pong) {
|
|
12027
|
+
this.answers = true;
|
|
12028
|
+
this.pinged = void 0;
|
|
12029
|
+
} else this.waiting.get(event.data?.id)?.(event.data);
|
|
11898
12030
|
});
|
|
11899
12031
|
this.port.addEventListener("close", () => this.fail("the folder's port closed"));
|
|
11900
12032
|
this.port.start();
|
|
@@ -11905,8 +12037,51 @@ var PortTransport = class {
|
|
|
11905
12037
|
get gone() {
|
|
11906
12038
|
return this.dead !== void 0;
|
|
11907
12039
|
}
|
|
12040
|
+
retire(why) {
|
|
12041
|
+
this.fail(why);
|
|
12042
|
+
}
|
|
12043
|
+
ping() {
|
|
12044
|
+
if (this.pinged !== void 0) return;
|
|
12045
|
+
this.pinged = Date.now();
|
|
12046
|
+
try {
|
|
12047
|
+
this.port.postMessage({ id: 0, op: "ping" });
|
|
12048
|
+
} catch {
|
|
12049
|
+
}
|
|
12050
|
+
}
|
|
12051
|
+
watchBeat() {
|
|
12052
|
+
if (this.beat || this.dead) return;
|
|
12053
|
+
this.ping();
|
|
12054
|
+
this.beat = setInterval(() => {
|
|
12055
|
+
if (this.waiting.size === 0) {
|
|
12056
|
+
clearInterval(this.beat);
|
|
12057
|
+
this.beat = void 0;
|
|
12058
|
+
return;
|
|
12059
|
+
}
|
|
12060
|
+
if (this.answers && this.pinged !== void 0 && Date.now() - this.pinged > this.deadMs) {
|
|
12061
|
+
this.fail("the folder's port stopped answering");
|
|
12062
|
+
return;
|
|
12063
|
+
}
|
|
12064
|
+
this.ping();
|
|
12065
|
+
}, this.beatMs);
|
|
12066
|
+
}
|
|
12067
|
+
async cleanup(op, body) {
|
|
12068
|
+
if (this.closed) throw fsError2("ENOTCONN", "the folder's port is closed");
|
|
12069
|
+
this.port.postMessage({ id: 0, op: "cleanup", name: op, body });
|
|
12070
|
+
}
|
|
12071
|
+
closed = false;
|
|
11908
12072
|
fail(why) {
|
|
11909
|
-
if (this.dead === void 0)
|
|
12073
|
+
if (this.dead === void 0) {
|
|
12074
|
+
setTimeout(() => {
|
|
12075
|
+
try {
|
|
12076
|
+
this.port.postMessage({ id: 0, op: "bye" });
|
|
12077
|
+
} catch {
|
|
12078
|
+
}
|
|
12079
|
+
this.closed = true;
|
|
12080
|
+
this.port.close();
|
|
12081
|
+
}, this.lingerMs);
|
|
12082
|
+
}
|
|
12083
|
+
clearInterval(this.beat);
|
|
12084
|
+
this.beat = void 0;
|
|
11910
12085
|
this.dead ??= why;
|
|
11911
12086
|
for (const settle of [...this.waiting.values()])
|
|
11912
12087
|
settle({ id: 0, error: { code: "ENOTCONN", message: why } });
|
|
@@ -11923,6 +12098,7 @@ var PortTransport = class {
|
|
|
11923
12098
|
},
|
|
11924
12099
|
this.requestMs + Math.ceil(bytes / 1048576) * PORT_MS_PER_MIB
|
|
11925
12100
|
);
|
|
12101
|
+
this.watchBeat();
|
|
11926
12102
|
this.waiting.set(id2, (reply) => {
|
|
11927
12103
|
clearTimeout(timer);
|
|
11928
12104
|
this.waiting.delete(id2);
|
|
@@ -11969,6 +12145,7 @@ var PortTransport = class {
|
|
|
11969
12145
|
}
|
|
11970
12146
|
resolve();
|
|
11971
12147
|
};
|
|
12148
|
+
this.watchBeat();
|
|
11972
12149
|
this.waiting.set(id2, (reply) => {
|
|
11973
12150
|
if (reply.open) return onOpen();
|
|
11974
12151
|
if (reply.event) return onEvent(reply.event);
|
|
@@ -11992,11 +12169,38 @@ var errorOf = (err) => ({
|
|
|
11992
12169
|
});
|
|
11993
12170
|
function serveHostfsPort(port, transport) {
|
|
11994
12171
|
const watches = /* @__PURE__ */ new Map();
|
|
12172
|
+
const handles = /* @__PURE__ */ new Set();
|
|
12173
|
+
let stopped = false;
|
|
12174
|
+
const closeHandles = () => {
|
|
12175
|
+
for (const fh of handles) void transport.call("release", { fh }, true).catch(() => {
|
|
12176
|
+
});
|
|
12177
|
+
handles.clear();
|
|
12178
|
+
};
|
|
11995
12179
|
const send = (reply, transfer = []) => port.postMessage(reply, transfer);
|
|
12180
|
+
const pending = /* @__PURE__ */ new Set();
|
|
12181
|
+
const track = (work) => {
|
|
12182
|
+
pending.add(work);
|
|
12183
|
+
const done4 = () => pending.delete(work);
|
|
12184
|
+
work.then(done4, done4);
|
|
12185
|
+
return work;
|
|
12186
|
+
};
|
|
12187
|
+
const answer = async (name2, body) => {
|
|
12188
|
+
const result = await track(transport.call(name2, body));
|
|
12189
|
+
const opened2 = result?.fh;
|
|
12190
|
+
if (name2 === "open" && typeof opened2 === "number") handles.add(opened2);
|
|
12191
|
+
if (name2 === "release") handles.delete(body?.fh);
|
|
12192
|
+
if (stopped) closeHandles();
|
|
12193
|
+
return result;
|
|
12194
|
+
};
|
|
11996
12195
|
const listener = async ({
|
|
11997
12196
|
data
|
|
11998
12197
|
}) => {
|
|
11999
12198
|
const { id: id2, op } = data;
|
|
12199
|
+
if (op === "ping") return send({ id: id2, pong: true });
|
|
12200
|
+
if (op === "bye") return stop();
|
|
12201
|
+
if (op === "cleanup")
|
|
12202
|
+
return void Promise.allSettled([...pending]).then(() => answer(data.name, data.body)).catch(() => {
|
|
12203
|
+
});
|
|
12000
12204
|
if (op === "unwatch") return watches.get(id2)?.abort();
|
|
12001
12205
|
if (op === "watch") {
|
|
12002
12206
|
const stop2 = new AbortController();
|
|
@@ -12017,32 +12221,35 @@ function serveHostfsPort(port, transport) {
|
|
|
12017
12221
|
}
|
|
12018
12222
|
try {
|
|
12019
12223
|
if (op === "read") {
|
|
12020
|
-
const read = await
|
|
12021
|
-
|
|
12022
|
-
|
|
12023
|
-
|
|
12024
|
-
|
|
12224
|
+
const read = await track(
|
|
12225
|
+
transport.read(
|
|
12226
|
+
data.fh,
|
|
12227
|
+
data.offset,
|
|
12228
|
+
data.size,
|
|
12229
|
+
data.ifMatch
|
|
12230
|
+
)
|
|
12025
12231
|
);
|
|
12026
12232
|
const bytes = read.slice();
|
|
12027
12233
|
return send({ id: id2, bytes }, [bytes.buffer]);
|
|
12028
12234
|
}
|
|
12029
12235
|
if (op === "write") {
|
|
12030
|
-
await
|
|
12236
|
+
await track(
|
|
12237
|
+
transport.write(data.fh, data.offset, data.bytes)
|
|
12238
|
+
);
|
|
12031
12239
|
return send({ id: id2 });
|
|
12032
12240
|
}
|
|
12033
12241
|
if (op !== "call") throw fsError2("EINVAL", `unknown hostfs port op ${op}`);
|
|
12034
|
-
send({
|
|
12035
|
-
id: id2,
|
|
12036
|
-
result: await transport.call(data.name, data.body)
|
|
12037
|
-
});
|
|
12242
|
+
send({ id: id2, result: await answer(data.name, data.body) });
|
|
12038
12243
|
} catch (err) {
|
|
12039
12244
|
send({ id: id2, error: errorOf(err) });
|
|
12040
12245
|
}
|
|
12041
12246
|
};
|
|
12042
12247
|
const stop = () => {
|
|
12248
|
+
stopped = true;
|
|
12043
12249
|
port.removeEventListener("message", listener);
|
|
12044
12250
|
port.removeEventListener("close", stop);
|
|
12045
12251
|
for (const watch of watches.values()) watch.abort();
|
|
12252
|
+
closeHandles();
|
|
12046
12253
|
port.close();
|
|
12047
12254
|
};
|
|
12048
12255
|
port.addEventListener("message", listener);
|
|
@@ -12050,10 +12257,10 @@ function serveHostfsPort(port, transport) {
|
|
|
12050
12257
|
port.start();
|
|
12051
12258
|
return stop;
|
|
12052
12259
|
}
|
|
12053
|
-
function grantConnect(hook, fetch2,
|
|
12260
|
+
function grantConnect(hook, fetch2, liveness = {}) {
|
|
12054
12261
|
return async (source, options) => {
|
|
12055
12262
|
const first = await hook(source, options);
|
|
12056
|
-
if ("port" in first) return new PortTransport(first,
|
|
12263
|
+
if ("port" in first) return new PortTransport(first, liveness);
|
|
12057
12264
|
const grant = async () => {
|
|
12058
12265
|
const again = await hook(source, options);
|
|
12059
12266
|
if ("port" in again)
|
|
@@ -12073,7 +12280,10 @@ async function openHostfs(spec, connect, timing = {}) {
|
|
|
12073
12280
|
const stop = new AbortController();
|
|
12074
12281
|
let active;
|
|
12075
12282
|
const handlers = hostfsHandlers(() => active);
|
|
12076
|
-
const ranged2 = () =>
|
|
12283
|
+
const ranged2 = () => ({
|
|
12284
|
+
...active?.capabilities.ranges === true ? { ranges: true } : {},
|
|
12285
|
+
...active ? { maxIo: Math.min(active.maxIo, HOSTFS_IO) } : {}
|
|
12286
|
+
});
|
|
12077
12287
|
const served = serveFilesystem(
|
|
12078
12288
|
port2,
|
|
12079
12289
|
{ ...slot.handlers, mount: ranged2 },
|
|
@@ -12089,7 +12299,7 @@ async function openHostfs(spec, connect, timing = {}) {
|
|
|
12089
12299
|
{
|
|
12090
12300
|
connect: () => connect(spec.source, { readonly: readonly2 }),
|
|
12091
12301
|
up(client) {
|
|
12092
|
-
active = client;
|
|
12302
|
+
active = timed(client, timing.callMs ?? HOSTFS_CALL_MS);
|
|
12093
12303
|
slot.insert(client, handlers);
|
|
12094
12304
|
served.invalidate(true);
|
|
12095
12305
|
},
|
|
@@ -12588,8 +12798,11 @@ var Launcher = class {
|
|
|
12588
12798
|
createWorker;
|
|
12589
12799
|
modulesDir;
|
|
12590
12800
|
env;
|
|
12591
|
-
|
|
12592
|
-
|
|
12801
|
+
catalogs = /* @__PURE__ */ new Map();
|
|
12802
|
+
ownCatalogs = /* @__PURE__ */ new Map();
|
|
12803
|
+
binfmts = /* @__PURE__ */ new Map();
|
|
12804
|
+
scopes;
|
|
12805
|
+
unwatchScopes = () => void 0;
|
|
12593
12806
|
warned = /* @__PURE__ */ new Set();
|
|
12594
12807
|
compiled = /* @__PURE__ */ new Map();
|
|
12595
12808
|
processes = /* @__PURE__ */ new Map();
|
|
@@ -12669,7 +12882,11 @@ var Launcher = class {
|
|
|
12669
12882
|
ca: this.ca
|
|
12670
12883
|
});
|
|
12671
12884
|
enableCdp(this.net, this.cdp);
|
|
12672
|
-
const fs = withCommandDirs(
|
|
12885
|
+
const fs = withCommandDirs(
|
|
12886
|
+
this.base,
|
|
12887
|
+
async () => new Set((await this.commands()).keys()),
|
|
12888
|
+
(dir) => this.userCommandDir(dir)
|
|
12889
|
+
);
|
|
12673
12890
|
this.nodes = new VfsNodes(fs, (path) => this.released(path));
|
|
12674
12891
|
this.nodes.keepsOnDisk = true;
|
|
12675
12892
|
this.openFiles.add(this.nodes);
|
|
@@ -12687,35 +12904,99 @@ var Launcher = class {
|
|
|
12687
12904
|
this.fs = this.unlinked.wrap(keepingOpen(fs, this.nodes));
|
|
12688
12905
|
this.users = new UserDb(this.fs);
|
|
12689
12906
|
void this.unlinked.sweep("/");
|
|
12690
|
-
this.watchers.watch(
|
|
12691
|
-
this.
|
|
12692
|
-
|
|
12907
|
+
this.watchers.watch(
|
|
12908
|
+
[this.modulesDir, this.pnpmHome],
|
|
12909
|
+
{ recursive: true },
|
|
12910
|
+
() => this.resetCatalogs()
|
|
12911
|
+
);
|
|
12912
|
+
this.watchers.watch([PASSWD], { recursive: false }, () => {
|
|
12913
|
+
this.scopes = void 0;
|
|
12914
|
+
this.resetCatalogs();
|
|
12693
12915
|
});
|
|
12694
12916
|
}
|
|
12695
12917
|
setRoutes(table) {
|
|
12696
12918
|
this.routes.set(table);
|
|
12697
12919
|
}
|
|
12698
|
-
commands() {
|
|
12699
|
-
|
|
12700
|
-
|
|
12920
|
+
commands(scope = "") {
|
|
12921
|
+
let found = this.catalogs.get(scope);
|
|
12922
|
+
if (!found) {
|
|
12923
|
+
found = this.roots(scope).then((roots) => scanCommands(this.base, roots));
|
|
12924
|
+
this.catalogs.set(scope, found);
|
|
12925
|
+
}
|
|
12926
|
+
return found;
|
|
12701
12927
|
}
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
);
|
|
12710
|
-
return this.binfmts;
|
|
12928
|
+
ownCommands(scope) {
|
|
12929
|
+
let found = this.ownCatalogs.get(scope);
|
|
12930
|
+
if (!found) {
|
|
12931
|
+
found = pnpmGlobalRoots(this.base, scope).then((roots) => scanCommands(this.base, roots)).then((commands) => new Set(commands.keys()));
|
|
12932
|
+
this.ownCatalogs.set(scope, found);
|
|
12933
|
+
}
|
|
12934
|
+
return found;
|
|
12711
12935
|
}
|
|
12712
|
-
|
|
12713
|
-
|
|
12936
|
+
interpreters(scope = "") {
|
|
12937
|
+
let found = this.binfmts.get(scope);
|
|
12938
|
+
if (!found) {
|
|
12939
|
+
found = this.roots(scope).then(
|
|
12940
|
+
(roots) => scanBinfmts(this.base, roots, (message3) => {
|
|
12941
|
+
if (this.warned.has(message3)) return;
|
|
12942
|
+
this.warned.add(message3);
|
|
12943
|
+
console.warn(message3);
|
|
12944
|
+
})
|
|
12945
|
+
);
|
|
12946
|
+
this.binfmts.set(scope, found);
|
|
12947
|
+
}
|
|
12948
|
+
return found;
|
|
12714
12949
|
}
|
|
12715
|
-
async
|
|
12716
|
-
const
|
|
12717
|
-
|
|
12718
|
-
|
|
12950
|
+
async roots(scope = "") {
|
|
12951
|
+
const system = [this.modulesDir, ...await pnpmGlobalRoots(this.base, this.pnpmHome)];
|
|
12952
|
+
return scope ? [...await pnpmGlobalRoots(this.base, scope), ...system] : system;
|
|
12953
|
+
}
|
|
12954
|
+
resetCatalogs() {
|
|
12955
|
+
this.catalogs.clear();
|
|
12956
|
+
this.ownCatalogs.clear();
|
|
12957
|
+
this.binfmts.clear();
|
|
12958
|
+
}
|
|
12959
|
+
userScopes() {
|
|
12960
|
+
this.scopes ??= this.users.list().then(
|
|
12961
|
+
(accounts) => {
|
|
12962
|
+
const users = accounts.filter((account) => account.uid !== 0);
|
|
12963
|
+
const homes = users.map((account) => pnpmHomeOf(account, this.pnpmHome));
|
|
12964
|
+
this.unwatchScopes();
|
|
12965
|
+
this.unwatchScopes = this.watchers.watch(
|
|
12966
|
+
homes,
|
|
12967
|
+
{ recursive: true },
|
|
12968
|
+
() => this.resetCatalogs()
|
|
12969
|
+
);
|
|
12970
|
+
return {
|
|
12971
|
+
byUid: new Map(users.map((account, i) => [account.uid, homes[i]])),
|
|
12972
|
+
byBin: new Map(homes.map((home) => [`${home}/bin`, home]))
|
|
12973
|
+
};
|
|
12974
|
+
},
|
|
12975
|
+
() => ({ byUid: /* @__PURE__ */ new Map(), byBin: /* @__PURE__ */ new Map() })
|
|
12976
|
+
);
|
|
12977
|
+
return this.scopes;
|
|
12978
|
+
}
|
|
12979
|
+
async userCommandDir(dir) {
|
|
12980
|
+
const scope = (await this.userScopes()).byBin.get(dir);
|
|
12981
|
+
return scope === void 0 ? void 0 : this.ownCommands(scope);
|
|
12982
|
+
}
|
|
12983
|
+
async scopeOf(uid) {
|
|
12984
|
+
return (await this.userScopes()).byUid.get(uid) ?? "";
|
|
12985
|
+
}
|
|
12986
|
+
async commandAt(file, scope) {
|
|
12987
|
+
const system = COMMAND2.exec(file)?.[1];
|
|
12988
|
+
if (system !== void 0) return (await this.commands()).get(system);
|
|
12989
|
+
if (!file.includes("/")) return (await this.commands(scope)).get(file);
|
|
12990
|
+
const slash = file.lastIndexOf("/");
|
|
12991
|
+
const owner = (await this.userScopes()).byBin.get(file.slice(0, slash));
|
|
12992
|
+
if (owner === void 0 || !(await this.ownCommands(owner)).has(file.slice(slash + 1))) {
|
|
12993
|
+
return void 0;
|
|
12994
|
+
}
|
|
12995
|
+
return (await this.commands(owner)).get(file.slice(slash + 1));
|
|
12996
|
+
}
|
|
12997
|
+
async resolve(file, argv0, cwd, scope = "") {
|
|
12998
|
+
if (!file.includes("/") || COMMAND2.test(file)) {
|
|
12999
|
+
const command = await this.commandAt(file, scope);
|
|
12719
13000
|
return command && !command.script ? targetOf(command) : void 0;
|
|
12720
13001
|
}
|
|
12721
13002
|
const path = this.fs.resolvePath(cwd, file);
|
|
@@ -12726,6 +13007,8 @@ var Launcher = class {
|
|
|
12726
13007
|
const byPath = !COMMAND2.test(path) && (await this.commands()).get(linked)?.argv0Path === true;
|
|
12727
13008
|
return target && byPath ? { ...target, argv0: path } : target;
|
|
12728
13009
|
}
|
|
13010
|
+
const own = await this.commandAt(glue, scope);
|
|
13011
|
+
if (own) return own.script ? void 0 : targetOf(own);
|
|
12729
13012
|
const head = await this.head(glue);
|
|
12730
13013
|
if (!head) return void 0;
|
|
12731
13014
|
if (isWasm(head)) {
|
|
@@ -12748,45 +13031,45 @@ var Launcher = class {
|
|
|
12748
13031
|
return void 0;
|
|
12749
13032
|
}
|
|
12750
13033
|
}
|
|
12751
|
-
async scriptCommand(file) {
|
|
12752
|
-
const
|
|
12753
|
-
|
|
12754
|
-
const command =
|
|
13034
|
+
async scriptCommand(file, cwd, scope) {
|
|
13035
|
+
const direct = !file.includes("/") || COMMAND2.test(file);
|
|
13036
|
+
const at = direct ? file : await followLinks(this.base, this.fs.resolvePath(cwd, file));
|
|
13037
|
+
const command = await this.commandAt(at, scope);
|
|
12755
13038
|
return command?.script ? command : void 0;
|
|
12756
13039
|
}
|
|
12757
|
-
async interpreted(file, argv, cwd, depth = 0) {
|
|
12758
|
-
const command = await this.scriptCommand(file);
|
|
13040
|
+
async interpreted(file, argv, cwd, scope, depth = 0) {
|
|
13041
|
+
const command = await this.scriptCommand(file, cwd, scope);
|
|
12759
13042
|
const script = command?.script ?? this.fs.resolvePath(cwd, file);
|
|
12760
13043
|
const head = await this.head(script);
|
|
12761
13044
|
if (!head) return void 0;
|
|
12762
|
-
const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(), script);
|
|
13045
|
+
const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(scope), script);
|
|
12763
13046
|
const words = shebang(head) ?? (binfmt ? [binfmt] : void 0);
|
|
12764
13047
|
if (!words) return void 0;
|
|
12765
13048
|
if (baseName(words[0]) === "env") words.shift();
|
|
12766
13049
|
const [interp, ...rest2] = words;
|
|
12767
13050
|
const arg = rest2.join(" ");
|
|
12768
13051
|
const passed = [...arg ? [arg] : [], command ? script : file, ...argv.slice(1)];
|
|
12769
|
-
const found = interp ? await this.resolve(interp, interp, cwd) : void 0;
|
|
13052
|
+
const found = interp ? await this.resolve(interp, interp, cwd, scope) : void 0;
|
|
12770
13053
|
if (!found) {
|
|
12771
|
-
if (!interp || depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp)) {
|
|
13054
|
+
if (!interp || depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp, cwd, scope)) {
|
|
12772
13055
|
return void 0;
|
|
12773
13056
|
}
|
|
12774
|
-
const inner = await this.interpreted(interp, [interp, ...passed], cwd, depth + 1);
|
|
13057
|
+
const inner = await this.interpreted(interp, [interp, ...passed], cwd, scope, depth + 1);
|
|
12775
13058
|
return inner && { ...inner, target: withScriptEnv(inner.target, command) };
|
|
12776
13059
|
}
|
|
12777
13060
|
const target = withScriptEnv(found, command);
|
|
12778
13061
|
return { target, args: [...target.prefix ?? [], ...passed] };
|
|
12779
13062
|
}
|
|
12780
|
-
async plan(file, argv, cwd) {
|
|
12781
|
-
const direct = await this.resolve(file, argv[0] ?? file, cwd);
|
|
13063
|
+
async plan(file, argv, cwd, scope) {
|
|
13064
|
+
const direct = await this.resolve(file, argv[0] ?? file, cwd, scope);
|
|
12782
13065
|
if (direct) return { target: direct, args: [...direct.prefix ?? [], ...argv.slice(1)] };
|
|
12783
|
-
return this.interpreted(file, argv, cwd);
|
|
13066
|
+
return this.interpreted(file, argv, cwd, scope);
|
|
12784
13067
|
}
|
|
12785
|
-
async unrunnable(file, cwd) {
|
|
13068
|
+
async unrunnable(file, cwd, scope) {
|
|
12786
13069
|
const path = this.fs.resolvePath(cwd, file);
|
|
12787
13070
|
const head = await this.base.readFileBuffer(path).catch(() => null);
|
|
12788
13071
|
if (!head || head[0] === 35 && head[1] === 33) return "ENOENT";
|
|
12789
|
-
return binfmtOf(await this.interpreters(), path) ? "ENOENT" : "ENOEXEC";
|
|
13072
|
+
return binfmtOf(await this.interpreters(scope), path) ? "ENOENT" : "ENOEXEC";
|
|
12790
13073
|
}
|
|
12791
13074
|
module(path) {
|
|
12792
13075
|
return this.base.stat(path).then((st) => {
|
|
@@ -12996,10 +13279,11 @@ var Launcher = class {
|
|
|
12996
13279
|
}
|
|
12997
13280
|
spawner(ppid, report) {
|
|
12998
13281
|
return async (req, fds) => {
|
|
12999
|
-
const
|
|
13282
|
+
const scope = await this.scopeOf(this.credOf(ppid).euid);
|
|
13283
|
+
const planned = await this.plan(req.file, req.argv, req.cwd, scope);
|
|
13000
13284
|
if (!planned) {
|
|
13001
13285
|
await fds.closeAll();
|
|
13002
|
-
throw new SpawnError(await this.unrunnable(req.file, req.cwd));
|
|
13286
|
+
throw new SpawnError(await this.unrunnable(req.file, req.cwd, scope));
|
|
13003
13287
|
}
|
|
13004
13288
|
const exec = req.exec ? { exec: true } : {};
|
|
13005
13289
|
const deciding = req.exec ? void 0 : this.undecided(ppid);
|
|
@@ -13118,11 +13402,7 @@ var Launcher = class {
|
|
|
13118
13402
|
throw fsError("ENODEV", "this kernel has no hostfs hook (createKernel({ hostfs }))");
|
|
13119
13403
|
return openHostfs(
|
|
13120
13404
|
spec,
|
|
13121
|
-
grantConnect(
|
|
13122
|
-
hostfs,
|
|
13123
|
-
hostfsFetch ?? ((url, init) => fetch(url, init)),
|
|
13124
|
-
hostfsTiming?.requestMs
|
|
13125
|
-
),
|
|
13405
|
+
grantConnect(hostfs, hostfsFetch ?? ((url, init) => fetch(url, init)), hostfsTiming),
|
|
13126
13406
|
hostfsTiming
|
|
13127
13407
|
);
|
|
13128
13408
|
}
|
|
@@ -13305,18 +13585,18 @@ var Launcher = class {
|
|
|
13305
13585
|
...account.umask !== void 0 ? { umask: account.umask } : {}
|
|
13306
13586
|
};
|
|
13307
13587
|
}
|
|
13308
|
-
async starting(argv, dir) {
|
|
13309
|
-
this.
|
|
13310
|
-
|
|
13588
|
+
async starting(argv, dir, account) {
|
|
13589
|
+
this.resetCatalogs();
|
|
13590
|
+
const scope = account.uid === 0 ? "" : pnpmHomeOf(account, this.pnpmHome);
|
|
13311
13591
|
const start2 = dir ?? "/";
|
|
13312
13592
|
const cwd = await physicalPathAsync(this.base, start2.startsWith("/") ? start2 : `/${start2}`);
|
|
13313
13593
|
const [file = ""] = argv;
|
|
13314
13594
|
await this.base.mkdir(cwd, { recursive: true });
|
|
13315
|
-
return { cwd, file, planned: await this.plan(file, argv, cwd) };
|
|
13595
|
+
return { cwd, file, planned: await this.plan(file, argv, cwd, scope) };
|
|
13316
13596
|
}
|
|
13317
13597
|
async openTerminal(argv, options) {
|
|
13318
13598
|
const account = await this.account(options.user);
|
|
13319
|
-
const { cwd, file, planned } = await this.starting(argv, options.cwd);
|
|
13599
|
+
const { cwd, file, planned } = await this.starting(argv, options.cwd, account);
|
|
13320
13600
|
if (!planned) throw new Error(`${file}: command not found`);
|
|
13321
13601
|
let leader = 0;
|
|
13322
13602
|
const tty = new KernelTty(
|
|
@@ -13359,7 +13639,7 @@ var Launcher = class {
|
|
|
13359
13639
|
}
|
|
13360
13640
|
async run(argv, options = {}) {
|
|
13361
13641
|
const account = await this.account(options.user);
|
|
13362
|
-
const { cwd, file, planned } = await this.starting(argv, options.cwd);
|
|
13642
|
+
const { cwd, file, planned } = await this.starting(argv, options.cwd, account);
|
|
13363
13643
|
const env = this.environment(cwd, options.env, account);
|
|
13364
13644
|
const out = [];
|
|
13365
13645
|
const err = [];
|