@pushary/agent-hooks 0.89.9 → 0.89.11
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 +54 -0
- package/dist/bin/pushary-bell-hook.js +4 -4
- package/dist/bin/pushary-bell.js +7 -7
- package/dist/bin/pushary-claude.js +84 -36
- package/dist/bin/pushary-clean.js +52 -45
- package/dist/bin/pushary-codex-bridge.js +15 -9
- package/dist/bin/pushary-codex-hook.js +12 -8
- package/dist/bin/pushary-codex.js +7 -7
- package/dist/bin/pushary-connect.js +6 -6
- package/dist/bin/pushary-daemon.js +16 -16
- package/dist/bin/pushary-disconnect.js +34 -9
- package/dist/bin/pushary-doctor.js +19 -19
- package/dist/bin/pushary-elicitation-hook.js +4 -4
- package/dist/bin/pushary-gemini-bridge.js +15 -8
- package/dist/bin/pushary-gemini-hook.js +8 -8
- package/dist/bin/pushary-hook.js +11 -10
- package/dist/bin/pushary-login.js +4 -4
- package/dist/bin/pushary-logout.js +4 -4
- package/dist/bin/pushary-mode.js +3 -3
- package/dist/bin/pushary-notification-hook.js +7 -7
- package/dist/bin/pushary-opencode-hook.js +8 -8
- package/dist/bin/pushary-permission-denied-hook.js +11 -10
- package/dist/bin/pushary-permission-hook.js +11 -10
- package/dist/bin/pushary-post-hook.js +7 -7
- package/dist/bin/pushary-prompt-hook.js +7 -7
- package/dist/bin/pushary-session-end-hook.js +7 -7
- package/dist/bin/pushary-session-start-hook.js +7 -7
- package/dist/bin/pushary-setup.js +52 -54
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +8 -8
- package/dist/bin/pushary-stop-hook.js +7 -7
- package/dist/bin/pushary-stopfailure-hook.js +7 -7
- package/dist/bin/pushary-upgrade.js +15 -15
- package/dist/bin/pushary-wait.js +3 -3
- package/dist/{chunk-SS72Z7FV.js → chunk-253WC6O5.js} +1 -1
- package/dist/{chunk-T3LRWZKI.js → chunk-2LPEZYMC.js} +1 -1
- package/dist/{chunk-O5B7EK5U.js → chunk-34GN3IPX.js} +1 -1
- package/dist/{chunk-V6N4Q223.js → chunk-3CUDMTL4.js} +1 -1
- package/dist/{chunk-VZKXBFUA.js → chunk-3HFK3UXM.js} +1 -1
- package/dist/{chunk-543KNJKC.js → chunk-3SQYKLBW.js} +3 -3
- package/dist/{chunk-ATBKJNWS.js → chunk-6EKQZA6R.js} +5 -1
- package/dist/{chunk-NKHEHEUM.js → chunk-6XUABEX2.js} +1 -1
- package/dist/{chunk-I5L6SK5V.js → chunk-BLODMQKJ.js} +1 -1
- package/dist/{chunk-DKDDLF5P.js → chunk-DKWM5T3P.js} +1 -1
- package/dist/{chunk-4XZCDSNG.js → chunk-FXIL7RCL.js} +5 -5
- package/dist/{chunk-BAE45YLP.js → chunk-FZ73G2RO.js} +3 -3
- package/dist/{chunk-PMHTCDEO.js → chunk-IUN6VOKV.js} +1 -1
- package/dist/{chunk-OFRNZWC4.js → chunk-IUWRVTQK.js} +1 -1
- package/dist/{chunk-EJT2ER6G.js → chunk-J4K75V4D.js} +46 -35
- package/dist/{chunk-GKAKAOKC.js → chunk-JHJHCMAF.js} +65 -12
- package/dist/{chunk-J2JZJZKB.js → chunk-KVE5RCQL.js} +1 -1
- package/dist/{chunk-A4O5ROZK.js → chunk-MZGWKIUZ.js} +2 -2
- package/dist/chunk-NCVIWGVA.js +18 -0
- package/dist/{chunk-C623LWJR.js → chunk-OLAGKKSR.js} +1 -1
- package/dist/{chunk-EA3E74P2.js → chunk-PZRWFWSR.js} +6 -6
- package/dist/{chunk-VA7VUNRD.js → chunk-Q223OM5J.js} +58 -8
- package/dist/{chunk-GMI5QZLG.js → chunk-QA72MKIT.js} +5 -1
- package/dist/{chunk-AIGDBRIJ.js → chunk-SYUIXXQW.js} +1 -1
- package/dist/{chunk-TF247BET.js → chunk-TEZ2FZD6.js} +1 -1
- package/dist/{chunk-KOPUTJQC.js → chunk-TG3T3M4K.js} +1 -1
- package/dist/{chunk-FX5QSMPY.js → chunk-TRVIRETT.js} +1 -1
- package/dist/{chunk-U5TQCQ25.js → chunk-TY5HVMXT.js} +1 -1
- package/dist/chunk-UDBJHQMG.js +240 -0
- package/dist/{chunk-PZA7G4CN.js → chunk-X4ZUUFKV.js} +1 -0
- package/dist/{chunk-L4C2JXJS.js → chunk-X6RXX2NF.js} +5 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +11 -10
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
shouldDeferToNativeMode
|
|
3
|
+
} from "./chunk-X6RXX2NF.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
|
+
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
detectClaudeVersion
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KVE5RCQL.js";
|
|
4
4
|
import {
|
|
5
5
|
claudeWired,
|
|
6
6
|
codexWired,
|
|
7
7
|
geminiWired
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-FZ73G2RO.js";
|
|
9
9
|
import {
|
|
10
10
|
GEMINI_HOOK_BINARY,
|
|
11
11
|
addClaudeMcpServer,
|
|
@@ -14,14 +14,14 @@ import {
|
|
|
14
14
|
addPusharyHooks,
|
|
15
15
|
addPusharyToolPermissions,
|
|
16
16
|
hasGeminiHooks
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-Q223OM5J.js";
|
|
18
18
|
import {
|
|
19
19
|
guardBinary
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-SYUIXXQW.js";
|
|
21
21
|
import {
|
|
22
22
|
resolveGlobalBinDir,
|
|
23
23
|
resolveGlobalBinary
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-X4ZUUFKV.js";
|
|
25
25
|
import {
|
|
26
26
|
CODEX_HOOK_BINARY,
|
|
27
27
|
addCodexHookTrust,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
codexHooksJson,
|
|
33
33
|
hasCodexHooks,
|
|
34
34
|
vscodePluginMcp
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-QA72MKIT.js";
|
|
36
36
|
import {
|
|
37
37
|
readJsonSafe
|
|
38
38
|
} from "./chunk-XX2V4QS5.js";
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRETOOLUSE_GATED_TOOLS,
|
|
3
3
|
PRETOOLUSE_PHONE_ANSWERED_TOOLS
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6EKQZA6R.js";
|
|
5
5
|
import {
|
|
6
6
|
guardBinary
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-SYUIXXQW.js";
|
|
8
8
|
import {
|
|
9
9
|
isProtectedByScope
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-QA72MKIT.js";
|
|
11
11
|
import {
|
|
12
12
|
HOOK_BUDGETS,
|
|
13
13
|
HOOK_LOCATOR_BINARY
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-X6RXX2NF.js";
|
|
15
15
|
|
|
16
16
|
// src/claude-config.ts
|
|
17
17
|
import { join } from "path";
|
|
@@ -60,6 +60,7 @@ var CLAUDE_HOOK_EVENTS = [
|
|
|
60
60
|
var supportedClaudeEvents = (version) => CLAUDE_HOOK_EVENTS.filter((hook) => version ? atLeast(version, hook.since) : hook.since === ESTABLISHED);
|
|
61
61
|
|
|
62
62
|
// src/claude-config.ts
|
|
63
|
+
import { isDeepStrictEqual } from "util";
|
|
63
64
|
var PUSHARY_MCP_URL = "https://pushary.com/api/mcp/mcp";
|
|
64
65
|
var PUSHARY_PERMISSION_RULE = "mcp__pushary__*";
|
|
65
66
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
@@ -85,14 +86,59 @@ var isPusharyHook = (entry2, scope) => {
|
|
|
85
86
|
return namesPusharyBinary(command);
|
|
86
87
|
});
|
|
87
88
|
};
|
|
89
|
+
var pusharyMcpServer = (apiKey) => ({
|
|
90
|
+
type: "http",
|
|
91
|
+
url: PUSHARY_MCP_URL,
|
|
92
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
93
|
+
});
|
|
88
94
|
var addClaudeMcpServer = (config, apiKey) => {
|
|
89
95
|
const mcpServers = ensureRecord(config, "mcpServers");
|
|
90
|
-
mcpServers.pushary =
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
mcpServers.pushary = pusharyMcpServer(apiKey);
|
|
97
|
+
};
|
|
98
|
+
var isClaudeMcpSnapshot = (value) => {
|
|
99
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
100
|
+
const snapshot = value;
|
|
101
|
+
return typeof snapshot.hadMcpServers === "boolean" && typeof snapshot.mcpServersWasRecord === "boolean" && (!snapshot.hadMcpServers || snapshot.mcpServersWasRecord || Object.hasOwn(snapshot, "mcpServers")) && typeof snapshot.hadPushary === "boolean" && (!snapshot.hadPushary || Object.hasOwn(snapshot, "pushary")) && (!Object.hasOwn(snapshot, "installedPushary") || asRecord(snapshot.installedPushary)?.type === "http" && asRecord(snapshot.installedPushary)?.url === PUSHARY_MCP_URL);
|
|
102
|
+
};
|
|
103
|
+
var captureClaudeMcpServer = (config, apiKey) => {
|
|
104
|
+
const mcpServers = asRecord(config.mcpServers);
|
|
105
|
+
const hadMcpServers = Object.hasOwn(config, "mcpServers");
|
|
106
|
+
return {
|
|
107
|
+
hadMcpServers,
|
|
108
|
+
mcpServersWasRecord: Boolean(mcpServers),
|
|
109
|
+
...hadMcpServers && !mcpServers ? { mcpServers: structuredClone(config.mcpServers) } : {},
|
|
110
|
+
hadPushary: Boolean(mcpServers && Object.hasOwn(mcpServers, "pushary")),
|
|
111
|
+
...mcpServers && Object.hasOwn(mcpServers, "pushary") ? { pushary: structuredClone(mcpServers.pushary) } : {},
|
|
112
|
+
...apiKey ? { installedPushary: pusharyMcpServer(apiKey) } : {}
|
|
94
113
|
};
|
|
95
114
|
};
|
|
115
|
+
var updateClaudeMcpSnapshot = (snapshot, apiKey) => ({
|
|
116
|
+
...snapshot,
|
|
117
|
+
installedPushary: pusharyMcpServer(apiKey)
|
|
118
|
+
});
|
|
119
|
+
var restoreClaudeMcpServer = (config, snapshot) => {
|
|
120
|
+
const mcpServers = asRecord(config.mcpServers);
|
|
121
|
+
const current = asRecord(mcpServers?.pushary);
|
|
122
|
+
if (Object.hasOwn(snapshot, "installedPushary")) {
|
|
123
|
+
if (!isDeepStrictEqual(mcpServers?.pushary, snapshot.installedPushary)) return false;
|
|
124
|
+
} else if (current?.type !== "http" || current.url !== PUSHARY_MCP_URL) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
if (!snapshot.mcpServersWasRecord) {
|
|
128
|
+
if (Object.keys(mcpServers).length === 1) {
|
|
129
|
+
if (snapshot.hadMcpServers) config.mcpServers = structuredClone(snapshot.mcpServers);
|
|
130
|
+
else delete config.mcpServers;
|
|
131
|
+
} else {
|
|
132
|
+
delete mcpServers.pushary;
|
|
133
|
+
}
|
|
134
|
+
} else if (snapshot.hadPushary) {
|
|
135
|
+
mcpServers.pushary = structuredClone(snapshot.pushary);
|
|
136
|
+
} else {
|
|
137
|
+
delete mcpServers.pushary;
|
|
138
|
+
if (!snapshot.hadMcpServers && Object.keys(mcpServers).length === 0) delete config.mcpServers;
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
};
|
|
96
142
|
var removeClaudeMcpServers = (config) => {
|
|
97
143
|
let changed = false;
|
|
98
144
|
const removeFrom = (target) => {
|
|
@@ -285,6 +331,10 @@ export {
|
|
|
285
331
|
parseVersion,
|
|
286
332
|
compareVersion,
|
|
287
333
|
addClaudeMcpServer,
|
|
334
|
+
isClaudeMcpSnapshot,
|
|
335
|
+
captureClaudeMcpServer,
|
|
336
|
+
updateClaudeMcpSnapshot,
|
|
337
|
+
restoreClaudeMcpServer,
|
|
288
338
|
removeClaudeMcpServers,
|
|
289
339
|
addPusharyToolPermissions,
|
|
290
340
|
addPusharyHooks,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
HOOK_BUDGETS,
|
|
3
3
|
HOOK_LOCATOR_BINARY
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-X6RXX2NF.js";
|
|
5
5
|
|
|
6
6
|
// src/codex-config.ts
|
|
7
7
|
import { createHash } from "crypto";
|
|
@@ -268,6 +268,8 @@ var vscodePluginDir = () => join2(homedir2(), ".pushary", "plugins", "vscode");
|
|
|
268
268
|
var vscodePluginMcp = () => join2(vscodePluginDir(), ".mcp.json");
|
|
269
269
|
var claudeSettingsLocal = () => join2(homedir2(), ".claude", "settings.local.json");
|
|
270
270
|
var pusharyDir = () => join2(homedir2(), ".pushary");
|
|
271
|
+
var claudeMcpReceipt = () => join2(pusharyDir(), "setup", "claude-mcp.json");
|
|
272
|
+
var hermesConfigReceipt = () => join2(pusharyDir(), "setup", "hermes-config.json");
|
|
271
273
|
var codexSkillDir2 = () => join2(codexHome(), "skills", "pushary");
|
|
272
274
|
var codexAgentsMd2 = () => join2(codexHome(), "AGENTS.md");
|
|
273
275
|
var geminiSettings = () => join2(homedir2(), ".gemini", "settings.json");
|
|
@@ -306,6 +308,8 @@ export {
|
|
|
306
308
|
vscodePluginMcp,
|
|
307
309
|
claudeSettingsLocal,
|
|
308
310
|
pusharyDir,
|
|
311
|
+
claudeMcpReceipt,
|
|
312
|
+
hermesConfigReceipt,
|
|
309
313
|
codexSkillDir2,
|
|
310
314
|
codexAgentsMd2,
|
|
311
315
|
geminiSettings,
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolvePathBinary
|
|
3
|
+
} from "./chunk-X4ZUUFKV.js";
|
|
4
|
+
|
|
5
|
+
// src/hermes-config.ts
|
|
6
|
+
import { execFileSync } from "child_process";
|
|
7
|
+
import { existsSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
8
|
+
import { homedir, tmpdir } from "os";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
11
|
+
var isSnapshot = (value) => isRecord(value) && typeof value.present === "boolean" && Object.hasOwn(value, "value");
|
|
12
|
+
var isHermesConfigReceipt = (value) => {
|
|
13
|
+
if (!isRecord(value)) return false;
|
|
14
|
+
return ["security", "approval", "transport", "fallback", "plugins", "enabled", "agent", "disabled"].every((key) => isSnapshot(value[key])) && typeof value.pushary_enabled === "boolean" && typeof value.clarify_disabled === "boolean";
|
|
15
|
+
};
|
|
16
|
+
var HERMES_CONFIG_SCRIPT = String.raw`
|
|
17
|
+
import json, os, sys
|
|
18
|
+
from hermes_cli.config import load_config, save_config
|
|
19
|
+
|
|
20
|
+
mode, receipt_path = sys.argv[1], sys.argv[2]
|
|
21
|
+
c = load_config()
|
|
22
|
+
|
|
23
|
+
def snap(obj, key):
|
|
24
|
+
return {"present": key in obj, "value": obj.get(key)}
|
|
25
|
+
|
|
26
|
+
restore_conflicts = []
|
|
27
|
+
|
|
28
|
+
def restore(obj, key, before, ours):
|
|
29
|
+
if before.get("present") and before.get("value") == ours:
|
|
30
|
+
return
|
|
31
|
+
if before.get("present"):
|
|
32
|
+
if key in obj and obj.get(key) == before.get("value"):
|
|
33
|
+
return
|
|
34
|
+
elif key not in obj:
|
|
35
|
+
return
|
|
36
|
+
if obj.get(key) != ours:
|
|
37
|
+
restore_conflicts.append(key)
|
|
38
|
+
return
|
|
39
|
+
if before.get("present"):
|
|
40
|
+
obj[key] = before.get("value")
|
|
41
|
+
else:
|
|
42
|
+
obj.pop(key, None)
|
|
43
|
+
|
|
44
|
+
def restore_container(obj, key, before, current, changed):
|
|
45
|
+
raw = obj.get(key)
|
|
46
|
+
if isinstance(before.get("value"), dict):
|
|
47
|
+
if not isinstance(raw, dict):
|
|
48
|
+
if changed:
|
|
49
|
+
restore_conflicts.append(key)
|
|
50
|
+
return
|
|
51
|
+
if current or before.get("present"):
|
|
52
|
+
obj[key] = current
|
|
53
|
+
else:
|
|
54
|
+
obj.pop(key, None)
|
|
55
|
+
elif isinstance(raw, dict) and current:
|
|
56
|
+
restore_conflicts.append(key)
|
|
57
|
+
else:
|
|
58
|
+
restore(obj, key, before, {})
|
|
59
|
+
|
|
60
|
+
def restore_list_member(obj, key, before, ours, ours_before):
|
|
61
|
+
raw = obj.get(key)
|
|
62
|
+
if isinstance(before.get("value"), list):
|
|
63
|
+
if ours_before:
|
|
64
|
+
return
|
|
65
|
+
if not isinstance(raw, list):
|
|
66
|
+
restore_conflicts.append(key)
|
|
67
|
+
return
|
|
68
|
+
remaining = [item for item in raw if item != ours]
|
|
69
|
+
if remaining or before.get("present"):
|
|
70
|
+
obj[key] = remaining
|
|
71
|
+
else:
|
|
72
|
+
obj.pop(key, None)
|
|
73
|
+
return
|
|
74
|
+
if before.get("present") and raw == before.get("value"):
|
|
75
|
+
return
|
|
76
|
+
if not before.get("present") and key not in obj:
|
|
77
|
+
return
|
|
78
|
+
if not isinstance(raw, list):
|
|
79
|
+
restore_conflicts.append(key)
|
|
80
|
+
return
|
|
81
|
+
remaining = [item for item in raw if item != ours]
|
|
82
|
+
if remaining:
|
|
83
|
+
restore_conflicts.append(key)
|
|
84
|
+
else:
|
|
85
|
+
restore(obj, key, before, raw)
|
|
86
|
+
|
|
87
|
+
def valid_snapshot(value):
|
|
88
|
+
return isinstance(value, dict) and isinstance(value.get("present"), bool) and "value" in value
|
|
89
|
+
|
|
90
|
+
def valid_receipt(value):
|
|
91
|
+
snapshots = ["security", "approval", "transport", "fallback", "plugins", "enabled", "agent", "disabled"]
|
|
92
|
+
return (isinstance(value, dict)
|
|
93
|
+
and all(valid_snapshot(value.get(key)) for key in snapshots)
|
|
94
|
+
and isinstance(value.get("pushary_enabled"), bool)
|
|
95
|
+
and isinstance(value.get("clarify_disabled"), bool))
|
|
96
|
+
|
|
97
|
+
if mode == "setup":
|
|
98
|
+
security_before = snap(c, "security")
|
|
99
|
+
s = c.get("security") if isinstance(c.get("security"), dict) else {}
|
|
100
|
+
approval_before = snap(s, "approval")
|
|
101
|
+
ap = s.get("approval") if isinstance(s.get("approval"), dict) else {}
|
|
102
|
+
plugins_before = snap(c, "plugins")
|
|
103
|
+
p = c.get("plugins") if isinstance(c.get("plugins"), dict) else {}
|
|
104
|
+
agent_before = snap(c, "agent")
|
|
105
|
+
a = c.get("agent") if isinstance(c.get("agent"), dict) else {}
|
|
106
|
+
enabled = p.get("enabled") if isinstance(p.get("enabled"), list) else []
|
|
107
|
+
disabled = a.get("disabled_toolsets") if isinstance(a.get("disabled_toolsets"), list) else []
|
|
108
|
+
receipt = {
|
|
109
|
+
"security": security_before,
|
|
110
|
+
"approval": approval_before,
|
|
111
|
+
"transport": snap(ap, "transport"),
|
|
112
|
+
"fallback": snap(ap, "transport_fallback"),
|
|
113
|
+
"plugins": plugins_before,
|
|
114
|
+
"enabled": snap(p, "enabled"),
|
|
115
|
+
"pushary_enabled": "pushary" in enabled,
|
|
116
|
+
"agent": agent_before,
|
|
117
|
+
"disabled": snap(a, "disabled_toolsets"),
|
|
118
|
+
"clarify_disabled": "clarify" in disabled,
|
|
119
|
+
}
|
|
120
|
+
if os.path.exists(receipt_path):
|
|
121
|
+
with open(receipt_path, encoding="utf-8") as handle:
|
|
122
|
+
existing_receipt = json.load(handle)
|
|
123
|
+
if not valid_receipt(existing_receipt):
|
|
124
|
+
raise ValueError("invalid Pushary Hermes setup receipt")
|
|
125
|
+
else:
|
|
126
|
+
os.makedirs(os.path.dirname(receipt_path), exist_ok=True)
|
|
127
|
+
temp = receipt_path + ".tmp"
|
|
128
|
+
with open(temp, "x", encoding="utf-8") as handle:
|
|
129
|
+
json.dump(receipt, handle)
|
|
130
|
+
os.chmod(temp, 0o600)
|
|
131
|
+
os.replace(temp, receipt_path)
|
|
132
|
+
ap["transport"] = "pushary"
|
|
133
|
+
ap["transport_fallback"] = "builtin"
|
|
134
|
+
s["approval"] = ap
|
|
135
|
+
c["security"] = s
|
|
136
|
+
p["enabled"] = enabled if "pushary" in enabled else enabled + ["pushary"]
|
|
137
|
+
c["plugins"] = p
|
|
138
|
+
a["disabled_toolsets"] = disabled if "clarify" in disabled else disabled + ["clarify"]
|
|
139
|
+
c["agent"] = a
|
|
140
|
+
else:
|
|
141
|
+
receipt_exists = os.path.exists(receipt_path)
|
|
142
|
+
try:
|
|
143
|
+
with open(receipt_path, encoding="utf-8") as handle:
|
|
144
|
+
receipt = json.load(handle)
|
|
145
|
+
except (OSError, ValueError):
|
|
146
|
+
if receipt_exists:
|
|
147
|
+
raise
|
|
148
|
+
receipt = None
|
|
149
|
+
s = c.get("security") if isinstance(c.get("security"), dict) else {}
|
|
150
|
+
ap = s.get("approval") if isinstance(s.get("approval"), dict) else {}
|
|
151
|
+
if receipt:
|
|
152
|
+
if not valid_receipt(receipt):
|
|
153
|
+
raise ValueError("invalid Pushary Hermes setup receipt")
|
|
154
|
+
approval_changed = (not receipt["transport"].get("present")
|
|
155
|
+
or receipt["transport"].get("value") != "pushary"
|
|
156
|
+
or not receipt["fallback"].get("present")
|
|
157
|
+
or receipt["fallback"].get("value") != "builtin")
|
|
158
|
+
plugins_changed = not (isinstance(receipt["enabled"].get("value"), list)
|
|
159
|
+
and receipt["pushary_enabled"])
|
|
160
|
+
agent_changed = not (isinstance(receipt["disabled"].get("value"), list)
|
|
161
|
+
and receipt["clarify_disabled"])
|
|
162
|
+
restore(ap, "transport", receipt["transport"], "pushary")
|
|
163
|
+
restore(ap, "transport_fallback", receipt["fallback"], "builtin")
|
|
164
|
+
else:
|
|
165
|
+
if ap.get("transport") == "pushary": ap.pop("transport", None)
|
|
166
|
+
if ap.get("transport_fallback") == "builtin": ap.pop("transport_fallback", None)
|
|
167
|
+
if receipt:
|
|
168
|
+
restore_container(s, "approval", receipt["approval"], ap, approval_changed)
|
|
169
|
+
restore_container(c, "security", receipt["security"], s, approval_changed)
|
|
170
|
+
else:
|
|
171
|
+
if ap: s["approval"] = ap
|
|
172
|
+
else: s.pop("approval", None)
|
|
173
|
+
if s: c["security"] = s
|
|
174
|
+
else: c.pop("security", None)
|
|
175
|
+
|
|
176
|
+
p = c.get("plugins") if isinstance(c.get("plugins"), dict) else {}
|
|
177
|
+
if receipt:
|
|
178
|
+
restore_list_member(p, "enabled", receipt["enabled"], "pushary", receipt["pushary_enabled"])
|
|
179
|
+
restore_container(c, "plugins", receipt["plugins"], p, plugins_changed)
|
|
180
|
+
else:
|
|
181
|
+
enabled = p.get("enabled") if isinstance(p.get("enabled"), list) else []
|
|
182
|
+
enabled = [item for item in enabled if item != "pushary"]
|
|
183
|
+
if enabled: p["enabled"] = enabled
|
|
184
|
+
else: p.pop("enabled", None)
|
|
185
|
+
if p: c["plugins"] = p
|
|
186
|
+
else: c.pop("plugins", None)
|
|
187
|
+
|
|
188
|
+
a = c.get("agent") if isinstance(c.get("agent"), dict) else {}
|
|
189
|
+
if receipt:
|
|
190
|
+
restore_list_member(a, "disabled_toolsets", receipt["disabled"], "clarify", receipt["clarify_disabled"])
|
|
191
|
+
restore_container(c, "agent", receipt["agent"], a, agent_changed)
|
|
192
|
+
else:
|
|
193
|
+
disabled = a.get("disabled_toolsets") if isinstance(a.get("disabled_toolsets"), list) else []
|
|
194
|
+
disabled = [item for item in disabled if item != "clarify"]
|
|
195
|
+
if disabled: a["disabled_toolsets"] = disabled
|
|
196
|
+
else: a.pop("disabled_toolsets", None)
|
|
197
|
+
if a: c["agent"] = a
|
|
198
|
+
else: c.pop("agent", None)
|
|
199
|
+
|
|
200
|
+
if receipt and restore_conflicts:
|
|
201
|
+
raise ValueError("Hermes config changed after Pushary setup")
|
|
202
|
+
|
|
203
|
+
save_config(c)
|
|
204
|
+
`;
|
|
205
|
+
var resolveHermesPython = () => {
|
|
206
|
+
const launcher = resolvePathBinary("hermes");
|
|
207
|
+
if (launcher && process.platform !== "win32") {
|
|
208
|
+
try {
|
|
209
|
+
const shebang = readFileSync(launcher, "utf-8").split("\n", 1)[0];
|
|
210
|
+
const interpreter = shebang?.startsWith("#!") ? shebang.slice(2).trim().split(/\s+/)[0] : void 0;
|
|
211
|
+
if (interpreter && /python/i.test(interpreter) && existsSync(interpreter)) return interpreter;
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const root = join(homedir(), ".hermes", "hermes-agent", "venv");
|
|
216
|
+
const candidates = process.platform === "win32" ? [join(root, "Scripts", "python.exe"), join(root, "Scripts", "python3.exe")] : [join(root, "bin", "python3"), join(root, "bin", "python")];
|
|
217
|
+
return candidates.find(existsSync) ?? null;
|
|
218
|
+
};
|
|
219
|
+
var runHermesPythonScript = (python, script, ...args) => {
|
|
220
|
+
const path = join(tmpdir(), `pushary-hermes-${process.pid}-${Date.now()}.py`);
|
|
221
|
+
try {
|
|
222
|
+
writeFileSync(path, script, { encoding: "utf-8", mode: 384 });
|
|
223
|
+
execFileSync(python, [path, ...args], { env: { ...process.env }, stdio: "pipe", timeout: 15e3 });
|
|
224
|
+
return true;
|
|
225
|
+
} catch {
|
|
226
|
+
return false;
|
|
227
|
+
} finally {
|
|
228
|
+
try {
|
|
229
|
+
rmSync(path, { force: true });
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export {
|
|
236
|
+
isHermesConfigReceipt,
|
|
237
|
+
HERMES_CONFIG_SCRIPT,
|
|
238
|
+
resolveHermesPython,
|
|
239
|
+
runHermesPythonScript
|
|
240
|
+
};
|
|
@@ -1080,6 +1080,9 @@ var shouldDeferToNativeMode = (permissionMode, toolName, options = {}) => {
|
|
|
1080
1080
|
if (permissionMode === "bypassPermissions") return options.deferBypass === true;
|
|
1081
1081
|
return false;
|
|
1082
1082
|
};
|
|
1083
|
+
var ASKED_IN_EVERY_MODE_TOOLS = ["AskUserQuestion", "ExitPlanMode"];
|
|
1084
|
+
var ASKED_IN_EVERY_MODE = new Set(ASKED_IN_EVERY_MODE_TOOLS);
|
|
1085
|
+
var isAskedInEveryMode = (toolName) => ASKED_IN_EVERY_MODE.has(toolName);
|
|
1083
1086
|
var resolveGate = (input) => {
|
|
1084
1087
|
const { modeState, config, toolName, toolInputs, cwd, repoKey, sessionId } = input;
|
|
1085
1088
|
if (modeState.kill) return { kind: "kill", reason: KILL_REASON };
|
|
@@ -1088,6 +1091,7 @@ var resolveGate = (input) => {
|
|
|
1088
1091
|
const autoApproves = verdict === "allow";
|
|
1089
1092
|
const autoDenies = verdict === "deny";
|
|
1090
1093
|
if (autoDenies) return { kind: "deny", policy, reason: `Denied by policy for ${policy.tool}` };
|
|
1094
|
+
if (isAskedInEveryMode(toolName)) return { kind: "gate", policy };
|
|
1091
1095
|
if (isModeStateDegraded(modeState)) {
|
|
1092
1096
|
if (autoApproves) {
|
|
1093
1097
|
return survivesDegraded(config, toolName, toolInputs, cwd, repoKey, sessionId) ? { kind: "allow", policy, source: "allowlist" } : { kind: "defer", policy };
|
|
@@ -1359,6 +1363,7 @@ export {
|
|
|
1359
1363
|
resolvePolicy,
|
|
1360
1364
|
KILL_REASON,
|
|
1361
1365
|
shouldDeferToNativeMode,
|
|
1366
|
+
ASKED_IN_EVERY_MODE_TOOLS,
|
|
1362
1367
|
resolveGate,
|
|
1363
1368
|
modeStateFromResponse,
|
|
1364
1369
|
claudePreToolUseOutput,
|
package/dist/src/index.d.ts
CHANGED
|
@@ -121,6 +121,7 @@ interface AskUserParams {
|
|
|
121
121
|
actionBody?: string;
|
|
122
122
|
scopePath?: string;
|
|
123
123
|
toolUseId?: string;
|
|
124
|
+
waitEndsAt?: string;
|
|
124
125
|
requestId?: string;
|
|
125
126
|
}
|
|
126
127
|
interface AskUserResponse {
|
|
@@ -132,6 +133,10 @@ interface AskUserResponse {
|
|
|
132
133
|
};
|
|
133
134
|
suppressed?: boolean;
|
|
134
135
|
noDevices?: boolean;
|
|
136
|
+
answered?: boolean;
|
|
137
|
+
value?: string;
|
|
138
|
+
note?: string;
|
|
139
|
+
waitEndsAt?: string;
|
|
135
140
|
answerUrl?: string;
|
|
136
141
|
}
|
|
137
142
|
interface AnswerResult {
|
package/dist/src/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handlePreToolUse
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
3
|
+
} from "../chunk-J4K75V4D.js";
|
|
4
|
+
import "../chunk-6EKQZA6R.js";
|
|
5
|
+
import "../chunk-NCVIWGVA.js";
|
|
6
|
+
import "../chunk-3HFK3UXM.js";
|
|
6
7
|
import "../chunk-YHG74UFF.js";
|
|
7
8
|
import {
|
|
8
9
|
fetchModeOverride,
|
|
@@ -12,18 +13,18 @@ import {
|
|
|
12
13
|
handlePostToolUse,
|
|
13
14
|
handleStop,
|
|
14
15
|
reportEvent
|
|
15
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-FXIL7RCL.js";
|
|
16
17
|
import {
|
|
17
18
|
askUser,
|
|
18
19
|
cancelQuestion,
|
|
19
20
|
waitForAnswer
|
|
20
|
-
} from "../chunk-
|
|
21
|
-
import "../chunk-
|
|
22
|
-
import "../chunk-
|
|
23
|
-
import "../chunk-
|
|
21
|
+
} from "../chunk-JHJHCMAF.js";
|
|
22
|
+
import "../chunk-TG3T3M4K.js";
|
|
23
|
+
import "../chunk-DKWM5T3P.js";
|
|
24
|
+
import "../chunk-QA72MKIT.js";
|
|
24
25
|
import "../chunk-BSZYIAZL.js";
|
|
25
26
|
import "../chunk-SAF6HGAA.js";
|
|
26
|
-
import "../chunk-
|
|
27
|
+
import "../chunk-IUWRVTQK.js";
|
|
27
28
|
import "../chunk-7QLSKOSU.js";
|
|
28
29
|
import "../chunk-KZERVKTD.js";
|
|
29
30
|
import "../chunk-XX2V4QS5.js";
|
|
@@ -33,7 +34,7 @@ import {
|
|
|
33
34
|
} from "../chunk-2UMNXADU.js";
|
|
34
35
|
import {
|
|
35
36
|
resolvePolicy
|
|
36
|
-
} from "../chunk-
|
|
37
|
+
} from "../chunk-X6RXX2NF.js";
|
|
37
38
|
export {
|
|
38
39
|
askUser,
|
|
39
40
|
cancelQuestion,
|