@taskless-app/swarm 0.4.5 → 0.4.7
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 +8 -2
- package/dist/cli.js +6 -6
- package/dist/overmind.js +293 -137
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ CLI, который поднимает у вас на машине **рой др
|
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npx @taskless-app/swarm login --workspace <slug> # войти в свой Taskless-воркспейс
|
|
23
|
-
npx @taskless-app/swarm start
|
|
23
|
+
npx @taskless-app/swarm start # поднять демон роя
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Или один раз глобально:
|
|
@@ -28,10 +28,16 @@ npx @taskless-app/swarm start --repo <путь к репо> # поднят
|
|
|
28
28
|
```bash
|
|
29
29
|
npm i -g @taskless-app/swarm
|
|
30
30
|
swarm login --workspace <slug>
|
|
31
|
-
swarm start
|
|
31
|
+
swarm start # поднять рой: уходит в фон, терминал остаётся вам
|
|
32
32
|
swarm --help # список остальных команд (spawn/ls/questions/answer/…)
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
**Пути к репозиториям указывать не нужно.** Демон сам находит клоны на машине: у
|
|
36
|
+
каждого в `.git/config` записан origin, и по нему клон сопоставляется с репозиторием
|
|
37
|
+
проекта в Taskless. Смотрит в привычных местах (`~/projects`, `~/code`, `~/src`,
|
|
38
|
+
`~/work`, …) на три уровня вглубь — на типовой машине это десятки миллисекунд. Клоны
|
|
39
|
+
лежат где-то ещё — скажите один раз, `swarm start --repo <путь>`, дальше запомнится.
|
|
40
|
+
|
|
35
41
|
**Демон открепляется от терминала.** `swarm start` не занимает окно: процесс уходит
|
|
36
42
|
в фон, а весь вывод пишется в `~/.taskless-swarm/overmind.log` — тот самый файл, из
|
|
37
43
|
которого агент читает хвост лога удалённо. Посмотреть: `swarm logs -f`. Нужен вывод
|
package/dist/cli.js
CHANGED
|
@@ -557,6 +557,7 @@ var DRONE_CHANNEL_GRACE_MS = envNum("SWARM_DRONE_CHANNEL_GRACE_MS", 12e4);
|
|
|
557
557
|
var HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join(SWARM_HOME, "overmind.heartbeat");
|
|
558
558
|
var HEARTBEAT_STALE_MS = Number(process.env.SWARM_HEARTBEAT_STALE_MS) || 3e4;
|
|
559
559
|
var SWARM_REPO = process.env.SWARM_REPO || "";
|
|
560
|
+
var SWARM_SCAN_DEPTH = Number(process.env.SWARM_SCAN_DEPTH) || 3;
|
|
560
561
|
var SWARM_REPO_PATHS = (process.env.SWARM_REPOS || SWARM_REPO).split(",").map((s) => s.trim()).filter(Boolean);
|
|
561
562
|
var TASKLESS_API = process.env.TASKLESS_API || "https://taskless.ru";
|
|
562
563
|
var SWARM_SHOT_TIMEOUT_MS = envNum("SWARM_SHOT_TIMEOUT_MS", 2e4);
|
|
@@ -3940,7 +3941,7 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
3940
3941
|
// package.json
|
|
3941
3942
|
var package_default = {
|
|
3942
3943
|
name: "@taskless-app/swarm",
|
|
3943
|
-
version: "0.4.
|
|
3944
|
+
version: "0.4.7",
|
|
3944
3945
|
type: "module",
|
|
3945
3946
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3946
3947
|
keywords: [
|
|
@@ -7277,7 +7278,9 @@ function usage() {
|
|
|
7277
7278
|
swarm login [--workspace <slug|id>] [--no-open]
|
|
7278
7279
|
\u0432\u043E\u0439\u0442\u0438 \u0432 Taskless \u0438 \u0432\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441
|
|
7279
7280
|
swarm start [--repo <path>] [--foreground]
|
|
7280
|
-
\u043F\u043E\u0434\u043D\u044F\u0442\u044C \u0440\u043E\u0439 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435: \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u043E\u0447\u0435\u0440\u0435\u0434\u044C, \u0441\u043F\u0430\u0432\u043D\u0438\u0442 \u0438 \u0431\u0443\u0434\u0438\u0442 \u0434\u0440\u043E\u043D\u043E\u0432
|
|
7281
|
+
\u043F\u043E\u0434\u043D\u044F\u0442\u044C \u0440\u043E\u0439 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435: \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u043E\u0447\u0435\u0440\u0435\u0434\u044C, \u0441\u043F\u0430\u0432\u043D\u0438\u0442 \u0438 \u0431\u0443\u0434\u0438\u0442 \u0434\u0440\u043E\u043D\u043E\u0432.
|
|
7282
|
+
\u0420\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u043D\u0430\u0445\u043E\u0434\u0438\u0442 \u0441\u0430\u043C \u2014 \u043A\u043B\u043E\u043D\u044B \u043F\u0440\u043E\u0435\u043A\u0442\u043E\u0432 Taskless \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435;
|
|
7283
|
+
--repo \u043D\u0443\u0436\u0435\u043D, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043E\u043D\u0438 \u043B\u0435\u0436\u0430\u0442 \u0432 \u043D\u0435\u043E\u0431\u044B\u0447\u043D\u043E\u043C \u043C\u0435\u0441\u0442\u0435
|
|
7281
7284
|
swarm status \u0436\u0438\u0432 \u043B\u0438 \u0434\u0435\u043C\u043E\u043D, \u043D\u0430 \u043F\u0430\u0443\u0437\u0435 \u043B\u0438 \u0440\u043E\u0439, \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0434\u0440\u043E\u043D\u043E\u0432 \u0432 \u0440\u0430\u0431\u043E\u0442\u0435
|
|
7282
7285
|
|
|
7283
7286
|
\u0414\u0410\u0422\u042C \u0420\u041E\u042E \u0420\u0410\u0411\u041E\u0422\u0423
|
|
@@ -7422,14 +7425,11 @@ if (cmd === "start") {
|
|
|
7422
7425
|
}
|
|
7423
7426
|
const { repo: repoFlag } = takeRepo(startArgs);
|
|
7424
7427
|
const repo = repoFlag ?? SWARM_REPO;
|
|
7425
|
-
if (!repo) {
|
|
7426
|
-
console.warn("SWARM_REPO \u043D\u0435 \u0437\u0430\u0434\u0430\u043D \u2014 \u043F\u0440\u043E\u0433\u043E\u043D\u044B \u0431\u0435\u0437 \u044F\u0432\u043D\u043E\u0433\u043E repo \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u0442\u044F\u0442\u0441\u044F (\u043D\u0443\u0436\u0435\u043D spawn --repo).");
|
|
7427
|
-
}
|
|
7428
7428
|
if (!foreground) {
|
|
7429
7429
|
process.exit(await runStartDetached(startArgs));
|
|
7430
7430
|
}
|
|
7431
7431
|
try {
|
|
7432
|
-
await runDaemon({ repo });
|
|
7432
|
+
await runDaemon({ repo, repoFlag });
|
|
7433
7433
|
} catch (e) {
|
|
7434
7434
|
console.error(`\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0441\u044F: ${e instanceof Error ? e.message : e}`);
|
|
7435
7435
|
process.exit(1);
|
package/dist/overmind.js
CHANGED
|
@@ -407,13 +407,13 @@ var init_slug = __esm({
|
|
|
407
407
|
function extractText(d) {
|
|
408
408
|
if (typeof d === "string") return d;
|
|
409
409
|
const out = [];
|
|
410
|
-
const
|
|
410
|
+
const walk2 = (n) => {
|
|
411
411
|
if (!n || typeof n !== "object") return;
|
|
412
412
|
const node = n;
|
|
413
413
|
if (node.text) out.push(node.text);
|
|
414
|
-
(node.content ?? []).forEach(
|
|
414
|
+
(node.content ?? []).forEach(walk2);
|
|
415
415
|
};
|
|
416
|
-
|
|
416
|
+
walk2(d);
|
|
417
417
|
return out.join(" ");
|
|
418
418
|
}
|
|
419
419
|
function sanitizeEventText(text) {
|
|
@@ -793,6 +793,7 @@ __export(config_exports, {
|
|
|
793
793
|
SWARM_REPO: () => SWARM_REPO,
|
|
794
794
|
SWARM_REPO_PATHS: () => SWARM_REPO_PATHS,
|
|
795
795
|
SWARM_REVIEW_CAP: () => SWARM_REVIEW_CAP,
|
|
796
|
+
SWARM_SCAN_DEPTH: () => SWARM_SCAN_DEPTH,
|
|
796
797
|
SWARM_SHOT_TIMEOUT_MS: () => SWARM_SHOT_TIMEOUT_MS,
|
|
797
798
|
SWARM_STORM_URL: () => SWARM_STORM_URL,
|
|
798
799
|
SWARM_SWEEP_INTERVAL_MS: () => SWARM_SWEEP_INTERVAL_MS,
|
|
@@ -892,7 +893,7 @@ function reviewCutoffAt() {
|
|
|
892
893
|
}
|
|
893
894
|
return at;
|
|
894
895
|
}
|
|
895
|
-
var SWARM_HOME, DB_PATH, DRONE_HOME, DRONE_HOME_ISOLATION, DRONE_ISOLATION, WORKTREES_NOINDEX_DIR, WORKTREES_LEGACY_DIR, WORKTREES_DIR, RUN_LOGS_DIR, DAEMON_LOG_PATH, DIST_BUILD, SWARM_BACKEND, SWARM_OUTBOX, SWARM_MAX_IN_FLIGHT, SWARM_CLAIM_INTERVAL_MS, SWARM_MAX_LOAD_FACTOR, SWARM_MIN_FREE_GB, SWARM_MAX_FD_LOAD_FACTOR, SWARM_MAX_CPU_BUSY, SWARM_RELIEF_LOAD_FACTOR, SWARM_RELIEF_MIN_FREE_GB, SWARM_RELIEF_MAX_FD_LOAD_FACTOR, SWARM_RELIEF_CPU_BUSY, SWARM_RELIEF_COST_WINDOW_MS, SWARM_RELIEF_ALERT_PAUSES, SWARM_RELIEF_ALERT_LOST_MS, SWARM_RELIEF_ALERT_MIN_STEPS, SWARM_RELIEF_ALERT_SUSTAIN_MS, SWARM_LOW_DISK_GB, SWARM_REHYDRATE_MS, SWARM_DRONE_BRIDGE_MS, SWARM_PROD_HEARTBEAT_MS, SWARM_VERSION_CHECK_MS, SWARM_SWEEP_INTERVAL_MS, DRONE_CHANNEL_ENABLED, DRONE_CHANNEL_PORT, DRONE_CHANNEL_URL, DRONE_CHANNEL_SECRET_PATH, DRONE_CHANNEL_GRACE_MS, HEARTBEAT_PATH, HEARTBEAT_STALE_MS, SWARM_REPO, SWARM_REPO_PATHS, TASKLESS_API, SWARM_SHOT_TIMEOUT_MS, TASKLESS_MCP_PATH, TASKLESS_TOKEN, TASKLESS_WORKSPACE, TASKLESS_WORKSPACE_SLUG, TASKLESS_TASK_URL_BASE, SWARM_VERIFY, SWARM_AUTO_REVIEW, REVIEW_CUTOFF_PATH, SWARM_REVIEW_CAP, SWARM_STORM_URL, WEB_PORT, WEB_REDIRECT_URI, SWARM_ANOMALY_RENOTIFY_MS, SWARM_ANOMALY_MAX_REPEATS, SWARM_ANOMALY_RUN_DOWN_WINDOW_MS, SWARM_ANOMALY_FAILED_SPIKE_COUNT, SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS, SWARM_ANOMALY_QUEUE_STALLED_MS, SWARM_ANOMALY_PR_STUCK_MERGE_MS, SWARM_ANOMALY_PR_NO_REVIEWER_MS, SWARM_ANOMALY_QUESTION_UNANSWERED_MS, SWARM_ANOMALY_OUTBOX_STUCK_MS, SWARM_ANOMALY_RESOURCE_GATE_STALL_MS, SWARM_WATCHDOG_STALL_MS;
|
|
896
|
+
var SWARM_HOME, DB_PATH, DRONE_HOME, DRONE_HOME_ISOLATION, DRONE_ISOLATION, WORKTREES_NOINDEX_DIR, WORKTREES_LEGACY_DIR, WORKTREES_DIR, RUN_LOGS_DIR, DAEMON_LOG_PATH, DIST_BUILD, SWARM_BACKEND, SWARM_OUTBOX, SWARM_MAX_IN_FLIGHT, SWARM_CLAIM_INTERVAL_MS, SWARM_MAX_LOAD_FACTOR, SWARM_MIN_FREE_GB, SWARM_MAX_FD_LOAD_FACTOR, SWARM_MAX_CPU_BUSY, SWARM_RELIEF_LOAD_FACTOR, SWARM_RELIEF_MIN_FREE_GB, SWARM_RELIEF_MAX_FD_LOAD_FACTOR, SWARM_RELIEF_CPU_BUSY, SWARM_RELIEF_COST_WINDOW_MS, SWARM_RELIEF_ALERT_PAUSES, SWARM_RELIEF_ALERT_LOST_MS, SWARM_RELIEF_ALERT_MIN_STEPS, SWARM_RELIEF_ALERT_SUSTAIN_MS, SWARM_LOW_DISK_GB, SWARM_REHYDRATE_MS, SWARM_DRONE_BRIDGE_MS, SWARM_PROD_HEARTBEAT_MS, SWARM_VERSION_CHECK_MS, SWARM_SWEEP_INTERVAL_MS, DRONE_CHANNEL_ENABLED, DRONE_CHANNEL_PORT, DRONE_CHANNEL_URL, DRONE_CHANNEL_SECRET_PATH, DRONE_CHANNEL_GRACE_MS, HEARTBEAT_PATH, HEARTBEAT_STALE_MS, SWARM_REPO, SWARM_SCAN_DEPTH, SWARM_REPO_PATHS, TASKLESS_API, SWARM_SHOT_TIMEOUT_MS, TASKLESS_MCP_PATH, TASKLESS_TOKEN, TASKLESS_WORKSPACE, TASKLESS_WORKSPACE_SLUG, TASKLESS_TASK_URL_BASE, SWARM_VERIFY, SWARM_AUTO_REVIEW, REVIEW_CUTOFF_PATH, SWARM_REVIEW_CAP, SWARM_STORM_URL, WEB_PORT, WEB_REDIRECT_URI, SWARM_ANOMALY_RENOTIFY_MS, SWARM_ANOMALY_MAX_REPEATS, SWARM_ANOMALY_RUN_DOWN_WINDOW_MS, SWARM_ANOMALY_FAILED_SPIKE_COUNT, SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS, SWARM_ANOMALY_QUEUE_STALLED_MS, SWARM_ANOMALY_PR_STUCK_MERGE_MS, SWARM_ANOMALY_PR_NO_REVIEWER_MS, SWARM_ANOMALY_QUESTION_UNANSWERED_MS, SWARM_ANOMALY_OUTBOX_STUCK_MS, SWARM_ANOMALY_RESOURCE_GATE_STALL_MS, SWARM_WATCHDOG_STALL_MS;
|
|
896
897
|
var init_config = __esm({
|
|
897
898
|
"src/config.ts"() {
|
|
898
899
|
"use strict";
|
|
@@ -940,6 +941,7 @@ var init_config = __esm({
|
|
|
940
941
|
HEARTBEAT_PATH = process.env.SWARM_HEARTBEAT || join(SWARM_HOME, "overmind.heartbeat");
|
|
941
942
|
HEARTBEAT_STALE_MS = Number(process.env.SWARM_HEARTBEAT_STALE_MS) || 3e4;
|
|
942
943
|
SWARM_REPO = process.env.SWARM_REPO || "";
|
|
944
|
+
SWARM_SCAN_DEPTH = Number(process.env.SWARM_SCAN_DEPTH) || 3;
|
|
943
945
|
SWARM_REPO_PATHS = (process.env.SWARM_REPOS || SWARM_REPO).split(",").map((s) => s.trim()).filter(Boolean);
|
|
944
946
|
TASKLESS_API = process.env.TASKLESS_API || "https://taskless.ru";
|
|
945
947
|
SWARM_SHOT_TIMEOUT_MS = envNum("SWARM_SHOT_TIMEOUT_MS", 2e4);
|
|
@@ -2468,16 +2470,16 @@ var init_procCleanup = __esm({
|
|
|
2468
2470
|
});
|
|
2469
2471
|
|
|
2470
2472
|
// src/git.ts
|
|
2471
|
-
import { existsSync as existsSync8, readdirSync as
|
|
2473
|
+
import { existsSync as existsSync8, readdirSync as readdirSync8, statSync as statSync5 } from "fs";
|
|
2472
2474
|
import { appendFile, mkdir, readFile, rm } from "fs/promises";
|
|
2473
|
-
import { isAbsolute, join as
|
|
2475
|
+
import { isAbsolute, join as join11 } from "path";
|
|
2474
2476
|
import { $ as $2 } from "execa";
|
|
2475
2477
|
async function reclaimStaleWorktree(repo, worktree) {
|
|
2476
2478
|
await detachWorktree(repo, worktree);
|
|
2477
2479
|
}
|
|
2478
2480
|
function isLinkedWorktree(dir) {
|
|
2479
2481
|
try {
|
|
2480
|
-
return
|
|
2482
|
+
return statSync5(join11(dir, ".git")).isFile();
|
|
2481
2483
|
} catch {
|
|
2482
2484
|
return false;
|
|
2483
2485
|
}
|
|
@@ -2583,9 +2585,9 @@ async function ensureGitExcluded(worktree, pattern) {
|
|
|
2583
2585
|
const out = await $2({ reject: false })`git -C ${worktree} rev-parse --git-common-dir`;
|
|
2584
2586
|
const raw = out.stdout.trim();
|
|
2585
2587
|
if (out.exitCode !== 0 || !raw) return false;
|
|
2586
|
-
const commonDir = isAbsolute(raw) ? raw :
|
|
2587
|
-
const infoDir =
|
|
2588
|
-
const excludeFile =
|
|
2588
|
+
const commonDir = isAbsolute(raw) ? raw : join11(worktree, raw);
|
|
2589
|
+
const infoDir = join11(commonDir, "info");
|
|
2590
|
+
const excludeFile = join11(infoDir, "exclude");
|
|
2589
2591
|
let current = "";
|
|
2590
2592
|
try {
|
|
2591
2593
|
current = await readFile(excludeFile, "utf8");
|
|
@@ -2618,7 +2620,7 @@ async function worktreeDirty(path) {
|
|
|
2618
2620
|
async function repairMovedWorktrees(targetDir) {
|
|
2619
2621
|
const subdirs = (dir) => {
|
|
2620
2622
|
try {
|
|
2621
|
-
return
|
|
2623
|
+
return readdirSync8(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => join11(dir, d.name));
|
|
2622
2624
|
} catch {
|
|
2623
2625
|
return [];
|
|
2624
2626
|
}
|
|
@@ -2730,18 +2732,18 @@ __export(droneChannel_exports, {
|
|
|
2730
2732
|
verifyLocalCliToken: () => verifyLocalCliToken
|
|
2731
2733
|
});
|
|
2732
2734
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
2733
|
-
import { chmodSync, mkdirSync as mkdirSync5, readFileSync as
|
|
2735
|
+
import { chmodSync, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "fs";
|
|
2734
2736
|
import { dirname as dirname3 } from "path";
|
|
2735
2737
|
import { serve } from "@hono/node-server";
|
|
2736
2738
|
function loadChannelSecret(path) {
|
|
2737
2739
|
try {
|
|
2738
|
-
const s =
|
|
2740
|
+
const s = readFileSync12(path, "utf8").trim();
|
|
2739
2741
|
if (s.length >= 32) return s;
|
|
2740
2742
|
} catch {
|
|
2741
2743
|
}
|
|
2742
2744
|
const secret = randomBytes(32).toString("hex");
|
|
2743
2745
|
mkdirSync5(dirname3(path), { recursive: true });
|
|
2744
|
-
|
|
2746
|
+
writeFileSync8(path, secret, { mode: 384 });
|
|
2745
2747
|
try {
|
|
2746
2748
|
chmodSync(path, 384);
|
|
2747
2749
|
} catch {
|
|
@@ -3071,10 +3073,10 @@ var init_droneChannel = __esm({
|
|
|
3071
3073
|
|
|
3072
3074
|
// src/attachments.ts
|
|
3073
3075
|
import { mkdir as mkdir2, rm as rm2, writeFile } from "fs/promises";
|
|
3074
|
-
import { join as
|
|
3076
|
+
import { join as join13 } from "path";
|
|
3075
3077
|
function attachmentsFromDoc(doc, origin) {
|
|
3076
3078
|
const out = [];
|
|
3077
|
-
const
|
|
3079
|
+
const walk2 = (node) => {
|
|
3078
3080
|
if (!node || typeof node !== "object") return;
|
|
3079
3081
|
const n = node;
|
|
3080
3082
|
const attrs = n.attrs ?? {};
|
|
@@ -3093,9 +3095,9 @@ function attachmentsFromDoc(doc, origin) {
|
|
|
3093
3095
|
});
|
|
3094
3096
|
}
|
|
3095
3097
|
}
|
|
3096
|
-
if (Array.isArray(n.content)) for (const c of n.content)
|
|
3098
|
+
if (Array.isArray(n.content)) for (const c of n.content) walk2(c);
|
|
3097
3099
|
};
|
|
3098
|
-
|
|
3100
|
+
walk2(doc);
|
|
3099
3101
|
return out;
|
|
3100
3102
|
}
|
|
3101
3103
|
function collectTaskAttachments(task) {
|
|
@@ -3162,7 +3164,7 @@ async function materializeAttachments(opts) {
|
|
|
3162
3164
|
};
|
|
3163
3165
|
const result = { files: [], skipped: [] };
|
|
3164
3166
|
if (opts.refs.length === 0) return result;
|
|
3165
|
-
const dir =
|
|
3167
|
+
const dir = join13(opts.worktree, ATTACHMENTS_DIR);
|
|
3166
3168
|
await io.removeDir(dir);
|
|
3167
3169
|
let dirReady = false;
|
|
3168
3170
|
let total = 0;
|
|
@@ -3208,7 +3210,7 @@ async function materializeAttachments(opts) {
|
|
|
3208
3210
|
await io.mkdir(dir);
|
|
3209
3211
|
dirReady = true;
|
|
3210
3212
|
}
|
|
3211
|
-
await io.writeFile(
|
|
3213
|
+
await io.writeFile(join13(dir, name), file.bytes);
|
|
3212
3214
|
} catch (e) {
|
|
3213
3215
|
result.skipped.push(`${label} \u2014 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u0434\u0438\u0441\u043A (${errText(e)})`);
|
|
3214
3216
|
continue;
|
|
@@ -3361,7 +3363,7 @@ var init_attachments = __esm({
|
|
|
3361
3363
|
|
|
3362
3364
|
// src/shotUpload.ts
|
|
3363
3365
|
import { readFile as fsReadFile } from "fs/promises";
|
|
3364
|
-
import { basename as basename2, isAbsolute as isAbsolute2, join as
|
|
3366
|
+
import { basename as basename2, isAbsolute as isAbsolute2, join as join14 } from "path";
|
|
3365
3367
|
function shotMimetype(path) {
|
|
3366
3368
|
const dot = path.lastIndexOf(".");
|
|
3367
3369
|
if (dot < 0) return null;
|
|
@@ -3387,7 +3389,7 @@ async function readShots(paths, opts) {
|
|
|
3387
3389
|
}
|
|
3388
3390
|
let bytes;
|
|
3389
3391
|
try {
|
|
3390
|
-
bytes = await read(isAbsolute2(path) ? path :
|
|
3392
|
+
bytes = await read(isAbsolute2(path) ? path : join14(opts.worktree, path));
|
|
3391
3393
|
} catch (e) {
|
|
3392
3394
|
skipped.push({ path, reason: `\u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D: ${e instanceof Error ? e.message : String(e)}` });
|
|
3393
3395
|
continue;
|
|
@@ -3465,8 +3467,8 @@ var init_shotUpload = __esm({
|
|
|
3465
3467
|
|
|
3466
3468
|
// src/oauth.ts
|
|
3467
3469
|
import { createHash, randomBytes as randomBytes2 } from "crypto";
|
|
3468
|
-
import { readFileSync as
|
|
3469
|
-
import { join as
|
|
3470
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, unlinkSync as unlinkSync2 } from "fs";
|
|
3471
|
+
import { join as join15 } from "path";
|
|
3470
3472
|
function makePkce() {
|
|
3471
3473
|
const verifier = randomBytes2(48).toString("base64url");
|
|
3472
3474
|
const challenge = createHash("sha256").update(verifier).digest("base64url");
|
|
@@ -3550,13 +3552,13 @@ function toStore(clientId, t, now) {
|
|
|
3550
3552
|
}
|
|
3551
3553
|
function loadStore() {
|
|
3552
3554
|
try {
|
|
3553
|
-
return JSON.parse(
|
|
3555
|
+
return JSON.parse(readFileSync13(STORE_PATH, "utf8"));
|
|
3554
3556
|
} catch {
|
|
3555
3557
|
return null;
|
|
3556
3558
|
}
|
|
3557
3559
|
}
|
|
3558
3560
|
function saveStore(s) {
|
|
3559
|
-
|
|
3561
|
+
writeFileSync9(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
|
|
3560
3562
|
}
|
|
3561
3563
|
function clearStore() {
|
|
3562
3564
|
try {
|
|
@@ -3566,7 +3568,7 @@ function clearStore() {
|
|
|
3566
3568
|
}
|
|
3567
3569
|
function loadClientId() {
|
|
3568
3570
|
try {
|
|
3569
|
-
return JSON.parse(
|
|
3571
|
+
return JSON.parse(readFileSync13(CLIENT_PATH, "utf8")).clientId;
|
|
3570
3572
|
} catch {
|
|
3571
3573
|
return null;
|
|
3572
3574
|
}
|
|
@@ -3575,7 +3577,7 @@ async function ensureClientId(api, redirectUri, fetchFn = fetch) {
|
|
|
3575
3577
|
const existing = loadClientId();
|
|
3576
3578
|
if (existing) return existing;
|
|
3577
3579
|
const clientId = await registerClient({ api, redirectUri }, fetchFn);
|
|
3578
|
-
|
|
3580
|
+
writeFileSync9(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
|
|
3579
3581
|
return clientId;
|
|
3580
3582
|
}
|
|
3581
3583
|
async function ensureAccess(store, opts = {}) {
|
|
@@ -3601,18 +3603,18 @@ var init_oauth = __esm({
|
|
|
3601
3603
|
AUTHORIZE = "/api/oauth/authorize";
|
|
3602
3604
|
TOKEN = "/api/oauth/token";
|
|
3603
3605
|
SCOPE = "openid profile email taskless:full";
|
|
3604
|
-
STORE_PATH =
|
|
3605
|
-
CLIENT_PATH =
|
|
3606
|
+
STORE_PATH = join15(SWARM_HOME, "oauth.json");
|
|
3607
|
+
CLIENT_PATH = join15(SWARM_HOME, "oauth-client.json");
|
|
3606
3608
|
base = (api) => api.replace(/\/$/, "");
|
|
3607
3609
|
}
|
|
3608
3610
|
});
|
|
3609
3611
|
|
|
3610
3612
|
// src/agentToken.ts
|
|
3611
|
-
import { readFileSync as
|
|
3612
|
-
import { join as
|
|
3613
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync10, unlinkSync as unlinkSync3 } from "fs";
|
|
3614
|
+
import { join as join16 } from "path";
|
|
3613
3615
|
function loadAgentToken() {
|
|
3614
3616
|
try {
|
|
3615
|
-
const s = JSON.parse(
|
|
3617
|
+
const s = JSON.parse(readFileSync14(STORE_PATH2, "utf8"));
|
|
3616
3618
|
return s.token ? s : null;
|
|
3617
3619
|
} catch {
|
|
3618
3620
|
return null;
|
|
@@ -3649,17 +3651,17 @@ var init_agentToken = __esm({
|
|
|
3649
3651
|
"src/agentToken.ts"() {
|
|
3650
3652
|
"use strict";
|
|
3651
3653
|
init_config();
|
|
3652
|
-
STORE_PATH2 =
|
|
3654
|
+
STORE_PATH2 = join16(SWARM_HOME, "agent.json");
|
|
3653
3655
|
base2 = (api) => api.replace(/\/$/, "");
|
|
3654
3656
|
}
|
|
3655
3657
|
});
|
|
3656
3658
|
|
|
3657
3659
|
// src/workspace.ts
|
|
3658
|
-
import { readFileSync as
|
|
3659
|
-
import { join as
|
|
3660
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync11, unlinkSync as unlinkSync4 } from "fs";
|
|
3661
|
+
import { join as join17 } from "path";
|
|
3660
3662
|
function loadWorkspace() {
|
|
3661
3663
|
try {
|
|
3662
|
-
const w = JSON.parse(
|
|
3664
|
+
const w = JSON.parse(readFileSync15(WORKSPACE_PATH, "utf8"));
|
|
3663
3665
|
return w && typeof w.id === "string" && w.id ? w : null;
|
|
3664
3666
|
} catch {
|
|
3665
3667
|
return null;
|
|
@@ -3673,7 +3675,7 @@ var init_workspace = __esm({
|
|
|
3673
3675
|
"src/workspace.ts"() {
|
|
3674
3676
|
"use strict";
|
|
3675
3677
|
init_config();
|
|
3676
|
-
WORKSPACE_PATH =
|
|
3678
|
+
WORKSPACE_PATH = join17(SWARM_HOME, "workspace.json");
|
|
3677
3679
|
}
|
|
3678
3680
|
});
|
|
3679
3681
|
|
|
@@ -4137,8 +4139,9 @@ var init_droneStore = __esm({
|
|
|
4137
4139
|
|
|
4138
4140
|
// src/overmind.ts
|
|
4139
4141
|
init_db();
|
|
4140
|
-
import {
|
|
4141
|
-
import {
|
|
4142
|
+
import { homedir as homedir3 } from "os";
|
|
4143
|
+
import { existsSync as existsSync12, readFileSync as readFileSync17, readdirSync as readdirSync10, renameSync as renameSync7 } from "fs";
|
|
4144
|
+
import { basename as basename4, dirname as dirname5, join as join21 } from "path";
|
|
4142
4145
|
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
4143
4146
|
import { spawn as spawn3 } from "child_process";
|
|
4144
4147
|
|
|
@@ -6069,19 +6072,137 @@ async function buildRepoRegistry(paths) {
|
|
|
6069
6072
|
return resolved.filter((e) => e !== null);
|
|
6070
6073
|
}
|
|
6071
6074
|
|
|
6075
|
+
// src/repoMemory.ts
|
|
6076
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
6077
|
+
import { join as join4 } from "path";
|
|
6078
|
+
var FILE = "repos.json";
|
|
6079
|
+
function loadRememberedRepos(home) {
|
|
6080
|
+
let raw;
|
|
6081
|
+
try {
|
|
6082
|
+
raw = readFileSync6(join4(home, FILE), "utf8");
|
|
6083
|
+
} catch {
|
|
6084
|
+
return [];
|
|
6085
|
+
}
|
|
6086
|
+
try {
|
|
6087
|
+
const parsed = JSON.parse(raw);
|
|
6088
|
+
if (!Array.isArray(parsed.paths)) return [];
|
|
6089
|
+
return parsed.paths.filter((p) => typeof p === "string" && p.trim().length > 0);
|
|
6090
|
+
} catch {
|
|
6091
|
+
return [];
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
6094
|
+
function rememberRepos(home, paths, now = () => /* @__PURE__ */ new Date()) {
|
|
6095
|
+
const clean = paths.filter((p) => p.trim().length > 0);
|
|
6096
|
+
if (clean.length === 0) return;
|
|
6097
|
+
writeFileSync5(join4(home, FILE), `${JSON.stringify({ paths: clean, at: now().toISOString() }, null, 2)}
|
|
6098
|
+
`);
|
|
6099
|
+
}
|
|
6100
|
+
function pickRepoPaths(args) {
|
|
6101
|
+
if (args.flag.length > 0) return { paths: [...args.flag], source: "flag" };
|
|
6102
|
+
if (args.env.length > 0) return { paths: [...args.env], source: "env" };
|
|
6103
|
+
if (args.remembered.length > 0) return { paths: [...args.remembered], source: "memory" };
|
|
6104
|
+
return { paths: [], source: "none" };
|
|
6105
|
+
}
|
|
6106
|
+
|
|
6107
|
+
// src/repoScan.ts
|
|
6108
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
|
|
6109
|
+
import { join as join5 } from "path";
|
|
6110
|
+
var SKIP = /* @__PURE__ */ new Set([
|
|
6111
|
+
"node_modules",
|
|
6112
|
+
"vendor",
|
|
6113
|
+
"dist",
|
|
6114
|
+
"build",
|
|
6115
|
+
"target",
|
|
6116
|
+
"Library",
|
|
6117
|
+
"Applications",
|
|
6118
|
+
".Trash",
|
|
6119
|
+
"venv",
|
|
6120
|
+
".venv",
|
|
6121
|
+
"__pycache__"
|
|
6122
|
+
]);
|
|
6123
|
+
function parseOriginFromGitConfig(text) {
|
|
6124
|
+
let inOrigin = false;
|
|
6125
|
+
for (const line of text.split("\n")) {
|
|
6126
|
+
const t = line.trim();
|
|
6127
|
+
if (t.startsWith("[")) {
|
|
6128
|
+
inOrigin = /^\[remote\s+"origin"\]$/.test(t);
|
|
6129
|
+
continue;
|
|
6130
|
+
}
|
|
6131
|
+
if (!inOrigin) continue;
|
|
6132
|
+
const m = /^url\s*=\s*(.+)$/.exec(t);
|
|
6133
|
+
if (m?.[1]) return m[1].trim();
|
|
6134
|
+
}
|
|
6135
|
+
return null;
|
|
6136
|
+
}
|
|
6137
|
+
function originOf(dir) {
|
|
6138
|
+
try {
|
|
6139
|
+
return parseOriginFromGitConfig(readFileSync7(join5(dir, ".git", "config"), "utf8"));
|
|
6140
|
+
} catch {
|
|
6141
|
+
return null;
|
|
6142
|
+
}
|
|
6143
|
+
}
|
|
6144
|
+
function scanRoots(roots, opts = {}) {
|
|
6145
|
+
const maxDepth = opts.maxDepth ?? 3;
|
|
6146
|
+
const found = /* @__PURE__ */ new Map();
|
|
6147
|
+
for (const root of roots) walk(root, 0, maxDepth, found);
|
|
6148
|
+
return found;
|
|
6149
|
+
}
|
|
6150
|
+
function walk(dir, depth, maxDepth, found) {
|
|
6151
|
+
if (depth > maxDepth) return;
|
|
6152
|
+
const origin = originOf(dir);
|
|
6153
|
+
if (origin) {
|
|
6154
|
+
const full = parseRepoFullName(origin);
|
|
6155
|
+
if (full && !found.has(full)) found.set(full, dir);
|
|
6156
|
+
return;
|
|
6157
|
+
}
|
|
6158
|
+
let entries;
|
|
6159
|
+
try {
|
|
6160
|
+
entries = readdirSync2(dir);
|
|
6161
|
+
} catch {
|
|
6162
|
+
return;
|
|
6163
|
+
}
|
|
6164
|
+
for (const name of entries) {
|
|
6165
|
+
if (name.startsWith(".") || SKIP.has(name)) continue;
|
|
6166
|
+
const child = join5(dir, name);
|
|
6167
|
+
try {
|
|
6168
|
+
if (!statSync2(child).isDirectory()) continue;
|
|
6169
|
+
} catch {
|
|
6170
|
+
continue;
|
|
6171
|
+
}
|
|
6172
|
+
walk(child, depth + 1, maxDepth, found);
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
function defaultScanRoots(home, knownPaths = []) {
|
|
6176
|
+
const out = [];
|
|
6177
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6178
|
+
const add = (p) => {
|
|
6179
|
+
if (!p || seen.has(p)) return;
|
|
6180
|
+
seen.add(p);
|
|
6181
|
+
out.push(p);
|
|
6182
|
+
};
|
|
6183
|
+
for (const p of knownPaths) {
|
|
6184
|
+
const parent = p.replace(/\/+$/, "").replace(/\/[^/]+$/, "");
|
|
6185
|
+
if (parent && parent !== p) add(parent);
|
|
6186
|
+
}
|
|
6187
|
+
for (const name of ["projects", "code", "src", "dev", "work", "repos", "git", "Projects", "Code", "Developer", "Documents/GitHub", "go/src"]) {
|
|
6188
|
+
add(join5(home, name));
|
|
6189
|
+
}
|
|
6190
|
+
return out;
|
|
6191
|
+
}
|
|
6192
|
+
|
|
6072
6193
|
// src/hostIdentity.ts
|
|
6073
6194
|
import { hostname } from "os";
|
|
6074
|
-
import { existsSync as existsSync5, readFileSync as
|
|
6075
|
-
import { join as
|
|
6195
|
+
import { existsSync as existsSync5, readFileSync as readFileSync8, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
6196
|
+
import { join as join6 } from "path";
|
|
6076
6197
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6077
6198
|
function hostIdentityPath(home) {
|
|
6078
|
-
return
|
|
6199
|
+
return join6(home, "host.json");
|
|
6079
6200
|
}
|
|
6080
6201
|
function loadHostIdentity(deps) {
|
|
6081
6202
|
const exists = deps.exists ?? existsSync5;
|
|
6082
|
-
const readFile2 = deps.readFile ?? ((p) =>
|
|
6203
|
+
const readFile2 = deps.readFile ?? ((p) => readFileSync8(p, "utf8"));
|
|
6083
6204
|
const writeFile2 = deps.writeFile ?? ((p, data) => {
|
|
6084
|
-
|
|
6205
|
+
writeFileSync6(`${p}.tmp`, data);
|
|
6085
6206
|
renameSync4(`${p}.tmp`, p);
|
|
6086
6207
|
});
|
|
6087
6208
|
const host = deps.hostname ?? hostname;
|
|
@@ -6264,14 +6385,14 @@ function buildReliefCostCandidates(cost, t, sustainMs, build) {
|
|
|
6264
6385
|
|
|
6265
6386
|
// src/launcher.ts
|
|
6266
6387
|
import { spawn } from "child_process";
|
|
6267
|
-
import { closeSync as closeSync2, existsSync as existsSync7, mkdirSync as mkdirSync4, openSync as openSync2, readdirSync as
|
|
6388
|
+
import { closeSync as closeSync2, existsSync as existsSync7, mkdirSync as mkdirSync4, openSync as openSync2, readdirSync as readdirSync5, statSync as statSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
6268
6389
|
import { createRequire as createRequire2 } from "module";
|
|
6269
6390
|
import { homedir as homedir2 } from "os";
|
|
6270
|
-
import { dirname as dirname2, join as
|
|
6391
|
+
import { dirname as dirname2, join as join8 } from "path";
|
|
6271
6392
|
import { fileURLToPath } from "url";
|
|
6272
6393
|
|
|
6273
6394
|
// src/claudeBin.ts
|
|
6274
|
-
import { existsSync as existsSync6, readdirSync as
|
|
6395
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
|
|
6275
6396
|
import { win32 as winPath } from "path";
|
|
6276
6397
|
var CLAUDE_FALLBACK = "claude";
|
|
6277
6398
|
function compareVersionDirs(a, b) {
|
|
@@ -6286,7 +6407,7 @@ function compareVersionDirs(a, b) {
|
|
|
6286
6407
|
}
|
|
6287
6408
|
function realListDir(dir) {
|
|
6288
6409
|
try {
|
|
6289
|
-
return
|
|
6410
|
+
return readdirSync3(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
6290
6411
|
} catch {
|
|
6291
6412
|
return [];
|
|
6292
6413
|
}
|
|
@@ -6343,8 +6464,8 @@ function resolveClaudeBin(opts = {}) {
|
|
|
6343
6464
|
}
|
|
6344
6465
|
|
|
6345
6466
|
// src/droneHome.ts
|
|
6346
|
-
import { lstatSync, mkdirSync as mkdirSync3, readdirSync as
|
|
6347
|
-
import { join as
|
|
6467
|
+
import { lstatSync, mkdirSync as mkdirSync3, readdirSync as readdirSync4, rmSync, statSync as statSync3, symlinkSync } from "fs";
|
|
6468
|
+
import { join as join7, relative, sep } from "path";
|
|
6348
6469
|
var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
|
|
6349
6470
|
function droneHomeDeny(env = process.env) {
|
|
6350
6471
|
const extra = (env.SWARM_DRONE_HOME_DENY || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -6363,28 +6484,28 @@ function buildDroneHome(opts) {
|
|
|
6363
6484
|
const deny = opts.deny ?? droneHomeDeny();
|
|
6364
6485
|
const platform = opts.platform ?? process.platform;
|
|
6365
6486
|
mkdirSync3(droneHome, { recursive: true, mode: 448 });
|
|
6366
|
-
const wanted = new Set(mirroredEntries(
|
|
6487
|
+
const wanted = new Set(mirroredEntries(readdirSync4(realHome), deny, platform));
|
|
6367
6488
|
const rel = relative(realHome, droneHome);
|
|
6368
6489
|
if (rel && !rel.startsWith("..") && !rel.includes(sep)) wanted.delete(rel);
|
|
6369
|
-
for (const existing of
|
|
6490
|
+
for (const existing of readdirSync4(droneHome)) {
|
|
6370
6491
|
if (wanted.has(existing)) continue;
|
|
6371
|
-
const p =
|
|
6492
|
+
const p = join7(droneHome, existing);
|
|
6372
6493
|
try {
|
|
6373
6494
|
if (lstatSync(p).isSymbolicLink()) rmSync(p);
|
|
6374
6495
|
} catch {
|
|
6375
6496
|
}
|
|
6376
6497
|
}
|
|
6377
6498
|
for (const name of wanted) {
|
|
6378
|
-
const link =
|
|
6499
|
+
const link = join7(droneHome, name);
|
|
6379
6500
|
try {
|
|
6380
6501
|
lstatSync(link);
|
|
6381
6502
|
continue;
|
|
6382
6503
|
} catch {
|
|
6383
6504
|
}
|
|
6384
|
-
const target =
|
|
6505
|
+
const target = join7(realHome, name);
|
|
6385
6506
|
let isDir;
|
|
6386
6507
|
try {
|
|
6387
|
-
isDir =
|
|
6508
|
+
isDir = statSync3(target).isDirectory();
|
|
6388
6509
|
} catch {
|
|
6389
6510
|
continue;
|
|
6390
6511
|
}
|
|
@@ -6402,7 +6523,7 @@ var REAL_PATH_ENV_PINS = [
|
|
|
6402
6523
|
{ env: "YARN_GLOBAL_FOLDER", segments: [".yarn", "berry"] }
|
|
6403
6524
|
];
|
|
6404
6525
|
function realPathEnvPins(realHome) {
|
|
6405
|
-
return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env,
|
|
6526
|
+
return Object.fromEntries(REAL_PATH_ENV_PINS.map(({ env, segments }) => [env, join7(realHome, ...segments)]));
|
|
6406
6527
|
}
|
|
6407
6528
|
function droneDenyRules(realHome, swarmHome) {
|
|
6408
6529
|
const paths = [
|
|
@@ -6494,7 +6615,7 @@ function droneEnv(worktree, base3 = process.env, isolation, droneCap = null) {
|
|
|
6494
6615
|
const env = { ...base3 };
|
|
6495
6616
|
const isolate = base3.SWARM_TURBO_ISOLATE !== "0";
|
|
6496
6617
|
if (isolate && !base3.TURBO_CACHE_DIR) {
|
|
6497
|
-
env.TURBO_CACHE_DIR =
|
|
6618
|
+
env.TURBO_CACHE_DIR = join8(worktree, ".turbo", "cache");
|
|
6498
6619
|
}
|
|
6499
6620
|
Object.assign(env, droneTestEnv(droneCap, base3));
|
|
6500
6621
|
for (const key of DAEMON_STATE_ENV) delete env[key];
|
|
@@ -6522,29 +6643,29 @@ var DAEMON_STATE_ENV = [
|
|
|
6522
6643
|
"TASKLESS_TOKEN"
|
|
6523
6644
|
];
|
|
6524
6645
|
function bundledRoleDir(kind) {
|
|
6525
|
-
return
|
|
6646
|
+
return join8(dirname2(fileURLToPath(import.meta.url)), "..", "roles", kind);
|
|
6526
6647
|
}
|
|
6527
6648
|
function roleDir(worktree, kind) {
|
|
6528
|
-
return
|
|
6649
|
+
return join8(worktree, ".claude", "roles", kind);
|
|
6529
6650
|
}
|
|
6530
6651
|
function roleDirs(worktree, kind) {
|
|
6531
6652
|
return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync7(dir));
|
|
6532
6653
|
}
|
|
6533
6654
|
function transcriptDir(worktree, projectsDir) {
|
|
6534
|
-
return
|
|
6655
|
+
return join8(projectsDir, worktree.replace(/[/.]/g, "-"));
|
|
6535
6656
|
}
|
|
6536
|
-
function sessionTranscriptExists(worktree, projectsDir =
|
|
6657
|
+
function sessionTranscriptExists(worktree, projectsDir = join8(homedir2(), ".claude", "projects")) {
|
|
6537
6658
|
try {
|
|
6538
|
-
return
|
|
6659
|
+
return readdirSync5(transcriptDir(worktree, projectsDir)).some((f) => f.endsWith(".jsonl"));
|
|
6539
6660
|
} catch {
|
|
6540
6661
|
return false;
|
|
6541
6662
|
}
|
|
6542
6663
|
}
|
|
6543
|
-
function transcriptMtimeMs(worktree, projectsDir =
|
|
6664
|
+
function transcriptMtimeMs(worktree, projectsDir = join8(homedir2(), ".claude", "projects")) {
|
|
6544
6665
|
const dir = transcriptDir(worktree, projectsDir);
|
|
6545
6666
|
let files;
|
|
6546
6667
|
try {
|
|
6547
|
-
files =
|
|
6668
|
+
files = readdirSync5(dir);
|
|
6548
6669
|
} catch {
|
|
6549
6670
|
return null;
|
|
6550
6671
|
}
|
|
@@ -6552,7 +6673,7 @@ function transcriptMtimeMs(worktree, projectsDir = join6(homedir2(), ".claude",
|
|
|
6552
6673
|
for (const f of files) {
|
|
6553
6674
|
if (!f.endsWith(".jsonl")) continue;
|
|
6554
6675
|
try {
|
|
6555
|
-
const mtime2 =
|
|
6676
|
+
const mtime2 = statSync4(join8(dir, f)).mtimeMs;
|
|
6556
6677
|
if (latest === null || mtime2 > latest) latest = mtime2;
|
|
6557
6678
|
} catch {
|
|
6558
6679
|
}
|
|
@@ -6560,11 +6681,11 @@ function transcriptMtimeMs(worktree, projectsDir = join6(homedir2(), ".claude",
|
|
|
6560
6681
|
return latest;
|
|
6561
6682
|
}
|
|
6562
6683
|
function nextRunLogPath(runLogsDir, runId) {
|
|
6563
|
-
const dir =
|
|
6684
|
+
const dir = join8(runLogsDir, String(runId));
|
|
6564
6685
|
mkdirSync4(dir, { recursive: true });
|
|
6565
6686
|
let files = [];
|
|
6566
6687
|
try {
|
|
6567
|
-
files =
|
|
6688
|
+
files = readdirSync5(dir);
|
|
6568
6689
|
} catch {
|
|
6569
6690
|
}
|
|
6570
6691
|
let next = 0;
|
|
@@ -6572,7 +6693,7 @@ function nextRunLogPath(runLogsDir, runId) {
|
|
|
6572
6693
|
const m = /^(\d+)\.json/.exec(f);
|
|
6573
6694
|
if (m) next = Math.max(next, Number(m[1]) + 1);
|
|
6574
6695
|
}
|
|
6575
|
-
return
|
|
6696
|
+
return join8(dir, `${next}.json`);
|
|
6576
6697
|
}
|
|
6577
6698
|
function openNextRunLog(runLogsDir, runId) {
|
|
6578
6699
|
for (let attempt = 0; attempt < 50; attempt++) {
|
|
@@ -6646,16 +6767,16 @@ function makeLauncher(cfg) {
|
|
|
6646
6767
|
}
|
|
6647
6768
|
}
|
|
6648
6769
|
};
|
|
6649
|
-
const cfgPath =
|
|
6650
|
-
|
|
6770
|
+
const cfgPath = join8(worktree, ".swarm-mcp.json");
|
|
6771
|
+
writeFileSync7(cfgPath, JSON.stringify(mcpConfig, null, 2));
|
|
6651
6772
|
const model = run.model ?? process.env.SWARM_DRONE_MODEL ?? void 0;
|
|
6652
6773
|
const pluginDirs = roleDirs(worktree, run.kind);
|
|
6653
6774
|
const realHome = cfg.droneIsolation?.realHome ?? homedir2();
|
|
6654
6775
|
const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
|
|
6655
6776
|
const settings = cfg.droneIsolation ? droneSettingsJson(realHome, cfg.droneIsolation.swarmHome) : void 0;
|
|
6656
|
-
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync7(
|
|
6777
|
+
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync7(join8(cfg.droneIsolation.swarmHome, "token"))) {
|
|
6657
6778
|
console.error(
|
|
6658
|
-
`[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 ${
|
|
6779
|
+
`[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 ${join8(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.`
|
|
6659
6780
|
);
|
|
6660
6781
|
}
|
|
6661
6782
|
let stdio = "ignore";
|
|
@@ -6689,8 +6810,8 @@ function makeLauncher(cfg) {
|
|
|
6689
6810
|
init_procCleanup();
|
|
6690
6811
|
|
|
6691
6812
|
// src/cost.ts
|
|
6692
|
-
import { readFileSync as
|
|
6693
|
-
import { join as
|
|
6813
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync6, renameSync as renameSync5 } from "fs";
|
|
6814
|
+
import { join as join9 } from "path";
|
|
6694
6815
|
|
|
6695
6816
|
// src/pricing.ts
|
|
6696
6817
|
var MODEL_PRICING = {
|
|
@@ -6741,23 +6862,23 @@ function launchCostUsd(result, model) {
|
|
|
6741
6862
|
var UNPROCESSED_LOG_RE = /^\d+\.json$/;
|
|
6742
6863
|
function unprocessedLogFiles(dir) {
|
|
6743
6864
|
try {
|
|
6744
|
-
return
|
|
6865
|
+
return readdirSync6(dir).filter((f) => UNPROCESSED_LOG_RE.test(f));
|
|
6745
6866
|
} catch {
|
|
6746
6867
|
return [];
|
|
6747
6868
|
}
|
|
6748
6869
|
}
|
|
6749
6870
|
function accountRunCost(deps, run) {
|
|
6750
|
-
const dir =
|
|
6871
|
+
const dir = join9(deps.runLogsDir, String(run.id));
|
|
6751
6872
|
const files = unprocessedLogFiles(dir);
|
|
6752
6873
|
if (files.length === 0) return;
|
|
6753
6874
|
const model = run.model ?? process.env.SWARM_DRONE_MODEL ?? null;
|
|
6754
6875
|
let added = 0;
|
|
6755
6876
|
const processed = [];
|
|
6756
6877
|
for (const f of files) {
|
|
6757
|
-
const full =
|
|
6878
|
+
const full = join9(dir, f);
|
|
6758
6879
|
let raw = null;
|
|
6759
6880
|
try {
|
|
6760
|
-
raw =
|
|
6881
|
+
raw = readFileSync9(full, "utf8");
|
|
6761
6882
|
} catch {
|
|
6762
6883
|
continue;
|
|
6763
6884
|
}
|
|
@@ -6784,14 +6905,14 @@ function makeCostAccountant(deps) {
|
|
|
6784
6905
|
}
|
|
6785
6906
|
|
|
6786
6907
|
// src/runOutcome.ts
|
|
6787
|
-
import { readdirSync as
|
|
6788
|
-
import { join as
|
|
6908
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync10 } from "fs";
|
|
6909
|
+
import { join as join10 } from "path";
|
|
6789
6910
|
var RESULT_FILE_RE = /^(\d+)\.json(\.done)?$/;
|
|
6790
6911
|
function classifyExit(runLogsDir, runId) {
|
|
6791
|
-
const dir =
|
|
6912
|
+
const dir = join10(runLogsDir, String(runId));
|
|
6792
6913
|
let files;
|
|
6793
6914
|
try {
|
|
6794
|
-
files =
|
|
6915
|
+
files = readdirSync7(dir);
|
|
6795
6916
|
} catch {
|
|
6796
6917
|
return "unknown";
|
|
6797
6918
|
}
|
|
@@ -6805,7 +6926,7 @@ function classifyExit(runLogsDir, runId) {
|
|
|
6805
6926
|
if (!latest) return "unknown";
|
|
6806
6927
|
let raw;
|
|
6807
6928
|
try {
|
|
6808
|
-
raw =
|
|
6929
|
+
raw = readFileSync10(join10(dir, latest.name), "utf8");
|
|
6809
6930
|
} catch {
|
|
6810
6931
|
return "unknown";
|
|
6811
6932
|
}
|
|
@@ -6819,8 +6940,8 @@ function makeExitClassifier(runLogsDir) {
|
|
|
6819
6940
|
init_git();
|
|
6820
6941
|
|
|
6821
6942
|
// src/deps.ts
|
|
6822
|
-
import { existsSync as existsSync9, readdirSync as
|
|
6823
|
-
import { join as
|
|
6943
|
+
import { existsSync as existsSync9, readdirSync as readdirSync9, readFileSync as readFileSync11 } from "fs";
|
|
6944
|
+
import { join as join12 } from "path";
|
|
6824
6945
|
import { execa } from "execa";
|
|
6825
6946
|
var PREPARE_TIMEOUT_MS = 15 * 6e4;
|
|
6826
6947
|
var MAX_PLAN_COMMANDS = 12;
|
|
@@ -6836,21 +6957,21 @@ async function runCommand(cmd, worktree) {
|
|
|
6836
6957
|
}
|
|
6837
6958
|
function safeReadText(p) {
|
|
6838
6959
|
try {
|
|
6839
|
-
return
|
|
6960
|
+
return readFileSync11(p, "utf8");
|
|
6840
6961
|
} catch {
|
|
6841
6962
|
return null;
|
|
6842
6963
|
}
|
|
6843
6964
|
}
|
|
6844
6965
|
function safeListDirs(p) {
|
|
6845
6966
|
try {
|
|
6846
|
-
return
|
|
6967
|
+
return readdirSync9(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
6847
6968
|
} catch {
|
|
6848
6969
|
return [];
|
|
6849
6970
|
}
|
|
6850
6971
|
}
|
|
6851
6972
|
function safeListFiles(p) {
|
|
6852
6973
|
try {
|
|
6853
|
-
return
|
|
6974
|
+
return readdirSync9(p, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name);
|
|
6854
6975
|
} catch {
|
|
6855
6976
|
return [];
|
|
6856
6977
|
}
|
|
@@ -6878,19 +6999,19 @@ function overridePlan(override) {
|
|
|
6878
6999
|
function detectPackageManager(worktree, io = {}) {
|
|
6879
7000
|
const exists = io.exists ?? existsSync9;
|
|
6880
7001
|
const readText = io.readText ?? safeReadText;
|
|
6881
|
-
const hasPkgJson = exists(
|
|
7002
|
+
const hasPkgJson = exists(join12(worktree, "package.json"));
|
|
6882
7003
|
if (!hasPkgJson) {
|
|
6883
|
-
if (exists(
|
|
7004
|
+
if (exists(join12(worktree, "deno.json")) || exists(join12(worktree, "deno.jsonc")) || exists(join12(worktree, "deno.lock"))) return "deno";
|
|
6884
7005
|
return null;
|
|
6885
7006
|
}
|
|
6886
|
-
const pkg = readJson(readText,
|
|
7007
|
+
const pkg = readJson(readText, join12(worktree, "package.json"));
|
|
6887
7008
|
const declared = typeof pkg?.packageManager === "string" ? pkg.packageManager.split("@")[0] : void 0;
|
|
6888
7009
|
if (declared === "pnpm" || declared === "bun" || declared === "npm" || declared === "yarn") return declared;
|
|
6889
|
-
if (exists(
|
|
6890
|
-
if (exists(
|
|
6891
|
-
if (exists(
|
|
6892
|
-
if (exists(
|
|
6893
|
-
if (exists(
|
|
7010
|
+
if (exists(join12(worktree, "bun.lock")) || exists(join12(worktree, "bun.lockb"))) return "bun";
|
|
7011
|
+
if (exists(join12(worktree, "pnpm-lock.yaml"))) return "pnpm";
|
|
7012
|
+
if (exists(join12(worktree, "yarn.lock"))) return "yarn";
|
|
7013
|
+
if (exists(join12(worktree, "package-lock.json"))) return "npm";
|
|
7014
|
+
if (exists(join12(worktree, "deno.lock"))) return "deno";
|
|
6894
7015
|
return "npm";
|
|
6895
7016
|
}
|
|
6896
7017
|
function jsInstallCommand(pm) {
|
|
@@ -6951,7 +7072,7 @@ function detectPlan(worktree, io) {
|
|
|
6951
7072
|
// node_modules на месте — установку пропускаем (быстрый путь переиспользованного worktree).
|
|
6952
7073
|
// У deno каталога может не быть вовсе (кэш в DENO_DIR) — тогда `deno install` отработает
|
|
6953
7074
|
// заново, но он дешёвый: тянет из того же общего кэша.
|
|
6954
|
-
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists:
|
|
7075
|
+
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join12(worktree, "node_modules"), source: "detect" }
|
|
6955
7076
|
];
|
|
6956
7077
|
for (const target of workspaceBuildTargets(worktree, io)) {
|
|
6957
7078
|
commands.push({ label: `\u0441\u0431\u043E\u0440\u043A\u0430 ${target.name}`, run: jsRunScript(pm, "build"), cwd: target.dir, source: "detect" });
|
|
@@ -6959,12 +7080,12 @@ function detectPlan(worktree, io) {
|
|
|
6959
7080
|
return { source: "detect", ecosystem: `node/${pm}`, commands };
|
|
6960
7081
|
}
|
|
6961
7082
|
for (const marker of ECOSYSTEM_MARKERS) {
|
|
6962
|
-
const hit = marker.files.some((f) => exists(
|
|
7083
|
+
const hit = marker.files.some((f) => exists(join12(worktree, f))) || marker.ext !== void 0 && listFiles(worktree).some((n) => n.endsWith(marker.ext));
|
|
6963
7084
|
if (!hit) continue;
|
|
6964
7085
|
let ecosystem = marker.ecosystem;
|
|
6965
7086
|
let command = marker.command;
|
|
6966
7087
|
for (const refine of marker.refine ?? []) {
|
|
6967
|
-
if (!exists(
|
|
7088
|
+
if (!exists(join12(worktree, refine.file))) continue;
|
|
6968
7089
|
ecosystem = refine.ecosystem;
|
|
6969
7090
|
command = refine.command;
|
|
6970
7091
|
break;
|
|
@@ -7021,12 +7142,12 @@ var CI_INSTALL_PATTERNS = [
|
|
|
7021
7142
|
function ciWorkflowPlan(worktree, io) {
|
|
7022
7143
|
const readText = io.readText ?? safeReadText;
|
|
7023
7144
|
const listFiles = io.listFiles ?? safeListFiles;
|
|
7024
|
-
const dir =
|
|
7145
|
+
const dir = join12(worktree, ".github", "workflows");
|
|
7025
7146
|
const files = listFiles(dir).filter((n) => n.endsWith(".yml") || n.endsWith(".yaml"));
|
|
7026
7147
|
const seen = /* @__PURE__ */ new Set();
|
|
7027
7148
|
const commands = [];
|
|
7028
7149
|
for (const name of files.sort()) {
|
|
7029
|
-
const yaml = readText(
|
|
7150
|
+
const yaml = readText(join12(dir, name));
|
|
7030
7151
|
if (yaml === null) continue;
|
|
7031
7152
|
for (const raw of workflowRunLines(yaml)) {
|
|
7032
7153
|
const line = raw.replace(/\s+/g, " ").trim();
|
|
@@ -7041,7 +7162,7 @@ function ciWorkflowPlan(worktree, io) {
|
|
|
7041
7162
|
return commands.length ? { source: "ci-workflow", ecosystem: "ci-\u043A\u043E\u043D\u0444\u0438\u0433", commands } : null;
|
|
7042
7163
|
}
|
|
7043
7164
|
function workspaceGlobs(worktree, readText) {
|
|
7044
|
-
const yaml = readText(
|
|
7165
|
+
const yaml = readText(join12(worktree, "pnpm-workspace.yaml"));
|
|
7045
7166
|
if (yaml !== null) {
|
|
7046
7167
|
const globs = [];
|
|
7047
7168
|
let inPackages = false;
|
|
@@ -7061,7 +7182,7 @@ function workspaceGlobs(worktree, readText) {
|
|
|
7061
7182
|
}
|
|
7062
7183
|
if (globs.length) return globs;
|
|
7063
7184
|
}
|
|
7064
|
-
const pkg = readJson(readText,
|
|
7185
|
+
const pkg = readJson(readText, join12(worktree, "package.json"));
|
|
7065
7186
|
const ws = pkg?.workspaces;
|
|
7066
7187
|
const list = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
|
|
7067
7188
|
return list.filter((g) => typeof g === "string");
|
|
@@ -7072,14 +7193,14 @@ function workspaceDirs(worktree, globs, io) {
|
|
|
7072
7193
|
if (glob.startsWith("!")) continue;
|
|
7073
7194
|
const star = glob.indexOf("*");
|
|
7074
7195
|
if (star === -1) {
|
|
7075
|
-
const dir =
|
|
7076
|
-
if (io.exists(
|
|
7196
|
+
const dir = join12(worktree, glob);
|
|
7197
|
+
if (io.exists(join12(dir, "package.json"))) dirs.add(dir);
|
|
7077
7198
|
continue;
|
|
7078
7199
|
}
|
|
7079
|
-
const base3 =
|
|
7200
|
+
const base3 = join12(worktree, glob.slice(0, star).replace(/\/$/, ""));
|
|
7080
7201
|
for (const name of io.listDirs(base3)) {
|
|
7081
|
-
const dir =
|
|
7082
|
-
if (io.exists(
|
|
7202
|
+
const dir = join12(base3, name);
|
|
7203
|
+
if (io.exists(join12(dir, "package.json"))) dirs.add(dir);
|
|
7083
7204
|
}
|
|
7084
7205
|
}
|
|
7085
7206
|
return [...dirs];
|
|
@@ -7107,11 +7228,11 @@ function workspaceBuildTargets(worktree, io = {}) {
|
|
|
7107
7228
|
if (!globs.length) return [];
|
|
7108
7229
|
const pkgs = [];
|
|
7109
7230
|
for (const dir of workspaceDirs(worktree, globs, { exists, listDirs })) {
|
|
7110
|
-
const pkg = readJson(readText,
|
|
7231
|
+
const pkg = readJson(readText, join12(dir, "package.json"));
|
|
7111
7232
|
if (!pkg || typeof pkg.name !== "string") continue;
|
|
7112
7233
|
const scripts = pkg.scripts ?? {};
|
|
7113
7234
|
const entries = entryPaths(pkg);
|
|
7114
|
-
const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(
|
|
7235
|
+
const needsBuild = typeof scripts.build === "string" && entries.length > 0 && entries.some((e) => !exists(join12(dir, e)));
|
|
7115
7236
|
const deps = Object.keys({ ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} });
|
|
7116
7237
|
pkgs.push({ name: pkg.name, dir, deps, needsBuild });
|
|
7117
7238
|
}
|
|
@@ -8685,7 +8806,7 @@ function makeExclusiveReader(opts) {
|
|
|
8685
8806
|
|
|
8686
8807
|
// src/envGate.ts
|
|
8687
8808
|
import { existsSync as existsSync10 } from "fs";
|
|
8688
|
-
import { join as
|
|
8809
|
+
import { join as join18, delimiter } from "path";
|
|
8689
8810
|
import { createConnection } from "net";
|
|
8690
8811
|
import { execa as execa2 } from "execa";
|
|
8691
8812
|
var PROBE_TIMEOUT_MS = 2500;
|
|
@@ -8737,7 +8858,7 @@ var COMPOSE_MARKERS = [
|
|
|
8737
8858
|
];
|
|
8738
8859
|
function detectRequirements(repo, io = {}) {
|
|
8739
8860
|
const exists = io.exists ?? existsSync10;
|
|
8740
|
-
const marker = COMPOSE_MARKERS.find((f) => exists(
|
|
8861
|
+
const marker = COMPOSE_MARKERS.find((f) => exists(join18(repo, f)));
|
|
8741
8862
|
return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
|
|
8742
8863
|
}
|
|
8743
8864
|
function resolveRequirements(repo, declared, io = {}) {
|
|
@@ -8759,7 +8880,7 @@ async function hasCommand(name, env) {
|
|
|
8759
8880
|
if (name.includes("/")) return existsSync10(name);
|
|
8760
8881
|
const dirs = (env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
8761
8882
|
const exts = process.platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
|
|
8762
|
-
return dirs.some((dir) => exts.some((ext) => existsSync10(
|
|
8883
|
+
return dirs.some((dir) => exts.some((ext) => existsSync10(join18(dir, name + ext))));
|
|
8763
8884
|
}
|
|
8764
8885
|
async function portOpen(host, port) {
|
|
8765
8886
|
return new Promise((resolve3) => {
|
|
@@ -8876,8 +8997,8 @@ import { basename as basename3 } from "path";
|
|
|
8876
8997
|
import { serve as serve2 } from "@hono/node-server";
|
|
8877
8998
|
|
|
8878
8999
|
// src/diff.ts
|
|
8879
|
-
import { statSync as
|
|
8880
|
-
import { join as
|
|
9000
|
+
import { statSync as statSync6 } from "fs";
|
|
9001
|
+
import { join as join19 } from "path";
|
|
8881
9002
|
import { $ as $4 } from "execa";
|
|
8882
9003
|
var MAX_FILES = 200;
|
|
8883
9004
|
var MAX_LINES_PER_FILE = 1500;
|
|
@@ -9020,7 +9141,7 @@ async function resolveBase(wt, baseBranch) {
|
|
|
9020
9141
|
}
|
|
9021
9142
|
function mtime(path) {
|
|
9022
9143
|
try {
|
|
9023
|
-
return
|
|
9144
|
+
return statSync6(path).mtimeMs;
|
|
9024
9145
|
} catch {
|
|
9025
9146
|
return 0;
|
|
9026
9147
|
}
|
|
@@ -9045,7 +9166,7 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
9045
9166
|
files.push(pf);
|
|
9046
9167
|
}
|
|
9047
9168
|
}
|
|
9048
|
-
files.sort((a, b) => mtime(
|
|
9169
|
+
files.sort((a, b) => mtime(join19(worktree, b.path)) - mtime(join19(worktree, a.path)));
|
|
9049
9170
|
const truncatedFiles = files.length > MAX_FILES;
|
|
9050
9171
|
if (truncatedFiles) files = files.slice(0, MAX_FILES);
|
|
9051
9172
|
const added = files.reduce((s, f) => s + f.added, 0);
|
|
@@ -9064,7 +9185,7 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
9064
9185
|
// package.json
|
|
9065
9186
|
var package_default = {
|
|
9066
9187
|
name: "@taskless-app/swarm",
|
|
9067
|
-
version: "0.4.
|
|
9188
|
+
version: "0.4.7",
|
|
9068
9189
|
type: "module",
|
|
9069
9190
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
9070
9191
|
keywords: [
|
|
@@ -10796,16 +10917,16 @@ async function fetchExternalStorm(url, opts = {}) {
|
|
|
10796
10917
|
}
|
|
10797
10918
|
|
|
10798
10919
|
// src/anomalyState.ts
|
|
10799
|
-
import { existsSync as existsSync11, readFileSync as
|
|
10800
|
-
import { join as
|
|
10920
|
+
import { existsSync as existsSync11, readFileSync as readFileSync16, renameSync as renameSync6, writeFileSync as writeFileSync12 } from "fs";
|
|
10921
|
+
import { join as join20 } from "path";
|
|
10801
10922
|
var KEY_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
10802
10923
|
var EMPTY = { since: {}, lastFiredAt: {}, fires: {}, stopQuiet: null };
|
|
10803
10924
|
function anomalyStatePath(home) {
|
|
10804
|
-
return
|
|
10925
|
+
return join20(home, "anomaly-state.json");
|
|
10805
10926
|
}
|
|
10806
10927
|
function loadAnomalyState(home, now = Date.now(), io = {}) {
|
|
10807
10928
|
const exists = io.exists ?? existsSync11;
|
|
10808
|
-
const readFile2 = io.readFile ?? ((p) =>
|
|
10929
|
+
const readFile2 = io.readFile ?? ((p) => readFileSync16(p, "utf8"));
|
|
10809
10930
|
const path = anomalyStatePath(home);
|
|
10810
10931
|
if (!exists(path)) return { ...EMPTY };
|
|
10811
10932
|
try {
|
|
@@ -10831,7 +10952,7 @@ function loadAnomalyState(home, now = Date.now(), io = {}) {
|
|
|
10831
10952
|
function saveAnomalyState(home, state, io = {}) {
|
|
10832
10953
|
const path = anomalyStatePath(home);
|
|
10833
10954
|
const writeFile2 = io.writeFile ?? ((p, data) => {
|
|
10834
|
-
|
|
10955
|
+
writeFileSync12(`${p}.tmp`, data);
|
|
10835
10956
|
renameSync6(`${p}.tmp`, p);
|
|
10836
10957
|
});
|
|
10837
10958
|
writeFile2(path, JSON.stringify(state, null, 2));
|
|
@@ -11474,10 +11595,10 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
|
|
|
11474
11595
|
}
|
|
11475
11596
|
return parts.join("\n");
|
|
11476
11597
|
}
|
|
11477
|
-
var HERO_CHARTER_PATH =
|
|
11598
|
+
var HERO_CHARTER_PATH = join21(dirname5(THIS_FILE), "..", "docs", "HERO.md");
|
|
11478
11599
|
function buildHeroPrompt(task, run, taskContext, attachmentsBlock) {
|
|
11479
11600
|
const name = run.name || `#${run.id}`;
|
|
11480
|
-
const charter =
|
|
11601
|
+
const charter = readFileSync17(HERO_CHARTER_PATH, "utf8");
|
|
11481
11602
|
const head = task ? `\u042D\u041F\u0418\u041A ${run.task_slug}: ${task.title}
|
|
11482
11603
|
|
|
11483
11604
|
${task.description}` : `\u042D\u041F\u0418\u041A ${run.task_slug} (\u0442\u0435\u043B\u043E \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u0439 \u043F\u043E slug \u0438 \u0437\u0434\u0440\u0430\u0432\u043E\u043C\u0443 \u0441\u043C\u044B\u0441\u043B\u0443).`;
|
|
@@ -11813,9 +11934,9 @@ function repoHasCi(deps, run) {
|
|
|
11813
11934
|
function repoHasCiWorkflows(worktreePath, repo) {
|
|
11814
11935
|
for (const base3 of [worktreePath, repo]) {
|
|
11815
11936
|
if (!base3) continue;
|
|
11816
|
-
const dir =
|
|
11937
|
+
const dir = join21(base3, ".github", "workflows");
|
|
11817
11938
|
try {
|
|
11818
|
-
if (
|
|
11939
|
+
if (readdirSync10(dir).some((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) return true;
|
|
11819
11940
|
} catch {
|
|
11820
11941
|
}
|
|
11821
11942
|
}
|
|
@@ -13104,7 +13225,7 @@ async function resolveConflict(deps, reviewRun) {
|
|
|
13104
13225
|
workspace_id: reviewRun.workspace_id
|
|
13105
13226
|
// та же задача, тот же воркспейс (TASWM-288)
|
|
13106
13227
|
});
|
|
13107
|
-
const wt =
|
|
13228
|
+
const wt = join21(deps.worktreesDir, basename4(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
|
|
13108
13229
|
try {
|
|
13109
13230
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
13110
13231
|
await deps.git.setWorktreeIdentity(wt, resolver.name);
|
|
@@ -13156,7 +13277,7 @@ async function spawnReviewer(deps, reviewRun, files) {
|
|
|
13156
13277
|
// (своей у ревьюера нет, TASWM-204), и коммент с вердиктом должен уехать именно туда.
|
|
13157
13278
|
workspace_id: reviewRun.workspace_id
|
|
13158
13279
|
});
|
|
13159
|
-
const wt =
|
|
13280
|
+
const wt = join21(deps.worktreesDir, basename4(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
|
|
13160
13281
|
try {
|
|
13161
13282
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
13162
13283
|
await deps.git.setWorktreeIdentity(wt, reviewer.name);
|
|
@@ -13251,20 +13372,51 @@ async function syncCrashedOnStartup(deps) {
|
|
|
13251
13372
|
}
|
|
13252
13373
|
return { orphaned, merged, requeued };
|
|
13253
13374
|
}
|
|
13254
|
-
async function makeStore(repo, worktreesDir) {
|
|
13375
|
+
async function makeStore(repo, worktreesDir, repoFlag) {
|
|
13255
13376
|
const host = loadHostIdentity({
|
|
13256
13377
|
home: SWARM_HOME,
|
|
13257
13378
|
envHostId: process.env.SWARM_HOST_ID,
|
|
13258
13379
|
envHostName: process.env.SWARM_HOST_NAME
|
|
13259
13380
|
});
|
|
13260
13381
|
console.log(`[overmind] \u043C\u0430\u0448\u0438\u043D\u0430: ${host.hostName} (${host.hostId})`);
|
|
13382
|
+
const picked = pickRepoPaths({
|
|
13383
|
+
flag: effectiveRepoPaths(repoFlag ?? "", []),
|
|
13384
|
+
env: effectiveRepoPaths("", SWARM_REPO_PATHS),
|
|
13385
|
+
remembered: effectiveRepoPaths("", loadRememberedRepos(SWARM_HOME))
|
|
13386
|
+
});
|
|
13387
|
+
const repoPaths = picked.paths;
|
|
13388
|
+
if (picked.source === "memory") {
|
|
13389
|
+
console.log(`[overmind] \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438 \u0432\u0437\u044F\u0442\u044B \u0438\u0437 \u043F\u0430\u043C\u044F\u0442\u0438 \u043F\u0440\u043E\u0448\u043B\u043E\u0433\u043E \u0437\u0430\u043F\u0443\u0441\u043A\u0430: ${repoPaths.join(", ")} (\u0441\u043C\u0435\u043D\u0438\u0442\u044C: swarm start --repo <\u043F\u0443\u0442\u044C>)`);
|
|
13390
|
+
}
|
|
13391
|
+
if (picked.source === "flag" || picked.source === "env") {
|
|
13392
|
+
try {
|
|
13393
|
+
rememberRepos(SWARM_HOME, repoPaths);
|
|
13394
|
+
} catch (e) {
|
|
13395
|
+
console.warn(`[overmind] \u043D\u0435 \u0441\u043C\u043E\u0433 \u0437\u0430\u043F\u043E\u043C\u043D\u0438\u0442\u044C \u043F\u0443\u0442\u044C \u043A \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044E (${e instanceof Error ? e.message : e}) \u2014 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0437\u0430\u043F\u0443\u0441\u043A \u0441\u043D\u043E\u0432\u0430 \u043F\u043E\u043F\u0440\u043E\u0441\u0438\u0442 --repo`);
|
|
13396
|
+
}
|
|
13397
|
+
}
|
|
13261
13398
|
if (SWARM_BACKEND !== "client") return { store: new SwarmDb(DB_PATH, repo), reconciled: true, host };
|
|
13262
13399
|
const call = resilientSwarmMcp({
|
|
13263
13400
|
onDown: (ms) => console.warn(`[overmind] \u043F\u0440\u043E\u0434-MCP \u043C\u043E\u043B\u0447\u0438\u0442 ${Math.round(ms / 1e3)}\u0441 \u2014 \u0440\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0432\u0441\u043B\u0435\u043F\u0443\u044E, \u0441\u0434\u0430\u0447\u0438 \u0438 \u0432\u0435\u0440\u0434\u0438\u043A\u0442\u044B \u043D\u0435 \u0434\u043E\u0435\u0437\u0436\u0430\u044E\u0442.`),
|
|
13264
13401
|
onUp: () => console.warn("[overmind] \u043F\u0440\u043E\u0434-MCP \u0441\u043D\u043E\u0432\u0430 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438.")
|
|
13265
13402
|
});
|
|
13266
|
-
const
|
|
13267
|
-
const
|
|
13403
|
+
const explicitRegistry = await buildRepoRegistry(repoPaths);
|
|
13404
|
+
const byName = new Map(explicitRegistry.map((e) => [e.repoFullName, e.path]));
|
|
13405
|
+
const scanRootsList = defaultScanRoots(homedir3(), repoPaths);
|
|
13406
|
+
const scanStartedAt = Date.now();
|
|
13407
|
+
const scanned = scanRoots(scanRootsList, { maxDepth: SWARM_SCAN_DEPTH });
|
|
13408
|
+
let addedByScan = 0;
|
|
13409
|
+
for (const [full, path] of scanned) {
|
|
13410
|
+
if (byName.has(full)) continue;
|
|
13411
|
+
byName.set(full, path);
|
|
13412
|
+
addedByScan++;
|
|
13413
|
+
}
|
|
13414
|
+
if (addedByScan > 0) {
|
|
13415
|
+
console.log(
|
|
13416
|
+
`[overmind] \u043D\u0430\u0448\u0451\u043B \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435 ${addedByScan} \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439(\u0435\u0432) \u0441\u043A\u0430\u043D\u043E\u043C \u0437\u0430 ${Date.now() - scanStartedAt}\u043C\u0441: ${[...scanned.keys()].slice(0, 8).join(", ")}${scanned.size > 8 ? ` \u0438 \u0435\u0449\u0451 ${scanned.size - 8}` : ""}`
|
|
13417
|
+
);
|
|
13418
|
+
}
|
|
13419
|
+
const repoRegistry = [...byName].map(([repoFullName, path]) => ({ repoFullName, path }));
|
|
13268
13420
|
if (repoPaths.length > 0 && repoRegistry.length < repoPaths.length) {
|
|
13269
13421
|
console.warn(
|
|
13270
13422
|
`[overmind] \u0440\u0435\u043F\u043E-\u0440\u0435\u0435\u0441\u0442\u0440: ${repoRegistry.length}/${repoPaths.length} \u043F\u0443\u0442\u0435\u0439 \u0440\u0435\u0437\u043E\u043B\u0432\u043D\u0443\u043B\u0438\u0441\u044C \u0432 repoFullName (\u043E\u0441\u0442\u0430\u043B\u044C\u043D\u044B\u0435 \u2014 \u043D\u0435 git-\u0440\u0435\u043F\u043E \u0438\u043B\u0438 \u0431\u0435\u0437 remote origin)`
|
|
@@ -13272,7 +13424,7 @@ async function makeStore(repo, worktreesDir) {
|
|
|
13272
13424
|
}
|
|
13273
13425
|
if (repoRegistry.length === 0) {
|
|
13274
13426
|
console.warn(
|
|
13275
|
-
|
|
13427
|
+
`[overmind] \u041D\u0415 \u041D\u0410\u0428\u0401\u041B \u041D\u0418 \u041E\u0414\u041D\u041E\u0413\u041E \u0420\u0415\u041F\u041E\u0417\u0418\u0422\u041E\u0420\u0418\u042F \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u2014 \u0441\u0432\u043E\u0431\u043E\u0434\u043D\u044B\u0435 \u043F\u0440\u043E\u0433\u043E\u043D\u044B \u0431\u0440\u0430\u0442\u044C\u0441\u044F \u043D\u0435 \u0431\u0443\u0434\u0443\u0442: \u0438\u0445 \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u0438\u043C\u0435\u043D\u0435\u043C owner/repo, \u0430 \u0441\u043E\u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0435\u0433\u043E \u043D\u0435 \u0441 \u0447\u0435\u043C. \u0418\u0441\u043A\u0430\u043B \u0432: ${scanRootsList.slice(0, 6).join(", ")}. \u0415\u0441\u043B\u0438 \u043A\u043B\u043E\u043D\u044B \u043B\u0435\u0436\u0430\u0442 \u0432 \u0434\u0440\u0443\u0433\u043E\u043C \u043C\u0435\u0441\u0442\u0435 \u2014 \u0443\u043A\u0430\u0436\u0438: swarm start --repo <\u043F\u0443\u0442\u044C \u043A \u0440\u0435\u043F\u043E> (\u0437\u0430\u043F\u043E\u043C\u043D\u0438\u0442\u0441\u044F).`
|
|
13276
13428
|
);
|
|
13277
13429
|
}
|
|
13278
13430
|
const client = new SwarmClient({ call, outboxPath: SWARM_OUTBOX, defaultRepo: repo, repoRegistry, host });
|
|
@@ -13309,7 +13461,7 @@ async function migrateWorktreesAtStartup() {
|
|
|
13309
13461
|
exists: (d) => existsSync12(d),
|
|
13310
13462
|
isNonEmptyDir: (d) => {
|
|
13311
13463
|
try {
|
|
13312
|
-
return
|
|
13464
|
+
return readdirSync10(d).length > 0;
|
|
13313
13465
|
} catch {
|
|
13314
13466
|
return false;
|
|
13315
13467
|
}
|
|
@@ -13340,7 +13492,11 @@ async function runDaemon(opts) {
|
|
|
13340
13492
|
installCrashGuards();
|
|
13341
13493
|
const daemonStart = await captureDaemonStart(THIS_FILE);
|
|
13342
13494
|
const worktreesDir = await migrateWorktreesAtStartup();
|
|
13343
|
-
|
|
13495
|
+
if (!opts.repo) {
|
|
13496
|
+
const remembered = effectiveRepoPaths("", loadRememberedRepos(SWARM_HOME))[0];
|
|
13497
|
+
if (remembered) opts.repo = remembered;
|
|
13498
|
+
}
|
|
13499
|
+
const { store: db, reconciled, host } = await makeStore(opts.repo, worktreesDir, opts.repoFlag);
|
|
13344
13500
|
await preflightAuth();
|
|
13345
13501
|
const tl = await connectTaskless();
|
|
13346
13502
|
const tlFor = (workspaceId) => tl ? tl.forWorkspace(workspaceId) : null;
|
package/package.json
CHANGED