@taskless-app/swarm 0.4.2 → 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 CHANGED
@@ -28,10 +28,20 @@ npx @taskless-app/swarm start --repo <путь к репо> # поднят
28
28
  ```bash
29
29
  npm i -g @taskless-app/swarm
30
30
  swarm login --workspace <slug>
31
- swarm start --repo <путь к репо> # держит процесс живым — разбирает очередь, спавнит/будит дронов
31
+ swarm start --repo <путь к репо> # поднять рой: уходит в фон, терминал остаётся вам
32
32
  swarm --help # список остальных команд (spawn/ls/questions/answer/…)
33
33
  ```
34
34
 
35
+ **Демон открепляется от терминала.** `swarm start` не занимает окно: процесс уходит
36
+ в фон, а весь вывод пишется в `~/.taskless-swarm/overmind.log` — тот самый файл, из
37
+ которого агент читает хвост лога удалённо. Посмотреть: `swarm logs -f`. Нужен вывод
38
+ прямо в терминале (отладка, systemd, docker, где фоном занимается супервизор) —
39
+ `swarm start --foreground` (он же `-f`, `--attach`).
40
+
41
+ Команда не отчитывается о запуске раньше времени: она ждёт, пока демон реально
42
+ поднимется, и только тогда говорит «поднят». Не поднялся — покажет хвост лога с
43
+ настоящей причиной (занятый порт, отсутствующий токен) и завершится с ошибкой.
44
+
35
45
  Полный гайд по настройке и запуску роя — на [taskless.ru](https://taskless.ru).
36
46
 
37
47
  ## Остановить, приостановить, отключить машину
@@ -138,14 +148,19 @@ swarm_daemon_log(hostId, lines) # хвост лога самого демона
138
148
  трекера лог не оседает, строка обмена умирает на чтении. Чужие логи недоступны —
139
149
  и запрос, и доставка скоупятся владельцем машины.
140
150
 
141
- Лог самого демона демон не заводит (пишет в stdout) — заведите его перенаправлением
142
- или укажите свой путь:
151
+ Лог самого демона заводится сам: фоновый `swarm start` пишет в
152
+ `~/.taskless-swarm/overmind.log` и ротирует его при старте (прошлый — `.1`). Свой
153
+ путь — `SWARM_DAEMON_LOG`. Смотреть на месте:
143
154
 
144
155
  ```bash
145
- swarm start --repo <путь> >> ~/.taskless-swarm/overmind.log 2>&1 # путь по умолчанию
146
- SWARM_DAEMON_LOG=/var/log/overmind.log swarm start --repo <путь> # свой путь
156
+ swarm logs # хвост лога демона (по умолчанию 200 строк)
157
+ swarm logs -n 50 -f # следить за новыми строками
158
+ swarm log <runId> # это ДРУГОЕ: лог конкретного прогона
147
159
  ```
148
160
 
161
+ Запустили с `--foreground` — вывод идёт в терминал, и файла не будет: тогда за него
162
+ отвечает тот, кто перенаправил (systemd, docker, `tee`).
163
+
149
164
  ## Правообладатель и лицензия
150
165
 
151
166
  © 2026 Ilya Smurov. Проприетарное коммерческое ПО — см. [LICENSE](./LICENSE).
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 existsSync9, readdirSync as readdirSync6 } from "fs";
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
 
@@ -3285,6 +3285,85 @@ function parseDecision(v) {
3285
3285
  }
3286
3286
  }
3287
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
+
3288
3367
  // src/procCleanup.ts
3289
3368
  import { execFileSync as execFileSync3 } from "child_process";
3290
3369
 
