@taskless-app/swarm 0.6.62 → 0.6.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +421 -182
- package/dist/overmind.js +571 -310
- 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 {
|
|
@@ -5600,7 +5836,7 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5600
5836
|
const repoN = normalizeRepo(repo);
|
|
5601
5837
|
const already = this.db.prepare("SELECT run_count FROM imports WHERE source = ?").get(abs);
|
|
5602
5838
|
if (already) return { imported: false, runs: already.run_count };
|
|
5603
|
-
if (!
|
|
5839
|
+
if (!existsSync13(abs)) throw new Error(`import-db: \u0444\u0430\u0439\u043B\u0430 \u043D\u0435\u0442: ${abs}`);
|
|
5604
5840
|
const src = new this.databaseCtor(abs);
|
|
5605
5841
|
try {
|
|
5606
5842
|
const runs = src.prepare("SELECT * FROM runs ORDER BY id").all();
|
|
@@ -5928,8 +6164,8 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
5928
6164
|
});
|
|
5929
6165
|
|
|
5930
6166
|
// src/repoScan.ts
|
|
5931
|
-
import { readdirSync as
|
|
5932
|
-
import { join as
|
|
6167
|
+
import { readdirSync as readdirSync11, readFileSync as readFileSync14, statSync as statSync11 } from "fs";
|
|
6168
|
+
import { join as join21 } from "path";
|
|
5933
6169
|
var init_repoScan = __esm({
|
|
5934
6170
|
"src/repoScan.ts"() {
|
|
5935
6171
|
"use strict";
|
|
@@ -5939,8 +6175,8 @@ var init_repoScan = __esm({
|
|
|
5939
6175
|
|
|
5940
6176
|
// src/repoDiscovery.ts
|
|
5941
6177
|
import { homedir as homedir6 } from "os";
|
|
5942
|
-
import { readFileSync as readFileSync15, renameSync as renameSync7, writeFileSync as
|
|
5943
|
-
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";
|
|
5944
6180
|
function repoMapLines(rows) {
|
|
5945
6181
|
if (!rows?.length) return [];
|
|
5946
6182
|
const missing = rows.filter((r) => !r.path).length;
|
|
@@ -6331,10 +6567,10 @@ __export(statusCli_exports, {
|
|
|
6331
6567
|
probeLink: () => probeLink,
|
|
6332
6568
|
runStatusCli: () => runStatusCli
|
|
6333
6569
|
});
|
|
6334
|
-
import { existsSync as
|
|
6570
|
+
import { existsSync as existsSync14, readFileSync as readFileSync16 } from "fs";
|
|
6335
6571
|
function outboxPending(path) {
|
|
6336
6572
|
try {
|
|
6337
|
-
if (!
|
|
6573
|
+
if (!existsSync14(path)) return 0;
|
|
6338
6574
|
return readFileSync16(path, "utf8").split("\n").filter((l) => l.trim()).length;
|
|
6339
6575
|
} catch {
|
|
6340
6576
|
return null;
|
|
@@ -6542,8 +6778,8 @@ var init_prodMergeGate = __esm({
|
|
|
6542
6778
|
});
|
|
6543
6779
|
|
|
6544
6780
|
// src/platform.ts
|
|
6545
|
-
import { existsSync as
|
|
6546
|
-
import { delimiter, join as
|
|
6781
|
+
import { existsSync as existsSync15, readFileSync as readFileSync17 } from "fs";
|
|
6782
|
+
import { delimiter, join as join23 } from "path";
|
|
6547
6783
|
function safeReadText(path) {
|
|
6548
6784
|
try {
|
|
6549
6785
|
return readFileSync17(path, "utf8");
|
|
@@ -6552,13 +6788,13 @@ function safeReadText(path) {
|
|
|
6552
6788
|
}
|
|
6553
6789
|
}
|
|
6554
6790
|
function resolveCommandPath(name, env = process.env, platform = process.platform) {
|
|
6555
|
-
if (name.includes("/") || name.includes("\\")) return
|
|
6791
|
+
if (name.includes("/") || name.includes("\\")) return existsSync15(name) ? name : null;
|
|
6556
6792
|
const dirs = (env.PATH ?? env.Path ?? "").split(delimiter).filter(Boolean);
|
|
6557
6793
|
const exts = platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
|
|
6558
6794
|
for (const dir of dirs) {
|
|
6559
6795
|
for (const ext of exts) {
|
|
6560
|
-
const candidate =
|
|
6561
|
-
if (
|
|
6796
|
+
const candidate = join23(dir, name + ext);
|
|
6797
|
+
if (existsSync15(candidate)) return candidate;
|
|
6562
6798
|
}
|
|
6563
6799
|
}
|
|
6564
6800
|
return null;
|
|
@@ -6661,13 +6897,13 @@ var init_platform = __esm({
|
|
|
6661
6897
|
});
|
|
6662
6898
|
|
|
6663
6899
|
// src/envGate.ts
|
|
6664
|
-
import { existsSync as
|
|
6665
|
-
import { join as
|
|
6900
|
+
import { existsSync as existsSync16 } from "fs";
|
|
6901
|
+
import { join as join24 } from "path";
|
|
6666
6902
|
import { createConnection } from "net";
|
|
6667
6903
|
import { execa as execa3 } from "execa";
|
|
6668
6904
|
function detectRequirements(repo, io = {}) {
|
|
6669
|
-
const exists = io.exists ??
|
|
6670
|
-
const marker = COMPOSE_MARKERS.find((f) => exists(
|
|
6905
|
+
const exists = io.exists ?? existsSync16;
|
|
6906
|
+
const marker = COMPOSE_MARKERS.find((f) => exists(join24(repo, f)));
|
|
6671
6907
|
return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
|
|
6672
6908
|
}
|
|
6673
6909
|
async function dockerAlive() {
|
|
@@ -6699,7 +6935,7 @@ var init_envGate = __esm({
|
|
|
6699
6935
|
});
|
|
6700
6936
|
|
|
6701
6937
|
// src/doctor.ts
|
|
6702
|
-
import { isAbsolute as isAbsolute4, join as
|
|
6938
|
+
import { isAbsolute as isAbsolute4, join as join25 } from "path";
|
|
6703
6939
|
function ok(id, title, detail) {
|
|
6704
6940
|
return { id, title, status: "ok", detail, remedies: [], critical: false };
|
|
6705
6941
|
}
|
|
@@ -6724,12 +6960,14 @@ function checkExecutor(io, executor = resolveExecutor()) {
|
|
|
6724
6960
|
const bin = executor.resolveBin();
|
|
6725
6961
|
const where = /[/\\]/.test(bin) ? `\u043D\u0430\u0439\u0434\u0435\u043D: ${bin}` : "\u043D\u0430\u0439\u0434\u0435\u043D \u0432 PATH";
|
|
6726
6962
|
if (io.hasCommand(bin)) return ok("claude", executor.title, where);
|
|
6963
|
+
const custom = executor.install.command == null ? null : io.platform.kind === "windows" && executor.install.windowsCommand ? executor.install.windowsCommand : executor.install.command;
|
|
6727
6964
|
const remedies = [
|
|
6728
6965
|
{
|
|
6729
6966
|
what: executor.install.what,
|
|
6730
|
-
command: globalInstallCommand(io.globalPm === "sources" ? "npm" : io.globalPm, executor.install.pkg),
|
|
6967
|
+
command: custom ?? globalInstallCommand(io.globalPm === "sources" ? "npm" : io.globalPm, executor.install.pkg),
|
|
6731
6968
|
sudo: false,
|
|
6732
|
-
|
|
6969
|
+
// Свой инсталлер (не npm-пакет) человек ставит сам: автозапуск скрипта с сайта — нет.
|
|
6970
|
+
auto: custom == null
|
|
6733
6971
|
}
|
|
6734
6972
|
];
|
|
6735
6973
|
const detail = io.platform.kind === "windows" ? `\u0432 PATH \u043D\u0435\u0442 \`${bin}\`. \u041D\u0430 Windows spawn \u0438\u0449\u0435\u0442 \u0444\u0430\u0439\u043B \u043F\u043E PATHEXT \u2014 bash-\u043E\u0431\u0451\u0440\u0442\u043A\u0430 \u0431\u0435\u0437 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u0435\u043C\u0443 \u043D\u0435 \u0432\u0438\u0434\u043D\u0430; \u043D\u0435\u0441\u0442\u0430\u043D\u0434\u0430\u0440\u0442\u043D\u0430\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u0437\u0430\u0434\u0430\u0451\u0442\u0441\u044F ${executor.binEnv}` : `\u0432 PATH \u043D\u0435\u0442 \`${bin}\` \u2014 \u0434\u0440\u043E\u043D\u0443 \u043D\u0435\u0447\u0435\u043C \u043F\u0438\u0441\u0430\u0442\u044C \u043A\u043E\u0434`;
|
|
@@ -6906,7 +7144,7 @@ function checkRepos(io, snap) {
|
|
|
6906
7144
|
critical: true
|
|
6907
7145
|
};
|
|
6908
7146
|
}
|
|
6909
|
-
if (env && !io.exists(
|
|
7147
|
+
if (env && !io.exists(join25(env, ".git"))) {
|
|
6910
7148
|
return {
|
|
6911
7149
|
id: "repos",
|
|
6912
7150
|
title,
|
|
@@ -6916,7 +7154,7 @@ function checkRepos(io, snap) {
|
|
|
6916
7154
|
critical: true
|
|
6917
7155
|
};
|
|
6918
7156
|
}
|
|
6919
|
-
const broken = snap.paths.filter((p) => !io.exists(
|
|
7157
|
+
const broken = snap.paths.filter((p) => !io.exists(join25(p, ".git")));
|
|
6920
7158
|
if (broken.length) {
|
|
6921
7159
|
return {
|
|
6922
7160
|
id: "repos",
|
|
@@ -7106,12 +7344,12 @@ var init_doctor = __esm({
|
|
|
7106
7344
|
});
|
|
7107
7345
|
|
|
7108
7346
|
// src/repoMemory.ts
|
|
7109
|
-
import { readFileSync as readFileSync18, writeFileSync as
|
|
7110
|
-
import { join as
|
|
7347
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
|
|
7348
|
+
import { join as join26 } from "path";
|
|
7111
7349
|
function loadRememberedRepos(home) {
|
|
7112
7350
|
let raw;
|
|
7113
7351
|
try {
|
|
7114
|
-
raw = readFileSync18(
|
|
7352
|
+
raw = readFileSync18(join26(home, FILE), "utf8");
|
|
7115
7353
|
} catch {
|
|
7116
7354
|
return [];
|
|
7117
7355
|
}
|
|
@@ -7147,7 +7385,7 @@ __export(installCli_exports, {
|
|
|
7147
7385
|
runInstallHookCli: () => runInstallHookCli,
|
|
7148
7386
|
shortenError: () => shortenError
|
|
7149
7387
|
});
|
|
7150
|
-
import { existsSync as
|
|
7388
|
+
import { existsSync as existsSync17 } from "fs";
|
|
7151
7389
|
function classifyChromiumError(message) {
|
|
7152
7390
|
if (/playwright-core не установлен|Cannot find (module|package)/i.test(message)) return "no-module";
|
|
7153
7391
|
if (/error while loading shared libraries|cannot open shared object file|missing dependencies|\blib[\w.+-]*\.so[.\d]*\b/i.test(message)) return "no-libs";
|
|
@@ -7202,7 +7440,7 @@ function defaultDoctorIO(packageVersion) {
|
|
|
7202
7440
|
packageVersion,
|
|
7203
7441
|
entry,
|
|
7204
7442
|
hasCommand: (name) => hasCommandInPath(name),
|
|
7205
|
-
exists:
|
|
7443
|
+
exists: existsSync17,
|
|
7206
7444
|
run: (command) => runQuiet(command),
|
|
7207
7445
|
latestVersion: () => latestMemo ??= fetchLatestVersion(),
|
|
7208
7446
|
probeChromium: () => probeChromium(),
|
|
@@ -7430,8 +7668,8 @@ var init_updateCli = __esm({
|
|
|
7430
7668
|
|
|
7431
7669
|
// src/cli.ts
|
|
7432
7670
|
init_cliIo();
|
|
7433
|
-
import { basename as basename6, dirname as dirname5, join as
|
|
7434
|
-
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";
|
|
7435
7673
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
7436
7674
|
|
|
7437
7675
|
// src/swarmClient.ts
|
|
@@ -7439,7 +7677,7 @@ init_config();
|
|
|
7439
7677
|
init_resources();
|
|
7440
7678
|
init_names2();
|
|
7441
7679
|
init_storm();
|
|
7442
|
-
import { appendFileSync, existsSync as
|
|
7680
|
+
import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
7443
7681
|
import { dirname as dirname2 } from "path";
|
|
7444
7682
|
import { randomUUID } from "crypto";
|
|
7445
7683
|
|
|
@@ -7472,24 +7710,24 @@ init_store();
|
|
|
7472
7710
|
init_toolFailure();
|
|
7473
7711
|
|
|
7474
7712
|
// src/logTail.ts
|
|
7475
|
-
import { closeSync, existsSync as
|
|
7476
|
-
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";
|
|
7477
7715
|
|
|
7478
7716
|
// src/runLogDir.ts
|
|
7479
|
-
import { existsSync as
|
|
7480
|
-
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";
|
|
7481
7719
|
function runLogDirName(runLogKey) {
|
|
7482
7720
|
const safe = runLogKey.replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^[-_]+|[-_]+$/g, "");
|
|
7483
7721
|
if (!safe) throw new Error(`\u043A\u043B\u044E\u0447 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435 \u0433\u043E\u0434\u0438\u0442\u0441\u044F \u0432 \u0438\u043C\u044F \u043A\u0430\u0442\u0430\u043B\u043E\u0433\u0430 run-log'\u043E\u0432: ${JSON.stringify(runLogKey)}`);
|
|
7484
7722
|
return /^\d+$/.test(safe) ? `k-${safe}` : safe;
|
|
7485
7723
|
}
|
|
7486
7724
|
function runLogDirPath(runLogsDir, runLogKey) {
|
|
7487
|
-
return
|
|
7725
|
+
return join9(runLogsDir, runLogDirName(runLogKey));
|
|
7488
7726
|
}
|
|
7489
7727
|
function findRunLogDir(runLogsDir, keys) {
|
|
7490
7728
|
for (const key of keys) {
|
|
7491
7729
|
const dir = runLogDirPath(runLogsDir, key);
|
|
7492
|
-
if (
|
|
7730
|
+
if (existsSync5(dir)) return dir;
|
|
7493
7731
|
}
|
|
7494
7732
|
return null;
|
|
7495
7733
|
}
|
|
@@ -7695,25 +7933,25 @@ var SwarmClient = class _SwarmClient {
|
|
|
7695
7933
|
this.repoFullNames = registry.map((e) => e.repoFullName);
|
|
7696
7934
|
this.repoPathByFullName = new Map(registry.map((e) => [e.repoFullName, normalizeRepo(e.path)]));
|
|
7697
7935
|
this.host = opts.host ?? null;
|
|
7698
|
-
this.repoExists = opts.repoExists ??
|
|
7936
|
+
this.repoExists = opts.repoExists ?? existsSync7;
|
|
7699
7937
|
this.questionSyncMs = opts.questionSyncMs ?? 3e3;
|
|
7700
7938
|
this.executorId = opts.executorId ?? EXECUTOR.id;
|
|
7701
7939
|
this.outboxPath = opts.outboxPath;
|
|
7702
7940
|
this.mapPath = `${opts.outboxPath}.prodmap.json`;
|
|
7703
7941
|
this.stormPath = `${opts.outboxPath}.storm.jsonl`;
|
|
7704
|
-
|
|
7942
|
+
mkdirSync7(dirname2(this.outboxPath), { recursive: true });
|
|
7705
7943
|
this.recover();
|
|
7706
7944
|
}
|
|
7707
7945
|
// ── рестарт: поднять outbox (незаписанные операции) и карту runKey→prodId ─────
|
|
7708
7946
|
recover() {
|
|
7709
|
-
if (
|
|
7947
|
+
if (existsSync7(this.mapPath)) {
|
|
7710
7948
|
try {
|
|
7711
7949
|
const obj = JSON.parse(readFileSync6(this.mapPath, "utf8"));
|
|
7712
7950
|
for (const [k, v] of Object.entries(obj)) this.prodIdByKey.set(k, v);
|
|
7713
7951
|
} catch {
|
|
7714
7952
|
}
|
|
7715
7953
|
}
|
|
7716
|
-
if (
|
|
7954
|
+
if (existsSync7(this.outboxPath)) {
|
|
7717
7955
|
const txt = readFileSync6(this.outboxPath, "utf8");
|
|
7718
7956
|
for (const line of txt.split("\n")) {
|
|
7719
7957
|
const s = line.trim();
|
|
@@ -7734,7 +7972,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
7734
7972
|
// eventsLog ПЕРВЫМИ (до hydrate), в порядке файла (= хронологический); id
|
|
7735
7973
|
// переприсваиваем локально (id событий — только порядок, не корреляция с продом).
|
|
7736
7974
|
replayGlobalEvents() {
|
|
7737
|
-
if (!
|
|
7975
|
+
if (!existsSync7(this.stormPath)) return;
|
|
7738
7976
|
let txt;
|
|
7739
7977
|
try {
|
|
7740
7978
|
txt = readFileSync6(this.stormPath, "utf8");
|
|
@@ -8734,7 +8972,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
8734
8972
|
* транзиентным сбоем ОС, который этот PR должен переживать, а не превращать в мор.
|
|
8735
8973
|
*/
|
|
8736
8974
|
reconcileRuntime(opts) {
|
|
8737
|
-
const wtExists = opts.worktreeExists ??
|
|
8975
|
+
const wtExists = opts.worktreeExists ?? existsSync7;
|
|
8738
8976
|
const pidByWorktree = /* @__PURE__ */ new Map();
|
|
8739
8977
|
try {
|
|
8740
8978
|
for (const p of opts.listProcesses()) if (p.worktree) pidByWorktree.set(p.worktree, p.pid);
|
|
@@ -9354,7 +9592,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9354
9592
|
*
|
|
9355
9593
|
* Прод недоступен / ответ без поручений → пустые списки (демон работает дальше).
|
|
9356
9594
|
*/
|
|
9357
|
-
async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors) {
|
|
9595
|
+
async heartbeat(daemonVersion, pause, capacity, resources, storm, worktreesDir, signIn, tick, executors, providers) {
|
|
9358
9596
|
try {
|
|
9359
9597
|
const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
|
|
9360
9598
|
if (daemonVersion) args.daemonVersion = daemonVersion;
|
|
@@ -9385,6 +9623,7 @@ var SwarmClient = class _SwarmClient {
|
|
|
9385
9623
|
args.tick = { seq: Math.max(0, Math.trunc(tick.seq)), ageMs: Math.max(0, Math.trunc(tick.ageMs)), intervalMs: Math.trunc(tick.intervalMs) };
|
|
9386
9624
|
}
|
|
9387
9625
|
if (executors) args.executorsAvailable = executors;
|
|
9626
|
+
if (providers) args.providersAvailable = providers;
|
|
9388
9627
|
const res = await this.call("swarm_heartbeat", args);
|
|
9389
9628
|
if (res?.isError) return EMPTY_HEARTBEAT_REPLY;
|
|
9390
9629
|
this.heartbeatOkAt = this.now();
|
|
@@ -9972,13 +10211,13 @@ var SwarmClient = class _SwarmClient {
|
|
|
9972
10211
|
}
|
|
9973
10212
|
persistPending() {
|
|
9974
10213
|
try {
|
|
9975
|
-
|
|
10214
|
+
writeFileSync6(this.outboxPath, this.pending.map((e) => JSON.stringify(e)).join("\n") + (this.pending.length ? "\n" : ""));
|
|
9976
10215
|
} catch {
|
|
9977
10216
|
}
|
|
9978
10217
|
}
|
|
9979
10218
|
persistMap() {
|
|
9980
10219
|
try {
|
|
9981
|
-
|
|
10220
|
+
writeFileSync6(this.mapPath, JSON.stringify(Object.fromEntries(this.prodIdByKey)));
|
|
9982
10221
|
} catch {
|
|
9983
10222
|
}
|
|
9984
10223
|
}
|
|
@@ -10178,20 +10417,20 @@ function tailFile(path, lines) {
|
|
|
10178
10417
|
}
|
|
10179
10418
|
|
|
10180
10419
|
// src/logRotate.ts
|
|
10181
|
-
import { copyFileSync as
|
|
10420
|
+
import { copyFileSync as copyFileSync4, renameSync as renameSync3, statSync as statSync7, fstatSync, truncateSync, unlinkSync as unlinkSync2, existsSync as existsSync8 } from "fs";
|
|
10182
10421
|
var realIo = {
|
|
10183
10422
|
size: (path) => {
|
|
10184
10423
|
try {
|
|
10185
|
-
return
|
|
10424
|
+
return statSync7(path).size;
|
|
10186
10425
|
} catch {
|
|
10187
10426
|
return null;
|
|
10188
10427
|
}
|
|
10189
10428
|
},
|
|
10190
|
-
copy:
|
|
10429
|
+
copy: copyFileSync4,
|
|
10191
10430
|
rename: renameSync3,
|
|
10192
10431
|
truncate: (path) => truncateSync(path, 0),
|
|
10193
10432
|
unlink: unlinkSync2,
|
|
10194
|
-
exists:
|
|
10433
|
+
exists: existsSync8
|
|
10195
10434
|
};
|
|
10196
10435
|
var STRAY_GENERATIONS = 8;
|
|
10197
10436
|
function rotateDaemonLog(path, opts, io = realIo) {
|
|
@@ -10219,17 +10458,17 @@ init_repoRegistry();
|
|
|
10219
10458
|
|
|
10220
10459
|
// src/hostIdentity.ts
|
|
10221
10460
|
import { hostname } from "os";
|
|
10222
|
-
import { existsSync as
|
|
10223
|
-
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";
|
|
10224
10463
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
10225
10464
|
function hostIdentityPath(home) {
|
|
10226
|
-
return
|
|
10465
|
+
return join11(home, "host.json");
|
|
10227
10466
|
}
|
|
10228
10467
|
function loadHostIdentity(deps) {
|
|
10229
|
-
const exists = deps.exists ??
|
|
10468
|
+
const exists = deps.exists ?? existsSync9;
|
|
10230
10469
|
const readFile2 = deps.readFile ?? ((p) => readFileSync8(p, "utf8"));
|
|
10231
10470
|
const writeFile3 = deps.writeFile ?? ((p, data) => {
|
|
10232
|
-
|
|
10471
|
+
writeFileSync7(`${p}.tmp`, data);
|
|
10233
10472
|
renameSync4(`${p}.tmp`, p);
|
|
10234
10473
|
});
|
|
10235
10474
|
const host = deps.hostname ?? hostname;
|
|
@@ -10344,8 +10583,8 @@ import { basename as basename4 } from "path";
|
|
|
10344
10583
|
import { serve as serve2 } from "@hono/node-server";
|
|
10345
10584
|
|
|
10346
10585
|
// src/diff.ts
|
|
10347
|
-
import { statSync as
|
|
10348
|
-
import { join as
|
|
10586
|
+
import { statSync as statSync8 } from "fs";
|
|
10587
|
+
import { join as join12 } from "path";
|
|
10349
10588
|
import { $ as $3 } from "execa";
|
|
10350
10589
|
var MAX_FILES = 200;
|
|
10351
10590
|
var MAX_LINES_PER_FILE = 1500;
|
|
@@ -10488,7 +10727,7 @@ async function resolveBase(wt, baseBranch) {
|
|
|
10488
10727
|
}
|
|
10489
10728
|
function mtime(path) {
|
|
10490
10729
|
try {
|
|
10491
|
-
return
|
|
10730
|
+
return statSync8(path).mtimeMs;
|
|
10492
10731
|
} catch {
|
|
10493
10732
|
return 0;
|
|
10494
10733
|
}
|
|
@@ -10513,7 +10752,7 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
10513
10752
|
files.push(pf);
|
|
10514
10753
|
}
|
|
10515
10754
|
}
|
|
10516
|
-
files.sort((a, b) => mtime(
|
|
10755
|
+
files.sort((a, b) => mtime(join12(worktree, b.path)) - mtime(join12(worktree, a.path)));
|
|
10517
10756
|
const truncatedFiles = files.length > MAX_FILES;
|
|
10518
10757
|
if (truncatedFiles) files = files.slice(0, MAX_FILES);
|
|
10519
10758
|
const added = files.reduce((s, f) => s + f.added, 0);
|
|
@@ -10683,9 +10922,9 @@ function mergeQueueCandidates(runs, repo, repoFallback) {
|
|
|
10683
10922
|
const byPr = /* @__PURE__ */ new Map();
|
|
10684
10923
|
for (const r of runs) {
|
|
10685
10924
|
if (effectiveRepoOf(r.repo, repoFallback) !== repo || !isMergeCandidate(r)) continue;
|
|
10686
|
-
const
|
|
10687
|
-
const seen = byPr.get(
|
|
10688
|
-
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);
|
|
10689
10928
|
}
|
|
10690
10929
|
return [...byPr.values()];
|
|
10691
10930
|
}
|
|
@@ -10698,10 +10937,10 @@ function makeMergeQueueResolver(deps) {
|
|
|
10698
10937
|
};
|
|
10699
10938
|
return async (run) => {
|
|
10700
10939
|
if (!deps.baseSync || !run.pr_url) return null;
|
|
10701
|
-
const
|
|
10702
|
-
if (
|
|
10940
|
+
const num2 = prNumberOf(run.pr_url);
|
|
10941
|
+
if (num2 == null) return null;
|
|
10703
10942
|
const candidates = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback);
|
|
10704
|
-
const mine = candidates.find((c) => prNumberOf(c.pr_url) ===
|
|
10943
|
+
const mine = candidates.find((c) => prNumberOf(c.pr_url) === num2);
|
|
10705
10944
|
if (!mine) return null;
|
|
10706
10945
|
const ready = [];
|
|
10707
10946
|
const urlOf = /* @__PURE__ */ new Map();
|
|
@@ -10817,10 +11056,10 @@ function makeMergeIntentResolver(deps) {
|
|
|
10817
11056
|
};
|
|
10818
11057
|
return async (run) => {
|
|
10819
11058
|
if (!deps.baseSync || !run.pr_url || !hasMergeIntent(run)) return null;
|
|
10820
|
-
const
|
|
10821
|
-
if (
|
|
11059
|
+
const num2 = prNumberOf(run.pr_url);
|
|
11060
|
+
if (num2 == null) return null;
|
|
10822
11061
|
const marked = mergeQueueCandidates(deps.listRuns(), effectiveRepoOf(run.repo, deps.repoFallback), deps.repoFallback).filter(hasMergeIntent);
|
|
10823
|
-
const mine = marked.find((c) => prNumberOf(c.pr_url) ===
|
|
11062
|
+
const mine = marked.find((c) => prNumberOf(c.pr_url) === num2);
|
|
10824
11063
|
if (!mine) return null;
|
|
10825
11064
|
const queue = orderMergeQueue(marked.map(entryOf));
|
|
10826
11065
|
const entry = entryOf(mine);
|
|
@@ -12537,15 +12776,15 @@ import { cancelRun, respawnRun, runDaemon, wakeRun, OVERMIND_ENTRY, listDronePro
|
|
|
12537
12776
|
|
|
12538
12777
|
// src/launcher.ts
|
|
12539
12778
|
import { spawn as spawn2 } from "child_process";
|
|
12540
|
-
import { closeSync as closeSync3, existsSync as
|
|
12779
|
+
import { closeSync as closeSync3, existsSync as existsSync10, mkdirSync as mkdirSync10, openSync as openSync3, readdirSync as readdirSync9 } from "fs";
|
|
12541
12780
|
import { createRequire } from "module";
|
|
12542
12781
|
import { homedir as homedir5 } from "os";
|
|
12543
|
-
import { dirname as dirname4, join as
|
|
12782
|
+
import { dirname as dirname4, join as join15 } from "path";
|
|
12544
12783
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12545
12784
|
|
|
12546
12785
|
// src/droneHome.ts
|
|
12547
|
-
import { lstatSync, mkdirSync as
|
|
12548
|
-
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";
|
|
12549
12788
|
var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
|
|
12550
12789
|
function droneHomeDeny(env = process.env) {
|
|
12551
12790
|
const extra = (env.SWARM_DRONE_HOME_DENY || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -12563,34 +12802,34 @@ function buildDroneHome(opts) {
|
|
|
12563
12802
|
const { realHome, droneHome } = opts;
|
|
12564
12803
|
const deny = opts.deny ?? droneHomeDeny();
|
|
12565
12804
|
const platform = opts.platform ?? process.platform;
|
|
12566
|
-
|
|
12567
|
-
const wanted = new Set(mirroredEntries(
|
|
12805
|
+
mkdirSync9(droneHome, { recursive: true, mode: 448 });
|
|
12806
|
+
const wanted = new Set(mirroredEntries(readdirSync8(realHome), deny, platform));
|
|
12568
12807
|
const rel = relative(realHome, droneHome);
|
|
12569
12808
|
if (rel && !rel.startsWith("..") && !rel.includes(sep)) wanted.delete(rel);
|
|
12570
|
-
for (const existing of
|
|
12809
|
+
for (const existing of readdirSync8(droneHome)) {
|
|
12571
12810
|
if (wanted.has(existing)) continue;
|
|
12572
|
-
const p =
|
|
12811
|
+
const p = join14(droneHome, existing);
|
|
12573
12812
|
try {
|
|
12574
12813
|
if (lstatSync(p).isSymbolicLink()) rmSync(p);
|
|
12575
12814
|
} catch {
|
|
12576
12815
|
}
|
|
12577
12816
|
}
|
|
12578
12817
|
for (const name of wanted) {
|
|
12579
|
-
const link =
|
|
12818
|
+
const link = join14(droneHome, name);
|
|
12580
12819
|
try {
|
|
12581
12820
|
lstatSync(link);
|
|
12582
12821
|
continue;
|
|
12583
12822
|
} catch {
|
|
12584
12823
|
}
|
|
12585
|
-
const target =
|
|
12824
|
+
const target = join14(realHome, name);
|
|
12586
12825
|
let isDir;
|
|
12587
12826
|
try {
|
|
12588
|
-
isDir =
|
|
12827
|
+
isDir = statSync9(target).isDirectory();
|
|
12589
12828
|
} catch {
|
|
12590
12829
|
continue;
|
|
12591
12830
|
}
|
|
12592
12831
|
try {
|
|
12593
|
-
|
|
12832
|
+
symlinkSync3(target, link, isDir ? "junction" : "file");
|
|
12594
12833
|
} catch (e) {
|
|
12595
12834
|
if (e.code !== "EEXIST") throw e;
|
|
12596
12835
|
}
|
|
@@ -12603,7 +12842,7 @@ var REAL_PATH_ENV_PINS = [
|
|
|
12603
12842
|
{ env: "YARN_GLOBAL_FOLDER", segments: [".yarn", "berry"] }
|
|
12604
12843
|
];
|
|
12605
12844
|
function realPathEnvPins(realHome) {
|
|
12606
|
-
return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env,
|
|
12845
|
+
return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join14(realHome, ...segments)]));
|
|
12607
12846
|
}
|
|
12608
12847
|
function dronePrivatePaths(realHome, swarmHome) {
|
|
12609
12848
|
return [
|
|
@@ -12686,7 +12925,7 @@ function droneEnv(worktree, base3 = process.env, isolation, droneCap = null) {
|
|
|
12686
12925
|
const env = { ...base3 };
|
|
12687
12926
|
const isolate = base3.SWARM_TURBO_ISOLATE !== "0";
|
|
12688
12927
|
if (isolate && !base3.TURBO_CACHE_DIR) {
|
|
12689
|
-
env.TURBO_CACHE_DIR =
|
|
12928
|
+
env.TURBO_CACHE_DIR = join15(worktree, ".turbo", "cache");
|
|
12690
12929
|
}
|
|
12691
12930
|
Object.assign(env, droneTestEnv(droneCap, base3));
|
|
12692
12931
|
for (const key of DAEMON_STATE_ENV) delete env[key];
|
|
@@ -12717,21 +12956,21 @@ var DAEMON_STATE_ENV = [
|
|
|
12717
12956
|
...PROVIDER_TOKEN_ENV
|
|
12718
12957
|
];
|
|
12719
12958
|
function bundledRoleDir(kind) {
|
|
12720
|
-
return
|
|
12959
|
+
return join15(dirname4(fileURLToPath2(import.meta.url)), "..", "roles", kind);
|
|
12721
12960
|
}
|
|
12722
12961
|
function roleDir(worktree, kind) {
|
|
12723
|
-
return
|
|
12962
|
+
return join15(worktree, ".claude", "roles", kind);
|
|
12724
12963
|
}
|
|
12725
12964
|
function roleDirs(worktree, kind) {
|
|
12726
|
-
return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) =>
|
|
12965
|
+
return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync10(dir));
|
|
12727
12966
|
}
|
|
12728
12967
|
function nextRunLogPath(runLogsDir, runLogKeys) {
|
|
12729
12968
|
const dir = runLogDirForWrite(runLogsDir, runLogKeys);
|
|
12730
12969
|
if (!dir) throw new Error("\u043D\u0435\u0447\u0435\u043C \u0430\u0434\u0440\u0435\u0441\u043E\u0432\u0430\u0442\u044C \u043A\u0430\u0442\u0430\u043B\u043E\u0433 run-log'\u043E\u0432: \u0443 \u043F\u0440\u043E\u0433\u043E\u043D\u0430 \u043D\u0435\u0442 \u0441\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u043E\u0433\u043E \u043A\u043B\u044E\u0447\u0430 (SwarmStore.runLogKeys)");
|
|
12731
|
-
|
|
12970
|
+
mkdirSync10(dir, { recursive: true });
|
|
12732
12971
|
let files = [];
|
|
12733
12972
|
try {
|
|
12734
|
-
files =
|
|
12973
|
+
files = readdirSync9(dir);
|
|
12735
12974
|
} catch {
|
|
12736
12975
|
}
|
|
12737
12976
|
let next = 0;
|
|
@@ -12739,7 +12978,7 @@ function nextRunLogPath(runLogsDir, runLogKeys) {
|
|
|
12739
12978
|
const m = /^(\d+)\.json/.exec(f);
|
|
12740
12979
|
if (m) next = Math.max(next, Number(m[1]) + 1);
|
|
12741
12980
|
}
|
|
12742
|
-
return
|
|
12981
|
+
return join15(dir, `${next}.json`);
|
|
12743
12982
|
}
|
|
12744
12983
|
function openNextRunLog(runLogsDir, runLogKeys) {
|
|
12745
12984
|
for (let attempt = 0; attempt < 50; attempt++) {
|
|
@@ -12831,9 +13070,9 @@ function makeLauncher(cfg) {
|
|
|
12831
13070
|
}
|
|
12832
13071
|
const realHome = cfg.droneIsolation?.realHome ?? homedir5();
|
|
12833
13072
|
const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
|
|
12834
|
-
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !
|
|
13073
|
+
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync10(join15(cfg.droneIsolation.swarmHome, "token"))) {
|
|
12835
13074
|
console.error(
|
|
12836
|
-
`[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${
|
|
13075
|
+
`[launcher] TASKLESS_TOKEN \u0437\u0430\u0434\u0430\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0432 env \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0434\u0440\u043E\u043D\u0443 \u043E\u043D \u043D\u0435 \u043D\u0430\u0441\u043B\u0435\u0434\u0443\u0435\u0442\u0441\u044F (TASWM-289), \u0430 \u0444\u0430\u0439\u043B\u0430 ${join15(cfg.droneIsolation.swarmHome, "token")} \u043D\u0435\u0442. read_task \u0434\u0440\u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 slug-only. \u041F\u043E\u0447\u0438\u043D\u0438: \`swarm login\` (oauth.json) \u0438\u043B\u0438 \u043F\u043E\u043B\u043E\u0436\u0438 \u0442\u043E\u043A\u0435\u043D \u0432 \u044D\u0442\u043E\u0442 \u0444\u0430\u0439\u043B.`
|
|
12837
13076
|
);
|
|
12838
13077
|
}
|
|
12839
13078
|
const executor = cfg.executor ?? executorForRun(run, cfg.swarmModels?.() ?? null);
|
|
@@ -13732,7 +13971,7 @@ async function runDaemonLogs(argv) {
|
|
|
13732
13971
|
console.error(`\u043B\u043E\u0433 \u041F\u0420\u041E\u0413\u041E\u041D\u0410 \u2014 \u044D\u0442\u043E \xABswarm log ${stray}\xBB (\u0431\u0435\u0437 s). \xABswarm logs\xBB \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u043B\u043E\u0433 \u0434\u0435\u043C\u043E\u043D\u0430; \u0447\u0438\u0441\u043B\u043E \u0441\u0442\u0440\u043E\u043A \u2014 \xABswarm logs -n 200\xBB.`);
|
|
13733
13972
|
return 1;
|
|
13734
13973
|
}
|
|
13735
|
-
if (!
|
|
13974
|
+
if (!existsSync18(DAEMON_LOG_PATH)) {
|
|
13736
13975
|
console.error(`\u043B\u043E\u0433\u0430 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442: ${DAEMON_LOG_PATH}`);
|
|
13737
13976
|
console.error("\u043E\u043D \u043F\u0438\u0448\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u0444\u043E\u043D\u043E\u0432\u043E\u043C \u0437\u0430\u043F\u0443\u0441\u043A\u0435 (swarm start). \u0414\u0435\u043C\u043E\u043D \u043F\u043E\u0434\u043D\u044F\u0442 \u0432 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435 (--foreground) \u2014 \u0432\u044B\u0432\u043E\u0434 \u0438\u0434\u0451\u0442 \u0442\u0443\u0434\u0430.");
|
|
13738
13977
|
return 1;
|
|
@@ -13740,12 +13979,12 @@ async function runDaemonLogs(argv) {
|
|
|
13740
13979
|
const head = tailFile(DAEMON_LOG_PATH, lines);
|
|
13741
13980
|
if (head) console.log(head);
|
|
13742
13981
|
if (!follow) return 0;
|
|
13743
|
-
let pos =
|
|
13982
|
+
let pos = statSync12(DAEMON_LOG_PATH).size;
|
|
13744
13983
|
for (; ; ) {
|
|
13745
13984
|
await sleep2(500);
|
|
13746
13985
|
let size;
|
|
13747
13986
|
try {
|
|
13748
|
-
size =
|
|
13987
|
+
size = statSync12(DAEMON_LOG_PATH).size;
|
|
13749
13988
|
} catch {
|
|
13750
13989
|
continue;
|
|
13751
13990
|
}
|
|
@@ -14014,10 +14253,10 @@ function effectiveWorktreesDir() {
|
|
|
14014
14253
|
legacyDir: WORKTREES_LEGACY_DIR,
|
|
14015
14254
|
targetDir: WORKTREES_DIR,
|
|
14016
14255
|
envOverride: process.env.SWARM_WORKTREES || "",
|
|
14017
|
-
exists: (d) =>
|
|
14256
|
+
exists: (d) => existsSync18(d),
|
|
14018
14257
|
isNonEmptyDir: (d) => {
|
|
14019
14258
|
try {
|
|
14020
|
-
return
|
|
14259
|
+
return readdirSync12(d).length > 0;
|
|
14021
14260
|
} catch {
|
|
14022
14261
|
return false;
|
|
14023
14262
|
}
|
|
@@ -14073,15 +14312,15 @@ async function scoutRepo(repo) {
|
|
|
14073
14312
|
await $6({ reject: false })`git -C ${worktree} reset --hard ${baseRef}`;
|
|
14074
14313
|
}
|
|
14075
14314
|
await setWorktreeIdentity(worktree, name);
|
|
14076
|
-
const abs = (rel) =>
|
|
14315
|
+
const abs = (rel) => join27(worktree, rel);
|
|
14077
14316
|
const code = await runRepoScout({
|
|
14078
14317
|
repoFullName,
|
|
14079
14318
|
repoPath: worktree,
|
|
14080
14319
|
name,
|
|
14081
|
-
readFile: (rel) =>
|
|
14320
|
+
readFile: (rel) => existsSync18(abs(rel)) ? readFileSync19(abs(rel), "utf8") : null,
|
|
14082
14321
|
writeFile: (rel, text) => {
|
|
14083
|
-
|
|
14084
|
-
|
|
14322
|
+
mkdirSync11(dirname5(abs(rel)), { recursive: true });
|
|
14323
|
+
writeFileSync15(abs(rel), text);
|
|
14085
14324
|
},
|
|
14086
14325
|
runScout: (prompt) => new Promise((resolve5) => {
|
|
14087
14326
|
const child = spawn3(bin, [...ISOLATION_FLAGS, "--dangerously-skip-permissions", "-p", prompt], {
|