@pushary/agent-hooks 1.9.0 → 1.9.2

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.
Files changed (105) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
  3. package/data/cursor-plugin/CHANGELOG.md +4 -0
  4. package/data/cursor-plugin/scripts/pushary-gate.mjs +1 -1
  5. package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
  6. package/data/vscode-plugin/CHANGELOG.md +4 -0
  7. package/data/vscode-plugin/scripts/pushary-gate.mjs +1 -1
  8. package/dist/bin/pushary-bell-hook.js +23 -23
  9. package/dist/bin/pushary-bell.js +9 -9
  10. package/dist/bin/pushary-claude.js +26 -26
  11. package/dist/bin/pushary-clean.js +28 -28
  12. package/dist/bin/pushary-codex-bridge.js +18 -18
  13. package/dist/bin/pushary-codex-hook.js +31 -31
  14. package/dist/bin/pushary-codex.js +13 -13
  15. package/dist/bin/pushary-connect.js +13 -13
  16. package/dist/bin/pushary-cowork.js +5 -5
  17. package/dist/bin/pushary-daemon.js +26 -26
  18. package/dist/bin/pushary-disconnect.js +18 -18
  19. package/dist/bin/pushary-doctor.js +99 -48
  20. package/dist/bin/pushary-elicitation-hook.js +25 -25
  21. package/dist/bin/pushary-gemini-bridge.js +13 -13
  22. package/dist/bin/pushary-gemini-hook.js +28 -28
  23. package/dist/bin/pushary-hook.js +32 -32
  24. package/dist/bin/pushary-login.js +26 -23
  25. package/dist/bin/pushary-logout.js +7 -7
  26. package/dist/bin/pushary-mcp.js +9 -9
  27. package/dist/bin/pushary-mode.js +9 -9
  28. package/dist/bin/pushary-notification-hook.js +28 -28
  29. package/dist/bin/pushary-opencode-hook.js +27 -27
  30. package/dist/bin/pushary-permission-denied-hook.js +32 -32
  31. package/dist/bin/pushary-permission-hook.js +31 -31
  32. package/dist/bin/pushary-post-hook.js +28 -28
  33. package/dist/bin/pushary-prompt-hook.js +28 -28
  34. package/dist/bin/pushary-session-end-hook.js +28 -28
  35. package/dist/bin/pushary-session-start-hook.js +31 -31
  36. package/dist/bin/pushary-setup.js +69 -52
  37. package/dist/bin/pushary-stats.js +5 -5
  38. package/dist/bin/pushary-status.js +15 -15
  39. package/dist/bin/pushary-stop-hook.js +28 -28
  40. package/dist/bin/pushary-stopfailure-hook.js +28 -28
  41. package/dist/bin/pushary-suggestions.js +4 -4
  42. package/dist/bin/pushary-transcript-register.js +19 -19
  43. package/dist/bin/pushary-transcripts.js +7 -7
  44. package/dist/bin/pushary-upgrade.js +42 -394
  45. package/dist/bin/pushary-wait.js +9 -9
  46. package/dist/bin/pushary.js +6 -6
  47. package/dist/{chunk-7TQQSQZ7.js → chunk-2E4SRGYD.js} +17 -12
  48. package/dist/{chunk-TUZ7WKOF.js → chunk-2XK6D4WL.js} +1 -1
  49. package/dist/{chunk-42E4DE6C.js → chunk-35N53QLA.js} +10 -9
  50. package/dist/{chunk-VV4SBPCQ.js → chunk-3O4EBTR3.js} +1 -1
  51. package/dist/{chunk-XGS2DHGF.js → chunk-4KMTRXNL.js} +2 -2
  52. package/dist/chunk-6S727YUV.js +242 -0
  53. package/dist/{chunk-K75TP4JY.js → chunk-73BH4SZY.js} +2 -2
  54. package/dist/{chunk-SDSKTRIV.js → chunk-ABIW72ST.js} +1 -1
  55. package/dist/{chunk-HHNFGWR5.js → chunk-AL4NHBZ6.js} +1 -1
  56. package/dist/{chunk-YFS4HYCI.js → chunk-B6BLCHJW.js} +3 -3
  57. package/dist/{chunk-JOQEPWDH.js → chunk-BFVE3WPB.js} +2 -2
  58. package/dist/{chunk-IQUBCAEK.js → chunk-BK3R5NRE.js} +1 -1
  59. package/dist/{chunk-ET6QSY3H.js → chunk-CEHDCHXK.js} +1 -1
  60. package/dist/{chunk-P25O4K3J.js → chunk-COVPHB56.js} +7 -5
  61. package/dist/{chunk-MBVV3RX3.js → chunk-CTYCABBH.js} +1 -1
  62. package/dist/{chunk-RZXQFAWR.js → chunk-DKDZXJHZ.js} +1 -1
  63. package/dist/chunk-EO23EUOK.js +14 -0
  64. package/dist/{chunk-CKPDSFY3.js → chunk-ESME4HN2.js} +5 -5
  65. package/dist/{chunk-NAJ3UV4E.js → chunk-EUNYNLDU.js} +3 -3
  66. package/dist/{chunk-MQJWO3AA.js → chunk-FHLRRCUZ.js} +3 -3
  67. package/dist/{chunk-7LLNRCOK.js → chunk-FWQM6V7Z.js} +1 -1
  68. package/dist/{chunk-CFAD2LTH.js → chunk-HLNSTJND.js} +2 -2
  69. package/dist/{chunk-R76C43H7.js → chunk-HZ57E6LS.js} +84 -50
  70. package/dist/{chunk-ZMVLKPT7.js → chunk-HZYUDXIH.js} +5 -5
  71. package/dist/{chunk-OXQKXZJ3.js → chunk-IH5NZMN4.js} +2 -2
  72. package/dist/{chunk-2V64X5RE.js → chunk-IVPVCCFT.js} +1 -1
  73. package/dist/chunk-JXLANQLV.js +175 -0
  74. package/dist/{chunk-OLG6FJV2.js → chunk-KP45FGHD.js} +4 -4
  75. package/dist/{chunk-EG46RJRF.js → chunk-L52OIZXV.js} +1 -1
  76. package/dist/{chunk-IJJ3HHFT.js → chunk-MZXXLIF6.js} +1 -1
  77. package/dist/{chunk-YJZDIFON.js → chunk-N2ATGXI4.js} +2 -2
  78. package/dist/{chunk-WFVNXGZ6.js → chunk-PN3F7LOB.js} +8 -8
  79. package/dist/{chunk-4ZXYWEEH.js → chunk-Q36G3P7I.js} +1 -1
  80. package/dist/{chunk-XXVMUMKT.js → chunk-QDCEEQUS.js} +5 -5
  81. package/dist/{chunk-VYNTRHB6.js → chunk-RTKPR4UA.js} +1 -1
  82. package/dist/{chunk-MBNMO5IR.js → chunk-SFKU5DXZ.js} +2 -2
  83. package/dist/{chunk-JW7DKTT6.js → chunk-SI327OZF.js} +44 -16
  84. package/dist/{chunk-MGXXQHCM.js → chunk-U4DLXAGD.js} +1 -1
  85. package/dist/{chunk-US3RDXK7.js → chunk-UBCTOE5X.js} +2 -2
  86. package/dist/{chunk-4GMZOGH4.js → chunk-UBNJYMTG.js} +7 -6
  87. package/dist/{chunk-HSM5FIA3.js → chunk-UX2YSFKS.js} +1 -1
  88. package/dist/{chunk-P6VDRQF3.js → chunk-V3P2VSX7.js} +3 -3
  89. package/dist/{chunk-HNMMKFYN.js → chunk-VMGLXL74.js} +10 -10
  90. package/dist/{chunk-LQESQPIT.js → chunk-WCHFDVCC.js} +5 -5
  91. package/dist/{chunk-3OFHFVPX.js → chunk-WM2MEFCA.js} +1 -1
  92. package/dist/{chunk-CPBRXBLG.js → chunk-XOXW4D6P.js} +3 -3
  93. package/dist/{chunk-BG4QCOHQ.js → chunk-XXSAGQAV.js} +4 -4
  94. package/dist/{chunk-HQWPMVR4.js → chunk-Y4AUVOR7.js} +3 -3
  95. package/dist/{chunk-75CEJ4WP.js → chunk-Y6DSV2OS.js} +3 -3
  96. package/dist/{chunk-KETBIX7I.js → chunk-Y73RBCQD.js} +1 -1
  97. package/dist/{chunk-4PCF34AB.js → chunk-ZFDGCEKO.js} +89 -24
  98. package/dist/{chunk-ZRHA4PGN.js → chunk-ZNUNWWCN.js} +4 -2
  99. package/dist/{chunk-K7DY2DQN.js → chunk-ZSOFZDLY.js} +6 -6
  100. package/dist/global-upgrade-N24WINE7.js +24 -0
  101. package/dist/paths-53PCHAOJ.js +71 -0
  102. package/dist/{reapply-FYCVOWCW.js → reapply-2SU2ZHEK.js} +8 -8
  103. package/dist/src/index.js +20 -20
  104. package/dist/upgrade-lock-F6SM6IEZ.js +17 -0
  105. package/package.json +1 -1
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  CLAUDE_HOOK_EVENTS,
3
3
  supportedClaudeEvents
