@sberpay-cli/cli-diagnostic 0.4.0-dev.5 → 0.4.0-dev.7

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.
@@ -708,7 +708,7 @@ var require_graceful_fs = __commonJS({
708
708
  }
709
709
  function ReadStream$open() {
710
710
  var that = this;
711
- open7(that.path, that.flags, that.mode, function(err, fd) {
711
+ open8(that.path, that.flags, that.mode, function(err, fd) {
712
712
  if (err) {
713
713
  if (that.autoClose)
714
714
  that.destroy();
@@ -728,7 +728,7 @@ var require_graceful_fs = __commonJS({
728
728
  }
729
729
  function WriteStream$open() {
730
730
  var that = this;
731
- open7(that.path, that.flags, that.mode, function(err, fd) {
731
+ open8(that.path, that.flags, that.mode, function(err, fd) {
732
732
  if (err) {
733
733
  that.destroy();
734
734
  that.emit("error", err);
@@ -745,8 +745,8 @@ var require_graceful_fs = __commonJS({
745
745
  return new fs2.WriteStream(path, options);
746
746
  }
747
747
  var fs$open = fs2.open;
748
- fs2.open = open7;
749
- function open7(path, flags, mode, cb) {
748
+ fs2.open = open8;
749
+ function open8(path, flags, mode, cb) {
750
750
  if (typeof mode === "function")
751
751
  cb = mode, mode = null;
752
752
  return go$open(path, flags, mode, cb);
@@ -1618,27 +1618,13 @@ var require_proper_lockfile = __commonJS({
1618
1618
 
1619
1619
  // scripts/diagnostics/npm-entry.mts
1620
1620
  import { readFile as readFile3 } from "node:fs/promises";
1621
- import { dirname as dirname6, join as join11 } from "node:path";
1621
+ import { dirname as dirname7, join as join11 } from "node:path";
1622
1622
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
1623
1623
  import { homedir as homedir4 } from "node:os";
1624
1624
 
1625
- // packages/cli/src/core/paths.ts
1626
- import { homedir } from "node:os";
1627
- import { join } from "node:path";
1628
- function cliPaths(env = process.env) {
1629
- const home = env.SBERPAY_CLI_HOME;
1630
- if (home) return { configDir: join(home, "config"), cacheDir: join(home, "cache"), stateDir: join(home, "state") };
1631
- const h = homedir();
1632
- return {
1633
- configDir: join(env.XDG_CONFIG_HOME ?? join(h, ".config"), "sberpay-cli"),
1634
- cacheDir: join(env.XDG_CACHE_HOME ?? join(h, ".cache"), "sberpay-cli"),
1635
- stateDir: join(env.XDG_STATE_HOME ?? join(h, ".local", "state"), "sberpay-cli")
1636
- };
1637
- }
1638
-
1639
- // packages/cli/src/paymentMode/state.ts
1640
- import { lstatSync, readFileSync, openSync as openSync2, closeSync as closeSync2, fstatSync, constants } from "node:fs";
1641
- import { join as join2 } from "node:path";
1625
+ // packages/cli/src/core/windowsAcl.ts
1626
+ import { spawnSync } from "node:child_process";
1627
+ import { dirname, win32 } from "node:path";
1642
1628
 
1643
1629
  // node_modules/zod/v4/classic/external.js
1644
1630
  var external_exports = {};
@@ -2362,9 +2348,9 @@ function floatSafeRemainder(val, step) {
2362
2348
  return ratio - roundedRatio;
2363
2349
  }
2364
2350
  var EVALUATING = /* @__PURE__ */ Symbol("evaluating");
2365
- function defineLazy(object3, key, getter) {
2351
+ function defineLazy(object2, key, getter) {
2366
2352
  let value = void 0;
2367
- Object.defineProperty(object3, key, {
2353
+ Object.defineProperty(object2, key, {
2368
2354
  get() {
2369
2355
  if (value === EVALUATING) {
2370
2356
  return void 0;
@@ -2376,7 +2362,7 @@ function defineLazy(object3, key, getter) {
2376
2362
  return value;
2377
2363
  },
2378
2364
  set(v) {
2379
- Object.defineProperty(object3, key, {
2365
+ Object.defineProperty(object2, key, {
2380
2366
  value: v
2381
2367
  // configurable: true,
2382
2368
  });
@@ -17443,8 +17429,8 @@ function foldObjects(members2) {
17443
17429
  }
17444
17430
  const properties = {};
17445
17431
  const required2 = /* @__PURE__ */ new Set();
17446
- for (const object3 of objects) {
17447
- for (const key in object3.properties) {
17432
+ for (const object2 of objects) {
17433
+ for (const key in object2.properties) {
17448
17434
  if (Object.prototype.hasOwnProperty.call(properties, key))
17449
17435
  continue;
17450
17436
  const parts = [];
@@ -17458,18 +17444,18 @@ function foldObjects(members2) {
17458
17444
  const merged = parts.length === 1 ? parts[0] : foldObjects(parts) ?? { allOf: parts };
17459
17445
  assignProp(properties, key, merged);
17460
17446
  }
17461
- for (const key of object3.required ?? [])
17447
+ for (const key of object2.required ?? [])
17462
17448
  required2.add(key);
17463
17449
  }
17464
17450
  const folded = { type: "object", properties };
17465
17451
  if (required2.size)
17466
17452
  folded.required = [...required2];
17467
- if (objects.every((object3) => object3.additionalProperties === false)) {
17453
+ if (objects.every((object2) => object2.additionalProperties === false)) {
17468
17454
  folded.additionalProperties = false;
17469
17455
  } else {
17470
17456
  const constraints = [];
17471
- for (const object3 of objects) {
17472
- const constraint = undeclaredConstraint(object3);
17457
+ for (const object2 of objects) {
17458
+ const constraint = undeclaredConstraint(object2);
17473
17459
  if (constraint && !constraints.some((seen) => JSON.stringify(seen) === JSON.stringify(constraint)))
17474
17460
  constraints.push(constraint);
17475
17461
  }
@@ -21308,12 +21294,135 @@ function date4(params) {
21308
21294
  return _coercedDate(ZodDate, params);
21309
21295
  }
21310
21296
 
21297
+ // packages/cli/src/core/windowsAcl.ts
21298
+ var Sid = external_exports.string().regex(/^S-1-(?:\d+-)*\d+$/);
21299
+ var Inspection = external_exports.object({ owner: Sid, currentUser: Sid, reparse: external_exports.literal(false), rules: external_exports.array(external_exports.object({ sid: Sid, allow: external_exports.boolean(), rights: external_exports.number().int() })).min(1) });
21300
+ var WRITE_ACCESS = 852310 | 1342177280;
21301
+ function assertWindowsAcl(value, policy) {
21302
+ const parsed = Inspection.safeParse(value);
21303
+ if (!parsed.success) throw Error("WINDOWS_ACL_UNSAFE");
21304
+ const s = parsed.data, trusted = /* @__PURE__ */ new Set([s.currentUser, "S-1-5-18", "S-1-5-32-544"]);
21305
+ if (!trusted.has(s.owner) || s.rules.some((r) => r.allow && !trusted.has(r.sid) && (policy === "private" ? r.rights !== 0 : (r.rights & WRITE_ACCESS) !== 0))) throw Error("WINDOWS_ACL_UNSAFE");
21306
+ }
21307
+ var SCRIPT = String.raw`
21308
+ $ErrorActionPreference='Stop'
21309
+ [Console]::OutputEncoding=New-Object System.Text.UTF8Encoding($false)
21310
+ $p=[IO.Path]::GetFullPath($env:SBERPAY_ACL_PATH)
21311
+ $cursor=$p
21312
+ while($cursor){
21313
+ if(Test-Path -LiteralPath $cursor){
21314
+ $item=Get-Item -LiteralPath $cursor -Force
21315
+ if(($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0){throw 'REPARSE_POINT'}
21316
+ }
21317
+ $next=[IO.Path]::GetDirectoryName($cursor)
21318
+ if($next -eq $cursor){break};$cursor=$next
21319
+ }
21320
+ $sid=[Security.Principal.WindowsIdentity]::GetCurrent().User
21321
+ if($env:SBERPAY_ACL_ACTION -eq 'create' -and !(Test-Path -LiteralPath $p)){
21322
+ $acl=New-Object Security.AccessControl.DirectorySecurity
21323
+ $acl.SetOwner($sid);$acl.SetAccessRuleProtection($true,$false)
21324
+ foreach($id in @($sid.Value,'S-1-5-18','S-1-5-32-544')){
21325
+ $principal=New-Object Security.Principal.SecurityIdentifier($id)
21326
+ $rule=New-Object Security.AccessControl.FileSystemAccessRule($principal,'FullControl','ContainerInherit,ObjectInherit','None','Allow')
21327
+ $acl.AddAccessRule($rule)
21328
+ }
21329
+ [IO.Directory]::CreateDirectory($p,$acl)|Out-Null
21330
+ }
21331
+ $acl=Get-Acl -LiteralPath $p
21332
+ $rules=@($acl.GetAccessRules($true,$true,[Security.Principal.SecurityIdentifier])|ForEach-Object{
21333
+ @{sid=$_.IdentityReference.Value;allow=($_.AccessControlType -eq 'Allow');rights=[int]$_.FileSystemRights}
21334
+ })
21335
+ @{owner=$acl.GetOwner([Security.Principal.SecurityIdentifier]).Value;currentUser=$sid.Value;reparse=$false;rules=$rules}|ConvertTo-Json -Compress -Depth 5
21336
+ `;
21337
+ function windowsAclCommand(path, action, env = process.env) {
21338
+ const root2 = env.SystemRoot;
21339
+ if (!root2 || !win32.isAbsolute(root2)) throw Error("WINDOWS_SYSTEM_ROOT_MISSING");
21340
+ return { file: win32.join(root2, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"), args: ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(SCRIPT, "utf16le").toString("base64")], env: { SystemRoot: root2, SBERPAY_ACL_PATH: path, SBERPAY_ACL_ACTION: action } };
21341
+ }
21342
+ function inspect(path, policy, action) {
21343
+ const c = windowsAclCommand(path, action);
21344
+ const r = spawnSync(c.file, c.args, { env: c.env, encoding: "utf8", windowsHide: true, timeout: 1e4, maxBuffer: 128 * 1024 });
21345
+ if (r.error || r.status !== 0) throw Error("WINDOWS_ACL_CHECK_FAILED");
21346
+ let value;
21347
+ try {
21348
+ value = JSON.parse(r.stdout.trim().replace(/^\uFEFF/, ""));
21349
+ } catch {
21350
+ throw Error("WINDOWS_ACL_CHECK_FAILED");
21351
+ }
21352
+ assertWindowsAcl(value, policy);
21353
+ }
21354
+ function assertWindowsPrivatePath(path) {
21355
+ inspect(path, "private", "inspect");
21356
+ }
21357
+ function ensureWindowsPrivateDirectory(path) {
21358
+ inspect(path, "private", "create");
21359
+ }
21360
+
21361
+ // packages/cli/src/report/platform.ts
21362
+ import { posix, win32 as win322 } from "node:path";
21363
+ import { createHash } from "node:crypto";
21364
+ var bashQuote = (s) => "'" + s.replaceAll("'", "'\\''") + "'";
21365
+ function diagnosticLauncherCommand(platform, launcher, node2) {
21366
+ return platform === "win32" ? "& " + [node2, launcher].map((s) => "'" + s.replaceAll("'", "''") + "'").join(" ") : bashQuote(launcher);
21367
+ }
21368
+ function skillFileHashes(skill, files, platform = process.platform) {
21369
+ const paths = platform === "win32" ? win322 : posix;
21370
+ return Object.fromEntries(files.flatMap((f) => {
21371
+ const rel = paths.relative(skill, f.path);
21372
+ return rel && !paths.isAbsolute(rel) && rel !== ".." && !rel.startsWith(".." + paths.sep) ? [[rel.split(paths.sep).join("/"), createHash("sha256").update(f.text).digest("hex")]] : [];
21373
+ }));
21374
+ }
21375
+ function renderDiagnosticSkill(source2, platform, launcher, node2) {
21376
+ if (platform === "win32") source2 = source2.replace(/(\{\{diagnosticLauncher\}\} auth complete --json) <<'TOKEN'\n([\s\S]*?)\nTOKEN/g, "@'\n$2\n'@ | $1").replace(/\\\r?\n\s*/g, " ");
21377
+ return source2.replaceAll("{{diagnosticLauncher}}", diagnosticLauncherCommand(platform, launcher, node2)) + (platform === "win32" ? "\n\u041D\u0430 Windows \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0432\u044B\u0448\u0435 \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u044E\u0442\u0441\u044F \u0432 PowerShell. \u0414\u043B\u044F Claude Code SessionStart hook \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F Git Bash; CLAUDE_ENV_FILE \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u0442 Bash export, \u0430 \u043D\u0435 PowerShell.\n" : "");
21378
+ }
21379
+
21380
+ // packages/cli/src/core/paths.ts
21381
+ import { homedir } from "node:os";
21382
+ import { join } from "node:path";
21383
+ function cliPaths(env = process.env) {
21384
+ const home = env.SBERPAY_CLI_HOME;
21385
+ if (home) return { configDir: join(home, "config"), cacheDir: join(home, "cache"), stateDir: join(home, "state") };
21386
+ const h = homedir();
21387
+ return {
21388
+ configDir: join(env.XDG_CONFIG_HOME ?? join(h, ".config"), "sberpay-cli"),
21389
+ cacheDir: join(env.XDG_CACHE_HOME ?? join(h, ".cache"), "sberpay-cli"),
21390
+ stateDir: join(env.XDG_STATE_HOME ?? join(h, ".local", "state"), "sberpay-cli")
21391
+ };
21392
+ }
21393
+
21394
+ // packages/cli/src/core/privatePath.ts
21395
+ import { lstatSync } from "node:fs";
21396
+ import { open as open2 } from "node:fs/promises";
21397
+ function assertPrivateFile(path, info) {
21398
+ const named = lstatSync(path);
21399
+ if (!info.isFile() || !named.isFile() || named.isSymbolicLink() || info.ino !== named.ino || info.dev !== named.dev) throw Error("UNSAFE_FILE");
21400
+ if (process.platform === "win32") assertWindowsPrivatePath(path);
21401
+ else if (process.getuid && info.uid !== process.getuid() || info.mode & 63) throw Error("UNSAFE_FILE");
21402
+ }
21403
+ async function syncDirectory(path) {
21404
+ if (process.platform === "win32") return;
21405
+ const f = await open2(path, "r");
21406
+ try {
21407
+ await f.sync();
21408
+ } finally {
21409
+ await f.close();
21410
+ }
21411
+ }
21412
+
21413
+ // packages/cli/src/paymentMode/state.ts
21414
+ import { lstatSync as lstatSync2, readFileSync, openSync as openSync2, closeSync as closeSync2, fstatSync, constants } from "node:fs";
21415
+ import { join as join2 } from "node:path";
21416
+
21311
21417
  // packages/cli/src/core/atomicFile.ts
21312
- import { mkdirSync, openSync, writeFileSync, fsyncSync, closeSync, renameSync, rmSync } from "node:fs";
21313
- import { dirname } from "node:path";
21418
+ import { mkdirSync, existsSync, openSync, writeFileSync, fsyncSync, closeSync, renameSync, rmSync } from "node:fs";
21419
+ import { dirname as dirname2 } from "node:path";
21314
21420
  import { randomUUID } from "node:crypto";
21315
21421
  function atomicJson(path, value) {
21316
- mkdirSync(dirname(path), { recursive: true, mode: 448 });
21422
+ if (process.platform === "win32") {
21423
+ ensureWindowsPrivateDirectory(dirname2(path));
21424
+ if (existsSync(path)) assertWindowsPrivatePath(path);
21425
+ } else mkdirSync(dirname2(path), { recursive: true, mode: 448 });
21317
21426
  const temp = `${path}.${randomUUID()}.tmp`;
21318
21427
  try {
21319
21428
  const fd = openSync(temp, "wx", 384);
@@ -21325,7 +21434,7 @@ function atomicJson(path, value) {
21325
21434
  }
21326
21435
  renameSync(temp, path);
21327
21436
  if (process.platform !== "win32") {
21328
- const dir = openSync(dirname(path), "r");
21437
+ const dir = openSync(dirname2(path), "r");
21329
21438
  try {
21330
21439
  fsyncSync(dir);
21331
21440
  } finally {
@@ -21384,8 +21493,9 @@ function invalid() {
21384
21493
  throw new CliError(EXIT.CONFIG_OR_NETWORK, "PAYMENT_MODE_INVALID", "PAYMENT_MODE_INVALID: \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u0440\u0435\u0436\u0438\u043C\u0430 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0438\u043B\u0438 \u043F\u043E\u0432\u0440\u0435\u0436\u0434\u0435\u043D\u0430; \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 payment-mode set.");
21385
21494
  }
21386
21495
  function checkDir(paths) {
21387
- const s = lstatSync(paths.configDir);
21388
- if (!s.isDirectory() || s.isSymbolicLink() || (s.mode & 63) !== 0) invalid();
21496
+ const s = lstatSync2(paths.configDir);
21497
+ if (!s.isDirectory() || s.isSymbolicLink() || process.platform !== "win32" && (s.mode & 63) !== 0) invalid();
21498
+ if (process.platform === "win32") assertWindowsPrivatePath(paths.configDir);
21389
21499
  }
21390
21500
  function readPaymentMode(paths) {
21391
21501
  try {
@@ -21393,7 +21503,8 @@ function readPaymentMode(paths) {
21393
21503
  const fd = openSync2(modeFile(paths), constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
21394
21504
  try {
21395
21505
  const s = fstatSync(fd);
21396
- if (!s.isFile() || s.size > 4096 || (s.mode & 63) !== 0) invalid();
21506
+ assertPrivateFile(modeFile(paths), s);
21507
+ if (s.size > 4096) invalid();
21397
21508
  return PaymentModeStateSchema.parse(JSON.parse(readFileSync(fd, "utf8")));
21398
21509
  } finally {
21399
21510
  closeSync2(fd);
@@ -21404,8 +21515,8 @@ function readPaymentMode(paths) {
21404
21515
  }
21405
21516
 
21406
21517
  // packages/cli/src/core/fileLock.ts
21407
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, rmSync as rmSync2, renameSync as renameSync2, existsSync } from "node:fs";
21408
- import { dirname as dirname2, join as join3 } from "node:path";
21518
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, rmSync as rmSync2, renameSync as renameSync2, existsSync as existsSync2 } from "node:fs";
21519
+ import { dirname as dirname3, join as join3 } from "node:path";
21409
21520
  import { randomUUID as randomUUID2 } from "node:crypto";
21410
21521
  function processAlive(pid) {
21411
21522
  if (!Number.isSafeInteger(pid) || pid <= 0) return void 0;
@@ -21442,7 +21553,7 @@ function owner(path) {
21442
21553
  }
21443
21554
  }
21444
21555
  async function withFileLock(path, fn, timeoutMs = 5e3) {
21445
- mkdirSync2(dirname2(path), { recursive: true, mode: 448 });
21556
+ mkdirSync2(dirname3(path), { recursive: true, mode: 448 });
21446
21557
  const nonce = randomUUID2(), deadline = Date.now() + timeoutMs;
21447
21558
  for (; ; ) {
21448
21559
  try {
@@ -21469,7 +21580,7 @@ async function withFileLock(path, fn, timeoutMs = 5e3) {
21469
21580
  } finally {
21470
21581
  if (acquired) rmSync2(reaper, { recursive: true, force: true });
21471
21582
  }
21472
- if (!existsSync(path)) continue;
21583
+ if (!existsSync2(path)) continue;
21473
21584
  }
21474
21585
  if (Date.now() >= deadline)
21475
21586
  throw new CliError(EXIT.CONFIG_OR_NETWORK, "BINDING_BUSY", "\u0414\u0440\u0443\u0433\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430 \u0432\u043B\u0430\u0434\u0435\u0435\u0442 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435\u043C \u0438\u043B\u0438 \u0432\u043B\u0430\u0434\u0435\u043B\u0435\u0446 \u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u043A\u0438 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D. \u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0435 \u043F\u043E\u0437\u0436\u0435.");
@@ -21485,7 +21596,7 @@ async function withFileLock(path, fn, timeoutMs = 5e3) {
21485
21596
 
21486
21597
  // packages/cli/src/probe/journal.ts
21487
21598
  import {
21488
- existsSync as existsSync2,
21599
+ existsSync as existsSync3,
21489
21600
  mkdirSync as mkdirSync4,
21490
21601
  readFileSync as readFileSync4,
21491
21602
  rmSync as rmSync3,
@@ -21493,7 +21604,7 @@ import {
21493
21604
  openSync as openSync4,
21494
21605
  fsyncSync as fsyncSync3,
21495
21606
  closeSync as closeSync4,
21496
- lstatSync as lstatSync3
21607
+ lstatSync as lstatSync4
21497
21608
  } from "node:fs";
21498
21609
  import { join as join5 } from "node:path";
21499
21610
 
@@ -21519,7 +21630,7 @@ import {
21519
21630
  closeSync as closeSync3,
21520
21631
  fsyncSync as fsyncSync2,
21521
21632
  fstatSync as fstatSync2,
21522
- lstatSync as lstatSync2,
21633
+ lstatSync as lstatSync3,
21523
21634
  mkdirSync as mkdirSync3,
21524
21635
  openSync as openSync3,
21525
21636
  readFileSync as readFileSync3,
@@ -21536,14 +21647,16 @@ function journalError() {
21536
21647
  );
21537
21648
  }
21538
21649
  function privateDir(path) {
21539
- mkdirSync3(path, { recursive: true, mode: 448 });
21540
- const s = lstatSync2(path);
21650
+ if (process.platform === "win32") ensureWindowsPrivateDirectory(path);
21651
+ else mkdirSync3(path, { recursive: true, mode: 448 });
21652
+ const s = lstatSync3(path);
21541
21653
  if (!s.isDirectory() || s.isSymbolicLink() || process.platform !== "win32" && ((s.mode & 63) !== 0 || s.uid !== process.getuid?.()))
21542
21654
  journalError();
21543
21655
  }
21544
21656
  function registryDir(paths) {
21545
- mkdirSync3(paths.stateDir, { recursive: true, mode: 448 });
21546
- if (lstatSync2(paths.stateDir).isSymbolicLink()) journalError();
21657
+ if (process.platform === "win32") ensureWindowsPrivateDirectory(paths.stateDir);
21658
+ else mkdirSync3(paths.stateDir, { recursive: true, mode: 448 });
21659
+ if (lstatSync3(paths.stateDir).isSymbolicLink()) journalError();
21547
21660
  const dir = join4(paths.stateDir, "payment-probes");
21548
21661
  privateDir(dir);
21549
21662
  return dir;
@@ -21560,6 +21673,12 @@ function syncDir(dir) {
21560
21673
  function openPrivate(path, flags) {
21561
21674
  const fd = openSync3(path, flags | (constants2.O_NOFOLLOW ?? 0), 384);
21562
21675
  const s = fstatSync2(fd);
21676
+ try {
21677
+ assertPrivateFile(path, s);
21678
+ } catch {
21679
+ closeSync3(fd);
21680
+ journalError();
21681
+ }
21563
21682
  if (!s.isFile() || s.nlink !== 1 || s.size > MAX_FILE || process.platform !== "win32" && ((s.mode & 63) !== 0 || s.uid !== process.getuid?.())) {
21564
21683
  closeSync3(fd);
21565
21684
  journalError();
@@ -21710,7 +21829,7 @@ function lockError(code) {
21710
21829
  }
21711
21830
  function ownerAlive(lock) {
21712
21831
  try {
21713
- if (lstatSync3(lock).isSymbolicLink()) journalError();
21832
+ if (lstatSync4(lock).isSymbolicLink()) journalError();
21714
21833
  const raw = readFileSync4(join5(lock, "owner.json"), "utf8");
21715
21834
  if (raw.length > 256) journalError();
21716
21835
  const pid = JSON.parse(raw).pid;
@@ -21729,7 +21848,7 @@ function ownerAlive(lock) {
21729
21848
  async function exclusive(paths, recover, fn, installing2 = false) {
21730
21849
  const dir = registryDir(paths), lock = join5(dir, ".lock");
21731
21850
  let recovered = false;
21732
- if (existsSync2(lock)) {
21851
+ if (existsSync3(lock)) {
21733
21852
  if (ownerAlive(lock)) lockError("PROBE_BUSY");
21734
21853
  if (!recover) lockError("PROBE_STALE_LOCK");
21735
21854
  try {
@@ -21765,7 +21884,7 @@ async function exclusive(paths, recover, fn, installing2 = false) {
21765
21884
  }
21766
21885
  syncDir(lock);
21767
21886
  syncDir(dir);
21768
- if (!installing2 && existsSync2(join5(paths.configDir, "../..", "install-pending.json")))
21887
+ if (!installing2 && existsSync3(join5(paths.configDir, "../..", "install-pending.json")))
21769
21888
  throw new CliError(EXIT.CONFIG_OR_NETWORK, "INSTALL_IN_PROGRESS", "\u0423\u0441\u0442\u0430\u043D\u043E\u0432\u043A\u0430 \u043D\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430");
21770
21889
  return await fn();
21771
21890
  } finally {
@@ -21780,29 +21899,8 @@ var withPaymentLock = (paths, fn, installing2 = false) => exclusive(paths, insta
21780
21899
  return fn();
21781
21900
  }, installing2);
21782
21901
 
21783
- // packages/cli/src/report/installTransaction.ts
21784
- var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
21785
- import { mkdir as mkdir2, open as open3, rename as rename2, rm as rm2, lstat as lstat2, readFile } from "node:fs/promises";
21786
- import { join as join6, dirname as dirname3 } from "node:path";
21787
- import { createHash, randomUUID as randomUUID3 } from "node:crypto";
21788
-
21789
- // packages/cli/src/report/transport.ts
21790
- import { constants as constants3 } from "node:fs";
21791
- import { open as open2, lstat, rename, rm, mkdir } from "node:fs/promises";
21792
-
21793
- // packages/case-protocol/src/index.ts
21794
- var MAX_ZIP = 20 * 1024 * 1024;
21795
- var MAX_EXPANDED = 100 * 1024 * 1024;
21796
- var CaseId = external_exports.uuid();
21797
- var Receipt = external_exports.object({ schemaVersion: external_exports.literal(1), caseId: CaseId, sha256: external_exports.string().regex(/^[a-f0-9]{64}$/), bytes: external_exports.number().int().positive().max(MAX_ZIP), status: external_exports.literal("accepted"), acceptedAt: external_exports.iso.datetime() }).strict();
21798
- var ConversationSource = external_exports.union([
21799
- external_exports.object({ attached: external_exports.literal(true).optional(), identity: external_exports.literal("verified"), coverage: external_exports.enum(["partial", "complete"]) }),
21800
- external_exports.object({ attached: external_exports.literal(false), identity: external_exports.enum(["verified", "ambiguous", "unavailable"]), coverage: external_exports.enum(["unavailable", "unsupported", "failed"]), warnings: external_exports.array(external_exports.string()).min(1).refine((items) => items.includes("HISTORY_NOT_ATTACHED")) })
21801
- ]);
21802
- var Manifest = external_exports.object({ schemaVersion: external_exports.union([external_exports.literal(1), external_exports.literal(2)]), caseId: CaseId, scope: external_exports.enum(["current_session", "purchase_episode"]), sources: external_exports.object({ conversation: ConversationSource, cli: external_exports.object({ coverage: external_exports.enum(["partial", "unavailable"]) }) }), redaction: external_exports.object({ version: external_exports.literal(1), status: external_exports.literal("best_effort") }), files: external_exports.array(external_exports.object({ path: external_exports.string(), bytes: external_exports.number().int().nonnegative(), sha256: external_exports.string().regex(/^[a-f0-9]{64}$/) }).strict()).length(5) });
21803
-
21804
21902
  // packages/cli/src/report/storage.ts
21805
- import { lstatSync as lstatSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync2, unlinkSync } from "node:fs";
21903
+ import { lstatSync as lstatSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync2, unlinkSync } from "node:fs";
21806
21904
 
21807
21905
  // packages/cli/src/report/bindingFiles.ts
21808
21906
  var BindingError = class extends Error {
@@ -21819,18 +21917,41 @@ var BindingError = class extends Error {
21819
21917
 
21820
21918
  // packages/cli/src/report/storage.ts
21821
21919
  function privateDir2(path) {
21822
- mkdirSync5(path, { recursive: true, mode: 448 });
21823
- const info = lstatSync4(path);
21824
- if (!info.isDirectory() || info.isSymbolicLink() || process.getuid && info.uid !== process.getuid() || info.mode & 63) throw new BindingError("UNSAFE_REPORT_DIRECTORY");
21920
+ if (process.platform === "win32") ensureWindowsPrivateDirectory(path);
21921
+ else mkdirSync5(path, { recursive: true, mode: 448 });
21922
+ const info = lstatSync5(path);
21923
+ if (!info.isDirectory() || info.isSymbolicLink() || process.getuid && info.uid !== process.getuid() || process.platform !== "win32" && info.mode & 63) throw new BindingError("UNSAFE_REPORT_DIRECTORY");
21825
21924
  return path;
21826
21925
  }
21827
21926
 
21927
+ // packages/cli/src/report/installTransaction.ts
21928
+ var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
21929
+ import { mkdir as mkdir2, open as open4, rename as rename2, rm as rm2, lstat as lstat2, readFile } from "node:fs/promises";
21930
+ import { join as join6, dirname as dirname4 } from "node:path";
21931
+ import { createHash as createHash2, randomUUID as randomUUID3 } from "node:crypto";
21932
+
21933
+ // packages/cli/src/report/transport.ts
21934
+ import { constants as constants3 } from "node:fs";
21935
+ import { open as open3, lstat, rename, rm, mkdir } from "node:fs/promises";
21936
+
21937
+ // packages/case-protocol/src/index.ts
21938
+ var MAX_ZIP = 20 * 1024 * 1024;
21939
+ var MAX_EXPANDED = 100 * 1024 * 1024;
21940
+ var CaseId = external_exports.uuid();
21941
+ var Receipt = external_exports.object({ schemaVersion: external_exports.literal(1), caseId: CaseId, sha256: external_exports.string().regex(/^[a-f0-9]{64}$/), bytes: external_exports.number().int().positive().max(MAX_ZIP), status: external_exports.literal("accepted"), acceptedAt: external_exports.iso.datetime() }).strict();
21942
+ var ConversationSource = external_exports.union([
21943
+ external_exports.object({ attached: external_exports.literal(true).optional(), identity: external_exports.literal("verified"), coverage: external_exports.enum(["partial", "complete"]) }),
21944
+ external_exports.object({ attached: external_exports.literal(false), identity: external_exports.enum(["verified", "ambiguous", "unavailable"]), coverage: external_exports.enum(["unavailable", "unsupported", "failed"]), warnings: external_exports.array(external_exports.string()).min(1).refine((items) => items.includes("HISTORY_NOT_ATTACHED")) })
21945
+ ]);
21946
+ var Manifest = external_exports.object({ schemaVersion: external_exports.union([external_exports.literal(1), external_exports.literal(2)]), caseId: CaseId, scope: external_exports.enum(["current_session", "purchase_episode"]), sources: external_exports.object({ conversation: ConversationSource, cli: external_exports.object({ coverage: external_exports.enum(["partial", "unavailable"]) }) }), redaction: external_exports.object({ version: external_exports.literal(1), status: external_exports.literal("best_effort") }), files: external_exports.array(external_exports.object({ path: external_exports.string(), bytes: external_exports.number().int().nonnegative(), sha256: external_exports.string().regex(/^[a-f0-9]{64}$/) }).strict()).length(5) });
21947
+
21828
21948
  // packages/cli/src/report/transport.ts
21829
21949
  async function privateRead(path, limit) {
21830
- const file2 = await open2(path, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
21950
+ const file2 = await open3(path, constants3.O_RDONLY | constants3.O_NOFOLLOW | constants3.O_NONBLOCK);
21831
21951
  try {
21832
21952
  const info = await file2.stat();
21833
- if (!info.isFile() || process.getuid && info.uid !== process.getuid() || info.mode & 63 || info.size > limit) throw Error("UNSAFE_FILE");
21953
+ assertPrivateFile(path, info);
21954
+ if (info.size > limit) throw Error("UNSAFE_FILE");
21834
21955
  const buffer = Buffer.alloc(info.size);
21835
21956
  let at = 0;
21836
21957
  while (at < buffer.length) {
@@ -21846,12 +21967,13 @@ async function privateRead(path, limit) {
21846
21967
  var configSchema = external_exports.object({ endpoint: external_exports.string().url(), token: external_exports.string().min(32).max(512).regex(/^[A-Za-z0-9_-]+$/) }).strict();
21847
21968
 
21848
21969
  // packages/cli/src/report/installTransaction.ts
21849
- var hash2 = (s) => createHash("sha256").update(s).digest("hex");
21970
+ var hash2 = (s) => createHash2("sha256").update(s).digest("hex");
21850
21971
  async function durableWrite(path, text, mode = 384) {
21851
- await mkdir2(dirname3(path), { recursive: true, mode: 448 });
21972
+ if (process.platform === "win32") privateDir2(dirname4(path));
21973
+ else await mkdir2(dirname4(path), { recursive: true, mode: 448 });
21852
21974
  const temp = path + "." + randomUUID3() + ".tmp";
21853
21975
  try {
21854
- const f = await open3(temp, "wx", mode);
21976
+ const f = await open4(temp, "wx", mode);
21855
21977
  try {
21856
21978
  await f.writeFile(text);
21857
21979
  await f.sync();
@@ -21859,21 +21981,16 @@ async function durableWrite(path, text, mode = 384) {
21859
21981
  await f.close();
21860
21982
  }
21861
21983
  await rename2(temp, path);
21862
- const d = await open3(dirname3(path), "r");
21863
- try {
21864
- await d.sync();
21865
- } finally {
21866
- await d.close();
21867
- }
21984
+ await syncDirectory(dirname4(path));
21868
21985
  } finally {
21869
21986
  await rm2(temp, { force: true });
21870
21987
  }
21871
21988
  }
21872
21989
  async function withInstallLock(root2, fn) {
21873
- await mkdir2(root2, { recursive: true, mode: 448 });
21990
+ privateDir2(root2);
21874
21991
  let release;
21875
21992
  try {
21876
- release = await import_proper_lockfile.default.lock(root2, { lockfilePath: join6(root2, ".setup-lock"), realpath: false, stale: 5e3, update: 1e3, retries: 0 });
21993
+ release = await import_proper_lockfile.default.lock(root2, { lockfilePath: join6(root2, ".setup-lock"), realpath: false, stale: process.platform === "win32" ? 12e4 : 5e3, update: 1e3, retries: 0 });
21877
21994
  } catch (e) {
21878
21995
  if (e.code === "ELOCKED") throw Error("INSTALL_BUSY");
21879
21996
  throw e;
@@ -21935,22 +22052,17 @@ async function applyInstallFiles(root2, buildId, files, finish) {
21935
22052
  for (const file2 of files) await durableWrite(file2.path, file2.text, file2.mode);
21936
22053
  await finish();
21937
22054
  await rm2(journal);
21938
- const d = await open3(root2, "r");
21939
- try {
21940
- await d.sync();
21941
- } finally {
21942
- await d.close();
21943
- }
22055
+ await syncDirectory(root2);
21944
22056
  }
21945
22057
 
21946
22058
  // packages/cli/src/report/openEnrollment.ts
21947
22059
  import { generateKeyPairSync as generateKeyPairSync2, createPrivateKey as createPrivateKey2, createPublicKey as createPublicKey2, randomUUID as randomUUID5, sign as sign2 } from "node:crypto";
21948
- import { open as open5, rename as rename4, rm as rm4, lstat as lstat3 } from "node:fs/promises";
22060
+ import { open as open6, rename as rename4, rm as rm4, lstat as lstat3 } from "node:fs/promises";
21949
22061
  import { join as join8 } from "node:path";
21950
22062
 
21951
22063
  // packages/cli/src/report/enrollment.ts
21952
- import { generateKeyPairSync, createHash as createHash2, createPrivateKey, createPublicKey, randomUUID as randomUUID4, sign } from "node:crypto";
21953
- import { open as open4, rename as rename3, rm as rm3 } from "node:fs/promises";
22064
+ import { generateKeyPairSync, createHash as createHash3, createPrivateKey, createPublicKey, randomUUID as randomUUID4, sign } from "node:crypto";
22065
+ import { open as open5, rename as rename3, rm as rm3 } from "node:fs/promises";
21954
22066
  import { join as join7 } from "node:path";
21955
22067
  import { request as httpsRequest } from "node:https";
21956
22068
  var DIAGNOSTIC_ENDPOINT = "https://aiplatilka.ru/case-reports";
@@ -21984,36 +22096,28 @@ var enrollmentPost = (path, body) => new Promise((resolve2, reject) => {
21984
22096
  request.on("error", reject);
21985
22097
  request.end(bytes);
21986
22098
  });
21987
- async function syncDir2(dir) {
21988
- const f = await open4(dir, "r");
21989
- try {
21990
- await f.sync();
21991
- } finally {
21992
- await f.close();
21993
- }
21994
- }
21995
22099
  async function enrollInvitation(path, env = process.env, request = enrollmentPost) {
21996
22100
  try {
21997
22101
  const invite = Invitation.parse(JSON.parse((await privateRead(path, 8192)).toString("utf8")));
21998
- const hash4 = createHash2("sha256").update(invite.token).digest("hex");
22102
+ const hash4 = createHash3("sha256").update(invite.token).digest("hex");
21999
22103
  const dir = privateDir2(cliPaths(env).configDir);
22000
22104
  const statePath = join7(dir, "report-installation.json");
22001
22105
  try {
22002
22106
  const pair = generateKeyPairSync("ed25519");
22003
- const file2 = await open4(statePath, "wx", 384);
22107
+ const file2 = await open5(statePath, "wx", 384);
22004
22108
  try {
22005
22109
  await file2.writeFile(JSON.stringify({ invitationHash: hash4, installationId: randomUUID4(), privateKey: pair.privateKey.export({ format: "pem", type: "pkcs8" }), publicKey: pair.publicKey.export({ format: "pem", type: "spki" }) }));
22006
22110
  await file2.sync();
22007
22111
  } finally {
22008
22112
  await file2.close();
22009
22113
  }
22010
- await syncDir2(dir);
22114
+ await syncDirectory(dir);
22011
22115
  } catch (error62) {
22012
22116
  if (error62.code !== "EEXIST") throw error62;
22013
22117
  }
22014
22118
  const state = State.parse(JSON.parse((await privateRead(statePath, 8192)).toString("utf8")));
22015
22119
  if (state.invitationHash !== hash4 || createPublicKey(state.privateKey).export({ format: "pem", type: "spki" }).toString() !== state.publicKey) throw Error("INSTALLATION_CONFLICT");
22016
- await syncDir2(dir);
22120
+ await syncDirectory(dir);
22017
22121
  const body = { inviteToken: invite.token, installationId: state.installationId, publicKey: state.publicKey };
22018
22122
  const challenge = external_exports.object({ challenge: external_exports.string().max(2048) }).strict().parse(await request("/v1/enroll/challenge", body));
22019
22123
  const signature = sign(null, Buffer.from("sberpay-enroll-v1\n" + challenge.challenge), createPrivateKey(state.privateKey)).toString("base64url");
@@ -22021,7 +22125,7 @@ async function enrollInvitation(path, env = process.env, request = enrollmentPos
22021
22125
  if (response.installationId !== state.installationId) throw Error("INSTALLATION_MISMATCH");
22022
22126
  const temp = join7(dir, `.upload-${randomUUID4()}.tmp`);
22023
22127
  try {
22024
- const file2 = await open4(temp, "wx", 384);
22128
+ const file2 = await open5(temp, "wx", 384);
22025
22129
  try {
22026
22130
  await file2.writeFile(JSON.stringify({ endpoint: DIAGNOSTIC_ENDPOINT, token: response.token }));
22027
22131
  await file2.sync();
@@ -22029,7 +22133,7 @@ async function enrollInvitation(path, env = process.env, request = enrollmentPos
22029
22133
  await file2.close();
22030
22134
  }
22031
22135
  await rename3(temp, join7(dir, "report-upload.json"));
22032
- await syncDir2(dir);
22136
+ await syncDirectory(dir);
22033
22137
  } finally {
22034
22138
  await rm3(temp, { force: true });
22035
22139
  }
@@ -22043,14 +22147,6 @@ async function enrollInvitation(path, env = process.env, request = enrollmentPos
22043
22147
  var State2 = external_exports.object({ schemaVersion: external_exports.literal(1), installationId: external_exports.uuid(), privateKey: external_exports.string().max(256), publicKey: external_exports.string().max(256) }).strict();
22044
22148
  var Credential2 = external_exports.object({ schemaVersion: external_exports.literal(1), installationId: external_exports.uuid(), owner: external_exports.string(), token: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/) }).strict();
22045
22149
  var Upload = external_exports.object({ endpoint: external_exports.literal(DIAGNOSTIC_ENDPOINT), token: external_exports.string().min(32).max(512).regex(/^[A-Za-z0-9_-]+$/) }).strict();
22046
- async function syncDir3(dir) {
22047
- const f = await open5(dir, "r");
22048
- try {
22049
- await f.sync();
22050
- } finally {
22051
- await f.close();
22052
- }
22053
- }
22054
22150
  async function enrollOpen(env = process.env, request = enrollmentPost) {
22055
22151
  const dir = privateDir2(cliPaths(env).configDir), statePath = join8(dir, "report-open-installation.json");
22056
22152
  try {
@@ -22071,7 +22167,7 @@ async function enrollOpen(env = process.env, request = enrollmentPost) {
22071
22167
  if (e.code !== "ENOENT") throw e;
22072
22168
  const pair = generateKeyPairSync2("ed25519"), temp = statePath + "." + randomUUID5() + ".tmp";
22073
22169
  try {
22074
- const f = await open5(temp, "wx", 384);
22170
+ const f = await open6(temp, "wx", 384);
22075
22171
  try {
22076
22172
  await f.writeFile(JSON.stringify({ schemaVersion: 1, installationId: randomUUID5(), privateKey: pair.privateKey.export({ format: "pem", type: "pkcs8" }), publicKey: pair.publicKey.export({ format: "pem", type: "spki" }) }));
22077
22173
  await f.sync();
@@ -22079,7 +22175,7 @@ async function enrollOpen(env = process.env, request = enrollmentPost) {
22079
22175
  await f.close();
22080
22176
  }
22081
22177
  await rename4(temp, statePath);
22082
- await syncDir3(dir);
22178
+ await syncDirectory(dir);
22083
22179
  } finally {
22084
22180
  await rm4(temp, { force: true });
22085
22181
  }
@@ -22091,7 +22187,7 @@ async function enrollOpen(env = process.env, request = enrollmentPost) {
22091
22187
  } catch {
22092
22188
  throw Error("INSTALLATION_STATE_INVALID");
22093
22189
  }
22094
- await syncDir3(dir);
22190
+ await syncDirectory(dir);
22095
22191
  try {
22096
22192
  const body = { installationId: state.installationId, publicKey: state.publicKey };
22097
22193
  const c = external_exports.object({ challenge: external_exports.string().max(2048) }).strict().parse(await request("/v2/enroll/challenge", body));
@@ -22100,7 +22196,7 @@ async function enrollOpen(env = process.env, request = enrollmentPost) {
22100
22196
  if (response.installationId !== state.installationId || response.owner !== "installation:" + state.installationId) throw Error("INSTALLATION_MISMATCH");
22101
22197
  const temp = join8(dir, ".upload-" + randomUUID5() + ".tmp");
22102
22198
  try {
22103
- const f = await open5(temp, "wx", 384);
22199
+ const f = await open6(temp, "wx", 384);
22104
22200
  try {
22105
22201
  await f.writeFile(JSON.stringify({ endpoint: DIAGNOSTIC_ENDPOINT, token: response.token }));
22106
22202
  await f.sync();
@@ -22108,7 +22204,7 @@ async function enrollOpen(env = process.env, request = enrollmentPost) {
22108
22204
  await f.close();
22109
22205
  }
22110
22206
  await rename4(temp, join8(dir, "report-upload.json"));
22111
- await syncDir3(dir);
22207
+ await syncDirectory(dir);
22112
22208
  } finally {
22113
22209
  await rm4(temp, { force: true });
22114
22210
  }
@@ -22118,8 +22214,65 @@ async function enrollOpen(env = process.env, request = enrollmentPost) {
22118
22214
  }
22119
22215
  }
22120
22216
 
22217
+ // packages/cli/src/report/windowsSender.ts
22218
+ import { win32 as win323 } from "node:path";
22219
+ import { createHash as createHash4 } from "node:crypto";
22220
+ var windowsArgument = (s) => '"' + s.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/, "$1$1") + '"';
22221
+ function windowsSenderConfig(home, launcher, node2) {
22222
+ if (!win323.isAbsolute(launcher) || !win323.isAbsolute(node2) || /[\0\r\n]/.test(launcher + node2)) throw Error("INVALID_SENDER_PATH");
22223
+ return { path: win323.join(home, ".local", "share", "sberpay-diagnostic", "sender-task.json"), text: JSON.stringify({ name: "SberPay.Diagnostic." + createHash4("sha256").update(launcher.toLowerCase()).digest("hex").slice(0, 24), node: node2, launcher, arguments: windowsArgument(launcher) + " report flush --json" }) };
22224
+ }
22225
+ var SCRIPT2 = String.raw`
22226
+ $ErrorActionPreference='Stop'
22227
+ [Console]::OutputEncoding=New-Object System.Text.UTF8Encoding($false)
22228
+ $c=Get-Content -LiteralPath $env:SBERPAY_SENDER_CONFIG -Raw -Encoding UTF8|ConvertFrom-Json
22229
+ $sid=[Security.Principal.WindowsIdentity]::GetCurrent().User.Value
22230
+ $service=New-Object -ComObject 'Schedule.Service'
22231
+ $service.Connect()
22232
+ $folder=$service.GetFolder('\')
22233
+ $source='sberpay-diagnostic:'+ $c.launcher
22234
+ $existing=$null
22235
+ try{$existing=$folder.GetTask($c.name)}catch{$e=$_.Exception;while($e.InnerException){$e=$e.InnerException};if($e.HResult -ne -2147024894){throw}}
22236
+ if($existing){
22237
+ if($existing.Definition.RegistrationInfo.Source -cne $source -or $existing.Definition.Principal.UserId -ne $sid){throw 'SENDER_TASK_CONFLICT'}
22238
+ }
22239
+ $task=$service.NewTask(0)
22240
+ $task.RegistrationInfo.Source=$source
22241
+ $task.RegistrationInfo.Description='Deliver queued SberPay diagnostic cases'
22242
+ $task.Principal.UserId=$sid
22243
+ $task.Principal.LogonType=3
22244
+ $task.Principal.RunLevel=0
22245
+ $task.Settings.Enabled=$true
22246
+ $task.Settings.StartWhenAvailable=$true
22247
+ $task.Settings.DisallowStartIfOnBatteries=$false
22248
+ $task.Settings.StopIfGoingOnBatteries=$false
22249
+ $task.Settings.MultipleInstances=2
22250
+ # Each upload has its own deadline; never kill a sequential queue before later cases.
22251
+ $task.Settings.ExecutionTimeLimit='PT0S'
22252
+ $timer=$task.Triggers.Create(1)
22253
+ $timer.StartBoundary=(Get-Date).AddMinutes(1).ToString('yyyy-MM-ddTHH:mm:ss')
22254
+ $timer.Repetition.Interval='PT5M'
22255
+ $timer.Enabled=$true
22256
+ $logon=$task.Triggers.Create(9)
22257
+ $logon.UserId=$sid
22258
+ $logon.Enabled=$true
22259
+ $action=$task.Actions.Create(0)
22260
+ $action.Path=$c.node
22261
+ $action.Arguments=$c.arguments
22262
+ $action.WorkingDirectory=[IO.Path]::GetDirectoryName($c.launcher)
22263
+ $registered=$folder.RegisterTaskDefinition($c.name,$task,6,$sid,$null,3,$null)
22264
+ $read=$folder.GetTask($c.name)
22265
+ $d=$read.Definition
22266
+ if($d.Settings.ExecutionTimeLimit -ne 'PT0S' -or $d.Settings.MultipleInstances -ne 2 -or !$read.Enabled -or $d.Principal.UserId -ne $sid -or $d.Principal.LogonType -ne 3 -or $d.Principal.RunLevel -ne 0 -or $d.Actions.Count -ne 1 -or $d.Actions.Item(1).Path -cne $c.node -or $d.Actions.Item(1).Arguments -cne $c.arguments -or $d.Triggers.Count -ne 2 -or $d.Triggers.Item(1).Repetition.Interval -ne 'PT5M'){throw 'SENDER_TASK_NOT_ACTIVE'}
22267
+ @{status='active';taskName=$c.name}|ConvertTo-Json -Compress
22268
+ `;
22269
+ function windowsSenderCommand(path, env = process.env) {
22270
+ if (!env.SystemRoot || !win323.isAbsolute(env.SystemRoot)) throw Error("WINDOWS_SYSTEM_ROOT_MISSING");
22271
+ return { file: win323.join(env.SystemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe"), args: ["-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(SCRIPT2, "utf16le").toString("base64")], env: { SystemRoot: env.SystemRoot, SBERPAY_SENDER_CONFIG: path } };
22272
+ }
22273
+
22121
22274
  // packages/cli/src/report/sender.ts
22122
- import { join as join9, dirname as dirname4 } from "node:path";
22275
+ import { join as join9, dirname as dirname5 } from "node:path";
22123
22276
  import { homedir as homedir2 } from "node:os";
22124
22277
  import { mkdir as mkdir3, writeFile as writeFile2, lstat as lstat4, rename as rename5, rm as rm5 } from "node:fs/promises";
22125
22278
  import { execFile } from "node:child_process";
@@ -22148,15 +22301,18 @@ Persistent=true
22148
22301
  [Install]
22149
22302
  WantedBy=timers.target
22150
22303
  ` }];
22304
+ if (platform === "win32") return [windowsSenderConfig(home, launcher, node2)];
22151
22305
  return [];
22152
22306
  }
22153
22307
  async function installSender(home, launcher) {
22154
22308
  const files = senderConfig(process.platform, home, launcher, process.execPath);
22155
22309
  for (const f of files) {
22156
- await mkdir3(dirname4(f.path), { recursive: true, mode: 448 });
22310
+ if (process.platform === "win32") privateDir2(dirname5(f.path));
22311
+ else await mkdir3(dirname5(f.path), { recursive: true, mode: 448 });
22157
22312
  try {
22158
22313
  const s = await lstat4(f.path);
22159
22314
  if (!s.isFile() || s.isSymbolicLink()) throw Error("UNSAFE_SENDER_PATH");
22315
+ if (process.platform === "win32") assertWindowsPrivatePath(f.path);
22160
22316
  } catch (e) {
22161
22317
  if (e.code !== "ENOENT") throw e;
22162
22318
  }
@@ -22175,6 +22331,10 @@ async function installSender(home, launcher) {
22175
22331
  await run("launchctl", ["bootout", domain2 + "/" + label]).catch(() => {
22176
22332
  });
22177
22333
  await run("launchctl", ["bootstrap", domain2, files[0].path]);
22334
+ } else if (process.platform === "win32") {
22335
+ const c = windowsSenderCommand(files[0].path);
22336
+ const result = await run(c.file, c.args, { env: c.env, windowsHide: true, timeout: 3e4, maxBuffer: 65536 });
22337
+ if (JSON.parse(result.stdout.trim().replace(/^\uFEFF/, "")).status !== "active") throw Error("SENDER_TASK_NOT_ACTIVE");
22178
22338
  } else {
22179
22339
  await run("systemctl", ["--user", "daemon-reload"]);
22180
22340
  await run("systemctl", ["--user", "enable", "--now", label + ".timer"]);
@@ -22193,15 +22353,10 @@ var LAST_LISTED_MAJOR = 24;
22193
22353
  import { constants as constants4 } from "node:fs";
22194
22354
  import { pathToFileURL } from "node:url";
22195
22355
  import { homedir as homedir3 } from "node:os";
22196
- import { join as join10, resolve, dirname as dirname5, relative } from "node:path";
22197
- import { mkdir as mkdir4, readFile as readFile2, writeFile as writeFile3, lstat as lstat5, rename as rename6, rm as rm6, cp, open as open6, chmod } from "node:fs/promises";
22198
- import { createHash as createHash3, randomUUID as randomUUID7 } from "node:crypto";
22199
-
22200
- // packages/cli/src/report/claudeHook.ts
22201
- var shellQuote = (text) => `'${text.replace(/'/g, "'\\''")}'`;
22202
-
22203
- // packages/cli/src/report/installer.ts
22204
- var hash3 = (text) => createHash3("sha256").update(text).digest("hex");
22356
+ import { join as join10, resolve, dirname as dirname6 } from "node:path";
22357
+ import { mkdir as mkdir4, readFile as readFile2, writeFile as writeFile3, lstat as lstat5, rename as rename6, rm as rm6, cp, open as open7, chmod } from "node:fs/promises";
22358
+ import { createHash as createHash5, randomUUID as randomUUID7 } from "node:crypto";
22359
+ var hash3 = (text) => createHash5("sha256").update(text).digest("hex");
22205
22360
  var Manifest2 = external_exports.object({ schemaVersion: external_exports.literal(1), buildId: external_exports.string().regex(/^[a-f0-9]{64}$/), setupHash: external_exports.string().regex(/^[a-f0-9]{64}$/).optional(), files: external_exports.record(external_exports.string(), external_exports.string().regex(/^[a-f0-9]{64}$/)) }).strict();
22206
22361
  async function regular(path) {
22207
22362
  const s = await lstat5(path);
@@ -22223,7 +22378,7 @@ async function installDiagnostic(source2, home = homedir3(), options = {}) {
22223
22378
  async function installLocked(source2, home, options) {
22224
22379
  const enroll = typeof options === "function" ? options : options.enrollInvitation ?? enrollInvitation;
22225
22380
  const [major = 0, minor = 0] = process.versions.node.split(".").map(Number);
22226
- if (!(major > LAST_LISTED_MAJOR || MIN_NODE_MINOR[major] !== void 0 && minor >= MIN_NODE_MINOR[major]) || process.platform === "win32") throw Error("UNSUPPORTED_RUNTIME");
22381
+ if (!(major > LAST_LISTED_MAJOR || MIN_NODE_MINOR[major] !== void 0 && minor >= MIN_NODE_MINOR[major])) throw Error("UNSUPPORTED_RUNTIME");
22227
22382
  source2 = resolve(source2);
22228
22383
  try {
22229
22384
  const info = JSON.parse(await readFile2(join10(source2, "build-info.json"), "utf8"));
@@ -22249,6 +22404,7 @@ async function installLocked(source2, home, options) {
22249
22404
  await regular(join10(packageRoot, path));
22250
22405
  if (hash3(await readFile2(join10(packageRoot, path))) !== digest) throw Error("PACKAGE_HASH_MISMATCH");
22251
22406
  }
22407
+ if (process.platform === "win32" && !manifest.files[`helpers/win32-${process.arch}/sberpay-tpm.exe`]) throw Error("WINDOWS_HELPER_NOT_PACKAGED");
22252
22408
  const root2 = privateDir2(join10(home, ".local", "share", "sberpay-diagnostic"));
22253
22409
  const skill = join10(home, ".agents", "skills", "sberpay-diagnostic");
22254
22410
  const markerPath = join10(root2, "installed.json");
@@ -22319,9 +22475,9 @@ async function installLocked(source2, home, options) {
22319
22475
  try {
22320
22476
  for (const [path] of entries) {
22321
22477
  const target = join10(staged, path);
22322
- await mkdir4(dirname5(target), { recursive: true, mode: 448 });
22478
+ await mkdir4(dirname6(target), { recursive: true, mode: 448 });
22323
22479
  await cp(join10(packageRoot, path), target, { force: false, errorOnExist: true });
22324
- await chmod(target, /^helpers\/[^/]+\/sberpay-(se|tpm)$/.test(path) ? 448 : 384);
22480
+ await chmod(target, /^helpers\/[^/]+\/sberpay-(se|tpm)(\.exe)?$/.test(path) ? 448 : 384);
22325
22481
  }
22326
22482
  await rename6(staged, release);
22327
22483
  } finally {
@@ -22337,11 +22493,12 @@ async function installLocked(source2, home, options) {
22337
22493
  if (error62.code !== "ENOENT") throw error62;
22338
22494
  }
22339
22495
  if (hasInvitation) {
22340
- const invitationFile = await open6(join10(source2, "invitation.json"), constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
22496
+ const invitationFile = await open7(join10(source2, "invitation.json"), constants4.O_RDONLY | constants4.O_NOFOLLOW | constants4.O_NONBLOCK);
22341
22497
  try {
22342
22498
  const info = await invitationFile.stat();
22343
22499
  if (!info.isFile() || process.getuid && info.uid !== process.getuid()) throw Error("UNSAFE_INVITATION");
22344
- await invitationFile.chmod(384);
22500
+ if (process.platform === "win32") assertWindowsPrivatePath(join10(source2, "invitation.json"));
22501
+ else await invitationFile.chmod(384);
22345
22502
  } finally {
22346
22503
  await invitationFile.close();
22347
22504
  }
@@ -22385,11 +22542,14 @@ process.env.SBERPAY_DIAGNOSTIC_CAPTURE="1";
22385
22542
  await import(${JSON.stringify(pathToFileURL(join10(release, "bin", "sberpay-cli.mjs")).href)});
22386
22543
  `;
22387
22544
  const original = await readFile2(join10(release, "skill", "sberpay", "SKILL.md"), "utf8");
22388
- const text = original.replaceAll("{{diagnosticLauncher}}", shellQuote(launcher));
22389
- await mkdir4(dirname5(skill), { recursive: true, mode: 448 });
22390
- await mkdir4(skill, { mode: 448 }).catch((e) => {
22391
- if (e.code !== "EEXIST") throw e;
22392
- });
22545
+ const text = renderDiagnosticSkill(original, process.platform, launcher, process.execPath);
22546
+ if (process.platform === "win32") privateDir2(skill);
22547
+ else {
22548
+ await mkdir4(dirname6(skill), { recursive: true, mode: 448 });
22549
+ await mkdir4(skill, { mode: 448 }).catch((e) => {
22550
+ if (e.code !== "EEXIST") throw e;
22551
+ });
22552
+ }
22393
22553
  const managed = [{ path: paymentModeFile, text: JSON.stringify(modeState), mode: 384 }, { path: launcher, text: launcherText, mode: 448 }, { path: join10(skill, "SKILL.md"), text, mode: 384 }];
22394
22554
  for (const [path] of entries) {
22395
22555
  if (!path.startsWith("skill/sberpay/") || path === "skill/sberpay/SKILL.md") continue;
@@ -22420,7 +22580,7 @@ await import(${JSON.stringify(pathToFileURL(join10(release, "bin", "sberpay-cli.
22420
22580
  }
22421
22581
  managed.push({ path: target, text: await readFile2(join10(release, path), "utf8"), mode: 384 });
22422
22582
  }
22423
- const skillFiles = Object.fromEntries(managed.filter((f) => f.path.startsWith(skill + "/")).map((f) => [relative(skill, f.path), hash3(f.text)]));
22583
+ const skillFiles = skillFileHashes(skill, managed);
22424
22584
  const marker = { buildId: manifest.buildId, stateFormat: 2, skillHash: hash3(text), skillFiles, launcher, profile };
22425
22585
  let sender;
22426
22586
  await applyInstallFiles(root2, manifest.buildId, managed, async () => {
@@ -22432,7 +22592,7 @@ await import(${JSON.stringify(pathToFileURL(join10(release, "bin", "sberpay-cli.
22432
22592
  }
22433
22593
 
22434
22594
  // scripts/diagnostics/npm-entry.mts
22435
- var source = dirname6(dirname6(fileURLToPath2(import.meta.url)));
22595
+ var source = dirname7(dirname7(fileURLToPath2(import.meta.url)));
22436
22596
  var root = join11(homedir4(), ".local/share/sberpay-diagnostic");
22437
22597
  try {
22438
22598
  if (process.argv[2] === "setup") {