shadok-ai 0.17.8 → 0.17.10

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 CHANGED
@@ -501,6 +501,16 @@ across OS users (Claude rewrites the credential single-owner on refresh), which
501
501
  is why each user logs in separately. Design:
502
502
  `docs/superpowers/specs/2026-11-04-connectors-via-global-login-design.md`.
503
503
 
504
+ **Persist the group homes (recommended in Docker).** Each dropped agent runs as
505
+ its own OS user with HOME under `SHADOK_HOME_BASE` (default `/home`), which is
506
+ **not** on the `shadok-data` volume by default — so a `docker rm` + recreate
507
+ wipes those homes, taking their Claude first-run/trust state (agents briefly land
508
+ on the "trust this folder" dialog until it is re-seeded) and, in **login** mode,
509
+ the per-user credentials (every user has to log in again). shadok re-seeds the
510
+ trust robustly on boot, but to avoid the churn entirely mount the home base on a
511
+ volume, the same move as the SSH key (below): e.g. add `-v shadok-homes:/home` to
512
+ the `docker run`. A plain restart never needed this; only a recreate does.
513
+
504
514
  Telegram stays single-owner, and there are no
505
515
  disk quotas yet. Design: `docs/superpowers/specs/2026-10-29-multi-tenant-scoped-isolation-design.md`.
506
516
 
@@ -39,6 +39,23 @@ export declare function seedPlan(existing: ClaudeHome, opts: {
39
39
  version: string;
40
40
  cwd?: string;
41
41
  }): ClaudeHome | null;
42
+ /**
43
+ * Pure: the object to write after trusting EVERY cwd in `cwds` (plus the
44
+ * globals), or null when nothing is missing.
45
+ *
46
+ * This is `seedPlan` folded over many directories so a shared group HOME is
47
+ * seeded in ONE atomic write instead of one racy write per spawn. The
48
+ * motivating bug: N agents share a single `~/.claude.json`, and rebuilding it
49
+ * one cwd at a time during a boot respawn storm let a sibling `claude`'s stale
50
+ * write clobber a trust entry before that agent's own process read it —
51
+ * stranding it on the "trust this folder" dialog. Seeding the complete set up
52
+ * front (before any such process starts) removes the window entirely. Each
53
+ * `cwd` keeps `seedPlan`'s additive, never-overwrite discipline.
54
+ */
55
+ export declare function seedPlanForCwds(existing: ClaudeHome, opts: {
56
+ version: string;
57
+ cwds: readonly string[];
58
+ }): ClaudeHome | null;
42
59
  export interface ClaudeSettings {
43
60
  tui?: string;
44
61
  [k: string]: unknown;
@@ -124,6 +124,35 @@ export function seedPlan(existing, opts) {
124
124
  }
125
125
  return changed ? out : null;
126
126
  }