4
- } from "./chunk-YFS4HYCI.js";
4
+ } from "./chunk-B6BLCHJW.js";
5
5
  import {
6
6
  guardBinary,
7
7
  holdBudgetOf,
8
8
  isProtectedByScope,
9
9
  registerHookEntry
10
- } from "./chunk-R76C43H7.js";
10
+ } from "./chunk-HZ57E6LS.js";
11
11
  import {
12
12
  HOOK_LOCATOR_BINARY,
13
13
  withHookHoldBudget
14
- } from "./chunk-4PCF34AB.js";
14
+ } from "./chunk-ZFDGCEKO.js";
15
15
 
16
16
  // src/claude-config.ts
17
17
  import { join } from "path";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NPX_LATEST_COMMAND
3
- } from "./chunk-SDSKTRIV.js";
3
+ } from "./chunk-ABIW72ST.js";
4
4
 
5
5
  // src/setup/codex-trust.ts
6
6
  var CODEX_TRUST_TELEMETRY = {
@@ -10,10 +10,10 @@ import {
10
10
  resolvePackageVersionFromBinary,
11
11
  resolvePathBinary,
12
12
  resolvePathBinaryAsync
13
- } from "./chunk-R76C43H7.js";
13
+ } from "./chunk-HZ57E6LS.js";
14
14
  import {
15
15
  MANAGED_PROVIDERS
16
- } from "./chunk-4PCF34AB.js";
16
+ } from "./chunk-ZFDGCEKO.js";
17
17
 
