apiblaze 0.4.2 → 0.4.7
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/dist/index.js +125 -86
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25,10 +25,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// src/index.ts
|
|
27
27
|
var import_commander = require("commander");
|
|
28
|
-
var
|
|
28
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
29
29
|
|
|
30
30
|
// package.json
|
|
31
|
-
var version = "0.4.
|
|
31
|
+
var version = "0.4.7";
|
|
32
32
|
|
|
33
33
|
// src/types.ts
|
|
34
34
|
var ApiError = class extends Error {
|
|
@@ -1104,14 +1104,23 @@ function fail(message) {
|
|
|
1104
1104
|
console.error(import_chalk6.default.red(`Error: ${message}`));
|
|
1105
1105
|
process.exit(1);
|
|
1106
1106
|
}
|
|
1107
|
-
function
|
|
1107
|
+
function buildTryItCurl(url, authType, apiKey) {
|
|
1108
|
+
if (authType === "api_key") {
|
|
1109
|
+
if (!apiKey) return null;
|
|
1110
|
+
return `curl ${url} -H "X-API-Key: ${apiKey}"`;
|
|
1111
|
+
}
|
|
1112
|
+
if (authType === "none") return `curl ${url}`;
|
|
1113
|
+
return null;
|
|
1114
|
+
}
|
|
1115
|
+
function printCurlExample(url, authType, apiKey, devPortal) {
|
|
1116
|
+
const curl = buildTryItCurl(url, authType, apiKey);
|
|
1108
1117
|
console.log();
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
console.log(` ${import_chalk6.default.cyan(
|
|
1112
|
-
|
|
1113
|
-
}
|
|
1114
|
-
console.log(`
|
|
1118
|
+
if (curl) {
|
|
1119
|
+
console.log(` ${import_chalk6.default.dim("Try it \u2014 copy/paste:")}`);
|
|
1120
|
+
console.log(` ${import_chalk6.default.cyan(curl)}`);
|
|
1121
|
+
} else if (authType === "oauth") {
|
|
1122
|
+
console.log(` ${import_chalk6.default.dim("Try it:")} this proxy uses OAuth \u2014 sign in at ${import_chalk6.default.bold(devPortal ?? "the dev portal")} to get a token,`);
|
|
1123
|
+
console.log(` ${import_chalk6.default.dim(`then call ${url} with`)} ${import_chalk6.default.cyan('-H "Authorization: Bearer <token>"')}`);
|
|
1115
1124
|
}
|
|
1116
1125
|
}
|
|
1117
1126
|
var VALID_AUTH = ["api_key", "none", "oauth"];
|
|
@@ -1259,7 +1268,7 @@ async function runCreate(opts = {}) {
|
|
|
1259
1268
|
console.log(import_chalk6.default.dim(` (Separate keys were also created for: ${otherEnvs.join(", ")}.)`));
|
|
1260
1269
|
}
|
|
1261
1270
|
}
|
|
1262
|
-
printCurlExample(proxyUrl, auth
|
|
1271
|
+
printCurlExample(proxyUrl, auth, adminKey, devPortal);
|
|
1263
1272
|
console.log();
|
|
1264
1273
|
}
|
|
1265
1274
|
async function runAnonymousCreate(opts) {
|
|
@@ -1370,7 +1379,7 @@ async function runAnonymousCreate(opts) {
|
|
|
1370
1379
|
console.log(` ${import_chalk6.default.bold.green(apiKey)}`);
|
|
1371
1380
|
console.log(import_chalk6.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
|
|
1372
1381
|
}
|
|
1373
|
-
if (prodEndpoint) printCurlExample(prodEndpoint, apiKey);
|
|
1382
|
+
if (prodEndpoint) printCurlExample(prodEndpoint, opts.auth || "api_key", apiKey, result.portal);
|
|
1374
1383
|
if (result.claim_url) {
|
|
1375
1384
|
console.log();
|
|
1376
1385
|
console.log(` ${import_chalk6.default.yellow("\u26A0 Anonymous proxy \u2014 claim it to your account within 30 days or it expires:")}`);
|
|
@@ -1507,12 +1516,12 @@ function openBrowser2(url) {
|
|
|
1507
1516
|
} catch {
|
|
1508
1517
|
}
|
|
1509
1518
|
}
|
|
1510
|
-
async function deviceLogin(clientId, scope, onPrompt) {
|
|
1519
|
+
async function deviceLogin(clientId, scope, onPrompt, resource) {
|
|
1511
1520
|
const { verifier, challenge } = pkce();
|
|
1512
1521
|
const startRes = await fetch(`${AUTH_BASE}/device_authorization`, {
|
|
1513
1522
|
method: "POST",
|
|
1514
1523
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1515
|
-
body: new URLSearchParams({ client_id: clientId, scope, code_challenge: challenge, code_challenge_method: "S256" })
|
|
1524
|
+
body: new URLSearchParams({ client_id: clientId, scope, code_challenge: challenge, code_challenge_method: "S256", ...resource ? { resource } : {} })
|
|
1516
1525
|
});
|
|
1517
1526
|
const start = await startRes.json().catch(() => ({}));
|
|
1518
1527
|
if (!startRes.ok) {
|
|
@@ -1532,7 +1541,11 @@ async function deviceLogin(clientId, scope, onPrompt) {
|
|
|
1532
1541
|
const tokRes = await fetch(`${AUTH_BASE}/token`, {
|
|
1533
1542
|
method: "POST",
|
|
1534
1543
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1535
|
-
|
|
1544
|
+
// RFC 8707 resource indicator: sets the access token's `aud` to the portal
|
|
1545
|
+
// resource so the apikeys plane (which derives accepted auds as
|
|
1546
|
+
// https://{tenant}.portal.apiblaze.com/{ver}) accepts this token. Without it the
|
|
1547
|
+
// device grant falls back to aud=https://auth.apiblaze.com/device/callback → 401.
|
|
1548
|
+
body: new URLSearchParams({ grant_type: DEVICE_GRANT, device_code: deviceCode, code_verifier: verifier, ...resource ? { resource } : {} })
|
|
1536
1549
|
});
|
|
1537
1550
|
const tok = await tokRes.json().catch(() => ({}));
|
|
1538
1551
|
if (tokRes.ok && tok.access_token) {
|
|
@@ -1984,6 +1997,7 @@ var import_chalk16 = __toESM(require("chalk"));
|
|
|
1984
1997
|
|
|
1985
1998
|
// src/lib/trace.ts
|
|
1986
1999
|
var import_chalk15 = __toESM(require("chalk"));
|
|
2000
|
+
var CONTROL_API_VERSION = "1.0.0";
|
|
1987
2001
|
var verbose = false;
|
|
1988
2002
|
var entries = [];
|
|
1989
2003
|
function setVerbose(v) {
|
|
@@ -2002,24 +2016,23 @@ function renderTrace() {
|
|
|
2002
2016
|
console.log(import_chalk15.default.dim("\n" + "\u2500".repeat(64)));
|
|
2003
2017
|
console.log(import_chalk15.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
|
|
2004
2018
|
console.log(
|
|
2005
|
-
import_chalk15.default.dim("
|
|
2019
|
+
import_chalk15.default.dim("The same thing on the official API \u2014 copy/paste with your control-plane key\n(get one from the Developers section of dashboard.apiblaze.com, then\n`export APIBLAZE_CONTROLPLANE_APIKEY=sk_...`):\n")
|
|
2006
2020
|
);
|
|
2007
2021
|
entries.forEach((e, i) => {
|
|
2008
2022
|
const n = entries.length > 1 ? import_chalk15.default.bold(`${i + 1}. `) : "";
|
|
2009
2023
|
if (e.summary) console.log(`${n}${import_chalk15.default.cyan(e.summary)}${e.status ? import_chalk15.default.dim(` (HTTP ${e.status})`) : ""}`);
|
|
2024
|
+
const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
|
|
2010
2025
|
const masked = maskBody(e.body);
|
|
2011
|
-
const
|
|
2012
|
-
console.log(import_chalk15.default.green(
|
|
2013
|
-
console.log(import_chalk15.default.green(' -H "
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
` # \u2192 admin-api leaf (Lane 3, X-User-Assertion minted server-side): ${e.method} ${e.path}`
|
|
2019
|
-
)
|
|
2020
|
-
);
|
|
2026
|
+
const hasBody = e.method !== "GET" && masked !== void 0;
|
|
2027
|
+
console.log(import_chalk15.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
|
|
2028
|
+
console.log(import_chalk15.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
|
|
2029
|
+
if (hasBody) {
|
|
2030
|
+
console.log(import_chalk15.default.green(" -H 'Content-Type: application/json' \\"));
|
|
2031
|
+
console.log(import_chalk15.default.green(` -d '${masked}'`));
|
|
2032
|
+
}
|
|
2021
2033
|
if (i < entries.length - 1) console.log();
|
|
2022
2034
|
});
|
|
2035
|
+
entries.length = 0;
|
|
2023
2036
|
}
|
|
2024
2037
|
|
|
2025
2038
|
// src/lib/admin.ts
|
|
@@ -2148,7 +2161,7 @@ async function runDelete(project, version2, opts) {
|
|
|
2148
2161
|
try {
|
|
2149
2162
|
await admin({
|
|
2150
2163
|
method: "DELETE",
|
|
2151
|
-
path:
|
|
2164
|
+
path: `/projects/${proj2.projectId}/${proj2.apiVersion}`,
|
|
2152
2165
|
summary: `Delete proxy ${proj2.projectName} v${proj2.apiVersion} (full cascade)`
|
|
2153
2166
|
});
|
|
2154
2167
|
s2.succeed(`Deleted ${proj2.projectName} v${proj2.apiVersion}.`);
|
|
@@ -2169,7 +2182,7 @@ async function patchConfig(project, opts, body, summary) {
|
|
|
2169
2182
|
try {
|
|
2170
2183
|
const out = await admin({
|
|
2171
2184
|
method: "PATCH",
|
|
2172
|
-
path:
|
|
2185
|
+
path: `/projects/${proj2.projectId}/${proj2.apiVersion}`,
|
|
2173
2186
|
body,
|
|
2174
2187
|
summary
|
|
2175
2188
|
});
|
|
@@ -2228,9 +2241,10 @@ async function runDomainAdd(project, opts) {
|
|
|
2228
2241
|
try {
|
|
2229
2242
|
const out = await admin({
|
|
2230
2243
|
method: "POST",
|
|
2231
|
-
path: `/projects/${proj2.projectId}/
|
|
2244
|
+
path: `/projects/${proj2.projectId}/domains`,
|
|
2245
|
+
// --tenant binds the domain to one of the project's tenants (per-tenant scope).
|
|
2232
2246
|
body: { hostname: opts.domain, api_version: proj2.apiVersion, ...opts.tenant ? { tenant_name: opts.tenant } : {} },
|
|
2233
|
-
summary: `Add custom domain ${opts.domain} to ${proj2.projectName} v${proj2.apiVersion}`
|
|
2247
|
+
summary: `Add custom domain ${opts.domain} to ${proj2.projectName} v${proj2.apiVersion}${opts.tenant ? ` (tenant ${opts.tenant})` : ""}`
|
|
2234
2248
|
});
|
|
2235
2249
|
spinner.succeed(`Registered ${opts.domain}. Add these DNS records, then run \`apiblaze domain status\`:`);
|
|
2236
2250
|
if (opts.json) {
|
|
@@ -2250,7 +2264,7 @@ async function runDomainList(project, opts) {
|
|
|
2250
2264
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2251
2265
|
const out = await admin({
|
|
2252
2266
|
method: "GET",
|
|
2253
|
-
path: `/projects/${proj2.projectId}/
|
|
2267
|
+
path: `/projects/${proj2.projectId}/domains?api_version=${encodeURIComponent(proj2.apiVersion)}`,
|
|
2254
2268
|
summary: `List custom domains for ${proj2.projectName} v${proj2.apiVersion}`
|
|
2255
2269
|
});
|
|
2256
2270
|
const domains = out?.domains ?? [];
|
|
@@ -2273,7 +2287,7 @@ async function runDomainStatus(project, opts) {
|
|
|
2273
2287
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2274
2288
|
const out = await admin({
|
|
2275
2289
|
method: "GET",
|
|
2276
|
-
path: `/projects/${proj2.projectId}/
|
|
2290
|
+
path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}/status`,
|
|
2277
2291
|
summary: `Check custom-domain status`
|
|
2278
2292
|
});
|
|
2279
2293
|
console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk20.default.bold(out?.hostname ?? opts.id)}: ${import_chalk20.default.cyan(out?.status ?? "unknown")}`);
|
|
@@ -2289,7 +2303,7 @@ async function runDomainRemove(project, opts) {
|
|
|
2289
2303
|
try {
|
|
2290
2304
|
await admin({
|
|
2291
2305
|
method: "DELETE",
|
|
2292
|
-
path: `/projects/${proj2.projectId}/
|
|
2306
|
+
path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}`,
|
|
2293
2307
|
summary: `Remove custom domain ${opts.id}`
|
|
2294
2308
|
});
|
|
2295
2309
|
spinner.succeed("Removed.");
|
|
@@ -2556,10 +2570,11 @@ async function runSpecSet(project, opts) {
|
|
|
2556
2570
|
}
|
|
2557
2571
|
|
|
2558
2572
|
// src/commands/agent.ts
|
|
2559
|
-
var
|
|
2573
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
2560
2574
|
var import_ora11 = __toESM(require("ora"));
|
|
2561
2575
|
|
|
2562
2576
|
// src/lib/tools.ts
|
|
2577
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
2563
2578
|
async function proj(teamId, name, version2) {
|
|
2564
2579
|
return resolveProject(teamId, name, version2);
|
|
2565
2580
|
}
|
|
@@ -2568,7 +2583,25 @@ var TOOLS = [
|
|
|
2568
2583
|
name: "create_proxy",
|
|
2569
2584
|
description: "Create a new API proxy (project) from a backend URL. auth is one of: api_key, none, oauth.",
|
|
2570
2585
|
params: { name: "proxy name (becomes its subdomain)", target: "backend URL to forward to", auth: "api_key | none | oauth (default api_key)" },
|
|
2571
|
-
run: async (a, { teamId }) =>
|
|
2586
|
+
run: async (a, { teamId }) => {
|
|
2587
|
+
const auth = a.auth ?? a.auth_type ?? "api_key";
|
|
2588
|
+
const res = await createProxy({ name: a.name, target_url: a.target ?? a.target_url ?? a.url, auth_type: auth, team_id: teamId });
|
|
2589
|
+
const version2 = res.api_version || "1.0.0";
|
|
2590
|
+
const keys = res.api_keys ?? {};
|
|
2591
|
+
const key = keys.dev ?? Object.values(keys)[0];
|
|
2592
|
+
const url = `https://${a.name}.apiblaze.com/${version2}/dev`;
|
|
2593
|
+
const tryIt = buildTryItCurl(url, auth, key);
|
|
2594
|
+
const lines = [` ${import_chalk24.default.dim("Proxy URL:")} ${import_chalk24.default.bold(url)}`];
|
|
2595
|
+
if (res.devPortal) lines.push(` ${import_chalk24.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
2596
|
+
const envs = Object.keys(keys);
|
|
2597
|
+
if (envs.length) {
|
|
2598
|
+
lines.push("", ` ${import_chalk24.default.bold("API keys")} ${import_chalk24.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
2599
|
+
const w = Math.max(...envs.map((e) => e.length));
|
|
2600
|
+
for (const env of envs) lines.push(` ${import_chalk24.default.cyan(env.padEnd(w))} ${import_chalk24.default.green(keys[env])}`);
|
|
2601
|
+
}
|
|
2602
|
+
if (tryIt) lines.push("", ` ${import_chalk24.default.dim("Try it:")}`, ` ${import_chalk24.default.cyan(tryIt)}`);
|
|
2603
|
+
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
2604
|
+
}
|
|
2572
2605
|
},
|
|
2573
2606
|
{
|
|
2574
2607
|
name: "list_projects",
|
|
@@ -2583,7 +2616,7 @@ var TOOLS = [
|
|
|
2583
2616
|
run: async (a, { teamId }) => {
|
|
2584
2617
|
const p = await proj(teamId, a.project, a.apiversion);
|
|
2585
2618
|
const body = a.env ? { environments: { [a.env]: { target: a.url } } } : { target_url: a.url };
|
|
2586
|
-
return admin({ method: "PATCH", path:
|
|
2619
|
+
return admin({ method: "PATCH", path: `/projects/${p.projectId}/${p.apiVersion}`, body, summary: `Set target for ${p.projectName}` });
|
|
2587
2620
|
}
|
|
2588
2621
|
},
|
|
2589
2622
|
{
|
|
@@ -2596,7 +2629,7 @@ var TOOLS = [
|
|
|
2596
2629
|
if (a.rate != null) throttling.userRateLimit = Number(a.rate);
|
|
2597
2630
|
if (a.quota != null) throttling.proxyQuota = Number(a.quota);
|
|
2598
2631
|
if (a.period) throttling.quotaPeriod = a.period;
|
|
2599
|
-
return admin({ method: "PATCH", path:
|
|
2632
|
+
return admin({ method: "PATCH", path: `/projects/${p.projectId}/${p.apiVersion}`, body: { throttling }, summary: `Throttle ${p.projectName}` });
|
|
2600
2633
|
}
|
|
2601
2634
|
},
|
|
2602
2635
|
{
|
|
@@ -2605,7 +2638,7 @@ var TOOLS = [
|
|
|
2605
2638
|
params: { project: "project name or id", display_name: "new display name" },
|
|
2606
2639
|
run: async (a, { teamId }) => {
|
|
2607
2640
|
const p = await proj(teamId, a.project, a.apiversion);
|
|
2608
|
-
return admin({ method: "PATCH", path:
|
|
2641
|
+
return admin({ method: "PATCH", path: `/projects/${p.projectId}/${p.apiVersion}`, body: { display_name: a.display_name }, summary: `Rename ${p.projectName}` });
|
|
2609
2642
|
}
|
|
2610
2643
|
},
|
|
2611
2644
|
{
|
|
@@ -2614,7 +2647,7 @@ var TOOLS = [
|
|
|
2614
2647
|
params: { project: "project name or id", version: "optional api version" },
|
|
2615
2648
|
run: async (a, { teamId }) => {
|
|
2616
2649
|
const p = await proj(teamId, a.project, a.version);
|
|
2617
|
-
return admin({ method: "DELETE", path:
|
|
2650
|
+
return admin({ method: "DELETE", path: `/projects/${p.projectId}/${p.apiVersion}`, summary: `Delete ${p.projectName} (cascade)` });
|
|
2618
2651
|
}
|
|
2619
2652
|
},
|
|
2620
2653
|
{
|
|
@@ -2661,7 +2694,7 @@ var TOOLS = [
|
|
|
2661
2694
|
params: { project: "project name or id" },
|
|
2662
2695
|
run: async (a, { teamId }) => {
|
|
2663
2696
|
const p = await proj(teamId, a.project, a.apiversion);
|
|
2664
|
-
return admin({ method: "GET", path: `/projects/${p.projectId}/
|
|
2697
|
+
return admin({ method: "GET", path: `/projects/${p.projectId}/domains?api_version=${encodeURIComponent(p.apiVersion)}`, summary: `List domains for ${p.projectName}` });
|
|
2665
2698
|
}
|
|
2666
2699
|
},
|
|
2667
2700
|
{
|
|
@@ -2720,17 +2753,17 @@ function truncate(value, max = 1500) {
|
|
|
2720
2753
|
}
|
|
2721
2754
|
function printCost(llm) {
|
|
2722
2755
|
const usd = llm.cost > 0 ? `$${llm.cost.toFixed(4)}` : "<$0.0001";
|
|
2723
|
-
console.log(
|
|
2756
|
+
console.log(import_chalk25.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk25.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
|
|
2724
2757
|
}
|
|
2725
2758
|
async function runAgent(opts) {
|
|
2726
2759
|
requireAuth();
|
|
2727
2760
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
2728
2761
|
const { default: inquirer2 } = await import("inquirer");
|
|
2729
|
-
console.log(
|
|
2730
|
-
console.log(
|
|
2762
|
+
console.log(import_chalk25.default.bold("APIblaze agent") + import_chalk25.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
2763
|
+
console.log(import_chalk25.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
2731
2764
|
const history = [];
|
|
2732
2765
|
while (true) {
|
|
2733
|
-
const { input } = await inquirer2.prompt([{ type: "input", name: "input", message:
|
|
2766
|
+
const { input } = await inquirer2.prompt([{ type: "input", name: "input", message: import_chalk25.default.cyan("you") + " \u203A" }]);
|
|
2734
2767
|
const text = (input ?? "").trim();
|
|
2735
2768
|
if (!text) continue;
|
|
2736
2769
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
@@ -2744,14 +2777,14 @@ async function runAgent(opts) {
|
|
|
2744
2777
|
} catch (err) {
|
|
2745
2778
|
spinner.stop();
|
|
2746
2779
|
if (err instanceof ApiError && err.status === 402) {
|
|
2747
|
-
console.log(
|
|
2780
|
+
console.log(import_chalk25.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
2748
2781
|
break;
|
|
2749
2782
|
}
|
|
2750
2783
|
throw err;
|
|
2751
2784
|
}
|
|
2752
2785
|
history.push({ role: "assistant", content: resp.raw });
|
|
2753
2786
|
printCost(resp.llm);
|
|
2754
|
-
if (resp.reply) console.log(
|
|
2787
|
+
if (resp.reply) console.log(import_chalk25.default.green("agent") + " \u203A " + resp.reply);
|
|
2755
2788
|
if (!resp.action) break;
|
|
2756
2789
|
const tool = findTool(resp.action.tool);
|
|
2757
2790
|
if (!tool) {
|
|
@@ -2762,32 +2795,35 @@ async function runAgent(opts) {
|
|
|
2762
2795
|
try {
|
|
2763
2796
|
const result = await tool.run(resp.action.args, { teamId });
|
|
2764
2797
|
runSpinner.succeed(`${tool.name} \u2713`);
|
|
2765
|
-
|
|
2798
|
+
let forHistory = result;
|
|
2799
|
+
if (result && typeof result === "object" && typeof result.display === "string") {
|
|
2800
|
+
console.log("\n" + result.display + "\n");
|
|
2801
|
+
const { display, ...rest } = result;
|
|
2802
|
+
forHistory = { ...rest, _note: "Details + keys already shown to the user verbatim; do not repeat the keys." };
|
|
2803
|
+
}
|
|
2804
|
+
history.push({ role: "user", content: `TOOL_RESULT ${tool.name}: ${truncate(forHistory)}` });
|
|
2766
2805
|
} catch (err) {
|
|
2767
2806
|
runSpinner.fail(`${tool.name} failed`);
|
|
2768
2807
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2769
2808
|
history.push({ role: "user", content: `TOOL_RESULT ${tool.name}: error \u2014 ${truncate(msg, 400)}` });
|
|
2770
2809
|
}
|
|
2810
|
+
renderTrace();
|
|
2771
2811
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
2772
|
-
console.log(
|
|
2812
|
+
console.log(import_chalk25.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
2773
2813
|
}
|
|
2774
2814
|
}
|
|
2775
2815
|
}
|
|
2776
|
-
console.log(
|
|
2816
|
+
console.log(import_chalk25.default.dim("\nBye."));
|
|
2777
2817
|
}
|
|
2778
2818
|
|
|
2779
2819
|
// src/commands/consumer.ts
|
|
2780
|
-
var
|
|
2820
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
2781
2821
|
var import_ora12 = __toESM(require("ora"));
|
|
2782
|
-
var APIKEYS_VER = "1.0.0";
|
|
2783
2822
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
2784
|
-
|
|
2785
|
-
const tmpl = process.env.APIBLAZE_APIKEYS_BASE_TMPL;
|
|
2786
|
-
return tmpl ? tmpl.replace("{tenant}", tenant2) : `https://${tenant2}.apikeys.apiblaze.com`;
|
|
2787
|
-
}
|
|
2823
|
+
var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
|
|
2788
2824
|
async function consumerFetch(creds, suffix, init) {
|
|
2789
2825
|
const fresh = await validConsumerToken(creds) ?? creds;
|
|
2790
|
-
const res = await fetch(`${
|
|
2826
|
+
const res = await fetch(`${APIKEYS_BASE}${suffix}`, {
|
|
2791
2827
|
...init,
|
|
2792
2828
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${fresh.accessToken}`, ...init?.headers ?? {} }
|
|
2793
2829
|
});
|
|
@@ -2801,7 +2837,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
2801
2837
|
function requireConsumer() {
|
|
2802
2838
|
const c = loadConsumer();
|
|
2803
2839
|
if (!c) {
|
|
2804
|
-
console.error(
|
|
2840
|
+
console.error(import_chalk26.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
2805
2841
|
process.exit(1);
|
|
2806
2842
|
}
|
|
2807
2843
|
return c;
|
|
@@ -2812,7 +2848,7 @@ async function runConsumerLogin(opts) {
|
|
|
2812
2848
|
let clientId = opts.client;
|
|
2813
2849
|
if (clientId) {
|
|
2814
2850
|
if (!tenant2) {
|
|
2815
|
-
console.error(
|
|
2851
|
+
console.error(import_chalk26.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
2816
2852
|
process.exit(1);
|
|
2817
2853
|
}
|
|
2818
2854
|
} else {
|
|
@@ -2825,7 +2861,7 @@ async function runConsumerLogin(opts) {
|
|
|
2825
2861
|
(t) => typeof t === "string" ? { tenant_name: t } : t
|
|
2826
2862
|
);
|
|
2827
2863
|
if (!tenants.length) {
|
|
2828
|
-
console.error(
|
|
2864
|
+
console.error(import_chalk26.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
2829
2865
|
process.exit(1);
|
|
2830
2866
|
}
|
|
2831
2867
|
if (!tenant2) {
|
|
@@ -2841,19 +2877,20 @@ async function runConsumerLogin(opts) {
|
|
|
2841
2877
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
2842
2878
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
2843
2879
|
if (!pick2) {
|
|
2844
|
-
console.error(
|
|
2880
|
+
console.error(import_chalk26.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
2845
2881
|
process.exit(1);
|
|
2846
2882
|
}
|
|
2847
2883
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
2848
2884
|
}
|
|
2849
|
-
|
|
2885
|
+
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
2886
|
+
console.log(`${import_chalk26.default.cyan("\u2192")} Logging in to ${import_chalk26.default.bold(tenant2)} as a consumer...`);
|
|
2850
2887
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
2851
2888
|
console.log(`
|
|
2852
|
-
Open: ${
|
|
2853
|
-
console.log(` Code: ${
|
|
2889
|
+
Open: ${import_chalk26.default.underline(verificationUri)}`);
|
|
2890
|
+
console.log(` Code: ${import_chalk26.default.bold(userCode)}
|
|
2854
2891
|
`);
|
|
2855
|
-
console.log(
|
|
2856
|
-
});
|
|
2892
|
+
console.log(import_chalk26.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
2893
|
+
}, portalResource);
|
|
2857
2894
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
2858
2895
|
const creds = {
|
|
2859
2896
|
tenant: tenant2,
|
|
@@ -2867,7 +2904,7 @@ async function runConsumerLogin(opts) {
|
|
|
2867
2904
|
obtainedAt: Date.now()
|
|
2868
2905
|
};
|
|
2869
2906
|
saveConsumer(creds);
|
|
2870
|
-
console.log(
|
|
2907
|
+
console.log(import_chalk26.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
2871
2908
|
}
|
|
2872
2909
|
async function runConsumerTokens(opts) {
|
|
2873
2910
|
const creds = requireConsumer();
|
|
@@ -2880,18 +2917,18 @@ async function runConsumerTokens(opts) {
|
|
|
2880
2917
|
console.log(JSON.stringify({ tenant: fresh.tenant, access_token: fresh.accessToken, refresh_token: fresh.refreshToken, id_token: fresh.idToken, expires_at: new Date(fresh.expiresAt).toISOString() }, null, 2));
|
|
2881
2918
|
return;
|
|
2882
2919
|
}
|
|
2883
|
-
console.log(`${
|
|
2920
|
+
console.log(`${import_chalk26.default.cyan("Consumer")} ${import_chalk26.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk26.default.bold(fresh.tenant)}
|
|
2884
2921
|
`);
|
|
2885
|
-
console.log(`${
|
|
2922
|
+
console.log(`${import_chalk26.default.bold("access_token")} ${import_chalk26.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
2886
2923
|
${fresh.accessToken}
|
|
2887
2924
|
`);
|
|
2888
|
-
if (fresh.idToken) console.log(`${
|
|
2925
|
+
if (fresh.idToken) console.log(`${import_chalk26.default.bold("id_token")} ${import_chalk26.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
2889
2926
|
${fresh.idToken}
|
|
2890
2927
|
`);
|
|
2891
|
-
if (fresh.refreshToken) console.log(`${
|
|
2928
|
+
if (fresh.refreshToken) console.log(`${import_chalk26.default.bold("refresh_token")}
|
|
2892
2929
|
${fresh.refreshToken}
|
|
2893
2930
|
`);
|
|
2894
|
-
console.log(
|
|
2931
|
+
console.log(import_chalk26.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
2895
2932
|
}
|
|
2896
2933
|
async function runConsumerApikeys(opts) {
|
|
2897
2934
|
const creds = requireConsumer();
|
|
@@ -2901,8 +2938,8 @@ async function runConsumerApikeys(opts) {
|
|
|
2901
2938
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
2902
2939
|
spinner.stop();
|
|
2903
2940
|
if (list.status >= 400) {
|
|
2904
|
-
console.error(
|
|
2905
|
-
if (list.status === 401) console.error(
|
|
2941
|
+
console.error(import_chalk26.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
2942
|
+
if (list.status === 401) console.error(import_chalk26.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
2906
2943
|
process.exit(1);
|
|
2907
2944
|
}
|
|
2908
2945
|
const keys = list.data?.keys ?? [];
|
|
@@ -2910,16 +2947,16 @@ async function runConsumerApikeys(opts) {
|
|
|
2910
2947
|
if (opts.json) {
|
|
2911
2948
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
2912
2949
|
} else if (!keys.length) {
|
|
2913
|
-
console.log(
|
|
2950
|
+
console.log(import_chalk26.default.yellow("No API keys yet."));
|
|
2914
2951
|
} else {
|
|
2915
2952
|
for (const k of keys) {
|
|
2916
2953
|
const clear = revealMap[k.environment]?.key;
|
|
2917
|
-
const shown = clear ?
|
|
2918
|
-
const exp = k.expires_at ?
|
|
2919
|
-
console.log(` ${
|
|
2954
|
+
const shown = clear ? import_chalk26.default.green(clear) : import_chalk26.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
2955
|
+
const exp = k.expires_at ? import_chalk26.default.dim(`exp ${k.expires_at}`) : import_chalk26.default.dim("no expiry");
|
|
2956
|
+
console.log(` ${import_chalk26.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk26.default.dim(k.description ?? "")}`);
|
|
2920
2957
|
}
|
|
2921
2958
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
2922
|
-
console.log(
|
|
2959
|
+
console.log(import_chalk26.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
2923
2960
|
}
|
|
2924
2961
|
}
|
|
2925
2962
|
if (opts.json) return;
|
|
@@ -2941,8 +2978,8 @@ async function runConsumerApikeys(opts) {
|
|
|
2941
2978
|
}
|
|
2942
2979
|
s2.succeed("Key created.");
|
|
2943
2980
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
2944
|
-
if (key) console.log(` ${
|
|
2945
|
-
else console.log(
|
|
2981
|
+
if (key) console.log(` ${import_chalk26.default.green(key)} ${import_chalk26.default.dim("(shown once \u2014 store it now)")}`);
|
|
2982
|
+
else console.log(import_chalk26.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
2946
2983
|
}
|
|
2947
2984
|
|
|
2948
2985
|
// src/index.ts
|
|
@@ -2956,7 +2993,9 @@ function action(fn) {
|
|
|
2956
2993
|
return async (...args) => {
|
|
2957
2994
|
try {
|
|
2958
2995
|
await fn(...args);
|
|
2996
|
+
renderTrace();
|
|
2959
2997
|
} catch (err) {
|
|
2998
|
+
renderTrace();
|
|
2960
2999
|
printError(err);
|
|
2961
3000
|
process.exit(1);
|
|
2962
3001
|
}
|
|
@@ -2970,7 +3009,7 @@ program.command("login").description("Authenticate with APIblaze").action(async
|
|
|
2970
3009
|
process.exit(1);
|
|
2971
3010
|
}
|
|
2972
3011
|
});
|
|
2973
|
-
program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.apiblaze.com)").option("--target <url>", "Target URL to forward requests to").option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("--apiversion <version>", "API version to create (e.g. 2.0.0). Creating a new version of a proxy you own adds a version to the existing project.").option("--
|
|
3012
|
+
program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.apiblaze.com)").option("--target <url>", "Target URL to forward requests to").option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("--apiversion <version>", "API version to create (e.g. 2.0.0). Creating a new version of a proxy you own adds a version to the existing project.").option("--product <slug>", "Product tag to group this project under in the portal (team-scoped; anonymous create). Defaults to your team's existing/placeholder tag.").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--json", "Output machine-readable JSON (non-interactive)").action(async (opts) => {
|
|
2974
3013
|
try {
|
|
2975
3014
|
await runCreate(opts);
|
|
2976
3015
|
} catch (err) {
|
|
@@ -2986,7 +3025,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
2986
3025
|
try {
|
|
2987
3026
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
2988
3027
|
if (Number.isNaN(resolved)) {
|
|
2989
|
-
console.error(
|
|
3028
|
+
console.error(import_chalk27.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
2990
3029
|
process.exit(1);
|
|
2991
3030
|
}
|
|
2992
3031
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -3037,7 +3076,7 @@ program.command("target").description("Change where a proxy forwards requests").
|
|
|
3037
3076
|
program.command("throttle").description("Set rate limits and quotas for a proxy").argument("<project>", "Project name or id").option("--rate <n>", "User rate limit (requests/sec)").option("--end-user-rate <n>", "Per-end-user rate limit (requests/sec)").option("--quota <n>", "Proxy quota (requests/period)").option("--period <p>", "Quota period: daily | weekly | monthly").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runThrottleSet(project, opts)));
|
|
3038
3077
|
program.command("rename").description("Change a proxy's display name").argument("<project>", "Project name or id").requiredOption("--display-name <name>", "New human-friendly display name").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runRename(project, opts)));
|
|
3039
3078
|
var domain = program.command("domain").description("Use your own domain for a proxy");
|
|
3040
|
-
domain.command("add").description("Add your own domain (shows the DNS records to set)").argument("<project>", "Project name or id").requiredOption("--domain <host>", "Custom hostname to add").option("--tenant <slug>", "
|
|
3079
|
+
domain.command("add").description("Add your own domain (shows the DNS records to set)").argument("<project>", "Project name or id").requiredOption("--domain <host>", "Custom hostname to add").option("--tenant <slug>", "Bind the domain to one of the project's tenants (per-tenant scope)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainAdd(project, opts)));
|
|
3041
3080
|
domain.command("list").description("List custom domains for a proxy").argument("<project>", "Project name or id").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainList(project, opts)));
|
|
3042
3081
|
domain.command("status").description("Check a custom domain's validation status").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainStatus(project, opts)));
|
|
3043
3082
|
domain.command("rm").description("Remove a custom domain").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainRemove(project, opts)));
|
|
@@ -3071,7 +3110,7 @@ function groupedCommandHelp() {
|
|
|
3071
3110
|
const c = byName.get(n);
|
|
3072
3111
|
return c ? ` ${n.padEnd(width)}${c.description()}` : "";
|
|
3073
3112
|
}).filter(Boolean).join("\n");
|
|
3074
|
-
return `${
|
|
3113
|
+
return `${import_chalk27.default.bold(g.title)}
|
|
3075
3114
|
${rows}`;
|
|
3076
3115
|
}).join("\n\n");
|
|
3077
3116
|
}
|
|
@@ -3097,13 +3136,13 @@ Examples:
|
|
|
3097
3136
|
`);
|
|
3098
3137
|
function printError(err) {
|
|
3099
3138
|
if (err instanceof ApiError) {
|
|
3100
|
-
console.error(
|
|
3139
|
+
console.error(import_chalk27.default.red(`
|
|
3101
3140
|
API error (${err.status}): ${err.message}`));
|
|
3102
3141
|
} else if (err instanceof Error) {
|
|
3103
|
-
console.error(
|
|
3142
|
+
console.error(import_chalk27.default.red(`
|
|
3104
3143
|
Error: ${err.message}`));
|
|
3105
3144
|
} else {
|
|
3106
|
-
console.error(
|
|
3145
|
+
console.error(import_chalk27.default.red("\nUnknown error"));
|
|
3107
3146
|
}
|
|
3108
3147
|
}
|
|
3109
3148
|
program.parse(process.argv);
|