@pushary/agent-hooks 1.9.29 → 1.9.31

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 (104) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/data/SKILL.md +46 -12
  3. package/data/cursor-plugin/skills/pushary/SKILL.md +46 -12
  4. package/data/vscode-plugin/skills/pushary/SKILL.md +46 -12
  5. package/dist/bin/pushary-bell-hook.js +26 -26
  6. package/dist/bin/pushary-bell.js +9 -9
  7. package/dist/bin/pushary-claude.js +30 -27
  8. package/dist/bin/pushary-clean.js +29 -29
  9. package/dist/bin/pushary-codex-bridge.js +17 -17
  10. package/dist/bin/pushary-codex-hook.js +41 -40
  11. package/dist/bin/pushary-codex.js +12 -12
  12. package/dist/bin/pushary-connect.js +24 -24
  13. package/dist/bin/pushary-cowork.js +5 -5
  14. package/dist/bin/pushary-daemon.js +33 -33
  15. package/dist/bin/pushary-disconnect.js +24 -24
  16. package/dist/bin/pushary-doctor.js +33 -33
  17. package/dist/bin/pushary-elicitation-hook.js +26 -26
  18. package/dist/bin/pushary-gemini-bridge.js +13 -13
  19. package/dist/bin/pushary-gemini-hook.js +28 -28
  20. package/dist/bin/pushary-hook.js +30 -30
  21. package/dist/bin/pushary-login.js +12 -12
  22. package/dist/bin/pushary-logout.js +24 -24
  23. package/dist/bin/pushary-mcp.js +9 -9
  24. package/dist/bin/pushary-mode.js +12 -12
  25. package/dist/bin/pushary-notification-hook.js +25 -25
  26. package/dist/bin/pushary-opencode-hook.js +27 -27
  27. package/dist/bin/pushary-permission-denied-hook.js +30 -30
  28. package/dist/bin/pushary-permission-hook.js +29 -29
  29. package/dist/bin/pushary-post-hook.js +25 -25
  30. package/dist/bin/pushary-prompt-hook.js +25 -25
  31. package/dist/bin/pushary-session-end-hook.js +25 -25
  32. package/dist/bin/pushary-session-start-hook.js +29 -29
  33. package/dist/bin/pushary-setup.js +35 -35
  34. package/dist/bin/pushary-stats.js +5 -5
  35. package/dist/bin/pushary-status.js +26 -26
  36. package/dist/bin/pushary-stop-hook.js +25 -25
  37. package/dist/bin/pushary-stopfailure-hook.js +25 -25
  38. package/dist/bin/pushary-suggestions.js +12 -12
  39. package/dist/bin/pushary-transcript-register.js +18 -18
  40. package/dist/bin/pushary-transcripts.js +8 -8
  41. package/dist/bin/pushary-upgrade.js +30 -30
  42. package/dist/bin/pushary-wait.js +12 -12
  43. package/dist/bin/pushary.js +6 -6
  44. package/dist/{chunk-QOMUEL44.js → chunk-2ROXUEZV.js} +1 -1
  45. package/dist/{chunk-WJUYWTNT.js → chunk-35RJKPIC.js} +1 -1
  46. package/dist/{chunk-SLKVEJLM.js → chunk-3EIJDBVB.js} +5 -5
  47. package/dist/{chunk-Y3OZ3CT6.js → chunk-464FX6W5.js} +6 -5
  48. package/dist/{chunk-KMTSU366.js → chunk-4JOWKPUF.js} +1 -1
  49. package/dist/{chunk-35CMXPES.js → chunk-4WGKAGIU.js} +3 -3
  50. package/dist/{chunk-TXKAPRIA.js → chunk-5EX2SD5N.js} +1 -1
  51. package/dist/{chunk-WLDFMVRY.js → chunk-5F32ZTWD.js} +2 -2
  52. package/dist/{chunk-NRV5PIPA.js → chunk-5FNE6ZVV.js} +2 -2
  53. package/dist/{chunk-L7PGUSAU.js → chunk-5GDNIEL5.js} +1 -1
  54. package/dist/{chunk-OUWDJIJF.js → chunk-7447DYNW.js} +1 -1
  55. package/dist/{chunk-5L7H32JK.js → chunk-75UCMLT5.js} +1 -1
  56. package/dist/{chunk-7MYGOFVX.js → chunk-77J4RVB2.js} +4 -4
  57. package/dist/{chunk-CCI2UBYR.js → chunk-7S4Z36H7.js} +3 -3
  58. package/dist/{chunk-LSB66XQC.js → chunk-ALSIMOFS.js} +4 -4
  59. package/dist/{chunk-M2L4EHYW.js → chunk-AXANUGOV.js} +2 -2
  60. package/dist/{chunk-Z2WS466S.js → chunk-BMUTA66M.js} +15 -13
  61. package/dist/{chunk-XH7NMH3Q.js → chunk-CJCVPCOB.js} +2 -2
  62. package/dist/{chunk-PRVG3COH.js → chunk-D3TVRNZC.js} +2 -2
  63. package/dist/{chunk-CPZZTS4I.js → chunk-DP2FAIW6.js} +1 -1
  64. package/dist/{chunk-VTR4VRL2.js → chunk-DWQYCOPI.js} +2 -4
  65. package/dist/{chunk-LRLF6PZ2.js → chunk-EPVY3JU3.js} +1 -1
  66. package/dist/{chunk-GVIPEBEL.js → chunk-FEICGRNI.js} +3 -3
  67. package/dist/{chunk-C2ZIVQ6W.js → chunk-FGFFUCEL.js} +6 -6
  68. package/dist/{chunk-TJXCBTL4.js → chunk-FXJ5SEQT.js} +5 -5
  69. package/dist/{chunk-G4X3Z3YS.js → chunk-GROYT52D.js} +4 -4
  70. package/dist/{chunk-2FOAQ64R.js → chunk-GSBOLJZ4.js} +177 -56
  71. package/dist/{chunk-UW37WHVP.js → chunk-H2PQXK26.js} +2 -2
  72. package/dist/{chunk-BUOLA2AM.js → chunk-IDGESFKZ.js} +1 -1
  73. package/dist/{chunk-BAVNBGH4.js → chunk-IIP4LEO3.js} +7 -7
  74. package/dist/{chunk-GMVZYB3W.js → chunk-IKJJB4QG.js} +3 -3
  75. package/dist/{chunk-S3DP6DCZ.js → chunk-KBRKQMCI.js} +2 -2
  76. package/dist/{chunk-AZMHKUUU.js → chunk-MG523AVN.js} +1 -1
  77. package/dist/{chunk-AVQ6ETA7.js → chunk-MRR4NQ3H.js} +1 -1
  78. package/dist/{chunk-ETLVZHRG.js → chunk-N4LLBUU2.js} +2 -2
  79. package/dist/{chunk-YFEHKEGQ.js → chunk-NTWTU6FA.js} +1 -1
  80. package/dist/{chunk-UM2HNUJA.js → chunk-OS6AE2MR.js} +3 -3
  81. package/dist/{chunk-DQGM57ZW.js → chunk-OSUAY4SA.js} +1 -1
  82. package/dist/{chunk-IWPEAA2J.js → chunk-OTDRNEED.js} +1 -1
  83. package/dist/{chunk-BP3WMLXJ.js → chunk-OULHOX5C.js} +5 -5
  84. package/dist/{chunk-4DTKE3AS.js → chunk-Q2GSTQ3R.js} +2 -2
  85. package/dist/{chunk-DUVHHQEL.js → chunk-Q7H2IMJQ.js} +2 -2
  86. package/dist/{chunk-PX4RRHEB.js → chunk-QCOE42GO.js} +4 -4
  87. package/dist/{chunk-BKOWTW5P.js → chunk-QGCEQGH6.js} +4 -4
  88. package/dist/{chunk-JGDBHB6L.js → chunk-RN3KZDWP.js} +2 -2
  89. package/dist/{chunk-BJMVQ6D5.js → chunk-SAHD5DYW.js} +4 -4
  90. package/dist/{chunk-62LJB5U3.js → chunk-SJJA2GPD.js} +2 -2
  91. package/dist/{chunk-IM73P6JT.js → chunk-SQIZGUAN.js} +5 -5
  92. package/dist/{chunk-CBOHZ6NY.js → chunk-SRRJLA24.js} +4 -4
  93. package/dist/{chunk-TILXDMUR.js → chunk-SYUHM6V3.js} +3 -3
  94. package/dist/{chunk-H7QTZTIE.js → chunk-TDLEFGAM.js} +1641 -178
  95. package/dist/{chunk-CJQEJWLU.js → chunk-VFNYTVTJ.js} +5 -5
  96. package/dist/{chunk-OSVDGVLL.js → chunk-X427XA76.js} +12 -12
  97. package/dist/{chunk-2XM4TGIM.js → chunk-X5HK4XUN.js} +4 -4
  98. package/dist/{chunk-EO2GYHWH.js → chunk-ZYDSHR5P.js} +3 -3
  99. package/dist/{global-upgrade-ZPULZXOY.js → global-upgrade-YMBTZHDP.js} +6 -6
  100. package/dist/{paths-Y7ZWWCFK.js → paths-BPFRUWMB.js} +2 -2
  101. package/dist/{reapply-I4YMQP7F.js → reapply-ZC2LSUVZ.js} +20 -20
  102. package/dist/src/index.js +19 -19
  103. package/dist/{upgrade-lock-IDAAFSH2.js → upgrade-lock-TTZMLZPB.js} +3 -3
  104. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.31
