@pushary/agent-hooks 1.9.65 → 1.9.67

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 (129) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/data/SKILL.md +1 -1
  3. package/data/cursor-plugin/skills/pushary/SKILL.md +1 -1
  4. package/data/vscode-plugin/skills/pushary/SKILL.md +1 -1
  5. package/dist/{api-32B3XLJR.js → api-3WIPD5GC.js} +12 -10
  6. package/dist/bin/pushary-bell-hook.js +25 -27
  7. package/dist/bin/pushary-bell.js +10 -10
  8. package/dist/bin/pushary-claude.js +26 -28
  9. package/dist/bin/pushary-clean.js +74 -77
  10. package/dist/bin/pushary-codex-bridge.js +21 -22
  11. package/dist/bin/pushary-codex-hook.js +30 -32
  12. package/dist/bin/pushary-codex-session.js +8 -8
  13. package/dist/bin/pushary-codex.js +13 -13
  14. package/dist/bin/pushary-connect.js +28 -28
  15. package/dist/bin/pushary-cowork.js +5 -5
  16. package/dist/bin/pushary-daemon.js +40 -41
  17. package/dist/bin/pushary-disconnect.js +25 -26
  18. package/dist/bin/pushary-doctor.js +118 -76
  19. package/dist/bin/pushary-elicitation-hook.js +25 -27
  20. package/dist/bin/pushary-gemini-bridge.js +15 -16
  21. package/dist/bin/pushary-gemini-hook.js +27 -29
  22. package/dist/bin/pushary-hook.js +29 -31
  23. package/dist/bin/pushary-login.js +20 -19
  24. package/dist/bin/pushary-logout.js +26 -28
  25. package/dist/bin/pushary-mcp.js +11 -11
  26. package/dist/bin/pushary-mode.js +16 -15
  27. package/dist/bin/pushary-notification-hook.js +24 -26
  28. package/dist/bin/pushary-opencode-hook.js +26 -28
  29. package/dist/bin/pushary-permission-denied-hook.js +29 -31
  30. package/dist/bin/pushary-permission-hook.js +32 -31
  31. package/dist/bin/pushary-post-hook.js +24 -26
  32. package/dist/bin/pushary-prompt-hook.js +24 -26
  33. package/dist/bin/pushary-session-end-hook.js +26 -28
  34. package/dist/bin/pushary-session-start-hook.js +34 -35
  35. package/dist/bin/pushary-setup.js +111 -99
  36. package/dist/bin/pushary-stats.js +5 -5
  37. package/dist/bin/pushary-status.js +37 -36
  38. package/dist/bin/pushary-stop-hook.js +24 -26
  39. package/dist/bin/pushary-stopfailure-hook.js +24 -26
  40. package/dist/bin/pushary-suggestions.js +15 -14
  41. package/dist/bin/pushary-transcript-register.js +16 -18
  42. package/dist/bin/pushary-transcripts.js +18 -17
  43. package/dist/bin/pushary-upgrade.js +45 -39
  44. package/dist/bin/pushary-wait.js +26 -20
  45. package/dist/bin/pushary.js +7 -7
  46. package/dist/{chunk-Y64JGYTB.js → chunk-32OSSYGZ.js} +1 -1
  47. package/dist/{chunk-UHS5MMAI.js → chunk-3THDNNVL.js} +3 -3
  48. package/dist/{chunk-OA6EADFT.js → chunk-564IPPBL.js} +3 -3
  49. package/dist/chunk-5MOJCOCF.js +25 -0
  50. package/dist/{chunk-RRHHWYPX.js → chunk-6H4E5UBA.js} +6 -6
  51. package/dist/{chunk-UM2X7HL6.js → chunk-6LXXKMV6.js} +42 -16
  52. package/dist/chunk-76SWHCAR.js +299 -0
  53. package/dist/{chunk-AN7C3CHH.js → chunk-77L2TNJW.js} +1 -1
  54. package/dist/{chunk-XJIIU36L.js → chunk-7YJSDXLM.js} +1 -1
  55. package/dist/{chunk-XZCJC3QO.js → chunk-B3V2WOJX.js} +285 -50
  56. package/dist/{chunk-RABLE5R6.js → chunk-BBFGDYYI.js} +1 -1
  57. package/dist/{chunk-GTBWWCCM.js → chunk-BFF3ABFV.js} +115 -9
  58. package/dist/{chunk-NOH3EJFH.js → chunk-C4EDAHXI.js} +1 -1
  59. package/dist/{chunk-ITYKN2K3.js → chunk-CHYBOTQN.js} +2 -2
  60. package/dist/{chunk-FFSIYBBU.js → chunk-CJZLIAJF.js} +27 -10
  61. package/dist/{chunk-UMTMFDL2.js → chunk-CPLQ5OHX.js} +1 -1
  62. package/dist/{chunk-DTVHZDYM.js → chunk-D3U6CN7T.js} +26 -12
  63. package/dist/{chunk-ICFHMJTW.js → chunk-D7Z4USSB.js} +27 -8
  64. package/dist/{chunk-DXMUJTYF.js → chunk-DP6LU6BB.js} +2 -2
  65. package/dist/{chunk-GA257T2E.js → chunk-DXCHIKAN.js} +8 -8
  66. package/dist/{chunk-PP7FC3Y7.js → chunk-EQR6JSFE.js} +9 -7
  67. package/dist/{chunk-Z3M627TQ.js → chunk-FONWVVDD.js} +1 -1
  68. package/dist/{chunk-3KXBRSL3.js → chunk-GMHQYX3D.js} +1 -1
  69. package/dist/{chunk-WY6YAMZW.js → chunk-ICQCQAUO.js} +5 -4
  70. package/dist/{chunk-CZ65D6Q5.js → chunk-IPG3RS23.js} +2 -2
  71. package/dist/{chunk-JCWHOB36.js → chunk-IRV2MNMA.js} +53 -3
  72. package/dist/{chunk-HIYLAC3C.js → chunk-IVB5O6NY.js} +3 -3
  73. package/dist/{chunk-DM24VZOI.js → chunk-IXTVUINH.js} +3 -3
  74. package/dist/{chunk-BEQ3MEE3.js → chunk-JBRMIADM.js} +2 -2
  75. package/dist/{chunk-CU2PXLZ6.js → chunk-JHB5SR5B.js} +4 -4
  76. package/dist/{chunk-ED2SWTPC.js → chunk-JOBSKQ2Z.js} +4 -4
  77. package/dist/{chunk-W5L2ZLHE.js → chunk-JOD2325S.js} +3 -3
  78. package/dist/{chunk-CSKABVSH.js → chunk-JSPCGPSH.js} +3 -3
  79. package/dist/{chunk-R4FL5JHK.js → chunk-KE4TSJUD.js} +4 -4
  80. package/dist/{chunk-3LF7T3NW.js → chunk-KIXRHNCW.js} +6 -6
  81. package/dist/{chunk-BKCI53W6.js → chunk-KNUYUQBP.js} +4 -4
  82. package/dist/{chunk-4LUWNBGD.js → chunk-KTLE7NIK.js} +23 -8
  83. package/dist/{chunk-ADXHPGBZ.js → chunk-LJHDUKBB.js} +6 -6
  84. package/dist/{chunk-U2P2LQ5B.js → chunk-LS42NV6E.js} +15 -6
  85. package/dist/{chunk-QNZ6G3LY.js → chunk-LYTWK5TF.js} +5 -5
  86. package/dist/{chunk-2VADJ6JH.js → chunk-METUT7V4.js} +2 -2
  87. package/dist/{chunk-P5HZEGT7.js → chunk-NSS2AIGI.js} +6 -6
  88. package/dist/{chunk-QSW3HAKI.js → chunk-NWN6GGJL.js} +61 -2
  89. package/dist/{chunk-7ILQX47W.js → chunk-P4RXSZDT.js} +3 -3
  90. package/dist/{chunk-FWS2KLV2.js → chunk-PIBWX3EF.js} +2 -2
  91. package/dist/{chunk-F56GTJFG.js → chunk-PO2P3XDC.js} +5 -5
  92. package/dist/{chunk-EEVE7H5Z.js → chunk-R2BZH2NK.js} +4 -4
  93. package/dist/{chunk-I2UBF2SS.js → chunk-RO2B4YBF.js} +1 -1
  94. package/dist/{chunk-MNKIR2ZB.js → chunk-RQOT6QL3.js} +67 -6
  95. package/dist/{chunk-Q2BD4IBX.js → chunk-SB6TYV4M.js} +11 -13
  96. package/dist/{chunk-ATPJGHUP.js → chunk-TI4JYPGQ.js} +7 -3
  97. package/dist/{chunk-LMC5UPL7.js → chunk-UC2J5RR4.js} +1 -1
  98. package/dist/{chunk-IZVEBBDH.js → chunk-UQIPWXBL.js} +3 -3
  99. package/dist/{chunk-VHKK3US3.js → chunk-UXN73YAE.js} +3 -3
  100. package/dist/{chunk-WOHP7EL6.js → chunk-VEYFXRZ6.js} +7 -6
  101. package/dist/{chunk-2Z7I7DHT.js → chunk-VHMZPN3N.js} +1 -1
  102. package/dist/{chunk-HVOQUPSO.js → chunk-VKZ3RJ5U.js} +1 -1
  103. package/dist/chunk-VVIQWGS7.js +2298 -0
  104. package/dist/{chunk-SCMEVDAR.js → chunk-WJLHZ6E6.js} +3 -3
  105. package/dist/{chunk-BAGBCSXA.js → chunk-X2MTVD4W.js} +1 -1
  106. package/dist/{chunk-3UNAPXED.js → chunk-XFDWLDHG.js} +9 -9
  107. package/dist/{chunk-6CZX6YOE.js → chunk-XNUWKKGS.js} +3 -3
  108. package/dist/{chunk-NUEPJ5W6.js → chunk-XZKSUJ55.js} +3 -5
  109. package/dist/{chunk-HDGB6SQ4.js → chunk-Y4MRHAE5.js} +2 -2
  110. package/dist/{chunk-SZBMTOUZ.js → chunk-Y6ZN454E.js} +1 -1
  111. package/dist/{chunk-TK4JU2F7.js → chunk-YSD4SMHE.js} +1 -1
  112. package/dist/{chunk-SROWUG5W.js → chunk-ZK5WYAMI.js} +1 -1
  113. package/dist/{chunk-JFKMPLNF.js → chunk-ZPIYJLQ3.js} +2 -2
  114. package/dist/{codex-app-cards-switch-HK4RMOBI.js → codex-app-cards-switch-25TDTMUZ.js} +11 -11
  115. package/dist/{codex-desktop-launch-OCO6N7SA.js → codex-desktop-launch-JDKUCD2Y.js} +1 -1
  116. package/dist/{codex-session-launch-ETZQTVFS.js → codex-session-launch-6N35AAEO.js} +18 -19
  117. package/dist/{config-YY54G3CC.js → config-OBGYURFX.js} +3 -3
  118. package/dist/{global-upgrade-5DN5G3ZQ.js → global-upgrade-2QNQD5OM.js} +10 -10
  119. package/dist/{identity-EGL6HAWG.js → identity-CUPX2U7Q.js} +4 -4
  120. package/dist/{live-capabilities-WU3FYJJG.js → live-capabilities-CUEIWMSM.js} +5 -5
  121. package/dist/{paths-ZG6KDM6Y.js → paths-BHLBZ6FY.js} +7 -3
  122. package/dist/{reapply-AQLNCCTE.js → reapply-4GXLWZ5M.js} +27 -27
  123. package/dist/src/index.js +30 -20
  124. package/dist/{upgrade-lock-BIRXIKHJ.js → upgrade-lock-RDKTAHWR.js} +4 -4
  125. package/dist/{usage-43C2DHDU.js → usage-KX2HN47E.js} +3 -3
  126. package/package.json +1 -1
  127. package/dist/chunk-ANP3DIST.js +0 -118
  128. package/dist/chunk-ONBL2GCU.js +0 -615
  129. package/dist/chunk-TJUJAK5L.js +0 -62
