@naparnik/mcp 0.10.42 → 0.10.47

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.
@@ -2,8 +2,9 @@ import { execFile, spawn, execFileSync } from 'child_process';
2
2
  import { createHash, randomBytes, randomUUID } from 'crypto';
3
3
  import { existsSync, readFileSync, closeSync, mkdirSync, openSync, readdirSync, statSync, rmSync, writeFileSync, renameSync, symlinkSync, chmodSync, lstatSync, realpathSync, mkdtempSync, linkSync, readlinkSync, appendFileSync, fstatSync, readSync, fsyncSync } from 'fs';
4
4
  import { platform, homedir, arch, release, userInfo, tmpdir } from 'os';
5
- import { win32, join, dirname, sep, resolve, relative, isAbsolute, basename, extname } from 'path';
5
+ import { win32, join, dirname, sep, resolve, basename, relative, isAbsolute, extname } from 'path';
6
6
  import { promisify } from 'util';
7
+ import { AsyncLocalStorage } from 'async_hooks';
7
8
  import { gunzipSync } from 'zlib';
8
9
  import { pathToFileURL, fileURLToPath } from 'url';
9
10
  import { readFile } from 'fs/promises';
@@ -728,9 +729,9 @@ function parsePositiveInt(raw, fallback) {
728
729
  }
729
730
 
730
731
  // src/version.ts
731
- var VERSION = "0.10.42";
732
+ var VERSION = "0.10.47";
732
733
  var USER_AGENT = `naparnik-mcp/${VERSION}`;
733
- var MIN_ENGINE = "2026.09.24-446";
734
+ var MIN_ENGINE = "2026.09.24-466";
734
735
  var LAUNCHER_PROTOCOL = 1;
735
736
 
736
737
  // src/contracts/sdk-headers.ts
@@ -1548,7 +1549,65 @@ var Engine = class {
1548
1549
  }
1549
1550
  /** Python подготовки кандидата — намеренно без fallback на venv старого пака. */
1550
1551
  bootstrapPython() {
1551
- return this.ourPython() ?? (platform() === "win32" ? "python" : "python3");
1552
+ return this.bootstrapPythons()[0] ?? (platform() === "win32" ? "python" : "python3");
1553
+ }
1554
+ /**
1555
+ * Чем собирать окружение новой версии — по порядку, до первого, кто сумеет.
1556
+ *
1557
+ * ⚠ БАЗОВЫЙ ПИТОН РАБОЧЕЙ ВЕРСИИ СТОИТ ВПЕРЕДИ «python3» ИЗ PATH, И ЭТО
1558
+ * ПОЧИНКА, А НЕ ВКУС. Claude Desktop на маке запускает нас без
1559
+ * `/opt/homebrew/bin` в PATH, и «python3» там — `/usr/bin/python3`, питон
1560
+ * Xcode 3.9. Он собирает venv только ссылками («This build of python cannot
1561
+ * create venvs without using symlinks»), а ссылка на интерпретатор вне версии
1562
+ * не проходит сверку `candidateVenvTrouble`. Обновление 2026.09.24-467 у
1563
+ * владельца встало ровно на этом, хотя рабочая версия собрана Homebrew 3.14
1564
+ * копиями и работает. Движок при первой установке ищет питон сам
1565
+ * (`setup/python_finder.py`) и пишет, какой взял, в `pyvenv.cfg` окружения —
1566
+ * этот и берём: он уже однажды собрал здесь рабочее окружение копиями.
1567
+ *
1568
+ * ⚠ НЕ ПИТОН САМОГО VENV. Окружение старой версии удаляется уборкой после
1569
+ * обновления, и кандидат, собранный его питоном, остался бы без интерпретатора
1570
+ * (`engine-candidate.test.ts`). База из `pyvenv.cfg` лежит вне версий.
1571
+ */
1572
+ bootstrapPythons() {
1573
+ const found = [
1574
+ this.ourPython(),
1575
+ this.workingBasePython(),
1576
+ platform() === "win32" ? "python" : "python3"
1577
+ ].filter((one) => one !== null);
1578
+ return [...new Set(found)];
1579
+ }
1580
+ /**
1581
+ * Базовый интерпретатор окружения рабочей версии — из её `pyvenv.cfg`.
1582
+ *
1583
+ * `home` раньше `executable`: у Homebrew `executable` указывает в папку
1584
+ * конкретного выпуска (`Cellar/python@3.14/3.14.7/…`), и после `brew upgrade`
1585
+ * её уже нет, а `home` (`/opt/homebrew/opt/python@3.14/bin`) переживает
1586
+ * обновление. Нет файла или записанного питона на диске — `null`.
1587
+ */
1588
+ workingBasePython() {
1589
+ const place = this.place();
1590
+ if (place === null) return null;
1591
+ let text2;
1592
+ try {
1593
+ text2 = readFileSync(join(place.pack, ".venv", "pyvenv.cfg"), "utf8");
1594
+ } catch {
1595
+ return null;
1596
+ }
1597
+ const value = (key) => {
1598
+ const found = new RegExp(`^\\s*${key}\\s*=\\s*(.+?)\\s*$`, "m").exec(text2);
1599
+ return found?.[1] ?? null;
1600
+ };
1601
+ const home = value("home");
1602
+ if (home !== null) {
1603
+ const names = platform() === "win32" ? ["python.exe"] : ["python3", "python"];
1604
+ for (const name of names) {
1605
+ const candidate = join(home, name);
1606
+ if (existsSync(candidate)) return candidate;
1607
+ }
1608
+ }
1609
+ const executable = value("executable");
1610
+ return executable !== null && existsSync(executable) ? executable : null;
1552
1611
  }
1553
1612
  /** Python ровно названного кандидата, а не текущей версии. */
1554
1613
  candidatePython(place) {
@@ -1676,6 +1735,45 @@ var Engine = class {
1676
1735
  env: montageEnv(this.pythonEnv(), key)
1677
1736
  });
1678
1737
  }
1738
+ /**
1739
+ * То же, что `command`, но строки stderr уходят в `onLine` по мере печати.
1740
+ *
1741
+ * ⚠ РАДИ СКАЧИВАНИЯ НАЧИНКИ. `deliver` печатает «Скачано 3 МБ из 6 (50%)» в
1742
+ * stderr по ходу, а `command` отдаёт всё разом в конце — и полоса обновления
1743
+ * стояла без единой цифры, пока архив качался (владелец 2026-09-24: «как
1744
+ * будто завис»). Строки те же, что у задач движка, и полоса разбирает их тем
1745
+ * же `DOWNLOAD` (`job-report.ts`).
1746
+ */
1747
+ async commandLive(args, { timeout = 3e5, key }, onLine) {
1748
+ const place = this.place();
1749
+ if (place === null) return { ok: false, text: this.whyNoEngine() };
1750
+ const trouble = await this.pythonTrouble();
1751
+ if (trouble !== null) return { ok: false, text: trouble };
1752
+ try {
1753
+ const { stdout, stderr } = await new Promise((done, fail) => {
1754
+ const child = execFile(this.enginePython(), [place.consolePy, ...args], {
1755
+ cwd: existsSync(place.pack) ? place.pack : place.engine,
1756
+ maxBuffer: 8 << 20,
1757
+ timeout,
1758
+ encoding: "utf8",
1759
+ env: montageEnv(this.pythonEnv(), key)
1760
+ }, (err, out, errOut) => {
1761
+ if (err !== null) fail(Object.assign(err, { stdout: out, stderr: errOut }));
1762
+ else done({ stdout: out, stderr: errOut });
1763
+ });
1764
+ let rest = "";
1765
+ child.stderr?.on("data", (chunk) => {
1766
+ rest += String(chunk);
1767
+ const lines = rest.split(/\r?\n|\r/);
1768
+ rest = lines.pop() ?? "";
1769
+ for (const line of lines) if (line.trim() !== "") onLine(line.trim());
1770
+ });
1771
+ });
1772
+ return { ok: true, text: (stdout + stderr).trim() };
1773
+ } catch (err) {
1774
+ return parseFailure(err, "\u041A\u043E\u043C\u0430\u043D\u0434\u0430 \u043D\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043B\u0430\u0441\u044C");
1775
+ }
1776
+ }
1679
1777
  /** Одна строка вывода команды — например, номер заведённого задания. */