4
+
5
+ - SQL sent through `psql`, `mysql` and `mariadb` is now read the way the database reads it. These used to run under an approve rule and now ask: a write after a dollar quote with a non-ASCII tag, such as `psql -c 'SELECT $é$ -- $é$; UPDATE users SET x=1'`; a write after a carriage return that ends a `--` comment; and a write inside a MySQL `/*! ... */` or MariaDB `/*M! ... */` comment, which those clients run.
6
+ - `SELECT ... INTO` and a `LOCK` statement now count as writes and ask, as do `EXECUTE IMMEDIATE` and `PREPARE ... FROM`.
7
+ - A command after `bash -c` and its options is now seen, so a `Bash(git push:*)` deny rule now refuses `bash -c -x 'git push'`, `bash -c -- 'git push'` and `zsh -c -x 'git push'`, which used to run.
8
+ - `env` options are read the way GNU and macOS `env` read them, abbreviations included, so the same deny rule now refuses `env --spl='git push'`. When the two versions of `env` would run different commands, Pushary asks.
9
+ - Codex: `gh api -iXDELETE` and `gh api -iX DELETE` are read as DELETE requests and ask, instead of being passed back to Codex.
10
+ - A tool name that is not one concrete tool, such as `*`, can no longer be saved as an Always allow rule.
11
+ - History capture works when Claude Code or Codex keeps its files under a custom `CLAUDE_CONFIG_DIR` or `CODEX_HOME`.
12
+ - `pushary-mcp` tells Pushary which computer is asking, so the phone's Try it step checks the computer you chose.
13
+
14
+ ## 1.9.30
15
+
16
+ - "Always allow PowerShell" and "Always allow Monitor" now ask about the same consequences "Always allow Bash" does. A Monitor command is judged exactly as the same Bash command. In PowerShell that includes `Remove-Item -Recurse`, `irm ... | iex`, a downloaded script then run, `Set-Content .env`, `Stop-Service`, `Format-Volume` and a recursive `Get-ChildItem` piped to `Remove-Item`. A plain `Remove-Item file.txt`, `git push`, `Stop-Process`, `-WhatIf` and every read such as `Get-ChildItem` keep running.
17
+ - A `*` approve rule no longer runs a destructive PowerShell or Monitor command, such as `Remove-Item -Recurse -Force ~`, `cmd /c rd /s` or `rm -rf ~`, without asking. It asks, as it already did for Bash.
18
+ - Codex: a call nobody answers is now refused instead of passed back to Codex, which ran calls it does not ask about itself. Only a rule you set to approve on timeout lets it run.
19
+ - A call refused because your phone approvals for a session are paused now says so, and tells you to tap Approve on the notification to resume. It used to say "No response within timeout".
20
+ - Codex: a GitHub, Stripe, AWS or Twilio write behind `caffeinate`, `stdbuf` or `flock` now asks, and `gh` asks about anything that is not a known read, such as `gh extension install`, `gh ssh-key add` or `gh label clone`. `gh pr co` now runs like `gh pr checkout`.
21
+ - Codex: a patch to a GitHub workflow or secrets file now asks however its path is capitalised, so `.GitHub/Workflows/ci.yml` no longer runs unasked.
22
+ - When two of your rules hold a call equally, Pushary now uses the one that refuses it if nobody answers, whatever order the rules are in.
23
+ - Antigravity CLI: input to a running command that is not text is now refused instead of shown as blank input.
24
+
3
25
  ## 1.9.29
