@pushary/agent-hooks 0.59.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-BVAWO6RE.js → chunk-S4TFBJ5O.js} +8 -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
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NetworkError
|
|
3
|
+
} from "./chunk-7QLSKOSU.js";
|
|
4
|
+
import {
|
|
5
|
+
EXIT
|
|
6
|
+
} from "./chunk-SML23YOT.js";
|
|
7
|
+
import {
|
|
8
|
+
isValidApiKey
|
|
9
|
+
} from "./chunk-DQAN3JQP.js";
|
|
10
|
+
import {
|
|
11
|
+
getBaseUrl
|
|
12
|
+
} from "./chunk-2UMNXADU.js";
|
|
13
|
+
|
|
14
|
+
// src/cli/net.ts
|
|
15
|
+
import { createRequire } from "module";
|
|
16
|
+
var requireCjs = createRequire(import.meta.url);
|
|
17
|
+
var PROXY_VARS = ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"];
|
|
18
|
+
var proxyFromEnv = () => {
|
|
19
|
+
for (const name of PROXY_VARS) {
|
|
20
|
+
const value = process.env[name]?.trim();
|
|
21
|
+
if (value) return value;
|
|
22
|
+
}
|
|
23
|
+
return void 0;
|
|
24
|
+
};
|
|
25
|
+
var installed = false;
|
|
26
|
+
var installProxyDispatcher = () => {
|
|
27
|
+
if (installed) return;
|
|
28
|
+
installed = true;
|
|
29
|
+
if (!proxyFromEnv()) return;
|
|
30
|
+
try {
|
|
31
|
+
const { EnvHttpProxyAgent, setGlobalDispatcher } = requireCjs("undici");
|
|
32
|
+
setGlobalDispatcher(new EnvHttpProxyAgent());
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var isNetworkFailure = (err) => {
|
|
37
|
+
if (!(err instanceof Error)) return false;
|
|
38
|
+
const code = err.code;
|
|
39
|
+
if (code && ["ENOTFOUND", "ECONNREFUSED", "ECONNRESET", "ETIMEDOUT", "EAI_AGAIN", "EPROTO", "CERT_HAS_EXPIRED", "UNABLE_TO_VERIFY_LEAF_SIGNATURE"].includes(code)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return err.name === "AbortError" || err.name === "TimeoutError" || /fetch failed/i.test(err.message);
|
|
43
|
+
};
|
|
44
|
+
var DEFAULT_TIMEOUT_MS = 15e3;
|
|
45
|
+
var pusharyFetch = async (url, init = {}) => {
|
|
46
|
+
installProxyDispatcher();
|
|
47
|
+
const { timeoutMs = DEFAULT_TIMEOUT_MS, signal, ...rest } = init;
|
|
48
|
+
try {
|
|
49
|
+
return await fetch(url, {
|
|
50
|
+
...rest,
|
|
51
|
+
signal: signal ?? AbortSignal.timeout(timeoutMs)
|
|
52
|
+
});
|
|
53
|
+
} catch (err) {
|
|
54
|
+
if (isNetworkFailure(err)) {
|
|
55
|
+
const proxy = proxyFromEnv();
|
|
56
|
+
const via = proxy ? ` (via proxy ${proxy})` : "";
|
|
57
|
+
throw new NetworkError(`Could not reach ${new URL(url).host}${via}: ${err instanceof Error ? err.message : String(err)}`);
|
|
58
|
+
}
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/session.ts
|
|
64
|
+
var IDENTITY_PATH = "/api/v1/server/identity";
|
|
65
|
+
var DEFAULT_TIMEOUT_MS2 = 1e4;
|
|
66
|
+
var SUBSCRIPTION_REQUIRED_CODE = "subscription_required";
|
|
67
|
+
var DEFAULT_UPGRADE_PATH = "/dashboard/settings?tab=billing";
|
|
68
|
+
var KEY_SCOPES = ["full_access", "connector", "bound"];
|
|
69
|
+
var SUBSCRIPTION_STATUSES = [
|
|
70
|
+
"active",
|
|
71
|
+
"past_due",
|
|
72
|
+
"canceled",
|
|
73
|
+
"incomplete",
|
|
74
|
+
"trialing",
|
|
75
|
+
"expired",
|
|
76
|
+
"none"
|
|
77
|
+
];
|
|
78
|
+
var asString = (value) => typeof value === "string" && value ? value : null;
|
|
79
|
+
var asScope = (value) => KEY_SCOPES.includes(value) ? value : "full_access";
|
|
80
|
+
var asStatus = (value) => SUBSCRIPTION_STATUSES.includes(value) ? value : "none";
|
|
81
|
+
var asCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
|
|
82
|
+
var absoluteUrl = (baseUrl, pathOrUrl) => {
|
|
83
|
+
if (/^https?:\/\//i.test(pathOrUrl)) return pathOrUrl;
|
|
84
|
+
return `${baseUrl.replace(/\/+$/, "")}${pathOrUrl.startsWith("/") ? "" : "/"}${pathOrUrl}`;
|
|
85
|
+
};
|
|
86
|
+
var toIdentity = (body) => {
|
|
87
|
+
if (typeof body !== "object" || body === null) return null;
|
|
88
|
+
const raw = body;
|
|
89
|
+
const keyId = asString(raw.keyId);
|
|
90
|
+
const siteId = asString(raw.siteId);
|
|
91
|
+
if (!keyId || !siteId) return null;
|
|
92
|
+
return {
|
|
93
|
+
keyId,
|
|
94
|
+
keyPrefix: asString(raw.keyPrefix) ?? "",
|
|
95
|
+
keyName: asString(raw.keyName) ?? "",
|
|
96
|
+
keyScope: asScope(raw.keyScope),
|
|
97
|
+
siteId,
|
|
98
|
+
siteSlug: asString(raw.siteSlug) ?? "",
|
|
99
|
+
siteName: asString(raw.siteName) ?? "",
|
|
100
|
+
workspaceId: asString(raw.workspaceId) ?? "",
|
|
101
|
+
plan: asString(raw.plan) ?? "unknown",
|
|
102
|
+
surfacePlan: asString(raw.surfacePlan) ?? "unknown",
|
|
103
|
+
subscriptionStatus: asStatus(raw.subscriptionStatus),
|
|
104
|
+
locked: raw.locked === true,
|
|
105
|
+
machineId: asString(raw.machineId),
|
|
106
|
+
authorizedUserId: asString(raw.authorizedUserId),
|
|
107
|
+
workspaceMembers: asCount(raw.workspaceMembers),
|
|
108
|
+
createdAt: asString(raw.createdAt),
|
|
109
|
+
lastUsedAt: asString(raw.lastUsedAt)
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
var readJsonBody = async (response) => {
|
|
113
|
+
try {
|
|
114
|
+
return await response.json();
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
var checkApiKey = async (apiKey, options = {}) => {
|
|
120
|
+
if (!isValidApiKey(apiKey)) return { kind: "malformed" };
|
|
121
|
+
const baseUrl = options.baseUrl ?? getBaseUrl();
|
|
122
|
+
const fetchImpl = options.fetchImpl ?? pusharyFetch;
|
|
123
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
124
|
+
let response;
|
|
125
|
+
try {
|
|
126
|
+
response = await fetchImpl(`${baseUrl.replace(/\/+$/, "")}${IDENTITY_PATH}`, {
|
|
127
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
128
|
+
timeoutMs
|
|
129
|
+
});
|
|
130
|
+
} catch (err) {
|
|
131
|
+
const detail = err instanceof NetworkError || err instanceof Error ? err.message : String(err);
|
|
132
|
+
return { kind: "unreachable", detail };
|
|
133
|
+
}
|
|
134
|
+
if (response.status === 401) return { kind: "rejected" };
|
|
135
|
+
if (response.status === 403) {
|
|
136
|
+
const body = await readJsonBody(response);
|
|
137
|
+
const raw = typeof body === "object" && body !== null ? body : {};
|
|
138
|
+
if (raw.code !== SUBSCRIPTION_REQUIRED_CODE) {
|
|
139
|
+
return { kind: "unreachable", detail: asString(raw.message) ?? `HTTP ${response.status}` };
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
kind: "locked",
|
|
143
|
+
plan: asString(raw.plan),
|
|
144
|
+
upgradeUrl: absoluteUrl(baseUrl, asString(raw.upgradeUrl) ?? DEFAULT_UPGRADE_PATH)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (!response.ok) {
|
|
148
|
+
return { kind: "unreachable", detail: `HTTP ${response.status}` };
|
|
149
|
+
}
|
|
150
|
+
const identity = toIdentity(await readJsonBody(response));
|
|
151
|
+
if (!identity) {
|
|
152
|
+
return { kind: "unreachable", detail: "the server answered with an unexpected body" };
|
|
153
|
+
}
|
|
154
|
+
return { kind: "ok", identity };
|
|
155
|
+
};
|
|
156
|
+
var keyCheckFromResponse = async (response, baseUrl = getBaseUrl()) => {
|
|
157
|
+
if (response.status === 401) return { kind: "rejected" };
|
|
158
|
+
if (response.status !== 403) return null;
|
|
159
|
+
const body = await readJsonBody(response.clone());
|
|
160
|
+
const raw = typeof body === "object" && body !== null ? body : {};
|
|
161
|
+
if (raw.code !== SUBSCRIPTION_REQUIRED_CODE) return null;
|
|
162
|
+
return {
|
|
163
|
+
kind: "locked",
|
|
164
|
+
plan: asString(raw.plan),
|
|
165
|
+
upgradeUrl: absoluteUrl(baseUrl, asString(raw.upgradeUrl) ?? DEFAULT_UPGRADE_PATH)
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
var describeKeyCheck = (check, baseUrl = getBaseUrl()) => {
|
|
169
|
+
const settingsUrl = absoluteUrl(baseUrl, "/dashboard/agent/settings");
|
|
170
|
+
switch (check.kind) {
|
|
171
|
+
case "ok":
|
|
172
|
+
return {
|
|
173
|
+
message: `Key verified for ${check.identity.siteSlug || check.identity.siteName || "your site"}.`,
|
|
174
|
+
next: [],
|
|
175
|
+
exitCode: EXIT.OK,
|
|
176
|
+
fatal: false
|
|
177
|
+
};
|
|
178
|
+
case "malformed":
|
|
179
|
+
return {
|
|
180
|
+
message: "That does not look like a Pushary API key.",
|
|
181
|
+
next: [`Expected the pk_xxx.xxx form. Copy yours from ${settingsUrl}`],
|
|
182
|
+
exitCode: EXIT.USAGE,
|
|
183
|
+
fatal: true
|
|
184
|
+
};
|
|
185
|
+
case "rejected":
|
|
186
|
+
return {
|
|
187
|
+
message: "This key was rejected by pushary.com.",
|
|
188
|
+
next: [`Get a working key: ${settingsUrl}`, "Then re-run: pushary setup"],
|
|
189
|
+
exitCode: EXIT.UNAUTHENTICATED,
|
|
190
|
+
fatal: true
|
|
191
|
+
};
|
|
192
|
+
case "locked":
|
|
193
|
+
return {
|
|
194
|
+
message: "Your Pushary plan is not active, so approvals will not deliver yet.",
|
|
195
|
+
next: [
|
|
196
|
+
"Your agents are configured and your key itself is fine.",
|
|
197
|
+
`Activate the plan: ${check.upgradeUrl}`,
|
|
198
|
+
"Then run: pushary doctor"
|
|
199
|
+
],
|
|
200
|
+
exitCode: EXIT.PROBLEMS_FOUND,
|
|
201
|
+
fatal: false
|
|
202
|
+
};
|
|
203
|
+
case "unreachable":
|
|
204
|
+
return {
|
|
205
|
+
message: `Could not reach pushary.com to verify the key (${check.detail}).`,
|
|
206
|
+
next: ["Continuing anyway. Run `pushary doctor` once you are online."],
|
|
207
|
+
exitCode: EXIT.UNREACHABLE,
|
|
208
|
+
fatal: false
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export {
|
|
214
|
+
checkApiKey,
|
|
215
|
+
keyCheckFromResponse,
|
|
216
|
+
describeKeyCheck
|
|
217
|
+
};
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
callMcpTool,
|
|
6
6
|
withRetry
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HNTKTK5B.js";
|
|
8
8
|
import {
|
|
9
9
|
ACTION_BODY_MAX,
|
|
10
10
|
DECISION_LINE_MAX,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
import {
|
|
25
25
|
getApiKey,
|
|
26
26
|
getBaseUrl
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-2UMNXADU.js";
|
|
28
28
|
|
|
29
29
|
// src/policy.ts
|
|
30
30
|
import { createHash } from "crypto";
|
package/dist/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handlePreToolUse
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-HI4AGGE6.js";
|
|
4
4
|
import "../chunk-CAJZAFVS.js";
|
|
5
5
|
import "../chunk-YHG74UFF.js";
|
|
6
6
|
import {
|
|
@@ -15,15 +15,15 @@ import {
|
|
|
15
15
|
reportEvent,
|
|
16
16
|
resolvePolicy,
|
|
17
17
|
waitForAnswer
|
|
18
|
-
} from "../chunk-
|
|
18
|
+
} from "../chunk-VFBYRR2N.js";
|
|
19
19
|
import "../chunk-RN3NOEJF.js";
|
|
20
20
|
import "../chunk-WLGEY4UX.js";
|
|
21
|
-
import "../chunk-
|
|
21
|
+
import "../chunk-HNTKTK5B.js";
|
|
22
22
|
import "../chunk-DQAN3JQP.js";
|
|
23
23
|
import {
|
|
24
24
|
getApiKey,
|
|
25
25
|
getBaseUrl
|
|
26
|
-
} from "../chunk-
|
|
26
|
+
} from "../chunk-2UMNXADU.js";
|
|
27
27
|
export {
|
|
28
28
|
askUser,
|
|
29
29
|
cancelQuestion,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pushary/agent-hooks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.61.0",
|
|
4
4
|
"description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pushary",
|
|
@@ -60,6 +60,10 @@
|
|
|
60
60
|
"pushary-gemini-hook": "./dist/bin/pushary-gemini-hook.js",
|
|
61
61
|
"pushary-setup": "./dist/bin/pushary-setup.js",
|
|
62
62
|
"pushary-clean": "./dist/bin/pushary-clean.js",
|
|
63
|
+
"pushary-status": "./dist/bin/pushary-status.js",
|
|
64
|
+
"pushary-login": "./dist/bin/pushary-login.js",
|
|
65
|
+
"pushary-logout": "./dist/bin/pushary-logout.js",
|
|
66
|
+
"pushary-connect": "./dist/bin/pushary-connect.js",
|
|
63
67
|
"pushary-doctor": "./dist/bin/pushary-doctor.js",
|
|
64
68
|
"pushary-mode": "./dist/bin/pushary-mode.js",
|
|
65
69
|
"pushary-wait": "./dist/bin/pushary-wait.js",
|
|
@@ -69,18 +73,21 @@
|
|
|
69
73
|
},
|
|
70
74
|
"files": [
|
|
71
75
|
"dist",
|
|
72
|
-
"data"
|
|
76
|
+
"data",
|
|
77
|
+
"CHANGELOG.md"
|
|
73
78
|
],
|
|
74
79
|
"scripts": {
|
|
75
80
|
"build": "node scripts/bundle-plugin.mjs && tsup",
|
|
76
81
|
"dev": "tsup --watch",
|
|
77
|
-
"test": "
|
|
82
|
+
"test": "node ../../scripts/ci/run-tests.mjs src bin",
|
|
83
|
+
"typecheck": "tsc --noEmit"
|
|
78
84
|
},
|
|
79
85
|
"dependencies": {
|
|
80
86
|
"@inquirer/prompts": "^8.4.2",
|
|
81
87
|
"qrcode-terminal": "^0.12.0",
|
|
82
88
|
"smol-toml": "^1.6.1",
|
|
83
|
-
"tweetnacl": "^1.0.3"
|
|
89
|
+
"tweetnacl": "^1.0.3",
|
|
90
|
+
"undici": "^6.21.0"
|
|
84
91
|
},
|
|
85
92
|
"devDependencies": {
|
|
86
93
|
"@pushary/contracts": "workspace:*",
|