127
+ /**
128
+ * Pure: the object to write after trusting EVERY cwd in `cwds` (plus the
129
+ * globals), or null when nothing is missing.
130
+ *
131
+ * This is `seedPlan` folded over many directories so a shared group HOME is
132
+ * seeded in ONE atomic write instead of one racy write per spawn. The
133
+ * motivating bug: N agents share a single `~/.claude.json`, and rebuilding it
134
+ * one cwd at a time during a boot respawn storm let a sibling `claude`'s stale
135
+ * write clobber a trust entry before that agent's own process read it —
136
+ * stranding it on the "trust this folder" dialog. Seeding the complete set up
137
+ * front (before any such process starts) removes the window entirely. Each
138
+ * `cwd` keeps `seedPlan`'s additive, never-overwrite discipline.
139
+ */
140
+ export function seedPlanForCwds(existing, opts) {
141
+ let current = existing;
142
+ let changed = false;
143
+ // Globals first (no cwd), then each directory — threading the running object
144
+ // so every change lands in one result. seedPlan returns null for "nothing to
145
+ // add", in which case we keep the object we already have.
146
+ const steps = [undefined, ...opts.cwds];
147
+ for (const cwd of steps) {
148
+ const next = seedPlan(current, { version: opts.version, cwd });
149
+ if (next) {
150
+ current = next;
151
+ changed = true;
152
+ }
153
+ }
154
+ return changed ? current : null;
155
+ }
127
156
  /**
128
157
  * Pure: `~/.claude/settings.json` after adding the keys that suppress a
129
158
  * first-run *upsell*, or null when nothing is missing.
@@ -1 +1 @@
1
- {"version":3,"file":"claude-home.js","sourceRoot":"","sources":["../src/claude-home.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claude-home.js","sourceRoot":"","sources":["../src/claude-home.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -19,7 +19,7 @@ import { extractLiveText } from "../public/live-text.js";
19
19
  import { findTransientErrors, newTransientErrors, RETRY_DELAYS_MS } from "./retry.js";
20
20
  import { backgroundTasks, screenShowsWork, moveToOption } from "./detect.js";
21
21
  import { PtyPilot } from "./session.js";
22
- import { ensureClaudeHome, ensureProjectTrusted, getDefaultModel, setDefaultModel, parseClaudeVersion, seedPlan, settingsPlan, } from "./claude-home.js";
22
+ import { ensureClaudeHome, ensureProjectTrusted, getDefaultModel, setDefaultModel, parseClaudeVersion, seedPlanForCwds, settingsPlan, } from "./claude-home.js";
23
23
  import { authStatus, cancelLogin, invalidateAuthStatus, startLogin, submitLoginCode } from "./claude-auth.js";
24
24
  import { startSetupToken, submitSetupTokenCode, cancelSetupToken } from "./setup-token.js";
25
25
  import { startAgentLogin, submitAgentLoginCode, cancelAgentLogin, agentLoginPending, agentLoginInfo, } from "./agent-login.js";
@@ -42,7 +42,7 @@ import { computePace, paceBlock, WINDOW_SEC } from "./pace.js";
42
42
  import { getUsage } from "./usage.js";
43
43
  import { loadChannels, loadGroups, saveGroups, normalizeGroups, ensureMainGroup, upsertChannel, removeChannel, mergeClientChannels, channelWriteAllowed, profilePatch, loadTgGroup, saveTgGroup, isHomeChannel, homeAdoptionTarget, resolveSessionTarget, resolveResumeId, resumeTarget, } from "./channels.js";
44
44
  import { deliverWithRetry } from "./reload.js";
45
- import { startTelegram, renameTelegramTopic, closeTelegramTopic, probeToken, isStalePreface, announceLoggedOut, resetLoggedOutNotice, attachmentPrompt, pasteFileName, MEDIA_DIR, } from "./telegram.js";
45
+ import { startTelegram, renameTelegramTopic, closeTelegramTopic, probeToken, isStalePreface, announceLoggedOut, resetLoggedOutNotice, attachmentPrompt, pasteFileName, mediaDir, } from "./telegram.js";
46
46
  import { transcribe, audioClipExtension } from "./whisper.js";
47
47
  import { cancelPairingCode, issuePairingCode, pairingStatus } from "./tg-pairing.js";
48
48
  import { migrateTgBindings } from "./channels.js";
@@ -1377,8 +1377,9 @@ app.post("/paste", express.raw({ type: () => true, limit: PASTE_LIMIT }), (req,
1377
1377
  const name = decodeURIComponent(String(req.headers["x-filename"] ?? ""));
1378
1378
  const kind = contentType.trim().toLowerCase().startsWith("image/") ? "image" : "file";
1379
1379
  try {
1380
- fs.mkdirSync(MEDIA_DIR, { recursive: true });
1381
- const file = path.join(MEDIA_DIR, pasteFileName(randomUUID(), name, contentType));
1380
+ const dir = mediaDir();
1381
+ fs.mkdirSync(dir, { recursive: true });
1382
+ const file = path.join(dir, pasteFileName(randomUUID(), name, contentType));
1382
1383
  fs.writeFileSync(file, body);
1383
1384
  // The very wording Telegram already uses, so an agent sees one format.
1384
1385
  res.json({ path: file, line: attachmentPrompt([{ path: file, kind }]) });
@@ -2246,6 +2247,51 @@ function channelStateHome(chan) {
2246
2247
  }
2247
2248
  /** Task 12: the one shared pot every group HOME links as `~/shared`. */
2248
2249
  const SHARED_POT_PATH = (process.env.SHADOK_SHARED_POT_PATH ?? "").trim() || DEFAULT_SHARED_POT_PATH;
