@taskless-app/swarm 0.6.2 → 0.6.4
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/README.md +18 -0
- package/dist/cli.js +165 -34
- package/dist/overmind.js +460 -127
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -211,6 +211,24 @@ deny-правил на файловые тулы у него нет — дрон
|
|
|
211
211
|
(`src/executors/index.ts`). Если для него пришлось тронуть общий код — значит,
|
|
212
212
|
чинить надо интерфейс, а не общий код; на это и стоит `contract.test.ts`.
|
|
213
213
|
|
|
214
|
+
## Служебные файлы роя в worktree
|
|
215
|
+
|
|
216
|
+
Кое-что рой кладёт прямо в рабочее дерево дрона: `.swarm-mcp.json` (конфиг
|
|
217
|
+
MCP-сервера роя, в нём **токен прогона**) и `.swarm-task/` (вложения задачи).
|
|
218
|
+
Ваш `.gitignore` про них знать не обязан — это файлы роя, а не проекта, и рой
|
|
219
|
+
прячет их сам, на каждом подготовленном worktree (TASWM-372):
|
|
220
|
+
|
|
221
|
+
- паттерны уезжают в `.git/info/exclude` — локальный exclude вашего клона, файлы
|
|
222
|
+
в репозиторий не коммитятся, а сам репозиторий при этом не тронут;
|
|
223
|
+
- `pre-commit`/`pre-push` рвут операцию, если служебный файл всё-таки оказался в
|
|
224
|
+
индексе (`git add -f`, файл приехал закоммиченным с ветки). Хуки ставятся
|
|
225
|
+
строго пер-worktree (`git config --worktree core.hooksPath`, каталог —
|
|
226
|
+
`.git/worktrees/<id>/swarm-hooks`) и в конце **вызывают ваш прежний хук**:
|
|
227
|
+
husky и прочая дисциплина репозитория продолжают работать.
|
|
228
|
+
|
|
229
|
+
Список файлов — один, `src/swarmFiles.ts`; оттуда же берут имена те, кто их
|
|
230
|
+
пишет. Кладёте в worktree что-то новое — впишите туда, и оно спрячется само.
|
|
231
|
+
|
|
214
232
|
## Windows
|
|
215
233
|
|
|
216
234
|
Рой работает на Windows, но **рекомендуемый способ — WSL2**: там он ведёт себя ровно
|
package/dist/cli.js
CHANGED
|
@@ -104,7 +104,13 @@ var init_types = __esm({
|
|
|
104
104
|
// делать нечего». Находки вроде «в дизайн-системе нет иконки микрофона» раньше либо
|
|
105
105
|
// превращались в такой дорогой вопрос, либо тонули в отчёте о сдаче (дрон LAMPYRIS сдал
|
|
106
106
|
// заглушку «Мик» текстом на кнопке — уехало в прод). note уезжает оператору, дрон работает дальше.
|
|
107
|
-
"note"
|
|
107
|
+
"note",
|
|
108
|
+
// Зависимость прогона снята НЕуспешным финалом блокера (TASWM-361): тот остановлен/упал/
|
|
109
|
+
// переигран, успешного финала не будет — ждать больше нечего. Пишется в ленту ЖДАВШЕГО
|
|
110
|
+
// прогона перед самым спавном: «поехал молча» означало бы, что причина запуска исчезла из
|
|
111
|
+
// истории, и следующий разбор «почему он стартовал поверх незакрытого блокера» упёрся бы в
|
|
112
|
+
// пустоту. Успешное снятие (блокер смержен) события не порождает — это штатный ход.
|
|
113
|
+
"dep_cleared"
|
|
108
114
|
];
|
|
109
115
|
MILESTONE_KINDS = [
|
|
110
116
|
"spawned",
|
|
@@ -125,8 +131,10 @@ var init_types = __esm({
|
|
|
125
131
|
"storm_dead",
|
|
126
132
|
"anomaly",
|
|
127
133
|
// надзор overmind (TASWM-194) — та же нотификация, что улетела в инбокс, вехой в Истории
|
|
128
|
-
"note"
|
|
134
|
+
"note",
|
|
129
135
|
// заметка дрона «замечено по пути» (TASWM-279) — веха, иначе находка тонет в ленте step'ов
|
|
136
|
+
"dep_cleared"
|
|
137
|
+
// зависимость снята неуспешным финалом блокера (TASWM-361) — решение роя, веха как conflict
|
|
130
138
|
];
|
|
131
139
|
MILESTONE_KIND_SET = new Set(MILESTONE_KINDS);
|
|
132
140
|
}
|
|
@@ -355,6 +363,32 @@ var init_status = __esm({
|
|
|
355
363
|
}
|
|
356
364
|
});
|
|
357
365
|
|
|
366
|
+
// ../swarm-core/src/deps.ts
|
|
367
|
+
function judgeDep(runs) {
|
|
368
|
+
const real = runs.filter((r) => !r.service);
|
|
369
|
+
if (real.length === 0) return { ...PENDING };
|
|
370
|
+
if (real.some((r) => !isTerminalStatus(r.status))) return { ...PENDING };
|
|
371
|
+
if (real.some((r) => r.status === "done")) {
|
|
372
|
+
return { outcome: "done", met: true, status: "done", name: null, stopReason: null };
|
|
373
|
+
}
|
|
374
|
+
const last = real[real.length - 1];
|
|
375
|
+
return {
|
|
376
|
+
outcome: "exhausted",
|
|
377
|
+
met: true,
|
|
378
|
+
status: last.status,
|
|
379
|
+
name: last.name ?? null,
|
|
380
|
+
stopReason: last.stopReason ?? null
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
var PENDING;
|
|
384
|
+
var init_deps = __esm({
|
|
385
|
+
"../swarm-core/src/deps.ts"() {
|
|
386
|
+
"use strict";
|
|
387
|
+
init_status();
|
|
388
|
+
PENDING = { outcome: "pending", met: false, status: null, name: null, stopReason: null };
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
358
392
|
// ../swarm-core/src/time.ts
|
|
359
393
|
function stamp(d = /* @__PURE__ */ new Date()) {
|
|
360
394
|
return d.toISOString().slice(0, 19).replace("T", " ");
|
|
@@ -1061,6 +1095,7 @@ var init_src = __esm({
|
|
|
1061
1095
|
init_timeline();
|
|
1062
1096
|
init_reviewPhase();
|
|
1063
1097
|
init_status();
|
|
1098
|
+
init_deps();
|
|
1064
1099
|
init_time();
|
|
1065
1100
|
init_windows();
|
|
1066
1101
|
init_slug();
|
|
@@ -1156,6 +1191,17 @@ var init_claudeBin = __esm({
|
|
|
1156
1191
|
}
|
|
1157
1192
|
});
|
|
1158
1193
|
|
|
1194
|
+
// src/swarmFiles.ts
|
|
1195
|
+
var SWARM_MCP_CONFIG_FILE, SWARM_TASK_DIR, SWARM_EXCLUDE_PATTERNS;
|
|
1196
|
+
var init_swarmFiles = __esm({
|
|
1197
|
+
"src/swarmFiles.ts"() {
|
|
1198
|
+
"use strict";
|
|
1199
|
+
SWARM_MCP_CONFIG_FILE = ".swarm-mcp.json";
|
|
1200
|
+
SWARM_TASK_DIR = ".swarm-task";
|
|
1201
|
+
SWARM_EXCLUDE_PATTERNS = [`/${SWARM_MCP_CONFIG_FILE}`, `/${SWARM_TASK_DIR}/`];
|
|
1202
|
+
}
|
|
1203
|
+
});
|
|
1204
|
+
|
|
1159
1205
|
// src/pricing.ts
|
|
1160
1206
|
function costFromUsage(usage2, model, table = MODEL_PRICING) {
|
|
1161
1207
|
const pricing = model ? table[model] : void 0;
|
|
@@ -1294,9 +1340,10 @@ var init_claudeCode = __esm({
|
|
|
1294
1340
|
"src/executors/claudeCode.ts"() {
|
|
1295
1341
|
"use strict";
|
|
1296
1342
|
init_claudeBin();
|
|
1343
|
+
init_swarmFiles();
|
|
1297
1344
|
init_claudeResult();
|
|
1298
1345
|
ISOLATION_FLAGS = ["--setting-sources", "project", "--strict-mcp-config"];
|
|
1299
|
-
MCP_CONFIG_FILE =
|
|
1346
|
+
MCP_CONFIG_FILE = SWARM_MCP_CONFIG_FILE;
|
|
1300
1347
|
claudeCodeExecutor = {
|
|
1301
1348
|
id: "claude-code",
|
|
1302
1349
|
title: "Claude Code (claude)",
|
|
@@ -1837,7 +1884,7 @@ var init_package = __esm({
|
|
|
1837
1884
|
"package.json"() {
|
|
1838
1885
|
package_default = {
|
|
1839
1886
|
name: "@taskless-app/swarm",
|
|
1840
|
-
version: "0.6.
|
|
1887
|
+
version: "0.6.4",
|
|
1841
1888
|
type: "module",
|
|
1842
1889
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
1843
1890
|
keywords: [
|
|
@@ -1887,7 +1934,7 @@ var init_package = __esm({
|
|
|
1887
1934
|
execa: "^10.0.0",
|
|
1888
1935
|
zod: "3.25.76"
|
|
1889
1936
|
},
|
|
1890
|
-
gitHead: "
|
|
1937
|
+
gitHead: "12ecebcf1e60baab07e68cae3e78a29d78cb7fde"
|
|
1891
1938
|
};
|
|
1892
1939
|
}
|
|
1893
1940
|
});
|
|
@@ -2676,18 +2723,40 @@ CREATE TABLE IF NOT EXISTS imports (
|
|
|
2676
2723
|
return rows.map((r) => r.area);
|
|
2677
2724
|
}
|
|
2678
2725
|
/**
|
|
2679
|
-
* Из depends_on прогона — слаги зависимостей, ещё НЕ
|
|
2680
|
-
*
|
|
2681
|
-
*
|
|
2682
|
-
*
|
|
2683
|
-
*
|
|
2684
|
-
*
|
|
2726
|
+
* Из depends_on прогона — слаги зависимостей, ещё НЕ исчерпанных. Пусто → все выполнены,
|
|
2727
|
+
* дрона можно спавнить. Явная зависимость (spawn --after) отдельна от area-lock: area
|
|
2728
|
+
* освобождается на review (до мержа), а тут ждём финала блокера, чтобы следующий дрон
|
|
2729
|
+
* стартовал от его кода.
|
|
2730
|
+
*
|
|
2731
|
+
* TASWM-361: критерий — «живых прогонов по слагу не осталось», а не «есть done». Раньше
|
|
2732
|
+
* зависимость снимал ТОЛЬКО успех, и остановленный оператором блокер держал ждущих вечно
|
|
2733
|
+
* (VESPA и TYPHON — сутки очереди при пустом рое). Само правило и его обоснование —
|
|
2734
|
+
* `judgeDep` в swarm-core/deps.ts (его же читают зеркало прода и прод-снапшот).
|
|
2735
|
+
*
|
|
2736
|
+
* Несуществующий/зациклённый слаг прогонов не имеет вовсе → `pending` → прогон ждёт, как и
|
|
2737
|
+
* раньше, но очередь не виснет (гейт — пропуск в цикле, не блокировка).
|
|
2685
2738
|
*/
|
|
2686
2739
|
unmetDeps(depends_on) {
|
|
2740
|
+
return this.depVerdicts(depends_on).filter((d) => !d.met).map((d) => d.slug);
|
|
2741
|
+
}
|
|
2742
|
+
/**
|
|
2743
|
+
* TASWM-361: вердикт по каждой зависимости — не только «держит ли», но и КАК закрылась
|
|
2744
|
+
* (успехом блокера или его неуспешным финалом). Разница нужна overmind: снятие по
|
|
2745
|
+
* неуспеху оставляет след в ленге ждавшего прогона, штатное — нет.
|
|
2746
|
+
*/
|
|
2747
|
+
depVerdicts(depends_on) {
|
|
2687
2748
|
if (!depends_on || depends_on.length === 0) return [];
|
|
2688
|
-
const
|
|
2689
|
-
const
|
|
2690
|
-
|
|
2749
|
+
const slugs = [...new Set(depends_on)];
|
|
2750
|
+
const rows = this.db.prepare(
|
|
2751
|
+
`SELECT task_slug, status, name, stop_reason FROM runs WHERE task_slug IN (${slugs.map(() => "?").join(",")}) AND resolves_run_id IS NULL AND reviews_run_id IS NULL ORDER BY id`
|
|
2752
|
+
).all(...slugs);
|
|
2753
|
+
const bySlug = /* @__PURE__ */ new Map();
|
|
2754
|
+
for (const r of rows) {
|
|
2755
|
+
const list = bySlug.get(r.task_slug) ?? [];
|
|
2756
|
+
list.push({ status: r.status, name: r.name, stopReason: r.stop_reason });
|
|
2757
|
+
bySlug.set(r.task_slug, list);
|
|
2758
|
+
}
|
|
2759
|
+
return depends_on.map((slug) => ({ slug, ...judgeDep(bySlug.get(slug) ?? []) }));
|
|
2691
2760
|
}
|
|
2692
2761
|
/** Все репо, засветившиеся в прогонах — whitelist для валидации spawn из веба. */
|
|
2693
2762
|
distinctRepos() {
|
|
@@ -4172,10 +4241,13 @@ var SwarmClient = class _SwarmClient {
|
|
|
4172
4241
|
// редко — без этого лог был бы простынёй из одной и той же строки, и в ней потонуло бы
|
|
4173
4242
|
// всё остальное. Меняется набор (эпик закрыли / блокер прошли) → печатаем снова.
|
|
4174
4243
|
lastClaimSkipLog = null;
|
|
4175
|
-
// Зависимости, подтверждённые ПРОДОМ (TASWM-300): слаг →
|
|
4176
|
-
//
|
|
4177
|
-
//
|
|
4178
|
-
|
|
4244
|
+
// Зависимости, подтверждённые ПРОДОМ (TASWM-300): слаг → вердикт по ВСЕМ его прогонам.
|
|
4245
|
+
// `done` монотонен (не откатывается) → положительный ответ не протухает и больше не
|
|
4246
|
+
// спрашивается; прочие вердикты переспрашиваются по TTL — «исчерпана неуспешно»
|
|
4247
|
+
// (TASWM-361) монотонностью НЕ обладает: оператор вправе переигрть остановленный блокер
|
|
4248
|
+
// новым прогоном того же слага, и тогда зависимость снова становится ожиданием.
|
|
4249
|
+
// Живёт в памяти: рестарт демона просто переспросит прод.
|
|
4250
|
+
depsProd = /* @__PURE__ */ new Map();
|
|
4179
4251
|
depsAskedAt = /* @__PURE__ */ new Map();
|
|
4180
4252
|
static DEPS_TTL_MS = 6e4;
|
|
4181
4253
|
// TASWM-327: прод старее демона и не знает аргумент `level` у swarm_notify. Выясняется
|
|
@@ -4602,12 +4674,36 @@ var SwarmClient = class _SwarmClient {
|
|
|
4602
4674
|
return [...seen];
|
|
4603
4675
|
}
|
|
4604
4676
|
unmetDeps(depends_on) {
|
|
4677
|
+
return this.depVerdicts(depends_on).filter((d) => !d.met).map((d) => d.slug);
|
|
4678
|
+
}
|
|
4679
|
+
/**
|
|
4680
|
+
* TASWM-361: вердикт по каждой зависимости — см. докблок SwarmStore.depVerdicts.
|
|
4681
|
+
*
|
|
4682
|
+
* АСИММЕТРИЯ ЗЕРКАЛА И ПРОДА, и почему она здесь намеренная. Зеркало — ЧАСТИЧНАЯ выборка
|
|
4683
|
+
* (только прогоны этой машины, только окно гидрации), и по частичной выборке два вывода
|
|
4684
|
+
* стоят разного:
|
|
4685
|
+
* • «есть смерженный прогон слага» — вывод от НАЛИЧИЯ: чего зеркало не видит, того он не
|
|
4686
|
+
* портит, поэтому успех подтверждаем и локально (так было до TASWM-361);
|
|
4687
|
+
* • «живых прогонов слага не осталось» — вывод от ОТСУТСТВИЯ: невидимый зеркалу прогон
|
|
4688
|
+
* на соседней машине как раз и может быть живым. Поэтому «исчерпана неуспешно»
|
|
4689
|
+
* принимаем ТОЛЬКО из ответа прода (refreshDeps) — он видит всех.
|
|
4690
|
+
* Прод недоступен → вердикта нет → прогон честно ждёт следующего окна: лишняя минута
|
|
4691
|
+
* очереди дешевле дрона, поднятого поверх работающего блокера.
|
|
4692
|
+
*/
|
|
4693
|
+
depVerdicts(depends_on) {
|
|
4605
4694
|
if (!depends_on || depends_on.length === 0) return [];
|
|
4606
|
-
const
|
|
4695
|
+
const doneInMirror = /* @__PURE__ */ new Set();
|
|
4607
4696
|
for (const r of this.runs.values()) {
|
|
4608
|
-
if (r.status === "done" && r.resolves_run_id == null)
|
|
4697
|
+
if (r.status === "done" && r.resolves_run_id == null && r.reviews_run_id == null) doneInMirror.add(r.task_slug);
|
|
4609
4698
|
}
|
|
4610
|
-
return depends_on.
|
|
4699
|
+
return depends_on.map((slug) => {
|
|
4700
|
+
if (doneInMirror.has(slug)) {
|
|
4701
|
+
return { slug, outcome: "done", met: true, status: "done", name: null, stopReason: null };
|
|
4702
|
+
}
|
|
4703
|
+
const fromProd = this.depsProd.get(slug);
|
|
4704
|
+
if (fromProd) return { slug, ...fromProd };
|
|
4705
|
+
return { slug, outcome: "pending", met: false, status: null, name: null, stopReason: null };
|
|
4706
|
+
});
|
|
4611
4707
|
}
|
|
4612
4708
|
/**
|
|
4613
4709
|
* TASWM-300: спросить ПРОД про зависимости — см. докблок SwarmStore.refreshDeps.
|
|
@@ -4618,14 +4714,15 @@ var SwarmClient = class _SwarmClient {
|
|
|
4618
4714
|
* просто позавчера, в зеркало не попадала — и `--after` держал прогон в очереди вечно
|
|
4619
4715
|
* (04.08: снимали руками).
|
|
4620
4716
|
*
|
|
4621
|
-
* Кеш.
|
|
4622
|
-
* положительный ответ помним навсегда и больше не спрашиваем.
|
|
4717
|
+
* Кеш. «Смержено» МОНОТОННО: прогон, ставший done, назад не откатывается, — поэтому
|
|
4718
|
+
* положительный ответ помним навсегда и больше не спрашиваем. Остальные вердикты (включая
|
|
4719
|
+
* «исчерпана неуспешно», TASWM-361 — блокер могут переиграть новым прогоном) переспрашиваем
|
|
4623
4720
|
* не чаще DEPS_TTL_MS: тик идёт раз в ~4с, а ждущих зависимость прогонов может быть
|
|
4624
4721
|
* много — без TTL это был бы поход в прод на каждый слаг каждый тик.
|
|
4625
4722
|
*/
|
|
4626
4723
|
async refreshDeps(slugs, nowMs = Date.now()) {
|
|
4627
4724
|
for (const slug of new Set(slugs)) {
|
|
4628
|
-
if (!slug || this.
|
|
4725
|
+
if (!slug || this.depsProd.get(slug)?.outcome === "done") continue;
|
|
4629
4726
|
const asked = this.depsAskedAt.get(slug);
|
|
4630
4727
|
if (asked !== void 0 && nowMs - asked < _SwarmClient.DEPS_TTL_MS) continue;
|
|
4631
4728
|
this.depsAskedAt.set(slug, nowMs);
|
|
@@ -4638,7 +4735,18 @@ var SwarmClient = class _SwarmClient {
|
|
|
4638
4735
|
}
|
|
4639
4736
|
if (res?.isError) continue;
|
|
4640
4737
|
const runs = res.structuredContent?.runs ?? [];
|
|
4641
|
-
|
|
4738
|
+
const facts = runs.slice().sort((a, b) => String(a.createdAt ?? "").localeCompare(String(b.createdAt ?? ""))).map((r) => ({
|
|
4739
|
+
status: String(r.status ?? ""),
|
|
4740
|
+
// Служебный сабран под тем же слагом (резолвер конфликта / ревьюер): его финал —
|
|
4741
|
+
// не финал задачи. Резолвер вычёркивался и до TASWM-361, ревьюер добавлен вместе
|
|
4742
|
+
// с расширением критерия — иначе остановленный ревьюер закрывал бы зависимость.
|
|
4743
|
+
service: Boolean(r.resolvesRunId) || Boolean(r.reviewsRunId),
|
|
4744
|
+
// В проде имя дрона — колонка droneName (см. hydrateRun); `name` терпим как
|
|
4745
|
+
// запасной вариант ради фикстур и старых ответов.
|
|
4746
|
+
name: r.droneName ?? r.name ?? null,
|
|
4747
|
+
stopReason: r.stopReason ?? null
|
|
4748
|
+
}));
|
|
4749
|
+
this.depsProd.set(slug, judgeDep(facts));
|
|
4642
4750
|
}
|
|
4643
4751
|
}
|
|
4644
4752
|
/**
|
|
@@ -5293,6 +5401,26 @@ var SwarmClient = class _SwarmClient {
|
|
|
5293
5401
|
const v = sc.config?.exclusive;
|
|
5294
5402
|
return typeof v === "boolean" ? v : null;
|
|
5295
5403
|
}
|
|
5404
|
+
/**
|
|
5405
|
+
* Live КВОТА «дронов на проект» из Taskless (TASWM-365): тот же swarm_repo_config по
|
|
5406
|
+
* repoFullName — Project.swarmConfig.maxDrones. Сколько прогонов этого проекта могут идти
|
|
5407
|
+
* одновременно; 0 — без ограничения (дефолт проекта). Отдаём ЧИСЛО КАК ЕСТЬ, включая ноль:
|
|
5408
|
+
* превращать его в «без ограничения» — дело ридера (makeDroneCapReader), клиент здесь только
|
|
5409
|
+
* достаёт настройку. null (прод недоступен / repo не смэплен / поля нет / мусор) → ридер
|
|
5410
|
+
* возьмёт «без ограничения», то есть прежнее поведение.
|
|
5411
|
+
*/
|
|
5412
|
+
async repoDroneCap(repoFullName) {
|
|
5413
|
+
let res;
|
|
5414
|
+
try {
|
|
5415
|
+
res = await this.call("swarm_repo_config", { repoFullName });
|
|
5416
|
+
} catch {
|
|
5417
|
+
return null;
|
|
5418
|
+
}
|
|
5419
|
+
if (res?.isError) return null;
|
|
5420
|
+
const sc = res.structuredContent ?? {};
|
|
5421
|
+
const v = sc.config?.maxDrones;
|
|
5422
|
+
return typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : null;
|
|
5423
|
+
}
|
|
5296
5424
|
/**
|
|
5297
5425
|
* TASWM-291: МАШИНЫ роя владельца (swarm_list_hosts) — чтобы `swarm spawn --host
|
|
5298
5426
|
* <имя>` резолвил человеческое имя в hostId, а `swarm hosts` показывал, какие
|
|
@@ -6404,7 +6532,7 @@ function hostIdentityPath(home) {
|
|
|
6404
6532
|
function loadHostIdentity(deps) {
|
|
6405
6533
|
const exists = deps.exists ?? existsSync6;
|
|
6406
6534
|
const readFile2 = deps.readFile ?? ((p) => readFileSync7(p, "utf8"));
|
|
6407
|
-
const
|
|
6535
|
+
const writeFile3 = deps.writeFile ?? ((p, data) => {
|
|
6408
6536
|
writeFileSync5(`${p}.tmp`, data);
|
|
6409
6537
|
renameSync4(`${p}.tmp`, p);
|
|
6410
6538
|
});
|
|
@@ -6424,7 +6552,7 @@ function loadHostIdentity(deps) {
|
|
|
6424
6552
|
}
|
|
6425
6553
|
const hostId = newId();
|
|
6426
6554
|
try {
|
|
6427
|
-
|
|
6555
|
+
writeFile3(path, JSON.stringify({ hostId, hostName, createdAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2));
|
|
6428
6556
|
} catch {
|
|
6429
6557
|
console.warn(`[overmind] \u043D\u0435 \u0441\u043C\u043E\u0433 \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C ${path} \u2014 hostId \u0436\u0438\u0432\u0451\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043E \u0440\u0435\u0441\u0442\u0430\u0440\u0442\u0430 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430`);
|
|
6430
6558
|
}
|
|
@@ -7610,8 +7738,8 @@ var GK = ['claw','spore','eye','hex'];
|
|
|
7610
7738
|
var ST = ${jsonForScript(statusTuples())};
|
|
7611
7739
|
var ORDER = { running:0, blocked:1, paused:2, storm_dead:3, review:4, stalled:5, queued:6, stopped:7, done:8, failed:9, superseded:10 };
|
|
7612
7740
|
var TERM = ${jsonForScript(Object.fromEntries(TERMINAL_STATUSES.map((s) => [s, 1])))};
|
|
7613
|
-
var EVL = { spawned:'\u0434\u0440\u043E\u043D \u0437\u0430\u043F\u0443\u0449\u0435\u043D', step:'', blocked:'\u0432\u043E\u043F\u0440\u043E\u0441 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443', answered:'\u043F\u043E\u043B\u0443\u0447\u0435\u043D \u043E\u0442\u0432\u0435\u0442', pr_opened:'PR \u043E\u0442\u043A\u0440\u044B\u0442', merged:'PR \u0441\u043C\u0435\u0440\u0436\u0451\u043D, \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0430\u043A\u0440\u044B\u0442\u0430', done:'\u0433\u043E\u0442\u043E\u0432\u043E', failed:'\u0434\u0440\u043E\u043D \u0443\u043F\u0430\u043B', stalled:'\u0434\u0440\u043E\u043D \u0437\u0430\u0432\u0438\u0441/\u043F\u043E\u0433\u0438\u0431', respawned:'\u0430\u0432\u0442\u043E-\u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u0435', paused:'\u0437\u0430\u043C\u0435\u0440 \u2014 overmind \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D', resumed:'overmind \u0432\u0435\u0440\u043D\u0443\u043B\u0441\u044F \u2014 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u043B', rescued:'worktree \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u2014 \u0437\u0430\u0431\u0435\u0440\u0438 \u0440\u0443\u043A\u0430\u043C\u0438', conflict:'\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0441 main (merge-main-in)', died:'\u0434\u0440\u043E\u043D \u0443\u043C\u0435\u0440', storm_start:'\u26A1 \u043F\u0441\u0438\u043E\u043D\u043D\u0430\u044F \u0441\u0432\u044F\u0437\u044C \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430 \u2014 Anthropic \u0448\u0442\u043E\u0440\u043C\u0438\u0442', storm_end:'\u0441\u0432\u044F\u0437\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u2014 \u0440\u043E\u0439 \u043F\u0440\u043E\u0431\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044F', storm_dead:'\u0434\u0440\u043E\u043D \u0432 \u043A\u043E\u043A\u043E\u043D\u0435 (\u0448\u0442\u043E\u0440\u043C)', stopped:'\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D', superseded:'\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D', anomaly:'\u26A0\uFE0F \u0430\u043D\u043E\u043C\u0430\u043B\u0438\u044F \u0440\u043E\u044F' };
|
|
7614
|
-
var MDOT = { merged:'--success', done:'--success', pr_opened:'--info', spawned:'--muted-foreground', stalled:'--destructive', failed:'--destructive', respawned:'--warning', paused:'--warning', resumed:'--success', rescued:'--destructive', conflict:'--warning', died:'--destructive', storm_start:'--destructive', storm_end:'--success', storm_dead:'--warning', stopped:'--muted-foreground', superseded:'--muted-foreground', anomaly:'--warning' };
|
|
7741
|
+
var EVL = { spawned:'\u0434\u0440\u043E\u043D \u0437\u0430\u043F\u0443\u0449\u0435\u043D', step:'', blocked:'\u0432\u043E\u043F\u0440\u043E\u0441 \u043E\u043F\u0435\u0440\u0430\u0442\u043E\u0440\u0443', answered:'\u043F\u043E\u043B\u0443\u0447\u0435\u043D \u043E\u0442\u0432\u0435\u0442', pr_opened:'PR \u043E\u0442\u043A\u0440\u044B\u0442', merged:'PR \u0441\u043C\u0435\u0440\u0436\u0451\u043D, \u0437\u0430\u0434\u0430\u0447\u0430 \u0437\u0430\u043A\u0440\u044B\u0442\u0430', done:'\u0433\u043E\u0442\u043E\u0432\u043E', failed:'\u0434\u0440\u043E\u043D \u0443\u043F\u0430\u043B', stalled:'\u0434\u0440\u043E\u043D \u0437\u0430\u0432\u0438\u0441/\u043F\u043E\u0433\u0438\u0431', respawned:'\u0430\u0432\u0442\u043E-\u043E\u0436\u0438\u0432\u043B\u0435\u043D\u0438\u0435', paused:'\u0437\u0430\u043C\u0435\u0440 \u2014 overmind \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D', resumed:'overmind \u0432\u0435\u0440\u043D\u0443\u043B\u0441\u044F \u2014 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u043B', rescued:'worktree \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u2014 \u0437\u0430\u0431\u0435\u0440\u0438 \u0440\u0443\u043A\u0430\u043C\u0438', conflict:'\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442 \u0441 main (merge-main-in)', died:'\u0434\u0440\u043E\u043D \u0443\u043C\u0435\u0440', storm_start:'\u26A1 \u043F\u0441\u0438\u043E\u043D\u043D\u0430\u044F \u0441\u0432\u044F\u0437\u044C \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430 \u2014 Anthropic \u0448\u0442\u043E\u0440\u043C\u0438\u0442', storm_end:'\u0441\u0432\u044F\u0437\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u2014 \u0440\u043E\u0439 \u043F\u0440\u043E\u0431\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044F', storm_dead:'\u0434\u0440\u043E\u043D \u0432 \u043A\u043E\u043A\u043E\u043D\u0435 (\u0448\u0442\u043E\u0440\u043C)', stopped:'\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D', superseded:'\u043F\u0435\u0440\u0435\u0438\u0433\u0440\u0430\u043D', anomaly:'\u26A0\uFE0F \u0430\u043D\u043E\u043C\u0430\u043B\u0438\u044F \u0440\u043E\u044F', dep_cleared:'\u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u044C \u0441\u043D\u044F\u0442\u0430 \u2014 \u0431\u043B\u043E\u043A\u0435\u0440 \u043D\u0435 \u0434\u043E\u0435\u0434\u0435\u0442' };
|
|
7742
|
+
var MDOT = { merged:'--success', done:'--success', pr_opened:'--info', spawned:'--muted-foreground', stalled:'--destructive', failed:'--destructive', respawned:'--warning', paused:'--warning', resumed:'--success', rescued:'--destructive', conflict:'--warning', died:'--destructive', storm_start:'--destructive', storm_end:'--success', storm_dead:'--warning', stopped:'--muted-foreground', superseded:'--muted-foreground', anomaly:'--warning', dep_cleared:'--warning' };
|
|
7615
7743
|
|
|
7616
7744
|
function E(s){ return String(s==null?"":s).replace(/[&<>"]/g,function(c){return{'&':'&','<':'<','>':'>','"':'"'}[c]}); }
|
|
7617
7745
|
function color(st){ return 'var('+(ST[st]||ST.queued)[2]+')'; }
|
|
@@ -8772,9 +8900,10 @@ init_droneChannel();
|
|
|
8772
8900
|
|
|
8773
8901
|
// src/git.ts
|
|
8774
8902
|
import { existsSync as existsSync8, readdirSync as readdirSync8, statSync as statSync8 } from "fs";
|
|
8775
|
-
import { appendFile, mkdir, readFile, rm } from "fs/promises";
|
|
8776
|
-
import { isAbsolute as isAbsolute2, join as join13 } from "path";
|
|
8903
|
+
import { appendFile, chmod, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
8904
|
+
import { isAbsolute as isAbsolute2, join as join13, sep as sep2 } from "path";
|
|
8777
8905
|
import { $ as $5 } from "execa";
|
|
8906
|
+
init_swarmFiles();
|
|
8778
8907
|
function isLinkedWorktree(dir) {
|
|
8779
8908
|
try {
|
|
8780
8909
|
return statSync8(join13(dir, ".git")).isFile();
|
|
@@ -8825,8 +8954,10 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
8825
8954
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
8826
8955
|
|
|
8827
8956
|
// src/attachments.ts
|
|
8828
|
-
import { mkdir as mkdir2, rm as rm2, writeFile } from "fs/promises";
|
|
8957
|
+
import { mkdir as mkdir2, rm as rm2, writeFile as writeFile2 } from "fs/promises";
|
|
8829
8958
|
import { join as join15 } from "path";
|
|
8959
|
+
init_swarmFiles();
|
|
8960
|
+
var ATTACHMENTS_DIR = `${SWARM_TASK_DIR}/attachments`;
|
|
8830
8961
|
var ATTACH_MAX_TOTAL_BYTES = 24 * 1024 * 1024;
|
|
8831
8962
|
var ATTACH_MAX_FILE_BYTES = 8 * 1024 * 1024;
|
|
8832
8963
|
var IMAGE_EXT = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp", "avif", "heic"]);
|
|
@@ -9239,11 +9370,11 @@ function savePauseState(home, state, io = {}) {
|
|
|
9239
9370
|
remove(path);
|
|
9240
9371
|
return;
|
|
9241
9372
|
}
|
|
9242
|
-
const
|
|
9373
|
+
const writeFile3 = io.writeFile ?? ((p, data) => {
|
|
9243
9374
|
writeFileSync10(`${p}.tmp`, data);
|
|
9244
9375
|
renameSync5(`${p}.tmp`, p);
|
|
9245
9376
|
});
|
|
9246
|
-
|
|
9377
|
+
writeFile3(path, JSON.stringify(state, null, 2));
|
|
9247
9378
|
}
|
|
9248
9379
|
function makePauseState(source, reason, now = Date.now()) {
|
|
9249
9380
|
return {
|