@pushary/agent-hooks 0.95.8 → 0.97.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 +17 -0
- package/dist/bin/pushary-bell-hook.js +19 -21
- package/dist/bin/pushary-bell.js +6 -8
- package/dist/bin/pushary-claude.js +21 -23
- package/dist/bin/pushary-clean.js +19 -22
- package/dist/bin/pushary-codex-bridge.js +13 -14
- package/dist/bin/pushary-codex-hook.js +452 -104
- package/dist/bin/pushary-codex.js +8 -8
- package/dist/bin/pushary-connect.js +6 -6
- package/dist/bin/pushary-cowork.js +3 -3
- package/dist/bin/pushary-daemon.js +25 -27
- package/dist/bin/pushary-disconnect.js +6 -8
- package/dist/bin/pushary-doctor.js +28 -23
- package/dist/bin/pushary-elicitation-hook.js +19 -21
- package/dist/bin/pushary-gemini-bridge.js +11 -12
- package/dist/bin/pushary-gemini-hook.js +22 -24
- package/dist/bin/pushary-hook.js +24 -26
- package/dist/bin/pushary-login.js +7 -8
- package/dist/bin/pushary-logout.js +4 -4
- package/dist/bin/pushary-mode.js +3 -3
- package/dist/bin/pushary-notification-hook.js +21 -23
- package/dist/bin/pushary-opencode-hook.js +22 -24
- package/dist/bin/pushary-permission-denied-hook.js +28 -27
- package/dist/bin/pushary-permission-hook.js +24 -26
- package/dist/bin/pushary-post-hook.js +21 -23
- package/dist/bin/pushary-prompt-hook.js +21 -23
- package/dist/bin/pushary-session-end-hook.js +21 -23
- package/dist/bin/pushary-session-start-hook.js +24 -26
- package/dist/bin/pushary-setup.js +34 -38
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +10 -11
- package/dist/bin/pushary-stop-hook.js +21 -23
- package/dist/bin/pushary-stopfailure-hook.js +21 -23
- package/dist/bin/pushary-transcript-register.js +17 -19
- package/dist/bin/pushary-transcripts.js +5 -5
- package/dist/bin/pushary-upgrade.js +17 -20
- package/dist/bin/pushary-wait.js +3 -3
- package/dist/{chunk-RE25RJ5A.js → chunk-3SNTEHOH.js} +4 -4
- package/dist/{chunk-R3OZ5ISU.js → chunk-4AXWBCCO.js} +4 -4
- package/dist/{chunk-XEVOPOKT.js → chunk-5ODZEFP3.js} +1 -1
- package/dist/{chunk-NOVA7KZ2.js → chunk-67RNDRKL.js} +260 -32
- package/dist/{chunk-3YFJGKJQ.js → chunk-6VXZKOBB.js} +8 -8
- package/dist/{chunk-ATKGKAP6.js → chunk-6WJKYD3Q.js} +8 -8
- package/dist/{chunk-EMGGUO2Q.js → chunk-A4AIISZZ.js} +1 -1
- package/dist/{chunk-N6FAXQDD.js → chunk-AXSDMDJI.js} +1 -1
- package/dist/{chunk-XO3JR3OP.js → chunk-B3557BDL.js} +2 -2
- package/dist/{chunk-QJPYJVL3.js → chunk-BYH75I7Y.js} +1 -1
- package/dist/{chunk-VERXHIQF.js → chunk-C2FYSFTQ.js} +1 -1
- package/dist/{chunk-SKPXQRKN.js → chunk-CDSA3NTB.js} +1 -1
- package/dist/{chunk-HUGIKB5O.js → chunk-DGUMR5LZ.js} +6 -6
- package/dist/{chunk-54W7ELDQ.js → chunk-DNLFSRCF.js} +2 -2
- package/dist/{chunk-R6LBGKWQ.js → chunk-DOKFLZWL.js} +10 -14
- package/dist/{chunk-U5RIXR7I.js → chunk-EHQC3GWL.js} +1 -1
- package/dist/{chunk-QD73WATQ.js → chunk-FU5SQKAY.js} +15 -4
- package/dist/{chunk-TY6IJY6I.js → chunk-GGYH6LIE.js} +1 -1
- package/dist/{chunk-XZWUOCJZ.js → chunk-H4VNAOCG.js} +1 -1
- package/dist/{chunk-MC5WUOJJ.js → chunk-HUZW6SEO.js} +75 -1
- package/dist/{chunk-QV266EGS.js → chunk-IBRX7LRE.js} +8 -10
- package/dist/{chunk-UKMLSQXA.js → chunk-ICUBJNFL.js} +2 -2
- package/dist/{chunk-E47WV7BJ.js → chunk-IZKCA35M.js} +2 -2
- package/dist/{chunk-F4TMY6XI.js → chunk-K7MLXXPY.js} +2 -2
- package/dist/{chunk-Y6SCMXR6.js → chunk-KDPYXMUI.js} +2 -2
- package/dist/{chunk-XS33QBJA.js → chunk-LGXUGYUH.js} +50 -1
- package/dist/{chunk-I7LKI4WO.js → chunk-LYWTLNMG.js} +6 -8
- package/dist/chunk-OYTOLQ3N.js +15 -0
- package/dist/{chunk-XUTSZYYJ.js → chunk-QFAZ6QCT.js} +1 -4
- package/dist/{chunk-FRXZMCVA.js → chunk-ROTIPRM4.js} +1 -1
- package/dist/{chunk-BQL7UDIB.js → chunk-SW37HB27.js} +1 -1
- package/dist/{chunk-Z4JVEL7R.js → chunk-VCKQ45QA.js} +191 -2
- package/dist/{chunk-SDNTRS76.js → chunk-VDTC5DEM.js} +1 -1
- package/dist/{chunk-VMQU7YEJ.js → chunk-VF7YOT2W.js} +1 -1
- package/dist/{chunk-5GKNAQTG.js → chunk-VOC55PQV.js} +1 -1
- package/dist/{chunk-L7NOG547.js → chunk-VTUN7WSG.js} +1 -1
- package/dist/{chunk-GOPIMNVC.js → chunk-WIKEADOS.js} +1 -1
- package/dist/chunk-XDMHLQMH.js +202 -0
- package/dist/{chunk-4DUEIUUO.js → chunk-YRXVC4HL.js} +1 -1
- package/dist/{chunk-A22VQ5ZL.js → chunk-YX3U2CLP.js} +111 -55
- package/dist/src/index.js +11 -11
- package/package.json +1 -1
- package/dist/chunk-3AJIK5AO.js +0 -57
- package/dist/chunk-L4ZCFMWW.js +0 -177
- package/dist/chunk-VDI645ZX.js +0 -18
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
blockedActionFingerprint,
|
|
3
|
+
defersToNativeMode,
|
|
4
|
+
forgetBlockedActionHint,
|
|
5
|
+
hasBlockedActionHint,
|
|
6
|
+
recordBlockedActionHint
|
|
7
|
+
} from "./chunk-XDMHLQMH.js";
|
|
4
8
|
import {
|
|
5
9
|
isGatingMoment,
|
|
6
10
|
recordKeylessMoment
|
|
7
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-VOC55PQV.js";
|
|
8
12
|
import {
|
|
9
13
|
denyReasonFrom,
|
|
10
14
|
isDeferAnswer
|
|
@@ -20,7 +24,7 @@ import {
|
|
|
20
24
|
reportEvent,
|
|
21
25
|
settleQuestionsForToolUse,
|
|
22
26
|
throttlePass
|
|
23
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-DGUMR5LZ.js";
|
|
24
28
|
import {
|
|
25
29
|
UNNAMED_ACTION,
|
|
26
30
|
deriveAction,
|
|
@@ -30,37 +34,46 @@ import {
|
|
|
30
34
|
describeToolCall,
|
|
31
35
|
permissionDeniedAction,
|
|
32
36
|
scopePathFor
|
|
33
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-VCKQ45QA.js";
|
|
34
38
|
import {
|
|
35
39
|
DECISION_STOP_REASON,
|
|
36
40
|
askUser,
|
|
41
|
+
cancelQuestion,
|
|
42
|
+
claimBlockedAction,
|
|
37
43
|
isDecisionStopped,
|
|
38
44
|
isLocalQuestionHandoff,
|
|
45
|
+
pollForAnswer,
|
|
46
|
+
requestGate,
|
|
39
47
|
sendNotification,
|
|
40
48
|
waitForQuestion
|
|
41
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-LGXUGYUH.js";
|
|
42
50
|
import {
|
|
43
51
|
getMachineId
|
|
44
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-IZKCA35M.js";
|
|
45
53
|
import {
|
|
46
54
|
getApiKey,
|
|
47
55
|
getBaseUrl
|
|
48
56
|
} from "./chunk-2UMNXADU.js";
|
|
49
57
|
import {
|
|
58
|
+
GATE_BLOCK_REASON_MAX_LENGTH,
|
|
59
|
+
GATE_WIRE,
|
|
50
60
|
buildDecisionEpisodeFeatures,
|
|
61
|
+
claudePermissionDeniedOutput,
|
|
51
62
|
claudePermissionRequestOutput,
|
|
52
63
|
claudePreToolUseOutput,
|
|
53
64
|
effectiveWaitSeconds,
|
|
65
|
+
hookHoldWindowMs,
|
|
54
66
|
hookWaitClamped,
|
|
55
67
|
hookWaitDeadline,
|
|
56
68
|
hookWritePath,
|
|
69
|
+
isBlockedActionRequest,
|
|
57
70
|
isPermissionRequestOwnedByPreToolUse,
|
|
58
71
|
isPusharyOwnTool,
|
|
59
72
|
parseAgentQuestionAnswers,
|
|
60
73
|
parseAgentQuestions,
|
|
61
74
|
resolveGate,
|
|
62
75
|
stoppedOutput
|
|
63
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-HUZW6SEO.js";
|
|
64
77
|
|
|
65
78
|
// src/decision-episode.ts
|
|
66
79
|
var EPISODE_PATH = "/api/agent/decision-episode";
|
|
@@ -89,7 +102,7 @@ import { basename, join } from "path";
|
|
|
89
102
|
import { tmpdir } from "os";
|
|
90
103
|
import { existsSync, writeFileSync } from "fs";
|
|
91
104
|
var START_MS = Date.now();
|
|
92
|
-
var allow = () => claudePreToolUseOutput("allow");
|
|
105
|
+
var allow = (reason) => claudePreToolUseOutput("allow", reason);
|
|
93
106
|
var deny = (reason) => claudePreToolUseOutput("deny", reason);
|
|
94
107
|
var allowWithInput = (updatedInput) => claudePreToolUseOutput("allow", void 0, updatedInput);
|
|
95
108
|
var ask = (reason) => claudePreToolUseOutput("ask", reason);
|
|
@@ -449,6 +462,45 @@ var claudeGateInput = (modeState, config, input) => {
|
|
|
449
462
|
sessionId: input.session_id
|
|
450
463
|
};
|
|
451
464
|
};
|
|
465
|
+
var BLOCKED_ACTION_GATE_TIMEOUT_MS = 15e3;
|
|
466
|
+
var BLOCKED_ACTION_CLAIM_TIMEOUT_MS = 4e3;
|
|
467
|
+
var BLOCKED_ACTION_ALLOW_REASON = "Approved from Pushary after auto mode blocked it";
|
|
468
|
+
var blockReasonOf = (reason) => typeof reason === "string" ? reason.trim().slice(0, GATE_BLOCK_REASON_MAX_LENGTH).trim() || void 0 : void 0;
|
|
469
|
+
var blockedActionFingerprintFor = (input) => blockedActionFingerprint({
|
|
470
|
+
sessionId: input.session_id,
|
|
471
|
+
toolName: input.tool_name,
|
|
472
|
+
toolInput: input.tool_input,
|
|
473
|
+
cwd: input.cwd
|
|
474
|
+
});
|
|
475
|
+
var blockedActionGateRequest = (input, event) => ({
|
|
476
|
+
v: GATE_WIRE,
|
|
477
|
+
source: "claude",
|
|
478
|
+
event,
|
|
479
|
+
toolName: input.tool_name,
|
|
480
|
+
toolInputs: [input.tool_input ?? {}],
|
|
481
|
+
sessionId: input.session_id,
|
|
482
|
+
toolUseId: input.tool_use_id,
|
|
483
|
+
machineId: getMachineId(),
|
|
484
|
+
cwd: input.cwd,
|
|
485
|
+
repoKey: repoKeyFor(input.cwd),
|
|
486
|
+
permissionMode: input.permission_mode,
|
|
487
|
+
...event === "PermissionDenied" ? { createDecision: true, localSurfaceAvailable: false, blockReason: blockReasonOf(input.reason) } : {}
|
|
488
|
+
});
|
|
489
|
+
var claimedBlockedActionRetry = async (apiKey, input) => {
|
|
490
|
+
const fingerprint = blockedActionFingerprintFor(input);
|
|
491
|
+
if (!fingerprint || !hasBlockedActionHint(fingerprint)) return false;
|
|
492
|
+
try {
|
|
493
|
+
const granted = await claimBlockedAction(
|
|
494
|
+
apiKey,
|
|
495
|
+
blockedActionGateRequest(input, "PreToolUse"),
|
|
496
|
+
BLOCKED_ACTION_CLAIM_TIMEOUT_MS
|
|
497
|
+
);
|
|
498
|
+
if (granted !== null) forgetBlockedActionHint(fingerprint);
|
|
499
|
+
return granted === true;
|
|
500
|
+
} catch {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
};
|
|
452
504
|
var handlePreToolUse = async (input) => {
|
|
453
505
|
if (isPusharyOwnTool(input.tool_name)) return void 0;
|
|
454
506
|
let apiKey;
|
|
@@ -469,6 +521,7 @@ var handlePreToolUse = async (input) => {
|
|
|
469
521
|
if (input.tool_name === "ExitPlanMode") {
|
|
470
522
|
return handleExitPlanMode(apiKey, input);
|
|
471
523
|
}
|
|
524
|
+
if (await claimedBlockedActionRetry(apiKey, input)) return allow(BLOCKED_ACTION_ALLOW_REASON);
|
|
472
525
|
if (shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
|
|
473
526
|
return void 0;
|
|
474
527
|
}
|
|
@@ -531,12 +584,6 @@ var handlePermissionRequest = async (input) => {
|
|
|
531
584
|
return void 0;
|
|
532
585
|
}
|
|
533
586
|
};
|
|
534
|
-
var PERMISSION_DENIED_WINDOW_MS = 5 * 60 * 1e3;
|
|
535
|
-
var PERMISSION_DENIED_WAIT_SECONDS = 25;
|
|
536
|
-
var automodeRetryEnabled = () => {
|
|
537
|
-
const flag = process.env.PUSHARY_AUTOMODE_RETRY;
|
|
538
|
-
return flag === "1" || flag === "true";
|
|
539
|
-
};
|
|
540
587
|
var reportPermissionDenied = (input) => reportEvent({
|
|
541
588
|
event: "permission_denied",
|
|
542
589
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -548,9 +595,48 @@ var reportPermissionDenied = (input) => reportEvent({
|
|
|
548
595
|
// Side report, not a command carrier. Without timeoutMs this takes the phone's
|
|
549
596
|
// queued instruction and acknowledges it delivered, and this hook can print
|
|
550
597
|
// only {retry}. 4s rather than the Notification path's 2.5s because
|
|
551
|
-
// PermissionDenied
|
|
598
|
+
// PermissionDenied holds the agent for the full hook budget, not 10s.
|
|
552
599
|
}, { timeoutMs: 4e3 }).catch(() => void 0);
|
|
553
|
-
var
|
|
600
|
+
var isAffirmativeAnswer = (answer) => answer.answered && !isDecisionStopped(answer) && answer.value === "yes";
|
|
601
|
+
var PERMISSION_DENIED_NOTICE_WINDOW_MS = 5 * 60 * 1e3;
|
|
602
|
+
var blockedActionHoldEndsAt = (holdBudgetSeconds) => START_MS + hookHoldWindowMs("claude", holdBudgetSeconds);
|
|
603
|
+
var notifyPermissionDenied = async (apiKey, input) => {
|
|
604
|
+
const sessionKey = input.session_id || DEFAULT_SESSION;
|
|
605
|
+
if (!throttlePass(`permdenied:${sessionKey}:${input.tool_name}`, PERMISSION_DENIED_NOTICE_WINDOW_MS)) return;
|
|
606
|
+
const mode = await fetchModeState(apiKey, input.session_id).catch(() => null);
|
|
607
|
+
if (mode?.kill || mode?.mode === "terminal_only") return;
|
|
608
|
+
const projectName = basename(input.cwd ?? process.cwd());
|
|
609
|
+
await sendNotification(apiKey, {
|
|
610
|
+
title: `Auto mode blocked ${input.tool_name}`,
|
|
611
|
+
body: `Your agent was denied ${input.tool_name} in ${projectName}.`,
|
|
612
|
+
agentName: `Claude Code - ${projectName}`,
|
|
613
|
+
sessionId: input.session_id
|
|
614
|
+
}, 4e3).catch(() => void 0);
|
|
615
|
+
};
|
|
616
|
+
var isRetryOutput = (output) => {
|
|
617
|
+
if (!output) return false;
|
|
618
|
+
try {
|
|
619
|
+
const parsed = JSON.parse(output);
|
|
620
|
+
return parsed?.hookSpecificOutput?.hookEventName === "PermissionDenied" && parsed.hookSpecificOutput.retry === true;
|
|
621
|
+
} catch {
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
var retryOnce = (fingerprint) => recordBlockedActionHint(fingerprint) ? claudePermissionDeniedOutput("allow") : void 0;
|
|
626
|
+
var askAboutBlockedAction = async (apiKey, input, fingerprint, holdEndsAt) => {
|
|
627
|
+
const reply = await requestGate(apiKey, blockedActionGateRequest(input, "PermissionDenied"), BLOCKED_ACTION_GATE_TIMEOUT_MS);
|
|
628
|
+
if (!reply) return void 0;
|
|
629
|
+
if (reply.kind === "no_opinion") return isRetryOutput(reply.output) ? retryOnce(fingerprint) : void 0;
|
|
630
|
+
if (reply.kind !== "ask" || !reply.correlationId || reply.deadlineSeconds === void 0) return void 0;
|
|
631
|
+
if (reply.noDevices) {
|
|
632
|
+
await cancelQuestion(apiKey, reply.correlationId, AbortSignal.timeout(4e3)).catch(() => void 0);
|
|
633
|
+
return void 0;
|
|
634
|
+
}
|
|
635
|
+
const deadline = Math.min(hookWaitDeadline(START_MS, reply.deadlineSeconds, "claude", Date.now()), holdEndsAt);
|
|
636
|
+
const answer = await pollForAnswer(apiKey, reply.correlationId, deadline);
|
|
637
|
+
return isAffirmativeAnswer(answer) ? retryOnce(fingerprint) : void 0;
|
|
638
|
+
};
|
|
639
|
+
var handlePermissionDenied = async (input, holdBudgetSeconds) => {
|
|
554
640
|
await reportPermissionDenied(input);
|
|
555
641
|
if (isPusharyOwnTool(input.tool_name)) return void 0;
|
|
556
642
|
let apiKey;
|
|
@@ -560,46 +646,16 @@ var handlePermissionDenied = async (input) => {
|
|
|
560
646
|
return void 0;
|
|
561
647
|
}
|
|
562
648
|
try {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (!
|
|
567
|
-
const
|
|
568
|
-
if (
|
|
569
|
-
|
|
570
|
-
await sendNotification(apiKey, {
|
|
571
|
-
title: `Auto-mode blocked ${input.tool_name}`,
|
|
572
|
-
body: `Your agent was denied ${input.tool_name} in ${projectName}.`,
|
|
573
|
-
agentName: `Claude Code - ${projectName}`,
|
|
574
|
-
sessionId: input.session_id
|
|
575
|
-
}, 4e3).catch(() => {
|
|
576
|
-
});
|
|
649
|
+
await settleQuestionsForToolUse(input.session_id || DEFAULT_SESSION, input.tool_use_id);
|
|
650
|
+
if (!isBlockedActionRequest({ source: "claude", event: "PermissionDenied", toolName: input.tool_name })) return void 0;
|
|
651
|
+
const fingerprint = blockedActionFingerprintFor(input);
|
|
652
|
+
if (!fingerprint) return void 0;
|
|
653
|
+
const holdEndsAt = blockedActionHoldEndsAt(holdBudgetSeconds);
|
|
654
|
+
if (holdEndsAt - Date.now() <= BLOCKED_ACTION_GATE_TIMEOUT_MS) {
|
|
655
|
+
await notifyPermissionDenied(apiKey, input);
|
|
577
656
|
return void 0;
|
|
578
657
|
}
|
|
579
|
-
|
|
580
|
-
try {
|
|
581
|
-
result = await askUser(apiKey, {
|
|
582
|
-
question: `Auto-mode blocked ${input.tool_name} in ${projectName}. Let the agent retry it?`,
|
|
583
|
-
type: "confirm",
|
|
584
|
-
context: "Your agent hit a permission wall in auto mode",
|
|
585
|
-
agentName: `Claude Code - ${projectName}`,
|
|
586
|
-
sessionId: input.session_id,
|
|
587
|
-
machineId: getMachineId(),
|
|
588
|
-
toolName: input.tool_name,
|
|
589
|
-
// Suffixed because the PreToolUse gate may already have asked about this
|
|
590
|
-
// same tool call. Without it, the retry prompt would collide with that
|
|
591
|
-
// decision and hand back its answer instead of asking a new question.
|
|
592
|
-
toolUseId: input.tool_use_id ? `${input.tool_use_id}#retry` : void 0
|
|
593
|
-
});
|
|
594
|
-
} catch {
|
|
595
|
-
return void 0;
|
|
596
|
-
}
|
|
597
|
-
const deadline = hookWaitDeadline(START_MS, PERMISSION_DENIED_WAIT_SECONDS, "claude", Date.now());
|
|
598
|
-
const answer = await waitForQuestion(apiKey, result, deadline);
|
|
599
|
-
if (answer.answered && answer.value === "yes") {
|
|
600
|
-
return { hookSpecificOutput: { hookEventName: "PermissionDenied", retry: true } };
|
|
601
|
-
}
|
|
602
|
-
return void 0;
|
|
658
|
+
return await askAboutBlockedAction(apiKey, input, fingerprint, holdEndsAt);
|
|
603
659
|
} catch {
|
|
604
660
|
return void 0;
|
|
605
661
|
}
|
package/dist/src/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handlePreToolUse
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-YX3U2CLP.js";
|
|
4
|
+
import "../chunk-XDMHLQMH.js";
|
|
5
|
+
import "../chunk-VOC55PQV.js";
|
|
6
6
|
import "../chunk-YHG74UFF.js";
|
|
7
7
|
import {
|
|
8
8
|
fetchAccountPublicKey
|
|
@@ -15,28 +15,28 @@ import {
|
|
|
15
15
|
handlePostToolUse,
|
|
16
16
|
handleStop,
|
|
17
17
|
reportEvent
|
|
18
|
-
} from "../chunk-
|
|
19
|
-
import "../chunk-
|
|
20
|
-
import "../chunk-
|
|
18
|
+
} from "../chunk-DGUMR5LZ.js";
|
|
19
|
+
import "../chunk-VCKQ45QA.js";
|
|
20
|
+
import "../chunk-QFAZ6QCT.js";
|
|
21
21
|
import {
|
|
22
22
|
askUser,
|
|
23
23
|
cancelQuestion,
|
|
24
24
|
waitForAnswer
|
|
25
|
-
} from "../chunk-
|
|
26
|
-
import "../chunk-
|
|
27
|
-
import "../chunk-
|
|
25
|
+
} from "../chunk-LGXUGYUH.js";
|
|
26
|
+
import "../chunk-IZKCA35M.js";
|
|
27
|
+
import "../chunk-67RNDRKL.js";
|
|
28
28
|
import "../chunk-EYRGBYZG.js";
|
|
29
29
|
import "../chunk-SAF6HGAA.js";
|
|
30
30
|
import "../chunk-7QLSKOSU.js";
|
|
31
31
|
import "../chunk-KZERVKTD.js";
|
|
32
|
-
import "../chunk-
|
|
32
|
+
import "../chunk-VTUN7WSG.js";
|
|
33
33
|
import {
|
|
34
34
|
getApiKey,
|
|
35
35
|
getBaseUrl
|
|
36
36
|
} from "../chunk-2UMNXADU.js";
|
|
37
37
|
import {
|
|
38
38
|
resolvePolicy
|
|
39
|
-
} from "../chunk-
|
|
39
|
+
} from "../chunk-HUZW6SEO.js";
|
|
40
40
|
export {
|
|
41
41
|
askUser,
|
|
42
42
|
cancelQuestion,
|
package/package.json
CHANGED
package/dist/chunk-3AJIK5AO.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
quoteIfNeeded
|
|
3
|
-
} from "./chunk-L4ZCFMWW.js";
|
|
4
|
-
|
|
5
|
-
// src/hook-command.ts
|
|
6
|
-
var HOOK_SHELL = "/bin/sh -c";
|
|
7
|
-
var LOCATE = 'B=$(command -v "$0") || exit 0; [ -f "$B" ] || exit 0; [ -x "$B" ] || exit 0';
|
|
8
|
-
var NODE_PRESENT = "command -v node >/dev/null 2>&1 || exit 0";
|
|
9
|
-
var CURSOR_NO_OPINION = { permission: "deny", user_message: "Command denied via Pushary.", agent_message: "Pushary hook is unavailable. Repair the integration before retrying." };
|
|
10
|
-
var shellDoubleQuoted = (text) => `"${text.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
11
|
-
var CURSOR_NATIVE_PROMPT = { permission: "ask" };
|
|
12
|
-
var CURSOR_NATIVE_PROMPT_EVENTS = ["beforeShellExecution", "beforeMCPExecution"];
|
|
13
|
-
var hookEventIsOneOf = (events) => events.flatMap((event) => [`*\\"hook_event_name\\":\\"${event}\\"*`, `*\\"hook_event_name\\":\\ \\"${event}\\"*`]).join("|");
|
|
14
|
-
var CURSOR_MISS = `{ E=$(cat); case "$E" in ${hookEventIsOneOf(CURSOR_NATIVE_PROMPT_EVENTS)}) printf %s ${shellDoubleQuoted(JSON.stringify(CURSOR_NATIVE_PROMPT))} ;; *) printf %s ${shellDoubleQuoted(JSON.stringify(CURSOR_NO_OPINION))} ;; esac; exit 0; }`;
|
|
15
|
-
var HOOK_GUARD_SCRIPTS = {
|
|
16
|
-
native_binary: `${LOCATE}; exec "$B" "$@"`,
|
|
17
|
-
node_binary: `${LOCATE}; ${NODE_PRESENT}; exec "$B" "$@"`,
|
|
18
|
-
node_script: `[ -f "$0" ] || exit 0; ${NODE_PRESENT}; exec node "$0" "$@"`,
|
|
19
|
-
cursor_gate_script: `[ -f "$0" ] || ${CURSOR_MISS}; command -v node >/dev/null 2>&1 || ${CURSOR_MISS}; exec node "$0" "$@"`
|
|
20
|
-
};
|
|
21
|
-
var BINARY_GUARDS = {
|
|
22
|
-
native: "native_binary",
|
|
23
|
-
node: "node_binary"
|
|
24
|
-
};
|
|
25
|
-
var guardKindOf = (target) => {
|
|
26
|
-
switch (target.kind) {
|
|
27
|
-
case "node":
|
|
28
|
-
return "node_script";
|
|
29
|
-
case "cursor_gate":
|
|
30
|
-
return "cursor_gate_script";
|
|
31
|
-
case "binary":
|
|
32
|
-
return BINARY_GUARDS[target.runtime];
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
var targetPath = (target) => target.kind === "binary" ? target.path : target.script;
|
|
36
|
-
var runsThroughNode = (target) => target.kind !== "binary";
|
|
37
|
-
var guardHookCommand = (target, platform = process.platform) => {
|
|
38
|
-
const path = quoteIfNeeded(targetPath(target));
|
|
39
|
-
if (platform === "win32") return runsThroughNode(target) ? `node ${path}` : path;
|
|
40
|
-
return `${HOOK_SHELL} '${HOOK_GUARD_SCRIPTS[guardKindOf(target)]}' ${path}`;
|
|
41
|
-
};
|
|
42
|
-
var guardBinary = (path, platform) => guardHookCommand({ kind: "binary", path, runtime: "node" }, platform);
|
|
43
|
-
var guardCursorGate = (script, platform) => guardHookCommand({ kind: "cursor_gate", script }, platform);
|
|
44
|
-
var guardedArgv = (target, args = [], platform = process.platform) => {
|
|
45
|
-
const path = targetPath(target);
|
|
46
|
-
if (platform === "win32") {
|
|
47
|
-
return runsThroughNode(target) ? ["node", path, ...args] : [path, ...args];
|
|
48
|
-
}
|
|
49
|
-
const [shell, ...shellArgs] = HOOK_SHELL.split(/\s+/);
|
|
50
|
-
return [shell, ...shellArgs, HOOK_GUARD_SCRIPTS[guardKindOf(target)], path, ...args];
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export {
|
|
54
|
-
guardBinary,
|
|
55
|
-
guardCursorGate,
|
|
56
|
-
guardedArgv
|
|
57
|
-
};
|
package/dist/chunk-L4ZCFMWW.js
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
// src/npm.ts
|
|
2
|
-
import { execFileSync, execSync } from "child_process";
|
|
3
|
-
import { existsSync, readFileSync, realpathSync } from "fs";
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve } from "path";
|
|
5
|
-
var cleanNpmEnv = () => {
|
|
6
|
-
const env = {};
|
|
7
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
8
|
-
if (key.toLowerCase().startsWith("npm_config_workspace")) continue;
|
|
9
|
-
env[key] = value;
|
|
10
|
-
}
|
|
11
|
-
return env;
|
|
12
|
-
};
|
|
13
|
-
var npmErrorMessage = (err) => {
|
|
14
|
-
const e = err;
|
|
15
|
-
const text = [e?.stderr, e?.stdout].map((part) => part ? part.toString() : "").join("\n");
|
|
16
|
-
const line = text.split("\n").map((l) => l.replace(/^npm error\s*/i, "").trim()).find((l) => l && !l.startsWith("A complete log") && !/^code\s/i.test(l));
|
|
17
|
-
return line || e?.message || String(err);
|
|
18
|
-
};
|
|
19
|
-
var execNpm = (args, options = {}) => {
|
|
20
|
-
return execSync(`npm ${args}`, {
|
|
21
|
-
timeout: 12e4,
|
|
22
|
-
stdio: "pipe",
|
|
23
|
-
...options,
|
|
24
|
-
env: { ...cleanNpmEnv(), ...options.env ?? {} }
|
|
25
|
-
});
|
|
26
|
-
};
|
|
27
|
-
var resolveGlobalBinary = (name) => {
|
|
28
|
-
try {
|
|
29
|
-
const prefix = execNpm("prefix -g --no-workspaces", { timeout: 5e3 }).toString().trim();
|
|
30
|
-
const candidates = process.platform === "win32" ? [join(prefix, `${name}.cmd`), join(prefix, name)] : [join(prefix, "bin", name)];
|
|
31
|
-
return candidates.find(existsSync);
|
|
32
|
-
} catch {
|
|
33
|
-
return void 0;
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
var resolvePathBinary = (name, platform = process.platform) => {
|
|
37
|
-
try {
|
|
38
|
-
return execFileSync(platform === "win32" ? "where" : "which", [name], {
|
|
39
|
-
encoding: "utf8",
|
|
40
|
-
stdio: "pipe",
|
|
41
|
-
timeout: 5e3
|
|
42
|
-
}).split(/\r?\n/, 1)[0]?.trim() || void 0;
|
|
43
|
-
} catch {
|
|
44
|
-
return void 0;
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
var resolveBinary = (name) => resolveGlobalBinary(name) ?? resolvePathBinary(name);
|
|
48
|
-
var resolvePackageVersionFromBinary = (binary, packageName) => {
|
|
49
|
-
try {
|
|
50
|
-
let dir = dirname(realpathSync(binary));
|
|
51
|
-
const packagePath = packageName.split("/");
|
|
52
|
-
for (let depth = 0; depth < 8; depth++) {
|
|
53
|
-
for (const manifest of [
|
|
54
|
-
join(dir, "package.json"),
|
|
55
|
-
join(dir, "node_modules", ...packagePath, "package.json")
|
|
56
|
-
]) {
|
|
57
|
-
try {
|
|
58
|
-
const value = JSON.parse(readFileSync(manifest, "utf8"));
|
|
59
|
-
if (value.name === packageName && typeof value.version === "string") return value.version;
|
|
60
|
-
} catch {
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
const parent = dirname(dir);
|
|
64
|
-
if (parent === dir) break;
|
|
65
|
-
dir = parent;
|
|
66
|
-
}
|
|
67
|
-
} catch {
|
|
68
|
-
}
|
|
69
|
-
return void 0;
|
|
70
|
-
};
|
|
71
|
-
var resolveGlobalPackageVersion = (name) => {
|
|
72
|
-
try {
|
|
73
|
-
const result = JSON.parse(execNpm("list -g --depth=0 --json --no-workspaces", { timeout: 5e3 }).toString());
|
|
74
|
-
const value = result.dependencies?.[name]?.version;
|
|
75
|
-
return typeof value === "string" ? value : void 0;
|
|
76
|
-
} catch {
|
|
77
|
-
return void 0;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
var localImportsPresent = (entry, packageDir, seen = /* @__PURE__ */ new Set()) => {
|
|
81
|
-
if (seen.has(entry)) return true;
|
|
82
|
-
seen.add(entry);
|
|
83
|
-
try {
|
|
84
|
-
const source = readFileSync(entry, "utf8");
|
|
85
|
-
for (const match of source.matchAll(/\b(?:import|export)\s+(?:[^"'()]*?\s+from\s+)?["'](\.{1,2}\/[^"']+)["']|\bimport\s*\(\s*["'](\.{1,2}\/[^"']+)["']/g)) {
|
|
86
|
-
const dependency = resolve(dirname(entry), match[1] ?? match[2]);
|
|
87
|
-
const localPath = relative(packageDir, dependency);
|
|
88
|
-
if (localPath.startsWith("..") || isAbsolute(localPath) || !existsSync(dependency)) return false;
|
|
89
|
-
if (/\.[cm]?js$/.test(dependency) && !localImportsPresent(dependency, packageDir, seen)) return false;
|
|
90
|
-
}
|
|
91
|
-
return true;
|
|
92
|
-
} catch {
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
var packageBinsPresent = (packageDir, prefix, platform = process.platform, requiredFiles = []) => {
|
|
97
|
-
try {
|
|
98
|
-
const manifest = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8"));
|
|
99
|
-
if (!manifest.bin || typeof manifest.bin !== "object" || Array.isArray(manifest.bin)) return false;
|
|
100
|
-
const bins = Object.entries(manifest.bin);
|
|
101
|
-
if (bins.length === 0) return false;
|
|
102
|
-
if (!requiredFiles.every((path) => existsSync(join(packageDir, path)))) return false;
|
|
103
|
-
const seen = /* @__PURE__ */ new Set();
|
|
104
|
-
return bins.every(([name, target]) => {
|
|
105
|
-
if (typeof target !== "string") return false;
|
|
106
|
-
const shims = platform === "win32" ? [join(prefix, `${name}.cmd`), join(prefix, name)] : [join(prefix, "bin", name)];
|
|
107
|
-
const entry = join(packageDir, target);
|
|
108
|
-
return existsSync(entry) && shims.some(existsSync) && localImportsPresent(entry, packageDir, seen);
|
|
109
|
-
});
|
|
110
|
-
} catch {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
var AGENT_HOOKS_RUNTIME_FILES = [
|
|
115
|
-
"data/SKILL.md",
|
|
116
|
-
"data/cursor-plugin/.cursor-plugin/plugin.json",
|
|
117
|
-
"data/cursor-plugin/hooks/hooks.json",
|
|
118
|
-
"data/cursor-plugin/mcp.json",
|
|
119
|
-
"data/cursor-plugin/scripts/pushary-gate.mjs",
|
|
120
|
-
"data/vscode-plugin/.claude-plugin/plugin.json",
|
|
121
|
-
"data/vscode-plugin/hooks/hooks.json",
|
|
122
|
-
"data/vscode-plugin/.mcp.json",
|
|
123
|
-
"data/vscode-plugin/scripts/pushary-gate.mjs"
|
|
124
|
-
];
|
|
125
|
-
var globalPackageBinsPresent = (name) => {
|
|
126
|
-
try {
|
|
127
|
-
const root = execNpm("root -g --no-workspaces", { timeout: 5e3 }).toString().trim();
|
|
128
|
-
const prefix = execNpm("prefix -g --no-workspaces", { timeout: 5e3 }).toString().trim();
|
|
129
|
-
return packageBinsPresent(join(root, ...name.split("/")), prefix, process.platform, AGENT_HOOKS_RUNTIME_FILES);
|
|
130
|
-
} catch {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
var resolveGlobalBinDir = (probe) => {
|
|
135
|
-
if (process.platform === "win32") return void 0;
|
|
136
|
-
const resolved = resolveGlobalBinary(probe);
|
|
137
|
-
return resolved ? resolved.slice(0, resolved.length - probe.length - 1) : void 0;
|
|
138
|
-
};
|
|
139
|
-
var quoteIfNeeded = (path) => /\s/.test(path) ? `"${path}"` : path;
|
|
140
|
-
var needsGlobalInstall = (requested, installed, binaryPresent) => requested !== installed || !binaryPresent;
|
|
141
|
-
var installState;
|
|
142
|
-
var installGlobally = (options) => {
|
|
143
|
-
if (installState) {
|
|
144
|
-
return installState.then((result) => result === "installed" ? "cached" : result);
|
|
145
|
-
}
|
|
146
|
-
const started = (async () => {
|
|
147
|
-
const pinned = process.env.PUSHARY_PIN_GLOBAL_VERSION !== "0";
|
|
148
|
-
if (pinned && !needsGlobalInstall(
|
|
149
|
-
options.version,
|
|
150
|
-
resolveGlobalPackageVersion("@pushary/agent-hooks"),
|
|
151
|
-
globalPackageBinsPresent("@pushary/agent-hooks")
|
|
152
|
-
)) {
|
|
153
|
-
return "skipped";
|
|
154
|
-
}
|
|
155
|
-
const spec = pinned && options.version && options.version !== "0.0.0" ? `@pushary/agent-hooks@${options.version}` : "@pushary/agent-hooks@latest";
|
|
156
|
-
execNpm(`install -g --no-workspaces ${spec}`, { timeout: options.timeoutMs ?? 12e4 });
|
|
157
|
-
return "installed";
|
|
158
|
-
})();
|
|
159
|
-
installState = started;
|
|
160
|
-
started.catch(() => {
|
|
161
|
-
if (installState === started) installState = void 0;
|
|
162
|
-
});
|
|
163
|
-
return started;
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
export {
|
|
167
|
-
npmErrorMessage,
|
|
168
|
-
execNpm,
|
|
169
|
-
resolveGlobalBinary,
|
|
170
|
-
resolvePathBinary,
|
|
171
|
-
resolveBinary,
|
|
172
|
-
resolvePackageVersionFromBinary,
|
|
173
|
-
resolveGlobalPackageVersion,
|
|
174
|
-
resolveGlobalBinDir,
|
|
175
|
-
quoteIfNeeded,
|
|
176
|
-
installGlobally
|
|
177
|
-
};
|
package/dist/chunk-VDI645ZX.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
shouldDeferToNativeMode
|
|
3
|
-
} from "./chunk-MC5WUOJJ.js";
|
|
4
|
-
|
|
5
|
-
// src/native-defer.ts
|
|
6
|
-
var nativeDeferOptions = () => {
|
|
7
|
-
const respect = process.env.PUSHARY_RESPECT_PERMISSION_MODE;
|
|
8
|
-
const bypass = process.env.PUSHARY_DEFER_BYPASS;
|
|
9
|
-
return {
|
|
10
|
-
respectPermissionMode: !(respect === "0" || respect === "false"),
|
|
11
|
-
deferBypass: bypass === "1" || bypass === "true"
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
var defersToNativeMode = (permissionMode, toolName) => shouldDeferToNativeMode(permissionMode, toolName, nativeDeferOptions());
|
|
15
|
-
|
|
16
|
-
export {
|
|
17
|
-
defersToNativeMode
|
|
18
|
-
};
|