@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.
@@ -294,10 +294,31 @@ var ERRNO_BY_DOM_ERROR = {
294
294
  TypeError: "EINVAL",
295
295
  NotAllowedError: "EACCES"
296
296
  };
297
- function translate(err, path) {
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 name2 = err?.name;
300
- return fsError(typeof name2 === "string" && ERRNO_BY_DOM_ERROR[name2] || "EIO", path);
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) throw translate(err, path);
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
- truncate: (size) => writable.truncate(size),
738
- close: () => writable.close()
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
- const writable = await file.createWritable();
774
- await writable.write(content);
775
- await writable.close();
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 ?? []].map((g) => {
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
- for (const g of extra) if (!g.members.includes(name2)) g.members.push(name2);
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(reported) {
11396
+ status(reported2) {
11272
11397
  if (this.ending) return [this.ending.code, this.ending.sig];
11273
- return [typeof reported === "number" ? reported : CRASHED, void 0];
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(["users-add", "users-remove", "users-list"]);
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) {