@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
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
exitOnHelpFlag
|
|
4
|
+
} from "../chunk-HUJQSP4F.js";
|
|
5
|
+
import "../chunk-SML23YOT.js";
|
|
2
6
|
import {
|
|
3
7
|
readLedgerSummary
|
|
4
8
|
} from "../chunk-WLGEY4UX.js";
|
|
5
9
|
import "../chunk-DQAN3JQP.js";
|
|
6
10
|
import {
|
|
7
11
|
getApiKey
|
|
8
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-2UMNXADU.js";
|
|
9
13
|
|
|
10
14
|
// bin/pushary-stats.ts
|
|
15
|
+
exitOnHelpFlag("stats");
|
|
11
16
|
var DAYS = 7;
|
|
12
17
|
var summary = readLedgerSummary(DAYS);
|
|
13
18
|
var connected = true;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
describeReach,
|
|
4
|
+
fetchChannels,
|
|
5
|
+
reachVerdict
|
|
6
|
+
} from "../chunk-GX64YGU3.js";
|
|
7
|
+
import "../chunk-3EGEA4KH.js";
|
|
8
|
+
import {
|
|
9
|
+
readKeySource
|
|
10
|
+
} from "../chunk-B26DNXCA.js";
|
|
11
|
+
import {
|
|
12
|
+
checkApiKey,
|
|
13
|
+
describeKeyCheck
|
|
14
|
+
} from "../chunk-UKNAAVEE.js";
|
|
15
|
+
import {
|
|
16
|
+
createIo
|
|
17
|
+
} from "../chunk-TX7KBKT7.js";
|
|
18
|
+
import "../chunk-7QLSKOSU.js";
|
|
19
|
+
import "../chunk-BC3VCZ3E.js";
|
|
20
|
+
import {
|
|
21
|
+
exitOnHelpFlag
|
|
22
|
+
} from "../chunk-HUJQSP4F.js";
|
|
23
|
+
import {
|
|
24
|
+
EXIT
|
|
25
|
+
} from "../chunk-SML23YOT.js";
|
|
26
|
+
import "../chunk-HNTKTK5B.js";
|
|
27
|
+
import "../chunk-DQAN3JQP.js";
|
|
28
|
+
import "../chunk-2UMNXADU.js";
|
|
29
|
+
|
|
30
|
+
// src/status.ts
|
|
31
|
+
var keySourceLine = (source) => {
|
|
32
|
+
if (source.kind === "none") {
|
|
33
|
+
return { label: "Key", value: "none found", ok: false };
|
|
34
|
+
}
|
|
35
|
+
const prefix = source.key ? source.key.split(".")[0] : "unknown";
|
|
36
|
+
const where = source.kind === "env" ? "$PUSHARY_API_KEY" : source.origin ?? "config file";
|
|
37
|
+
return { label: "Key", value: `${prefix} from ${where}`, ok: true };
|
|
38
|
+
};
|
|
39
|
+
var reachLine = (channels) => {
|
|
40
|
+
const verdict = reachVerdict(channels);
|
|
41
|
+
const line = describeReach(channels);
|
|
42
|
+
if (verdict.kind === "nobody") return { label: "Reachable", value: line, ok: false };
|
|
43
|
+
if (verdict.kind === "yours") {
|
|
44
|
+
const inPocket = (channels.yours?.phoneReachable ?? channels.phoneReachable) || channels.webPhone > 0;
|
|
45
|
+
return { label: "Reachable", value: line, ok: inPocket };
|
|
46
|
+
}
|
|
47
|
+
return { label: "Reachable", value: line, ok: false };
|
|
48
|
+
};
|
|
49
|
+
var siteLine = (channels) => {
|
|
50
|
+
const site = channels.site;
|
|
51
|
+
if (site === null || site.otherMembers === 0) return null;
|
|
52
|
+
const devices = site.appDevices + site.webActive;
|
|
53
|
+
return {
|
|
54
|
+
label: "Team",
|
|
55
|
+
value: `${devices} device${devices === 1 ? "" : "s"} across ${site.otherMembers + 1} members`,
|
|
56
|
+
ok: null
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
var buildStatusReport = (facts) => {
|
|
60
|
+
const lines = [keySourceLine(facts.source)];
|
|
61
|
+
if (facts.source.drift) {
|
|
62
|
+
lines.push({
|
|
63
|
+
label: "Key drift",
|
|
64
|
+
value: `$PUSHARY_API_KEY (${facts.source.drift.envPrefix}) differs from ${facts.source.drift.configPath} (${facts.source.drift.configPrefix}); the environment wins`,
|
|
65
|
+
ok: false
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (facts.source.kind === "none") {
|
|
69
|
+
return {
|
|
70
|
+
lines,
|
|
71
|
+
headline: "Not set up on this machine.",
|
|
72
|
+
next: ["Run `pushary setup` to connect an agent."],
|
|
73
|
+
exitCode: EXIT.NOT_CONFIGURED
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const check = facts.keyCheck;
|
|
77
|
+
if (check && check.kind !== "ok") {
|
|
78
|
+
const report = describeKeyCheck(check);
|
|
79
|
+
lines.push({ label: "Server", value: report.message, ok: check.kind === "unreachable" ? null : false });
|
|
80
|
+
return { lines, headline: report.message, next: [...report.next], exitCode: report.exitCode };
|
|
81
|
+
}
|
|
82
|
+
if (check?.kind === "ok") {
|
|
83
|
+
const id = check.identity;
|
|
84
|
+
lines.push({ label: "Site", value: `${id.siteName || id.siteSlug} (${id.siteSlug})`, ok: true });
|
|
85
|
+
lines.push({
|
|
86
|
+
label: "Plan",
|
|
87
|
+
value: id.plan === id.surfacePlan ? `${id.plan} \xB7 ${id.subscriptionStatus}` : `${id.plan} (from ${id.surfacePlan}) \xB7 ${id.subscriptionStatus}`,
|
|
88
|
+
ok: true
|
|
89
|
+
});
|
|
90
|
+
if (id.workspaceMembers > 1) {
|
|
91
|
+
lines.push({
|
|
92
|
+
label: "Workspace",
|
|
93
|
+
value: `${id.workspaceMembers} members \u2014 some devices below may not be yours`,
|
|
94
|
+
ok: null
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const channels = facts.channels?.kind === "ok" ? facts.channels.status : null;
|
|
99
|
+
if (channels) {
|
|
100
|
+
lines.push(reachLine(channels));
|
|
101
|
+
const site = siteLine(channels);
|
|
102
|
+
if (site) lines.push(site);
|
|
103
|
+
} else if (facts.channels && facts.channels.kind === "unreachable") {
|
|
104
|
+
lines.push({ label: "Reachable", value: `could not check (${facts.channels.detail})`, ok: null });
|
|
105
|
+
}
|
|
106
|
+
if (facts.source.drift) {
|
|
107
|
+
return {
|
|
108
|
+
lines,
|
|
109
|
+
headline: "Working, but two different keys are configured.",
|
|
110
|
+
next: [
|
|
111
|
+
"The shell export wins over the config file, and it may be the older one.",
|
|
112
|
+
"Run `pushary setup` to line them up, or open a new terminal."
|
|
113
|
+
],
|
|
114
|
+
exitCode: EXIT.PROBLEMS_FOUND
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (facts.channels && facts.channels.kind === "unreachable") {
|
|
118
|
+
return {
|
|
119
|
+
lines,
|
|
120
|
+
headline: "Key is fine; could not read which devices are connected.",
|
|
121
|
+
next: ["Run `pushary status` again in a minute."],
|
|
122
|
+
exitCode: EXIT.UNREACHABLE
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (channels) {
|
|
126
|
+
const verdict = reachVerdict(channels);
|
|
127
|
+
if (verdict.kind !== "yours") {
|
|
128
|
+
return {
|
|
129
|
+
lines,
|
|
130
|
+
headline: verdict.kind === "nobody" ? "Configured, but no device can receive an approval." : verdict.kind === "others" ? "Configured, but the only devices here are your teammates'." : "Configured, and nothing proves an approval reaches you.",
|
|
131
|
+
next: verdict.kind === "nobody" ? ["Run `pushary connect` to add your phone, or install the Pushary app."] : ["Run `pushary connect` to add your own phone.", "Or `pushary doctor --roundtrip` to prove one of these is yours."],
|
|
132
|
+
exitCode: EXIT.NO_DEVICE
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (!(channels.yours?.phoneReachable ?? channels.phoneReachable) && channels.webPhone === 0) {
|
|
136
|
+
return {
|
|
137
|
+
lines,
|
|
138
|
+
headline: "Configured, but nothing in your pocket can receive an approval.",
|
|
139
|
+
next: ["Run `pushary connect` and connect your phone, or install the Pushary app."],
|
|
140
|
+
exitCode: EXIT.NO_DEVICE
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
lines,
|
|
146
|
+
headline: "Ready.",
|
|
147
|
+
next: [],
|
|
148
|
+
exitCode: EXIT.OK
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
var statusJson = (facts, report) => ({
|
|
152
|
+
ok: report.exitCode === EXIT.OK,
|
|
153
|
+
headline: report.headline,
|
|
154
|
+
exitCode: report.exitCode,
|
|
155
|
+
keySource: facts.source.kind,
|
|
156
|
+
keyOrigin: facts.source.origin,
|
|
157
|
+
keyPrefix: facts.source.key ? facts.source.key.split(".")[0] : null,
|
|
158
|
+
drift: facts.source.drift ? {
|
|
159
|
+
env: facts.source.drift.envPrefix,
|
|
160
|
+
config: facts.source.drift.configPrefix,
|
|
161
|
+
configPath: facts.source.drift.configPath
|
|
162
|
+
} : null,
|
|
163
|
+
keyCheck: facts.keyCheck?.kind ?? null,
|
|
164
|
+
identity: facts.keyCheck?.kind === "ok" ? facts.keyCheck.identity : null,
|
|
165
|
+
channels: facts.channels?.kind === "ok" ? facts.channels.status : null,
|
|
166
|
+
channelsProbe: facts.channels?.kind ?? null
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// bin/pushary-status.ts
|
|
170
|
+
exitOnHelpFlag("status");
|
|
171
|
+
var { bold, dim, green, red, yellow } = createIo();
|
|
172
|
+
var args = process.argv.slice(2);
|
|
173
|
+
var json = args.includes("--json");
|
|
174
|
+
var main = async () => {
|
|
175
|
+
const source = readKeySource();
|
|
176
|
+
let keyCheck = null;
|
|
177
|
+
let channels = null;
|
|
178
|
+
if (source.key) {
|
|
179
|
+
keyCheck = await checkApiKey(source.key);
|
|
180
|
+
if (keyCheck.kind === "ok") channels = await fetchChannels(source.key);
|
|
181
|
+
}
|
|
182
|
+
const facts = { source, keyCheck, channels };
|
|
183
|
+
const report = buildStatusReport(facts);
|
|
184
|
+
if (json) {
|
|
185
|
+
process.stdout.write(`${JSON.stringify(statusJson(facts, report), null, 2)}
|
|
186
|
+
`);
|
|
187
|
+
process.exit(report.exitCode);
|
|
188
|
+
}
|
|
189
|
+
const width = Math.max(...report.lines.map((line) => line.label.length)) + 2;
|
|
190
|
+
console.log();
|
|
191
|
+
console.log(` ${bold("Pushary")}`);
|
|
192
|
+
console.log();
|
|
193
|
+
for (const line of report.lines) {
|
|
194
|
+
const icon = line.ok === true ? green("\u2713") : line.ok === false ? red("\u2717") : dim("\xB7");
|
|
195
|
+
console.log(` ${icon} ${line.label.padEnd(width)}${line.value}`);
|
|
196
|
+
}
|
|
197
|
+
console.log();
|
|
198
|
+
console.log(` ${report.exitCode === 0 ? green(bold(report.headline)) : yellow(bold(report.headline))}`);
|
|
199
|
+
for (const next of report.next) console.log(` ${dim(next)}`);
|
|
200
|
+
console.log();
|
|
201
|
+
process.exit(report.exitCode);
|
|
202
|
+
};
|
|
203
|
+
main().catch((err) => {
|
|
204
|
+
console.error(` ${yellow("!")} status failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleStop
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-VFBYRR2N.js";
|
|
5
5
|
import "../chunk-RN3NOEJF.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-HNTKTK5B.js";
|
|
7
7
|
import "../chunk-DQAN3JQP.js";
|
|
8
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-2UMNXADU.js";
|
|
9
9
|
|
|
10
10
|
// bin/pushary-stop-hook.ts
|
|
11
11
|
var main = async () => {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
handleStopFailure
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-VFBYRR2N.js";
|
|
5
5
|
import "../chunk-RN3NOEJF.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-HNTKTK5B.js";
|
|
7
7
|
import "../chunk-DQAN3JQP.js";
|
|
8
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-2UMNXADU.js";
|
|
9
9
|
|
|
10
10
|
// bin/pushary-stopfailure-hook.ts
|
|
11
11
|
var main = async () => {
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createIo
|
|
4
|
+
} from "../chunk-TX7KBKT7.js";
|
|
5
|
+
import {
|
|
6
|
+
exitOnHelpFlag
|
|
7
|
+
} from "../chunk-HUJQSP4F.js";
|
|
8
|
+
import "../chunk-SML23YOT.js";
|
|
2
9
|
import {
|
|
3
10
|
getApiKey,
|
|
4
11
|
getBaseUrl
|
|
5
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-2UMNXADU.js";
|
|
6
13
|
|
|
7
14
|
// bin/pushary-suggestions.ts
|
|
8
|
-
|
|
9
|
-
var green = (
|
|
10
|
-
var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
11
|
-
var red = (s) => `\x1B[31m${s}\x1B[0m`;
|
|
15
|
+
exitOnHelpFlag("suggestions");
|
|
16
|
+
var { dim, green, cyan, red } = createIo();
|
|
12
17
|
var errorMessage = async (res) => {
|
|
13
18
|
try {
|
|
14
19
|
const body = await res.json();
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
execNpm,
|
|
4
|
-
npmErrorMessage,
|
|
5
|
-
resolveGlobalBinDir
|
|
6
|
-
} from "../chunk-J7JWI3KU.js";
|
|
7
2
|
import {
|
|
8
3
|
addClaudeMcpServer,
|
|
9
4
|
addPusharyHooks,
|
|
10
5
|
addPusharyToolPermissions
|
|
11
6
|
} from "../chunk-CAJZAFVS.js";
|
|
7
|
+
import {
|
|
8
|
+
execNpm,
|
|
9
|
+
npmErrorMessage,
|
|
10
|
+
resolveGlobalBinDir
|
|
11
|
+
} from "../chunk-MUEW424A.js";
|
|
12
|
+
import {
|
|
13
|
+
exitOnHelpFlag
|
|
14
|
+
} from "../chunk-HUJQSP4F.js";
|
|
15
|
+
import "../chunk-SML23YOT.js";
|
|
12
16
|
import "../chunk-DQAN3JQP.js";
|
|
13
17
|
import {
|
|
14
18
|
getApiKey
|
|
15
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-2UMNXADU.js";
|
|
16
20
|
|
|
17
21
|
// src/reapply.ts
|
|
18
22
|
import { join, dirname } from "path";
|
|
@@ -64,6 +68,7 @@ var reapplyClaudeSettings = (apiKey, paths = {}) => {
|
|
|
64
68
|
};
|
|
65
69
|
|
|
66
70
|
// bin/pushary-upgrade.ts
|
|
71
|
+
exitOnHelpFlag("upgrade");
|
|
67
72
|
var getInstalledVersion = () => {
|
|
68
73
|
try {
|
|
69
74
|
return execNpm("list -g --no-workspaces @pushary/agent-hooks --depth=0", { timeout: 1e4 }).toString().match(/@pushary\/agent-hooks@([\d.]+)/)?.[1] ?? "";
|
package/dist/bin/pushary-wait.js
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
exitOnFailedResponse
|
|
4
|
+
} from "../chunk-R6C4USIV.js";
|
|
2
5
|
import {
|
|
3
6
|
describeWaitLadder
|
|
4
7
|
} from "../chunk-IIENZE2J.js";
|
|
8
|
+
import "../chunk-UKNAAVEE.js";
|
|
9
|
+
import {
|
|
10
|
+
createIo
|
|
11
|
+
} from "../chunk-TX7KBKT7.js";
|
|
12
|
+
import "../chunk-7QLSKOSU.js";
|
|
13
|
+
import {
|
|
14
|
+
exitOnHelpFlag
|
|
15
|
+
} from "../chunk-HUJQSP4F.js";
|
|
16
|
+
import {
|
|
17
|
+
EXIT
|
|
18
|
+
} from "../chunk-SML23YOT.js";
|
|
19
|
+
import "../chunk-DQAN3JQP.js";
|
|
5
20
|
import {
|
|
6
21
|
getApiKey,
|
|
7
22
|
getBaseUrl
|
|
8
|
-
} from "../chunk-
|
|
23
|
+
} from "../chunk-2UMNXADU.js";
|
|
9
24
|
|
|
10
25
|
// bin/pushary-wait.ts
|
|
11
|
-
|
|
12
|
-
var bold = (
|
|
13
|
-
var green = (s) => `\x1B[32m${s}\x1B[0m`;
|
|
14
|
-
var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
|
|
15
|
-
var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
26
|
+
exitOnHelpFlag("wait");
|
|
27
|
+
var { dim, bold, green, cyan, yellow } = createIo();
|
|
16
28
|
var MAX_WINDOW = 300;
|
|
17
29
|
var printLadder = (data) => {
|
|
18
30
|
console.log();
|
|
@@ -36,7 +48,7 @@ var printLadder = (data) => {
|
|
|
36
48
|
};
|
|
37
49
|
var fetchPolicy = async (baseUrl, headers) => {
|
|
38
50
|
const res = await fetch(`${baseUrl}/api/mcp/policy`, { headers });
|
|
39
|
-
if (!res.ok)
|
|
51
|
+
if (!res.ok) await exitOnFailedResponse(res, baseUrl);
|
|
40
52
|
return res.json();
|
|
41
53
|
};
|
|
42
54
|
var setWindow = async (baseUrl, headers, seconds) => {
|
|
@@ -45,10 +57,7 @@ var setWindow = async (baseUrl, headers, seconds) => {
|
|
|
45
57
|
headers,
|
|
46
58
|
body: JSON.stringify({ defaultPushFirstSeconds: seconds })
|
|
47
59
|
});
|
|
48
|
-
if (!res.ok)
|
|
49
|
-
const err = await res.json().catch(() => ({}));
|
|
50
|
-
throw new Error(err.error ?? res.statusText);
|
|
51
|
-
}
|
|
60
|
+
if (!res.ok) await exitOnFailedResponse(res, baseUrl);
|
|
52
61
|
const data = await res.json();
|
|
53
62
|
return data.defaultPushFirstSeconds;
|
|
54
63
|
};
|
|
@@ -75,7 +84,7 @@ var main = async () => {
|
|
|
75
84
|
if (!Number.isFinite(seconds) || seconds < 0 || seconds > MAX_WINDOW) {
|
|
76
85
|
console.log(` ${yellow("!")} Invalid window: ${arg} ${dim(`(use 0-${MAX_WINDOW} seconds, or "status" / "clear")`)}`);
|
|
77
86
|
console.log(` ${dim("Usage: pushary wait <seconds> | status | clear")}`);
|
|
78
|
-
|
|
87
|
+
process.exit(EXIT.USAGE);
|
|
79
88
|
}
|
|
80
89
|
const value = await setWindow(baseUrl, headers, Math.trunc(seconds));
|
|
81
90
|
console.log(` ${green("\u2713")} Default push window set to ${cyan(`${value}s`)} ${dim("\u2014 ping your phone that long, then the terminal takes over")}`);
|
package/dist/bin/pushary.js
CHANGED
|
@@ -1,65 +1,47 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
COMMANDS,
|
|
4
|
+
EXIT,
|
|
5
|
+
getPackageVersion,
|
|
6
|
+
isCommandName,
|
|
7
|
+
renderHelp
|
|
8
|
+
} from "../chunk-SML23YOT.js";
|
|
2
9
|
|
|
3
10
|
// bin/pushary.ts
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
with nothing running. Leave it in a project dir; it launches a headless
|
|
38
|
-
'pushary claude --remote' when you send a prompt from the app.
|
|
39
|
-
doctor Verify your Pushary installation is working
|
|
40
|
-
clean Remove all Pushary configuration (--yes for non-interactive)
|
|
41
|
-
mode Switch approval mode (push_only, push_first, terminal_only)
|
|
42
|
-
wait Show or set the "wait for your phone" ladder (pushary wait 45)
|
|
43
|
-
stats Show the approval moments your agents hit while not connected
|
|
44
|
-
suggestions List rules mined from your own approvals; accept one with
|
|
45
|
-
'pushary suggestions accept <id>' to always-allow that pattern
|
|
46
|
-
upgrade Update the globally installed hooks to the latest version
|
|
47
|
-
hook Run as a PreToolUse hook (reads stdin, writes stdout)
|
|
48
|
-
|
|
49
|
-
Setup options:
|
|
50
|
-
--key <pk_xxx.xxx> Use this API key instead of prompting for one
|
|
51
|
-
--connect web Connect your phone via the PWA QR code (default)
|
|
52
|
-
--connect app Pair the native Pushary app, no key to paste
|
|
53
|
-
--skip-phone Skip the phone connect step (CI or non-interactive shells)
|
|
54
|
-
|
|
55
|
-
Usage:
|
|
56
|
-
npx @pushary/agent-hooks@latest setup
|
|
57
|
-
npx @pushary/agent-hooks@latest setup --key pk_xxx.xxx --skip-phone
|
|
58
|
-
npx @pushary/agent-hooks@latest doctor
|
|
59
|
-
npx @pushary/agent-hooks@latest mode push_only --for 30m
|
|
60
|
-
npx @pushary/agent-hooks@latest wait 45
|
|
61
|
-
pushary claude # native terminal, reachable from your phone when idle
|
|
62
|
-
pushary claude --remote -p "start the refactor" # start headless, drive entirely from your phone
|
|
63
|
-
pushary daemon # keep this machine ready to spawn a session from your phone
|
|
11
|
+
var LOADERS = {
|
|
12
|
+
setup: () => import("./pushary-setup.js"),
|
|
13
|
+
claude: () => import("./pushary-claude.js"),
|
|
14
|
+
daemon: () => import("./pushary-daemon.js"),
|
|
15
|
+
status: () => import("./pushary-status.js"),
|
|
16
|
+
login: () => import("./pushary-login.js"),
|
|
17
|
+
logout: () => import("./pushary-logout.js"),
|
|
18
|
+
connect: () => import("./pushary-connect.js"),
|
|
19
|
+
doctor: () => import("./pushary-doctor.js"),
|
|
20
|
+
clean: () => import("./pushary-clean.js"),
|
|
21
|
+
mode: () => import("./pushary-mode.js"),
|
|
22
|
+
wait: () => import("./pushary-wait.js"),
|
|
23
|
+
stats: () => import("./pushary-stats.js"),
|
|
24
|
+
suggestions: () => import("./pushary-suggestions.js"),
|
|
25
|
+
upgrade: () => import("./pushary-upgrade.js"),
|
|
26
|
+
hook: () => import("./pushary-hook.js")
|
|
27
|
+
};
|
|
28
|
+
var argv = process.argv.slice(2);
|
|
29
|
+
var command = argv[0] ?? "";
|
|
30
|
+
var wants = (...names) => argv.some((arg) => names.includes(arg));
|
|
31
|
+
if (!command || wants("--help", "-h") && !isCommandName(command)) {
|
|
32
|
+
process.stdout.write(renderHelp(getPackageVersion()));
|
|
33
|
+
process.exit(EXIT.OK);
|
|
34
|
+
}
|
|
35
|
+
if (wants("--version", "-v") && !isCommandName(command)) {
|
|
36
|
+
process.stdout.write(`${getPackageVersion()}
|
|
37
|
+
`);
|
|
38
|
+
process.exit(EXIT.OK);
|
|
39
|
+
}
|
|
40
|
+
if (!isCommandName(command)) {
|
|
41
|
+
process.stderr.write(`Unknown command: ${command}
|
|
42
|
+
`);
|
|
43
|
+
process.stderr.write(`Run \`pushary --help\`. Available: ${COMMANDS.map((c) => c.name).join(", ")}
|
|
64
44
|
`);
|
|
45
|
+
process.exit(EXIT.USAGE);
|
|
65
46
|
}
|
|
47
|
+
await LOADERS[command]();
|
|
@@ -12,18 +12,34 @@ var readKeyFromConfigFile = () => {
|
|
|
12
12
|
return void 0;
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
|
+
var driftChecked = false;
|
|
16
|
+
var warnOnKeyDrift = (envKey) => {
|
|
17
|
+
if (driftChecked || process.env.PUSHARY_SUPPRESS_KEY_DRIFT_WARNING === "1") return;
|
|
18
|
+
driftChecked = true;
|
|
19
|
+
const fileKey = readKeyFromConfigFile();
|
|
20
|
+
if (!fileKey || fileKey === envKey) return;
|
|
21
|
+
const short = (key) => key.split(".")[0] ?? key;
|
|
22
|
+
process.stderr.write(
|
|
23
|
+
`[pushary] PUSHARY_API_KEY in your environment (${short(envKey)}) differs from ${configFilePath()} (${short(fileKey)}). The environment one is being used. Run 'pushary doctor' for details, or set PUSHARY_SUPPRESS_KEY_DRIFT_WARNING=1 if this is deliberate.
|
|
24
|
+
`
|
|
25
|
+
);
|
|
26
|
+
};
|
|
15
27
|
var getApiKey = () => {
|
|
16
28
|
const envKey = process.env.PUSHARY_API_KEY?.trim();
|
|
17
|
-
if (envKey)
|
|
29
|
+
if (envKey) {
|
|
30
|
+
warnOnKeyDrift(envKey);
|
|
31
|
+
return envKey;
|
|
32
|
+
}
|
|
18
33
|
const fileKey = readKeyFromConfigFile();
|
|
19
34
|
if (fileKey) return fileKey;
|
|
20
35
|
throw new Error(
|
|
21
36
|
"PUSHARY_API_KEY is not set and no key was found in ~/.pushary/config.json. Run `npx @pushary/agent-hooks setup`, or get your API key at https://pushary.com/sign-up?from=ai-coding"
|
|
22
37
|
);
|
|
23
38
|
};
|
|
24
|
-
var getBaseUrl = () => process.env.PUSHARY_BASE_URL
|
|
39
|
+
var getBaseUrl = () => process.env.PUSHARY_BASE_URL?.trim() || process.env.PUSHARY_API_URL?.trim() || "https://pushary.com";
|
|
25
40
|
|
|
26
41
|
export {
|
|
42
|
+
configFilePath,
|
|
27
43
|
getApiKey,
|
|
28
44
|
getBaseUrl
|
|
29
45
|
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// src/pairing.ts
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
var X25519_SPKI_PREFIX = Buffer.from("302a300506032b656e032100", "hex");
|
|
4
|
+
var HKDF_INFO = Buffer.from("pushary-pair-v1");
|
|
5
|
+
var HEADER_BYTES = 32 + 12 + 16;
|
|
6
|
+
var rawToPublicKey = (raw) => crypto.createPublicKey({
|
|
7
|
+
key: Buffer.concat([X25519_SPKI_PREFIX, raw]),
|
|
8
|
+
format: "der",
|
|
9
|
+
type: "spki"
|
|
10
|
+
});
|
|
11
|
+
var publicKeyToRaw = (key) => key.export({ format: "der", type: "spki" }).subarray(-32);
|
|
12
|
+
var generateKeypair = () => {
|
|
13
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync("x25519");
|
|
14
|
+
const publicKeyRaw = publicKeyToRaw(publicKey);
|
|
15
|
+
return { publicKeyB64: publicKeyRaw.toString("base64"), publicKeyRaw, privateKey };
|
|
16
|
+
};
|
|
17
|
+
var publicKeyFingerprint = (b64) => {
|
|
18
|
+
const raw = Buffer.from(b64, "base64");
|
|
19
|
+
const digest = crypto.createHash("sha256").update(raw).digest("hex");
|
|
20
|
+
return (digest.slice(0, 8).match(/.{2}/g) ?? []).join(" ").toUpperCase();
|
|
21
|
+
};
|
|
22
|
+
var openSealedKey = (blobB64, keypair) => {
|
|
23
|
+
const blob = Buffer.from(blobB64, "base64");
|
|
24
|
+
if (blob.length <= HEADER_BYTES) throw new Error("sealed payload too short");
|
|
25
|
+
const ephemeralRaw = blob.subarray(0, 32);
|
|
26
|
+
const nonce = blob.subarray(32, 44);
|
|
27
|
+
const tag = blob.subarray(44, 60);
|
|
28
|
+
const ciphertext = blob.subarray(60);
|
|
29
|
+
const shared = crypto.diffieHellman({
|
|
30
|
+
privateKey: keypair.privateKey,
|
|
31
|
+
publicKey: rawToPublicKey(ephemeralRaw)
|
|
32
|
+
});
|
|
33
|
+
const salt = Buffer.concat([ephemeralRaw, keypair.publicKeyRaw]);
|
|
34
|
+
const key = Buffer.from(crypto.hkdfSync("sha256", shared, salt, HKDF_INFO, 32));
|
|
35
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, nonce);
|
|
36
|
+
decipher.setAuthTag(tag);
|
|
37
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
generateKeypair,
|
|
42
|
+
publicKeyFingerprint,
|
|
43
|
+
openSealedKey
|
|
44
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/cli/errors.ts
|
|
2
|
+
var UsageError = class extends Error {
|
|
3
|
+
constructor(hint) {
|
|
4
|
+
super(hint);
|
|
5
|
+
this.hint = hint;
|
|
6
|
+
this.name = "UsageError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
var NetworkError = class extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "NetworkError";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
UsageError,
|
|
18
|
+
NetworkError
|
|
19
|
+
};
|