@pushary/agent-hooks 0.87.1 → 0.87.3
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/data/cursor-plugin/hooks/hooks.json +7 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +85 -28
- package/data/cursor-plugin/scripts/pushary-gate.test.mjs +42 -1
- package/dist/bin/pushary-bell-hook.js +4 -4
- package/dist/bin/pushary-bell.js +7 -6
- package/dist/bin/pushary-claude.js +14 -11
- package/dist/bin/pushary-clean.js +36 -20
- package/dist/bin/pushary-codex-bridge.js +29 -11
- package/dist/bin/pushary-codex-hook.js +129 -52
- package/dist/bin/pushary-codex.js +4 -3
- package/dist/bin/pushary-connect.js +6 -6
- package/dist/bin/pushary-daemon.js +5 -4
- package/dist/bin/pushary-disconnect.js +65 -8
- package/dist/bin/pushary-doctor.js +79 -18
- package/dist/bin/pushary-elicitation-hook.js +1 -1
- package/dist/bin/pushary-gemini-bridge.js +6 -5
- package/dist/bin/pushary-gemini-hook.js +12 -10
- package/dist/bin/pushary-hook.js +6 -5
- package/dist/bin/pushary-login.js +4 -4
- package/dist/bin/pushary-logout.js +5 -4
- package/dist/bin/pushary-mode.js +3 -3
- package/dist/bin/pushary-notification-hook.js +4 -3
- package/dist/bin/pushary-opencode-hook.d.ts +1 -0
- package/dist/bin/pushary-opencode-hook.js +329 -0
- package/dist/bin/pushary-permission-denied-hook.js +6 -5
- package/dist/bin/pushary-permission-hook.js +6 -5
- package/dist/bin/pushary-post-hook.js +4 -3
- package/dist/bin/pushary-prompt-hook.js +4 -3
- package/dist/bin/pushary-session-end-hook.js +4 -3
- package/dist/bin/pushary-session-start-hook.js +4 -3
- package/dist/bin/pushary-setup.js +240 -85
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +5 -5
- package/dist/bin/pushary-stop-hook.js +4 -3
- package/dist/bin/pushary-stopfailure-hook.js +4 -3
- package/dist/bin/pushary-upgrade.js +7 -6
- package/dist/bin/pushary-wait.js +3 -3
- package/dist/{chunk-5646VKR4.js → chunk-2JYQAW4B.js} +5 -53
- package/dist/chunk-7J67NYK4.js +287 -0
- package/dist/{chunk-M7RYIZX6.js → chunk-B7ZBHWOW.js} +10 -1
- package/dist/{chunk-7YJS2VTG.js → chunk-DMW54VWK.js} +12 -1
- package/dist/{chunk-LT2VLPUK.js → chunk-DPUOW6NS.js} +18 -2
- package/dist/{chunk-A3PFIWWN.js → chunk-EJL3PJU7.js} +56 -6
- package/dist/chunk-GIRT7677.js +11 -0
- package/dist/{chunk-HAUTLCMA.js → chunk-ICERLAE2.js} +1 -1
- package/dist/{chunk-ZZE6FN7T.js → chunk-K42RXJPG.js} +52 -1
- package/dist/{chunk-Y3UTTFTZ.js → chunk-KBS53WOE.js} +1 -1
- package/dist/{chunk-SJVLFIYP.js → chunk-KCAFCGGY.js} +1 -1
- package/dist/{chunk-A4I5JGCW.js → chunk-KFAH6KRE.js} +1 -1
- package/dist/{chunk-QBWYNVKY.js → chunk-LGVHYFIB.js} +1 -1
- package/dist/{chunk-HJ44HIUW.js → chunk-OMA2OZRE.js} +11 -9
- package/dist/{chunk-ECEUMWBH.js → chunk-QLMX5LLV.js} +1 -1
- package/dist/{chunk-XW5CHAQX.js → chunk-TAYHEJFF.js} +2 -2
- package/dist/{chunk-JAICP7BA.js → chunk-TD3M232T.js} +1 -1
- package/dist/{chunk-WLLBFDVY.js → chunk-TV3UFGT7.js} +1 -1
- package/dist/{chunk-7MBDFNUC.js → chunk-VIHRP25S.js} +1 -1
- package/dist/{chunk-B3V2H6SA.js → chunk-VL5ZNK6V.js} +43 -12
- package/dist/{chunk-V2XPF77V.js → chunk-VOE4SYPR.js} +42 -279
- package/dist/{chunk-OT4YD54Q.js → chunk-YX6ZKCC5.js} +1 -1
- package/dist/{chunk-2T3GX7WG.js → chunk-YYTLTEGU.js} +1 -1
- package/dist/chunk-ZGC3W23N.js +371 -0
- package/dist/src/index.js +6 -5
- package/package.json +2 -1
|
@@ -6,19 +6,20 @@ import {
|
|
|
6
6
|
postLiveSession,
|
|
7
7
|
postLiveSessionUntilAccepted,
|
|
8
8
|
startLiveSessionHeartbeat
|
|
9
|
-
} from "../chunk-
|
|
10
|
-
import "../chunk-
|
|
9
|
+
} from "../chunk-TV3UFGT7.js";
|
|
10
|
+
import "../chunk-QLMX5LLV.js";
|
|
11
11
|
import {
|
|
12
12
|
startProcessOwnership,
|
|
13
13
|
withSpawnProcessOwnership
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-LGVHYFIB.js";
|
|
15
15
|
import {
|
|
16
16
|
spawnClaude
|
|
17
17
|
} from "../chunk-XHKBHWLX.js";
|
|
18
18
|
import {
|
|
19
19
|
resolveBinary
|
|
20
20
|
} from "../chunk-VT4IVERX.js";
|
|
21
|
-
import "../chunk-
|
|
21
|
+
import "../chunk-VL5ZNK6V.js";
|
|
22
|
+
import "../chunk-GIRT7677.js";
|
|
22
23
|
import {
|
|
23
24
|
getMachineId
|
|
24
25
|
} from "../chunk-RN3NOEJF.js";
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
getApiKey,
|
|
28
29
|
getBaseUrl
|
|
29
30
|
} from "../chunk-2UMNXADU.js";
|
|
30
|
-
import "../chunk-
|
|
31
|
+
import "../chunk-DMW54VWK.js";
|
|
31
32
|
|
|
32
33
|
// bin/pushary-codex-bridge.ts
|
|
33
34
|
import { existsSync } from "fs";
|
|
@@ -36,6 +37,8 @@ import { existsSync } from "fs";
|
|
|
36
37
|
import { spawn } from "child_process";
|
|
37
38
|
import { basename } from "path";
|
|
38
39
|
var MAX_RPC_LINE = 1024 * 1024;
|
|
40
|
+
var STDERR_TAIL_LIMIT = 2e3;
|
|
41
|
+
var FAILURE_MESSAGE_MAX = 240;
|
|
39
42
|
var MAX_PENDING_RPC = 8;
|
|
40
43
|
var MAX_TRANSCRIPT_TEXT = 32 * 1024;
|
|
41
44
|
var MAX_MESSAGE_BUFFERS = 16;
|
|
@@ -46,20 +49,34 @@ var JsonRpcClient = class {
|
|
|
46
49
|
nextId = 1;
|
|
47
50
|
buffer = "";
|
|
48
51
|
closed = false;
|
|
52
|
+
stderrTail = "";
|
|
53
|
+
failure = null;
|
|
49
54
|
constructor(command, cwd, timeoutMs) {
|
|
50
55
|
const options = { cwd, env: process.env, stdio: ["pipe", "pipe", "pipe"] };
|
|
51
56
|
this.child = process.platform === "win32" ? spawnClaude(command.command, command.args ?? [], options) : spawn(command.command, command.args ?? [], options);
|
|
52
57
|
this.child.stdout?.setEncoding("utf8");
|
|
53
58
|
this.child.stdout?.on("data", (chunk) => this.read(String(chunk)));
|
|
54
|
-
this.child.stderr?.
|
|
59
|
+
this.child.stderr?.setEncoding("utf8");
|
|
60
|
+
this.child.stderr?.on("data", (chunk) => this.keepStderrTail(String(chunk)));
|
|
55
61
|
this.child.once("error", (error) => this.fail(error));
|
|
56
|
-
this.child.once("exit", () => this.fail(new Error(
|
|
62
|
+
this.child.once("exit", (code, signal) => this.fail(new Error(this.exitMessage(code, signal))));
|
|
57
63
|
this.timeoutMs = timeoutMs;
|
|
58
64
|
}
|
|
65
|
+
keepStderrTail(chunk) {
|
|
66
|
+
this.stderrTail = (this.stderrTail + chunk).slice(-STDERR_TAIL_LIMIT);
|
|
67
|
+
}
|
|
68
|
+
exitMessage(code, signal) {
|
|
69
|
+
const how = signal ? `signal ${signal}` : `code ${code ?? "unknown"}`;
|
|
70
|
+
const tail = this.stderrTail.trim();
|
|
71
|
+
return tail ? `Codex app-server exited (${how}): ${tail}` : `Codex app-server exited (${how})`;
|
|
72
|
+
}
|
|
59
73
|
timeoutMs;
|
|
60
74
|
get isClosed() {
|
|
61
75
|
return this.closed;
|
|
62
76
|
}
|
|
77
|
+
get closedReason() {
|
|
78
|
+
return this.failure?.message ?? "Codex app-server exited";
|
|
79
|
+
}
|
|
63
80
|
onNotification(listener) {
|
|
64
81
|
this.notifications.add(listener);
|
|
65
82
|
return () => this.notifications.delete(listener);
|
|
@@ -142,6 +159,7 @@ var JsonRpcClient = class {
|
|
|
142
159
|
fail(error) {
|
|
143
160
|
if (this.closed) return;
|
|
144
161
|
this.closed = true;
|
|
162
|
+
this.failure = error;
|
|
145
163
|
this.rejectPending(error);
|
|
146
164
|
if (this.child.exitCode == null) this.child.kill();
|
|
147
165
|
}
|
|
@@ -281,7 +299,7 @@ var runCodexBridge = async (options) => {
|
|
|
281
299
|
await relay?.flush();
|
|
282
300
|
await liveSessionSleep(options.pollIntervalMs ?? 2e3, options.signal);
|
|
283
301
|
}
|
|
284
|
-
if (rpc.isClosed && !options.signal.aborted) throw new Error(
|
|
302
|
+
if (rpc.isClosed && !options.signal.aborted) throw new Error(rpc.closedReason);
|
|
285
303
|
if (activeTurnId) {
|
|
286
304
|
await rpc.request("turn/interrupt", { threadId, turnId: activeTurnId }).catch(() => null);
|
|
287
305
|
}
|
|
@@ -289,8 +307,8 @@ var runCodexBridge = async (options) => {
|
|
|
289
307
|
await report("session_closed", "Session stopped").catch(() => null);
|
|
290
308
|
return "stopped";
|
|
291
309
|
} catch (error) {
|
|
310
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
292
311
|
if (!linked && options.claimId) {
|
|
293
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
294
312
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
295
313
|
const acknowledged = await postLiveSession(options, "/api/agent/spawn/ack", {
|
|
296
314
|
spawnId: options.spawnId,
|
|
@@ -299,7 +317,7 @@ var runCodexBridge = async (options) => {
|
|
|
299
317
|
state: "failed",
|
|
300
318
|
failure: {
|
|
301
319
|
code: "bridge_failed",
|
|
302
|
-
message: message.slice(0,
|
|
320
|
+
message: message.slice(0, FAILURE_MESSAGE_MAX),
|
|
303
321
|
retryable: false
|
|
304
322
|
}
|
|
305
323
|
}).catch(() => null);
|
|
@@ -307,7 +325,7 @@ var runCodexBridge = async (options) => {
|
|
|
307
325
|
await liveSessionSleep(200 * (attempt + 1), options.signal);
|
|
308
326
|
}
|
|
309
327
|
}
|
|
310
|
-
await report("error",
|
|
328
|
+
await report("error", `Live bridge failed: ${message}`.slice(0, FAILURE_MESSAGE_MAX)).catch(() => null);
|
|
311
329
|
return "failed";
|
|
312
330
|
} finally {
|
|
313
331
|
stopHeartbeat();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
isGatingMoment,
|
|
4
4
|
recordKeylessMoment
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-VIHRP25S.js";
|
|
6
6
|
import {
|
|
7
7
|
readHookInput
|
|
8
8
|
} from "../chunk-6CUUA7HO.js";
|
|
@@ -10,19 +10,19 @@ import {
|
|
|
10
10
|
denyReasonFrom,
|
|
11
11
|
isDeferAnswer
|
|
12
12
|
} from "../chunk-YHG74UFF.js";
|
|
13
|
+
import {
|
|
14
|
+
canonicalJson
|
|
15
|
+
} from "../chunk-GIRT7677.js";
|
|
13
16
|
import {
|
|
14
17
|
CODEX_AGENT,
|
|
15
18
|
codexAllow,
|
|
16
19
|
codexDeny,
|
|
17
20
|
codexPass,
|
|
18
|
-
deriveActionBody,
|
|
19
|
-
deriveBlocker,
|
|
20
|
-
deriveToolTarget,
|
|
21
21
|
describeApplyPatch,
|
|
22
|
-
describeToolCall,
|
|
23
22
|
fetchModeState,
|
|
24
23
|
getPolicy,
|
|
25
24
|
handlePostToolUse,
|
|
25
|
+
handleSessionEnd,
|
|
26
26
|
handleStop,
|
|
27
27
|
handleUserPrompt,
|
|
28
28
|
permissionTimeoutDecision,
|
|
@@ -31,11 +31,10 @@ import {
|
|
|
31
31
|
readLastPrompt,
|
|
32
32
|
repoKeyFor,
|
|
33
33
|
reportEvent,
|
|
34
|
-
scopePathFor,
|
|
35
34
|
toCodexWire,
|
|
36
35
|
toPolicyLookup,
|
|
37
36
|
toPolicyLookups
|
|
38
|
-
} from "../chunk-
|
|
37
|
+
} from "../chunk-VOE4SYPR.js";
|
|
39
38
|
import {
|
|
40
39
|
DEFAULT_SESSION,
|
|
41
40
|
askUser,
|
|
@@ -44,12 +43,19 @@ import {
|
|
|
44
43
|
savePendingQuestion,
|
|
45
44
|
sendNotification
|
|
46
45
|
} from "../chunk-J3YDT4HM.js";
|
|
46
|
+
import {
|
|
47
|
+
deriveActionBody,
|
|
48
|
+
deriveBlocker,
|
|
49
|
+
deriveToolTarget,
|
|
50
|
+
describeToolCall,
|
|
51
|
+
scopePathFor
|
|
52
|
+
} from "../chunk-7J67NYK4.js";
|
|
47
53
|
import {
|
|
48
54
|
getMachineId
|
|
49
55
|
} from "../chunk-RN3NOEJF.js";
|
|
50
56
|
import "../chunk-BSZYIAZL.js";
|
|
51
57
|
import "../chunk-SAF6HGAA.js";
|
|
52
|
-
import "../chunk-
|
|
58
|
+
import "../chunk-KBS53WOE.js";
|
|
53
59
|
import "../chunk-7QLSKOSU.js";
|
|
54
60
|
import "../chunk-KZERVKTD.js";
|
|
55
61
|
import {
|
|
@@ -62,57 +68,107 @@ import {
|
|
|
62
68
|
hookWaitClamped,
|
|
63
69
|
hookWaitDeadline,
|
|
64
70
|
resolveGate
|
|
65
|
-
} from "../chunk-
|
|
71
|
+
} from "../chunk-DMW54VWK.js";
|
|
66
72
|
|
|
67
73
|
// bin/pushary-codex-hook.ts
|
|
68
|
-
import { basename
|
|
74
|
+
import { basename } from "path";
|
|
75
|
+
|
|
76
|
+
// src/codex-call-decision.ts
|
|
77
|
+
import { createHash } from "crypto";
|
|
78
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
|
|
69
79
|
import { tmpdir, userInfo } from "os";
|
|
70
|
-
import {
|
|
71
|
-
var
|
|
72
|
-
var APPROVAL_TTL_MS = 10 * 60 * 1e3;
|
|
73
|
-
var sanitizeId = (value) => value.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128);
|
|
80
|
+
import { join } from "path";
|
|
81
|
+
var CODEX_DECISION_TTL_MS = 60 * 1e3;
|
|
74
82
|
var userScope = () => {
|
|
75
83
|
try {
|
|
76
84
|
const info = userInfo();
|
|
77
85
|
const id = typeof info.uid === "number" && info.uid >= 0 ? String(info.uid) : info.username;
|
|
78
|
-
return
|
|
86
|
+
return id.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || "default";
|
|
79
87
|
} catch {
|
|
80
88
|
return "default";
|
|
81
89
|
}
|
|
82
90
|
};
|
|
83
|
-
var
|
|
84
|
-
var
|
|
85
|
-
|
|
91
|
+
var decisionDir = () => join(tmpdir(), `pushary-codex-decisions-${userScope()}`);
|
|
92
|
+
var decisionFile = (key) => join(decisionDir(), key);
|
|
93
|
+
var PRESENTATION_ONLY_INPUT_KEYS = ["description"];
|
|
94
|
+
var identifyingToolInput = (toolInput) => {
|
|
95
|
+
if (!toolInput) return {};
|
|
96
|
+
const identifying = {};
|
|
97
|
+
for (const [key, value] of Object.entries(toolInput)) {
|
|
98
|
+
if (PRESENTATION_ONLY_INPUT_KEYS.includes(key)) continue;
|
|
99
|
+
identifying[key] = value;
|
|
100
|
+
}
|
|
101
|
+
return identifying;
|
|
102
|
+
};
|
|
103
|
+
var codexCallKey = (identity) => {
|
|
104
|
+
if (!identity.sessionId || !identity.turnId || !identity.toolName) return null;
|
|
105
|
+
const material = canonicalJson([
|
|
106
|
+
identity.sessionId,
|
|
107
|
+
identity.turnId,
|
|
108
|
+
identity.toolName,
|
|
109
|
+
identifyingToolInput(identity.toolInput)
|
|
110
|
+
]);
|
|
111
|
+
return createHash("sha256").update(material, "utf-8").digest("hex").slice(0, 32);
|
|
112
|
+
};
|
|
113
|
+
var parseDecision = (raw) => {
|
|
114
|
+
try {
|
|
115
|
+
const parsed = JSON.parse(raw);
|
|
116
|
+
if (parsed.kind === "approved") return { kind: "approved" };
|
|
117
|
+
if (parsed.kind === "handled") return { kind: "handled" };
|
|
118
|
+
if (parsed.kind === "denied") {
|
|
119
|
+
return { kind: "denied", reason: typeof parsed.reason === "string" ? parsed.reason : "" };
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var sweepExpired = (dir, nowMs) => {
|
|
86
127
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
128
|
+
for (const name of readdirSync(dir)) {
|
|
129
|
+
const path = join(dir, name);
|
|
130
|
+
try {
|
|
131
|
+
if (nowMs - statSync(path).mtimeMs >= CODEX_DECISION_TTL_MS) rmSync(path, { force: true });
|
|
132
|
+
} catch {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
89
136
|
} catch {
|
|
137
|
+
return;
|
|
90
138
|
}
|
|
91
139
|
};
|
|
92
|
-
var
|
|
93
|
-
if (!
|
|
94
|
-
const path = join(APPROVAL_DIR, sanitizeId(toolUseId));
|
|
140
|
+
var recordCodexCallDecision = (key, decision) => {
|
|
141
|
+
if (!key) return;
|
|
95
142
|
try {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
143
|
+
const dir = decisionDir();
|
|
144
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
|
|
145
|
+
else sweepExpired(dir, Date.now());
|
|
146
|
+
writeFileSync(decisionFile(key), JSON.stringify(decision), { encoding: "utf-8", mode: 384 });
|
|
99
147
|
} catch {
|
|
100
|
-
return
|
|
148
|
+
return;
|
|
101
149
|
}
|
|
102
150
|
};
|
|
103
|
-
var
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const path = join(APPROVAL_DIR, `notify-${sanitizeId(toolUseId)}`);
|
|
151
|
+
var consumeCodexCallDecision = (key) => {
|
|
152
|
+
if (!key) return null;
|
|
153
|
+
const path = decisionFile(key);
|
|
107
154
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return
|
|
155
|
+
const fresh = Date.now() - statSync(path).mtimeMs < CODEX_DECISION_TTL_MS;
|
|
156
|
+
const raw = readFileSync(path, "utf-8");
|
|
157
|
+
rmSync(path, { force: true });
|
|
158
|
+
return fresh ? parseDecision(raw) : null;
|
|
112
159
|
} catch {
|
|
113
|
-
return
|
|
160
|
+
return null;
|
|
114
161
|
}
|
|
115
162
|
};
|
|
163
|
+
|
|
164
|
+
// bin/pushary-codex-hook.ts
|
|
165
|
+
var callKeyFor = (input) => codexCallKey({
|
|
166
|
+
sessionId: input.session_id,
|
|
167
|
+
turnId: input.turn_id,
|
|
168
|
+
toolName: input.tool_name,
|
|
169
|
+
toolInput: input.tool_input
|
|
170
|
+
});
|
|
171
|
+
var START_MS = Date.now();
|
|
116
172
|
var agentNameFor = (input) => `Codex - ${basename(input.cwd ?? process.cwd())}`;
|
|
117
173
|
var describeFor = (input, lookup) => input.tool_name === "apply_patch" ? describeApplyPatch(input.tool_input?.command) ?? describeToolCall(lookup.tool, lookup.input, "hook") : describeToolCall(lookup.tool, lookup.input, "hook");
|
|
118
174
|
var pushQuestion = async (apiKey, input, lookup, waitSeconds, mode) => {
|
|
@@ -184,7 +240,14 @@ var codexGate = async (apiKey, input) => {
|
|
|
184
240
|
});
|
|
185
241
|
return { verdict, lookup };
|
|
186
242
|
};
|
|
243
|
+
var fromRecordedDecision = (decision) => {
|
|
244
|
+
if (decision.kind === "approved") return codexAllow();
|
|
245
|
+
if (decision.kind === "denied") return codexDeny(decision.reason);
|
|
246
|
+
return codexPass();
|
|
247
|
+
};
|
|
187
248
|
var decidePermissionRequest = async (input) => {
|
|
249
|
+
const recorded = consumeCodexCallDecision(callKeyFor(input));
|
|
250
|
+
if (recorded) return fromRecordedDecision(recorded);
|
|
188
251
|
try {
|
|
189
252
|
const apiKey = getApiKey();
|
|
190
253
|
const { verdict, lookup } = await codexGate(apiKey, input);
|
|
@@ -195,7 +258,7 @@ var decidePermissionRequest = async (input) => {
|
|
|
195
258
|
const toolPolicy = verdict.policy;
|
|
196
259
|
if (toolPolicy.mode === "terminal_only") return codexPass();
|
|
197
260
|
if (toolPolicy.mode === "notify_only") {
|
|
198
|
-
|
|
261
|
+
await notifyApprovalNeeded(apiKey, input, lookup);
|
|
199
262
|
return codexPass();
|
|
200
263
|
}
|
|
201
264
|
const waitSeconds = toolPolicy.mode === "push_first" ? toolPolicy.pushFirstSeconds : effectiveWaitSeconds(toolPolicy.timeoutAction, toolPolicy.timeoutSeconds, "codex");
|
|
@@ -207,22 +270,24 @@ var decidePermissionRequest = async (input) => {
|
|
|
207
270
|
}
|
|
208
271
|
if (answer.answered) {
|
|
209
272
|
if (isDeferAnswer(answer.value)) return codexPass();
|
|
210
|
-
if (answer.value === "yes")
|
|
211
|
-
if (toolPolicy.mode === "push_only") markApproved(input.tool_use_id);
|
|
212
|
-
return codexAllow();
|
|
213
|
-
}
|
|
273
|
+
if (answer.value === "yes") return codexAllow();
|
|
214
274
|
return codexDeny(denyReasonFrom(answer.value, answer.note));
|
|
215
275
|
}
|
|
216
276
|
savePendingQuestion(input.session_id || DEFAULT_SESSION, correlationId);
|
|
217
277
|
if (toolPolicy.mode === "push_first") return codexPass();
|
|
218
278
|
if (clampCut) return codexPass();
|
|
219
|
-
|
|
220
|
-
if (decision.kind === "allow") markApproved(input.tool_use_id);
|
|
221
|
-
return decision;
|
|
279
|
+
return permissionTimeoutDecision(toolPolicy.timeoutAction);
|
|
222
280
|
} catch {
|
|
223
281
|
return codexPass();
|
|
224
282
|
}
|
|
225
283
|
};
|
|
284
|
+
var recordPreToolUse = (key, outcome) => {
|
|
285
|
+
recordCodexCallDecision(
|
|
286
|
+
key,
|
|
287
|
+
outcome.kind === "deny" ? { kind: "denied", reason: outcome.reason } : { kind: "handled" }
|
|
288
|
+
);
|
|
289
|
+
return outcome;
|
|
290
|
+
};
|
|
226
291
|
var decidePreToolUse = async (input) => {
|
|
227
292
|
let apiKey;
|
|
228
293
|
try {
|
|
@@ -249,30 +314,35 @@ var decidePreToolUse = async (input) => {
|
|
|
249
314
|
if (verdict.kind === "deny") return codexDeny(verdict.reason);
|
|
250
315
|
if (verdict.kind === "defer") return codexPass();
|
|
251
316
|
const toolPolicy = verdict.policy;
|
|
317
|
+
const callKey = callKeyFor(input);
|
|
252
318
|
if (toolPolicy.mode === "push_only") {
|
|
253
|
-
if (consumeApproval(input.tool_use_id)) return codexPass();
|
|
254
319
|
let pushed;
|
|
255
320
|
try {
|
|
256
321
|
pushed = await pushQuestion(apiKey, input, lookup, effectiveWaitSeconds(toolPolicy.timeoutAction, toolPolicy.timeoutSeconds, "codex"), "push_only");
|
|
257
322
|
} catch {
|
|
258
|
-
return preToolUseTimeoutDecision(toolPolicy.timeoutAction, "Push notification failed, denying per policy");
|
|
323
|
+
return recordPreToolUse(callKey, preToolUseTimeoutDecision(toolPolicy.timeoutAction, "Push notification failed, denying per policy"));
|
|
259
324
|
}
|
|
260
325
|
const { answer, correlationId, suppressed, clampCut } = pushed;
|
|
261
326
|
if (suppressed) {
|
|
262
327
|
await cancelQuestion(apiKey, correlationId).catch(() => {
|
|
263
328
|
});
|
|
264
|
-
return codexPass();
|
|
329
|
+
return recordPreToolUse(callKey, codexPass());
|
|
265
330
|
}
|
|
266
331
|
if (answer.answered) {
|
|
267
|
-
if (isDeferAnswer(answer.value)) return codexPass();
|
|
268
|
-
|
|
332
|
+
if (isDeferAnswer(answer.value)) return recordPreToolUse(callKey, codexPass());
|
|
333
|
+
if (answer.value === "yes") {
|
|
334
|
+
recordCodexCallDecision(callKey, { kind: "approved" });
|
|
335
|
+
return codexPass();
|
|
336
|
+
}
|
|
337
|
+
return recordPreToolUse(callKey, codexDeny(denyReasonFrom(answer.value, answer.note)));
|
|
269
338
|
}
|
|
270
339
|
savePendingQuestion(input.session_id || DEFAULT_SESSION, correlationId);
|
|
271
|
-
if (clampCut) return codexPass();
|
|
272
|
-
return preToolUseTimeoutDecision(toolPolicy.timeoutAction);
|
|
340
|
+
if (clampCut) return recordPreToolUse(callKey, codexPass());
|
|
341
|
+
return recordPreToolUse(callKey, preToolUseTimeoutDecision(toolPolicy.timeoutAction));
|
|
273
342
|
}
|
|
274
343
|
if (toolPolicy.mode === "notify_only") {
|
|
275
|
-
|
|
344
|
+
await notifyApprovalNeeded(apiKey, input, lookup);
|
|
345
|
+
recordCodexCallDecision(callKey, { kind: "handled" });
|
|
276
346
|
return codexPass();
|
|
277
347
|
}
|
|
278
348
|
return codexPass();
|
|
@@ -336,6 +406,13 @@ var main = async () => {
|
|
|
336
406
|
case "SessionStart":
|
|
337
407
|
await reportSessionStart(input);
|
|
338
408
|
break;
|
|
409
|
+
case "SessionEnd":
|
|
410
|
+
await handleSessionEnd({
|
|
411
|
+
cwd: input.cwd,
|
|
412
|
+
session_id: input.session_id,
|
|
413
|
+
reason: input.reason
|
|
414
|
+
}, CODEX_AGENT, { reconcile: false, reportAttempts: 1, reportTimeoutMs: 1200 });
|
|
415
|
+
break;
|
|
339
416
|
default:
|
|
340
417
|
break;
|
|
341
418
|
}
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
reportEvent
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-VOE4SYPR.js";
|
|
5
5
|
import {
|
|
6
6
|
askUser,
|
|
7
7
|
waitForAnswer
|
|
8
8
|
} from "../chunk-J3YDT4HM.js";
|
|
9
|
+
import "../chunk-7J67NYK4.js";
|
|
9
10
|
import {
|
|
10
11
|
getMachineId
|
|
11
12
|
} from "../chunk-RN3NOEJF.js";
|
|
12
13
|
import "../chunk-BSZYIAZL.js";
|
|
13
14
|
import "../chunk-SAF6HGAA.js";
|
|
14
|
-
import "../chunk-
|
|
15
|
+
import "../chunk-KBS53WOE.js";
|
|
15
16
|
import "../chunk-7QLSKOSU.js";
|
|
16
17
|
import "../chunk-KZERVKTD.js";
|
|
17
18
|
import {
|
|
18
19
|
getApiKey
|
|
19
20
|
} from "../chunk-2UMNXADU.js";
|
|
20
|
-
import "../chunk-
|
|
21
|
+
import "../chunk-DMW54VWK.js";
|
|
21
22
|
|
|
22
23
|
// bin/pushary-codex.ts
|
|
23
24
|
import { basename } from "path";
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
printKeyCheck
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-TD3M232T.js";
|
|
5
5
|
import {
|
|
6
6
|
confirmAppConnection,
|
|
7
7
|
connectDevice,
|
|
8
8
|
printConnectInstructions
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-EJL3PJU7.js";
|
|
10
10
|
import "../chunk-3EGEA4KH.js";
|
|
11
11
|
import {
|
|
12
12
|
checkApiKey
|
|
13
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-KFAH6KRE.js";
|
|
14
14
|
import {
|
|
15
15
|
createIo
|
|
16
16
|
} from "../chunk-5RUIFDTP.js";
|
|
17
17
|
import {
|
|
18
18
|
readKeySource
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-YX6ZKCC5.js";
|
|
20
20
|
import {
|
|
21
21
|
parseFlags
|
|
22
22
|
} from "../chunk-GMXKITVA.js";
|
|
@@ -29,7 +29,7 @@ import "../chunk-DINDL2CF.js";
|
|
|
29
29
|
import "../chunk-6MTNS63X.js";
|
|
30
30
|
import "../chunk-BSZYIAZL.js";
|
|
31
31
|
import "../chunk-SAF6HGAA.js";
|
|
32
|
-
import "../chunk-
|
|
32
|
+
import "../chunk-KBS53WOE.js";
|
|
33
33
|
import {
|
|
34
34
|
UsageError
|
|
35
35
|
} from "../chunk-7QLSKOSU.js";
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
EXIT
|
|
38
38
|
} from "../chunk-KZERVKTD.js";
|
|
39
39
|
import "../chunk-2UMNXADU.js";
|
|
40
|
-
import "../chunk-
|
|
40
|
+
import "../chunk-DMW54VWK.js";
|
|
41
41
|
|
|
42
42
|
// bin/pushary-connect.ts
|
|
43
43
|
exitOnHelpFlag("connect");
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
acquireDaemonLease,
|
|
9
9
|
readDaemonStatus,
|
|
10
10
|
requestDaemonHandoff
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-KCAFCGGY.js";
|
|
12
12
|
import {
|
|
13
13
|
CODEX_BRIDGE_CAPABILITY,
|
|
14
14
|
CODEX_RESUME_CAPABILITY,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import {
|
|
18
18
|
findLiveProcessBySpawnId,
|
|
19
19
|
ownsLiveProcess
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-LGVHYFIB.js";
|
|
21
21
|
import {
|
|
22
22
|
spawnClaude
|
|
23
23
|
} from "../chunk-XHKBHWLX.js";
|
|
@@ -26,7 +26,8 @@ import {
|
|
|
26
26
|
resolveGlobalPackageVersion,
|
|
27
27
|
resolvePackageVersionFromBinary
|
|
28
28
|
} from "../chunk-VT4IVERX.js";
|
|
29
|
-
import "../chunk-
|
|
29
|
+
import "../chunk-VL5ZNK6V.js";
|
|
30
|
+
import "../chunk-GIRT7677.js";
|
|
30
31
|
import {
|
|
31
32
|
getMachineId
|
|
32
33
|
} from "../chunk-RN3NOEJF.js";
|
|
@@ -43,7 +44,7 @@ import {
|
|
|
43
44
|
getApiKey,
|
|
44
45
|
getBaseUrl
|
|
45
46
|
} from "../chunk-2UMNXADU.js";
|
|
46
|
-
import "../chunk-
|
|
47
|
+
import "../chunk-DMW54VWK.js";
|
|
47
48
|
|
|
48
49
|
// src/spawn-daemon.ts
|
|
49
50
|
import { execFileSync, spawn } from "child_process";
|
|
@@ -2,12 +2,22 @@
|
|
|
2
2
|
import {
|
|
3
3
|
removeClaudeMcpServers,
|
|
4
4
|
removePusharySettings
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-2JYQAW4B.js";
|
|
6
|
+
import {
|
|
7
|
+
OPENCODE_PLUGIN_SIBLING,
|
|
8
|
+
OPENCODE_PLUGIN_SIBLING_CONTENTS,
|
|
9
|
+
hasForeignPlugins,
|
|
10
|
+
isOpenCodePluginOurs,
|
|
11
|
+
openCodeConfigFile,
|
|
12
|
+
openCodePluginDir,
|
|
13
|
+
openCodePluginFile,
|
|
14
|
+
removeOpenCodeMcpServer
|
|
15
|
+
} from "../chunk-ZGC3W23N.js";
|
|
6
16
|
import {
|
|
7
17
|
removeGeminiSettings
|
|
8
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-K42RXJPG.js";
|
|
9
19
|
import "../chunk-ATBKJNWS.js";
|
|
10
|
-
import "../chunk-
|
|
20
|
+
import "../chunk-B7ZBHWOW.js";
|
|
11
21
|
import "../chunk-VT4IVERX.js";
|
|
12
22
|
import {
|
|
13
23
|
claudeJson,
|
|
@@ -21,21 +31,23 @@ import {
|
|
|
21
31
|
geminiSettings,
|
|
22
32
|
removeCodexHooks,
|
|
23
33
|
removeCodexSettings
|
|
24
|
-
} from "../chunk-
|
|
34
|
+
} from "../chunk-VL5ZNK6V.js";
|
|
35
|
+
import "../chunk-GIRT7677.js";
|
|
25
36
|
import {
|
|
26
37
|
exitOnHelpFlag
|
|
27
38
|
} from "../chunk-SQOFGPNX.js";
|
|
28
39
|
import "../chunk-GOEXZ5QJ.js";
|
|
29
40
|
import "../chunk-DINDL2CF.js";
|
|
30
41
|
import "../chunk-KZERVKTD.js";
|
|
31
|
-
import "../chunk-
|
|
42
|
+
import "../chunk-DMW54VWK.js";
|
|
32
43
|
|
|
33
44
|
// bin/pushary-disconnect.ts
|
|
34
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
45
|
+
import { readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
35
46
|
import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
|
|
36
47
|
|
|
37
48
|
// src/disconnect.ts
|
|
38
|
-
|
|
49
|
+
import { dirname } from "path";
|
|
50
|
+
var DISCONNECTABLE_AGENTS = ["claude", "codex", "gemini", "cursor", "opencode"];
|
|
39
51
|
var isDisconnectableAgent = (value) => DISCONNECTABLE_AGENTS.includes(value);
|
|
40
52
|
var removeCursorHooks = (config) => {
|
|
41
53
|
const hooks = config.hooks;
|
|
@@ -85,8 +97,24 @@ var disconnectPlan = (agent) => {
|
|
|
85
97
|
{ path: cursorUserHooks(), format: "json", remove: removeCursorHooks },
|
|
86
98
|
{ path: cursorUserMcp(), format: "json", remove: removeClaudeMcpServers }
|
|
87
99
|
];
|
|
100
|
+
// The plugin itself is a whole file of ours and comes out in
|
|
101
|
+
// `disconnectRemovals` below; the MCP entry is an ordinary edit to a config
|
|
102
|
+
// that is otherwise the person's own.
|
|
103
|
+
case "opencode":
|
|
104
|
+
return [{ path: openCodeConfigFile(), format: "json", remove: removeOpenCodeMcpServer }];
|
|
88
105
|
}
|
|
89
106
|
};
|
|
107
|
+
var disconnectRemovals = (agent) => {
|
|
108
|
+
if (agent !== "opencode") return [];
|
|
109
|
+
return [
|
|
110
|
+
{ path: openCodePluginFile(), ownedBy: isOpenCodePluginOurs },
|
|
111
|
+
{
|
|
112
|
+
path: `${openCodePluginDir()}/${OPENCODE_PLUGIN_SIBLING}`,
|
|
113
|
+
ownedBy: (contents) => contents === OPENCODE_PLUGIN_SIBLING_CONTENTS,
|
|
114
|
+
onlyIfLast: true
|
|
115
|
+
}
|
|
116
|
+
];
|
|
117
|
+
};
|
|
90
118
|
var disconnectAgent = (agent, io) => {
|
|
91
119
|
const changed = [];
|
|
92
120
|
const untouched = [];
|
|
@@ -109,6 +137,27 @@ var disconnectAgent = (agent, io) => {
|
|
|
109
137
|
untouched.push(edit.path);
|
|
110
138
|
}
|
|
111
139
|
}
|
|
140
|
+
for (const removal of disconnectRemovals(agent)) {
|
|
141
|
+
const raw = io.read(removal.path);
|
|
142
|
+
if (raw === null) continue;
|
|
143
|
+
if (!removal.ownedBy(raw)) {
|
|
144
|
+
untouched.push(removal.path);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (removal.onlyIfLast) {
|
|
148
|
+
const entries = io.list?.(dirname(removal.path));
|
|
149
|
+
if (entries === void 0 || hasForeignPlugins(entries)) {
|
|
150
|
+
untouched.push(removal.path);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
io.remove?.(removal.path);
|
|
156
|
+
changed.push(removal.path);
|
|
157
|
+
} catch {
|
|
158
|
+
untouched.push(removal.path);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
112
161
|
return { agent, changed, untouched };
|
|
113
162
|
};
|
|
114
163
|
|
|
@@ -130,7 +179,15 @@ var result = disconnectAgent(requested, {
|
|
|
130
179
|
},
|
|
131
180
|
write: (path, contents) => writeFileSync(path, contents, "utf-8"),
|
|
132
181
|
parseToml: (raw) => parseTOML(raw),
|
|
133
|
-
stringifyToml: (config) => stringifyTOML(config)
|
|
182
|
+
stringifyToml: (config) => stringifyTOML(config),
|
|
183
|
+
remove: (path) => rmSync(path, { force: true }),
|
|
184
|
+
list: (dir) => {
|
|
185
|
+
try {
|
|
186
|
+
return readdirSync(dir);
|
|
187
|
+
} catch {
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
134
191
|
});
|
|
135
192
|
if (result.changed.length === 0) {
|
|
136
193
|
console.log(`${requested} was not connected.`);
|