@naparnik/mcp 0.10.48 → 0.10.50

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, randomUUID, randomBytes } from 'crypto';
2
+ import { existsSync, readFileSync, closeSync, mkdirSync, openSync, rmSync, renameSync, readdirSync, statSync, writeFileSync, symlinkSync, chmodSync, lstatSync, realpathSync, mkdtempSync, linkSync, readlinkSync, appendFileSync, fstatSync, readSync, fsyncSync } from 'fs';
3
+ import { win32, join, dirname, sep, resolve, basename, relative, isAbsolute, extname } from 'path';
4
+ import { gunzipSync } from 'zlib';
1
5
  import { execFile, spawn, execFileSync } from 'child_process';
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
6
  import { platform, homedir, arch, release, userInfo, tmpdir } from 'os';
5
- import { win32, join, dirname, sep, resolve, basename, relative, isAbsolute, extname } from 'path';
6
7
  import { promisify } from 'util';
7
8
  import { AsyncLocalStorage } from 'async_hooks';
8
- import { gunzipSync } from 'zlib';
9
9
  import { pathToFileURL, fileURLToPath } from 'url';
10
10
  import { Worker } from 'worker_threads';
11
11
  import { readFile } from 'fs/promises';
@@ -730,9 +730,9 @@ function parsePositiveInt(raw, fallback) {
730
730
  }
731
731
 
732
732
  // src/version.ts
733
- var VERSION = "0.10.48";
733
+ var VERSION = "0.10.50";
734
734
  var USER_AGENT = `naparnik-mcp/${VERSION}`;
735
- var MIN_ENGINE = "2026.09.24-466";
735
+ var MIN_ENGINE = "2026.09.24-478";
736
736
  var LAUNCHER_PROTOCOL = 1;
737
737
 
738
738
  // src/contracts/sdk-headers.ts