@@ -1,25 +1,24 @@
1
- import {
2
- GEMINI_HOOK_EVENTS,
3
- hasGeminiHooks
4
- } from "./chunk-ANP3DIST.js";
5
1
  import {
6
2
  AGENT_CHOICES
7
- } from "./chunk-SROWUG5W.js";
3
+ } from "./chunk-ZK5WYAMI.js";
8
4
  import {
9
5
  BELL_HOOK_BINARY
10
- } from "./chunk-Z3M627TQ.js";
6
+ } from "./chunk-FONWVVDD.js";
11
7
  import {
12
8
  CLAUDE_HOOK_EVENTS,
13
9
  CODEX_HOOK_EVENTS,
14
10
  HOOK_SHELL,
15
11
  hasCodexHooks,
16
- holdBudgetOf
17
- } from "./chunk-ATPJGHUP.js";
12
+ holdBudgetOf,
13
+ isProtectedByScope,
14
+ registerHookEntry
15
+ } from "./chunk-TI4JYPGQ.js";
18
16
  import {
19
17
  CURSOR_GATE_EVENTS,
18
+ HOOK_BUDGETS,
20
19
  HOOK_LOCATOR_BINARY,
21
20
  hookCommandHoldBudget
22
- } from "./chunk-MNKIR2ZB.js";
21
+ } from "./chunk-RQOT6QL3.js";
23
22
 
