@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/overmind.js CHANGED
@@ -592,10 +592,10 @@ function pick(rows) {
592
592
  return newest(rows.filter((r) => r.author)) ?? newest(rows);
593
593
  }
594
594
  function pickRunForPr(rows, ref, ctxRepo) {
595
- const num = prNumberOf(ref);
596
- if (num == null) return null;
595
+ const num2 = prNumberOf(ref);
596
+ if (num2 == null) return null;
597
597
  const target = repoKeyOf(ref) ?? repoKeyOf(ctxRepo);
598
- const hits = rows.filter((r) => r.prUrl && prNumberOf(r.prUrl) === num);
598
+ const hits = rows.filter((r) => r.prUrl && prNumberOf(r.prUrl) === num2);
599
599
  if (hits.length === 0) return null;
600
600
  if (target) {
601
601
  const mine = hits.filter((r) => keyOf(r) === target);
@@ -647,6 +647,9 @@ var init_returnReason = __esm({
647
647
  });
648
648
 
649
649
  // ../swarm-core/src/modelPolicy.ts
650
+ function isHarnessDefaultModel(model) {
651
+ return typeof model === "string" && model.trim().toLowerCase() === HARNESS_DEFAULT_MODEL;
652
+ }
650
653
  function findProvider(id) {
651
654
  const key = typeof id === "string" ? id.trim().toLowerCase() : "";
652
655
  return MODEL_PROVIDERS.find((p) => p.id === key) ?? null;
@@ -698,6 +701,17 @@ var init_modelPolicy = __esm({
698
701
  selectable: true,
699
702
  roles: { executor: "glm-5.3", review: "glm-5.3" }
700
703
  },
704
+ // Grok CLI. Каталог — живой `grok models` (16.10.2026), целиком и без выдуманных
705
+ // имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5
706
+ // (проверка — младшая, та же идея, что sonnet против opus). Тарифа нет: цену пишет CLI.
707
+ {
708
+ id: "xai",
709
+ label: "xAI (Grok)",
710
+ executorId: "grok",
711
+ selectable: true,
712
+ roles: { executor: "grok-4.7", review: "grok-4.5" },
713
+ models: ["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5"]
714
+ },
701
715
  // TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
702
716
  // Имён моделей здесь нет СОЗНАТЕЛЬНО: у владельца подписка, состав моделей в ней нам
703
717
  // неизвестен, а выдуманное имя хуже маркера «решает Codex».
@@ -1183,18 +1197,18 @@ var init_hosts = __esm({
1183
1197
  function parseHostResources(raw) {
1184
1198
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
1185
1199
  const v = raw;
1186
- const num = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
1187
- const memFreeGb = num(v.memFreeGb);
1188
- const memTotalGb = num(v.memTotalGb);
1200
+ const num2 = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
1201
+ const memFreeGb = num2(v.memFreeGb);
1202
+ const memTotalGb = num2(v.memTotalGb);
1189
1203
  if (memFreeGb == null || memTotalGb == null) return null;
1190
1204
  if (typeof v.measuredAt !== "string" || Number.isNaN(Date.parse(v.measuredAt))) return null;
1191
- const cpu = num(v.cpuBusyPct);
1205
+ const cpu = num2(v.cpuBusyPct);
1192
1206
  return {
1193
1207
  cpuBusyPct: cpu == null ? null : Math.min(100, Math.max(0, cpu)),
1194
1208
  memFreeGb,
1195
1209
  memTotalGb,
1196
- diskFreeGb: num(v.diskFreeGb),
1197
- diskTotalGb: num(v.diskTotalGb),
1210
+ diskFreeGb: num2(v.diskFreeGb),
1211
+ diskTotalGb: num2(v.diskTotalGb),
1198
1212
  measuredAt: v.measuredAt
1199
1213
  };
1200
1214
  }
@@ -1211,6 +1225,7 @@ var init_hostPool = __esm({
1211
1225
  "../swarm-core/src/hostPool.ts"() {
1212
1226
  "use strict";
1213
1227
  init_hosts();
1228
+ init_modelPolicy();
1214
1229
  HOST_REJECTION_TEXT = {
1215
1230
  offline: "\u043D\u0435 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438",
1216
1231
  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}`,
@@ -2330,10 +2345,10 @@ function launchCostUsd(result, model) {
2330
2345
  return costFromUsage(result.usage, model);
2331
2346
  }
2332
2347
  function launchSpend(result) {
2333
- const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2348
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2334
2349
  const sum = (u) => ({
2335
- tokensIn: num(u.input_tokens) + num(u.cache_creation_input_tokens) + num(u.cache_read_input_tokens),
2336
- tokensOut: num(u.output_tokens)
2350
+ tokensIn: num2(u.input_tokens) + num2(u.cache_creation_input_tokens) + num2(u.cache_read_input_tokens),
2351
+ tokensOut: num2(u.output_tokens)
2337
2352
  });
2338
2353
  const spend = sum(result.usage ?? {});
2339
2354
  if (spend.tokensIn === 0 && spend.tokensOut === 0 && result.modelUsage) {
@@ -2343,7 +2358,7 @@ function launchSpend(result) {
2343
2358
  spend.tokensOut += perModel.tokensOut;
2344
2359
  }
2345
2360
  }
2346
- return { ...spend, durationMs: num(result.duration_ms) };
2361
+ return { ...spend, durationMs: num2(result.duration_ms) };
2347
2362
  }
2348
2363
  function asTokenUsage(m) {
2349
2364
  return {
@@ -2435,9 +2450,9 @@ function transcriptSpendInWindow(worktree, fromMs, toMs, opts = {}) {
2435
2450
  unpriced += 1;
2436
2451
  unpricedModel ??= model;
2437
2452
  }
2438
- const num = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2439
- tokensIn += num(usage.input_tokens) + num(usage.cache_creation_input_tokens) + num(usage.cache_read_input_tokens);
2440
- tokensOut += num(usage.output_tokens);
2453
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
2454
+ tokensIn += num2(usage.input_tokens) + num2(usage.cache_creation_input_tokens) + num2(usage.cache_read_input_tokens);
2455
+ tokensOut += num2(usage.output_tokens);
2441
2456
  if (firstAt === null || at < firstAt) firstAt = at;
2442
2457
  if (lastAt === null || at > lastAt) lastAt = at;
2443
2458
  replies += 1;
@@ -2665,13 +2680,15 @@ var init_swarmHome = __esm({
2665
2680
  });
2666
2681
 
2667
2682
  // src/executors/codex.ts
2668
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync4, statSync as statSync3, symlinkSync } from "fs";
2683
+ import { copyFileSync as copyFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync4, statSync as statSync3, symlinkSync } from "fs";
2669
2684
  import { join as join6 } from "path";
2670
2685
  function resolveCodexBin(env = process.env) {
2671
2686
  return (env.SWARM_CODEX_BIN ?? "").trim() || "codex";
2672
2687
  }
2673
2688
  function codexHomePath(worktree, swarmHome = SWARM_HOME) {
2674
- return join6(swarmHome, "executor-state", "codex", worktree.replace(/[/.]/g, "-"));
2689
+ const folded = worktree.replace(/[/.]/g, "-");
2690
+ const key = /[:\\]/.test(folded) ? worktree.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "") || "worktree" : folded;
2691
+ return join6(swarmHome, "executor-state", "codex", key);
2675
2692
  }
2676
2693
  function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWARM_HOME) {
2677
2694
  const home = codexHomePath(worktree, swarmHome);
@@ -2684,6 +2701,10 @@ function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWAR
2684
2701
  try {
2685
2702
  symlinkSync(target, link2);
2686
2703
  } catch {
2704
+ try {
2705
+ copyFileSync2(target, link2);
2706
+ } catch {
2707
+ }
2687
2708
  }
2688
2709
  }
2689
2710
  return home;
@@ -2834,6 +2855,206 @@ ${notice}` : spec.prompt;
2834
2855
  }
2835
2856
  });
2836
2857
 
2858
+ // src/executors/grok.ts
2859
+ import { copyFileSync as copyFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync5, statSync as statSync4, symlinkSync as symlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2860
+ import { join as join7 } from "path";
2861
+ function resolveGrokBin(env = process.env) {
2862
+ return (env.SWARM_GROK_BIN ?? "").trim() || "grok";
2863
+ }
2864
+ function grokHomePath(worktree, swarmHome = SWARM_HOME) {
2865
+ const key = worktree.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "") || "worktree";
2866
+ return join7(swarmHome, "executor-state", "grok", key);
2867
+ }
2868
+ function grokSessionDir(worktree, swarmHome = SWARM_HOME) {
2869
+ return join7(grokHomePath(worktree, swarmHome), "sessions", encodeURIComponent(worktree));
2870
+ }
2871
+ function tomlBasic(value) {
2872
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
2873
+ return `"${escaped}"`;
2874
+ }
2875
+ function grokMcpToml(spec) {
2876
+ const env = Object.entries(spec.mcp.env).map(([k, v]) => `${k} = ${tomlBasic(v)}`).join(", ");
2877
+ const args = spec.mcp.args.map((a) => tomlBasic(a)).join(", ");
2878
+ return [
2879
+ "[mcp_servers." + spec.mcp.name + "]",
2880
+ "command = " + tomlBasic(spec.mcp.command),
2881
+ "args = [" + args + "]",
2882
+ "env = { " + env + " }",
2883
+ ""
2884
+ ].join("\n");
2885
+ }
2886
+ function ensureGrokHome(worktree, realHome, env = process.env, swarmHome = SWARM_HOME) {
2887
+ const home = grokHomePath(worktree, swarmHome);
2888
+ mkdirSync4(home, { recursive: true });
2889
+ const real = (env.GROK_HOME ?? "").trim() || join7(realHome, ".grok");
2890
+ const target = join7(real, "auth.json");
2891
+ const link2 = join7(home, "auth.json");
2892
+ if (existsSync5(target) && !existsSync5(link2)) {
2893
+ try {
2894
+ symlinkSync2(target, link2);
2895
+ } catch {
2896
+ try {
2897
+ copyFileSync3(target, link2);
2898
+ } catch {
2899
+ }
2900
+ }
2901
+ }
2902
+ return home;
2903
+ }
2904
+ function modelArgs(model) {
2905
+ if (!model || isHarnessDefaultModel(model)) return [];
2906
+ return ["--model", model];
2907
+ }
2908
+ function denyArgs(paths) {
2909
+ return paths.flatMap((p) => ["--deny", `Read(${p}) Edit(${p}) Write(${p})`]);
2910
+ }
2911
+ function newestSessionMtime(dir, depth = 3) {
2912
+ let entries;
2913
+ try {
2914
+ entries = readdirSync5(dir, { withFileTypes: true });
2915
+ } catch {
2916
+ return null;
2917
+ }
2918
+ let latest = null;
2919
+ for (const entry of entries) {
2920
+ const full = join7(dir, entry.name);
2921
+ try {
2922
+ if (entry.isDirectory()) {
2923
+ if (depth <= 0) continue;
2924
+ const nested = newestSessionMtime(full, depth - 1);
2925
+ if (nested != null && (latest == null || nested > latest)) latest = nested;
2926
+ } else if (entry.isFile()) {
2927
+ const mtime2 = statSync4(full).mtimeMs;
2928
+ if (latest == null || mtime2 > latest) latest = mtime2;
2929
+ }
2930
+ } catch {
2931
+ }
2932
+ }
2933
+ return latest;
2934
+ }
2935
+ function num(v) {
2936
+ return typeof v === "number" && Number.isFinite(v) ? v : 0;
2937
+ }
2938
+ function grokAbort(rec) {
2939
+ if (rec.type === "error") {
2940
+ const raw = typeof rec.message === "string" && rec.message ? rec.message : "error";
2941
+ const kind = /tool/i.test(raw) ? "tools" : /401|unauthorized|network|fetch|ECONN|api/i.test(raw) ? "link" : "other";
2942
+ return { kind, raw: raw.slice(0, 300) };
2943
+ }
2944
+ const stop = typeof rec.stopReason === "string" ? rec.stopReason : "";
2945
+ if (rec.is_error === true || stop === "error" || stop.startsWith("error_")) {
2946
+ const raw = stop || "is_error";
2947
+ return { kind: /tool/i.test(raw) ? "tools" : "other", raw };
2948
+ }
2949
+ return void 0;
2950
+ }
2951
+ function parseGrokResult(raw) {
2952
+ const trimmed = raw.trim();
2953
+ if (!trimmed) return null;
2954
+ let parsed;
2955
+ try {
2956
+ parsed = JSON.parse(trimmed);
2957
+ } catch {
2958
+ return null;
2959
+ }
2960
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
2961
+ const rec = parsed;
2962
+ const hasShape = typeof rec.text === "string" || typeof rec.stopReason === "string" || typeof rec.sessionId === "string" || rec.type === "error";
2963
+ if (!hasShape) return null;
2964
+ const reported = typeof rec.total_cost_usd === "number" && Number.isFinite(rec.total_cost_usd);
2965
+ const usage = rec.usage;
2966
+ let tokensIn;
2967
+ let tokensOut;
2968
+ if (typeof usage === "object" && usage !== null) {
2969
+ const u = usage;
2970
+ const hasAny = ["input_tokens", "output_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"].some(
2971
+ (k) => typeof u[k] === "number"
2972
+ );
2973
+ if (hasAny) {
2974
+ tokensIn = num(u.input_tokens) + num(u.cache_read_input_tokens) + num(u.cache_creation_input_tokens);
2975
+ tokensOut = num(u.output_tokens);
2976
+ }
2977
+ }
2978
+ const aborted = grokAbort(rec);
2979
+ const result = {
2980
+ costUsd: reported ? rec.total_cost_usd : 0
2981
+ };
2982
+ 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)";
2983
+ if (tokensIn != null && tokensOut != null) {
2984
+ result.tokensIn = tokensIn;
2985
+ result.tokensOut = tokensOut;
2986
+ }
2987
+ if (aborted) result.aborted = aborted;
2988
+ return result;
2989
+ }
2990
+ var grokExecutor;
2991
+ var init_grok = __esm({
2992
+ "src/executors/grok.ts"() {
2993
+ "use strict";
2994
+ init_src();
2995
+ init_swarmHome();
2996
+ init_swarmFiles();
2997
+ init_codex();
2998
+ grokExecutor = {
2999
+ id: "grok",
3000
+ title: "Grok CLI (grok)",
3001
+ binEnv: "SWARM_GROK_BIN",
3002
+ install: {
3003
+ 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.",
3004
+ pkg: "grok",
3005
+ command: "curl -fsSL https://x.ai/cli/install.sh | bash",
3006
+ windowsCommand: "irm https://x.ai/cli/install.ps1 | iex"
3007
+ },
3008
+ resolveBin: (env) => resolveGrokBin(env ?? process.env),
3009
+ plan: (spec) => {
3010
+ const home = ensureGrokHome(spec.worktree, spec.realHome);
3011
+ writeFileSync4(join7(home, "config.toml"), grokMcpToml(spec));
3012
+ writeFileSync4(
3013
+ join7(spec.worktree, SWARM_MCP_CONFIG_FILE),
3014
+ JSON.stringify(
3015
+ { mcpServers: { [spec.mcp.name]: { command: spec.mcp.command, args: spec.mcp.args, env: spec.mcp.env } } },
3016
+ null,
3017
+ 2
3018
+ )
3019
+ );
3020
+ const notice = skillsNotice(spec.skillDirs);
3021
+ const prompt = notice ? `${spec.prompt}
3022
+
3023
+ ${notice}` : spec.prompt;
3024
+ const args = [
3025
+ // `--cwd` стоит первым: по нему реконсайл узнаёт worktree живого процесса.
3026
+ // Флаг настоящий. Выдуманный `--mcp-config` grok отверг бы и дрон не стартовал.
3027
+ "--cwd",
3028
+ spec.worktree,
3029
+ "--no-auto-update",
3030
+ "--output-format",
3031
+ "json",
3032
+ "--yolo",
3033
+ ...spec.continueSession ? ["--continue"] : [],
3034
+ ...modelArgs(spec.model),
3035
+ ...spec.effort ? ["--effort", spec.effort] : [],
3036
+ ...denyArgs(spec.denyPaths),
3037
+ "-p",
3038
+ prompt
3039
+ ];
3040
+ return { bin: resolveGrokBin(), args, env: { GROK_HOME: home } };
3041
+ },
3042
+ parseResult(raw) {
3043
+ return parseGrokResult(raw);
3044
+ },
3045
+ session: {
3046
+ exists: (worktree) => newestSessionMtime(grokSessionDir(worktree)) !== null,
3047
+ mtimeMs: (worktree) => newestSessionMtime(grokSessionDir(worktree))
3048
+ },
3049
+ probe: {
3050
+ args: ["-p", "ok", "--output-format", "json", "--yolo", "--max-turns", "1", "--no-auto-update"],
3051
+ limitPattern: /usage limit/i,
3052
+ authPattern: /not logged in|unauthorized|\b401\b|authentication required|auth required/i
3053
+ }
3054
+ };
3055
+ }
3056
+ });
3057
+
2837
3058
  // src/swarmModels.ts
2838
3059
  function pick2(v) {
2839
3060
  const s = typeof v === "string" ? v.trim() : "";
@@ -2849,10 +3070,22 @@ function forKind(kind, swarm, norm) {
2849
3070
  if (!swarm) return null;
2850
3071
  return isReviewKind(kind) ? norm(swarm.reviewer) ?? norm(swarm.drone) : norm(swarm.drone);
2851
3072
  }
3073
+ function providersFromEnv(env = process.env) {
3074
+ const raw = (env.SWARM_PROVIDERS ?? "").trim();
3075
+ if (!raw) return null;
3076
+ const known = new Set(MODEL_PROVIDERS.map((p) => p.id));
3077
+ const list = [...new Set(raw.split(/[\s,]+/).map((p) => p.trim().toLowerCase()).filter((p) => known.has(p)))];
3078
+ return list.length ? list : null;
3079
+ }
2852
3080
  function resolveRunModelRef(run, swarm, env = process.env) {
2853
- const configured = parseModelRef(
2854
- pick2(run.model) ?? forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL)
2855
- );
3081
+ const pinned = parseModelRef(pick2(run.model));
3082
+ if (pinned) return pinned;
3083
+ const configured = parseModelRef(forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL));
3084
+ const own = providersFromEnv(env);
3085
+ if (own) {
3086
+ if (configured && own.includes(configured.providerId)) return configured;
3087
+ return { providerId: own[0], model: defaultModelFor(modelRoleOf(run.kind), own[0]) };
3088
+ }
2856
3089
  if (configured) return configured;
2857
3090
  const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
2858
3091
  return { providerId, model: defaultModelFor(modelRoleOf(run.kind), providerId) };
@@ -2899,14 +3132,17 @@ var init_executors = __esm({
2899
3132
  init_src();
2900
3133
  init_claudeCode();
2901
3134
  init_codex();
3135
+ init_grok();
2902
3136
  init_swarmModels();
2903
3137
  init_types2();
2904
3138
  init_claudeCode();
2905
3139
  init_codex();
3140
+ init_grok();
2906
3141
  DEFAULT_EXECUTOR_ID = "claude-code";
2907
3142
  EXECUTORS = {
2908
3143
  [claudeCodeExecutor.id]: claudeCodeExecutor,
2909
- [codexExecutor.id]: codexExecutor
3144
+ [codexExecutor.id]: codexExecutor,
3145
+ [grokExecutor.id]: grokExecutor
2910
3146
  };
2911
3147
  }
2912
3148
  });
@@ -3013,8 +3249,8 @@ __export(config_exports, {
3013
3249
  restEndpoint: () => restEndpoint,
3014
3250
  reviewCutoffAt: () => reviewCutoffAt
3015
3251
  });
3016
- import { basename, join as join7, resolve } from "path";
3017
- import { readFileSync as readFileSync4, realpathSync, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
3252
+ import { basename, join as join8, resolve } from "path";
3253
+ import { readFileSync as readFileSync4, realpathSync, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "fs";
3018
3254
  function normalizeRepo(repo) {
3019
3255
  const r = (repo ?? "").trim();
3020
3256
  if (!r) return "";
@@ -3028,10 +3264,10 @@ function effectiveRepoOf(repo, fallback) {
3028
3264
  return normalizeRepo(repo || fallback || "");
3029
3265
  }
3030
3266
  function droneWorktreePath(worktreesDir, repo, slug) {
3031
- return join7(droneWorktreeRoot(worktreesDir, repo), slug);
3267
+ return join8(droneWorktreeRoot(worktreesDir, repo), slug);
3032
3268
  }
3033
3269
  function droneWorktreeRoot(worktreesDir, repo) {
3034
- return join7(worktreesDir, basename(normalizeRepo(repo)));
3270
+ return join8(worktreesDir, basename(normalizeRepo(repo)));
3035
3271
  }
3036
3272
  function resolveEffectiveWorktreesDir(deps) {
3037
3273
  if (deps.envOverride) return deps.envOverride;
@@ -3079,7 +3315,7 @@ function mcpEndpoint(api, path = TASKLESS_MCP_PATH) {
3079
3315
  }
3080
3316
  function tokenFromFile() {
3081
3317
  try {
3082
- return readFileSync4(join7(SWARM_HOME, "token"), "utf8").trim();
3318
+ return readFileSync4(join8(SWARM_HOME, "token"), "utf8").trim();
3083
3319
  } catch {
3084
3320
  return "";
3085
3321
  }
@@ -3093,7 +3329,7 @@ function reviewCutoffAt() {
3093
3329
  const at = stamp();
3094
3330
  try {
3095
3331
  const tmp = `${REVIEW_CUTOFF_PATH}.tmp`;
3096
- writeFileSync4(tmp, JSON.stringify({ at }));
3332
+ writeFileSync5(tmp, JSON.stringify({ at }));
3097
3333
  renameSync2(tmp, REVIEW_CUTOFF_PATH);
3098
3334
  } catch {
3099
3335
  }
@@ -3107,25 +3343,25 @@ var init_config = __esm({
3107
3343
  init_executors();
3108
3344
  init_swarmHome();
3109
3345
  init_swarmHome();
3110
- DB_PATH = process.env.SWARM_DB || join7(SWARM_HOME, "swarm.db");
3346
+ DB_PATH = process.env.SWARM_DB || join8(SWARM_HOME, "swarm.db");
3111
3347
  EXECUTOR = resolveExecutor(process.env.SWARM_EXECUTOR);
3112
- DRONE_HOME = process.env.SWARM_DRONE_HOME || join7(SWARM_HOME, "drone-home");
3348
+ DRONE_HOME = process.env.SWARM_DRONE_HOME || join8(SWARM_HOME, "drone-home");
3113
3349
  DRONE_HOME_ISOLATION = process.env.SWARM_DRONE_ISOLATE_HOME !== "0" && process.platform !== "win32";
3114
3350
  DRONE_ISOLATION = DRONE_HOME_ISOLATION ? { home: DRONE_HOME, swarmHome: SWARM_HOME } : void 0;
3115
- WORKTREES_NOINDEX_DIR = join7(SWARM_HOME, "worktrees.noindex");
3116
- WORKTREES_LEGACY_DIR = join7(SWARM_HOME, "worktrees");
3351
+ WORKTREES_NOINDEX_DIR = join8(SWARM_HOME, "worktrees.noindex");
3352
+ WORKTREES_LEGACY_DIR = join8(SWARM_HOME, "worktrees");
3117
3353
  WORKTREES_DIR = process.env.SWARM_WORKTREES || WORKTREES_NOINDEX_DIR;
3118
- RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS || join7(SWARM_HOME, "run-logs");
3119
- DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG || join7(SWARM_HOME, "overmind.log");
3354
+ RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS || join8(SWARM_HOME, "run-logs");
3355
+ DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG || join8(SWARM_HOME, "overmind.log");
3120
3356
  DAEMON_LOG_MAX_BYTES = envNum("SWARM_DAEMON_LOG_MAX_BYTES", 16 * 1024 * 1024);
3121
3357
  DAEMON_LOG_KEEP = envNum("SWARM_DAEMON_LOG_KEEP", 2);
3122
3358
  DAEMON_LOG_CHECK_MS = envNum("SWARM_DAEMON_LOG_CHECK_MS", 6e4);
3123
3359
  DIST_BUILD = true;
3124
3360
  SWARM_BACKEND = process.env.SWARM_BACKEND === "client" ? "client" : process.env.SWARM_BACKEND === "sqlite" ? "sqlite" : DIST_BUILD ? "client" : "sqlite";
3125
- SWARM_OUTBOX = process.env.SWARM_OUTBOX || join7(SWARM_HOME, "outbox.jsonl");
3361
+ SWARM_OUTBOX = process.env.SWARM_OUTBOX || join8(SWARM_HOME, "outbox.jsonl");
3126
3362
  SWARM_MAX_IN_FLIGHT = Math.trunc(envNum("SWARM_MAX_IN_FLIGHT", 3));
3127
3363
  SWARM_CLAIM_INTERVAL_MS = envNum("SWARM_CLAIM_INTERVAL_MS", 6e4);
3128
- ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join7(SWARM_HOME, "env-cache");
3364
+ ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join8(SWARM_HOME, "env-cache");
3129
3365
  ENV_CACHE_ENABLED = process.env.SWARM_ENV_CACHE !== "0";
3130
3366
  ENV_CACHE_KEEP = Math.trunc(envNum("SWARM_ENV_CACHE_KEEP", 4));
3131
3367
  SWARM_MAX_LOAD_FACTOR = envNum("SWARM_MAX_LOAD_FACTOR", 0.9);
@@ -3155,15 +3391,15 @@ var init_config = __esm({
3155
3391
  DRONE_CHANNEL_ENABLED = process.env.SWARM_DRONE_CHANNEL !== "0";
3156
3392
  DRONE_CHANNEL_PORT = Number(process.env.SWARM_DRONE_CHANNEL_PORT) || 7421;
3157
3393
  DRONE_CHANNEL_URL = process.env.SWARM_DRONE_CHANNEL_URL || `http://127.0.0.1:${DRONE_CHANNEL_PORT}`;
3158
- DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET || join7(SWARM_HOME, "drone-channel.key");
3394
+ DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET || join8(SWARM_HOME, "drone-channel.key");
3159
3395
  DRONE_CHANNEL_GRACE_MS = envNum("SWARM_DRONE_CHANNEL_GRACE_MS", 12e4);
3160
- HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join7(SWARM_HOME, "overmind.heartbeat");
3396
+ HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join8(SWARM_HOME, "overmind.heartbeat");
3161
3397
  HEARTBEAT_STALE_MS = Number(process.env.SWARM_HEARTBEAT_STALE_MS) || 3e4;
3162
3398
  SWARM_REPO = process.env.SWARM_REPO || "";
3163
3399
  SWARM_SCAN_DEPTH = Number(process.env.SWARM_SCAN_DEPTH) || 3;
3164
3400
  SWARM_REPO_PATHS = (process.env.SWARM_REPOS || SWARM_REPO).split(",").map((s) => s.trim()).filter(Boolean);
3165
3401
  SWARM_SCAN_ROOTS_RAW = process.env.SWARM_SCAN_ROOTS || "";
3166
- REPO_MAP_PATH = process.env.SWARM_REPO_MAP || join7(SWARM_HOME, "repo-map.json");
3402
+ REPO_MAP_PATH = process.env.SWARM_REPO_MAP || join8(SWARM_HOME, "repo-map.json");
3167
3403
  SWARM_REPO_MAP_MS = envNum("SWARM_REPO_MAP_MS", 15 * 6e4);
3168
3404
  SWARM_REPO_SCAN_TTL_MS = envNum("SWARM_REPO_SCAN_TTL_MS", 24 * 60 * 6e4);
3169
3405
  TASKLESS_API = process.env.TASKLESS_API || "https://taskless.ru";
@@ -3178,7 +3414,7 @@ var init_config = __esm({
3178
3414
  SWARM_AUTO_REVIEW = process.env.SWARM_AUTO_REVIEW !== "0";
3179
3415
  SWARM_AUTO_VERIFY = process.env.SWARM_AUTO_VERIFY !== "0";
3180
3416
  SWARM_MERGE_QUEUE = process.env.SWARM_MERGE_QUEUE !== "0";
3181
- REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join7(SWARM_HOME, "review-cutoff.json");
3417
+ REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join8(SWARM_HOME, "review-cutoff.json");
3182
3418
  SWARM_REVIEW_CAP = Math.trunc(envNum("SWARM_REVIEW_CAP", 1));
3183
3419
  SWARM_STORM_URL = process.env.SWARM_STORM_URL || "";
3184
3420
  WEB_PORT = Number(process.env.SWARM_WEB_PORT) || 7420;
@@ -3702,8 +3938,8 @@ var init_store = __esm({
3702
3938
  });
3703
3939
 
3704
3940
  // src/agentToken.ts
3705
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, unlinkSync as unlinkSync2 } from "fs";
3706
- import { join as join8 } from "path";
3941
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, unlinkSync as unlinkSync2 } from "fs";
3942
+ import { join as join9 } from "path";
3707
3943
  function loadAgentToken() {
3708
3944
  try {
3709
3945
  const s = JSON.parse(readFileSync6(STORE_PATH, "utf8"));
@@ -3759,7 +3995,7 @@ var init_agentToken = __esm({
3759
3995
  "src/agentToken.ts"() {
3760
3996
  "use strict";
3761
3997
  init_config();
3762
- STORE_PATH = join8(SWARM_HOME, "agent.json");
3998
+ STORE_PATH = join9(SWARM_HOME, "agent.json");
3763
3999
  base = (api) => api.replace(/\/$/, "");
3764
4000
  TOKEN_PROBE_TIMEOUT_MS = 8e3;
3765
4001
  }
@@ -3873,10 +4109,10 @@ var init_toolFailure = __esm({
3873
4109
  });
3874
4110
 
3875
4111
  // src/heartbeat.ts
3876
- import { readFileSync as readFileSync8, renameSync as renameSync4, writeFileSync as writeFileSync7 } from "fs";
4112
+ import { readFileSync as readFileSync8, renameSync as renameSync4, writeFileSync as writeFileSync8 } from "fs";
3877
4113
  function writeHeartbeat(path, now) {
3878
4114
  const tmp = `${path}.tmp`;
3879
- writeFileSync7(tmp, String(now));
4115
+ writeFileSync8(tmp, String(now));
3880
4116
  renameSync4(tmp, path);
3881
4117
  }
3882
4118
  function readHeartbeat(path) {
@@ -4367,9 +4603,9 @@ var init_procCleanup = __esm({
4367
4603
  });
4368
4604
 
4369
4605
  // src/git.ts
4370
- import { existsSync as existsSync12, readdirSync as readdirSync12, realpathSync as realpathSync2, statSync as statSync10 } from "fs";
4606
+ import { existsSync as existsSync13, readdirSync as readdirSync13, realpathSync as realpathSync2, statSync as statSync11 } from "fs";
4371
4607
  import { appendFile, chmod, mkdir, readFile, rename, rm, writeFile } from "fs/promises";
4372
- import { isAbsolute as isAbsolute3, join as join21, resolve as resolve3, sep as sep2 } from "path";
4608
+ import { isAbsolute as isAbsolute3, join as join22, resolve as resolve3, sep as sep2 } from "path";
4373
4609
  import { $ as $2 } from "execa";
4374
4610
  function gitNet(repo, args, timeoutMs) {
4375
4611
  return withHardTimeout(
@@ -4391,14 +4627,14 @@ async function reclaimStaleWorktree(repo, worktree) {
4391
4627
  await detachWorktree(repo, worktree);
4392
4628
  }
4393
4629
  async function inspectWorktreePath(path) {
4394
- if (!existsSync12(path)) return "free";
4630
+ if (!existsSync13(path)) return "free";
4395
4631
  try {
4396
- if (readdirSync12(path).length === 0) return "free";
4632
+ if (readdirSync13(path).length === 0) return "free";
4397
4633
  } catch {
4398
4634
  return "orphan";
4399
4635
  }
4400
4636
  try {
4401
- if (statSync10(join21(path, ".git")).isDirectory()) return "main-checkout";
4637
+ if (statSync11(join22(path, ".git")).isDirectory()) return "main-checkout";
4402
4638
  } catch {
4403
4639
  }
4404
4640
  const top = await $2({ reject: false })`git -C ${path} rev-parse --show-toplevel`;
@@ -4437,7 +4673,7 @@ async function clearWorktreePath(repo, path, branch, opts = {}) {
4437
4673
  await detachWorktree(repo, path);
4438
4674
  opts.onStaleReclaim?.(path);
4439
4675
  }
4440
- if (!existsSync12(path)) return;
4676
+ if (!existsSync13(path)) return;
4441
4677
  await parkOrphanDir(repo, path, branch);
4442
4678
  }
4443
4679
  async function parkOrphanDir(repo, path, branch) {
@@ -4458,7 +4694,7 @@ async function parkOrphanDir(repo, path, branch) {
4458
4694
  }
4459
4695
  function isLinkedWorktree(dir) {
4460
4696
  try {
4461
- return statSync10(join21(dir, ".git")).isFile();
4697
+ return statSync11(join22(dir, ".git")).isFile();
4462
4698
  } catch {
4463
4699
  return false;
4464
4700
  }
@@ -4470,7 +4706,7 @@ async function detachWorktree(repo, worktree) {
4470
4706
  }
4471
4707
  }
4472
4708
  async function ensureWorktreeDirGone(worktree) {
4473
- if (!existsSync12(worktree)) return false;
4709
+ if (!existsSync13(worktree)) return false;
4474
4710
  if (!isLinkedWorktree(worktree)) {
4475
4711
  console.error(
4476
4712
  `[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.`
@@ -4485,7 +4721,7 @@ async function ensureWorktreeDirGone(worktree) {
4485
4721
  );
4486
4722
  return true;
4487
4723
  }
4488
- if (existsSync12(worktree)) {
4724
+ if (existsSync13(worktree)) {
4489
4725
  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.`);
4490
4726
  }
4491
4727
  return true;
@@ -4534,7 +4770,7 @@ async function checkoutWorktree(repo, path, branch, opts) {
4534
4770
  }
4535
4771
  const busyAt = await worktreeForBranch(repo, branch);
4536
4772
  if (busyAt) {
4537
- const exists = existsSync12(busyAt);
4773
+ const exists = existsSync13(busyAt);
4538
4774
  const live = exists && (opts.isWorktreeHeldLive ? await opts.isWorktreeHeldLive(busyAt) : true);
4539
4775
  if (live) throw new WorktreeBusyError(branch, busyAt);
4540
4776
  if (exists && await worktreeDirty(busyAt)) throw new WorktreeBusyError(branch, busyAt);
@@ -4570,8 +4806,8 @@ async function ensureGitExcluded(worktree, pattern) {
4570
4806
  try {
4571
4807
  const commonDir = await gitPath(worktree, "--git-common-dir");
4572
4808
  if (!commonDir) return false;
4573
- const infoDir = join21(commonDir, "info");
4574
- const excludeFile = join21(infoDir, "exclude");
4809
+ const infoDir = join22(commonDir, "info");
4810
+ const excludeFile = join22(infoDir, "exclude");
4575
4811
  let current = "";
4576
4812
  try {
4577
4813
  current = await readFile(excludeFile, "utf8");
@@ -4591,7 +4827,7 @@ async function gitPath(worktree, flag) {
4591
4827
  const out = await $2({ reject: false })`git -C ${worktree} rev-parse ${flag}`;
4592
4828
  const raw = out.stdout.trim();
4593
4829
  if (out.exitCode !== 0 || !raw) return null;
4594
- return isAbsolute3(raw) ? raw : join21(worktree, raw);
4830
+ return isAbsolute3(raw) ? raw : join22(worktree, raw);
4595
4831
  }
4596
4832
  async function ensureWorktreeHygiene(worktree) {
4597
4833
  for (const pattern of SWARM_EXCLUDE_PATTERNS) await ensureGitExcluded(worktree, pattern);
@@ -4601,12 +4837,12 @@ async function installGuardHooks(worktree) {
4601
4837
  try {
4602
4838
  const gitDir = await gitPath(worktree, "--git-dir");
4603
4839
  if (!gitDir) return false;
4604
- const hooksDir = join21(gitDir, "swarm-hooks");
4840
+ const hooksDir = join22(gitDir, "swarm-hooks");
4605
4841
  const prev = await previousHooksPath(worktree);
4606
4842
  await mkdir(hooksDir, { recursive: true });
4607
4843
  for (const hook of GUARD_HOOKS) {
4608
- await writeFile(join21(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
4609
- await chmod(join21(hooksDir, hook), 493);
4844
+ await writeFile(join22(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
4845
+ await chmod(join22(hooksDir, hook), 493);
4610
4846
  }
4611
4847
  await $2`git -C ${worktree} config extensions.worktreeConfig true`;
4612
4848
  await $2`git -C ${worktree} config --worktree core.hooksPath ${posix(hooksDir)}`;
@@ -4625,7 +4861,7 @@ async function previousHooksPath(worktree) {
4625
4861
  if (out.exitCode === 0 && value) return value;
4626
4862
  }
4627
4863
  const commonDir = await gitPath(worktree, "--git-common-dir");
4628
- return commonDir ? join21(commonDir, "hooks") : null;
4864
+ return commonDir ? join22(commonDir, "hooks") : null;
4629
4865
  }
4630
4866
  async function removeWorktree(repo, path) {
4631
4867
  const branch = (await $2({ reject: false })`git -C ${path} symbolic-ref --quiet --short HEAD`).stdout.trim();
@@ -4650,7 +4886,7 @@ async function worktreeDirty(path) {
4650
4886
  async function repairMovedWorktrees(targetDir) {
4651
4887
  const subdirs = (dir) => {
4652
4888
  try {
4653
- return readdirSync12(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join21(dir, d.name));
4889
+ return readdirSync13(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join22(dir, d.name));
4654
4890
  } catch {
4655
4891
  return [];
4656
4892
  }
@@ -4787,7 +5023,7 @@ __export(droneChannel_exports, {
4787
5023
  verifyLocalCliToken: () => verifyLocalCliToken
4788
5024
  });
4789
5025
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
4790
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync10, readFileSync as readFileSync19, writeFileSync as writeFileSync12 } from "fs";
5026
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync11, readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
4791
5027
  import { dirname as dirname5 } from "path";
4792
5028
  import { serve } from "@hono/node-server";
4793
5029
  function loadChannelSecret(path) {
@@ -4797,8 +5033,8 @@ function loadChannelSecret(path) {
4797
5033
  } catch {
4798
5034
  }
4799
5035
  const secret = randomBytes(32).toString("hex");
4800
- mkdirSync10(dirname5(path), { recursive: true });
4801
- writeFileSync12(path, secret, { mode: 384 });
5036
+ mkdirSync11(dirname5(path), { recursive: true });
5037
+ writeFileSync13(path, secret, { mode: 384 });
4802
5038
  try {
4803
5039
  chmodSync2(path, 384);
4804
5040
  } catch {
@@ -4916,13 +5152,13 @@ function makeChannelHandler(deps) {
4916
5152
  log(`[\u043A\u0430\u043D\u0430\u043B] \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${id} \u043D\u0435 \u0434\u043E\u0447\u0438\u0442\u0430\u043B\u0438\u0441\u044C \u0438\u0437 \u043F\u0440\u043E\u0434\u0430 (\u043E\u0442\u0432\u0435\u0447\u0430\u044E \u043F\u043E \u0437\u0435\u0440\u043A\u0430\u043B\u0443): ${e instanceof Error ? e.message : String(e)}`);
4917
5153
  }
4918
5154
  };
4919
- const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
5155
+ const num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
4920
5156
  const str2 = (v) => typeof v === "string" ? v : "";
4921
5157
  switch (method) {
4922
5158
  case "myRun":
4923
5159
  return Response.json({ run });
4924
5160
  case "getRun": {
4925
- const id = num(p.id);
5161
+ const id = num2(p.id);
4926
5162
  if (id == null || !inScope(id)) return bad(403, "\u043F\u0440\u043E\u0433\u043E\u043D \u0432\u043D\u0435 \u0441\u043A\u043E\u0443\u043F\u0430 \u043A\u0430\u043D\u0430\u043B\u0430");
4927
5163
  return Response.json({ run: deps.store.getRun(id) });
4928
5164
  }
@@ -4986,7 +5222,7 @@ function makeChannelHandler(deps) {
4986
5222
  }
4987
5223
  case "answer": {
4988
5224
  if (!heroOnly()) return bad(403, "\u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C \u0434\u0440\u043E\u043D\u0430\u043C \u043C\u043E\u0436\u0435\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0413\u0435\u0440\u043E\u0439");
4989
- const qid = num(p.id);
5225
+ const qid = num2(p.id);
4990
5226
  if (qid == null) return bad(400, "\u043D\u0443\u0436\u0435\u043D id \u0432\u043E\u043F\u0440\u043E\u0441\u0430");
4991
5227
  const q = deps.store.getQuestion(qid);
4992
5228
  const squad = new Set(deps.store.squadRuns(run.id).map((r) => r.id));
@@ -5188,7 +5424,7 @@ var init_droneChannel = __esm({
5188
5424
 
5189
5425
  // src/attachments.ts
5190
5426
  import { mkdir as mkdir3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
5191
- import { join as join26 } from "path";
5427
+ import { join as join27 } from "path";
5192
5428
  function attachmentsFromDoc(doc, origin) {
5193
5429
  const out = [];
5194
5430
  const walk2 = (node) => {
@@ -5256,9 +5492,9 @@ function slugifyCaption(caption) {
5256
5492
  return translit.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40).replace(/-+$/, "");
5257
5493
  }
5258
5494
  function attachmentFileName(index, caption, ext) {
5259
- const num = String(index).padStart(2, "0");
5495
+ const num2 = String(index).padStart(2, "0");
5260
5496
  const slug = slugifyCaption(caption) || "attachment";
5261
- return `${num}-${slug}.${ext}`;
5497
+ return `${num2}-${slug}.${ext}`;
5262
5498
  }
5263
5499
  function attachmentExt(ref, contentType) {
5264
5500
  const fromName = extOf(ref.caption) || extOf(pathOf(ref.url));
@@ -5279,7 +5515,7 @@ async function materializeAttachments(opts) {
5279
5515
  };
5280
5516
  const result = { files: [], skipped: [] };
5281
5517
  if (opts.refs.length === 0) return result;
5282
- const dir = join26(opts.worktree, ATTACHMENTS_DIR);
5518
+ const dir = join27(opts.worktree, ATTACHMENTS_DIR);
5283
5519
  await io.removeDir(dir);
5284
5520
  let dirReady = false;
5285
5521
  let total = 0;
@@ -5325,7 +5561,7 @@ async function materializeAttachments(opts) {
5325
5561
  await io.mkdir(dir);
5326
5562
  dirReady = true;
5327
5563
  }
5328
- await io.writeFile(join26(dir, name), file.bytes);
5564
+ await io.writeFile(join27(dir, name), file.bytes);
5329
5565
  } catch (e) {
5330
5566
  result.skipped.push(`${label2} \u2014 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0434\u0438\u0441\u043A (${errText2(e)})`);
5331
5567
  continue;
@@ -5478,7 +5714,7 @@ var init_attachments = __esm({
5478
5714
 
5479
5715
  // src/shotUpload.ts
5480
5716
  import { readFile as fsReadFile } from "fs/promises";
5481
- import { basename as basename4, isAbsolute as isAbsolute4, join as join27 } from "path";
5717
+ import { basename as basename4, isAbsolute as isAbsolute4, join as join28 } from "path";
5482
5718
  function shotMimetype(path) {
5483
5719
  const dot = path.lastIndexOf(".");
5484
5720
  if (dot < 0) return null;
@@ -5504,7 +5740,7 @@ async function readShots(paths, opts) {
5504
5740
  }
5505
5741
  let bytes;
5506
5742
  try {
5507
- bytes = await read(isAbsolute4(path) ? path : join27(opts.worktree, path));
5743
+ bytes = await read(isAbsolute4(path) ? path : join28(opts.worktree, path));
5508
5744
  } catch (e) {
5509
5745
  skipped.push({ path, reason: `\u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D: ${e instanceof Error ? e.message : String(e)}` });
5510
5746
  continue;
@@ -5585,8 +5821,8 @@ var init_shotUpload = __esm({
5585
5821
 
5586
5822
  // src/oauth.ts
5587
5823
  import { createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
5588
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync13, unlinkSync as unlinkSync4 } from "fs";
5589
- import { join as join28 } from "path";
5824
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync14, unlinkSync as unlinkSync4 } from "fs";
5825
+ import { join as join29 } from "path";
5590
5826
  function makePkce() {
5591
5827
  const verifier = randomBytes2(48).toString("base64url");
5592
5828
  const challenge = createHash3("sha256").update(verifier).digest("base64url");
@@ -5676,7 +5912,7 @@ function loadStore() {
5676
5912
  }
5677
5913
  }
5678
5914
  function saveStore(s) {
5679
- writeFileSync13(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
5915
+ writeFileSync14(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
5680
5916
  }
5681
5917
  function clearStore() {
5682
5918
  try {
@@ -5695,7 +5931,7 @@ async function ensureClientId(api, redirectUri, fetchFn = fetch) {
5695
5931
  const existing = loadClientId();
5696
5932
  if (existing) return existing;
5697
5933
  const clientId = await registerClient({ api, redirectUri }, fetchFn);
5698
- writeFileSync13(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
5934
+ writeFileSync14(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
5699
5935
  return clientId;
5700
5936
  }
5701
5937
  async function ensureAccess(store, opts = {}) {
@@ -5721,8 +5957,8 @@ var init_oauth = __esm({
5721
5957
  AUTHORIZE = "/api/oauth/authorize";
5722
5958
  TOKEN = "/api/oauth/token";
5723
5959
  SCOPE = "openid profile email taskless:full";
5724
- STORE_PATH2 = join28(SWARM_HOME, "oauth.json");
5725
- CLIENT_PATH = join28(SWARM_HOME, "oauth-client.json");
5960
+ STORE_PATH2 = join29(SWARM_HOME, "oauth.json");
5961
+ CLIENT_PATH = join29(SWARM_HOME, "oauth-client.json");
5726
5962
  base2 = (api) => api.replace(/\/$/, "");
5727
5963
  }
5728
5964
  });
@@ -5768,8 +6004,8 @@ var init_authState = __esm({
5768
6004
  });
5769
6005
 
5770
6006
  // src/workspace.ts
5771
- import { readFileSync as readFileSync21, writeFileSync as writeFileSync14, unlinkSync as unlinkSync5 } from "fs";
5772
- import { join as join29 } from "path";
6007
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync15, unlinkSync as unlinkSync5 } from "fs";
6008
+ import { join as join30 } from "path";
5773
6009
  function loadWorkspace() {
5774
6010
  try {
5775
6011
  const w = JSON.parse(readFileSync21(WORKSPACE_PATH, "utf8"));
@@ -5786,7 +6022,7 @@ var init_workspace = __esm({
5786
6022
  "src/workspace.ts"() {
5787
6023
  "use strict";
5788
6024
  init_config();
5789
- WORKSPACE_PATH = join29(SWARM_HOME, "workspace.json");
6025
+ WORKSPACE_PATH = join30(SWARM_HOME, "workspace.json");
5790
6026
  }
5791
6027
  });
5792
6028
 
@@ -6389,7 +6625,7 @@ __export(db_exports, {
6389
6625
  });
6390
6626
  import { createRequire as createRequire2 } from "module";
6391
6627
  import { randomUUID as randomUUID3 } from "crypto";
6392
- import { existsSync as existsSync16 } from "fs";
6628
+ import { existsSync as existsSync17 } from "fs";
6393
6629
  import { resolve as resolve4 } from "path";
6394
6630
  function rowToQuestion(row) {
6395
6631
  return {
@@ -7053,7 +7289,7 @@ CREATE TABLE IF NOT EXISTS imports (
7053
7289
  const repoN = normalizeRepo(repo);
7054
7290
  const already = this.db.prepare("SELECT run_count FROM imports WHERE source = ?").get(abs);
7055
7291
  if (already) return { imported: false, runs: already.run_count };
7056
- if (!existsSync16(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
7292
+ if (!existsSync17(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
7057
7293
  const src = new this.databaseCtor(abs);
7058
7294
  try {
7059
7295
  const runs = src.prepare("SELECT * FROM runs ORDER BY id").all();
@@ -7423,8 +7659,8 @@ var init_droneStore = __esm({
7423
7659
 
7424
7660
  // src/overmind.ts
7425
7661
  import { homedir as homedir7 } from "os";
7426
- import { existsSync as existsSync21, readFileSync as readFileSync25, readdirSync as readdirSync14, renameSync as renameSync12, statSync as statSync13 } from "fs";
7427
- import { basename as basename6, dirname as dirname6, join as join36 } from "path";
7662
+ import { existsSync as existsSync22, readFileSync as readFileSync25, readdirSync as readdirSync15, renameSync as renameSync12, statSync as statSync14 } from "fs";
7663
+ import { basename as basename6, dirname as dirname6, join as join37 } from "path";
7428
7664
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
7429
7665
  import { spawn as spawn4 } from "child_process";
7430
7666
 
@@ -7504,7 +7740,7 @@ init_config();
7504
7740
  init_resources();
7505
7741
  init_names2();
7506
7742
  init_storm();
7507
- import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
7743
+ import { appendFileSync, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
7508
7744
  import { dirname as dirname2 } from "path";
7509
7745
  import { randomUUID } from "crypto";
7510
7746
 
@@ -7562,12 +7798,12 @@ init_store();
7562
7798
  init_toolFailure();
7563
7799
 
7564
7800
  // src/logTail.ts
7565
- import { closeSync, existsSync as existsSync6, openSync, readSync, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
7566
- import { join as join10 } from "path";
7801
+ import { closeSync, existsSync as existsSync7, openSync, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
7802
+ import { join as join11 } from "path";
7567
7803
 
7568
7804
  // src/runLogDir.ts
7569
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync5, renameSync as renameSync3, statSync as statSync4 } from "fs";
7570
- import { join as join9 } from "path";
7805
+ import { existsSync as existsSync6, mkdirSync as mkdirSync6, readdirSync as readdirSync6, renameSync as renameSync3, statSync as statSync5 } from "fs";
7806
+ import { join as join10 } from "path";
7571
7807
  var LEGACY_RUN_LOGS_SUBDIR = "legacy";
7572
7808
  function runLogDirName(runLogKey) {
7573
7809
  const safe2 = runLogKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^[-_]+|[-_]+$/g, "");
@@ -7575,12 +7811,12 @@ function runLogDirName(runLogKey) {
7575
7811
  return /^\d+$/.test(safe2) ? `k-${safe2}` : safe2;
7576
7812
  }
7577
7813
  function runLogDirPath(runLogsDir, runLogKey) {
7578
- return join9(runLogsDir, runLogDirName(runLogKey));
7814
+ return join10(runLogsDir, runLogDirName(runLogKey));
7579
7815
  }
7580
7816
  function findRunLogDir(runLogsDir, keys) {
7581
7817
  for (const key of keys) {
7582
7818
  const dir = runLogDirPath(runLogsDir, key);
7583
- if (existsSync5(dir)) return dir;
7819
+ if (existsSync6(dir)) return dir;
7584
7820
  }
7585
7821
  return null;
7586
7822
  }
@@ -7590,10 +7826,10 @@ function runLogDirForWrite(runLogsDir, keys) {
7590
7826
  return findRunLogDir(runLogsDir, keys) ?? runLogDirPath(runLogsDir, canonical);
7591
7827
  }
7592
7828
  function sweepLegacyRunLogs(runLogsDir) {
7593
- const legacyDir = join9(runLogsDir, LEGACY_RUN_LOGS_SUBDIR);
7829
+ const legacyDir = join10(runLogsDir, LEGACY_RUN_LOGS_SUBDIR);
7594
7830
  let entries;
7595
7831
  try {
7596
- entries = readdirSync5(runLogsDir);
7832
+ entries = readdirSync6(runLogsDir);
7597
7833
  } catch {
7598
7834
  return { moved: 0, failed: 0, legacyDir };
7599
7835
  }
@@ -7601,12 +7837,12 @@ function sweepLegacyRunLogs(runLogsDir) {
7601
7837
  let failed = 0;
7602
7838
  for (const name of entries) {
7603
7839
  if (!/^\d+$/.test(name)) continue;
7604
- const from = join9(runLogsDir, name);
7840
+ const from = join10(runLogsDir, name);
7605
7841
  try {
7606
- if (!statSync4(from).isDirectory()) continue;
7607
- mkdirSync5(legacyDir, { recursive: true });
7608
- let to = join9(legacyDir, name);
7609
- for (let n = 2; existsSync5(to) && n < 100; n++) to = join9(legacyDir, `${name}-${n}`);
7842
+ if (!statSync5(from).isDirectory()) continue;
7843
+ mkdirSync6(legacyDir, { recursive: true });
7844
+ let to = join10(legacyDir, name);
7845
+ for (let n = 2; existsSync6(to) && n < 100; n++) to = join10(legacyDir, `${name}-${n}`);
7610
7846
  renameSync3(from, to);
7611
7847
  moved++;
7612
7848
  } catch {
@@ -7626,7 +7862,7 @@ function fileHeader(name, size, shown) {
7626
7862
  function readTail(path, opts) {
7627
7863
  let size;
7628
7864
  try {
7629
- size = statSync5(path).size;
7865
+ size = statSync6(path).size;
7630
7866
  } catch {
7631
7867
  return null;
7632
7868
  }
@@ -7661,7 +7897,7 @@ function latestRunLaunch(runLogsDir, runLogKeys) {
7661
7897
  if (!dir) return null;
7662
7898
  let files;
7663
7899
  try {
7664
- files = readdirSync6(dir);
7900
+ files = readdirSync7(dir);
7665
7901
  } catch {
7666
7902
  return null;
7667
7903
  }
@@ -7672,8 +7908,8 @@ function latestRunLaunch(runLogsDir, runLogKeys) {
7672
7908
  }
7673
7909
  if (indexes.size === 0) return null;
7674
7910
  const index = Math.max(...indexes);
7675
- const out = [join10(dir, `${index}.json`), join10(dir, `${index}.json.done`)].find((p) => existsSync6(p)) ?? null;
7676
- const err = existsSync6(join10(dir, `${index}.err.log`)) ? join10(dir, `${index}.err.log`) : null;
7911
+ const out = [join11(dir, `${index}.json`), join11(dir, `${index}.json.done`)].find((p) => existsSync7(p)) ?? null;
7912
+ const err = existsSync7(join11(dir, `${index}.err.log`)) ? join11(dir, `${index}.err.log`) : null;
7677
7913
  return { index, total: indexes.size, out, err };
7678
7914
  }
7679
7915
  function runLogTail(runLogsDir, runLogKeys, opts) {
@@ -7943,25 +8179,25 @@ var SwarmClient = class _SwarmClient {
7943
8179
  this.repoFullNames = registry.map((e) => e.repoFullName);
7944
8180
  this.repoPathByFullName = new Map(registry.map((e) => [e.repoFullName, normalizeRepo(e.path)]));
7945
8181
  this.host = opts.host ?? null;
7946
- this.repoExists = opts.repoExists ?? existsSync7;
8182
+ this.repoExists = opts.repoExists ?? existsSync8;
7947
8183
  this.questionSyncMs = opts.questionSyncMs ?? 3e3;
7948
8184
  this.executorId = opts.executorId ?? EXECUTOR.id;
7949
8185
  this.outboxPath = opts.outboxPath;
7950
8186
  this.mapPath = `${opts.outboxPath}.prodmap.json`;
7951
8187
  this.stormPath = `${opts.outboxPath}.storm.jsonl`;
7952
- mkdirSync6(dirname2(this.outboxPath), { recursive: true });
8188
+ mkdirSync7(dirname2(this.outboxPath), { recursive: true });
7953
8189
  this.recover();
7954
8190
  }
7955
8191
  // ── рестарт: поднять outbox (незаписанные операции) и карту runKey→prodId ─────
7956
8192
  recover() {
7957
- if (existsSync7(this.mapPath)) {
8193
+ if (existsSync8(this.mapPath)) {
7958
8194
  try {
7959
8195
  const obj = JSON.parse(readFileSync7(this.mapPath, "utf8"));
7960
8196
  for (const [k, v] of Object.entries(obj)) this.prodIdByKey.set(k, v);
7961
8197
  } catch {
7962
8198
  }
7963
8199
  }
7964
- if (existsSync7(this.outboxPath)) {
8200
+ if (existsSync8(this.outboxPath)) {
7965
8201
  const txt = readFileSync7(this.outboxPath, "utf8");
7966
8202
  for (const line of txt.split("\n")) {
7967
8203
  const s = line.trim();
@@ -7982,7 +8218,7 @@ var SwarmClient = class _SwarmClient {
7982
8218
  // eventsLog ПЕРВЫМИ (до hydrate), в порядке файла (= хронологический); id
7983
8219
  // переприсваиваем локально (id событий — только порядок, не корреляция с продом).
7984
8220
  replayGlobalEvents() {
7985
- if (!existsSync7(this.stormPath)) return;
8221
+ if (!existsSync8(this.stormPath)) return;
7986
8222
  let txt;
7987
8223
  try {
7988
8224
  txt = readFileSync7(this.stormPath, "utf8");
@@ -8982,7 +9218,7 @@ var SwarmClient = class _SwarmClient {
8982
9218
  * транзиентным сбоем ОС, который этот PR должен переживать, а не превращать в мор.
8983
9219
  */
8984
9220
  reconcileRuntime(opts) {
8985
- const wtExists = opts.worktreeExists ?? existsSync7;
9221
+ const wtExists = opts.worktreeExists ?? existsSync8;
8986
9222
  const pidByWorktree = /* @__PURE__ */ new Map();
8987
9223
  try {
8988
9224
  for (const p of opts.listProcesses()) if (p.worktree) pidByWorktree.set(p.worktree, p.pid);
@@ -9602,7 +9838,7 @@ var SwarmClient = class _SwarmClient {
9602
9838
  *
9603
9839
  * Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
9604
9840
  */
9605
- async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors) {
9841
+ async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors, providers) {
9606
9842
  try {
9607
9843
  const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
9608
9844
  if (daemonVersion) args.daemonVersion = daemonVersion;
@@ -9633,6 +9869,7 @@ var SwarmClient = class _SwarmClient {
9633
9869
  args.tick = { seq: Math.max(0, Math.trunc(tick.seq)), ageMs: Math.max(0, Math.trunc(tick.ageMs)), intervalMs: Math.trunc(tick.intervalMs) };
9634
9870
  }
9635
9871
  if (executors) args.executorsAvailable = executors;
9872
+ if (providers) args.providersAvailable = providers;
9636
9873
  const res = await this.call("swarm_heartbeat", args);
9637
9874
  if (res?.isError) return EMPTY_HEARTBEAT_REPLY;
9638
9875
  this.heartbeatOkAt = this.now();
@@ -10220,13 +10457,13 @@ var SwarmClient = class _SwarmClient {
10220
10457
  }
10221
10458
  persistPending() {
10222
10459
  try {
10223
- writeFileSync6(this.outboxPath, this.pending.map((e) => JSON.stringify(e)).join("\n") + (this.pending.length ? "\n" : ""));
10460
+ writeFileSync7(this.outboxPath, this.pending.map((e) => JSON.stringify(e)).join("\n") + (this.pending.length ? "\n" : ""));
10224
10461
  } catch {
10225
10462
  }
10226
10463
  }
10227
10464
  persistMap() {
10228
10465
  try {
10229
- writeFileSync6(this.mapPath, JSON.stringify(Object.fromEntries(this.prodIdByKey)));
10466
+ writeFileSync7(this.mapPath, JSON.stringify(Object.fromEntries(this.prodIdByKey)));
10230
10467
  } catch {
10231
10468
  }
10232
10469
  }
@@ -10297,20 +10534,20 @@ function tickPulse(health, now, intervalMs) {
10297
10534
  }
10298
10535
 
10299
10536
  // src/logRotate.ts
10300
- import { copyFileSync as copyFileSync2, renameSync as renameSync5, statSync as statSync6, fstatSync, truncateSync, unlinkSync as unlinkSync3, existsSync as existsSync8 } from "fs";
10537
+ import { copyFileSync as copyFileSync4, renameSync as renameSync5, statSync as statSync7, fstatSync, truncateSync, unlinkSync as unlinkSync3, existsSync as existsSync9 } from "fs";
10301
10538
  var realIo = {
10302
10539
  size: (path) => {
10303
10540
  try {
10304
- return statSync6(path).size;
10541
+ return statSync7(path).size;
10305
10542
  } catch {
10306
10543
  return null;
10307
10544
  }
10308
10545
  },
10309
- copy: copyFileSync2,
10546
+ copy: copyFileSync4,
10310
10547
  rename: renameSync5,
10311
10548
  truncate: (path) => truncateSync(path, 0),
10312
10549
  unlink: unlinkSync3,
10313
- exists: existsSync8
10550
+ exists: existsSync9
10314
10551
  };
10315
10552
  var realOwnerIo = {
10316
10553
  statFd: (fd) => {
@@ -10323,7 +10560,7 @@ var realOwnerIo = {
10323
10560
  },
10324
10561
  statPath: (path) => {
10325
10562
  try {
10326
- const s = statSync6(path);
10563
+ const s = statSync7(path);
10327
10564
  return { dev: s.dev, ino: s.ino };
10328
10565
  } catch {
10329
10566
  return null;
@@ -10457,13 +10694,13 @@ async function buildRepoRegistry(paths) {
10457
10694
  }
10458
10695
 
10459
10696
  // src/repoMemory.ts
10460
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
10461
- import { join as join11 } from "path";
10697
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "fs";
10698
+ import { join as join12 } from "path";
10462
10699
  var FILE = "repos.json";
10463
10700
  function loadRememberedRepos(home) {
10464
10701
  let raw;
10465
10702
  try {
10466
- raw = readFileSync10(join11(home, FILE), "utf8");
10703
+ raw = readFileSync10(join12(home, FILE), "utf8");
10467
10704
  } catch {
10468
10705
  return [];
10469
10706
  }
@@ -10478,7 +10715,7 @@ function loadRememberedRepos(home) {
10478
10715
  function rememberRepos(home, paths, now = () => /* @__PURE__ */ new Date()) {
10479
10716
  const clean = paths.filter((p) => p.trim().length > 0);
10480
10717
  if (clean.length === 0) return;
10481
- writeFileSync8(join11(home, FILE), `${JSON.stringify({ paths: clean, at: now().toISOString() }, null, 2)}
10718
+ writeFileSync9(join12(home, FILE), `${JSON.stringify({ paths: clean, at: now().toISOString() }, null, 2)}
10482
10719
  `);
10483
10720
  }
10484
10721
  function pickRepoPaths(args) {
@@ -10493,12 +10730,12 @@ init_src();
10493
10730
  init_config();
10494
10731
  init_linkFailure();
10495
10732
  import { homedir as homedir5 } from "os";
10496
- import { readFileSync as readFileSync12, renameSync as renameSync6, writeFileSync as writeFileSync9 } from "fs";
10497
- import { isAbsolute, join as join13, resolve as resolve2 } from "path";
10733
+ import { readFileSync as readFileSync12, renameSync as renameSync6, writeFileSync as writeFileSync10 } from "fs";
10734
+ import { isAbsolute, join as join14, resolve as resolve2 } from "path";
10498
10735
 
10499
10736
  // src/repoScan.ts
10500
- import { readdirSync as readdirSync7, readFileSync as readFileSync11, statSync as statSync7 } from "fs";
10501
- import { join as join12 } from "path";
10737
+ import { readdirSync as readdirSync8, readFileSync as readFileSync11, statSync as statSync8 } from "fs";
10738
+ import { join as join13 } from "path";
10502
10739
  var SKIP = /* @__PURE__ */ new Set([
10503
10740
  "node_modules",
10504
10741
  "vendor",
@@ -10528,7 +10765,7 @@ function parseOriginFromGitConfig(text) {
10528
10765
  }
10529
10766
  function originOf(dir) {
10530
10767
  try {
10531
- return parseOriginFromGitConfig(readFileSync11(join12(dir, ".git", "config"), "utf8"));
10768
+ return parseOriginFromGitConfig(readFileSync11(join13(dir, ".git", "config"), "utf8"));
10532
10769
  } catch {
10533
10770
  return null;
10534
10771
  }
@@ -10557,15 +10794,15 @@ function walk(dir, depth, maxDepth, found) {
10557
10794
  }
10558
10795
  let entries;
10559
10796
  try {
10560
- entries = readdirSync7(dir);
10797
+ entries = readdirSync8(dir);
10561
10798
  } catch {
10562
10799
  return;
10563
10800
  }
10564
10801
  for (const name of entries) {
10565
10802
  if (name.startsWith(".") || SKIP.has(name)) continue;
10566
- const child = join12(dir, name);
10803
+ const child = join13(dir, name);
10567
10804
  try {
10568
- if (!statSync7(child).isDirectory()) continue;
10805
+ if (!statSync8(child).isDirectory()) continue;
10569
10806
  } catch {
10570
10807
  continue;
10571
10808
  }
@@ -10585,7 +10822,7 @@ function defaultScanRoots(home, knownPaths = []) {
10585
10822
  if (parent && parent !== p) add(parent);
10586
10823
  }
10587
10824
  for (const name of ["projects", "code", "src", "dev", "work", "repos", "git", "Projects", "Code", "Developer", "Documents/GitHub", "go/src"]) {
10588
- add(join12(home, name));
10825
+ add(join13(home, name));
10589
10826
  }
10590
10827
  return out;
10591
10828
  }
@@ -10599,7 +10836,7 @@ function resolveScanRoots(raw, home = homedir5(), knownPaths = []) {
10599
10836
  for (const part of v.split(",")) {
10600
10837
  const p = part.trim();
10601
10838
  if (!p) continue;
10602
- const abs = p === "~" ? home : p.startsWith("~/") ? join13(home, p.slice(2)) : isAbsolute(p) ? p : resolve2(home, p);
10839
+ const abs = p === "~" ? home : p.startsWith("~/") ? join14(home, p.slice(2)) : isAbsolute(p) ? p : resolve2(home, p);
10603
10840
  if (!out.includes(abs)) out.push(abs);
10604
10841
  }
10605
10842
  return out;
@@ -10828,7 +11065,7 @@ function loadRepoMapCache(path) {
10828
11065
  function writeRepoMapCache(path, entries, scannedAt = stamp()) {
10829
11066
  try {
10830
11067
  const tmp = `${path}.tmp`;
10831
- writeFileSync9(tmp, JSON.stringify({ at: stamp(), scannedAt, entries }, null, 2));
11068
+ writeFileSync10(tmp, JSON.stringify({ at: stamp(), scannedAt, entries }, null, 2));
10832
11069
  renameSync6(tmp, path);
10833
11070
  } catch {
10834
11071
  }
@@ -10836,17 +11073,17 @@ function writeRepoMapCache(path, entries, scannedAt = stamp()) {
10836
11073
 
10837
11074
  // src/hostIdentity.ts
10838
11075
  import { hostname } from "os";
10839
- import { existsSync as existsSync9, readFileSync as readFileSync13, renameSync as renameSync7, writeFileSync as writeFileSync10 } from "fs";
10840
- import { join as join14 } from "path";
11076
+ import { existsSync as existsSync10, readFileSync as readFileSync13, renameSync as renameSync7, writeFileSync as writeFileSync11 } from "fs";
11077
+ import { join as join15 } from "path";
10841
11078
  import { randomUUID as randomUUID2 } from "crypto";
10842
11079
  function hostIdentityPath(home) {
10843
- return join14(home, "host.json");
11080
+ return join15(home, "host.json");
10844
11081
  }
10845
11082
  function loadHostIdentity(deps) {
10846
- const exists = deps.exists ?? existsSync9;
11083
+ const exists = deps.exists ?? existsSync10;
10847
11084
  const readFile2 = deps.readFile ?? ((p) => readFileSync13(p, "utf8"));
10848
11085
  const writeFile4 = deps.writeFile ?? ((p, data) => {
10849
- writeFileSync10(`${p}.tmp`, data);
11086
+ writeFileSync11(`${p}.tmp`, data);
10850
11087
  renameSync7(`${p}.tmp`, p);
10851
11088
  });
10852
11089
  const host = deps.hostname ?? hostname;
@@ -10939,8 +11176,8 @@ function pickResumeCandidate(pausedForRelief) {
10939
11176
  }
10940
11177
 
10941
11178
  // src/reliefLoop.ts
10942
- import { existsSync as existsSync10, readFileSync as readFileSync14, renameSync as renameSync8, writeFileSync as writeFileSync11 } from "fs";
10943
- import { join as join15 } from "path";
11179
+ import { existsSync as existsSync11, readFileSync as readFileSync14, renameSync as renameSync8, writeFileSync as writeFileSync12 } from "fs";
11180
+ import { join as join16 } from "path";
10944
11181
  var numEnv3 = (name, def) => {
10945
11182
  const v = Number(process.env[name]);
10946
11183
  return Number.isFinite(v) && v > 0 ? v : def;
@@ -10975,10 +11212,10 @@ function reliefLoopTripped(entry, cfg = DEFAULT_RELIEF_LOOP_CONFIG) {
10975
11212
  return !!entry && entry.pauses >= cfg.limit;
10976
11213
  }
10977
11214
  function reliefLoopStatePath(home) {
10978
- return join15(home, "relief-loop.json");
11215
+ return join16(home, "relief-loop.json");
10979
11216
  }
10980
11217
  function loadReliefLoopState(home, now = Date.now(), io = {}, cfg = DEFAULT_RELIEF_LOOP_CONFIG) {
10981
- const exists = io.exists ?? existsSync10;
11218
+ const exists = io.exists ?? existsSync11;
10982
11219
  const readFile2 = io.readFile ?? ((p) => readFileSync14(p, "utf8"));
10983
11220
  const path = reliefLoopStatePath(home);
10984
11221
  if (!exists(path)) return {};
@@ -11001,7 +11238,7 @@ function loadReliefLoopState(home, now = Date.now(), io = {}, cfg = DEFAULT_RELI
11001
11238
  function saveReliefLoopState(home, state, io = {}) {
11002
11239
  const path = reliefLoopStatePath(home);
11003
11240
  const writeFile4 = io.writeFile ?? ((p, data) => {
11004
- writeFileSync11(`${p}.tmp`, data);
11241
+ writeFileSync12(`${p}.tmp`, data);
11005
11242
  renameSync8(`${p}.tmp`, p);
11006
11243
  });
11007
11244
  writeFile4(path, JSON.stringify(state, null, 2));
@@ -11163,15 +11400,15 @@ function buildReliefCostCandidates(cost, t, sustainMs, build) {
11163
11400
 
11164
11401
  // src/launcher.ts
11165
11402
  import { spawn as spawn2 } from "child_process";
11166
- import { closeSync as closeSync3, existsSync as existsSync11, mkdirSync as mkdirSync8, openSync as openSync3, readdirSync as readdirSync9 } from "fs";
11403
+ import { closeSync as closeSync3, existsSync as existsSync12, mkdirSync as mkdirSync9, openSync as openSync3, readdirSync as readdirSync10 } from "fs";
11167
11404
  import { createRequire } from "module";
11168
11405
  import { homedir as homedir6 } from "os";
11169
- import { dirname as dirname3, join as join17 } from "path";
11406
+ import { dirname as dirname3, join as join18 } from "path";
11170
11407
  import { fileURLToPath as fileURLToPath2 } from "url";
11171
11408
 
11172
11409
  // src/droneHome.ts
11173
- import { lstatSync, mkdirSync as mkdirSync7, readdirSync as readdirSync8, rmSync, statSync as statSync8, symlinkSync as symlinkSync2 } from "fs";
11174
- import { join as join16, relative, sep } from "path";
11410
+ import { lstatSync, mkdirSync as mkdirSync8, readdirSync as readdirSync9, rmSync, statSync as statSync9, symlinkSync as symlinkSync3 } from "fs";
11411
+ import { join as join17, relative, sep } from "path";
11175
11412
  var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
11176
11413
  function droneHomeDeny(env = process.env) {
11177
11414
  const extra = (env.SWARM_DRONE_HOME_DENY || "").split(",").map((s) => s.trim()).filter(Boolean);
@@ -11189,34 +11426,34 @@ function buildDroneHome(opts) {
11189
11426
  const { realHome, droneHome } = opts;
11190
11427
  const deny = opts.deny ?? droneHomeDeny();
11191
11428
  const platform = opts.platform ?? process.platform;
11192
- mkdirSync7(droneHome, { recursive: true, mode: 448 });
11193
- const wanted = new Set(mirroredEntries(readdirSync8(realHome), deny, platform));
11429
+ mkdirSync8(droneHome, { recursive: true, mode: 448 });
11430
+ const wanted = new Set(mirroredEntries(readdirSync9(realHome), deny, platform));
11194
11431
  const rel = relative(realHome, droneHome);
11195
11432
  if (rel && !rel.startsWith("..") && !rel.includes(sep)) wanted.delete(rel);
11196
- for (const existing of readdirSync8(droneHome)) {
11433
+ for (const existing of readdirSync9(droneHome)) {
11197
11434
  if (wanted.has(existing)) continue;
11198
- const p = join16(droneHome, existing);
11435
+ const p = join17(droneHome, existing);
11199
11436
  try {
11200
11437
  if (lstatSync(p).isSymbolicLink()) rmSync(p);
11201
11438
  } catch {
11202
11439
  }
11203
11440
  }
11204
11441
  for (const name of wanted) {
11205
- const link2 = join16(droneHome, name);
11442
+ const link2 = join17(droneHome, name);
11206
11443
  try {
11207
11444
  lstatSync(link2);
11208
11445
  continue;
11209
11446
  } catch {
11210
11447
  }
11211
- const target = join16(realHome, name);
11448
+ const target = join17(realHome, name);
11212
11449
  let isDir;
11213
11450
  try {
11214
- isDir = statSync8(target).isDirectory();
11451
+ isDir = statSync9(target).isDirectory();
11215
11452
  } catch {
11216
11453
  continue;
11217
11454
  }
11218
11455
  try {
11219
- symlinkSync2(target, link2, isDir ? "junction" : "file");
11456
+ symlinkSync3(target, link2, isDir ? "junction" : "file");
11220
11457
  } catch (e) {
11221
11458
  if (e.code !== "EEXIST") throw e;
11222
11459
  }
@@ -11229,7 +11466,7 @@ var REAL_PATH_ENV_PINS = [
11229
11466
  { env: "YARN_GLOBAL_FOLDER", segments: [".yarn", "berry"] }
11230
11467
  ];
11231
11468
  function realPathEnvPins(realHome) {
11232
- return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join16(realHome, ...segments)]));
11469
+ return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join17(realHome, ...segments)]));
11233
11470
  }
11234
11471
  function dronePrivatePaths(realHome, swarmHome) {
11235
11472
  return [
@@ -11312,7 +11549,7 @@ function droneEnv(worktree, base3 = process.env, isolation, droneCap = null) {
11312
11549
  const env = { ...base3 };
11313
11550
  const isolate = base3.SWARM_TURBO_ISOLATE !== "0";
11314
11551
  if (isolate && !base3.TURBO_CACHE_DIR) {
11315
- env.TURBO_CACHE_DIR = join17(worktree, ".turbo", "cache");
11552
+ env.TURBO_CACHE_DIR = join18(worktree, ".turbo", "cache");
11316
11553
  }
11317
11554
  Object.assign(env, droneTestEnv(droneCap, base3));
11318
11555
  for (const key of DAEMON_STATE_ENV) delete env[key];
@@ -11343,21 +11580,21 @@ var DAEMON_STATE_ENV = [
11343
11580
  ...PROVIDER_TOKEN_ENV
11344
11581
  ];
11345
11582
  function bundledRoleDir(kind) {
11346
- return join17(dirname3(fileURLToPath2(import.meta.url)), "..", "roles", kind);
11583
+ return join18(dirname3(fileURLToPath2(import.meta.url)), "..", "roles", kind);
11347
11584
  }
11348
11585
  function roleDir(worktree, kind) {
11349
- return join17(worktree, ".claude", "roles", kind);
11586
+ return join18(worktree, ".claude", "roles", kind);
11350
11587
  }
11351
11588
  function roleDirs(worktree, kind) {
11352
- return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync11(dir));
11589
+ return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync12(dir));
11353
11590
  }
11354
11591
  function nextRunLogPath(runLogsDir, runLogKeys) {
11355
11592
  const dir = runLogDirForWrite(runLogsDir, runLogKeys);
11356
11593
  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)");
11357
- mkdirSync8(dir, { recursive: true });
11594
+ mkdirSync9(dir, { recursive: true });
11358
11595
  let files = [];
11359
11596
  try {
11360
- files = readdirSync9(dir);
11597
+ files = readdirSync10(dir);
11361
11598
  } catch {
11362
11599
  }
11363
11600
  let next = 0;
@@ -11365,7 +11602,7 @@ function nextRunLogPath(runLogsDir, runLogKeys) {
11365
11602
  const m = /^(\d+)\.json/.exec(f);
11366
11603
  if (m) next = Math.max(next, Number(m[1]) + 1);
11367
11604
  }
11368
- return join17(dir, `${next}.json`);
11605
+ return join18(dir, `${next}.json`);
11369
11606
  }
11370
11607
  function openNextRunLog(runLogsDir, runLogKeys) {
11371
11608
  for (let attempt = 0; attempt < 50; attempt++) {
@@ -11457,9 +11694,9 @@ function makeLauncher(cfg) {
11457
11694
  }
11458
11695
  const realHome = cfg.droneIsolation?.realHome ?? homedir6();
11459
11696
  const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
11460
- if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync11(join17(cfg.droneIsolation.swarmHome, "token"))) {
11697
+ if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync12(join18(cfg.droneIsolation.swarmHome, "token"))) {
11461
11698
  console.error(
11462
- `[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 ${join17(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.`
11699
+ `[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 ${join18(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.`
11463
11700
  );
11464
11701
  }
11465
11702
  const executor = cfg.executor ?? executorForRun(run, cfg.swarmModels?.() ?? null);
@@ -11521,8 +11758,8 @@ function makeLauncher(cfg) {
11521
11758
  init_procCleanup();
11522
11759
 
11523
11760
  // src/cost.ts
11524
- import { readFileSync as readFileSync15, readdirSync as readdirSync10, renameSync as renameSync9, statSync as statSync9 } from "fs";
11525
- import { join as join18 } from "path";
11761
+ import { readFileSync as readFileSync15, readdirSync as readdirSync11, renameSync as renameSync9, statSync as statSync10 } from "fs";
11762
+ import { join as join19 } from "path";
11526
11763
  init_executors();
11527
11764
  init_swarmModels();
11528
11765
  var UNPROCESSED_LOG_RE = /^\d+\.json$/;
@@ -11530,7 +11767,7 @@ var SEGMENT_LOG_RE = /^(\d+)\.json(\.done)?$/;
11530
11767
  var RESCUE_OPEN_WINDOW_MAX_MS = 12 * 60 * 60 * 1e3;
11531
11768
  function unprocessedLogFiles(dir) {
11532
11769
  try {
11533
- return readdirSync10(dir).filter((f) => UNPROCESSED_LOG_RE.test(f));
11770
+ return readdirSync11(dir).filter((f) => UNPROCESSED_LOG_RE.test(f));
11534
11771
  } catch {
11535
11772
  return [];
11536
11773
  }
@@ -11539,7 +11776,7 @@ function segmentStarts(dir) {
11539
11776
  const starts = /* @__PURE__ */ new Map();
11540
11777
  let entries;
11541
11778
  try {
11542
- entries = readdirSync10(dir);
11779
+ entries = readdirSync11(dir);
11543
11780
  } catch {
11544
11781
  return starts;
11545
11782
  }
@@ -11547,7 +11784,7 @@ function segmentStarts(dir) {
11547
11784
  const m = SEGMENT_LOG_RE.exec(f);
11548
11785
  if (!m) continue;
11549
11786
  try {
11550
- const st = statSync9(join18(dir, f));
11787
+ const st = statSync10(join19(dir, f));
11551
11788
  starts.set(Number(m[1]), st.birthtimeMs > 0 ? st.birthtimeMs : st.ctimeMs);
11552
11789
  } catch {
11553
11790
  }
@@ -11600,7 +11837,7 @@ function accountRunCost(deps, run) {
11600
11837
  let unmeasured = 0;
11601
11838
  const processed = [];
11602
11839
  for (const f of files) {
11603
- const full = join18(dir, f);
11840
+ const full = join19(dir, f);
11604
11841
  let raw = null;
11605
11842
  try {
11606
11843
  raw = readFileSync15(full, "utf8");
@@ -11657,8 +11894,8 @@ function makeCostSweeper(deps) {
11657
11894
  }
11658
11895
 
11659
11896
  // src/runOutcome.ts
11660
- import { readdirSync as readdirSync11, readFileSync as readFileSync16 } from "fs";
11661
- import { join as join19 } from "path";
11897
+ import { readdirSync as readdirSync12, readFileSync as readFileSync16 } from "fs";
11898
+ import { join as join20 } from "path";
11662
11899
  init_executors();
11663
11900
  function endedItself(outcome) {
11664
11901
  return outcome === "graceful" || outcome === "budget" || outcome === "aborted";
@@ -11672,7 +11909,7 @@ function readLatestResultRaw(runLogsDir, runLogKeys) {
11672
11909
  if (!dir) return null;
11673
11910
  let files;
11674
11911
  try {
11675
- files = readdirSync11(dir);
11912
+ files = readdirSync12(dir);
11676
11913
  } catch {
11677
11914
  return null;
11678
11915
  }
@@ -11685,7 +11922,7 @@ function readLatestResultRaw(runLogsDir, runLogKeys) {
11685
11922
  }
11686
11923
  if (!latest) return null;
11687
11924
  try {
11688
- return readFileSync16(join19(dir, latest.name), "utf8");
11925
+ return readFileSync16(join20(dir, latest.name), "utf8");
11689
11926
  } catch {
11690
11927
  return null;
11691
11928
  }
@@ -11717,9 +11954,11 @@ init_claudeCode();
11717
11954
  init_swarmModels();
11718
11955
 
11719
11956
  // src/executors/detect.ts
11957
+ init_src();
11958
+ init_swarmModels();
11720
11959
  init_executors();
11721
11960
  import { accessSync, constants } from "fs";
11722
- import { delimiter, join as join20 } from "path";
11961
+ import { delimiter, join as join21 } from "path";
11723
11962
  function binRunnable(bin, env) {
11724
11963
  if (!bin) return false;
11725
11964
  if (bin.includes("/") || bin.includes("\\")) {
@@ -11735,7 +11974,7 @@ function binRunnable(bin, env) {
11735
11974
  if (!dir) continue;
11736
11975
  for (const ext of exts) {
11737
11976
  try {
11738
- accessSync(join20(dir, bin + ext), constants.X_OK);
11977
+ accessSync(join21(dir, bin + ext), constants.X_OK);
11739
11978
  return true;
11740
11979
  } catch {
11741
11980
  }
@@ -11766,26 +12005,36 @@ function cachedInstalledExecutorIds(now = Date.now(), ttlMs = 3e5, env = process
11766
12005
  }
11767
12006
  return cachedIds;
11768
12007
  }
12008
+ function availableProviders(installedExecutors, env = process.env) {
12009
+ const explicit = providersFromEnv(env);
12010
+ if (explicit) return explicit;
12011
+ const installed2 = new Set(installedExecutors);
12012
+ return MODEL_PROVIDERS.filter((p) => installed2.has(p.executorId)).map((p) => p.id);
12013
+ }
12014
+ function providerUnavailableReason(providerId, available) {
12015
+ if (available.length === 0 || available.includes(providerId)) return null;
12016
+ return `provider_unavailable: \u043C\u043E\u0434\u0435\u043B\u044C \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \xAB${providerId}\xBB, \u0430 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0442\u043E\u043B\u044C\u043A\u043E ${available.join(", ")} \u2014 \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0435 \u0431\u0435\u0440\u0443, \u0435\u0433\u043E \u0432\u043E\u0437\u044C\u043C\u0451\u0442 \u0434\u0440\u0443\u0433\u0430\u044F \u043C\u0430\u0448\u0438\u043D\u0430`;
12017
+ }
11769
12018
 
11770
12019
  // src/orchestrator.ts
11771
12020
  init_src();
11772
12021
  init_git();
11773
12022
 
11774
12023
  // src/deps.ts
11775
- import { existsSync as existsSync14, readdirSync as readdirSync13, readFileSync as readFileSync18 } from "fs";
11776
- import { join as join24 } from "path";
12024
+ import { existsSync as existsSync15, readdirSync as readdirSync14, readFileSync as readFileSync18 } from "fs";
12025
+ import { join as join25 } from "path";
11777
12026
  import { execa } from "execa";
11778
12027
 
11779
12028
  // src/prepareLock.ts
11780
12029
  import { createHash } from "crypto";
11781
- import { closeSync as closeSync4, openSync as openSync4, rmSync as rmSync2, statSync as statSync11, writeSync } from "fs";
11782
- import { join as join22 } from "path";
12030
+ import { closeSync as closeSync4, openSync as openSync4, rmSync as rmSync2, statSync as statSync12, writeSync } from "fs";
12031
+ import { join as join23 } from "path";
11783
12032
  import { tmpdir } from "os";
11784
12033
  var WAIT_TIMEOUT_MS = 5 * 6e4;
11785
12034
  var POLL_MS = 300;
11786
12035
  var STALE_MS = 20 * 6e4;
11787
12036
  function machineLockPath(key, dir = tmpdir()) {
11788
- return join22(dir, `swarm-prepare-${createHash("sha1").update(key).digest("hex").slice(0, 16)}.lock`);
12037
+ return join23(dir, `swarm-prepare-${createHash("sha1").update(key).digest("hex").slice(0, 16)}.lock`);
11789
12038
  }
11790
12039
  function defaultTryCreate(path, content) {
11791
12040
  try {
@@ -11802,7 +12051,7 @@ function defaultTryCreate(path, content) {
11802
12051
  }
11803
12052
  function defaultAgeMs(path) {
11804
12053
  try {
11805
- return Date.now() - statSync11(path).mtimeMs;
12054
+ return Date.now() - statSync12(path).mtimeMs;
11806
12055
  } catch {
11807
12056
  return null;
11808
12057
  }
@@ -11846,8 +12095,8 @@ init_config();
11846
12095
  init_config();
11847
12096
  import { createHash as createHash2 } from "crypto";
11848
12097
  import { cp, link, lstat, mkdir as mkdir2, readdir, readlink, rename as rename2, rm as rm2, stat, symlink, utimes, writeFile as writeFile2 } from "fs/promises";
11849
- import { existsSync as existsSync13, readFileSync as readFileSync17 } from "fs";
11850
- import { join as join23 } from "path";
12098
+ import { existsSync as existsSync14, readFileSync as readFileSync17 } from "fs";
12099
+ import { join as join24 } from "path";
11851
12100
  function fingerprint(parts) {
11852
12101
  const h = createHash2("sha256");
11853
12102
  for (const p of parts) {
@@ -11861,8 +12110,8 @@ async function cloneTree(src, dst) {
11861
12110
  const entries = await readdir(src, { withFileTypes: true });
11862
12111
  await Promise.all(
11863
12112
  entries.map(async (e) => {
11864
- const from = join23(src, e.name);
11865
- const to = join23(dst, e.name);
12113
+ const from = join24(src, e.name);
12114
+ const to = join24(dst, e.name);
11866
12115
  if (e.isDirectory()) return cloneTree(from, to);
11867
12116
  if (e.isSymbolicLink()) {
11868
12117
  return symlink(await readlink(from), to).catch(() => cp(from, to, { recursive: true, verbatimSymlinks: true }));
@@ -11875,11 +12124,11 @@ function cacheRoot(io) {
11875
12124
  return io.root ?? ENV_CACHE_DIR;
11876
12125
  }
11877
12126
  function snapshotDir(io, key) {
11878
- return join23(cacheRoot(io), key);
12127
+ return join24(cacheRoot(io), key);
11879
12128
  }
11880
12129
  function readManifest(dir) {
11881
12130
  try {
11882
- const parsed = JSON.parse(readFileSync17(join23(dir, "manifest.json"), "utf8"));
12131
+ const parsed = JSON.parse(readFileSync17(join24(dir, "manifest.json"), "utf8"));
11883
12132
  if (!parsed || typeof parsed !== "object") return null;
11884
12133
  const m = parsed;
11885
12134
  if (typeof m.key !== "string" || !Array.isArray(m.dirs)) return null;
@@ -11891,11 +12140,11 @@ function readManifest(dir) {
11891
12140
  }
11892
12141
  }
11893
12142
  function hasEnvSnapshot(key, io = {}) {
11894
- const exists = io.exists ?? existsSync13;
11895
- return exists(join23(snapshotDir(io, key), "manifest.json"));
12143
+ const exists = io.exists ?? existsSync14;
12144
+ return exists(join24(snapshotDir(io, key), "manifest.json"));
11896
12145
  }
11897
12146
  async function restoreEnvSnapshot(worktree, key, io = {}) {
11898
- const exists = io.exists ?? existsSync13;
12147
+ const exists = io.exists ?? existsSync14;
11899
12148
  const clone = io.clone ?? cloneTree;
11900
12149
  const dir = snapshotDir(io, key);
11901
12150
  const manifest = readManifest(dir);
@@ -11904,13 +12153,13 @@ async function restoreEnvSnapshot(worktree, key, io = {}) {
11904
12153
  try {
11905
12154
  for (let i = 0; i < manifest.dirs.length; i++) {
11906
12155
  const rel = manifest.dirs[i] ?? "";
11907
- const target = join23(worktree, rel);
12156
+ const target = join24(worktree, rel);
11908
12157
  if (exists(target)) continue;
11909
- await clone(join23(dir, "trees", String(i)), target);
12158
+ await clone(join24(dir, "trees", String(i)), target);
11910
12159
  made.push(rel);
11911
12160
  }
11912
12161
  } catch {
11913
- for (const rel of made) await rm2(join23(worktree, rel), { recursive: true, force: true }).catch(() => {
12162
+ for (const rel of made) await rm2(join24(worktree, rel), { recursive: true, force: true }).catch(() => {
11914
12163
  });
11915
12164
  return null;
11916
12165
  }
@@ -11923,19 +12172,19 @@ async function touch(dir) {
11923
12172
  });
11924
12173
  }
11925
12174
  async function saveEnvSnapshot(worktree, key, dirs, io = {}) {
11926
- const exists = io.exists ?? existsSync13;
12175
+ const exists = io.exists ?? existsSync14;
11927
12176
  const clone = io.clone ?? cloneTree;
11928
12177
  if (hasEnvSnapshot(key, io)) return true;
11929
- const present = dirs.filter((rel) => exists(join23(worktree, rel)));
12178
+ const present = dirs.filter((rel) => exists(join24(worktree, rel)));
11930
12179
  if (!present.length) return false;
11931
12180
  const root = cacheRoot(io);
11932
- const tmp = join23(root, `.tmp-${key}-${process.pid}`);
12181
+ const tmp = join24(root, `.tmp-${key}-${process.pid}`);
11933
12182
  try {
11934
12183
  await rm2(tmp, { recursive: true, force: true });
11935
- await mkdir2(join23(tmp, "trees"), { recursive: true });
11936
- for (let i = 0; i < present.length; i++) await clone(join23(worktree, present[i] ?? ""), join23(tmp, "trees", String(i)));
12184
+ await mkdir2(join24(tmp, "trees"), { recursive: true });
12185
+ for (let i = 0; i < present.length; i++) await clone(join24(worktree, present[i] ?? ""), join24(tmp, "trees", String(i)));
11937
12186
  const manifest = { key, dirs: present, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
11938
- await writeFile2(join23(tmp, "manifest.json"), JSON.stringify(manifest, null, 2));
12187
+ await writeFile2(join24(tmp, "manifest.json"), JSON.stringify(manifest, null, 2));
11939
12188
  await rename2(tmp, snapshotDir(io, key));
11940
12189
  return true;
11941
12190
  } catch {
@@ -11955,13 +12204,13 @@ async function pruneEnvSnapshots(io = {}) {
11955
12204
  }
11956
12205
  const withTime = [];
11957
12206
  for (const name of names) {
11958
- const s = await stat(join23(root, name)).catch(() => null);
12207
+ const s = await stat(join24(root, name)).catch(() => null);
11959
12208
  if (s) withTime.push({ name, mtime: s.mtimeMs });
11960
12209
  }
11961
12210
  withTime.sort((a, b) => b.mtime - a.mtime);
11962
12211
  let removed = 0;
11963
12212
  for (const { name } of withTime.slice(Math.max(0, keep))) {
11964
- await rm2(join23(root, name), { recursive: true, force: true }).catch(() => {
12213
+ await rm2(join24(root, name), { recursive: true, force: true }).catch(() => {
11965
12214
  });
11966
12215
  removed++;
11967
12216
  }
@@ -11977,9 +12226,9 @@ async function sweepEnvCacheTemp(io = {}) {
11977
12226
  }
11978
12227
  let removed = 0;
11979
12228
  for (const name of names) {
11980
- const s = await lstat(join23(root, name)).catch(() => null);
12229
+ const s = await lstat(join24(root, name)).catch(() => null);
11981
12230
  if (!s || Date.now() - s.mtimeMs < 60 * 6e4) continue;
11982
- await rm2(join23(root, name), { recursive: true, force: true }).catch(() => {
12231
+ await rm2(join24(root, name), { recursive: true, force: true }).catch(() => {
11983
12232
  });
11984
12233
  removed++;
11985
12234
  }
@@ -12013,14 +12262,14 @@ function safeReadText(p) {
12013
12262
  }
12014
12263
  function safeListDirs(p) {
12015
12264
  try {
12016
- return readdirSync13(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
12265
+ return readdirSync14(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
12017
12266
  } catch {
12018
12267
  return [];
12019
12268
  }
12020
12269
  }
12021
12270
  function safeListFiles(p) {
12022
12271
  try {
12023
- return readdirSync13(p, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name);
12272
+ return readdirSync14(p, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name);
12024
12273
  } catch {
12025
12274
  return [];
12026
12275
  }
@@ -12046,21 +12295,21 @@ function overridePlan(override) {
12046
12295
  };
12047
12296
  }
12048
12297
  function detectPackageManager(worktree, io = {}) {
12049
- const exists = io.exists ?? existsSync14;
12298
+ const exists = io.exists ?? existsSync15;
12050
12299
  const readText = io.readText ?? safeReadText;
12051
- const hasPkgJson = exists(join24(worktree, "package.json"));
12300
+ const hasPkgJson = exists(join25(worktree, "package.json"));
12052
12301
  if (!hasPkgJson) {
12053
- if (exists(join24(worktree, "deno.json")) || exists(join24(worktree, "deno.jsonc")) || exists(join24(worktree, "deno.lock"))) return "deno";
12302
+ if (exists(join25(worktree, "deno.json")) || exists(join25(worktree, "deno.jsonc")) || exists(join25(worktree, "deno.lock"))) return "deno";
12054
12303
  return null;
12055
12304
  }
12056
- const pkg = readJson(readText, join24(worktree, "package.json"));
12305
+ const pkg = readJson(readText, join25(worktree, "package.json"));
12057
12306
  const declared = typeof pkg?.packageManager === "string" ? pkg.packageManager.split("@")[0] : void 0;
12058
12307
  if (declared === "pnpm" || declared === "bun" || declared === "npm" || declared === "yarn") return declared;
12059
- if (exists(join24(worktree, "bun.lock")) || exists(join24(worktree, "bun.lockb"))) return "bun";
12060
- if (exists(join24(worktree, "pnpm-lock.yaml"))) return "pnpm";
12061
- if (exists(join24(worktree, "yarn.lock"))) return "yarn";
12062
- if (exists(join24(worktree, "package-lock.json"))) return "npm";
12063
- if (exists(join24(worktree, "deno.lock"))) return "deno";
12308
+ if (exists(join25(worktree, "bun.lock")) || exists(join25(worktree, "bun.lockb"))) return "bun";
12309
+ if (exists(join25(worktree, "pnpm-lock.yaml"))) return "pnpm";
12310
+ if (exists(join25(worktree, "yarn.lock"))) return "yarn";
12311
+ if (exists(join25(worktree, "package-lock.json"))) return "npm";
12312
+ if (exists(join25(worktree, "deno.lock"))) return "deno";
12064
12313
  return "npm";
12065
12314
  }
12066
12315
  function jsInstallCommand(pm) {
@@ -12116,7 +12365,7 @@ var ECOSYSTEM_MARKERS = [
12116
12365
  // конфигурация — дело сборки, не подготовки
12117
12366
  ];
12118
12367
  function detectPlan(worktree, io) {
12119
- const exists = io.exists ?? existsSync14;
12368
+ const exists = io.exists ?? existsSync15;
12120
12369
  const listFiles = io.listFiles ?? safeListFiles;
12121
12370
  const pm = detectPackageManager(worktree, io);
12122
12371
  if (pm) {
@@ -12124,7 +12373,7 @@ function detectPlan(worktree, io) {
12124
12373
  // node_modules на месте — установку пропускаем (быстрый путь переиспользованного worktree).
12125
12374
  // У deno каталога может не быть вовсе (кэш в DENO_DIR) — тогда `deno install` отработает
12126
12375
  // заново, но он дешёвый: тянет из того же общего кэша.
12127
- { label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join24(worktree, "node_modules"), source: "detect" }
12376
+ { label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join25(worktree, "node_modules"), source: "detect" }
12128
12377
  ];
12129
12378
  for (const target of workspaceBuildTargets(worktree, io)) {
12130
12379
  commands.push({ label: `\u0441\u0431\u043E\u0440\u043A\u0430 ${target.name}`, run: jsRunScript(pm, "build"), cwd: target.dir, source: "detect" });
@@ -12133,12 +12382,12 @@ function detectPlan(worktree, io) {
12133
12382
  return { source: "detect", ecosystem: `node/${pm}`, commands };
12134
12383
  }
12135
12384
  for (const marker of ECOSYSTEM_MARKERS) {
12136
- const hit = marker.files.some((f) => exists(join24(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
12385
+ const hit = marker.files.some((f) => exists(join25(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
12137
12386
  if (!hit) continue;
12138
12387
  let ecosystem = marker.ecosystem;
12139
12388
  let command = marker.command;
12140
12389
  for (const refine of marker.refine ?? []) {
12141
- if (!exists(join24(worktree, refine.file))) continue;
12390
+ if (!exists(join25(worktree, refine.file))) continue;
12142
12391
  ecosystem = refine.ecosystem;
12143
12392
  command = refine.command;
12144
12393
  break;
@@ -12195,12 +12444,12 @@ var CI_INSTALL_PATTERNS = [
12195
12444
  function ciWorkflowPlan(worktree, io) {
12196
12445
  const readText = io.readText ?? safeReadText;
12197
12446
  const listFiles = io.listFiles ?? safeListFiles;
12198
- const dir = join24(worktree, ".github", "workflows");
12447
+ const dir = join25(worktree, ".github", "workflows");
12199
12448
  const files = listFiles(dir).filter((n) => n.endsWith(".yml") || n.endsWith(".yaml"));
12200
12449
  const seen = /* @__PURE__ */ new Set();
12201
12450
  const commands = [];
12202
12451
  for (const name of files.sort()) {
12203
- const yaml = readText(join24(dir, name));
12452
+ const yaml = readText(join25(dir, name));
12204
12453
  if (yaml === null) continue;
12205
12454
  for (const raw of workflowRunLines(yaml)) {
12206
12455
  const line = raw.replace(/\s+/g, " ").trim();
@@ -12215,7 +12464,7 @@ function ciWorkflowPlan(worktree, io) {
12215
12464
  return commands.length ? { source: "ci-workflow", ecosystem: "ci-\u043A\u043E\u043D\u0444\u0438\u0433", commands } : null;
12216
12465
  }
12217
12466
  function workspaceGlobs(worktree, readText) {
12218
- const yaml = readText(join24(worktree, "pnpm-workspace.yaml"));
12467
+ const yaml = readText(join25(worktree, "pnpm-workspace.yaml"));
12219
12468
  if (yaml !== null) {
12220
12469
  const globs = [];
12221
12470
  let inPackages = false;
@@ -12235,7 +12484,7 @@ function workspaceGlobs(worktree, readText) {
12235
12484
  }
12236
12485
  if (globs.length) return globs;
12237
12486
  }
12238
- const pkg = readJson(readText, join24(worktree, "package.json"));
12487
+ const pkg = readJson(readText, join25(worktree, "package.json"));
12239
12488
  const ws = pkg?.workspaces;
12240
12489
  const list = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
12241
12490
  return list.filter((g) => typeof g === "string");
@@ -12246,14 +12495,14 @@ function workspaceDirs(worktree, globs, io) {
12246
12495
  if (glob.startsWith("!")) continue;
12247
12496
  const star = glob.indexOf("*");
12248
12497
  if (star === -1) {
12249
- const dir = join24(worktree, glob);
12250
- if (io.exists(join24(dir, "package.json"))) dirs.add(dir);
12498
+ const dir = join25(worktree, glob);
12499
+ if (io.exists(join25(dir, "package.json"))) dirs.add(dir);
12251
12500
  continue;
12252
12501
  }
12253
- const base3 = join24(worktree, glob.slice(0, star).replace(/\/$/, ""));
12502
+ const base3 = join25(worktree, glob.slice(0, star).replace(/\/$/, ""));
12254
12503
  for (const name of io.listDirs(base3)) {
12255
- const dir = join24(base3, name);
12256
- if (io.exists(join24(dir, "package.json"))) dirs.add(dir);
12504
+ const dir = join25(base3, name);
12505
+ if (io.exists(join25(dir, "package.json"))) dirs.add(dir);
12257
12506
  }
12258
12507
  }
12259
12508
  return [...dirs];
@@ -12274,18 +12523,18 @@ function entryPaths(pkg) {
12274
12523
  return out;
12275
12524
  }
12276
12525
  function workspaceBuildTargets(worktree, io = {}) {
12277
- const exists = io.exists ?? existsSync14;
12526
+ const exists = io.exists ?? existsSync15;
12278
12527
  const readText = io.readText ?? safeReadText;
12279
12528
  const listDirs = io.listDirs ?? safeListDirs;
12280
12529
  const globs = workspaceGlobs(worktree, readText);
12281
12530
  if (!globs.length) return [];
12282
12531
  const pkgs = [];
12283
12532
  for (const dir of workspaceDirs(worktree, globs, { exists, listDirs })) {
12284
- const pkg = readJson(readText, join24(dir, "package.json"));
12533
+ const pkg = readJson(readText, join25(dir, "package.json"));
12285
12534
  if (!pkg || typeof pkg.name !== "string") continue;
12286
12535
  const scripts = pkg.scripts ?? {};
12287
12536
  const entries = entryPaths(pkg);
12288
- const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join24(dir, e)));
12537
+ const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join25(dir, e)));
12289
12538
  const deps = Object.keys({ ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} });
12290
12539
  pkgs.push({ name: pkg.name, dir, deps, needsBuild });
12291
12540
  }
@@ -12313,7 +12562,7 @@ function devDatabaseUrl(worktree, io = {}) {
12313
12562
  const fromEnv = (io.env ?? process.env).DATABASE_URL;
12314
12563
  if (fromEnv && fromEnv.trim()) return fromEnv.trim();
12315
12564
  for (const file of [".env", ".env.example"]) {
12316
- const raw = readText(join24(worktree, file));
12565
+ const raw = readText(join25(worktree, file));
12317
12566
  if (raw === null) continue;
12318
12567
  const m = /^\s*(?:export\s+)?DATABASE_URL\s*=\s*(.*)$/m.exec(raw);
12319
12568
  const value = (m?.[1] ?? "").trim().replace(/^['"]|['"]$/g, "");
@@ -12329,11 +12578,11 @@ function isLocalDatabase(url) {
12329
12578
  }
12330
12579
  }
12331
12580
  function prismaPackages(worktree, io) {
12332
- const exists = io.exists ?? existsSync14;
12581
+ const exists = io.exists ?? existsSync15;
12333
12582
  const readText = io.readText ?? safeReadText;
12334
12583
  const listDirs = io.listDirs ?? safeListDirs;
12335
12584
  const dirs = [worktree, ...workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs })];
12336
- return dirs.filter((dir) => exists(join24(dir, "prisma", "schema.prisma")) && listDirs(join24(dir, "prisma", "migrations")).length > 0);
12585
+ return dirs.filter((dir) => exists(join25(dir, "prisma", "schema.prisma")) && listDirs(join25(dir, "prisma", "migrations")).length > 0);
12337
12586
  }
12338
12587
  function jsExecBin(pm, command) {
12339
12588
  switch (pm) {
@@ -12363,25 +12612,25 @@ function migrateCommands(worktree, pm, io) {
12363
12612
  var ENV_DEFINING_FILES = ["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lock", "bun.lockb", "deno.lock", "pnpm-workspace.yaml"];
12364
12613
  function envCachePlan(worktree, io = {}) {
12365
12614
  if (!ENV_CACHE_ENABLED) return null;
12366
- const exists = io.exists ?? existsSync14;
12615
+ const exists = io.exists ?? existsSync15;
12367
12616
  const readText = io.readText ?? safeReadText;
12368
12617
  const listDirs = io.listDirs ?? safeListDirs;
12369
12618
  const pm = detectPackageManager(worktree, io);
12370
12619
  if (!pm) return null;
12371
12620
  const parts = [`pm:${pm}`];
12372
12621
  for (const name of ENV_DEFINING_FILES) {
12373
- const raw = readText(join24(worktree, name));
12622
+ const raw = readText(join25(worktree, name));
12374
12623
  if (raw !== null) parts.push(`${name}
12375
12624
  ${raw}`);
12376
12625
  }
12377
12626
  const pkgDirs = workspaceDirs(worktree, workspaceGlobs(worktree, readText), { exists, listDirs });
12378
12627
  const rels = pkgDirs.map((dir) => dir.slice(worktree.length + 1)).sort();
12379
12628
  for (const rel of ["", ...rels]) {
12380
- const raw = readText(join24(worktree, rel, "package.json"));
12629
+ const raw = readText(join25(worktree, rel, "package.json"));
12381
12630
  if (raw !== null) parts.push(`${rel}/package.json
12382
12631
  ${raw}`);
12383
12632
  }
12384
- return { key: fingerprint(parts), dirs: ["node_modules", ...rels.map((rel) => join24(rel, "node_modules"))] };
12633
+ return { key: fingerprint(parts), dirs: ["node_modules", ...rels.map((rel) => join25(rel, "node_modules"))] };
12385
12634
  }
12386
12635
  function planPrepare(worktree, io = {}) {
12387
12636
  const plan = overridePlan(io.override) ?? detectPlan(worktree, io) ?? ciWorkflowPlan(worktree, io);
@@ -12389,7 +12638,7 @@ function planPrepare(worktree, io = {}) {
12389
12638
  return { ...plan, commands: plan.commands.slice(0, MAX_PLAN_COMMANDS) };
12390
12639
  }
12391
12640
  async function ensureWorktreeDeps(worktree, io = {}) {
12392
- const exists = io.exists ?? existsSync14;
12641
+ const exists = io.exists ?? existsSync15;
12393
12642
  const exec = io.exec ?? ((cmd) => runCommand(cmd, worktree));
12394
12643
  const approve = io.approve ?? (() => true);
12395
12644
  const cachePlan = envCachePlan(worktree, io);
@@ -12406,7 +12655,7 @@ async function ensureWorktreeDeps(worktree, io = {}) {
12406
12655
  restored: [],
12407
12656
  cached: false
12408
12657
  };
12409
- if (cachePlan && !exists(join24(worktree, "node_modules"))) {
12658
+ if (cachePlan && !exists(join25(worktree, "node_modules"))) {
12410
12659
  report.restored = await restoreEnvSnapshot(worktree, cachePlan.key, cacheIO) ?? [];
12411
12660
  }
12412
12661
  const plan = planPrepare(worktree, io);
@@ -12493,10 +12742,10 @@ function depsPromptNotice(report) {
12493
12742
 
12494
12743
  // src/orchestrator.ts
12495
12744
  init_config();
12496
- import { existsSync as existsSync15, mkdirSync as mkdirSync9, rmSync as rmSync3 } from "fs";
12497
- import { join as join25, dirname as dirname4 } from "path";
12745
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, rmSync as rmSync3 } from "fs";
12746
+ import { join as join26, dirname as dirname4 } from "path";
12498
12747
  function repolessRunDir(worktreesDir, taskSlug) {
12499
- return join25(dirname4(worktreesDir), "runs", taskSlug);
12748
+ return join26(dirname4(worktreesDir), "runs", taskSlug);
12500
12749
  }
12501
12750
  var WORKTREE_HELD_STATUSES = /* @__PURE__ */ new Set(["running", "blocked", "review", "paused", "storm_dead"]);
12502
12751
  function worktreePathHeldLive(db, path, exceptRunId) {
@@ -12541,11 +12790,11 @@ function healTaskSlug(db, run, task) {
12541
12790
  async function spawnDrone(deps, run) {
12542
12791
  if (run.repoless) {
12543
12792
  const workdir = repolessRunDir(deps.worktreesDir, run.task_slug);
12544
- if (existsSync15(workdir)) {
12793
+ if (existsSync16(workdir)) {
12545
12794
  rmSync3(workdir, { recursive: true, force: true });
12546
12795
  deps.db.addEvent(run.id, "step", `\u0440\u0435\u043A\u043B\u0435\u0439\u043C \u043E\u0441\u0438\u0440\u043E\u0442\u0435\u0432\u0448\u0435\u0433\u043E \u0441\u043A\u0440\u0430\u0442\u0447-\u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 ${workdir} (\u0434\u0435\u0440\u0436\u0430\u043B \u043C\u0451\u0440\u0442\u0432\u044B\u0439 \u043F\u0440\u043E\u0433\u043E\u043D)`);
12547
12796
  }
12548
- mkdirSync9(workdir, { recursive: true });
12797
+ mkdirSync10(workdir, { recursive: true });
12549
12798
  if (run.area && deps.db.runningAreas("").includes(run.area)) throw new AreaBusyError(run.area);
12550
12799
  deps.db.addEvent(run.id, "step", `repoless-\u043F\u0440\u043E\u0433\u043E\u043D: \u0441\u043A\u0440\u0430\u0442\u0447-\u043A\u0430\u0442\u0430\u043B\u043E\u0433 ${workdir} (\u0431\u0435\u0437 worktree/\u0432\u0435\u0442\u043A\u0438/PR \u2014 TASWM-595)`);
12551
12800
  const built2 = await deps.buildPrompt(run, workdir);
@@ -12760,7 +13009,7 @@ function parkReasonUnretriable(reason) {
12760
13009
  // package.json
12761
13010
  var package_default = {
12762
13011
  name: "@taskless-app/swarm",
12763
- version: "0.6.62",
13012
+ version: "0.6.63",
12764
13013
  type: "module",
12765
13014
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
12766
13015
  keywords: [
@@ -12811,7 +13060,7 @@ var package_default = {
12811
13060
  execa: "^10.0.0",
12812
13061
  zod: "3.25.76"
12813
13062
  },
12814
- gitHead: "e999630218010a25c03a6024e1b71ef34b2359ec"
13063
+ gitHead: "8d438b6288da036308528cff1160a157b4b64abd"
12815
13064
  };
12816
13065
 
12817
13066
  // src/version.ts
@@ -13599,8 +13848,8 @@ import { z } from "zod";
13599
13848
  init_git();
13600
13849
 
13601
13850
  // src/diff.ts
13602
- import { statSync as statSync12 } from "fs";
13603
- import { join as join30 } from "path";
13851
+ import { statSync as statSync13 } from "fs";
13852
+ import { join as join31 } from "path";
13604
13853
  import { $ as $4 } from "execa";
13605
13854
  var MAX_FILES = 200;
13606
13855
  var MAX_LINES_PER_FILE = 1500;
@@ -13743,7 +13992,7 @@ async function resolveBase(wt, baseBranch) {
13743
13992
  }
13744
13993
  function mtime(path) {
13745
13994
  try {
13746
- return statSync12(path).mtimeMs;
13995
+ return statSync13(path).mtimeMs;
13747
13996
  } catch {
13748
13997
  return 0;
13749
13998
  }
@@ -13768,7 +14017,7 @@ async function collectRunDiff(worktree, opts = {}) {
13768
14017
  files.push(pf);
13769
14018
  }
13770
14019
  }
13771
- files.sort((a, b) => mtime(join30(worktree, b.path)) - mtime(join30(worktree, a.path)));
14020
+ files.sort((a, b) => mtime(join31(worktree, b.path)) - mtime(join31(worktree, a.path)));
13772
14021
  const truncatedFiles = files.length > MAX_FILES;
13773
14022
  if (truncatedFiles) files = files.slice(0, MAX_FILES);
13774
14023
  const added = files.reduce((s, f) => s + f.added, 0);
@@ -15768,9 +16017,9 @@ function mergeQueueCandidates(runs, repo, repoFallback) {
15768
16017
  const byPr = /* @__PURE__ */ new Map();
15769
16018
  for (const r of runs) {
15770
16019
  if (effectiveRepoOf(r.repo, repoFallback) !== repo || !isMergeCandidate(r)) continue;
15771
- const num = prNumberOf(r.pr_url);
15772
- const seen = byPr.get(num);
15773
- if (!seen || r.id > seen.id) byPr.set(num, r);
16020
+ const num2 = prNumberOf(r.pr_url);
16021
+ const seen = byPr.get(num2);
16022
+ if (!seen || r.id > seen.id) byPr.set(num2, r);
15774
16023
  }
15775
16024
  return [...byPr.values()];
15776
16025
  }
@@ -15783,10 +16032,10 @@ function makeMergeQueueResolver(deps) {
15783
16032
  };
15784
16033
  return async (run) => {
15785
16034
  if (!deps.baseSync || !run.pr_url) return null;
15786
- const num = prNumberOf(run.pr_url);
15787
- if (num == null) return null;
16035
+ const num2 = prNumberOf(run.pr_url);
16036
+ if (num2 == null) return null;
15788
16037
  const candidates = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback);
15789
- const mine = candidates.find((c) => prNumberOf(c.pr_url) === num);
16038
+ const mine = candidates.find((c) => prNumberOf(c.pr_url) === num2);
15790
16039
  if (!mine) return null;
15791
16040
  const ready = [];
15792
16041
  const urlOf = /* @__PURE__ */ new Map();
@@ -15902,10 +16151,10 @@ function makeMergeIntentResolver(deps) {
15902
16151
  };
15903
16152
  return async (run) => {
15904
16153
  if (!deps.baseSync || !run.pr_url || !hasMergeIntent(run)) return null;
15905
- const num = prNumberOf(run.pr_url);
15906
- if (num == null) return null;
16154
+ const num2 = prNumberOf(run.pr_url);
16155
+ if (num2 == null) return null;
15907
16156
  const marked = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback).filter(hasMergeIntent);
15908
- const mine = marked.find((c) => prNumberOf(c.pr_url) === num);
16157
+ const mine = marked.find((c) => prNumberOf(c.pr_url) === num2);
15909
16158
  if (!mine) return null;
15910
16159
  const queue = orderMergeQueue(marked.map(entryOf));
15911
16160
  const entry = entryOf(mine);
@@ -16062,22 +16311,22 @@ async function prodClearMergeIntent(runId, deps = {}) {
16062
16311
  }
16063
16312
 
16064
16313
  // src/envGate.ts
16065
- import { existsSync as existsSync18 } from "fs";
16066
- import { join as join32 } from "path";
16314
+ import { existsSync as existsSync19 } from "fs";
16315
+ import { join as join33 } from "path";
16067
16316
  import { createConnection } from "net";
16068
16317
  import { execa as execa2 } from "execa";
16069
16318
 
16070
16319
  // src/platform.ts
16071
- import { existsSync as existsSync17, readFileSync as readFileSync22 } from "fs";
16072
- import { delimiter as delimiter2, join as join31 } from "path";
16320
+ import { existsSync as existsSync18, readFileSync as readFileSync22 } from "fs";
16321
+ import { delimiter as delimiter2, join as join32 } from "path";
16073
16322
  function resolveCommandPath(name, env = process.env, platform = process.platform) {
16074
- if (name.includes("/") || name.includes("\\")) return existsSync17(name) ? name : null;
16323
+ if (name.includes("/") || name.includes("\\")) return existsSync18(name) ? name : null;
16075
16324
  const dirs = (env.PATH ?? env.Path ?? "").split(delimiter2).filter(Boolean);
16076
16325
  const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
16077
16326
  for (const dir of dirs) {
16078
16327
  for (const ext of exts) {
16079
- const candidate = join31(dir, name + ext);
16080
- if (existsSync17(candidate)) return candidate;
16328
+ const candidate = join32(dir, name + ext);
16329
+ if (existsSync18(candidate)) return candidate;
16081
16330
  }
16082
16331
  }
16083
16332
  return null;
@@ -16160,8 +16409,8 @@ var COMPOSE_MARKERS = [
16160
16409
  "compose.yaml"
16161
16410
  ];
16162
16411
  function detectRequirements(repo, io = {}) {
16163
- const exists = io.exists ?? existsSync18;
16164
- const marker = COMPOSE_MARKERS.find((f) => exists(join32(repo, f)));
16412
+ const exists = io.exists ?? existsSync19;
16413
+ const marker = COMPOSE_MARKERS.find((f) => exists(join33(repo, f)));
16165
16414
  return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
16166
16415
  }
16167
16416
  function resolveRequirements(repo, declared, io = {}) {
@@ -17916,10 +18165,10 @@ function closeWebServer(server) {
17916
18165
 
17917
18166
  // src/hostCommands.ts
17918
18167
  init_src();
17919
- import { readFileSync as readFileSync23, renameSync as renameSync10, writeFileSync as writeFileSync15 } from "fs";
17920
- import { join as join33 } from "path";
18168
+ import { readFileSync as readFileSync23, renameSync as renameSync10, writeFileSync as writeFileSync16 } from "fs";
18169
+ import { join as join34 } from "path";
17921
18170
  function hostCommandStatePath(home) {
17922
- return join33(home, "host-command.json");
18171
+ return join34(home, "host-command.json");
17923
18172
  }
17924
18173
  function readState(path) {
17925
18174
  try {
@@ -17930,7 +18179,7 @@ function readState(path) {
17930
18179
  }
17931
18180
  function writeState(path, state) {
17932
18181
  try {
17933
- writeFileSync15(`${path}.tmp`, JSON.stringify(state));
18182
+ writeFileSync16(`${path}.tmp`, JSON.stringify(state));
17934
18183
  renameSync10(`${path}.tmp`, path);
17935
18184
  } catch {
17936
18185
  }
@@ -18093,8 +18342,8 @@ async function doRestart(cmd, opts, note) {
18093
18342
 
18094
18343
  // src/selfUpdate.ts
18095
18344
  init_git();
18096
- import { existsSync as existsSync19 } from "fs";
18097
- import { join as join34 } from "path";
18345
+ import { existsSync as existsSync20 } from "fs";
18346
+ import { join as join35 } from "path";
18098
18347
  import { $ as $5 } from "execa";
18099
18348
  var DAEMON_PACKAGE = "@taskless-app/swarm";
18100
18349
  var defaultRun = async (cmd, args, cwd) => {
@@ -18107,7 +18356,7 @@ function tail(text, max = 400) {
18107
18356
  }
18108
18357
  async function runSelfUpdate(io) {
18109
18358
  const run = io.run ?? defaultRun;
18110
- const exists = io.exists ?? existsSync19;
18359
+ const exists = io.exists ?? existsSync20;
18111
18360
  const repo = io.repo;
18112
18361
  await io.onStep("fetch", "\u0442\u044F\u043D\u0443 \u0441\u0432\u0435\u0436\u0438\u0439 \u043A\u043E\u0434");
18113
18362
  const fetched = await run("git", ["-C", repo, "fetch", "--quiet", "origin"], repo);
@@ -18136,7 +18385,7 @@ async function runSelfUpdate(io) {
18136
18385
  };
18137
18386
  }
18138
18387
  await io.onStep("install", "\u0441\u0442\u0430\u0432\u043B\u044E \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0438");
18139
- if (!exists(join34(repo, "pnpm-lock.yaml"))) {
18388
+ if (!exists(join35(repo, "pnpm-lock.yaml"))) {
18140
18389
  return { ok: false, step: "install", message: "\u0432 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442 pnpm-lock.yaml \u2014 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u0435\u0433\u043E \u044D\u0442\u0438\u043C \u043F\u0443\u0442\u0451\u043C \u043D\u0435 \u0431\u0435\u0440\u0443\u0441\u044C" };
18141
18390
  }
18142
18391
  const installed2 = await run("pnpm", ["install", "--frozen-lockfile"], repo);
@@ -18301,18 +18550,18 @@ async function fetchExternalStorm(url, opts = {}) {
18301
18550
  }
18302
18551
 
18303
18552
  // src/anomalyState.ts
18304
- import { existsSync as existsSync20, readFileSync as readFileSync24, renameSync as renameSync11, writeFileSync as writeFileSync16 } from "fs";
18305
- import { join as join35 } from "path";
18553
+ import { existsSync as existsSync21, readFileSync as readFileSync24, renameSync as renameSync11, writeFileSync as writeFileSync17 } from "fs";
18554
+ import { join as join36 } from "path";
18306
18555
  var KEY_TTL_MS = 7 * 24 * 60 * 6e4;
18307
18556
  var MAX_RESOLVED = 200;
18308
18557
  function emptyState() {
18309
18558
  return { since: {}, lastFiredAt: {}, fires: {}, resolved: [], stopQuiet: null };
18310
18559
  }
18311
18560
  function anomalyStatePath(home) {
18312
- return join35(home, "anomaly-state.json");
18561
+ return join36(home, "anomaly-state.json");
18313
18562
  }
18314
18563
  function loadAnomalyState(home, now = Date.now(), io = {}) {
18315
- const exists = io.exists ?? existsSync20;
18564
+ const exists = io.exists ?? existsSync21;
18316
18565
  const readFile2 = io.readFile ?? ((p) => readFileSync24(p, "utf8"));
18317
18566
  const path = anomalyStatePath(home);
18318
18567
  if (!exists(path)) return emptyState();
@@ -18343,7 +18592,7 @@ function loadAnomalyState(home, now = Date.now(), io = {}) {
18343
18592
  function saveAnomalyState(home, state, io = {}) {
18344
18593
  const path = anomalyStatePath(home);
18345
18594
  const writeFile4 = io.writeFile ?? ((p, data) => {
18346
- writeFileSync16(`${p}.tmp`, data);
18595
+ writeFileSync17(`${p}.tmp`, data);
18347
18596
  renameSync11(`${p}.tmp`, p);
18348
18597
  });
18349
18598
  writeFile4(path, JSON.stringify(state, null, 2));
@@ -18870,8 +19119,16 @@ function worktreeFromCmd(args) {
18870
19119
  const quoted = args.match(/--mcp-config\s+"([^"]+[/\\]\.swarm-mcp\.json)"/);
18871
19120
  const bare = quoted ? null : args.match(/--mcp-config\s+(\S+[/\\]\.swarm-mcp\.json)/);
18872
19121
  const path = quoted?.[1] ?? bare?.[1];
18873
- if (!path) return null;
18874
- return path.replace(/[/\\]\.swarm-mcp\.json$/, "");
19122
+ if (path) return path.replace(/[/\\]\.swarm-mcp\.json$/, "");
19123
+ return worktreeFromGrokCwd(args);
19124
+ }
19125
+ function worktreeFromGrokCwd(args) {
19126
+ const trimmed = args.trim();
19127
+ const first = trimmed.startsWith('"') ? trimmed.slice(1, trimmed.indexOf('"', 1)) : trimmed.split(/\s/)[0] ?? "";
19128
+ if (!/(?:^|[\\/])grok(?:\.exe)?$/i.test(first)) return null;
19129
+ const cwdQuoted = args.match(/--cwd\s+"([^"]+)"/);
19130
+ const cwdBare = cwdQuoted ? null : args.match(/--cwd\s+(\S+)/);
19131
+ return cwdQuoted?.[1] ?? cwdBare?.[1] ?? null;
18875
19132
  }
18876
19133
  function listDroneProcesses() {
18877
19134
  const rows = [];
@@ -19616,7 +19873,7 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
19616
19873
  }
19617
19874
  return parts.join("\n");
19618
19875
  }
19619
- var HERO_CHARTER_PATH = join36(dirname6(THIS_FILE), "..", "docs", "HERO.md");
19876
+ var HERO_CHARTER_PATH = join37(dirname6(THIS_FILE), "..", "docs", "HERO.md");
19620
19877
  function buildHeroPrompt(task, run, taskContext, attachmentsBlock) {
19621
19878
  const name = run.name || `#${run.id}`;
19622
19879
  const charter = readFileSync25(HERO_CHARTER_PATH, "utf8");
@@ -19676,7 +19933,7 @@ function ciTrustLine(prUrl, verifyMode) {
19676
19933
  }
19677
19934
  return `\u041D\u0435 \u043F\u0435\u0440\u0435\u0433\u043E\u043D\u044F\u0439 \u0442\u0435\u0441\u0442-\u0441\u044C\u044E\u0442/typecheck \u0437\u0430\u043D\u043E\u0432\u043E \u2014 \u044D\u0442\u043E \u0443\u0436\u0435 \u0441\u0434\u0435\u043B\u0430\u043B CI \u043D\u0430 \u044D\u0442\u043E\u043C \u0436\u0435 \u043A\u043E\u043C\u043C\u0438\u0442\u0435 (\u0434\u0432\u043E\u0439\u043D\u043E\u0439 \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430 \u043E\u0442\u043C\u0435\u043D\u0451\u043D, TASWM-249). \u041F\u0440\u043E\u0447\u0438\u0442\u0430\u0439 \u0435\u0433\u043E \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u0441\u0430\u043C: ${readChecks}. \u0414\u0430\u043B\u044C\u0448\u0435 \u0442\u0440\u0438 \u0440\u0430\u0437\u043D\u044B\u0445 \u0441\u043B\u0443\u0447\u0430\u044F: \u2460 \u0445\u043E\u0442\u044C \u043E\u0434\u0438\u043D \u0447\u0435\u043A \u0435\u0449\u0451 PENDING \u2014 \u0432\u0435\u0440\u0434\u0438\u043A\u0442 \u0432\u043E\u043E\u0431\u0449\u0435 \u043D\u0435 \u0432\u044B\u043D\u043E\u0441\u0438: \u043D\u0435 \u043F\u043E\u0441\u0442\u0438 REQUEST CHANGES, \u043D\u0435 \u0437\u043E\u0432\u0438 done(), \u043F\u0440\u043E\u0441\u0442\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438 \u0441\u0435\u0441\u0441\u0438\u044E \u043A\u0430\u043A \u0435\u0441\u0442\u044C \u2014 \u0440\u043E\u0439 \u0441\u0430\u043C \u0437\u0430\u043C\u0435\u0442\u0438\u0442, \u0447\u0442\u043E \u0442\u044B \u0436\u0434\u0451\u0448\u044C CI, \u0438 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442 \u0434\u043E\u0434\u0435\u043B\u0430\u0442\u044C \u0440\u0435\u0432\u044C\u044E, \u043A\u043E\u0433\u0434\u0430 \u0432\u0441\u0435 \u0447\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0442\u0441\u044F (\u044D\u0442\u043E \u0448\u0442\u0430\u0442\u043D\u044B\u0439 \u043F\u0443\u0442\u044C, \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0430 \u0437\u0432\u0430\u0442\u044C \u043D\u0435 \u043D\u0443\u0436\u043D\u043E). \u2461 \u0432\u0441\u0435 \u0447\u0435\u043A\u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0438\u0441\u044C \u0438 \u0435\u0441\u0442\u044C FAIL \u2014 REQUEST CHANGES, \u043D\u0430\u0437\u043E\u0432\u0438 \u043F\u043E\u0438\u043C\u0451\u043D\u043D\u043E, \u0447\u0442\u043E \u0443\u043F\u0430\u043B\u043E. \u2462 \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043C\u0438\u0442\u0435 \u0447\u0435\u043A\u043E\u0432 \u043D\u0435\u0442 \u0432\u043E\u0432\u0441\u0435 \u2014 \u0442\u043E\u0436\u0435 REQUEST CHANGES: \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u043C (\u0443 \u044D\u0442\u043E\u0433\u043E \u043F\u0440\u043E\u0435\u043A\u0442\u0430 verifyMode=ci, \u0442\u043E \u0435\u0441\u0442\u044C CI \u043E\u0431\u044F\u0437\u0430\u043D \u0431\u044B\u0442\u044C). ${dontTrustAuthor}`;
19678
19935
  }
19679
- var REVIEW_SKILL_PATH = join36(bundledRoleDir("drone"), "skills", "taskless-review", "SKILL.md");
19936
+ var REVIEW_SKILL_PATH = join37(bundledRoleDir("drone"), "skills", "taskless-review", "SKILL.md");
19680
19937
  function reviewDefectClasses(skillPath = REVIEW_SKILL_PATH) {
19681
19938
  const text = readFileSync25(skillPath, "utf8");
19682
19939
  const m = /^## Классы дефектов\n([\s\S]*?)(?=^## |(?![\s\S]))/m.exec(text);
@@ -19942,7 +20199,7 @@ async function wakeRun(deps, runId, opts = {}) {
19942
20199
  if (run.status === "review" && run.pr_url && !needsFixIteration(run) && !stoppedForHuman) {
19943
20200
  return { ok: false, why: `\u043F\u0440\u043E\u0433\u043E\u043D #${runId} \u0443\u0436\u0435 \u0441\u0434\u0430\u043D \u043D\u0430 \u0440\u0435\u0432\u044C\u044E (${run.pr_url}) \u2014 \u0431\u0443\u0434\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E; \u043F\u0440\u0430\u0432\u043A\u0438 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442 \u0440\u043E\u0439 \u043F\u043E \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u0443` };
19944
20201
  }
19945
- const worktreeExists = deps.worktreeExists ?? existsSync21;
20202
+ const worktreeExists = deps.worktreeExists ?? existsSync22;
19946
20203
  const hasTranscript = deps.hasTranscript ?? executorHasSession;
19947
20204
  const wt = run.worktree_path;
19948
20205
  const canContinue = !!wt && worktreeExists(wt) && hasTranscript(run);
@@ -20067,9 +20324,9 @@ function repoHasCi(deps, run) {
20067
20324
  function repoHasCiWorkflows(worktreePath, repo) {
20068
20325
  for (const base3 of [worktreePath, repo]) {
20069
20326
  if (!base3) continue;
20070
- const dir = join36(base3, ".github", "workflows");
20327
+ const dir = join37(base3, ".github", "workflows");
20071
20328
  try {
20072
- if (readdirSync14(dir).some((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) return true;
20329
+ if (readdirSync15(dir).some((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) return true;
20073
20330
  } catch {
20074
20331
  }
20075
20332
  }
@@ -20268,7 +20525,7 @@ async function wakeReviewerOnCiReady(deps, run, over) {
20268
20525
  return true;
20269
20526
  }
20270
20527
  function worktreeAndTranscriptAlive(deps, run) {
20271
- const exists = deps.worktreeExists ?? existsSync21;
20528
+ const exists = deps.worktreeExists ?? existsSync22;
20272
20529
  const transcript = deps.hasTranscript ?? executorHasSession;
20273
20530
  const wt = run.worktree_path;
20274
20531
  return !!wt && exists(wt) && transcript(run);
@@ -20368,7 +20625,7 @@ async function wakeAuthorOnChanges(deps, snapshot, now, droneCap) {
20368
20625
  }
20369
20626
  const wt = run.worktree_path;
20370
20627
  const wtPath = wt || (deps.worktreesDir ? droneWorktreePath(deps.worktreesDir, normalizeRepo(run.repo || deps.repoFallback || ""), run.task_slug) : null);
20371
- const worktreeReady = !!wtPath && (deps.worktreeExists ?? existsSync21)(wtPath);
20628
+ const worktreeReady = !!wtPath && (deps.worktreeExists ?? existsSync22)(wtPath);
20372
20629
  if (!worktreeReady && !await freshChangesWakeAllowed(deps, run, prUrl, now)) return false;
20373
20630
  try {
20374
20631
  await deps.respawn(run, "fix");
@@ -20451,7 +20708,7 @@ async function zoneConflictFor(db, models, activeFiles, run, repo, repoFallback)
20451
20708
  activeFiles.prune(holders.map((r) => r.id));
20452
20709
  const fileExists = (p) => {
20453
20710
  try {
20454
- return statSync13(join36(repo, p), { throwIfNoEntry: false })?.isFile() ?? false;
20711
+ return statSync14(join37(repo, p), { throwIfNoEntry: false })?.isFile() ?? false;
20455
20712
  } catch {
20456
20713
  return false;
20457
20714
  }
@@ -20687,6 +20944,8 @@ async function trySpawnQueuedRun(deps, run) {
20687
20944
  if (!await projectQuotaGateAllows(deps, run)) return "skipped";
20688
20945
  const prodOverride = await prodOverrideReason(deps, run, ["queued"]);
20689
20946
  if (prodOverride) return deferSpawn(deps, run, prodOverride, { event: false });
20947
+ const providerGap = deps.providerGap?.(run) ?? null;
20948
+ if (providerGap) return deferSpawn(deps, run, providerGap);
20690
20949
  if (deps.db.takeRun && !await deps.db.takeRun(run.id)) {
20691
20950
  return deferSpawn(deps, run, "\u043D\u0435 \u0432\u0437\u044F\u043B \u043F\u0440\u043E\u0433\u043E\u043D \u043D\u0430 \u0441\u0432\u043E\u044E \u043C\u0430\u0448\u0438\u043D\u0443 \u2014 \u0441\u0435\u0440\u0432\u0435\u0440 \u043E\u0442\u0434\u0430\u043B \u0435\u0433\u043E \u0434\u0440\u0443\u0433\u043E\u043C\u0443 \u0434\u0435\u043C\u043E\u043D\u0443 (TASWM-281)");
20692
20951
  }
@@ -20843,7 +21102,7 @@ async function liftReturners(deps, returners, now, droneCap) {
20843
21102
  deps.returners?.delete(run.id);
20844
21103
  continue;
20845
21104
  }
20846
- lifted = await wakeAuthorOnRedCi(deps, run, now, entry.ci, deps.worktreeExists ?? existsSync21, deps.hasTranscript ?? executorHasSession, droneCap);
21105
+ lifted = await wakeAuthorOnRedCi(deps, run, now, entry.ci, deps.worktreeExists ?? existsSync22, deps.hasTranscript ?? executorHasSession, droneCap);
20847
21106
  }
20848
21107
  if (lifted) deps.returners?.delete(run.id);
20849
21108
  }
@@ -20862,7 +21121,7 @@ async function resumeWatchdogParked(deps, droneCap) {
20862
21121
  }
20863
21122
  if (!candidate) return;
20864
21123
  if (candidate.pid != null && deps.isAlive(candidate.pid)) return;
20865
- const worktreeExists = deps.worktreeExists ?? existsSync21;
21124
+ const worktreeExists = deps.worktreeExists ?? existsSync22;
20866
21125
  const hasTranscript = deps.hasTranscript ?? executorHasSession;
20867
21126
  const wt = candidate.worktree_path;
20868
21127
  const canContinue = !!wt && worktreeExists(wt) && hasTranscript(candidate);
@@ -21649,7 +21908,7 @@ function buildEscalationCandidates(db) {
21649
21908
  }
21650
21909
  async function tickOnce(deps) {
21651
21910
  const now = deps.now ? deps.now() : Date.now();
21652
- const worktreeExists = deps.worktreeExists ?? existsSync21;
21911
+ const worktreeExists = deps.worktreeExists ?? existsSync22;
21653
21912
  const hasTranscript = deps.hasTranscript ?? executorHasSession;
21654
21913
  deps.sampleHost?.();
21655
21914
  if (deps.rehydrate) {
@@ -22550,7 +22809,7 @@ async function resolveConflict(deps, reviewRun) {
22550
22809
  model: reviewRun.model,
22551
22810
  review_model: reviewRun.review_model
22552
22811
  });
22553
- const wt = join36(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
22812
+ const wt = join37(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
22554
22813
  try {
22555
22814
  await deps.git.prepareDetachedWorktree(repo, branch, wt);
22556
22815
  await deps.git.setWorktreeIdentity(wt, resolver.name);
@@ -22630,7 +22889,7 @@ async function spawnReviewer(deps, reviewRun, files) {
22630
22889
  // (своей у ревьюера нет, TASWM-204), и коммент с вердиктом должен уехать именно туда.
22631
22890
  workspace_id: reviewRun.workspace_id
22632
22891
  });
22633
- const wt = join36(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
22892
+ const wt = join37(deps.worktreesDir, basename6(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
22634
22893
  try {
22635
22894
  await deps.git.prepareDetachedWorktree(repo, branch, wt);
22636
22895
  await deps.git.setWorktreeIdentity(wt, reviewer.name);
@@ -22711,7 +22970,7 @@ async function spawnVerifier(deps, verifiedRun, files) {
22711
22970
  // штамп цикла: фикс-итерацию проверяет свежий верификатор
22712
22971
  workspace_id: verifiedRun.workspace_id
22713
22972
  });
22714
- const wt = join36(deps.worktreesDir, basename6(repo), `${verifiedRun.task_slug}-verify-${verifier.id}`);
22973
+ const wt = join37(deps.worktreesDir, basename6(repo), `${verifiedRun.task_slug}-verify-${verifier.id}`);
22715
22974
  try {
22716
22975
  await deps.git.prepareDetachedWorktree(repo, branch, wt);
22717
22976
  await deps.git.setWorktreeIdentity(wt, verifier.name);
@@ -22759,7 +23018,7 @@ async function respawnRun(deps, run, mode, nudge) {
22759
23018
  ]);
22760
23019
  const base3 = effectiveBaseBranch(run.base_branch, projectBase);
22761
23020
  const runToSpawn = base3 && run.base_branch !== base3 ? deps.db.updateRun(run.id, { base_branch: base3 }) ?? run : run;
22762
- if (!existsSync21(wt)) {
23021
+ if (!existsSync22(wt)) {
22763
23022
  const branch = runToSpawn.branch || runToSpawn.task_slug;
22764
23023
  const source = await addWorktree(repo, wt, branch, {
22765
23024
  ...base3 ? { base: await remoteBaseRef(repo, base3) } : {},
@@ -22800,7 +23059,7 @@ function worktreePathPatch(run, wt) {
22800
23059
  return run.worktree_path === wt ? {} : { worktree_path: wt };
22801
23060
  }
22802
23061
  async function syncCrashedOnStartup(deps) {
22803
- const worktreeExists = deps.worktreeExists ?? existsSync21;
23062
+ const worktreeExists = deps.worktreeExists ?? existsSync22;
22804
23063
  let orphaned = 0;
22805
23064
  let merged = 0;
22806
23065
  let requeued = 0;
@@ -22954,10 +23213,10 @@ async function migrateWorktreesAtStartup() {
22954
23213
  legacyDir: WORKTREES_LEGACY_DIR,
22955
23214
  targetDir: WORKTREES_DIR,
22956
23215
  envOverride: process.env.SWARM_WORKTREES || "",
22957
- exists: (d) => existsSync21(d),
23216
+ exists: (d) => existsSync22(d),
22958
23217
  isNonEmptyDir: (d) => {
22959
23218
  try {
22960
- return readdirSync14(d).length > 0;
23219
+ return readdirSync15(d).length > 0;
22961
23220
  } catch {
22962
23221
  return false;
22963
23222
  }
@@ -22969,7 +23228,7 @@ async function migrateWorktreesAtStartup() {
22969
23228
  });
22970
23229
  } catch (e) {
22971
23230
  console.error("[migrate] \u043C\u0438\u0433\u0440\u0430\u0446\u0438\u044F worktrees \u0441\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C \u2014 \u0444\u043E\u043B\u0431\u044D\u043A:", e);
22972
- return existsSync21(WORKTREES_DIR) ? WORKTREES_DIR : WORKTREES_LEGACY_DIR;
23231
+ return existsSync22(WORKTREES_DIR) ? WORKTREES_DIR : WORKTREES_LEGACY_DIR;
22973
23232
  }
22974
23233
  }
22975
23234
  async function reportWorkspaceAccess(tl, db) {
@@ -23153,7 +23412,8 @@ async function runDaemon(opts) {
23153
23412
  // настенным временем. Не «демон запущен», а «цикл прокрутился».
23154
23413
  tickPulse(tickHealth, Date.now(), tickIntervalMs),
23155
23414
  // TASWM-592: установленные агенты машины — справочник для парка машин.
23156
- cachedInstalledExecutorIds()
23415
+ cachedInstalledExecutorIds(),
23416
+ availableProviders(cachedInstalledExecutorIds())
23157
23417
  );
23158
23418
  if (reply.maxDrones !== void 0) operatorMaxDrones = reply.maxDrones;
23159
23419
  if (reply.commands.length) onHostCommands(reply.commands);
@@ -23594,6 +23854,7 @@ async function runDaemon(opts) {
23594
23854
  // TASWM-364: swarmModels — ТОТ ЖЕ снимок, что уехал в launcher: цена токенов считается
23595
23855
  // по модели, на которой прогон реально работал. TASWM-592: исполнитель НЕ пинится —
23596
23856
  // аккаунтант сам резолвит его по прогону (итог писал харнесс ЭТОГО прогона).
23857
+ providerGap: (run) => providerUnavailableReason(resolveRunModelRef(run, lastSwarmModels).providerId, availableProviders(cachedInstalledExecutorIds())),
23597
23858
  accountCost: makeCostAccountant({ runLogsDir: RUN_LOGS_DIR, db, swarmModels: () => lastSwarmModels, modelRefFor: (run) => runModelRefPin.get(run.id) ?? null }),
23598
23859
  // TASWM-413: тот же аккаунтант, но поводом служит завершённый ЗАПУСК, а не смерть
23599
23860
  // прогона в running — иначе сданный через done() прогон не получает цену никогда.
@@ -23659,7 +23920,7 @@ async function runDaemon(opts) {
23659
23920
  isDirty: (p) => worktreeDirty(p),
23660
23921
  releaseWorktree: (repo, p) => releaseWorktree(repo || opts.repo, p),
23661
23922
  pruneWorktrees: (repo) => pruneWorktrees(repo || opts.repo),
23662
- worktreeExists: existsSync21,
23923
+ worktreeExists: existsSync22,
23663
23924
  liveWorktrees: () => new Set(listDroneProcesses().map((p) => p.worktree)),
23664
23925
  narrate: narrator.narrate
23665
23926
  });
@@ -23794,7 +24055,7 @@ async function runDaemon(opts) {
23794
24055
  mode,
23795
24056
  nudge
23796
24057
  ),
23797
- worktreeExists: (p) => existsSync21(p),
24058
+ worktreeExists: (p) => existsSync22(p),
23798
24059
  // TASWM-514: с этого момента считаем попытки спавна — счётчик живёт ровно одну жизнь
23799
24060
  // процесса демона и рестарт не переживает (см. preStartAttempts).
23800
24061
  startedAt: Date.now(),