@pushary/agent-hooks 0.68.0 → 0.69.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 +89 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +36 -10
- package/data/vscode-plugin/scripts/pushary-gate.mjs +32 -9
- package/dist/bin/pushary-claude.js +21 -11
- package/dist/bin/pushary-clean.js +58 -20
- package/dist/bin/pushary-codex-hook.js +38 -23
- package/dist/bin/pushary-codex.js +1 -1
- package/dist/bin/pushary-connect.js +1 -1
- package/dist/bin/pushary-doctor.js +139 -26
- package/dist/bin/pushary-gemini-hook.js +32 -16
- package/dist/bin/pushary-hook.js +3 -3
- 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 +16 -6
- package/dist/bin/pushary-status.js +1 -1
- package/dist/bin/pushary-stop-hook.js +1 -1
- package/dist/bin/pushary-stopfailure-hook.js +1 -1
- package/dist/chunk-3USMXNVB.js +64 -0
- package/dist/{chunk-SONXGCZY.js → chunk-C5I2RAMT.js} +45 -27
- package/dist/{chunk-XOUYM27W.js → chunk-H46LSQRT.js} +182 -103
- package/dist/{chunk-KB4ODLGB.js → chunk-I4BYZIX4.js} +2 -0
- package/dist/{chunk-YMFZUXL5.js → chunk-P7VBAYQO.js} +25 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-YHG74UFF.js +0 -12
|
@@ -3,11 +3,14 @@ import {
|
|
|
3
3
|
describeWaitLadder
|
|
4
4
|
} from "../chunk-IIENZE2J.js";
|
|
5
5
|
import {
|
|
6
|
+
agentProbes,
|
|
7
|
+
detectAgent,
|
|
6
8
|
detectAllAgents,
|
|
7
9
|
hasInstructionBlock,
|
|
10
|
+
isDetected,
|
|
8
11
|
isPluginRegistered,
|
|
9
12
|
vscodeSettingsTargets
|
|
10
|
-
} from "../chunk-
|
|
13
|
+
} from "../chunk-I4BYZIX4.js";
|
|
11
14
|
import {
|
|
12
15
|
GEMINI_HOOK_BINARY,
|
|
13
16
|
hasGeminiHooks,
|
|
@@ -27,6 +30,7 @@ import {
|
|
|
27
30
|
geminiSettings,
|
|
28
31
|
hasCodexHooks,
|
|
29
32
|
missingCodexHookEvents,
|
|
33
|
+
pusharyConfigFile,
|
|
30
34
|
readCodexMcpAuth,
|
|
31
35
|
untrustedCodexHookEvents,
|
|
32
36
|
vscodePluginDir
|
|
@@ -35,7 +39,7 @@ import {
|
|
|
35
39
|
describeReach,
|
|
36
40
|
fetchChannels,
|
|
37
41
|
reachVerdict
|
|
38
|
-
} from "../chunk-
|
|
42
|
+
} from "../chunk-P7VBAYQO.js";
|
|
39
43
|
import "../chunk-3EGEA4KH.js";
|
|
40
44
|
import {
|
|
41
45
|
keyPrefix,
|
|
@@ -135,6 +139,42 @@ var redactText = (text) => text.replace(KEY_PATTERN, (match) => `${match.split("
|
|
|
135
139
|
var redact = (bundle) => JSON.parse(redactText(JSON.stringify(bundle)));
|
|
136
140
|
var bundleFileName = (bundle) => `pushary-diagnostics-${bundle.generatedAt.replace(/[:.]/g, "-")}.json`;
|
|
137
141
|
|
|
142
|
+
// src/diagnostics/exit-code.ts
|
|
143
|
+
var resolveDoctorExit = ({ keyExit, failures }) => {
|
|
144
|
+
if (keyExit !== null) return keyExit;
|
|
145
|
+
if (failures.length === 0) return EXIT.OK;
|
|
146
|
+
const every = (kind) => failures.every((f) => f.kind === kind);
|
|
147
|
+
if (every("unreachable")) return EXIT.UNREACHABLE;
|
|
148
|
+
if (every("reachability")) return EXIT.NO_DEVICE;
|
|
149
|
+
return EXIT.PROBLEMS_FOUND;
|
|
150
|
+
};
|
|
151
|
+
|
|
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
|
+
|
|
138
178
|
// bin/pushary-doctor.ts
|
|
139
179
|
exitOnHelpFlag("doctor");
|
|
140
180
|
var parseDoctorOptions = (argv) => {
|
|
@@ -215,12 +255,21 @@ var extractHookTimeout = (entries, needle) => {
|
|
|
215
255
|
return null;
|
|
216
256
|
};
|
|
217
257
|
var results = [];
|
|
218
|
-
var check = (passed, label, detail) => {
|
|
219
|
-
results.push({ passed, label, detail });
|
|
258
|
+
var check = (passed, label, detail, kind = "config") => {
|
|
259
|
+
results.push({ passed, label, detail, kind });
|
|
220
260
|
const icon = passed ? pass : fail;
|
|
221
261
|
const suffix = detail ? ` ${dim(`(${detail})`)}` : "";
|
|
222
262
|
console.log(` ${icon} ${label}${suffix}`);
|
|
223
263
|
};
|
|
264
|
+
var probes = agentProbes();
|
|
265
|
+
var installed = (agent) => isDetected(detectAgent(probes[agent]));
|
|
266
|
+
var agentsChecked = [];
|
|
267
|
+
var skipped = (label, why) => {
|
|
268
|
+
console.log(` ${dim("\u2013")} ${label} ${dim(`(${why})`)}`);
|
|
269
|
+
};
|
|
270
|
+
var checking = (label) => {
|
|
271
|
+
agentsChecked.push(label);
|
|
272
|
+
};
|
|
224
273
|
var main = async () => {
|
|
225
274
|
console.log();
|
|
226
275
|
console.log(` ${bold("Pushary Doctor")}`);
|
|
@@ -247,8 +296,12 @@ var main = async () => {
|
|
|
247
296
|
if (keySource === "config-file") {
|
|
248
297
|
console.log(` ${pass} API key in ${dim(source.origin ?? "the config file")} ${dim(`(${maskKey(apiKey)})`)}`);
|
|
249
298
|
} else if (keySource === "profile") {
|
|
250
|
-
|
|
251
|
-
|
|
299
|
+
check(
|
|
300
|
+
false,
|
|
301
|
+
"API key readable by hooks",
|
|
302
|
+
`found only in a shell profile (${maskKey(apiKey)}), which hooks do not read \u2014 re-run setup, or open a new terminal`
|
|
303
|
+
);
|
|
304
|
+
console.log(` ${dim("Hooks read $PUSHARY_API_KEY, then")} ${dim(pusharyConfigFile())}`);
|
|
252
305
|
} else {
|
|
253
306
|
check(!!apiKey, "API key in environment", apiKey ? maskKey(apiKey) : "PUSHARY_API_KEY not set");
|
|
254
307
|
}
|
|
@@ -282,19 +335,34 @@ var main = async () => {
|
|
|
282
335
|
check(false, "Key format", "expected pk_xxx.xxx");
|
|
283
336
|
break;
|
|
284
337
|
case "unreachable":
|
|
285
|
-
|
|
338
|
+
check(false, "Key verified with pushary.com", `could not reach pushary.com (${keyVerdict.detail})`, "unreachable");
|
|
286
339
|
break;
|
|
287
340
|
}
|
|
288
341
|
}
|
|
289
342
|
const CLAUDE_JSON = claudeJson();
|
|
290
|
-
const
|
|
343
|
+
const claudeInPlay = claudeWired({
|
|
344
|
+
claudeJson: readJson(CLAUDE_JSON),
|
|
345
|
+
settings: readJson(CLAUDE_SETTINGS),
|
|
346
|
+
skillExists: existsSync(SKILL_PATH)
|
|
347
|
+
});
|
|
348
|
+
const claudeJson2 = claudeInPlay ? readJson(CLAUDE_JSON) : null;
|
|
291
349
|
const mcpServers = claudeJson2?.mcpServers ?? {};
|
|
292
350
|
const pusharyServer = mcpServers?.pushary;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
351
|
+
if (!claudeInPlay) {
|
|
352
|
+
skipped("Claude Code", installed("claude_code") ? "installed, but Pushary is not set up for it" : "not installed");
|
|
353
|
+
} else {
|
|
354
|
+
checking("Claude Code");
|
|
355
|
+
check(!!pusharyServer, "Claude Code: MCP server configured");
|
|
356
|
+
if (pusharyServer) {
|
|
357
|
+
check(pusharyServer.type === "http", "Claude Code: MCP server type", pusharyServer.type ? String(pusharyServer.type) : 'missing \u2014 add type: "http"');
|
|
358
|
+
const claudeAuth = pusharyServer.headers?.Authorization;
|
|
359
|
+
if (typeof claudeAuth === "string" && apiKey) {
|
|
360
|
+
const matches = claudeAuth === `Bearer ${apiKey}`;
|
|
361
|
+
check(matches, "Claude Code: MCP key matches active key", matches ? void 0 : "the key in ~/.claude.json is not the key hooks use (rotated?) \u2014 re-run setup");
|
|
362
|
+
}
|
|
363
|
+
}
|
|
296
364
|
}
|
|
297
|
-
const settings = readJson(CLAUDE_SETTINGS);
|
|
365
|
+
const settings = claudeInPlay ? readJson(CLAUDE_SETTINGS) : null;
|
|
298
366
|
if (settings) {
|
|
299
367
|
const hooks = settings.hooks;
|
|
300
368
|
const CLAUDE_HOOK_CHECKS = [
|
|
@@ -325,19 +393,24 @@ var main = async () => {
|
|
|
325
393
|
if (hasLegacyPerms) {
|
|
326
394
|
console.log(` ${warn} Legacy individual permissions detected ${dim("(run pushary clean, then setup again)")}`);
|
|
327
395
|
}
|
|
328
|
-
} else {
|
|
396
|
+
} else if (claudeInPlay) {
|
|
329
397
|
check(false, "Claude Code: settings.json", "not found");
|
|
330
398
|
}
|
|
331
399
|
const codexConfigPath = codexConfigToml();
|
|
332
400
|
const codexHooksPath = codexHooksJson();
|
|
333
401
|
const codexHooksConfig = readJson(codexHooksPath);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
402
|
+
const codexConfig = existsSync(codexConfigPath) ? readFileSync(codexConfigPath, "utf-8") : "";
|
|
403
|
+
let codexParsed = {};
|
|
404
|
+
try {
|
|
405
|
+
codexParsed = parseTOML(codexConfig);
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
if (codexWired({
|
|
409
|
+
config: codexParsed,
|
|
410
|
+
hooks: codexHooksConfig,
|
|
411
|
+
skillExists: existsSync(join(codexSkillDir(), "SKILL.md"))
|
|
412
|
+
})) {
|
|
413
|
+
checking("Codex");
|
|
341
414
|
const codexMcp = codexParsed.mcp_servers?.pushary;
|
|
342
415
|
const codexTransport = !!codexMcp && (typeof codexMcp.url === "string" || typeof codexMcp.command === "string");
|
|
343
416
|
const codexAuth = readCodexMcpAuth(codexParsed);
|
|
@@ -401,10 +474,13 @@ var main = async () => {
|
|
|
401
474
|
const codexAgentsMd2 = codexAgentsMd();
|
|
402
475
|
const hasCodexInstructions = hasInstructionBlock(codexAgentsMd2);
|
|
403
476
|
check(hasCodexInstructions, "Codex: proactive instructions (AGENTS.md)", hasCodexInstructions ? codexAgentsMd2 : "missing \u2014 re-run setup so Codex asks via push, not the terminal");
|
|
477
|
+
} else {
|
|
478
|
+
skipped("Codex", installed("codex") ? "installed, but Pushary is not set up for it" : "not installed");
|
|
404
479
|
}
|
|
405
480
|
const geminiSettingsPath = geminiSettings();
|
|
406
481
|
const geminiSettings2 = readJson(geminiSettingsPath);
|
|
407
|
-
if (geminiSettings2) {
|
|
482
|
+
if (geminiSettings2 && geminiWired({ settings: geminiSettings2 })) {
|
|
483
|
+
checking("Gemini CLI");
|
|
408
484
|
const geminiServers = geminiSettings2.mcpServers ?? {};
|
|
409
485
|
const geminiPushary = geminiServers.pushary;
|
|
410
486
|
check(!!geminiPushary, "Gemini CLI: MCP server configured");
|
|
@@ -443,12 +519,15 @@ var main = async () => {
|
|
|
443
519
|
const geminiMd2 = geminiMd();
|
|
444
520
|
const hasGeminiInstructions = hasInstructionBlock(geminiMd2);
|
|
445
521
|
check(hasGeminiInstructions, "Gemini CLI: proactive instructions (GEMINI.md)", hasGeminiInstructions ? geminiMd2 : "missing \u2014 re-run setup so Gemini asks via push, not the terminal");
|
|
522
|
+
} else {
|
|
523
|
+
skipped("Gemini CLI", installed("gemini_cli") ? "installed, but Pushary is not set up for it" : "not installed");
|
|
446
524
|
}
|
|
447
525
|
const cursorPluginDir2 = cursorPluginDir();
|
|
448
526
|
if (existsSync(cursorPluginDir2)) {
|
|
527
|
+
checking("Cursor");
|
|
449
528
|
const cursorUserHooks2 = cursorUserHooks();
|
|
450
529
|
const userHooks = readJson(cursorUserHooks2);
|
|
451
|
-
const gateEntry = userHooks
|
|
530
|
+
const gateEntry = userHooks?.hooks?.beforeShellExecution?.find(
|
|
452
531
|
(h) => String(h.command ?? "").includes("pushary-gate")
|
|
453
532
|
);
|
|
454
533
|
if (!gateEntry) {
|
|
@@ -465,12 +544,17 @@ var main = async () => {
|
|
|
465
544
|
check(false, "Cursor: API key linked", "no Authorization in plugin mcp.json \u2014 re-run setup");
|
|
466
545
|
} else if (cursorAuth.includes("PUSHARY_API_KEY")) {
|
|
467
546
|
check(false, "Cursor: API key linked", "plugin relies on $PUSHARY_API_KEY; re-run setup to embed the key so the gate works when Cursor is launched from the GUI");
|
|
547
|
+
} else if (apiKey && cursorAuth !== `Bearer ${apiKey}`) {
|
|
548
|
+
check(false, "Cursor: API key matches active key", "the embedded key is not the key hooks use (rotated?) \u2014 re-run setup");
|
|
468
549
|
} else {
|
|
469
550
|
check(true, "Cursor: API key linked", "embedded in plugin mcp.json");
|
|
470
551
|
}
|
|
552
|
+
} else {
|
|
553
|
+
skipped("Cursor", installed("cursor") ? "installed, but Pushary is not set up for it" : "not installed");
|
|
471
554
|
}
|
|
472
555
|
const vscodePluginDir2 = vscodePluginDir();
|
|
473
556
|
if (existsSync(vscodePluginDir2)) {
|
|
557
|
+
checking("VS Code");
|
|
474
558
|
const registeredIn = vscodeSettingsTargets(existsSync).filter(
|
|
475
559
|
(settingsPath) => existsSync(settingsPath) && isPluginRegistered(readFileSync(settingsPath, "utf-8"), vscodePluginDir2)
|
|
476
560
|
);
|
|
@@ -499,11 +583,22 @@ var main = async () => {
|
|
|
499
583
|
check(false, "VS Code: API key linked", "no Authorization in plugin .mcp.json \u2014 re-run setup");
|
|
500
584
|
} else if (vscodeAuth.includes("PUSHARY_API_KEY")) {
|
|
501
585
|
check(false, "VS Code: API key linked", "plugin relies on $PUSHARY_API_KEY; re-run setup to embed the key so the gate works when VS Code is launched from the GUI");
|
|
586
|
+
} else if (apiKey && vscodeAuth !== `Bearer ${apiKey}`) {
|
|
587
|
+
check(false, "VS Code: API key matches active key", "the embedded key is not the key hooks use (rotated?) \u2014 re-run setup");
|
|
502
588
|
} else {
|
|
503
589
|
check(true, "VS Code: API key linked", "embedded in plugin .mcp.json");
|
|
504
590
|
}
|
|
591
|
+
} else {
|
|
592
|
+
skipped("VS Code", installed("vscode") ? "installed, but Pushary is not set up for it" : "not installed");
|
|
593
|
+
}
|
|
594
|
+
check(
|
|
595
|
+
agentsChecked.length > 0,
|
|
596
|
+
"Wired to at least one agent",
|
|
597
|
+
agentsChecked.length > 0 ? agentsChecked.join(", ") : "Pushary is installed but configured for no agent \u2014 run npx @pushary/agent-hooks@latest setup"
|
|
598
|
+
);
|
|
599
|
+
if (claudeInPlay) {
|
|
600
|
+
check(existsSync(SKILL_PATH), "Skill installed", existsSync(SKILL_PATH) ? SKILL_PATH : "not found");
|
|
505
601
|
}
|
|
506
|
-
check(existsSync(SKILL_PATH), "Skill installed", existsSync(SKILL_PATH) ? SKILL_PATH : "not found");
|
|
507
602
|
let globalVersion2 = "";
|
|
508
603
|
try {
|
|
509
604
|
globalVersion2 = execNpm("list -g --no-workspaces @pushary/agent-hooks --depth=0", { timeout: 1e4 }).toString().match(/@pushary\/agent-hooks@([\d.]+)/)?.[1] ?? "";
|
|
@@ -613,11 +708,21 @@ var main = async () => {
|
|
|
613
708
|
console.log(` ${dim("Notification Delivery")}`);
|
|
614
709
|
const probe = await fetchChannels(apiKey);
|
|
615
710
|
if (probe.kind !== "ok") {
|
|
616
|
-
check(
|
|
711
|
+
check(
|
|
712
|
+
false,
|
|
713
|
+
"Phone reachable",
|
|
714
|
+
probe.kind === "unreachable" ? `could not read your devices (${probe.detail})` : probe.kind === "locked" ? "no plan active, so nothing can be delivered" : "the server rejected this key",
|
|
715
|
+
probe.kind === "unreachable" ? "unreachable" : "config"
|
|
716
|
+
);
|
|
617
717
|
} else if (probe.status.precise) {
|
|
618
718
|
const channels = probe.status;
|
|
619
719
|
const verdict = reachVerdict(channels);
|
|
620
|
-
check(
|
|
720
|
+
check(
|
|
721
|
+
verdict.kind === "yours",
|
|
722
|
+
"Phone reachable",
|
|
723
|
+
verdict.kind === "yours" ? describeReach(channels) : verdict.kind === "nobody" ? "nothing connected, run pushary connect or get the app at https://pushary.com/download" : `${describeReach(channels)}; run pushary connect to add your own`,
|
|
724
|
+
"reachability"
|
|
725
|
+
);
|
|
621
726
|
}
|
|
622
727
|
const pushSuppressed = !options.roundtrip && (options.noPush || options.json || !process.stdout.isTTY);
|
|
623
728
|
if (pushSuppressed) {
|
|
@@ -662,6 +767,14 @@ var main = async () => {
|
|
|
662
767
|
});
|
|
663
768
|
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
664
769
|
check(waitResult.answered === true, "Answer received", waitResult.answered ? `"${waitResult.value}" (${elapsed}s roundtrip)` : "no response within timeout");
|
|
770
|
+
if (waitResult.answered !== true) {
|
|
771
|
+
await callMcpTool(apiKey, "cancel_question", { correlationId: askResult.correlationId }, {
|
|
772
|
+
id: 5,
|
|
773
|
+
sessionId,
|
|
774
|
+
timeoutMs: 1e4
|
|
775
|
+
}).catch(() => {
|
|
776
|
+
});
|
|
777
|
+
}
|
|
665
778
|
} else {
|
|
666
779
|
check(false, "Question sent", "failed to get correlationId");
|
|
667
780
|
}
|
|
@@ -710,7 +823,7 @@ var main = async () => {
|
|
|
710
823
|
}
|
|
711
824
|
console.log();
|
|
712
825
|
const keyExit = keyVerdict && (keyVerdict.kind === "rejected" || keyVerdict.kind === "locked" || keyVerdict.kind === "malformed") ? describeKeyCheck(keyVerdict).exitCode : null;
|
|
713
|
-
const exitCode = keyExit
|
|
826
|
+
const exitCode = resolveDoctorExit({ keyExit, failures: failed });
|
|
714
827
|
if (options.bundle) {
|
|
715
828
|
try {
|
|
716
829
|
const bundle = redact(
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
KILL_REASON,
|
|
3
4
|
denyReasonFrom,
|
|
4
|
-
isDeferAnswer
|
|
5
|
-
|
|
5
|
+
isDeferAnswer,
|
|
6
|
+
resolveGate
|
|
7
|
+
} from "../chunk-3USMXNVB.js";
|
|
6
8
|
import {
|
|
7
9
|
DEFAULT_SESSION,
|
|
8
10
|
askUser,
|
|
@@ -18,12 +20,13 @@ import {
|
|
|
18
20
|
handleUserPrompt,
|
|
19
21
|
readLastPrompt,
|
|
20
22
|
readLastUserPrompt,
|
|
23
|
+
repoKeyFor,
|
|
21
24
|
reportEvent,
|
|
22
|
-
resolvePolicy,
|
|
23
25
|
savePendingQuestion,
|
|
26
|
+
scopePathFor,
|
|
24
27
|
sendNotification,
|
|
25
28
|
waitForAnswer
|
|
26
|
-
} from "../chunk-
|
|
29
|
+
} from "../chunk-H46LSQRT.js";
|
|
27
30
|
import {
|
|
28
31
|
isGatingMoment,
|
|
29
32
|
recordKeylessMoment
|
|
@@ -49,6 +52,7 @@ import "../chunk-KZERVKTD.js";
|
|
|
49
52
|
import { basename } from "path";
|
|
50
53
|
|
|
51
54
|
// src/gemini-adapter.ts
|
|
55
|
+
import { isAbsolute, resolve } from "path";
|
|
52
56
|
var GEMINI_AGENT = { type: "gemini_cli", label: "Gemini CLI" };
|
|
53
57
|
var GEMINI_TOOL_MAP = {
|
|
54
58
|
run_shell_command: "Bash",
|
|
@@ -57,7 +61,7 @@ var GEMINI_TOOL_MAP = {
|
|
|
57
61
|
read_file: "Read"
|
|
58
62
|
};
|
|
59
63
|
var firstString = (...values) => values.find((v) => typeof v === "string" && v.length > 0);
|
|
60
|
-
var toGeminiPolicyLookup = (toolName, toolInput) => {
|
|
64
|
+
var toGeminiPolicyLookup = (toolName, toolInput, cwd) => {
|
|
61
65
|
const canonical = GEMINI_TOOL_MAP[toolName];
|
|
62
66
|
if (!canonical) return { tool: toolName, input: toolInput };
|
|
63
67
|
if (canonical === "Bash") {
|
|
@@ -65,7 +69,11 @@ var toGeminiPolicyLookup = (toolName, toolInput) => {
|
|
|
65
69
|
return { tool: "Bash", input: command ? { command } : {} };
|
|
66
70
|
}
|
|
67
71
|
const filePath = firstString(toolInput.file_path, toolInput.absolute_path, toolInput.path);
|
|
68
|
-
return { tool: canonical, input:
|
|
72
|
+
if (!filePath) return { tool: canonical, input: {} };
|
|
73
|
+
return {
|
|
74
|
+
tool: canonical,
|
|
75
|
+
input: { file_path: cwd && !isAbsolute(filePath) ? resolve(cwd, filePath) : filePath }
|
|
76
|
+
};
|
|
69
77
|
};
|
|
70
78
|
var geminiAllow = () => ({ kind: "allow" });
|
|
71
79
|
var geminiDeny = (reason) => ({ kind: "deny", reason });
|
|
@@ -82,9 +90,8 @@ var geminiTimeoutDecision = (timeoutAction, denyReason = "No response within tim
|
|
|
82
90
|
};
|
|
83
91
|
|
|
84
92
|
// bin/pushary-gemini-hook.ts
|
|
85
|
-
var KILL_REASON = "Stopped by user: this agent was halted from Pushary";
|
|
86
93
|
var START_MS = Date.now();
|
|
87
|
-
var sleep = (ms) => new Promise((
|
|
94
|
+
var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
88
95
|
var pollForAnswer = async (apiKey, correlationId, deadlineMs, pollInterval = 2e3) => {
|
|
89
96
|
while (Date.now() < deadlineMs) {
|
|
90
97
|
const remaining = Math.min(Math.max(deadlineMs - Date.now(), 1e3), 3e4);
|
|
@@ -210,14 +217,23 @@ var decideBeforeTool = async (input) => {
|
|
|
210
217
|
}
|
|
211
218
|
try {
|
|
212
219
|
const modeState = await fetchModeState(apiKey, input.session_id);
|
|
213
|
-
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
220
|
+
const lookup = toGeminiPolicyLookup(input.tool_name ?? "", input.tool_input ?? {}, input.cwd);
|
|
221
|
+
const config = await getPolicy(apiKey, modeState.policyVersion, "gemini_cli");
|
|
222
|
+
const scopePath = modeState.scope ? scopePathFor(lookup.tool, lookup.input, input.cwd) : void 0;
|
|
223
|
+
const verdict = resolveGate({
|
|
224
|
+
modeState,
|
|
225
|
+
config,
|
|
226
|
+
toolName: lookup.tool,
|
|
227
|
+
toolInputs: [lookup.input],
|
|
228
|
+
scopePaths: scopePath ? [scopePath] : [],
|
|
229
|
+
cwd: input.cwd,
|
|
230
|
+
repoKey: repoKeyFor(input.cwd)
|
|
231
|
+
});
|
|
232
|
+
if (verdict.kind === "kill") return geminiDeny(KILL_REASON);
|
|
233
|
+
if (verdict.kind === "allow") return geminiAllow();
|
|
234
|
+
if (verdict.kind === "deny") return geminiDeny(verdict.reason);
|
|
235
|
+
if (verdict.kind === "defer") return geminiPass();
|
|
236
|
+
const toolPolicy = verdict.policy;
|
|
221
237
|
switch (toolPolicy.mode) {
|
|
222
238
|
case "push_only":
|
|
223
239
|
return handlePushOnly(apiKey, input, lookup, toolPolicy.timeoutSeconds, toolPolicy.timeoutAction);
|
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-C5I2RAMT.js";
|
|
5
5
|
import "../chunk-CAJZAFVS.js";
|
|
6
6
|
import {
|
|
7
7
|
exitOnHelpFlag
|
|
8
8
|
} from "../chunk-ATP6UQPH.js";
|
|
9
9
|
import "../chunk-JGH37QRB.js";
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
10
|
+
import "../chunk-3USMXNVB.js";
|
|
11
|
+
import "../chunk-H46LSQRT.js";
|
|
12
12
|
import "../chunk-WLGEY4UX.js";
|
|
13
13
|
import "../chunk-BSZYIAZL.js";
|
|
14
14
|
import "../chunk-SAF6HGAA.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePermissionDenied
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-C5I2RAMT.js";
|
|
5
5
|
import "../chunk-CAJZAFVS.js";
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-3USMXNVB.js";
|
|
7
|
+
import "../chunk-H46LSQRT.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-C5I2RAMT.js";
|
|
5
5
|
import "../chunk-CAJZAFVS.js";
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-3USMXNVB.js";
|
|
7
|
+
import "../chunk-H46LSQRT.js";
|
|
8
8
|
import "../chunk-WLGEY4UX.js";
|
|
9
9
|
import "../chunk-BSZYIAZL.js";
|
|
10
10
|
import "../chunk-SAF6HGAA.js";
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
shortenHome,
|
|
19
19
|
vscodeSettingsTargets,
|
|
20
20
|
writeInstructionBlock
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-I4BYZIX4.js";
|
|
22
22
|
import {
|
|
23
23
|
GEMINI_HOOK_BINARY,
|
|
24
24
|
addGeminiHooks,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
connectViaAppPairing,
|
|
49
49
|
printConnectInstructions,
|
|
50
50
|
setHumanStream
|
|
51
|
-
} from "../chunk-
|
|
51
|
+
} from "../chunk-P7VBAYQO.js";
|
|
52
52
|
import "../chunk-3EGEA4KH.js";
|
|
53
53
|
import {
|
|
54
54
|
KEY_FILE_MODE,
|
|
@@ -89,7 +89,7 @@ import {
|
|
|
89
89
|
} from "../chunk-JGH37QRB.js";
|
|
90
90
|
import {
|
|
91
91
|
reportEvent
|
|
92
|
-
} from "../chunk-
|
|
92
|
+
} from "../chunk-H46LSQRT.js";
|
|
93
93
|
import "../chunk-BSZYIAZL.js";
|
|
94
94
|
import "../chunk-SAF6HGAA.js";
|
|
95
95
|
import {
|
|
@@ -1239,7 +1239,9 @@ var reportDryRun = (apiKey, agents, keyCheck) => {
|
|
|
1239
1239
|
console.log();
|
|
1240
1240
|
console.log(` ${bold("Dry run")} ${dim("\u2014 nothing below has been written")}`);
|
|
1241
1241
|
console.log();
|
|
1242
|
-
console.log(
|
|
1242
|
+
console.log(
|
|
1243
|
+
apiKey ? ` ${dim("Key:")} ${dim(`${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`)} ${dim(`(${keyCheck?.kind ?? "not verified"})`)}` : ` ${dim("Key:")} ${dim("none on this machine \u2014 a real run would acquire one")}`
|
|
1244
|
+
);
|
|
1243
1245
|
console.log(` ${dim("Key store:")} ${PUSHARY_CONFIG_FILE}`);
|
|
1244
1246
|
const rc = resolveShellRc();
|
|
1245
1247
|
console.log(` ${dim("Shell rc:")} ${rc ? rc.path : dim("none for this shell; the export line would be printed instead")}`);
|
|
@@ -1323,7 +1325,7 @@ var main = async () => {
|
|
|
1323
1325
|
const masked = `${envKey.slice(0, 8)}...${envKey.slice(-4)}`;
|
|
1324
1326
|
console.log(` ${check} Found API key in environment: ${dim(masked)}`);
|
|
1325
1327
|
console.log();
|
|
1326
|
-
const useExisting = options.nonInteractive ? true : await confirm({ message: "Use this key?", default: true });
|
|
1328
|
+
const useExisting = options.nonInteractive || options.dryRun ? true : await confirm({ message: "Use this key?", default: true });
|
|
1327
1329
|
if (useExisting) {
|
|
1328
1330
|
trimmedKey = envKey;
|
|
1329
1331
|
storedKeyCheck = envCheck;
|
|
@@ -1331,6 +1333,14 @@ var main = async () => {
|
|
|
1331
1333
|
const apiKey = await input({ message: "API key:" });
|
|
1332
1334
|
trimmedKey = apiKey.trim();
|
|
1333
1335
|
}
|
|
1336
|
+
} else if (options.dryRun) {
|
|
1337
|
+
console.log(` ${dim("No key on this machine. A real run would connect one first.")}`);
|
|
1338
|
+
console.log();
|
|
1339
|
+
markStep("resolve-agents");
|
|
1340
|
+
const plannedAgents = await resolveAgents(options);
|
|
1341
|
+
reportDryRun(null, plannedAgents, null);
|
|
1342
|
+
emitJson(options, { ok: true, dryRun: true, agents: plannedAgents, keyCheck: "skipped", key: "none" });
|
|
1343
|
+
process.exit(EXIT.OK);
|
|
1334
1344
|
} else if (connectMode === "app" && canRunBrowserLogin() && !options.skipPhone) {
|
|
1335
1345
|
console.log(` ${dim("Pairing with your Pushary app.")}`);
|
|
1336
1346
|
console.log(` ${dim("No app? Ctrl-C, then:")} ${cyan("npx @pushary/agent-hooks setup --connect browser")}`);
|
|
@@ -1405,7 +1415,7 @@ var main = async () => {
|
|
|
1405
1415
|
`);
|
|
1406
1416
|
process.exit(await abortWith("invalid-key", EXIT.USAGE));
|
|
1407
1417
|
}
|
|
1408
|
-
recordSetupKey(trimmedKey);
|
|
1418
|
+
if (!options.dryRun) recordSetupKey(trimmedKey);
|
|
1409
1419
|
markStep("verify-key");
|
|
1410
1420
|
const keyCheck = keyFromPairing ? null : await verifyKeyOrExit(trimmedKey, options, storedKeyCheck ?? void 0);
|
|
1411
1421
|
markStep("resolve-agents");
|