agentkey-ai 1.2.0 → 1.2.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/agentkey.js +1 -1
- package/cli.mjs +99 -28
- package/package.json +1 -1
package/agentkey.js
CHANGED
|
@@ -20,7 +20,7 @@ const TIMEOUT_MS = 5000;
|
|
|
20
20
|
|
|
21
21
|
// Production API. Override with baseUrl only if you self-host.
|
|
22
22
|
export const DEFAULT_BASE_URL = "https://agentkey.base44.app";
|
|
23
|
-
export const VERSION = "1.2.
|
|
23
|
+
export const VERSION = "1.2.1";
|
|
24
24
|
const USER_AGENT = `agentkey-sdk-js/${VERSION}`;
|
|
25
25
|
|
|
26
26
|
// Process-wide flag so the first-run info block prints once per process.
|
package/cli.mjs
CHANGED
|
@@ -34,8 +34,67 @@ function step(ok, label, detail, nextStep) {
|
|
|
34
34
|
return ok;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
//
|
|
37
|
+
// A warning is not a pass mark: doctor prints " ! " and exits non-zero when
|
|
38
|
+
// one appears. Used for the edge-firewall block, a broken setup that must
|
|
39
|
+
// not read as healthy.
|
|
40
|
+
function stepWarn(label, detail, nextStep) {
|
|
41
|
+
console.log(` ! ${label}${detail ? ` - ${detail}` : ""}`);
|
|
42
|
+
if (nextStep) console.log(` → ${nextStep}`);
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The backend host is internal plumbing: output shows a friendly alias, and
|
|
47
|
+
// the raw host appears only when AGENTKEY_DEBUG is set.
|
|
48
|
+
const API_ALIAS = "AgentKey API";
|
|
49
|
+
const displayTarget = (baseUrl) => (process.env.AGENTKEY_DEBUG ? baseUrl : API_ALIAS);
|
|
50
|
+
|
|
51
|
+
// An edge firewall (e.g. Cloudflare) can block a network or IP outright,
|
|
52
|
+
// answering 403 with an HTML block page while the real API is fine. Detect
|
|
53
|
+
// the block from the body and headers, never the status code alone: a plain
|
|
54
|
+
// 401/400 from the API still proves it is reachable.
|
|
55
|
+
function looksBlocked(headers, bodyText) {
|
|
56
|
+
const h = (n) => String((headers && headers.get && headers.get(n)) || "");
|
|
57
|
+
const body = String(bodyText || "");
|
|
58
|
+
if (h("cf-mitigated").toLowerCase() === "block") return true;
|
|
59
|
+
if (body.includes("error code: 1010")) return true;
|
|
60
|
+
if (body.includes("Attention Required") && body.includes("Cloudflare")) return true;
|
|
61
|
+
if (body.includes("Access denied") && body.includes("Cloudflare")) return true;
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// The ray ID to hand support when an edge block is reported.
|
|
66
|
+
function rayIdFrom(headers, bodyText) {
|
|
67
|
+
const h = String((headers && headers.get && headers.get("cf-ray")) || "");
|
|
68
|
+
if (h) return h;
|
|
69
|
+
const m = String(bodyText || "").match(/Ray ID:\s*([0-9a-f-]{8,})/i);
|
|
70
|
+
return m ? m[1] : "unknown";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function skippedReason(r) {
|
|
74
|
+
return r.blocked ? "skipped, your network or IP appears blocked" : "skipped, the API is unreachable";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function nextStepFor(r) {
|
|
78
|
+
if (r.blocked) return `try another network, or contact support with ray ID ${r.rayId || "unknown"}`;
|
|
79
|
+
return "check the URL (AGENTKEY_BASE_URL overrides it) and your network";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function authEndpointCheck(baseUrl) {
|
|
83
|
+
const a = await apiReachable(baseUrl, "/api/functions/authorize");
|
|
84
|
+
if (!a.reachable) {
|
|
85
|
+
return step(false, "Authorization endpoint reachable", "unreachable",
|
|
86
|
+
"authorization checks fail closed until this is reachable");
|
|
87
|
+
}
|
|
88
|
+
if (a.blocked) {
|
|
89
|
+
return stepWarn("Authorization endpoint reachable",
|
|
90
|
+
`blocked by the edge firewall (HTTP ${a.status})`, nextStepFor(a));
|
|
91
|
+
}
|
|
92
|
+
return step(true, "Authorization endpoint reachable");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Any plain HTTP response (including 401/400) proves the endpoint is
|
|
96
|
+
// reachable; an edge-firewall block does not (see looksBlocked). Only a
|
|
97
|
+
// network-level failure (DNS, timeout, refused) means unreachable.
|
|
39
98
|
async function apiReachable(baseUrl, path, method = "POST") {
|
|
40
99
|
const controller = new AbortController();
|
|
41
100
|
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
@@ -46,9 +105,13 @@ async function apiReachable(baseUrl, path, method = "POST") {
|
|
|
46
105
|
body: method === "POST" ? "{}" : undefined,
|
|
47
106
|
signal: controller.signal,
|
|
48
107
|
});
|
|
49
|
-
|
|
108
|
+
const text = await res.text().catch(() => "");
|
|
109
|
+
if (looksBlocked(res.headers, text)) {
|
|
110
|
+
return { reachable: true, blocked: true, status: res.status, rayId: rayIdFrom(res.headers, text) };
|
|
111
|
+
}
|
|
112
|
+
return { reachable: true, blocked: false, status: res.status, rayId: "" };
|
|
50
113
|
} catch {
|
|
51
|
-
return
|
|
114
|
+
return { reachable: false, blocked: false, status: null, rayId: "" };
|
|
52
115
|
} finally {
|
|
53
116
|
clearTimeout(timeout);
|
|
54
117
|
}
|
|
@@ -70,8 +133,11 @@ async function validateRaw(baseUrl, key) {
|
|
|
70
133
|
const data = JSON.parse(text);
|
|
71
134
|
if (data && typeof data.valid === "boolean") return data;
|
|
72
135
|
} catch {}
|
|
73
|
-
if (
|
|
74
|
-
|
|
136
|
+
if (looksBlocked(res.headers, text)) {
|
|
137
|
+
const error = text.includes("error code: 1010")
|
|
138
|
+
? "request blocked by the edge WAF (Cloudflare error 1010) - not an authentication failure"
|
|
139
|
+
: "request blocked by the edge firewall - not an authentication failure";
|
|
140
|
+
return { valid: false, error, blocked: true, rayId: rayIdFrom(res.headers, text) };
|
|
75
141
|
}
|
|
76
142
|
return { valid: false, error: `API returned HTTP ${res.status}` };
|
|
77
143
|
} catch (e) {
|
|
@@ -186,6 +252,8 @@ async function cmdInit() {
|
|
|
186
252
|
console.log("The key is saved at the config file above. The AGENTKEY_API_KEY");
|
|
187
253
|
console.log("environment variable overrides it; unset yours to use the saved");
|
|
188
254
|
console.log("key. Remove it any time: agentkey logout");
|
|
255
|
+
} else {
|
|
256
|
+
console.log("To remove a key saved by init later: agentkey logout");
|
|
189
257
|
}
|
|
190
258
|
console.log("Next:");
|
|
191
259
|
console.log(" 1. agentkey doctor");
|
|
@@ -195,7 +263,11 @@ async function cmdInit() {
|
|
|
195
263
|
const detail = String(v.error || v.message || v.reason || "rejected by the API");
|
|
196
264
|
step(false, "API key validated", detail);
|
|
197
265
|
console.log();
|
|
198
|
-
if (
|
|
266
|
+
if (v.blocked && !detail.includes("1010")) {
|
|
267
|
+
console.log("FAILED: the request was blocked by the edge firewall. This is not an");
|
|
268
|
+
console.log("authentication failure. Your network or IP may be blocked; try");
|
|
269
|
+
console.log(`another network, or contact support with this ray ID: ${v.rayId || "unknown"}`);
|
|
270
|
+
} else if (detail.includes("WAF") || detail.includes("1010")) {
|
|
199
271
|
console.log("FAILED: the request was blocked by the edge WAF. This is not an");
|
|
200
272
|
console.log("authentication failure; a proxy or modified client is likely");
|
|
201
273
|
console.log("stripping the SDK's User-Agent header. Restore it, then run:");
|
|
@@ -214,48 +286,48 @@ async function cmdInit() {
|
|
|
214
286
|
|
|
215
287
|
async function cmdDoctor() {
|
|
216
288
|
const baseUrl = (process.env.AGENTKEY_BASE_URL || "").trim() || DEFAULT_BASE_URL;
|
|
289
|
+
const target = displayTarget(baseUrl);
|
|
217
290
|
console.log("AgentKey doctor");
|
|
218
291
|
console.log(RULE);
|
|
219
292
|
console.log();
|
|
220
293
|
let ok = true;
|
|
221
294
|
ok = step(true, "SDK installed", `agentkey-ai ${VERSION}`) && ok;
|
|
222
295
|
const { key, source } = await resolveKey();
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
296
|
+
const reach = await apiReachable(baseUrl, "/api/functions/validate_api_key", "GET");
|
|
297
|
+
if (!reach.reachable) {
|
|
298
|
+
ok = step(false, "API reachable", `${target} - unreachable`, nextStepFor(reach)) && ok;
|
|
299
|
+
} else if (reach.blocked) {
|
|
300
|
+
ok = stepWarn("API reachable", `${target} - blocked by the edge firewall (HTTP ${reach.status})`, nextStepFor(reach)) && ok;
|
|
301
|
+
} else {
|
|
302
|
+
ok = step(true, "API reachable", target) && ok;
|
|
303
|
+
}
|
|
304
|
+
const usable = reach.reachable && !reach.blocked;
|
|
226
305
|
if (!key) {
|
|
227
306
|
ok = step(false, "Credentials configured", "no API key configured", "run: agentkey init") && ok;
|
|
228
|
-
if (
|
|
229
|
-
ok = step(false, "Credentials valid",
|
|
230
|
-
const [areach, astatus] = await apiReachable(baseUrl, "/api/functions/authorize");
|
|
231
|
-
ok = step(areach, "Authorization endpoint reachable", areach ? `HTTP ${astatus}` : "unreachable",
|
|
232
|
-
"authorization checks fail closed until this is reachable") && ok;
|
|
233
|
-
} else {
|
|
234
|
-
// Same host, same fate: no point making more doomed requests.
|
|
235
|
-
ok = step(false, "Credentials valid", "skipped, the API is unreachable",
|
|
236
|
-
"check the URL (AGENTKEY_BASE_URL overrides it) and your network") && ok;
|
|
307
|
+
if (!usable) {
|
|
308
|
+
ok = step(false, "Credentials valid", skippedReason(reach), nextStepFor(reach)) && ok;
|
|
237
309
|
ok = step(false, "Authorization endpoint reachable", "skipped, same host as the API",
|
|
238
|
-
"authorization checks fail closed until the API is
|
|
310
|
+
"authorization checks fail closed until the API is usable") && ok;
|
|
311
|
+
} else {
|
|
312
|
+
ok = step(false, "Credentials valid", "skipped, no key", "run: agentkey init") && ok;
|
|
313
|
+
ok = (await authEndpointCheck(baseUrl)) && ok;
|
|
239
314
|
}
|
|
240
315
|
} else {
|
|
241
316
|
const where = source === "environment"
|
|
242
317
|
? "environment: AGENTKEY_API_KEY"
|
|
243
318
|
: `config file: ${configFilePath()}`;
|
|
244
319
|
ok = step(true, "Credentials configured", where) && ok;
|
|
245
|
-
if (!
|
|
246
|
-
ok = step(false, "Credentials valid",
|
|
247
|
-
"check the URL (AGENTKEY_BASE_URL overrides it) and your network") && ok;
|
|
320
|
+
if (!usable) {
|
|
321
|
+
ok = step(false, "Credentials valid", skippedReason(reach), nextStepFor(reach)) && ok;
|
|
248
322
|
ok = step(false, "Authorization endpoint reachable", "skipped, same host as the API",
|
|
249
|
-
"authorization checks fail closed until the API is
|
|
323
|
+
"authorization checks fail closed until the API is usable") && ok;
|
|
250
324
|
} else {
|
|
251
325
|
const v = await validateRaw(baseUrl, key);
|
|
252
326
|
const valid = v.valid === true;
|
|
253
327
|
const detail = valid ? "" : String(v.error || v.message || v.reason || "rejected by the API");
|
|
254
328
|
ok = step(valid, "Credentials valid", detail,
|
|
255
329
|
`run: agentkey init, or create a fresh key at ${DASHBOARD_URL}/connect`) && ok;
|
|
256
|
-
|
|
257
|
-
ok = step(areach, "Authorization endpoint reachable", areach ? `HTTP ${astatus}` : "unreachable",
|
|
258
|
-
"authorization checks fail closed until this is reachable") && ok;
|
|
330
|
+
ok = (await authEndpointCheck(baseUrl)) && ok;
|
|
259
331
|
}
|
|
260
332
|
}
|
|
261
333
|
console.log();
|
|
@@ -305,7 +377,6 @@ function onboarding() {
|
|
|
305
377
|
console.log(` ${QUICKSTART_URL}`);
|
|
306
378
|
console.log();
|
|
307
379
|
console.log(`Get an API key at ${DASHBOARD_URL} (Connect wizard).`);
|
|
308
|
-
console.log("agentkey logout deletes the key saved by init.");
|
|
309
380
|
return 0;
|
|
310
381
|
}
|
|
311
382
|
|
package/package.json
CHANGED