apiblaze 0.19.28 → 0.19.30

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 +1354 -447
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -237,7 +237,7 @@ var init_api = __esm({
237
237
  "use strict";
238
238
  init_auth();
239
239
  init_types();
240
- DASHBOARD_BASE = "https://dashboard.apiblaze.com";
240
+ DASHBOARD_BASE = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
241
241
  PUBLIC_API_BASE = "https://api.apiblaze.com";
242
242
  }
243
243
  });
@@ -272,14 +272,14 @@ async function resolveLinkedTeam(opts) {
272
272
  return { teamId: picked2.teamId, teamName: picked2.name };
273
273
  }
274
274
  if (opts.preferredId) {
275
- console.log(import_chalk.default.yellow("\nYour previously linked team is no longer available."));
275
+ console.error(import_chalk.default.yellow("\nYour previously linked team is no longer available."));
276
276
  }
277
277
  if (teams.length === 1) {
278
- console.log(`${import_chalk.default.cyan("\u2192")} Linking to your team ${import_chalk.default.bold(teams[0].name)}.`);
278
+ console.error(`${import_chalk.default.cyan("\u2192")} Linking to your team ${import_chalk.default.bold(teams[0].name)}.`);
279
279
  return { teamId: teams[0].teamId, teamName: teams[0].name };
280
280
  }
281
281
  if (!opts.interactive) {
282
- console.log(import_chalk.default.yellow(`Linking to "${teams[0].name}" \u2014 pass --team to choose another.`));
282
+ console.error(import_chalk.default.yellow(`Linking to "${teams[0].name}" \u2014 pass --team to choose another.`));
283
283
  return { teamId: teams[0].teamId, teamName: teams[0].name };
284
284
  }
285
285
  const { default: inquirer3 } = await import("inquirer");
@@ -392,24 +392,24 @@ function maskBody(body) {
392
392
  }
