@taskless-app/swarm 0.6.62 → 0.6.63

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.
Files changed (3) hide show
  1. package/dist/cli.js +421 -182
  2. package/dist/overmind.js +571 -310
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -607,10 +607,10 @@ function pick(rows) {
607
607
  return newest(rows.filter((r) => r.author)) ?? newest(rows);
608
608
  }
609
609
  function pickRunForPr(rows, ref, ctxRepo) {
610
- const num = prNumberOf(ref);
611
- if (num == null) return null;
610
+ const num2 = prNumberOf(ref);
611
+ if (num2 == null) return null;
612
612
  const target = repoKeyOf(ref) ?? repoKeyOf(ctxRepo);
613
- const hits = rows.filter((r) => r.prUrl && prNumberOf(r.prUrl) === num);
613
+ const hits = rows.filter((r) => r.prUrl && prNumberOf(r.prUrl) === num2);
614
614
  if (hits.length === 0) return null;
615
615
  if (target) {
616
616
  const mine = hits.filter((r) => keyOf(r) === target);
@@ -650,6 +650,9 @@ var init_returnReason = __esm({
650
650
  });
651
651
 
652
652
  // ../swarm-core/src/modelPolicy.ts
653
+ function isHarnessDefaultModel(model) {
654
+ return typeof model === "string" && model.trim().toLowerCase() === HARNESS_DEFAULT_MODEL;
655
+ }
653
656
  function findProvider(id) {
654
657
  const key = typeof id === "string" ? id.trim().toLowerCase() : "";
655
658
  return MODEL_PROVIDERS.find((p) => p.id === key) ?? null;
@@ -701,6 +704,17 @@ var init_modelPolicy = __esm({
701
704
  selectable: true,
702
705
  roles: { executor: "glm-5.3", review: "glm-5.3" }
703
706
  },
707
+ // Grok CLI. Каталог — живой `grok models` (16.10.2026), целиком и без выдуманных
708
+ // имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5
709
+ // (проверка — младшая, та же идея, что sonnet против opus). Тарифа нет: цену пишет CLI.
710
+ {
711
+ id: "xai",
712
+ label: "xAI (Grok)",
713
+ executorId: "grok",
714
+ selectable: true,
715
+ roles: { executor: "grok-4.7", review: "grok-4.5" },
716
+ models: ["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5"]
717
+ },
704
718
  // TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
705
719
  // Имён моделей здесь нет СОЗНАТЕЛЬНО: у владельца подписка, состав моделей в ней нам
706
720
  // неизвестен, а выдуманное имя хуже маркера «решает Codex».
@@ -1089,18 +1103,18 @@ var init_hosts = __esm({
1089
1103
  function parseHostResources(raw) {
1090
1104
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
1091
1105
  const v = raw;
1092
- const num = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
1093
- const memFreeGb = num(v.memFreeGb);
1094
- const memTotalGb = num(v.memTotalGb);
1106
+ const num2 = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
1107
+ const memFreeGb = num2(v.memFreeGb);
1108
+ const memTotalGb = num2(v.memTotalGb);
1095
1109
  if (memFreeGb == null || memTotalGb == null) return null;
1096
1110
  if (typeof v.measuredAt !== "string" || Number.isNaN(Date.parse(v.measuredAt))) return null;
1097
- const cpu = num(v.cpuBusyPct);
1111
+ const cpu = num2(v.cpuBusyPct);
1098
1112
  return {
1099
1113
  cpuBusyPct: cpu == null ? null : Math.min(100, Math.max(0, cpu)),
1100
1114
  memFreeGb,
1101
1115
  memTotalGb,
1102
- diskFreeGb: num(v.diskFreeGb),
1103
- diskTotalGb: num(v.diskTotalGb),
1116
+ diskFreeGb: num2(v.diskFreeGb),
1117
+ diskTotalGb: num2(v.diskTotalGb),
1104
1118
  measuredAt: v.measuredAt
1105
1119
  };
1106
1120
  }
@@ -1137,6 +1151,7 @@ var init_hostPool = __esm({
1137
1151
  "../swarm-core/src/hostPool.ts"() {
1138
1152
  "use strict";
1139
1153
  init_hosts();
1154
+ init_modelPolicy();
1140
1155
  HOST_REJECTION_TEXT = {
1141
1156
  offline: "\u043D\u0435 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438",
1142
1157
  tick_stalled: `\u0446\u0438\u043A\u043B \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B\u0430 \u0432\u0441\u0442\u0430\u043B \u2014 \u043F\u0443\u043B\u044C\u0441 \u0438\u0434\u0451\u0442, \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442; ${HOST_TICK_STALLED_ACTION}`,
@@ -2109,10 +2124,10 @@ function launchCostUsd(result, model) {
2109
2124
  return costFromUsage(result.usage, model);
2110
2125
  }
2111
2126
  function launchSpend(result) {
2112
- const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2127
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2113
2128
  const sum = (u) => ({
2114
- tokensIn: num(u.input_tokens) + num(u.cache_creation_input_tokens) + num(u.cache_read_input_tokens),
2115
- tokensOut: num(u.output_tokens)
2129
+ tokensIn: num2(u.input_tokens) + num2(u.cache_creation_input_tokens) + num2(u.cache_read_input_tokens),
2130
+ tokensOut: num2(u.output_tokens)
2116
2131
  });
2117
2132
  const spend = sum(result.usage ?? {});
2118
2133
  if (spend.tokensIn === 0 && spend.tokensOut === 0 && result.modelUsage) {
@@ -2122,7 +2137,7 @@ function launchSpend(result) {
2122
2137
  spend.tokensOut += perModel.tokensOut;
2123
2138
  }
2124
2139
  }
2125
- return { ...spend, durationMs: num(result.duration_ms) };
2140
+ return { ...spend, durationMs: num2(result.duration_ms) };
2126
2141
  }
2127
2142
  function asTokenUsage(m) {
2128
2143
  return {
@@ -2214,9 +2229,9 @@ function transcriptSpendInWindow(worktree, fromMs, toMs, opts = {}) {
2214
2229
  unpriced += 1;
2215
2230
  unpricedModel ??= model;
2216
2231
  }
2217
- const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2218
- tokensIn += num(usage2.input_tokens) + num(usage2.cache_creation_input_tokens) + num(usage2.cache_read_input_tokens);
2219
- tokensOut += num(usage2.output_tokens);
2232
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2233
+ tokensIn += num2(usage2.input_tokens) + num2(usage2.cache_creation_input_tokens) + num2(usage2.cache_read_input_tokens);
2234
+ tokensOut += num2(usage2.output_tokens);
2220
2235
  if (firstAt === null || at < firstAt) firstAt = at;
2221
2236
  if (lastAt === null || at > lastAt) lastAt = at;
2222
2237
  replies += 1;
@@ -2444,13 +2459,15 @@ var init_swarmHome = __esm({
2444
2459
  });
2445
2460
 
2446
2461
  // src/executors/codex.ts
2447
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync4, statSync as statSync3, symlinkSync } from "fs";
2462
+ import { copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync4, statSync as statSync3, symlinkSync } from "fs";
2448
2463
  import { join as join5 } from "path";
2449
2464
  function resolveCodexBin(env = process.env) {
2450
2465
  return (env.SWARM_CODEX_BIN ?? "").trim() || "codex";
2451
2466
  }
2452
2467
  function codexHomePath(worktree, swarmHome = SWARM_HOME) {
2453
- return join5(swarmHome, "executor-state", "codex", worktree.replace(/[/.]/g, "-"));
2468
+ const folded = worktree.replace(/[/.]/g, "-");
2469
+ const key = /[:\\]/.test(folded) ? worktree.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "") || "worktree" : folded;
2470
+ return join5(swarmHome, "executor-state", "codex", key);
2454
2471
  }
2455
2472
  function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWARM_HOME) {
2456
2473
  const home = codexHomePath(worktree, swarmHome);
@@ -2463,6 +2480,10 @@ function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWAR
2463
2480
  try {
2464
2481
  symlinkSync(target, link);
2465
2482
  } catch {
2483
+ try {
2484
+ copyFileSync2(target, link);
2485
+ } catch {
2486
+ }
2466
2487
  }
2467
2488
  }
2468
2489
  return home;
@@ -2613,6 +2634,206 @@ ${notice}` : spec.prompt;
2613
2634
  }
2614
2635
  });
2615
2636
 
2637
+ // src/executors/grok.ts
2638
+ import { copyFileSync as copyFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync5, statSync as statSync4, symlinkSync as symlinkSync2, writeFileSync as writeFileSync3 } from "fs";
2639
+ import { join as join6 } from "path";
2640
+ function resolveGrokBin(env = process.env) {
2641
+ return (env.SWARM_GROK_BIN ?? "").trim() || "grok";
2642
+ }
2643
+ function grokHomePath(worktree, swarmHome = SWARM_HOME) {
2644
+ const key = worktree.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "") || "worktree";
2645
+ return join6(swarmHome, "executor-state", "grok", key);
2646
+ }
2647
+ function grokSessionDir(worktree, swarmHome = SWARM_HOME) {
2648
+ return join6(grokHomePath(worktree, swarmHome), "sessions", encodeURIComponent(worktree));
2649
+ }
2650
+ function tomlBasic(value) {
2651
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
2652
+ return `"${escaped}"`;
2653
+ }
2654
+ function grokMcpToml(spec) {
2655
+ const env = Object.entries(spec.mcp.env).map(([k, v]) => `${k} = ${tomlBasic(v)}`).join(", ");
2656
+ const args = spec.mcp.args.map((a) => tomlBasic(a)).join(", ");
2657
+ return [
2658
+ "[mcp_servers." + spec.mcp.name + "]",
2659
+ "command = " + tomlBasic(spec.mcp.command),
2660
+ "args = [" + args + "]",
2661
+ "env = { " + env + " }",
2662
+ ""
2663
+ ].join("\n");
2664
+ }
2665
+ function ensureGrokHome(worktree, realHome, env = process.env, swarmHome = SWARM_HOME) {
2666
+ const home = grokHomePath(worktree, swarmHome);
2667
+ mkdirSync4(home, { recursive: true });
2668
+ const real = (env.GROK_HOME ?? "").trim() || join6(realHome, ".grok");
2669
+ const target = join6(real, "auth.json");
2670
+ const link = join6(home, "auth.json");
2671
+ if (existsSync4(target) && !existsSync4(link)) {
2672
+ try {
2673
+ symlinkSync2(target, link);
2674
+ } catch {
2675
+ try {
2676
+ copyFileSync3(target, link);
2677
+ } catch {
2678
+ }
2679
+ }
2680
+ }
2681
+ return home;
2682
+ }
2683
+ function modelArgs(model) {
2684
+ if (!model || isHarnessDefaultModel(model)) return [];
2685
+ return ["--model", model];
2686
+ }
2687
+ function denyArgs(paths) {
2688
+ return paths.flatMap((p) => ["--deny", `Read(${p}) Edit(${p}) Write(${p})`]);
2689
+ }
2690
+ function newestSessionMtime(dir, depth = 3) {
2691
+ let entries;
2692
+ try {
2693
+ entries = readdirSync5(dir, { withFileTypes: true });
2694
+ } catch {
2695
+ return null;
2696
+ }
2697
+ let latest = null;
2698
+ for (const entry of entries) {
2699
+ const full = join6(dir, entry.name);
2700
+ try {
2701
+ if (entry.isDirectory()) {
2702
+ if (depth <= 0) continue;
2703
+ const nested = newestSessionMtime(full, depth - 1);
2704
+ if (nested != null && (latest == null || nested > latest)) latest = nested;
2705
+ } else if (entry.isFile()) {
2706
+ const mtime2 = statSync4(full).mtimeMs;
2707
+ if (latest == null || mtime2 > latest) latest = mtime2;
2708
+ }
2709
+ } catch {
2710
+ }
2711
+ }
2712
+ return latest;
2713
+ }
2714
+ function num(v) {
2715
+ return typeof v === "number" && Number.isFinite(v) ? v : 0;
2716
+ }
2717
+ function grokAbort(rec) {
2718
+ if (rec.type === "error") {
2719
+ const raw = typeof rec.message === "string" && rec.message ? rec.message : "error";
2720
+ const kind = /tool/i.test(raw) ? "tools" : /401|unauthorized|network|fetch|ECONN|api/i.test(raw) ? "link" : "other";
2721
+ return { kind, raw: raw.slice(0, 300) };
2722
+ }
2723
+ const stop = typeof rec.stopReason === "string" ? rec.stopReason : "";
2724
+ if (rec.is_error === true || stop === "error" || stop.startsWith("error_")) {
2725
+ const raw = stop || "is_error";
2726
+ return { kind: /tool/i.test(raw) ? "tools" : "other", raw };
2727
+ }
2728
+ return void 0;
2729
+ }
2730
+ function parseGrokResult(raw) {
2731
+ const trimmed = raw.trim();
2732
+ if (!trimmed) return null;
2733
+ let parsed;
2734
+ try {
2735
+ parsed = JSON.parse(trimmed);
2736
+ } catch {
2737
+ return null;
2738
+ }
2739
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
2740
+ const rec = parsed;
2741
+ const hasShape = typeof rec.text === "string" || typeof rec.stopReason === "string" || typeof rec.sessionId === "string" || rec.type === "error";
2742
+ if (!hasShape) return null;
2743
+ const reported = typeof rec.total_cost_usd === "number" && Number.isFinite(rec.total_cost_usd);
2744
+ const usage2 = rec.usage;
2745
+ let tokensIn;
2746
+ let tokensOut;
2747
+ if (typeof usage2 === "object" && usage2 !== null) {
2748
+ const u = usage2;
2749
+ const hasAny = ["input_tokens", "output_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"].some(
2750
+ (k) => typeof u[k] === "number"
2751
+ );
2752
+ if (hasAny) {
2753
+ tokensIn = num(u.input_tokens) + num(u.cache_read_input_tokens) + num(u.cache_creation_input_tokens);
2754
+ tokensOut = num(u.output_tokens);
2755
+ }
2756
+ }
2757
+ const aborted = grokAbort(rec);
2758
+ const result = {
2759
+ costUsd: reported ? rec.total_cost_usd : 0
2760
+ };
2761
+ if (!reported) result.warning = "grok: total_cost_usd \u043D\u0435\u0442 \u2014 \u0441\u0442\u043E\u0438\u043C\u043E\u0441\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0430 (\u044D\u0442\u043E \u043D\u0435 \u043D\u043E\u043B\u044C)";
2762
+ if (tokensIn != null && tokensOut != null) {
2763
+ result.tokensIn = tokensIn;
2764
+ result.tokensOut = tokensOut;
2765
+ }
2766
+ if (aborted) result.aborted = aborted;
2767
+ return result;
2768
+ }
2769
+ var grokExecutor;
2770
+ var init_grok = __esm({
2771
+ "src/executors/grok.ts"() {
2772
+ "use strict";
2773
+ init_src();
2774
+ init_swarmHome();
2775
+ init_swarmFiles();
2776
+ init_codex();
2777
+ grokExecutor = {
2778
+ id: "grok",
2779
+ title: "Grok CLI (grok)",
2780
+ binEnv: "SWARM_GROK_BIN",
2781
+ install: {
2782
+ what: "Grok CLI (xAI). \u0421\u0442\u0430\u0432\u0438\u0442\u0441\u044F \u043E\u0444\u0438\u0446\u0438\u0430\u043B\u044C\u043D\u044B\u043C \u0438\u043D\u0441\u0442\u0430\u043B\u043B\u0435\u0440\u043E\u043C, \u043D\u0435 \u0447\u0435\u0440\u0435\u0437 npm.",
2783
+ pkg: "grok",
2784
+ command: "curl -fsSL https://x.ai/cli/install.sh | bash",
2785
+ windowsCommand: "irm https://x.ai/cli/install.ps1 | iex"
2786
+ },
2787
+ resolveBin: (env) => resolveGrokBin(env ?? process.env),
2788
+ plan: (spec) => {
2789
+ const home = ensureGrokHome(spec.worktree, spec.realHome);
2790
+ writeFileSync3(join6(home, "config.toml"), grokMcpToml(spec));
2791
+ writeFileSync3(
2792
+ join6(spec.worktree, SWARM_MCP_CONFIG_FILE),
2793
+ JSON.stringify(
2794
+ { mcpServers: { [spec.mcp.name]: { command: spec.mcp.command, args: spec.mcp.args, env: spec.mcp.env } } },
2795
+ null,
2796
+ 2
2797
+ )
2798
+ );
2799
+ const notice = skillsNotice(spec.skillDirs);
2800
+ const prompt = notice ? `${spec.prompt}
2801
+
2802
+ ${notice}` : spec.prompt;
2803
+ const args = [
2804
+ // `--cwd` стоит первым: по нему реконсайл узнаёт worktree живого процесса.
2805
+ // Флаг настоящий. Выдуманный `--mcp-config` grok отверг бы и дрон не стартовал.
2806
+ "--cwd",
2807
+ spec.worktree,
2808
+ "--no-auto-update",
2809
+ "--output-format",
2810
+ "json",
2811
+ "--yolo",
2812
+ ...spec.continueSession ? ["--continue"] : [],
2813
+ ...modelArgs(spec.model),
2814
+ ...spec.effort ? ["--effort", spec.effort] : [],
2815
+ ...denyArgs(spec.denyPaths),
2816
+ "-p",
2817
+ prompt
2818
+ ];
2819
+ return { bin: resolveGrokBin(), args, env: { GROK_HOME: home } };
2820
+ },
2821
+ parseResult(raw) {
2822
+ return parseGrokResult(raw);
2823
+ },
2824
+ session: {
2825
+ exists: (worktree) => newestSessionMtime(grokSessionDir(worktree)) !== null,
2826
+ mtimeMs: (worktree) => newestSessionMtime(grokSessionDir(worktree))
2827
+ },
2828
+ probe: {
2829
+ args: ["-p", "ok", "--output-format", "json", "--yolo", "--max-turns", "1", "--no-auto-update"],
2830
+ limitPattern: /usage limit/i,
2831
+ authPattern: /not logged in|unauthorized|\b401\b|authentication required|auth required/i
2832
+ }
2833
+ };
2834
+ }
2835
+ });
2836
+
2616
2837
  // src/swarmModels.ts
2617
2838
  function pick2(v) {
2618
2839
  const s = typeof v === "string" ? v.trim() : "";
@@ -2628,10 +2849,22 @@ function forKind(kind, swarm, norm) {
2628
2849
  if (!swarm) return null;
2629
2850
  return isReviewKind(kind) ? norm(swarm.reviewer) ?? norm(swarm.drone) : norm(swarm.drone);
2630
2851
  }
2852
+ function providersFromEnv(env = process.env) {
2853
+ const raw = (env.SWARM_PROVIDERS ?? "").trim();
2854
+ if (!raw) return null;
2855
+ const known = new Set(MODEL_PROVIDERS.map((p) => p.id));
2856
+ const list = [...new Set(raw.split(/[\s,]+/).map((p) => p.trim().toLowerCase()).filter((p) => known.has(p)))];
2857
+ return list.length ? list : null;
2858
+ }
2631
2859
  function resolveRunModelRef(run, swarm, env = process.env) {
2632
- const configured = parseModelRef(
2633
- pick2(run.model) ?? forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL)
2634
- );
2860
+ const pinned = parseModelRef(pick2(run.model));
2861
+ if (pinned) return pinned;
2862
+ const configured = parseModelRef(forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL));
2863
+ const own = providersFromEnv(env);
2864
+ if (own) {
2865
+ if (configured && own.includes(configured.providerId)) return configured;
2866
+ return { providerId: own[0], model: defaultModelFor(modelRoleOf(run.kind), own[0]) };
2867
+ }
2635
2868
  if (configured) return configured;
2636
2869
  const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
2637
2870
  return { providerId, model: defaultModelFor(modelRoleOf(run.kind), providerId) };
@@ -2678,21 +2911,24 @@ var init_executors = __esm({
2678
2911
  init_src();
2679
2912
  init_claudeCode();
2680
2913
  init_codex();
2914
+ init_grok();
2681
2915
  init_swarmModels();
2682
2916
  init_types2();
2683
2917
  init_claudeCode();
2684
2918
  init_codex();
2919
+ init_grok();
2685
2920
  DEFAULT_EXECUTOR_ID = "claude-code";
2686
2921
  EXECUTORS = {
2687
2922
  [claudeCodeExecutor.id]: claudeCodeExecutor,
2688
- [codexExecutor.id]: codexExecutor
2923
+ [codexExecutor.id]: codexExecutor,
2924
+ [grokExecutor.id]: grokExecutor
2689
2925
  };
2690
2926
  }
2691
2927
  });
2692
2928
 
2693
2929
  // src/config.ts
2694
- import { basename, join as join6, resolve } from "path";
2695
- import { readFileSync as readFileSync3, realpathSync, renameSync, writeFileSync as writeFileSync3 } from "fs";
2930
+ import { basename, join as join7, resolve } from "path";
2931
+ import { readFileSync as readFileSync3, realpathSync, renameSync, writeFileSync as writeFileSync4 } from "fs";
2696
2932
  function normalizeRepo(repo) {
2697
2933
  const r = (repo ?? "").trim();
2698
2934
  if (!r) return "";
@@ -2706,10 +2942,10 @@ function effectiveRepoOf(repo, fallback) {
2706
2942
  return normalizeRepo(repo || fallback || "");
2707
2943
  }
2708
2944
  function droneWorktreePath(worktreesDir, repo, slug) {
2709
- return join6(droneWorktreeRoot(worktreesDir, repo), slug);
2945
+ return join7(droneWorktreeRoot(worktreesDir, repo), slug);
2710
2946
  }
2711
2947
  function droneWorktreeRoot(worktreesDir, repo) {
2712
- return join6(worktreesDir, basename(normalizeRepo(repo)));
2948
+ return join7(worktreesDir, basename(normalizeRepo(repo)));
2713
2949
  }
2714
2950
  function resolveEffectiveWorktreesDir(deps) {
2715
2951
  if (deps.envOverride) return deps.envOverride;
@@ -2736,7 +2972,7 @@ function mcpEndpoint(api, path = TASKLESS_MCP_PATH) {
2736
2972
  }
2737
2973
  function tokenFromFile() {
2738
2974
  try {
2739
- return readFileSync3(join6(SWARM_HOME, "token"), "utf8").trim();
2975
+ return readFileSync3(join7(SWARM_HOME, "token"), "utf8").trim();
2740
2976
  } catch {
2741
2977
  return "";
2742
2978
  }
@@ -2749,25 +2985,25 @@ var init_config = __esm({
2749
2985
  init_executors();
2750
2986
  init_swarmHome();
2751
2987
  init_swarmHome();
2752
- DB_PATH = process.env.SWARM_DB || join6(SWARM_HOME, "swarm.db");
2988
+ DB_PATH = process.env.SWARM_DB || join7(SWARM_HOME, "swarm.db");
2753
2989
  EXECUTOR = resolveExecutor(process.env.SWARM_EXECUTOR);
2754
- DRONE_HOME = process.env.SWARM_DRONE_HOME || join6(SWARM_HOME, "drone-home");
2990
+ DRONE_HOME = process.env.SWARM_DRONE_HOME || join7(SWARM_HOME, "drone-home");
2755
2991
  DRONE_HOME_ISOLATION = process.env.SWARM_DRONE_ISOLATE_HOME !== "0" && process.platform !== "win32";
2756
2992
  DRONE_ISOLATION = DRONE_HOME_ISOLATION ? { home: DRONE_HOME, swarmHome: SWARM_HOME } : void 0;
2757
- WORKTREES_NOINDEX_DIR = join6(SWARM_HOME, "worktrees.noindex");
2758
- WORKTREES_LEGACY_DIR = join6(SWARM_HOME, "worktrees");
2993
+ WORKTREES_NOINDEX_DIR = join7(SWARM_HOME, "worktrees.noindex");
2994
+ WORKTREES_LEGACY_DIR = join7(SWARM_HOME, "worktrees");
2759
2995
  WORKTREES_DIR = process.env.SWARM_WORKTREES || WORKTREES_NOINDEX_DIR;
2760
- RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS || join6(SWARM_HOME, "run-logs");
2761
- DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG || join6(SWARM_HOME, "overmind.log");
2996
+ RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS || join7(SWARM_HOME, "run-logs");
2997
+ DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG || join7(SWARM_HOME, "overmind.log");
2762
2998
  DAEMON_LOG_MAX_BYTES = envNum("SWARM_DAEMON_LOG_MAX_BYTES", 16 * 1024 * 1024);
2763
2999
  DAEMON_LOG_KEEP = envNum("SWARM_DAEMON_LOG_KEEP", 2);
2764
3000
  DAEMON_LOG_CHECK_MS = envNum("SWARM_DAEMON_LOG_CHECK_MS", 6e4);
2765
3001
  DIST_BUILD = true;
2766
3002
  SWARM_BACKEND = process.env.SWARM_BACKEND === "client" ? "client" : process.env.SWARM_BACKEND === "sqlite" ? "sqlite" : DIST_BUILD ? "client" : "sqlite";
2767
- SWARM_OUTBOX = process.env.SWARM_OUTBOX || join6(SWARM_HOME, "outbox.jsonl");
3003
+ SWARM_OUTBOX = process.env.SWARM_OUTBOX || join7(SWARM_HOME, "outbox.jsonl");
2768
3004
  SWARM_MAX_IN_FLIGHT = Math.trunc(envNum("SWARM_MAX_IN_FLIGHT", 3));
2769
3005
  SWARM_CLAIM_INTERVAL_MS = envNum("SWARM_CLAIM_INTERVAL_MS", 6e4);
2770
- ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join6(SWARM_HOME, "env-cache");
3006
+ ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join7(SWARM_HOME, "env-cache");
2771
3007
  ENV_CACHE_ENABLED = process.env.SWARM_ENV_CACHE !== "0";
2772
3008
  ENV_CACHE_KEEP = Math.trunc(envNum("SWARM_ENV_CACHE_KEEP", 4));
2773
3009
  SWARM_MAX_LOAD_FACTOR = envNum("SWARM_MAX_LOAD_FACTOR", 0.9);
@@ -2797,15 +3033,15 @@ var init_config = __esm({
2797
3033
  DRONE_CHANNEL_ENABLED = process.env.SWARM_DRONE_CHANNEL !== "0";
2798
3034
  DRONE_CHANNEL_PORT = Number(process.env.SWARM_DRONE_CHANNEL_PORT) || 7421;
2799
3035
  DRONE_CHANNEL_URL = process.env.SWARM_DRONE_CHANNEL_URL || `http://127.0.0.1:${DRONE_CHANNEL_PORT}`;
2800
- DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET || join6(SWARM_HOME, "drone-channel.key");
3036
+ DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET || join7(SWARM_HOME, "drone-channel.key");
2801
3037
  DRONE_CHANNEL_GRACE_MS = envNum("SWARM_DRONE_CHANNEL_GRACE_MS", 12e4);
2802
- HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join6(SWARM_HOME, "overmind.heartbeat");
3038
+ HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join7(SWARM_HOME, "overmind.heartbeat");
2803
3039
  HEARTBEAT_STALE_MS = Number(process.env.SWARM_HEARTBEAT_STALE_MS) || 3e4;
2804
3040
  SWARM_REPO = process.env.SWARM_REPO || "";
2805
3041
  SWARM_SCAN_DEPTH = Number(process.env.SWARM_SCAN_DEPTH) || 3;
2806
3042
  SWARM_REPO_PATHS = (process.env.SWARM_REPOS || SWARM_REPO).split(",").map((s) => s.trim()).filter(Boolean);
2807
3043
  SWARM_SCAN_ROOTS_RAW = process.env.SWARM_SCAN_ROOTS || "";
2808
- REPO_MAP_PATH = process.env.SWARM_REPO_MAP || join6(SWARM_HOME, "repo-map.json");
3044
+ REPO_MAP_PATH = process.env.SWARM_REPO_MAP || join7(SWARM_HOME, "repo-map.json");
2809
3045
  SWARM_REPO_MAP_MS = envNum("SWARM_REPO_MAP_MS", 15 * 6e4);
2810
3046
  SWARM_REPO_SCAN_TTL_MS = envNum("SWARM_REPO_SCAN_TTL_MS", 24 * 60 * 6e4);
2811
3047
  TASKLESS_API = process.env.TASKLESS_API || "https://taskless.ru";
@@ -2820,7 +3056,7 @@ var init_config = __esm({
2820
3056
  SWARM_AUTO_REVIEW = process.env.SWARM_AUTO_REVIEW !== "0";
2821
3057
  SWARM_AUTO_VERIFY = process.env.SWARM_AUTO_VERIFY !== "0";
2822
3058
  SWARM_MERGE_QUEUE = process.env.SWARM_MERGE_QUEUE !== "0";
2823
- REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join6(SWARM_HOME, "review-cutoff.json");
3059
+ REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join7(SWARM_HOME, "review-cutoff.json");
2824
3060
  SWARM_REVIEW_CAP = Math.trunc(envNum("SWARM_REVIEW_CAP", 1));
2825
3061
  SWARM_STORM_URL = process.env.SWARM_STORM_URL || "";
2826
3062
  WEB_PORT = Number(process.env.SWARM_WEB_PORT) || 7420;
@@ -3148,8 +3384,8 @@ var init_store = __esm({
3148
3384
  });
3149
3385
 
3150
3386
  // src/agentToken.ts
3151
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, unlinkSync } from "fs";
3152
- import { join as join7 } from "path";
3387
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, unlinkSync } from "fs";
3388
+ import { join as join8 } from "path";
3153
3389
  function loadAgentToken() {
3154
3390
  try {
3155
3391
  const s = JSON.parse(readFileSync5(STORE_PATH, "utf8"));
@@ -3159,7 +3395,7 @@ function loadAgentToken() {
3159
3395
  }
3160
3396
  }
3161
3397
  function saveAgentToken(s) {
3162
- writeFileSync4(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
3398
+ writeFileSync5(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
3163
3399
  }
3164
3400
  function clearAgentToken() {
3165
3401
  try {
@@ -3230,7 +3466,7 @@ var init_agentToken = __esm({
3230
3466
  "src/agentToken.ts"() {
3231
3467
  "use strict";
3232
3468
  init_config();
3233
- STORE_PATH = join7(SWARM_HOME, "agent.json");
3469
+ STORE_PATH = join8(SWARM_HOME, "agent.json");
3234
3470
  base = (api) => api.replace(/\/$/, "");
3235
3471
  TOKEN_PROBE_TIMEOUT_MS = 8e3;
3236
3472
  }
@@ -3616,7 +3852,7 @@ var init_package = __esm({
3616
3852
  "package.json"() {
3617
3853
  package_default = {
3618
3854
  name: "@taskless-app/swarm",
3619
- version: "0.6.62",
3855
+ version: "0.6.63",
3620
3856
  type: "module",
3621
3857
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
3622
3858
  keywords: [
@@ -3667,7 +3903,7 @@ var init_package = __esm({
3667
3903
  execa: "^10.0.0",
3668
3904
  zod: "3.25.76"
3669
3905
  },
3670
- gitHead: "e999630218010a25c03a6024e1b71ef34b2359ec"
3906
+ gitHead: "8d438b6288da036308528cff1160a157b4b64abd"
3671
3907
  };
3672
3908
  }
3673
3909
  });
@@ -3833,7 +4069,7 @@ var init_netTimeout = __esm({
3833
4069
 
3834
4070
  // src/droneChannel.ts
3835
4071
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
3836
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
4072
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
3837
4073
  import { dirname as dirname3 } from "path";
3838
4074
  import { serve } from "@hono/node-server";
3839
4075
  function loadChannelSecret(path) {
@@ -3843,8 +4079,8 @@ function loadChannelSecret(path) {
3843
4079
  } catch {
3844
4080
  }
3845
4081
  const secret = randomBytes(32).toString("hex");
3846
- mkdirSync7(dirname3(path), { recursive: true });
3847
- writeFileSync7(path, secret, { mode: 384 });
4082
+ mkdirSync8(dirname3(path), { recursive: true });
4083
+ writeFileSync8(path, secret, { mode: 384 });
3848
4084
  try {
3849
4085
  chmodSync2(path, 384);
3850
4086
  } catch {
@@ -3921,8 +4157,8 @@ var init_authState = __esm({
3921
4157
 
3922
4158
  // src/oauth.ts
3923
4159
  import { createHash, randomBytes as randomBytes2 } from "crypto";
3924
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3 } from "fs";
3925
- import { join as join12 } from "path";
4160
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3 } from "fs";
4161
+ import { join as join13 } from "path";
3926
4162
  function makePkce() {
3927
4163
  const verifier = randomBytes2(48).toString("base64url");
3928
4164
  const challenge = createHash("sha256").update(verifier).digest("base64url");
@@ -4012,7 +4248,7 @@ function loadStore() {
4012
4248
  }
4013
4249
  }
4014
4250
  function saveStore(s) {
4015
- writeFileSync8(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
4251
+ writeFileSync9(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
4016
4252
  }
4017
4253
  function clearStore() {
4018
4254
  try {
@@ -4031,7 +4267,7 @@ async function ensureClientId(api, redirectUri, fetchFn = fetch) {
4031
4267
  const existing = loadClientId();
4032
4268
  if (existing) return existing;
4033
4269
  const clientId = await registerClient({ api, redirectUri }, fetchFn);
4034
- writeFileSync8(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
4270
+ writeFileSync9(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
4035
4271
  return clientId;
4036
4272
  }
4037
4273
  async function ensureAccess(store, opts = {}) {
@@ -4057,16 +4293,16 @@ var init_oauth = __esm({
4057
4293
  AUTHORIZE = "/api/oauth/authorize";
4058
4294
  TOKEN = "/api/oauth/token";
4059
4295
  SCOPE = "openid profile email taskless:full";
4060
- STORE_PATH2 = join12(SWARM_HOME, "oauth.json");
4061
- CLIENT_PATH = join12(SWARM_HOME, "oauth-client.json");
4296
+ STORE_PATH2 = join13(SWARM_HOME, "oauth.json");
4297
+ CLIENT_PATH = join13(SWARM_HOME, "oauth-client.json");
4062
4298
  base2 = (api) => api.replace(/\/$/, "");
4063
4299
  }
4064
4300
  });
4065
4301
 
4066
4302
  // src/git.ts
4067
- import { existsSync as existsSync10, readdirSync as readdirSync9, realpathSync as realpathSync2, statSync as statSync9 } from "fs";
4303
+ import { existsSync as existsSync11, readdirSync as readdirSync10, realpathSync as realpathSync2, statSync as statSync10 } from "fs";
4068
4304
  import { appendFile, chmod, mkdir, readFile, rename, rm, writeFile } from "fs/promises";
4069
- import { isAbsolute, join as join15, resolve as resolve2, sep as sep2 } from "path";
4305
+ import { isAbsolute, join as join16, resolve as resolve2, sep as sep2 } from "path";
4070
4306
  import { $ as $5 } from "execa";
4071
4307
  function gitNet(repo, args, timeoutMs) {
4072
4308
  return withHardTimeout(
@@ -4080,14 +4316,14 @@ async function reclaimStaleWorktree(repo, worktree) {
4080
4316
  await detachWorktree(repo, worktree);
4081
4317
  }
4082
4318
  async function inspectWorktreePath(path) {
4083
- if (!existsSync10(path)) return "free";
4319
+ if (!existsSync11(path)) return "free";
4084
4320
  try {
4085
- if (readdirSync9(path).length === 0) return "free";
4321
+ if (readdirSync10(path).length === 0) return "free";
4086
4322
  } catch {
4087
4323
  return "orphan";
4088
4324
  }
4089
4325
  try {
4090
- if (statSync9(join15(path, ".git")).isDirectory()) return "main-checkout";
4326
+ if (statSync10(join16(path, ".git")).isDirectory()) return "main-checkout";
4091
4327
  } catch {
4092
4328
  }
4093
4329
  const top = await $5({ reject: false })`git -C ${path} rev-parse --show-toplevel`;
@@ -4126,7 +4362,7 @@ async function clearWorktreePath(repo, path, branch, opts = {}) {
4126
4362
  await detachWorktree(repo, path);
4127
4363
  opts.onStaleReclaim?.(path);
4128
4364
  }
4129
- if (!existsSync10(path)) return;
4365
+ if (!existsSync11(path)) return;
4130
4366
  await parkOrphanDir(repo, path, branch);
4131
4367
  }
4132
4368
  async function parkOrphanDir(repo, path, branch) {
@@ -4147,7 +4383,7 @@ async function parkOrphanDir(repo, path, branch) {
4147
4383
  }
4148
4384
  function isLinkedWorktree(dir) {
4149
4385
  try {
4150
- return statSync9(join15(dir, ".git")).isFile();
4386
+ return statSync10(join16(dir, ".git")).isFile();
4151
4387
  } catch {
4152
4388
  return false;
4153
4389
  }
@@ -4159,7 +4395,7 @@ async function detachWorktree(repo, worktree) {
4159
4395
  }
4160
4396
  }
4161
4397
  async function ensureWorktreeDirGone(worktree) {
4162
- if (!existsSync10(worktree)) return false;
4398
+ if (!existsSync11(worktree)) return false;
4163
4399
  if (!isLinkedWorktree(worktree)) {
4164
4400
  console.error(
4165
4401
  `[git] ${worktree} \u2014 \u043D\u0435 \u043F\u0440\u0438\u0441\u0442\u0451\u0433\u043D\u0443\u0442\u044B\u0439 worktree (.git \u043D\u0435 \u0444\u0430\u0439\u043B): \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u041D\u0415 \u0442\u0440\u043E\u0433\u0430\u044E. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u043E worktree \u0434\u0440\u043E\u043D\u0430 \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0433\u043E \u0440\u0443\u043A\u0430\u043C\u0438; \u0435\u0441\u043B\u0438 \u044D\u0442\u043E \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u0447\u0435\u043A\u0430\u0443\u0442, \u0437\u043D\u0430\u0447\u0438\u0442 \u0432\u0435\u0442\u043A\u0430 \u0441\u043B\u0430\u0433\u0430 \u0447\u0435\u043A\u043D\u0443\u0442\u0430 \u0432 \u043D\u0451\u043C (git checkout <slug>) \u2014 \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u0435 \u0435\u0433\u043E \u043D\u0430 \u0434\u0440\u0443\u0433\u0443\u044E \u0432\u0435\u0442\u043A\u0443.`
@@ -4174,7 +4410,7 @@ async function ensureWorktreeDirGone(worktree) {
4174
4410
  );
4175
4411
  return true;
4176
4412
  }
4177
- if (existsSync10(worktree)) {
4413
+ if (existsSync11(worktree)) {
4178
4414
  console.error(`[git] \u043A\u0430\u0442\u0430\u043B\u043E\u0433 worktree ${worktree} \u043F\u0435\u0440\u0435\u0436\u0438\u043B rm \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u0443\u043A\u0430\u043C\u0438, \u0438\u043D\u0430\u0447\u0435 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0441\u043F\u0430\u0432\u043D \u0441\u043B\u0430\u0433\u0430 \u0432\u0441\u0442\u0430\u043D\u0435\u0442.`);
4179
4415
  }
4180
4416
  return true;
@@ -4223,7 +4459,7 @@ async function checkoutWorktree(repo, path, branch, opts) {
4223
4459
  }
4224
4460
  const busyAt = await worktreeForBranch(repo, branch);
4225
4461
  if (busyAt) {
4226
- const exists = existsSync10(busyAt);
4462
+ const exists = existsSync11(busyAt);
4227
4463
  const live = exists && (opts.isWorktreeHeldLive ? await opts.isWorktreeHeldLive(busyAt) : true);
4228
4464
  if (live) throw new WorktreeBusyError(branch, busyAt);
4229
4465
  if (exists && await worktreeDirty(busyAt)) throw new WorktreeBusyError(branch, busyAt);
@@ -4254,8 +4490,8 @@ async function ensureGitExcluded(worktree, pattern) {
4254
4490
  try {
4255
4491
  const commonDir = await gitPath(worktree, "--git-common-dir");
4256
4492
  if (!commonDir) return false;
4257
- const infoDir = join15(commonDir, "info");
4258
- const excludeFile = join15(infoDir, "exclude");
4493
+ const infoDir = join16(commonDir, "info");
4494
+ const excludeFile = join16(infoDir, "exclude");
4259
4495
  let current = "";
4260
4496
  try {
4261
4497
  current = await readFile(excludeFile, "utf8");
@@ -4275,7 +4511,7 @@ async function gitPath(worktree, flag) {
4275
4511
  const out = await $5({ reject: false })`git -C ${worktree} rev-parse ${flag}`;
4276
4512
  const raw = out.stdout.trim();
4277
4513
  if (out.exitCode !== 0 || !raw) return null;
4278
- return isAbsolute(raw) ? raw : join15(worktree, raw);
4514
+ return isAbsolute(raw) ? raw : join16(worktree, raw);
4279
4515
  }
4280
4516
  async function ensureWorktreeHygiene(worktree) {
4281
4517
  for (const pattern of SWARM_EXCLUDE_PATTERNS) await ensureGitExcluded(worktree, pattern);
@@ -4285,12 +4521,12 @@ async function installGuardHooks(worktree) {
4285
4521
  try {
4286
4522
  const gitDir = await gitPath(worktree, "--git-dir");
4287
4523
  if (!gitDir) return false;
4288
- const hooksDir = join15(gitDir, "swarm-hooks");
4524
+ const hooksDir = join16(gitDir, "swarm-hooks");
4289
4525
  const prev = await previousHooksPath(worktree);
4290
4526
  await mkdir(hooksDir, { recursive: true });
4291
4527
  for (const hook of GUARD_HOOKS) {
4292
- await writeFile(join15(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
4293
- await chmod(join15(hooksDir, hook), 493);
4528
+ await writeFile(join16(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
4529
+ await chmod(join16(hooksDir, hook), 493);
4294
4530
  }
4295
4531
  await $5`git -C ${worktree} config extensions.worktreeConfig true`;
4296
4532
  await $5`git -C ${worktree} config --worktree core.hooksPath ${posix(hooksDir)}`;
@@ -4309,7 +4545,7 @@ async function previousHooksPath(worktree) {
4309
4545
  if (out.exitCode === 0 && value) return value;
4310
4546
  }
4311
4547
  const commonDir = await gitPath(worktree, "--git-common-dir");
4312
- return commonDir ? join15(commonDir, "hooks") : null;
4548
+ return commonDir ? join16(commonDir, "hooks") : null;
4313
4549
  }
4314
4550
  async function removeWorktree(repo, path) {
4315
4551
  const branch = (await $5({ reject: false })`git -C ${path} symbolic-ref --quiet --short HEAD`).stdout.trim();
@@ -4372,7 +4608,7 @@ var init_git = __esm({
4372
4608
 
4373
4609
  // src/attachments.ts
4374
4610
  import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
4375
- import { join as join16 } from "path";
4611
+ import { join as join17 } from "path";
4376
4612
  var ATTACHMENTS_DIR, ATTACH_MAX_TOTAL_BYTES, ATTACH_MAX_FILE_BYTES, IMAGE_EXT, DOC_EXT, TEXT_EXT, KNOWN_EXT;
4377
4613
  var init_attachments = __esm({
4378
4614
  "src/attachments.ts"() {
@@ -4391,7 +4627,7 @@ var init_attachments = __esm({
4391
4627
 
4392
4628
  // src/shotUpload.ts
4393
4629
  import { readFile as fsReadFile } from "fs/promises";
4394
- import { basename as basename5, isAbsolute as isAbsolute2, join as join17 } from "path";
4630
+ import { basename as basename5, isAbsolute as isAbsolute2, join as join18 } from "path";
4395
4631
  var SHOT_MAX_FILE_BYTES;
4396
4632
  var init_shotUpload = __esm({
4397
4633
  "src/shotUpload.ts"() {
@@ -4401,8 +4637,8 @@ var init_shotUpload = __esm({
4401
4637
  });
4402
4638
 
4403
4639
  // src/workspace.ts
4404
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, unlinkSync as unlinkSync4 } from "fs";
4405
- import { join as join18 } from "path";
4640
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, unlinkSync as unlinkSync4 } from "fs";
4641
+ import { join as join19 } from "path";
4406
4642
  function loadWorkspace() {
4407
4643
  try {
4408
4644
  const w = JSON.parse(readFileSync11(WORKSPACE_PATH, "utf8"));
@@ -4412,7 +4648,7 @@ function loadWorkspace() {
4412
4648
  }
4413
4649
  }
4414
4650
  function saveWorkspace(w) {
4415
- writeFileSync9(WORKSPACE_PATH, JSON.stringify(w, null, 2), { mode: 384 });
4651
+ writeFileSync10(WORKSPACE_PATH, JSON.stringify(w, null, 2), { mode: 384 });
4416
4652
  }
4417
4653
  function clearWorkspace() {
4418
4654
  try {
@@ -4428,7 +4664,7 @@ var init_workspace = __esm({
4428
4664
  "src/workspace.ts"() {
4429
4665
  "use strict";
4430
4666
  init_config();
4431
- WORKSPACE_PATH = join18(SWARM_HOME, "workspace.json");
4667
+ WORKSPACE_PATH = join19(SWARM_HOME, "workspace.json");
4432
4668
  }
4433
4669
  });
4434
4670
 
@@ -4846,13 +5082,13 @@ var init_daemonLink = __esm({
4846
5082
  });
4847
5083
 
4848
5084
  // src/pauseState.ts
4849
- import { existsSync as existsSync11, readFileSync as readFileSync12, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync10 } from "fs";
4850
- import { join as join19 } from "path";
5085
+ import { existsSync as existsSync12, readFileSync as readFileSync12, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync11 } from "fs";
5086
+ import { join as join20 } from "path";
4851
5087
  function pauseStatePath(home) {
4852
- return join19(home, "pause.json");
5088
+ return join20(home, "pause.json");
4853
5089
  }
4854
5090
  function loadPauseState(home, io = {}) {
4855
- const exists = io.exists ?? existsSync11;
5091
+ const exists = io.exists ?? existsSync12;
4856
5092
  const readFile2 = io.readFile ?? ((p) => readFileSync12(p, "utf8"));
4857
5093
  const path = pauseStatePath(home);
4858
5094
  if (!exists(path)) return ACTIVE;
@@ -4872,12 +5108,12 @@ function loadPauseState(home, io = {}) {
4872
5108
  function savePauseState(home, state, io = {}) {
4873
5109
  const path = pauseStatePath(home);
4874
5110
  if (!state.paused) {
4875
- const remove = io.remove ?? ((p) => existsSync11(p) && unlinkSync5(p));
5111
+ const remove = io.remove ?? ((p) => existsSync12(p) && unlinkSync5(p));
4876
5112
  remove(path);
4877
5113
  return;
4878
5114
  }
4879
5115
  const writeFile3 = io.writeFile ?? ((p, data) => {
4880
- writeFileSync10(`${p}.tmp`, data);
5116
+ writeFileSync11(`${p}.tmp`, data);
4881
5117
  renameSync5(`${p}.tmp`, p);
4882
5118
  });
4883
5119
  writeFile3(path, JSON.stringify(state, null, 2));
@@ -4914,7 +5150,7 @@ var init_pauseState = __esm({
4914
5150
  });
4915
5151
 
4916
5152
  // src/heartbeat.ts
4917
- import { readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync11 } from "fs";
5153
+ import { readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync12 } from "fs";
4918
5154
  function readHeartbeat(path) {
4919
5155
  try {
4920
5156
  const ms = Number(readFileSync13(path, "utf8").trim());
@@ -4936,7 +5172,7 @@ __export(db_exports, {
4936
5172
  });
4937
5173
  import { createRequire as createRequire2 } from "module";
4938
5174
  import { randomUUID as randomUUID3 } from "crypto";
4939
- import { existsSync as existsSync12 } from "fs";
5175
+ import { existsSync as existsSync13 } from "fs";
4940
5176
  import { resolve as resolve3 } from "path";
4941
5177
  function rowToQuestion(row) {
4942
5178
  return {
@@ -5600,7 +5836,7 @@ CREATE TABLE IF NOT EXISTS imports (
5600
5836
  const repoN = normalizeRepo(repo);
5601
5837
  const already = this.db.prepare("SELECT run_count FROM imports WHERE source = ?").get(abs);
5602
5838
  if (already) return { imported: false, runs: already.run_count };
5603
- if (!existsSync12(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
5839
+ if (!existsSync13(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
5604
5840
  const src = new this.databaseCtor(abs);
5605
5841
  try {
5606
5842
  const runs = src.prepare("SELECT * FROM runs ORDER BY id").all();
@@ -5928,8 +6164,8 @@ CREATE TABLE IF NOT EXISTS imports (
5928
6164
  });
5929
6165
 
5930
6166
  // src/repoScan.ts
5931
- import { readdirSync as readdirSync10, readFileSync as readFileSync14, statSync as statSync10 } from "fs";
5932
- import { join as join20 } from "path";
6167
+ import { readdirSync as readdirSync11, readFileSync as readFileSync14, statSync as statSync11 } from "fs";
6168
+ import { join as join21 } from "path";
5933
6169
  var init_repoScan = __esm({
5934
6170
  "src/repoScan.ts"() {
5935
6171
  "use strict";
@@ -5939,8 +6175,8 @@ var init_repoScan = __esm({
5939
6175
 
5940
6176
  // src/repoDiscovery.ts
5941
6177
  import { homedir as homedir6 } from "os";
5942
- import { readFileSync as readFileSync15, renameSync as renameSync7, writeFileSync as writeFileSync12 } from "fs";
5943
- import { isAbsolute as isAbsolute3, join as join21, resolve as resolve4 } from "path";
6178
+ import { readFileSync as readFileSync15, renameSync as renameSync7, writeFileSync as writeFileSync13 } from "fs";
6179
+ import { isAbsolute as isAbsolute3, join as join22, resolve as resolve4 } from "path";
5944
6180
  function repoMapLines(rows) {
5945
6181
  if (!rows?.length) return [];
5946
6182
  const missing = rows.filter((r) => !r.path).length;
@@ -6331,10 +6567,10 @@ __export(statusCli_exports, {
6331
6567
  probeLink: () => probeLink,
6332
6568
  runStatusCli: () => runStatusCli
6333
6569
  });
6334
- import { existsSync as existsSync13, readFileSync as readFileSync16 } from "fs";
6570
+ import { existsSync as existsSync14, readFileSync as readFileSync16 } from "fs";
6335
6571
  function outboxPending(path) {
6336
6572
  try {
6337
- if (!existsSync13(path)) return 0;
6573
+ if (!existsSync14(path)) return 0;
6338
6574
  return readFileSync16(path, "utf8").split("\n").filter((l) => l.trim()).length;
6339
6575
  } catch {
6340
6576
  return null;
@@ -6542,8 +6778,8 @@ var init_prodMergeGate = __esm({
6542
6778
  });
6543
6779
 
6544
6780
  // src/platform.ts
6545
- import { existsSync as existsSync14, readFileSync as readFileSync17 } from "fs";
6546
- import { delimiter, join as join22 } from "path";
6781
+ import { existsSync as existsSync15, readFileSync as readFileSync17 } from "fs";
6782
+ import { delimiter, join as join23 } from "path";
6547
6783
  function safeReadText(path) {
6548
6784
  try {
6549
6785
  return readFileSync17(path, "utf8");
@@ -6552,13 +6788,13 @@ function safeReadText(path) {
6552
6788
  }
6553
6789
  }
6554
6790
  function resolveCommandPath(name, env = process.env, platform = process.platform) {
6555
- if (name.includes("/") || name.includes("\\")) return existsSync14(name) ? name : null;
6791
+ if (name.includes("/") || name.includes("\\")) return existsSync15(name) ? name : null;
6556
6792
  const dirs = (env.PATH ?? env.Path ?? "").split(delimiter).filter(Boolean);
6557
6793
  const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
6558
6794
  for (const dir of dirs) {
6559
6795
  for (const ext of exts) {
6560
- const candidate = join22(dir, name + ext);
6561
- if (existsSync14(candidate)) return candidate;
6796
+ const candidate = join23(dir, name + ext);
6797
+ if (existsSync15(candidate)) return candidate;
6562
6798
  }
6563
6799
  }
6564
6800
  return null;
@@ -6661,13 +6897,13 @@ var init_platform = __esm({
6661
6897
  });
6662
6898
 
6663
6899
  // src/envGate.ts
6664
- import { existsSync as existsSync15 } from "fs";
6665
- import { join as join23 } from "path";
6900
+ import { existsSync as existsSync16 } from "fs";
6901
+ import { join as join24 } from "path";
6666
6902
  import { createConnection } from "net";
6667
6903
  import { execa as execa3 } from "execa";
6668
6904
  function detectRequirements(repo, io = {}) {
6669
- const exists = io.exists ?? existsSync15;
6670
- const marker = COMPOSE_MARKERS.find((f) => exists(join23(repo, f)));
6905
+ const exists = io.exists ?? existsSync16;
6906
+ const marker = COMPOSE_MARKERS.find((f) => exists(join24(repo, f)));
6671
6907
  return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
6672
6908
  }
6673
6909
  async function dockerAlive() {
@@ -6699,7 +6935,7 @@ var init_envGate = __esm({
6699
6935
  });
6700
6936
 
6701
6937
  // src/doctor.ts
6702
- import { isAbsolute as isAbsolute4, join as join24 } from "path";
6938
+ import { isAbsolute as isAbsolute4, join as join25 } from "path";
6703
6939
  function ok(id, title, detail) {
6704
6940
  return { id, title, status: "ok", detail, remedies: [], critical: false };
6705
6941
  }
@@ -6724,12 +6960,14 @@ function checkExecutor(io, executor = resolveExecutor()) {
6724
6960
  const bin = executor.resolveBin();
6725
6961
  const where = /[/\\]/.test(bin) ? `\u043D\u0430\u0439\u0434\u0435\u043D: ${bin}` : "\u043D\u0430\u0439\u0434\u0435\u043D \u0432 PATH";
6726
6962
  if (io.hasCommand(bin)) return ok("claude", executor.title, where);
6963
+ const custom = executor.install.command == null ? null : io.platform.kind === "windows" && executor.install.windowsCommand ? executor.install.windowsCommand : executor.install.command;
6727
6964
  const remedies = [
6728
6965
  {
6729
6966
  what: executor.install.what,
6730
- command: globalInstallCommand(io.globalPm === "sources" ? "npm" : io.globalPm, executor.install.pkg),
6967
+ command: custom ?? globalInstallCommand(io.globalPm === "sources" ? "npm" : io.globalPm, executor.install.pkg),
6731
6968
  sudo: false,
6732
- auto: true
6969
+ // Свой инсталлер (не npm-пакет) человек ставит сам: автозапуск скрипта с сайта — нет.
6970
+ auto: custom == null
6733
6971
  }
6734
6972
  ];
6735
6973
  const detail = io.platform.kind === "windows" ? `\u0432 PATH \u043D\u0435\u0442 \`${bin}\`. \u041D\u0430 Windows spawn \u0438\u0449\u0435\u0442 \u0444\u0430\u0439\u043B \u043F\u043E PATHEXT \u2014 bash-\u043E\u0431\u0451\u0440\u0442\u043A\u0430 \u0431\u0435\u0437 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u0435\u043C\u0443 \u043D\u0435 \u0432\u0438\u0434\u043D\u0430; \u043D\u0435\u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u043D\u0430\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0437\u0430\u0434\u0430\u0451\u0442\u0441\u044F ${executor.binEnv}` : `\u0432 PATH \u043D\u0435\u0442 \`${bin}\` \u2014 \u0434\u0440\u043E\u043D\u0443 \u043D\u0435\u0447\u0435\u043C \u043F\u0438\u0441\u0430\u0442\u044C \u043A\u043E\u0434`;
@@ -6906,7 +7144,7 @@ function checkRepos(io, snap) {
6906
7144
  critical: true
6907
7145
  };
6908
7146
  }
6909
- if (env && !io.exists(join24(env, ".git"))) {
7147
+ if (env && !io.exists(join25(env, ".git"))) {
6910
7148
  return {
6911
7149
  id: "repos",
6912
7150
  title,
@@ -6916,7 +7154,7 @@ function checkRepos(io, snap) {
6916
7154
  critical: true
6917
7155
  };
6918
7156
  }
6919
- const broken = snap.paths.filter((p) => !io.exists(join24(p, ".git")));
7157
+ const broken = snap.paths.filter((p) => !io.exists(join25(p, ".git")));
6920
7158
  if (broken.length) {
6921
7159
  return {
6922
7160
  id: "repos",
@@ -7106,12 +7344,12 @@ var init_doctor = __esm({
7106
7344
  });
7107
7345
 
7108
7346
  // src/repoMemory.ts
7109
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync13 } from "fs";
7110
- import { join as join25 } from "path";
7347
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
7348
+ import { join as join26 } from "path";
7111
7349
  function loadRememberedRepos(home) {
7112
7350
  let raw;
7113
7351
  try {
7114
- raw = readFileSync18(join25(home, FILE), "utf8");
7352
+ raw = readFileSync18(join26(home, FILE), "utf8");
7115
7353
  } catch {
7116
7354
  return [];
7117
7355
  }
@@ -7147,7 +7385,7 @@ __export(installCli_exports, {
7147
7385
  runInstallHookCli: () => runInstallHookCli,
7148
7386
  shortenError: () => shortenError
7149
7387
  });
7150
- import { existsSync as existsSync16 } from "fs";
7388
+ import { existsSync as existsSync17 } from "fs";
7151
7389
  function classifyChromiumError(message) {
7152
7390
  if (/playwright-core не установлен|Cannot find (module|package)/i.test(message)) return "no-module";
7153
7391
  if (/error while loading shared libraries|cannot open shared object file|missing dependencies|\blib[\w.+-]*\.so[.\d]*\b/i.test(message)) return "no-libs";
@@ -7202,7 +7440,7 @@ function defaultDoctorIO(packageVersion) {
7202
7440
  packageVersion,
7203
7441
  entry,
7204
7442
  hasCommand: (name) => hasCommandInPath(name),
7205
- exists: existsSync16,
7443
+ exists: existsSync17,
7206
7444
  run: (command) => runQuiet(command),
7207
7445
  latestVersion: () => latestMemo ??= fetchLatestVersion(),
7208
7446
  probeChromium: () => probeChromium(),
@@ -7430,8 +7668,8 @@ var init_updateCli = __esm({
7430
7668
 
7431
7669
  // src/cli.ts
7432
7670
  init_cliIo();
7433
- import { basename as basename6, dirname as dirname5, join as join26 } from "path";
7434
- import { existsSync as existsSync17, readFileSync as readFileSync19, writeFileSync as writeFileSync14, mkdirSync as mkdirSync10, readdirSync as readdirSync11, statSync as statSync11, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
7671
+ import { basename as basename6, dirname as dirname5, join as join27 } from "path";
7672
+ import { existsSync as existsSync18, readFileSync as readFileSync19, writeFileSync as writeFileSync15, mkdirSync as mkdirSync11, readdirSync as readdirSync12, statSync as statSync12, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
7435
7673
  import { setTimeout as sleep2 } from "timers/promises";
7436
7674
 
7437
7675
  // src/swarmClient.ts
@@ -7439,7 +7677,7 @@ init_config();
7439
7677
  init_resources();
7440
7678
  init_names2();
7441
7679
  init_storm();
7442
- import { appendFileSync, existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
7680
+ import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
7443
7681
  import { dirname as dirname2 } from "path";
7444
7682
  import { randomUUID } from "crypto";
7445
7683
 
@@ -7472,24 +7710,24 @@ init_store();
7472
7710
  init_toolFailure();
7473
7711
 
7474
7712
  // src/logTail.ts
7475
- import { closeSync, existsSync as existsSync5, openSync, readSync, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
7476
- import { join as join9 } from "path";
7713
+ import { closeSync, existsSync as existsSync6, openSync, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
7714
+ import { join as join10 } from "path";
7477
7715
 
7478
7716
  // src/runLogDir.ts
7479
- import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync5, renameSync as renameSync2, statSync as statSync4 } from "fs";
7480
- import { join as join8 } from "path";
7717
+ import { existsSync as existsSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync6, renameSync as renameSync2, statSync as statSync5 } from "fs";
7718
+ import { join as join9 } from "path";
7481
7719
  function runLogDirName(runLogKey) {
7482
7720
  const safe = runLogKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^[-_]+|[-_]+$/g, "");
7483
7721
  if (!safe) throw new Error(`\u043A\u043B\u044E\u0447 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435 \u0433\u043E\u0434\u0438\u0442\u0441\u044F \u0432 \u0438\u043C\u044F \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 run-log'\u043E\u0432: ${JSON.stringify(runLogKey)}`);
7484
7722
  return /^\d+$/.test(safe) ? `k-${safe}` : safe;
7485
7723
  }
7486
7724
  function runLogDirPath(runLogsDir, runLogKey) {
7487
- return join8(runLogsDir, runLogDirName(runLogKey));
7725
+ return join9(runLogsDir, runLogDirName(runLogKey));
7488
7726
  }
7489
7727
  function findRunLogDir(runLogsDir, keys) {
7490
7728
  for (const key of keys) {
7491
7729
  const dir = runLogDirPath(runLogsDir, key);
7492
- if (existsSync4(dir)) return dir;
7730
+ if (existsSync5(dir)) return dir;
7493
7731
  }
7494
7732
  return null;
7495
7733
  }
@@ -7695,25 +7933,25 @@ var SwarmClient = class _SwarmClient {
7695
7933
  this.repoFullNames = registry.map((e) => e.repoFullName);
7696
7934
  this.repoPathByFullName = new Map(registry.map((e) => [e.repoFullName, normalizeRepo(e.path)]));
7697
7935
  this.host = opts.host ?? null;
7698
- this.repoExists = opts.repoExists ?? existsSync6;
7936
+ this.repoExists = opts.repoExists ?? existsSync7;
7699
7937
  this.questionSyncMs = opts.questionSyncMs ?? 3e3;
7700
7938
  this.executorId = opts.executorId ?? EXECUTOR.id;
7701
7939
  this.outboxPath = opts.outboxPath;
7702
7940
  this.mapPath = `${opts.outboxPath}.prodmap.json`;
7703
7941
  this.stormPath = `${opts.outboxPath}.storm.jsonl`;
7704
- mkdirSync6(dirname2(this.outboxPath), { recursive: true });
7942
+ mkdirSync7(dirname2(this.outboxPath), { recursive: true });
7705
7943
  this.recover();
7706
7944
  }
7707
7945
  // ── рестарт: поднять outbox (незаписанные операции) и карту runKey→prodId ─────
7708
7946
  recover() {
7709
- if (existsSync6(this.mapPath)) {
7947
+ if (existsSync7(this.mapPath)) {
7710
7948
  try {
7711
7949
  const obj = JSON.parse(readFileSync6(this.mapPath, "utf8"));
7712
7950
  for (const [k, v] of Object.entries(obj)) this.prodIdByKey.set(k, v);
7713
7951
  } catch {
7714
7952
  }
7715
7953
  }
7716
- if (existsSync6(this.outboxPath)) {
7954
+ if (existsSync7(this.outboxPath)) {
7717
7955
  const txt = readFileSync6(this.outboxPath, "utf8");
7718
7956
  for (const line of txt.split("\n")) {
7719
7957
  const s = line.trim();
@@ -7734,7 +7972,7 @@ var SwarmClient = class _SwarmClient {
7734
7972
  // eventsLog ПЕРВЫМИ (до hydrate), в порядке файла (= хронологический); id
7735
7973
  // переприсваиваем локально (id событий — только порядок, не корреляция с продом).
7736
7974
  replayGlobalEvents() {
7737
- if (!existsSync6(this.stormPath)) return;
7975
+ if (!existsSync7(this.stormPath)) return;
7738
7976
  let txt;
7739
7977
  try {
7740
7978
  txt = readFileSync6(this.stormPath, "utf8");
@@ -8734,7 +8972,7 @@ var SwarmClient = class _SwarmClient {
8734
8972
  * транзиентным сбоем ОС, который этот PR должен переживать, а не превращать в мор.
8735
8973
  */
8736
8974
  reconcileRuntime(opts) {
8737
- const wtExists = opts.worktreeExists ?? existsSync6;
8975
+ const wtExists = opts.worktreeExists ?? existsSync7;
8738
8976
  const pidByWorktree = /* @__PURE__ */ new Map();
8739
8977
  try {
8740
8978
  for (const p of opts.listProcesses()) if (p.worktree) pidByWorktree.set(p.worktree, p.pid);
@@ -9354,7 +9592,7 @@ var SwarmClient = class _SwarmClient {
9354
9592
  *
9355
9593
  * Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
9356
9594
  */
9357
- async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors) {
9595
+ async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors, providers) {
9358
9596
  try {
9359
9597
  const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
9360
9598
  if (daemonVersion) args.daemonVersion = daemonVersion;
@@ -9385,6 +9623,7 @@ var SwarmClient = class _SwarmClient {
9385
9623
  args.tick = { seq: Math.max(0, Math.trunc(tick.seq)), ageMs: Math.max(0, Math.trunc(tick.ageMs)), intervalMs: Math.trunc(tick.intervalMs) };
9386
9624
  }
9387
9625
  if (executors) args.executorsAvailable = executors;
9626
+ if (providers) args.providersAvailable = providers;
9388
9627
  const res = await this.call("swarm_heartbeat", args);
9389
9628
  if (res?.isError) return EMPTY_HEARTBEAT_REPLY;
9390
9629
  this.heartbeatOkAt = this.now();
@@ -9972,13 +10211,13 @@ var SwarmClient = class _SwarmClient {
9972
10211
  }
9973
10212
  persistPending() {
9974
10213
  try {
9975
- writeFileSync5(this.outboxPath, this.pending.map((e) => JSON.stringify(e)).join("\n") + (this.pending.length ? "\n" : ""));
10214
+ writeFileSync6(this.outboxPath, this.pending.map((e) => JSON.stringify(e)).join("\n") + (this.pending.length ? "\n" : ""));
9976
10215
  } catch {
9977
10216
  }
9978
10217
  }
9979
10218
  persistMap() {
9980
10219
  try {
9981
- writeFileSync5(this.mapPath, JSON.stringify(Object.fromEntries(this.prodIdByKey)));
10220
+ writeFileSync6(this.mapPath, JSON.stringify(Object.fromEntries(this.prodIdByKey)));
9982
10221
  } catch {
9983
10222
  }
9984
10223
  }
@@ -10178,20 +10417,20 @@ function tailFile(path, lines) {
10178
10417
  }
10179
10418
 
10180
10419
  // src/logRotate.ts
10181
- import { copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync6, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync7 } from "fs";
10420
+ import { copyFileSync as copyFileSync4, renameSync as renameSync3, statSync as statSync7, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync8 } from "fs";
10182
10421
  var realIo = {
10183
10422
  size: (path) => {
10184
10423
  try {
10185
- return statSync6(path).size;
10424
+ return statSync7(path).size;
10186
10425
  } catch {
10187
10426
  return null;
10188
10427
  }
10189
10428
  },
10190
- copy: copyFileSync2,
10429
+ copy: copyFileSync4,
10191
10430
  rename: renameSync3,
10192
10431
  truncate: (path) => truncateSync(path, 0),
10193
10432
  unlink: unlinkSync2,
10194
- exists: existsSync7
10433
+ exists: existsSync8
10195
10434
  };
10196
10435
  var STRAY_GENERATIONS = 8;
10197
10436
  function rotateDaemonLog(path, opts, io = realIo) {
@@ -10219,17 +10458,17 @@ init_repoRegistry();
10219
10458
 
10220
10459
  // src/hostIdentity.ts
10221
10460
  import { hostname } from "os";
10222
- import { existsSync as existsSync8, readFileSync as readFileSync8, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "fs";
10223
- import { join as join10 } from "path";
10461
+ import { existsSync as existsSync9, readFileSync as readFileSync8, renameSync as renameSync4, writeFileSync as writeFileSync7 } from "fs";
10462
+ import { join as join11 } from "path";
10224
10463
  import { randomUUID as randomUUID2 } from "crypto";
10225
10464
  function hostIdentityPath(home) {
10226
- return join10(home, "host.json");
10465
+ return join11(home, "host.json");
10227
10466
  }
10228
10467
  function loadHostIdentity(deps) {
10229
- const exists = deps.exists ?? existsSync8;
10468
+ const exists = deps.exists ?? existsSync9;
10230
10469
  const readFile2 = deps.readFile ?? ((p) => readFileSync8(p, "utf8"));
10231
10470
  const writeFile3 = deps.writeFile ?? ((p, data) => {
10232
- writeFileSync6(`${p}.tmp`, data);
10471
+ writeFileSync7(`${p}.tmp`, data);
10233
10472
  renameSync4(`${p}.tmp`, p);
10234
10473
  });
10235
10474
  const host = deps.hostname ?? hostname;
@@ -10344,8 +10583,8 @@ import { basename as basename4 } from "path";
10344
10583
  import { serve as serve2 } from "@hono/node-server";
10345
10584
 
10346
10585
  // src/diff.ts
10347
- import { statSync as statSync7 } from "fs";
10348
- import { join as join11 } from "path";
10586
+ import { statSync as statSync8 } from "fs";
10587
+ import { join as join12 } from "path";
10349
10588
  import { $ as $3 } from "execa";
10350
10589
  var MAX_FILES = 200;
10351
10590
  var MAX_LINES_PER_FILE = 1500;
@@ -10488,7 +10727,7 @@ async function resolveBase(wt, baseBranch) {
10488
10727
  }
10489
10728
  function mtime(path) {
10490
10729
  try {
10491
- return statSync7(path).mtimeMs;
10730
+ return statSync8(path).mtimeMs;
10492
10731
  } catch {
10493
10732
  return 0;
10494
10733
  }
@@ -10513,7 +10752,7 @@ async function collectRunDiff(worktree, opts = {}) {
10513
10752
  files.push(pf);
10514
10753
  }
10515
10754
  }
10516
- files.sort((a, b) => mtime(join11(worktree, b.path)) - mtime(join11(worktree, a.path)));
10755
+ files.sort((a, b) => mtime(join12(worktree, b.path)) - mtime(join12(worktree, a.path)));
10517
10756
  const truncatedFiles = files.length > MAX_FILES;
10518
10757
  if (truncatedFiles) files = files.slice(0, MAX_FILES);
10519
10758
  const added = files.reduce((s, f) => s + f.added, 0);
@@ -10683,9 +10922,9 @@ function mergeQueueCandidates(runs, repo, repoFallback) {
10683
10922
  const byPr = /* @__PURE__ */ new Map();
10684
10923
  for (const r of runs) {
10685
10924
  if (effectiveRepoOf(r.repo, repoFallback) !== repo || !isMergeCandidate(r)) continue;
10686
- const num = prNumberOf(r.pr_url);
10687
- const seen = byPr.get(num);
10688
- if (!seen || r.id > seen.id) byPr.set(num, r);
10925
+ const num2 = prNumberOf(r.pr_url);
10926
+ const seen = byPr.get(num2);
10927
+ if (!seen || r.id > seen.id) byPr.set(num2, r);
10689
10928
  }
10690
10929
  return [...byPr.values()];
10691
10930
  }
@@ -10698,10 +10937,10 @@ function makeMergeQueueResolver(deps) {
10698
10937
  };
10699
10938
  return async (run) => {
10700
10939
  if (!deps.baseSync || !run.pr_url) return null;
10701
- const num = prNumberOf(run.pr_url);
10702
- if (num == null) return null;
10940
+ const num2 = prNumberOf(run.pr_url);
10941
+ if (num2 == null) return null;
10703
10942
  const candidates = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback);
10704
- const mine = candidates.find((c) => prNumberOf(c.pr_url) === num);
10943
+ const mine = candidates.find((c) => prNumberOf(c.pr_url) === num2);
10705
10944
  if (!mine) return null;
10706
10945
  const ready = [];
10707
10946
  const urlOf = /* @__PURE__ */ new Map();
@@ -10817,10 +11056,10 @@ function makeMergeIntentResolver(deps) {
10817
11056
  };
10818
11057
  return async (run) => {
10819
11058
  if (!deps.baseSync || !run.pr_url || !hasMergeIntent(run)) return null;
10820
- const num = prNumberOf(run.pr_url);
10821
- if (num == null) return null;
11059
+ const num2 = prNumberOf(run.pr_url);
11060
+ if (num2 == null) return null;
10822
11061
  const marked = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback).filter(hasMergeIntent);
10823
- const mine = marked.find((c) => prNumberOf(c.pr_url) === num);
11062
+ const mine = marked.find((c) => prNumberOf(c.pr_url) === num2);
10824
11063
  if (!mine) return null;
10825
11064
  const queue = orderMergeQueue(marked.map(entryOf));
10826
11065
  const entry = entryOf(mine);
@@ -12537,15 +12776,15 @@ import { cancelRun, respawnRun, runDaemon, wakeRun, OVERMIND_ENTRY, listDronePro
12537
12776
 
12538
12777
  // src/launcher.ts
12539
12778
  import { spawn as spawn2 } from "child_process";
12540
- import { closeSync as closeSync3, existsSync as existsSync9, mkdirSync as mkdirSync9, openSync as openSync3, readdirSync as readdirSync8 } from "fs";
12779
+ import { closeSync as closeSync3, existsSync as existsSync10, mkdirSync as mkdirSync10, openSync as openSync3, readdirSync as readdirSync9 } from "fs";
12541
12780
  import { createRequire } from "module";
12542
12781
  import { homedir as homedir5 } from "os";
12543
- import { dirname as dirname4, join as join14 } from "path";
12782
+ import { dirname as dirname4, join as join15 } from "path";
12544
12783
  import { fileURLToPath as fileURLToPath2 } from "url";
12545
12784
 
12546
12785
  // src/droneHome.ts
12547
- import { lstatSync, mkdirSync as mkdirSync8, readdirSync as readdirSync7, rmSync, statSync as statSync8, symlinkSync as symlinkSync2 } from "fs";
12548
- import { join as join13, relative, sep } from "path";
12786
+ import { lstatSync, mkdirSync as mkdirSync9, readdirSync as readdirSync8, rmSync, statSync as statSync9, symlinkSync as symlinkSync3 } from "fs";
12787
+ import { join as join14, relative, sep } from "path";
12549
12788
  var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
12550
12789
  function droneHomeDeny(env = process.env) {
12551
12790
  const extra = (env.SWARM_DRONE_HOME_DENY || "").split(",").map((s) => s.trim()).filter(Boolean);
@@ -12563,34 +12802,34 @@ function buildDroneHome(opts) {
12563
12802
  const { realHome, droneHome } = opts;
12564
12803
  const deny = opts.deny ?? droneHomeDeny();
12565
12804
  const platform = opts.platform ?? process.platform;
12566
- mkdirSync8(droneHome, { recursive: true, mode: 448 });
12567
- const wanted = new Set(mirroredEntries(readdirSync7(realHome), deny, platform));
12805
+ mkdirSync9(droneHome, { recursive: true, mode: 448 });
12806
+ const wanted = new Set(mirroredEntries(readdirSync8(realHome), deny, platform));
12568
12807
  const rel = relative(realHome, droneHome);
12569
12808
  if (rel && !rel.startsWith("..") && !rel.includes(sep)) wanted.delete(rel);
12570
- for (const existing of readdirSync7(droneHome)) {
12809
+ for (const existing of readdirSync8(droneHome)) {
12571
12810
  if (wanted.has(existing)) continue;
12572
- const p = join13(droneHome, existing);
12811
+ const p = join14(droneHome, existing);
12573
12812
  try {
12574
12813
  if (lstatSync(p).isSymbolicLink()) rmSync(p);
12575
12814
  } catch {
12576
12815
  }
12577
12816
  }
12578
12817
  for (const name of wanted) {
12579
- const link = join13(droneHome, name);
12818
+ const link = join14(droneHome, name);
12580
12819
  try {
12581
12820
  lstatSync(link);
12582
12821
  continue;
12583
12822
  } catch {
12584
12823
  }
12585
- const target = join13(realHome, name);
12824
+ const target = join14(realHome, name);
12586
12825
  let isDir;
12587
12826
  try {
12588
- isDir = statSync8(target).isDirectory();
12827
+ isDir = statSync9(target).isDirectory();
12589
12828
  } catch {
12590
12829
  continue;
12591
12830
  }
12592
12831
  try {
12593
- symlinkSync2(target, link, isDir ? "junction" : "file");
12832
+ symlinkSync3(target, link, isDir ? "junction" : "file");
12594
12833
  } catch (e) {
12595
12834
  if (e.code !== "EEXIST") throw e;
12596
12835
  }
@@ -12603,7 +12842,7 @@ var REAL_PATH_ENV_PINS = [
12603
12842
  { env: "YARN_GLOBAL_FOLDER", segments: [".yarn", "berry"] }
12604
12843
  ];
12605
12844
  function realPathEnvPins(realHome) {
12606
- return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join13(realHome, ...segments)]));
12845
+ return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join14(realHome, ...segments)]));
12607
12846
  }
12608
12847
  function dronePrivatePaths(realHome, swarmHome) {
12609
12848
  return [
@@ -12686,7 +12925,7 @@ function droneEnv(worktree, base3 = process.env, isolation, droneCap = null) {
12686
12925
  const env = { ...base3 };
12687
12926
  const isolate = base3.SWARM_TURBO_ISOLATE !== "0";
12688
12927
  if (isolate && !base3.TURBO_CACHE_DIR) {
12689
- env.TURBO_CACHE_DIR = join14(worktree, ".turbo", "cache");
12928
+ env.TURBO_CACHE_DIR = join15(worktree, ".turbo", "cache");
12690
12929
  }
12691
12930
  Object.assign(env, droneTestEnv(droneCap, base3));
12692
12931
  for (const key of DAEMON_STATE_ENV) delete env[key];
@@ -12717,21 +12956,21 @@ var DAEMON_STATE_ENV = [
12717
12956
  ...PROVIDER_TOKEN_ENV
12718
12957
  ];
12719
12958
  function bundledRoleDir(kind) {
12720
- return join14(dirname4(fileURLToPath2(import.meta.url)), "..", "roles", kind);
12959
+ return join15(dirname4(fileURLToPath2(import.meta.url)), "..", "roles", kind);
12721
12960
  }
12722
12961
  function roleDir(worktree, kind) {
12723
- return join14(worktree, ".claude", "roles", kind);
12962
+ return join15(worktree, ".claude", "roles", kind);
12724
12963
  }
12725
12964
  function roleDirs(worktree, kind) {
12726
- return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync9(dir));
12965
+ return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync10(dir));
12727
12966
  }
12728
12967
  function nextRunLogPath(runLogsDir, runLogKeys) {
12729
12968
  const dir = runLogDirForWrite(runLogsDir, runLogKeys);
12730
12969
  if (!dir) throw new Error("\u043D\u0435\u0447\u0435\u043C \u0430\u0434\u0440\u0435\u0441\u043E\u0432\u0430\u0442\u044C \u043A\u0430\u0442\u0430\u043B\u043E\u0433 run-log'\u043E\u0432: \u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435\u0442 \u0441\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u043B\u044E\u0447\u0430 (SwarmStore.runLogKeys)");
12731
- mkdirSync9(dir, { recursive: true });
12970
+ mkdirSync10(dir, { recursive: true });
12732
12971
  let files = [];
12733
12972
  try {
12734
- files = readdirSync8(dir);
12973
+ files = readdirSync9(dir);
12735
12974
  } catch {
12736
12975
  }
12737
12976
  let next = 0;
@@ -12739,7 +12978,7 @@ function nextRunLogPath(runLogsDir, runLogKeys) {
12739
12978
  const m = /^(\d+)\.json/.exec(f);
12740
12979
  if (m) next = Math.max(next, Number(m[1]) + 1);
12741
12980
  }
12742
- return join14(dir, `${next}.json`);
12981
+ return join15(dir, `${next}.json`);
12743
12982
  }
12744
12983
  function openNextRunLog(runLogsDir, runLogKeys) {
12745
12984
  for (let attempt = 0; attempt < 50; attempt++) {
@@ -12831,9 +13070,9 @@ function makeLauncher(cfg) {
12831
13070
  }
12832
13071
  const realHome = cfg.droneIsolation?.realHome ?? homedir5();
12833
13072
  const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
12834
- if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync9(join14(cfg.droneIsolation.swarmHome, "token"))) {
13073
+ if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync10(join15(cfg.droneIsolation.swarmHome, "token"))) {
12835
13074
  console.error(
12836
- `[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${join14(cfg.droneIsolation.swarmHome, "token")} \u043D\u0435\u0442. read_task \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 slug-only. \u041F\u043E\u0447\u0438\u043D\u0438: \`swarm login\` (oauth.json) \u0438\u043B\u0438 \u043F\u043E\u043B\u043E\u0436\u0438 \u0442\u043E\u043A\u0435\u043D \u0432 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B.`
13075
+ `[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${join15(cfg.droneIsolation.swarmHome, "token")} \u043D\u0435\u0442. read_task \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 slug-only. \u041F\u043E\u0447\u0438\u043D\u0438: \`swarm login\` (oauth.json) \u0438\u043B\u0438 \u043F\u043E\u043B\u043E\u0436\u0438 \u0442\u043E\u043A\u0435\u043D \u0432 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B.`
12837
13076
  );
12838
13077
  }
12839
13078
  const executor = cfg.executor ?? executorForRun(run, cfg.swarmModels?.() ?? null);
@@ -13732,7 +13971,7 @@ async function runDaemonLogs(argv) {
13732
13971
  console.error(`\u043B\u043E\u0433 \u041F\u0420\u041E\u0413\u041E\u041D\u0410 \u2014 \u044D\u0442\u043E \xABswarm log ${stray}\xBB (\u0431\u0435\u0437 s). \xABswarm logs\xBB \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u043B\u043E\u0433 \u0434\u0435\u043C\u043E\u043D\u0430; \u0447\u0438\u0441\u043B\u043E \u0441\u0442\u0440\u043E\u043A \u2014 \xABswarm logs -n 200\xBB.`);
13733
13972
  return 1;
13734
13973
  }
13735
- if (!existsSync17(DAEMON_LOG_PATH)) {
13974
+ if (!existsSync18(DAEMON_LOG_PATH)) {
13736
13975
  console.error(`\u043B\u043E\u0433\u0430 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442: ${DAEMON_LOG_PATH}`);
13737
13976
  console.error("\u043E\u043D \u043F\u0438\u0448\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u0444\u043E\u043D\u043E\u0432\u043E\u043C \u0437\u0430\u043F\u0443\u0441\u043A\u0435 (swarm start). \u0414\u0435\u043C\u043E\u043D \u043F\u043E\u0434\u043D\u044F\u0442 \u0432 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435 (--foreground) \u2014 \u0432\u044B\u0432\u043E\u0434 \u0438\u0434\u0451\u0442 \u0442\u0443\u0434\u0430.");
13738
13977
  return 1;
@@ -13740,12 +13979,12 @@ async function runDaemonLogs(argv) {
13740
13979
  const head = tailFile(DAEMON_LOG_PATH, lines);
13741
13980
  if (head) console.log(head);
13742
13981
  if (!follow) return 0;
13743
- let pos = statSync11(DAEMON_LOG_PATH).size;
13982
+ let pos = statSync12(DAEMON_LOG_PATH).size;
13744
13983
  for (; ; ) {
13745
13984
  await sleep2(500);
13746
13985
  let size;
13747
13986
  try {
13748
- size = statSync11(DAEMON_LOG_PATH).size;
13987
+ size = statSync12(DAEMON_LOG_PATH).size;
13749
13988
  } catch {
13750
13989
  continue;
13751
13990
  }
@@ -14014,10 +14253,10 @@ function effectiveWorktreesDir() {
14014
14253
  legacyDir: WORKTREES_LEGACY_DIR,
14015
14254
  targetDir: WORKTREES_DIR,
14016
14255
  envOverride: process.env.SWARM_WORKTREES || "",
14017
- exists: (d) => existsSync17(d),
14256
+ exists: (d) => existsSync18(d),
14018
14257
  isNonEmptyDir: (d) => {
14019
14258
  try {
14020
- return readdirSync11(d).length > 0;
14259
+ return readdirSync12(d).length > 0;
14021
14260
  } catch {
14022
14261
  return false;
14023
14262
  }
@@ -14073,15 +14312,15 @@ async function scoutRepo(repo) {
14073
14312
  await $6({ reject: false })`git -C ${worktree} reset --hard ${baseRef}`;
14074
14313
  }
14075
14314
  await setWorktreeIdentity(worktree, name);
14076
- const abs = (rel) => join26(worktree, rel);
14315
+ const abs = (rel) => join27(worktree, rel);
14077
14316
  const code = await runRepoScout({
14078
14317
  repoFullName,
14079
14318
  repoPath: worktree,
14080
14319
  name,
14081
- readFile: (rel) => existsSync17(abs(rel)) ? readFileSync19(abs(rel), "utf8") : null,
14320
+ readFile: (rel) => existsSync18(abs(rel)) ? readFileSync19(abs(rel), "utf8") : null,
14082
14321
  writeFile: (rel, text) => {
14083
- mkdirSync10(dirname5(abs(rel)), { recursive: true });
14084
- writeFileSync14(abs(rel), text);
14322
+ mkdirSync11(dirname5(abs(rel)), { recursive: true });
14323
+ writeFileSync15(abs(rel), text);
14085
14324
  },
14086
14325
  runScout: (prompt) => new Promise((resolve5) => {
14087
14326
  const child = spawn3(bin, [...ISOLATION_FLAGS, "--dangerously-skip-permissions", "-p", prompt], {