@naparnik/mcp 0.10.22 → 0.10.25

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.
@@ -1,11 +1,11 @@
1
- import { createHash, randomBytes, randomUUID } from 'crypto';
2
1
  import { execFile, spawn, execFileSync } from 'child_process';
3
- import { existsSync, readFileSync, closeSync, mkdirSync, openSync, readdirSync, statSync, rmSync, writeFileSync, renameSync, symlinkSync, chmodSync, lstatSync, realpathSync, mkdtempSync, linkSync, readlinkSync, appendFileSync, fsyncSync } from 'fs';
4
- import { platform, homedir, tmpdir } from 'os';
5
- import { win32, join, dirname, sep, resolve, relative, isAbsolute, basename } from 'path';
2
+ import { createHash, randomBytes, randomUUID } from 'crypto';
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
+ import { platform, homedir, arch, release, userInfo, tmpdir } from 'os';
5
+ import { win32, join, dirname, sep, resolve, relative, isAbsolute, basename, extname } from 'path';
6
6
  import { promisify } from 'util';
7
7
  import { gunzipSync } from 'zlib';
8
- import { pathToFileURL } from 'url';
8
+ import { pathToFileURL, fileURLToPath } from 'url';
9
9
  import { readFile } from 'fs/promises';
10
10
 
11
11
  var __defProp = Object.defineProperty;
@@ -176,12 +176,12 @@ var StdioServerTransport = class {
176
176
  throw new McpError(McpErrorCode.InternalError, "Stdio-\u0442\u0440\u0430\u043D\u0441\u043F\u043E\u0440\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D");
177
177
  }
178
178
  const line = encodeNdjsonLine(message);