18
18
  // src/setup/codex-install.ts
19
19
  var NATIVE_BINARY_MIN_BYTES = 20 * 1024 * 1024;
@@ -13,7 +13,45 @@ import {
13
13
  isReleaseVersion,
14
14
  ownPackageName,
15
15
  withHookHoldBudget
16
- } from "./chunk-4PCF34AB.js";
16
+ } from "./chunk-ZFDGCEKO.js";
17
+
18
+ // src/setup/paths.ts
19
+ import { homedir as homedir2 } from "os";
20
+ import { join as join3 } from "path";
21
+
22
+ // src/codex-config.ts
23
+ import { createHash } from "crypto";
24
+ import { homedir } from "os";
25
+ import { join as join2 } from "path";
26
+
27
+ // src/registration-holder.ts
28
+ var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
29
+ var registeredCommands = (document) => {
30
+ const hooks = asRecord(document?.hooks);
31
+ if (!hooks) return [];
32
+ const found = [];
33
+ for (const [event, value] of Object.entries(hooks)) {
34
+ if (!Array.isArray(value)) continue;
35
+ for (const item of value) {
36
+ const inner = asRecord(item)?.hooks;
37
+ if (!Array.isArray(inner)) continue;
38
+ for (const hook of inner) {
39
+ const command = asRecord(hook)?.command;
40
+ if (typeof command === "string") found.push({ event, command });
41
+ }
42
+ }
43
+ }
44
+ return found;
45
+ };
46
+ var wholeSegments = (command) => command.split(/[\s/\\"']+/).map((segment) => segment.replace(/\.(cmd|exe)$/i, ""));
47
+ var namesLocator = (command) => wholeSegments(command).includes(HOOK_LOCATOR_BINARY);
48
+ var namesCliBinary = (command, cliBinaries) => wholeSegments(command).some((segment) => cliBinaries.has(segment));
49
+ var namesOurBinary = (command, cliBinaries) => namesLocator(command) || namesCliBinary(command, cliBinaries);
50
+ var registrationHolderOf = (document, cliBinaries) => {
51
+ const commands = registeredCommands(document);
52
+ if (commands.some(({ command }) => namesLocator(command))) return "app";
53
+ return commands.some(({ command }) => namesCliBinary(command, cliBinaries)) ? "cli" : "none";
54
+ };
17
55
 
18
56
  // src/npm.ts
19
57
  import { promisify } from "util";
@@ -327,19 +365,32 @@ var installGlobally = (options) => {
327
365
  }
328
366
  const started = (async () => {
329
367
  if (!npmIsOnPath()) throw new NpmMissingError();
330
- const platform = process.platform;
331
- const { prefix, packageName } = planGlobalInstall(platform);
332
- const pinned = process.env.PUSHARY_PIN_GLOBAL_VERSION !== "0";
333
- if (pinned && prefix !== void 0 && !needsGlobalInstall(
334
- options.version,
335
- installedVersionOf(prefix, packageName, platform),
336
- globalPackageBinsPresent(packageName, prefix, platform)
337
- )) {
338
- return "skipped";
368
+ const { withUpgradeLock } = await import("./global-upgrade-N24WINE7.js");
369
+ const { installWithUpgradeLock, ownsUpgradeLock } = await import("./upgrade-lock-F6SM6IEZ.js");
370
+ const { autoUpdateLock: autoUpdateLock2 } = await import("./paths-53PCHAOJ.js");
371
+ let result = "skipped";
372
+ const install = async () => {
373
+ const platform = process.platform;
374
+ const { prefix, packageName } = planGlobalInstall(platform);
375
+ const pinned = process.env.PUSHARY_PIN_GLOBAL_VERSION !== "0";
376
+ if (pinned && prefix !== void 0 && !needsGlobalInstall(
377
+ options.version,
378
+ installedVersionOf(prefix, packageName, platform),
379
+ globalPackageBinsPresent(packageName, prefix, platform)
380
+ )) {
381
+ return;
382
+ }
383
+ const version = pinned && options.version && options.version !== "0.0.0" ? options.version : LATEST_TAG;
384
+ installWithUpgradeLock({ packageName, version, timeoutMs: options.timeoutMs ?? 12e4 }, options.lockPath);
385
+ result = "installed";
386
+ };
387
+ if (ownsUpgradeLock(options.lockPath ?? autoUpdateLock2())) {
388
+ await install();
389
+ } else {
390
+ const lock = await withUpgradeLock(install, options);
391
+ if (lock.kind === "refused") throw new Error(lock.message);
339
392
  }
340
- const version = pinned && options.version && options.version !== "0.0.0" ? options.version : LATEST_TAG;
341
- installPackageGlobally({ packageName, version, timeoutMs: options.timeoutMs ?? 12e4 });
342
- return "installed";
393
+ return result;
343
394
  })();
344
395
  installState = started;
345
396
  started.catch(() => {
@@ -397,40 +448,6 @@ var guardedArgv = (target, args = [], platform = process.platform) => {
397
448
  return [shell, ...shellArgs, HOOK_GUARD_SCRIPTS[guardKindOf(target)], path, ...args];
398
449
  };
399
450
 
400
- // src/codex-config.ts
401
- import { createHash } from "crypto";
402
- import { homedir } from "os";
403
- import { join as join2 } from "path";
404
-
405
- // src/registration-holder.ts
406
- var asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
407
- var registeredCommands = (document) => {
408
- const hooks = asRecord(document?.hooks);
409
- if (!hooks) return [];
410
- const found = [];
411
- for (const [event, value] of Object.entries(hooks)) {
412
- if (!Array.isArray(value)) continue;
413
- for (const item of value) {
414
- const inner = asRecord(item)?.hooks;
415
- if (!Array.isArray(inner)) continue;
416
- for (const hook of inner) {
417
- const command = asRecord(hook)?.command;
418
- if (typeof command === "string") found.push({ event, command });
419
- }
420
- }
421
- }
422
- return found;
423
- };
424
- var wholeSegments = (command) => command.split(/[\s/\\"']+/).map((segment) => segment.replace(/\.(cmd|exe)$/i, ""));
425
- var namesLocator = (command) => wholeSegments(command).includes(HOOK_LOCATOR_BINARY);
426
- var namesCliBinary = (command, cliBinaries) => wholeSegments(command).some((segment) => cliBinaries.has(segment));
427
- var namesOurBinary = (command, cliBinaries) => namesLocator(command) || namesCliBinary(command, cliBinaries);
428
- var registrationHolderOf = (document, cliBinaries) => {
429
- const commands = registeredCommands(document);
430
- if (commands.some(({ command }) => namesLocator(command))) return "app";
431
- return commands.some(({ command }) => namesCliBinary(command, cliBinaries)) ? "cli" : "none";
432
- };
433
-
434
451
  // src/hook-entries.ts
435
452
  var registerHookEntry = (existing, isOurs, entry) => {
436
453
  const rewritten = [];
@@ -493,6 +510,7 @@ var CODEX_HOOK_EVENTS = [
493
510
  { event: "PreCompact", matcher: "manual|auto", delivery: "telemetry", timeout: 5 },
494
511
  { event: "PostCompact", matcher: "manual|auto", delivery: "telemetry", timeout: 5 }
495
512
  ];
513
+ var CODEX_REQUIRED_APPROVAL_EVENTS = CODEX_HOOK_EVENTS.filter((event) => event.delivery === "approval").map((event) => event.event);
496
514
  var CODEX_EVENT_KEY = {
497
515
  PermissionRequest: "permission_request",
498
516
  PreToolUse: "pre_tool_use",
@@ -684,11 +702,22 @@ var codexTrustGaps = (config, hooksConfig, hooksJsonPath, command, positions) =>
684
702
  for (const definition of CODEX_HOOK_EVENTS) {
685
703
  const position = positions[definition.event];
686
704
  if (!position) continue;
687
- const stored = asRecord2(state?.[codexHookStateKey(hooksJsonPath, definition.event, position)])?.trusted_hash;
705
+ const hookState = asRecord2(state?.[codexHookStateKey(hooksJsonPath, definition.event, position)]);
706
+ if (definition.delivery === "approval" && hookState?.enabled === false) {
707
+ gaps.push({ event: definition.event, kind: "disabled" });
708
+ continue;
709
+ }
710
+ const stored = hookState?.trusted_hash;
688
711
  const eventKey = CODEX_EVENT_KEY[definition.event] ?? definition.event;
689
712
  const onDisk = asRecord2(
690
713
  asRecord2(hooks?.[definition.event]) ?? (Array.isArray(hooks?.[definition.event]) ? hooks[definition.event][position[0]] : void 0)
691
714
  );
715
+ const handlers = onDisk?.hooks;
716
+ const handler = Array.isArray(handlers) ? asRecord2(handlers[position[1]]) : void 0;
717
+ if (definition.delivery === "approval" && handler?.async === true) {
718
+ gaps.push({ event: definition.event, kind: "asynchronous" });
719
+ continue;
720
+ }
692
721
  const diskHash = onDisk ? codexEntryTrustHash(eventKey, onDisk) : void 0;
693
722
  if (stored !== void 0 && diskHash !== void 0 && stored === diskHash) {
694
723
  if (diskHash !== codexHookTrustHash(definition, command)) {
@@ -759,13 +788,14 @@ var staleTrustKeys = (state, hooksJsonPath, event, currentKey, ourHash) => {
759
788
  };
760
789
 
761
790
  // src/setup/paths.ts
762
- import { homedir as homedir2 } from "os";
763
- import { join as join3 } from "path";
764
791
  var claudeSettings = () => join3(homedir2(), ".claude", "settings.json");
765
792
  var claudeJson = () => join3(homedir2(), ".claude.json");
766
793
  var claudeSkillDir = () => join3(homedir2(), ".claude", "skills", "pushary");
767
794
  var cursorPluginDir = () => join3(homedir2(), ".cursor", "plugins", "local", "pushary");
768
795
  var cursorUserHooks = () => join3(homedir2(), ".cursor", "hooks.json");
796
+ var cursorPluginMcp = () => join3(cursorPluginDir(), "mcp.json");
797
+ var cursorPluginManifest = () => join3(cursorPluginDir(), ".cursor-plugin", "plugin.json");
798
+ var cursorPluginRule = () => join3(cursorPluginDir(), "rules", "pushary.mdc");
769
799
  var cursorUserMcp = () => join3(homedir2(), ".cursor", "mcp.json");
770
800
  var vscodePluginDir = () => join3(homedir2(), ".pushary", "plugins", "vscode");
771
801
  var vscodePluginMcp = () => join3(vscodePluginDir(), ".mcp.json");
@@ -837,6 +867,7 @@ export {
837
867
  codexAgentsMd,
838
868
  codexSkillDir,
839
869
  CODEX_HOOK_EVENTS,
870
+ CODEX_REQUIRED_APPROVAL_EVENTS,
840
871
  addCodexMcpServer,
841
872
  readCodexMcpAuth,
842
873
  isPusharyCodexCommand,
@@ -859,6 +890,9 @@ export {
859
890
  claudeSkillDir,
860
891
  cursorPluginDir,
861
892
  cursorUserHooks,
893
+ cursorPluginMcp,
894
+ cursorPluginManifest,
895
+ cursorPluginRule,
862
896
  cursorUserMcp,
863
897
  vscodePluginDir,
864
898
  vscodePluginMcp,
@@ -1,23 +1,23 @@
1
1
  import {
2
2
  clientErrorKindOf,
3
3
  reportClientError
4
- } from "./chunk-BG4QCOHQ.js";
4
+ } from "./chunk-XXSAGQAV.js";
5
5
  import {
6
6
  SubscriptionRequiredError,
7
7
  callMcpTool
8
- } from "./chunk-CPBRXBLG.js";
8
+ } from "./chunk-XOXW4D6P.js";
9
9
  import {
10
10
  installNetworkDefaults
11
- } from "./chunk-P6VDRQF3.js";
11
+ } from "./chunk-V3P2VSX7.js";
12
12
  import {
13
13
  getBaseUrl
14
- } from "./chunk-EG46RJRF.js";
14
+ } from "./chunk-L52OIZXV.js";
15
15
  import {
16
16
  ASK_AGENT_NAME_MAX,
17
17
  ASK_QUESTION_MAX,
18
18
  DECISION_LINE_MAX,
19
19
  truncateLine
20
- } from "./chunk-4PCF34AB.js";
20
+ } from "./chunk-ZFDGCEKO.js";
21
21
 
22
22
  // src/api.ts
23
23
  import { randomUUID } from "crypto";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  registerTranscript
3
- } from "./chunk-WFVNXGZ6.js";
3
+ } from "./chunk-PN3F7LOB.js";
4
4
  import {
5
5
  writeDegraded
6
- } from "./chunk-BG4QCOHQ.js";
6
+ } from "./chunk-XXSAGQAV.js";
7
7
 
8
8
  // src/tool-hook-input.ts
9
9
  var OPTIONAL_TEXT_FIELDS = ["hook_event_name", "session_id", "cwd", "transcript_path", "tool_use_id", "permission_mode"];
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  describeKeyCheck,
6
6
  keyCheckFromResponse
7
- } from "./chunk-75CEJ4WP.js";
7
+ } from "./chunk-Y6DSV2OS.js";
8
8
  import {
9
9
  EXIT
10
10
  } from "./chunk-KZERVKTD.js";
@@ -0,0 +1,175 @@
1
+ import {
2
+ autoUpdateLock,
3
+ installPackageGlobally,
4
+ npmErrorMessage
5
+ } from "./chunk-HZ57E6LS.js";
6
+ import {
7
+ writeJsonAtomic
8
+ } from "./chunk-T7E75EKA.js";
9
+
10
+ // src/upgrade-lock.ts
11
+ import { fork } from "child_process";
12
+ import { createHash } from "crypto";
13
+ import { existsSync, mkdirSync, readFileSync, realpathSync, unlinkSync } from "fs";
14
+ import { createServer, Server } from "net";
15
+ import { basename, dirname, join } from "path";
16
+ var LOCK_HOST = "127.0.0.1";
17
+ var LOCK_PORT_START = 49152;
18
+ var LOCK_PORT_COUNT = 16384;
19
+ var INHERITED_LOCK_ENV = "PUSHARY_UPGRADE_LOCK";
20
+ var LOCK_READY = "pushary-upgrade-lock-ready";
21
+ var LOCK_HANDLE = "pushary-upgrade-lock-handle";
22
+ var HANDOFF_TIMEOUT_MS = 1e4;
23
+ var FINISH_TIMEOUT_MS = 5 * 60 * 1e3;
24
+ var KERNEL_LOCK_FORMAT = "kernel-v1";
25
+ var heldLocks = /* @__PURE__ */ new Map();
26
+ var canonicalLockPath = (path) => {
27
+ mkdirSync(dirname(path), { recursive: true });
28
+ return join(realpathSync(dirname(path)), basename(path));
29
+ };
30
+ var lockPort = (path) => LOCK_PORT_START + createHash("sha256").update(path).digest().readUInt32BE(0) % LOCK_PORT_COUNT;
31
+ var readLockRecord = (path) => {
32
+ try {
33
+ const value = JSON.parse(readFileSync(path, "utf8"));
34
+ const pid = value && typeof value === "object" && "pid" in value ? value.pid : void 0;
35
+ return {
36
+ pid: typeof pid === "number" && Number.isInteger(pid) && pid > 0 ? pid : void 0,
37
+ installationPending: value !== null && typeof value === "object" && "installationPending" in value && value.installationPending === true,
38
+ usesKernelLock: value !== null && typeof value === "object" && "format" in value && value.format === KERNEL_LOCK_FORMAT
39
+ };
40
+ } catch {
41
+ return { pid: void 0, usesKernelLock: false, installationPending: false };
42
+ }
43
+ };
44
+ var isProcessAlive = (pid) => {
45
+ try {
46
+ process.kill(pid, 0);
47
+ return true;
48
+ } catch (error) {
49
+ return !(error instanceof Error && "code" in error && error.code === "ESRCH");
50
+ }
51
+ };
52
+ var rejectConnection = (socket) => {
53
+ socket.destroy();
54
+ };
55
+ var inheritLock = (path) => new Promise((resolve) => {
56
+ const done = (claim) => {
57
+ clearTimeout(timer);
58
+ process.off("message", receive);
59
+ process.off("disconnect", disconnected);
60
+ resolve(claim);
61
+ };
62
+ const disconnected = () => done({ kind: "unavailable", reason: "Upgrade parent disconnected before transferring its lock" });
63
+ const receive = (message, handle) => {
64
+ if (message !== LOCK_HANDLE || !(handle instanceof Server)) return;
65
+ const address = handle.address();
66
+ if (address === null || typeof address === "string" || address.port !== lockPort(path) || address.address !== LOCK_HOST) {
67
+ handle.close();
68
+ done({ kind: "unavailable", reason: "Invalid inherited upgrade lock" });
69
+ return;
70
+ }
71
+ handle.on("connection", rejectConnection);
72
+ heldLocks.set(path, handle);
73
+ done({ kind: "inherited" });
74
+ process.disconnect?.();
75
+ };
76
+ const timer = setTimeout(() => done({ kind: "unavailable", reason: "Upgrade lock handoff timed out" }), HANDOFF_TIMEOUT_MS);
77
+ process.on("message", receive);
78
+ process.once("disconnect", disconnected);
79
+ if (!process.send || !process.connected) return disconnected();
80
+ process.send(LOCK_READY, (error) => {
81
+ if (error) disconnected();
82
+ });
83
+ });
84
+ var claimUpgradeLock = async (request = {}) => {
85
+ let server;
86
+ try {
87
+ const path = canonicalLockPath(request.lockPath ?? autoUpdateLock());
88
+ const inheritedPath = process.env[INHERITED_LOCK_ENV];
89
+ delete process.env[INHERITED_LOCK_ENV];
90
+ if (inheritedPath === path && !heldLocks.has(path)) return await inheritLock(path);
91
+ server = createServer(rejectConnection);
92
+ const listener = server;
93
+ await new Promise((resolve, reject) => {
94
+ listener.once("error", reject);
95
+ listener.listen({ host: LOCK_HOST, port: lockPort(path), exclusive: true }, resolve);
96
+ });
97
+ const record = readLockRecord(path);
98
+ if (record.installationPending) {
99
+ server.close();
100
+ return { kind: "unavailable", reason: `An earlier installation did not finish safely. Stop its npm processes (or restart the computer), then remove ${path} and retry the upgrade.` };
101
+ }
102
+ if (existsSync(path) && !record.usesKernelLock && (record.pid === void 0 || (request.isAlive ?? isProcessAlive)(record.pid))) {
103
+ server.close();
104
+ return { kind: "held", holderPid: record.pid };
105
+ }
106
+ writeJsonAtomic(path, { pid: process.pid, format: KERNEL_LOCK_FORMAT }, 384);
107
+ heldLocks.set(path, server);
108
+ return { kind: "acquired" };
109
+ } catch (error) {
110
+ server?.close();
111
+ if (error instanceof Error && "code" in error && error.code === "EADDRINUSE") {
112
+ return { kind: "held", holderPid: readLockRecord(request.lockPath ?? autoUpdateLock()).pid };
113
+ }
114
+ return { kind: "unavailable", reason: npmErrorMessage(error) };
115
+ }
116
+ };
117
+ var ownsUpgradeLock = (lockPath) => {
118
+ try {
119
+ return heldLocks.has(canonicalLockPath(lockPath));
120
+ } catch {
121
+ return false;
122
+ }
123
+ };
124
+ var releaseAutoUpdateLock = (lockPath) => {
125
+ const path = canonicalLockPath(lockPath);
126
+ const server = heldLocks.get(path);
127
+ if (!server) return;
128
+ heldLocks.delete(path);
129
+ try {
130
+ const record = readLockRecord(path);
131
+ if (record.pid === process.pid && !record.installationPending) unlinkSync(path);
132
+ } finally {
133
+ server.close();
134
+ }
135
+ };
136
+ var finishWithUpgradeLock = (request) => new Promise((resolve, reject) => {
137
+ const path = canonicalLockPath(request.lockPath ?? autoUpdateLock());
138
+ const server = heldLocks.get(path);
139
+ if (!server) return reject(new Error("Cannot finish an upgrade without its lock"));
140
+ const child = fork(request.entry, [...request.args], {
141
+ execArgv: [],
142
+ stdio: request.stdio ?? ["inherit", "inherit", "inherit", "ipc"],
143
+ env: { ...process.env, ...request.env, [INHERITED_LOCK_ENV]: path }
144
+ });
145
+ const timer = setTimeout(() => child.kill("SIGKILL"), FINISH_TIMEOUT_MS);
146
+ child.on("message", (message) => {
147
+ if (message === LOCK_READY) child.send(LOCK_HANDLE, server, (error) => {
148
+ if (error) child.kill();
149
+ });
150
+ });
151
+ child.once("error", (error) => {
152
+ clearTimeout(timer);
153
+ reject(error);
154
+ });
155
+ child.once("exit", (code, signal) => {
156
+ clearTimeout(timer);
157
+ if (code === 0) resolve();
158
+ else reject(new Error(`Upgrade finish exited with ${signal ?? code}`));
159
+ });
160
+ });
161
+ var installWithUpgradeLock = (request, lockPath = autoUpdateLock(), runNpm) => {
162
+ const path = canonicalLockPath(lockPath);
163
+ if (!heldLocks.has(path)) throw new Error("Cannot install an upgrade without its lock");
164
+ writeJsonAtomic(path, { pid: process.pid, format: KERNEL_LOCK_FORMAT, installationPending: true }, 384);
165
+ installPackageGlobally(request, runNpm);
166
+ writeJsonAtomic(path, { pid: process.pid, format: KERNEL_LOCK_FORMAT }, 384);
167
+ };
168
+
169
+ export {
170
+ claimUpgradeLock,
171
+ ownsUpgradeLock,
172
+ releaseAutoUpdateLock,
173
+ finishWithUpgradeLock,
174
+ installWithUpgradeLock
175
+ };
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  AGENT_CHOICES
3
- } from "./chunk-ET6QSY3H.js";
3
+ } from "./chunk-CEHDCHXK.js";
4
4
  import {
5
5
  isBridgeLocator
6
- } from "./chunk-TUZ7WKOF.js";
6
+ } from "./chunk-2XK6D4WL.js";
7
7
  import {
8
8
  pusharyHookExecutable
9
- } from "./chunk-NAJ3UV4E.js";
9
+ } from "./chunk-EUNYNLDU.js";
10
10
  import {
11
11
  BELL_HOOK_BINARY
12
- } from "./chunk-RZXQFAWR.js";
12
+ } from "./chunk-DKDZXJHZ.js";
13
13
 