24
23
  // src/diagnostics/bridge.ts
25
24
  import { execFileSync } from "child_process";
@@ -97,6 +96,105 @@ var liveBridgeProbe = () => ({
97
96
  home: homedir()
98
97
  });
99
98
 
99
+ // src/gemini-config.ts
100
+ var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
101
+ var GEMINI_MCP_URL = "https://pushary.com/api/mcp/mcp";
102
+ var GEMINI_HOOK_EVENTS = [
103
+ { event: "BeforeTool", matcher: "run_shell_command|write_file|replace|mcp_.*", delivery: "approval", timeoutMs: HOOK_BUDGETS.gemini.budgetSeconds * 1e3 },
104
+ { event: "AfterTool", matcher: "run_shell_command|write_file|replace|mcp_.*", delivery: "telemetry", timeoutMs: 1e4 },
105
+ { event: "BeforeAgent", delivery: "telemetry", timeoutMs: 1e4 },
106
+ { event: "AfterAgent", delivery: "telemetry", timeoutMs: 1e4 },
107
+ { event: "SessionStart", delivery: "telemetry", timeoutMs: 1e4 },
108
+ { event: "SessionEnd", delivery: "telemetry", timeoutMs: 1e4 }
109
+ ];
110
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
111
+ var ensureRecord = (target, key) => {
112
+ const existing = asRecord(target[key]);
113
+ if (existing) return existing;
114
+ const created = {};
115
+ target[key] = created;
116
+ return created;
117
+ };
118
+ var isPusharyGeminiHook = (entry, scope) => {
119
+ const hooks = asRecord(entry)?.hooks;
120
+ if (!Array.isArray(hooks)) return false;
121
+ return hooks.some((hook) => {
122
+ const command = String(asRecord(hook)?.command ?? "");
123
+ if (isProtectedByScope(command, scope)) return false;
124
+ return command.includes(GEMINI_HOOK_BINARY) || command.includes(HOOK_LOCATOR_BINARY);
125
+ });
126
+ };
127
+ var addGeminiMcpServer = (settings, apiKey) => {
128
+ const mcpServers = ensureRecord(settings, "mcpServers");
129
+ mcpServers.pushary = {
130
+ httpUrl: GEMINI_MCP_URL,
131
+ headers: { Authorization: `Bearer ${apiKey}` },
132
+ trust: true
133
+ };
134
+ };
135
+ var addGeminiHooks = (settings, command) => {
136
+ const hooks = ensureRecord(settings, "hooks");
137
+ for (const definition of GEMINI_HOOK_EVENTS) {
138
+ const existing = Array.isArray(hooks[definition.event]) ? hooks[definition.event] : [];
139
+ hooks[definition.event] = registerHookEntry(existing, isPusharyGeminiHook, {
140
+ ...definition.matcher ? { matcher: definition.matcher } : {},
141
+ hooks: [{
142
+ name: "pushary",
143
+ type: "command",
144
+ command,
145
+ timeout: definition.timeoutMs
146
+ }]
147
+ });
148
+ }
149
+ };
150
+ var removeGeminiHooks = (settings, scope) => {
151
+ const hooks = asRecord(settings.hooks);
152
+ if (!hooks) return false;
153
+ let changed = false;
154
+ for (const definition of GEMINI_HOOK_EVENTS) {
155
+ const entries = hooks[definition.event];
156
+ if (!Array.isArray(entries)) continue;
157
+ const filtered = entries.filter((entry) => !isPusharyGeminiHook(entry, scope));
158
+ if (filtered.length !== entries.length) {
159
+ if (filtered.length === 0) {
160
+ delete hooks[definition.event];
161
+ } else {
162
+ hooks[definition.event] = filtered;
163
+ }
164
+ changed = true;
165
+ }
166
+ }
167
+ if (Object.keys(hooks).length === 0) delete settings.hooks;
168
+ return changed;
169
+ };
170
+ var removeGeminiMcpServer = (settings) => {
171
+ const mcpServers = asRecord(settings.mcpServers);
172
+ if (!mcpServers?.pushary) return false;
173
+ delete mcpServers.pushary;
174
+ if (Object.keys(mcpServers).length === 0) delete settings.mcpServers;
175
+ return true;
176
+ };
177
+ var removeGeminiSettings = (settings, scope) => {
178
+ const mcpRemoved = scope?.keepMcpServer === true ? false : removeGeminiMcpServer(settings);
179
+ const hooksRemoved = removeGeminiHooks(settings, scope);
180
+ return mcpRemoved || hooksRemoved;
181
+ };
182
+ var hasGeminiHooks = (settings) => {
183
+ const hooks = asRecord(settings.hooks);
184
+ if (!hooks) return false;
185
+ return GEMINI_HOOK_EVENTS.some((definition) => {
186
+ const entries = hooks[definition.event];
187
+ return Array.isArray(entries) && entries.some((entry) => isPusharyGeminiHook(entry));
188
+ });
189
+ };
190
+ var missingGeminiHookEvents = (settings) => {
191
+ const hooks = asRecord(settings.hooks);
192
+ return GEMINI_HOOK_EVENTS.filter((definition) => {
193
+ const entries = hooks?.[definition.event];
194
+ return !Array.isArray(entries) || !entries.some((entry) => isPusharyGeminiHook(entry));
195
+ }).map((definition) => definition.event);
196
+ };
197
+
100
198
  // src/diagnostics/wiring.ts