4
26
 
5
27
  - Codex: answering "Handle on your machine" now always refuses the call, and tells you to ask Codex to run it again while you are at your computer, or to run it yourself. In Codex's default modes the call used to pass back to Codex, and a call Codex does not ask about itself, such as a remote API write or a file deletion, could run without anyone approving it.
package/data/SKILL.md CHANGED
@@ -174,20 +174,46 @@ Put a boundary that is not a file path in `promises`, never in `allowedPaths`. R
174
174
 
175
175
  **Look at the machine first. Do not guess the install path.** Run these. Each one is read-only and fast. Run them as separate commands.
176
176
 
177
+ Use `uname -s` in a Unix shell (`Darwin` is Mac, `Linux` is Linux). In
178
+ PowerShell, inspect `$env:OS` (`Windows_NT` is Windows). Platform discovery must
179
+ not depend on Node: the native Mac route does not require it.
180
+
181
+ In a Unix shell, run these separately:
182
+
177
183
  ```bash
178
- node -p "process.platform"
184
+ uname -s
179
185
  [ -n "${PUSHARY_API_KEY:+x}" ] && echo key-in-env
180
- node -e "try{process.exit(JSON.parse(require('fs').readFileSync(process.env.HOME+'/.pushary/config.json','utf8')).apiKey?.trim()?0:1)}catch{process.exit(1)}" && echo keyed
186
+ command -v node >/dev/null 2>&1 && echo node-available
187
+ command -v npm >/dev/null 2>&1 && echo npm-available
181
188
  test -x ~/.pushary/bin/pushary-bridge && echo mac-app
182
189
  ```
183
190
 
184
- The third and fourth tests answer different questions.
191
+ On Mac, check the stored key without Node or printing its value:
192
+
193
+ ```bash
194
+ if pushary_key_check=$(/usr/bin/plutil -extract apiKey raw -expect string -o - ~/.pushary/config.json 2>/dev/null); then
195
+ printf '%s\n' "$pushary_key_check" | /usr/bin/awk 'NF { found=1 } END { exit !found }' && echo keyed
196
+ fi
197
+ unset pushary_key_check
198
+ ```
199
+
200
+ When Node is available, other Unix platforms can check the stored key with:
185
201
 
186
- The third says a key is stored **and is not empty**. Test the value, not the file.
202
+ ```bash
203
+ node -e "try{process.exit(JSON.parse(require('fs').readFileSync(process.env.HOME+'/.pushary/config.json','utf8')).apiKey?.trim()?0:1)}catch{process.exit(1)}" && echo keyed
204
+ ```
205
+
206
+ In PowerShell, check for a nonempty environment/stored key without printing it;
207
+ use `Get-Content` and `ConvertFrom-Json` for the config in `$env:USERPROFILE`.
208
+ Check `Get-Command node,npm` before choosing the terminal installer.
209
+
210
+ The stored-key and bridge tests answer different questions.
211
+
212
+ The stored-key test says a key is stored **and is not empty**. Test the value, not the file.
187
213
  The file stays behind after a logout removes the key. A test for the file alone
188
214
  tells a logged-out user they are ready.
189
215
 
190
- The fourth says the Mac app is installed here. Only the Mac app writes that file.
216
+ The bridge test says the Mac app is installed here. Only the Mac app writes that file.
191
217
 
192
218
  Check the environment before the stored key. An exported key wins over a stored
193
219
  one. Never print the key itself.
@@ -196,14 +222,22 @@ Then take one branch.
196
222
 
197
223
  ### Branch 1. `key-in-env`, `keyed`, or `mac-app`
198
224
 
199
- This machine is set up. Offer no install. Do not run `setup` again.
225
+ This machine has an existing installation or credential. Offer no new install.
226
+ Verify its readiness before reporting success. Do not run `setup` again.
200
227
 
201
228
  `mac-app` counts on its own. The Mac app signs in for the user. It writes the key
202
229
  into the agent configuration files it wires, not into `~/.pushary/config.json`. A