14
14
  // src/setup/hook-ownership.ts
15
15
  var commandsIn = (value) => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NPX_LATEST_COMMAND
3
- } from "./chunk-SDSKTRIV.js";
3
+ } from "./chunk-ABIW72ST.js";
4
4
 
5
5
  // src/config.ts
6
6
  import { readFileSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isSafeReadOnlyCommand
3
- } from "./chunk-4PCF34AB.js";
3
+ } from "./chunk-ZFDGCEKO.js";
4
4
 
5
5
  // src/ledger.ts
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync } from "fs";
@@ -5,7 +5,7 @@ import {
5
5
  permissionDeniedAction,
6
6
  releaseThrottle,
7
7
  reportEvent
8
- } from "./chunk-42E4DE6C.js";
8
+ } from "./chunk-35N53QLA.js";
9
9
  import {
10
10
  DEFAULT_SESSION
11
11
  } from "./chunk-N6IOSZOE.js";
@@ -13,7 +13,7 @@ import {
13
13
  machineDecisionFor,
14
14
  shouldDeferToNativeMode,
15
15
  wireVerdictFor
16
- } from "./chunk-4PCF34AB.js";
16
+ } from "./chunk-ZFDGCEKO.js";
17
17
 
18
18
  // src/machine-decision-audit.ts
