@pushary/agent-hooks 0.84.0 → 0.85.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 +63 -0
- package/README.md +24 -0
- package/dist/bin/pushary-bell-hook.js +5 -3
- package/dist/bin/pushary-bell.js +8 -7
- package/dist/bin/pushary-claude.js +29 -20
- package/dist/bin/pushary-clean.js +6 -5
- package/dist/bin/pushary-codex-bridge.js +8 -7
- package/dist/bin/pushary-codex-hook.js +11 -14
- package/dist/bin/pushary-codex.js +1 -1
- package/dist/bin/pushary-connect.js +5 -5
- package/dist/bin/pushary-daemon.js +36 -18
- package/dist/bin/pushary-disconnect.d.ts +1 -0
- package/dist/bin/pushary-disconnect.js +125 -0
- package/dist/bin/pushary-doctor.js +9 -9
- package/dist/bin/pushary-gemini-bridge.js +8 -7
- package/dist/bin/pushary-gemini-hook.js +12 -15
- package/dist/bin/pushary-hook.js +13 -20
- package/dist/bin/pushary-login.js +2 -2
- package/dist/bin/pushary-logout.js +3 -3
- package/dist/bin/pushary-mode.js +2 -2
- package/dist/bin/pushary-notification-hook.js +6 -9
- package/dist/bin/pushary-permission-denied-hook.js +11 -9
- package/dist/bin/pushary-permission-hook.js +11 -9
- package/dist/bin/pushary-post-hook.js +6 -9
- package/dist/bin/pushary-prompt-hook.js +6 -9
- package/dist/bin/pushary-session-end-hook.js +6 -6
- package/dist/bin/pushary-session-start-hook.js +6 -6
- package/dist/bin/pushary-setup.js +23 -24
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +2 -2
- package/dist/bin/pushary-stop-hook.js +6 -6
- package/dist/bin/pushary-stopfailure-hook.js +6 -6
- package/dist/bin/pushary-suggestions.js +2 -2
- package/dist/bin/pushary-upgrade.js +13 -8
- package/dist/bin/pushary-wait.js +2 -2
- package/dist/bin/pushary.js +2 -1
- package/dist/{chunk-HZNOAJGB.js → chunk-4QPOMJUB.js} +1 -1
- package/dist/chunk-64PXDATC.js +187 -0
- package/dist/chunk-6CUUA7HO.js +59 -0
- package/dist/{chunk-UXWEE3QI.js → chunk-ADISVXZL.js} +1 -1
- package/dist/chunk-AXRATIS6.js +70 -0
- package/dist/{chunk-YJCMI5GF.js → chunk-ERYKGH4J.js} +11 -4
- package/dist/{chunk-OPMSMF5R.js → chunk-EXMUM226.js} +21 -8
- package/dist/{chunk-NWYGW7HY.js → chunk-GCKAWHFP.js} +9 -2
- package/dist/{chunk-QNBA5OXQ.js → chunk-GFM3STWO.js} +3 -3
- package/dist/{chunk-MGJBC6DD.js → chunk-GOEXZ5QJ.js} +1 -0
- package/dist/{chunk-37AD5K7W.js → chunk-GXIFADGD.js} +1 -1
- package/dist/{chunk-SEAHGHCE.js → chunk-HVZIHTOO.js} +33 -0
- package/dist/chunk-PWTKKQQI.js +27 -0
- package/dist/{chunk-J4KFVOCQ.js → chunk-SQOFGPNX.js} +1 -1
- package/dist/{chunk-CCH775Y6.js → chunk-WEAQQSHQ.js} +1 -1
- package/dist/{chunk-EVJ6ULHN.js → chunk-YKW6JCWI.js} +68 -29
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +6 -4
- package/package.json +3 -2
- package/dist/chunk-UKSQWTBH.js +0 -208
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.85.0
|
|
4
|
+
|
|
5
|
+
### A hook command that cannot outlive what it points at
|
|
6
|
+
|
|
7
|
+
Every hook command written into `~/.claude/settings.json`, `~/.codex/hooks.json`
|
|
8
|
+
and `~/.gemini/settings.json`, the free bell included, is now wrapped so that a missing binary, a removed
|
|
9
|
+
`node`, a changed npm prefix or a moved home directory exits 0 and the agent reads
|
|
10
|
+
"no opinion". Before, any of those turned every tool call into a failing hook.
|
|
11
|
+
The guard is tested on every shell that can be `/bin/sh`, including dash, and with
|
|
12
|
+
`node` off PATH, which used to exit 127 and print on every tool call.
|
|
13
|
+
|
|
14
|
+
Codex hashes the exact command string to trust a hook, so `pushary upgrade` rewrites
|
|
15
|
+
the trust entry when it re-applies the hooks.
|
|
16
|
+
|
|
17
|
+
### Hooks registered for the CLI you have
|
|
18
|
+
|
|
19
|
+
Claude Code events are registered from a table with a version floor per event.
|
|
20
|
+
`SubagentStart`, `SubagentStop`, `PreCompact`, `PostCompact` and `TeammateIdle`
|
|
21
|
+
are added on a CLI that understands them, and `pushary upgrade` re-registers
|
|
22
|
+
against the CLI you have now. Disconnect removes every event in the table; a
|
|
23
|
+
hand-typed list used to leave the five observability events behind.
|
|
24
|
+
|
|
25
|
+
### `pushary disconnect <agent>`
|
|
26
|
+
|
|
27
|
+
Removes the MCP server, the hooks, the skill and, for Codex, the trust entries and
|
|
28
|
+
the API key from `config.toml`, for one agent, leaving the others alone. The bin
|
|
29
|
+
map entry was written in a shape the build silently dropped, and the command read
|
|
30
|
+
the dispatcher's own token as the agent name; both are fixed before this, its
|
|
31
|
+
first release, and a test now holds every bin entry to the one shape tsup builds.
|
|
32
|
+
|
|
33
|
+
### A kill switch per hook path
|
|
34
|
+
|
|
35
|
+
`~/.pushary/admission-rules.json` (`{"version":1,"disabled":["claude:PostToolUse"]}`)
|
|
36
|
+
silences one event on one agent, or one agent entirely, without a release. Every
|
|
37
|
+
hook binary reads its input through this check, from the same file the macOS
|
|
38
|
+
helper reads, so one file governs both transports. Deny-only: nothing in the file
|
|
39
|
+
can enable a hook.
|
|
40
|
+
|
|
41
|
+
### `PostToolUse` no longer pays two round trips per tool call
|
|
42
|
+
|
|
43
|
+
The telemetry hooks reuse the mode state the gate fetched for the same session
|
|
44
|
+
within the last 60 seconds. The gate itself still reads it live, so a remote stop
|
|
45
|
+
and a scope contract are never served stale where they decide anything.
|
|
46
|
+
|
|
47
|
+
## 0.84.1
|
|
48
|
+
|
|
49
|
+
### Remote starts survive lost acknowledgements without double-launching
|
|
50
|
+
|
|
51
|
+
A phone-started agent now claims exclusive process ownership before the provider
|
|
52
|
+
starts. If the daemon restarts or the launch acknowledgement is lost, retrying the
|
|
53
|
+
same request reconnects to the existing process instead of starting a second one.
|
|
54
|
+
|
|
55
|
+
### Repository approval scopes reach Codex and Gemini
|
|
56
|
+
|
|
57
|
+
Codex and Gemini sessions now carry their repository identity through approval
|
|
58
|
+
requests, so repository-scoped rules stay inside the repository that created them.
|
|
59
|
+
|
|
60
|
+
### Encrypted transcripts retry safely
|
|
61
|
+
|
|
62
|
+
Transcript writers now retry while a session key is temporarily unavailable and
|
|
63
|
+
stop on a real key conflict, preventing undecryptable transcript entries from being
|
|
64
|
+
accepted or silently dropped.
|
|
65
|
+
|
|
3
66
|
## 0.84.0
|
|
4
67
|
|
|
5
68
|
### The phone can start an agent on a machine that is not running one
|
package/README.md
CHANGED
|
@@ -108,6 +108,29 @@ npx @pushary/agent-hooks@latest doctor
|
|
|
108
108
|
|
|
109
109
|
Any other MCP client can use notify and ask by pointing at the Pushary MCP endpoint directly.
|
|
110
110
|
|
|
111
|
+
## Hook events, gated by version
|
|
112
|
+
|
|
113
|
+
Claude Code 2.1.239 names thirty-one hook events and the set grows release by release,
|
|
114
|
+
so the ones we register are gated on the version actually installed. The version is
|
|
115
|
+
read from disk and never by running the binary: `codex --version` once submitted the
|
|
116
|
+
Codex CLI to XProtect, which killed it and moved the 181 MB binary to the Trash, so
|
|
117
|
+
setup deleted the user's installation as a side effect of asking its version.
|
|
118
|
+
|
|
119
|
+
An unreadable version registers only the events that predate gating. Each registration
|
|
120
|
+
records `registered|<version>`, which is what lets an upgrade pick up newer events
|
|
121
|
+
without anyone re-running setup.
|
|
122
|
+
|
|
123
|
+
## Admission rules
|
|
124
|
+
|
|
125
|
+
Publishing this package is a version bump merged to main, and a hook change is not
|
|
126
|
+
live until that happens, so a hook that floods on one event would otherwise need a
|
|
127
|
+
release to stop. `~/.pushary/admission-rules.json` can disable an `(agent, event)`
|
|
128
|
+
pair as data.
|
|
129
|
+
|
|
130
|
+
Deny-only by construction: a rule can silence a hook we ship, and no shape of the file
|
|
131
|
+
can enable one we do not. A file that cannot be read admits everything, because
|
|
132
|
+
failing closed would let one malformed download silence every hook on the machine.
|
|
133
|
+
|
|
111
134
|
## Commands
|
|
112
135
|
|
|
113
136
|
<!-- Managed: every command below is declared in src/cli/commands.ts, and a test
|
|
@@ -123,6 +146,7 @@ logout Remove the key this machine stores, and say what it does not remove
|
|
|
123
146
|
connect Connect a phone without re-running setup (--app for the Pushary app)
|
|
124
147
|
bell Free local bell when an agent finishes or needs you. No account, no key, no network
|
|
125
148
|
doctor Verify your Pushary installation is working
|
|
149
|
+
disconnect Turn off one agent (claude|codex|gemini|cursor)
|
|
126
150
|
clean Remove all Pushary configuration (--yes for non-interactive)
|
|
127
151
|
mode Switch approval mode (push_only, push_first, terminal_only)
|
|
128
152
|
wait Show or set the "wait for your phone" ladder (pushary wait 45)
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
handleBell
|
|
4
4
|
} from "../chunk-7PZPJSWI.js";
|
|
5
|
+
import {
|
|
6
|
+
readHookInput
|
|
7
|
+
} from "../chunk-6CUUA7HO.js";
|
|
5
8
|
import "../chunk-K7CSRGKZ.js";
|
|
6
9
|
import "../chunk-BC3VCZ3E.js";
|
|
7
10
|
import "../chunk-6MTNS63X.js";
|
|
@@ -11,10 +14,9 @@ import "../chunk-MQMPG5X4.js";
|
|
|
11
14
|
|
|
12
15
|
// bin/pushary-bell-hook.ts
|
|
13
16
|
var main = async () => {
|
|
14
|
-
let rawInput = "";
|
|
15
17
|
try {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
const input = await readHookInput("claude");
|
|
19
|
+
if (input) handleBell(input);
|
|
18
20
|
} catch {
|
|
19
21
|
}
|
|
20
22
|
process.stdout.write("{}");
|
package/dist/bin/pushary-bell.js
CHANGED
|
@@ -6,9 +6,13 @@ import {
|
|
|
6
6
|
ring,
|
|
7
7
|
upgradeThreshold
|
|
8
8
|
} from "../chunk-7PZPJSWI.js";
|
|
9
|
+
import {
|
|
10
|
+
guardBinary
|
|
11
|
+
} from "../chunk-PWTKKQQI.js";
|
|
12
|
+
import "../chunk-VT4IVERX.js";
|
|
9
13
|
import {
|
|
10
14
|
claudeSettings
|
|
11
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-HVZIHTOO.js";
|
|
12
16
|
import {
|
|
13
17
|
createIo
|
|
14
18
|
} from "../chunk-5RUIFDTP.js";
|
|
@@ -18,8 +22,8 @@ import {
|
|
|
18
22
|
} from "../chunk-GMXKITVA.js";
|
|
19
23
|
import {
|
|
20
24
|
exitOnHelpFlag
|
|
21
|
-
} from "../chunk-
|
|
22
|
-
import "../chunk-
|
|
25
|
+
} from "../chunk-SQOFGPNX.js";
|
|
26
|
+
import "../chunk-GOEXZ5QJ.js";
|
|
23
27
|
import "../chunk-BC3VCZ3E.js";
|
|
24
28
|
import "../chunk-DINDL2CF.js";
|
|
25
29
|
import {
|
|
@@ -53,10 +57,7 @@ var isBellHook = (entry) => {
|
|
|
53
57
|
var BELL_TIMEOUT_SECONDS = 5;
|
|
54
58
|
var BELL_NOTIFICATION_MATCHER = "idle_prompt|agent_needs_input|agent_completed";
|
|
55
59
|
var addBellHooks = (settings, binDir) => {
|
|
56
|
-
const resolve = (name) =>
|
|
57
|
-
const path = binDir ? join(binDir, name) : name;
|
|
58
|
-
return /\s/.test(path) ? `"${path}"` : path;
|
|
59
|
-
};
|
|
60
|
+
const resolve = (name) => guardBinary(binDir ? join(binDir, name) : name);
|
|
60
61
|
const hooks = ensureRecord(settings, "hooks");
|
|
61
62
|
const notification = (Array.isArray(hooks.Notification) ? hooks.Notification : []).filter((entry) => !isBellHook(entry));
|
|
62
63
|
notification.push({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ensureDaemonRunning
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-WEAQQSHQ.js";
|
|
5
5
|
import {
|
|
6
6
|
buildTranscriptRecords,
|
|
7
7
|
decodePublicKey,
|
|
@@ -9,21 +9,22 @@ import {
|
|
|
9
9
|
generateSessionKey,
|
|
10
10
|
uploadTranscriptRecords,
|
|
11
11
|
wrapSessionKey
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-GCKAWHFP.js";
|
|
13
13
|
import {
|
|
14
|
-
startProcessOwnership
|
|
15
|
-
|
|
14
|
+
startProcessOwnership,
|
|
15
|
+
withSpawnProcessOwnership
|
|
16
|
+
} from "../chunk-YKW6JCWI.js";
|
|
16
17
|
import {
|
|
17
18
|
needsShell,
|
|
18
19
|
spawnClaude
|
|
19
20
|
} from "../chunk-XHKBHWLX.js";
|
|
20
21
|
import "../chunk-VT4IVERX.js";
|
|
21
|
-
import "../chunk-
|
|
22
|
+
import "../chunk-HVZIHTOO.js";
|
|
22
23
|
import {
|
|
23
24
|
KILL_REASON,
|
|
24
25
|
isDeferAnswer,
|
|
25
26
|
resolveGate
|
|
26
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-GXIFADGD.js";
|
|
27
28
|
import {
|
|
28
29
|
askUser,
|
|
29
30
|
cancelQuestion,
|
|
@@ -35,7 +36,7 @@ import {
|
|
|
35
36
|
reportEvent,
|
|
36
37
|
scopePathFor,
|
|
37
38
|
waitForAnswer
|
|
38
|
-
} from "../chunk-
|
|
39
|
+
} from "../chunk-EXMUM226.js";
|
|
39
40
|
import {
|
|
40
41
|
getMachineId
|
|
41
42
|
} from "../chunk-RN3NOEJF.js";
|
|
@@ -113,9 +114,9 @@ var runLocalPassthrough = (binary, args2) => {
|
|
|
113
114
|
cleanup();
|
|
114
115
|
resolve2(127);
|
|
115
116
|
});
|
|
116
|
-
child.on("exit", (
|
|
117
|
+
child.on("exit", (code2, signal) => {
|
|
117
118
|
cleanup();
|
|
118
|
-
if (typeof
|
|
119
|
+
if (typeof code2 === "number") resolve2(code2);
|
|
119
120
|
else if (signal) resolve2(128 + (SIGNAL_NUMBERS[signal] ?? 0));
|
|
120
121
|
else resolve2(0);
|
|
121
122
|
});
|
|
@@ -139,9 +140,11 @@ var createSyncState = () => ({
|
|
|
139
140
|
sessionKey: null,
|
|
140
141
|
wrappedKey: null,
|
|
141
142
|
seen: /* @__PURE__ */ new Set(),
|
|
142
|
-
keyUploaded: false
|
|
143
|
+
keyUploaded: false,
|
|
144
|
+
keyRejected: false
|
|
143
145
|
});
|
|
144
146
|
var syncTick = async (lines, state, uploader, sessionId, machineId) => {
|
|
147
|
+
if (state.keyRejected) return state;
|
|
145
148
|
let sessionKey = state.sessionKey;
|
|
146
149
|
let wrappedKey = state.wrappedKey;
|
|
147
150
|
if (!sessionKey) {
|
|
@@ -152,9 +155,9 @@ var syncTick = async (lines, state, uploader, sessionId, machineId) => {
|
|
|
152
155
|
}
|
|
153
156
|
const built = buildTranscriptRecords(lines, sessionKey, state.seen);
|
|
154
157
|
if (built.records.length === 0) {
|
|
155
|
-
return { sessionKey, wrappedKey, seen: built.seen
|
|
158
|
+
return { ...state, sessionKey, wrappedKey, seen: built.seen };
|
|
156
159
|
}
|
|
157
|
-
const
|
|
160
|
+
const result = await uploader.uploadRecords({
|
|
158
161
|
sessionId,
|
|
159
162
|
machineId,
|
|
160
163
|
dataEncryptionKey: state.keyUploaded ? void 0 : wrappedKey ?? void 0,
|
|
@@ -163,8 +166,9 @@ var syncTick = async (lines, state, uploader, sessionId, machineId) => {
|
|
|
163
166
|
return {
|
|
164
167
|
sessionKey,
|
|
165
168
|
wrappedKey,
|
|
166
|
-
seen: ok ? built.seen : state.seen,
|
|
167
|
-
keyUploaded: state.keyUploaded ||
|
|
169
|
+
seen: result.ok ? built.seen : state.seen,
|
|
170
|
+
keyUploaded: state.keyUploaded || result.keyRegistered,
|
|
171
|
+
keyRejected: result.keyConflict
|
|
168
172
|
};
|
|
169
173
|
};
|
|
170
174
|
var readLines = (path) => {
|
|
@@ -258,7 +262,7 @@ var ensureClaudeSdk = async (log) => {
|
|
|
258
262
|
{ cwd: dir, stdio: "ignore", shell: process.platform === "win32" }
|
|
259
263
|
);
|
|
260
264
|
child.on("error", () => resolve2(false));
|
|
261
|
-
child.on("close", (
|
|
265
|
+
child.on("close", (code2) => resolve2(code2 === 0));
|
|
262
266
|
});
|
|
263
267
|
if (!installed) {
|
|
264
268
|
log?.(`[pushary] could not auto-install the SDK. Install it manually: npm i -g ${SDK_SPEC}`);
|
|
@@ -1039,13 +1043,13 @@ var runLocalLeg = (session, deps = {}) => {
|
|
|
1039
1043
|
}
|
|
1040
1044
|
finish();
|
|
1041
1045
|
});
|
|
1042
|
-
child.on("exit", (
|
|
1046
|
+
child.on("exit", (code2, signal) => {
|
|
1043
1047
|
if (signal === "SIGTERM" && legAbort.signal.aborted) {
|
|
1044
1048
|
} else if (!exitReason) {
|
|
1045
1049
|
if (session.input.bufferedCount > 0) {
|
|
1046
1050
|
exitReason = "switch";
|
|
1047
|
-
} else if (typeof
|
|
1048
|
-
exitReason = { type: "exit", code };
|
|
1051
|
+
} else if (typeof code2 === "number") {
|
|
1052
|
+
exitReason = { type: "exit", code: code2 };
|
|
1049
1053
|
} else if (signal) {
|
|
1050
1054
|
exitReason = { type: "exit", code: SIGNAL_EXIT_BASE + (SIGNAL_NUMBERS2[signal] ?? 0) };
|
|
1051
1055
|
} else {
|
|
@@ -1492,10 +1496,15 @@ var runClaudeWrapper = async (args2) => {
|
|
|
1492
1496
|
|
|
1493
1497
|
// bin/pushary-claude.ts
|
|
1494
1498
|
var args = resolveClaudeArgs(process.argv);
|
|
1495
|
-
|
|
1499
|
+
var spawnId = process.env.PUSHARY_SPAWN_ID?.trim() || void 0;
|
|
1500
|
+
var code = await withSpawnProcessOwnership(spawnId, async () => {
|
|
1501
|
+
await ensureDaemonRunning();
|
|
1502
|
+
return runClaudeWrapper(args);
|
|
1503
|
+
}).catch((err) => {
|
|
1496
1504
|
process.stderr.write(
|
|
1497
1505
|
`[pushary] wrapper error: ${err instanceof Error ? err.message : String(err)}
|
|
1498
1506
|
`
|
|
1499
1507
|
);
|
|
1500
|
-
|
|
1508
|
+
return 1;
|
|
1501
1509
|
});
|
|
1510
|
+
process.exit(code);
|
|
@@ -4,14 +4,15 @@ import {
|
|
|
4
4
|
shortenHome,
|
|
5
5
|
unregisterPluginLocation,
|
|
6
6
|
vscodeSettingsTargets
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-ADISVXZL.js";
|
|
8
8
|
import {
|
|
9
9
|
removeGeminiSettings
|
|
10
10
|
} from "../chunk-DOXSWT4Q.js";
|
|
11
11
|
import {
|
|
12
12
|
removeClaudeMcpServers,
|
|
13
13
|
removePusharySettings
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-64PXDATC.js";
|
|
15
|
+
import "../chunk-PWTKKQQI.js";
|
|
15
16
|
import {
|
|
16
17
|
execNpm
|
|
17
18
|
} from "../chunk-VT4IVERX.js";
|
|
@@ -32,14 +33,14 @@ import {
|
|
|
32
33
|
pusharyDir,
|
|
33
34
|
removeCodexHooks,
|
|
34
35
|
vscodePluginDir
|
|
35
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-HVZIHTOO.js";
|
|
36
37
|
import {
|
|
37
38
|
rejectUnknownFlags
|
|
38
39
|
} from "../chunk-GMXKITVA.js";
|
|
39
40
|
import {
|
|
40
41
|
exitOnHelpFlag
|
|
41
|
-
} from "../chunk-
|
|
42
|
-
import "../chunk-
|
|
42
|
+
} from "../chunk-SQOFGPNX.js";
|
|
43
|
+
import "../chunk-GOEXZ5QJ.js";
|
|
43
44
|
import {
|
|
44
45
|
removeClaudeAlias
|
|
45
46
|
} from "../chunk-BC3VCZ3E.js";
|
|
@@ -6,18 +6,19 @@ import {
|
|
|
6
6
|
postLiveSession,
|
|
7
7
|
postLiveSessionUntilAccepted,
|
|
8
8
|
startLiveSessionHeartbeat
|
|
9
|
-
} from "../chunk-
|
|
10
|
-
import "../chunk-
|
|
9
|
+
} from "../chunk-ERYKGH4J.js";
|
|
10
|
+
import "../chunk-GCKAWHFP.js";
|
|
11
11
|
import {
|
|
12
|
-
startProcessOwnership
|
|
13
|
-
|
|
12
|
+
startProcessOwnership,
|
|
13
|
+
withSpawnProcessOwnership
|
|
14
|
+
} from "../chunk-YKW6JCWI.js";
|
|
14
15
|
import {
|
|
15
16
|
spawnClaude
|
|
16
17
|
} from "../chunk-XHKBHWLX.js";
|
|
17
18
|
import {
|
|
18
19
|
resolveBinary
|
|
19
20
|
} from "../chunk-VT4IVERX.js";
|
|
20
|
-
import "../chunk-
|
|
21
|
+
import "../chunk-HVZIHTOO.js";
|
|
21
22
|
import {
|
|
22
23
|
getMachineId
|
|
23
24
|
} from "../chunk-RN3NOEJF.js";
|
|
@@ -327,7 +328,7 @@ var abort = new AbortController();
|
|
|
327
328
|
var signals = process.platform === "win32" ? ["SIGINT", "SIGTERM"] : ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
328
329
|
var stop = () => abort.abort();
|
|
329
330
|
for (const signal of signals) process.on(signal, stop);
|
|
330
|
-
var outcome = await runCodexBridge({
|
|
331
|
+
var outcome = await withSpawnProcessOwnership(spawnId, () => runCodexBridge({
|
|
331
332
|
apiKey: getApiKey(),
|
|
332
333
|
baseUrl: getBaseUrl(),
|
|
333
334
|
machineId: getMachineId(),
|
|
@@ -343,6 +344,6 @@ var outcome = await runCodexBridge({
|
|
|
343
344
|
},
|
|
344
345
|
signal: abort.signal,
|
|
345
346
|
transcripts: process.env.PUSHARY_TRANSCRIPTS === "1"
|
|
346
|
-
});
|
|
347
|
+
}));
|
|
347
348
|
for (const signal of signals) process.off(signal, stop);
|
|
348
349
|
process.exit(outcome === "stopped" ? 0 : 1);
|
|
@@ -3,12 +3,15 @@ import {
|
|
|
3
3
|
isGatingMoment,
|
|
4
4
|
recordKeylessMoment
|
|
5
5
|
} from "../chunk-NJ7JT26G.js";
|
|
6
|
+
import {
|
|
7
|
+
readHookInput
|
|
8
|
+
} from "../chunk-6CUUA7HO.js";
|
|
6
9
|
import {
|
|
7
10
|
KILL_REASON,
|
|
8
11
|
denyReasonFrom,
|
|
9
12
|
isDeferAnswer,
|
|
10
13
|
resolveGate
|
|
11
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-GXIFADGD.js";
|
|
12
15
|
import {
|
|
13
16
|
CODEX_AGENT,
|
|
14
17
|
DEFAULT_SESSION,
|
|
@@ -28,6 +31,7 @@ import {
|
|
|
28
31
|
handleStop,
|
|
29
32
|
handleUserPrompt,
|
|
30
33
|
permissionTimeoutDecision,
|
|
34
|
+
policyRequestFor,
|
|
31
35
|
preToolUseTimeoutDecision,
|
|
32
36
|
readLastPrompt,
|
|
33
37
|
repoKeyFor,
|
|
@@ -39,7 +43,7 @@ import {
|
|
|
39
43
|
toPolicyLookup,
|
|
40
44
|
toPolicyLookups,
|
|
41
45
|
waitForAnswer
|
|
42
|
-
} from "../chunk-
|
|
46
|
+
} from "../chunk-EXMUM226.js";
|
|
43
47
|
import {
|
|
44
48
|
getMachineId
|
|
45
49
|
} from "../chunk-RN3NOEJF.js";
|
|
@@ -143,6 +147,7 @@ var pushQuestion = async (apiKey, input, lookup, waitSeconds, mode) => {
|
|
|
143
147
|
action: description.slice(0, DECISION_LINE_MAX),
|
|
144
148
|
blocker: deriveBlocker(mode),
|
|
145
149
|
actionBody: deriveActionBody(input.tool_name ?? "", input.tool_input ?? {}),
|
|
150
|
+
repoKey: repoKeyFor(input.cwd),
|
|
146
151
|
// Codex has always sent this; it is what claimNotify and markApproved already
|
|
147
152
|
// key on locally. Passing it through lets the server collapse a retried create
|
|
148
153
|
// the same way, instead of putting the same approval on the phone twice.
|
|
@@ -177,9 +182,10 @@ var notifyApprovalNeeded = async (apiKey, input, lookup) => {
|
|
|
177
182
|
};
|
|
178
183
|
var codexGate = async (apiKey, input) => {
|
|
179
184
|
const modeState = await fetchModeState(apiKey, input.session_id);
|
|
185
|
+
const policyRequest = policyRequestFor("codex");
|
|
180
186
|
const lookup = toPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd);
|
|
181
187
|
const lookups = toPolicyLookups(input.tool_name ?? "", input.tool_input ?? {}, input.cwd);
|
|
182
|
-
const config = await getPolicy(apiKey, modeState.policyVersion,
|
|
188
|
+
const config = await getPolicy(apiKey, modeState.policyVersion, policyRequest.agent, policyRequest.repoAware, input.session_id);
|
|
183
189
|
const verdict = resolveGate({
|
|
184
190
|
modeState,
|
|
185
191
|
config,
|
|
@@ -307,17 +313,8 @@ var emit = (wire) => {
|
|
|
307
313
|
if (wire) process.stdout.write(JSON.stringify(wire));
|
|
308
314
|
};
|
|
309
315
|
var main = async () => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
rawInput += chunk;
|
|
313
|
-
}
|
|
314
|
-
if (!rawInput.trim()) {
|
|
315
|
-
process.exit(0);
|
|
316
|
-
}
|
|
317
|
-
let input;
|
|
318
|
-
try {
|
|
319
|
-
input = JSON.parse(rawInput);
|
|
320
|
-
} catch {
|
|
316
|
+
const input = await readHookInput("codex");
|
|
317
|
+
if (!input) {
|
|
321
318
|
process.exit(0);
|
|
322
319
|
}
|
|
323
320
|
try {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
printKeyCheck
|
|
4
|
+
} from "../chunk-FBGBJXVC.js";
|
|
2
5
|
import {
|
|
3
6
|
confirmAppConnection,
|
|
4
7
|
connectDevice,
|
|
5
8
|
printConnectInstructions
|
|
6
9
|
} from "../chunk-6LC4WSCL.js";
|
|
7
10
|
import "../chunk-3EGEA4KH.js";
|
|
8
|
-
import {
|
|
9
|
-
printKeyCheck
|
|
10
|
-
} from "../chunk-FBGBJXVC.js";
|
|
11
11
|
import {
|
|
12
12
|
checkApiKey
|
|
13
13
|
} from "../chunk-F52SIIDG.js";
|
|
@@ -22,8 +22,8 @@ import {
|
|
|
22
22
|
} from "../chunk-GMXKITVA.js";
|
|
23
23
|
import {
|
|
24
24
|
exitOnHelpFlag
|
|
25
|
-
} from "../chunk-
|
|
26
|
-
import "../chunk-
|
|
25
|
+
} from "../chunk-SQOFGPNX.js";
|
|
26
|
+
import "../chunk-GOEXZ5QJ.js";
|
|
27
27
|
import "../chunk-BC3VCZ3E.js";
|
|
28
28
|
import "../chunk-DINDL2CF.js";
|
|
29
29
|
import "../chunk-6MTNS63X.js";
|
|
@@ -8,15 +8,16 @@ import {
|
|
|
8
8
|
acquireDaemonLease,
|
|
9
9
|
readDaemonStatus,
|
|
10
10
|
requestDaemonHandoff
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-WEAQQSHQ.js";
|
|
12
12
|
import {
|
|
13
13
|
CODEX_BRIDGE_CAPABILITY,
|
|
14
14
|
CODEX_RESUME_CAPABILITY,
|
|
15
15
|
geminiLiveCapabilities
|
|
16
16
|
} from "../chunk-NRGRUOAR.js";
|
|
17
17
|
import {
|
|
18
|
+
findLiveProcessBySpawnId,
|
|
18
19
|
ownsLiveProcess
|
|
19
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-YKW6JCWI.js";
|
|
20
21
|
import {
|
|
21
22
|
spawnClaude
|
|
22
23
|
} from "../chunk-XHKBHWLX.js";
|
|
@@ -25,14 +26,14 @@ import {
|
|
|
25
26
|
resolveGlobalPackageVersion,
|
|
26
27
|
resolvePackageVersionFromBinary
|
|
27
28
|
} from "../chunk-VT4IVERX.js";
|
|
28
|
-
import "../chunk-
|
|
29
|
+
import "../chunk-HVZIHTOO.js";
|
|
29
30
|
import {
|
|
30
31
|
getMachineId
|
|
31
32
|
} from "../chunk-RN3NOEJF.js";
|
|
32
33
|
import {
|
|
33
34
|
exitOnHelpFlag
|
|
34
|
-
} from "../chunk-
|
|
35
|
-
import "../chunk-
|
|
35
|
+
} from "../chunk-SQOFGPNX.js";
|
|
36
|
+
import "../chunk-GOEXZ5QJ.js";
|
|
36
37
|
import "../chunk-DINDL2CF.js";
|
|
37
38
|
import "../chunk-6MTNS63X.js";
|
|
38
39
|
import {
|
|
@@ -135,6 +136,23 @@ var spawnAcknowledgement = (request, machineId, outcome) => {
|
|
|
135
136
|
}
|
|
136
137
|
};
|
|
137
138
|
};
|
|
139
|
+
var launchSpawnClaim = async (request, runtime) => {
|
|
140
|
+
const existingPid = await runtime.findExistingPid(request.id);
|
|
141
|
+
if (existingPid !== null) {
|
|
142
|
+
await runtime.acknowledge(spawnAcknowledgement(request, runtime.machineId, { state: "started", pid: existingPid }));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
let pid;
|
|
146
|
+
try {
|
|
147
|
+
pid = await runtime.launch(request);
|
|
148
|
+
} catch (err) {
|
|
149
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
150
|
+
runtime.reportLaunchError(message);
|
|
151
|
+
await runtime.acknowledge(spawnAcknowledgement(request, runtime.machineId, { state: "failed", message }));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
await runtime.acknowledge(spawnAcknowledgement(request, runtime.machineId, { state: "started", pid }));
|
|
155
|
+
};
|
|
138
156
|
var DrainError = class extends Error {
|
|
139
157
|
constructor(status) {
|
|
140
158
|
super(`drain ${status}`);
|
|
@@ -397,19 +415,19 @@ var runSpawnDaemon = async () => {
|
|
|
397
415
|
}
|
|
398
416
|
throw lastError ?? new Error("spawn acknowledgement stopped");
|
|
399
417
|
};
|
|
400
|
-
const launchClaim =
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
pid
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
};
|
|
418
|
+
const launchClaim = (request) => launchSpawnClaim(request, {
|
|
419
|
+
machineId,
|
|
420
|
+
findExistingPid: async (spawnId) => {
|
|
421
|
+
for (const [pid, launchedSpawnId] of launchedProcesses) {
|
|
422
|
+
if (launchedSpawnId === spawnId) return pid;
|
|
423
|
+
}
|
|
424
|
+
return findLiveProcessBySpawnId(spawnId);
|
|
425
|
+
},
|
|
426
|
+
launch,
|
|
427
|
+
acknowledge,
|
|
428
|
+
reportLaunchError: (message) => stderr(`[pushary] could not launch session: ${message}
|
|
429
|
+
`)
|
|
430
|
+
});
|
|
413
431
|
const nextDelay = () => {
|
|
414
432
|
if (errorStreak > 0) return Math.min(SLOW_POLL_MS * 2 ** (errorStreak - 1), MAX_ERROR_BACKOFF_MS);
|
|
415
433
|
return idleTicks >= FAST_DECAY_TICKS ? SLOW_POLL_MS : FAST_POLL_MS;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|