@@ -1001,6 +1001,258 @@ function describe(err) {
1001
1001
  function sleep(ms) {
1002
1002
  return new Promise((resolve5) => setTimeout(resolve5, ms));
1003
1003
  }
1004
+ var DeliveryError = class extends Error {
1005
+ constructor(message) {
1006
+ super(message);
1007
+ this.name = "DeliveryTrouble";
1008
+ }
1009
+ };
1010
+ async function downloadFile(opts) {
1011
+ const { url: url2, dest, sha256, bytes, timeoutMs = 30 * 6e4, report, key } = opts;
1012
+ if (!/^[a-f0-9]{64}$/.test(sha256)) {
1013
+ throw new DeliveryError(`\u043D\u0435\u0447\u0435\u043C \u0441\u0432\u0435\u0440\u0438\u0442\u044C \u0441\u043A\u0430\u0447\u0430\u043D\u043D\u043E\u0435: \u0441\u0443\u043C\u043C\u0430 \xAB${sha256}\xBB \u043D\u0435 \u043F\u043E\u0445\u043E\u0436\u0430 \u043D\u0430 sha256`);
1014
+ }
1015
+ mkdirSync(dirname(dest), { recursive: true });
1016
+ const temp = `${dest}.downloading-${process.pid}-${randomUUID()}`;
1017
+ const guard = new AbortController();
1018
+ const timer = setTimeout(() => {
1019
+ guard.abort();
1020
+ }, timeoutMs);
1021
+ try {
1022
+ const response = await fetch(url2, {
1023
+ signal: guard.signal,
1024
+ redirect: "follow",
1025
+ ...key !== null && key !== void 0 ? { headers: { authorization: `Bearer ${key}` } } : {}
1026
+ });
1027
+ const hasKey = key !== null && key !== void 0;
1028
+ if (!response.ok) {
1029
+ if (response.status === 401 || response.status === 403) {
1030
+ if (response.status === 403 && hasKey) {
1031
+ throw new DeliveryError(
1032
+ `${url2} \u043D\u0435 \u043E\u0442\u0434\u0430\u0451\u0442 \u0444\u0430\u0439\u043B: \u043A\u043B\u044E\u0447 \u043F\u0440\u0438\u0437\u043D\u0430\u043D, \u043D\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438 \u043D\u0430 \u043C\u043E\u043D\u0442\u0430\u0436 \u043D\u0435\u0442. \u0421\u043A\u0430\u0447\u0438\u0432\u0430\u043D\u0438\u0435 \u043D\u043E\u0432\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E, \u043F\u043E\u043A\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0430 \u0430\u043A\u0442\u0438\u0432\u043D\u0430; \u0432\u0441\u0451, \u0447\u0442\u043E \u0443\u0436\u0435 \u0441\u0442\u043E\u0438\u0442 \u043D\u0430 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435, \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0435\u0442 \u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C. \u041F\u0440\u043E\u0434\u043B\u0438\u0442\u0435 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0443 \u0432 \u0431\u043E\u0442\u0435 \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435.`
1033
+ );
1034
+ }
1035
+ throw new DeliveryError(
1036
+ !hasKey ? `${url2} \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043A\u043B\u044E\u0447 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430, \u0430 \u043E\u043D \u043D\u0435 \u0437\u0430\u0434\u0430\u043D. \u0412\u043F\u0438\u0448\u0438\u0442\u0435 \u043A\u043B\u044E\u0447 \u0432 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044E MCP-\u0441\u0435\u0440\u0432\u0435\u0440\u0430 (\u0441\u0435\u043A\u0446\u0438\u044F env) \u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0443 \u2014 \u041A\u043B\u043E\u0434\u0430 \u0438\u043B\u0438 Codex.` : `${url2} \u043D\u0435 \u043F\u0440\u0438\u043D\u044F\u043B \u043A\u043B\u044E\u0447 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430 (${response.status}). \u0421\u043A\u043E\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043E, \u043A\u043B\u044E\u0447 \u0437\u0430\u043C\u0435\u043D\u0451\u043D \u0431\u043E\u043B\u0435\u0435 \u043D\u043E\u0432\u044B\u043C: \u043D\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B \u0438\u0437 \u0431\u043E\u0442\u0430 \u0443\u0436\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D \u0437\u0434\u0435\u0441\u044C \u0438\u043B\u0438 \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u043C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435. \u0412\u043E\u0437\u044C\u043C\u0438\u0442\u0435 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0444\u0430\u0439\u043B \u0443 \u0431\u043E\u0442\u0430 ${KEY_SOURCE_BOT} \u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u0435\u0433\u043E.`
1037
+ );
1038
+ }
1039
+ throw new DeliveryError(`${url2} \u043E\u0442\u0432\u0435\u0442\u0438\u043B ${response.status} ${response.statusText}`);
1040
+ }
1041
+ const body = await response.arrayBuffer();
1042
+ const buffer = Buffer.from(body);
1043
+ const sum = createHash("sha256").update(buffer).digest("hex");
1044
+ if (bytes !== void 0 && buffer.length !== bytes) {
1045
+ throw new DeliveryError(
1046
+ looksLikeHtml(buffer) ? `\u043F\u043E \u0430\u0434\u0440\u0435\u0441\u0443 ${url2} \u043B\u0435\u0436\u0438\u0442 \u043D\u0435 \u0430\u0440\u0445\u0438\u0432, \u0430 \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u2014 \u0444\u0430\u0439\u043B \u0435\u0449\u0451 \u043D\u0435 \u0432\u044B\u043B\u043E\u0436\u0435\u043D.` : `\u0441\u043A\u0430\u0447\u0430\u043B\u043E\u0441\u044C ${buffer.length} \u0431\u0430\u0439\u0442 \u0432\u043C\u0435\u0441\u0442\u043E ${bytes} \u2014 \u0441\u0432\u044F\u0437\u044C \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C, \u043F\u043E\u0432\u0442\u043E\u0440\u0438.`
1047
+ );
1048
+ }
1049
+ if (sum !== sha256) {
1050
+ throw new DeliveryError(
1051
+ looksLikeHtml(buffer) ? `\u043F\u043E \u0430\u0434\u0440\u0435\u0441\u0443 ${url2} \u043B\u0435\u0436\u0438\u0442 \u043D\u0435 \u0430\u0440\u0445\u0438\u0432, \u0430 \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u2014 \u0444\u0430\u0439\u043B \u0435\u0449\u0451 \u043D\u0435 \u0432\u044B\u043B\u043E\u0436\u0435\u043D.` : `\u0441\u043A\u0430\u0447\u0430\u043D\u043D\u043E\u0435 \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u043B\u043E \u0441 \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u043E\u0439 \u0441\u0443\u043C\u043C\u043E\u0439.
1052
+ \u0436\u0434\u0430\u043B\u0438 ${sha256}
1053
+ \u0432\u044B\u0448\u043B\u043E ${sum}
1054
+ \u041F\u043E\u0432\u0442\u043E\u0440\u0438; \u0435\u0441\u043B\u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442\u0441\u044F \u2014 \u0444\u0430\u0439\u043B \u043F\u043E\u0434\u043C\u0435\u043D\u0451\u043D \u0438\u043B\u0438 \u0431\u0438\u0442\u044B\u0439, \u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0435\u0433\u043E \u043D\u0435\u043B\u044C\u0437\u044F.`
1055
+ );
1056
+ }
1057
+ writeFileSync(temp, buffer);
1058
+ if (existsSync(dest)) {
1059
+ throw new DeliveryError(
1060
+ `\u0444\u0430\u0439\u043B ${dest} \u0443\u0436\u0435 \u0437\u0430\u043D\u044F\u043B \u0434\u0440\u0443\u0433\u043E\u0439 \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u2014 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0443\u0434\u0430\u043B\u044F\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u044E \u0441 \u043D\u043E\u0432\u044B\u043C staging.`
1061
+ );
1062
+ }
1063
+ renameSync(temp, dest);
1064
+ report?.(`\u0441\u043A\u0430\u0447\u0430\u043D\u043E ${Math.round(buffer.length / 1024 / 1024)} \u041C\u0411`);
1065
+ return { file: dest, bytes: buffer.length, sha256: sum };
1066
+ } catch (trouble) {
1067
+ rmSync(temp, { force: true });
1068
+ if (trouble instanceof DeliveryError) throw trouble;
1069
+ if (guard.signal.aborted) {
1070
+ throw new DeliveryError(`${url2} \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0444\u0430\u0439\u043B \u0437\u0430 ${Math.round(timeoutMs / 6e4)} \u043C\u0438\u043D.`);
1071
+ }
1072
+ throw new DeliveryError(`\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043A\u0430\u0447\u0430\u0442\u044C ${url2}: ${describeFailure(trouble)}`);
1073
+ } finally {
1074
+ clearTimeout(timer);
1075
+ }
1076
+ }
1077
+ function looksLikeHtml(buffer) {
1078
+ const start = buffer.subarray(0, 200).toString("latin1").trimStart().toLowerCase();
1079
+ return start.startsWith("<!doctype html") || start.startsWith("<html");
1080
+ }
1081
+ var BLOCK = 512;
1082
+ function tarEntries(data) {
1083
+ const entries = [];
1084
+ let offset = 0;
1085
+ let longName = null;
1086
+ let longTarget = null;
1087
+ while (offset + BLOCK <= data.length) {
1088
+ const header = data.subarray(offset, offset + BLOCK);
1089
+ if (header.every((b) => b === 0)) break;
1090
+ const name = fieldLine(header, 0, 100);
1091
+ const mode = octal(header, 100, 8);
1092
+ const size = fieldSize(header, 124, 12);
1093
+ const type = String.fromCharCode(header[156]);
1094
+ const target = fieldLine(header, 157, 100);
1095
+ const prefix = fieldLine(header, 345, 155);
1096
+ offset += BLOCK;
1097
+ const body = data.subarray(offset, offset + size);
1098
+ offset += Math.ceil(size / BLOCK) * BLOCK;
1099
+ if (type === "L") {
1100
+ longName = body.toString("utf8").replace(/\0+$/, "");
1101
+ continue;
1102
+ }
1103
+ if (type === "K") {
1104
+ longTarget = body.toString("utf8").replace(/\0+$/, "");
1105
+ continue;
1106
+ }
1107
+ if (type === "x") {
1108
+ const fields = parsePax(body);
1109
+ if (fields["path"] !== void 0) longName = fields["path"];
1110
+ if (fields["linkpath"] !== void 0) longTarget = fields["linkpath"];
1111
+ continue;
1112
+ }
1113
+ if (type === "g") continue;
1114
+ const full = longName ?? (prefix !== "" ? `${prefix}/${name}` : name);
1115
+ const linkTarget = longTarget ?? target;
1116
+ longName = null;
1117
+ longTarget = null;
1118
+ const kind = type === "5" ? "dir" : type === "2" ? "symlink" : type === "1" ? "hardlink" : "file";
1119
+ if (type !== "" && type !== "0" && type !== "\0" && kind === "file" && type !== "7") continue;
1120
+ entries.push({ name: full, kind, mode, body, target: linkTarget });
1121
+ }
1122
+ return entries;
1123
+ }
1124
+ function parsePax(body) {
1125
+ const fields = {};
1126
+ let offset = 0;
1127
+ while (offset < body.length) {
1128
+ const space = body.indexOf(32, offset);
1129
+ if (space === -1) break;
1130
+ const length = Number.parseInt(body.subarray(offset, space).toString("ascii"), 10);
1131
+ if (!Number.isFinite(length) || length <= 0 || offset + length > body.length) break;
1132
+ const entry = body.subarray(space + 1, offset + length).toString("utf8").replace(/\n$/, "");
1133
+ const equal = entry.indexOf("=");
1134
+ if (equal > 0) fields[entry.slice(0, equal)] = entry.slice(equal + 1);
1135
+ offset += length;
1136
+ }
1137
+ return fields;
1138
+ }
1139
+ function fieldLine(header, start, length) {
1140
+ const chunk = header.subarray(start, start + length);
1141
+ const end = chunk.indexOf(0);
1142
+ return chunk.subarray(0, end === -1 ? chunk.length : end).toString("utf8");
1143
+ }
1144
+ function octal(header, start, length) {
1145
+ const text2 = fieldLine(header, start, length).trim();
1146
+ const ch = Number.parseInt(text2, 8);
1147
+ return Number.isFinite(ch) ? ch : 0;
1148
+ }
1149
+ function fieldSize(header, start, length) {
1150
+ if ((header[start] & 128) !== 0) {
1151
+ let ch = 0;
1152
+ for (let i = start + 1; i < start + length; i += 1) ch = ch * 256 + header[i];
1153
+ return ch;
1154
+ }
1155
+ return octal(header, start, length);
1156
+ }
1157
+ function inside(root, path2) {
1158
+ if (path2 === "" || path2.startsWith("/") || path2.startsWith("\\")) return false;
1159
+ if (/^[A-Za-z]:/.test(path2)) return false;
1160
+ const target = resolve(root, path2);
1161
+ const bottom = resolve(root);
1162
+ return target === bottom || target.startsWith(bottom + sep);
1163
+ }
1164
+ function unpackArchive(archive, dest) {
1165
+ const entries = tarEntries(gunzipSync(readFileSync(archive)));
1166
+ if (entries.length === 0) throw new DeliveryError(`\u0430\u0440\u0445\u0438\u0432 ${archive} \u043F\u0443\u0441\u0442 \u2014 \u0440\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E`);
1167
+ for (const entry of entries) {
1168
+ if (!inside(dest, entry.name)) throw escapesRoot(archive, entry.name);
1169
+ if (entry.kind === "symlink" || entry.kind === "hardlink") {
1170
+ if (entry.target.startsWith("/") || entry.target.startsWith("\\") || /^[A-Za-z]:/.test(entry.target)) {
1171
+ throw escapesRoot(archive, `${entry.name} \u2192 ${entry.target}`);
1172
+ }
1173
+ const from = entry.kind === "hardlink" ? entry.target : join(dirname(entry.name), entry.target);
1174
+ if (!inside(dest, from)) throw escapesRoot(archive, `${entry.name} \u2192 ${entry.target}`);
1175
+ }
1176
+ }
1177
+ for (const entry of entries) {
1178
+ const path2 = join(dest, entry.name);
1179
+ if (entry.kind === "dir") {
1180
+ mkdirSync(path2, { recursive: true });
1181
+ continue;
1182
+ }
1183
+ mkdirSync(dirname(path2), { recursive: true });
1184
+ if (entry.kind === "symlink") {
1185
+ rmSync(path2, { force: true });
1186
+ symlinkSync(entry.target, path2);
1187
+ continue;
1188
+ }
1189
+ if (entry.kind === "hardlink") {
1190
+ writeFileSync(path2, readFileSync(join(dest, entry.target)));
1191
+ continue;
1192
+ }
1193
+ writeFileSync(path2, entry.body);
1194
+ if ((entry.mode & 73) !== 0) chmodSync(path2, entry.mode & 511);
1195
+ }
1196
+ }
1197
+ function escapesRoot(archive, name) {
1198
+ return new DeliveryError(
1199
+ `\u0430\u0440\u0445\u0438\u0432 ${archive} \u043F\u044B\u0442\u0430\u0435\u0442\u0441\u044F \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0444\u0430\u0439\u043B \u0437\u0430 \u043F\u0440\u0435\u0434\u0435\u043B\u044B \u043F\u0430\u043F\u043A\u0438 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438: \xAB${name}\xBB.
1200
+ \u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0440\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u0430\u043D\u043E. \u0421\u043A\u0430\u0447\u0430\u0439 \u0437\u0430\u043D\u043E\u0432\u043E \u2014 \u0432\u0435\u0440\u043E\u044F\u0442\u043D\u0435\u0435 \u0432\u0441\u0435\u0433\u043E, \u0444\u0430\u0439\u043B \u043F\u043E\u0432\u0440\u0435\u0436\u0434\u0451\u043D \u0438\u043B\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u0451\u043D.`
1201
+ );
1202
+ }
1203
+ function dirExists(path2) {
1204
+ try {
1205
+ return statSync(path2).isDirectory();
1206
+ } catch {
1207
+ return false;
1208
+ }
1209
+ }
1210
+ var LOCK_FILE = "deps.lock.json";
1211
+ function platformKey(os = process.platform, arch2 = process.arch) {
1212
+ if (os === "darwin") return arch2 === "arm64" ? "Darwin-arm64" : arch2 === "x64" ? "Darwin-x86_64" : null;
1213
+ if (os === "win32") return arch2 === "x64" ? "Windows-AMD64" : null;
1214
+ if (os === "linux") return arch2 === "x64" ? "Linux-x86_64" : arch2 === "arm64" ? "Linux-aarch64" : null;
1215
+ return null;
1216
+ }
1217
+ function depFolderName(entry) {
1218
+ return `${entry.version}-${entry.archives.map((one) => one.sha256.slice(0, 8)).join("")}`;
1219
+ }
1220
+ function lockedPython(engineDir, home, key = platformKey()) {
1221
+ if (key === null) return null;
1222
+ const file2 = join(engineDir, LOCK_FILE);
1223
+ if (!existsSync(file2)) return null;
1224
+ const lock = JSON.parse(readFileSync(file2, "utf8"));
1225
+ const entry = lock.python?.platforms?.[key];
1226
+ const archive = entry?.archives[0];
1227
+ if (entry === void 0 || archive === void 0) return null;
1228
+ const folder = join(home, "deps", "python", depFolderName(entry));
1229
+ return { version: entry.version, archive, folder, executable: join(folder, entry.run) };
1230
+ }
1231
+ async function bringPython(python, report) {
1232
+ if (existsSync(python.executable)) return python.executable;
1233
+ const stage = `${python.folder}.partial-${process.pid}`;
1234
+ rmSync(stage, { recursive: true, force: true });
1235
+ try {
1236
+ const archive = join(stage, "python.tar.gz");
1237
+ await downloadFile({
1238
+ url: python.archive.url,
1239
+ dest: archive,
1240
+ sha256: python.archive.sha256,
1241
+ bytes: python.archive.bytes,
1242
+ key: null,
1243
+ ...report !== void 0 ? { report } : {}
1244
+ });
1245
+ unpackArchive(archive, join(stage, "tree"));
1246
+ if (!existsSync(join(stage, "tree", python.executable.slice(python.folder.length + 1)))) {
1247
+ throw new DeliveryError("\u0432 \u0430\u0440\u0445\u0438\u0432\u0435 \u043F\u0438\u0442\u043E\u043D\u0430 \u043D\u0435\u0442 \u0438\u043D\u0442\u0435\u0440\u043F\u0440\u0435\u0442\u0430\u0442\u043E\u0440\u0430, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u043D\u0430\u0437\u0432\u0430\u043B \u0437\u0430\u043C\u043E\u043A");
1248
+ }
1249
+ mkdirSync(dirname(python.folder), { recursive: true });
1250
+ if (!dirExists(python.folder)) renameSync(join(stage, "tree"), python.folder);
1251
+ } finally {
1252
+ rmSync(stage, { recursive: true, force: true });
1253
+ }
1254
+ return python.executable;
1255
+ }
1004
1256
  var TAIL_MS = 10 * 6e4;
1005
1257
  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.";
1006
1258
  var realClock = {
@@ -1315,7 +1567,6 @@ function currentMachineFingerprint() {
1315
1567
  return cached;
1316
1568
  }
1317
1569
  var execFileP = promisify(execFile);
1318
- var INSIDE_PYTHON = ["python.exe", join("bin", "python3"), join("bin", "python")];
1319
1570
  var FOREIGN_PYTHON_VARS = [
1320
1571
  "PYTHONHOME",
1321
1572
  "PYTHONPATH",
@@ -1521,94 +1772,44 @@ var Engine = class {
1521
1772
  return null;
1522
1773
  }
1523
1774
  /**
1524
- * Портативный python, который движок доставил сам (шаг «python» установки).
1775
+ * Питон по замку начинки (`engine/deps.lock.json`), если он уже в хранилище.
1525
1776
  *
1526
- * ⚠ ЗНАТЬ ПРО НЕГО ОБЯЗАНА И ЭТА СТОРОНА. Он лежит в `<дом>/bin/python` и в
1527
- * PATH не попадает намеренно — PATH человека мы не трогаем. Пропусти его
1528
- * здесь, и получится: установка принесла питон, окружение на нём собралось,
1529
- * а сервер MCP по-прежнему зовёт системный «python», которого нет или
1530
- * который заглушка из Microsoft Store.
1777
+ * ⚠ ТОЛЬКО СВОЙ. До 2026-09-24 здесь был «python3» из PATH на случай, когда
1778
+ * своего нет, а окружение новой версии собиралось перебором «свой → базовый
1779
+ * питон рабочей версии → системный». Claude Desktop подсунул питон Xcode 3.9,
1780
+ * тот не собрал окружение, и обновление у владельца не вставало целый день.
1781
+ * Решение владельца — «как node_modules»: свой питон по замку, чужого нет.
1531
1782
  */
1532
- ourPython() {
1533
- const root = join(this.home(), "bin", "python");
1534
- for (const rel of INSIDE_PYTHON) {
1535
- const candidate = join(root, rel);
1536
- if (existsSync(candidate)) return candidate;
1537
- }
1538
- return null;
1783
+ ourPython(place = this.place()) {
1784
+ if (place === null) return null;
1785
+ const python = lockedPython(place.engine, this.home());
1786
+ return python !== null && existsSync(python.executable) ? python.executable : null;
1539
1787
  }
1540
1788
  /**
1541
- * Python для команд движка.
1789
+ * Python для команд движка: окружение пака, а пока его нет — свой питон по
1790
+ * замку. Нет ни того, ни другого — путь, которого нет: `pythonTrouble`
1791
+ * ответит словами «позови setup», а не «spawn python3 ENOENT».
1542
1792
  *
1543
- * Окружения пака может не быть вовсе — именно это и проверяет `check_setup`,
1544
- * поэтому запускать проверку его питоном нельзя: получилась бы курица и
1545
- * яйцо. Команды движка написаны на голой стандартной библиотеке и идут
1546
- * системным python'ом, когда своего ещё нет.
1793
+ * ⚠ «python3» ИЗ PATH — ТОЛЬКО ПРИ `NAPARNIK_SYSTEM_TOOLS=1`: так тесты и
1794
+ * разработка гоняют движок из исходников без хранилища (та же переменная у
1795
+ * движка, `engine/media/ffmpeg.py`). У покупателя её нет.
1547
1796
  */
1548
1797
  enginePython() {
1549
- return this.packPython() ?? this.ourPython() ?? (platform() === "win32" ? "python" : "python3");
1550
- }
1551
- /** Python подготовки кандидата — намеренно без fallback на venv старого пака. */
1552
- bootstrapPython() {
1553
- return this.bootstrapPythons()[0] ?? (platform() === "win32" ? "python" : "python3");
1798
+ const fallback = this.env["NAPARNIK_SYSTEM_TOOLS"] === "1" ? platform() === "win32" ? "python" : "python3" : join(this.home(), "deps", "python", "not-installed");
1799
+ return this.packPython() ?? this.ourPython() ?? fallback;
1554
1800
  }
1555
1801
  /**
1556
- * Чем собирать окружение новой версии — по порядку, до первого, кто сумеет.
1557
- *
1558
- * ⚠ БАЗОВЫЙ ПИТОН РАБОЧЕЙ ВЕРСИИ СТОИТ ВПЕРЕДИ «python3» ИЗ PATH, И ЭТО
1559
- * ПОЧИНКА, А НЕ ВКУС. Claude Desktop на маке запускает нас без
1560
- * `/opt/homebrew/bin` в PATH, и «python3» там — `/usr/bin/python3`, питон
1561
- * Xcode 3.9. Он собирает venv только ссылками («This build of python cannot
1562
- * create venvs without using symlinks»), а ссылка на интерпретатор вне версии
1563
- * не проходит сверку `candidateVenvTrouble`. Обновление 2026.09.24-467 у
1564
- * владельца встало ровно на этом, хотя рабочая версия собрана Homebrew 3.14
1565
- * копиями и работает. Движок при первой установке ищет питон сам
1566
- * (`setup/python_finder.py`) и пишет, какой взял, в `pyvenv.cfg` окружения —
1567
- * этот и берём: он уже однажды собрал здесь рабочее окружение копиями.
1568
- *
1569
- * ⚠ НЕ ПИТОН САМОГО VENV. Окружение старой версии удаляется уборкой после
1570
- * обновления, и кандидат, собранный его питоном, остался бы без интерпретатора
1571
- * (`engine-candidate.test.ts`). База из `pyvenv.cfg` лежит вне версий.
1572
- */
1573
- bootstrapPythons() {
1574
- const found = [
1575
- this.ourPython(),
1576
- this.workingBasePython(),
1577
- platform() === "win32" ? "python" : "python3"
1578
- ].filter((one) => one !== null);
1579
- return [...new Set(found)];
1580
- }
1581
- /**
1582
- * Базовый интерпретатор окружения рабочей версии — из её `pyvenv.cfg`.
1583
- *
1584
- * `home` раньше `executable`: у Homebrew `executable` указывает в папку
1585
- * конкретного выпуска (`Cellar/python@3.14/3.14.7/…`), и после `brew upgrade`
1586
- * её уже нет, а `home` (`/opt/homebrew/opt/python@3.14/bin`) переживает
1587
- * обновление. Нет файла или записанного питона на диске — `null`.
1802
+ * Принести питон замка ЭТОГО кандидата (или взять уже лежащий) — им движок
1803
+ * кандидата доводит до замка всё остальное (`env-install --step env`).
1588
1804
  */
1589
- workingBasePython() {
1590
- const place = this.place();
1591
- if (place === null) return null;
1592
- let text2;
1593
- try {
1594
- text2 = readFileSync(join(place.pack, ".venv", "pyvenv.cfg"), "utf8");
1595
- } catch {
1596
- return null;
1805
+ async bringPython(place, report) {
1806
+ const python = lockedPython(place.engine, this.home());
1807
+ if (python === null) {
1808
+ throw new Error(`\u043F\u043E\u0434 \u044D\u0442\u0443 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 \u0432 \u0437\u0430\u043C\u043A\u0435 \u043D\u0435\u0442 \u0441\u0431\u043E\u0440\u043A\u0438 \u043F\u0438\u0442\u043E\u043D\u0430. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043D\u0430\u043F\u0438\u0448\u0438 \u0431\u043E\u0442\u0443 ${KEY_SOURCE_BOT} \u2014 \u043F\u043E\u0434\u0431\u0435\u0440\u0451\u043C \u0441\u0431\u043E\u0440\u043A\u0443`);
1597
1809
  }
1598
- const value = (key) => {
1599
- const found = new RegExp(`^\\s*${key}\\s*=\\s*(.+?)\\s*$`, "m").exec(text2);
1600
- return found?.[1] ?? null;
1601
- };
1602
- const home = value("home");
1603
- if (home !== null) {
1604
- const names = platform() === "win32" ? ["python.exe"] : ["python3", "python"];
1605
- for (const name of names) {
1606
- const candidate = join(home, name);
1607
- if (existsSync(candidate)) return candidate;
1608
- }
1609
- }
1610
- const executable = value("executable");
1611
- return executable !== null && existsSync(executable) ? executable : null;
1810
+ const executable = await bringPython(python, report);
1811
+ this.forgetPython();
1812
+ return executable;
1612
1813
  }
1613
1814
  /** Python ровно названного кандидата, а не текущей версии. */
1614
1815
  candidatePython(place) {
@@ -1632,6 +1833,9 @@ var Engine = class {
1632
1833
  pythonEnv() {
1633
1834
  const env = { ...this.env, PYTHONUTF8: "1", PYTHONIOENCODING: "utf-8" };
1634
1835
  for (const name of FOREIGN_PYTHON_VARS) delete env[name];
1836
+ for (const [name, value] of Object.entries(env)) {
1837
+ if (value !== void 0 && value.trim() !== "" && fromHost(value) === "") delete env[name];
1838
+ }
1635
1839
  delete env["NAPARNIK_API_KEY"];
1636
1840
  return env;
1637
1841
  }
@@ -1641,12 +1845,7 @@ var Engine = class {
1641
1845
  * Весь движок (включая проверку и установку) идёт через запуск python'а.
1642
1846
  * Когда его нет, `execFile` бросает ENOENT, и клиентской нейросети
1643
1847
  * доставалось «spawn python3 ENOENT» — текст, из которого человеку не понять
1644
- * ничего. Шаг «python», написанный ровно для этого случая, при этом
1645
- * недостижим по устройству: он сам запускается python'ом.
1646
- *
1647
- * На Windows есть отдельная беда: в PATH лежит заглушка Microsoft Store —
1648
- * `python` там есть, он печатает рекламу магазина и выходит с кодом 9009.
1649
- * Поэтому проверяем не наличие файла, а ответ на вопрос о версии.
1848
+ * ничего. Проверяем не наличие файла, а ответ на вопрос о версии.
1650
1849
  */
1651
1850
  async pythonTrouble() {
1652
1851
  if (this.pythonChecked !== void 0) return this.pythonChecked;
@@ -1662,7 +1861,7 @@ var Engine = class {
1662
1861
  }
1663
1862
  } catch {
1664
1863
  }
1665
- this.pythonChecked = "\u041D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435 \u043D\u0435\u0442 Python \u2014 \u0430 \u0431\u0435\u0437 \u043D\u0435\u0433\u043E \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C: \u0432\u0435\u0441\u044C \u0434\u0432\u0438\u0436\u043E\u043A \u043D\u0430\u043F\u0438\u0441\u0430\u043D \u043D\u0430 \u043D\u0451\u043C.\n" + (platform() === "win32" ? "\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0441\u043A\u0430\u0436\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C Python \u0441 python.org/downloads \u0438 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u043E\u0442\u043C\u0435\u0442\u0438\u0442\u044C \u0433\u0430\u043B\u043E\u0447\u043A\u0443 \xABAdd python.exe to PATH\xBB. \u0415\u0441\u043B\u0438 \u0432 PATH \u0441\u0435\u0439\u0447\u0430\u0441 \u043B\u0435\u0436\u0438\u0442 \u0437\u0430\u0433\u043B\u0443\u0448\u043A\u0430 Microsoft Store (\u043E\u043D\u0430 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043C\u0430\u0433\u0430\u0437\u0438\u043D \u0432\u043C\u0435\u0441\u0442\u043E \u0437\u0430\u043F\u0443\u0441\u043A\u0430) \u2014 \u0435\u0451 \u043D\u0430\u0434\u043E \u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C: \u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u2192 \u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u2192 \u041F\u0441\u0435\u0432\u0434\u043E\u043D\u0438\u043C\u044B \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u2192 \u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C python.exe.\n" : "\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0441\u043A\u0430\u0436\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C Python \u0441 python.org/downloads.\n") + "\u041F\u043E\u0441\u043B\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0443 \u2014 \u043F\u0443\u0442\u044C \u043A python \u043F\u043E\u0434\u0445\u0432\u0430\u0442\u044B\u0432\u0430\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435 \u2014 \u0438 \u043F\u043E\u0437\u043E\u0432\u0438 setup \u0441\u043D\u043E\u0432\u0430.";
1864
+ this.pythonChecked = "\u0421\u0432\u043E\u0435\u0433\u043E \u043F\u0438\u0442\u043E\u043D\u0430 \u043C\u043E\u043D\u0442\u0430\u0436\u0430 \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435 \u0435\u0449\u0451 \u043D\u0435\u0442 \u2014 \u043E\u043D \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044F \u0432\u043C\u0435\u0441\u0442\u0435 \u0441 \u0434\u0432\u0438\u0436\u043A\u043E\u043C, \u043E\u0434\u043D\u043E\u0439 \u043F\u0430\u043F\u043A\u043E\u0439, \u0432 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044F.\n\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0437\u043E\u0432\u0438 setup \u0441 what=all \u2014 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E \u0434\u0432\u0438\u0436\u043E\u043A \u0438 \u0432\u0441\u0451, \u0447\u0442\u043E \u0435\u043C\u0443 \u043D\u0443\u0436\u043D\u043E.";
1666
1865
  return this.pythonChecked;
1667
1866
  }
1668
1867
  // ─── запуск ─────────────────────────────────────────────────────────────
@@ -13357,6 +13556,9 @@ function candidateVenvTrouble(dir, version2, place, requirePresent = false) {
13357
13556
  if (venvStat.isSymbolicLink() || !venvStat.isDirectory()) return ".venv \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043E\u0431\u044B\u0447\u043D\u043E\u0439 \u043F\u0430\u043F\u043A\u043E\u0439";
13358
13557
  if (!realpathSync(venv).startsWith(buildReal + sep)) return ".venv \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u0432\u044B\u0448\u0435\u043B \u0437\u0430 \u043F\u0440\u0435\u0434\u0435\u043B\u044B \u0432\u0435\u0440\u0441\u0438\u0438";
13359
13558
  const insideBuild = (path2) => path2 === buildReal || path2.startsWith(buildReal + sep);
13559
+ const store = join(dirname(dir), "deps", "python");
13560
+ const storeReal = existsSync(store) ? realpathSync(store) : null;
13561
+ const insideStore = (path2) => storeReal !== null && path2.startsWith(storeReal + sep);
13360
13562
  const inspectedDirectories = /* @__PURE__ */ new Set();
13361
13563
  const inspectTree = (folder, insideCriticalParent, insideSitePackages) => {
13362
13564
  const folderReal = realpathSync(folder);
@@ -13370,8 +13572,10 @@ function candidateVenvTrouble(dir, version2, place, requirePresent = false) {
13370
13572
  const inSitePackages = insideSitePackages || entry.name.toLowerCase() === "site-packages";
13371
13573
  const stat = lstatSync(path2);
13372
13574
  if (stat.isSymbolicLink()) {
13373
- if (inCriticalParent || inSitePackages || /^(python(?:3(?:\.\d+)?)?|python\.exe)$/i.test(entry.name)) {
13575
+ const interpreter = /^(python(?:3(?:\.\d+)?)?|python\.exe)$/i.test(entry.name);
13576
+ if (inCriticalParent || inSitePackages || interpreter) {
13374
13577
  const actual = realpathSync(path2);
13578
+ if (!insideBuild(actual) && interpreter && insideStore(actual)) continue;
13375
13579
  if (!insideBuild(actual)) return `${path2} \u0432\u0435\u0434\u0451\u0442 \u0437\u0430 \u043F\u0440\u0435\u0434\u0435\u043B\u044B \u0432\u0435\u0440\u0441\u0438\u0438 \u0432 ${actual}`;
13376
13580
  if (statSync(path2).isDirectory()) {
13377
13581
  const trouble = inspectTree(path2, inCriticalParent, inSitePackages);
@@ -13956,7 +14160,7 @@ function tidyOld2(engine) {
13956
14160
  }
13957
14161
  function candidateDeps(engine) {
13958
14162
  const value = engine;
13959
- return typeof value.home === "function" && typeof value.placeForVersion === "function" && typeof value.bootstrapPython === "function" && typeof value.candidatePython === "function" && typeof value.runWith === "function";
14163
+ return typeof value.home === "function" && typeof value.placeForVersion === "function" && typeof value.bringPython === "function" && typeof value.candidatePython === "function" && typeof value.runWith === "function";
13960
14164
  }
13961
14165
  async function deploy(engine, manifest, trial = probe, key, hooks = {}) {
13962
14166
  const place = engine.place();
@@ -13999,6 +14203,9 @@ async function deployLocked(engine, manifest, trial, key, hooks) {
13999
14203
  text: `\u041F\u0430\u043F\u043A\u0430 \u0432\u0435\u0440\u0441\u0438\u0438 ${manifest.version} \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u043D\u043E digest \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u0434\u0440\u0443\u0433\u043E\u0439 \u0438\u043B\u0438 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D: \u0447\u0443\u0436\u043E\u0439 final \u043D\u0435 \u043F\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u043B \u0438 \u043D\u0435 \u0443\u0434\u0430\u043B\u044F\u043B. \u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0438\u0441\u044B\u0432\u0430\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0432\u044B\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0434\u0440\u0443\u0433\u043E\u0435 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u043C\u043E\u0435 \u043F\u043E\u0434 \u043D\u043E\u0432\u044B\u043C \u043D\u043E\u043C\u0435\u0440\u043E\u043C \u0432\u0435\u0440\u0441\u0438\u0438.`
14000
14204
  };
14001
14205
  }
14206
+ if (candidate.state === "blocked" && currentVersion(dir) !== manifest.version && pollutedPayload(dest, candidate)) {
14207
+ keepBroken(dir, manifest.version, "\u0424\u0430\u0439\u043B\u044B \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0438\u0441\u044C \u043F\u043E\u0441\u043B\u0435 \u0441\u043A\u0430\u0447\u0438\u0432\u0430\u043D\u0438\u044F \u2014 \u043E\u043F\u0438\u0441\u044C \u0431\u0430\u0439\u0442\u043E\u0432 \u043D\u0435 \u0441\u043E\u0448\u043B\u0430\u0441\u044C; \u0441\u043A\u0430\u0447\u0430\u043D \u0437\u0430\u043D\u043E\u0432\u043E.");
14208
+ }
14002
14209
  }
14003
14210
  if (!existsSync(dest)) {
14004
14211
  const stage = createVersionStage(dir, manifest.version);
@@ -14125,16 +14332,12 @@ function recoverPublicationLocked(dir) {
14125
14332
  }
14126
14333
  clearPublication(dir, publication.operationId);
14127
14334
  }
14128
- async function continuePreparedCandidate(engine, expected, bootstrap, trial = probe, extras = "now") {
14335
+ async function continuePreparedCandidate(engine, expected, trial = probe, extras = "now") {
14129
14336
  try {
14130
14337
  return await withVersionLock(engine.versionsDir(), async () => {
14131
- const checked = preparedCandidateBeforeBootstrap(engine, expected);
14338
+ const checked = preparedCandidateBeforeActivation(engine, expected);
14132
14339
  if (!checked.ok) return checked.outcome;
14133
- const bootstrapped = await bootstrap({ candidate: checked.candidate, place: checked.place });
14134
- const after = preparedCandidateAfterBootstrap(engine, checked.candidate);
14135
- if (!after.ok) return after.outcome;
14136
- if (!bootstrapped.ok) return bootstrapped;
14137
- return activatePreparedCandidateLocked(engine, after.candidate, trial, extras);
14340
+ return activatePreparedCandidateLocked(engine, checked.candidate, trial, extras);
14138
14341
  });
14139
14342
  } catch (trouble) {
14140
14343
  if (trouble instanceof VersionLockError) return { ok: false, text: trouble.message };
@@ -14145,7 +14348,7 @@ async function continuePreparedCandidate(engine, expected, bootstrap, trial = pr
14145
14348
  };
14146
14349
  }
14147
14350
  }
14148
- function preparedCandidateBeforeBootstrap(engine, expected) {
14351
+ function preparedCandidateBeforeActivation(engine, expected) {
14149
14352
  const dir = engine.versionsDir();
14150
14353
  const candidate = readCandidate(dir);
14151
14354
  if (candidate === null || !sameCandidateState(candidate, expected)) {
@@ -14153,21 +14356,21 @@ function preparedCandidateBeforeBootstrap(engine, expected) {
14153
14356
  ok: false,
14154
14357
  outcome: {
14155
14358
  ok: false,
14156
- text: "\u041F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043B\u0435\u043D\u043D\u044B\u0439 candidate \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u0434\u043E bootstrap \u2014 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B \u0438 \u043D\u0435 \u0437\u0430\u0442\u0438\u0440\u0430\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup."
14359
+ text: "\u041F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043B\u0435\u043D\u043D\u044B\u0439 candidate \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u0434\u043E \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F \u2014 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B \u0438 \u043D\u0435 \u0437\u0430\u0442\u0438\u0440\u0430\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup."
14157
14360
  }
14158
14361
  };
14159
14362
  }
14160
14363
  const place = engine.placeForVersion(candidate.version);
14161
14364
  if (place === null) {
14162
- return { ok: false, outcome: blockCandidate(dir, candidate, "\u0423 prepared-\u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u043D\u0435\u0442 engine/console.py \u2014 bootstrap \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B.") };
14365
+ return { ok: false, outcome: blockCandidate(dir, candidate, "\u0423 prepared-\u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u043D\u0435\u0442 engine/console.py \u2014 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B.") };
14163
14366
  }
14164
14367
  const proofTrouble = candidateProofTrouble(dir, candidate, place);
14165
14368
  if (proofTrouble !== null) {
14166
- return { ok: false, outcome: blockCandidate(dir, candidate, `${proofTrouble} bootstrap \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B; current \u043D\u0435 \u043C\u0435\u043D\u044F\u043B.`) };
14369
+ return { ok: false, outcome: blockCandidate(dir, candidate, `${proofTrouble} \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B; current \u043D\u0435 \u043C\u0435\u043D\u044F\u043B.`) };
14167
14370
  }
14168
14371
  const venvTrouble = candidateVenvTrouble(dir, candidate.version, place, false);
14169
14372
  if (venvTrouble !== null) {
14170
- return { ok: false, outcome: blockCandidate(dir, candidate, `${venvTrouble} bootstrap \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B; current \u043D\u0435 \u043C\u0435\u043D\u044F\u043B.`) };
14373
+ return { ok: false, outcome: blockCandidate(dir, candidate, `${venvTrouble} \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B; current \u043D\u0435 \u043C\u0435\u043D\u044F\u043B.`) };
14171
14374
  }
14172
14375
  const current = currentVersion(dir);
14173
14376
  if (current !== candidate.baseCurrent) {
@@ -14175,58 +14378,14 @@ function preparedCandidateBeforeBootstrap(engine, expected) {
14175
14378
  ok: false,
14176
14379
  outcome: {
14177
14380
  ok: false,
14178
- text: `current \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u0434\u043E bootstrap (${candidate.baseCurrent ?? "\u043D\u0435\u0442"} \u2192 ${current ?? "\u043D\u0435\u0442"}) \u2014 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B \u0438 \u043D\u0435 \u0437\u0430\u0442\u0438\u0440\u0430\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0437\u0430\u043D\u043E\u0432\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0438\u0442\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 setup.`
14381
+ text: `current \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u0434\u043E \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F (${candidate.baseCurrent ?? "\u043D\u0435\u0442"} \u2192 ${current ?? "\u043D\u0435\u0442"}) \u2014 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B \u0438 \u043D\u0435 \u0437\u0430\u0442\u0438\u0440\u0430\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0437\u0430\u043D\u043E\u0432\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0438\u0442\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 setup.`
14179
14382
  }
14180
14383
  };
14181
14384
  }
14182
14385
  return { ok: true, candidate, place };
14183
14386
  }
14184
- function preparedCandidateAfterBootstrap(engine, before) {
14185
- const dir = engine.versionsDir();
14186
- const candidate = readCandidate(dir);
14187
- if (candidate === null || !sameCandidateState(candidate, before)) {
14188
- return {
14189
- ok: false,
14190
- outcome: {
14191
- ok: false,
14192
- text: "candidate \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u0432\u043E \u0432\u0440\u0435\u043C\u044F bootstrap \u2014 \u0447\u0443\u0436\u043E\u0439 \u0443\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C \u043D\u0435 \u0437\u0430\u0442\u0438\u0440\u0430\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup."
14193
- }
14194
- };
14195
- }
14196
- if (currentVersion(dir) !== before.baseCurrent) {
14197
- return {
14198
- ok: false,
14199
- outcome: {
14200
- ok: false,
14201
- text: "current \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u0432\u043E \u0432\u0440\u0435\u043C\u044F bootstrap \u2014 \u0447\u0443\u0436\u043E\u0439 current \u043D\u0435 \u0437\u0430\u0442\u0438\u0440\u0430\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup."
14202
- }
14203
- };
14204
- }
14205
- const place = engine.placeForVersion(candidate.version);
14206
- if (place === null) {
14207
- return {
14208
- ok: false,
14209
- outcome: {
14210
- ok: false,
14211
- text: "\u041F\u0443\u0442\u044C prepared-\u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u0432\u043E \u0432\u0440\u0435\u043C\u044F bootstrap \u2014 current \u043D\u0435 \u043C\u0435\u043D\u044F\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup."
14212
- }
14213
- };
14214
- }
14215
- const proofTrouble = candidateProofTrouble(dir, candidate, place);
14216
- const venvTrouble = candidateVenvTrouble(dir, candidate.version, place, false);
14217
- if (proofTrouble !== null || venvTrouble !== null) {
14218
- return {
14219
- ok: false,
14220
- outcome: {
14221
- ok: false,
14222
- text: `${proofTrouble ?? venvTrouble} \u043F\u043E\u0441\u043B\u0435 bootstrap \u2014 candidate \u0438 current \u043D\u0435 \u0437\u0430\u0442\u0438\u0440\u0430\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u0444\u0430\u0439\u043B\u043E\u0432 \u043D\u0430 \u0434\u0438\u0441\u043A\u0435.`
14223
- }
14224
- };
14225
- }
14226
- return { ok: true, candidate, place };
14227
- }
14228
- function blockCandidate(dir, candidate, text2) {
14229
- if (!sameCandidateState(readCandidate(dir), candidate)) {
14387
+ function blockCandidate(dir, candidate, text2) {
14388
+ if (!sameCandidateState(readCandidate(dir), candidate)) {
14230
14389
  return {
14231
14390
  ok: false,
14232
14391
  text: "\u0423\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C candidate \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F \u0432\u043E \u0432\u0440\u0435\u043C\u044F \u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0438 \u2014 \u0447\u0443\u0436\u043E\u0439 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u044B\u0432\u0430\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup."
@@ -14239,7 +14398,7 @@ function blockCandidate(dir, candidate, text2) {
14239
14398
  text: text2.includes("\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C:") ? `${lead}
14240
14399
  ${text2}` : `${lead}
14241
14400
  ${text2}
14242
- \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438 setup \u043F\u043E\u0437\u0436\u0435 \u2014 \u0441\u0432\u044F\u0437\u044C \u043C\u043E\u0433\u043B\u0430 \u043E\u0431\u043E\u0440\u0432\u0430\u0442\u044C\u0441\u044F. \u041E\u0442\u043A\u0430\u0436\u0435\u0442 \u0442\u0435\u043C \u0436\u0435 \u2014 \u044D\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u044F \u0441\u043B\u043E\u043C\u0430\u043D\u0430: \u043C\u043E\u043D\u0442\u0438\u0440\u0443\u0439 \u043D\u0430 \u043F\u0440\u0435\u0436\u043D\u0435\u0439 \u0438 \u0434\u043E\u0436\u0434\u0438\u0441\u044C \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0439.`
14401
+ \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043C\u043E\u043D\u0442\u0438\u0440\u0443\u0439 \u043D\u0430 \u043F\u0440\u0435\u0436\u043D\u0435\u0439. \u0421\u0440\u0430\u0437\u0443 setup \u043D\u0435 \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0439 \u2014 \u043E\u0442\u043A\u0430\u0436\u0435\u0442 \u0442\u0435\u043C \u0436\u0435; \xAB\u043E\u0431\u043D\u043E\u0432\u0438\xBB \u0435\u0449\u0451 \u0440\u0430\u0437 \u2014 \u043F\u043E\u0437\u0436\u0435 \u0438\u043B\u0438 \u043A\u043E\u0433\u0434\u0430 \u0432\u044B\u0439\u0434\u0435\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F.`
14243
14402
  };
14244
14403
  }
14245
14404
  function commandWords(trouble) {
@@ -14260,12 +14419,25 @@ function candidateProofTrouble(dir, candidate, place) {
14260
14419
  if (pathTrouble !== null) return pathTrouble;
14261
14420
  try {
14262
14421
  const actual = payloadProof(join(dir, candidate.version));
14263
- if (!samePayloadProof(candidate.payload, actual)) return "\u0424\u0430\u043A\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0431\u0430\u0439\u0442\u044B prepared-\u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0438\u0441\u044C \u043F\u043E\u0441\u043B\u0435 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043A\u0438.";
14422
+ if (!samePayloadProof(candidate.payload, actual)) {
14423
+ const said = "\u0424\u0430\u0439\u043B\u044B \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0438\u0441\u044C \u043F\u043E\u0441\u043B\u0435 \u0441\u043A\u0430\u0447\u0438\u0432\u0430\u043D\u0438\u044F (\u043E\u043F\u0438\u0441\u044C \u0431\u0430\u0439\u0442\u043E\u0432 \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u043D\u0435 \u0441\u043E\u0448\u043B\u0430\u0441\u044C).";
14424
+ noteInstallFault("candidate-payload", new Error(said));
14425
+ return said;
14426
+ }
14264
14427
  } catch (trouble) {
14265
- return `\u0424\u0430\u043A\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0431\u0430\u0439\u0442\u044B prepared-\u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C: ${describeFailure(trouble)}`;
14428
+ return `\u0424\u0430\u0439\u043B\u044B \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C (\u043E\u043F\u0438\u0441\u044C \u0431\u0430\u0439\u0442\u043E\u0432 \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430): ${describeFailure(trouble)}`;
14266
14429
  }
14267
14430
  return null;
14268
14431
  }
14432
+ function pollutedPayload(dest, candidate) {
14433
+ if (candidate.payload === void 0) return false;
14434
+ try {
14435
+ removePythonBytecode(dest);
14436
+ return !samePayloadProof(candidate.payload, payloadProof(dest));
14437
+ } catch {
14438
+ return false;
14439
+ }
14440
+ }
14269
14441
  function samePreparedCandidate(actual, expected) {
14270
14442
  return actual !== null && actual.version === expected.version && actual.sha256 === expected.sha256 && actual.state === "prepared" && actual.baseCurrent === expected.baseCurrent && actual.environment === expected.environment && actual.mineDigest === expected.mineDigest && actual.payload !== void 0 && expected.payload !== void 0 && samePayloadProof(actual.payload, expected.payload);
14271
14443
  }
@@ -14286,71 +14458,6 @@ async function runCandidateWith(engine, dir, version2, executable, place, args,
14286
14458
  removePythonBytecode(join(dir, version2));
14287
14459
  return result;
14288
14460
  }
14289
- async function prepareCopiedCandidateVenv(engine, dir, version2, place) {
14290
- const trouble = candidateVenvTrouble(dir, version2, place, false);
14291
- if (trouble !== null) throw new Error(trouble);
14292
- const venv = join(place.pack, ".venv");
14293
- const existingPython = engine.candidatePython(place);
14294
- const ready = existingPython !== null && await candidatePipReady(engine, existingPython);
14295
- if (!ready) {
14296
- rmSync(venv, { recursive: true, force: true });
14297
- if (engine.prepareCandidateVenv !== void 0) {
14298
- await engine.prepareCandidateVenv(place);
14299
- } else {
14300
- await copiedVenv(engine, venv);
14301
- }
14302
- }
14303
- const createdTrouble = candidateVenvTrouble(dir, version2, place, true);
14304
- if (createdTrouble !== null) throw new Error(createdTrouble);
14305
- const python = engine.candidatePython(place);
14306
- if (python === null || !await candidatePipReady(engine, python)) {
14307
- throw new Error("\u0448\u0442\u0430\u0442\u043D\u044B\u0439 .venv \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442\u0430 \u0441\u043E\u0437\u0434\u0430\u043D \u0431\u0435\u0437 \u0440\u0430\u0431\u043E\u0447\u0435\u0433\u043E pip");
14308
- }
14309
- }
14310
- var SYMLINKS_ONLY = "cannot create venvs without using symlinks";
14311
- async function copiedVenv(engine, venv) {
14312
- const { execFile: execFile3 } = await import('child_process');
14313
- const { promisify: promisify2 } = await import('util');
14314
- const pythons = engine.bootstrapPythons?.() ?? [engine.bootstrapPython()];
14315
- const refusals = [];
14316
- for (const python of pythons) {
14317
- try {
14318
- await promisify2(execFile3)(python, ["-m", "venv", "--copies", venv], {
14319
- timeout: 10 * 6e4,
14320
- encoding: "utf8",
14321
- env: { ...engine.pythonEnv(), PYTHONDONTWRITEBYTECODE: "1" }
14322
- });
14323
- return;
14324
- } catch (trouble) {
14325
- rmSync(venv, { recursive: true, force: true });
14326
- const said = `${String(trouble.stderr ?? "")} ${describeFailure(trouble)}`;
14327
- refusals.push(said.includes(SYMLINKS_ONLY) ? `${python}: \u0441\u043E\u0431\u0438\u0440\u0430\u0435\u0442 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u0441\u044B\u043B\u043A\u0430\u043C\u0438` : `${python}: ${lastLine(said.trim())}`);
14328
- }
14329
- }
14330
- throw new Error(
14331
- `\u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0441\u043E\u0431\u0440\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C \u2014 ${refusals.join("; ")}. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0441\u0442\u0430\u0432\u044C Python \u0441 python.org/downloads \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438 \xAB\u043E\u0431\u043D\u043E\u0432\u0438\xBB.`
14332
- );
14333
- }
14334
- async function candidatePipReady(engine, python) {
14335
- if (engine.candidatePipReady !== void 0) return engine.candidatePipReady(python);
14336
- const { execFile: execFile3 } = await import('child_process');
14337
- const { promisify: promisify2 } = await import('util');
14338
- const env = { ...engine.pythonEnv(), PYTHONDONTWRITEBYTECODE: "1" };
14339
- delete env["PYTHONHOME"];
14340
- delete env["PYTHONPATH"];
14341
- delete env["VIRTUAL_ENV"];
14342
- delete env["__PYVENV_LAUNCHER__"];
14343
- try {
14344
- await promisify2(execFile3)(python, ["-B", "-m", "pip", "--version"], {
14345
- timeout: 6e4,
14346
- encoding: "utf8",
14347
- env
14348
- });
14349
- return true;
14350
- } catch {
14351
- return false;
14352
- }
14353
- }
14354
14461
  async function compatibilityProof(engine, dir, version2, python, place) {
14355
14462
  let output = "";
14356
14463
  try {
@@ -14413,24 +14520,12 @@ async function activatePreparedCandidateLocked(engine, manifest, trial, extras =
14413
14520
  const resumesReadyBeforeCurrent = candidate.state === "ready" && current !== candidate.version;
14414
14521
  if (candidate.environment !== "ready" || resumesReadyBeforeCurrent) {
14415
14522
  try {
14416
- if (candidate.baseCurrent === null) {
14417
- await runCandidateWith(
14418
- engine,
14419
- dir,
14420
- candidate.version,
14421
- engine.bootstrapPython(),
14422
- place,
14423
- ["env-install", "--pack", place.pack, "--step", "ffmpeg", "--consent"],
14424
- { timeout: 60 * 6e4 },
14425
- false
14426
- );
14427
- }
14428
- await prepareCopiedCandidateVenv(engine, dir, candidate.version, place);
14523
+ const base = await engine.bringPython(place);
14429
14524
  await runCandidateWith(
14430
14525
  engine,
14431
14526
  dir,
14432
14527
  candidate.version,
14433
- engine.bootstrapPython(),
14528
+ base,
14434
14529
  place,
14435
14530
  [
14436
14531
  "env-install",
@@ -14442,7 +14537,7 @@ async function activatePreparedCandidateLocked(engine, manifest, trial, extras =
14442
14537
  ...extras === "later" ? requiredOnly(dir, candidate.version) : []
14443
14538
  ],
14444
14539
  { timeout: 60 * 6e4 },
14445
- true
14540
+ false
14446
14541
  );
14447
14542
  const installedVenvTrouble = candidateVenvTrouble(dir, candidate.version, place, true);
14448
14543
  if (installedVenvTrouble !== null) throw new Error(installedVenvTrouble);
@@ -14698,212 +14793,6 @@ function rollback(engine) {
14698
14793
  };
14699
14794
  }
14700
14795
  }
14701
- var DeliveryError = class extends Error {
14702
- constructor(message) {
14703
- super(message);
14704
- this.name = "DeliveryTrouble";
14705
- }
14706
- };
14707
- async function downloadFile(opts) {
14708
- const { url: url2, dest, sha256, bytes, timeoutMs = 30 * 6e4, report, key } = opts;
14709
- if (!/^[a-f0-9]{64}$/.test(sha256)) {
14710
- throw new DeliveryError(`\u043D\u0435\u0447\u0435\u043C \u0441\u0432\u0435\u0440\u0438\u0442\u044C \u0441\u043A\u0430\u0447\u0430\u043D\u043D\u043E\u0435: \u0441\u0443\u043C\u043C\u0430 \xAB${sha256}\xBB \u043D\u0435 \u043F\u043E\u0445\u043E\u0436\u0430 \u043D\u0430 sha256`);
14711
- }
14712
- mkdirSync(dirname(dest), { recursive: true });
14713
- const temp = `${dest}.downloading-${process.pid}-${randomUUID()}`;
14714
- const guard = new AbortController();
14715
- const timer = setTimeout(() => {
14716
- guard.abort();
14717
- }, timeoutMs);
14718
- try {
14719
- const response = await fetch(url2, {
14720
- signal: guard.signal,
14721
- redirect: "follow",
14722
- ...key !== null && key !== void 0 ? { headers: { authorization: `Bearer ${key}` } } : {}
14723
- });
14724
- const hasKey = key !== null && key !== void 0;
14725
- if (!response.ok) {
14726
- if (response.status === 401 || response.status === 403) {
14727
- if (response.status === 403 && hasKey) {
14728
- throw new DeliveryError(
14729
- `${url2} \u043D\u0435 \u043E\u0442\u0434\u0430\u0451\u0442 \u0444\u0430\u0439\u043B: \u043A\u043B\u044E\u0447 \u043F\u0440\u0438\u0437\u043D\u0430\u043D, \u043D\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438 \u043D\u0430 \u043C\u043E\u043D\u0442\u0430\u0436 \u043D\u0435\u0442. \u0421\u043A\u0430\u0447\u0438\u0432\u0430\u043D\u0438\u0435 \u043D\u043E\u0432\u043E\u0433\u043E \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E, \u043F\u043E\u043A\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0430 \u0430\u043A\u0442\u0438\u0432\u043D\u0430; \u0432\u0441\u0451, \u0447\u0442\u043E \u0443\u0436\u0435 \u0441\u0442\u043E\u0438\u0442 \u043D\u0430 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435, \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0435\u0442 \u0440\u0430\u0431\u043E\u0442\u0430\u0442\u044C. \u041F\u0440\u043E\u0434\u043B\u0438\u0442\u0435 \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0443 \u0432 \u0431\u043E\u0442\u0435 \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435.`
14730
- );
14731
- }
14732
- throw new DeliveryError(
14733
- !hasKey ? `${url2} \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043A\u043B\u044E\u0447 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430, \u0430 \u043E\u043D \u043D\u0435 \u0437\u0430\u0434\u0430\u043D. \u0412\u043F\u0438\u0448\u0438\u0442\u0435 \u043A\u043B\u044E\u0447 \u0432 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044E MCP-\u0441\u0435\u0440\u0432\u0435\u0440\u0430 (\u0441\u0435\u043A\u0446\u0438\u044F env) \u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0443 \u2014 \u041A\u043B\u043E\u0434\u0430 \u0438\u043B\u0438 Codex.` : `${url2} \u043D\u0435 \u043F\u0440\u0438\u043D\u044F\u043B \u043A\u043B\u044E\u0447 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430 (${response.status}). \u0421\u043A\u043E\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043E, \u043A\u043B\u044E\u0447 \u0437\u0430\u043C\u0435\u043D\u0451\u043D \u0431\u043E\u043B\u0435\u0435 \u043D\u043E\u0432\u044B\u043C: \u043D\u043E\u0432\u044B\u0439 \u0444\u0430\u0439\u043B \u0438\u0437 \u0431\u043E\u0442\u0430 \u0443\u0436\u0435 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D \u0437\u0434\u0435\u0441\u044C \u0438\u043B\u0438 \u043D\u0430 \u0434\u0440\u0443\u0433\u043E\u043C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435. \u0412\u043E\u0437\u044C\u043C\u0438\u0442\u0435 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0439 \u0444\u0430\u0439\u043B \u0443 \u0431\u043E\u0442\u0430 ${KEY_SOURCE_BOT} \u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u044C\u0442\u0435 \u0435\u0433\u043E.`
14734
- );
14735
- }
14736
- throw new DeliveryError(`${url2} \u043E\u0442\u0432\u0435\u0442\u0438\u043B ${response.status} ${response.statusText}`);
14737
- }
14738
- const body = await response.arrayBuffer();
14739
- const buffer = Buffer.from(body);
14740
- const sum = createHash("sha256").update(buffer).digest("hex");
14741
- if (bytes !== void 0 && buffer.length !== bytes) {
14742
- throw new DeliveryError(
14743
- looksLikeHtml(buffer) ? `\u043F\u043E \u0430\u0434\u0440\u0435\u0441\u0443 ${url2} \u043B\u0435\u0436\u0438\u0442 \u043D\u0435 \u0430\u0440\u0445\u0438\u0432, \u0430 \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u2014 \u0444\u0430\u0439\u043B \u0435\u0449\u0451 \u043D\u0435 \u0432\u044B\u043B\u043E\u0436\u0435\u043D.` : `\u0441\u043A\u0430\u0447\u0430\u043B\u043E\u0441\u044C ${buffer.length} \u0431\u0430\u0439\u0442 \u0432\u043C\u0435\u0441\u0442\u043E ${bytes} \u2014 \u0441\u0432\u044F\u0437\u044C \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0430\u0441\u044C, \u043F\u043E\u0432\u0442\u043E\u0440\u0438.`
14744
- );
14745
- }
14746
- if (sum !== sha256) {
14747
- throw new DeliveryError(
14748
- looksLikeHtml(buffer) ? `\u043F\u043E \u0430\u0434\u0440\u0435\u0441\u0443 ${url2} \u043B\u0435\u0436\u0438\u0442 \u043D\u0435 \u0430\u0440\u0445\u0438\u0432, \u0430 \u0432\u0435\u0431-\u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u2014 \u0444\u0430\u0439\u043B \u0435\u0449\u0451 \u043D\u0435 \u0432\u044B\u043B\u043E\u0436\u0435\u043D.` : `\u0441\u043A\u0430\u0447\u0430\u043D\u043D\u043E\u0435 \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u043B\u043E \u0441 \u043A\u043E\u043D\u0442\u0440\u043E\u043B\u044C\u043D\u043E\u0439 \u0441\u0443\u043C\u043C\u043E\u0439.
14749
- \u0436\u0434\u0430\u043B\u0438 ${sha256}
14750
- \u0432\u044B\u0448\u043B\u043E ${sum}
14751
- \u041F\u043E\u0432\u0442\u043E\u0440\u0438; \u0435\u0441\u043B\u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442\u0441\u044F \u2014 \u0444\u0430\u0439\u043B \u043F\u043E\u0434\u043C\u0435\u043D\u0451\u043D \u0438\u043B\u0438 \u0431\u0438\u0442\u044B\u0439, \u0441\u0442\u0430\u0432\u0438\u0442\u044C \u0435\u0433\u043E \u043D\u0435\u043B\u044C\u0437\u044F.`
14752
- );
14753
- }
14754
- writeFileSync(temp, buffer);
14755
- if (existsSync(dest)) {
14756
- throw new DeliveryError(
14757
- `\u0444\u0430\u0439\u043B ${dest} \u0443\u0436\u0435 \u0437\u0430\u043D\u044F\u043B \u0434\u0440\u0443\u0433\u043E\u0439 \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u2014 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0443\u0434\u0430\u043B\u044F\u043B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 \u043E\u043F\u0435\u0440\u0430\u0446\u0438\u044E \u0441 \u043D\u043E\u0432\u044B\u043C staging.`
14758
- );
14759
- }
14760
- renameSync(temp, dest);
14761
- report?.(`\u0441\u043A\u0430\u0447\u0430\u043D\u043E ${Math.round(buffer.length / 1024 / 1024)} \u041C\u0411`);
14762
- return { file: dest, bytes: buffer.length, sha256: sum };
14763
- } catch (trouble) {
14764
- rmSync(temp, { force: true });
14765
- if (trouble instanceof DeliveryError) throw trouble;
14766
- if (guard.signal.aborted) {
14767
- throw new DeliveryError(`${url2} \u043D\u0435 \u043E\u0442\u0434\u0430\u043B \u0444\u0430\u0439\u043B \u0437\u0430 ${Math.round(timeoutMs / 6e4)} \u043C\u0438\u043D.`);
14768
- }
14769
- throw new DeliveryError(`\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043A\u0430\u0447\u0430\u0442\u044C ${url2}: ${describeFailure(trouble)}`);
14770
- } finally {
14771
- clearTimeout(timer);
14772
- }
14773
- }
14774
- function looksLikeHtml(buffer) {
14775
- const start = buffer.subarray(0, 200).toString("latin1").trimStart().toLowerCase();
14776
- return start.startsWith("<!doctype html") || start.startsWith("<html");
14777
- }
14778
- var BLOCK = 512;
14779
- function tarEntries(data) {
14780
- const entries = [];
14781
- let offset = 0;
14782
- let longName = null;
14783
- let longTarget = null;
14784
- while (offset + BLOCK <= data.length) {
14785
- const header = data.subarray(offset, offset + BLOCK);
14786
- if (header.every((b) => b === 0)) break;
14787
- const name = fieldLine(header, 0, 100);
14788
- const mode = octal(header, 100, 8);
14789
- const size = fieldSize(header, 124, 12);
14790
- const type = String.fromCharCode(header[156]);
14791
- const target = fieldLine(header, 157, 100);
14792
- const prefix = fieldLine(header, 345, 155);
14793
- offset += BLOCK;
14794
- const body = data.subarray(offset, offset + size);
14795
- offset += Math.ceil(size / BLOCK) * BLOCK;
14796
- if (type === "L") {
14797
- longName = body.toString("utf8").replace(/\0+$/, "");
14798
- continue;
14799
- }
14800
- if (type === "K") {
14801
- longTarget = body.toString("utf8").replace(/\0+$/, "");
14802
- continue;
14803
- }
14804
- if (type === "x") {
14805
- const fields = parsePax(body);
14806
- if (fields["path"] !== void 0) longName = fields["path"];
14807
- if (fields["linkpath"] !== void 0) longTarget = fields["linkpath"];
14808
- continue;
14809
- }
14810
- if (type === "g") continue;
14811
- const full = longName ?? (prefix !== "" ? `${prefix}/${name}` : name);
14812
- const linkTarget = longTarget ?? target;
14813
- longName = null;
14814
- longTarget = null;
14815
- const kind = type === "5" ? "dir" : type === "2" ? "symlink" : type === "1" ? "hardlink" : "file";
14816
- if (type !== "" && type !== "0" && type !== "\0" && kind === "file" && type !== "7") continue;
14817
- entries.push({ name: full, kind, mode, body, target: linkTarget });
14818
- }
14819
- return entries;
14820
- }
14821
- function parsePax(body) {
14822
- const fields = {};
14823
- let offset = 0;
14824
- while (offset < body.length) {
14825
- const space = body.indexOf(32, offset);
14826
- if (space === -1) break;
14827
- const length = Number.parseInt(body.subarray(offset, space).toString("ascii"), 10);
14828
- if (!Number.isFinite(length) || length <= 0 || offset + length > body.length) break;
14829
- const entry = body.subarray(space + 1, offset + length).toString("utf8").replace(/\n$/, "");
14830
- const equal = entry.indexOf("=");
14831
- if (equal > 0) fields[entry.slice(0, equal)] = entry.slice(equal + 1);
14832
- offset += length;
14833
- }
14834
- return fields;
14835
- }
14836
- function fieldLine(header, start, length) {
14837
- const chunk = header.subarray(start, start + length);
14838
- const end = chunk.indexOf(0);
14839
- return chunk.subarray(0, end === -1 ? chunk.length : end).toString("utf8");
14840
- }
14841
- function octal(header, start, length) {
14842
- const text2 = fieldLine(header, start, length).trim();
14843
- const ch = Number.parseInt(text2, 8);
14844
- return Number.isFinite(ch) ? ch : 0;
14845
- }
14846
- function fieldSize(header, start, length) {
14847
- if ((header[start] & 128) !== 0) {
14848
- let ch = 0;
14849
- for (let i = start + 1; i < start + length; i += 1) ch = ch * 256 + header[i];
14850
- return ch;
14851
- }
14852
- return octal(header, start, length);
14853
- }
14854
- function inside(root, path2) {
14855
- if (path2 === "" || path2.startsWith("/") || path2.startsWith("\\")) return false;
14856
- if (/^[A-Za-z]:/.test(path2)) return false;
14857
- const target = resolve(root, path2);
14858
- const bottom = resolve(root);
14859
- return target === bottom || target.startsWith(bottom + sep);
14860
- }
14861
- function unpackArchive(archive, dest) {
14862
- const entries = tarEntries(gunzipSync(readFileSync(archive)));
14863
- if (entries.length === 0) throw new DeliveryError(`\u0430\u0440\u0445\u0438\u0432 ${archive} \u043F\u0443\u0441\u0442 \u2014 \u0440\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E`);
14864
- for (const entry of entries) {
14865
- if (!inside(dest, entry.name)) throw escapesRoot(archive, entry.name);
14866
- if (entry.kind === "symlink" || entry.kind === "hardlink") {
14867
- if (entry.target.startsWith("/") || entry.target.startsWith("\\") || /^[A-Za-z]:/.test(entry.target)) {
14868
- throw escapesRoot(archive, `${entry.name} \u2192 ${entry.target}`);
14869
- }
14870
- const from = entry.kind === "hardlink" ? entry.target : join(dirname(entry.name), entry.target);
14871
- if (!inside(dest, from)) throw escapesRoot(archive, `${entry.name} \u2192 ${entry.target}`);
14872
- }
14873
- }
14874
- for (const entry of entries) {
14875
- const path2 = join(dest, entry.name);
14876
- if (entry.kind === "dir") {
14877
- mkdirSync(path2, { recursive: true });
14878
- continue;
14879
- }
14880
- mkdirSync(dirname(path2), { recursive: true });
14881
- if (entry.kind === "symlink") {
14882
- rmSync(path2, { force: true });
14883
- symlinkSync(entry.target, path2);
14884
- continue;
14885
- }
14886
- if (entry.kind === "hardlink") {
14887
- writeFileSync(path2, readFileSync(join(dest, entry.target)));
14888
- continue;
14889
- }
14890
- writeFileSync(path2, entry.body);
14891
- if ((entry.mode & 73) !== 0) chmodSync(path2, entry.mode & 511);
14892
- }
14893
- }
14894
- function escapesRoot(archive, name) {
14895
- return new DeliveryError(
14896
- `\u0430\u0440\u0445\u0438\u0432 ${archive} \u043F\u044B\u0442\u0430\u0435\u0442\u0441\u044F \u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0444\u0430\u0439\u043B \u0437\u0430 \u043F\u0440\u0435\u0434\u0435\u043B\u044B \u043F\u0430\u043F\u043A\u0438 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438: \xAB${name}\xBB.
14897
- \u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0440\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u0430\u043D\u043E. \u0421\u043A\u0430\u0447\u0430\u0439 \u0437\u0430\u043D\u043E\u0432\u043E \u2014 \u0432\u0435\u0440\u043E\u044F\u0442\u043D\u0435\u0435 \u0432\u0441\u0435\u0433\u043E, \u0444\u0430\u0439\u043B \u043F\u043E\u0432\u0440\u0435\u0436\u0434\u0451\u043D \u0438\u043B\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u0451\u043D.`
14898
- );
14899
- }
14900
- function dirExists(path2) {
14901
- try {
14902
- return statSync(path2).isDirectory();
14903
- } catch {
14904
- return false;
14905
- }
14906
- }
14907
14796
  var BODY_ENTRY = "cli.js";
14908
14797
  var BODY_MANIFEST = "shell.json";
14909
14798
  function shellDir(home) {
@@ -15418,7 +15307,6 @@ function rollbackShell(home) {
15418
15307
  };
15419
15308
  }
15420
15309
  }
15421
- var SOURCES_FILE = "python-sources.json";
15422
15310
  var PUBLICATION_FILE2 = ".publication.json";
15423
15311
  var CANDIDATE_FILE = ".candidate.json";
15424
15312
  function recoverInterruptedPublication(dir) {
@@ -15482,26 +15370,6 @@ function recoverInterruptedPublication(dir) {
15482
15370
  \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.`
15483
15371
  };
15484
15372
  }
15485
- function platformKey(os = process.platform, arch2 = process.arch) {
15486
- if (os === "darwin") return arch2 === "arm64" ? "Darwin-arm64" : arch2 === "x64" ? "Darwin-x86_64" : null;
15487
- if (os === "win32") return arch2 === "x64" ? "Windows-AMD64" : null;
15488
- if (os === "linux") return arch2 === "x64" ? "Linux-x86_64" : arch2 === "arm64" ? "Linux-aarch64" : null;
15489
- return null;
15490
- }
15491
- function pythonSource(engineDir, key = platformKey()) {
15492
- if (key === null) return null;
15493
- const file2 = join(engineDir, SOURCES_FILE);
15494
- if (!existsSync(file2)) return null;
15495
- const table = JSON.parse(readFileSync(file2, "utf8"));
15496
- const row = table["platforms"][key];
15497
- if (row === void 0) return null;
15498
- return { url: table["base"] + row["tail"], sha256: row.sha256, mb: row["mb"] };
15499
- }
15500
- function pythonVersion(engineDir) {
15501
- const file2 = join(engineDir, SOURCES_FILE);
15502
- if (!existsSync(file2)) return "";
15503
- return JSON.parse(readFileSync(file2, "utf8"))["version"];
15504
- }
15505
15373
  async function installPayload(engine, opts, hooks = {}) {
15506
15374
  if (engine.pathSetByHand()) {
15507
15375
  return {
@@ -15600,7 +15468,7 @@ ${VOICE_CLEANER_NOTE}
15600
15468
  return {
15601
15469
  ok: true,
15602
15470
  text: `\u041D\u0430\u0447\u0438\u043D\u043A\u0430 ${manifest.version} \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043B\u0435\u043D\u0430: ${dest}
15603
- \u041E\u043D\u0430 \u0435\u0449\u0451 \u043D\u0435 \u0441\u0442\u0430\u043B\u0430 \u0440\u0430\u0431\u043E\u0447\u0435\u0439. \u0414\u0430\u043B\u044C\u0448\u0435 setup \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442 \u044D\u0442\u043E\u0442 \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442: \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442 python, \u0441\u043E\u0431\u0435\u0440\u0451\u0442 \u0435\u0433\u043E \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442 current.
15471
+ \u041E\u043D\u0430 \u0435\u0449\u0451 \u043D\u0435 \u0441\u0442\u0430\u043B\u0430 \u0440\u0430\u0431\u043E\u0447\u0435\u0439. \u0414\u0430\u043B\u044C\u0448\u0435 setup \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442 \u044D\u0442\u043E\u0442 \u043A\u0430\u043D\u0434\u0438\u0434\u0430\u0442: \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442 \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u043F\u043E \u0435\u0433\u043E \u0437\u0430\u043C\u043A\u0443 \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E\u0441\u043B\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u043E\u043A \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442 current.
15604
15472
  \u0420\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 \u0438 \u0447\u0438\u0441\u0442\u043A\u0430 \u0433\u043E\u043B\u043E\u0441\u0430 \u0432 \u043D\u0435\u0433\u043E \u043D\u0435 \u0432\u0445\u043E\u0434\u044F\u0442: \u043E\u043D\u0438 \u0441\u0442\u0430\u0432\u044F\u0442\u0441\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u043C\u0438 \u0448\u0430\u0433\u0430\u043C\u0438 \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u043F\u043E \u043F\u0440\u043E\u0441\u044C\u0431\u0435 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430.`
15605
15473
  };
15606
15474
  });
@@ -15640,113 +15508,6 @@ async function deliver(manifest, dest, intakeNear, key, baseCurrent, hooks) {
15640
15508
  discardVersionStage(stage);
15641
15509
  }
15642
15510
  }
15643
- async function installPython(engine, opts) {
15644
- try {
15645
- return await withVersionLock(
15646
- engine.versionsDir(),
15647
- async () => installPythonUnderLock(engine, opts)
15648
- );
15649
- } catch (trouble) {
15650
- if (trouble instanceof VersionLockError) return { ok: false, text: trouble.message };
15651
- return { ok: false, text: troubleText("Python \u0441\u043A\u0430\u0447\u0430\u0442\u044C \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C", trouble) };
15652
- }
15653
- }
15654
- async function installPythonUnderLock(engine, opts) {
15655
- const place = opts.version === void 0 ? engine.place() : engine.placeForVersion(opts.version);
15656
- if (place === null) {
15657
- return { ok: false, text: engine.whyNoEngine() };
15658
- }
15659
- const key = platformKey();
15660
- const source = pythonSource(place.engine, key);
15661
- if (source === null || source.sha256 === "") {
15662
- return {
15663
- ok: false,
15664
- text: `\u0413\u043E\u0442\u043E\u0432\u043E\u0439 \u043F\u043E\u0440\u0442\u0430\u0442\u0438\u0432\u043D\u043E\u0439 \u0441\u0431\u043E\u0440\u043A\u0438 Python \u043F\u043E\u0434 \u044D\u0442\u0443 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 (${key ?? `${process.platform}-${process.arch}`}) \u0443 \u043C\u0435\u043D\u044F \u043D\u0435\u0442.
15665
- \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u0441\u043A\u0430\u0436\u0438 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C Python 3.10 \u0438\u043B\u0438 \u043D\u043E\u0432\u0435\u0435 \u0441 python.org/downloads \u0438 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0443.`
15666
- };
15667
- }
15668
- const dest = join(engine.home(), "bin");
15669
- const version2 = pythonVersion(place.engine);
15670
- if (!opts.consent) {
15671
- return {
15672
- ok: false,
15673
- text: `\u041D\u0430 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435 \u043D\u0435\u0442 Python, \u0430 \u0431\u0435\u0437 \u043D\u0435\u0433\u043E \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C.
15674
- \u041C\u043E\u0433\u0443 \u0441\u043A\u0430\u0447\u0430\u0442\u044C \u043F\u043E\u0440\u0442\u0430\u0442\u0438\u0432\u043D\u044B\u0439 Python ${version2} (${source.mb} \u041C\u0411) \u0432 ${dest}: \u043E\u043D \u043B\u0435\u0436\u0438\u0442 \u043E\u0434\u043D\u043E\u0439 \u043F\u0430\u043F\u043A\u043E\u0439 \u0438 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043E\u0442\u0442\u0443\u0434\u0430 \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, PATH \u043D\u0435 \u043C\u0435\u043D\u044F\u0435\u0442\u0441\u044F, \u043F\u0440\u0430\u0432\u0430 \u0430\u0434\u043C\u0438\u043D\u0438\u0441\u0442\u0440\u0430\u0442\u043E\u0440\u0430 \u043D\u0435 \u043D\u0443\u0436\u043D\u044B, \u0443\u0434\u0430\u043B\u0438\u0442\u044C \u0437\u043D\u0430\u0447\u0438\u0442 \u0443\u0434\u0430\u043B\u0438\u0442\u044C \u043F\u0430\u043F\u043A\u0443. \u041F\u043E\u0434\u043F\u0438\u0441\u044C \u0444\u0430\u0439\u043B\u0430 \u0443 \u043C\u0435\u043D\u044F \u0435\u0441\u0442\u044C, \u0441\u0432\u0435\u0440\u044E.
15675
- \u0421\u043F\u0440\u043E\u0441\u0438 \u0443 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0438 \u043F\u043E\u0437\u043E\u0432\u0438 setup \u0441 what=all \u0438 consent=true.`
15676
- };
15677
- }
15678
- const pythonDir = join(dest, "python");
15679
- try {
15680
- safePythonParent(engine.home(), dest);
15681
- if (existsSync(pythonDir)) {
15682
- safePythonDirectory(engine.home(), pythonDir);
15683
- engine.forgetPython();
15684
- if (await engine.pythonTrouble() === null) {
15685
- return {
15686
- ok: true,
15687
- text: `Python ${version2} \u0443\u0436\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D: ${pythonDir}
15688
- \u041F\u043E\u0432\u0442\u043E\u0440\u043D\u043E \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u043A\u0430\u0447\u0438\u0432\u0430\u043B.`
15689
- };
15690
- }
15691
- throw new DeliveryError(
15692
- `\u0412 ${pythonDir} \u0443\u0436\u0435 \u0447\u0442\u043E-\u0442\u043E \u043B\u0435\u0436\u0438\u0442, \u043D\u043E \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u044D\u0442\u043E \u043D\u0435 \u0432\u044B\u0448\u043B\u043E. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435, \u043D\u0435 \u0438\u0434\u0451\u0442 \u043B\u0438 \u0434\u0440\u0443\u0433\u0430\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430, \u0437\u0430\u0442\u0435\u043C \u0443\u0434\u0430\u043B\u0438\u0442\u0435 \u043E\u0441\u0442\u0430\u0442\u043E\u043A \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435.`
15693
- );
15694
- }
15695
- const stage = createVersionStage(engine.versionsDir(), opts.version ?? "0-python-bootstrap");
15696
- try {
15697
- const archive = join(stage.root, "python.tar.gz");
15698
- await downloadFile({ url: source.url, dest: archive, sha256: source.sha256, key: null });
15699
- unpackArchive(archive, stage.payload);
15700
- const stagedPython = join(stage.payload, "python");
15701
- if (!dirExists(stagedPython)) throw new DeliveryError("\u0432 \u0430\u0440\u0445\u0438\u0432\u0435 Python \u043D\u0435\u0442 \u043F\u0430\u043F\u043A\u0438 python");
15702
- if (dirExists(pythonDir)) {
15703
- throw new DeliveryError(`\u043F\u0430\u043F\u043A\u0430 ${pythonDir} \u0437\u0430\u043D\u044F\u0442\u0430 \u0434\u0440\u0443\u0433\u0438\u043C \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u043E\u043C. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 setup \u043F\u043E\u0437\u0436\u0435`);
15704
- }
15705
- renameSync(stagedPython, pythonDir);
15706
- } finally {
15707
- discardVersionStage(stage);
15708
- }
15709
- } catch (trouble2) {
15710
- return { ok: false, text: troubleText("Python \u0441\u043A\u0430\u0447\u0430\u0442\u044C \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C", trouble2) };
15711
- }
15712
- engine.forgetPython();
15713
- const trouble = await engine.pythonTrouble();
15714
- if (trouble !== null) {
15715
- return {
15716
- ok: false,
15717
- text: `Python \u0441\u043A\u0430\u0447\u0430\u043B\u0441\u044F \u0438 \u0440\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u0430\u043B\u0441\u044F \u0432 ${pythonDir}, \u043D\u043E \u043D\u0435 \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u043B\u0441\u044F.
15718
- ${trouble}`
15719
- };
15720
- }
15721
- return {
15722
- ok: true,
15723
- text: `Python ${version2} \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D: ${pythonDir}
15724
- \u0412 \u0441\u0438\u0441\u0442\u0435\u043C\u0443 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u0442\u0430\u0432\u0438\u043B\u043E\u0441\u044C. \u0414\u0430\u043B\u044C\u0448\u0435 \u2014 setup \u0441 what=all.`
15725
- };
15726
- }
15727
- function safePythonParent(home, parent) {
15728
- mkdirSync(parent, { recursive: true });
15729
- const stat = lstatSync(parent);
15730
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
15731
- throw new DeliveryError(`${parent} \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043E\u0431\u044B\u0447\u043D\u043E\u0439 \u043F\u0430\u043F\u043A\u043E\u0439 \u0432\u043D\u0443\u0442\u0440\u0438 \u0434\u043E\u043C\u0430 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430`);
15732
- }
15733
- const homeReal = realpathSync(home);
15734
- const parentReal = realpathSync(parent);
15735
- if (!parentReal.startsWith(homeReal + sep)) {
15736
- throw new DeliveryError(`${parent} \u0432\u044B\u0448\u0435\u043B \u0437\u0430 \u043F\u0440\u0435\u0434\u0435\u043B\u044B \u0434\u043E\u043C\u0430 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430`);
15737
- }
15738
- }
15739
- function safePythonDirectory(home, pythonDir) {
15740
- const stat = lstatSync(pythonDir);
15741
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
15742
- throw new DeliveryError(`${pythonDir} \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043E\u0431\u044B\u0447\u043D\u043E\u0439 \u043F\u0430\u043F\u043A\u043E\u0439`);
15743
- }
15744
- const homeReal = realpathSync(home);
15745
- const pythonReal = realpathSync(pythonDir);
15746
- if (!pythonReal.startsWith(homeReal + sep)) {
15747
- throw new DeliveryError(`${pythonDir} \u0432\u044B\u0448\u0435\u043B \u0437\u0430 \u043F\u0440\u0435\u0434\u0435\u043B\u044B \u0434\u043E\u043C\u0430 \u041D\u0430\u043F\u0430\u0440\u043D\u0438\u043A\u0430`);
15748
- }
15749
- }
15750
15511
  function troubleText(what, trouble) {
15751
15512
  const tail2 = trouble instanceof DeliveryError ? trouble.message : trouble instanceof Error ? trouble.message : String(trouble);
15752
15513
  return `${what}, \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043C\u0435\u043D\u044F\u043B.
@@ -16924,6 +16685,10 @@ function cardRows(readiness) {
16924
16685
  }));
16925
16686
  return rows.map((row) => ({ ...row, mb: row.state === "ok" ? 0 : row.mb, say: rowWords(row.state, row.mb) }));
16926
16687
  }
16688
+ var FOR_MODEL = /\bsetup\b|\bwait\b|what=|consent|\bjob\b|Скажи человеку|Спроси человека|скажи ему|перескажи/i;
16689
+ function forHuman(line) {
16690
+ return FOR_MODEL.test(line) ? "" : line;
16691
+ }
16927
16692
  function setupCard(o) {
16928
16693
  const rows = cardRows(o.readiness);
16929
16694
  const missingMb = rows.filter((row) => row.state === "missing").reduce((all, row) => all + row.mb, 0);
@@ -16935,7 +16700,7 @@ function setupCard(o) {
16935
16700
  next = `\u041D\u0430\u043F\u0438\u0448\u0438 \xAB\u0441\u043A\u0430\u0447\u0430\u0439\xBB \u2014 \u0441\u043A\u0430\u0447\u0430\u044E ${o.noEngine ? "\u043C\u043E\u043D\u0442\u0430\u0436" : "Python"}.`;
16936
16701
  } else if (o.readiness === null) {
16937
16702
  title = o.title ?? TITLE_UNKNOWN;
16938
- next = o.note ?? "";
16703
+ next = forHuman(o.note ?? "");
16939
16704
  } else if (!ready) {
16940
16705
  title = missingMb ? `\u041D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0441\u043A\u0430\u0447\u0430\u0442\u044C ${weight(missingMb)} \u0434\u043B\u044F \u043F\u043E\u043B\u043D\u043E\u0446\u0435\u043D\u043D\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u044B` : "\u041D\u0435 \u0432\u0441\u0451 \u0433\u043E\u0442\u043E\u0432\u043E \u0434\u043B\u044F \u043F\u043E\u043B\u043D\u043E\u0446\u0435\u043D\u043D\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u044B";
16941
16706
  next = missingMb ? `\u041D\u0430\u043F\u0438\u0448\u0438 \xAB\u0441\u043A\u0430\u0447\u0430\u0439\xBB \u2014 \u0441\u043A\u0430\u0447\u0430\u044E ${weight(missingMb)}.` : "\u041D\u0430\u043F\u0438\u0448\u0438 \xAB\u0441\u043A\u0430\u0447\u0430\u0439\xBB \u2014 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E, \u0447\u0435\u0433\u043E \u043D\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442.";
@@ -18091,8 +17856,6 @@ function nameOrRefusal(candidate) {
18091
17856
  \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.`
18092
17857
  };
18093
17858
  }
18094
-
18095
- // src/setup-job.ts
18096
17859
  var SETUP_JOB_MARK = "setup-";
18097
17860
  function isSetupJob(id) {
18098
17861
  return id.startsWith(SETUP_JOB_MARK);
@@ -18107,13 +17870,108 @@ var CHECK = "\u041F\u0440\u043E\u0432\u0435\u0440\u044F\u044E, \u0447\u0442\u043
18107
17870
  var INSTALL_NEW = "\u0421\u0442\u0430\u0432\u043B\u044E \u043D\u043E\u0432\u0443\u044E \u0432\u0435\u0440\u0441\u0438\u044E";
18108
17871
  var MISSING = "\u0421\u0442\u0430\u0432\u043B\u044E \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u044E\u0449\u0435\u0435";
18109
17872
  var works = /* @__PURE__ */ new Map();
18110
- function running() {
18111
- for (const work of works.values()) if (work.ended === null) return work;
17873
+ var JOBS_DIR = "jobs";
17874
+ var JOBS_KEPT2 = 10;
17875
+ var BEAT_MS = 1e4;
17876
+ var STALE_MS = 6e4;
17877
+ var JOB_ID = /^setup-[\w-]{1,80}$/;
17878
+ function running(home, clock) {
17879
+ for (const work of works.values()) if (work.ended === null) return work.id;
17880
+ if (home === null) return null;
17881
+ for (const record2 of records(home)) {
17882
+ if (record2.ended === null && alive(record2, clock)) return record2.id;
17883
+ }
18112
17884
  return null;
18113
17885
  }
18114
- function startSetupJob(what, run, clock = Date.now) {
18115
- const already = running();
18116
- if (already !== null) return already.id;
17886
+ function records(home) {
17887
+ let names;
17888
+ try {
17889
+ names = readdirSync(join(home, JOBS_DIR)).filter((name) => name.endsWith(".json"));
17890
+ } catch {
17891
+ return [];
17892
+ }
17893
+ return names.sort().reverse().map((name) => load(home, name.slice(0, -".json".length))).filter((record2) => record2 !== null);
17894
+ }
17895
+ function load(home, id) {
17896
+ if (!JOB_ID.test(id)) return null;
17897
+ try {
17898
+ const value = JSON.parse(readFileSync(join(home, JOBS_DIR, `${id}.json`), "utf8"));
17899
+ if (value.id !== id || typeof value.what !== "string" || typeof value.started !== "number" || !Array.isArray(value.stages) || typeof value.at !== "number" || typeof value.pid !== "number" || typeof value.beat !== "number") return null;
17900
+ return {
17901
+ id,
17902
+ what: value.what,
17903
+ started: value.started,
17904
+ stages: value.stages.map(String),
17905
+ at: value.at,
17906
+ message: typeof value.message === "string" ? value.message : "",
17907
+ ended: value.ended ?? null,
17908
+ pid: value.pid,
17909
+ beat: value.beat
17910
+ };
17911
+ } catch {
17912
+ return null;
17913
+ }
17914
+ }
17915
+ function alive(record2, clock) {
17916
+ if (clock() - record2.beat > STALE_MS) return false;
17917
+ try {
17918
+ process.kill(record2.pid, 0);
17919
+ return true;
17920
+ } catch (trouble) {
17921
+ return trouble.code === "EPERM";
17922
+ }
17923
+ }
17924
+ function save(work, clock) {
17925
+ if (work.home === null) return;
17926
+ try {
17927
+ const dir = join(work.home, JOBS_DIR);
17928
+ mkdirSync(dir, { recursive: true });
17929
+ const file2 = join(dir, `${work.id}.json`);
17930
+ const temp = `${file2}.${process.pid}.tmp`;
17931
+ const record2 = {
17932
+ id: work.id,
17933
+ what: work.what,
17934
+ started: work.started,
17935
+ stages: work.stages,
17936
+ at: work.at,
17937
+ message: work.message,
17938
+ ended: work.ended,
17939
+ pid: process.pid,
17940
+ beat: clock()
17941
+ };
17942
+ writeFileSync(temp, JSON.stringify(record2), "utf8");
17943
+ renameSync(temp, file2);
17944
+ } catch {
17945
+ }
17946
+ }
17947
+ function prune2(home) {
17948
+ try {
17949
+ const dir = join(home, JOBS_DIR);
17950
+ const names = readdirSync(dir).filter((name) => JOB_ID.test(name.replace(/\.json$/, ""))).sort();
17951
+ for (const name of names.slice(0, Math.max(0, names.length - JOBS_KEPT2))) rmSync(join(dir, name), { force: true });
17952
+ } catch {
17953
+ }
17954
+ }
17955
+ function fromDisk(home, id, clock) {
17956
+ const record2 = load(home, id);
17957
+ if (record2 === null) return null;
17958
+ if (record2.ended !== null || alive(record2, clock)) return record2;
17959
+ return {
17960
+ ...record2,
17961
+ ended: {
17962
+ at: record2.beat,
17963
+ ending: {
17964
+ ok: false,
17965
+ trouble: "\u041E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u043E\u0441\u044C: \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u0430, \u043A\u043E\u0442\u043E\u0440\u0430\u044F \u0435\u0433\u043E \u0432\u0435\u043B\u0430, \u0437\u0430\u043A\u0440\u044B\u043B\u0430\u0441\u044C \u043F\u043E\u0441\u0440\u0435\u0434\u0438 \u0440\u0430\u0431\u043E\u0442\u044B. \u041F\u0440\u0435\u0436\u043D\u044F\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u043D\u0430 \u043C\u0435\u0441\u0442\u0435, \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043C\u043E\u0436\u043D\u043E.",
17966
+ advice: "\u041D\u0430\u043F\u0438\u0448\u0438 \xAB\u043E\u0431\u043D\u043E\u0432\u0438\xBB \u0435\u0449\u0451 \u0440\u0430\u0437 \u2014 \u043D\u0430\u0447\u043D\u0443 \u0437\u0430\u043D\u043E\u0432\u043E.",
17967
+ report: `\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u043E\u0431\u043E\u043B\u043E\u0447\u043A\u0438 ${record2.pid}, \u043A\u043E\u0442\u043E\u0440\u044B\u0439 \u0432\u0451\u043B \u0437\u0430\u0434\u0430\u0447\u0443, \u043F\u0440\u043E\u043F\u0430\u043B \u043D\u0430 \u044D\u0442\u0430\u043F\u0435 \xAB${record2.stages[record2.at - 1] ?? CHECK}\xBB.`
17968
+ }
17969
+ }
17970
+ };
17971
+ }
17972
+ function startSetupJob(what, run, clock = Date.now, home = null) {
17973
+ const already = running(home, clock);
17974
+ if (already !== null) return already;
18117
17975
  const started = clock();
18118
17976
  const id = `${SETUP_JOB_MARK}${new Date(started).toISOString().replace(/[:.]/g, "-")}`;
18119
17977
  const work = {
@@ -18124,23 +17982,31 @@ function startSetupJob(what, run, clock = Date.now) {
18124
17982
  at: 1,
18125
17983
  message: "",
18126
17984
  ended: null,
18127
- done: Promise.resolve()
17985
+ done: Promise.resolve(),
17986
+ home
18128
17987
  };
18129
17988
  const control = {
18130
17989
  plan(stages) {
18131
17990
  work.stages = stages;
18132
17991
  work.at = Math.min(work.at, stages.length);
17992
+ save(work, clock);
18133
17993
  },
18134
17994
  go(stage) {
18135
17995
  const index = work.stages.indexOf(stage);
18136
17996
  if (index >= 0) work.at = index + 1;
18137
17997
  work.message = "";
17998
+ save(work, clock);
18138
17999
  },
18139
18000
  line(text2) {
18140
18001
  work.message = text2;
18002
+ save(work, clock);
18141
18003
  }
18142
18004
  };
18143
18005
  works.set(id, work);
18006
+ if (home !== null) prune2(home);
18007
+ save(work, clock);
18008
+ const beat = home === null ? null : setInterval(() => save(work, clock), BEAT_MS);
18009
+ beat?.unref();
18144
18010
  work.done = collectJobInstallFaults(id, () => run(control).catch((trouble) => {
18145
18011
  noteInstallFault("update-job", trouble);
18146
18012
  return {
@@ -18151,15 +18017,17 @@ function startSetupJob(what, run, clock = Date.now) {
18151
18017
  };
18152
18018
  })).then((ending) => {
18153
18019
  work.ended = { at: clock(), ending };
18020
+ if (beat !== null) clearInterval(beat);
18021
+ save(work, clock);
18154
18022
  });
18155
18023
  return id;
18156
18024
  }
18157
18025
  function quiet(line) {
18158
18026
  return line.replace(/^⚠\s*/, "").replace(/^Что делать:/, "\u0421\u043E\u0432\u0435\u0442:");
18159
18027
  }
18160
- function setupJobText(id, clock = Date.now) {
18161
- const work = works.get(id);
18162
- if (work === void 0) return null;
18028
+ function setupJobText(id, clock = Date.now, home = null) {
18029
+ const work = works.get(id) ?? (home === null ? null : fromDisk(home, id, clock));
18030
+ if (work === null) return null;
18163
18031
  const head2 = `\u0417\u0430\u0434\u0430\u043D\u0438\u0435 ${work.id}: ${work.what}`;
18164
18032
  if (work.ended === null) {
18165
18033
  const sec2 = Math.max(0, Math.round((clock() - work.started) / 1e3));
@@ -18183,8 +18051,8 @@ function setupJobText(id, clock = Date.now) {
18183
18051
  `\u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: ${ending.advice}`
18184
18052
  ].join("\n");
18185
18053
  }
18186
- function setupJobReport(id) {
18187
- const text2 = setupJobText(id);
18054
+ function setupJobReport(id, home = null) {
18055
+ const text2 = setupJobText(id, Date.now, home);
18188
18056
  return text2 === null ? null : readJob(text2);
18189
18057
  }
18190
18058
  function weightExact(bytes) {
@@ -18265,9 +18133,10 @@ async function runUpdate(job, deps) {
18265
18133
  updated = true;
18266
18134
  steps.push({ report: outcome.text });
18267
18135
  } else {
18136
+ const cause = causeOf(outcome.text);
18268
18137
  steps.push({
18269
18138
  report: outcome.text,
18270
- trouble: `\u041D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u0434\u0432\u0438\u0436\u043A\u0430 \u043D\u0435 \u0432\u0441\u0442\u0430\u043B\u0430 \u2014 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043F\u0440\u0435\u0436\u043D\u044F\u044F${engineWas === null ? "" : ` ${engineWas}`}, \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043C\u043E\u0436\u043D\u043E.`
18139
+ trouble: `\u041D\u043E\u0432\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u0434\u0432\u0438\u0436\u043A\u0430 \u043D\u0435 \u0432\u0441\u0442\u0430\u043B\u0430${cause === "" ? "" : `: ${cause}`}. \u0420\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043F\u0440\u0435\u0436\u043D\u044F\u044F${engineWas === null ? "" : ` ${engineWas}`}, \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043C\u043E\u0436\u043D\u043E.`
18271
18140
  });
18272
18141
  }
18273
18142
  }
@@ -18283,6 +18152,11 @@ async function runUpdate(job, deps) {
18283
18152
  if (updated) return { ok: true, say: SAY_RESTART, report };
18284
18153
  return { ok: true, say: installed ? SAY_INSTALLED : SAY_NOTHING, report };
18285
18154
  }
18155
+ function causeOf(text2) {
18156
+ const line = text2.split("\n").map((one) => one.trim()).find((one) => one !== "" && !/^(Новая версия|Версия) \S+ не встала/.test(one) && !/^Что делать:/.test(one)) ?? "";
18157
+ const phrase = (line.split(/\.\s|\s—\s|;\s/)[0] ?? "").replace(/\s*\([^)]*\)/g, "").replace(/\.$/, "").trim();
18158
+ return phrase === "" ? "" : phrase[0].toLowerCase() + phrase.slice(1);
18159
+ }
18286
18160
  async function deliverMissing(job, deps) {
18287
18161
  const answer = await deps.installMissing();
18288
18162
  if (answer.type === "error") {
@@ -18382,25 +18256,15 @@ async function installStep(engine, asked, key, baseUrl, tell) {
18382
18256
  `\u041A\u0430\u043D\u0434\u0438\u0434\u0430\u0442 ${candidate.version} \u0443\u0436\u0435 \u043F\u043E\u0434\u0433\u043E\u0442\u043E\u0432\u043B\u0435\u043D, \u043D\u043E \u0435\u0449\u0451 \u043D\u0435 \u0440\u0430\u0431\u043E\u0447\u0438\u0439. \u0421\u043F\u0440\u043E\u0441\u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u0441\u043E\u0431\u0440\u0430\u0442\u044C \u0435\u0433\u043E \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0438 \u043F\u043E\u0437\u043E\u0432\u0438 setup \u0441 consent=true.`
18383
18257
  );
18384
18258
  }
18385
- let bootstrapText = "";
18386
18259
  const extras = step === "all" ? "later" : "now";
18387
- const activated = await continuePreparedCandidate(engine, candidate, async ({ candidate: locked }) => {
18388
- if (await engine.pythonTrouble() === null) {
18389
- return { ok: true, text: "Python \u0443\u0436\u0435 \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D; \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0441\u043A\u0430\u0447\u0438\u0432\u0430\u043B." };
18390
- }
18391
- const python2 = await installPythonUnderLock(engine, { consent, version: locked.version });
18392
- bootstrapText = python2.text;
18393
- return python2;
18394
- }, void 0, extras);
18260
+ const activated = await continuePreparedCandidate(engine, candidate, void 0, extras);
18395
18261
  if (!activated.ok) return toolError(activated.text);
18396
18262
  engine.forgetPlace();
18397
- if (step === "python") {
18398
- return toolSuccess(`${bootstrapText}
18399
- ${activated.text}`.trim());
18400
- }
18263
+ engine.forgetPython();
18264
+ if (step === "python") return toolSuccess(activated.text);
18401
18265
  if (step === "all") {
18402
18266
  const ready = `${activated.text}
18403
- \u041A\u0430\u043D\u0434\u0438\u0434\u0430\u0442 \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u043B ffmpeg, \u0441\u043E\u0431\u0440\u0430\u043B \u0441\u0432\u043E\u0451 \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435 \u0438 \u043F\u0440\u043E\u0448\u0451\u043B env-check. \u0412\u0441\u0451 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E\u0435 \u0433\u043E\u0442\u043E\u0432\u043E.`;
18267
+ \u041A\u0430\u043D\u0434\u0438\u0434\u0430\u0442 \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u043B \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u043F\u043E \u0441\u0432\u043E\u0435\u043C\u0443 \u0437\u0430\u043C\u043A\u0443 \u0438 \u043F\u0440\u043E\u0448\u0451\u043B env-check. \u0412\u0441\u0451 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E\u0435 \u0433\u043E\u0442\u043E\u0432\u043E.`;
18404
18268
  const later = await installStep(engine, asked, key, baseUrl, tell);
18405
18269
  if (later.type === "error") {
18406
18270
  return toolSuccess(
@@ -18414,16 +18278,15 @@ ${activated.text}`.trim());
18414
18278
  ${EXTRAS_LATER}`), later]);
18415
18279
  }
18416
18280
  }
18417
- if (step === "python" || step === "all") {
18418
- if (await engine.pythonTrouble() !== null) {
18419
- const result = await installPython(engine, { consent });
18420
- if (!result.ok) return toolError(result.text);
18421
- if (step === "python") return toolSuccess(result.text);
18422
- return toolSuccess(result.text + "\n\u041F\u043E\u0437\u043E\u0432\u0438 setup \u0441 what=all \u0435\u0449\u0451 \u0440\u0430\u0437.");
18423
- }
18424
- }
18425
18281
  const place = engine.place();
18426
18282
  if (place === null) return toolError(engine.whyNoEngine());
18283
+ if (consent && await engine.pythonTrouble() !== null) {
18284
+ try {
18285
+ await engine.bringPython(place);
18286
+ } catch (trouble) {
18287
+ return toolError(`\u041F\u0438\u0442\u043E\u043D \u043C\u043E\u043D\u0442\u0430\u0436\u0430 \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C: ${describeFailure(trouble)}. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0432\u0442\u043E\u0440\u0438 setup.`);
18288
+ }
18289
+ }
18427
18290
  const common = [
18428
18291
  "--pack",
18429
18292
  place.pack,
@@ -18659,7 +18522,7 @@ async function lookAround(engine, model, key, baseUrl) {
18659
18522
  shell: shellNews,
18660
18523
  ...o.noEngine === true ? { noEngine: true } : {},
18661
18524
  ...o.noPython === true ? { noPython: true } : {},
18662
- note: firstLine(said),
18525
+ note: o.note ?? firstLine(said),
18663
18526
  shellVersion: VERSION
18664
18527
  }));
18665
18528
  const shell = updateShell(engine, false, key, baseUrl, NEWS_POINTER_MS, toShell);
@@ -18671,10 +18534,10 @@ async function lookAround(engine, model, key, baseUrl) {
18671
18534
  });
18672
18535
  return card(together([toolSuccess(payload.text), await shell]), { noEngine: true });
18673
18536
  }
18674
- if (await engine.pythonTrouble() !== null) {
18675
- const python = await installPython(engine, { consent: false });
18537
+ const noPython = await engine.pythonTrouble();
18538
+ if (noPython !== null) {
18676
18539
  return card(together([
18677
- toolSuccess(python.text),
18540
+ toolSuccess(noPython),
18678
18541
  await updateEngine(engine, false, key, baseUrl, NEWS_POINTER_MS, toEngine),
18679
18542
  await shell
18680
18543
  ]), { noPython: true });
@@ -18686,7 +18549,7 @@ async function lookAround(engine, model, key, baseUrl) {
18686
18549
  ]);
18687
18550
  return card(
18688
18551
  together([newsForModel(shellNews, engineNews), machine.result, news, own]),
18689
- { readiness: machine.readiness }
18552
+ { readiness: machine.readiness, note: firstLine(together([machine.result, news, own])) }
18690
18553
  );
18691
18554
  }
18692
18555
  function newsForModel(shell, engine) {
@@ -18716,8 +18579,8 @@ function startUpdate(engine, what, key, baseUrl) {
18716
18579
  what,
18717
18580
  pointerMs: ASKED_POINTER_MS,
18718
18581
  installMissing: () => installMissing(engine, key, baseUrl, void 0)
18719
- }));
18720
- const report = setupJobReport(id);
18582
+ }), Date.now, engine.home());
18583
+ const report = setupJobReport(id, engine.home());
18721
18584
  return toolSuccess(
18722
18585
  `\u0417\u0430\u0434\u0430\u0447\u0430 ${id}: ${report?.what ?? title}.
18723
18586
  \u0418\u0434\u0451\u0442 \u043D\u0430 \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F: \u043F\u0440\u043E\u0432\u0435\u0440\u044E, \u0447\u0442\u043E \u0432\u044B\u0448\u043B\u043E, \u0441\u043A\u0430\u0447\u0430\u044E \u0438 \u043F\u043E\u0441\u0442\u0430\u0432\u043B\u044E. \u041F\u043E\u043B\u043E\u0441\u0430 \u0443 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0441\u0430\u043C\u0430. \u041F\u043E\u0437\u043E\u0432\u0438 wait \u0441 job "` + id + '" \u2014 \u0434\u043E\u0436\u0434\u0451\u0442\u0441\u044F \u0438 \u0441\u043A\u0430\u0436\u0435\u0442, \u0447\u0435\u043C \u043A\u043E\u043D\u0447\u0438\u043B\u043E\u0441\u044C; setup \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E \u043D\u0435 \u0437\u043E\u0432\u0438. \u0413\u043E\u0442\u043E\u0432\u043E \u2014 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0436\u0438 \u0441\u0442\u0440\u043E\u043A\u0443 \xAB\u0427\u0435\u043B\u043E\u0432\u0435\u043A\u0443\xBB.',
@@ -18749,10 +18612,8 @@ async function askBeforeHeavy(engine, step, model, key, baseUrl) {
18749
18612
  });
18750
18613
  return toolSuccess(payload.text);
18751
18614
  }
18752
- if (await engine.pythonTrouble() !== null) {
18753
- const python = await installPython(engine, { consent: false });
18754
- return toolSuccess(python.text);
18755
- }
18615
+ const noPython = await engine.pythonTrouble();
18616
+ if (noPython !== null) return toolSuccess(noPython);
18756
18617
  return asToolResult(await engine.command([
18757
18618
  "env-install",
18758
18619
  "--pack",
@@ -18857,7 +18718,7 @@ var TRIALS_STEP = "\u041F\u043E\u0442\u043E\u043C \u0441\u043E\u0431\u0435\u0440
18857
18718
  async function followWork(engine, asked, pace = realPace, tell) {
18858
18719
  const ask2 = async () => {
18859
18720
  if (!isSetupJob(asked.job)) return engine.command(["job", asked.job]);
18860
- const text2 = setupJobText(asked.job);
18721
+ const text2 = setupJobText(asked.job, Date.now, engine.home());
18861
18722
  return text2 !== null ? { ok: true, text: text2 } : {
18862
18723
  ok: false,
18863
18724
  text: `\u0417\u0430\u0434\u0430\u0447\u0438 ${asked.job} \u043D\u0435\u0442: \u0435\u0451 \u0432\u0451\u043B \u043F\u0440\u0435\u0436\u043D\u0438\u0439 \u0437\u0430\u043F\u0443\u0441\u043A \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B. \u0427\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C: \u043F\u043E\u0437\u043E\u0432\u0438 setup \u2014 \u043E\u043D \u0441\u043A\u0430\u0436\u0435\u0442, \u0447\u0442\u043E \u0441\u0442\u043E\u0438\u0442 \u0441\u0435\u0439\u0447\u0430\u0441.`
@@ -23165,4 +23026,4 @@ ${inTable}`, file2);
23165
23026
  };
23166
23027
  }
23167
23028
 
23168
- export { CrashReporter, DEFAULT_HOME, DeliveryError, Engine, ForeignBlockError, INSIDE_PYTHON, INSTALL_STEPS, KEEP_COPIES, KEEP_VERSIONS, LONG_STEPS, MANIFEST_TIMEOUT_MS, MANIFEST_URL, MARKER, MIN_ENGINE, McpServer, NaparnikApiClient, NaparnikAuthError, NaparnikClientError, NaparnikConfigError, NaparnikError, NaparnikInsufficientBalanceError, NaparnikScopeError, NaparnikServerError, NaparnikTimeoutError, PAUSE_HOURS, SERVER_NAME, SOURCES_FILE, START_SEC, StdioServerTransport, TOOL_SEC, USER_AGENT, VERSION, WHERE_TO_GET_KEY, Wakefulness, buildAll, buildBlock, buildResources, bundleUrlFor, chooseBody, compare, configPath, currentMachineFingerprint, decide, decideShell, defineTool, deploy, downloadFile, fetchManifest, fetchShellManifest, fileOrRefusal, findBlock, fromHost, fromNpxCache, handOverToBody, inside, install, installPayload, installPython, installStep, installedShell, jsonSchemaOf, keyFromBlock, keyInTable, lastLine, loadConfig, manifestUrlFor, markChecked, markChecked2, markCurrent2 as markCurrent, parseEngineManifest, parseFailure, path, platformKey, previousVersion2 as previousVersion, probe, pythonSource, readConfig, realPace, refusalFor, remove, reportInstallFaults, rollback, rollbackShell, shellDir, systemBlocker, tarEntries, tidyOld2 as tidyOld, timeToCheck, timeToCheck2, unpackArchive, updateEngine, watchProcessCrashes, withArgs };
23029
+ export { CrashReporter, DEFAULT_HOME, DeliveryError, Engine, ForeignBlockError, INSTALL_STEPS, KEEP_COPIES, KEEP_VERSIONS, LOCK_FILE, LONG_STEPS, MANIFEST_TIMEOUT_MS, MANIFEST_URL, MARKER, MIN_ENGINE, McpServer, NaparnikApiClient, NaparnikAuthError, NaparnikClientError, NaparnikConfigError, NaparnikError, NaparnikInsufficientBalanceError, NaparnikScopeError, NaparnikServerError, NaparnikTimeoutError, PAUSE_HOURS, SERVER_NAME, START_SEC, StdioServerTransport, TOOL_SEC, USER_AGENT, VERSION, WHERE_TO_GET_KEY, Wakefulness, bringPython, buildAll, buildBlock, buildResources, bundleUrlFor, chooseBody, compare, configPath, currentMachineFingerprint, decide, decideShell, defineTool, depFolderName, deploy, downloadFile, fetchManifest, fetchShellManifest, fileOrRefusal, findBlock, fromHost, fromNpxCache, handOverToBody, inside, install, installPayload, installStep, installedShell, jsonSchemaOf, keyFromBlock, keyInTable, lastLine, loadConfig, lockedPython, manifestUrlFor, markChecked, markChecked2, markCurrent2 as markCurrent, parseEngineManifest, parseFailure, path, platformKey, previousVersion2 as previousVersion, probe, readConfig, realPace, refusalFor, remove, reportInstallFaults, rollback, rollbackShell, shellDir, systemBlocker, tarEntries, tidyOld2 as tidyOld, timeToCheck, timeToCheck2, unpackArchive, updateEngine, watchProcessCrashes, withArgs };