@naparnik/mcp 0.10.38 → 0.10.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Изменения @naparnik/mcp
2
2
 
3
+ ## 0.10.41 — 2026-09-24. Новый файл из бота не выключает монтаж раньше времени
4
+
5
+ Взяли в боте новый файл — прежняя установка работает, пока вы его не
6
+ поставите. Поставили здесь или на другом компьютере — прежняя
7
+ выключается.
8
+
9
+ Если монтаж отказал, потому что ключ сменился, вы так и услышите:
10
+ поставьте последний файл из бота.
11
+
12
+ ## 0.10.40 — 2026-09-24. Видно, что стоит и что качается
13
+
14
+ Под ответом проверки больше нет пустой серой рамки. Там карточка: что
15
+ уже стоит, чего не хватает и сколько это весит, вышло ли новое и что
16
+ сказать дальше.
17
+
18
+ Пока идёт установка, видна полоса: сколько скачано из скольких, процент
19
+ и сколько ещё ждать. Модель распознавания речи теперь тоже показывает
20
+ ход, а не молчит десять минут.
21
+
3
22
  ## 0.10.38 — 2026-09-24. Порядок внутри
4
23
 
5
24
  **Для вас ничего не меняется.** Убрали старый способ монтажа, которым
@@ -727,7 +727,7 @@ function parsePositiveInt(raw, fallback) {
727
727
  }
728
728
 
729
729
  // src/version.ts
730
- var VERSION = "0.10.38";
730
+ var VERSION = "0.10.41";
731
731
  var USER_AGENT = `naparnik-mcp/${VERSION}`;
732
732
  var MIN_ENGINE = "2026.09.24-446";
733
733
  var LAUNCHER_PROTOCOL = 1;
@@ -13778,7 +13778,7 @@ async function askPointer(opts) {
13778
13778
  ...key !== void 0 ? { headers: { authorization: `Bearer ${key}` } } : {}
13779
13779
  });
13780
13780
  if (!response.ok) {
13781
- return { ok: false, why: `\u0440\u0430\u0437\u0434\u0430\u0447\u0430 \u043E\u0442\u0432\u0435\u0442\u0438\u043B\u0430 ${response.status} \u2014 ${what} \u043D\u0435 \u043E\u0442\u0434\u0430\u043D` };
13781
+ return { ok: false, why: `\u0440\u0430\u0437\u0434\u0430\u0447\u0430 \u043E\u0442\u0432\u0435\u0442\u0438\u043B\u0430 ${response.status} \u2014 ${what} \u043D\u0435 \u043E\u0442\u0434\u0430\u043D`, status: response.status };
13782
13782
  }
13783
13783
  const type = (response.headers.get("content-type") ?? "").toLowerCase();
13784
13784
  if (!type.includes("json")) {
@@ -14584,7 +14584,7 @@ async function downloadFile(opts) {
14584
14584
  );
14585
14585
  }
14586
14586
  throw new DeliveryError(
14587
- !hasKey ? `${url2} \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043A\u043B\u044E\u0447 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430, \u0430 \u043E\u043D \u043D\u0435 \u0437\u0430\u0434\u0430\u043D. \u0412\u043F\u0438\u0448\u0438\u0442\u0435 \u043A\u043B\u044E\u0447 \u0432 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044E MCP-\u0441\u0435\u0440\u0432\u0435\u0440\u0430 (\u0441\u0435\u043A\u0446\u0438\u044F env) \u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0443 \u2014 \u041A\u043B\u043E\u0434\u0430 \u0438\u043B\u0438 Codex.` : `${url2} \u043D\u0435 \u043F\u0440\u0438\u043D\u044F\u043B \u043A\u043B\u044E\u0447 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430 (${response.status}). \u041A\u043B\u044E\u0447 \u043C\u043E\u0433 \u043F\u0440\u043E\u0442\u0443\u0445\u043D\u0443\u0442\u044C \u0438\u043B\u0438 \u0431\u044B\u0442\u044C \u043E\u0442\u043E\u0437\u0432\u0430\u043D \u2014 \u0432\u044B\u043F\u0443\u0441\u0442\u0438\u0442\u0435 \u043D\u043E\u0432\u044B\u0439 \u0447\u0435\u0440\u0435\u0437 \u0431\u043E\u0442\u0430.`
14587
+ !hasKey ? `${url2} \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043A\u043B\u044E\u0447 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430, \u0430 \u043E\u043D \u043D\u0435 \u0437\u0430\u0434\u0430\u043D. \u0412\u043F\u0438\u0448\u0438\u0442\u0435 \u043A\u043B\u044E\u0447 \u0432 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044E MCP-\u0441\u0435\u0440\u0432\u0435\u0440\u0430 (\u0441\u0435\u043A\u0446\u0438\u044F env) \u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0443 \u2014 \u041A\u043B\u043E\u0434\u0430 \u0438\u043B\u0438 Codex.` : `${url2} \u043D\u0435 \u043F\u0440\u0438\u043D\u044F\u043B \u043A\u043B\u044E\u0447 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430 (${response.status}). \u0421\u043A\u043E\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043E, \u043A\u043B\u044E\u0447 \u0437\u0430\u043C\u0435\u043D\u0451\u043D \u0431\u043E\u043B\u0435\u0435 \u043D\u043E\u0432\u044B\u043C: \u043D\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B \u0438\u0437 \u0431\u043E\u0442\u0430 \u0443\u0436\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D \u0437\u0434\u0435\u0441\u044C \u0438\u043B\u0438 \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u043C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435. \u0412\u043E\u0437\u044C\u043C\u0438\u0442\u0435 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0444\u0430\u0439\u043B \u0443 \u0431\u043E\u0442\u0430 ${KEY_SOURCE_BOT} \u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u0435\u0433\u043E.`
14588
14588
  );
14589
14589
  }
14590
14590
  throw new DeliveryError(`${url2} \u043E\u0442\u0432\u0435\u0442\u0438\u043B ${response.status} ${response.statusText}`);
@@ -15823,6 +15823,10 @@ function empty() {
15823
15823
  behind: "",
15824
15824
  ahead: "",
15825
15825
  message: "",
15826
+ got: 0,
15827
+ of: 0,
15828
+ percent: 0,
15829
+ eta: "",
15826
15830
  file: "",
15827
15831
  outcome: "",
15828
15832
  trouble: "",
@@ -15849,6 +15853,7 @@ var RUNNING = /^Ещё считает, (\d+(?:[.,]\d+)?) с\.$/;
15849
15853
  var COMPLAINT = /^⚠ (.+)$/;
15850
15854
  var PLAN_HEAD = /^План:$/;
15851
15855
  var PLAN_ROW = /^· (.+)$/;
15856
+ var DOWNLOAD = /^Скачано (\d+) МБ(?: из (\d+) \((\d+)%\)(?:, (.+))?)?$/;
15852
15857
  var STYLE = /^Стиль: /;
15853
15858
  var LEFT_TAIL = /\.\s+(Осталось .+)$/;
15854
15859
  function readJob(text2) {
@@ -15955,6 +15960,13 @@ function readJob(text2) {
15955
15960
  }
15956
15961
  if (job.status === "running" || job.status === "waiting") job.message = line;
15957
15962
  }
15963
+ const download = job.status === "running" ? DOWNLOAD.exec(job.message) : null;
15964
+ if (download !== null) {
15965
+ job.got = Number(download[1]);
15966
+ job.of = Number(download[2] ?? 0);
15967
+ job.percent = Math.min(100, Number(download[3] ?? 0));
15968
+ job.eta = download[4] ?? "";
15969
+ }
15958
15970
  return job;
15959
15971
  }