203
- machine the app set up therefore prints `mac-app` and nothing else. Treat it as
204
- ready.
230
+ machine the app set up therefore prints `mac-app` and nothing else. The locator
231
+ proves installation, not sign-in, entitlement, current agent admission or delivery.
232
+
233
+ For `mac-app` without Node/npm, open Pushary and check the intended account,
234
+ subscription and Agents connection state there. Use the native app to connect or
235
+ repair the selected agents and phone. Then send a harmless question from the
236
+ current agent and confirm its returned answer. Do not install Node to inspect
237
+ the native setup and do not claim a CLI status exit code when no CLI ran.
205
238
 
206
- Check it with `npx pushary@latest status --json`. The exit code is the answer:
239
+ When Node/npm are available, check it with `npx pushary@latest status --json`.
240
+ The exit code is the answer:
207
241
 
208
242
  | Code | Meaning |
209
243
  | --- | --- |
@@ -224,7 +258,7 @@ grep -lq pushary-bridge ~/.claude/settings.json ~/.gemini/settings.json ~/.curso
224
258
 
225
259
  If the app owns them, `setup` would keep them and write almost nothing, so telling the user to re-run it is bad advice. Point them at the Pushary app instead.
226
260
 
227
- ### Branch 2. `darwin`, no key, no `mac-app`
261
+ ### Branch 2. `Darwin`, no key, no `mac-app`
228
262
 
229
263
  Offer the Mac app first. It needs no Node and no terminal. It writes the agent configuration itself, and it answers questions in the notch at the desk.
230
264
 
@@ -236,7 +270,7 @@ They can also download it from https://pushary.com/download. It needs macOS 14 o
236
270
 
237
271
  The command line works on macOS too. Offer it if the user prefers the terminal, or if the user runs Hermes, because Hermes needs a Python the app cannot install.
238
272
 
239
- ### Branch 3. `linux` or `win32`
273
+ ### Branch 3. `Linux` or `Windows_NT`
240
274
 
241
275
  There is no Mac app for these machines. Use the command line. It is fully supported.
242
276
 
@@ -251,7 +285,7 @@ Node 20.17+, 22.13+ or 23.5+ is necessary. Then the user needs a phone to answer
251
285
 
252
286
  On Windows, setup writes no shell file, so `~/.pushary/config.json` is the only key store. On a Linux machine with no screen, browser login does not work, but the pairing QR does.
253
287
 
254
- ### Branch 4. `darwin`, `mac-app`, and the user asked for the command line
288
+ ### Branch 4. `Darwin`, `mac-app`, and the user asked for the command line
255
289
 
256
290
  Run `setup`. It reads the key the app signed in with, so it mints no second key, and it keeps the hooks the app owns. Pass `--take-over-hooks` only when the user wants the command line to own them instead.
257
291
 
@@ -167,20 +167,46 @@ Put a boundary that is not a file path in `promises`, never in `allowedPaths`. R
167
167
 
168
168
  **Look at the machine first. Do not guess the install path.** Run these. Each one is read-only and fast. Run them as separate commands.
169
169
 
170
+ Use `uname -s` in a Unix shell (`Darwin` is Mac, `Linux` is Linux). In
171
+ PowerShell, inspect `$env:OS` (`Windows_NT` is Windows). Platform discovery must
172
+ not depend on Node: the native Mac route does not require it.
173
+
174
+ In a Unix shell, run these separately:
175
+
170
176
  ```bash
171
- node -p "process.platform"
177
+ uname -s
172
178
  [ -n "${PUSHARY_API_KEY:+x}" ] && echo key-in-env
173
- node -e "try{process.exit(JSON.parse(require('fs').readFileSync(process.env.HOME+'/.pushary/config.json','utf8')).apiKey?.trim()?0:1)}catch{process.exit(1)}" && echo keyed
179
+ command -v node >/dev/null 2>&1 && echo node-available
180
+ command -v npm >/dev/null 2>&1 && echo npm-available
174
181
  test -x ~/.pushary/bin/pushary-bridge && echo mac-app
175
182
  ```
176
183
 
177
- The third and fourth tests answer different questions.
184
+ On Mac, check the stored key without Node or printing its value:
185
+
186
+ ```bash
187
+ if pushary_key_check=$(/usr/bin/plutil -extract apiKey raw -expect string -o - ~/.pushary/config.json 2>/dev/null); then
188
+ printf '%s\n' "$pushary_key_check" | /usr/bin/awk 'NF { found=1 } END { exit !found }' && echo keyed
189
+ fi
190
+ unset pushary_key_check
191
+ ```
192
+
193
+ When Node is available, other Unix platforms can check the stored key with:
178
194
 
179
- The third says a key is stored **and is not empty**. Test the value, not the file.
195
+ ```bash
196
+ node -e "try{process.exit(JSON.parse(require('fs').readFileSync(process.env.HOME+'/.pushary/config.json','utf8')).apiKey?.trim()?0:1)}catch{process.exit(1)}" && echo keyed
197
+ ```
198
+
199
+ In PowerShell, check for a nonempty environment/stored key without printing it;
200
+ use `Get-Content` and `ConvertFrom-Json` for the config in `$env:USERPROFILE`.
201
+ Check `Get-Command node,npm` before choosing the terminal installer.
202
+
203
+ The stored-key and bridge tests answer different questions.
204
+
205
+ The stored-key test says a key is stored **and is not empty**. Test the value, not the file.
180
206
  The file stays behind after a logout removes the key. A test for the file alone
181
207
  tells a logged-out user they are ready.
182
208
 
183
- The fourth says the Mac app is installed here. Only the Mac app writes that file.
209
+ The bridge test says the Mac app is installed here. Only the Mac app writes that file.
184
210
 
185
211
  Check the environment before the stored key. An exported key wins over a stored
186
212
  one. Never print the key itself.
