@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.
@@ -2626,6 +2626,29 @@ function userEnv(account, system) {
2626
2626
  PATH: `${pnpm}/bin:/usr/local/bin:/usr/bin:/bin`
2627
2627
  };
2628
2628
  }
2629
+ function groupsOf(groups, names) {
2630
+ return names.map((g) => {
2631
+ const row = groups.find((r) => r.name === g);
2632
+ if (!row) throw fsError("ENOENT", `no group ${g}`);
2633
+ return row;
2634
+ });
2635
+ }
2636
+ function homeModeOf(mode) {
2637
+ if (mode === void 0) return void 0;
2638
+ if (!Number.isInteger(mode) || mode < 0 || mode > 4095) {
2639
+ throw fsError("EINVAL", `not a mode: ${String(mode)}`);
2640
+ }
2641
+ return mode;
2642
+ }
2643
+ function joined(name2, rows) {
2644
+ let changed = false;
2645
+ for (const g of rows) {
2646
+ if (g.members.includes(name2)) continue;
2647
+ g.members.push(name2);
2648
+ changed = true;
2649
+ }
2650
+ return changed;
2651
+ }
2629
2652
  function homeGroupOf(groups, name2) {
2630
2653
  if (name2 === void 0) return void 0;
2631
2654
  const found = groups.find((g) => g.name === name2);
@@ -2641,14 +2664,14 @@ var UserDb = class {
2641
2664
  this.fs = fs;
2642
2665
  this.owners = owners;
2643
2666
  }
2644
- async stampHome(row, shared) {
2667
+ async stampHome(row, shared, homeMode) {
2645
2668
  const home = this.fs.resolvePath("/", row.home);
2646
2669
  if (!this.owners || !this.fs.chown || !HOME_DIR.test(home)) return;
2647
2670
  if (!await this.realDir(home)) return;
2648
2671
  const gid = shared?.gid ?? row.gid;
2649
2672
  await this.fs.chown(home, { uid: row.uid, gid, follow: false, kind: "adopt" });
2650
2673
  const scoop = row.name.startsWith("scoop-");
2651
- await this.fs.chmod(home, shared ? 1512 : scoop ? 488 : 448);
2674
+ await this.fs.chmod(home, homeMode ?? (shared ? 1512 : scoop ? 488 : 448));
2652
2675
  const pnpm = `${home}/.local/share/pnpm`;
2653
2676
  if (await this.realDir(pnpm)) await this.fs.chmod(pnpm, scoop ? 488 : 448);
2654
2677
  }
@@ -2762,31 +2785,33 @@ var UserDb = class {
2762
2785
  const groups = parsed(group);
2763
2786
  const found = users.find((r) => r.name === name2);
2764
2787
  const shared = homeGroupOf(groups, options.homeGroup);
2788
+ const homeMode = homeModeOf(options.homeMode);
2765
2789
  if (found) {
2790
+ const more = groupsOf(groups, options.groups ?? []);
2791
+ if (more.some((g) => g.gid === found.gid)) {
2792
+ throw fsError("EINVAL", `a user is no member of its own group ${name2}`);
2793
+ }
2766
2794
  if (found.uid >= FIRST_UID && found.uid <= LAST_UID) {
2767
2795
  await this.provision(found);
2768
- await this.stampHome(found, shared);
2796
+ await this.stampHome(found, shared, homeMode);
2769
2797
  }
2798
+ if (joined(name2, more)) await this.fs.writeFile(GROUP, formatGroup(group));
2770
2799
  remember();
2771
2800
  return this.account(found, groups);
2772
2801
  }
2773
2802
  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
- });
2803
+ const extra = groupsOf(groups, [USERS_GROUP, ...options.groups ?? []]);
2779
2804
  if (groups.some((r) => r.name === name2)) throw fsError("EEXIST", `group ${name2} exists`);
2780
2805
  const uid = freeId(users, groups);
2781
2806
  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);
2807
+ joined(name2, extra);
2783
2808
  group.push({ name: name2, gid: uid, members: [] });
2784
2809
  passwd.push(row);
2785
2810
  const fresh = !await this.fs.exists(home);
2786
2811
  await this.fs.mkdir(home, { recursive: true });