101
199
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
102
200
  var CLAUDE_HOOK_BINARY = /pushary-[a-z-]*hook/;
@@ -326,6 +424,14 @@ export {
326
424
  describeBridgeResolution,
327
425
  isBridgeLocator,
328
426
  liveBridgeProbe,
427
+ GEMINI_HOOK_BINARY,
428
+ GEMINI_HOOK_EVENTS,
429
+ addGeminiMcpServer,
430
+ addGeminiHooks,
431
+ removeGeminiMcpServer,
432
+ removeGeminiSettings,
433
+ hasGeminiHooks,
434
+ missingGeminiHookEvents,
329
435
  pusharyHookExecutable,
330
436
  claudeWired,
331
437
  codexWired,
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-3EGEA4KH.js";
10
10
  import {
11
11
  getBaseUrl
12
- } from "./chunk-Y64JGYTB.js";
12
+ } from "./chunk-32OSSYGZ.js";
13
13
 
14
14
  // src/cli/login-ui.ts
15
15
  import { hostname } from "os";
@@ -4,10 +4,10 @@ import {
4
4
  keyPrefix,
5
5
  readConfigFileKey,
6
6
  readMacAppKeyPrefix
7
- } from "./chunk-ED2SWTPC.js";
7
+ } from "./chunk-JOBSKQ2Z.js";
8
8
  import {
9
9
  NPX_LATEST_COMMAND
10
- } from "./chunk-BAGBCSXA.js";
10
+ } from "./chunk-X2MTVD4W.js";
11
11
 
12
12
  // src/key-signed-out.ts
13
13
  var VERDICT_BY_CHECK = {
@@ -2,25 +2,31 @@ import {
2
2
  SIGNED_OUT_ON_MAC_MESSAGE,
3
3
  describeSignedOutKey,
4
4
  readSignedOutDiagnosis
5
- } from "./chunk-ITYKN2K3.js";
5
+ } from "./chunk-CHYBOTQN.js";
6
+ import {
7
+ APPROVAL_RULES_PATH
8
+ } from "./chunk-5MOJCOCF.js";
6
9
  import {
7
10
  createIo
8
11
  } from "./chunk-FO3MDD26.js";
9
12
  import {
10
13
  readKeySource
11
- } from "./chunk-ED2SWTPC.js";
14
+ } from "./chunk-JOBSKQ2Z.js";
12
15
  import {
13
16
  absoluteUrl,
14
17
  checkApiKey,
15
18
  describeKeyCheck,
16
19
  keyCheckFromResponse
17
- } from "./chunk-UHS5MMAI.js";
20
+ } from "./chunk-3THDNNVL.js";
18
21
  import {
19
22
  EXIT
20
23
  } from "./chunk-KZERVKTD.js";
21
24
  import {
22
25
  getBaseUrl
23
- } from "./chunk-Y64JGYTB.js";
26
+ } from "./chunk-32OSSYGZ.js";
27
+ import {
28
+ KEY_SCOPE
29
+ } from "./chunk-RQOT6QL3.js";
24
30
 
25
31
  // src/cli/report-key.ts
26
32
  var { dim, yellow } = createIo();
@@ -61,16 +67,28 @@ var exitOnFailedResponse = async (response, baseUrl) => {
61
67
  };
62
68
  var REFUSED_SETTINGS_ACTION = {
63
69
  mode: "change the approval mode",
70
+ mode_clear: "end a temporary approval mode",
64
71
  wait: "change how long approvals wait for you",
65
72
  suggestions: "read or accept rule suggestions"
66
73
  };
