@taskless-app/swarm 0.4.4 → 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 +7 -7
- package/dist/overmind.js +312 -138
- 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);
|
|
@@ -3480,7 +3481,7 @@ function pidsWithCwdUnder(dir, opts = {}) {
|
|
|
3480
3481
|
if ((opts.platform ?? process.platform) === "win32") return winProcsUnder(dir, opts.procs ?? listProcs);
|
|
3481
3482
|
let out;
|
|
3482
3483
|
try {
|
|
3483
|
-
out = execQuiet("lsof", ["-
|
|
3484
|
+
out = execQuiet("lsof", ["-a", "-d", "cwd", "-F", "pn"], { maxBuffer: PROC_SNAPSHOT_MAX_BUFFER });
|
|
3484
3485
|
} catch (e) {
|
|
3485
3486
|
logTransientSpawnError("lsof", e);
|
|
3486
3487
|
return [];
|
|
@@ -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);
|
|
@@ -2278,7 +2280,7 @@ function pidsWithCwdUnder(dir, opts = {}) {
|
|
|
2278
2280
|
if ((opts.platform ?? process.platform) === "win32") return winProcsUnder(dir, opts.procs ?? listProcs);
|
|
2279
2281
|
let out;
|
|
2280
2282
|
try {
|
|
2281
|
-
out = execQuiet("lsof", ["-
|
|
2283
|
+
out = execQuiet("lsof", ["-a", "-d", "cwd", "-F", "pn"], { maxBuffer: PROC_SNAPSHOT_MAX_BUFFER });
|
|
2282
2284
|
} catch (e) {
|
|
2283
2285
|
logTransientSpawnError("lsof", e);
|
|
2284
2286
|
return [];
|
|
@@ -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
|
|
|
@@ -6035,6 +6038,18 @@ init_heartbeat();
|
|
|
6035
6038
|
// src/repoRegistry.ts
|
|
6036
6039
|
init_config();
|
|
6037
6040
|
import { $ } from "execa";
|
|
6041
|
+
function effectiveRepoPaths(flagRepo, envPaths) {
|
|
6042
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6043
|
+
const out = [];
|
|
6044
|
+
for (const raw of [flagRepo ?? "", ...envPaths]) {
|
|
6045
|
+
if (!raw.trim()) continue;
|
|
6046
|
+
const key = normalizeRepo(raw);
|
|
6047
|
+
if (!key || seen.has(key)) continue;
|
|
6048
|
+
seen.add(key);
|
|
6049
|
+
out.push(key);
|
|
6050
|
+
}
|
|
6051
|
+
return out;
|
|
6052
|
+
}
|
|
6038
6053
|
function parseRepoFullName(remoteUrl) {
|
|
6039
6054
|
const m = remoteUrl.trim().match(/^(?:https?:\/\/(?:[^@/]+@)?github\.com\/|git@github\.com:)([^/]+)\/([^/]+?)(\.git)?\/?$/i);
|
|
6040
6055
|
if (!m) return null;
|
|
@@ -6057,19 +6072,137 @@ async function buildRepoRegistry(paths) {
|
|
|
6057
6072
|
return resolved.filter((e) => e !== null);
|
|
6058
6073
|
}
|
|
6059
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
|
+
|
|
6060
6193
|
// src/hostIdentity.ts
|
|
6061
6194
|
import { hostname } from "os";
|
|
6062
|
-
import { existsSync as existsSync5, readFileSync as
|
|
6063
|
-
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";
|
|
6064
6197
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6065
6198
|
function hostIdentityPath(home) {
|
|
6066
|
-
return
|
|
6199
|
+
return join6(home, "host.json");
|
|
6067
6200
|
}
|
|
6068
6201
|
function loadHostIdentity(deps) {
|
|
6069
6202
|
const exists = deps.exists ?? existsSync5;
|
|
6070
|
-
const readFile2 = deps.readFile ?? ((p) =>
|
|
6203
|
+
const readFile2 = deps.readFile ?? ((p) => readFileSync8(p, "utf8"));
|
|
6071
6204
|
const writeFile2 = deps.writeFile ?? ((p, data) => {
|
|
6072
|
-
|
|
6205
|
+
writeFileSync6(`${p}.tmp`, data);
|
|
6073
6206
|
renameSync4(`${p}.tmp`, p);
|
|
6074
6207
|
});
|
|
6075
6208
|
const host = deps.hostname ?? hostname;
|
|
@@ -6252,14 +6385,14 @@ function buildReliefCostCandidates(cost, t, sustainMs, build) {
|
|
|
6252
6385
|
|
|
6253
6386
|
// src/launcher.ts
|
|
6254
6387
|
import { spawn } from "child_process";
|
|
6255
|
-
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";
|
|
6256
6389
|
import { createRequire as createRequire2 } from "module";
|
|
6257
6390
|
import { homedir as homedir2 } from "os";
|
|
6258
|
-
import { dirname as dirname2, join as
|
|
6391
|
+
import { dirname as dirname2, join as join8 } from "path";
|
|
6259
6392
|
import { fileURLToPath } from "url";
|
|
6260
6393
|
|
|
6261
6394
|
// src/claudeBin.ts
|
|
6262
|
-
import { existsSync as existsSync6, readdirSync as
|
|
6395
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3 } from "fs";
|
|
6263
6396
|
import { win32 as winPath } from "path";
|
|
6264
6397
|
var CLAUDE_FALLBACK = "claude";
|
|
6265
6398
|
function compareVersionDirs(a, b) {
|
|
@@ -6274,7 +6407,7 @@ function compareVersionDirs(a, b) {
|
|
|
6274
6407
|
}
|
|
6275
6408
|
function realListDir(dir) {
|
|
6276
6409
|
try {
|
|
6277
|
-
return
|
|
6410
|
+
return readdirSync3(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
6278
6411
|
} catch {
|
|
6279
6412
|
return [];
|
|
6280
6413
|
}
|
|
@@ -6331,8 +6464,8 @@ function resolveClaudeBin(opts = {}) {
|
|
|
6331
6464
|
}
|
|
6332
6465
|
|
|
6333
6466
|
// src/droneHome.ts
|
|
6334
|
-
import { lstatSync, mkdirSync as mkdirSync3, readdirSync as
|
|
6335
|
-
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";
|
|
6336
6469
|
var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
|
|
6337
6470
|
function droneHomeDeny(env = process.env) {
|
|
6338
6471
|
const extra = (env.SWARM_DRONE_HOME_DENY || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -6351,28 +6484,28 @@ function buildDroneHome(opts) {
|
|
|
6351
6484
|
const deny = opts.deny ?? droneHomeDeny();
|
|
6352
6485
|
const platform = opts.platform ?? process.platform;
|
|
6353
6486
|
mkdirSync3(droneHome, { recursive: true, mode: 448 });
|
|
6354
|
-
const wanted = new Set(mirroredEntries(
|
|
6487
|
+
const wanted = new Set(mirroredEntries(readdirSync4(realHome), deny, platform));
|
|
6355
6488
|
const rel = relative(realHome, droneHome);
|
|
6356
6489
|
if (rel && !rel.startsWith("..") && !rel.includes(sep)) wanted.delete(rel);
|
|
6357
|
-
for (const existing of
|
|
6490
|
+
for (const existing of readdirSync4(droneHome)) {
|
|
6358
6491
|
if (wanted.has(existing)) continue;
|
|
6359
|
-
const p =
|
|
6492
|
+
const p = join7(droneHome, existing);
|
|
6360
6493
|
try {
|
|
6361
6494
|
if (lstatSync(p).isSymbolicLink()) rmSync(p);
|
|
6362
6495
|
} catch {
|
|
6363
6496
|
}
|
|
6364
6497
|
}
|
|
6365
6498
|
for (const name of wanted) {
|
|
6366
|
-
const link =
|
|
6499
|
+
const link = join7(droneHome, name);
|
|
6367
6500
|
try {
|
|
6368
6501
|
lstatSync(link);
|
|
6369
6502
|
continue;
|
|
6370
6503
|
} catch {
|
|
6371
6504
|
}
|
|
6372
|
-
const target =
|
|
6505
|
+
const target = join7(realHome, name);
|
|
6373
6506
|
let isDir;
|
|
6374
6507
|
try {
|
|
6375
|
-
isDir =
|
|
6508
|
+
isDir = statSync3(target).isDirectory();
|
|
6376
6509
|
} catch {
|
|
6377
6510
|
continue;
|
|
6378
6511
|
}
|
|
@@ -6390,7 +6523,7 @@ var REAL_PATH_ENV_PINS = [
|
|
|
6390
6523
|
{ env: "YARN_GLOBAL_FOLDER", segments: [".yarn", "berry"] }
|
|
6391
6524
|
];
|
|
6392
6525
|
function realPathEnvPins(realHome) {
|
|
6393
|
-
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)]));
|
|
6394
6527
|
}
|
|
6395
6528
|
function droneDenyRules(realHome, swarmHome) {
|
|
6396
6529
|
const paths = [
|
|
@@ -6482,7 +6615,7 @@ function droneEnv(worktree, base3 = process.env, isolation, droneCap = null) {
|
|
|
6482
6615
|
const env = { ...base3 };
|
|
6483
6616
|
const isolate = base3.SWARM_TURBO_ISOLATE !== "0";
|
|
6484
6617
|
if (isolate && !base3.TURBO_CACHE_DIR) {
|
|
6485
|
-
env.TURBO_CACHE_DIR =
|
|
6618
|
+
env.TURBO_CACHE_DIR = join8(worktree, ".turbo", "cache");
|
|
6486
6619
|
}
|
|
6487
6620
|
Object.assign(env, droneTestEnv(droneCap, base3));
|
|
6488
6621
|
for (const key of DAEMON_STATE_ENV) delete env[key];
|
|
@@ -6510,29 +6643,29 @@ var DAEMON_STATE_ENV = [
|
|
|
6510
6643
|
"TASKLESS_TOKEN"
|
|
6511
6644
|
];
|
|
6512
6645
|
function bundledRoleDir(kind) {
|
|
6513
|
-
return
|
|
6646
|
+
return join8(dirname2(fileURLToPath(import.meta.url)), "..", "roles", kind);
|
|
6514
6647
|
}
|
|
6515
6648
|
function roleDir(worktree, kind) {
|
|
6516
|
-
return
|
|
6649
|
+
return join8(worktree, ".claude", "roles", kind);
|
|
6517
6650
|
}
|
|
6518
6651
|
function roleDirs(worktree, kind) {
|
|
6519
6652
|
return [bundledRoleDir(kind), roleDir(worktree, kind)].filter((dir) => existsSync7(dir));
|
|
6520
6653
|
}
|
|
6521
6654
|
function transcriptDir(worktree, projectsDir) {
|
|
6522
|
-
return
|
|
6655
|
+
return join8(projectsDir, worktree.replace(/[/.]/g, "-"));
|
|
6523
6656
|
}
|
|
6524
|
-
function sessionTranscriptExists(worktree, projectsDir =
|
|
6657
|
+
function sessionTranscriptExists(worktree, projectsDir = join8(homedir2(), ".claude", "projects")) {
|
|
6525
6658
|
try {
|
|
6526
|
-
return
|
|
6659
|
+
return readdirSync5(transcriptDir(worktree, projectsDir)).some((f) => f.endsWith(".jsonl"));
|
|
6527
6660
|
} catch {
|
|
6528
6661
|
return false;
|
|
6529
6662
|
}
|
|
6530
6663
|
}
|
|
6531
|
-
function transcriptMtimeMs(worktree, projectsDir =
|
|
6664
|
+
function transcriptMtimeMs(worktree, projectsDir = join8(homedir2(), ".claude", "projects")) {
|
|
6532
6665
|
const dir = transcriptDir(worktree, projectsDir);
|
|
6533
6666
|
let files;
|
|
6534
6667
|
try {
|
|
6535
|
-
files =
|
|
6668
|
+
files = readdirSync5(dir);
|
|
6536
6669
|
} catch {
|
|
6537
6670
|
return null;
|
|
6538
6671
|
}
|
|
@@ -6540,7 +6673,7 @@ function transcriptMtimeMs(worktree, projectsDir = join6(homedir2(), ".claude",
|
|
|
6540
6673
|
for (const f of files) {
|
|
6541
6674
|
if (!f.endsWith(".jsonl")) continue;
|
|
6542
6675
|
try {
|
|
6543
|
-
const mtime2 =
|
|
6676
|
+
const mtime2 = statSync4(join8(dir, f)).mtimeMs;
|
|
6544
6677
|
if (latest === null || mtime2 > latest) latest = mtime2;
|
|
6545
6678
|
} catch {
|
|
6546
6679
|
}
|
|
@@ -6548,11 +6681,11 @@ function transcriptMtimeMs(worktree, projectsDir = join6(homedir2(), ".claude",
|
|
|
6548
6681
|
return latest;
|
|
6549
6682
|
}
|
|
6550
6683
|
function nextRunLogPath(runLogsDir, runId) {
|
|
6551
|
-
const dir =
|
|
6684
|
+
const dir = join8(runLogsDir, String(runId));
|
|
6552
6685
|
mkdirSync4(dir, { recursive: true });
|
|
6553
6686
|
let files = [];
|
|
6554
6687
|
try {
|
|
6555
|
-
files =
|
|
6688
|
+
files = readdirSync5(dir);
|
|
6556
6689
|
} catch {
|
|
6557
6690
|
}
|
|
6558
6691
|
let next = 0;
|
|
@@ -6560,7 +6693,7 @@ function nextRunLogPath(runLogsDir, runId) {
|
|
|
6560
6693
|
const m = /^(\d+)\.json/.exec(f);
|
|
6561
6694
|
if (m) next = Math.max(next, Number(m[1]) + 1);
|
|
6562
6695
|
}
|
|
6563
|
-
return
|
|
6696
|
+
return join8(dir, `${next}.json`);
|
|
6564
6697
|
}
|
|
6565
6698
|
function openNextRunLog(runLogsDir, runId) {
|
|
6566
6699
|
for (let attempt = 0; attempt < 50; attempt++) {
|
|
@@ -6634,16 +6767,16 @@ function makeLauncher(cfg) {
|
|
|
6634
6767
|
}
|
|
6635
6768
|
}
|
|
6636
6769
|
};
|
|
6637
|
-
const cfgPath =
|
|
6638
|
-
|
|
6770
|
+
const cfgPath = join8(worktree, ".swarm-mcp.json");
|
|
6771
|
+
writeFileSync7(cfgPath, JSON.stringify(mcpConfig, null, 2));
|
|
6639
6772
|
const model = run.model ?? process.env.SWARM_DRONE_MODEL ?? void 0;
|
|
6640
6773
|
const pluginDirs = roleDirs(worktree, run.kind);
|
|
6641
6774
|
const realHome = cfg.droneIsolation?.realHome ?? homedir2();
|
|
6642
6775
|
const home = cfg.droneIsolation ? buildDroneHome({ realHome, droneHome: cfg.droneIsolation.home }) : void 0;
|
|
6643
6776
|
const settings = cfg.droneIsolation ? droneSettingsJson(realHome, cfg.droneIsolation.swarmHome) : void 0;
|
|
6644
|
-
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync7(
|
|
6777
|
+
if (cfg.droneIsolation && process.env.TASKLESS_TOKEN && !existsSync7(join8(cfg.droneIsolation.swarmHome, "token"))) {
|
|
6645
6778
|
console.error(
|
|
6646
|
-
`[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.`
|
|
6647
6780
|
);
|
|
6648
6781
|
}
|
|
6649
6782
|
let stdio = "ignore";
|
|
@@ -6677,8 +6810,8 @@ function makeLauncher(cfg) {
|
|
|
6677
6810
|
init_procCleanup();
|
|
6678
6811
|
|
|
6679
6812
|
// src/cost.ts
|
|
6680
|
-
import { readFileSync as
|
|
6681
|
-
import { join as
|
|
6813
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync6, renameSync as renameSync5 } from "fs";
|
|
6814
|
+
import { join as join9 } from "path";
|
|
6682
6815
|
|
|
6683
6816
|
// src/pricing.ts
|
|
6684
6817
|
var MODEL_PRICING = {
|
|
@@ -6729,23 +6862,23 @@ function launchCostUsd(result, model) {
|
|
|
6729
6862
|
var UNPROCESSED_LOG_RE = /^\d+\.json$/;
|
|
6730
6863
|
function unprocessedLogFiles(dir) {
|
|
6731
6864
|
try {
|
|
6732
|
-
return
|
|
6865
|
+
return readdirSync6(dir).filter((f) => UNPROCESSED_LOG_RE.test(f));
|
|
6733
6866
|
} catch {
|
|
6734
6867
|
return [];
|
|
6735
6868
|
}
|
|
6736
6869
|
}
|
|
6737
6870
|
function accountRunCost(deps, run) {
|
|
6738
|
-
const dir =
|
|
6871
|
+
const dir = join9(deps.runLogsDir, String(run.id));
|
|
6739
6872
|
const files = unprocessedLogFiles(dir);
|
|
6740
6873
|
if (files.length === 0) return;
|
|
6741
6874
|
const model = run.model ?? process.env.SWARM_DRONE_MODEL ?? null;
|
|
6742
6875
|
let added = 0;
|
|
6743
6876
|
const processed = [];
|
|
6744
6877
|
for (const f of files) {
|
|
6745
|
-
const full =
|
|
6878
|
+
const full = join9(dir, f);
|
|
6746
6879
|
let raw = null;
|
|
6747
6880
|
try {
|
|
6748
|
-
raw =
|
|
6881
|
+
raw = readFileSync9(full, "utf8");
|
|
6749
6882
|
} catch {
|
|
6750
6883
|
continue;
|
|
6751
6884
|
}
|
|
@@ -6772,14 +6905,14 @@ function makeCostAccountant(deps) {
|
|
|
6772
6905
|
}
|
|
6773
6906
|
|
|
6774
6907
|
// src/runOutcome.ts
|
|
6775
|
-
import { readdirSync as
|
|
6776
|
-
import { join as
|
|
6908
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync10 } from "fs";
|
|
6909
|
+
import { join as join10 } from "path";
|
|
6777
6910
|
var RESULT_FILE_RE = /^(\d+)\.json(\.done)?$/;
|
|
6778
6911
|
function classifyExit(runLogsDir, runId) {
|
|
6779
|
-
const dir =
|
|
6912
|
+
const dir = join10(runLogsDir, String(runId));
|
|
6780
6913
|
let files;
|
|
6781
6914
|
try {
|
|
6782
|
-
files =
|
|
6915
|
+
files = readdirSync7(dir);
|
|
6783
6916
|
} catch {
|
|
6784
6917
|
return "unknown";
|
|
6785
6918
|
}
|
|
@@ -6793,7 +6926,7 @@ function classifyExit(runLogsDir, runId) {
|
|
|
6793
6926
|
if (!latest) return "unknown";
|
|
6794
6927
|
let raw;
|
|
6795
6928
|
try {
|
|
6796
|
-
raw =
|
|
6929
|
+
raw = readFileSync10(join10(dir, latest.name), "utf8");
|
|
6797
6930
|
} catch {
|
|
6798
6931
|
return "unknown";
|
|
6799
6932
|
}
|
|
@@ -6807,8 +6940,8 @@ function makeExitClassifier(runLogsDir) {
|
|
|
6807
6940
|
init_git();
|
|
6808
6941
|
|
|
6809
6942
|
// src/deps.ts
|
|
6810
|
-
import { existsSync as existsSync9, readdirSync as
|
|
6811
|
-
import { join as
|
|
6943
|
+
import { existsSync as existsSync9, readdirSync as readdirSync9, readFileSync as readFileSync11 } from "fs";
|
|
6944
|
+
import { join as join12 } from "path";
|
|
6812
6945
|
import { execa } from "execa";
|
|
6813
6946
|
var PREPARE_TIMEOUT_MS = 15 * 6e4;
|
|
6814
6947
|
var MAX_PLAN_COMMANDS = 12;
|
|
@@ -6824,21 +6957,21 @@ async function runCommand(cmd, worktree) {
|
|
|
6824
6957
|
}
|
|
6825
6958
|
function safeReadText(p) {
|
|
6826
6959
|
try {
|
|
6827
|
-
return
|
|
6960
|
+
return readFileSync11(p, "utf8");
|
|
6828
6961
|
} catch {
|
|
6829
6962
|
return null;
|
|
6830
6963
|
}
|
|
6831
6964
|
}
|
|
6832
6965
|
function safeListDirs(p) {
|
|
6833
6966
|
try {
|
|
6834
|
-
return
|
|
6967
|
+
return readdirSync9(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
6835
6968
|
} catch {
|
|
6836
6969
|
return [];
|
|
6837
6970
|
}
|
|
6838
6971
|
}
|
|
6839
6972
|
function safeListFiles(p) {
|
|
6840
6973
|
try {
|
|
6841
|
-
return
|
|
6974
|
+
return readdirSync9(p, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name);
|
|
6842
6975
|
} catch {
|
|
6843
6976
|
return [];
|
|
6844
6977
|
}
|
|
@@ -6866,19 +6999,19 @@ function overridePlan(override) {
|
|
|
6866
6999
|
function detectPackageManager(worktree, io = {}) {
|
|
6867
7000
|
const exists = io.exists ?? existsSync9;
|
|
6868
7001
|
const readText = io.readText ?? safeReadText;
|
|
6869
|
-
const hasPkgJson = exists(
|
|
7002
|
+
const hasPkgJson = exists(join12(worktree, "package.json"));
|
|
6870
7003
|
if (!hasPkgJson) {
|
|
6871
|
-
if (exists(
|
|
7004
|
+
if (exists(join12(worktree, "deno.json")) || exists(join12(worktree, "deno.jsonc")) || exists(join12(worktree, "deno.lock"))) return "deno";
|
|
6872
7005
|
return null;
|
|
6873
7006
|
}
|
|
6874
|
-
const pkg = readJson(readText,
|
|
7007
|
+
const pkg = readJson(readText, join12(worktree, "package.json"));
|
|
6875
7008
|
const declared = typeof pkg?.packageManager === "string" ? pkg.packageManager.split("@")[0] : void 0;
|
|
6876
7009
|
if (declared === "pnpm" || declared === "bun" || declared === "npm" || declared === "yarn") return declared;
|
|
6877
|
-
if (exists(
|
|
6878
|
-
if (exists(
|
|
6879
|
-
if (exists(
|
|
6880
|
-
if (exists(
|
|
6881
|
-
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";
|
|
6882
7015
|
return "npm";
|
|
6883
7016
|
}
|
|
6884
7017
|
function jsInstallCommand(pm) {
|
|
@@ -6939,7 +7072,7 @@ function detectPlan(worktree, io) {
|
|
|
6939
7072
|
// node_modules на месте — установку пропускаем (быстрый путь переиспользованного worktree).
|
|
6940
7073
|
// У deno каталога может не быть вовсе (кэш в DENO_DIR) — тогда `deno install` отработает
|
|
6941
7074
|
// заново, но он дешёвый: тянет из того же общего кэша.
|
|
6942
|
-
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists:
|
|
7075
|
+
{ label: `${pm} install`, run: jsInstallCommand(pm), skipIfExists: join12(worktree, "node_modules"), source: "detect" }
|
|
6943
7076
|
];
|
|
6944
7077
|
for (const target of workspaceBuildTargets(worktree, io)) {
|
|
6945
7078
|
commands.push({ label: `\u0441\u0431\u043E\u0440\u043A\u0430 ${target.name}`, run: jsRunScript(pm, "build"), cwd: target.dir, source: "detect" });
|
|
@@ -6947,12 +7080,12 @@ function detectPlan(worktree, io) {
|
|
|
6947
7080
|
return { source: "detect", ecosystem: `node/${pm}`, commands };
|
|
6948
7081
|
}
|
|
6949
7082
|
for (const marker of ECOSYSTEM_MARKERS) {
|
|
6950
|
-
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));
|
|
6951
7084
|
if (!hit) continue;
|
|
6952
7085
|
let ecosystem = marker.ecosystem;
|
|
6953
7086
|
let command = marker.command;
|
|
6954
7087
|
for (const refine of marker.refine ?? []) {
|
|
6955
|
-
if (!exists(
|
|
7088
|
+
if (!exists(join12(worktree, refine.file))) continue;
|
|
6956
7089
|
ecosystem = refine.ecosystem;
|
|
6957
7090
|
command = refine.command;
|
|
6958
7091
|
break;
|
|
@@ -7009,12 +7142,12 @@ var CI_INSTALL_PATTERNS = [
|
|
|
7009
7142
|
function ciWorkflowPlan(worktree, io) {
|
|
7010
7143
|
const readText = io.readText ?? safeReadText;
|
|
7011
7144
|
const listFiles = io.listFiles ?? safeListFiles;
|
|
7012
|
-
const dir =
|
|
7145
|
+
const dir = join12(worktree, ".github", "workflows");
|
|
7013
7146
|
const files = listFiles(dir).filter((n) => n.endsWith(".yml") || n.endsWith(".yaml"));
|
|
7014
7147
|
const seen = /* @__PURE__ */ new Set();
|
|
7015
7148
|
const commands = [];
|
|
7016
7149
|
for (const name of files.sort()) {
|
|
7017
|
-
const yaml = readText(
|
|
7150
|
+
const yaml = readText(join12(dir, name));
|
|
7018
7151
|
if (yaml === null) continue;
|
|
7019
7152
|
for (const raw of workflowRunLines(yaml)) {
|
|
7020
7153
|
const line = raw.replace(/\s+/g, " ").trim();
|
|
@@ -7029,7 +7162,7 @@ function ciWorkflowPlan(worktree, io) {
|
|
|
7029
7162
|
return commands.length ? { source: "ci-workflow", ecosystem: "ci-\u043A\u043E\u043D\u0444\u0438\u0433", commands } : null;
|
|
7030
7163
|
}
|
|
7031
7164
|
function workspaceGlobs(worktree, readText) {
|
|
7032
|
-
const yaml = readText(
|
|
7165
|
+
const yaml = readText(join12(worktree, "pnpm-workspace.yaml"));
|
|
7033
7166
|
if (yaml !== null) {
|
|
7034
7167
|
const globs = [];
|
|
7035
7168
|
let inPackages = false;
|
|
@@ -7049,7 +7182,7 @@ function workspaceGlobs(worktree, readText) {
|
|
|
7049
7182
|
}
|
|
7050
7183
|
if (globs.length) return globs;
|
|
7051
7184
|
}
|
|
7052
|
-
const pkg = readJson(readText,
|
|
7185
|
+
const pkg = readJson(readText, join12(worktree, "package.json"));
|
|
7053
7186
|
const ws = pkg?.workspaces;
|
|
7054
7187
|
const list = Array.isArray(ws) ? ws : Array.isArray(ws?.packages) ? ws.packages : [];
|
|
7055
7188
|
return list.filter((g) => typeof g === "string");
|
|
@@ -7060,14 +7193,14 @@ function workspaceDirs(worktree, globs, io) {
|
|
|
7060
7193
|
if (glob.startsWith("!")) continue;
|
|
7061
7194
|
const star = glob.indexOf("*");
|
|
7062
7195
|
if (star === -1) {
|
|
7063
|
-
const dir =
|
|
7064
|
-
if (io.exists(
|
|
7196
|
+
const dir = join12(worktree, glob);
|
|
7197
|
+
if (io.exists(join12(dir, "package.json"))) dirs.add(dir);
|
|
7065
7198
|
continue;
|
|
7066
7199
|
}
|
|
7067
|
-
const base3 =
|
|
7200
|
+
const base3 = join12(worktree, glob.slice(0, star).replace(/\/$/, ""));
|
|
7068
7201
|
for (const name of io.listDirs(base3)) {
|
|
7069
|
-
const dir =
|
|
7070
|
-
if (io.exists(
|
|
7202
|
+
const dir = join12(base3, name);
|
|
7203
|
+
if (io.exists(join12(dir, "package.json"))) dirs.add(dir);
|
|
7071
7204
|
}
|
|
7072
7205
|
}
|
|
7073
7206
|
return [...dirs];
|
|
@@ -7095,11 +7228,11 @@ function workspaceBuildTargets(worktree, io = {}) {
|
|
|
7095
7228
|
if (!globs.length) return [];
|
|
7096
7229
|
const pkgs = [];
|
|
7097
7230
|
for (const dir of workspaceDirs(worktree, globs, { exists, listDirs })) {
|
|
7098
|
-
const pkg = readJson(readText,
|
|
7231
|
+
const pkg = readJson(readText, join12(dir, "package.json"));
|
|
7099
7232
|
if (!pkg || typeof pkg.name !== "string") continue;
|
|
7100
7233
|
const scripts = pkg.scripts ?? {};
|
|
7101
7234
|
const entries = entryPaths(pkg);
|
|
7102
|
-
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)));
|
|
7103
7236
|
const deps = Object.keys({ ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} });
|
|
7104
7237
|
pkgs.push({ name: pkg.name, dir, deps, needsBuild });
|
|
7105
7238
|
}
|
|
@@ -8673,7 +8806,7 @@ function makeExclusiveReader(opts) {
|
|
|
8673
8806
|
|
|
8674
8807
|
// src/envGate.ts
|
|
8675
8808
|
import { existsSync as existsSync10 } from "fs";
|
|
8676
|
-
import { join as
|
|
8809
|
+
import { join as join18, delimiter } from "path";
|
|
8677
8810
|
import { createConnection } from "net";
|
|
8678
8811
|
import { execa as execa2 } from "execa";
|
|
8679
8812
|
var PROBE_TIMEOUT_MS = 2500;
|
|
@@ -8725,7 +8858,7 @@ var COMPOSE_MARKERS = [
|
|
|
8725
8858
|
];
|
|
8726
8859
|
function detectRequirements(repo, io = {}) {
|
|
8727
8860
|
const exists = io.exists ?? existsSync10;
|
|
8728
|
-
const marker = COMPOSE_MARKERS.find((f) => exists(
|
|
8861
|
+
const marker = COMPOSE_MARKERS.find((f) => exists(join18(repo, f)));
|
|
8729
8862
|
return marker ? [{ kind: "docker", raw: `docker (\u0434\u0435\u0442\u0435\u043A\u0442: ${marker})` }] : [];
|
|
8730
8863
|
}
|
|
8731
8864
|
function resolveRequirements(repo, declared, io = {}) {
|
|
@@ -8747,7 +8880,7 @@ async function hasCommand(name, env) {
|
|
|
8747
8880
|
if (name.includes("/")) return existsSync10(name);
|
|
8748
8881
|
const dirs = (env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
8749
8882
|
const exts = process.platform === "win32" ? (env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";").filter(Boolean) : [""];
|
|
8750
|
-
return dirs.some((dir) => exts.some((ext) => existsSync10(
|
|
8883
|
+
return dirs.some((dir) => exts.some((ext) => existsSync10(join18(dir, name + ext))));
|
|
8751
8884
|
}
|
|
8752
8885
|
async function portOpen(host, port) {
|
|
8753
8886
|
return new Promise((resolve3) => {
|
|
@@ -8864,8 +8997,8 @@ import { basename as basename3 } from "path";
|
|
|
8864
8997
|
import { serve as serve2 } from "@hono/node-server";
|
|
8865
8998
|
|
|
8866
8999
|
// src/diff.ts
|
|
8867
|
-
import { statSync as
|
|
8868
|
-
import { join as
|
|
9000
|
+
import { statSync as statSync6 } from "fs";
|
|
9001
|
+
import { join as join19 } from "path";
|
|
8869
9002
|
import { $ as $4 } from "execa";
|
|
8870
9003
|
var MAX_FILES = 200;
|
|
8871
9004
|
var MAX_LINES_PER_FILE = 1500;
|
|
@@ -9008,7 +9141,7 @@ async function resolveBase(wt, baseBranch) {
|
|
|
9008
9141
|
}
|
|
9009
9142
|
function mtime(path) {
|
|
9010
9143
|
try {
|
|
9011
|
-
return
|
|
9144
|
+
return statSync6(path).mtimeMs;
|
|
9012
9145
|
} catch {
|
|
9013
9146
|
return 0;
|
|
9014
9147
|
}
|
|
@@ -9033,7 +9166,7 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
9033
9166
|
files.push(pf);
|
|
9034
9167
|
}
|
|
9035
9168
|
}
|
|
9036
|
-
files.sort((a, b) => mtime(
|
|
9169
|
+
files.sort((a, b) => mtime(join19(worktree, b.path)) - mtime(join19(worktree, a.path)));
|
|
9037
9170
|
const truncatedFiles = files.length > MAX_FILES;
|
|
9038
9171
|
if (truncatedFiles) files = files.slice(0, MAX_FILES);
|
|
9039
9172
|
const added = files.reduce((s, f) => s + f.added, 0);
|
|
@@ -9052,7 +9185,7 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
9052
9185
|
// package.json
|
|
9053
9186
|
var package_default = {
|
|
9054
9187
|
name: "@taskless-app/swarm",
|
|
9055
|
-
version: "0.4.
|
|
9188
|
+
version: "0.4.7",
|
|
9056
9189
|
type: "module",
|
|
9057
9190
|
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
9058
9191
|
keywords: [
|
|
@@ -10784,16 +10917,16 @@ async function fetchExternalStorm(url, opts = {}) {
|
|
|
10784
10917
|
}
|
|
10785
10918
|
|
|
10786
10919
|
// src/anomalyState.ts
|
|
10787
|
-
import { existsSync as existsSync11, readFileSync as
|
|
10788
|
-
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";
|
|
10789
10922
|
var KEY_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
10790
10923
|
var EMPTY = { since: {}, lastFiredAt: {}, fires: {}, stopQuiet: null };
|
|
10791
10924
|
function anomalyStatePath(home) {
|
|
10792
|
-
return
|
|
10925
|
+
return join20(home, "anomaly-state.json");
|
|
10793
10926
|
}
|
|
10794
10927
|
function loadAnomalyState(home, now = Date.now(), io = {}) {
|
|
10795
10928
|
const exists = io.exists ?? existsSync11;
|
|
10796
|
-
const readFile2 = io.readFile ?? ((p) =>
|
|
10929
|
+
const readFile2 = io.readFile ?? ((p) => readFileSync16(p, "utf8"));
|
|
10797
10930
|
const path = anomalyStatePath(home);
|
|
10798
10931
|
if (!exists(path)) return { ...EMPTY };
|
|
10799
10932
|
try {
|
|
@@ -10819,7 +10952,7 @@ function loadAnomalyState(home, now = Date.now(), io = {}) {
|
|
|
10819
10952
|
function saveAnomalyState(home, state, io = {}) {
|
|
10820
10953
|
const path = anomalyStatePath(home);
|
|
10821
10954
|
const writeFile2 = io.writeFile ?? ((p, data) => {
|
|
10822
|
-
|
|
10955
|
+
writeFileSync12(`${p}.tmp`, data);
|
|
10823
10956
|
renameSync6(`${p}.tmp`, p);
|
|
10824
10957
|
});
|
|
10825
10958
|
writeFile2(path, JSON.stringify(state, null, 2));
|
|
@@ -11462,10 +11595,10 @@ ${task.description}` : `\u0417\u0430\u0434\u0430\u0447\u0430 ${run.task_slug} (\
|
|
|
11462
11595
|
}
|
|
11463
11596
|
return parts.join("\n");
|
|
11464
11597
|
}
|
|
11465
|
-
var HERO_CHARTER_PATH =
|
|
11598
|
+
var HERO_CHARTER_PATH = join21(dirname5(THIS_FILE), "..", "docs", "HERO.md");
|
|
11466
11599
|
function buildHeroPrompt(task, run, taskContext, attachmentsBlock) {
|
|
11467
11600
|
const name = run.name || `#${run.id}`;
|
|
11468
|
-
const charter =
|
|
11601
|
+
const charter = readFileSync17(HERO_CHARTER_PATH, "utf8");
|
|
11469
11602
|
const head = task ? `\u042D\u041F\u0418\u041A ${run.task_slug}: ${task.title}
|
|
11470
11603
|
|
|
11471
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).`;
|
|
@@ -11801,9 +11934,9 @@ function repoHasCi(deps, run) {
|
|
|
11801
11934
|
function repoHasCiWorkflows(worktreePath, repo) {
|
|
11802
11935
|
for (const base3 of [worktreePath, repo]) {
|
|
11803
11936
|
if (!base3) continue;
|
|
11804
|
-
const dir =
|
|
11937
|
+
const dir = join21(base3, ".github", "workflows");
|
|
11805
11938
|
try {
|
|
11806
|
-
if (
|
|
11939
|
+
if (readdirSync10(dir).some((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) return true;
|
|
11807
11940
|
} catch {
|
|
11808
11941
|
}
|
|
11809
11942
|
}
|
|
@@ -13092,7 +13225,7 @@ async function resolveConflict(deps, reviewRun) {
|
|
|
13092
13225
|
workspace_id: reviewRun.workspace_id
|
|
13093
13226
|
// та же задача, тот же воркспейс (TASWM-288)
|
|
13094
13227
|
});
|
|
13095
|
-
const wt =
|
|
13228
|
+
const wt = join21(deps.worktreesDir, basename4(repo), `${reviewRun.task_slug}-resolve-${resolver.id}`);
|
|
13096
13229
|
try {
|
|
13097
13230
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
13098
13231
|
await deps.git.setWorktreeIdentity(wt, resolver.name);
|
|
@@ -13144,7 +13277,7 @@ async function spawnReviewer(deps, reviewRun, files) {
|
|
|
13144
13277
|
// (своей у ревьюера нет, TASWM-204), и коммент с вердиктом должен уехать именно туда.
|
|
13145
13278
|
workspace_id: reviewRun.workspace_id
|
|
13146
13279
|
});
|
|
13147
|
-
const wt =
|
|
13280
|
+
const wt = join21(deps.worktreesDir, basename4(repo), `${reviewRun.task_slug}-review-${reviewer.id}`);
|
|
13148
13281
|
try {
|
|
13149
13282
|
await deps.git.prepareDetachedWorktree(repo, branch, wt);
|
|
13150
13283
|
await deps.git.setWorktreeIdentity(wt, reviewer.name);
|
|
@@ -13239,22 +13372,59 @@ async function syncCrashedOnStartup(deps) {
|
|
|
13239
13372
|
}
|
|
13240
13373
|
return { orphaned, merged, requeued };
|
|
13241
13374
|
}
|
|
13242
|
-
async function makeStore(repo, worktreesDir) {
|
|
13375
|
+
async function makeStore(repo, worktreesDir, repoFlag) {
|
|
13243
13376
|
const host = loadHostIdentity({
|
|
13244
13377
|
home: SWARM_HOME,
|
|
13245
13378
|
envHostId: process.env.SWARM_HOST_ID,
|
|
13246
13379
|
envHostName: process.env.SWARM_HOST_NAME
|
|
13247
13380
|
});
|
|
13248
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
|
+
}
|
|
13249
13398
|
if (SWARM_BACKEND !== "client") return { store: new SwarmDb(DB_PATH, repo), reconciled: true, host };
|
|
13250
13399
|
const call = resilientSwarmMcp({
|
|
13251
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.`),
|
|
13252
13401
|
onUp: () => console.warn("[overmind] \u043F\u0440\u043E\u0434-MCP \u0441\u043D\u043E\u0432\u0430 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438.")
|
|
13253
13402
|
});
|
|
13254
|
-
const
|
|
13255
|
-
|
|
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 }));
|
|
13420
|
+
if (repoPaths.length > 0 && repoRegistry.length < repoPaths.length) {
|
|
13256
13421
|
console.warn(
|
|
13257
|
-
`[overmind] \u0440\u0435\u043F\u043E-\u0440\u0435\u0435\u0441\u0442\u0440: ${repoRegistry.length}/${
|
|
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)`
|
|
13423
|
+
);
|
|
13424
|
+
}
|
|
13425
|
+
if (repoRegistry.length === 0) {
|
|
13426
|
+
console.warn(
|
|
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).`
|
|
13258
13428
|
);
|
|
13259
13429
|
}
|
|
13260
13430
|
const client = new SwarmClient({ call, outboxPath: SWARM_OUTBOX, defaultRepo: repo, repoRegistry, host });
|
|
@@ -13291,7 +13461,7 @@ async function migrateWorktreesAtStartup() {
|
|
|
13291
13461
|
exists: (d) => existsSync12(d),
|
|
13292
13462
|
isNonEmptyDir: (d) => {
|
|
13293
13463
|
try {
|
|
13294
|
-
return
|
|
13464
|
+
return readdirSync10(d).length > 0;
|
|
13295
13465
|
} catch {
|
|
13296
13466
|
return false;
|
|
13297
13467
|
}
|
|
@@ -13322,7 +13492,11 @@ async function runDaemon(opts) {
|
|
|
13322
13492
|
installCrashGuards();
|
|
13323
13493
|
const daemonStart = await captureDaemonStart(THIS_FILE);
|
|
13324
13494
|
const worktreesDir = await migrateWorktreesAtStartup();
|
|
13325
|
-
|
|
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);
|
|
13326
13500
|
await preflightAuth();
|
|
13327
13501
|
const tl = await connectTaskless();
|
|
13328
13502
|
const tlFor = (workspaceId) => tl ? tl.forWorkspace(workspaceId) : null;
|
package/package.json
CHANGED