2250
+ // Multi-tenant: Telegram/paste attachments must be readable by a privilege-
2251
+ // dropped agent, but the default `~/.shadok-ai/media` is `/root/.shadok-ai/media`
2252
+ // — and `/root` (mode 700) is un-traversable by the `main`/`sk-*` drop users, so
2253
+ // an agent handed `[Attached image: /root/.shadok-ai/media/…]` cannot open it
2254
+ // (invariant 54's family). Relocate media under the shared pot (which every drop
2255
+ // user is in `shadok-shared` to traverse), carry existing attachments over so
2256
+ // already-sent images keep resolving, and point `mediaDir()` there via the env.
2257
+ // Best-effort: a failure just leaves media at `~/.shadok-ai/media`, the old
2258
+ // (broken-for-agents) behaviour, never a boot failure. Mono-tenant is untouched.
2259
+ if (DEPLOYMENT_MODE === "multi-tenant") {
2260
+ try {
2261
+ const shared = path.join(SHARED_POT_PATH, "media");
2262
+ fs.mkdirSync(shared, { recursive: true });
2263
+ // Own it to the pot's group (`shadok-shared`) with setgid, so files the
2264
+ // server writes inherit that group and the drop users can read them.
2265
+ try {
2266
+ const potGid = fs.statSync(SHARED_POT_PATH).gid;
2267
+ fs.chownSync(shared, 0, potGid);
2268
+ fs.chmodSync(shared, 0o2775);
2269
+ }
2270
+ catch {
2271
+ /* pot not provisioned yet (pre-migration) — the next boot fixes it */
2272
+ }
2273
+ const legacy = path.join(os.homedir(), ".shadok-ai", "media");
2274
+ if (legacy !== shared && fs.existsSync(legacy)) {
2275
+ for (const name of fs.readdirSync(legacy)) {
2276
+ const dst = path.join(shared, name);
2277
+ if (!fs.existsSync(dst)) {
2278
+ try {
2279
+ fs.copyFileSync(path.join(legacy, name), dst);
2280
+ }
2281
+ catch {
2282
+ /* skip an unreadable/odd entry */
2283
+ }
2284
+ }
2285
+ }
2286
+ }
2287
+ process.env.SHADOK_MEDIA_DIR = shared;
2288
+ console.log(`media: using ${shared} (reachable by dropped agents)`);
2289
+ }
2290
+ catch (e) {
2291
+ console.warn("media: could not set up the shared media dir, keeping ~/.shadok-ai/media: " +
2292
+ (e instanceof Error ? e.message : String(e)));
2293
+ }
2294
+ }
2249
2295
  /** Run a privileged account command. Root runs it directly; otherwise go through
2250
2296
  * NON-interactive sudo (`-n`) so a MISSING right fails fast with a non-zero exit
2251
2297
  * instead of prompting — the T3 false positive (sudoers allows `true` but not
@@ -2525,6 +2571,10 @@ async function ensureGroupsProvisioned() {
2525
2571
  // invite-redeem hook nor the per-account migration loop ever fires for it.
2526
2572
  // Give it a Personal group here; idempotent (reuses an existing private group).
2527
2573
  await ensurePrivateGroupProvisioned(BOOTSTRAP_ADMIN);
2574
+ // Before any agent comes back: fill every drop user's HOME with the full set
2575
+ // of trusted directories in one write each, so none strands on the trust
2576
+ // dialog when the resume storm rebuilds a wiped (ephemeral) group HOME.
2577
+ preseedGroupHomes();
2528
2578
  }
2529
2579
  /** Step 1 — create the non-root commons user `main` (+ HOME). Idempotent. */