393
393
  function renderTrace() {
394
394
  if (!verbose || entries.length === 0) return;
395
- console.log(import_chalk6.default.dim("\n" + "\u2500".repeat(64)));
396
- console.log(import_chalk6.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
397
- console.log(
395
+ console.error(import_chalk6.default.dim("\n" + "\u2500".repeat(64)));
396
+ console.error(import_chalk6.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
397
+ console.error(
398
398
  import_chalk6.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")
399
399
  );
400
400
  entries.forEach((e, i) => {
401
401
  const n = entries.length > 1 ? import_chalk6.default.bold(`${i + 1}. `) : "";
402
- if (e.summary) console.log(`${n}${import_chalk6.default.cyan(e.summary)}${e.status ? import_chalk6.default.dim(` (HTTP ${e.status})`) : ""}`);
402
+ if (e.summary) console.error(`${n}${import_chalk6.default.cyan(e.summary)}${e.status ? import_chalk6.default.dim(` (HTTP ${e.status})`) : ""}`);
403
403
  const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
404
404
  const masked = maskBody(e.body);
405
405
  const hasBody = e.method !== "GET" && masked !== void 0;
406
- console.log(import_chalk6.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
407
- console.log(import_chalk6.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
406
+ console.error(import_chalk6.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
407
+ console.error(import_chalk6.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
408
408
  if (hasBody) {
409
- console.log(import_chalk6.default.green(" -H 'Content-Type: application/json' \\"));
410
- console.log(import_chalk6.default.green(` -d '${masked}'`));
409
+ console.error(import_chalk6.default.green(" -H 'Content-Type: application/json' \\"));
410
+ console.error(import_chalk6.default.green(` -d '${masked}'`));
411
411
  }
412
- if (i < entries.length - 1) console.log();
412
+ if (i < entries.length - 1) console.error();
413
413
  });
414
414
  entries.length = 0;
415
415
  }
@@ -458,7 +458,7 @@ function formatBilling(billing) {
458
458
  }
459
459
  function maybePrintBilling(data) {
460
460
  const line = formatBilling(data?.billing);
461
- if (line) console.log(import_chalk7.default.magenta(` ${line}`));
461
+ if (line) console.error(import_chalk7.default.magenta(` ${line}`));
462
462
  }
463
463
  var import_chalk7, DASHBOARD_BASE3;
464
464
  var init_admin = __esm({
@@ -714,7 +714,7 @@ async function createTenantRow(teamId) {
714
714
  const { name } = await inquirer3.prompt([{
715
715
  type: "input",
716
716
  name: "name",
717
- message: `Tenant name ${import_chalk27.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
717
+ message: `Tenant name ${import_chalk30.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
718
718
  validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
719
719
  }]);
720
720
  const slug = name.trim();
@@ -729,7 +729,7 @@ async function createTenantRow(teamId) {
729
729
  } catch (err) {
730
730
  const msg = err instanceof Error ? err.message : String(err);
731
731
  if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
732
- console.log(import_chalk27.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
732
+ console.log(import_chalk30.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
733
733
  continue;
734
734
  }
735
735
  throw err;
@@ -746,11 +746,11 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
746
746
  pageSize: 10,
747
747
  choices: [
748
748
  ...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
749
- ...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk27.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
749
+ ...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk30.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
750
750
  ]
751
751
  }]);
752
752
  if (!provider) {
753
- console.log(import_chalk27.default.dim(" No login method yet \u2014 add one later under Login methods."));
753
+ console.log(import_chalk30.default.dim(" No login method yet \u2014 add one later under Login methods."));
754
754
  return false;
755
755
  }
756
756
  const opt = PROVIDERS.find((p) => p.id === provider);
@@ -796,26 +796,26 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
796
796
  async function createTenantInteractive(teamId) {
797
797
  const { default: inquirer3 } = await import("inquirer");
798
798
  const tenant2 = await createTenantRow(teamId);
799
- console.log(import_chalk27.default.green(` Tenant ${import_chalk27.default.bold(tenant2)} created.`));
799
+ console.log(import_chalk30.default.green(` Tenant ${import_chalk30.default.bold(tenant2)} created.`));
800
800
  const { users } = await inquirer3.prompt([{
801
801
  type: "confirm",
802
802
  name: "users",
803
803
  default: false,
804
- message: `Enable Users & groups? ${import_chalk27.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
804
+ message: `Enable Users & groups? ${import_chalk30.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
805
805
  }]);
806
806
  if (users) {
807
807
  await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
808
808
  });
809
- console.log(import_chalk27.default.green(" Users & groups enabled."));
809
+ console.log(import_chalk30.default.green(" Users & groups enabled."));
810
810
  }
811
811
  await addApiblazeHostedLogin(teamId, tenant2, { allowSkip: true });
812
812
  return tenant2;
813
813
  }
814
- var import_chalk27, import_ora10, PROVIDERS, DEFAULT_SCOPES;
814
+ var import_chalk30, import_ora10, PROVIDERS, DEFAULT_SCOPES;
815
815
  var init_tenant_create = __esm({
816
816
  "src/lib/tenant-create.ts"() {
817
817
  "use strict";
818
- import_chalk27 = __toESM(require("chalk"));
818
+ import_chalk30 = __toESM(require("chalk"));
819
819
  import_ora10 = __toESM(require("ora"));
820
820
  init_admin();
821
821
  PROVIDERS = [
@@ -841,7 +841,7 @@ var tenant_pick_exports = {};
841
841
  __export(tenant_pick_exports, {
842
842
  pickTenant: () => pickTenant
843
843
  });
844
- async function fetchPage(teamId, q) {
844
+ async function fetchPage2(teamId, q) {
845
845
  const out = await admin({
846
846
  method: "GET",
847
847
  path: `/teams/${encodeURIComponent(teamId)}/tenants?detail=1&limit=${PAGE}${q ? `&q=${encodeURIComponent(q)}` : ""}`,
@@ -854,10 +854,10 @@ async function fetchPage(teamId, q) {
854
854
  }
855
855
  function label(t, defaultTenant, active) {
856
856
  const tags = [
857
- t.tenant_name === active ? import_chalk28.default.cyan("active scope") : "",
858
- t.tenant_name === defaultTenant ? import_chalk28.default.dim("team default") : ""
857
+ t.tenant_name === active ? import_chalk31.default.cyan("active scope") : "",
858
+ t.tenant_name === defaultTenant ? import_chalk31.default.dim("team default") : ""
859
859
  ].filter(Boolean).join(", ");
860
- const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk28.default.dim(` ${t.display_name}`) : "";
860
+ const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk31.default.dim(` ${t.display_name}`) : "";
861
861
  return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
862
862
  }
863
863
  async function pickTenant(teamId, opts = {}) {
@@ -866,13 +866,13 @@ async function pickTenant(teamId, opts = {}) {
866
866
  let q = opts.initialQuery ?? "";
867
867
  for (; ; ) {
868
868
  const spinner = (0, import_ora11.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
869
- const page = await fetchPage(teamId, q).finally(() => spinner.stop());
869
+ const page = await fetchPage2(teamId, q).finally(() => spinner.stop());
870
870
  if (!page.total && !q) {
871
871
  if (opts.allowCreate) {
872
872
  const { make } = await inquirer3.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
873
873
  if (make) return await createTenantInline(teamId);
874
874
  }
875
- console.error(import_chalk28.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
875
+ console.error(import_chalk31.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
876
876
  return null;
877
877
  }
878
878
  const truncated = page.total > page.rows.length;
@@ -881,7 +881,7 @@ async function pickTenant(teamId, opts = {}) {
881
881
  value: t.tenant_name
882
882
  }));
883
883
  if (truncated || q) {
884
- choices.push(new inquirer3.Separator(import_chalk28.default.dim(
884
+ choices.push(new inquirer3.Separator(import_chalk31.default.dim(
885
885
  truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
886
886
  )));
887
887
  choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
@@ -915,11 +915,11 @@ async function createTenantInline(teamId) {
915
915
  const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
916
916
  return createTenantInteractive2(teamId);
917
917
  }
918
- var import_chalk28, import_ora11, PAGE;
918
+ var import_chalk31, import_ora11, PAGE;
919
919
  var init_tenant_pick = __esm({
920
920
  "src/lib/tenant-pick.ts"() {
921
921
  "use strict";
922
- import_chalk28 = __toESM(require("chalk"));
922
+ import_chalk31 = __toESM(require("chalk"));
923
923
  import_ora11 = __toESM(require("ora"));
924
924
  init_admin();
925
925
  init_auth();
@@ -929,10 +929,10 @@ var init_tenant_pick = __esm({
929
929
 
930
930
  // src/index.ts
931
931
  var import_commander = require("commander");
932
- var import_chalk45 = __toESM(require("chalk"));
932
+ var import_chalk48 = __toESM(require("chalk"));
933
933
 
934
934
  // package.json
935
- var version = "0.19.28";
935
+ var version = "0.19.30";
936
936
 
937
937
  // src/index.ts
938
938
  init_types();
@@ -942,7 +942,7 @@ var import_chalk2 = __toESM(require("chalk"));
942
942
  var import_ora = __toESM(require("ora"));
943
943
  init_auth();
944
944
  init_team();
945
- var DASHBOARD_BASE2 = "https://dashboard.apiblaze.com";
945
+ var DASHBOARD_BASE2 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
946
946
  function openBrowser(url) {
947
947
  const { exec } = require("child_process");
948
948
  const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
@@ -3304,8 +3304,863 @@ async function runExport(projectArg, versionArg, opts) {
3304
3304
  if (target === "kong") console.log(import_chalk24.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
3305
3305
  }
3306
3306
 
3307
- // src/commands/config.ts
3307
+ // src/commands/logs.ts
3308
+ var import_chalk26 = __toESM(require("chalk"));
3309
+ init_admin();
3310
+ init_auth();
3311
+ init_resolve();
3312
+
3313
+ // src/lib/log-view.ts
3308
3314
  var import_chalk25 = __toESM(require("chalk"));
3315
+ var readline2 = __toESM(require("readline"));
3316
+ function statusColor(status) {
3317
+ if (status < 300) return import_chalk25.default.green;
3318
+ if (status < 400) return import_chalk25.default.blue;
3319
+ if (status < 500) return import_chalk25.default.yellow;
3320
+ return import_chalk25.default.red;
3321
+ }
3322
+ function methodColor(method) {
3323
+ switch ((method || "").toUpperCase()) {
3324
+ case "GET":
3325
+ return import_chalk25.default.green;
3326
+ case "POST":
3327
+ return import_chalk25.default.blue;
3328
+ case "PUT":
3329
+ case "PATCH":
3330
+ return import_chalk25.default.yellow;
3331
+ case "DELETE":
3332
+ return import_chalk25.default.red;
3333
+ default:
3334
+ return import_chalk25.default.white;
3335
+ }
3336
+ }
3337
+ function clock(ts) {
3338
+ return new Date(ts).toLocaleTimeString(void 0, { hour12: false });
3339
+ }
3340
+ var ROUTE_W = 34;
3341
+ function routeCell(route) {
3342
+ const r = route || "\u2014";
3343
+ return (r.length > ROUTE_W ? `\u2026${r.slice(-(ROUTE_W - 1))}` : r).padEnd(ROUTE_W);
3344
+ }
3345
+ function formatRow(r) {
3346
+ const time = import_chalk25.default.dim(clock(r.ts));
3347
+ const method = methodColor(r.method)(String(r.method || "\u2014").padEnd(6));
3348
+ const route = routeCell(String(r.route || "\u2014"));
3349
+ const status = statusColor(r.status)(String(r.status ?? "\u2014").padStart(3));
3350
+ const ms = import_chalk25.default.dim(`${r.duration_ms}ms`.padStart(8));
3351
+ const step = r.error_step ? import_chalk25.default.dim(` ${r.error_step}`) : "";
3352
+ return `${time} ${method} ${route} ${status} ${ms}${step}`;
3353
+ }
3354
+ function printRow(r, json) {
3355
+ console.log(json ? JSON.stringify(r) : formatRow(r));
3356
+ }
3357
+ var WindowNotices = class {
3358
+ constructor() {
3359
+ this.saidUnavailable = false;
3360
+ this.saidGap = false;
3361
+ this.saidDropped = false;
3362
+ }
3363
+ note(res) {
3364
+ if (res.history_unavailable && !this.saidUnavailable) {
3365
+ this.saidUnavailable = true;
3366
+ console.error(import_chalk25.default.yellow("\u26A0 log history is unreachable right now \u2014 showing the live buffer only, older requests exist but cannot be read."));
3367
+ }
3368
+ if ((res.history_markers?.length ?? 0) > 0 && !this.saidGap) {
3369
+ this.saidGap = true;
3370
+ console.error(import_chalk25.default.yellow("\u26A0 this range includes a volume-ceiling gap \u2014 some rows were dropped at intake and are gone; counts are incomplete."));
3371
+ }
3372
+ if ((res.dropped_this_hour ?? 0) > 0 && !this.saidDropped) {
3373
+ this.saidDropped = true;
3374
+ console.error(import_chalk25.default.yellow(`\u26A0 volume ceiling \u2014 ${res.dropped_this_hour} rows dropped this hour; errors keep logging.`));
3375
+ }
3376
+ }
3377
+ };
3378
+ var MAX_RECONNECTS = 5;
3379
+ var PING_MS = Number(process.env.APIBLAZE_TAIL_PING_MS) || 3e4;
3380
+ var LIVENESS_MS = PING_MS * 2.5;
3381
+ var STABLE_MS = 3e4;
3382
+ var SEEN_CAP = 1e4;
3383
+ var TAIL_INSPECT_BUFFER = 500;
3384
+ var TAIL_INSPECT_CHOICES = 40;
3385
+ async function runTailSession(opts) {
3386
+ const { default: WebSocket2 } = await import("ws");
3387
+ guardStdoutPipe();
3388
+ let closed = false;
3389
+ let everConnected = false;
3390
+ let reconnects = 0;
3391
+ let openedAt = 0;
3392
+ let lastFailure = "";
3393
+ let pingTimer;
3394
+ let activeSocket;
3395
+ const seen = /* @__PURE__ */ new Set();
3396
+ const seenOrder = [];
3397
+ function remember(id) {
3398
+ seen.add(id);
3399
+ seenOrder.push(id);
3400
+ if (seenOrder.length > SEEN_CAP) seen.delete(seenOrder.shift());
3401
+ }
3402
+ const interactive = !opts.json && !!process.stdin.isTTY && !!process.stdout.isTTY;
3403
+ const recent = [];
3404
+ const held = [];
3405
+ let heldRowsDropped = 0;
3406
+ let heldNotesDropped = 0;
3407
+ let paused = false;
3408
+ let keysBound = false;
3409
+ function pushHeld(item) {
3410
+ if (held.length >= TAIL_INSPECT_BUFFER) {
3411
+ const victimIdx = held.findIndex((h) => h.kind === "row");
3412
+ if (victimIdx >= 0) {
3413
+ held.splice(victimIdx, 1);
3414
+ heldRowsDropped++;
3415
+ } else {
3416
+ held.shift();
3417
+ heldNotesDropped++;
3418
+ }
3419
+ }
3420
+ held.push(item);
3421
+ }
3422
+ function diag(msg) {
3423
+ if (paused) {
3424
+ pushHeld({ kind: "note", msg });
3425
+ return;
3426
+ }
3427
+ console.error(msg);
3428
+ }
3429
+ function flushHeld() {
3430
+ if (!held.length && !heldRowsDropped && !heldNotesDropped) return;
3431
+ const rows = held.filter((h) => h.kind === "row").length;
3432
+ const total = rows + heldRowsDropped;
3433
+ if (total) console.log(import_chalk25.default.dim(`\u2500\u2500\u2500 ${total} request${total === 1 ? "" : "s"} arrived while paused \u2500\u2500\u2500`));
3434
+ for (const h of held) {
3435
+ if (h.kind === "row") printRow(h.row, opts.json);
3436
+ else console.error(h.msg);
3437
+ }
3438
+ if (heldRowsDropped || heldNotesDropped) {
3439
+ console.log(import_chalk25.default.dim(`\u2500\u2500\u2500 ${heldRowsDropped} older one${heldRowsDropped === 1 ? "" : "s"} not shown (pause buffer full) \u2014 browse them with \`logs list\` \u2500\u2500\u2500`));
3440
+ }
3441
+ held.length = 0;
3442
+ heldRowsDropped = 0;
3443
+ heldNotesDropped = 0;
3444
+ }
3445
+ function shutdown() {
3446
+ closed = true;
3447
+ if (pingTimer) clearInterval(pingTimer);
3448
+ unbindKeys();
3449
+ try {
3450
+ activeSocket?.close();
3451
+ } catch {
3452
+ }
3453
+ if (!opts.json) console.log(import_chalk25.default.dim("\nStopped."));
3454
+ process.exit(0);
3455
+ }
3456
+ function onKeypress(_s, key) {
3457
+ if (key?.ctrl && key.name === "c") {
3458
+ shutdown();
3459
+ return;
3460
+ }
3461
+ if (key?.name === "return" || key?.name === "enter") void openInspector();
3462
+ }
3463
+ function bindKeys() {
3464
+ if (!interactive || keysBound || paused) return;
3465
+ keysBound = true;
3466
+ readline2.emitKeypressEvents(process.stdin);
3467
+ process.stdin.setRawMode(true);
3468
+ process.stdin.on("keypress", onKeypress);
3469
+ process.stdin.resume();
3470
+ }
3471
+ function unbindKeys() {
3472
+ if (!keysBound) return;
3473
+ keysBound = false;
3474
+ process.stdin.off("keypress", onKeypress);
3475
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
3476
+ process.stdin.pause();
3477
+ }
3478
+ async function openInspector() {
3479
+ if (paused) return;
3480
+ if (recent.length === 0) {
3481
+ console.log(import_chalk25.default.dim(" nothing captured yet \u2014 waiting for a request\u2026"));
3482
+ return;
3483
+ }
3484
+ paused = true;
3485
+ unbindKeys();
3486
+ try {
3487
+ const { default: inquirer3 } = await import("inquirer");
3488
+ console.log(import_chalk25.default.dim("\n\u2500\u2500\u2500 paused \xB7 still capturing in the background \u2500\u2500\u2500"));
3489
+ for (; ; ) {
3490
+ const choices = recent.slice(-TAIL_INSPECT_CHOICES).reverse().map((r) => ({ name: formatRow(r), value: r.request_id }));
3491
+ choices.push({ name: import_chalk25.default.dim("\u2039resume tail\u203A"), value: "__resume__" });
3492
+ const { picked } = await inquirer3.prompt([{
3493
+ type: "list",
3494
+ name: "picked",
3495
+ message: "Inspect which request?",
3496
+ choices,
3497
+ pageSize: 15
3498
+ }]);
3499
+ if (picked === "__resume__") break;
3500
+ const row = recent.find((r) => r.request_id === picked);
3501
+ if (row) printDetail(row, opts.detail);
3502
+ else console.log(import_chalk25.default.dim(" that request has scrolled out of the tail buffer \u2014 find it with `logs list`.\n"));
3503
+ }
3504
+ } finally {
3505
+ paused = false;
3506
+ flushHeld();
3507
+ console.log(import_chalk25.default.dim("\u2500\u2500\u2500 resumed \u2500\u2500\u2500"));
3508
+ bindKeys();
3509
+ }
3510
+ }
3511
+ process.on("SIGINT", shutdown);
3512
+ function scheduleReconnect() {
3513
+ if (closed) return;
3514
+ if (reconnects >= MAX_RECONNECTS) {
3515
+ paused = false;
3516
+ flushHeld();
3517
+ console.error(import_chalk25.default.red(`Connection lost \u2014 giving up after repeated reconnects${lastFailure ? ` (last error: ${lastFailure})` : ""}.`));
3518
+ process.exit(1);
3519
+ }
3520
+ const delay = Math.min(1e3 * 2 ** reconnects, 15e3);
3521
+ reconnects++;
3522
+ diag(import_chalk25.default.dim(`disconnected${lastFailure ? ` (${lastFailure})` : ""} \u2014 reconnecting in ${Math.round(delay / 1e3)}s\u2026`));
3523
+ setTimeout(() => {
3524
+ if (!closed) void connect();
3525
+ }, delay);
3526
+ }
3527
+ async function connect() {
3528
+ let url;
3529
+ try {
3530
+ url = await opts.mintTicket();
3531
+ } catch (err) {
3532
+ if (!everConnected) {
3533
+ console.error(import_chalk25.default.red(`Could not start tail: ${err.message}`));
3534
+ process.exit(1);
3535
+ }
3536
+ lastFailure = `ticket mint failed: ${err.message}`;
3537
+ scheduleReconnect();
3538
+ return;
3539
+ }
3540
+ let socket;
3541
+ try {
3542
+ socket = new WebSocket2(url);
3543
+ } catch (err) {
3544
+ lastFailure = `bad tail URL: ${err.message}`;
3545
+ if (!everConnected) {
3546
+ console.error(import_chalk25.default.red(`Could not start tail: ${lastFailure}`));
3547
+ process.exit(1);
3548
+ }
3549
+ scheduleReconnect();
3550
+ return;
3551
+ }
3552
+ activeSocket = socket;
3553
+ let lastInbound = Date.now();
3554
+ socket.on("open", () => {
3555
+ const isReconnect = everConnected;
3556
+ everConnected = true;
3557
+ openedAt = Date.now();
3558
+ lastInbound = Date.now();
3559
+ lastFailure = "";
3560
+ if (isReconnect) {
3561
+ diag(import_chalk25.default.dim("reconnected \u2014 requests during the gap were not replayed (browse them with `logs list`)."));
3562
+ } else {
3563
+ diag(import_chalk25.default.dim(interactive ? "[Enter] inspect a request \xB7 [Ctrl+C] stop\n" : "Ctrl+C to stop."));
3564
+ }
3565
+ bindKeys();
3566
+ pingTimer = setInterval(() => {
3567
+ if (Date.now() - lastInbound > LIVENESS_MS) {
3568
+ lastFailure = `no pong for ${Math.round(LIVENESS_MS / 1e3)}s \u2014 connection presumed dead`;
3569
+ try {
3570
+ socket.terminate();
3571
+ } catch {
3572
+ }
3573
+ return;
3574
+ }
3575
+ try {
3576
+ socket.send("ping");
3577
+ } catch {
3578
+ }
3579
+ }, PING_MS);
3580
+ });
3581
+ socket.on("message", (data) => {
3582
+ lastInbound = Date.now();
3583
+ let frame;
3584
+ try {
3585
+ frame = JSON.parse(String(data));
3586
+ } catch {
3587
+ return;
3588
+ }
3589
+ if (frame?.type === "ceiling") {
3590
+ diag(import_chalk25.default.yellow(
3591
+ `\u26A0 volume ceiling \u2014 ${Number(frame.dropped_this_hour) || 0} rows dropped this hour; errors keep streaming.`
3592
+ ));
3593
+ return;
3594
+ }
3595
+ if (!frame?.request_id || seen.has(frame.request_id)) return;
3596
+ remember(frame.request_id);
3597
+ if (opts.errorsOnly && Number(frame.status) < 400) return;
3598
+ recent.push(frame);
3599
+ if (recent.length > TAIL_INSPECT_BUFFER) recent.shift();
3600
+ if (paused) {
3601
+ pushHeld({ kind: "row", row: frame });
3602
+ return;
3603
+ }
3604
+ printRow(frame, opts.json);
3605
+ });
3606
+ socket.on("error", (err) => {
3607
+ lastFailure = err?.message || String(err);
3608
+ });
3609
+ socket.on("close", (code, reason) => {
3610
+ if (pingTimer) clearInterval(pingTimer);
3611
+ if (openedAt && Date.now() - openedAt >= STABLE_MS) reconnects = 0;
3612
+ openedAt = 0;
3613
+ const why = String(reason ?? "").trim();
3614
+ if (why) lastFailure = `socket closed: ${why} (${code})`;
3615
+ scheduleReconnect();
3616
+ });
3617
+ }
3618
+ await connect();
3619
+ }
3620
+ function localDayMs(ts) {
3621
+ const d = new Date(ts);
3622
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
3623
+ }
3624
+ function nextLocalDayMs(dayMs) {
3625
+ const d = new Date(dayMs);
3626
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1).getTime();
3627
+ }
3628
+ function dayKey(ts) {
3629
+ const d = new Date(ts);
3630
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
3631
+ }
3632
+ function dayLabel(dayMs, now) {
3633
+ const today = localDayMs(now);
3634
+ const n = new Date(now);
3635
+ const yesterday = new Date(n.getFullYear(), n.getMonth(), n.getDate() - 1).getTime();
3636
+ if (dayMs === today) return "Today";
3637
+ if (dayMs === yesterday) return "Yesterday";
3638
+ return new Date(dayMs).toLocaleDateString(void 0, { weekday: "short", month: "short", day: "numeric" });
3639
+ }
3640
+ function groupIntoDays(rows) {
3641
+ const byKey = /* @__PURE__ */ new Map();
3642
+ const order = [];
3643
+ for (const r of [...rows].sort((a, b) => b.ts - a.ts)) {
3644
+ const k = dayKey(r.ts);
3645
+ let b = byKey.get(k);
3646
+ if (!b) {
3647
+ b = { key: k, dayMs: localDayMs(r.ts), rows: [], errors: 0 };
3648
+ byKey.set(k, b);
3649
+ order.push(b);
3650
+ }
3651
+ b.rows.push(r);
3652
+ if (r.status >= 400) b.errors++;
3653
+ }
3654
+ return order;
3655
+ }
3656
+ var SCAN_LIMIT = 200;
3657
+ var PAGE_LIMIT = 50;
3658
+ var JSON_PAGE_LIMIT = 500;
3659
+ var MAX_EMPTY_HOPS = 100;
3660
+ async function fetchPage(fetchWindow3, params, notices) {
3661
+ let cursor = params.cursor;
3662
+ for (let hop = 0; ; hop++) {
3663
+ const res = await fetchWindow3({ ...params, cursor });
3664
+ notices.note(res);
3665
+ const rows = res.rows ?? [];
3666
+ if (rows.length > 0 || !res.next_cursor) {
3667
+ return { rows, nextCursor: res.next_cursor ?? void 0 };
3668
+ }
3669
+ if (hop >= MAX_EMPTY_HOPS) {
3670
+ console.error(import_chalk25.default.yellow("\u26A0 stopped after scanning a long empty span \u2014 older rows may still exist."));
3671
+ return { rows: [], nextCursor: res.next_cursor, fuseTripped: true };
3672
+ }
3673
+ if (hop === 0) console.error(import_chalk25.default.dim("searching older history\u2026"));
3674
+ cursor = res.next_cursor;
3675
+ }
3676
+ }
3677
+ var epipeGuarded = false;
3678
+ function guardStdoutPipe() {
3679
+ if (epipeGuarded) return;
3680
+ epipeGuarded = true;
3681
+ process.stdout.on("error", (err) => {
3682
+ if (err?.code === "EPIPE") process.exit(0);
3683
+ throw err;
3684
+ });
3685
+ }
3686
+ var PLAIN_MAX_ROWS = 200;
3687
+ async function runPlainList(opts, notices) {
3688
+ guardStdoutPipe();
3689
+ const rows = [];
3690
+ let cursor;
3691
+ let more = false;
3692
+ let fuseTripped = false;
3693
+ while (rows.length < PLAIN_MAX_ROWS) {
3694
+ const page = await fetchPage(opts.fetchWindow, {
3695
+ limit: Math.min(PAGE_LIMIT, PLAIN_MAX_ROWS - rows.length),
3696
+ errorsOnly: opts.errorsOnly,
3697
+ cursor
3698
+ }, notices);
3699
+ rows.push(...page.rows);
3700
+ if (page.fuseTripped) {
3701
+ more = true;
3702
+ fuseTripped = true;
3703
+ break;
3704
+ }
3705
+ if (!page.nextCursor) break;
3706
+ cursor = page.nextCursor;
3707
+ if (rows.length >= PLAIN_MAX_ROWS) {
3708
+ more = true;
3709
+ break;
3710
+ }
3711
+ }
3712
+ if (rows.length === 0) {
3713
+ console.log(import_chalk25.default.dim(fuseTripped ? "Nothing found in the scanned span \u2014 older requests may still exist." : opts.errorsOnly ? "No errors logged yet." : "No requests logged yet."));
3714
+ return;
3715
+ }
3716
+ const now = Date.now();
3717
+ for (const d of groupIntoDays(rows)) {
3718
+ console.log(import_chalk25.default.bold(dayLabel(d.dayMs, now)));
3719
+ for (const r of d.rows) console.log(` ${formatRow(r)}`);
3720
+ console.log("");
3721
+ }
3722
+ const errs = rows.filter((r) => r.status >= 400).length;
3723
+ const tail = fuseTripped ? " \xB7 stopped early (long empty span) \u2014 older requests may still exist" : more ? " \xB7 showing the most recent \u2014 `--json` streams the rest" : "";
3724
+ console.log(import_chalk25.default.dim(
3725
+ `${rows.length} request${rows.length === 1 ? "" : "s"} \xB7 ${errs} error${errs === 1 ? "" : "s"}${tail}`
3726
+ ));
3727
+ }
3728
+ async function runHistoryBrowser(opts) {
3729
+ const notices = new WindowNotices();
3730
+ if (opts.json) {
3731
+ guardStdoutPipe();
3732
+ let cursor2;
3733
+ let any = false;
3734
+ for (; ; ) {
3735
+ const page = await fetchPage(opts.fetchWindow, {
3736
+ limit: JSON_PAGE_LIMIT,
3737
+ errorsOnly: opts.errorsOnly,
3738
+ cursor: cursor2
3739
+ }, notices);
3740
+ for (const r of page.rows) console.log(JSON.stringify(r));
3741
+ any = any || page.rows.length > 0;
3742
+ if (page.fuseTripped || !page.nextCursor) {
3743
+ if (!any && !page.fuseTripped) console.error(import_chalk25.default.dim(opts.errorsOnly ? "No errors logged yet." : "No requests logged yet."));
3744
+ return;
3745
+ }
3746
+ cursor2 = page.nextCursor;
3747
+ }
3748
+ }
3749
+ if (opts.plain || !process.stdout.isTTY || !process.stdin.isTTY) {
3750
+ await runPlainList(opts, notices);
3751
+ return;
3752
+ }
3753
+ const { default: inquirer3 } = await import("inquirer");
3754
+ let scanRows = [];
3755
+ let scanCursor;
3756
+ {
3757
+ const first = await fetchPage(opts.fetchWindow, {
3758
+ limit: SCAN_LIMIT,
3759
+ errorsOnly: opts.errorsOnly
3760
+ }, notices);
3761
+ scanRows = first.rows;
3762
+ scanCursor = first.nextCursor;
3763
+ if (scanRows.length === 0) {
3764
+ console.log(import_chalk25.default.dim(first.fuseTripped ? "Nothing found in the scanned span \u2014 older rows may exist." : opts.errorsOnly ? "No errors logged yet." : "No requests logged yet."));
3765
+ return;
3766
+ }
3767
+ }
3768
+ let day;
3769
+ let scanFloorUnknown = false;
3770
+ for (; ; ) {
3771
+ const days = groupIntoDays(scanRows);
3772
+ const now2 = Date.now();
3773
+ const oldestKey = days[days.length - 1]?.key;
3774
+ const choices = days.map((d) => ({
3775
+ name: `${dayLabel(d.dayMs, now2).padEnd(12)} ${String(d.rows.length).padStart(4)}${(scanCursor !== void 0 || scanFloorUnknown) && d.key === oldestKey ? "+" : ""} requests${d.errors ? import_chalk25.default.red(` ${d.errors} errors`) : ""}`,
3776
+ value: d.key
3777
+ }));
3778
+ if (scanCursor) choices.push({ name: import_chalk25.default.dim("\u2026 older days"), value: "__older__" });
3779
+ const { chosenDay } = await inquirer3.prompt([{
3780
+ type: "list",
3781
+ name: "chosenDay",
3782
+ message: "Which day?",
3783
+ choices,
3784
+ pageSize: 20
3785
+ }]);
3786
+ if (chosenDay === "__older__") {
3787
+ const more = await fetchPage(opts.fetchWindow, {
3788
+ limit: SCAN_LIMIT,
3789
+ errorsOnly: opts.errorsOnly,
3790
+ cursor: scanCursor
3791
+ }, notices);
3792
+ scanRows = scanRows.concat(more.rows);
3793
+ if (more.fuseTripped) {
3794
+ scanFloorUnknown = true;
3795
+ scanCursor = void 0;
3796
+ } else {
3797
+ scanCursor = more.nextCursor;
3798
+ if (more.rows.length === 0 && !scanCursor) console.log(import_chalk25.default.dim("That was everything \u2014 no older requests."));
3799
+ }
3800
+ continue;
3801
+ }
3802
+ day = days.find((d) => d.key === chosenDay);
3803
+ break;
3804
+ }
3805
+ const since = day.dayMs;
3806
+ const until = nextLocalDayMs(day.dayMs) - 1;
3807
+ const now = Date.now();
3808
+ let cursor;
3809
+ let pageRows = [];
3810
+ let moreCursor;
3811
+ for (; ; ) {
3812
+ const page = await fetchPage(opts.fetchWindow, {
3813
+ limit: PAGE_LIMIT,
3814
+ since,
3815
+ until,
3816
+ errorsOnly: opts.errorsOnly,
3817
+ cursor
3818
+ }, notices);
3819
+ if (page.rows.length === 0 && pageRows.length === 0) {
3820
+ console.log(import_chalk25.default.dim(page.fuseTripped ? "Stopped scanning this day (long empty span) \u2014 older rows in it may exist." : "No requests for this day."));
3821
+ return;
3822
+ }
3823
+ if (page.rows.length === 0) {
3824
+ console.log(import_chalk25.default.dim(page.fuseTripped ? "Stopped scanning further into this day (long empty span) \u2014 older rows in it may exist." : "That was everything for this day."));
3825
+ moreCursor = void 0;
3826
+ } else {
3827
+ pageRows = page.rows;
3828
+ moreCursor = page.fuseTripped ? void 0 : page.nextCursor;
3829
+ }
3830
+ console.log("");
3831
+ const choices = pageRows.map((r) => ({
3832
+ name: formatRow(r),
3833
+ value: r.request_id
3834
+ }));
3835
+ if (moreCursor) choices.push({ name: import_chalk25.default.dim("\u2026 load more"), value: "__more__" });
3836
+ choices.push({ name: import_chalk25.default.dim("quit"), value: "__quit__" });
3837
+ for (; ; ) {
3838
+ const { picked } = await inquirer3.prompt([{
3839
+ type: "list",
3840
+ name: "picked",
3841
+ message: `${dayLabel(day.dayMs, now)} \u2014 select a request for detail`,
3842
+ choices,
3843
+ pageSize: 20
3844
+ }]);
3845
+ if (picked === "__quit__") return;
3846
+ if (picked === "__more__") {
3847
+ cursor = moreCursor;
3848
+ break;
3849
+ }
3850
+ const row = pageRows.find((r) => r.request_id === picked);
3851
+ if (row) printDetail(row, opts.detail);
3852
+ }
3853
+ }
3854
+ }
3855
+ var NOISE_HEADER = /^(cf-|sec-ch-|sec-fetch-|x-forwarded-|x-real-ip$)/i;
3856
+ var HEADER_PRIORITY = [
3857
+ "authorization",
3858
+ "x-api-key",
3859
+ "content-type",
3860
+ "accept",
3861
+ "x-end-user-id",
3862
+ "user-agent",
3863
+ "host",
3864
+ "origin",
3865
+ "referer",
3866
+ "content-length",
3867
+ "cookie"
3868
+ ];
3869
+ var PANE_INDENT = " ".repeat(13);
3870
+ function wrapCells(items, width) {
3871
+ const cols = Math.max(40, width - PANE_INDENT.length);
3872
+ const lines = [];
3873
+ let line = "";
3874
+ for (const it of items) {
3875
+ if (line && line.length + 2 + it.length > cols) {
3876
+ lines.push(line);
3877
+ line = "";
3878
+ }
3879
+ line = line ? `${line} ${it}` : it;
3880
+ }
3881
+ if (line) lines.push(line);
3882
+ return lines.join(`
3883
+ ${PANE_INDENT}`);
3884
+ }
3885
+ function renderHeaders(h) {
3886
+ if (!h) return import_chalk25.default.dim("\u2014");
3887
+ const width = process.stdout.columns || 100;
3888
+ if (h.values) {
3889
+ const e = Object.entries(h.values);
3890
+ if (!e.length) return import_chalk25.default.dim("none");
3891
+ const rank = (k) => {
3892
+ const i = HEADER_PRIORITY.indexOf(k.toLowerCase());
3893
+ return i === -1 ? NOISE_HEADER.test(k) ? 999 : 500 : i;
3894
+ };
3895
+ return e.sort((a, b) => rank(a[0]) - rank(b[0])).map(([k, v]) => `${import_chalk25.default.dim(k + ":")} ${v}`).join(`
3896
+ ${PANE_INDENT}`);
3897
+ }
3898
+ if (!h.names?.length) return import_chalk25.default.dim("none");
3899
+ const names = [...h.names];
3900
+ const noise = names.filter((n) => NOISE_HEADER.test(n));
3901
+ const shown = names.filter((n) => !NOISE_HEADER.test(n)).sort((a, b) => {
3902
+ const ia = HEADER_PRIORITY.indexOf(a.toLowerCase());
3903
+ const ib = HEADER_PRIORITY.indexOf(b.toLowerCase());
3904
+ return (ia === -1 ? 500 : ia) - (ib === -1 ? 500 : ib) || a.localeCompare(b);
3905
+ });
3906
+ const body = shown.length ? wrapCells(shown, width) : import_chalk25.default.dim("(all forwarding headers)");
3907
+ const tail = noise.length ? `
3908
+ ${PANE_INDENT}${import_chalk25.default.dim(`+ ${noise.length} forwarding/client-hint header${noise.length === 1 ? "" : "s"} (cf-*, sec-*)`)}` : "";
3909
+ return `${body}${tail} ${import_chalk25.default.dim("(names only)")}`;
3910
+ }
3911
+ function renderBody(b) {
3912
+ if (!b) return import_chalk25.default.dim("\u2014");
3913
+ const meta = [b.content_type, b.size != null ? `${b.size}B` : null, b.truncated ? "truncated" : null].filter(Boolean).join(" \xB7 ");
3914
+ if (b.not_captured) return import_chalk25.default.dim(`not captured (${b.not_captured})${meta ? " \xB7 " + meta : ""}`);
3915
+ const isShape = b.value === void 0 && b.shape !== void 0;
3916
+ const content = b.value !== void 0 ? b.value : b.shape;
3917
+ if (content === null || content === void 0 || content === "" || content === "empty") {
3918
+ return import_chalk25.default.dim(`empty${meta ? " \xB7 " + meta : ""}`);
3919
+ }
3920
+ const text = typeof content === "string" ? content : JSON.stringify(content, null, 2);
3921
+ const head = isShape ? import_chalk25.default.dim("shape only (values not captured)\n ") : "";
3922
+ return head + text.split("\n").join("\n ");
3923
+ }
3924
+ function pane(title, tone, view, empty) {
3925
+ console.log(` ${tone(title)}`);
3926
+ if (empty !== void 0) {
3927
+ console.log(` ${import_chalk25.default.dim(empty)}`);
3928
+ return;
3929
+ }
3930
+ const v = view ?? {};
3931
+ if (v.url) console.log(` ${import_chalk25.default.dim("target")} ${v.url}`);
3932
+ if (v.status != null) {
3933
+ console.log(` ${import_chalk25.default.dim("status")} ${statusColor(v.status)(String(v.status))}${v.status_text ? import_chalk25.default.dim(" " + v.status_text) : ""}`);
3934
+ }
3935
+ if (v.raw_path) console.log(` ${import_chalk25.default.dim("path")} ${v.raw_path}`);
3936
+ if (v.query && Object.keys(v.query).length) {
3937
+ console.log(` ${import_chalk25.default.dim("query")} ${Object.entries(v.query).map(([k, q]) => `${k}=${q}`).join(" ")}`);
3938
+ }
3939
+ if (v.jwt !== void 0) console.log(` ${import_chalk25.default.dim("jwt")} ${JSON.stringify(v.jwt)}`);
3940
+ console.log(` ${import_chalk25.default.dim("headers")} ${renderHeaders(v.headers)}`);
3941
+ console.log(` ${import_chalk25.default.dim("body")} ${renderBody(v.body)}`);
3942
+ }
3943
+ function printDetail(r, mode = "producer") {
3944
+ const v = r.views;
3945
+ console.log("");
3946
+ console.log(import_chalk25.default.bold(`${r.method} ${r.route}`) + " " + statusColor(r.status)(String(r.status)) + import_chalk25.default.dim(` ${r.duration_ms}ms`));
3947
+ const cost = typeof r.cost_cents === "number" && r.cost_cents > 0 ? ` \xB7 ${r.cost_cents}\xA2` : "";
3948
+ console.log(import_chalk25.default.dim(` request_id ${r.request_id}${r.environment ? ` \xB7 env ${r.environment}` : ""}${r.consumer_id ? ` \xB7 consumer ${r.consumer_id}` : ""}${cost}`));
3949
+ console.log("");
3950
+ if (!v) {
3951
+ console.log(import_chalk25.default.dim(" No view detail captured for this request.\n"));
3952
+ return;
3953
+ }
3954
+ pane("Request (before) \u2014 client \u2192 APIblaze", import_chalk25.default.cyan, v.request);
3955
+ if (mode === "producer") {
3956
+ pane(
3957
+ "Request (after) \u2014 APIblaze \u2192 upstream",
3958
+ import_chalk25.default.cyan,
3959
+ v.forwarded_request,
3960
+ v.forwarded_request ? void 0 : "Request did not reach an upstream."
3961
+ );
3962
+ pane(
3963
+ "Response (before) \u2014 upstream \u2192 APIblaze",
3964
+ import_chalk25.default.magenta,
3965
+ v.upstream_response,
3966
+ v.upstream_response ? void 0 : "No upstream response."
3967
+ );
3968
+ }
3969
+ pane("Response (after) \u2014 APIblaze \u2192 client", import_chalk25.default.magenta, v.response);
3970
+ console.log("");
3971
+ }
3972
+
3973
+ // src/commands/logs.ts
3974
+ async function resolve(projectArg, versionArg, opts) {
3975
+ try {
3976
+ getAccessToken();
3977
+ } catch (err) {
3978
+ const consumerOnly = !loadCredentials() && !!loadConsumer();
3979
+ throw new Error(
3980
+ err.message + (consumerOnly ? "\nYou're signed in as an API Consumer. A proxy's full logs need a Producer login; for your own requests run `apiblaze consumer logs`." : "")
3981
+ );
3982
+ }
3983
+ const { teamId } = await resolveTeam(opts.team);
3984
+ const match = await resolveProject(teamId, projectArg, versionArg);
3985
+ const projectId = match.projectId;
3986
+ const version2 = versionArg || match.apiVersion;
3987
+ let tenant2 = opts.tenant;
3988
+ if (!tenant2) {
3989
+ const res = await admin({
3990
+ path: `/projects/${projectId}/${version2}/tenants`,
3991
+ method: "GET",
3992
+ summary: "list project tenants"
3993
+ });
3994
+ const tenants = res?.tenants ?? [];
3995
+ if (tenants.length === 0) {
3996
+ throw new Error(`No tenants attached to ${match.projectName}@${version2} \u2014 nothing to read logs for.`);
3997
+ } else if (tenants.length === 1) {
3998
+ tenant2 = tenants[0].tenant_name;
3999
+ } else if (opts.json || !process.stdin.isTTY) {
4000
+ throw new Error(`Project has ${tenants.length} tenants \u2014 pass --tenant <slug>. Attached: ${tenants.map((t) => t.tenant_name).join(", ")}`);
4001
+ } else {
4002
+ const { default: inquirer3 } = await import("inquirer");
4003
+ const ans = await inquirer3.prompt([{
4004
+ type: "list",
4005
+ name: "tenant",
4006
+ message: "Which tenant?",
4007
+ choices: tenants.map((t) => ({
4008
+ name: t.display_name ? `${t.display_name} (${t.tenant_name})` : t.tenant_name,
4009
+ value: t.tenant_name
4010
+ }))
4011
+ }]);
4012
+ tenant2 = ans.tenant;
4013
+ }
4014
+ }
4015
+ return { projectId, projectName: match.projectName, version: version2, tenant: tenant2 };
4016
+ }
4017
+ function otherPlaneHint() {
4018
+ if (loadConsumer()) console.log(import_chalk26.default.dim(" \u2039consumer view: apiblaze consumer logs\u203A"));
4019
+ }
4020
+ function fetchWindow(projectId, version2, tenant2) {
4021
+ return (params) => {
4022
+ const q = new URLSearchParams({ tenant: tenant2, limit: String(params.limit) });
4023
+ if (params.since !== void 0) q.set("since", String(params.since));
4024
+ if (params.until !== void 0) q.set("until", String(params.until));
4025
+ if (params.errorsOnly) q.set("errors_only", "true");
4026
+ if (params.cursor) q.set("cursor", params.cursor);
4027
+ return admin({
4028
+ path: `/projects/${projectId}/${version2}/logs/window?${q.toString()}`,
4029
+ method: "GET",
4030
+ summary: "read log window"
4031
+ });
4032
+ };
4033
+ }
4034
+ async function runLogsTail(projectArg, versionArg, opts) {
4035
+ const { projectId, projectName, version: version2, tenant: tenant2 } = await resolve(projectArg, versionArg, opts);
4036
+ if (!opts.json) {
4037
+ console.log(import_chalk26.default.bold("API Producer logs") + import_chalk26.default.dim(` \xB7 ${projectName}@${version2} \xB7 tenant ${tenant2}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
4038
+ console.log(import_chalk26.default.dim("every caller's requests, with cost and upstream detail"));
4039
+ otherPlaneHint();
4040
+ }
4041
+ await runTailSession({
4042
+ mintTicket: async () => {
4043
+ const res = await admin({
4044
+ path: `/projects/${projectId}/${version2}/logs/tail-ticket?tenant=${encodeURIComponent(tenant2)}`,
4045
+ method: "POST",
4046
+ summary: "mint log tail ticket"
4047
+ });
4048
+ if (!res?.ws_url) throw new Error("No ws_url returned by tail-ticket");
4049
+ return res.ws_url;
4050
+ },
4051
+ errorsOnly: opts.errorsOnly,
4052
+ json: opts.json,
4053
+ detail: "producer"
4054
+ });
4055
+ }
4056
+ async function runLogsList(projectArg, versionArg, opts) {
4057
+ const { projectId, projectName, version: version2, tenant: tenant2 } = await resolve(projectArg, versionArg, opts);
4058
+ if (!opts.json) {
4059
+ console.log(import_chalk26.default.bold("API Producer logs") + import_chalk26.default.dim(` \xB7 ${projectName}@${version2} \xB7 tenant ${tenant2}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
4060
+ console.log(import_chalk26.default.dim("every caller's requests, with cost and upstream detail"));
4061
+ otherPlaneHint();
4062
+ console.log("");
4063
+ }
4064
+ await runHistoryBrowser({
4065
+ fetchWindow: fetchWindow(projectId, version2, tenant2),
4066
+ errorsOnly: opts.errorsOnly,
4067
+ json: opts.json,
4068
+ plain: opts.plain,
4069
+ detail: "producer"
4070
+ });
4071
+ }
4072
+
4073
+ // src/commands/consumer-logs.ts
4074
+ var import_chalk27 = __toESM(require("chalk"));
4075
+ init_auth();
4076
+ var CONSUMER_LOGS_BASE = process.env.APIBLAZE_CONSUMER_LOGS_BASE || "https://logs.apiblaze.com/consumer";
4077
+ function requireConsumer() {
4078
+ const c = loadConsumer();
4079
+ if (!c) {
4080
+ console.error(import_chalk27.default.red("Not logged in as an API Consumer. Run `apiblaze consumer login` first."));
4081
+ if (loadCredentials()) {
4082
+ console.error(import_chalk27.default.dim("You're signed in as an API Producer \u2014 for a proxy's full logs run `apiblaze logs <project>`."));
4083
+ }
4084
+ process.exit(1);
4085
+ }
4086
+ return { creds: c };
4087
+ }
4088
+ async function consumerLogsFetch(session, suffix, init) {
4089
+ const fresh = await validConsumerToken(session.creds) ?? session.creds;
4090
+ session.creds = fresh;
4091
+ const res = await fetch(`${CONSUMER_LOGS_BASE}${suffix}`, {
4092
+ ...init,
4093
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${fresh.accessToken}`, ...init?.headers ?? {} }
4094
+ });
4095
+ let data = null;
4096
+ try {
4097
+ data = await res.json();
4098
+ } catch {
4099
+ }
4100
+ if (res.status === 404 || res.status === 405 || res.status === 501) {
4101
+ throw new Error("Consumer log access is not available on this tenant yet.");
4102
+ }
4103
+ if (res.status === 401) {
4104
+ throw new Error("Your consumer session was rejected \u2014 run `apiblaze consumer login` again.");
4105
+ }
4106
+ if (!res.ok) {
4107
+ const msg = data?.error ?? res.statusText;
4108
+ throw new Error(typeof msg === "string" ? msg : JSON.stringify(msg));
4109
+ }
4110
+ return data;
4111
+ }
4112
+ function otherPlaneHint2() {
4113
+ if (loadCredentials()) console.log(import_chalk27.default.dim(" \u2039producer view: apiblaze logs <project>\u203A"));
4114
+ }
4115
+ function fetchWindow2(session, project) {
4116
+ return (params) => {
4117
+ const q = new URLSearchParams({ limit: String(params.limit) });
4118
+ if (project) q.set("project", project);
4119
+ if (params.since !== void 0) q.set("since", String(params.since));
4120
+ if (params.until !== void 0) q.set("until", String(params.until));
4121
+ if (params.errorsOnly) q.set("errors_only", "true");
4122
+ if (params.cursor) q.set("cursor", params.cursor);
4123
+ return consumerLogsFetch(session, `/logs/window?${q.toString()}`);
4124
+ };
4125
+ }
4126
+ async function runConsumerLogsTail(opts) {
4127
+ const session = requireConsumer();
4128
+ if (!opts.json) {
4129
+ console.log(import_chalk27.default.bold("API Consumer logs") + import_chalk27.default.dim(` \xB7 your own requests on ${session.creds.tenant}${opts.project ? ` \xB7 ${opts.project}` : ""}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
4130
+ console.log(import_chalk27.default.dim("cost and upstream details are producer-only and are not sent"));
4131
+ otherPlaneHint2();
4132
+ }
4133
+ await runTailSession({
4134
+ mintTicket: async () => {
4135
+ const q = opts.project ? `?project=${encodeURIComponent(opts.project)}` : "";
4136
+ const res = await consumerLogsFetch(session, `/logs/tail-ticket${q}`, { method: "POST" });
4137
+ if (!res?.ws_url) throw new Error("No ws_url returned by tail-ticket");
4138
+ return res.ws_url;
4139
+ },
4140
+ errorsOnly: opts.errorsOnly,
4141
+ json: opts.json,
4142
+ detail: "consumer"
4143
+ });
4144
+ }
4145
+ async function runConsumerLogsList(opts) {
4146
+ const session = requireConsumer();
4147
+ if (!opts.json) {
4148
+ console.log(import_chalk27.default.bold("API Consumer logs") + import_chalk27.default.dim(` \xB7 your own requests on ${session.creds.tenant}${opts.project ? ` \xB7 ${opts.project}` : ""}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
4149
+ console.log(import_chalk27.default.dim("cost and upstream details are producer-only and are not sent"));
4150
+ otherPlaneHint2();
4151
+ console.log("");
4152
+ }
4153
+ await runHistoryBrowser({
4154
+ fetchWindow: fetchWindow2(session, opts.project),
4155
+ errorsOnly: opts.errorsOnly,
4156
+ json: opts.json,
4157
+ plain: opts.plain,
4158
+ detail: "consumer"
4159
+ });
4160
+ }
4161
+
4162
+ // src/commands/config.ts
4163
+ var import_chalk28 = __toESM(require("chalk"));
3309
4164
  var import_ora8 = __toESM(require("ora"));
3310
4165
  init_admin();
3311
4166
  init_resolve();
@@ -3329,7 +4184,7 @@ async function patchConfig(project, opts, body, summary) {
3329
4184
  }
3330
4185
  async function runTargetSet(project, opts) {
3331
4186
  if (!opts.url) {
3332
- console.error(import_chalk25.default.red("--url is required."));
4187
+ console.error(import_chalk28.default.red("--url is required."));
3333
4188
  process.exit(1);
3334
4189
  }
3335
4190
  const body = opts.env ? { environments: { [opts.env]: { target: opts.url } } } : { target_url: opts.url };
@@ -3342,40 +4197,40 @@ async function runThrottleSet(project, opts) {
3342
4197
  if (opts.quota !== void 0) throttling.proxyQuota = Number(opts.quota);
3343
4198
  if (opts.period !== void 0) {
3344
4199
  if (!["daily", "weekly", "monthly"].includes(opts.period)) {
3345
- console.error(import_chalk25.default.red("--period must be daily, weekly, or monthly."));
4200
+ console.error(import_chalk28.default.red("--period must be daily, weekly, or monthly."));
3346
4201
  process.exit(1);
3347
4202
  }
3348
4203
  throttling.quotaPeriod = opts.period;
3349
4204
  }
3350
4205
  if (Object.keys(throttling).length === 0) {
3351
- console.error(import_chalk25.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
4206
+ console.error(import_chalk28.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
3352
4207
  process.exit(1);
3353
4208
  }
3354
4209
  await patchConfig(project, opts, { throttling }, `Update throttling ${JSON.stringify(throttling)}`);
3355
4210
  }
3356
4211
  async function runRename(project, opts) {
3357
4212
  if (!opts.displayName) {
3358
- console.error(import_chalk25.default.red("--display-name is required."));
4213
+ console.error(import_chalk28.default.red("--display-name is required."));
3359
4214
  process.exit(1);
3360
4215
  }
3361
4216
  await patchConfig(project, opts, { display_name: opts.displayName }, `Rename \u2192 "${opts.displayName}"`);
3362
4217
  }
3363
4218
 
3364
4219
  // src/commands/config-browse.ts
3365
- var import_chalk34 = __toESM(require("chalk"));
4220
+ var import_chalk37 = __toESM(require("chalk"));
3366
4221
  var import_ora16 = __toESM(require("ora"));
3367
4222
  init_admin();
3368
4223
  init_auth();
3369
4224
  init_resolve();
3370
4225
 
3371
4226
  // src/commands/domain.ts
3372
- var import_chalk26 = __toESM(require("chalk"));
4227
+ var import_chalk29 = __toESM(require("chalk"));
3373
4228
  var import_ora9 = __toESM(require("ora"));
3374
4229
  init_admin();
3375
4230
  init_resolve();
3376
4231
  async function runDomainAdd(project, opts) {
3377
4232
  if (!opts.domain) {
3378
- console.error(import_chalk26.default.red("--domain is required."));
4233
+ console.error(import_chalk29.default.red("--domain is required."));
3379
4234
  process.exit(1);
3380
4235
  }
3381
4236
  const { teamId } = await resolveTeam(opts.team);
@@ -3394,9 +4249,9 @@ async function runDomainAdd(project, opts) {
3394
4249
  console.log(JSON.stringify(out));
3395
4250
  return;
3396
4251
  }
3397
- if (out?.cnameRecord) console.log(` ${import_chalk26.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
3398
- if (out?.dcv) console.log(` ${import_chalk26.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
3399
- if (out?.id) console.log(import_chalk26.default.dim(` domain id: ${out.id}`));
4252
+ if (out?.cnameRecord) console.log(` ${import_chalk29.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
4253
+ if (out?.dcv) console.log(` ${import_chalk29.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
4254
+ if (out?.id) console.log(import_chalk29.default.dim(` domain id: ${out.id}`));
3400
4255
  } catch (err) {
3401
4256
  spinner.fail("Domain registration failed.");
3402
4257
  throw err;
@@ -3416,14 +4271,14 @@ async function runDomainList(project, opts) {
3416
4271
  return;
3417
4272
  }
3418
4273
  if (!domains.length) {
3419
- console.log(import_chalk26.default.yellow("No custom domains."));
4274
+ console.log(import_chalk29.default.yellow("No custom domains."));
3420
4275
  return;
3421
4276
  }
3422
- for (const d of domains) console.log(` ${import_chalk26.default.bold(d.hostname)} ${import_chalk26.default.dim(d.status ?? "")} ${import_chalk26.default.dim(d.id ?? "")}`);
4277
+ for (const d of domains) console.log(` ${import_chalk29.default.bold(d.hostname)} ${import_chalk29.default.dim(d.status ?? "")} ${import_chalk29.default.dim(d.id ?? "")}`);
3423
4278
  }
3424
4279
  async function runDomainStatus(project, opts) {
3425
4280
  if (!opts.id) {
3426
- console.error(import_chalk26.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
4281
+ console.error(import_chalk29.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
3427
4282
  process.exit(1);
3428
4283
  }
3429
4284
  const { teamId } = await resolveTeam(opts.team);
@@ -3433,11 +4288,11 @@ async function runDomainStatus(project, opts) {
3433
4288
  path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}/status`,
3434
4289
  summary: `Check custom-domain status`
3435
4290
  });
3436
- console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk26.default.bold(out?.hostname ?? opts.id)}: ${import_chalk26.default.cyan(out?.status ?? "unknown")}`);
4291
+ console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk29.default.bold(out?.hostname ?? opts.id)}: ${import_chalk29.default.cyan(out?.status ?? "unknown")}`);
3437
4292
  }
3438
4293
  async function runDomainRemove(project, opts) {
3439
4294
  if (!opts.id) {
3440
- console.error(import_chalk26.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
4295
+ console.error(import_chalk29.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
3441
4296
  process.exit(1);
3442
4297
  }
3443
4298
  const { teamId } = await resolveTeam(opts.team);
@@ -3475,7 +4330,7 @@ async function runDomainSetBase(project, opts) {
3475
4330
  }
3476
4331
 
3477
4332
  // src/commands/tenant.ts
3478
- var import_chalk29 = __toESM(require("chalk"));
4333
+ var import_chalk32 = __toESM(require("chalk"));
3479
4334
  var import_ora12 = __toESM(require("ora"));
3480
4335
  init_admin();
3481
4336
  init_resolve();
@@ -3484,20 +4339,20 @@ init_tenant_pick();
3484
4339
  async function runTenantUse(query, opts) {
3485
4340
  const creds = loadCredentials();
3486
4341
  if (!creds) {
3487
- console.error(import_chalk29.default.red("Not logged in. Run `apiblaze login` first."));
4342
+ console.error(import_chalk32.default.red("Not logged in. Run `apiblaze login` first."));
3488
4343
  process.exit(1);
3489
4344
  }
3490
4345
  if (opts.clear) {
3491
4346
  delete creds.activeTenant;
3492
4347
  saveCredentials(creds);
3493
- console.log(import_chalk29.default.green("Tenant scope cleared."));
4348
+ console.log(import_chalk32.default.green("Tenant scope cleared."));
3494
4349
  return;
3495
4350
  }
3496
4351
  const { teamId } = await resolveTeam(opts.team);
3497
4352
  const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
3498
4353
  if (!slug) process.exit(1);
3499
4354
  saveCredentials({ ...creds, activeTenant: slug });
3500
- console.log(import_chalk29.default.green(`Tenant scope set to ${import_chalk29.default.bold(slug)}.`) + import_chalk29.default.dim(" (clear with `apiblaze tenant use --clear`)"));
4355
+ console.log(import_chalk32.default.green(`Tenant scope set to ${import_chalk32.default.bold(slug)}.`) + import_chalk32.default.dim(" (clear with `apiblaze tenant use --clear`)"));
3501
4356
  }
3502
4357
  async function runTenantList(opts) {
3503
4358
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -3514,22 +4369,22 @@ async function runTenantList(opts) {
3514
4369
  return;
3515
4370
  }
3516
4371
  if (!tenants.length) {
3517
- console.log(import_chalk29.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
4372
+ console.log(import_chalk32.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
3518
4373
  return;
3519
4374
  }
3520
4375
  for (const t of tenants) {
3521
4376
  const name = typeof t === "string" ? t : t.tenant_name;
3522
- const display = typeof t === "string" ? "" : import_chalk29.default.dim(` ${t.display_name ?? ""}`);
3523
- console.log(` ${import_chalk29.default.bold(name)}${display}`);
4377
+ const display = typeof t === "string" ? "" : import_chalk32.default.dim(` ${t.display_name ?? ""}`);
4378
+ console.log(` ${import_chalk32.default.bold(name)}${display}`);
3524
4379
  }
3525
4380
  const total = out?.total ?? tenants.length;
3526
4381
  if (total > tenants.length) {
3527
- console.log(import_chalk29.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
4382
+ console.log(import_chalk32.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
3528
4383
  }
3529
4384
  }
3530
4385
  async function runTenantCreate(opts) {
3531
4386
  if (!opts.name) {
3532
- console.error(import_chalk29.default.red("--name (display name) is required."));
4387
+ console.error(import_chalk32.default.red("--name (display name) is required."));
3533
4388
  process.exit(1);
3534
4389
  }
3535
4390
  const { teamId } = await resolveTeam(opts.team);
@@ -3541,7 +4396,7 @@ async function runTenantCreate(opts) {
3541
4396
  body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
3542
4397
  summary: `Create tenant "${opts.name}"`
3543
4398
  });
3544
- spinner.succeed(`Created tenant ${import_chalk29.default.bold(out?.tenant_name ?? opts.name)}.`);
4399
+ spinner.succeed(`Created tenant ${import_chalk32.default.bold(out?.tenant_name ?? opts.name)}.`);
3545
4400
  if (opts.json) console.log(JSON.stringify(out));
3546
4401
  } catch (err) {
3547
4402
  spinner.fail("Tenant create failed.");
@@ -3550,7 +4405,7 @@ async function runTenantCreate(opts) {
3550
4405
  }
3551
4406
  async function runTenantAttach(project, opts) {
3552
4407
  if (!opts.tenant) {
3553
- console.error(import_chalk29.default.red("--tenant <slug> is required."));
4408
+ console.error(import_chalk32.default.red("--tenant <slug> is required."));
3554
4409
  process.exit(1);
3555
4410
  }
3556
4411
  const { teamId } = await resolveTeam(opts.team);
@@ -3578,7 +4433,7 @@ async function runTenantDelete(slug, opts) {
3578
4433
  { type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
3579
4434
  ]);
3580
4435
  if (!confirm) {
3581
- console.log(import_chalk29.default.dim("Aborted."));
4436
+ console.log(import_chalk32.default.dim("Aborted."));
3582
4437
  return;
3583
4438
  }
3584
4439
  }
@@ -3597,7 +4452,7 @@ async function runTenantDelete(slug, opts) {
3597
4452
  }
3598
4453
  async function runTenantCors(opts) {
3599
4454
  if (!opts.tenant) {
3600
- console.error(import_chalk29.default.red("--tenant <slug> is required."));
4455
+ console.error(import_chalk32.default.red("--tenant <slug> is required."));
3601
4456
  process.exit(1);
3602
4457
  }
3603
4458
  const { teamId } = await resolveTeam(opts.team);
@@ -3619,7 +4474,7 @@ async function runTenantCors(opts) {
3619
4474
  }
3620
4475
 
3621
4476
  // src/commands/tenant-drill.ts
3622
- var import_chalk30 = __toESM(require("chalk"));
4477
+ var import_chalk33 = __toESM(require("chalk"));
3623
4478
  var import_ora13 = __toESM(require("ora"));
3624
4479
  var import_crypto = require("crypto");
3625
4480
  init_admin();
@@ -3650,15 +4505,15 @@ async function validScopedTenant(teamId, query) {
3650
4505
  const next = { ...creds };
3651
4506
  delete next.activeTenant;
3652
4507
  saveCredentials2(next);
3653
- console.log(import_chalk30.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
4508
+ console.log(import_chalk33.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
3654
4509
  return void 0;
3655
4510
  }
3656
4511
  async function tenantHome(teamId, tenant2) {
3657
4512
  const { default: inquirer3 } = await import("inquirer");
3658
4513
  const base2 = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
3659
- console.log(import_chalk30.default.bold(`
4514
+ console.log(import_chalk33.default.bold(`
3660
4515
  Tenant ${tenant2}`));
3661
- console.log(import_chalk30.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
4516
+ console.log(import_chalk33.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
3662
4517
  for (; ; ) {
3663
4518
  const spinner = (0, import_ora13.default)("Reading tenant state...").start();
3664
4519
  const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
@@ -3674,18 +4529,18 @@ Tenant ${tenant2}`));
3674
4529
  const nClients = Array.isArray(clients) ? clients.length : 0;
3675
4530
  const nOpaque = opaque?.opaque?.endpoint ? 1 : 0;
3676
4531
  const nLogin = nClients + nIssuers + nOpaque;
3677
- const onOff = (b) => b ? import_chalk30.default.green("on") : import_chalk30.default.dim("off");
4532
+ const onOff = (b) => b ? import_chalk33.default.green("on") : import_chalk33.default.dim("off");
3678
4533
  const { pick: pick2 } = await inquirer3.prompt([{
3679
4534
  type: "list",
3680
4535
  name: "pick",
3681
4536
  message: `Tenant ${tenant2}:`,
3682
4537
  pageSize: 12,
3683
4538
  choices: [
3684
- { name: `Login methods (${nLogin}) ${import_chalk30.default.dim("how your consumers sign in")}`, value: "login" },
3685
- { name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk30.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
3686
- { name: `Portal admins (${nEmails}) ${import_chalk30.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
3687
- new inquirer3.Separator(import_chalk30.default.dim(" Settings")),
3688
- { name: `CORS: ${cors?.cors ? import_chalk30.default.cyan(JSON.stringify(cors.cors)) : import_chalk30.default.dim("(unset)")} ${import_chalk30.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
4539
+ { name: `Login methods (${nLogin}) ${import_chalk33.default.dim("how your consumers sign in")}`, value: "login" },
4540
+ { name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk33.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
4541
+ { name: `Portal admins (${nEmails}) ${import_chalk33.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
4542
+ new inquirer3.Separator(import_chalk33.default.dim(" Settings")),
4543
+ { name: `CORS: ${cors?.cors ? import_chalk33.default.cyan(JSON.stringify(cors.cors)) : import_chalk33.default.dim("(unset)")} ${import_chalk33.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
3689
4544
  { name: "\u2190 Back", value: "back" }
3690
4545
  ]
3691
4546
  }]);
@@ -3698,7 +4553,7 @@ Tenant ${tenant2}`));
3698
4553
  case "iam": {
3699
4554
  const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
3700
4555
  await admin({ method: "PATCH", path: `${base2}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
3701
- console.log(import_chalk30.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
4556
+ console.log(import_chalk33.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
3702
4557
  break;
3703
4558
  }
3704
4559
  case "cors": {
@@ -3711,11 +4566,11 @@ Tenant ${tenant2}`));
3711
4566
  if (v === "") break;
3712
4567
  const parsed = v === "null" ? null : safeJson(v);
3713
4568
  if (parsed === void 0) {
3714
- console.log(import_chalk30.default.yellow(" Not valid JSON \u2014 unchanged."));
4569
+ console.log(import_chalk33.default.yellow(" Not valid JSON \u2014 unchanged."));
3715
4570
  break;
3716
4571
  }
3717
4572
  await admin({ method: "PUT", path: `${base2}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
3718
- console.log(import_chalk30.default.green(" CORS updated."));
4573
+ console.log(import_chalk33.default.green(" CORS updated."));
3719
4574
  break;
3720
4575
  }
3721
4576
  case "emails":
@@ -3739,15 +4594,15 @@ async function loginMethodsMenu(teamId, tenant2, base2) {
3739
4594
  const choices = [];
3740
4595
  for (const c of appClients) {
3741
4596
  const nP = (c.providers ?? []).length || c.providers_count || 0;
3742
- choices.push({ name: `${import_chalk30.default.cyan("APIblaze-hosted")} ${import_chalk30.default.bold(c.name ?? c.clientId)} ${import_chalk30.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk30.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
4597
+ choices.push({ name: `${import_chalk33.default.cyan("APIblaze-hosted")} ${import_chalk33.default.bold(c.name ?? c.clientId)} ${import_chalk33.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk33.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
3743
4598
  }
3744
4599
  for (const i of issuers) {
3745
- choices.push({ name: `${import_chalk30.default.cyan("Your own \u2014 JWT ")} ${import_chalk30.default.bold(i.iss)} ${import_chalk30.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
4600
+ choices.push({ name: `${import_chalk33.default.cyan("Your own \u2014 JWT ")} ${import_chalk33.default.bold(i.iss)} ${import_chalk33.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
3746
4601
  }
3747
4602
  if (opaque?.endpoint) {
3748
- choices.push({ name: `${import_chalk30.default.cyan("Your own \u2014 opaque")} ${import_chalk30.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
4603
+ choices.push({ name: `${import_chalk33.default.cyan("Your own \u2014 opaque")} ${import_chalk33.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
3749
4604
  }
3750
- if (!choices.length) console.log(import_chalk30.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
4605
+ if (!choices.length) console.log(import_chalk33.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
3751
4606
  const { pick: pick2 } = await inquirer3.prompt([{
3752
4607
  type: "list",
3753
4608
  name: "pick",
@@ -3787,9 +4642,9 @@ async function addLoginMethod(teamId, tenant2, base2) {
3787
4642
  message: "How do people log in?",
3788
4643
  pageSize: 8,
3789
4644
  choices: [
3790
- { name: `APIblaze hosted login page ${import_chalk30.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
3791
- { name: `Your own hosted login \u2014 JWT ${import_chalk30.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
3792
- { name: `Your own login \u2014 opaque token ${import_chalk30.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
4645
+ { name: `APIblaze hosted login page ${import_chalk33.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
4646
+ { name: `Your own hosted login \u2014 JWT ${import_chalk33.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
4647
+ { name: `Your own login \u2014 opaque token ${import_chalk33.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
3793
4648
  { name: "\u2190 Back", value: null }
3794
4649
  ]
3795
4650
  }]);
@@ -3813,8 +4668,8 @@ function safeJson(s) {
3813
4668
  async function emailsMenu(base2, emails) {
3814
4669
  const { default: inquirer3 } = await import("inquirer");
3815
4670
  console.log();
3816
- if (!emails.length) console.log(import_chalk30.default.dim(" No consumer-admin emails."));
3817
- for (const e of emails) console.log(` ${import_chalk30.default.bold(e.email ?? e)} ${import_chalk30.default.dim(e.status ?? "")}`);
4671
+ if (!emails.length) console.log(import_chalk33.default.dim(" No consumer-admin emails."));
4672
+ for (const e of emails) console.log(` ${import_chalk33.default.bold(e.email ?? e)} ${import_chalk33.default.dim(e.status ?? "")}`);
3818
4673
  const { act } = await inquirer3.prompt([{
3819
4674
  type: "list",
3820
4675
  name: "act",
@@ -3829,7 +4684,7 @@ async function emailsMenu(base2, emails) {
3829
4684
  if (act === "add") {
3830
4685
  const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
3831
4686
  await admin({ method: "POST", path: `${base2}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
3832
- console.log(import_chalk30.default.green(` ${email} added.`));
4687
+ console.log(import_chalk33.default.green(` ${email} added.`));
3833
4688
  } else {
3834
4689
  const { e } = await inquirer3.prompt([{
3835
4690
  type: "list",
@@ -3839,7 +4694,7 @@ async function emailsMenu(base2, emails) {
3839
4694
  }]);
3840
4695
  if (!e) return;
3841
4696
  await admin({ method: "DELETE", path: `${base2}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
3842
- console.log(import_chalk30.default.green(` ${e} removed.`));
4697
+ console.log(import_chalk33.default.green(` ${e} removed.`));
3843
4698
  }
3844
4699
  }
3845
4700
  async function addIssuer(base2) {
@@ -3860,19 +4715,19 @@ async function addIssuer(base2) {
3860
4715
  body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
3861
4716
  summary: `Add external issuer ${a.iss.trim()}`
3862
4717
  });
3863
- console.log(import_chalk30.default.green(" JWT login method saved."));
4718
+ console.log(import_chalk33.default.green(" JWT login method saved."));
3864
4719
  }
3865
4720
  async function issuerHome(base2, issuer) {
3866
4721
  const { default: inquirer3 } = await import("inquirer");
3867
4722
  console.log(`
3868
- ${import_chalk30.default.bold(issuer.iss)} ${import_chalk30.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
4723
+ ${import_chalk33.default.bold(issuer.iss)} ${import_chalk33.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
3869
4724
  const { act } = await inquirer3.prompt([{
3870
4725
  type: "list",
3871
4726
  name: "act",
3872
4727
  message: "This JWT login method:",
3873
4728
  choices: [
3874
4729
  { name: "Replace it (re-enter details)", value: "edit" },
3875
- { name: import_chalk30.default.red("Delete it"), value: "rm" },
4730
+ { name: import_chalk33.default.red("Delete it"), value: "rm" },
3876
4731
  { name: "\u2190 Back", value: "back" }
3877
4732
  ]
3878
4733
  }]);
@@ -3888,7 +4743,7 @@ async function issuerHome(base2, issuer) {
3888
4743
  path: `${base2}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
3889
4744
  summary: `Delete issuer ${issuer.iss}`
3890
4745
  });
3891
- console.log(import_chalk30.default.green(" Deleted."));
4746
+ console.log(import_chalk33.default.green(" Deleted."));
3892
4747
  }
3893
4748
  async function setOpaque(base2, cur) {
3894
4749
  const { default: inquirer3 } = await import("inquirer");
@@ -3897,19 +4752,19 @@ async function setOpaque(base2, cur) {
3897
4752
  { type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
3898
4753
  ]);
3899
4754
  await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
3900
- console.log(import_chalk30.default.green(" Opaque login method set."));
4755
+ console.log(import_chalk33.default.green(" Opaque login method set."));
3901
4756
  }
3902
4757
  async function opaqueHome(base2, cur) {
3903
4758
  const { default: inquirer3 } = await import("inquirer");
3904
4759
  console.log(`
3905
- ${import_chalk30.default.bold(cur.endpoint)} ${import_chalk30.default.dim(cur.method ?? "GET")}`);
4760
+ ${import_chalk33.default.bold(cur.endpoint)} ${import_chalk33.default.dim(cur.method ?? "GET")}`);
3906
4761
  const { act } = await inquirer3.prompt([{
3907
4762
  type: "list",
3908
4763
  name: "act",
3909
4764
  message: "This opaque login method:",
3910
4765
  choices: [
3911
4766
  { name: "Replace it", value: "edit" },
3912
- { name: import_chalk30.default.red("Delete it"), value: "rm" },
4767
+ { name: import_chalk33.default.red("Delete it"), value: "rm" },
3913
4768
  { name: "\u2190 Back", value: "back" }
3914
4769
  ]
3915
4770
  }]);
@@ -3919,7 +4774,7 @@ async function opaqueHome(base2, cur) {
3919
4774
  return;
3920
4775
  }
3921
4776
  await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
3922
- console.log(import_chalk30.default.green(" Deleted."));
4777
+ console.log(import_chalk33.default.green(" Deleted."));
3923
4778
  }
3924
4779
  async function clientHome(base2, summary) {
3925
4780
  const { default: inquirer3 } = await import("inquirer");
@@ -3938,13 +4793,13 @@ async function clientHome(base2, summary) {
3938
4793
  message: `${c.name ?? id}:`,
3939
4794
  pageSize: 12,
3940
4795
  choices: [
3941
- { name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk30.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
3942
- { name: `Callback URLs: ${cb.length ? import_chalk30.default.cyan(cb.join(", ")) : import_chalk30.default.dim("(none)")}`, value: "callbacks" },
3943
- { name: `Scopes: ${scopes.length ? import_chalk30.default.cyan(scopes.join(" ")) : import_chalk30.default.dim("(defaults)")}`, value: "scopes" },
4796
+ { name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk33.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
4797
+ { name: `Callback URLs: ${cb.length ? import_chalk33.default.cyan(cb.join(", ")) : import_chalk33.default.dim("(none)")}`, value: "callbacks" },
4798
+ { name: `Scopes: ${scopes.length ? import_chalk33.default.cyan(scopes.join(" ")) : import_chalk33.default.dim("(defaults)")}`, value: "scopes" },
3944
4799
  { name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
3945
4800
  { name: "Reveal client secret", value: "secret" },
3946
4801
  { name: "Rotate client secret", value: "rotate" },
3947
- { name: import_chalk30.default.red("Delete this app client"), value: "delete" },
4802
+ { name: import_chalk33.default.red("Delete this app client"), value: "delete" },
3948
4803
  { name: "\u2190 Back", value: "back" }
3949
4804
  ]
3950
4805
  }]);
@@ -3957,13 +4812,13 @@ async function clientHome(base2, summary) {
3957
4812
  case "callbacks": {
3958
4813
  const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
3959
4814
  await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
3960
- console.log(import_chalk30.default.green(" Callbacks updated."));
4815
+ console.log(import_chalk33.default.green(" Callbacks updated."));
3961
4816
  break;
3962
4817
  }
3963
4818
  case "scopes": {
3964
4819
  const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
3965
4820
  await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
3966
- console.log(import_chalk30.default.green(" Scopes updated."));
4821
+ console.log(import_chalk33.default.green(" Scopes updated."));
3967
4822
  break;
3968
4823
  }
3969
4824
  case "expiries": {
@@ -3978,14 +4833,14 @@ async function clientHome(base2, summary) {
3978
4833
  body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
3979
4834
  summary: "Update token expiries"
3980
4835
  });
3981
- console.log(import_chalk30.default.green(" Expiries updated."));
4836
+ console.log(import_chalk33.default.green(" Expiries updated."));
3982
4837
  break;
3983
4838
  }
3984
4839
  case "secret": {
3985
4840
  const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
3986
4841
  if (!sure) break;
3987
4842
  const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
3988
- console.log(` ${import_chalk30.default.bold("client_secret")}: ${import_chalk30.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
4843
+ console.log(` ${import_chalk33.default.bold("client_secret")}: ${import_chalk33.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3989
4844
  break;
3990
4845
  }
3991
4846
  case "rotate": {
@@ -3993,14 +4848,14 @@ async function clientHome(base2, summary) {
3993
4848
  if (!sure) break;
3994
4849
  const fresh = randomSecret();
3995
4850
  await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
3996
- console.log(` New ${import_chalk30.default.bold("client_secret")}: ${import_chalk30.default.green(fresh)} ${import_chalk30.default.dim("(store it now)")}`);
4851
+ console.log(` New ${import_chalk33.default.bold("client_secret")}: ${import_chalk33.default.green(fresh)} ${import_chalk33.default.dim("(store it now)")}`);
3997
4852
  break;
3998
4853
  }
3999
4854
  case "delete": {
4000
4855
  const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Delete app client "${c.name ?? id}"? Consumers logged in through it will lose access.`, default: false }]);
4001
4856
  if (!sure) break;
4002
4857
  await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
4003
- console.log(import_chalk30.default.green(" App client deleted."));
4858
+ console.log(import_chalk33.default.green(" App client deleted."));
4004
4859
  return;
4005
4860
  }
4006
4861
  }
@@ -4027,9 +4882,9 @@ async function providersMenu(cBase, clientLabel) {
4027
4882
  const providers = Array.isArray(raw) ? raw : [];
4028
4883
  console.log();
4029
4884
  for (const p of providers) {
4030
- console.log(` ${import_chalk30.default.bold(p.type)} ${import_chalk30.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
4885
+ console.log(` ${import_chalk33.default.bold(p.type)} ${import_chalk33.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
4031
4886
  }
4032
- if (!providers.length) console.log(import_chalk30.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
4887
+ if (!providers.length) console.log(import_chalk33.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
4033
4888
  const { act } = await inquirer3.prompt([{
4034
4889
  type: "list",
4035
4890
  name: "act",
@@ -4087,23 +4942,23 @@ async function providersMenu(cBase, clientLabel) {
4087
4942
  body.targetServerToken = routing;
4088
4943
  }
4089
4944
  await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
4090
- console.log(import_chalk30.default.green(` ${type} provider added.`));
4945
+ console.log(import_chalk33.default.green(` ${type} provider added.`));
4091
4946
  } else {
4092
4947
  const { p } = await inquirer3.prompt([{
4093
4948
  type: "list",
4094
4949
  name: "p",
4095
4950
  message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
4096
- choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk30.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
4951
+ choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk33.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
4097
4952
  }]);
4098
4953
  if (!p) continue;
4099
4954
  if (act === "rm") {
4100
4955
  const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Remove the ${p.type} provider? Consumers using it can no longer sign in.`, default: false }]);
4101
4956
  if (!sure) continue;
4102
4957
  await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
4103
- console.log(import_chalk30.default.green(` ${p.type} removed.`));
4958
+ console.log(import_chalk33.default.green(` ${p.type} removed.`));
4104
4959
  } else {
4105
4960
  const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
4106
- console.log(` ${import_chalk30.default.bold("client_secret")}: ${import_chalk30.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
4961
+ console.log(` ${import_chalk33.default.bold("client_secret")}: ${import_chalk33.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
4107
4962
  }
4108
4963
  }
4109
4964
  }
@@ -4111,7 +4966,7 @@ async function providersMenu(cBase, clientLabel) {
4111
4966
 
4112
4967
  // src/commands/spec.ts
4113
4968
  var fs7 = __toESM(require("fs"));
4114
- var import_chalk31 = __toESM(require("chalk"));
4969
+ var import_chalk34 = __toESM(require("chalk"));
4115
4970
  var import_ora14 = __toESM(require("ora"));
4116
4971
  init_admin();
4117
4972
  init_api();
@@ -4128,14 +4983,14 @@ async function runSpecGet(project, opts) {
4128
4983
  }
4129
4984
  async function runSpecSet(project, opts) {
4130
4985
  if (!opts.file) {
4131
- console.error(import_chalk31.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
4986
+ console.error(import_chalk34.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
4132
4987
  process.exit(1);
4133
4988
  }
4134
4989
  let specContent;
4135
4990
  try {
4136
4991
  specContent = fs7.readFileSync(opts.file, "utf-8");
4137
4992
  } catch {
4138
- console.error(import_chalk31.default.red(`Cannot read file: ${opts.file}`));
4993
+ console.error(import_chalk34.default.red(`Cannot read file: ${opts.file}`));
4139
4994
  process.exit(1);
4140
4995
  }
4141
4996
  const { teamId } = await resolveTeam(opts.team);
@@ -4164,23 +5019,23 @@ async function runSpecSet(project, opts) {
4164
5019
  function printSpecChangeReport(report, projectName) {
4165
5020
  const r = report.routes;
4166
5021
  if (r) {
4167
- for (const a of r.added) console.log(import_chalk31.default.green(` + ${a}`));
4168
- for (const d of r.removed) console.log(import_chalk31.default.red(` \u2212 ${d}`));
4169
- for (const rn of r.renamed ?? []) console.log(import_chalk31.default.yellow(` ~ ${rn.from} \u2192 ${rn.to}`));
4170
- for (const m of r.modified ?? []) console.log(import_chalk31.default.dim(` \xB1 ${m} (params/schema changed)`));
5022
+ for (const a of r.added) console.log(import_chalk34.default.green(` + ${a}`));
5023
+ for (const d of r.removed) console.log(import_chalk34.default.red(` \u2212 ${d}`));
5024
+ for (const rn of r.renamed ?? []) console.log(import_chalk34.default.yellow(` ~ ${rn.from} \u2192 ${rn.to}`));
5025
+ for (const m of r.modified ?? []) console.log(import_chalk34.default.dim(` \xB1 ${m} (params/schema changed)`));
4171
5026
  }
4172
- if (report.applied?.mcp_regenerated) console.log(import_chalk31.default.dim(" MCP catalogue regenerated."));
4173
- if (report.applied?.candidates_cleared) console.log(import_chalk31.default.dim(` ${report.applied.candidates_cleared} route candidate(s) now documented \u2014 cleared.`));
5027
+ if (report.applied?.mcp_regenerated) console.log(import_chalk34.default.dim(" MCP catalogue regenerated."));
5028
+ if (report.applied?.candidates_cleared) console.log(import_chalk34.default.dim(` ${report.applied.candidates_cleared} route candidate(s) now documented \u2014 cleared.`));
4174
5029
  for (const item of report.attention ?? []) {
4175
- console.log(import_chalk31.default.yellow(` \u26A0 ${item.detail}`));
5030
+ console.log(import_chalk34.default.yellow(` \u26A0 ${item.detail}`));
4176
5031
  if (item.suggestion?.carry_from && item.suggestion.carry_to) {
4177
- console.log(import_chalk31.default.dim(` Fix: apiblaze spec carry ${projectName} --method ${item.suggestion.method} --from "${item.suggestion.carry_from}" --to "${item.suggestion.carry_to}"`));
5032
+ console.log(import_chalk34.default.dim(` Fix: apiblaze spec carry ${projectName} --method ${item.suggestion.method} --from "${item.suggestion.carry_from}" --to "${item.suggestion.carry_to}"`));
4178
5033
  }
4179
5034
  }
4180
5035
  }
4181
5036
  async function runSpecCarry(project, opts) {
4182
5037
  if (!opts.method || !opts.from || !opts.to) {
4183
- console.error(import_chalk31.default.red("--method, --from and --to are required."));
5038
+ console.error(import_chalk34.default.red("--method, --from and --to are required."));
4184
5039
  process.exit(1);
4185
5040
  }
4186
5041
  const { teamId } = await resolveTeam(opts.team);
@@ -4202,7 +5057,7 @@ async function runSpecCarry(project, opts) {
4202
5057
  }
4203
5058
  async function runSpecDeleteRule(project, opts) {
4204
5059
  if (!opts.method || !opts.route) {
4205
- console.error(import_chalk31.default.red("--method and --route are required."));
5060
+ console.error(import_chalk34.default.red("--method and --route are required."));
4206
5061
  process.exit(1);
4207
5062
  }
4208
5063
  const { teamId } = await resolveTeam(opts.team);
@@ -4222,14 +5077,14 @@ async function runSpecDeleteRule(project, opts) {
4222
5077
  }
4223
5078
 
4224
5079
  // src/commands/agent.ts
4225
- var import_chalk33 = __toESM(require("chalk"));
5080
+ var import_chalk36 = __toESM(require("chalk"));
4226
5081
  var import_ora15 = __toESM(require("ora"));
4227
5082
  init_auth();
4228
5083
  init_admin();
4229
5084
  init_resolve();
4230
5085
 
4231
5086
  // src/lib/tools.ts
4232
- var import_chalk32 = __toESM(require("chalk"));
5087
+ var import_chalk35 = __toESM(require("chalk"));
4233
5088
  init_admin();
4234
5089
  init_api();
4235
5090
  init_resolve();
@@ -4249,15 +5104,15 @@ var TOOLS = [
4249
5104
  const key = keys.dev ?? Object.values(keys)[0];
4250
5105
  const url = `https://${a.name}.abz.run/${version2}/dev`;
4251
5106
  const tryIt = buildTryItCurl(url, auth, key);
4252
- const lines = [` ${import_chalk32.default.dim("Proxy URL:")} ${import_chalk32.default.bold(url)}`];
4253
- if (res.devPortal) lines.push(` ${import_chalk32.default.dim("Dev portal:")} ${res.devPortal}`);
5107
+ const lines = [` ${import_chalk35.default.dim("Proxy URL:")} ${import_chalk35.default.bold(url)}`];
5108
+ if (res.devPortal) lines.push(` ${import_chalk35.default.dim("Dev portal:")} ${res.devPortal}`);
4254
5109
  const envs = Object.keys(keys);
4255
5110
  if (envs.length) {
4256
- lines.push("", ` ${import_chalk32.default.bold("API keys")} ${import_chalk32.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
5111
+ lines.push("", ` ${import_chalk35.default.bold("API keys")} ${import_chalk35.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
4257
5112
  const w = Math.max(...envs.map((e) => e.length));
4258
- for (const env of envs) lines.push(` ${import_chalk32.default.cyan(env.padEnd(w))} ${import_chalk32.default.green(keys[env])}`);
5113
+ for (const env of envs) lines.push(` ${import_chalk35.default.cyan(env.padEnd(w))} ${import_chalk35.default.green(keys[env])}`);
4259
5114
  }
4260
- if (tryIt) lines.push("", ` ${import_chalk32.default.dim("Try it:")}`, ` ${import_chalk32.default.cyan(tryIt)}`);
5115
+ if (tryIt) lines.push("", ` ${import_chalk35.default.dim("Try it:")}`, ` ${import_chalk35.default.cyan(tryIt)}`);
4261
5116
  return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
4262
5117
  }
4263
5118
  },
@@ -4413,17 +5268,17 @@ function truncate(value, max = 1500) {
4413
5268
  }
4414
5269
  function printCost(resp) {
4415
5270
  const line = formatBilling(resp.billing);
4416
- if (line) console.log(import_chalk33.default.magenta(` ${line}`) + import_chalk33.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
5271
+ if (line) console.log(import_chalk36.default.magenta(` ${line}`) + import_chalk36.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
4417
5272
  }
4418
5273
  async function runAgent(opts) {
4419
5274
  requireAuth();
4420
5275
  const { teamId, teamName } = await resolveTeam(opts.team);
4421
5276
  const { default: inquirer3 } = await import("inquirer");
4422
- console.log(import_chalk33.default.bold("APIblaze agent") + import_chalk33.default.dim(` \xB7 team ${teamName ?? teamId}`));
4423
- console.log(import_chalk33.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
5277
+ console.log(import_chalk36.default.bold("APIblaze agent") + import_chalk36.default.dim(` \xB7 team ${teamName ?? teamId}`));
5278
+ console.log(import_chalk36.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
4424
5279
  const history = [];
4425
5280
  while (true) {
4426
- const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk33.default.cyan("you") + " \u203A" }]);
5281
+ const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk36.default.cyan("you") + " \u203A" }]);
4427
5282
  const text = (input ?? "").trim();
4428
5283
  if (!text) continue;
4429
5284
  if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
@@ -4437,14 +5292,14 @@ async function runAgent(opts) {
4437
5292
  } catch (err) {
4438
5293
  spinner.stop();
4439
5294
  if (err instanceof ApiError && err.status === 402) {
4440
- console.log(import_chalk33.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
5295
+ console.log(import_chalk36.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
4441
5296
  break;
4442
5297
  }
4443
5298
  throw err;
4444
5299
  }
4445
5300
  history.push({ role: "assistant", content: resp.raw });
4446
5301
  printCost(resp);
4447
- if (resp.reply) console.log(import_chalk33.default.green("agent") + " \u203A " + resp.reply);
5302
+ if (resp.reply) console.log(import_chalk36.default.green("agent") + " \u203A " + resp.reply);
4448
5303
  if (!resp.action) break;
4449
5304
  const tool = findTool(resp.action.tool);
4450
5305
  if (!tool) {
@@ -4469,11 +5324,11 @@ async function runAgent(opts) {
4469
5324
  }
4470
5325
  renderTrace();
4471
5326
  if (step === MAX_TOOL_STEPS - 1) {
4472
- console.log(import_chalk33.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
5327
+ console.log(import_chalk36.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
4473
5328
  }
4474
5329
  }
4475
5330
  }
4476
- console.log(import_chalk33.default.dim("\nBye."));
5331
+ console.log(import_chalk36.default.dim("\nBye."));
4477
5332
  }
4478
5333
 
4479
5334
  // src/commands/config-browse.ts
@@ -4615,6 +5470,53 @@ var SETTINGS = [
4615
5470
  desc: "Team-scoped tag grouping projects in the consumer portal",
4616
5471
  toPatch: (v) => ({ product_slug: v })
4617
5472
  },
5473
+ // Who funds AI chat here (specs/chat_funding.MD). A partial PATCH merges, so
5474
+ // setting one key never resurrects a switch the producer turned off.
5475
+ {
5476
+ key: "chat_sponsorship.enabled",
5477
+ label: "Pay for your users' AI chats",
5478
+ group: "Portal & MCP",
5479
+ type: "boolean",
5480
+ desc: "Every chat message costs money and your users can't pay for it here yet, so you cover it \u2014 for people who sign in, and for API keys you gave the use-premium permission. Off = everyone brings their own AI key, your own tools included",
5481
+ read: (cfg) => cfg.chat_sponsorship?.enabled !== false,
5482
+ toPatch: (v) => ({ chat_sponsorship: { enabled: v } })
5483
+ },
5484
+ {
5485
+ key: "chat_sponsorship.quota_enabled",
5486
+ label: "Limit AI chat per person per day",
5487
+ group: "Portal & MCP",
5488
+ type: "boolean",
5489
+ desc: "Give each person their own daily allowance, reset at midnight UTC. Off = your wallet running out is the only thing that stops it",
5490
+ read: (cfg) => cfg.chat_sponsorship?.quota_enabled !== false,
5491
+ toPatch: (v) => ({ chat_sponsorship: { quota_enabled: v } })
5492
+ },
5493
+ {
5494
+ key: "chat_sponsorship.cap_cents_per_day",
5495
+ label: "AI chat: cents per person per day",
5496
+ group: "Portal & MCP",
5497
+ type: "number",
5498
+ desc: "Most you'll spend on one person per day, in cents you're actually charged (not raw model price). 25 is roughly 25 short chats. Max 5000; 0 blocks everyone",
5499
+ read: (cfg) => cfg.chat_sponsorship?.cap_cents_per_day ?? 25,
5500
+ toPatch: (v) => ({ chat_sponsorship: { cap_cents_per_day: v } })
5501
+ },
5502
+ {
5503
+ key: "chat_sponsorship.max_chats_per_day",
5504
+ label: "AI chat: chats per person per day",
5505
+ group: "Portal & MCP",
5506
+ type: "number",
5507
+ desc: "Chats one person can have per day \u2014 counted even when a chat is cheap, so nobody runs thousands of tiny ones. Max 1000; 0 blocks everyone",
5508
+ read: (cfg) => cfg.chat_sponsorship?.max_chats_per_day ?? 25,
5509
+ toPatch: (v) => ({ chat_sponsorship: { max_chats_per_day: v } })
5510
+ },
5511
+ {
5512
+ key: "chat_sponsorship.max_chars_per_message",
5513
+ label: "AI chat: longest single message",
5514
+ group: "Portal & MCP",
5515
+ type: "number",
5516
+ desc: "2000 characters is about 300 words. Has its own limit because one very long message can cost more than a whole day of short ones. Max 64000",
5517
+ read: (cfg) => cfg.chat_sponsorship?.max_chars_per_message ?? 2e3,
5518
+ toPatch: (v) => ({ chat_sponsorship: { max_chars_per_message: v } })
5519
+ },
4618
5520
  // ── Advanced (specs/missing.md): all optional, default-off; null clears ────
4619
5521
  {
4620
5522
  key: "ip_access.mode",
@@ -4712,11 +5614,11 @@ function dig(blob, dotted) {
4712
5614
  }
4713
5615
  var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
4714
5616
  function show(v) {
4715
- if (v === void 0) return import_chalk34.default.dim("(unset)");
4716
- if (v === null) return import_chalk34.default.dim("null");
4717
- if (typeof v === "object") return import_chalk34.default.cyan(JSON.stringify(v));
4718
- if (typeof v === "boolean") return v ? import_chalk34.default.green("on") : import_chalk34.default.red("off");
4719
- return import_chalk34.default.cyan(String(v));
5617
+ if (v === void 0) return import_chalk37.default.dim("(unset)");
5618
+ if (v === null) return import_chalk37.default.dim("null");
5619
+ if (typeof v === "object") return import_chalk37.default.cyan(JSON.stringify(v));
5620
+ if (typeof v === "boolean") return v ? import_chalk37.default.green("on") : import_chalk37.default.red("off");
5621
+ return import_chalk37.default.cyan(String(v));
4720
5622
  }
4721
5623
  function parseValue(raw) {
4722
5624
  if (raw === "true") return true;
@@ -4758,10 +5660,10 @@ async function patchSetting(proj2, s, value, cfg) {
4758
5660
  }
4759
5661
  }
4760
5662
  var loginFirst = (what) => {
4761
- console.log(import_chalk34.default.yellow(`
5663
+ console.log(import_chalk37.default.yellow(`
4762
5664
  Log in first to ${what}.`));
4763
- console.log(import_chalk34.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
4764
- console.log(import_chalk34.default.dim(" anonymously and want to bring it into your account.\n"));
5665
+ console.log(import_chalk37.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
5666
+ console.log(import_chalk37.default.dim(" anonymously and want to bring it into your account.\n"));
4765
5667
  };
4766
5668
  async function runConfig(project, key, value, opts) {
4767
5669
  const creds = loadCredentials();
@@ -4778,9 +5680,9 @@ async function runConfig(project, key, value, opts) {
4778
5680
  }
4779
5681
  const setting = SETTINGS.find((s) => s.key === key);
4780
5682
  if (!setting) {
4781
- console.error(import_chalk34.default.red(`Unknown setting "${key}".`));
4782
- console.error(import_chalk34.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
4783
- console.error(import_chalk34.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
5683
+ console.error(import_chalk37.default.red(`Unknown setting "${key}".`));
5684
+ console.error(import_chalk37.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
5685
+ console.error(import_chalk37.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
4784
5686
  process.exit(1);
4785
5687
  }
4786
5688
  if (value === void 0) {
@@ -4795,7 +5697,7 @@ async function pickProject(teamId) {
4795
5697
  const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
4796
5698
  const projects = await getProjects2(teamId).catch(() => []);
4797
5699
  if (!projects.length) {
4798
- console.error(import_chalk34.default.red("No projects in this team. Create one: `npx apiblaze create`."));
5700
+ console.error(import_chalk37.default.red("No projects in this team. Create one: `npx apiblaze create`."));
4799
5701
  process.exit(1);
4800
5702
  }
4801
5703
  const { default: inquirer3 } = await import("inquirer");
@@ -4803,7 +5705,7 @@ async function pickProject(teamId) {
4803
5705
  type: "list",
4804
5706
  name: "picked",
4805
5707
  message: "Which project?",
4806
- choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk34.default.dim("v" + p.apiVersion)}`, value: p }))
5708
+ choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk37.default.dim("v" + p.apiVersion)}`, value: p }))
4807
5709
  }]);
4808
5710
  return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
4809
5711
  }
@@ -4814,25 +5716,25 @@ function printAll(proj2, cfg, json) {
4814
5716
  console.log(JSON.stringify(out, null, 2));
4815
5717
  return;
4816
5718
  }
4817
- console.log(import_chalk34.default.bold(`
5719
+ console.log(import_chalk37.default.bold(`
4818
5720
  ${proj2.projectName} v${proj2.apiVersion} \u2014 settings
4819
5721
  `));
4820
5722
  for (const group2 of SETTING_GROUPS) {
4821
- console.log(import_chalk34.default.bold(group2));
5723
+ console.log(import_chalk37.default.bold(group2));
4822
5724
  for (const s of SETTINGS.filter((x) => x.group === group2)) {
4823
- console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk34.default.dim(s.desc)}`);
5725
+ console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk37.default.dim(s.desc)}`);
4824
5726
  }
4825
5727
  console.log();
4826
5728
  }
4827
- console.log(import_chalk34.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
5729
+ console.log(import_chalk37.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
4828
5730
  }
4829
5731
  async function discoveryMenu(project) {
4830
5732
  const { default: inquirer3 } = await import("inquirer");
4831
- console.log(import_chalk34.default.bold(`
5733
+ console.log(import_chalk37.default.bold(`
4832
5734
  APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
4833
5735
  `));
4834
- console.log(import_chalk34.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
4835
- console.log(import_chalk34.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
5736
+ console.log(import_chalk37.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
5737
+ console.log(import_chalk37.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
4836
5738
  for (; ; ) {
4837
5739
  const { pick: pick2 } = await inquirer3.prompt([{
4838
5740
  type: "list",
@@ -4840,13 +5742,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
4840
5742
  message: "Explore:",
4841
5743
  pageSize: 20,
4842
5744
  choices: [
4843
- new inquirer3.Separator(import_chalk34.default.bold("\u2014 Settings \u2014")),
5745
+ new inquirer3.Separator(import_chalk37.default.bold("\u2014 Settings \u2014")),
4844
5746
  ...SETTING_GROUPS.map((g) => ({
4845
- name: `${g} ${import_chalk34.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
5747
+ name: `${g} ${import_chalk37.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
4846
5748
  value: { kind: "settings", g }
4847
5749
  })),
4848
- new inquirer3.Separator(import_chalk34.default.bold("\u2014 Features \u2014")),
4849
- ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk34.default.dim(f.desc)}`, value: { kind: "feature", f } })),
5750
+ new inquirer3.Separator(import_chalk37.default.bold("\u2014 Features \u2014")),
5751
+ ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk37.default.dim(f.desc)}`, value: { kind: "feature", f } })),
4850
5752
  new inquirer3.Separator(),
4851
5753
  { name: "Exit", value: { kind: "exit" } }
4852
5754
  ]
@@ -4855,24 +5757,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
4855
5757
  if (pick2.kind === "settings") {
4856
5758
  console.log();
4857
5759
  for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
4858
- console.log(` ${import_chalk34.default.bold(s.label.padEnd(28))} ${import_chalk34.default.dim(s.desc)}`);
4859
- console.log(` ${import_chalk34.default.dim(" key: " + s.key)}`);
5760
+ console.log(` ${import_chalk37.default.bold(s.label.padEnd(28))} ${import_chalk37.default.dim(s.desc)}`);
5761
+ console.log(` ${import_chalk37.default.dim(" key: " + s.key)}`);
4860
5762
  }
4861
5763
  loginFirst("view or change these settings");
4862
5764
  } else {
4863
5765
  const f = pick2.f;
4864
5766
  console.log(`
4865
- ${import_chalk34.default.bold(f.label)} \u2014 ${f.desc}`);
5767
+ ${import_chalk37.default.bold(f.label)} \u2014 ${f.desc}`);
4866
5768
  loginFirst(`use ${f.label.toLowerCase()}`);
4867
5769
  }
4868
5770
  }
4869
5771
  }
4870
5772
  async function navigator(proj2, cfg, opts) {
4871
5773
  const { default: inquirer3 } = await import("inquirer");
4872
- console.log(import_chalk34.default.bold(`
5774
+ console.log(import_chalk37.default.bold(`
4873
5775
  ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
4874
5776
  `));
4875
- console.log(import_chalk34.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
5777
+ console.log(import_chalk37.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
4876
5778
  let blob = cfg;
4877
5779
  for (; ; ) {
4878
5780
  const { pick: pick2 } = await inquirer3.prompt([{
@@ -4881,10 +5783,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
4881
5783
  message: "Where to?",
4882
5784
  pageSize: 20,
4883
5785
  choices: [
4884
- new inquirer3.Separator(import_chalk34.default.bold("\u2014 Settings \u2014")),
5786
+ new inquirer3.Separator(import_chalk37.default.bold("\u2014 Settings \u2014")),
4885
5787
  ...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
4886
- new inquirer3.Separator(import_chalk34.default.bold("\u2014 Features \u2014")),
4887
- ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk34.default.dim(f.desc)}`, value: { kind: f.go } })),
5788
+ new inquirer3.Separator(import_chalk37.default.bold("\u2014 Features \u2014")),
5789
+ ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk37.default.dim(f.desc)}`, value: { kind: f.go } })),
4888
5790
  new inquirer3.Separator(),
4889
5791
  { name: "Show all settings", value: { kind: "list" } },
4890
5792
  { name: "Exit", value: { kind: "exit" } }
@@ -4924,7 +5826,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
4924
5826
  }
4925
5827
  }
4926
5828
  } catch (err) {
4927
- console.error(import_chalk34.default.red(` ${err instanceof Error ? err.message : String(err)}`));
5829
+ console.error(import_chalk37.default.red(` ${err instanceof Error ? err.message : String(err)}`));
4928
5830
  }
4929
5831
  }
4930
5832
  }
@@ -4938,7 +5840,7 @@ async function settingsGroup(proj2, cfg, group2) {
4938
5840
  message: group2 + ":",
4939
5841
  pageSize: 16,
4940
5842
  choices: [
4941
- ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk34.default.dim(s2.desc)}`, value: s2 })),
5843
+ ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk37.default.dim(s2.desc)}`, value: s2 })),
4942
5844
  new inquirer3.Separator(),
4943
5845
  { name: "\u2190 Back", value: null }
4944
5846
  ]
@@ -4956,7 +5858,7 @@ async function settingsGroup(proj2, cfg, group2) {
4956
5858
  } else if (s.type === "number") {
4957
5859
  const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
4958
5860
  if (v === "" || Number.isNaN(Number(v))) {
4959
- console.log(import_chalk34.default.yellow(" Not a number \u2014 unchanged."));
5861
+ console.log(import_chalk37.default.yellow(" Not a number \u2014 unchanged."));
4960
5862
  continue;
4961
5863
  }
4962
5864
  value = Number(v);
@@ -5037,7 +5939,7 @@ async function buildCondition(phase) {
5037
5939
  const items = [];
5038
5940
  for (; ; ) {
5039
5941
  const a = await inquirer3.prompt([
5040
- { type: "input", name: "source", message: `Condition field ${import_chalk34.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
5942
+ { type: "input", name: "source", message: `Condition field ${import_chalk37.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
5041
5943
  { type: "list", name: "operator", message: "Operator:", choices: [
5042
5944
  "eq",
5043
5945
  "neq",
@@ -5068,7 +5970,7 @@ async function buildCondition(phase) {
5068
5970
  function showCondition(cond) {
5069
5971
  if (!Array.isArray(cond) || !cond.length) return "";
5070
5972
  const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
5071
- return import_chalk34.default.dim(` when ${s}`);
5973
+ return import_chalk37.default.dim(` when ${s}`);
5072
5974
  }
5073
5975
  async function transformsMenu(proj2) {
5074
5976
  const { default: inquirer3 } = await import("inquirer");
@@ -5077,12 +5979,12 @@ async function transformsMenu(proj2) {
5077
5979
  const out = await admin({ method: "GET", path: base2, summary: "List transform rules" });
5078
5980
  const rules = out?.rules ?? [];
5079
5981
  console.log();
5080
- if (!rules.length) console.log(import_chalk34.default.dim(" No transform rules yet."));
5982
+ if (!rules.length) console.log(import_chalk37.default.dim(" No transform rules yet."));
5081
5983
  for (const r of rules) {
5082
5984
  const a = r.action ?? {};
5083
5985
  const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
5084
- 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_chalk34.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
5085
- console.log(` ${r.enabled ? import_chalk34.default.green("\u25CF") : import_chalk34.default.dim("\u25CB")} ${import_chalk34.default.bold(r.name)} ${import_chalk34.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
5986
+ 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_chalk37.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
5987
+ console.log(` ${r.enabled ? import_chalk37.default.green("\u25CF") : import_chalk37.default.dim("\u25CB")} ${import_chalk37.default.bold(r.name)} ${import_chalk37.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
5086
5988
  }
5087
5989
  const { act } = await inquirer3.prompt([{
5088
5990
  type: "list",
@@ -5094,7 +5996,7 @@ async function transformsMenu(proj2) {
5094
5996
  { name: "Enable/disable a rule", value: "toggle" },
5095
5997
  { name: "Delete a rule", value: "delete" }
5096
5998
  ] : [],
5097
- { name: import_chalk34.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
5999
+ { name: import_chalk37.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
5098
6000
  { name: "\u2190 Back", value: "back" }
5099
6001
  ]
5100
6002
  }]);
@@ -5107,11 +6009,11 @@ async function transformsMenu(proj2) {
5107
6009
  }]);
5108
6010
  const body = parseValue(raw);
5109
6011
  if (!body || typeof body !== "object" || Array.isArray(body)) {
5110
- console.log(import_chalk34.default.yellow(" Not a JSON object \u2014 skipped."));
6012
+ console.log(import_chalk37.default.yellow(" Not a JSON object \u2014 skipped."));
5111
6013
  continue;
5112
6014
  }
5113
6015
  await admin({ method: "POST", path: base2, body, summary: "Create transform rule (raw JSON)" });
5114
- console.log(import_chalk34.default.green(" Rule created."));
6016
+ console.log(import_chalk37.default.green(" Rule created."));
5115
6017
  continue;
5116
6018
  }
5117
6019
  if (act === "add") {
@@ -5127,7 +6029,7 @@ async function transformsMenu(proj2) {
5127
6029
  { name: "Remove a field", value: "remove" }
5128
6030
  ] }
5129
6031
  ]);
5130
- const fieldHint = import_chalk34.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
6032
+ const fieldHint = import_chalk37.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
5131
6033
  let action2;
5132
6034
  if (ans.kind === "hardcode") {
5133
6035
  const a = await inquirer3.prompt([
@@ -5174,16 +6076,16 @@ async function transformsMenu(proj2) {
5174
6076
  type: "list",
5175
6077
  name: "rule",
5176
6078
  message: act === "toggle" ? "Which rule?" : "Delete which rule?",
5177
- choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk34.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
6079
+ choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk37.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
5178
6080
  }]);
5179
6081
  if (!rule) continue;
5180
6082
  if (act === "toggle") {
5181
6083
  const flipped = { ...rule, enabled: rule.enabled === false };
5182
6084
  await admin({ method: "PUT", path: `${base2}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
5183
- console.log(import_chalk34.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
6085
+ console.log(import_chalk37.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
5184
6086
  } else {
5185
6087
  await admin({ method: "DELETE", path: `${base2}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
5186
- console.log(import_chalk34.default.green(` ${rule.name} deleted.`));
6088
+ console.log(import_chalk37.default.green(` ${rule.name} deleted.`));
5187
6089
  }
5188
6090
  }
5189
6091
  }
@@ -5195,9 +6097,9 @@ async function mappingsMenu(proj2) {
5195
6097
  const out = await admin({ method: "GET", path: base2, summary: "List mapping tables" });
5196
6098
  const tables = out?.mappings ?? out?.tables ?? [];
5197
6099
  console.log();
5198
- if (!tables.length) console.log(import_chalk34.default.dim(" No mapping tables yet."));
6100
+ if (!tables.length) console.log(import_chalk37.default.dim(" No mapping tables yet."));
5199
6101
  for (const t of tables) {
5200
- console.log(` ${import_chalk34.default.bold(t.name)} ${import_chalk34.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
6102
+ console.log(` ${import_chalk37.default.bold(t.name)} ${import_chalk37.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
5201
6103
  }
5202
6104
  const { act } = await inquirer3.prompt([{
5203
6105
  type: "list",
@@ -5217,11 +6119,11 @@ async function mappingsMenu(proj2) {
5217
6119
  ]);
5218
6120
  const entries2 = parseValue(a.entries);
5219
6121
  if (!Array.isArray(entries2)) {
5220
- console.log(import_chalk34.default.yellow(" Entries must be a JSON array \u2014 not created."));
6122
+ console.log(import_chalk37.default.yellow(" Entries must be a JSON array \u2014 not created."));
5221
6123
  continue;
5222
6124
  }
5223
6125
  await admin({ method: "POST", path: base2, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
5224
- console.log(import_chalk34.default.green(` Table "${a.name}" created.`));
6126
+ console.log(import_chalk37.default.green(` Table "${a.name}" created.`));
5225
6127
  } else {
5226
6128
  const { table } = await inquirer3.prompt([{
5227
6129
  type: "list",
@@ -5231,7 +6133,7 @@ async function mappingsMenu(proj2) {
5231
6133
  }]);
5232
6134
  if (!table) continue;
5233
6135
  await admin({ method: "DELETE", path: `${base2}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
5234
- console.log(import_chalk34.default.green(` ${table.name} deleted.`));
6136
+ console.log(import_chalk37.default.green(` ${table.name} deleted.`));
5235
6137
  }
5236
6138
  }
5237
6139
  }
@@ -5242,14 +6144,14 @@ async function tenantsMenu(proj2, opts) {
5242
6144
  const out = await admin({ method: "GET", path: base2, summary: "List attached tenants" });
5243
6145
  const tenants = out?.tenants ?? [];
5244
6146
  console.log();
5245
- if (!tenants.length) console.log(import_chalk34.default.dim(" No tenants attached (consumers use the default tenant)."));
5246
- for (const t of tenants) console.log(` ${import_chalk34.default.bold(t.tenant_name ?? t.name)} ${import_chalk34.default.dim(t.display_name ?? "")}`);
6147
+ if (!tenants.length) console.log(import_chalk37.default.dim(" No tenants attached (consumers use the default tenant)."));
6148
+ for (const t of tenants) console.log(` ${import_chalk37.default.bold(t.tenant_name ?? t.name)} ${import_chalk37.default.dim(t.display_name ?? "")}`);
5247
6149
  const { act } = await inquirer3.prompt([{
5248
6150
  type: "list",
5249
6151
  name: "act",
5250
6152
  message: "Tenants:",
5251
6153
  choices: [
5252
- { name: `Manage a tenant\u2026 ${import_chalk34.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
6154
+ { name: `Manage a tenant\u2026 ${import_chalk37.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
5253
6155
  { name: "Attach a tenant to this project", value: "attach" },
5254
6156
  ...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
5255
6157
  { name: "\u2190 Back", value: "back" }
@@ -5272,7 +6174,7 @@ async function tenantsMenu(proj2, opts) {
5272
6174
  }]);
5273
6175
  if (!t) continue;
5274
6176
  await admin({ method: "DELETE", path: `${base2}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
5275
- console.log(import_chalk34.default.green(` Detached ${t.tenant_name ?? t.name}.`));
6177
+ console.log(import_chalk37.default.green(` Detached ${t.tenant_name ?? t.name}.`));
5276
6178
  }
5277
6179
  }
5278
6180
  }
@@ -5315,7 +6217,7 @@ async function specMenu(proj2, opts) {
5315
6217
  choices: [
5316
6218
  { name: "Print the stored spec", value: "get" },
5317
6219
  { name: "Refresh the spec from its source", value: "refresh" },
5318
- { name: import_chalk34.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
6220
+ { name: import_chalk37.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
5319
6221
  { name: "\u2190 Back", value: "back" }
5320
6222
  ]
5321
6223
  }]);
@@ -5323,7 +6225,7 @@ async function specMenu(proj2, opts) {
5323
6225
  if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
5324
6226
  else if (act === "refresh") {
5325
6227
  await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
5326
- console.log(import_chalk34.default.green(" Spec refresh triggered."));
6228
+ console.log(import_chalk37.default.green(" Spec refresh triggered."));
5327
6229
  } else await runOpenapi(proj2.projectName, proj2.apiVersion);
5328
6230
  }
5329
6231
  async function agentsMenu(proj2, opts) {
@@ -5348,7 +6250,7 @@ async function agentsMenu(proj2, opts) {
5348
6250
  }
5349
6251
 
5350
6252
  // src/commands/group.ts
5351
- var import_chalk35 = __toESM(require("chalk"));
6253
+ var import_chalk38 = __toESM(require("chalk"));
5352
6254
  var import_ora17 = __toESM(require("ora"));
5353
6255
  init_auth();
5354
6256
  init_admin();
@@ -5442,7 +6344,7 @@ async function userByRefOrCreate(t, ref) {
5442
6344
  if (!ref.includes("@")) throw e;
5443
6345
  const created = await t.call("/v1/admin/users", "POST", { email: ref.trim().toLowerCase() });
5444
6346
  if (!created?.abz_sub) throw e;
5445
- console.log(import_chalk35.default.dim(` ${ref} wasn't a user yet \u2014 created.`));
6347
+ console.log(import_chalk38.default.dim(` ${ref} wasn't a user yet \u2014 created.`));
5446
6348
  return { abz_sub: created.abz_sub, email: created.email ?? ref, display_name: null };
5447
6349
  }
5448
6350
  }
@@ -5450,11 +6352,11 @@ async function runGroupList(opts) {
5450
6352
  const t = await resolveTransport(opts);
5451
6353
  const groups = await listGroups(t);
5452
6354
  if (opts.json) return void console.log(JSON.stringify(groups));
5453
- if (!groups.length) return void console.log(import_chalk35.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
5454
- console.log(import_chalk35.default.dim(`Tenant ${t.tenant}:`));
6355
+ if (!groups.length) return void console.log(import_chalk38.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
6356
+ console.log(import_chalk38.default.dim(`Tenant ${t.tenant}:`));
5455
6357
  for (const g of groups) {
5456
- const sealed = g.name === "apiblaze_admins" ? import_chalk35.default.dim(" (admins \u2014 managed by allowlist)") : "";
5457
- console.log(` ${import_chalk35.default.bold(g.name)} ${import_chalk35.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
6358
+ const sealed = g.name === "apiblaze_admins" ? import_chalk38.default.dim(" (admins \u2014 managed by allowlist)") : "";
6359
+ console.log(` ${import_chalk38.default.bold(g.name)} ${import_chalk38.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
5458
6360
  }
5459
6361
  }
5460
6362
  async function runGroupCreate(name, opts) {
@@ -5482,7 +6384,7 @@ async function runGroupCreate(name, opts) {
5482
6384
  const spinner = (0, import_ora17.default)(`Creating group ${name}...`).start();
5483
6385
  try {
5484
6386
  await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
5485
- spinner.succeed(`Group ${import_chalk35.default.bold(name)} created in ${t.tenant}.`);
6387
+ spinner.succeed(`Group ${import_chalk38.default.bold(name)} created in ${t.tenant}.`);
5486
6388
  } catch (e) {
5487
6389
  spinner.fail("Create failed.");
5488
6390
  throw e;
@@ -5494,7 +6396,7 @@ async function runGroupDelete(name, opts) {
5494
6396
  const spinner = (0, import_ora17.default)(`Deleting group ${name}...`).start();
5495
6397
  try {
5496
6398
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
5497
- spinner.succeed(`Group ${import_chalk35.default.bold(name)} deleted.`);
6399
+ spinner.succeed(`Group ${import_chalk38.default.bold(name)} deleted.`);
5498
6400
  } catch (e) {
5499
6401
  spinner.fail("Delete failed.");
5500
6402
  throw e;
@@ -5507,11 +6409,11 @@ async function runGroupMembers(name, opts) {
5507
6409
  const subs = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/subgroups`);
5508
6410
  if (opts.json) return void console.log(JSON.stringify({ ...detail, subgroups: subs?.subgroups ?? [] }));
5509
6411
  const members = detail?.members ?? [];
5510
- console.log(import_chalk35.default.bold(`${g.name}`) + import_chalk35.default.dim(` (${t.tenant})`));
5511
- for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk35.default.cyan("group-admin") : import_chalk35.default.dim("member")}`);
5512
- if (!members.length) console.log(import_chalk35.default.dim(" no members"));
6412
+ console.log(import_chalk38.default.bold(`${g.name}`) + import_chalk38.default.dim(` (${t.tenant})`));
6413
+ for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk38.default.cyan("group-admin") : import_chalk38.default.dim("member")}`);
6414
+ if (!members.length) console.log(import_chalk38.default.dim(" no members"));
5513
6415
  const sg = subs?.subgroups ?? [];
5514
- if (sg.length) console.log(import_chalk35.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
6416
+ if (sg.length) console.log(import_chalk38.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
5515
6417
  }
5516
6418
  async function runGroupAddUser(userRef, groupName, opts) {
5517
6419
  const t = await resolveTransport(opts);
@@ -5521,7 +6423,7 @@ async function runGroupAddUser(userRef, groupName, opts) {
5521
6423
  const spinner = (0, import_ora17.default)(`Adding ${label2(u)} to ${g.name}...`).start();
5522
6424
  try {
5523
6425
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
5524
- spinner.succeed(`${import_chalk35.default.bold(label2(u))} added to ${import_chalk35.default.bold(g.name)} as ${role}.`);
6426
+ spinner.succeed(`${import_chalk38.default.bold(label2(u))} added to ${import_chalk38.default.bold(g.name)} as ${role}.`);
5525
6427
  } catch (e) {
5526
6428
  spinner.fail("Add failed.");
5527
6429
  throw e;
@@ -5533,7 +6435,7 @@ async function runGroupRemoveUser(userRef, groupName, opts) {
5533
6435
  const spinner = (0, import_ora17.default)(`Removing ${label2(u)} from ${g.name}...`).start();
5534
6436
  try {
5535
6437
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
5536
- spinner.succeed(`${import_chalk35.default.bold(label2(u))} removed from ${import_chalk35.default.bold(g.name)}.`);
6438
+ spinner.succeed(`${import_chalk38.default.bold(label2(u))} removed from ${import_chalk38.default.bold(g.name)}.`);
5537
6439
  } catch (e) {
5538
6440
  spinner.fail("Remove failed.");
5539
6441
  throw e;
@@ -5545,7 +6447,7 @@ async function runGroupAddGroup(childName, parentName, opts) {
5545
6447
  const spinner = (0, import_ora17.default)(`Nesting ${child.name} under ${parent.name}...`).start();
5546
6448
  try {
5547
6449
  await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
5548
- spinner.succeed(`${import_chalk35.default.bold(child.name)} is now a subgroup of ${import_chalk35.default.bold(parent.name)}.`);
6450
+ spinner.succeed(`${import_chalk38.default.bold(child.name)} is now a subgroup of ${import_chalk38.default.bold(parent.name)}.`);
5549
6451
  } catch (e) {
5550
6452
  spinner.fail("Nest failed.");
5551
6453
  throw e;
@@ -5557,7 +6459,7 @@ async function runGroupRemoveGroup(childName, parentName, opts) {
5557
6459
  const spinner = (0, import_ora17.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
5558
6460
  try {
5559
6461
  await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
5560
- spinner.succeed(`${import_chalk35.default.bold(child.name)} un-nested from ${import_chalk35.default.bold(parent.name)}.`);
6462
+ spinner.succeed(`${import_chalk38.default.bold(child.name)} un-nested from ${import_chalk38.default.bold(parent.name)}.`);
5561
6463
  } catch (e) {
5562
6464
  spinner.fail("Un-nest failed.");
5563
6465
  throw e;
@@ -5568,18 +6470,18 @@ async function runGroupUsers(opts) {
5568
6470
  const users = await listUsers(t);
5569
6471
  const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
5570
6472
  if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
5571
- console.log(import_chalk35.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
5572
- for (const u of users) console.log(` ${label2(u)} ${import_chalk35.default.dim(u.abz_sub)}`);
5573
- if (!users.length) console.log(import_chalk35.default.dim(" no users yet"));
6473
+ console.log(import_chalk38.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
6474
+ for (const u of users) console.log(` ${label2(u)} ${import_chalk38.default.dim(u.abz_sub)}`);
6475
+ if (!users.length) console.log(import_chalk38.default.dim(" no users yet"));
5574
6476
  const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
5575
6477
  if (fresh.length) {
5576
- console.log(import_chalk35.default.dim("Seen in traffic (not yet users):"));
5577
- for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk35.default.dim(o.source_label ?? "")}`);
6478
+ console.log(import_chalk38.default.dim("Seen in traffic (not yet users):"));
6479
+ for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk38.default.dim(o.source_label ?? "")}`);
5578
6480
  }
5579
6481
  }
5580
6482
 
5581
6483
  // src/commands/key.ts
5582
- var import_chalk36 = __toESM(require("chalk"));
6484
+ var import_chalk39 = __toESM(require("chalk"));
5583
6485
  var import_ora18 = __toESM(require("ora"));
5584
6486
  init_caller();
5585
6487
  async function runApikeysMenu(opts) {
@@ -5609,11 +6511,11 @@ async function runKeyList(opts) {
5609
6511
  return;
5610
6512
  }
5611
6513
  if (!keys.length) {
5612
- console.log(import_chalk36.default.yellow("No developer keys."));
6514
+ console.log(import_chalk39.default.yellow("No developer keys."));
5613
6515
  return;
5614
6516
  }
5615
6517
  for (const k of keys) {
5616
- console.log(` ${import_chalk36.default.bold(k.key_id ?? k.id)} ${import_chalk36.default.dim(k.description ?? "")} ${import_chalk36.default.dim(k.expires_at ?? "no expiry")}`);
6518
+ console.log(` ${import_chalk39.default.bold(k.key_id ?? k.id)} ${import_chalk39.default.dim(k.description ?? "")} ${import_chalk39.default.dim(k.expires_at ?? "no expiry")}`);
5617
6519
  }
5618
6520
  }
5619
6521
  async function runKeyMint(opts) {
@@ -5635,9 +6537,9 @@ async function runKeyMint(opts) {
5635
6537
  console.log(JSON.stringify(out));
5636
6538
  return;
5637
6539
  }
5638
- console.log(` ${import_chalk36.default.bold("key_id")}: ${out?.key_id}`);
5639
- console.log(` ${import_chalk36.default.bold("key")}: ${import_chalk36.default.green(out?.key)} ${import_chalk36.default.dim("(shown once \u2014 store it now)")}`);
5640
- if (out?.expires_at) console.log(` ${import_chalk36.default.dim("expires:")} ${out.expires_at}`);
6540
+ console.log(` ${import_chalk39.default.bold("key_id")}: ${out?.key_id}`);
6541
+ console.log(` ${import_chalk39.default.bold("key")}: ${import_chalk39.default.green(out?.key)} ${import_chalk39.default.dim("(shown once \u2014 store it now)")}`);
6542
+ if (out?.expires_at) console.log(` ${import_chalk39.default.dim("expires:")} ${out.expires_at}`);
5641
6543
  } catch (err) {
5642
6544
  spinner.fail("Mint failed.");
5643
6545
  throw err;
@@ -5664,7 +6566,7 @@ async function runKeyRevoke(keyId, opts) {
5664
6566
  init_iam();
5665
6567
 
5666
6568
  // src/commands/admins.ts
5667
- var import_chalk37 = __toESM(require("chalk"));
6569
+ var import_chalk40 = __toESM(require("chalk"));
5668
6570
  var import_ora19 = __toESM(require("ora"));
5669
6571
  init_caller();
5670
6572
  function requireTenant(opts) {
@@ -5692,13 +6594,13 @@ async function runAdminsList(opts) {
5692
6594
  return;
5693
6595
  }
5694
6596
  if (!admins2.length) {
5695
- console.log(import_chalk37.default.yellow(`No tenant admins for ${tenant2} yet.`));
6597
+ console.log(import_chalk40.default.yellow(`No tenant admins for ${tenant2} yet.`));
5696
6598
  return;
5697
6599
  }
5698
6600
  for (const a of admins2) {
5699
6601
  const email = typeof a === "string" ? a : a.email;
5700
6602
  const flags = [a.active === false ? "pending" : null, a.pinned ? "pinned" : null].filter(Boolean).join(", ");
5701
- console.log(` ${import_chalk37.default.bold(email)}${flags ? import_chalk37.default.dim(` (${flags})`) : ""}`);
6603
+ console.log(` ${import_chalk40.default.bold(email)}${flags ? import_chalk40.default.dim(` (${flags})`) : ""}`);
5702
6604
  }
5703
6605
  }
5704
6606
  async function runAdminsAdd(email, opts) {
@@ -5713,9 +6615,9 @@ async function runAdminsAdd(email, opts) {
5713
6615
  body: { email },
5714
6616
  summary: `Add ${email} to tenant ${tenant2} admins`
5715
6617
  });
5716
- spinner?.succeed(`${import_chalk37.default.bold(email)} is now a tenant admin of ${import_chalk37.default.bold(tenant2)}.`);
6618
+ spinner?.succeed(`${import_chalk40.default.bold(email)} is now a tenant admin of ${import_chalk40.default.bold(tenant2)}.`);
5717
6619
  if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
5718
- else console.log(import_chalk37.default.dim(" Reload the Users & Groups widget \u2014 access flips from \u201Cpending\u201D to ready."));
6620
+ else console.log(import_chalk40.default.dim(" Reload the Users & Groups widget \u2014 access flips from \u201Cpending\u201D to ready."));
5719
6621
  } catch (err) {
5720
6622
  spinner?.fail("Add failed.");
5721
6623
  throw err;
@@ -5732,7 +6634,7 @@ async function runAdminsRemove(email, opts) {
5732
6634
  path: `${base(teamId, tenant2)}/${encodeURIComponent(email)}`,
5733
6635
  summary: `Remove ${email} from tenant ${tenant2} admins`
5734
6636
  });
5735
- spinner?.succeed(`Removed ${import_chalk37.default.bold(email)} from the tenant admins of ${import_chalk37.default.bold(tenant2)}.`);
6637
+ spinner?.succeed(`Removed ${import_chalk40.default.bold(email)} from the tenant admins of ${import_chalk40.default.bold(tenant2)}.`);
5736
6638
  } catch (err) {
5737
6639
  spinner?.fail("Remove failed.");
5738
6640
  throw err;
@@ -5740,7 +6642,7 @@ async function runAdminsRemove(email, opts) {
5740
6642
  }
5741
6643
 
5742
6644
  // src/commands/preapprove.ts
5743
- var import_chalk38 = __toESM(require("chalk"));
6645
+ var import_chalk41 = __toESM(require("chalk"));
5744
6646
  var import_ora20 = __toESM(require("ora"));
5745
6647
 
5746
6648
  // src/lib/preapproval.ts
@@ -5805,14 +6707,14 @@ async function runPreapprove(who, opts) {
5805
6707
  const rules = await listPreapprovalRules(tenant2);
5806
6708
  if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
5807
6709
  if (!rules.length) {
5808
- console.log(import_chalk38.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
5809
- console.log(import_chalk38.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
6710
+ console.log(import_chalk41.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
6711
+ console.log(import_chalk41.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
5810
6712
  return;
5811
6713
  }
5812
- console.log(import_chalk38.default.dim(`Pre-approved for ${import_chalk38.default.bold(tenant2)}:`));
6714
+ console.log(import_chalk41.default.dim(`Pre-approved for ${import_chalk41.default.bold(tenant2)}:`));
5813
6715
  for (const r of rules) {
5814
- const tag = r.kind === "domain" ? import_chalk38.default.cyan("@" + r.value + " (whole domain)") : r.value;
5815
- const grp = r.groups?.length ? import_chalk38.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
6716
+ const tag = r.kind === "domain" ? import_chalk41.default.cyan("@" + r.value + " (whole domain)") : r.value;
6717
+ const grp = r.groups?.length ? import_chalk41.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
5816
6718
  console.log(` ${tag}${grp}`);
5817
6719
  }
5818
6720
  return;
@@ -5823,17 +6725,17 @@ async function runPreapprove(who, opts) {
5823
6725
  if (opts.remove) {
5824
6726
  const spinner2 = (0, import_ora20.default)(`Removing ${who} from ${tenant2}\u2026`).start();
5825
6727
  const { removed, value } = await removePreapprovalRule(tenant2, who);
5826
- if (removed) spinner2.succeed(`${import_chalk38.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
5827
- else spinner2.warn(`No pre-approval rule for ${import_chalk38.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
6728
+ if (removed) spinner2.succeed(`${import_chalk41.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
6729
+ else spinner2.warn(`No pre-approval rule for ${import_chalk41.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
5828
6730
  return;
5829
6731
  }
5830
6732
  const spinner = (0, import_ora20.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
5831
6733
  try {
5832
6734
  const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
5833
6735
  const what = kind === "domain" ? `Anyone @${value}` : value;
5834
- spinner.succeed(`${import_chalk38.default.bold(what)} can now sign in to ${import_chalk38.default.bold(tenant2)}.`);
5835
- if (opts.group?.length) console.log(import_chalk38.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
5836
- console.log(import_chalk38.default.dim(` See the full list: apiblaze preapprove --list`));
6736
+ spinner.succeed(`${import_chalk41.default.bold(what)} can now sign in to ${import_chalk41.default.bold(tenant2)}.`);
6737
+ if (opts.group?.length) console.log(import_chalk41.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
6738
+ console.log(import_chalk41.default.dim(` See the full list: apiblaze preapprove --list`));
5837
6739
  } catch (err) {
5838
6740
  spinner.fail("Could not add the rule.");
5839
6741
  throw err;
@@ -5844,7 +6746,7 @@ async function runPreapprove(who, opts) {
5844
6746
  var fs9 = __toESM(require("fs"));
5845
6747
  var path6 = __toESM(require("path"));
5846
6748
  var crypto2 = __toESM(require("crypto"));
5847
- var import_chalk40 = __toESM(require("chalk"));
6749
+ var import_chalk43 = __toESM(require("chalk"));
5848
6750
  var import_ora21 = __toESM(require("ora"));
5849
6751
  var import_yaml2 = require("yaml");
5850
6752
  init_auth();
@@ -5855,7 +6757,7 @@ init_admin();
5855
6757
  // src/commands/llm.ts
5856
6758
  var fs8 = __toESM(require("fs"));
5857
6759
  var path5 = __toESM(require("path"));
5858
- var import_chalk39 = __toESM(require("chalk"));
6760
+ var import_chalk42 = __toESM(require("chalk"));
5859
6761
  var import_inquirer2 = __toESM(require("inquirer"));
5860
6762
  init_auth();
5861
6763
  var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
@@ -5899,27 +6801,27 @@ async function runLlmSetKey(keyArg, opts) {
5899
6801
  }
5900
6802
  const existing = loadLlmConfig();
5901
6803
  saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
5902
- console.log(`${import_chalk39.default.green("\u2713")} Saved ${import_chalk39.default.bold(provider)} key ${maskSecret(key)}`);
5903
- console.log(import_chalk39.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
5904
- if (opts.model) console.log(import_chalk39.default.gray(` Model: ${opts.model}`));
6804
+ console.log(`${import_chalk42.default.green("\u2713")} Saved ${import_chalk42.default.bold(provider)} key ${maskSecret(key)}`);
6805
+ console.log(import_chalk42.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
6806
+ if (opts.model) console.log(import_chalk42.default.gray(` Model: ${opts.model}`));
5905
6807
  }
5906
6808
  async function runLlmShow() {
5907
6809
  const cfg = loadLlmConfig();
5908
6810
  if (!cfg) {
5909
- console.log(import_chalk39.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
6811
+ console.log(import_chalk42.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
5910
6812
  return;
5911
6813
  }
5912
- console.log(`Provider: ${import_chalk39.default.bold(cfg.provider)}`);
6814
+ console.log(`Provider: ${import_chalk42.default.bold(cfg.provider)}`);
5913
6815
  console.log(`Key: ${maskSecret(cfg.key)}`);
5914
6816
  if (cfg.model) console.log(`Model: ${cfg.model}`);
5915
- console.log(import_chalk39.default.gray(`Stored at ${LLM_PATH}`));
6817
+ console.log(import_chalk42.default.gray(`Stored at ${LLM_PATH}`));
5916
6818
  }
5917
6819
  async function runLlmClearKey() {
5918
6820
  try {
5919
6821
  fs8.unlinkSync(LLM_PATH);
5920
- console.log(`${import_chalk39.default.green("\u2713")} Removed local LLM key.`);
6822
+ console.log(`${import_chalk42.default.green("\u2713")} Removed local LLM key.`);
5921
6823
  } catch {
5922
- console.log(import_chalk39.default.gray("No LLM key was set."));
6824
+ console.log(import_chalk42.default.gray("No LLM key was set."));
5923
6825
  }
5924
6826
  }
5925
6827
 
@@ -5927,9 +6829,9 @@ async function runLlmClearKey() {
5927
6829
  init_trace();
5928
6830
  init_types();
5929
6831
  function fail4(message, hint) {
5930
- console.error(import_chalk40.default.red(`
6832
+ console.error(import_chalk43.default.red(`
5931
6833
  Error: ${message}`));
5932
- if (hint) console.error(import_chalk40.default.dim(hint));
6834
+ if (hint) console.error(import_chalk43.default.dim(hint));
5933
6835
  process.exit(1);
5934
6836
  }
5935
6837
  function normalizeName2(raw) {
@@ -6077,7 +6979,7 @@ async function resolveTargetAuth(spec2, opts) {
6077
6979
  "Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
6078
6980
  );
6079
6981
  }
6080
- if (sawOAuth) console.log(import_chalk40.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
6982
+ if (sawOAuth) console.log(import_chalk43.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
6081
6983
  return null;
6082
6984
  }
6083
6985
  if (candidates.length === 1 && !noneAllowed) return candidates[0];
@@ -6150,7 +7052,7 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
6150
7052
  const fresh = await validConsumerToken(existing);
6151
7053
  if (fresh) {
6152
7054
  if (fresh.accessToken !== existing.accessToken) saveConsumer({ ...fresh, resource });
6153
- console.log(import_chalk40.default.dim(` Using your consumer session on ${import_chalk40.default.bold(tenant2)}${fresh.email ? ` (${fresh.email})` : ""}.`));
7055
+ console.log(import_chalk43.default.dim(` Using your consumer session on ${import_chalk43.default.bold(tenant2)}${fresh.email ? ` (${fresh.email})` : ""}.`));
6154
7056
  return fresh;
6155
7057
  }
6156
7058
  }
@@ -6169,13 +7071,13 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
6169
7071
  );
6170
7072
  }
6171
7073
  const clientId = pick2.client_id ?? pick2.clientId;
6172
- console.log(`${import_chalk40.default.cyan("\u2192")} This proxy signs consumers in with OAuth \u2014 logging you in to ${import_chalk40.default.bold(tenant2)}...`);
7074
+ console.log(`${import_chalk43.default.cyan("\u2192")} This proxy signs consumers in with OAuth \u2014 logging you in to ${import_chalk43.default.bold(tenant2)}...`);
6173
7075
  const result = await deviceLogin(clientId, "openid email profile offline_access", ({ verificationUri, userCode }) => {
6174
7076
  console.log(`
6175
- Open: ${import_chalk40.default.underline(verificationUri)}`);
6176
- console.log(` Code: ${import_chalk40.default.bold(userCode)}
7077
+ Open: ${import_chalk43.default.underline(verificationUri)}`);
7078
+ console.log(` Code: ${import_chalk43.default.bold(userCode)}
6177
7079
  `);
6178
- console.log(import_chalk40.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
7080
+ console.log(import_chalk43.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
6179
7081
  }, resource);
6180
7082
  const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
6181
7083
  const creds = {
@@ -6191,7 +7093,7 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
6191
7093
  resource
6192
7094
  };
6193
7095
  saveConsumer(creds);
6194
- console.log(` ${import_chalk40.default.green("\u2714")} Signed in as${creds.email ? ` ${import_chalk40.default.bold(creds.email)}` : " a consumer"} on ${tenant2}.`);
7096
+ console.log(` ${import_chalk43.default.green("\u2714")} Signed in as${creds.email ? ` ${import_chalk43.default.bold(creds.email)}` : " a consumer"} on ${tenant2}.`);
6195
7097
  return creds;
6196
7098
  }
6197
7099
  async function cpPost(anon, path8, body, summary) {
@@ -6220,7 +7122,7 @@ async function provision(spec2, target, opts) {
6220
7122
  let name = opts.name ? base2 : `${base2}${salt()}`;
6221
7123
  const access = anon ? "open" : opts.access === "open" ? "open" : "invite";
6222
7124
  if (anon && opts.access === "invite") {
6223
- console.log(import_chalk40.default.dim(" Note: --access invite needs an account to pre-approve people. Staying open for this anonymous proxy \u2014 run `apiblaze login`, then `apiblaze apichat --access invite`."));
7125
+ console.log(import_chalk43.default.dim(" Note: --access invite needs an account to pre-approve people. Staying open for this anonymous proxy \u2014 run `apiblaze login`, then `apiblaze apichat --access invite`."));
6224
7126
  }
6225
7127
  const DUAL_AUTH = {
6226
7128
  mode: "authenticate",
@@ -6319,7 +7221,7 @@ async function provision(spec2, target, opts) {
6319
7221
  try {
6320
7222
  await addPreapprovalRule(tenant2, email);
6321
7223
  } catch {
6322
- console.log(import_chalk40.default.dim(` (Could not auto-approve your email for sign-in \u2014 add it later: apiblaze preapprove ${email})`));
7224
+ console.log(import_chalk43.default.dim(` (Could not auto-approve your email for sign-in \u2014 add it later: apiblaze preapprove ${email})`));
6323
7225
  }
6324
7226
  }
6325
7227
  }
@@ -6363,7 +7265,7 @@ async function uploadSpec(p, specText, opts) {
6363
7265
  throw err;
6364
7266
  }
6365
7267
  if (out && out.reused === true) {
6366
- console.log(import_chalk40.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
7268
+ console.log(import_chalk43.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
6367
7269
  } else if (out && out.changed === true && out.previous_spec_hash) {
6368
7270
  const interactive = !!process.stdin.isTTY && !opts.yes;
6369
7271
  if (interactive) {
@@ -6371,7 +7273,7 @@ async function uploadSpec(p, specText, opts) {
6371
7273
  const { go } = await inquirer3.prompt([
6372
7274
  { type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
6373
7275
  ]);
6374
- if (!go) console.log(import_chalk40.default.dim(" Keeping the existing MCP catalogue."));
7276
+ if (!go) console.log(import_chalk43.default.dim(" Keeping the existing MCP catalogue."));
6375
7277
  }
6376
7278
  }
6377
7279
  }
@@ -6412,15 +7314,15 @@ var revealAuth = false;
6412
7314
  function renderToolEvents(events, cred) {
6413
7315
  for (const e of events ?? []) {
6414
7316
  const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
6415
- const mark = ok ? import_chalk40.default.green("\u2713") : import_chalk40.default.red("\u2717");
6416
- console.log(` ${mark} ${import_chalk40.default.cyan(e.name)} ${import_chalk40.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
7317
+ const mark = ok ? import_chalk43.default.green("\u2713") : import_chalk43.default.red("\u2717");
7318
+ console.log(` ${mark} ${import_chalk43.default.cyan(e.name)} ${import_chalk43.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
6417
7319
  if (isVerbose() && e.method && e.url) {
6418
- console.log(import_chalk40.default.dim(` curl -sS -X ${e.method} '${e.url}'${cred ? " \\" : ""}`));
7320
+ console.log(import_chalk43.default.dim(` curl -sS -X ${e.method} '${e.url}'${cred ? " \\" : ""}`));
6419
7321
  if (cred) {
6420
7322
  const shown = revealAuth ? cred.value : maskKey(cred.value);
6421
7323
  const keyLine = ` -H '${cred.header}: ${shown}'`;
6422
- const hint = revealAuth ? "" : import_chalk40.default.yellow(" \u2190 /showauth will reveal this");
6423
- console.log(import_chalk40.default.dim(keyLine) + hint);
7324
+ const hint = revealAuth ? "" : import_chalk43.default.yellow(" \u2190 /showauth will reveal this");
7325
+ console.log(import_chalk43.default.dim(keyLine) + hint);
6424
7326
  }
6425
7327
  }
6426
7328
  }
@@ -6441,20 +7343,20 @@ function renderToolResults(delta, events) {
6441
7343
  }
6442
7344
  })();
6443
7345
  const lines = pretty.split("\n");
6444
- console.log(import_chalk40.default.dim(" response:"));
7346
+ console.log(import_chalk43.default.dim(" response:"));
6445
7347
  for (const line of lines.slice(0, cap)) {
6446
- console.log(import_chalk40.default.dim(` ${line}`));
7348
+ console.log(import_chalk43.default.dim(` ${line}`));
6447
7349
  }
6448
- if (lines.length > cap) console.log(import_chalk40.default.dim(` \u2026${lines.length - cap} more lines`));
7350
+ if (lines.length > cap) console.log(import_chalk43.default.dim(` \u2026${lines.length - cap} more lines`));
6449
7351
  }
6450
7352
  }
6451
7353
  function billingLine(billing) {
6452
7354
  if (!billing || typeof billing.cents !== "number") return null;
6453
7355
  if (typeof billing.free_turns_remaining === "number") return null;
6454
7356
  const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
6455
- let line = import_chalk40.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk40.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
7357
+ let line = import_chalk43.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk43.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
6456
7358
  if (typeof billing.credits_remaining === "number") {
6457
- line += import_chalk40.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
7359
+ line += import_chalk43.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
6458
7360
  }
6459
7361
  return line;
6460
7362
  }
@@ -6462,21 +7364,21 @@ function freeBudgetWarning(billing, anon) {
6462
7364
  if (!anon || !billing) return null;
6463
7365
  if (typeof billing.free_turns_remaining === "number") {
6464
7366
  const left2 = billing.free_turns_remaining;
6465
- if (left2 <= 0) return import_chalk40.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
6466
- return import_chalk40.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
7367
+ if (left2 <= 0) return import_chalk43.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
7368
+ return import_chalk43.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
6467
7369
  }
6468
7370
  if (typeof billing.free_remaining_cents !== "number") return null;
6469
7371
  const perTurn = Math.max(billing.cents || 0, 0.02);
6470
7372
  const left = Math.floor(billing.free_remaining_cents / perTurn);
6471
7373
  if (left > 8) return null;
6472
- if (left <= 0) return import_chalk40.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
6473
- return import_chalk40.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
7374
+ if (left <= 0) return import_chalk43.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
7375
+ return import_chalk43.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
6474
7376
  }
6475
7377
  function printAssistant(delta) {
6476
7378
  for (let i = delta.length - 1; i >= 0; i--) {
6477
7379
  const m = delta[i];
6478
7380
  if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
6479
- console.log("\n" + import_chalk40.default.green("assistant \u203A ") + m.content + "\n");
7381
+ console.log("\n" + import_chalk43.default.green("assistant \u203A ") + m.content + "\n");
6480
7382
  return;
6481
7383
  }
6482
7384
  }
@@ -6502,7 +7404,7 @@ async function replTurn(p, messages, userText) {
6502
7404
  });
6503
7405
  } catch (err) {
6504
7406
  spinner.fail("Network error.");
6505
- console.log(import_chalk40.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
7407
+ console.log(import_chalk43.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
6506
7408
  return;
6507
7409
  }
6508
7410
  let data = null;
@@ -6522,9 +7424,9 @@ async function replTurn(p, messages, userText) {
6522
7424
  if (res.status === 401) {
6523
7425
  const reason = data && (data.error || data.message) || null;
6524
7426
  const usingKey = !p.consumerAuth;
6525
- console.log(import_chalk40.default.red(` The proxy rejected the request (401)${reason ? `: ${reason}` : "."}`));
7427
+ console.log(import_chalk43.default.red(` The proxy rejected the request (401)${reason ? `: ${reason}` : "."}`));
6526
7428
  console.log(
6527
- import_chalk40.default.dim(
7429
+ import_chalk43.default.dim(
6528
7430
  usingKey ? " Sent an API key. If the key was revoked, re-run `apiblaze apichat` to re-provision." : " Sent your consumer OAuth token. Re-run `apiblaze apichat` to sign in again."
6529
7431
  )
6530
7432
  );
@@ -6532,7 +7434,7 @@ async function replTurn(p, messages, userText) {
6532
7434
  }
6533
7435
  if (!res.ok || !data) {
6534
7436
  const err = data && data.error || `HTTP ${res.status}`;
6535
- console.log(import_chalk40.default.red(` Chat error: ${err}`));
7437
+ console.log(import_chalk43.default.red(` Chat error: ${err}`));
6536
7438
  return;
6537
7439
  }
6538
7440
  if (Array.isArray(data.delta)) {
@@ -6553,25 +7455,25 @@ async function replTurn(p, messages, userText) {
6553
7455
  if (warn) console.log(warn);
6554
7456
  if (!data.continue) return;
6555
7457
  }
6556
- console.log(import_chalk40.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
7458
+ console.log(import_chalk43.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
6557
7459
  }
6558
7460
  function renderUpsell(p, upsell) {
6559
7461
  const loggedIn = !!loadCredentials();
6560
7462
  if (upsell.reason === "CAPPED" && !loggedIn) {
6561
- console.log("\n" + import_chalk40.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
6562
- console.log(import_chalk40.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
7463
+ console.log("\n" + import_chalk43.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
7464
+ console.log(import_chalk43.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
6563
7465
  console.log();
6564
7466
  return;
6565
7467
  }
6566
- console.log("\n" + import_chalk40.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
6567
- if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
7468
+ console.log("\n" + import_chalk43.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
7469
+ if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "QUOTA" || upsell.reason === "PROXY_CAP" || upsell.reason === "PAUSED" || upsell.reason === "BYO_REQUIRED") {
6568
7470
  if (!loggedIn) {
6569
- console.log(import_chalk40.default.dim(" Options: `/login` for more free chats and requests, or `apiblaze llm set-key` to bring your own model key."));
7471
+ console.log(import_chalk43.default.dim(" Options: `/login` for more free chats and requests, or `apiblaze llm set-key` to bring your own model key."));
6570
7472
  } else {
6571
- console.log(import_chalk40.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
7473
+ console.log(import_chalk43.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
6572
7474
  }
6573
7475
  } else if (upsell.reason === "INFLIGHT") {
6574
- console.log(import_chalk40.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
7476
+ console.log(import_chalk43.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
6575
7477
  }
6576
7478
  console.log();
6577
7479
  }
@@ -6664,9 +7566,9 @@ async function openServerProxy(project) {
6664
7566
  let consumerAuth = false;
6665
7567
  if (acceptsApiKey) {
6666
7568
  if (!dpKey) {
6667
- console.log(import_chalk40.default.dim(` Minting an API key for tenant ${import_chalk40.default.bold(tenant2)} to query project ${import_chalk40.default.bold(project.projectName)}\u2026`));
7569
+ console.log(import_chalk43.default.dim(` Minting an API key for tenant ${import_chalk43.default.bold(tenant2)} to query project ${import_chalk43.default.bold(project.projectName)}\u2026`));
6668
7570
  dpKey = await mintDurableProxyKey(project.teamId, tenant2);
6669
- console.log(` ${import_chalk40.default.green("\u2714")} API key: ${import_chalk40.default.dim(maskKey(dpKey))}`);
7571
+ console.log(` ${import_chalk43.default.green("\u2714")} API key: ${import_chalk43.default.dim(maskKey(dpKey))}`);
6670
7572
  }
6671
7573
  } else {
6672
7574
  consumerAuth = true;
@@ -6695,9 +7597,9 @@ async function openServerProxy(project) {
6695
7597
  const spec2 = raw && (raw.spec ?? raw);
6696
7598
  spinner.stop();
6697
7599
  if (!spec2 || !(spec2.paths || spec2.openapi)) {
6698
- console.log(import_chalk40.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
7600
+ console.log(import_chalk43.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
6699
7601
  } else {
6700
- console.log(import_chalk40.default.dim(" Using the proxy's existing MCP catalogue (`apiblaze mcp` to rebuild it)."));
7602
+ console.log(import_chalk43.default.dim(" Using the proxy's existing MCP catalogue (`apiblaze mcp` to rebuild it)."));
6701
7603
  }
6702
7604
  } catch (err) {
6703
7605
  spinner.fail("Could not open the proxy.");
@@ -6753,7 +7655,7 @@ async function noArgsMenu(opts) {
6753
7655
  const me = loadCredentials()?.apiblazeUserId;
6754
7656
  const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
6755
7657
  const choices = saved.map((a) => ({
6756
- name: `Chat with ${import_chalk40.default.bold(a.name)} ${import_chalk40.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
7658
+ name: `Chat with ${import_chalk43.default.bold(a.name)} ${import_chalk43.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
6757
7659
  value: { type: "existing", a }
6758
7660
  }));
6759
7661
  const creds = loadCredentials();
@@ -6763,14 +7665,14 @@ async function noArgsMenu(opts) {
6763
7665
  const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
6764
7666
  for (const pr of proxies) {
6765
7667
  choices.push({
6766
- name: `Chat with ${import_chalk40.default.bold(pr.projectName)} ${import_chalk40.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
7668
+ name: `Chat with ${import_chalk43.default.bold(pr.projectName)} ${import_chalk43.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
6767
7669
  value: { type: "server", project: pr }
6768
7670
  });
6769
7671
  }
6770
7672
  } catch {
6771
7673
  }
6772
7674
  }
6773
- choices.push({ name: import_chalk40.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
7675
+ choices.push({ name: import_chalk43.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
6774
7676
  const { pick: pick2 } = await inquirer3.prompt([
6775
7677
  { type: "list", name: "pick", message: "What would you like to do?", choices }
6776
7678
  ]);
@@ -6873,36 +7775,36 @@ async function noArgsMenu(opts) {
6873
7775
  async function runRepl(p, initialMessages) {
6874
7776
  const { default: inquirer3 } = await import("inquirer");
6875
7777
  const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
6876
- console.log("\n" + import_chalk40.default.cyan.bold("Chat with your API") + import_chalk40.default.dim(` \xB7 ${p.mcpHost}`));
6877
- if (messages.length) console.log(import_chalk40.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
7778
+ console.log("\n" + import_chalk43.default.cyan.bold("Chat with your API") + import_chalk43.default.dim(` \xB7 ${p.mcpHost}`));
7779
+ if (messages.length) console.log(import_chalk43.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
6878
7780
  const llm2 = loadLlmConfig();
6879
7781
  console.log(
6880
- import_chalk40.default.dim(
7782
+ import_chalk43.default.dim(
6881
7783
  llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for more free chats \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
6882
7784
  )
6883
7785
  );
6884
7786
  for (; ; ) {
6885
- const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk40.default.green("you \u203A") }]);
7787
+ const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk43.default.green("you \u203A") }]);
6886
7788
  const text = (input ?? "").trim();
6887
7789
  if (!text) continue;
6888
7790
  if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
6889
7791
  if (text === "/login") {
6890
7792
  try {
6891
7793
  await runLogin();
6892
- console.log(import_chalk40.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
7794
+ console.log(import_chalk43.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
6893
7795
  } catch (err) {
6894
- console.log(import_chalk40.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
7796
+ console.log(import_chalk43.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
6895
7797
  }
6896
7798
  continue;
6897
7799
  }
6898
7800
  if (text === "/claim") {
6899
7801
  const justLoggedIn = !loadCredentials();
6900
7802
  if (justLoggedIn) {
6901
- console.log(import_chalk40.default.dim(" Logging in to claim your workspace\u2026"));
7803
+ console.log(import_chalk43.default.dim(" Logging in to claim your workspace\u2026"));
6902
7804
  try {
6903
7805
  await runLogin();
6904
7806
  } catch (err) {
6905
- console.log(import_chalk40.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
7807
+ console.log(import_chalk43.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
6906
7808
  continue;
6907
7809
  }
6908
7810
  if (!loadCredentials()) continue;
@@ -6913,30 +7815,30 @@ async function runRepl(p, initialMessages) {
6913
7815
  p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
6914
7816
  p.anon = false;
6915
7817
  claimApichat(p, loadCredentials()?.apiblazeUserId);
6916
- console.log(import_chalk40.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
7818
+ console.log(import_chalk43.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
6917
7819
  }
6918
7820
  } catch (err) {
6919
- console.log(import_chalk40.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
7821
+ console.log(import_chalk43.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
6920
7822
  }
6921
7823
  continue;
6922
7824
  }
6923
7825
  if (text === "/showauth") {
6924
7826
  revealAuth = !revealAuth;
6925
- console.log(import_chalk40.default.dim(revealAuth ? " The real API key will be shown in the next call's curl. /showauth again to re-mask." : " API key re-masked."));
7827
+ console.log(import_chalk43.default.dim(revealAuth ? " The real API key will be shown in the next call's curl. /showauth again to re-mask." : " API key re-masked."));
6926
7828
  continue;
6927
7829
  }
6928
7830
  if (text.startsWith("/")) {
6929
- console.log(import_chalk40.default.dim(" Commands: /login /claim /showauth /exit"));
7831
+ console.log(import_chalk43.default.dim(" Commands: /login /claim /showauth /exit"));
6930
7832
  continue;
6931
7833
  }
6932
7834
  await replTurn(p, messages, text);
6933
7835
  saveTranscript(p, messages);
6934
7836
  }
6935
- console.log(import_chalk40.default.dim("\nBye."));
7837
+ console.log(import_chalk43.default.dim("\nBye."));
6936
7838
  }
6937
7839
  async function runApichat(opts) {
6938
7840
  setVerbose(opts.verbose !== false);
6939
- console.log(import_chalk40.default.bold("\napichat \u2014 turn any API into a chat\n"));
7841
+ console.log(import_chalk43.default.bold("\napichat \u2014 turn any API into a chat\n"));
6940
7842
  if (!opts.openapispec && !opts.target) {
6941
7843
  if (!process.stdin.isTTY) {
6942
7844
  fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
@@ -6949,7 +7851,7 @@ async function runApichat(opts) {
6949
7851
  }
6950
7852
  const { spec: spec2, sourceUrl } = await loadSpec(opts);
6951
7853
  const target = resolveTarget(spec2, opts, sourceUrl);
6952
- console.log(` ${import_chalk40.default.dim("Target:")} ${import_chalk40.default.bold(target)}`);
7854
+ console.log(` ${import_chalk43.default.dim("Target:")} ${import_chalk43.default.bold(target)}`);
6953
7855
  const auth = await resolveTargetAuth(spec2, opts);
6954
7856
  if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
6955
7857
  fail4(
@@ -6958,7 +7860,7 @@ async function runApichat(opts) {
6958
7860
  );
6959
7861
  }
6960
7862
  const p = await provision(spec2, target, opts);
6961
- console.log(` ${import_chalk40.default.dim("Proxy: ")} ${import_chalk40.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
7863
+ console.log(` ${import_chalk43.default.dim("Proxy: ")} ${import_chalk43.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
6962
7864
  upsertApichat({
6963
7865
  name: p.projectId,
6964
7866
  target,
@@ -6978,30 +7880,30 @@ async function runApichat(opts) {
6978
7880
  const secret = await captureTargetSecret(auth, opts);
6979
7881
  if (secret) await writeTargetAuth(p, auth, secret);
6980
7882
  } else {
6981
- console.log(import_chalk40.default.dim(" Target auth: none required."));
7883
+ console.log(import_chalk43.default.dim(" Target auth: none required."));
6982
7884
  }
6983
7885
  const specText = JSON.stringify(spec2);
6984
7886
  await uploadSpec(p, specText, opts);
6985
7887
  const mcpUrl = await publishMcp(p, spec2);
6986
7888
  console.log();
6987
- if (p.proxyUrl) console.log(` ${import_chalk40.default.green("\u2713")} proxy ${import_chalk40.default.bold(p.proxyUrl)}`);
7889
+ if (p.proxyUrl) console.log(` ${import_chalk43.default.green("\u2713")} proxy ${import_chalk43.default.bold(p.proxyUrl)}`);
6988
7890
  if (mcpUrl) {
6989
- console.log(` ${import_chalk40.default.green("\u2713")} mcp ${import_chalk40.default.bold(mcpUrl)}`);
7891
+ console.log(` ${import_chalk43.default.green("\u2713")} mcp ${import_chalk43.default.bold(mcpUrl)}`);
6990
7892
  if (p.access === "invite") {
6991
- console.log(import_chalk40.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: invite \u2014 only you + emails you pre-approve"));
6992
- console.log(import_chalk40.default.dim(` Let others in: apiblaze preapprove someone@company.com${p.tenant ? ` --tenant ${p.tenant}` : ""} (or re-run with --access open)`));
7893
+ console.log(import_chalk43.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: invite \u2014 only you + emails you pre-approve"));
7894
+ console.log(import_chalk43.default.dim(` Let others in: apiblaze preapprove someone@company.com${p.tenant ? ` --tenant ${p.tenant}` : ""} (or re-run with --access open)`));
6993
7895
  } else {
6994
- console.log(import_chalk40.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: open \u2014 anyone who signs in can call this API"));
7896
+ console.log(import_chalk43.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: open \u2014 anyone who signs in can call this API"));
6995
7897
  }
6996
7898
  }
6997
7899
  if (p.anon) {
6998
- console.log(import_chalk40.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
7900
+ console.log(import_chalk43.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
6999
7901
  }
7000
7902
  await runRepl(p);
7001
7903
  }
7002
7904
 
7003
7905
  // src/commands/consumer.ts
7004
- var import_chalk41 = __toESM(require("chalk"));
7906
+ var import_chalk44 = __toESM(require("chalk"));
7005
7907
  var import_ora22 = __toESM(require("ora"));
7006
7908
  init_admin();
7007
7909
  init_resolve();
@@ -7020,10 +7922,10 @@ async function consumerFetch(creds, suffix, init) {
7020
7922
  }
7021
7923
  return { status: res.status, data, creds: fresh };
7022
7924
  }
7023
- function requireConsumer() {
7925
+ function requireConsumer2() {
7024
7926
  const c = loadConsumer();
7025
7927
  if (!c) {
7026
- console.error(import_chalk41.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
7928
+ console.error(import_chalk44.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
7027
7929
  process.exit(1);
7028
7930
  }
7029
7931
  return c;
@@ -7034,7 +7936,7 @@ async function runConsumerLogin(opts) {
7034
7936
  let clientId = opts.client;
7035
7937
  if (clientId) {
7036
7938
  if (!tenant2) {
7037
- console.error(import_chalk41.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
7939
+ console.error(import_chalk44.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
7038
7940
  process.exit(1);
7039
7941
  }
7040
7942
  } else {
@@ -7052,19 +7954,19 @@ async function runConsumerLogin(opts) {
7052
7954
  const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
7053
7955
  const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
7054
7956
  if (!pick2) {
7055
- console.error(import_chalk41.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
7957
+ console.error(import_chalk44.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
7056
7958
  process.exit(1);
7057
7959
  }
7058
7960
  clientId = pick2.client_id ?? pick2.clientId;
7059
7961
  }
7060
7962
  const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
7061
- console.log(`${import_chalk41.default.cyan("\u2192")} Logging in to ${import_chalk41.default.bold(tenant2)} as a consumer...`);
7963
+ console.log(`${import_chalk44.default.cyan("\u2192")} Logging in to ${import_chalk44.default.bold(tenant2)} as a consumer...`);
7062
7964
  const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
7063
7965
  console.log(`
7064
- Open: ${import_chalk41.default.underline(verificationUri)}`);
7065
- console.log(` Code: ${import_chalk41.default.bold(userCode)}
7966
+ Open: ${import_chalk44.default.underline(verificationUri)}`);
7967
+ console.log(` Code: ${import_chalk44.default.bold(userCode)}
7066
7968
  `);
7067
- console.log(import_chalk41.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
7969
+ console.log(import_chalk44.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
7068
7970
  }, portalResource);
7069
7971
  const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
7070
7972
  const creds = {
@@ -7079,10 +7981,10 @@ async function runConsumerLogin(opts) {
7079
7981
  obtainedAt: Date.now()
7080
7982
  };
7081
7983
  saveConsumer(creds);
7082
- console.log(import_chalk41.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
7984
+ console.log(import_chalk44.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
7083
7985
  }
7084
7986
  async function runConsumerTokens(opts) {
7085
- const creds = requireConsumer();
7987
+ const creds = requireConsumer2();
7086
7988
  const fresh = await validConsumerToken(creds) ?? creds;
7087
7989
  const exp = (t) => {
7088
7990
  const p = t ? decodeJwt2(t) : null;
@@ -7092,29 +7994,29 @@ async function runConsumerTokens(opts) {
7092
7994
  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));
7093
7995
  return;
7094
7996
  }
7095
- console.log(`${import_chalk41.default.cyan("Consumer")} ${import_chalk41.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk41.default.bold(fresh.tenant)}
7997
+ console.log(`${import_chalk44.default.cyan("Consumer")} ${import_chalk44.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk44.default.bold(fresh.tenant)}
7096
7998
  `);
7097
- console.log(`${import_chalk41.default.bold("access_token")} ${import_chalk41.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
7999
+ console.log(`${import_chalk44.default.bold("access_token")} ${import_chalk44.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
7098
8000
  ${fresh.accessToken}
7099
8001
  `);
7100
- if (fresh.idToken) console.log(`${import_chalk41.default.bold("id_token")} ${import_chalk41.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
8002
+ if (fresh.idToken) console.log(`${import_chalk44.default.bold("id_token")} ${import_chalk44.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
7101
8003
  ${fresh.idToken}
7102
8004
  `);
7103
- if (fresh.refreshToken) console.log(`${import_chalk41.default.bold("refresh_token")}
8005
+ if (fresh.refreshToken) console.log(`${import_chalk44.default.bold("refresh_token")}
7104
8006
  ${fresh.refreshToken}
7105
8007
  `);
7106
- console.log(import_chalk41.default.dim("These are your own tokens \u2014 keep them secret."));
8008
+ console.log(import_chalk44.default.dim("These are your own tokens \u2014 keep them secret."));
7107
8009
  }
7108
8010
  async function runConsumerApikeys(opts) {
7109
- const creds = requireConsumer();
8011
+ const creds = requireConsumer2();
7110
8012
  const { default: inquirer3 } = await import("inquirer");
7111
8013
  const spinner = (0, import_ora22.default)("Loading your API keys...").start();
7112
8014
  const list = await consumerFetch(creds, "/apikeys");
7113
8015
  const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
7114
8016
  spinner.stop();
7115
8017
  if (list.status >= 400) {
7116
- console.error(import_chalk41.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
7117
- if (list.status === 401) console.error(import_chalk41.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
8018
+ console.error(import_chalk44.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
8019
+ if (list.status === 401) console.error(import_chalk44.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
7118
8020
  process.exit(1);
7119
8021
  }
7120
8022
  const keys = list.data?.keys ?? [];
@@ -7122,16 +8024,16 @@ async function runConsumerApikeys(opts) {
7122
8024
  if (opts.json) {
7123
8025
  console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
7124
8026
  } else if (!keys.length) {
7125
- console.log(import_chalk41.default.yellow("No API keys yet."));
8027
+ console.log(import_chalk44.default.yellow("No API keys yet."));
7126
8028
  } else {
7127
8029
  for (const k of keys) {
7128
8030
  const clear = revealMap[k.environment]?.key;
7129
- const shown = clear ? import_chalk41.default.green(clear) : import_chalk41.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
7130
- const exp = k.expires_at ? import_chalk41.default.dim(`exp ${k.expires_at}`) : import_chalk41.default.dim("no expiry");
7131
- console.log(` ${import_chalk41.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk41.default.dim(k.description ?? "")}`);
8031
+ const shown = clear ? import_chalk44.default.green(clear) : import_chalk44.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
8032
+ const exp = k.expires_at ? import_chalk44.default.dim(`exp ${k.expires_at}`) : import_chalk44.default.dim("no expiry");
8033
+ console.log(` ${import_chalk44.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk44.default.dim(k.description ?? "")}`);
7132
8034
  }
7133
8035
  if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
7134
- console.log(import_chalk41.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
8036
+ console.log(import_chalk44.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
7135
8037
  }
7136
8038
  }
7137
8039
  if (opts.json) return;
@@ -7153,12 +8055,12 @@ async function runConsumerApikeys(opts) {
7153
8055
  }
7154
8056
  s2.succeed("Key created.");
7155
8057
  const key = created.data?.key ?? created.data?.fullKey;
7156
- if (key) console.log(` ${import_chalk41.default.green(key)} ${import_chalk41.default.dim("(shown once \u2014 store it now)")}`);
7157
- else console.log(import_chalk41.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
8058
+ if (key) console.log(` ${import_chalk44.default.green(key)} ${import_chalk44.default.dim("(shown once \u2014 store it now)")}`);
8059
+ else console.log(import_chalk44.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
7158
8060
  }
7159
8061
 
7160
8062
  // src/commands/sidecar.ts
7161
- var import_chalk42 = __toESM(require("chalk"));
8063
+ var import_chalk45 = __toESM(require("chalk"));
7162
8064
  var import_ora23 = __toESM(require("ora"));
7163
8065
  var fs10 = __toESM(require("fs"));
7164
8066
  var path7 = __toESM(require("path"));
@@ -7201,7 +8103,7 @@ function upsertEnvLocal(root, token) {
7201
8103
  }
7202
8104
  function installSidecarPackage(root) {
7203
8105
  if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
7204
- console.log(` ${import_chalk42.default.green("\u2713")} apiblaze package already installed`);
8106
+ console.log(` ${import_chalk45.default.green("\u2713")} apiblaze package already installed`);
7205
8107
  return;
7206
8108
  }
7207
8109
  const has = (f) => fs10.existsSync(path7.join(root, f));
@@ -7212,7 +8114,7 @@ function installSidecarPackage(root) {
7212
8114
  execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
7213
8115
  spinner.succeed("Installed apiblaze (the sidecar runtime).");
7214
8116
  } catch {
7215
- spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk42.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk42.default.cyan("npm run dev")}.`);
8117
+ spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk45.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk45.default.cyan("npm run dev")}.`);
7216
8118
  }
7217
8119
  }
7218
8120
  function readEnvKey(root) {
@@ -7363,29 +8265,29 @@ async function runAnonymousInit(root, router, opts) {
7363
8265
  if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
7364
8266
  const envState = upsertEnvLocal(root, out.token);
7365
8267
  ensureGitignored(root);
7366
- console.log(` ${import_chalk42.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
7367
- console.log(` ${import_chalk42.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
8268
+ console.log(` ${import_chalk45.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
8269
+ console.log(` ${import_chalk45.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
7368
8270
  installSidecarPackage(root);
7369
8271
  let inspectorPath = null;
7370
8272
  if (!opts.noInspector) {
7371
8273
  inspectorPath = generateInspector(root, router);
7372
- if (inspectorPath) console.log(` ${import_chalk42.default.green("\u2713")} inspector at ${inspectorPath}`);
8274
+ if (inspectorPath) console.log(` ${import_chalk45.default.green("\u2713")} inspector at ${inspectorPath}`);
7373
8275
  }
7374
8276
  console.log("");
7375
- console.log(import_chalk42.default.bold("Done (no account needed). What happens next:"));
7376
- console.log(` 1. ${import_chalk42.default.cyan("npm run dev")} and use your app.`);
8277
+ console.log(import_chalk45.default.bold("Done (no account needed). What happens next:"));
8278
+ console.log(` 1. ${import_chalk45.default.cyan("npm run dev")} and use your app.`);
7377
8279
  console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
7378
- console.log(` ${import_chalk42.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
8280
+ console.log(` ${import_chalk45.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
7379
8281
  console.log("");
7380
- console.log(import_chalk42.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
7381
- console.log(` ${import_chalk42.default.cyan("apiblaze login")} then ${import_chalk42.default.cyan("apiblaze claim")} ${import_chalk42.default.dim("(no code needed here)")}`);
7382
- console.log(import_chalk42.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
8282
+ console.log(import_chalk45.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
8283
+ console.log(` ${import_chalk45.default.cyan("apiblaze login")} then ${import_chalk45.default.cyan("apiblaze claim")} ${import_chalk45.default.dim("(no code needed here)")}`);
8284
+ console.log(import_chalk45.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
7383
8285
  }
7384
8286
  async function runSidecar(opts) {
7385
8287
  const root = path7.resolve(opts.dir ?? process.cwd());
7386
8288
  const detected = detectNextProject(root);
7387
8289
  if (!detected.found) {
7388
- console.log(import_chalk42.default.yellow(`No Next.js project detected in ${root}.`));
8290
+ console.log(import_chalk45.default.yellow(`No Next.js project detected in ${root}.`));
7389
8291
  console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
7390
8292
  return;
7391
8293
  }
@@ -7396,10 +8298,10 @@ async function runSidecar(opts) {
7396
8298
  if (!loadCredentials()) {
7397
8299
  upsertEnvLocal(root, readEnvKey(root));
7398
8300
  ensureGitignored(root);
7399
- console.log(` ${import_chalk42.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
7400
- console.log(` ${import_chalk42.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
8301
+ console.log(` ${import_chalk45.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
8302
+ console.log(` ${import_chalk45.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
7401
8303
  installSidecarPackage(root);
7402
- console.log(import_chalk42.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
8304
+ console.log(import_chalk45.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
7403
8305
  return;
7404
8306
  }
7405
8307
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -7422,38 +8324,38 @@ async function runSidecar(opts) {
7422
8324
  throw err;
7423
8325
  }
7424
8326
  } else {
7425
- console.log(import_chalk42.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
8327
+ console.log(import_chalk45.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
7426
8328
  }
7427
8329
  const envState = upsertEnvLocal(root, token);
7428
8330
  ensureGitignored(root);
7429
- console.log(` ${import_chalk42.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
8331
+ console.log(` ${import_chalk45.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
7430
8332
  const wireState = wireInstrumentation(root);
7431
- console.log(` ${import_chalk42.default.green("\u2713")} instrumentation.ts ${wireState}`);
8333
+ console.log(` ${import_chalk45.default.green("\u2713")} instrumentation.ts ${wireState}`);
7432
8334
  installSidecarPackage(root);
7433
8335
  let inspectorPath = null;
7434
8336
  if (!opts.noInspector) {
7435
8337
  inspectorPath = generateInspector(root, detected.router);
7436
- if (inspectorPath) console.log(` ${import_chalk42.default.green("\u2713")} inspector at ${inspectorPath}`);
8338
+ if (inspectorPath) console.log(` ${import_chalk45.default.green("\u2713")} inspector at ${inspectorPath}`);
7437
8339
  }
7438
8340
  console.log("");
7439
- console.log(import_chalk42.default.bold("Done. What happens next:"));
7440
- console.log(` 1. ${import_chalk42.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
7441
- console.log(` 2. The origins your app calls appear as ${import_chalk42.default.bold("candidates")} \u2014 list them: ${import_chalk42.default.cyan("apiblaze sidecar")}`);
7442
- console.log(` 3. Approve the ones to route: ${import_chalk42.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
8341
+ console.log(import_chalk45.default.bold("Done. What happens next:"));
8342
+ console.log(` 1. ${import_chalk45.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
8343
+ console.log(` 2. The origins your app calls appear as ${import_chalk45.default.bold("candidates")} \u2014 list them: ${import_chalk45.default.cyan("apiblaze sidecar")}`);
8344
+ console.log(` 3. Approve the ones to route: ${import_chalk45.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
7443
8345
  console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
7444
- if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk42.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
7445
- if (switchingTeam) console.log(import_chalk42.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
8346
+ if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk45.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
8347
+ if (switchingTeam) console.log(import_chalk45.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
7446
8348
  console.log("");
7447
- console.log(import_chalk42.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
7448
- console.log(import_chalk42.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
7449
- console.log(import_chalk42.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
8349
+ console.log(import_chalk45.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
8350
+ console.log(import_chalk45.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
8351
+ console.log(import_chalk45.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
7450
8352
  console.log("");
7451
- console.log(import_chalk42.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
7452
- console.log(import_chalk42.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
8353
+ console.log(import_chalk45.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
8354
+ console.log(import_chalk45.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
7453
8355
  }
7454
8356
 
7455
8357
  // src/commands/origins.ts
7456
- var import_chalk43 = __toESM(require("chalk"));
8358
+ var import_chalk46 = __toESM(require("chalk"));
7457
8359
  var import_ora24 = __toESM(require("ora"));
7458
8360
  init_admin();
7459
8361
  init_resolve();
@@ -7464,7 +8366,7 @@ async function runOriginsList(opts) {
7464
8366
  if (!loadCredentials()) {
7465
8367
  const cred = loadAnonCred();
7466
8368
  if (!cred) {
7467
- console.log(import_chalk43.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
8369
+ console.log(import_chalk46.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
7468
8370
  return;
7469
8371
  }
7470
8372
  out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
@@ -7482,27 +8384,27 @@ async function runOriginsList(opts) {
7482
8384
  }
7483
8385
  const routed = out.routed ?? [];
7484
8386
  const candidates = out.candidates ?? [];
7485
- console.log(import_chalk43.default.bold(`
8387
+ console.log(import_chalk46.default.bold(`
7486
8388
  Routed through APIblaze (${routed.length})`));
7487
- if (!routed.length) console.log(import_chalk43.default.dim(" none yet"));
7488
- for (const r of routed) console.log(` ${import_chalk43.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk43.default.dim(`\u2192 ${r.project_id}`)}`);
7489
- console.log(import_chalk43.default.bold(`
8389
+ if (!routed.length) console.log(import_chalk46.default.dim(" none yet"));
8390
+ for (const r of routed) console.log(` ${import_chalk46.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk46.default.dim(`\u2192 ${r.project_id}`)}`);
8391
+ console.log(import_chalk46.default.bold(`
7490
8392
  Candidates \u2014 going direct, not yet approved (${candidates.length})`));
7491
- if (!candidates.length) console.log(import_chalk43.default.dim(" none \u2014 run your app to discover the origins it calls"));
8393
+ if (!candidates.length) console.log(import_chalk46.default.dim(" none \u2014 run your app to discover the origins it calls"));
7492
8394
  for (const c of candidates) {
7493
- console.log(` ${import_chalk43.default.yellow("\u25CB")} ${c.origin} ${import_chalk43.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
8395
+ console.log(` ${import_chalk46.default.yellow("\u25CB")} ${c.origin} ${import_chalk46.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
7494
8396
  }
7495
8397
  if (candidates.length) {
7496
- console.log(import_chalk43.default.dim(`
8398
+ console.log(import_chalk46.default.dim(`
7497
8399
  Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
7498
- console.log(import_chalk43.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
8400
+ console.log(import_chalk46.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
7499
8401
  }
7500
8402
  }
7501
8403
  async function runOriginsApprove(origin, opts) {
7502
8404
  if (!loadCredentials()) {
7503
8405
  const cred = loadAnonCred();
7504
8406
  if (!cred) {
7505
- console.error(import_chalk43.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
8407
+ console.error(import_chalk46.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
7506
8408
  process.exit(1);
7507
8409
  }
7508
8410
  const spinner2 = (0, import_ora24.default)(`Approving ${origin} (anonymous)...`).start();
@@ -7555,7 +8457,7 @@ async function runOriginsRemove(origin, opts) {
7555
8457
  }
7556
8458
 
7557
8459
  // src/commands/op.ts
7558
- var import_chalk44 = __toESM(require("chalk"));
8460
+ var import_chalk47 = __toESM(require("chalk"));
7559
8461
  init_auth();
7560
8462
  init_trace();
7561
8463
  init_types();
@@ -7588,82 +8490,82 @@ function printResidue(report, applied) {
7588
8490
  const up = report?.upstash ?? {};
7589
8491
  const fga = report?.fga ?? {};
7590
8492
  const ghosts = report?.ghosts ?? {};
7591
- console.log(import_chalk44.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
7592
- console.log(import_chalk44.default.bold("\n Upstash"));
8493
+ console.log(import_chalk47.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
8494
+ console.log(import_chalk47.default.bold("\n Upstash"));
7593
8495
  const orphans = up.orphans ?? [];
7594
- if (orphans.length === 0) console.log(import_chalk44.default.green(" no orphaned keys"));
7595
- for (const o of orphans) console.log(` ${import_chalk44.default.yellow(o.key)} ${import_chalk44.default.dim(`\u2014 ${o.reason}`)}`);
7596
- console.log(import_chalk44.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
8496
+ if (orphans.length === 0) console.log(import_chalk47.default.green(" no orphaned keys"));
8497
+ for (const o of orphans) console.log(` ${import_chalk47.default.yellow(o.key)} ${import_chalk47.default.dim(`\u2014 ${o.reason}`)}`);
8498
+ console.log(import_chalk47.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
7597
8499
  if (up.anon_wallet_detail) {
7598
8500
  const d = up.anon_wallet_detail;
7599
- console.log(import_chalk44.default.dim(` anon wallets: ${d.count} ($${(d.total_cents / 100).toFixed(2)}), ${d.no_ttl} with NO TTL${d.no_ttl ? " \u26A0" : " (all self-expire)"}`));
8501
+ console.log(import_chalk47.default.dim(` anon wallets: ${d.count} ($${(d.total_cents / 100).toFixed(2)}), ${d.no_ttl} with NO TTL${d.no_ttl ? " \u26A0" : " (all self-expire)"}`));
7600
8502
  }
7601
8503
  if (up.keyspace_census) {
7602
8504
  const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
7603
- console.log(import_chalk44.default.dim(` keyspace: ${census}`));
8505
+ console.log(import_chalk47.default.dim(` keyspace: ${census}`));
7604
8506
  }
7605
- if (up.unknown?.length) console.log(import_chalk44.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
7606
- if (applied) console.log(` ${import_chalk44.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
7607
- for (const e of up.errors ?? []) console.log(import_chalk44.default.red(` error: ${e}`));
7608
- console.log(import_chalk44.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
8507
+ if (up.unknown?.length) console.log(import_chalk47.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
8508
+ if (applied) console.log(` ${import_chalk47.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
8509
+ for (const e of up.errors ?? []) console.log(import_chalk47.default.red(` error: ${e}`));
8510
+ console.log(import_chalk47.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
7609
8511
  if (applied) {
7610
8512
  const swept = fga?.swept ?? [];
7611
- if (swept.length === 0) console.log(import_chalk44.default.green(" no orphaned stores"));
8513
+ if (swept.length === 0) console.log(import_chalk47.default.green(" no orphaned stores"));
7612
8514
  for (const s of swept) {
7613
8515
  console.log(
7614
- ` ${import_chalk44.default.yellow(s.store_id)} ${import_chalk44.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
8516
+ ` ${import_chalk47.default.yellow(s.store_id)} ${import_chalk47.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
7615
8517
  );
7616
8518
  }
7617
- if (fga?.remaining) console.log(import_chalk44.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
8519
+ if (fga?.remaining) console.log(import_chalk47.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
7618
8520
  const st = fga?.side_tables;
7619
- if (st) console.log(import_chalk44.default.dim(` Neon side-tables purged: ${st.soft_deleted_stores} store records, ${st.orphan_models} models, ${st.orphan_changelog} changelog rows${st.error ? ` (${st.error})` : ""}`));
8521
+ if (st) console.log(import_chalk47.default.dim(` Neon side-tables purged: ${st.soft_deleted_stores} store records, ${st.orphan_models} models, ${st.orphan_changelog} changelog rows${st.error ? ` (${st.error})` : ""}`));
7620
8522
  } else {
7621
8523
  const fgaOrphans = fga?.orphans ?? [];
7622
- if (fgaOrphans.length === 0) console.log(import_chalk44.default.green(" no orphaned stores"));
8524
+ if (fgaOrphans.length === 0) console.log(import_chalk47.default.green(" no orphaned stores"));
7623
8525
  for (const s of fgaOrphans) {
7624
8526
  const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
7625
- console.log(` ${import_chalk44.default.yellow(s.store_id)} ${import_chalk44.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
8527
+ console.log(` ${import_chalk47.default.yellow(s.store_id)} ${import_chalk47.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
7626
8528
  }
7627
- console.log(import_chalk44.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
8529
+ console.log(import_chalk47.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
7628
8530
  const st = fga?.side_tables;
7629
- if (st) console.log(import_chalk44.default.dim(` Neon side-table residue: ${st.soft_deleted_stores} soft-deleted store records, ${st.orphan_models} orphan models, ${st.orphan_changelog} orphan changelog rows`));
8531
+ if (st) console.log(import_chalk47.default.dim(` Neon side-table residue: ${st.soft_deleted_stores} soft-deleted store records, ${st.orphan_models} orphan models, ${st.orphan_changelog} orphan changelog rows`));
7630
8532
  }
7631
- for (const e of fga?.errors ?? []) console.log(import_chalk44.default.red(` error: ${e}`));
7632
- console.log(import_chalk44.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
8533
+ for (const e of fga?.errors ?? []) console.log(import_chalk47.default.red(` error: ${e}`));
8534
+ console.log(import_chalk47.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
7633
8535
  if (applied) {
7634
- if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk44.default.green(" no ghost tuples"));
7635
- else console.log(` ${import_chalk44.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk44.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
8536
+ if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk47.default.green(" no ghost tuples"));
8537
+ else console.log(` ${import_chalk47.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk47.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
7636
8538
  } else {
7637
8539
  const n = ghosts?.ghost_count ?? 0;
7638
- if (n === 0) console.log(import_chalk44.default.green(` no ghost tuples ${import_chalk44.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
8540
+ if (n === 0) console.log(import_chalk47.default.green(` no ghost tuples ${import_chalk47.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
7639
8541
  else {
7640
- console.log(import_chalk44.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
8542
+ console.log(import_chalk47.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
7641
8543
  for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
7642
- console.log(import_chalk44.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
8544
+ console.log(import_chalk47.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
7643
8545
  }
7644
- if (n > 20) console.log(import_chalk44.default.dim(` \u2026 and ${n - 20} more`));
8546
+ if (n > 20) console.log(import_chalk47.default.dim(` \u2026 and ${n - 20} more`));
7645
8547
  }
7646
8548
  }
7647
- for (const e of ghosts?.errors ?? []) console.log(import_chalk44.default.red(` error: ${e}`));
8549
+ for (const e of ghosts?.errors ?? []) console.log(import_chalk47.default.red(` error: ${e}`));
7648
8550
  console.log();
7649
8551
  }
7650
8552
  async function runOp(sub, opts = {}) {
7651
8553
  if (!loadCredentials()) {
7652
- console.log(import_chalk44.default.dim("Not logged in. Run `apiblaze login`."));
8554
+ console.log(import_chalk47.default.dim("Not logged in. Run `apiblaze login`."));
7653
8555
  return;
7654
8556
  }
7655
8557
  if (!isOperatorLogin()) {
7656
- console.log(import_chalk44.default.dim("`apiblaze op` is only available to platform operators."));
8558
+ console.log(import_chalk47.default.dim("`apiblaze op` is only available to platform operators."));
7657
8559
  return;
7658
8560
  }
7659
8561
  switch (sub) {
7660
8562
  case void 0:
7661
8563
  case "menu": {
7662
- console.log(import_chalk44.default.bold("\nOperator menu"));
7663
- console.log(` ${import_chalk44.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
7664
- console.log(` ${import_chalk44.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk44.default.dim("-y to skip")})`);
7665
- console.log(` ${import_chalk44.default.cyan("apiblaze op credits")} list credit wallets`);
7666
- console.log(import_chalk44.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
8564
+ console.log(import_chalk47.default.bold("\nOperator menu"));
8565
+ console.log(` ${import_chalk47.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
8566
+ console.log(` ${import_chalk47.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk47.default.dim("-y to skip")})`);
8567
+ console.log(` ${import_chalk47.default.cyan("apiblaze op credits")} list credit wallets`);
8568
+ console.log(import_chalk47.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
7667
8569
  `));
7668
8570
  return;
7669
8571
  }
@@ -7682,17 +8584,17 @@ async function runOp(sub, opts = {}) {
7682
8584
  const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
7683
8585
  printResidue(report, false);
7684
8586
  if (nUp + nFga + nGhost + nSide === 0) {
7685
- console.log(import_chalk44.default.green("Nothing to sweep."));
8587
+ console.log(import_chalk47.default.green("Nothing to sweep."));
7686
8588
  return;
7687
8589
  }
7688
8590
  if (!opts.yes) {
7689
- const readline2 = await import("readline/promises");
7690
- const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
8591
+ const readline3 = await import("readline/promises");
8592
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
7691
8593
  const answer = await rl.question(
7692
- import_chalk44.default.red(`Delete ${nUp} Upstash key(s) + ${nFga} OpenFGA store(s) + ${nGhost} ghost tuple(s) + ${nSide} Neon side-table row(s)? Type 'sweep' to confirm: `)
8594
+ import_chalk47.default.red(`Delete ${nUp} Upstash key(s) + ${nFga} OpenFGA store(s) + ${nGhost} ghost tuple(s) + ${nSide} Neon side-table row(s)? Type 'sweep' to confirm: `)
7693
8595
  );
7694
8596
  rl.close();
7695
- if (answer.trim() !== "sweep") return void console.log(import_chalk44.default.dim("Aborted."));
8597
+ if (answer.trim() !== "sweep") return void console.log(import_chalk47.default.dim("Aborted."));
7696
8598
  }
7697
8599
  const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
7698
8600
  if (opts.json) return void console.log(JSON.stringify(result, null, 2));
@@ -7703,15 +8605,15 @@ async function runOp(sub, opts = {}) {
7703
8605
  const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
7704
8606
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
7705
8607
  const accounts = data?.accounts ?? [];
7706
- if (accounts.length === 0) return void console.log(import_chalk44.default.dim("No credit wallets."));
8608
+ if (accounts.length === 0) return void console.log(import_chalk47.default.dim("No credit wallets."));
7707
8609
  for (const a of accounts) {
7708
8610
  const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
7709
- console.log(` ${import_chalk44.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk44.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk44.default.dim(" \u2014 anon") : ""}`);
8611
+ console.log(` ${import_chalk47.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk47.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk47.default.dim(" \u2014 anon") : ""}`);
7710
8612
  }
7711
8613
  return;
7712
8614
  }
7713
8615
  default:
7714
- console.log(import_chalk44.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
8616
+ console.log(import_chalk47.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
7715
8617
  }
7716
8618
  }
7717
8619
 
@@ -7766,15 +8668,15 @@ var runSetup = (opts) => runSidecar({ team: opts.team, dir: opts.dir, yes: opts.
7766
8668
  var withSetupOptions = (cmd) => cmd.option("--team <id|name>", "Team to set up under (defaults to your active team)").option("--dir <path>", "Project directory (defaults to cwd)").option("--no-inspector", "Skip generating the dev-only /abz-inspector page").option("--rotate", "Mint a fresh invoke key even if one already exists").option("--new-session", "Start a fresh anonymous session (logged-out only)").option("-y, --yes", "Skip prompts");
7767
8669
  withSetupOptions(program.command("init").description("Set up the APIblaze sidecar in a Next.js app (shortcut for `apiblaze sidecar setup`)")).action(action((opts) => runSetup(opts)));
7768
8670
  var sidecar = program.command("sidecar").description("The APIblaze sidecar \u2014 set it up, then approve which origins route through APIblaze").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Machine-readable output").action(action((opts) => runOriginsList(opts)));
7769
- withSetupOptions(sidecar.command("setup").description("Wire a Next.js app to route its external fetches through APIblaze")).action(action((opts) => runSetup(opts)));
7770
- sidecar.command("approve").description("Route an origin through APIblaze (creates its proxy)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Machine-readable output").action(action((origin, opts) => runOriginsApprove(origin, opts)));
7771
- sidecar.command("deny").description("Dismiss a candidate origin so it stops being suggested").argument("<origin>", "Origin, e.g. sentry.io").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts) => runOriginsDeny(origin, opts)));
7772
- sidecar.command("remove").description("Un-route an approved origin (deletes its proxy; the app goes direct again)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts) => runOriginsRemove(origin, opts)));
8671
+ withSetupOptions(sidecar.command("setup").description("Wire a Next.js app to route its external fetches through APIblaze")).action(action((opts, cmd) => runSetup({ ...cmd.parent?.opts(), ...opts })));
8672
+ sidecar.command("approve").description("Route an origin through APIblaze (creates its proxy)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Machine-readable output").action(action((origin, opts, cmd) => runOriginsApprove(origin, { ...cmd.parent?.opts(), ...opts })));
8673
+ sidecar.command("deny").description("Dismiss a candidate origin so it stops being suggested").argument("<origin>", "Origin, e.g. sentry.io").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts, cmd) => runOriginsDeny(origin, { ...cmd.parent?.opts(), ...opts })));
8674
+ sidecar.command("remove").description("Un-route an approved origin (deletes its proxy; the app goes direct again)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts, cmd) => runOriginsRemove(origin, { ...cmd.parent?.opts(), ...opts })));
7773
8675
  program.command("dev").description("Put your localhost behind a public URL (dev tunnel)").argument("[port]", "Local port to tunnel (positional; overrides --port)").option("-p, --port <number>", "Local port to tunnel", "3000").option("--project <nameOrId>", "Tunnel this specific project (skips the picker \u2014 for scripts)").option("-y, --yes", "Skip confirmation prompts (non-interactive)").option("-o, --capture-file <path>", "Stream full request/response traffic to a file (JSON lines)").option("--new-session", "Logged-out only: start a fresh anonymous workspace instead of reusing this machine's (each run = a throwaway proxy)").action(async (port, opts) => {
7774
8676
  try {
7775
8677
  const resolved = parseInt(port ?? opts.port, 10);
7776
8678
  if (Number.isNaN(resolved)) {
7777
- console.error(import_chalk45.default.red(`Invalid port: ${port ?? opts.port}`));
8679
+ console.error(import_chalk48.default.red(`Invalid port: ${port ?? opts.port}`));
7778
8680
  process.exit(1);
7779
8681
  }
7780
8682
  await runDev({ port: resolved, project: opts.project, yes: opts.yes, captureFile: opts.captureFile, newSession: opts.newSession });
@@ -7787,6 +8689,8 @@ var consumer = program.command("consumer").description("Act as a consumer of you
7787
8689
  consumer.command("login").description("Log in to a tenant's portal as a consumer (device flow)").option("--team <id|name>", "Team whose tenants to choose from (producer mode)").option("--tenant <slug>", "Tenant to log in to (skips the picker)").option("--client <clientId>", "App client id \u2014 for standalone login without a producer session").action(action((opts) => runConsumerLogin(opts)));
7788
8690
  consumer.command("tokens").description("Show the logged-in consumer access / refresh / id tokens").option("--json", "Output machine-readable JSON").action(action((opts) => runConsumerTokens(opts)));
7789
8691
  consumer.command("apikeys").description("List your consumer API keys (reveals expiring ones), then offer to create one").option("--json", "Output machine-readable JSON").action(action((opts) => runConsumerApikeys(opts)));
8692
+ var consumerLogs = consumer.command("logs").description("API Consumer view \u2014 stream YOUR OWN requests on this tenant live (see `consumer logs list` for past days)").option("--project <name>", "Only this project (when the tenant serves several)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit raw NDJSON rows (pipe into jq)").action(action((opts) => runConsumerLogsTail(opts)));
8693
+ consumerLogs.command("list").description("API Consumer view \u2014 browse YOUR OWN past requests by day, then open one for detail").option("--project <name>", "Only this project (when the tenant serves several)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit ALL matching rows as NDJSON, newest first (non-interactive)").option("--plain", "Print day-grouped rows instead of prompting (automatic when piped)").action(action((_o, cmd) => runConsumerLogsList(cmd.optsWithGlobals())));
7790
8694
  program.command("flush").description("Wipe ALL local state (login, workspace keys, apichats, DP keys, secrets)").option("-y, --yes", "Skip the confirmation prompt").action(runFlush);
7791
8695
  program.command("logout").description("Sign out (asks whether to drop the Producer or Consumer login)").option("--producer", "Log out of the API Producer identity").option("--consumer", "Log out of the API Consumer identity").option("--all", "Log out of both").action(action((opts) => runLogout(opts)));
7792
8696
  program.command("whoami").description("Show who you are \u2014 both API Producer and API Consumer").option("--json", "Output machine-readable JSON").action(async (opts) => {
@@ -7823,6 +8727,8 @@ program.command("projects").description("List the projects in your team").action
7823
8727
  });
7824
8728
  program.command("delete").description("Delete a proxy and everything under it (asks first)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--team <id|name>", "Team the project is in (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").option("--json", "Output machine-readable JSON").action(action((project, version2, opts) => runDelete(project, version2, opts)));
7825
8729
  program.command("export").description("Export config and data for migration out of APIblaze (Kong, ...)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--kong", "Produce a runnable Kong OSS bundle (decK config + plugins + docker-compose)").option("--data", "Plain data export (default)").option("--secrets", "Include decrypted producer-supplied secrets (member role; audit-logged)").option("--keys <mode>", "API-key export: hashes (default, consumers keep keys) | mint | none").option("--no-consumers", "Skip the end-user lane (users, groups, keys)").option("-o, --out <file>", "Output zip path").option("--team <id|name>", "Team the project is in (defaults to active team)").action(action((project, version2, opts) => runExport(project, version2, { ...opts, noConsumers: opts.consumers === false })));
8730
+ var logs = program.command("logs").description("API Producer view \u2014 stream this proxy's requests from EVERY caller live (see `apiblaze logs list` to browse past days)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--tenant <slug>", "Tenant to tail (auto when one is attached; prompted otherwise)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit raw NDJSON rows (pipe into jq)").option("--team <id|name>", "Team the project is in (defaults to active team)").action(action((project, version2, opts) => runLogsTail(project, version2, opts)));
8731
+ logs.command("list").description("API Producer view \u2014 browse EVERY caller's past requests by day, then open one to see its 4 views").argument("<project>", "Project name or id").argument("[version]", "API version (defaults to the first match)").option("--tenant <slug>", "Tenant to read (auto when one is attached; prompted otherwise)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit ALL matching rows as NDJSON, newest first (non-interactive)").option("--plain", "Print day-grouped rows instead of prompting (automatic when piped)").option("--team <id|name>", "Team the project is in (defaults to active team)").action(action((project, version2, _o, cmd) => runLogsList(project, version2, cmd.optsWithGlobals())));
7826
8732
  program.command("config").description("Browse and change every proxy setting & feature (interactive; git-config-style get/set)").argument("[project]", "Project name or id (omit to pick interactively)").argument("[key]", "Setting key, e.g. throttling.proxyQuota (omit for the menu)").argument("[value]", "New value (omit to read the current value)").option("--list", "Print every setting and its current value").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version (defaults to the first match)").option("--json", "Machine-readable output (with --list or a key read)").action(action((project, key, value, opts) => runConfig(project, key, value, opts)));
7827
8733
  program.command("target").description("Change where a proxy forwards requests").argument("<project>", "Project name or id").requiredOption("--url <url>", "Target URL to forward to").option("--env <env>", "Environment to scope the target to (e.g. prod, dev)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version (defaults to the first match)").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTargetSet(project, opts)));
7828
8734
  program.command("throttle").description("Set rate limits and quotas for a proxy").argument("<project>", "Project name or id").option("--rate <n>", "User rate limit (requests/sec)").option("--end-user-rate <n>", "Per-end-user rate limit (requests/sec)").option("--quota <n>", "Proxy quota (requests/period)").option("--period <p>", "Quota period: daily | weekly | monthly").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runThrottleSet(project, opts)));
@@ -7874,10 +8780,11 @@ spec.command("delete-rule").description("Delete the saved rules for a route (e.g
7874
8780
  var HELP_GROUPS = [
7875
8781
  { title: "Chat", commands: ["apichat", "agent"] },
7876
8782
  { title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
7877
- { title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "admins", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
8783
+ { title: "Control plane commands", commands: ["config", "projects", "logs", "tenant", "group", "admins", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
7878
8784
  { title: "Data plane commands", commands: [
7879
8785
  { parent: "consumer", sub: "login" },
7880
- { parent: "consumer", sub: "apikeys" }
8786
+ { parent: "consumer", sub: "apikeys" },
8787
+ { parent: "consumer", sub: "logs" }
7881
8788
  ] }
7882
8789
  ];
7883
8790
  var helpLabel = (e) => typeof e === "string" ? e : `${e.parent} ${e.sub}`;
@@ -7893,7 +8800,7 @@ function groupedCommandHelp() {
7893
8800
  const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
7894
8801
  return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
7895
8802
  }).filter(Boolean).join("\n");
7896
- return `${import_chalk45.default.bold(g.title)}
8803
+ return `${import_chalk48.default.bold(g.title)}
7897
8804
  ${rows}`;
7898
8805
  }).join("\n\n");
7899
8806
  }
@@ -7928,14 +8835,14 @@ async function recoverStaleTeam() {
7928
8835
  const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
7929
8836
  const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
7930
8837
  if (!linked) {
7931
- console.error(import_chalk45.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
8838
+ console.error(import_chalk48.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
7932
8839
  return;
7933
8840
  }
7934
8841
  if (linked.teamId === creds.teamId) return;
7935
8842
  const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
7936
8843
  delete next.activeTenant;
7937
8844
  saveCredentials(next);
7938
- console.error(import_chalk45.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk45.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
8845
+ console.error(import_chalk48.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk48.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
7939
8846
  } catch {
7940
8847
  }
7941
8848
  }
@@ -7943,16 +8850,16 @@ async function printError(err) {
7943
8850
  if (err instanceof ApiError) {
7944
8851
  const data = err.body;
7945
8852
  const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
7946
- console.error(import_chalk45.default.red(`
8853
+ console.error(import_chalk48.default.red(`
7947
8854
  API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
7948
8855
  if (err.status === 403 || err.status === 404) {
7949
8856
  await recoverStaleTeam();
7950
8857
  }
7951
8858
  } else if (err instanceof Error) {
7952
- console.error(import_chalk45.default.red(`
8859
+ console.error(import_chalk48.default.red(`
7953
8860
  Error: ${err.message}`));
7954
8861
  } else {
7955
- console.error(import_chalk45.default.red("\nUnknown error"));
8862
+ console.error(import_chalk48.default.red("\nUnknown error"));
7956
8863
  }
7957
8864
  }
7958
8865
  program.parse(process.argv);