@pushary/agent-hooks 1.7.3 → 1.8.1
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.
- package/CHANGELOG.md +14 -0
- package/README.md +39 -19
- package/data/SKILL.md +8 -8
- package/data/cowork/SKILL.md +2 -2
- package/data/cursor-plugin/README.md +2 -2
- package/data/cursor-plugin/scripts/pushary-gate.mjs +2 -21
- package/data/cursor-plugin/scripts/redaction.mjs +53 -0
- package/data/cursor-plugin/skills/pushary/SKILL.md +7 -7
- package/data/vscode-plugin/README.md +4 -4
- package/data/vscode-plugin/scripts/pushary-gate.mjs +3 -23
- package/data/vscode-plugin/scripts/redaction.mjs +53 -0
- package/data/vscode-plugin/skills/pushary/SKILL.md +7 -7
- package/dist/bin/pushary-bell-hook.js +23 -23
- package/dist/bin/pushary-bell.js +9 -9
- package/dist/bin/pushary-claude.js +26 -26
- package/dist/bin/pushary-clean.js +97 -111
- package/dist/bin/pushary-codex-bridge.js +18 -18
- package/dist/bin/pushary-codex-hook.js +42 -43
- package/dist/bin/pushary-codex.js +13 -13
- package/dist/bin/pushary-connect.js +13 -13
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +27 -27
- package/dist/bin/pushary-disconnect.js +18 -18
- package/dist/bin/pushary-doctor.js +92 -86
- package/dist/bin/pushary-elicitation-hook.js +25 -25
- package/dist/bin/pushary-gemini-bridge.js +12 -12
- package/dist/bin/pushary-gemini-hook.js +29 -30
- package/dist/bin/pushary-hook.js +32 -32
- package/dist/bin/pushary-login.js +12 -12
- package/dist/bin/pushary-logout.js +7 -7
- package/dist/bin/pushary-mcp.js +85 -28
- package/dist/bin/pushary-mode.js +9 -9
- package/dist/bin/pushary-notification-hook.js +28 -28
- package/dist/bin/pushary-opencode-hook.js +28 -29
- package/dist/bin/pushary-permission-denied-hook.js +32 -32
- package/dist/bin/pushary-permission-hook.js +31 -31
- package/dist/bin/pushary-post-hook.js +28 -28
- package/dist/bin/pushary-prompt-hook.js +28 -28
- package/dist/bin/pushary-session-end-hook.js +28 -28
- package/dist/bin/pushary-session-start-hook.js +32 -32
- package/dist/bin/pushary-setup.js +62 -129
- package/dist/bin/pushary-stats.js +5 -5
- package/dist/bin/pushary-status.js +15 -15
- package/dist/bin/pushary-stop-hook.js +28 -28
- package/dist/bin/pushary-stopfailure-hook.js +28 -28
- package/dist/bin/pushary-suggestions.js +4 -4
- package/dist/bin/pushary-transcript-register.js +19 -19
- package/dist/bin/pushary-transcripts.js +7 -7
- package/dist/bin/pushary-upgrade.js +236 -110
- package/dist/bin/pushary-wait.js +9 -9
- package/dist/bin/pushary.js +6 -6
- package/dist/{chunk-DUQO4C4I.js → chunk-2SLCHCVN.js} +3 -3
- package/dist/{chunk-YYCTF4Q5.js → chunk-35X5UDML.js} +7 -7
- package/dist/{chunk-OVC5QXN5.js → chunk-4A3UQERT.js} +1 -1
- package/dist/chunk-5DGNEUXK.js +73 -0
- package/dist/{chunk-62CVUUNU.js → chunk-5Y2DY6X4.js} +2 -2
- package/dist/{chunk-G4GLCLKK.js → chunk-5YY3IJT4.js} +159 -36
- package/dist/{chunk-KJXUIPS4.js → chunk-64H5IZZD.js} +28 -16
- package/dist/{chunk-OTNOANMX.js → chunk-6TK4IY3A.js} +8 -8
- package/dist/{chunk-LPNJLKBQ.js → chunk-72WOCX22.js} +1 -1
- package/dist/{chunk-IRZG3HEU.js → chunk-7ITOAMCD.js} +1 -1
- package/dist/{chunk-G62JL2NT.js → chunk-7U7ZWUTR.js} +1 -1
- package/dist/{chunk-IPSRMLCJ.js → chunk-ASWRMBOM.js} +1 -1
- package/dist/{chunk-5S2VBSJI.js → chunk-CB23AO4D.js} +2 -2
- package/dist/{chunk-VKVSRCBO.js → chunk-CHMOVJ2M.js} +1 -1
- package/dist/{chunk-BGBLLHZ4.js → chunk-DLEKCAQL.js} +1 -1
- package/dist/{chunk-EEEJ2JIL.js → chunk-DVW2MYCM.js} +3 -3
- package/dist/{chunk-AWVNAYGD.js → chunk-DXBS2EL7.js} +1 -1
- package/dist/{chunk-ZKJ34T2F.js → chunk-EIGJYCOE.js} +31 -17
- package/dist/{chunk-U6RVDUJ5.js → chunk-F6NTWZZC.js} +283 -41
- package/dist/{chunk-7RUJKCR4.js → chunk-FLQPM2GQ.js} +2 -2
- package/dist/{chunk-TCVMK6GI.js → chunk-GBNQXYN4.js} +4 -4
- package/dist/{chunk-G6RRVJOG.js → chunk-GH6KGWMC.js} +6 -6
- package/dist/chunk-GU3EJCVV.js +43 -0
- package/dist/{chunk-6JQFIHZC.js → chunk-HBWC3JVZ.js} +3 -3
- package/dist/{chunk-IWJKSIB4.js → chunk-HMNR7B2F.js} +1 -1
- package/dist/{chunk-NOI3XTCY.js → chunk-HOEB6AKG.js} +1 -1
- package/dist/{chunk-62VT3BPH.js → chunk-HQRCVEI2.js} +32 -17
- package/dist/{chunk-MIXWMA73.js → chunk-HTEGMMT3.js} +3 -3
- package/dist/{chunk-BIH2Y2HP.js → chunk-HVDMGDYV.js} +2 -2
- package/dist/{chunk-VE7TXLR4.js → chunk-I4HKMUZQ.js} +3 -3
- package/dist/{chunk-YPUBA2FL.js → chunk-INSMZNS7.js} +5 -5
- package/dist/{chunk-G3OMOTJ4.js → chunk-JRQRNNGA.js} +3 -3
- package/dist/{chunk-N5MZH5A6.js → chunk-KB2FTO4O.js} +4 -4
- package/dist/{chunk-O3JDWNLU.js → chunk-LK2S5XIV.js} +183 -28
- package/dist/{chunk-YC27PROF.js → chunk-MMVCI6DT.js} +20 -5
- package/dist/{chunk-3EUBQARL.js → chunk-MYHG7PX2.js} +1 -1
- package/dist/{chunk-EADXTSHE.js → chunk-N6JJJTPH.js} +6 -3
- package/dist/{chunk-PRNFSORD.js → chunk-NHCUXTEM.js} +1 -1
- package/dist/{chunk-KEL5MXRL.js → chunk-NOLPURUI.js} +1 -1
- package/dist/{chunk-WTJM7PSL.js → chunk-OCVVSRDA.js} +3 -3
- package/dist/{chunk-R6OYCUNK.js → chunk-ON5JW2HP.js} +1 -1
- package/dist/{chunk-UOQRM7JQ.js → chunk-QB6QYZ6B.js} +5 -5
- package/dist/{chunk-7OA34PNW.js → chunk-T55H6YEP.js} +40 -10
- package/dist/{chunk-D6OODZCM.js → chunk-TJMAXQ5Q.js} +2 -2
- package/dist/{chunk-WGMW7OXW.js → chunk-TKQ6LZQZ.js} +6 -6
- package/dist/{chunk-WPX67AKT.js → chunk-UMMCHS6P.js} +1 -1
- package/dist/{chunk-MZBRL5H5.js → chunk-UPLV5AJF.js} +108 -25
- package/dist/{chunk-P2VIBGXA.js → chunk-URNXU3FO.js} +1 -1
- package/dist/{chunk-EUGNVLMG.js → chunk-VFFHQC7W.js} +3 -1
- package/dist/{chunk-Y4ZH64UK.js → chunk-VZMIHERK.js} +2 -2
- package/dist/{chunk-RLW3JWDY.js → chunk-W2LJ4O3A.js} +2 -2
- package/dist/{chunk-LHC45YUC.js → chunk-ZJYJAD52.js} +1 -1
- package/dist/{reapply-PH5T4BGR.js → reapply-ICGTBHVE.js} +9 -9
- package/dist/src/index.js +20 -20
- package/package.json +1 -1
- package/dist/chunk-5GB22QYL.js +0 -255
- package/dist/chunk-VYPJHGG6.js +0 -92
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.8.1
|
|
4
|
+
|
|
5
|
+
- The CLI is now also published as `pushary`, from the same build as `@pushary/agent-hooks`, and the commands it prints now show `npx pushary@latest`. `npx @pushary/agent-hooks@latest` keeps working.
|
|
6
|
+
|
|
7
|
+
## 1.8.0
|
|
8
|
+
|
|
9
|
+
- **An approval you give after auto mode blocks a call is no longer lost when the agent rewords the call.** Your approval covers only the exact call that was blocked. Claude Code tells the agent only that it may retry, and an agent that sent a changed command was blocked again, so the approval you gave from your phone or the Mac notch did nothing. Pushary now refuses the first changed call and tells the agent to send the blocked call again unchanged. If the agent ends its turn without sending it, Pushary tells it to send it.
|
|
10
|
+
- Claude Code no longer tells your phone and the Mac notch that it is waiting for you while its background tasks are still running. Claude Code reports itself idle after a minute at the prompt even when it will continue by itself once those tasks finish. A question from the agent still reaches you as before.
|
|
11
|
+
- A prompt longer than 500 characters, or a shell command longer than about 490, no longer stops your agent at its next phone approval. The hook sent the whole text and the server refused it, so Claude Code halted with "Pushary could not create a verifiable approval" and Codex and OpenCode denied the call. The question, intent, action, blocker and agent name are now cut to what the card can show, and a cut line ends in "…". The server now does the same for every older CLI, so this fix does not wait for you to update. (#1604)
|
|
12
|
+
- A line cut to fit, including a long diff, never ends on half an emoji. The server stores these lines as JSON, and Postgres refuses a half character, so the approval failed to save.
|
|
13
|
+
- `pushary doctor`, `pushary upgrade`, `pushary clean` and automatic updates recognise a global install under either package name, `@pushary/agent-hooks` or `pushary`. A machine keeps the name it already has, and setup on a new machine installs the name it was run from. Nothing changes for an existing install.
|
|
14
|
+
- `pushary doctor` flags a machine with both names installed globally, because uninstalling either one removes every Pushary command.
|
|
15
|
+
- `pushary upgrade` no longer installs over an automatic update that is still running. It says another upgrade is running, with its process id, and stops.
|
|
16
|
+
|
|
3
17
|
## 1.7.3
|
|
4
18
|
|
|
5
19
|
- **Approval text never hides part of a command.** The value after a credential name is now hidden only up to the first space, quote or piece of shell syntax. 1.7.2 could still hide a command: `eval TOKEN="x; rm -rf ~"` read `eval TOKEN="[redacted]"`, `password= reboot` read `password= [redacted]`, and `rm -rf {password=x,/}` hid the `,/` that also deletes `/`. A private key block is hidden only when it holds nothing but the key. Tokens, keys and quoted values without spaces are hidden as before. A quoted value with spaces now shows what follows its first word, as in `--password='[redacted] pass'`. Logs and error reports still hide the whole value.
|
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Pushary
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/pushary)
|
|
4
|
+
[](https://www.npmjs.com/package/pushary)
|
|
5
|
+
[](./LICENSE)
|
|
6
6
|
|
|
7
7
|
Push notifications, questions, and permission approvals for AI agents. Get a push when your agent finishes work, answer its questions from your phone, and approve risky commands before they run. Works with Claude Code, Codex, Cursor, VS Code, OpenCode, Gemini CLI, Hermes, and fx.
|
|
8
8
|
|
|
9
9
|
Your agent froze waiting for a yes. This is how it reaches you.
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx
|
|
12
|
+
npx pushary@latest setup
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
One command wires up the MCP server, the permission hooks, and the agent instruction files. Then you close your laptop and the agent still reaches you.
|
|
@@ -30,7 +30,7 @@ If your agent runs in front of you and you only need to know when it finishes,
|
|
|
30
30
|
you do not need any of the above and you should not pay for it:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
npx
|
|
33
|
+
npx pushary@latest bell
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
That is the whole thing. It makes a noise in your terminal and raises a desktop
|
|
@@ -80,7 +80,7 @@ talking. Set `PUSHARY_BELL_QUIET=1` if you would rather it never mentioned it.
|
|
|
80
80
|
Run setup:
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
npx
|
|
83
|
+
npx pushary@latest setup
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
It shows a QR code and short link. Open either in the Pushary app, check that the
|
|
@@ -91,15 +91,35 @@ pairing.
|
|
|
91
91
|
For CI or another non-interactive install, pass an existing key explicitly:
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
npx
|
|
94
|
+
npx pushary@latest setup --key pk_xxx.sk_xxx --skip-phone
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
Verify it worked:
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
|
-
npx
|
|
100
|
+
npx pushary@latest doctor
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
Setup installs this package globally with npm, at the version you ran, because the
|
|
104
|
+
hooks run from that install. You never install it by hand.
|
|
105
|
+
|
|
106
|
+
## Remove it
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npx pushary@latest clean
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
It removes the hooks, the MCP entries, the stored key and the background service.
|
|
113
|
+
Add `--dry-run` to see what it would remove first.
|
|
114
|
+
|
|
115
|
+
## One package, two names
|
|
116
|
+
|
|
117
|
+
`pushary` and `@pushary/agent-hooks` are the same build, published at the same
|
|
118
|
+
version. Every older command keeps working: `npx @pushary/agent-hooks@latest setup`
|
|
119
|
+
does exactly what `npx pushary@latest setup` does.
|
|
120
|
+
|
|
121
|
+
`pip install pushary` is a different package: the Pushary Python SDK.
|
|
122
|
+
|
|
103
123
|
## Supported agents
|
|
104
124
|
|
|
105
125
|
| Agent | Notify | Ask | Gate |
|
|
@@ -215,19 +235,19 @@ defaults rather than waiting for a keypress that can never arrive.
|
|
|
215
235
|
|
|
216
236
|
```bash
|
|
217
237
|
# Everything detected on this machine, no prompts
|
|
218
|
-
npx
|
|
238
|
+
npx pushary@latest setup --key-stdin --yes --skip-phone < key.txt
|
|
219
239
|
|
|
220
240
|
# Named agents only
|
|
221
|
-
npx
|
|
241
|
+
npx pushary@latest setup --agents claude_code,codex --yes --skip-phone
|
|
222
242
|
|
|
223
243
|
# Store the key and configure nothing
|
|
224
|
-
npx
|
|
244
|
+
npx pushary@latest setup --agents none --yes
|
|
225
245
|
|
|
226
246
|
# Show what would be written, change nothing
|
|
227
|
-
npx
|
|
247
|
+
npx pushary@latest setup --agents auto --dry-run
|
|
228
248
|
|
|
229
249
|
# One machine-readable object on stdout; every human line goes to stderr
|
|
230
|
-
npx
|
|
250
|
+
npx pushary@latest setup --agents auto --yes --json
|
|
231
251
|
```
|
|
232
252
|
|
|
233
253
|
Prefer `--key-stdin` over `--key` in CI: `--key` puts the secret in argv, where
|
|
@@ -240,9 +260,9 @@ that failed), `130` cancelled with Ctrl-C.
|
|
|
240
260
|
Examples:
|
|
241
261
|
|
|
242
262
|
```bash
|
|
243
|
-
npx
|
|
244
|
-
npx
|
|
245
|
-
npx
|
|
263
|
+
npx pushary@latest mode push_only --for 30m
|
|
264
|
+
npx pushary@latest wait 45
|
|
265
|
+
npx pushary@latest clean
|
|
246
266
|
```
|
|
247
267
|
|
|
248
268
|
## Updates
|
|
@@ -254,7 +274,7 @@ already run from, then refreshes each agent's config the way `pushary upgrade` d
|
|
|
254
274
|
The session does not wait for it, and the new version runs from the next hook call.
|
|
255
275
|
|
|
256
276
|
- It stays within the same major version. A new major shows in `pushary doctor`, and
|
|
257
|
-
you move to it with `npx
|
|
277
|
+
you move to it with `npx pushary@latest upgrade`.
|
|
258
278
|
- It only updates a global npm install. A copy run through `npx`, a plugin, or the Mac
|
|
259
279
|
app updates by its own route.
|
|
260
280
|
- It never uses `sudo`. If npm cannot write to the global folder, the attempt is
|
|
@@ -311,7 +331,7 @@ MIT
|
|
|
311
331
|
|
|
312
332
|
## Claude Desktop and Cowork
|
|
313
333
|
|
|
314
|
-
Run `pushary cowork` (or `npx
|
|
334
|
+
Run `pushary cowork` (or `npx pushary@latest cowork`) for connector setup. `pushary setup --agents cowork` opens the same setup path without storing a CLI key or writing hooks. Add the OAuth URL in Claude and sign in with the account used by your Pushary phone/Mac app.
|
|
315
335
|
|
|
316
336
|
Use `pushary cowork --instructions` for standing instructions or `pushary cowork --skill > SKILL.md` to export the bundled skill for upload. `--json` reports `manual_setup_required`; printing a link does not prove a connection. Ask a test question in Claude, answer it in Pushary, and verify Claude receives it.
|
|
317
337
|
|
package/data/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pushary
|
|
3
|
-
version: 0.11.
|
|
3
|
+
version: 0.11.2
|
|
4
4
|
description: Push notifications and human-in-the-loop for AI agents. Use this whenever a running agent needs a human and nobody is at the terminal, such as before an irreversible or destructive action, before spending money, deploying, force-pushing or deleting, when blocked on a decision outside your authority, when running unattended and you hit a genuine ambiguity, when another skill's workflow says to confirm with the user, and when a long task finishes or fails with nobody watching. Also use it when the user says things like keep going and ping me on my phone if you need anything, notify me when my agent needs me, approve from my phone, ask me questions while I am away from the terminal, run this overnight, keep working while I am in a meeting, I am stepping away, do not wait for me, or wants a long task to run unattended. Send alerts when tasks finish or fail, ask questions (yes/no, multiple choice, or free text) via push, and get answers from their connected devices. Use these tools proactively - do not wait for the user to ask for notifications. Every question and answer is recorded, so an unattended run stays reviewable afterwards. Works with Claude Code, Codex, Cursor, VS Code, Windsurf, Hermes, Lovable, or any MCP client; no Claude Max subscription required. Pushary is a hosted service, $9.99/mo after a 3-day card-first trial.
|
|
5
5
|
metadata:
|
|
6
6
|
hermes:
|
|
@@ -203,7 +203,7 @@ into the agent configuration files it wires, not into `~/.pushary/config.json`.
|
|
|
203
203
|
machine the app set up therefore prints `mac-app` and nothing else. Treat it as
|
|
204
204
|
ready.
|
|
205
205
|
|
|
206
|
-
Check it with `npx
|
|
206
|
+
Check it with `npx pushary@latest status --json`. The exit code is the answer:
|
|
207
207
|
|
|
208
208
|
| Code | Meaning |
|
|
209
209
|
| --- | --- |
|
|
@@ -214,7 +214,7 @@ Check it with `npx @pushary/agent-hooks@latest status --json`. The exit code is
|
|
|
214
214
|
| 6 | No device can answer |
|
|
215
215
|
| 8 | Pushary could not be reached |
|
|
216
216
|
|
|
217
|
-
On 6, the user needs to connect a phone: `npx
|
|
217
|
+
On 6, the user needs to connect a phone: `npx pushary@latest connect`. That adds a phone and rewrites no agent configuration.
|
|
218
218
|
|
|
219
219
|
If `mac-app` printed, the Mac app is installed here and it may also own the hooks. Read the hook command to know, because the command is the record:
|
|
220
220
|
|
|
@@ -241,7 +241,7 @@ The command line works on macOS too. Offer it if the user prefers the terminal,
|
|
|
241
241
|
There is no Mac app for these machines. Use the command line. It is fully supported.
|
|
242
242
|
|
|
243
243
|
```bash
|
|
244
|
-
npx
|
|
244
|
+
npx pushary@latest setup
|
|
245
245
|
```
|
|
246
246
|
|
|
247
247
|
Node 20.17+, 22.13+ or 23.5+ is necessary. Then the user needs a phone to answer on:
|
|
@@ -258,7 +258,7 @@ Run `setup`. It reads the key the app signed in with, so it mints no second key,
|
|
|
258
258
|
### What setup does
|
|
259
259
|
|
|
260
260
|
```bash
|
|
261
|
-
npx
|
|
261
|
+
npx pushary@latest setup
|
|
262
262
|
```
|
|
263
263
|
|
|
264
264
|
Setup pairs first and configures MCP, hooks, permissions and the skill only once pairing succeeds. Until someone completes the steps below, nothing is written and this machine has no Pushary. Treat pairing as the task, not as a prompt to wait out.
|
|
@@ -281,7 +281,7 @@ If `PUSHARY_API_KEY` is already in the environment or in an existing MCP config,
|
|
|
281
281
|
No app on their phone yet? They can get it at https://pushary.com/download. Or answer through the browser instead:
|
|
282
282
|
|
|
283
283
|
```bash
|
|
284
|
-
npx
|
|
284
|
+
npx pushary@latest setup --connect browser
|
|
285
285
|
```
|
|
286
286
|
|
|
287
287
|
This is web push, not a login tab. It prints a QR for the user's own subscribe page, and it waits for a browser on that page to subscribe. On iOS the user must first add that page to the Home Screen, because iOS sends web push only from an installed page.
|
|
@@ -291,7 +291,7 @@ Manual MCP configuration also works, but it needs a key, so the user signs up fi
|
|
|
291
291
|
After setup, verify with:
|
|
292
292
|
|
|
293
293
|
```bash
|
|
294
|
-
npx
|
|
294
|
+
npx pushary@latest doctor
|
|
295
295
|
```
|
|
296
296
|
|
|
297
297
|
## Answer surfaces and account boundaries
|
|
@@ -303,7 +303,7 @@ npx @pushary/agent-hooks@latest doctor
|
|
|
303
303
|
| Slack | Answer through buttons, menus or text modals when the integration and intended recipient are configured. |
|
|
304
304
|
| Browser | Open the decision page as a fallback; browser notification delivery requires permission. |
|
|
305
305
|
|
|
306
|
-
Personal setup connects the operator's devices. For a Mac, install from https://pushary.com/download, sign in to the same personal account and connect your agents in the app. Run `npx
|
|
306
|
+
Personal setup connects the operator's devices. For a Mac, install from https://pushary.com/download, sign in to the same personal account and connect your agents in the app. Run `npx pushary@latest doctor`, then request one harmless test question and verify it reaches the intended surface. Test phone fallback while away from the Mac; do not infer delivery from a successful API call alone.
|
|
307
307
|
|
|
308
308
|
Partner customers use scoped enrollment links issued by their application. Do not enroll them into the operator's account or send their decisions through personal tools. The Mac notch currently uses the personal account/session API; do not promise a Partner customer inbox on Mac. See https://pushary.com/docs/agents/embed for Partner setup.
|
|
309
309
|
|
package/data/cowork/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pushary-cowork
|
|
3
|
-
version: 0.4.
|
|
3
|
+
version: 0.4.3
|
|
4
4
|
description: Phone notifications and human-in-the-loop for Claude Cowork through the Pushary connector. Use inside a Cowork session whenever you need a human and nobody is watching the session, such as before an irreversible or destructive action, before spending money, deploying, force-pushing or deleting, when blocked on a decision outside your authority, when running unattended and you hit a genuine ambiguity, when another skill's workflow says to confirm with the user, and when a task finishes or fails with nobody watching. Also use it when the user says things like ping me on my phone when this is done, ask me before doing anything risky, keep me in the loop while I am away, or notify me if you get stuck. Sends completion alerts, asks questions (yes/no, multiple choice, or free text) via push, and gets answers from connected devices. This Pushary connector is cooperative; it does not install native Cowork permission hooks. Pushary is a hosted service, $9.99/mo after a 3-day card-first trial.
|
|
5
5
|
metadata:
|
|
6
6
|
tags: notifications, push, mcp, human-in-the-loop, cowork, claude, alerts, approvals
|
|
@@ -55,7 +55,7 @@ Delivery is controlled by the user's policy: `push_first` uses presence, `push_o
|
|
|
55
55
|
| Slack | Answer through buttons, menus or text modals when the integration and intended recipient are configured. |
|
|
56
56
|
| Browser | Open the decision page as a fallback; browser notification delivery requires permission. |
|
|
57
57
|
|
|
58
|
-
Personal setup connects the operator's devices. For a Mac, install from https://pushary.com/download, sign in to the same personal account and connect your agents in the app. Run `npx
|
|
58
|
+
Personal setup connects the operator's devices. For a Mac, install from https://pushary.com/download, sign in to the same personal account and connect your agents in the app. Run `npx pushary@latest cowork` for connector setup, then ask one harmless test question inside Cowork, answer it from Pushary, and verify Cowork receives the answer. CLI doctor checks do not verify a hosted connector. Test phone fallback while away from the Mac; do not infer delivery from a successful API call alone.
|
|
59
59
|
|
|
60
60
|
Partner customers use scoped enrollment links issued by their application. Do not enroll them into the operator's account or send their decisions through personal tools. The Mac notch currently uses the personal account/session API; do not promise a Partner customer inbox on Mac. See https://pushary.com/docs/agents/embed for Partner setup.
|
|
61
61
|
|
|
@@ -35,7 +35,7 @@ Open the Marketplace panel in Cursor, search for Pushary, and click install. The
|
|
|
35
35
|
This also sets up Claude Code, Codex, and Hermes if you use them.
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npx
|
|
38
|
+
npx pushary@latest setup
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
### Option 3: Manual MCP
|
|
@@ -94,7 +94,7 @@ Timeout approvals apply only after the configured wait actually elapsed. Short e
|
|
|
94
94
|
|
|
95
95
|
## Development
|
|
96
96
|
|
|
97
|
-
`skills/pushary/SKILL.md` mirrors the Pushary skill that ships with
|
|
97
|
+
`skills/pushary/SKILL.md` mirrors the Pushary skill that ships with the Pushary CLI (`pushary` on npm). Keep the two the same.
|
|
98
98
|
|
|
99
99
|
Test the plugin locally before publishing:
|
|
100
100
|
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Pushary Cursor hooks: server policy, fenced phone approvals, and lifecycle telemetry.
|
|
3
3
|
// Dependency-free for marketplace installs. Regenerate agent-hooks/data after edits.
|
|
4
4
|
|
|
5
|
+
import { redactSecrets, redactSecretsDeep } from './redaction.mjs'
|
|
6
|
+
export { redactSecrets } from './redaction.mjs'
|
|
5
7
|
import { createHash, randomUUID } from 'node:crypto'
|
|
6
8
|
import { hostname } from 'node:os'
|
|
7
9
|
import { existsSync, readFileSync } from 'node:fs'
|
|
@@ -294,27 +296,6 @@ export const describeNetworkFailure = (error, env = process.env) => {
|
|
|
294
296
|
// line reached the lock screen verbatim.
|
|
295
297
|
const ACTION_BODY_MAX = 4000
|
|
296
298
|
const ACTION_BODY_TRUNCATION_MARKER = '\n… [truncated]'
|
|
297
|
-
const REDACTION_RULES = [
|
|
298
|
-
[/-----BEGIN[A-Z0-9 ]*PRIVATE KEY-----(?:\r?\n|(?:\\r)?\\n)?(?:(?:Proc-Type|DEK-Info): [A-Za-z0-9,-]+(?:\r?\n|(?:\\r)?\\n))*(?:[A-Za-z0-9+/=\r\n]|\\[rn])*?-----END[A-Z0-9 ]*PRIVATE KEY-----/g, '[redacted key]'],
|
|
299
|
-
[/\bsk-[A-Za-z0-9_-]{16,}\b/g, '[redacted]'],
|
|
300
|
-
[/\b[spr]k_(?:live|test)_[A-Za-z0-9]{8,}\b/g, '[redacted]'],
|
|
301
|
-
[/\bwhsec_[A-Za-z0-9]{16,}\b/g, '[redacted]'],
|
|
302
|
-
[/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b/g, '[redacted]'],
|
|
303
|
-
[/\bgithub_pat_[A-Za-z0-9_]{22,}\b/g, '[redacted]'],
|
|
304
|
-
[/\bglpat-[A-Za-z0-9_-]{20,}\b/g, '[redacted]'],
|
|
305
|
-
[/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, '[redacted]'],
|
|
306
|
-
[/\bAIza[A-Za-z0-9_-]{35}\b/g, '[redacted]'],
|
|
307
|
-
[/\bAKIA[0-9A-Z]{16}\b/g, '[redacted]'],
|
|
308
|
-
[/\bnpm_[A-Za-z0-9]{36}\b/g, '[redacted]'],
|
|
309
|
-
[/\bxai-[A-Za-z0-9]{16,}\b/g, '[redacted]'],
|
|
310
|
-
[/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, '[redacted]'],
|
|
311
|
-
[/\bbearer\s+[A-Za-z0-9._~+/=-]+/gi, 'bearer [redacted]'],
|
|
312
|
-
[/\bauthorization:\s*(?:[A-Za-z0-9_\-./+=:@%^!#]|\\[^\s'"])+/gi, 'authorization: [redacted]'],
|
|
313
|
-
[/((?:secret|token|password|passwd|api[_-]?key|access[_-]?key|client[_-]?secret|private[_-]?key)["']?(?:[ \t]*:[ \t]*|[ \t]+=[ \t]*|=))(?:(")(?:[A-Za-z0-9_\-./+=:@%^!#]|\\[^\s'"])*"|(')[A-Za-z0-9_\-./+=:@%^!#\\]*'|(["']?)(?:[A-Za-z0-9_\-./+=:@%^!#]|\\[^\s'"])+)/gi, '$1$2$3$4[redacted]$2$3'],
|
|
314
|
-
]
|
|
315
|
-
const HIGH_ENTROPY_RULE = [/[A-Za-z0-9+/]{40,}={0,2}/g, '[redacted]']
|
|
316
|
-
export const redactSecrets = (text) => REDACTION_RULES.reduce((acc, [pattern, replacement]) => acc.replace(pattern, replacement), text)
|
|
317
|
-
const redactSecretsDeep = (text) => redactSecrets(text).replace(HIGH_ENTROPY_RULE[0], HIGH_ENTROPY_RULE[1])
|
|
318
299
|
const capActionBody = (text) =>
|
|
319
300
|
text.length <= ACTION_BODY_MAX ? text : `${text.slice(0, ACTION_BODY_MAX - ACTION_BODY_TRUNCATION_MARKER.length)}${ACTION_BODY_TRUNCATION_MARKER}`
|
|
320
301
|
const deriveActionBody = (command) => capActionBody(redactSecretsDeep(command))
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Generated by scripts/generate-editor-redaction.mjs.
|
|
2
|
+
const CREDENTIAL_NAME = String.raw `(?:authorization|secret|token|password|passwd|api[_-]?key|access[_-]?key|client[_-]?secret|private[_-]?key)["']?`;
|
|
3
|
+
const CREDENTIAL_KEY = String.raw `${CREDENTIAL_NAME}\s*[=:]\s*`;
|
|
4
|
+
const DISPLAYED_CREDENTIAL_KEY = String.raw `${CREDENTIAL_NAME}(?:[ \t]*:[ \t]*|[ \t]+=[ \t]*|=)`;
|
|
5
|
+
const SHELL_INERT_UNIT = String.raw `(?:[A-Za-z0-9_\-./+=:@%^!#]|\\[^\s'"])`;
|
|
6
|
+
const SHELL_INERT_SINGLE_QUOTED_UNIT = String.raw `[A-Za-z0-9_\-./+=:@%^!#\\]`;
|
|
7
|
+
const SHELL_INERT_URL_PASSWORD_UNIT = String.raw `(?:[A-Za-z0-9_\-.+=:%^!#]|\\[^\s'"@/])`;
|
|
8
|
+
const PEM_LINE_BREAK = String.raw `(?:\r?\n|(?:\\r)?\\n)`;
|
|
9
|
+
const credentialAssignmentRule = (key, doubleQuoted, singleQuoted, bare) => ({
|
|
10
|
+
pattern: new RegExp(String.raw `(${key})(?:(")${doubleQuoted}|(')${singleQuoted}|(["']?)${bare})`, 'gi'),
|
|
11
|
+
replacement: '$1$2$3$4[redacted]$2$3',
|
|
12
|
+
});
|
|
13
|
+
const DISPLAYED_SECRET_SHAPES = {
|
|
14
|
+
separator: String.raw `[ \t]`,
|
|
15
|
+
privateKeyBody: String.raw `${PEM_LINE_BREAK}?(?:(?:Proc-Type|DEK-Info): [A-Za-z0-9,-]+${PEM_LINE_BREAK})*(?:[A-Za-z0-9+/=\r\n]|\\[rn])*?`,
|
|
16
|
+
urlPassword: `${SHELL_INERT_URL_PASSWORD_UNIT}+`,
|
|
17
|
+
authorizationValue: `${SHELL_INERT_UNIT}+`,
|
|
18
|
+
credentialAssignment: credentialAssignmentRule(DISPLAYED_CREDENTIAL_KEY, `${SHELL_INERT_UNIT}*"`, `${SHELL_INERT_SINGLE_QUOTED_UNIT}*'`, `${SHELL_INERT_UNIT}+`),
|
|
19
|
+
};
|
|
20
|
+
const CUT_OFF_SECRET_SHAPES = {
|
|
21
|
+
separator: String.raw `\s`,
|
|
22
|
+
privateKeyBody: String.raw `[\s\S]*?`,
|
|
23
|
+
urlPassword: String.raw `[^\s@/]+`,
|
|
24
|
+
authorizationValue: String.raw `\S+`,
|
|
25
|
+
credentialAssignment: credentialAssignmentRule(CREDENTIAL_KEY, String.raw `(?:\\[\s\S]|[^"\\])*(?:"|\\?$)`, "[^']*(?:'|$)", String.raw `\S+`),
|
|
26
|
+
};
|
|
27
|
+
const secretRedactionRules = (shapes) => [
|
|
28
|
+
{ pattern: /\bpk_[A-Za-z0-9_]+\.[A-Za-z0-9_]+/g, replacement: '[redacted]' },
|
|
29
|
+
{ pattern: new RegExp(String.raw `-----BEGIN[A-Z0-9 ]*PRIVATE KEY-----${shapes.privateKeyBody}-----END[A-Z0-9 ]*PRIVATE KEY-----`, 'g'), replacement: '[redacted key]' },
|
|
30
|
+
{ pattern: /\bsk-[A-Za-z0-9_-]{16,}\b/g, replacement: '[redacted]' },
|
|
31
|
+
{ pattern: /\b[spr]k_(?:live|test)_[A-Za-z0-9]{8,}\b/g, replacement: '[redacted]' },
|
|
32
|
+
{ pattern: /\bwhsec_[A-Za-z0-9]{16,}\b/g, replacement: '[redacted]' },
|
|
33
|
+
{ pattern: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b/g, replacement: '[redacted]' },
|
|
34
|
+
{ pattern: /\bgithub_pat_[A-Za-z0-9_]{22,}\b/g, replacement: '[redacted]' },
|
|
35
|
+
{ pattern: /\bglpat-[A-Za-z0-9_-]{20,}\b/g, replacement: '[redacted]' },
|
|
36
|
+
{ pattern: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, replacement: '[redacted]' },
|
|
37
|
+
{ pattern: /\bAIza[A-Za-z0-9_-]{35}\b/g, replacement: '[redacted]' },
|
|
38
|
+
{ pattern: /\bAKIA[0-9A-Z]{16}\b/g, replacement: '[redacted]' },
|
|
39
|
+
{ pattern: /\bnpm_[A-Za-z0-9]{36}\b/g, replacement: '[redacted]' },
|
|
40
|
+
{ pattern: /\bxai-[A-Za-z0-9]{16,}\b/g, replacement: '[redacted]' },
|
|
41
|
+
{ pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, replacement: '[redacted]' },
|
|
42
|
+
{ pattern: new RegExp(String.raw `\b([a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:)${shapes.urlPassword}@`, 'gi'), replacement: '$1[redacted]@' },
|
|
43
|
+
{ pattern: new RegExp(String.raw `\bbearer${shapes.separator}+[A-Za-z0-9._~+/=-]+`, 'gi'), replacement: 'bearer [redacted]' },
|
|
44
|
+
{ pattern: new RegExp(String.raw `\bauthorization:${shapes.separator}*${shapes.authorizationValue}`, 'gi'), replacement: 'authorization: [redacted]' },
|
|
45
|
+
shapes.credentialAssignment,
|
|
46
|
+
];
|
|
47
|
+
export const SECRET_REDACTION_RULES = secretRedactionRules(DISPLAYED_SECRET_SHAPES);
|
|
48
|
+
const CUT_OFF_TEXT_REDACTION_RULES = secretRedactionRules(CUT_OFF_SECRET_SHAPES);
|
|
49
|
+
const HIGH_ENTROPY_RULE = { pattern: /[A-Za-z0-9+/]{40,}={0,2}/g, replacement: '[redacted]' };
|
|
50
|
+
const applyRedactionRules = (rules, text) => rules.reduce((acc, rule) => acc.replace(rule.pattern, rule.replacement), text);
|
|
51
|
+
export const redactSecrets = (text) => applyRedactionRules(SECRET_REDACTION_RULES, text);
|
|
52
|
+
export const redactSecretsFromContent = (text) => applyRedactionRules(CUT_OFF_TEXT_REDACTION_RULES, text);
|
|
53
|
+
export const redactSecretsDeep = (text) => redactSecrets(text).replace(HIGH_ENTROPY_RULE.pattern, HIGH_ENTROPY_RULE.replacement);
|
|
@@ -196,7 +196,7 @@ into the agent configuration files it wires, not into `~/.pushary/config.json`.
|
|
|
196
196
|
machine the app set up therefore prints `mac-app` and nothing else. Treat it as
|
|
197
197
|
ready.
|
|
198
198
|
|
|
199
|
-
Check it with `npx
|
|
199
|
+
Check it with `npx pushary@latest status --json`. The exit code is the answer:
|
|
200
200
|
|
|
201
201
|
| Code | Meaning |
|
|
202
202
|
| --- | --- |
|
|
@@ -207,7 +207,7 @@ Check it with `npx @pushary/agent-hooks@latest status --json`. The exit code is
|
|
|
207
207
|
| 6 | No device can answer |
|
|
208
208
|
| 8 | Pushary could not be reached |
|
|
209
209
|
|
|
210
|
-
On 6, the user needs to connect a phone: `npx
|
|
210
|
+
On 6, the user needs to connect a phone: `npx pushary@latest connect`. That adds a phone and rewrites no agent configuration.
|
|
211
211
|
|
|
212
212
|
If `mac-app` printed, the Mac app is installed here and it may also own the hooks. Read the hook command to know, because the command is the record:
|
|
213
213
|
|
|
@@ -234,7 +234,7 @@ The command line works on macOS too. Offer it if the user prefers the terminal,
|
|
|
234
234
|
There is no Mac app for these machines. Use the command line. It is fully supported.
|
|
235
235
|
|
|
236
236
|
```bash
|
|
237
|
-
npx
|
|
237
|
+
npx pushary@latest setup
|
|
238
238
|
```
|
|
239
239
|
|
|
240
240
|
Node 20.17+, 22.13+ or 23.5+ is necessary. Then the user needs a phone to answer on:
|
|
@@ -251,7 +251,7 @@ Run `setup`. It reads the key the app signed in with, so it mints no second key,
|
|
|
251
251
|
### What setup does
|
|
252
252
|
|
|
253
253
|
```bash
|
|
254
|
-
npx
|
|
254
|
+
npx pushary@latest setup
|
|
255
255
|
```
|
|
256
256
|
|
|
257
257
|
Setup pairs first and configures MCP, hooks, permissions and the skill only once pairing succeeds. Until someone completes the steps below, nothing is written and this machine has no Pushary. Treat pairing as the task, not as a prompt to wait out.
|
|
@@ -274,7 +274,7 @@ If `PUSHARY_API_KEY` is already in the environment or in an existing MCP config,
|
|
|
274
274
|
No app on their phone yet? They can get it at https://pushary.com/download. Or answer through the browser instead:
|
|
275
275
|
|
|
276
276
|
```bash
|
|
277
|
-
npx
|
|
277
|
+
npx pushary@latest setup --connect browser
|
|
278
278
|
```
|
|
279
279
|
|
|
280
280
|
This is web push, not a login tab. It prints a QR for the user's own subscribe page, and it waits for a browser on that page to subscribe. On iOS the user must first add that page to the Home Screen, because iOS sends web push only from an installed page.
|
|
@@ -284,7 +284,7 @@ Manual MCP configuration also works, but it needs a key, so the user signs up fi
|
|
|
284
284
|
After setup, verify with:
|
|
285
285
|
|
|
286
286
|
```bash
|
|
287
|
-
npx
|
|
287
|
+
npx pushary@latest doctor
|
|
288
288
|
```
|
|
289
289
|
|
|
290
290
|
## Answer surfaces and account boundaries
|
|
@@ -296,7 +296,7 @@ npx @pushary/agent-hooks@latest doctor
|
|
|
296
296
|
| Slack | Answer through buttons, menus or text modals when the integration and intended recipient are configured. |
|
|
297
297
|
| Browser | Open the decision page as a fallback; browser notification delivery requires permission. |
|
|
298
298
|
|
|
299
|
-
Personal setup connects the operator's devices. For a Mac, install from https://pushary.com/download, sign in to the same personal account and connect your agents in the app. Run `npx
|
|
299
|
+
Personal setup connects the operator's devices. For a Mac, install from https://pushary.com/download, sign in to the same personal account and connect your agents in the app. Run `npx pushary@latest doctor`, then request one harmless test question and verify it reaches the intended surface. Test phone fallback while away from the Mac; do not infer delivery from a successful API call alone.
|
|
300
300
|
|
|
301
301
|
Partner customers use scoped enrollment links issued by their application. Do not enroll them into the operator's account or send their decisions through personal tools. The Mac notch currently uses the personal account/session API; do not promise a Partner customer inbox on Mac. See https://pushary.com/docs/agents/embed for Partner setup.
|
|
302
302
|
|
|
@@ -35,13 +35,13 @@ There are three things.
|
|
|
35
35
|
This installs the plugin, registers it with VS Code, and links your API key. It also sets up Claude Code, Codex, Cursor, and Hermes if you use them.
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npx
|
|
38
|
+
npx pushary@latest setup
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
To configure only VS Code:
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
npx
|
|
44
|
+
npx pushary@latest setup --agents vscode
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
### Option 2: Install from source
|
|
@@ -106,11 +106,11 @@ This repository uses the `.claude-plugin/plugin.json` layout, which VS Code, Git
|
|
|
106
106
|
|
|
107
107
|
The layout is portable; this plugin is not meant to be. `hooks/hooks.json` uses VS Code's flat hook form, and the gate speaks VS Code's tool names, so treat this as a VS Code plugin that happens to sit in a portable folder shape.
|
|
108
108
|
|
|
109
|
-
Claude Code users are served by `npx
|
|
109
|
+
Claude Code users are served by `npx pushary@latest setup --agents claude_code`, which installs a hook built for Claude Code's own tool names and matcher format. Do not install this directory as a Claude Code plugin as well: at best it duplicates the MCP server, and the two installs would fight over the same approvals.
|
|
110
110
|
|
|
111
111
|
## Development
|
|
112
112
|
|
|
113
|
-
`skills/pushary/SKILL.md` mirrors the Pushary skill that ships with
|
|
113
|
+
`skills/pushary/SKILL.md` mirrors the Pushary skill that ships with the Pushary CLI (`pushary` on npm). Keep the two the same.
|
|
114
114
|
|
|
115
115
|
Test the gate without VS Code:
|
|
116
116
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Pushary VS Code hooks: server policy, fenced phone approvals, and lifecycle telemetry.
|
|
3
3
|
// Dependency-free for marketplace installs. Regenerate agent-hooks/data after edits.
|
|
4
4
|
|
|
5
|
+
import { redactSecrets, redactSecretsDeep } from './redaction.mjs'
|
|
5
6
|
import { createHash, randomUUID } from 'node:crypto'
|
|
6
7
|
import { homedir, hostname } from 'node:os'
|
|
7
8
|
import { existsSync, readFileSync } from 'node:fs'
|
|
@@ -337,27 +338,6 @@ const ACTION_BODY_TRUNCATION_MARKER = '\n… [truncated]'
|
|
|
337
338
|
// One combined list used to serve both, which meant the only text this gate
|
|
338
339
|
// scrubbed was the action body. The question and the notification body carried
|
|
339
340
|
// the raw command.
|
|
340
|
-
const REDACTION_RULES = [
|
|
341
|
-
[/-----BEGIN[A-Z0-9 ]*PRIVATE KEY-----(?:\r?\n|(?:\\r)?\\n)?(?:(?:Proc-Type|DEK-Info): [A-Za-z0-9,-]+(?:\r?\n|(?:\\r)?\\n))*(?:[A-Za-z0-9+/=\r\n]|\\[rn])*?-----END[A-Z0-9 ]*PRIVATE KEY-----/g, '[redacted key]'],
|
|
342
|
-
[/\bsk-[A-Za-z0-9_-]{16,}\b/g, '[redacted]'],
|
|
343
|
-
[/\b[spr]k_(?:live|test)_[A-Za-z0-9]{8,}\b/g, '[redacted]'],
|
|
344
|
-
[/\bwhsec_[A-Za-z0-9]{16,}\b/g, '[redacted]'],
|
|
345
|
-
[/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b/g, '[redacted]'],
|
|
346
|
-
[/\bgithub_pat_[A-Za-z0-9_]{22,}\b/g, '[redacted]'],
|
|
347
|
-
[/\bglpat-[A-Za-z0-9_-]{20,}\b/g, '[redacted]'],
|
|
348
|
-
[/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, '[redacted]'],
|
|
349
|
-
[/\bAIza[A-Za-z0-9_-]{35}\b/g, '[redacted]'],
|
|
350
|
-
[/\bAKIA[0-9A-Z]{16}\b/g, '[redacted]'],
|
|
351
|
-
[/\bnpm_[A-Za-z0-9]{36}\b/g, '[redacted]'],
|
|
352
|
-
[/\bxai-[A-Za-z0-9]{16,}\b/g, '[redacted]'],
|
|
353
|
-
[/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, '[redacted]'],
|
|
354
|
-
[/\bbearer\s+[A-Za-z0-9._~+/=-]+/gi, 'bearer [redacted]'],
|
|
355
|
-
[/\bauthorization:\s*(?:[A-Za-z0-9_\-./+=:@%^!#]|\\[^\s'"])+/gi, 'authorization: [redacted]'],
|
|
356
|
-
[/((?:secret|token|password|passwd|api[_-]?key|access[_-]?key|client[_-]?secret|private[_-]?key)["']?(?:[ \t]*:[ \t]*|[ \t]+=[ \t]*|=))(?:(")(?:[A-Za-z0-9_\-./+=:@%^!#]|\\[^\s'"])*"|(')[A-Za-z0-9_\-./+=:@%^!#\\]*'|(["']?)(?:[A-Za-z0-9_\-./+=:@%^!#]|\\[^\s'"])+)/gi, '$1$2$3$4[redacted]$2$3'],
|
|
357
|
-
]
|
|
358
|
-
const HIGH_ENTROPY_RULE = [/[A-Za-z0-9+/]{40,}={0,2}/g, '[redacted]']
|
|
359
|
-
const redactSecrets = (text) => REDACTION_RULES.reduce((acc, [pattern, replacement]) => acc.replace(pattern, replacement), text)
|
|
360
|
-
const redactSecretsDeep = (text) => redactSecrets(text).replace(HIGH_ENTROPY_RULE[0], HIGH_ENTROPY_RULE[1])
|
|
361
341
|
const capActionBody = (text) =>
|
|
362
342
|
text.length <= ACTION_BODY_MAX ? text : `${text.slice(0, ACTION_BODY_MAX - ACTION_BODY_TRUNCATION_MARKER.length)}${ACTION_BODY_TRUNCATION_MARKER}`
|
|
363
343
|
const deriveActionBody = (command) => capActionBody(redactSecretsDeep(command))
|
|
@@ -723,9 +703,9 @@ const main = async () => {
|
|
|
723
703
|
|
|
724
704
|
const apiKey = resolveApiKey()
|
|
725
705
|
if (!apiKey) {
|
|
726
|
-
diag('no API key found (PUSHARY_API_KEY, the plugin .mcp.json, or ~/.pushary/config.json). Run: npx
|
|
706
|
+
diag('no API key found (PUSHARY_API_KEY, the plugin .mcp.json, or ~/.pushary/config.json). Run: npx pushary@latest setup')
|
|
727
707
|
return respond(
|
|
728
|
-
unresolved(input.tool_name, 'Pushary is not configured: run `npx
|
|
708
|
+
unresolved(input.tool_name, 'Pushary is not configured: run `npx pushary@latest setup` (get a key at https://pushary.com) to route this approval to your phone.')
|
|
729
709
|
)
|
|
730
710
|
}
|
|
731
711
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Generated by scripts/generate-editor-redaction.mjs.
|
|
2
|
+
const CREDENTIAL_NAME = String.raw `(?:authorization|secret|token|password|passwd|api[_-]?key|access[_-]?key|client[_-]?secret|private[_-]?key)["']?`;
|
|
3
|
+
const CREDENTIAL_KEY = String.raw `${CREDENTIAL_NAME}\s*[=:]\s*`;
|
|
4
|
+
const DISPLAYED_CREDENTIAL_KEY = String.raw `${CREDENTIAL_NAME}(?:[ \t]*:[ \t]*|[ \t]+=[ \t]*|=)`;
|
|
5
|
+
const SHELL_INERT_UNIT = String.raw `(?:[A-Za-z0-9_\-./+=:@%^!#]|\\[^\s'"])`;
|
|
6
|
+
const SHELL_INERT_SINGLE_QUOTED_UNIT = String.raw `[A-Za-z0-9_\-./+=:@%^!#\\]`;
|
|
7
|
+
const SHELL_INERT_URL_PASSWORD_UNIT = String.raw `(?:[A-Za-z0-9_\-.+=:%^!#]|\\[^\s'"@/])`;
|
|
8
|
+
const PEM_LINE_BREAK = String.raw `(?:\r?\n|(?:\\r)?\\n)`;
|
|
9
|
+
const credentialAssignmentRule = (key, doubleQuoted, singleQuoted, bare) => ({
|
|
10
|
+
pattern: new RegExp(String.raw `(${key})(?:(")${doubleQuoted}|(')${singleQuoted}|(["']?)${bare})`, 'gi'),
|
|
11
|
+
replacement: '$1$2$3$4[redacted]$2$3',
|
|
12
|
+
});
|
|
13
|
+
const DISPLAYED_SECRET_SHAPES = {
|
|
14
|
+
separator: String.raw `[ \t]`,
|
|
15
|
+
privateKeyBody: String.raw `${PEM_LINE_BREAK}?(?:(?:Proc-Type|DEK-Info): [A-Za-z0-9,-]+${PEM_LINE_BREAK})*(?:[A-Za-z0-9+/=\r\n]|\\[rn])*?`,
|
|
16
|
+
urlPassword: `${SHELL_INERT_URL_PASSWORD_UNIT}+`,
|
|
17
|
+
authorizationValue: `${SHELL_INERT_UNIT}+`,
|
|
18
|
+
credentialAssignment: credentialAssignmentRule(DISPLAYED_CREDENTIAL_KEY, `${SHELL_INERT_UNIT}*"`, `${SHELL_INERT_SINGLE_QUOTED_UNIT}*'`, `${SHELL_INERT_UNIT}+`),
|
|
19
|
+
};
|
|
20
|
+
const CUT_OFF_SECRET_SHAPES = {
|
|
21
|
+
separator: String.raw `\s`,
|
|
22
|
+
privateKeyBody: String.raw `[\s\S]*?`,
|
|
23
|
+
urlPassword: String.raw `[^\s@/]+`,
|
|
24
|
+
authorizationValue: String.raw `\S+`,
|
|
25
|
+
credentialAssignment: credentialAssignmentRule(CREDENTIAL_KEY, String.raw `(?:\\[\s\S]|[^"\\])*(?:"|\\?$)`, "[^']*(?:'|$)", String.raw `\S+`),
|
|
26
|
+
};
|
|
27
|
+
const secretRedactionRules = (shapes) => [
|
|
28
|
+
{ pattern: /\bpk_[A-Za-z0-9_]+\.[A-Za-z0-9_]+/g, replacement: '[redacted]' },
|
|
29
|
+
{ pattern: new RegExp(String.raw `-----BEGIN[A-Z0-9 ]*PRIVATE KEY-----${shapes.privateKeyBody}-----END[A-Z0-9 ]*PRIVATE KEY-----`, 'g'), replacement: '[redacted key]' },
|
|
30
|
+
{ pattern: /\bsk-[A-Za-z0-9_-]{16,}\b/g, replacement: '[redacted]' },
|
|
31
|
+
{ pattern: /\b[spr]k_(?:live|test)_[A-Za-z0-9]{8,}\b/g, replacement: '[redacted]' },
|
|
32
|
+
{ pattern: /\bwhsec_[A-Za-z0-9]{16,}\b/g, replacement: '[redacted]' },
|
|
33
|
+
{ pattern: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b/g, replacement: '[redacted]' },
|
|
34
|
+
{ pattern: /\bgithub_pat_[A-Za-z0-9_]{22,}\b/g, replacement: '[redacted]' },
|
|
35
|
+
{ pattern: /\bglpat-[A-Za-z0-9_-]{20,}\b/g, replacement: '[redacted]' },
|
|
36
|
+
{ pattern: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g, replacement: '[redacted]' },
|
|
37
|
+
{ pattern: /\bAIza[A-Za-z0-9_-]{35}\b/g, replacement: '[redacted]' },
|
|
38
|
+
{ pattern: /\bAKIA[0-9A-Z]{16}\b/g, replacement: '[redacted]' },
|
|
39
|
+
{ pattern: /\bnpm_[A-Za-z0-9]{36}\b/g, replacement: '[redacted]' },
|
|
40
|
+
{ pattern: /\bxai-[A-Za-z0-9]{16,}\b/g, replacement: '[redacted]' },
|
|
41
|
+
{ pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, replacement: '[redacted]' },
|
|
42
|
+
{ pattern: new RegExp(String.raw `\b([a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:)${shapes.urlPassword}@`, 'gi'), replacement: '$1[redacted]@' },
|
|
43
|
+
{ pattern: new RegExp(String.raw `\bbearer${shapes.separator}+[A-Za-z0-9._~+/=-]+`, 'gi'), replacement: 'bearer [redacted]' },
|
|
44
|
+
{ pattern: new RegExp(String.raw `\bauthorization:${shapes.separator}*${shapes.authorizationValue}`, 'gi'), replacement: 'authorization: [redacted]' },
|
|
45
|
+
shapes.credentialAssignment,
|
|
46
|
+
];
|
|
47
|
+
export const SECRET_REDACTION_RULES = secretRedactionRules(DISPLAYED_SECRET_SHAPES);
|
|
48
|
+
const CUT_OFF_TEXT_REDACTION_RULES = secretRedactionRules(CUT_OFF_SECRET_SHAPES);
|
|
49
|
+
const HIGH_ENTROPY_RULE = { pattern: /[A-Za-z0-9+/]{40,}={0,2}/g, replacement: '[redacted]' };
|
|
50
|
+
const applyRedactionRules = (rules, text) => rules.reduce((acc, rule) => acc.replace(rule.pattern, rule.replacement), text);
|
|
51
|
+
export const redactSecrets = (text) => applyRedactionRules(SECRET_REDACTION_RULES, text);
|
|
52
|
+
export const redactSecretsFromContent = (text) => applyRedactionRules(CUT_OFF_TEXT_REDACTION_RULES, text);
|
|
53
|
+
export const redactSecretsDeep = (text) => redactSecrets(text).replace(HIGH_ENTROPY_RULE.pattern, HIGH_ENTROPY_RULE.replacement);
|
|
@@ -196,7 +196,7 @@ into the agent configuration files it wires, not into `~/.pushary/config.json`.
|
|
|
196
196
|
machine the app set up therefore prints `mac-app` and nothing else. Treat it as
|
|
197
197
|
ready.
|
|
198
198
|
|
|
199
|
-
Check it with `npx
|
|
199
|
+
Check it with `npx pushary@latest status --json`. The exit code is the answer:
|
|
200
200
|
|
|
201
201
|
| Code | Meaning |
|
|
202
202
|
| --- | --- |
|
|
@@ -207,7 +207,7 @@ Check it with `npx @pushary/agent-hooks@latest status --json`. The exit code is
|
|
|
207
207
|
| 6 | No device can answer |
|
|
208
208
|
| 8 | Pushary could not be reached |
|
|
209
209
|
|
|
210
|
-
On 6, the user needs to connect a phone: `npx
|
|
210
|
+
On 6, the user needs to connect a phone: `npx pushary@latest connect`. That adds a phone and rewrites no agent configuration.
|
|
211
211
|
|
|
212
212
|
If `mac-app` printed, the Mac app is installed here and it may also own the hooks. Read the hook command to know, because the command is the record:
|
|
213
213
|
|
|
@@ -234,7 +234,7 @@ The command line works on macOS too. Offer it if the user prefers the terminal,
|
|
|
234
234
|
There is no Mac app for these machines. Use the command line. It is fully supported.
|
|
235
235
|
|
|
236
236
|
```bash
|
|
237
|
-
npx
|
|
237
|
+
npx pushary@latest setup
|
|
238
238
|
```
|
|
239
239
|
|
|
240
240
|
Node 20.17+, 22.13+ or 23.5+ is necessary. Then the user needs a phone to answer on:
|
|
@@ -251,7 +251,7 @@ Run `setup`. It reads the key the app signed in with, so it mints no second key,
|
|
|
251
251
|
### What setup does
|
|
252
252
|
|
|
253
253
|
```bash
|
|
254
|
-
npx
|
|
254
|
+
npx pushary@latest setup
|
|
255
255
|
```
|
|
256
256
|
|
|
257
257
|
Setup pairs first and configures MCP, hooks, permissions and the skill only once pairing succeeds. Until someone completes the steps below, nothing is written and this machine has no Pushary. Treat pairing as the task, not as a prompt to wait out.
|
|
@@ -274,7 +274,7 @@ If `PUSHARY_API_KEY` is already in the environment or in an existing MCP config,
|
|
|
274
274
|
No app on their phone yet? They can get it at https://pushary.com/download. Or answer through the browser instead:
|
|
275
275
|
|
|
276
276
|
```bash
|
|
277
|
-
npx
|
|
277
|
+
npx pushary@latest setup --connect browser
|
|
278
278
|
```
|
|
279
279
|
|
|
280
280
|
This is web push, not a login tab. It prints a QR for the user's own subscribe page, and it waits for a browser on that page to subscribe. On iOS the user must first add that page to the Home Screen, because iOS sends web push only from an installed page.
|
|
@@ -284,7 +284,7 @@ Manual MCP configuration also works, but it needs a key, so the user signs up fi
|
|
|
284
284
|
After setup, verify with:
|
|
285
285
|
|
|
286
286
|
```bash
|
|
287
|
-
npx
|
|
287
|
+
npx pushary@latest doctor
|
|
288
288
|
```
|
|
289
289
|
|
|
290
290
|
## Answer surfaces and account boundaries
|
|
@@ -296,7 +296,7 @@ npx @pushary/agent-hooks@latest doctor
|
|
|
296
296
|
| Slack | Answer through buttons, menus or text modals when the integration and intended recipient are configured. |
|
|
297
297
|
| Browser | Open the decision page as a fallback; browser notification delivery requires permission. |
|
|
298
298
|
|
|
299
|
-
Personal setup connects the operator's devices. For a Mac, install from https://pushary.com/download, sign in to the same personal account and connect your agents in the app. Run `npx
|
|
299
|
+
Personal setup connects the operator's devices. For a Mac, install from https://pushary.com/download, sign in to the same personal account and connect your agents in the app. Run `npx pushary@latest doctor`, then request one harmless test question and verify it reaches the intended surface. Test phone fallback while away from the Mac; do not infer delivery from a successful API call alone.
|
|
300
300
|
|
|
301
301
|
Partner customers use scoped enrollment links issued by their application. Do not enroll them into the operator's account or send their decisions through personal tools. The Mac notch currently uses the personal account/session API; do not promise a Partner customer inbox on Mac. See https://pushary.com/docs/agents/embed for Partner setup.
|
|
302
302
|
|