67
- var APPROVAL_SETTINGS_PATH = "/dashboard/agent/settings";
68
- var AGENT_KEY_SCOPE = "agent";
69
- var isAgentKeyRefusal = (status, check) => status === 401 && check.kind === "ok" && check.identity.keyScope === AGENT_KEY_SCOPE;
74
+ var settingsChangeStep = (request, baseUrl) => {
75
+ const approvalRules = absoluteUrl(baseUrl, APPROVAL_RULES_PATH);
76
+ switch (request) {
77
+ case "mode":
78
+ return `Change it in the Pushary app, or on the dashboard: ${approvalRules}`;
79
+ case "mode_clear":
80
+ return "A temporary mode ends on its own. To end it now, pick a mode in the Pushary app.";
81
+ case "wait":
82
+ return `Change it on the dashboard: ${approvalRules}`;
83
+ case "suggestions":
84
+ return `Review them on the dashboard: ${approvalRules}`;
85
+ }
86
+ };
87
+ var isAgentKeyRefusal = (status, check) => status === 401 && check.kind === "ok" && check.identity.keyScope === KEY_SCOPE.agent;
70
88
  var describeAgentKeyRefusal = (request, baseUrl = getBaseUrl()) => ({
71
- message: `This computer's key cannot ${REFUSED_SETTINGS_ACTION[request]}. Keys from pushary login, phone pairing and the Mac app run approvals and notifications, not account settings.`,
89
+ message: `This computer's key cannot ${REFUSED_SETTINGS_ACTION[request]}. It runs approvals and notifications, not account settings.`,
72
90
  next: [
73
- `Change it from the Pushary app, your phone, or the dashboard: ${absoluteUrl(baseUrl, APPROVAL_SETTINGS_PATH)}`,
91
+ settingsChangeStep(request, baseUrl),
74
92
  "The key itself is valid. Nothing on this computer needs fixing."
75
93
  ],
76
94
  exitCode: EXIT.USAGE,
@@ -90,6 +108,5 @@ var exitOnFailedSettingsRequest = async (response, settings) => {
90
108
 
91
109
  export {
92
110
  printKeyCheck,
93
- APPROVAL_SETTINGS_PATH,
94
111
  exitOnFailedSettingsRequest
95
112
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DEFAULT_SESSION
3
- } from "./chunk-3UNAPXED.js";
3
+ } from "./chunk-XFDWLDHG.js";
4
4
 
5
5
  // src/answer.ts
6
6
  var isDeferAnswer = (value) => value === "defer";
@@ -6,18 +6,18 @@ import {
6
6
  McpToolError,
7
7
  SubscriptionRequiredError,
8
8
  callMcpTool
9
- } from "./chunk-BKCI53W6.js";
9
+ } from "./chunk-KNUYUQBP.js";
10
10
  import {
11
11
  installNetworkDefaults,
12
12
  isNetworkFailure
13
- } from "./chunk-7ILQX47W.js";
13
+ } from "./chunk-P4RXSZDT.js";
14
14
  import {
15
15
  clientReportClaims
16
- } from "./chunk-ATPJGHUP.js";
16
+ } from "./chunk-TI4JYPGQ.js";
17
17
  import {
18
18
  getApiKey,
19
19
  getBaseUrl
20
- } from "./chunk-Y64JGYTB.js";
20
+ } from "./chunk-32OSSYGZ.js";
21
21
  import {
22
22
  ASK_AGENT_NAME_MAX,
23
23
  CLIENT_REPORT_PATH,
@@ -26,7 +26,7 @@ import {
26
26
  TOOL_FAILURE_CODES,
27
27
  degradedLine,
28
28
  truncateLine
29
- } from "./chunk-MNKIR2ZB.js";
29
+ } from "./chunk-RQOT6QL3.js";
30
30
 
31
31
  // src/api.ts
32
32
  import { randomUUID } from "crypto";
@@ -235,25 +235,38 @@ var nextLongPollMs = (deadlineMs, nowMs) => {
235
235
  const hold = Math.min(deadlineMs - nowMs - WAIT_RESPONSE_GRACE_MS, MAX_LONG_POLL_MS);
236
236
  return hold >= MIN_LONG_POLL_MS ? hold : null;
237
237
  };
