@ai-ecoverse/slicc-kernel 1.59.1 → 1.60.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 +21 -2
- package/dist/index.js +12 -1
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +159 -24
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node.js +157 -27
- package/dist/node.js.map +3 -3
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -294,10 +294,31 @@ var ERRNO_BY_DOM_ERROR = {
|
|
|
294
294
|
TypeError: "EINVAL",
|
|
295
295
|
NotAllowedError: "EACCES"
|
|
296
296
|
};
|
|
297
|
-
|
|
297
|
+
var reported = /* @__PURE__ */ new Set();
|
|
298
|
+
function translate(err, path, op = "lookup", mapped = true) {
|
|
298
299
|
if (typeof err?.code === "string") return err;
|
|
299
|
-
const
|
|
300
|
-
|
|
300
|
+
const raw = err?.name;
|
|
301
|
+
const name2 = typeof raw === "string" ? raw : "Error";
|
|
302
|
+
const code = mapped && ERRNO_BY_DOM_ERROR[name2] || "EIO";
|
|
303
|
+
const error = Object.assign(fsError(code, path), { domName: name2, op, cause: err });
|
|
304
|
+
if (code !== "EIO") return error;
|
|
305
|
+
const detail = err?.message;
|
|
306
|
+
error.message += ` (OPFS ${op}: ${name2}${typeof detail === "string" && detail ? `: ${detail}` : ""})`;
|
|
307
|
+
if (!reported.has(`${name2}/${op}`)) {
|
|
308
|
+
reported.add(`${name2}/${op}`);
|
|
309
|
+
console.warn(
|
|
310
|
+
`slicc-kernel: ${error.message}, reported as EIO; logged once per error and operation`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
return error;
|
|
314
|
+
}
|
|
315
|
+
var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
|
|
316
|
+
function sidecar(meta) {
|
|
317
|
+
const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
|
|
318
|
+
const guarded = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
|
|
319
|
+
throw translate(err, pathOf(args), "sidecar", false);
|
|
320
|
+
});
|
|
321
|
+
return Object.fromEntries(META_OPS.map((op) => [op, guarded(op)]));
|
|
301
322
|
}
|
|
302
323
|
function missing(err) {
|
|
303
324
|
const code = err?.code;
|
|
@@ -386,7 +407,7 @@ var OpfsFs = class {
|
|
|
386
407
|
owners = false;
|
|
387
408
|
constructor(root, meta = new MemoryMeta(), channel, options = {}) {
|
|
388
409
|
this.root = root;
|
|
389
|
-
this.meta = meta;
|
|
410
|
+
this.meta = sidecar(meta);
|
|
390
411
|
this.ranged = options.ranged === true;
|
|
391
412
|
this.channel = channel === void 0 ? void 0 : new BroadcastChannel(channel);
|
|
392
413
|
if (this.channel) {
|
|
@@ -691,7 +712,8 @@ var OpfsFs = class {
|
|
|
691
712
|
} catch (err) {
|
|
692
713
|
const name2 = err?.name;
|
|
693
714
|
const again = name2 === "NotReadableError" || locks && LOCKED.has(name2);
|
|
694
|
-
if (!again || attempt >= READ_ATTEMPTS)
|
|
715
|
+
if (!again || attempt >= READ_ATTEMPTS)
|
|
716
|
+
throw translate(err, path, locks ? "write" : "read");
|
|
695
717
|
await new Promise((resolve) => setTimeout(resolve, attempt * 10));
|
|
696
718
|
}
|
|
697
719
|
}
|
|
@@ -733,9 +755,15 @@ var OpfsFs = class {
|
|
|
733
755
|
true
|
|
734
756
|
);
|
|
735
757
|
return {
|
|
736
|
-
write: (at, bytes) => writable.write({ type: "write", position: at, data: bytes })
|
|
737
|
-
|
|
738
|
-
|
|
758
|
+
write: (at, bytes) => writable.write({ type: "write", position: at, data: bytes }).catch((err) => {
|
|
759
|
+
throw translate(err, path, "write", false);
|
|
760
|
+
}),
|
|
761
|
+
truncate: (size) => writable.truncate(size).catch((err) => {
|
|
762
|
+
throw translate(err, path, "write", false);
|
|
763
|
+
}),
|
|
764
|
+
close: () => writable.close().catch((err) => {
|
|
765
|
+
throw translate(err, path, "commit", false);
|
|
766
|
+
})
|
|
739
767
|
};
|
|
740
768
|
}
|
|
741
769
|
async readFile(path) {
|
|
@@ -770,9 +798,13 @@ var OpfsFs = class {
|
|
|
770
798
|
const [file, existed, target] = await this.fileFor(path);
|
|
771
799
|
if (!existed) await this.stampNew(target, owner, false);
|
|
772
800
|
if (!existed && content.length === 0) return;
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
801
|
+
try {
|
|
802
|
+
const writable = await file.createWritable();
|
|
803
|
+
await writable.write(content);
|
|
804
|
+
await writable.close();
|
|
805
|
+
} catch (err) {
|
|
806
|
+
throw translate(err, path, "commit", false);
|
|
807
|
+
}
|
|
776
808
|
}
|
|
777
809
|
async listing(path) {
|
|
778
810
|
const found = await this.locate(path, true);
|
|
@@ -1048,6 +1080,49 @@ var OpfsFs = class {
|
|
|
1048
1080
|
return removed;
|
|
1049
1081
|
}
|
|
1050
1082
|
};
|
|
1083
|
+
var READS = [
|
|
1084
|
+
"stat",
|
|
1085
|
+
"lstat",
|
|
1086
|
+
"exists",
|
|
1087
|
+
"readFile",
|
|
1088
|
+
"readFileBuffer",
|
|
1089
|
+
"pread",
|
|
1090
|
+
"readdir",
|
|
1091
|
+
"readdirStat",
|
|
1092
|
+
"readlink",
|
|
1093
|
+
"realpath"
|
|
1094
|
+
];
|
|
1095
|
+
var WRITES = [
|
|
1096
|
+
"writeFile",
|
|
1097
|
+
"pwrite",
|
|
1098
|
+
"truncate",
|
|
1099
|
+
"writeSession",
|
|
1100
|
+
"mkdir",
|
|
1101
|
+
"rm",
|
|
1102
|
+
"link",
|
|
1103
|
+
"rename",
|
|
1104
|
+
"symlink",
|
|
1105
|
+
"chmod",
|
|
1106
|
+
"utimes",
|
|
1107
|
+
"lutimes",
|
|
1108
|
+
"chown"
|
|
1109
|
+
];
|
|
1110
|
+
for (const [names, op] of [
|
|
1111
|
+
[READS, "read"],
|
|
1112
|
+
[WRITES, "write"]
|
|
1113
|
+
]) {
|
|
1114
|
+
for (const name2 of names) {
|
|
1115
|
+
const proto = OpfsFs.prototype;
|
|
1116
|
+
const inner = proto[name2];
|
|
1117
|
+
proto[name2] = async function(...args) {
|
|
1118
|
+
try {
|
|
1119
|
+
return await inner.apply(this, args);
|
|
1120
|
+
} catch (err) {
|
|
1121
|
+
throw translate(err, String(args[0]), op, false);
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1051
1126
|
|
|
1052
1127
|
// src/mount/media.ts
|
|
1053
1128
|
var MEDIA_STORE = "handles";
|
|
@@ -2626,6 +2701,29 @@ function userEnv(account, system) {
|
|
|
2626
2701
|
PATH: `${pnpm}/bin:/usr/local/bin:/usr/bin:/bin`
|
|
2627
2702
|
};
|
|
2628
2703
|
}
|
|
2704
|
+
function groupsOf(groups, names) {
|
|
2705
|
+
return names.map((g) => {
|
|
2706
|
+
const row = groups.find((r) => r.name === g);
|
|
2707
|
+
if (!row) throw fsError("ENOENT", `no group ${g}`);
|
|
2708
|
+
return row;
|
|
2709
|
+
});
|
|
2710
|
+
}
|
|
2711
|
+
function homeModeOf(mode) {
|
|
2712
|
+
if (mode === void 0) return void 0;
|
|
2713
|
+
if (!Number.isInteger(mode) || mode < 0 || mode > 4095) {
|
|
2714
|
+
throw fsError("EINVAL", `not a mode: ${String(mode)}`);
|
|
2715
|
+
}
|
|
2716
|
+
return mode;
|
|
2717
|
+
}
|
|
2718
|
+
function joined(name2, rows) {
|
|
2719
|
+
let changed = false;
|
|
2720
|
+
for (const g of rows) {
|
|
2721
|
+
if (g.members.includes(name2)) continue;
|
|
2722
|
+
g.members.push(name2);
|
|
2723
|
+
changed = true;
|
|
2724
|
+
}
|
|
2725
|
+
return changed;
|
|
2726
|
+
}
|
|
2629
2727
|
function homeGroupOf(groups, name2) {
|
|
2630
2728
|
if (name2 === void 0) return void 0;
|
|
2631
2729
|
const found = groups.find((g) => g.name === name2);
|
|
@@ -2641,14 +2739,14 @@ var UserDb = class {
|
|
|
2641
2739
|
this.fs = fs;
|
|
2642
2740
|
this.owners = owners;
|
|
2643
2741
|
}
|
|
2644
|
-
async stampHome(row, shared) {
|
|
2742
|
+
async stampHome(row, shared, homeMode) {
|
|
2645
2743
|
const home = this.fs.resolvePath("/", row.home);
|
|
2646
2744
|
if (!this.owners || !this.fs.chown || !HOME_DIR.test(home)) return;
|
|
2647
2745
|
if (!await this.realDir(home)) return;
|
|
2648
2746
|
const gid = shared?.gid ?? row.gid;
|
|
2649
2747
|
await this.fs.chown(home, { uid: row.uid, gid, follow: false, kind: "adopt" });
|
|
2650
2748
|
const scoop = row.name.startsWith("scoop-");
|
|
2651
|
-
await this.fs.chmod(home, shared ? 1512 : scoop ? 488 : 448);
|
|
2749
|
+
await this.fs.chmod(home, homeMode ?? (shared ? 1512 : scoop ? 488 : 448));
|
|
2652
2750
|
const pnpm = `${home}/.local/share/pnpm`;
|
|
2653
2751
|
if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
|
|
2654
2752
|
}
|
|
@@ -2762,31 +2860,33 @@ var UserDb = class {
|
|
|
2762
2860
|
const groups = parsed(group);
|
|
2763
2861
|
const found = users.find((r) => r.name === name2);
|
|
2764
2862
|
const shared = homeGroupOf(groups, options.homeGroup);
|
|
2863
|
+
const homeMode = homeModeOf(options.homeMode);
|
|
2765
2864
|
if (found) {
|
|
2865
|
+
const more = groupsOf(groups, options.groups ?? []);
|
|
2866
|
+
if (more.some((g) => g.gid === found.gid)) {
|
|
2867
|
+
throw fsError("EINVAL", `a user is no member of its own group ${name2}`);
|
|
2868
|
+
}
|
|
2766
2869
|
if (found.uid >= FIRST_UID && found.uid <= LAST_UID) {
|
|
2767
2870
|
await this.provision(found);
|
|
2768
|
-
await this.stampHome(found, shared);
|
|
2871
|
+
await this.stampHome(found, shared, homeMode);
|
|
2769
2872
|
}
|
|
2873
|
+
if (joined(name2, more)) await this.fs.writeFile(GROUP, formatGroup(group));
|
|
2770
2874
|
remember();
|
|
2771
2875
|
return this.account(found, groups);
|
|
2772
2876
|
}
|
|
2773
2877
|
const home = homeOf(options.home ?? `/home/${name2}`);
|
|
2774
|
-
const extra = [USERS_GROUP, ...options.groups ?? []]
|
|
2775
|
-
const row2 = groups.find((r) => r.name === g);
|
|
2776
|
-
if (!row2) throw fsError("ENOENT", `no group ${g}`);
|
|
2777
|
-
return row2;
|
|
2778
|
-
});
|
|
2878
|
+
const extra = groupsOf(groups, [USERS_GROUP, ...options.groups ?? []]);
|
|
2779
2879
|
if (groups.some((r) => r.name === name2)) throw fsError("EEXIST", `group ${name2} exists`);
|
|
2780
2880
|
const uid = freeId(users, groups);
|
|
2781
2881
|
const row = { name: name2, uid, gid: uid, gecos: name2, home, shell: "/bin/bash" };
|
|
2782
|
-
|
|
2882
|
+
joined(name2, extra);
|
|
2783
2883
|
group.push({ name: name2, gid: uid, members: [] });
|
|
2784
2884
|
passwd.push(row);
|
|
2785
2885
|
const fresh = !await this.fs.exists(home);
|
|
2786
2886
|
await this.fs.mkdir(home, { recursive: true });
|
|
2787
2887
|
try {
|
|
2788
2888
|
await this.provision(row);
|
|
2789
|
-
await this.stampHome(row, shared);
|
|
2889
|
+
await this.stampHome(row, shared, homeMode);
|
|
2790
2890
|
await this.fs.mkdir("/etc", { recursive: true });
|
|
2791
2891
|
await this.commit(formatPasswd(passwd), formatGroup(group));
|
|
2792
2892
|
} catch (err) {
|
|
@@ -2797,6 +2897,31 @@ var UserDb = class {
|
|
|
2797
2897
|
return this.account(row, parsed(group));
|
|
2798
2898
|
});
|
|
2799
2899
|
}
|
|
2900
|
+
async groups() {
|
|
2901
|
+
const { group } = await this.tables();
|
|
2902
|
+
return parsed(group).map(({ name: name2, gid, members }) => ({ name: name2, gid, members: [...members] }));
|
|
2903
|
+
}
|
|
2904
|
+
addMember(group, user) {
|
|
2905
|
+
return this.member(group, user, true);
|
|
2906
|
+
}
|
|
2907
|
+
removeMember(group, user) {
|
|
2908
|
+
return this.member(group, user, false);
|
|
2909
|
+
}
|
|
2910
|
+
member(name2, user, on) {
|
|
2911
|
+
return this.serial(async () => {
|
|
2912
|
+
const { passwd, group } = await this.tables();
|
|
2913
|
+
const row = parsed(group).find((g) => g.name === name2);
|
|
2914
|
+
if (!row) throw fsError("ENOENT", `no group ${String(name2)}`);
|
|
2915
|
+
const who = parsed(passwd).find((u) => u.name === user);
|
|
2916
|
+
if (!who) throw fsError("ENOENT", `no user ${String(user)}`);
|
|
2917
|
+
if (who.gid === row.gid) throw fsError("EINVAL", `${name2} is the own group of ${user}`);
|
|
2918
|
+
if (row.members.includes(user) !== on) {
|
|
2919
|
+
row.members = on ? [...row.members, user] : row.members.filter((m) => m !== user);
|
|
2920
|
+
await this.fs.writeFile(GROUP, formatGroup(group));
|
|
2921
|
+
}
|
|
2922
|
+
return { name: row.name, gid: row.gid, members: [...row.members] };
|
|
2923
|
+
});
|
|
2924
|
+
}
|
|
2800
2925
|
remove(name2, options = {}) {
|
|
2801
2926
|
return this.serial(async () => {
|
|
2802
2927
|
const { passwd, group } = await this.tables();
|
|
@@ -11268,9 +11393,9 @@ var Dying = class {
|
|
|
11268
11393
|
clear() {
|
|
11269
11394
|
clearTimeout(this.timer);
|
|
11270
11395
|
}
|
|
11271
|
-
status(
|
|
11396
|
+
status(reported2) {
|
|
11272
11397
|
if (this.ending) return [this.ending.code, this.ending.sig];
|
|
11273
|
-
return [typeof
|
|
11398
|
+
return [typeof reported2 === "number" ? reported2 : CRASHED, void 0];
|
|
11274
11399
|
}
|
|
11275
11400
|
};
|
|
11276
11401
|
function killThread(thread, caught, sig) {
|
|
@@ -14491,7 +14616,14 @@ function answered(data, remote, uplink) {
|
|
|
14491
14616
|
else return false;
|
|
14492
14617
|
return true;
|
|
14493
14618
|
}
|
|
14494
|
-
var USERS_OPS = /* @__PURE__ */ new Set([
|
|
14619
|
+
var USERS_OPS = /* @__PURE__ */ new Set([
|
|
14620
|
+
"users-add",
|
|
14621
|
+
"users-remove",
|
|
14622
|
+
"users-list",
|
|
14623
|
+
"groups-list",
|
|
14624
|
+
"groups-add-member",
|
|
14625
|
+
"groups-remove-member"
|
|
14626
|
+
]);
|
|
14495
14627
|
function isUsersRequest(req) {
|
|
14496
14628
|
return USERS_OPS.has(req.op);
|
|
14497
14629
|
}
|
|
@@ -14505,6 +14637,9 @@ function failure(err) {
|
|
|
14505
14637
|
function usersOp(l, req) {
|
|
14506
14638
|
if (req.op === "users-add") return l.users.add(req.user);
|
|
14507
14639
|
if (req.op === "users-remove") return l.users.remove(req.name, req.options);
|
|
14640
|
+
if (req.op === "groups-list") return l.users.groups();
|
|
14641
|
+
if (req.op === "groups-add-member") return l.users.addMember(req.group, req.user);
|
|
14642
|
+
if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
|
|
14508
14643
|
return l.users.list();
|
|
14509
14644
|
}
|
|
14510
14645
|
function hostOptions(req) {
|