19
19
  import { basename } from "path";
@@ -6,38 +6,38 @@ import {
6
6
  } from "./chunk-SGZJ5LYH.js";
7
7
  import {
8
8
  hookOwnerOf
9
- } from "./chunk-OLG6FJV2.js";
9
+ } from "./chunk-KP45FGHD.js";
10
10
  import {
11
11
  ensureDaemonRunning
12
- } from "./chunk-LQESQPIT.js";
12
+ } from "./chunk-WCHFDVCC.js";
13
13
  import {
14
14
  buildTranscriptRecords,
15
15
  decodePublicKey,
16
16
  generateSessionKey,
17
17
  wrapSessionKey
18
- } from "./chunk-MGXXQHCM.js";
18
+ } from "./chunk-U4DLXAGD.js";
19
19
  import {
20
20
  fetchTranscriptRecipients,
21
21
  uploadTranscriptRecords
22
- } from "./chunk-HSM5FIA3.js";
22
+ } from "./chunk-UX2YSFKS.js";
23
23
  import {
24
24
  transcriptEnvironmentPreference,
25
25
  transcriptsEnabled
26
- } from "./chunk-MBVV3RX3.js";
26
+ } from "./chunk-CTYCABBH.js";
27
27
  import {
28
28
  readTranscriptPreference
29
- } from "./chunk-ZRHA4PGN.js";
29
+ } from "./chunk-ZNUNWWCN.js";
30
30
  import {
31
31
  codexHome,
32
32
  pusharyDir
33
- } from "./chunk-R76C43H7.js";
33
+ } from "./chunk-HZ57E6LS.js";
34
34
  import {
35
35
  writeJsonAtomic
36
36
  } from "./chunk-T7E75EKA.js";
