@taskless-app/swarm 0.6.62 → 0.6.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +465 -206
- package/dist/overmind.js +2118 -1803
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -388,8 +388,8 @@ function computeBlocker(status, stopReason, lastStep, pr, ci, deps, openQ) {
|
|
|
388
388
|
}
|
|
389
389
|
}
|
|
390
390
|
function pickHeadline(lanes) {
|
|
391
|
-
const
|
|
392
|
-
const by = (pred) =>
|
|
391
|
+
const live2 = lanes.filter((l2) => l2.live && !l2.obsolete);
|
|
392
|
+
const by = (pred) => live2.find(pred);
|
|
393
393
|
const fmt = (l2, extra) => `${l2.name} \xB7 ${l2.slug} \u2014 ${extra}`;
|
|
394
394
|
let l = by((x) => x.status === "review" && x.ci?.tone === "fail");
|
|
395
395
|
if (l) return { icon: "\u274C", text: fmt(l, "CI \u0443\u043F\u0430\u043B \u2014 \u0447\u0438\u043D\u0438\u0442\u044C"), tone: "fail" };
|
|
@@ -407,7 +407,7 @@ function pickHeadline(lanes) {
|
|
|
407
407
|
l = by((x) => x.status === "review");
|
|
408
408
|
if (l) return { icon: "\u23F3", text: fmt(l, "review \u2014 CI \u0438\u0434\u0451\u0442"), tone: "review" };
|
|
409
409
|
l = by((x) => x.status === "running");
|
|
410
|
-
if (l) return { icon: "\u25B6", text: `${
|
|
410
|
+
if (l) return { icon: "\u25B6", text: `${live2.filter((x) => x.status === "running").length} \u0434\u0440\u043E\u043D\u0430 \u0432 \u0440\u0430\u0431\u043E\u0442\u0435 \u2014 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043E\u043A \u043D\u0435\u0442`, tone: "run" };
|
|
411
411
|
return null;
|
|
412
412
|
}
|
|
413
413
|
function computeCounters(lanes) {
|
|
@@ -607,10 +607,10 @@ function pick(rows) {
|
|
|
607
607
|
return newest(rows.filter((r) => r.author)) ?? newest(rows);
|
|
608
608
|
}
|
|
609
609
|
function pickRunForPr(rows, ref, ctxRepo) {
|
|
610
|
-
const
|
|
611
|
-
if (
|
|
610
|
+
const num2 = prNumberOf(ref);
|
|
611
|
+
if (num2 == null) return null;
|
|
612
612
|
const target = repoKeyOf(ref) ?? repoKeyOf(ctxRepo);
|
|
613
|
-
const hits = rows.filter((r) => r.prUrl && prNumberOf(r.prUrl) ===
|
|
613
|
+
const hits = rows.filter((r) => r.prUrl && prNumberOf(r.prUrl) === num2);
|
|
614
614
|
if (hits.length === 0) return null;
|
|
615
615
|
if (target) {
|
|
616
616
|
const mine = hits.filter((r) => keyOf(r) === target);
|
|
@@ -650,6 +650,9 @@ var init_returnReason = __esm({
|
|
|
650
650
|
});
|
|
651
651
|
|
|
652
652
|
// ../swarm-core/src/modelPolicy.ts
|
|
653
|
+
function isHarnessDefaultModel(model) {
|
|
654
|
+
return typeof model === "string" && model.trim().toLowerCase() === HARNESS_DEFAULT_MODEL;
|
|
655
|
+
}
|
|
653
656
|
function findProvider(id) {
|
|
654
657
|
const key = typeof id === "string" ? id.trim().toLowerCase() : "";
|
|
655
658
|
return MODEL_PROVIDERS.find((p) => p.id === key) ?? null;
|
|
@@ -701,6 +704,19 @@ var init_modelPolicy = __esm({
|
|
|
701
704
|
selectable: true,
|
|
702
705
|
roles: { executor: "glm-5.3", review: "glm-5.3" }
|
|
703
706
|
},
|
|
707
|
+
// Grok CLI. Каталог — живой `grok models` (16.10.2026), целиком и без выдуманных
|
|
708
|
+
// имён: grok-4.7 (дефолт CLI и исполнителя), grok-4.7-build-fast, grok-4.6, grok-4.5
|
|
709
|
+
// TASWM-605: обе роли — на свежей модели (решение владельца: рой работает на свежих
|
|
710
|
+
// моделях). Это только фолбэк: демон берёт дефолт живого `grok models` (liveModels.ts).
|
|
711
|
+
// Тарифа нет: цену пишет CLI.
|
|
712
|
+
{
|
|
713
|
+
id: "xai",
|
|
714
|
+
label: "xAI (Grok)",
|
|
715
|
+
executorId: "grok",
|
|
716
|
+
selectable: true,
|
|
717
|
+
roles: { executor: "grok-4.7", review: "grok-4.7" },
|
|
718
|
+
models: ["grok-4.7", "grok-4.7-build-fast", "grok-4.6", "grok-4.5"]
|
|
719
|
+
},
|
|
704
720
|
// TASWM-565 · шаг 1 из четырёх: провайдер в реестре ЕСТЬ, выбрать его ещё нельзя.
|
|
705
721
|
// Имён моделей здесь нет СОЗНАТЕЛЬНО: у владельца подписка, состав моделей в ней нам
|
|
706
722
|
// неизвестен, а выдуманное имя хуже маркера «решает Codex».
|
|
@@ -1089,18 +1105,18 @@ var init_hosts = __esm({
|
|
|
1089
1105
|
function parseHostResources(raw) {
|
|
1090
1106
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
1091
1107
|
const v = raw;
|
|
1092
|
-
const
|
|
1093
|
-
const memFreeGb =
|
|
1094
|
-
const memTotalGb =
|
|
1108
|
+
const num2 = (x) => typeof x === "number" && Number.isFinite(x) ? x : null;
|
|
1109
|
+
const memFreeGb = num2(v.memFreeGb);
|
|
1110
|
+
const memTotalGb = num2(v.memTotalGb);
|
|
1095
1111
|
if (memFreeGb == null || memTotalGb == null) return null;
|
|
1096
1112
|
if (typeof v.measuredAt !== "string" || Number.isNaN(Date.parse(v.measuredAt))) return null;
|
|
1097
|
-
const cpu =
|
|
1113
|
+
const cpu = num2(v.cpuBusyPct);
|
|
1098
1114
|
return {
|
|
1099
1115
|
cpuBusyPct: cpu == null ? null : Math.min(100, Math.max(0, cpu)),
|
|
1100
1116
|
memFreeGb,
|
|
1101
1117
|
memTotalGb,
|
|
1102
|
-
diskFreeGb:
|
|
1103
|
-
diskTotalGb:
|
|
1118
|
+
diskFreeGb: num2(v.diskFreeGb),
|
|
1119
|
+
diskTotalGb: num2(v.diskTotalGb),
|
|
1104
1120
|
measuredAt: v.measuredAt
|
|
1105
1121
|
};
|
|
1106
1122
|
}
|
|
@@ -1137,6 +1153,7 @@ var init_hostPool = __esm({
|
|
|
1137
1153
|
"../swarm-core/src/hostPool.ts"() {
|
|
1138
1154
|
"use strict";
|
|
1139
1155
|
init_hosts();
|
|
1156
|
+
init_modelPolicy();
|
|
1140
1157
|
HOST_REJECTION_TEXT = {
|
|
1141
1158
|
offline: "\u043D\u0435 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438",
|
|
1142
1159
|
tick_stalled: `\u0446\u0438\u043A\u043B \u0440\u0435\u043A\u043E\u043D\u0441\u0430\u0439\u043B\u0430 \u0432\u0441\u0442\u0430\u043B \u2014 \u043F\u0443\u043B\u044C\u0441 \u0438\u0434\u0451\u0442, \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442; ${HOST_TICK_STALLED_ACTION}`,
|
|
@@ -2109,10 +2126,10 @@ function launchCostUsd(result, model) {
|
|
|
2109
2126
|
return costFromUsage(result.usage, model);
|
|
2110
2127
|
}
|
|
2111
2128
|
function launchSpend(result) {
|
|
2112
|
-
const
|
|
2129
|
+
const num2 = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
|
|
2113
2130
|
const sum = (u) => ({
|
|
2114
|
-
tokensIn:
|
|
2115
|
-
tokensOut:
|
|
2131
|
+
tokensIn: num2(u.input_tokens) + num2(u.cache_creation_input_tokens) + num2(u.cache_read_input_tokens),
|
|
2132
|
+
tokensOut: num2(u.output_tokens)
|
|
2116
2133
|
});
|
|
2117
2134
|
const spend = sum(result.usage ?? {});
|
|
2118
2135
|
if (spend.tokensIn === 0 && spend.tokensOut === 0 && result.modelUsage) {
|
|
@@ -2122,7 +2139,7 @@ function launchSpend(result) {
|
|
|
2122
2139
|
spend.tokensOut += perModel.tokensOut;
|
|
2123
2140
|
}
|
|
2124
2141
|
}
|
|
2125
|
-
return { ...spend, durationMs:
|
|
2142
|
+
return { ...spend, durationMs: num2(result.duration_ms) };
|
|
2126
2143
|
}
|
|
2127
2144
|
function asTokenUsage(m) {
|
|
2128
2145
|
return {
|
|
@@ -2214,9 +2231,9 @@ function transcriptSpendInWindow(worktree, fromMs, toMs, opts = {}) {
|
|
|
2214
2231
|
unpriced += 1;
|
|
2215
2232
|
unpricedModel ??= model;
|
|
2216
2233
|
}
|
|
2217
|
-
const
|
|
2218
|
-
tokensIn +=
|
|
2219
|
-
tokensOut +=
|
|
2234
|
+
const num2 = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0;
|
|
2235
|
+
tokensIn += num2(usage2.input_tokens) + num2(usage2.cache_creation_input_tokens) + num2(usage2.cache_read_input_tokens);
|
|
2236
|
+
tokensOut += num2(usage2.output_tokens);
|
|
2220
2237
|
if (firstAt === null || at < firstAt) firstAt = at;
|
|
2221
2238
|
if (lastAt === null || at > lastAt) lastAt = at;
|
|
2222
2239
|
replies += 1;
|
|
@@ -2444,13 +2461,15 @@ var init_swarmHome = __esm({
|
|
|
2444
2461
|
});
|
|
2445
2462
|
|
|
2446
2463
|
// src/executors/codex.ts
|
|
2447
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync4, statSync as statSync3, symlinkSync } from "fs";
|
|
2464
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync4, statSync as statSync3, symlinkSync } from "fs";
|
|
2448
2465
|
import { join as join5 } from "path";
|
|
2449
2466
|
function resolveCodexBin(env = process.env) {
|
|
2450
2467
|
return (env.SWARM_CODEX_BIN ?? "").trim() || "codex";
|
|
2451
2468
|
}
|
|
2452
2469
|
function codexHomePath(worktree, swarmHome = SWARM_HOME) {
|
|
2453
|
-
|
|
2470
|
+
const folded = worktree.replace(/[/.]/g, "-");
|
|
2471
|
+
const key = /[:\\]/.test(folded) ? worktree.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "") || "worktree" : folded;
|
|
2472
|
+
return join5(swarmHome, "executor-state", "codex", key);
|
|
2454
2473
|
}
|
|
2455
2474
|
function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWARM_HOME) {
|
|
2456
2475
|
const home = codexHomePath(worktree, swarmHome);
|
|
@@ -2463,6 +2482,10 @@ function ensureCodexHome(worktree, realHome, env = process.env, swarmHome = SWAR
|
|
|
2463
2482
|
try {
|
|
2464
2483
|
symlinkSync(target, link);
|
|
2465
2484
|
} catch {
|
|
2485
|
+
try {
|
|
2486
|
+
copyFileSync2(target, link);
|
|
2487
|
+
} catch {
|
|
2488
|
+
}
|
|
2466
2489
|
}
|
|
2467
2490
|
}
|
|
2468
2491
|
return home;
|
|
@@ -2613,7 +2636,224 @@ ${notice}` : spec.prompt;
|
|
|
2613
2636
|
}
|
|
2614
2637
|
});
|
|
2615
2638
|
|
|
2639
|
+
// src/executors/grok.ts
|
|
2640
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync4, readdirSync as readdirSync5, statSync as statSync4, symlinkSync as symlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
2641
|
+
import { join as join6 } from "path";
|
|
2642
|
+
function resolveGrokBin(env = process.env) {
|
|
2643
|
+
return (env.SWARM_GROK_BIN ?? "").trim() || "grok";
|
|
2644
|
+
}
|
|
2645
|
+
function grokHomePath(worktree, swarmHome = SWARM_HOME) {
|
|
2646
|
+
const key = worktree.replace(/[^A-Za-z0-9]+/g, "-").replace(/^-|-$/g, "") || "worktree";
|
|
2647
|
+
return join6(swarmHome, "executor-state", "grok", key);
|
|
2648
|
+
}
|
|
2649
|
+
function grokSessionDir(worktree, swarmHome = SWARM_HOME) {
|
|
2650
|
+
return join6(grokHomePath(worktree, swarmHome), "sessions", encodeURIComponent(worktree));
|
|
2651
|
+
}
|
|
2652
|
+
function tomlBasic(value) {
|
|
2653
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
2654
|
+
return `"${escaped}"`;
|
|
2655
|
+
}
|
|
2656
|
+
function grokMcpToml(spec) {
|
|
2657
|
+
const env = Object.entries(spec.mcp.env).map(([k, v]) => `${k} = ${tomlBasic(v)}`).join(", ");
|
|
2658
|
+
const args = spec.mcp.args.map((a) => tomlBasic(a)).join(", ");
|
|
2659
|
+
return [
|
|
2660
|
+
"[mcp_servers." + spec.mcp.name + "]",
|
|
2661
|
+
"command = " + tomlBasic(spec.mcp.command),
|
|
2662
|
+
"args = [" + args + "]",
|
|
2663
|
+
"env = { " + env + " }",
|
|
2664
|
+
""
|
|
2665
|
+
].join("\n");
|
|
2666
|
+
}
|
|
2667
|
+
function ensureGrokHome(worktree, realHome, env = process.env, swarmHome = SWARM_HOME) {
|
|
2668
|
+
const home = grokHomePath(worktree, swarmHome);
|
|
2669
|
+
mkdirSync4(home, { recursive: true });
|
|
2670
|
+
const real = (env.GROK_HOME ?? "").trim() || join6(realHome, ".grok");
|
|
2671
|
+
const target = join6(real, "auth.json");
|
|
2672
|
+
const link = join6(home, "auth.json");
|
|
2673
|
+
if (existsSync4(target) && !existsSync4(link)) {
|
|
2674
|
+
try {
|
|
2675
|
+
symlinkSync2(target, link);
|
|
2676
|
+
} catch {
|
|
2677
|
+
try {
|
|
2678
|
+
copyFileSync3(target, link);
|
|
2679
|
+
} catch {
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
return home;
|
|
2684
|
+
}
|
|
2685
|
+
function modelArgs(model) {
|
|
2686
|
+
if (!model || isHarnessDefaultModel(model)) return [];
|
|
2687
|
+
return ["--model", model];
|
|
2688
|
+
}
|
|
2689
|
+
function denyArgs(paths) {
|
|
2690
|
+
return paths.flatMap((p) => ["--deny", `Read(${p}) Edit(${p}) Write(${p})`]);
|
|
2691
|
+
}
|
|
2692
|
+
function newestSessionMtime(dir, depth = 3) {
|
|
2693
|
+
let entries;
|
|
2694
|
+
try {
|
|
2695
|
+
entries = readdirSync5(dir, { withFileTypes: true });
|
|
2696
|
+
} catch {
|
|
2697
|
+
return null;
|
|
2698
|
+
}
|
|
2699
|
+
let latest = null;
|
|
2700
|
+
for (const entry of entries) {
|
|
2701
|
+
const full = join6(dir, entry.name);
|
|
2702
|
+
try {
|
|
2703
|
+
if (entry.isDirectory()) {
|
|
2704
|
+
if (depth <= 0) continue;
|
|
2705
|
+
const nested = newestSessionMtime(full, depth - 1);
|
|
2706
|
+
if (nested != null && (latest == null || nested > latest)) latest = nested;
|
|
2707
|
+
} else if (entry.isFile()) {
|
|
2708
|
+
const mtime2 = statSync4(full).mtimeMs;
|
|
2709
|
+
if (latest == null || mtime2 > latest) latest = mtime2;
|
|
2710
|
+
}
|
|
2711
|
+
} catch {
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
return latest;
|
|
2715
|
+
}
|
|
2716
|
+
function num(v) {
|
|
2717
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
2718
|
+
}
|
|
2719
|
+
function grokAbort(rec) {
|
|
2720
|
+
if (rec.type === "error") {
|
|
2721
|
+
const raw = typeof rec.message === "string" && rec.message ? rec.message : "error";
|
|
2722
|
+
const kind = /tool/i.test(raw) ? "tools" : /401|unauthorized|network|fetch|ECONN|api/i.test(raw) ? "link" : "other";
|
|
2723
|
+
return { kind, raw: raw.slice(0, 300) };
|
|
2724
|
+
}
|
|
2725
|
+
const stop = typeof rec.stopReason === "string" ? rec.stopReason : "";
|
|
2726
|
+
if (rec.is_error === true || stop === "error" || stop.startsWith("error_")) {
|
|
2727
|
+
const raw = stop || "is_error";
|
|
2728
|
+
return { kind: /tool/i.test(raw) ? "tools" : "other", raw };
|
|
2729
|
+
}
|
|
2730
|
+
return void 0;
|
|
2731
|
+
}
|
|
2732
|
+
function parseGrokResult(raw) {
|
|
2733
|
+
const trimmed = raw.trim();
|
|
2734
|
+
if (!trimmed) return null;
|
|
2735
|
+
let parsed;
|
|
2736
|
+
try {
|
|
2737
|
+
parsed = JSON.parse(trimmed);
|
|
2738
|
+
} catch {
|
|
2739
|
+
return null;
|
|
2740
|
+
}
|
|
2741
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
2742
|
+
const rec = parsed;
|
|
2743
|
+
const hasShape = typeof rec.text === "string" || typeof rec.stopReason === "string" || typeof rec.sessionId === "string" || rec.type === "error";
|
|
2744
|
+
if (!hasShape) return null;
|
|
2745
|
+
const reported = typeof rec.total_cost_usd === "number" && Number.isFinite(rec.total_cost_usd);
|
|
2746
|
+
const usage2 = rec.usage;
|
|
2747
|
+
let tokensIn;
|
|
2748
|
+
let tokensOut;
|
|
2749
|
+
if (typeof usage2 === "object" && usage2 !== null) {
|
|
2750
|
+
const u = usage2;
|
|
2751
|
+
const hasAny = ["input_tokens", "output_tokens", "cache_read_input_tokens", "cache_creation_input_tokens"].some(
|
|
2752
|
+
(k) => typeof u[k] === "number"
|
|
2753
|
+
);
|
|
2754
|
+
if (hasAny) {
|
|
2755
|
+
tokensIn = num(u.input_tokens) + num(u.cache_read_input_tokens) + num(u.cache_creation_input_tokens);
|
|
2756
|
+
tokensOut = num(u.output_tokens);
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
const aborted = grokAbort(rec);
|
|
2760
|
+
const result = {
|
|
2761
|
+
costUsd: reported ? rec.total_cost_usd : 0
|
|
2762
|
+
};
|
|
2763
|
+
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)";
|
|
2764
|
+
if (tokensIn != null && tokensOut != null) {
|
|
2765
|
+
result.tokensIn = tokensIn;
|
|
2766
|
+
result.tokensOut = tokensOut;
|
|
2767
|
+
}
|
|
2768
|
+
if (aborted) result.aborted = aborted;
|
|
2769
|
+
return result;
|
|
2770
|
+
}
|
|
2771
|
+
var grokExecutor;
|
|
2772
|
+
var init_grok = __esm({
|
|
2773
|
+
"src/executors/grok.ts"() {
|
|
2774
|
+
"use strict";
|
|
2775
|
+
init_src();
|
|
2776
|
+
init_swarmHome();
|
|
2777
|
+
init_swarmFiles();
|
|
2778
|
+
init_codex();
|
|
2779
|
+
grokExecutor = {
|
|
2780
|
+
id: "grok",
|
|
2781
|
+
title: "Grok CLI (grok)",
|
|
2782
|
+
binEnv: "SWARM_GROK_BIN",
|
|
2783
|
+
install: {
|
|
2784
|
+
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.",
|
|
2785
|
+
pkg: "grok",
|
|
2786
|
+
command: "curl -fsSL https://x.ai/cli/install.sh | bash",
|
|
2787
|
+
windowsCommand: "irm https://x.ai/cli/install.ps1 | iex"
|
|
2788
|
+
},
|
|
2789
|
+
resolveBin: (env) => resolveGrokBin(env ?? process.env),
|
|
2790
|
+
plan: (spec) => {
|
|
2791
|
+
const home = ensureGrokHome(spec.worktree, spec.realHome);
|
|
2792
|
+
writeFileSync3(join6(home, "config.toml"), grokMcpToml(spec));
|
|
2793
|
+
writeFileSync3(
|
|
2794
|
+
join6(spec.worktree, SWARM_MCP_CONFIG_FILE),
|
|
2795
|
+
JSON.stringify(
|
|
2796
|
+
{ mcpServers: { [spec.mcp.name]: { command: spec.mcp.command, args: spec.mcp.args, env: spec.mcp.env } } },
|
|
2797
|
+
null,
|
|
2798
|
+
2
|
|
2799
|
+
)
|
|
2800
|
+
);
|
|
2801
|
+
const notice = skillsNotice(spec.skillDirs);
|
|
2802
|
+
const prompt = notice ? `${spec.prompt}
|
|
2803
|
+
|
|
2804
|
+
${notice}` : spec.prompt;
|
|
2805
|
+
const args = [
|
|
2806
|
+
// `--cwd` стоит первым: по нему реконсайл узнаёт worktree живого процесса.
|
|
2807
|
+
// Флаг настоящий. Выдуманный `--mcp-config` grok отверг бы и дрон не стартовал.
|
|
2808
|
+
"--cwd",
|
|
2809
|
+
spec.worktree,
|
|
2810
|
+
"--no-auto-update",
|
|
2811
|
+
"--output-format",
|
|
2812
|
+
"json",
|
|
2813
|
+
"--yolo",
|
|
2814
|
+
...spec.continueSession ? ["--continue"] : [],
|
|
2815
|
+
...modelArgs(spec.model),
|
|
2816
|
+
...spec.effort ? ["--effort", spec.effort] : [],
|
|
2817
|
+
...denyArgs(spec.denyPaths),
|
|
2818
|
+
"-p",
|
|
2819
|
+
prompt
|
|
2820
|
+
];
|
|
2821
|
+
return { bin: resolveGrokBin(), args, env: { GROK_HOME: home } };
|
|
2822
|
+
},
|
|
2823
|
+
parseResult(raw) {
|
|
2824
|
+
return parseGrokResult(raw);
|
|
2825
|
+
},
|
|
2826
|
+
session: {
|
|
2827
|
+
exists: (worktree) => newestSessionMtime(grokSessionDir(worktree)) !== null,
|
|
2828
|
+
mtimeMs: (worktree) => newestSessionMtime(grokSessionDir(worktree))
|
|
2829
|
+
},
|
|
2830
|
+
probe: {
|
|
2831
|
+
args: ["-p", "ok", "--output-format", "json", "--yolo", "--max-turns", "1", "--no-auto-update"],
|
|
2832
|
+
limitPattern: /usage limit/i,
|
|
2833
|
+
authPattern: /not logged in|unauthorized|\b401\b|authentication required|auth required/i
|
|
2834
|
+
}
|
|
2835
|
+
};
|
|
2836
|
+
}
|
|
2837
|
+
});
|
|
2838
|
+
|
|
2839
|
+
// src/liveModels.ts
|
|
2840
|
+
import { execFile } from "child_process";
|
|
2841
|
+
function liveDefaultModel(providerId) {
|
|
2842
|
+
return live.get(providerId) ?? null;
|
|
2843
|
+
}
|
|
2844
|
+
var live;
|
|
2845
|
+
var init_liveModels = __esm({
|
|
2846
|
+
"src/liveModels.ts"() {
|
|
2847
|
+
"use strict";
|
|
2848
|
+
init_grok();
|
|
2849
|
+
live = /* @__PURE__ */ new Map();
|
|
2850
|
+
}
|
|
2851
|
+
});
|
|
2852
|
+
|
|
2616
2853
|
// src/swarmModels.ts
|
|
2854
|
+
function roleDefault(role, providerId) {
|
|
2855
|
+
return liveDefaultModel(providerId) ?? defaultModelFor(role, providerId);
|
|
2856
|
+
}
|
|
2617
2857
|
function pick2(v) {
|
|
2618
2858
|
const s = typeof v === "string" ? v.trim() : "";
|
|
2619
2859
|
return s.length > 0 ? s : null;
|
|
@@ -2628,13 +2868,25 @@ function forKind(kind, swarm, norm) {
|
|
|
2628
2868
|
if (!swarm) return null;
|
|
2629
2869
|
return isReviewKind(kind) ? norm(swarm.reviewer) ?? norm(swarm.drone) : norm(swarm.drone);
|
|
2630
2870
|
}
|
|
2871
|
+
function providersFromEnv(env = process.env) {
|
|
2872
|
+
const raw = (env.SWARM_PROVIDERS ?? "").trim();
|
|
2873
|
+
if (!raw) return null;
|
|
2874
|
+
const known = new Set(MODEL_PROVIDERS.map((p) => p.id));
|
|
2875
|
+
const list = [...new Set(raw.split(/[\s,]+/).map((p) => p.trim().toLowerCase()).filter((p) => known.has(p)))];
|
|
2876
|
+
return list.length ? list : null;
|
|
2877
|
+
}
|
|
2631
2878
|
function resolveRunModelRef(run, swarm, env = process.env) {
|
|
2632
|
-
const
|
|
2633
|
-
|
|
2634
|
-
);
|
|
2879
|
+
const pinned = parseModelRef(pick2(run.model));
|
|
2880
|
+
if (pinned) return pinned;
|
|
2881
|
+
const configured = parseModelRef(forKind(run.kind, swarm, pick2) ?? pick2(env.SWARM_DRONE_MODEL));
|
|
2882
|
+
const own = providersFromEnv(env);
|
|
2883
|
+
if (own) {
|
|
2884
|
+
if (configured && own.includes(configured.providerId)) return configured;
|
|
2885
|
+
return { providerId: own[0], model: roleDefault(modelRoleOf(run.kind), own[0]) };
|
|
2886
|
+
}
|
|
2635
2887
|
if (configured) return configured;
|
|
2636
2888
|
const providerId = pick2(env.SWARM_MODEL_PROVIDER) ?? DEFAULT_PROVIDER_ID;
|
|
2637
|
-
return { providerId, model:
|
|
2889
|
+
return { providerId, model: roleDefault(modelRoleOf(run.kind), providerId) };
|
|
2638
2890
|
}
|
|
2639
2891
|
function resolveRunEffort(run, swarm, env = process.env) {
|
|
2640
2892
|
return toEffortLevel(run.effort) ?? forKind(run.kind, swarm, toEffortLevel) ?? toEffortLevel(env.SWARM_DRONE_EFFORT) ?? null;
|
|
@@ -2643,6 +2895,7 @@ var init_swarmModels = __esm({
|
|
|
2643
2895
|
"src/swarmModels.ts"() {
|
|
2644
2896
|
"use strict";
|
|
2645
2897
|
init_src();
|
|
2898
|
+
init_liveModels();
|
|
2646
2899
|
}
|
|
2647
2900
|
});
|
|
2648
2901
|
|
|
@@ -2678,21 +2931,24 @@ var init_executors = __esm({
|
|
|
2678
2931
|
init_src();
|
|
2679
2932
|
init_claudeCode();
|
|
2680
2933
|
init_codex();
|
|
2934
|
+
init_grok();
|
|
2681
2935
|
init_swarmModels();
|
|
2682
2936
|
init_types2();
|
|
2683
2937
|
init_claudeCode();
|
|
2684
2938
|
init_codex();
|
|
2939
|
+
init_grok();
|
|
2685
2940
|
DEFAULT_EXECUTOR_ID = "claude-code";
|
|
2686
2941
|
EXECUTORS = {
|
|
2687
2942
|
[claudeCodeExecutor.id]: claudeCodeExecutor,
|
|
2688
|
-
[codexExecutor.id]: codexExecutor
|
|
2943
|
+
[codexExecutor.id]: codexExecutor,
|
|
2944
|
+
[grokExecutor.id]: grokExecutor
|
|
2689
2945
|
};
|
|
2690
2946
|
}
|
|
2691
2947
|
});
|
|
2692
2948
|
|
|
2693
2949
|
// src/config.ts
|
|
2694
|
-
import { basename, join as
|
|
2695
|
-
import { readFileSync as readFileSync3, realpathSync, renameSync, writeFileSync as
|
|
2950
|
+
import { basename, join as join7, resolve } from "path";
|
|
2951
|
+
import { readFileSync as readFileSync3, realpathSync, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
2696
2952
|
function normalizeRepo(repo) {
|
|
2697
2953
|
const r = (repo ?? "").trim();
|
|
2698
2954
|
if (!r) return "";
|
|
@@ -2706,10 +2962,10 @@ function effectiveRepoOf(repo, fallback) {
|
|
|
2706
2962
|
return normalizeRepo(repo || fallback || "");
|
|
2707
2963
|
}
|
|
2708
2964
|
function droneWorktreePath(worktreesDir, repo, slug) {
|
|
2709
|
-
return
|
|
2965
|
+
return join7(droneWorktreeRoot(worktreesDir, repo), slug);
|
|
2710
2966
|
}
|
|
2711
2967
|
function droneWorktreeRoot(worktreesDir, repo) {
|
|
2712
|
-
return
|
|
2968
|
+
return join7(worktreesDir, basename(normalizeRepo(repo)));
|
|
2713
2969
|
}
|
|
2714
2970
|
function resolveEffectiveWorktreesDir(deps) {
|
|
2715
2971
|
if (deps.envOverride) return deps.envOverride;
|
|
@@ -2736,7 +2992,7 @@ function mcpEndpoint(api, path = TASKLESS_MCP_PATH) {
|
|
|
2736
2992
|
}
|
|
2737
2993
|
function tokenFromFile() {
|
|
2738
2994
|
try {
|
|
2739
|
-
return readFileSync3(
|
|
2995
|
+
return readFileSync3(join7(SWARM_HOME, "token"), "utf8").trim();
|
|
2740
2996
|
} catch {
|
|
2741
2997
|
return "";
|
|
2742
2998
|
}
|
|
@@ -2749,25 +3005,25 @@ var init_config = __esm({
|
|
|
2749
3005
|
init_executors();
|
|
2750
3006
|
init_swarmHome();
|
|
2751
3007
|
init_swarmHome();
|
|
2752
|
-
DB_PATH = process.env.SWARM_DB ||
|
|
3008
|
+
DB_PATH = process.env.SWARM_DB || join7(SWARM_HOME, "swarm.db");
|
|
2753
3009
|
EXECUTOR = resolveExecutor(process.env.SWARM_EXECUTOR);
|
|
2754
|
-
DRONE_HOME = process.env.SWARM_DRONE_HOME ||
|
|
3010
|
+
DRONE_HOME = process.env.SWARM_DRONE_HOME || join7(SWARM_HOME, "drone-home");
|
|
2755
3011
|
DRONE_HOME_ISOLATION = process.env.SWARM_DRONE_ISOLATE_HOME !== "0" && process.platform !== "win32";
|
|
2756
3012
|
DRONE_ISOLATION = DRONE_HOME_ISOLATION ? { home: DRONE_HOME, swarmHome: SWARM_HOME } : void 0;
|
|
2757
|
-
WORKTREES_NOINDEX_DIR =
|
|
2758
|
-
WORKTREES_LEGACY_DIR =
|
|
3013
|
+
WORKTREES_NOINDEX_DIR = join7(SWARM_HOME, "worktrees.noindex");
|
|
3014
|
+
WORKTREES_LEGACY_DIR = join7(SWARM_HOME, "worktrees");
|
|
2759
3015
|
WORKTREES_DIR = process.env.SWARM_WORKTREES || WORKTREES_NOINDEX_DIR;
|
|
2760
|
-
RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS ||
|
|
2761
|
-
DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG ||
|
|
3016
|
+
RUN_LOGS_DIR = process.env.SWARM_RUN_LOGS || join7(SWARM_HOME, "run-logs");
|
|
3017
|
+
DAEMON_LOG_PATH = process.env.SWARM_DAEMON_LOG || join7(SWARM_HOME, "overmind.log");
|
|
2762
3018
|
DAEMON_LOG_MAX_BYTES = envNum("SWARM_DAEMON_LOG_MAX_BYTES", 16 * 1024 * 1024);
|
|
2763
3019
|
DAEMON_LOG_KEEP = envNum("SWARM_DAEMON_LOG_KEEP", 2);
|
|
2764
3020
|
DAEMON_LOG_CHECK_MS = envNum("SWARM_DAEMON_LOG_CHECK_MS", 6e4);
|
|
2765
3021
|
DIST_BUILD = true;
|
|
2766
3022
|
SWARM_BACKEND = process.env.SWARM_BACKEND === "client" ? "client" : process.env.SWARM_BACKEND === "sqlite" ? "sqlite" : DIST_BUILD ? "client" : "sqlite";
|
|
2767
|
-
SWARM_OUTBOX = process.env.SWARM_OUTBOX ||
|
|
3023
|
+
SWARM_OUTBOX = process.env.SWARM_OUTBOX || join7(SWARM_HOME, "outbox.jsonl");
|
|
2768
3024
|
SWARM_MAX_IN_FLIGHT = Math.trunc(envNum("SWARM_MAX_IN_FLIGHT", 3));
|
|
2769
3025
|
SWARM_CLAIM_INTERVAL_MS = envNum("SWARM_CLAIM_INTERVAL_MS", 6e4);
|
|
2770
|
-
ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR ||
|
|
3026
|
+
ENV_CACHE_DIR = process.env.SWARM_ENV_CACHE_DIR || join7(SWARM_HOME, "env-cache");
|
|
2771
3027
|
ENV_CACHE_ENABLED = process.env.SWARM_ENV_CACHE !== "0";
|
|
2772
3028
|
ENV_CACHE_KEEP = Math.trunc(envNum("SWARM_ENV_CACHE_KEEP", 4));
|
|
2773
3029
|
SWARM_MAX_LOAD_FACTOR = envNum("SWARM_MAX_LOAD_FACTOR", 0.9);
|
|
@@ -2797,15 +3053,15 @@ var init_config = __esm({
|
|
|
2797
3053
|
DRONE_CHANNEL_ENABLED = process.env.SWARM_DRONE_CHANNEL !== "0";
|
|
2798
3054
|
DRONE_CHANNEL_PORT = Number(process.env.SWARM_DRONE_CHANNEL_PORT) || 7421;
|
|
2799
3055
|
DRONE_CHANNEL_URL = process.env.SWARM_DRONE_CHANNEL_URL || `http://127.0.0.1:${DRONE_CHANNEL_PORT}`;
|
|
2800
|
-
DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET ||
|
|
3056
|
+
DRONE_CHANNEL_SECRET_PATH = process.env.SWARM_DRONE_CHANNEL_SECRET || join7(SWARM_HOME, "drone-channel.key");
|
|
2801
3057
|
DRONE_CHANNEL_GRACE_MS = envNum("SWARM_DRONE_CHANNEL_GRACE_MS", 12e4);
|
|
2802
|
-
HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT ||
|
|
3058
|
+
HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join7(SWARM_HOME, "overmind.heartbeat");
|
|
2803
3059
|
HEARTBEAT_STALE_MS = Number(process.env.SWARM_HEARTBEAT_STALE_MS) || 3e4;
|
|
2804
3060
|
SWARM_REPO = process.env.SWARM_REPO || "";
|
|
2805
3061
|
SWARM_SCAN_DEPTH = Number(process.env.SWARM_SCAN_DEPTH) || 3;
|
|
2806
3062
|
SWARM_REPO_PATHS = (process.env.SWARM_REPOS || SWARM_REPO).split(",").map((s) => s.trim()).filter(Boolean);
|
|
2807
3063
|
SWARM_SCAN_ROOTS_RAW = process.env.SWARM_SCAN_ROOTS || "";
|
|
2808
|
-
REPO_MAP_PATH = process.env.SWARM_REPO_MAP ||
|
|
3064
|
+
REPO_MAP_PATH = process.env.SWARM_REPO_MAP || join7(SWARM_HOME, "repo-map.json");
|
|
2809
3065
|
SWARM_REPO_MAP_MS = envNum("SWARM_REPO_MAP_MS", 15 * 6e4);
|
|
2810
3066
|
SWARM_REPO_SCAN_TTL_MS = envNum("SWARM_REPO_SCAN_TTL_MS", 24 * 60 * 6e4);
|
|
2811
3067
|
TASKLESS_API = process.env.TASKLESS_API || "https://taskless.ru";
|
|
@@ -2820,7 +3076,7 @@ var init_config = __esm({
|
|
|
2820
3076
|
SWARM_AUTO_REVIEW = process.env.SWARM_AUTO_REVIEW !== "0";
|
|
2821
3077
|
SWARM_AUTO_VERIFY = process.env.SWARM_AUTO_VERIFY !== "0";
|
|
2822
3078
|
SWARM_MERGE_QUEUE = process.env.SWARM_MERGE_QUEUE !== "0";
|
|
2823
|
-
REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF ||
|
|
3079
|
+
REVIEW_CUTOFF_PATH = process.env.SWARM_REVIEW_CUTOFF || join7(SWARM_HOME, "review-cutoff.json");
|
|
2824
3080
|
SWARM_REVIEW_CAP = Math.trunc(envNum("SWARM_REVIEW_CAP", 1));
|
|
2825
3081
|
SWARM_STORM_URL = process.env.SWARM_STORM_URL || "";
|
|
2826
3082
|
WEB_PORT = Number(process.env.SWARM_WEB_PORT) || 7420;
|
|
@@ -3052,11 +3308,11 @@ var init_resources = __esm({
|
|
|
3052
3308
|
|
|
3053
3309
|
// src/names.ts
|
|
3054
3310
|
function pickName(rows) {
|
|
3055
|
-
const
|
|
3311
|
+
const live2 = /* @__PURE__ */ new Set();
|
|
3056
3312
|
const lastUse = /* @__PURE__ */ new Map();
|
|
3057
3313
|
for (const r of rows) {
|
|
3058
3314
|
if (!r.name) continue;
|
|
3059
|
-
if (NAME_LIVE.has(r.status))
|
|
3315
|
+
if (NAME_LIVE.has(r.status)) live2.add(r.name);
|
|
3060
3316
|
const b = baseName(r.name);
|
|
3061
3317
|
lastUse.set(b, Math.max(lastUse.get(b) ?? 0, r.id));
|
|
3062
3318
|
}
|
|
@@ -3066,7 +3322,7 @@ function pickName(rows) {
|
|
|
3066
3322
|
for (let g = 1; ; g++) {
|
|
3067
3323
|
for (const base3 of ordered) {
|
|
3068
3324
|
const cand = genName(base3, g);
|
|
3069
|
-
if (!
|
|
3325
|
+
if (!live2.has(cand)) return cand;
|
|
3070
3326
|
}
|
|
3071
3327
|
}
|
|
3072
3328
|
}
|
|
@@ -3148,8 +3404,8 @@ var init_store = __esm({
|
|
|
3148
3404
|
});
|
|
3149
3405
|
|
|
3150
3406
|
// src/agentToken.ts
|
|
3151
|
-
import { readFileSync as readFileSync5, writeFileSync as
|
|
3152
|
-
import { join as
|
|
3407
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, unlinkSync } from "fs";
|
|
3408
|
+
import { join as join8 } from "path";
|
|
3153
3409
|
function loadAgentToken() {
|
|
3154
3410
|
try {
|
|
3155
3411
|
const s = JSON.parse(readFileSync5(STORE_PATH, "utf8"));
|
|
@@ -3159,7 +3415,7 @@ function loadAgentToken() {
|
|
|
3159
3415
|
}
|
|
3160
3416
|
}
|
|
3161
3417
|
function saveAgentToken(s) {
|
|
3162
|
-
|
|
3418
|
+
writeFileSync5(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
|
|
3163
3419
|
}
|
|
3164
3420
|
function clearAgentToken() {
|
|
3165
3421
|
try {
|
|
@@ -3230,7 +3486,7 @@ var init_agentToken = __esm({
|
|
|
3230
3486
|
"src/agentToken.ts"() {
|
|
3231
3487
|
"use strict";
|
|
3232
3488
|
init_config();
|
|
3233
|
-
STORE_PATH =
|
|
3489
|
+
STORE_PATH = join8(SWARM_HOME, "agent.json");
|
|
3234
3490
|
base = (api) => api.replace(/\/$/, "");
|
|
3235
3491
|
TOKEN_PROBE_TIMEOUT_MS = 8e3;
|
|
3236
3492
|
}
|
|
@@ -3616,7 +3872,7 @@ var init_package = __esm({
|
|
|
3616
3872
|
"package.json"() {
|
|
3617
3873
|
package_default = {
|
|
3618
3874
|
name: "@taskless-app/swarm",
|
|
3619
|
-
version: "0.6.
|
|
3875
|
+
version: "0.6.64",
|
|
3620
3876
|
type: "module",
|
|
3621
3877
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3622
3878
|
keywords: [
|
|
@@ -3667,7 +3923,7 @@ var init_package = __esm({
|
|
|
3667
3923
|
execa: "^10.0.0",
|
|
3668
3924
|
zod: "3.25.76"
|
|
3669
3925
|
},
|
|
3670
|
-
gitHead: "
|
|
3926
|
+
gitHead: "7f27042a00bf1554798cce7b8a233ee6c5686a50"
|
|
3671
3927
|
};
|
|
3672
3928
|
}
|
|
3673
3929
|
});
|
|
@@ -3833,7 +4089,7 @@ var init_netTimeout = __esm({
|
|
|
3833
4089
|
|
|
3834
4090
|
// src/droneChannel.ts
|
|
3835
4091
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
3836
|
-
import { chmodSync as chmodSync2, mkdirSync as
|
|
4092
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
3837
4093
|
import { dirname as dirname3 } from "path";
|
|
3838
4094
|
import { serve } from "@hono/node-server";
|
|
3839
4095
|
function loadChannelSecret(path) {
|
|
@@ -3843,8 +4099,8 @@ function loadChannelSecret(path) {
|
|
|
3843
4099
|
} catch {
|
|
3844
4100
|
}
|
|
3845
4101
|
const secret = randomBytes(32).toString("hex");
|
|
3846
|
-
|
|
3847
|
-
|
|
4102
|
+
mkdirSync8(dirname3(path), { recursive: true });
|
|
4103
|
+
writeFileSync8(path, secret, { mode: 384 });
|
|
3848
4104
|
try {
|
|
3849
4105
|
chmodSync2(path, 384);
|
|
3850
4106
|
} catch {
|
|
@@ -3921,8 +4177,8 @@ var init_authState = __esm({
|
|
|
3921
4177
|
|
|
3922
4178
|
// src/oauth.ts
|
|
3923
4179
|
import { createHash, randomBytes as randomBytes2 } from "crypto";
|
|
3924
|
-
import { readFileSync as readFileSync10, writeFileSync as
|
|
3925
|
-
import { join as
|
|
4180
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3 } from "fs";
|
|
4181
|
+
import { join as join13 } from "path";
|
|
3926
4182
|
function makePkce() {
|
|
3927
4183
|
const verifier = randomBytes2(48).toString("base64url");
|
|
3928
4184
|
const challenge = createHash("sha256").update(verifier).digest("base64url");
|
|
@@ -4012,7 +4268,7 @@ function loadStore() {
|
|
|
4012
4268
|
}
|
|
4013
4269
|
}
|
|
4014
4270
|
function saveStore(s) {
|
|
4015
|
-
|
|
4271
|
+
writeFileSync9(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
|
|
4016
4272
|
}
|
|
4017
4273
|
function clearStore() {
|
|
4018
4274
|
try {
|
|
@@ -4031,7 +4287,7 @@ async function ensureClientId(api, redirectUri, fetchFn = fetch) {
|
|
|
4031
4287
|
const existing = loadClientId();
|
|
4032
4288
|
if (existing) return existing;
|
|
4033
4289
|
const clientId = await registerClient({ api, redirectUri }, fetchFn);
|
|
4034
|
-
|
|
4290
|
+
writeFileSync9(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
|
|
4035
4291
|
return clientId;
|
|
4036
4292
|
}
|
|
4037
4293
|
async function ensureAccess(store, opts = {}) {
|
|
@@ -4057,16 +4313,16 @@ var init_oauth = __esm({
|
|
|
4057
4313
|
AUTHORIZE = "/api/oauth/authorize";
|
|
4058
4314
|
TOKEN = "/api/oauth/token";
|
|
4059
4315
|
SCOPE = "openid profile email taskless:full";
|
|
4060
|
-
STORE_PATH2 =
|
|
4061
|
-
CLIENT_PATH =
|
|
4316
|
+
STORE_PATH2 = join13(SWARM_HOME, "oauth.json");
|
|
4317
|
+
CLIENT_PATH = join13(SWARM_HOME, "oauth-client.json");
|
|
4062
4318
|
base2 = (api) => api.replace(/\/$/, "");
|
|
4063
4319
|
}
|
|
4064
4320
|
});
|
|
4065
4321
|
|
|
4066
4322
|
// src/git.ts
|
|
4067
|
-
import { existsSync as
|
|
4323
|
+
import { existsSync as existsSync11, readdirSync as readdirSync10, realpathSync as realpathSync2, statSync as statSync10 } from "fs";
|
|
4068
4324
|
import { appendFile, chmod, mkdir, readFile, rename, rm, writeFile } from "fs/promises";
|
|
4069
|
-
import { isAbsolute, join as
|
|
4325
|
+
import { isAbsolute, join as join16, resolve as resolve2, sep as sep2 } from "path";
|
|
4070
4326
|
import { $ as $5 } from "execa";
|
|
4071
4327
|
function gitNet(repo, args, timeoutMs) {
|
|
4072
4328
|
return withHardTimeout(
|
|
@@ -4080,14 +4336,14 @@ async function reclaimStaleWorktree(repo, worktree) {
|
|
|
4080
4336
|
await detachWorktree(repo, worktree);
|
|
4081
4337
|
}
|
|
4082
4338
|
async function inspectWorktreePath(path) {
|
|
4083
|
-
if (!
|
|
4339
|
+
if (!existsSync11(path)) return "free";
|
|
4084
4340
|
try {
|
|
4085
|
-
if (
|
|
4341
|
+
if (readdirSync10(path).length === 0) return "free";
|
|
4086
4342
|
} catch {
|
|
4087
4343
|
return "orphan";
|
|
4088
4344
|
}
|
|
4089
4345
|
try {
|
|
4090
|
-
if (
|
|
4346
|
+
if (statSync10(join16(path, ".git")).isDirectory()) return "main-checkout";
|
|
4091
4347
|
} catch {
|
|
4092
4348
|
}
|
|
4093
4349
|
const top = await $5({ reject: false })`git -C ${path} rev-parse --show-toplevel`;
|
|
@@ -4126,7 +4382,7 @@ async function clearWorktreePath(repo, path, branch, opts = {}) {
|
|
|
4126
4382
|
await detachWorktree(repo, path);
|
|
4127
4383
|
opts.onStaleReclaim?.(path);
|
|
4128
4384
|
}
|
|
4129
|
-
if (!
|
|
4385
|
+
if (!existsSync11(path)) return;
|
|
4130
4386
|
await parkOrphanDir(repo, path, branch);
|
|
4131
4387
|
}
|
|
4132
4388
|
async function parkOrphanDir(repo, path, branch) {
|
|
@@ -4147,7 +4403,7 @@ async function parkOrphanDir(repo, path, branch) {
|
|
|
4147
4403
|
}
|
|
4148
4404
|
function isLinkedWorktree(dir) {
|
|
4149
4405
|
try {
|
|
4150
|
-
return
|
|
4406
|
+
return statSync10(join16(dir, ".git")).isFile();
|
|
4151
4407
|
} catch {
|
|
4152
4408
|
return false;
|
|
4153
4409
|
}
|
|
@@ -4159,7 +4415,7 @@ async function detachWorktree(repo, worktree) {
|
|
|
4159
4415
|
}
|
|
4160
4416
|
}
|
|
4161
4417
|
async function ensureWorktreeDirGone(worktree) {
|
|
4162
|
-
if (!
|
|
4418
|
+
if (!existsSync11(worktree)) return false;
|
|
4163
4419
|
if (!isLinkedWorktree(worktree)) {
|
|
4164
4420
|
console.error(
|
|
4165
4421
|
`[git] ${worktree} \u2014 \u043D\u0435 \u043F\u0440\u0438\u0441\u0442\u0451\u0433\u043D\u0443\u0442\u044B\u0439 worktree (.git \u043D\u0435 \u0444\u0430\u0439\u043B): \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u041D\u0415 \u0442\u0440\u043E\u0433\u0430\u044E. \u0415\u0441\u043B\u0438 \u044D\u0442\u043E \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043B\u044C\u043D\u043E worktree \u0434\u0440\u043E\u043D\u0430 \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 \u0435\u0433\u043E \u0440\u0443\u043A\u0430\u043C\u0438; \u0435\u0441\u043B\u0438 \u044D\u0442\u043E \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u0447\u0435\u043A\u0430\u0443\u0442, \u0437\u043D\u0430\u0447\u0438\u0442 \u0432\u0435\u0442\u043A\u0430 \u0441\u043B\u0430\u0433\u0430 \u0447\u0435\u043A\u043D\u0443\u0442\u0430 \u0432 \u043D\u0451\u043C (git checkout <slug>) \u2014 \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u0435 \u0435\u0433\u043E \u043D\u0430 \u0434\u0440\u0443\u0433\u0443\u044E \u0432\u0435\u0442\u043A\u0443.`
|
|
@@ -4174,7 +4430,7 @@ async function ensureWorktreeDirGone(worktree) {
|
|
|
4174
4430
|
);
|
|
4175
4431
|
return true;
|
|
4176
4432
|
}
|
|
4177
|
-
if (
|
|
4433
|
+
if (existsSync11(worktree)) {
|
|
4178
4434
|
console.error(`[git] \u043A\u0430\u0442\u0430\u043B\u043E\u0433 worktree ${worktree} \u043F\u0435\u0440\u0435\u0436\u0438\u043B rm \u2014 \u0443\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u0443\u043A\u0430\u043C\u0438, \u0438\u043D\u0430\u0447\u0435 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0441\u043F\u0430\u0432\u043D \u0441\u043B\u0430\u0433\u0430 \u0432\u0441\u0442\u0430\u043D\u0435\u0442.`);
|
|
4179
4435
|
}
|
|
4180
4436
|
return true;
|
|
@@ -4223,9 +4479,9 @@ async function checkoutWorktree(repo, path, branch, opts) {
|
|
|
4223
4479
|
}
|
|
4224
4480
|
const busyAt = await worktreeForBranch(repo, branch);
|
|
4225
4481
|
if (busyAt) {
|
|
4226
|
-
const exists =
|
|
4227
|
-
const
|
|
4228
|
-
if (
|
|
4482
|
+
const exists = existsSync11(busyAt);
|
|
4483
|
+
const live2 = exists && (opts.isWorktreeHeldLive ? await opts.isWorktreeHeldLive(busyAt) : true);
|
|
4484
|
+
if (live2) throw new WorktreeBusyError(branch, busyAt);
|
|
4229
4485
|
if (exists && await worktreeDirty(busyAt)) throw new WorktreeBusyError(branch, busyAt);
|
|
4230
4486
|
await reclaimStaleWorktree(repo, busyAt);
|
|
4231
4487
|
opts.onStaleReclaim?.(busyAt);
|
|
@@ -4254,8 +4510,8 @@ async function ensureGitExcluded(worktree, pattern) {
|
|
|
4254
4510
|
try {
|
|
4255
4511
|
const commonDir = await gitPath(worktree, "--git-common-dir");
|
|
4256
4512
|
if (!commonDir) return false;
|
|
4257
|
-
const infoDir =
|
|
4258
|
-
const excludeFile =
|
|
4513
|
+
const infoDir = join16(commonDir, "info");
|
|
4514
|
+
const excludeFile = join16(infoDir, "exclude");
|
|
4259
4515
|
let current = "";
|
|
4260
4516
|
try {
|
|
4261
4517
|
current = await readFile(excludeFile, "utf8");
|
|
@@ -4275,7 +4531,7 @@ async function gitPath(worktree, flag) {
|
|
|
4275
4531
|
const out = await $5({ reject: false })`git -C ${worktree} rev-parse ${flag}`;
|
|
4276
4532
|
const raw = out.stdout.trim();
|
|
4277
4533
|
if (out.exitCode !== 0 || !raw) return null;
|
|
4278
|
-
return isAbsolute(raw) ? raw :
|
|
4534
|
+
return isAbsolute(raw) ? raw : join16(worktree, raw);
|
|
4279
4535
|
}
|
|
4280
4536
|
async function ensureWorktreeHygiene(worktree) {
|
|
4281
4537
|
for (const pattern of SWARM_EXCLUDE_PATTERNS) await ensureGitExcluded(worktree, pattern);
|
|
@@ -4285,12 +4541,12 @@ async function installGuardHooks(worktree) {
|
|
|
4285
4541
|
try {
|
|
4286
4542
|
const gitDir = await gitPath(worktree, "--git-dir");
|
|
4287
4543
|
if (!gitDir) return false;
|
|
4288
|
-
const hooksDir =
|
|
4544
|
+
const hooksDir = join16(gitDir, "swarm-hooks");
|
|
4289
4545
|
const prev = await previousHooksPath(worktree);
|
|
4290
4546
|
await mkdir(hooksDir, { recursive: true });
|
|
4291
4547
|
for (const hook of GUARD_HOOKS) {
|
|
4292
|
-
await writeFile(
|
|
4293
|
-
await chmod(
|
|
4548
|
+
await writeFile(join16(hooksDir, hook), guardHookScript(hook, prev ? `${posix(prev)}/${hook}` : null));
|
|
4549
|
+
await chmod(join16(hooksDir, hook), 493);
|
|
4294
4550
|
}
|
|
4295
4551
|
await $5`git -C ${worktree} config extensions.worktreeConfig true`;
|
|
4296
4552
|
await $5`git -C ${worktree} config --worktree core.hooksPath ${posix(hooksDir)}`;
|
|
@@ -4309,7 +4565,7 @@ async function previousHooksPath(worktree) {
|
|
|
4309
4565
|
if (out.exitCode === 0 && value) return value;
|
|
4310
4566
|
}
|
|
4311
4567
|
const commonDir = await gitPath(worktree, "--git-common-dir");
|
|
4312
|
-
return commonDir ?
|
|
4568
|
+
return commonDir ? join16(commonDir, "hooks") : null;
|
|
4313
4569
|
}
|
|
4314
4570
|
async function removeWorktree(repo, path) {
|
|
4315
4571
|
const branch = (await $5({ reject: false })`git -C ${path} symbolic-ref --quiet --short HEAD`).stdout.trim();
|
|
@@ -4372,7 +4628,7 @@ var init_git = __esm({
|
|
|
4372
4628
|
|
|
4373
4629
|
// src/attachments.ts
|
|
4374
4630
|
import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
4375
|
-
import { join as
|
|
4631
|
+
import { join as join17 } from "path";
|
|
4376
4632
|
var ATTACHMENTS_DIR, ATTACH_MAX_TOTAL_BYTES, ATTACH_MAX_FILE_BYTES, IMAGE_EXT, DOC_EXT, TEXT_EXT, KNOWN_EXT;
|
|
4377
4633
|
var init_attachments = __esm({
|
|
4378
4634
|
"src/attachments.ts"() {
|
|
@@ -4391,7 +4647,7 @@ var init_attachments = __esm({
|
|
|
4391
4647
|
|
|
4392
4648
|
// src/shotUpload.ts
|
|
4393
4649
|
import { readFile as fsReadFile } from "fs/promises";
|
|
4394
|
-
import { basename as basename5, isAbsolute as isAbsolute2, join as
|
|
4650
|
+
import { basename as basename5, isAbsolute as isAbsolute2, join as join18 } from "path";
|
|
4395
4651
|
var SHOT_MAX_FILE_BYTES;
|
|
4396
4652
|
var init_shotUpload = __esm({
|
|
4397
4653
|
"src/shotUpload.ts"() {
|
|
@@ -4401,8 +4657,8 @@ var init_shotUpload = __esm({
|
|
|
4401
4657
|
});
|
|
4402
4658
|
|
|
4403
4659
|
// src/workspace.ts
|
|
4404
|
-
import { readFileSync as readFileSync11, writeFileSync as
|
|
4405
|
-
import { join as
|
|
4660
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync10, unlinkSync as unlinkSync4 } from "fs";
|
|
4661
|
+
import { join as join19 } from "path";
|
|
4406
4662
|
function loadWorkspace() {
|
|
4407
4663
|
try {
|
|
4408
4664
|
const w = JSON.parse(readFileSync11(WORKSPACE_PATH, "utf8"));
|
|
@@ -4412,7 +4668,7 @@ function loadWorkspace() {
|
|
|
4412
4668
|
}
|
|
4413
4669
|
}
|
|
4414
4670
|
function saveWorkspace(w) {
|
|
4415
|
-
|
|
4671
|
+
writeFileSync10(WORKSPACE_PATH, JSON.stringify(w, null, 2), { mode: 384 });
|
|
4416
4672
|
}
|
|
4417
4673
|
function clearWorkspace() {
|
|
4418
4674
|
try {
|
|
@@ -4428,7 +4684,7 @@ var init_workspace = __esm({
|
|
|
4428
4684
|
"src/workspace.ts"() {
|
|
4429
4685
|
"use strict";
|
|
4430
4686
|
init_config();
|
|
4431
|
-
WORKSPACE_PATH =
|
|
4687
|
+
WORKSPACE_PATH = join19(SWARM_HOME, "workspace.json");
|
|
4432
4688
|
}
|
|
4433
4689
|
});
|
|
4434
4690
|
|
|
@@ -4846,13 +5102,13 @@ var init_daemonLink = __esm({
|
|
|
4846
5102
|
});
|
|
4847
5103
|
|
|
4848
5104
|
// src/pauseState.ts
|
|
4849
|
-
import { existsSync as
|
|
4850
|
-
import { join as
|
|
5105
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync11 } from "fs";
|
|
5106
|
+
import { join as join20 } from "path";
|
|
4851
5107
|
function pauseStatePath(home) {
|
|
4852
|
-
return
|
|
5108
|
+
return join20(home, "pause.json");
|
|
4853
5109
|
}
|
|
4854
5110
|
function loadPauseState(home, io = {}) {
|
|
4855
|
-
const exists = io.exists ??
|
|
5111
|
+
const exists = io.exists ?? existsSync12;
|
|
4856
5112
|
const readFile2 = io.readFile ?? ((p) => readFileSync12(p, "utf8"));
|
|
4857
5113
|
const path = pauseStatePath(home);
|
|
4858
5114
|
if (!exists(path)) return ACTIVE;
|
|
@@ -4872,12 +5128,12 @@ function loadPauseState(home, io = {}) {
|
|
|
4872
5128
|
function savePauseState(home, state, io = {}) {
|
|
4873
5129
|
const path = pauseStatePath(home);
|
|
4874
5130
|
if (!state.paused) {
|
|
4875
|
-
const remove = io.remove ?? ((p) =>
|
|
5131
|
+
const remove = io.remove ?? ((p) => existsSync12(p) && unlinkSync5(p));
|
|
4876
5132
|
remove(path);
|
|
4877
5133
|
return;
|
|
4878
5134
|
}
|
|
4879
5135
|
const writeFile3 = io.writeFile ?? ((p, data) => {
|
|
4880
|
-
|
|
5136
|
+
writeFileSync11(`${p}.tmp`, data);
|
|
4881
5137
|
renameSync5(`${p}.tmp`, p);
|
|
4882
5138
|
});
|
|
4883
5139
|
writeFile3(path, JSON.stringify(state, null, 2));
|
|
@@ -4914,7 +5170,7 @@ var init_pauseState = __esm({
|
|
|
4914
5170
|
});
|
|
4915
5171
|
|
|
4916
5172
|
// src/heartbeat.ts
|
|
4917
|
-
import { readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as
|
|
5173
|
+
import { readFileSync as readFileSync13, renameSync as renameSync6, writeFileSync as writeFileSync12 } from "fs";
|
|
4918
5174
|
function readHeartbeat(path) {
|
|
4919
5175
|
try {
|
|
4920
5176
|
const ms = Number(readFileSync13(path, "utf8").trim());
|
|
@@ -4936,7 +5192,7 @@ __export(db_exports, {
|
|
|
4936
5192
|
});
|
|
4937
5193
|
import { createRequire as createRequire2 } from "module";
|
|
4938
5194
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4939
|
-
import { existsSync as
|
|
5195
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4940
5196
|
import { resolve as resolve3 } from "path";
|
|
4941
5197
|
function rowToQuestion(row) {
|
|
4942
5198
|
return {
|
|
@@ -5600,7 +5856,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5600
5856
|
const repoN = normalizeRepo(repo);
|
|
5601
5857
|
const already = this.db.prepare("SELECT run_count FROM imports WHERE source = ?").get(abs);
|
|
5602
5858
|
if (already) return { imported: false, runs: already.run_count };
|
|
5603
|
-
if (!
|
|
5859
|
+
if (!existsSync13(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
|
|
5604
5860
|
const src = new this.databaseCtor(abs);
|
|
5605
5861
|
try {
|
|
5606
5862
|
const runs = src.prepare("SELECT * FROM runs ORDER BY id").all();
|
|
@@ -5928,8 +6184,8 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5928
6184
|
});
|
|
5929
6185
|
|
|
5930
6186
|
// src/repoScan.ts
|
|
5931
|
-
import { readdirSync as
|
|
5932
|
-
import { join as
|
|
6187
|
+
import { readdirSync as readdirSync11, readFileSync as readFileSync14, statSync as statSync11 } from "fs";
|
|
6188
|
+
import { join as join21 } from "path";
|
|
5933
6189
|
var init_repoScan = __esm({
|
|
5934
6190
|
"src/repoScan.ts"() {
|
|
5935
6191
|
"use strict";
|
|
@@ -5939,8 +6195,8 @@ var init_repoScan = __esm({
|
|
|
5939
6195
|
|
|
5940
6196
|
// src/repoDiscovery.ts
|
|
5941
6197
|
import { homedir as homedir6 } from "os";
|
|
5942
|
-
import { readFileSync as readFileSync15, renameSync as renameSync7, writeFileSync as
|
|
5943
|
-
import { isAbsolute as isAbsolute3, join as
|
|
6198
|
+
import { readFileSync as readFileSync15, renameSync as renameSync7, writeFileSync as writeFileSync13 } from "fs";
|
|
6199
|
+
import { isAbsolute as isAbsolute3, join as join22, resolve as resolve4 } from "path";
|
|
5944
6200
|
function repoMapLines(rows) {
|
|
5945
6201
|
if (!rows?.length) return [];
|
|
5946
6202
|
const missing = rows.filter((r) => !r.path).length;
|
|
@@ -6331,10 +6587,10 @@ __export(statusCli_exports, {
|
|
|
6331
6587
|
probeLink: () => probeLink,
|
|
6332
6588
|
runStatusCli: () => runStatusCli
|
|
6333
6589
|
});
|
|
6334
|
-
import { existsSync as
|
|
6590
|
+
import { existsSync as existsSync14, readFileSync as readFileSync16 } from "fs";
|
|
6335
6591
|
function outboxPending(path) {
|
|
6336
6592
|
try {
|
|
6337
|
-
if (!
|
|
6593
|
+
if (!existsSync14(path)) return 0;
|
|
6338
6594
|
return readFileSync16(path, "utf8").split("\n").filter((l) => l.trim()).length;
|
|
6339
6595
|
} catch {
|
|
6340
6596
|
return null;
|
|
@@ -6542,8 +6798,8 @@ var init_prodMergeGate = __esm({
|
|
|
6542
6798
|
});
|
|
6543
6799
|
|
|
6544
6800
|
// src/platform.ts
|
|
6545
|
-
import { existsSync as
|
|
6546
|
-
import { delimiter, join as
|
|
6801
|
+
import { existsSync as existsSync15, readFileSync as readFileSync17 } from "fs";
|
|
6802
|
+
import { delimiter, join as join23 } from "path";
|
|
6547
6803
|
function safeReadText(path) {
|
|
6548
6804
|
try {
|
|
6549
6805
|
return readFileSync17(path, "utf8");
|
|
@@ -6552,13 +6808,13 @@ function safeReadText(path) {
|
|
|
6552
6808
|
}
|
|
6553
6809
|
}
|
|
6554
6810
|
function resolveCommandPath(name, env = process.env, platform = process.platform) {
|
|
6555
|
-
if (name.includes("/") || name.includes("\\")) return
|
|
6811
|
+
if (name.includes("/") || name.includes("\\")) return existsSync15(name) ? name : null;
|
|
6556
6812
|
const dirs = (env.PATH ?? env.Path ?? "").split(delimiter).filter(Boolean);
|
|
6557
6813
|
const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
|
|
6558
6814
|
for (const dir of dirs) {
|
|
6559
6815
|
for (const ext of exts) {
|
|
6560
|
-
const candidate =
|
|
6561
|
-
if (
|
|
6816
|
+
const candidate = join23(dir, name + ext);
|
|
6817
|
+
if (existsSync15(candidate)) return candidate;
|
|
6562
6818
|
}
|
|
6563
6819
|
}
|
|
6564
6820
|
return null;
|
|
@@ -6661,13 +6917,13 @@ var init_platform = __esm({
|
|
|
6661
6917
|
});
|
|
6662
6918
|
|
|
6663
6919
|
// src/envGate.ts
|
|
6664
|
-
import { existsSync as
|
|
6665
|
-
import { join as
|
|
6920
|
+
import { existsSync as existsSync16 } from "fs";
|
|
6921
|
+
import { join as join24 } from "path";
|
|
6666
6922
|
import { createConnection } from "net";
|
|
6667
6923
|
import { execa as execa3 } from "execa";
|
|
6668
6924
|
function detectRequirements(repo, io = {}) {
|
|
6669
|
-
const exists = io.exists ??
|
|
6670
|
-
const marker = COMPOSE_MARKERS.find((f) => exists(
|
|
6925
|
+
const exists = io.exists ?? existsSync16;
|
|
6926
|
+
const marker = COMPOSE_MARKERS.find((f) => exists(join24(repo, f)));
|
|
6671
6927
|
return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
|
|
6672
6928
|
}
|
|
6673
6929
|
async function dockerAlive() {
|
|
@@ -6699,7 +6955,7 @@ var init_envGate = __esm({
|
|
|
6699
6955
|
});
|
|
6700
6956
|
|
|
6701
6957
|
// src/doctor.ts
|
|
6702
|
-
import { isAbsolute as isAbsolute4, join as
|
|
6958
|
+
import { isAbsolute as isAbsolute4, join as join25 } from "path";
|
|
6703
6959
|
function ok(id, title, detail) {
|
|
6704
6960
|
return { id, title, status: "ok", detail, remedies: [], critical: false };
|
|
6705
6961
|
}
|
|
@@ -6724,12 +6980,14 @@ function checkExecutor(io, executor = resolveExecutor()) {
|
|
|
6724
6980
|
const bin = executor.resolveBin();
|
|
6725
6981
|
const where = /[/\\]/.test(bin) ? `\u043D\u0430\u0439\u0434\u0435\u043D: ${bin}` : "\u043D\u0430\u0439\u0434\u0435\u043D \u0432 PATH";
|
|
6726
6982
|
if (io.hasCommand(bin)) return ok("claude", executor.title, where);
|
|
6983
|
+
const custom = executor.install.command == null ? null : io.platform.kind === "windows" && executor.install.windowsCommand ? executor.install.windowsCommand : executor.install.command;
|
|
6727
6984
|
const remedies = [
|
|
6728
6985
|
{
|
|
6729
6986
|
what: executor.install.what,
|
|
6730
|
-
command: globalInstallCommand(io.globalPm === "sources" ? "npm" : io.globalPm, executor.install.pkg),
|
|
6987
|
+
command: custom ?? globalInstallCommand(io.globalPm === "sources" ? "npm" : io.globalPm, executor.install.pkg),
|
|
6731
6988
|
sudo: false,
|
|
6732
|
-
|
|
6989
|
+
// Свой инсталлер (не npm-пакет) человек ставит сам: автозапуск скрипта с сайта — нет.
|
|
6990
|
+
auto: custom == null
|
|
6733
6991
|
}
|
|
6734
6992
|
];
|
|
6735
6993
|
const detail = io.platform.kind === "windows" ? `\u0432 PATH \u043D\u0435\u0442 \`${bin}\`. \u041D\u0430 Windows spawn \u0438\u0449\u0435\u0442 \u0444\u0430\u0439\u043B \u043F\u043E PATHEXT \u2014 bash-\u043E\u0431\u0451\u0440\u0442\u043A\u0430 \u0431\u0435\u0437 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u0435\u043C\u0443 \u043D\u0435 \u0432\u0438\u0434\u043D\u0430; \u043D\u0435\u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u043D\u0430\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0437\u0430\u0434\u0430\u0451\u0442\u0441\u044F ${executor.binEnv}` : `\u0432 PATH \u043D\u0435\u0442 \`${bin}\` \u2014 \u0434\u0440\u043E\u043D\u0443 \u043D\u0435\u0447\u0435\u043C \u043F\u0438\u0441\u0430\u0442\u044C \u043A\u043E\u0434`;
|
|
@@ -6906,7 +7164,7 @@ function checkRepos(io, snap) {
|
|
|
6906
7164
|
critical: true
|
|
6907
7165
|
};
|
|
6908
7166
|
}
|
|
6909
|
-
if (env && !io.exists(
|
|
7167
|
+
if (env && !io.exists(join25(env, ".git"))) {
|
|
6910
7168
|
return {
|
|
6911
7169
|
id: "repos",
|
|
6912
7170
|
title,
|
|
@@ -6916,7 +7174,7 @@ function checkRepos(io, snap) {
|
|
|
6916
7174
|
critical: true
|
|
6917
7175
|
};
|
|
6918
7176
|
}
|
|
6919
|
-
const broken = snap.paths.filter((p) => !io.exists(
|
|
7177
|
+
const broken = snap.paths.filter((p) => !io.exists(join25(p, ".git")));
|
|
6920
7178
|
if (broken.length) {
|
|
6921
7179
|
return {
|
|
6922
7180
|
id: "repos",
|
|
@@ -7106,12 +7364,12 @@ var init_doctor = __esm({
|
|
|
7106
7364
|
});
|
|
7107
7365
|
|
|
7108
7366
|
// src/repoMemory.ts
|
|
7109
|
-
import { readFileSync as readFileSync18, writeFileSync as
|
|
7110
|
-
import { join as
|
|
7367
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
|
|
7368
|
+
import { join as join26 } from "path";
|
|
7111
7369
|
function loadRememberedRepos(home) {
|
|
7112
7370
|
let raw;
|
|
7113
7371
|
try {
|
|
7114
|
-
raw = readFileSync18(
|
|
7372
|
+
raw = readFileSync18(join26(home, FILE), "utf8");
|
|
7115
7373
|
} catch {
|
|
7116
7374
|
return [];
|
|
7117
7375
|
}
|
|
@@ -7147,7 +7405,7 @@ __export(installCli_exports, {
|
|
|
7147
7405
|
runInstallHookCli: () => runInstallHookCli,
|
|
7148
7406
|
shortenError: () => shortenError
|
|
7149
7407
|
});
|
|
7150
|
-
import { existsSync as
|
|
7408
|
+
import { existsSync as existsSync17 } from "fs";
|
|
7151
7409
|
function classifyChromiumError(message) {
|
|
7152
7410
|
if (/playwright-core не установлен|Cannot find (module|package)/i.test(message)) return "no-module";
|
|
7153
7411
|
if (/error while loading shared libraries|cannot open shared object file|missing dependencies|\blib[\w.+-]*\.so[.\d]*\b/i.test(message)) return "no-libs";
|
|
@@ -7202,7 +7460,7 @@ function defaultDoctorIO(packageVersion) {
|
|
|
7202
7460
|
packageVersion,
|
|
7203
7461
|
entry,
|
|
7204
7462
|
hasCommand: (name) => hasCommandInPath(name),
|
|
7205
|
-
exists:
|
|
7463
|
+
exists: existsSync17,
|
|
7206
7464
|
run: (command) => runQuiet(command),
|
|
7207
7465
|
latestVersion: () => latestMemo ??= fetchLatestVersion(),
|
|
7208
7466
|
probeChromium: () => probeChromium(),
|
|
@@ -7430,8 +7688,8 @@ var init_updateCli = __esm({
|
|
|
7430
7688
|
|
|
7431
7689
|
// src/cli.ts
|
|
7432
7690
|
init_cliIo();
|
|
7433
|
-
import { basename as basename6, dirname as dirname5, join as
|
|
7434
|
-
import { existsSync as
|
|
7691
|
+
import { basename as basename6, dirname as dirname5, join as join27 } from "path";
|
|
7692
|
+
import { existsSync as existsSync18, readFileSync as readFileSync19, writeFileSync as writeFileSync15, mkdirSync as mkdirSync11, readdirSync as readdirSync12, statSync as statSync12, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
|
|
7435
7693
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
7436
7694
|
|
|
7437
7695
|
// src/swarmClient.ts
|
|
@@ -7439,7 +7697,7 @@ init_config();
|
|
|
7439
7697
|
init_resources();
|
|
7440
7698
|
init_names2();
|
|
7441
7699
|
init_storm();
|
|
7442
|
-
import { appendFileSync, existsSync as
|
|
7700
|
+
import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
7443
7701
|
import { dirname as dirname2 } from "path";
|
|
7444
7702
|
import { randomUUID } from "crypto";
|
|
7445
7703
|
|
|
@@ -7472,24 +7730,24 @@ init_store();
|
|
|
7472
7730
|
init_toolFailure();
|
|
7473
7731
|
|
|
7474
7732
|
// src/logTail.ts
|
|
7475
|
-
import { closeSync, existsSync as
|
|
7476
|
-
import { join as
|
|
7733
|
+
import { closeSync, existsSync as existsSync6, openSync, readSync, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
7734
|
+
import { join as join10 } from "path";
|
|
7477
7735
|
|
|
7478
7736
|
// src/runLogDir.ts
|
|
7479
|
-
import { existsSync as
|
|
7480
|
-
import { join as
|
|
7737
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync6, renameSync as renameSync2, statSync as statSync5 } from "fs";
|
|
7738
|
+
import { join as join9 } from "path";
|
|
7481
7739
|
function runLogDirName(runLogKey) {
|
|
7482
7740
|
const safe = runLogKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^[-_]+|[-_]+$/g, "");
|
|
7483
7741
|
if (!safe) throw new Error(`\u043A\u043B\u044E\u0447 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435 \u0433\u043E\u0434\u0438\u0442\u0441\u044F \u0432 \u0438\u043C\u044F \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 run-log'\u043E\u0432: ${JSON.stringify(runLogKey)}`);
|
|
7484
7742
|
return /^\d+$/.test(safe) ? `k-${safe}` : safe;
|
|
7485
7743
|
}
|
|
7486
7744
|
function runLogDirPath(runLogsDir, runLogKey) {
|
|
7487
|
-
return
|
|
7745
|
+
return join9(runLogsDir, runLogDirName(runLogKey));
|
|
7488
7746
|
}
|
|
7489
7747
|
function findRunLogDir(runLogsDir, keys) {
|
|
7490
7748
|
for (const key of keys) {
|
|
7491
7749
|
const dir = runLogDirPath(runLogsDir, key);
|
|
7492
|
-
if (
|
|
7750
|
+
if (existsSync5(dir)) return dir;
|
|
7493
7751
|
}
|
|
7494
7752
|
return null;
|
|
7495
7753
|
}
|
|
@@ -7695,25 +7953,25 @@ var SwarmClient = class _SwarmClient {
|
|
|
7695
7953
|
this.repoFullNames = registry.map((e) => e.repoFullName);
|
|
7696
7954
|
this.repoPathByFullName = new Map(registry.map((e) => [e.repoFullName, normalizeRepo(e.path)]));
|
|
7697
7955
|
this.host = opts.host ?? null;
|
|
7698
|
-
this.repoExists = opts.repoExists ??
|
|
7956
|
+
this.repoExists = opts.repoExists ?? existsSync7;
|
|
7699
7957
|
this.questionSyncMs = opts.questionSyncMs ?? 3e3;
|
|
7700
7958
|
this.executorId = opts.executorId ?? EXECUTOR.id;
|
|
7701
7959
|
this.outboxPath = opts.outboxPath;
|
|
7702
7960
|
this.mapPath = `${opts.outboxPath}.prodmap.json`;
|
|
7703
7961
|
this.stormPath = `${opts.outboxPath}.storm.jsonl`;
|
|
7704
|
-
|
|
7962
|
+
mkdirSync7(dirname2(this.outboxPath), { recursive: true });
|
|
7705
7963
|
this.recover();
|
|
7706
7964
|
}
|
|
7707
7965
|
// ── рестарт: поднять outbox (незаписанные операции) и карту runKey→prodId ─────
|
|
7708
7966
|
recover() {
|
|
7709
|
-
if (
|
|
7967
|
+
if (existsSync7(this.mapPath)) {
|
|
7710
7968
|
try {
|
|
7711
7969
|
const obj = JSON.parse(readFileSync6(this.mapPath, "utf8"));
|
|
7712
7970
|
for (const [k, v] of Object.entries(obj)) this.prodIdByKey.set(k, v);
|
|
7713
7971
|
} catch {
|
|
7714
7972
|
}
|
|
7715
7973
|
}
|
|
7716
|
-
if (
|
|
7974
|
+
if (existsSync7(this.outboxPath)) {
|
|
7717
7975
|
const txt = readFileSync6(this.outboxPath, "utf8");
|
|
7718
7976
|
for (const line of txt.split("\n")) {
|
|
7719
7977
|
const s = line.trim();
|
|
@@ -7734,7 +7992,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
7734
7992
|
// eventsLog ПЕРВЫМИ (до hydrate), в порядке файла (= хронологический); id
|
|
7735
7993
|
// переприсваиваем локально (id событий — только порядок, не корреляция с продом).
|
|
7736
7994
|
replayGlobalEvents() {
|
|
7737
|
-
if (!
|
|
7995
|
+
if (!existsSync7(this.stormPath)) return;
|
|
7738
7996
|
let txt;
|
|
7739
7997
|
try {
|
|
7740
7998
|
txt = readFileSync6(this.stormPath, "utf8");
|
|
@@ -8734,7 +8992,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
8734
8992
|
* транзиентным сбоем ОС, который этот PR должен переживать, а не превращать в мор.
|
|
8735
8993
|
*/
|
|
8736
8994
|
reconcileRuntime(opts) {
|
|
8737
|
-
const wtExists = opts.worktreeExists ??
|
|
8995
|
+
const wtExists = opts.worktreeExists ?? existsSync7;
|
|
8738
8996
|
const pidByWorktree = /* @__PURE__ */ new Map();
|
|
8739
8997
|
try {
|
|
8740
8998
|
for (const p of opts.listProcesses()) if (p.worktree) pidByWorktree.set(p.worktree, p.pid);
|
|
@@ -9354,7 +9612,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9354
9612
|
*
|
|
9355
9613
|
* Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
|
|
9356
9614
|
*/
|
|
9357
|
-
async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors) {
|
|
9615
|
+
async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors, providers) {
|
|
9358
9616
|
try {
|
|
9359
9617
|
const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
|
|
9360
9618
|
if (daemonVersion) args.daemonVersion = daemonVersion;
|
|
@@ -9385,6 +9643,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9385
9643
|
args.tick = { seq: Math.max(0, Math.trunc(tick.seq)), ageMs: Math.max(0, Math.trunc(tick.ageMs)), intervalMs: Math.trunc(tick.intervalMs) };
|
|
9386
9644
|
}
|
|
9387
9645
|
if (executors) args.executorsAvailable = executors;
|
|
9646
|
+
if (providers) args.providersAvailable = providers;
|
|
9388
9647
|
const res = await this.call("swarm_heartbeat", args);
|
|
9389
9648
|
if (res?.isError) return EMPTY_HEARTBEAT_REPLY;
|
|
9390
9649
|
this.heartbeatOkAt = this.now();
|
|
@@ -9972,13 +10231,13 @@ var SwarmClient = class _SwarmClient {
|
|
|
9972
10231
|
}
|
|
9973
10232
|
persistPending() {
|
|
9974
10233
|
try {
|
|
9975
|
-
|
|
10234
|
+
writeFileSync6(this.outboxPath, this.pending.map((e) => JSON.stringify(e)).join("\n") + (this.pending.length ? "\n" : ""));
|
|
9976
10235
|
} catch {
|
|
9977
10236
|
}
|
|
9978
10237
|
}
|
|
9979
10238
|
persistMap() {
|
|
9980
10239
|
try {
|
|
9981
|
-
|
|
10240
|
+
writeFileSync6(this.mapPath, JSON.stringify(Object.fromEntries(this.prodIdByKey)));
|
|
9982
10241
|
} catch {
|
|
9983
10242
|
}
|
|
9984
10243
|
}
|
|
@@ -10178,20 +10437,20 @@ function tailFile(path, lines) {
|
|
|
10178
10437
|
}
|
|
10179
10438
|
|
|
10180
10439
|
// src/logRotate.ts
|
|
10181
|
-
import { copyFileSync as
|
|
10440
|
+
import { copyFileSync as copyFileSync4, renameSync as renameSync3, statSync as statSync7, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync8 } from "fs";
|
|
10182
10441
|
var realIo = {
|
|
10183
10442
|
size: (path) => {
|
|
10184
10443
|
try {
|
|
10185
|
-
return
|
|
10444
|
+
return statSync7(path).size;
|
|
10186
10445
|
} catch {
|
|
10187
10446
|
return null;
|
|
10188
10447
|
}
|
|
10189
10448
|
},
|
|
10190
|
-
copy:
|
|
10449
|
+
copy: copyFileSync4,
|
|
10191
10450
|
rename: renameSync3,
|
|
10192
10451
|
truncate: (path) => truncateSync(path, 0),
|
|
10193
10452
|
unlink: unlinkSync2,
|
|
10194
|
-
exists:
|
|
10453
|
+
exists: existsSync8
|
|
10195
10454
|
};
|
|
10196
10455
|
var STRAY_GENERATIONS = 8;
|
|
10197
10456
|
function rotateDaemonLog(path, opts, io = realIo) {
|
|
@@ -10219,17 +10478,17 @@ init_repoRegistry();
|
|
|
10219
10478
|
|
|
10220
10479
|
// src/hostIdentity.ts
|
|
10221
10480
|
import { hostname } from "os";
|
|
10222
|
-
import { existsSync as
|
|
10223
|
-
import { join as
|
|
10481
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, renameSync as renameSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
10482
|
+
import { join as join11 } from "path";
|
|
10224
10483
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
10225
10484
|
function hostIdentityPath(home) {
|
|
10226
|
-
return
|
|
10485
|
+
return join11(home, "host.json");
|
|
10227
10486
|
}
|
|
10228
10487
|
function loadHostIdentity(deps) {
|
|
10229
|
-
const exists = deps.exists ??
|
|
10488
|
+
const exists = deps.exists ?? existsSync9;
|
|
10230
10489
|
const readFile2 = deps.readFile ?? ((p) => readFileSync8(p, "utf8"));
|
|
10231
10490
|
const writeFile3 = deps.writeFile ?? ((p, data) => {
|
|
10232
|
-
|
|
10491
|
+
writeFileSync7(`${p}.tmp`, data);
|
|
10233
10492
|
renameSync4(`${p}.tmp`, p);
|
|
10234
10493
|
});
|
|
10235
10494
|
const host = deps.hostname ?? hostname;
|
|
@@ -10344,8 +10603,8 @@ import { basename as basename4 } from "path";
|
|
|
10344
10603
|
import { serve as serve2 } from "@hono/node-server";
|
|
10345
10604
|
|
|
10346
10605
|
// src/diff.ts
|
|
10347
|
-
import { statSync as
|
|
10348
|
-
import { join as
|
|
10606
|
+
import { statSync as statSync8 } from "fs";
|
|
10607
|
+
import { join as join12 } from "path";
|
|
10349
10608
|
import { $ as $3 } from "execa";
|
|
10350
10609
|
var MAX_FILES = 200;
|
|
10351
10610
|
var MAX_LINES_PER_FILE = 1500;
|
|
@@ -10488,7 +10747,7 @@ async function resolveBase(wt, baseBranch) {
|
|
|
10488
10747
|
}
|
|
10489
10748
|
function mtime(path) {
|
|
10490
10749
|
try {
|
|
10491
|
-
return
|
|
10750
|
+
return statSync8(path).mtimeMs;
|
|
10492
10751
|
} catch {
|
|
10493
10752
|
return 0;
|
|
10494
10753
|
}
|
|
@@ -10513,7 +10772,7 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
10513
10772
|
files.push(pf);
|
|
10514
10773
|
}
|
|
10515
10774
|
}
|
|
10516
|
-
files.sort((a, b) => mtime(
|
|
10775
|
+
files.sort((a, b) => mtime(join12(worktree, b.path)) - mtime(join12(worktree, a.path)));
|
|
10517
10776
|
const truncatedFiles = files.length > MAX_FILES;
|
|
10518
10777
|
if (truncatedFiles) files = files.slice(0, MAX_FILES);
|
|
10519
10778
|
const added = files.reduce((s, f) => s + f.added, 0);
|
|
@@ -10683,9 +10942,9 @@ function mergeQueueCandidates(runs, repo, repoFallback) {
|
|
|
10683
10942
|
const byPr = /* @__PURE__ */ new Map();
|
|
10684
10943
|
for (const r of runs) {
|
|
10685
10944
|
if (effectiveRepoOf(r.repo, repoFallback) !== repo || !isMergeCandidate(r)) continue;
|
|
10686
|
-
const
|
|
10687
|
-
const seen = byPr.get(
|
|
10688
|
-
if (!seen || r.id > seen.id) byPr.set(
|
|
10945
|
+
const num2 = prNumberOf(r.pr_url);
|
|
10946
|
+
const seen = byPr.get(num2);
|
|
10947
|
+
if (!seen || r.id > seen.id) byPr.set(num2, r);
|
|
10689
10948
|
}
|
|
10690
10949
|
return [...byPr.values()];
|
|
10691
10950
|
}
|
|
@@ -10698,10 +10957,10 @@ function makeMergeQueueResolver(deps) {
|
|
|
10698
10957
|
};
|
|
10699
10958
|
return async (run) => {
|
|
10700
10959
|
if (!deps.baseSync || !run.pr_url) return null;
|
|
10701
|
-
const
|
|
10702
|
-
if (
|
|
10960
|
+
const num2 = prNumberOf(run.pr_url);
|
|
10961
|
+
if (num2 == null) return null;
|
|
10703
10962
|
const candidates = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback);
|
|
10704
|
-
const mine = candidates.find((c) => prNumberOf(c.pr_url) ===
|
|
10963
|
+
const mine = candidates.find((c) => prNumberOf(c.pr_url) === num2);
|
|
10705
10964
|
if (!mine) return null;
|
|
10706
10965
|
const ready = [];
|
|
10707
10966
|
const urlOf = /* @__PURE__ */ new Map();
|
|
@@ -10799,28 +11058,28 @@ function makeMergeIntentResolver(deps) {
|
|
|
10799
11058
|
if (!sha) {
|
|
10800
11059
|
return { action: "wait", code: "checks_unknown", reason: "gh \u043D\u0435 \u0441\u043A\u0430\u0437\u0430\u043B HEAD PR \u2014 \u043D\u0435 \u043F\u043E\u043D\u044F\u0442\u044C, \u043F\u0440\u043E \u043A\u0430\u043A\u043E\u0439 \u043A\u043E\u043C\u043C\u0438\u0442 \u044D\u0442\u043E\u0442 \u0432\u0435\u0440\u0434\u0438\u043A\u0442; \u0441\u043F\u0440\u043E\u0448\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C" };
|
|
10801
11060
|
}
|
|
10802
|
-
const
|
|
10803
|
-
if (!
|
|
11061
|
+
const live2 = deps.baseSync ? await deps.baseSync(prUrl).catch(() => null) : null;
|
|
11062
|
+
if (!live2 || !live2.headSha) {
|
|
10804
11063
|
return { action: "wait", code: "base_unknown", reason: "\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0435\u0440\u0435\u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u0436\u0438\u0432\u043E\u0439 HEAD PR \u2014 \u0441\u043F\u0440\u043E\u0448\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0442\u0438\u043A\u043E\u043C" };
|
|
10805
11064
|
}
|
|
10806
|
-
if (verdict.action === "merge" &&
|
|
11065
|
+
if (verdict.action === "merge" && live2.behindBy > 0) {
|
|
10807
11066
|
return {
|
|
10808
11067
|
action: "refresh_base",
|
|
10809
11068
|
code: "base_stale",
|
|
10810
|
-
reason: `\u0431\u0430\u0437\u0430 \u0443\u0448\u043B\u0430 \u0432\u043F\u0435\u0440\u0451\u0434 \u043D\u0430 ${
|
|
11069
|
+
reason: `\u0431\u0430\u0437\u0430 \u0443\u0448\u043B\u0430 \u0432\u043F\u0435\u0440\u0451\u0434 \u043D\u0430 ${live2.behindBy} \u043A\u043E\u043C\u043C\u0438\u0442(\u043E\u0432) \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0435\u0433\u043E \u043C\u0435\u0440\u0436\u0430 \u2014 \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u0432\u043B\u0438\u0442\u044C main \u0438 \u0434\u043E\u0436\u0434\u0430\u0442\u044C\u0441\u044F CI \u043D\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D\u043D\u043E\u0439 \u0431\u0430\u0437\u0435`
|
|
10811
11070
|
};
|
|
10812
11071
|
}
|
|
10813
|
-
if (
|
|
10814
|
-
return { action: "wait", code: "checks_pending", reason: `\u043D\u0430 PR \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u043C\u043C\u0438\u0442 ${shortSha(
|
|
11072
|
+
if (live2.headSha !== sha) {
|
|
11073
|
+
return { action: "wait", code: "checks_pending", reason: `\u043D\u0430 PR \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u043C\u043C\u0438\u0442 ${shortSha(live2.headSha)} \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u044E \u0437\u0430\u043D\u043E\u0432\u043E` };
|
|
10815
11074
|
}
|
|
10816
11075
|
return verdict;
|
|
10817
11076
|
};
|
|
10818
11077
|
return async (run) => {
|
|
10819
11078
|
if (!deps.baseSync || !run.pr_url || !hasMergeIntent(run)) return null;
|
|
10820
|
-
const
|
|
10821
|
-
if (
|
|
11079
|
+
const num2 = prNumberOf(run.pr_url);
|
|
11080
|
+
if (num2 == null) return null;
|
|
10822
11081
|
const marked = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback).filter(hasMergeIntent);
|
|
10823
|
-
const mine = marked.find((c) => prNumberOf(c.pr_url) ===
|
|
11082
|
+
const mine = marked.find((c) => prNumberOf(c.pr_url) === num2);
|
|
10824
11083
|
if (!mine) return null;
|
|
10825
11084
|
const queue = orderMergeQueue(marked.map(entryOf));
|
|
10826
11085
|
const entry = entryOf(mine);
|
|
@@ -11091,13 +11350,13 @@ function timelineState(db2, now = Date.now(), overmindLog = []) {
|
|
|
11091
11350
|
const lanes = [];
|
|
11092
11351
|
const reviewPRs = [];
|
|
11093
11352
|
for (const r of runs) {
|
|
11094
|
-
const
|
|
11095
|
-
const recencyGated = !
|
|
11353
|
+
const live2 = !isTerminalStatus(r.status);
|
|
11354
|
+
const recencyGated = !live2 || r.status === "stalled";
|
|
11096
11355
|
if (recencyGated && sqliteMs(r.updated_at) < cutoff) continue;
|
|
11097
11356
|
const events = db2.events(r.id).map((e) => ({ kind: e.kind, text: e.text ?? null, at: sqliteIso(e.at) }));
|
|
11098
11357
|
const steps = events.filter((e) => e.kind === "step" && e.text).map((e) => ({ at: e.at, text: e.text }));
|
|
11099
11358
|
const lastStep = steps.length ? steps[steps.length - 1].text : null;
|
|
11100
|
-
const laneEnd =
|
|
11359
|
+
const laneEnd = live2 ? nowIso : sqliteIso(r.updated_at);
|
|
11101
11360
|
const segments = buildSegments(sqliteIso(r.created_at), events, laneEnd);
|
|
11102
11361
|
const deps = (r.depends_on ?? []).map((ref) => {
|
|
11103
11362
|
const { slug, unresolved } = resolveDepRef(ref, { slugByCuid: EMPTY_CUID_MAP, knownSlugs });
|
|
@@ -11119,7 +11378,7 @@ function timelineState(db2, now = Date.now(), overmindLog = []) {
|
|
|
11119
11378
|
ci = ciCache.get(r.pr_url)?.ci ?? null;
|
|
11120
11379
|
}
|
|
11121
11380
|
}
|
|
11122
|
-
const obsoleteBy =
|
|
11381
|
+
const obsoleteBy = live2 && doneNameBySlug.has(r.task_slug) ? doneNameBySlug.get(r.task_slug) : null;
|
|
11123
11382
|
const obsolete = obsoleteBy != null;
|
|
11124
11383
|
const blocker = obsolete ? { icon: "\u{1F9DF}", text: `\u0443\u0441\u0442\u0430\u0440\u0435\u043B \u2014 \u0437\u0430\u0434\u0430\u0447\u0443 \u0437\u0430\u043A\u0440\u044B\u043B ${obsoleteBy}`, tone: "queued" } : computeBlocker(r.status, r.stop_reason, lastStep, r.pr_url, ci, deps, openQ.get(r.id) ?? null);
|
|
11125
11384
|
lanes.push({
|
|
@@ -11131,7 +11390,7 @@ function timelineState(db2, now = Date.now(), overmindLog = []) {
|
|
|
11131
11390
|
area: r.area,
|
|
11132
11391
|
start: sqliteIso(r.created_at),
|
|
11133
11392
|
end: laneEnd,
|
|
11134
|
-
live,
|
|
11393
|
+
live: live2,
|
|
11135
11394
|
obsolete,
|
|
11136
11395
|
obsoleteBy,
|
|
11137
11396
|
segments,
|
|
@@ -12248,7 +12507,7 @@ function setPausedVia(deps, paused, source, reason) {
|
|
|
12248
12507
|
function daemonStatus(deps) {
|
|
12249
12508
|
const counts = {};
|
|
12250
12509
|
for (const r of deps.db.listRuns()) counts[r.status] = (counts[r.status] ?? 0) + 1;
|
|
12251
|
-
const
|
|
12510
|
+
const live2 = deps.liveSessionIds?.() ?? [];
|
|
12252
12511
|
const ver = deps.daemonVersion?.() ?? null;
|
|
12253
12512
|
const queuedReady = deps.db.listRuns("queued").filter((r) => deps.db.unmetDeps(r.depends_on).length === 0).length;
|
|
12254
12513
|
return {
|
|
@@ -12265,9 +12524,9 @@ function daemonStatus(deps) {
|
|
|
12265
12524
|
backend: SWARM_BACKEND,
|
|
12266
12525
|
paused: deps.control?.paused ?? false,
|
|
12267
12526
|
pause: deps.control?.state ?? null,
|
|
12268
|
-
restart: deps.restart ? { phase: deps.restart.phase, intent: deps.restart.intent, sessions:
|
|
12527
|
+
restart: deps.restart ? { phase: deps.restart.phase, intent: deps.restart.intent, sessions: live2 } : null,
|
|
12269
12528
|
counts,
|
|
12270
|
-
live,
|
|
12529
|
+
live: live2,
|
|
12271
12530
|
version: PACKAGE_VERSION,
|
|
12272
12531
|
repos: deps.repoMap?.() ?? [],
|
|
12273
12532
|
// TASWM-324: «нас не пускают» — то же состояние процесса, что кричит рамкой в лог.
|
|
@@ -12537,15 +12796,15 @@ import { cancelRun, respawnRun, runDaemon, wakeRun, OVERMIND_ENTRY, listDronePro
|
|
|
12537
12796
|
|
|
12538
12797
|
// src/launcher.ts
|
|
12539
12798
|
import { spawn as spawn2 } from "child_process";
|
|
12540
|
-
import { closeSync as closeSync3, existsSync as
|
|
12799
|
+
import { closeSync as closeSync3, existsSync as existsSync10, mkdirSync as mkdirSync10, openSync as openSync3, readdirSync as readdirSync9 } from "fs";
|
|
12541
12800
|
import { createRequire } from "module";
|
|
12542
12801
|
import { homedir as homedir5 } from "os";
|
|
12543
|
-
import { dirname as dirname4, join as
|
|
12802
|
+
import { dirname as dirname4, join as join15 } from "path";
|
|
12544
12803
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12545
12804
|
|
|
12546
12805
|
// src/droneHome.ts
|
|
12547
|
-
import { lstatSync, mkdirSync as
|
|
12548
|
-
import { join as
|
|
12806
|
+
import { lstatSync, mkdirSync as mkdirSync9, readdirSync as readdirSync8, rmSync, statSync as statSync9, symlinkSync as symlinkSync3 } from "fs";
|
|
12807
|
+
import { join as join14, relative, sep } from "path";
|
|
12549
12808
|
var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
|
|
12550
12809
|
function droneHomeDeny(env = process.env) {
|
|
12551
12810
|
const extra = (env.SWARM_DRONE_HOME_DENY || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -12563,34 +12822,34 @@ function buildDroneHome(opts) {
|
|
|
12563
12822
|
const { realHome, droneHome } = opts;
|
|
12564
12823
|
const deny = opts.deny ?? droneHomeDeny();
|
|
12565
12824
|
const platform = opts.platform ?? process.platform;
|
|
12566
|
-
|
|
12567
|
-
const wanted = new Set(mirroredEntries(
|
|
12825
|
+
mkdirSync9(droneHome, { recursive: true, mode: 448 });
|
|
12826
|
+
const wanted = new Set(mirroredEntries(readdirSync8(realHome), deny, platform));
|
|
12568
12827
|
const rel = relative(realHome, droneHome);
|
|
12569
12828
|
if (rel && !rel.startsWith("..") && !rel.includes(sep)) wanted.delete(rel);
|
|
12570
|
-
for (const existing of
|
|
12829
|
+
for (const existing of readdirSync8(droneHome)) {
|
|
12571
12830
|
if (wanted.has(existing)) continue;
|
|
12572
|
-
const p =
|
|
12831
|
+
const p = join14(droneHome, existing);
|
|
12573
12832
|
try {
|
|
12574
12833
|
if (lstatSync(p).isSymbolicLink()) rmSync(p);
|
|
12575
12834
|
} catch {
|
|
12576
12835
|
}
|
|
12577
12836
|
}
|
|
12578
12837
|
for (const name of wanted) {
|
|
12579
|
-
const link =
|
|
12838
|
+
const link = join14(droneHome, name);
|
|
12580
12839
|
try {
|
|
12581
12840
|
lstatSync(link);
|
|
12582
12841
|
continue;
|
|
12583
12842
|
} catch {
|
|
12584
12843
|
}
|
|
12585
|
-
const target =
|
|
12844
|
+
const target = join14(realHome, name);
|
|
12586
12845
|
let isDir;
|
|
12587
12846
|
try {
|
|
12588
|
-
isDir =
|
|
12847
|
+
isDir = statSync9(target).isDirectory();
|
|
12589
12848
|
} catch {
|
|
12590
12849
|
continue;
|
|
12591
12850
|
}
|
|
12592
12851
|
try {
|
|
12593
|
-
|
|
12852
|
+
symlinkSync3(target, link, isDir ? "junction" : "file");
|
|
12594
12853
|
} catch (e) {
|
|
12595
12854
|
if (e.code !== "EEXIST") throw e;
|
|
12596
12855
|
}
|
|
@@ -12603,7 +12862,7 @@ var REAL_PATH_ENV_PINS = [
|
|
|
12603
12862
|
{ env: "YARN_GLOBAL_FOLDER", segments: [".yarn", "berry"] }
|
|
12604
12863
|
];
|
|
12605
12864
|
function realPathEnvPins(realHome) {
|
|
12606
|
-
return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env,
|
|
12865
|
+
return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join14(realHome, ...segments)]));
|
|
12607
12866
|
}
|
|
12608
12867
|
function dronePrivatePaths(realHome, swarmHome) {
|
|
12609
12868
|
return [
|
|
@@ -12686,7 +12945,7 @@ function droneEnv(worktree, base3 = process.env, isolation, droneCap = null) {
|
|
|
12686
12945
|
const env = { ...base3 };
|
|
12687
12946
|
const isolate = base3.SWARM_TURBO_ISOLATE !== "0";
|
|
12688
12947
|
if (isolate && !base3.TURBO_CACHE_DIR) {
|
|
12689
|
-
env.TURBO_CACHE_DIR =
|
|
12948
|
+
env.TURBO_CACHE_DIR = join15(worktree, ".turbo", "cache");
|
|
12690
12949
|
}
|
|
12691
12950
|
Object.assign(env, droneTestEnv(droneCap, base3));
|
|
12692
12951
|
for (const key of DAEMON_STATE_ENV) delete env[key];
|
|
@@ -12717,21 +12976,21 @@ var DAEMON_STATE_ENV = [
|
|
|
12717
12976
|
...PROVIDER_TOKEN_ENV
|
|
12718
12977
|
];
|
|
12719
12978
|
function bundledRoleDir(kind) {
|
|
12720
|
-
return
|
|
12979
|
+
return join15(dirname4(fileURLToPath2(import.meta.url)), "..", "roles", kind);
|
|
12721
12980
|
}
|
|
12722
12981
|
function roleDir(worktree, kind) {
|
|
12723
|
-
return
|
|
12982
|
+
return join15(worktree, ".claude", "roles", kind);
|
|
12724
12983
|
}
|
|
12725
12984
|
function roleDirs(worktree, kind) {
|
|
12726
|
-
return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) =>
|
|
12985
|
+
return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync10(dir));
|
|
12727
12986
|
}
|
|
12728
12987
|
function nextRunLogPath(runLogsDir, runLogKeys) {
|
|
12729
12988
|
const dir = runLogDirForWrite(runLogsDir, runLogKeys);
|
|
12730
12989
|
if (!dir) throw new Error("\u043D\u0435\u0447\u0435\u043C \u0430\u0434\u0440\u0435\u0441\u043E\u0432\u0430\u0442\u044C \u043A\u0430\u0442\u0430\u043B\u043E\u0433 run-log'\u043E\u0432: \u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435\u0442 \u0441\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u043B\u044E\u0447\u0430 (SwarmStore.runLogKeys)");
|
|
12731
|
-
|
|
12990
|
+
mkdirSync10(dir, { recursive: true });
|
|
12732
12991
|
let files = [];
|
|
12733
12992
|
try {
|
|
12734
|
-
files =
|
|
12993
|
+
files = readdirSync9(dir);
|
|
12735
12994
|
} catch {
|
|
12736
12995
|
}
|
|
12737
12996
|
let next = 0;
|
|
@@ -12739,7 +12998,7 @@ function nextRunLogPath(runLogsDir, runLogKeys) {
|
|
|
12739
12998
|
const m = /^(\d+)\.json/.exec(f);
|
|
12740
12999
|
if (m) next = Math.max(next, Number(m[1]) + 1);
|
|
12741
13000
|
}
|
|
12742
|
-
return
|
|
13001
|
+
return join15(dir, `${next}.json`);
|
|
12743
13002
|
}
|
|
12744
13003
|
function openNextRunLog(runLogsDir, runLogKeys) {
|
|
12745
13004
|
for (let attempt = 0; attempt < 50; attempt++) {
|
|
@@ -12825,15 +13084,15 @@ function makeLauncher(cfg) {
|
|
|
12825
13084
|
console.error(`[launcher] \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0443\u0441\u0438\u043B\u0438\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run.id}: ${String(e)}`);
|
|
12826
13085
|
}
|
|
12827
13086
|
try {
|
|
12828
|
-
cfg.recordModelRef?.(run.id, modelRef);
|
|
13087
|
+
cfg.recordModelRef?.(run.id, modelRef, run);
|
|
12829
13088
|
} catch (e) {
|
|
12830
13089
|
console.error(`[launcher] \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u043C\u043E\u0434\u0435\u043B\u044C \u043F\u0440\u043E\u0433\u043E\u043D\u0430 #${run.id}: ${String(e)}`);
|
|
12831
13090
|
}
|
|
12832
13091
|
const realHome = cfg.droneIsolation?.realHome ?? homedir5();
|
|
12833
13092
|
const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
|
|
12834
|
-
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !
|
|
13093
|
+
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync10(join15(cfg.droneIsolation.swarmHome, "token"))) {
|
|
12835
13094
|
console.error(
|
|
12836
|
-
`[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${
|
|
13095
|
+
`[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${join15(cfg.droneIsolation.swarmHome, "token")} \u043D\u0435\u0442. read_task \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 slug-only. \u041F\u043E\u0447\u0438\u043D\u0438: \`swarm login\` (oauth.json) \u0438\u043B\u0438 \u043F\u043E\u043B\u043E\u0436\u0438 \u0442\u043E\u043A\u0435\u043D \u0432 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B.`
|
|
12837
13096
|
);
|
|
12838
13097
|
}
|
|
12839
13098
|
const executor = cfg.executor ?? executorForRun(run, cfg.swarmModels?.() ?? null);
|
|
@@ -13732,7 +13991,7 @@ async function runDaemonLogs(argv) {
|
|
|
13732
13991
|
console.error(`\u043B\u043E\u0433 \u041F\u0420\u041E\u0413\u041E\u041D\u0410 \u2014 \u044D\u0442\u043E \xABswarm log ${stray}\xBB (\u0431\u0435\u0437 s). \xABswarm logs\xBB \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u043B\u043E\u0433 \u0434\u0435\u043C\u043E\u043D\u0430; \u0447\u0438\u0441\u043B\u043E \u0441\u0442\u0440\u043E\u043A \u2014 \xABswarm logs -n 200\xBB.`);
|
|
13733
13992
|
return 1;
|
|
13734
13993
|
}
|
|
13735
|
-
if (!
|
|
13994
|
+
if (!existsSync18(DAEMON_LOG_PATH)) {
|
|
13736
13995
|
console.error(`\u043B\u043E\u0433\u0430 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442: ${DAEMON_LOG_PATH}`);
|
|
13737
13996
|
console.error("\u043E\u043D \u043F\u0438\u0448\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u0444\u043E\u043D\u043E\u0432\u043E\u043C \u0437\u0430\u043F\u0443\u0441\u043A\u0435 (swarm start). \u0414\u0435\u043C\u043E\u043D \u043F\u043E\u0434\u043D\u044F\u0442 \u0432 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435 (--foreground) \u2014 \u0432\u044B\u0432\u043E\u0434 \u0438\u0434\u0451\u0442 \u0442\u0443\u0434\u0430.");
|
|
13738
13997
|
return 1;
|
|
@@ -13740,12 +13999,12 @@ async function runDaemonLogs(argv) {
|
|
|
13740
13999
|
const head = tailFile(DAEMON_LOG_PATH, lines);
|
|
13741
14000
|
if (head) console.log(head);
|
|
13742
14001
|
if (!follow) return 0;
|
|
13743
|
-
let pos =
|
|
14002
|
+
let pos = statSync12(DAEMON_LOG_PATH).size;
|
|
13744
14003
|
for (; ; ) {
|
|
13745
14004
|
await sleep2(500);
|
|
13746
14005
|
let size;
|
|
13747
14006
|
try {
|
|
13748
|
-
size =
|
|
14007
|
+
size = statSync12(DAEMON_LOG_PATH).size;
|
|
13749
14008
|
} catch {
|
|
13750
14009
|
continue;
|
|
13751
14010
|
}
|
|
@@ -14014,10 +14273,10 @@ function effectiveWorktreesDir() {
|
|
|
14014
14273
|
legacyDir: WORKTREES_LEGACY_DIR,
|
|
14015
14274
|
targetDir: WORKTREES_DIR,
|
|
14016
14275
|
envOverride: process.env.SWARM_WORKTREES || "",
|
|
14017
|
-
exists: (d) =>
|
|
14276
|
+
exists: (d) => existsSync18(d),
|
|
14018
14277
|
isNonEmptyDir: (d) => {
|
|
14019
14278
|
try {
|
|
14020
|
-
return
|
|
14279
|
+
return readdirSync12(d).length > 0;
|
|
14021
14280
|
} catch {
|
|
14022
14281
|
return false;
|
|
14023
14282
|
}
|
|
@@ -14073,15 +14332,15 @@ async function scoutRepo(repo) {
|
|
|
14073
14332
|
await $6({ reject: false })`git -C ${worktree} reset --hard ${baseRef}`;
|
|
14074
14333
|
}
|
|
14075
14334
|
await setWorktreeIdentity(worktree, name);
|
|
14076
|
-
const abs = (rel) =>
|
|
14335
|
+
const abs = (rel) => join27(worktree, rel);
|
|
14077
14336
|
const code = await runRepoScout({
|
|
14078
14337
|
repoFullName,
|
|
14079
14338
|
repoPath: worktree,
|
|
14080
14339
|
name,
|
|
14081
|
-
readFile: (rel) =>
|
|
14340
|
+
readFile: (rel) => existsSync18(abs(rel)) ? readFileSync19(abs(rel), "utf8") : null,
|
|
14082
14341
|
writeFile: (rel, text) => {
|
|
14083
|
-
|
|
14084
|
-
|
|
14342
|
+
mkdirSync11(dirname5(abs(rel)), { recursive: true });
|
|
14343
|
+
writeFileSync15(abs(rel), text);
|
|
14085
14344
|
},
|
|
14086
14345
|
runScout: (prompt) => new Promise((resolve5) => {
|
|
14087
14346
|
const child = spawn3(bin, [...ISOLATION_FLAGS, "--dangerously-skip-permissions", "-p", prompt], {
|