@naparnik/mcp 0.10.43 → 0.10.48

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,10 +2,12 @@ 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';
10
+ import { Worker } from 'worker_threads';
9
11
  import { readFile } from 'fs/promises';
10
12
 
11
13
  var __defProp = Object.defineProperty;
@@ -728,7 +730,7 @@ function parsePositiveInt(raw, fallback) {
728
730
  }
729
731
 
730
732
  // src/version.ts
731
- var VERSION = "0.10.43";
733
+ var VERSION = "0.10.48";
732
734
  var USER_AGENT = `naparnik-mcp/${VERSION}`;
733
735
  var MIN_ENGINE = "2026.09.24-466";
734
736
  var LAUNCHER_PROTOCOL = 1;
@@ -1548,7 +1550,65 @@ var Engine = class {
1548
1550
  }
1549
1551
  /** Python подготовки кандидата — намеренно без fallback на venv старого пака. */
1550
1552
  bootstrapPython() {
1551
- return this.ourPython() ?? (platform() === "win32" ? "python" : "python3");
1553
+ return this.bootstrapPythons()[0] ?? (platform() === "win32" ? "python" : "python3");
1554
+ }
1555
+ /**
1556
+ * Чем собирать окружение новой версии — по порядку, до первого, кто сумеет.
1557
+ *
1558
+ * ⚠ БАЗОВЫЙ ПИТОН РАБОЧЕЙ ВЕРСИИ СТОИТ ВПЕРЕДИ «python3» ИЗ PATH, И ЭТО
1559
+ * ПОЧИНКА, А НЕ ВКУС. Claude Desktop на маке запускает нас без
1560
+ * `/opt/homebrew/bin` в PATH, и «python3» там — `/usr/bin/python3`, питон
1561
+ * Xcode 3.9. Он собирает venv только ссылками («This build of python cannot
1562
+ * create venvs without using symlinks»), а ссылка на интерпретатор вне версии
1563
+ * не проходит сверку `candidateVenvTrouble`. Обновление 2026.09.24-467 у
1564
+ * владельца встало ровно на этом, хотя рабочая версия собрана Homebrew 3.14
1565
+ * копиями и работает. Движок при первой установке ищет питон сам
1566
+ * (`setup/python_finder.py`) и пишет, какой взял, в `pyvenv.cfg` окружения —
1567
+ * этот и берём: он уже однажды собрал здесь рабочее окружение копиями.
1568
+ *
1569
+ * ⚠ НЕ ПИТОН САМОГО VENV. Окружение старой версии удаляется уборкой после
1570
+ * обновления, и кандидат, собранный его питоном, остался бы без интерпретатора
1571
+ * (`engine-candidate.test.ts`). База из `pyvenv.cfg` лежит вне версий.
1572
+ */
1573
+ bootstrapPythons() {
1574
+ const found = [
1575
+ this.ourPython(),
1576
+ this.workingBasePython(),
1577
+ platform() === "win32" ? "python" : "python3"
1578
+ ].filter((one) => one !== null);
1579
+ return [...new Set(found)];
1580
+ }
1581
+ /**
1582
+ * Базовый интерпретатор окружения рабочей версии — из её `pyvenv.cfg`.
1583
+ *
1584
+ * `home` раньше `executable`: у Homebrew `executable` указывает в папку
1585
+ * конкретного выпуска (`Cellar/python@3.14/3.14.7/…`), и после `brew upgrade`
1586
+ * её уже нет, а `home` (`/opt/homebrew/opt/python@3.14/bin`) переживает
1587
+ * обновление. Нет файла или записанного питона на диске — `null`.
1588
+ */
1589
+ workingBasePython() {
1590
+ const place = this.place();
1591
+ if (place === null) return null;
1592
+ let text2;
1593
+ try {
1594
+ text2 = readFileSync(join(place.pack, ".venv", "pyvenv.cfg"), "utf8");
1595
+ } catch {
1596
+ return null;
1597
+ }
1598
+ const value = (key) => {
1599
+ const found = new RegExp(`^\\s*${key}\\s*=\\s*(.+?)\\s*$`, "m").exec(text2);
1600
+ return found?.[1] ?? null;
1601
+ };
1602
+ const home = value("home");
1603
+ if (home !== null) {
1604
+ const names = platform() === "win32" ? ["python.exe"] : ["python3", "python"];
1605
+ for (const name of names) {
1606
+ const candidate = join(home, name);
1607
+ if (existsSync(candidate)) return candidate;
1608
+ }
1609
+ }
1610
+ const executable = value("executable");
1611
+ return executable !== null && existsSync(executable) ? executable : null;
1552
1612
  }
1553
1613
  /** Python ровно названного кандидата, а не текущей версии. */
1554
1614
  candidatePython(place) {
@@ -1676,6 +1736,45 @@ var Engine = class {
1676
1736
  env: montageEnv(this.pythonEnv(), key)
1677
1737
  });
1678
1738
  }
1739
+ /**
1740
+ * То же, что `command`, но строки stderr уходят в `onLine` по мере печати.
1741
+ *
1742
+ * ⚠ РАДИ СКАЧИВАНИЯ НАЧИНКИ. `deliver` печатает «Скачано 3 МБ из 6 (50%)» в
1743
+ * stderr по ходу, а `command` отдаёт всё разом в конце — и полоса обновления
1744
+ * стояла без единой цифры, пока архив качался (владелец 2026-09-24: «как
1745
+ * будто завис»). Строки те же, что у задач движка, и полоса разбирает их тем
1746
+ * же `DOWNLOAD` (`job-report.ts`).
1747
+ */
1748
+ async commandLive(args, { timeout = 3e5, key }, onLine) {
1749
+ const place = this.place();
1750
+ if (place === null) return { ok: false, text: this.whyNoEngine() };
1751
+ const trouble = await this.pythonTrouble();
1752
+ if (trouble !== null) return { ok: false, text: trouble };
1753
+ try {
1754
+ const { stdout, stderr } = await new Promise((done, fail) => {
1755
+ const child = execFile(this.enginePython(), [place.consolePy, ...args], {
1756
+ cwd: existsSync(place.pack) ? place.pack : place.engine,
1757
+ maxBuffer: 8 << 20,
1758
+ timeout,
1759
+ encoding: "utf8",
1760
+ env: montageEnv(this.pythonEnv(), key)
1761
+ }, (err, out, errOut) => {
1762
+ if (err !== null) fail(Object.assign(err, { stdout: out, stderr: errOut }));
1763
+ else done({ stdout: out, stderr: errOut });
1764
+ });
1765
+ let rest = "";
1766
+ child.stderr?.on("data", (chunk) => {
1767
+ rest += String(chunk);
1768
+ const lines = rest.split(/\r?\n|\r/);
1769
+ rest = lines.pop() ?? "";
1770
+ for (const line of lines) if (line.trim() !== "") onLine(line.trim());
1771
+ });
1772
+ });
1773
+ return { ok: true, text: (stdout + stderr).trim() };
1774
+ } catch (err) {
1775
+ return parseFailure(err, "\u041A\u043E\u043C\u0430\u043D\u0434\u0430 \u043D\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043B\u0430\u0441\u044C");
1776
+ }
1777
+ }
1679
1778
  /** Одна строка вывода команды — например, номер заведённого задания. */