238
- var pollForAnswer = async (apiKey, correlationId, deadlineMs, pollInterval = 2e3) => {
239
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
238
+ var ABANDONED_WAIT = { answered: false, status: "abandoned" };
239
+ var sleepUnlessAborted = (ms, signal) => new Promise((resolve) => {
240
+ const wake = () => {
241
+ clearTimeout(timer);
242
+ signal?.removeEventListener("abort", wake);
243
+ resolve();
244
+ };
245
+ const timer = setTimeout(wake, ms);
246
+ signal?.addEventListener("abort", wake, { once: true });
247
+ });
248
+ var boundedBy = (timeoutMs, signal) => signal ? AbortSignal.any([AbortSignal.timeout(timeoutMs), signal]) : AbortSignal.timeout(timeoutMs);
249
+ var pollForAnswer = async (apiKey, correlationId, deadlineMs, pollInterval = 2e3, signal) => {
240
250
  while (Date.now() < deadlineMs) {
251
+ if (signal?.aborted) return ABANDONED_WAIT;
241
252
  const hold = nextLongPollMs(deadlineMs, Date.now());
242
253
  if (hold === null) {
243
- await sleep(Math.max(0, deadlineMs - Date.now()));
254
+ await sleepUnlessAborted(Math.max(0, deadlineMs - Date.now()), signal);
244
255
  continue;
245
256
  }
246
257
  let answer;
247
258
  try {
248
- answer = await waitForAnswer(apiKey, correlationId, hold, AbortSignal.timeout(Math.max(1, deadlineMs - Date.now())));
259
+ answer = await waitForAnswer(apiKey, correlationId, hold, boundedBy(Math.max(1, deadlineMs - Date.now()), signal));
249
260
  } catch {
261
+ if (signal?.aborted) return ABANDONED_WAIT;
250
262
  const late = await withdrawQuestion(apiKey, correlationId);
251
263
  return late.answered ? late : STOPPED;
252
264
  }
253
265
  if (isDecisionStopped(answer) || answer.answered) return answer;
254
266
  if (answer.status === "expired") return STOPPED;
255
- await sleep(Math.min(pollInterval, Math.max(0, deadlineMs - Date.now())));
267
+ await sleepUnlessAborted(Math.min(pollInterval, Math.max(0, deadlineMs - Date.now())), signal);
256
268
  }
269
+ if (signal?.aborted) return ABANDONED_WAIT;
257
270
  return withdrawQuestion(apiKey, correlationId);
258
271
  };
259
272
  var cancelQuestion = async (apiKey, correlationId, signal, baseUrl) => {
@@ -271,10 +284,10 @@ var withdrawQuestion = async (apiKey, correlationId, transport = { cancelQuestio
271
284
  return STOPPED;
272
285
  }
273
286
  };
274
- var waitForQuestion = async (apiKey, question, deadlineMs, pollInterval = 2e3) => {
287
+ var waitForQuestion = async (apiKey, question, deadlineMs, pollInterval = 2e3, signal) => {
275
288
  if (isDecisionStopped(question) || question.answered) return { ...question, answered: !!question.answered };
276
289
  if (question.status === "expired") return STOPPED;
277
- return isAwayPaused(question) || isLocalQuestionHandoff(question) ? withdrawQuestion(apiKey, question.correlationId) : pollForAnswer(apiKey, question.correlationId, deadlineMs, pollInterval);
290
+ return isAwayPaused(question) || isLocalQuestionHandoff(question) ? withdrawQuestion(apiKey, question.correlationId) : pollForAnswer(apiKey, question.correlationId, deadlineMs, pollInterval, signal);
278
291
  };
279
292
  var sendNotification = async (apiKey, params, timeoutMs = 15e3, signal) => {
280
293
  await callMcpTool(apiKey, "send_notification", { ...params }, { maxRetries: signal ? 1 : 3, timeoutMs, signal });
@@ -328,6 +341,7 @@ export {
328
341
  waitForAnswer,
329
342
  WAIT_RESPONSE_GRACE_MS,
330
343
  nextLongPollMs,
344
+ ABANDONED_WAIT,
331
345
  pollForAnswer,
332
346
  cancelQuestion,
333
347
  withdrawQuestion,
@@ -1,13 +1,24 @@
1
+ import {
2
+ APPROVAL_RULES_PATH,
3
+ canChangeApprovalSettings
4
+ } from "./chunk-5MOJCOCF.js";
1
5
  import {
2
6
  callMcpTool
3
- } from "./chunk-BKCI53W6.js";
7
+ } from "./chunk-KNUYUQBP.js";
8
+ import {
9
+ absoluteUrl
10
+ } from "./chunk-3THDNNVL.js";
11
+ import {
12
+ getBaseUrl
13
+ } from "./chunk-32OSSYGZ.js";
4
14
  import {
5
15
  NPX_LATEST_COMMAND
6
- } from "./chunk-BAGBCSXA.js";
16
+ } from "./chunk-X2MTVD4W.js";
7
17
  import {
18
+ APPROVAL_MODE_META,
8
19
  classifyConfirmResponse,
9
20
  isAnswerSource
10
- } from "./chunk-MNKIR2ZB.js";
21
+ } from "./chunk-RQOT6QL3.js";
11
22
 
12
23
  // src/reach.ts
13
24
  var reachVerdict = (channels) => {
@@ -304,7 +315,15 @@ var unansweredNextStep = (appPlatforms) => {
304
315
  const step = notificationAnswerStep(appPlatforms);
305
316
  return step === null ? CHECK_THE_NOTIFICATION_OPENED : `To send another one, run pushary doctor --roundtrip. ${step}`;
306
317
  };
307
- var describeRoundTripOutcome = (outcome, appPlatforms = []) => {
318
+ var PHONE_APPROVAL_MODES = ["push_only", "push_first"];
319
+ var NOTIFY_ONLY_SENT = `Sent. This site is set to ${APPROVAL_MODE_META.notify_only.label}, so questions never wait for an answer.`;
320
+ var phoneApprovalModeLabels = PHONE_APPROVAL_MODES.map((mode) => APPROVAL_MODE_META[mode].label).join(" or ");
321
+ var phoneApprovalModeCommands = PHONE_APPROVAL_MODES.map((mode) => `pushary mode ${mode}`).join(" or ");
322
+ var answerFromYourPhoneSteps = (baseUrl, keyScope) => {
323
+ const inTheAppOrDashboard = `To answer approvals from your phone, choose ${phoneApprovalModeLabels} in the Pushary app, or on the dashboard: ${absoluteUrl(baseUrl, APPROVAL_RULES_PATH)}`;
324
+ return canChangeApprovalSettings(keyScope) ? [inTheAppOrDashboard, `With this key you can also run ${phoneApprovalModeCommands}.`] : [inTheAppOrDashboard];
325
+ };
326
+ var describeRoundTripOutcome = (outcome, { appPlatforms = [], baseUrl = getBaseUrl(), keyScope = null } = {}) => {
308
327
  switch (outcome.kind) {
309
328
  case "answered":
310
329
  return {
@@ -324,8 +343,8 @@ var describeRoundTripOutcome = (outcome, appPlatforms = []) => {
324
343
  case "notify-only":
325
344
  return {
326
345
  ok: true,
327
- message: "Sent. This site is in notify-only mode, so questions never wait for an answer.",
328
- next: ["Switch with pushary mode push_first if you want approvals to hold for your phone."]
346
+ message: NOTIFY_ONLY_SENT,
347
+ next: [...answerFromYourPhoneSteps(baseUrl, keyScope)]
329
348
  };
330
349
  case "no-recipients":
331
350
  return {
@@ -344,8 +363,8 @@ var describeRoundTripOutcome = (outcome, appPlatforms = []) => {
344
363
  };
345
364
  }
346
365
  };
347
- var describeRoundTrip = (outcome, appPlatforms = []) => {
348
- const report = describeRoundTripOutcome(outcome, appPlatforms);
366
+ var describeRoundTrip = (outcome, context = {}) => {
367
+ const report = describeRoundTripOutcome(outcome, context);
349
368
  return { ...report, next: [...report.next, "Agent hook activation remains unverified. Restart your agent and check one real approval."] };
350
369
  };
351
370
  var roundTripEvidence = (outcome) => ({
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  autoUpdateLock
3
- } from "./chunk-ATPJGHUP.js";
3
+ } from "./chunk-TI4JYPGQ.js";
4
4
  import {
5
5
  installPackageGlobally,
6
6
  npmErrorMessage,
7
7
  writeJsonAtomic
8
- } from "./chunk-SCMEVDAR.js";
8
+ } from "./chunk-WJLHZ6E6.js";
9
9
 
10
10
  // src/upgrade-lock.ts
11
11
  import { fork } from "child_process";
@@ -1,9 +1,9 @@
1
- import {
2
- APPROVAL_SETTINGS_PATH
3
- } from "./chunk-FFSIYBBU.js";
4
1
  import {
5
2
  readMacAppAgentKey
6
- } from "./chunk-U2P2LQ5B.js";
3
+ } from "./chunk-LS42NV6E.js";
4
+ import {
5
+ API_KEYS_PATH
6
+ } from "./chunk-5MOJCOCF.js";
7
7
  import {
8
8
  findExportedKeyLines,
9
9
  isAdoptedMacAppKey,
@@ -13,13 +13,13 @@ import {
13
13
  readConfigFileKey,
14
14
  readKeySource,
15
15
  readMacAppKeyPrefix
16
- } from "./chunk-ED2SWTPC.js";
16
+ } from "./chunk-JOBSKQ2Z.js";
17
17
  import {
18
18
  absoluteUrl
19
- } from "./chunk-UHS5MMAI.js";
19
+ } from "./chunk-3THDNNVL.js";
20
20
  import {
21
21
  isValidApiKey
22
- } from "./chunk-MNKIR2ZB.js";
22
+ } from "./chunk-RQOT6QL3.js";
23
23
 
24
24
  // src/key-revocation.ts
25
25
  var SELF_REVOKE_PATH = "/api/v1/server/keys/self/revoke";
@@ -128,7 +128,7 @@ var SPARED_KEY_WHY = {
128
128
  no_creation_record: "This CLI has no record of creating it, so it may be in use by the Mac app or on another machine."
129
129
  };
130
130
  var localRemovalOf = (dryRun) => dryRun ? "would be removed here" : "removed here";
131
- var settingsUrlOf = (baseUrl) => absoluteUrl(baseUrl, APPROVAL_SETTINGS_PATH);
131
+ var settingsUrlOf = (baseUrl) => absoluteUrl(baseUrl, API_KEYS_PATH);
132
132
  var revokeYourselfHint = (reason, baseUrl) => reason === "no_creation_record" ? `Revoke it at ${settingsUrlOf(baseUrl)} if nothing else uses it.` : null;
133
133
  var describeCliKeyRevocation = (outcome, run) => {
134
134
  const label = `API key ${outcome.keyPrefix}`;
@@ -1,25 +1,25 @@
1
1
  import {
2
2
  registerTranscript
3
- } from "./chunk-Q2BD4IBX.js";
3
+ } from "./chunk-SB6TYV4M.js";
4
4
  import {
5
5
  namedEvent
6
- } from "./chunk-WY6YAMZW.js";
6
+ } from "./chunk-ICQCQAUO.js";
7
7
  import {
8
8
  isBridgeLocator,
9
9
  liveBridgeProbe,
10
10
  pusharyHookExecutable,
11
11
  resolveBridgeStub
12
- } from "./chunk-GTBWWCCM.js";
12
+ } from "./chunk-BFF3ABFV.js";
13
13
  import {
14
14
  writeDegraded
15
- } from "./chunk-DTVHZDYM.js";
15
+ } from "./chunk-D3U6CN7T.js";
16
16
  import {
17
17
  cursorUserHooks
18
- } from "./chunk-ATPJGHUP.js";
18
+ } from "./chunk-TI4JYPGQ.js";
19
19
  import {
20
20
  atLeast,
21
21
  parseVersion
22
- } from "./chunk-SCMEVDAR.js";
22
+ } from "./chunk-WJLHZ6E6.js";
23
23
  import {
24
24
  CURSOR_GATE_COVERAGE,
25
25
  CURSOR_GATE_SCRIPT,
@@ -33,7 +33,7 @@ import {
33
33
  isGrokSessionEndStop,
34
34
  isRecord,
35
35
  toolsCoveredByGateEntry
36
- } from "./chunk-MNKIR2ZB.js";
36
+ } from "./chunk-RQOT6QL3.js";
37
37
 