2787
2812
  try {
2788
2813
  await this.provision(row);
2789
- await this.stampHome(row, shared);
2814
+ await this.stampHome(row, shared, homeMode);
2790
2815
  await this.fs.mkdir("/etc", { recursive: true });
2791
2816
  await this.commit(formatPasswd(passwd), formatGroup(group));
2792
2817
  } catch (err) {
@@ -2797,6 +2822,31 @@ var UserDb = class {
2797
2822
  return this.account(row, parsed(group));
2798
2823
  });
2799
2824
  }
2825
+ async groups() {
2826
+ const { group } = await this.tables();
2827
+ return parsed(group).map(({ name: name2, gid, members }) => ({ name: name2, gid, members: [...members] }));
2828
+ }
2829
+ addMember(group, user) {
2830
+ return this.member(group, user, true);
2831
+ }
2832
+ removeMember(group, user) {
2833
+ return this.member(group, user, false);
2834
+ }
2835
+ member(name2, user, on) {
2836
+ return this.serial(async () => {
2837
+ const { passwd, group } = await this.tables();
2838
+ const row = parsed(group).find((g) => g.name === name2);
2839
+ if (!row) throw fsError("ENOENT", `no group ${String(name2)}`);
2840
+ const who = parsed(passwd).find((u) => u.name === user);
2841
+ if (!who) throw fsError("ENOENT", `no user ${String(user)}`);
2842
+ if (who.gid === row.gid) throw fsError("EINVAL", `${name2} is the own group of ${user}`);
2843
+ if (row.members.includes(user) !== on) {
2844
+ row.members = on ? [...row.members, user] : row.members.filter((m) => m !== user);
2845
+ await this.fs.writeFile(GROUP, formatGroup(group));
2846
+ }
2847
+ return { name: row.name, gid: row.gid, members: [...row.members] };
2848
+ });
2849
+ }
2800
2850
  remove(name2, options = {}) {
2801
2851
  return this.serial(async () => {
2802
2852
  const { passwd, group } = await this.tables();
@@ -8779,6 +8829,11 @@ var TOKEN3 = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
8779
8829
  function message(e) {
8780
8830
  return e instanceof Error ? e.message : String(e);
8781
8831
  }
8832
+ var ERRNO = /^E[A-Z]+$|^EAI_[A-Z]+$/;
8833
+ function errnoOf(e) {
8834
+ const code = e?.code;
8835
+ return typeof code === "string" && ERRNO.test(code) ? code : "ECONNREFUSED";
8836
+ }
8782
8837
  function fail(errno, text) {
8783
8838
  return { ok: false, errno, message: text };
8784
8839
  }
@@ -8822,13 +8877,13 @@ var WsHandles = class {
8822
8877
  let socket;
8823
8878
  try {
8824
8879
  socket = await transport.websocket({
8825
- url: req.url,
8880
+ url: toHostLoopback(req.url),
8826
8881
  protocols: req.protocols,
8827
8882
  headers: req.headers,
8828
8883
  signal: entry.abort.signal
8829
8884
  });
8830
8885
  } catch (e) {
8831
- return fail("ECONNREFUSED", message(e));
8886
+ return fail(errnoOf(e), message(e));
8832
8887
  }
8833
8888
  if (!this.open.has(handle)) {
8834
8889
  socket.close(1001, "the program went away");
@@ -14486,7 +14541,14 @@ function answered(data, remote, uplink) {
14486
14541
  else return false;
14487
14542
  return true;
14488
14543
  }
14489
- var USERS_OPS = /* @__PURE__ */ new Set(["users-add", "users-remove", "users-list"]);
14544
+ var USERS_OPS = /* @__PURE__ */ new Set([
14545
+ "users-add",
14546
+ "users-remove",
14547
+ "users-list",
14548
+ "groups-list",
14549
+ "groups-add-member",
14550
+ "groups-remove-member"
14551
+ ]);
14490
14552
  function isUsersRequest(req) {
14491
14553
  return USERS_OPS.has(req.op);
14492
14554
  }
@@ -14500,6 +14562,9 @@ function failure(err) {
14500
14562
  function usersOp(l, req) {
14501
14563
  if (req.op === "users-add") return l.users.add(req.user);
14502
14564
  if (req.op === "users-remove") return l.users.remove(req.name, req.options);
14565
+ if (req.op === "groups-list") return l.users.groups();
14566
+ if (req.op === "groups-add-member") return l.users.addMember(req.group, req.user);
14567
+ if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
14503
14568
  return l.users.list();
14504
14569
  }
14505
14570
  function hostOptions(req) {