@@ -3546,7 +3625,7 @@ async function buildRepoRegistry(paths) {
3546
3625
 
3547
3626
  // src/hostIdentity.ts
3548
3627
  import { hostname } from "os";
3549
- import { existsSync as existsSync4, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
3628
+ import { existsSync as existsSync4, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync4 } from "fs";
3550
3629
  import { join as join3 } from "path";
3551
3630
  import { randomUUID as randomUUID2 } from "crypto";
3552
3631
  function hostIdentityPath(home) {
@@ -3554,10 +3633,10 @@ function hostIdentityPath(home) {
3554
3633
  }
3555
3634
  function loadHostIdentity(deps) {
3556
3635
  const exists = deps.exists ?? existsSync4;
3557
- const readFile2 = deps.readFile ?? ((p) => readFileSync4(p, "utf8"));
3636
+ const readFile2 = deps.readFile ?? ((p) => readFileSync5(p, "utf8"));
3558
3637
  const writeFile2 = deps.writeFile ?? ((p, data) => {
3559
- writeFileSync3(`${p}.tmp`, data);
3560
- renameSync2(`${p}.tmp`, p);
3638
+ writeFileSync4(`${p}.tmp`, data);
3639
+ renameSync3(`${p}.tmp`, p);
3561
3640
  });
3562
3641
  const host = deps.hostname ?? hostname;
3563
3642
  const newId = deps.newId ?? randomUUID2;
@@ -3665,7 +3744,7 @@ import { basename as basename2 } from "path";
3665
3744
  import { serve as serve2 } from "@hono/node-server";
3666
3745
 
3667
3746
  // src/diff.ts
3668
- import { statSync as statSync2 } from "fs";
3747
+ import { statSync as statSync3 } from "fs";
3669
3748
  import { join as join4 } from "path";
3670
3749
  import { $ as $2 } from "execa";
3671
3750
  var MAX_FILES = 200;
@@ -3809,7 +3888,7 @@ async function resolveBase(wt, baseBranch) {
3809
3888
  }
3810
3889
  function mtime(path) {
3811
3890
  try {
3812
- return statSync2(path).mtimeMs;
3891
+ return statSync3(path).mtimeMs;
3813
3892
  } catch {
3814
3893
  return 0;
3815
3894
  }
@@ -3853,7 +3932,7 @@ async function collectRunDiff(worktree, opts = {}) {
3853
3932
  // package.json
3854
3933
  var package_default = {
3855
3934
  name: "@taskless-app/swarm",
3856
- version: "0.4.2",
3935
+ version: "0.4.3",
3857
3936
  type: "module",
3858
3937
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
3859
3938
  keywords: [
@@ -3956,18 +4035,18 @@ async function ghChecks(pr, run = realChecksRunner) {
3956
4035
 
3957
4036
  // src/droneChannel.ts
3958
4037
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
3959
- import { chmodSync, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
4038
+ import { chmodSync, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
3960
4039
  import { dirname as dirname2 } from "path";
3961
4040
  import { serve } from "@hono/node-server";
3962
4041
  function loadChannelSecret(path) {
3963
4042
  try {
3964
- const s = readFileSync5(path, "utf8").trim();
4043
+ const s = readFileSync6(path, "utf8").trim();
3965
4044
  if (s.length >= 32) return s;
3966
4045
  } catch {
3967
4046
  }
3968
4047
  const secret = randomBytes(32).toString("hex");
3969
4048
  mkdirSync3(dirname2(path), { recursive: true });
3970
- writeFileSync4(path, secret, { mode: 384 });
4049
+ writeFileSync5(path, secret, { mode: 384 });
3971
4050
  try {
3972
4051
  chmodSync(path, 384);
3973
4052
  } catch {
@@ -4001,7 +4080,7 @@ var EVENT_KIND_SET = new Set(EVENT_KINDS);
4001
4080
 
4002
4081
  // src/oauth.ts
4003
4082
  import { createHash, randomBytes as randomBytes2 } from "crypto";
4004
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, unlinkSync } from "fs";
4083
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, unlinkSync } from "fs";
4005
4084
  import { join as join5 } from "path";
4006
4085
  var REGISTER = "/api/oauth/register";
4007
4086
  var AUTHORIZE = "/api/oauth/authorize";
@@ -4093,13 +4172,13 @@ function toStore(clientId, t, now) {
4093
4172
  }
4094
4173
  function loadStore() {
4095
4174
  try {
4096
- return JSON.parse(readFileSync6(STORE_PATH, "utf8"));
4175
+ return JSON.parse(readFileSync7(STORE_PATH, "utf8"));
4097
4176
  } catch {
4098
4177
  return null;
4099
4178
  }
4100
4179
  }
4101
4180
  function saveStore(s) {
4102
- writeFileSync5(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
4181
+ writeFileSync6(STORE_PATH, JSON.stringify(s, null, 2), { mode: 384 });
4103
4182
  }
4104
4183
  function clearStore() {
4105
4184
  try {
@@ -4109,7 +4188,7 @@ function clearStore() {
4109
4188
  }
4110
4189
  function loadClientId() {
4111
4190
  try {
4112
- return JSON.parse(readFileSync6(CLIENT_PATH, "utf8")).clientId;
4191
+ return JSON.parse(readFileSync7(CLIENT_PATH, "utf8")).clientId;
4113
4192
  } catch {
4114
4193
  return null;
4115
4194
  }
@@ -4118,7 +4197,7 @@ async function ensureClientId(api, redirectUri, fetchFn = fetch) {
4118
4197
  const existing = loadClientId();
4119
4198
  if (existing) return existing;
4120
4199
  const clientId = await registerClient({ api, redirectUri }, fetchFn);
4121
- writeFileSync5(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
4200
+ writeFileSync6(CLIENT_PATH, JSON.stringify({ clientId }, null, 2), { mode: 384 });
4122
4201
  return clientId;
4123
4202
  }
4124
4203
  async function ensureAccess(store, opts = {}) {
@@ -5697,8 +5776,8 @@ function startWeb(deps, port = WEB_PORT) {
5697
5776
  import { cancelRun, respawnRun, runDaemon, wakeRun, OVERMIND_ENTRY, listDroneProcesses } from "./overmind.js";
5698
5777
 
5699
5778
  // src/launcher.ts
5700
- import { spawn } from "child_process";
5701
- import { closeSync as closeSync2, existsSync as existsSync6, mkdirSync as mkdirSync5, openSync as openSync2, readdirSync as readdirSync4, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
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";
5702
5781
  import { createRequire as createRequire2 } from "module";
5703
5782
  import { homedir as homedir2 } from "os";
5704
5783
  import { dirname as dirname3, join as join7 } from "path";
@@ -5777,7 +5856,7 @@ function resolveClaudeBin(opts = {}) {
5777
5856
  }
5778
5857
 
5779
5858
  // src/droneHome.ts
5780
- import { lstatSync, mkdirSync as mkdirSync4, readdirSync as readdirSync3, rmSync, statSync as statSync3, symlinkSync } from "fs";
5859
+ import { lstatSync, mkdirSync as mkdirSync4, readdirSync as readdirSync3, rmSync, statSync as statSync4, symlinkSync } from "fs";
5781
5860
  import { join as join6, relative, sep } from "path";
5782
5861
  var DRONE_HOME_DENY_DEFAULT = [".taskless-swarm", ".bash_history", ".zsh_history"];
5783
5862
  function droneHomeDeny(env = process.env) {
@@ -5818,7 +5897,7 @@ function buildDroneHome(opts) {
5818
5897
  const target = join6(realHome, name);
5819
5898
  let isDir;
5820
5899
  try {
5821
- isDir = statSync3(target).isDirectory();
5900
+ isDir = statSync4(target).isDirectory();
5822
5901
  } catch {
5823
5902
  continue;
5824
5903
  }
@@ -5984,14 +6063,14 @@ function openNextRunLog(runLogsDir, runId) {
5984
6063
  const path = nextRunLogPath(runLogsDir, runId);
5985
6064
  let out;
5986
6065
  try {
5987
- out = openSync2(path, "wx");
6066
+ out = openSync3(path, "wx");
5988
6067
  } catch (e) {
5989
6068
  if (e.code !== "EEXIST") throw e;
5990
6069
  continue;
5991
6070
  }
5992
6071
  let err = null;
5993
6072
  try {
5994
- err = openSync2(`${path.slice(0, -".json".length)}.err.log`, "w");
6073
+ err = openSync3(`${path.slice(0, -".json".length)}.err.log`, "w");
5995
6074
  } catch {
5996
6075
  err = null;
5997
6076
  }
@@ -6052,7 +6131,7 @@ function makeLauncher(cfg) {
6052
6131
  }
6053
6132
  };
6054
6133
  const cfgPath = join7(worktree, ".swarm-mcp.json");
6055
- writeFileSync6(cfgPath, JSON.stringify(mcpConfig, null, 2));
6134
+ writeFileSync7(cfgPath, JSON.stringify(mcpConfig, null, 2));
6056
6135
  const model = run.model ?? process.env.SWARM_DRONE_MODEL ?? void 0;
6057
6136
  const pluginDirs = roleDirs(worktree, run.kind);
6058
6137
  const realHome = cfg.droneIsolation?.realHome ?? homedir2();
@@ -6072,7 +6151,7 @@ function makeLauncher(cfg) {
6072
6151
  errFd = fds.err ?? void 0;
6073
6152
  stdio = ["ignore", outFd, errFd ?? "ignore"];
6074
6153
  }
6075
- const child = spawn(cfg.claudeBin ?? resolveClaudeBin(), claudeArgs(cfgPath, prompt, continueSession, model, pluginDirs, settings), {
6154
+ const child = spawn2(cfg.claudeBin ?? resolveClaudeBin(), claudeArgs(cfgPath, prompt, continueSession, model, pluginDirs, settings), {
6076
6155
  cwd: worktree,
6077
6156
  detached: true,
6078
6157
  stdio,
@@ -6082,8 +6161,8 @@ function makeLauncher(cfg) {
6082
6161
  // Остальной env демона наследуется, как раньше.
6083
6162
  env: droneEnv(worktree, process.env, home ? { droneHome: home, realHome } : void 0, cfg.droneCap?.() ?? null)
6084
6163
  });
6085
- if (outFd !== void 0) closeSync2(outFd);
6086
- if (errFd !== void 0) closeSync2(errFd);
6164
+ if (outFd !== void 0) closeSync3(outFd);
6165
+ if (errFd !== void 0) closeSync3(errFd);
6087
6166
  child.unref();
6088
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)");
6089
6168
  return child.pid;
@@ -6091,13 +6170,13 @@ function makeLauncher(cfg) {
6091
6170
  }
6092
6171
 
6093
6172
  // src/git.ts
6094
- import { existsSync as existsSync7, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
6173
+ import { existsSync as existsSync7, readdirSync as readdirSync5, statSync as statSync6 } from "fs";
6095
6174
  import { appendFile, mkdir, readFile, rm } from "fs/promises";
6096
6175
  import { isAbsolute, join as join8 } from "path";
6097
6176
  import { $ as $4 } from "execa";
6098
6177
  function isLinkedWorktree(dir) {
6099
6178
  try {
6100
- return statSync5(join8(dir, ".git")).isFile();
6179
+ return statSync6(join8(dir, ".git")).isFile();
6101
6180
  } catch {
6102
6181
  return false;
6103
6182
  }
@@ -6137,20 +6216,20 @@ async function removeWorktree(repo, path) {
6137
6216
  }
6138
6217
 
6139
6218
  // src/agentToken.ts
6140
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync7, unlinkSync as unlinkSync2 } from "fs";
6219
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, unlinkSync as unlinkSync2 } from "fs";
6141
6220
  import { join as join9 } from "path";
6142
6221
  var STORE_PATH2 = join9(SWARM_HOME, "agent.json");
6143
6222
  var base2 = (api) => api.replace(/\/$/, "");
6144
6223
  function loadAgentToken() {
6145
6224
  try {
6146
- const s = JSON.parse(readFileSync7(STORE_PATH2, "utf8"));
6225
+ const s = JSON.parse(readFileSync8(STORE_PATH2, "utf8"));
6147
6226
  return s.token ? s : null;
6148
6227
  } catch {
6149
6228
  return null;
6150
6229
  }
6151
6230
  }
6152
6231
  function saveAgentToken(s) {
6153
- writeFileSync7(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
6232
+ writeFileSync8(STORE_PATH2, JSON.stringify(s, null, 2), { mode: 384 });
6154
6233
  }
6155
6234
  function clearAgentToken() {
6156
6235
  try {
@@ -6202,19 +6281,19 @@ import { basename as basename3, isAbsolute as isAbsolute2, join as join11 } from
6202
6281
  var SHOT_MAX_FILE_BYTES = 10 * 1024 * 1024;
6203
6282
 
6204
6283
  // src/workspace.ts
6205
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3 } from "fs";
6284
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, unlinkSync as unlinkSync3 } from "fs";
6206
6285
  import { join as join12 } from "path";
6207
6286
  var WORKSPACE_PATH = join12(SWARM_HOME, "workspace.json");
6208
6287
  function loadWorkspace() {
6209
6288
  try {
6210
- const w = JSON.parse(readFileSync8(WORKSPACE_PATH, "utf8"));
6289
+ const w = JSON.parse(readFileSync9(WORKSPACE_PATH, "utf8"));
6211
6290
  return w && typeof w.id === "string" && w.id ? w : null;
6212
6291
  } catch {
6213
6292
  return null;
6214
6293
  }
6215
6294
  }
6216
6295
  function saveWorkspace(w) {
6217
- writeFileSync8(WORKSPACE_PATH, JSON.stringify(w, null, 2), { mode: 384 });
6296
+ writeFileSync9(WORKSPACE_PATH, JSON.stringify(w, null, 2), { mode: 384 });
6218
6297
  }
6219
6298
  function clearWorkspace() {
6220
6299
  try {
@@ -6695,7 +6774,7 @@ async function daemonStatus2() {
6695
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`;
6696
6775
 
6697
6776
  // src/pauseState.ts
6698
- import { existsSync as existsSync8, readFileSync as readFileSync9, renameSync as renameSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync9 } from "fs";
6777
+ import { existsSync as existsSync8, readFileSync as readFileSync10, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync10 } from "fs";
6699
6778
  import { join as join13 } from "path";
6700
6779
  function pauseStatePath(home) {
6701
6780
  return join13(home, "pause.json");
@@ -6703,7 +6782,7 @@ function pauseStatePath(home) {
6703
6782
  var ACTIVE = { paused: false, at: "", source: "cli" };
6704
6783
  function loadPauseState(home, io = {}) {
6705
6784
  const exists = io.exists ?? existsSync8;
6706
- const readFile2 = io.readFile ?? ((p) => readFileSync9(p, "utf8"));
6785
+ const readFile2 = io.readFile ?? ((p) => readFileSync10(p, "utf8"));
6707
6786
  const path = pauseStatePath(home);
6708
6787
  if (!exists(path)) return ACTIVE;
6709
6788
  try {
@@ -6727,8 +6806,8 @@ function savePauseState(home, state, io = {}) {
6727
6806
  return;
6728
6807
  }
6729
6808
  const writeFile2 = io.writeFile ?? ((p, data) => {
6730
- writeFileSync9(`${p}.tmp`, data);
6731
- renameSync3(`${p}.tmp`, p);
6809
+ writeFileSync10(`${p}.tmp`, data);
6810
+ renameSync4(`${p}.tmp`, p);
6732
6811
  });
6733
6812
  writeFile2(path, JSON.stringify(state, null, 2));
6734
6813
  }
@@ -6757,10 +6836,10 @@ function sinceText(at, now) {
6757
6836
  }
6758
6837
 
6759
6838
  // src/heartbeat.ts
6760
- import { readFileSync as readFileSync10, renameSync as renameSync4, writeFileSync as writeFileSync10 } from "fs";
6839
+ import { readFileSync as readFileSync11, renameSync as renameSync5, writeFileSync as writeFileSync11 } from "fs";
6761
6840
  function readHeartbeat(path) {
6762
6841
  try {
6763
- const ms = Number(readFileSync10(path, "utf8").trim());
6842
+ const ms = Number(readFileSync11(path, "utf8").trim());
6764
6843
  return Number.isFinite(ms) ? ms : null;
6765
6844
  } catch {
6766
6845
  return null;
@@ -6948,6 +7027,98 @@ async function waitDaemonDown(opts) {
6948
7027
  await sleep2(pollMs);
6949
7028
  }
6950
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
+ }
6951
7122
  async function runStop(argv) {
6952
7123
  const force = argv.includes("--force");
6953
7124
  const wait = argv.includes("--wait");
@@ -7092,33 +7263,62 @@ function printRuns(runs, view) {
7092
7263
  }).forEach((l) => console.log(l));
7093
7264
  }
7094
7265
  function usage() {
7095
- console.log(`swarm \u2014 \u0440\u043E\u0439 \u0434\u0440\u043E\u043D\u043E\u0432 (v${PACKAGE_VERSION2})
7266
+ console.log(`swarm \u2014 \u0440\u043E\u0439 AI-\u0434\u0440\u043E\u043D\u043E\u0432 (v${PACKAGE_VERSION2})
7096
7267
 
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
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
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
7099
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
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
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)
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)
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)
7108
- swarm spawn <slug> --hero [--name HYDRA] [--repo <path>] [--after <s1,s2>] [--priority <n>] \u0413\u0435\u0440\u043E\u0439-\u043B\u0435\u0439\u0442\u0435\u043D\u0430\u043D\u0442 \u044D\u043F\u0438\u043A\u0430
7109
- swarm wake <runId> \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442\u044C \u043F\u0440\u043E\u0433\u043E\u043D (id \u0438\u0437 swarm ls) \u2014 \u0447\u0435\u0440\u0435\u0437 \u0436\u0438\u0432\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430; \u043D\u0435\u0447\u0435\u0433\u043E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u2192 \u0441\u0442\u0430\u0440\u0442 \u0441 \u043D\u0443\u043B\u044F
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
7110
7282
  swarm questions \u043E\u0442\u043A\u0440\u044B\u0442\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0434\u0440\u043E\u043D\u043E\u0432
7111
- swarm answer <qid> <...> \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043D\u0430 \u0432\u043E\u043F\u0440\u043E\u0441 (\u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442 \u0434\u0440\u043E\u043D\u0430)
7112
- swarm cancel <slug> <\u043F\u0440\u0438\u0447\u0438\u043D\u0430\u2026> \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0434\u0440\u043E\u043D\u0430 \u0447\u0435\u0440\u0435\u0437 \u0436\u0438\u0432\u043E\u0433\u043E \u0434\u0435\u043C\u043E\u043D\u0430 (\u0441\u0442\u0430\u0442\u0443\u0441 \xAB\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\xBB + \u043F\u0440\u0438\u0447\u0438\u043D\u0430; \u0432 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F)
7113
- swarm import-db <path> --repo <path> \u0438\u043C\u043F\u043E\u0440\u0442 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u0438\u0437 \u0434\u0440\u0443\u0433\u043E\u0439 \u0411\u0414 (\u0438\u0434\u0435\u043C\u043F\u043E\u0442\u0435\u043D\u0442\u043D\u043E)
7114
- swarm log <runId> \u043B\u043E\u0433 \u043F\u0440\u043E\u0433\u043E\u043D\u0430
7115
- 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
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
7116
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
7117
- swarm web \u0432\u0435\u0431-\u043C\u043E\u0440\u0434\u0430 (\u0432\u0445\u043E\u0434 + \u0441\u0442\u0430\u0442\u0443\u0441) \u043D\u0430 127.0.0.1
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
7118
7303
  swarm shot <url> <out-prefix> [--timeout <ms>] [--storage-state <path>]
7119
- headless-\u0441\u043A\u0440\u0438\u043D desktop+mobile \u041E\u0414\u041D\u041E\u0419 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 (TASWM-40):
7304
+ headless-\u0441\u043A\u0440\u0438\u043D\u044B desktop+mobile \u043E\u0434\u043D\u043E\u0439 \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439:
7120
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)
7121
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
7122
7322
  `);
7123
7323
  }
7124
7324
  var effectiveWorktreesDirMemo;
@@ -7208,14 +7408,18 @@ if (cmd === "import-db") {
7208
7408
  process.exit(0);
7209
7409
  }
7210
7410
  if (cmd === "start") {
7411
+ const { foreground, rest: startArgs } = parseStartMode(rawArgs);
7211
7412
  if (SWARM_BACKEND !== "client") {
7212
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).");
7213
7414
  }
7214
- const { repo: repoFlag } = takeRepo(rawArgs);
7415
+ const { repo: repoFlag } = takeRepo(startArgs);
7215
7416
  const repo = repoFlag ?? SWARM_REPO;
7216
7417
  if (!repo) {
7217
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).");
7218
7419
  }
7420
+ if (!foreground) {
7421
+ process.exit(await runStartDetached(startArgs));
7422
+ }
7219
7423
  try {
7220
7424
  await runDaemon({ repo });
7221
7425
  } catch (e) {
@@ -7224,6 +7428,9 @@ if (cmd === "start") {
7224
7428
  }
7225
7429
  process.exit(0);
7226
7430
  }
7431
+ if (cmd === "logs") {
7432
+ process.exit(await runDaemonLogs(rawArgs));
7433
+ }
7227
7434
  if (cmd === "ls") {
7228
7435
  const snap = await daemonRuns(rawArgs[0]);
7229
7436
  if (snap) {
package/dist/overmind.js CHANGED
@@ -9035,7 +9035,7 @@ async function collectRunDiff(worktree, opts = {}) {
9035
9035
  // package.json
9036
9036
  var package_default = {
9037
9037
  name: "@taskless-app/swarm",
9038
- version: "0.4.2",
9038
+ version: "0.4.3",
9039
9039
  type: "module",
9040
9040
  description: "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
9041
9041
  keywords: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taskless-app/swarm",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "description": "Local control plane for a fleet of AI coding agents (drones), coordinating through Taskless. The overmind runs the swarm.",
6
6
  "keywords": [