38
38
  // src/tool-hook-input.ts
39
39
  var OPTIONAL_TEXT_FIELDS = ["hook_event_name", "session_id", "cwd", "transcript_path", "tool_use_id", "permission_mode", "cursor_version"];
@@ -210,6 +210,8 @@ function readToolHookCall(source, event, stdin = process.stdin, { readCursorGate
210
210
  }
211
211
 
212
212
  export {
213
+ readAdmissionRules,
214
+ isAdmitted,
213
215
  readHookInput,
214
216
  readToolHookInput,
215
217
  readToolHookCall
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  guardBinary
3
- } from "./chunk-ATPJGHUP.js";
3
+ } from "./chunk-TI4JYPGQ.js";
4
4
 
5
5
  // src/bell/install.ts
6
6
  import { join } from "path";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  localRepoKey,
3
3
  normalizeRepoRemote
4
- } from "./chunk-MNKIR2ZB.js";
4
+ } from "./chunk-RQOT6QL3.js";
5
5
 
6
6
  // src/repo.ts
7
7
  import { existsSync, readFileSync, statSync } from "fs";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  addGeminiMcpServer,
3
3
  removeGeminiMcpServer
4
- } from "./chunk-ANP3DIST.js";
4
+ } from "./chunk-BFF3ABFV.js";
5
5
  import {
6
6
  CLAUDE_CODE_AGENT,
7
7
  compatibleToolLookup
8
- } from "./chunk-3UNAPXED.js";
8
+ } from "./chunk-XFDWLDHG.js";
9
9
  import {
10
10
  CLAUDE_HOOK_EVENTS,
11
11
  PUSHARY_MCP_URL,
@@ -17,7 +17,7 @@ import {
17
17
  isPusharyHook,
18
18
  registerHookEntry,
19
19
  removeClaudeMcpServers
20
- } from "./chunk-ATPJGHUP.js";
20
+ } from "./chunk-TI4JYPGQ.js";
21
21
  import {
22
22
  HOOK_COMPATIBLE_PROFILES,
23
23
  PHONE_ANSWERED_TOOLS,
@@ -28,7 +28,7 @@ import {
28
28
  pusharyMissingReason,
29
29
  refusalOutput,
30
30
  withHookHoldBudget
31
- } from "./chunk-MNKIR2ZB.js";
31
+ } from "./chunk-RQOT6QL3.js";
32
32
 
