@taskless-app/swarm 0.4.1 → 0.4.2
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 +29 -0
- package/dist/cli.js +436 -74
- 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 } 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
|
/**
|
|
@@ -3793,6 +3850,64 @@ async function collectRunDiff(worktree, opts = {}) {
|
|
|
3793
3850
|
};
|
|
3794
3851
|
}
|
|
3795
3852
|
|
|
3853
|
+
// package.json
|
|
3854
|
+
var package_default = {
|
|
3855
|
+
name: "@taskless-app/swarm",
|
|
3856
|
+
version: "0.4.2",
|
|
3857
|
+
type: "module",
|
|
3858
|
+
description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
|
|
3859
|
+
keywords: [
|
|
3860
|
+
"ai-agents",
|
|
3861
|
+
"coding-agents",
|
|
3862
|
+
"swarm",
|
|
3863
|
+
"orchestration",
|
|
3864
|
+
"autonomous-agents",
|
|
3865
|
+
"mcp",
|
|
3866
|
+
"taskless",
|
|
3867
|
+
"cli"
|
|
3868
|
+
],
|
|
3869
|
+
license: "SEE LICENSE IN LICENSE",
|
|
3870
|
+
author: "Ilya Smurov <ilya@smurov.com>",
|
|
3871
|
+
homepage: "https://github.com/ilyasmurov/taskless/tree/main/packages/swarm-daemon#readme",
|
|
3872
|
+
repository: {
|
|
3873
|
+
type: "git",
|
|
3874
|
+
url: "git+https://github.com/ilyasmurov/taskless.git",
|
|
3875
|
+
directory: "packages/swarm-daemon"
|
|
3876
|
+
},
|
|
3877
|
+
engines: {
|
|
3878
|
+
node: ">=22"
|
|
3879
|
+
},
|
|
3880
|
+
bin: {
|
|
3881
|
+
swarm: "dist/cli.js"
|
|
3882
|
+
},
|
|
3883
|
+
files: [
|
|
3884
|
+
"dist",
|
|
3885
|
+
"docs/HERO.md",
|
|
3886
|
+
"roles"
|
|
3887
|
+
],
|
|
3888
|
+
scripts: {
|
|
3889
|
+
overmind: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/overmind.ts',
|
|
3890
|
+
swarm: 'NODE_OPTIONS="${NODE_OPTIONS:-} --conditions=development" tsx src/cli.ts',
|
|
3891
|
+
build: "tsup",
|
|
3892
|
+
start: "node dist/overmind.js",
|
|
3893
|
+
prepublishOnly: "pnpm run build",
|
|
3894
|
+
test: "vitest run && SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
|
|
3895
|
+
"test:client": "SWARM_TEST_BACKEND=client vitest run src/overmind.test.ts src/overmind.storm.test.ts src/db.storm.test.ts",
|
|
3896
|
+
"test:watch": "vitest",
|
|
3897
|
+
typecheck: "tsc -p tsconfig.json --noEmit",
|
|
3898
|
+
lint: "echo 'no lint in swarm-daemon yet'"
|
|
3899
|
+
},
|
|
3900
|
+
dependencies: {
|
|
3901
|
+
"@hono/node-server": "^2.0.12",
|
|
3902
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
3903
|
+
execa: "^10.0.0",
|
|
3904
|
+
zod: "3.25.76"
|
|
3905
|
+
},
|
|
3906
|
+
optionalDependencies: {
|
|
3907
|
+
"better-sqlite3": "^12.11.1"
|
|
3908
|
+
}
|
|
3909
|
+
};
|
|
3910
|
+
|
|
3796
3911
|
// src/gh.ts
|
|
3797
3912
|
import { $ as $3 } from "execa";
|
|
3798
3913
|
var NO_CHECKS_STDERR = /no checks reported/i;
|
|
@@ -4022,6 +4137,7 @@ async function ensureAccess(store, opts = {}) {
|
|
|
4022
4137
|
}
|
|
4023
4138
|
|
|
4024
4139
|
// src/web.ts
|
|
4140
|
+
var PACKAGE_VERSION = package_default.version;
|
|
4025
4141
|
var esc = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
4026
4142
|
var jsonForScript = (v) => JSON.stringify(v).replace(/</g, "\\u003c");
|
|
4027
4143
|
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 +5418,36 @@ async function runDiff(db2, id, now = Date.now()) {
|
|
|
5302
5418
|
}
|
|
5303
5419
|
var readNarratorLog = (log) => log?.() ?? [];
|
|
5304
5420
|
var readRestartSnap = (restart, liveSessionIds) => restart ? { phase: restart.phase, sessionIds: liveSessionIds?.() ?? [] } : null;
|
|
5421
|
+
function pauseSourceOf(raw) {
|
|
5422
|
+
return raw === "cli" || raw === "remote" ? raw : "web";
|
|
5423
|
+
}
|
|
5424
|
+
function setPausedVia(deps, paused, source, reason) {
|
|
5425
|
+
const control = deps.control;
|
|
5426
|
+
if (control.set) {
|
|
5427
|
+
control.set(paused, { source, reason });
|
|
5428
|
+
return;
|
|
5429
|
+
}
|
|
5430
|
+
control.paused = paused;
|
|
5431
|
+
deps.narrate?.(
|
|
5432
|
+
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."
|
|
5433
|
+
);
|
|
5434
|
+
}
|
|
5435
|
+
function daemonStatus(deps) {
|
|
5436
|
+
const counts = {};
|
|
5437
|
+
for (const r of deps.db.listRuns()) counts[r.status] = (counts[r.status] ?? 0) + 1;
|
|
5438
|
+
const live = deps.liveSessionIds?.() ?? [];
|
|
5439
|
+
return {
|
|
5440
|
+
hostId: deps.selfHost?.hostId ?? null,
|
|
5441
|
+
hostName: deps.selfHost?.hostName ?? null,
|
|
5442
|
+
backend: SWARM_BACKEND,
|
|
5443
|
+
paused: deps.control?.paused ?? false,
|
|
5444
|
+
pause: deps.control?.state ?? null,
|
|
5445
|
+
restart: deps.restart ? { phase: deps.restart.phase, intent: deps.restart.intent, sessions: live } : null,
|
|
5446
|
+
counts,
|
|
5447
|
+
live,
|
|
5448
|
+
version: PACKAGE_VERSION
|
|
5449
|
+
};
|
|
5450
|
+
}
|
|
5305
5451
|
function eventsStream(db2, narratorLog, control, daemonVersion, restart, liveSessionIds, selfHost) {
|
|
5306
5452
|
let iv;
|
|
5307
5453
|
let last = "";
|
|
@@ -5470,18 +5616,25 @@ function makeHandler(deps) {
|
|
|
5470
5616
|
return redirect("/");
|
|
5471
5617
|
}
|
|
5472
5618
|
if (p === "/pause" && req.method === "POST") {
|
|
5473
|
-
if (!
|
|
5619
|
+
if (!authed(req)) return unauthorized();
|
|
5474
5620
|
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
|
-
|
|
5621
|
+
const f = await formValue(req);
|
|
5622
|
+
const reason = (f.get("reason") ?? "").trim() || null;
|
|
5623
|
+
setPausedVia(deps, true, pauseSourceOf(f.get("source")), reason);
|
|
5624
|
+
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
5625
|
}
|
|
5479
5626
|
if (p === "/resume" && req.method === "POST") {
|
|
5480
|
-
if (!
|
|
5627
|
+
if (!authed(req)) return unauthorized();
|
|
5481
5628
|
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
|
-
|
|
5629
|
+
setPausedVia(deps, false, pauseSourceOf((await formValue(req)).get("source")), null);
|
|
5630
|
+
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" });
|
|
5631
|
+
}
|
|
5632
|
+
if (p === "/status") return Response.json(daemonStatus(deps));
|
|
5633
|
+
if (p === "/shutdown" && req.method === "POST") {
|
|
5634
|
+
if (!authed(req)) return unauthorized();
|
|
5635
|
+
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 });
|
|
5636
|
+
const f = await formValue(req);
|
|
5637
|
+
return Response.json(deps.requestStop({ force: f.get("force") === "1", wait: f.get("wait") === "1" }));
|
|
5485
5638
|
}
|
|
5486
5639
|
if (p === "/revive" && req.method === "POST") {
|
|
5487
5640
|
if (!auth()) return unauthorized();
|
|
@@ -6516,68 +6669,106 @@ function daemonCancel(slug, reason) {
|
|
|
6516
6669
|
function daemonWake(runId) {
|
|
6517
6670
|
return post("/wake", { runId: String(runId) });
|
|
6518
6671
|
}
|
|
6672
|
+
function daemonPause(reason) {
|
|
6673
|
+
return post("/pause", { source: "cli", ...reason ? { reason } : {} });
|
|
6674
|
+
}
|
|
6675
|
+
function daemonResume() {
|
|
6676
|
+
return post("/resume", { source: "cli" });
|
|
6677
|
+
}
|
|
6678
|
+
async function daemonStop(opts) {
|
|
6679
|
+
const res = await post("/shutdown", { force: opts.force ? "1" : "0", wait: opts.wait ? "1" : "0" });
|
|
6680
|
+
if (!res) return null;
|
|
6681
|
+
const raw = res;
|
|
6682
|
+
if (raw.stopping === void 0 && raw.waiting === void 0) return null;
|
|
6683
|
+
return { stopping: !!raw.stopping, waiting: !!raw.waiting, sessions: Array.isArray(raw.sessions) ? raw.sessions : [] };
|
|
6684
|
+
}
|
|
6685
|
+
async function daemonStatus2() {
|
|
6686
|
+
try {
|
|
6687
|
+
const res = await fetch(`${BASE}/status`, { headers: headers(), signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
6688
|
+
if (!res.ok) return null;
|
|
6689
|
+
const body = await res.json();
|
|
6690
|
+
return typeof body?.paused === "boolean" ? body : null;
|
|
6691
|
+
} catch {
|
|
6692
|
+
return null;
|
|
6693
|
+
}
|
|
6694
|
+
}
|
|
6519
6695
|
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
6696
|
|
|
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"
|
|
6697
|
+
// src/pauseState.ts
|
|
6698
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9, renameSync as renameSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
6699
|
+
import { join as join13 } from "path";
|
|
6700
|
+
function pauseStatePath(home) {
|
|
6701
|
+
return join13(home, "pause.json");
|
|
6702
|
+
}
|
|
6703
|
+
var ACTIVE = { paused: false, at: "", source: "cli" };
|
|
6704
|
+
function loadPauseState(home, io = {}) {
|
|
6705
|
+
const exists = io.exists ?? existsSync8;
|
|
6706
|
+
const readFile2 = io.readFile ?? ((p) => readFileSync9(p, "utf8"));
|
|
6707
|
+
const path = pauseStatePath(home);
|
|
6708
|
+
if (!exists(path)) return ACTIVE;
|
|
6709
|
+
try {
|
|
6710
|
+
const parsed = JSON.parse(readFile2(path));
|
|
6711
|
+
if (parsed.paused !== true) return ACTIVE;
|
|
6712
|
+
return {
|
|
6713
|
+
paused: true,
|
|
6714
|
+
at: typeof parsed.at === "string" ? parsed.at : "",
|
|
6715
|
+
source: parsed.source === "web" || parsed.source === "remote" ? parsed.source : "cli",
|
|
6716
|
+
...typeof parsed.reason === "string" && parsed.reason.trim() ? { reason: parsed.reason.trim() } : {}
|
|
6717
|
+
};
|
|
6718
|
+
} catch {
|
|
6719
|
+
return ACTIVE;
|
|
6576
6720
|
}
|
|
6577
|
-
}
|
|
6721
|
+
}
|
|
6722
|
+
function savePauseState(home, state, io = {}) {
|
|
6723
|
+
const path = pauseStatePath(home);
|
|
6724
|
+
if (!state.paused) {
|
|
6725
|
+
const remove = io.remove ?? ((p) => existsSync8(p) && unlinkSync4(p));
|
|
6726
|
+
remove(path);
|
|
6727
|
+
return;
|
|
6728
|
+
}
|
|
6729
|
+
const writeFile2 = io.writeFile ?? ((p, data) => {
|
|
6730
|
+
writeFileSync9(`${p}.tmp`, data);
|
|
6731
|
+
renameSync3(`${p}.tmp`, p);
|
|
6732
|
+
});
|
|
6733
|
+
writeFile2(path, JSON.stringify(state, null, 2));
|
|
6734
|
+
}
|
|
6735
|
+
function makePauseState(source, reason, now = Date.now()) {
|
|
6736
|
+
return {
|
|
6737
|
+
paused: true,
|
|
6738
|
+
at: new Date(now).toISOString(),
|
|
6739
|
+
source,
|
|
6740
|
+
...reason && reason.trim() ? { reason: reason.trim() } : {}
|
|
6741
|
+
};
|
|
6742
|
+
}
|
|
6743
|
+
function pauseText(state, now = Date.now()) {
|
|
6744
|
+
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";
|
|
6745
|
+
const since = state.at ? sinceText(state.at, now) : "";
|
|
6746
|
+
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];
|
|
6747
|
+
const why = state.reason ? ` \u2014 ${state.reason}` : "";
|
|
6748
|
+
return `\u0440\u043E\u0439 \u041D\u0410 \u041F\u0410\u0423\u0417\u0415${since} (${who})${why}`;
|
|
6749
|
+
}
|
|
6750
|
+
function sinceText(at, now) {
|
|
6751
|
+
const ms = Date.parse(at);
|
|
6752
|
+
if (Number.isNaN(ms)) return "";
|
|
6753
|
+
const min = Math.max(0, Math.round((now - ms) / 6e4));
|
|
6754
|
+
if (min < 1) return " (\u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E)";
|
|
6755
|
+
if (min < 60) return ` (${min} \u043C\u0438\u043D)`;
|
|
6756
|
+
return ` (${Math.round(min / 60)} \u0447)`;
|
|
6757
|
+
}
|
|
6758
|
+
|
|
6759
|
+
// src/heartbeat.ts
|
|
6760
|
+
import { readFileSync as readFileSync10, renameSync as renameSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
6761
|
+
function readHeartbeat(path) {
|
|
6762
|
+
try {
|
|
6763
|
+
const ms = Number(readFileSync10(path, "utf8").trim());
|
|
6764
|
+
return Number.isFinite(ms) ? ms : null;
|
|
6765
|
+
} catch {
|
|
6766
|
+
return null;
|
|
6767
|
+
}
|
|
6768
|
+
}
|
|
6578
6769
|
|
|
6579
6770
|
// src/cli.ts
|
|
6580
|
-
var
|
|
6771
|
+
var PACKAGE_VERSION2 = package_default.version;
|
|
6581
6772
|
function takeRepo(argv) {
|
|
6582
6773
|
const i = argv.indexOf("--repo");
|
|
6583
6774
|
if (i === -1) return { repo: null, rest: argv };
|
|
@@ -6733,6 +6924,123 @@ async function runLogin(db2, opts) {
|
|
|
6733
6924
|
web.server.close();
|
|
6734
6925
|
}
|
|
6735
6926
|
}
|
|
6927
|
+
async function liveSessionNames(ids) {
|
|
6928
|
+
if (ids.length === 0) return "";
|
|
6929
|
+
const snap = await daemonRuns();
|
|
6930
|
+
const byId = new Map((snap?.runs ?? []).map((r) => [r.id, r]));
|
|
6931
|
+
return ids.map((id) => {
|
|
6932
|
+
const r = byId.get(id);
|
|
6933
|
+
return r ? `${r.name} (${r.task_slug})` : `#${id}`;
|
|
6934
|
+
}).join(", ");
|
|
6935
|
+
}
|
|
6936
|
+
async function waitDaemonDown(opts) {
|
|
6937
|
+
const pollMs = opts.pollMs ?? 3e3;
|
|
6938
|
+
const until = Date.now() + opts.timeoutMs;
|
|
6939
|
+
let lastLive = -1;
|
|
6940
|
+
for (; ; ) {
|
|
6941
|
+
const st = await daemonStatus2();
|
|
6942
|
+
if (!st) return true;
|
|
6943
|
+
if (st.live.length !== lastLive) {
|
|
6944
|
+
lastLive = st.live.length;
|
|
6945
|
+
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");
|
|
6946
|
+
}
|
|
6947
|
+
if (Date.now() >= until) return false;
|
|
6948
|
+
await sleep2(pollMs);
|
|
6949
|
+
}
|
|
6950
|
+
}
|
|
6951
|
+
async function runStop(argv) {
|
|
6952
|
+
const force = argv.includes("--force");
|
|
6953
|
+
const wait = argv.includes("--wait");
|
|
6954
|
+
if (force && wait) {
|
|
6955
|
+
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");
|
|
6956
|
+
return 1;
|
|
6957
|
+
}
|
|
6958
|
+
const res = await daemonStop({ force, wait });
|
|
6959
|
+
if (!res) {
|
|
6960
|
+
const beat = readHeartbeat(HEARTBEAT_PATH);
|
|
6961
|
+
const freshBeat = beat != null && Date.now() - beat < 6e4;
|
|
6962
|
+
if (freshBeat) {
|
|
6963
|
+
console.error(
|
|
6964
|
+
`\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.`
|
|
6965
|
+
);
|
|
6966
|
+
return 1;
|
|
6967
|
+
}
|
|
6968
|
+
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.`);
|
|
6969
|
+
return 0;
|
|
6970
|
+
}
|
|
6971
|
+
if (res.stopping) {
|
|
6972
|
+
if (res.sessions.length) {
|
|
6973
|
+
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)}`);
|
|
6974
|
+
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)");
|
|
6975
|
+
} else {
|
|
6976
|
+
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");
|
|
6977
|
+
}
|
|
6978
|
+
const down = await waitDaemonDown({ timeoutMs: 3e4, pollMs: 500 });
|
|
6979
|
+
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`");
|
|
6980
|
+
return down ? 0 : 1;
|
|
6981
|
+
}
|
|
6982
|
+
if (res.waiting) {
|
|
6983
|
+
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)}`);
|
|
6984
|
+
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");
|
|
6985
|
+
const down = await waitDaemonDown({ timeoutMs: 6 * 60 * 6e4 });
|
|
6986
|
+
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)");
|
|
6987
|
+
return down ? 0 : 1;
|
|
6988
|
+
}
|
|
6989
|
+
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)}`);
|
|
6990
|
+
console.error(
|
|
6991
|
+
"\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"
|
|
6992
|
+
);
|
|
6993
|
+
return 1;
|
|
6994
|
+
}
|
|
6995
|
+
async function runRemove(argv) {
|
|
6996
|
+
const yes = argv.includes("--yes") || argv.includes("-y");
|
|
6997
|
+
if (!yes) {
|
|
6998
|
+
if (!process.stdin.isTTY) {
|
|
6999
|
+
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");
|
|
7000
|
+
return 1;
|
|
7001
|
+
}
|
|
7002
|
+
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();
|
|
7003
|
+
if (answer !== "y" && answer !== "yes" && answer !== "\u0434" && answer !== "\u0434\u0430") {
|
|
7004
|
+
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");
|
|
7005
|
+
return 1;
|
|
7006
|
+
}
|
|
7007
|
+
}
|
|
7008
|
+
const stopCode = await runStop(argv.filter((a) => a !== "--yes" && a !== "-y"));
|
|
7009
|
+
if (stopCode !== 0) {
|
|
7010
|
+
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");
|
|
7011
|
+
return stopCode;
|
|
7012
|
+
}
|
|
7013
|
+
if (SWARM_BACKEND !== "client") {
|
|
7014
|
+
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");
|
|
7015
|
+
} else {
|
|
7016
|
+
const db2 = await makeCliStore(SWARM_REPO);
|
|
7017
|
+
try {
|
|
7018
|
+
if (db2 instanceof SwarmClient) {
|
|
7019
|
+
const res = await db2.forgetHost();
|
|
7020
|
+
console.log(
|
|
7021
|
+
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"
|
|
7022
|
+
);
|
|
7023
|
+
}
|
|
7024
|
+
} catch (e) {
|
|
7025
|
+
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}`);
|
|
7026
|
+
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");
|
|
7027
|
+
db2.close();
|
|
7028
|
+
return 1;
|
|
7029
|
+
}
|
|
7030
|
+
db2.close();
|
|
7031
|
+
}
|
|
7032
|
+
clearAgentToken();
|
|
7033
|
+
clearStore();
|
|
7034
|
+
clearWorkspace();
|
|
7035
|
+
try {
|
|
7036
|
+
savePauseState(SWARM_HOME, { paused: false, at: "", source: "cli" });
|
|
7037
|
+
} catch {
|
|
7038
|
+
}
|
|
7039
|
+
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.");
|
|
7040
|
+
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.");
|
|
7041
|
+
console.log("\u0432\u0435\u0440\u043D\u0443\u0442\u044C \u043C\u0430\u0448\u0438\u043D\u0443 \u0432 \u0440\u043E\u0439: swarm login && swarm start");
|
|
7042
|
+
return 0;
|
|
7043
|
+
}
|
|
6736
7044
|
var STATUS_MARK = {
|
|
6737
7045
|
queued: "\xB7",
|
|
6738
7046
|
running: "\u25B6",
|
|
@@ -6784,10 +7092,16 @@ function printRuns(runs, view) {
|
|
|
6784
7092
|
}).forEach((l) => console.log(l));
|
|
6785
7093
|
}
|
|
6786
7094
|
function usage() {
|
|
6787
|
-
console.log(`swarm \u2014 \u0440\u043E\u0439 \u0434\u0440\u043E\u043D\u043E\u0432 (v${
|
|
7095
|
+
console.log(`swarm \u2014 \u0440\u043E\u0439 \u0434\u0440\u043E\u043D\u043E\u0432 (v${PACKAGE_VERSION2})
|
|
6788
7096
|
|
|
6789
7097
|
swarm login [--workspace <slug|id>] [--no-open] \u0432\u0445\u043E\u0434 \u0432 \u043F\u0440\u043E\u0434 + \u0432\u044B\u0431\u043E\u0440 \u0432\u043E\u0440\u043A\u0441\u043F\u0435\u0439\u0441\u0430
|
|
6790
7098
|
swarm start [--repo <path>] \u043F\u043E\u0434\u043D\u044F\u0442\u044C \u0434\u0435\u043C\u043E\u043D\u0430: \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u0442 \u043E\u0447\u0435\u0440\u0435\u0434\u044C, \u0441\u043F\u0430\u0432\u043D\u0438\u0442/\u0431\u0443\u0434\u0438\u0442 \u0434\u0440\u043E\u043D\u043E\u0432
|
|
7099
|
+
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
|
|
7100
|
+
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); \u043F\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u0440\u0435\u0441\u0442\u0430\u0440\u0442 \u0434\u0435\u043C\u043E\u043D\u0430
|
|
7101
|
+
swarm resume \u0441\u043D\u044F\u0442\u044C \u043F\u0430\u0443\u0437\u0443
|
|
7102
|
+
swarm stop [--wait|--force] \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: \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,
|
|
7103
|
+
\u0430 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442, \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0438\u0445; --wait \u2014 \u0434\u043E\u0436\u0434\u0430\u0442\u044C\u0441\u044F \u0438\u0445 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F; --force \u2014 \u0441\u0435\u0439\u0447\u0430\u0441
|
|
7104
|
+
swarm remove [--yes] [--wait|--force] \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 \u0434\u0435\u043C\u043E\u043D\u0430, \u0443\u0431\u0440\u0430\u0442\u044C \u0435\u0451 \u0438\u0437 \u043F\u0430\u0440\u043A\u0430 \u0438 \u0441\u0442\u0435\u0440\u0435\u0442\u044C \u0442\u043E\u043A\u0435\u043D\u044B
|
|
6791
7105
|
swarm ls [status] \u0441\u043F\u0438\u0441\u043E\u043A \u043F\u0440\u043E\u0433\u043E\u043D\u043E\u0432 \u0416\u0418\u0412\u041E\u0413\u041E \u0414\u0415\u041C\u041E\u041D\u0410 \u2014 \u0440\u043E\u0432\u043D\u043E \u0442\u0435, \u0447\u0442\u043E \u0432 \u0434\u0430\u0448\u0431\u043E\u0440\u0434\u0435 (\u043E\u043F\u0446. \u0444\u0438\u043B\u044C\u0442\u0440 \u043F\u043E \u0441\u0442\u0430\u0442\u0443\u0441\u0443)
|
|
6792
7106
|
swarm spawn <slug> [area] [--repo <path>] [--after <s1,s2>] [--priority <n>] [--base <\u0432\u0435\u0442\u043A\u0430>] [--host <\u043C\u0430\u0448\u0438\u043D\u0430|any>] \u0432 \u043E\u0447\u0435\u0440\u0435\u0434\u044C (--after \u2192 \u0436\u0434\u0430\u0442\u044C done \u0437\u0430\u0432\u0438\u0441\u0438\u043C\u043E\u0441\u0442\u0435\u0439, --priority \u2192 \u0432\u044B\u0448\u0435 \u0431\u0435\u0440\u0451\u0442\u0441\u044F \u0440\u0430\u043D\u044C\u0448\u0435, --base \u2192 PR \u0432 \u044D\u0442\u0443 \u0432\u0435\u0442\u043A\u0443, \u0430 \u043D\u0435 \u0432 \u0434\u0435\u0444\u043E\u043B\u0442\u043D\u0443\u044E, --host \u2192 \u043C\u0430\u0448\u0438\u043D\u0430 \u043F\u0440\u043E\u0433\u043E\u043D\u0430: \u041F\u041E \u0423\u041C\u041E\u041B\u0427\u0410\u041D\u0418\u042E \u044D\u0442\u0430, \xABany\xBB \u2192 \u043B\u044E\u0431\u0430\u044F \u0441\u0432\u043E\u0431\u043E\u0434\u043D\u0430\u044F)
|
|
6793
7107
|
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 \u0434\u043B\u044F --host)
|
|
@@ -6814,7 +7128,7 @@ function effectiveWorktreesDir() {
|
|
|
6814
7128
|
legacyDir: WORKTREES_LEGACY_DIR,
|
|
6815
7129
|
targetDir: WORKTREES_DIR,
|
|
6816
7130
|
envOverride: process.env.SWARM_WORKTREES || "",
|
|
6817
|
-
exists: (d) =>
|
|
7131
|
+
exists: (d) => existsSync9(d),
|
|
6818
7132
|
isNonEmptyDir: (d) => {
|
|
6819
7133
|
try {
|
|
6820
7134
|
return readdirSync6(d).length > 0;
|
|
@@ -6847,7 +7161,7 @@ async function finishClient(db2) {
|
|
|
6847
7161
|
}
|
|
6848
7162
|
var [cmd, ...rawArgs] = process.argv.slice(2);
|
|
6849
7163
|
if (cmd === "--version" || cmd === "-v") {
|
|
6850
|
-
console.log(
|
|
7164
|
+
console.log(PACKAGE_VERSION2);
|
|
6851
7165
|
process.exit(0);
|
|
6852
7166
|
}
|
|
6853
7167
|
if (cmd === "logout") {
|
|
@@ -6946,6 +7260,54 @@ if (cmd === "cancel") {
|
|
|
6946
7260
|
}
|
|
6947
7261
|
console.warn(NO_DAEMON_NOTE);
|
|
6948
7262
|
}
|
|
7263
|
+
if (cmd === "status") {
|
|
7264
|
+
const st = await daemonStatus2();
|
|
7265
|
+
if (!st) {
|
|
7266
|
+
const local = loadPauseState(SWARM_HOME);
|
|
7267
|
+
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`);
|
|
7268
|
+
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");
|
|
7269
|
+
process.exit(1);
|
|
7270
|
+
}
|
|
7271
|
+
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}`);
|
|
7272
|
+
console.log(pauseText(st.pause ?? (st.paused ? { paused: true, at: "", source: "web" } : null)));
|
|
7273
|
+
if (st.restart?.phase === "waiting") {
|
|
7274
|
+
console.log(
|
|
7275
|
+
`\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`
|
|
7276
|
+
);
|
|
7277
|
+
}
|
|
7278
|
+
const counts = Object.entries(st.counts).filter(([, n]) => n > 0);
|
|
7279
|
+
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");
|
|
7280
|
+
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");
|
|
7281
|
+
process.exit(0);
|
|
7282
|
+
}
|
|
7283
|
+
if (cmd === "pause" || cmd === "resume") {
|
|
7284
|
+
const wantPause = cmd === "pause";
|
|
7285
|
+
const reason = wantPause ? rawArgs.join(" ").trim() || null : null;
|
|
7286
|
+
const verdict = wantPause ? await daemonPause(reason) : await daemonResume();
|
|
7287
|
+
if (verdict) {
|
|
7288
|
+
console.log(verdict.ok ? verdict.why : verdict.why);
|
|
7289
|
+
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");
|
|
7290
|
+
process.exit(verdict.ok ? 0 : 1);
|
|
7291
|
+
}
|
|
7292
|
+
try {
|
|
7293
|
+
savePauseState(SWARM_HOME, wantPause ? makePauseState("cli", reason) : { paused: false, at: "", source: "cli" });
|
|
7294
|
+
} catch (e) {
|
|
7295
|
+
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}`);
|
|
7296
|
+
process.exit(1);
|
|
7297
|
+
}
|
|
7298
|
+
console.log(
|
|
7299
|
+
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"
|
|
7300
|
+
);
|
|
7301
|
+
process.exit(0);
|
|
7302
|
+
}
|
|
7303
|
+
if (cmd === "stop") {
|
|
7304
|
+
const code = await runStop(rawArgs);
|
|
7305
|
+
process.exit(code);
|
|
7306
|
+
}
|
|
7307
|
+
if (cmd === "remove") {
|
|
7308
|
+
const code = await runRemove(rawArgs);
|
|
7309
|
+
process.exit(code);
|
|
7310
|
+
}
|
|
6949
7311
|
if (cmd === "shot") {
|
|
6950
7312
|
const code = await runShotCli(rawArgs);
|
|
6951
7313
|
process.exit(code);
|