apiblaze 0.11.1 → 0.12.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.
Files changed (2) hide show
  1. package/dist/index.js +921 -325
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -300,12 +300,199 @@ var init_anon_cred = __esm({
300
300
  }
301
301
  });
302
302
 
303
+ // src/lib/trace.ts
304
+ function setVerbose(v) {
305
+ verbose = v;
306
+ }
307
+ function recordCall(e) {
308
+ if (verbose) entries.push(e);
309
+ }
310
+ function maskBody(body) {
311
+ if (body === void 0) return void 0;
312
+ return JSON.stringify(body, (k, v) => SECRET_KEY.test(k) && typeof v === "string" ? "***" : v);
313
+ }
314
+ function renderTrace() {
315
+ if (!verbose || entries.length === 0) return;
316
+ console.log(import_chalk15.default.dim("\n" + "\u2500".repeat(64)));
317
+ console.log(import_chalk15.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
318
+ console.log(
319
+ 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_...`).\nFull API reference: https://api.apiblaze.com/openapi.json\n")
320
+ );
321
+ entries.forEach((e, i) => {
322
+ const n = entries.length > 1 ? import_chalk15.default.bold(`${i + 1}. `) : "";
323
+ if (e.summary) console.log(`${n}${import_chalk15.default.cyan(e.summary)}${e.status ? import_chalk15.default.dim(` (HTTP ${e.status})`) : ""}`);
324
+ const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
325
+ const masked = maskBody(e.body);
326
+ const hasBody = e.method !== "GET" && masked !== void 0;
327
+ console.log(import_chalk15.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
328
+ console.log(import_chalk15.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
329
+ if (hasBody) {
330
+ console.log(import_chalk15.default.green(" -H 'Content-Type: application/json' \\"));
331
+ console.log(import_chalk15.default.green(` -d '${masked}'`));
332
+ }
333
+ if (i < entries.length - 1) console.log();
334
+ });
335
+ entries.length = 0;
336
+ }
337
+ var import_chalk15, CONTROL_API_VERSION, verbose, entries, SECRET_KEY;
338
+ var init_trace = __esm({
339
+ "src/lib/trace.ts"() {
340
+ "use strict";
341
+ import_chalk15 = __toESM(require("chalk"));
342
+ CONTROL_API_VERSION = "1.0.0";
343
+ verbose = false;
344
+ entries = [];
345
+ SECRET_KEY = /secret|token|password|api[_-]?key|client_secret/i;
346
+ }
347
+ });
348
+
349
+ // src/lib/admin.ts
350
+ async function admin(call) {
351
+ const token = getAccessToken();
352
+ const res = await fetch(`${DASHBOARD_BASE3}/api/cli/admin`, {
353
+ method: "POST",
354
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
355
+ body: JSON.stringify({ path: call.path, method: call.method, body: call.body })
356
+ });
357
+ let data = null;
358
+ try {
359
+ data = await res.json();
360
+ } catch {
361
+ }
362
+ recordCall({ method: call.method, path: call.path, body: call.body, status: res.status, summary: call.summary });
363
+ maybePrintBilling(data);
364
+ if (!res.ok) {
365
+ const msg = data?.details ?? data?.error ?? res.statusText;
366
+ throw new ApiError(res.status, typeof msg === "string" ? msg : JSON.stringify(msg), data);
367
+ }
368
+ return data;
369
+ }
370
+ function maybePrintBilling(data) {
371
+ const b = data?.billing;
372
+ if (b && typeof b.charged_cents === "number") {
373
+ const usd = (b.charged_cents / 100).toFixed(2);
374
+ const rem = typeof b.credits_remaining === "number" ? ` \xB7 $${(b.credits_remaining / 100).toFixed(2)} credit left` : "";
375
+ console.log(import_chalk16.default.magenta(` \u{1F4B3} Charged $${usd}${rem}`));
376
+ }
377
+ }
378
+ var import_chalk16, DASHBOARD_BASE3;
379
+ var init_admin = __esm({
380
+ "src/lib/admin.ts"() {
381
+ "use strict";
382
+ import_chalk16 = __toESM(require("chalk"));
383
+ init_auth();
384
+ init_trace();
385
+ init_types();
386
+ DASHBOARD_BASE3 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
387
+ }
388
+ });
389
+
390
+ // src/lib/tenant-pick.ts
391
+ var tenant_pick_exports = {};
392
+ __export(tenant_pick_exports, {
393
+ pickTenant: () => pickTenant
394
+ });
395
+ async function fetchPage(teamId, q) {
396
+ const out = await admin({
397
+ method: "GET",
398
+ path: `/teams/${encodeURIComponent(teamId)}/tenants?detail=1&limit=${PAGE}${q ? `&q=${encodeURIComponent(q)}` : ""}`,
399
+ summary: q ? `Search tenants matching "${q}"` : "List tenants (first page)"
400
+ });
401
+ const rows = (out?.tenants ?? []).map(
402
+ (t) => typeof t === "string" ? { tenant_name: t } : t
403
+ );
404
+ return { rows, total: out?.total ?? rows.length, defaultTenant: out?.default_tenant ?? null };
405
+ }
406
+ function label(t, defaultTenant, active) {
407
+ const tags = [
408
+ t.tenant_name === active ? import_chalk22.default.cyan("active scope") : "",
409
+ t.tenant_name === defaultTenant ? import_chalk22.default.dim("team default") : ""
410
+ ].filter(Boolean).join(", ");
411
+ const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk22.default.dim(` ${t.display_name}`) : "";
412
+ return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
413
+ }
414
+ async function pickTenant(teamId, opts = {}) {
415
+ const { default: inquirer2 } = await import("inquirer");
416
+ const active = loadCredentials()?.activeTenant;
417
+ let q = opts.initialQuery ?? "";
418
+ for (; ; ) {
419
+ const spinner = (0, import_ora8.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
420
+ const page = await fetchPage(teamId, q).finally(() => spinner.stop());
421
+ if (!page.total && !q) {
422
+ if (opts.allowCreate) {
423
+ const { make } = await inquirer2.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
424
+ if (make) return await createTenantInline(teamId);
425
+ }
426
+ console.error(import_chalk22.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
427
+ return null;
428
+ }
429
+ const truncated = page.total > page.rows.length;
430
+ const choices = page.rows.map((t) => ({
431
+ name: label(t, page.defaultTenant, active),
432
+ value: t.tenant_name
433
+ }));
434
+ if (truncated || q) {
435
+ choices.push(new inquirer2.Separator(import_chalk22.default.dim(
436
+ truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
437
+ )));
438
+ choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
439
+ }
440
+ if (q) choices.push({ name: "\u21BA Show all (clear search)", value: "\0clear" });
441
+ if (opts.allowCreate) choices.push({ name: "\uFF0B Create a new tenant\u2026", value: "\0create" });
442
+ if (opts.allowBack) choices.push({ name: "\u2190 Back", value: "\0back" });
443
+ const { picked } = await inquirer2.prompt([{
444
+ type: "list",
445
+ name: "picked",
446
+ pageSize: PAGE + 5,
447
+ message: opts.message ?? "Which tenant?",
448
+ default: active && page.rows.some((t) => t.tenant_name === active) ? active : void 0,
449
+ choices
450
+ }]);
451
+ if (picked === "\0back") return null;
452
+ if (picked === "\0clear") {
453
+ q = "";
454
+ continue;
455
+ }
456
+ if (picked === "\0create") return await createTenantInline(teamId);
457
+ if (picked === "\0search") {
458
+ const { nq } = await inquirer2.prompt([{ type: "input", name: "nq", message: "Search (name or display name):", default: q }]);
459
+ q = String(nq ?? "").trim();
460
+ continue;
461
+ }
462
+ return picked;
463
+ }
464
+ }
465
+ async function createTenantInline(teamId) {
466
+ const { default: inquirer2 } = await import("inquirer");
467
+ const { display } = await inquirer2.prompt([{ type: "input", name: "display", message: "Display name for the new tenant:", validate: (s) => !!s.trim() || "required" }]);
468
+ const out = await admin({
469
+ method: "POST",
470
+ path: `/teams/${encodeURIComponent(teamId)}/tenants`,
471
+ body: { display_name: display.trim() },
472
+ summary: `Create tenant "${display.trim()}"`
473
+ });
474
+ const slug = out?.tenant_name ?? out?.tenant?.tenant_name;
475
+ console.log(import_chalk22.default.green(` Tenant ${import_chalk22.default.bold(slug)} created.`));
476
+ return slug;
477
+ }
478
+ var import_chalk22, import_ora8, PAGE;
479
+ var init_tenant_pick = __esm({
480
+ "src/lib/tenant-pick.ts"() {
481
+ "use strict";
482
+ import_chalk22 = __toESM(require("chalk"));
483
+ import_ora8 = __toESM(require("ora"));
484
+ init_admin();
485
+ init_auth();
486
+ PAGE = 15;
487
+ }
488
+ });
489
+
303
490
  // src/index.ts
304
491
  var import_commander = require("commander");
305
- var import_chalk32 = __toESM(require("chalk"));
492
+ var import_chalk34 = __toESM(require("chalk"));
306
493
 
307
494
  // package.json
308
- var version = "0.11.1";
495
+ var version = "0.12.1";
309
496
 
310
497
  // src/index.ts
311
498
  init_types();
@@ -1009,9 +1196,9 @@ function printTunnelEndpoints(restore, targets) {
1009
1196
  if (restore.length === 0) return;
1010
1197
  console.log(import_chalk4.default.bold("\nYour proxy is live at:"));
1011
1198
  for (const r of restore) {
1012
- const label = targets.find((t) => t.projectId === r.projectId)?.projectName ?? r.projectId;
1199
+ const label2 = targets.find((t) => t.projectId === r.projectId)?.projectName ?? r.projectId;
1013
1200
  console.log(`
1014
- ${import_chalk4.default.bold(label)}`);
1201
+ ${import_chalk4.default.bold(label2)}`);
1015
1202
  const internalEnvs = Object.keys(r.environments ?? {}).filter((e) => isInternalTarget(r.environments[e]?.target));
1016
1203
  const envs = internalEnvs.includes("dev") ? ["dev"] : internalEnvs.length ? internalEnvs : ["dev"];
1017
1204
  for (const env of envs) {
@@ -1835,6 +2022,7 @@ async function runWhoami(opts = {}) {
1835
2022
  console.log(` ${import_chalk9.default.cyan("Signed in as")} ${import_chalk9.default.bold(who)}`);
1836
2023
  if (creds.email && creds.email !== who) console.log(` Email: ${creds.email}`);
1837
2024
  if (creds.teamName || creds.teamId) console.log(` Team: ${import_chalk9.default.bold(creds.teamName ?? creds.teamId)}`);
2025
+ if (creds.activeTenant) console.log(` Tenant scope: ${import_chalk9.default.bold(creds.activeTenant)} ${import_chalk9.default.dim("(clear: `apiblaze tenant use --clear`)")}`);
1838
2026
  if (Date.now() >= creds.expiresAt) console.log(import_chalk9.default.yellow(" \u26A0 Session expired \u2014 run `apiblaze login`."));
1839
2027
  }
1840
2028
  console.log(import_chalk9.default.bold("\nAPI Consumer"));
@@ -2181,82 +2369,7 @@ async function runMcp(projectArg, apiVersionArg, opts) {
2181
2369
  // src/commands/delete.ts
2182
2370
  var import_chalk18 = __toESM(require("chalk"));
2183
2371
  var import_ora5 = __toESM(require("ora"));
2184
-
2185
- // src/lib/admin.ts
2186
- var import_chalk16 = __toESM(require("chalk"));
2187
- init_auth();
2188
-
2189
- // src/lib/trace.ts
2190
- var import_chalk15 = __toESM(require("chalk"));
2191
- var CONTROL_API_VERSION = "1.0.0";
2192
- var verbose = false;
2193
- var entries = [];
2194
- function setVerbose(v) {
2195
- verbose = v;
2196
- }
2197
- function recordCall(e) {
2198
- if (verbose) entries.push(e);
2199
- }
2200
- var SECRET_KEY = /secret|token|password|api[_-]?key|client_secret/i;
2201
- function maskBody(body) {
2202
- if (body === void 0) return void 0;
2203
- return JSON.stringify(body, (k, v) => SECRET_KEY.test(k) && typeof v === "string" ? "***" : v);
2204
- }
2205
- function renderTrace() {
2206
- if (!verbose || entries.length === 0) return;
2207
- console.log(import_chalk15.default.dim("\n" + "\u2500".repeat(64)));
2208
- console.log(import_chalk15.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
2209
- console.log(
2210
- 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_...`).\nFull API reference: https://api.apiblaze.com/openapi.json\n")
2211
- );
2212
- entries.forEach((e, i) => {
2213
- const n = entries.length > 1 ? import_chalk15.default.bold(`${i + 1}. `) : "";
2214
- if (e.summary) console.log(`${n}${import_chalk15.default.cyan(e.summary)}${e.status ? import_chalk15.default.dim(` (HTTP ${e.status})`) : ""}`);
2215
- const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
2216
- const masked = maskBody(e.body);
2217
- const hasBody = e.method !== "GET" && masked !== void 0;
2218
- console.log(import_chalk15.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
2219
- console.log(import_chalk15.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
2220
- if (hasBody) {
2221
- console.log(import_chalk15.default.green(" -H 'Content-Type: application/json' \\"));
2222
- console.log(import_chalk15.default.green(` -d '${masked}'`));
2223
- }
2224
- if (i < entries.length - 1) console.log();
2225
- });
2226
- entries.length = 0;
2227
- }
2228
-
2229
- // src/lib/admin.ts
2230
- init_types();
2231
- var DASHBOARD_BASE3 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
2232
- async function admin(call) {
2233
- const token = getAccessToken();
2234
- const res = await fetch(`${DASHBOARD_BASE3}/api/cli/admin`, {
2235
- method: "POST",
2236
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
2237
- body: JSON.stringify({ path: call.path, method: call.method, body: call.body })
2238
- });
2239
- let data = null;
2240
- try {
2241
- data = await res.json();
2242
- } catch {
2243
- }
2244
- recordCall({ method: call.method, path: call.path, body: call.body, status: res.status, summary: call.summary });
2245
- maybePrintBilling(data);
2246
- if (!res.ok) {
2247
- const msg = data?.details ?? data?.error ?? res.statusText;
2248
- throw new ApiError(res.status, typeof msg === "string" ? msg : JSON.stringify(msg), data);
2249
- }
2250
- return data;
2251
- }
2252
- function maybePrintBilling(data) {
2253
- const b = data?.billing;
2254
- if (b && typeof b.charged_cents === "number") {
2255
- const usd = (b.charged_cents / 100).toFixed(2);
2256
- const rem = typeof b.credits_remaining === "number" ? ` \xB7 $${(b.credits_remaining / 100).toFixed(2)} credit left` : "";
2257
- console.log(import_chalk16.default.magenta(` \u{1F4B3} Charged $${usd}${rem}`));
2258
- }
2259
- }
2372
+ init_admin();
2260
2373
 
2261
2374
  // src/lib/resolve.ts
2262
2375
  var import_chalk17 = __toESM(require("chalk"));
@@ -2369,6 +2482,7 @@ async function runDelete(project, version2, opts) {
2369
2482
  // src/commands/export.ts
2370
2483
  var import_chalk19 = __toESM(require("chalk"));
2371
2484
  var import_fs3 = require("fs");
2485
+ init_admin();
2372
2486
  init_auth();
2373
2487
  var DASHBOARD_BASE4 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
2374
2488
  async function runExport(projectArg, versionArg, opts) {
@@ -2425,6 +2539,7 @@ async function runExport(projectArg, versionArg, opts) {
2425
2539
  // src/commands/config.ts
2426
2540
  var import_chalk20 = __toESM(require("chalk"));
2427
2541
  var import_ora6 = __toESM(require("ora"));
2542
+ init_admin();
2428
2543
  async function patchConfig(project, opts, body, summary) {
2429
2544
  const { teamId } = await resolveTeam(opts.team);
2430
2545
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
@@ -2478,13 +2593,15 @@ async function runRename(project, opts) {
2478
2593
  }
2479
2594
 
2480
2595
  // src/commands/config-browse.ts
2481
- var import_chalk26 = __toESM(require("chalk"));
2482
- var import_ora11 = __toESM(require("ora"));
2596
+ var import_chalk28 = __toESM(require("chalk"));
2597
+ var import_ora13 = __toESM(require("ora"));
2598
+ init_admin();
2483
2599
  init_auth();
2484
2600
 
2485
2601
  // src/commands/domain.ts
2486
2602
  var import_chalk21 = __toESM(require("chalk"));
2487
2603
  var import_ora7 = __toESM(require("ora"));
2604
+ init_admin();
2488
2605
  async function runDomainAdd(project, opts) {
2489
2606
  if (!opts.domain) {
2490
2607
  console.error(import_chalk21.default.red("--domain is required."));
@@ -2587,14 +2704,37 @@ async function runDomainSetBase(project, opts) {
2587
2704
  }
2588
2705
 
2589
2706
  // src/commands/tenant.ts
2590
- var import_chalk22 = __toESM(require("chalk"));
2591
- var import_ora8 = __toESM(require("ora"));
2707
+ var import_chalk23 = __toESM(require("chalk"));
2708
+ var import_ora9 = __toESM(require("ora"));
2709
+ init_admin();
2710
+ init_auth();
2711
+ init_tenant_pick();
2712
+ async function runTenantUse(query, opts) {
2713
+ const creds = loadCredentials();
2714
+ if (!creds) {
2715
+ console.error(import_chalk23.default.red("Not logged in. Run `apiblaze login` first."));
2716
+ process.exit(1);
2717
+ }
2718
+ if (opts.clear) {
2719
+ delete creds.activeTenant;
2720
+ saveCredentials(creds);
2721
+ console.log(import_chalk23.default.green("Tenant scope cleared."));
2722
+ return;
2723
+ }
2724
+ const { teamId } = await resolveTeam(opts.team);
2725
+ const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
2726
+ if (!slug) process.exit(1);
2727
+ saveCredentials({ ...creds, activeTenant: slug });
2728
+ console.log(import_chalk23.default.green(`Tenant scope set to ${import_chalk23.default.bold(slug)}.`) + import_chalk23.default.dim(" (clear with `apiblaze tenant use --clear`)"));
2729
+ }
2592
2730
  async function runTenantList(opts) {
2593
2731
  const { teamId, teamName } = await resolveTeam(opts.team);
2732
+ const limit = opts.limit ?? (opts.q ? "50" : void 0);
2733
+ const qs = `?detail=1${opts.q ? `&q=${encodeURIComponent(opts.q)}` : ""}${limit ? `&limit=${encodeURIComponent(limit)}` : ""}`;
2594
2734
  const out = await admin({
2595
2735
  method: "GET",
2596
- path: `/teams/${encodeURIComponent(teamId)}/tenants?detail=1`,
2597
- summary: `List tenants for team ${teamName ?? teamId}`
2736
+ path: `/teams/${encodeURIComponent(teamId)}/tenants${qs}`,
2737
+ summary: opts.q ? `Search tenants matching "${opts.q}"` : `List tenants for team ${teamName ?? teamId}`
2598
2738
  });
2599
2739
  const tenants = out?.tenants ?? [];
2600
2740
  if (opts.json) {
@@ -2602,22 +2742,26 @@ async function runTenantList(opts) {
2602
2742
  return;
2603
2743
  }
2604
2744
  if (!tenants.length) {
2605
- console.log(import_chalk22.default.yellow("No tenants."));
2745
+ console.log(import_chalk23.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
2606
2746
  return;
2607
2747
  }
2608
2748
  for (const t of tenants) {
2609
2749
  const name = typeof t === "string" ? t : t.tenant_name;
2610
- const display = typeof t === "string" ? "" : import_chalk22.default.dim(` ${t.display_name ?? ""}`);
2611
- console.log(` ${import_chalk22.default.bold(name)}${display}`);
2750
+ const display = typeof t === "string" ? "" : import_chalk23.default.dim(` ${t.display_name ?? ""}`);
2751
+ console.log(` ${import_chalk23.default.bold(name)}${display}`);
2752
+ }
2753
+ const total = out?.total ?? tenants.length;
2754
+ if (total > tenants.length) {
2755
+ console.log(import_chalk23.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
2612
2756
  }
2613
2757
  }
2614
2758
  async function runTenantCreate(opts) {
2615
2759
  if (!opts.name) {
2616
- console.error(import_chalk22.default.red("--name (display name) is required."));
2760
+ console.error(import_chalk23.default.red("--name (display name) is required."));
2617
2761
  process.exit(1);
2618
2762
  }
2619
2763
  const { teamId } = await resolveTeam(opts.team);
2620
- const spinner = (0, import_ora8.default)("Creating tenant...").start();
2764
+ const spinner = (0, import_ora9.default)("Creating tenant...").start();
2621
2765
  try {
2622
2766
  const out = await admin({
2623
2767
  method: "POST",
@@ -2625,7 +2769,7 @@ async function runTenantCreate(opts) {
2625
2769
  body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
2626
2770
  summary: `Create tenant "${opts.name}"`
2627
2771
  });
2628
- spinner.succeed(`Created tenant ${import_chalk22.default.bold(out?.tenant_name ?? opts.name)}.`);
2772
+ spinner.succeed(`Created tenant ${import_chalk23.default.bold(out?.tenant_name ?? opts.name)}.`);
2629
2773
  if (opts.json) console.log(JSON.stringify(out));
2630
2774
  } catch (err) {
2631
2775
  spinner.fail("Tenant create failed.");
@@ -2634,12 +2778,12 @@ async function runTenantCreate(opts) {
2634
2778
  }
2635
2779
  async function runTenantAttach(project, opts) {
2636
2780
  if (!opts.tenant) {
2637
- console.error(import_chalk22.default.red("--tenant <slug> is required."));
2781
+ console.error(import_chalk23.default.red("--tenant <slug> is required."));
2638
2782
  process.exit(1);
2639
2783
  }
2640
2784
  const { teamId } = await resolveTeam(opts.team);
2641
2785
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
2642
- const spinner = (0, import_ora8.default)("Attaching tenant...").start();
2786
+ const spinner = (0, import_ora9.default)("Attaching tenant...").start();
2643
2787
  try {
2644
2788
  const out = await admin({
2645
2789
  method: "POST",
@@ -2662,11 +2806,11 @@ async function runTenantDelete(slug, opts) {
2662
2806
  { type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
2663
2807
  ]);
2664
2808
  if (!confirm) {
2665
- console.log(import_chalk22.default.dim("Aborted."));
2809
+ console.log(import_chalk23.default.dim("Aborted."));
2666
2810
  return;
2667
2811
  }
2668
2812
  }
2669
- const spinner = (0, import_ora8.default)("Deleting tenant...").start();
2813
+ const spinner = (0, import_ora9.default)("Deleting tenant...").start();
2670
2814
  try {
2671
2815
  await admin({
2672
2816
  method: "DELETE",
@@ -2681,13 +2825,13 @@ async function runTenantDelete(slug, opts) {
2681
2825
  }
2682
2826
  async function runTenantCors(opts) {
2683
2827
  if (!opts.tenant) {
2684
- console.error(import_chalk22.default.red("--tenant <slug> is required."));
2828
+ console.error(import_chalk23.default.red("--tenant <slug> is required."));
2685
2829
  process.exit(1);
2686
2830
  }
2687
2831
  const { teamId } = await resolveTeam(opts.team);
2688
2832
  const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
2689
2833
  const cors = origins.length ? { allowed_origins: origins } : null;
2690
- const spinner = (0, import_ora8.default)("Updating CORS...").start();
2834
+ const spinner = (0, import_ora9.default)("Updating CORS...").start();
2691
2835
  try {
2692
2836
  await admin({
2693
2837
  method: "PUT",
@@ -2702,10 +2846,458 @@ async function runTenantCors(opts) {
2702
2846
  }
2703
2847
  }
2704
2848
 
2849
+ // src/commands/tenant-drill.ts
2850
+ var import_chalk24 = __toESM(require("chalk"));
2851
+ var import_ora10 = __toESM(require("ora"));
2852
+ var import_crypto = require("crypto");
2853
+ init_admin();
2854
+ init_auth();
2855
+ init_tenant_pick();
2856
+ init_api();
2857
+ var trailingComma = /\s*,\s*/;
2858
+ var parseList = (s) => s.split(trailingComma).map((x) => x.trim()).filter(Boolean);
2859
+ async function runTenantManage(query, opts) {
2860
+ const { teamId } = await resolveTeam(opts.team);
2861
+ const slug = opts.tenant ?? loadCredentialsTenant(query) ?? await pickTenant(teamId, { message: "Manage which tenant?", initialQuery: query, allowCreate: true });
2862
+ if (!slug) return;
2863
+ await tenantHome(teamId, slug);
2864
+ }
2865
+ function loadCredentialsTenant(query) {
2866
+ if (query) return void 0;
2867
+ return loadCredentials()?.activeTenant ?? void 0;
2868
+ }
2869
+ async function tenantHome(teamId, tenant2) {
2870
+ const { default: inquirer2 } = await import("inquirer");
2871
+ const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
2872
+ console.log(import_chalk24.default.bold(`
2873
+ Tenant ${tenant2}`));
2874
+ console.log(import_chalk24.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
2875
+ for (; ; ) {
2876
+ const spinner = (0, import_ora10.default)("Reading tenant state...").start();
2877
+ const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
2878
+ admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
2879
+ admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
2880
+ admin({ method: "GET", path: `${base}/admin-emails`, summary: "List consumer-admin emails" }).catch(() => null),
2881
+ admin({ method: "GET", path: `${base}/external-issuers`, summary: "List external issuers" }).catch(() => null),
2882
+ admin({ method: "GET", path: `${base}/opaque`, summary: "Read opaque validator" }).catch(() => null),
2883
+ admin({ method: "GET", path: `${base}/app-clients`, summary: "List app clients" }).catch(() => [])
2884
+ ]).finally(() => spinner.stop());
2885
+ const nEmails = (emails?.admin_emails ?? []).length;
2886
+ const nIssuers = (issuers?.external_issuers ?? []).length;
2887
+ const nClients = Array.isArray(clients) ? clients.length : 0;
2888
+ const onOff = (b) => b ? import_chalk24.default.green("on") : import_chalk24.default.dim("off");
2889
+ const { pick: pick2 } = await inquirer2.prompt([{
2890
+ type: "list",
2891
+ name: "pick",
2892
+ message: `Tenant ${tenant2}:`,
2893
+ pageSize: 12,
2894
+ choices: [
2895
+ { name: `App clients (${nClients}) ${import_chalk24.default.dim("OAuth clients your consumers log in through \u2014 providers live inside")}`, value: "clients" },
2896
+ { name: `IAM enforcement: ${onOff(iam?.iam_enabled)} ${import_chalk24.default.dim("key/identity enforcement for this tenant")}`, value: "iam" },
2897
+ { name: `CORS override: ${cors?.cors ? import_chalk24.default.cyan(JSON.stringify(cors.cors)) : import_chalk24.default.dim("(unset)")}`, value: "cors" },
2898
+ { name: `Consumer-admin emails (${nEmails}) ${import_chalk24.default.dim("who may administer the tenant portal")}`, value: "emails" },
2899
+ { name: `External JWT issuers (${nIssuers}) ${import_chalk24.default.dim("bring-your-own auth: trust tokens you already mint")}`, value: "issuers" },
2900
+ { name: `Opaque-token validator: ${opaque?.opaque?.endpoint ? import_chalk24.default.cyan(opaque.opaque.endpoint) : import_chalk24.default.dim("(unset)")}`, value: "opaque" },
2901
+ { name: "\u2190 Back", value: "back" }
2902
+ ]
2903
+ }]);
2904
+ switch (pick2) {
2905
+ case "back":
2906
+ return;
2907
+ case "clients":
2908
+ await clientsMenu(teamId, tenant2, base);
2909
+ break;
2910
+ case "iam": {
2911
+ const { v } = await inquirer2.prompt([{ type: "confirm", name: "v", message: "Enable IAM enforcement?", default: !!iam?.iam_enabled }]);
2912
+ await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
2913
+ console.log(import_chalk24.default.green(` IAM enforcement ${v ? "enabled" : "disabled"}.`));
2914
+ break;
2915
+ }
2916
+ case "cors": {
2917
+ const { v } = await inquirer2.prompt([{
2918
+ type: "input",
2919
+ name: "v",
2920
+ message: 'CORS JSON (e.g. {"allow_all_origins":true} \u2014 "null" clears):',
2921
+ default: cors?.cors ? JSON.stringify(cors.cors) : ""
2922
+ }]);
2923
+ if (v === "") break;
2924
+ const parsed = v === "null" ? null : safeJson(v);
2925
+ if (parsed === void 0) {
2926
+ console.log(import_chalk24.default.yellow(" Not valid JSON \u2014 unchanged."));
2927
+ break;
2928
+ }
2929
+ await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
2930
+ console.log(import_chalk24.default.green(" CORS updated."));
2931
+ break;
2932
+ }
2933
+ case "emails":
2934
+ await emailsMenu(base, emails?.admin_emails ?? []);
2935
+ break;
2936
+ case "issuers":
2937
+ await issuersMenu(base, issuers?.external_issuers ?? []);
2938
+ break;
2939
+ case "opaque": {
2940
+ const cur = opaque?.opaque;
2941
+ const { mode } = await inquirer2.prompt([{
2942
+ type: "list",
2943
+ name: "mode",
2944
+ message: "Opaque-token validator:",
2945
+ choices: [
2946
+ { name: cur ? "Replace it" : "Set one up", value: "set" },
2947
+ ...cur ? [{ name: "Clear it", value: "clear" }] : [],
2948
+ { name: "\u2190 Back", value: "back" }
2949
+ ]
2950
+ }]);
2951
+ if (mode === "back") break;
2952
+ if (mode === "clear") {
2953
+ await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
2954
+ console.log(import_chalk24.default.green(" Cleared."));
2955
+ break;
2956
+ }
2957
+ const a = await inquirer2.prompt([
2958
+ { type: "input", name: "endpoint", message: "Introspection endpoint (https):", default: cur?.endpoint, validate: (s) => s.startsWith("https://") || "must be https" },
2959
+ { type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
2960
+ ]);
2961
+ await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
2962
+ console.log(import_chalk24.default.green(" Opaque validator set."));
2963
+ break;
2964
+ }
2965
+ }
2966
+ }
2967
+ }
2968
+ function safeJson(s) {
2969
+ try {
2970
+ return JSON.parse(s);
2971
+ } catch {
2972
+ return void 0;
2973
+ }
2974
+ }
2975
+ async function emailsMenu(base, emails) {
2976
+ const { default: inquirer2 } = await import("inquirer");
2977
+ console.log();
2978
+ if (!emails.length) console.log(import_chalk24.default.dim(" No consumer-admin emails."));
2979
+ for (const e of emails) console.log(` ${import_chalk24.default.bold(e.email ?? e)} ${import_chalk24.default.dim(e.status ?? "")}`);
2980
+ const { act } = await inquirer2.prompt([{
2981
+ type: "list",
2982
+ name: "act",
2983
+ message: "Consumer-admin emails:",
2984
+ choices: [
2985
+ { name: "Add an email", value: "add" },
2986
+ ...emails.length ? [{ name: "Remove an email", value: "rm" }] : [],
2987
+ { name: "\u2190 Back", value: "back" }
2988
+ ]
2989
+ }]);
2990
+ if (act === "back") return;
2991
+ if (act === "add") {
2992
+ const { email } = await inquirer2.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
2993
+ await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
2994
+ console.log(import_chalk24.default.green(` ${email} added.`));
2995
+ } else {
2996
+ const { e } = await inquirer2.prompt([{
2997
+ type: "list",
2998
+ name: "e",
2999
+ message: "Remove which?",
3000
+ choices: [...emails.map((x) => ({ name: x.email ?? String(x), value: x.email ?? String(x) })), { name: "\u2190 Back", value: null }]
3001
+ }]);
3002
+ if (!e) return;
3003
+ await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
3004
+ console.log(import_chalk24.default.green(` ${e} removed.`));
3005
+ }
3006
+ }
3007
+ async function issuersMenu(base, issuers) {
3008
+ const { default: inquirer2 } = await import("inquirer");
3009
+ console.log();
3010
+ if (!issuers.length) console.log(import_chalk24.default.dim(" No external issuers \u2014 consumers use APIblaze-issued tokens."));
3011
+ for (const i of issuers) console.log(` ${import_chalk24.default.bold(i.iss)} aud=${i.aud} ${import_chalk24.default.dim(i.sub_semantics ?? "")}`);
3012
+ const { act } = await inquirer2.prompt([{
3013
+ type: "list",
3014
+ name: "act",
3015
+ message: "External JWT issuers:",
3016
+ choices: [
3017
+ { name: "Add / replace one issuer", value: "add" },
3018
+ ...issuers.length ? [{ name: "Delete an issuer", value: "rm" }] : [],
3019
+ { name: "\u2190 Back", value: "back" }
3020
+ ]
3021
+ }]);
3022
+ if (act === "back") return;
3023
+ if (act === "add") {
3024
+ const a = await inquirer2.prompt([
3025
+ { type: "input", name: "iss", message: "Issuer URL (iss):", validate: (s) => !!s.trim() || "required" },
3026
+ { type: "input", name: "aud", message: "Audience (aud):", validate: (s) => !!s.trim() || "required" },
3027
+ { type: "input", name: "jwks", message: "JWKS URL (empty = derive from issuer):" },
3028
+ { type: "list", name: "sem", message: "Where is the end-user id?", choices: [
3029
+ { name: "The token sub IS the end user (tenant-owned)", value: "tenant_owned" },
3030
+ { name: "Extract it from a claim\u2026", value: "extract_from_claim" }
3031
+ ] }
3032
+ ]);
3033
+ const claim = a.sem === "extract_from_claim" ? (await inquirer2.prompt([{ type: "input", name: "c", message: "Claim name:", validate: (s) => !!s.trim() || "required" }])).c : void 0;
3034
+ await admin({
3035
+ method: "POST",
3036
+ path: `${base}/external-issuers`,
3037
+ body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
3038
+ summary: `Add external issuer ${a.iss.trim()}`
3039
+ });
3040
+ console.log(import_chalk24.default.green(" Issuer saved."));
3041
+ } else {
3042
+ const { i } = await inquirer2.prompt([{
3043
+ type: "list",
3044
+ name: "i",
3045
+ message: "Delete which issuer?",
3046
+ choices: [...issuers.map((x) => ({ name: `${x.iss} (aud=${x.aud})`, value: x })), { name: "\u2190 Back", value: null }]
3047
+ }]);
3048
+ if (!i) return;
3049
+ await admin({
3050
+ method: "DELETE",
3051
+ path: `${base}/external-issuers?iss=${encodeURIComponent(i.iss)}&aud=${encodeURIComponent(i.aud)}`,
3052
+ summary: `Delete issuer ${i.iss}`
3053
+ });
3054
+ console.log(import_chalk24.default.green(" Issuer deleted."));
3055
+ }
3056
+ }
3057
+ async function clientsMenu(teamId, tenant2, base) {
3058
+ const { default: inquirer2 } = await import("inquirer");
3059
+ for (; ; ) {
3060
+ const spinner = (0, import_ora10.default)("Loading app clients...").start();
3061
+ const raw = await admin({ method: "GET", path: `${base}/app-clients`, summary: "List app clients" }).catch(() => []);
3062
+ spinner.stop();
3063
+ const clients = Array.isArray(raw) ? raw : [];
3064
+ const { pick: pick2 } = await inquirer2.prompt([{
3065
+ type: "list",
3066
+ name: "pick",
3067
+ message: `App clients of ${tenant2}:`,
3068
+ pageSize: 15,
3069
+ choices: [
3070
+ ...clients.map((c) => ({
3071
+ name: `${import_chalk24.default.bold(c.name ?? c.clientId)} ${import_chalk24.default.dim(`${c.clientId}${c.projectName ? ` \xB7 ${c.projectName}` : ""}`)}`,
3072
+ value: c
3073
+ })),
3074
+ ...clients.length ? [] : [new inquirer2.Separator(import_chalk24.default.dim(" no app clients yet"))],
3075
+ { name: "\uFF0B Create an app client\u2026", value: " create" },
3076
+ { name: "\u2190 Back", value: " back" }
3077
+ ]
3078
+ }]);
3079
+ if (pick2 === " back") return;
3080
+ if (pick2 === " create") {
3081
+ const projects = await getProjects(teamId).catch(() => []);
3082
+ if (!projects.length) {
3083
+ console.log(import_chalk24.default.yellow(" No projects in this team \u2014 create a proxy first."));
3084
+ continue;
3085
+ }
3086
+ const a = await inquirer2.prompt([
3087
+ { type: "input", name: "name", message: "Client name:", validate: (s) => !!s.trim() || "required" },
3088
+ { type: "list", name: "proj", message: "For which project?", choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk24.default.dim("v" + p.apiVersion)}`, value: p })) },
3089
+ { type: "input", name: "callbacks", message: "Callback URLs (comma-separated, empty = none):" }
3090
+ ]);
3091
+ const created = await admin({
3092
+ method: "POST",
3093
+ path: `${base}/app-clients`,
3094
+ body: {
3095
+ name: a.name.trim(),
3096
+ projectName: a.proj.projectName,
3097
+ apiVersion: a.proj.apiVersion,
3098
+ ...a.callbacks.trim() ? { authorizedCallbackUrls: parseList(a.callbacks) } : {}
3099
+ },
3100
+ summary: `Create app client "${a.name.trim()}"`
3101
+ });
3102
+ console.log(import_chalk24.default.green(` App client created${created?.clientId ? ` (${created.clientId})` : ""}.`));
3103
+ continue;
3104
+ }
3105
+ await clientHome(base, pick2);
3106
+ }
3107
+ }
3108
+ async function clientHome(base, summary) {
3109
+ const { default: inquirer2 } = await import("inquirer");
3110
+ const id = summary.clientId ?? summary.client_id;
3111
+ const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
3112
+ for (; ; ) {
3113
+ const spinner = (0, import_ora10.default)("Reading app client...").start();
3114
+ const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
3115
+ spinner.stop();
3116
+ const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
3117
+ const scopes = c.scopes ?? [];
3118
+ const nProviders = (c.providers ?? []).length;
3119
+ const { pick: pick2 } = await inquirer2.prompt([{
3120
+ type: "list",
3121
+ name: "pick",
3122
+ message: `${c.name ?? id}:`,
3123
+ pageSize: 12,
3124
+ choices: [
3125
+ { name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk24.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
3126
+ { name: `Callback URLs: ${cb.length ? import_chalk24.default.cyan(cb.join(", ")) : import_chalk24.default.dim("(none)")}`, value: "callbacks" },
3127
+ { name: `Scopes: ${scopes.length ? import_chalk24.default.cyan(scopes.join(" ")) : import_chalk24.default.dim("(defaults)")}`, value: "scopes" },
3128
+ { name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
3129
+ { name: "Reveal client secret", value: "secret" },
3130
+ { name: "Rotate client secret", value: "rotate" },
3131
+ { name: import_chalk24.default.red("Delete this app client"), value: "delete" },
3132
+ { name: "\u2190 Back", value: "back" }
3133
+ ]
3134
+ }]);
3135
+ switch (pick2) {
3136
+ case "back":
3137
+ return;
3138
+ case "providers":
3139
+ await providersMenu(cBase, c.name ?? id);
3140
+ break;
3141
+ case "callbacks": {
3142
+ const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
3143
+ await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
3144
+ console.log(import_chalk24.default.green(" Callbacks updated."));
3145
+ break;
3146
+ }
3147
+ case "scopes": {
3148
+ const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
3149
+ await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
3150
+ console.log(import_chalk24.default.green(" Scopes updated."));
3151
+ break;
3152
+ }
3153
+ case "expiries": {
3154
+ const a = await inquirer2.prompt([
3155
+ { type: "input", name: "access", message: "Access token expiry (seconds):", default: String(c.accessTokenExpiry ?? 3600) },
3156
+ { type: "input", name: "id", message: "ID token expiry (seconds):", default: String(c.idTokenExpiry ?? 3600) },
3157
+ { type: "input", name: "refresh", message: "Refresh token expiry (seconds):", default: String(c.refreshTokenExpiry ?? 2592e3) }
3158
+ ]);
3159
+ await admin({
3160
+ method: "PATCH",
3161
+ path: cBase,
3162
+ body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
3163
+ summary: "Update token expiries"
3164
+ });
3165
+ console.log(import_chalk24.default.green(" Expiries updated."));
3166
+ break;
3167
+ }
3168
+ case "secret": {
3169
+ const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
3170
+ if (!sure) break;
3171
+ const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
3172
+ console.log(` ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3173
+ break;
3174
+ }
3175
+ case "rotate": {
3176
+ const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: "Rotate the secret? Existing integrations using it will break.", default: false }]);
3177
+ if (!sure) break;
3178
+ const fresh = randomSecret();
3179
+ await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
3180
+ console.log(` New ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(fresh)} ${import_chalk24.default.dim("(store it now)")}`);
3181
+ break;
3182
+ }
3183
+ case "delete": {
3184
+ const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: `Delete app client "${c.name ?? id}"? Consumers logged in through it will lose access.`, default: false }]);
3185
+ if (!sure) break;
3186
+ await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
3187
+ console.log(import_chalk24.default.green(" App client deleted."));
3188
+ return;
3189
+ }
3190
+ }
3191
+ }
3192
+ }
3193
+ function randomSecret() {
3194
+ const bytes = new Uint8Array(24);
3195
+ (0, import_crypto.randomFillSync)(bytes);
3196
+ return Buffer.from(bytes).toString("base64url");
3197
+ }
3198
+ var PROVIDER_TYPES = ["google", "github", "microsoft", "facebook", "auth0", "other"];
3199
+ var DEFAULT_SCOPES = {
3200
+ google: "openid email profile",
3201
+ microsoft: "openid email profile",
3202
+ github: "read:user user:email",
3203
+ facebook: "public_profile email"
3204
+ };
3205
+ async function providersMenu(cBase, clientLabel) {
3206
+ const { default: inquirer2 } = await import("inquirer");
3207
+ for (; ; ) {
3208
+ const spinner = (0, import_ora10.default)("Loading providers...").start();
3209
+ const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
3210
+ spinner.stop();
3211
+ const providers = Array.isArray(raw) ? raw : [];
3212
+ console.log();
3213
+ for (const p of providers) {
3214
+ console.log(` ${import_chalk24.default.bold(p.type)} ${import_chalk24.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
3215
+ }
3216
+ if (!providers.length) console.log(import_chalk24.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
3217
+ const { act } = await inquirer2.prompt([{
3218
+ type: "list",
3219
+ name: "act",
3220
+ message: `Login providers of ${clientLabel}:`,
3221
+ choices: [
3222
+ { name: "\uFF0B Add a provider", value: "add" },
3223
+ ...providers.length ? [
3224
+ { name: "Reveal a provider secret", value: "secret" },
3225
+ { name: "Remove a provider", value: "rm" }
3226
+ ] : [],
3227
+ { name: "\u2190 Back", value: "back" }
3228
+ ]
3229
+ }]);
3230
+ if (act === "back") return;
3231
+ if (act === "add") {
3232
+ const { type } = await inquirer2.prompt([{ type: "list", name: "type", message: "Provider:", choices: PROVIDER_TYPES }]);
3233
+ let body;
3234
+ if (type === "github") {
3235
+ const { managed } = await inquirer2.prompt([{
3236
+ type: "confirm",
3237
+ name: "managed",
3238
+ default: true,
3239
+ message: "Use the APIblaze-managed GitHub app (no credentials needed)?"
3240
+ }]);
3241
+ if (managed) body = { type, managed: true };
3242
+ }
3243
+ if (!body) {
3244
+ const a = await inquirer2.prompt([
3245
+ { type: "input", name: "clientId", message: `${type} OAuth client id:`, validate: (s) => !!s.trim() || "required" },
3246
+ { type: "password", name: "clientSecret", mask: "*", message: `${type} OAuth client secret:`, validate: (s) => s.length >= 6 && s.length <= 200 || "6\u2013200 chars" },
3247
+ ...type === "auth0" || type === "other" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : [],
3248
+ { type: "input", name: "scopes", message: "Scopes:", default: DEFAULT_SCOPES[type] ?? "" }
3249
+ ]);
3250
+ body = {
3251
+ type,
3252
+ clientId: a.clientId.trim(),
3253
+ clientSecret: a.clientSecret,
3254
+ ...a.domain?.trim() ? { domain: a.domain.trim() } : {},
3255
+ scopes: String(a.scopes).split(/[\s,]+/).filter(Boolean)
3256
+ };
3257
+ }
3258
+ const { routing } = await inquirer2.prompt([{
3259
+ type: "list",
3260
+ name: "routing",
3261
+ message: "What does your upstream receive?",
3262
+ choices: [
3263
+ { name: "APIblaze token (recommended \u2014 provider stays an identity source)", value: null },
3264
+ { name: `The ${type} access token`, value: "third_party_access_token" },
3265
+ { name: `The ${type} id token`, value: "third_party_id_token" },
3266
+ { name: "Nothing (strip auth)", value: "none" }
3267
+ ]
3268
+ }]);
3269
+ if (routing) {
3270
+ body.tokenType = "thirdParty";
3271
+ body.targetServerToken = routing;
3272
+ }
3273
+ await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
3274
+ console.log(import_chalk24.default.green(` ${type} provider added.`));
3275
+ } else {
3276
+ const { p } = await inquirer2.prompt([{
3277
+ type: "list",
3278
+ name: "p",
3279
+ message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
3280
+ choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk24.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
3281
+ }]);
3282
+ if (!p) continue;
3283
+ if (act === "rm") {
3284
+ const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: `Remove the ${p.type} provider? Consumers using it can no longer sign in.`, default: false }]);
3285
+ if (!sure) continue;
3286
+ await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
3287
+ console.log(import_chalk24.default.green(` ${p.type} removed.`));
3288
+ } else {
3289
+ const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
3290
+ console.log(` ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3291
+ }
3292
+ }
3293
+ }
3294
+ }
3295
+
2705
3296
  // src/commands/spec.ts
2706
3297
  var fs6 = __toESM(require("fs"));
2707
- var import_chalk23 = __toESM(require("chalk"));
2708
- var import_ora9 = __toESM(require("ora"));
3298
+ var import_chalk25 = __toESM(require("chalk"));
3299
+ var import_ora11 = __toESM(require("ora"));
3300
+ init_admin();
2709
3301
  async function runSpecGet(project, opts) {
2710
3302
  const { teamId } = await resolveTeam(opts.team);
2711
3303
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
@@ -2718,19 +3310,19 @@ async function runSpecGet(project, opts) {
2718
3310
  }
2719
3311
  async function runSpecSet(project, opts) {
2720
3312
  if (!opts.file) {
2721
- console.error(import_chalk23.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
3313
+ console.error(import_chalk25.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
2722
3314
  process.exit(1);
2723
3315
  }
2724
3316
  let specContent;
2725
3317
  try {
2726
3318
  specContent = fs6.readFileSync(opts.file, "utf-8");
2727
3319
  } catch {
2728
- console.error(import_chalk23.default.red(`Cannot read file: ${opts.file}`));
3320
+ console.error(import_chalk25.default.red(`Cannot read file: ${opts.file}`));
2729
3321
  process.exit(1);
2730
3322
  }
2731
3323
  const { teamId } = await resolveTeam(opts.team);
2732
3324
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
2733
- const spinner = (0, import_ora9.default)("Uploading spec...").start();
3325
+ const spinner = (0, import_ora11.default)("Uploading spec...").start();
2734
3326
  try {
2735
3327
  const out = await admin({
2736
3328
  method: "POST",
@@ -2747,12 +3339,13 @@ async function runSpecSet(project, opts) {
2747
3339
  }
2748
3340
 
2749
3341
  // src/commands/agent.ts
2750
- var import_chalk25 = __toESM(require("chalk"));
2751
- var import_ora10 = __toESM(require("ora"));
3342
+ var import_chalk27 = __toESM(require("chalk"));
3343
+ var import_ora12 = __toESM(require("ora"));
2752
3344
  init_auth();
2753
3345
 
2754
3346
  // src/lib/tools.ts
2755
- var import_chalk24 = __toESM(require("chalk"));
3347
+ var import_chalk26 = __toESM(require("chalk"));
3348
+ init_admin();
2756
3349
  init_api();
2757
3350
  async function proj(teamId, name, version2) {
2758
3351
  return resolveProject(teamId, name, version2);
@@ -2770,15 +3363,15 @@ var TOOLS = [
2770
3363
  const key = keys.dev ?? Object.values(keys)[0];
2771
3364
  const url = `https://${a.name}.abz.run/${version2}/dev`;
2772
3365
  const tryIt = buildTryItCurl(url, auth, key);
2773
- const lines = [` ${import_chalk24.default.dim("Proxy URL:")} ${import_chalk24.default.bold(url)}`];
2774
- if (res.devPortal) lines.push(` ${import_chalk24.default.dim("Dev portal:")} ${res.devPortal}`);
3366
+ const lines = [` ${import_chalk26.default.dim("Proxy URL:")} ${import_chalk26.default.bold(url)}`];
3367
+ if (res.devPortal) lines.push(` ${import_chalk26.default.dim("Dev portal:")} ${res.devPortal}`);
2775
3368
  const envs = Object.keys(keys);
2776
3369
  if (envs.length) {
2777
- lines.push("", ` ${import_chalk24.default.bold("API keys")} ${import_chalk24.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
3370
+ lines.push("", ` ${import_chalk26.default.bold("API keys")} ${import_chalk26.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
2778
3371
  const w = Math.max(...envs.map((e) => e.length));
2779
- for (const env of envs) lines.push(` ${import_chalk24.default.cyan(env.padEnd(w))} ${import_chalk24.default.green(keys[env])}`);
3372
+ for (const env of envs) lines.push(` ${import_chalk26.default.cyan(env.padEnd(w))} ${import_chalk26.default.green(keys[env])}`);
2780
3373
  }
2781
- if (tryIt) lines.push("", ` ${import_chalk24.default.dim("Try it:")}`, ` ${import_chalk24.default.cyan(tryIt)}`);
3374
+ if (tryIt) lines.push("", ` ${import_chalk26.default.dim("Try it:")}`, ` ${import_chalk26.default.cyan(tryIt)}`);
2782
3375
  return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
2783
3376
  }
2784
3377
  },
@@ -2903,6 +3496,7 @@ function findTool(name) {
2903
3496
  }
2904
3497
 
2905
3498
  // src/commands/agent.ts
3499
+ init_trace();
2906
3500
  init_types();
2907
3501
  var DASHBOARD_BASE5 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
2908
3502
  var MAX_TOOL_STEPS = 6;
@@ -2933,23 +3527,23 @@ function truncate(value, max = 1500) {
2933
3527
  }
2934
3528
  function printCost(llm) {
2935
3529
  const usd = llm.cost > 0 ? `$${llm.cost.toFixed(4)}` : "<$0.0001";
2936
- console.log(import_chalk25.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk25.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
3530
+ console.log(import_chalk27.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk27.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
2937
3531
  }
2938
3532
  async function runAgent(opts) {
2939
3533
  requireAuth();
2940
3534
  const { teamId, teamName } = await resolveTeam(opts.team);
2941
3535
  const { default: inquirer2 } = await import("inquirer");
2942
- console.log(import_chalk25.default.bold("APIblaze agent") + import_chalk25.default.dim(` \xB7 team ${teamName ?? teamId}`));
2943
- console.log(import_chalk25.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
3536
+ console.log(import_chalk27.default.bold("APIblaze agent") + import_chalk27.default.dim(` \xB7 team ${teamName ?? teamId}`));
3537
+ console.log(import_chalk27.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
2944
3538
  const history = [];
2945
3539
  while (true) {
2946
- const { input } = await inquirer2.prompt([{ type: "input", name: "input", message: import_chalk25.default.cyan("you") + " \u203A" }]);
3540
+ const { input } = await inquirer2.prompt([{ type: "input", name: "input", message: import_chalk27.default.cyan("you") + " \u203A" }]);
2947
3541
  const text = (input ?? "").trim();
2948
3542
  if (!text) continue;
2949
3543
  if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
2950
3544
  history.push({ role: "user", content: text });
2951
3545
  for (let step = 0; step < MAX_TOOL_STEPS; step++) {
2952
- const spinner = (0, import_ora10.default)({ text: "thinking...", color: "magenta" }).start();
3546
+ const spinner = (0, import_ora12.default)({ text: "thinking...", color: "magenta" }).start();
2953
3547
  let resp;
2954
3548
  try {
2955
3549
  resp = await callAgent(history, teamId);
@@ -2957,21 +3551,21 @@ async function runAgent(opts) {
2957
3551
  } catch (err) {
2958
3552
  spinner.stop();
2959
3553
  if (err instanceof ApiError && err.status === 402) {
2960
- console.log(import_chalk25.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
3554
+ console.log(import_chalk27.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
2961
3555
  break;
2962
3556
  }
2963
3557
  throw err;
2964
3558
  }
2965
3559
  history.push({ role: "assistant", content: resp.raw });
2966
3560
  printCost(resp.llm);
2967
- if (resp.reply) console.log(import_chalk25.default.green("agent") + " \u203A " + resp.reply);
3561
+ if (resp.reply) console.log(import_chalk27.default.green("agent") + " \u203A " + resp.reply);
2968
3562
  if (!resp.action) break;
2969
3563
  const tool = findTool(resp.action.tool);
2970
3564
  if (!tool) {
2971
3565
  history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
2972
3566
  continue;
2973
3567
  }
2974
- const runSpinner = (0, import_ora10.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
3568
+ const runSpinner = (0, import_ora12.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
2975
3569
  try {
2976
3570
  const result = await tool.run(resp.action.args, { teamId });
2977
3571
  runSpinner.succeed(`${tool.name} \u2713`);
@@ -2989,11 +3583,11 @@ async function runAgent(opts) {
2989
3583
  }
2990
3584
  renderTrace();
2991
3585
  if (step === MAX_TOOL_STEPS - 1) {
2992
- console.log(import_chalk25.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
3586
+ console.log(import_chalk27.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
2993
3587
  }
2994
3588
  }
2995
3589
  }
2996
- console.log(import_chalk25.default.dim("\nBye."));
3590
+ console.log(import_chalk27.default.dim("\nBye."));
2997
3591
  }
2998
3592
 
2999
3593
  // src/commands/config-browse.ts
@@ -3140,7 +3734,7 @@ var SETTING_GROUPS = ["Basics", "Traffic & limits", "Access & auth", "Portal & M
3140
3734
  var FEATURES = [
3141
3735
  { go: "transforms", label: "Transforms", desc: "Rewrite requests/responses (headers, body fields) without touching your upstream" },
3142
3736
  { go: "mappings", label: "Mapping tables", desc: "Reusable value-mapping tables used by map transforms (values can be hidden/encrypted)" },
3143
- { go: "tenants", label: "Tenants", desc: "Separate groups of your API consumers \u2014 each with its own portal, login, and keys" },
3737
+ { go: "tenants", label: "Tenants", desc: "Consumer groups \u2014 portal, login app clients & providers, issuers, IAM, CORS" },
3144
3738
  { go: "domains", label: "Custom domains", desc: "Serve the proxy on your own hostname + choose what the bare URL serves" },
3145
3739
  { go: "spec", label: "OpenAPI spec & traffic", desc: "View the stored spec, refresh it from source, or build it from captured traffic" },
3146
3740
  { go: "agents", label: "AI agents", desc: "Chat to build your spec, design access rules, or publish an MCP server (billed per turn)" },
@@ -3156,11 +3750,11 @@ function dig(blob, dotted) {
3156
3750
  }
3157
3751
  var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
3158
3752
  function show(v) {
3159
- if (v === void 0) return import_chalk26.default.dim("(unset)");
3160
- if (v === null) return import_chalk26.default.dim("null");
3161
- if (typeof v === "object") return import_chalk26.default.cyan(JSON.stringify(v));
3162
- if (typeof v === "boolean") return v ? import_chalk26.default.green("on") : import_chalk26.default.red("off");
3163
- return import_chalk26.default.cyan(String(v));
3753
+ if (v === void 0) return import_chalk28.default.dim("(unset)");
3754
+ if (v === null) return import_chalk28.default.dim("null");
3755
+ if (typeof v === "object") return import_chalk28.default.cyan(JSON.stringify(v));
3756
+ if (typeof v === "boolean") return v ? import_chalk28.default.green("on") : import_chalk28.default.red("off");
3757
+ return import_chalk28.default.cyan(String(v));
3164
3758
  }
3165
3759
  function parseValue(raw) {
3166
3760
  if (raw === "true") return true;
@@ -3187,7 +3781,7 @@ async function fetchConfigBlob(proj2) {
3187
3781
  }
3188
3782
  async function patchSetting(proj2, s, value, cfg) {
3189
3783
  const body = s.toPatch(value, cfg);
3190
- const spinner = (0, import_ora11.default)(`Set ${s.key}...`).start();
3784
+ const spinner = (0, import_ora13.default)(`Set ${s.key}...`).start();
3191
3785
  try {
3192
3786
  await admin({
3193
3787
  method: "PATCH",
@@ -3202,10 +3796,10 @@ async function patchSetting(proj2, s, value, cfg) {
3202
3796
  }
3203
3797
  }
3204
3798
  var loginFirst = (what) => {
3205
- console.log(import_chalk26.default.yellow(`
3799
+ console.log(import_chalk28.default.yellow(`
3206
3800
  Log in first to ${what}.`));
3207
- console.log(import_chalk26.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
3208
- console.log(import_chalk26.default.dim(" anonymously and want to bring it into your account.\n"));
3801
+ console.log(import_chalk28.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
3802
+ console.log(import_chalk28.default.dim(" anonymously and want to bring it into your account.\n"));
3209
3803
  };
3210
3804
  async function runConfig(project, key, value, opts) {
3211
3805
  const creds = loadCredentials();
@@ -3222,9 +3816,9 @@ async function runConfig(project, key, value, opts) {
3222
3816
  }
3223
3817
  const setting = SETTINGS.find((s) => s.key === key);
3224
3818
  if (!setting) {
3225
- console.error(import_chalk26.default.red(`Unknown setting "${key}".`));
3226
- console.error(import_chalk26.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
3227
- console.error(import_chalk26.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
3819
+ console.error(import_chalk28.default.red(`Unknown setting "${key}".`));
3820
+ console.error(import_chalk28.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
3821
+ console.error(import_chalk28.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
3228
3822
  process.exit(1);
3229
3823
  }
3230
3824
  if (value === void 0) {
@@ -3239,7 +3833,7 @@ async function pickProject(teamId) {
3239
3833
  const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
3240
3834
  const projects = await getProjects2(teamId).catch(() => []);
3241
3835
  if (!projects.length) {
3242
- console.error(import_chalk26.default.red("No projects in this team. Create one: `npx apiblaze create`."));
3836
+ console.error(import_chalk28.default.red("No projects in this team. Create one: `npx apiblaze create`."));
3243
3837
  process.exit(1);
3244
3838
  }
3245
3839
  const { default: inquirer2 } = await import("inquirer");
@@ -3247,7 +3841,7 @@ async function pickProject(teamId) {
3247
3841
  type: "list",
3248
3842
  name: "picked",
3249
3843
  message: "Which project?",
3250
- choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk26.default.dim("v" + p.apiVersion)}`, value: p }))
3844
+ choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk28.default.dim("v" + p.apiVersion)}`, value: p }))
3251
3845
  }]);
3252
3846
  return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
3253
3847
  }
@@ -3258,25 +3852,25 @@ function printAll(proj2, cfg, json) {
3258
3852
  console.log(JSON.stringify(out, null, 2));
3259
3853
  return;
3260
3854
  }
3261
- console.log(import_chalk26.default.bold(`
3855
+ console.log(import_chalk28.default.bold(`
3262
3856
  ${proj2.projectName} v${proj2.apiVersion} \u2014 settings
3263
3857
  `));
3264
3858
  for (const group of SETTING_GROUPS) {
3265
- console.log(import_chalk26.default.bold(group));
3859
+ console.log(import_chalk28.default.bold(group));
3266
3860
  for (const s of SETTINGS.filter((x) => x.group === group)) {
3267
- console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk26.default.dim(s.desc)}`);
3861
+ console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk28.default.dim(s.desc)}`);
3268
3862
  }
3269
3863
  console.log();
3270
3864
  }
3271
- console.log(import_chalk26.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
3865
+ console.log(import_chalk28.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
3272
3866
  }
3273
3867
  async function discoveryMenu(project) {
3274
3868
  const { default: inquirer2 } = await import("inquirer");
3275
- console.log(import_chalk26.default.bold(`
3869
+ console.log(import_chalk28.default.bold(`
3276
3870
  APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
3277
3871
  `));
3278
- console.log(import_chalk26.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
3279
- console.log(import_chalk26.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
3872
+ console.log(import_chalk28.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
3873
+ console.log(import_chalk28.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
3280
3874
  for (; ; ) {
3281
3875
  const { pick: pick2 } = await inquirer2.prompt([{
3282
3876
  type: "list",
@@ -3284,13 +3878,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
3284
3878
  message: "Explore:",
3285
3879
  pageSize: 20,
3286
3880
  choices: [
3287
- new inquirer2.Separator(import_chalk26.default.bold("\u2014 Settings \u2014")),
3881
+ new inquirer2.Separator(import_chalk28.default.bold("\u2014 Settings \u2014")),
3288
3882
  ...SETTING_GROUPS.map((g) => ({
3289
- name: `${g} ${import_chalk26.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
3883
+ name: `${g} ${import_chalk28.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
3290
3884
  value: { kind: "settings", g }
3291
3885
  })),
3292
- new inquirer2.Separator(import_chalk26.default.bold("\u2014 Features \u2014")),
3293
- ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk26.default.dim(f.desc)}`, value: { kind: "feature", f } })),
3886
+ new inquirer2.Separator(import_chalk28.default.bold("\u2014 Features \u2014")),
3887
+ ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk28.default.dim(f.desc)}`, value: { kind: "feature", f } })),
3294
3888
  new inquirer2.Separator(),
3295
3889
  { name: "Exit", value: { kind: "exit" } }
3296
3890
  ]
@@ -3299,24 +3893,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
3299
3893
  if (pick2.kind === "settings") {
3300
3894
  console.log();
3301
3895
  for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
3302
- console.log(` ${import_chalk26.default.bold(s.label.padEnd(28))} ${import_chalk26.default.dim(s.desc)}`);
3303
- console.log(` ${import_chalk26.default.dim(" key: " + s.key)}`);
3896
+ console.log(` ${import_chalk28.default.bold(s.label.padEnd(28))} ${import_chalk28.default.dim(s.desc)}`);
3897
+ console.log(` ${import_chalk28.default.dim(" key: " + s.key)}`);
3304
3898
  }
3305
3899
  loginFirst("view or change these settings");
3306
3900
  } else {
3307
3901
  const f = pick2.f;
3308
3902
  console.log(`
3309
- ${import_chalk26.default.bold(f.label)} \u2014 ${f.desc}`);
3903
+ ${import_chalk28.default.bold(f.label)} \u2014 ${f.desc}`);
3310
3904
  loginFirst(`use ${f.label.toLowerCase()}`);
3311
3905
  }
3312
3906
  }
3313
3907
  }
3314
3908
  async function navigator(proj2, cfg, opts) {
3315
3909
  const { default: inquirer2 } = await import("inquirer");
3316
- console.log(import_chalk26.default.bold(`
3910
+ console.log(import_chalk28.default.bold(`
3317
3911
  ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
3318
3912
  `));
3319
- console.log(import_chalk26.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
3913
+ console.log(import_chalk28.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
3320
3914
  let blob = cfg;
3321
3915
  for (; ; ) {
3322
3916
  const { pick: pick2 } = await inquirer2.prompt([{
@@ -3325,10 +3919,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
3325
3919
  message: "Where to?",
3326
3920
  pageSize: 20,
3327
3921
  choices: [
3328
- new inquirer2.Separator(import_chalk26.default.bold("\u2014 Settings \u2014")),
3922
+ new inquirer2.Separator(import_chalk28.default.bold("\u2014 Settings \u2014")),
3329
3923
  ...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
3330
- new inquirer2.Separator(import_chalk26.default.bold("\u2014 Features \u2014")),
3331
- ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk26.default.dim(f.desc)}`, value: { kind: f.go } })),
3924
+ new inquirer2.Separator(import_chalk28.default.bold("\u2014 Features \u2014")),
3925
+ ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk28.default.dim(f.desc)}`, value: { kind: f.go } })),
3332
3926
  new inquirer2.Separator(),
3333
3927
  { name: "Show all settings", value: { kind: "list" } },
3334
3928
  { name: "Exit", value: { kind: "exit" } }
@@ -3368,7 +3962,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
3368
3962
  }
3369
3963
  }
3370
3964
  } catch (err) {
3371
- console.error(import_chalk26.default.red(` ${err instanceof Error ? err.message : String(err)}`));
3965
+ console.error(import_chalk28.default.red(` ${err instanceof Error ? err.message : String(err)}`));
3372
3966
  }
3373
3967
  }
3374
3968
  }
@@ -3382,7 +3976,7 @@ async function settingsGroup(proj2, cfg, group) {
3382
3976
  message: group + ":",
3383
3977
  pageSize: 16,
3384
3978
  choices: [
3385
- ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk26.default.dim(s2.desc)}`, value: s2 })),
3979
+ ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk28.default.dim(s2.desc)}`, value: s2 })),
3386
3980
  new inquirer2.Separator(),
3387
3981
  { name: "\u2190 Back", value: null }
3388
3982
  ]
@@ -3400,7 +3994,7 @@ async function settingsGroup(proj2, cfg, group) {
3400
3994
  } else if (s.type === "number") {
3401
3995
  const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
3402
3996
  if (v === "" || Number.isNaN(Number(v))) {
3403
- console.log(import_chalk26.default.yellow(" Not a number \u2014 unchanged."));
3997
+ console.log(import_chalk28.default.yellow(" Not a number \u2014 unchanged."));
3404
3998
  continue;
3405
3999
  }
3406
4000
  value = Number(v);
@@ -3481,7 +4075,7 @@ async function buildCondition(phase) {
3481
4075
  const items = [];
3482
4076
  for (; ; ) {
3483
4077
  const a = await inquirer2.prompt([
3484
- { type: "input", name: "source", message: `Condition field ${import_chalk26.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
4078
+ { type: "input", name: "source", message: `Condition field ${import_chalk28.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
3485
4079
  { type: "list", name: "operator", message: "Operator:", choices: [
3486
4080
  "eq",
3487
4081
  "neq",
@@ -3512,7 +4106,7 @@ async function buildCondition(phase) {
3512
4106
  function showCondition(cond) {
3513
4107
  if (!Array.isArray(cond) || !cond.length) return "";
3514
4108
  const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
3515
- return import_chalk26.default.dim(` when ${s}`);
4109
+ return import_chalk28.default.dim(` when ${s}`);
3516
4110
  }
3517
4111
  async function transformsMenu(proj2) {
3518
4112
  const { default: inquirer2 } = await import("inquirer");
@@ -3521,12 +4115,12 @@ async function transformsMenu(proj2) {
3521
4115
  const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
3522
4116
  const rules = out?.rules ?? [];
3523
4117
  console.log();
3524
- if (!rules.length) console.log(import_chalk26.default.dim(" No transform rules yet."));
4118
+ if (!rules.length) console.log(import_chalk28.default.dim(" No transform rules yet."));
3525
4119
  for (const r of rules) {
3526
4120
  const a = r.action ?? {};
3527
4121
  const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
3528
- const what = a.type === "hardcode" ? `${a.destination} = "${a.value}"` : a.type === "remove" ? `remove ${a.field}` : `${a.source} \u2192 ${a.destination}${a.lookup ? " (mapped)" : ""}${fns.length ? import_chalk26.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
3529
- console.log(` ${r.enabled ? import_chalk26.default.green("\u25CF") : import_chalk26.default.dim("\u25CB")} ${import_chalk26.default.bold(r.name)} ${import_chalk26.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
4122
+ const what = a.type === "hardcode" ? `${a.destination} = "${a.value}"` : a.type === "remove" ? `remove ${a.field}` : `${a.source} \u2192 ${a.destination}${a.lookup ? " (mapped)" : ""}${fns.length ? import_chalk28.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
4123
+ console.log(` ${r.enabled ? import_chalk28.default.green("\u25CF") : import_chalk28.default.dim("\u25CB")} ${import_chalk28.default.bold(r.name)} ${import_chalk28.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
3530
4124
  }
3531
4125
  const { act } = await inquirer2.prompt([{
3532
4126
  type: "list",
@@ -3538,7 +4132,7 @@ async function transformsMenu(proj2) {
3538
4132
  { name: "Enable/disable a rule", value: "toggle" },
3539
4133
  { name: "Delete a rule", value: "delete" }
3540
4134
  ] : [],
3541
- { name: import_chalk26.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
4135
+ { name: import_chalk28.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
3542
4136
  { name: "\u2190 Back", value: "back" }
3543
4137
  ]
3544
4138
  }]);
@@ -3551,11 +4145,11 @@ async function transformsMenu(proj2) {
3551
4145
  }]);
3552
4146
  const body = parseValue(raw);
3553
4147
  if (!body || typeof body !== "object" || Array.isArray(body)) {
3554
- console.log(import_chalk26.default.yellow(" Not a JSON object \u2014 skipped."));
4148
+ console.log(import_chalk28.default.yellow(" Not a JSON object \u2014 skipped."));
3555
4149
  continue;
3556
4150
  }
3557
4151
  await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
3558
- console.log(import_chalk26.default.green(" Rule created."));
4152
+ console.log(import_chalk28.default.green(" Rule created."));
3559
4153
  continue;
3560
4154
  }
3561
4155
  if (act === "add") {
@@ -3571,7 +4165,7 @@ async function transformsMenu(proj2) {
3571
4165
  { name: "Remove a field", value: "remove" }
3572
4166
  ] }
3573
4167
  ]);
3574
- const fieldHint = import_chalk26.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
4168
+ const fieldHint = import_chalk28.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
3575
4169
  let action2;
3576
4170
  if (ans.kind === "hardcode") {
3577
4171
  const a = await inquirer2.prompt([
@@ -3602,7 +4196,7 @@ async function transformsMenu(proj2) {
3602
4196
  };
3603
4197
  }
3604
4198
  const condition = await buildCondition(ans.phase);
3605
- const spinner = (0, import_ora11.default)("Creating rule...").start();
4199
+ const spinner = (0, import_ora13.default)("Creating rule...").start();
3606
4200
  try {
3607
4201
  await admin({
3608
4202
  method: "POST",
@@ -3620,16 +4214,16 @@ async function transformsMenu(proj2) {
3620
4214
  type: "list",
3621
4215
  name: "rule",
3622
4216
  message: act === "toggle" ? "Which rule?" : "Delete which rule?",
3623
- choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk26.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
4217
+ choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk28.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
3624
4218
  }]);
3625
4219
  if (!rule) continue;
3626
4220
  if (act === "toggle") {
3627
4221
  const flipped = { ...rule, enabled: rule.enabled === false };
3628
4222
  await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
3629
- console.log(import_chalk26.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
4223
+ console.log(import_chalk28.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
3630
4224
  } else {
3631
4225
  await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
3632
- console.log(import_chalk26.default.green(` ${rule.name} deleted.`));
4226
+ console.log(import_chalk28.default.green(` ${rule.name} deleted.`));
3633
4227
  }
3634
4228
  }
3635
4229
  }
@@ -3641,9 +4235,9 @@ async function mappingsMenu(proj2) {
3641
4235
  const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
3642
4236
  const tables = out?.mappings ?? out?.tables ?? [];
3643
4237
  console.log();
3644
- if (!tables.length) console.log(import_chalk26.default.dim(" No mapping tables yet."));
4238
+ if (!tables.length) console.log(import_chalk28.default.dim(" No mapping tables yet."));
3645
4239
  for (const t of tables) {
3646
- console.log(` ${import_chalk26.default.bold(t.name)} ${import_chalk26.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
4240
+ console.log(` ${import_chalk28.default.bold(t.name)} ${import_chalk28.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
3647
4241
  }
3648
4242
  const { act } = await inquirer2.prompt([{
3649
4243
  type: "list",
@@ -3663,11 +4257,11 @@ async function mappingsMenu(proj2) {
3663
4257
  ]);
3664
4258
  const entries2 = parseValue(a.entries);
3665
4259
  if (!Array.isArray(entries2)) {
3666
- console.log(import_chalk26.default.yellow(" Entries must be a JSON array \u2014 not created."));
4260
+ console.log(import_chalk28.default.yellow(" Entries must be a JSON array \u2014 not created."));
3667
4261
  continue;
3668
4262
  }
3669
4263
  await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
3670
- console.log(import_chalk26.default.green(` Table "${a.name}" created.`));
4264
+ console.log(import_chalk28.default.green(` Table "${a.name}" created.`));
3671
4265
  } else {
3672
4266
  const { table } = await inquirer2.prompt([{
3673
4267
  type: "list",
@@ -3677,7 +4271,7 @@ async function mappingsMenu(proj2) {
3677
4271
  }]);
3678
4272
  if (!table) continue;
3679
4273
  await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
3680
- console.log(import_chalk26.default.green(` ${table.name} deleted.`));
4274
+ console.log(import_chalk28.default.green(` ${table.name} deleted.`));
3681
4275
  }
3682
4276
  }
3683
4277
  }
@@ -3688,22 +4282,22 @@ async function tenantsMenu(proj2, opts) {
3688
4282
  const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
3689
4283
  const tenants = out?.tenants ?? [];
3690
4284
  console.log();
3691
- if (!tenants.length) console.log(import_chalk26.default.dim(" No tenants attached (consumers use the default tenant)."));
3692
- for (const t of tenants) console.log(` ${import_chalk26.default.bold(t.tenant_name ?? t.name)} ${import_chalk26.default.dim(t.display_name ?? "")}`);
4285
+ if (!tenants.length) console.log(import_chalk28.default.dim(" No tenants attached (consumers use the default tenant)."));
4286
+ for (const t of tenants) console.log(` ${import_chalk28.default.bold(t.tenant_name ?? t.name)} ${import_chalk28.default.dim(t.display_name ?? "")}`);
3693
4287
  const { act } = await inquirer2.prompt([{
3694
4288
  type: "list",
3695
4289
  name: "act",
3696
4290
  message: "Tenants:",
3697
4291
  choices: [
3698
- { name: "Attach a tenant", value: "attach" },
3699
- ...tenants.length ? [{ name: "Detach a tenant", value: "detach" }] : [],
3700
- { name: import_chalk26.default.dim("Manage team tenants (create/CORS/login) \u2192 `apiblaze tenant --help`"), value: "hint" },
4292
+ { name: `Manage a tenant\u2026 ${import_chalk28.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
4293
+ { name: "Attach a tenant to this project", value: "attach" },
4294
+ ...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
3701
4295
  { name: "\u2190 Back", value: "back" }
3702
4296
  ]
3703
4297
  }]);
3704
4298
  if (act === "back") return;
3705
- if (act === "hint") {
3706
- console.log(import_chalk26.default.dim("\n apiblaze tenant list | create | attach | cors | delete\n"));
4299
+ if (act === "manage") {
4300
+ await runTenantManage(void 0, { team: opts.team });
3707
4301
  continue;
3708
4302
  }
3709
4303
  if (act === "attach") {
@@ -3718,7 +4312,7 @@ async function tenantsMenu(proj2, opts) {
3718
4312
  }]);
3719
4313
  if (!t) continue;
3720
4314
  await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
3721
- console.log(import_chalk26.default.green(` Detached ${t.tenant_name ?? t.name}.`));
4315
+ console.log(import_chalk28.default.green(` Detached ${t.tenant_name ?? t.name}.`));
3722
4316
  }
3723
4317
  }
3724
4318
  }
@@ -3761,7 +4355,7 @@ async function specMenu(proj2, opts) {
3761
4355
  choices: [
3762
4356
  { name: "Print the stored spec", value: "get" },
3763
4357
  { name: "Refresh the spec from its source", value: "refresh" },
3764
- { name: import_chalk26.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
4358
+ { name: import_chalk28.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
3765
4359
  { name: "\u2190 Back", value: "back" }
3766
4360
  ]
3767
4361
  }]);
@@ -3769,7 +4363,7 @@ async function specMenu(proj2, opts) {
3769
4363
  if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
3770
4364
  else if (act === "refresh") {
3771
4365
  await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
3772
- console.log(import_chalk26.default.green(" Spec refresh triggered."));
4366
+ console.log(import_chalk28.default.green(" Spec refresh triggered."));
3773
4367
  } else await runOpenapi(proj2.projectName, proj2.apiVersion);
3774
4368
  }
3775
4369
  async function agentsMenu(proj2, opts) {
@@ -3794,8 +4388,9 @@ async function agentsMenu(proj2, opts) {
3794
4388
  }
3795
4389
 
3796
4390
  // src/commands/key.ts
3797
- var import_chalk27 = __toESM(require("chalk"));
3798
- var import_ora12 = __toESM(require("ora"));
4391
+ var import_chalk29 = __toESM(require("chalk"));
4392
+ var import_ora14 = __toESM(require("ora"));
4393
+ init_admin();
3799
4394
  async function runApikeysMenu(opts) {
3800
4395
  await runKeyList(opts);
3801
4396
  if (opts.json) return;
@@ -3822,11 +4417,11 @@ async function runKeyList(opts) {
3822
4417
  return;
3823
4418
  }
3824
4419
  if (!keys.length) {
3825
- console.log(import_chalk27.default.yellow("No developer keys."));
4420
+ console.log(import_chalk29.default.yellow("No developer keys."));
3826
4421
  return;
3827
4422
  }
3828
4423
  for (const k of keys) {
3829
- console.log(` ${import_chalk27.default.bold(k.key_id ?? k.id)} ${import_chalk27.default.dim(k.description ?? "")} ${import_chalk27.default.dim(k.expires_at ?? "no expiry")}`);
4424
+ console.log(` ${import_chalk29.default.bold(k.key_id ?? k.id)} ${import_chalk29.default.dim(k.description ?? "")} ${import_chalk29.default.dim(k.expires_at ?? "no expiry")}`);
3830
4425
  }
3831
4426
  }
3832
4427
  async function runKeyMint(opts) {
@@ -3834,7 +4429,7 @@ async function runKeyMint(opts) {
3834
4429
  const body = { role: "consumer-admin" };
3835
4430
  if (opts.desc) body.description = opts.desc;
3836
4431
  if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
3837
- const spinner = (0, import_ora12.default)("Minting key...").start();
4432
+ const spinner = (0, import_ora14.default)("Minting key...").start();
3838
4433
  try {
3839
4434
  const out = await admin({
3840
4435
  method: "POST",
@@ -3847,9 +4442,9 @@ async function runKeyMint(opts) {
3847
4442
  console.log(JSON.stringify(out));
3848
4443
  return;
3849
4444
  }
3850
- console.log(` ${import_chalk27.default.bold("key_id")}: ${out?.key_id}`);
3851
- console.log(` ${import_chalk27.default.bold("key")}: ${import_chalk27.default.green(out?.key)} ${import_chalk27.default.dim("(shown once \u2014 store it now)")}`);
3852
- if (out?.expires_at) console.log(` ${import_chalk27.default.dim("expires:")} ${out.expires_at}`);
4445
+ console.log(` ${import_chalk29.default.bold("key_id")}: ${out?.key_id}`);
4446
+ console.log(` ${import_chalk29.default.bold("key")}: ${import_chalk29.default.green(out?.key)} ${import_chalk29.default.dim("(shown once \u2014 store it now)")}`);
4447
+ if (out?.expires_at) console.log(` ${import_chalk29.default.dim("expires:")} ${out.expires_at}`);
3853
4448
  } catch (err) {
3854
4449
  spinner.fail("Mint failed.");
3855
4450
  throw err;
@@ -3857,7 +4452,7 @@ async function runKeyMint(opts) {
3857
4452
  }
3858
4453
  async function runKeyRevoke(keyId, opts) {
3859
4454
  const { teamId } = await resolveTeam(opts.team);
3860
- const spinner = (0, import_ora12.default)("Revoking key...").start();
4455
+ const spinner = (0, import_ora14.default)("Revoking key...").start();
3861
4456
  try {
3862
4457
  await admin({
3863
4458
  method: "DELETE",
@@ -3872,8 +4467,9 @@ async function runKeyRevoke(keyId, opts) {
3872
4467
  }
3873
4468
 
3874
4469
  // src/commands/consumer.ts
3875
- var import_chalk28 = __toESM(require("chalk"));
3876
- var import_ora13 = __toESM(require("ora"));
4470
+ var import_chalk30 = __toESM(require("chalk"));
4471
+ var import_ora15 = __toESM(require("ora"));
4472
+ init_admin();
3877
4473
  var DEFAULT_SCOPE = "openid email profile offline_access";
3878
4474
  var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
3879
4475
  async function consumerFetch(creds, suffix, init) {
@@ -3892,7 +4488,7 @@ async function consumerFetch(creds, suffix, init) {
3892
4488
  function requireConsumer() {
3893
4489
  const c = loadConsumer();
3894
4490
  if (!c) {
3895
- console.error(import_chalk28.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
4491
+ console.error(import_chalk30.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
3896
4492
  process.exit(1);
3897
4493
  }
3898
4494
  return c;
@@ -3903,48 +4499,37 @@ async function runConsumerLogin(opts) {
3903
4499
  let clientId = opts.client;
3904
4500
  if (clientId) {
3905
4501
  if (!tenant2) {
3906
- console.error(import_chalk28.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
4502
+ console.error(import_chalk30.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
3907
4503
  process.exit(1);
3908
4504
  }
3909
4505
  } else {
3910
4506
  requireAuth();
3911
4507
  const { teamId, teamName } = await resolveTeam(opts.team);
3912
- const spinner = (0, import_ora13.default)("Loading your tenants...").start();
3913
- const tdata = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants?detail=1`, summary: `List tenants for ${teamName ?? teamId}` });
3914
- spinner.stop();
3915
- const tenants = (tdata?.tenants ?? []).map(
3916
- (t) => typeof t === "string" ? { tenant_name: t } : t
3917
- );
3918
- if (!tenants.length) {
3919
- console.error(import_chalk28.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
3920
- process.exit(1);
3921
- }
3922
4508
  if (!tenant2) {
3923
- if (tenants.length === 1) tenant2 = tenants[0].tenant_name;
3924
- else {
3925
- const { chosen } = await inquirer2.prompt([{ type: "list", name: "chosen", message: "Which tenant portal?", choices: tenants.map((t) => ({ name: t.display_name ? `${t.display_name} (${t.tenant_name})` : t.tenant_name, value: t.tenant_name })) }]);
3926
- tenant2 = chosen;
3927
- }
4509
+ const { pickTenant: pickTenant2 } = await Promise.resolve().then(() => (init_tenant_pick(), tenant_pick_exports));
4510
+ const picked = await pickTenant2(teamId, { message: `Which tenant portal${teamName ? ` (team ${teamName})` : ""}?` });
4511
+ if (!picked) process.exit(1);
4512
+ tenant2 = picked;
3928
4513
  }
3929
- const s2 = (0, import_ora13.default)("Finding the login app...").start();
4514
+ const s2 = (0, import_ora15.default)("Finding the login app...").start();
3930
4515
  const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
3931
4516
  s2.stop();
3932
4517
  const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
3933
4518
  const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
3934
4519
  if (!pick2) {
3935
- console.error(import_chalk28.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
4520
+ console.error(import_chalk30.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
3936
4521
  process.exit(1);
3937
4522
  }
3938
4523
  clientId = pick2.client_id ?? pick2.clientId;
3939
4524
  }
3940
4525
  const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
3941
- console.log(`${import_chalk28.default.cyan("\u2192")} Logging in to ${import_chalk28.default.bold(tenant2)} as a consumer...`);
4526
+ console.log(`${import_chalk30.default.cyan("\u2192")} Logging in to ${import_chalk30.default.bold(tenant2)} as a consumer...`);
3942
4527
  const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
3943
4528
  console.log(`
3944
- Open: ${import_chalk28.default.underline(verificationUri)}`);
3945
- console.log(` Code: ${import_chalk28.default.bold(userCode)}
4529
+ Open: ${import_chalk30.default.underline(verificationUri)}`);
4530
+ console.log(` Code: ${import_chalk30.default.bold(userCode)}
3946
4531
  `);
3947
- console.log(import_chalk28.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
4532
+ console.log(import_chalk30.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
3948
4533
  }, portalResource);
3949
4534
  const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
3950
4535
  const creds = {
@@ -3959,7 +4544,7 @@ async function runConsumerLogin(opts) {
3959
4544
  obtainedAt: Date.now()
3960
4545
  };
3961
4546
  saveConsumer(creds);
3962
- console.log(import_chalk28.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
4547
+ console.log(import_chalk30.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
3963
4548
  }
3964
4549
  async function runConsumerTokens(opts) {
3965
4550
  const creds = requireConsumer();
@@ -3972,29 +4557,29 @@ async function runConsumerTokens(opts) {
3972
4557
  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));
3973
4558
  return;
3974
4559
  }
3975
- console.log(`${import_chalk28.default.cyan("Consumer")} ${import_chalk28.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk28.default.bold(fresh.tenant)}
4560
+ console.log(`${import_chalk30.default.cyan("Consumer")} ${import_chalk30.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk30.default.bold(fresh.tenant)}
3976
4561
  `);
3977
- console.log(`${import_chalk28.default.bold("access_token")} ${import_chalk28.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
4562
+ console.log(`${import_chalk30.default.bold("access_token")} ${import_chalk30.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
3978
4563
  ${fresh.accessToken}
3979
4564
  `);
3980
- if (fresh.idToken) console.log(`${import_chalk28.default.bold("id_token")} ${import_chalk28.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
4565
+ if (fresh.idToken) console.log(`${import_chalk30.default.bold("id_token")} ${import_chalk30.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
3981
4566
  ${fresh.idToken}
3982
4567
  `);
3983
- if (fresh.refreshToken) console.log(`${import_chalk28.default.bold("refresh_token")}
4568
+ if (fresh.refreshToken) console.log(`${import_chalk30.default.bold("refresh_token")}
3984
4569
  ${fresh.refreshToken}
3985
4570
  `);
3986
- console.log(import_chalk28.default.dim("These are your own tokens \u2014 keep them secret."));
4571
+ console.log(import_chalk30.default.dim("These are your own tokens \u2014 keep them secret."));
3987
4572
  }
3988
4573
  async function runConsumerApikeys(opts) {
3989
4574
  const creds = requireConsumer();
3990
4575
  const { default: inquirer2 } = await import("inquirer");
3991
- const spinner = (0, import_ora13.default)("Loading your API keys...").start();
4576
+ const spinner = (0, import_ora15.default)("Loading your API keys...").start();
3992
4577
  const list = await consumerFetch(creds, "/apikeys");
3993
4578
  const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
3994
4579
  spinner.stop();
3995
4580
  if (list.status >= 400) {
3996
- console.error(import_chalk28.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
3997
- if (list.status === 401) console.error(import_chalk28.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
4581
+ console.error(import_chalk30.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
4582
+ if (list.status === 401) console.error(import_chalk30.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
3998
4583
  process.exit(1);
3999
4584
  }
4000
4585
  const keys = list.data?.keys ?? [];
@@ -4002,16 +4587,16 @@ async function runConsumerApikeys(opts) {
4002
4587
  if (opts.json) {
4003
4588
  console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
4004
4589
  } else if (!keys.length) {
4005
- console.log(import_chalk28.default.yellow("No API keys yet."));
4590
+ console.log(import_chalk30.default.yellow("No API keys yet."));
4006
4591
  } else {
4007
4592
  for (const k of keys) {
4008
4593
  const clear = revealMap[k.environment]?.key;
4009
- const shown = clear ? import_chalk28.default.green(clear) : import_chalk28.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
4010
- const exp = k.expires_at ? import_chalk28.default.dim(`exp ${k.expires_at}`) : import_chalk28.default.dim("no expiry");
4011
- console.log(` ${import_chalk28.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk28.default.dim(k.description ?? "")}`);
4594
+ const shown = clear ? import_chalk30.default.green(clear) : import_chalk30.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
4595
+ const exp = k.expires_at ? import_chalk30.default.dim(`exp ${k.expires_at}`) : import_chalk30.default.dim("no expiry");
4596
+ console.log(` ${import_chalk30.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk30.default.dim(k.description ?? "")}`);
4012
4597
  }
4013
4598
  if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
4014
- console.log(import_chalk28.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
4599
+ console.log(import_chalk30.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
4015
4600
  }
4016
4601
  }
4017
4602
  if (opts.json) return;
@@ -4025,7 +4610,7 @@ async function runConsumerApikeys(opts) {
4025
4610
  const body = { environment: answers.environment };
4026
4611
  if (answers.description) body.description = answers.description;
4027
4612
  if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
4028
- const s2 = (0, import_ora13.default)("Creating key...").start();
4613
+ const s2 = (0, import_ora15.default)("Creating key...").start();
4029
4614
  const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
4030
4615
  if (created.status >= 400) {
4031
4616
  s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
@@ -4033,15 +4618,16 @@ async function runConsumerApikeys(opts) {
4033
4618
  }
4034
4619
  s2.succeed("Key created.");
4035
4620
  const key = created.data?.key ?? created.data?.fullKey;
4036
- if (key) console.log(` ${import_chalk28.default.green(key)} ${import_chalk28.default.dim("(shown once \u2014 store it now)")}`);
4037
- else console.log(import_chalk28.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
4621
+ if (key) console.log(` ${import_chalk30.default.green(key)} ${import_chalk30.default.dim("(shown once \u2014 store it now)")}`);
4622
+ else console.log(import_chalk30.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
4038
4623
  }
4039
4624
 
4040
4625
  // src/commands/sidecar.ts
4041
- var import_chalk29 = __toESM(require("chalk"));
4042
- var import_ora14 = __toESM(require("ora"));
4626
+ var import_chalk31 = __toESM(require("chalk"));
4627
+ var import_ora16 = __toESM(require("ora"));
4043
4628
  var fs7 = __toESM(require("fs"));
4044
4629
  var path4 = __toESM(require("path"));
4630
+ init_admin();
4045
4631
  init_auth();
4046
4632
  function detectNextProject(root) {
4047
4633
  const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs7.existsSync(path4.join(root, f)));
@@ -4079,18 +4665,18 @@ function upsertEnvLocal(root, token) {
4079
4665
  }
4080
4666
  function installSidecarPackage(root) {
4081
4667
  if (fs7.existsSync(path4.join(root, "node_modules", "apiblaze", "package.json"))) {
4082
- console.log(` ${import_chalk29.default.green("\u2713")} apiblaze package already installed`);
4668
+ console.log(` ${import_chalk31.default.green("\u2713")} apiblaze package already installed`);
4083
4669
  return;
4084
4670
  }
4085
4671
  const has = (f) => fs7.existsSync(path4.join(root, f));
4086
4672
  const pm = has("bun.lockb") || has("bun.lock") ? { cmd: "bun", add: "add" } : has("pnpm-lock.yaml") ? { cmd: "pnpm", add: "add" } : has("yarn.lock") ? { cmd: "yarn", add: "add" } : { cmd: "npm", add: "install" };
4087
- const spinner = (0, import_ora14.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
4673
+ const spinner = (0, import_ora16.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
4088
4674
  try {
4089
4675
  const { execSync } = require("child_process");
4090
4676
  execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
4091
4677
  spinner.succeed("Installed apiblaze (the sidecar runtime).");
4092
4678
  } catch {
4093
- spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk29.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk29.default.cyan("npm run dev")}.`);
4679
+ spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk31.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk31.default.cyan("npm run dev")}.`);
4094
4680
  }
4095
4681
  }
4096
4682
  function readEnvKey(root) {
@@ -4229,7 +4815,7 @@ async function runAnonymousInit(root, router, opts) {
4229
4815
  const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
4230
4816
  const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
4231
4817
  if (opts.newSession) clearAnonCred2();
4232
- const spinner = (0, import_ora14.default)("Setting up a sidecar (no login needed)...").start();
4818
+ const spinner = (0, import_ora16.default)("Setting up a sidecar (no login needed)...").start();
4233
4819
  let out;
4234
4820
  try {
4235
4821
  out = await sidecarInitAnonymous2();
@@ -4241,29 +4827,29 @@ async function runAnonymousInit(root, router, opts) {
4241
4827
  if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
4242
4828
  const envState = upsertEnvLocal(root, out.token);
4243
4829
  ensureGitignored(root);
4244
- console.log(` ${import_chalk29.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
4245
- console.log(` ${import_chalk29.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
4830
+ console.log(` ${import_chalk31.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
4831
+ console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
4246
4832
  installSidecarPackage(root);
4247
4833
  let inspectorPath = null;
4248
4834
  if (!opts.noInspector) {
4249
4835
  inspectorPath = generateInspector(root, router);
4250
- if (inspectorPath) console.log(` ${import_chalk29.default.green("\u2713")} inspector at ${inspectorPath}`);
4836
+ if (inspectorPath) console.log(` ${import_chalk31.default.green("\u2713")} inspector at ${inspectorPath}`);
4251
4837
  }
4252
4838
  console.log("");
4253
- console.log(import_chalk29.default.bold("Done (no account needed). What happens next:"));
4254
- console.log(` 1. ${import_chalk29.default.cyan("npm run dev")} and use your app.`);
4839
+ console.log(import_chalk31.default.bold("Done (no account needed). What happens next:"));
4840
+ console.log(` 1. ${import_chalk31.default.cyan("npm run dev")} and use your app.`);
4255
4841
  console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
4256
- console.log(` ${import_chalk29.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
4842
+ console.log(` ${import_chalk31.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
4257
4843
  console.log("");
4258
- console.log(import_chalk29.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
4259
- console.log(` ${import_chalk29.default.cyan("apiblaze login")} then ${import_chalk29.default.cyan("apiblaze claim")} ${import_chalk29.default.dim("(no code needed here)")}`);
4260
- console.log(import_chalk29.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
4844
+ console.log(import_chalk31.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
4845
+ console.log(` ${import_chalk31.default.cyan("apiblaze login")} then ${import_chalk31.default.cyan("apiblaze claim")} ${import_chalk31.default.dim("(no code needed here)")}`);
4846
+ console.log(import_chalk31.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
4261
4847
  }
4262
4848
  async function runSidecar(opts) {
4263
4849
  const root = path4.resolve(opts.dir ?? process.cwd());
4264
4850
  const detected = detectNextProject(root);
4265
4851
  if (!detected.found) {
4266
- console.log(import_chalk29.default.yellow(`No Next.js project detected in ${root}.`));
4852
+ console.log(import_chalk31.default.yellow(`No Next.js project detected in ${root}.`));
4267
4853
  console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
4268
4854
  return;
4269
4855
  }
@@ -4274,10 +4860,10 @@ async function runSidecar(opts) {
4274
4860
  if (!loadCredentials()) {
4275
4861
  upsertEnvLocal(root, readEnvKey(root));
4276
4862
  ensureGitignored(root);
4277
- console.log(` ${import_chalk29.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
4278
- console.log(` ${import_chalk29.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
4863
+ console.log(` ${import_chalk31.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
4864
+ console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
4279
4865
  installSidecarPackage(root);
4280
- console.log(import_chalk29.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
4866
+ console.log(import_chalk31.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
4281
4867
  return;
4282
4868
  }
4283
4869
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -4286,7 +4872,7 @@ async function runSidecar(opts) {
4286
4872
  const mustMint = !existingKey || opts.rotate || switchingTeam;
4287
4873
  let token = existingKey ?? "";
4288
4874
  if (mustMint) {
4289
- const spinner = (0, import_ora14.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
4875
+ const spinner = (0, import_ora16.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
4290
4876
  try {
4291
4877
  const out = await admin({
4292
4878
  method: "POST",
@@ -4300,39 +4886,40 @@ async function runSidecar(opts) {
4300
4886
  throw err;
4301
4887
  }
4302
4888
  } else {
4303
- console.log(import_chalk29.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
4889
+ console.log(import_chalk31.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
4304
4890
  }
4305
4891
  const envState = upsertEnvLocal(root, token);
4306
4892
  ensureGitignored(root);
4307
- console.log(` ${import_chalk29.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
4893
+ console.log(` ${import_chalk31.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
4308
4894
  const wireState = wireInstrumentation(root);
4309
- console.log(` ${import_chalk29.default.green("\u2713")} instrumentation.ts ${wireState}`);
4895
+ console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireState}`);
4310
4896
  installSidecarPackage(root);
4311
4897
  let inspectorPath = null;
4312
4898
  if (!opts.noInspector) {
4313
4899
  inspectorPath = generateInspector(root, detected.router);
4314
- if (inspectorPath) console.log(` ${import_chalk29.default.green("\u2713")} inspector at ${inspectorPath}`);
4900
+ if (inspectorPath) console.log(` ${import_chalk31.default.green("\u2713")} inspector at ${inspectorPath}`);
4315
4901
  }
4316
4902
  console.log("");
4317
- console.log(import_chalk29.default.bold("Done. What happens next:"));
4318
- console.log(` 1. ${import_chalk29.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
4319
- console.log(` 2. The origins your app calls appear as ${import_chalk29.default.bold("candidates")} \u2014 list them: ${import_chalk29.default.cyan("apiblaze sidecar")}`);
4320
- console.log(` 3. Approve the ones to route: ${import_chalk29.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
4903
+ console.log(import_chalk31.default.bold("Done. What happens next:"));
4904
+ console.log(` 1. ${import_chalk31.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
4905
+ console.log(` 2. The origins your app calls appear as ${import_chalk31.default.bold("candidates")} \u2014 list them: ${import_chalk31.default.cyan("apiblaze sidecar")}`);
4906
+ console.log(` 3. Approve the ones to route: ${import_chalk31.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
4321
4907
  console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
4322
- if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk29.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path4.dirname(inspectorPath)} before shipping)`);
4323
- if (switchingTeam) console.log(import_chalk29.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
4908
+ if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk31.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path4.dirname(inspectorPath)} before shipping)`);
4909
+ if (switchingTeam) console.log(import_chalk31.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
4324
4910
  console.log("");
4325
- console.log(import_chalk29.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
4326
- console.log(import_chalk29.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
4327
- console.log(import_chalk29.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
4911
+ console.log(import_chalk31.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
4912
+ console.log(import_chalk31.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
4913
+ console.log(import_chalk31.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
4328
4914
  console.log("");
4329
- console.log(import_chalk29.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
4330
- console.log(import_chalk29.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
4915
+ console.log(import_chalk31.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
4916
+ console.log(import_chalk31.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
4331
4917
  }
4332
4918
 
4333
4919
  // src/commands/origins.ts
4334
- var import_chalk30 = __toESM(require("chalk"));
4335
- var import_ora15 = __toESM(require("ora"));
4920
+ var import_chalk32 = __toESM(require("chalk"));
4921
+ var import_ora17 = __toESM(require("ora"));
4922
+ init_admin();
4336
4923
  init_auth();
4337
4924
  init_anon_cred();
4338
4925
  async function runOriginsList(opts) {
@@ -4340,7 +4927,7 @@ async function runOriginsList(opts) {
4340
4927
  if (!loadCredentials()) {
4341
4928
  const cred = loadAnonCred();
4342
4929
  if (!cred) {
4343
- console.log(import_chalk30.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
4930
+ console.log(import_chalk32.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
4344
4931
  return;
4345
4932
  }
4346
4933
  out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
@@ -4358,30 +4945,30 @@ async function runOriginsList(opts) {
4358
4945
  }
4359
4946
  const routed = out.routed ?? [];
4360
4947
  const candidates = out.candidates ?? [];
4361
- console.log(import_chalk30.default.bold(`
4948
+ console.log(import_chalk32.default.bold(`
4362
4949
  Routed through APIblaze (${routed.length})`));
4363
- if (!routed.length) console.log(import_chalk30.default.dim(" none yet"));
4364
- for (const r of routed) console.log(` ${import_chalk30.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk30.default.dim(`\u2192 ${r.project_id}`)}`);
4365
- console.log(import_chalk30.default.bold(`
4950
+ if (!routed.length) console.log(import_chalk32.default.dim(" none yet"));
4951
+ for (const r of routed) console.log(` ${import_chalk32.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk32.default.dim(`\u2192 ${r.project_id}`)}`);
4952
+ console.log(import_chalk32.default.bold(`
4366
4953
  Candidates \u2014 going direct, not yet approved (${candidates.length})`));
4367
- if (!candidates.length) console.log(import_chalk30.default.dim(" none \u2014 run your app to discover the origins it calls"));
4954
+ if (!candidates.length) console.log(import_chalk32.default.dim(" none \u2014 run your app to discover the origins it calls"));
4368
4955
  for (const c of candidates) {
4369
- console.log(` ${import_chalk30.default.yellow("\u25CB")} ${c.origin} ${import_chalk30.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
4956
+ console.log(` ${import_chalk32.default.yellow("\u25CB")} ${c.origin} ${import_chalk32.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
4370
4957
  }
4371
4958
  if (candidates.length) {
4372
- console.log(import_chalk30.default.dim(`
4959
+ console.log(import_chalk32.default.dim(`
4373
4960
  Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
4374
- console.log(import_chalk30.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
4961
+ console.log(import_chalk32.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
4375
4962
  }
4376
4963
  }
4377
4964
  async function runOriginsApprove(origin, opts) {
4378
4965
  if (!loadCredentials()) {
4379
4966
  const cred = loadAnonCred();
4380
4967
  if (!cred) {
4381
- console.error(import_chalk30.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
4968
+ console.error(import_chalk32.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
4382
4969
  process.exit(1);
4383
4970
  }
4384
- const spinner2 = (0, import_ora15.default)(`Approving ${origin} (anonymous)...`).start();
4971
+ const spinner2 = (0, import_ora17.default)(`Approving ${origin} (anonymous)...`).start();
4385
4972
  try {
4386
4973
  const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
4387
4974
  spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
@@ -4392,7 +4979,7 @@ async function runOriginsApprove(origin, opts) {
4392
4979
  return;
4393
4980
  }
4394
4981
  const { teamId } = await resolveTeam(opts.team);
4395
- const spinner = (0, import_ora15.default)(`Approving ${origin}...`).start();
4982
+ const spinner = (0, import_ora17.default)(`Approving ${origin}...`).start();
4396
4983
  try {
4397
4984
  const out = await admin({
4398
4985
  method: "POST",
@@ -4409,7 +4996,7 @@ async function runOriginsApprove(origin, opts) {
4409
4996
  }
4410
4997
  async function runOriginsDeny(origin, opts) {
4411
4998
  const { teamId } = await resolveTeam(opts.team);
4412
- const spinner = (0, import_ora15.default)(`Dismissing ${origin}...`).start();
4999
+ const spinner = (0, import_ora17.default)(`Dismissing ${origin}...`).start();
4413
5000
  try {
4414
5001
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
4415
5002
  spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
@@ -4420,7 +5007,7 @@ async function runOriginsDeny(origin, opts) {
4420
5007
  }
4421
5008
  async function runOriginsRemove(origin, opts) {
4422
5009
  const { teamId } = await resolveTeam(opts.team);
4423
- const spinner = (0, import_ora15.default)(`Removing the proxy for ${origin}...`).start();
5010
+ const spinner = (0, import_ora17.default)(`Removing the proxy for ${origin}...`).start();
4424
5011
  try {
4425
5012
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
4426
5013
  spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
@@ -4431,8 +5018,9 @@ async function runOriginsRemove(origin, opts) {
4431
5018
  }
4432
5019
 
4433
5020
  // src/commands/op.ts
4434
- var import_chalk31 = __toESM(require("chalk"));
5021
+ var import_chalk33 = __toESM(require("chalk"));
4435
5022
  init_auth();
5023
+ init_trace();
4436
5024
  init_types();
4437
5025
  var OPERATOR_EMAILS = /* @__PURE__ */ new Set(["julienpmjacquet@gmail.com", "chkev@umich.edu"]);
4438
5026
  var DASHBOARD_BASE6 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
@@ -4462,53 +5050,53 @@ async function opCall(call) {
4462
5050
  function printResidue(report, applied) {
4463
5051
  const up = report?.upstash ?? {};
4464
5052
  const fga = report?.fga ?? {};
4465
- console.log(import_chalk31.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
4466
- console.log(import_chalk31.default.bold("\n Upstash"));
5053
+ console.log(import_chalk33.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
5054
+ console.log(import_chalk33.default.bold("\n Upstash"));
4467
5055
  const orphans = up.orphans ?? [];
4468
- if (orphans.length === 0) console.log(import_chalk31.default.green(" no orphaned keys"));
4469
- for (const o of orphans) console.log(` ${import_chalk31.default.yellow(o.key)} ${import_chalk31.default.dim(`\u2014 ${o.reason}`)}`);
4470
- console.log(import_chalk31.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
4471
- if (up.unknown?.length) console.log(import_chalk31.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
4472
- if (applied) console.log(` ${import_chalk31.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
4473
- for (const e of up.errors ?? []) console.log(import_chalk31.default.red(` error: ${e}`));
4474
- console.log(import_chalk31.default.bold("\n OpenFGA / Neon"));
5056
+ if (orphans.length === 0) console.log(import_chalk33.default.green(" no orphaned keys"));
5057
+ for (const o of orphans) console.log(` ${import_chalk33.default.yellow(o.key)} ${import_chalk33.default.dim(`\u2014 ${o.reason}`)}`);
5058
+ console.log(import_chalk33.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
5059
+ if (up.unknown?.length) console.log(import_chalk33.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
5060
+ if (applied) console.log(` ${import_chalk33.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
5061
+ for (const e of up.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
5062
+ console.log(import_chalk33.default.bold("\n OpenFGA / Neon"));
4475
5063
  if (applied) {
4476
5064
  const swept = fga?.swept ?? [];
4477
- if (swept.length === 0) console.log(import_chalk31.default.green(" no orphaned stores"));
5065
+ if (swept.length === 0) console.log(import_chalk33.default.green(" no orphaned stores"));
4478
5066
  for (const s of swept) {
4479
5067
  console.log(
4480
- ` ${import_chalk31.default.yellow(s.store_id)} ${import_chalk31.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
5068
+ ` ${import_chalk33.default.yellow(s.store_id)} ${import_chalk33.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
4481
5069
  );
4482
5070
  }
4483
- if (fga?.remaining) console.log(import_chalk31.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
5071
+ if (fga?.remaining) console.log(import_chalk33.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
4484
5072
  } else {
4485
5073
  const fgaOrphans = fga?.orphans ?? [];
4486
- if (fgaOrphans.length === 0) console.log(import_chalk31.default.green(" no orphaned stores"));
5074
+ if (fgaOrphans.length === 0) console.log(import_chalk33.default.green(" no orphaned stores"));
4487
5075
  for (const s of fgaOrphans) {
4488
5076
  const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
4489
- console.log(` ${import_chalk31.default.yellow(s.store_id)} ${import_chalk31.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
5077
+ console.log(` ${import_chalk33.default.yellow(s.store_id)} ${import_chalk33.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
4490
5078
  }
4491
- console.log(import_chalk31.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
5079
+ console.log(import_chalk33.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
4492
5080
  }
4493
- for (const e of fga?.errors ?? []) console.log(import_chalk31.default.red(` error: ${e}`));
5081
+ for (const e of fga?.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
4494
5082
  console.log();
4495
5083
  }
4496
5084
  async function runOp(sub, opts = {}) {
4497
5085
  if (!loadCredentials()) {
4498
- console.log(import_chalk31.default.dim("Not logged in. Run `apiblaze login`."));
5086
+ console.log(import_chalk33.default.dim("Not logged in. Run `apiblaze login`."));
4499
5087
  return;
4500
5088
  }
4501
5089
  if (!isOperatorLogin()) {
4502
- console.log(import_chalk31.default.dim("`apiblaze op` is only available to platform operators."));
5090
+ console.log(import_chalk33.default.dim("`apiblaze op` is only available to platform operators."));
4503
5091
  return;
4504
5092
  }
4505
5093
  switch (sub) {
4506
5094
  case void 0:
4507
5095
  case "menu": {
4508
- console.log(import_chalk31.default.bold("\nOperator menu"));
4509
- console.log(` ${import_chalk31.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
4510
- console.log(` ${import_chalk31.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk31.default.dim("-y to skip")})`);
4511
- console.log(` ${import_chalk31.default.cyan("apiblaze op credits")} list credit wallets
5096
+ console.log(import_chalk33.default.bold("\nOperator menu"));
5097
+ console.log(` ${import_chalk33.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
5098
+ console.log(` ${import_chalk33.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk33.default.dim("-y to skip")})`);
5099
+ console.log(` ${import_chalk33.default.cyan("apiblaze op credits")} list credit wallets
4512
5100
  `);
4513
5101
  return;
4514
5102
  }
@@ -4524,15 +5112,15 @@ async function runOp(sub, opts = {}) {
4524
5112
  const nFga = report?.fga?.orphans?.length ?? 0;
4525
5113
  printResidue(report, false);
4526
5114
  if (nUp + nFga === 0) {
4527
- console.log(import_chalk31.default.green("Nothing to sweep."));
5115
+ console.log(import_chalk33.default.green("Nothing to sweep."));
4528
5116
  return;
4529
5117
  }
4530
5118
  if (!opts.yes) {
4531
5119
  const readline2 = await import("readline/promises");
4532
5120
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
4533
- const answer = await rl.question(import_chalk31.default.red(`Delete ${nUp} Upstash key(s) + ${nFga} OpenFGA store(s)? Type 'sweep' to confirm: `));
5121
+ const answer = await rl.question(import_chalk33.default.red(`Delete ${nUp} Upstash key(s) + ${nFga} OpenFGA store(s)? Type 'sweep' to confirm: `));
4534
5122
  rl.close();
4535
- if (answer.trim() !== "sweep") return void console.log(import_chalk31.default.dim("Aborted."));
5123
+ if (answer.trim() !== "sweep") return void console.log(import_chalk33.default.dim("Aborted."));
4536
5124
  }
4537
5125
  const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
4538
5126
  if (opts.json) return void console.log(JSON.stringify(result, null, 2));
@@ -4543,19 +5131,20 @@ async function runOp(sub, opts = {}) {
4543
5131
  const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
4544
5132
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
4545
5133
  const accounts = data?.accounts ?? [];
4546
- if (accounts.length === 0) return void console.log(import_chalk31.default.dim("No credit wallets."));
5134
+ if (accounts.length === 0) return void console.log(import_chalk33.default.dim("No credit wallets."));
4547
5135
  for (const a of accounts) {
4548
5136
  const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
4549
- console.log(` ${import_chalk31.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk31.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk31.default.dim(" \u2014 anon") : ""}`);
5137
+ console.log(` ${import_chalk33.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk33.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk33.default.dim(" \u2014 anon") : ""}`);
4550
5138
  }
4551
5139
  return;
4552
5140
  }
4553
5141
  default:
4554
- console.log(import_chalk31.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
5142
+ console.log(import_chalk33.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
4555
5143
  }
4556
5144
  }
4557
5145
 
4558
5146
  // src/index.ts
5147
+ init_trace();
4559
5148
  var program = new import_commander.Command();
4560
5149
  program.name("apiblaze").description("APIblaze CLI \u2014 create & manage API proxies and run dev tunnels").version(version).option("-v, --verbose", "Print the exact series of API calls each command makes (curl-equivalent you could run yourself)");
4561
5150
  program.hook("preAction", () => {
@@ -4606,7 +5195,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
4606
5195
  try {
4607
5196
  const resolved = parseInt(port ?? opts.port, 10);
4608
5197
  if (Number.isNaN(resolved)) {
4609
- console.error(import_chalk32.default.red(`Invalid port: ${port ?? opts.port}`));
5198
+ console.error(import_chalk34.default.red(`Invalid port: ${port ?? opts.port}`));
4610
5199
  process.exit(1);
4611
5200
  }
4612
5201
  await runDev({ port: resolved, captureFile: opts.captureFile });
@@ -4664,8 +5253,10 @@ domain.command("list").description("List custom domains for a proxy").argument("
4664
5253
  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)));
4665
5254
  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)));
4666
5255
  domain.command("set-base").description("Choose which version/environment your main URL serves").argument("<project>", "Project name or id").option("--env <env>", "Environment (default: prod)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainSetBase(project, opts)));
4667
- var tenant = program.command("tenant").description("Manage tenants \u2014 separate groups of your API's users");
4668
- tenant.command("list").description("List tenants in your team").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantList(opts)));
5256
+ var tenant = program.command("tenant").description("Manage tenants \u2014 bare command opens the interactive picker (settings, app clients, providers)").action(action(() => runTenantManage(void 0, {})));
5257
+ tenant.command("manage").description("Browse & edit one tenant: settings, login app clients, providers, issuers (search-first picker)").argument("[query]", "Search by tenant name/display name (omit to use your tenant scope or pick)").option("--tenant <slug>", "Exact tenant slug (skips the picker)").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantManage(query, opts)));
5258
+ tenant.command("use").description("Set the sticky tenant scope for future commands (search-first; --clear to unset)").argument("[query]", "Search by tenant name/display name").option("--clear", "Clear the tenant scope").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantUse(query, opts)));
5259
+ tenant.command("list").description("List tenants in your team (--q searches server-side)").option("--q <search>", "Filter by tenant name or display name").option("--limit <n>", "Page size (max 200)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantList(opts)));
4669
5260
  tenant.command("create").description("Create a tenant in your team").requiredOption("--name <display>", "Display name").option("--slug <tenant_name>", "Explicit tenant slug (generated if omitted)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantCreate(opts)));
4670
5261
  tenant.command("attach").description("Attach a tenant to a proxy").argument("<project>", "Project name or id").requiredOption("--tenant <slug>", "Tenant slug to attach").option("--auth-config <id>", "Auth config id to bind").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTenantAttach(project, opts)));
4671
5262
  tenant.command("delete").description("Delete a tenant (full cascade)").argument("<slug>", "Tenant slug to delete").option("--team <id|name>", "Team (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").action(action((slug, opts) => runTenantDelete(slug, opts)));
@@ -4702,7 +5293,7 @@ function groupedCommandHelp() {
4702
5293
  const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
4703
5294
  return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
4704
5295
  }).filter(Boolean).join("\n");
4705
- return `${import_chalk32.default.bold(g.title)}
5296
+ return `${import_chalk34.default.bold(g.title)}
4706
5297
  ${rows}`;
4707
5298
  }).join("\n\n");
4708
5299
  }
@@ -4730,13 +5321,18 @@ Examples:
4730
5321
  `);
4731
5322
  function printError(err) {
4732
5323
  if (err instanceof ApiError) {
4733
- console.error(import_chalk32.default.red(`
4734
- API error (${err.status}): ${err.message}`));
5324
+ const data = err.body;
5325
+ const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
5326
+ console.error(import_chalk34.default.red(`
5327
+ API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
5328
+ if (err.status === 403 || err.status === 404) {
5329
+ console.error(import_chalk34.default.dim("If your team/tenant was recently deleted or recreated, re-run `apiblaze login` or switch with `apiblaze team`."));
5330
+ }
4735
5331
  } else if (err instanceof Error) {
4736
- console.error(import_chalk32.default.red(`
5332
+ console.error(import_chalk34.default.red(`
4737
5333
  Error: ${err.message}`));
4738
5334
  } else {
4739
- console.error(import_chalk32.default.red("\nUnknown error"));
5335
+ console.error(import_chalk34.default.red("\nUnknown error"));
4740
5336
  }
4741
5337
  }
4742
5338
  program.parse(process.argv);