33
33
  // src/config-document.ts
34
34
  import { parse, stringify } from "smol-toml";
@@ -312,6 +312,7 @@ export {
312
312
  profileFromArgv,
313
313
  activeIdentity,
314
314
  namedEvent,
315
+ namedSource,
315
316
  activeHookSource,
316
317
  normalizeToolCall,
317
318
  grokHome,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  findNativeClaudeInstall
3
- } from "./chunk-RABLE5R6.js";
3
+ } from "./chunk-BBFGDYYI.js";
4
4
  import {
5
5
  isFile
6
- } from "./chunk-SCMEVDAR.js";
6
+ } from "./chunk-WJLHZ6E6.js";
7
7
 
8
8
  // src/wrapper/claudeBinary.ts
9
9
  import { homedir } from "os";
@@ -1,9 +1,12 @@
1
1
  import {
2
2
  CODEX_HOOK_STATUS
3
- } from "./chunk-HDGB6SQ4.js";
3
+ } from "./chunk-Y4MRHAE5.js";
4
+ import {
5
+ retryAgentCommand
6
+ } from "./chunk-JOD2325S.js";
4
7
  import {
5
8
  NPX_LATEST_COMMAND
6
- } from "./chunk-BAGBCSXA.js";
9
+ } from "./chunk-X2MTVD4W.js";
7
10
 
8
11
  // src/setup/codex-trust.ts
9
12
  var CODEX_TRUST_TELEMETRY = {
@@ -61,9 +64,56 @@ var describeCodexTrustConfirmation = (confirmation) => {
61
64
  }
62
65
  };
63
66
 
67
+ // src/hermes/messages.ts
68
+ var HERMES_RESTART_COMMAND = "hermes gateway restart";
69
+ var HERMES_MAC_APP_REPAIR = "open Pushary on this Mac and connect Hermes again";
70
+ var describeHermesInstallFailure = (reason, pluginDirectory, receiptPath) => {
71
+ switch (reason) {
72
+ case "home_unknown":
73
+ return "Hermes did not say where its settings live. Check that `hermes config path` works, then run setup again.";
74
+ case "receipt_invalid":
75
+ return `${receiptPath} is unreadable, so setup cannot record what it changes. Move it aside, then run setup again.`;
76
+ case "settings_unreadable":
77
+ return "Hermes would not report its settings through `hermes config get`. Run `hermes update`, then run setup again.";
78
+ case "plugin_folder_not_ours":
79
+ return `${pluginDirectory} holds a pushary plugin that Pushary did not install. Remove that folder, then run setup again.`;
80
+ case "plugin_write_failed":
81
+ return `Could not write the Pushary plugin to ${pluginDirectory}.`;
82
+ case "settings_failed":
83
+ return "The plugin is in place, but Hermes refused a settings change. Run `hermes plugins enable pushary`, then run setup again.";
84
+ }
85
+ };
86
+ var PROBLEM_DESCRIPTIONS = {
87
+ plugin_missing: "the Pushary plugin folder is missing",
88
+ plugin_not_enabled: "pushary is not in plugins.enabled",
89
+ plugin_disabled: "pushary is in plugins.disabled"
90
+ };
91
+ var describeHermesProblems = (problems) => problems.map((problem) => PROBLEM_DESCRIPTIONS[problem]).join(", ");
92
+ var hermesRepairStep = (installer) => installer === "mac_app" ? HERMES_MAC_APP_REPAIR : `run ${retryAgentCommand("hermes")}`;
93
+ var describeHermesState = (state) => {
94
+ switch (state.kind) {
95
+ case "not_wired":
96
+ return "Pushary is not set up for Hermes";
97
+ case "needs_repair":
98
+ return `Hermes needs repair: ${describeHermesProblems(state.problems)}`;
99
+ case "restart_needed":
100
+ return `Restart Hermes to load Pushary ${state.installedVersion ?? ""}`.trim();
101
+ case "connected":
102
+ if (!state.live) {
103
+ return state.gatewayRunning ? "Hermes is running, and this plugin copy does not report whether it loaded" : "Hermes loads Pushary the next time it starts";
104
+ }
105
+ return state.runningVersion === state.version ? "Hermes loaded Pushary" : `Hermes runs Pushary ${state.runningVersion ?? "from an older copy"}; restart it to load ${state.version ?? "the new copy"}`;
106
+ }
107
+ };
108
+
64
109
  export {
65
110
  codexTrustOutcome,
66
111
  CODEX_HOOK_REMEDY,
67
112
  needsCodexTrustStep,
68
- describeCodexTrustConfirmation
113
+ describeCodexTrustConfirmation,
114
+ HERMES_RESTART_COMMAND,
115
+ describeHermesInstallFailure,
116
+ describeHermesProblems,
117
+ hermesRepairStep,
118
+ describeHermesState
69
119
  };