37
37
  import {
38
38
  getApiKey,
39
39
  getBaseUrl
40
- } from "./chunk-EG46RJRF.js";
40
+ } from "./chunk-L52OIZXV.js";
41
41
 
42
42
  // src/transcript-registration.ts
43
43
  import { createHash } from "crypto";
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-3EGEA4KH.js";
10
10
  import {
11
11
  getBaseUrl
12
- } from "./chunk-EG46RJRF.js";
12
+ } from "./chunk-L52OIZXV.js";
13
13
 
14
14
  // src/cli/login-ui.ts
15
15
  import { hostname } from "os";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  ensureCodexTrustsPusharyHooks
3
- } from "./chunk-JW7DKTT6.js";
3
+ } from "./chunk-SI327OZF.js";
4
4
  import {
5
5
  addClaudeMcpServer,
6
6
  addPusharyHooks,
7
7
  addPusharyToolPermissions
8
- } from "./chunk-MQJWO3AA.js";
8
+ } from "./chunk-FHLRRCUZ.js";
9
9
  import {
10
10
  detectClaudeVersion
11
11
  } from "./chunk-RIVNNBEK.js";
@@ -15,7 +15,7 @@ import {
15
15
  codexUnprovenHolds,
16
16
  codexWired,
17
17
  geminiWired
18
- } from "./chunk-NAJ3UV4E.js";
18
+ } from "./chunk-EUNYNLDU.js";
19
19
  import {
20
20
  CLAUDE_HOOK_BINARY,
21
21
  CLAUDE_HOOK_EVENTS,
@@ -26,7 +26,7 @@ import {
26
26
  needsReregistration,
27
27
  registrationSignatures,
28
28
  supportedClaudeEvents
29
- } from "./chunk-YFS4HYCI.js";
29
+ } from "./chunk-B6BLCHJW.js";
30
30
  import {
31
31
  formatVersion
32
32
  } from "./chunk-6ICPEJY5.js";
@@ -49,7 +49,7 @@ import {
49
49
  registrationHolderOf,
50
50
  resolveGlobalBinary,
51
51
  vscodePluginMcp
52
- } from "./chunk-R76C43H7.js";
52
+ } from "./chunk-HZ57E6LS.js";
53
53
  import {
54
54
  readJsonSafe,
55
55
  writeFileAtomic,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  PRETOOLUSE_GATED_TOOLS
3
- } from "./chunk-4PCF34AB.js";
3
+ } from "./chunk-ZFDGCEKO.js";
4
4
 
5
5
  // src/claude-tools.ts
6
6
  var POSTTOOLUSE_MIRROR_ONLY_TOOLS = ["TodoWrite", "Task", "WebFetch", "WebSearch"];
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  parseSseMessages
3
- } from "./chunk-CPBRXBLG.js";
3
+ } from "./chunk-XOXW4D6P.js";
4
4
  import {
5
5
  NPX_LATEST_COMMAND
6
- } from "./chunk-SDSKTRIV.js";
6
+ } from "./chunk-ABIW72ST.js";
7
7
 
8
8
  // src/mcp-stdio-bridge.ts
9
9
  var MCP_BRIDGE_BINARY = "pushary-mcp";