@sberpay-cli/cli-diagnostic 0.4.0-dev.8 → 0.4.0-dev.9

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.
@@ -7375,7 +7375,7 @@ import { fileURLToPath } from "node:url";
7375
7375
  var NPM_PACKAGE = true ? "@sberpay-cli/cli-diagnostic" : "@sberpay-cli/cli";
7376
7376
  var kind = true ? "release" : "dev";
7377
7377
  var BUILD = {
7378
- sourceCommit: true ? "7f4bbd2ecf2ceb9eec2d043b2487863d9a74e578" : "dev",
7378
+ sourceCommit: true ? "db0ec0c6594c1d28d55158cde8f6e5d564251e46" : "dev",
7379
7379
  profile: true ? "diagnostic" : "standard",
7380
7380
  kind,
7381
7381
  // Бандл — dist/cli.mjs в корне пакета; этот файл под tsx — packages/cli/src/core/.
@@ -27152,7 +27152,7 @@ import { join } from "node:path";
27152
27152
  import { mkdirSync, existsSync, openSync, writeFileSync, fsyncSync, closeSync, renameSync, rmSync } from "node:fs";
27153
27153
  import { dirname as dirname2 } from "node:path";
27154
27154
  import { randomUUID } from "node:crypto";
27155
- function atomicJson(path2, value) {
27155
+ function atomicJson(path2, value, options = {}) {
27156
27156
  if (process.platform === "win32") {
27157
27157
  ensureWindowsPrivateDirectory(dirname2(path2));
27158
27158
  if (existsSync(path2)) assertWindowsPrivatePath(path2);
@@ -27161,7 +27161,7 @@ function atomicJson(path2, value) {
27161
27161
  try {
27162
27162
  const fd2 = openSync(temp, "wx", 384);
27163
27163
  try {
27164
- writeFileSync(fd2, JSON.stringify(value, null, 2) + "\n");
27164
+ writeFileSync(fd2, (options.compact ? JSON.stringify(value) : JSON.stringify(value, null, 2)) + "\n");
27165
27165
  fsyncSync(fd2);
27166
27166
  } finally {
27167
27167
  closeSync(fd2);
@@ -28040,17 +28040,17 @@ async function probePay(ctx, opts, order, purchaseContext) {
28040
28040
  try {
28041
28041
  const store = await credentialStoreFor(ctx);
28042
28042
  await (async () => {
28043
- const snapshot = await withBindingLock(
28043
+ const snapshot2 = await withBindingLock(
28044
28044
  bindingScope(ctx),
28045
28045
  async () => store.read()
28046
28046
  );
28047
- if (!snapshot)
28047
+ if (!snapshot2)
28048
28048
  throw new CliError(
28049
28049
  EXIT.AUTH,
28050
28050
  "NO_CARD",
28051
28051
  "\u041A\u0430\u0440\u0442\u0430 \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u0430 \u043D\u0435 \u043F\u0440\u0438\u0432\u044F\u0437\u0430\u043D\u0430"
28052
28052
  );
28053
- const prepared3 = prepareProbeToken(snapshot.value.agentToken);
28053
+ const prepared3 = prepareProbeToken(snapshot2.value.agentToken);
28054
28054
  mutationApplied = true;
28055
28055
  journal.bindScope(store.scopeHash);
28056
28056
  try {
@@ -28496,7 +28496,7 @@ async function sendCase(caseId, env = process.env, deps = {}) {
28496
28496
  }
28497
28497
 
28498
28498
  // packages/cli/src/report/binding.ts
28499
- import { readdir, realpath as realpath3, stat as stat3 } from "node:fs/promises";
28499
+ import { lstat as lstat2, readdir, realpath as realpath3, stat as stat3 } from "node:fs/promises";
28500
28500
  import { homedir as homedir3 } from "node:os";
28501
28501
  import { join as join9 } from "node:path";
28502
28502
 
@@ -28612,13 +28612,13 @@ async function collectTranscript(agent, parser3, binding, options = {}) {
28612
28612
  return result;
28613
28613
  }
28614
28614
  handle = await open5(path2, constants5.O_RDONLY | (constants5.O_NOFOLLOW ?? 0) | (constants5.O_NONBLOCK ?? 0));
28615
- const snapshot = await handle.stat();
28616
- if (!snapshot.isFile() || snapshot.ino !== before.ino || snapshot.dev !== before.dev || process.getuid && snapshot.uid !== process.getuid()) {
28615
+ const snapshot2 = await handle.stat();
28616
+ if (!snapshot2.isFile() || snapshot2.ino !== before.ino || snapshot2.dev !== before.dev || process.getuid && snapshot2.uid !== process.getuid()) {
28617
28617
  result.coverage = "failed";
28618
28618
  warn(result, "UNSAFE_SOURCE");
28619
28619
  return result;
28620
28620
  }
28621
- const length = Math.min(snapshot.size, maxBytes);
28621
+ const length = Math.min(snapshot2.size, maxBytes);
28622
28622
  const buffer = Buffer.alloc(length);
28623
28623
  let offset = 0;
28624
28624
  while (offset < length) {
@@ -28632,10 +28632,10 @@ async function collectTranscript(agent, parser3, binding, options = {}) {
28632
28632
  offset += bytesRead;
28633
28633
  }
28634
28634
  result.snapshotBytes = offset;
28635
- if (snapshot.size > maxBytes) warn(result, "SIZE_LIMIT");
28635
+ if (snapshot2.size > maxBytes) warn(result, "SIZE_LIMIT");
28636
28636
  if (offset < length) warn(result, "SOURCE_CHANGED");
28637
28637
  const after = await handle.stat();
28638
- if (after.size < snapshot.size || after.size === snapshot.size && after.mtimeMs !== snapshot.mtimeMs) warn(result, "SOURCE_CHANGED");
28638
+ if (after.size < snapshot2.size || after.size === snapshot2.size && after.mtimeMs !== snapshot2.mtimeMs) warn(result, "SOURCE_CHANGED");
28639
28639
  const lines = buffer.subarray(0, offset).toString("utf8").split("\n");
28640
28640
  if (lines.at(-1) !== "") {
28641
28641
  lines.pop();
@@ -28920,12 +28920,34 @@ async function codexBinding(env) {
28920
28920
  return { sessionId, transcriptPath, allowedRoot, sourceVersion: runtimeVersion(meta3.cli_version) };
28921
28921
  }
28922
28922
  async function claudeBinding(env) {
28923
- const sessionId = requireId(env.SBERPAY_CLAUDE_SESSION_ID);
28923
+ const hookId = env.SBERPAY_CLAUDE_SESSION_ID, hostId = env.CLAUDE_CODE_SESSION_ID;
28924
+ if (hookId && hostId && hookId !== hostId) throw new BindingError("CONFLICTING_SESSION_IDS");
28925
+ if (!hookId || hostId && (!env.SBERPAY_CLAUDE_TRANSCRIPT || !env.SBERPAY_CLAUDE_CONFIG_DIR)) return claudeHostBinding(env);
28926
+ const sessionId = requireId(hookId);
28924
28927
  if (!env.SBERPAY_CLAUDE_TRANSCRIPT || !env.SBERPAY_CLAUDE_CONFIG_DIR) throw new BindingError("CLAUDE_HOOK_NOT_ACTIVE");
28925
28928
  const home = await realpath3(env.SBERPAY_CLAUDE_CONFIG_DIR);
28926
28929
  if (env.CLAUDE_CONFIG_DIR && await realpath3(env.CLAUDE_CONFIG_DIR) !== home) throw new BindingError("PROFILE_MISMATCH");
28930
+ return claudeTranscript(sessionId, env.SBERPAY_CLAUDE_TRANSCRIPT, join9(home, "projects"));
28931
+ }
28932
+ async function claudeHostBinding(env) {
28933
+ const sessionId = requireId(env.CLAUDE_CODE_SESSION_ID);
28934
+ const home = await realpath3(env.CLAUDE_CONFIG_DIR || join9(homedir3(), ".claude"));
28927
28935
  const allowedRoot = join9(home, "projects");
28928
- const transcriptPath = env.SBERPAY_CLAUDE_TRANSCRIPT;
28936
+ const matches = [];
28937
+ for (const entry of await readdir(allowedRoot, { withFileTypes: true })) {
28938
+ if (!entry.isDirectory()) continue;
28939
+ const candidate = join9(allowedRoot, entry.name, `${sessionId}.jsonl`);
28940
+ try {
28941
+ if ((await lstat2(candidate)).isFile()) matches.push(candidate);
28942
+ } catch (error62) {
28943
+ if (error62.code !== "ENOENT") throw error62;
28944
+ }
28945
+ if (matches.length > 1) throw new BindingError("AMBIGUOUS_SESSION_SOURCE");
28946
+ }
28947
+ if (!matches.length) throw new BindingError("SOURCE_NOT_FOUND");
28948
+ return claudeTranscript(sessionId, matches[0], allowedRoot);
28949
+ }
28950
+ async function claudeTranscript(sessionId, transcriptPath, allowedRoot) {
28929
28951
  const prefix = await readPrefix(allowedRoot, transcriptPath);
28930
28952
  const lines = prefix.split("\n");
28931
28953
  lines.pop();
@@ -28941,7 +28963,7 @@ async function claudeBinding(env) {
28941
28963
  async function resolveCurrentBinding(agent = "auto", env = process.env) {
28942
28964
  try {
28943
28965
  const hasCodex = !!(env.CODEX_THREAD_ID || env.CODEX_SESSION_ID);
28944
- const hasClaude = !!env.SBERPAY_CLAUDE_SESSION_ID;
28966
+ const hasClaude = !!(env.SBERPAY_CLAUDE_SESSION_ID || env.CLAUDE_CODE_SESSION_ID);
28945
28967
  if (agent === "auto") {
28946
28968
  if (hasCodex && hasClaude) throw new BindingError("AMBIGUOUS_HOST");
28947
28969
  if (!hasCodex && !hasClaude) throw new BindingError("HOST_NOT_DETECTED");
@@ -28978,7 +29000,7 @@ function sessionSummary(result) {
28978
29000
 
28979
29001
  // packages/cli/src/report/package.ts
28980
29002
  import { createHash as createHash5, randomUUID as randomUUID6 } from "node:crypto";
28981
- import { lstat as lstat2, readdir as readdir2, writeFile as writeFile2, rename as rename2, rm as rm2 } from "node:fs/promises";
29003
+ import { lstat as lstat3, readdir as readdir2, writeFile as writeFile2, rename as rename2, rm as rm2 } from "node:fs/promises";
28982
29004
  import { join as join11 } from "node:path";
28983
29005
 
28984
29006
  // node_modules/fflate/esm/index.mjs
@@ -29698,7 +29720,7 @@ function zipSync(data, opts) {
29698
29720
  }
29699
29721
 
29700
29722
  // packages/cli/src/core/version.ts
29701
- var CLI_VERSION = "0.4.0-dev.8";
29723
+ var CLI_VERSION = "0.4.0-dev.9";
29702
29724
 
29703
29725
  // packages/cli/src/report/journal.ts
29704
29726
  import { closeSync as closeSync5, constants as constants6, openSync as openSync5, writeSync as writeSync2 } from "node:fs";
@@ -29798,7 +29820,7 @@ async function logs(dir2, sourceId) {
29798
29820
  for (const name of matching.slice(0, 1e3)) {
29799
29821
  try {
29800
29822
  const path2 = join11(dir2, name);
29801
- const info = await lstat2(path2);
29823
+ const info = await lstat3(path2);
29802
29824
  if (!info.isFile() || info.isSymbolicLink() || info.size > 256 * 1024) throw new Error("unsafe");
29803
29825
  if (bytes + info.size > 8 * 1024 * 1024) {
29804
29826
  warnings.push("BYTE_LIMIT");
@@ -29879,9 +29901,11 @@ var enabled = (env = process.env) => env.SBERPAY_DIAGNOSTIC_CAPTURE === "1";
29879
29901
  function dir(env) {
29880
29902
  return privateDir2(join12(privateDir2(join12(cliPaths(env).stateDir, "diagnostics")), "episodes"));
29881
29903
  }
29904
+ var SESSION_HINT = "CLI \u043D\u0435 \u043D\u0430\u0448\u0451\u043B \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u0442\u0435\u043A\u0443\u0449\u0435\u0433\u043E \u0447\u0430\u0442\u0430: \u043F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 report session --json \u0438 \u043F\u0435\u0440\u0435\u0441\u043A\u0430\u0436\u0438\u0442\u0435 \u043A\u043B\u0438\u0435\u043D\u0442\u0443 \u043A\u043E\u0434. \u0412\u0440\u0443\u0447\u043D\u0443\u044E \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u043D\u044B\u0435 \u043D\u0435 \u043F\u043E\u0434\u0441\u0442\u0430\u0432\u043B\u044F\u0439\u0442\u0435";
29882
29905
  async function session(env) {
29883
29906
  const s = await collectCurrentSession("auto", env);
29884
- if (s.status !== "bound" || s.transcript.identity !== "verified") throw Error("EPISODE_SESSION_UNAVAILABLE");
29907
+ if (s.status !== "bound") throw new BindingError("EPISODE_SESSION_UNAVAILABLE", SESSION_HINT, s.code);
29908
+ if (s.transcript.identity !== "verified") throw new BindingError("EPISODE_SESSION_UNAVAILABLE", SESSION_HINT, "IDENTITY_" + String(s.transcript.identity).toUpperCase());
29885
29909
  return s;
29886
29910
  }
29887
29911
  async function atomic(path2, value) {
@@ -30535,11 +30559,11 @@ async function livePay(ctx, opts) {
30535
30559
  const order = validatePayOptions(opts);
30536
30560
  const purchaseContext = parseContext(opts.context);
30537
30561
  const store = await credentialStoreFor(ctx);
30538
- const snapshot = await withBindingLock(
30562
+ const snapshot2 = await withBindingLock(
30539
30563
  bindingScope(ctx),
30540
30564
  async () => store.read()
30541
30565
  );
30542
- if (!snapshot)
30566
+ if (!snapshot2)
30543
30567
  throw new CliError(EXIT.AUTH, "NO_CARD", "\u041A\u0430\u0440\u0442\u0430 \u0434\u043B\u044F \u0430\u0433\u0435\u043D\u0442\u0430 \u043D\u0435 \u043F\u0440\u0438\u0432\u044F\u0437\u0430\u043D\u0430", {
30544
30568
  hint: RELINK_HINT
30545
30569
  });
@@ -30550,7 +30574,7 @@ async function livePay(ctx, opts) {
30550
30574
  currency: "643",
30551
30575
  ...purchaseContext ? { purchaseContext } : {}
30552
30576
  };
30553
- const res = await ctx.elcomV3.pay(input2, snapshot.value.agentToken).catch(withOutcomeHint);
30577
+ const res = await ctx.elcomV3.pay(input2, snapshot2.value.agentToken).catch(withOutcomeHint);
30554
30578
  if (res.status === "SUCCESS") {
30555
30579
  try {
30556
30580
  await recordPayment(res.mdOrder ?? order.mdOrder, process.env, opts.merchant);
@@ -30570,7 +30594,7 @@ async function livePay(ctx, opts) {
30570
30594
  }
30571
30595
  const reason = res.reasonCode;
30572
30596
  if (reason === "TOKEN_EXPIRED" || reason === "TOKEN_REVOKED")
30573
- await forgetLocalToken(ctx, snapshot.fingerprint);
30597
+ await forgetLocalToken(ctx, snapshot2.fingerprint);
30574
30598
  const hint = reason ? HINTS[reason] ?? unknownReasonHint(reason) : void 0;
30575
30599
  ctx.out.result(
30576
30600
  {
@@ -34168,11 +34192,11 @@ var program = new Command();
34168
34192
 
34169
34193
  // packages/cli/src/report/command.ts
34170
34194
  import { homedir as homedir5 } from "node:os";
34171
- import { join as join16, dirname as dirname6, resolve as resolve2 } from "node:path";
34195
+ import { join as join16, dirname as dirname6, resolve as resolve2, isAbsolute as isAbsolute3 } from "node:path";
34172
34196
 
34173
34197
  // packages/cli/src/report/claudeHook.ts
34174
34198
  import { dirname as dirname5 } from "node:path";
34175
- import { open as open7, chmod, lstat as lstat3, mkdir as mkdir2, readFile as readFile2, realpath as realpath4, rename as rename5, writeFile as writeFile4 } from "node:fs/promises";
34199
+ import { open as open7, chmod, lstat as lstat4, mkdir as mkdir2, readFile as readFile2, realpath as realpath4, rename as rename5, writeFile as writeFile4 } from "node:fs/promises";
34176
34200
  import { constants as constants7 } from "node:fs";
34177
34201
  import { homedir as homedir4 } from "node:os";
34178
34202
  import { join as join15 } from "node:path";
@@ -34192,7 +34216,7 @@ async function captureClaudeBinding(input2, env = process.env) {
34192
34216
  if (process.platform === "win32") {
34193
34217
  assertWindowsPrivatePath(dirname5(env.CLAUDE_ENV_FILE));
34194
34218
  try {
34195
- await lstat3(env.CLAUDE_ENV_FILE);
34219
+ await lstat4(env.CLAUDE_ENV_FILE);
34196
34220
  assertWindowsPrivatePath(env.CLAUDE_ENV_FILE);
34197
34221
  } catch (e) {
34198
34222
  if (e.code !== "ENOENT") throw e;
@@ -34207,13 +34231,16 @@ async function captureClaudeBinding(input2, env = process.env) {
34207
34231
  await file2.close();
34208
34232
  }
34209
34233
  }
34210
- async function installClaudeHook(configDir, command) {
34211
- if (process.platform === "win32") ensureWindowsPrivateDirectory(configDir);
34212
- else await mkdir2(configDir, { recursive: true, mode: 448 });
34234
+ var claudeHookCommandFor = (args) => [...args, "report", "claude-hook"].map((s) => shellQuote(process.platform === "win32" ? s.replaceAll("\\", "/") : s)).join(" ");
34235
+ async function readSettings(configDir, create2) {
34236
+ if (create2) {
34237
+ if (process.platform === "win32") ensureWindowsPrivateDirectory(configDir);
34238
+ else await mkdir2(configDir, { recursive: true, mode: 448 });
34239
+ }
34213
34240
  const path2 = join15(configDir, "settings.json");
34214
34241
  let original;
34215
34242
  try {
34216
- const info = await lstat3(path2);
34243
+ const info = await lstat4(path2);
34217
34244
  if (!info.isFile() || info.isSymbolicLink()) throw new BindingError("UNSAFE_SETTINGS");
34218
34245
  if (process.platform === "win32") assertWindowsPrivatePath(path2);
34219
34246
  original = await readFile2(path2, "utf8");
@@ -34226,6 +34253,18 @@ async function installClaudeHook(configDir, command) {
34226
34253
  if (!hooks) throw new BindingError("INVALID_HOOK_SETTINGS");
34227
34254
  const start = hooks.SessionStart === void 0 ? [] : hooks.SessionStart;
34228
34255
  if (!Array.isArray(start)) throw new BindingError("INVALID_HOOK_SETTINGS");
34256
+ return { path: path2, original, settings, hooks, start };
34257
+ }
34258
+ async function writeSettings(path2, original, settings) {
34259
+ const suffix = randomUUID9();
34260
+ if (original !== void 0) await writeFile4(`${path2}.sberpay-${suffix}.bak`, original, { flag: "wx", mode: 384 });
34261
+ const temp = `${path2}.sberpay-${suffix}.tmp`;
34262
+ await writeFile4(temp, JSON.stringify(settings, null, 2) + "\n", { flag: "wx", mode: 384 });
34263
+ await rename5(temp, path2);
34264
+ await chmod(path2, 384);
34265
+ }
34266
+ async function installClaudeHook(configDir, command) {
34267
+ const { path: path2, original, settings, hooks, start } = await readSettings(configDir, true);
34229
34268
  if (start.some((entry) => {
34230
34269
  const group = object2(entry);
34231
34270
  if (!group || group.matcher !== void 0 || !Array.isArray(group.hooks)) return false;
@@ -34236,18 +34275,14 @@ async function installClaudeHook(configDir, command) {
34236
34275
  })) return;
34237
34276
  hooks.SessionStart = [...start, { hooks: [{ type: "command", command, timeout: 10 }] }];
34238
34277
  settings.hooks = hooks;
34239
- const suffix = randomUUID9();
34240
- if (original !== void 0) await writeFile4(`${path2}.sberpay-${suffix}.bak`, original, { flag: "wx", mode: 384 });
34241
- const temp = `${path2}.sberpay-${suffix}.tmp`;
34242
- await writeFile4(temp, JSON.stringify(settings, null, 2) + "\n", { flag: "wx", mode: 384 });
34243
- await rename5(temp, path2);
34244
- await chmod(path2, 384);
34278
+ await writeSettings(path2, original, settings);
34245
34279
  }
34246
34280
 
34247
34281
  // packages/cli/src/report/command.ts
34248
- function claudeHookCommand() {
34249
- const args = BUILD.kind === "release" ? [process.execPath, join16(BUILD.root, "dist", "cli.mjs")] : [process.execPath, "--import", import.meta.resolve("tsx"), join16(BUILD.root, "packages", "cli", "src", "main.ts")];
34250
- return [...args, "report", "claude-hook"].map((s) => shellQuote(process.platform === "win32" ? s.replaceAll("\\", "/") : s)).join(" ");
34282
+ function claudeHookCommand(env = process.env) {
34283
+ const launcher = env.SBERPAY_DIAGNOSTIC_LAUNCHER;
34284
+ const args = BUILD.kind === "release" ? [process.execPath, launcher && isAbsolute3(launcher) ? launcher : join16(BUILD.root, "dist", "cli.mjs")] : [process.execPath, "--import", import.meta.resolve("tsx"), join16(BUILD.root, "packages", "cli", "src", "main.ts")];
34285
+ return claudeHookCommandFor(args);
34251
34286
  }
34252
34287
  async function hookInput() {
34253
34288
  const chunks = [];
@@ -34296,7 +34331,7 @@ function registerReportCommands(program3) {
34296
34331
  });
34297
34332
  const output2 = () => createOutput(Boolean(program3.opts().json));
34298
34333
  const fail2 = (error62) => {
34299
- const code = error62 instanceof BindingError ? error62.code : "REPORT_FAILED";
34334
+ const code = error62 instanceof BindingError ? error62.code : error62 instanceof Error && /^[A-Z][A-Z0-9_]{2,63}$/.test(error62.message) ? error62.message : "REPORT_FAILED";
34300
34335
  output2().error(new CliError(EXIT.CONFIG_OR_NETWORK, code, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u0434\u0438\u0430\u0433\u043D\u043E\u0441\u0442\u0438\u043A\u0443: " + code, error62 instanceof BindingError ? { hint: error62.hint, reasonCode: error62.reasonCode } : {}));
34301
34336
  process.exitCode = EXIT.CONFIG_OR_NETWORK;
34302
34337
  };
@@ -35146,7 +35181,7 @@ import { join as join19 } from "node:path";
35146
35181
  // packages/cli/src/core/keyHelper.ts
35147
35182
  import { spawnSync as spawnSync3 } from "node:child_process";
35148
35183
  import { existsSync as existsSync6, realpathSync as realpathSync2, statSync } from "node:fs";
35149
- import { dirname as dirname8, isAbsolute as isAbsolute3, join as join18 } from "node:path";
35184
+ import { dirname as dirname8, isAbsolute as isAbsolute4, join as join18 } from "node:path";
35150
35185
  var PACKAGED = { "darwin-arm64": "darwin", "darwin-x64": "darwin", "linux-x64": "linux-x64", "linux-arm64": "linux-arm64", "win32-x64": "win32-x64", "win32-arm64": "win32-arm64" };
35151
35186
  function chipOf(platform2) {
35152
35187
  if (platform2 === "darwin") return "secure-enclave";
@@ -35202,7 +35237,7 @@ function unavailable(message2, hint) {
35202
35237
  }
35203
35238
  function findKeyHelper(opts) {
35204
35239
  const path2 = opts.override ?? opts.installed;
35205
- if (!isAbsolute3(path2)) throw unavailable(`\u041F\u0443\u0442\u044C \u043A \u0445\u0435\u043B\u043F\u0435\u0440\u0443 ${path2} \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0430\u0431\u0441\u043E\u043B\u044E\u0442\u043D\u044B\u043C`, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043F\u043E\u043B\u043D\u044B\u0439 \u043F\u0443\u0442\u044C \u0432 SBERPAY_KEY_HELPER");
35240
+ if (!isAbsolute4(path2)) throw unavailable(`\u041F\u0443\u0442\u044C \u043A \u0445\u0435\u043B\u043F\u0435\u0440\u0443 ${path2} \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0430\u0431\u0441\u043E\u043B\u044E\u0442\u043D\u044B\u043C`, "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043F\u043E\u043B\u043D\u044B\u0439 \u043F\u0443\u0442\u044C \u0432 SBERPAY_KEY_HELPER");
35206
35241
  if (!existsSync6(path2)) return void 0;
35207
35242
  if (!isTrustedFile(path2)) {
35208
35243
  throw unavailable(`\u0425\u0435\u043B\u043F\u0435\u0440 ${path2} \u043D\u0435 \u043F\u0440\u0438\u043D\u0430\u0434\u043B\u0435\u0436\u0438\u0442 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044E \u0438\u043B\u0438 \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u043D\u0430 \u0437\u0430\u043F\u0438\u0441\u044C \u0434\u0440\u0443\u0433\u0438\u043C`, "\u041F\u0435\u0440\u0435\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 sberpay-cli; \u043F\u0440\u0430\u0432\u0430 \u043D\u0430 \u0445\u0435\u043B\u043F\u0435\u0440 \u0438 \u0435\u0433\u043E \u043A\u0430\u0442\u0430\u043B\u043E\u0433: Unix 0755; Windows \u2014 \u0437\u0430\u043F\u0438\u0441\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0432\u043B\u0430\u0434\u0435\u043B\u044C\u0446\u0443, SYSTEM \u0438 Administrators");
@@ -35717,7 +35752,7 @@ var DeviceRequestClaims = external_exports.discriminatedUnion("op", [
35717
35752
  );
35718
35753
 
35719
35754
  // packages/contracts/src/v3/merchants.ts
35720
- var MERCHANT_PATHS = { check: "/merchants/check" };
35755
+ var MERCHANT_PATHS = { check: "/merchants/check", domains: "/merchants/domains" };
35721
35756
  var Merchant2 = external_exports.object({
35722
35757
  merchantId: external_exports.string(),
35723
35758
  name: external_exports.string(),
@@ -35732,6 +35767,15 @@ var CheckMerchantResponse2 = external_exports.object({
35732
35767
  supported: external_exports.boolean(),
35733
35768
  merchant: Merchant2.optional()
35734
35769
  });
35770
+ var MerchantDomain = external_exports.object({
35771
+ domain: external_exports.string().min(1).max(253),
35772
+ includeSubdomains: external_exports.boolean()
35773
+ });
35774
+ var MerchantDomainsResponse = external_exports.object({
35775
+ requestId: external_exports.string(),
35776
+ count: external_exports.number().int().nonnegative(),
35777
+ domains: external_exports.array(MerchantDomain).max(1e6)
35778
+ });
35735
35779
 
35736
35780
  // packages/cli/src/core/trustedRoots.ts
35737
35781
  import { createHash as createHash11, X509Certificate } from "node:crypto";
@@ -37409,8 +37453,9 @@ function createElcomClientV3(core, device2, options = {}) {
37409
37453
 
37410
37454
  // packages/cli/src/schemes/v2/merchantsClient.ts
37411
37455
  var PEER = "\u0424\u041F \u0423\u043C\u043D\u044B\u0435 \u043F\u043E\u043A\u0443\u043F\u043A\u0438";
37456
+ var DOMAINS_TIMEOUT_MS = 3e4;
37412
37457
  function createMerchantsClient(logger, baseUrl) {
37413
- const post = async (op, path2, body) => {
37458
+ const post = async (op, path2, body, extra = {}) => {
37414
37459
  if (!baseUrl) {
37415
37460
  throw new CliError(
37416
37461
  EXIT.CONFIG_OR_NETWORK,
@@ -37427,7 +37472,8 @@ function createMerchantsClient(logger, baseUrl) {
37427
37472
  peer: PEER,
37428
37473
  url: `${baseUrl}${path2}`,
37429
37474
  body: { requestId, ...body },
37430
- headers: { [HEADER_REQUEST_ID]: requestId }
37475
+ headers: { [HEADER_REQUEST_ID]: requestId },
37476
+ ...extra
37431
37477
  });
37432
37478
  };
37433
37479
  return {
@@ -37437,7 +37483,17 @@ function createMerchantsClient(logger, baseUrl) {
37437
37483
  domain: normalizeDomain(domain2)
37438
37484
  }),
37439
37485
  PEER
37440
- )
37486
+ ),
37487
+ domains: async () => {
37488
+ const res = expectResponse(
37489
+ MerchantDomainsResponse,
37490
+ await post("\u0441\u043F\u0438\u0441\u043E\u043A \u0434\u043E\u043C\u0435\u043D\u043E\u0432 \u043C\u0430\u0433\u0430\u0437\u0438\u043D\u043E\u0432", MERCHANT_PATHS.domains, {}, { timeoutMs: DOMAINS_TIMEOUT_MS, sensitive: true }),
37491
+ PEER
37492
+ );
37493
+ if (res.count !== res.domains.length)
37494
+ throw new CliError(EXIT.CONFIG_OR_NETWORK, "INVALID_RESPONSE", `${PEER}: count (${res.count}) \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0435\u0442 \u0441 \u0447\u0438\u0441\u043B\u043E\u043C \u0434\u043E\u043C\u0435\u043D\u043E\u0432 (${res.domains.length})`);
37495
+ return res;
37496
+ }
37441
37497
  };
37442
37498
  }
37443
37499
  function normalizeDomain(input2) {
@@ -38428,11 +38484,6 @@ async function authLoginCommand(ctx, opts) {
38428
38484
  session2?.legacy ? "LEGACY_PENDING_NO_LINK" : "LOGIN_STARTING",
38429
38485
  "\u0421\u0441\u044B\u043B\u043A\u0430 \u0442\u0435\u043A\u0443\u0449\u0435\u0439 \u043F\u043E\u043F\u044B\u0442\u043A\u0438 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430. \u041F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 auth status; \u0440\u0430\u043D\u0435\u0435 \u0432\u044B\u0434\u0430\u043D\u043D\u044B\u0439 \u0442\u043E\u043A\u0435\u043D \u043C\u043E\u0436\u043D\u043E \u043F\u0435\u0440\u0435\u0434\u0430\u0442\u044C \u0447\u0435\u0440\u0435\u0437 auth complete."
38430
38486
  );
38431
- process.stderr.write(`
38432
- \u041C\u0435\u0442\u043A\u0430 \u0441\u0435\u0441\u0441\u0438\u0438:
38433
- ${session2.mark.text}
38434
- ${session2.mark.names}
38435
- `);
38436
38487
  if (ctx.stand.cabinetMockUrl) process.stderr.write("\u041A\u0430\u0431\u0438\u043D\u0435\u0442 \u2014 \u043C\u043E\u043A, \u0430 \u043D\u0435 \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0438\u0439 \u043A\u0430\u0431\u0438\u043D\u0435\u0442 \u041A\u0413\u0421.\n");
38437
38488
  if (record2.mode === "local") await openInBrowser(session2.link, ctx.out, !opts.open);
38438
38489
  const update = updateForVersion(session2.cliLatest);
@@ -38484,19 +38535,19 @@ async function verifySavedToken(ctx, offline = false) {
38484
38535
  try {
38485
38536
  const store = await credentialStoreFor(ctx), scope = bindingScope(ctx);
38486
38537
  for (let attempt = 0; attempt < 3; attempt++) {
38487
- const { snapshot, meta: meta3 } = await withBindingLock(scope, async () => ({
38538
+ const { snapshot: snapshot2, meta: meta3 } = await withBindingLock(scope, async () => ({
38488
38539
  snapshot: store.read(),
38489
38540
  meta: offline ? loadConfig(ctx.paths).tokenMetaV3?.[store.scopeHash] : void 0
38490
38541
  }));
38491
- if (!snapshot) return { loggedIn: false, canPay: false };
38492
- const { tokenId, subId } = snapshot.value;
38542
+ if (!snapshot2) return { loggedIn: false, canPay: false };
38543
+ const { tokenId, subId } = snapshot2.value;
38493
38544
  if (offline) {
38494
38545
  return {
38495
38546
  loggedIn: true,
38496
38547
  canPay: false,
38497
38548
  source: "local-cache",
38498
38549
  tokenId,
38499
- ...meta3 && meta3.credentialHash === snapshot.fingerprint ? { token: toView({ tokenStatus: "ACTIVE", ...meta3 }) } : {}
38550
+ ...meta3 && meta3.credentialHash === snapshot2.fingerprint ? { token: toView({ tokenStatus: "ACTIVE", ...meta3 }) } : {}
38500
38551
  };
38501
38552
  }
38502
38553
  let response, networkError2;
@@ -38508,7 +38559,7 @@ async function verifySavedToken(ctx, offline = false) {
38508
38559
  const result = await withBindingLock(
38509
38560
  scope,
38510
38561
  async () => {
38511
- if (store.read()?.fingerprint !== snapshot.fingerprint) return;
38562
+ if (store.read()?.fingerprint !== snapshot2.fingerprint) return;
38512
38563
  if (networkError2)
38513
38564
  return failure2(
38514
38565
  networkError2 instanceof CliError ? networkError2.code : "TOKEN_STATUS_UNAVAILABLE",
@@ -38529,7 +38580,7 @@ async function verifySavedToken(ctx, offline = false) {
38529
38580
  );
38530
38581
  const canPay = status.tokenStatus === "ACTIVE";
38531
38582
  if (!canPay) {
38532
- store.deleteIfUnchanged(snapshot.fingerprint);
38583
+ store.deleteIfUnchanged(snapshot2.fingerprint);
38533
38584
  await ctx.updateConfig((c) => {
38534
38585
  const meta4 = { ...c.tokenMetaV3 };
38535
38586
  delete meta4[store.scopeHash];
@@ -38541,7 +38592,7 @@ async function verifySavedToken(ctx, offline = false) {
38541
38592
  tokenMetaV3: {
38542
38593
  ...c.tokenMetaV3,
38543
38594
  [store.scopeHash]: {
38544
- credentialHash: snapshot.fingerprint,
38595
+ credentialHash: snapshot2.fingerprint,
38545
38596
  cardMask: status.cardMask ?? "",
38546
38597
  purchaseLimit: status.purchaseLimit,
38547
38598
  dailyLimit: status.dailyLimit,
@@ -38655,8 +38706,8 @@ async function authLogoutCommand(ctx, opts) {
38655
38706
  cancelBindingLocked(ctx);
38656
38707
  ctx.secrets.delete("agentToken");
38657
38708
  const store = ctx.device.exists() ? await credentialStoreFor(ctx) : void 0;
38658
- const snapshot = store?.read();
38659
- if (snapshot) store.deleteIfUnchanged(snapshot.fingerprint);
38709
+ const snapshot2 = store?.read();
38710
+ if (snapshot2) store.deleteIfUnchanged(snapshot2.fingerprint);
38660
38711
  await ctx.updateConfig((c) => {
38661
38712
  const { token: _token, pendingSession: _pending, ...rest } = c;
38662
38713
  const tokenMetaV3 = { ...c.tokenMetaV3 };
@@ -38685,14 +38736,14 @@ var ALREADY = {
38685
38736
  };
38686
38737
  async function cardRevokeCommand(ctx) {
38687
38738
  const store = ctx.device.exists() ? await credentialStoreFor(ctx) : void 0;
38688
- const snapshot = await withBindingLock(bindingScope(ctx), async () => {
38739
+ const snapshot2 = await withBindingLock(bindingScope(ctx), async () => {
38689
38740
  cancelBindingLocked(ctx);
38690
38741
  return store?.read();
38691
38742
  });
38692
38743
  const res = await ctx.elcomV3.revoke();
38693
38744
  if (res.tokenStatus === "ACTIVE")
38694
38745
  throw new Error("\u041E\u0442\u0437\u044B\u0432 \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0451\u043D \u0431\u0430\u043D\u043A\u043E\u043C");
38695
- await forgetLocalToken(ctx, snapshot?.fingerprint);
38746
+ await forgetLocalToken(ctx, snapshot2?.fingerprint);
38696
38747
  const concurrent = store?.read();
38697
38748
  const verification = concurrent ? await verifySavedToken(ctx) : void 0;
38698
38749
  const human = verification ? "\u041E\u0442\u0437\u044B\u0432 \u043F\u043E \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0443 \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D. \u041B\u043E\u043A\u0430\u043B\u044C\u043D\u0430\u044F \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0430 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0430\u0441\u044C \u0432\u043E \u0432\u0440\u0435\u043C\u044F \u0437\u0430\u043F\u0440\u043E\u0441\u0430; \u0435\u0451 \u0442\u0435\u043A\u0443\u0449\u0430\u044F \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u0430 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E." : res.tokenStatus === "REVOKED" ? "\u2713 \u0414\u043E\u0441\u0442\u0443\u043F \u0430\u0433\u0435\u043D\u0442\u0430 \u043A \u043A\u0430\u0440\u0442\u0435 \u043E\u0442\u043E\u0437\u0432\u0430\u043D, \u043E\u043F\u043B\u0430\u0442\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u0440\u043E\u0439\u0434\u0443\u0442." : `\u2713 \u041E\u0442\u0437\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E: ${ALREADY[res.tokenStatus] ?? res.tokenStatus}. \u041E\u043F\u043B\u0430\u0442\u044B \u0441 \u044D\u0442\u043E\u0433\u043E \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0430 \u043D\u0435 \u043F\u0440\u043E\u0439\u0434\u0443\u0442.`;
@@ -38706,6 +38757,171 @@ async function cardRevokeCommand(ctx) {
38706
38757
  );
38707
38758
  }
38708
38759
 
38760
+ // packages/cli/src/schemes/v2/merchantsDomains.ts
38761
+ import { readFileSync as readFileSync12, rmSync as rmSync7 } from "node:fs";
38762
+ import { join as join24 } from "node:path";
38763
+ import { domainToUnicode } from "node:url";
38764
+ var DOMAINS_TTL_MS = 24 * 60 * 60 * 1e3;
38765
+ var REFRESH_BACKOFF_MS = 15 * 60 * 1e3;
38766
+ var LOCK_TIMEOUT_MS = 2e4;
38767
+ var CLOCK_SKEW_MS = 5 * 60 * 1e3;
38768
+ var CacheRecord = external_exports.object({
38769
+ format: external_exports.literal(1),
38770
+ stand: external_exports.string(),
38771
+ merchantsUrl: external_exports.string(),
38772
+ fetchedAt: external_exports.string(),
38773
+ count: external_exports.number().int().nonnegative(),
38774
+ domains: external_exports.array(MerchantDomain)
38775
+ });
38776
+ var AttemptRecord = external_exports.object({
38777
+ format: external_exports.literal(1),
38778
+ merchantsUrl: external_exports.string(),
38779
+ failedAt: external_exports.string(),
38780
+ code: external_exports.string()
38781
+ });
38782
+ function cacheFiles(ctx) {
38783
+ const base2 = join24(ctx.paths.cacheDir, `merchants-domains.${ctx.standName}`);
38784
+ return { data: `${base2}.json`, attempt: `${base2}.attempt.json`, lock: `${base2}.lock` };
38785
+ }
38786
+ function merchantsBase(ctx) {
38787
+ const url2 = ctx.stand.merchantsUrl?.replace(/\/+$/, "");
38788
+ if (!url2)
38789
+ throw new CliError(EXIT.CONFIG_OR_NETWORK, "STAND_NOT_CONFIGURED", "\u041D\u0430 \u044D\u0442\u043E\u043C \u0441\u0442\u0435\u043D\u0434\u0435 \u043D\u0435\u0442 \u0440\u0435\u0435\u0441\u0442\u0440\u0430 \u043C\u0430\u0433\u0430\u0437\u0438\u043D\u043E\u0432 \u0424\u041F (merchantsUrl \u043D\u0435 \u0437\u0430\u0434\u0430\u043D)", {
38790
+ hint: "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 stands.<\u0441\u0442\u0435\u043D\u0434>.merchantsUrl \u0432 config.json"
38791
+ });
38792
+ return url2;
38793
+ }
38794
+ function readJson(file2, schema) {
38795
+ try {
38796
+ const parsed = schema.safeParse(JSON.parse(readFileSync12(file2, "utf8")));
38797
+ return parsed.success ? parsed.data : void 0;
38798
+ } catch {
38799
+ return void 0;
38800
+ }
38801
+ }
38802
+ function readCache(ctx, url2) {
38803
+ const record2 = readJson(cacheFiles(ctx).data, CacheRecord);
38804
+ if (!record2 || record2.stand !== ctx.standName || record2.merchantsUrl !== url2) return void 0;
38805
+ if (record2.count !== record2.domains.length || Number.isNaN(Date.parse(record2.fetchedAt))) return void 0;
38806
+ return record2;
38807
+ }
38808
+ function isFresh(record2, now) {
38809
+ const fetched = Date.parse(record2.fetchedAt);
38810
+ return fetched <= now + CLOCK_SKEW_MS && now - fetched < DOMAINS_TTL_MS;
38811
+ }
38812
+ function snapshot(ctx, record2, source, now, refreshError) {
38813
+ return {
38814
+ stand: ctx.standName,
38815
+ source,
38816
+ fetchedAt: record2.fetchedAt,
38817
+ ageHours: Math.max(0, Math.round((now - Date.parse(record2.fetchedAt)) / 36e5 * 10) / 10),
38818
+ stale: refreshError !== void 0 || !isFresh(record2, now),
38819
+ ...refreshError ? { refreshError } : {},
38820
+ count: record2.count,
38821
+ file: cacheFiles(ctx).data,
38822
+ domains: record2.domains
38823
+ };
38824
+ }
38825
+ function backoff(ctx, url2, now) {
38826
+ const attempt = readJson(cacheFiles(ctx).attempt, AttemptRecord);
38827
+ if (!attempt || attempt.merchantsUrl !== url2) return void 0;
38828
+ const until = Date.parse(attempt.failedAt) + REFRESH_BACKOFF_MS;
38829
+ return Number.isNaN(until) || until <= now ? void 0 : { until, code: attempt.code };
38830
+ }
38831
+ async function loadDomains(ctx, opts = {}) {
38832
+ const now = () => ctx.now?.() ?? Date.now();
38833
+ const url2 = merchantsBase(ctx);
38834
+ const files = cacheFiles(ctx);
38835
+ const cached2 = readCache(ctx, url2);
38836
+ if (opts.offline) {
38837
+ if (!cached2)
38838
+ throw new CliError(EXIT.CONFIG_OR_NETWORK, "MERCHANTS_DOMAINS_NOT_CACHED", "\u0421\u043F\u0438\u0441\u043E\u043A \u0434\u043E\u043C\u0435\u043D\u043E\u0432 \u0435\u0449\u0451 \u043D\u0435 \u0441\u043A\u0430\u0447\u0430\u043D \u043D\u0430 \u044D\u0442\u043E\u043C \u043A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440\u0435", {
38839
+ hint: "\u0417\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435 sberpay-cli merchants domains \u0431\u0435\u0437 --offline"
38840
+ });
38841
+ return snapshot(ctx, cached2, "cache", now());
38842
+ }
38843
+ if (cached2 && !opts.refresh && isFresh(cached2, now())) return snapshot(ctx, cached2, "cache", now());
38844
+ const blocked = backoff(ctx, url2, now());
38845
+ if (blocked) {
38846
+ const refreshError = {
38847
+ code: blocked.code,
38848
+ message: `\u041F\u0440\u043E\u0448\u043B\u043E\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u043F\u0438\u0441\u043A\u0430 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C; \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0430\u044F \u043F\u043E\u043F\u044B\u0442\u043A\u0430 \u043F\u043E\u0441\u043B\u0435 ${new Date(blocked.until).toISOString()}`
38849
+ };
38850
+ if (cached2) return snapshot(ctx, cached2, "cache", now(), refreshError);
38851
+ throw new CliError(EXIT.CONFIG_OR_NETWORK, "MERCHANTS_DOMAINS_UNAVAILABLE", `\u0421\u043F\u0438\u0441\u043E\u043A \u0434\u043E\u043C\u0435\u043D\u043E\u0432 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${refreshError.message}`, {
38852
+ hint: "\u0414\u043B\u044F \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u043E\u0433\u043E \u0441\u0430\u0439\u0442\u0430 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 sberpay-cli merchants check <\u0434\u043E\u043C\u0435\u043D>"
38853
+ });
38854
+ }
38855
+ try {
38856
+ return await withFileLock(
38857
+ files.lock,
38858
+ async () => {
38859
+ const latest = readCache(ctx, url2);
38860
+ if (latest && latest.fetchedAt !== cached2?.fetchedAt && isFresh(latest, now())) return snapshot(ctx, latest, "cache", now());
38861
+ let response;
38862
+ try {
38863
+ response = await ctx.merchants.domains();
38864
+ } catch (error62) {
38865
+ if (error62 instanceof CliError)
38866
+ atomicJson(files.attempt, { format: 1, merchantsUrl: url2, failedAt: new Date(now()).toISOString(), code: error62.code });
38867
+ throw error62;
38868
+ }
38869
+ const record2 = {
38870
+ format: 1,
38871
+ stand: ctx.standName,
38872
+ merchantsUrl: url2,
38873
+ fetchedAt: new Date(now()).toISOString(),
38874
+ count: response.count,
38875
+ domains: response.domains
38876
+ };
38877
+ atomicJson(files.data, record2, { compact: true });
38878
+ rmSync7(files.attempt, { force: true });
38879
+ return snapshot(ctx, record2, "network", now());
38880
+ },
38881
+ LOCK_TIMEOUT_MS
38882
+ );
38883
+ } catch (error62) {
38884
+ if (!(error62 instanceof CliError)) throw error62;
38885
+ const latest = error62.code === "BINDING_BUSY" ? readCache(ctx, url2) ?? cached2 : cached2;
38886
+ ctx.warn(`merchants domains: \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u043F\u0438\u0441\u043A\u0430 \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C (${error62.code}): ${error62.message}`);
38887
+ if (latest) return snapshot(ctx, latest, "cache", now(), { code: error62.code, message: error62.message });
38888
+ if (error62.code === "BINDING_BUSY")
38889
+ throw new CliError(EXIT.CONFIG_OR_NETWORK, "MERCHANTS_DOMAINS_BUSY", "\u0421\u043F\u0438\u0441\u043E\u043A \u0434\u043E\u043C\u0435\u043D\u043E\u0432 \u0441\u0435\u0439\u0447\u0430\u0441 \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0435\u0442 \u0434\u0440\u0443\u0433\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430", {
38890
+ hint: "\u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 \u0447\u0435\u0440\u0435\u0437 \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0441\u0435\u043A\u0443\u043D\u0434"
38891
+ });
38892
+ throw error62;
38893
+ }
38894
+ }
38895
+ var indexDomains = (domains) => new Map(domains.map((entry) => [entry.domain, entry.includeSubdomains]));
38896
+ function matchDomain(index, input2) {
38897
+ let domain2;
38898
+ try {
38899
+ domain2 = normalizeDomain(input2);
38900
+ } catch (error62) {
38901
+ return { input: input2, supported: false, error: error62 instanceof CliError ? error62.code : "INVALID_ARGUMENT" };
38902
+ }
38903
+ if (index.has(domain2)) return { input: input2, domain: domain2, supported: true, registryDomain: domain2, matchedBy: "exact" };
38904
+ for (let candidate = domain2; candidate.includes("."); ) {
38905
+ candidate = candidate.slice(candidate.indexOf(".") + 1);
38906
+ if (index.get(candidate) === true) return { input: input2, domain: domain2, supported: true, registryDomain: candidate, matchedBy: "parent" };
38907
+ }
38908
+ return { input: input2, domain: domain2, supported: false };
38909
+ }
38910
+ function searchDomains(domains, query, limit) {
38911
+ const needle = query.trim().toLowerCase();
38912
+ if (!needle) throw new CliError(EXIT.VALIDATION, "INVALID_ARGUMENT", "--search: \u043F\u0443\u0441\u0442\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430 \u043F\u043E\u0438\u0441\u043A\u0430");
38913
+ const hits = [];
38914
+ let total = 0;
38915
+ for (const entry of domains) {
38916
+ const unicode = entry.domain.includes("xn--") ? domainToUnicode(entry.domain) : entry.domain;
38917
+ if (!entry.domain.includes(needle) && !unicode.toLowerCase().includes(needle)) continue;
38918
+ total++;
38919
+ if (hits.length < limit)
38920
+ hits.push({ domain: entry.domain, ...unicode && unicode !== entry.domain ? { unicode } : {}, includeSubdomains: entry.includeSubdomains });
38921
+ }
38922
+ return { total, truncated: total > hits.length, domains: hits };
38923
+ }
38924
+
38709
38925
  // packages/cli/src/schemes/v2/commands/merchants.ts
38710
38926
  async function merchantsCheckCommand(ctx, domain2) {
38711
38927
  const res = await ctx.merchants.check(domain2);
@@ -38717,6 +38933,47 @@ ${entry.domain}:
38717
38933
  ${entry.payload}`).join("") ?? "")
38718
38934
  );
38719
38935
  }
38936
+ var DEFAULT_SEARCH_LIMIT = 50;
38937
+ var MAX_SEARCH_LIMIT = 1e3;
38938
+ var MAX_MATCH_INPUTS = 500;
38939
+ function searchLimit(raw) {
38940
+ if (raw === void 0) return DEFAULT_SEARCH_LIMIT;
38941
+ const limit = Number(raw);
38942
+ if (!Number.isInteger(limit) || limit < 1 || limit > MAX_SEARCH_LIMIT)
38943
+ throw new CliError(EXIT.VALIDATION, "INVALID_ARGUMENT", `--limit: \u0446\u0435\u043B\u043E\u0435 \u043E\u0442 1 \u0434\u043E ${MAX_SEARCH_LIMIT}`);
38944
+ return limit;
38945
+ }
38946
+ async function merchantsDomainsCommand(ctx, opts) {
38947
+ const match = opts.match ?? [];
38948
+ if (match.length > MAX_MATCH_INPUTS)
38949
+ throw new CliError(EXIT.VALIDATION, "INVALID_ARGUMENT", `--match: \u043D\u0435 \u0431\u043E\u043B\u044C\u0448\u0435 ${MAX_MATCH_INPUTS} \u0434\u043E\u043C\u0435\u043D\u043E\u0432 \u0437\u0430 \u0432\u044B\u0437\u043E\u0432`);
38950
+ if (opts.refresh && opts.offline)
38951
+ throw new CliError(EXIT.VALIDATION, "INVALID_ARGUMENT", "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043E\u0434\u043D\u043E: --refresh \u0438\u043B\u0438 --offline");
38952
+ const limit = searchLimit(opts.limit);
38953
+ const { domains, ...summary } = await loadDomains(ctx, { refresh: opts.refresh, offline: opts.offline });
38954
+ const result = { ...summary };
38955
+ const lines = [
38956
+ `\u041C\u0430\u0433\u0430\u0437\u0438\u043D\u043E\u0432 \u0441 \u043E\u043F\u043B\u0430\u0442\u043E\u0439 \u0430\u0433\u0435\u043D\u0442\u043E\u043C: ${summary.count} (\u0441\u043F\u0438\u0441\u043E\u043A \u043E\u0442 ${summary.fetchedAt}${summary.stale ? ", \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043D\u0435\u043F\u043E\u043B\u043D\u044B\u043C" : ""})`
38957
+ ];
38958
+ if (match.length) {
38959
+ const index = indexDomains(domains);
38960
+ const matches = match.map((input2) => matchDomain(index, input2));
38961
+ result.matches = matches;
38962
+ lines.push(
38963
+ ...matches.map(
38964
+ (m) => m.error ? `? ${m.input}: \u043D\u0435 \u0434\u043E\u043C\u0435\u043D \u0438 \u043D\u0435 URL` : m.supported ? `\u2713 ${m.domain}${m.matchedBy === "parent" ? ` (\u0447\u0435\u0440\u0435\u0437 ${m.registryDomain})` : ""}` : `\u2717 ${m.domain}`
38965
+ )
38966
+ );
38967
+ }
38968
+ if (opts.search !== void 0) {
38969
+ const found = searchDomains(domains, opts.search, limit);
38970
+ result.search = { query: opts.search, ...found };
38971
+ lines.push(`\u041D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \xAB${opts.search}\xBB: ${found.total}${found.truncated ? ` (\u043F\u043E\u043A\u0430\u0437\u0430\u043D\u044B \u043F\u0435\u0440\u0432\u044B\u0435 ${found.domains.length})` : ""}`);
38972
+ lines.push(...found.domains.map((d) => ` ${d.unicode ?? d.domain}`));
38973
+ }
38974
+ if (!match.length && opts.search === void 0) lines.push(`\u041F\u043E\u043B\u043D\u044B\u0439 \u0441\u043F\u0438\u0441\u043E\u043A: ${summary.file}`);
38975
+ ctx.out.result(result, lines.join("\n"));
38976
+ }
38720
38977
 
38721
38978
  // packages/cli/src/schemes/v2/commands/device.ts
38722
38979
  async function deviceInfo(ctx) {
@@ -38833,20 +39090,20 @@ function createAddressesClient(core, device2, credentials, options = {}) {
38833
39090
  );
38834
39091
  if (!device2.exists())
38835
39092
  throw new CliError(EXIT.AUTH, "NO_CARD", "\u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u043F\u0440\u0438\u0432\u044F\u0436\u0438\u0442\u0435 \u043A\u0430\u0440\u0442\u0443");
38836
- const snapshot = await withBindingLock(
39093
+ const snapshot2 = await withBindingLock(
38837
39094
  bindingScope(core),
38838
39095
  async () => credentials.read()
38839
39096
  );
38840
- if (!snapshot)
39097
+ if (!snapshot2)
38841
39098
  throw new CliError(EXIT.AUTH, "NO_CARD", "\u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u043F\u0440\u0438\u0432\u044F\u0436\u0438\u0442\u0435 \u043A\u0430\u0440\u0442\u0443");
38842
- if (!AddressSubId.safeParse(snapshot.value.subId).success)
39099
+ if (!AddressSubId.safeParse(snapshot2.value.subId).success)
38843
39100
  throw new CliError(
38844
39101
  EXIT.AUTH,
38845
39102
  "TOKEN_MALFORMED",
38846
39103
  "subId \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D\u043D\u043E\u0439 \u043F\u0440\u0438\u0432\u044F\u0437\u043A\u0438 \u043D\u0435 \u0441\u043E\u043E\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043A\u043E\u043D\u0442\u0440\u0430\u043A\u0442\u0443 \u0424\u041F"
38847
39104
  );
38848
39105
  if (core.stand.addressesMode === "pilot-json")
38849
- return sendPilotAddress(core, path2, schema, snapshot.value.subId, deliveryAddress);
39106
+ return sendPilotAddress(core, path2, schema, snapshot2.value.subId, deliveryAddress);
38850
39107
  const pub = await device2.ensure(), kid = await calculateJwkThumbprint(pub.encKey);
38851
39108
  for (let attempt = 0; attempt < 2; attempt++) {
38852
39109
  const key = await getEncryptionKey(core, attempt > 0, options), requestId = randomUUID15(), iat = Math.floor(Date.now() / 1e3);
@@ -38859,7 +39116,7 @@ function createAddressesClient(core, device2, credentials, options = {}) {
38859
39116
  exp: iat + 60,
38860
39117
  jti: randomBytes2(16).toString("base64url"),
38861
39118
  body: {
38862
- subId: snapshot.value.subId,
39119
+ subId: snapshot2.value.subId,
38863
39120
  ...deliveryAddress ? { deliveryAddress } : {}
38864
39121
  }
38865
39122
  });
@@ -38997,6 +39254,10 @@ function createScheme(core) {
38997
39254
  addressGet: run3(addressGetCommand),
38998
39255
  addressSet: run3(addressSetCommand),
38999
39256
  merchantsCheck: async (domain2) => merchantsCheckCommand(merchantsCtx, domain2),
39257
+ merchantsDomains: async (opts) => merchantsDomainsCommand(
39258
+ { ...merchantsCtx, paths: core.paths, standName: core.standName, stand: core.stand, warn: (msg) => core.warn(msg) },
39259
+ opts
39260
+ ),
39000
39261
  deviceShow: run3(deviceShowCommand),
39001
39262
  deviceRegister: run3(deviceRegisterCommand)
39002
39263
  };
@@ -39013,9 +39274,9 @@ import { resolve as resolve5 } from "node:path";
39013
39274
 
39014
39275
  // packages/cli/src/ca/rootCa.ts
39015
39276
  import { randomBytes as randomBytes3, X509Certificate as X509Certificate2 } from "node:crypto";
39016
- import { closeSync as closeSync8, existsSync as existsSync10, fchmodSync, lstatSync as lstatSync6, mkdirSync as mkdirSync12, mkdtempSync, openSync as openSync8, readFileSync as readFileSync12, renameSync as renameSync5, rmSync as rmSync7, writeFileSync as writeFileSync5, writeSync as writeSync4 } from "node:fs";
39277
+ import { closeSync as closeSync8, existsSync as existsSync10, fchmodSync, lstatSync as lstatSync6, mkdirSync as mkdirSync12, mkdtempSync, openSync as openSync8, readFileSync as readFileSync13, renameSync as renameSync5, rmSync as rmSync8, writeFileSync as writeFileSync5, writeSync as writeSync4 } from "node:fs";
39017
39278
  import { tmpdir } from "node:os";
39018
- import { dirname as dirname10, join as join24, resolve as resolve3 } from "node:path";
39279
+ import { dirname as dirname10, join as join25, resolve as resolve3 } from "node:path";
39019
39280
  var FETCH_TIMEOUT_MS = 15e3;
39020
39281
  var LOGIN_KEYCHAIN = "Library/Keychains/login.keychain-db";
39021
39282
  var SECURITY_BIN = "/usr/bin/security";
@@ -39047,7 +39308,7 @@ function verifyRootCert(data, root, now = /* @__PURE__ */ new Date()) {
39047
39308
  }
39048
39309
  function readFetchedRoot(paths, root = RUSSIAN_TRUSTED_ROOT_CA) {
39049
39310
  const file2 = rootCaFile(paths, root);
39050
- return existsSync10(file2) ? verifyRootCert(readFileSync12(file2), root) : void 0;
39311
+ return existsSync10(file2) ? verifyRootCert(readFileSync13(file2), root) : void 0;
39051
39312
  }
39052
39313
  function ensureCaDir(dir2) {
39053
39314
  mkdirSync12(dir2, { recursive: true, mode: 448 });
@@ -39060,7 +39321,7 @@ function ensureCaDir(dir2) {
39060
39321
  }
39061
39322
  }
39062
39323
  function writeAtomically(path2, data, mode) {
39063
- const tmp = join24(dirname10(path2), `.${randomBytes3(6).toString("hex")}.tmp`);
39324
+ const tmp = join25(dirname10(path2), `.${randomBytes3(6).toString("hex")}.tmp`);
39064
39325
  const fd2 = openSync8(tmp, "wx", mode);
39065
39326
  try {
39066
39327
  fchmodSync(fd2, mode);
@@ -39071,7 +39332,7 @@ function writeAtomically(path2, data, mode) {
39071
39332
  try {
39072
39333
  renameSync5(tmp, path2);
39073
39334
  } catch (error62) {
39074
- rmSync7(tmp, { force: true });
39335
+ rmSync8(tmp, { force: true });
39075
39336
  throw error62;
39076
39337
  }
39077
39338
  }
@@ -39104,7 +39365,7 @@ function installPlan(opts) {
39104
39365
  if (platform2 === "darwin") {
39105
39366
  return {
39106
39367
  kind: "run",
39107
- argv: [SECURITY_BIN, "add-trusted-cert", "-r", "trustRoot", "-p", "ssl", "-k", join24(opts.home, LOGIN_KEYCHAIN), file2],
39368
+ argv: [SECURITY_BIN, "add-trusted-cert", "-r", "trustRoot", "-p", "ssl", "-k", join25(opts.home, LOGIN_KEYCHAIN), file2],
39108
39369
  say: "macOS \u0441\u043F\u0440\u043E\u0441\u0438\u0442 \u043F\u0430\u0440\u043E\u043B\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F \u2014 \u044D\u0442\u043E \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435. \u0414\u043E\u0432\u0435\u0440\u0438\u0435 \u2014 \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F TLS (-p ssl), \u0432 \u0441\u0432\u044F\u0437\u043A\u0435 \xAB\u0432\u0445\u043E\u0434\xBB"
39109
39370
  };
39110
39371
  }
@@ -39145,33 +39406,33 @@ function installRootCa(deps) {
39145
39406
  const planFor = (file2) => installPlan({ platform: deps.platform, file: file2, home: deps.home, exists: deps.exists, cert });
39146
39407
  const manual = planFor(rootCaFile(deps.paths, root));
39147
39408
  if (manual.kind === "manual") return { installed: false, needsHuman: true, ...manual.command ? { command: manual.command } : {}, say: manual.say };
39148
- const dir2 = mkdtempSync(join24(tmpdir(), "sberpay-ca-"));
39409
+ const dir2 = mkdtempSync(join25(tmpdir(), "sberpay-ca-"));
39149
39410
  try {
39150
- const copy = join24(dir2, root.fileName);
39411
+ const copy = join25(dir2, root.fileName);
39151
39412
  writeFileSync5(copy, cert.toString(), { flag: "wx", mode: 384 });
39152
39413
  const plan = planFor(copy);
39153
39414
  const result = plan.kind === "run" ? deps.run(plan.argv) : { status: 1, stderr: "\u043D\u0435\u0442 \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0438" };
39154
39415
  if (result.status !== 0) throw securityError(result.stderr);
39155
39416
  } finally {
39156
- rmSync7(dir2, { recursive: true, force: true });
39417
+ rmSync8(dir2, { recursive: true, force: true });
39157
39418
  }
39158
39419
  return { installed: true, already: false, say: `${root.name} \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D \u0432 \u0434\u043E\u0432\u0435\u0440\u0435\u043D\u043D\u044B\u0435 \u043A\u043E\u0440\u043D\u0438. ${CHECK_AFTER}` };
39159
39420
  }
39160
39421
 
39161
39422
  // packages/cli/src/setup/skill.ts
39162
39423
  import { createHash as createHash16 } from "node:crypto";
39163
- import { cpSync, existsSync as existsSync11, lstatSync as lstatSync7, mkdirSync as mkdirSync13, readdirSync as readdirSync3, readFileSync as readFileSync13, readlinkSync, realpathSync as realpathSync3, renameSync as renameSync6, rmSync as rmSync8, statSync as statSync2, symlinkSync, writeFileSync as writeFileSync6 } from "node:fs";
39424
+ import { cpSync, existsSync as existsSync11, lstatSync as lstatSync7, mkdirSync as mkdirSync13, readdirSync as readdirSync3, readFileSync as readFileSync14, readlinkSync, realpathSync as realpathSync3, renameSync as renameSync6, rmSync as rmSync9, statSync as statSync2, symlinkSync, writeFileSync as writeFileSync6 } from "node:fs";
39164
39425
  import { homedir as homedir6 } from "node:os";
39165
- import { basename, dirname as dirname11, join as join25, relative as relative3, resolve as resolve4 } from "node:path";
39426
+ import { basename, dirname as dirname11, join as join26, relative as relative3, resolve as resolve4 } from "node:path";
39166
39427
  var SKILL_NAME = "sberpay";
39167
39428
  var MANIFEST = ".sberpay-cli.json";
39168
39429
  var WORK_DIR = ".sberpay-cli";
39169
39430
  function skillSourceDir(build = BUILD) {
39170
- return join25(build.root, "skill", SKILL_NAME);
39431
+ return join26(build.root, "skill", SKILL_NAME);
39171
39432
  }
39172
39433
  function claudeSkillDir(opts = {}) {
39173
39434
  const { env = process.env, home = homedir6(), cwd = process.cwd() } = opts;
39174
- const base2 = opts.project ? join25(cwd, ".claude") : env.CLAUDE_CONFIG_DIR || join25(home, ".claude");
39435
+ const base2 = opts.project ? join26(cwd, ".claude") : env.CLAUDE_CONFIG_DIR || join26(home, ".claude");
39175
39436
  return resolve4(base2, "skills", SKILL_NAME);
39176
39437
  }
39177
39438
  function checkSkillDir(target) {
@@ -39180,13 +39441,13 @@ function checkSkillDir(target) {
39180
39441
  }
39181
39442
  function listFiles(dir2, root = dir2) {
39182
39443
  return readdirSync3(dir2, { withFileTypes: true }).flatMap((entry) => {
39183
- const path2 = join25(dir2, entry.name);
39444
+ const path2 = join26(dir2, entry.name);
39184
39445
  if (entry.isDirectory()) return listFiles(path2, root);
39185
39446
  return entry.name === MANIFEST ? [] : [relative3(root, path2)];
39186
39447
  });
39187
39448
  }
39188
39449
  function hashFiles(dir2) {
39189
- return Object.fromEntries(listFiles(dir2).sort().map((file2) => [file2, createHash16("sha256").update(readFileSync13(join25(dir2, file2))).digest("hex")]));
39450
+ return Object.fromEntries(listFiles(dir2).sort().map((file2) => [file2, createHash16("sha256").update(readFileSync14(join26(dir2, file2))).digest("hex")]));
39190
39451
  }
39191
39452
  var sameFiles = (a, b) => JSON.stringify(a) === JSON.stringify(b);
39192
39453
  function sameDir(a, b) {
@@ -39201,16 +39462,16 @@ function isLink(path2) {
39201
39462
  }
39202
39463
  }
39203
39464
  function readManifest(target) {
39204
- const file2 = join25(target, MANIFEST);
39465
+ const file2 = join26(target, MANIFEST);
39205
39466
  if (!existsSync11(file2)) return void 0;
39206
39467
  try {
39207
- const data = JSON.parse(readFileSync13(file2, "utf8"));
39468
+ const data = JSON.parse(readFileSync14(file2, "utf8"));
39208
39469
  return typeof data.version === "string" && data.files && typeof data.files === "object" ? data : void 0;
39209
39470
  } catch {
39210
39471
  return void 0;
39211
39472
  }
39212
39473
  }
39213
- var isStaleOwnLink = (target) => !existsSync11(target) && readlinkSync(target).replace(/[\\/]+$/, "").endsWith(join25("skill", SKILL_NAME));
39474
+ var isStaleOwnLink = (target) => !existsSync11(target) && readlinkSync(target).replace(/[\\/]+$/, "").endsWith(join26("skill", SKILL_NAME));
39214
39475
  function inspectSkill(source, target) {
39215
39476
  if (isLink(target)) {
39216
39477
  if (existsSync11(target)) return sameDir(target, source) ? { state: "current", mode: "link" } : { state: "foreign" };
@@ -39223,20 +39484,20 @@ function inspectSkill(source, target) {
39223
39484
  return { state: sameFiles(manifest.files, hashFiles(source)) ? "current" : "outdated", mode: "copy" };
39224
39485
  }
39225
39486
  function prepare(source, work, mode, version2) {
39226
- const fresh = join25(work, `new-${process.pid}`);
39227
- rmSync8(fresh, { recursive: true, force: true });
39487
+ const fresh = join26(work, `new-${process.pid}`);
39488
+ rmSync9(fresh, { recursive: true, force: true });
39228
39489
  if (mode === "link") {
39229
39490
  symlinkSync(realpathSync3(source), fresh, process.platform === "win32" ? "junction" : "dir");
39230
39491
  return fresh;
39231
39492
  }
39232
39493
  cpSync(source, fresh, { recursive: true, dereference: true });
39233
39494
  const manifest = { version: version2, files: hashFiles(fresh) };
39234
- writeFileSync6(join25(fresh, MANIFEST), `${JSON.stringify(manifest, null, 2)}
39495
+ writeFileSync6(join26(fresh, MANIFEST), `${JSON.stringify(manifest, null, 2)}
39235
39496
  `);
39236
39497
  return fresh;
39237
39498
  }
39238
39499
  function swap(fresh, target, aside, work) {
39239
- const previous = existsSync11(target) || isLink(target) ? aside ?? join25(work, `old-${process.pid}`) : void 0;
39500
+ const previous = existsSync11(target) || isLink(target) ? aside ?? join26(work, `old-${process.pid}`) : void 0;
39240
39501
  if (previous) renameSync6(target, previous);
39241
39502
  try {
39242
39503
  renameSync6(fresh, target);
@@ -39244,7 +39505,7 @@ function swap(fresh, target, aside, work) {
39244
39505
  if (previous) renameSync6(previous, target);
39245
39506
  throw error62;
39246
39507
  }
39247
- if (previous && !aside) rmSync8(previous, { recursive: true, force: true });
39508
+ if (previous && !aside) rmSync9(previous, { recursive: true, force: true });
39248
39509
  }
39249
39510
  function installSkill(source, target, opts) {
39250
39511
  const found = inspectSkill(source, target);
@@ -39256,14 +39517,14 @@ function installSkill(source, target, opts) {
39256
39517
  hint: "\u041F\u0435\u0440\u0435\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C: sberpay-cli setup --force-skill \u2014 \u043F\u0440\u0435\u0436\u043D\u0438\u0439 \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u0443\u0439\u0434\u0451\u0442 \u0432 \u0431\u044D\u043A\u0430\u043F"
39257
39518
  });
39258
39519
  }
39259
- const work = join25(dirname11(target), WORK_DIR);
39520
+ const work = join26(dirname11(target), WORK_DIR);
39260
39521
  mkdirSync13(work, { recursive: true });
39261
- const backup = ours ? void 0 : join25(work, `backup-${basename(target)}-${(opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`);
39522
+ const backup = ours ? void 0 : join26(work, `backup-${basename(target)}-${(opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`);
39262
39523
  const fresh = prepare(source, work, opts.mode, opts.version);
39263
39524
  try {
39264
39525
  swap(fresh, target, backup, work);
39265
39526
  } finally {
39266
- rmSync8(fresh, { recursive: true, force: true });
39527
+ rmSync9(fresh, { recursive: true, force: true });
39267
39528
  }
39268
39529
  return { state: "current", mode: opts.mode, changed: true, ...backup ? { backup } : {} };
39269
39530
  }
@@ -39791,10 +40052,237 @@ async function setupCommand(globals, opts) {
39791
40052
  }
39792
40053
  }
39793
40054
 
40055
+ // packages/cli/src/setup/environment.ts
40056
+ import { accessSync, constants as constants8, existsSync as existsSync13, readFileSync as readFileSync15 } from "node:fs";
40057
+ import { homedir as homedir8, release as release2 } from "node:os";
40058
+ var AGENT_TOKEN = "agentToken";
40059
+ var GATEWAY_CHECK_TIMEOUT_MS2 = 8e3;
40060
+ var OS_NAME = {
40061
+ darwin: "macOS",
40062
+ win32: "Windows",
40063
+ linux: "Linux",
40064
+ aix: void 0,
40065
+ android: void 0,
40066
+ freebsd: void 0,
40067
+ haiku: void 0,
40068
+ openbsd: void 0,
40069
+ sunos: void 0,
40070
+ cygwin: void 0,
40071
+ netbsd: void 0
40072
+ };
40073
+ function hasToken2(core) {
40074
+ try {
40075
+ if (osSecretStore(core.standName, core.stand).get(AGENT_TOKEN) !== void 0) return true;
40076
+ } catch {
40077
+ }
40078
+ try {
40079
+ return fileSecretStore(core.paths, core.standName).get(AGENT_TOKEN) !== void 0;
40080
+ } catch {
40081
+ return false;
40082
+ }
40083
+ }
40084
+ async function probeHelper(platform2) {
40085
+ const status = await keyHelperStatus(platform2);
40086
+ let missing = [];
40087
+ if (status.kind) {
40088
+ try {
40089
+ const backend = keyHelperBackend(platform2);
40090
+ if (backend) {
40091
+ const path2 = findKeyHelper({ installed: backend.path });
40092
+ if (path2) missing = missingLibraries(path2, platform2);
40093
+ }
40094
+ } catch {
40095
+ missing = [];
40096
+ }
40097
+ }
40098
+ return { ...status, missingLibraries: missing };
40099
+ }
40100
+ async function probeStorage(core) {
40101
+ const softwareAllowed = core.stand.allowSoftwareKeys;
40102
+ const helper = await probeHelper(process.platform);
40103
+ let osAvailable = false;
40104
+ try {
40105
+ osSecretStore(core.standName, core.stand).get(AGENT_TOKEN);
40106
+ osAvailable = true;
40107
+ } catch {
40108
+ }
40109
+ let fileWritable = false;
40110
+ try {
40111
+ accessSync(core.paths.configDir, constants8.W_OK);
40112
+ fileWritable = true;
40113
+ } catch {
40114
+ }
40115
+ const candidate = softwareAllowed ? ["hardware", "os", "file"] : ["hardware"];
40116
+ const avail = {
40117
+ hardware: helper.available,
40118
+ os: osAvailable,
40119
+ file: fileWritable
40120
+ };
40121
+ return {
40122
+ hardware: { kind: helper.kind ?? null, available: helper.available },
40123
+ os: { available: osAvailable },
40124
+ file: { writable: fileWritable },
40125
+ recommended: candidate.find((s) => avail[s]) ?? null,
40126
+ helper,
40127
+ softwareAllowed
40128
+ };
40129
+ }
40130
+ function nodeFits(node2) {
40131
+ const [major = 0, minor = 0] = node2.split(".").map(Number);
40132
+ return major > LAST_LISTED_MAJOR || MIN_NODE_MINOR[major] !== void 0 && minor >= MIN_NODE_MINOR[major];
40133
+ }
40134
+ function osMeets(platform2, releaseVersion) {
40135
+ const major = Number(releaseVersion.split(".")[0]);
40136
+ if (!Number.isFinite(major)) return null;
40137
+ if (platform2 === "darwin") return major >= 22;
40138
+ if (platform2 === "win32") return major >= 10;
40139
+ return true;
40140
+ }
40141
+ function targetOf(platform2, arch2) {
40142
+ if (platform2 === "darwin") return arch2 === "arm64" ? "macOS-ARM64" : "macOS-Intel";
40143
+ if (platform2 === "win32") return "Windows";
40144
+ if (platform2 === "linux") return "Linux";
40145
+ return "other";
40146
+ }
40147
+ function probeRootCa(core) {
40148
+ const url2 = new URL(core.stand.gatewayUrl);
40149
+ if (url2.protocol !== "https:") return "not-required";
40150
+ const root = PINNED_ROOTS.find((r) => coversHost(r, url2.hostname));
40151
+ if (!root) return "not-required";
40152
+ const found = findRoot(root);
40153
+ if (!found) return "missing";
40154
+ return found.expired ? "expired" : "present";
40155
+ }
40156
+ function probePersistence() {
40157
+ const home = homedir8();
40158
+ if (/(^|\/)(tmp|var\/tmp|run)(\/|$)/.test(home)) return "likely-ephemeral";
40159
+ try {
40160
+ if (existsSync13("/.dockerenv") || existsSync13("/run/.containerenv")) return "likely-ephemeral";
40161
+ } catch {
40162
+ }
40163
+ return "assumed-persistent";
40164
+ }
40165
+ async function probeGateway(core, offline) {
40166
+ if (offline) return { gateway: "unknown" };
40167
+ try {
40168
+ await getEncryptionKey(core, true, { timeoutMs: GATEWAY_CHECK_TIMEOUT_MS2 });
40169
+ return { gateway: "ok" };
40170
+ } catch (error62) {
40171
+ return { gateway: "fail", gatewayError: error62 instanceof CliError ? error62.code : "UNKNOWN" };
40172
+ }
40173
+ }
40174
+ async function collectEnvironment(core, offline = false) {
40175
+ const platform2 = process.platform;
40176
+ const arch2 = process.arch;
40177
+ const node2 = process.versions.node;
40178
+ const wsl = platform2 === "linux" ? (() => {
40179
+ try {
40180
+ return /microsoft/i.test(readFileSync15("/proc/version", "utf8"));
40181
+ } catch {
40182
+ return null;
40183
+ }
40184
+ })() : null;
40185
+ const environment = {
40186
+ platform: platform2,
40187
+ arch: arch2,
40188
+ osName: OS_NAME[platform2],
40189
+ osVersion: release2(),
40190
+ osMeets: osMeets(platform2, release2()),
40191
+ node: node2,
40192
+ nodeFits: nodeFits(node2),
40193
+ wsl,
40194
+ configDir: core.paths.configDir,
40195
+ configWritable: (() => {
40196
+ try {
40197
+ accessSync(core.paths.configDir, constants8.W_OK);
40198
+ return true;
40199
+ } catch {
40200
+ return false;
40201
+ }
40202
+ })(),
40203
+ persistence: probePersistence()
40204
+ };
40205
+ const storage = await probeStorage(core);
40206
+ const deviceKeys = deviceKeysExist(core);
40207
+ const existing = {
40208
+ deviceKeys,
40209
+ binding: deviceKeys && hasToken2(core),
40210
+ ...core.config.deviceKeys?.[core.standName] ? { keyStorage: core.config.deviceKeys[core.standName] } : {}
40211
+ };
40212
+ const rootCa = probeRootCa(core);
40213
+ const network = await probeGateway(core, offline);
40214
+ const target = targetOf(platform2, arch2);
40215
+ const implementation = platform2 === "darwin" || platform2 === "win32" || platform2 === "linux" ? "done" : "none";
40216
+ const e2e = platform2 === "darwin" && arch2 === "arm64" ? "confirmed" : "pending";
40217
+ const reasons = [];
40218
+ let next;
40219
+ let fit = "ok";
40220
+ if (!environment.nodeFits) {
40221
+ fit = "fail";
40222
+ reasons.push(`Node ${node2} \u043D\u0435 \u043F\u043E\u0434\u0445\u043E\u0434\u0438\u0442 (\u043D\u0443\u0436\u043D\u043E 22.19+ \u0438\u043B\u0438 24.5+)`);
40223
+ next = "\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u0435 Node \u0434\u043E 22.19+ \u0438\u043B\u0438 24.5+ \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 sberpay-cli setup doctor";
40224
+ }
40225
+ if (platform2 === "darwin" && environment.osMeets === false) {
40226
+ fit = "fail";
40227
+ reasons.push("macOS \u043D\u0438\u0436\u0435 13.0");
40228
+ next = "\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u0435 macOS \u0434\u043E 13.0+ \u0438\u043B\u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 Mac \u043D\u0430 \u0431\u043E\u043B\u0435\u0435 \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438";
40229
+ }
40230
+ if (implementation === "none") {
40231
+ fit = "fail";
40232
+ reasons.push("\u043F\u043B\u0430\u0442\u0444\u043E\u0440\u043C\u0430 \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F");
40233
+ } else if (!storage.softwareAllowed && !storage.hardware.available) {
40234
+ if (!storage.helper.found) {
40235
+ fit = "unknown";
40236
+ reasons.push("\u0445\u0435\u043B\u043F\u0435\u0440 \u043A\u043B\u044E\u0447\u0435\u0439 \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D, \u0447\u0438\u043F \u043D\u0435 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0451\u043D");
40237
+ next = "\u0421\u043E\u0431\u0435\u0440\u0438\u0442\u0435/\u043F\u0435\u0440\u0435\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 \u0445\u0435\u043B\u043F\u0435\u0440 \u0438 \u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435; \u0438\u043D\u0430\u0447\u0435 \u043D\u0443\u0436\u043D\u0430 \u043C\u0430\u0448\u0438\u043D\u0430 \u0441 Secure Enclave/TPM";
40238
+ } else {
40239
+ fit = "fail";
40240
+ reasons.push("\u0447\u0438\u043F \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D, \u0430 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u043D\u044B\u0435 \u043A\u043B\u044E\u0447\u0438 \u043D\u0430 \u0441\u0442\u0435\u043D\u0434\u0435 \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D\u044B");
40241
+ next = "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 \u043C\u0430\u0448\u0438\u043D\u0443 \u0441 Secure Enclave/TPM \u0438\u043B\u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u0435 \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u043D\u044B\u0435 \u043A\u043B\u044E\u0447\u0438 (allowSoftwareKeys: true)";
40242
+ }
40243
+ }
40244
+ const suitable = fit === "fail" ? false : fit === "unknown" ? "unknown" : true;
40245
+ return {
40246
+ schemaVersion: 1,
40247
+ environment,
40248
+ storage,
40249
+ existing,
40250
+ rootCa,
40251
+ network,
40252
+ verdict: {
40253
+ target,
40254
+ implementation,
40255
+ e2e,
40256
+ fit,
40257
+ suitable,
40258
+ ...reasons.length ? { reason: reasons.join("; ") } : {},
40259
+ ...next ? { next } : {}
40260
+ }
40261
+ };
40262
+ }
40263
+ async function doctorCommand(globals, opts) {
40264
+ const core = buildCoreContext(globals);
40265
+ const result = await collectEnvironment(core, Boolean(opts.offline));
40266
+ const v = result.verdict;
40267
+ const label = result.environment.wsl ? `${v.target} (WSL)` : v.target;
40268
+ const human = v.suitable === true ? [
40269
+ `\u0421\u0440\u0435\u0434\u0430: ${label} \xB7 Node ${result.environment.node} \xB7 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435: ${result.storage.recommended ?? "\u2014"}`,
40270
+ `\u041A\u043E\u0440\u0435\u043D\u044C \u041C\u0438\u043D\u0446\u0438\u0444\u0440\u044B: ${result.rootCa === "not-required" ? "\u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F" : result.rootCa} \xB7 \u0428\u043B\u044E\u0437 \u042D\u041A: ${result.network.gateway}`,
40271
+ ...result.existing.binding ? ["\u041F\u0440\u0438\u0432\u044F\u0437\u043A\u0430 \u0443\u0436\u0435 \u0435\u0441\u0442\u044C \u2014 \u043D\u043E\u0432\u0430\u044F \u043D\u0435 \u043D\u0430\u0447\u043D\u0451\u0442\u0441\u044F \u043F\u043E\u0432\u0435\u0440\u0445 \u043D\u0435\u0451"] : [],
40272
+ "\u041F\u0440\u0438\u0433\u043E\u0434\u043D\u043E\u0441\u0442\u044C \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0430 \u2014 \u043C\u043E\u0436\u043D\u043E \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0443 (sberpay-cli setup --skill-dir \u2026)"
40273
+ ].join("\n") : [
40274
+ `\u0421\u0440\u0435\u0434\u0430 \u043D\u0435 \u0433\u043E\u0442\u043E\u0432\u0430: ${v.reason ?? "\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E"}`,
40275
+ ...v.next ? [`\u2192 ${v.next}`] : [],
40276
+ "(\u043F\u043E\u043B\u043D\u044B\u0439 \u043E\u0442\u0447\u0451\u0442: sberpay-cli setup doctor --json)"
40277
+ ].join("\n");
40278
+ core.out.result(result, human);
40279
+ if (v.suitable === false) process.exitCode = EXIT.API;
40280
+ }
40281
+
39794
40282
  // packages/cli/src/ca/command.ts
39795
40283
  import { spawnSync as spawnSync5 } from "node:child_process";
39796
- import { existsSync as existsSync13 } from "node:fs";
39797
- import { homedir as homedir8 } from "node:os";
40284
+ import { existsSync as existsSync14 } from "node:fs";
40285
+ import { homedir as homedir9 } from "node:os";
39798
40286
  var INSTALL_TIMEOUT_MS = 12e4;
39799
40287
  var WHERE = { system: "\u0432 \u0434\u043E\u0432\u0435\u0440\u0435\u043D\u043D\u044B\u0445 \u043A\u043E\u0440\u043D\u044F\u0445 \u0441\u0438\u0441\u0442\u0435\u043C\u044B", extra: "\u0438\u0437 NODE_EXTRA_CA_CERTS (\u0441\u0438\u0441\u0442\u0435\u043C\u0430 \u0435\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0435\u0442)" };
39800
40288
  function runSecurity(argv) {
@@ -39834,7 +40322,7 @@ async function caFetchCommand(globals) {
39834
40322
  function caInstallCommand(globals) {
39835
40323
  const out = createOutput(Boolean(globals.json));
39836
40324
  out.progress("\u0414\u043E\u0431\u0430\u0432\u043B\u044F\u044E \u043A\u043E\u0440\u0435\u043D\u044C \u0432 \u0434\u043E\u0432\u0435\u0440\u0435\u043D\u043D\u044B\u0435 \u043A\u043E\u0440\u043D\u0438 \u0441\u0438\u0441\u0442\u0435\u043C\u044B \u2014 \u0441\u0438\u0441\u0442\u0435\u043C\u0430 \u043C\u043E\u0436\u0435\u0442 \u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u043F\u0430\u0440\u043E\u043B\u044C\u2026");
39837
- const result = installRootCa({ paths: cliPaths(), platform: process.platform, home: homedir8(), exists: existsSync13, run: runSecurity });
40325
+ const result = installRootCa({ paths: cliPaths(), platform: process.platform, home: homedir9(), exists: existsSync14, run: runSecurity });
39838
40326
  const human = result.installed ? `\u2713 ${result.say}` : [`\u041A\u043E\u043C\u0430\u043D\u0434\u0443 \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u0435\u0442 \u0447\u0435\u043B\u043E\u0432\u0435\u043A: ${result.command ?? "\u2014"}`, ` ${result.say}`].join("\n");
39839
40327
  out.result(result, human);
39840
40328
  }
@@ -39896,6 +40384,14 @@ setup.option("--no-skill", "\u043D\u0435 \u0441\u0442\u0430\u0432\u0438\u0442\u0
39896
40384
  fail(globals, error62);
39897
40385
  }
39898
40386
  });
40387
+ setup.command("doctor").description("\u043F\u0440\u043E\u0432\u0435\u0440\u043A\u0430 \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 \u0441\u0440\u0435\u0434\u044B \u043F\u0435\u0440\u0435\u0434 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u043E\u0439: \u0442\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u0435\u043D\u0438\u0435, \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043C\u0435\u043D\u044F\u0435\u0442").option("--offline", "\u043D\u0435 \u0445\u043E\u0434\u0438\u0442\u044C \u0432 \u0441\u0435\u0442\u044C, \u0448\u043B\u044E\u0437 \u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C").action(async (opts) => {
40388
+ const globals = program2.opts();
40389
+ try {
40390
+ await doctorCommand(globals, { offline: Boolean(opts.offline) });
40391
+ } catch (error62) {
40392
+ fail(globals, error62);
40393
+ }
40394
+ });
39899
40395
  function standalone(fn) {
39900
40396
  return async () => {
39901
40397
  const globals = program2.opts();
@@ -39970,6 +40466,13 @@ address.command("get").description("\u043F\u043E\u043B\u0443\u0447\u0438\u0442\u
39970
40466
  address.command("set").description("\u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C, \u0438\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u0438\u043B\u0438 \u0434\u0435\u0430\u043A\u0442\u0438\u0432\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0430\u0434\u0440\u0435\u0441").requiredOption("--json-file <path>", "JSON-\u0444\u0430\u0439\u043B \u0430\u0434\u0440\u0435\u0441\u0430; - \u0434\u043B\u044F stdin").action(action((s, opts) => s.addressSet(opts)));
39971
40467
  var merchants = program2.command("merchants").description("\u043C\u0430\u0433\u0430\u0437\u0438\u043D\u044B, \u0433\u0434\u0435 \u0430\u0433\u0435\u043D\u0442 \u043C\u043E\u0436\u0435\u0442 \u043F\u043B\u0430\u0442\u0438\u0442\u044C SberPay");
39972
40468
  merchants.command("check <domain>").description("\u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442 \u043B\u0438 \u0441\u0430\u0439\u0442 \u043E\u043F\u043B\u0430\u0442\u0443 \u0430\u0433\u0435\u043D\u0442\u043E\u043C; \u0434\u043E\u043C\u0435\u043D \u0438\u043B\u0438 URL").action(action((s, domain2) => s.merchantsCheck(domain2)));
40469
+ merchants.command("domains").description(
40470
+ "\u0433\u0434\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043E\u043F\u043B\u0430\u0442\u0430 \u0430\u0433\u0435\u043D\u0442\u043E\u043C: \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u0444\u0430\u0439\u043B \u0434\u043E\u043C\u0435\u043D\u043E\u0432 \u0440\u0435\u0435\u0441\u0442\u0440\u0430, \u043E\u0431\u043D\u043E\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u0440\u0430\u0437 \u0432 \u0441\u0443\u0442\u043A\u0438 \u043F\u0440\u0438 \u043E\u0431\u0440\u0430\u0449\u0435\u043D\u0438\u0438; \u043F\u0435\u0440\u0435\u0434 \u043E\u043F\u043B\u0430\u0442\u043E\u0439 \u2014 \u0432\u0441\u0451 \u0440\u0430\u0432\u043D\u043E check"
40471
+ ).option("--match <domain...>", "\u043A\u0430\u043A\u0438\u0435 \u0438\u0437 \u044D\u0442\u0438\u0445 \u0434\u043E\u043C\u0435\u043D\u043E\u0432 \u0438\u043B\u0438 URL \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u044E\u0442 \u043E\u043F\u043B\u0430\u0442\u0443 \u0430\u0433\u0435\u043D\u0442\u043E\u043C").option("--search <text>", "\u0434\u043E\u043C\u0435\u043D\u044B, \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0435 \u0442\u0435\u043A\u0441\u0442 (\u043B\u0430\u0442\u0438\u043D\u0438\u0446\u0430 \u0438\u043B\u0438 \u043A\u0438\u0440\u0438\u043B\u043B\u0438\u0446\u0430)").option("--limit <n>", "\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u043E\u0432 --search \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E 50)").option("--refresh", "\u0441\u043A\u0430\u0447\u0430\u0442\u044C \u0441\u043F\u0438\u0441\u043E\u043A \u0437\u0430\u043D\u043E\u0432\u043E, \u043D\u0435 \u0434\u043E\u0436\u0438\u0434\u0430\u044F\u0441\u044C \u0441\u0443\u0442\u043E\u043A").option("--offline", "\u0442\u043E\u043B\u044C\u043A\u043E \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u0444\u0430\u0439\u043B, \u0431\u0435\u0437 \u0441\u0435\u0442\u0438").action(
40472
+ action(
40473
+ (s, opts) => s.merchantsDomains(opts)
40474
+ )
40475
+ );
39973
40476
  if (BUILD.profile === "diagnostic") registerReportCommands(program2);
39974
40477
  if (BUILD.profile === "diagnostic") registerPaymentModeCommands(program2);
39975
40478
  program2.hook("preAction", async (_root, command) => {