apiblaze 0.15.2 → 0.16.0

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 +440 -332
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -457,6 +457,139 @@ var init_admin = __esm({
457
457
  }
458
458
  });
459
459
 
460
+ // src/lib/tenant-create.ts
461
+ var tenant_create_exports = {};
462
+ __export(tenant_create_exports, {
463
+ createTenantInteractive: () => createTenantInteractive
464
+ });
465
+ async function createTenantRow(teamId) {
466
+ const { default: inquirer2 } = await import("inquirer");
467
+ for (; ; ) {
468
+ const { name } = await inquirer2.prompt([{
469
+ type: "input",
470
+ name: "name",
471
+ message: `Tenant name ${import_chalk22.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
472
+ validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
473
+ }]);
474
+ const slug = name.trim();
475
+ try {
476
+ const out = await admin({
477
+ method: "POST",
478
+ path: `/teams/${encodeURIComponent(teamId)}/tenants`,
479
+ body: { tenant_name: slug, display_name: slug },
480
+ summary: `Create tenant "${slug}"`
481
+ });
482
+ return out?.tenant_name ?? out?.tenant?.tenant_name ?? slug;
483
+ } catch (err) {
484
+ const msg = err instanceof Error ? err.message : String(err);
485
+ if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
486
+ console.log(import_chalk22.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
487
+ continue;
488
+ }
489
+ throw err;
490
+ }
491
+ }
492
+ }
493
+ async function setUpLogin(teamId, tenant2) {
494
+ const { default: inquirer2 } = await import("inquirer");
495
+ const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
496
+ const { provider } = await inquirer2.prompt([{
497
+ type: "list",
498
+ name: "provider",
499
+ message: "How do people log in?",
500
+ pageSize: 10,
501
+ choices: [
502
+ ...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
503
+ new inquirer2.Separator(),
504
+ { name: import_chalk22.default.dim("Skip for now \u2014 set up a login method later"), value: null }
505
+ ]
506
+ }]);
507
+ if (!provider) {
508
+ console.log(import_chalk22.default.dim(" No login method yet \u2014 add one later under Login methods."));
509
+ return;
510
+ }
511
+ const opt = PROVIDERS.find((p) => p.id === provider);
512
+ let providerBody;
513
+ if (!opt.own) {
514
+ providerBody = { type: "github", managed: true };
515
+ } else {
516
+ const a = await inquirer2.prompt([
517
+ { type: "input", name: "clientId", message: `${opt.label} OAuth client id:`, validate: (s) => !!s.trim() || "required" },
518
+ { type: "password", name: "clientSecret", mask: "*", message: `${opt.label} OAuth client secret:`, validate: (s) => s.length >= 6 && s.length <= 200 || "6\u2013200 chars" },
519
+ ...provider === "auth0" || provider === "other" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : []
520
+ ]);
521
+ providerBody = {
522
+ type: provider,
523
+ clientId: a.clientId.trim(),
524
+ clientSecret: a.clientSecret,
525
+ ...a.domain?.trim() ? { domain: a.domain.trim() } : {},
526
+ scopes: (DEFAULT_SCOPES[provider] ?? "openid email profile").split(/\s+/).filter(Boolean)
527
+ };
528
+ }
529
+ const { advanced } = await inquirer2.prompt([{ type: "confirm", name: "advanced", message: "Configure advanced settings (callback URLs, scopes)?", default: false }]);
530
+ const clientBody = { name: `${tenant2}-login` };
531
+ if (advanced) {
532
+ const a = await inquirer2.prompt([
533
+ { type: "input", name: "callbacks", message: "Callback URLs (comma-separated, empty = none):" },
534
+ { type: "input", name: "scopes", message: `Provider scopes:`, default: (providerBody.scopes ?? []).join(" ") }
535
+ ]);
536
+ if (a.callbacks.trim()) clientBody.authorizedCallbackUrls = a.callbacks.split(/\s*,\s*/).map((s) => s.trim()).filter(Boolean);
537
+ if (a.scopes.trim() && opt.own) providerBody.scopes = a.scopes.split(/\s+/).filter(Boolean);
538
+ }
539
+ const spinner = (0, import_ora8.default)("Setting up login...").start();
540
+ try {
541
+ const client = await admin({ method: "POST", path: `${base}/app-clients`, body: clientBody, summary: `Create login for ${tenant2}` });
542
+ const clientId = client?.clientId ?? client?.client_id;
543
+ await admin({ method: "POST", path: `${base}/app-clients/${encodeURIComponent(clientId)}/providers`, body: providerBody, summary: `Add ${provider} provider` });
544
+ spinner.succeed(opt.own ? `${opt.label} login is ready.` : "APIblaze-hosted GitHub login is ready.");
545
+ } catch (err) {
546
+ spinner.fail("Login setup failed (the tenant was still created).");
547
+ throw err;
548
+ }
549
+ }
550
+ async function createTenantInteractive(teamId) {
551
+ const { default: inquirer2 } = await import("inquirer");
552
+ const tenant2 = await createTenantRow(teamId);
553
+ console.log(import_chalk22.default.green(` Tenant ${import_chalk22.default.bold(tenant2)} created.`));
554
+ const { users } = await inquirer2.prompt([{
555
+ type: "confirm",
556
+ name: "users",
557
+ default: false,
558
+ message: `Enable Users & groups? ${import_chalk22.default.dim(`(identity/key management at ${tenant2}.iam.apiblaze.com)`)}`
559
+ }]);
560
+ if (users) {
561
+ await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
562
+ });
563
+ console.log(import_chalk22.default.green(" Users & groups enabled."));
564
+ }
565
+ await setUpLogin(teamId, tenant2);
566
+ return tenant2;
567
+ }
568
+ var import_chalk22, import_ora8, PROVIDERS, DEFAULT_SCOPES;
569
+ var init_tenant_create = __esm({
570
+ "src/lib/tenant-create.ts"() {
571
+ "use strict";
572
+ import_chalk22 = __toESM(require("chalk"));
573
+ import_ora8 = __toESM(require("ora"));
574
+ init_admin();
575
+ PROVIDERS = [
576
+ { id: "apiblaze", label: "APIblaze (via GitHub) \u2014 zero setup, recommended", own: false },
577
+ { id: "google", label: "Google", own: true },
578
+ { id: "github", label: "GitHub", own: true },
579
+ { id: "microsoft", label: "Microsoft", own: true },
580
+ { id: "facebook", label: "Facebook", own: true },
581
+ { id: "auth0", label: "Auth0", own: true },
582
+ { id: "other", label: "Custom (any OIDC provider)", own: true }
583
+ ];
584
+ DEFAULT_SCOPES = {
585
+ google: "openid email profile",
586
+ microsoft: "openid email profile",
587
+ github: "read:user user:email",
588
+ facebook: "public_profile email"
589
+ };
590
+ }
591
+ });
592
+
460
593
  // src/lib/tenant-pick.ts
461
594
  var tenant_pick_exports = {};
462
595
  __export(tenant_pick_exports, {
@@ -475,10 +608,10 @@ async function fetchPage(teamId, q) {
475
608
  }
476
609
  function label(t, defaultTenant, active) {
477
610
  const tags = [
478
- t.tenant_name === active ? import_chalk22.default.cyan("active scope") : "",
479
- t.tenant_name === defaultTenant ? import_chalk22.default.dim("team default") : ""
611
+ t.tenant_name === active ? import_chalk23.default.cyan("active scope") : "",
612
+ t.tenant_name === defaultTenant ? import_chalk23.default.dim("team default") : ""
480
613
  ].filter(Boolean).join(", ");
481
- const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk22.default.dim(` ${t.display_name}`) : "";
614
+ const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk23.default.dim(` ${t.display_name}`) : "";
482
615
  return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
483
616
  }
484
617
  async function pickTenant(teamId, opts = {}) {
@@ -486,14 +619,14 @@ async function pickTenant(teamId, opts = {}) {
486
619
  const active = loadCredentials()?.activeTenant;
487
620
  let q = opts.initialQuery ?? "";
488
621
  for (; ; ) {
489
- const spinner = (0, import_ora8.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
622
+ const spinner = (0, import_ora9.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
490
623
  const page = await fetchPage(teamId, q).finally(() => spinner.stop());
491
624
  if (!page.total && !q) {
492
625
  if (opts.allowCreate) {
493
626
  const { make } = await inquirer2.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
494
627
  if (make) return await createTenantInline(teamId);
495
628
  }
496
- console.error(import_chalk22.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
629
+ console.error(import_chalk23.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
497
630
  return null;
498
631
  }
499
632
  const truncated = page.total > page.rows.length;
@@ -502,7 +635,7 @@ async function pickTenant(teamId, opts = {}) {
502
635
  value: t.tenant_name
503
636
  }));
504
637
  if (truncated || q) {
505
- choices.push(new inquirer2.Separator(import_chalk22.default.dim(
638
+ choices.push(new inquirer2.Separator(import_chalk23.default.dim(
506
639
  truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
507
640
  )));
508
641
  choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
@@ -533,41 +666,15 @@ async function pickTenant(teamId, opts = {}) {
533
666
  }
534
667
  }
535
668
  async function createTenantInline(teamId) {
536
- const { default: inquirer2 } = await import("inquirer");
537
- for (; ; ) {
538
- const { name } = await inquirer2.prompt([{
539
- type: "input",
540
- name: "name",
541
- message: `Tenant name ${import_chalk22.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
542
- validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
543
- }]);
544
- const slugInput = name.trim();
545
- try {
546
- const out = await admin({
547
- method: "POST",
548
- path: `/teams/${encodeURIComponent(teamId)}/tenants`,
549
- body: { tenant_name: slugInput, display_name: slugInput },
550
- summary: `Create tenant "${slugInput}"`
551
- });
552
- const slug = out?.tenant_name ?? out?.tenant?.tenant_name ?? slugInput;
553
- console.log(import_chalk22.default.green(` Tenant ${import_chalk22.default.bold(slug)} created.`));
554
- return slug;
555
- } catch (err) {
556
- const msg = err instanceof Error ? err.message : String(err);
557
- if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
558
- console.log(import_chalk22.default.yellow(` "${slugInput}" is not available (tenant names are global): ${msg}`));
559
- continue;
560
- }
561
- throw err;
562
- }
563
- }
669
+ const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
670
+ return createTenantInteractive2(teamId);
564
671
  }