1680
1778
  async asString(place, args, key) {
1681
1779
  const { stdout } = await this.run(place, args, {
@@ -13793,6 +13891,29 @@ async function askPointer(opts) {
13793
13891
  clearTimeout(timer);
13794
13892
  }
13795
13893
  }
13894
+ var scope = new AsyncLocalStorage();
13895
+ function noteInstallFault(stage, trouble) {
13896
+ scope.getStore()?.push({ stage, trouble });
13897
+ }
13898
+ async function collectInstallFaults(work) {
13899
+ const faults = [];
13900
+ const value = await scope.run(faults, work);
13901
+ return { value, faults };
13902
+ }
13903
+ var JOBS_KEPT = 10;
13904
+ var byJob = /* @__PURE__ */ new Map();
13905
+ function collectJobInstallFaults(id, work) {
13906
+ const faults = [];
13907
+ byJob.delete(id);
13908
+ byJob.set(id, faults);
13909
+ while (byJob.size > JOBS_KEPT) byJob.delete(byJob.keys().next().value);
13910
+ return scope.run(faults, work);
13911
+ }
13912
+ function jobInstallFaults(id) {
13913
+ return byJob.get(id) ?? [];
13914
+ }
13915
+
13916
+ // src/update.ts
13796
13917
  var MANIFEST_PATH = "/download/engine/manifest.json";
13797
13918
  function manifestUrlFor(baseUrl) {
13798
13919
  return pointerUrl(baseUrl, MANIFEST_PATH);
@@ -13846,6 +13967,7 @@ async function deploy(engine, manifest, trial = probe, key, hooks = {}) {
13846
13967
  return await withVersionLock(dir, async () => deployLocked(engine, manifest, trial, key, hooks));
13847
13968
  } catch (trouble) {
13848
13969
  if (trouble instanceof VersionLockError) return { ok: false, text: trouble.message };
13970
+ noteInstallFault("deploy", trouble);
13849
13971
  return {
13850
13972
  ok: false,
13851
13973
  text: `\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0434\u043E \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F current: ${describeFailure(trouble)}. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup.`
@@ -13880,20 +14002,20 @@ async function deployLocked(engine, manifest, trial, key, hooks) {
13880
14002
  if (!existsSync(dest)) {
13881
14003
  const stage = createVersionStage(dir, manifest.version);
13882
14004
  try {
13883
- const delivery = await engine.command(
13884
- [
13885
- "deliver",
13886
- "--url",
13887
- manifest.url,
13888
- "--dest",
13889
- stage.payload,
13890
- "--sha256",
13891
- manifest.sha256,
13892
- "--byte",
13893
- String(manifest.bytes)
13894
- ],
13895
- { timeout: 30 * 6e4, ...key !== void 0 ? { key } : {} }
13896
- );
14005
+ const deliverArgs = [
14006
+ "deliver",
14007
+ "--url",
14008
+ manifest.url,
14009
+ "--dest",
14010
+ stage.payload,
14011
+ "--sha256",
14012
+ manifest.sha256,
14013
+ "--byte",
14014
+ String(manifest.bytes)
14015
+ ];
14016
+ const deliverOpts = { timeout: 30 * 6e4, ...key !== void 0 ? { key } : {} };
14017
+ const say = hooks.line;
14018
+ const delivery = say !== void 0 && engine.commandLive !== void 0 ? await engine.commandLive(deliverArgs, deliverOpts, (text2) => say(text2)) : await engine.command(deliverArgs, deliverOpts);
13897
14019
  if (!delivery.ok) {
13898
14020
  return { ok: false, text: `\u041D\u0430\u0447\u0438\u043D\u043A\u0443 \u0441\u043A\u0430\u0447\u0430\u0442\u044C \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C, \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043C\u0435\u043D\u044F\u043B.
13899
14021
  ${delivery.text}` };
@@ -13935,6 +14057,7 @@ ${delivery.text}` };
13935
14057
  discardVersionStage(stage);
13936
14058
  }
13937
14059
  }
14060
+ hooks.downloaded?.();
13938
14061
  if (candidateDeps(engine)) return activatePreparedCandidateLocked(engine, manifest, trial);
13939
14062
  const smoke = await dryRun(engine, dest, manifest.version, trial);
13940
14063
  if (smoke !== null) {
@@ -14014,6 +14137,7 @@ async function continuePreparedCandidate(engine, expected, bootstrap, trial = pr
14014
14137
  });
14015
14138
  } catch (trouble) {
14016
14139
  if (trouble instanceof VersionLockError) return { ok: false, text: trouble.message };
14140
+ noteInstallFault("activate", trouble);
14017
14141
  return {
14018
14142
  ok: false,
14019
14143
  text: `\u041A\u0430\u043D\u0434\u0438\u0434\u0430\u0442 \u043D\u0435 \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0435\u043D: ${describeFailure(trouble)}. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0438\u0441\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u043F\u0440\u0438\u0447\u0438\u043D\u0443 \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup.`
@@ -14172,13 +14296,7 @@ async function prepareCopiedCandidateVenv(engine, dir, version2, place) {
14172
14296
  if (engine.prepareCandidateVenv !== void 0) {
14173
14297
  await engine.prepareCandidateVenv(place);
14174
14298
  } else {
14175
- const { execFile: execFile4 } = await import('child_process');
14176
- const { promisify: promisify3 } = await import('util');
14177
- await promisify3(execFile4)(engine.bootstrapPython(), ["-m", "venv", "--copies", venv], {
14178
- timeout: 10 * 6e4,
14179
- encoding: "utf8",
14180
- env: { ...engine.pythonEnv(), PYTHONDONTWRITEBYTECODE: "1" }
14181
- });
14299
+ await copiedVenv(engine, venv);
14182
14300
  }
14183
14301
  }
14184
14302
  const createdTrouble = candidateVenvTrouble(dir, version2, place, true);
@@ -14188,6 +14306,30 @@ async function prepareCopiedCandidateVenv(engine, dir, version2, place) {
14188
14306
  throw new Error("\u0448\u0442\u0430\u0442\u043D\u044B\u0439 .venv \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u0441\u043E\u0437\u0434\u0430\u043D \u0431\u0435\u0437 \u0440\u0430\u0431\u043E\u0447\u0435\u0433\u043E pip");
14189
14307
  }
14190
14308
  }
14309
+ var SYMLINKS_ONLY = "cannot create venvs without using symlinks";
14310
+ async function copiedVenv(engine, venv) {
14311
+ const { execFile: execFile4 } = await import('child_process');
14312
+ const { promisify: promisify3 } = await import('util');
14313
+ const pythons = engine.bootstrapPythons?.() ?? [engine.bootstrapPython()];
14314
+ const refusals = [];
14315
+ for (const python of pythons) {
14316
+ try {
14317
+ await promisify3(execFile4)(python, ["-m", "venv", "--copies", venv], {
14318
+ timeout: 10 * 6e4,
14319
+ encoding: "utf8",
14320
+ env: { ...engine.pythonEnv(), PYTHONDONTWRITEBYTECODE: "1" }
14321
+ });
14322
+ return;
14323
+ } catch (trouble) {
14324
+ rmSync(venv, { recursive: true, force: true });
14325
+ const said = `${String(trouble.stderr ?? "")} ${describeFailure(trouble)}`;
14326
+ refusals.push(said.includes(SYMLINKS_ONLY) ? `${python}: \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u0441\u044B\u043B\u043A\u0430\u043C\u0438` : `${python}: ${lastLine(said.trim())}`);
14327
+ }
14328
+ }
14329
+ throw new Error(
14330
+ `\u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0441\u043E\u0431\u0440\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C \u2014 ${refusals.join("; ")}. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0441\u0442\u0430\u0432\u044C Python \u0441 python.org/downloads \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \xAB\u043E\u0431\u043D\u043E\u0432\u0438\xBB.`
14331
+ );
14332
+ }
14191
14333
  async function candidatePipReady(engine, python) {
14192
14334
  if (engine.candidatePipReady !== void 0) return engine.candidatePipReady(python);
14193
14335
  const { execFile: execFile4 } = await import('child_process');
@@ -14304,6 +14446,7 @@ async function activatePreparedCandidateLocked(engine, manifest, trial, extras =
14304
14446
  const installedVenvTrouble = candidateVenvTrouble(dir, candidate.version, place, true);
14305
14447
  if (installedVenvTrouble !== null) throw new Error(installedVenvTrouble);
14306
14448
  } catch (trouble) {
14449
+ noteInstallFault("env", trouble);
14307
14450
  return blockCandidate(dir, candidate, `\u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u043D\u0435 \u0433\u043E\u0442\u043E\u0432\u043E \u2014 current \u043D\u0435 \u043C\u0435\u043D\u044F\u043B.
14308
14451
  ${commandWords(trouble)}`);
14309
14452
  }
@@ -14345,6 +14488,7 @@ ${smoke}`);
14345
14488
  { timeout: 5 * 6e4 }
14346
14489
  );
14347
14490
  } catch (trouble) {
14491
+ noteInstallFault("env-check", trouble);
14348
14492
  return blockCandidate(
14349
14493
  dir,
14350
14494
  candidate,
@@ -15830,6 +15974,7 @@ function empty() {
15830
15974
  eta: "",
15831
15975
  file: "",
15832
15976
  outcome: "",
15977
+ say: "",
15833
15978
  trouble: "",
15834
15979
  advice: "",
15835
15980
  complaints: [],
@@ -15840,18 +15985,18 @@ function startedJob(o) {
15840
15985
  return { ...empty(), id: o.id, what: o.what, ahead: o.ahead, message: o.message };
15841
15986
  }
15842
15987
  var INSTALL_TITLES = {
15843
- all: "\u0421\u0442\u0430\u0432\u043B\u044E \u043C\u043E\u043D\u0442\u0430\u0436",
15988
+ all: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u0434\u043B\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0430",
15844
15989
  check: "\u041F\u0440\u043E\u0432\u0435\u0440\u044F\u044E",
15845
- python: "\u0421\u0442\u0430\u0432\u043B\u044E \u043C\u043E\u043D\u0442\u0430\u0436",
15846
- ffmpeg: "\u0421\u0442\u0430\u0432\u043B\u044E \u043C\u043E\u043D\u0442\u0430\u0436",
15847
- env: "\u0421\u0442\u0430\u0432\u043B\u044E \u043C\u043E\u043D\u0442\u0430\u0436",
15848
- model: "\u0421\u0442\u0430\u0432\u043B\u044E \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044B",
15849
- browser: "\u0421\u0442\u0430\u0432\u043B\u044E \u0433\u0440\u0430\u0444\u0438\u043A\u0443",
15850
- cutout: "\u0421\u0442\u0430\u0432\u043B\u044E \u0437\u0430\u043C\u0435\u043D\u0443 \u0444\u043E\u043D\u0430",
15851
- voice: "\u0421\u0442\u0430\u0432\u043B\u044E \u0447\u0438\u0441\u0442\u043A\u0443 \u0433\u043E\u043B\u043E\u0441\u0430"
15990
+ python: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u0434\u043B\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0430",
15991
+ ffmpeg: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u0434\u043B\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0430",
15992
+ env: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u0434\u043B\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0430",
15993
+ model: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044B",
15994
+ browser: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0433\u0440\u0430\u0444\u0438\u043A\u0443",
15995
+ cutout: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0437\u0430\u043C\u0435\u043D\u0443 \u0444\u043E\u043D\u0430",
15996
+ voice: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0447\u0438\u0441\u0442\u043A\u0443 \u0433\u043E\u043B\u043E\u0441\u0430"
15852
15997
  };
15853
15998
  function installTitle(step) {
15854
- return INSTALL_TITLES[step] ?? "\u0421\u0442\u0430\u0432\u043B\u044E \u043C\u043E\u043D\u0442\u0430\u0436";
15999
+ return INSTALL_TITLES[step] ?? "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u0434\u043B\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0430";
15855
16000
  }
15856
16001
  var INSTALL_HEAD = /^установка: (\S+)$/;
15857
16002
  var HEAD = /^Задание (\S+): ?(.*)$/;
@@ -15862,6 +16007,7 @@ var DONE = /^Готово за (\d+(?:[.,]\d+)?) с\.$/;
15862
16007
  var FILE = /^Файл: (.+)$/;
15863
16008
  var FAILED = /^Не получилось за (\d+(?:[.,]\d+)?) с: (.*)$/;
15864
16009
  var ADVICE = /^Что делать: (.+)$/;
16010
+ var SAY = /^Человеку: (.+)$/;
15865
16011
  var ABORTED = /^Работа прервалась/;
15866
16012
  var ABORTED_SEC = /Прошло (\d+(?:[.,]\d+)?) с\./;
15867
16013
  var WAITING = /^Ждёт очереди/;
@@ -15943,7 +16089,7 @@ function readJob(text2) {
15943
16089
  if (failed !== null) {
15944
16090
  job.status = "error";
15945
16091
  job.sec = seconds(failed[1]);
15946
- job.trouble = failed[2] ?? "";
16092
+ job.trouble = withoutPaths(failed[2] ?? "");
15947
16093
  tail2 = null;
15948
16094
  continue;
15949
16095
  }
@@ -15960,15 +16106,20 @@ function readJob(text2) {
15960
16106
  tail2 = null;
15961
16107
  continue;
15962
16108
  }
16109
+ const say = SAY.exec(line);
16110
+ if (say !== null) {
16111
+ job.say = say[1] ?? "";
16112
+ continue;
16113
+ }
15963
16114
  if (WAITING.test(line)) {
15964
16115
  job.status = "waiting";
15965
16116
  job.message = line;
15966
16117
  continue;
15967
16118
  }
15968
- const running = RUNNING.exec(line);
15969
- if (running !== null) {
16119
+ const running2 = RUNNING.exec(line);
16120
+ if (running2 !== null) {
15970
16121
  job.status = "running";
15971
- job.sec = seconds(running[1]);
16122
+ job.sec = seconds(running2[1]);
15972
16123
  continue;
15973
16124
  }
15974
16125
  if (tail2 !== null) {
@@ -15987,6 +16138,9 @@ function readJob(text2) {
15987
16138
  }
15988
16139
  return job;
15989
16140
  }
16141
+ function withoutPaths(text2) {
16142
+ return text2.replace(/(^|[\s(«"])(?:[A-Za-z]:\\|~?\/)[^\s,;()«»"]*/g, "$1").replace(/\(\s*\)/g, "").replace(/\s{2,}/g, " ").replace(/\s+([.,;:])/g, "$1").trim();
16143
+ }
15990
16144
  function seconds(text2) {
15991
16145
  if (text2 === void 0) return 0;
15992
16146
  const value = Number(text2.replace(",", "."));
@@ -16001,6 +16155,8 @@ var FLUSH_EVERY_MS = 6e4;
16001
16155
  var SEND_TIMEOUT_MS = 3e3;
16002
16156
  var JOB_LOG_TAIL = 64 * 1024;
16003
16157
  var PYTHON_TRACEBACK = "Traceback (most recent call last):";
16158
+ var SUPPORT_TOPIC = "https://t.me/c/3763431875/10";
16159
+ var WORDS_GRACE_MS = 1e3;
16004
16160
  var CrashReporter = class {
16005
16161
  #deps;
16006
16162
  #now;
@@ -16039,9 +16195,31 @@ var CrashReporter = class {
16039
16195
  noteHost(host) {
16040
16196
  if (host !== null) this.#host = host;
16041
16197
  }
16042
- /** Отправить отчёт мимо ответа. Для тестов и смоука — `settled()`. */
16198
+ /**
16199
+ * Отправить отчёт. Ответ инструмента ждёт итог через {@link wordsFor}, а
16200
+ * всё прочее — нет; для тестов и смоука — `settled()`.
16201
+ */
16043
16202
  submit(crash) {
16044
- this.#track(this.#deliver(crash));
16203
+ const delivery = this.#deliver(crash).catch(() => "none");
16204
+ this.#track(delivery.then(() => void 0));
16205
+ return delivery;
16206
+ }
16207
+ /**
16208
+ * Итог отправки для слов человеку — не дольше таймаута отправки с запасом.
16209
+ * Не успела — отчёт ещё в пути и либо уйдёт, либо ляжет в очередь: «уйдёт»
16210
+ * правда в обоих случаях.
16211
+ */
16212
+ async outcome(delivery) {
16213
+ let timer;
16214
+ const late = new Promise((done) => {
16215
+ timer = setTimeout(() => done("queued"), (this.#deps.timeoutMs ?? SEND_TIMEOUT_MS) + WORDS_GRACE_MS);
16216
+ timer.unref?.();
16217
+ });
16218
+ try {
16219
+ return await Promise.race([delivery, late]);
16220
+ } finally {
16221
+ clearTimeout(timer);
16222
+ }
16045
16223
  }
16046
16224
  /**
16047
16225
  * Процесс падает: отчёт кладётся на диск СИНХРОННО и уходит на следующем
@@ -16091,10 +16269,19 @@ var CrashReporter = class {
16091
16269
  }
16092
16270
  async #deliver(crash) {
16093
16271
  const report = this.#build(crash);
16094
- if (report === null) return;
16272
+ if (report === null) return this.#queuedAlready(fingerprintOf2(crash.errorType, crash.frames)) ? "queued" : "sent";
16095
16273
  report.versions.ffmpeg = await this.#ffmpegVersion();
16096
16274
  const verdict = await this.#send(report);
16097
- if (verdict === "retry") this.#enqueue(report);
16275
+ if (verdict === "sent") return "sent";
16276
+ if (verdict === "drop") return "none";
16277
+ return this.#enqueue(report) ? "queued" : "none";
16278
+ }
16279
+ #queuedAlready(fingerprint) {
16280
+ try {
16281
+ return readdirSync(this.#queueDir()).some((f) => f.endsWith(`-${fingerprint}.json`));
16282
+ } catch {
16283
+ return false;
16284
+ }
16098
16285
  }
16099
16286
  async #flush() {
16100
16287
  const dir = this.#queueDir();
@@ -16191,6 +16378,7 @@ var CrashReporter = class {
16191
16378
  recent_calls: recentCalls(this.#deps.home)
16192
16379
  };
16193
16380
  }
16381
+ /** Положить отчёт в очередь. `false` — диск не принял, отчёт пропал. */
16194
16382
  #enqueue(report) {
16195
16383
  try {
16196
16384
  const dir = this.#queueDir();
@@ -16203,7 +16391,9 @@ var CrashReporter = class {
16203
16391
  for (const old of files.slice(0, Math.max(0, files.length - QUEUE_CAP))) {
16204
16392
  rmSync(join(dir, old), { force: true });
16205
16393
  }
16394
+ return true;
16206
16395
  } catch {
16396
+ return false;
16207
16397
  }
16208
16398
  }
16209
16399
  #queueDir() {
@@ -16252,49 +16442,83 @@ function withCrashReport(crashes) {
16252
16442
  crashes.noteHost(host);
16253
16443
  crashes.noteSecrets(secretsOf(input));
16254
16444
  let result;
16445
+ let faults;
16255
16446
  try {
16256
- result = await original(input, ...rest);
16447
+ ({ value: result, faults } = await collectInstallFaults(() => original(input, ...rest)));
16257
16448
  } catch (trouble) {
16258
16449
  const crash = fromThrown(trouble, "tool", tool.name, input, host);
16259
- crashes.submit(crash);
16260
- return toolError(brokenWords(crash, crashes));
16450
+ return toolError(await brokenWords(crash, crashes, crashes.submit(crash)));
16451
+ }
16452
+ const install2 = firstInstallCrash(faults);
16453
+ if (install2 !== null) {
16454
+ const crash = { ...install2, where: "tool", tool: tool.name, secrets: secretsOf(input), host };
16455
+ return withNotice(result, await brokenWords(crash, crashes, crashes.submit(crash)));
16261
16456
  }
16262
16457
  return caught(result, tool.name, input, host, crashes);
16263
16458
  }
16264
16459
  };
16265
16460
  };
16266
16461
  }
16267
- function caught(result, name, input, host, crashes) {
16462
+ async function reportInstallFaults(crashes, work) {
16463
+ const { value, faults } = await collectInstallFaults(work);
16464
+ const install2 = firstInstallCrash(faults);
16465
+ if (install2 !== null) {
16466
+ void crashes.submit({ ...install2, stage: `${install2.stage ?? ""} (\u0444\u043E\u043D\u043E\u043C \u043D\u0430 \u0441\u0442\u0430\u0440\u0442\u0435)`, where: "tool", tool: null, secrets: [], host: null });
16467
+ }
16468
+ return value;
16469
+ }
16470
+ function firstInstallCrash(faults) {
16471
+ for (const fault of faults) {
16472
+ const parsed = installCrash(fault);
16473
+ if (parsed !== null) return parsed;
16474
+ }
16475
+ return null;
16476
+ }
16477
+ function withNotice(result, notice) {
16478
+ if (result.type === "error") return { ...result, error: `${result.error}
16479
+
16480
+ ${notice}` };
16481
+ return typeof result.content === "string" ? { ...result, content: `${result.content}
16482
+
16483
+ ${notice}` } : { ...result, content: [...result.content, { type: "text", text: notice }] };
16484
+ }
16485
+ async function caught(result, name, input, host, crashes) {
16268
16486
  if (result.type === "error") {
16269
16487
  const parsed2 = pythonCrash(result.error);
16270
16488
  if (parsed2 === null) return result;
16271
16489
  const crash2 = { ...parsed2, where: "tool", tool: name, stage: null, secrets: secretsOf(input), host };
16272
- crashes.submit(crash2);
16273
- return toolError(brokenWords(crash2, crashes));
16490
+ return toolError(await brokenWords(crash2, crashes, crashes.submit(crash2)));
16274
16491
  }
16275
16492
  const job = result.structured;
16276
16493
  if (job === void 0 || job["contract"] !== JOB_CONTRACT || job["status"] !== "error") return result;
16277
16494
  const id = typeof job["id"] === "string" ? job["id"] : "";
16278
16495
  if (!/^[\w.-]{1,80}$/.test(id) || crashes.jobSeen(id)) return result;
16279
- const parsed = pythonCrash(jobLogTail(crashes.home, id));
16496
+ const fromLog = pythonCrash(jobLogTail(crashes.home, id));
16497
+ const install2 = fromLog === null ? firstInstallCrash(jobInstallFaults(id)) : null;
16498
+ const parsed = fromLog ?? install2;
16280
16499
  if (parsed === null) return result;
16281
16500
  crashes.rememberJob(id);
16282
- const stage = typeof job["now"] === "string" && job["now"] !== "" ? job["now"] : null;
16501
+ const stage = install2?.stage ?? (typeof job["now"] === "string" && job["now"] !== "" ? job["now"] : null);
16283
16502
  const crash = { ...parsed, where: "job", tool: name, stage, secrets: secretsOf(input), host };
16284
- crashes.submit(crash);
16285
- const notice = `
16286
-
16287
- ${brokenWords(crash, crashes)}`;
16288
- return typeof result.content === "string" ? { ...result, content: result.content + notice } : { ...result, content: [...result.content, { type: "text", text: notice.trim() }] };
16503
+ return withNotice(result, await brokenWords(crash, crashes, crashes.submit(crash)));
16289
16504
  }
16290
- function brokenWords(crash, crashes) {
16505
+ async function brokenWords(crash, crashes, delivery) {
16291
16506
  const essence = head(scrub(head(crash.essence, 600), crashes.scrubContext(crash.secrets)), 300);
16507
+ const fate = crashes.canSend ? await crashes.outcome(delivery) : "none";
16292
16508
  return [
16293
- "\u0421\u043B\u043E\u043C\u0430\u043B\u043E\u0441\u044C \u043D\u0430 \u043D\u0430\u0448\u0435\u0439 \u0441\u0442\u043E\u0440\u043E\u043D\u0435, \u043D\u0435 \u0443 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430.",
16294
- crashes.canSend ? `\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0441\u043A\u0430\u0436\u0438, \u0447\u0442\u043E \u0442\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u043E\u0442\u0447\u0451\u0442 \u0443\u0445\u043E\u0434\u0438\u0442 \u043D\u0430\u043C \u0441\u0430\u043C \u0438 \u0440\u0430\u0441\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0443\u0436\u043D\u043E, \u0438 \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0439 \u0435\u0449\u0451 \u0440\u0430\u0437. \u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0441\u044F \u2014 \u043F\u0443\u0441\u0442\u044C \u043D\u0430\u043F\u0438\u0448\u0435\u0442 \u043D\u0430\u043C \u0432 \u0431\u043E\u0442 ${KEY_SOURCE_BOT}.` : `\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0439 \u0435\u0449\u0451 \u0440\u0430\u0437. \u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0441\u044F \u2014 \u043F\u0443\u0441\u0442\u044C \u043D\u0430\u043F\u0438\u0448\u0435\u0442 \u043D\u0430\u043C \u0432 \u0431\u043E\u0442 ${KEY_SOURCE_BOT}.`,
16509
+ `\u0421\u043A\u0430\u0436\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u0442\u0430\u043A, \u0441\u0432\u043E\u0438\u043C\u0438 \u0441\u043B\u043E\u0432\u0430\u043C\u0438 \u043D\u0435 \u043C\u0435\u043D\u044F\u0439: \xAB${humanWords(fate)}\xBB`,
16295
16510
  `\u0421\u0443\u0442\u044C \u0434\u043B\u044F \u0442\u0435\u0431\u044F, \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u043D\u0435 \u0437\u0430\u0447\u0438\u0442\u044B\u0432\u0430\u0439: ${essence}`
16296
16511
  ].join("\n");
16297
16512
  }
16513
+ function humanWords(fate) {
16514
+ if (fate === "sent") {
16515
+ return `\u041F\u0440\u043E\u0438\u0437\u043E\u0448\u043B\u0430 \u043E\u0448\u0438\u0431\u043A\u0430. \u041E\u0442\u0447\u0451\u0442 \u0443\u0436\u0435 \u043E\u0442\u043F\u0440\u0430\u0432\u043B\u0435\u043D \u0440\u0430\u0437\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430\u043C \u2014 \u043C\u044B \u0435\u0451 \u0438\u0441\u043F\u0440\u0430\u0432\u0438\u043C. \u0415\u0441\u043B\u0438 \u043D\u0443\u0436\u043D\u043E \u0441\u0440\u043E\u0447\u043D\u043E \u2014 \u043D\u0430\u043F\u0438\u0448\u0438 \u043D\u0430\u043C: ${SUPPORT_TOPIC}`;
16516
+ }
16517
+ if (fate === "queued") {
16518
+ return `\u041F\u0440\u043E\u0438\u0437\u043E\u0448\u043B\u0430 \u043E\u0448\u0438\u0431\u043A\u0430. \u041E\u0442\u0447\u0451\u0442 \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D \u0438 \u0443\u0439\u0434\u0451\u0442 \u0440\u0430\u0437\u0440\u0430\u0431\u043E\u0442\u0447\u0438\u043A\u0430\u043C, \u043A\u0430\u043A \u043F\u043E\u044F\u0432\u0438\u0442\u0441\u044F \u0441\u0432\u044F\u0437\u044C, \u2014 \u043C\u044B \u0435\u0451 \u0438\u0441\u043F\u0440\u0430\u0432\u0438\u043C. \u0415\u0441\u043B\u0438 \u043D\u0443\u0436\u043D\u043E \u0441\u0440\u043E\u0447\u043D\u043E \u2014 \u043D\u0430\u043F\u0438\u0448\u0438 \u043D\u0430\u043C: ${SUPPORT_TOPIC}`;
16519
+ }
16520
+ return `\u041F\u0440\u043E\u0438\u0437\u043E\u0448\u043B\u0430 \u043E\u0448\u0438\u0431\u043A\u0430. \u041D\u0430\u043F\u0438\u0448\u0438 \u043D\u0430\u043C, \u043C\u044B \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043C\u0441\u044F: ${SUPPORT_TOPIC}`;
16521
+ }
16298
16522
  function watchProcessCrashes(crashes, proc = process, exit = (code) => process.exit(code)) {
16299
16523
  const fall = (trouble) => {
16300
16524
  crashes.persist(fromThrown(trouble, "process", null, {}, null));
@@ -16329,6 +16553,95 @@ function fromThrown(trouble, where, tool, input, host) {
16329
16553
  essence: `${errorType}: ${error40.message}${frames[0] ? ` \u2014 ${frames[0].file}` : ""}`
16330
16554
  };
16331
16555
  }
16556
+ function installCrash(fault) {
16557
+ const { stage, trouble } = fault;
16558
+ if (machineCause(trouble)) return null;
16559
+ const said = commandOutput(trouble);
16560
+ if (said.includes(PYTHON_TRACEBACK)) {
16561
+ const parsed = pythonCrash(said);
16562
+ return parsed === null ? null : { ...parsed, stage };
16563
+ }
16564
+ const error40 = trouble instanceof Error ? trouble : new Error(String(trouble));
16565
+ const reason = lastMeaningfulLine(said) ?? lastMeaningfulLine(error40.message) ?? error40.name;
16566
+ const command = typeof trouble.cmd === "string" ? trouble.cmd : "";
16567
+ const trace = [
16568
+ command === "" ? "" : `\u043A\u043E\u043C\u0430\u043D\u0434\u0430: ${command}`,
16569
+ said.trim() === "" ? "" : `\u0432\u044B\u0432\u043E\u0434 \u043A\u043E\u043C\u0430\u043D\u0434\u044B:
16570
+ ${said.trim().slice(-4e3)}`,
16571
+ error40.stack ?? `${error40.name}: ${error40.message}`
16572
+ ].filter((part) => part !== "").join("\n\n");
16573
+ return {
16574
+ stage,
16575
+ errorType: "InstallError",
16576
+ message: `${stage}: ${reason}`,
16577
+ trace,
16578
+ keep: "head",
16579
+ // ⚠ ПРИЧИНА В ОТПЕЧАТКЕ — БЕЗ ПУТЕЙ И ЧИСЕЛ. Иначе все беды шага склеились
16580
+ // бы в один отпечаток, а сервер пишет в бот про отпечаток раз в час: вторая,
16581
+ // другая беда того же шага промолчала бы. А с путями и версиями одна и та
16582
+ // же беда у двух покупателей считалась бы двумя.
16583
+ frames: [`install:${stage}`, reason.replace(/(?:[A-Za-z]:)?[\\/][^\s'"]*/g, "<path>").replace(/\d+/g, "0").slice(0, 160)],
16584
+ python: /python@?(\d)\.(\d{1,2})\b/i.exec(command)?.slice(1, 3).join(".") ?? null,
16585
+ essence: `\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430, \u0448\u0430\u0433 ${stage}: ${reason}`
16586
+ };
16587
+ }
16588
+ function machineCause(trouble) {
16589
+ const t = trouble ?? {};
16590
+ if (typeof t.code === "string" && MACHINE_CODES.has(t.code)) return true;
16591
+ if (t.code === 2) return true;
16592
+ if (t.killed === true || typeof t.signal === "string" && t.signal !== "") return true;
16593
+ const text2 = `${typeof t.message === "string" ? t.message : String(trouble)}
16594
+ ${commandOutput(trouble)}`;
16595
+ return MACHINE_WORDS.test(text2);
16596
+ }
16597
+ var MACHINE_CODES = /* @__PURE__ */ new Set([
16598
+ "ENOSPC",
16599
+ "EDQUOT",
16600
+ "EACCES",
16601
+ "EPERM",
16602
+ "EROFS",
16603
+ "EBUSY",
16604
+ "ENOTFOUND",
16605
+ "EAI_AGAIN",
16606
+ "ECONNRESET",
16607
+ "ECONNREFUSED",
16608
+ "ETIMEDOUT",
16609
+ "ENETUNREACH",
16610
+ "ENETDOWN",
16611
+ "EHOSTUNREACH"
16612
+ ]);
16613
+ var MACHINE_WORDS = new RegExp([
16614
+ "No space left on device",
16615
+ "Disk quota exceeded",
16616
+ "Permission denied",
16617
+ "Operation not permitted",
16618
+ "Read-only file system",
16619
+ "Access is denied",
16620
+ "Temporary failure in name resolution",
16621
+ "Name or service not known",
16622
+ "nodename nor servname",
16623
+ "getaddrinfo",
16624
+ "Network is unreachable",
16625
+ "No route to host",
16626
+ "Connection refused",
16627
+ "Connection reset",
16628
+ "Connection aborted",
16629
+ "timed out",
16630
+ "Max retries exceeded",
16631
+ "NewConnectionError",
16632
+ "ConnectTimeoutError",
16633
+ "ReadTimeoutError",
16634
+ "ProxyError",
16635
+ "CERTIFICATE_VERIFY_FAILED"
16636
+ ].map((one) => one.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "i");
16637
+ function commandOutput(trouble) {
16638
+ const t = trouble ?? {};
16639
+ return [t.stderr, t.stdout].filter((one) => typeof one === "string" && one.trim() !== "").join("\n");
16640
+ }
16641
+ function lastMeaningfulLine(text2) {
16642
+ const lines = text2.split("\n").map((one) => one.trim()).filter((one) => one !== "" && !/^Command failed:/.test(one));
16643
+ return lines[lines.length - 1] ?? null;
16644
+ }
16332
16645
  function jsFrames(stack) {
16333
16646
  const frames = [];
16334
16647
  for (const line of stack.split("\n")) {
@@ -16526,7 +16839,6 @@ function readReadiness(stdout) {
16526
16839
  var NOT_A_ROW = /* @__PURE__ */ new Set(["system"]);
16527
16840
  var TITLE_READY = "\u0412\u0441\u0451 \u0433\u043E\u0442\u043E\u0432\u043E \u2014 \u043C\u043E\u0436\u043D\u043E \u043D\u0430\u0447\u0438\u043D\u0430\u0442\u044C \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C :)";
16528
16841
  var TITLE_UNKNOWN = "\u041F\u0440\u043E\u0432\u0435\u0440\u044F\u044E, \u0447\u0442\u043E \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E";
16529
- var TITLE_UPDATING = "\u041E\u0431\u043D\u043E\u0432\u043B\u044F\u044E \u043C\u043E\u043D\u0442\u0430\u0436";
16530
16842
  function weight(mb) {
16531
16843
  if (mb >= 1e3) return `${(mb / 1024).toFixed(1).replace(".", ",")} \u0413\u0411`;
16532
16844
  return `${mb} \u041C\u0411`;
@@ -16547,11 +16859,23 @@ function refusalWords(status) {
16547
16859
  if (status === 403) return `\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430 \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u0430\u0441\u044C. \u041F\u0440\u043E\u0434\u043B\u0438 \u0435\u0451 \u0432 \u0442\u0435\u043B\u0435\u0433\u0440\u0430\u043C-\u0431\u043E\u0442\u0435 ${KEY_SOURCE_HANDLE}.`;
16548
16860
  return "";
16549
16861
  }
16862
+ function exactWeight(mb) {
16863
+ if (mb >= 1e3) return weight(mb);
16864
+ return `${mb.toFixed(1).replace(".", ",")} \u041C\u0411`;
16865
+ }
16866
+ function whatIsNew(shell, engine) {
16867
+ const parts = [];
16868
+ for (const [news, name] of [[engine, "\u0434\u0432\u0438\u0436\u043E\u043A"], [shell, "\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435"]]) {
16869
+ if (!news?.fresh) continue;
16870
+ parts.push(news.mb > 0 ? `${name} \xB7 ${exactWeight(news.mb)}` : name);
16871
+ }
16872
+ return parts.join(" \u0438 ");
16873
+ }
16550
16874
  function updateWords(shell, engine) {
16551
16875
  if (!shell?.fresh && !engine?.fresh) return "";
16552
16876
  const notes = shell?.fresh ? firstPhrase(shell.notes) : "";
16553
16877
  return [
16554
- `\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u043D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0451\u0440\u0430.${notes ? ` \u041F\u043E\u044F\u0432\u0438\u043B\u043E\u0441\u044C: ${notes}` : ""}`,
16878
+ `\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u043D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0451\u0440\u0430: ${whatIsNew(shell, engine)}.${notes ? ` \u041F\u043E\u044F\u0432\u0438\u043B\u043E\u0441\u044C: ${notes}` : ""}`,
16555
16879
  "\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0434\u043E \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438?",
16556
16880
  "\u041F\u0440\u043E\u0441\u0442\u043E \u043D\u0430\u043F\u0438\u0448\u0438 \xAB\u043E\u0431\u043D\u043E\u0432\u0438\xBB."
16557
16881
  ].join("\n");
@@ -17370,10 +17694,19 @@ function runbarWidgetHtml() {
17370
17694
  document.body.classList.remove('bad');
17371
17695
  if (!good) document.body.classList.add('bad');
17372
17696
 
17373
- if (good) {
17697
+ if (good && data.file) {
17374
17698
  show('endTitle', '\u0413\u043E\u0442\u043E\u0432\u043E \u0437\u0430 ' + howLong(data.sec));
17375
- show('endHint', data.file ? '\u0420\u043E\u043B\u0438\u043A \u0433\u043E\u0442\u043E\u0432:' : (data.outcome || ''));
17376
- show('path', data.file || '');
17699
+ show('endHint', '\u0420\u043E\u043B\u0438\u043A \u0433\u043E\u0442\u043E\u0432:');
17700
+ show('path', data.file);
17701
+ } else if (good) {
17702
+ // \u26A0 \u0423 \u0420\u0410\u0411\u041E\u0422\u042B \u0411\u0415\u0417 \u0424\u0410\u0419\u041B\u0410 \u2014 \u0424\u0420\u0410\u0417\u0410, \u0410 \u041D\u0415 \u041E\u0422\u0427\u0401\u0422 (\u0432\u043B\u0430\u0434\u0435\u043B\u0435\u0446 2026-09-24). \u041E\u0442\u0447\u0451\u0442
17703
+ // \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438 \u2014 \u0441\u0442\u0440\u043E\u043A\u0438 \u0434\u0432\u0438\u0436\u043A\u0430 \u0441 \u043F\u0443\u0442\u044F\u043C\u0438 \u0438 \u0438\u043C\u0435\u043D\u0430\u043C\u0438 \u0448\u0430\u0433\u043E\u0432, \u043E\u043D \u0434\u043B\u044F \u043C\u043E\u0434\u0435\u043B\u0438.
17704
+ // \xAB\u0413\u043E\u0442\u043E\u0432\u043E. \u041F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438 Claude \u2014 \u2026\xBB \u0434\u0435\u043B\u0438\u0442\u0441\u044F \u043D\u0430 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A \u0438 \u043F\u043E\u0434\u0441\u043A\u0430\u0437\u043A\u0443.
17705
+ var say = data.say || '\u0413\u043E\u0442\u043E\u0432\u043E \u2014 \u043C\u043E\u0436\u043D\u043E \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C.';
17706
+ var cut = /^(.+?)[.!]\\s+(.+)$/.exec(say);
17707
+ show('endTitle', cut ? cut[1] : say.replace(/[.]$/, ''));
17708
+ show('endHint', cut ? cut[2] : '');
17709
+ show('path', '');
17377
17710
  } else {
17378
17711
  show('endTitle', data.status === 'aborted' ? '\u0420\u0430\u0431\u043E\u0442\u0430 \u043F\u0440\u0435\u0440\u0432\u0430\u043B\u0430\u0441\u044C' : '\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C');
17379
17712
  show('endHint', data.trouble || '\u0427\u0442\u043E-\u0442\u043E \u0441\u043B\u043E\u043C\u0430\u043B\u043E\u0441\u044C. \u041D\u0430\u043F\u0438\u0448\u0438 \u041A\u043B\u043E\u0434\u0443 \xAB\u0447\u0442\u043E \u0441\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C\xBB.');
@@ -17723,6 +18056,226 @@ function nameOrRefusal(candidate) {
17723
18056
  };
17724
18057
  }
17725
18058
 
18059
+ // src/setup-job.ts
18060
+ var SETUP_JOB_MARK = "setup-";
18061
+ function isSetupJob(id) {
18062
+ return id.startsWith(SETUP_JOB_MARK);
18063
+ }
18064
+ var TITLE_UPDATE = "\u041E\u0431\u043D\u043E\u0432\u043B\u044F\u044E \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u0434\u043B\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0430";
18065
+ var TITLE_INSTALL = "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u0434\u043B\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0430";
18066
+ var SAY_RESTART = "\u0413\u043E\u0442\u043E\u0432\u043E. \u041F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438 Claude \u2014 \u043D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u0437\u0430\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043F\u043E\u0441\u043B\u0435 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0430.";
18067
+ var SAY_NOTHING = "\u0412\u0441\u0451 \u0443\u0436\u0435 \u0441\u0442\u043E\u0438\u0442 \u2014 \u043C\u043E\u0436\u043D\u043E \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C.";
18068
+ var SAY_INSTALLED = "\u0413\u043E\u0442\u043E\u0432\u043E \u2014 \u043C\u043E\u0436\u043D\u043E \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C.";
18069
+ var ADVICE_ASK = "\u041D\u0430\u043F\u0438\u0448\u0438 \u041A\u043B\u043E\u0434\u0443 \xAB\u0447\u0442\u043E \u0441\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C\xBB \u2014 \u043E\u043D \u0440\u0430\u0441\u0441\u043A\u0430\u0436\u0435\u0442, \u0447\u0442\u043E \u043F\u043E\u0448\u043B\u043E \u043D\u0435 \u0442\u0430\u043A.";
18070
+ var CHECK = "\u041F\u0440\u043E\u0432\u0435\u0440\u044F\u044E, \u0447\u0442\u043E \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C";
18071
+ var INSTALL_NEW = "\u0421\u0442\u0430\u0432\u043B\u044E \u043D\u043E\u0432\u0443\u044E \u0432\u0435\u0440\u0441\u0438\u044E";
18072
+ var MISSING = "\u0421\u0442\u0430\u0432\u043B\u044E \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u044E\u0449\u0435\u0435";
18073
+ var works = /* @__PURE__ */ new Map();
18074
+ function running() {
18075
+ for (const work of works.values()) if (work.ended === null) return work;
18076
+ return null;
18077
+ }
18078
+ function startSetupJob(what, run, clock = Date.now) {
18079
+ const already = running();
18080
+ if (already !== null) return already.id;
18081
+ const started = clock();
18082
+ const id = `${SETUP_JOB_MARK}${new Date(started).toISOString().replace(/[:.]/g, "-")}`;
18083
+ const work = {
18084
+ id,
18085
+ what,
18086
+ started,
18087
+ stages: [CHECK],
18088
+ at: 1,
18089
+ message: "",
18090
+ ended: null,
18091
+ done: Promise.resolve()
18092
+ };
18093
+ const control = {
18094
+ plan(stages) {
18095
+ work.stages = stages;
18096
+ work.at = Math.min(work.at, stages.length);
18097
+ },
18098
+ go(stage) {
18099
+ const index = work.stages.indexOf(stage);
18100
+ if (index >= 0) work.at = index + 1;
18101
+ work.message = "";
18102
+ },
18103
+ line(text2) {
18104
+ work.message = text2;
18105
+ }
18106
+ };
18107
+ works.set(id, work);
18108
+ work.done = collectJobInstallFaults(id, () => run(control).catch((trouble) => {
18109
+ noteInstallFault("update-job", trouble);
18110
+ return {
18111
+ ok: false,
18112
+ trouble: "\u0427\u0442\u043E-\u0442\u043E \u0441\u043B\u043E\u043C\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u0441\u0440\u0435\u0434\u0438 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u2014 \u043F\u0440\u0435\u0436\u043D\u044F\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u043D\u0430 \u043C\u0435\u0441\u0442\u0435, \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043C\u043E\u0436\u043D\u043E.",
18113
+ advice: ADVICE_ASK,
18114
+ report: String(trouble instanceof Error ? trouble.message : trouble)
18115
+ };
18116
+ })).then((ending) => {
18117
+ work.ended = { at: clock(), ending };
18118
+ });
18119
+ return id;
18120
+ }
18121
+ function quiet(line) {
18122
+ return line.replace(/^⚠\s*/, "").replace(/^Что делать:/, "\u0421\u043E\u0432\u0435\u0442:");
18123
+ }
18124
+ function setupJobText(id, clock = Date.now) {
18125
+ const work = works.get(id);
18126
+ if (work === void 0) return null;
18127
+ const head2 = `\u0417\u0430\u0434\u0430\u043D\u0438\u0435 ${work.id}: ${work.what}`;
18128
+ if (work.ended === null) {
18129
+ const sec2 = Math.max(0, Math.round((clock() - work.started) / 1e3));
18130
+ return [
18131
+ head2,
18132
+ `\u042D\u0442\u0430\u043F ${work.at} \u0438\u0437 ${work.stages.length}: ${work.stages[work.at - 1] ?? CHECK}`,
18133
+ ...work.message === "" ? [] : [work.message],
18134
+ `\u0415\u0449\u0451 \u0441\u0447\u0438\u0442\u0430\u0435\u0442, ${sec2} \u0441.`
18135
+ ].join("\n");
18136
+ }
18137
+ const sec = Math.max(0, Math.round((work.ended.at - work.started) / 1e3));
18138
+ const ending = work.ended.ending;
18139
+ const report = ending.report.split("\n").map((line) => line.trim()).filter((line) => line !== "").map(quiet);
18140
+ if (ending.ok) {
18141
+ return [head2, `\u0413\u043E\u0442\u043E\u0432\u043E \u0437\u0430 ${sec} \u0441.`, `\u0427\u0435\u043B\u043E\u0432\u0435\u043A\u0443: ${ending.say}`, ...report].join("\n");
18142
+ }
18143
+ return [
18144
+ head2,
18145
+ ...report,
18146
+ `\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C \u0437\u0430 ${sec} \u0441: ${ending.trouble}`,
18147
+ `\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: ${ending.advice}`
18148
+ ].join("\n");
18149
+ }
18150
+ function setupJobReport(id) {
18151
+ const text2 = setupJobText(id);
18152
+ return text2 === null ? null : readJob(text2);
18153
+ }
18154
+ function weightExact(bytes) {
18155
+ const mb = bytes / 1024 / 1024;
18156
+ if (mb >= 1e3) return `${(mb / 1024).toFixed(1).replace(".", ",")} \u0413\u0411`;
18157
+ return `${mb.toFixed(1).replace(".", ",")} \u041C\u0411`;
18158
+ }
18159
+ async function runUpdate(job, deps) {
18160
+ const { engine, key, baseUrl, what } = deps;
18161
+ const home = engine.home();
18162
+ const wantShell = what !== "engine";
18163
+ const hasEngine = engine.place() !== null;
18164
+ const wantEngine = what !== "shell" && hasEngine && !engine.pathSetByHand();
18165
+ const shellUrl = engine.variable("NAPARNIK_BUNDLE_MANIFEST") ?? bundleUrlFor(baseUrl);
18166
+ const [shellAsk, engineAsk] = await Promise.all([
18167
+ wantShell ? fetchShellManifest(shellUrl, deps.pointerMs, key) : Promise.resolve(null),
18168
+ wantEngine ? fetchManifest(manifestUrl(engine, baseUrl), deps.pointerMs, key) : Promise.resolve(null)
18169
+ ]);
18170
+ const shellWas = installedShell(home);
18171
+ const shell = decideShell(shellWas, shellAsk?.ok === true ? shellAsk.manifest : null);
18172
+ const engineWas = engine.freshVersion();
18173
+ const fresh = decide(engineWas, engineAsk?.ok === true ? engineAsk.manifest : null);
18174
+ const shellStage = shell.what === "newer_available" ? `\u0421\u043A\u0430\u0447\u0438\u0432\u0430\u044E \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435 \xB7 ${weightExact(shell.manifest.body?.bytes ?? 0)}` : null;
18175
+ const engineStage = fresh.what === "newer_available" ? `\u0421\u043A\u0430\u0447\u0438\u0432\u0430\u044E \u0434\u0432\u0438\u0436\u043E\u043A \xB7 ${weightExact(fresh.manifest.bytes)}` : null;
18176
+ const missing = what === "all" && engineStage === null;
18177
+ job.plan([
18178
+ CHECK,
18179
+ ...shellStage === null ? [] : [shellStage],
18180
+ ...engineStage === null ? [] : [engineStage, INSTALL_NEW],
18181
+ ...missing ? [MISSING] : []
18182
+ ]);
18183
+ const steps = [];
18184
+ let updated = false;
18185
+ let installed = false;
18186
+ for (const [ask2, whose] of [[shellAsk, "\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F"], [engineAsk, "\u0434\u0432\u0438\u0436\u043A\u0430"]]) {
18187
+ if (ask2 === null || ask2.ok) continue;
18188
+ const refusal = refusalWords(ask2.status ?? 0);
18189
+ steps.push({
18190
+ report: `\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C, \u0435\u0441\u0442\u044C \u043B\u0438 \u043D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F ${whose}: ${ask2.why}.`,
18191
+ trouble: refusal !== "" ? refusal : "\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C \u0441\u0432\u044F\u0437\u0430\u0442\u044C\u0441\u044F \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043E\u043C \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0439."
18192
+ });
18193
+ }
18194
+ if (shellAsk?.ok === true && shell.what === "nothing") {
18195
+ steps.push({ report: `\u0423 \u0432\u0430\u0441 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u0441\u0430\u043C\u0430\u044F \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u2014 ${shellWas}.` });
18196
+ }
18197
+ if (shell.what === "launcher_too_old") {
18198
+ steps.push({
18199
+ report: `\u041D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F ${shell.manifest.version} \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043D\u043E\u0432\u043E\u0433\u043E \u0444\u0430\u0439\u043B\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F.`,
18200
+ trouble: `\u0414\u043B\u044F \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043D\u0443\u0436\u0435\u043D \u043D\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F. \u0412\u043E\u0437\u044C\u043C\u0438 \u0435\u0433\u043E \u0432 \u0442\u0435\u043B\u0435\u0433\u0440\u0430\u043C-\u0431\u043E\u0442\u0435 ${KEY_SOURCE_BOT}.`
18201
+ });
18202
+ }
18203
+ if (fresh.what === "shell_too_old") {
18204
+ steps.push({
18205
+ report: `\u041D\u043E\u0432\u0430\u044F \u043D\u0430\u0447\u0438\u043D\u043A\u0430 ${fresh.manifest.version} \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u043D\u0435 \u0441\u0442\u0430\u0440\u0448\u0435 ${fresh.manifest.minShellVersion}.`,
18206
+ trouble: "\u041D\u043E\u0432\u043E\u043C\u0443 \u0434\u0432\u0438\u0436\u043A\u0443 \u043D\u0443\u0436\u043D\u043E \u043D\u043E\u0432\u043E\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435 \u2014 \u0435\u0433\u043E \u043F\u043E\u043A\u0430 \u043D\u0435\u0442 \u043D\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435."
18207
+ });
18208
+ }
18209
+ if (shell.what === "newer_available" && shellStage !== null) {
18210
+ job.go(shellStage);
18211
+ const outcome = await installShell({ home, manifest: shell.manifest, key: key ?? null });
18212
+ if (outcome.ok) {
18213
+ updated = true;
18214
+ steps.push({ report: outcome.text });
18215
+ } else {
18216
+ steps.push({ report: outcome.text, trouble: "\u041D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u043D\u0435 \u0432\u0441\u0442\u0430\u043B\u0430 \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043F\u0440\u0435\u0436\u043D\u044F\u044F." });
18217
+ }
18218
+ }
18219
+ if (fresh.what === "newer_available" && engineStage !== null) {
18220
+ job.go(engineStage);
18221
+ const outcome = await deploy(engine, fresh.manifest, void 0, key, {
18222
+ line: (text2) => job.line(text2),
18223
+ downloaded: () => {
18224
+ job.go(INSTALL_NEW);
18225
+ job.line("\u0421\u043E\u0431\u0438\u0440\u0430\u044E \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u2014 \u044D\u0442\u043E \u0434\u043E \u043F\u0430\u0440\u044B \u043C\u0438\u043D\u0443\u0442.");
18226
+ }
18227
+ });
18228
+ if (outcome.ok) {
18229
+ updated = true;
18230
+ steps.push({ report: outcome.text });
18231
+ } else {
18232
+ steps.push({
18233
+ report: outcome.text,
18234
+ trouble: `\u041D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u0434\u0432\u0438\u0436\u043A\u0430 \u043D\u0435 \u0432\u0441\u0442\u0430\u043B\u0430 \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043F\u0440\u0435\u0436\u043D\u044F\u044F${engineWas === null ? "" : ` ${engineWas}`}, \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043C\u043E\u0436\u043D\u043E.`
18235
+ });
18236
+ }
18237
+ }
18238
+ if (missing) {
18239
+ job.go(MISSING);
18240
+ const step = await deliverMissing(job, deps);
18241
+ if (step.trouble === void 0 && step.installed) installed = true;
18242
+ steps.push(step);
18243
+ }
18244
+ const report = steps.map((step) => step.report).join("\n");
18245
+ const trouble = steps.find((step) => step.trouble !== void 0)?.trouble;
18246
+ if (trouble !== void 0) return { ok: false, trouble, advice: ADVICE_ASK, report };
18247
+ if (updated) return { ok: true, say: SAY_RESTART, report };
18248
+ return { ok: true, say: installed ? SAY_INSTALLED : SAY_NOTHING, report };
18249
+ }
18250
+ async function deliverMissing(job, deps) {
18251
+ const answer = await deps.installMissing();
18252
+ if (answer.type === "error") {
18253
+ return { report: answer.error, trouble: "\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u044E\u0449\u0435\u0435.", installed: false };
18254
+ }
18255
+ const words = typeof answer.content === "string" ? answer.content : answer.content.map((block) => block.type === "text" ? block.text : "").join("\n");
18256
+ const id = typeof answer.structured?.["id"] === "string" ? answer.structured["id"] : "";
18257
+ if (id === "") return { report: words, installed: false };
18258
+ const pace = deps.pace ?? realPace;
18259
+ let state = await deps.engine.command(["job", id]);
18260
+ while (state.ok && stillWorking(state.text)) {
18261
+ const now = readJob(state.text);
18262
+ if (now !== null) job.line(now.message !== "" ? now.message : now.now);
18263
+ await pace.rest(POLL_EVERY_MS);
18264
+ state = await deps.engine.command(["job", id]);
18265
+ }
18266
+ const end = state.ok ? readJob(state.text) : null;
18267
+ if (end === null || end.status !== "done") {
18268
+ return {
18269
+ report: `${words}
18270
+ ${state.text}`,
18271
+ trouble: end?.trouble !== void 0 && end.trouble !== "" ? end.trouble : "\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u044E\u0449\u0435\u0435.",
18272
+ installed: false
18273
+ };
18274
+ }
18275
+ return { report: `${words}
18276
+ ${state.text}`, installed: true };
18277
+ }
18278
+
17726
18279
  // src/setup-tool.ts
17727
18280
  var ASKED_POINTER_MS = 15e3;
17728
18281
  var NEWS_POINTER_MS = 5e3;
@@ -17876,6 +18429,9 @@ ${EXTRAS_LATER}`), later]);
17876
18429
  })
17877
18430
  );
17878
18431
  }
18432
+ function tenths(bytes) {
18433
+ return Math.round(bytes / 1024 / 1024 * 10) / 10;
18434
+ }
17879
18435
  async function updateEngine(engine, consent, key, baseUrl, timeoutMs = ASKED_POINTER_MS, seen = () => {
17880
18436
  }) {
17881
18437
  if (engine.pathSetByHand()) {
@@ -17893,7 +18449,7 @@ async function updateEngine(engine, consent, key, baseUrl, timeoutMs = ASKED_POI
17893
18449
  seen({
17894
18450
  installed: engine.freshVersion(),
17895
18451
  fresh: decision.what === "nothing" ? null : decision.manifest.version,
17896
- mb: decision.what === "nothing" ? 0 : Math.round(decision.manifest.bytes / 1024 / 1024),
18452
+ mb: decision.what === "nothing" ? 0 : tenths(decision.manifest.bytes),
17897
18453
  status: outcome.ok ? 0 : outcome.status ?? 0,
17898
18454
  notes: ""
17899
18455
  });
@@ -17928,7 +18484,7 @@ async function updateShell(engine, consent, key, baseUrl, timeoutMs = ASKED_POIN
17928
18484
  seen({
17929
18485
  installed,
17930
18486
  fresh: decision.what === "nothing" ? null : decision.manifest.version,
17931
- mb: decision.what === "nothing" ? 0 : Math.round((decision.manifest.body?.bytes ?? 0) / 1024 / 1024),
18487
+ mb: decision.what === "nothing" ? 0 : tenths(decision.manifest.body?.bytes ?? 0),
17932
18488
  status: outcome.ok ? 0 : outcome.status ?? 0,
17933
18489
  notes: decision.what === "nothing" ? "" : decision.manifest.notes ?? ""
17934
18490
  });
@@ -17939,7 +18495,7 @@ async function updateShell(engine, consent, key, baseUrl, timeoutMs = ASKED_POIN
17939
18495
  );
17940
18496
  }
17941
18497
  return toolSuccess(
17942
- outcome.manifest.body === null ? `\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E ${installed}; \u043D\u0430 \u0440\u0430\u0437\u0434\u0430\u0447\u0435 ${outcome.manifest.version}, \u043D\u043E \u0444\u0430\u0439\u043B\u0430 \u0434\u043B\u044F \u0441\u0430\u043C\u043E\u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u0442\u0430\u043C \u043F\u043E\u043A\u0430 \u043D\u0435\u0442. \u041D\u0438\u0447\u0435\u0433\u043E \u0434\u0435\u043B\u0430\u0442\u044C \u043D\u0435 \u043D\u0443\u0436\u043D\u043E, \u043E\u043D \u043F\u043E\u044F\u0432\u0438\u0442\u0441\u044F \u0441 \u0431\u043B\u0438\u0436\u0430\u0439\u0448\u0438\u043C \u0432\u044B\u043A\u0430\u0442\u043E\u043C.` : `\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435 \u0441\u0432\u0435\u0436\u0435\u0435: \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E ${installed}, \u043D\u043E\u0432\u0435\u0435 \u043D\u0435 \u0432\u044B\u0448\u043B\u043E.`
18498
+ outcome.manifest.body === null ? `\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E ${installed}; \u043D\u0430 \u0440\u0430\u0437\u0434\u0430\u0447\u0435 ${outcome.manifest.version}, \u043D\u043E \u0444\u0430\u0439\u043B\u0430 \u0434\u043B\u044F \u0441\u0430\u043C\u043E\u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u0442\u0430\u043C \u043F\u043E\u043A\u0430 \u043D\u0435\u0442. \u041D\u0438\u0447\u0435\u0433\u043E \u0434\u0435\u043B\u0430\u0442\u044C \u043D\u0435 \u043D\u0443\u0436\u043D\u043E, \u043E\u043D \u043F\u043E\u044F\u0432\u0438\u0442\u0441\u044F \u0441 \u0431\u043B\u0438\u0436\u0430\u0439\u0448\u0438\u043C \u0432\u044B\u043A\u0430\u0442\u043E\u043C.` : `\u0423 \u0432\u0430\u0441 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u0441\u0430\u043C\u0430\u044F \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044F\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u2014 ${installed}.`
17943
18499
  );
17944
18500
  }
17945
18501
  if (decision.what === "launcher_too_old") {
@@ -17972,7 +18528,7 @@ function setupTool(engine, passport, key, baseUrl) {
17972
18528
  return defineTool({
17973
18529
  name: "setup",
17974
18530
  title: "\u041F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0432\u0441\u0451 \u0434\u043B\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0430",
17975
- description: "\u0412\u0441\u0451, \u0447\u0435\u043C \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435, \u2014 \u043E\u0434\u043D\u0438\u043C \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u043C. \u0411\u0435\u0437 \u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u043E\u0432 \u041F\u0420\u041E\u0412\u0415\u0420\u042F\u0415\u0422 \u0434\u0435\u043B\u043E\u043C: \u0447\u0442\u043E \u0441\u0442\u043E\u0438\u0442 \u0438 \u0447\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442, \u0447\u0442\u043E \u0434\u043E\u043A\u0430\u0447\u0430\u0435\u0442\u0441\u044F \u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435 \u0438 \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0432\u0435\u0441\u0438\u0442, \u0447\u0442\u043E \u0443\u043C\u0435\u0435\u0442 \u043C\u043E\u043D\u0442\u0430\u0436 (\u0448\u0430\u0433\u0438, \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0434\u043B\u044F \u043A\u0430\u0440\u0442\u043E\u0447\u0435\u043A choose_variant, \u0447\u0435\u0439 \u0440\u0435\u0446\u0435\u043F\u0442 \u0438 \u0447\u0435\u0439 \u0441\u0442\u0438\u043B\u044C) \u0438 \u0432\u044B\u0448\u043B\u0430 \u043B\u0438 \u043D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u0434\u0432\u0438\u0436\u043A\u0430 \u0438\u043B\u0438 \u0441\u0430\u043C\u043E\u0433\u043E \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F. \u0417\u043E\u0432\u0438 \u041F\u0415\u0420\u0412\u042B\u041C, \u0434\u043E \u043B\u044E\u0431\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u044B \u0441 \u0432\u0438\u0434\u0435\u043E, \u0438 \u0438\u0437 \u0441\u0432\u043E\u0435\u0439 \u043F\u0430\u043C\u044F\u0442\u0438 \u043E\u0431 \u044D\u0442\u043E\u043C \u043D\u0435 \u0440\u0430\u0441\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0439.\n\u0421\u0442\u0430\u0432\u0438\u0442 \u0438 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0435\u0442 \u0441 what, \u043D\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \xAB\u0434\u0430\xBB \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430: \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u0441\u043A\u0430\u0436\u0438 \u0435\u043C\u0443 \u0441\u043B\u043E\u0432\u0430\u043C\u0438, \u0447\u0442\u043E \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0448\u044C \u0438 \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u044D\u0442\u043E \u0432\u0435\u0441\u0438\u0442, \u043F\u043E\u0442\u043E\u043C \u0437\u043E\u0432\u0438 \u0441 \u0442\u0435\u043C \u0436\u0435 what \u0438 consent=true. \xAB\u0421\u043A\u0430\u0447\u0430\u0439\xBB \u0438 \xAB\u043E\u0431\u043D\u043E\u0432\u0438\xBB \u2014 \u0442\u043E \u0436\u0435 \xAB\u0434\u0430\xBB \u043D\u0430 \u0442\u043E, \u0447\u0442\u043E \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0443\u0436\u0435 \u043D\u0430\u0437\u0432\u0430\u043B\u0430 \u0441 \u0432\u0435\u0441\u043E\u043C: \u0437\u043E\u0432\u0438 what=all \u0441 consent=true. \u0411\u0435\u0437 consent \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043A\u0430\u0447\u0430\u0435\u0442\u0441\u044F \u2014 \u043F\u0440\u0438\u0434\u0443\u0442 \u0432\u0435\u0441 \u0438 \u0432\u043E\u043F\u0440\u043E\u0441. \u0422\u044F\u0436\u0451\u043B\u043E\u0435 \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044F \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0434 \u043F\u0440\u043E\u0441\u044C\u0431\u0443: \u043D\u0435 \u043F\u0440\u043E\u0441\u0438\u043B\u0438 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044B \u2014 \u043D\u0435 \u0441\u0442\u0430\u0432\u044C speech, \u043D\u0435 \u043F\u0440\u043E\u0441\u0438\u043B\u0438 \u043D\u043E\u0432\u044B\u0439 \u0444\u043E\u043D \u2014 \u043D\u0435 \u0441\u0442\u0430\u0432\u044C cutout.\n\u0414\u043E\u043B\u0433\u0430\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043D\u043E\u043C\u0435\u0440\u043E\u043C \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u0435\u0451 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u043C wait. \u041D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u0434\u0432\u0438\u0436\u043A\u0430 \u0438\u043B\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u0437\u0430\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043F\u043E\u0441\u043B\u0435 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u041A\u043B\u043E\u0434\u0430 \u0438\u043B\u0438 Codex, \u0441\u043A\u0430\u0436\u0438 \u044D\u0442\u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443. \u0421\u043B\u043E\u043C\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u0441\u043B\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u2014 rollback. \u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430 \u043D\u0435 \u043D\u0443\u0436\u043D\u0430: \u0443\u0437\u043D\u0430\u0442\u044C, \u0437\u0430 \u0447\u0442\u043E \u043F\u043B\u0430\u0442\u044F\u0442, \u043C\u043E\u0436\u043D\u043E \u0434\u043E \u043E\u043F\u043B\u0430\u0442\u044B, \u0430 \u0441\u043A\u0430\u0447\u0438\u0432\u0430\u043D\u0438\u0435 \u0441\u0430\u043C\u043E \u0443\u043F\u0438\u0440\u0430\u0435\u0442\u0441\u044F \u0432 \u043A\u043B\u044E\u0447 \u043D\u0430 \u0440\u0430\u0437\u0434\u0430\u0447\u0435.",
18531
+ description: "\u0412\u0441\u0451, \u0447\u0435\u043C \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435, \u2014 \u043E\u0434\u043D\u0438\u043C \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u043C. \u0411\u0435\u0437 \u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u043E\u0432 \u041F\u0420\u041E\u0412\u0415\u0420\u042F\u0415\u0422 \u0434\u0435\u043B\u043E\u043C: \u0447\u0442\u043E \u0441\u0442\u043E\u0438\u0442 \u0438 \u0447\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442, \u0447\u0442\u043E \u0434\u043E\u043A\u0430\u0447\u0430\u0435\u0442\u0441\u044F \u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435 \u0438 \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0432\u0435\u0441\u0438\u0442, \u0447\u0442\u043E \u0443\u043C\u0435\u0435\u0442 \u043C\u043E\u043D\u0442\u0430\u0436 (\u0448\u0430\u0433\u0438, \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0434\u043B\u044F \u043A\u0430\u0440\u0442\u043E\u0447\u0435\u043A choose_variant, \u0447\u0435\u0439 \u0440\u0435\u0446\u0435\u043F\u0442 \u0438 \u0447\u0435\u0439 \u0441\u0442\u0438\u043B\u044C) \u0438 \u0432\u044B\u0448\u043B\u0430 \u043B\u0438 \u043D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u0434\u0432\u0438\u0436\u043A\u0430 \u0438\u043B\u0438 \u0441\u0430\u043C\u043E\u0433\u043E \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F. \u0417\u043E\u0432\u0438 \u041F\u0415\u0420\u0412\u042B\u041C, \u0434\u043E \u043B\u044E\u0431\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u044B \u0441 \u0432\u0438\u0434\u0435\u043E, \u0438 \u0438\u0437 \u0441\u0432\u043E\u0435\u0439 \u043F\u0430\u043C\u044F\u0442\u0438 \u043E\u0431 \u044D\u0442\u043E\u043C \u043D\u0435 \u0440\u0430\u0441\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0439.\n\u0421\u0442\u0430\u0432\u0438\u0442 \u0438 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0435\u0442 \u0441 what, \u043D\u043E \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \xAB\u0434\u0430\xBB \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430: \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u0441\u043A\u0430\u0436\u0438 \u0435\u043C\u0443 \u0441\u043B\u043E\u0432\u0430\u043C\u0438, \u0447\u0442\u043E \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0448\u044C \u0438 \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u044D\u0442\u043E \u0432\u0435\u0441\u0438\u0442, \u043F\u043E\u0442\u043E\u043C \u0437\u043E\u0432\u0438 \u0441 \u0442\u0435\u043C \u0436\u0435 what \u0438 consent=true. \xAB\u0421\u043A\u0430\u0447\u0430\u0439\xBB \u0438 \xAB\u043E\u0431\u043D\u043E\u0432\u0438\xBB \u2014 \u0442\u043E \u0436\u0435 \xAB\u0434\u0430\xBB \u043D\u0430 \u0442\u043E, \u0447\u0442\u043E \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0443\u0436\u0435 \u043D\u0430\u0437\u0432\u0430\u043B\u0430 \u0441 \u0432\u0435\u0441\u043E\u043C: \u0437\u043E\u0432\u0438 what=all \u0441 consent=true \u041E\u0414\u0418\u041D \u0440\u0430\u0437 \u2014 \u043E\u0442\u0432\u0435\u0442 \u043D\u043E\u043C\u0435\u0440\u043E\u043C \u0437\u0430\u0434\u0430\u0447\u0438, \u0434\u0430\u043B\u044C\u0448\u0435 wait, setup \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E \u043D\u0435 \u0437\u043E\u0432\u0438. \u0411\u0435\u0437 consent \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043A\u0430\u0447\u0430\u0435\u0442\u0441\u044F \u2014 \u043F\u0440\u0438\u0434\u0443\u0442 \u0432\u0435\u0441 \u0438 \u0432\u043E\u043F\u0440\u043E\u0441. \u0422\u044F\u0436\u0451\u043B\u043E\u0435 \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044F \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0434 \u043F\u0440\u043E\u0441\u044C\u0431\u0443: \u043D\u0435 \u043F\u0440\u043E\u0441\u0438\u043B\u0438 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044B \u2014 \u043D\u0435 \u0441\u0442\u0430\u0432\u044C speech, \u043D\u0435 \u043F\u0440\u043E\u0441\u0438\u043B\u0438 \u043D\u043E\u0432\u044B\u0439 \u0444\u043E\u043D \u2014 \u043D\u0435 \u0441\u0442\u0430\u0432\u044C cutout.\n\u0414\u043E\u043B\u0433\u0430\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043D\u043E\u043C\u0435\u0440\u043E\u043C \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u0435\u0451 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u043C wait. \u041D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u0434\u0432\u0438\u0436\u043A\u0430 \u0438\u043B\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u0437\u0430\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043F\u043E\u0441\u043B\u0435 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u041A\u043B\u043E\u0434\u0430 \u0438\u043B\u0438 Codex, \u0441\u043A\u0430\u0436\u0438 \u044D\u0442\u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443. \u0421\u043B\u043E\u043C\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u0441\u043B\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F \u2014 rollback. \u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430 \u043D\u0435 \u043D\u0443\u0436\u043D\u0430: \u0443\u0437\u043D\u0430\u0442\u044C, \u0437\u0430 \u0447\u0442\u043E \u043F\u043B\u0430\u0442\u044F\u0442, \u043C\u043E\u0436\u043D\u043E \u0434\u043E \u043E\u043F\u043B\u0430\u0442\u044B, \u0430 \u0441\u043A\u0430\u0447\u0438\u0432\u0430\u043D\u0438\u0435 \u0441\u0430\u043C\u043E \u0443\u043F\u0438\u0440\u0430\u0435\u0442\u0441\u044F \u0432 \u043A\u043B\u044E\u0447 \u043D\u0430 \u0440\u0430\u0437\u0434\u0430\u0447\u0435.",
17976
18532
  // Полоса хода работы в ленте: установка качает десятки минут, и человек
17977
18533
  // видит, что качается и сколько уже скачано. ⚠ Ожидание задачи — у
17978
18534
  // отдельного инструмента `wait`, без полосы: полоса объявлена на
@@ -18024,9 +18580,6 @@ function setupTool(engine, passport, key, baseUrl) {
18024
18580
  engine: news.engine ?? { installed: engine.freshVersion(), fresh: null, mb: 0, status: 0, notes: "" },
18025
18581
  shell: news.shell,
18026
18582
  note: firstLine(said),
18027
- // Проверки здесь не было, а работа была: «Проверяю, что установлено»
18028
- // над ответом «поставил новую версию» соврало бы о том, что идёт.
18029
- ...args.consent === true && args.what !== void 0 && UPDATES.has(args.what) ? { title: TITLE_UPDATING } : {},
18030
18583
  shellVersion: VERSION
18031
18584
  }));
18032
18585
  }
@@ -18037,6 +18590,7 @@ function setupTool(engine, passport, key, baseUrl) {
18037
18590
  const consent = args.consent === true;
18038
18591
  const what = args.what;
18039
18592
  if (what === void 0) return lookAround(engine, args.model, key, baseUrl);
18593
+ if (consent && UPDATES.has(what)) return startUpdate(engine, what, key, baseUrl);
18040
18594
  if (what === "engine") {
18041
18595
  return updateEngine(engine, consent, key, baseUrl, ASKED_POINTER_MS, (seen) => {
18042
18596
  if (!consent) news.engine = seen;
@@ -18048,7 +18602,7 @@ function setupTool(engine, passport, key, baseUrl) {
18048
18602
  });
18049
18603
  }
18050
18604
  if (what === "all") {
18051
- return consent ? installEverything(engine, key, baseUrl, tell) : lookAround(engine, args.model, key, baseUrl);
18605
+ return lookAround(engine, args.model, key, baseUrl);
18052
18606
  }
18053
18607
  const step = HEAVY_STEPS[what];
18054
18608
  return consent ? installStep(engine, { step, model: args.model, consent: true }, key, baseUrl, tell) : askBeforeHeavy(engine, step, args.model, key, baseUrl);
@@ -18094,7 +18648,20 @@ async function lookAround(engine, model, key, baseUrl) {
18094
18648
  updateEngine(engine, false, key, baseUrl, NEWS_POINTER_MS, toEngine),
18095
18649
  shell
18096
18650
  ]);
18097
- return card(together([machine.result, news, own]), { readiness: machine.readiness });
18651
+ return card(
18652
+ together([newsForModel(shellNews, engineNews), machine.result, news, own]),
18653
+ { readiness: machine.readiness }
18654
+ );
18655
+ }
18656
+ function newsForModel(shell, engine) {
18657
+ if (!shell?.fresh && !engine?.fresh) return toolSuccess("");
18658
+ const numbers = [
18659
+ engine?.fresh ? `\u0434\u0432\u0438\u0436\u043E\u043A ${engine.installed ?? "\u043D\u0435\u0442"} \u2192 ${engine.fresh}` : "",
18660
+ shell?.fresh ? `\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435 ${shell.installed ?? VERSION} \u2192 ${shell.fresh}` : ""
18661
+ ].filter((one) => one !== "").join(", ");
18662
+ return toolSuccess(
18663
+ `\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u0430 \u043D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0451\u0440\u0430: ${whatIsNew(shell, engine)} (${numbers}). \u0421\u043A\u0430\u0436\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443, \u0447\u0442\u043E \u0438\u043C\u0435\u043D\u043D\u043E \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u0441\u044F \u0438 \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0432\u0435\u0441\u0438\u0442. \xAB\u041E\u0431\u043D\u043E\u0432\u0438\xBB \u2014 \u044D\u0442\u043E \xAB\u0434\u0430\xBB: \u043E\u0434\u0438\u043D \u0432\u044B\u0437\u043E\u0432 setup \u0441 what=all \u0438 consent=true, \u0434\u0430\u043B\u044C\u0448\u0435 wait \u043F\u043E \u043D\u043E\u043C\u0435\u0440\u0443 \u0437\u0430\u0434\u0430\u0447\u0438.`
18664
+ );
18098
18665
  }
18099
18666
  function withCard(result, card) {
18100
18667
  if (result.type !== "success" || result.structured !== void 0) return result;
@@ -18104,11 +18671,22 @@ function firstLine(result) {
18104
18671
  const text2 = result.type === "success" ? wordsOf(result.content) : result.error;
18105
18672
  return text2.split("\n").map((line) => line.trim()).find((line) => line !== "") ?? "";
18106
18673
  }
18107
- async function installEverything(engine, key, baseUrl, tell) {
18108
- const said = [await updateShell(engine, true, key, baseUrl)];
18109
- if (engine.place() !== null) said.push(await updateEngine(engine, true, key, baseUrl));
18110
- said.push(await installMissing(engine, key, baseUrl, tell));
18111
- return together(said);
18674
+ function startUpdate(engine, what, key, baseUrl) {
18675
+ const title = engine.place() === null ? TITLE_INSTALL : TITLE_UPDATE;
18676
+ const id = startSetupJob(title, (job) => runUpdate(job, {
18677
+ engine,
18678
+ key,
18679
+ baseUrl,
18680
+ what,
18681
+ pointerMs: ASKED_POINTER_MS,
18682
+ installMissing: () => installMissing(engine, key, baseUrl, void 0)
18683
+ }));
18684
+ const report = setupJobReport(id);
18685
+ return toolSuccess(
18686
+ `\u0417\u0430\u0434\u0430\u0447\u0430 ${id}: ${report?.what ?? title}.
18687
+ \u0418\u0434\u0451\u0442 \u043D\u0430 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F: \u043F\u0440\u043E\u0432\u0435\u0440\u044E, \u0447\u0442\u043E \u0432\u044B\u0448\u043B\u043E, \u0441\u043A\u0430\u0447\u0430\u044E \u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E. \u041F\u043E\u043B\u043E\u0441\u0430 \u0443 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0441\u0430\u043C\u0430. \u041F\u043E\u0437\u043E\u0432\u0438 wait \u0441 job "` + id + '" \u2014 \u0434\u043E\u0436\u0434\u0451\u0442\u0441\u044F \u0438 \u0441\u043A\u0430\u0436\u0435\u0442, \u0447\u0435\u043C \u043A\u043E\u043D\u0447\u0438\u043B\u043E\u0441\u044C; setup \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E \u043D\u0435 \u0437\u043E\u0432\u0438. \u0413\u043E\u0442\u043E\u0432\u043E \u2014 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0436\u0438 \u0441\u0442\u0440\u043E\u043A\u0443 \xAB\u0427\u0435\u043B\u043E\u0432\u0435\u043A\u0443\xBB.',
18688
+ report ?? void 0
18689
+ );
18112
18690
  }
18113
18691
  async function installMissing(engine, key, baseUrl, tell) {
18114
18692
  const place = engine.place();
@@ -18159,12 +18737,17 @@ function wordsOf(content) {
18159
18737
  if (typeof content === "string") return content;
18160
18738
  return content.map((block) => block.type === "text" ? block.text : "").join("\n");
18161
18739
  }
18740
+ var TRIAL_AXES = ["motion", "timing", "place", "camera", "color"];
18741
+ var TRIALS_MAX = 10;
18162
18742
  async function startRender(engine, asked, key, tell) {
18163
18743
  const place = engine.place();
18164
18744
  if (place === null) return toolError(engine.whyNoEngine());
18165
18745
  const where = fileOrRefusal(asked.video);
18166
18746
  if ("refusal" in where) return toolError(where.refusal);
18167
- if (asked.onlyPauses !== true) return startDocumentRender(engine, where.path, key, tell);
18747
+ const trials = { variants: asked.variants, vary: asked.vary };
18748
+ if (asked.onlyPauses !== true) {
18749
+ return startDocumentRender(engine, where.path, key, tell, trials);
18750
+ }
18168
18751
  if (tell !== void 0) for (const line of PAUSES_LADDER) tell(line);
18169
18752
  const ask2 = { pauses: {} };
18170
18753
  if (asked.tempo !== void 0 && asked.tempo !== 1) ask2["tempo"] = asked.tempo;
@@ -18175,7 +18758,7 @@ async function startRender(engine, asked, key, tell) {
18175
18758
  if ("refusal" in agreement) return toolError(agreement.refusal);
18176
18759
  return startDocumentRender(engine, reel, key, tell);
18177
18760
  }
18178
- async function startDocumentRender(engine, reel, key, tell) {
18761
+ async function startDocumentRender(engine, reel, key, tell, trials = {}) {
18179
18762
  const place = engine.place();
18180
18763
  if (place === null) return toolError(engine.whyNoEngine());
18181
18764
  const trouble = await engine.pythonTrouble();
@@ -18184,8 +18767,14 @@ async function startDocumentRender(engine, reel, key, tell) {
18184
18767
  if (python === null) {
18185
18768
  return toolError("\u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043C\u043E\u043D\u0442\u0430\u0436\u0430 \u0435\u0449\u0451 \u043D\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0437\u043E\u0432\u0438 setup.");
18186
18769
  }
18187
- if (tell !== void 0) for (const line of RENDER_LADDER) tell(line);
18188
- const what = `\u043C\u043E\u043D\u0442\u0430\u0436 ${basename(reel)}`;
18770
+ const ladder = trials.variants === void 0 ? RENDER_LADDER : [...RENDER_LADDER, TRIALS_STEP];
18771
+ if (tell !== void 0) for (const line of ladder) tell(line);
18772
+ const what = trials.variants === void 0 ? `\u043C\u043E\u043D\u0442\u0430\u0436 ${basename(reel)}` : `\u043C\u043E\u043D\u0442\u0430\u0436 ${basename(reel)} \u0438 ${trials.variants} \u0432\u0435\u0440\u0441\u0438\u0439 \u0434\u043B\u044F \u043F\u0440\u043E\u0431\u043D\u043E\u0433\u043E \u0440\u0435\u0436\u0438\u043C\u0430`;
18773
+ const more = trials.variants === void 0 ? [] : [
18774
+ "--variants",
18775
+ String(trials.variants),
18776
+ ...trials.vary !== void 0 && trials.vary.length > 0 ? ["--vary", trials.vary.join(",")] : []
18777
+ ];
18189
18778
  const launch = await engine.background({
18190
18779
  place,
18191
18780
  executable: python,
@@ -18197,7 +18786,7 @@ async function startDocumentRender(engine, reel, key, tell) {
18197
18786
  "--what",
18198
18787
  what,
18199
18788
  "--command",
18200
- JSON.stringify({ tool: "render", args: {} })
18789
+ JSON.stringify({ tool: "render", args: trials })
18201
18790
  ],
18202
18791
  args: (id) => [
18203
18792
  place.consolePy,
@@ -18207,7 +18796,8 @@ async function startDocumentRender(engine, reel, key, tell) {
18207
18796
  "--pack",
18208
18797
  place.pack,
18209
18798
  "--job",
18210
- id
18799
+ id,
18800
+ ...more
18211
18801
  ],
18212
18802
  advice: "\u043F\u043E\u0437\u043E\u0432\u0438 setup \u2014 \u0441\u043A\u043E\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043E, \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043D\u0435\u043F\u043E\u043B\u043D\u043E\u0435"
18213
18803
  });
@@ -18218,7 +18808,7 @@ async function startDocumentRender(engine, reel, key, tell) {
18218
18808
  startedJob({
18219
18809
  id: launch.id,
18220
18810
  what,
18221
- ahead: RENDER_LADDER.join(" "),
18811
+ ahead: ladder.join(" "),
18222
18812
  message: "\u0420\u043E\u043B\u0438\u043A \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044F \u043D\u0430 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F."
18223
18813
  })
18224
18814
  );
@@ -18227,8 +18817,16 @@ var RENDER_LADDER = [
18227
18817
  "\u041F\u0440\u043E\u0432\u0435\u0440\u044E, \u0447\u0442\u043E \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D \u0438 \u043D\u0435 \u043F\u0440\u0430\u0432\u043B\u0435\u043D \u043C\u0438\u043C\u043E plan_edit.",
18228
18818
  "\u041F\u043E\u0441\u0447\u0438\u0442\u0430\u044E \u044D\u0444\u0444\u0435\u043A\u0442\u044B \u0438\u0441\u0445\u043E\u0434\u043D\u0438\u043A\u0430 (\u0432\u044B\u0440\u0435\u0437 \u0433\u0435\u0440\u043E\u044F, \u0448\u0443\u043C\u043E\u0434\u0430\u0432) \u0442\u0430\u043C, \u0433\u0434\u0435 \u0438\u0445 \u0432\u0438\u0434\u043D\u043E, \u2014 \u043F\u043E\u0442\u043E\u043C \u0441\u043E\u0431\u0435\u0440\u0443 \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0443 \u0438 \u0437\u0432\u0443\u043A."
18229
18819
  ];
18820
+ var TRIALS_STEP = "\u041F\u043E\u0442\u043E\u043C \u0441\u043E\u0431\u0435\u0440\u0443 \u0432\u0435\u0440\u0441\u0438\u0438 \u0434\u043B\u044F \u043F\u0440\u043E\u0431\u043D\u043E\u0433\u043E \u0440\u0435\u0436\u0438\u043C\u0430 \u043F\u043E \u043E\u0434\u043D\u043E\u0439: \u0440\u0435\u0447\u044C \u0438 \u0437\u0432\u0443\u043A \u0442\u0435 \u0436\u0435, \u0434\u0440\u0443\u0433\u043E\u0435 \u0434\u0432\u0438\u0436\u0435\u043D\u0438\u0435, \u043C\u0435\u0441\u0442\u0430 \u0438 \u043A\u0440\u0443\u043F\u043D\u043E\u0441\u0442\u044C.";
18230
18821
  async function followWork(engine, asked, pace = realPace, tell) {
18231
- const ask2 = () => engine.command(["job", asked.job]);
18822
+ const ask2 = async () => {
18823
+ if (!isSetupJob(asked.job)) return engine.command(["job", asked.job]);
18824
+ const text2 = setupJobText(asked.job);
18825
+ return text2 !== null ? { ok: true, text: text2 } : {
18826
+ ok: false,
18827
+ text: `\u0417\u0430\u0434\u0430\u0447\u0438 ${asked.job} \u043D\u0435\u0442: \u0435\u0451 \u0432\u0451\u043B \u043F\u0440\u0435\u0436\u043D\u0438\u0439 \u0437\u0430\u043F\u0443\u0441\u043A \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0437\u043E\u0432\u0438 setup \u2014 \u043E\u043D \u0441\u043A\u0430\u0436\u0435\u0442, \u0447\u0442\u043E \u0441\u0442\u043E\u0438\u0442 \u0441\u0435\u0439\u0447\u0430\u0441.`
18828
+ };
18829
+ };
18232
18830
  const seconds2 = waitSeconds(asked.seconds ?? WAIT_LIMIT_SECONDS);
18233
18831
  if (seconds2 === 0) return asJobResult(await ask2());
18234
18832
  const state = await followJob({
@@ -18261,7 +18859,7 @@ function renderTool(engine, _passport, key) {
18261
18859
  return defineTool({
18262
18860
  name: "render",
18263
18861
  title: "\u0421\u043E\u0431\u0440\u0430\u0442\u044C \u0440\u043E\u043B\u0438\u043A",
18264
- description: '\u0421\u043E\u0431\u0438\u0440\u0430\u0435\u0442 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442 \u0440\u043E\u043B\u0438\u043A\u0430 \u043D\u0430 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430: \u044D\u0444\u0444\u0435\u043A\u0442\u044B \u0438\u0441\u0445\u043E\u0434\u043D\u0438\u043A\u0430 (\u0432\u044B\u0440\u0435\u0437 \u0433\u0435\u0440\u043E\u044F, \u0448\u0443\u043C\u043E\u0434\u0430\u0432), \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0443, \u0437\u0432\u0443\u043A, \u0433\u043E\u0442\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B. \u0417\u043E\u0432\u0438 \u041F\u041E\u0421\u041B\u0415 \u0442\u043E\u0433\u043E, \u043A\u0430\u043A \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0441\u043A\u0430\u0437\u0430\u043B \xAB\u0434\u0430\xBB \u043D\u0430 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A \u0438\u0437 plan \u0438 \u0442\u044B \u0437\u0430\u043F\u0438\u0441\u0430\u043B \u044D\u0442\u043E plan_edit \u0441 {"op": "agree"}. \u0421\u043E\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044F \u0422\u041E\u041B\u042C\u041A\u041E \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442, \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043D\u044B\u0439 \u0447\u0435\u0440\u0435\u0437 plan_edit: \u0444\u0430\u0439\u043B, \u043F\u043E\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0439 \u043C\u0438\u043C\u043E \u043D\u0435\u0433\u043E, \u0434\u0432\u0438\u0436\u043E\u043A \u043D\u0435 \u0441\u043E\u0431\u0435\u0440\u0451\u0442 \u2014 \xAB\u043F\u0440\u0438\u0448\u043B\u0438 \u043F\u0440\u0430\u0432\u043A\u0443 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u044F\u043C\u0438\xBB.\n\u041E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043D\u043E\u043C\u0435\u0440\u043E\u043C \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u0434\u0430\u043B\u044C\u0448\u0435 wait, \u043E\u043D \u043F\u043E\u0434\u043E\u0436\u0434\u0451\u0442 \u0438 \u0440\u0430\u0441\u0441\u043A\u0430\u0436\u0435\u0442 \u0445\u043E\u0434. \u041F\u043E\u0432\u0442\u043E\u0440 \u0431\u0435\u0437 \u043F\u0440\u0430\u0432\u043E\u043A \u043E\u0442\u0434\u0430\u0451\u0442 \u0433\u043E\u0442\u043E\u0432\u043E\u0435 \u0441\u0440\u0430\u0437\u0443.\n\u041F\u0440\u043E\u0441\u044F\u0442 \xAB\u043F\u0440\u043E\u0441\u0442\u043E \u0443\u0431\u0435\u0440\u0438 \u043F\u0430\u0443\u0437\u044B\xBB \u2014 only_pauses=true \u0441 video: \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A \u0441 \u043E\u0434\u043D\u0438\u043C\u0438 \u043F\u0430\u0443\u0437\u0430\u043C\u0438, \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u0438 \u0441\u0431\u043E\u0440\u043A\u0430 \u043E\u0434\u043D\u0438\u043C \u0432\u044B\u0437\u043E\u0432\u043E\u043C; tempo \u0440\u044F\u0434\u043E\u043C \u2014 \xAB\u0438 \u0443\u0441\u043A\u043E\u0440\u044C\xBB. \u041F\u0430\u0443\u0437\u044B \u0443\u0431\u0438\u0440\u0430\u044E\u0442\u0441\u044F \u043F\u043E \u0441\u043B\u043E\u0432\u0430\u043C, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043D\u0443\u0436\u043D\u0430 \u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u043D\u043D\u0430\u044F \u0440\u0435\u0447\u044C.\n\u0420\u0430\u0431\u043E\u0442\u0430 \u0438\u0434\u0451\u0442 \u043D\u0430 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430, \u043A \u043D\u0430\u043C \u0432\u0438\u0434\u0435\u043E \u043D\u0435 \u0443\u0445\u043E\u0434\u0438\u0442 \u2014 \u043D\u0430\u0432\u0435\u0440\u0445 \u0438\u0434\u0451\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438 \u043F\u043E \u043A\u043B\u044E\u0447\u0443.',
18862
+ description: '\u0421\u043E\u0431\u0438\u0440\u0430\u0435\u0442 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442 \u0440\u043E\u043B\u0438\u043A\u0430 \u043D\u0430 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430: \u044D\u0444\u0444\u0435\u043A\u0442\u044B \u0438\u0441\u0445\u043E\u0434\u043D\u0438\u043A\u0430 (\u0432\u044B\u0440\u0435\u0437 \u0433\u0435\u0440\u043E\u044F, \u0448\u0443\u043C\u043E\u0434\u0430\u0432), \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0443, \u0437\u0432\u0443\u043A, \u0433\u043E\u0442\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B. \u0417\u043E\u0432\u0438 \u041F\u041E\u0421\u041B\u0415 \u0442\u043E\u0433\u043E, \u043A\u0430\u043A \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0441\u043A\u0430\u0437\u0430\u043B \xAB\u0434\u0430\xBB \u043D\u0430 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A \u0438\u0437 plan \u0438 \u0442\u044B \u0437\u0430\u043F\u0438\u0441\u0430\u043B \u044D\u0442\u043E plan_edit \u0441 {"op": "agree"}. \u0421\u043E\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044F \u0422\u041E\u041B\u042C\u041A\u041E \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442, \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043D\u044B\u0439 \u0447\u0435\u0440\u0435\u0437 plan_edit: \u0444\u0430\u0439\u043B, \u043F\u043E\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u043D\u044B\u0439 \u043C\u0438\u043C\u043E \u043D\u0435\u0433\u043E, \u0434\u0432\u0438\u0436\u043E\u043A \u043D\u0435 \u0441\u043E\u0431\u0435\u0440\u0451\u0442 \u2014 \xAB\u043F\u0440\u0438\u0448\u043B\u0438 \u043F\u0440\u0430\u0432\u043A\u0443 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u044F\u043C\u0438\xBB.\n\u041E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043D\u043E\u043C\u0435\u0440\u043E\u043C \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u0434\u0430\u043B\u044C\u0448\u0435 wait, \u043E\u043D \u043F\u043E\u0434\u043E\u0436\u0434\u0451\u0442 \u0438 \u0440\u0430\u0441\u0441\u043A\u0430\u0436\u0435\u0442 \u0445\u043E\u0434. \u041F\u043E\u0432\u0442\u043E\u0440 \u0431\u0435\u0437 \u043F\u0440\u0430\u0432\u043E\u043A \u043E\u0442\u0434\u0430\u0451\u0442 \u0433\u043E\u0442\u043E\u0432\u043E\u0435 \u0441\u0440\u0430\u0437\u0443.\n\u041F\u0440\u043E\u0441\u044F\u0442 \xAB\u0441\u0434\u0435\u043B\u0430\u0439 \u0435\u0449\u0451 5 \u0434\u043B\u044F \u043F\u0440\u043E\u0431\u043D\u043E\u0433\u043E \u0440\u0435\u0436\u0438\u043C\u0430\xBB (Instagram) \u2014 variants=5: \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u0440\u043E\u043B\u0438\u043A (\u0433\u043E\u0442\u043E\u0432\u044B\u0439 \u0432\u043E\u0437\u044C\u043C\u0451\u0442\u0441\u044F \u0441\u0440\u0430\u0437\u0443), \u043F\u043E\u0442\u043E\u043C \u0432\u0435\u0440\u0441\u0438\u0438 \u0440\u044F\u0434\u043E\u043C \u0441 \u043D\u0438\u043C \u0444\u0430\u0439\u043B\u0430\u043C\u0438 -trial-1\u2026-trial-5. \u0420\u0435\u0447\u044C, \u0440\u0435\u0437\u044B \u0438 \u0437\u0432\u0443\u043A \u0442\u0435 \u0436\u0435, \u043E\u0442\u043B\u0438\u0447\u0430\u044E\u0442\u0441\u044F \u0434\u0432\u0438\u0436\u0435\u043D\u0438\u0435, \u0432\u0440\u0435\u043C\u044F \u0432\u0441\u0442\u0430\u0432\u043E\u043A, \u043C\u0435\u0441\u0442\u0430 \u0438 \u043A\u0440\u0443\u043F\u043D\u043E\u0441\u0442\u044C; \u0446\u0432\u0435\u0442 \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u043A\u043E\u0433\u0434\u0430 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0441\u043A\u0430\u0437\u0430\u043B \xAB\u0438 \u0446\u0432\u0435\u0442 \u0440\u0430\u0437\u043D\u044B\u0439\xBB: vary \u0441 color. \u041E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0433\u043E \xAB\u0434\u0430\xBB \u043D\u0430 \u0432\u0435\u0440\u0441\u0438\u0438 \u043D\u0435 \u043D\u0443\u0436\u043D\u043E. \u041C\u0435\u043D\u044F\u0442\u044C\u0441\u044F \u043D\u0435\u0447\u0435\u043C\u0443 \u2014 \u0434\u0432\u0438\u0436\u043E\u043A \u0441\u043A\u0430\u0436\u0435\u0442, \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0432\u044B\u0448\u043B\u043E, \u0431\u043B\u0438\u0437\u043D\u0435\u0446\u043E\u0432 \u043D\u0435 \u0431\u0443\u0434\u0435\u0442.\n\u041F\u0440\u043E\u0441\u044F\u0442 \xAB\u043F\u0440\u043E\u0441\u0442\u043E \u0443\u0431\u0435\u0440\u0438 \u043F\u0430\u0443\u0437\u044B\xBB \u2014 only_pauses=true \u0441 video: \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A \u0441 \u043E\u0434\u043D\u0438\u043C\u0438 \u043F\u0430\u0443\u0437\u0430\u043C\u0438, \u0441\u043E\u0433\u043B\u0430\u0441\u0438\u0435 \u0438 \u0441\u0431\u043E\u0440\u043A\u0430 \u043E\u0434\u043D\u0438\u043C \u0432\u044B\u0437\u043E\u0432\u043E\u043C; tempo \u0440\u044F\u0434\u043E\u043C \u2014 \xAB\u0438 \u0443\u0441\u043A\u043E\u0440\u044C\xBB. \u041F\u0430\u0443\u0437\u044B \u0443\u0431\u0438\u0440\u0430\u044E\u0442\u0441\u044F \u043F\u043E \u0441\u043B\u043E\u0432\u0430\u043C, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043D\u0443\u0436\u043D\u0430 \u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u043D\u043D\u0430\u044F \u0440\u0435\u0447\u044C.\n\u0420\u0430\u0431\u043E\u0442\u0430 \u0438\u0434\u0451\u0442 \u043D\u0430 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430, \u043A \u043D\u0430\u043C \u0432\u0438\u0434\u0435\u043E \u043D\u0435 \u0443\u0445\u043E\u0434\u0438\u0442 \u2014 \u043D\u0430\u0432\u0435\u0440\u0445 \u0438\u0434\u0451\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438 \u043F\u043E \u043A\u043B\u044E\u0447\u0443.',
18265
18863
  // Полоса хода работы: монтаж идёт минутами, и всё это время в ленте не
18266
18864
  // происходило бы ничего. ⚠ Ожидание — у `wait`, и полосы там нет: иначе
18267
18865
  // каждая проверка рисовала бы новую поверх этой.
@@ -18281,6 +18879,12 @@ function renderTool(engine, _passport, key) {
18281
18879
  ).optional(),
18282
18880
  style: external_exports.string().describe(
18283
18881
  "\u0422\u043E\u043B\u044C\u043A\u043E \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 only_pauses: \u043F\u0430\u043F\u043A\u0430 \u043F\u0430\u043A\u0430 \u0444\u0438\u0440\u043C\u0435\u043D\u043D\u043E\u0433\u043E \u0441\u0442\u0438\u043B\u044F"
18882
+ ).optional(),
18883
+ variants: external_exports.number().int().min(1).max(TRIALS_MAX).describe(
18884
+ `\u0415\u0449\u0451 \u0441\u0442\u043E\u043B\u044C\u043A\u043E \u0432\u0435\u0440\u0441\u0438\u0439 \u0434\u043B\u044F \u043F\u0440\u043E\u0431\u043D\u043E\u0433\u043E \u0440\u0435\u0436\u0438\u043C\u0430 Instagram, 1\u2013${TRIALS_MAX}; \u043E\u0431\u044B\u0447\u043D\u043E 5`
18885
+ ).optional(),
18886
+ vary: external_exports.array(external_exports.enum(TRIAL_AXES)).describe(
18887
+ "\u0422\u043E\u043B\u044C\u043A\u043E \u0441 variants: \u0447\u0435\u043C \u0432\u0435\u0440\u0441\u0438\u0438 \u043E\u0442\u043B\u0438\u0447\u0430\u044E\u0442\u0441\u044F. \u0411\u0435\u0437 \u043D\u0435\u0433\u043E \u2014 \u0432\u0441\u0451, \u043A\u0440\u043E\u043C\u0435 \u0446\u0432\u0435\u0442\u0430 (color)"
18284
18888
  ).optional()
18285
18889
  }).check((ctx) => {
18286
18890
  const asked = ctx.value;
@@ -18296,7 +18900,15 @@ function renderTool(engine, _passport, key) {
18296
18900
  if (asked.video !== void 0 && asked.reel !== void 0) {
18297
18901
  say("\xABvideo\xBB \u0438 \xABreel\xBB \u2014 \u044D\u0442\u043E \u043E\u0434\u043D\u043E \u0438 \u0442\u043E \u0436\u0435 \u043C\u0435\u0441\u0442\u043E, \u043D\u0430\u0437\u0432\u0430\u043D\u043D\u043E\u0435 \u0434\u0432\u0430\u0436\u0434\u044B. \u041E\u0441\u0442\u0430\u0432\u044C \u043E\u0434\u043D\u043E", "reel");
18298
18902
  }
18299
- if (asked.only_pauses === true) return;
18903
+ if (asked.vary !== void 0 && asked.variants === void 0) {
18904
+ say("\xABvary\xBB \u2014 \u043E\u0441\u0438 \u0432\u0435\u0440\u0441\u0438\u0439: \u0431\u0435\u0437 variants \u0432\u0435\u0440\u0441\u0438\u0439 \u043D\u0435\u0442. \u041D\u0430\u0437\u043E\u0432\u0438, \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0438\u0445: variants=5", "vary");
18905
+ }
18906
+ if (asked.only_pauses === true) {
18907
+ if (asked.variants !== void 0) {
18908
+ say("\u0412\u0435\u0440\u0441\u0438\u0438 \u0434\u0435\u043B\u0430\u044E\u0442\u0441\u044F \u0438\u0437 \u0441\u043E\u0433\u043B\u0430\u0441\u043E\u0432\u0430\u043D\u043D\u043E\u0433\u043E \u043C\u043E\u043D\u0442\u0430\u0436\u0430, \u0430 \u043D\u0435 \u0438\u0437 \xAB\u0442\u043E\u043B\u044C\u043A\u043E \u043F\u0430\u0443\u0437\u044B\xBB. \u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u0441\u043E\u0431\u0435\u0440\u0438 \u0440\u043E\u043B\u0438\u043A, \u043F\u043E\u0442\u043E\u043C render \u0441 reel \u0438 variants", "variants");
18909
+ }
18910
+ return;
18911
+ }
18300
18912
  const extra = ["tempo", "style"].filter((name) => asked[name] !== void 0);
18301
18913
  if (extra.length > 0) {
18302
18914
  say(
@@ -18312,7 +18924,9 @@ function renderTool(engine, _passport, key) {
18312
18924
  video: args.video ?? args.reel ?? "",
18313
18925
  onlyPauses: args.only_pauses,
18314
18926
  tempo: args.tempo,
18315
- style: args.style
18927
+ style: args.style,
18928
+ variants: args.variants,
18929
+ vary: args.vary
18316
18930
  },
18317
18931
  key,
18318
18932
  ctx?.emitProgress
@@ -18362,6 +18976,9 @@ function waitTool(engine, pace = realPace) {
18362
18976
  }),
18363
18977
  readOnly: true,
18364
18978
  async execute(args, ctx) {
18979
+ if (args.job !== void 0 && isSetupJob(args.job)) {
18980
+ return followWork(engine, { job: args.job, seconds: args.seconds }, pace, ctx?.emitProgress);
18981
+ }
18365
18982
  const place = engine.place();
18366
18983
  if (place === null) return toolError(engine.whyNoEngine());
18367
18984
  if (args.job === void 0) {
@@ -22512,4 +23129,4 @@ ${inTable}`, file2);
22512
23129
  };
22513
23130
  }
22514
23131
 
22515
- export { CrashReporter, DEFAULT_HOME, DeliveryError, Engine, ForeignBlockError, INSIDE_PYTHON, INSTALL_STEPS, KEEP_COPIES, KEEP_VERSIONS, LONG_STEPS, MANIFEST_TIMEOUT_MS, MANIFEST_URL, MARKER, MIN_ENGINE, McpServer, NaparnikApiClient, NaparnikAuthError, NaparnikClientError, NaparnikConfigError, NaparnikError, NaparnikInsufficientBalanceError, NaparnikScopeError, NaparnikServerError, NaparnikTimeoutError, PAUSE_HOURS, SERVER_NAME, SOURCES_FILE, START_SEC, StdioServerTransport, TOOL_SEC, USER_AGENT, VERSION, WHERE_TO_GET_KEY, Wakefulness, buildAll, buildBlock, buildResources, bundleUrlFor, chooseBody, compare, configPath, currentMachineFingerprint, decide, decideShell, defineTool, deploy, downloadFile, fetchManifest, fetchShellManifest, fileOrRefusal, findBlock, fromHost, fromNpxCache, handOverToBody, inside, install, installPayload, installPython, installStep, installedShell, jsonSchemaOf, keyFromBlock, keyInTable, lastLine, loadConfig, manifestUrlFor, markChecked, markChecked2, markCurrent2 as markCurrent, parseEngineManifest, parseFailure, path, platformKey, previousVersion2 as previousVersion, probe, pythonSource, readConfig, realPace, refusalFor, remove, rollback, rollbackShell, shellDir, systemBlocker, tarEntries, tidyOld2 as tidyOld, timeToCheck, timeToCheck2, unpackArchive, updateEngine, watchProcessCrashes, withArgs };
23132
+ export { CrashReporter, DEFAULT_HOME, DeliveryError, Engine, ForeignBlockError, INSIDE_PYTHON, INSTALL_STEPS, KEEP_COPIES, KEEP_VERSIONS, LONG_STEPS, MANIFEST_TIMEOUT_MS, MANIFEST_URL, MARKER, MIN_ENGINE, McpServer, NaparnikApiClient, NaparnikAuthError, NaparnikClientError, NaparnikConfigError, NaparnikError, NaparnikInsufficientBalanceError, NaparnikScopeError, NaparnikServerError, NaparnikTimeoutError, PAUSE_HOURS, SERVER_NAME, SOURCES_FILE, START_SEC, StdioServerTransport, TOOL_SEC, USER_AGENT, VERSION, WHERE_TO_GET_KEY, Wakefulness, buildAll, buildBlock, buildResources, bundleUrlFor, chooseBody, compare, configPath, currentMachineFingerprint, decide, decideShell, defineTool, deploy, downloadFile, fetchManifest, fetchShellManifest, fileOrRefusal, findBlock, fromHost, fromNpxCache, handOverToBody, inside, install, installPayload, installPython, installStep, installedShell, jsonSchemaOf, keyFromBlock, keyInTable, lastLine, loadConfig, manifestUrlFor, markChecked, markChecked2, markCurrent2 as markCurrent, parseEngineManifest, parseFailure, path, platformKey, previousVersion2 as previousVersion, probe, pythonSource, readConfig, realPace, refusalFor, remove, reportInstallFaults, rollback, rollbackShell, shellDir, systemBlocker, tarEntries, tidyOld2 as tidyOld, timeToCheck, timeToCheck2, unpackArchive, updateEngine, watchProcessCrashes, withArgs };