@@ -189,14 +215,22 @@ Then take one branch.
189
215
 
190
216
  ### Branch 1. `key-in-env`, `keyed`, or `mac-app`
191
217
 
192
- This machine is set up. Offer no install. Do not run `setup` again.
218
+ This machine has an existing installation or credential. Offer no new install.
219
+ Verify its readiness before reporting success. Do not run `setup` again.
193
220
 
194
221
  `mac-app` counts on its own. The Mac app signs in for the user. It writes the key
195
222
  into the agent configuration files it wires, not into `~/.pushary/config.json`. A
196
- machine the app set up therefore prints `mac-app` and nothing else. Treat it as
197
- ready.
223
+ machine the app set up therefore prints `mac-app` and nothing else. The locator
224
+ proves installation, not sign-in, entitlement, current agent admission or delivery.
225
+
226
+ For `mac-app` without Node/npm, open Pushary and check the intended account,
227
+ subscription and Agents connection state there. Use the native app to connect or
228
+ repair the selected agents and phone. Then send a harmless question from the
229
+ current agent and confirm its returned answer. Do not install Node to inspect
230
+ the native setup and do not claim a CLI status exit code when no CLI ran.
198
231
 
199
- Check it with `npx pushary@latest status --json`. The exit code is the answer:
232
+ When Node/npm are available, check it with `npx pushary@latest status --json`.
233
+ The exit code is the answer:
200
234
 
201
235
  | Code | Meaning |
202
236
  | --- | --- |
@@ -217,7 +251,7 @@ grep -lq pushary-bridge ~/.claude/settings.json ~/.gemini/settings.json ~/.curso
217
251
 
218
252
  If the app owns them, `setup` would keep them and write almost nothing, so telling the user to re-run it is bad advice. Point them at the Pushary app instead.
219
253
 
220
- ### Branch 2. `darwin`, no key, no `mac-app`
254
+ ### Branch 2. `Darwin`, no key, no `mac-app`
221
255
 
222
256
  Offer the Mac app first. It needs no Node and no terminal. It writes the agent configuration itself, and it answers questions in the notch at the desk.
223
257
 
@@ -229,7 +263,7 @@ They can also download it from https://pushary.com/download. It needs macOS 14 o
229
263
 
230
264
  The command line works on macOS too. Offer it if the user prefers the terminal, or if the user runs Hermes, because Hermes needs a Python the app cannot install.
231
265
 
232
- ### Branch 3. `linux` or `win32`
266
+ ### Branch 3. `Linux` or `Windows_NT`
233
267
 
234
268
  There is no Mac app for these machines. Use the command line. It is fully supported.
235
269
 
@@ -244,7 +278,7 @@ Node 20.17+, 22.13+ or 23.5+ is necessary. Then the user needs a phone to answer
244
278
 
245
279
  On Windows, setup writes no shell file, so `~/.pushary/config.json` is the only key store. On a Linux machine with no screen, browser login does not work, but the pairing QR does.
246
280
 
247
- ### Branch 4. `darwin`, `mac-app`, and the user asked for the command line
281
+ ### Branch 4. `Darwin`, `mac-app`, and the user asked for the command line
248
282
 
249
283
  Run `setup`. It reads the key the app signed in with, so it mints no second key, and it keeps the hooks the app owns. Pass `--take-over-hooks` only when the user wants the command line to own them instead.
250
284
 
@@ -167,20 +167,46 @@ Put a boundary that is not a file path in `promises`, never in `allowedPaths`. R
167
167
 
168
168
  **Look at the machine first. Do not guess the install path.** Run these. Each one is read-only and fast. Run them as separate commands.
169
169
 
170
+ Use `uname -s` in a Unix shell (`Darwin` is Mac, `Linux` is Linux). In
171
+ PowerShell, inspect `$env:OS` (`Windows_NT` is Windows). Platform discovery must
172
+ not depend on Node: the native Mac route does not require it.
173
+
174
+ In a Unix shell, run these separately:
175
+
170
176
  ```bash
171
- node -p "process.platform"
177
+ uname -s
172
178
  [ -n "${PUSHARY_API_KEY:+x}" ] && echo key-in-env
173
- node -e "try{process.exit(JSON.parse(require('fs').readFileSync(process.env.HOME+'/.pushary/config.json','utf8')).apiKey?.trim()?0:1)}catch{process.exit(1)}" && echo keyed
179
+ command -v node >/dev/null 2>&1 && echo node-available
180
+ command -v npm >/dev/null 2>&1 && echo npm-available
174
181
  test -x ~/.pushary/bin/pushary-bridge && echo mac-app
175
182
  ```
176
183
 
177
- The third and fourth tests answer different questions.
184
+ On Mac, check the stored key without Node or printing its value:
185
+
186
+ ```bash
187
+ if pushary_key_check=$(/usr/bin/plutil -extract apiKey raw -expect string -o - ~/.pushary/config.json 2>/dev/null); then
188
+ printf '%s\n' "$pushary_key_check" | /usr/bin/awk 'NF { found=1 } END { exit !found }' && echo keyed
189
+ fi
190
+ unset pushary_key_check
191
+ ```
192
+
193
+ When Node is available, other Unix platforms can check the stored key with:
178
194
 
179
- The third says a key is stored **and is not empty**. Test the value, not the file.
195
+ ```bash
196
+ node -e "try{process.exit(JSON.parse(require('fs').readFileSync(process.env.HOME+'/.pushary/config.json','utf8')).apiKey?.trim()?0:1)}catch{process.exit(1)}" && echo keyed
197
+ ```
198
+
199
+ In PowerShell, check for a nonempty environment/stored key without printing it;
200
+ use `Get-Content` and `ConvertFrom-Json` for the config in `$env:USERPROFILE`.
201
+ Check `Get-Command node,npm` before choosing the terminal installer.
202
+
203
+ The stored-key and bridge tests answer different questions.
204
+
205
+ The stored-key test says a key is stored **and is not empty**. Test the value, not the file.
180
206
  The file stays behind after a logout removes the key. A test for the file alone
