@pushary/agent-hooks 0.94.0 → 0.95.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/data/SKILL.md +31 -24
- package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
- package/data/cursor-plugin/CONTRIBUTING.md +31 -19
- package/data/cursor-plugin/skills/pushary/SKILL.md +30 -23
- package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
- package/data/vscode-plugin/CONTRIBUTING.md +31 -19
- package/data/vscode-plugin/skills/pushary/SKILL.md +30 -23
- package/dist/bin/pushary-bell-hook.js +26 -5
- package/dist/bin/pushary-bell.js +8 -8
- package/dist/bin/pushary-claude.js +343 -365
- package/dist/bin/pushary-clean.js +28 -23
- package/dist/bin/pushary-codex-bridge.js +12 -12
- package/dist/bin/pushary-codex-hook.js +28 -10
- package/dist/bin/pushary-codex.js +7 -7
- package/dist/bin/pushary-connect.js +9 -9
- package/dist/bin/pushary-daemon.js +41 -26
- package/dist/bin/pushary-disconnect.js +8 -6
- package/dist/bin/pushary-doctor.js +42 -39
- package/dist/bin/pushary-elicitation-hook.js +24 -4
- package/dist/bin/pushary-gemini-bridge.js +12 -12
- package/dist/bin/pushary-gemini-hook.js +27 -9
- package/dist/bin/pushary-hook.js +28 -11
- package/dist/bin/pushary-login.js +10 -10
- package/dist/bin/pushary-logout.js +7 -7
- package/dist/bin/pushary-mode.js +6 -6
- package/dist/bin/pushary-notification-hook.js +26 -8
- package/dist/bin/pushary-opencode-hook.js +27 -9
- package/dist/bin/pushary-permission-denied-hook.js +29 -11
- package/dist/bin/pushary-permission-hook.js +29 -11
- package/dist/bin/pushary-post-hook.js +26 -8
- package/dist/bin/pushary-prompt-hook.js +26 -8
- package/dist/bin/pushary-session-end-hook.js +30 -9
- package/dist/bin/pushary-session-start-hook.js +29 -14
- package/dist/bin/pushary-setup.js +59 -53
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +11 -11
- package/dist/bin/pushary-stop-hook.js +26 -8
- package/dist/bin/pushary-stopfailure-hook.js +26 -8
- package/dist/bin/pushary-suggestions.js +3 -3
- package/dist/bin/pushary-transcript-register.d.ts +1 -0
- package/dist/bin/pushary-transcript-register.js +42 -0
- package/dist/bin/pushary-transcripts.js +4 -4
- package/dist/bin/pushary-upgrade.js +19 -18
- package/dist/bin/pushary-wait.js +6 -6
- package/dist/{chunk-Y6XXFXVB.js → chunk-3F4VV5TS.js} +1 -1
- package/dist/chunk-77U3ZVV6.js +86 -0
- package/dist/{chunk-RH3TM7TO.js → chunk-7MGF5Q7G.js} +1 -1
- package/dist/{chunk-WY4ALDYN.js → chunk-AQ5OAYOH.js} +1 -1
- package/dist/chunk-BWNU4MX4.js +185 -0
- package/dist/{chunk-OYNPLEXP.js → chunk-C2MV4G4A.js} +1 -1
- package/dist/chunk-CC625NRG.js +503 -0
- package/dist/{chunk-ZTJF6ANY.js → chunk-CU5KI425.js} +2 -2
- package/dist/{chunk-HSAVPZ46.js → chunk-IFCS6LXB.js} +3 -3
- package/dist/{chunk-36LR2LOT.js → chunk-IP63LMZM.js} +10 -8
- package/dist/{chunk-BSEAOCCQ.js → chunk-IZRI4M3C.js} +1 -1
- package/dist/{chunk-5HGLZGI4.js → chunk-J55K6YS4.js} +6 -5
- package/dist/{chunk-6BIMJIRG.js → chunk-JSASMV3H.js} +1 -1
- package/dist/{chunk-6CUUA7HO.js → chunk-K7ZZGI2Y.js} +7 -1
- package/dist/{chunk-TDUTNYAQ.js → chunk-M64I4T7J.js} +1 -1
- package/dist/{chunk-ZO2XG3CX.js → chunk-MF64S7WO.js} +1 -1
- package/dist/chunk-N6FAXQDD.js +276 -0
- package/dist/{chunk-UZFVNEEU.js → chunk-PDFVIRPB.js} +1 -1
- package/dist/chunk-PQ4K74WL.js +30 -0
- package/dist/{chunk-ROHFVGXM.js → chunk-R7NPGCSD.js} +1 -1
- package/dist/{chunk-QGA2ZB4I.js → chunk-RNH2AE52.js} +1 -1
- package/dist/{chunk-NVPYRDCT.js → chunk-SSQ5X6SA.js} +1 -1
- package/dist/{chunk-OB4RF2TF.js → chunk-T23EGPRI.js} +1 -1
- package/dist/{chunk-ODL52O5V.js → chunk-T5B3LRYF.js} +1 -1
- package/dist/{chunk-Y3UGCQQC.js → chunk-TTO76NZJ.js} +1 -1
- package/dist/{chunk-46P3VES5.js → chunk-UFGDCMUD.js} +2 -1
- package/dist/{chunk-2ORRI66J.js → chunk-UQSKCQBO.js} +1 -1
- package/dist/{chunk-4PE2SQVM.js → chunk-VLKEAYJ4.js} +1 -1
- package/dist/chunk-WJZUTMMU.js +194 -0
- package/dist/{chunk-QKNLW7H6.js → chunk-Y24D5ZWS.js} +7 -7
- package/dist/{chunk-YANV5TGK.js → chunk-YPYPI6BG.js} +8 -90
- package/dist/{chunk-6WYL3XIP.js → chunk-ZOR5OJZJ.js} +4 -3
- package/dist/chunk-ZW3KAWTM.js +266 -0
- package/dist/src/index.js +11 -11
- package/package.json +3 -2
- package/dist/chunk-FYOIW5RV.js +0 -539
- package/dist/chunk-RX75MRGC.js +0 -368
- /package/dist/{chunk-Z6CP5ZZD.js → chunk-VOWZ5VOO.js} +0 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolvePathBinary
|
|
3
|
+
} from "./chunk-L4ZCFMWW.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_PIP_PACKAGE = "hermes-plugin-pushary";
|
|
17
|
+
var HERMES_CONFIG_SCRIPT = String.raw`
|
|
18
|
+
import json, os, sys
|
|
19
|
+
from hermes_cli.config import load_config, save_config
|
|
20
|
+
|
|
21
|
+
mode, receipt_path = sys.argv[1], sys.argv[2]
|
|
22
|
+
c = load_config()
|
|
23
|
+
|
|
24
|
+
def snap(obj, key):
|
|
25
|
+
return {"present": key in obj, "value": obj.get(key)}
|
|
26
|
+
|
|
27
|
+
restore_conflicts = []
|
|
28
|
+
|
|
29
|
+
def restore(obj, key, before, ours):
|
|
30
|
+
if before.get("present") and before.get("value") == ours:
|
|
31
|
+
return
|
|
32
|
+
if before.get("present"):
|
|
33
|
+
if key in obj and obj.get(key) == before.get("value"):
|
|
34
|
+
return
|
|
35
|
+
elif key not in obj:
|
|
36
|
+
return
|
|
37
|
+
if obj.get(key) != ours:
|
|
38
|
+
restore_conflicts.append(key)
|
|
39
|
+
return
|
|
40
|
+
if before.get("present"):
|
|
41
|
+
obj[key] = before.get("value")
|
|
42
|
+
else:
|
|
43
|
+
obj.pop(key, None)
|
|
44
|
+
|
|
45
|
+
def restore_container(obj, key, before, current, changed):
|
|
46
|
+
raw = obj.get(key)
|
|
47
|
+
if isinstance(before.get("value"), dict):
|
|
48
|
+
if not isinstance(raw, dict):
|
|
49
|
+
if changed:
|
|
50
|
+
restore_conflicts.append(key)
|
|
51
|
+
return
|
|
52
|
+
if current or before.get("present"):
|
|
53
|
+
obj[key] = current
|
|
54
|
+
else:
|
|
55
|
+
obj.pop(key, None)
|
|
56
|
+
elif isinstance(raw, dict) and current:
|
|
57
|
+
restore_conflicts.append(key)
|
|
58
|
+
else:
|
|
59
|
+
restore(obj, key, before, {})
|
|
60
|
+
|
|
61
|
+
def restore_list_member(obj, key, before, ours, ours_before):
|
|
62
|
+
raw = obj.get(key)
|
|
63
|
+
if isinstance(before.get("value"), list):
|
|
64
|
+
if ours_before:
|
|
65
|
+
return
|
|
66
|
+
if not isinstance(raw, list):
|
|
67
|
+
restore_conflicts.append(key)
|
|
68
|
+
return
|
|
69
|
+
remaining = [item for item in raw if item != ours]
|
|
70
|
+
if remaining or before.get("present"):
|
|
71
|
+
obj[key] = remaining
|
|
72
|
+
else:
|
|
73
|
+
obj.pop(key, None)
|
|
74
|
+
return
|
|
75
|
+
if before.get("present") and raw == before.get("value"):
|
|
76
|
+
return
|
|
77
|
+
if not before.get("present") and key not in obj:
|
|
78
|
+
return
|
|
79
|
+
if not isinstance(raw, list):
|
|
80
|
+
restore_conflicts.append(key)
|
|
81
|
+
return
|
|
82
|
+
remaining = [item for item in raw if item != ours]
|
|
83
|
+
if remaining:
|
|
84
|
+
restore_conflicts.append(key)
|
|
85
|
+
else:
|
|
86
|
+
restore(obj, key, before, raw)
|
|
87
|
+
|
|
88
|
+
def valid_snapshot(value):
|
|
89
|
+
return isinstance(value, dict) and isinstance(value.get("present"), bool) and "value" in value
|
|
90
|
+
|
|
91
|
+
def valid_receipt(value):
|
|
92
|
+
snapshots = ["security", "approval", "transport", "fallback", "plugins", "enabled", "agent", "disabled"]
|
|
93
|
+
return (isinstance(value, dict)
|
|
94
|
+
and all(valid_snapshot(value.get(key)) for key in snapshots)
|
|
95
|
+
and isinstance(value.get("pushary_enabled"), bool)
|
|
96
|
+
and isinstance(value.get("clarify_disabled"), bool))
|
|
97
|
+
|
|
98
|
+
if mode == "setup":
|
|
99
|
+
security_before = snap(c, "security")
|
|
100
|
+
s = c.get("security") if isinstance(c.get("security"), dict) else {}
|
|
101
|
+
approval_before = snap(s, "approval")
|
|
102
|
+
ap = s.get("approval") if isinstance(s.get("approval"), dict) else {}
|
|
103
|
+
plugins_before = snap(c, "plugins")
|
|
104
|
+
p = c.get("plugins") if isinstance(c.get("plugins"), dict) else {}
|
|
105
|
+
agent_before = snap(c, "agent")
|
|
106
|
+
a = c.get("agent") if isinstance(c.get("agent"), dict) else {}
|
|
107
|
+
enabled = p.get("enabled") if isinstance(p.get("enabled"), list) else []
|
|
108
|
+
disabled = a.get("disabled_toolsets") if isinstance(a.get("disabled_toolsets"), list) else []
|
|
109
|
+
receipt = {
|
|
110
|
+
"security": security_before,
|
|
111
|
+
"approval": approval_before,
|
|
112
|
+
"transport": snap(ap, "transport"),
|
|
113
|
+
"fallback": snap(ap, "transport_fallback"),
|
|
114
|
+
"plugins": plugins_before,
|
|
115
|
+
"enabled": snap(p, "enabled"),
|
|
116
|
+
"pushary_enabled": "pushary" in enabled,
|
|
117
|
+
"agent": agent_before,
|
|
118
|
+
"disabled": snap(a, "disabled_toolsets"),
|
|
119
|
+
"clarify_disabled": "clarify" in disabled,
|
|
120
|
+
}
|
|
121
|
+
if os.path.exists(receipt_path):
|
|
122
|
+
with open(receipt_path, encoding="utf-8") as handle:
|
|
123
|
+
existing_receipt = json.load(handle)
|
|
124
|
+
if not valid_receipt(existing_receipt):
|
|
125
|
+
raise ValueError("invalid Pushary Hermes setup receipt")
|
|
126
|
+
else:
|
|
127
|
+
os.makedirs(os.path.dirname(receipt_path), exist_ok=True)
|
|
128
|
+
temp = receipt_path + ".tmp"
|
|
129
|
+
with open(temp, "x", encoding="utf-8") as handle:
|
|
130
|
+
json.dump(receipt, handle)
|
|
131
|
+
os.chmod(temp, 0o600)
|
|
132
|
+
os.replace(temp, receipt_path)
|
|
133
|
+
ap["transport"] = "pushary"
|
|
134
|
+
ap["transport_fallback"] = "builtin"
|
|
135
|
+
s["approval"] = ap
|
|
136
|
+
c["security"] = s
|
|
137
|
+
p["enabled"] = enabled if "pushary" in enabled else enabled + ["pushary"]
|
|
138
|
+
c["plugins"] = p
|
|
139
|
+
a["disabled_toolsets"] = disabled if "clarify" in disabled else disabled + ["clarify"]
|
|
140
|
+
c["agent"] = a
|
|
141
|
+
else:
|
|
142
|
+
receipt_exists = os.path.exists(receipt_path)
|
|
143
|
+
try:
|
|
144
|
+
with open(receipt_path, encoding="utf-8") as handle:
|
|
145
|
+
receipt = json.load(handle)
|
|
146
|
+
except (OSError, ValueError):
|
|
147
|
+
if receipt_exists:
|
|
148
|
+
raise
|
|
149
|
+
receipt = None
|
|
150
|
+
s = c.get("security") if isinstance(c.get("security"), dict) else {}
|
|
151
|
+
ap = s.get("approval") if isinstance(s.get("approval"), dict) else {}
|
|
152
|
+
if receipt:
|
|
153
|
+
if not valid_receipt(receipt):
|
|
154
|
+
raise ValueError("invalid Pushary Hermes setup receipt")
|
|
155
|
+
approval_changed = (not receipt["transport"].get("present")
|
|
156
|
+
or receipt["transport"].get("value") != "pushary"
|
|
157
|
+
or not receipt["fallback"].get("present")
|
|
158
|
+
or receipt["fallback"].get("value") != "builtin")
|
|
159
|
+
plugins_changed = not (isinstance(receipt["enabled"].get("value"), list)
|
|
160
|
+
and receipt["pushary_enabled"])
|
|
161
|
+
agent_changed = not (isinstance(receipt["disabled"].get("value"), list)
|
|
162
|
+
and receipt["clarify_disabled"])
|
|
163
|
+
restore(ap, "transport", receipt["transport"], "pushary")
|
|
164
|
+
restore(ap, "transport_fallback", receipt["fallback"], "builtin")
|
|
165
|
+
else:
|
|
166
|
+
if ap.get("transport") == "pushary": ap.pop("transport", None)
|
|
167
|
+
if ap.get("transport_fallback") == "builtin": ap.pop("transport_fallback", None)
|
|
168
|
+
if receipt:
|
|
169
|
+
restore_container(s, "approval", receipt["approval"], ap, approval_changed)
|
|
170
|
+
restore_container(c, "security", receipt["security"], s, approval_changed)
|
|
171
|
+
else:
|
|
172
|
+
if ap: s["approval"] = ap
|
|
173
|
+
else: s.pop("approval", None)
|
|
174
|
+
if s: c["security"] = s
|
|
175
|
+
else: c.pop("security", None)
|
|
176
|
+
|
|
177
|
+
p = c.get("plugins") if isinstance(c.get("plugins"), dict) else {}
|
|
178
|
+
if receipt:
|
|
179
|
+
restore_list_member(p, "enabled", receipt["enabled"], "pushary", receipt["pushary_enabled"])
|
|
180
|
+
restore_container(c, "plugins", receipt["plugins"], p, plugins_changed)
|
|
181
|
+
else:
|
|
182
|
+
enabled = p.get("enabled") if isinstance(p.get("enabled"), list) else []
|
|
183
|
+
enabled = [item for item in enabled if item != "pushary"]
|
|
184
|
+
if enabled: p["enabled"] = enabled
|
|
185
|
+
else: p.pop("enabled", None)
|
|
186
|
+
if p: c["plugins"] = p
|
|
187
|
+
else: c.pop("plugins", None)
|
|
188
|
+
|
|
189
|
+
a = c.get("agent") if isinstance(c.get("agent"), dict) else {}
|
|
190
|
+
if receipt:
|
|
191
|
+
restore_list_member(a, "disabled_toolsets", receipt["disabled"], "clarify", receipt["clarify_disabled"])
|
|
192
|
+
restore_container(c, "agent", receipt["agent"], a, agent_changed)
|
|
193
|
+
else:
|
|
194
|
+
disabled = a.get("disabled_toolsets") if isinstance(a.get("disabled_toolsets"), list) else []
|
|
195
|
+
disabled = [item for item in disabled if item != "clarify"]
|
|
196
|
+
if disabled: a["disabled_toolsets"] = disabled
|
|
197
|
+
else: a.pop("disabled_toolsets", None)
|
|
198
|
+
if a: c["agent"] = a
|
|
199
|
+
else: c.pop("agent", None)
|
|
200
|
+
|
|
201
|
+
if receipt and restore_conflicts:
|
|
202
|
+
raise ValueError("Hermes config changed after Pushary setup")
|
|
203
|
+
|
|
204
|
+
save_config(c)
|
|
205
|
+
`;
|
|
206
|
+
var resolveHermesPython = () => {
|
|
207
|
+
const launcher = resolvePathBinary("hermes");
|
|
208
|
+
if (launcher && process.platform !== "win32") {
|
|
209
|
+
try {
|
|
210
|
+
const shebang = readFileSync(launcher, "utf-8").split("\n", 1)[0];
|
|
211
|
+
const interpreter = shebang?.startsWith("#!") ? shebang.slice(2).trim().split(/\s+/)[0] : void 0;
|
|
212
|
+
if (interpreter && /python/i.test(interpreter) && existsSync(interpreter)) return interpreter;
|
|
213
|
+
} catch {
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const root = join(homedir(), ".hermes", "hermes-agent", "venv");
|
|
217
|
+
const candidates = process.platform === "win32" ? [join(root, "Scripts", "python.exe"), join(root, "Scripts", "python3.exe")] : [join(root, "bin", "python3"), join(root, "bin", "python")];
|
|
218
|
+
return candidates.find(existsSync) ?? null;
|
|
219
|
+
};
|
|
220
|
+
var runHermesPythonScript = (python, script, ...args) => {
|
|
221
|
+
const path = join(tmpdir(), `pushary-hermes-${process.pid}-${Date.now()}.py`);
|
|
222
|
+
try {
|
|
223
|
+
writeFileSync(path, script, { encoding: "utf-8", mode: 384 });
|
|
224
|
+
execFileSync(python, [path, ...args], { env: { ...process.env }, stdio: "pipe", timeout: 15e3 });
|
|
225
|
+
return true;
|
|
226
|
+
} catch {
|
|
227
|
+
return false;
|
|
228
|
+
} finally {
|
|
229
|
+
try {
|
|
230
|
+
rmSync(path, { force: true });
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// src/cli/dry-run.ts
|
|
237
|
+
import { execSync } from "child_process";
|
|
238
|
+
import { rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
239
|
+
var createGuards = (isDryRun, note) => ({
|
|
240
|
+
write: (path, contents) => {
|
|
241
|
+
if (isDryRun()) return note("rewrite", path);
|
|
242
|
+
writeFileSync2(path, contents, "utf-8");
|
|
243
|
+
},
|
|
244
|
+
remove: (path, options = {}) => {
|
|
245
|
+
if (isDryRun()) return note("remove ", path);
|
|
246
|
+
rmSync2(path, { recursive: true, force: options.force ?? false });
|
|
247
|
+
},
|
|
248
|
+
exec: (command, options, describe) => {
|
|
249
|
+
if (isDryRun()) return note("run ", describe);
|
|
250
|
+
execSync(command, options);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
var describeService = (effects) => {
|
|
254
|
+
if (effects.controlMode === "off") return "skip the phone-start background service (--control off)";
|
|
255
|
+
const where = effects.serviceDefinitionPath ?? "the phone-start service definition";
|
|
256
|
+
const condition = effects.controlMode === "on" ? "" : ", when a supported agent and a service manager are present";
|
|
257
|
+
return `write ${where} and register it with the OS${condition}`;
|
|
258
|
+
};
|
|
259
|
+
var describeSetupSideEffects = (effects) => [
|
|
260
|
+
`run npm install -g @pushary/agent-hooks@${effects.version} (the hooks run from the global copy; up to two minutes)`,
|
|
261
|
+
`write ${effects.configFile} (the API key, mode 600)`,
|
|
262
|
+
effects.rcPath ? `edit ${effects.rcPath} (export PUSHARY_API_KEY)` : "print the export line for your shell, since no rc file was found",
|
|
263
|
+
describeService(effects),
|
|
264
|
+
"ask Add Pushary instructions to this project's CLAUDE.md/AGENTS.md? (default no)",
|
|
265
|
+
"ask Alias claude to pushary claude? (default no)"
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
export {
|
|
269
|
+
isHermesConfigReceipt,
|
|
270
|
+
HERMES_PIP_PACKAGE,
|
|
271
|
+
HERMES_CONFIG_SCRIPT,
|
|
272
|
+
resolveHermesPython,
|
|
273
|
+
runHermesPythonScript,
|
|
274
|
+
createGuards,
|
|
275
|
+
describeSetupSideEffects
|
|
276
|
+
};
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
isValidApiKey,
|
|
15
15
|
normalizeKeyScope
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VOWZ5VOO.js";
|
|
17
17
|
|
|
18
18
|
// src/session.ts
|
|
19
19
|
var samePusharyAccount = (left, right) => left.kind === "ok" && right.kind === "ok" && left.identity.workspaceId !== "" && left.identity.workspaceId === right.identity.workspaceId && left.identity.siteId === right.identity.siteId;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/wrapper/claudeBinary.ts
|
|
2
|
+
import { statSync } from "fs";
|
|
3
|
+
import { join, delimiter } from "path";
|
|
4
|
+
var WRAPPER_ACTIVE_ENV = "PUSHARY_WRAPPER_ACTIVE";
|
|
5
|
+
var isFile = (path) => {
|
|
6
|
+
try {
|
|
7
|
+
return statSync(path).isFile();
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var findClaudeBinary = () => {
|
|
13
|
+
const override = process.env.PUSHARY_CLAUDE_BIN?.trim();
|
|
14
|
+
if (override) return isFile(override) ? override : null;
|
|
15
|
+
const pathVar = process.env.PATH ?? "";
|
|
16
|
+
const exts = process.platform === "win32" ? [".cmd", ".exe", ".bat", ""] : [""];
|
|
17
|
+
for (const dir of pathVar.split(delimiter)) {
|
|
18
|
+
if (!dir) continue;
|
|
19
|
+
for (const ext of exts) {
|
|
20
|
+
const candidate = join(dir, `claude${ext}`);
|
|
21
|
+
if (isFile(candidate)) return candidate;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
WRAPPER_ACTIVE_ENV,
|
|
29
|
+
findClaudeBinary
|
|
30
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
redactSecrets
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VOWZ5VOO.js";
|
|
4
4
|
|
|
5
5
|
// src/crypto.ts
|
|
6
6
|
import nacl from "tweetnacl";
|
|
@@ -152,5 +152,6 @@ export {
|
|
|
152
152
|
decodePublicKey,
|
|
153
153
|
generateSessionKey,
|
|
154
154
|
wrapSessionKey,
|
|
155
|
+
encryptRecord,
|
|
155
156
|
buildTranscriptRecords
|
|
156
157
|
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLAUDE_HOOK_EVENTS,
|
|
3
|
+
supportedClaudeEvents
|
|
4
|
+
} from "./chunk-BWNU4MX4.js";
|
|
5
|
+
import {
|
|
6
|
+
guardBinary
|
|
7
|
+
} from "./chunk-EZIEXHYM.js";
|
|
8
|
+
import {
|
|
9
|
+
isProtectedByScope
|
|
10
|
+
} from "./chunk-3F4VV5TS.js";
|
|
11
|
+
import {
|
|
12
|
+
HOOK_LOCATOR_BINARY
|
|
13
|
+
} from "./chunk-VOWZ5VOO.js";
|
|
14
|
+
|
|
15
|
+
// src/claude-config.ts
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
import { isDeepStrictEqual } from "util";
|
|
18
|
+
var PUSHARY_MCP_URL = "https://pushary.com/api/mcp/mcp";
|
|
19
|
+
var PUSHARY_PERMISSION_RULE = "mcp__pushary__*";
|
|
20
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
21
|
+
var ensureRecord = (target, key) => {
|
|
22
|
+
const existing = asRecord(target[key]);
|
|
23
|
+
if (existing) return existing;
|
|
24
|
+
const created = {};
|
|
25
|
+
target[key] = created;
|
|
26
|
+
return created;
|
|
27
|
+
};
|
|
28
|
+
var isPusharyPermission = (rule) => typeof rule === "string" && (rule.includes("pushary") || rule.includes("MCP(pushary"));
|
|
29
|
+
var HOOK_BINARIES = /* @__PURE__ */ new Set([
|
|
30
|
+
...CLAUDE_HOOK_EVENTS.map((hook) => hook.binary),
|
|
31
|
+
HOOK_LOCATOR_BINARY
|
|
32
|
+
]);
|
|
33
|
+
var namesPusharyBinary = (command) => command.split(/[\s/\\"']+/).some((segment) => HOOK_BINARIES.has(segment.replace(/\.(cmd|exe)$/i, "")));
|
|
34
|
+
var isPusharyHook = (entry2, scope) => {
|
|
35
|
+
const hooks = asRecord(entry2)?.hooks;
|
|
36
|
+
if (!Array.isArray(hooks)) return false;
|
|
37
|
+
return hooks.some((hook) => {
|
|
38
|
+
const command = String(asRecord(hook)?.command ?? "");
|
|
39
|
+
if (isProtectedByScope(command, scope)) return false;
|
|
40
|
+
return namesPusharyBinary(command);
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
var pusharyMcpServer = (apiKey) => ({
|
|
44
|
+
type: "http",
|
|
45
|
+
url: PUSHARY_MCP_URL,
|
|
46
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
47
|
+
});
|
|
48
|
+
var addClaudeMcpServer = (config, apiKey) => {
|
|
49
|
+
const mcpServers = ensureRecord(config, "mcpServers");
|
|
50
|
+
mcpServers.pushary = pusharyMcpServer(apiKey);
|
|
51
|
+
};
|
|
52
|
+
var isClaudeMcpSnapshot = (value) => {
|
|
53
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
54
|
+
const snapshot = value;
|
|
55
|
+
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);
|
|
56
|
+
};
|
|
57
|
+
var captureClaudeMcpServer = (config, apiKey) => {
|
|
58
|
+
const mcpServers = asRecord(config.mcpServers);
|
|
59
|
+
const hadMcpServers = Object.hasOwn(config, "mcpServers");
|
|
60
|
+
return {
|
|
61
|
+
hadMcpServers,
|
|
62
|
+
mcpServersWasRecord: Boolean(mcpServers),
|
|
63
|
+
...hadMcpServers && !mcpServers ? { mcpServers: structuredClone(config.mcpServers) } : {},
|
|
64
|
+
hadPushary: Boolean(mcpServers && Object.hasOwn(mcpServers, "pushary")),
|
|
65
|
+
...mcpServers && Object.hasOwn(mcpServers, "pushary") ? { pushary: structuredClone(mcpServers.pushary) } : {},
|
|
66
|
+
...apiKey ? { installedPushary: pusharyMcpServer(apiKey) } : {}
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
var updateClaudeMcpSnapshot = (snapshot, apiKey) => ({
|
|
70
|
+
...snapshot,
|
|
71
|
+
installedPushary: pusharyMcpServer(apiKey)
|
|
72
|
+
});
|
|
73
|
+
var restoreClaudeMcpServer = (config, snapshot) => {
|
|
74
|
+
const mcpServers = asRecord(config.mcpServers);
|
|
75
|
+
const current = asRecord(mcpServers?.pushary);
|
|
76
|
+
if (Object.hasOwn(snapshot, "installedPushary")) {
|
|
77
|
+
if (!isDeepStrictEqual(mcpServers?.pushary, snapshot.installedPushary)) return false;
|
|
78
|
+
} else if (current?.type !== "http" || current.url !== PUSHARY_MCP_URL) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if (!snapshot.mcpServersWasRecord) {
|
|
82
|
+
if (Object.keys(mcpServers).length === 1) {
|
|
83
|
+
if (snapshot.hadMcpServers) config.mcpServers = structuredClone(snapshot.mcpServers);
|
|
84
|
+
else delete config.mcpServers;
|
|
85
|
+
} else {
|
|
86
|
+
delete mcpServers.pushary;
|
|
87
|
+
}
|
|
88
|
+
} else if (snapshot.hadPushary) {
|
|
89
|
+
mcpServers.pushary = structuredClone(snapshot.pushary);
|
|
90
|
+
} else {
|
|
91
|
+
delete mcpServers.pushary;
|
|
92
|
+
if (!snapshot.hadMcpServers && Object.keys(mcpServers).length === 0) delete config.mcpServers;
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
};
|
|
96
|
+
var removeClaudeMcpServers = (config) => {
|
|
97
|
+
let changed = false;
|
|
98
|
+
const removeFrom = (target) => {
|
|
99
|
+
const mcpServers = asRecord(target.mcpServers);
|
|
100
|
+
if (!mcpServers?.pushary) return;
|
|
101
|
+
delete mcpServers.pushary;
|
|
102
|
+
if (Object.keys(mcpServers).length === 0) delete target.mcpServers;
|
|
103
|
+
changed = true;
|
|
104
|
+
};
|
|
105
|
+
removeFrom(config);
|
|
106
|
+
const projects = asRecord(config.projects);
|
|
107
|
+
if (projects) {
|
|
108
|
+
for (const project of Object.values(projects)) {
|
|
109
|
+
const projectConfig = asRecord(project);
|
|
110
|
+
if (projectConfig) removeFrom(projectConfig);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return changed;
|
|
114
|
+
};
|
|
115
|
+
var addPusharyToolPermissions = (settings) => {
|
|
116
|
+
const permissions = ensureRecord(settings, "permissions");
|
|
117
|
+
const allow = Array.isArray(permissions.allow) ? permissions.allow : [];
|
|
118
|
+
const filtered = allow.filter((rule) => !isPusharyPermission(rule));
|
|
119
|
+
if (!filtered.includes(PUSHARY_PERMISSION_RULE)) {
|
|
120
|
+
filtered.push(PUSHARY_PERMISSION_RULE);
|
|
121
|
+
}
|
|
122
|
+
permissions.allow = filtered;
|
|
123
|
+
};
|
|
124
|
+
var addPusharyHooks = (settings, binDir, version = null) => {
|
|
125
|
+
const resolve = (name) => guardBinary(binDir ? join(binDir, name) : name);
|
|
126
|
+
const hooks = ensureRecord(settings, "hooks");
|
|
127
|
+
const supported = new Map(supportedClaudeEvents(version).map((hook) => [hook.event, hook]));
|
|
128
|
+
for (const known of CLAUDE_HOOK_EVENTS) {
|
|
129
|
+
const current = hooks[known.event];
|
|
130
|
+
const others = (Array.isArray(current) ? current : []).filter((item) => !isPusharyHook(item));
|
|
131
|
+
const definition = supported.get(known.event);
|
|
132
|
+
if (definition) {
|
|
133
|
+
hooks[known.event] = [...others, entry(definition, resolve)];
|
|
134
|
+
} else if (others.length > 0) {
|
|
135
|
+
hooks[known.event] = others;
|
|
136
|
+
} else {
|
|
137
|
+
delete hooks[known.event];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
var entry = (definition, resolve) => ({
|
|
142
|
+
...definition.matcher ? { matcher: definition.matcher } : {},
|
|
143
|
+
hooks: [{
|
|
144
|
+
type: "command",
|
|
145
|
+
command: resolve(definition.binary),
|
|
146
|
+
timeout: definition.timeout
|
|
147
|
+
}]
|
|
148
|
+
});
|
|
149
|
+
var removePusharySettings = (settings, scope) => {
|
|
150
|
+
let changed = scope?.keepMcpServer === true ? false : removeClaudeMcpServers(settings);
|
|
151
|
+
const permissions = scope?.keepMcpServer === true ? void 0 : asRecord(settings.permissions);
|
|
152
|
+
if (permissions && Array.isArray(permissions.allow)) {
|
|
153
|
+
const filtered = permissions.allow.filter((rule) => !isPusharyPermission(rule));
|
|
154
|
+
if (filtered.length !== permissions.allow.length) {
|
|
155
|
+
if (filtered.length === 0) {
|
|
156
|
+
delete permissions.allow;
|
|
157
|
+
} else {
|
|
158
|
+
permissions.allow = filtered;
|
|
159
|
+
}
|
|
160
|
+
if (Object.keys(permissions).length === 0) delete settings.permissions;
|
|
161
|
+
changed = true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const hooks = asRecord(settings.hooks);
|
|
165
|
+
if (hooks) {
|
|
166
|
+
for (const { event: key } of CLAUDE_HOOK_EVENTS) {
|
|
167
|
+
const entries = hooks[key];
|
|
168
|
+
if (!Array.isArray(entries)) continue;
|
|
169
|
+
const filtered = entries.filter((entry2) => !isPusharyHook(entry2, scope));
|
|
170
|
+
if (filtered.length !== entries.length) {
|
|
171
|
+
if (filtered.length === 0) {
|
|
172
|
+
delete hooks[key];
|
|
173
|
+
} else {
|
|
174
|
+
hooks[key] = filtered;
|
|
175
|
+
}
|
|
176
|
+
changed = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
180
|
+
}
|
|
181
|
+
return changed;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export {
|
|
185
|
+
addClaudeMcpServer,
|
|
186
|
+
isClaudeMcpSnapshot,
|
|
187
|
+
captureClaudeMcpServer,
|
|
188
|
+
updateClaudeMcpSnapshot,
|
|
189
|
+
restoreClaudeMcpServer,
|
|
190
|
+
removeClaudeMcpServers,
|
|
191
|
+
addPusharyToolPermissions,
|
|
192
|
+
addPusharyHooks,
|
|
193
|
+
removePusharySettings
|
|
194
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defersToNativeMode
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-C2MV4G4A.js";
|
|
4
4
|
import {
|
|
5
5
|
isGatingMoment,
|
|
6
6
|
recordKeylessMoment
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-UQSKCQBO.js";
|
|
8
8
|
import {
|
|
9
9
|
denyReasonFrom,
|
|
10
10
|
isDeferAnswer
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
reportEvent,
|
|
20
20
|
settleQuestionsForToolUse,
|
|
21
21
|
throttlePass
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-J55K6YS4.js";
|
|
23
23
|
import {
|
|
24
24
|
DEFAULT_SESSION,
|
|
25
25
|
askUser,
|
|
@@ -37,14 +37,14 @@ import {
|
|
|
37
37
|
describeToolCall,
|
|
38
38
|
permissionDeniedAction,
|
|
39
39
|
scopePathFor
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-VLKEAYJ4.js";
|
|
41
41
|
import {
|
|
42
42
|
PRETOOLUSE_HANDLED_TOOLS,
|
|
43
43
|
PRETOOLUSE_PHONE_ANSWERED_TOOLS
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-TTO76NZJ.js";
|
|
45
45
|
import {
|
|
46
46
|
getMachineId
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-SSQ5X6SA.js";
|
|
48
48
|
import {
|
|
49
49
|
getApiKey,
|
|
50
50
|
getBaseUrl
|
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
parseAgentQuestions,
|
|
62
62
|
resolveGate,
|
|
63
63
|
stoppedOutput
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-VOWZ5VOO.js";
|
|
65
65
|
|
|
66
66
|
// src/decision-episode.ts
|
|
67
67
|
var EPISODE_PATH = "/api/agent/decision-episode";
|