15960
15972
  function seconds(text2) {
@@ -16332,7 +16344,7 @@ function pythonCrash(text2) {
16332
16344
  if (/(?:^|\.)(?:KeyboardInterrupt|SystemExit|Explained|Refusal)$/.test(errorType)) return null;
16333
16345
  const message = [m[2] ?? "", ...lines.slice(at + 1)].join("\n").trim();
16334
16346
  const innermost = frames[frames.length - 1];
16335
- const firstLine = message.split("\n")[0] ?? "";
16347
+ const firstLine2 = message.split("\n")[0] ?? "";
16336
16348
  return {
16337
16349
  errorType,
16338
16350
  message,
@@ -16340,7 +16352,7 @@ function pythonCrash(text2) {
16340
16352
  keep: "tail",
16341
16353
  frames: frames.slice(-3).map((f) => `${basename(f.file.replace(/\\/g, "/"))}:${f.func}`),
16342
16354
  python: pythonVersionIn(frames.map((f) => f.file)),
16343
- essence: `${errorType}${firstLine === "" ? "" : `: ${firstLine}`}` + (innermost ? ` \u2014 ${basename(innermost.file.replace(/\\/g, "/"))}:${innermost.line}` : "")
16355
+ essence: `${errorType}${firstLine2 === "" ? "" : `: ${firstLine2}`}` + (innermost ? ` \u2014 ${basename(innermost.file.replace(/\\/g, "/"))}:${innermost.line}` : "")
16344
16356
  };
16345
16357
  }
16346
16358
  function pythonVersionIn(files) {
@@ -16440,6 +16452,91 @@ function tail(text2, max) {
16440
16452
  function orNull(value, max) {
16441
16453
  return value === null || value === "" ? null : head(value, max);
16442
16454
  }
16455
+ var SETUP_CONTRACT = "naparnik-setup/1";
16456
+ var READINESS_CONTRACT = "naparnik-readiness/1";
16457
+ var JSON_CHECK = "--json";
16458
+ function knowsJsonCheck(engineDir) {
16459
+ try {
16460
+ const contract = JSON.parse(readFileSync(join(engineDir, "shell-contract.json"), "utf8"));
16461
+ const flags = contract.env_check_flags;
16462
+ return Array.isArray(flags) && flags.includes(JSON_CHECK);
16463
+ } catch {
16464
+ return false;
16465
+ }
16466
+ }
16467
+ function readReadiness(stdout) {
16468
+ const lines = stdout.trim().split("\n");
16469
+ let raw;
16470
+ try {
16471
+ raw = JSON.parse(lines[lines.length - 1] ?? "");
16472
+ } catch {
16473
+ return null;
16474
+ }
16475
+ const data = raw;
16476
+ if (data === null || typeof data !== "object" || data["contract"] !== READINESS_CONTRACT) return null;
16477
+ if (typeof data["words"] !== "string" || !Array.isArray(data["parts"])) return null;
16478
+ const parts = [];
16479
+ for (const item of data["parts"]) {
16480
+ const part = item;
16481
+ if (part === null || typeof part !== "object" || typeof part["name"] !== "string") continue;
16482
+ parts.push({
16483
+ name: part["name"],
16484
+ title: typeof part["title"] === "string" && part["title"] !== "" ? part["title"] : part["name"],
16485
+ ok: part["ok"] === true,
16486
+ mb: typeof part["mb"] === "number" && part["mb"] > 0 ? Math.round(part["mb"]) : 0,
16487
+ on_demand: part["on_demand"] === true,
16488
+ step: typeof part["step"] === "string" ? part["step"] : ""
16489
+ });
16490
+ }
16491
+ return { words: data["words"], ready: data["ready"] === true, parts };
16492
+ }
16493
+ var NOT_A_ROW = /* @__PURE__ */ new Set(["system"]);
16494
+ function weight(mb) {
16495
+ if (mb >= 1e3) return `${(mb / 1024).toFixed(1).replace(".", ",")} \u0413\u0411`;
16496
+ return `${mb} \u041C\u0411`;
16497
+ }
16498
+ function refusalWords(status) {
16499
+ if (status === 401) {
16500
+ return `\u041A\u043B\u044E\u0447 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438 \u043D\u0435 \u043F\u0440\u0438\u043D\u044F\u0442 \u2014 \u043D\u043E\u0432\u044B\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043D\u0430\u043F\u0438\u0448\u0438\u0442\u0435 \u0431\u043E\u0442\u0443 ${KEY_SOURCE_BOT} \xAB\u041F\u043E\u043B\u0443\u0447\u0438\u0442\u044C \u043A\u043B\u044E\u0447\xBB.`;
16501
+ }
16502
+ if (status === 403) {
16503
+ return `\u041F\u043E\u0434\u043F\u0438\u0441\u043A\u0430 \u043A\u043E\u043D\u0447\u0438\u043B\u0430\u0441\u044C \u2014 \u043D\u043E\u0432\u044B\u0435 \u0432\u0435\u0440\u0441\u0438\u0438 \u043D\u0435 \u043F\u0440\u0438\u0445\u043E\u0434\u044F\u0442. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u0440\u043E\u0434\u043B\u0438\u0442\u0435 \u0435\u0451 \u0443 \u0431\u043E\u0442\u0430 ${KEY_SOURCE_BOT}.`;
16504
+ }
16505
+ return "";
16506
+ }
16507
+ function setupCard(o) {
16508
+ const rows = (o.readiness?.parts ?? []).filter((part) => !NOT_A_ROW.has(part.name)).map((part) => ({
16509
+ title: part.title,
16510
+ state: part.ok ? "ok" : part.on_demand ? "later" : "missing",
16511
+ mb: part.ok ? 0 : part.mb
16512
+ }));
16513
+ const missingMb = rows.filter((row) => row.state === "missing").reduce((all, row) => all + row.mb, 0);
16514
+ const ready = o.readiness?.ready === true;
16515
+ const fresh = [];
16516
+ if (o.shell?.fresh) fresh.push(`\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435 ${o.shell.fresh}${o.shell.mb ? ` (${weight(o.shell.mb)})` : ""}`);
16517
+ if (o.engine?.fresh) fresh.push(`\u0434\u0432\u0438\u0436\u043E\u043A ${o.engine.fresh}${o.engine.mb ? ` (${weight(o.engine.mb)})` : ""}`);
16518
+ const update = fresh.length ? `\u0412\u044B\u0448\u043B\u043E \u043D\u043E\u0432\u043E\u0435: ${fresh.join(", ")}.` : "";
16519
+ const trouble = refusalWords(o.shell?.status ?? 0) || refusalWords(o.engine?.status ?? 0);
16520
+ let next;
16521
+ if (o.noEngine) next = "\u0414\u0432\u0438\u0436\u043A\u0430 \u043C\u043E\u043D\u0442\u0430\u0436\u0430 \u0435\u0449\u0451 \u043D\u0435\u0442. \u0421\u043A\u0430\u0436\u0438\u0442\u0435 \xAB\u043F\u043E\u0441\u0442\u0430\u0432\u044C\xBB \u2014 \u043E\u043D \u0441\u043A\u0430\u0447\u0430\u0435\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 \u0432\u0430\u0448\u0435\u0433\u043E \xAB\u0434\u0430\xBB.";
16522
+ else if (o.noPython) next = "\u041D\u0443\u0436\u0435\u043D Python. \u0421\u043A\u0430\u0436\u0438\u0442\u0435 \xAB\u043F\u043E\u0441\u0442\u0430\u0432\u044C\xBB \u2014 \u043E\u043D \u0441\u043A\u0430\u0447\u0430\u0435\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 \u0432\u0430\u0448\u0435\u0433\u043E \xAB\u0434\u0430\xBB.";
16523
+ else if (o.readiness === null) next = o.note ?? "";
16524
+ else if (!ready) {
16525
+ next = `\u0414\u043B\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0430 \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442${missingMb ? ` ${weight(missingMb)}` : " \u0447\u0430\u0441\u0442\u0438"}. \u0421\u043A\u0430\u0436\u0438\u0442\u0435 \xAB\u043F\u043E\u0441\u0442\u0430\u0432\u044C\xBB \u2014 \u0434\u043E\u043A\u0430\u0447\u0430\u0435\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 \u0432\u0430\u0448\u0435\u0433\u043E \xAB\u0434\u0430\xBB.`;
16526
+ } else if (update !== "") next = "\u041C\u043E\u0436\u043D\u043E \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C. \u0421\u043A\u0430\u0436\u0438\u0442\u0435 \xAB\u043E\u0431\u043D\u043E\u0432\u0438\xBB \u2014 \u043D\u043E\u0432\u043E\u0435 \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.";
16527
+ else next = "\u0412\u0441\u0451 \u043D\u0430 \u043C\u0435\u0441\u0442\u0435 \u2014 \u043C\u043E\u0436\u043D\u043E \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C.";
16528
+ return {
16529
+ contract: SETUP_CONTRACT,
16530
+ engine: o.engine?.installed ?? "",
16531
+ shell: o.shellVersion,
16532
+ ready,
16533
+ rows,
16534
+ missingMb,
16535
+ update,
16536
+ trouble,
16537
+ next
16538
+ };
16539
+ }
16443
16540
  var WIDGET_MIME = "text/html;profile=mcp-app";
16444
16541
  function widgetStamp(html) {
16445
16542
  return createHash("sha256").update(html).digest("hex").slice(0, 8);
@@ -16650,6 +16747,108 @@ function runbarWidgetHtml() {
16650
16747
  .side[hidden] { display: none; }
16651
16748
  .side b { font-weight: 650; color: var(--ink); }
16652
16749
 
16750
+ /* \u2500\u2500\u2500 \u0428\u043A\u0430\u043B\u0430: \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u043A\u0430\u0447\u0430\u043D\u043E, \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0433\u043E\u0442\u043E\u0432\u043E \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
16751
+
16752
+ /* \u26A0 \u0428\u041A\u0410\u041B\u0410 \u2014 \u041E\u0414\u041D\u0410 \u041D\u0410 \u0421\u041A\u0410\u0427\u0418\u0412\u0410\u041D\u0418\u0415 \u0418 \u041D\u0410 \u041A\u0410\u0420\u0422\u041E\u0427\u041A\u0423. \u0412\u043B\u0430\u0434\u0435\u043B\u0435\u0446 2026-09-24: \xAB\u0447\u0442\u043E
16753
+ \u0441\u0442\u0430\u0432\u043B\u044E, \u0447\u0442\u043E \u043E\u0441\u0442\u0430\u043B\u043E\u0441\u044C, \u043F\u0440\u043E\u0433\u0440\u0435\u0441\u0441-\u0431\u0430\u0440\xBB. \u0416\u0451\u043B\u0442\u0430\u044F \u0437\u0430\u043B\u0438\u0432\u043A\u0430 \u2014 \u044D\u0442\u043E \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435, \u0442\u0430 \u0436\u0435
16754
+ \u0440\u043E\u043B\u044C, \u0447\u0442\u043E \u0443 \u0438\u0434\u0443\u0449\u0435\u0433\u043E \u044D\u0442\u0430\u043F\u0430; \u0448\u0438\u0440\u0438\u043D\u0443 \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u043A\u0440\u0438\u043F\u0442, \u0430 \u043F\u0435\u0440\u0435\u0445\u043E\u0434 \u043F\u043E \u0448\u0438\u0440\u0438\u043D\u0435
16755
+ \u043F\u043B\u0430\u0432\u043D\u044B\u0439, \u0447\u0442\u043E\u0431\u044B \u043E\u043F\u0440\u043E\u0441 \u0440\u0430\u0437 \u0432 2,5 \u0441 \u043D\u0435 \u0434\u0451\u0440\u0433\u0430\u043B \u0435\u0451 \u0441\u043A\u0430\u0447\u043A\u0430\u043C\u0438. */
16756
+ .meter {
16757
+ position: relative;
16758
+ height: 8px;
16759
+ margin-top: 12px;
16760
+ border-radius: 4px;
16761
+ background: var(--off);
16762
+ overflow: hidden;
16763
+ }
16764
+ .meter__fill {
16765
+ display: block;
16766
+ width: 0;
16767
+ height: 100%;
16768
+ border-radius: 4px;
16769
+ background: var(--yellow);
16770
+ transition: width .6s var(--ease);
16771
+ }
16772
+ .meter__cap {
16773
+ margin: 7px 0 0;
16774
+ color: var(--ink-soft);
16775
+ font-size: 13px;
16776
+ line-height: 1.4;
16777
+ font-variant-numeric: tabular-nums;
16778
+ }
16779
+ .meter__cap b { color: var(--ink); font-weight: 650; }
16780
+ .dl[hidden], .meter[hidden], .meter__cap[hidden] { display: none; }
16781
+
16782
+ /* \u2500\u2500\u2500 \u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F: \u043E\u0442\u0432\u0435\u0442 setup \u0431\u0435\u0437 \u0437\u0430\u0434\u0430\u0447\u0438 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
16783
+
16784
+ /* \u26A0 \u041A\u0410\u0420\u0422\u041E\u0427\u041A\u0410 \u0412\u041C\u0415\u0421\u0422\u041E \u0421\u0412\u0401\u0420\u0422\u041A\u0418, \u0418 \u042D\u0422\u041E \u0416\u0418\u0412\u0410\u042F \u041F\u041E\u041B\u041E\u041C\u041A\u0410 2026-09-24. \u0420\u0430\u043C\u043A\u0443 \u0432\u0438\u0434\u0436\u0435\u0442\u0430
16785
+ \u0445\u043E\u0441\u0442 \u0440\u0438\u0441\u0443\u0435\u0442 \u043D\u0430 \u043A\u0430\u0436\u0434\u043E\u043C \u0432\u044B\u0437\u043E\u0432\u0435 setup; \u0441\u0432\u0451\u0440\u043D\u0443\u0442\u0430\u044F \u043F\u043E\u043B\u043E\u0441\u0430 \u0432 Claude Desktop
16786
+ \u043E\u0441\u0442\u0430\u0432\u043B\u044F\u043B\u0430 \u043F\u043E\u0434 \u043E\u0442\u0432\u0435\u0442\u043E\u043C \u043F\u0443\u0441\u0442\u0443\u044E \u0441\u0435\u0440\u0443\u044E \u043F\u043B\u0430\u0448\u043A\u0443. */
16787
+ .card[hidden] { display: none; }
16788
+ .card__meta { margin: 4px 0 0; color: var(--ink-soft); font-size: 12.5px; font-variant-numeric: tabular-nums; }
16789
+ .card__meta[hidden] { display: none; }
16790
+ .parts { display: grid; gap: 2px 18px; margin: 14px 0 0; padding: 0; list-style: none; }
16791
+ .parts[hidden] { display: none; }
16792
+ .part {
16793
+ display: flex;
16794
+ align-items: center;
16795
+ gap: 10px;
16796
+ min-width: 0;
16797
+ padding: 6px 0;
16798
+ border-bottom: 1px solid var(--edge);
16799
+ font-size: 14px;
16800
+ line-height: 1.3;
16801
+ }
16802
+ /* \u0417\u043D\u0430\u043A \u0447\u0430\u0441\u0442\u0438 \u2014 \u0444\u043E\u0440\u043C\u0430, \u0430 \u043D\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u0446\u0432\u0435\u0442: \u0441\u0442\u043E\u0438\u0442 \u2014 \u0437\u0430\u043B\u0438\u0442\u044B\u0439 \u043A\u0440\u0443\u0436\u043E\u043A \u0441 \u0433\u0430\u043B\u043E\u0447\u043A\u043E\u0439,
16803
+ \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442 \u2014 \u0436\u0451\u043B\u0442\u043E\u0435 \u043A\u043E\u043B\u044C\u0446\u043E, \u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435 \u2014 \u0433\u043E\u043B\u0443\u0431\u043E\u0435 \u043F\u0443\u043D\u043A\u0442\u0438\u0440\u043D\u043E\u0435. */
16804
+ .part__m {
16805
+ position: relative;
16806
+ flex: none;
16807
+ width: 16px;
16808
+ height: 16px;
16809
+ border-radius: 50%;
16810
+ box-sizing: border-box;
16811
+ }
16812
+ .part.ok .part__m { background: var(--good); }
16813
+ .part.ok .part__m::before {
16814
+ content: "";
16815
+ position: absolute;
16816
+ top: 4px;
16817
+ left: 4px;
16818
+ width: 7px;
16819
+ height: 3.5px;
16820
+ border-left: 1.8px solid var(--good-ink);
16821
+ border-bottom: 1.8px solid var(--good-ink);
16822
+ transform: rotate(-45deg);
16823
+ }
16824
+ .part.missing .part__m { border: 2.5px solid var(--yellow); }
16825
+ .part.later .part__m { border: 2px dashed var(--note-dot); }
16826
+ .part__t { display: block; flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
16827
+ .part.ok .part__t { color: var(--ink-soft); }
16828
+ .part__w { flex: none; color: var(--ink-soft); font-size: 12.5px; font-variant-numeric: tabular-nums; }
16829
+ .part.missing .part__w { color: var(--ink); font-weight: 650; }
16830
+ /* \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C \u0434\u0430\u043B\u044C\u0448\u0435 \u2014 \u043A\u0440\u0443\u043F\u043D\u0435\u0435 \u0441\u043F\u0438\u0441\u043A\u0430: \u044D\u0442\u043E \u043E\u0442\u0432\u0435\u0442 \u043D\u0430 \xAB\u0438 \u0447\u0442\u043E \u0442\u0435\u043F\u0435\u0440\u044C\xBB. */
16831
+ .next {
16832
+ display: flex;
16833
+ align-items: flex-start;
16834
+ gap: 9px;
16835
+ margin: 14px 0 0;
16836
+ font-size: 15px;
16837
+ font-weight: 650;
16838
+ line-height: 1.35;
16839
+ letter-spacing: -0.01em;
16840
+ }
16841
+ .next[hidden] { display: none; }
16842
+ .next::before {
16843
+ content: "";
16844
+ flex: none;
16845
+ width: 8px;
16846
+ height: 8px;
16847
+ margin-top: 6px;
16848
+ border-radius: 2px;
16849
+ background: var(--yellow);
16850
+ }
16851
+
16653
16852
  /* \u2500\u2500\u2500 \u041A\u043E\u043D\u0435\u0446 \u0440\u0430\u0431\u043E\u0442\u044B \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
16654
16853
 
16655
16854
  /* \u26A0 \u041E\u0422\u0412\u0415\u0422 \u041A\u0420\u0423\u041F\u041D\u0415\u0415 \u0425\u041E\u0414\u0410. \u041E\u0442\u0431\u0440\u0430\u043A\u043E\u0432\u043A\u0430 \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0430 \u043D\u0430 \u0444\u043E\u0440\u043C\u0435 \u0432\u044B\u0431\u043E\u0440\u0430 2026-09-10:
@@ -16762,6 +16961,7 @@ function runbarWidgetHtml() {
16762
16961
  body { padding: 22px 20px 24px; }
16763
16962
  h2 { font-size: 21px; }
16764
16963
  .rail { gap: 5px; }
16964
+ .parts { grid-template-columns: 1fr 1fr; }
16765
16965
  }
16766
16966
  @media (min-width: 760px) {
16767
16967
  body { padding: 26px 24px 28px; }
@@ -16781,7 +16981,20 @@ function runbarWidgetHtml() {
16781
16981
  <span class="now__c" id="nowLeft" hidden></span>
16782
16982
  </span>
16783
16983
  </div>
16984
+ <div class="dl" id="dl" hidden>
16985
+ <div class="meter" id="dlMeter"><span class="meter__fill" id="dlFill"></span></div>
16986
+ <p class="meter__cap" id="dlCap"></p>
16987
+ </div>
16784
16988
  <div class="msg" id="msg" hidden></div>
16989
+ <section class="card" id="card" hidden>
16990
+ <p class="card__meta" id="cardMeta" hidden></p>
16991
+ <div class="meter" id="cardMeter" hidden><span class="meter__fill" id="cardFill"></span></div>
16992
+ <p class="meter__cap" id="cardCap" hidden></p>
16993
+ <ul class="parts" id="cardRows" hidden></ul>
16994
+ <div class="msg" id="cardUpdate" hidden></div>
16995
+ <div class="note" id="cardTrouble" hidden></div>
16996
+ <p class="next" id="cardNext" hidden></p>
16997
+ </section>
16785
16998
  <p class="side" id="behind" hidden></p>
16786
16999
  <p class="side" id="ahead" hidden></p>
16787
17000
  <div class="end" id="end" hidden>
@@ -16805,6 +17018,7 @@ function runbarWidgetHtml() {
16805
17018
  // \u043F\u0440\u0438\u0447\u0438\u043D\u0435 (\u0430\u043D\u0442\u0438\u0432\u0438\u0440\u0443\u0441, \u043D\u0435\u0445\u0432\u0430\u0442\u043A\u0430 \u043F\u0430\u043C\u044F\u0442\u0438, \u0433\u043E\u043D\u043A\u0430 \u0437\u0430 \u0444\u0430\u0439\u043B\u043E\u043C \u0437\u0430\u0434\u0430\u043D\u0438\u044F).
16806
17019
  var GIVE_UP_AFTER = 5;
16807
17020
  var CONTRACT = '${JOB_CONTRACT}';
17021
+ var SETUP = '${SETUP_CONTRACT}';
16808
17022
 
16809
17023
  var nextId = 1;
16810
17024
  // Object.create(null), \u0430 \u043D\u0435 {}: \u0438\u043D\u0430\u0447\u0435 id \u0432\u0438\u0434\u0430 \xABtoString\xBB \u0434\u043E\u0441\u0442\u0430\u0451\u0442 \u0444\u0443\u043D\u043A\u0446\u0438\u044E \u0441
@@ -16936,6 +17150,7 @@ function runbarWidgetHtml() {
16936
17150
  job = data;
16937
17151
  // \u0414\u0430\u043D\u043D\u044B\u0435 \u043F\u0440\u0438\u0448\u043B\u0438 \u043F\u043E\u0441\u043B\u0435 \u0441\u0432\u0451\u0440\u0442\u043A\u0438 \u2014 \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043C\u0441\u044F \u0432 \u043B\u0435\u043D\u0442\u0443.
16938
17152
  document.body.classList.remove('gone');
17153
+ byId('card').hidden = true;
16939
17154
  var ended = !working(data.status);
16940
17155
 
16941
17156
  show('what', data.what || '\u0420\u0430\u0431\u043E\u0442\u0430\u044E');
@@ -16955,9 +17170,13 @@ function runbarWidgetHtml() {
16955
17170
  if (data.at && data.total) title = title + ' \xB7 \u044D\u0442\u0430\u043F ' + data.at + ' \u0438\u0437 ' + data.total;
16956
17171
  show('nowTitle', title);
16957
17172
  show('nowLeft', data.left || '');
16958
- show('msg', data.status === 'waiting' ? '' : (data.message || ''));
17173
+ var loading = download(data);
17174
+ // \u0421\u043A\u0430\u0447\u0438\u0432\u0430\u043D\u0438\u0435 \u0443\u0436\u0435 \u0441\u043A\u0430\u0437\u0430\u043D\u043E \u0448\u043A\u0430\u043B\u043E\u0439 \u0438 \u043F\u043E\u0434\u043F\u0438\u0441\u044C\u044E \u043F\u043E\u0434 \u043D\u0435\u0439 \u2014 \u0442\u0430 \u0436\u0435 \u0441\u0442\u0440\u043E\u043A\u0430
17175
+ // \u0433\u043E\u043B\u0443\u0431\u043E\u0439 \u043F\u043B\u0430\u0448\u043A\u043E\u0439 \u0440\u044F\u0434\u043E\u043C \u0431\u044B\u043B\u0430 \u0431\u044B \u0432\u0442\u043E\u0440\u044B\u043C \u044D\u043A\u0437\u0435\u043C\u043F\u043B\u044F\u0440\u043E\u043C \u043E\u0434\u043D\u0438\u0445 \u0438 \u0442\u0435\u0445 \u0436\u0435 \u0447\u0438\u0441\u0435\u043B.
17176
+ show('msg', data.status === 'waiting' || loading ? '' : (data.message || ''));
16959
17177
  } else {
16960
17178
  byId('msg').hidden = true;
17179
+ byId('dl').hidden = true;
16961
17180
  }
16962
17181
 
16963
17182
  // \xAB\u041F\u043E\u0437\u0430\u0434\u0438\xBB \u0438 \xAB\u0434\u0430\u043B\u044C\u0448\u0435\xBB \u043F\u0440\u0438\u0445\u043E\u0434\u044F\u0442 \u0441\u0442\u0440\u043E\u043A\u0430\u043C\u0438 \u0434\u0432\u0438\u0436\u043A\u0430 \u0446\u0435\u043B\u0438\u043A\u043E\u043C: \u0434\u0440\u043E\u0431\u0438\u0442\u044C \u0438\u0445 \u043F\u043E
@@ -16971,6 +17190,131 @@ function runbarWidgetHtml() {
16971
17190
  reportSize();
16972
17191
  }
16973
17192
 
17193
+ /**
17194
+ * \u0417\u0430\u0433\u043B\u0430\u0432\u043D\u0430\u044F \u0443 \u0440\u0443\u0441\u0441\u043A\u043E\u0433\u043E \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u044F \u0447\u0430\u0441\u0442\u0438, \u0430 \u0438\u043C\u044F \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B \u2014 \u043A\u0430\u043A \u0435\u0441\u0442\u044C.
17195
+ *
17196
+ * \u26A0 \u041D\u0415 CSS \xABfirst-letter\xBB. \u041D\u0430\u0437\u0432\u0430\u043D\u0438\u044F \u0447\u0430\u0441\u0442\u0435\u0439 \u043F\u0438\u0448\u0435\u0442 \u0434\u0432\u0438\u0436\u043E\u043A \u0441\u0442\u0440\u043E\u0447\u043D\u044B\u043C\u0438: \u043E\u043D\u0438 \u0436\u0435
17197
+ * \u0438\u0434\u0443\u0442 \u0432 \u0435\u0433\u043E \u043E\u0442\u0432\u0435\u0442 \u043C\u043E\u0434\u0435\u043B\u0438 \u0441\u0435\u0440\u0435\u0434\u0438\u043D\u043E\u0439 \u0444\u0440\u0430\u0437\u044B. \u041D\u043E \xABffmpeg\xBB \u2014 \u0438\u043C\u044F \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B, \u0438
17198
+ * \u043F\u0440\u0430\u0432\u0438\u043B\u043E \u0434\u043B\u044F \u0432\u0441\u0435\u0445 \u043F\u0435\u0440\u0432\u044B\u0445 \u0431\u0443\u043A\u0432 \u0434\u0435\u043B\u0430\u043B\u043E \u0438\u0437 \u043D\u0435\u0433\u043E \xABFfmpeg\xBB (\u0441\u043D\u0438\u043C\u043E\u043A 2026-09-24).
17199
+ */
17200
+ function capital(text) {
17201
+ var s = String(text || '');
17202
+ return /^[\u0430-\u044F\u0451]/.test(s) ? s.charAt(0).toUpperCase() + s.slice(1) : s;
17203
+ }
17204
+
17205
+ /** \xAB245 \u041C\u0411\xBB, \xAB1,6 \u0413\u0411\xBB \u2014 \u043A\u0430\u043A \u0432 \u0441\u043B\u043E\u0432\u0430\u0445 setup, \u0430 \u043D\u0435 \u0433\u043E\u043B\u044B\u043C \u0447\u0438\u0441\u043B\u043E\u043C \u043C\u0435\u0433\u0430\u0431\u0430\u0439\u0442. */
17206
+ function weight(mb) {
17207
+ var n = Math.max(0, Math.round(Number(mb) || 0));
17208
+ if (n >= 1000) return (n / 1024).toFixed(1).replace('.', ',') + ' \u0413\u0411';
17209
+ return n + ' \u041C\u0411';
17210
+ }
17211
+
17212
+ function fill(id, percent) {
17213
+ var el = byId(id);
17214
+ if (el) el.style.width = Math.max(0, Math.min(100, Number(percent) || 0)) + '%';
17215
+ }
17216
+
17217
+ /**
17218
+ * \u0428\u043A\u0430\u043B\u0430 \u0441\u043A\u0430\u0447\u0438\u0432\u0430\u043D\u0438\u044F \u043F\u043E \u0447\u0438\u0441\u043B\u0430\u043C \u0437\u0430\u0434\u0430\u0447\u0438. \u0412\u0435\u0440\u043D\u0451\u0442, \u043D\u0430\u0440\u0438\u0441\u043E\u0432\u0430\u043D\u0430 \u043B\u0438 \u043E\u043D\u0430.
17219
+ *
17220
+ * \u26A0 \u0411\u0415\u0417 \xAB\u0418\u0417 \u0421\u041A\u041E\u041B\u042C\u041A\u0418\u0425\xBB \u0417\u0410\u041B\u0418\u0412\u041A\u0418 \u041D\u0415\u0422, \u0410 \u041F\u041E\u0414\u041F\u0418\u0421\u042C \u0415\u0421\u0422\u042C. \u0412\u0441\u0435\u0433\u043E \u0434\u0432\u0438\u0436\u043E\u043A \u0437\u043D\u0430\u0435\u0442 \u043D\u0435
17221
+ * \u0432\u0441\u0435\u0433\u0434\u0430; \u0437\u0430\u043B\u0438\u0442\u044C \u043D\u0430\u0443\u0433\u0430\u0434 \u2014 \u0441\u043E\u0432\u0440\u0430\u0442\u044C \u043E \u0434\u043E\u043B\u0435, \u0430 \u043C\u0435\u0433\u0430\u0431\u0430\u0439\u0442\u044B \u043F\u0440\u0430\u0432\u0434\u0438\u0432\u044B.
17222
+ */
17223
+ function download(data) {
17224
+ var box = byId('dl');
17225
+ if (!box) return false;
17226
+ var got = Number(data.got) || 0;
17227
+ if (!got) { box.hidden = true; return false; }
17228
+ var of = Number(data.of) || 0;
17229
+ byId('dlMeter').hidden = !of;
17230
+ fill('dlFill', of ? data.percent : 0);
17231
+ var cap = byId('dlCap');
17232
+ cap.innerHTML = '';
17233
+ var b = document.createElement('b');
17234
+ b.textContent = of ? (got + ' \u0438\u0437 ' + weight(of)) : ('\u0441\u043A\u0430\u0447\u0430\u043D\u043E ' + weight(got));
17235
+ cap.appendChild(b);
17236
+ var tail = of ? ' \xB7 ' + (Number(data.percent) || 0) + '%' : '';
17237
+ if (data.eta) tail += ' \xB7 ' + data.eta;
17238
+ cap.appendChild(document.createTextNode(tail));
17239
+ cap.hidden = false;
17240
+ box.hidden = false;
17241
+ return true;
17242
+ }
17243
+
17244
+ // \u2500\u2500\u2500 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
17245
+
17246
+ /**
17247
+ * \u041E\u0442\u0432\u0435\u0442 setup \u0431\u0435\u0437 \u0437\u0430\u0434\u0430\u0447\u0438: \u0447\u0442\u043E \u0441\u0442\u043E\u0438\u0442, \u0447\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442, \u0447\u0442\u043E \u0432\u044B\u0448\u043B\u043E, \u0447\u0442\u043E \u0434\u0430\u043B\u044C\u0448\u0435.
17248
+ *
17249
+ * \u26A0 \u041E\u041F\u0420\u041E\u0421 \u041D\u0415 \u0417\u0410\u0412\u041E\u0414\u0418\u0422\u0421\u042F: \u0440\u0430\u0431\u043E\u0442\u044B \u043D\u0435\u0442, \u0441\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044C \u043D\u0435 \u043E \u0447\u0435\u043C. \u041A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u2014 \u0441\u043D\u0438\u043C\u043E\u043A
17250
+ * \u043D\u0430 \u043C\u043E\u043C\u0435\u043D\u0442 \u043E\u0442\u0432\u0435\u0442\u0430; \u043D\u043E\u0432\u044B\u0439 \u0432\u044B\u0437\u043E\u0432 setup \u043F\u0440\u0438\u043D\u0435\u0441\u0451\u0442 \u043D\u043E\u0432\u0443\u044E.
17251
+ */
17252
+ function renderCard(data) {
17253
+ if (closed) return;
17254
+ job = null;
17255
+ document.body.classList.remove('gone');
17256
+ document.body.classList.remove('bad');
17257
+ var hide = ['now', 'rail', 'msg', 'dl', 'behind', 'ahead', 'clock', 'end', 'note'];
17258
+ for (var i = 0; i < hide.length; i++) { var el = byId(hide[i]); if (el) el.hidden = true; }
17259
+
17260
+ var rows = data.rows || [];
17261
+ var ok = 0, need = 0;
17262
+ for (var j = 0; j < rows.length; j++) {
17263
+ if (rows[j].state === 'ok') { ok++; need++; }
17264
+ else if (rows[j].state === 'missing') need++;
17265
+ }
17266
+ show('what', data.ready ? '\u0412\u0441\u0451 \u0433\u043E\u0442\u043E\u0432\u043E \u043A \u043C\u043E\u043D\u0442\u0430\u0436\u0443' : (need > ok ? '\u0414\u043B\u044F \u043C\u043E\u043D\u0442\u0430\u0436\u0430 \u043D\u0435 \u0432\u0441\u0451 \u043D\u0430 \u043C\u0435\u0441\u0442\u0435' : '\u041C\u043E\u043D\u0442\u0430\u0436 \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435'));
17267
+
17268
+ var meta = [];
17269
+ if (data.engine) meta.push('\u0434\u0432\u0438\u0436\u043E\u043A ' + data.engine);
17270
+ if (data.shell) meta.push('\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435 ' + data.shell);
17271
+ show('cardMeta', meta.join(' \xB7 '));
17272
+
17273
+ // \u0428\u043A\u0430\u043B\u0430 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u2014 \u043F\u043E \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E\u043C\u0443: \u0442\u043E, \u0447\u0442\u043E \u0432\u0441\u0442\u0430\u043D\u0435\u0442 \u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435, \u0434\u043E\u043B\u0435\u0439
17274
+ // \xAB\u043D\u0435 \u0433\u043E\u0442\u043E\u0432\u043E\xBB \u043D\u0435 \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044F, \u0438\u043D\u0430\u0447\u0435 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0431\u0435\u0437 \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u043E\u0432 \u043D\u0430\u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u0438\u0434\u0435\u043B \u0431\u044B
17275
+ // \u043D\u0435\u0434\u043E\u043B\u0438\u0442\u0443\u044E \u043F\u043E\u043B\u043E\u0441\u0443 \u043D\u0430 \u0438\u0441\u043F\u0440\u0430\u0432\u043D\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435.
17276
+ byId('cardMeter').hidden = !need;
17277
+ fill('cardFill', need ? ok * 100 / need : 0);
17278
+ var cap = byId('cardCap');
17279
+ cap.innerHTML = '';
17280
+ if (need) {
17281
+ var b = document.createElement('b');
17282
+ b.textContent = '\u0413\u043E\u0442\u043E\u0432\u043E ' + ok + ' \u0438\u0437 ' + need;
17283
+ cap.appendChild(b);
17284
+ if (data.missingMb) cap.appendChild(document.createTextNode(' \xB7 \u0434\u043E\u043A\u0430\u0447\u0430\u0442\u044C ' + weight(data.missingMb)));
17285
+ }
17286
+ cap.hidden = !need;
17287
+
17288
+ var list = byId('cardRows');
17289
+ list.innerHTML = '';
17290
+ for (var k = 0; k < rows.length; k++) {
17291
+ var row = rows[k];
17292
+ var li = document.createElement('li');
17293
+ li.className = 'part ' + row.state;
17294
+ var mark = document.createElement('span');
17295
+ mark.className = 'part__m';
17296
+ li.appendChild(mark);
17297
+ var title = document.createElement('span');
17298
+ title.className = 'part__t';
17299
+ title.textContent = capital(row.title);
17300
+ li.appendChild(title);
17301
+ var w = document.createElement('span');
17302
+ w.className = 'part__w';
17303
+ w.textContent = row.state === 'ok' ? '\u0441\u0442\u043E\u0438\u0442'
17304
+ : row.state === 'later' ? ('\u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435' + (row.mb ? ' \xB7 ' + weight(row.mb) : ''))
17305
+ : (row.mb ? weight(row.mb) : '\u043D\u0435\u0442');
17306
+ li.appendChild(w);
17307
+ list.appendChild(li);
17308
+ }
17309
+ list.hidden = !rows.length;
17310
+
17311
+ show('cardUpdate', data.update || '');
17312
+ show('cardTrouble', data.trouble || '');
17313
+ show('cardNext', data.next || '');
17314
+ byId('card').hidden = false;
17315
+ reportSize();
17316
+ }
17317
+
16974
17318
  function side(id, label, text) {
16975
17319
  var el = byId(id);
16976
17320
  if (!el) return;
@@ -17015,6 +17359,9 @@ function runbarWidgetHtml() {
17015
17359
  /** \u041E\u0442\u043A\u0430\u0437 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0430: \u0441\u043B\u043E\u0432\u0430 \u0432\u043C\u0435\u0441\u0442\u043E \u043F\u043E\u043B\u043E\u0441\u044B. \u041F\u0443\u0441\u0442\u0430\u044F \u0440\u0430\u043C\u043A\u0430 \u0442\u0443\u0442 \u0441\u0442\u0440\u0430\u0448\u043D\u0435\u0435 \u0431\u0435\u0434\u044B. */
17016
17360
  function finishWithWords(text) {
17017
17361
  job = null;
17362
+ document.body.classList.remove('gone');
17363
+ byId('card').hidden = true;
17364
+ byId('dl').hidden = true;
17018
17365
  byId('now').hidden = true;
17019
17366
  byId('rail').hidden = true;
17020
17367
  byId('msg').hidden = true;
@@ -17044,7 +17391,7 @@ function runbarWidgetHtml() {
17044
17391
  // \xAB\u0437\u0430\u043A\u0440\u044B\u0442\u043E\xBB, \u043F\u0440\u0438\u0448\u0435\u0434\u0448\u0430\u044F \u0441\u043B\u0435\u0434\u043E\u043C \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430 (\u0445\u043E\u0441\u0442 \u0432\u043F\u0440\u0430\u0432\u0435 \u043F\u0440\u0438\u0441\u043B\u0430\u0442\u044C \u0434\u0432\u0430
17045
17392
  // \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u043F\u043E\u0434\u0440\u044F\u0434) \u043E\u0441\u0442\u0430\u043B\u0430\u0441\u044C \u0431\u044B \u0431\u0435\u0437 \u043E\u043F\u0440\u043E\u0441\u0430 \u2014 \u043F\u043E\u043B\u043E\u0441\u0430 \u043F\u043E\u043A\u0430\u0437\u0430\u043B\u0430 \u0431\u044B \u043F\u0435\u0440\u0432\u044B\u0439
17046
17393
  // \u043A\u0430\u0434\u0440 \u0438 \u0437\u0430\u043C\u0435\u0440\u043B\u0430.
17047
- var all = ['now', 'rail', 'msg', 'behind', 'ahead', 'clock', 'end', 'note', 'what'];
17394
+ var all = ['now', 'rail', 'msg', 'dl', 'card', 'behind', 'ahead', 'clock', 'end', 'note', 'what'];
17048
17395
  for (var i = 0; i < all.length; i++) { var el = byId(all[i]); if (el) el.hidden = true; }
17049
17396
  document.body.classList.add('gone');
17050
17397
  reportSize();
@@ -17085,8 +17432,16 @@ function runbarWidgetHtml() {
17085
17432
  }
17086
17433
  if (m.method === 'ui/notifications/tool-result') {
17087
17434
  if (m.params && m.params.isError) { finishWithWords(words(m.params)); return; }
17435
+ // \u041E\u0442\u0432\u0435\u0442 setup \u0431\u0435\u0437 \u0437\u0430\u0434\u0430\u0447\u0438 \u2014 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F, \u0430 \u043D\u0435 \u0441\u0432\u0451\u0440\u0442\u043A\u0430: \u0440\u0430\u043C\u043A\u0443 \u0445\u043E\u0441\u0442
17436
+ // \u0440\u0438\u0441\u0443\u0435\u0442 \u0432\u0441\u0451 \u0440\u0430\u0432\u043D\u043E, \u0438 \u0441\u0432\u0451\u0440\u043D\u0443\u0442\u0430\u044F \u043F\u043E\u043B\u043E\u0441\u0430 \u043E\u0441\u0442\u0430\u0432\u043B\u044F\u043B\u0430 \u0431\u044B \u043F\u0443\u0441\u0442\u0443\u044E \u043F\u043B\u0430\u0448\u043A\u0443.
17437
+ var card = m.params && m.params.structuredContent;
17438
+ if (card && card.contract === SETUP) { renderCard(card); return; }
17088
17439
  var data = parse(m.params);
17089
- // \u26A0 \u041E\u0422\u0412\u0415\u0422 \u0411\u0415\u0417 \u0417\u0410\u0414\u0410\u0427\u0418 \u2014 \u0421\u0412\u041E\u0420\u0410\u0427\u0418\u0412\u0410\u0415\u041C\u0421\u042F, \u0410 \u041D\u0415 \u0412\u0418\u0421\u0418\u041C \u041D\u0410 \u0417\u0410\u0413\u041B\u0423\u0428\u041A\u0415. \u041F\u043E\u043B\u043E\u0441\u0430
17440
+ // \u26A0 \u041E\u0422\u0412\u0415\u0422 \u0411\u0415\u0417 \u0417\u0410\u0414\u0410\u0427\u0418 \u0418 \u0411\u0415\u0417 \u041A\u0410\u0420\u0422\u041E\u0427\u041A\u0418 \u2014 \u0421\u0412\u041E\u0420\u0410\u0427\u0418\u0412\u0410\u0415\u041C\u0421\u042F, \u0410 \u041D\u0415 \u0412\u0418\u0421\u0418\u041C \u041D\u0410
17441
+ // \u0417\u0410\u0413\u041B\u0423\u0428\u041A\u0415. \u0423 setup \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u0435\u0441\u0442\u044C \u0432\u0441\u0435\u0433\u0434\u0430 (\u0432\u044B\u0448\u0435, 2026-09-24): \u0441\u0432\u0451\u0440\u0442\u043A\u0443
17442
+ // Claude Desktop \u043D\u0435 \u0443\u0431\u0438\u0440\u0430\u0435\u0442, \u0438 \u0442\u0430\u043C \u043E\u0441\u0442\u0430\u0432\u0430\u043B\u0430\u0441\u044C \u043F\u0443\u0441\u0442\u0430\u044F \u0440\u0430\u043C\u043A\u0430. \u0417\u0434\u0435\u0441\u044C
17443
+ // \u043E\u0441\u0442\u0430\u043B\u0438\u0441\u044C \u0447\u0443\u0436\u0438\u0435 \u043E\u0442\u0432\u0435\u0442\u044B \u0431\u0435\u0437 \u0440\u0430\u0431\u043E\u0442\u044B \u2014 plan \u0431\u0435\u0437 \u0432\u0438\u0434\u0435\u043E (docs/plan/README.md,
17444
+ // \u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 \u0431\u0440\u0430\u043A). \u041F\u043E\u043B\u043E\u0441\u0430
17090
17445
  // \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D\u0430 \u0443 \u0412\u0421\u0415\u0413\u041E \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u0430, \u0430 \u043D\u0435 \u0443 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0433\u043E \u043E\u0442\u0432\u0435\u0442\u0430, \u0438 \u0445\u043E\u0441\u0442 \u0440\u0438\u0441\u0443\u0435\u0442
17091
17446
  // \u0435\u0451 \u043D\u0430 \u043A\u0430\u0436\u0434\u043E\u043C \u0432\u044B\u0437\u043E\u0432\u0435 \u2014 \u0432 \u0442\u043E\u043C \u0447\u0438\u0441\u043B\u0435 \u043D\u0430 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0435 setup \u0431\u0435\u0437 \u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u043E\u0432,
17092
17447
  // \u0433\u0434\u0435 \u043D\u0438\u043A\u0430\u043A\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u044B \u043D\u0435\u0442 \u0438 \u043D\u043E\u043C\u0435\u0440\u0430 \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043E\u0442\u0432\u0435\u0442\u0435 \u0442\u043E\u0436\u0435. \u0420\u0430\u043D\u044C\u0448\u0435 \u0437\u0434\u0435\u0441\u044C
@@ -17337,23 +17692,47 @@ function nameOrRefusal(candidate) {
17337
17692
  var ASKED_POINTER_MS = 15e3;
17338
17693
  var NEWS_POINTER_MS = 5e3;
17339
17694
  var EXTRAS_LATER = "\u041C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0443\u0436\u0435 \u043C\u043E\u0436\u043D\u043E. \u0424\u043E\u043D\u043E\u043C \u0434\u043E\u043A\u0430\u0447\u0438\u0432\u0430\u044E \u0442\u043E, \u0431\u0435\u0437 \u0447\u0435\u0433\u043E \u043C\u043E\u043D\u0442\u0430\u0436 \u0438 \u0442\u0430\u043A \u0438\u0434\u0451\u0442: \u0448\u0443\u043C\u043E\u0434\u0430\u0432 \u0433\u043E\u043B\u043E\u0441\u0430 \u2014 \u043E\u043A\u043E\u043B\u043E 30 \u041C\u0411, \u043A\u0430\u0440\u0442\u0443 \u0433\u0435\u0440\u043E\u044F \u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0434\u043B\u044F \u0433\u0440\u0430\u0444\u0438\u043A\u0438 \u2014 \u0434\u043E 150 \u041C\u0411. \u0411\u0440\u0430\u0443\u0437\u0435\u0440 \u043A\u0430\u0447\u0430\u0435\u0442\u0441\u044F, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u0435\u0433\u043E \u0441\u0431\u043E\u0440\u043A\u0430 \u0434\u043B\u044F \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u044B \u0443\u0436\u0435 \u0432\u044B\u043B\u043E\u0436\u0435\u043D\u0430: \u0433\u0440\u0430\u0444\u0438\u043A\u0430 \u0432 \u043C\u043E\u043D\u0442\u0430\u0436 \u0435\u0449\u0451 \u043D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0430. \u041D\u0435 \u0434\u043E\u043A\u0430\u0447\u0430\u0435\u0442\u0441\u044F \u2014 \u043C\u043E\u043D\u0442\u0430\u0436 \u043D\u0435 \u0443\u043F\u0430\u0434\u0451\u0442, \u0430 setup \u0441\u043A\u0430\u0436\u0435\u0442 \u0441\u043B\u043E\u0432\u0430\u043C\u0438, \u0447\u0435\u0433\u043E \u043D\u0435\u0442.";
17340
- async function checkSetup(engine, asked) {
17695
+ async function inspectMachine(engine, asked) {
17341
17696
  const video = asked.video;
17342
17697
  const model = asked.model;
17343
17698
  const place = engine.place();
17344
- if (place === null) return toolError(engine.whyNoEngine());
17345
- const outcome = await engine.command([
17699
+ if (place === null) return { result: toolError(engine.whyNoEngine()), readiness: null };
17700
+ const args = [
17346
17701
  "env-check",
17347
17702
  "--pack",
17348
17703
  place.pack,
17349
17704
  ...video !== void 0 ? ["--video", path(video)] : [],
17350
17705
  ...model !== void 0 ? ["--model", model] : []
17351
- ]);
17352
- if (!outcome.ok) return toolError(outcome.text);
17706
+ ];
17707
+ let readiness = null;
17708
+ let words;
17709
+ if (knowsJsonCheck(place.engine)) {
17710
+ const answer = await askReadiness(engine, place, args);
17711
+ if ("ok" in answer && !answer.ok) return { result: toolError(answer.text), readiness: null };
17712
+ if ("parts" in answer) readiness = answer;
17713
+ words = "parts" in answer ? answer.words : answer.text;
17714
+ } else {
17715
+ const outcome = await engine.command(args);
17716
+ if (!outcome.ok) return { result: toolError(outcome.text), readiness: null };
17717
+ words = outcome.text;
17718
+ }
17353
17719
  const catalog = await askCatalog(engine, place.pack, video);
17354
- return toolSuccess(`${outcome.text}
17720
+ return { result: toolSuccess(`${words}
17355
17721
 
17356
- ${catalog}`);
17722
+ ${catalog}`), readiness };
17723
+ }
17724
+ async function askReadiness(engine, place, args) {
17725
+ const python = await engine.pythonTrouble();
17726
+ if (python !== null) return { ok: false, text: python };
17727
+ let stdout;
17728
+ try {
17729
+ stdout = (await engine.run(place, [...args, JSON_CHECK])).stdout;
17730
+ } catch (err) {
17731
+ const e = err;
17732
+ if (e.code !== 2 || typeof e.stdout !== "string") return parseFailure(err, "\u041F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u043C\u0430\u0448\u0438\u043D\u044B \u043D\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u043B\u0430\u0441\u044C");
17733
+ stdout = e.stdout;
17734
+ }
17735
+ return readReadiness(stdout) ?? { ok: true, text: stdout.trim() };
17357
17736
  }
17358
17737
  async function installStep(engine, asked, key, baseUrl, tell) {
17359
17738
  const step = asked.step;
@@ -17462,7 +17841,8 @@ ${EXTRAS_LATER}`), later]);
17462
17841
  })
17463
17842
  );
17464
17843
  }
17465
- async function updateEngine(engine, consent, key, baseUrl, timeoutMs = ASKED_POINTER_MS) {
17844
+ async function updateEngine(engine, consent, key, baseUrl, timeoutMs = ASKED_POINTER_MS, seen = () => {
17845
+ }) {
17466
17846
  if (engine.pathSetByHand()) {
17467
17847
  return toolSuccess(
17468
17848
  "\u041F\u0430\u043F\u043A\u0430 \u0434\u0432\u0438\u0436\u043A\u0430 \u0437\u0430\u0434\u0430\u043D\u0430 \u0432\u0440\u0443\u0447\u043D\u0443\u044E \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439 NAPARNIK_ENGINE \u2014 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E: \u0441\u0430\u043C\u043E\u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u0441 \u0432\u0435\u0440\u0441\u0438\u043E\u043D\u043D\u043E\u0439 \u0440\u0430\u0441\u043A\u043B\u0430\u0434\u043A\u043E\u0439 \u0432 ~/.naparnik/engine, \u0430 \u044D\u0442\u0430 \u043F\u0430\u043F\u043A\u0430 \u043C\u0438\u043C\u043E \u043D\u0435\u0451. \u0421\u043A\u0430\u0436\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443, \u0447\u0442\u043E \u0434\u0432\u0438\u0436\u043E\u043A \u0437\u0434\u0435\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0442\u0435\u043C \u0436\u0435 \u0441\u043F\u043E\u0441\u043E\u0431\u043E\u043C, \u043A\u0430\u043A\u0438\u043C \u0431\u044B\u043B \u043F\u043E\u043B\u043E\u0436\u0435\u043D."
@@ -17475,6 +17855,12 @@ async function updateEngine(engine, consent, key, baseUrl, timeoutMs = ASKED_POI
17475
17855
  }
17476
17856
  const outcome = await fetchManifest(manifestUrl(engine, baseUrl), timeoutMs, key);
17477
17857
  const decision = decide(engine.freshVersion(), outcome.ok ? outcome.manifest : null);
17858
+ seen({
17859
+ installed: engine.freshVersion(),
17860
+ fresh: decision.what === "nothing" ? null : decision.manifest.version,
17861
+ mb: decision.what === "nothing" ? 0 : Math.round(decision.manifest.bytes / 1024 / 1024),
17862
+ status: outcome.ok ? 0 : outcome.status ?? 0
17863
+ });
17478
17864
  if (decision.what === "nothing") {
17479
17865
  const ownDir2 = engine.freshVersion();
17480
17866
  return toolSuccess(
@@ -17496,12 +17882,19 @@ ${VOICE_CLEANER_NOTE}
17496
17882
  }
17497
17883
  return asToolResult(await deploy(engine, decision.manifest, void 0, key));
17498
17884
  }
17499
- async function updateShell(engine, consent, key, baseUrl, timeoutMs = ASKED_POINTER_MS) {
17885
+ async function updateShell(engine, consent, key, baseUrl, timeoutMs = ASKED_POINTER_MS, seen = () => {
17886
+ }) {
17500
17887
  const home = engine.home();
17501
17888
  const url2 = engine.variable("NAPARNIK_BUNDLE_MANIFEST") ?? bundleUrlFor(baseUrl);
17502
17889
  const outcome = await fetchShellManifest(url2, timeoutMs, key);
17503
17890
  const installed = installedShell(home);
17504
17891
  const decision = decideShell(installed, outcome.ok ? outcome.manifest : null);
17892
+ seen({
17893
+ installed,
17894
+ fresh: decision.what === "nothing" ? null : decision.manifest.version,
17895
+ mb: decision.what === "nothing" ? 0 : Math.round((decision.manifest.body?.bytes ?? 0) / 1024 / 1024),
17896
+ status: outcome.ok ? 0 : outcome.status ?? 0
17897
+ });
17505
17898
  if (decision.what === "nothing") {
17506
17899
  if (!outcome.ok) {
17507
17900
  return toolSuccess(
@@ -17586,46 +17979,89 @@ function setupTool(engine, passport, key, baseUrl) {
17586
17979
  }
17587
17980
  }),
17588
17981
  async execute(args, ctx) {
17589
- if (args.rollback === "engine") return asToolResult(rollback(engine));
17590
- if (args.rollback === "shell") return asToolResult(rollbackShell(engine.home()));
17591
- const consent = args.consent === true;
17592
- const tell = ctx?.emitProgress;
17593
- const what = args.what;
17594
- if (what === void 0) return lookAround(engine, args.model, key, baseUrl);
17595
- if (what === "engine") return updateEngine(engine, consent, key, baseUrl);
17596
- if (what === "shell") return updateShell(engine, consent, key, baseUrl);
17597
- if (what === "all") {
17598
- return consent ? installEverything(engine, key, baseUrl, tell) : lookAround(engine, args.model, key, baseUrl);
17599
- }
17600
- const step = HEAVY_STEPS[what];
17601
- return consent ? installStep(engine, { step, model: args.model, consent: true }, key, baseUrl, tell) : askBeforeHeavy(engine, step, args.model, key, baseUrl);
17982
+ const news = { engine: null, shell: null };
17983
+ const said = await answer(args, ctx?.emitProgress, news);
17984
+ return withCard(said, () => setupCard({
17985
+ readiness: null,
17986
+ engine: news.engine ?? { installed: engine.freshVersion(), fresh: null, mb: 0, status: 0 },
17987
+ shell: news.shell,
17988
+ note: firstLine(said),
17989
+ shellVersion: VERSION
17990
+ }));
17602
17991
  }
17603
17992
  });
17993
+ async function answer(args, tell, news) {
17994
+ if (args.rollback === "engine") return asToolResult(rollback(engine));
17995
+ if (args.rollback === "shell") return asToolResult(rollbackShell(engine.home()));
17996
+ const consent = args.consent === true;
17997
+ const what = args.what;
17998
+ if (what === void 0) return lookAround(engine, args.model, key, baseUrl);
17999
+ if (what === "engine") {
18000
+ return updateEngine(engine, consent, key, baseUrl, ASKED_POINTER_MS, (seen) => {
18001
+ if (!consent) news.engine = seen;
18002
+ });
18003
+ }
18004
+ if (what === "shell") {
18005
+ return updateShell(engine, consent, key, baseUrl, ASKED_POINTER_MS, (seen) => {
18006
+ if (!consent) news.shell = seen;
18007
+ });
18008
+ }
18009
+ if (what === "all") {
18010
+ return consent ? installEverything(engine, key, baseUrl, tell) : lookAround(engine, args.model, key, baseUrl);
18011
+ }
18012
+ const step = HEAVY_STEPS[what];
18013
+ return consent ? installStep(engine, { step, model: args.model, consent: true }, key, baseUrl, tell) : askBeforeHeavy(engine, step, args.model, key, baseUrl);
18014
+ }
17604
18015
  }
17605
18016
  async function lookAround(engine, model, key, baseUrl) {
17606
- const shell = updateShell(engine, false, key, baseUrl, NEWS_POINTER_MS);
18017
+ let engineNews = null;
18018
+ let shellNews = null;
18019
+ const toEngine = (news2) => {
18020
+ engineNews = news2;
18021
+ };
18022
+ const toShell = (news2) => {
18023
+ shellNews = news2;
18024
+ };
18025
+ const card = (said, o) => withCard(said, () => setupCard({
18026
+ readiness: o.readiness ?? null,
18027
+ engine: engineNews ?? { installed: engine.freshVersion(), fresh: null, mb: 0, status: 0 },
18028
+ shell: shellNews,
18029
+ ...o.noEngine === true ? { noEngine: true } : {},
18030
+ ...o.noPython === true ? { noPython: true } : {},
18031
+ note: firstLine(said),
18032
+ shellVersion: VERSION
18033
+ }));
18034
+ const shell = updateShell(engine, false, key, baseUrl, NEWS_POINTER_MS, toShell);
17607
18035
  if (engine.place() === null) {
17608
18036
  const payload = await installPayload(engine, {
17609
18037
  consent: false,
17610
18038
  manifestUrl: manifestUrl(engine, baseUrl),
17611
18039
  ...key !== void 0 ? { key } : {}
17612
18040
  });
17613
- return together([toolSuccess(payload.text), await shell]);
18041
+ return card(together([toolSuccess(payload.text), await shell]), { noEngine: true });
17614
18042
  }
17615
18043
  if (await engine.pythonTrouble() !== null) {
17616
18044
  const python = await installPython(engine, { consent: false });
17617
- return together([
18045
+ return card(together([
17618
18046
  toolSuccess(python.text),
17619
- await updateEngine(engine, false, key, baseUrl, NEWS_POINTER_MS),
18047
+ await updateEngine(engine, false, key, baseUrl, NEWS_POINTER_MS, toEngine),
17620
18048
  await shell
17621
- ]);
18049
+ ]), { noPython: true });
17622
18050
  }
17623
18051
  const [machine, news, own] = await Promise.all([
17624
- checkSetup(engine, { model }),
17625
- updateEngine(engine, false, key, baseUrl, NEWS_POINTER_MS),
18052
+ inspectMachine(engine, { model }),
18053
+ updateEngine(engine, false, key, baseUrl, NEWS_POINTER_MS, toEngine),
17626
18054
  shell
17627
18055
  ]);
17628
- return together([machine, news, own]);
18056
+ return card(together([machine.result, news, own]), { readiness: machine.readiness });
18057
+ }
18058
+ function withCard(result, card) {
18059
+ if (result.type !== "success" || result.structured !== void 0) return result;
18060
+ return toolSuccess(wordsOf(result.content), card());
18061
+ }
18062
+ function firstLine(result) {
18063
+ const text2 = result.type === "success" ? wordsOf(result.content) : result.error;
18064
+ return text2.split("\n").map((line) => line.trim()).find((line) => line !== "") ?? "";
17629
18065
  }
17630
18066
  async function installEverything(engine, key, baseUrl, tell) {
17631
18067
  const said = [await updateShell(engine, true, key, baseUrl)];
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Engine, chooseBody, handOverToBody, VERSION, rollback, rollbackShell, remove, fromNpxCache, install, keyFromBlock, readConfig, ForeignBlockError, currentMachineFingerprint, CrashReporter, watchProcessCrashes, McpServer, buildAll, realPace, buildResources, StdioServerTransport, fromHost, WHERE_TO_GET_KEY, Wakefulness, systemBlocker, updateEngine, installStep, NaparnikApiClient, timeToCheck2, manifestUrlFor, fetchManifest, markChecked2, decide, shellDir, timeToCheck, bundleUrlFor, fetchShellManifest, markChecked, decideShell, installedShell, NaparnikConfigError } from './chunk-MCZJVJFA.js';
2
+ import { Engine, chooseBody, handOverToBody, VERSION, rollback, rollbackShell, remove, fromNpxCache, install, keyFromBlock, readConfig, ForeignBlockError, currentMachineFingerprint, CrashReporter, watchProcessCrashes, McpServer, buildAll, realPace, buildResources, StdioServerTransport, fromHost, WHERE_TO_GET_KEY, Wakefulness, systemBlocker, updateEngine, installStep, NaparnikApiClient, timeToCheck2, manifestUrlFor, fetchManifest, markChecked2, decide, shellDir, timeToCheck, bundleUrlFor, fetchShellManifest, markChecked, decideShell, installedShell, NaparnikConfigError } from './chunk-EOGD2CRG.js';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { realpathSync, readFileSync } from 'fs';
5
5
 
@@ -74,9 +74,9 @@ choose_variant: \u043E\u043D\u0438 \u0441\u0447\u0438\u0442\u0430\u044E\u0442 \u
74
74
 
75
75
  \u041F\u043E\u0441\u043B\u0435 \u044D\u0442\u043E\u0433\u043E \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u041A\u043B\u043E\u0434\u0430 \u0438\u043B\u0438 Codex. ${hint}
76
76
 
77
- \u26A0\uFE0F \u041A\u041B\u042E\u0427 \u0416\u0418\u0412\u0401\u0422 \u041D\u0410 \u041E\u0414\u041D\u041E\u041C \u041A\u041E\u041C\u041F\u042C\u042E\u0422\u0415\u0420\u0415: \u043D\u043E\u0432\u044B\u0439 \u0432\u044B\u043F\u0443\u0441\u043A \u043E\u0442\u0437\u044B\u0432\u0430\u0435\u0442 \u043F\u0440\u0435\u0436\u043D\u0438\u0439. \xAB\u041F\u0440\u0438\u0448\u043B\u0438\u0442\u0435 \u043C\u043E\u0439
78
- \u043A\u043B\u044E\u0447\xBB \u043E\u0437\u043D\u0430\u0447\u0430\u0435\u0442 \xAB\u0432\u044B\u0434\u0430\u0439\u0442\u0435 \u043D\u043E\u0432\u044B\u0439, \u0441\u0442\u0430\u0440\u044B\u0439 \u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u0435\xBB \u2014 \u0441\u043A\u0430\u0437\u0430\u0442\u044C \u044D\u0442\u043E \u043D\u0430\u0434\u043E \u0414\u041E, \u0430 \u043D\u0435
79
- \u043F\u043E\u0441\u043B\u0435. \u0421\u043C\u0435\u043D\u0438\u0442\u044C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440 \u043C\u043E\u0436\u043D\u043E \u043A\u043D\u043E\u043F\u043A\u043E\u0439 \u0432 \u0431\u043E\u0442\u0435.
77
+ \u26A0\uFE0F \u0420\u0410\u0411\u041E\u0422\u0410\u0415\u0422 \u041E\u0414\u0418\u041D \u041A\u041B\u042E\u0427. \u041D\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B \u0438\u0437 \u0431\u043E\u0442\u0430 \u043F\u0440\u0435\u0436\u043D\u044E\u044E \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0443 \u043D\u0435 \u0442\u0440\u043E\u0433\u0430\u0435\u0442,
78
+ \u043F\u043E\u043A\u0430 \u0435\u0433\u043E \u043D\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u043B\u0438; \u043F\u0435\u0440\u0432\u044B\u0439 \u0437\u0430\u043F\u0440\u043E\u0441 \u0441 \u043D\u043E\u0432\u044B\u043C \u043A\u043B\u044E\u0447\u043E\u043C \u043E\u0442\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043F\u0440\u0435\u0436\u043D\u0438\u0439 \u2014 \u0437\u0434\u0435\u0441\u044C
79
+ \u0438\u043B\u0438 \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u043C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435. \u0421\u043C\u0435\u043D\u0438\u0442\u044C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440 \u043C\u043E\u0436\u043D\u043E \u043A\u043D\u043E\u043F\u043A\u043E\u0439 \u0432 \u0431\u043E\u0442\u0435.
80
80
  `.trim();
81
81
  }
82
82
  function configuredInstructions(baseUrl) {
package/dist/index.d.ts CHANGED
@@ -1534,9 +1534,9 @@ declare class NaparnikConfigError extends NaparnikError {
1534
1534
  * годится ли она для новой начинки: разойдись она с пакетом, и в логах будет
1535
1535
  * стоять чужое число, а обновление начнёт считать оболочку не той.
1536
1536
  */
1537
- declare const VERSION = "0.10.38";
1537
+ declare const VERSION = "0.10.41";
1538
1538
  /** Как мы представляемся серверу Напарника. По нему в логах видно оболочку. */
1539
- declare const USER_AGENT = "naparnik-mcp/0.10.38";
1539
+ declare const USER_AGENT = "naparnik-mcp/0.10.41";
1540
1540
  /**
1541
1541
  * Самая старая начинка (движок монтажа), с которой эта оболочка работает.
1542
1542
  * Обновление начинки ниже этой версии не ставится, а выше — требует оболочки
@@ -1622,12 +1622,18 @@ declare function parseEngineManifest(raw: unknown, from: string): ManifestOutcom
1622
1622
  * Что вышло из разбора: наш объект — или причина, по которой его нет.
1623
1623
  * Одна форма на оба указателя.
1624
1624
  */
1625
+ /**
1626
+ * `status` — код раздачи, когда она ответила отказом. Нужен карточке `setup`:
1627
+ * 401 (ключа нет или чужой) и 403 (ключ наш, подписка кончилась) человеку
1628
+ * говорятся разными словами, а в `why` они слиты в одну фразу для модели.
1629
+ */
1625
1630
  type Parsed<T> = {
1626
1631
  ok: true;
1627
1632
  manifest: T;
1628
1633
  } | {
1629
1634
  ok: false;
1630
1635
  why: string;
1636
+ status?: number;
1631
1637
  };
1632
1638
 
1633
1639
  /** Умолчание: раздача прода. Одна правда с адресом API — `config.ts`. */
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { DEFAULT_HOME, DeliveryError, Engine, ForeignBlockError, INSIDE_PYTHON, INSTALL_STEPS, KEEP_COPIES, KEEP_VERSIONS, LONG_STEPS, MANIFEST_TIMEOUT_MS, MANIFEST_URL, MARKER, MIN_ENGINE, NaparnikApiClient, NaparnikAuthError, NaparnikClientError, NaparnikConfigError, NaparnikError, NaparnikInsufficientBalanceError, NaparnikScopeError, NaparnikServerError, NaparnikTimeoutError, PAUSE_HOURS, SERVER_NAME, SOURCES_FILE, START_SEC, TOOL_SEC, USER_AGENT, VERSION, buildAll, buildBlock, buildResources, compare, configPath, decide, defineTool, deploy, downloadFile, fetchManifest, fileOrRefusal, findBlock, fromNpxCache, inside, install, installPayload, installPython, jsonSchemaOf, keyInTable, lastLine, loadConfig, markChecked2 as markChecked, markCurrent, parseEngineManifest, parseFailure, path, platformKey, previousVersion, probe, pythonSource, refusalFor, remove, rollback, tarEntries, tidyOld, timeToCheck2 as timeToCheck, unpackArchive, withArgs } from './chunk-MCZJVJFA.js';
1
+ export { DEFAULT_HOME, DeliveryError, Engine, ForeignBlockError, INSIDE_PYTHON, INSTALL_STEPS, KEEP_COPIES, KEEP_VERSIONS, LONG_STEPS, MANIFEST_TIMEOUT_MS, MANIFEST_URL, MARKER, MIN_ENGINE, NaparnikApiClient, NaparnikAuthError, NaparnikClientError, NaparnikConfigError, NaparnikError, NaparnikInsufficientBalanceError, NaparnikScopeError, NaparnikServerError, NaparnikTimeoutError, PAUSE_HOURS, SERVER_NAME, SOURCES_FILE, START_SEC, TOOL_SEC, USER_AGENT, VERSION, buildAll, buildBlock, buildResources, compare, configPath, decide, defineTool, deploy, downloadFile, fetchManifest, fileOrRefusal, findBlock, fromNpxCache, inside, install, installPayload, installPython, jsonSchemaOf, keyInTable, lastLine, loadConfig, markChecked2 as markChecked, markCurrent, parseEngineManifest, parseFailure, path, platformKey, previousVersion, probe, pythonSource, refusalFor, remove, rollback, tarEntries, tidyOld, timeToCheck2 as timeToCheck, unpackArchive, withArgs } from './chunk-EOGD2CRG.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naparnik/mcp",
3
- "version": "0.10.38",
3
+ "version": "0.10.41",
4
4
  "description": "MCP-сервер Напарника: монтаж вертикального видео на машине пользователя — паузы, субтитры, герой на новом фоне, звук. Видео никуда не уезжает.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "private": false,