181
207
  tells a logged-out user they are ready.
182
208
 
183
- The fourth says the Mac app is installed here. Only the Mac app writes that file.
209
+ The bridge test says the Mac app is installed here. Only the Mac app writes that file.
184
210
 
185
211
  Check the environment before the stored key. An exported key wins over a stored
186
212
  one. Never print the key itself.
@@ -189,14 +215,22 @@ Then take one branch.
189
215
 
190
216
  ### Branch 1. `key-in-env`, `keyed`, or `mac-app`
191
217
 
192
- This machine is set up. Offer no install. Do not run `setup` again.
218
+ This machine has an existing installation or credential. Offer no new install.
219
+ Verify its readiness before reporting success. Do not run `setup` again.
193
220
 
194
221
  `mac-app` counts on its own. The Mac app signs in for the user. It writes the key
195
222
  into the agent configuration files it wires, not into `~/.pushary/config.json`. A
196
- machine the app set up therefore prints `mac-app` and nothing else. Treat it as
197
- ready.
223
+ machine the app set up therefore prints `mac-app` and nothing else. The locator
224
+ proves installation, not sign-in, entitlement, current agent admission or delivery.
225
+
226
+ For `mac-app` without Node/npm, open Pushary and check the intended account,
227
+ subscription and Agents connection state there. Use the native app to connect or
228
+ repair the selected agents and phone. Then send a harmless question from the
229
+ current agent and confirm its returned answer. Do not install Node to inspect
230
+ the native setup and do not claim a CLI status exit code when no CLI ran.
198
231
 
199
- Check it with `npx pushary@latest status --json`. The exit code is the answer:
232
+ When Node/npm are available, check it with `npx pushary@latest status --json`.
233
+ The exit code is the answer:
200
234
 
201
235
  | Code | Meaning |
202
236
  | --- | --- |
@@ -217,7 +251,7 @@ grep -lq pushary-bridge ~/.claude/settings.json ~/.gemini/settings.json ~/.curso
217
251
 
218
252
  If the app owns them, `setup` would keep them and write almost nothing, so telling the user to re-run it is bad advice. Point them at the Pushary app instead.
219
253
 
220
- ### Branch 2. `darwin`, no key, no `mac-app`
254
+ ### Branch 2. `Darwin`, no key, no `mac-app`
221
255
 
222
256
  Offer the Mac app first. It needs no Node and no terminal. It writes the agent configuration itself, and it answers questions in the notch at the desk.
223
257
 
@@ -229,7 +263,7 @@ They can also download it from https://pushary.com/download. It needs macOS 14 o
229
263
 
230
264
  The command line works on macOS too. Offer it if the user prefers the terminal, or if the user runs Hermes, because Hermes needs a Python the app cannot install.
231
265
 
232
- ### Branch 3. `linux` or `win32`
266
+ ### Branch 3. `Linux` or `Windows_NT`
233
267
 
234
268
  There is no Mac app for these machines. Use the command line. It is fully supported.
235
269
 
@@ -244,7 +278,7 @@ Node 20.17+, 22.13+ or 23.5+ is necessary. Then the user needs a phone to answer
244
278
 
245
279
  On Windows, setup writes no shell file, so `~/.pushary/config.json` is the only key store. On a Linux machine with no screen, browser login does not work, but the pairing QR does.
246
280
 
247
- ### Branch 4. `darwin`, `mac-app`, and the user asked for the command line
281
+ ### Branch 4. `Darwin`, `mac-app`, and the user asked for the command line
248
282
 
249
283
  Run `setup`. It reads the key the app signed in with, so it mints no second key, and it keeps the hooks the app owns. Pass `--take-over-hooks` only when the user wants the command line to own them instead.
250
284
 
@@ -1,41 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  handleBell
4
- } from "../chunk-5L7H32JK.js";
4
+ } from "../chunk-75UCMLT5.js";
5
5
  import {
6
6
  readHookInput
7
- } from "../chunk-LSB66XQC.js";
8
- import "../chunk-2FOAQ64R.js";
9
- import "../chunk-VTR4VRL2.js";
10
- import "../chunk-CJQEJWLU.js";
11
- import "../chunk-OUWDJIJF.js";
12
- import "../chunk-LRLF6PZ2.js";
13
- import "../chunk-AVQ6ETA7.js";
7
+ } from "../chunk-ALSIMOFS.js";
8
+ import "../chunk-GSBOLJZ4.js";
9
+ import "../chunk-DWQYCOPI.js";
10
+ import "../chunk-VFNYTVTJ.js";
11
+ import "../chunk-7447DYNW.js";
12
+ import "../chunk-EPVY3JU3.js";
13
+ import "../chunk-MRR4NQ3H.js";
14
14
  import "../chunk-XHKBHWLX.js";
15
- import "../chunk-BJMVQ6D5.js";
16
- import "../chunk-TJXCBTL4.js";
17
- import "../chunk-UM2HNUJA.js";
18
- import "../chunk-WJUYWTNT.js";
19
- import "../chunk-BUOLA2AM.js";
15
+ import "../chunk-SAHD5DYW.js";
16
+ import "../chunk-FXJ5SEQT.js";
17
+ import "../chunk-OS6AE2MR.js";
18
+ import "../chunk-35RJKPIC.js";
19
+ import "../chunk-IDGESFKZ.js";
20
20
  import "../chunk-GMXKITVA.js";
21
21
  import "../chunk-6ICPEJY5.js";
