@pushary/agent-hooks 0.69.0 → 0.71.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 +67 -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 +175 -34
- 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
|
@@ -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 {
|
|
@@ -105,7 +110,7 @@ import {
|
|
|
105
110
|
} from "../chunk-KZERVKTD.js";
|
|
106
111
|
|
|
107
112
|
// bin/pushary-setup.ts
|
|
108
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, rmSync, renameSync } from "fs";
|
|
113
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, cpSync, rmSync, renameSync, realpathSync } from "fs";
|
|
109
114
|
import { join, dirname, basename } from "path";
|
|
110
115
|
import { homedir, tmpdir } from "os";
|
|
111
116
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -193,6 +198,53 @@ var describeClosing = (facts) => {
|
|
|
193
198
|
};
|
|
194
199
|
};
|
|
195
200
|
|
|
201
|
+
// src/setup/codex-version.ts
|
|
202
|
+
var parseCodexVersion = (raw) => {
|
|
203
|
+
const match = raw.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
204
|
+
if (!match) return null;
|
|
205
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
206
|
+
};
|
|
207
|
+
var compareCodexVersion = (a, b) => {
|
|
208
|
+
for (let i = 0; i < 3; i++) {
|
|
209
|
+
if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1;
|
|
210
|
+
}
|
|
211
|
+
return 0;
|
|
212
|
+
};
|
|
213
|
+
var versionFromPackageManifest = (probe, binPath) => {
|
|
214
|
+
let dir = probe.dirname(binPath);
|
|
215
|
+
for (let depth = 0; depth < 4; depth++) {
|
|
216
|
+
const raw = probe.readFile(probe.join(dir, "package.json"));
|
|
217
|
+
if (raw) {
|
|
218
|
+
try {
|
|
219
|
+
const manifest = JSON.parse(raw);
|
|
220
|
+
if (typeof manifest.version === "string") {
|
|
221
|
+
const parsed = parseCodexVersion(manifest.version);
|
|
222
|
+
if (parsed) return parsed;
|
|
223
|
+
}
|
|
224
|
+
} catch {
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const parent = probe.dirname(dir);
|
|
228
|
+
if (parent === dir) break;
|
|
229
|
+
dir = parent;
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
};
|
|
233
|
+
var readCodexVersion = (probe) => {
|
|
234
|
+
const onPath = probe.resolveOnPath();
|
|
235
|
+
if (onPath) {
|
|
236
|
+
const real = probe.realpath(onPath) ?? onPath;
|
|
237
|
+
const fromManifest = versionFromPackageManifest(probe, real);
|
|
238
|
+
if (fromManifest) return { kind: "known", version: fromManifest, source: "package manifest" };
|
|
239
|
+
}
|
|
240
|
+
const brew = probe.brewVersions();
|
|
241
|
+
if (brew) {
|
|
242
|
+
const parsed = parseCodexVersion(brew);
|
|
243
|
+
if (parsed) return { kind: "known", version: parsed, source: "homebrew" };
|
|
244
|
+
}
|
|
245
|
+
return { kind: "unknown" };
|
|
246
|
+
};
|
|
247
|
+
|
|
196
248
|
// src/skills-cli.ts
|
|
197
249
|
import { execSync } from "child_process";
|
|
198
250
|
var installSkillViaSkillsCli = (agent) => {
|
|
@@ -318,6 +370,9 @@ var parseSetupOptions = (input2) => {
|
|
|
318
370
|
if (key && keyStdin) {
|
|
319
371
|
throw new UsageError("Pass either --key or --key-stdin, not both");
|
|
320
372
|
}
|
|
373
|
+
if (key && !isValidApiKey(key)) {
|
|
374
|
+
throw new UsageError("--key is not a valid API key. Expected the pk_xxx.xxx shape from pushary.com/dashboard/agent/settings");
|
|
375
|
+
}
|
|
321
376
|
return {
|
|
322
377
|
connectMode,
|
|
323
378
|
connectExplicit: connectRaw !== void 0,
|
|
@@ -328,7 +383,14 @@ var parseSetupOptions = (input2) => {
|
|
|
328
383
|
// Anything that means "there is nobody to answer a prompt". A non-TTY stdin
|
|
329
384
|
// is included because that is the case that used to hang: it is better to
|
|
330
385
|
// take the defaults and say so than to wait forever for a keypress.
|
|
331
|
-
|
|
386
|
+
//
|
|
387
|
+
// --json counts. Under it stdout carries exactly one object and every human
|
|
388
|
+
// line is redirected to stderr, so a prompt has nowhere to render: the
|
|
389
|
+
// question goes to stderr while the run blocks on a keypress the caller
|
|
390
|
+
// cannot see it is waiting for, and whatever is reading stdout waits forever
|
|
391
|
+
// for an object that never arrives. Asking for machine-readable output is
|
|
392
|
+
// itself a statement that no human is watching.
|
|
393
|
+
nonInteractive: yes || agentsFlag !== void 0 || flags.bools.has("json") || !input2.stdinIsTty,
|
|
332
394
|
json: flags.bools.has("json"),
|
|
333
395
|
dryRun: flags.bools.has("dry-run"),
|
|
334
396
|
help,
|
|
@@ -359,6 +421,7 @@ var humanStream = process.stdout;
|
|
|
359
421
|
var writeHuman = (text) => {
|
|
360
422
|
humanStream.write(text);
|
|
361
423
|
};
|
|
424
|
+
var jsonMode = false;
|
|
362
425
|
var dim = (s) => `\x1B[2m${s}\x1B[0m`;
|
|
363
426
|
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
364
427
|
var green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
@@ -367,6 +430,10 @@ var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
|
367
430
|
var check = green("\u2713");
|
|
368
431
|
var abortWith = async (reason, code, keyCheck) => {
|
|
369
432
|
await reportSetupAbort(reason, code, keyCheck);
|
|
433
|
+
if (jsonMode) {
|
|
434
|
+
process.stdout.write(`${JSON.stringify({ ok: false, reason, exitCode: code, ...keyCheck ? { keyCheck } : {} }, null, 2)}
|
|
435
|
+
`);
|
|
436
|
+
}
|
|
370
437
|
return code;
|
|
371
438
|
};
|
|
372
439
|
var readKeyFromStdin = async (options) => {
|
|
@@ -621,26 +688,46 @@ var setupHermes = async (_apiKey) => {
|
|
|
621
688
|
var CODEX_HOOKS_JSON = join(CODEX_HOME, "hooks.json");
|
|
622
689
|
var CODEX_HOOKS_MIN_VERSION = [0, 122, 0];
|
|
623
690
|
var CODEX_TRUST_VERIFIED_MAX = [0, 142, 2];
|
|
624
|
-
var
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
};
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
691
|
+
var detectCodexVersion = () => readCodexVersion({
|
|
692
|
+
resolveOnPath: () => {
|
|
693
|
+
const whichCmd = IS_WINDOWS ? "where" : "which";
|
|
694
|
+
try {
|
|
695
|
+
const found = execSync2(`${whichCmd} codex`, { encoding: "utf-8", stdio: "pipe", timeout: 5e3 }).split("\n")[0].trim();
|
|
696
|
+
return found || null;
|
|
697
|
+
} catch {
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
},
|
|
701
|
+
realpath: (path) => {
|
|
702
|
+
try {
|
|
703
|
+
return realpathSync(path);
|
|
704
|
+
} catch {
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
},
|
|
708
|
+
readFile: (path) => {
|
|
709
|
+
try {
|
|
710
|
+
return readFileSync(path, "utf-8");
|
|
711
|
+
} catch {
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
brewVersions: () => {
|
|
716
|
+
try {
|
|
717
|
+
return execSync2("brew list --versions codex", {
|
|
718
|
+
encoding: "utf-8",
|
|
719
|
+
stdio: "pipe",
|
|
720
|
+
timeout: 1e4
|
|
721
|
+
}).trim() || null;
|
|
722
|
+
} catch {
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
},
|
|
726
|
+
join,
|
|
727
|
+
dirname
|
|
728
|
+
});
|
|
729
|
+
var codexSupportsHooks = (result) => result.kind === "known" && compareCodexVersion(result.version, CODEX_HOOKS_MIN_VERSION) >= 0;
|
|
730
|
+
var codexTrustAutoSupported = (result) => result.kind === "known" && codexSupportsHooks(result) && compareCodexVersion(result.version, CODEX_TRUST_VERIFIED_MAX) <= 0;
|
|
644
731
|
var removeCodexNotifyEntry = (codexConfig) => {
|
|
645
732
|
let raw = "";
|
|
646
733
|
try {
|
|
@@ -691,7 +778,7 @@ var setupCodex = async (apiKey) => {
|
|
|
691
778
|
addCodexMcpServer(config, apiKey);
|
|
692
779
|
writeFileAtomic(codexConfig, stringifyTOML(config), KEY_FILE_MODE);
|
|
693
780
|
});
|
|
694
|
-
const codexVersion =
|
|
781
|
+
const codexVersion = detectCodexVersion();
|
|
695
782
|
const hooksSupported = codexSupportsHooks(codexVersion);
|
|
696
783
|
const trustAuto = codexTrustAutoSupported(codexVersion);
|
|
697
784
|
let trusted = false;
|
|
@@ -719,9 +806,16 @@ var setupCodex = async (apiKey) => {
|
|
|
719
806
|
removeCodexNotifyEntry(codexConfig);
|
|
720
807
|
});
|
|
721
808
|
} else {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
809
|
+
if (codexVersion.kind === "known") {
|
|
810
|
+
console.log(` ${yellow("!")} This Codex version predates native hooks (needs ${CODEX_HOOKS_MIN_VERSION.join(".")}+).`);
|
|
811
|
+
console.log(` ${dim("Installing the deprecated notify handler instead. Upgrade Codex and re-run setup")}`);
|
|
812
|
+
console.log(` ${dim("to get policy enforcement, phone approvals, and session tracking.")}`);
|
|
813
|
+
} else {
|
|
814
|
+
console.log(` ${yellow("!")} Could not read your Codex version from its install, so hooks were skipped.`);
|
|
815
|
+
console.log(` ${dim("Deliberately not run: asking the binary its version is what macOS quarantines.")}`);
|
|
816
|
+
console.log(` ${dim(`Installing the notify handler, which works on every version. If Codex is ${CODEX_HOOKS_MIN_VERSION.join(".")}+,`)}`);
|
|
817
|
+
console.log(` ${dim("re-run setup after installing it through npm or Homebrew to get native hooks.")}`);
|
|
818
|
+
}
|
|
725
819
|
await spinner("Adding notify handler for Codex events (deprecated)", async () => {
|
|
726
820
|
addCodexNotifyEntry(codexConfig);
|
|
727
821
|
});
|
|
@@ -1053,6 +1147,44 @@ var setupGemini = async (apiKey) => {
|
|
|
1053
1147
|
console.log(` ${dim("Restart Gemini CLI to load the new config.")}`);
|
|
1054
1148
|
noteManual("Restart Gemini CLI to load the new config.");
|
|
1055
1149
|
};
|
|
1150
|
+
var agentIsWired = (agent) => {
|
|
1151
|
+
const readJson2 = (path) => {
|
|
1152
|
+
try {
|
|
1153
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
1154
|
+
} catch {
|
|
1155
|
+
return null;
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
switch (agent) {
|
|
1159
|
+
case "claude_code":
|
|
1160
|
+
return claudeWired({
|
|
1161
|
+
claudeJson: readJson2(CLAUDE_JSON),
|
|
1162
|
+
settings: readJson2(CLAUDE_SETTINGS),
|
|
1163
|
+
skillExists: existsSync(join(CLAUDE_SKILL_DIR, "SKILL.md"))
|
|
1164
|
+
});
|
|
1165
|
+
case "codex": {
|
|
1166
|
+
let config = null;
|
|
1167
|
+
try {
|
|
1168
|
+
config = parseTOML(readFileSync(join(CODEX_HOME, "config.toml"), "utf-8"));
|
|
1169
|
+
} catch {
|
|
1170
|
+
config = null;
|
|
1171
|
+
}
|
|
1172
|
+
return codexWired({
|
|
1173
|
+
config,
|
|
1174
|
+
hooks: readJson2(CODEX_HOOKS_JSON),
|
|
1175
|
+
skillExists: existsSync(join(CODEX_SKILL_DIR, "SKILL.md"))
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
case "gemini_cli":
|
|
1179
|
+
return geminiWired({ settings: readJson2(GEMINI_SETTINGS) });
|
|
1180
|
+
case "cursor":
|
|
1181
|
+
return existsSync(join(CURSOR_PLUGIN_DIR, "mcp.json"));
|
|
1182
|
+
case "vscode":
|
|
1183
|
+
return existsSync(join(VSCODE_PLUGIN_DIR, ".mcp.json"));
|
|
1184
|
+
default:
|
|
1185
|
+
return true;
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1056
1188
|
var AGENT_SETUP = {
|
|
1057
1189
|
claude_code: setupClaudeCode,
|
|
1058
1190
|
codex: setupCodex,
|
|
@@ -1201,7 +1333,8 @@ var resolveAgents = async (options) => {
|
|
|
1201
1333
|
return options.agents.agents;
|
|
1202
1334
|
}
|
|
1203
1335
|
const detected = detectAgents();
|
|
1204
|
-
|
|
1336
|
+
const wantsAuto = options.agents.kind === "auto" || options.nonInteractive;
|
|
1337
|
+
if (wantsAuto) {
|
|
1205
1338
|
const picked = Object.keys(detected).filter((agent) => detected[agent].found);
|
|
1206
1339
|
if (picked.length === 0) {
|
|
1207
1340
|
console.log(` ${yellow("!")} No agents detected, so nothing was configured. Your key is saved.`);
|
|
@@ -1283,6 +1416,7 @@ var main = async () => {
|
|
|
1283
1416
|
}
|
|
1284
1417
|
recordSetupOptions(options);
|
|
1285
1418
|
if (options.json) {
|
|
1419
|
+
jsonMode = true;
|
|
1286
1420
|
humanStream = process.stderr;
|
|
1287
1421
|
setHumanStream(process.stderr);
|
|
1288
1422
|
console.log = (...args) => {
|
|
@@ -1304,12 +1438,12 @@ var main = async () => {
|
|
|
1304
1438
|
let keyFromPairing = false;
|
|
1305
1439
|
const storedKey = readConfigFileKey();
|
|
1306
1440
|
const pairingRequested = connectMode === "app" && options.connectExplicit && !options.skipPhone;
|
|
1307
|
-
const ambientEligible =
|
|
1441
|
+
const ambientEligible = flagKey === void 0 && !pairingRequested;
|
|
1308
1442
|
const envCheck = ambientEligible && envKey && isValidApiKey(envKey) ? await usableStoredKey(envKey) : null;
|
|
1309
1443
|
const storedCheck = ambientEligible && envCheck === null && storedKey !== null && isValidApiKey(storedKey) && // login writes both, so without this the same key is probed twice.
|
|
1310
1444
|
storedKey !== envKey ? await usableStoredKey(storedKey) : null;
|
|
1311
1445
|
let storedKeyCheck = null;
|
|
1312
|
-
if (flagKey
|
|
1446
|
+
if (flagKey) {
|
|
1313
1447
|
const masked = `${flagKey.slice(0, 8)}...${flagKey.slice(-4)}`;
|
|
1314
1448
|
console.log(` ${check} Using API key: ${dim(masked)}`);
|
|
1315
1449
|
console.log();
|
|
@@ -1445,8 +1579,15 @@ var main = async () => {
|
|
|
1445
1579
|
markStep(`configure:${agent}`);
|
|
1446
1580
|
try {
|
|
1447
1581
|
const outcome = await AGENT_SETUP[agent](trimmedKey, context);
|
|
1448
|
-
if (outcome === "skipped")
|
|
1449
|
-
|
|
1582
|
+
if (outcome === "skipped") {
|
|
1583
|
+
skipped.push(agent);
|
|
1584
|
+
} else if (agentIsWired(agent)) {
|
|
1585
|
+
completed.push(agent);
|
|
1586
|
+
} else {
|
|
1587
|
+
failed.push(agent.replace("_", " "));
|
|
1588
|
+
console.log(` ${yellow("!")} ${agent.replace("_", " ")} reported success but its config is not there.`);
|
|
1589
|
+
console.log(` ${dim("Re-run setup, then")} ${cyan("npx @pushary/agent-hooks@latest doctor")} ${dim("to see what is missing.")}`);
|
|
1590
|
+
}
|
|
1450
1591
|
} catch (err) {
|
|
1451
1592
|
failed.push(agent.replace("_", " "));
|
|
1452
1593
|
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
|