@pushary/agent-hooks 0.55.0 → 0.57.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/dist/bin/pushary-claude.js +5 -4
- package/dist/bin/pushary-clean.js +3 -3
- package/dist/bin/pushary-codex-hook.js +3 -3
- package/dist/bin/pushary-codex.js +2 -2
- package/dist/bin/pushary-doctor.js +3 -3
- package/dist/bin/pushary-gemini-hook.js +3 -3
- package/dist/bin/pushary-hook.js +5 -5
- package/dist/bin/pushary-notification-hook.js +2 -2
- package/dist/bin/pushary-permission-denied-hook.js +5 -5
- package/dist/bin/pushary-permission-hook.js +5 -5
- package/dist/bin/pushary-post-hook.js +2 -2
- package/dist/bin/pushary-prompt-hook.js +2 -2
- package/dist/bin/pushary-session-end-hook.js +2 -2
- package/dist/bin/pushary-session-start-hook.js +2 -2
- package/dist/bin/pushary-setup.js +4 -4
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-stop-hook.js +2 -2
- package/dist/bin/pushary-stopfailure-hook.js +2 -2
- package/dist/bin/pushary-upgrade.js +2 -2
- package/dist/{chunk-UXIAIMSS.js → chunk-5TECIV6T.js} +1 -1
- package/dist/{chunk-2VW2FEYC.js → chunk-GSERIC6Q.js} +1 -1
- package/dist/{chunk-H6RGO7DN.js → chunk-LEWSYLHL.js} +148 -41
- package/dist/{chunk-XKXF2G7T.js → chunk-NPYANTRC.js} +9 -8
- package/dist/{chunk-PSZAXNUB.js → chunk-QYQO6RHJ.js} +1 -1
- package/dist/{chunk-P5VOLS3Q.js → chunk-SYAAGYLZ.js} +42 -0
- package/dist/src/index.d.ts +3 -2
- package/dist/src/index.js +5 -5
- package/package.json +2 -2
|
@@ -13,17 +13,18 @@ import {
|
|
|
13
13
|
describeToolCall,
|
|
14
14
|
fetchModeState,
|
|
15
15
|
getPolicy,
|
|
16
|
+
repoKeyFor,
|
|
16
17
|
reportEvent,
|
|
17
18
|
resolvePolicy,
|
|
18
19
|
waitForAnswer
|
|
19
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-LEWSYLHL.js";
|
|
20
21
|
import {
|
|
21
22
|
getMachineId
|
|
22
23
|
} from "../chunk-RN3NOEJF.js";
|
|
23
24
|
import "../chunk-DWED7BS3.js";
|
|
24
25
|
import {
|
|
25
26
|
redactSecrets
|
|
26
|
-
} from "../chunk-
|
|
27
|
+
} from "../chunk-SYAAGYLZ.js";
|
|
27
28
|
import {
|
|
28
29
|
getApiKey,
|
|
29
30
|
getBaseUrl
|
|
@@ -528,8 +529,8 @@ var createRemoteApprover = (deps) => {
|
|
|
528
529
|
const sessionId = deps.getSessionId?.();
|
|
529
530
|
const modeState = await fetchModeState2(deps.apiKey, sessionId);
|
|
530
531
|
if (modeState.kill) return deny("Stopped by user \u2014 this agent was halted from Pushary");
|
|
531
|
-
const policyConfig = await getPolicy2(deps.apiKey, modeState.policyVersion, "claude_code");
|
|
532
|
-
const policy = resolvePolicy2(policyConfig, toolName, modeState.mode, input, deps.cwd);
|
|
532
|
+
const policyConfig = await getPolicy2(deps.apiKey, modeState.policyVersion, "claude_code", true);
|
|
533
|
+
const policy = resolvePolicy2(policyConfig, toolName, modeState.mode, input, deps.cwd, repoKeyFor(deps.cwd));
|
|
533
534
|
if (policy.timeoutSeconds === 0 && policy.timeoutAction === "approve") return allow(input);
|
|
534
535
|
if (policy.timeoutSeconds === 0 && policy.timeoutAction === "deny") {
|
|
535
536
|
return deny(`Denied by policy for ${policy.tool}`);
|
|
@@ -6,15 +6,15 @@ import {
|
|
|
6
6
|
removeCodexHooks,
|
|
7
7
|
removeGeminiSettings,
|
|
8
8
|
removeInstructionBlock
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-GSERIC6Q.js";
|
|
10
10
|
import {
|
|
11
11
|
execNpm
|
|
12
12
|
} from "../chunk-J7JWI3KU.js";
|
|
13
13
|
import {
|
|
14
14
|
removeClaudeMcpServers,
|
|
15
15
|
removePusharySettings
|
|
16
|
-
} from "../chunk-
|
|
17
|
-
import "../chunk-
|
|
16
|
+
} from "../chunk-QYQO6RHJ.js";
|
|
17
|
+
import "../chunk-SYAAGYLZ.js";
|
|
18
18
|
|
|
19
19
|
// bin/pushary-clean.ts
|
|
20
20
|
import { existsSync, readFileSync, writeFileSync, rmSync, readdirSync } from "fs";
|
|
@@ -31,21 +31,21 @@ import {
|
|
|
31
31
|
toCodexWire,
|
|
32
32
|
toPolicyLookup,
|
|
33
33
|
waitForAnswer
|
|
34
|
-
} from "../chunk-
|
|
34
|
+
} from "../chunk-LEWSYLHL.js";
|
|
35
35
|
import {
|
|
36
36
|
getMachineId
|
|
37
37
|
} from "../chunk-RN3NOEJF.js";
|
|
38
38
|
import {
|
|
39
39
|
isGatingMoment,
|
|
40
40
|
recordKeylessMoment
|
|
41
|
-
} from "../chunk-
|
|
41
|
+
} from "../chunk-5TECIV6T.js";
|
|
42
42
|
import "../chunk-DWED7BS3.js";
|
|
43
43
|
import {
|
|
44
44
|
DECISION_LINE_MAX,
|
|
45
45
|
effectiveWaitSeconds,
|
|
46
46
|
hookWaitClamped,
|
|
47
47
|
hookWaitDeadline
|
|
48
|
-
} from "../chunk-
|
|
48
|
+
} from "../chunk-SYAAGYLZ.js";
|
|
49
49
|
import {
|
|
50
50
|
getApiKey
|
|
51
51
|
} from "../chunk-NKXSILEW.js";
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
askUser,
|
|
4
4
|
reportEvent,
|
|
5
5
|
waitForAnswer
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-LEWSYLHL.js";
|
|
7
7
|
import {
|
|
8
8
|
getMachineId
|
|
9
9
|
} from "../chunk-RN3NOEJF.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-SYAAGYLZ.js";
|
|
12
12
|
import {
|
|
13
13
|
getApiKey
|
|
14
14
|
} from "../chunk-NKXSILEW.js";
|
|
@@ -11,18 +11,18 @@ import {
|
|
|
11
11
|
missingGeminiHookEvents,
|
|
12
12
|
readCodexMcpAuth,
|
|
13
13
|
untrustedCodexHookEvents
|
|
14
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-GSERIC6Q.js";
|
|
15
15
|
import {
|
|
16
16
|
execNpm
|
|
17
17
|
} from "../chunk-J7JWI3KU.js";
|
|
18
18
|
import {
|
|
19
19
|
readLedgerSummary
|
|
20
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-5TECIV6T.js";
|
|
21
21
|
import {
|
|
22
22
|
callMcpTool,
|
|
23
23
|
sendMcpRequest
|
|
24
24
|
} from "../chunk-DWED7BS3.js";
|
|
25
|
-
import "../chunk-
|
|
25
|
+
import "../chunk-SYAAGYLZ.js";
|
|
26
26
|
import {
|
|
27
27
|
getBaseUrl
|
|
28
28
|
} from "../chunk-NKXSILEW.js";
|
|
@@ -23,21 +23,21 @@ import {
|
|
|
23
23
|
savePendingQuestion,
|
|
24
24
|
sendNotification,
|
|
25
25
|
waitForAnswer
|
|
26
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-LEWSYLHL.js";
|
|
27
27
|
import {
|
|
28
28
|
getMachineId
|
|
29
29
|
} from "../chunk-RN3NOEJF.js";
|
|
30
30
|
import {
|
|
31
31
|
isGatingMoment,
|
|
32
32
|
recordKeylessMoment
|
|
33
|
-
} from "../chunk-
|
|
33
|
+
} from "../chunk-5TECIV6T.js";
|
|
34
34
|
import "../chunk-DWED7BS3.js";
|
|
35
35
|
import {
|
|
36
36
|
DECISION_LINE_MAX,
|
|
37
37
|
effectiveWaitSeconds,
|
|
38
38
|
hookWaitClamped,
|
|
39
39
|
hookWaitDeadline
|
|
40
|
-
} from "../chunk-
|
|
40
|
+
} from "../chunk-SYAAGYLZ.js";
|
|
41
41
|
import {
|
|
42
42
|
getApiKey
|
|
43
43
|
} from "../chunk-NKXSILEW.js";
|
package/dist/bin/pushary-hook.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePreToolUse
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-NPYANTRC.js";
|
|
5
|
+
import "../chunk-QYQO6RHJ.js";
|
|
6
6
|
import "../chunk-YHG74UFF.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-LEWSYLHL.js";
|
|
8
8
|
import "../chunk-RN3NOEJF.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-5TECIV6T.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-SYAAGYLZ.js";
|
|
12
12
|
import "../chunk-NKXSILEW.js";
|
|
13
13
|
|
|
14
14
|
// bin/pushary-hook.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleNotification
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-LEWSYLHL.js";
|
|
5
5
|
import "../chunk-RN3NOEJF.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-SYAAGYLZ.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
9
9
|
|
|
10
10
|
// bin/pushary-notification-hook.ts
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePermissionDenied
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-NPYANTRC.js";
|
|
5
|
+
import "../chunk-QYQO6RHJ.js";
|
|
6
6
|
import "../chunk-YHG74UFF.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-LEWSYLHL.js";
|
|
8
8
|
import "../chunk-RN3NOEJF.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-5TECIV6T.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-SYAAGYLZ.js";
|
|
12
12
|
import "../chunk-NKXSILEW.js";
|
|
13
13
|
|
|
14
14
|
// bin/pushary-permission-denied-hook.ts
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePermissionRequest
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-NPYANTRC.js";
|
|
5
|
+
import "../chunk-QYQO6RHJ.js";
|
|
6
6
|
import "../chunk-YHG74UFF.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-LEWSYLHL.js";
|
|
8
8
|
import "../chunk-RN3NOEJF.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-5TECIV6T.js";
|
|
10
10
|
import "../chunk-DWED7BS3.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-SYAAGYLZ.js";
|
|
12
12
|
import "../chunk-NKXSILEW.js";
|
|
13
13
|
|
|
14
14
|
// bin/pushary-permission-hook.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePostToolUse
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-LEWSYLHL.js";
|
|
5
5
|
import "../chunk-RN3NOEJF.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-SYAAGYLZ.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
9
9
|
|
|
10
10
|
// bin/pushary-post-hook.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleUserPrompt
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-LEWSYLHL.js";
|
|
5
5
|
import "../chunk-RN3NOEJF.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-SYAAGYLZ.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
9
9
|
|
|
10
10
|
// bin/pushary-prompt-hook.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleSessionEnd
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-LEWSYLHL.js";
|
|
5
5
|
import "../chunk-RN3NOEJF.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-SYAAGYLZ.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
9
9
|
|
|
10
10
|
// bin/pushary-session-end-hook.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleSessionStart
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-LEWSYLHL.js";
|
|
5
5
|
import "../chunk-RN3NOEJF.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-SYAAGYLZ.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
9
9
|
|
|
10
10
|
// bin/pushary-session-start-hook.ts
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
renderAgentInstructions,
|
|
14
14
|
renderProjectAgentInstructions,
|
|
15
15
|
writeInstructionBlock
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-GSERIC6Q.js";
|
|
17
17
|
import {
|
|
18
18
|
execNpm,
|
|
19
19
|
npmErrorMessage,
|
|
@@ -25,15 +25,15 @@ import {
|
|
|
25
25
|
addClaudeMcpServer,
|
|
26
26
|
addPusharyHooks,
|
|
27
27
|
addPusharyToolPermissions
|
|
28
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-QYQO6RHJ.js";
|
|
29
29
|
import {
|
|
30
30
|
reportEvent
|
|
31
|
-
} from "../chunk-
|
|
31
|
+
} from "../chunk-LEWSYLHL.js";
|
|
32
32
|
import "../chunk-RN3NOEJF.js";
|
|
33
33
|
import "../chunk-DWED7BS3.js";
|
|
34
34
|
import {
|
|
35
35
|
isValidApiKey
|
|
36
|
-
} from "../chunk-
|
|
36
|
+
} from "../chunk-SYAAGYLZ.js";
|
|
37
37
|
import "../chunk-NKXSILEW.js";
|
|
38
38
|
|
|
39
39
|
// bin/pushary-setup.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleStop
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-LEWSYLHL.js";
|
|
5
5
|
import "../chunk-RN3NOEJF.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-SYAAGYLZ.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
9
9
|
|
|
10
10
|
// bin/pushary-stop-hook.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleStopFailure
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-LEWSYLHL.js";
|
|
5
5
|
import "../chunk-RN3NOEJF.js";
|
|
6
6
|
import "../chunk-DWED7BS3.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-SYAAGYLZ.js";
|
|
8
8
|
import "../chunk-NKXSILEW.js";
|
|
9
9
|
|
|
10
10
|
// bin/pushary-stopfailure-hook.ts
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
addClaudeMcpServer,
|
|
9
9
|
addPusharyHooks,
|
|
10
10
|
addPusharyToolPermissions
|
|
11
|
-
} from "../chunk-
|
|
12
|
-
import "../chunk-
|
|
11
|
+
} from "../chunk-QYQO6RHJ.js";
|
|
12
|
+
import "../chunk-SYAAGYLZ.js";
|
|
13
13
|
import {
|
|
14
14
|
getApiKey
|
|
15
15
|
} from "../chunk-NKXSILEW.js";
|
|
@@ -10,13 +10,16 @@ import {
|
|
|
10
10
|
DECISION_LINE_MAX,
|
|
11
11
|
classifyDecisionRisk,
|
|
12
12
|
isApprovalMode,
|
|
13
|
+
isRepoKeyMatch,
|
|
13
14
|
isSafeReadOnlyCommand,
|
|
15
|
+
localRepoKey,
|
|
14
16
|
matchRankWeight,
|
|
15
17
|
matchToolPatternAny,
|
|
18
|
+
normalizeRepoRemote,
|
|
16
19
|
policyArgForms,
|
|
17
20
|
redactSecrets,
|
|
18
21
|
redactSecretsDeep
|
|
19
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-SYAAGYLZ.js";
|
|
20
23
|
import {
|
|
21
24
|
getApiKey,
|
|
22
25
|
getBaseUrl
|
|
@@ -47,14 +50,19 @@ var isWaitForAnswerResponse = (data) => {
|
|
|
47
50
|
|
|
48
51
|
// src/policy.ts
|
|
49
52
|
var CACHE_TTL_MS = 60 * 1e3;
|
|
50
|
-
var
|
|
51
|
-
const
|
|
53
|
+
var policyCacheFile = (apiKey, agent, repoAware = false) => {
|
|
54
|
+
const parts = [apiKey, agent, repoAware ? "repoAware" : void 0].filter(Boolean);
|
|
55
|
+
const hash = createHash("sha256").update(parts.join(":")).digest("hex").slice(0, 12);
|
|
52
56
|
return join(tmpdir(), `pushary-policy-${hash}.json`);
|
|
53
57
|
};
|
|
54
|
-
var fetchPolicy = async (apiKey, agent) => {
|
|
58
|
+
var fetchPolicy = async (apiKey, agent, repoAware = false) => {
|
|
55
59
|
return withRetry(async () => {
|
|
56
60
|
const baseUrl = getBaseUrl();
|
|
57
|
-
const
|
|
61
|
+
const params = new URLSearchParams();
|
|
62
|
+
if (agent) params.set("agent", agent);
|
|
63
|
+
if (repoAware) params.set("repoAware", "1");
|
|
64
|
+
const query = params.toString();
|
|
65
|
+
const url = query ? `${baseUrl}/api/mcp/policy?${query}` : `${baseUrl}/api/mcp/policy`;
|
|
58
66
|
const response = await fetch(url, {
|
|
59
67
|
headers: { "Authorization": `Bearer ${apiKey}` },
|
|
60
68
|
signal: AbortSignal.timeout(1e4)
|
|
@@ -67,8 +75,8 @@ var fetchPolicy = async (apiKey, agent) => {
|
|
|
67
75
|
return raw;
|
|
68
76
|
}, { maxAttempts: 2 });
|
|
69
77
|
};
|
|
70
|
-
var getPolicy = async (apiKey, expectedVersion, agent) => {
|
|
71
|
-
const path =
|
|
78
|
+
var getPolicy = async (apiKey, expectedVersion, agent, repoAware = false) => {
|
|
79
|
+
const path = policyCacheFile(apiKey, agent, repoAware);
|
|
72
80
|
let staleCache = null;
|
|
73
81
|
if (existsSync(path)) {
|
|
74
82
|
try {
|
|
@@ -85,7 +93,7 @@ var getPolicy = async (apiKey, expectedVersion, agent) => {
|
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
95
|
try {
|
|
88
|
-
const policy = await fetchPolicy(apiKey, agent);
|
|
96
|
+
const policy = await fetchPolicy(apiKey, agent, repoAware);
|
|
89
97
|
try {
|
|
90
98
|
writeFileSync(path, JSON.stringify({ ...policy, _cachedAt: Date.now(), _policyVersion: expectedVersion ?? null }), "utf-8");
|
|
91
99
|
} catch {
|
|
@@ -96,18 +104,23 @@ var getPolicy = async (apiKey, expectedVersion, agent) => {
|
|
|
96
104
|
throw new Error("Failed to fetch policy and no cached policy available");
|
|
97
105
|
}
|
|
98
106
|
};
|
|
99
|
-
var findBestMatch = (policies, toolName, args) => {
|
|
107
|
+
var findBestMatch = (policies, toolName, args, repoKey) => {
|
|
100
108
|
let best;
|
|
101
109
|
let bestWeight = 0;
|
|
110
|
+
let bestScoped = -1;
|
|
102
111
|
let bestLength = -1;
|
|
103
112
|
for (const candidate of policies) {
|
|
113
|
+
if (!isRepoKeyMatch(candidate.repoKey, repoKey)) continue;
|
|
104
114
|
const rank = matchToolPatternAny(candidate.tool, toolName, args);
|
|
105
115
|
if (rank === "none") continue;
|
|
106
116
|
const weight = matchRankWeight(rank);
|
|
117
|
+
const scoped = candidate.repoKey ? 1 : 0;
|
|
107
118
|
const length = rank === "prefix" ? candidate.tool.length : -1;
|
|
108
|
-
|
|
119
|
+
const better = weight > bestWeight || weight === bestWeight && scoped > bestScoped || weight === bestWeight && scoped === bestScoped && length > bestLength;
|
|
120
|
+
if (better) {
|
|
109
121
|
best = { policy: candidate, rank };
|
|
110
122
|
bestWeight = weight;
|
|
123
|
+
bestScoped = scoped;
|
|
111
124
|
bestLength = length;
|
|
112
125
|
}
|
|
113
126
|
}
|
|
@@ -120,20 +133,20 @@ var autoApprove = (tool) => ({
|
|
|
120
133
|
mode: "terminal_only",
|
|
121
134
|
pushFirstSeconds: 0
|
|
122
135
|
});
|
|
123
|
-
var resolveAutoResolveOrigin = (config, toolName, toolInput, cwd) => {
|
|
136
|
+
var resolveAutoResolveOrigin = (config, toolName, toolInput, cwd, repoKey) => {
|
|
124
137
|
const args = toolInput ? policyArgForms(toolName, toolInput, cwd) : [];
|
|
125
138
|
const arg = args[0];
|
|
126
|
-
const match = findBestMatch(config.policies, toolName, args);
|
|
139
|
+
const match = findBestMatch(config.policies, toolName, args, repoKey);
|
|
127
140
|
const governedBySpecificRule = match?.rank === "exact" || match?.rank === "prefix";
|
|
128
141
|
if (!governedBySpecificRule && toolName === "Bash" && typeof arg === "string" && isSafeReadOnlyCommand(arg)) {
|
|
129
142
|
return "safe_readonly";
|
|
130
143
|
}
|
|
131
144
|
return "policy_timeout";
|
|
132
145
|
};
|
|
133
|
-
var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd) => {
|
|
146
|
+
var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey) => {
|
|
134
147
|
const args = toolInput ? policyArgForms(toolName, toolInput, cwd) : [];
|
|
135
148
|
const arg = args[0];
|
|
136
|
-
const match = findBestMatch(config.policies, toolName, args);
|
|
149
|
+
const match = findBestMatch(config.policies, toolName, args, repoKey);
|
|
137
150
|
let base = match?.policy ?? config.policies.find((p) => p.tool === "*") ?? {
|
|
138
151
|
tool: toolName,
|
|
139
152
|
timeoutSeconds: config.defaultTimeoutSeconds,
|
|
@@ -271,6 +284,16 @@ var deriveToolTarget = (toolName, toolInput) => {
|
|
|
271
284
|
}
|
|
272
285
|
return void 0;
|
|
273
286
|
};
|
|
287
|
+
var SCRIPT_RUNNERS = /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun", "npx", "deno", "rake", "make", "just", "task", "turbo"]);
|
|
288
|
+
var RUN_SUBCOMMANDS = /* @__PURE__ */ new Set(["run", "run-script", "exec", "task"]);
|
|
289
|
+
var deriveReceiptCommandHead = (command) => {
|
|
290
|
+
if (typeof command !== "string") return void 0;
|
|
291
|
+
const tokens = command.trim().split(/\s+/);
|
|
292
|
+
const [first, second] = tokens;
|
|
293
|
+
const keep = first && second && SCRIPT_RUNNERS.has(first.toLowerCase()) && RUN_SUBCOMMANDS.has(second.toLowerCase()) ? 3 : 2;
|
|
294
|
+
const head = tokens.slice(0, keep).join(" ");
|
|
295
|
+
return head ? head.slice(0, TOOL_TARGET_MAX_LENGTH) : void 0;
|
|
296
|
+
};
|
|
274
297
|
var RECEIPT_TARGET_MAX_LENGTH = 256;
|
|
275
298
|
var isToolResultError = (toolResult) => {
|
|
276
299
|
try {
|
|
@@ -296,7 +319,7 @@ var deriveReceiptMeta = (toolName, toolInput, toolResult, cwd) => {
|
|
|
296
319
|
};
|
|
297
320
|
}
|
|
298
321
|
if (toolName === "Bash" || toolName === "PowerShell" || toolName === "Monitor") {
|
|
299
|
-
const head =
|
|
322
|
+
const head = deriveReceiptCommandHead(toolInput.command);
|
|
300
323
|
if (!head) return void 0;
|
|
301
324
|
return {
|
|
302
325
|
kind: head === "git commit" ? "commit" : "bash",
|
|
@@ -641,17 +664,101 @@ var permissionTimeoutDecision = (timeoutAction) => {
|
|
|
641
664
|
};
|
|
642
665
|
var preToolUseTimeoutDecision = (timeoutAction, denyReason = "No response within timeout") => timeoutAction === "deny" ? codexDeny(denyReason) : codexPass();
|
|
643
666
|
|
|
667
|
+
// src/repo.ts
|
|
668
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
669
|
+
import { basename, dirname, isAbsolute as isAbsolute2, join as join4, resolve as resolve2 } from "path";
|
|
670
|
+
var MAX_PARENT_WALK = 64;
|
|
671
|
+
var GIT_FILE_PREFIX = "gitdir:";
|
|
672
|
+
var findGitDir = (startDir) => {
|
|
673
|
+
let current = resolve2(startDir);
|
|
674
|
+
for (let depth = 0; depth < MAX_PARENT_WALK; depth += 1) {
|
|
675
|
+
const candidate = join4(current, ".git");
|
|
676
|
+
try {
|
|
677
|
+
if (existsSync3(candidate)) {
|
|
678
|
+
if (statSync3(candidate).isDirectory()) return candidate;
|
|
679
|
+
const pointer = readFileSync3(candidate, "utf-8").trim();
|
|
680
|
+
if (pointer.startsWith(GIT_FILE_PREFIX)) {
|
|
681
|
+
const target = pointer.slice(GIT_FILE_PREFIX.length).trim();
|
|
682
|
+
return isAbsolute2(target) ? target : resolve2(current, target);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
} catch {
|
|
686
|
+
}
|
|
687
|
+
const parent = dirname(current);
|
|
688
|
+
if (parent === current) return void 0;
|
|
689
|
+
current = parent;
|
|
690
|
+
}
|
|
691
|
+
return void 0;
|
|
692
|
+
};
|
|
693
|
+
var configPathFor = (gitDir) => {
|
|
694
|
+
const index = gitDir.replace(/\\/g, "/").indexOf("/worktrees/");
|
|
695
|
+
return index === -1 ? join4(gitDir, "config") : join4(gitDir.slice(0, index), "config");
|
|
696
|
+
};
|
|
697
|
+
var ORIGIN_SECTION = /^\[remote "origin"\]/;
|
|
698
|
+
var SECTION_HEADER = /^\[/;
|
|
699
|
+
var URL_LINE = /^url\s*=\s*(.+)$/;
|
|
700
|
+
var readOriginRemote = (gitDir) => {
|
|
701
|
+
try {
|
|
702
|
+
const configPath = configPathFor(gitDir);
|
|
703
|
+
if (!existsSync3(configPath)) return void 0;
|
|
704
|
+
let inOrigin = false;
|
|
705
|
+
for (const rawLine of readFileSync3(configPath, "utf-8").split("\n")) {
|
|
706
|
+
const line = rawLine.trim();
|
|
707
|
+
if (SECTION_HEADER.test(line)) {
|
|
708
|
+
inOrigin = ORIGIN_SECTION.test(line);
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
if (!inOrigin) continue;
|
|
712
|
+
const url = URL_LINE.exec(line);
|
|
713
|
+
if (url) return url[1]?.trim();
|
|
714
|
+
}
|
|
715
|
+
} catch {
|
|
716
|
+
}
|
|
717
|
+
return void 0;
|
|
718
|
+
};
|
|
719
|
+
var deriveRepoKey = (cwd) => {
|
|
720
|
+
const dir = cwd && cwd.length > 0 ? cwd : process.cwd();
|
|
721
|
+
try {
|
|
722
|
+
const gitDir = findGitDir(dir);
|
|
723
|
+
if (gitDir) {
|
|
724
|
+
const remote = readOriginRemote(gitDir);
|
|
725
|
+
const normalized = remote ? normalizeRepoRemote(remote) : void 0;
|
|
726
|
+
if (normalized) return normalized;
|
|
727
|
+
const root = gitDir.endsWith(".git") ? dirname(gitDir) : dir;
|
|
728
|
+
return localRepoKey(basename(root));
|
|
729
|
+
}
|
|
730
|
+
return localRepoKey(basename(resolve2(dir)));
|
|
731
|
+
} catch {
|
|
732
|
+
return void 0;
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
var repoKeyCache = /* @__PURE__ */ new Map();
|
|
736
|
+
var REPO_KEY_ENV = "PUSHARY_REPO_KEY";
|
|
737
|
+
var repoKeyFor = (cwd) => {
|
|
738
|
+
const override = process.env[REPO_KEY_ENV];
|
|
739
|
+
if (typeof override === "string") {
|
|
740
|
+
const trimmed = override.trim();
|
|
741
|
+
if (trimmed.toLowerCase() === "off") return void 0;
|
|
742
|
+
if (trimmed.length > 0) return trimmed.toLowerCase();
|
|
743
|
+
}
|
|
744
|
+
const dir = cwd && cwd.length > 0 ? cwd : process.cwd();
|
|
745
|
+
if (repoKeyCache.has(dir)) return repoKeyCache.get(dir);
|
|
746
|
+
const derived = deriveRepoKey(dir);
|
|
747
|
+
repoKeyCache.set(dir, derived);
|
|
748
|
+
return derived;
|
|
749
|
+
};
|
|
750
|
+
|
|
644
751
|
// src/throttle.ts
|
|
645
|
-
import { join as
|
|
752
|
+
import { join as join5 } from "path";
|
|
646
753
|
import { tmpdir as tmpdir4 } from "os";
|
|
647
754
|
import { createHash as createHash2 } from "crypto";
|
|
648
|
-
import { existsSync as
|
|
649
|
-
var THROTTLE_DIR =
|
|
650
|
-
var markerPath = (key) =>
|
|
755
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
756
|
+
var THROTTLE_DIR = join5(tmpdir4(), "pushary-throttle");
|
|
757
|
+
var markerPath = (key) => join5(THROTTLE_DIR, createHash2("sha256").update(key).digest("hex").slice(0, 16));
|
|
651
758
|
var throttlePass = (key, windowMs) => {
|
|
652
759
|
try {
|
|
653
760
|
const path = markerPath(key);
|
|
654
|
-
if (
|
|
761
|
+
if (existsSync4(path) && Date.now() - statSync4(path).mtimeMs < windowMs) return false;
|
|
655
762
|
mkdirSync3(THROTTLE_DIR, { recursive: true });
|
|
656
763
|
writeFileSync4(path, "", "utf-8");
|
|
657
764
|
return true;
|
|
@@ -661,15 +768,14 @@ var throttlePass = (key, windowMs) => {
|
|
|
661
768
|
};
|
|
662
769
|
|
|
663
770
|
// src/events.ts
|
|
664
|
-
import { basename, join as
|
|
665
|
-
import {
|
|
666
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, statSync as statSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
771
|
+
import { basename as basename2, join as join6 } from "path";
|
|
772
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, statSync as statSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
667
773
|
import { tmpdir as tmpdir5 } from "os";
|
|
668
|
-
var INTENT_DIR =
|
|
774
|
+
var INTENT_DIR = join6(tmpdir5(), "pushary-intent");
|
|
669
775
|
var INTENT_MAX_BYTES = 2048;
|
|
670
776
|
var INTENT_GRACE_MS = 10 * 60 * 1e3;
|
|
671
777
|
var sanitizeSession = (sessionId) => sessionId.replace(/[^A-Za-z0-9_-]/g, "_").slice(0, 128) || DEFAULT_SESSION;
|
|
672
|
-
var intentFile = (sessionId) =>
|
|
778
|
+
var intentFile = (sessionId) => join6(INTENT_DIR, sanitizeSession(sessionId));
|
|
673
779
|
var saveLastPrompt = (sessionId, prompt) => {
|
|
674
780
|
try {
|
|
675
781
|
const trimmed = prompt.replace(/\s+/g, " ").trim().slice(0, INTENT_MAX_BYTES);
|
|
@@ -682,8 +788,8 @@ var saveLastPrompt = (sessionId, prompt) => {
|
|
|
682
788
|
var readLastPrompt = (sessionId) => {
|
|
683
789
|
try {
|
|
684
790
|
const path = intentFile(sessionId);
|
|
685
|
-
if (isDefaultSession(sessionId) &&
|
|
686
|
-
const value =
|
|
791
|
+
if (isDefaultSession(sessionId) && statSync5(path).mtimeMs < Date.now() - INTENT_GRACE_MS) return void 0;
|
|
792
|
+
const value = readFileSync4(path, "utf-8").trim();
|
|
687
793
|
return value.length > 0 ? value : void 0;
|
|
688
794
|
} catch {
|
|
689
795
|
return void 0;
|
|
@@ -739,10 +845,9 @@ var notifyLateAnswers = async (apiKey, late, agentName, sessionId) => {
|
|
|
739
845
|
var CLAUDE_CODE_AGENT = { type: "claude_code", label: "Claude Code" };
|
|
740
846
|
var POLICY_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
741
847
|
var readFreshCachedPolicy = (apiKey) => {
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
const cached = JSON.parse(readFileSync3(path, "utf-8"));
|
|
848
|
+
const path = policyCacheFile(apiKey);
|
|
849
|
+
if (!existsSync5(path)) return null;
|
|
850
|
+
const cached = JSON.parse(readFileSync4(path, "utf-8"));
|
|
746
851
|
if (!isPolicyConfig(cached)) return null;
|
|
747
852
|
if (!cached._cachedAt || Date.now() - cached._cachedAt >= POLICY_CACHE_TTL_MS) return null;
|
|
748
853
|
return cached;
|
|
@@ -752,7 +857,7 @@ var deriveDecisionSource = (toolName, toolInput, liveMode, cwd) => {
|
|
|
752
857
|
if (liveMode.kill) return "terminal";
|
|
753
858
|
const policy = readFreshCachedPolicy(getApiKey());
|
|
754
859
|
if (!policy) return void 0;
|
|
755
|
-
const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd);
|
|
860
|
+
const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd, repoKeyFor(cwd));
|
|
756
861
|
if (resolved.timeoutSeconds === 0 && resolved.timeoutAction === "approve") return "policy_auto";
|
|
757
862
|
if (resolved.mode === "push_only" || resolved.mode === "push_first") return "human";
|
|
758
863
|
return "terminal";
|
|
@@ -765,10 +870,10 @@ var deriveAutoDecisionBeacon = (toolName, toolInput, liveMode, cwd) => {
|
|
|
765
870
|
if (liveMode.kill) return void 0;
|
|
766
871
|
const policy = readFreshCachedPolicy(getApiKey());
|
|
767
872
|
if (!policy) return void 0;
|
|
768
|
-
const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd);
|
|
873
|
+
const resolved = resolvePolicy(policy, toolName, liveMode.mode, toolInput, cwd, repoKeyFor(cwd));
|
|
769
874
|
if (resolved.timeoutSeconds !== 0 || resolved.timeoutAction !== "approve") return void 0;
|
|
770
875
|
return {
|
|
771
|
-
decisionOrigin: resolveAutoResolveOrigin(policy, toolName, toolInput, cwd),
|
|
876
|
+
decisionOrigin: resolveAutoResolveOrigin(policy, toolName, toolInput, cwd, repoKeyFor(cwd)),
|
|
772
877
|
toolName,
|
|
773
878
|
toolTarget: deriveToolTarget(toolName, toolInput)
|
|
774
879
|
};
|
|
@@ -809,6 +914,7 @@ var reportEvent = async (event, options = {}) => {
|
|
|
809
914
|
body: JSON.stringify({
|
|
810
915
|
...event,
|
|
811
916
|
machineId: event.machineId ?? getMachineId(),
|
|
917
|
+
repoKey: event.repoKey ?? repoKeyFor(),
|
|
812
918
|
installMode: detectInstallMode(),
|
|
813
919
|
// Link-back for a phone-spawned session: forward the spawn id the daemon
|
|
814
920
|
// set in the environment so the server can map spawnId -> sessionId.
|
|
@@ -833,7 +939,7 @@ var reportEvent = async (event, options = {}) => {
|
|
|
833
939
|
var NON_GATED_MIRROR_TOOLS = /* @__PURE__ */ new Set(["TodoWrite"]);
|
|
834
940
|
var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
835
941
|
try {
|
|
836
|
-
const projectName =
|
|
942
|
+
const projectName = basename2(input.cwd ?? process.cwd());
|
|
837
943
|
const action = describeToolCall(input.tool_name, input.tool_input, "event");
|
|
838
944
|
const lookup = toPolicyLookup(input.tool_name, input.tool_input);
|
|
839
945
|
const toolResult = input.tool_response ?? input.tool_result;
|
|
@@ -888,7 +994,7 @@ var handlePostToolUse = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
|
888
994
|
var TASK_TITLE_MAX_LENGTH = 120;
|
|
889
995
|
var handleUserPrompt = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
890
996
|
try {
|
|
891
|
-
const projectName =
|
|
997
|
+
const projectName = basename2(input.cwd ?? process.cwd());
|
|
892
998
|
const sessionKey = input.session_id || DEFAULT_SESSION;
|
|
893
999
|
const titlesEnabled = process.env.PUSHARY_TASK_TITLES !== "off";
|
|
894
1000
|
const taskTitle = titlesEnabled ? input.prompt?.replace(/\s+/g, " ").trim().slice(0, TASK_TITLE_MAX_LENGTH) || void 0 : void 0;
|
|
@@ -933,7 +1039,7 @@ var summarizeStop = (input) => {
|
|
|
933
1039
|
};
|
|
934
1040
|
var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
935
1041
|
try {
|
|
936
|
-
const projectName =
|
|
1042
|
+
const projectName = basename2(input.cwd ?? process.cwd());
|
|
937
1043
|
const sessionKey = input.session_id || DEFAULT_SESSION;
|
|
938
1044
|
const late = await reconcilePendingQuestions(sessionKey);
|
|
939
1045
|
const tasks = [
|
|
@@ -969,7 +1075,7 @@ var handleStop = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
|
969
1075
|
};
|
|
970
1076
|
var handleSessionStart = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
971
1077
|
try {
|
|
972
|
-
const projectName =
|
|
1078
|
+
const projectName = basename2(input.cwd ?? process.cwd());
|
|
973
1079
|
const action = input.source === "resume" ? "Session resumed" : "Session started";
|
|
974
1080
|
await reportEvent({
|
|
975
1081
|
event: "session_start",
|
|
@@ -991,7 +1097,7 @@ var SESSION_END_ACTIONS = {
|
|
|
991
1097
|
};
|
|
992
1098
|
var handleSessionEnd = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
993
1099
|
try {
|
|
994
|
-
const projectName =
|
|
1100
|
+
const projectName = basename2(input.cwd ?? process.cwd());
|
|
995
1101
|
const action = typeof input.reason === "string" && SESSION_END_ACTIONS[input.reason] || "Session ended";
|
|
996
1102
|
const sessionKey = input.session_id || DEFAULT_SESSION;
|
|
997
1103
|
const late = await reconcilePendingQuestions(sessionKey);
|
|
@@ -1015,7 +1121,7 @@ var NOTIFICATION_PUSH_TYPES = /* @__PURE__ */ new Set(["idle_prompt", "agent_nee
|
|
|
1015
1121
|
var NOTIFICATION_THROTTLE_MS = 60 * 1e3;
|
|
1016
1122
|
var handleNotification = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
1017
1123
|
try {
|
|
1018
|
-
const projectName =
|
|
1124
|
+
const projectName = basename2(input.cwd ?? process.cwd());
|
|
1019
1125
|
const notifType = input.notification_type ?? input.type ?? "";
|
|
1020
1126
|
const sessionKey = input.session_id || DEFAULT_SESSION;
|
|
1021
1127
|
if (NOTIFICATION_PUSH_TYPES.has(notifType)) {
|
|
@@ -1085,7 +1191,7 @@ var handleStopFailure = async (input, agent = CLAUDE_CODE_AGENT) => {
|
|
|
1085
1191
|
} catch {
|
|
1086
1192
|
return;
|
|
1087
1193
|
}
|
|
1088
|
-
const projectName =
|
|
1194
|
+
const projectName = basename2(input.cwd ?? process.cwd());
|
|
1089
1195
|
const mode = await fetchModeState(apiKey, input.session_id).catch(() => null);
|
|
1090
1196
|
if (mode?.kill || mode?.mode === "terminal_only") return;
|
|
1091
1197
|
const reason = describeStopFailure(input.error);
|
|
@@ -1117,6 +1223,7 @@ export {
|
|
|
1117
1223
|
deriveBlocker,
|
|
1118
1224
|
DEFAULT_SESSION,
|
|
1119
1225
|
savePendingQuestion,
|
|
1226
|
+
repoKeyFor,
|
|
1120
1227
|
throttlePass,
|
|
1121
1228
|
readLastUserPrompt,
|
|
1122
1229
|
CODEX_AGENT,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRETOOLUSE_HANDLED_TOOLS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-QYQO6RHJ.js";
|
|
4
4
|
import {
|
|
5
5
|
denyReasonFrom,
|
|
6
6
|
isDeferAnswer
|
|
@@ -18,25 +18,26 @@ import {
|
|
|
18
18
|
getPolicy,
|
|
19
19
|
readLastPrompt,
|
|
20
20
|
readLastUserPrompt,
|
|
21
|
+
repoKeyFor,
|
|
21
22
|
resolvePolicy,
|
|
22
23
|
savePendingQuestion,
|
|
23
24
|
sendNotification,
|
|
24
25
|
throttlePass,
|
|
25
26
|
waitForAnswer
|
|
26
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-LEWSYLHL.js";
|
|
27
28
|
import {
|
|
28
29
|
getMachineId
|
|
29
30
|
} from "./chunk-RN3NOEJF.js";
|
|
30
31
|
import {
|
|
31
32
|
isGatingMoment,
|
|
32
33
|
recordKeylessMoment
|
|
33
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-5TECIV6T.js";
|
|
34
35
|
import {
|
|
35
36
|
buildDecisionEpisodeFeatures,
|
|
36
37
|
effectiveWaitSeconds,
|
|
37
38
|
hookWaitClamped,
|
|
38
39
|
hookWaitDeadline
|
|
39
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-SYAAGYLZ.js";
|
|
40
41
|
import {
|
|
41
42
|
getApiKey,
|
|
42
43
|
getBaseUrl
|
|
@@ -385,7 +386,7 @@ var handlePreToolUse = async (input) => {
|
|
|
385
386
|
}
|
|
386
387
|
try {
|
|
387
388
|
const modeState = await fetchModeState(apiKey, input.session_id);
|
|
388
|
-
const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code");
|
|
389
|
+
const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code", true);
|
|
389
390
|
if (modeState.kill) {
|
|
390
391
|
return deny("Stopped by user, this agent was halted from Pushary");
|
|
391
392
|
}
|
|
@@ -395,7 +396,7 @@ var handlePreToolUse = async (input) => {
|
|
|
395
396
|
if (shouldDeferToNativeMode(input.permission_mode, input.tool_name)) {
|
|
396
397
|
return void 0;
|
|
397
398
|
}
|
|
398
|
-
const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input, input.cwd);
|
|
399
|
+
const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input, input.cwd, repoKeyFor(input.cwd));
|
|
399
400
|
if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") {
|
|
400
401
|
return allow();
|
|
401
402
|
}
|
|
@@ -440,9 +441,9 @@ var handlePermissionRequest = async (input) => {
|
|
|
440
441
|
}
|
|
441
442
|
try {
|
|
442
443
|
const modeState = await fetchModeState(apiKey, input.session_id);
|
|
443
|
-
const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code");
|
|
444
|
+
const policy = await getPolicy(apiKey, modeState.policyVersion, "claude_code", true);
|
|
444
445
|
if (modeState.kill) return permReqDeny("Stopped by user \u2014 this agent was halted from Pushary");
|
|
445
|
-
const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input, input.cwd);
|
|
446
|
+
const toolPolicy = resolvePolicy(policy, input.tool_name, modeState.mode, input.tool_input, input.cwd, repoKeyFor(input.cwd));
|
|
446
447
|
if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "approve") return permReqAllow();
|
|
447
448
|
if (toolPolicy.timeoutSeconds === 0 && toolPolicy.timeoutAction === "deny") {
|
|
448
449
|
return permReqDeny(`Denied by policy for ${toolPolicy.tool}`);
|
|
@@ -19,6 +19,45 @@ var hookWaitDeadline = (startMs, policyWaitSeconds, agent, nowMs) => {
|
|
|
19
19
|
};
|
|
20
20
|
var hookWaitClamped = (startMs, policyWaitSeconds, agent, nowMs) => startMs + hookMaxWaitSeconds(agent) * 1e3 < nowMs + Math.max(policyWaitSeconds, 0) * 1e3;
|
|
21
21
|
var effectiveWaitSeconds = (timeoutAction, policySeconds, agent) => timeoutAction === "wait" ? hookMaxWaitSeconds(agent) : policySeconds;
|
|
22
|
+
var REPO_KEY_MAX_LENGTH = 200;
|
|
23
|
+
var isRepoKeyMatch = (ruleRepoKey, currentRepoKey) => {
|
|
24
|
+
if (!ruleRepoKey) return true;
|
|
25
|
+
return ruleRepoKey === currentRepoKey;
|
|
26
|
+
};
|
|
27
|
+
var SCP_LIKE_REMOTE = /^[A-Za-z0-9._-]+@([A-Za-z0-9._-]+):(.+)$/;
|
|
28
|
+
var URL_LIKE_REMOTE = /^[A-Za-z][A-Za-z0-9+.-]*:\/\/(.+)$/;
|
|
29
|
+
var NUMERIC_PORT = /:\d+$/;
|
|
30
|
+
var stripCredentials = (host) => {
|
|
31
|
+
const at = host.lastIndexOf("@");
|
|
32
|
+
return at === -1 ? host : host.slice(at + 1);
|
|
33
|
+
};
|
|
34
|
+
var normalizeRepoRemote = (remoteUrl) => {
|
|
35
|
+
const raw = remoteUrl.trim();
|
|
36
|
+
if (raw.length === 0) return void 0;
|
|
37
|
+
let hostAndPath;
|
|
38
|
+
const scp = SCP_LIKE_REMOTE.exec(raw);
|
|
39
|
+
if (scp) {
|
|
40
|
+
hostAndPath = `${scp[1]}/${scp[2]}`;
|
|
41
|
+
} else {
|
|
42
|
+
const url = URL_LIKE_REMOTE.exec(raw);
|
|
43
|
+
if (!url) return void 0;
|
|
44
|
+
if (/^file:/i.test(raw)) return void 0;
|
|
45
|
+
hostAndPath = url[1];
|
|
46
|
+
}
|
|
47
|
+
const slash = hostAndPath.indexOf("/");
|
|
48
|
+
if (slash <= 0) return void 0;
|
|
49
|
+
const host = stripCredentials(hostAndPath.slice(0, slash)).replace(NUMERIC_PORT, "");
|
|
50
|
+
const path = hostAndPath.slice(slash + 1).split("/").filter((segment) => segment.length > 0).join("/");
|
|
51
|
+
if (host.length === 0 || path.length === 0) return void 0;
|
|
52
|
+
const key = `${host}/${path}`.replace(/\.git$/i, "").toLowerCase();
|
|
53
|
+
return key.length > REPO_KEY_MAX_LENGTH ? key.slice(0, REPO_KEY_MAX_LENGTH) : key;
|
|
54
|
+
};
|
|
55
|
+
var localRepoKey = (directoryName) => {
|
|
56
|
+
const name = directoryName.trim().replace(/[/\\]+$/, "").toLowerCase();
|
|
57
|
+
if (name.length === 0 || name === "." || name === "..") return void 0;
|
|
58
|
+
const key = `local/${name}`;
|
|
59
|
+
return key.length > REPO_KEY_MAX_LENGTH ? key.slice(0, REPO_KEY_MAX_LENGTH) : key;
|
|
60
|
+
};
|
|
22
61
|
var MATCH_RANKS = ["none", "tool", "prefix", "exact"];
|
|
23
62
|
var matchRankWeight = (rank) => MATCH_RANKS.indexOf(rank);
|
|
24
63
|
var GLOB_MAX_WILDCARDS = 8;
|
|
@@ -692,6 +731,9 @@ export {
|
|
|
692
731
|
hookWaitDeadline,
|
|
693
732
|
hookWaitClamped,
|
|
694
733
|
effectiveWaitSeconds,
|
|
734
|
+
isRepoKeyMatch,
|
|
735
|
+
normalizeRepoRemote,
|
|
736
|
+
localRepoKey,
|
|
695
737
|
matchRankWeight,
|
|
696
738
|
policyArgForms,
|
|
697
739
|
matchToolPatternAny,
|
package/dist/src/index.d.ts
CHANGED
|
@@ -26,9 +26,9 @@ interface ReceiptMeta {
|
|
|
26
26
|
ok: boolean;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
declare const getPolicy: (apiKey: string, expectedVersion?: string | null, agent?: string) => Promise<PolicyConfig>;
|
|
29
|
+
declare const getPolicy: (apiKey: string, expectedVersion?: string | null, agent?: string, repoAware?: boolean) => Promise<PolicyConfig>;
|
|
30
30
|
type AutoResolveOrigin = 'safe_readonly' | 'policy_timeout';
|
|
31
|
-
declare const resolvePolicy: (config: PolicyConfig, toolName: string, modeOverride?: ApprovalMode | null, toolInput?: Record<string, unknown>, cwd?: string) => ToolPolicy;
|
|
31
|
+
declare const resolvePolicy: (config: PolicyConfig, toolName: string, modeOverride?: ApprovalMode | null, toolInput?: Record<string, unknown>, cwd?: string, repoKey?: string) => ToolPolicy;
|
|
32
32
|
interface ModeState {
|
|
33
33
|
readonly mode: ApprovalMode | null;
|
|
34
34
|
readonly kill: boolean;
|
|
@@ -57,6 +57,7 @@ interface AgentEvent {
|
|
|
57
57
|
agentName?: string;
|
|
58
58
|
action?: string;
|
|
59
59
|
machineId?: string;
|
|
60
|
+
repoKey?: string;
|
|
60
61
|
sessionId?: string;
|
|
61
62
|
error?: string;
|
|
62
63
|
taskTitle?: string;
|
package/dist/src/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handlePreToolUse
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-NPYANTRC.js";
|
|
4
|
+
import "../chunk-QYQO6RHJ.js";
|
|
5
5
|
import "../chunk-YHG74UFF.js";
|
|
6
6
|
import {
|
|
7
7
|
askUser,
|
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
reportEvent,
|
|
16
16
|
resolvePolicy,
|
|
17
17
|
waitForAnswer
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-LEWSYLHL.js";
|
|
19
19
|
import "../chunk-RN3NOEJF.js";
|
|
20
|
-
import "../chunk-
|
|
20
|
+
import "../chunk-5TECIV6T.js";
|
|
21
21
|
import "../chunk-DWED7BS3.js";
|
|
22
|
-
import "../chunk-
|
|
22
|
+
import "../chunk-SYAAGYLZ.js";
|
|
23
23
|
import {
|
|
24
24
|
getApiKey,
|
|
25
25
|
getBaseUrl
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushary/agent-hooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.57.0",
|
|
4
4
|
"description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pushary",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"scripts": {
|
|
75
75
|
"build": "node scripts/bundle-plugin.mjs && tsup",
|
|
76
76
|
"dev": "tsup --watch",
|
|
77
|
-
"test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/reapply.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts && bun test src/wrapper/wrapper.test.ts && bun test src/wrapper/messageQueue.test.ts && bun test src/wrapper/approver.test.ts && bun test src/wrapper/remoteLoop.test.ts && bun test src/wrapper/spawnClaude.test.ts && bun test src/wrapper/wsProtocol.test.ts && bun test src/wrapper/relayClient.test.ts && bun test src/wrapper/modeLoop.test.ts && bun test src/wrapper/stdinHandoff.test.ts && bun test src/wrapper/localLeg.test.ts && bun test src/crypto.test.ts && bun test src/crypto-vector.test.ts && bun test src/transcript-capture.test.ts && bun test src/transcript-sync.test.ts && bun test src/spawn-daemon.test.ts && bun test src/decision-episode.test.ts"
|
|
77
|
+
"test": "bun test src/api.test.ts && bun test src/claude-config.test.ts && bun test src/config.test.ts && bun test src/mcp-http.test.ts && bun test src/retry.test.ts && bun test src/usage.test.ts && bun test src/validate.test.ts && bun test src/policy.test.ts && bun test src/npm.test.ts && bun test src/identity.test.ts && bun test src/pending.test.ts && bun test src/events.test.ts && bun test src/describe.test.ts && bun test src/reapply.test.ts && bun test src/suggestions.test.ts && bun test src/safe-commands.test.ts && bun test src/hook.test.ts && bun test src/codex-adapter.test.ts && bun test src/codex-config.test.ts && bun test src/gemini-adapter.test.ts && bun test src/gemini-config.test.ts && bun test src/instruction-file.test.ts && bun test src/pairing.test.ts && bun test src/wait-ladder.test.ts && bun test src/ledger.test.ts && bun test src/wrapper/wrapper.test.ts && bun test src/wrapper/messageQueue.test.ts && bun test src/wrapper/approver.test.ts && bun test src/wrapper/remoteLoop.test.ts && bun test src/wrapper/spawnClaude.test.ts && bun test src/wrapper/wsProtocol.test.ts && bun test src/wrapper/relayClient.test.ts && bun test src/wrapper/modeLoop.test.ts && bun test src/wrapper/stdinHandoff.test.ts && bun test src/wrapper/localLeg.test.ts && bun test src/crypto.test.ts && bun test src/crypto-vector.test.ts && bun test src/transcript-capture.test.ts && bun test src/transcript-sync.test.ts && bun test src/spawn-daemon.test.ts && bun test src/decision-episode.test.ts && bun test src/repo.test.ts"
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"@inquirer/prompts": "^8.4.2",
|