22
- import "../chunk-GMVZYB3W.js";
23
- import "../chunk-OSVDGVLL.js";
24
- import "../chunk-KMTSU366.js";
25
- import "../chunk-Y3OZ3CT6.js";
22
+ import "../chunk-IKJJB4QG.js";
23
+ import "../chunk-X427XA76.js";
24
+ import "../chunk-4JOWKPUF.js";
25
+ import "../chunk-464FX6W5.js";
26
26
  import "../chunk-POFBE6ZY.js";
27
- import "../chunk-GVIPEBEL.js";
28
- import "../chunk-TILXDMUR.js";
29
- import "../chunk-35CMXPES.js";
30
- import "../chunk-YFEHKEGQ.js";
27
+ import "../chunk-FEICGRNI.js";
28
+ import "../chunk-SYUHM6V3.js";
29
+ import "../chunk-4WGKAGIU.js";
30
+ import "../chunk-NTWTU6FA.js";
31
31
  import "../chunk-7QLSKOSU.js";
32
32
  import "../chunk-KZERVKTD.js";
33
- import "../chunk-S3DP6DCZ.js";
34
- import "../chunk-CBOHZ6NY.js";
33
+ import "../chunk-KBRKQMCI.js";
34
+ import "../chunk-SRRJLA24.js";
35
35
  import "../chunk-AQFEP65J.js";
36
- import "../chunk-TXKAPRIA.js";
37
- import "../chunk-QOMUEL44.js";
38
- import "../chunk-H7QTZTIE.js";
36
+ import "../chunk-5EX2SD5N.js";
37
+ import "../chunk-2ROXUEZV.js";
38
+ import "../chunk-TDLEFGAM.js";
39
39
 
40
40
  // bin/pushary-bell-hook.ts
41
41
  var main = async () => {
@@ -5,39 +5,39 @@ import {
5
5
  liveAgentCount,
6
6
  ring,
7
7
  upgradeThreshold
8
- } from "../chunk-5L7H32JK.js";
8
+ } from "../chunk-75UCMLT5.js";
9
9
  import {
10
10
  createIo
11
11
  } from "../chunk-FO3MDD26.js";
12
12
  import {
13
13
  exitOnHelpFlag
14
- } from "../chunk-XH7NMH3Q.js";
14
+ } from "../chunk-CJCVPCOB.js";
15
15
  import {
16
16
  addBellHooks,
17
17
  bellInstalled,
18
18
  removeBellHooks
19
- } from "../chunk-BUOLA2AM.js";
19
+ } from "../chunk-IDGESFKZ.js";
20
20
  import {
21
21
  parseFlags
22
22
  } from "../chunk-GMXKITVA.js";
23
- import "../chunk-GMVZYB3W.js";
23
+ import "../chunk-IKJJB4QG.js";
24
24
  import "../chunk-7QLSKOSU.js";
25
25
  import {
26
26
  EXIT
27
27
  } from "../chunk-KZERVKTD.js";
28
- import "../chunk-S3DP6DCZ.js";
28
+ import "../chunk-KBRKQMCI.js";
29
29
  import {
30
30
  claudeSettings
31
- } from "../chunk-CBOHZ6NY.js";
31
+ } from "../chunk-SRRJLA24.js";
32
32
  import {
33
33
  readJsonSafe,
34
34
  writeJsonAtomic
35
35
  } from "../chunk-AQFEP65J.js";
36
- import "../chunk-TXKAPRIA.js";
37
- import "../chunk-QOMUEL44.js";
36
+ import "../chunk-5EX2SD5N.js";
37
+ import "../chunk-2ROXUEZV.js";
38
38
  import {
39
39
  isRecord
40
- } from "../chunk-H7QTZTIE.js";
40
+ } from "../chunk-TDLEFGAM.js";
41
41
 
42
42
  // bin/pushary-bell.ts
43
43
  exitOnHelpFlag("bell");
@@ -8,39 +8,39 @@ import {
8
8
  describeIdleWindow,
9
9
  startProcessOwnership,
10
10
  withSpawnProcessOwnership
11
- } from "../chunk-PRVG3COH.js";
11
+ } from "../chunk-D3TVRNZC.js";
12
12
  import {
13
13
  announceSubscriptionRequired,
14
14
  isDeferAnswer
15
- } from "../chunk-AZMHKUUU.js";
15
+ } from "../chunk-MG523AVN.js";
16
16
  import {
17
17
  claudeTranscriptPath,
18
18
  loadSessionKey,
19
19
  registerTranscript,
20
20
  registeredTranscriptEnabled
21
- } from "../chunk-2FOAQ64R.js";
22
- import "../chunk-VTR4VRL2.js";
21
+ } from "../chunk-GSBOLJZ4.js";
22
+ import "../chunk-DWQYCOPI.js";
23
23
  import {
24
24
  ensureDaemonRunning
25
- } from "../chunk-CJQEJWLU.js";
25
+ } from "../chunk-VFNYTVTJ.js";
26
26
  import {
27
27
  encryptRecord
28
- } from "../chunk-OUWDJIJF.js";
29
- import "../chunk-LRLF6PZ2.js";
28
+ } from "../chunk-7447DYNW.js";
29
+ import "../chunk-EPVY3JU3.js";
30
30
  import {
31
31
  transcriptsEnabled
32
- } from "../chunk-AVQ6ETA7.js";
32
+ } from "../chunk-MRR4NQ3H.js";
33
33
  import {
34
34
  needsShell,
35
35
  spawnClaude
36
36
  } from "../chunk-XHKBHWLX.js";
