@pushary/agent-hooks 0.61.0 → 0.62.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 +76 -0
- package/dist/bin/pushary-claude.js +4 -4
- package/dist/bin/pushary-clean.js +82 -35
- package/dist/bin/pushary-codex-hook.js +4 -4
- package/dist/bin/pushary-codex.js +3 -3
- package/dist/bin/pushary-connect.js +7 -6
- package/dist/bin/pushary-daemon.js +37 -5
- package/dist/bin/pushary-doctor.js +114 -34
- package/dist/bin/pushary-gemini-hook.js +4 -4
- package/dist/bin/pushary-hook.js +5 -5
- package/dist/bin/pushary-login.js +5 -4
- package/dist/bin/pushary-logout.js +52 -9
- package/dist/bin/pushary-mode.js +4 -4
- package/dist/bin/pushary-notification-hook.js +2 -2
- package/dist/bin/pushary-permission-denied-hook.js +3 -3
- package/dist/bin/pushary-permission-hook.js +3 -3
- 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 +97 -155
- package/dist/bin/pushary-stats.js +2 -2
- package/dist/bin/pushary-status.js +6 -5
- package/dist/bin/pushary-stop-hook.js +2 -2
- package/dist/bin/pushary-stopfailure-hook.js +2 -2
- package/dist/bin/pushary-suggestions.js +2 -2
- package/dist/bin/pushary-upgrade.js +189 -18
- package/dist/bin/pushary-wait.js +4 -4
- package/dist/bin/pushary.js +1 -1
- package/dist/{chunk-GX64YGU3.js → chunk-3O27ZSRE.js} +25 -0
- package/dist/{chunk-HI4AGGE6.js → chunk-5SO3CEGJ.js} +4 -4
- package/dist/chunk-6MTNS63X.js +55 -0
- package/dist/{chunk-B26DNXCA.js → chunk-COC6NPWG.js} +13 -59
- package/dist/chunk-E2U35RLD.js +108 -0
- package/dist/chunk-ER657KRJ.js +168 -0
- package/dist/{chunk-HUJQSP4F.js → chunk-IIZZYUWK.js} +1 -1
- package/dist/chunk-N7XJ4L2W.js +79 -0
- package/dist/{chunk-R6C4USIV.js → chunk-ONVEYEVR.js} +2 -2
- package/dist/{chunk-SML23YOT.js → chunk-SP7OZXCQ.js} +10 -2
- package/dist/chunk-UXXRRXUS.js +93 -0
- package/dist/{chunk-UKNAAVEE.js → chunk-YJ7YZRVV.js} +1 -1
- package/dist/src/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-S4TFBJ5O.js +0 -339
- package/dist/{chunk-VFBYRR2N.js → chunk-HIIBSYFJ.js} +3 -3
|
@@ -4,32 +4,57 @@ import {
|
|
|
4
4
|
addPusharyHooks,
|
|
5
5
|
addPusharyToolPermissions
|
|
6
6
|
} from "../chunk-CAJZAFVS.js";
|
|
7
|
+
import {
|
|
8
|
+
GEMINI_HOOK_BINARY,
|
|
9
|
+
addGeminiHooks,
|
|
10
|
+
addGeminiMcpServer,
|
|
11
|
+
hasGeminiHooks
|
|
12
|
+
} from "../chunk-E2U35RLD.js";
|
|
13
|
+
import {
|
|
14
|
+
CODEX_HOOK_BINARY,
|
|
15
|
+
addCodexHookTrust,
|
|
16
|
+
addCodexHooks,
|
|
17
|
+
addCodexMcpServer,
|
|
18
|
+
codexConfigToml,
|
|
19
|
+
codexHooksJson,
|
|
20
|
+
hasCodexHooks
|
|
21
|
+
} from "../chunk-ER657KRJ.js";
|
|
22
|
+
import {
|
|
23
|
+
readJsonSafe
|
|
24
|
+
} from "../chunk-6MTNS63X.js";
|
|
7
25
|
import {
|
|
8
26
|
execNpm,
|
|
9
27
|
npmErrorMessage,
|
|
10
|
-
|
|
28
|
+
quoteIfNeeded,
|
|
29
|
+
resolveGlobalBinDir,
|
|
30
|
+
resolveGlobalBinary
|
|
11
31
|
} from "../chunk-MUEW424A.js";
|
|
12
32
|
import {
|
|
13
33
|
exitOnHelpFlag
|
|
14
|
-
} from "../chunk-
|
|
15
|
-
import "../chunk-
|
|
34
|
+
} from "../chunk-IIZZYUWK.js";
|
|
35
|
+
import "../chunk-SP7OZXCQ.js";
|
|
16
36
|
import "../chunk-DQAN3JQP.js";
|
|
17
37
|
import {
|
|
18
38
|
getApiKey
|
|
19
39
|
} from "../chunk-2UMNXADU.js";
|
|
20
40
|
|
|
41
|
+
// bin/pushary-upgrade.ts
|
|
42
|
+
import { existsSync as existsSync2 } from "fs";
|
|
43
|
+
import { homedir as homedir2 } from "os";
|
|
44
|
+
import { join as join2 } from "path";
|
|
45
|
+
|
|
21
46
|
// src/reapply.ts
|
|
22
47
|
import { join, dirname } from "path";
|
|
23
48
|
import { homedir } from "os";
|
|
24
49
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
50
|
+
import { parse as parseTOML, stringify as stringifyTOML } from "smol-toml";
|
|
25
51
|
var CLAUDE_SETTINGS = join(homedir(), ".claude", "settings.json");
|
|
26
52
|
var CLAUDE_JSON = join(homedir(), ".claude.json");
|
|
27
53
|
var readJson = (filePath) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
54
|
+
const result = readJsonSafe(filePath);
|
|
55
|
+
if (result.kind === "ok") return result.value;
|
|
56
|
+
if (result.kind === "missing") return {};
|
|
57
|
+
return null;
|
|
33
58
|
};
|
|
34
59
|
var writeJson = (filePath, data) => {
|
|
35
60
|
const dir = dirname(filePath);
|
|
@@ -45,10 +70,13 @@ var reapplyClaudeSettings = (apiKey, paths = {}) => {
|
|
|
45
70
|
const claudeJson = readJson(claudeJsonPath);
|
|
46
71
|
const configured = settings != null && hasPusharyClaudeHooks(settings) || claudeJson != null && hasPusharyMcp(claudeJson);
|
|
47
72
|
if (!configured) return { reapplied: false, hooks: false, mcp: false };
|
|
73
|
+
const settingsWritable = settings != null;
|
|
74
|
+
const claudeJsonWritable = claudeJson != null;
|
|
48
75
|
let hooks = false;
|
|
49
76
|
let mcp = false;
|
|
50
77
|
try {
|
|
51
|
-
|
|
78
|
+
if (!settingsWritable) throw new Error("settings.json could not be parsed");
|
|
79
|
+
const s = settings;
|
|
52
80
|
addPusharyToolPermissions(s);
|
|
53
81
|
addPusharyHooks(s, resolveGlobalBinDir("pushary-hook"));
|
|
54
82
|
writeJson(settingsPath, s);
|
|
@@ -57,7 +85,8 @@ var reapplyClaudeSettings = (apiKey, paths = {}) => {
|
|
|
57
85
|
}
|
|
58
86
|
if (apiKey) {
|
|
59
87
|
try {
|
|
60
|
-
|
|
88
|
+
if (!claudeJsonWritable) throw new Error(".claude.json could not be parsed");
|
|
89
|
+
const cj = claudeJson;
|
|
61
90
|
addClaudeMcpServer(cj, apiKey);
|
|
62
91
|
writeJson(claudeJsonPath, cj);
|
|
63
92
|
mcp = true;
|
|
@@ -66,9 +95,130 @@ var reapplyClaudeSettings = (apiKey, paths = {}) => {
|
|
|
66
95
|
}
|
|
67
96
|
return { reapplied: hooks || mcp, hooks, mcp };
|
|
68
97
|
};
|
|
98
|
+
var readToml = (filePath) => {
|
|
99
|
+
try {
|
|
100
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
101
|
+
return raw.trim() === "" ? {} : parseTOML(raw);
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
var reapplyCodexSettings = (apiKey, paths = {}) => {
|
|
107
|
+
const configPath = paths.configPath ?? codexConfigToml();
|
|
108
|
+
const hooksPath = paths.hooksPath ?? codexHooksJson();
|
|
109
|
+
const config = readToml(configPath);
|
|
110
|
+
const hooksConfig = readJson(hooksPath);
|
|
111
|
+
const configured = config != null && !!config.mcp_servers?.pushary || hooksConfig != null && hasCodexHooks(hooksConfig);
|
|
112
|
+
if (!configured) return { reapplied: false, hooks: false, mcp: false };
|
|
113
|
+
const command = quoteIfNeeded(resolveGlobalBinary(CODEX_HOOK_BINARY) ?? CODEX_HOOK_BINARY);
|
|
114
|
+
let hooks = false;
|
|
115
|
+
let mcp = false;
|
|
116
|
+
if (hooksConfig != null) {
|
|
117
|
+
try {
|
|
118
|
+
addCodexHooks(hooksConfig, command);
|
|
119
|
+
writeJson(hooksPath, hooksConfig);
|
|
120
|
+
hooks = true;
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (config != null) {
|
|
125
|
+
try {
|
|
126
|
+
if (hooks) addCodexHookTrust(config, hooksPath, command);
|
|
127
|
+
if (apiKey) {
|
|
128
|
+
addCodexMcpServer(config, apiKey);
|
|
129
|
+
mcp = true;
|
|
130
|
+
}
|
|
131
|
+
if (hooks || apiKey) writeFileSync(configPath, stringifyTOML(config), "utf-8");
|
|
132
|
+
} catch {
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { reapplied: hooks || mcp, hooks, mcp };
|
|
136
|
+
};
|
|
137
|
+
var reapplyGeminiSettings = (apiKey, paths = {}) => {
|
|
138
|
+
const settingsPath = paths.settingsPath ?? join(homedir(), ".gemini", "settings.json");
|
|
139
|
+
const settings = readJson(settingsPath);
|
|
140
|
+
if (settings == null) return { reapplied: false, hooks: false, mcp: false };
|
|
141
|
+
const configured = hasGeminiHooks(settings) || !!settings.mcpServers?.pushary;
|
|
142
|
+
if (!configured) return { reapplied: false, hooks: false, mcp: false };
|
|
143
|
+
let hooks = false;
|
|
144
|
+
let mcp = false;
|
|
145
|
+
try {
|
|
146
|
+
addGeminiHooks(settings, quoteIfNeeded(resolveGlobalBinary(GEMINI_HOOK_BINARY) ?? GEMINI_HOOK_BINARY));
|
|
147
|
+
hooks = true;
|
|
148
|
+
if (apiKey) {
|
|
149
|
+
addGeminiMcpServer(settings, apiKey);
|
|
150
|
+
mcp = true;
|
|
151
|
+
}
|
|
152
|
+
writeJson(settingsPath, settings);
|
|
153
|
+
} catch {
|
|
154
|
+
}
|
|
155
|
+
return { reapplied: hooks || mcp, hooks, mcp };
|
|
156
|
+
};
|
|
157
|
+
var reapplyCursorPlugin = (apiKey, paths = {}) => {
|
|
158
|
+
const mcpPath = paths.pluginMcpPath ?? join(homedir(), ".cursor", "plugins", "local", "pushary", "mcp.json");
|
|
159
|
+
if (!apiKey) return { reapplied: false, hooks: false, mcp: false };
|
|
160
|
+
const mcpConfig = readJson(mcpPath);
|
|
161
|
+
if (mcpConfig == null) return { reapplied: false, hooks: false, mcp: false };
|
|
162
|
+
const servers = mcpConfig.mcpServers;
|
|
163
|
+
if (!servers?.pushary) return { reapplied: false, hooks: false, mcp: false };
|
|
164
|
+
try {
|
|
165
|
+
addClaudeMcpServer(mcpConfig, apiKey);
|
|
166
|
+
writeJson(mcpPath, mcpConfig);
|
|
167
|
+
return { reapplied: true, hooks: false, mcp: true };
|
|
168
|
+
} catch {
|
|
169
|
+
return { reapplied: false, hooks: false, mcp: false };
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
var reapplyAllAgents = (apiKey) => {
|
|
173
|
+
const runners = [
|
|
174
|
+
{ label: "Claude Code", run: () => reapplyClaudeSettings(apiKey) },
|
|
175
|
+
{ label: "Codex", run: () => reapplyCodexSettings(apiKey) },
|
|
176
|
+
{ label: "Gemini CLI", run: () => reapplyGeminiSettings(apiKey) },
|
|
177
|
+
{ label: "Cursor", run: () => reapplyCursorPlugin(apiKey) }
|
|
178
|
+
];
|
|
179
|
+
const done = [];
|
|
180
|
+
for (const runner of runners) {
|
|
181
|
+
try {
|
|
182
|
+
const result = runner.run();
|
|
183
|
+
if (result.reapplied) done.push({ label: runner.label, result });
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return done;
|
|
188
|
+
};
|
|
69
189
|
|
|
70
190
|
// bin/pushary-upgrade.ts
|
|
71
191
|
exitOnHelpFlag("upgrade");
|
|
192
|
+
var detectInstallModes = () => {
|
|
193
|
+
const candidates = [
|
|
194
|
+
{
|
|
195
|
+
label: "Cursor plugin",
|
|
196
|
+
upgrade: "updates itself through Cursor",
|
|
197
|
+
present: existsSync2(join2(homedir2(), ".cursor", "plugins", "local", "pushary"))
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
label: "Codex config",
|
|
201
|
+
upgrade: "npx @pushary/agent-hooks@latest setup --agents codex",
|
|
202
|
+
present: existsSync2(codexConfigToml())
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
label: "Claude Code config",
|
|
206
|
+
upgrade: "npx @pushary/agent-hooks@latest setup --agents claude_code",
|
|
207
|
+
present: existsSync2(join2(homedir2(), ".claude.json"))
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
label: "Gemini config",
|
|
211
|
+
upgrade: "npx @pushary/agent-hooks@latest setup --agents gemini_cli",
|
|
212
|
+
present: existsSync2(join2(homedir2(), ".gemini", "settings.json"))
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
label: "Hermes plugin (pip)",
|
|
216
|
+
upgrade: "pip install --upgrade pushary-hermes",
|
|
217
|
+
present: existsSync2(join2(homedir2(), ".hermes"))
|
|
218
|
+
}
|
|
219
|
+
];
|
|
220
|
+
return candidates.filter((candidate) => candidate.present).map(({ label, upgrade }) => ({ label, upgrade }));
|
|
221
|
+
};
|
|
72
222
|
var getInstalledVersion = () => {
|
|
73
223
|
try {
|
|
74
224
|
return execNpm("list -g --no-workspaces @pushary/agent-hooks --depth=0", { timeout: 1e4 }).toString().match(/@pushary\/agent-hooks@([\d.]+)/)?.[1] ?? "";
|
|
@@ -100,8 +250,20 @@ var isNewer = (a, b) => {
|
|
|
100
250
|
var main = async () => {
|
|
101
251
|
const installed = getInstalledVersion();
|
|
102
252
|
if (!installed) {
|
|
103
|
-
|
|
104
|
-
console.log("
|
|
253
|
+
const found = detectInstallModes();
|
|
254
|
+
console.log("No global install of @pushary/agent-hooks found, so there is nothing to upgrade here.");
|
|
255
|
+
console.log();
|
|
256
|
+
if (found.length === 0) {
|
|
257
|
+
console.log("Nothing on this machine looks Pushary-configured either.");
|
|
258
|
+
console.log(" Set it up with: npx @pushary/agent-hooks@latest setup");
|
|
259
|
+
} else {
|
|
260
|
+
console.log("What this machine does have:");
|
|
261
|
+
for (const mode of found) {
|
|
262
|
+
console.log(` ${mode.label.padEnd(22)} ${mode.upgrade}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
console.log();
|
|
266
|
+
console.log("Check the result with: pushary doctor");
|
|
105
267
|
return;
|
|
106
268
|
}
|
|
107
269
|
const latest = await fetchLatestVersion();
|
|
@@ -122,14 +284,23 @@ var main = async () => {
|
|
|
122
284
|
} catch {
|
|
123
285
|
apiKey = void 0;
|
|
124
286
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
287
|
+
const refreshed = reapplyAllAgents(apiKey);
|
|
288
|
+
console.log();
|
|
289
|
+
if (refreshed.length > 0) {
|
|
290
|
+
console.log("Refreshed so the new hook set is active now:");
|
|
291
|
+
for (const agent of refreshed) {
|
|
292
|
+
const parts = [agent.result.hooks ? "hooks" : null, agent.result.mcp ? "MCP key" : null].filter(Boolean).join(" + ");
|
|
293
|
+
console.log(` ${agent.label.padEnd(14)} ${parts}`);
|
|
129
294
|
}
|
|
130
|
-
|
|
295
|
+
if (!apiKey) {
|
|
296
|
+
console.log();
|
|
297
|
+
console.log("No key was readable here, so MCP entries kept the key they already had.");
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
console.log("No agent config on this machine needed refreshing.");
|
|
131
301
|
}
|
|
132
|
-
console.log(
|
|
302
|
+
console.log();
|
|
303
|
+
console.log("Done. Your hooks point at the global install.");
|
|
133
304
|
console.log("Verify with: pushary doctor");
|
|
134
305
|
};
|
|
135
306
|
main();
|
package/dist/bin/pushary-wait.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
exitOnFailedResponse
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-ONVEYEVR.js";
|
|
5
5
|
import {
|
|
6
6
|
describeWaitLadder
|
|
7
7
|
} from "../chunk-IIENZE2J.js";
|
|
8
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-YJ7YZRVV.js";
|
|
9
9
|
import {
|
|
10
10
|
createIo
|
|
11
11
|
} from "../chunk-TX7KBKT7.js";
|
|
12
12
|
import "../chunk-7QLSKOSU.js";
|
|
13
13
|
import {
|
|
14
14
|
exitOnHelpFlag
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-IIZZYUWK.js";
|
|
16
16
|
import {
|
|
17
17
|
EXIT
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-SP7OZXCQ.js";
|
|
19
19
|
import "../chunk-DQAN3JQP.js";
|
|
20
20
|
import {
|
|
21
21
|
getApiKey,
|
package/dist/bin/pushary.js
CHANGED
|
@@ -155,6 +155,7 @@ var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
|
155
155
|
var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
156
156
|
var check = green("\u2713");
|
|
157
157
|
var apiBase = () => process.env.PUSHARY_API_URL?.trim() || process.env.PUSHARY_BASE_URL?.trim() || "https://pushary.com";
|
|
158
|
+
var EXIT_ABORTED = 130;
|
|
158
159
|
var CONNECT_POLL_INTERVAL_MS = 2500;
|
|
159
160
|
var CONNECT_TIMEOUT_MS = 18e4;
|
|
160
161
|
var EMPTY_CHANNELS = {
|
|
@@ -463,6 +464,17 @@ var startPairing = async (cliPublicKey) => {
|
|
|
463
464
|
return null;
|
|
464
465
|
}
|
|
465
466
|
};
|
|
467
|
+
var cancelPairing = async (pairId) => {
|
|
468
|
+
try {
|
|
469
|
+
await fetch(`${apiBase()}/api/mobile/pair/start`, {
|
|
470
|
+
method: "DELETE",
|
|
471
|
+
headers: { "Content-Type": "application/json" },
|
|
472
|
+
body: JSON.stringify({ pairId }),
|
|
473
|
+
signal: AbortSignal.timeout(5e3)
|
|
474
|
+
});
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
};
|
|
466
478
|
var claimPairing = async (pairId) => {
|
|
467
479
|
try {
|
|
468
480
|
const res = await fetch(`${apiBase()}/api/mobile/pair/claim?id=${encodeURIComponent(pairId)}`, {
|
|
@@ -504,8 +516,16 @@ var connectViaAppPairing = async () => {
|
|
|
504
516
|
const left = Math.max(0, Math.ceil((deadline - Date.now()) / 1e3));
|
|
505
517
|
return `Waiting for the app to authorize ${dim(`(${left}s)`)}`;
|
|
506
518
|
});
|
|
519
|
+
let cancelled = false;
|
|
520
|
+
const onInterrupt = () => {
|
|
521
|
+
cancelled = true;
|
|
522
|
+
stop(yellow("!"), "Cancelled");
|
|
523
|
+
void cancelPairing(pairId).finally(() => process.exit(EXIT_ABORTED));
|
|
524
|
+
};
|
|
525
|
+
process.once("SIGINT", onInterrupt);
|
|
507
526
|
try {
|
|
508
527
|
while (Date.now() < deadline) {
|
|
528
|
+
if (cancelled) return null;
|
|
509
529
|
const claim = await claimPairing(pairId);
|
|
510
530
|
if (claim?.status === "authorized") {
|
|
511
531
|
let apiKey;
|
|
@@ -513,6 +533,7 @@ var connectViaAppPairing = async () => {
|
|
|
513
533
|
apiKey = openSealedKey(claim.sealed, keypair);
|
|
514
534
|
} catch {
|
|
515
535
|
stop(yellow("!"), "Pairing failed to decrypt \u2014 re-run setup to try again");
|
|
536
|
+
await cancelPairing(pairId);
|
|
516
537
|
return null;
|
|
517
538
|
}
|
|
518
539
|
stop(check, "App connected");
|
|
@@ -525,10 +546,14 @@ var connectViaAppPairing = async () => {
|
|
|
525
546
|
await sleep(PAIR_POLL_INTERVAL_MS);
|
|
526
547
|
}
|
|
527
548
|
stop(yellow("!"), "Timed out waiting for the app");
|
|
549
|
+
await cancelPairing(pairId);
|
|
528
550
|
return null;
|
|
529
551
|
} catch (err) {
|
|
530
552
|
stop(yellow("!"), `Pairing error ${dim(`(${err instanceof Error ? err.message : "network"})`)}`);
|
|
553
|
+
await cancelPairing(pairId);
|
|
531
554
|
return null;
|
|
555
|
+
} finally {
|
|
556
|
+
process.off("SIGINT", onInterrupt);
|
|
532
557
|
}
|
|
533
558
|
};
|
|
534
559
|
|
|
@@ -25,10 +25,7 @@ import {
|
|
|
25
25
|
sendNotification,
|
|
26
26
|
throttlePass,
|
|
27
27
|
waitForAnswer
|
|
28
|
-
} from "./chunk-
|
|
29
|
-
import {
|
|
30
|
-
getMachineId
|
|
31
|
-
} from "./chunk-RN3NOEJF.js";
|
|
28
|
+
} from "./chunk-HIIBSYFJ.js";
|
|
32
29
|
import {
|
|
33
30
|
isGatingMoment,
|
|
34
31
|
recordKeylessMoment
|
|
@@ -42,6 +39,9 @@ import {
|
|
|
42
39
|
scopeChangeReason,
|
|
43
40
|
scopeRequiresHuman
|
|
44
41
|
} from "./chunk-DQAN3JQP.js";
|
|
42
|
+
import {
|
|
43
|
+
getMachineId
|
|
44
|
+
} from "./chunk-RN3NOEJF.js";
|
|
45
45
|
import {
|
|
46
46
|
getApiKey,
|
|
47
47
|
getBaseUrl
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/cli/fsx.ts
|
|
2
|
+
import { copyFileSync, existsSync, fsyncSync, mkdirSync, openSync, closeSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
var readJsonSafe = (filePath) => {
|
|
5
|
+
let raw;
|
|
6
|
+
try {
|
|
7
|
+
raw = readFileSync(filePath, "utf-8");
|
|
8
|
+
} catch (err) {
|
|
9
|
+
const error = err;
|
|
10
|
+
if (error.code === "ENOENT") return { kind: "missing" };
|
|
11
|
+
return { kind: "unreadable", error };
|
|
12
|
+
}
|
|
13
|
+
if (raw.trim() === "") return { kind: "ok", value: {} };
|
|
14
|
+
try {
|
|
15
|
+
return { kind: "ok", value: JSON.parse(raw) };
|
|
16
|
+
} catch (err) {
|
|
17
|
+
return { kind: "unparseable", error: err, raw };
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var stamp = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
21
|
+
var backupFile = (filePath) => {
|
|
22
|
+
const target = `${filePath}.pushary-backup-${stamp()}`;
|
|
23
|
+
copyFileSync(filePath, target);
|
|
24
|
+
return target;
|
|
25
|
+
};
|
|
26
|
+
var writeFileAtomic = (filePath, contents, mode) => {
|
|
27
|
+
const dir = dirname(filePath);
|
|
28
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
29
|
+
const temp = join(dir, `.${process.pid}-${stamp()}.pushary-tmp`);
|
|
30
|
+
try {
|
|
31
|
+
writeFileSync(temp, contents, mode === void 0 ? "utf-8" : { encoding: "utf-8", mode });
|
|
32
|
+
const handle = openSync(temp, "r+");
|
|
33
|
+
try {
|
|
34
|
+
fsyncSync(handle);
|
|
35
|
+
} finally {
|
|
36
|
+
closeSync(handle);
|
|
37
|
+
}
|
|
38
|
+
renameSync(temp, filePath);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
try {
|
|
41
|
+
if (existsSync(temp)) unlinkSync(temp);
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var writeJsonAtomic = (filePath, data, mode) => writeFileAtomic(filePath, `${JSON.stringify(data, null, 2)}
|
|
48
|
+
`, mode);
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
readJsonSafe,
|
|
52
|
+
backupFile,
|
|
53
|
+
writeFileAtomic,
|
|
54
|
+
writeJsonAtomic
|
|
55
|
+
};
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
backupFile,
|
|
3
|
+
readJsonSafe,
|
|
4
|
+
writeFileAtomic,
|
|
5
|
+
writeJsonAtomic
|
|
6
|
+
} from "./chunk-6MTNS63X.js";
|
|
1
7
|
import {
|
|
2
8
|
resolveShellRc
|
|
3
9
|
} from "./chunk-BC3VCZ3E.js";
|
|
@@ -5,59 +11,10 @@ import {
|
|
|
5
11
|
configFilePath
|
|
6
12
|
} from "./chunk-2UMNXADU.js";
|
|
7
13
|
|
|
8
|
-
// src/cli/fsx.ts
|
|
9
|
-
import { copyFileSync, existsSync, fsyncSync, mkdirSync, openSync, closeSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "fs";
|
|
10
|
-
import { dirname, join } from "path";
|
|
11
|
-
var readJsonSafe = (filePath) => {
|
|
12
|
-
let raw;
|
|
13
|
-
try {
|
|
14
|
-
raw = readFileSync(filePath, "utf-8");
|
|
15
|
-
} catch (err) {
|
|
16
|
-
const error = err;
|
|
17
|
-
if (error.code === "ENOENT") return { kind: "missing" };
|
|
18
|
-
return { kind: "unreadable", error };
|
|
19
|
-
}
|
|
20
|
-
if (raw.trim() === "") return { kind: "ok", value: {} };
|
|
21
|
-
try {
|
|
22
|
-
return { kind: "ok", value: JSON.parse(raw) };
|
|
23
|
-
} catch (err) {
|
|
24
|
-
return { kind: "unparseable", error: err, raw };
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
var stamp = () => (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
28
|
-
var backupFile = (filePath) => {
|
|
29
|
-
const target = `${filePath}.pushary-backup-${stamp()}`;
|
|
30
|
-
copyFileSync(filePath, target);
|
|
31
|
-
return target;
|
|
32
|
-
};
|
|
33
|
-
var writeFileAtomic = (filePath, contents, mode) => {
|
|
34
|
-
const dir = dirname(filePath);
|
|
35
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
36
|
-
const temp = join(dir, `.${process.pid}-${stamp()}.pushary-tmp`);
|
|
37
|
-
try {
|
|
38
|
-
writeFileSync(temp, contents, mode === void 0 ? "utf-8" : { encoding: "utf-8", mode });
|
|
39
|
-
const handle = openSync(temp, "r+");
|
|
40
|
-
try {
|
|
41
|
-
fsyncSync(handle);
|
|
42
|
-
} finally {
|
|
43
|
-
closeSync(handle);
|
|
44
|
-
}
|
|
45
|
-
renameSync(temp, filePath);
|
|
46
|
-
} catch (err) {
|
|
47
|
-
try {
|
|
48
|
-
if (existsSync(temp)) unlinkSync(temp);
|
|
49
|
-
} catch {
|
|
50
|
-
}
|
|
51
|
-
throw err;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
var writeJsonAtomic = (filePath, data, mode) => writeFileAtomic(filePath, `${JSON.stringify(data, null, 2)}
|
|
55
|
-
`, mode);
|
|
56
|
-
|
|
57
14
|
// src/keystore.ts
|
|
58
|
-
import { chmodSync, existsSync
|
|
15
|
+
import { chmodSync, existsSync, readFileSync, realpathSync, writeFileSync } from "fs";
|
|
59
16
|
import { homedir } from "os";
|
|
60
|
-
import { join
|
|
17
|
+
import { join } from "path";
|
|
61
18
|
var KEY_FILE_MODE = 384;
|
|
62
19
|
var FALLBACK_RCS = [".zshrc", ".zprofile", ".bashrc", ".bash_profile"];
|
|
63
20
|
var OUR_EXPORT = /^[ \t]*export[ \t]+PUSHARY_API_KEY=(['"]?)(pk_[a-f0-9]+\.[a-f0-9]+)\1[ \t]*$/;
|
|
@@ -86,7 +43,7 @@ var rewriteRcExport = (rcPath, apiKey, shell = null) => {
|
|
|
86
43
|
const target = realPathOf(rcPath);
|
|
87
44
|
let content = "";
|
|
88
45
|
try {
|
|
89
|
-
content =
|
|
46
|
+
content = readFileSync(target, "utf-8");
|
|
90
47
|
} catch (err) {
|
|
91
48
|
const error = err;
|
|
92
49
|
if (error.code !== "ENOENT") {
|
|
@@ -141,7 +98,7 @@ var rewriteRcExport = (rcPath, apiKey, shell = null) => {
|
|
|
141
98
|
var resolveRcTarget = () => {
|
|
142
99
|
const resolved = resolveShellRc();
|
|
143
100
|
if (resolved) return { path: resolved.path, shell: resolved.shell };
|
|
144
|
-
const fallback = FALLBACK_RCS.map((name) =>
|
|
101
|
+
const fallback = FALLBACK_RCS.map((name) => join(homedir(), name)).find(existsSync);
|
|
145
102
|
return fallback ? { path: fallback, shell: null } : null;
|
|
146
103
|
};
|
|
147
104
|
var writeKey = (apiKey, options = {}) => {
|
|
@@ -181,13 +138,13 @@ var clearKey = (options = {}) => {
|
|
|
181
138
|
if (target) {
|
|
182
139
|
try {
|
|
183
140
|
const real = realpathSync(target.path);
|
|
184
|
-
const lines =
|
|
141
|
+
const lines = readFileSync(real, "utf8").split("\n");
|
|
185
142
|
const next = lines.map((line) => {
|
|
186
143
|
if (!OUR_EXPORT.test(line)) return line;
|
|
187
144
|
rcLinesDisabled += 1;
|
|
188
145
|
return `# ${line.trim()} # removed by pushary logout`;
|
|
189
146
|
});
|
|
190
|
-
if (rcLinesDisabled > 0)
|
|
147
|
+
if (rcLinesDisabled > 0) writeFileSync(real, next.join("\n"));
|
|
191
148
|
} catch {
|
|
192
149
|
rcLinesDisabled = 0;
|
|
193
150
|
}
|
|
@@ -223,12 +180,9 @@ var readKeySource = () => {
|
|
|
223
180
|
};
|
|
224
181
|
|
|
225
182
|
export {
|
|
226
|
-
readJsonSafe,
|
|
227
|
-
backupFile,
|
|
228
|
-
writeFileAtomic,
|
|
229
|
-
writeJsonAtomic,
|
|
230
183
|
KEY_FILE_MODE,
|
|
231
184
|
exportLine,
|
|
185
|
+
keyPrefix,
|
|
232
186
|
writeKey,
|
|
233
187
|
clearKey,
|
|
234
188
|
readKeySource
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HOOK_BUDGETS
|
|
3
|
+
} from "./chunk-DQAN3JQP.js";
|
|
4
|
+
|
|
5
|
+
// src/gemini-config.ts
|
|
6
|
+
var GEMINI_HOOK_BINARY = "pushary-gemini-hook";
|
|
7
|
+
var GEMINI_MCP_URL = "https://pushary.com/api/mcp/mcp";
|
|
8
|
+
var GEMINI_HOOK_EVENTS = [
|
|
9
|
+
{ event: "BeforeTool", matcher: "run_shell_command|write_file|replace", timeoutMs: HOOK_BUDGETS.gemini.budgetSeconds * 1e3 },
|
|
10
|
+
{ event: "AfterTool", matcher: "run_shell_command|write_file|replace", timeoutMs: 1e4 },
|
|
11
|
+
{ event: "BeforeAgent", timeoutMs: 1e4 },
|
|
12
|
+
{ event: "SessionStart", timeoutMs: 1e4 },
|
|
13
|
+
{ event: "SessionEnd", timeoutMs: 1e4 }
|
|
14
|
+
];
|
|
15
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
16
|
+
var ensureRecord = (target, key) => {
|
|
17
|
+
const existing = asRecord(target[key]);
|
|
18
|
+
if (existing) return existing;
|
|
19
|
+
const created = {};
|
|
20
|
+
target[key] = created;
|
|
21
|
+
return created;
|
|
22
|
+
};
|
|
23
|
+
var isPusharyGeminiHook = (entry) => {
|
|
24
|
+
const hooks = asRecord(entry)?.hooks;
|
|
25
|
+
if (!Array.isArray(hooks)) return false;
|
|
26
|
+
return hooks.some((hook) => String(asRecord(hook)?.command ?? "").includes(GEMINI_HOOK_BINARY));
|
|
27
|
+
};
|
|
28
|
+
var addGeminiMcpServer = (settings, apiKey) => {
|
|
29
|
+
const mcpServers = ensureRecord(settings, "mcpServers");
|
|
30
|
+
mcpServers.pushary = {
|
|
31
|
+
httpUrl: GEMINI_MCP_URL,
|
|
32
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
33
|
+
trust: true
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
var addGeminiHooks = (settings, command) => {
|
|
37
|
+
const hooks = ensureRecord(settings, "hooks");
|
|
38
|
+
for (const definition of GEMINI_HOOK_EVENTS) {
|
|
39
|
+
const existing = Array.isArray(hooks[definition.event]) ? hooks[definition.event] : [];
|
|
40
|
+
const entries = existing.filter((entry) => !isPusharyGeminiHook(entry));
|
|
41
|
+
entries.push({
|
|
42
|
+
...definition.matcher ? { matcher: definition.matcher } : {},
|
|
43
|
+
hooks: [{
|
|
44
|
+
name: "pushary",
|
|
45
|
+
type: "command",
|
|
46
|
+
command,
|
|
47
|
+
timeout: definition.timeoutMs
|
|
48
|
+
}]
|
|
49
|
+
});
|
|
50
|
+
hooks[definition.event] = entries;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var removeGeminiHooks = (settings) => {
|
|
54
|
+
const hooks = asRecord(settings.hooks);
|
|
55
|
+
if (!hooks) return false;
|
|
56
|
+
let changed = false;
|
|
57
|
+
for (const definition of GEMINI_HOOK_EVENTS) {
|
|
58
|
+
const entries = hooks[definition.event];
|
|
59
|
+
if (!Array.isArray(entries)) continue;
|
|
60
|
+
const filtered = entries.filter((entry) => !isPusharyGeminiHook(entry));
|
|
61
|
+
if (filtered.length !== entries.length) {
|
|
62
|
+
if (filtered.length === 0) {
|
|
63
|
+
delete hooks[definition.event];
|
|
64
|
+
} else {
|
|
65
|
+
hooks[definition.event] = filtered;
|
|
66
|
+
}
|
|
67
|
+
changed = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
71
|
+
return changed;
|
|
72
|
+
};
|
|
73
|
+
var removeGeminiMcpServer = (settings) => {
|
|
74
|
+
const mcpServers = asRecord(settings.mcpServers);
|
|
75
|
+
if (!mcpServers?.pushary) return false;
|
|
76
|
+
delete mcpServers.pushary;
|
|
77
|
+
if (Object.keys(mcpServers).length === 0) delete settings.mcpServers;
|
|
78
|
+
return true;
|
|
79
|
+
};
|
|
80
|
+
var removeGeminiSettings = (settings) => {
|
|
81
|
+
const mcpRemoved = removeGeminiMcpServer(settings);
|
|
82
|
+
const hooksRemoved = removeGeminiHooks(settings);
|
|
83
|
+
return mcpRemoved || hooksRemoved;
|
|
84
|
+
};
|
|
85
|
+
var hasGeminiHooks = (settings) => {
|
|
86
|
+
const hooks = asRecord(settings.hooks);
|
|
87
|
+
if (!hooks) return false;
|
|
88
|
+
return GEMINI_HOOK_EVENTS.some((definition) => {
|
|
89
|
+
const entries = hooks[definition.event];
|
|
90
|
+
return Array.isArray(entries) && entries.some(isPusharyGeminiHook);
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
var missingGeminiHookEvents = (settings) => {
|
|
94
|
+
const hooks = asRecord(settings.hooks);
|
|
95
|
+
return GEMINI_HOOK_EVENTS.filter((definition) => {
|
|
96
|
+
const entries = hooks?.[definition.event];
|
|
97
|
+
return !Array.isArray(entries) || !entries.some(isPusharyGeminiHook);
|
|
98
|
+
}).map((definition) => definition.event);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
GEMINI_HOOK_BINARY,
|
|
103
|
+
addGeminiMcpServer,
|
|
104
|
+
addGeminiHooks,
|
|
105
|
+
removeGeminiSettings,
|
|
106
|
+
hasGeminiHooks,
|
|
107
|
+
missingGeminiHookEvents
|
|
108
|
+
};
|