@ai-ecoverse/slicc-kernel 1.59.0 → 1.60.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 +22 -3
- package/dist/index.js +37 -8
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +78 -13
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-process-worker.js +1 -1
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +172 -38
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +1 -1
- package/dist/process-worker.js.map +2 -2
- package/package.json +1 -1
package/dist/node.js
CHANGED
|
@@ -1323,6 +1323,11 @@ function blockedReason(url, e) {
|
|
|
1323
1323
|
}
|
|
1324
1324
|
return `${url}: ${e instanceof Error ? e.message : String(e)}`;
|
|
1325
1325
|
}
|
|
1326
|
+
function unopened(url, code) {
|
|
1327
|
+
return Object.assign(new Error(`${url}: the WebSocket closed (${code}) before it opened`), {
|
|
1328
|
+
status: 502
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1326
1331
|
function openWebSocket(Socket, request, withHeaders) {
|
|
1327
1332
|
const { url, protocols, signal } = request;
|
|
1328
1333
|
signal.throwIfAborted();
|
|
@@ -1336,13 +1341,22 @@ function openWebSocket(Socket, request, withHeaders) {
|
|
|
1336
1341
|
const queue = new MessageQueue(() => socket.close(1e3, "too many waiting messages"));
|
|
1337
1342
|
return new Promise((resolve, reject) => {
|
|
1338
1343
|
let open = false;
|
|
1344
|
+
let settled = false;
|
|
1345
|
+
const fail3 = (error) => {
|
|
1346
|
+
if (settled) return;
|
|
1347
|
+
settled = true;
|
|
1348
|
+
signal.removeEventListener("abort", abort);
|
|
1349
|
+
reject(error);
|
|
1350
|
+
};
|
|
1339
1351
|
const abort = () => {
|
|
1340
1352
|
socket.close(1e3, "aborted");
|
|
1341
|
-
|
|
1353
|
+
fail3(signal.reason);
|
|
1342
1354
|
};
|
|
1343
1355
|
signal.addEventListener("abort", abort, { once: true });
|
|
1344
1356
|
socket.addEventListener("open", () => {
|
|
1357
|
+
if (settled) return;
|
|
1345
1358
|
open = true;
|
|
1359
|
+
settled = true;
|
|
1346
1360
|
signal.removeEventListener("abort", abort);
|
|
1347
1361
|
resolve({
|
|
1348
1362
|
protocol: socket.protocol,
|
|
@@ -1358,15 +1372,19 @@ function openWebSocket(Socket, request, withHeaders) {
|
|
|
1358
1372
|
socket.addEventListener("message", (event) => {
|
|
1359
1373
|
queue.push(typeof event.data === "string" ? event.data : new Uint8Array(event.data));
|
|
1360
1374
|
});
|
|
1375
|
+
socket.addEventListener("error", () => {
|
|
1376
|
+
if (open || settled) return;
|
|
1377
|
+
try {
|
|
1378
|
+
socket.close();
|
|
1379
|
+
} catch {
|
|
1380
|
+
}
|
|
1381
|
+
queue.end({ code: 1006, reason: "" });
|
|
1382
|
+
fail3(unopened(url, 1006));
|
|
1383
|
+
});
|
|
1361
1384
|
socket.addEventListener("close", (event) => {
|
|
1362
1385
|
queue.end({ code: event.code, reason: event.reason });
|
|
1363
1386
|
if (open) return;
|
|
1364
|
-
|
|
1365
|
-
reject(
|
|
1366
|
-
Object.assign(new Error(`${url}: the WebSocket closed (${event.code}) before it opened`), {
|
|
1367
|
-
status: 502
|
|
1368
|
-
})
|
|
1369
|
-
);
|
|
1387
|
+
fail3(unopened(url, event.code));
|
|
1370
1388
|
});
|
|
1371
1389
|
});
|
|
1372
1390
|
}
|
|
@@ -1756,6 +1774,29 @@ function userEnv(account, system) {
|
|
|
1756
1774
|
PATH: `${pnpm}/bin:/usr/local/bin:/usr/bin:/bin`
|
|
1757
1775
|
};
|
|
1758
1776
|
}
|
|
1777
|
+
function groupsOf(groups, names) {
|
|
1778
|
+
return names.map((g) => {
|
|
1779
|
+
const row = groups.find((r) => r.name === g);
|
|
1780
|
+
if (!row) throw fsError("ENOENT", `no group ${g}`);
|
|
1781
|
+
return row;
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
function homeModeOf(mode) {
|
|
1785
|
+
if (mode === void 0) return void 0;
|
|
1786
|
+
if (!Number.isInteger(mode) || mode < 0 || mode > 4095) {
|
|
1787
|
+
throw fsError("EINVAL", `not a mode: ${String(mode)}`);
|
|
1788
|
+
}
|
|
1789
|
+
return mode;
|
|
1790
|
+
}
|
|
1791
|
+
function joined(name2, rows) {
|
|
1792
|
+
let changed = false;
|
|
1793
|
+
for (const g of rows) {
|
|
1794
|
+
if (g.members.includes(name2)) continue;
|
|
1795
|
+
g.members.push(name2);
|
|
1796
|
+
changed = true;
|
|
1797
|
+
}
|
|
1798
|
+
return changed;
|
|
1799
|
+
}
|
|
1759
1800
|
function homeGroupOf(groups, name2) {
|
|
1760
1801
|
if (name2 === void 0) return void 0;
|
|
1761
1802
|
const found = groups.find((g) => g.name === name2);
|
|
@@ -1771,14 +1812,14 @@ var UserDb = class {
|
|
|
1771
1812
|
this.fs = fs;
|
|
1772
1813
|
this.owners = owners;
|
|
1773
1814
|
}
|
|
1774
|
-
async stampHome(row, shared) {
|
|
1815
|
+
async stampHome(row, shared, homeMode) {
|
|
1775
1816
|
const home = this.fs.resolvePath("/", row.home);
|
|
1776
1817
|
if (!this.owners || !this.fs.chown || !HOME_DIR.test(home)) return;
|
|
1777
1818
|
if (!await this.realDir(home)) return;
|
|
1778
1819
|
const gid = shared?.gid ?? row.gid;
|
|
1779
1820
|
await this.fs.chown(home, { uid: row.uid, gid, follow: false, kind: "adopt" });
|
|
1780
1821
|
const scoop = row.name.startsWith("scoop-");
|
|
1781
|
-
await this.fs.chmod(home, shared ? 1512 : scoop ? 488 : 448);
|
|
1822
|
+
await this.fs.chmod(home, homeMode ?? (shared ? 1512 : scoop ? 488 : 448));
|
|
1782
1823
|
const pnpm = `${home}/.local/share/pnpm`;
|
|
1783
1824
|
if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
|
|
1784
1825
|
}
|
|
@@ -1892,31 +1933,33 @@ var UserDb = class {
|
|
|
1892
1933
|
const groups = parsed(group);
|
|
1893
1934
|
const found = users.find((r) => r.name === name2);
|
|
1894
1935
|
const shared = homeGroupOf(groups, options.homeGroup);
|
|
1936
|
+
const homeMode = homeModeOf(options.homeMode);
|
|
1895
1937
|
if (found) {
|
|
1938
|
+
const more = groupsOf(groups, options.groups ?? []);
|
|
1939
|
+
if (more.some((g) => g.gid === found.gid)) {
|
|
1940
|
+
throw fsError("EINVAL", `a user is no member of its own group ${name2}`);
|
|
1941
|
+
}
|
|
1896
1942
|
if (found.uid >= FIRST_UID && found.uid <= LAST_UID) {
|
|
1897
1943
|
await this.provision(found);
|
|
1898
|
-
await this.stampHome(found, shared);
|
|
1944
|
+
await this.stampHome(found, shared, homeMode);
|
|
1899
1945
|
}
|
|
1946
|
+
if (joined(name2, more)) await this.fs.writeFile(GROUP, formatGroup(group));
|
|
1900
1947
|
remember();
|
|
1901
1948
|
return this.account(found, groups);
|
|
1902
1949
|
}
|
|
1903
1950
|
const home = homeOf(options.home ?? `/home/${name2}`);
|
|
1904
|
-
const extra = [USERS_GROUP, ...options.groups ?? []]
|
|
1905
|
-
const row2 = groups.find((r) => r.name === g);
|
|
1906
|
-
if (!row2) throw fsError("ENOENT", `no group ${g}`);
|
|
1907
|
-
return row2;
|
|
1908
|
-
});
|
|
1951
|
+
const extra = groupsOf(groups, [USERS_GROUP, ...options.groups ?? []]);
|
|
1909
1952
|
if (groups.some((r) => r.name === name2)) throw fsError("EEXIST", `group ${name2} exists`);
|
|
1910
1953
|
const uid = freeId(users, groups);
|
|
1911
1954
|
const row = { name: name2, uid, gid: uid, gecos: name2, home, shell: "/bin/bash" };
|
|
1912
|
-
|
|
1955
|
+
joined(name2, extra);
|
|
1913
1956
|
group.push({ name: name2, gid: uid, members: [] });
|
|
1914
1957
|
passwd.push(row);
|
|
1915
1958
|
const fresh = !await this.fs.exists(home);
|
|
1916
1959
|
await this.fs.mkdir(home, { recursive: true });
|
|
1917
1960
|
try {
|
|
1918
1961
|
await this.provision(row);
|
|
1919
|
-
await this.stampHome(row, shared);
|
|
1962
|
+
await this.stampHome(row, shared, homeMode);
|
|
1920
1963
|
await this.fs.mkdir("/etc", { recursive: true });
|
|
1921
1964
|
await this.commit(formatPasswd(passwd), formatGroup(group));
|
|
1922
1965
|
} catch (err) {
|
|
@@ -1927,6 +1970,31 @@ var UserDb = class {
|
|
|
1927
1970
|
return this.account(row, parsed(group));
|
|
1928
1971
|
});
|
|
1929
1972
|
}
|
|
1973
|
+
async groups() {
|
|
1974
|
+
const { group } = await this.tables();
|
|
1975
|
+
return parsed(group).map(({ name: name2, gid, members }) => ({ name: name2, gid, members: [...members] }));
|
|
1976
|
+
}
|
|
1977
|
+
addMember(group, user) {
|
|
1978
|
+
return this.member(group, user, true);
|
|
1979
|
+
}
|
|
1980
|
+
removeMember(group, user) {
|
|
1981
|
+
return this.member(group, user, false);
|
|
1982
|
+
}
|
|
1983
|
+
member(name2, user, on) {
|
|
1984
|
+
return this.serial(async () => {
|
|
1985
|
+
const { passwd, group } = await this.tables();
|
|
1986
|
+
const row = parsed(group).find((g) => g.name === name2);
|
|
1987
|
+
if (!row) throw fsError("ENOENT", `no group ${String(name2)}`);
|
|
1988
|
+
const who = parsed(passwd).find((u) => u.name === user);
|
|
1989
|
+
if (!who) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
1990
|
+
if (who.gid === row.gid) throw fsError("EINVAL", `${name2} is the own group of ${user}`);
|
|
1991
|
+
if (row.members.includes(user) !== on) {
|
|
1992
|
+
row.members = on ? [...row.members, user] : row.members.filter((m) => m !== user);
|
|
1993
|
+
await this.fs.writeFile(GROUP, formatGroup(group));
|
|
1994
|
+
}
|
|
1995
|
+
return { name: row.name, gid: row.gid, members: [...row.members] };
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1930
1998
|
remove(name2, options = {}) {
|
|
1931
1999
|
return this.serial(async () => {
|
|
1932
2000
|
const { passwd, group } = await this.tables();
|
|
@@ -8399,7 +8467,7 @@ function enableNetwork(net, options) {
|
|
|
8399
8467
|
let proxy;
|
|
8400
8468
|
const transport = options.transport ?? missingTransport();
|
|
8401
8469
|
net.activate({ family: "inet", host: "127.0.0.1", port: REALM_PROXY_PORT }, () => {
|
|
8402
|
-
const
|
|
8470
|
+
const tls2 = new TlsTerminator({
|
|
8403
8471
|
ca: options.ca,
|
|
8404
8472
|
engine: options.engine,
|
|
8405
8473
|
...options.now ? { now: options.now } : {}
|
|
@@ -8407,7 +8475,7 @@ function enableNetwork(net, options) {
|
|
|
8407
8475
|
const started = new RealmProxy({
|
|
8408
8476
|
net,
|
|
8409
8477
|
transport,
|
|
8410
|
-
tunnel:
|
|
8478
|
+
tunnel: tls2.handler,
|
|
8411
8479
|
tunnelReady: async () => {
|
|
8412
8480
|
await options.engine();
|
|
8413
8481
|
}
|
|
@@ -8415,7 +8483,7 @@ function enableNetwork(net, options) {
|
|
|
8415
8483
|
proxy = started;
|
|
8416
8484
|
void started.closed.then(async () => {
|
|
8417
8485
|
if (proxy === started) proxy = void 0;
|
|
8418
|
-
await
|
|
8486
|
+
await tls2.close();
|
|
8419
8487
|
});
|
|
8420
8488
|
});
|
|
8421
8489
|
return () => proxy;
|
|
@@ -8434,6 +8502,11 @@ var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
|
8434
8502
|
function message(e) {
|
|
8435
8503
|
return e instanceof Error ? e.message : String(e);
|
|
8436
8504
|
}
|
|
8505
|
+
var ERRNO = /^E[A-Z]+$|^EAI_[A-Z]+$/;
|
|
8506
|
+
function errnoOf(e) {
|
|
8507
|
+
const code = e?.code;
|
|
8508
|
+
return typeof code === "string" && ERRNO.test(code) ? code : "ECONNREFUSED";
|
|
8509
|
+
}
|
|
8437
8510
|
function fail(errno, text) {
|
|
8438
8511
|
return { ok: false, errno, message: text };
|
|
8439
8512
|
}
|
|
@@ -8477,13 +8550,13 @@ var WsHandles = class {
|
|
|
8477
8550
|
let socket;
|
|
8478
8551
|
try {
|
|
8479
8552
|
socket = await transport.websocket({
|
|
8480
|
-
url: req.url,
|
|
8553
|
+
url: toHostLoopback(req.url),
|
|
8481
8554
|
protocols: req.protocols,
|
|
8482
8555
|
headers: req.headers,
|
|
8483
8556
|
signal: entry.abort.signal
|
|
8484
8557
|
});
|
|
8485
8558
|
} catch (e) {
|
|
8486
|
-
return fail(
|
|
8559
|
+
return fail(errnoOf(e), message(e));
|
|
8487
8560
|
}
|
|
8488
8561
|
if (!this.open.has(handle)) {
|
|
8489
8562
|
socket.close(1001, "the program went away");
|
|
@@ -8838,14 +8911,14 @@ var Resolver = class {
|
|
|
8838
8911
|
if (hit && hit.expires > this.now()) return hit.addresses;
|
|
8839
8912
|
const signal = blocking?.();
|
|
8840
8913
|
if (signal?.aborted) throw new KernelError("EINTR");
|
|
8841
|
-
let
|
|
8842
|
-
if (!
|
|
8914
|
+
let lookup2 = this.pending.get(id2);
|
|
8915
|
+
if (!lookup2) {
|
|
8843
8916
|
const abort = new AbortController();
|
|
8844
|
-
|
|
8845
|
-
this.pending.set(id2,
|
|
8846
|
-
void
|
|
8917
|
+
lookup2 = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
|
|
8918
|
+
this.pending.set(id2, lookup2);
|
|
8919
|
+
void lookup2.work.then(() => this.pending.delete(id2));
|
|
8847
8920
|
}
|
|
8848
|
-
return waitFor(
|
|
8921
|
+
return waitFor(lookup2, signal);
|
|
8849
8922
|
}
|
|
8850
8923
|
async lookup(name2, family, id2, abandoned) {
|
|
8851
8924
|
const uplink = this.uplink;
|
|
@@ -8886,17 +8959,17 @@ var Resolver = class {
|
|
|
8886
8959
|
}
|
|
8887
8960
|
}
|
|
8888
8961
|
};
|
|
8889
|
-
function waitFor(
|
|
8890
|
-
|
|
8962
|
+
function waitFor(lookup2, signal) {
|
|
8963
|
+
lookup2.waiters++;
|
|
8891
8964
|
return new Promise((resolve, reject) => {
|
|
8892
8965
|
const abort = () => {
|
|
8893
|
-
if (--
|
|
8966
|
+
if (--lookup2.waiters === 0) lookup2.abort.abort(new KernelError("EINTR"));
|
|
8894
8967
|
reject(new KernelError("EINTR"));
|
|
8895
8968
|
};
|
|
8896
8969
|
signal?.addEventListener("abort", abort, { once: true });
|
|
8897
|
-
void
|
|
8970
|
+
void lookup2.work.then((value) => {
|
|
8898
8971
|
if (signal?.aborted) return;
|
|
8899
|
-
|
|
8972
|
+
lookup2.waiters--;
|
|
8900
8973
|
signal?.removeEventListener("abort", abort);
|
|
8901
8974
|
resolve(value);
|
|
8902
8975
|
});
|
|
@@ -14490,6 +14563,62 @@ function isMemoryRoot(handle) {
|
|
|
14490
14563
|
return handle instanceof MemoryDirectory;
|
|
14491
14564
|
}
|
|
14492
14565
|
|
|
14566
|
+
// src/node/ws-diagnose.ts
|
|
14567
|
+
import { lookup } from "node:dns/promises";
|
|
14568
|
+
import { isIP, connect as tcp } from "node:net";
|
|
14569
|
+
import { connect as tls } from "node:tls";
|
|
14570
|
+
var PROBE_MS = 3e3;
|
|
14571
|
+
var PASSED = /* @__PURE__ */ new Set(["ECONNREFUSED", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT", "ECONNRESET"]);
|
|
14572
|
+
var NO_NAME = /* @__PURE__ */ new Set(["ENOTFOUND", "EAI_AGAIN", "EAI_NONAME", "EAI_FAIL", "ENODATA"]);
|
|
14573
|
+
function codeOf5(e) {
|
|
14574
|
+
const code = e?.code;
|
|
14575
|
+
return typeof code === "string" ? code : void 0;
|
|
14576
|
+
}
|
|
14577
|
+
function probe(secure, host, port, ms) {
|
|
14578
|
+
return new Promise((resolve) => {
|
|
14579
|
+
const socket = secure ? tls({ host, port, ...isIP(host) ? {} : { servername: host } }) : tcp({ host, port });
|
|
14580
|
+
const done4 = (code) => {
|
|
14581
|
+
clearTimeout(timer);
|
|
14582
|
+
socket.destroy();
|
|
14583
|
+
resolve(code);
|
|
14584
|
+
};
|
|
14585
|
+
const timer = setTimeout(() => done4("ETIMEDOUT"), ms);
|
|
14586
|
+
socket.once(secure ? "secureConnect" : "connect", () => done4("EPROTO"));
|
|
14587
|
+
socket.once("error", (e) => {
|
|
14588
|
+
const code = codeOf5(e) ?? "";
|
|
14589
|
+
done4(PASSED.has(code) ? code : "EPROTO");
|
|
14590
|
+
});
|
|
14591
|
+
});
|
|
14592
|
+
}
|
|
14593
|
+
async function diagnose(url, ms = PROBE_MS) {
|
|
14594
|
+
const at = new URL(url);
|
|
14595
|
+
const host = at.hostname.replace(/^\[(.*)\]$/, "$1");
|
|
14596
|
+
const secure = at.protocol === "wss:";
|
|
14597
|
+
const port = Number(at.port) || (secure ? 443 : 80);
|
|
14598
|
+
try {
|
|
14599
|
+
await lookup(host);
|
|
14600
|
+
} catch (e) {
|
|
14601
|
+
return NO_NAME.has(codeOf5(e) ?? "") ? "EAI_NONAME" : void 0;
|
|
14602
|
+
}
|
|
14603
|
+
return probe(secure, host, port, ms);
|
|
14604
|
+
}
|
|
14605
|
+
function diagnosing(transport, check = diagnose) {
|
|
14606
|
+
const open = transport.websocket;
|
|
14607
|
+
if (!open) return transport;
|
|
14608
|
+
return {
|
|
14609
|
+
...transport,
|
|
14610
|
+
websocket: async (request) => {
|
|
14611
|
+
try {
|
|
14612
|
+
return await open(request);
|
|
14613
|
+
} catch (e) {
|
|
14614
|
+
if (request.signal.aborted || !(e instanceof Error) || codeOf5(e)) throw e;
|
|
14615
|
+
const code = await check(request.url).catch(() => void 0);
|
|
14616
|
+
throw code ? Object.assign(e, { code }) : e;
|
|
14617
|
+
}
|
|
14618
|
+
}
|
|
14619
|
+
};
|
|
14620
|
+
}
|
|
14621
|
+
|
|
14493
14622
|
// src/kernel/net/remote-transport.ts
|
|
14494
14623
|
var TransportError = class extends Error {
|
|
14495
14624
|
status;
|
|
@@ -14752,10 +14881,10 @@ var ByteSource = class {
|
|
|
14752
14881
|
async more() {
|
|
14753
14882
|
const { done: done4, value } = await this.reader.read();
|
|
14754
14883
|
if (done4) return false;
|
|
14755
|
-
const
|
|
14756
|
-
|
|
14757
|
-
|
|
14758
|
-
this.buffered =
|
|
14884
|
+
const joined2 = new Uint8Array(this.buffered.length + value.length);
|
|
14885
|
+
joined2.set(this.buffered);
|
|
14886
|
+
joined2.set(value, this.buffered.length);
|
|
14887
|
+
this.buffered = joined2;
|
|
14759
14888
|
return true;
|
|
14760
14889
|
}
|
|
14761
14890
|
async until(marker) {
|
|
@@ -15284,7 +15413,7 @@ function hostfsFetchOf(options) {
|
|
|
15284
15413
|
}
|
|
15285
15414
|
function nodeTransport() {
|
|
15286
15415
|
const transport = fetchTransport({ webSocket: true, webSocketHeaders: true, redirect: "manual" });
|
|
15287
|
-
return { ...transport, traits: { ...transport.traits, crossOrigin: "any" } };
|
|
15416
|
+
return diagnosing({ ...transport, traits: { ...transport.traits, crossOrigin: "any" } });
|
|
15288
15417
|
}
|
|
15289
15418
|
var TERMINATED = "the kernel is terminated";
|
|
15290
15419
|
var EXIT_GRACE_MS = 50;
|
|
@@ -15443,6 +15572,11 @@ async function createNodeKernel(options = {}) {
|
|
|
15443
15572
|
remove: (name2, options2) => guard(() => launcher.users.remove(name2, options2)),
|
|
15444
15573
|
list: () => guard(() => launcher.users.list())
|
|
15445
15574
|
},
|
|
15575
|
+
groups: {
|
|
15576
|
+
list: () => guard(() => launcher.users.groups()),
|
|
15577
|
+
addMember: (group, user) => guard(() => launcher.users.addMember(group, user)),
|
|
15578
|
+
removeMember: (group, user) => guard(() => launcher.users.removeMember(group, user))
|
|
15579
|
+
},
|
|
15446
15580
|
async connect({ user } = {}) {
|
|
15447
15581
|
if (terminated) throw new Error(TERMINATED);
|
|
15448
15582
|
if (user !== void 0 && !await launcher.users.lookup(user)) {
|