@taskless-app/swarm 0.4.1 → 0.4.3
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 +49 -5
- package/dist/cli.js +690 -121
- package/dist/overmind.js +804 -225
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { basename as basename4 } from "path";
|
|
5
|
-
import { existsSync as
|
|
5
|
+
import { existsSync as existsSync9, readdirSync as readdirSync6, statSync as statSync7, openSync as openSync4, readSync as readSync2, closeSync as closeSync4 } from "fs";
|
|
6
6
|
import { createInterface } from "readline/promises";
|
|
7
7
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
8
8
|
|
|
@@ -448,6 +448,8 @@ function groupByHost(items, ref, opts = {}) {
|
|
|
448
448
|
unknown,
|
|
449
449
|
lastSeenAt: unknown ? null : info?.lastSeenAt ?? null,
|
|
450
450
|
offline: false,
|
|
451
|
+
paused: unknown ? false : info?.paused === true,
|
|
452
|
+
pauseReason: unknown ? null : info?.pauseReason ?? null,
|
|
451
453
|
runs: []
|
|
452
454
|
};
|
|
453
455
|
byKey.set(key, g);
|
|
@@ -477,7 +479,9 @@ function hostStateText(host, nowMs = Date.now(), staleMs = HOST_HEARTBEAT_STALE_
|
|
|
477
479
|
if (!host.lastSeenAt) return "\u043F\u0443\u043B\u044C\u0441\u0430 \u043D\u0435 \u0431\u044B\u043B\u043E";
|
|
478
480
|
const at = Date.parse(host.lastSeenAt);
|
|
479
481
|
if (Number.isNaN(at)) return "\u043F\u0443\u043B\u044C\u0441\u0430 \u043D\u0435 \u0431\u044B\u043B\u043E";
|
|
480
|
-
if (!isHostOffline(host.lastSeenAt, nowMs, staleMs))
|
|
482
|
+
if (!isHostOffline(host.lastSeenAt, nowMs, staleMs)) {
|
|
483
|
+
return host.paused ? host.pauseReason ? `\u043D\u0430 \u043F\u0430\u0443\u0437\u0435 \u2014 ${host.pauseReason}` : "\u043D\u0430 \u043F\u0430\u0443\u0437\u0435" : "\u043D\u0430 \u0441\u0432\u044F\u0437\u0438";
|
|
484
|
+
}
|
|
481
485
|
const min = Math.max(1, Math.round((nowMs - at) / 6e4));
|
|
482
486
|
return `\u043C\u043E\u043B\u0447\u0438\u0442 ${min} \u043C\u0438\u043D`;
|
|
483
487
|
}
|
|
@@ -581,6 +585,8 @@ var SWARM_STORM_URL = process.env.SWARM_STORM_URL || "";
|
|
|
581
585
|
var WEB_PORT = Number(process.env.SWARM_WEB_PORT) || 7420;
|
|
582
586
|
var WEB_REDIRECT_URI = process.env.SWARM_REDIRECT_URI || `http://127.0.0.1:${WEB_PORT}/callback`;
|
|
583
587
|
var SWARM_ANOMALY_RENOTIFY_MS = envNum("SWARM_ANOMALY_RENOTIFY_MS", 60 * 6e4);
|
|
588
|
+
var SWARM_ANOMALY_MAX_REPEATS = Math.trunc(envNum("SWARM_ANOMALY_MAX_REPEATS", 3));
|
|
589
|
+
var SWARM_ANOMALY_RUN_DOWN_WINDOW_MS = envNum("SWARM_ANOMALY_RUN_DOWN_WINDOW_MS", 24 * 60 * 6e4);
|
|
584
590
|
var SWARM_ANOMALY_FAILED_SPIKE_COUNT = Math.trunc(envNum("SWARM_ANOMALY_FAILED_SPIKE_COUNT", 3));
|
|
585
591
|
var SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS = envNum("SWARM_ANOMALY_FAILED_SPIKE_WINDOW_MS", 30 * 6e4);
|
|
586
592
|
var SWARM_ANOMALY_QUEUE_STALLED_MS = envNum("SWARM_ANOMALY_QUEUE_STALLED_MS", 10 * 6e4);
|
|
@@ -1670,6 +1676,10 @@ var SwarmClient = class _SwarmClient {
|
|
|
1670
1676
|
depsDoneInProd = /* @__PURE__ */ new Set();
|
|
1671
1677
|
depsAskedAt = /* @__PURE__ */ new Map();
|
|
1672
1678
|
static DEPS_TTL_MS = 6e4;
|
|
1679
|
+
// TASWM-327: прод старее демона и не знает аргумент `level` у swarm_notify. Выясняется
|
|
1680
|
+
// ровно один раз (первой отвергнутой нотификацией) и держится всю жизнь процесса — иначе
|
|
1681
|
+
// каждый сигнал платил бы за это лишним отвергнутым вызовом. См. notifyAnomaly.
|
|
1682
|
+
notifyLevelUnsupported = false;
|
|
1673
1683
|
// Корреляция с продом.
|
|
1674
1684
|
keyByLocal = /* @__PURE__ */ new Map();
|
|
1675
1685
|
// local run id → runKey
|
|
@@ -2731,9 +2741,32 @@ var SwarmClient = class _SwarmClient {
|
|
|
2731
2741
|
lastSeenAt: typeof h.lastSeenAt === "string" ? h.lastSeenAt : null,
|
|
2732
2742
|
// TASWM-298: репозитории машины — «на связи» ещё не значит «возьмёт эту задачу».
|
|
2733
2743
|
// Прод старой версии поля не пришлёт → пустой список, строка просто без хвоста.
|
|
2734
|
-
reposServed: Array.isArray(h.reposServed) ? h.reposServed.filter((r) => typeof r === "string") : []
|
|
2744
|
+
reposServed: Array.isArray(h.reposServed) ? h.reposServed.filter((r) => typeof r === "string") : [],
|
|
2745
|
+
// TASWM-326: машина на паузе — «на связи, но работу не берёт». Прод старой
|
|
2746
|
+
// версии поля не пришлёт → undefined, строка выглядит как раньше.
|
|
2747
|
+
...typeof h.paused === "boolean" ? { paused: h.paused } : {},
|
|
2748
|
+
...typeof h.pausedAt === "string" ? { pausedAt: h.pausedAt } : {},
|
|
2749
|
+
...typeof h.pauseReason === "string" ? { pauseReason: h.pauseReason } : {}
|
|
2735
2750
|
}));
|
|
2736
2751
|
}
|
|
2752
|
+
/**
|
|
2753
|
+
* TASWM-326: убрать ЭТУ машину из парка (`swarm remove`). Прод удаляет строку
|
|
2754
|
+
* SwarmHost и освобождает её незавершённые прогоны — ровно то же, что делает свипер
|
|
2755
|
+
* с молчащей машиной, только сразу и по явной команде. Без этого выведенная из роя
|
|
2756
|
+
* машина висела в списке вечно с подписью «молчит N минут», неотличимо от упавшей.
|
|
2757
|
+
*
|
|
2758
|
+
* ПРЯМОЙ вызов, не outbox: это не durable-факт роя, который надо досылать после
|
|
2759
|
+
* восстановления сети, — команда имеет смысл только сейчас. Бросает при сбое:
|
|
2760
|
+
* вызывающий обязан сказать человеку, что из парка машину убрать не удалось, а не
|
|
2761
|
+
* молча стереть локальные токены (тогда её вообще нечем будет убрать).
|
|
2762
|
+
*/
|
|
2763
|
+
async forgetHost() {
|
|
2764
|
+
if (!this.host) throw new Error("\u043C\u0430\u0448\u0438\u043D\u0430 \u043D\u0435 \u043F\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043B\u0430\u0441\u044C (\u043D\u0435\u0442 hostId) \u2014 \u043D\u0435\u0447\u0435\u0433\u043E \u0443\u0431\u0438\u0440\u0430\u0442\u044C \u0438\u0437 \u043F\u0430\u0440\u043A\u0430");
|
|
2765
|
+
const res = await this.call("swarm_host_forget", { hostId: this.host.hostId });
|
|
2766
|
+
if (res?.isError) throw new Error("swarm_host_forget \u043E\u0442\u0432\u0435\u0440\u0433 \u0437\u0430\u043F\u0440\u043E\u0441 \u2014 \u043C\u0430\u0448\u0438\u043D\u0430 \u043E\u0441\u0442\u0430\u043B\u0430\u0441\u044C \u0432 \u043F\u0430\u0440\u043A\u0435");
|
|
2767
|
+
const sc = res.structuredContent ?? {};
|
|
2768
|
+
return { removed: sc.removed !== false, released: typeof sc.released === "number" ? sc.released : 0 };
|
|
2769
|
+
}
|
|
2737
2770
|
/**
|
|
2738
2771
|
* Пульс overmind в прод (TASWM-129): swarm_heartbeat обновляет User.swarmHeartbeatAt —
|
|
2739
2772
|
* дашборд /swarm сравнивает свежесть со своим порогом устаревания («нет связи с
|
|
@@ -2761,10 +2794,21 @@ var SwarmClient = class _SwarmClient {
|
|
|
2761
2794
|
* «пришли хвост лога» (logRequests). Возвращаем их вызывающему; пульс недоступен или
|
|
2762
2795
|
* прод старый (поля нет) → пустой список, всё как раньше.
|
|
2763
2796
|
*/
|
|
2764
|
-
|
|
2797
|
+
/**
|
|
2798
|
+
* TASWM-326: тем же пульсом уходит ПАУЗА этой машины (`pause` — durable-запись из
|
|
2799
|
+
* pause.json либо null). В парке машин «на паузе» обязано отличаться от «нет работы»
|
|
2800
|
+
* и от «молчит», а единственный канал «демон → прод» с нужной кадансностью — пульс.
|
|
2801
|
+
* Шлём ВСЕГДА (в т.ч. `paused:false`): это состояние, а не команда, и молчание тут
|
|
2802
|
+
* означало бы «прод помнит вчерашнюю паузу», когда рой давно работает.
|
|
2803
|
+
*/
|
|
2804
|
+
async heartbeat(daemonVersion, pause) {
|
|
2765
2805
|
try {
|
|
2766
2806
|
const args = this.repoFullNames.length ? { repoFullNames: this.repoFullNames } : {};
|
|
2767
2807
|
if (daemonVersion) args.daemonVersion = daemonVersion;
|
|
2808
|
+
if (pause !== void 0) {
|
|
2809
|
+
args.paused = !!pause?.paused;
|
|
2810
|
+
if (pause?.reason) args.pauseReason = pause.reason;
|
|
2811
|
+
}
|
|
2768
2812
|
if (this.host) {
|
|
2769
2813
|
args.hostId = this.host.hostId;
|
|
2770
2814
|
args.hostName = this.host.hostName;
|
|
@@ -2820,13 +2864,26 @@ var SwarmClient = class _SwarmClient {
|
|
|
2820
2864
|
*/
|
|
2821
2865
|
async notifyAnomaly(signal) {
|
|
2822
2866
|
const prodRunId = signal.runId != null ? this.prodIdByKey.get(this.runKeyFor(signal.runId) ?? "") : void 0;
|
|
2823
|
-
const
|
|
2867
|
+
const base3 = {
|
|
2824
2868
|
tag: signal.key,
|
|
2825
2869
|
title: signal.title,
|
|
2826
2870
|
body: signal.body,
|
|
2827
2871
|
...signal.href ? { href: signal.href } : {},
|
|
2828
2872
|
...prodRunId ? { runId: prodRunId } : {}
|
|
2829
|
-
}
|
|
2873
|
+
};
|
|
2874
|
+
if (!this.notifyLevelUnsupported) {
|
|
2875
|
+
const res2 = await this.call("swarm_notify", { ...base3, level: signal.level ?? "decision" });
|
|
2876
|
+
if (!res2?.isError) return;
|
|
2877
|
+
const text = JSON.stringify(res2);
|
|
2878
|
+
if (!(text.includes("\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D") && text.includes("\u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442") && text.includes("level"))) {
|
|
2879
|
+
throw new Error(`swarm_notify \u043E\u0442\u0432\u0435\u0440\u0433 \xAB${signal.key}\xBB`);
|
|
2880
|
+
}
|
|
2881
|
+
this.notifyLevelUnsupported = true;
|
|
2882
|
+
console.warn(
|
|
2883
|
+
"[swarm-client] \u043F\u0440\u043E\u0434 \u0441\u0442\u0430\u0440\u0435\u0435 \u0434\u0435\u043C\u043E\u043D\u0430 (swarm_notify \u0431\u0435\u0437 level) \u2014 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u0440\u043E\u044F \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u043E \u0438\u0434\u0443\u0442 \u043E\u0434\u043D\u0438\u043C \u0443\u0440\u043E\u0432\u043D\u0435\u043C, \u043A\u0430\u043A \u0434\u043E TASWM-327. \u041E\u0431\u043D\u043E\u0432\u0438 \u043F\u0440\u043E\u0434 \u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438 \u0434\u0435\u043C\u043E\u043D."
|
|
2884
|
+
);
|
|
2885
|
+
}
|
|
2886
|
+
const res = await this.call("swarm_notify", base3);
|
|
2830
2887
|
if (res?.isError) throw new Error(`swarm_notify \u043E\u0442\u0432\u0435\u0440\u0433 \xAB${signal.key}\xBB`);
|
|
2831
2888
|
}
|
|
2832
2889
|
/**
|
|
@@ -3228,6 +3285,85 @@ function parseDecision(v) {
|
|
|
3228
3285
|
}
|
|
3229
3286
|
}
|
|
3230
3287
|
|
|
3288
|
+
// src/daemonLaunch.ts
|
|
3289
|
+
import { openSync as openSync2, closeSync as closeSync2, renameSync as renameSync2, statSync as statSync2, writeFileSync as writeFileSync3, readFileSync as readFileSync4 } from "fs";
|
|
3290
|
+
import { spawn } from "child_process";
|
|
3291
|
+
var FOREGROUND_FLAGS = /* @__PURE__ */ new Set(["--foreground", "-f", "--attach"]);
|
|
3292
|
+
function parseStartMode(argv) {
|
|
3293
|
+
const rest = argv.filter((a) => !FOREGROUND_FLAGS.has(a));
|
|
3294
|
+
return { foreground: rest.length !== argv.length, rest };
|
|
3295
|
+
}
|
|
3296
|
+
function childStartArgv(rest) {
|
|
3297
|
+
return ["start", ...rest.filter((a) => !FOREGROUND_FLAGS.has(a)), "--foreground"];
|
|
3298
|
+
}
|
|
3299
|
+
function rotateDaemonLog(path, maxBytes) {
|
|
3300
|
+
let size;
|
|
3301
|
+
try {
|
|
3302
|
+
size = statSync2(path).size;
|
|
3303
|
+
} catch {
|
|
3304
|
+
return "missing";
|
|
3305
|
+
}
|
|
3306
|
+
if (size <= maxBytes) return "kept";
|
|
3307
|
+
renameSync2(path, `${path}.1`);
|
|
3308
|
+
writeFileSync3(path, "");
|
|
3309
|
+
return "rotated";
|
|
3310
|
+
}
|
|
3311
|
+
function tailText(text, lines) {
|
|
3312
|
+
const rows = text.split("\n");
|
|
3313
|
+
if (rows[rows.length - 1] === "") rows.pop();
|
|
3314
|
+
return rows.slice(-lines).join("\n");
|
|
3315
|
+
}
|
|
3316
|
+
async function waitForReady(opts) {
|
|
3317
|
+
const now = opts.now ?? Date.now;
|
|
3318
|
+
const sleep3 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
3319
|
+
const started = now();
|
|
3320
|
+
for (; ; ) {
|
|
3321
|
+
if (await opts.probe()) return "ready";
|
|
3322
|
+
if (!opts.alive()) return "died";
|
|
3323
|
+
if (now() - started >= opts.timeoutMs) return "timeout";
|
|
3324
|
+
await sleep3(opts.pollMs);
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
function launchDetached(opts) {
|
|
3328
|
+
const fd = openSync2(opts.logPath, "a");
|
|
3329
|
+
try {
|
|
3330
|
+
const child = spawn(opts.execPath, [...opts.execArgv, opts.entry, ...opts.args], {
|
|
3331
|
+
detached: true,
|
|
3332
|
+
stdio: ["ignore", fd, fd],
|
|
3333
|
+
env: opts.env
|
|
3334
|
+
});
|
|
3335
|
+
child.unref();
|
|
3336
|
+
let exited = false;
|
|
3337
|
+
child.once("exit", () => {
|
|
3338
|
+
exited = true;
|
|
3339
|
+
});
|
|
3340
|
+
return {
|
|
3341
|
+
pid: child.pid ?? -1,
|
|
3342
|
+
// Не `child.killed` (он про посланный сигнал) и не exit-код: после unref событие
|
|
3343
|
+
// может не прийти вовсе — спрашиваем ядро сигналом 0, это и есть «процесс есть».
|
|
3344
|
+
alive: () => {
|
|
3345
|
+
if (exited) return false;
|
|
3346
|
+
if (!child.pid) return false;
|
|
3347
|
+
try {
|
|
3348
|
+
process.kill(child.pid, 0);
|
|
3349
|
+
return true;
|
|
3350
|
+
} catch {
|
|
3351
|
+
return false;
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
};
|
|
3355
|
+
} finally {
|
|
3356
|
+
closeSync2(fd);
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
function tailFile(path, lines) {
|
|
3360
|
+
try {
|
|
3361
|
+
return tailText(readFileSync4(path, "utf8"), lines);
|
|
3362
|
+
} catch {
|
|
3363
|
+
return "";
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3231
3367
|
// src/procCleanup.ts
|
|
3232
3368
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
3233
3369
|
|
|
@@ -3489,7 +3625,7 @@ async function buildRepoRegistry(paths) {
|
|
|
3489
3625
|
|
|
3490
3626
|
// src/hostIdentity.ts
|
|
3491
3627
|
import { hostname } from "os";
|
|
3492
|
-
import { existsSync as existsSync4, readFileSync as
|
|
3628
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
3493
3629
|
import { join as join3 } from "path";
|
|
3494
3630
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3495
3631
|
function hostIdentityPath(home) {
|
|
@@ -3497,10 +3633,10 @@ function hostIdentityPath(home) {
|
|
|
3497
3633
|
}
|
|
3498
3634
|
function loadHostIdentity(deps) {
|
|
3499
3635
|
const exists = deps.exists ?? existsSync4;
|
|
3500
|
-
const readFile2 = deps.readFile ?? ((p) =>
|
|
3636
|
+
const readFile2 = deps.readFile ?? ((p) => readFileSync5(p, "utf8"));
|
|
3501
3637
|
const writeFile2 = deps.writeFile ?? ((p, data) => {
|
|
3502
|
-
|
|
3503
|
-
|
|
3638
|
+
writeFileSync4(`${p}.tmp`, data);
|
|
3639
|
+
renameSync3(`${p}.tmp`, p);
|
|
3504
3640
|
});
|
|
3505
3641
|
const host = deps.hostname ?? hostname;
|
|
3506
3642
|
const newId = deps.newId ?? randomUUID2;
|
|
@@ -3608,7 +3744,7 @@ import { basename as basename2 } from "path";
|
|
|
3608
3744
|
import { serve as serve2 } from "@hono/node-server";
|
|
3609
3745
|
|
|
3610
3746
|
// src/diff.ts
|
|
3611
|
-
import { statSync as
|
|
3747
|
+
import { statSync as statSync3 } from "fs";
|
|
3612
3748
|
import { join as join4 } from "path";
|
|
3613
3749
|
import { $ as $2 } from "execa";
|
|
3614
3750
|
var MAX_FILES = 200;
|
|
@@ -3752,7 +3888,7 @@ async function resolveBase(wt, baseBranch) {
|
|
|
3752
3888
|
}
|
|
3753
3889
|
function mtime(path) {
|
|
3754
3890
|
try {
|
|
3755
|
-
return
|
|
3891
|
+
return statSync3(path).mtimeMs;
|
|
3756
3892
|
} catch {
|
|
3757
3893
|
return 0;
|
|
3758
3894
|
}
|
|
@@ -3793,6 +3929,64 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
3793
3929
|
};
|
|
3794
3930
|
}
|
|
3795
3931
|
|
|
3932
|
+
// package.json
|
|
3933
|
+
var package_default = {
|
|
3934
|
+
name: "@taskless-app/swarm",
|
|
3935
|
+
version: "0.4.3",
|
|
3936
|
+
type: "module",
|
|
3937
|
+
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3938
|
+
keywords: [
|
|
3939
|
+
"ai-agents",
|
|
3940
|
+
"coding-agents",
|
|
3941
|
+
"swarm",
|
|
3942
|
+
"orchestration",
|
|
3943
|
+
"autonomous-agents",
|
|
3944
|
+
"mcp",
|
|
3945
|
+
"taskless",
|
|
3946
|
+
"cli"
|
|
3947
|
+
],
|
|
3948
|
+
license: "SEE LICENSE IN LICENSE",
|
|
3949
|
+
author: "Ilya Smurov <ilya@smurov.com>",
|
|
3950
|
+
homepage: "https://github.com/ilyasmurov/taskless/tree/main/packages/swarm-daemon#readme",
|
|
3951
|
+
repository: {
|
|
3952
|
+
type: "git",
|
|
3953
|
+
url: "git+https://github.com/ilyasmurov/taskless.git",
|
|
3954
|
+
directory: "packages/swarm-daemon"
|
|
3955
|
+
},
|
|
3956
|
+
engines: {
|
|
3957
|
+
node: ">=22"
|
|
3958
|
+
},
|
|
3959
|
+
bin: {
|
|
3960
|
+
swarm: "dist/cli.js"
|
|
3961
|
+
},
|
|
3962
|
+
files: [
|
|
3963
|
+
"dist",
|
|
3964
|
+
"docs/HERO.md",
|
|
3965
|
+
"roles"
|
|
3966
|
+
],
|
|
3967
|
+
scripts: {
|
|
3968
|
+
overmind: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/overmind.ts',
|
|
3969
|
+
swarm: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/cli.ts',
|
|
3970
|
+
build: "tsup",
|
|
3971
|
+
start: "node dist/overmind.js",
|
|
3972
|
+
prepublishOnly: "pnpm run build",
|
|
3973
|
+
test: "vitest run && SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
|
|
3974
|
+
"test:client": "SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
|
|
3975
|
+
"test:watch": "vitest",
|
|
3976
|
+
typecheck: "tsc -p tsconfig.json --noEmit",
|
|
3977
|
+
lint: "echo 'no lint in swarm-daemon yet'"
|
|
3978
|
+
},
|
|
3979
|
+
dependencies: {
|
|
3980
|
+
"@hono/node-server": "^2.0.12",
|
|
3981
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
3982
|
+
execa: "^10.0.0",
|
|
3983
|
+
zod: "3.25.76"
|
|
3984
|
+
},
|
|
3985
|
+
optionalDependencies: {
|
|
3986
|
+
"better-sqlite3": "^12.11.1"
|
|
3987
|
+
}
|
|
3988
|
+
};
|
|
3989
|
+
|
|
3796
3990
|
// src/gh.ts
|
|
3797
3991
|
import { $ as $3 } from "execa";
|
|
3798
3992
|
var NO_CHECKS_STDERR = /no checks reported/i;
|
|
@@ -3841,18 +4035,18 @@ async function ghChecks(pr, run = realChecksRunner) {
|
|
|
3841
4035
|
|
|
3842
4036
|
// src/droneChannel.ts
|
|
3843
4037
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
3844
|
-
import { chmodSync, mkdirSync as mkdirSync3, readFileSync as
|
|
4038
|
+
import { chmodSync, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
3845
4039
|
import { dirname as dirname2 } from "path";
|
|
3846
4040
|
import { serve } from "@hono/node-server";
|
|
3847
4041
|
function loadChannelSecret(path) {
|
|
3848
4042
|
try {
|
|
3849
|
-
const s =
|
|
4043
|
+
const s = readFileSync6(path, "utf8").trim();
|
|
3850
4044
|
if (s.length >= 32) return s;
|
|
3851
4045
|
} catch {
|
|
3852
4046
|
}
|
|
3853
4047
|
const secret = randomBytes(32).toString("hex");
|
|
3854
4048
|
mkdirSync3(dirname2(path), { recursive: true });
|
|
3855
|
-
|
|
4049
|
+
writeFileSync5(path, secret, { mode: 384 });
|
|
3856
4050
|
try {
|
|
3857
4051
|
chmodSync(path, 384);
|
|
3858
4052
|
} catch {
|
|
@@ -3886,7 +4080,7 @@ var EVENT_KIND_SET = new Set(EVENT_KINDS);
|
|
|
3886
4080
|
|
|
3887
4081
|
// src/oauth.ts
|
|
3888
4082
|
import { createHash, randomBytes as randomBytes2 } from "crypto";
|
|
3889
|
-
import { readFileSync as
|
|
4083
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, unlinkSync } from "fs";
|
|
3890
4084
|
import { join as join5 } from "path";
|
|
3891
4085
|
var REGISTER = "/api/oauth/register";
|
|
3892
4086
|
var AUTHORIZE = "/api/oauth/authorize";
|
|
@@ -3978,13 +4172,13 @@ function toStore(clientId, t, now) {
|
|
|
3978
4172
|
}
|
|
3979
4173
|
function loadStore() {
|
|
3980
4174
|
try {
|
|
3981
|
-
return JSON.parse(
|
|
4175
|
+
return JSON.parse(readFileSync7(STORE_PATH, "utf8"));
|
|
3982
4176
|
} catch {
|
|
3983
4177
|
return null;
|
|
3984
4178
|
}
|
|
3985
4179
|
}
|
|
3986
4180
|
function saveStore(s) {
|
|
3987
|
-
|
|
4181
|
+
writeFileSync6(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
|
|
3988
4182
|
}
|
|
3989
4183
|
function clearStore() {
|
|
3990
4184
|
try {
|
|
@@ -3994,7 +4188,7 @@ function clearStore() {
|
|
|
3994
4188
|
}
|
|
3995
4189
|
function loadClientId() {
|
|
3996
4190
|
try {
|
|
3997
|
-
return JSON.parse(
|
|
4191
|
+
return JSON.parse(readFileSync7(CLIENT_PATH, "utf8")).clientId;
|
|
3998
4192
|
} catch {
|
|
3999
4193
|
return null;
|
|
4000
4194
|
}
|
|
@@ -4003,7 +4197,7 @@ async function ensureClientId(api, redirectUri, fetchFn = fetch) {
|
|
|
4003
4197
|
const existing = loadClientId();
|
|
4004
4198
|
if (existing) return existing;
|
|
4005
4199
|
const clientId = await registerClient({ api, redirectUri }, fetchFn);
|
|
4006
|
-
|
|
4200
|
+
writeFileSync6(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
|
|
4007
4201
|
return clientId;
|
|
4008
4202
|
}
|
|
4009
4203
|
async function ensureAccess(store, opts = {}) {
|
|
@@ -4022,6 +4216,7 @@ async function ensureAccess(store, opts = {}) {
|
|
|
4022
4216
|
}
|
|
4023
4217
|
|
|
4024
4218
|
// src/web.ts
|
|
4219
|
+
var PACKAGE_VERSION = package_default.version;
|
|
4025
4220
|
var esc = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
4026
4221
|
var jsonForScript = (v) => JSON.stringify(v).replace(/</g, "\\u003c");
|
|
4027
4222
|
var TASKLESS_MARK = `<svg viewBox="0 0 60 54" width="19" height="19" fill="none" aria-hidden="true" style="display:block"><g transform="matrix(1.0512,0,0,1.0512,0,-0.021)"><path fill="currentColor" fill-rule="nonzero" d="M46.92,0 L10.24,0 C4.59,0 0,4.59 0,10.24 L0,10.4 C0,16.05 4.59,20.64 10.24,20.64 L16.84,20.64 C19.56,20.64 22.12,19.59 24.05,17.67 L36.06,5.77 C37.25,4.6 38.82,3.95 40.49,3.95 L46.93,3.95 C50.4,3.95 53.22,6.77 53.22,10.24 L53.22,10.4 C53.22,13.87 50.4,16.69 46.93,16.69 L39.82,16.69 C37.05,16.69 34.36,17.83 32.44,19.83 L21.13,31.58 C19.28,33.5 18.27,36.02 18.27,38.68 L18.27,40.94 C18.27,46.59 22.86,51.18 28.51,51.18 C34.16,51.18 38.75,46.59 38.75,40.94 L38.75,20.74 C39.1,20.68 39.46,20.64 39.82,20.64 L46.93,20.64 C52.58,20.64 57.17,16.05 57.17,10.4 L57.17,10.24 C57.17,4.59 52.58,0 46.93,0 L46.92,0 Z M21.26,14.87 C20.07,16.04 18.5,16.69 16.83,16.69 L10.23,16.69 C6.76,16.69 3.94,13.87 3.94,10.4 L3.94,10.24 C3.94,6.77 6.76,3.95 10.23,3.95 L32.27,3.95 L21.25,14.87 L21.26,14.87 Z M34.79,40.94 C34.79,44.41 31.97,47.23 28.5,47.23 C25.03,47.23 22.21,44.41 22.21,40.94 L22.21,38.68 C22.21,37.05 22.83,35.5 23.97,34.32 L34.79,23.07 L34.79,40.94 L34.79,40.94 Z"/></g></svg>`;
|
|
@@ -5302,6 +5497,36 @@ async function runDiff(db2, id, now = Date.now()) {
|
|
|
5302
5497
|
}
|
|
5303
5498
|
var readNarratorLog = (log) => log?.() ?? [];
|
|
5304
5499
|
var readRestartSnap = (restart, liveSessionIds) => restart ? { phase: restart.phase, sessionIds: liveSessionIds?.() ?? [] } : null;
|
|
5500
|
+
function pauseSourceOf(raw) {
|
|
5501
|
+
return raw === "cli" || raw === "remote" ? raw : "web";
|
|
5502
|
+
}
|
|
5503
|
+
function setPausedVia(deps, paused, source, reason) {
|
|
5504
|
+
const control = deps.control;
|
|
5505
|
+
if (control.set) {
|
|
5506
|
+
control.set(paused, { source, reason });
|
|
5507
|
+
return;
|
|
5508
|
+
}
|
|
5509
|
+
control.paused = paused;
|
|
5510
|
+
deps.narrate?.(
|
|
5511
|
+
paused ? "\u23F8 \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u043B \u0440\u043E\u0439 \u043D\u0430 \u043F\u0430\u0443\u0437\u0443 \u2014 \u043D\u043E\u0432\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0435 \u0431\u0435\u0440\u0451\u043C, \u0436\u0438\u0432\u044B\u0435 \u0434\u0440\u043E\u043D\u044B \u0434\u043E\u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0442." : "\u25B6\uFE0F \u041E\u043F\u0435\u0440\u0430\u0442\u043E\u0440 \u0441\u043D\u044F\u043B \u0440\u043E\u0439 \u0441 \u043F\u0430\u0443\u0437\u044B \u2014 \u0441\u043D\u043E\u0432\u0430 \u0431\u0435\u0440\u0451\u043C \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0437 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u0438 \u0438\u043D\u0442\u0435\u0439\u043A\u0430."
|
|
5512
|
+
);
|
|
5513
|
+
}
|
|
5514
|
+
function daemonStatus(deps) {
|
|
5515
|
+
const counts = {};
|
|
5516
|
+
for (const r of deps.db.listRuns()) counts[r.status] = (counts[r.status] ?? 0) + 1;
|
|
5517
|
+
const live = deps.liveSessionIds?.() ?? [];
|
|
5518
|
+
return {
|
|
5519
|
+
hostId: deps.selfHost?.hostId ?? null,
|
|
5520
|
+
hostName: deps.selfHost?.hostName ?? null,
|
|
5521
|
+
backend: SWARM_BACKEND,
|
|
5522
|
+
paused: deps.control?.paused ?? false,
|
|
5523
|
+
pause: deps.control?.state ?? null,
|
|
5524
|
+
restart: deps.restart ? { phase: deps.restart.phase, intent: deps.restart.intent, sessions: live } : null,
|
|
5525
|
+
counts,
|
|
5526
|
+
live,
|
|
5527
|
+
version: PACKAGE_VERSION
|
|
5528
|
+
};
|
|
5529
|
+
}
|
|
5305
5530
|
function eventsStream(db2, narratorLog, control, daemonVersion, restart, liveSessionIds, selfHost) {
|
|
5306
5531
|
let iv;
|
|
5307
5532
|
let last = "";
|
|
@@ -5470,18 +5695,25 @@ function makeHandler(deps) {
|
|
|
5470
5695
|
return redirect("/");
|
|
5471
5696
|
}
|
|
5472
5697
|
if (p === "/pause" && req.method === "POST") {
|
|
5473
|
-
if (!
|
|
5698
|
+
if (!authed(req)) return unauthorized();
|
|
5474
5699
|
if (!deps.control) return new Response("\u043F\u0430\u0443\u0437\u0430 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430: \u043C\u043E\u0440\u0434\u0430 \u0431\u0435\u0437 \u0436\u0438\u0432\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430", { status: 503 });
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5700
|
+
const f = await formValue(req);
|
|
5701
|
+
const reason = (f.get("reason") ?? "").trim() || null;
|
|
5702
|
+
setPausedVia(deps, true, pauseSourceOf(f.get("source")), reason);
|
|
5703
|
+
return Response.json({ ok: true, paused: true, why: "\u0440\u043E\u0439 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435 \u2014 \u043D\u043E\u0432\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443 \u043D\u0435 \u0431\u0435\u0440\u0451\u043C, \u0436\u0438\u0432\u044B\u0435 \u0434\u0440\u043E\u043D\u044B \u0434\u043E\u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0442" });
|
|
5478
5704
|
}
|
|
5479
5705
|
if (p === "/resume" && req.method === "POST") {
|
|
5480
|
-
if (!
|
|
5706
|
+
if (!authed(req)) return unauthorized();
|
|
5481
5707
|
if (!deps.control) return new Response("\u043F\u0430\u0443\u0437\u0430 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430: \u043C\u043E\u0440\u0434\u0430 \u0431\u0435\u0437 \u0436\u0438\u0432\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430", { status: 503 });
|
|
5482
|
-
deps.
|
|
5483
|
-
|
|
5484
|
-
|
|
5708
|
+
setPausedVia(deps, false, pauseSourceOf((await formValue(req)).get("source")), null);
|
|
5709
|
+
return Response.json({ ok: true, paused: false, why: "\u0440\u043E\u0439 \u0441\u043D\u044F\u0442 \u0441 \u043F\u0430\u0443\u0437\u044B \u2014 \u0441\u043D\u043E\u0432\u0430 \u0431\u0435\u0440\u0451\u043C \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0437 \u043E\u0447\u0435\u0440\u0435\u0434\u0438 \u0438 \u0438\u043D\u0442\u0435\u0439\u043A\u0430" });
|
|
5710
|
+
}
|
|
5711
|
+
if (p === "/status") return Response.json(daemonStatus(deps));
|
|
5712
|
+
if (p === "/shutdown" && req.method === "POST") {
|
|
5713
|
+
if (!authed(req)) return unauthorized();
|
|
5714
|
+
if (!deps.requestStop) return new Response("\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430: \u043C\u043E\u0440\u0434\u0430 \u0431\u0435\u0437 \u0436\u0438\u0432\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430", { status: 503 });
|
|
5715
|
+
const f = await formValue(req);
|
|
5716
|
+
return Response.json(deps.requestStop({ force: f.get("force") === "1", wait: f.get("wait") === "1" }));
|
|
5485
5717
|
}
|
|
5486
5718
|
if (p === "/revive" && req.method === "POST") {
|
|
5487
5719
|
if (!auth()) return unauthorized();
|
|
@@ -5544,8 +5776,8 @@ function startWeb(deps, port = WEB_PORT) {
|
|
|
5544
5776
|
import { cancelRun, respawnRun, runDaemon, wakeRun, OVERMIND_ENTRY, listDroneProcesses } from "./overmind.js";
|
|
5545
5777
|
|
|
5546
5778
|
// src/launcher.ts
|
|
5547
|
-
import { spawn } from "child_process";
|
|
5548
|
-
import { closeSync as
|
|
5779
|
+
import { spawn as spawn2 } from "child_process";
|
|
5780
|
+
import { closeSync as closeSync3, existsSync as existsSync6, mkdirSync as mkdirSync5, openSync as openSync3, readdirSync as readdirSync4, statSync as statSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
5549
5781
|
import { createRequire as createRequire2 } from "module";
|
|
5550
5782
|
import { homedir as homedir2 } from "os";
|
|
5551
5783
|
import { dirname as dirname3, join as join7 } from "path";
|
|
@@ -5624,7 +5856,7 @@ function resolveClaudeBin(opts = {}) {
|
|
|
5624
5856
|
}
|
|
5625
5857
|
|
|
5626
5858
|
// src/droneHome.ts
|
|
5627
|
-
import { lstatSync, mkdirSync as mkdirSync4, readdirSync as readdirSync3, rmSync, statSync as
|
|
5859
|
+
import { lstatSync, mkdirSync as mkdirSync4, readdirSync as readdirSync3, rmSync, statSync as statSync4, symlinkSync } from "fs";
|
|
5628
5860
|
import { join as join6, relative, sep } from "path";
|
|
5629
5861
|
var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
|
|
5630
5862
|
function droneHomeDeny(env = process.env) {
|
|
@@ -5665,7 +5897,7 @@ function buildDroneHome(opts) {
|
|
|
5665
5897
|
const target = join6(realHome, name);
|
|
5666
5898
|
let isDir;
|
|
5667
5899
|
try {
|
|
5668
|
-
isDir =
|
|
5900
|
+
isDir = statSync4(target).isDirectory();
|
|
5669
5901
|
} catch {
|
|
5670
5902
|
continue;
|
|
5671
5903
|
}
|
|
@@ -5831,14 +6063,14 @@ function openNextRunLog(runLogsDir, runId) {
|
|
|
5831
6063
|
const path = nextRunLogPath(runLogsDir, runId);
|
|
5832
6064
|
let out;
|
|
5833
6065
|
try {
|
|
5834
|
-
out =
|
|
6066
|
+
out = openSync3(path, "wx");
|
|
5835
6067
|
} catch (e) {
|
|
5836
6068
|
if (e.code !== "EEXIST") throw e;
|
|
5837
6069
|
continue;
|
|
5838
6070
|
}
|
|
5839
6071
|
let err = null;
|
|
5840
6072
|
try {
|
|
5841
|
-
err =
|
|
6073
|
+
err = openSync3(`${path.slice(0, -".json".length)}.err.log`, "w");
|
|
5842
6074
|
} catch {
|
|
5843
6075
|
err = null;
|
|
5844
6076
|
}
|
|
@@ -5899,7 +6131,7 @@ function makeLauncher(cfg) {
|
|
|
5899
6131
|
}
|
|
5900
6132
|
};
|
|
5901
6133
|
const cfgPath = join7(worktree, ".swarm-mcp.json");
|
|
5902
|
-
|
|
6134
|
+
writeFileSync7(cfgPath, JSON.stringify(mcpConfig, null, 2));
|
|
5903
6135
|
const model = run.model ?? process.env.SWARM_DRONE_MODEL ?? void 0;
|
|
5904
6136
|
const pluginDirs = roleDirs(worktree, run.kind);
|
|
5905
6137
|
const realHome = cfg.droneIsolation?.realHome ?? homedir2();
|
|
@@ -5919,7 +6151,7 @@ function makeLauncher(cfg) {
|
|
|
5919
6151
|
errFd = fds.err ?? void 0;
|
|
5920
6152
|
stdio = ["ignore", outFd, errFd ?? "ignore"];
|
|
5921
6153
|
}
|
|
5922
|
-
const child =
|
|
6154
|
+
const child = spawn2(cfg.claudeBin ?? resolveClaudeBin(), claudeArgs(cfgPath, prompt, continueSession, model, pluginDirs, settings), {
|
|
5923
6155
|
cwd: worktree,
|
|
5924
6156
|
detached: true,
|
|
5925
6157
|
stdio,
|
|
@@ -5929,8 +6161,8 @@ function makeLauncher(cfg) {
|
|
|
5929
6161
|
// Остальной env демона наследуется, как раньше.
|
|
5930
6162
|
env: droneEnv(worktree, process.env, home ? { droneHome: home, realHome } : void 0, cfg.droneCap?.() ?? null)
|
|
5931
6163
|
});
|
|
5932
|
-
if (outFd !== void 0)
|
|
5933
|
-
if (errFd !== void 0)
|
|
6164
|
+
if (outFd !== void 0) closeSync3(outFd);
|
|
6165
|
+
if (errFd !== void 0) closeSync3(errFd);
|
|
5934
6166
|
child.unref();
|
|
5935
6167
|
if (child.pid == null) throw new Error("\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0434\u0440\u043E\u043D\u0430 (\u043D\u0435\u0442 pid)");
|
|
5936
6168
|
return child.pid;
|
|
@@ -5938,13 +6170,13 @@ function makeLauncher(cfg) {
|
|
|
5938
6170
|
}
|
|
5939
6171
|
|
|
5940
6172
|
// src/git.ts
|
|
5941
|
-
import { existsSync as existsSync7, readdirSync as readdirSync5, statSync as
|
|
6173
|
+
import { existsSync as existsSync7, readdirSync as readdirSync5, statSync as statSync6 } from "fs";
|
|
5942
6174
|
import { appendFile, mkdir, readFile, rm } from "fs/promises";
|
|
5943
6175
|
import { isAbsolute, join as join8 } from "path";
|
|
5944
6176
|
import { $ as $4 } from "execa";
|
|
5945
6177
|
function isLinkedWorktree(dir) {
|
|
5946
6178
|
try {
|
|
5947
|
-
return
|
|
6179
|
+
return statSync6(join8(dir, ".git")).isFile();
|
|
5948
6180
|
} catch {
|
|
5949
6181
|
return false;
|
|
5950
6182
|
}
|
|
@@ -5984,20 +6216,20 @@ async function removeWorktree(repo, path) {
|
|
|
5984
6216
|
}
|
|
5985
6217
|
|
|
5986
6218
|
// src/agentToken.ts
|
|
5987
|
-
import { readFileSync as
|
|
6219
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, unlinkSync as unlinkSync2 } from "fs";
|
|
5988
6220
|
import { join as join9 } from "path";
|
|
5989
6221
|
var STORE_PATH2 = join9(SWARM_HOME, "agent.json");
|
|
5990
6222
|
var base2 = (api) => api.replace(/\/$/, "");
|
|
5991
6223
|
function loadAgentToken() {
|
|
5992
6224
|
try {
|
|
5993
|
-
const s = JSON.parse(
|
|
6225
|
+
const s = JSON.parse(readFileSync8(STORE_PATH2, "utf8"));
|
|
5994
6226
|
return s.token ? s : null;
|
|
5995
6227
|
} catch {
|
|
5996
6228
|
return null;
|
|
5997
6229
|
}
|
|
5998
6230
|
}
|
|
5999
6231
|
function saveAgentToken(s) {
|
|
6000
|
-
|
|
6232
|
+
writeFileSync8(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
|
|
6001
6233
|
}
|
|
6002
6234
|
function clearAgentToken() {
|
|
6003
6235
|
try {
|
|
@@ -6049,19 +6281,19 @@ import { basename as basename3, isAbsolute as isAbsolute2, join as join11 } from
|
|
|
6049
6281
|
var SHOT_MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
6050
6282
|
|
|
6051
6283
|
// src/workspace.ts
|
|
6052
|
-
import { readFileSync as
|
|
6284
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3 } from "fs";
|
|
6053
6285
|
import { join as join12 } from "path";
|
|
6054
6286
|
var WORKSPACE_PATH = join12(SWARM_HOME, "workspace.json");
|
|
6055
6287
|
function loadWorkspace() {
|
|
6056
6288
|
try {
|
|
6057
|
-
const w = JSON.parse(
|
|
6289
|
+
const w = JSON.parse(readFileSync9(WORKSPACE_PATH, "utf8"));
|
|
6058
6290
|
return w && typeof w.id === "string" && w.id ? w : null;
|
|
6059
6291
|
} catch {
|
|
6060
6292
|
return null;
|
|
6061
6293
|
}
|
|
6062
6294
|
}
|
|
6063
6295
|
function saveWorkspace(w) {
|
|
6064
|
-
|
|
6296
|
+
writeFileSync9(WORKSPACE_PATH, JSON.stringify(w, null, 2), { mode: 384 });
|
|
6065
6297
|
}
|
|
6066
6298
|
function clearWorkspace() {
|
|
6067
6299
|
try {
|
|
@@ -6516,68 +6748,106 @@ function daemonCancel(slug, reason) {
|
|
|
6516
6748
|
function daemonWake(runId) {
|
|
6517
6749
|
return post("/wake", { runId: String(runId) });
|
|
6518
6750
|
}
|
|
6751
|
+
function daemonPause(reason) {
|
|
6752
|
+
return post("/pause", { source: "cli", ...reason ? { reason } : {} });
|
|
6753
|
+
}
|
|
6754
|
+
function daemonResume() {
|
|
6755
|
+
return post("/resume", { source: "cli" });
|
|
6756
|
+
}
|
|
6757
|
+
async function daemonStop(opts) {
|
|
6758
|
+
const res = await post("/shutdown", { force: opts.force ? "1" : "0", wait: opts.wait ? "1" : "0" });
|
|
6759
|
+
if (!res) return null;
|
|
6760
|
+
const raw = res;
|
|
6761
|
+
if (raw.stopping === void 0 && raw.waiting === void 0) return null;
|
|
6762
|
+
return { stopping: !!raw.stopping, waiting: !!raw.waiting, sessions: Array.isArray(raw.sessions) ? raw.sessions : [] };
|
|
6763
|
+
}
|
|
6764
|
+
async function daemonStatus2() {
|
|
6765
|
+
try {
|
|
6766
|
+
const res = await fetch(`${BASE}/status`, { headers: headers(), signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
6767
|
+
if (!res.ok) return null;
|
|
6768
|
+
const body = await res.json();
|
|
6769
|
+
return typeof body?.paused === "boolean" ? body : null;
|
|
6770
|
+
} catch {
|
|
6771
|
+
return null;
|
|
6772
|
+
}
|
|
6773
|
+
}
|
|
6519
6774
|
var NO_DAEMON_NOTE = `\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 (\u043C\u043E\u0440\u0434\u0430 127.0.0.1:${WEB_PORT}) \u2014 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u044E \u041B\u041E\u041A\u0410\u041B\u042C\u041D\u042B\u0419 \u0441\u0442\u043E\u0440 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u044B, \u043E\u043D \u043C\u043E\u0436\u0435\u0442 \u0440\u0430\u0441\u0445\u043E\u0434\u0438\u0442\u044C\u0441\u044F \u0441 \u0434\u0430\u0448\u0431\u043E\u0440\u0434\u043E\u043C. \u041F\u043E\u0434\u043D\u0438\u043C\u0438 \u0434\u0435\u043C\u043E\u043D\u0430: swarm start`;
|
|
6520
6775
|
|
|
6521
|
-
//
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
},
|
|
6545
|
-
engines: {
|
|
6546
|
-
node: ">=22"
|
|
6547
|
-
},
|
|
6548
|
-
bin: {
|
|
6549
|
-
swarm: "dist/cli.js"
|
|
6550
|
-
},
|
|
6551
|
-
files: [
|
|
6552
|
-
"dist",
|
|
6553
|
-
"docs/HERO.md",
|
|
6554
|
-
"roles"
|
|
6555
|
-
],
|
|
6556
|
-
scripts: {
|
|
6557
|
-
overmind: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/overmind.ts',
|
|
6558
|
-
swarm: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/cli.ts',
|
|
6559
|
-
build: "tsup",
|
|
6560
|
-
start: "node dist/overmind.js",
|
|
6561
|
-
prepublishOnly: "pnpm run build",
|
|
6562
|
-
test: "vitest run && SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
|
|
6563
|
-
"test:client": "SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
|
|
6564
|
-
"test:watch": "vitest",
|
|
6565
|
-
typecheck: "tsc -p tsconfig.json --noEmit",
|
|
6566
|
-
lint: "echo 'no lint in swarm-daemon yet'"
|
|
6567
|
-
},
|
|
6568
|
-
dependencies: {
|
|
6569
|
-
"@hono/node-server": "^2.0.12",
|
|
6570
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
6571
|
-
execa: "^10.0.0",
|
|
6572
|
-
zod: "3.25.76"
|
|
6573
|
-
},
|
|
6574
|
-
optionalDependencies: {
|
|
6575
|
-
"better-sqlite3": "^12.11.1"
|
|
6776
|
+
// src/pauseState.ts
|
|
6777
|
+
import { existsSync as existsSync8, readFileSync as readFileSync10, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
6778
|
+
import { join as join13 } from "path";
|
|
6779
|
+
function pauseStatePath(home) {
|
|
6780
|
+
return join13(home, "pause.json");
|
|
6781
|
+
}
|
|
6782
|
+
var ACTIVE = { paused: false, at: "", source: "cli" };
|
|
6783
|
+
function loadPauseState(home, io = {}) {
|
|
6784
|
+
const exists = io.exists ?? existsSync8;
|
|
6785
|
+
const readFile2 = io.readFile ?? ((p) => readFileSync10(p, "utf8"));
|
|
6786
|
+
const path = pauseStatePath(home);
|
|
6787
|
+
if (!exists(path)) return ACTIVE;
|
|
6788
|
+
try {
|
|
6789
|
+
const parsed = JSON.parse(readFile2(path));
|
|
6790
|
+
if (parsed.paused !== true) return ACTIVE;
|
|
6791
|
+
return {
|
|
6792
|
+
paused: true,
|
|
6793
|
+
at: typeof parsed.at === "string" ? parsed.at : "",
|
|
6794
|
+
source: parsed.source === "web" || parsed.source === "remote" ? parsed.source : "cli",
|
|
6795
|
+
...typeof parsed.reason === "string" && parsed.reason.trim() ? { reason: parsed.reason.trim() } : {}
|
|
6796
|
+
};
|
|
6797
|
+
} catch {
|
|
6798
|
+
return ACTIVE;
|
|
6576
6799
|
}
|
|
6577
|
-
}
|
|
6800
|
+
}
|
|
6801
|
+
function savePauseState(home, state, io = {}) {
|
|
6802
|
+
const path = pauseStatePath(home);
|
|
6803
|
+
if (!state.paused) {
|
|
6804
|
+
const remove = io.remove ?? ((p) => existsSync8(p) && unlinkSync4(p));
|
|
6805
|
+
remove(path);
|
|
6806
|
+
return;
|
|
6807
|
+
}
|
|
6808
|
+
const writeFile2 = io.writeFile ?? ((p, data) => {
|
|
6809
|
+
writeFileSync10(`${p}.tmp`, data);
|
|
6810
|
+
renameSync4(`${p}.tmp`, p);
|
|
6811
|
+
});
|
|
6812
|
+
writeFile2(path, JSON.stringify(state, null, 2));
|
|
6813
|
+
}
|
|
6814
|
+
function makePauseState(source, reason, now = Date.now()) {
|
|
6815
|
+
return {
|
|
6816
|
+
paused: true,
|
|
6817
|
+
at: new Date(now).toISOString(),
|
|
6818
|
+
source,
|
|
6819
|
+
...reason && reason.trim() ? { reason: reason.trim() } : {}
|
|
6820
|
+
};
|
|
6821
|
+
}
|
|
6822
|
+
function pauseText(state, now = Date.now()) {
|
|
6823
|
+
if (!state?.paused) return "\u0440\u043E\u0439 \u0430\u043A\u0442\u0438\u0432\u0435\u043D \u2014 \u0431\u0435\u0440\u0451\u0442 \u043D\u043E\u0432\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443";
|
|
6824
|
+
const since = state.at ? sinceText(state.at, now) : "";
|
|
6825
|
+
const who = { cli: "\u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 \u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0435", web: "\u0441 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u0439 \u043C\u043E\u0440\u0434\u044B", remote: "\u0438\u0437 Taskless" }[state.source];
|
|
6826
|
+
const why = state.reason ? ` \u2014 ${state.reason}` : "";
|
|
6827
|
+
return `\u0440\u043E\u0439 \u041D\u0410 \u041F\u0410\u0423\u0417\u0415${since} (${who})${why}`;
|
|
6828
|
+
}
|
|
6829
|
+
function sinceText(at, now) {
|
|
6830
|
+
const ms = Date.parse(at);
|
|
6831
|
+
if (Number.isNaN(ms)) return "";
|
|
6832
|
+
const min = Math.max(0, Math.round((now - ms) / 6e4));
|
|
6833
|
+
if (min < 1) return " (\u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E)";
|
|
6834
|
+
if (min < 60) return ` (${min} \u043C\u0438\u043D)`;
|
|
6835
|
+
return ` (${Math.round(min / 60)} \u0447)`;
|
|
6836
|
+
}
|
|
6837
|
+
|
|
6838
|
+
// src/heartbeat.ts
|
|
6839
|
+
import { readFileSync as readFileSync11, renameSync as renameSync5, writeFileSync as writeFileSync11 } from "fs";
|
|
6840
|
+
function readHeartbeat(path) {
|
|
6841
|
+
try {
|
|
6842
|
+
const ms = Number(readFileSync11(path, "utf8").trim());
|
|
6843
|
+
return Number.isFinite(ms) ? ms : null;
|
|
6844
|
+
} catch {
|
|
6845
|
+
return null;
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6578
6848
|
|
|
6579
6849
|
// src/cli.ts
|
|
6580
|
-
var
|
|
6850
|
+
var PACKAGE_VERSION2 = package_default.version;
|
|
6581
6851
|
function takeRepo(argv) {
|
|
6582
6852
|
const i = argv.indexOf("--repo");
|
|
6583
6853
|
if (i === -1) return { repo: null, rest: argv };
|
|
@@ -6733,6 +7003,215 @@ async function runLogin(db2, opts) {
|
|
|
6733
7003
|
web.server.close();
|
|
6734
7004
|
}
|
|
6735
7005
|
}
|
|
7006
|
+
async function liveSessionNames(ids) {
|
|
7007
|
+
if (ids.length === 0) return "";
|
|
7008
|
+
const snap = await daemonRuns();
|
|
7009
|
+
const byId = new Map((snap?.runs ?? []).map((r) => [r.id, r]));
|
|
7010
|
+
return ids.map((id) => {
|
|
7011
|
+
const r = byId.get(id);
|
|
7012
|
+
return r ? `${r.name} (${r.task_slug})` : `#${id}`;
|
|
7013
|
+
}).join(", ");
|
|
7014
|
+
}
|
|
7015
|
+
async function waitDaemonDown(opts) {
|
|
7016
|
+
const pollMs = opts.pollMs ?? 3e3;
|
|
7017
|
+
const until = Date.now() + opts.timeoutMs;
|
|
7018
|
+
let lastLive = -1;
|
|
7019
|
+
for (; ; ) {
|
|
7020
|
+
const st = await daemonStatus2();
|
|
7021
|
+
if (!st) return true;
|
|
7022
|
+
if (st.live.length !== lastLive) {
|
|
7023
|
+
lastLive = st.live.length;
|
|
7024
|
+
console.log(lastLive ? `\u0436\u0434\u0443 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u0436\u0438\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439: ${lastLive}\u2026` : "\u0436\u0438\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u043D\u0435 \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C \u2014 \u0433\u0430\u0448\u0443\u2026");
|
|
7025
|
+
}
|
|
7026
|
+
if (Date.now() >= until) return false;
|
|
7027
|
+
await sleep2(pollMs);
|
|
7028
|
+
}
|
|
7029
|
+
}
|
|
7030
|
+
var MAX_DAEMON_LOG_BYTES = 16 * 1024 * 1024;
|
|
7031
|
+
var START_READY_TIMEOUT_MS = 45e3;
|
|
7032
|
+
async function runStartDetached(args) {
|
|
7033
|
+
const already = await daemonStatus2();
|
|
7034
|
+
if (already) {
|
|
7035
|
+
console.log(`\u0434\u0435\u043C\u043E\u043D \u0443\u0436\u0435 \u043F\u043E\u0434\u043D\u044F\u0442 (v${already.version})${already.hostName ? `, \u043C\u0430\u0448\u0438\u043D\u0430 \xAB${already.hostName}\xBB` : ""} \u2014 \u0432\u0442\u043E\u0440\u043E\u0433\u043E \u043D\u0435 \u043D\u0443\u0436\u043D\u043E.`);
|
|
7036
|
+
console.log("\u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435: swarm status \xB7 \u043B\u043E\u0433: swarm logs -f \xB7 \u043F\u043E\u0433\u0430\u0441\u0438\u0442\u044C: swarm stop");
|
|
7037
|
+
return 0;
|
|
7038
|
+
}
|
|
7039
|
+
const rotated = rotateDaemonLog(DAEMON_LOG_PATH, MAX_DAEMON_LOG_BYTES);
|
|
7040
|
+
const entry = process.argv[1];
|
|
7041
|
+
if (!entry) {
|
|
7042
|
+
console.error("\u043D\u0435\u0447\u0435\u043C \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0441\u0435\u0431\u044F \u0444\u043E\u043D\u043E\u043C (\u043F\u0443\u0441\u0442\u043E\u0439 argv[1]) \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0438 \u0432 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435: swarm start --foreground");
|
|
7043
|
+
return 1;
|
|
7044
|
+
}
|
|
7045
|
+
let launch;
|
|
7046
|
+
try {
|
|
7047
|
+
launch = launchDetached({
|
|
7048
|
+
execPath: process.execPath,
|
|
7049
|
+
// execArgv тащим за собой намеренно: в монорепе CLI запускают через tsx-loader, и
|
|
7050
|
+
// без него ребёнок не прочитает .ts-исходник, с которого сам же и стартовал.
|
|
7051
|
+
execArgv: process.execArgv,
|
|
7052
|
+
entry,
|
|
7053
|
+
args: childStartArgv(args),
|
|
7054
|
+
logPath: DAEMON_LOG_PATH,
|
|
7055
|
+
env: process.env
|
|
7056
|
+
});
|
|
7057
|
+
} catch (e) {
|
|
7058
|
+
console.error(`\u043D\u0435 \u0441\u043C\u043E\u0433 \u043E\u0442\u0434\u0435\u043B\u0438\u0442\u044C \u0434\u0435\u043C\u043E\u043D\u0430 \u043E\u0442 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0430: ${e instanceof Error ? e.message : e}`);
|
|
7059
|
+
console.error("\u043F\u043E\u0434\u043D\u0438\u043C\u0438 \u0432 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435: swarm start --foreground");
|
|
7060
|
+
return 1;
|
|
7061
|
+
}
|
|
7062
|
+
console.log(`\u043F\u043E\u0434\u043D\u0438\u043C\u0430\u044E \u0440\u043E\u0439 \u0444\u043E\u043D\u043E\u043C (pid ${launch.pid}), \u043B\u043E\u0433: ${DAEMON_LOG_PATH}${rotated === "rotated" ? " (\u043F\u0440\u043E\u0448\u043B\u044B\u0439 \u0443\u0435\u0445\u0430\u043B \u0432 .1)" : ""}`);
|
|
7063
|
+
const verdict = await waitForReady({
|
|
7064
|
+
probe: async () => await daemonStatus2() != null,
|
|
7065
|
+
alive: launch.alive,
|
|
7066
|
+
timeoutMs: START_READY_TIMEOUT_MS,
|
|
7067
|
+
pollMs: 500
|
|
7068
|
+
});
|
|
7069
|
+
if (verdict === "ready") {
|
|
7070
|
+
console.log("\u0440\u043E\u0439 \u043F\u043E\u0434\u043D\u044F\u0442, \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B \u0441\u0432\u043E\u0431\u043E\u0434\u0435\u043D. \u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435: swarm status \xB7 \u043B\u043E\u0433: swarm logs -f \xB7 \u043F\u043E\u0433\u0430\u0441\u0438\u0442\u044C: swarm stop");
|
|
7071
|
+
return 0;
|
|
7072
|
+
}
|
|
7073
|
+
console.error(
|
|
7074
|
+
verdict === "died" ? "\u0434\u0435\u043C\u043E\u043D \u041D\u0415 \u043F\u043E\u0434\u043D\u044F\u043B\u0441\u044F \u2014 \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0432\u044B\u0448\u0435\u043B \u0441\u0440\u0430\u0437\u0443. \u041F\u0440\u0438\u0447\u0438\u043D\u0430 \u0432 \u0445\u0432\u043E\u0441\u0442\u0435 \u043B\u043E\u0433\u0430:" : `\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u043E\u0437\u0432\u0430\u043B\u0441\u044F \u0437\u0430 ${Math.round(START_READY_TIMEOUT_MS / 1e3)}\u0441 (\u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0435\u0449\u0451 \u0436\u0438\u0432, pid ${launch.pid}). \u0425\u0432\u043E\u0441\u0442 \u043B\u043E\u0433\u0430:`
|
|
7075
|
+
);
|
|
7076
|
+
console.error(tailFile(DAEMON_LOG_PATH, 20) || `(\u043B\u043E\u0433 \u043F\u0443\u0441\u0442: ${DAEMON_LOG_PATH})`);
|
|
7077
|
+
return 1;
|
|
7078
|
+
}
|
|
7079
|
+
async function runDaemonLogs(argv) {
|
|
7080
|
+
const follow = argv.includes("-f") || argv.includes("--follow");
|
|
7081
|
+
const nAt = argv.findIndex((a) => a === "-n" || a === "--lines");
|
|
7082
|
+
const lines = nAt >= 0 ? Number(argv[nAt + 1]) : 200;
|
|
7083
|
+
if (!Number.isFinite(lines) || lines <= 0) {
|
|
7084
|
+
console.error("\u043D\u0443\u0436\u043D\u043E \u0447\u0438\u0441\u043B\u043E \u0441\u0442\u0440\u043E\u043A: swarm logs -n 200");
|
|
7085
|
+
return 1;
|
|
7086
|
+
}
|
|
7087
|
+
const stray = argv.find((a, i) => /^\d+$/.test(a) && i !== nAt + 1);
|
|
7088
|
+
if (stray) {
|
|
7089
|
+
console.error(`\u043B\u043E\u0433 \u041F\u0420\u041E\u0413\u041E\u041D\u0410 \u2014 \u044D\u0442\u043E \xABswarm log ${stray}\xBB (\u0431\u0435\u0437 s). \xABswarm logs\xBB \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u043B\u043E\u0433 \u0434\u0435\u043C\u043E\u043D\u0430; \u0447\u0438\u0441\u043B\u043E \u0441\u0442\u0440\u043E\u043A \u2014 \xABswarm logs -n 200\xBB.`);
|
|
7090
|
+
return 1;
|
|
7091
|
+
}
|
|
7092
|
+
if (!existsSync9(DAEMON_LOG_PATH)) {
|
|
7093
|
+
console.error(`\u043B\u043E\u0433\u0430 \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435\u0442: ${DAEMON_LOG_PATH}`);
|
|
7094
|
+
console.error("\u043E\u043D \u043F\u0438\u0448\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u0444\u043E\u043D\u043E\u0432\u043E\u043C \u0437\u0430\u043F\u0443\u0441\u043A\u0435 (swarm start). \u0414\u0435\u043C\u043E\u043D \u043F\u043E\u0434\u043D\u044F\u0442 \u0432 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435 (--foreground) \u2014 \u0432\u044B\u0432\u043E\u0434 \u0438\u0434\u0451\u0442 \u0442\u0443\u0434\u0430.");
|
|
7095
|
+
return 1;
|
|
7096
|
+
}
|
|
7097
|
+
const head = tailFile(DAEMON_LOG_PATH, lines);
|
|
7098
|
+
if (head) console.log(head);
|
|
7099
|
+
if (!follow) return 0;
|
|
7100
|
+
let pos = statSync7(DAEMON_LOG_PATH).size;
|
|
7101
|
+
for (; ; ) {
|
|
7102
|
+
await sleep2(500);
|
|
7103
|
+
let size;
|
|
7104
|
+
try {
|
|
7105
|
+
size = statSync7(DAEMON_LOG_PATH).size;
|
|
7106
|
+
} catch {
|
|
7107
|
+
continue;
|
|
7108
|
+
}
|
|
7109
|
+
if (size < pos) pos = 0;
|
|
7110
|
+
if (size === pos) continue;
|
|
7111
|
+
const fd = openSync4(DAEMON_LOG_PATH, "r");
|
|
7112
|
+
try {
|
|
7113
|
+
const buf = Buffer.alloc(size - pos);
|
|
7114
|
+
const read = readSync2(fd, buf, 0, buf.length, pos);
|
|
7115
|
+
process.stdout.write(buf.subarray(0, read).toString("utf8"));
|
|
7116
|
+
pos += read;
|
|
7117
|
+
} finally {
|
|
7118
|
+
closeSync4(fd);
|
|
7119
|
+
}
|
|
7120
|
+
}
|
|
7121
|
+
}
|
|
7122
|
+
async function runStop(argv) {
|
|
7123
|
+
const force = argv.includes("--force");
|
|
7124
|
+
const wait = argv.includes("--wait");
|
|
7125
|
+
if (force && wait) {
|
|
7126
|
+
console.error("--wait \u0438 --force \u0432\u043C\u0435\u0441\u0442\u0435 \u043D\u0435 \u0438\u043C\u0435\u044E\u0442 \u0441\u043C\u044B\u0441\u043B\u0430: \u043B\u0438\u0431\u043E \u0436\u0434\u0451\u043C \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u0434\u0440\u043E\u043D\u043E\u0432, \u043B\u0438\u0431\u043E \u043E\u0431\u0440\u044B\u0432\u0430\u0435\u043C \u0438\u0445 \u0441\u0435\u0439\u0447\u0430\u0441");
|
|
7127
|
+
return 1;
|
|
7128
|
+
}
|
|
7129
|
+
const res = await daemonStop({ force, wait });
|
|
7130
|
+
if (!res) {
|
|
7131
|
+
const beat = readHeartbeat(HEARTBEAT_PATH);
|
|
7132
|
+
const freshBeat = beat != null && Date.now() - beat < 6e4;
|
|
7133
|
+
if (freshBeat) {
|
|
7134
|
+
console.error(
|
|
7135
|
+
`\u0434\u0435\u043C\u043E\u043D \u043F\u043E\u0434\u0430\u0451\u0442 \u043F\u0443\u043B\u044C\u0441 (${HEARTBEAT_PATH}), \u043D\u043E \u043C\u043E\u0440\u0434\u0430 127.0.0.1:${WEB_PORT} \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u2014 \u0441\u0430\u043C \u0441\u0435\u0431\u044F \u043F\u043E\u0433\u0430\u0441\u0438\u0442\u044C \u043E\u043D \u043D\u0435 \u043C\u043E\u0436\u0435\u0442. \u041F\u043E\u0445\u043E\u0436\u0435, \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0437\u0430\u0432\u0438\u0441: \u043D\u0430\u0439\u0434\u0438 \u0435\u0433\u043E (ps) \u0438 \u043F\u043E\u0433\u0430\u0441\u0438 \u0441\u0438\u0433\u043D\u0430\u043B\u043E\u043C \u0432\u0440\u0443\u0447\u043D\u0443\u044E.`
|
|
7136
|
+
);
|
|
7137
|
+
return 1;
|
|
7138
|
+
}
|
|
7139
|
+
console.log(`\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u0442 (\u043C\u043E\u0440\u0434\u0430 127.0.0.1:${WEB_PORT} \u043C\u043E\u043B\u0447\u0438\u0442) \u2014 \u0433\u0430\u0441\u0438\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E.`);
|
|
7140
|
+
return 0;
|
|
7141
|
+
}
|
|
7142
|
+
if (res.stopping) {
|
|
7143
|
+
if (res.sessions.length) {
|
|
7144
|
+
console.log(`\u0433\u0430\u0448\u0443 \u0434\u0435\u043C\u043E\u043D\u0430 \u041F\u0420\u0418\u041D\u0423\u0414\u0418\u0422\u0415\u041B\u042C\u041D\u041E \u2014 \u043E\u0431\u043E\u0440\u0432\u0430\u043D\u044B \u0436\u0438\u0432\u044B\u0435 \u0441\u0435\u0441\u0441\u0438\u0438 (${res.sessions.length}): ${await liveSessionNames(res.sessions)}`);
|
|
7145
|
+
console.log("\u0438\u0445 \u043D\u0435\u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043D\u0430\u044F \u0440\u0430\u0431\u043E\u0442\u0430 \u043F\u043E\u0442\u0435\u0440\u044F\u043D\u0430; \u043F\u043E\u0434\u043D\u0438\u043C\u0443\u0442\u0441\u044F \u0437\u0430\u043D\u043E\u0432\u043E \u043F\u043E\u0441\u043B\u0435 swarm start (--continue \u043F\u043E \u0441\u0432\u043E\u0435\u043C\u0443 worktree)");
|
|
7146
|
+
} else {
|
|
7147
|
+
console.log("\u0433\u0430\u0448\u0443 \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0436\u0438\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u043D\u0435 \u0431\u044B\u043B\u043E");
|
|
7148
|
+
}
|
|
7149
|
+
const down = await waitDaemonDown({ timeoutMs: 3e4, pollMs: 500 });
|
|
7150
|
+
console.log(down ? "\u0434\u0435\u043C\u043E\u043D \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D. \u041F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u0431\u0440\u0430\u0442\u043D\u043E: swarm start" : "\u0434\u0435\u043C\u043E\u043D \u043F\u0440\u0438\u043D\u044F\u043B \u043A\u043E\u043C\u0430\u043D\u0434\u0443, \u043D\u043E \u0435\u0449\u0451 \u043D\u0435 \u0432\u044B\u0448\u0435\u043B \u2014 \u043F\u0440\u043E\u0432\u0435\u0440\u044C `swarm status`");
|
|
7151
|
+
return down ? 0 : 1;
|
|
7152
|
+
}
|
|
7153
|
+
if (res.waiting) {
|
|
7154
|
+
console.log(`\u0434\u0435\u043C\u043E\u043D \u0433\u0430\u0441\u043D\u0435\u0442 \u041F\u041E\u0421\u041B\u0415 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u0436\u0438\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 (${res.sessions.length}): ${await liveSessionNames(res.sessions)}`);
|
|
7155
|
+
console.log("\u043D\u043E\u0432\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443 \u043E\u043D \u0443\u0436\u0435 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442. \u041F\u0440\u0435\u0440\u0432\u0430\u0442\u044C \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435 \u2014 \xAB\u041E\u0442\u043C\u0435\u043D\u0438\u0442\u044C \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435\xBB \u043D\u0430 \u043C\u043E\u0440\u0434\u0435 \u0434\u0435\u043C\u043E\u043D\u0430; \u043E\u0431\u043E\u0440\u0432\u0430\u0442\u044C \u0441\u0435\u0439\u0447\u0430\u0441 \u2014 swarm stop --force");
|
|
7156
|
+
const down = await waitDaemonDown({ timeoutMs: 6 * 60 * 6e4 });
|
|
7157
|
+
console.log(down ? "\u0434\u0435\u043C\u043E\u043D \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D. \u041F\u043E\u0434\u043D\u044F\u0442\u044C \u043E\u0431\u0440\u0430\u0442\u043D\u043E: swarm start" : "\u0436\u0434\u0443 \u0431\u043E\u043B\u044C\u0448\u0435 6 \u0447\u0430\u0441\u043E\u0432 \u2014 \u0431\u0440\u043E\u0441\u0430\u044E \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u0435, \u0434\u0435\u043C\u043E\u043D \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442 \u0433\u0430\u0441\u043D\u0443\u0442\u044C \u0441\u0430\u043C (swarm status \u043F\u043E\u043A\u0430\u0436\u0435\u0442)");
|
|
7158
|
+
return down ? 0 : 1;
|
|
7159
|
+
}
|
|
7160
|
+
console.error(`\u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 ${res.sessions.length} \u0436\u0438\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439: ${await liveSessionNames(res.sessions)}`);
|
|
7161
|
+
console.error(
|
|
7162
|
+
"\u043E\u043D\u0438 \u043F\u0438\u0448\u0443\u0442 \u043A\u043E\u0434 \u043F\u0440\u044F\u043C\u043E \u0441\u0435\u0439\u0447\u0430\u0441, \u0438 \u043D\u0435\u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043D\u043E\u0435 \u0443\u043C\u0440\u0451\u0442 \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u043D\u0438\u043C\u0438 \u2014 \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u043D\u0435 \u0433\u0430\u0448\u0443.\n swarm stop --wait \u0434\u043E\u0436\u0434\u0430\u0442\u044C\u0441\u044F, \u043F\u043E\u043A\u0430 \u0434\u043E\u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0442 (\u043D\u043E\u0432\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443 \u0440\u043E\u0439 \u0443\u0436\u0435 \u043D\u0435 \u0431\u0435\u0440\u0451\u0442)\n swarm stop --force \u043F\u043E\u0433\u0430\u0441\u0438\u0442\u044C \u0441\u0435\u0439\u0447\u0430\u0441, \u043E\u0431\u043E\u0440\u0432\u0430\u0432 \u0438\u0445\n swarm pause \u043F\u0440\u043E\u0441\u0442\u043E \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0442\u044C \u0431\u0440\u0430\u0442\u044C \u043D\u043E\u0432\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443, \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0435 \u0433\u0430\u0441\u0438\u0442\u044C"
|
|
7163
|
+
);
|
|
7164
|
+
return 1;
|
|
7165
|
+
}
|
|
7166
|
+
async function runRemove(argv) {
|
|
7167
|
+
const yes = argv.includes("--yes") || argv.includes("-y");
|
|
7168
|
+
if (!yes) {
|
|
7169
|
+
if (!process.stdin.isTTY) {
|
|
7170
|
+
console.error("swarm remove \u043E\u0442\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043C\u0430\u0448\u0438\u043D\u0443 \u043E\u0442 \u0440\u043E\u044F (\u0433\u0430\u0441\u0438\u0442 \u0434\u0435\u043C\u043E\u043D\u0430, \u0443\u0431\u0438\u0440\u0430\u0435\u0442 \u0435\u0451 \u0438\u0437 \u043F\u0430\u0440\u043A\u0430, \u0441\u0442\u0438\u0440\u0430\u0435\u0442 \u0442\u043E\u043A\u0435\u043D\u044B). \u041D\u0435\u0438\u043D\u0442\u0435\u0440\u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u0437\u0430\u043F\u0443\u0441\u043A \u2014 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438 \u044F\u0432\u043D\u043E: swarm remove --yes");
|
|
7171
|
+
return 1;
|
|
7172
|
+
}
|
|
7173
|
+
const answer = (await askTty(`\u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043C\u0430\u0448\u0438\u043D\u0443 \xAB${thisHost.hostName}\xBB \u043E\u0442 \u0440\u043E\u044F? \u0414\u0435\u043C\u043E\u043D \u0431\u0443\u0434\u0435\u0442 \u043F\u043E\u0433\u0430\u0448\u0435\u043D, \u043C\u0430\u0448\u0438\u043D\u0430 \u0438\u0441\u0447\u0435\u0437\u043D\u0435\u0442 \u0438\u0437 \u043F\u0430\u0440\u043A\u0430, \u0442\u043E\u043A\u0435\u043D\u044B \u0441\u0442\u0451\u0440\u0442\u044B [y/N]: `)).trim().toLowerCase();
|
|
7174
|
+
if (answer !== "y" && answer !== "yes" && answer !== "\u0434" && answer !== "\u0434\u0430") {
|
|
7175
|
+
console.log("\u043E\u0442\u043C\u0435\u043D\u0435\u043D\u043E \u2014 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0442\u0440\u043E\u043D\u0443\u0442\u043E");
|
|
7176
|
+
return 1;
|
|
7177
|
+
}
|
|
7178
|
+
}
|
|
7179
|
+
const stopCode = await runStop(argv.filter((a) => a !== "--yes" && a !== "-y"));
|
|
7180
|
+
if (stopCode !== 0) {
|
|
7181
|
+
console.error("\u043C\u0430\u0448\u0438\u043D\u0430 \u041D\u0415 \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u0430: \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u043D\u0443\u0436\u043D\u043E \u043F\u043E\u0433\u0430\u0441\u0438\u0442\u044C \u0434\u0435\u043C\u043E\u043D\u0430 (\u0441\u043C. \u0432\u044B\u0448\u0435) \u2014 \u0442\u043E\u043A\u0435\u043D\u044B \u0438 \u043F\u0430\u0440\u043A \u043D\u0435 \u0442\u0440\u043E\u043D\u0443\u0442\u044B");
|
|
7182
|
+
return stopCode;
|
|
7183
|
+
}
|
|
7184
|
+
if (SWARM_BACKEND !== "client") {
|
|
7185
|
+
console.log("\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 sqlite-\u0431\u044D\u043A\u0435\u043D\u0434: \u043F\u0430\u0440\u043A\u0430 \u043C\u0430\u0448\u0438\u043D \u043D\u0435\u0442 \u2014 \u0443\u0431\u0438\u0440\u0430\u0442\u044C \u0438\u0437 \u043D\u0435\u0433\u043E \u043D\u0435\u0447\u0435\u0433\u043E, \u0447\u0438\u0449\u0443 \u0442\u043E\u043B\u044C\u043A\u043E \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435");
|
|
7186
|
+
} else {
|
|
7187
|
+
const db2 = await makeCliStore(SWARM_REPO);
|
|
7188
|
+
try {
|
|
7189
|
+
if (db2 instanceof SwarmClient) {
|
|
7190
|
+
const res = await db2.forgetHost();
|
|
7191
|
+
console.log(
|
|
7192
|
+
res.removed ? `\u043C\u0430\u0448\u0438\u043D\u0430 \u0443\u0431\u0440\u0430\u043D\u0430 \u0438\u0437 \u043F\u0430\u0440\u043A\u0430${res.released ? ` \u2014 \u043E\u0441\u0432\u043E\u0431\u043E\u0436\u0434\u0435\u043D\u043E \u0435\u0451 \u043D\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D\u043D\u044B\u0445 \u043F\u0440\u043E\u0433\u043E\u043D\u043E\u0432: ${res.released}` : ""}` : "\u043C\u0430\u0448\u0438\u043D\u044B \u0432 \u043F\u0430\u0440\u043A\u0435 \u0438 \u043D\u0435 \u0431\u044B\u043B\u043E (\u043F\u0443\u043B\u044C\u0441 \u043D\u0438 \u0440\u0430\u0437\u0443 \u043D\u0435 \u0434\u043E\u0445\u043E\u0434\u0438\u043B) \u2014 \u0443\u0431\u0438\u0440\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E"
|
|
7193
|
+
);
|
|
7194
|
+
}
|
|
7195
|
+
} catch (e) {
|
|
7196
|
+
console.error(`\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0443\u0431\u0440\u0430\u0442\u044C \u043C\u0430\u0448\u0438\u043D\u0443 \u0438\u0437 \u043F\u0430\u0440\u043A\u0430: ${e instanceof Error ? e.message : e}`);
|
|
7197
|
+
console.error("\u0442\u043E\u043A\u0435\u043D\u044B \u041D\u0415 \u0441\u0442\u0451\u0440\u0442\u044B \u2014 \u0438\u043D\u0430\u0447\u0435 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C \u0431\u044B\u043B\u043E \u0431\u044B \u043D\u0435\u0447\u0435\u043C. \u041F\u0440\u043E\u0432\u0435\u0440\u044C \u0441\u0432\u044F\u0437\u044C \u0441 \u043F\u0440\u043E\u0434\u043E\u043C \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438: swarm remove --yes");
|
|
7198
|
+
db2.close();
|
|
7199
|
+
return 1;
|
|
7200
|
+
}
|
|
7201
|
+
db2.close();
|
|
7202
|
+
}
|
|
7203
|
+
clearAgentToken();
|
|
7204
|
+
clearStore();
|
|
7205
|
+
clearWorkspace();
|
|
7206
|
+
try {
|
|
7207
|
+
savePauseState(SWARM_HOME, { paused: false, at: "", source: "cli" });
|
|
7208
|
+
} catch {
|
|
7209
|
+
}
|
|
7210
|
+
console.log("\u043C\u0430\u0448\u0438\u043D\u0430 \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u0430 \u043E\u0442 \u0440\u043E\u044F: \u0434\u0435\u043C\u043E\u043D \u043F\u043E\u0433\u0430\u0448\u0435\u043D, \u0442\u043E\u043A\u0435\u043D\u044B (agent.json/oauth.json/workspace.json) \u0441\u0442\u0451\u0440\u0442\u044B.");
|
|
7211
|
+
console.log("\u043D\u0430\u043F\u043E\u043C\u0438\u043D\u0430\u043D\u0438\u0435: \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u0435 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u0435 \u0442\u043E\u043A\u0435\u043D\u0430 \u043D\u0435 \u043E\u0442\u0437\u044B\u0432\u0430\u0435\u0442 \u0435\u0433\u043E \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u2014 \u043E\u0442\u0437\u044B\u0432 \u0432 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430, \u041F\u0440\u043E\u0444\u0438\u043B\u044C \u2192 \u0420\u043E\u0439.");
|
|
7212
|
+
console.log("\u0432\u0435\u0440\u043D\u0443\u0442\u044C \u043C\u0430\u0448\u0438\u043D\u0443 \u0432 \u0440\u043E\u0439: swarm login && swarm start");
|
|
7213
|
+
return 0;
|
|
7214
|
+
}
|
|
6736
7215
|
var STATUS_MARK = {
|
|
6737
7216
|
queued: "\xB7",
|
|
6738
7217
|
running: "\u25B6",
|
|
@@ -6784,27 +7263,62 @@ function printRuns(runs, view) {
|
|
|
6784
7263
|
}).forEach((l) => console.log(l));
|
|
6785
7264
|
}
|
|
6786
7265
|
function usage() {
|
|
6787
|
-
console.log(`swarm \u2014 \u0440\u043E\u0439
|
|
7266
|
+
console.log(`swarm \u2014 \u0440\u043E\u0439 AI-\u0434\u0440\u043E\u043D\u043E\u0432 (v${PACKAGE_VERSION2})
|
|
6788
7267
|
|
|
6789
|
-
|
|
6790
|
-
swarm
|
|
6791
|
-
|
|
6792
|
-
swarm
|
|
6793
|
-
|
|
6794
|
-
swarm
|
|
6795
|
-
|
|
7268
|
+
\u041D\u0410\u0427\u0410\u0422\u042C
|
|
7269
|
+
swarm login [--workspace <slug|id>] [--no-open]
|
|
7270
|
+
\u0432\u043E\u0439\u0442\u0438 \u0432 Taskless \u0438 \u0432\u044B\u0431\u0440\u0430\u0442\u044C \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441
|
|
7271
|
+
swarm start [--repo <path>] [--foreground]
|
|
7272
|
+
\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
|
|
7273
|
+
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
|
|
7274
|
+
|
|
7275
|
+
\u0414\u0410\u0422\u042C \u0420\u041E\u042E \u0420\u0410\u0411\u041E\u0422\u0423
|
|
7276
|
+
swarm spawn <slug> [area] [\u0444\u043B\u0430\u0433\u0438 \u043D\u0438\u0436\u0435]
|
|
7277
|
+
\u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u044C
|
|
7278
|
+
swarm ls [\u0441\u0442\u0430\u0442\u0443\u0441] \u043F\u0440\u043E\u0433\u043E\u043D\u044B \u0436\u0438\u0432\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430 \u2014 \u0440\u043E\u0432\u043D\u043E \u0442\u0435, \u0447\u0442\u043E \u0432 \u0434\u0430\u0448\u0431\u043E\u0440\u0434\u0435
|
|
7279
|
+
swarm wake <runId> \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D (id \u0438\u0437 swarm ls); \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E \u2192 \u043D\u0430\u0447\u043D\u0451\u0442 \u0441 \u043D\u0443\u043B\u044F
|
|
7280
|
+
swarm cancel <slug> <\u043F\u0440\u0438\u0447\u0438\u043D\u0430\u2026>
|
|
7281
|
+
\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0434\u0440\u043E\u043D\u0430: \u0441\u0442\u0430\u0442\u0443\u0441 \xAB\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\xBB \u0441 \u043F\u0440\u0438\u0447\u0438\u043D\u043E\u0439, \u0432 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u043E\u0441\u0442\u0430\u043D\u0435\u0442\u0441\u044F
|
|
6796
7282
|
swarm questions \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0434\u0440\u043E\u043D\u043E\u0432
|
|
6797
|
-
swarm answer <qid>
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
swarm
|
|
7283
|
+
swarm answer <qid> <\u0442\u0435\u043A\u0441\u0442\u2026>
|
|
7284
|
+
\u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043D\u0430 \u0432\u043E\u043F\u0440\u043E\u0441 \u0438 \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442\u044C \u0434\u0440\u043E\u043D\u0430
|
|
7285
|
+
|
|
7286
|
+
\u0423\u041F\u0420\u0410\u0412\u041B\u042F\u0422\u042C \u041C\u0410\u0428\u0418\u041D\u041E\u0419
|
|
7287
|
+
swarm pause [\u043F\u0440\u0438\u0447\u0438\u043D\u0430\u2026] \u043D\u0435 \u0431\u0440\u0430\u0442\u044C \u043D\u043E\u0432\u0443\u044E \u0440\u0430\u0431\u043E\u0442\u0443; \u0436\u0438\u0432\u044B\u0435 \u0434\u0440\u043E\u043D\u044B \u0434\u043E\u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0442. \u041F\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0435\u0441\u0442\u0430\u0440\u0442
|
|
7288
|
+
swarm resume \u0441\u043D\u043E\u0432\u0430 \u0431\u0440\u0430\u0442\u044C \u0440\u0430\u0431\u043E\u0442\u0443
|
|
7289
|
+
swarm stop [--wait|--force]
|
|
7290
|
+
\u043F\u043E\u0433\u0430\u0441\u0438\u0442\u044C \u0434\u0435\u043C\u043E\u043D\u0430 \u043D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435
|
|
7291
|
+
swarm remove [--yes] [--wait|--force]
|
|
7292
|
+
\u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043C\u0430\u0448\u0438\u043D\u0443 \u043E\u0442 \u0440\u043E\u044F: \u043F\u043E\u0433\u0430\u0441\u0438\u0442\u044C, \u0443\u0431\u0440\u0430\u0442\u044C \u0438\u0437 \u043F\u0430\u0440\u043A\u0430, \u0441\u0442\u0435\u0440\u0435\u0442\u044C \u0442\u043E\u043A\u0435\u043D\u044B
|
|
7293
|
+
swarm hosts \u043C\u0430\u0448\u0438\u043D\u044B \u0440\u043E\u044F: \u0438\u043C\u044F, \u043F\u0443\u043B\u044C\u0441, \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0438, id (\u0438\u043C\u044F \u0433\u043E\u0434\u0438\u0442\u0441\u044F \u0434\u043B\u044F --host)
|
|
7294
|
+
|
|
7295
|
+
\u0421\u041C\u041E\u0422\u0420\u0415\u0422\u042C, \u0427\u0422\u041E \u041F\u0420\u041E\u0418\u0421\u0425\u041E\u0414\u0418\u0422
|
|
7296
|
+
swarm logs [-n 200] [-f] \u043B\u043E\u0433 \u0414\u0415\u041C\u041E\u041D\u0410 (~/.taskless-swarm/overmind.log)
|
|
7297
|
+
swarm log <runId> \u043B\u043E\u0433 \u043E\u0434\u043D\u043E\u0433\u043E \u041F\u0420\u041E\u0413\u041E\u041D\u0410 \u2014 \u043D\u0435 \u043F\u0443\u0442\u0430\u0442\u044C \u0441 \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0435\u0439
|
|
7298
|
+
swarm web \u0432\u0435\u0431-\u043C\u043E\u0440\u0434\u0430 \u0440\u043E\u044F \u043D\u0430 127.0.0.1:${WEB_PORT}
|
|
7299
|
+
|
|
7300
|
+
\u041F\u0420\u041E\u0427\u0415\u0415
|
|
6802
7301
|
swarm whoami \u043A\u0442\u043E \u0432\u043E\u0448\u0451\u043B \u0438 \u0432 \u043A\u0430\u043A\u043E\u043C \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0435
|
|
6803
|
-
swarm
|
|
7302
|
+
swarm logout \u0437\u0430\u0431\u044B\u0442\u044C \u0442\u043E\u043A\u0435\u043D\u044B \u0438 \u0432\u044B\u0431\u043E\u0440 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0430
|
|
6804
7303
|
swarm shot <url> <out-prefix> [--timeout <ms>] [--storage-state <path>]
|
|
6805
|
-
headless-\u0441\u043A\u0440\u0438\u043D desktop+mobile \
|
|
7304
|
+
headless-\u0441\u043A\u0440\u0438\u043D\u044B desktop+mobile \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439:
|
|
6806
7305
|
<out-prefix>-desktop.png, <out-prefix>-mobile.png
|
|
7306
|
+
swarm import-db <path> --repo <path>
|
|
7307
|
+
\u0438\u043C\u043F\u043E\u0440\u0442 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u043F\u0440\u043E\u0433\u043E\u043D\u043E\u0432 \u0438\u0437 \u0434\u0440\u0443\u0433\u043E\u0439 \u0411\u0414 (\u0438\u0434\u0435\u043C\u043F\u043E\u0442\u0435\u043D\u0442\u043D\u043E)
|
|
6807
7308
|
swarm --version \u0432\u0435\u0440\u0441\u0438\u044F \u043F\u0430\u043A\u0435\u0442\u0430
|
|
7309
|
+
|
|
7310
|
+
\u0424\u041B\u0410\u0413\u0418, \u041A\u041E\u0422\u041E\u0420\u042B\u0415 \u0421\u0422\u041E\u0418\u0422 \u0417\u041D\u0410\u0422\u042C
|
|
7311
|
+
start \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u0434\u0435\u043C\u043E\u043D \u041E\u0422\u041A\u0420\u0415\u041F\u041B\u042F\u0415\u0422\u0421\u042F: \u0443\u0445\u043E\u0434\u0438\u0442 \u0432 \u0444\u043E\u043D, \u043F\u0438\u0448\u0435\u0442 \u0432 ~/.taskless-swarm/overmind.log,
|
|
7312
|
+
\u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B \u0441\u0432\u043E\u0431\u043E\u0434\u0435\u043D. --foreground (\u043E\u043D \u0436\u0435 -f, --attach) \u0434\u0435\u0440\u0436\u0438\u0442 \u0435\u0433\u043E \u0432 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435 \u2014
|
|
7313
|
+
\u0434\u043B\u044F \u043E\u0442\u043B\u0430\u0434\u043A\u0438 \u0438 \u0442\u0430\u043C, \u0433\u0434\u0435 \u0444\u043E\u043D\u043E\u043C \u0437\u0430\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0441\u0443\u043F\u0435\u0440\u0432\u0438\u0437\u043E\u0440 (systemd, docker)
|
|
7314
|
+
spawn --repo <path> \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u0439 \u043F\u0440\u043E\u0433\u043E\u043D\u0430, \u0435\u0441\u043B\u0438 \u043D\u0435 \u0434\u0435\u0444\u043E\u043B\u0442\u043D\u044B\u0439
|
|
7315
|
+
--after <s1,s2> \u0436\u0434\u0430\u0442\u044C, \u043F\u043E\u043A\u0430 \u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u0435\u043D\u043D\u044B\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043D\u0435 \u0431\u0443\u0434\u0443\u0442 done
|
|
7316
|
+
--priority <n> \u0432\u044B\u0448\u0435 \u0447\u0438\u0441\u043B\u043E \u2014 \u0440\u0430\u043D\u044C\u0448\u0435 \u0432\u043E\u0437\u044C\u043C\u0443\u0442 \u0438\u0437 \u043E\u0447\u0435\u0440\u0435\u0434\u0438
|
|
7317
|
+
--base <\u0432\u0435\u0442\u043A\u0430> PR \u043D\u0435 \u0432 \u0434\u0435\u0444\u043E\u043B\u0442\u043D\u0443\u044E \u0432\u0435\u0442\u043A\u0443, \u0430 \u0432 \u044D\u0442\u0443
|
|
7318
|
+
--host <\u043C\u0430\u0448\u0438\u043D\u0430|any> \u043D\u0430 \u043A\u0430\u043A\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C; \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E \u044D\u0442\u0430, \xABany\xBB \u2014 \u043B\u044E\u0431\u0430\u044F \u0441\u0432\u043E\u0431\u043E\u0434\u043D\u0430\u044F
|
|
7319
|
+
--hero [--name HYDRA] \u0413\u0435\u0440\u043E\u0439-\u043B\u0435\u0439\u0442\u0435\u043D\u0430\u043D\u0442 \u044D\u043F\u0438\u043A\u0430 \u0432\u043C\u0435\u0441\u0442\u043E \u043E\u0431\u044B\u0447\u043D\u043E\u0433\u043E \u0434\u0440\u043E\u043D\u0430
|
|
7320
|
+
stop \u0431\u0435\u0437 \u0444\u043B\u0430\u0433\u043E\u0432 \u043F\u0440\u0438 \u0436\u0438\u0432\u044B\u0445 \u0434\u0440\u043E\u043D\u0430\u0445 \u041D\u0415 \u0433\u0430\u0441\u0438\u0442, \u0430 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442, \u043A\u0442\u043E \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442: \u0438\u0445 \u043D\u0435\u0437\u0430\u043F\u0443\u0448\u0435\u043D\u043D\u043E\u0435
|
|
7321
|
+
\u0443\u043C\u0440\u0451\u0442 \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u043D\u0438\u043C\u0438. --wait \u2014 \u0434\u043E\u0436\u0434\u0430\u0442\u044C\u0441\u044F, \u043F\u043E\u043A\u0430 \u0434\u043E\u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0442; --force \u2014 \u043E\u0431\u043E\u0440\u0432\u0430\u0442\u044C \u0441\u0435\u0439\u0447\u0430\u0441
|
|
6808
7322
|
`);
|
|
6809
7323
|
}
|
|
6810
7324
|
var effectiveWorktreesDirMemo;
|
|
@@ -6814,7 +7328,7 @@ function effectiveWorktreesDir() {
|
|
|
6814
7328
|
legacyDir: WORKTREES_LEGACY_DIR,
|
|
6815
7329
|
targetDir: WORKTREES_DIR,
|
|
6816
7330
|
envOverride: process.env.SWARM_WORKTREES || "",
|
|
6817
|
-
exists: (d) =>
|
|
7331
|
+
exists: (d) => existsSync9(d),
|
|
6818
7332
|
isNonEmptyDir: (d) => {
|
|
6819
7333
|
try {
|
|
6820
7334
|
return readdirSync6(d).length > 0;
|
|
@@ -6847,7 +7361,7 @@ async function finishClient(db2) {
|
|
|
6847
7361
|
}
|
|
6848
7362
|
var [cmd, ...rawArgs] = process.argv.slice(2);
|
|
6849
7363
|
if (cmd === "--version" || cmd === "-v") {
|
|
6850
|
-
console.log(
|
|
7364
|
+
console.log(PACKAGE_VERSION2);
|
|
6851
7365
|
process.exit(0);
|
|
6852
7366
|
}
|
|
6853
7367
|
if (cmd === "logout") {
|
|
@@ -6894,14 +7408,18 @@ if (cmd === "import-db") {
|
|
|
6894
7408
|
process.exit(0);
|
|
6895
7409
|
}
|
|
6896
7410
|
if (cmd === "start") {
|
|
7411
|
+
const { foreground, rest: startArgs } = parseStartMode(rawArgs);
|
|
6897
7412
|
if (SWARM_BACKEND !== "client") {
|
|
6898
7413
|
console.warn("SWARM_BACKEND=client \u043D\u0435 \u0437\u0430\u0434\u0430\u043D \u2014 \u0434\u0435\u043C\u043E\u043D \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F \u0432 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u043C sqlite-\u0440\u0435\u0436\u0438\u043C\u0435, \u0411\u0415\u0417 \u0441\u0432\u044F\u0437\u0438 \u0441 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u043E\u043C Taskless (\u043D\u0435 \u0443\u0432\u0438\u0434\u0438\u0442 \u0437\u0430\u0434\u0430\u0447\u0438, \u043D\u0435 \u043F\u043E\u0448\u043B\u0451\u0442 \u043F\u0443\u043B\u044C\u0441).");
|
|
6899
7414
|
}
|
|
6900
|
-
const { repo: repoFlag } = takeRepo(
|
|
7415
|
+
const { repo: repoFlag } = takeRepo(startArgs);
|
|
6901
7416
|
const repo = repoFlag ?? SWARM_REPO;
|
|
6902
7417
|
if (!repo) {
|
|
6903
7418
|
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).");
|
|
6904
7419
|
}
|
|
7420
|
+
if (!foreground) {
|
|
7421
|
+
process.exit(await runStartDetached(startArgs));
|
|
7422
|
+
}
|
|
6905
7423
|
try {
|
|
6906
7424
|
await runDaemon({ repo });
|
|
6907
7425
|
} catch (e) {
|
|
@@ -6910,6 +7428,9 @@ if (cmd === "start") {
|
|
|
6910
7428
|
}
|
|
6911
7429
|
process.exit(0);
|
|
6912
7430
|
}
|
|
7431
|
+
if (cmd === "logs") {
|
|
7432
|
+
process.exit(await runDaemonLogs(rawArgs));
|
|
7433
|
+
}
|
|
6913
7434
|
if (cmd === "ls") {
|
|
6914
7435
|
const snap = await daemonRuns(rawArgs[0]);
|
|
6915
7436
|
if (snap) {
|
|
@@ -6946,6 +7467,54 @@ if (cmd === "cancel") {
|
|
|
6946
7467
|
}
|
|
6947
7468
|
console.warn(NO_DAEMON_NOTE);
|
|
6948
7469
|
}
|
|
7470
|
+
if (cmd === "status") {
|
|
7471
|
+
const st = await daemonStatus2();
|
|
7472
|
+
if (!st) {
|
|
7473
|
+
const local = loadPauseState(SWARM_HOME);
|
|
7474
|
+
console.log(`\u0434\u0435\u043C\u043E\u043D \u041D\u0415 \u041F\u041E\u0414\u041D\u042F\u0422 (\u043C\u043E\u0440\u0434\u0430 127.0.0.1:${WEB_PORT} \u043C\u043E\u043B\u0447\u0438\u0442). \u041F\u043E\u0434\u043D\u044F\u0442\u044C: swarm start`);
|
|
7475
|
+
console.log(local.paused ? `${pauseText(local)} \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F \u0423\u0416\u0415 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435, \u0441\u043D\u044F\u0442\u044C: swarm resume` : "\u043F\u0430\u0443\u0437\u044B \u043D\u0435\u0442 \u2014 \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F \u0432 \u0440\u0430\u0431\u043E\u0442\u0443");
|
|
7476
|
+
process.exit(1);
|
|
7477
|
+
}
|
|
7478
|
+
console.log(`\u0434\u0435\u043C\u043E\u043D \u0436\u0438\u0432 (v${st.version})${st.hostName ? `, \u043C\u0430\u0448\u0438\u043D\u0430 \xAB${st.hostName}\xBB` : ""}, \u0431\u044D\u043A\u0435\u043D\u0434 ${st.backend}`);
|
|
7479
|
+
console.log(pauseText(st.pause ?? (st.paused ? { paused: true, at: "", source: "web" } : null)));
|
|
7480
|
+
if (st.restart?.phase === "waiting") {
|
|
7481
|
+
console.log(
|
|
7482
|
+
`\u0434\u0435\u043C\u043E\u043D \u0416\u0414\u0401\u0422 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F \u0436\u0438\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 (${st.restart.sessions.length}), \u0447\u0442\u043E\u0431\u044B ${st.restart.intent === "stop" ? "\u043F\u043E\u0433\u0430\u0441\u043D\u0443\u0442\u044C" : "\u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C\u0441\u044F"} \u2014 \u043F\u0440\u0438\u0451\u043C \u043D\u043E\u0432\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u044B \u043F\u0440\u0438\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D \u043D\u0430 \u044D\u0442\u043E \u0432\u0440\u0435\u043C\u044F`
|
|
7483
|
+
);
|
|
7484
|
+
}
|
|
7485
|
+
const counts = Object.entries(st.counts).filter(([, n]) => n > 0);
|
|
7486
|
+
console.log(counts.length ? `\u043F\u0440\u043E\u0433\u043E\u043D\u044B: ${counts.map(([s, n]) => `${s} ${n}`).join(", ")}` : "\u043F\u0440\u043E\u0433\u043E\u043D\u043E\u0432 \u043D\u0435\u0442");
|
|
7487
|
+
console.log(st.live.length ? `\u0436\u0438\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u0441\u0435\u0439\u0447\u0430\u0441: ${st.live.length} (#${st.live.join(", #")})` : "\u0436\u0438\u0432\u044B\u0445 \u0441\u0435\u0441\u0441\u0438\u0439 \u043D\u0435\u0442");
|
|
7488
|
+
process.exit(0);
|
|
7489
|
+
}
|
|
7490
|
+
if (cmd === "pause" || cmd === "resume") {
|
|
7491
|
+
const wantPause = cmd === "pause";
|
|
7492
|
+
const reason = wantPause ? rawArgs.join(" ").trim() || null : null;
|
|
7493
|
+
const verdict = wantPause ? await daemonPause(reason) : await daemonResume();
|
|
7494
|
+
if (verdict) {
|
|
7495
|
+
console.log(verdict.ok ? verdict.why : verdict.why);
|
|
7496
|
+
if (verdict.ok && wantPause) console.log("\u043F\u0430\u0443\u0437\u0430 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u0430 \u043D\u0430 \u0434\u0438\u0441\u043A \u2014 \u043F\u0435\u0440\u0435\u0436\u0438\u0432\u0451\u0442 \u0440\u0435\u0441\u0442\u0430\u0440\u0442 \u0434\u0435\u043C\u043E\u043D\u0430 \u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0443 \u043C\u0430\u0448\u0438\u043D\u044B");
|
|
7497
|
+
process.exit(verdict.ok ? 0 : 1);
|
|
7498
|
+
}
|
|
7499
|
+
try {
|
|
7500
|
+
savePauseState(SWARM_HOME, wantPause ? makePauseState("cli", reason) : { paused: false, at: "", source: "cli" });
|
|
7501
|
+
} catch (e) {
|
|
7502
|
+
console.error(`\u043D\u0435 \u0441\u043C\u043E\u0433 \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043F\u0430\u0443\u0437\u044B: ${e instanceof Error ? e.message : e}`);
|
|
7503
|
+
process.exit(1);
|
|
7504
|
+
}
|
|
7505
|
+
console.log(
|
|
7506
|
+
wantPause ? `\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u2014 \u0437\u0430\u043F\u0438\u0441\u0430\u043B \u043F\u0430\u0443\u0437\u0443 \u043D\u0430 \u0434\u0438\u0441\u043A: \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F (swarm start) \u0423\u0416\u0415 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435. \u0421\u043D\u044F\u0442\u044C: swarm resume` : "\u0434\u0435\u043C\u043E\u043D \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u2014 \u0441\u043D\u044F\u043B \u043F\u0430\u0443\u0437\u0443 \u043D\u0430 \u0434\u0438\u0441\u043A\u0435: \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 swarm start \u043F\u043E\u0434\u043D\u0438\u043C\u0435\u0442\u0441\u044F \u0432 \u0440\u0430\u0431\u043E\u0442\u0443"
|
|
7507
|
+
);
|
|
7508
|
+
process.exit(0);
|
|
7509
|
+
}
|
|
7510
|
+
if (cmd === "stop") {
|
|
7511
|
+
const code = await runStop(rawArgs);
|
|
7512
|
+
process.exit(code);
|
|
7513
|
+
}
|
|
7514
|
+
if (cmd === "remove") {
|
|
7515
|
+
const code = await runRemove(rawArgs);
|
|
7516
|
+
process.exit(code);
|
|
7517
|
+
}
|
|
6949
7518
|
if (cmd === "shot") {
|
|
6950
7519
|
const code = await runShotCli(rawArgs);
|
|
6951
7520
|
process.exit(code);
|