1680
1779
  async asString(place, args, key) {
1681
1780
  const { stdout } = await this.run(place, args, {
@@ -13793,6 +13892,29 @@ async function askPointer(opts) {
13793
13892
  clearTimeout(timer);
13794
13893
  }
13795
13894
  }
13895
+ var scope = new AsyncLocalStorage();
13896
+ function noteInstallFault(stage, trouble) {
13897
+ scope.getStore()?.push({ stage, trouble });
13898
+ }
13899
+ async function collectInstallFaults(work) {
13900
+ const faults = [];
13901
+ const value = await scope.run(faults, work);
13902
+ return { value, faults };
13903
+ }
13904
+ var JOBS_KEPT = 10;
13905
+ var byJob = /* @__PURE__ */ new Map();
13906
+ function collectJobInstallFaults(id, work) {
13907
+ const faults = [];
13908
+ byJob.delete(id);
13909
+ byJob.set(id, faults);
13910
+ while (byJob.size > JOBS_KEPT) byJob.delete(byJob.keys().next().value);
13911
+ return scope.run(faults, work);
13912
+ }
13913
+ function jobInstallFaults(id) {
13914
+ return byJob.get(id) ?? [];
13915
+ }
13916
+
13917
+ // src/update.ts
13796
13918
  var MANIFEST_PATH = "/download/engine/manifest.json";
13797
13919
  function manifestUrlFor(baseUrl) {
13798
13920
  return pointerUrl(baseUrl, MANIFEST_PATH);
@@ -13846,6 +13968,7 @@ async function deploy(engine, manifest, trial = probe, key, hooks = {}) {
13846
13968
  return await withVersionLock(dir, async () => deployLocked(engine, manifest, trial, key, hooks));
13847
13969
  } catch (trouble) {
13848
13970
  if (trouble instanceof VersionLockError) return { ok: false, text: trouble.message };
13971
+ noteInstallFault("deploy", trouble);
13849
13972
  return {
13850
13973
  ok: false,
13851
13974
  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 +14003,20 @@ async function deployLocked(engine, manifest, trial, key, hooks) {
13880
14003
  if (!existsSync(dest)) {
13881
14004
  const stage = createVersionStage(dir, manifest.version);
13882
14005
  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
- );
14006
+ const deliverArgs = [
14007
+ "deliver",
14008
+ "--url",
14009
+ manifest.url,
14010
+ "--dest",
14011
+ stage.payload,
14012
+ "--sha256",
14013
+ manifest.sha256,
14014
+ "--byte",
14015
+ String(manifest.bytes)
14016
+ ];
14017
+ const deliverOpts = { timeout: 30 * 6e4, ...key !== void 0 ? { key } : {} };
14018
+ const say = hooks.line;
14019
+ const delivery = say !== void 0 && engine.commandLive !== void 0 ? await engine.commandLive(deliverArgs, deliverOpts, (text2) => say(text2)) : await engine.command(deliverArgs, deliverOpts);
13897
14020
  if (!delivery.ok) {
13898
14021
  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
14022
  ${delivery.text}` };
@@ -13935,6 +14058,7 @@ ${delivery.text}` };
13935
14058
  discardVersionStage(stage);
13936
14059
  }
13937
14060
  }
14061
+ hooks.downloaded?.();
13938
14062
  if (candidateDeps(engine)) return activatePreparedCandidateLocked(engine, manifest, trial);
13939
14063
  const smoke = await dryRun(engine, dest, manifest.version, trial);
13940
14064
  if (smoke !== null) {
@@ -14014,6 +14138,7 @@ async function continuePreparedCandidate(engine, expected, bootstrap, trial = pr
14014
14138
  });
14015
14139
  } catch (trouble) {
14016
14140
  if (trouble instanceof VersionLockError) return { ok: false, text: trouble.message };
14141
+ noteInstallFault("activate", trouble);
14017
14142
  return {
14018
14143
  ok: false,
14019
14144
  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 +14297,7 @@ async function prepareCopiedCandidateVenv(engine, dir, version2, place) {
14172
14297
  if (engine.prepareCandidateVenv !== void 0) {
14173
14298
  await engine.prepareCandidateVenv(place);
14174
14299
  } 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
- });
14300
+ await copiedVenv(engine, venv);
14182
14301
  }
14183
14302
  }
14184
14303
  const createdTrouble = candidateVenvTrouble(dir, version2, place, true);
@@ -14188,17 +14307,41 @@ async function prepareCopiedCandidateVenv(engine, dir, version2, place) {
14188
14307
  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
14308
  }
14190
14309
  }
14310
+ var SYMLINKS_ONLY = "cannot create venvs without using symlinks";
14311
+ async function copiedVenv(engine, venv) {
14312
+ const { execFile: execFile3 } = await import('child_process');
14313
+ const { promisify: promisify2 } = await import('util');
14314
+ const pythons = engine.bootstrapPythons?.() ?? [engine.bootstrapPython()];
14315
+ const refusals = [];
14316
+ for (const python of pythons) {
14317
+ try {
14318
+ await promisify2(execFile3)(python, ["-m", "venv", "--copies", venv], {
14319
+ timeout: 10 * 6e4,
14320
+ encoding: "utf8",
14321
+ env: { ...engine.pythonEnv(), PYTHONDONTWRITEBYTECODE: "1" }
14322
+ });
14323
+ return;
14324
+ } catch (trouble) {
14325
+ rmSync(venv, { recursive: true, force: true });
14326
+ const said = `${String(trouble.stderr ?? "")} ${describeFailure(trouble)}`;
14327
+ 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())}`);
14328
+ }
14329
+ }
14330
+ throw new Error(
14331
+ `\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.`
14332
+ );
14333
+ }
14191
14334
  async function candidatePipReady(engine, python) {
14192
14335
  if (engine.candidatePipReady !== void 0) return engine.candidatePipReady(python);
14193
- const { execFile: execFile4 } = await import('child_process');
14194
- const { promisify: promisify3 } = await import('util');
14336
+ const { execFile: execFile3 } = await import('child_process');
14337
+ const { promisify: promisify2 } = await import('util');
14195
14338
  const env = { ...engine.pythonEnv(), PYTHONDONTWRITEBYTECODE: "1" };
14196
14339
  delete env["PYTHONHOME"];
14197
14340
  delete env["PYTHONPATH"];
14198
14341
  delete env["VIRTUAL_ENV"];
14199
14342
  delete env["__PYVENV_LAUNCHER__"];
14200
14343
  try {
14201
- await promisify3(execFile4)(python, ["-B", "-m", "pip", "--version"], {
14344
+ await promisify2(execFile3)(python, ["-B", "-m", "pip", "--version"], {
14202
14345
  timeout: 6e4,
14203
14346
  encoding: "utf8",
14204
14347
  env
@@ -14304,6 +14447,7 @@ async function activatePreparedCandidateLocked(engine, manifest, trial, extras =
14304
14447
  const installedVenvTrouble = candidateVenvTrouble(dir, candidate.version, place, true);
14305
14448
  if (installedVenvTrouble !== null) throw new Error(installedVenvTrouble);
14306
14449
  } catch (trouble) {
14450
+ noteInstallFault("env", trouble);
14307
14451
  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
14452
  ${commandWords(trouble)}`);
14309
14453
  }
@@ -14345,6 +14489,7 @@ ${smoke}`);
14345
14489
  { timeout: 5 * 6e4 }
14346
14490
  );
14347
14491
  } catch (trouble) {
14492
+ noteInstallFault("env-check", trouble);
14348
14493
  return blockCandidate(
14349
14494
  dir,
14350
14495
  candidate,
@@ -14510,9 +14655,9 @@ async function dryRun(engine, build, expected, trial) {
14510
14655
  }
14511
14656
  }
14512
14657
  async function probe(engine, consolePy) {
14513
- const { execFile: execFile4 } = await import('child_process');
14514
- const { promisify: promisify3 } = await import('util');
14515
- return promisify3(execFile4)(engine.enginePython(), [consolePy, "version"], {
14658
+ const { execFile: execFile3 } = await import('child_process');
14659
+ const { promisify: promisify2 } = await import('util');
14660
+ return promisify2(execFile3)(engine.enginePython(), [consolePy, "version"], {
14516
14661
  timeout: 6e4,
14517
14662
  encoding: "utf8",
14518
14663
  env: engine.pythonEnv()
@@ -14941,50 +15086,100 @@ function removePublication(dir, version2) {
14941
15086
  function publicationMatchesManifest(publication, manifest) {
14942
15087
  return publication.version === manifest.version && publication.artifactSha256 === manifest.body?.sha256 && publication.artifactBytes === manifest.body.bytes;
14943
15088
  }
15089
+ var PROBE_TIMEOUT_MS = 6e4;
15090
+ var REQUIRED_TOOLS = ["setup", "render", "wait"];
15091
+ var PROBE_THREAD = `
15092
+ const { parentPort, workerData } = require('node:worker_threads')
15093
+ import(workerData.entry).then((body) => {
15094
+ if (typeof body.start !== 'function') throw new Error('\u0432\u0445\u043E\u0434 \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u043D\u0435 \u043E\u0442\u0434\u0430\u0451\u0442 start()')
15095
+ return body.start()
15096
+ }).catch((trouble) => {
15097
+ parentPort.postMessage(trouble instanceof Error
15098
+ ? { message: trouble.name + ': ' + String(trouble.message).split('\\n')[0], stack: String(trouble.stack || '') }
15099
+ : { message: String(trouble), stack: '' })
15100
+ })
15101
+ `;
14944
15102
  var probeShell = async (entry) => {
14945
15103
  const probeHome = mkdtempSync(join(tmpdir(), "naparnik-shell-probe-"));
14946
- const env = {
14947
- ...process.env,
14948
- NAPARNIK_HOME: probeHome,
14949
- NAPARNIK_NO_BODY: "1",
14950
- NAPARNIK_NO_UPDATE: "1"
14951
- };
15104
+ const env = {};
15105
+ for (const [name, value] of Object.entries(process.env)) {
15106
+ if (value !== void 0 && name !== "NAPARNIK_API_KEY") env[name] = value;
15107
+ }
15108
+ Object.assign(env, { NAPARNIK_HOME: probeHome, NAPARNIK_NO_BODY: "1", NAPARNIK_NO_UPDATE: "1" });
15109
+ const thread = new Worker(PROBE_THREAD, {
15110
+ eval: true,
15111
+ argv: [],
15112
+ env,
15113
+ stdin: true,
15114
+ stdout: true,
15115
+ stderr: true,
15116
+ workerData: { entry: pathToFileURL(entry).href }
15117
+ });
14952
15118
  try {
14953
- const version2 = await promisify(execFile)(process.execPath, [entry, "--version"], {
14954
- timeout: 6e4,
14955
- encoding: "utf8",
14956
- env
14957
- });
14958
- await new Promise((resolve5, reject) => {
14959
- const child = spawn(process.execPath, [entry], { stdio: ["pipe", "pipe", "pipe"], env });
15119
+ const named = await new Promise((resolve5, reject) => {
14960
15120
  let output = "";
14961
15121
  let errors = "";
14962
15122
  let finished = false;
14963
- const timer = setTimeout(() => finish(new Error("\u0442\u0435\u043B\u043E \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B\u043E \u043D\u0430 initialize/tools-list")), 15e3);
14964
- const finish = (trouble) => {
15123
+ const timer = setTimeout(
15124
+ () => finish(failure(`\u0442\u0435\u043B\u043E \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B\u043E \u043D\u0430 initialize/tools-list \u0437\u0430 ${PROBE_TIMEOUT_MS / 1e3} \u0441`)),
15125
+ PROBE_TIMEOUT_MS
15126
+ );
15127
+ const finish = (trouble, said) => {
14965
15128
  if (finished) return;
14966
15129
  finished = true;
14967
15130
  clearTimeout(timer);
14968
- child.kill();
14969
- if (trouble === void 0) resolve5();
15131
+ if (trouble === null) resolve5(said ?? "");
14970
15132
  else reject(trouble);
14971
15133
  };
14972
15134
  const send = (message) => {
14973
- child.stdin.write(`${JSON.stringify(message)}
15135
+ thread.stdin?.write(`${JSON.stringify(message)}
14974
15136
  `);
