@pushary/agent-hooks 0.60.0 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +113 -0
- package/README.md +52 -9
- package/dist/bin/pushary-claude.js +3 -3
- package/dist/bin/pushary-clean.js +26 -11
- package/dist/bin/pushary-codex-hook.js +3 -3
- package/dist/bin/pushary-codex.js +3 -3
- package/dist/bin/pushary-connect.d.ts +1 -0
- package/dist/bin/pushary-connect.js +92 -0
- package/dist/bin/pushary-daemon.js +7 -2
- package/dist/bin/pushary-doctor.js +142 -46
- package/dist/bin/pushary-gemini-hook.js +3 -3
- package/dist/bin/pushary-hook.js +9 -4
- package/dist/bin/pushary-login.d.ts +1 -0
- package/dist/bin/pushary-login.js +156 -0
- package/dist/bin/pushary-logout.d.ts +1 -0
- package/dist/bin/pushary-logout.js +102 -0
- package/dist/bin/pushary-mode.js +24 -13
- package/dist/bin/pushary-notification-hook.js +3 -3
- package/dist/bin/pushary-permission-denied-hook.js +4 -4
- package/dist/bin/pushary-permission-hook.js +4 -4
- package/dist/bin/pushary-post-hook.js +3 -3
- package/dist/bin/pushary-prompt-hook.js +3 -3
- package/dist/bin/pushary-session-end-hook.js +3 -3
- package/dist/bin/pushary-session-start-hook.js +3 -3
- package/dist/bin/pushary-setup.js +858 -547
- package/dist/bin/pushary-stats.js +6 -1
- package/dist/bin/pushary-status.d.ts +1 -0
- package/dist/bin/pushary-status.js +206 -0
- package/dist/bin/pushary-stop-hook.js +3 -3
- package/dist/bin/pushary-stopfailure-hook.js +3 -3
- package/dist/bin/pushary-suggestions.js +10 -5
- package/dist/bin/pushary-upgrade.js +11 -6
- package/dist/bin/pushary-wait.js +21 -12
- package/dist/bin/pushary.js +42 -60
- package/dist/{chunk-NKXSILEW.js → chunk-2UMNXADU.js} +18 -2
- package/dist/chunk-3EGEA4KH.js +44 -0
- package/dist/chunk-7QLSKOSU.js +19 -0
- package/dist/chunk-B26DNXCA.js +235 -0
- package/dist/chunk-GX64YGU3.js +544 -0
- package/dist/{chunk-BE5X3WXL.js → chunk-HI4AGGE6.js} +2 -2
- package/dist/{chunk-DWED7BS3.js → chunk-HNTKTK5B.js} +1 -1
- package/dist/chunk-HUJQSP4F.js +18 -0
- package/dist/{chunk-J7JWI3KU.js → chunk-MUEW424A.js} +19 -1
- package/dist/chunk-N4DA4CJB.js +57 -0
- package/dist/chunk-QHOVJXOT.js +255 -0
- package/dist/chunk-R6C4USIV.js +44 -0
- package/dist/{chunk-BQLCELYC.js → chunk-S4TFBJ5O.js} +7 -0
- package/dist/chunk-SML23YOT.js +216 -0
- package/dist/chunk-TX7KBKT7.js +79 -0
- package/dist/chunk-UKNAAVEE.js +217 -0
- package/dist/{chunk-WNRYRN2R.js → chunk-VFBYRR2N.js} +2 -2
- package/dist/src/index.js +4 -4
- package/package.json +11 -4
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
} from "../chunk-IIENZE2J.js";
|
|
5
5
|
import {
|
|
6
6
|
GEMINI_HOOK_BINARY,
|
|
7
|
+
codexConfigToml,
|
|
8
|
+
codexHooksJson,
|
|
7
9
|
hasCodexHooks,
|
|
8
10
|
hasGeminiHooks,
|
|
9
11
|
hasInstructionBlock,
|
|
@@ -11,21 +13,50 @@ import {
|
|
|
11
13
|
missingGeminiHookEvents,
|
|
12
14
|
readCodexMcpAuth,
|
|
13
15
|
untrustedCodexHookEvents
|
|
14
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-S4TFBJ5O.js";
|
|
17
|
+
import {
|
|
18
|
+
describeReach,
|
|
19
|
+
fetchChannels,
|
|
20
|
+
reachVerdict
|
|
21
|
+
} from "../chunk-GX64YGU3.js";
|
|
22
|
+
import "../chunk-3EGEA4KH.js";
|
|
23
|
+
import {
|
|
24
|
+
parseFlags
|
|
25
|
+
} from "../chunk-N4DA4CJB.js";
|
|
26
|
+
import {
|
|
27
|
+
readKeySource
|
|
28
|
+
} from "../chunk-B26DNXCA.js";
|
|
29
|
+
import {
|
|
30
|
+
checkApiKey,
|
|
31
|
+
describeKeyCheck
|
|
32
|
+
} from "../chunk-UKNAAVEE.js";
|
|
33
|
+
import {
|
|
34
|
+
createIo
|
|
35
|
+
} from "../chunk-TX7KBKT7.js";
|
|
36
|
+
import {
|
|
37
|
+
UsageError
|
|
38
|
+
} from "../chunk-7QLSKOSU.js";
|
|
39
|
+
import "../chunk-BC3VCZ3E.js";
|
|
15
40
|
import {
|
|
16
41
|
execNpm
|
|
17
|
-
} from "../chunk-
|
|
42
|
+
} from "../chunk-MUEW424A.js";
|
|
43
|
+
import {
|
|
44
|
+
exitOnHelpFlag
|
|
45
|
+
} from "../chunk-HUJQSP4F.js";
|
|
46
|
+
import {
|
|
47
|
+
EXIT
|
|
48
|
+
} from "../chunk-SML23YOT.js";
|
|
18
49
|
import {
|
|
19
50
|
readLedgerSummary
|
|
20
51
|
} from "../chunk-WLGEY4UX.js";
|
|
21
52
|
import {
|
|
22
53
|
callMcpTool,
|
|
23
54
|
sendMcpRequest
|
|
24
|
-
} from "../chunk-
|
|
55
|
+
} from "../chunk-HNTKTK5B.js";
|
|
25
56
|
import "../chunk-DQAN3JQP.js";
|
|
26
57
|
import {
|
|
27
58
|
getBaseUrl
|
|
28
|
-
} from "../chunk-
|
|
59
|
+
} from "../chunk-2UMNXADU.js";
|
|
29
60
|
|
|
30
61
|
// bin/pushary-doctor.ts
|
|
31
62
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -34,12 +65,35 @@ import { homedir } from "os";
|
|
|
34
65
|
import { execSync } from "child_process";
|
|
35
66
|
import { confirm } from "@inquirer/prompts";
|
|
36
67
|
import { parse as parseTOML } from "smol-toml";
|
|
37
|
-
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
68
|
+
exitOnHelpFlag("doctor");
|
|
69
|
+
var parseDoctorOptions = (argv) => {
|
|
70
|
+
const flags = parseFlags(argv, { bools: ["json", "roundtrip", "no-push", "help", "h"], values: [] });
|
|
71
|
+
if (flags.command !== void 0 && flags.command !== "doctor") {
|
|
72
|
+
throw new UsageError(`doctor takes no positional arguments (got ${flags.command})`);
|
|
73
|
+
}
|
|
74
|
+
const roundtrip = flags.bools.has("roundtrip");
|
|
75
|
+
const noPush = flags.bools.has("no-push");
|
|
76
|
+
if (roundtrip && noPush) {
|
|
77
|
+
throw new UsageError("--roundtrip needs to send a push, so it cannot be combined with --no-push");
|
|
78
|
+
}
|
|
79
|
+
return { json: flags.bools.has("json"), roundtrip, noPush };
|
|
80
|
+
};
|
|
81
|
+
var options;
|
|
82
|
+
try {
|
|
83
|
+
options = parseDoctorOptions(process.argv.slice(2));
|
|
84
|
+
} catch (err) {
|
|
85
|
+
process.stderr.write(` ! ${err instanceof Error ? err.message : String(err)}
|
|
86
|
+
`);
|
|
87
|
+
process.stderr.write(" Run `pushary doctor --help` for the full list.\n");
|
|
88
|
+
process.exit(EXIT.USAGE);
|
|
89
|
+
}
|
|
90
|
+
if (options.json) {
|
|
91
|
+
console.log = (...args) => {
|
|
92
|
+
process.stderr.write(`${args.map((a) => typeof a === "string" ? a : String(a)).join(" ")}
|
|
93
|
+
`);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
var { bold, dim, green, yellow, red, cyan } = createIo();
|
|
43
97
|
var pass = green("\u2713");
|
|
44
98
|
var fail = red("\u2717");
|
|
45
99
|
var warn = yellow("!");
|
|
@@ -101,19 +155,9 @@ var main = async () => {
|
|
|
101
155
|
console.log(` ${bold("Pushary Doctor")}`);
|
|
102
156
|
console.log();
|
|
103
157
|
console.log(` ${dim("Configuration")}`);
|
|
104
|
-
|
|
105
|
-
let
|
|
106
|
-
|
|
107
|
-
if (!apiKey) {
|
|
108
|
-
try {
|
|
109
|
-
const parsed = JSON.parse(readFileSync(PUSHARY_CONFIG_FILE, "utf-8"));
|
|
110
|
-
if (typeof parsed.apiKey === "string" && parsed.apiKey) {
|
|
111
|
-
apiKey = parsed.apiKey;
|
|
112
|
-
keySource = "config-file";
|
|
113
|
-
}
|
|
114
|
-
} catch {
|
|
115
|
-
}
|
|
116
|
-
}
|
|
158
|
+
const source = readKeySource();
|
|
159
|
+
let apiKey = source.key ?? void 0;
|
|
160
|
+
let keySource = source.kind === "env" ? "env" : source.kind === "config" ? "config-file" : null;
|
|
117
161
|
if (!apiKey) {
|
|
118
162
|
for (const f of SHELL_FILES) {
|
|
119
163
|
try {
|
|
@@ -130,13 +174,47 @@ var main = async () => {
|
|
|
130
174
|
}
|
|
131
175
|
const maskKey = (k) => `pk_${k.split(".")[0]?.slice(3, 7)}...`;
|
|
132
176
|
if (keySource === "config-file") {
|
|
133
|
-
console.log(` ${pass} API key in ${dim("
|
|
177
|
+
console.log(` ${pass} API key in ${dim(source.origin ?? "the config file")} ${dim(`(${maskKey(apiKey)})`)}`);
|
|
134
178
|
} else if (keySource === "profile") {
|
|
135
179
|
console.log(` ${pass} API key in shell profile ${dim(`(${maskKey(apiKey)})`)}`);
|
|
136
180
|
console.log(` ${warn} Re-run ${cyan("npx @pushary/agent-hooks setup")} so hooks read it from ${dim("~/.pushary/config.json")} without a shell reload`);
|
|
137
181
|
} else {
|
|
138
182
|
check(!!apiKey, "API key in environment", apiKey ? maskKey(apiKey) : "PUSHARY_API_KEY not set");
|
|
139
183
|
}
|
|
184
|
+
if (source.drift) {
|
|
185
|
+
check(
|
|
186
|
+
false,
|
|
187
|
+
"One key, not two",
|
|
188
|
+
`shell has ${source.drift.envPrefix}, ${source.drift.configPath} has ${source.drift.configPrefix} \u2014 the shell one wins. Re-run setup, or open a new terminal.`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
let keyVerdict = null;
|
|
192
|
+
if (apiKey) {
|
|
193
|
+
keyVerdict = await checkApiKey(apiKey);
|
|
194
|
+
switch (keyVerdict.kind) {
|
|
195
|
+
case "ok":
|
|
196
|
+
check(
|
|
197
|
+
true,
|
|
198
|
+
"Key verified with pushary.com",
|
|
199
|
+
`${keyVerdict.identity.siteSlug || keyVerdict.identity.siteName} \xB7 plan ${keyVerdict.identity.plan} \xB7 ${keyVerdict.identity.subscriptionStatus}`
|
|
200
|
+
);
|
|
201
|
+
break;
|
|
202
|
+
case "locked":
|
|
203
|
+
check(false, "Plan active", describeKeyCheck(keyVerdict).message);
|
|
204
|
+
for (const line of describeKeyCheck(keyVerdict).next) console.log(` ${dim(line)}`);
|
|
205
|
+
break;
|
|
206
|
+
case "rejected":
|
|
207
|
+
check(false, "Key accepted by pushary.com", describeKeyCheck(keyVerdict).message);
|
|
208
|
+
for (const line of describeKeyCheck(keyVerdict).next) console.log(` ${dim(line)}`);
|
|
209
|
+
break;
|
|
210
|
+
case "malformed":
|
|
211
|
+
check(false, "Key format", "expected pk_xxx.xxx");
|
|
212
|
+
break;
|
|
213
|
+
case "unreachable":
|
|
214
|
+
console.log(` ${warn} Could not verify the key with pushary.com ${dim(`(${keyVerdict.detail})`)}`);
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
140
218
|
const CLAUDE_JSON = join(homedir(), ".claude.json");
|
|
141
219
|
const claudeJson = readJson(CLAUDE_JSON);
|
|
142
220
|
const mcpServers = claudeJson?.mcpServers ?? {};
|
|
@@ -179,10 +257,10 @@ var main = async () => {
|
|
|
179
257
|
} else {
|
|
180
258
|
check(false, "Claude Code: settings.json", "not found");
|
|
181
259
|
}
|
|
182
|
-
const codexConfigPath =
|
|
183
|
-
const codexHooksPath =
|
|
184
|
-
const
|
|
185
|
-
if (existsSync(codexConfigPath) ||
|
|
260
|
+
const codexConfigPath = codexConfigToml();
|
|
261
|
+
const codexHooksPath = codexHooksJson();
|
|
262
|
+
const codexHooksConfig = readJson(codexHooksPath);
|
|
263
|
+
if (existsSync(codexConfigPath) || codexHooksConfig) {
|
|
186
264
|
const codexConfig = existsSync(codexConfigPath) ? readFileSync(codexConfigPath, "utf-8") : "";
|
|
187
265
|
let codexParsed = {};
|
|
188
266
|
try {
|
|
@@ -221,11 +299,11 @@ var main = async () => {
|
|
|
221
299
|
}
|
|
222
300
|
}
|
|
223
301
|
const codexNotifyPath = codexConfig.match(/["']([^"']*pushary-codex[^"']*)["']/)?.[1] ?? null;
|
|
224
|
-
const hooksInstalled = !!
|
|
302
|
+
const hooksInstalled = !!codexHooksConfig && hasCodexHooks(codexHooksConfig);
|
|
225
303
|
if (hooksInstalled) {
|
|
226
|
-
const missingEvents = missingCodexHookEvents(
|
|
304
|
+
const missingEvents = missingCodexHookEvents(codexHooksConfig);
|
|
227
305
|
check(missingEvents.length === 0, "Codex: native hooks installed", missingEvents.length === 0 ? "all 6 events" : `missing ${missingEvents.join(", ")}, re-run setup`);
|
|
228
|
-
const hookCommand = extractHookCommand(
|
|
306
|
+
const hookCommand = extractHookCommand(codexHooksConfig.hooks?.PreToolUse, "pushary-codex-hook") ?? extractHookCommand(codexHooksConfig.hooks?.PermissionRequest, "pushary-codex-hook");
|
|
229
307
|
if (hookCommand) {
|
|
230
308
|
const resolves = commandResolves(hookCommand);
|
|
231
309
|
check(resolves, "Codex: hook command resolves", resolves ? hookCommand : `not on PATH: ${hookCommand}`);
|
|
@@ -428,25 +506,29 @@ var main = async () => {
|
|
|
428
506
|
}
|
|
429
507
|
console.log();
|
|
430
508
|
console.log(` ${dim("Notification Delivery")}`);
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
509
|
+
const probe = await fetchChannels(apiKey);
|
|
510
|
+
if (probe.kind !== "ok") {
|
|
511
|
+
check(false, "Phone reachable", probe.kind === "unreachable" ? `could not read your devices (${probe.detail})` : probe.kind === "locked" ? "no plan active, so nothing can be delivered" : "the server rejected this key");
|
|
512
|
+
} else if (probe.status.precise) {
|
|
513
|
+
const channels = probe.status;
|
|
514
|
+
const verdict = reachVerdict(channels);
|
|
515
|
+
check(verdict.kind === "yours", "Phone reachable", verdict.kind === "yours" ? describeReach(channels) : verdict.kind === "nobody" ? "nothing connected, run pushary connect or get the app at https://pushary.com/download" : `${describeReach(channels)}; run pushary connect to add your own`);
|
|
516
|
+
}
|
|
517
|
+
if (options.noPush) {
|
|
518
|
+
console.log(` ${dim("\u2013")} Test push skipped ${dim("(--no-push)")}`);
|
|
519
|
+
} else try {
|
|
520
|
+
const result = await callMcpTool(apiKey, "send_notification", {
|
|
521
|
+
title: "Pushary Doctor",
|
|
522
|
+
body: "If you see this, push notifications are working.",
|
|
523
|
+
agentName: "Pushary Doctor"
|
|
524
|
+
}, { sessionId, timeoutMs: 2e4 });
|
|
525
|
+
const sent = typeof result.sent === "number" ? result.sent : (result.delivery?.mobile?.recipients ?? 0) + (result.delivery?.web?.recipients ?? 0);
|
|
526
|
+
check(sent > 0, "Push notification sent", sent > 0 ? `${sent} device${sent === 1 ? "" : "s"} \u2014 check your phone` : "reached 0 devices");
|
|
445
527
|
} catch (err) {
|
|
446
528
|
const msg = err instanceof Error ? err.message : "network error";
|
|
447
529
|
check(false, "Push notification sent", msg);
|
|
448
530
|
}
|
|
449
|
-
const testQuestion = process.stdin.isTTY ? await confirm({ message: "Test question roundtrip? (sends a push notification)", default: false })
|
|
531
|
+
const testQuestion = options.roundtrip ? true : options.noPush || options.json || !process.stdin.isTTY ? false : await confirm({ message: "Test question roundtrip? (sends a push notification)", default: false });
|
|
450
532
|
if (testQuestion) {
|
|
451
533
|
console.log();
|
|
452
534
|
console.log(` ${dim("Question Roundtrip")}`);
|
|
@@ -520,6 +602,20 @@ var main = async () => {
|
|
|
520
602
|
console.log(` ${dim("Run")} ${cyan("npx @pushary/agent-hooks@latest clean")} ${dim("then")} ${cyan("setup")} ${dim("to fix.")}`);
|
|
521
603
|
}
|
|
522
604
|
console.log();
|
|
605
|
+
const keyExit = keyVerdict && (keyVerdict.kind === "rejected" || keyVerdict.kind === "locked" || keyVerdict.kind === "malformed") ? describeKeyCheck(keyVerdict).exitCode : null;
|
|
606
|
+
const exitCode = keyExit ?? (failed.length > 0 ? EXIT.PROBLEMS_FOUND : EXIT.OK);
|
|
607
|
+
if (options.json) {
|
|
608
|
+
process.stdout.write(`${JSON.stringify({
|
|
609
|
+
ok: exitCode === EXIT.OK,
|
|
610
|
+
exitCode,
|
|
611
|
+
keyCheck: keyVerdict?.kind ?? null,
|
|
612
|
+
passed: results.filter((r) => r.passed).length,
|
|
613
|
+
failed: failed.length,
|
|
614
|
+
checks: results.map((r) => ({ label: r.label, passed: r.passed, detail: r.detail ?? null }))
|
|
615
|
+
}, null, 2)}
|
|
616
|
+
`);
|
|
617
|
+
}
|
|
618
|
+
process.exit(exitCode);
|
|
523
619
|
};
|
|
524
620
|
main().catch((err) => {
|
|
525
621
|
console.error(` doctor failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
savePendingQuestion,
|
|
24
24
|
sendNotification,
|
|
25
25
|
waitForAnswer
|
|
26
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-VFBYRR2N.js";
|
|
27
27
|
import {
|
|
28
28
|
getMachineId
|
|
29
29
|
} from "../chunk-RN3NOEJF.js";
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
isGatingMoment,
|
|
32
32
|
recordKeylessMoment
|
|
33
33
|
} from "../chunk-WLGEY4UX.js";
|
|
34
|
-
import "../chunk-
|
|
34
|
+
import "../chunk-HNTKTK5B.js";
|
|
35
35
|
import {
|
|
36
36
|
DECISION_LINE_MAX,
|
|
37
37
|
effectiveWaitSeconds,
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
} from "../chunk-DQAN3JQP.js";
|
|
41
41
|
import {
|
|
42
42
|
getApiKey
|
|
43
|
-
} from "../chunk-
|
|
43
|
+
} from "../chunk-2UMNXADU.js";
|
|
44
44
|
|
|
45
45
|
// bin/pushary-gemini-hook.ts
|
|
46
46
|
import { basename } from "path";
|
package/dist/bin/pushary-hook.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handlePreToolUse
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-HI4AGGE6.js";
|
|
5
5
|
import "../chunk-CAJZAFVS.js";
|
|
6
|
+
import {
|
|
7
|
+
exitOnHelpFlag
|
|
8
|
+
} from "../chunk-HUJQSP4F.js";
|
|
9
|
+
import "../chunk-SML23YOT.js";
|
|
6
10
|
import "../chunk-YHG74UFF.js";
|
|
7
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-VFBYRR2N.js";
|
|
8
12
|
import "../chunk-RN3NOEJF.js";
|
|
9
13
|
import "../chunk-WLGEY4UX.js";
|
|
10
|
-
import "../chunk-
|
|
14
|
+
import "../chunk-HNTKTK5B.js";
|
|
11
15
|
import "../chunk-DQAN3JQP.js";
|
|
12
|
-
import "../chunk-
|
|
16
|
+
import "../chunk-2UMNXADU.js";
|
|
13
17
|
|
|
14
18
|
// bin/pushary-hook.ts
|
|
19
|
+
exitOnHelpFlag("hook");
|
|
15
20
|
var main = async () => {
|
|
16
21
|
let rawInput = "";
|
|
17
22
|
for await (const chunk of process.stdin) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runBrowserLogin
|
|
4
|
+
} from "../chunk-QHOVJXOT.js";
|
|
5
|
+
import "../chunk-3EGEA4KH.js";
|
|
6
|
+
import {
|
|
7
|
+
parseFlags
|
|
8
|
+
} from "../chunk-N4DA4CJB.js";
|
|
9
|
+
import {
|
|
10
|
+
readKeySource,
|
|
11
|
+
writeKey
|
|
12
|
+
} from "../chunk-B26DNXCA.js";
|
|
13
|
+
import {
|
|
14
|
+
checkApiKey,
|
|
15
|
+
describeKeyCheck
|
|
16
|
+
} from "../chunk-UKNAAVEE.js";
|
|
17
|
+
import {
|
|
18
|
+
createIo
|
|
19
|
+
} from "../chunk-TX7KBKT7.js";
|
|
20
|
+
import {
|
|
21
|
+
UsageError
|
|
22
|
+
} from "../chunk-7QLSKOSU.js";
|
|
23
|
+
import "../chunk-BC3VCZ3E.js";
|
|
24
|
+
import {
|
|
25
|
+
exitOnHelpFlag
|
|
26
|
+
} from "../chunk-HUJQSP4F.js";
|
|
27
|
+
import {
|
|
28
|
+
EXIT,
|
|
29
|
+
getPackageVersion
|
|
30
|
+
} from "../chunk-SML23YOT.js";
|
|
31
|
+
import {
|
|
32
|
+
isValidApiKey
|
|
33
|
+
} from "../chunk-DQAN3JQP.js";
|
|
34
|
+
import "../chunk-2UMNXADU.js";
|
|
35
|
+
|
|
36
|
+
// bin/pushary-login.ts
|
|
37
|
+
exitOnHelpFlag("login");
|
|
38
|
+
var parseLoginOptions = (argv) => {
|
|
39
|
+
const flags = parseFlags(argv, {
|
|
40
|
+
bools: ["with-token", "no-browser", "json", "force", "help", "h"],
|
|
41
|
+
values: []
|
|
42
|
+
});
|
|
43
|
+
if (flags.command !== void 0 && flags.command !== "login") {
|
|
44
|
+
throw new UsageError(`login takes no positional arguments (got ${flags.command})`);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
withToken: flags.bools.has("with-token"),
|
|
48
|
+
noBrowser: flags.bools.has("no-browser"),
|
|
49
|
+
json: flags.bools.has("json"),
|
|
50
|
+
force: flags.bools.has("force")
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
var options;
|
|
54
|
+
try {
|
|
55
|
+
options = parseLoginOptions(process.argv.slice(2));
|
|
56
|
+
} catch (err) {
|
|
57
|
+
process.stderr.write(` ! ${err instanceof Error ? err.message : String(err)}
|
|
58
|
+
`);
|
|
59
|
+
process.stderr.write(" Run `pushary login --help` for the full list.\n");
|
|
60
|
+
process.exit(EXIT.USAGE);
|
|
61
|
+
}
|
|
62
|
+
var { bold, dim, green, cyan, yellow } = createIo();
|
|
63
|
+
var check = green("\u2713");
|
|
64
|
+
var say = options.json ? (line) => process.stderr.write(`${line}
|
|
65
|
+
`) : (line) => process.stdout.write(`${line}
|
|
66
|
+
`);
|
|
67
|
+
var emit = (payload) => {
|
|
68
|
+
if (options.json) process.stdout.write(`${JSON.stringify(payload, null, 2)}
|
|
69
|
+
`);
|
|
70
|
+
};
|
|
71
|
+
var readStdin = async () => {
|
|
72
|
+
const chunks = [];
|
|
73
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
74
|
+
return Buffer.concat(chunks).toString("utf8").trim();
|
|
75
|
+
};
|
|
76
|
+
var store = (apiKey, siteSlug, keyName) => {
|
|
77
|
+
const result = writeKey(apiKey);
|
|
78
|
+
say("");
|
|
79
|
+
say(` ${check} ${bold("Signed in.")}${siteSlug ? ` ${dim(`Site ${siteSlug}`)}` : ""}`);
|
|
80
|
+
say(` ${dim(`Key saved to ${result.configPath}`)}`);
|
|
81
|
+
if (result.rc.kind === "written" || result.rc.kind === "updated") {
|
|
82
|
+
say(` ${dim(`Shell export ${result.rc.kind} in ${result.rc.path}`)}`);
|
|
83
|
+
} else if (result.rc.kind === "declined") {
|
|
84
|
+
say(` ${yellow("!")} ${dim(`Left ${result.rc.path} alone: line ${result.rc.declinedLine} already sets PUSHARY_API_KEY.`)}`);
|
|
85
|
+
}
|
|
86
|
+
emit({ ok: true, siteSlug, keyName, configPath: result.configPath, rc: result.rc.kind });
|
|
87
|
+
};
|
|
88
|
+
var loginWithToken = async () => {
|
|
89
|
+
const token = await readStdin();
|
|
90
|
+
if (!token) {
|
|
91
|
+
say(` ${yellow("!")} Nothing arrived on stdin. Pipe the key in: pushary login --with-token < key.txt`);
|
|
92
|
+
emit({ ok: false, reason: "no_input" });
|
|
93
|
+
process.exit(EXIT.INPUT_REQUIRED);
|
|
94
|
+
}
|
|
95
|
+
if (!isValidApiKey(token)) {
|
|
96
|
+
say(` ${yellow("!")} That does not look like a Pushary key. Expected pk_xxx.xxx`);
|
|
97
|
+
emit({ ok: false, reason: "malformed" });
|
|
98
|
+
process.exit(EXIT.USAGE);
|
|
99
|
+
}
|
|
100
|
+
const verdict = await checkApiKey(token);
|
|
101
|
+
if (verdict.kind !== "ok") {
|
|
102
|
+
const report = describeKeyCheck(verdict);
|
|
103
|
+
say(` ${yellow("!")} ${report.message}`);
|
|
104
|
+
for (const line of report.next) say(` ${dim(line)}`);
|
|
105
|
+
emit({ ok: false, reason: verdict.kind });
|
|
106
|
+
if (verdict.kind !== "unreachable") process.exit(report.exitCode);
|
|
107
|
+
}
|
|
108
|
+
store(token, verdict.kind === "ok" ? verdict.identity.siteSlug : "", "pasted key");
|
|
109
|
+
process.exit(EXIT.OK);
|
|
110
|
+
};
|
|
111
|
+
var main = async () => {
|
|
112
|
+
if (options.withToken) await loginWithToken();
|
|
113
|
+
if (!options.force) {
|
|
114
|
+
const existing = readKeySource();
|
|
115
|
+
if (existing.key) {
|
|
116
|
+
const verdict = await checkApiKey(existing.key);
|
|
117
|
+
if (verdict.kind === "ok") {
|
|
118
|
+
say("");
|
|
119
|
+
say(` ${check} Already signed in ${dim(`(${verdict.identity.siteSlug})`)}`);
|
|
120
|
+
say(` ${dim("Run")} ${cyan("pushary login --force")} ${dim("to replace this key.")}`);
|
|
121
|
+
emit({ ok: true, reused: true, siteSlug: verdict.identity.siteSlug });
|
|
122
|
+
process.exit(EXIT.OK);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const outcome = await runBrowserLogin(
|
|
127
|
+
{ say, bold, dim, cyan, yellow },
|
|
128
|
+
{ cliVersion: getPackageVersion(), noBrowser: options.noBrowser }
|
|
129
|
+
);
|
|
130
|
+
if (outcome.kind === "ok") {
|
|
131
|
+
store(outcome.apiKey, outcome.siteSlug, outcome.keyName);
|
|
132
|
+
process.exit(EXIT.OK);
|
|
133
|
+
}
|
|
134
|
+
if (outcome.kind === "unavailable") {
|
|
135
|
+
say(` ${yellow("!")} Couldn't start a login ${dim(`(${outcome.detail})`)}`);
|
|
136
|
+
say(` ${dim("Check your connection, or paste a key with")} ${cyan("pushary login --with-token < key.txt")}`);
|
|
137
|
+
emit({ ok: false, reason: "unreachable", detail: outcome.detail });
|
|
138
|
+
process.exit(EXIT.UNREACHABLE);
|
|
139
|
+
}
|
|
140
|
+
if (outcome.kind === "rejected") {
|
|
141
|
+
say("");
|
|
142
|
+
say(` ${yellow("!")} That request was rejected in the browser. Nothing was written.`);
|
|
143
|
+
emit({ ok: false, reason: "rejected" });
|
|
144
|
+
process.exit(EXIT.NOT_CONFIGURED);
|
|
145
|
+
}
|
|
146
|
+
say("");
|
|
147
|
+
say(` ${yellow("!")} The code expired before it was approved.`);
|
|
148
|
+
say(` ${dim("Run")} ${cyan("pushary login")} ${dim("for a fresh one.")}`);
|
|
149
|
+
emit({ ok: false, reason: "expired" });
|
|
150
|
+
process.exit(EXIT.NOT_CONFIGURED);
|
|
151
|
+
};
|
|
152
|
+
main().catch((err) => {
|
|
153
|
+
process.stderr.write(` ! login failed: ${err instanceof Error ? err.message : String(err)}
|
|
154
|
+
`);
|
|
155
|
+
process.exit(EXIT.FAILED);
|
|
156
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
parseFlags
|
|
4
|
+
} from "../chunk-N4DA4CJB.js";
|
|
5
|
+
import {
|
|
6
|
+
clearKey,
|
|
7
|
+
readKeySource
|
|
8
|
+
} from "../chunk-B26DNXCA.js";
|
|
9
|
+
import {
|
|
10
|
+
createIo
|
|
11
|
+
} from "../chunk-TX7KBKT7.js";
|
|
12
|
+
import {
|
|
13
|
+
UsageError
|
|
14
|
+
} from "../chunk-7QLSKOSU.js";
|
|
15
|
+
import "../chunk-BC3VCZ3E.js";
|
|
16
|
+
import {
|
|
17
|
+
exitOnHelpFlag
|
|
18
|
+
} from "../chunk-HUJQSP4F.js";
|
|
19
|
+
import {
|
|
20
|
+
EXIT
|
|
21
|
+
} from "../chunk-SML23YOT.js";
|
|
22
|
+
import "../chunk-2UMNXADU.js";
|
|
23
|
+
|
|
24
|
+
// bin/pushary-logout.ts
|
|
25
|
+
import { existsSync } from "fs";
|
|
26
|
+
import { homedir } from "os";
|
|
27
|
+
import { join } from "path";
|
|
28
|
+
exitOnHelpFlag("logout");
|
|
29
|
+
var parseLogoutOptions = (argv) => {
|
|
30
|
+
const flags = parseFlags(argv, { bools: ["json", "help", "h"], values: [] });
|
|
31
|
+
if (flags.command !== void 0 && flags.command !== "logout") {
|
|
32
|
+
throw new UsageError(`logout takes no positional arguments (got ${flags.command})`);
|
|
33
|
+
}
|
|
34
|
+
return { json: flags.bools.has("json") };
|
|
35
|
+
};
|
|
36
|
+
var options;
|
|
37
|
+
try {
|
|
38
|
+
options = parseLogoutOptions(process.argv.slice(2));
|
|
39
|
+
} catch (err) {
|
|
40
|
+
process.stderr.write(` ! ${err instanceof Error ? err.message : String(err)}
|
|
41
|
+
`);
|
|
42
|
+
process.exit(EXIT.USAGE);
|
|
43
|
+
}
|
|
44
|
+
var { bold, dim, green, cyan, yellow } = createIo();
|
|
45
|
+
var say = options.json ? (line) => process.stderr.write(`${line}
|
|
46
|
+
`) : (line) => process.stdout.write(`${line}
|
|
47
|
+
`);
|
|
48
|
+
var AGENT_CONFIGS = [
|
|
49
|
+
{ label: "Claude Code", path: join(homedir(), ".claude.json") },
|
|
50
|
+
{ label: "Codex", path: join(process.env.CODEX_HOME ?? join(homedir(), ".codex"), "config.toml") },
|
|
51
|
+
{ label: "Gemini CLI", path: join(homedir(), ".gemini", "settings.json") },
|
|
52
|
+
{ label: "Cursor", path: join(homedir(), ".cursor", "mcp.json") }
|
|
53
|
+
];
|
|
54
|
+
var main = () => {
|
|
55
|
+
const before = readKeySource();
|
|
56
|
+
const result = clearKey();
|
|
57
|
+
const remaining = AGENT_CONFIGS.filter((entry) => existsSync(entry.path));
|
|
58
|
+
say("");
|
|
59
|
+
if (!result.removedFromConfig && result.rcLinesDisabled === 0 && before.kind !== "env") {
|
|
60
|
+
say(` ${dim("No key was stored on this machine. Nothing to remove.")}`);
|
|
61
|
+
} else {
|
|
62
|
+
say(` ${green("\u2713")} ${bold("Signed out.")}`);
|
|
63
|
+
if (result.removedFromConfig) say(` ${dim(`Key removed from ${result.configPath}`)}`);
|
|
64
|
+
if (result.rcLinesDisabled > 0) {
|
|
65
|
+
say(` ${dim(`Commented out ${result.rcLinesDisabled} export line in ${result.rcPath}`)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (before.kind === "env") {
|
|
69
|
+
say("");
|
|
70
|
+
say(` ${yellow("!")} PUSHARY_API_KEY is set in this shell, and a child process cannot unset it.`);
|
|
71
|
+
say(` ${dim("Run")} ${cyan("unset PUSHARY_API_KEY")} ${dim("here, or open a new terminal.")}`);
|
|
72
|
+
}
|
|
73
|
+
if (remaining.length > 0) {
|
|
74
|
+
say("");
|
|
75
|
+
say(` ${dim("Still holding a copy of the key, untouched by logout:")}`);
|
|
76
|
+
for (const entry of remaining) say(` ${dim(`\u2022 ${entry.label} ${entry.path}`)}`);
|
|
77
|
+
say(` ${dim("Run")} ${cyan("pushary clean")} ${dim("to remove Pushary from those too.")}`);
|
|
78
|
+
}
|
|
79
|
+
say("");
|
|
80
|
+
say(` ${dim("The key itself is still valid server-side. Revoke it at")} ${cyan("pushary.com/dashboard/agent/settings")}`);
|
|
81
|
+
say("");
|
|
82
|
+
if (options.json) {
|
|
83
|
+
process.stdout.write(
|
|
84
|
+
`${JSON.stringify(
|
|
85
|
+
{
|
|
86
|
+
ok: true,
|
|
87
|
+
removedFromConfig: result.removedFromConfig,
|
|
88
|
+
configPath: result.configPath,
|
|
89
|
+
rcPath: result.rcPath,
|
|
90
|
+
rcLinesDisabled: result.rcLinesDisabled,
|
|
91
|
+
envStillSet: before.kind === "env",
|
|
92
|
+
agentConfigsRetainingKey: remaining.map((entry) => entry.path)
|
|
93
|
+
},
|
|
94
|
+
null,
|
|
95
|
+
2
|
|
96
|
+
)}
|
|
97
|
+
`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
process.exit(EXIT.OK);
|
|
101
|
+
};
|
|
102
|
+
main();
|
package/dist/bin/pushary-mode.js
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
exitOnFailedResponse
|
|
4
|
+
} from "../chunk-R6C4USIV.js";
|
|
5
|
+
import "../chunk-UKNAAVEE.js";
|
|
6
|
+
import {
|
|
7
|
+
createIo
|
|
8
|
+
} from "../chunk-TX7KBKT7.js";
|
|
9
|
+
import "../chunk-7QLSKOSU.js";
|
|
10
|
+
import {
|
|
11
|
+
exitOnHelpFlag
|
|
12
|
+
} from "../chunk-HUJQSP4F.js";
|
|
13
|
+
import {
|
|
14
|
+
EXIT
|
|
15
|
+
} from "../chunk-SML23YOT.js";
|
|
16
|
+
import "../chunk-DQAN3JQP.js";
|
|
2
17
|
import {
|
|
3
18
|
getApiKey,
|
|
4
19
|
getBaseUrl
|
|
5
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-2UMNXADU.js";
|
|
6
21
|
|
|
7
22
|
// bin/pushary-mode.ts
|
|
23
|
+
exitOnHelpFlag("mode");
|
|
8
24
|
var VALID_MODES = ["push_only", "push_first", "terminal_only", "notify_only"];
|
|
9
|
-
var dim = (
|
|
10
|
-
var green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
11
|
-
var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
12
|
-
var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
25
|
+
var { dim, green, cyan, yellow } = createIo();
|
|
13
26
|
var parseDuration = (value) => {
|
|
14
27
|
const match = value.match(/^(\d+)(m|h)$/);
|
|
15
28
|
if (!match) return null;
|
|
@@ -30,6 +43,7 @@ var main = async () => {
|
|
|
30
43
|
};
|
|
31
44
|
if (!mode || mode === "status") {
|
|
32
45
|
const res2 = await fetch(`${baseUrl}/api/mcp/mode`, { headers });
|
|
46
|
+
if (!res2.ok) await exitOnFailedResponse(res2, baseUrl);
|
|
33
47
|
const data2 = await res2.json();
|
|
34
48
|
if (!data2.override) {
|
|
35
49
|
console.log(` Mode: ${cyan("default")} ${dim("(using per-tool policies)")}`);
|
|
@@ -44,7 +58,8 @@ var main = async () => {
|
|
|
44
58
|
return;
|
|
45
59
|
}
|
|
46
60
|
if (mode === "clear" || mode === "reset") {
|
|
47
|
-
await fetch(`${baseUrl}/api/mcp/mode`, { method: "DELETE", headers });
|
|
61
|
+
const res2 = await fetch(`${baseUrl}/api/mcp/mode`, { method: "DELETE", headers });
|
|
62
|
+
if (!res2.ok) await exitOnFailedResponse(res2, baseUrl);
|
|
48
63
|
console.log(` ${green("\u2713")} Mode override cleared \u2014 using per-tool policies`);
|
|
49
64
|
return;
|
|
50
65
|
}
|
|
@@ -53,14 +68,14 @@ var main = async () => {
|
|
|
53
68
|
console.log(` Valid modes: ${VALID_MODES.join(", ")}`);
|
|
54
69
|
console.log(` ${dim("Usage: pushary mode <mode> [--for <duration>]")}`);
|
|
55
70
|
console.log(` ${dim("Example: pushary mode push_only --for 30m")}`);
|
|
56
|
-
|
|
71
|
+
process.exit(EXIT.USAGE);
|
|
57
72
|
}
|
|
58
73
|
let ttlSeconds;
|
|
59
74
|
if (forFlag === "--for" && forValue) {
|
|
60
75
|
const parsed = parseDuration(forValue);
|
|
61
76
|
if (!parsed) {
|
|
62
77
|
console.log(` ${yellow("!")} Invalid duration: ${forValue} ${dim("(use e.g. 30m, 1h)")}`);
|
|
63
|
-
|
|
78
|
+
process.exit(EXIT.USAGE);
|
|
64
79
|
}
|
|
65
80
|
ttlSeconds = parsed;
|
|
66
81
|
}
|
|
@@ -69,11 +84,7 @@ var main = async () => {
|
|
|
69
84
|
headers,
|
|
70
85
|
body: JSON.stringify({ mode, ttlSeconds })
|
|
71
86
|
});
|
|
72
|
-
if (!res.ok)
|
|
73
|
-
const err = await res.json();
|
|
74
|
-
console.log(` ${yellow("!")} Failed: ${err.error ?? res.statusText}`);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
87
|
+
if (!res.ok) await exitOnFailedResponse(res, baseUrl);
|
|
77
88
|
const data = await res.json();
|
|
78
89
|
console.log(` ${green("\u2713")} Mode set to ${cyan(data.override.mode)}`);
|
|
79
90
|
if (data.override.expiresAt) {
|