2530
2580
  async function migCreateCommonsUser() {
@@ -4289,8 +4339,9 @@ app.get("/download", (req, res) => {
4289
4339
  // same `nosniff` + `sandbox` CSP as /download neutralises an SVG opened directly.
4290
4340
  app.get("/media/:name", (req, res) => {
4291
4341
  const base = path.basename(String(req.params.name ?? ""));
4292
- const file = path.join(MEDIA_DIR, base);
4293
- if (!file.startsWith(path.join(MEDIA_DIR, path.sep)) && path.dirname(file) !== MEDIA_DIR) {
4342
+ const dir = mediaDir();
4343
+ const file = path.join(dir, base);
4344
+ if (!file.startsWith(path.join(dir, path.sep)) && path.dirname(file) !== dir) {
4294
4345
  return res.status(400).type("text").send("bad name");
4295
4346
  }
4296
4347
  let stat;
@@ -4870,7 +4921,7 @@ function ensureAgentSshKey(user, home) {
4870
4921
  * seed the shadok SKILLS into the group HOME (they live only in the server's
4871
4922
  * ~/.claude/skills); that gap is tracked separately.
4872
4923
  */
4873
- function seedGroupAgentHome(user, home, cwd) {
4924
+ function seedHomeTrust(user, home, cwds) {
4874
4925
  const version = currentClaudeVersion() ?? "0.0.0";
4875
4926
  const readObj = (file) => {
4876
4927
  const r = spawnAsUserSync(user, "cat", [file]);
@@ -4890,7 +4941,9 @@ function seedGroupAgentHome(user, home, cwd) {
4890
4941
  const homeJson = `${home}/.claude.json`;
4891
4942
  const existing = readObj(homeJson);
4892
4943
  if (existing) {
4893
- const plan = seedPlan(existing, { version, cwd });
4944
+ // ONE write for the globals + every cwd (not one racy write per cwd) —
4945
+ // see `seedPlanForCwds` and `preseedGroupHomes` for why that matters.
4946
+ const plan = seedPlanForCwds(existing, { version, cwds });
4894
4947
  if (plan)
4895
4948
  writeAsUserAtomic(user, homeJson, JSON.stringify(plan, null, 2));
4896
4949
  }
@@ -4909,6 +4962,75 @@ function seedGroupAgentHome(user, home, cwd) {
4909
4962
  console.log(`claude-home: could not seed group HOME ${home} — ${e instanceof Error ? e.message : String(e)}`);
4910
4963
  }
4911
4964
  }
4965
+ /**
4966
+ * The drop user each channel's agent runs as, mapped to the directories that
4967
+ * user's agents live in. The input to seeding a group HOME with the right set
4968
+ * of trusted directories. Only DROP users appear (commons `main`, private
4969
+ * `sk-*`); a public/mono channel runs as the server's own user (null), whose
4970
+ * `/root` is handled by `ensureClaudeHome` + `ensureProjectTrusted` and lives
4971
+ * on the shadok-data volume — it never suffers the ephemeral-rebuild race this
4972
+ * map exists to close.
4973
+ */
4974
+ function dropUserCwds() {
4975
+ const out = new Map();
4976
+ const groups = new Map(loadGroups().map((g) => [g.id, g]));
4977
+ const commons = commonsOsUser({ migrated: groupsMigrated, mode: DEPLOYMENT_MODE });
4978
+ const add = (user, cwd) => {
4979
+ if (!user || !cwd)
4980
+ return;
4981
+ let s = out.get(user);
4982
+ if (!s) {
4983
+ s = new Set();
4984
+ out.set(user, s);
4985
+ }
4986
+ s.add(cwd);
4987
+ };
4988
+ for (const ch of loadChannels()) {
4989
+ const g = ch.group ? groups.get(ch.group) : undefined;
4990
+ add((g ? osUserFor(g) : null) ?? commons, ch.cwd);
4991
+ }
4992
+ // The commons also runs the lead agent in the launch dir itself.
4993
+ if (commons)
4994
+ add(commons, process.cwd());
4995
+ return out;
4996
+ }
4997
+ /**
4998
+ * Seed a dropped agent's OWN HOME before it spawns — this agent's cwd AND every
4999
+ * sibling cwd that shares the HOME, in ONE write. The shared `~/.claude.json`
5000
+ * is read-modify-written by every agent in the HOME (commons agents all share
5001
+ * `/home/main`), so rebuilding it one cwd at a time during a respawn storm let
5002
+ * a stale sibling `claude` write clobber a trust entry before its owner read it
5003
+ * — stranding that agent on the "trust this folder" dialog (invariant 23's
5004
+ * family, on the group HOME). A complete write closes the window. See the file
5005
+ * comment on `seedHomeTrust` / `preseedGroupHomes`. Runs AS the group user,
5006
+ * SYNCHRONOUSLY and ADDITIVELY; best-effort.
5007
+ */
5008
+ function seedGroupAgentHome(user, home, cwd) {
5009
+ const cwds = new Set([cwd]);
5010
+ const siblings = dropUserCwds().get(user);
5011
+ if (siblings)
5012
+ for (const c of siblings)
5013
+ cwds.add(c);
5014
+ seedHomeTrust(user, home, [...cwds]);
5015
+ }
5016
+ /**
5017
+ * Boot pre-seed: seed EVERY drop user's HOME with its full set of trusted
5018
+ * directories, in one atomic write each, BEFORE the resume/respawn storm — so a
5019
+ * `claude` that starts in that storm always finds its cwd already trusted. The
5020
+ * group HOMEs are not on the shadok-data volume, so a `docker` recreate wipes
5021
+ * them and they are rebuilt from empty exactly when many agents come back at
5022
+ * once; the per-spawn seed alone then raced (measured: an agent stuck on the
5023
+ * trust dialog with its entry missing from the shared `/home/main/.claude.json`
5024
+ * while `/root`'s — volume-backed — had it). Best-effort; mono-tenant or
5025
+ * un-migrated → no drop users → no-op.
5026
+ */
5027
+ function preseedGroupHomes() {
5028
+ if (DEPLOYMENT_MODE !== "multi-tenant" || !groupsMigrated)
5029
+ return;
5030
+ for (const [user, cwds] of dropUserCwds()) {
5031
+ seedHomeTrust(user, homeForUser(user, GROUP_HOME_BASE), [...cwds]);
5032
+ }
5033
+ }
4912
5034
  function makePilot(id, cwd, args, profileName, model, effort,
4913
5035
  /**
4914
5036
  * The OS user to drop this agent to (multi-tenant isolation, `src/osusers.ts`).