565
- var import_chalk22, import_ora8, PAGE;
672
+ var import_chalk23, import_ora9, PAGE;
566
673
  var init_tenant_pick = __esm({
567
674
  "src/lib/tenant-pick.ts"() {
568
675
  "use strict";
569
- import_chalk22 = __toESM(require("chalk"));
570
- import_ora8 = __toESM(require("ora"));
676
+ import_chalk23 = __toESM(require("chalk"));
677
+ import_ora9 = __toESM(require("ora"));
571
678
  init_admin();
572
679
  init_auth();
573
680
  PAGE = 15;
@@ -576,10 +683,10 @@ var init_tenant_pick = __esm({
576
683
 
577
684
  // src/index.ts
578
685
  var import_commander = require("commander");
579
- var import_chalk34 = __toESM(require("chalk"));
686
+ var import_chalk35 = __toESM(require("chalk"));
580
687
 
581
688
  // package.json
582
- var version = "0.15.2";
689
+ var version = "0.16.0";
583
690
 
584
691
  // src/index.ts
585
692
  init_types();
@@ -2652,8 +2759,8 @@ async function runRename(project, opts) {
2652
2759
  }
2653
2760
 
2654
2761
  // src/commands/config-browse.ts
2655
- var import_chalk28 = __toESM(require("chalk"));
2656
- var import_ora13 = __toESM(require("ora"));
2762
+ var import_chalk29 = __toESM(require("chalk"));
2763
+ var import_ora14 = __toESM(require("ora"));
2657
2764
  init_admin();
2658
2765
  init_auth();
2659
2766
 
@@ -2763,28 +2870,28 @@ async function runDomainSetBase(project, opts) {
2763
2870
  }
2764
2871
 
2765
2872
  // src/commands/tenant.ts
2766
- var import_chalk23 = __toESM(require("chalk"));
2767
- var import_ora9 = __toESM(require("ora"));
2873
+ var import_chalk24 = __toESM(require("chalk"));
2874
+ var import_ora10 = __toESM(require("ora"));
2768
2875
  init_admin();
2769
2876
  init_auth();
2770
2877
  init_tenant_pick();
2771
2878
  async function runTenantUse(query, opts) {
2772
2879
  const creds = loadCredentials();
2773
2880
  if (!creds) {
2774
- console.error(import_chalk23.default.red("Not logged in. Run `apiblaze login` first."));
2881
+ console.error(import_chalk24.default.red("Not logged in. Run `apiblaze login` first."));
2775
2882
  process.exit(1);
2776
2883
  }
2777
2884
  if (opts.clear) {
2778
2885
  delete creds.activeTenant;
2779
2886
  saveCredentials(creds);
2780
- console.log(import_chalk23.default.green("Tenant scope cleared."));
2887
+ console.log(import_chalk24.default.green("Tenant scope cleared."));
2781
2888
  return;
2782
2889
  }
2783
2890
  const { teamId } = await resolveTeam(opts.team);
2784
2891
  const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
2785
2892
  if (!slug) process.exit(1);
2786
2893
  saveCredentials({ ...creds, activeTenant: slug });
2787
- console.log(import_chalk23.default.green(`Tenant scope set to ${import_chalk23.default.bold(slug)}.`) + import_chalk23.default.dim(" (clear with `apiblaze tenant use --clear`)"));
2894
+ console.log(import_chalk24.default.green(`Tenant scope set to ${import_chalk24.default.bold(slug)}.`) + import_chalk24.default.dim(" (clear with `apiblaze tenant use --clear`)"));
2788
2895
  }
2789
2896
  async function runTenantList(opts) {
2790
2897
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -2801,26 +2908,26 @@ async function runTenantList(opts) {
2801
2908
  return;
2802
2909
  }
2803
2910
  if (!tenants.length) {
2804
- console.log(import_chalk23.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
2911
+ console.log(import_chalk24.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
2805
2912
  return;
2806
2913
  }
2807
2914
  for (const t of tenants) {
2808
2915
  const name = typeof t === "string" ? t : t.tenant_name;
2809
- const display = typeof t === "string" ? "" : import_chalk23.default.dim(` ${t.display_name ?? ""}`);
2810
- console.log(` ${import_chalk23.default.bold(name)}${display}`);
2916
+ const display = typeof t === "string" ? "" : import_chalk24.default.dim(` ${t.display_name ?? ""}`);
2917
+ console.log(` ${import_chalk24.default.bold(name)}${display}`);
2811
2918
  }
2812
2919
  const total = out?.total ?? tenants.length;
2813
2920
  if (total > tenants.length) {
2814
- console.log(import_chalk23.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
2921
+ console.log(import_chalk24.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
2815
2922
  }
2816
2923
  }
2817
2924
  async function runTenantCreate(opts) {
2818
2925
  if (!opts.name) {
2819
- console.error(import_chalk23.default.red("--name (display name) is required."));
2926
+ console.error(import_chalk24.default.red("--name (display name) is required."));
2820
2927
  process.exit(1);
2821
2928
  }
2822
2929
  const { teamId } = await resolveTeam(opts.team);
2823
- const spinner = (0, import_ora9.default)("Creating tenant...").start();
2930
+ const spinner = (0, import_ora10.default)("Creating tenant...").start();
2824
2931
  try {
2825
2932
  const out = await admin({
2826
2933
  method: "POST",
@@ -2828,7 +2935,7 @@ async function runTenantCreate(opts) {
2828
2935
  body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
2829
2936
  summary: `Create tenant "${opts.name}"`
2830
2937
  });
2831
- spinner.succeed(`Created tenant ${import_chalk23.default.bold(out?.tenant_name ?? opts.name)}.`);
2938
+ spinner.succeed(`Created tenant ${import_chalk24.default.bold(out?.tenant_name ?? opts.name)}.`);
2832
2939
  if (opts.json) console.log(JSON.stringify(out));
2833
2940
  } catch (err) {
2834
2941
  spinner.fail("Tenant create failed.");
@@ -2837,12 +2944,12 @@ async function runTenantCreate(opts) {
2837
2944
  }
2838
2945
  async function runTenantAttach(project, opts) {
2839
2946
  if (!opts.tenant) {
2840
- console.error(import_chalk23.default.red("--tenant <slug> is required."));
2947
+ console.error(import_chalk24.default.red("--tenant <slug> is required."));
2841
2948
  process.exit(1);
2842
2949
  }
2843
2950
  const { teamId } = await resolveTeam(opts.team);
2844
2951
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
2845
- const spinner = (0, import_ora9.default)("Attaching tenant...").start();
2952
+ const spinner = (0, import_ora10.default)("Attaching tenant...").start();
2846
2953
  try {
2847
2954
  const out = await admin({
2848
2955
  method: "POST",
@@ -2865,11 +2972,11 @@ async function runTenantDelete(slug, opts) {
2865
2972
  { type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
2866
2973
  ]);
2867
2974
  if (!confirm) {
2868
- console.log(import_chalk23.default.dim("Aborted."));
2975
+ console.log(import_chalk24.default.dim("Aborted."));
2869
2976
  return;
2870
2977
  }
2871
2978
  }
2872
- const spinner = (0, import_ora9.default)("Deleting tenant...").start();
2979
+ const spinner = (0, import_ora10.default)("Deleting tenant...").start();
2873
2980
  try {
2874
2981
  await admin({
2875
2982
  method: "DELETE",
@@ -2884,13 +2991,13 @@ async function runTenantDelete(slug, opts) {
2884
2991
  }
2885
2992
  async function runTenantCors(opts) {
2886
2993
  if (!opts.tenant) {
2887
- console.error(import_chalk23.default.red("--tenant <slug> is required."));
2994
+ console.error(import_chalk24.default.red("--tenant <slug> is required."));
2888
2995
  process.exit(1);
2889
2996
  }
2890
2997
  const { teamId } = await resolveTeam(opts.team);
2891
2998
  const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
2892
2999
  const cors = origins.length ? { allowed_origins: origins } : null;
2893
- const spinner = (0, import_ora9.default)("Updating CORS...").start();
3000
+ const spinner = (0, import_ora10.default)("Updating CORS...").start();
2894
3001
  try {
2895
3002
  await admin({
2896
3003
  method: "PUT",
@@ -2906,8 +3013,8 @@ async function runTenantCors(opts) {
2906
3013
  }
2907
3014
 
2908
3015
  // src/commands/tenant-drill.ts
2909
- var import_chalk24 = __toESM(require("chalk"));
2910
- var import_ora10 = __toESM(require("ora"));
3016
+ var import_chalk25 = __toESM(require("chalk"));
3017
+ var import_ora11 = __toESM(require("ora"));
2911
3018
  var import_crypto = require("crypto");
2912
3019
  init_admin();
2913
3020
  init_auth();
@@ -2936,17 +3043,17 @@ async function validScopedTenant(teamId, query) {
2936
3043
  const next = { ...creds };
2937
3044
  delete next.activeTenant;
2938
3045
  saveCredentials2(next);
2939
- console.log(import_chalk24.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
3046
+ console.log(import_chalk25.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
2940
3047
  return void 0;
2941
3048
  }
2942
3049
  async function tenantHome(teamId, tenant2) {
2943
3050
  const { default: inquirer2 } = await import("inquirer");
2944
3051
  const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
2945
- console.log(import_chalk24.default.bold(`
3052
+ console.log(import_chalk25.default.bold(`
2946
3053
  Tenant ${tenant2}`));
2947
- console.log(import_chalk24.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
3054
+ console.log(import_chalk25.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
2948
3055
  for (; ; ) {
2949
- const spinner = (0, import_ora10.default)("Reading tenant state...").start();
3056
+ const spinner = (0, import_ora11.default)("Reading tenant state...").start();
2950
3057
  const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
2951
3058
  admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
2952
3059
  admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
@@ -2958,19 +3065,20 @@ Tenant ${tenant2}`));
2958
3065
  const nEmails = (emails?.admin_emails ?? []).length;
2959
3066
  const nIssuers = (issuers?.external_issuers ?? []).length;
2960
3067
  const nClients = Array.isArray(clients) ? clients.length : 0;
2961
- const onOff = (b) => b ? import_chalk24.default.green("on") : import_chalk24.default.dim("off");
3068
+ const onOff = (b) => b ? import_chalk25.default.green("on") : import_chalk25.default.dim("off");
2962
3069
  const { pick: pick2 } = await inquirer2.prompt([{
2963
3070
  type: "list",
2964
3071
  name: "pick",
2965
3072
  message: `Tenant ${tenant2}:`,
2966
3073
  pageSize: 12,
2967
3074
  choices: [
2968
- { name: `App clients (${nClients}) ${import_chalk24.default.dim("OAuth clients your consumers log in through \u2014 providers live inside")}`, value: "clients" },
2969
- { name: `IAM enforcement: ${onOff(iam?.iam_enabled)} ${import_chalk24.default.dim("key/identity enforcement for this tenant")}`, value: "iam" },
2970
- { name: `CORS override: ${cors?.cors ? import_chalk24.default.cyan(JSON.stringify(cors.cors)) : import_chalk24.default.dim("(unset)")}`, value: "cors" },
2971
- { name: `Consumer-admin emails (${nEmails}) ${import_chalk24.default.dim("who may administer the tenant portal")}`, value: "emails" },
2972
- { name: `External JWT issuers (${nIssuers}) ${import_chalk24.default.dim("bring-your-own auth: trust tokens you already mint")}`, value: "issuers" },
2973
- { name: `Opaque-token validator: ${opaque?.opaque?.endpoint ? import_chalk24.default.cyan(opaque.opaque.endpoint) : import_chalk24.default.dim("(unset)")}`, value: "opaque" },
3075
+ { name: `Login methods (${nClients}) ${import_chalk25.default.dim("how your consumers sign in \u2014 providers live inside")}`, value: "clients" },
3076
+ { name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk25.default.dim(`identity & key management (${tenant2}.iam.apiblaze.com)`)}`, value: "iam" },
3077
+ { name: `Your own hosted login \u2014 JWT (${nIssuers}) ${import_chalk25.default.dim("trust tokens your own identity provider issues")}`, value: "issuers" },
3078
+ { name: `Portal admins (${nEmails}) ${import_chalk25.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
3079
+ new inquirer2.Separator(import_chalk25.default.dim(" Settings")),
3080
+ { name: `CORS: ${cors?.cors ? import_chalk25.default.cyan(JSON.stringify(cors.cors)) : import_chalk25.default.dim("(unset)")} ${import_chalk25.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
3081
+ { name: `Opaque-token login (advanced): ${opaque?.opaque?.endpoint ? import_chalk25.default.cyan(opaque.opaque.endpoint) : import_chalk25.default.dim("(unset)")}`, value: "opaque" },
2974
3082
  { name: "\u2190 Back", value: "back" }
2975
3083
  ]
2976
3084
  }]);
@@ -2981,9 +3089,9 @@ Tenant ${tenant2}`));
2981
3089
  await clientsMenu(teamId, tenant2, base);
2982
3090
  break;
2983
3091
  case "iam": {
2984
- const { v } = await inquirer2.prompt([{ type: "confirm", name: "v", message: "Enable IAM enforcement?", default: !!iam?.iam_enabled }]);
3092
+ const { v } = await inquirer2.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
2985
3093
  await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
2986
- console.log(import_chalk24.default.green(` IAM enforcement ${v ? "enabled" : "disabled"}.`));
3094
+ console.log(import_chalk25.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
2987
3095
  break;
2988
3096
  }
2989
3097
  case "cors": {
@@ -2996,11 +3104,11 @@ Tenant ${tenant2}`));
2996
3104
  if (v === "") break;
2997
3105
  const parsed = v === "null" ? null : safeJson(v);
2998
3106
  if (parsed === void 0) {
2999
- console.log(import_chalk24.default.yellow(" Not valid JSON \u2014 unchanged."));
3107
+ console.log(import_chalk25.default.yellow(" Not valid JSON \u2014 unchanged."));
3000
3108
  break;
3001
3109
  }
3002
3110
  await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
3003
- console.log(import_chalk24.default.green(" CORS updated."));
3111
+ console.log(import_chalk25.default.green(" CORS updated."));
3004
3112
  break;
3005
3113
  }
3006
3114
  case "emails":
@@ -3024,7 +3132,7 @@ Tenant ${tenant2}`));
3024
3132
  if (mode === "back") break;
3025
3133
  if (mode === "clear") {
3026
3134
  await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
3027
- console.log(import_chalk24.default.green(" Cleared."));
3135
+ console.log(import_chalk25.default.green(" Cleared."));
3028
3136
  break;
3029
3137
  }
3030
3138
  const a = await inquirer2.prompt([
@@ -3032,7 +3140,7 @@ Tenant ${tenant2}`));
3032
3140
  { type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
3033
3141
  ]);
3034
3142
  await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
3035
- console.log(import_chalk24.default.green(" Opaque validator set."));
3143
+ console.log(import_chalk25.default.green(" Opaque validator set."));
3036
3144
  break;
3037
3145
  }
3038
3146
  }
@@ -3048,8 +3156,8 @@ function safeJson(s) {
3048
3156
  async function emailsMenu(base, emails) {
3049
3157
  const { default: inquirer2 } = await import("inquirer");
3050
3158
  console.log();
3051
- if (!emails.length) console.log(import_chalk24.default.dim(" No consumer-admin emails."));
3052
- for (const e of emails) console.log(` ${import_chalk24.default.bold(e.email ?? e)} ${import_chalk24.default.dim(e.status ?? "")}`);
3159
+ if (!emails.length) console.log(import_chalk25.default.dim(" No consumer-admin emails."));
3160
+ for (const e of emails) console.log(` ${import_chalk25.default.bold(e.email ?? e)} ${import_chalk25.default.dim(e.status ?? "")}`);
3053
3161
  const { act } = await inquirer2.prompt([{
3054
3162
  type: "list",
3055
3163
  name: "act",
@@ -3064,7 +3172,7 @@ async function emailsMenu(base, emails) {
3064
3172
  if (act === "add") {
3065
3173
  const { email } = await inquirer2.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
3066
3174
  await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
3067
- console.log(import_chalk24.default.green(` ${email} added.`));
3175
+ console.log(import_chalk25.default.green(` ${email} added.`));
3068
3176
  } else {
3069
3177
  const { e } = await inquirer2.prompt([{
3070
3178
  type: "list",
@@ -3074,14 +3182,14 @@ async function emailsMenu(base, emails) {
3074
3182
  }]);
3075
3183
  if (!e) return;
3076
3184
  await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
3077
- console.log(import_chalk24.default.green(` ${e} removed.`));
3185
+ console.log(import_chalk25.default.green(` ${e} removed.`));
3078
3186
  }
3079
3187
  }
3080
3188
  async function issuersMenu(base, issuers) {
3081
3189
  const { default: inquirer2 } = await import("inquirer");
3082
3190
  console.log();
3083
- if (!issuers.length) console.log(import_chalk24.default.dim(" No external issuers \u2014 consumers use APIblaze-issued tokens."));
3084
- for (const i of issuers) console.log(` ${import_chalk24.default.bold(i.iss)} aud=${i.aud} ${import_chalk24.default.dim(i.sub_semantics ?? "")}`);
3191
+ if (!issuers.length) console.log(import_chalk25.default.dim(" No external issuers \u2014 consumers use APIblaze-issued tokens."));
3192
+ for (const i of issuers) console.log(` ${import_chalk25.default.bold(i.iss)} aud=${i.aud} ${import_chalk25.default.dim(i.sub_semantics ?? "")}`);
3085
3193
  const { act } = await inquirer2.prompt([{
3086
3194
  type: "list",
3087
3195
  name: "act",
@@ -3110,7 +3218,7 @@ async function issuersMenu(base, issuers) {
3110
3218
  body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
3111
3219
  summary: `Add external issuer ${a.iss.trim()}`
3112
3220
  });
3113
- console.log(import_chalk24.default.green(" Issuer saved."));
3221
+ console.log(import_chalk25.default.green(" Issuer saved."));
3114
3222
  } else {
3115
3223
  const { i } = await inquirer2.prompt([{
3116
3224
  type: "list",
@@ -3124,28 +3232,28 @@ async function issuersMenu(base, issuers) {
3124
3232
  path: `${base}/external-issuers?iss=${encodeURIComponent(i.iss)}&aud=${encodeURIComponent(i.aud)}`,
3125
3233
  summary: `Delete issuer ${i.iss}`
3126
3234
  });
3127
- console.log(import_chalk24.default.green(" Issuer deleted."));
3235
+ console.log(import_chalk25.default.green(" Issuer deleted."));
3128
3236
  }
3129
3237
  }
3130
3238
  async function clientsMenu(teamId, tenant2, base) {
3131
3239
  const { default: inquirer2 } = await import("inquirer");
3132
3240
  for (; ; ) {
3133
- const spinner = (0, import_ora10.default)("Loading app clients...").start();
3241
+ const spinner = (0, import_ora11.default)("Loading app clients...").start();
3134
3242
  const raw = await admin({ method: "GET", path: `${base}/app-clients`, summary: "List app clients" }).catch(() => []);
3135
3243
  spinner.stop();
3136
3244
  const clients = Array.isArray(raw) ? raw : [];
3137
3245
  const { pick: pick2 } = await inquirer2.prompt([{
3138
3246
  type: "list",
3139
3247
  name: "pick",
3140
- message: `App clients of ${tenant2}:`,
3248
+ message: `Login methods for ${tenant2}:`,
3141
3249
  pageSize: 15,
3142
3250
  choices: [
3143
3251
  ...clients.map((c) => ({
3144
- name: `${import_chalk24.default.bold(c.name ?? c.clientId)} ${import_chalk24.default.dim(`${c.clientId}${c.projectName ? ` \xB7 ${c.projectName}` : ""}`)}`,
3252
+ name: `${import_chalk25.default.bold(c.name ?? c.clientId)} ${import_chalk25.default.dim(`${c.clientId}${c.projectName ? ` \xB7 ${c.projectName}` : ""}`)}`,
3145
3253
  value: c
3146
3254
  })),
3147
- ...clients.length ? [] : [new inquirer2.Separator(import_chalk24.default.dim(" no app clients yet"))],
3148
- { name: "\uFF0B Create an app client\u2026", value: " create" },
3255
+ ...clients.length ? [] : [new inquirer2.Separator(import_chalk25.default.dim(" no app clients yet"))],
3256
+ { name: "\uFF0B Add a login method\u2026", value: " create" },
3149
3257
  { name: "\u2190 Back", value: " back" }
3150
3258
  ]
3151
3259
  }]);
@@ -3164,7 +3272,7 @@ async function clientsMenu(teamId, tenant2, base) {
3164
3272
  },
3165
3273
  summary: `Create app client "${a.name.trim()}"`
3166
3274
  });
3167
- console.log(import_chalk24.default.green(` App client created${created?.clientId ? ` (${created.clientId})` : ""}.`));
3275
+ console.log(import_chalk25.default.green(` Login method created${created?.clientId ? ` (${created.clientId})` : ""}.`));
3168
3276
  continue;
3169
3277
  }
3170
3278
  await clientHome(base, pick2);
@@ -3175,7 +3283,7 @@ async function clientHome(base, summary) {
3175
3283
  const id = summary.clientId ?? summary.client_id;
3176
3284
  const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
3177
3285
  for (; ; ) {
3178
- const spinner = (0, import_ora10.default)("Reading app client...").start();
3286
+ const spinner = (0, import_ora11.default)("Reading app client...").start();
3179
3287
  const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
3180
3288
  spinner.stop();
3181
3289
  const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
@@ -3187,13 +3295,13 @@ async function clientHome(base, summary) {
3187
3295
  message: `${c.name ?? id}:`,
3188
3296
  pageSize: 12,
3189
3297
  choices: [
3190
- { name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk24.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
3191
- { name: `Callback URLs: ${cb.length ? import_chalk24.default.cyan(cb.join(", ")) : import_chalk24.default.dim("(none)")}`, value: "callbacks" },
3192
- { name: `Scopes: ${scopes.length ? import_chalk24.default.cyan(scopes.join(" ")) : import_chalk24.default.dim("(defaults)")}`, value: "scopes" },
3298
+ { name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk25.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
3299
+ { name: `Callback URLs: ${cb.length ? import_chalk25.default.cyan(cb.join(", ")) : import_chalk25.default.dim("(none)")}`, value: "callbacks" },
3300
+ { name: `Scopes: ${scopes.length ? import_chalk25.default.cyan(scopes.join(" ")) : import_chalk25.default.dim("(defaults)")}`, value: "scopes" },
3193
3301
  { name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
3194
3302
  { name: "Reveal client secret", value: "secret" },
3195
3303
  { name: "Rotate client secret", value: "rotate" },
3196
- { name: import_chalk24.default.red("Delete this app client"), value: "delete" },
3304
+ { name: import_chalk25.default.red("Delete this app client"), value: "delete" },
3197
3305
  { name: "\u2190 Back", value: "back" }
3198
3306
  ]
3199
3307
  }]);
@@ -3206,13 +3314,13 @@ async function clientHome(base, summary) {
3206
3314
  case "callbacks": {
3207
3315
  const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
3208
3316
  await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
3209
- console.log(import_chalk24.default.green(" Callbacks updated."));
3317
+ console.log(import_chalk25.default.green(" Callbacks updated."));
3210
3318
  break;
3211
3319
  }
3212
3320
  case "scopes": {
3213
3321
  const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
3214
3322
  await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
3215
- console.log(import_chalk24.default.green(" Scopes updated."));
3323
+ console.log(import_chalk25.default.green(" Scopes updated."));
3216
3324
  break;
3217
3325
  }
3218
3326
  case "expiries": {
@@ -3227,14 +3335,14 @@ async function clientHome(base, summary) {
3227
3335
  body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
3228
3336
  summary: "Update token expiries"
3229
3337
  });
3230
- console.log(import_chalk24.default.green(" Expiries updated."));
3338
+ console.log(import_chalk25.default.green(" Expiries updated."));
3231
3339
  break;
3232
3340
  }
3233
3341
  case "secret": {
3234
3342
  const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
3235
3343
  if (!sure) break;
3236
3344
  const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
3237
- console.log(` ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3345
+ console.log(` ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3238
3346
  break;
3239
3347
  }
3240
3348
  case "rotate": {
@@ -3242,14 +3350,14 @@ async function clientHome(base, summary) {
3242
3350
  if (!sure) break;
3243
3351
  const fresh = randomSecret();
3244
3352
  await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
3245
- console.log(` New ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(fresh)} ${import_chalk24.default.dim("(store it now)")}`);
3353
+ console.log(` New ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(fresh)} ${import_chalk25.default.dim("(store it now)")}`);
3246
3354
  break;
3247
3355
  }
3248
3356
  case "delete": {
3249
3357
  const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: `Delete app client "${c.name ?? id}"? Consumers logged in through it will lose access.`, default: false }]);
3250
3358
  if (!sure) break;
3251
3359
  await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
3252
- console.log(import_chalk24.default.green(" App client deleted."));
3360
+ console.log(import_chalk25.default.green(" App client deleted."));
3253
3361
  return;
3254
3362
  }
3255
3363
  }
@@ -3261,7 +3369,7 @@ function randomSecret() {
3261
3369
  return Buffer.from(bytes).toString("base64url");
3262
3370
  }
3263
3371
  var PROVIDER_TYPES = ["google", "github", "microsoft", "facebook", "auth0", "other"];
3264
- var DEFAULT_SCOPES = {
3372
+ var DEFAULT_SCOPES2 = {
3265
3373
  google: "openid email profile",
3266
3374
  microsoft: "openid email profile",
3267
3375
  github: "read:user user:email",
@@ -3270,19 +3378,19 @@ var DEFAULT_SCOPES = {
3270
3378
  async function providersMenu(cBase, clientLabel) {
3271
3379
  const { default: inquirer2 } = await import("inquirer");
3272
3380
  for (; ; ) {
3273
- const spinner = (0, import_ora10.default)("Loading providers...").start();
3381
+ const spinner = (0, import_ora11.default)("Loading providers...").start();
3274
3382
  const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
3275
3383
  spinner.stop();
3276
3384
  const providers = Array.isArray(raw) ? raw : [];
3277
3385
  console.log();
3278
3386
  for (const p of providers) {
3279
- console.log(` ${import_chalk24.default.bold(p.type)} ${import_chalk24.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
3387
+ console.log(` ${import_chalk25.default.bold(p.type)} ${import_chalk25.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
3280
3388
  }
3281
- if (!providers.length) console.log(import_chalk24.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
3389
+ if (!providers.length) console.log(import_chalk25.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
3282
3390
  const { act } = await inquirer2.prompt([{
3283
3391
  type: "list",
3284
3392
  name: "act",
3285
- message: `Login providers of ${clientLabel}:`,
3393
+ message: `Sign-in providers for ${clientLabel}:`,
3286
3394
  choices: [
3287
3395
  { name: "\uFF0B Add a provider", value: "add" },
3288
3396
  ...providers.length ? [
@@ -3310,7 +3418,7 @@ async function providersMenu(cBase, clientLabel) {
3310
3418
  { type: "input", name: "clientId", message: `${type} OAuth client id:`, validate: (s) => !!s.trim() || "required" },
3311
3419
  { type: "password", name: "clientSecret", mask: "*", message: `${type} OAuth client secret:`, validate: (s) => s.length >= 6 && s.length <= 200 || "6\u2013200 chars" },
3312
3420
  ...type === "auth0" || type === "other" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : [],
3313
- { type: "input", name: "scopes", message: "Scopes:", default: DEFAULT_SCOPES[type] ?? "" }
3421
+ { type: "input", name: "scopes", message: "Scopes:", default: DEFAULT_SCOPES2[type] ?? "" }
3314
3422
  ]);
3315
3423
  body = {
3316
3424
  type,
@@ -3336,23 +3444,23 @@ async function providersMenu(cBase, clientLabel) {
3336
3444
  body.targetServerToken = routing;
3337
3445
  }
3338
3446
  await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
3339
- console.log(import_chalk24.default.green(` ${type} provider added.`));
3447
+ console.log(import_chalk25.default.green(` ${type} provider added.`));
3340
3448
  } else {
3341
3449
  const { p } = await inquirer2.prompt([{
3342
3450
  type: "list",
3343
3451
  name: "p",
3344
3452
  message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
3345
- choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk24.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
3453
+ choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk25.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
3346
3454
  }]);
3347
3455
  if (!p) continue;
3348
3456
  if (act === "rm") {
3349
3457
  const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: `Remove the ${p.type} provider? Consumers using it can no longer sign in.`, default: false }]);
3350
3458
  if (!sure) continue;
3351
3459
  await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
3352
- console.log(import_chalk24.default.green(` ${p.type} removed.`));
3460
+ console.log(import_chalk25.default.green(` ${p.type} removed.`));
3353
3461
  } else {
3354
3462
  const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
3355
- console.log(` ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3463
+ console.log(` ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3356
3464
  }
3357
3465
  }
3358
3466
  }
@@ -3360,8 +3468,8 @@ async function providersMenu(cBase, clientLabel) {
3360
3468
 
3361
3469
  // src/commands/spec.ts
3362
3470
  var fs6 = __toESM(require("fs"));
3363
- var import_chalk25 = __toESM(require("chalk"));
3364
- var import_ora11 = __toESM(require("ora"));
3471
+ var import_chalk26 = __toESM(require("chalk"));
3472
+ var import_ora12 = __toESM(require("ora"));
3365
3473
  init_admin();
3366
3474
  async function runSpecGet(project, opts) {
3367
3475
  const { teamId } = await resolveTeam(opts.team);
@@ -3375,19 +3483,19 @@ async function runSpecGet(project, opts) {
3375
3483
  }
3376
3484
  async function runSpecSet(project, opts) {
3377
3485
  if (!opts.file) {
3378
- console.error(import_chalk25.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
3486
+ console.error(import_chalk26.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
3379
3487
  process.exit(1);
3380
3488
  }
3381
3489
  let specContent;
3382
3490
  try {
3383
3491
  specContent = fs6.readFileSync(opts.file, "utf-8");
3384
3492
  } catch {
3385
- console.error(import_chalk25.default.red(`Cannot read file: ${opts.file}`));
3493
+ console.error(import_chalk26.default.red(`Cannot read file: ${opts.file}`));
3386
3494
  process.exit(1);
3387
3495
  }
3388
3496
  const { teamId } = await resolveTeam(opts.team);
3389
3497
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
3390
- const spinner = (0, import_ora11.default)("Uploading spec...").start();
3498
+ const spinner = (0, import_ora12.default)("Uploading spec...").start();
3391
3499
  try {
3392
3500
  const out = await admin({
3393
3501
  method: "POST",
@@ -3404,12 +3512,12 @@ async function runSpecSet(project, opts) {
3404
3512
  }
3405
3513
 
3406
3514
  // src/commands/agent.ts
3407
- var import_chalk27 = __toESM(require("chalk"));
3408
- var import_ora12 = __toESM(require("ora"));
3515
+ var import_chalk28 = __toESM(require("chalk"));
3516
+ var import_ora13 = __toESM(require("ora"));
3409
3517
  init_auth();
3410
3518
 
3411
3519
  // src/lib/tools.ts
3412
- var import_chalk26 = __toESM(require("chalk"));
3520
+ var import_chalk27 = __toESM(require("chalk"));
3413
3521
  init_admin();
3414
3522
  init_api();
3415
3523
  async function proj(teamId, name, version2) {
@@ -3428,15 +3536,15 @@ var TOOLS = [
3428
3536
  const key = keys.dev ?? Object.values(keys)[0];
3429
3537
  const url = `https://${a.name}.abz.run/${version2}/dev`;
3430
3538
  const tryIt = buildTryItCurl(url, auth, key);
3431
- const lines = [` ${import_chalk26.default.dim("Proxy URL:")} ${import_chalk26.default.bold(url)}`];
3432
- if (res.devPortal) lines.push(` ${import_chalk26.default.dim("Dev portal:")} ${res.devPortal}`);
3539
+ const lines = [` ${import_chalk27.default.dim("Proxy URL:")} ${import_chalk27.default.bold(url)}`];
3540
+ if (res.devPortal) lines.push(` ${import_chalk27.default.dim("Dev portal:")} ${res.devPortal}`);
3433
3541
  const envs = Object.keys(keys);
3434
3542
  if (envs.length) {
3435
- lines.push("", ` ${import_chalk26.default.bold("API keys")} ${import_chalk26.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
3543
+ lines.push("", ` ${import_chalk27.default.bold("API keys")} ${import_chalk27.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
3436
3544
  const w = Math.max(...envs.map((e) => e.length));
3437
- for (const env of envs) lines.push(` ${import_chalk26.default.cyan(env.padEnd(w))} ${import_chalk26.default.green(keys[env])}`);
3545
+ for (const env of envs) lines.push(` ${import_chalk27.default.cyan(env.padEnd(w))} ${import_chalk27.default.green(keys[env])}`);
3438
3546
  }
3439
- if (tryIt) lines.push("", ` ${import_chalk26.default.dim("Try it:")}`, ` ${import_chalk26.default.cyan(tryIt)}`);
3547
+ if (tryIt) lines.push("", ` ${import_chalk27.default.dim("Try it:")}`, ` ${import_chalk27.default.cyan(tryIt)}`);
3440
3548
  return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
3441
3549
  }
3442
3550
  },
@@ -3592,23 +3700,23 @@ function truncate(value, max = 1500) {
3592
3700
  }
3593
3701
  function printCost(llm) {
3594
3702
  const usd = llm.cost > 0 ? `$${llm.cost.toFixed(4)}` : "<$0.0001";
3595
- console.log(import_chalk27.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk27.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
3703
+ console.log(import_chalk28.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk28.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
3596
3704
  }
3597
3705
  async function runAgent(opts) {
3598
3706
  requireAuth();
3599
3707
  const { teamId, teamName } = await resolveTeam(opts.team);
3600
3708
  const { default: inquirer2 } = await import("inquirer");
3601
- console.log(import_chalk27.default.bold("APIblaze agent") + import_chalk27.default.dim(` \xB7 team ${teamName ?? teamId}`));
3602
- console.log(import_chalk27.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
3709
+ console.log(import_chalk28.default.bold("APIblaze agent") + import_chalk28.default.dim(` \xB7 team ${teamName ?? teamId}`));
3710
+ console.log(import_chalk28.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
3603
3711
  const history = [];
3604
3712
  while (true) {
3605
- const { input } = await inquirer2.prompt([{ type: "input", name: "input", message: import_chalk27.default.cyan("you") + " \u203A" }]);
3713
+ const { input } = await inquirer2.prompt([{ type: "input", name: "input", message: import_chalk28.default.cyan("you") + " \u203A" }]);
3606
3714
  const text = (input ?? "").trim();
3607
3715
  if (!text) continue;
3608
3716
  if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
3609
3717
  history.push({ role: "user", content: text });
3610
3718
  for (let step = 0; step < MAX_TOOL_STEPS; step++) {
3611
- const spinner = (0, import_ora12.default)({ text: "thinking...", color: "magenta" }).start();
3719
+ const spinner = (0, import_ora13.default)({ text: "thinking...", color: "magenta" }).start();
3612
3720
  let resp;
3613
3721
  try {
3614
3722
  resp = await callAgent(history, teamId);
@@ -3616,21 +3724,21 @@ async function runAgent(opts) {
3616
3724
  } catch (err) {
3617
3725
  spinner.stop();
3618
3726
  if (err instanceof ApiError && err.status === 402) {
3619
- console.log(import_chalk27.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
3727
+ console.log(import_chalk28.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
3620
3728
  break;
3621
3729
  }
3622
3730
  throw err;
3623
3731
  }
3624
3732
  history.push({ role: "assistant", content: resp.raw });
3625
3733
  printCost(resp.llm);
3626
- if (resp.reply) console.log(import_chalk27.default.green("agent") + " \u203A " + resp.reply);
3734
+ if (resp.reply) console.log(import_chalk28.default.green("agent") + " \u203A " + resp.reply);
3627
3735
  if (!resp.action) break;
3628
3736
  const tool = findTool(resp.action.tool);
3629
3737
  if (!tool) {
3630
3738
  history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
3631
3739
  continue;
3632
3740
  }
3633
- const runSpinner = (0, import_ora12.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
3741
+ const runSpinner = (0, import_ora13.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
3634
3742
  try {
3635
3743
  const result = await tool.run(resp.action.args, { teamId });
3636
3744
  runSpinner.succeed(`${tool.name} \u2713`);
@@ -3648,11 +3756,11 @@ async function runAgent(opts) {
3648
3756
  }
3649
3757
  renderTrace();
3650
3758
  if (step === MAX_TOOL_STEPS - 1) {
3651
- console.log(import_chalk27.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
3759
+ console.log(import_chalk28.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
3652
3760
  }
3653
3761
  }
3654
3762
  }
3655
- console.log(import_chalk27.default.dim("\nBye."));
3763
+ console.log(import_chalk28.default.dim("\nBye."));
3656
3764
  }
3657
3765
 
3658
3766
  // src/commands/config-browse.ts
@@ -3815,11 +3923,11 @@ function dig(blob, dotted) {
3815
3923
  }
3816
3924
  var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
3817
3925
  function show(v) {
3818
- if (v === void 0) return import_chalk28.default.dim("(unset)");
3819
- if (v === null) return import_chalk28.default.dim("null");
3820
- if (typeof v === "object") return import_chalk28.default.cyan(JSON.stringify(v));
3821
- if (typeof v === "boolean") return v ? import_chalk28.default.green("on") : import_chalk28.default.red("off");
3822
- return import_chalk28.default.cyan(String(v));
3926
+ if (v === void 0) return import_chalk29.default.dim("(unset)");
3927
+ if (v === null) return import_chalk29.default.dim("null");
3928
+ if (typeof v === "object") return import_chalk29.default.cyan(JSON.stringify(v));
3929
+ if (typeof v === "boolean") return v ? import_chalk29.default.green("on") : import_chalk29.default.red("off");
3930
+ return import_chalk29.default.cyan(String(v));
3823
3931
  }
3824
3932
  function parseValue(raw) {
3825
3933
  if (raw === "true") return true;
@@ -3846,7 +3954,7 @@ async function fetchConfigBlob(proj2) {
3846
3954
  }
3847
3955
  async function patchSetting(proj2, s, value, cfg) {
3848
3956
  const body = s.toPatch(value, cfg);
3849
- const spinner = (0, import_ora13.default)(`Set ${s.key}...`).start();
3957
+ const spinner = (0, import_ora14.default)(`Set ${s.key}...`).start();
3850
3958
  try {
3851
3959
  await admin({
3852
3960
  method: "PATCH",
@@ -3861,10 +3969,10 @@ async function patchSetting(proj2, s, value, cfg) {
3861
3969
  }
3862
3970
  }
3863
3971
  var loginFirst = (what) => {
3864
- console.log(import_chalk28.default.yellow(`
3972
+ console.log(import_chalk29.default.yellow(`
3865
3973
  Log in first to ${what}.`));
3866
- console.log(import_chalk28.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
3867
- console.log(import_chalk28.default.dim(" anonymously and want to bring it into your account.\n"));
3974
+ console.log(import_chalk29.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
3975
+ console.log(import_chalk29.default.dim(" anonymously and want to bring it into your account.\n"));
3868
3976
  };
3869
3977
  async function runConfig(project, key, value, opts) {
3870
3978
  const creds = loadCredentials();
@@ -3881,9 +3989,9 @@ async function runConfig(project, key, value, opts) {
3881
3989
  }
3882
3990
  const setting = SETTINGS.find((s) => s.key === key);
3883
3991
  if (!setting) {
3884
- console.error(import_chalk28.default.red(`Unknown setting "${key}".`));
3885
- console.error(import_chalk28.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
3886
- console.error(import_chalk28.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
3992
+ console.error(import_chalk29.default.red(`Unknown setting "${key}".`));
3993
+ console.error(import_chalk29.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
3994
+ console.error(import_chalk29.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
3887
3995
  process.exit(1);
3888
3996
  }
3889
3997
  if (value === void 0) {
@@ -3898,7 +4006,7 @@ async function pickProject(teamId) {
3898
4006
  const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
3899
4007
  const projects = await getProjects2(teamId).catch(() => []);
3900
4008
  if (!projects.length) {
3901
- console.error(import_chalk28.default.red("No projects in this team. Create one: `npx apiblaze create`."));
4009
+ console.error(import_chalk29.default.red("No projects in this team. Create one: `npx apiblaze create`."));
3902
4010
  process.exit(1);
3903
4011
  }
3904
4012
  const { default: inquirer2 } = await import("inquirer");
@@ -3906,7 +4014,7 @@ async function pickProject(teamId) {
3906
4014
  type: "list",
3907
4015
  name: "picked",
3908
4016
  message: "Which project?",
3909
- choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk28.default.dim("v" + p.apiVersion)}`, value: p }))
4017
+ choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk29.default.dim("v" + p.apiVersion)}`, value: p }))
3910
4018
  }]);
3911
4019
  return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
3912
4020
  }
@@ -3917,25 +4025,25 @@ function printAll(proj2, cfg, json) {
3917
4025
  console.log(JSON.stringify(out, null, 2));
3918
4026
  return;
3919
4027
  }
3920
- console.log(import_chalk28.default.bold(`
4028
+ console.log(import_chalk29.default.bold(`
3921
4029
  ${proj2.projectName} v${proj2.apiVersion} \u2014 settings
3922
4030
  `));
3923
4031
  for (const group of SETTING_GROUPS) {
3924
- console.log(import_chalk28.default.bold(group));
4032
+ console.log(import_chalk29.default.bold(group));
3925
4033
  for (const s of SETTINGS.filter((x) => x.group === group)) {
3926
- console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk28.default.dim(s.desc)}`);
4034
+ console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk29.default.dim(s.desc)}`);
3927
4035
  }
3928
4036
  console.log();
3929
4037
  }
3930
- console.log(import_chalk28.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
4038
+ console.log(import_chalk29.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
3931
4039
  }
3932
4040
  async function discoveryMenu(project) {
3933
4041
  const { default: inquirer2 } = await import("inquirer");
3934
- console.log(import_chalk28.default.bold(`
4042
+ console.log(import_chalk29.default.bold(`
3935
4043
  APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
3936
4044
  `));
3937
- console.log(import_chalk28.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
3938
- console.log(import_chalk28.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
4045
+ console.log(import_chalk29.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
4046
+ console.log(import_chalk29.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
3939
4047
  for (; ; ) {
3940
4048
  const { pick: pick2 } = await inquirer2.prompt([{
3941
4049
  type: "list",
@@ -3943,13 +4051,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
3943
4051
  message: "Explore:",
3944
4052
  pageSize: 20,
3945
4053
  choices: [
3946
- new inquirer2.Separator(import_chalk28.default.bold("\u2014 Settings \u2014")),
4054
+ new inquirer2.Separator(import_chalk29.default.bold("\u2014 Settings \u2014")),
3947
4055
  ...SETTING_GROUPS.map((g) => ({
3948
- name: `${g} ${import_chalk28.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
4056
+ name: `${g} ${import_chalk29.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
3949
4057
  value: { kind: "settings", g }
3950
4058
  })),
3951
- new inquirer2.Separator(import_chalk28.default.bold("\u2014 Features \u2014")),
3952
- ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk28.default.dim(f.desc)}`, value: { kind: "feature", f } })),
4059
+ new inquirer2.Separator(import_chalk29.default.bold("\u2014 Features \u2014")),
4060
+ ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk29.default.dim(f.desc)}`, value: { kind: "feature", f } })),
3953
4061
  new inquirer2.Separator(),
3954
4062
  { name: "Exit", value: { kind: "exit" } }
3955
4063
  ]
@@ -3958,24 +4066,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
3958
4066
  if (pick2.kind === "settings") {
3959
4067
  console.log();
3960
4068
  for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
3961
- console.log(` ${import_chalk28.default.bold(s.label.padEnd(28))} ${import_chalk28.default.dim(s.desc)}`);
3962
- console.log(` ${import_chalk28.default.dim(" key: " + s.key)}`);
4069
+ console.log(` ${import_chalk29.default.bold(s.label.padEnd(28))} ${import_chalk29.default.dim(s.desc)}`);
4070
+ console.log(` ${import_chalk29.default.dim(" key: " + s.key)}`);
3963
4071
  }
3964
4072
  loginFirst("view or change these settings");
3965
4073
  } else {
3966
4074
  const f = pick2.f;
3967
4075
  console.log(`
3968
- ${import_chalk28.default.bold(f.label)} \u2014 ${f.desc}`);
4076
+ ${import_chalk29.default.bold(f.label)} \u2014 ${f.desc}`);
3969
4077
  loginFirst(`use ${f.label.toLowerCase()}`);
3970
4078
  }
3971
4079
  }
3972
4080
  }
3973
4081
  async function navigator(proj2, cfg, opts) {
3974
4082
  const { default: inquirer2 } = await import("inquirer");
3975
- console.log(import_chalk28.default.bold(`
4083
+ console.log(import_chalk29.default.bold(`
3976
4084
  ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
3977
4085
  `));
3978
- console.log(import_chalk28.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
4086
+ console.log(import_chalk29.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
3979
4087
  let blob = cfg;
3980
4088
  for (; ; ) {
3981
4089
  const { pick: pick2 } = await inquirer2.prompt([{
@@ -3984,10 +4092,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
3984
4092
  message: "Where to?",
3985
4093
  pageSize: 20,
3986
4094
  choices: [
3987
- new inquirer2.Separator(import_chalk28.default.bold("\u2014 Settings \u2014")),
4095
+ new inquirer2.Separator(import_chalk29.default.bold("\u2014 Settings \u2014")),
3988
4096
  ...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
3989
- new inquirer2.Separator(import_chalk28.default.bold("\u2014 Features \u2014")),
3990
- ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk28.default.dim(f.desc)}`, value: { kind: f.go } })),
4097
+ new inquirer2.Separator(import_chalk29.default.bold("\u2014 Features \u2014")),
4098
+ ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk29.default.dim(f.desc)}`, value: { kind: f.go } })),
3991
4099
  new inquirer2.Separator(),
3992
4100
  { name: "Show all settings", value: { kind: "list" } },
3993
4101
  { name: "Exit", value: { kind: "exit" } }
@@ -4027,7 +4135,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
4027
4135
  }
4028
4136
  }
4029
4137
  } catch (err) {
4030
- console.error(import_chalk28.default.red(` ${err instanceof Error ? err.message : String(err)}`));
4138
+ console.error(import_chalk29.default.red(` ${err instanceof Error ? err.message : String(err)}`));
4031
4139
  }
4032
4140
  }
4033
4141
  }
@@ -4041,7 +4149,7 @@ async function settingsGroup(proj2, cfg, group) {
4041
4149
  message: group + ":",
4042
4150
  pageSize: 16,
4043
4151
  choices: [
4044
- ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk28.default.dim(s2.desc)}`, value: s2 })),
4152
+ ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk29.default.dim(s2.desc)}`, value: s2 })),
4045
4153
  new inquirer2.Separator(),
4046
4154
  { name: "\u2190 Back", value: null }
4047
4155
  ]
@@ -4059,7 +4167,7 @@ async function settingsGroup(proj2, cfg, group) {
4059
4167
  } else if (s.type === "number") {
4060
4168
  const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
4061
4169
  if (v === "" || Number.isNaN(Number(v))) {
4062
- console.log(import_chalk28.default.yellow(" Not a number \u2014 unchanged."));
4170
+ console.log(import_chalk29.default.yellow(" Not a number \u2014 unchanged."));
4063
4171
  continue;
4064
4172
  }
4065
4173
  value = Number(v);
@@ -4140,7 +4248,7 @@ async function buildCondition(phase) {
4140
4248
  const items = [];
4141
4249
  for (; ; ) {
4142
4250
  const a = await inquirer2.prompt([
4143
- { type: "input", name: "source", message: `Condition field ${import_chalk28.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
4251
+ { type: "input", name: "source", message: `Condition field ${import_chalk29.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
4144
4252
  { type: "list", name: "operator", message: "Operator:", choices: [
4145
4253
  "eq",
4146
4254
  "neq",
@@ -4171,7 +4279,7 @@ async function buildCondition(phase) {
4171
4279
  function showCondition(cond) {
4172
4280
  if (!Array.isArray(cond) || !cond.length) return "";
4173
4281
  const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
4174
- return import_chalk28.default.dim(` when ${s}`);
4282
+ return import_chalk29.default.dim(` when ${s}`);
4175
4283
  }
4176
4284
  async function transformsMenu(proj2) {
4177
4285
  const { default: inquirer2 } = await import("inquirer");
@@ -4180,12 +4288,12 @@ async function transformsMenu(proj2) {
4180
4288
  const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
4181
4289
  const rules = out?.rules ?? [];
4182
4290
  console.log();
4183
- if (!rules.length) console.log(import_chalk28.default.dim(" No transform rules yet."));
4291
+ if (!rules.length) console.log(import_chalk29.default.dim(" No transform rules yet."));
4184
4292
  for (const r of rules) {
4185
4293
  const a = r.action ?? {};
4186
4294
  const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
4187
- const what = a.type === "hardcode" ? `${a.destination} = "${a.value}"` : a.type === "remove" ? `remove ${a.field}` : `${a.source} \u2192 ${a.destination}${a.lookup ? " (mapped)" : ""}${fns.length ? import_chalk28.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
4188
- console.log(` ${r.enabled ? import_chalk28.default.green("\u25CF") : import_chalk28.default.dim("\u25CB")} ${import_chalk28.default.bold(r.name)} ${import_chalk28.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
4295
+ 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_chalk29.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
4296
+ console.log(` ${r.enabled ? import_chalk29.default.green("\u25CF") : import_chalk29.default.dim("\u25CB")} ${import_chalk29.default.bold(r.name)} ${import_chalk29.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
4189
4297
  }
4190
4298
  const { act } = await inquirer2.prompt([{
4191
4299
  type: "list",
@@ -4197,7 +4305,7 @@ async function transformsMenu(proj2) {
4197
4305
  { name: "Enable/disable a rule", value: "toggle" },
4198
4306
  { name: "Delete a rule", value: "delete" }
4199
4307
  ] : [],
4200
- { name: import_chalk28.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
4308
+ { name: import_chalk29.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
4201
4309
  { name: "\u2190 Back", value: "back" }
4202
4310
  ]
4203
4311
  }]);
@@ -4210,11 +4318,11 @@ async function transformsMenu(proj2) {
4210
4318
  }]);
4211
4319
  const body = parseValue(raw);
4212
4320
  if (!body || typeof body !== "object" || Array.isArray(body)) {
4213
- console.log(import_chalk28.default.yellow(" Not a JSON object \u2014 skipped."));
4321
+ console.log(import_chalk29.default.yellow(" Not a JSON object \u2014 skipped."));
4214
4322
  continue;
4215
4323
  }
4216
4324
  await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
4217
- console.log(import_chalk28.default.green(" Rule created."));
4325
+ console.log(import_chalk29.default.green(" Rule created."));
4218
4326
  continue;
4219
4327
  }
4220
4328
  if (act === "add") {
@@ -4230,7 +4338,7 @@ async function transformsMenu(proj2) {
4230
4338
  { name: "Remove a field", value: "remove" }
4231
4339
  ] }
4232
4340
  ]);
4233
- const fieldHint = import_chalk28.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
4341
+ const fieldHint = import_chalk29.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
4234
4342
  let action2;
4235
4343
  if (ans.kind === "hardcode") {
4236
4344
  const a = await inquirer2.prompt([
@@ -4261,7 +4369,7 @@ async function transformsMenu(proj2) {
4261
4369
  };
4262
4370
  }
4263
4371
  const condition = await buildCondition(ans.phase);
4264
- const spinner = (0, import_ora13.default)("Creating rule...").start();
4372
+ const spinner = (0, import_ora14.default)("Creating rule...").start();
4265
4373
  try {
4266
4374
  await admin({
4267
4375
  method: "POST",
@@ -4279,16 +4387,16 @@ async function transformsMenu(proj2) {
4279
4387
  type: "list",
4280
4388
  name: "rule",
4281
4389
  message: act === "toggle" ? "Which rule?" : "Delete which rule?",
4282
- choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk28.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
4390
+ choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk29.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
4283
4391
  }]);
4284
4392
  if (!rule) continue;
4285
4393
  if (act === "toggle") {
4286
4394
  const flipped = { ...rule, enabled: rule.enabled === false };
4287
4395
  await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
4288
- console.log(import_chalk28.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
4396
+ console.log(import_chalk29.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
4289
4397
  } else {
4290
4398
  await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
4291
- console.log(import_chalk28.default.green(` ${rule.name} deleted.`));
4399
+ console.log(import_chalk29.default.green(` ${rule.name} deleted.`));
4292
4400
  }
4293
4401
  }
4294
4402
  }
@@ -4300,9 +4408,9 @@ async function mappingsMenu(proj2) {
4300
4408
  const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
4301
4409
  const tables = out?.mappings ?? out?.tables ?? [];
4302
4410
  console.log();
4303
- if (!tables.length) console.log(import_chalk28.default.dim(" No mapping tables yet."));
4411
+ if (!tables.length) console.log(import_chalk29.default.dim(" No mapping tables yet."));
4304
4412
  for (const t of tables) {
4305
- console.log(` ${import_chalk28.default.bold(t.name)} ${import_chalk28.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
4413
+ console.log(` ${import_chalk29.default.bold(t.name)} ${import_chalk29.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
4306
4414
  }
4307
4415
  const { act } = await inquirer2.prompt([{
4308
4416
  type: "list",
@@ -4322,11 +4430,11 @@ async function mappingsMenu(proj2) {
4322
4430
  ]);
4323
4431
  const entries2 = parseValue(a.entries);
4324
4432
  if (!Array.isArray(entries2)) {
4325
- console.log(import_chalk28.default.yellow(" Entries must be a JSON array \u2014 not created."));
4433
+ console.log(import_chalk29.default.yellow(" Entries must be a JSON array \u2014 not created."));
4326
4434
  continue;
4327
4435
  }
4328
4436
  await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
4329
- console.log(import_chalk28.default.green(` Table "${a.name}" created.`));
4437
+ console.log(import_chalk29.default.green(` Table "${a.name}" created.`));
4330
4438
  } else {
4331
4439
  const { table } = await inquirer2.prompt([{
4332
4440
  type: "list",
@@ -4336,7 +4444,7 @@ async function mappingsMenu(proj2) {
4336
4444
  }]);
4337
4445
  if (!table) continue;
4338
4446
  await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
4339
- console.log(import_chalk28.default.green(` ${table.name} deleted.`));
4447
+ console.log(import_chalk29.default.green(` ${table.name} deleted.`));
4340
4448
  }
4341
4449
  }
4342
4450
  }
@@ -4347,14 +4455,14 @@ async function tenantsMenu(proj2, opts) {
4347
4455
  const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
4348
4456
  const tenants = out?.tenants ?? [];
4349
4457
  console.log();
4350
- if (!tenants.length) console.log(import_chalk28.default.dim(" No tenants attached (consumers use the default tenant)."));
4351
- for (const t of tenants) console.log(` ${import_chalk28.default.bold(t.tenant_name ?? t.name)} ${import_chalk28.default.dim(t.display_name ?? "")}`);
4458
+ if (!tenants.length) console.log(import_chalk29.default.dim(" No tenants attached (consumers use the default tenant)."));
4459
+ for (const t of tenants) console.log(` ${import_chalk29.default.bold(t.tenant_name ?? t.name)} ${import_chalk29.default.dim(t.display_name ?? "")}`);
4352
4460
  const { act } = await inquirer2.prompt([{
4353
4461
  type: "list",
4354
4462
  name: "act",
4355
4463
  message: "Tenants:",
4356
4464
  choices: [
4357
- { name: `Manage a tenant\u2026 ${import_chalk28.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
4465
+ { name: `Manage a tenant\u2026 ${import_chalk29.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
4358
4466
  { name: "Attach a tenant to this project", value: "attach" },
4359
4467
  ...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
4360
4468
  { name: "\u2190 Back", value: "back" }
@@ -4377,7 +4485,7 @@ async function tenantsMenu(proj2, opts) {
4377
4485
  }]);
4378
4486
  if (!t) continue;
4379
4487
  await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
4380
- console.log(import_chalk28.default.green(` Detached ${t.tenant_name ?? t.name}.`));
4488
+ console.log(import_chalk29.default.green(` Detached ${t.tenant_name ?? t.name}.`));
4381
4489
  }
4382
4490
  }
4383
4491
  }
@@ -4420,7 +4528,7 @@ async function specMenu(proj2, opts) {
4420
4528
  choices: [
4421
4529
  { name: "Print the stored spec", value: "get" },
4422
4530
  { name: "Refresh the spec from its source", value: "refresh" },
4423
- { name: import_chalk28.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
4531
+ { name: import_chalk29.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
4424
4532
  { name: "\u2190 Back", value: "back" }
4425
4533
  ]
4426
4534
  }]);
@@ -4428,7 +4536,7 @@ async function specMenu(proj2, opts) {
4428
4536
  if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
4429
4537
  else if (act === "refresh") {
4430
4538
  await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
4431
- console.log(import_chalk28.default.green(" Spec refresh triggered."));
4539
+ console.log(import_chalk29.default.green(" Spec refresh triggered."));
4432
4540
  } else await runOpenapi(proj2.projectName, proj2.apiVersion);
4433
4541
  }
4434
4542
  async function agentsMenu(proj2, opts) {
@@ -4453,8 +4561,8 @@ async function agentsMenu(proj2, opts) {
4453
4561
  }
4454
4562
 
4455
4563
  // src/commands/key.ts
4456
- var import_chalk29 = __toESM(require("chalk"));
4457
- var import_ora14 = __toESM(require("ora"));
4564
+ var import_chalk30 = __toESM(require("chalk"));
4565
+ var import_ora15 = __toESM(require("ora"));
4458
4566
  init_admin();
4459
4567
  async function runApikeysMenu(opts) {
4460
4568
  await runKeyList(opts);
@@ -4482,11 +4590,11 @@ async function runKeyList(opts) {
4482
4590
  return;
4483
4591
  }
4484
4592
  if (!keys.length) {
4485
- console.log(import_chalk29.default.yellow("No developer keys."));
4593
+ console.log(import_chalk30.default.yellow("No developer keys."));
4486
4594
  return;
4487
4595
  }
4488
4596
  for (const k of keys) {
4489
- console.log(` ${import_chalk29.default.bold(k.key_id ?? k.id)} ${import_chalk29.default.dim(k.description ?? "")} ${import_chalk29.default.dim(k.expires_at ?? "no expiry")}`);
4597
+ console.log(` ${import_chalk30.default.bold(k.key_id ?? k.id)} ${import_chalk30.default.dim(k.description ?? "")} ${import_chalk30.default.dim(k.expires_at ?? "no expiry")}`);
4490
4598
  }
4491
4599
  }
4492
4600
  async function runKeyMint(opts) {
@@ -4494,7 +4602,7 @@ async function runKeyMint(opts) {
4494
4602
  const body = { role: "consumer-admin" };
4495
4603
  if (opts.desc) body.description = opts.desc;
4496
4604
  if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
4497
- const spinner = (0, import_ora14.default)("Minting key...").start();
4605
+ const spinner = (0, import_ora15.default)("Minting key...").start();
4498
4606
  try {
4499
4607
  const out = await admin({
4500
4608
  method: "POST",
@@ -4507,9 +4615,9 @@ async function runKeyMint(opts) {
4507
4615
  console.log(JSON.stringify(out));
4508
4616
  return;
4509
4617
  }
4510
- console.log(` ${import_chalk29.default.bold("key_id")}: ${out?.key_id}`);
4511
- console.log(` ${import_chalk29.default.bold("key")}: ${import_chalk29.default.green(out?.key)} ${import_chalk29.default.dim("(shown once \u2014 store it now)")}`);
4512
- if (out?.expires_at) console.log(` ${import_chalk29.default.dim("expires:")} ${out.expires_at}`);
4618
+ console.log(` ${import_chalk30.default.bold("key_id")}: ${out?.key_id}`);
4619
+ console.log(` ${import_chalk30.default.bold("key")}: ${import_chalk30.default.green(out?.key)} ${import_chalk30.default.dim("(shown once \u2014 store it now)")}`);
4620
+ if (out?.expires_at) console.log(` ${import_chalk30.default.dim("expires:")} ${out.expires_at}`);
4513
4621
  } catch (err) {
4514
4622
  spinner.fail("Mint failed.");
4515
4623
  throw err;
@@ -4517,7 +4625,7 @@ async function runKeyMint(opts) {
4517
4625
  }
4518
4626
  async function runKeyRevoke(keyId, opts) {
4519
4627
  const { teamId } = await resolveTeam(opts.team);
4520
- const spinner = (0, import_ora14.default)("Revoking key...").start();
4628
+ const spinner = (0, import_ora15.default)("Revoking key...").start();
4521
4629
  try {
4522
4630
  await admin({
4523
4631
  method: "DELETE",
@@ -4532,8 +4640,8 @@ async function runKeyRevoke(keyId, opts) {
4532
4640
  }
4533
4641
 
4534
4642
  // src/commands/consumer.ts
4535
- var import_chalk30 = __toESM(require("chalk"));
4536
- var import_ora15 = __toESM(require("ora"));
4643
+ var import_chalk31 = __toESM(require("chalk"));
4644
+ var import_ora16 = __toESM(require("ora"));
4537
4645
  init_admin();
4538
4646
  var DEFAULT_SCOPE = "openid email profile offline_access";
4539
4647
  var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
@@ -4553,7 +4661,7 @@ async function consumerFetch(creds, suffix, init) {
4553
4661
  function requireConsumer() {
4554
4662
  const c = loadConsumer();
4555
4663
  if (!c) {
4556
- console.error(import_chalk30.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
4664
+ console.error(import_chalk31.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
4557
4665
  process.exit(1);
4558
4666
  }
4559
4667
  return c;
@@ -4564,7 +4672,7 @@ async function runConsumerLogin(opts) {
4564
4672
  let clientId = opts.client;
4565
4673
  if (clientId) {
4566
4674
  if (!tenant2) {
4567
- console.error(import_chalk30.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
4675
+ console.error(import_chalk31.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
4568
4676
  process.exit(1);
4569
4677
  }
4570
4678
  } else {
@@ -4576,25 +4684,25 @@ async function runConsumerLogin(opts) {
4576
4684
  if (!picked) process.exit(1);
4577
4685
  tenant2 = picked;
4578
4686
  }
4579
- const s2 = (0, import_ora15.default)("Finding the login app...").start();
4687
+ const s2 = (0, import_ora16.default)("Finding the login app...").start();
4580
4688
  const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
4581
4689
  s2.stop();
4582
4690
  const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
4583
4691
  const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
4584
4692
  if (!pick2) {
4585
- console.error(import_chalk30.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
4693
+ console.error(import_chalk31.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
4586
4694
  process.exit(1);
4587
4695
  }
4588
4696
  clientId = pick2.client_id ?? pick2.clientId;
4589
4697
  }
4590
4698
  const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
4591
- console.log(`${import_chalk30.default.cyan("\u2192")} Logging in to ${import_chalk30.default.bold(tenant2)} as a consumer...`);
4699
+ console.log(`${import_chalk31.default.cyan("\u2192")} Logging in to ${import_chalk31.default.bold(tenant2)} as a consumer...`);
4592
4700
  const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
4593
4701
  console.log(`
4594
- Open: ${import_chalk30.default.underline(verificationUri)}`);
4595
- console.log(` Code: ${import_chalk30.default.bold(userCode)}
4702
+ Open: ${import_chalk31.default.underline(verificationUri)}`);
4703
+ console.log(` Code: ${import_chalk31.default.bold(userCode)}
4596
4704
  `);
4597
- console.log(import_chalk30.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
4705
+ console.log(import_chalk31.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
4598
4706
  }, portalResource);
4599
4707
  const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
4600
4708
  const creds = {
@@ -4609,7 +4717,7 @@ async function runConsumerLogin(opts) {
4609
4717
  obtainedAt: Date.now()
4610
4718
  };
4611
4719
  saveConsumer(creds);
4612
- console.log(import_chalk30.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
4720
+ console.log(import_chalk31.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
4613
4721
  }
4614
4722
  async function runConsumerTokens(opts) {
4615
4723
  const creds = requireConsumer();
@@ -4622,29 +4730,29 @@ async function runConsumerTokens(opts) {
4622
4730
  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));
4623
4731
  return;
4624
4732
  }
4625
- console.log(`${import_chalk30.default.cyan("Consumer")} ${import_chalk30.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk30.default.bold(fresh.tenant)}
4733
+ console.log(`${import_chalk31.default.cyan("Consumer")} ${import_chalk31.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk31.default.bold(fresh.tenant)}
4626
4734
  `);
4627
- console.log(`${import_chalk30.default.bold("access_token")} ${import_chalk30.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
4735
+ console.log(`${import_chalk31.default.bold("access_token")} ${import_chalk31.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
4628
4736
  ${fresh.accessToken}
4629
4737
  `);
4630
- if (fresh.idToken) console.log(`${import_chalk30.default.bold("id_token")} ${import_chalk30.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
4738
+ if (fresh.idToken) console.log(`${import_chalk31.default.bold("id_token")} ${import_chalk31.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
4631
4739
  ${fresh.idToken}
4632
4740
  `);
4633
- if (fresh.refreshToken) console.log(`${import_chalk30.default.bold("refresh_token")}
4741
+ if (fresh.refreshToken) console.log(`${import_chalk31.default.bold("refresh_token")}
4634
4742
  ${fresh.refreshToken}
4635
4743
  `);
4636
- console.log(import_chalk30.default.dim("These are your own tokens \u2014 keep them secret."));
4744
+ console.log(import_chalk31.default.dim("These are your own tokens \u2014 keep them secret."));
4637
4745
  }
4638
4746
  async function runConsumerApikeys(opts) {
4639
4747
  const creds = requireConsumer();
4640
4748
  const { default: inquirer2 } = await import("inquirer");
4641
- const spinner = (0, import_ora15.default)("Loading your API keys...").start();
4749
+ const spinner = (0, import_ora16.default)("Loading your API keys...").start();
4642
4750
  const list = await consumerFetch(creds, "/apikeys");
4643
4751
  const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
4644
4752
  spinner.stop();
4645
4753
  if (list.status >= 400) {
4646
- console.error(import_chalk30.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
4647
- if (list.status === 401) console.error(import_chalk30.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
4754
+ console.error(import_chalk31.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
4755
+ if (list.status === 401) console.error(import_chalk31.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
4648
4756
  process.exit(1);
4649
4757
  }
4650
4758
  const keys = list.data?.keys ?? [];
@@ -4652,16 +4760,16 @@ async function runConsumerApikeys(opts) {
4652
4760
  if (opts.json) {
4653
4761
  console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
4654
4762
  } else if (!keys.length) {
4655
- console.log(import_chalk30.default.yellow("No API keys yet."));
4763
+ console.log(import_chalk31.default.yellow("No API keys yet."));
4656
4764
  } else {
4657
4765
  for (const k of keys) {
4658
4766
  const clear = revealMap[k.environment]?.key;
4659
- const shown = clear ? import_chalk30.default.green(clear) : import_chalk30.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
4660
- const exp = k.expires_at ? import_chalk30.default.dim(`exp ${k.expires_at}`) : import_chalk30.default.dim("no expiry");
4661
- console.log(` ${import_chalk30.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk30.default.dim(k.description ?? "")}`);
4767
+ const shown = clear ? import_chalk31.default.green(clear) : import_chalk31.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
4768
+ const exp = k.expires_at ? import_chalk31.default.dim(`exp ${k.expires_at}`) : import_chalk31.default.dim("no expiry");
4769
+ console.log(` ${import_chalk31.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk31.default.dim(k.description ?? "")}`);
4662
4770
  }
4663
4771
  if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
4664
- console.log(import_chalk30.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
4772
+ console.log(import_chalk31.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
4665
4773
  }
4666
4774
  }
4667
4775
  if (opts.json) return;
@@ -4675,7 +4783,7 @@ async function runConsumerApikeys(opts) {
4675
4783
  const body = { environment: answers.environment };
4676
4784
  if (answers.description) body.description = answers.description;
4677
4785
  if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
4678
- const s2 = (0, import_ora15.default)("Creating key...").start();
4786
+ const s2 = (0, import_ora16.default)("Creating key...").start();
4679
4787
  const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
4680
4788
  if (created.status >= 400) {
4681
4789
  s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
@@ -4683,13 +4791,13 @@ async function runConsumerApikeys(opts) {
4683
4791
  }
4684
4792
  s2.succeed("Key created.");
4685
4793
  const key = created.data?.key ?? created.data?.fullKey;
4686
- if (key) console.log(` ${import_chalk30.default.green(key)} ${import_chalk30.default.dim("(shown once \u2014 store it now)")}`);
4687
- else console.log(import_chalk30.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
4794
+ if (key) console.log(` ${import_chalk31.default.green(key)} ${import_chalk31.default.dim("(shown once \u2014 store it now)")}`);
4795
+ else console.log(import_chalk31.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
4688
4796
  }
4689
4797
 
4690
4798
  // src/commands/sidecar.ts
4691
- var import_chalk31 = __toESM(require("chalk"));
4692
- var import_ora16 = __toESM(require("ora"));
4799
+ var import_chalk32 = __toESM(require("chalk"));
4800
+ var import_ora17 = __toESM(require("ora"));
4693
4801
  var fs7 = __toESM(require("fs"));
4694
4802
  var path4 = __toESM(require("path"));
4695
4803
  init_admin();
@@ -4730,18 +4838,18 @@ function upsertEnvLocal(root, token) {
4730
4838
  }
4731
4839
  function installSidecarPackage(root) {
4732
4840
  if (fs7.existsSync(path4.join(root, "node_modules", "apiblaze", "package.json"))) {
4733
- console.log(` ${import_chalk31.default.green("\u2713")} apiblaze package already installed`);
4841
+ console.log(` ${import_chalk32.default.green("\u2713")} apiblaze package already installed`);
4734
4842
  return;
4735
4843
  }
4736
4844
  const has = (f) => fs7.existsSync(path4.join(root, f));
4737
4845
  const pm = has("bun.lockb") || has("bun.lock") ? { cmd: "bun", add: "add" } : has("pnpm-lock.yaml") ? { cmd: "pnpm", add: "add" } : has("yarn.lock") ? { cmd: "yarn", add: "add" } : { cmd: "npm", add: "install" };
4738
- const spinner = (0, import_ora16.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
4846
+ const spinner = (0, import_ora17.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
4739
4847
  try {
4740
4848
  const { execSync } = require("child_process");
4741
4849
  execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
4742
4850
  spinner.succeed("Installed apiblaze (the sidecar runtime).");
4743
4851
  } catch {
4744
- spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk31.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk31.default.cyan("npm run dev")}.`);
4852
+ spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk32.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk32.default.cyan("npm run dev")}.`);
4745
4853
  }
4746
4854
  }
4747
4855
  function readEnvKey(root) {
@@ -4880,7 +4988,7 @@ async function runAnonymousInit(root, router, opts) {
4880
4988
  const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
4881
4989
  const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
4882
4990
  if (opts.newSession) clearAnonCred2();
4883
- const spinner = (0, import_ora16.default)("Setting up a sidecar (no login needed)...").start();
4991
+ const spinner = (0, import_ora17.default)("Setting up a sidecar (no login needed)...").start();
4884
4992
  let out;
4885
4993
  try {
4886
4994
  out = await sidecarInitAnonymous2();
@@ -4892,29 +5000,29 @@ async function runAnonymousInit(root, router, opts) {
4892
5000
  if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
4893
5001
  const envState = upsertEnvLocal(root, out.token);
4894
5002
  ensureGitignored(root);
4895
- console.log(` ${import_chalk31.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
4896
- console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
5003
+ console.log(` ${import_chalk32.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
5004
+ console.log(` ${import_chalk32.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
4897
5005
  installSidecarPackage(root);
4898
5006
  let inspectorPath = null;
4899
5007
  if (!opts.noInspector) {
4900
5008
  inspectorPath = generateInspector(root, router);
4901
- if (inspectorPath) console.log(` ${import_chalk31.default.green("\u2713")} inspector at ${inspectorPath}`);
5009
+ if (inspectorPath) console.log(` ${import_chalk32.default.green("\u2713")} inspector at ${inspectorPath}`);
4902
5010
  }
4903
5011
  console.log("");
4904
- console.log(import_chalk31.default.bold("Done (no account needed). What happens next:"));
4905
- console.log(` 1. ${import_chalk31.default.cyan("npm run dev")} and use your app.`);
5012
+ console.log(import_chalk32.default.bold("Done (no account needed). What happens next:"));
5013
+ console.log(` 1. ${import_chalk32.default.cyan("npm run dev")} and use your app.`);
4906
5014
  console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
4907
- console.log(` ${import_chalk31.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
5015
+ console.log(` ${import_chalk32.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
4908
5016
  console.log("");
4909
- console.log(import_chalk31.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
4910
- console.log(` ${import_chalk31.default.cyan("apiblaze login")} then ${import_chalk31.default.cyan("apiblaze claim")} ${import_chalk31.default.dim("(no code needed here)")}`);
4911
- console.log(import_chalk31.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
5017
+ console.log(import_chalk32.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
5018
+ console.log(` ${import_chalk32.default.cyan("apiblaze login")} then ${import_chalk32.default.cyan("apiblaze claim")} ${import_chalk32.default.dim("(no code needed here)")}`);
5019
+ console.log(import_chalk32.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
4912
5020
  }
4913
5021
  async function runSidecar(opts) {
4914
5022
  const root = path4.resolve(opts.dir ?? process.cwd());
4915
5023
  const detected = detectNextProject(root);
4916
5024
  if (!detected.found) {
4917
- console.log(import_chalk31.default.yellow(`No Next.js project detected in ${root}.`));
5025
+ console.log(import_chalk32.default.yellow(`No Next.js project detected in ${root}.`));
4918
5026
  console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
4919
5027
  return;
4920
5028
  }
@@ -4925,10 +5033,10 @@ async function runSidecar(opts) {
4925
5033
  if (!loadCredentials()) {
4926
5034
  upsertEnvLocal(root, readEnvKey(root));
4927
5035
  ensureGitignored(root);
4928
- console.log(` ${import_chalk31.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
4929
- console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
5036
+ console.log(` ${import_chalk32.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
5037
+ console.log(` ${import_chalk32.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
4930
5038
  installSidecarPackage(root);
4931
- console.log(import_chalk31.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
5039
+ console.log(import_chalk32.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
4932
5040
  return;
4933
5041
  }
4934
5042
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -4937,7 +5045,7 @@ async function runSidecar(opts) {
4937
5045
  const mustMint = !existingKey || opts.rotate || switchingTeam;
4938
5046
  let token = existingKey ?? "";
4939
5047
  if (mustMint) {
4940
- const spinner = (0, import_ora16.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
5048
+ const spinner = (0, import_ora17.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
4941
5049
  try {
4942
5050
  const out = await admin({
4943
5051
  method: "POST",
@@ -4951,39 +5059,39 @@ async function runSidecar(opts) {
4951
5059
  throw err;
4952
5060
  }
4953
5061
  } else {
4954
- console.log(import_chalk31.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
5062
+ console.log(import_chalk32.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
4955
5063
  }
4956
5064
  const envState = upsertEnvLocal(root, token);
4957
5065
  ensureGitignored(root);
4958
- console.log(` ${import_chalk31.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
5066
+ console.log(` ${import_chalk32.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
4959
5067
  const wireState = wireInstrumentation(root);
4960
- console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireState}`);
5068
+ console.log(` ${import_chalk32.default.green("\u2713")} instrumentation.ts ${wireState}`);
4961
5069
  installSidecarPackage(root);
4962
5070
  let inspectorPath = null;
4963
5071
  if (!opts.noInspector) {
4964
5072
  inspectorPath = generateInspector(root, detected.router);
4965
- if (inspectorPath) console.log(` ${import_chalk31.default.green("\u2713")} inspector at ${inspectorPath}`);
5073
+ if (inspectorPath) console.log(` ${import_chalk32.default.green("\u2713")} inspector at ${inspectorPath}`);
4966
5074
  }
4967
5075
  console.log("");
4968
- console.log(import_chalk31.default.bold("Done. What happens next:"));
4969
- console.log(` 1. ${import_chalk31.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
4970
- console.log(` 2. The origins your app calls appear as ${import_chalk31.default.bold("candidates")} \u2014 list them: ${import_chalk31.default.cyan("apiblaze sidecar")}`);
4971
- console.log(` 3. Approve the ones to route: ${import_chalk31.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
5076
+ console.log(import_chalk32.default.bold("Done. What happens next:"));
5077
+ console.log(` 1. ${import_chalk32.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
5078
+ console.log(` 2. The origins your app calls appear as ${import_chalk32.default.bold("candidates")} \u2014 list them: ${import_chalk32.default.cyan("apiblaze sidecar")}`);
5079
+ console.log(` 3. Approve the ones to route: ${import_chalk32.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
4972
5080
  console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
4973
- if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk31.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path4.dirname(inspectorPath)} before shipping)`);
4974
- if (switchingTeam) console.log(import_chalk31.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
5081
+ if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk32.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path4.dirname(inspectorPath)} before shipping)`);
5082
+ if (switchingTeam) console.log(import_chalk32.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
4975
5083
  console.log("");
4976
- console.log(import_chalk31.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
4977
- console.log(import_chalk31.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
4978
- console.log(import_chalk31.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
5084
+ console.log(import_chalk32.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
5085
+ console.log(import_chalk32.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
5086
+ console.log(import_chalk32.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
4979
5087
  console.log("");
4980
- console.log(import_chalk31.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
4981
- console.log(import_chalk31.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
5088
+ console.log(import_chalk32.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
5089
+ console.log(import_chalk32.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
4982
5090
  }
4983
5091
 
4984
5092
  // src/commands/origins.ts
4985
- var import_chalk32 = __toESM(require("chalk"));
4986
- var import_ora17 = __toESM(require("ora"));
5093
+ var import_chalk33 = __toESM(require("chalk"));
5094
+ var import_ora18 = __toESM(require("ora"));
4987
5095
  init_admin();
4988
5096
  init_auth();
4989
5097
  init_anon_cred();
@@ -4992,7 +5100,7 @@ async function runOriginsList(opts) {
4992
5100
  if (!loadCredentials()) {
4993
5101
  const cred = loadAnonCred();
4994
5102
  if (!cred) {
4995
- console.log(import_chalk32.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
5103
+ console.log(import_chalk33.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
4996
5104
  return;
4997
5105
  }
4998
5106
  out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
@@ -5010,30 +5118,30 @@ async function runOriginsList(opts) {
5010
5118
  }
5011
5119
  const routed = out.routed ?? [];
5012
5120
  const candidates = out.candidates ?? [];
5013
- console.log(import_chalk32.default.bold(`
5121
+ console.log(import_chalk33.default.bold(`
5014
5122
  Routed through APIblaze (${routed.length})`));
5015
- if (!routed.length) console.log(import_chalk32.default.dim(" none yet"));
5016
- for (const r of routed) console.log(` ${import_chalk32.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk32.default.dim(`\u2192 ${r.project_id}`)}`);
5017
- console.log(import_chalk32.default.bold(`
5123
+ if (!routed.length) console.log(import_chalk33.default.dim(" none yet"));
5124
+ for (const r of routed) console.log(` ${import_chalk33.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk33.default.dim(`\u2192 ${r.project_id}`)}`);
5125
+ console.log(import_chalk33.default.bold(`
5018
5126
  Candidates \u2014 going direct, not yet approved (${candidates.length})`));
5019
- if (!candidates.length) console.log(import_chalk32.default.dim(" none \u2014 run your app to discover the origins it calls"));
5127
+ if (!candidates.length) console.log(import_chalk33.default.dim(" none \u2014 run your app to discover the origins it calls"));
5020
5128
  for (const c of candidates) {
5021
- console.log(` ${import_chalk32.default.yellow("\u25CB")} ${c.origin} ${import_chalk32.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
5129
+ console.log(` ${import_chalk33.default.yellow("\u25CB")} ${c.origin} ${import_chalk33.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
5022
5130
  }
5023
5131
  if (candidates.length) {
5024
- console.log(import_chalk32.default.dim(`
5132
+ console.log(import_chalk33.default.dim(`
5025
5133
  Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
5026
- console.log(import_chalk32.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
5134
+ console.log(import_chalk33.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
5027
5135
  }
5028
5136
  }
5029
5137
  async function runOriginsApprove(origin, opts) {
5030
5138
  if (!loadCredentials()) {
5031
5139
  const cred = loadAnonCred();
5032
5140
  if (!cred) {
5033
- console.error(import_chalk32.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
5141
+ console.error(import_chalk33.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
5034
5142
  process.exit(1);
5035
5143
  }
5036
- const spinner2 = (0, import_ora17.default)(`Approving ${origin} (anonymous)...`).start();
5144
+ const spinner2 = (0, import_ora18.default)(`Approving ${origin} (anonymous)...`).start();
5037
5145
  try {
5038
5146
  const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
5039
5147
  spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
@@ -5044,7 +5152,7 @@ async function runOriginsApprove(origin, opts) {
5044
5152
  return;
5045
5153
  }
5046
5154
  const { teamId } = await resolveTeam(opts.team);
5047
- const spinner = (0, import_ora17.default)(`Approving ${origin}...`).start();
5155
+ const spinner = (0, import_ora18.default)(`Approving ${origin}...`).start();
5048
5156
  try {
5049
5157
  const out = await admin({
5050
5158
  method: "POST",
@@ -5061,7 +5169,7 @@ async function runOriginsApprove(origin, opts) {
5061
5169
  }
5062
5170
  async function runOriginsDeny(origin, opts) {
5063
5171
  const { teamId } = await resolveTeam(opts.team);
5064
- const spinner = (0, import_ora17.default)(`Dismissing ${origin}...`).start();
5172
+ const spinner = (0, import_ora18.default)(`Dismissing ${origin}...`).start();
5065
5173
  try {
5066
5174
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
5067
5175
  spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
@@ -5072,7 +5180,7 @@ async function runOriginsDeny(origin, opts) {
5072
5180
  }
5073
5181
  async function runOriginsRemove(origin, opts) {
5074
5182
  const { teamId } = await resolveTeam(opts.team);
5075
- const spinner = (0, import_ora17.default)(`Removing the proxy for ${origin}...`).start();
5183
+ const spinner = (0, import_ora18.default)(`Removing the proxy for ${origin}...`).start();
5076
5184
  try {
5077
5185
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
5078
5186
  spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
@@ -5083,7 +5191,7 @@ async function runOriginsRemove(origin, opts) {
5083
5191
  }
5084
5192
 
5085
5193
  // src/commands/op.ts
5086
- var import_chalk33 = __toESM(require("chalk"));
5194
+ var import_chalk34 = __toESM(require("chalk"));
5087
5195
  init_auth();
5088
5196
  init_trace();
5089
5197
  init_types();
@@ -5116,82 +5224,82 @@ function printResidue(report, applied) {
5116
5224
  const up = report?.upstash ?? {};
5117
5225
  const fga = report?.fga ?? {};
5118
5226
  const ghosts = report?.ghosts ?? {};
5119
- console.log(import_chalk33.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
5120
- console.log(import_chalk33.default.bold("\n Upstash"));
5227
+ console.log(import_chalk34.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
5228
+ console.log(import_chalk34.default.bold("\n Upstash"));
5121
5229
  const orphans = up.orphans ?? [];
5122
- if (orphans.length === 0) console.log(import_chalk33.default.green(" no orphaned keys"));
5123
- for (const o of orphans) console.log(` ${import_chalk33.default.yellow(o.key)} ${import_chalk33.default.dim(`\u2014 ${o.reason}`)}`);
5124
- console.log(import_chalk33.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
5230
+ if (orphans.length === 0) console.log(import_chalk34.default.green(" no orphaned keys"));
5231
+ for (const o of orphans) console.log(` ${import_chalk34.default.yellow(o.key)} ${import_chalk34.default.dim(`\u2014 ${o.reason}`)}`);
5232
+ console.log(import_chalk34.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
5125
5233
  if (up.anon_wallet_detail) {
5126
5234
  const d = up.anon_wallet_detail;
5127
- console.log(import_chalk33.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)"}`));
5235
+ console.log(import_chalk34.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)"}`));
5128
5236
  }
5129
5237
  if (up.keyspace_census) {
5130
5238
  const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
5131
- console.log(import_chalk33.default.dim(` keyspace: ${census}`));
5239
+ console.log(import_chalk34.default.dim(` keyspace: ${census}`));
5132
5240
  }
5133
- if (up.unknown?.length) console.log(import_chalk33.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
5134
- if (applied) console.log(` ${import_chalk33.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
5135
- for (const e of up.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
5136
- console.log(import_chalk33.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
5241
+ if (up.unknown?.length) console.log(import_chalk34.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
5242
+ if (applied) console.log(` ${import_chalk34.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
5243
+ for (const e of up.errors ?? []) console.log(import_chalk34.default.red(` error: ${e}`));
5244
+ console.log(import_chalk34.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
5137
5245
  if (applied) {
5138
5246
  const swept = fga?.swept ?? [];
5139
- if (swept.length === 0) console.log(import_chalk33.default.green(" no orphaned stores"));
5247
+ if (swept.length === 0) console.log(import_chalk34.default.green(" no orphaned stores"));
5140
5248
  for (const s of swept) {
5141
5249
  console.log(
5142
- ` ${import_chalk33.default.yellow(s.store_id)} ${import_chalk33.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
5250
+ ` ${import_chalk34.default.yellow(s.store_id)} ${import_chalk34.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
5143
5251
  );
5144
5252
  }
5145
- if (fga?.remaining) console.log(import_chalk33.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
5253
+ if (fga?.remaining) console.log(import_chalk34.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
5146
5254
  const st = fga?.side_tables;
5147
- if (st) console.log(import_chalk33.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})` : ""}`));
5255
+ if (st) console.log(import_chalk34.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})` : ""}`));
5148
5256
  } else {
5149
5257
  const fgaOrphans = fga?.orphans ?? [];
5150
- if (fgaOrphans.length === 0) console.log(import_chalk33.default.green(" no orphaned stores"));
5258
+ if (fgaOrphans.length === 0) console.log(import_chalk34.default.green(" no orphaned stores"));
5151
5259
  for (const s of fgaOrphans) {
5152
5260
  const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
5153
- console.log(` ${import_chalk33.default.yellow(s.store_id)} ${import_chalk33.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
5261
+ console.log(` ${import_chalk34.default.yellow(s.store_id)} ${import_chalk34.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
5154
5262
  }
5155
- console.log(import_chalk33.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
5263
+ console.log(import_chalk34.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
5156
5264
  const st = fga?.side_tables;
5157
- if (st) console.log(import_chalk33.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`));
5265
+ if (st) console.log(import_chalk34.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`));
5158
5266
  }
5159
- for (const e of fga?.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
5160
- console.log(import_chalk33.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
5267
+ for (const e of fga?.errors ?? []) console.log(import_chalk34.default.red(` error: ${e}`));
5268
+ console.log(import_chalk34.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
5161
5269
  if (applied) {
5162
- if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk33.default.green(" no ghost tuples"));
5163
- else console.log(` ${import_chalk33.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk33.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
5270
+ if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk34.default.green(" no ghost tuples"));
5271
+ else console.log(` ${import_chalk34.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk34.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
5164
5272
  } else {
5165
5273
  const n = ghosts?.ghost_count ?? 0;
5166
- if (n === 0) console.log(import_chalk33.default.green(` no ghost tuples ${import_chalk33.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
5274
+ if (n === 0) console.log(import_chalk34.default.green(` no ghost tuples ${import_chalk34.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
5167
5275
  else {
5168
- console.log(import_chalk33.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
5276
+ console.log(import_chalk34.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
5169
5277
  for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
5170
- console.log(import_chalk33.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
5278
+ console.log(import_chalk34.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
5171
5279
  }
5172
- if (n > 20) console.log(import_chalk33.default.dim(` \u2026 and ${n - 20} more`));
5280
+ if (n > 20) console.log(import_chalk34.default.dim(` \u2026 and ${n - 20} more`));
5173
5281
  }
5174
5282
  }
5175
- for (const e of ghosts?.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
5283
+ for (const e of ghosts?.errors ?? []) console.log(import_chalk34.default.red(` error: ${e}`));
5176
5284
  console.log();
5177
5285
  }
5178
5286
  async function runOp(sub, opts = {}) {
5179
5287
  if (!loadCredentials()) {
5180
- console.log(import_chalk33.default.dim("Not logged in. Run `apiblaze login`."));
5288
+ console.log(import_chalk34.default.dim("Not logged in. Run `apiblaze login`."));
5181
5289
  return;
5182
5290
  }
5183
5291
  if (!isOperatorLogin()) {
5184
- console.log(import_chalk33.default.dim("`apiblaze op` is only available to platform operators."));
5292
+ console.log(import_chalk34.default.dim("`apiblaze op` is only available to platform operators."));
5185
5293
  return;
5186
5294
  }
5187
5295
  switch (sub) {
5188
5296
  case void 0:
5189
5297
  case "menu": {
5190
- console.log(import_chalk33.default.bold("\nOperator menu"));
5191
- console.log(` ${import_chalk33.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
5192
- console.log(` ${import_chalk33.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk33.default.dim("-y to skip")})`);
5193
- console.log(` ${import_chalk33.default.cyan("apiblaze op credits")} list credit wallets`);
5194
- console.log(import_chalk33.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
5298
+ console.log(import_chalk34.default.bold("\nOperator menu"));
5299
+ console.log(` ${import_chalk34.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
5300
+ console.log(` ${import_chalk34.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk34.default.dim("-y to skip")})`);
5301
+ console.log(` ${import_chalk34.default.cyan("apiblaze op credits")} list credit wallets`);
5302
+ console.log(import_chalk34.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
5195
5303
  `));
5196
5304
  return;
5197
5305
  }
@@ -5210,17 +5318,17 @@ async function runOp(sub, opts = {}) {
5210
5318
  const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
5211
5319
  printResidue(report, false);
5212
5320
  if (nUp + nFga + nGhost + nSide === 0) {
5213
- console.log(import_chalk33.default.green("Nothing to sweep."));
5321
+ console.log(import_chalk34.default.green("Nothing to sweep."));
5214
5322
  return;
5215
5323
  }
5216
5324
  if (!opts.yes) {
5217
5325
  const readline2 = await import("readline/promises");
5218
5326
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
5219
5327
  const answer = await rl.question(
5220
- import_chalk33.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: `)
5328
+ import_chalk34.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: `)
5221
5329
  );
5222
5330
  rl.close();
5223
- if (answer.trim() !== "sweep") return void console.log(import_chalk33.default.dim("Aborted."));
5331
+ if (answer.trim() !== "sweep") return void console.log(import_chalk34.default.dim("Aborted."));
5224
5332
  }
5225
5333
  const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
5226
5334
  if (opts.json) return void console.log(JSON.stringify(result, null, 2));
@@ -5231,15 +5339,15 @@ async function runOp(sub, opts = {}) {
5231
5339
  const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
5232
5340
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
5233
5341
  const accounts = data?.accounts ?? [];
5234
- if (accounts.length === 0) return void console.log(import_chalk33.default.dim("No credit wallets."));
5342
+ if (accounts.length === 0) return void console.log(import_chalk34.default.dim("No credit wallets."));
5235
5343
  for (const a of accounts) {
5236
5344
  const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
5237
- console.log(` ${import_chalk33.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk33.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk33.default.dim(" \u2014 anon") : ""}`);
5345
+ console.log(` ${import_chalk34.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk34.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk34.default.dim(" \u2014 anon") : ""}`);
5238
5346
  }
5239
5347
  return;
5240
5348
  }
5241
5349
  default:
5242
- console.log(import_chalk33.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
5350
+ console.log(import_chalk34.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
5243
5351
  }
5244
5352
  }
5245
5353
 
@@ -5296,7 +5404,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
5296
5404
  try {
5297
5405
  const resolved = parseInt(port ?? opts.port, 10);
5298
5406
  if (Number.isNaN(resolved)) {
5299
- console.error(import_chalk34.default.red(`Invalid port: ${port ?? opts.port}`));
5407
+ console.error(import_chalk35.default.red(`Invalid port: ${port ?? opts.port}`));
5300
5408
  process.exit(1);
5301
5409
  }
5302
5410
  await runDev({ port: resolved, captureFile: opts.captureFile });
@@ -5394,7 +5502,7 @@ function groupedCommandHelp() {
5394
5502
  const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
5395
5503
  return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
5396
5504
  }).filter(Boolean).join("\n");
5397
- return `${import_chalk34.default.bold(g.title)}
5505
+ return `${import_chalk35.default.bold(g.title)}
5398
5506
  ${rows}`;
5399
5507
  }).join("\n\n");
5400
5508
  }
@@ -5427,14 +5535,14 @@ async function recoverStaleTeam() {
5427
5535
  const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
5428
5536
  const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
5429
5537
  if (!linked) {
5430
- console.error(import_chalk34.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
5538
+ console.error(import_chalk35.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
5431
5539
  return;
5432
5540
  }
5433
5541
  if (linked.teamId === creds.teamId) return;
5434
5542
  const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
5435
5543
  delete next.activeTenant;
5436
5544
  saveCredentials(next);
5437
- console.error(import_chalk34.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk34.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
5545
+ console.error(import_chalk35.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk35.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
5438
5546
  } catch {
5439
5547
  }
5440
5548
  }
@@ -5442,16 +5550,16 @@ async function printError(err) {
5442
5550
  if (err instanceof ApiError) {
5443
5551
  const data = err.body;
5444
5552
  const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
5445
- console.error(import_chalk34.default.red(`
5553
+ console.error(import_chalk35.default.red(`
5446
5554
  API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
5447
5555
  if (err.status === 403 || err.status === 404) {
5448
5556
  await recoverStaleTeam();
5449
5557
  }
5450
5558
  } else if (err instanceof Error) {
5451
- console.error(import_chalk34.default.red(`
5559
+ console.error(import_chalk35.default.red(`
5452
5560
  Error: ${err.message}`));
5453
5561
  } else {
5454
- console.error(import_chalk34.default.red("\nUnknown error"));
5562
+ console.error(import_chalk35.default.red("\nUnknown error"));
5455
5563
  }
5456
5564
  }
5457
5565
  program.parse(process.argv);