@pushary/agent-hooks 1.7.3 → 1.8.1
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 +14 -0
- package/README.md +39 -19
- package/data/SKILL.md +8 -8
- package/data/cowork/SKILL.md +2 -2
- package/data/cursor-plugin/README.md +2 -2
- package/data/cursor-plugin/scripts/pushary-gate.mjs +2 -21
- package/data/cursor-plugin/scripts/redaction.mjs +53 -0
- package/data/cursor-plugin/skills/pushary/SKILL.md +7 -7
- package/data/vscode-plugin/README.md +4 -4
- package/data/vscode-plugin/scripts/pushary-gate.mjs +3 -23
- package/data/vscode-plugin/scripts/redaction.mjs +53 -0
- package/data/vscode-plugin/skills/pushary/SKILL.md +7 -7
- package/dist/bin/pushary-bell-hook.js +23 -23
- package/dist/bin/pushary-bell.js +9 -9
- package/dist/bin/pushary-claude.js +26 -26
- package/dist/bin/pushary-clean.js +97 -111
- package/dist/bin/pushary-codex-bridge.js +18 -18
- package/dist/bin/pushary-codex-hook.js +42 -43
- package/dist/bin/pushary-codex.js +13 -13
- package/dist/bin/pushary-connect.js +13 -13
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +27 -27
- package/dist/bin/pushary-disconnect.js +18 -18
- package/dist/bin/pushary-doctor.js +92 -86
- package/dist/bin/pushary-elicitation-hook.js +25 -25
- package/dist/bin/pushary-gemini-bridge.js +12 -12
- package/dist/bin/pushary-gemini-hook.js +29 -30
- package/dist/bin/pushary-hook.js +32 -32
- package/dist/bin/pushary-login.js +12 -12
- package/dist/bin/pushary-logout.js +7 -7
- package/dist/bin/pushary-mcp.js +85 -28
- package/dist/bin/pushary-mode.js +9 -9
- package/dist/bin/pushary-notification-hook.js +28 -28
- package/dist/bin/pushary-opencode-hook.js +28 -29
- package/dist/bin/pushary-permission-denied-hook.js +32 -32
- package/dist/bin/pushary-permission-hook.js +31 -31
- package/dist/bin/pushary-post-hook.js +28 -28
- package/dist/bin/pushary-prompt-hook.js +28 -28
- package/dist/bin/pushary-session-end-hook.js +28 -28
- package/dist/bin/pushary-session-start-hook.js +32 -32
- package/dist/bin/pushary-setup.js +62 -129
- package/dist/bin/pushary-stats.js +5 -5
- package/dist/bin/pushary-status.js +15 -15
- package/dist/bin/pushary-stop-hook.js +28 -28
- package/dist/bin/pushary-stopfailure-hook.js +28 -28
- package/dist/bin/pushary-suggestions.js +4 -4
- package/dist/bin/pushary-transcript-register.js +19 -19
- package/dist/bin/pushary-transcripts.js +7 -7
- package/dist/bin/pushary-upgrade.js +236 -110
- package/dist/bin/pushary-wait.js +9 -9
- package/dist/bin/pushary.js +6 -6
- package/dist/{chunk-DUQO4C4I.js → chunk-2SLCHCVN.js} +3 -3
- package/dist/{chunk-YYCTF4Q5.js → chunk-35X5UDML.js} +7 -7
- package/dist/{chunk-OVC5QXN5.js → chunk-4A3UQERT.js} +1 -1
- package/dist/chunk-5DGNEUXK.js +73 -0
- package/dist/{chunk-62CVUUNU.js → chunk-5Y2DY6X4.js} +2 -2
- package/dist/{chunk-G4GLCLKK.js → chunk-5YY3IJT4.js} +159 -36
- package/dist/{chunk-KJXUIPS4.js → chunk-64H5IZZD.js} +28 -16
- package/dist/{chunk-OTNOANMX.js → chunk-6TK4IY3A.js} +8 -8
- package/dist/{chunk-LPNJLKBQ.js → chunk-72WOCX22.js} +1 -1
- package/dist/{chunk-IRZG3HEU.js → chunk-7ITOAMCD.js} +1 -1
- package/dist/{chunk-G62JL2NT.js → chunk-7U7ZWUTR.js} +1 -1
- package/dist/{chunk-IPSRMLCJ.js → chunk-ASWRMBOM.js} +1 -1
- package/dist/{chunk-5S2VBSJI.js → chunk-CB23AO4D.js} +2 -2
- package/dist/{chunk-VKVSRCBO.js → chunk-CHMOVJ2M.js} +1 -1
- package/dist/{chunk-BGBLLHZ4.js → chunk-DLEKCAQL.js} +1 -1
- package/dist/{chunk-EEEJ2JIL.js → chunk-DVW2MYCM.js} +3 -3
- package/dist/{chunk-AWVNAYGD.js → chunk-DXBS2EL7.js} +1 -1
- package/dist/{chunk-ZKJ34T2F.js → chunk-EIGJYCOE.js} +31 -17
- package/dist/{chunk-U6RVDUJ5.js → chunk-F6NTWZZC.js} +283 -41
- package/dist/{chunk-7RUJKCR4.js → chunk-FLQPM2GQ.js} +2 -2
- package/dist/{chunk-TCVMK6GI.js → chunk-GBNQXYN4.js} +4 -4
- package/dist/{chunk-G6RRVJOG.js → chunk-GH6KGWMC.js} +6 -6
- package/dist/chunk-GU3EJCVV.js +43 -0
- package/dist/{chunk-6JQFIHZC.js → chunk-HBWC3JVZ.js} +3 -3
- package/dist/{chunk-IWJKSIB4.js → chunk-HMNR7B2F.js} +1 -1
- package/dist/{chunk-NOI3XTCY.js → chunk-HOEB6AKG.js} +1 -1
- package/dist/{chunk-62VT3BPH.js → chunk-HQRCVEI2.js} +32 -17
- package/dist/{chunk-MIXWMA73.js → chunk-HTEGMMT3.js} +3 -3
- package/dist/{chunk-BIH2Y2HP.js → chunk-HVDMGDYV.js} +2 -2
- package/dist/{chunk-VE7TXLR4.js → chunk-I4HKMUZQ.js} +3 -3
- package/dist/{chunk-YPUBA2FL.js → chunk-INSMZNS7.js} +5 -5
- package/dist/{chunk-G3OMOTJ4.js → chunk-JRQRNNGA.js} +3 -3
- package/dist/{chunk-N5MZH5A6.js → chunk-KB2FTO4O.js} +4 -4
- package/dist/{chunk-O3JDWNLU.js → chunk-LK2S5XIV.js} +183 -28
- package/dist/{chunk-YC27PROF.js → chunk-MMVCI6DT.js} +20 -5
- package/dist/{chunk-3EUBQARL.js → chunk-MYHG7PX2.js} +1 -1
- package/dist/{chunk-EADXTSHE.js → chunk-N6JJJTPH.js} +6 -3
- package/dist/{chunk-PRNFSORD.js → chunk-NHCUXTEM.js} +1 -1
- package/dist/{chunk-KEL5MXRL.js → chunk-NOLPURUI.js} +1 -1
- package/dist/{chunk-WTJM7PSL.js → chunk-OCVVSRDA.js} +3 -3
- package/dist/{chunk-R6OYCUNK.js → chunk-ON5JW2HP.js} +1 -1
- package/dist/{chunk-UOQRM7JQ.js → chunk-QB6QYZ6B.js} +5 -5
- package/dist/{chunk-7OA34PNW.js → chunk-T55H6YEP.js} +40 -10
- package/dist/{chunk-D6OODZCM.js → chunk-TJMAXQ5Q.js} +2 -2
- package/dist/{chunk-WGMW7OXW.js → chunk-TKQ6LZQZ.js} +6 -6
- package/dist/{chunk-WPX67AKT.js → chunk-UMMCHS6P.js} +1 -1
- package/dist/{chunk-MZBRL5H5.js → chunk-UPLV5AJF.js} +108 -25
- package/dist/{chunk-P2VIBGXA.js → chunk-URNXU3FO.js} +1 -1
- package/dist/{chunk-EUGNVLMG.js → chunk-VFFHQC7W.js} +3 -1
- package/dist/{chunk-Y4ZH64UK.js → chunk-VZMIHERK.js} +2 -2
- package/dist/{chunk-RLW3JWDY.js → chunk-W2LJ4O3A.js} +2 -2
- package/dist/{chunk-LHC45YUC.js → chunk-ZJYJAD52.js} +1 -1
- package/dist/{reapply-PH5T4BGR.js → reapply-ICGTBHVE.js} +9 -9
- package/dist/src/index.js +20 -20
- package/package.json +1 -1
- package/dist/chunk-5GB22QYL.js +0 -255
- package/dist/chunk-VYPJHGG6.js +0 -92
|
@@ -11,35 +11,43 @@ import {
|
|
|
11
11
|
savePendingCommand
|
|
12
12
|
} from "./chunk-N6IOSZOE.js";
|
|
13
13
|
import {
|
|
14
|
+
IDLE_PROMPT_NOTIFICATION,
|
|
14
15
|
NOTIFICATION_PUSH_TYPES,
|
|
15
16
|
POSTTOOLUSE_MIRROR_ONLY_TOOLS
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-VFFHQC7W.js";
|
|
17
18
|
import {
|
|
18
19
|
cancelQuestion,
|
|
19
20
|
isPolicyConfig,
|
|
20
21
|
sendNotification,
|
|
21
22
|
waitForAnswer
|
|
22
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-MMVCI6DT.js";
|
|
23
24
|
import {
|
|
24
25
|
clientErrorKindOf,
|
|
25
26
|
reportClientError
|
|
26
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-KB2FTO4O.js";
|
|
27
28
|
import {
|
|
28
29
|
withRetry
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-I4HKMUZQ.js";
|
|
30
31
|
import {
|
|
31
32
|
getMachineId
|
|
32
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-4A3UQERT.js";
|
|
33
34
|
import {
|
|
34
35
|
reportedInstallSource
|
|
35
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-5Y2DY6X4.js";
|
|
37
|
+
import {
|
|
38
|
+
canonicalJson
|
|
39
|
+
} from "./chunk-UPLV5AJF.js";
|
|
40
|
+
import {
|
|
41
|
+
writeFileAtomic
|
|
42
|
+
} from "./chunk-T7E75EKA.js";
|
|
36
43
|
import {
|
|
37
44
|
getApiKey,
|
|
38
45
|
getBaseUrl
|
|
39
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-ON5JW2HP.js";
|
|
40
47
|
import {
|
|
41
48
|
ACTION_BODY_MAX,
|
|
42
49
|
AGENT_LABELS_BY_TYPE,
|
|
50
|
+
BLOCKED_ACTION_GRANT_TTL_SECONDS,
|
|
43
51
|
DECISION_LINE_MAX,
|
|
44
52
|
FILE_TARGET_TOOLS,
|
|
45
53
|
GATE_MAX_TOOL_INPUTS,
|
|
@@ -47,6 +55,7 @@ import {
|
|
|
47
55
|
HOOK_SOURCES,
|
|
48
56
|
PENDING_COMMAND_PREFIX,
|
|
49
57
|
REPO_AWARE_AGENTS,
|
|
58
|
+
blockedActionIdentity,
|
|
50
59
|
canonicalToolName,
|
|
51
60
|
gateOutputFor,
|
|
52
61
|
hookCompatibleProfileBySource,
|
|
@@ -59,8 +68,9 @@ import {
|
|
|
59
68
|
redactSecretsDeep,
|
|
60
69
|
resolveAutoResolveOrigin,
|
|
61
70
|
resolvePolicy,
|
|
62
|
-
toToolLookup
|
|
63
|
-
|
|
71
|
+
toToolLookup,
|
|
72
|
+
truncateLine
|
|
73
|
+
} from "./chunk-5YY3IJT4.js";
|
|
64
74
|
|
|
65
75
|
// src/policy.ts
|
|
66
76
|
import { createHash } from "crypto";
|
|
@@ -550,7 +560,7 @@ var scopePathFor = (toolName, toolInput, cwd) => {
|
|
|
550
560
|
};
|
|
551
561
|
var firstString = (...values) => values.find((v) => typeof v === "string" && v.length > 0);
|
|
552
562
|
var ACTION_BODY_TRUNCATION_MARKER = "\n\u2026 [truncated]";
|
|
553
|
-
var capActionBody = (text) => text
|
|
563
|
+
var capActionBody = (text) => truncateLine(text, ACTION_BODY_MAX, ACTION_BODY_TRUNCATION_MARKER);
|
|
554
564
|
var rawActionBody = (toolName, toolInput) => {
|
|
555
565
|
if (toolName === "Edit" || toolName === "replace") {
|
|
556
566
|
const before = firstString(toolInput.old_string);
|
|
@@ -577,7 +587,7 @@ var deriveActionBody = (toolName, toolInput) => {
|
|
|
577
587
|
return void 0;
|
|
578
588
|
}
|
|
579
589
|
};
|
|
580
|
-
var deriveAction = (toolName, toolInput) => describeToolCall(toolName, toolInput, "hook")
|
|
590
|
+
var deriveAction = (toolName, toolInput) => truncateLine(describeToolCall(toolName, toolInput, "hook"), DECISION_LINE_MAX);
|
|
581
591
|
var BLOCKER_BY_MODE = {
|
|
582
592
|
push_only: "Approval required before this runs",
|
|
583
593
|
push_first: "Paused for your approval before continuing",
|
|
@@ -585,7 +595,7 @@ var BLOCKER_BY_MODE = {
|
|
|
585
595
|
};
|
|
586
596
|
var deriveBlocker = (mode, reason) => {
|
|
587
597
|
const text = reason?.replace(/\s+/g, " ").trim() || BLOCKER_BY_MODE[mode] || "Waiting for your approval";
|
|
588
|
-
return text
|
|
598
|
+
return truncateLine(text, DECISION_LINE_MAX);
|
|
589
599
|
};
|
|
590
600
|
|
|
591
601
|
// ../hook-mapping/src/map.ts
|
|
@@ -1203,6 +1213,96 @@ var permissionTimeoutDecision = (timeoutAction) => {
|
|
|
1203
1213
|
};
|
|
1204
1214
|
var preToolUseTimeoutDecision = (timeoutAction, denyReason = "No response within timeout") => timeoutAction === "deny" ? codexDeny(denyReason) : codexPass();
|
|
1205
1215
|
|
|
1216
|
+
// src/codex-call-decision.ts
|
|
1217
|
+
import { createHash as createHash3 } from "crypto";
|
|
1218
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, readdirSync, rmSync, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1219
|
+
import { tmpdir as tmpdir3, userInfo } from "os";
|
|
1220
|
+
import { join as join4 } from "path";
|
|
1221
|
+
var CODEX_DECISION_TTL_MS = 60 * 1e3;
|
|
1222
|
+
var userScope = () => {
|
|
1223
|
+
try {
|
|
1224
|
+
const info = userInfo();
|
|
1225
|
+
const id = typeof info.uid === "number" && info.uid >= 0 ? String(info.uid) : info.username;
|
|
1226
|
+
return id.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || "default";
|
|
1227
|
+
} catch {
|
|
1228
|
+
return "default";
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
var decisionDir = () => join4(tmpdir3(), `pushary-codex-decisions-${userScope()}`);
|
|
1232
|
+
var decisionFile = (key) => join4(decisionDir(), key);
|
|
1233
|
+
var PRESENTATION_ONLY_INPUT_KEYS = ["description"];
|
|
1234
|
+
var identifyingToolInput = (toolInput) => {
|
|
1235
|
+
if (!toolInput) return {};
|
|
1236
|
+
const identifying = {};
|
|
1237
|
+
for (const [key, value] of Object.entries(toolInput)) {
|
|
1238
|
+
if (PRESENTATION_ONLY_INPUT_KEYS.includes(key)) continue;
|
|
1239
|
+
identifying[key] = value;
|
|
1240
|
+
}
|
|
1241
|
+
return identifying;
|
|
1242
|
+
};
|
|
1243
|
+
var codexCallKey = (identity) => {
|
|
1244
|
+
if (!identity.sessionId || !identity.turnId || !identity.toolName) return null;
|
|
1245
|
+
const material = canonicalJson([
|
|
1246
|
+
identity.sessionId,
|
|
1247
|
+
identity.turnId,
|
|
1248
|
+
identity.toolName,
|
|
1249
|
+
identifyingToolInput(identity.toolInput)
|
|
1250
|
+
]);
|
|
1251
|
+
return createHash3("sha256").update(material, "utf-8").digest("hex").slice(0, 32);
|
|
1252
|
+
};
|
|
1253
|
+
var parseDecision = (raw) => {
|
|
1254
|
+
try {
|
|
1255
|
+
const parsed = JSON.parse(raw);
|
|
1256
|
+
if (parsed.kind === "approved") {
|
|
1257
|
+
return typeof parsed.toolUseId === "string" && parsed.toolUseId ? { kind: "approved", toolUseId: parsed.toolUseId } : null;
|
|
1258
|
+
}
|
|
1259
|
+
if (parsed.kind === "handled") return { kind: "handled" };
|
|
1260
|
+
if (parsed.kind === "denied") {
|
|
1261
|
+
return { kind: "denied", reason: typeof parsed.reason === "string" ? parsed.reason : "" };
|
|
1262
|
+
}
|
|
1263
|
+
return null;
|
|
1264
|
+
} catch {
|
|
1265
|
+
return null;
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
var sweepExpired = (dir, nowMs) => {
|
|
1269
|
+
try {
|
|
1270
|
+
for (const name of readdirSync(dir)) {
|
|
1271
|
+
const path = join4(dir, name);
|
|
1272
|
+
try {
|
|
1273
|
+
if (nowMs - statSync3(path).mtimeMs >= CODEX_DECISION_TTL_MS) rmSync(path, { force: true });
|
|
1274
|
+
} catch {
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
} catch {
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
var recordCodexCallDecision = (key, decision) => {
|
|
1283
|
+
if (!key) return;
|
|
1284
|
+
try {
|
|
1285
|
+
const dir = decisionDir();
|
|
1286
|
+
if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
1287
|
+
else sweepExpired(dir, Date.now());
|
|
1288
|
+
writeFileSync3(decisionFile(key), JSON.stringify(decision), { encoding: "utf-8", mode: 384 });
|
|
1289
|
+
} catch {
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
var consumeCodexCallDecision = (key) => {
|
|
1294
|
+
if (!key) return null;
|
|
1295
|
+
const path = decisionFile(key);
|
|
1296
|
+
try {
|
|
1297
|
+
const fresh = Date.now() - statSync3(path).mtimeMs < CODEX_DECISION_TTL_MS;
|
|
1298
|
+
const raw = readFileSync4(path, "utf-8");
|
|
1299
|
+
rmSync(path, { force: true });
|
|
1300
|
+
return fresh ? parseDecision(raw) : null;
|
|
1301
|
+
} catch {
|
|
1302
|
+
return null;
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1206
1306
|
// src/driver.ts
|
|
1207
1307
|
var DRIVER_ENV = "PUSHARY_DRIVER";
|
|
1208
1308
|
var PHONE_DRIVER = "phone";
|
|
@@ -1213,50 +1313,182 @@ var phoneDrivenEnv = (base) => ({
|
|
|
1213
1313
|
});
|
|
1214
1314
|
|
|
1215
1315
|
// src/throttle.ts
|
|
1216
|
-
import { join as
|
|
1217
|
-
import { tmpdir as
|
|
1218
|
-
import { createHash as
|
|
1219
|
-
import { existsSync as
|
|
1220
|
-
var THROTTLE_DIR =
|
|
1221
|
-
var markerPath = (key) =>
|
|
1316
|
+
import { join as join5 } from "path";
|
|
1317
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
1318
|
+
import { createHash as createHash4 } from "crypto";
|
|
1319
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, statSync as statSync4, writeFileSync as writeFileSync4, rmSync as rmSync2 } from "fs";
|
|
1320
|
+
var THROTTLE_DIR = join5(tmpdir4(), "pushary-throttle");
|
|
1321
|
+
var markerPath = (key) => join5(THROTTLE_DIR, createHash4("sha256").update(key).digest("hex").slice(0, 16));
|
|
1222
1322
|
var claimThrottle = (key, windowMs) => {
|
|
1223
1323
|
try {
|
|
1224
1324
|
const path = markerPath(key);
|
|
1225
|
-
if (
|
|
1226
|
-
|
|
1227
|
-
|
|
1325
|
+
if (existsSync4(path) && Date.now() - statSync4(path).mtimeMs < windowMs) return "throttled";
|
|
1326
|
+
mkdirSync3(THROTTLE_DIR, { recursive: true });
|
|
1327
|
+
writeFileSync4(path, "", "utf-8");
|
|
1228
1328
|
return "passed";
|
|
1229
1329
|
} catch {
|
|
1230
1330
|
return "unavailable";
|
|
1231
1331
|
}
|
|
1232
1332
|
};
|
|
1233
1333
|
var releaseThrottle = (key) => {
|
|
1234
|
-
|
|
1334
|
+
rmSync2(markerPath(key), { force: true });
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1337
|
+
// src/blocked-action-hint.ts
|
|
1338
|
+
import { createHash as createHash5 } from "crypto";
|
|
1339
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, readdirSync as readdirSync2, rmSync as rmSync3, statSync as statSync5, utimesSync } from "fs";
|
|
1340
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
1341
|
+
import { join as join6 } from "path";
|
|
1342
|
+
var BLOCKED_ACTION_HINT_TTL_MS = BLOCKED_ACTION_GRANT_TTL_SECONDS * 1e3;
|
|
1343
|
+
var FINGERPRINT = /^[0-9a-f]{64}$/;
|
|
1344
|
+
var isFingerprint = (value) => typeof value === "string" && FINGERPRINT.test(value);
|
|
1345
|
+
var blockedActionSubject = (source, sessionId, toolName, toolInput, cwd) => {
|
|
1346
|
+
if (source !== "claude") return { source, sessionId, toolName, toolInput };
|
|
1347
|
+
return cwd ? { source, sessionId, cwd, toolName, toolInput } : null;
|
|
1348
|
+
};
|
|
1349
|
+
var blockedActionFingerprint = ({ source = "claude", sessionId, toolName, toolInput, cwd }) => {
|
|
1350
|
+
if (!sessionId || !toolInput) return null;
|
|
1351
|
+
const canonical = canonicalGateInputs(source, toolName, [toolInput], cwd);
|
|
1352
|
+
if (!canonical || canonical.toolInputs.length !== 1) return null;
|
|
1353
|
+
const [canonicalInput] = canonical.toolInputs;
|
|
1354
|
+
if (!canonicalInput) return null;
|
|
1355
|
+
const subject = blockedActionSubject(source, sessionId, canonical.toolName, canonicalInput, cwd);
|
|
1356
|
+
return subject ? createHash5("sha256").update(blockedActionIdentity(subject), "utf8").digest("hex") : null;
|
|
1357
|
+
};
|
|
1358
|
+
var hintDir = () => join6(tmpdir5(), `pushary-blocked-action-hints-${userScope()}`);
|
|
1359
|
+
var hintFile = (fingerprint) => join6(hintDir(), fingerprint);
|
|
1360
|
+
var isExpired = (mtimeMs, nowMs) => nowMs - mtimeMs >= BLOCKED_ACTION_HINT_TTL_MS;
|
|
1361
|
+
var sweepExpired2 = (dir, nowMs) => {
|
|
1362
|
+
try {
|
|
1363
|
+
for (const name of readdirSync2(dir)) {
|
|
1364
|
+
const path = join6(dir, name);
|
|
1365
|
+
try {
|
|
1366
|
+
if (isExpired(statSync5(path).mtimeMs, nowMs)) rmSync3(path, { force: true });
|
|
1367
|
+
} catch {
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
} catch {
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
var recordBlockedActionHint = (fingerprint, approved) => {
|
|
1376
|
+
if (!isFingerprint(fingerprint)) return false;
|
|
1377
|
+
try {
|
|
1378
|
+
const dir = hintDir();
|
|
1379
|
+
if (!existsSync5(dir)) mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
1380
|
+
else sweepExpired2(dir, Date.now());
|
|
1381
|
+
writeFileAtomic(hintFile(fingerprint), JSON.stringify({ fingerprint, ...approved }), 384);
|
|
1382
|
+
return true;
|
|
1383
|
+
} catch {
|
|
1384
|
+
return false;
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
var readLiveHint = (fingerprint) => {
|
|
1388
|
+
const path = hintFile(fingerprint);
|
|
1389
|
+
try {
|
|
1390
|
+
const stat = statSync5(path);
|
|
1391
|
+
if (!stat.isFile()) return null;
|
|
1392
|
+
if (isExpired(stat.mtimeMs, Date.now())) {
|
|
1393
|
+
rmSync3(path, { force: true });
|
|
1394
|
+
return null;
|
|
1395
|
+
}
|
|
1396
|
+
const parsed = JSON.parse(readFileSync5(path, "utf-8"));
|
|
1397
|
+
return parsed?.fingerprint === fingerprint ? parsed : null;
|
|
1398
|
+
} catch {
|
|
1399
|
+
return null;
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
var hasBlockedActionHint = (fingerprint) => isFingerprint(fingerprint) && readLiveHint(fingerprint) !== null;
|
|
1403
|
+
var approvalIn = (fingerprint, sessionId) => {
|
|
1404
|
+
const hint = readLiveHint(fingerprint);
|
|
1405
|
+
if (hint?.sessionId !== sessionId || typeof hint.toolName !== "string") return null;
|
|
1406
|
+
return { fingerprint, sessionId, toolName: hint.toolName, reminded: hint.reminded === true };
|
|
1407
|
+
};
|
|
1408
|
+
var approvalAwaitingRetry = (sessionId) => {
|
|
1409
|
+
if (!sessionId) return null;
|
|
1410
|
+
try {
|
|
1411
|
+
for (const name of readdirSync2(hintDir())) {
|
|
1412
|
+
const approval = isFingerprint(name) ? approvalIn(name, sessionId) : null;
|
|
1413
|
+
if (approval) return approval;
|
|
1414
|
+
}
|
|
1415
|
+
return null;
|
|
1416
|
+
} catch {
|
|
1417
|
+
return null;
|
|
1418
|
+
}
|
|
1419
|
+
};
|
|
1420
|
+
var markApprovalReminded = (approval) => {
|
|
1421
|
+
const path = hintFile(approval.fingerprint);
|
|
1422
|
+
try {
|
|
1423
|
+
const { atime, mtime } = statSync5(path);
|
|
1424
|
+
const { fingerprint, sessionId, toolName } = approval;
|
|
1425
|
+
writeFileAtomic(path, JSON.stringify({ fingerprint, sessionId, toolName, reminded: true }), 384);
|
|
1426
|
+
utimesSync(path, atime, mtime);
|
|
1427
|
+
} catch {
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
var forgetBlockedActionHint = (fingerprint) => {
|
|
1432
|
+
if (!isFingerprint(fingerprint)) return;
|
|
1433
|
+
try {
|
|
1434
|
+
rmSync3(hintFile(fingerprint), { force: true });
|
|
1435
|
+
} catch {
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1440
|
+
// src/background-work.ts
|
|
1441
|
+
import { createHash as createHash6 } from "crypto";
|
|
1442
|
+
import { mkdirSync as mkdirSync5, rmSync as rmSync4, statSync as statSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
1443
|
+
import { tmpdir as tmpdir6 } from "os";
|
|
1444
|
+
import { join as join7 } from "path";
|
|
1445
|
+
var BACKGROUND_WORK_TTL_MS = 60 * 60 * 1e3;
|
|
1446
|
+
var backgroundWorkDir = () => join7(tmpdir6(), "pushary-background-work");
|
|
1447
|
+
var markerFor = (sessionKey) => join7(backgroundWorkDir(), createHash6("sha256").update(sessionKey).digest("hex"));
|
|
1448
|
+
var hasRunningBackgroundTasks = (backgroundTasks) => Array.isArray(backgroundTasks) && backgroundTasks.length > 0;
|
|
1449
|
+
var noteBackgroundWork = (sessionKey, running) => {
|
|
1450
|
+
try {
|
|
1451
|
+
if (!running) {
|
|
1452
|
+
rmSync4(markerFor(sessionKey), { force: true });
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
mkdirSync5(backgroundWorkDir(), { recursive: true, mode: 448 });
|
|
1456
|
+
writeFileSync5(markerFor(sessionKey), "", { mode: 384 });
|
|
1457
|
+
} catch {
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
var isWaitingOnBackgroundWork = (sessionKey, nowMs = Date.now()) => {
|
|
1462
|
+
try {
|
|
1463
|
+
return nowMs - statSync6(markerFor(sessionKey)).mtimeMs < BACKGROUND_WORK_TTL_MS;
|
|
1464
|
+
} catch {
|
|
1465
|
+
return false;
|
|
1466
|
+
}
|
|
1235
1467
|
};
|
|
1236
1468
|
|
|
1237
1469
|
// src/events.ts
|
|
1238
|
-
import { basename as basename3, join as
|
|
1239
|
-
import { existsSync as
|
|
1240
|
-
import { tmpdir as
|
|
1241
|
-
var INTENT_DIR =
|
|
1470
|
+
import { basename as basename3, join as join8 } from "path";
|
|
1471
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync6, statSync as statSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
1472
|
+
import { tmpdir as tmpdir7 } from "os";
|
|
1473
|
+
var INTENT_DIR = join8(tmpdir7(), "pushary-intent");
|
|
1242
1474
|
var INTENT_MAX_BYTES = 2048;
|
|
1243
1475
|
var INTENT_GRACE_MS = 10 * 60 * 1e3;
|
|
1244
1476
|
var sanitizeSession = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
|
|
1245
|
-
var intentFile = (sessionId) =>
|
|
1477
|
+
var intentFile = (sessionId) => join8(INTENT_DIR, sanitizeSession(sessionId));
|
|
1246
1478
|
var saveLastPrompt = (sessionId, prompt) => {
|
|
1247
1479
|
try {
|
|
1248
1480
|
const trimmed = prompt.replace(/\s+/g, " ").trim().slice(0, INTENT_MAX_BYTES);
|
|
1249
1481
|
if (!trimmed) return;
|
|
1250
|
-
|
|
1251
|
-
|
|
1482
|
+
mkdirSync6(INTENT_DIR, { recursive: true });
|
|
1483
|
+
writeFileSync6(intentFile(sessionId), trimmed, "utf-8");
|
|
1252
1484
|
} catch {
|
|
1253
1485
|
}
|
|
1254
1486
|
};
|
|
1255
1487
|
var readLastPrompt = (sessionId) => {
|
|
1256
1488
|
try {
|
|
1257
1489
|
const path = intentFile(sessionId);
|
|
1258
|
-
if (isDefaultSession(sessionId) &&
|
|
1259
|
-
const value =
|
|
1490
|
+
if (isDefaultSession(sessionId) && statSync7(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
|
|
1491
|
+
const value = readFileSync6(path, "utf-8").trim();
|
|
1260
1492
|
return value.length > 0 ? value : void 0;
|
|
1261
1493
|
} catch {
|
|
1262
1494
|
return void 0;
|
|
@@ -1320,8 +1552,8 @@ var POLICY_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
|
1320
1552
|
var readFreshCachedPolicy = (apiKey, agentType, sessionId) => {
|
|
1321
1553
|
const request = policyRequestFor(agentType);
|
|
1322
1554
|
const path = policyCacheFile(apiKey, request.agent, request.repoAware);
|
|
1323
|
-
if (!
|
|
1324
|
-
const cached = JSON.parse(
|
|
1555
|
+
if (!existsSync6(path)) return null;
|
|
1556
|
+
const cached = JSON.parse(readFileSync6(path, "utf-8"));
|
|
1325
1557
|
if (!isPolicyConfig(cached)) return null;
|
|
1326
1558
|
if ((cached._sessionId ?? null) !== (sessionId ?? null)) return null;
|
|
1327
1559
|
if (!cached._cachedAt || Date.now() - cached._cachedAt >= POLICY_CACHE_TTL_MS) return null;
|
|
@@ -1618,12 +1850,15 @@ var handleUserPrompt = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
|
1618
1850
|
return void 0;
|
|
1619
1851
|
}
|
|
1620
1852
|
};
|
|
1853
|
+
var UNUSED_APPROVAL_INSTRUCTION = "The person allowed, from Pushary, the call auto mode blocked. Send that call again now, exactly as it was with nothing changed, to use their approval. It covers only that exact call.";
|
|
1854
|
+
var phoneInstructionOf = (pendingCommand) => typeof pendingCommand === "string" && pendingCommand.trim().length > 0 ? `The user sent a new instruction from their phone via Pushary: ${pendingCommand.trim()}` : null;
|
|
1621
1855
|
var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
1622
1856
|
const deadline = Date.now() + HOOK_BUDGET_MS;
|
|
1623
1857
|
const signal = AbortSignal.timeout(HOOK_BUDGET_MS);
|
|
1624
1858
|
try {
|
|
1625
1859
|
const projectName = basename3(input.cwd ?? process.cwd());
|
|
1626
1860
|
const sessionKey = input.session_id || DEFAULT_SESSION;
|
|
1861
|
+
noteBackgroundWork(sessionKey, hasRunningBackgroundTasks(input.background_tasks));
|
|
1627
1862
|
const late = await reconcilePendingQuestions(sessionKey, void 0, signal);
|
|
1628
1863
|
const tasks = [
|
|
1629
1864
|
reportEvent({
|
|
@@ -1642,13 +1877,9 @@ var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
|
1642
1877
|
if (!isDefaultSession(sessionKey) && listPendingQuestions(sessionKey).length === 0) removePendingSession(sessionKey);
|
|
1643
1878
|
const [reported] = await Promise.allSettled(tasks);
|
|
1644
1879
|
const pendingCommand = reported.status === "fulfilled" ? reported.value?.pendingCommand : void 0;
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
reason: `The user sent a new instruction from their phone via Pushary: ${pendingCommand.trim()}`
|
|
1649
|
-
};
|
|
1650
|
-
}
|
|
1651
|
-
return void 0;
|
|
1880
|
+
const unusedApproval = !input.stop_hook_active && approvalAwaitingRetry(input.session_id) !== null;
|
|
1881
|
+
const instructions = [unusedApproval ? UNUSED_APPROVAL_INSTRUCTION : null, phoneInstructionOf(pendingCommand)].filter((instruction) => instruction !== null);
|
|
1882
|
+
return instructions.length > 0 ? { decision: "block", reason: instructions.join("\n\n") } : void 0;
|
|
1652
1883
|
} catch {
|
|
1653
1884
|
return void 0;
|
|
1654
1885
|
}
|
|
@@ -1726,6 +1957,7 @@ var handleSessionEnd = async (input, agent = CLAUDE_CODE_AGENT, options = {}) =>
|
|
|
1726
1957
|
}
|
|
1727
1958
|
};
|
|
1728
1959
|
var NOTIFICATION_THROTTLE_MS = 60 * 1e3;
|
|
1960
|
+
var pushesNotification = (notifType, sessionKey) => NOTIFICATION_PUSH_TYPES.has(notifType) && !(notifType === IDLE_PROMPT_NOTIFICATION && isWaitingOnBackgroundWork(sessionKey));
|
|
1729
1961
|
var handleNotification = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
1730
1962
|
const signal = AbortSignal.timeout(HOOK_BUDGET_MS);
|
|
1731
1963
|
try {
|
|
@@ -1757,7 +1989,7 @@ var handleNotification = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
|
1757
1989
|
// and all three have to fit inside Claude Code's 10s Notification budget.
|
|
1758
1990
|
}, { maxAttempts: 1, timeoutMs: 2500, signal }).catch(() => {
|
|
1759
1991
|
});
|
|
1760
|
-
if (!teammateIdle &&
|
|
1992
|
+
if (!teammateIdle && pushesNotification(notifType, sessionKey)) {
|
|
1761
1993
|
if (claimThrottle(`notify:${sessionKey}:${notifType}`, NOTIFICATION_THROTTLE_MS) !== "passed") return;
|
|
1762
1994
|
let apiKey;
|
|
1763
1995
|
try {
|
|
@@ -1856,6 +2088,16 @@ export {
|
|
|
1856
2088
|
toPolicyLookups,
|
|
1857
2089
|
permissionTimeoutDecision,
|
|
1858
2090
|
preToolUseTimeoutDecision,
|
|
2091
|
+
userScope,
|
|
2092
|
+
codexCallKey,
|
|
2093
|
+
recordCodexCallDecision,
|
|
2094
|
+
consumeCodexCallDecision,
|
|
2095
|
+
blockedActionFingerprint,
|
|
2096
|
+
recordBlockedActionHint,
|
|
2097
|
+
hasBlockedActionHint,
|
|
2098
|
+
approvalAwaitingRetry,
|
|
2099
|
+
markApprovalReminded,
|
|
2100
|
+
forgetBlockedActionHint,
|
|
1859
2101
|
readLastPrompt,
|
|
1860
2102
|
CLAUDE_CODE_AGENT,
|
|
1861
2103
|
AUTO_DECISION_WINDOW_MS,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
registerTranscript
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6TK4IY3A.js";
|
|
4
4
|
import {
|
|
5
5
|
writeDegraded
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-KB2FTO4O.js";
|
|
7
7
|
|
|
8
8
|
// src/tool-hook-input.ts
|
|
9
9
|
var OPTIONAL_TEXT_FIELDS = ["hook_event_name", "session_id", "cwd", "transcript_path", "tool_use_id", "permission_mode"];
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AGENT_CHOICES
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DLEKCAQL.js";
|
|
4
4
|
import {
|
|
5
5
|
isBridgeLocator
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-MYHG7PX2.js";
|
|
7
7
|
import {
|
|
8
8
|
pusharyHookExecutable
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-OCVVSRDA.js";
|
|
10
10
|
import {
|
|
11
11
|
BELL_HOOK_BINARY
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-HOEB6AKG.js";
|
|
13
13
|
|
|
14
14
|
// src/setup/hook-ownership.ts
|
|
15
15
|
var commandsIn = (value) => {
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
2
|
detectClaudeVersion
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ASWRMBOM.js";
|
|
4
4
|
import {
|
|
5
5
|
ensureCodexTrustsPusharyHooks
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-W2LJ4O3A.js";
|
|
7
7
|
import {
|
|
8
8
|
addClaudeMcpServer,
|
|
9
9
|
addPusharyHooks,
|
|
10
10
|
addPusharyToolPermissions
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-JRQRNNGA.js";
|
|
12
12
|
import {
|
|
13
13
|
claudeUnprovenHolds,
|
|
14
14
|
claudeWired,
|
|
15
15
|
codexUnprovenHolds,
|
|
16
16
|
codexWired,
|
|
17
17
|
geminiWired
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-OCVVSRDA.js";
|
|
19
19
|
import {
|
|
20
20
|
CLAUDE_HOOK_BINARY,
|
|
21
21
|
CLAUDE_HOOK_EVENTS,
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
needsReregistration,
|
|
28
28
|
registrationSignatures,
|
|
29
29
|
supportedClaudeEvents
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-HTEGMMT3.js";
|
|
31
31
|
import {
|
|
32
32
|
CODEX_HOOK_BINARY,
|
|
33
33
|
addCodexHookTrust,
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
registrationHolderOf,
|
|
48
48
|
resolveGlobalBinary,
|
|
49
49
|
vscodePluginMcp
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-UPLV5AJF.js";
|
|
51
51
|
import {
|
|
52
52
|
readJsonSafe,
|
|
53
53
|
writeFileAtomic,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/diagnostics/service-runtime.ts
|
|
2
|
+
var unescapeXml = (value) => value.replaceAll(""", '"').replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">").replaceAll("&", "&");
|
|
3
|
+
var unquoteSystemd = (value) => value.replaceAll('\\"', '"').replaceAll("\\\\", "\\").replaceAll("%%", "%");
|
|
4
|
+
var readServiceExecPath = (contents, platform) => {
|
|
5
|
+
if (platform === "darwin") {
|
|
6
|
+
const program = contents.match(/<key>ProgramArguments<\/key>\s*<array>\s*<string>([^<]*)<\/string>/)?.[1];
|
|
7
|
+
return program ? unescapeXml(program) : null;
|
|
8
|
+
}
|
|
9
|
+
if (platform === "linux") {
|
|
10
|
+
const exec = contents.match(/^ExecStart="((?:[^"\\]|\\.)*)"/m)?.[1];
|
|
11
|
+
return exec ? unquoteSystemd(exec) : null;
|
|
12
|
+
}
|
|
13
|
+
if (platform === "win32") {
|
|
14
|
+
const command = contents.match(/<Command>([^<]*)<\/Command>/)?.[1];
|
|
15
|
+
return command ? unescapeXml(command) : null;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
};
|
|
19
|
+
var MANAGER_SEGMENTS = [
|
|
20
|
+
[".nvm", "nvm"],
|
|
21
|
+
["nvm", "nvm"],
|
|
22
|
+
["fnm", "fnm"],
|
|
23
|
+
[".fnm", "fnm"],
|
|
24
|
+
[".volta", "volta"],
|
|
25
|
+
["volta", "volta"],
|
|
26
|
+
[".asdf", "asdf"],
|
|
27
|
+
["asdf", "asdf"]
|
|
28
|
+
];
|
|
29
|
+
var versionManagerOf = (execPath) => {
|
|
30
|
+
const segments = execPath.split(/[/\\]/);
|
|
31
|
+
for (const segment of segments) {
|
|
32
|
+
const hit = MANAGER_SEGMENTS.find(([name]) => name === segment);
|
|
33
|
+
if (hit) return hit[1];
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
var describeVersionManagerPin = (execPath, manager) => `The background service is pinned to ${execPath}, which ${manager} manages. Switching Node versions strands it; run pushary daemon install afterwards.`;
|
|
38
|
+
|
|
39
|
+
export {
|
|
40
|
+
readServiceExecPath,
|
|
41
|
+
versionManagerOf,
|
|
42
|
+
describeVersionManagerPin
|
|
43
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
pusharyHookExecutable
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OCVVSRDA.js";
|
|
4
4
|
import {
|
|
5
5
|
isProtectedByScope,
|
|
6
6
|
registerHookEntry
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UPLV5AJF.js";
|
|
8
8
|
|
|
9
9
|
// src/skills-cli.ts
|
|
10
10
|
import { execSync } from "child_process";
|
|
@@ -79,7 +79,7 @@ var describeService = (effects) => {
|
|
|
79
79
|
return `write ${where} and register it with the OS${condition}`;
|
|
80
80
|
};
|
|
81
81
|
var describeSetupSideEffects = (effects) => [
|
|
82
|
-
`run npm install -g
|
|
82
|
+
`run npm install -g ${effects.globalPackage}@${effects.version} (the hooks run from the global copy; up to two minutes)`,
|
|
83
83
|
`write ${effects.configFile} (the API key, mode 600)`,
|
|
84
84
|
effects.rcPath ? `edit ${effects.rcPath} (export PUSHARY_API_KEY)` : "print the export line for your shell, since no rc file was found",
|
|
85
85
|
describeService(effects),
|