14975
15137
  };
14976
- child.stderr.setEncoding("utf8");
14977
- child.stderr.on("data", (chunk) => {
14978
- errors += chunk;
15138
+ const failure = (text2, stack = "") => {
15139
+ const trouble = new Error(text2);
15140
+ const said = errors.trim();
15141
+ trouble.stack = [text2, stack, said === "" ? "" : `stderr \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430:
15142
+ ${said.slice(-4e3)}`].filter((part) => part !== "").join("\n");
15143
+ return trouble;
15144
+ };
15145
+ thread.on("message", (message) => {
15146
+ finish(failure(`\u0442\u0435\u043B\u043E \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u043B\u043E\u0441\u044C: ${String(message.message)}`, String(message.stack ?? "")));
15147
+ });
15148
+ thread.on("error", (trouble) => {
15149
+ finish(failure(`\u0442\u0435\u043B\u043E \u0443\u043F\u0430\u043B\u043E: ${describeFailure(trouble)}`, trouble.stack ?? ""));
14979
15150
  });
14980
- child.on("error", (trouble) => {
14981
- finish(trouble);
15151
+ thread.on("exit", (code) => {
15152
+ finish(failure(`\u0442\u0435\u043B\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u043E\u0441\u044C \u0441 \u043A\u043E\u0434\u043E\u043C ${String(code)}`));
14982
15153
  });
14983
- child.on("exit", (code) => {
14984
- if (!finished) finish(new Error(`\u0442\u0435\u043B\u043E \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u043E\u0441\u044C \u0441 \u043A\u043E\u0434\u043E\u043C ${String(code)}: ${errors.trim()}`));
15154
+ thread.stderr.setEncoding("utf8");
15155
+ thread.stderr.on("data", (chunk) => {
15156
+ errors += chunk;
14985
15157
  });
14986
- child.stdout.setEncoding("utf8");
14987
- child.stdout.on("data", (chunk) => {
15158
+ let version2 = null;
15159
+ const answer = (reply) => {
15160
+ if (reply.error !== void 0) {
15161
+ finish(failure(`MCP-\u043F\u0440\u043E\u0431\u0430 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u0430 \u043E\u0442\u043A\u0430\u0437: ${JSON.stringify(reply.error)}`));
15162
+ } else if (reply.id === 1) {
15163
+ const named2 = reply.result?.serverInfo?.version;
15164
+ if (typeof named2 !== "string" || named2 === "") {
15165
+ finish(failure("initialize \u043D\u0435 \u043D\u0430\u0437\u0432\u0430\u043B \u0432\u0435\u0440\u0441\u0438\u044E \u0442\u0435\u043B\u0430"));
15166
+ return;
15167
+ }
15168
+ version2 = named2;
15169
+ send({ jsonrpc: "2.0", method: "notifications/initialized" });
15170
+ send({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} });
15171
+ } else if (reply.id === 2 && version2 !== null) {
15172
+ const names = new Set((reply.result?.tools ?? []).map((tool) => tool.name));
15173
+ const missing = REQUIRED_TOOLS.filter((name) => !names.has(name));
15174
+ if (missing.length > 0) {
15175
+ finish(failure(`tools/list \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u043D\u0435 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u0442 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B: ${missing.join(", ")}`));
15176
+ return;
15177
+ }
15178
+ finish(null, version2);
15179
+ }
15180
+ };
15181
+ thread.stdout.setEncoding("utf8");
15182
+ thread.stdout.on("data", (chunk) => {
14988
15183
  output += chunk;
14989
15184
  for (; ; ) {
14990
15185
  const end = output.indexOf("\n");
@@ -14992,36 +15187,14 @@ var probeShell = async (entry) => {
14992
15187
  const line = output.slice(0, end).trim();
14993
15188
  output = output.slice(end + 1);
14994
15189
  if (line === "") continue;
14995
- let response;
15190
+ let reply;
14996
15191
  try {
14997
- response = JSON.parse(line);
15192
+ reply = JSON.parse(line);
14998
15193
  } catch {
14999
- finish(new Error(`\u0442\u0435\u043B\u043E \u043D\u0430\u043F\u0435\u0447\u0430\u0442\u0430\u043B\u043E \u043D\u0435 JSON: ${line.slice(0, 200)}`));
15000
- return;
15001
- }
15002
- if (response.error !== void 0) {
15003
- finish(new Error(`MCP-\u043F\u0440\u043E\u0431\u0430 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u0430 \u043E\u0442\u043A\u0430\u0437: ${JSON.stringify(response.error)}`));
15194
+ finish(failure(`\u0442\u0435\u043B\u043E \u043D\u0430\u043F\u0435\u0447\u0430\u0442\u0430\u043B\u043E \u043D\u0435 JSON: ${line.slice(0, 200)}`));
15004
15195
  return;
15005
15196
  }
15006
- if (response.id === 1) {
15007
- const expected = version2.stdout.trim();
15008
- if (response.result?.serverInfo?.version !== expected) {
15009
- finish(new Error(
15010
- `initialize \u043F\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043B \u0442\u0435\u043B\u043E \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \xAB${String(response.result?.serverInfo?.version)}\xBB, \u0430 --version \u043E\u0442\u0432\u0435\u0442\u0438\u043B \xAB${expected}\xBB`
15011
- ));
15012
- return;
15013
- }
15014
- send({ jsonrpc: "2.0", method: "notifications/initialized" });
15015
- send({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} });
15016
- } else if (response.id === 2) {
15017
- const names = new Set((response.result?.tools ?? []).map((tool) => tool.name));
15018
- const missing = ["setup", "render", "wait"].filter((name) => !names.has(name));
15019
- if (missing.length > 0) {
15020
- finish(new Error(`tools/list \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u043D\u0435 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u0442 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B: ${missing.join(", ")}`));
15021
- return;
15022
- }
15023
- finish();
15024
- }
15197
+ answer(reply);
15025
15198
  }
15026
15199
  });
15027
15200
  send({
@@ -15031,8 +15204,10 @@ var probeShell = async (entry) => {
15031
15204
  params: { protocolVersion: "2025-06-18", capabilities: {}, clientInfo: { name: "update-probe", version: "1" } }
15032
15205
  });
15033
15206
  });
15034
- return version2;
15207
+ return { stdout: `${named}
15208
+ ` };
15035
15209
  } finally {
15210
+ await thread.terminate();
15036
15211
  rmSync(probeHome, { recursive: true, force: true, maxRetries: 3 });
15037
15212
  }
15038
15213
  };
@@ -15183,9 +15358,14 @@ async function dryRun2(build, expected, trial) {
15183
15358
  const { stdout } = await trial(entry);
15184
15359
  const line = stdout.trim().split("\n").pop() ?? "";
15185
15360
  const said = versionFrom(line);
15186
- if (said !== expected) return `\u0441\u0431\u043E\u0440\u043A\u0430 \u043F\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043B\u0430\u0441\u044C \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \xAB${said}\xBB, \u0430 \u0436\u0434\u0430\u043B\u0438 \xAB${expected}\xBB`;
15361
+ if (said !== expected) {
15362
+ const trouble = `\u0441\u0431\u043E\u0440\u043A\u0430 \u043F\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043B\u0430\u0441\u044C \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \xAB${said}\xBB, \u0430 \u0436\u0434\u0430\u043B\u0438 \xAB${expected}\xBB`;
15363
+ noteInstallFault("shell-probe", new Error(trouble));
15364
+ return trouble;
15365
+ }
15187
15366
  return null;
15188
15367
  } catch (trouble) {
15368
+ noteInstallFault("shell-probe", trouble);
15189
15369
  return describeFailure(trouble);
15190
15370
  }
15191
15371
  }
@@ -15830,6 +16010,7 @@ function empty() {
15830
16010
  eta: "",
15831
16011
  file: "",
15832
16012
  outcome: "",
16013
+ say: "",
15833
16014
  trouble: "",
15834
16015
  advice: "",
15835
16016
  complaints: [],
@@ -15840,18 +16021,18 @@ function startedJob(o) {
15840
16021
  return { ...empty(), id: o.id, what: o.what, ahead: o.ahead, message: o.message };
15841
16022
  }
15842
16023
  var INSTALL_TITLES = {
15843
- all: "\u0421\u0442\u0430\u0432\u043B\u044E \u043C\u043E\u043D\u0442\u0430\u0436",
16024
+ 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
16025
  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"
16026
+ 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",
16027
+ 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",
16028
+ 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",
16029
+ model: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044B",
16030
+ browser: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0433\u0440\u0430\u0444\u0438\u043A\u0443",
16031
+ cutout: "\u0423\u0441\u0442\u0430\u043D\u0430\u0432\u043B\u0438\u0432\u0430\u044E \u0437\u0430\u043C\u0435\u043D\u0443 \u0444\u043E\u043D\u0430",
16032
+ 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
16033
  };
15853
16034
  function installTitle(step) {
15854
- return INSTALL_TITLES[step] ?? "\u0421\u0442\u0430\u0432\u043B\u044E \u043C\u043E\u043D\u0442\u0430\u0436";
16035
+ 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
16036
  }
15856
16037
  var INSTALL_HEAD = /^установка: (\S+)$/;
15857
16038
  var HEAD = /^Задание (\S+): ?(.*)$/;
@@ -15862,6 +16043,7 @@ var DONE = /^Готово за (\d+(?:[.,]\d+)?) с\.$/;
15862
16043
  var FILE = /^Файл: (.+)$/;
15863
16044
  var FAILED = /^Не получилось за (\d+(?:[.,]\d+)?) с: (.*)$/;
15864
16045
  var ADVICE = /^Что делать: (.+)$/;
16046
+ var SAY = /^Человеку: (.+)$/;
15865
16047
  var ABORTED = /^Работа прервалась/;
15866
16048
  var ABORTED_SEC = /Прошло (\d+(?:[.,]\d+)?) с\./;
15867
16049
  var WAITING = /^Ждёт очереди/;
@@ -15943,7 +16125,7 @@ function readJob(text2) {
15943
16125
  if (failed !== null) {
15944
16126
  job.status = "error";
15945
16127
  job.sec = seconds(failed[1]);
15946
- job.trouble = failed[2] ?? "";
16128
+ job.trouble = withoutPaths(failed[2] ?? "");
15947
16129
  tail2 = null;
15948
16130
  continue;
15949
16131
  }
@@ -15960,15 +16142,20 @@ function readJob(text2) {
15960
16142
  tail2 = null;
15961
16143
  continue;
15962
16144
  }
16145
+ const say = SAY.exec(line);
16146
+ if (say !== null) {
16147
+ job.say = say[1] ?? "";
16148
+ continue;
16149
+ }
15963
16150
  if (WAITING.test(line)) {
15964
16151
  job.status = "waiting";
15965
16152
  job.message = line;
15966
16153
  continue;
15967
16154
  }
15968
- const running = RUNNING.exec(line);
15969
- if (running !== null) {
16155
+ const running2 = RUNNING.exec(line);
16156
+ if (running2 !== null) {
15970
16157
  job.status = "running";
15971
- job.sec = seconds(running[1]);
16158
+ job.sec = seconds(running2[1]);
15972
16159
  continue;
15973
16160
  }
15974
16161
  if (tail2 !== null) {
@@ -15987,6 +16174,9 @@ function readJob(text2) {
15987
16174
  }
15988
16175
  return job;
15989
16176
  }
16177
+ function withoutPaths(text2) {
16178
+ return text2.replace(/(^|[\s(«"])(?:[A-Za-z]:\\|~?\/)[^\s,;()«»"]*/g, "$1").replace(/\(\s*\)/g, "").replace(/\s{2,}/g, " ").replace(/\s+([.,;:])/g, "$1").trim();
16179
+ }
15990
16180
  function seconds(text2) {
15991
16181
  if (text2 === void 0) return 0;
15992
16182
  const value = Number(text2.replace(",", "."));
@@ -16001,6 +16191,8 @@ var FLUSH_EVERY_MS = 6e4;
16001
16191
  var SEND_TIMEOUT_MS = 3e3;
16002
16192
  var JOB_LOG_TAIL = 64 * 1024;
16003
16193
  var PYTHON_TRACEBACK = "Traceback (most recent call last):";
16194
+ var SUPPORT_TOPIC = "https://t.me/c/3763431875/10";
16195
+ var WORDS_GRACE_MS = 1e3;
16004
16196
  var CrashReporter = class {
16005
16197
  #deps;
16006
16198
  #now;
@@ -16039,9 +16231,31 @@ var CrashReporter = class {
16039
16231
  noteHost(host) {
16040
16232
  if (host !== null) this.#host = host;
16041
16233
  }
16042
- /** Отправить отчёт мимо ответа. Для тестов и смоука — `settled()`. */
16234
+ /**
16235
+ * Отправить отчёт. Ответ инструмента ждёт итог через {@link wordsFor}, а
16236
+ * всё прочее — нет; для тестов и смоука — `settled()`.
16237
+ */
16043
16238
  submit(crash) {
16044
- this.#track(this.#deliver(crash));
16239
+ const delivery = this.#deliver(crash).catch(() => "none");
16240
+ this.#track(delivery.then(() => void 0));
16241
+ return delivery;
16242
+ }
16243
+ /**
16244
+ * Итог отправки для слов человеку — не дольше таймаута отправки с запасом.
16245
+ * Не успела — отчёт ещё в пути и либо уйдёт, либо ляжет в очередь: «уйдёт»
16246
+ * правда в обоих случаях.
16247
+ */
16248
+ async outcome(delivery) {
16249
+ let timer;
16250
+ const late = new Promise((done) => {
16251
+ timer = setTimeout(() => done("queued"), (this.#deps.timeoutMs ?? SEND_TIMEOUT_MS) + WORDS_GRACE_MS);
16252
+ timer.unref?.();
16253
+ });
16254
+ try {
16255
+ return await Promise.race([delivery, late]);
16256
+ } finally {
16257
+ clearTimeout(timer);
16258
+ }
16045
16259
  }
16046
16260
  /**
16047
16261
  * Процесс падает: отчёт кладётся на диск СИНХРОННО и уходит на следующем
@@ -16091,10 +16305,19 @@ var CrashReporter = class {
16091
16305
  }
16092
16306
  async #deliver(crash) {
16093
16307
  const report = this.#build(crash);
16094
- if (report === null) return;
16308
+ if (report === null) return this.#queuedAlready(fingerprintOf2(crash.errorType, crash.frames)) ? "queued" : "sent";
16095
16309
  report.versions.ffmpeg = await this.#ffmpegVersion();
16096
16310
  const verdict = await this.#send(report);
16097
- if (verdict === "retry") this.#enqueue(report);
16311
+ if (verdict === "sent") return "sent";
16312
+ if (verdict === "drop") return "none";
16313
+ return this.#enqueue(report) ? "queued" : "none";
16314
+ }
16315
+ #queuedAlready(fingerprint) {
16316
+ try {
16317
+ return readdirSync(this.#queueDir()).some((f) => f.endsWith(`-${fingerprint}.json`));
16318
+ } catch {
16319
+ return false;
16320
+ }
16098
16321
  }
16099
16322
  async #flush() {
16100
16323
  const dir = this.#queueDir();
@@ -16191,6 +16414,7 @@ var CrashReporter = class {
16191
16414
  recent_calls: recentCalls(this.#deps.home)
16192
16415
  };
16193
16416
  }
16417
+ /** Положить отчёт в очередь. `false` — диск не принял, отчёт пропал. */
16194
16418
  #enqueue(report) {
16195
16419
  try {
16196
16420
  const dir = this.#queueDir();
@@ -16203,7 +16427,9 @@ var CrashReporter = class {
16203
16427
  for (const old of files.slice(0, Math.max(0, files.length - QUEUE_CAP))) {
16204
16428
  rmSync(join(dir, old), { force: true });
16205
16429
  }
16430
+ return true;
16206
16431
  } catch {
16432
+ return false;
16207
16433
  }
16208
16434
  }
16209
16435
  #queueDir() {
@@ -16252,49 +16478,83 @@ function withCrashReport(crashes) {
16252
16478
  crashes.noteHost(host);
16253
16479
  crashes.noteSecrets(secretsOf(input));
16254
16480
  let result;
16481
+ let faults;
16255
16482
  try {
16256
- result = await original(input, ...rest);
16483
+ ({ value: result, faults } = await collectInstallFaults(() => original(input, ...rest)));
16257
16484
  } catch (trouble) {
16258
16485
  const crash = fromThrown(trouble, "tool", tool.name, input, host);
16259
- crashes.submit(crash);
16260
- return toolError(brokenWords(crash, crashes));
16486
+ return toolError(await brokenWords(crash, crashes, crashes.submit(crash)));
16487
+ }
16488
+ const install2 = firstInstallCrash(faults);
16489
+ if (install2 !== null) {
16490
+ const crash = { ...install2, where: "tool", tool: tool.name, secrets: secretsOf(input), host };
16491
+ return withNotice(result, await brokenWords(crash, crashes, crashes.submit(crash)));
16261
16492
  }
16262
16493
  return caught(result, tool.name, input, host, crashes);
16263
16494
  }
16264
16495
  };
16265
16496
  };
16266
16497
  }
16267
- function caught(result, name, input, host, crashes) {
16498
+ async function reportInstallFaults(crashes, work) {
16499
+ const { value, faults } = await collectInstallFaults(work);
16500
+ const install2 = firstInstallCrash(faults);
16501
+ if (install2 !== null) {
16502
+ 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 });
16503
+ }
16504
+ return value;
16505
+ }
16506
+ function firstInstallCrash(faults) {
16507
+ for (const fault of faults) {
16508
+ const parsed = installCrash(fault);
16509
+ if (parsed !== null) return parsed;
16510
+ }
16511
+ return null;
16512
+ }
16513
+ function withNotice(result, notice) {
16514
+ if (result.type === "error") return { ...result, error: `${result.error}
16515
+
16516
+ ${notice}` };
16517
+ return typeof result.content === "string" ? { ...result, content: `${result.content}
16518
+
16519
+ ${notice}` } : { ...result, content: [...result.content, { type: "text", text: notice }] };
16520
+ }
16521
+ async function caught(result, name, input, host, crashes) {
16268
16522
  if (result.type === "error") {
16269
16523
  const parsed2 = pythonCrash(result.error);
16270
16524
  if (parsed2 === null) return result;
16271
16525
  const crash2 = { ...parsed2, where: "tool", tool: name, stage: null, secrets: secretsOf(input), host };
16272
- crashes.submit(crash2);
16273
- return toolError(brokenWords(crash2, crashes));
16526
+ return toolError(await brokenWords(crash2, crashes, crashes.submit(crash2)));
16274
16527
  }
16275
16528
  const job = result.structured;
16276
16529
  if (job === void 0 || job["contract"] !== JOB_CONTRACT || job["status"] !== "error") return result;
16277
16530
  const id = typeof job["id"] === "string" ? job["id"] : "";
16278
16531
  if (!/^[\w.-]{1,80}$/.test(id) || crashes.jobSeen(id)) return result;
16279
- const parsed = pythonCrash(jobLogTail(crashes.home, id));
16532
+ const fromLog = pythonCrash(jobLogTail(crashes.home, id));
16533
+ const install2 = fromLog === null ? firstInstallCrash(jobInstallFaults(id)) : null;
16534
+ const parsed = fromLog ?? install2;
16280
16535
  if (parsed === null) return result;
16281
16536
  crashes.rememberJob(id);
16282
- const stage = typeof job["now"] === "string" && job["now"] !== "" ? job["now"] : null;
16537
+ const stage = install2?.stage ?? (typeof job["now"] === "string" && job["now"] !== "" ? job["now"] : null);
16283
16538
  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() }] };
16539
+ return withNotice(result, await brokenWords(crash, crashes, crashes.submit(crash)));
16289
16540
  }
16290
- function brokenWords(crash, crashes) {
16541
+ async function brokenWords(crash, crashes, delivery) {
16291
16542
  const essence = head(scrub(head(crash.essence, 600), crashes.scrubContext(crash.secrets)), 300);
16543
+ const fate = crashes.canSend ? await crashes.outcome(delivery) : "none";
16292
16544
  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}.`,
16545
+ `\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
16546
  `\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
16547
  ].join("\n");
16297
16548
  }
16549
+ function humanWords(fate) {
16550
+ if (fate === "sent") {
16551
+ 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}`;
16552
+ }
16553
+ if (fate === "queued") {
16554
+ 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}`;
16555
+ }
16556
+ 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}`;
16557
+ }
16298
16558
  function watchProcessCrashes(crashes, proc = process, exit = (code) => process.exit(code)) {
16299
16559
  const fall = (trouble) => {
16300
16560
  crashes.persist(fromThrown(trouble, "process", null, {}, null));
@@ -16329,6 +16589,95 @@ function fromThrown(trouble, where, tool, input, host) {
16329
16589
  essence: `${errorType}: ${error40.message}${frames[0] ? ` \u2014 ${frames[0].file}` : ""}`
16330
16590
  };
16331
16591
  }
16592
+ function installCrash(fault) {
16593
+ const { stage, trouble } = fault;
16594
+ if (machineCause(trouble)) return null;
16595
+ const said = commandOutput(trouble);
16596
+ if (said.includes(PYTHON_TRACEBACK)) {
16597
+ const parsed = pythonCrash(said);
16598
+ return parsed === null ? null : { ...parsed, stage };
16599
+ }
16600
+ const error40 = trouble instanceof Error ? trouble : new Error(String(trouble));
16601
+ const reason = lastMeaningfulLine(said) ?? lastMeaningfulLine(error40.message) ?? error40.name;
16602
+ const command = typeof trouble.cmd === "string" ? trouble.cmd : "";
16603
+ const trace = [
16604
+ command === "" ? "" : `\u043A\u043E\u043C\u0430\u043D\u0434\u0430: ${command}`,
16605
+ said.trim() === "" ? "" : `\u0432\u044B\u0432\u043E\u0434 \u043A\u043E\u043C\u0430\u043D\u0434\u044B:
16606
+ ${said.trim().slice(-4e3)}`,
16607
+ error40.stack ?? `${error40.name}: ${error40.message}`
16608
+ ].filter((part) => part !== "").join("\n\n");
16609
+ return {
16610
+ stage,
16611
+ errorType: "InstallError",
16612
+ message: `${stage}: ${reason}`,
16613
+ trace,
16614
+ keep: "head",
16615
+ // ⚠ ПРИЧИНА В ОТПЕЧАТКЕ — БЕЗ ПУТЕЙ И ЧИСЕЛ. Иначе все беды шага склеились
16616
+ // бы в один отпечаток, а сервер пишет в бот про отпечаток раз в час: вторая,
16617
+ // другая беда того же шага промолчала бы. А с путями и версиями одна и та
16618
+ // же беда у двух покупателей считалась бы двумя.
16619
+ frames: [`install:${stage}`, reason.replace(/(?:[A-Za-z]:)?[\\/][^\s'"]*/g, "<path>").replace(/\d+/g, "0").slice(0, 160)],
16620
+ python: /python@?(\d)\.(\d{1,2})\b/i.exec(command)?.slice(1, 3).join(".") ?? null,
16621
+ essence: `\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430, \u0448\u0430\u0433 ${stage}: ${reason}`
16622
+ };
16623
+ }
16624
+ function machineCause(trouble) {
16625
+ const t = trouble ?? {};
16626
+ if (typeof t.code === "string" && MACHINE_CODES.has(t.code)) return true;
16627
+ if (t.code === 2) return true;
16628
+ if (t.killed === true || typeof t.signal === "string" && t.signal !== "") return true;
16629
+ const text2 = `${typeof t.message === "string" ? t.message : String(trouble)}
16630
+ ${commandOutput(trouble)}`;
16631
+ return MACHINE_WORDS.test(text2);
16632
+ }
16633
+ var MACHINE_CODES = /* @__PURE__ */ new Set([
16634
+ "ENOSPC",
16635
+ "EDQUOT",
16636
+ "EACCES",
16637
+ "EPERM",
16638
+ "EROFS",
16639
+ "EBUSY",
16640
+ "ENOTFOUND",
16641
+ "EAI_AGAIN",
16642
+ "ECONNRESET",
16643
+ "ECONNREFUSED",
16644
+ "ETIMEDOUT",
16645
+ "ENETUNREACH",
16646
+ "ENETDOWN",
16647
+ "EHOSTUNREACH"
16648
+ ]);
16649
+ var MACHINE_WORDS = new RegExp([
16650
+ "No space left on device",
16651
+ "Disk quota exceeded",
16652
+ "Permission denied",
16653
+ "Operation not permitted",
16654
+ "Read-only file system",
16655
+ "Access is denied",
16656
+ "Temporary failure in name resolution",
16657
+ "Name or service not known",
16658
+ "nodename nor servname",
16659
+ "getaddrinfo",
16660
+ "Network is unreachable",
16661
+ "No route to host",
16662
+ "Connection refused",
16663
+ "Connection reset",
16664
+ "Connection aborted",
16665
+ "timed out",
16666
+ "Max retries exceeded",
16667
+ "NewConnectionError",
16668
+ "ConnectTimeoutError",
16669
+ "ReadTimeoutError",
16670
+ "ProxyError",
16671
+ "CERTIFICATE_VERIFY_FAILED"
16672
+ ].map((one) => one.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "i");
16673
+ function commandOutput(trouble) {
16674
+ const t = trouble ?? {};
16675
+ return [t.stderr, t.stdout].filter((one) => typeof one === "string" && one.trim() !== "").join("\n");
16676
+ }
16677
+ function lastMeaningfulLine(text2) {
16678
+ const lines = text2.split("\n").map((one) => one.trim()).filter((one) => one !== "" && !/^Command failed:/.test(one));
16679
+ return lines[lines.length - 1] ?? null;
16680
+ }
16332
16681
  function jsFrames(stack) {
16333
16682
  const frames = [];
16334
16683
  for (const line of stack.split("\n")) {
@@ -16526,7 +16875,6 @@ function readReadiness(stdout) {
16526
16875
  var NOT_A_ROW = /* @__PURE__ */ new Set(["system"]);
16527
16876
  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
16877
  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
16878
  function weight(mb) {
16531
16879
  if (mb >= 1e3) return `${(mb / 1024).toFixed(1).replace(".", ",")} \u0413\u0411`;
16532
16880
  return `${mb} \u041C\u0411`;
@@ -16547,11 +16895,23 @@ function refusalWords(status) {
16547
16895
  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
16896
  return "";
16549
16897
  }
16898
+ function exactWeight(mb) {
16899
+ if (mb >= 1e3) return weight(mb);
16900
+ return `${mb.toFixed(1).replace(".", ",")} \u041C\u0411`;
16901
+ }
16902
+ function whatIsNew(shell, engine) {
16903
+ const parts = [];
16904
+ for (const [news, name] of [[engine, "\u0434\u0432\u0438\u0436\u043E\u043A"], [shell, "\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435"]]) {
16905
+ if (!news?.fresh) continue;
16906
+ parts.push(news.mb > 0 ? `${name} \xB7 ${exactWeight(news.mb)}` : name);
16907
+ }
16908
+ return parts.join(" \u0438 ");
16909
+ }
16550
16910
  function updateWords(shell, engine) {
16551
16911
  if (!shell?.fresh && !engine?.fresh) return "";
16552
16912
  const notes = shell?.fresh ? firstPhrase(shell.notes) : "";
16553
16913
  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}` : ""}`,
16914
+ `\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
16915
  "\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0434\u043E \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438?",
16556
16916
  "\u041F\u0440\u043E\u0441\u0442\u043E \u043D\u0430\u043F\u0438\u0448\u0438 \xAB\u043E\u0431\u043D\u043E\u0432\u0438\xBB."
16557
16917
  ].join("\n");
@@ -17370,10 +17730,19 @@ function runbarWidgetHtml() {
17370
17730
  document.body.classList.remove('bad');
17371
17731
  if (!good) document.body.classList.add('bad');
17372
17732
 
17373
- if (good) {
17733
+ if (good && data.file) {
17374
17734
  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 || '');
17735
+ show('endHint', '\u0420\u043E\u043B\u0438\u043A \u0433\u043E\u0442\u043E\u0432:');
17736
+ show('path', data.file);
17737
+ } else if (good) {
17738
+ // \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
17739
+ // \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.
17740
+ // \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.
17741
+ 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.';
17742
+ var cut = /^(.+?)[.!]\\s+(.+)$/.exec(say);
17743
+ show('endTitle', cut ? cut[1] : say.replace(/[.]$/, ''));
17744
+ show('endHint', cut ? cut[2] : '');
17745
+ show('path', '');
17377
17746
  } else {
17378
17747
  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
17748
  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 +18092,226 @@ function nameOrRefusal(candidate) {
17723
18092
  };
17724
18093
  }
17725
18094
 
18095
+ // src/setup-job.ts
18096
+ var SETUP_JOB_MARK = "setup-";
18097
+ function isSetupJob(id) {
18098
+ return id.startsWith(SETUP_JOB_MARK);
18099
+ }
18100
+ 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";
18101
+ 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";
18102
+ 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.";
18103
+ 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.";
18104
+ 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.";
18105
+ 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.";
18106
+ var CHECK = "\u041F\u0440\u043E\u0432\u0435\u0440\u044F\u044E, \u0447\u0442\u043E \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C";
18107
+ var INSTALL_NEW = "\u0421\u0442\u0430\u0432\u043B\u044E \u043D\u043E\u0432\u0443\u044E \u0432\u0435\u0440\u0441\u0438\u044E";
18108
+ var MISSING = "\u0421\u0442\u0430\u0432\u043B\u044E \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u044E\u0449\u0435\u0435";
18109
+ var works = /* @__PURE__ */ new Map();
18110
+ function running() {
18111
+ for (const work of works.values()) if (work.ended === null) return work;
18112
+ return null;
18113
+ }
18114
+ function startSetupJob(what, run, clock = Date.now) {
18115
+ const already = running();
18116
+ if (already !== null) return already.id;
18117
+ const started = clock();
18118
+ const id = `${SETUP_JOB_MARK}${new Date(started).toISOString().replace(/[:.]/g, "-")}`;
18119
+ const work = {
18120
+ id,
18121
+ what,
18122
+ started,
18123
+ stages: [CHECK],
18124
+ at: 1,
18125
+ message: "",
18126
+ ended: null,
18127
+ done: Promise.resolve()
18128
+ };
18129
+ const control = {
18130
+ plan(stages) {
18131
+ work.stages = stages;
18132
+ work.at = Math.min(work.at, stages.length);
18133
+ },
18134
+ go(stage) {
18135
+ const index = work.stages.indexOf(stage);
18136
+ if (index >= 0) work.at = index + 1;
18137
+ work.message = "";
18138
+ },
18139
+ line(text2) {
18140
+ work.message = text2;
18141
+ }
18142
+ };
18143
+ works.set(id, work);
18144
+ work.done = collectJobInstallFaults(id, () => run(control).catch((trouble) => {
18145
+ noteInstallFault("update-job", trouble);
18146
+ return {
18147
+ ok: false,
18148
+ 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.",
18149
+ advice: ADVICE_ASK,
18150
+ report: String(trouble instanceof Error ? trouble.message : trouble)
18151
+ };
18152
+ })).then((ending) => {
18153
+ work.ended = { at: clock(), ending };
18154
+ });
18155
+ return id;
18156
+ }
18157
+ function quiet(line) {
18158
+ return line.replace(/^⚠\s*/, "").replace(/^Что делать:/, "\u0421\u043E\u0432\u0435\u0442:");
18159
+ }
18160
+ function setupJobText(id, clock = Date.now) {
18161
+ const work = works.get(id);
18162
+ if (work === void 0) return null;
18163
+ const head2 = `\u0417\u0430\u0434\u0430\u043D\u0438\u0435 ${work.id}: ${work.what}`;
18164
+ if (work.ended === null) {
18165
+ const sec2 = Math.max(0, Math.round((clock() - work.started) / 1e3));
18166
+ return [
18167
+ head2,
18168
+ `\u042D\u0442\u0430\u043F ${work.at} \u0438\u0437 ${work.stages.length}: ${work.stages[work.at - 1] ?? CHECK}`,
18169
+ ...work.message === "" ? [] : [work.message],
18170
+ `\u0415\u0449\u0451 \u0441\u0447\u0438\u0442\u0430\u0435\u0442, ${sec2} \u0441.`
18171
+ ].join("\n");
18172
+ }
18173
+ const sec = Math.max(0, Math.round((work.ended.at - work.started) / 1e3));
18174
+ const ending = work.ended.ending;
18175
+ const report = ending.report.split("\n").map((line) => line.trim()).filter((line) => line !== "").map(quiet);
18176
+ if (ending.ok) {
18177
+ 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");
18178
+ }
18179
+ return [
18180
+ head2,
18181
+ ...report,
18182
+ `\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C \u0437\u0430 ${sec} \u0441: ${ending.trouble}`,
18183
+ `\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: ${ending.advice}`
18184
+ ].join("\n");
18185
+ }
18186
+ function setupJobReport(id) {
18187
+ const text2 = setupJobText(id);
18188
+ return text2 === null ? null : readJob(text2);
18189
+ }
18190
+ function weightExact(bytes) {
18191
+ const mb = bytes / 1024 / 1024;
18192
+ if (mb >= 1e3) return `${(mb / 1024).toFixed(1).replace(".", ",")} \u0413\u0411`;
18193
+ return `${mb.toFixed(1).replace(".", ",")} \u041C\u0411`;
18194
+ }
18195
+ async function runUpdate(job, deps) {
18196
+ const { engine, key, baseUrl, what } = deps;
18197
+ const home = engine.home();
18198
+ const wantShell = what !== "engine";
18199
+ const hasEngine = engine.place() !== null;
18200
+ const wantEngine = what !== "shell" && hasEngine && !engine.pathSetByHand();
18201
+ const shellUrl = engine.variable("NAPARNIK_BUNDLE_MANIFEST") ?? bundleUrlFor(baseUrl);
18202
+ const [shellAsk, engineAsk] = await Promise.all([
18203
+ wantShell ? fetchShellManifest(shellUrl, deps.pointerMs, key) : Promise.resolve(null),
18204
+ wantEngine ? fetchManifest(manifestUrl(engine, baseUrl), deps.pointerMs, key) : Promise.resolve(null)
18205
+ ]);
18206
+ const shellWas = installedShell(home);
18207
+ const shell = decideShell(shellWas, shellAsk?.ok === true ? shellAsk.manifest : null);
18208
+ const engineWas = engine.freshVersion();
18209
+ const fresh = decide(engineWas, engineAsk?.ok === true ? engineAsk.manifest : null);
18210
+ 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;
18211
+ 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;
18212
+ const missing = what === "all" && engineStage === null;
18213
+ job.plan([
18214
+ CHECK,
18215
+ ...shellStage === null ? [] : [shellStage],
18216
+ ...engineStage === null ? [] : [engineStage, INSTALL_NEW],
18217
+ ...missing ? [MISSING] : []
18218
+ ]);
18219
+ const steps = [];
18220
+ let updated = false;
18221
+ let installed = false;
18222
+ for (const [ask2, whose] of [[shellAsk, "\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F"], [engineAsk, "\u0434\u0432\u0438\u0436\u043A\u0430"]]) {
18223
+ if (ask2 === null || ask2.ok) continue;
18224
+ const refusal = refusalWords(ask2.status ?? 0);
18225
+ steps.push({
18226
+ 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}.`,
18227
+ 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."
18228
+ });
18229
+ }
18230
+ if (shellAsk?.ok === true && shell.what === "nothing") {
18231
+ 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}.` });
18232
+ }
18233
+ if (shell.what === "launcher_too_old") {
18234
+ steps.push({
18235
+ 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.`,
18236
+ 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}.`
18237
+ });
18238
+ }
18239
+ if (fresh.what === "shell_too_old") {
18240
+ steps.push({
18241
+ 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}.`,
18242
+ 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."
18243
+ });
18244
+ }
18245
+ if (shell.what === "newer_available" && shellStage !== null) {
18246
+ job.go(shellStage);
18247
+ const outcome = await installShell({ home, manifest: shell.manifest, key: key ?? null });
18248
+ if (outcome.ok) {
18249
+ updated = true;
18250
+ steps.push({ report: outcome.text });
18251
+ } else {
18252
+ 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." });
18253
+ }
18254
+ }
18255
+ if (fresh.what === "newer_available" && engineStage !== null) {
18256
+ job.go(engineStage);
18257
+ const outcome = await deploy(engine, fresh.manifest, void 0, key, {
18258
+ line: (text2) => job.line(text2),
18259
+ downloaded: () => {
18260
+ job.go(INSTALL_NEW);
18261
+ 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.");
18262
+ }
18263
+ });
18264
+ if (outcome.ok) {
18265
+ updated = true;
18266
+ steps.push({ report: outcome.text });
18267
+ } else {
18268
+ steps.push({
18269
+ report: outcome.text,
18270
+ 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.`
18271
+ });
18272
+ }
18273
+ }
18274
+ if (missing) {
18275
+ job.go(MISSING);
18276
+ const step = await deliverMissing(job, deps);
18277
+ if (step.trouble === void 0 && step.installed) installed = true;
18278
+ steps.push(step);
18279
+ }
18280
+ const report = steps.map((step) => step.report).join("\n");
18281
+ const trouble = steps.find((step) => step.trouble !== void 0)?.trouble;
18282
+ if (trouble !== void 0) return { ok: false, trouble, advice: ADVICE_ASK, report };
18283
+ if (updated) return { ok: true, say: SAY_RESTART, report };
18284
+ return { ok: true, say: installed ? SAY_INSTALLED : SAY_NOTHING, report };
18285
+ }
18286
+ async function deliverMissing(job, deps) {
18287
+ const answer = await deps.installMissing();
18288
+ if (answer.type === "error") {
18289
+ 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 };
18290
+ }
18291
+ const words = typeof answer.content === "string" ? answer.content : answer.content.map((block) => block.type === "text" ? block.text : "").join("\n");
18292
+ const id = typeof answer.structured?.["id"] === "string" ? answer.structured["id"] : "";
18293
+ if (id === "") return { report: words, installed: false };
18294
+ const pace = deps.pace ?? realPace;
18295
+ let state = await deps.engine.command(["job", id]);
18296
+ while (state.ok && stillWorking(state.text)) {
18297
+ const now = readJob(state.text);
18298
+ if (now !== null) job.line(now.message !== "" ? now.message : now.now);
18299
+ await pace.rest(POLL_EVERY_MS);
18300
+ state = await deps.engine.command(["job", id]);
18301
+ }
18302
+ const end = state.ok ? readJob(state.text) : null;
18303
+ if (end === null || end.status !== "done") {
18304
+ return {
18305
+ report: `${words}
18306
+ ${state.text}`,
18307
+ 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.",
18308
+ installed: false
18309
+ };
18310
+ }
18311
+ return { report: `${words}
18312
+ ${state.text}`, installed: true };
18313
+ }
18314
+
17726
18315
  // src/setup-tool.ts
17727
18316
  var ASKED_POINTER_MS = 15e3;
17728
18317
  var NEWS_POINTER_MS = 5e3;
@@ -17876,6 +18465,9 @@ ${EXTRAS_LATER}`), later]);
17876
18465
  })
17877
18466
  );
17878
18467
  }
18468
+ function tenths(bytes) {
18469
+ return Math.round(bytes / 1024 / 1024 * 10) / 10;
18470
+ }
17879
18471
  async function updateEngine(engine, consent, key, baseUrl, timeoutMs = ASKED_POINTER_MS, seen = () => {
17880
18472
  }) {
17881
18473
  if (engine.pathSetByHand()) {
@@ -17893,7 +18485,7 @@ async function updateEngine(engine, consent, key, baseUrl, timeoutMs = ASKED_POI
17893
18485
  seen({
17894
18486
  installed: engine.freshVersion(),
17895
18487
  fresh: decision.what === "nothing" ? null : decision.manifest.version,
17896
- mb: decision.what === "nothing" ? 0 : Math.round(decision.manifest.bytes / 1024 / 1024),
18488
+ mb: decision.what === "nothing" ? 0 : tenths(decision.manifest.bytes),
17897
18489
  status: outcome.ok ? 0 : outcome.status ?? 0,
17898
18490
  notes: ""
17899
18491
  });
@@ -17928,7 +18520,7 @@ async function updateShell(engine, consent, key, baseUrl, timeoutMs = ASKED_POIN
17928
18520
  seen({
17929
18521
  installed,
17930
18522
  fresh: decision.what === "nothing" ? null : decision.manifest.version,
17931
- mb: decision.what === "nothing" ? 0 : Math.round((decision.manifest.body?.bytes ?? 0) / 1024 / 1024),
18523
+ mb: decision.what === "nothing" ? 0 : tenths(decision.manifest.body?.bytes ?? 0),
17932
18524
  status: outcome.ok ? 0 : outcome.status ?? 0,
17933
18525
  notes: decision.what === "nothing" ? "" : decision.manifest.notes ?? ""
17934
18526
  });
@@ -17939,7 +18531,7 @@ async function updateShell(engine, consent, key, baseUrl, timeoutMs = ASKED_POIN
17939
18531
  );
17940
18532
  }
17941
18533
  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.`
18534
+ 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
18535
  );
17944
18536
  }
17945
18537
  if (decision.what === "launcher_too_old") {
@@ -17972,7 +18564,7 @@ function setupTool(engine, passport, key, baseUrl) {
17972
18564
  return defineTool({
17973
18565
  name: "setup",
17974
18566
  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.",
18567
+ 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
18568
  // Полоса хода работы в ленте: установка качает десятки минут, и человек
17977
18569
  // видит, что качается и сколько уже скачано. ⚠ Ожидание задачи — у
17978
18570
  // отдельного инструмента `wait`, без полосы: полоса объявлена на
@@ -18024,9 +18616,6 @@ function setupTool(engine, passport, key, baseUrl) {
18024
18616
  engine: news.engine ?? { installed: engine.freshVersion(), fresh: null, mb: 0, status: 0, notes: "" },
18025
18617
  shell: news.shell,
18026
18618
  note: firstLine(said),
18027
- // Проверки здесь не было, а работа была: «Проверяю, что установлено»
18028
- // над ответом «поставил новую версию» соврало бы о том, что идёт.
18029
- ...args.consent === true && args.what !== void 0 && UPDATES.has(args.what) ? { title: TITLE_UPDATING } : {},
18030
18619
  shellVersion: VERSION
18031
18620
  }));
18032
18621
  }
@@ -18037,6 +18626,7 @@ function setupTool(engine, passport, key, baseUrl) {
18037
18626
  const consent = args.consent === true;
18038
18627
  const what = args.what;
18039
18628
  if (what === void 0) return lookAround(engine, args.model, key, baseUrl);
18629
+ if (consent && UPDATES.has(what)) return startUpdate(engine, what, key, baseUrl);
18040
18630
  if (what === "engine") {
18041
18631
  return updateEngine(engine, consent, key, baseUrl, ASKED_POINTER_MS, (seen) => {
18042
18632
  if (!consent) news.engine = seen;
@@ -18048,7 +18638,7 @@ function setupTool(engine, passport, key, baseUrl) {
18048
18638
  });
18049
18639
  }
18050
18640
  if (what === "all") {
18051
- return consent ? installEverything(engine, key, baseUrl, tell) : lookAround(engine, args.model, key, baseUrl);
18641
+ return lookAround(engine, args.model, key, baseUrl);
18052
18642
  }
18053
18643
  const step = HEAVY_STEPS[what];
18054
18644
  return consent ? installStep(engine, { step, model: args.model, consent: true }, key, baseUrl, tell) : askBeforeHeavy(engine, step, args.model, key, baseUrl);
@@ -18094,7 +18684,20 @@ async function lookAround(engine, model, key, baseUrl) {
18094
18684
  updateEngine(engine, false, key, baseUrl, NEWS_POINTER_MS, toEngine),
18095
18685
  shell
18096
18686
  ]);
18097
- return card(together([machine.result, news, own]), { readiness: machine.readiness });
18687
+ return card(
18688
+ together([newsForModel(shellNews, engineNews), machine.result, news, own]),
18689
+ { readiness: machine.readiness }
18690
+ );
18691
+ }
18692
+ function newsForModel(shell, engine) {
18693
+ if (!shell?.fresh && !engine?.fresh) return toolSuccess("");
18694
+ const numbers = [
18695
+ engine?.fresh ? `\u0434\u0432\u0438\u0436\u043E\u043A ${engine.installed ?? "\u043D\u0435\u0442"} \u2192 ${engine.fresh}` : "",
18696
+ shell?.fresh ? `\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435 ${shell.installed ?? VERSION} \u2192 ${shell.fresh}` : ""
18697
+ ].filter((one) => one !== "").join(", ");
18698
+ return toolSuccess(
18699
+ `\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.`
18700
+ );
18098
18701
  }
18099
18702
  function withCard(result, card) {
18100
18703
  if (result.type !== "success" || result.structured !== void 0) return result;
@@ -18104,11 +18707,22 @@ function firstLine(result) {
18104
18707
  const text2 = result.type === "success" ? wordsOf(result.content) : result.error;
18105
18708
  return text2.split("\n").map((line) => line.trim()).find((line) => line !== "") ?? "";
18106
18709
  }
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);
18710
+ function startUpdate(engine, what, key, baseUrl) {
18711
+ const title = engine.place() === null ? TITLE_INSTALL : TITLE_UPDATE;
18712
+ const id = startSetupJob(title, (job) => runUpdate(job, {
18713
+ engine,
18714
+ key,
18715
+ baseUrl,
18716
+ what,
18717
+ pointerMs: ASKED_POINTER_MS,
18718
+ installMissing: () => installMissing(engine, key, baseUrl, void 0)
18719
+ }));
18720
+ const report = setupJobReport(id);
18721
+ return toolSuccess(
18722
+ `\u0417\u0430\u0434\u0430\u0447\u0430 ${id}: ${report?.what ?? title}.
18723
+ \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.',
18724
+ report ?? void 0
18725
+ );
18112
18726
  }
18113
18727
  async function installMissing(engine, key, baseUrl, tell) {
18114
18728
  const place = engine.place();
@@ -18241,7 +18855,14 @@ var RENDER_LADDER = [
18241
18855
  ];
18242
18856
  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.";
18243
18857
  async function followWork(engine, asked, pace = realPace, tell) {
18244
- const ask2 = () => engine.command(["job", asked.job]);
18858
+ const ask2 = async () => {
18859
+ if (!isSetupJob(asked.job)) return engine.command(["job", asked.job]);
18860
+ const text2 = setupJobText(asked.job);
18861
+ return text2 !== null ? { ok: true, text: text2 } : {
18862
+ ok: false,
18863
+ 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.`
18864
+ };
18865
+ };
18245
18866
  const seconds2 = waitSeconds(asked.seconds ?? WAIT_LIMIT_SECONDS);
18246
18867
  if (seconds2 === 0) return asJobResult(await ask2());
18247
18868
  const state = await followJob({
@@ -18391,6 +19012,9 @@ function waitTool(engine, pace = realPace) {
18391
19012
  }),
18392
19013
  readOnly: true,
18393
19014
  async execute(args, ctx) {
19015
+ if (args.job !== void 0 && isSetupJob(args.job)) {
19016
+ return followWork(engine, { job: args.job, seconds: args.seconds }, pace, ctx?.emitProgress);
19017
+ }
18394
19018
  const place = engine.place();
18395
19019
  if (place === null) return toolError(engine.whyNoEngine());
18396
19020
  if (args.job === void 0) {
@@ -22541,4 +23165,4 @@ ${inTable}`, file2);
22541
23165
  };
22542
23166
  }
22543
23167
 
22544
- 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 };
23168
+ 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 };