@ru-code/ru-code 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +807 -577
- package/dist/preflight.mjs +4 -0
- package/package.json +2 -1
package/dist/bin.mjs
CHANGED
|
@@ -15294,20 +15294,7 @@ const layer$20 = sync$2(NetService, make$34);
|
|
|
15294
15294
|
|
|
15295
15295
|
//#endregion
|
|
15296
15296
|
//#region package.json
|
|
15297
|
-
var version = "
|
|
15298
|
-
|
|
15299
|
-
//#endregion
|
|
15300
|
-
//#region ../../packages/contracts/src/appName.ts
|
|
15301
|
-
/**
|
|
15302
|
-
* APP_NAME — single source of truth for the application's short name.
|
|
15303
|
-
*
|
|
15304
|
-
* Used to derive every user-visible identifier that should be branded with
|
|
15305
|
-
* the app: the home directory (`~/.${APP_NAME}`), env var prefixes, temp
|
|
15306
|
-
* file prefixes, etc. Change here to rebrand everywhere.
|
|
15307
|
-
*/
|
|
15308
|
-
const APP_NAME$1 = "ru-fork";
|
|
15309
|
-
/** `~/.${APP_NAME}` directory name (without leading `/`). */
|
|
15310
|
-
const APP_HOME_DIRNAME = `.${APP_NAME$1}`;
|
|
15297
|
+
var version = "3.0.0";
|
|
15311
15298
|
|
|
15312
15299
|
//#endregion
|
|
15313
15300
|
//#region ../../packages/contracts/src/baseSchemas.ts
|
|
@@ -15772,6 +15759,17 @@ var OpenError = class extends TaggedErrorClass()("OpenError", {
|
|
|
15772
15759
|
*/
|
|
15773
15760
|
const APP_NAME = "Ru Code";
|
|
15774
15761
|
/**
|
|
15762
|
+
* APP_HOME_SLUG — kebab-case slug used to derive on-disk identifiers: the
|
|
15763
|
+
* per-user home dot-directory (`~/.ru-fork`), env-var prefixes and temp-file
|
|
15764
|
+
* prefixes. Distinct from {@link APP_NAME} (the display name) because these
|
|
15765
|
+
* paths must be filesystem- and shell-safe and stable across re-skins. This is
|
|
15766
|
+
* the one and only place the home-dir slug lives; the installer and the app's
|
|
15767
|
+
* base-dir resolution both derive from it so they cannot diverge.
|
|
15768
|
+
*/
|
|
15769
|
+
const APP_HOME_SLUG = "ru-fork";
|
|
15770
|
+
/** `~/.${APP_HOME_SLUG}` directory name (without leading `/`), e.g. `.ru-fork`. */
|
|
15771
|
+
const APP_HOME_DIRNAME = `.${APP_HOME_SLUG}`;
|
|
15772
|
+
/**
|
|
15775
15773
|
* CLI_NAME — name of the underlying CLI binary / ACP provider. This is the one
|
|
15776
15774
|
* and only place the CLI name literal lives. Used for the spawned binary, the
|
|
15777
15775
|
* provider kind id, user-facing labels and config-directory derivation.
|
|
@@ -20040,10 +20038,13 @@ var ServerConfig = class ServerConfig extends Service()("t3/config/ServerConfig"
|
|
|
20040
20038
|
const baseDir = typeof baseDirOrPrefix === "string" ? baseDirOrPrefix : yield* fs.makeTempDirectoryScoped({ prefix: baseDirOrPrefix.prefix });
|
|
20041
20039
|
const derivedPaths = yield* deriveServerPaths(baseDir, devUrl);
|
|
20042
20040
|
yield* ensureServerDirectories(derivedPaths);
|
|
20041
|
+
const path = yield* Path;
|
|
20043
20042
|
return {
|
|
20044
20043
|
logLevel: "Error",
|
|
20045
20044
|
cwd,
|
|
20046
20045
|
baseDir,
|
|
20046
|
+
cliJs: path.join(baseDir, "cli.js"),
|
|
20047
|
+
cliConfigDir: path.join(path.dirname(baseDir), CLI_FOLDER),
|
|
20047
20048
|
...derivedPaths,
|
|
20048
20049
|
mode: "web",
|
|
20049
20050
|
autoBootstrapProjectFromCwd: false,
|
|
@@ -25118,6 +25119,449 @@ const resolveBaseDir = fn(function* (raw) {
|
|
|
25118
25119
|
return resolve(yield* expandHomePath$4(raw.trim()));
|
|
25119
25120
|
});
|
|
25120
25121
|
|
|
25122
|
+
//#endregion
|
|
25123
|
+
//#region src/ru-fork/preflight/common/constants.ts
|
|
25124
|
+
/** CLI config dir name, e.g. ".qwen". */
|
|
25125
|
+
const CLI_DIR = CLI_CONFIG_DIRNAME;
|
|
25126
|
+
/** Our app home dir name, e.g. ".ru-fork". */
|
|
25127
|
+
const APP_DIR = APP_HOME_DIRNAME;
|
|
25128
|
+
const NODE_ENGINE_RANGE = "^22.16 || ^23.11 || >=24.10";
|
|
25129
|
+
/** Minimum CLI version; "" disables the version check (presence only). */
|
|
25130
|
+
const CLI_MIN_VERSION = "0.13.1";
|
|
25131
|
+
const CLI_PROBE_TIMEOUT_MS = 15e3;
|
|
25132
|
+
const GIT_PROBE_TIMEOUT_MS = 5e3;
|
|
25133
|
+
|
|
25134
|
+
//#endregion
|
|
25135
|
+
//#region src/ru-fork/preflight/common/messages.ts
|
|
25136
|
+
const MESSAGES = {
|
|
25137
|
+
CONFIG_NOT_FOUND: "Каталог конфигурации CLI не найден. Проверены пути:",
|
|
25138
|
+
SOURCES_DISAGREE: "Два источника расположения cli.js не совпадают (bin и .install-dir указывают на разное).",
|
|
25139
|
+
INSTALL_DIR_NOWHERE: "Файл .install-dir указывает на несуществующий cli.js.",
|
|
25140
|
+
CLI_NOT_FOUND: "cli.js не найден. Проверены пути:",
|
|
25141
|
+
NODE_OK: "Node.js {found} ✓",
|
|
25142
|
+
NODE_LOW: `Node.js {found} установлен, требуется ${NODE_ENGINE_RANGE}. Обновите: https://nodejs.org/`,
|
|
25143
|
+
GIT_OK: "Git {found} ✓",
|
|
25144
|
+
GIT_MISSING: "Git не найден на PATH. Установите Git: https://git-scm.com/downloads",
|
|
25145
|
+
GIT_BROKEN: "Git установлен, но `git --version` завершилась с ошибкой или превысила тайм-аут.",
|
|
25146
|
+
CLI_OK: "CLI {found} ✓",
|
|
25147
|
+
CLI_BROKEN: "CLI установлен, но `cli.js --version` завершилась с ошибкой или превысила тайм-аут.",
|
|
25148
|
+
CLI_LOW: `CLI {found} установлен, требуется ≥ ${CLI_MIN_VERSION}. Обновите: npm install -g ${CLI_NPM_PACKAGE}@latest`,
|
|
25149
|
+
FOOTER_FAIL: "Установите недостающие компоненты и перезапустите."
|
|
25150
|
+
};
|
|
25151
|
+
|
|
25152
|
+
//#endregion
|
|
25153
|
+
//#region src/ru-fork/preflight/common/render.ts
|
|
25154
|
+
const render = (template, values) => template.replace(/\{(\w+)\}/g, (_, k) => values[k] ?? `{${k}}`);
|
|
25155
|
+
|
|
25156
|
+
//#endregion
|
|
25157
|
+
//#region src/ru-fork/preflight/common/version.ts
|
|
25158
|
+
const parseVersion = (input) => {
|
|
25159
|
+
const match = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(input);
|
|
25160
|
+
if (!match) return null;
|
|
25161
|
+
return [
|
|
25162
|
+
Number(match[1]),
|
|
25163
|
+
Number(match[2]),
|
|
25164
|
+
Number(match[3] ?? 0)
|
|
25165
|
+
];
|
|
25166
|
+
};
|
|
25167
|
+
/** Three-component numeric `actual >= minimum`. */
|
|
25168
|
+
const isAtLeast = (actual, minimum) => {
|
|
25169
|
+
const actualParts = parseVersion(actual);
|
|
25170
|
+
const minimumParts = parseVersion(minimum);
|
|
25171
|
+
if (!actualParts || !minimumParts) return false;
|
|
25172
|
+
const [actualMajor, actualMinor, actualPatch] = actualParts;
|
|
25173
|
+
const [minimumMajor, minimumMinor, minimumPatch] = minimumParts;
|
|
25174
|
+
if (actualMajor !== minimumMajor) return actualMajor > minimumMajor;
|
|
25175
|
+
if (actualMinor !== minimumMinor) return actualMinor > minimumMinor;
|
|
25176
|
+
return actualPatch >= minimumPatch;
|
|
25177
|
+
};
|
|
25178
|
+
/** Does `actual` satisfy an `^X.Y[.Z] || >=X.Y[.Z]` range? */
|
|
25179
|
+
const satisfiesRange = (actual, range) => {
|
|
25180
|
+
const actualParts = parseVersion(actual);
|
|
25181
|
+
if (!actualParts) return false;
|
|
25182
|
+
const [actualMajor, actualMinor, actualPatch] = actualParts;
|
|
25183
|
+
return range.split("||").some((rawDisjunct) => {
|
|
25184
|
+
const disjunct = rawDisjunct.trim();
|
|
25185
|
+
const caretMatch = /^\^(\d+)\.(\d+)(?:\.(\d+))?$/.exec(disjunct);
|
|
25186
|
+
if (caretMatch) return actualMajor === Number(caretMatch[1]) && actualMinor >= Number(caretMatch[2]);
|
|
25187
|
+
const atLeastMatch = /^>=(\d+)\.(\d+)(?:\.(\d+))?$/.exec(disjunct);
|
|
25188
|
+
if (atLeastMatch) {
|
|
25189
|
+
const targetMajor = Number(atLeastMatch[1]);
|
|
25190
|
+
const targetMinor = Number(atLeastMatch[2]);
|
|
25191
|
+
const targetPatch = Number(atLeastMatch[3] ?? 0);
|
|
25192
|
+
if (actualMajor !== targetMajor) return actualMajor > targetMajor;
|
|
25193
|
+
if (actualMinor !== targetMinor) return actualMinor > targetMinor;
|
|
25194
|
+
return actualPatch >= targetPatch;
|
|
25195
|
+
}
|
|
25196
|
+
return false;
|
|
25197
|
+
});
|
|
25198
|
+
};
|
|
25199
|
+
/**
|
|
25200
|
+
* Extract "X.Y[.Z]" from arbitrary output. ASCII digits survive any codepage,
|
|
25201
|
+
* so this parses correctly even when surrounding text is mojibake — which is
|
|
25202
|
+
* why probing `node cli.js --version` directly is reliable where a PATH shim
|
|
25203
|
+
* through cmd.exe was not.
|
|
25204
|
+
*/
|
|
25205
|
+
const extractVersion = (output) => {
|
|
25206
|
+
const match = /\d+\.\d+(?:\.\d+)?/.exec(output);
|
|
25207
|
+
return match ? match[0] : null;
|
|
25208
|
+
};
|
|
25209
|
+
|
|
25210
|
+
//#endregion
|
|
25211
|
+
//#region src/ru-fork/preflight/common/diagnostics.ts
|
|
25212
|
+
const POSIX_ENV_KEYS = [
|
|
25213
|
+
"HOME",
|
|
25214
|
+
"NODE_PATH",
|
|
25215
|
+
"TRY_TO_FIND_CLI"
|
|
25216
|
+
];
|
|
25217
|
+
const WINDOWS_ENV_KEYS = [
|
|
25218
|
+
"MSYSTEM",
|
|
25219
|
+
"USERPROFILE",
|
|
25220
|
+
"HOME",
|
|
25221
|
+
"HOMEDRIVE",
|
|
25222
|
+
"HOMEPATH",
|
|
25223
|
+
"APPDATA",
|
|
25224
|
+
"LOCALAPPDATA",
|
|
25225
|
+
"NODE_PATH",
|
|
25226
|
+
"TRY_TO_FIND_CLI"
|
|
25227
|
+
];
|
|
25228
|
+
const envKeys = () => process.platform === "win32" ? WINDOWS_ENV_KEYS : POSIX_ENV_KEYS;
|
|
25229
|
+
const envValue = (name) => process.env[name] ?? "(unset)";
|
|
25230
|
+
const collectDiagnostics = () => {
|
|
25231
|
+
let username = "?";
|
|
25232
|
+
try {
|
|
25233
|
+
username = NodeOS.userInfo().username;
|
|
25234
|
+
} catch {}
|
|
25235
|
+
return [
|
|
25236
|
+
`platform : ${process.platform} ${NodeOS.release()}`,
|
|
25237
|
+
`version : ${NodeOS.version()}`,
|
|
25238
|
+
`arch : ${process.arch}`,
|
|
25239
|
+
`node : v${process.versions.node} (${process.execPath})`,
|
|
25240
|
+
`home : ${NodeOS.homedir()}`,
|
|
25241
|
+
`user : ${username}`,
|
|
25242
|
+
`cwd : ${process.cwd()}`,
|
|
25243
|
+
...envKeys().map((name) => `env ${name} = ${envValue(name)}`),
|
|
25244
|
+
`PATH : ${envValue("PATH")}`
|
|
25245
|
+
];
|
|
25246
|
+
};
|
|
25247
|
+
|
|
25248
|
+
//#endregion
|
|
25249
|
+
//#region src/ru-fork/preflight/common/expand.ts
|
|
25250
|
+
const expand = (pattern, env = process.env) => {
|
|
25251
|
+
const replaced = pattern.replace(/\{home\}/g, NodeOS.homedir()).replace(/\{appdata\}/g, env.APPDATA ?? "").replace(/\{localappdata\}/g, env.LOCALAPPDATA ?? "");
|
|
25252
|
+
return path$1.normalize(replaced);
|
|
25253
|
+
};
|
|
25254
|
+
|
|
25255
|
+
//#endregion
|
|
25256
|
+
//#region src/ru-fork/preflight/common/fs.ts
|
|
25257
|
+
const isDir = (p) => {
|
|
25258
|
+
try {
|
|
25259
|
+
return NFS.statSync(p).isDirectory();
|
|
25260
|
+
} catch {
|
|
25261
|
+
return false;
|
|
25262
|
+
}
|
|
25263
|
+
};
|
|
25264
|
+
const isFile = (p) => {
|
|
25265
|
+
try {
|
|
25266
|
+
return NFS.statSync(p).isFile();
|
|
25267
|
+
} catch {
|
|
25268
|
+
return false;
|
|
25269
|
+
}
|
|
25270
|
+
};
|
|
25271
|
+
/** Read the bin path recorded in `<configDir>/.install-dir`, or "" if absent. */
|
|
25272
|
+
const readInstallRecord = (configDir) => {
|
|
25273
|
+
const file = path$1.join(configDir, ".install-dir");
|
|
25274
|
+
try {
|
|
25275
|
+
return NFS.readFileSync(file, "utf8").replace(/\r/g, "").trim();
|
|
25276
|
+
} catch {
|
|
25277
|
+
return "";
|
|
25278
|
+
}
|
|
25279
|
+
};
|
|
25280
|
+
|
|
25281
|
+
//#endregion
|
|
25282
|
+
//#region src/ru-fork/preflight/common/probe.ts
|
|
25283
|
+
const probeVersion = (command, args, timeoutMs) => {
|
|
25284
|
+
const probe = spawnSync(command, [...args], {
|
|
25285
|
+
timeout: timeoutMs,
|
|
25286
|
+
encoding: "utf8",
|
|
25287
|
+
windowsHide: true
|
|
25288
|
+
});
|
|
25289
|
+
if (probe.error) {
|
|
25290
|
+
const errorCode = probe.error.code;
|
|
25291
|
+
if (errorCode === "ENOENT") return {
|
|
25292
|
+
ok: false,
|
|
25293
|
+
reason: "missing"
|
|
25294
|
+
};
|
|
25295
|
+
if (errorCode === "ETIMEDOUT") return {
|
|
25296
|
+
ok: false,
|
|
25297
|
+
reason: "timeout"
|
|
25298
|
+
};
|
|
25299
|
+
return {
|
|
25300
|
+
ok: false,
|
|
25301
|
+
reason: "broken"
|
|
25302
|
+
};
|
|
25303
|
+
}
|
|
25304
|
+
if (probe.status === null && probe.signal) return {
|
|
25305
|
+
ok: false,
|
|
25306
|
+
reason: "timeout"
|
|
25307
|
+
};
|
|
25308
|
+
if (probe.status !== 0) return {
|
|
25309
|
+
ok: false,
|
|
25310
|
+
reason: "broken"
|
|
25311
|
+
};
|
|
25312
|
+
const version = extractVersion(`${probe.stdout ?? ""}${probe.stderr ?? ""}`);
|
|
25313
|
+
return version ? {
|
|
25314
|
+
ok: true,
|
|
25315
|
+
version
|
|
25316
|
+
} : {
|
|
25317
|
+
ok: false,
|
|
25318
|
+
reason: "broken"
|
|
25319
|
+
};
|
|
25320
|
+
};
|
|
25321
|
+
|
|
25322
|
+
//#endregion
|
|
25323
|
+
//#region src/ru-fork/preflight/paths.ts
|
|
25324
|
+
/**
|
|
25325
|
+
* WHERE the CLI keeps its config dir — always `{home}/$CLI_DIR`, identical
|
|
25326
|
+
* across platforms. First `isDir()` match wins. Any alternative runtime/bin
|
|
25327
|
+
* location is NEVER searched here — it is read from the CLI's own `.install-dir`
|
|
25328
|
+
* record inside the config dir.
|
|
25329
|
+
*/
|
|
25330
|
+
const CONFIG = {
|
|
25331
|
+
darwin: [`{home}/${CLI_CONFIG_DIRNAME}`],
|
|
25332
|
+
linux: [`{home}/${CLI_CONFIG_DIRNAME}`],
|
|
25333
|
+
win32: [`{home}/${CLI_CONFIG_DIRNAME}`]
|
|
25334
|
+
};
|
|
25335
|
+
/**
|
|
25336
|
+
* FALLBACK bin probe — for CLI installers that drop `cli.js` outside
|
|
25337
|
+
* `$CLI_DIR/bin`. OFF in production; enabled via the `TRY_TO_FIND_CLI` env flag
|
|
25338
|
+
* for local testing. Each entry is probed for an existing `cli.js`.
|
|
25339
|
+
*
|
|
25340
|
+
* The `<cli>` segments are placeholders — fill them with your real local test
|
|
25341
|
+
* layout. With them unfilled, nothing resolves (so production, where the flag
|
|
25342
|
+
* is unset, can only use the two authoritative sources).
|
|
25343
|
+
*/
|
|
25344
|
+
const CLI_BIN_PATHS = {
|
|
25345
|
+
darwin: [
|
|
25346
|
+
"{home}/Library/pnpm/global/5/node_modules/@qwen-code/qwen-code/cli.js",
|
|
25347
|
+
"{home}/.npm-global/lib/node_modules/<cli>/cli.js",
|
|
25348
|
+
"/opt/homebrew/lib/node_modules/<cli>/cli.js"
|
|
25349
|
+
],
|
|
25350
|
+
linux: ["{home}/.local/share/pnpm/global/5/node_modules/<cli>/cli.js", "/usr/local/lib/node_modules/<cli>/cli.js"],
|
|
25351
|
+
win32: ["{appdata}/npm/node_modules/<cli>/cli.js", "{localappdata}/<cli>/cli.js"]
|
|
25352
|
+
};
|
|
25353
|
+
|
|
25354
|
+
//#endregion
|
|
25355
|
+
//#region src/ru-fork/preflight/common/resolve.ts
|
|
25356
|
+
const toPlatformKey = (platform) => platform === "darwin" ? "darwin" : platform === "win32" ? "win32" : "linux";
|
|
25357
|
+
/**
|
|
25358
|
+
* Step 3 → FALLBACK: probe CLI_BIN_PATHS. Returns the resolved cli.js, or the
|
|
25359
|
+
* list of probed paths (when nothing matched) so the caller can STOP + report.
|
|
25360
|
+
*/
|
|
25361
|
+
const tryFallbackBinPaths = (platformKey, env, tryFindCli) => {
|
|
25362
|
+
if (!tryFindCli) return { stop: ["TRY_TO_FIND_CLI выключен"] };
|
|
25363
|
+
const patterns = CLI_BIN_PATHS[platformKey] ?? [];
|
|
25364
|
+
if (patterns.length === 0) return { stop: ["CLI_BIN_PATHS пуст"] };
|
|
25365
|
+
const probedPaths = [];
|
|
25366
|
+
for (const pattern of patterns) {
|
|
25367
|
+
const candidate = expand(pattern, env);
|
|
25368
|
+
probedPaths.push(candidate);
|
|
25369
|
+
if (isFile(candidate)) return { cliJs: candidate };
|
|
25370
|
+
}
|
|
25371
|
+
return { stop: probedPaths };
|
|
25372
|
+
};
|
|
25373
|
+
const resolveCli = (options = {}) => {
|
|
25374
|
+
const platform = options.platform ?? process.platform;
|
|
25375
|
+
const env = options.env ?? process.env;
|
|
25376
|
+
const tryFindCli = options.tryFindCli ?? false;
|
|
25377
|
+
const platformKey = toPlatformKey(platform);
|
|
25378
|
+
const checkedConfigPaths = [];
|
|
25379
|
+
let configDir;
|
|
25380
|
+
for (const pattern of CONFIG[platformKey] ?? []) {
|
|
25381
|
+
const candidate = expand(pattern, env);
|
|
25382
|
+
checkedConfigPaths.push(candidate);
|
|
25383
|
+
if (isDir(candidate)) {
|
|
25384
|
+
configDir = candidate;
|
|
25385
|
+
break;
|
|
25386
|
+
}
|
|
25387
|
+
}
|
|
25388
|
+
if (!configDir) return {
|
|
25389
|
+
ok: false,
|
|
25390
|
+
reason: MESSAGES.CONFIG_NOT_FOUND,
|
|
25391
|
+
details: checkedConfigPaths
|
|
25392
|
+
};
|
|
25393
|
+
const homeBinCli = path$1.join(configDir, "bin", "cli.js");
|
|
25394
|
+
const recordedBinDir = readInstallRecord(configDir);
|
|
25395
|
+
const recordedCli = recordedBinDir ? path$1.join(recordedBinDir, "cli.js") : "";
|
|
25396
|
+
const resolveStandard = () => ({
|
|
25397
|
+
ok: true,
|
|
25398
|
+
configDir,
|
|
25399
|
+
cliJs: homeBinCli,
|
|
25400
|
+
source: "standard",
|
|
25401
|
+
ourRoot: path$1.join(path$1.dirname(configDir), APP_DIR)
|
|
25402
|
+
});
|
|
25403
|
+
const resolveFromInstallRecord = () => ({
|
|
25404
|
+
ok: true,
|
|
25405
|
+
configDir,
|
|
25406
|
+
cliJs: recordedCli,
|
|
25407
|
+
source: "install-dir",
|
|
25408
|
+
ourRoot: path$1.join(path$1.dirname(path$1.dirname(recordedBinDir)), APP_DIR)
|
|
25409
|
+
});
|
|
25410
|
+
const resolveFromFallback = () => {
|
|
25411
|
+
const fallback = tryFallbackBinPaths(platformKey, env, tryFindCli);
|
|
25412
|
+
if ("cliJs" in fallback) return {
|
|
25413
|
+
ok: true,
|
|
25414
|
+
configDir,
|
|
25415
|
+
cliJs: fallback.cliJs,
|
|
25416
|
+
source: "fallback",
|
|
25417
|
+
ourRoot: path$1.join(NodeOS.homedir(), APP_DIR)
|
|
25418
|
+
};
|
|
25419
|
+
return {
|
|
25420
|
+
ok: false,
|
|
25421
|
+
reason: MESSAGES.CLI_NOT_FOUND,
|
|
25422
|
+
details: fallback.stop,
|
|
25423
|
+
configDir
|
|
25424
|
+
};
|
|
25425
|
+
};
|
|
25426
|
+
if (isFile(homeBinCli)) {
|
|
25427
|
+
if (recordedCli && path$1.normalize(recordedCli) !== path$1.normalize(homeBinCli)) return {
|
|
25428
|
+
ok: false,
|
|
25429
|
+
reason: MESSAGES.SOURCES_DISAGREE,
|
|
25430
|
+
details: [`bin: ${homeBinCli}`, `.install-dir: ${recordedCli}`],
|
|
25431
|
+
configDir
|
|
25432
|
+
};
|
|
25433
|
+
return resolveStandard();
|
|
25434
|
+
}
|
|
25435
|
+
if (recordedBinDir) {
|
|
25436
|
+
if (!isFile(recordedCli)) return {
|
|
25437
|
+
ok: false,
|
|
25438
|
+
reason: MESSAGES.INSTALL_DIR_NOWHERE,
|
|
25439
|
+
details: [`.install-dir → ${recordedBinDir}`],
|
|
25440
|
+
configDir
|
|
25441
|
+
};
|
|
25442
|
+
return path$1.basename(recordedBinDir) === "bin" && path$1.basename(path$1.dirname(recordedBinDir)) === CLI_DIR ? resolveFromInstallRecord() : resolveFromFallback();
|
|
25443
|
+
}
|
|
25444
|
+
return resolveFromFallback();
|
|
25445
|
+
};
|
|
25446
|
+
|
|
25447
|
+
//#endregion
|
|
25448
|
+
//#region src/ru-fork/preflight/common/checks.ts
|
|
25449
|
+
/** Node cannot be "missing" here — we are running on it. Validate the engine
|
|
25450
|
+
* range against the process's own version. */
|
|
25451
|
+
const checkNodeEngine = () => {
|
|
25452
|
+
const found = `v${process.versions.node}`;
|
|
25453
|
+
if (!satisfiesRange(process.versions.node, NODE_ENGINE_RANGE)) return {
|
|
25454
|
+
ok: false,
|
|
25455
|
+
line: render(MESSAGES.NODE_LOW, { found })
|
|
25456
|
+
};
|
|
25457
|
+
return {
|
|
25458
|
+
ok: true,
|
|
25459
|
+
line: render(MESSAGES.NODE_OK, { found })
|
|
25460
|
+
};
|
|
25461
|
+
};
|
|
25462
|
+
const checkGit = () => {
|
|
25463
|
+
const gitProbe = probeVersion("git", ["--version"], GIT_PROBE_TIMEOUT_MS);
|
|
25464
|
+
if (!gitProbe.ok) return {
|
|
25465
|
+
ok: false,
|
|
25466
|
+
line: gitProbe.reason === "missing" ? MESSAGES.GIT_MISSING : MESSAGES.GIT_BROKEN
|
|
25467
|
+
};
|
|
25468
|
+
return {
|
|
25469
|
+
ok: true,
|
|
25470
|
+
line: render(MESSAGES.GIT_OK, { found: gitProbe.version })
|
|
25471
|
+
};
|
|
25472
|
+
};
|
|
25473
|
+
/** Probe the resolved cli.js directly with the running node interpreter. */
|
|
25474
|
+
const checkCli = (cliJs) => {
|
|
25475
|
+
const cliProbe = probeVersion(process.execPath, [cliJs, "--version"], CLI_PROBE_TIMEOUT_MS);
|
|
25476
|
+
if (!cliProbe.ok) return {
|
|
25477
|
+
ok: false,
|
|
25478
|
+
line: MESSAGES.CLI_BROKEN
|
|
25479
|
+
};
|
|
25480
|
+
if (CLI_MIN_VERSION && !isAtLeast(cliProbe.version, CLI_MIN_VERSION)) return {
|
|
25481
|
+
ok: false,
|
|
25482
|
+
line: render(MESSAGES.CLI_LOW, { found: cliProbe.version })
|
|
25483
|
+
};
|
|
25484
|
+
return {
|
|
25485
|
+
ok: true,
|
|
25486
|
+
line: render(MESSAGES.CLI_OK, { found: cliProbe.version })
|
|
25487
|
+
};
|
|
25488
|
+
};
|
|
25489
|
+
|
|
25490
|
+
//#endregion
|
|
25491
|
+
//#region src/ru-fork/preflight/preflight-startup.ts
|
|
25492
|
+
/**
|
|
25493
|
+
* Startup preflight — the launch-time twin of the installer preflight.
|
|
25494
|
+
*
|
|
25495
|
+
* Runs the SAME shared resolver + checks as `preflight-install.ts` (the `common/`
|
|
25496
|
+
* core), so install-time and launch-time agree on cli.js / config dir / app root.
|
|
25497
|
+
* This is the whole point of the design: one deterministic resolver, run by the
|
|
25498
|
+
* installer AND the app, so the two never diverge (see
|
|
25499
|
+
* `ru-fork-instrumental/changes/common-preflight.md`).
|
|
25500
|
+
*
|
|
25501
|
+
* Two entry points, because the daemon launcher runs the checks in the PARENT
|
|
25502
|
+
* and spawns the child with `--no-preflight-check`:
|
|
25503
|
+
*
|
|
25504
|
+
* - {@link resolveStartupCli} — ALWAYS run. Resolves cli.js / config dir /
|
|
25505
|
+
* app root via the §10 state machine. The app cannot function without a
|
|
25506
|
+
* cli.js, so a failed resolution STOPs startup with a readable Russian
|
|
25507
|
+
* report (exactly like the installer) and a `PreflightFailedError`. Its
|
|
25508
|
+
* result is threaded into ServerConfig (base dir, CLI config dir) and the
|
|
25509
|
+
* direct-node CLI spawn (cli.js path).
|
|
25510
|
+
*
|
|
25511
|
+
* - {@link runStartupChecks} — gated by `--no-preflight-check`. node engine +
|
|
25512
|
+
* git + `node cli.js --version`, identical to the installer's steps 4-6.
|
|
25513
|
+
*
|
|
25514
|
+
* No shell / Git-Bash / terminal check: the CLI is spawned via `node cli.js`
|
|
25515
|
+
* directly (never bash / cmd / PowerShell), so the launch shell is irrelevant.
|
|
25516
|
+
*/
|
|
25517
|
+
var PreflightFailedError = class extends TaggedError("PreflightFailedError") {};
|
|
25518
|
+
const tryFindCliEnabled = () => process.env.TRY_TO_FIND_CLI !== "0";
|
|
25519
|
+
/**
|
|
25520
|
+
* Resolve cli.js / config dir / app root. Always runs (the daemon child skips
|
|
25521
|
+
* the version checks but still needs these paths). STOPs on a failed resolution.
|
|
25522
|
+
*/
|
|
25523
|
+
const resolveStartupCli = gen(function* () {
|
|
25524
|
+
for (const line of collectDiagnostics()) yield* logInfo(line);
|
|
25525
|
+
const resolution = resolveCli({ tryFindCli: tryFindCliEnabled() });
|
|
25526
|
+
if (!resolution.ok) {
|
|
25527
|
+
yield* logError(resolution.reason);
|
|
25528
|
+
for (const detail of resolution.details) yield* logError(` ${detail}`);
|
|
25529
|
+
yield* logError(MESSAGES.FOOTER_FAIL);
|
|
25530
|
+
return yield* new PreflightFailedError({ failures: [resolution.reason, ...resolution.details] });
|
|
25531
|
+
}
|
|
25532
|
+
yield* logInfo(`CLI config dir : ${resolution.configDir}`);
|
|
25533
|
+
yield* logInfo(`CLI bin (cli.js): ${resolution.cliJs} [source: ${resolution.source}]`);
|
|
25534
|
+
yield* logInfo(`app root : ${resolution.ourRoot}`);
|
|
25535
|
+
return {
|
|
25536
|
+
cliJs: resolution.cliJs,
|
|
25537
|
+
configDir: resolution.configDir,
|
|
25538
|
+
ourRoot: resolution.ourRoot,
|
|
25539
|
+
source: resolution.source
|
|
25540
|
+
};
|
|
25541
|
+
});
|
|
25542
|
+
/**
|
|
25543
|
+
* node engine + git + `node cli.js --version`. Gated by `--no-preflight-check`.
|
|
25544
|
+
* Aggregates failures and STOPs with a `PreflightFailedError` if any check fails
|
|
25545
|
+
* — identical behaviour to the installer's aggregated steps 4-6.
|
|
25546
|
+
*
|
|
25547
|
+
* The probes are synchronous (`spawnSync` via the shared `probe.ts`); acceptable
|
|
25548
|
+
* here because this runs once at startup, before the server begins serving.
|
|
25549
|
+
*/
|
|
25550
|
+
const runStartupChecks = (cliJs) => gen(function* () {
|
|
25551
|
+
const results = [
|
|
25552
|
+
checkNodeEngine(),
|
|
25553
|
+
checkGit(),
|
|
25554
|
+
checkCli(cliJs)
|
|
25555
|
+
];
|
|
25556
|
+
for (const result of results) if (result.ok) yield* logInfo(result.line);
|
|
25557
|
+
else yield* logError(result.line);
|
|
25558
|
+
const failures = results.filter((result) => !result.ok).map((result) => result.line);
|
|
25559
|
+
if (failures.length > 0) {
|
|
25560
|
+
yield* logError(MESSAGES.FOOTER_FAIL);
|
|
25561
|
+
return yield* new PreflightFailedError({ failures });
|
|
25562
|
+
}
|
|
25563
|
+
});
|
|
25564
|
+
|
|
25121
25565
|
//#endregion
|
|
25122
25566
|
//#region src/ru-fork/local-startup/defaults.ts
|
|
25123
25567
|
const DESKTOP_RUNTIME_MODE = "desktop";
|
|
@@ -26139,7 +26583,7 @@ const sharedServerCommandFlags = {
|
|
|
26139
26583
|
};
|
|
26140
26584
|
const authLocationFlags = sharedServerLocationFlags;
|
|
26141
26585
|
const resolveOptionPrecedence = (...values) => firstSomeOf(values);
|
|
26142
|
-
const resolveServerConfig = (flags, cliLogLevel, options) => gen(function* () {
|
|
26586
|
+
const resolveServerConfig = (flags, cliLogLevel, cli, options) => gen(function* () {
|
|
26143
26587
|
const { findAvailablePort } = yield* NetService;
|
|
26144
26588
|
const path = yield* Path;
|
|
26145
26589
|
const fs = yield* FileSystem;
|
|
@@ -26173,7 +26617,7 @@ const resolveServerConfig = (flags, cliLogLevel, options) => gen(function* () {
|
|
|
26173
26617
|
});
|
|
26174
26618
|
const devUrl = getOrElse(resolveOptionPrecedence(normalizedFlags.devUrl, fromUndefinedOr(env.devUrl)), () => void 0);
|
|
26175
26619
|
const basePath = normalizeBasePath(getOrUndefined(resolveOptionPrecedence(normalizedFlags.baseUrl, fromUndefinedOr(env.baseUrl))));
|
|
26176
|
-
const baseDir = yield* resolveBaseDir(getOrUndefined(resolveOptionPrecedence(normalizedFlags.baseDir, fromUndefinedOr(env.t3Home), fromUndefinedOr(bootstrap?.t3Home))));
|
|
26620
|
+
const baseDir = yield* resolveBaseDir(getOrUndefined(resolveOptionPrecedence(normalizedFlags.baseDir, fromUndefinedOr(env.t3Home), fromUndefinedOr(bootstrap?.t3Home))) ?? cli.ourRoot);
|
|
26177
26621
|
const rawCwd = getOrElse(normalizedFlags.cwd, () => process.cwd());
|
|
26178
26622
|
const cwd = path.resolve(yield* expandHomePath$4(rawCwd.trim()));
|
|
26179
26623
|
yield* fs.makeDirectory(cwd, { recursive: true });
|
|
@@ -26193,6 +26637,8 @@ const resolveServerConfig = (flags, cliLogLevel, options) => gen(function* () {
|
|
|
26193
26637
|
port,
|
|
26194
26638
|
cwd,
|
|
26195
26639
|
baseDir,
|
|
26640
|
+
cliJs: cli.cliJs,
|
|
26641
|
+
cliConfigDir: cli.configDir,
|
|
26196
26642
|
...derivedPaths,
|
|
26197
26643
|
host,
|
|
26198
26644
|
staticDir,
|
|
@@ -26205,22 +26651,25 @@ const resolveServerConfig = (flags, cliLogLevel, options) => gen(function* () {
|
|
|
26205
26651
|
basePath
|
|
26206
26652
|
};
|
|
26207
26653
|
});
|
|
26208
|
-
const resolveCliAuthConfig = (flags, cliLogLevel) =>
|
|
26209
|
-
|
|
26210
|
-
|
|
26211
|
-
|
|
26212
|
-
|
|
26213
|
-
|
|
26214
|
-
|
|
26215
|
-
|
|
26216
|
-
|
|
26217
|
-
|
|
26218
|
-
|
|
26219
|
-
|
|
26220
|
-
|
|
26221
|
-
|
|
26222
|
-
|
|
26223
|
-
|
|
26654
|
+
const resolveCliAuthConfig = (flags, cliLogLevel) => gen(function* () {
|
|
26655
|
+
const cli = yield* resolveStartupCli;
|
|
26656
|
+
return yield* resolveServerConfig({
|
|
26657
|
+
mode: none$4(),
|
|
26658
|
+
port: none$4(),
|
|
26659
|
+
host: none$4(),
|
|
26660
|
+
baseDir: flags.baseDir,
|
|
26661
|
+
cwd: none$4(),
|
|
26662
|
+
devUrl: flags.devUrl ?? none$4(),
|
|
26663
|
+
baseUrl: flags.baseUrl ?? none$4(),
|
|
26664
|
+
noBrowser: none$4(),
|
|
26665
|
+
noPreflightCheck: none$4(),
|
|
26666
|
+
bootstrapFd: none$4(),
|
|
26667
|
+
autoBootstrapProjectFromCwd: none$4(),
|
|
26668
|
+
logWebSocketEvents: none$4(),
|
|
26669
|
+
injectExtraPaths: none$4(),
|
|
26670
|
+
windowsUseBashFor: none$4()
|
|
26671
|
+
}, cliLogLevel, cli);
|
|
26672
|
+
});
|
|
26224
26673
|
const DurationShorthandPattern = /^(?<value>\d+)(?<unit>ms|s|m|h|d|w)$/i;
|
|
26225
26674
|
const parseDurationInput = (value) => {
|
|
26226
26675
|
const trimmed = value.trim();
|
|
@@ -31698,6 +32147,11 @@ const initSpawnPolicy = (input) => {
|
|
|
31698
32147
|
if (prepend.length === 0) return;
|
|
31699
32148
|
process.env.PATH = [...prepend, process.env.PATH ?? ""].filter(Boolean).join(isWin ? ";" : ":");
|
|
31700
32149
|
};
|
|
32150
|
+
const buildCliSpawn = (cliJs, args) => ({
|
|
32151
|
+
command: process.execPath,
|
|
32152
|
+
args: [cliJs, ...args],
|
|
32153
|
+
shell: false
|
|
32154
|
+
});
|
|
31701
32155
|
const resolveSpawn = (bin, args, options) => {
|
|
31702
32156
|
if (process.platform !== "win32") return {
|
|
31703
32157
|
command: bin,
|
|
@@ -34779,523 +35233,6 @@ const projectCommand = make$35("project").pipe(withDescription$1("Manage project
|
|
|
34779
35233
|
projectRenameCommand
|
|
34780
35234
|
]));
|
|
34781
35235
|
|
|
34782
|
-
//#endregion
|
|
34783
|
-
//#region ../../packages/shared/src/semver.ts
|
|
34784
|
-
const SEMVER_NUMBER_SEGMENT = /^\d+$/;
|
|
34785
|
-
function normalizeSemverVersion(version) {
|
|
34786
|
-
const [main, prerelease] = version.trim().split("-", 2);
|
|
34787
|
-
const segments = (main ?? "").split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
|
|
34788
|
-
if (segments.length === 2) segments.push("0");
|
|
34789
|
-
return prerelease ? `${segments.join(".")}-${prerelease}` : segments.join(".");
|
|
34790
|
-
}
|
|
34791
|
-
function parseSemver(value) {
|
|
34792
|
-
const [main = "", prerelease] = normalizeSemverVersion(value).replace(/^v/, "").split("-", 2);
|
|
34793
|
-
const segments = main.split(".");
|
|
34794
|
-
if (segments.length !== 3) return null;
|
|
34795
|
-
const [majorSegment, minorSegment, patchSegment] = segments;
|
|
34796
|
-
if (majorSegment === void 0 || minorSegment === void 0 || patchSegment === void 0) return null;
|
|
34797
|
-
if (!SEMVER_NUMBER_SEGMENT.test(majorSegment) || !SEMVER_NUMBER_SEGMENT.test(minorSegment) || !SEMVER_NUMBER_SEGMENT.test(patchSegment)) return null;
|
|
34798
|
-
const major = Number.parseInt(majorSegment, 10);
|
|
34799
|
-
const minor = Number.parseInt(minorSegment, 10);
|
|
34800
|
-
const patch = Number.parseInt(patchSegment, 10);
|
|
34801
|
-
if (![
|
|
34802
|
-
major,
|
|
34803
|
-
minor,
|
|
34804
|
-
patch
|
|
34805
|
-
].every(Number.isInteger)) return null;
|
|
34806
|
-
return {
|
|
34807
|
-
major,
|
|
34808
|
-
minor,
|
|
34809
|
-
patch,
|
|
34810
|
-
prerelease: prerelease?.split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0) ?? []
|
|
34811
|
-
};
|
|
34812
|
-
}
|
|
34813
|
-
function comparePrereleaseIdentifier(left, right) {
|
|
34814
|
-
const leftNumeric = SEMVER_NUMBER_SEGMENT.test(left);
|
|
34815
|
-
const rightNumeric = SEMVER_NUMBER_SEGMENT.test(right);
|
|
34816
|
-
if (leftNumeric && rightNumeric) return Number.parseInt(left, 10) - Number.parseInt(right, 10);
|
|
34817
|
-
if (leftNumeric) return -1;
|
|
34818
|
-
if (rightNumeric) return 1;
|
|
34819
|
-
return left.localeCompare(right);
|
|
34820
|
-
}
|
|
34821
|
-
function compareSemverVersions(left, right) {
|
|
34822
|
-
const parsedLeft = parseSemver(left);
|
|
34823
|
-
const parsedRight = parseSemver(right);
|
|
34824
|
-
if (!parsedLeft || !parsedRight) return left.localeCompare(right);
|
|
34825
|
-
if (parsedLeft.major !== parsedRight.major) return parsedLeft.major - parsedRight.major;
|
|
34826
|
-
if (parsedLeft.minor !== parsedRight.minor) return parsedLeft.minor - parsedRight.minor;
|
|
34827
|
-
if (parsedLeft.patch !== parsedRight.patch) return parsedLeft.patch - parsedRight.patch;
|
|
34828
|
-
if (parsedLeft.prerelease.length === 0 && parsedRight.prerelease.length === 0) return 0;
|
|
34829
|
-
if (parsedLeft.prerelease.length === 0) return 1;
|
|
34830
|
-
if (parsedRight.prerelease.length === 0) return -1;
|
|
34831
|
-
const length = Math.max(parsedLeft.prerelease.length, parsedRight.prerelease.length);
|
|
34832
|
-
for (let index = 0; index < length; index += 1) {
|
|
34833
|
-
const leftIdentifier = parsedLeft.prerelease[index];
|
|
34834
|
-
const rightIdentifier = parsedRight.prerelease[index];
|
|
34835
|
-
if (leftIdentifier === void 0) return -1;
|
|
34836
|
-
if (rightIdentifier === void 0) return 1;
|
|
34837
|
-
const comparison = comparePrereleaseIdentifier(leftIdentifier, rightIdentifier);
|
|
34838
|
-
if (comparison !== 0) return comparison;
|
|
34839
|
-
}
|
|
34840
|
-
return 0;
|
|
34841
|
-
}
|
|
34842
|
-
|
|
34843
|
-
//#endregion
|
|
34844
|
-
//#region src/provider/providerMaintenance.ts
|
|
34845
|
-
const LATEST_VERSION_CACHE_TTL_MS = 3600 * 1e3;
|
|
34846
|
-
const LATEST_VERSION_TIMEOUT_MS = 4e3;
|
|
34847
|
-
const PROVIDER_UPDATE_ACTION_TOAST_MESSAGE = "Install the update now or review provider settings.";
|
|
34848
|
-
const latestVersionCache = /* @__PURE__ */ new Map();
|
|
34849
|
-
const NpmLatestVersionResponse = Struct({ version: optional$3(String$1) });
|
|
34850
|
-
function nonEmptyString(value) {
|
|
34851
|
-
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
34852
|
-
}
|
|
34853
|
-
function makeProviderMaintenanceCapabilities(input) {
|
|
34854
|
-
const update = input.updateExecutable === null || input.updateLockKey === null ? null : {
|
|
34855
|
-
command: [input.updateExecutable, ...input.updateArgs].join(" "),
|
|
34856
|
-
executable: input.updateExecutable,
|
|
34857
|
-
args: input.updateArgs,
|
|
34858
|
-
lockKey: input.updateLockKey
|
|
34859
|
-
};
|
|
34860
|
-
return {
|
|
34861
|
-
provider: input.provider,
|
|
34862
|
-
packageName: input.packageName,
|
|
34863
|
-
update
|
|
34864
|
-
};
|
|
34865
|
-
}
|
|
34866
|
-
function makeManualOnlyProviderMaintenanceCapabilities(input) {
|
|
34867
|
-
return makeProviderMaintenanceCapabilities({
|
|
34868
|
-
provider: input.provider,
|
|
34869
|
-
packageName: input.packageName,
|
|
34870
|
-
updateExecutable: null,
|
|
34871
|
-
updateArgs: [],
|
|
34872
|
-
updateLockKey: null
|
|
34873
|
-
});
|
|
34874
|
-
}
|
|
34875
|
-
function hasPathSeparator(value) {
|
|
34876
|
-
return value.includes("/") || value.includes("\\");
|
|
34877
|
-
}
|
|
34878
|
-
function makeStaticProviderMaintenanceResolver(capabilities) {
|
|
34879
|
-
return { resolve: () => capabilities };
|
|
34880
|
-
}
|
|
34881
|
-
function makeManualProviderMaintenanceCapabilities$1(provider) {
|
|
34882
|
-
return makeManualOnlyProviderMaintenanceCapabilities({
|
|
34883
|
-
provider,
|
|
34884
|
-
packageName: null
|
|
34885
|
-
});
|
|
34886
|
-
}
|
|
34887
|
-
const resolveProviderMaintenanceCapabilitiesEffect = fn("resolveProviderMaintenanceCapabilitiesEffect")(function* (resolver, options) {
|
|
34888
|
-
const binaryPath = nonEmptyString(options?.binaryPath);
|
|
34889
|
-
if (!binaryPath) return resolver.resolve(options);
|
|
34890
|
-
const resolvedCommandPath = resolveCommandPath(binaryPath, {
|
|
34891
|
-
...options?.platform ? { platform: options.platform } : {},
|
|
34892
|
-
...options?.env ? { env: options.env } : {}
|
|
34893
|
-
}) ?? (hasPathSeparator(binaryPath) ? binaryPath : null);
|
|
34894
|
-
if (!resolvedCommandPath) return resolver.resolve(options);
|
|
34895
|
-
const realCommandPath = yield* (yield* FileSystem).realPath(resolvedCommandPath).pipe(catch_(() => succeed(resolvedCommandPath)));
|
|
34896
|
-
return resolver.resolve({
|
|
34897
|
-
...options,
|
|
34898
|
-
realCommandPath
|
|
34899
|
-
});
|
|
34900
|
-
});
|
|
34901
|
-
function deriveVersionAdvisory(input) {
|
|
34902
|
-
if (!input.currentVersion) return {
|
|
34903
|
-
status: "unknown",
|
|
34904
|
-
message: null
|
|
34905
|
-
};
|
|
34906
|
-
if (!input.latestVersion) return {
|
|
34907
|
-
status: "unknown",
|
|
34908
|
-
message: null
|
|
34909
|
-
};
|
|
34910
|
-
if (compareSemverVersions(input.currentVersion, input.latestVersion) < 0) return {
|
|
34911
|
-
status: "behind_latest",
|
|
34912
|
-
message: PROVIDER_UPDATE_ACTION_TOAST_MESSAGE
|
|
34913
|
-
};
|
|
34914
|
-
return {
|
|
34915
|
-
status: "current",
|
|
34916
|
-
message: null
|
|
34917
|
-
};
|
|
34918
|
-
}
|
|
34919
|
-
function createProviderVersionAdvisory(input) {
|
|
34920
|
-
const capabilities = input.maintenanceCapabilities ?? makeManualProviderMaintenanceCapabilities$1(input.driver);
|
|
34921
|
-
const latestVersion = input.latestVersion ?? null;
|
|
34922
|
-
const advisory = deriveVersionAdvisory({
|
|
34923
|
-
currentVersion: input.currentVersion,
|
|
34924
|
-
latestVersion
|
|
34925
|
-
});
|
|
34926
|
-
return {
|
|
34927
|
-
status: advisory.status,
|
|
34928
|
-
currentVersion: input.currentVersion,
|
|
34929
|
-
latestVersion,
|
|
34930
|
-
updateCommand: capabilities.update?.command ?? null,
|
|
34931
|
-
canUpdate: capabilities.update !== null,
|
|
34932
|
-
checkedAt: input.checkedAt ?? null,
|
|
34933
|
-
message: advisory.message
|
|
34934
|
-
};
|
|
34935
|
-
}
|
|
34936
|
-
const fetchNpmLatestVersion = fn("fetchNpmLatestVersion")(function* (packageName) {
|
|
34937
|
-
const client = yield* HttpClient;
|
|
34938
|
-
const request = get$7(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`).pipe(setHeader("accept", "application/json"));
|
|
34939
|
-
const response = yield* client.execute(request).pipe(timeoutOption(LATEST_VERSION_TIMEOUT_MS), catch_(() => succeed(none$4())));
|
|
34940
|
-
if (isNone(response)) return null;
|
|
34941
|
-
const httpResponse = response.value;
|
|
34942
|
-
if (httpResponse.status < 200 || httpResponse.status >= 300) return null;
|
|
34943
|
-
const payload = yield* httpResponse.json.pipe(flatMap(decodeUnknownEffect(NpmLatestVersionResponse)), catch_(() => succeed(null)));
|
|
34944
|
-
return payload ? nonEmptyString(payload.version) : null;
|
|
34945
|
-
});
|
|
34946
|
-
const resolveLatestProviderVersion = fn("resolveLatestProviderVersion")(function* (maintenanceCapabilities) {
|
|
34947
|
-
const packageName = maintenanceCapabilities.packageName;
|
|
34948
|
-
if (!packageName) return null;
|
|
34949
|
-
const cached = latestVersionCache.get(packageName);
|
|
34950
|
-
const now$6 = toEpochMillis(yield* now);
|
|
34951
|
-
if (cached && cached.expiresAt > now$6) return cached.version;
|
|
34952
|
-
const version = yield* fetchNpmLatestVersion(packageName);
|
|
34953
|
-
latestVersionCache.set(packageName, {
|
|
34954
|
-
expiresAt: now$6 + LATEST_VERSION_CACHE_TTL_MS,
|
|
34955
|
-
version
|
|
34956
|
-
});
|
|
34957
|
-
return version;
|
|
34958
|
-
});
|
|
34959
|
-
const enrichProviderSnapshotWithVersionAdvisory = fn("enrichProviderSnapshotWithVersionAdvisory")(function* (snapshot, maintenanceCapabilities) {
|
|
34960
|
-
const capabilities = maintenanceCapabilities ?? makeManualProviderMaintenanceCapabilities$1(snapshot.driver);
|
|
34961
|
-
if (!snapshot.enabled || !snapshot.installed || !snapshot.version) return {
|
|
34962
|
-
...snapshot,
|
|
34963
|
-
versionAdvisory: createProviderVersionAdvisory({
|
|
34964
|
-
driver: snapshot.driver,
|
|
34965
|
-
currentVersion: snapshot.version,
|
|
34966
|
-
checkedAt: snapshot.checkedAt,
|
|
34967
|
-
maintenanceCapabilities: capabilities
|
|
34968
|
-
})
|
|
34969
|
-
};
|
|
34970
|
-
const latestVersion = yield* resolveLatestProviderVersion(capabilities);
|
|
34971
|
-
return {
|
|
34972
|
-
...snapshot,
|
|
34973
|
-
versionAdvisory: createProviderVersionAdvisory({
|
|
34974
|
-
driver: snapshot.driver,
|
|
34975
|
-
currentVersion: snapshot.version,
|
|
34976
|
-
latestVersion,
|
|
34977
|
-
checkedAt: formatIso(yield* now),
|
|
34978
|
-
maintenanceCapabilities: capabilities
|
|
34979
|
-
})
|
|
34980
|
-
};
|
|
34981
|
-
});
|
|
34982
|
-
|
|
34983
|
-
//#endregion
|
|
34984
|
-
//#region src/stream/collectUint8StreamText.ts
|
|
34985
|
-
const collectUint8StreamText = (input) => {
|
|
34986
|
-
const decoder = new TextDecoder();
|
|
34987
|
-
const maxBytes = input.maxBytes ?? Number.POSITIVE_INFINITY;
|
|
34988
|
-
const truncatedMarker = input.truncatedMarker ?? "";
|
|
34989
|
-
return input.stream.pipe(runFold(() => ({
|
|
34990
|
-
text: "",
|
|
34991
|
-
bytes: 0,
|
|
34992
|
-
truncated: false
|
|
34993
|
-
}), (state, chunk) => {
|
|
34994
|
-
if (state.truncated) return state;
|
|
34995
|
-
const remainingBytes = maxBytes - state.bytes;
|
|
34996
|
-
if (remainingBytes <= 0) return {
|
|
34997
|
-
...state,
|
|
34998
|
-
text: `${state.text}${truncatedMarker}`,
|
|
34999
|
-
truncated: true
|
|
35000
|
-
};
|
|
35001
|
-
const nextChunk = chunk.byteLength > remainingBytes ? chunk.slice(0, remainingBytes) : chunk;
|
|
35002
|
-
const text = `${state.text}${decoder.decode(nextChunk, { stream: true })}`;
|
|
35003
|
-
const bytes = state.bytes + nextChunk.byteLength;
|
|
35004
|
-
const truncated = chunk.byteLength > remainingBytes;
|
|
35005
|
-
return {
|
|
35006
|
-
text: truncated ? `${text}${truncatedMarker}` : text,
|
|
35007
|
-
bytes,
|
|
35008
|
-
truncated
|
|
35009
|
-
};
|
|
35010
|
-
}), map$3((state) => ({
|
|
35011
|
-
text: state.truncated ? state.text : `${state.text}${decoder.decode()}`,
|
|
35012
|
-
bytes: state.bytes,
|
|
35013
|
-
truncated: state.truncated
|
|
35014
|
-
})));
|
|
35015
|
-
};
|
|
35016
|
-
|
|
35017
|
-
//#endregion
|
|
35018
|
-
//#region src/provider/providerSnapshot.ts
|
|
35019
|
-
function isCommandMissingCause(error) {
|
|
35020
|
-
const lower = error.message.toLowerCase();
|
|
35021
|
-
return lower.includes("enoent") || lower.includes("notfound");
|
|
35022
|
-
}
|
|
35023
|
-
function parseGenericCliVersion(output) {
|
|
35024
|
-
return output.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
|
|
35025
|
-
}
|
|
35026
|
-
function buildServerProvider(input) {
|
|
35027
|
-
const versionAdvisory = input.driver ? createProviderVersionAdvisory({
|
|
35028
|
-
driver: input.driver,
|
|
35029
|
-
currentVersion: input.probe.version,
|
|
35030
|
-
checkedAt: input.checkedAt
|
|
35031
|
-
}) : void 0;
|
|
35032
|
-
return {
|
|
35033
|
-
displayName: input.presentation.displayName,
|
|
35034
|
-
...input.presentation.badgeLabel ? { badgeLabel: input.presentation.badgeLabel } : {},
|
|
35035
|
-
...typeof input.presentation.showInteractionModeToggle === "boolean" ? { showInteractionModeToggle: input.presentation.showInteractionModeToggle } : {},
|
|
35036
|
-
enabled: input.enabled,
|
|
35037
|
-
installed: input.probe.installed,
|
|
35038
|
-
version: input.probe.version,
|
|
35039
|
-
status: input.enabled ? input.probe.status : "disabled",
|
|
35040
|
-
auth: input.probe.auth,
|
|
35041
|
-
checkedAt: input.checkedAt,
|
|
35042
|
-
...input.probe.message ? { message: input.probe.message } : {},
|
|
35043
|
-
models: input.models,
|
|
35044
|
-
slashCommands: [...input.slashCommands ?? []],
|
|
35045
|
-
skills: [...input.skills ?? []],
|
|
35046
|
-
...versionAdvisory ? { versionAdvisory } : {}
|
|
35047
|
-
};
|
|
35048
|
-
}
|
|
35049
|
-
const collectStreamAsString = (stream) => collectUint8StreamText({ stream }).pipe(map$3((collected) => collected.text));
|
|
35050
|
-
|
|
35051
|
-
//#endregion
|
|
35052
|
-
//#region src/ru-fork/startup/constants.ts
|
|
35053
|
-
/**
|
|
35054
|
-
* Startup-gate constants. Mirrored by hand in:
|
|
35055
|
-
* - `install` (bash, top-of-file constants block)
|
|
35056
|
-
* - `apps/server/package.json` engines.node (NODE_ENGINE_RANGE only)
|
|
35057
|
-
* - `package.json` (repo root) engines.node (NODE_ENGINE_RANGE only)
|
|
35058
|
-
*
|
|
35059
|
-
* Change procedure: update this file and every mirror site in the same
|
|
35060
|
-
* commit. Reviewer cross-checks all four.
|
|
35061
|
-
*/
|
|
35062
|
-
const NODE_ENGINE_RANGE = "^22.16 || ^23.11 || >=24.10";
|
|
35063
|
-
/**
|
|
35064
|
-
* Minimum required CLI version (compared with `isAtLeast`).
|
|
35065
|
-
* Set to "" (empty string) to disable the version check and only
|
|
35066
|
-
* verify presence.
|
|
35067
|
-
*/
|
|
35068
|
-
const CLI_MIN_VERSION = "0.13.1";
|
|
35069
|
-
/**
|
|
35070
|
-
* Russian message strings. `{found}` is the only placeholder rendered
|
|
35071
|
-
* at use time. `NODE_ENGINE_RANGE`, `CLI_MIN_VERSION`, `CLI_NAME`, and
|
|
35072
|
-
* `CLI_BINARY_NAME` are baked in via template literals at module load
|
|
35073
|
-
* — same pattern as the bash mirror in `install`, which uses `${VAR}`
|
|
35074
|
-
* interpolation at definition time.
|
|
35075
|
-
*
|
|
35076
|
-
* Note: NODE_MISSING intentionally has no TS counterpart — node cannot
|
|
35077
|
-
* be missing in a running node process. The install script's
|
|
35078
|
-
* MSG_NODE_MISSING covers the pre-install case.
|
|
35079
|
-
*/
|
|
35080
|
-
const MESSAGES = {
|
|
35081
|
-
HEADER: `${APP_NAME}: проверка зависимостей...`,
|
|
35082
|
-
NODE_OK: " Node.js {found} ✓",
|
|
35083
|
-
NODE_LOW: ` Node.js {found} установлен, требуется ${NODE_ENGINE_RANGE}. Обновите: https://nodejs.org/`,
|
|
35084
|
-
GIT_OK: " Git {found} ✓",
|
|
35085
|
-
GIT_MISSING: " Git не найден на PATH. Установите Git: https://git-scm.com/downloads",
|
|
35086
|
-
GIT_BASH_REQUIRED: ` ${APP_NAME} должен запускаться из Git Bash. Установите Git for Windows: https://git-scm.com/downloads`,
|
|
35087
|
-
GIT_BROKEN: " Git установлен, но команда `git --version` завершилась с ошибкой или превысила тайм-аут.",
|
|
35088
|
-
CLI_OK: ` ${CLI_NAME} {found} ✓`,
|
|
35089
|
-
CLI_MISSING: ` ${CLI_NAME} не найден. Установите: npm install -g ${CLI_NPM_PACKAGE}`,
|
|
35090
|
-
CLI_LOW: ` ${CLI_NAME} {found} установлен, требуется ≥ ${CLI_MIN_VERSION}. Обновите: npm install -g ${CLI_NPM_PACKAGE}@latest`,
|
|
35091
|
-
CLI_BROKEN: ` ${CLI_NAME} установлен, но команда \`${CLI_BINARY_NAME} --version\` завершилась с ошибкой или превысила тайм-аут.`,
|
|
35092
|
-
FOOTER_FAIL: "Установите недостающие компоненты и перезапустите."
|
|
35093
|
-
};
|
|
35094
|
-
|
|
35095
|
-
//#endregion
|
|
35096
|
-
//#region src/ru-fork/startup/versionRange.ts
|
|
35097
|
-
const parseVersion = (input) => {
|
|
35098
|
-
const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(input);
|
|
35099
|
-
if (!m) return null;
|
|
35100
|
-
return [
|
|
35101
|
-
Number(m[1]),
|
|
35102
|
-
Number(m[2]),
|
|
35103
|
-
Number(m[3] ?? 0)
|
|
35104
|
-
];
|
|
35105
|
-
};
|
|
35106
|
-
const isAtLeast = (actual, minimum) => {
|
|
35107
|
-
const a = parseVersion(actual);
|
|
35108
|
-
const b = parseVersion(minimum);
|
|
35109
|
-
if (!a || !b) return false;
|
|
35110
|
-
const [aMaj, aMin, aPat] = a;
|
|
35111
|
-
const [bMaj, bMin, bPat] = b;
|
|
35112
|
-
if (aMaj !== bMaj) return aMaj > bMaj;
|
|
35113
|
-
if (aMin !== bMin) return aMin > bMin;
|
|
35114
|
-
return aPat >= bPat;
|
|
35115
|
-
};
|
|
35116
|
-
const satisfiesRange = (actual, range) => {
|
|
35117
|
-
const parsed = parseVersion(actual);
|
|
35118
|
-
if (!parsed) return false;
|
|
35119
|
-
const [aMaj, aMin, aPat] = parsed;
|
|
35120
|
-
return range.split("||").some((raw) => {
|
|
35121
|
-
const disjunct = raw.trim();
|
|
35122
|
-
let m = /^\^(\d+)\.(\d+)(?:\.(\d+))?$/.exec(disjunct);
|
|
35123
|
-
if (m) return aMaj === Number(m[1]) && aMin >= Number(m[2]);
|
|
35124
|
-
m = /^>=(\d+)\.(\d+)(?:\.(\d+))?$/.exec(disjunct);
|
|
35125
|
-
if (m) {
|
|
35126
|
-
const tMaj = Number(m[1]);
|
|
35127
|
-
const tMin = Number(m[2]);
|
|
35128
|
-
const tPat = Number(m[3] ?? 0);
|
|
35129
|
-
if (aMaj !== tMaj) return aMaj > tMaj;
|
|
35130
|
-
if (aMin !== tMin) return aMin > tMin;
|
|
35131
|
-
return aPat >= tPat;
|
|
35132
|
-
}
|
|
35133
|
-
return false;
|
|
35134
|
-
});
|
|
35135
|
-
};
|
|
35136
|
-
|
|
35137
|
-
//#endregion
|
|
35138
|
-
//#region src/ru-fork/startup/preflight.ts
|
|
35139
|
-
/**
|
|
35140
|
-
* Startup-gate preflight Effect.
|
|
35141
|
-
*
|
|
35142
|
-
* Probes node / git / CLI, aggregates failures, logs each result via
|
|
35143
|
-
* `Effect.logInfo` (ok) or `Effect.logError` (miss), and fails with a
|
|
35144
|
-
* typed `PreflightFailedError` when any check fails so `Command.run`
|
|
35145
|
-
* exits non-zero naturally.
|
|
35146
|
-
*
|
|
35147
|
-
* Call sites: `runServerCommand` (cli/server.ts) and
|
|
35148
|
-
* `runDaemonLauncher` (daemonLauncher.ts), each gated by the
|
|
35149
|
-
* `--no-preflight-check` flag.
|
|
35150
|
-
*
|
|
35151
|
-
* Mirrors install (bash) behaviour. See
|
|
35152
|
-
* `ru-fork-instrumental/changes/deamon/startap-checks.md`.
|
|
35153
|
-
*/
|
|
35154
|
-
const GIT_TIMEOUT_MS = 5e3;
|
|
35155
|
-
const CLI_TIMEOUT_MS$1 = 15e3;
|
|
35156
|
-
var PreflightFailedError = class extends TaggedError("PreflightFailedError") {};
|
|
35157
|
-
const render = (template, values) => template.replace(/\{(\w+)\}/g, (_, k) => values[k] ?? `{${k}}`);
|
|
35158
|
-
/**
|
|
35159
|
-
* Spawns `bin --version` and inspects the outcome. Mirrors the exact
|
|
35160
|
-
* pattern in `CliProvider.ts:127-195`: pipe spawn → `Effect.timeoutOption`
|
|
35161
|
-
* → `Effect.result`, then branch on the Result/Option shape:
|
|
35162
|
-
* - failure (e.g. ENOENT) → "missing" or "broken" via `isCommandMissingCause`
|
|
35163
|
-
* - None (timeoutOption returned None) → "timeout"
|
|
35164
|
-
* - exitCode !== 0 → "broken"
|
|
35165
|
-
* - else → ok with stdout
|
|
35166
|
-
*
|
|
35167
|
-
* `shell` mirrors the existing per-tool spawn shapes:
|
|
35168
|
-
* - git: shell=false (matches `GitVcsDriverCore.ts:642`)
|
|
35169
|
-
* - CLI: shell=process.platform==="win32" (matches `CliProvider.ts:135`)
|
|
35170
|
-
*/
|
|
35171
|
-
const probe = (bin, args, timeoutMs, shell) => gen(function* () {
|
|
35172
|
-
const resolved = resolveSpawn(bin, args, { shell });
|
|
35173
|
-
const probeResult = yield* gen(function* () {
|
|
35174
|
-
const spawner = yield* ChildProcessSpawner;
|
|
35175
|
-
const command = make$46(resolved.command, [...resolved.args], { shell: resolved.shell });
|
|
35176
|
-
const child = yield* spawner.spawn(command);
|
|
35177
|
-
const [stdout, stderr, exitCode] = yield* all([
|
|
35178
|
-
collectStreamAsString(child.stdout),
|
|
35179
|
-
collectStreamAsString(child.stderr),
|
|
35180
|
-
child.exitCode.pipe(map$3(Number))
|
|
35181
|
-
], { concurrency: "unbounded" });
|
|
35182
|
-
return {
|
|
35183
|
-
stdout,
|
|
35184
|
-
stderr,
|
|
35185
|
-
exitCode
|
|
35186
|
-
};
|
|
35187
|
-
}).pipe(scoped, timeoutOption(timeoutMs), result);
|
|
35188
|
-
if (isFailure$1(probeResult)) return isCommandMissingCause(probeResult.failure) ? {
|
|
35189
|
-
ok: false,
|
|
35190
|
-
reason: "missing"
|
|
35191
|
-
} : {
|
|
35192
|
-
ok: false,
|
|
35193
|
-
reason: "broken"
|
|
35194
|
-
};
|
|
35195
|
-
if (isNone(probeResult.success)) return {
|
|
35196
|
-
ok: false,
|
|
35197
|
-
reason: "timeout"
|
|
35198
|
-
};
|
|
35199
|
-
const { stdout, stderr, exitCode } = probeResult.success.value;
|
|
35200
|
-
return exitCode === 0 ? {
|
|
35201
|
-
ok: true,
|
|
35202
|
-
stdout
|
|
35203
|
-
} : {
|
|
35204
|
-
ok: false,
|
|
35205
|
-
reason: "broken",
|
|
35206
|
-
stderr
|
|
35207
|
-
};
|
|
35208
|
-
});
|
|
35209
|
-
const checkShell = () => sync$1(() => {
|
|
35210
|
-
if (process.platform !== "win32") return {
|
|
35211
|
-
ok: true,
|
|
35212
|
-
line: ""
|
|
35213
|
-
};
|
|
35214
|
-
if (!process.env.MSYSTEM) return {
|
|
35215
|
-
ok: false,
|
|
35216
|
-
line: MESSAGES.GIT_BASH_REQUIRED
|
|
35217
|
-
};
|
|
35218
|
-
return {
|
|
35219
|
-
ok: true,
|
|
35220
|
-
line: ""
|
|
35221
|
-
};
|
|
35222
|
-
});
|
|
35223
|
-
const checkNode = () => sync$1(() => {
|
|
35224
|
-
const current = `v${process.versions.node}`;
|
|
35225
|
-
if (!satisfiesRange(process.versions.node, NODE_ENGINE_RANGE)) return {
|
|
35226
|
-
ok: false,
|
|
35227
|
-
line: render(MESSAGES.NODE_LOW, { found: current })
|
|
35228
|
-
};
|
|
35229
|
-
return {
|
|
35230
|
-
ok: true,
|
|
35231
|
-
line: render(MESSAGES.NODE_OK, { found: current })
|
|
35232
|
-
};
|
|
35233
|
-
});
|
|
35234
|
-
const appendDetail = (baseLine, stderr) => {
|
|
35235
|
-
const detail = stderr?.trim();
|
|
35236
|
-
return detail ? `${baseLine}\n ${detail}` : baseLine;
|
|
35237
|
-
};
|
|
35238
|
-
const checkGit = () => gen(function* () {
|
|
35239
|
-
const result = yield* probe("git", ["--version"], GIT_TIMEOUT_MS, false);
|
|
35240
|
-
if (!result.ok) return {
|
|
35241
|
-
ok: false,
|
|
35242
|
-
line: appendDetail(result.reason === "missing" ? MESSAGES.GIT_MISSING : MESSAGES.GIT_BROKEN, result.stderr)
|
|
35243
|
-
};
|
|
35244
|
-
const parsed = parseVersion(result.stdout);
|
|
35245
|
-
if (!parsed) return {
|
|
35246
|
-
ok: false,
|
|
35247
|
-
line: MESSAGES.GIT_BROKEN
|
|
35248
|
-
};
|
|
35249
|
-
return {
|
|
35250
|
-
ok: true,
|
|
35251
|
-
line: render(MESSAGES.GIT_OK, { found: parsed.join(".") })
|
|
35252
|
-
};
|
|
35253
|
-
});
|
|
35254
|
-
const checkCli = () => gen(function* () {
|
|
35255
|
-
const result = yield* probe(CLI_BINARY_NAME, ["--version"], CLI_TIMEOUT_MS$1, process.platform === "win32");
|
|
35256
|
-
if (!result.ok) return {
|
|
35257
|
-
ok: false,
|
|
35258
|
-
line: appendDetail(result.reason === "missing" ? MESSAGES.CLI_MISSING : MESSAGES.CLI_BROKEN, result.stderr)
|
|
35259
|
-
};
|
|
35260
|
-
const parsed = parseVersion(result.stdout);
|
|
35261
|
-
if (!parsed) return {
|
|
35262
|
-
ok: false,
|
|
35263
|
-
line: MESSAGES.CLI_BROKEN
|
|
35264
|
-
};
|
|
35265
|
-
const found = parsed.join(".");
|
|
35266
|
-
if (CLI_MIN_VERSION && !isAtLeast(found, CLI_MIN_VERSION)) return {
|
|
35267
|
-
ok: false,
|
|
35268
|
-
line: render(MESSAGES.CLI_LOW, { found })
|
|
35269
|
-
};
|
|
35270
|
-
return {
|
|
35271
|
-
ok: true,
|
|
35272
|
-
line: render(MESSAGES.CLI_OK, { found })
|
|
35273
|
-
};
|
|
35274
|
-
});
|
|
35275
|
-
/**
|
|
35276
|
-
* Run all three checks, log each result line at info (ok) or error
|
|
35277
|
-
* (failure) level, fail with `PreflightFailedError` if any failed.
|
|
35278
|
-
*/
|
|
35279
|
-
const runPreflight = gen(function* () {
|
|
35280
|
-
yield* logInfo(MESSAGES.HEADER);
|
|
35281
|
-
const results = yield* all([
|
|
35282
|
-
checkShell(),
|
|
35283
|
-
checkNode(),
|
|
35284
|
-
checkGit(),
|
|
35285
|
-
checkCli()
|
|
35286
|
-
], { concurrency: "unbounded" });
|
|
35287
|
-
for (const r of results) {
|
|
35288
|
-
if (r.line.length === 0) continue;
|
|
35289
|
-
if (r.ok) yield* logInfo(r.line);
|
|
35290
|
-
else yield* logError(r.line);
|
|
35291
|
-
}
|
|
35292
|
-
const failureLines = results.filter((r) => !r.ok).map((r) => r.line).filter((line) => line.length > 0);
|
|
35293
|
-
if (failureLines.length > 0) {
|
|
35294
|
-
yield* logError(MESSAGES.FOOTER_FAIL);
|
|
35295
|
-
return yield* new PreflightFailedError({ failures: failureLines });
|
|
35296
|
-
}
|
|
35297
|
-
});
|
|
35298
|
-
|
|
35299
35236
|
//#endregion
|
|
35300
35237
|
//#region src/ru-fork/local-startup/assertLocalPortAvailable.ts
|
|
35301
35238
|
var PortInUseError = class extends TaggedError("PortInUseError") {};
|
|
@@ -39871,6 +39808,241 @@ const makeProviderMaintenanceCommandCoordinator = fn("makeProviderMaintenanceCom
|
|
|
39871
39808
|
return { withCommandLock };
|
|
39872
39809
|
});
|
|
39873
39810
|
|
|
39811
|
+
//#endregion
|
|
39812
|
+
//#region ../../packages/shared/src/semver.ts
|
|
39813
|
+
const SEMVER_NUMBER_SEGMENT = /^\d+$/;
|
|
39814
|
+
function normalizeSemverVersion(version) {
|
|
39815
|
+
const [main, prerelease] = version.trim().split("-", 2);
|
|
39816
|
+
const segments = (main ?? "").split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
|
|
39817
|
+
if (segments.length === 2) segments.push("0");
|
|
39818
|
+
return prerelease ? `${segments.join(".")}-${prerelease}` : segments.join(".");
|
|
39819
|
+
}
|
|
39820
|
+
function parseSemver(value) {
|
|
39821
|
+
const [main = "", prerelease] = normalizeSemverVersion(value).replace(/^v/, "").split("-", 2);
|
|
39822
|
+
const segments = main.split(".");
|
|
39823
|
+
if (segments.length !== 3) return null;
|
|
39824
|
+
const [majorSegment, minorSegment, patchSegment] = segments;
|
|
39825
|
+
if (majorSegment === void 0 || minorSegment === void 0 || patchSegment === void 0) return null;
|
|
39826
|
+
if (!SEMVER_NUMBER_SEGMENT.test(majorSegment) || !SEMVER_NUMBER_SEGMENT.test(minorSegment) || !SEMVER_NUMBER_SEGMENT.test(patchSegment)) return null;
|
|
39827
|
+
const major = Number.parseInt(majorSegment, 10);
|
|
39828
|
+
const minor = Number.parseInt(minorSegment, 10);
|
|
39829
|
+
const patch = Number.parseInt(patchSegment, 10);
|
|
39830
|
+
if (![
|
|
39831
|
+
major,
|
|
39832
|
+
minor,
|
|
39833
|
+
patch
|
|
39834
|
+
].every(Number.isInteger)) return null;
|
|
39835
|
+
return {
|
|
39836
|
+
major,
|
|
39837
|
+
minor,
|
|
39838
|
+
patch,
|
|
39839
|
+
prerelease: prerelease?.split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0) ?? []
|
|
39840
|
+
};
|
|
39841
|
+
}
|
|
39842
|
+
function comparePrereleaseIdentifier(left, right) {
|
|
39843
|
+
const leftNumeric = SEMVER_NUMBER_SEGMENT.test(left);
|
|
39844
|
+
const rightNumeric = SEMVER_NUMBER_SEGMENT.test(right);
|
|
39845
|
+
if (leftNumeric && rightNumeric) return Number.parseInt(left, 10) - Number.parseInt(right, 10);
|
|
39846
|
+
if (leftNumeric) return -1;
|
|
39847
|
+
if (rightNumeric) return 1;
|
|
39848
|
+
return left.localeCompare(right);
|
|
39849
|
+
}
|
|
39850
|
+
function compareSemverVersions(left, right) {
|
|
39851
|
+
const parsedLeft = parseSemver(left);
|
|
39852
|
+
const parsedRight = parseSemver(right);
|
|
39853
|
+
if (!parsedLeft || !parsedRight) return left.localeCompare(right);
|
|
39854
|
+
if (parsedLeft.major !== parsedRight.major) return parsedLeft.major - parsedRight.major;
|
|
39855
|
+
if (parsedLeft.minor !== parsedRight.minor) return parsedLeft.minor - parsedRight.minor;
|
|
39856
|
+
if (parsedLeft.patch !== parsedRight.patch) return parsedLeft.patch - parsedRight.patch;
|
|
39857
|
+
if (parsedLeft.prerelease.length === 0 && parsedRight.prerelease.length === 0) return 0;
|
|
39858
|
+
if (parsedLeft.prerelease.length === 0) return 1;
|
|
39859
|
+
if (parsedRight.prerelease.length === 0) return -1;
|
|
39860
|
+
const length = Math.max(parsedLeft.prerelease.length, parsedRight.prerelease.length);
|
|
39861
|
+
for (let index = 0; index < length; index += 1) {
|
|
39862
|
+
const leftIdentifier = parsedLeft.prerelease[index];
|
|
39863
|
+
const rightIdentifier = parsedRight.prerelease[index];
|
|
39864
|
+
if (leftIdentifier === void 0) return -1;
|
|
39865
|
+
if (rightIdentifier === void 0) return 1;
|
|
39866
|
+
const comparison = comparePrereleaseIdentifier(leftIdentifier, rightIdentifier);
|
|
39867
|
+
if (comparison !== 0) return comparison;
|
|
39868
|
+
}
|
|
39869
|
+
return 0;
|
|
39870
|
+
}
|
|
39871
|
+
|
|
39872
|
+
//#endregion
|
|
39873
|
+
//#region src/provider/providerMaintenance.ts
|
|
39874
|
+
const LATEST_VERSION_CACHE_TTL_MS = 3600 * 1e3;
|
|
39875
|
+
const LATEST_VERSION_TIMEOUT_MS = 4e3;
|
|
39876
|
+
const PROVIDER_UPDATE_ACTION_TOAST_MESSAGE = "Install the update now or review provider settings.";
|
|
39877
|
+
const latestVersionCache = /* @__PURE__ */ new Map();
|
|
39878
|
+
const NpmLatestVersionResponse = Struct({ version: optional$3(String$1) });
|
|
39879
|
+
function nonEmptyString(value) {
|
|
39880
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
39881
|
+
}
|
|
39882
|
+
function makeProviderMaintenanceCapabilities(input) {
|
|
39883
|
+
const update = input.updateExecutable === null || input.updateLockKey === null ? null : {
|
|
39884
|
+
command: [input.updateExecutable, ...input.updateArgs].join(" "),
|
|
39885
|
+
executable: input.updateExecutable,
|
|
39886
|
+
args: input.updateArgs,
|
|
39887
|
+
lockKey: input.updateLockKey
|
|
39888
|
+
};
|
|
39889
|
+
return {
|
|
39890
|
+
provider: input.provider,
|
|
39891
|
+
packageName: input.packageName,
|
|
39892
|
+
update
|
|
39893
|
+
};
|
|
39894
|
+
}
|
|
39895
|
+
function makeManualOnlyProviderMaintenanceCapabilities(input) {
|
|
39896
|
+
return makeProviderMaintenanceCapabilities({
|
|
39897
|
+
provider: input.provider,
|
|
39898
|
+
packageName: input.packageName,
|
|
39899
|
+
updateExecutable: null,
|
|
39900
|
+
updateArgs: [],
|
|
39901
|
+
updateLockKey: null
|
|
39902
|
+
});
|
|
39903
|
+
}
|
|
39904
|
+
function hasPathSeparator(value) {
|
|
39905
|
+
return value.includes("/") || value.includes("\\");
|
|
39906
|
+
}
|
|
39907
|
+
function makeStaticProviderMaintenanceResolver(capabilities) {
|
|
39908
|
+
return { resolve: () => capabilities };
|
|
39909
|
+
}
|
|
39910
|
+
function makeManualProviderMaintenanceCapabilities$1(provider) {
|
|
39911
|
+
return makeManualOnlyProviderMaintenanceCapabilities({
|
|
39912
|
+
provider,
|
|
39913
|
+
packageName: null
|
|
39914
|
+
});
|
|
39915
|
+
}
|
|
39916
|
+
const resolveProviderMaintenanceCapabilitiesEffect = fn("resolveProviderMaintenanceCapabilitiesEffect")(function* (resolver, options) {
|
|
39917
|
+
const binaryPath = nonEmptyString(options?.binaryPath);
|
|
39918
|
+
if (!binaryPath) return resolver.resolve(options);
|
|
39919
|
+
const resolvedCommandPath = resolveCommandPath(binaryPath, {
|
|
39920
|
+
...options?.platform ? { platform: options.platform } : {},
|
|
39921
|
+
...options?.env ? { env: options.env } : {}
|
|
39922
|
+
}) ?? (hasPathSeparator(binaryPath) ? binaryPath : null);
|
|
39923
|
+
if (!resolvedCommandPath) return resolver.resolve(options);
|
|
39924
|
+
const realCommandPath = yield* (yield* FileSystem).realPath(resolvedCommandPath).pipe(catch_(() => succeed(resolvedCommandPath)));
|
|
39925
|
+
return resolver.resolve({
|
|
39926
|
+
...options,
|
|
39927
|
+
realCommandPath
|
|
39928
|
+
});
|
|
39929
|
+
});
|
|
39930
|
+
function deriveVersionAdvisory(input) {
|
|
39931
|
+
if (!input.currentVersion) return {
|
|
39932
|
+
status: "unknown",
|
|
39933
|
+
message: null
|
|
39934
|
+
};
|
|
39935
|
+
if (!input.latestVersion) return {
|
|
39936
|
+
status: "unknown",
|
|
39937
|
+
message: null
|
|
39938
|
+
};
|
|
39939
|
+
if (compareSemverVersions(input.currentVersion, input.latestVersion) < 0) return {
|
|
39940
|
+
status: "behind_latest",
|
|
39941
|
+
message: PROVIDER_UPDATE_ACTION_TOAST_MESSAGE
|
|
39942
|
+
};
|
|
39943
|
+
return {
|
|
39944
|
+
status: "current",
|
|
39945
|
+
message: null
|
|
39946
|
+
};
|
|
39947
|
+
}
|
|
39948
|
+
function createProviderVersionAdvisory(input) {
|
|
39949
|
+
const capabilities = input.maintenanceCapabilities ?? makeManualProviderMaintenanceCapabilities$1(input.driver);
|
|
39950
|
+
const latestVersion = input.latestVersion ?? null;
|
|
39951
|
+
const advisory = deriveVersionAdvisory({
|
|
39952
|
+
currentVersion: input.currentVersion,
|
|
39953
|
+
latestVersion
|
|
39954
|
+
});
|
|
39955
|
+
return {
|
|
39956
|
+
status: advisory.status,
|
|
39957
|
+
currentVersion: input.currentVersion,
|
|
39958
|
+
latestVersion,
|
|
39959
|
+
updateCommand: capabilities.update?.command ?? null,
|
|
39960
|
+
canUpdate: capabilities.update !== null,
|
|
39961
|
+
checkedAt: input.checkedAt ?? null,
|
|
39962
|
+
message: advisory.message
|
|
39963
|
+
};
|
|
39964
|
+
}
|
|
39965
|
+
const fetchNpmLatestVersion = fn("fetchNpmLatestVersion")(function* (packageName) {
|
|
39966
|
+
const client = yield* HttpClient;
|
|
39967
|
+
const request = get$7(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`).pipe(setHeader("accept", "application/json"));
|
|
39968
|
+
const response = yield* client.execute(request).pipe(timeoutOption(LATEST_VERSION_TIMEOUT_MS), catch_(() => succeed(none$4())));
|
|
39969
|
+
if (isNone(response)) return null;
|
|
39970
|
+
const httpResponse = response.value;
|
|
39971
|
+
if (httpResponse.status < 200 || httpResponse.status >= 300) return null;
|
|
39972
|
+
const payload = yield* httpResponse.json.pipe(flatMap(decodeUnknownEffect(NpmLatestVersionResponse)), catch_(() => succeed(null)));
|
|
39973
|
+
return payload ? nonEmptyString(payload.version) : null;
|
|
39974
|
+
});
|
|
39975
|
+
const resolveLatestProviderVersion = fn("resolveLatestProviderVersion")(function* (maintenanceCapabilities) {
|
|
39976
|
+
const packageName = maintenanceCapabilities.packageName;
|
|
39977
|
+
if (!packageName) return null;
|
|
39978
|
+
const cached = latestVersionCache.get(packageName);
|
|
39979
|
+
const now$6 = toEpochMillis(yield* now);
|
|
39980
|
+
if (cached && cached.expiresAt > now$6) return cached.version;
|
|
39981
|
+
const version = yield* fetchNpmLatestVersion(packageName);
|
|
39982
|
+
latestVersionCache.set(packageName, {
|
|
39983
|
+
expiresAt: now$6 + LATEST_VERSION_CACHE_TTL_MS,
|
|
39984
|
+
version
|
|
39985
|
+
});
|
|
39986
|
+
return version;
|
|
39987
|
+
});
|
|
39988
|
+
const enrichProviderSnapshotWithVersionAdvisory = fn("enrichProviderSnapshotWithVersionAdvisory")(function* (snapshot, maintenanceCapabilities) {
|
|
39989
|
+
const capabilities = maintenanceCapabilities ?? makeManualProviderMaintenanceCapabilities$1(snapshot.driver);
|
|
39990
|
+
if (!snapshot.enabled || !snapshot.installed || !snapshot.version) return {
|
|
39991
|
+
...snapshot,
|
|
39992
|
+
versionAdvisory: createProviderVersionAdvisory({
|
|
39993
|
+
driver: snapshot.driver,
|
|
39994
|
+
currentVersion: snapshot.version,
|
|
39995
|
+
checkedAt: snapshot.checkedAt,
|
|
39996
|
+
maintenanceCapabilities: capabilities
|
|
39997
|
+
})
|
|
39998
|
+
};
|
|
39999
|
+
const latestVersion = yield* resolveLatestProviderVersion(capabilities);
|
|
40000
|
+
return {
|
|
40001
|
+
...snapshot,
|
|
40002
|
+
versionAdvisory: createProviderVersionAdvisory({
|
|
40003
|
+
driver: snapshot.driver,
|
|
40004
|
+
currentVersion: snapshot.version,
|
|
40005
|
+
latestVersion,
|
|
40006
|
+
checkedAt: formatIso(yield* now),
|
|
40007
|
+
maintenanceCapabilities: capabilities
|
|
40008
|
+
})
|
|
40009
|
+
};
|
|
40010
|
+
});
|
|
40011
|
+
|
|
40012
|
+
//#endregion
|
|
40013
|
+
//#region src/stream/collectUint8StreamText.ts
|
|
40014
|
+
const collectUint8StreamText = (input) => {
|
|
40015
|
+
const decoder = new TextDecoder();
|
|
40016
|
+
const maxBytes = input.maxBytes ?? Number.POSITIVE_INFINITY;
|
|
40017
|
+
const truncatedMarker = input.truncatedMarker ?? "";
|
|
40018
|
+
return input.stream.pipe(runFold(() => ({
|
|
40019
|
+
text: "",
|
|
40020
|
+
bytes: 0,
|
|
40021
|
+
truncated: false
|
|
40022
|
+
}), (state, chunk) => {
|
|
40023
|
+
if (state.truncated) return state;
|
|
40024
|
+
const remainingBytes = maxBytes - state.bytes;
|
|
40025
|
+
if (remainingBytes <= 0) return {
|
|
40026
|
+
...state,
|
|
40027
|
+
text: `${state.text}${truncatedMarker}`,
|
|
40028
|
+
truncated: true
|
|
40029
|
+
};
|
|
40030
|
+
const nextChunk = chunk.byteLength > remainingBytes ? chunk.slice(0, remainingBytes) : chunk;
|
|
40031
|
+
const text = `${state.text}${decoder.decode(nextChunk, { stream: true })}`;
|
|
40032
|
+
const bytes = state.bytes + nextChunk.byteLength;
|
|
40033
|
+
const truncated = chunk.byteLength > remainingBytes;
|
|
40034
|
+
return {
|
|
40035
|
+
text: truncated ? `${text}${truncatedMarker}` : text,
|
|
40036
|
+
bytes,
|
|
40037
|
+
truncated
|
|
40038
|
+
};
|
|
40039
|
+
}), map$3((state) => ({
|
|
40040
|
+
text: state.truncated ? state.text : `${state.text}${decoder.decode()}`,
|
|
40041
|
+
bytes: state.bytes,
|
|
40042
|
+
truncated: state.truncated
|
|
40043
|
+
})));
|
|
40044
|
+
};
|
|
40045
|
+
|
|
39874
40046
|
//#endregion
|
|
39875
40047
|
//#region src/provider/providerMaintenanceRunner.ts
|
|
39876
40048
|
const isServerProviderUpdateError = is(ServerProviderUpdateError);
|
|
@@ -40257,9 +40429,8 @@ const makeCachedFsScanner = (config) => gen(function* () {
|
|
|
40257
40429
|
|
|
40258
40430
|
//#endregion
|
|
40259
40431
|
//#region src/ru-fork/common/cliRoots.ts
|
|
40260
|
-
const cliUserRoot = (
|
|
40261
|
-
|
|
40262
|
-
return path.join(path.dirname(baseDir), CLI_FOLDER, subdir);
|
|
40432
|
+
const cliUserRoot = (configDir, subdir) => gen(function* () {
|
|
40433
|
+
return (yield* Path).join(configDir, subdir);
|
|
40263
40434
|
});
|
|
40264
40435
|
const cliProjectRoot = (cwd, subdir) => gen(function* () {
|
|
40265
40436
|
return (yield* Path).join(cwd, CLI_FOLDER, subdir);
|
|
@@ -40390,7 +40561,7 @@ const makeScanner$1 = gen(function* () {
|
|
|
40390
40561
|
itemSchema: ServerProviderSkill,
|
|
40391
40562
|
scanUser: (now) => gen(function* () {
|
|
40392
40563
|
return {
|
|
40393
|
-
items: yield* scanCliSkillsDir(yield* cliUserRoot(config.
|
|
40564
|
+
items: yield* scanCliSkillsDir(yield* cliUserRoot(config.cliConfigDir, SKILLS_SUBDIR), SCOPE_USER),
|
|
40394
40565
|
scannedAt: now
|
|
40395
40566
|
};
|
|
40396
40567
|
}),
|
|
@@ -40508,7 +40679,7 @@ const makeScanner = gen(function* () {
|
|
|
40508
40679
|
itemSchema: ServerProviderSubagent,
|
|
40509
40680
|
scanUser: (now) => gen(function* () {
|
|
40510
40681
|
return {
|
|
40511
|
-
items: yield* scanCliAgentsDir(yield* cliUserRoot(config.
|
|
40682
|
+
items: yield* scanCliAgentsDir(yield* cliUserRoot(config.cliConfigDir, AGENTS_SUBDIR), SCOPE_USER),
|
|
40512
40683
|
scannedAt: now
|
|
40513
40684
|
};
|
|
40514
40685
|
}),
|
|
@@ -46663,8 +46834,15 @@ const makeProviderService = fn("makeProviderService")(function* (options) {
|
|
|
46663
46834
|
const routed = yield* resolveRoutableSession({
|
|
46664
46835
|
threadId: input.threadId,
|
|
46665
46836
|
operation: "ProviderService.interruptTurn",
|
|
46666
|
-
allowRecovery:
|
|
46837
|
+
allowRecovery: false
|
|
46667
46838
|
});
|
|
46839
|
+
if (!routed.isActive) {
|
|
46840
|
+
yield* logDebug("[provider.interruptTurn] no live session — skipping (no-op)", {
|
|
46841
|
+
threadId: input.threadId,
|
|
46842
|
+
operation: "ProviderService.interruptTurn"
|
|
46843
|
+
});
|
|
46844
|
+
return;
|
|
46845
|
+
}
|
|
46668
46846
|
metricProvider = routed.adapter.provider;
|
|
46669
46847
|
yield* annotateCurrentSpan({
|
|
46670
46848
|
"provider.operation": "interrupt-turn",
|
|
@@ -47568,7 +47746,7 @@ function extractCliResultText(rawStdout) {
|
|
|
47568
47746
|
const assistantMsg = messages.find((m) => m.type === "assistant");
|
|
47569
47747
|
return ((assistantMsg && assistantMsg.message?.content?.find((part) => part.type === "text")?.text) ?? "").trim();
|
|
47570
47748
|
}
|
|
47571
|
-
const makeCliTextGeneration = fn("makeCliTextGeneration")(function* (cliSettings, environment = process.env) {
|
|
47749
|
+
const makeCliTextGeneration = fn("makeCliTextGeneration")(function* (cliJs, cliSettings, environment = process.env) {
|
|
47572
47750
|
const commandSpawner = yield* ChildProcessSpawner;
|
|
47573
47751
|
const cliEnvironment = {
|
|
47574
47752
|
...environment,
|
|
@@ -47576,12 +47754,12 @@ const makeCliTextGeneration = fn("makeCliTextGeneration")(function* (cliSettings
|
|
|
47576
47754
|
};
|
|
47577
47755
|
const readStreamAsString = (operation, stream) => stream.pipe(decodeText(), runFold(() => "", (acc, chunk) => acc + chunk), mapError((cause) => normalizeCliError(CLI_NAME$1, operation, cause, "Failed to collect process output")));
|
|
47578
47756
|
const runCliCommand = fn("runCliCommand")(function* (input) {
|
|
47579
|
-
const resolved =
|
|
47757
|
+
const resolved = buildCliSpawn(cliJs, [
|
|
47580
47758
|
"-p",
|
|
47581
47759
|
input.prompt,
|
|
47582
47760
|
"--output-format",
|
|
47583
47761
|
"json"
|
|
47584
|
-
]
|
|
47762
|
+
]);
|
|
47585
47763
|
const command = make$46(resolved.command, [...resolved.args], {
|
|
47586
47764
|
env: cliEnvironment,
|
|
47587
47765
|
cwd: input.cwd,
|
|
@@ -52008,14 +52186,13 @@ const makeAcpSessionRuntime = (options) => gen(function* () {
|
|
|
52008
52186
|
status: "failed",
|
|
52009
52187
|
cause
|
|
52010
52188
|
})))));
|
|
52011
|
-
const
|
|
52012
|
-
const child = yield* spawner.spawn(make$46(resolved.command, [...resolved.args], {
|
|
52189
|
+
const child = yield* spawner.spawn(make$46(options.spawn.command, [...options.spawn.args], {
|
|
52013
52190
|
...options.spawn.cwd ? { cwd: options.spawn.cwd } : {},
|
|
52014
52191
|
...options.spawn.env ? { env: {
|
|
52015
52192
|
...process.env,
|
|
52016
52193
|
...options.spawn.env
|
|
52017
52194
|
} } : {},
|
|
52018
|
-
shell:
|
|
52195
|
+
shell: false
|
|
52019
52196
|
})).pipe(provideService(Scope, runtimeScope), mapError((cause) => new AcpSpawnError({
|
|
52020
52197
|
command: options.spawn.command,
|
|
52021
52198
|
cause
|
|
@@ -52464,14 +52641,15 @@ function parseLaunchArgs(launchArgs) {
|
|
|
52464
52641
|
if (!trimmed || trimmed.length === 0) return [];
|
|
52465
52642
|
return trimmed.split(/\s+/);
|
|
52466
52643
|
}
|
|
52467
|
-
function buildCliAcpSpawnInput(cliSettings, cwd, environment) {
|
|
52644
|
+
function buildCliAcpSpawnInput(cliJs, cliSettings, cwd, environment) {
|
|
52468
52645
|
const env = { ...environment };
|
|
52469
52646
|
const homePath = cliSettings?.homePath?.trim();
|
|
52470
52647
|
if (homePath) env.CLI_HOME = homePath;
|
|
52471
52648
|
if (ACP_SERVER_NO_SSL) env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
52649
|
+
const spawn = buildCliSpawn(cliJs, [...parseLaunchArgs(cliSettings?.launchArgs), "--acp"]);
|
|
52472
52650
|
return {
|
|
52473
|
-
command:
|
|
52474
|
-
args: [...
|
|
52651
|
+
command: spawn.command,
|
|
52652
|
+
args: [...spawn.args],
|
|
52475
52653
|
cwd,
|
|
52476
52654
|
...Object.keys(env).length > 0 ? { env } : {}
|
|
52477
52655
|
};
|
|
@@ -52479,7 +52657,7 @@ function buildCliAcpSpawnInput(cliSettings, cwd, environment) {
|
|
|
52479
52657
|
const makeCliAcpRuntime = (input) => gen(function* () {
|
|
52480
52658
|
const acpContext = yield* build(AcpSessionRuntime.layer({
|
|
52481
52659
|
...input,
|
|
52482
|
-
spawn: buildCliAcpSpawnInput(input.cliSettings, input.cwd, input.environment),
|
|
52660
|
+
spawn: buildCliAcpSpawnInput(input.cliJs, input.cliSettings, input.cwd, input.environment),
|
|
52483
52661
|
authMethodId: CLI_AUTH_METHOD_ID
|
|
52484
52662
|
}).pipe(provide$2(succeed$3(ChildProcessSpawner, input.childProcessSpawner))));
|
|
52485
52663
|
return yield* service(AcpSessionRuntime).pipe(provide(acpContext));
|
|
@@ -53194,6 +53372,7 @@ function makeCliAdapter(cliSettings, options) {
|
|
|
53194
53372
|
cliSettings,
|
|
53195
53373
|
...options?.environment ? { environment: options.environment } : {},
|
|
53196
53374
|
childProcessSpawner,
|
|
53375
|
+
cliJs: serverConfig.cliJs,
|
|
53197
53376
|
cwd,
|
|
53198
53377
|
clientInfo: {
|
|
53199
53378
|
name: "t3-code",
|
|
@@ -53870,6 +54049,40 @@ function makeCliAdapter(cliSettings, options) {
|
|
|
53870
54049
|
});
|
|
53871
54050
|
}
|
|
53872
54051
|
|
|
54052
|
+
//#endregion
|
|
54053
|
+
//#region src/provider/providerSnapshot.ts
|
|
54054
|
+
function isCommandMissingCause(error) {
|
|
54055
|
+
const lower = error.message.toLowerCase();
|
|
54056
|
+
return lower.includes("enoent") || lower.includes("notfound");
|
|
54057
|
+
}
|
|
54058
|
+
function parseGenericCliVersion(output) {
|
|
54059
|
+
return output.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
|
|
54060
|
+
}
|
|
54061
|
+
function buildServerProvider(input) {
|
|
54062
|
+
const versionAdvisory = input.driver ? createProviderVersionAdvisory({
|
|
54063
|
+
driver: input.driver,
|
|
54064
|
+
currentVersion: input.probe.version,
|
|
54065
|
+
checkedAt: input.checkedAt
|
|
54066
|
+
}) : void 0;
|
|
54067
|
+
return {
|
|
54068
|
+
displayName: input.presentation.displayName,
|
|
54069
|
+
...input.presentation.badgeLabel ? { badgeLabel: input.presentation.badgeLabel } : {},
|
|
54070
|
+
...typeof input.presentation.showInteractionModeToggle === "boolean" ? { showInteractionModeToggle: input.presentation.showInteractionModeToggle } : {},
|
|
54071
|
+
enabled: input.enabled,
|
|
54072
|
+
installed: input.probe.installed,
|
|
54073
|
+
version: input.probe.version,
|
|
54074
|
+
status: input.enabled ? input.probe.status : "disabled",
|
|
54075
|
+
auth: input.probe.auth,
|
|
54076
|
+
checkedAt: input.checkedAt,
|
|
54077
|
+
...input.probe.message ? { message: input.probe.message } : {},
|
|
54078
|
+
models: input.models,
|
|
54079
|
+
slashCommands: [...input.slashCommands ?? []],
|
|
54080
|
+
skills: [...input.skills ?? []],
|
|
54081
|
+
...versionAdvisory ? { versionAdvisory } : {}
|
|
54082
|
+
};
|
|
54083
|
+
}
|
|
54084
|
+
const collectStreamAsString = (stream) => collectUint8StreamText({ stream }).pipe(map$3((collected) => collected.text));
|
|
54085
|
+
|
|
53873
54086
|
//#endregion
|
|
53874
54087
|
//#region src/provider/Layers/CliProvider.ts
|
|
53875
54088
|
const VERSION_TIMEOUT_MS = 8e3;
|
|
@@ -53928,9 +54141,9 @@ function buildInitialCliProviderSnapshot(cliSettings) {
|
|
|
53928
54141
|
});
|
|
53929
54142
|
});
|
|
53930
54143
|
}
|
|
53931
|
-
const runCliVersionCommand = (
|
|
54144
|
+
const runCliVersionCommand = (cliJs, environment = process.env) => gen(function* () {
|
|
53932
54145
|
const spawner = yield* ChildProcessSpawner;
|
|
53933
|
-
const resolved =
|
|
54146
|
+
const resolved = buildCliSpawn(cliJs, ["--version"]);
|
|
53934
54147
|
const command = make$46(resolved.command, [...resolved.args], {
|
|
53935
54148
|
env: environment,
|
|
53936
54149
|
shell: resolved.shell
|
|
@@ -53947,7 +54160,7 @@ const runCliVersionCommand = (cliSettings, environment = process.env) => gen(fun
|
|
|
53947
54160
|
code: exitCode
|
|
53948
54161
|
};
|
|
53949
54162
|
}).pipe(scoped);
|
|
53950
|
-
const checkCliProviderStatus = fn("checkCliProviderStatus")(function* (cliSettings, environment = process.env) {
|
|
54163
|
+
const checkCliProviderStatus = fn("checkCliProviderStatus")(function* (cliJs, cliSettings, environment = process.env) {
|
|
53951
54164
|
const checkedAt = formatIso(yield* now);
|
|
53952
54165
|
if (!cliSettings.enabled) return buildServerProvider({
|
|
53953
54166
|
presentation: CLI_PRESENTATION,
|
|
@@ -53962,7 +54175,7 @@ const checkCliProviderStatus = fn("checkCliProviderStatus")(function* (cliSettin
|
|
|
53962
54175
|
message: `${CLI_NAME} отключён в настройках ${APP_NAME}.`
|
|
53963
54176
|
}
|
|
53964
54177
|
});
|
|
53965
|
-
const versionProbe = yield* runCliVersionCommand(
|
|
54178
|
+
const versionProbe = yield* runCliVersionCommand(cliJs, environment).pipe(timeoutOption(VERSION_TIMEOUT_MS), result);
|
|
53966
54179
|
if (isFailure$1(versionProbe)) {
|
|
53967
54180
|
const error = versionProbe.failure;
|
|
53968
54181
|
return buildServerProvider({
|
|
@@ -54138,6 +54351,7 @@ const CliDriver = {
|
|
|
54138
54351
|
defaultConfig: () => decodeCliSettings({}),
|
|
54139
54352
|
create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => gen(function* () {
|
|
54140
54353
|
const spawner = yield* ChildProcessSpawner;
|
|
54354
|
+
const serverConfig = yield* ServerConfig;
|
|
54141
54355
|
const eventLoggers = yield* ProviderEventLoggers;
|
|
54142
54356
|
const processEnv = mergeProviderInstanceEnvironment(environment);
|
|
54143
54357
|
const continuationIdentity = defaultProviderContinuationIdentity({
|
|
@@ -54163,8 +54377,8 @@ const CliDriver = {
|
|
|
54163
54377
|
...eventLoggers.native ? { nativeEventLogger: eventLoggers.native } : {},
|
|
54164
54378
|
instanceId
|
|
54165
54379
|
});
|
|
54166
|
-
const textGeneration = yield* makeCliTextGeneration(effectiveConfig, processEnv);
|
|
54167
|
-
const checkProvider = checkCliProviderStatus(effectiveConfig, processEnv).pipe(map$3(stampIdentity), provideService(ChildProcessSpawner, spawner));
|
|
54380
|
+
const textGeneration = yield* makeCliTextGeneration(serverConfig.cliJs, effectiveConfig, processEnv);
|
|
54381
|
+
const checkProvider = checkCliProviderStatus(serverConfig.cliJs, effectiveConfig, processEnv).pipe(map$3(stampIdentity), provideService(ChildProcessSpawner, spawner));
|
|
54168
54382
|
return {
|
|
54169
54383
|
instanceId,
|
|
54170
54384
|
driverKind: DRIVER_KIND,
|
|
@@ -57732,6 +57946,13 @@ const make$3 = gen(function* () {
|
|
|
57732
57946
|
const turnId = toTurnId$1(event.turnId);
|
|
57733
57947
|
if (turnId) {
|
|
57734
57948
|
const assistantMessageIds = yield* getAssistantMessageIdsForTurn(thread.id, turnId);
|
|
57949
|
+
yield* logDebug("[ingestion.turn-completed] finalize assistant messages", {
|
|
57950
|
+
threadId: thread.id,
|
|
57951
|
+
turnId,
|
|
57952
|
+
state: event.type === "turn.completed" ? event.payload.state : void 0,
|
|
57953
|
+
assistantMessageIdCount: assistantMessageIds.size,
|
|
57954
|
+
assistantMessageIds: Array.from(assistantMessageIds)
|
|
57955
|
+
});
|
|
57735
57956
|
yield* forEach(assistantMessageIds, (assistantMessageId) => finalizeAssistantMessage({
|
|
57736
57957
|
event,
|
|
57737
57958
|
threadId: thread.id,
|
|
@@ -60850,8 +61071,9 @@ const runServerCommand = (flags, options) => gen(function* () {
|
|
|
60850
61071
|
injectExtraPaths: getOrUndefined(flags.injectExtraPaths),
|
|
60851
61072
|
windowsUseBashFor: getOrUndefined(flags.windowsUseBashFor)
|
|
60852
61073
|
});
|
|
60853
|
-
|
|
60854
|
-
|
|
61074
|
+
const cli = yield* resolveStartupCli;
|
|
61075
|
+
if (!getOrElse(flags.noPreflightCheck, () => false)) yield* runStartupChecks(cli.cliJs);
|
|
61076
|
+
const config = yield* resolveServerConfig(flags, yield* LogLevel, cli, options);
|
|
60855
61077
|
if (config.mode === DESKTOP_RUNTIME_MODE) yield* assertLocalPortAvailable(config.port);
|
|
60856
61078
|
return yield* runServer.pipe(provideService(ServerConfig, config), provide(succeed$3(MinimumLogLevel, config.logLevel)));
|
|
60857
61079
|
});
|
|
@@ -61028,7 +61250,7 @@ const fetchPairingStartupUrl = (origin) => tryPromise({
|
|
|
61028
61250
|
})
|
|
61029
61251
|
}).pipe(catch_(() => succeed(null)));
|
|
61030
61252
|
const resolveDerivedPaths = (input) => gen(function* () {
|
|
61031
|
-
return yield* deriveServerPaths(yield* resolveBaseDir(input.baseDirOverride !== void 0 ? yield* expandHomePath$4(input.baseDirOverride.trim()) : void 0), input.devUrlOverride);
|
|
61253
|
+
return yield* deriveServerPaths(yield* resolveBaseDir((input.baseDirOverride !== void 0 ? yield* expandHomePath$4(input.baseDirOverride.trim()) : void 0) ?? input.ourRoot), input.devUrlOverride);
|
|
61032
61254
|
});
|
|
61033
61255
|
const announceReady = (input) => gen(function* () {
|
|
61034
61256
|
yield* printDaemonReadyBanner({
|
|
@@ -61043,8 +61265,12 @@ const runDaemonLauncher = (input) => gen(function* () {
|
|
|
61043
61265
|
injectExtraPaths: input.injectExtraPaths,
|
|
61044
61266
|
windowsUseBashFor: input.windowsUseBashFor
|
|
61045
61267
|
});
|
|
61046
|
-
|
|
61047
|
-
|
|
61268
|
+
const cli = yield* resolveStartupCli;
|
|
61269
|
+
if (!input.noPreflightCheck) yield* runStartupChecks(cli.cliJs);
|
|
61270
|
+
const { serverRuntimeStatePath, logsDir } = yield* resolveDerivedPaths({
|
|
61271
|
+
...input,
|
|
61272
|
+
ourRoot: cli.ourRoot
|
|
61273
|
+
});
|
|
61048
61274
|
const launcherLogPath = path$1.join(logsDir, "ru-fork.log");
|
|
61049
61275
|
const existingState = yield* readPersistedServerRuntimeState(serverRuntimeStatePath);
|
|
61050
61276
|
if (existingState._tag === "Some") {
|
|
@@ -61082,7 +61308,11 @@ const runDaemonLauncher = (input) => gen(function* () {
|
|
|
61082
61308
|
});
|
|
61083
61309
|
});
|
|
61084
61310
|
const runStopCommand = (input) => gen(function* () {
|
|
61085
|
-
const
|
|
61311
|
+
const cli = yield* resolveStartupCli;
|
|
61312
|
+
const state = yield* readPersistedServerRuntimeState((yield* resolveDerivedPaths({
|
|
61313
|
+
...input,
|
|
61314
|
+
ourRoot: cli.ourRoot
|
|
61315
|
+
})).serverRuntimeStatePath);
|
|
61086
61316
|
if (state._tag !== "Some") {
|
|
61087
61317
|
yield* log("");
|
|
61088
61318
|
yield* log(headline(ARROW_DIM, "ru-fork не запущен"));
|