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