@pushary/agent-hooks 0.69.0 → 0.70.0
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 +49 -0
- package/README.md +3 -2
- package/data/cursor-plugin/scripts/pushary-gate.mjs +34 -5
- package/data/vscode-plugin/scripts/pushary-gate.mjs +27 -2
- package/dist/bin/pushary-claude.js +2 -2
- package/dist/bin/pushary-clean.js +2 -2
- package/dist/bin/pushary-codex-hook.js +2 -2
- package/dist/bin/pushary-codex.js +1 -1
- package/dist/bin/pushary-connect.js +6 -4
- package/dist/bin/pushary-daemon.js +3 -3
- package/dist/bin/pushary-doctor.js +8 -29
- package/dist/bin/pushary-gemini-hook.js +2 -2
- package/dist/bin/pushary-hook.js +5 -5
- package/dist/bin/pushary-login.js +2 -2
- package/dist/bin/pushary-logout.js +2 -2
- package/dist/bin/pushary-mode.js +2 -2
- package/dist/bin/pushary-notification-hook.js +1 -1
- package/dist/bin/pushary-permission-denied-hook.js +3 -3
- package/dist/bin/pushary-permission-hook.js +3 -3
- package/dist/bin/pushary-post-hook.js +1 -1
- package/dist/bin/pushary-prompt-hook.js +1 -1
- package/dist/bin/pushary-session-end-hook.js +1 -1
- package/dist/bin/pushary-session-start-hook.js +1 -1
- package/dist/bin/pushary-setup.js +76 -9
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +4 -4
- package/dist/bin/pushary-stop-hook.js +1 -1
- package/dist/bin/pushary-stopfailure-hook.js +1 -1
- package/dist/bin/pushary-suggestions.js +2 -2
- package/dist/bin/pushary-upgrade.js +2 -2
- package/dist/bin/pushary-wait.js +2 -2
- package/dist/bin/pushary.js +1 -1
- package/dist/{chunk-JGH37QRB.js → chunk-7OYGFJYZ.js} +1 -1
- package/dist/chunk-B7R4YSAB.js +38 -0
- package/dist/{chunk-ATP6UQPH.js → chunk-BBK3TTU2.js} +1 -1
- package/dist/{chunk-H46LSQRT.js → chunk-FZIQSDTQ.js} +27 -12
- package/dist/{chunk-3USMXNVB.js → chunk-GD275GEX.js} +5 -3
- package/dist/{chunk-C5I2RAMT.js → chunk-HU43EZ5Q.js} +2 -2
- package/dist/{chunk-P7VBAYQO.js → chunk-MGFZXUUR.js} +32 -4
- package/dist/{chunk-YZT2CBC3.js → chunk-NQTRA7H5.js} +1 -1
- package/dist/src/index.js +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.70.0
|
|
4
|
+
|
|
5
|
+
### A key you name is the key you get
|
|
6
|
+
|
|
7
|
+
Passing `--key` with a malformed value did not fail. Setup read it as no key at
|
|
8
|
+
all, went looking elsewhere, and used whichever key it found: one already stored
|
|
9
|
+
on the machine, one in your environment, or a brand new one it created by pairing.
|
|
10
|
+
You named a credential and quietly got a different one. It now stops and says the
|
|
11
|
+
key is not valid.
|
|
12
|
+
|
|
13
|
+
### setup --json no longer stops to ask a question nobody can see
|
|
14
|
+
|
|
15
|
+
Asking for machine-readable output says plainly that no human is watching, but
|
|
16
|
+
setup could still reach an agent-selection prompt. Piped into a script it waited
|
|
17
|
+
for a keypress that was never coming, and the prompt drew itself onto the same
|
|
18
|
+
output a program was trying to read as JSON. It now selects the agents it detects,
|
|
19
|
+
which is exactly what pressing Enter on that prompt would have chosen.
|
|
20
|
+
|
|
21
|
+
`--json` also promises a single object of output, and every failing exit used to
|
|
22
|
+
print nothing at all. A refused key, a cancelled pairing and a crash were
|
|
23
|
+
indistinguishable to anything reading the output. Each now says which it was.
|
|
24
|
+
|
|
25
|
+
### Setup checks its own work
|
|
26
|
+
|
|
27
|
+
An agent installer that finished without an error was reported as configured,
|
|
28
|
+
even if nothing had been written. Doctor would then contradict setup minutes
|
|
29
|
+
later. Setup now confirms the configuration is really there, using the same check
|
|
30
|
+
doctor uses, so the two cannot disagree.
|
|
31
|
+
|
|
32
|
+
### connect points at the fix that matches your situation
|
|
33
|
+
|
|
34
|
+
`pushary connect --app` gave one answer for every way a phone can be out of
|
|
35
|
+
reach: open the app and allow notifications. That is right when the app is signed
|
|
36
|
+
in and the permission was declined, and wrong otherwise. A terminal that was
|
|
37
|
+
never paired needs setup. A phone signed in to a colleague's account needs your
|
|
38
|
+
account, not a permission toggle. It now says which applies, and when it cannot
|
|
39
|
+
tell the two apart it says both rather than sounding certain about the wrong one.
|
|
40
|
+
|
|
41
|
+
### The editor gates say when a proxy is in the way
|
|
42
|
+
|
|
43
|
+
The Cursor and VS Code gates cannot route through a corporate proxy. They still
|
|
44
|
+
hand the decision safely to the editor's own prompt, so nothing is blocked, but
|
|
45
|
+
every approval quietly stopped reaching your phone with no explanation. When a
|
|
46
|
+
proxy is configured they now name it as the likely cause, and on Node 24 or newer
|
|
47
|
+
they name the setting that fixes it.
|
|
48
|
+
|
|
49
|
+
VS Code is also now listed among the supported agents, which it has been since
|
|
50
|
+
its plugin shipped.
|
|
51
|
+
|
|
3
52
|
## 0.69.0
|
|
4
53
|
|
|
5
54
|
### An approval is never granted on a state Pushary could not confirm
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@pushary/agent-hooks)
|
|
5
5
|
[](./LICENSE)
|
|
6
6
|
|
|
7
|
-
Push notifications, questions, and permission approvals for AI coding 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, Gemini CLI, and Hermes.
|
|
7
|
+
Push notifications, questions, and permission approvals for AI coding 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, Gemini CLI, and Hermes.
|
|
8
8
|
|
|
9
9
|
Your agent froze waiting for a yes. This is how it reaches you.
|
|
10
10
|
|
|
@@ -53,6 +53,7 @@ npx @pushary/agent-hooks@latest doctor
|
|
|
53
53
|
| Cursor | yes | yes | yes |
|
|
54
54
|
| Gemini CLI | yes | yes | yes |
|
|
55
55
|
| Hermes | yes | yes | yes |
|
|
56
|
+
| VS Code | yes | yes | yes |
|
|
56
57
|
|
|
57
58
|
Any other MCP client can use notify and ask by pointing at the Pushary MCP endpoint directly.
|
|
58
59
|
|
|
@@ -62,7 +63,7 @@ Any other MCP client can use notify and ask by pointing at the Pushary MCP endpo
|
|
|
62
63
|
asserts this list matches it. Do not edit one without the other. -->
|
|
63
64
|
|
|
64
65
|
```
|
|
65
|
-
setup Configure Claude Code, Codex, Gemini CLI, Hermes, or
|
|
66
|
+
setup Configure Claude Code, Codex, Gemini CLI, Hermes, Cursor, or VS Code with Pushary
|
|
66
67
|
claude Run Claude Code through Pushary, reachable from your phone even when idle
|
|
67
68
|
daemon Keep this machine reachable so your phone can START a new session
|
|
68
69
|
status One screen: which key is in force, whether the server accepts it, what can receive an approval
|
|
@@ -271,6 +271,31 @@ const capActionBody = (text) =>
|
|
|
271
271
|
text.length <= ACTION_BODY_MAX ? text : `${text.slice(0, ACTION_BODY_MAX - ACTION_BODY_TRUNCATION_MARKER.length)}${ACTION_BODY_TRUNCATION_MARKER}`
|
|
272
272
|
const deriveActionBody = (command) => capActionBody(redactSecretsDeep(command))
|
|
273
273
|
|
|
274
|
+
// ── network diagnosis ────────────────────────────────────────────────────────
|
|
275
|
+
//
|
|
276
|
+
// This gate is a dependency-free .mjs, so unlike the CLI it cannot install
|
|
277
|
+
// undici's EnvHttpProxyAgent and its fetch ignores HTTP_PROXY entirely. Node
|
|
278
|
+
// gained a native equivalent in 24 (NODE_USE_ENV_PROXY), but that is read at
|
|
279
|
+
// startup, so a script cannot switch it on for itself.
|
|
280
|
+
//
|
|
281
|
+
// The gate already fails safe: any error here hands the decision to the editor's
|
|
282
|
+
// own prompt. The cost is therefore not a blocked command, it is SILENCE. On a
|
|
283
|
+
// corporate network every approval quietly stops reaching the phone and the only
|
|
284
|
+
// trace is one stderr line nobody reads. So when a proxy is configured and the
|
|
285
|
+
// network call fails, say which of those two it is.
|
|
286
|
+
export const PROXY_VARS = ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy']
|
|
287
|
+
|
|
288
|
+
export const proxyConfigured = (env = process.env) =>
|
|
289
|
+
PROXY_VARS.some(name => (env[name] ?? '').trim() !== '')
|
|
290
|
+
|
|
291
|
+
export const describeNetworkFailure = (error, env = process.env) => {
|
|
292
|
+
const detail = error?.message ?? String(error)
|
|
293
|
+
if (!proxyConfigured(env)) return detail
|
|
294
|
+
const enabled = (env.NODE_USE_ENV_PROXY ?? '').trim() !== ''
|
|
295
|
+
if (enabled) return detail
|
|
296
|
+
return `${detail} (a proxy is set in HTTP_PROXY/HTTPS_PROXY and this hook cannot use it; on Node 24+ set NODE_USE_ENV_PROXY=1 in the editor's environment)`
|
|
297
|
+
}
|
|
298
|
+
|
|
274
299
|
// ── ask / wait ───────────────────────────────────────────────────────────────
|
|
275
300
|
// Redacted, not raw. This is the text a human reads on a lock screen and in
|
|
276
301
|
// Slack, and it used to be the command verbatim: `curl -H "Authorization:
|
|
@@ -447,12 +472,16 @@ const main = async () => {
|
|
|
447
472
|
return respond(await handlePushFirst(apiKey, command, project, ident, tool.pushFirstSeconds))
|
|
448
473
|
}
|
|
449
474
|
} catch (error) {
|
|
450
|
-
process.stderr.write(`[pushary-gate] ${error
|
|
475
|
+
process.stderr.write(`[pushary-gate] ${describeNetworkFailure(error)}\n`)
|
|
451
476
|
return respond(ask())
|
|
452
477
|
}
|
|
453
478
|
}
|
|
454
479
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
480
|
+
// Guarded like the VS Code gate: importing this file to unit-test its pure
|
|
481
|
+
// helpers must not start a hook run that reads stdin and writes a decision.
|
|
482
|
+
if (!process.env.PUSHARY_GATE_IMPORT) {
|
|
483
|
+
main().catch((error) => {
|
|
484
|
+
process.stderr.write(`[pushary-gate] fatal: ${describeNetworkFailure(error)}\n`)
|
|
485
|
+
respond(ask())
|
|
486
|
+
})
|
|
487
|
+
}
|
|
@@ -345,6 +345,31 @@ export const shouldGate = (toolName, toolInput) => {
|
|
|
345
345
|
return command
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
// ── network diagnosis ────────────────────────────────────────────────────────
|
|
349
|
+
//
|
|
350
|
+
// This gate is a dependency-free .mjs, so unlike the CLI it cannot install
|
|
351
|
+
// undici's EnvHttpProxyAgent and its fetch ignores HTTP_PROXY entirely. Node
|
|
352
|
+
// gained a native equivalent in 24 (NODE_USE_ENV_PROXY), but that is read at
|
|
353
|
+
// startup, so a script cannot switch it on for itself.
|
|
354
|
+
//
|
|
355
|
+
// The gate already fails safe: any error here hands the decision to the editor's
|
|
356
|
+
// own prompt. The cost is therefore not a blocked command, it is SILENCE. On a
|
|
357
|
+
// corporate network every approval quietly stops reaching the phone and the only
|
|
358
|
+
// trace is one stderr line nobody reads. So when a proxy is configured and the
|
|
359
|
+
// network call fails, say which of those two it is.
|
|
360
|
+
export const PROXY_VARS = ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy']
|
|
361
|
+
|
|
362
|
+
export const proxyConfigured = (env = process.env) =>
|
|
363
|
+
PROXY_VARS.some(name => (env[name] ?? '').trim() !== '')
|
|
364
|
+
|
|
365
|
+
export const describeNetworkFailure = (error, env = process.env) => {
|
|
366
|
+
const detail = error?.message ?? String(error)
|
|
367
|
+
if (!proxyConfigured(env)) return detail
|
|
368
|
+
const enabled = (env.NODE_USE_ENV_PROXY ?? '').trim() !== ''
|
|
369
|
+
if (enabled) return detail
|
|
370
|
+
return `${detail} (a proxy is set in HTTP_PROXY/HTTPS_PROXY and this hook cannot use it; on Node 24+ set NODE_USE_ENV_PROXY=1 in the editor's environment)`
|
|
371
|
+
}
|
|
372
|
+
|
|
348
373
|
// ── ask / wait ────────────────────────────────────────────────────────────────
|
|
349
374
|
// Redacted, not raw. This is the text a human reads on a lock screen and in
|
|
350
375
|
// Slack, and it used to be the command verbatim: `curl -H "Authorization:
|
|
@@ -517,7 +542,7 @@ const main = async () => {
|
|
|
517
542
|
return respond(await handlePushFirst(apiKey, command, project, ident, tool.pushFirstSeconds))
|
|
518
543
|
}
|
|
519
544
|
} catch (error) {
|
|
520
|
-
diag(
|
|
545
|
+
diag(describeNetworkFailure(error))
|
|
521
546
|
return respond(ask())
|
|
522
547
|
}
|
|
523
548
|
}
|
|
@@ -525,7 +550,7 @@ const main = async () => {
|
|
|
525
550
|
// Importing this file for its testable helpers must not consume stdin or exit.
|
|
526
551
|
if (!process.env.PUSHARY_GATE_IMPORT) {
|
|
527
552
|
main().catch((error) => {
|
|
528
|
-
diag(`fatal: ${error
|
|
553
|
+
diag(`fatal: ${describeNetworkFailure(error)}`)
|
|
529
554
|
respond(ask())
|
|
530
555
|
})
|
|
531
556
|
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
KILL_REASON,
|
|
8
8
|
isDeferAnswer,
|
|
9
9
|
resolveGate
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-GD275GEX.js";
|
|
11
11
|
import {
|
|
12
12
|
askUser,
|
|
13
13
|
cancelQuestion,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
reportEvent,
|
|
20
20
|
scopePathFor,
|
|
21
21
|
waitForAnswer
|
|
22
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-FZIQSDTQ.js";
|
|
23
23
|
import "../chunk-BSZYIAZL.js";
|
|
24
24
|
import "../chunk-SAF6HGAA.js";
|
|
25
25
|
import "../chunk-7QLSKOSU.js";
|
|
@@ -41,8 +41,8 @@ import {
|
|
|
41
41
|
} from "../chunk-MUEW424A.js";
|
|
42
42
|
import {
|
|
43
43
|
exitOnHelpFlag
|
|
44
|
-
} from "../chunk-
|
|
45
|
-
import "../chunk-
|
|
44
|
+
} from "../chunk-BBK3TTU2.js";
|
|
45
|
+
import "../chunk-7OYGFJYZ.js";
|
|
46
46
|
import "../chunk-7QLSKOSU.js";
|
|
47
47
|
import "../chunk-DQAN3JQP.js";
|
|
48
48
|
import {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
denyReasonFrom,
|
|
5
5
|
isDeferAnswer,
|
|
6
6
|
resolveGate
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-GD275GEX.js";
|
|
8
8
|
import {
|
|
9
9
|
CODEX_AGENT,
|
|
10
10
|
DEFAULT_SESSION,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
toPolicyLookup,
|
|
36
36
|
toPolicyLookups,
|
|
37
37
|
waitForAnswer
|
|
38
|
-
} from "../chunk-
|
|
38
|
+
} from "../chunk-FZIQSDTQ.js";
|
|
39
39
|
import {
|
|
40
40
|
isGatingMoment,
|
|
41
41
|
recordKeylessMoment
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
confirmAppConnection,
|
|
7
7
|
connectDevice,
|
|
8
8
|
printConnectInstructions
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-MGFZXUUR.js";
|
|
10
10
|
import "../chunk-3EGEA4KH.js";
|
|
11
11
|
import {
|
|
12
12
|
readKeySource
|
|
@@ -24,8 +24,8 @@ import {
|
|
|
24
24
|
} from "../chunk-GMXKITVA.js";
|
|
25
25
|
import {
|
|
26
26
|
exitOnHelpFlag
|
|
27
|
-
} from "../chunk-
|
|
28
|
-
import "../chunk-
|
|
27
|
+
} from "../chunk-BBK3TTU2.js";
|
|
28
|
+
import "../chunk-7OYGFJYZ.js";
|
|
29
29
|
import "../chunk-BSZYIAZL.js";
|
|
30
30
|
import "../chunk-SAF6HGAA.js";
|
|
31
31
|
import {
|
|
@@ -84,7 +84,9 @@ var main = async () => {
|
|
|
84
84
|
console.log();
|
|
85
85
|
process.exit(EXIT.OK);
|
|
86
86
|
}
|
|
87
|
-
console.log(` ${dim("No device of yours can answer yet.
|
|
87
|
+
console.log(` ${dim("No device of yours can answer yet.")}`);
|
|
88
|
+
for (const line of result.repair ?? []) console.log(` ${dim(line)}`);
|
|
89
|
+
console.log(` ${dim("Then run")} ${cyan("pushary connect")} ${dim("again.")}`);
|
|
88
90
|
console.log();
|
|
89
91
|
process.exit(EXIT.NO_DEVICE);
|
|
90
92
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
HEARTBEAT_INTERVAL_MS,
|
|
4
4
|
sendHeartbeat
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-NQTRA7H5.js";
|
|
6
6
|
import {
|
|
7
7
|
spawnClaude
|
|
8
8
|
} from "../chunk-XHKBHWLX.js";
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
} from "../chunk-MUEW424A.js";
|
|
12
12
|
import {
|
|
13
13
|
exitOnHelpFlag
|
|
14
|
-
} from "../chunk-
|
|
15
|
-
import "../chunk-
|
|
14
|
+
} from "../chunk-BBK3TTU2.js";
|
|
15
|
+
import "../chunk-7OYGFJYZ.js";
|
|
16
16
|
import {
|
|
17
17
|
getMachineId
|
|
18
18
|
} from "../chunk-RN3NOEJF.js";
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
describeWaitLadder
|
|
4
4
|
} from "../chunk-IIENZE2J.js";
|
|
5
|
+
import {
|
|
6
|
+
claudeWired,
|
|
7
|
+
codexWired,
|
|
8
|
+
geminiWired
|
|
9
|
+
} from "../chunk-B7R4YSAB.js";
|
|
5
10
|
import {
|
|
6
11
|
agentProbes,
|
|
7
12
|
detectAgent,
|
|
@@ -39,7 +44,7 @@ import {
|
|
|
39
44
|
describeReach,
|
|
40
45
|
fetchChannels,
|
|
41
46
|
reachVerdict
|
|
42
|
-
} from "../chunk-
|
|
47
|
+
} from "../chunk-MGFZXUUR.js";
|
|
43
48
|
import "../chunk-3EGEA4KH.js";
|
|
44
49
|
import {
|
|
45
50
|
keyPrefix,
|
|
@@ -62,10 +67,10 @@ import {
|
|
|
62
67
|
} from "../chunk-MUEW424A.js";
|
|
63
68
|
import {
|
|
64
69
|
exitOnHelpFlag
|
|
65
|
-
} from "../chunk-
|
|
70
|
+
} from "../chunk-BBK3TTU2.js";
|
|
66
71
|
import {
|
|
67
72
|
getPackageVersion
|
|
68
|
-
} from "../chunk-
|
|
73
|
+
} from "../chunk-7OYGFJYZ.js";
|
|
69
74
|
import {
|
|
70
75
|
readLedgerSummary
|
|
71
76
|
} from "../chunk-WLGEY4UX.js";
|
|
@@ -149,32 +154,6 @@ var resolveDoctorExit = ({ keyExit, failures }) => {
|
|
|
149
154
|
return EXIT.PROBLEMS_FOUND;
|
|
150
155
|
};
|
|
151
156
|
|
|
152
|
-
// src/diagnostics/wiring.ts
|
|
153
|
-
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
154
|
-
var CLAUDE_HOOK_BINARY = /pushary-[a-z-]*hook/;
|
|
155
|
-
var claudeWired = ({ claudeJson: claudeJson2, settings, skillExists }) => {
|
|
156
|
-
if (record(record(claudeJson2)?.mcpServers)?.pushary) return true;
|
|
157
|
-
const s = record(settings);
|
|
158
|
-
if (s) {
|
|
159
|
-
if (CLAUDE_HOOK_BINARY.test(JSON.stringify(s.hooks ?? {}))) return true;
|
|
160
|
-
const allow = record(s.permissions)?.allow;
|
|
161
|
-
if (Array.isArray(allow) && allow.some((rule) => String(rule).includes("mcp__pushary__"))) return true;
|
|
162
|
-
}
|
|
163
|
-
return skillExists;
|
|
164
|
-
};
|
|
165
|
-
var codexWired = ({ config, hooks, skillExists }) => {
|
|
166
|
-
if (record(record(config)?.mcp_servers)?.pushary) return true;
|
|
167
|
-
const h = record(hooks);
|
|
168
|
-
if (h && hasCodexHooks(h)) return true;
|
|
169
|
-
return skillExists;
|
|
170
|
-
};
|
|
171
|
-
var geminiWired = ({ settings }) => {
|
|
172
|
-
const s = record(settings);
|
|
173
|
-
if (!s) return false;
|
|
174
|
-
if (record(s.mcpServers)?.pushary) return true;
|
|
175
|
-
return hasGeminiHooks(s);
|
|
176
|
-
};
|
|
177
|
-
|
|
178
157
|
// bin/pushary-doctor.ts
|
|
179
158
|
exitOnHelpFlag("doctor");
|
|
180
159
|
var parseDoctorOptions = (argv) => {
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
denyReasonFrom,
|
|
5
5
|
isDeferAnswer,
|
|
6
6
|
resolveGate
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-GD275GEX.js";
|
|
8
8
|
import {
|
|
9
9
|
DEFAULT_SESSION,
|
|
10
10
|
askUser,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
scopePathFor,
|
|
27
27
|
sendNotification,
|
|
28
28
|
waitForAnswer
|
|
29
|
-
} from "../chunk-
|
|
29
|
+
} from "../chunk-FZIQSDTQ.js";
|
|
30
30
|
import {
|
|
31
31
|
isGatingMoment,
|
|
32
32
|
recordKeylessMoment
|
package/dist/bin/pushary-hook.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePreToolUse
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-HU43EZ5Q.js";
|
|
5
5
|
import "../chunk-CAJZAFVS.js";
|
|
6
6
|
import {
|
|
7
7
|
exitOnHelpFlag
|
|
8
|
-
} from "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
8
|
+
} from "../chunk-BBK3TTU2.js";
|
|
9
|
+
import "../chunk-7OYGFJYZ.js";
|
|
10
|
+
import "../chunk-GD275GEX.js";
|
|
11
|
+
import "../chunk-FZIQSDTQ.js";
|
|
12
12
|
import "../chunk-WLGEY4UX.js";
|
|
13
13
|
import "../chunk-BSZYIAZL.js";
|
|
14
14
|
import "../chunk-SAF6HGAA.js";
|
|
@@ -21,10 +21,10 @@ import {
|
|
|
21
21
|
} from "../chunk-GMXKITVA.js";
|
|
22
22
|
import {
|
|
23
23
|
exitOnHelpFlag
|
|
24
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-BBK3TTU2.js";
|
|
25
25
|
import {
|
|
26
26
|
getPackageVersion
|
|
27
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-7OYGFJYZ.js";
|
|
28
28
|
import "../chunk-SAF6HGAA.js";
|
|
29
29
|
import {
|
|
30
30
|
UsageError
|
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
} from "../chunk-GMXKITVA.js";
|
|
20
20
|
import {
|
|
21
21
|
exitOnHelpFlag
|
|
22
|
-
} from "../chunk-
|
|
23
|
-
import "../chunk-
|
|
22
|
+
} from "../chunk-BBK3TTU2.js";
|
|
23
|
+
import "../chunk-7OYGFJYZ.js";
|
|
24
24
|
import {
|
|
25
25
|
UsageError
|
|
26
26
|
} from "../chunk-7QLSKOSU.js";
|
package/dist/bin/pushary-mode.js
CHANGED
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
} from "../chunk-GMXKITVA.js";
|
|
12
12
|
import {
|
|
13
13
|
exitOnHelpFlag
|
|
14
|
-
} from "../chunk-
|
|
15
|
-
import "../chunk-
|
|
14
|
+
} from "../chunk-BBK3TTU2.js";
|
|
15
|
+
import "../chunk-7OYGFJYZ.js";
|
|
16
16
|
import "../chunk-SAF6HGAA.js";
|
|
17
17
|
import {
|
|
18
18
|
UsageError
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePermissionDenied
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-HU43EZ5Q.js";
|
|
5
5
|
import "../chunk-CAJZAFVS.js";
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-GD275GEX.js";
|
|
7
|
+
import "../chunk-FZIQSDTQ.js";
|
|
8
8
|
import "../chunk-WLGEY4UX.js";
|
|
9
9
|
import "../chunk-BSZYIAZL.js";
|
|
10
10
|
import "../chunk-SAF6HGAA.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePermissionRequest
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-HU43EZ5Q.js";
|
|
5
5
|
import "../chunk-CAJZAFVS.js";
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-GD275GEX.js";
|
|
7
|
+
import "../chunk-FZIQSDTQ.js";
|
|
8
8
|
import "../chunk-WLGEY4UX.js";
|
|
9
9
|
import "../chunk-BSZYIAZL.js";
|
|
10
10
|
import "../chunk-SAF6HGAA.js";
|
|
@@ -7,6 +7,11 @@ import {
|
|
|
7
7
|
addPusharyHooks,
|
|
8
8
|
addPusharyToolPermissions
|
|
9
9
|
} from "../chunk-CAJZAFVS.js";
|
|
10
|
+
import {
|
|
11
|
+
claudeWired,
|
|
12
|
+
codexWired,
|
|
13
|
+
geminiWired
|
|
14
|
+
} from "../chunk-B7R4YSAB.js";
|
|
10
15
|
import {
|
|
11
16
|
describeDetection,
|
|
12
17
|
detectAllAgents,
|
|
@@ -48,7 +53,7 @@ import {
|
|
|
48
53
|
connectViaAppPairing,
|
|
49
54
|
printConnectInstructions,
|
|
50
55
|
setHumanStream
|
|
51
|
-
} from "../chunk-
|
|
56
|
+
} from "../chunk-MGFZXUUR.js";
|
|
52
57
|
import "../chunk-3EGEA4KH.js";
|
|
53
58
|
import {
|
|
54
59
|
KEY_FILE_MODE,
|
|
@@ -86,10 +91,10 @@ import {
|
|
|
86
91
|
getPackageVersion,
|
|
87
92
|
isNewerVersion,
|
|
88
93
|
renderCommandHelp
|
|
89
|
-
} from "../chunk-
|
|
94
|
+
} from "../chunk-7OYGFJYZ.js";
|
|
90
95
|
import {
|
|
91
96
|
reportEvent
|
|
92
|
-
} from "../chunk-
|
|
97
|
+
} from "../chunk-FZIQSDTQ.js";
|
|
93
98
|
import "../chunk-BSZYIAZL.js";
|
|
94
99
|
import "../chunk-SAF6HGAA.js";
|
|
95
100
|
import {
|
|
@@ -318,6 +323,9 @@ var parseSetupOptions = (input2) => {
|
|
|
318
323
|
if (key && keyStdin) {
|
|
319
324
|
throw new UsageError("Pass either --key or --key-stdin, not both");
|
|
320
325
|
}
|
|
326
|
+
if (key && !isValidApiKey(key)) {
|
|
327
|
+
throw new UsageError("--key is not a valid API key. Expected the pk_xxx.xxx shape from pushary.com/dashboard/agent/settings");
|
|
328
|
+
}
|
|
321
329
|
return {
|
|
322
330
|
connectMode,
|
|
323
331
|
connectExplicit: connectRaw !== void 0,
|
|
@@ -328,7 +336,14 @@ var parseSetupOptions = (input2) => {
|
|
|
328
336
|
// Anything that means "there is nobody to answer a prompt". A non-TTY stdin
|
|
329
337
|
// is included because that is the case that used to hang: it is better to
|
|
330
338
|
// take the defaults and say so than to wait forever for a keypress.
|
|
331
|
-
|
|
339
|
+
//
|
|
340
|
+
// --json counts. Under it stdout carries exactly one object and every human
|
|
341
|
+
// line is redirected to stderr, so a prompt has nowhere to render: the
|
|
342
|
+
// question goes to stderr while the run blocks on a keypress the caller
|
|
343
|
+
// cannot see it is waiting for, and whatever is reading stdout waits forever
|
|
344
|
+
// for an object that never arrives. Asking for machine-readable output is
|
|
345
|
+
// itself a statement that no human is watching.
|
|
346
|
+
nonInteractive: yes || agentsFlag !== void 0 || flags.bools.has("json") || !input2.stdinIsTty,
|
|
332
347
|
json: flags.bools.has("json"),
|
|
333
348
|
dryRun: flags.bools.has("dry-run"),
|
|
334
349
|
help,
|
|
@@ -359,6 +374,7 @@ var humanStream = process.stdout;
|
|
|
359
374
|
var writeHuman = (text) => {
|
|
360
375
|
humanStream.write(text);
|
|
361
376
|
};
|
|
377
|
+
var jsonMode = false;
|
|
362
378
|
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
363
379
|
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
364
380
|
var green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
@@ -367,6 +383,10 @@ var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
|
367
383
|
var check = green("\u2713");
|
|
368
384
|
var abortWith = async (reason, code, keyCheck) => {
|
|
369
385
|
await reportSetupAbort(reason, code, keyCheck);
|
|
386
|
+
if (jsonMode) {
|
|
387
|
+
process.stdout.write(`${JSON.stringify({ ok: false, reason, exitCode: code, ...keyCheck ? { keyCheck } : {} }, null, 2)}
|
|
388
|
+
`);
|
|
389
|
+
}
|
|
370
390
|
return code;
|
|
371
391
|
};
|
|
372
392
|
var readKeyFromStdin = async (options) => {
|
|
@@ -1053,6 +1073,44 @@ var setupGemini = async (apiKey) => {
|
|
|
1053
1073
|
console.log(` ${dim("Restart Gemini CLI to load the new config.")}`);
|
|
1054
1074
|
noteManual("Restart Gemini CLI to load the new config.");
|
|
1055
1075
|
};
|
|
1076
|
+
var agentIsWired = (agent) => {
|
|
1077
|
+
const readJson2 = (path) => {
|
|
1078
|
+
try {
|
|
1079
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
1080
|
+
} catch {
|
|
1081
|
+
return null;
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
switch (agent) {
|
|
1085
|
+
case "claude_code":
|
|
1086
|
+
return claudeWired({
|
|
1087
|
+
claudeJson: readJson2(CLAUDE_JSON),
|
|
1088
|
+
settings: readJson2(CLAUDE_SETTINGS),
|
|
1089
|
+
skillExists: existsSync(join(CLAUDE_SKILL_DIR, "SKILL.md"))
|
|
1090
|
+
});
|
|
1091
|
+
case "codex": {
|
|
1092
|
+
let config = null;
|
|
1093
|
+
try {
|
|
1094
|
+
config = parseTOML(readFileSync(join(CODEX_HOME, "config.toml"), "utf-8"));
|
|
1095
|
+
} catch {
|
|
1096
|
+
config = null;
|
|
1097
|
+
}
|
|
1098
|
+
return codexWired({
|
|
1099
|
+
config,
|
|
1100
|
+
hooks: readJson2(CODEX_HOOKS_JSON),
|
|
1101
|
+
skillExists: existsSync(join(CODEX_SKILL_DIR, "SKILL.md"))
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
case "gemini_cli":
|
|
1105
|
+
return geminiWired({ settings: readJson2(GEMINI_SETTINGS) });
|
|
1106
|
+
case "cursor":
|
|
1107
|
+
return existsSync(join(CURSOR_PLUGIN_DIR, "mcp.json"));
|
|
1108
|
+
case "vscode":
|
|
1109
|
+
return existsSync(join(VSCODE_PLUGIN_DIR, ".mcp.json"));
|
|
1110
|
+
default:
|
|
1111
|
+
return true;
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1056
1114
|
var AGENT_SETUP = {
|
|
1057
1115
|
claude_code: setupClaudeCode,
|
|
1058
1116
|
codex: setupCodex,
|
|
@@ -1201,7 +1259,8 @@ var resolveAgents = async (options) => {
|
|
|
1201
1259
|
return options.agents.agents;
|
|
1202
1260
|
}
|
|
1203
1261
|
const detected = detectAgents();
|
|
1204
|
-
|
|
1262
|
+
const wantsAuto = options.agents.kind === "auto" || options.nonInteractive;
|
|
1263
|
+
if (wantsAuto) {
|
|
1205
1264
|
const picked = Object.keys(detected).filter((agent) => detected[agent].found);
|
|
1206
1265
|
if (picked.length === 0) {
|
|
1207
1266
|
console.log(` ${yellow("!")} No agents detected, so nothing was configured. Your key is saved.`);
|
|
@@ -1283,6 +1342,7 @@ var main = async () => {
|
|
|
1283
1342
|
}
|
|
1284
1343
|
recordSetupOptions(options);
|
|
1285
1344
|
if (options.json) {
|
|
1345
|
+
jsonMode = true;
|
|
1286
1346
|
humanStream = process.stderr;
|
|
1287
1347
|
setHumanStream(process.stderr);
|
|
1288
1348
|
console.log = (...args) => {
|
|
@@ -1304,12 +1364,12 @@ var main = async () => {
|
|
|
1304
1364
|
let keyFromPairing = false;
|
|
1305
1365
|
const storedKey = readConfigFileKey();
|
|
1306
1366
|
const pairingRequested = connectMode === "app" && options.connectExplicit && !options.skipPhone;
|
|
1307
|
-
const ambientEligible =
|
|
1367
|
+
const ambientEligible = flagKey === void 0 && !pairingRequested;
|
|
1308
1368
|
const envCheck = ambientEligible && envKey && isValidApiKey(envKey) ? await usableStoredKey(envKey) : null;
|
|
1309
1369
|
const storedCheck = ambientEligible && envCheck === null && storedKey !== null && isValidApiKey(storedKey) && // login writes both, so without this the same key is probed twice.
|
|
1310
1370
|
storedKey !== envKey ? await usableStoredKey(storedKey) : null;
|
|
1311
1371
|
let storedKeyCheck = null;
|
|
1312
|
-
if (flagKey
|
|
1372
|
+
if (flagKey) {
|
|
1313
1373
|
const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
|
|
1314
1374
|
console.log(` ${check} Using API key: ${dim(masked)}`);
|
|
1315
1375
|
console.log();
|
|
@@ -1445,8 +1505,15 @@ var main = async () => {
|
|
|
1445
1505
|
markStep(`configure:${agent}`);
|
|
1446
1506
|
try {
|
|
1447
1507
|
const outcome = await AGENT_SETUP[agent](trimmedKey, context);
|
|
1448
|
-
if (outcome === "skipped")
|
|
1449
|
-
|
|
1508
|
+
if (outcome === "skipped") {
|
|
1509
|
+
skipped.push(agent);
|
|
1510
|
+
} else if (agentIsWired(agent)) {
|
|
1511
|
+
completed.push(agent);
|
|
1512
|
+
} else {
|
|
1513
|
+
failed.push(agent.replace("_", " "));
|
|
1514
|
+
console.log(` ${yellow("!")} ${agent.replace("_", " ")} reported success but its config is not there.`);
|
|
1515
|
+
console.log(` ${dim("Re-run setup, then")} ${cyan("npx @pushary/agent-hooks@latest doctor")} ${dim("to see what is missing.")}`);
|
|
1516
|
+
}
|
|
1450
1517
|
} catch (err) {
|
|
1451
1518
|
failed.push(agent.replace("_", " "));
|
|
1452
1519
|
console.log(` ${yellow("!")} ${agent.replace("_", " ")} setup failed: ${formatError(err)}`);
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import {
|
|
3
3
|
describeLastSeen,
|
|
4
4
|
listMachines
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-NQTRA7H5.js";
|
|
6
6
|
import {
|
|
7
7
|
describeReach,
|
|
8
8
|
fetchChannels,
|
|
9
9
|
reachVerdict
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-MGFZXUUR.js";
|
|
11
11
|
import "../chunk-3EGEA4KH.js";
|
|
12
12
|
import {
|
|
13
13
|
readKeySource
|
|
@@ -26,8 +26,8 @@ import {
|
|
|
26
26
|
} from "../chunk-GMXKITVA.js";
|
|
27
27
|
import {
|
|
28
28
|
exitOnHelpFlag
|
|
29
|
-
} from "../chunk-
|
|
30
|
-
import "../chunk-
|
|
29
|
+
} from "../chunk-BBK3TTU2.js";
|
|
30
|
+
import "../chunk-7OYGFJYZ.js";
|
|
31
31
|
import "../chunk-BSZYIAZL.js";
|
|
32
32
|
import "../chunk-SAF6HGAA.js";
|
|
33
33
|
import {
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
} from "../chunk-GMXKITVA.js";
|
|
8
8
|
import {
|
|
9
9
|
exitOnHelpFlag
|
|
10
|
-
} from "../chunk-
|
|
11
|
-
import "../chunk-
|
|
10
|
+
} from "../chunk-BBK3TTU2.js";
|
|
11
|
+
import "../chunk-7OYGFJYZ.js";
|
|
12
12
|
import {
|
|
13
13
|
UsageError
|
|
14
14
|
} from "../chunk-7QLSKOSU.js";
|
|
@@ -36,10 +36,10 @@ import {
|
|
|
36
36
|
} from "../chunk-MUEW424A.js";
|
|
37
37
|
import {
|
|
38
38
|
exitOnHelpFlag
|
|
39
|
-
} from "../chunk-
|
|
39
|
+
} from "../chunk-BBK3TTU2.js";
|
|
40
40
|
import {
|
|
41
41
|
isNewerVersion
|
|
42
|
-
} from "../chunk-
|
|
42
|
+
} from "../chunk-7OYGFJYZ.js";
|
|
43
43
|
import "../chunk-DQAN3JQP.js";
|
|
44
44
|
import {
|
|
45
45
|
getApiKey
|
package/dist/bin/pushary-wait.js
CHANGED
|
@@ -14,8 +14,8 @@ import {
|
|
|
14
14
|
} from "../chunk-GMXKITVA.js";
|
|
15
15
|
import {
|
|
16
16
|
exitOnHelpFlag
|
|
17
|
-
} from "../chunk-
|
|
18
|
-
import "../chunk-
|
|
17
|
+
} from "../chunk-BBK3TTU2.js";
|
|
18
|
+
import "../chunk-7OYGFJYZ.js";
|
|
19
19
|
import "../chunk-SAF6HGAA.js";
|
|
20
20
|
import {
|
|
21
21
|
UsageError
|
package/dist/bin/pushary.js
CHANGED
|
@@ -3,7 +3,7 @@ var COMMAND_LIST = [
|
|
|
3
3
|
{
|
|
4
4
|
name: "setup",
|
|
5
5
|
module: "pushary-setup",
|
|
6
|
-
summary: "Configure Claude Code, Codex, Gemini CLI, Hermes, or
|
|
6
|
+
summary: "Configure Claude Code, Codex, Gemini CLI, Hermes, Cursor, or VS Code with Pushary"
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
name: "claude",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hasGeminiHooks
|
|
3
|
+
} from "./chunk-E2U35RLD.js";
|
|
4
|
+
import {
|
|
5
|
+
hasCodexHooks
|
|
6
|
+
} from "./chunk-7HG4WUIE.js";
|
|
7
|
+
|
|
8
|
+
// src/diagnostics/wiring.ts
|
|
9
|
+
var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
10
|
+
var CLAUDE_HOOK_BINARY = /pushary-[a-z-]*hook/;
|
|
11
|
+
var claudeWired = ({ claudeJson, settings, skillExists }) => {
|
|
12
|
+
if (record(record(claudeJson)?.mcpServers)?.pushary) return true;
|
|
13
|
+
const s = record(settings);
|
|
14
|
+
if (s) {
|
|
15
|
+
if (CLAUDE_HOOK_BINARY.test(JSON.stringify(s.hooks ?? {}))) return true;
|
|
16
|
+
const allow = record(s.permissions)?.allow;
|
|
17
|
+
if (Array.isArray(allow) && allow.some((rule) => String(rule).includes("mcp__pushary__"))) return true;
|
|
18
|
+
}
|
|
19
|
+
return skillExists;
|
|
20
|
+
};
|
|
21
|
+
var codexWired = ({ config, hooks, skillExists }) => {
|
|
22
|
+
if (record(record(config)?.mcp_servers)?.pushary) return true;
|
|
23
|
+
const h = record(hooks);
|
|
24
|
+
if (h && hasCodexHooks(h)) return true;
|
|
25
|
+
return skillExists;
|
|
26
|
+
};
|
|
27
|
+
var geminiWired = ({ settings }) => {
|
|
28
|
+
const s = record(settings);
|
|
29
|
+
if (!s) return false;
|
|
30
|
+
if (record(s.mcpServers)?.pushary) return true;
|
|
31
|
+
return hasGeminiHooks(s);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
claudeWired,
|
|
36
|
+
codexWired,
|
|
37
|
+
geminiWired
|
|
38
|
+
};
|
|
@@ -55,6 +55,10 @@ var isWaitForAnswerResponse = (data) => {
|
|
|
55
55
|
// src/policy.ts
|
|
56
56
|
var CACHE_TTL_MS = 60 * 1e3;
|
|
57
57
|
var STALE_CACHE_MAX_MS = 12 * 60 * 60 * 1e3;
|
|
58
|
+
var policyRequestFor = (agentType) => ({
|
|
59
|
+
agent: agentType,
|
|
60
|
+
repoAware: agentType === "claude_code"
|
|
61
|
+
});
|
|
58
62
|
var policyCacheFile = (apiKey, agent, repoAware = false) => {
|
|
59
63
|
const parts = [apiKey, agent, repoAware ? "repoAware" : void 0].filter(Boolean);
|
|
60
64
|
const hash = createHash("sha256").update(parts.join(":")).digest("hex").slice(0, 12);
|
|
@@ -132,6 +136,8 @@ var findBestMatch = (policies, toolName, args, repoKey) => {
|
|
|
132
136
|
}
|
|
133
137
|
return best;
|
|
134
138
|
};
|
|
139
|
+
var isAutoApprove = (policy) => policy.timeoutSeconds === 0 && policy.timeoutAction === "approve";
|
|
140
|
+
var isAutoDeny = (policy) => policy.timeoutSeconds === 0 && policy.timeoutAction === "deny";
|
|
135
141
|
var autoApprove = (tool) => ({
|
|
136
142
|
tool,
|
|
137
143
|
timeoutSeconds: 0,
|
|
@@ -164,7 +170,7 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey) =>
|
|
|
164
170
|
if (!governedBySpecificRule && toolName === "Bash" && typeof arg === "string" && isSafeReadOnlyCommand(arg)) {
|
|
165
171
|
base = autoApprove(base.tool);
|
|
166
172
|
}
|
|
167
|
-
const wouldAutoApprove = base
|
|
173
|
+
const wouldAutoApprove = isAutoApprove(base);
|
|
168
174
|
if (!governedBySpecificRule && wouldAutoApprove && toolName === "Bash" && typeof arg === "string" && classifyDecisionRisk(toolName, arg) === "destructive") {
|
|
169
175
|
base = {
|
|
170
176
|
tool: base.tool,
|
|
@@ -181,8 +187,8 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey) =>
|
|
|
181
187
|
return base;
|
|
182
188
|
};
|
|
183
189
|
var restraintRank = (policy) => {
|
|
184
|
-
if (policy
|
|
185
|
-
if (policy
|
|
190
|
+
if (isAutoDeny(policy)) return 4;
|
|
191
|
+
if (isAutoApprove(policy)) return 0;
|
|
186
192
|
if (policy.mode === "push_only") return 3;
|
|
187
193
|
return policy.mode === "terminal_only" || policy.mode === "notify_only" ? 1 : 2;
|
|
188
194
|
};
|
|
@@ -945,31 +951,38 @@ var notifyLateAnswers = async (apiKey, late, agentName, sessionId) => {
|
|
|
945
951
|
};
|
|
946
952
|
var CLAUDE_CODE_AGENT = { type: "claude_code", label: "Claude Code" };
|
|
947
953
|
var POLICY_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
948
|
-
var readFreshCachedPolicy = (apiKey) => {
|
|
949
|
-
const
|
|
954
|
+
var readFreshCachedPolicy = (apiKey, agentType) => {
|
|
955
|
+
const request = policyRequestFor(agentType);
|
|
956
|
+
const path = policyCacheFile(apiKey, request.agent, request.repoAware);
|
|
950
957
|
if (!existsSync5(path)) return null;
|
|
951
958
|
const cached = JSON.parse(readFileSync4(path, "utf-8"));
|
|
952
959
|
if (!isPolicyConfig(cached)) return null;
|
|
953
960
|
if (!cached._cachedAt || Date.now() - cached._cachedAt >= POLICY_CACHE_TTL_MS) return null;
|
|
954
961
|
return cached;
|
|
955
962
|
};
|
|
956
|
-
var
|
|
963
|
+
var autoDecisionEnabled = () => {
|
|
964
|
+
const flag = process.env.PUSHARY_AUTO_DECISION_BEACON;
|
|
965
|
+
return flag === "1" || flag === "true";
|
|
966
|
+
};
|
|
967
|
+
var deriveDecisionSource = (toolName, toolInput, liveMode, agentType, cwd) => {
|
|
957
968
|
try {
|
|
958
969
|
if (liveMode.kill) return "terminal";
|
|
959
|
-
|
|
970
|
+
if (!autoDecisionEnabled()) return void 0;
|
|
971
|
+
const policy = readFreshCachedPolicy(getApiKey(), agentType);
|
|
960
972
|
if (!policy) return void 0;
|
|
961
973
|
const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd, repoKeyFor(cwd));
|
|
962
|
-
if (resolved
|
|
974
|
+
if (isAutoApprove(resolved)) return "policy_auto";
|
|
963
975
|
if (resolved.mode === "push_only" || resolved.mode === "push_first") return "human";
|
|
964
976
|
return "terminal";
|
|
965
977
|
} catch {
|
|
966
978
|
return void 0;
|
|
967
979
|
}
|
|
968
980
|
};
|
|
969
|
-
var deriveAutoDecisionBeacon = (toolName, toolInput, liveMode, cwd) => {
|
|
981
|
+
var deriveAutoDecisionBeacon = (toolName, toolInput, liveMode, agentType, cwd) => {
|
|
970
982
|
try {
|
|
971
983
|
if (liveMode.kill) return void 0;
|
|
972
|
-
|
|
984
|
+
if (!autoDecisionEnabled()) return void 0;
|
|
985
|
+
const policy = readFreshCachedPolicy(getApiKey(), agentType);
|
|
973
986
|
if (!policy) return void 0;
|
|
974
987
|
const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd, repoKeyFor(cwd));
|
|
975
988
|
if (resolved.timeoutSeconds !== 0 || resolved.timeoutAction !== "approve") return void 0;
|
|
@@ -1055,8 +1068,8 @@ var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
|
1055
1068
|
for (const l of late) removePendingQuestion(sessionKey, l.correlationId);
|
|
1056
1069
|
}
|
|
1057
1070
|
const isMirrorOnly = NON_GATED_MIRROR_TOOLS.has(input.tool_name);
|
|
1058
|
-
const decisionSource = isMirrorOnly ? void 0 : deriveDecisionSource(lookup.tool, lookup.input, liveMode, input.cwd);
|
|
1059
|
-
const beacon = !isMirrorOnly && decisionSource === "policy_auto" && throttlePass(`autodecision:${sessionKey}:${lookup.tool}`, AUTO_DECISION_WINDOW_MS) ? deriveAutoDecisionBeacon(lookup.tool, lookup.input, liveMode, input.cwd) : void 0;
|
|
1071
|
+
const decisionSource = isMirrorOnly ? void 0 : deriveDecisionSource(lookup.tool, lookup.input, liveMode, agent.type, input.cwd);
|
|
1072
|
+
const beacon = !isMirrorOnly && decisionSource === "policy_auto" && throttlePass(`autodecision:${sessionKey}:${lookup.tool}`, AUTO_DECISION_WINDOW_MS) ? deriveAutoDecisionBeacon(lookup.tool, lookup.input, liveMode, agent.type, input.cwd) : void 0;
|
|
1060
1073
|
const toolReport = reportEvent({
|
|
1061
1074
|
event: isError ? "tool_error" : "tool_complete",
|
|
1062
1075
|
agentType: agent.type,
|
|
@@ -1310,6 +1323,8 @@ var handleStopFailure = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
|
1310
1323
|
|
|
1311
1324
|
export {
|
|
1312
1325
|
getPolicy,
|
|
1326
|
+
isAutoApprove,
|
|
1327
|
+
isAutoDeny,
|
|
1313
1328
|
resolveAutoResolveOrigin,
|
|
1314
1329
|
resolvePolicy,
|
|
1315
1330
|
resolvePolicyAcross,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
+
isAutoApprove,
|
|
3
|
+
isAutoDeny,
|
|
2
4
|
isModeStateDegraded,
|
|
3
5
|
resolveAutoResolveOrigin,
|
|
4
6
|
resolvePolicyAcross
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FZIQSDTQ.js";
|
|
6
8
|
import {
|
|
7
9
|
evaluateScope,
|
|
8
10
|
scopeChangeReason,
|
|
@@ -34,8 +36,8 @@ var resolveGate = (input) => {
|
|
|
34
36
|
const { modeState, config, toolName, toolInputs, cwd, repoKey } = input;
|
|
35
37
|
if (modeState.kill) return { kind: "kill", reason: KILL_REASON };
|
|
36
38
|
const policy = resolvePolicyAcross(config, toolName, modeState.mode, toolInputs, cwd, repoKey);
|
|
37
|
-
const autoApproves = policy
|
|
38
|
-
const autoDenies = policy
|
|
39
|
+
const autoApproves = isAutoApprove(policy);
|
|
40
|
+
const autoDenies = isAutoDeny(policy);
|
|
39
41
|
if (autoDenies) return { kind: "deny", policy, reason: `Denied by policy for ${policy.tool}` };
|
|
40
42
|
if (isModeStateDegraded(modeState)) {
|
|
41
43
|
if (autoApproves) {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
denyReasonFrom,
|
|
6
6
|
isDeferAnswer,
|
|
7
7
|
resolveGate
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-GD275GEX.js";
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_SESSION,
|
|
11
11
|
askUser,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
sendNotification,
|
|
26
26
|
throttlePass,
|
|
27
27
|
waitForAnswer
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-FZIQSDTQ.js";
|
|
29
29
|
import {
|
|
30
30
|
isGatingMoment,
|
|
31
31
|
recordKeylessMoment
|
|
@@ -45,6 +45,27 @@ var describeReach = (channels) => {
|
|
|
45
45
|
if (verdict.kind === "unattributable") return `${summary}, and no way to tell whose`;
|
|
46
46
|
return summary;
|
|
47
47
|
};
|
|
48
|
+
var describeAppRepair = (verdict) => {
|
|
49
|
+
switch (verdict.kind) {
|
|
50
|
+
case "yours":
|
|
51
|
+
return null;
|
|
52
|
+
case "nobody":
|
|
53
|
+
return [
|
|
54
|
+
"Open the Pushary app, sign in, and allow notifications.",
|
|
55
|
+
"If this terminal was never paired with your phone, run: npx @pushary/agent-hooks@latest setup --connect app"
|
|
56
|
+
];
|
|
57
|
+
case "others":
|
|
58
|
+
return [
|
|
59
|
+
`Reachable devices belong to other members of this workspace (${verdict.otherMembers}).`,
|
|
60
|
+
"Sign in on the Pushary app with your own account so approvals can reach you."
|
|
61
|
+
];
|
|
62
|
+
case "unattributable":
|
|
63
|
+
return [
|
|
64
|
+
"Something is reachable here, but not identifiably yours.",
|
|
65
|
+
"Sign in on the Pushary app so your approvals are routed to your device."
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
};
|
|
48
69
|
|
|
49
70
|
// src/cli/human.ts
|
|
50
71
|
var stream = process.stdout;
|
|
@@ -324,7 +345,12 @@ var printQr = (url) => new Promise((resolve, reject) => {
|
|
|
324
345
|
reject(err);
|
|
325
346
|
}
|
|
326
347
|
});
|
|
327
|
-
var phoneFailure = (detail) => ({
|
|
348
|
+
var phoneFailure = (detail, repair) => ({
|
|
349
|
+
reachesYou: false,
|
|
350
|
+
outcome: null,
|
|
351
|
+
detail,
|
|
352
|
+
...repair ? { repair } : {}
|
|
353
|
+
});
|
|
328
354
|
var waitForDevice = async (apiKey, baseline) => {
|
|
329
355
|
const deadline = Date.now() + CONNECT_TIMEOUT_MS;
|
|
330
356
|
let consecutiveFailures = 0;
|
|
@@ -469,9 +495,11 @@ var confirmAppConnection = async (apiKey) => {
|
|
|
469
495
|
const channels = probe.status;
|
|
470
496
|
const ownedApp = channels.yours?.appDevices ?? channels.appDevices;
|
|
471
497
|
if (channels.precise && ownedApp === 0) {
|
|
472
|
-
|
|
473
|
-
console.log(` ${
|
|
474
|
-
|
|
498
|
+
const repair = describeAppRepair(reachVerdict(channels)) ?? [];
|
|
499
|
+
console.log(` ${yellow("!")} No device of yours is registered for notifications yet.`);
|
|
500
|
+
for (const line of repair) console.log(` ${dim(line)}`);
|
|
501
|
+
console.log(` ${dim("Then run")} ${cyan("pushary doctor")}${dim(".")}`);
|
|
502
|
+
return phoneFailure("app_not_registered", repair);
|
|
475
503
|
}
|
|
476
504
|
return sendAndConfirm(apiKey, channels);
|
|
477
505
|
};
|
package/dist/src/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handlePreToolUse
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-HU43EZ5Q.js";
|
|
4
4
|
import "../chunk-CAJZAFVS.js";
|
|
5
|
-
import "../chunk-
|
|
5
|
+
import "../chunk-GD275GEX.js";
|
|
6
6
|
import {
|
|
7
7
|
askUser,
|
|
8
8
|
cancelQuestion,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
reportEvent,
|
|
16
16
|
resolvePolicy,
|
|
17
17
|
waitForAnswer
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-FZIQSDTQ.js";
|
|
19
19
|
import "../chunk-WLGEY4UX.js";
|
|
20
20
|
import "../chunk-BSZYIAZL.js";
|
|
21
21
|
import "../chunk-SAF6HGAA.js";
|