37
- import "../chunk-TJXCBTL4.js";
38
- import "../chunk-UM2HNUJA.js";
39
- import "../chunk-WJUYWTNT.js";
40
- import "../chunk-BUOLA2AM.js";
37
+ import "../chunk-FXJ5SEQT.js";
38
+ import "../chunk-OS6AE2MR.js";
39
+ import "../chunk-35RJKPIC.js";
40
+ import "../chunk-IDGESFKZ.js";
41
41
  import "../chunk-GMXKITVA.js";
42
42
  import "../chunk-6ICPEJY5.js";
43
- import "../chunk-GMVZYB3W.js";
43
+ import "../chunk-IKJJB4QG.js";
44
44
  import {
45
45
  DEFAULT_SESSION,
46
46
  UNNAMED_ACTION,
@@ -58,48 +58,50 @@ import {
58
58
  reportEvent,
59
59
  savePendingCommand,
60
60
  scopePathFor
61
- } from "../chunk-OSVDGVLL.js";
62
- import "../chunk-KMTSU366.js";
61
+ } from "../chunk-X427XA76.js";
62
+ import "../chunk-4JOWKPUF.js";
63
63
  import {
64
64
  askUser,
65
65
  cancelQuestion,
66
+ isAwayPaused,
66
67
  isDecisionStopped,
67
68
  isLocalQuestionHandoff,
68
69
  nextLongPollMs,
69
70
  waitForAnswer,
70
71
  waitsForThePhone,
71
72
  withdrawQuestion
72
- } from "../chunk-Y3OZ3CT6.js";
73
+ } from "../chunk-464FX6W5.js";
73
74
  import "../chunk-POFBE6ZY.js";
74
75
  import {
75
76
  SubscriptionRequiredError
76
- } from "../chunk-GVIPEBEL.js";
77
- import "../chunk-TILXDMUR.js";
78
- import "../chunk-35CMXPES.js";
77
+ } from "../chunk-FEICGRNI.js";
78
+ import "../chunk-SYUHM6V3.js";
79
+ import "../chunk-4WGKAGIU.js";
79
80
  import {
80
81
  getMachineId
81
- } from "../chunk-YFEHKEGQ.js";
82
+ } from "../chunk-NTWTU6FA.js";
82
83
  import "../chunk-7QLSKOSU.js";
83
84
  import "../chunk-KZERVKTD.js";
84
- import "../chunk-S3DP6DCZ.js";
85
- import "../chunk-CBOHZ6NY.js";
85
+ import "../chunk-KBRKQMCI.js";
86
+ import "../chunk-SRRJLA24.js";
86
87
  import "../chunk-AQFEP65J.js";
87
88
  import {
88
89
  getApiKey,
89
90
  getBaseUrl
90
- } from "../chunk-TXKAPRIA.js";
91
+ } from "../chunk-5EX2SD5N.js";
91
92
  import {
92
93
  NPX_LATEST_COMMAND
93
- } from "../chunk-QOMUEL44.js";
94
+ } from "../chunk-2ROXUEZV.js";
94
95
  import {
95
96
  DECISION_STOP_REASON,
96
97
  HAND_BACK_WITHOUT_A_PROMPT,
97
98
  KILL_REASON,
99
+ SESSION_PAUSED_REASON,
98
100
  UNVERIFIABLE_APPROVAL_REASON,
99
101
  redactSecretsFromContent,
100
102
  resolveGate,
101
103
  timeoutActionWithoutLocalPrompt
102
- } from "../chunk-H7QTZTIE.js";
104
+ } from "../chunk-TDLEFGAM.js";
103
105
 
104
106
  // src/wrapper/localPassthrough.ts
105
107
  var SIGNAL_NUMBERS = {
@@ -431,6 +433,7 @@ var BoundedInputQueue = class {
431
433
 
432
434
  // src/wrapper/approver.ts
433
435
  var DEFAULT_MAX_CONCURRENT = 16;
436
+ var NO_APPROVAL_IN_TIME = "No approval received in time";
434
437
  var NO_LOCAL_APPROVAL_UI = "This remote session has no local approval UI. Continue in an interactive session or choose a mode that permits remote approval.";
435
438
  var SUBSCRIPTION_REQUIRED_NO_LOCAL_APPROVAL_UI = "Your Pushary workspace has no active subscription, so approvals cannot reach your phone, and this remote session has no local approval UI. Continue in an interactive session.";
436
439
  var DEFAULT_APPROVAL_WINDOW_MS = 5 * 6e4;
@@ -456,7 +459,7 @@ var createRemoteApprover = (deps) => {
456
459
  const ceiling = policy.timeoutAction === "wait" ? MAX_APPROVAL_WINDOW_MS : base;
457
460
  return Math.min(Math.max(base, 15e3), Math.max(ceiling, 15e3));
458
461
  };
459
- const applyTimeout = (policy, input) => timeoutActionWithoutLocalPrompt(policy) === "approve" ? allow(input) : deny("No approval received in time");
462
+ const applyTimeout = (policy, input, paused) => timeoutActionWithoutLocalPrompt(policy) === "approve" ? allow(input) : deny(paused ? SESSION_PAUSED_REASON : NO_APPROVAL_IN_TIME);
460
463
  const waitForPhone = async (entry, correlationId, windowMs) => {
461
464
  const deadline = now() + windowMs;
462
465
  while (now() < deadline && !entry.controller.signal.aborted) {
@@ -558,7 +561,7 @@ var createRemoteApprover = (deps) => {
558
561
  return answer.value === "yes" ? allow(input) : deny("Denied from your phone");
559
562
  }
560
563
  if (localHandoff) return deny(NO_LOCAL_APPROVAL_UI);
561
- return applyTimeout(policy, input);
564
+ return applyTimeout(policy, input, isAwayPaused(question));
562
565
  } catch {
563
566
  return deny("Approval unavailable; denied by default");
564
567
  } finally {