179
- await new Promise((resolve4, reject) => {
179
+ await new Promise((resolve5, reject) => {
180
180
  let settled = false;
181
181
  const done = () => {
182
182
  if (settled) return;
183
183
  settled = true;
184
- resolve4();
184
+ resolve5();
185
185
  };
186
186
  const flushed = process.stdout.write(line, "utf8", (err) => {
187
187
  if (err !== null && err !== void 0) {
@@ -412,6 +412,13 @@ var McpServer = class {
412
412
  * Обрабатывает запрос инициализации.
413
413
  */
414
414
  handleInitialize(params) {
415
+ const client = params?.["clientInfo"];
416
+ if (typeof client?.name === "string") {
417
+ this.toolContext = {
418
+ ...this.toolContext,
419
+ host: { name: client.name, version: typeof client.version === "string" ? client.version : "" }
420
+ };
421
+ }
415
422
  const clientVersion = params?.["protocolVersion"];
416
423
  const negotiatedVersion = typeof clientVersion === "string" && clientVersion.length > 0 ? clientVersion : MCP_PROTOCOL_VERSION;
417
424
  return {
@@ -658,8 +665,8 @@ function describeFailure(err) {
658
665
  const bottom = err.cause;
659
666
  if (bottom instanceof Error) {
660
667
  const code = bottom.code;
661
- const tail = typeof code === "string" ? `${code}: ${bottom.message}` : bottom.message;
662
- return err.message === tail || tail === "" ? err.message : `${err.message} (${tail})`;
668
+ const tail2 = typeof code === "string" ? `${code}: ${bottom.message}` : bottom.message;
669
+ return err.message === tail2 || tail2 === "" ? err.message : `${err.message} (${tail2})`;
663
670
  }
664
671
  return err.message;
665
672
  }
@@ -679,8 +686,8 @@ function loadConfig(env = process.env) {
679
686
  throw new NaparnikConfigError(state.message);
680
687
  }
681
688
  function fromHost(value) {
682
- const text = (value ?? "").trim();
683
- return /^\$\{[^}]*\}$/.test(text) ? "" : text;
689
+ const text2 = (value ?? "").trim();
690
+ return /^\$\{[^}]*\}$/.test(text2) ? "" : text2;
684
691
  }
685
692
  function readConfig(env = process.env) {
686
693
  const apiKey = fromHost(env["NAPARNIK_API_KEY"]);
@@ -720,7 +727,7 @@ function parsePositiveInt(raw, fallback) {
720
727
  }
721
728
 
722
729
  // src/version.ts
723
- var VERSION = "0.10.22";
730
+ var VERSION = "0.10.25";
724
731
  var USER_AGENT = `naparnik-mcp/${VERSION}`;
725
732
  var MIN_ENGINE = "2026.09.23-406";
726
733
  var LAUNCHER_PROTOCOL = 1;
@@ -890,9 +897,9 @@ var NaparnikApiClient = class {
890
897
  return headers;
891
898
  }
892
899
  async parse(response, shape = {}) {
893
- let text;
900
+ let text2;
894
901
  try {
895
- text = await response.text();
902
+ text2 = await response.text();
896
903
  } catch (err) {
897
904
  throw new NaparnikServerError(
898
905
  `\u0421\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u0435 \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u0438 \u0447\u0442\u0435\u043D\u0438\u0438 \u043E\u0442\u0432\u0435\u0442\u0430 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430: ${describe(err)}`,
@@ -900,15 +907,15 @@ var NaparnikApiClient = class {
900
907
  );
901
908
  }
902
909
  if (response.ok) {
903
- if (shape.wantText === true) return text;
904
- if (text === "") return {};
910
+ if (shape.wantText === true) return text2;
911
+ if (text2 === "") return {};
905
912
  try {
906
- return JSON.parse(text);
913
+ return JSON.parse(text2);
907
914
  } catch {
908
- throw new NaparnikServerError("\u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A \u0432\u0435\u0440\u043D\u0443\u043B \u043D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 JSON", response.status, truncate(text));
915
+ throw new NaparnikServerError("\u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A \u0432\u0435\u0440\u043D\u0443\u043B \u043D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 JSON", response.status, truncate(text2));
909
916
  }
910
917
  }
911
- const payload = safeJson(text);
918
+ const payload = safeJson(text2);
912
919
  switch (response.status) {
913
920
  // ⚠ ЗАПАСНЫЕ ТЕКСТЫ ЗДЕСЬ ВЕЛИ В ПАНЕЛЬ, КОТОРОЙ НЕТ, и молча: обычно
914
921
  // сервер присылает свой текст, и эти строки видны только когда 401
@@ -940,20 +947,20 @@ var NaparnikApiClient = class {
940
947
  throw new NaparnikServerError(
941
948
  `\u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043E\u0448\u0438\u0431\u043A\u043E\u0439 ${response.status}. \u042D\u0442\u043E \u0432\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0439 \u0441\u0431\u043E\u0439 \u043D\u0430 \u043D\u0430\u0448\u0435\u0439 \u0441\u0442\u043E\u0440\u043E\u043D\u0435.`,
942
949
  response.status,
943
- truncate(text)
950
+ truncate(text2)
944
951
  );
945
952
  }
946
953
  throw new NaparnikClientError(
947
954
  messageFrom(payload) ?? `\u0417\u0430\u043F\u0440\u043E\u0441 \u043E\u0442\u043A\u043B\u043E\u043D\u0451\u043D (${response.status}).`,
948
955
  response.status,
949
- truncate(text)
956
+ truncate(text2)
950
957
  );
951
958
  }
952
959
  }
953
960
  };
954
- function safeJson(text) {
961
+ function safeJson(text2) {
955
962
  try {
956
- return JSON.parse(text);
963
+ return JSON.parse(text2);
957
964
  } catch {
958
965
  return null;
959
966
  }
@@ -970,17 +977,17 @@ function messageFrom(payload) {
970
977
  function numberOr(value, fallback) {
971
978
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
972
979
  }
973
- function truncate(text, limit = 500) {
974
- return text.length > limit ? `${text.slice(0, limit)}\u2026` : text;
980
+ function truncate(text2, limit = 500) {
981
+ return text2.length > limit ? `${text2.slice(0, limit)}\u2026` : text2;
975
982
  }
976
983
  function networkHint(err) {
977
- const text = describeFailure(err);
984
+ const text2 = describeFailure(err);
978
985
  const proxy = ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"].find((i) => (process.env[i] ?? "").trim() !== "");
979
986
  if (proxy !== void 0) {
980
987
  return `
981
988
  \u041D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u0437\u0430\u0434\u0430\u043D ${proxy}, \u043D\u043E Node \u043D\u0435 \u0445\u043E\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 \u043F\u0440\u043E\u043A\u0441\u0438 \u0441\u0430\u043C \u2014 \u0437\u0430\u043F\u0440\u043E\u0441 \u0443\u0448\u0451\u043B \u043D\u0430\u043F\u0440\u044F\u043C\u0443\u044E. \u0415\u0441\u043B\u0438 \u0432\u044B\u0445\u043E\u0434 \u0432 \u0438\u043D\u0442\u0435\u0440\u043D\u0435\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0435\u0440\u0435\u0437 \u043F\u0440\u043E\u043A\u0441\u0438, \u043D\u0443\u0436\u0435\u043D Node 24 \u0438\u043B\u0438 \u043D\u043E\u0432\u0435\u0435 \u0441 NODE_USE_ENV_PROXY=1.`;
982
989
  }
983
- if (/CERT|SELF_SIGNED|UNABLE_TO_VERIFY/i.test(text)) {
990
+ if (/CERT|SELF_SIGNED|UNABLE_TO_VERIFY/i.test(text2)) {
984
991
  return "\n\u041F\u043E\u0445\u043E\u0436\u0435 \u043D\u0430 \u043F\u0435\u0440\u0435\u0445\u0432\u0430\u0442 TLS (\u043A\u043E\u0440\u043F\u043E\u0440\u0430\u0442\u0438\u0432\u043D\u044B\u0439 \u0430\u043D\u0442\u0438\u0432\u0438\u0440\u0443\u0441 \u0438\u043B\u0438 \u043F\u0440\u043E\u043A\u0441\u0438). \u041F\u0443\u0442\u044C \u043A \u043A\u043E\u0440\u043D\u0435\u0432\u043E\u043C\u0443 \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043A\u0430\u0442\u0443 \u043E\u0440\u0433\u0430\u043D\u0438\u0437\u0430\u0446\u0438\u0438 \u0437\u0430\u0434\u0430\u0451\u0442\u0441\u044F \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u043E\u0439 NODE_EXTRA_CA_CERTS.";
985
992
  }
986
993
  return "";
@@ -989,7 +996,175 @@ function describe(err) {
989
996
  return describeFailure(err);
990
997
  }
991
998
  function sleep(ms) {
992
- return new Promise((resolve4) => setTimeout(resolve4, ms));
999
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
1000
+ }
1001
+ var TAIL_MS = 10 * 6e4;
1002
+ var AWAY_NOTE = "\u0421\u043A\u0430\u0436\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443: \u043C\u043E\u0436\u043D\u043E \u043E\u0442\u043E\u0439\u0442\u0438, \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440 \u043D\u0435 \u0443\u0441\u043D\u0451\u0442, \u043F\u043E\u043A\u0430 \u0438\u0434\u0451\u0442 \u0440\u0430\u0431\u043E\u0442\u0430. \u0422\u043E\u043B\u044C\u043A\u043E \u043A\u0440\u044B\u0448\u043A\u0443 \u043D\u043E\u0443\u0442\u0431\u0443\u043A\u0430 \u043D\u0435 \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0442\u044C.";
1003
+ var realClock = {
1004
+ now: () => Date.now(),
1005
+ later(ms, fire) {
1006
+ const timer = setTimeout(fire, ms);
1007
+ timer.unref();
1008
+ return () => clearTimeout(timer);
1009
+ }
1010
+ };
1011
+ var Wakefulness = class {
1012
+ constructor(blocker, opts = {}) {
1013
+ this.blocker = blocker;
1014
+ this.clock = opts.clock ?? realClock;
1015
+ this.pid = opts.pid ?? process.pid;
1016
+ this.tailMs = opts.tailMs ?? TAIL_MS;
1017
+ }
1018
+ blocker;
1019
+ holders = /* @__PURE__ */ new Set();
1020
+ until = 0;
1021
+ lamp = null;
1022
+ /**
1023
+ * Система однажды лампу не дала — больше не просим. Иначе каждый вызов
1024
+ * пытался бы заново запустить то, чего нет, а работа человека важнее.
1025
+ */
1026
+ refused = false;
1027
+ cancelTail = null;
1028
+ clock;
1029
+ pid;
1030
+ tailMs;
1031
+ /** Держать компьютер бодрым; вернуть снятие. Снять второй раз — ничего не делает. */
1032
+ hold() {
1033
+ const token = /* @__PURE__ */ Symbol("hold");
1034
+ this.holders.add(token);
1035
+ this.sync();
1036
+ return () => {
1037
+ if (!this.holders.delete(token)) return;
1038
+ this.until = Math.max(this.until, this.clock.now() + this.tailMs);
1039
+ this.sync();
1040
+ };
1041
+ }
1042
+ /**
1043
+ * Держать, пока идёт работа, — и когда она кончилась успехом, и когда упала.
1044
+ *
1045
+ * ⚠ Работа — функцией, а не готовым обещанием. Обещание уже запущено: тело
1046
+ * async-функции до первого await идёт при вычислении аргумента, и начало
1047
+ * работы оставалось без лампы (тест обёртки поймал ровно это).
1048
+ */
1049
+ async during(work) {
1050
+ const release2 = this.hold();
1051
+ try {
1052
+ return await work();
1053
+ } finally {
1054
+ release2();
1055
+ }
1056
+ }
1057
+ /**
1058
+ * Своя лампа для фоновой работы, привязанная к её процессу. Гаснет сама, когда
1059
+ * работа кончилась, — даже если нас к тому времени уже закрыли.
1060
+ */
1061
+ follow(pid) {
1062
+ if (this.refused) return;
1063
+ try {
1064
+ if (this.blocker(pid) === null) this.refused = true;
1065
+ } catch {
1066
+ this.refused = true;
1067
+ }
1068
+ }
1069
+ sync() {
1070
+ const want = this.holders.size > 0 || this.clock.now() < this.until;
1071
+ if (want && this.lamp === null && !this.refused) this.light();
1072
+ if (!want && this.lamp !== null) {
1073
+ this.lamp.stop();
1074
+ this.lamp = null;
1075
+ }
1076
+ this.cancelTail?.();
1077
+ this.cancelTail = null;
1078
+ if (this.holders.size === 0 && want) {
1079
+ this.cancelTail = this.clock.later(this.until - this.clock.now(), () => this.sync());
1080
+ }
1081
+ }
1082
+ light() {
1083
+ let lamp;
1084
+ try {
1085
+ lamp = this.blocker(this.pid);
1086
+ } catch {
1087
+ lamp = null;
1088
+ }
1089
+ if (lamp === null) {
1090
+ this.refused = true;
1091
+ return;
1092
+ }
1093
+ this.lamp = lamp;
1094
+ lamp.onExit(() => {
1095
+ if (this.lamp === lamp) this.lamp = null;
1096
+ });
1097
+ }
1098
+ };
1099
+ var ASLEEP = new Wakefulness(() => null);
1100
+ function systemBlocker(platform4 = process.platform, run = spawn, env = process.env) {
1101
+ return (pid) => {
1102
+ let command;
1103
+ let args;
1104
+ if (platform4 === "darwin") {
1105
+ command = "/usr/bin/caffeinate";
1106
+ args = ["-i", "-w", String(pid)];
1107
+ } else if (platform4 === "win32") {
1108
+ command = `${env["SystemRoot"] ?? "C:\\Windows"}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
1109
+ args = [
1110
+ "-NoProfile",
1111
+ "-NonInteractive",
1112
+ "-WindowStyle",
1113
+ "Hidden",
1114
+ // Закодированным, а не строкой: в тексте кавычки из C#, и Windows
1115
+ // склеивает их с кавычками командной строки непредсказуемо.
1116
+ "-EncodedCommand",
1117
+ Buffer.from(windowsScript(pid), "utf16le").toString("base64")
1118
+ ];
1119
+ } else {
1120
+ return null;
1121
+ }
1122
+ const child = run(command, args, {
1123
+ stdio: "ignore",
1124
+ windowsHide: true,
1125
+ detached: false
1126
+ });
1127
+ child.unref();
1128
+ let exited = false;
1129
+ const listeners = [];
1130
+ const gone = () => {
1131
+ if (exited) return;
1132
+ exited = true;
1133
+ for (const cb of listeners) cb();
1134
+ };
1135
+ child.on("error", gone);
1136
+ child.on("exit", gone);
1137
+ return {
1138
+ stop() {
1139
+ try {
1140
+ child.kill();
1141
+ } catch {
1142
+ }
1143
+ },
1144
+ onExit(cb) {
1145
+ if (exited) cb();
1146
+ else listeners.push(cb);
1147
+ }
1148
+ };
1149
+ };
1150
+ }
1151
+ function windowsScript(pid) {
1152
+ return [
1153
+ `$t = Add-Type -Name Power -Namespace Naparnik -PassThru -MemberDefinition '[DllImport("kernel32.dll")] public static extern uint SetThreadExecutionState(uint f);'`,
1154
+ "[void]$t::SetThreadExecutionState([uint32]2147483649)",
1155
+ `Wait-Process -Id ${pid} -ErrorAction SilentlyContinue`
1156
+ ].join("\n");
1157
+ }
1158
+ function withWakefulness(awake) {
1159
+ return (tool) => {
1160
+ const original = tool.execute.bind(tool);
1161
+ return {
1162
+ ...tool,
1163
+ async execute(input, ...rest) {
1164
+ return awake.during(() => original(input, ...rest));
1165
+ }
1166
+ };
1167
+ };
993
1168
  }
994
1169
  var FINGERPRINT_VERSION = "mid1";
995
1170
  var MIN_ID_LENGTH = 8;
@@ -1185,10 +1360,12 @@ function montageEnv(base, key) {
1185
1360
  };
1186
1361
  }
1187
1362
  var Engine = class {
1188
- constructor(env = process.env) {
1363
+ constructor(env = process.env, awake = ASLEEP) {
1189
1364
  this.env = env;
1365
+ this.awake = awake;
1190
1366
  }
1191
1367
  env;
1368
+ awake;
1192
1369
  passportCache;
1193
1370
  versionAtStart;
1194
1371
  pythonChecked;
@@ -1576,6 +1753,7 @@ var Engine = class {
1576
1753
  } catch {
1577
1754
  }
1578
1755
  }
1756
+ if (child.pid !== void 0) this.awake.follow(child.pid);
1579
1757
  this.watch(child, opts.place, id, opts.advice, log.file);
1580
1758
  return { id };
1581
1759
  }
@@ -1627,7 +1805,7 @@ var Engine = class {
1627
1805
  });
1628
1806
  });
1629
1807
  child.on("close", (code, signal) => {
1630
- const tail = logTail();
1808
+ const tail2 = logTail();
1631
1809
  if (signal) {
1632
1810
  void this.run(place, [
1633
1811
  "job-abort",
@@ -1635,17 +1813,17 @@ var Engine = class {
1635
1813
  "--signal",
1636
1814
  String(signal),
1637
1815
  "--tail",
1638
- lastLines(tail)
1816
+ lastLines(tail2)
1639
1817
  ]).catch(() => {
1640
1818
  });
1641
1819
  return;
1642
1820
  }
1643
- if (code === 0 || code === 2 || code === 3 || tail.trim() === "") return;
1821
+ if (code === 0 || code === 2 || code === 3 || tail2.trim() === "") return;
1644
1822
  void this.run(place, [
1645
1823
  "job-fail",
1646
1824
  id,
1647
1825
  "--what",
1648
- lastLines(tail),
1826
+ lastLines(tail2),
1649
1827
  "--what-to-do",
1650
1828
  "\u043F\u043E\u043A\u0430\u0436\u0438 \u044D\u0442\u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u0446\u0435\u043B\u0438\u043A\u043E\u043C"
1651
1829
  ]).catch(() => {
@@ -1696,8 +1874,8 @@ function lastLine(output) {
1696
1874
  const lines = output.trim().split("\n");
1697
1875
  return (lines[lines.length - 1] ?? "").trim();
1698
1876
  }
1699
- function lastLines(text, howMany) {
1700
- return text.trim().split("\n").slice(-8).join("\n");
1877
+ function lastLines(text2, howMany) {
1878
+ return text2.trim().split("\n").slice(-8).join("\n");
1701
1879
  }
1702
1880
  var NOT_JSON = "\u0414\u0432\u0438\u0436\u043E\u043A \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043D\u0435 \u0442\u0435\u043C, \u0447\u0435\u0433\u043E \u0436\u0434\u0430\u043B\u0438. \u041E\u0431\u043D\u043E\u0432\u0438 \u0434\u0432\u0438\u0436\u043E\u043A \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u043C setup \u0441 what=engine \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438.";
1703
1881
 
@@ -13301,19 +13479,19 @@ function acquireVersionLock(dir, hooks = {}) {
13301
13479
  };
13302
13480
  }
13303
13481
  async function withVersionLock(dir, work, hooks = {}) {
13304
- const release = acquireVersionLock(dir, hooks);
13482
+ const release2 = acquireVersionLock(dir, hooks);
13305
13483
  try {
13306
13484
  return await work();
13307
13485
  } finally {
13308
- release();
13486
+ release2();
13309
13487
  }
13310
13488
  }
13311
13489
  function withVersionLockSync(dir, work) {
13312
- const release = acquireVersionLock(dir);
13490
+ const release2 = acquireVersionLock(dir);
13313
13491
  try {
13314
13492
  return work();
13315
13493
  } finally {
13316
- release();
13494
+ release2();
13317
13495
  }
13318
13496
  }
13319
13497
  var PUBLICATION_FILE = ".publication.json";
@@ -13922,7 +14100,7 @@ function preparedCandidateAfterBootstrap(engine, before) {
13922
14100
  }
13923
14101
  return { ok: true, candidate, place };
13924
14102
  }
13925
- function blockCandidate(dir, candidate, text) {
14103
+ function blockCandidate(dir, candidate, text2) {
13926
14104
  if (!sameCandidateState(readCandidate(dir), candidate)) {
13927
14105
  return {
13928
14106
  ok: false,
@@ -13932,7 +14110,7 @@ function blockCandidate(dir, candidate, text) {
13932
14110
  writeCandidate(dir, { ...candidate, state: "blocked" });
13933
14111
  return {
13934
14112
  ok: false,
13935
- text: text.includes("\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C:") ? text : `${text}
14113
+ text: text2.includes("\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C:") ? text2 : `${text2}
13936
14114
  \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0438\u0441\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u0443\u043A\u0430\u0437\u0430\u043D\u043D\u0443\u044E \u043F\u0440\u0438\u0447\u0438\u043D\u0443 \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup.`
13937
14115
  };
13938
14116
  }
@@ -13986,9 +14164,9 @@ async function prepareCopiedCandidateVenv(engine, dir, version2, place) {
13986
14164
  if (engine.prepareCandidateVenv !== void 0) {
13987
14165
  await engine.prepareCandidateVenv(place);
13988
14166
  } else {
13989
- const { execFile: execFile3 } = await import('child_process');
14167
+ const { execFile: execFile4 } = await import('child_process');
13990
14168
  const { promisify: promisify3 } = await import('util');
13991
- await promisify3(execFile3)(engine.bootstrapPython(), ["-m", "venv", "--copies", venv], {
14169
+ await promisify3(execFile4)(engine.bootstrapPython(), ["-m", "venv", "--copies", venv], {
13992
14170
  timeout: 10 * 6e4,
13993
14171
  encoding: "utf8",
13994
14172
  env: { ...engine.pythonEnv(), PYTHONDONTWRITEBYTECODE: "1" }
@@ -14004,7 +14182,7 @@ async function prepareCopiedCandidateVenv(engine, dir, version2, place) {
14004
14182
  }
14005
14183
  async function candidatePipReady(engine, python) {
14006
14184
  if (engine.candidatePipReady !== void 0) return engine.candidatePipReady(python);
14007
- const { execFile: execFile3 } = await import('child_process');
14185
+ const { execFile: execFile4 } = await import('child_process');
14008
14186
  const { promisify: promisify3 } = await import('util');
14009
14187
  const env = { ...engine.pythonEnv(), PYTHONDONTWRITEBYTECODE: "1" };
14010
14188
  delete env["PYTHONHOME"];
@@ -14012,7 +14190,7 @@ async function candidatePipReady(engine, python) {
14012
14190
  delete env["VIRTUAL_ENV"];
14013
14191
  delete env["__PYVENV_LAUNCHER__"];
14014
14192
  try {
14015
- await promisify3(execFile3)(python, ["-B", "-m", "pip", "--version"], {
14193
+ await promisify3(execFile4)(python, ["-B", "-m", "pip", "--version"], {
14016
14194
  timeout: 6e4,
14017
14195
  encoding: "utf8",
14018
14196
  env
@@ -14324,9 +14502,9 @@ async function dryRun(engine, build, expected, trial) {
14324
14502
  }
14325
14503
  }
14326
14504
  async function probe(engine, consolePy) {
14327
- const { execFile: execFile3 } = await import('child_process');
14505
+ const { execFile: execFile4 } = await import('child_process');
14328
14506
  const { promisify: promisify3 } = await import('util');
14329
- return promisify3(execFile3)(engine.enginePython(), [consolePy, "version"], {
14507
+ return promisify3(execFile4)(engine.enginePython(), [consolePy, "version"], {
14330
14508
  timeout: 6e4,
14331
14509
  encoding: "utf8",
14332
14510
  env: engine.pythonEnv()
@@ -14508,8 +14686,8 @@ function fieldLine(header, start, length) {
14508
14686
  return chunk.subarray(0, end === -1 ? chunk.length : end).toString("utf8");
14509
14687
  }
14510
14688
  function octal(header, start, length) {
14511
- const text = fieldLine(header, start, length).trim();
14512
- const ch = Number.parseInt(text, 8);
14689
+ const text2 = fieldLine(header, start, length).trim();
14690
+ const ch = Number.parseInt(text2, 8);
14513
14691
  return Number.isFinite(ch) ? ch : 0;
14514
14692
  }
14515
14693
  function fieldSize(header, start, length) {
@@ -14769,7 +14947,7 @@ var probeShell = async (entry) => {
14769
14947
  encoding: "utf8",
14770
14948
  env
14771
14949
  });
14772
- await new Promise((resolve4, reject) => {
14950
+ await new Promise((resolve5, reject) => {
14773
14951
  const child = spawn(process.execPath, [entry], { stdio: ["pipe", "pipe", "pipe"], env });
14774
14952
  let output = "";
14775
14953
  let errors = "";
@@ -14780,7 +14958,7 @@ var probeShell = async (entry) => {
14780
14958
  finished = true;
14781
14959
  clearTimeout(timer);
14782
14960
  child.kill();
14783
- if (trouble === void 0) resolve4();
14961
+ if (trouble === void 0) resolve5();
14784
14962
  else reject(trouble);
14785
14963
  };
14786
14964
  const send = (message) => {
@@ -15116,10 +15294,10 @@ function recoverInterruptedPublication(dir) {
15116
15294
  \u041E\u043D\u0430 \u0435\u0449\u0451 \u043D\u0435 \u0440\u0430\u0431\u043E\u0447\u0430\u044F: setup \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442 \u044D\u0442\u043E\u0442 \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442 \u0438 \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u0441\u043E\u0431\u0435\u0440\u0451\u0442 \u0435\u0433\u043E \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435.`
15117
15295
  };
15118
15296
  }
15119
- function platformKey(os = process.platform, arch = process.arch) {
15120
- if (os === "darwin") return arch === "arm64" ? "Darwin-arm64" : arch === "x64" ? "Darwin-x86_64" : null;
15121
- if (os === "win32") return arch === "x64" ? "Windows-AMD64" : null;
15122
- if (os === "linux") return arch === "x64" ? "Linux-x86_64" : arch === "arm64" ? "Linux-aarch64" : null;
15297
+ function platformKey(os = process.platform, arch2 = process.arch) {
15298
+ if (os === "darwin") return arch2 === "arm64" ? "Darwin-arm64" : arch2 === "x64" ? "Darwin-x86_64" : null;
15299
+ if (os === "win32") return arch2 === "x64" ? "Windows-AMD64" : null;
15300
+ if (os === "linux") return arch2 === "x64" ? "Linux-x86_64" : arch2 === "arm64" ? "Linux-aarch64" : null;
15123
15301
  return null;
15124
15302
  }
15125
15303
  function pythonSource(engineDir, key = platformKey()) {
@@ -15382,9 +15560,276 @@ function safePythonDirectory(home, pythonDir) {
15382
15560
  }
15383
15561
  }
15384
15562
  function troubleText(what, trouble) {
15385
- const tail = trouble instanceof DeliveryError ? trouble.message : trouble instanceof Error ? trouble.message : String(trouble);
15563
+ const tail2 = trouble instanceof DeliveryError ? trouble.message : trouble instanceof Error ? trouble.message : String(trouble);
15386
15564
  return `${what}, \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043C\u0435\u043D\u044F\u043B.
15387
- ${tail}` + (tail.includes("\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C:") ? "" : "\n\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0438\u0441\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u0443\u043A\u0430\u0437\u0430\u043D\u043D\u0443\u044E \u043F\u0440\u0438\u0447\u0438\u043D\u0443 \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup.");
15565
+ ${tail2}` + (tail2.includes("\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C:") ? "" : "\n\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0438\u0441\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u0443\u043A\u0430\u0437\u0430\u043D\u043D\u0443\u044E \u043F\u0440\u0438\u0447\u0438\u043D\u0443 \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup.");
15566
+ }
15567
+
15568
+ // src/progress.ts
15569
+ var realPace = {
15570
+ now: () => Date.now(),
15571
+ rest: (ms) => new Promise((done) => {
15572
+ setTimeout(done, ms);
15573
+ })
15574
+ };
15575
+ var WAIT_LIMIT_SECONDS = 55;
15576
+ var POLL_EVERY_MS = 2e3;
15577
+ var JOB_STILL_COMPUTING = "\u0415\u0449\u0451 \u0441\u0447\u0438\u0442\u0430\u0435\u0442";
15578
+ var JOB_IN_QUEUE = "\u0416\u0434\u0451\u0442 \u043E\u0447\u0435\u0440\u0435\u0434\u0438";
15579
+ function stillWorking(text2) {
15580
+ return text2.includes(JOB_STILL_COMPUTING) || text2.includes(JOB_IN_QUEUE);
15581
+ }
15582
+ function waitSeconds(asked) {
15583
+ if (asked === void 0 || !Number.isFinite(asked) || asked <= 0) return 0;
15584
+ return Math.min(Math.round(asked), WAIT_LIMIT_SECONDS);
15585
+ }
15586
+ function freshLines(text2, told) {
15587
+ const news = [];
15588
+ for (const row of text2.split("\n").slice(1)) {
15589
+ const line = row.trim();
15590
+ if (line.length === 0) continue;
15591
+ if (line.startsWith(JOB_STILL_COMPUTING)) continue;
15592
+ if (told.has(line)) continue;
15593
+ told.add(line);
15594
+ news.push(line);
15595
+ }
15596
+ return news;
15597
+ }
15598
+ async function followJob(opts) {
15599
+ const pace = opts.pace ?? realPace;
15600
+ const until = pace.now() + opts.seconds * 1e3;
15601
+ const told = /* @__PURE__ */ new Set();
15602
+ let state = await opts.ask();
15603
+ retell(state, told, opts.tell);
15604
+ while (state.ok && stillWorking(state.text) && pace.now() < until) {
15605
+ await pace.rest(POLL_EVERY_MS);
15606
+ try {
15607
+ state = await opts.ask();
15608
+ } catch {
15609
+ continue;
15610
+ }
15611
+ retell(state, told, opts.tell);
15612
+ }
15613
+ return state;
15614
+ }
15615
+ function retell(state, told, tell) {
15616
+ if (tell === void 0) return;
15617
+ for (const line of freshLines(state.text, told)) tell(line);
15618
+ }
15619
+ var STAGE_WORDS = {
15620
+ silence: "\u0443\u0431\u0435\u0440\u0443 \u043F\u0430\u0443\u0437\u044B",
15621
+ voice: "\u0432\u044B\u0440\u043E\u0432\u043D\u044F\u044E \u0433\u043E\u043B\u043E\u0441",
15622
+ speech: "\u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u044E \u0440\u0435\u0447\u044C",
15623
+ phrases: "\u0440\u0430\u0437\u043B\u043E\u0436\u0443 \u0440\u0435\u0447\u044C \u043D\u0430 \u0444\u0440\u0430\u0437\u044B",
15624
+ plan: "\u043F\u0440\u0438\u0434\u0443\u043C\u0430\u044E, \u0447\u0442\u043E \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0438 \u043A\u043E\u0433\u0434\u0430",
15625
+ world: "\u043D\u0430\u0440\u0438\u0441\u0443\u044E \u0444\u043E\u043D \u0437\u0430 \u0433\u0435\u0440\u043E\u0435\u043C",
15626
+ cutout: "\u043E\u0442\u0434\u0435\u043B\u044E \u0433\u0435\u0440\u043E\u044F \u043E\u0442 \u0444\u043E\u043D\u0430",
15627
+ "hero-on-world": "\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E \u0433\u0435\u0440\u043E\u044F \u043D\u0430 \u0444\u043E\u043D",
15628
+ shots: "\u0440\u0430\u0441\u0441\u0442\u0430\u0432\u043B\u044E \u043A\u0440\u0443\u043F\u043D\u043E\u0441\u0442\u0438 \u043F\u043E \u043F\u043B\u0430\u043D\u0443",
15629
+ screen: "\u0432\u0441\u0442\u0430\u0432\u043B\u044E \u0437\u0430\u043F\u0438\u0441\u044C \u044D\u043A\u0440\u0430\u043D\u0430",
15630
+ inserts: "\u043D\u0430\u0440\u0438\u0441\u0443\u044E \u0433\u0440\u0430\u0444\u0438\u043A\u0443 \u043F\u043E \u043F\u043B\u0430\u043D\u0443",
15631
+ subtitles: "\u043D\u0430\u0440\u0438\u0441\u0443\u044E \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044B",
15632
+ layers: "\u043F\u043E\u043B\u043E\u0436\u0443 \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0438 \u043F\u043E\u0432\u0435\u0440\u0445",
15633
+ accents: "\u0440\u0430\u0441\u0441\u0442\u0430\u0432\u043B\u044E \u0437\u0432\u0443\u043A\u043E\u0432\u044B\u0435 \u0430\u043A\u0446\u0435\u043D\u0442\u044B",
15634
+ mix: "\u0441\u0432\u0435\u0434\u0443 \u0437\u0432\u0443\u043A",
15635
+ intro: "\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0443 \u0432 \u043D\u0430\u0447\u0430\u043B\u043E"
15636
+ };
15637
+ var NEAR_STEPS = 3;
15638
+ function montageLadder(stages) {
15639
+ const words = stages.map((stage) => STAGE_WORDS[stage]).filter((word) => word !== void 0);
15640
+ if (words.length === 0) return [];
15641
+ const near = words.slice(0, NEAR_STEPS);
15642
+ const far = words.slice(NEAR_STEPS);
15643
+ const lines = [`\u0421\u043D\u0430\u0447\u0430\u043B\u0430 ${near.join(", \u043F\u043E\u0442\u043E\u043C ")}.`];
15644
+ if (far.length > 0) {
15645
+ lines.push(
15646
+ `\u0414\u0430\u043B\u044C\u0448\u0435: ${far.join(", ")}. \u0428\u0430\u0433\u043E\u0432 \u0432\u0441\u0435\u0433\u043E \u0434\u043E ${stages.length} \u2014 \u0447\u0442\u043E \u043D\u0435 \u043F\u043E\u043D\u0430\u0434\u043E\u0431\u0438\u0442\u0441\u044F, \u043F\u0440\u043E\u043F\u0443\u0449\u0443.`
15647
+ );
15648
+ }
15649
+ return lines;
15650
+ }
15651
+ var PAUSES_LADDER = [
15652
+ "\u0423\u0431\u0435\u0440\u0443 \u043C\u043E\u043B\u0447\u0430\u043D\u0438\u0435 \u0438 \u0437\u0430\u043F\u0438\u043D\u043A\u0438 \u2014 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0442\u0440\u043E\u0433\u0430\u044E.",
15653
+ "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u044B, \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0438 \u043F\u043E\u0432\u0435\u0440\u0445 \u0438 \u0437\u0432\u0443\u043A \u2014 \u044D\u0442\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u0430\u044F \u0440\u0430\u0431\u043E\u0442\u0430, \u0435\u0451 \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442."
15654
+ ];
15655
+ var PAYLOAD_LADDER = [
15656
+ "\u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u043F\u043E\u043B\u043E\u0436\u0443 \u0434\u0432\u0438\u0436\u043E\u043A \u043C\u043E\u043D\u0442\u0430\u0436\u0430 \u043E\u0434\u043D\u043E\u0439 \u043F\u0430\u043F\u043A\u043E\u0439 \u2014 \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044F.",
15657
+ "\u041F\u043E\u0442\u043E\u043C ffmpeg, python \u0438 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043C\u043E\u043D\u0442\u0430\u0436\u0430. \u0422\u044F\u0436\u0451\u043B\u043E\u0435 \u2014 \u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0447\u0438, \u0432\u044B\u0440\u0435\u0437 \u0433\u0435\u0440\u043E\u044F, \u0447\u0438\u0441\u0442\u043A\u0430 \u0433\u043E\u043B\u043E\u0441\u0430 \u2014 \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435."
15658
+ ];
15659
+ var INSTALL_WORDS = {
15660
+ ffmpeg: "\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E ffmpeg \u2014 \u0438\u043C \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044F \u0432\u0438\u0434\u0435\u043E",
15661
+ python: "\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E python",
15662
+ env: "\u0441\u043E\u0431\u0435\u0440\u0443 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043C\u043E\u043D\u0442\u0430\u0436\u0430",
15663
+ model: "\u0441\u043A\u0430\u0447\u0430\u044E \u043C\u043E\u0434\u0435\u043B\u044C \u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u0432\u0430\u043D\u0438\u044F \u0440\u0435\u0447\u0438 \u2014 \u043E\u043D\u0430 \u0438\u0437 \u0432\u0441\u0435\u0433\u043E \u0441\u0430\u043C\u0430\u044F \u0442\u044F\u0436\u0451\u043B\u0430\u044F",
15664
+ cutout: "\u0441\u043A\u0430\u0447\u0430\u044E \u0442\u043E, \u0447\u0435\u043C \u043E\u0442\u0434\u0435\u043B\u044F\u044E \u0433\u0435\u0440\u043E\u044F \u043E\u0442 \u0444\u043E\u043D\u0430",
15665
+ voice: "\u0441\u043A\u0430\u0447\u0430\u044E \u0442\u043E, \u0447\u0435\u043C \u0447\u0438\u0449\u0443 \u0433\u043E\u043B\u043E\u0441",
15666
+ browser: "\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E \u0431\u0440\u0430\u0443\u0437\u0435\u0440, \u043A\u043E\u0442\u043E\u0440\u044B\u043C \u0440\u0438\u0441\u0443\u044E \u0433\u0440\u0430\u0444\u0438\u043A\u0443"
15667
+ };
15668
+ function installLadder(step) {
15669
+ if (step === "all") {
15670
+ return [
15671
+ "\u041F\u0440\u043E\u0432\u0435\u0440\u044E, \u0447\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442, \u0438 \u0434\u043E\u0441\u0442\u0430\u0432\u043B\u044E \u043F\u043E \u043E\u0447\u0435\u0440\u0435\u0434\u0438.",
15672
+ "\u0422\u044F\u0436\u0451\u043B\u043E\u0435 \u0441\u044E\u0434\u0430 \u043D\u0435 \u0432\u0445\u043E\u0434\u0438\u0442: \u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0447\u0438 \u0438 \u0432\u044B\u0440\u0435\u0437 \u0433\u0435\u0440\u043E\u044F \u0441\u0442\u0430\u0432\u044F\u0442\u0441\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \xAB\u0434\u0430\xBB \u043E\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430."
15673
+ ];
15674
+ }
15675
+ const word = INSTALL_WORDS[step];
15676
+ if (word === void 0) return [];
15677
+ return [
15678
+ `\u0421\u0435\u0439\u0447\u0430\u0441 ${word}.`,
15679
+ "\u0418\u0434\u0451\u0442 \u043C\u0438\u043D\u0443\u0442\u0430\u043C\u0438. \u041E\u0431\u043E\u0440\u0432\u0451\u0442\u0441\u044F \u2014 \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u0442\u043E\u0442 \u0436\u0435 \u0448\u0430\u0433: \u0443\u0436\u0435 \u0441\u043A\u0430\u0447\u0430\u043D\u043D\u043E\u0435 \u043D\u0435 \u043F\u0440\u043E\u043F\u0430\u0434\u0451\u0442."
15680
+ ];
15681
+ }
15682
+ var CALLS_FILE = "calls.jsonl";
15683
+ var CALLS_PREVIOUS = "calls.1.jsonl";
15684
+ var CALLS_CAP_BYTES = 1e6;
15685
+ function withCallLog(home, now = () => Date.now()) {
15686
+ return (tool) => {
15687
+ const original = tool.execute.bind(tool);
15688
+ return {
15689
+ ...tool,
15690
+ async execute(input, ...rest) {
15691
+ const started = now();
15692
+ const result = await original(input, ...rest);
15693
+ write(home, {
15694
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
15695
+ tool: tool.name,
15696
+ ok: result.type === "success",
15697
+ ms: now() - started
15698
+ });
15699
+ return result;
15700
+ }
15701
+ };
15702
+ };
15703
+ }
15704
+ function write(home, line) {
15705
+ try {
15706
+ mkdirSync(home, { recursive: true });
15707
+ const file2 = join(home, CALLS_FILE);
15708
+ rotate(file2, join(home, CALLS_PREVIOUS));
15709
+ appendFileSync(file2, `${JSON.stringify(line)}
15710
+ `, { encoding: "utf8", mode: 384 });
15711
+ } catch {
15712
+ }
15713
+ }
15714
+ function rotate(file2, previous) {
15715
+ try {
15716
+ if (statSync(file2).size >= CALLS_CAP_BYTES) renameSync(file2, previous);
15717
+ } catch {
15718
+ }
15719
+ }
15720
+
15721
+ // src/contracts/crash-report.ts
15722
+ var CRASH_REPORT_PATH = "/crash-report";
15723
+ var CRASH_CONTRACT = "naparnik-crash/1";
15724
+ var CRASH_LIMITS = {
15725
+ bodyBytes: 65536,
15726
+ errorType: 120,
15727
+ message: 500,
15728
+ trace: 8e3,
15729
+ stage: 120,
15730
+ osPlatform: 32,
15731
+ osRelease: 64,
15732
+ osArch: 16,
15733
+ hostName: 64,
15734
+ hostVersion: 32,
15735
+ shellVersion: 32,
15736
+ engineVersion: 64,
15737
+ pythonVersion: 16,
15738
+ ffmpegVersion: 64,
15739
+ recentCalls: 10,
15740
+ repeats: 1e5
15741
+ };
15742
+ var CRASH_TOOL_NAME = /^[a-z_]{1,64}$/;
15743
+ var CRASH_FINGERPRINT = /^[0-9a-f]{16}$/;
15744
+ var text = (max) => external_exports.string().max(max);
15745
+ var CrashReportRequest = external_exports.object({
15746
+ contract: external_exports.literal(CRASH_CONTRACT),
15747
+ occurred_at: external_exports.string().datetime({ offset: true }),
15748
+ where: external_exports.enum(["tool", "job", "process"]),
15749
+ tool: external_exports.string().regex(CRASH_TOOL_NAME).nullable(),
15750
+ stage: text(CRASH_LIMITS.stage).nullable(),
15751
+ error_type: external_exports.string().min(1).max(CRASH_LIMITS.errorType),
15752
+ message: text(CRASH_LIMITS.message),
15753
+ trace: text(CRASH_LIMITS.trace),
15754
+ fingerprint: external_exports.string().regex(CRASH_FINGERPRINT),
15755
+ repeats: external_exports.number().int().min(0).max(CRASH_LIMITS.repeats),
15756
+ os: external_exports.object({
15757
+ platform: text(CRASH_LIMITS.osPlatform),
15758
+ release: text(CRASH_LIMITS.osRelease),
15759
+ arch: text(CRASH_LIMITS.osArch)
15760
+ }).strict(),
15761
+ host: external_exports.object({
15762
+ name: text(CRASH_LIMITS.hostName),
15763
+ version: text(CRASH_LIMITS.hostVersion)
15764
+ }).strict().nullable(),
15765
+ versions: external_exports.object({
15766
+ shell: text(CRASH_LIMITS.shellVersion),
15767
+ engine: text(CRASH_LIMITS.engineVersion).nullable(),
15768
+ python: text(CRASH_LIMITS.pythonVersion).nullable(),
15769
+ ffmpeg: text(CRASH_LIMITS.ffmpegVersion).nullable()
15770
+ }).strict(),
15771
+ recent_calls: external_exports.array(external_exports.string().regex(CRASH_TOOL_NAME)).max(CRASH_LIMITS.recentCalls)
15772
+ }).strict();
15773
+ var SECRET_MIN = 4;
15774
+ var HUMAN_FILE = /[^\s"'«»“”/\\<>()]{0,255}\.(?:mp4|mov|m4v|mkv|avi|webm|mts|wav|mp3|m4a|aac|flac|ogg|jpg|jpeg|png|heic|gif|srt|vtt)\b/gi;
15775
+ var PATH = /(?:<naparnik>|<shell>|~|[A-Za-z]:)?(?:[\\/][^\s"'«»“”<>|,;()[\]{}\\/]+)+[\\/]?/g;
15776
+ var QUOTED = /(["'])([^"'\n]*)\1|«([^»\n]*)»|“([^”\n]*)”/g;
15777
+ var KEY = /npk_[A-Za-z0-9_-]{6,}/g;
15778
+ function scrub(text2, context) {
15779
+ let out = text2.replace(KEY, "<key>");
15780
+ for (const secret of secretForms(context.secrets)) {
15781
+ out = replaceAll(out, secret, "<video>");
15782
+ }
15783
+ out = replaceAll(out, context.shellDir, "<shell>");
15784
+ out = replaceAll(out, context.naparnikHome, "<naparnik>");
15785
+ out = replaceAll(out, context.home, "~");
15786
+ out = out.replace(/(?:\/Users|\/home|[A-Za-z]:\\Users|[A-Za-z]:\/Users)[\\/][^\s"'«»“”\\/]+/g, "~");
15787
+ out = out.replace(QUOTED, (whole, _q, plain, fir, curly) => {
15788
+ const inside2 = plain ?? fir ?? curly ?? "";
15789
+ if (!looksHuman(inside2)) return whole;
15790
+ const [open, close] = plain !== void 0 ? [_q, _q] : fir !== void 0 ? ["\xAB", "\xBB"] : ["\u201C", "\u201D"];
15791
+ return `${open}${looksLikePath(inside2) ? pathOrMark(inside2) : "<text>"}${close}`;
15792
+ });
15793
+ out = out.replace(PATH, (found) => pathOrMark(found));
15794
+ out = out.replace(HUMAN_FILE, "<file>");
15795
+ out = swallowSpacedTails(out);
15796
+ if (context.user.length >= 3) out = replaceAll(out, context.user, "<user>");
15797
+ return out;
15798
+ }
15799
+ function swallowSpacedTails(text2) {
15800
+ return text2.replace(/<(?:path|file)>[^\n:<>"'«»“”]{0,512}<(?:path|file)>/g, "<path>").replace(/(<(?:path|file)>)([^\n:<>"'«»“”]{0,512})/g, (whole, mark, tail2) => /[^\x00-\x7F]/.test(tail2) ? mark : whole);
15801
+ }
15802
+ function secretForms(secrets) {
15803
+ const forms = /* @__PURE__ */ new Set();
15804
+ for (const raw of secrets) {
15805
+ const value = raw.trim();
15806
+ if (value.length < SECRET_MIN) continue;
15807
+ forms.add(value);
15808
+ if (!looksLikePath(value)) continue;
15809
+ if (isAbsolute(value)) forms.add(resolve(value));
15810
+ const name = basename(value);
15811
+ const stem = name.slice(0, name.length - extname(name).length);
15812
+ for (const one of [name, stem, `${stem}.montage`]) {
15813
+ if (one.length >= SECRET_MIN) forms.add(one);
15814
+ }
15815
+ }
15816
+ return [...forms].sort((a, b) => b.length - a.length);
15817
+ }
15818
+ function pathOrMark(found) {
15819
+ const flat = found.replace(/\\/g, "/");
15820
+ const ours = /^(?:<naparnik>|~\/\.naparnik)\/(?:engine|shell|bin)(?:\/|$)/.test(flat) || flat.startsWith("<shell>") || /\/(?:site-packages|dist-packages|node_modules)\//.test(flat) || /\/lib\/python\d/i.test(flat) || /\/Python\d+\/Lib\//i.test(flat);
15821
+ return ours ? found : "<path>";
15822
+ }
15823
+ var HUMAN_FILE_ONE = new RegExp(HUMAN_FILE.source, "i");
15824
+ function looksLikePath(value) {
15825
+ return /[\\/]/.test(value) || HUMAN_FILE_ONE.test(value);
15826
+ }
15827
+ function looksHuman(inside2) {
15828
+ return /[^\x00-\x7F]/.test(inside2) || /\s/.test(inside2) || looksLikePath(inside2);
15829
+ }
15830
+ function replaceAll(text2, what, by) {
15831
+ if (what === "") return text2;
15832
+ return text2.split(what).join(by);
15388
15833
  }
15389
15834
 
15390
15835
  // src/job-report.ts
@@ -15431,14 +15876,14 @@ var PLAN_HEAD = /^План:$/;
15431
15876
  var PLAN_ROW = /^· (.+)$/;
15432
15877
  var STYLE = /^Стиль: /;
15433
15878
  var LEFT_TAIL = /\.\s+(Осталось .+)$/;
15434
- function readJob(text) {
15435
- const lines = text.split("\n").map((row) => row.trimEnd());
15436
- const head = HEAD.exec(lines[0]?.trim() ?? "");
15437
- if (head === null) return null;
15879
+ function readJob(text2) {
15880
+ const lines = text2.split("\n").map((row) => row.trimEnd());
15881
+ const head2 = HEAD.exec(lines[0]?.trim() ?? "");
15882
+ if (head2 === null) return null;
15438
15883
  const job = empty();
15439
- job.id = head[1] ?? "";
15440
- job.what = (head[2] ?? "").trim();
15441
- let tail = null;
15884
+ job.id = head2[1] ?? "";
15885
+ job.what = (head2[2] ?? "").trim();
15886
+ let tail2 = null;
15442
15887
  for (const row of lines.slice(1)) {
15443
15888
  const line = row.trim();
15444
15889
  if (line === "") continue;
@@ -15446,17 +15891,17 @@ function readJob(text) {
15446
15891
  const complaint = COMPLAINT.exec(line);
15447
15892
  if (complaint !== null) {
15448
15893
  job.complaints.push(complaint[1] ?? "");
15449
- tail = null;
15894
+ tail2 = null;
15450
15895
  continue;
15451
15896
  }
15452
15897
  if (PLAN_HEAD.test(line)) {
15453
- tail = null;
15898
+ tail2 = null;
15454
15899
  continue;
15455
15900
  }
15456
15901
  const planRow = PLAN_ROW.exec(line);
15457
15902
  if (planRow !== null) {
15458
15903
  job.plan.push(planRow[1] ?? "");
15459
- tail = null;
15904
+ tail2 = null;
15460
15905
  continue;
15461
15906
  }
15462
15907
  const behind = BEHIND.exec(line);
@@ -15487,13 +15932,13 @@ function readJob(text) {
15487
15932
  if (done !== null) {
15488
15933
  job.status = "done";
15489
15934
  job.sec = seconds(done[1]);
15490
- tail = [];
15935
+ tail2 = [];
15491
15936
  continue;
15492
15937
  }
15493
15938
  const file2 = FILE.exec(line);
15494
15939
  if (file2 !== null && job.status === "done") {
15495
15940
  job.file = file2[1] ?? "";
15496
- tail = null;
15941
+ tail2 = null;
15497
15942
  continue;
15498
15943
  }
15499
15944
  const failed = FAILED.exec(line);
@@ -15501,20 +15946,20 @@ function readJob(text) {
15501
15946
  job.status = "error";
15502
15947
  job.sec = seconds(failed[1]);
15503
15948
  job.trouble = failed[2] ?? "";
15504
- tail = null;
15949
+ tail2 = null;
15505
15950
  continue;
15506
15951
  }
15507
15952
  if (ABORTED.test(line)) {
15508
15953
  job.status = "aborted";
15509
15954
  job.trouble = line;
15510
15955
  job.sec = seconds(ABORTED_SEC.exec(line)?.[1]);
15511
- tail = null;
15956
+ tail2 = null;
15512
15957
  continue;
15513
15958
  }
15514
15959
  const advice = ADVICE.exec(line);
15515
15960
  if (advice !== null) {
15516
15961
  job.advice = advice[1] ?? "";
15517
- tail = null;
15962
+ tail2 = null;
15518
15963
  continue;
15519
15964
  }
15520
15965
  if (WAITING.test(line)) {
@@ -15528,52 +15973,530 @@ function readJob(text) {
15528
15973
  job.sec = seconds(running[1]);
15529
15974
  continue;
15530
15975
  }
15531
- if (tail !== null) {
15532
- tail.push(line);
15533
- job.outcome = tail.join("\n");
15976
+ if (tail2 !== null) {
15977
+ tail2.push(line);
15978
+ job.outcome = tail2.join("\n");
15534
15979
  continue;
15535
15980
  }
15536
15981
  if (job.status === "running" || job.status === "waiting") job.message = line;
15537
15982
  }
15538
15983
  return job;
15539
15984
  }
15540
- function seconds(text) {
15541
- if (text === void 0) return 0;
15542
- const value = Number(text.replace(",", "."));
15985
+ function seconds(text2) {
15986
+ if (text2 === void 0) return 0;
15987
+ const value = Number(text2.replace(",", "."));
15543
15988
  return Number.isFinite(value) ? value : 0;
15544
15989
  }
15545
- var WIDGET_MIME = "text/html;profile=mcp-app";
15546
- function widgetStamp(html) {
15547
- return createHash("sha256").update(html).digest("hex").slice(0, 8);
15548
- }
15549
- var WIDGET_PLATE_CSS = `
15550
- :root { --plate: #ffffff; --plate-end: #f5f5f7; }
15551
- :root:has(> body.dark) { --plate: #1d1d1f; --plate-end: #000000; }
15552
- :root:has(> body.full) { min-height: 100%; }
15553
- html {
15554
- background: var(--plate-end) linear-gradient(var(--plate) calc(100% - 197px), var(--plate-end)) no-repeat;
15990
+
15991
+ // src/crash-report.ts
15992
+ var RESEND_AFTER_MS = 30 * 6e4;
15993
+ var QUEUE_CAP = 20;
15994
+ var FLUSH_PER_PASS = 5;
15995
+ var FLUSH_EVERY_MS = 6e4;
15996
+ var SEND_TIMEOUT_MS = 3e3;
15997
+ var JOB_LOG_TAIL = 64 * 1024;
15998
+ var PYTHON_TRACEBACK = "Traceback (most recent call last):";
15999
+ var CrashReporter = class {
16000
+ #deps;
16001
+ #now;
16002
+ #inflight = /* @__PURE__ */ new Set();
16003
+ #lastFlush = Number.NEGATIVE_INFINITY;
16004
+ #host = null;
16005
+ /**
16006
+ * Строки из аргументов недавних вызовов — только в памяти процесса.
16007
+ *
16008
+ * ⚠ ЗАЧЕМ. Задание упало — отчёт о нём приходит на `wait`, а в аргументах
16009
+ * `wait` лишь номер задания; путь к ролику был в аргументах `render`,
16010
+ * который задание запустил. Без этой памяти путь с пробелами из трейса ffmpeg
16011
+ * вычищался бы только общими правилами, а они его режут на куски (ревью
16012
+ * безопасности 2026-09-24). На диск не пишется: это ровно то, что мы
16013
+ * обещали не хранить.
16014
+ */
16015
+ #recentSecrets = [];
16016
+ #ffmpeg = null;
16017
+ constructor(deps) {
16018
+ this.#deps = deps;
16019
+ this.#now = deps.now ?? (() => Date.now());
15555
16020
  }
15556
- `;
15557
- var WIDGET_HEADING_CSS = `
15558
- :root {
15559
- --font-display: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Icons", "Helvetica Neue", Helvetica, Arial, sans-serif;
16021
+ get canSend() {
16022
+ return this.#deps.canSend;
15560
16023
  }
15561
- h2, .end__t { font-family: var(--font-display); }
15562
- `;
15563
-
15564
- // src/runbar.ts
15565
- var RUNBAR_RESOURCE_URI = `ui://naparnik/runbar-${widgetStamp(runbarWidgetHtml())}`;
15566
- var RUNBAR_MIME = WIDGET_MIME;
15567
- function runbarResource() {
15568
- return {
15569
- resource: {
15570
- uri: RUNBAR_RESOURCE_URI,
15571
- name: "\u0425\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u044B",
15572
- description: "\u041F\u043E\u043B\u043E\u0441\u0430 \u044D\u0442\u0430\u043F\u043E\u0432: \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0435\u0442\u0441\u044F \u0441\u0435\u0439\u0447\u0430\u0441 \u0438 \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0443\u0436\u0435 \u0438\u0434\u0451\u0442",
15573
- mimeType: RUNBAR_MIME
15574
- },
15575
- contents: [{
15576
- uri: RUNBAR_RESOURCE_URI,
16024
+ get home() {
16025
+ return this.#deps.home;
16026
+ }
16027
+ /** Запомнить программу-хост: при падении всего процесса контекста вызова нет. */
16028
+ /** Запомнить строки аргументов вызова — чтобы вычеркнуть их из будущих отчётов. */
16029
+ noteSecrets(secrets) {
16030
+ const fresh = secrets.filter((one) => one.length >= 4);
16031
+ if (fresh.length === 0) return;
16032
+ this.#recentSecrets = [...this.#recentSecrets.filter((one) => !fresh.includes(one)), ...fresh].slice(-50);
16033
+ }
16034
+ noteHost(host) {
16035
+ if (host !== null) this.#host = host;
16036
+ }
16037
+ /** Отправить отчёт мимо ответа. Для тестов и смоука — `settled()`. */
16038
+ submit(crash) {
16039
+ this.#track(this.#deliver(crash));
16040
+ }
16041
+ /**
16042
+ * Процесс падает: отчёт кладётся на диск СИНХРОННО и уходит на следующем
16043
+ * запуске. Сеть здесь не трогаем — процессу осталось жить миллисекунды, и
16044
+ * недоставленный наполовину запрос хуже файла, который точно дойдёт.
16045
+ */
16046
+ persist(crash) {
16047
+ try {
16048
+ const report = this.#build(crash);
16049
+ if (report !== null) this.#enqueue(report);
16050
+ } catch {
16051
+ }
16052
+ }
16053
+ /** Досылка очереди — не чаще раза в минуту. */
16054
+ flushSoon() {
16055
+ const now = this.#now();
16056
+ if (now - this.#lastFlush < FLUSH_EVERY_MS) return;
16057
+ this.#lastFlush = now;
16058
+ this.#track(this.#flush());
16059
+ }
16060
+ /** Дождаться всего, что ушло мимо ответа. Боевому пути не нужно. */
16061
+ async settled() {
16062
+ while (this.#inflight.size > 0) await Promise.allSettled([...this.#inflight]);
16063
+ }
16064
+ /** Задание уже разобрано: отчёт о нём ушёл раньше, в этом или прошлом запуске. */
16065
+ jobSeen(id) {
16066
+ return this.#state().jobs.includes(id);
16067
+ }
16068
+ rememberJob(id) {
16069
+ const state = this.#state();
16070
+ state.jobs = [...state.jobs.filter((one) => one !== id), id].slice(-50);
16071
+ this.#save(state);
16072
+ }
16073
+ scrubContext(secrets) {
16074
+ return {
16075
+ home: this.#deps.osHome ?? homedir(),
16076
+ user: this.#deps.osUser ?? safeUser(),
16077
+ naparnikHome: this.#deps.home,
16078
+ shellDir: this.#deps.shellDir ?? ownDir(),
16079
+ secrets
16080
+ };
16081
+ }
16082
+ #track(work) {
16083
+ const guarded = work.catch(() => void 0);
16084
+ this.#inflight.add(guarded);
16085
+ void guarded.finally(() => this.#inflight.delete(guarded));
16086
+ }
16087
+ async #deliver(crash) {
16088
+ const report = this.#build(crash);
16089
+ if (report === null) return;
16090
+ report.versions.ffmpeg = await this.#ffmpegVersion();
16091
+ const verdict = await this.#send(report);
16092
+ if (verdict === "retry") this.#enqueue(report);
16093
+ }
16094
+ async #flush() {
16095
+ const dir = this.#queueDir();
16096
+ let files;
16097
+ try {
16098
+ files = readdirSync(dir).filter((f) => f.endsWith(".json")).sort();
16099
+ } catch {
16100
+ return;
16101
+ }
16102
+ for (const file2 of files.slice(0, FLUSH_PER_PASS)) {
16103
+ const path2 = join(dir, file2);
16104
+ let report;
16105
+ try {
16106
+ report = JSON.parse(readFileSync(path2, "utf8"));
16107
+ } catch {
16108
+ rmSync(path2, { force: true });
16109
+ continue;
16110
+ }
16111
+ const verdict = await this.#send(report);
16112
+ if (verdict === "retry") return;
16113
+ rmSync(path2, { force: true });
16114
+ }
16115
+ }
16116
+ /**
16117
+ * Отправить и решить, что делать с отчётом дальше.
16118
+ *
16119
+ * ⚠ В ОЧЕРЕДЬ ИДЁТ ТОЛЬКО ТО, ЧТО ПОВТОР ИСПРАВИТ: нет сети, таймаут, 5xx,
16120
+ * 429 и «ключа нет» (человек впишет его завтра). 400 и 413 — отчёт кривой,
16121
+ * 401 и 403 — ключ не наш: повтор даст тот же отказ, а очередь с потолком
16122
+ * вытесняла бы им живые отчёты.
16123
+ */
16124
+ async #send(report) {
16125
+ if (!CrashReportRequest.safeParse(report).success) return "drop";
16126
+ try {
16127
+ await this.#deps.client.postMontage(CRASH_REPORT_PATH, report, {
16128
+ once: true,
16129
+ timeoutMs: this.#deps.timeoutMs ?? SEND_TIMEOUT_MS
16130
+ });
16131
+ return "sent";
16132
+ } catch (trouble) {
16133
+ if (trouble instanceof NaparnikAuthError || trouble instanceof NaparnikScopeError) return "drop";
16134
+ if (trouble instanceof NaparnikClientError) return trouble.status === 429 ? "retry" : "drop";
16135
+ return "retry";
16136
+ }
16137
+ }
16138
+ /**
16139
+ * Собрать отчёт: обезличить, обрезать, посчитать отпечаток и решить по
16140
+ * частоте. `null` — этот отпечаток уже уходил меньше получаса назад, и
16141
+ * повтор записан в счётчик.
16142
+ */
16143
+ #build(crash) {
16144
+ const fingerprint = fingerprintOf2(crash.errorType, crash.frames);
16145
+ const state = this.#state();
16146
+ const now = this.#now();
16147
+ const seen = state.fingerprints[fingerprint];
16148
+ if (seen !== void 0 && now - seen.sentAt < RESEND_AFTER_MS) {
16149
+ seen.pending += 1;
16150
+ this.#save(state);
16151
+ return null;
16152
+ }
16153
+ const repeats = Math.min(seen?.pending ?? 0, CRASH_LIMITS.repeats);
16154
+ state.fingerprints[fingerprint] = { sentAt: now, pending: 0 };
16155
+ prune(state, now);
16156
+ this.#save(state);
16157
+ const context = this.scrubContext([...crash.secrets, ...this.#recentSecrets]);
16158
+ const clean = (text2) => scrub(text2, context);
16159
+ return {
16160
+ contract: CRASH_CONTRACT,
16161
+ occurred_at: new Date(now).toISOString(),
16162
+ where: crash.where,
16163
+ tool: crash.tool !== null && CRASH_TOOL_NAME.test(crash.tool) ? crash.tool : null,
16164
+ // ⚠ РЕЖЕМ ДО ОБЕЗЛИЧИВАНИЯ С ЗАПАСОМ И ЕЩЁ РАЗ ПОСЛЕ. Обезличивание идёт
16165
+ // синхронно, до ответа инструмента, а stderr движка бывает мегабайтами:
16166
+ // без первой резки длинный текст подвешивал бы ответ на секунды (ревью
16167
+ // безопасности 2026-09-24). Запас — вдвое: метки короче того, что заменяют.
16168
+ stage: crash.stage === null ? null : head(clean(head(crash.stage, 2 * CRASH_LIMITS.stage)), CRASH_LIMITS.stage),
16169
+ error_type: head(clean(head(crash.errorType, 2 * CRASH_LIMITS.errorType)), CRASH_LIMITS.errorType) || "Error",
16170
+ message: head(clean(head(crash.message, 2 * CRASH_LIMITS.message)), CRASH_LIMITS.message),
16171
+ trace: crash.keep === "tail" ? tail(clean(crash.trace.slice(-2 * CRASH_LIMITS.trace)), CRASH_LIMITS.trace) : head(clean(head(crash.trace, 2 * CRASH_LIMITS.trace)), CRASH_LIMITS.trace),
16172
+ fingerprint,
16173
+ repeats,
16174
+ os: {
16175
+ platform: head(platform(), CRASH_LIMITS.osPlatform),
16176
+ release: head(release(), CRASH_LIMITS.osRelease),
16177
+ arch: head(arch(), CRASH_LIMITS.osArch)
16178
+ },
16179
+ host: hostOf(crash.host ?? this.#host),
16180
+ versions: {
16181
+ shell: head(VERSION, CRASH_LIMITS.shellVersion),
16182
+ engine: orNull(this.#deps.engineVersion(), CRASH_LIMITS.engineVersion),
16183
+ python: orNull(crash.python, CRASH_LIMITS.pythonVersion),
16184
+ ffmpeg: null
16185
+ },
16186
+ recent_calls: recentCalls(this.#deps.home)
16187
+ };
16188
+ }
16189
+ #enqueue(report) {
16190
+ try {
16191
+ const dir = this.#queueDir();
16192
+ mkdirSync(dir, { recursive: true });
16193
+ const name = `${String(this.#now()).padStart(15, "0")}-${report.fingerprint}.json`;
16194
+ const temporary = join(dir, `${name}.tmp`);
16195
+ writeFileSync(temporary, JSON.stringify(report), { mode: 384 });
16196
+ renameSync(temporary, join(dir, name));
16197
+ const files = readdirSync(dir).filter((f) => f.endsWith(".json")).sort();
16198
+ for (const old of files.slice(0, Math.max(0, files.length - QUEUE_CAP))) {
16199
+ rmSync(join(dir, old), { force: true });
16200
+ }
16201
+ } catch {
16202
+ }
16203
+ }
16204
+ #queueDir() {
16205
+ return join(this.#deps.home, "crash-reports", "queue");
16206
+ }
16207
+ #stateFile() {
16208
+ return join(this.#deps.home, "crash-reports", "seen.json");
16209
+ }
16210
+ #state() {
16211
+ try {
16212
+ const parsed = JSON.parse(readFileSync(this.#stateFile(), "utf8"));
16213
+ return {
16214
+ fingerprints: typeof parsed.fingerprints === "object" && parsed.fingerprints !== null ? parsed.fingerprints : {},
16215
+ jobs: Array.isArray(parsed.jobs) ? parsed.jobs.filter((j) => typeof j === "string") : []
16216
+ };
16217
+ } catch {
16218
+ return { fingerprints: {}, jobs: [] };
16219
+ }
16220
+ }
16221
+ #save(state) {
16222
+ try {
16223
+ mkdirSync(dirname(this.#stateFile()), { recursive: true });
16224
+ writeFileSync(this.#stateFile(), JSON.stringify(state), { mode: 384 });
16225
+ } catch {
16226
+ }
16227
+ }
16228
+ #ffmpegVersion() {
16229
+ this.#ffmpeg ??= (this.#deps.ffmpegVersion ?? (() => probeFfmpeg(this.#deps.home)))().catch(() => null);
16230
+ return this.#ffmpeg.then((v) => orNull(v, CRASH_LIMITS.ffmpegVersion));
16231
+ }
16232
+ };
16233
+ function prune(state, now) {
16234
+ for (const [key, value] of Object.entries(state.fingerprints)) {
16235
+ if (now - value.sentAt > 24 * 36e5 && value.pending === 0) delete state.fingerprints[key];
16236
+ }
16237
+ }
16238
+ function withCrashReport(crashes) {
16239
+ return (tool) => {
16240
+ if (crashes === null) return tool;
16241
+ const original = tool.execute.bind(tool);
16242
+ return {
16243
+ ...tool,
16244
+ async execute(input, ...rest) {
16245
+ crashes.flushSoon();
16246
+ const host = hostFromContext(rest[0]);
16247
+ crashes.noteHost(host);
16248
+ crashes.noteSecrets(secretsOf(input));
16249
+ let result;
16250
+ try {
16251
+ result = await original(input, ...rest);
16252
+ } catch (trouble) {
16253
+ const crash = fromThrown(trouble, "tool", tool.name, input, host);
16254
+ crashes.submit(crash);
16255
+ return toolError(brokenWords(crash, crashes));
16256
+ }
16257
+ return caught(result, tool.name, input, host, crashes);
16258
+ }
16259
+ };
16260
+ };
16261
+ }
16262
+ function caught(result, name, input, host, crashes) {
16263
+ if (result.type === "error") {
16264
+ const parsed2 = pythonCrash(result.error);
16265
+ if (parsed2 === null) return result;
16266
+ const crash2 = { ...parsed2, where: "tool", tool: name, stage: null, secrets: secretsOf(input), host };
16267
+ crashes.submit(crash2);
16268
+ return toolError(brokenWords(crash2, crashes));
16269
+ }
16270
+ const job = result.structured;
16271
+ if (job === void 0 || job["contract"] !== JOB_CONTRACT || job["status"] !== "error") return result;
16272
+ const id = typeof job["id"] === "string" ? job["id"] : "";
16273
+ if (!/^[\w.-]{1,80}$/.test(id) || crashes.jobSeen(id)) return result;
16274
+ const parsed = pythonCrash(jobLogTail(crashes.home, id));
16275
+ if (parsed === null) return result;
16276
+ crashes.rememberJob(id);
16277
+ const stage = typeof job["now"] === "string" && job["now"] !== "" ? job["now"] : null;
16278
+ const crash = { ...parsed, where: "job", tool: name, stage, secrets: secretsOf(input), host };
16279
+ crashes.submit(crash);
16280
+ const notice = `
16281
+
16282
+ ${brokenWords(crash, crashes)}`;
16283
+ return typeof result.content === "string" ? { ...result, content: result.content + notice } : { ...result, content: [...result.content, { type: "text", text: notice.trim() }] };
16284
+ }
16285
+ function brokenWords(crash, crashes) {
16286
+ const essence = head(scrub(head(crash.essence, 600), crashes.scrubContext(crash.secrets)), 300);
16287
+ return [
16288
+ "\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.",
16289
+ 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}.`,
16290
+ `\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}`
16291
+ ].join("\n");
16292
+ }
16293
+ function watchProcessCrashes(crashes, proc = process, exit = (code) => process.exit(code)) {
16294
+ const fall = (trouble) => {
16295
+ crashes.persist(fromThrown(trouble, "process", null, {}, null));
16296
+ try {
16297
+ process.stderr.write(`naparnik-mcp: \u0443\u043F\u0430\u043B \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u2014 ${trouble instanceof Error ? trouble.stack ?? trouble.message : String(trouble)}
16298
+ `);
16299
+ } catch {
16300
+ }
16301
+ exit(1);
16302
+ };
16303
+ proc.on("uncaughtException", fall);
16304
+ proc.on("unhandledRejection", fall);
16305
+ crashes.flushSoon();
16306
+ }
16307
+ function fromThrown(trouble, where, tool, input, host) {
16308
+ const error40 = trouble instanceof Error ? trouble : new Error(String(trouble));
16309
+ const errorType = error40.name || error40.constructor.name || "Error";
16310
+ const trace = error40.stack ?? `${errorType}: ${error40.message}`;
16311
+ const frames = jsFrames(trace);
16312
+ return {
16313
+ where,
16314
+ tool,
16315
+ stage: null,
16316
+ errorType,
16317
+ message: error40.message,
16318
+ trace,
16319
+ keep: "head",
16320
+ frames: frames.slice(0, 3).map((f) => `${f.file}:${f.func}`),
16321
+ python: null,
16322
+ secrets: secretsOf(input),
16323
+ host,
16324
+ essence: `${errorType}: ${error40.message}${frames[0] ? ` \u2014 ${frames[0].file}` : ""}`
16325
+ };
16326
+ }
16327
+ function jsFrames(stack) {
16328
+ const frames = [];
16329
+ for (const line of stack.split("\n")) {
16330
+ const m = /^\s*at (?:(.+?) \()?(.+?):\d+:\d+\)?\s*$/.exec(line);
16331
+ if (m === null) continue;
16332
+ const file2 = basename(m[2] ?? "").replace(/-[A-Za-z0-9]{6,}(\.[cm]?js)$/, "$1");
16333
+ frames.push({ file: file2, func: m[1] ?? "<anonymous>" });
16334
+ }
16335
+ return frames;
16336
+ }
16337
+ function pythonCrash(text2) {
16338
+ const start = text2.lastIndexOf(PYTHON_TRACEBACK);
16339
+ if (start < 0) return null;
16340
+ const block = text2.slice(start).replace(/\s+$/, "");
16341
+ const lines = block.split("\n");
16342
+ const frames = [];
16343
+ let lastFrame = -1;
16344
+ lines.forEach((line, at2) => {
16345
+ const m2 = /^\s*File "(.+)", line (\d+), in (.+)$/.exec(line);
16346
+ if (m2 === null) return;
16347
+ frames.push({ file: m2[1] ?? "", line: m2[2] ?? "", func: (m2[3] ?? "").trim() });
16348
+ lastFrame = at2;
16349
+ });
16350
+ if (frames.length === 0) return null;
16351
+ if (frames.some((f) => /[\\/]mine[\\/]steps[\\/]/.test(f.file))) return null;
16352
+ const at = lines.findIndex((line, n) => n > lastFrame && line !== "" && !/^\s/.test(line));
16353
+ if (at < 0) return null;
16354
+ const m = /^([A-Za-z_][\w.]*)(?::\s?(.*))?$/.exec(lines[at] ?? "");
16355
+ if (m === null) return null;
16356
+ const errorType = m[1] ?? "Error";
16357
+ if (/(?:^|\.)(?:KeyboardInterrupt|SystemExit|Explained|Refusal)$/.test(errorType)) return null;
16358
+ const message = [m[2] ?? "", ...lines.slice(at + 1)].join("\n").trim();
16359
+ const innermost = frames[frames.length - 1];
16360
+ const firstLine = message.split("\n")[0] ?? "";
16361
+ return {
16362
+ errorType,
16363
+ message,
16364
+ trace: block,
16365
+ keep: "tail",
16366
+ frames: frames.slice(-3).map((f) => `${basename(f.file.replace(/\\/g, "/"))}:${f.func}`),
16367
+ python: pythonVersionIn(frames.map((f) => f.file)),
16368
+ essence: `${errorType}${firstLine === "" ? "" : `: ${firstLine}`}` + (innermost ? ` \u2014 ${basename(innermost.file.replace(/\\/g, "/"))}:${innermost.line}` : "")
16369
+ };
16370
+ }
16371
+ function pythonVersionIn(files) {
16372
+ for (const file2 of files) {
16373
+ const m = /python(\d)\.?(\d{1,2})[\\/]/i.exec(file2);
16374
+ if (m !== null) return `${m[1]}.${m[2]}`;
16375
+ }
16376
+ return null;
16377
+ }
16378
+ function jobLogTail(home, id) {
16379
+ const file2 = join(home, "logs", `${id}.log`);
16380
+ let fd = null;
16381
+ try {
16382
+ fd = openSync(file2, "r");
16383
+ const size = fstatSync(fd).size;
16384
+ const length = Math.min(size, JOB_LOG_TAIL);
16385
+ const buffer = Buffer.alloc(length);
16386
+ readSync(fd, buffer, 0, length, size - length);
16387
+ return buffer.toString("utf8");
16388
+ } catch {
16389
+ return "";
16390
+ } finally {
16391
+ if (fd !== null) try {
16392
+ closeSync(fd);
16393
+ } catch {
16394
+ }
16395
+ }
16396
+ }
16397
+ function fingerprintOf2(errorType, frames) {
16398
+ return createHash("sha256").update([errorType, ...frames].join("|")).digest("hex").slice(0, 16);
16399
+ }
16400
+ function secretsOf(input, depth = 0, out = []) {
16401
+ if (out.length >= 50 || depth > 4) return out;
16402
+ if (typeof input === "string") out.push(input);
16403
+ else if (Array.isArray(input)) for (const one of input) secretsOf(one, depth + 1, out);
16404
+ else if (typeof input === "object" && input !== null) {
16405
+ for (const one of Object.values(input)) secretsOf(one, depth + 1, out);
16406
+ }
16407
+ return out;
16408
+ }
16409
+ function recentCalls(home) {
16410
+ try {
16411
+ const text2 = readFileSync(join(home, CALLS_FILE), "utf8").slice(-8192);
16412
+ const names = [];
16413
+ for (const line of text2.split("\n")) {
16414
+ try {
16415
+ const tool = JSON.parse(line).tool;
16416
+ if (typeof tool === "string" && CRASH_TOOL_NAME.test(tool)) names.push(tool);
16417
+ } catch {
16418
+ }
16419
+ }
16420
+ return names.slice(-CRASH_LIMITS.recentCalls);
16421
+ } catch {
16422
+ return [];
16423
+ }
16424
+ }
16425
+ function hostFromContext(context) {
16426
+ const host = context?.host;
16427
+ return host !== void 0 && typeof host.name === "string" && typeof host.version === "string" ? host : null;
16428
+ }
16429
+ function hostOf(host) {
16430
+ return host === null ? null : { name: head(host.name, CRASH_LIMITS.hostName), version: head(host.version, CRASH_LIMITS.hostVersion) };
16431
+ }
16432
+ function probeFfmpeg(home) {
16433
+ const own = join(home, "bin", platform() === "win32" ? "ffmpeg.exe" : "ffmpeg");
16434
+ const program = existsSync(own) ? own : "ffmpeg";
16435
+ return new Promise((done) => {
16436
+ execFile(program, ["-version"], { timeout: 3e3, encoding: "utf8" }, (trouble, stdout) => {
16437
+ if (trouble !== null) return done(null);
16438
+ const m = /^ffmpeg version (\S+)/.exec(stdout);
16439
+ done(m?.[1] ?? null);
16440
+ });
16441
+ });
16442
+ }
16443
+ function ownDir() {
16444
+ try {
16445
+ return dirname(fileURLToPath(import.meta.url));
16446
+ } catch {
16447
+ return "";
16448
+ }
16449
+ }
16450
+ function safeUser() {
16451
+ try {
16452
+ return userInfo().username;
16453
+ } catch {
16454
+ return "";
16455
+ }
16456
+ }
16457
+ function head(text2, max) {
16458
+ return text2.length <= max ? text2 : text2.slice(0, max);
16459
+ }
16460
+ function tail(text2, max) {
16461
+ if (text2.length <= max) return text2;
16462
+ const cut = "\u2026(\u043D\u0430\u0447\u0430\u043B\u043E \u043E\u0431\u0440\u0435\u0437\u0430\u043D\u043E)\n";
16463
+ return cut + text2.slice(text2.length - (max - cut.length));
16464
+ }
16465
+ function orNull(value, max) {
16466
+ return value === null || value === "" ? null : head(value, max);
16467
+ }
16468
+ var WIDGET_MIME = "text/html;profile=mcp-app";
16469
+ function widgetStamp(html) {
16470
+ return createHash("sha256").update(html).digest("hex").slice(0, 8);
16471
+ }
16472
+ var WIDGET_PLATE_CSS = `
16473
+ :root { --plate: #ffffff; --plate-end: #f5f5f7; }
16474
+ :root:has(> body.dark) { --plate: #1d1d1f; --plate-end: #000000; }
16475
+ :root:has(> body.full) { min-height: 100%; }
16476
+ html {
16477
+ background: var(--plate-end) linear-gradient(var(--plate) calc(100% - 197px), var(--plate-end)) no-repeat;
16478
+ }
16479
+ `;
16480
+ var WIDGET_HEADING_CSS = `
16481
+ :root {
16482
+ --font-display: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Icons", "Helvetica Neue", Helvetica, Arial, sans-serif;
16483
+ }
16484
+ h2, .end__t { font-family: var(--font-display); }
16485
+ `;
16486
+
16487
+ // src/runbar.ts
16488
+ var RUNBAR_RESOURCE_URI = `ui://naparnik/runbar-${widgetStamp(runbarWidgetHtml())}`;
16489
+ var RUNBAR_MIME = WIDGET_MIME;
16490
+ function runbarResource() {
16491
+ return {
16492
+ resource: {
16493
+ uri: RUNBAR_RESOURCE_URI,
16494
+ name: "\u0425\u043E\u0434 \u0440\u0430\u0431\u043E\u0442\u044B",
16495
+ description: "\u041F\u043E\u043B\u043E\u0441\u0430 \u044D\u0442\u0430\u043F\u043E\u0432: \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0435\u0442\u0441\u044F \u0441\u0435\u0439\u0447\u0430\u0441 \u0438 \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0443\u0436\u0435 \u0438\u0434\u0451\u0442",
16496
+ mimeType: RUNBAR_MIME
16497
+ },
16498
+ contents: [{
16499
+ uri: RUNBAR_RESOURCE_URI,
15577
16500
  mimeType: RUNBAR_MIME,
15578
16501
  text: runbarWidgetHtml(),
15579
16502
  _meta: { ui: { prefersBorder: true } }
@@ -16233,121 +17156,6 @@ function runbarWidgetHtml() {
16233
17156
  </html>`;
16234
17157
  }
16235
17158
 
16236
- // src/progress.ts
16237
- var realPace = {
16238
- now: () => Date.now(),
16239
- rest: (ms) => new Promise((done) => {
16240
- setTimeout(done, ms);
16241
- })
16242
- };
16243
- var WAIT_LIMIT_SECONDS = 55;
16244
- var POLL_EVERY_MS = 2e3;
16245
- var JOB_STILL_COMPUTING = "\u0415\u0449\u0451 \u0441\u0447\u0438\u0442\u0430\u0435\u0442";
16246
- var JOB_IN_QUEUE = "\u0416\u0434\u0451\u0442 \u043E\u0447\u0435\u0440\u0435\u0434\u0438";
16247
- function stillWorking(text) {
16248
- return text.includes(JOB_STILL_COMPUTING) || text.includes(JOB_IN_QUEUE);
16249
- }
16250
- function waitSeconds(asked) {
16251
- if (asked === void 0 || !Number.isFinite(asked) || asked <= 0) return 0;
16252
- return Math.min(Math.round(asked), WAIT_LIMIT_SECONDS);
16253
- }
16254
- function freshLines(text, told) {
16255
- const news = [];
16256
- for (const row of text.split("\n").slice(1)) {
16257
- const line = row.trim();
16258
- if (line.length === 0) continue;
16259
- if (line.startsWith(JOB_STILL_COMPUTING)) continue;
16260
- if (told.has(line)) continue;
16261
- told.add(line);
16262
- news.push(line);
16263
- }
16264
- return news;
16265
- }
16266
- async function followJob(opts) {
16267
- const pace = opts.pace ?? realPace;
16268
- const until = pace.now() + opts.seconds * 1e3;
16269
- const told = /* @__PURE__ */ new Set();
16270
- let state = await opts.ask();
16271
- retell(state, told, opts.tell);
16272
- while (state.ok && stillWorking(state.text) && pace.now() < until) {
16273
- await pace.rest(POLL_EVERY_MS);
16274
- try {
16275
- state = await opts.ask();
16276
- } catch {
16277
- continue;
16278
- }
16279
- retell(state, told, opts.tell);
16280
- }
16281
- return state;
16282
- }
16283
- function retell(state, told, tell) {
16284
- if (tell === void 0) return;
16285
- for (const line of freshLines(state.text, told)) tell(line);
16286
- }
16287
- var STAGE_WORDS = {
16288
- silence: "\u0443\u0431\u0435\u0440\u0443 \u043F\u0430\u0443\u0437\u044B",
16289
- voice: "\u0432\u044B\u0440\u043E\u0432\u043D\u044F\u044E \u0433\u043E\u043B\u043E\u0441",
16290
- speech: "\u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u044E \u0440\u0435\u0447\u044C",
16291
- phrases: "\u0440\u0430\u0437\u043B\u043E\u0436\u0443 \u0440\u0435\u0447\u044C \u043D\u0430 \u0444\u0440\u0430\u0437\u044B",
16292
- plan: "\u043F\u0440\u0438\u0434\u0443\u043C\u0430\u044E, \u0447\u0442\u043E \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0438 \u043A\u043E\u0433\u0434\u0430",
16293
- world: "\u043D\u0430\u0440\u0438\u0441\u0443\u044E \u0444\u043E\u043D \u0437\u0430 \u0433\u0435\u0440\u043E\u0435\u043C",
16294
- cutout: "\u043E\u0442\u0434\u0435\u043B\u044E \u0433\u0435\u0440\u043E\u044F \u043E\u0442 \u0444\u043E\u043D\u0430",
16295
- "hero-on-world": "\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E \u0433\u0435\u0440\u043E\u044F \u043D\u0430 \u0444\u043E\u043D",
16296
- shots: "\u0440\u0430\u0441\u0441\u0442\u0430\u0432\u043B\u044E \u043A\u0440\u0443\u043F\u043D\u043E\u0441\u0442\u0438 \u043F\u043E \u043F\u043B\u0430\u043D\u0443",
16297
- screen: "\u0432\u0441\u0442\u0430\u0432\u043B\u044E \u0437\u0430\u043F\u0438\u0441\u044C \u044D\u043A\u0440\u0430\u043D\u0430",
16298
- inserts: "\u043D\u0430\u0440\u0438\u0441\u0443\u044E \u0433\u0440\u0430\u0444\u0438\u043A\u0443 \u043F\u043E \u043F\u043B\u0430\u043D\u0443",
16299
- subtitles: "\u043D\u0430\u0440\u0438\u0441\u0443\u044E \u0441\u0443\u0431\u0442\u0438\u0442\u0440\u044B",
16300
- layers: "\u043F\u043E\u043B\u043E\u0436\u0443 \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0438 \u043F\u043E\u0432\u0435\u0440\u0445",
16301
- accents: "\u0440\u0430\u0441\u0441\u0442\u0430\u0432\u043B\u044E \u0437\u0432\u0443\u043A\u043E\u0432\u044B\u0435 \u0430\u043A\u0446\u0435\u043D\u0442\u044B",
16302
- mix: "\u0441\u0432\u0435\u0434\u0443 \u0437\u0432\u0443\u043A",
16303
- intro: "\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0443 \u0432 \u043D\u0430\u0447\u0430\u043B\u043E"
16304
- };
16305
- var NEAR_STEPS = 3;
16306
- function montageLadder(stages) {
16307
- const words = stages.map((stage) => STAGE_WORDS[stage]).filter((word) => word !== void 0);
16308
- if (words.length === 0) return [];
16309
- const near = words.slice(0, NEAR_STEPS);
16310
- const far = words.slice(NEAR_STEPS);
16311
- const lines = [`\u0421\u043D\u0430\u0447\u0430\u043B\u0430 ${near.join(", \u043F\u043E\u0442\u043E\u043C ")}.`];
16312
- if (far.length > 0) {
16313
- lines.push(
16314
- `\u0414\u0430\u043B\u044C\u0448\u0435: ${far.join(", ")}. \u0428\u0430\u0433\u043E\u0432 \u0432\u0441\u0435\u0433\u043E \u0434\u043E ${stages.length} \u2014 \u0447\u0442\u043E \u043D\u0435 \u043F\u043E\u043D\u0430\u0434\u043E\u0431\u0438\u0442\u0441\u044F, \u043F\u0440\u043E\u043F\u0443\u0449\u0443.`
16315
- );
16316
- }
16317
- return lines;
16318
- }
16319
- var PAUSES_LADDER = [
16320
- "\u0423\u0431\u0435\u0440\u0443 \u043C\u043E\u043B\u0447\u0430\u043D\u0438\u0435 \u0438 \u0437\u0430\u043F\u0438\u043D\u043A\u0438 \u2014 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0442\u0440\u043E\u0433\u0430\u044E.",
16321
- "\u0421\u0443\u0431\u0442\u0438\u0442\u0440\u044B, \u043A\u0430\u0440\u0442\u0438\u043D\u043A\u0438 \u043F\u043E\u0432\u0435\u0440\u0445 \u0438 \u0437\u0432\u0443\u043A \u2014 \u044D\u0442\u043E \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u0430\u044F \u0440\u0430\u0431\u043E\u0442\u0430, \u0435\u0451 \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442."
16322
- ];
16323
- var PAYLOAD_LADDER = [
16324
- "\u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u043F\u043E\u043B\u043E\u0436\u0443 \u0434\u0432\u0438\u0436\u043E\u043A \u043C\u043E\u043D\u0442\u0430\u0436\u0430 \u043E\u0434\u043D\u043E\u0439 \u043F\u0430\u043F\u043A\u043E\u0439 \u2014 \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044F.",
16325
- "\u041F\u043E\u0442\u043E\u043C ffmpeg, python \u0438 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043C\u043E\u043D\u0442\u0430\u0436\u0430. \u0422\u044F\u0436\u0451\u043B\u043E\u0435 \u2014 \u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0447\u0438, \u0432\u044B\u0440\u0435\u0437 \u0433\u0435\u0440\u043E\u044F, \u0447\u0438\u0441\u0442\u043A\u0430 \u0433\u043E\u043B\u043E\u0441\u0430 \u2014 \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435."
16326
- ];
16327
- var INSTALL_WORDS = {
16328
- ffmpeg: "\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E ffmpeg \u2014 \u0438\u043C \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044F \u0432\u0438\u0434\u0435\u043E",
16329
- python: "\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E python",
16330
- env: "\u0441\u043E\u0431\u0435\u0440\u0443 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043C\u043E\u043D\u0442\u0430\u0436\u0430",
16331
- model: "\u0441\u043A\u0430\u0447\u0430\u044E \u043C\u043E\u0434\u0435\u043B\u044C \u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u0432\u0430\u043D\u0438\u044F \u0440\u0435\u0447\u0438 \u2014 \u043E\u043D\u0430 \u0438\u0437 \u0432\u0441\u0435\u0433\u043E \u0441\u0430\u043C\u0430\u044F \u0442\u044F\u0436\u0451\u043B\u0430\u044F",
16332
- cutout: "\u0441\u043A\u0430\u0447\u0430\u044E \u0442\u043E, \u0447\u0435\u043C \u043E\u0442\u0434\u0435\u043B\u044F\u044E \u0433\u0435\u0440\u043E\u044F \u043E\u0442 \u0444\u043E\u043D\u0430",
16333
- voice: "\u0441\u043A\u0430\u0447\u0430\u044E \u0442\u043E, \u0447\u0435\u043C \u0447\u0438\u0449\u0443 \u0433\u043E\u043B\u043E\u0441",
16334
- browser: "\u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E \u0431\u0440\u0430\u0443\u0437\u0435\u0440, \u043A\u043E\u0442\u043E\u0440\u044B\u043C \u0440\u0438\u0441\u0443\u044E \u0433\u0440\u0430\u0444\u0438\u043A\u0443"
16335
- };
16336
- function installLadder(step) {
16337
- if (step === "all") {
16338
- return [
16339
- "\u041F\u0440\u043E\u0432\u0435\u0440\u044E, \u0447\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442, \u0438 \u0434\u043E\u0441\u0442\u0430\u0432\u043B\u044E \u043F\u043E \u043E\u0447\u0435\u0440\u0435\u0434\u0438.",
16340
- "\u0422\u044F\u0436\u0451\u043B\u043E\u0435 \u0441\u044E\u0434\u0430 \u043D\u0435 \u0432\u0445\u043E\u0434\u0438\u0442: \u0440\u0430\u0441\u043F\u043E\u0437\u043D\u0430\u0432\u0430\u043D\u0438\u0435 \u0440\u0435\u0447\u0438 \u0438 \u0432\u044B\u0440\u0435\u0437 \u0433\u0435\u0440\u043E\u044F \u0441\u0442\u0430\u0432\u044F\u0442\u0441\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \xAB\u0434\u0430\xBB \u043E\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430."
16341
- ];
16342
- }
16343
- const word = INSTALL_WORDS[step];
16344
- if (word === void 0) return [];
16345
- return [
16346
- `\u0421\u0435\u0439\u0447\u0430\u0441 ${word}.`,
16347
- "\u0418\u0434\u0451\u0442 \u043C\u0438\u043D\u0443\u0442\u0430\u043C\u0438. \u041E\u0431\u043E\u0440\u0432\u0451\u0442\u0441\u044F \u2014 \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \u0442\u043E\u0442 \u0436\u0435 \u0448\u0430\u0433: \u0443\u0436\u0435 \u0441\u043A\u0430\u0447\u0430\u043D\u043D\u043E\u0435 \u043D\u0435 \u043F\u0440\u043E\u043F\u0430\u0434\u0451\u0442."
16348
- ];
16349
- }
16350
-
16351
17159
  // src/tool-kit.ts
16352
17160
  function manifestUrl(engine, baseUrl) {
16353
17161
  return engine.variable("NAPARNIK_UPDATE_MANIFEST") ?? manifestUrlFor(baseUrl);
@@ -16551,7 +17359,7 @@ async function startMontage(engine, opts) {
16551
17359
  return toolSuccess(
16552
17360
  `\u0417\u0430\u0434\u0430\u0447\u0430 ${launch.id}: ${opts.what}
16553
17361
  \u0412\u0441\u0451 \u0434\u0435\u043B\u0430\u0435\u0442\u0441\u044F \u043D\u0430 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F. ${opts.hint}
16554
- \u0420\u0430\u0431\u043E\u0442\u0430 \u043F\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B: \u043D\u043E\u043C\u0435\u0440 \u0437\u0430\u0434\u0430\u0447\u0438 \u0437\u0430\u043F\u043E\u043C\u043D\u0438 \u0438 \u043F\u043E\u0437\u043E\u0432\u0438 wait \u0441 job "${launch.id}" \u2014 \u043E\u043D \u043F\u043E\u0434\u043E\u0436\u0434\u0451\u0442 \u0438 \u0440\u0430\u0441\u0441\u043A\u0430\u0436\u0435\u0442 \u0445\u043E\u0434 \u043F\u043E \u0448\u0430\u0433\u0430\u043C, \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0439 \u0435\u0433\u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u0441\u0440\u0430\u0437\u0443.`,
17362
+ \u0420\u0430\u0431\u043E\u0442\u0430 \u043F\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B: \u043D\u043E\u043C\u0435\u0440 \u0437\u0430\u0434\u0430\u0447\u0438 \u0437\u0430\u043F\u043E\u043C\u043D\u0438 \u0438 \u043F\u043E\u0437\u043E\u0432\u0438 wait \u0441 job "${launch.id}" \u2014 \u043E\u043D \u043F\u043E\u0434\u043E\u0436\u0434\u0451\u0442 \u0438 \u0440\u0430\u0441\u0441\u043A\u0430\u0436\u0435\u0442 \u0445\u043E\u0434 \u043F\u043E \u0448\u0430\u0433\u0430\u043C, \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0439 \u0435\u0433\u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u0441\u0440\u0430\u0437\u0443. ${AWAY_NOTE}`,
16555
17363
  startedJob({
16556
17364
  id: launch.id,
16557
17365
  what: opts.what,
@@ -16560,10 +17368,10 @@ async function startMontage(engine, opts) {
16560
17368
  })
16561
17369
  );
16562
17370
  }
16563
- function fromWords(text, contract) {
17371
+ function fromWords(text2, contract) {
16564
17372
  let data;
16565
17373
  try {
16566
- data = JSON.parse(text);
17374
+ data = JSON.parse(text2);
16567
17375
  } catch {
16568
17376
  return { refusal: NOT_JSON };
16569
17377
  }
@@ -16573,13 +17381,13 @@ function fromWords(text, contract) {
16573
17381
  if (typeof data.words !== "string" || data.words === "") return { refusal: NOT_JSON };
16574
17382
  return { words: data.words, data };
16575
17383
  }
16576
- function folderAnswer(text) {
16577
- const read = fromWords(text);
17384
+ function folderAnswer(text2) {
17385
+ const read = fromWords(text2);
16578
17386
  return "refusal" in read ? toolError(read.refusal) : toolSuccess(read.words, read.data);
16579
17387
  }
16580
17388
  function nameOrRefusal(candidate) {
16581
- const looksLikePath = candidate.includes("/") || candidate.includes("\\") || candidate.startsWith("~") || candidate.startsWith(".") || candidate.length > 1 && candidate[1] === ":";
16582
- if (!looksLikePath) return { name: candidate };
17389
+ const looksLikePath2 = candidate.includes("/") || candidate.includes("\\") || candidate.startsWith("~") || candidate.startsWith(".") || candidate.length > 1 && candidate[1] === ":";
17390
+ if (!looksLikePath2) return { name: candidate };
16583
17391
  return {
16584
17392
  refusal: `\u0417\u0434\u0435\u0441\u044C \u0436\u0434\u0443\u0442 \u0418\u041C\u042F \u0448\u0430\u0433\u0430, \u0430 \u043F\u0440\u0438\u0448\u0451\u043B \u043F\u0443\u0442\u044C: ${candidate}
16585
17393
  \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043D\u0430\u0437\u043E\u0432\u0438 \u0448\u0430\u0433 \u043E\u0434\u043D\u0438\u043C \u0441\u043B\u043E\u0432\u043E\u043C \u2014 \u0442\u0430\u043A, \u043A\u0430\u043A \u043D\u0430\u0437\u044B\u0432\u0430\u0435\u0442\u0441\u044F \u0435\u0433\u043E \u043F\u0430\u043F\u043A\u0430. \u0421\u0432\u043E\u0439 \u0448\u0430\u0433 \u043B\u0435\u0436\u0438\u0442 \u0432 \`mine/steps/<\u0438\u043C\u044F>\` \u0432 \u0434\u043E\u043C\u0435 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430, \u043D\u0430\u0448\u0438 \u043F\u0435\u0440\u0435\u0447\u0438\u0441\u043B\u044F\u0435\u0442 setup, \u0438\u043C\u044F \u0443 \u043A\u0430\u0436\u0434\u043E\u0433\u043E \u0441\u0442\u043E\u0438\u0442 \u0432 \u0441\u043A\u043E\u0431\u043A\u0430\u0445.`
@@ -16706,7 +17514,7 @@ ${EXTRAS_LATER}`), later]);
16706
17514
  if ("refusal" in launch) return toolError(launch.refusal);
16707
17515
  return toolSuccess(
16708
17516
  `\u0417\u0430\u0434\u0430\u0447\u0430 ${launch.id}: \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430, \u0448\u0430\u0433 \xAB${step}\xBB.
16709
- \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 \u0438 \u043F\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B. \u041F\u043E\u0437\u043E\u0432\u0438 wait \u0441 job "${launch.id}" \u2014 \u043E\u043D \u043F\u043E\u0434\u043E\u0436\u0434\u0451\u0442 \u0438 \u043F\u043E \u0445\u043E\u0434\u0443 \u0440\u0430\u0441\u0441\u043A\u0430\u0436\u0435\u0442, \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u043A\u0430\u0447\u0430\u043D\u043E \u0438 \u043E \u0447\u0451\u043C \u043D\u0430\u0434\u043E \u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430. \u041F\u0435\u0440\u0435\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0439 \u044D\u0442\u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u0441\u0440\u0430\u0437\u0443, \u0430 \u043D\u0435 \u0432 \u043A\u043E\u043D\u0446\u0435.`,
17517
+ \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 \u0438 \u043F\u0435\u0440\u0435\u0436\u0438\u0432\u0430\u0435\u0442 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B. \u041F\u043E\u0437\u043E\u0432\u0438 wait \u0441 job "${launch.id}" \u2014 \u043E\u043D \u043F\u043E\u0434\u043E\u0436\u0434\u0451\u0442 \u0438 \u043F\u043E \u0445\u043E\u0434\u0443 \u0440\u0430\u0441\u0441\u043A\u0430\u0436\u0435\u0442, \u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u043A\u0430\u0447\u0430\u043D\u043E \u0438 \u043E \u0447\u0451\u043C \u043D\u0430\u0434\u043E \u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430. \u041F\u0435\u0440\u0435\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0439 \u044D\u0442\u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u0441\u0440\u0430\u0437\u0443, \u0430 \u043D\u0435 \u0432 \u043A\u043E\u043D\u0446\u0435. ${AWAY_NOTE}`,
16710
17518
  startedJob({
16711
17519
  id: launch.id,
16712
17520
  what: `\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430, \u0448\u0430\u0433 \xAB${step}\xBB`,
@@ -16729,9 +17537,9 @@ async function updateEngine(engine, consent, key, baseUrl, timeoutMs = ASKED_POI
16729
17537
  const outcome = await fetchManifest(manifestUrl(engine, baseUrl), timeoutMs, key);
16730
17538
  const decision = decide(engine.freshVersion(), outcome.ok ? outcome.manifest : null);
16731
17539
  if (decision.what === "nothing") {
16732
- const ownDir = engine.freshVersion();
17540
+ const ownDir2 = engine.freshVersion();
16733
17541
  return toolSuccess(
16734
- !outcome.ok ? `\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 \u043D\u0430\u0447\u0438\u043D\u043A\u0438: ${outcome.why}. \u042D\u0442\u043E \u043D\u0435 \u043F\u043E\u043B\u043E\u043C\u043A\u0430: \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F (${ownDir ?? "\u043D\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430"}) \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043A\u0430\u043A \u0440\u0430\u0431\u043E\u0442\u0430\u043B\u0430.` : `\u041D\u0430\u0447\u0438\u043D\u043A\u0430 \u0441\u0432\u0435\u0436\u0430\u044F: \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 ${ownDir ?? "\u043D\u0435\u0442"}, \u043D\u043E\u0432\u0435\u0435 \u043D\u0435 \u0432\u044B\u0448\u043B\u043E.`
17542
+ !outcome.ok ? `\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 \u043D\u0430\u0447\u0438\u043D\u043A\u0438: ${outcome.why}. \u042D\u0442\u043E \u043D\u0435 \u043F\u043E\u043B\u043E\u043C\u043A\u0430: \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043D\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F (${ownDir2 ?? "\u043D\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430"}) \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043A\u0430\u043A \u0440\u0430\u0431\u043E\u0442\u0430\u043B\u0430.` : `\u041D\u0430\u0447\u0438\u043D\u043A\u0430 \u0441\u0432\u0435\u0436\u0430\u044F: \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 ${ownDir2 ?? "\u043D\u0435\u0442"}, \u043D\u043E\u0432\u0435\u0435 \u043D\u0435 \u0432\u044B\u0448\u043B\u043E.`
16735
17543
  );
16736
17544
  }
16737
17545
  if (decision.what === "shell_too_old") {
@@ -16925,11 +17733,11 @@ async function askBeforeHeavy(engine, step, model, key, baseUrl) {
16925
17733
  ]));
16926
17734
  }
16927
17735
  function together(results) {
16928
- const text = results.map((one) => one.type === "success" ? wordsOf(one.content) : one.error).filter((words) => words !== "").join("\n\n");
17736
+ const text2 = results.map((one) => one.type === "success" ? wordsOf(one.content) : one.error).filter((words) => words !== "").join("\n\n");
16929
17737
  for (const one of results) {
16930
- if (one.type === "success" && one.structured !== void 0) return toolSuccess(text, one.structured);
17738
+ if (one.type === "success" && one.structured !== void 0) return toolSuccess(text2, one.structured);
16931
17739
  }
16932
- return results.some((one) => one.type === "error") ? toolError(text) : toolSuccess(text);
17740
+ return results.some((one) => one.type === "error") ? toolError(text2) : toolSuccess(text2);
16933
17741
  }
16934
17742
  function wordsOf(content) {
16935
17743
  if (typeof content === "string") return content;
@@ -16973,10 +17781,10 @@ async function openReel(engine, asked) {
16973
17781
  var PLAN_DRAFT = "naparnik-plan-draft/1";
16974
17782
  var COMPOSITION_VIEW = "naparnik-composition-view/1";
16975
17783
  var SKILLS_READ = "naparnik-skills-read/1";
16976
- function answerOrRefusal(text, contract) {
16977
- const read = fromWords(text, contract);
17784
+ function answerOrRefusal(text2, contract) {
17785
+ const read = fromWords(text2, contract);
16978
17786
  if ("refusal" in read) {
16979
- const plain = text.trim();
17787
+ const plain = text2.trim();
16980
17788
  return { refusal: plain.startsWith("{") || plain === "" ? read.refusal : plain };
16981
17789
  }
16982
17790
  const said = read.data;
@@ -17076,10 +17884,10 @@ async function planDraft(engine, asked, key, tell) {
17076
17884
  }
17077
17885
  }
17078
17886
  if (tell !== void 0) for (const line of DRAFT_LADDER) tell(line);
17079
- const head = opened !== null && opened.type === "success" && typeof opened.content === "string" ? `${opened.content}
17887
+ const head2 = opened !== null && opened.type === "success" && typeof opened.content === "string" ? `${opened.content}
17080
17888
 
17081
17889
  ` : "";
17082
- const withHead = (text) => `${head}${text}`;
17890
+ const withHead = (text2) => `${head2}${text2}`;
17083
17891
  const answer = await engine.command([
17084
17892
  "plan-draft",
17085
17893
  "--video",
@@ -17672,7 +18480,7 @@ async function startComposition(engine, reel, key, tell) {
17672
18480
  if ("refusal" in launch) return toolError(launch.refusal);
17673
18481
  return toolSuccess(
17674
18482
  `\u0417\u0430\u0434\u0430\u0447\u0430 ${launch.id}: \u043C\u043E\u043D\u0442\u0430\u0436 ${basename(reel)}
17675
- \u0414\u0430\u043B\u044C\u0448\u0435 \u043F\u043E\u0437\u043E\u0432\u0438 wait \u0441 \u044D\u0442\u0438\u043C \u043D\u043E\u043C\u0435\u0440\u043E\u043C.`,
18483
+ \u0414\u0430\u043B\u044C\u0448\u0435 \u043F\u043E\u0437\u043E\u0432\u0438 wait \u0441 \u044D\u0442\u0438\u043C \u043D\u043E\u043C\u0435\u0440\u043E\u043C. ${AWAY_NOTE}`,
17676
18484
  startedJob({
17677
18485
  id: launch.id,
17678
18486
  what: `\u043C\u043E\u043D\u0442\u0430\u0436 ${basename(reel)}`,
@@ -18002,10 +18810,10 @@ var OWN_STYLE_FOLDER = "style";
18002
18810
  function contractRefusal(theirs) {
18003
18811
  return `\u0414\u0432\u0438\u0436\u043E\u043A \u0433\u043E\u0432\u043E\u0440\u0438\u0442 \u043D\u0430 \u0434\u043E\u0433\u043E\u0432\u043E\u0440\u0435 \xAB${theirs}\xBB, \u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0435 \u043F\u043E\u043D\u0438\u043C\u0430\u0435\u0442 \xAB${CHOICES_CONTRACT}\xBB. \u041E\u0431\u043D\u043E\u0432\u0438 \u0442\u043E, \u0447\u0442\u043E \u0441\u0442\u0430\u0440\u0435\u0435: setup \u0441 what=engine \u0434\u043B\u044F \u0434\u0432\u0438\u0436\u043A\u0430, what=shell \u0434\u043B\u044F \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F.`;
18004
18812
  }
18005
- function notJson(text) {
18006
- const head = text.trim().slice(0, 160).replace(/\s+/g, " ");
18007
- return NOT_JSON2 + (head ? `
18008
- \u0427\u0442\u043E \u043F\u0440\u0438\u0448\u043B\u043E \u0432\u043C\u0435\u0441\u0442\u043E JSON: \xAB${head}\u2026\xBB` : "");
18813
+ function notJson(text2) {
18814
+ const head2 = text2.trim().slice(0, 160).replace(/\s+/g, " ");
18815
+ return NOT_JSON2 + (head2 ? `
18816
+ \u0427\u0442\u043E \u043F\u0440\u0438\u0448\u043B\u043E \u0432\u043C\u0435\u0441\u0442\u043E JSON: \xAB${head2}\u2026\xBB` : "");
18009
18817
  }
18010
18818
  var NOT_JSON2 = "\u0414\u0432\u0438\u0436\u043E\u043A \u043E\u0442\u0432\u0435\u0442\u0438\u043B \u043D\u0435 \u0442\u0435\u043C, \u0447\u0435\u0433\u043E \u0436\u0434\u0430\u043B\u0438. \u041E\u0431\u043D\u043E\u0432\u0438 \u0434\u0432\u0438\u0436\u043E\u043A \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u043E\u043C setup \u0441 what=engine \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438.";
18011
18819
  var FIRST_BATCH = 12;
@@ -18353,7 +19161,7 @@ async function showCards(engine, input) {
18353
19161
  // «Готово» требовало ответить и там. Нашло ревью устройства
18354
19162
  // 2026-09-18.
18355
19163
  only: wanted2,
18356
- clip: basename3(where.path),
19164
+ clip: basename5(where.path),
18357
19165
  video: where.path,
18358
19166
  // ⚠ СЫРЬЁ ПРЕВЬЮ ЕДЕТ ФОРМЕ ЦЕЛИКОМ: чистый кадр, числа вида и
18359
19167
  // умолчания движка. Картинку она складывает сама (решение владельца
@@ -18432,7 +19240,7 @@ function questionInWords(q) {
18432
19240
  return `${q.title} ${shown}${rest}`;
18433
19241
  }
18434
19242
  var LATIN_NAME = /^[a-z][a-z0-9_-]{0,31}$/;
18435
- function basename3(path2) {
19243
+ function basename5(path2) {
18436
19244
  const parts = path2.split(/[\\/]/);
18437
19245
  return parts[parts.length - 1] ?? path2;
18438
19246
  }
@@ -21119,44 +21927,6 @@ function chooseWidgetHtml() {
21119
21927
  </body>
21120
21928
  </html>`;
21121
21929
  }
21122
- var CALLS_FILE = "calls.jsonl";
21123
- var CALLS_PREVIOUS = "calls.1.jsonl";
21124
- var CALLS_CAP_BYTES = 1e6;
21125
- function withCallLog(home, now = () => Date.now()) {
21126
- return (tool) => {
21127
- const original = tool.execute.bind(tool);
21128
- return {
21129
- ...tool,
21130
- async execute(input, ...rest) {
21131
- const started = now();
21132
- const result = await original(input, ...rest);
21133
- write(home, {
21134
- ts: (/* @__PURE__ */ new Date()).toISOString(),
21135
- tool: tool.name,
21136
- ok: result.type === "success",
21137
- ms: now() - started
21138
- });
21139
- return result;
21140
- }
21141
- };
21142
- };
21143
- }
21144
- function write(home, line) {
21145
- try {
21146
- mkdirSync(home, { recursive: true });
21147
- const file2 = join(home, CALLS_FILE);
21148
- rotate(file2, join(home, CALLS_PREVIOUS));
21149
- appendFileSync(file2, `${JSON.stringify(line)}
21150
- `, { encoding: "utf8", mode: 384 });
21151
- } catch {
21152
- }
21153
- }
21154
- function rotate(file2, previous) {
21155
- try {
21156
- if (statSync(file2).size >= CALLS_CAP_BYTES) renameSync(file2, previous);
21157
- } catch {
21158
- }
21159
- }
21160
21930
 
21161
21931
  // src/update-nudge.ts
21162
21932
  function usable2(value) {
@@ -21317,7 +22087,7 @@ ${result.content}` : [{ type: "text", text: header }, ...result.content]
21317
22087
  }
21318
22088
 
21319
22089
  // src/tool-registry.ts
21320
- function buildAll(client, engine, passport, key, baseUrl, pace = realPace) {
22090
+ function buildAll(client, engine, passport, key, baseUrl, pace = realPace, awake = ASLEEP, crashes = null) {
21321
22091
  return [
21322
22092
  // Порядок — это порядок работы, и модель читает список сверху вниз:
21323
22093
  // проверить машину, придумать словами, поправить словами, собрать, дождаться.
@@ -21349,7 +22119,16 @@ function buildAll(client, engine, passport, key, baseUrl, pace = realPace) {
21349
22119
  // ходы МОДЕЛИ, а ход, отбитый опиской или мёртвой подпиской, стоил тех же
21350
22120
  // секунд. Встань счётчик внутрь — из замера выпали бы именно неудачные
21351
22121
  // ходы, то есть самые дорогие (`call-log.ts`).
21352
- ].map(withSubscription(client)).map(withArgs).map(withCallLog(engine.home()));
22122
+ //
22123
+ // ⚠ «НЕ СПИ» — СНАРУЖИ СВЕРКИ И ЗАСЛОНА, ПОД СЧЁТЧИКОМ. Держит любой ход
22124
+ // модели, в том числе отбитый: раз модель зовёт — она работает, и хвост
22125
+ // бодрости после него нужен так же (`keep-awake.ts`). Счётчик остаётся
22126
+ // самым внешним по доводу выше.
22127
+ //
22128
+ // ⚠ ЛОВУШКА ПОЛОМОК — СНАРУЖИ СВЕРКИ И ЗАСЛОНА: падение в самом заслоне
22129
+ // подписки — тоже наш баг, и его надо поймать. Под счётчиком — чтобы
22130
+ // пойманный вызов лёг в журнал отказом, а не пропал из замера.
22131
+ ].map(withSubscription(client)).map(withArgs).map(withCrashReport(crashes)).map(withWakefulness(awake)).map(withCallLog(engine.home()));
21353
22132
  }
21354
22133
  function buildResources() {
21355
22134
  return [chooseResource(), runbarResource()];
@@ -21402,8 +22181,8 @@ function buildBlock(entryFile, key) {
21402
22181
  function withoutBom(string5) {
21403
22182
  return string5.charCodeAt(0) === 65279 ? string5.slice(1) : string5;
21404
22183
  }
21405
- function findBlock(text) {
21406
- const lines = text.split("\n");
22184
+ function findBlock(text2) {
22185
+ const lines = text2.split("\n");
21407
22186
  const heading = new RegExp(`^\\[mcp_servers\\.(?:${SERVER_NAME}|"${SERVER_NAME}")\\]\\s*$`);
21408
22187
  const child = new RegExp(`^\\[mcp_servers\\.(?:${SERVER_NAME}|"${SERVER_NAME}")\\.`);
21409
22188
  const i = lines.findIndex((s) => heading.test(s));
@@ -21428,8 +22207,8 @@ function keyFromBlock(blockText) {
21428
22207
  }
21429
22208
  return void 0;
21430
22209
  }
21431
- function keyInTable(text) {
21432
- const lines = text.split("\n");
22210
+ function keyInTable(text2) {
22211
+ const lines = text2.split("\n");
21433
22212
  let inTable = false;
21434
22213
  for (const raw of lines) {
21435
22214
  const s = withoutBom(raw);
@@ -21546,4 +22325,4 @@ ${inTable}`, file2);
21546
22325
  };
21547
22326
  }
21548
22327
 
21549
- export { 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, 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, refusalFor, remove, rollback, rollbackShell, shellDir, tarEntries, tidyOld2 as tidyOld, timeToCheck, timeToCheck2, unpackArchive, updateEngine, withArgs };
22328
+ 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 };