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.
Files changed (2) hide show
  1. package/dist/index.js +125 -86
  2. 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 import_chalk26 = __toESM(require("chalk"));
28
+ var import_chalk27 = __toESM(require("chalk"));
29
29
 
30
30
  // package.json
31
- var version = "0.4.2";
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 printCurlExample(url, apiKey) {
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
- console.log(` ${import_chalk6.default.dim("Try it with curl:")}`);
1110
- if (apiKey) {
1111
- console.log(` ${import_chalk6.default.cyan(`curl ${url} \\`)}`);
1112
- console.log(` ${import_chalk6.default.cyan(` -H "X-API-Key: ${apiKey}"`)}`);
1113
- } else {
1114
- console.log(` ${import_chalk6.default.cyan(`curl ${url}`)}`);
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 === "none" ? void 0 : adminKey);
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
- body: new URLSearchParams({ grant_type: DEVICE_GRANT, device_code: deviceCode, code_verifier: verifier })
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("Copy/paste the curl below to do it yourself \u2014 it uses your own login token.\n")
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 payload = JSON.stringify({ path: e.path, method: e.method, ...masked ? { body: JSON.parse(masked) } : {} });
2012
- console.log(import_chalk15.default.green(" curl -sS -X POST https://dashboard.apiblaze.com/api/cli/admin \\"));
2013
- console.log(import_chalk15.default.green(' -H "Authorization: Bearer $(jq -r .accessToken ~/.apiblaze/credentials.json)" \\'));
2014
- console.log(import_chalk15.default.green(" -H 'Content-Type: application/json' \\"));
2015
- console.log(import_chalk15.default.green(` -d '${payload}'`));
2016
- console.log(
2017
- import_chalk15.default.dim(
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: `/${proj2.projectId}/${proj2.apiVersion}`,
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: `/${proj2.projectId}/${proj2.apiVersion}/config`,
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}/cf-domains`,
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}/cf-domains?api_version=${encodeURIComponent(proj2.apiVersion)}`,
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}/cf-domains/${encodeURIComponent(opts.id)}/status`,
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}/cf-domains/${encodeURIComponent(opts.id)}`,
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 import_chalk24 = __toESM(require("chalk"));
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 }) => createProxy({ name: a.name, target_url: a.target ?? a.target_url ?? a.url, auth_type: a.auth ?? a.auth_type ?? "api_key", team_id: 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: `/${p.projectId}/${p.apiVersion}/config`, body, summary: `Set target for ${p.projectName}` });
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: `/${p.projectId}/${p.apiVersion}/config`, body: { throttling }, summary: `Throttle ${p.projectName}` });
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: `/${p.projectId}/${p.apiVersion}/config`, body: { display_name: a.display_name }, summary: `Rename ${p.projectName}` });
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: `/${p.projectId}/${p.apiVersion}`, summary: `Delete ${p.projectName} (cascade)` });
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}/cf-domains?api_version=${encodeURIComponent(p.apiVersion)}`, summary: `List domains for ${p.projectName}` });
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(import_chalk24.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk24.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
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(import_chalk24.default.bold("APIblaze agent") + import_chalk24.default.dim(` \xB7 team ${teamName ?? teamId}`));
2730
- console.log(import_chalk24.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
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: import_chalk24.default.cyan("you") + " \u203A" }]);
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(import_chalk24.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
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(import_chalk24.default.green("agent") + " \u203A " + resp.reply);
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
- history.push({ role: "user", content: `TOOL_RESULT ${tool.name}: ${truncate(result)}` });
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(import_chalk24.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
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(import_chalk24.default.dim("\nBye."));
2816
+ console.log(import_chalk25.default.dim("\nBye."));
2777
2817
  }
2778
2818
 
2779
2819
  // src/commands/consumer.ts
2780
- var import_chalk25 = __toESM(require("chalk"));
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
- function apikeysBase(tenant2) {
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(`${apikeysBase(fresh.tenant)}/${APIKEYS_VER}${suffix}`, {
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(import_chalk25.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
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(import_chalk25.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
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(import_chalk25.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
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(import_chalk25.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
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
- console.log(`${import_chalk25.default.cyan("\u2192")} Logging in to ${import_chalk25.default.bold(tenant2)} as a consumer...`);
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: ${import_chalk25.default.underline(verificationUri)}`);
2853
- console.log(` Code: ${import_chalk25.default.bold(userCode)}
2889
+ Open: ${import_chalk26.default.underline(verificationUri)}`);
2890
+ console.log(` Code: ${import_chalk26.default.bold(userCode)}
2854
2891
  `);
2855
- console.log(import_chalk25.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
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(import_chalk25.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
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(`${import_chalk25.default.cyan("Consumer")} ${import_chalk25.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk25.default.bold(fresh.tenant)}
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(`${import_chalk25.default.bold("access_token")} ${import_chalk25.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
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(`${import_chalk25.default.bold("id_token")} ${import_chalk25.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
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(`${import_chalk25.default.bold("refresh_token")}
2928
+ if (fresh.refreshToken) console.log(`${import_chalk26.default.bold("refresh_token")}
2892
2929
  ${fresh.refreshToken}
2893
2930
  `);
2894
- console.log(import_chalk25.default.dim("These are your own tokens \u2014 keep them secret."));
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(import_chalk25.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
2905
- if (list.status === 401) console.error(import_chalk25.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
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(import_chalk25.default.yellow("No API keys yet."));
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 ? import_chalk25.default.green(clear) : import_chalk25.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
2918
- const exp = k.expires_at ? import_chalk25.default.dim(`exp ${k.expires_at}`) : import_chalk25.default.dim("no expiry");
2919
- console.log(` ${import_chalk25.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk25.default.dim(k.description ?? "")}`);
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(import_chalk25.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
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(` ${import_chalk25.default.green(key)} ${import_chalk25.default.dim("(shown once \u2014 store it now)")}`);
2945
- else console.log(import_chalk25.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
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("--tenant <slug>", "Tenant slug (anonymous create; generated if omitted)").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) => {
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(import_chalk26.default.red(`Invalid port: ${port ?? opts.port}`));
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>", "Tenant to scope the domain to").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)));
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 `${import_chalk26.default.bold(g.title)}
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(import_chalk26.default.red(`
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(import_chalk26.default.red(`
3142
+ console.error(import_chalk27.default.red(`
3104
3143
  Error: ${err.message}`));
3105
3144
  } else {
3106
- console.error(import_chalk26.default.red("\nUnknown error"));
3145
+ console.error(import_chalk27.default.red("\nUnknown error"));
3107
3146
  }
3108
3147
  }
3109
3148
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.4.2",
3
+ "version": "0.4.7",
4
4
  "description": "Dev tunnel CLI for APIblaze — route localhost projects through your APIblaze endpoints",
5
5
  "keywords": [
6
6
  "apiblaze",