apiblaze 0.18.8 → 0.19.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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +1125 -675
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -393,32 +393,32 @@ function maskBody(body) {
393
393
  }
394
394
  function renderTrace() {
395
395
  if (!verbose || entries.length === 0) return;
396
- console.log(import_chalk12.default.dim("\n" + "\u2500".repeat(64)));
397
- console.log(import_chalk12.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
396
+ console.log(import_chalk6.default.dim("\n" + "\u2500".repeat(64)));
397
+ console.log(import_chalk6.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
398
398
  console.log(
399
- import_chalk12.default.dim("The same thing on the official API \u2014 copy/paste with your control-plane key\n(get one from the Developers section of dashboard.apiblaze.com, then\n`export APIBLAZE_CONTROLPLANE_APIKEY=sk_...`).\nFull API reference: https://api.apiblaze.com/openapi.json\n")
399
+ import_chalk6.default.dim("The same thing on the official API \u2014 copy/paste with your control-plane key\n(get one from the Developers section of dashboard.apiblaze.com, then\n`export APIBLAZE_CONTROLPLANE_APIKEY=sk_...`).\nFull API reference: https://api.apiblaze.com/openapi.json\n")
400
400
  );
401
401
  entries.forEach((e, i) => {
402
- const n = entries.length > 1 ? import_chalk12.default.bold(`${i + 1}. `) : "";
403
- if (e.summary) console.log(`${n}${import_chalk12.default.cyan(e.summary)}${e.status ? import_chalk12.default.dim(` (HTTP ${e.status})`) : ""}`);
402
+ const n = entries.length > 1 ? import_chalk6.default.bold(`${i + 1}. `) : "";
403
+ if (e.summary) console.log(`${n}${import_chalk6.default.cyan(e.summary)}${e.status ? import_chalk6.default.dim(` (HTTP ${e.status})`) : ""}`);
404
404
  const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
405
405
  const masked = maskBody(e.body);
406
406
  const hasBody = e.method !== "GET" && masked !== void 0;
407
- console.log(import_chalk12.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
408
- console.log(import_chalk12.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
407
+ console.log(import_chalk6.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
408
+ console.log(import_chalk6.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
409
409
  if (hasBody) {
410
- console.log(import_chalk12.default.green(" -H 'Content-Type: application/json' \\"));
411
- console.log(import_chalk12.default.green(` -d '${masked}'`));
410
+ console.log(import_chalk6.default.green(" -H 'Content-Type: application/json' \\"));
411
+ console.log(import_chalk6.default.green(` -d '${masked}'`));
412
412
  }
413
413
  if (i < entries.length - 1) console.log();
414
414
  });
415
415
  entries.length = 0;
416
416
  }
417
- var import_chalk12, CONTROL_API_VERSION, verbose, entries, SECRET_KEY;
417
+ var import_chalk6, CONTROL_API_VERSION, verbose, entries, SECRET_KEY;
418
418
  var init_trace = __esm({
419
419
  "src/lib/trace.ts"() {
420
420
  "use strict";
421
- import_chalk12 = __toESM(require("chalk"));
421
+ import_chalk6 = __toESM(require("chalk"));
422
422
  CONTROL_API_VERSION = "1.0.0";
423
423
  verbose = false;
424
424
  entries = [];
@@ -459,13 +459,13 @@ function formatBilling(billing) {
459
459
  }
460
460
  function maybePrintBilling(data) {
461
461
  const line = formatBilling(data?.billing);
462
- if (line) console.log(import_chalk13.default.magenta(` ${line}`));
462
+ if (line) console.log(import_chalk7.default.magenta(` ${line}`));
463
463
  }
464
- var import_chalk13, DASHBOARD_BASE3;
464
+ var import_chalk7, DASHBOARD_BASE3;
465
465
  var init_admin = __esm({
466
466
  "src/lib/admin.ts"() {
467
467
  "use strict";
468
- import_chalk13 = __toESM(require("chalk"));
468
+ import_chalk7 = __toESM(require("chalk"));
469
469
  init_auth();
470
470
  init_trace();
471
471
  init_types();
@@ -473,6 +473,233 @@ var init_admin = __esm({
473
473
  }
474
474
  });
475
475
 
476
+ // src/lib/resolve.ts
477
+ function requireAuth() {
478
+ const creds = loadCredentials();
479
+ if (!creds) {
480
+ console.error(import_chalk8.default.red("Not logged in. Run `apiblaze login` first."));
481
+ process.exit(1);
482
+ }
483
+ return creds;
484
+ }
485
+ async function resolveTeam(opt) {
486
+ const creds = requireAuth();
487
+ if (!opt) {
488
+ if (!creds.teamId) {
489
+ console.error(import_chalk8.default.red("No active team. Run `apiblaze login` or pass --team."));
490
+ process.exit(1);
491
+ }
492
+ return { teamId: creds.teamId, teamName: creds.teamName };
493
+ }
494
+ if (opt.startsWith("team_")) return { teamId: opt };
495
+ const teams = await getTeams().catch(() => []);
496
+ const match = teams.find((t) => t.name === opt || t.teamId === opt);
497
+ if (!match) {
498
+ console.error(import_chalk8.default.red(`Team "${opt}" not found.${teams.length ? " Available: " + teams.map((t) => t.name).join(", ") : ""}`));
499
+ process.exit(1);
500
+ }
501
+ return { teamId: match.teamId, teamName: match.name };
502
+ }
503
+ async function resolveProject(teamId, nameOrId, version2) {
504
+ const projects = await getProjects(teamId).catch(() => []);
505
+ const candidates = projects.filter(
506
+ (p) => p.projectId === nameOrId || p.projectName === nameOrId
507
+ );
508
+ if (candidates.length === 0) {
509
+ console.error(import_chalk8.default.red(`Project "${nameOrId}" not found in this team.`));
510
+ if (projects.length) console.error(import_chalk8.default.dim(" Known: " + projects.map((p) => p.projectName).join(", ")));
511
+ process.exit(1);
512
+ }
513
+ const chosen = version2 ? candidates.find((p) => p.apiVersion === version2) : candidates[0];
514
+ if (!chosen) {
515
+ console.error(import_chalk8.default.red(`Project "${nameOrId}" has no version ${version2}. Versions: ${candidates.map((p) => p.apiVersion).join(", ")}`));
516
+ process.exit(1);
517
+ }
518
+ return {
519
+ projectId: chosen.projectId,
520
+ projectName: chosen.projectName,
521
+ apiVersion: chosen.apiVersion,
522
+ teamId,
523
+ tenant: chosen.tenant
524
+ };
525
+ }
526
+ var import_chalk8;
527
+ var init_resolve = __esm({
528
+ "src/lib/resolve.ts"() {
529
+ "use strict";
530
+ import_chalk8 = __toESM(require("chalk"));
531
+ init_auth();
532
+ init_api();
533
+ }
534
+ });
535
+
536
+ // src/lib/caller.ts
537
+ function resolveCaller() {
538
+ const creds = loadCredentials();
539
+ if (creds && Date.now() < creds.expiresAt) return { mode: "session" };
540
+ const anon = loadAnonCred();
541
+ if (anon?.cp_key) return { mode: "anon", cpKey: anon.cp_key, teamId: anon.team_id };
542
+ return null;
543
+ }
544
+ function requireCaller() {
545
+ const caller = resolveCaller();
546
+ if (!caller) {
547
+ throw new Error(
548
+ "No credential. Run `apiblaze login`, or `apiblaze create` first (an anonymous workspace works too \u2014 its key drives these commands until you claim it)."
549
+ );
550
+ }
551
+ return caller;
552
+ }
553
+ async function resolveActingTeam(caller, optTeam) {
554
+ if (caller.mode === "anon") {
555
+ if (optTeam && optTeam !== caller.teamId) {
556
+ throw new Error(
557
+ `--team is not available in an anonymous workspace (your team is ${caller.teamId}). Run \`apiblaze login\` to act on other teams.`
558
+ );
559
+ }
560
+ return { teamId: caller.teamId };
561
+ }
562
+ return resolveTeam(optTeam);
563
+ }
564
+ async function producer(caller, call) {
565
+ if (caller.mode === "session") return admin(call);
566
+ const out = await cpFetch(caller.cpKey, call.path, {
567
+ method: call.method,
568
+ ...call.body !== void 0 ? { body: JSON.stringify(call.body) } : {}
569
+ });
570
+ recordCall({ method: call.method, path: call.path, body: call.body, status: 200, summary: call.summary });
571
+ return out;
572
+ }
573
+ async function resolveProjectVia(caller, teamId, nameOrId, version2) {
574
+ const out = await producer(caller, {
575
+ method: "GET",
576
+ path: `/projects?team_id=${encodeURIComponent(teamId)}`,
577
+ summary: `List projects for ${teamId}`
578
+ });
579
+ const rows = out?.projects ?? [];
580
+ const candidates = rows.filter((r) => r.project_id === nameOrId || r.project_name === nameOrId);
581
+ if (!candidates.length) {
582
+ throw new Error(
583
+ `Project "${nameOrId}" not found in this team.` + (rows.length ? ` Known: ${rows.map((r) => r.project_name).join(", ")}` : "")
584
+ );
585
+ }
586
+ const chosen = version2 ? candidates.find((r) => r.api_version === version2) : candidates[0];
587
+ if (!chosen) {
588
+ throw new Error(
589
+ `Project "${nameOrId}" has no version ${version2}. Versions: ${candidates.map((r) => r.api_version).join(", ")}`
590
+ );
591
+ }
592
+ return {
593
+ projectId: chosen.project_id,
594
+ projectName: chosen.project_name,
595
+ apiVersion: chosen.api_version,
596
+ teamId,
597
+ tenant: chosen.tenant ?? chosen.config?.tenant
598
+ };
599
+ }
600
+ var init_caller = __esm({
601
+ "src/lib/caller.ts"() {
602
+ "use strict";
603
+ init_auth();
604
+ init_anon_cred();
605
+ init_admin();
606
+ init_trace();
607
+ init_resolve();
608
+ }
609
+ });
610
+
611
+ // src/commands/iam.ts
612
+ var iam_exports = {};
613
+ __export(iam_exports, {
614
+ runIamToggle: () => runIamToggle,
615
+ runIdentifiedToggle: () => runIdentifiedToggle
616
+ });
617
+ async function runIamToggle(project, state, opts) {
618
+ if (state !== "on" && state !== "off") {
619
+ throw new Error(`Invalid state "${state}". Use: apiblaze iam <project> on|off`);
620
+ }
621
+ const enabled = state === "on";
622
+ const caller = requireCaller();
623
+ const { teamId } = await resolveActingTeam(caller, opts.team);
624
+ const proj2 = await resolveProjectVia(caller, teamId, project, opts.apiversion);
625
+ const tenant2 = opts.tenant ?? proj2.tenant;
626
+ if (!tenant2) {
627
+ throw new Error("Could not resolve the tenant for this project. Pass --tenant <name>.");
628
+ }
629
+ const spinner = (0, import_ora4.default)(`Turning IAM ${state} for tenant ${tenant2}...`).start();
630
+ try {
631
+ const out = await producer(caller, {
632
+ method: "PATCH",
633
+ path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`,
634
+ body: { iam_enabled: enabled },
635
+ summary: `IAM ${state} for tenant ${tenant2}`
636
+ });
637
+ spinner.succeed(`IAM is ${import_chalk9.default.bold(state)} for tenant ${tenant2}.`);
638
+ if (opts.json) console.log(JSON.stringify(out ?? { iam_enabled: enabled }));
639
+ else if (enabled) {
640
+ console.log(import_chalk9.default.dim(" Groups now apply to identified calls. Identify callers with"));
641
+ console.log(import_chalk9.default.dim(" X-End-User-Id, a login token, or per-user keys \u2014 and consider"));
642
+ console.log(import_chalk9.default.dim(` \`apiblaze identified ${project} require\` to reject unattributed traffic.`));
643
+ }
644
+ } catch (err) {
645
+ spinner.fail(`IAM ${state} failed.`);
646
+ throw err;
647
+ }
648
+ }
649
+ async function runIdentifiedToggle(project, mode, opts) {
650
+ if (mode !== "require" && mode !== "allow-anon") {
651
+ throw new Error(`Invalid mode "${mode}". Use: apiblaze identified <project> require|allow-anon`);
652
+ }
653
+ const require2 = mode === "require";
654
+ const caller = requireCaller();
655
+ const { teamId } = await resolveActingTeam(caller, opts.team);
656
+ const proj2 = await resolveProjectVia(caller, teamId, project, opts.apiversion);
657
+ const spinner = (0, import_ora4.default)(
658
+ require2 ? "Requiring identified traffic..." : "Allowing unattributed traffic..."
659
+ ).start();
660
+ try {
661
+ const listing = await producer(caller, {
662
+ method: "GET",
663
+ path: `/projects?team_id=${encodeURIComponent(teamId)}`,
664
+ summary: `Read settings for ${proj2.projectName}`
665
+ });
666
+ const row = (listing?.projects ?? []).find(
667
+ (r) => r.project_id === proj2.projectId && r.api_version === proj2.apiVersion
668
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
669
+ ) ?? (listing?.projects ?? []).find((r) => r.project_id === proj2.projectId);
670
+ const current = row?.config?.requests_policy ?? {};
671
+ const merged = {
672
+ mode: current.mode ?? "passthrough",
673
+ methods: current.methods?.length ? current.methods : ["api_key"],
674
+ ...current,
675
+ identified_traffic_only: require2,
676
+ require_x_end_user_id: require2
677
+ };
678
+ const out = await producer(caller, {
679
+ method: "PATCH",
680
+ path: `/projects/${proj2.projectId}/${proj2.apiVersion}`,
681
+ body: { requests_policy: merged },
682
+ summary: `Set identified-traffic policy (${mode}) on ${proj2.projectName}`
683
+ });
684
+ spinner.succeed(
685
+ require2 ? `${proj2.projectName} now rejects calls that don't identify an end user.` : `${proj2.projectName} accepts unattributed calls again (they pass with no groups).`
686
+ );
687
+ if (opts.json) console.log(JSON.stringify(out ?? { identified_traffic_only: require2 }));
688
+ } catch (err) {
689
+ spinner.fail("Policy update failed.");
690
+ throw err;
691
+ }
692
+ }
693
+ var import_chalk9, import_ora4;
694
+ var init_iam = __esm({
695
+ "src/commands/iam.ts"() {
696
+ "use strict";
697
+ import_chalk9 = __toESM(require("chalk"));
698
+ import_ora4 = __toESM(require("ora"));
699
+ init_caller();
700
+ }
701
+ });
702
+
476
703
  // src/lib/tenant-create.ts
477
704
  var tenant_create_exports = {};
478
705
  __export(tenant_create_exports, {
@@ -485,7 +712,7 @@ async function createTenantRow(teamId) {
485
712
  const { name } = await inquirer3.prompt([{
486
713
  type: "input",
487
714
  name: "name",
488
- message: `Tenant name ${import_chalk25.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
715
+ message: `Tenant name ${import_chalk26.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
489
716
  validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
490
717
  }]);
491
718
  const slug = name.trim();
@@ -500,7 +727,7 @@ async function createTenantRow(teamId) {
500
727
  } catch (err) {
501
728
  const msg = err instanceof Error ? err.message : String(err);
502
729
  if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
503
- console.log(import_chalk25.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
730
+ console.log(import_chalk26.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
504
731
  continue;
505
732
  }
506
733
  throw err;
@@ -517,11 +744,11 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
517
744
  pageSize: 10,
518
745
  choices: [
519
746
  ...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
520
- ...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk25.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
747
+ ...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk26.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
521
748
  ]
522
749
  }]);
523
750
  if (!provider) {
524
- console.log(import_chalk25.default.dim(" No login method yet \u2014 add one later under Login methods."));
751
+ console.log(import_chalk26.default.dim(" No login method yet \u2014 add one later under Login methods."));
525
752
  return false;
526
753
  }
527
754
  const opt = PROVIDERS.find((p) => p.id === provider);
@@ -552,7 +779,7 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
552
779
  if (a.callbacks.trim()) clientBody.authorizedCallbackUrls = a.callbacks.split(/\s*,\s*/).map((s) => s.trim()).filter(Boolean);
553
780
  if (a.scopes.trim() && opt.own) providerBody.scopes = a.scopes.split(/\s+/).filter(Boolean);
554
781
  }
555
- const spinner = (0, import_ora9.default)("Setting up login...").start();
782
+ const spinner = (0, import_ora10.default)("Setting up login...").start();
556
783
  try {
557
784
  const client = await admin({ method: "POST", path: `${base}/app-clients`, body: clientBody, summary: `Create login for ${tenant2}` });
558
785
  const clientId = client?.clientId ?? client?.client_id;
@@ -567,27 +794,27 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
567
794
  async function createTenantInteractive(teamId) {
568
795
  const { default: inquirer3 } = await import("inquirer");
569
796
  const tenant2 = await createTenantRow(teamId);
570
- console.log(import_chalk25.default.green(` Tenant ${import_chalk25.default.bold(tenant2)} created.`));
797
+ console.log(import_chalk26.default.green(` Tenant ${import_chalk26.default.bold(tenant2)} created.`));
571
798
  const { users } = await inquirer3.prompt([{
572
799
  type: "confirm",
573
800
  name: "users",
574
801
  default: false,
575
- message: `Enable Users & groups? ${import_chalk25.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
802
+ message: `Enable Users & groups? ${import_chalk26.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
576
803
  }]);
577
804
  if (users) {
578
805
  await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
579
806
  });
580
- console.log(import_chalk25.default.green(" Users & groups enabled."));
807
+ console.log(import_chalk26.default.green(" Users & groups enabled."));
581
808
  }
582
809
  await addApiblazeHostedLogin(teamId, tenant2, { allowSkip: true });
583
810
  return tenant2;
584
811
  }
585
- var import_chalk25, import_ora9, PROVIDERS, DEFAULT_SCOPES;
812
+ var import_chalk26, import_ora10, PROVIDERS, DEFAULT_SCOPES;
586
813
  var init_tenant_create = __esm({
587
814
  "src/lib/tenant-create.ts"() {
588
815
  "use strict";
589
- import_chalk25 = __toESM(require("chalk"));
590
- import_ora9 = __toESM(require("ora"));
816
+ import_chalk26 = __toESM(require("chalk"));
817
+ import_ora10 = __toESM(require("ora"));
591
818
  init_admin();
592
819
  PROVIDERS = [
593
820
  { id: "apiblaze", label: "APIblaze (via GitHub) \u2014 zero setup, recommended", own: false },
@@ -625,10 +852,10 @@ async function fetchPage(teamId, q) {
625
852
  }
626
853
  function label(t, defaultTenant, active) {
627
854
  const tags = [
628
- t.tenant_name === active ? import_chalk26.default.cyan("active scope") : "",
629
- t.tenant_name === defaultTenant ? import_chalk26.default.dim("team default") : ""
855
+ t.tenant_name === active ? import_chalk27.default.cyan("active scope") : "",
856
+ t.tenant_name === defaultTenant ? import_chalk27.default.dim("team default") : ""
630
857
  ].filter(Boolean).join(", ");
631
- const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk26.default.dim(` ${t.display_name}`) : "";
858
+ const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk27.default.dim(` ${t.display_name}`) : "";
632
859
  return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
633
860
  }
634
861
  async function pickTenant(teamId, opts = {}) {
@@ -636,14 +863,14 @@ async function pickTenant(teamId, opts = {}) {
636
863
  const active = loadCredentials()?.activeTenant;
637
864
  let q = opts.initialQuery ?? "";
638
865
  for (; ; ) {
639
- const spinner = (0, import_ora10.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
866
+ const spinner = (0, import_ora11.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
640
867
  const page = await fetchPage(teamId, q).finally(() => spinner.stop());
641
868
  if (!page.total && !q) {
642
869
  if (opts.allowCreate) {
643
870
  const { make } = await inquirer3.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
644
871
  if (make) return await createTenantInline(teamId);
645
872
  }
646
- console.error(import_chalk26.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
873
+ console.error(import_chalk27.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
647
874
  return null;
648
875
  }
649
876
  const truncated = page.total > page.rows.length;
@@ -652,7 +879,7 @@ async function pickTenant(teamId, opts = {}) {
652
879
  value: t.tenant_name
653
880
  }));
654
881
  if (truncated || q) {
655
- choices.push(new inquirer3.Separator(import_chalk26.default.dim(
882
+ choices.push(new inquirer3.Separator(import_chalk27.default.dim(
656
883
  truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
657
884
  )));
658
885
  choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
@@ -686,12 +913,12 @@ async function createTenantInline(teamId) {
686
913
  const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
687
914
  return createTenantInteractive2(teamId);
688
915
  }
689
- var import_chalk26, import_ora10, PAGE;
916
+ var import_chalk27, import_ora11, PAGE;
690
917
  var init_tenant_pick = __esm({
691
918
  "src/lib/tenant-pick.ts"() {
692
919
  "use strict";
693
- import_chalk26 = __toESM(require("chalk"));
694
- import_ora10 = __toESM(require("ora"));
920
+ import_chalk27 = __toESM(require("chalk"));
921
+ import_ora11 = __toESM(require("ora"));
695
922
  init_admin();
696
923
  init_auth();
697
924
  PAGE = 15;
@@ -700,10 +927,10 @@ var init_tenant_pick = __esm({
700
927
 
701
928
  // src/index.ts
702
929
  var import_commander = require("commander");
703
- var import_chalk41 = __toESM(require("chalk"));
930
+ var import_chalk43 = __toESM(require("chalk"));
704
931
 
705
932
  // package.json
706
- var version = "0.18.8";
933
+ var version = "0.19.0";
707
934
 
708
935
  // src/index.ts
709
936
  init_types();
@@ -836,9 +1063,9 @@ async function offerClaimPendingAnon() {
836
1063
  }
837
1064
  try {
838
1065
  const { claimTeam: claimTeam2 } = await Promise.resolve().then(() => (init_api(), api_exports));
839
- const t = await claimTeam2(cred.claim_code);
1066
+ const t = await claimTeam2(cred.claim_code, loadCredentials()?.teamId);
840
1067
  clearAnonCred2();
841
- console.log(import_chalk2.default.green(` \u2714 Claimed your anonymous proxies into your account (team ${import_chalk2.default.bold(t.team_id)}).`));
1068
+ console.log(import_chalk2.default.green(` \u2714 Claimed your anonymous proxies into your account (team ${import_chalk2.default.bold(t.team_name || t.team_id)}).`));
842
1069
  } catch (err) {
843
1070
  const { ApiError: ApiError2 } = await Promise.resolve().then(() => (init_types(), types_exports));
844
1071
  const code = err instanceof ApiError2 && typeof err.body === "object" && err.body ? err.body.error : void 0;
@@ -1613,8 +1840,8 @@ ${projects.length} project${projects.length === 1 ? "" : "s"}`));
1613
1840
 
1614
1841
  // src/commands/create.ts
1615
1842
  var import_fs2 = __toESM(require("fs"));
1616
- var import_chalk6 = __toESM(require("chalk"));
1617
- var import_ora4 = __toESM(require("ora"));
1843
+ var import_chalk10 = __toESM(require("chalk"));
1844
+ var import_ora5 = __toESM(require("ora"));
1618
1845
  init_auth();
1619
1846
  init_api();
1620
1847
  function normalizeName(raw) {
@@ -1641,7 +1868,7 @@ function stripTenantFromPortal(devPortal) {
1641
1868
  }
1642
1869
  }
1643
1870
  function fail(message) {
1644
- console.error(import_chalk6.default.red(`Error: ${message}`));
1871
+ console.error(import_chalk10.default.red(`Error: ${message}`));
1645
1872
  process.exit(1);
1646
1873
  }
1647
1874
  function buildTryItCurl(url, authType, apiKey) {
@@ -1656,11 +1883,11 @@ function printCurlExample(url, authType, apiKey, devPortal) {
1656
1883
  const curl = buildTryItCurl(url, authType, apiKey);
1657
1884
  console.log();
1658
1885
  if (curl) {
1659
- console.log(` ${import_chalk6.default.dim("Try it \u2014 copy/paste:")}`);
1660
- console.log(` ${import_chalk6.default.cyan(curl)}`);
1886
+ console.log(` ${import_chalk10.default.dim("Try it \u2014 copy/paste:")}`);
1887
+ console.log(` ${import_chalk10.default.cyan(curl)}`);
1661
1888
  } else if (authType === "oauth") {
1662
- console.log(` ${import_chalk6.default.dim("Try it:")} this proxy uses OAuth \u2014 sign in at ${import_chalk6.default.bold(devPortal ?? "the dev portal")} to get a token,`);
1663
- console.log(` ${import_chalk6.default.dim(`then call ${url} with`)} ${import_chalk6.default.cyan('-H "Authorization: Bearer <token>"')}`);
1889
+ console.log(` ${import_chalk10.default.dim("Try it:")} this proxy uses OAuth \u2014 sign in at ${import_chalk10.default.bold(devPortal ?? "the dev portal")} to get a token,`);
1890
+ console.log(` ${import_chalk10.default.dim(`then call ${url} with`)} ${import_chalk10.default.cyan('-H "Authorization: Bearer <token>"')}`);
1664
1891
  }
1665
1892
  }
1666
1893
  var VALID_AUTH = ["api_key", "none", "oauth"];
@@ -1690,7 +1917,7 @@ async function runCreate(opts = {}) {
1690
1917
  teamId = match.teamId;
1691
1918
  }
1692
1919
  }
1693
- if (!opts.json) console.log(import_chalk6.default.bold("\nCreate an API proxy\n"));
1920
+ if (!opts.json) console.log(import_chalk10.default.bold("\nCreate an API proxy\n"));
1694
1921
  let name = "";
1695
1922
  if (opts.name !== void 0) {
1696
1923
  name = normalizeName(opts.name);
@@ -1710,23 +1937,23 @@ async function runCreate(opts = {}) {
1710
1937
  }]);
1711
1938
  name = normalizeName(rawName);
1712
1939
  if (name.length < 3) {
1713
- console.log(import_chalk6.default.yellow(" Name must be at least 3 characters (letters and digits only).\n"));
1940
+ console.log(import_chalk10.default.yellow(" Name must be at least 3 characters (letters and digits only).\n"));
1714
1941
  continue;
1715
1942
  }
1716
- const spinner2 = (0, import_ora4.default)("Checking availability...").start();
1943
+ const spinner2 = (0, import_ora5.default)("Checking availability...").start();
1717
1944
  try {
1718
1945
  const check = await checkProxyName(name, teamId, opts.apiversion);
1719
1946
  spinner2.stop();
1720
1947
  if (!check.canUseProjectName || !check.canUseApiVersion) {
1721
- console.log(import_chalk6.default.yellow(` "${name}" is not available${check.message ? ` \u2014 ${check.message}` : ""}. Try another.
1948
+ console.log(import_chalk10.default.yellow(` "${name}" is not available${check.message ? ` \u2014 ${check.message}` : ""}. Try another.
1722
1949
  `));
1723
1950
  continue;
1724
1951
  }
1725
1952
  } catch {
1726
1953
  spinner2.stop();
1727
- console.log(import_chalk6.default.dim(" (could not verify availability; continuing)"));
1954
+ console.log(import_chalk10.default.dim(" (could not verify availability; continuing)"));
1728
1955
  }
1729
- console.log(`${import_chalk6.default.cyan("\u2192")} Your API will live at ${import_chalk6.default.bold(`https://${name}.abz.run`)}
1956
+ console.log(`${import_chalk10.default.cyan("\u2192")} Your API will live at ${import_chalk10.default.bold(`https://${name}.abz.run`)}
1730
1957
  `);
1731
1958
  break;
1732
1959
  }
@@ -1746,7 +1973,7 @@ async function runCreate(opts = {}) {
1746
1973
  message: "Target URL to forward requests to (e.g. https://httpbin.org):"
1747
1974
  }]);
1748
1975
  if (!isHttpUrl(url)) {
1749
- console.log(import_chalk6.default.yellow(" Enter a valid http(s) URL.\n"));
1976
+ console.log(import_chalk10.default.yellow(" Enter a valid http(s) URL.\n"));
1750
1977
  continue;
1751
1978
  }
1752
1979
  targetUrl = url.trim();
@@ -1757,7 +1984,7 @@ async function runCreate(opts = {}) {
1757
1984
  }
1758
1985
  if (interactive && !opts.yes) {
1759
1986
  const { default: inquirer3 } = await import("inquirer");
1760
- console.log(`${import_chalk6.default.cyan("\u2192")} Auth: ${import_chalk6.default.bold(auth)}${auth === "api_key" ? " \u2014 consumers send an X-API-Key header" : ""}`);
1987
+ console.log(`${import_chalk10.default.cyan("\u2192")} Auth: ${import_chalk10.default.bold(auth)}${auth === "api_key" ? " \u2014 consumers send an X-API-Key header" : ""}`);
1761
1988
  const { ok } = await inquirer3.prompt([{
1762
1989
  type: "confirm",
1763
1990
  name: "ok",
@@ -1765,15 +1992,15 @@ async function runCreate(opts = {}) {
1765
1992
  default: true
1766
1993
  }]);
1767
1994
  if (!ok) {
1768
- console.log(import_chalk6.default.yellow("Cancelled."));
1995
+ console.log(import_chalk10.default.yellow("Cancelled."));
1769
1996
  return;
1770
1997
  }
1771
1998
  }
1772
- const spinner = !opts.json ? (0, import_ora4.default)("Creating proxy (tenant, keys, dev portal)...").start() : null;
1999
+ const spinner = !opts.json ? (0, import_ora5.default)("Creating proxy (tenant, keys, dev portal)...").start() : null;
1773
2000
  let result;
1774
2001
  try {
1775
2002
  result = await createProxy({ name, target_url: targetUrl, auth_type: auth, team_id: teamId, ...opts.apiversion ? { api_version: opts.apiversion } : {} });
1776
- spinner?.succeed(import_chalk6.default.green("Proxy created!"));
2003
+ spinner?.succeed(import_chalk10.default.green("Proxy created!"));
1777
2004
  } catch (err) {
1778
2005
  spinner?.fail("Failed to create proxy.");
1779
2006
  throw err;
@@ -1796,26 +2023,37 @@ async function runCreate(opts = {}) {
1796
2023
  return;
1797
2024
  }
1798
2025
  console.log();
1799
- console.log(` ${import_chalk6.default.dim("Proxy URL: ")} ${import_chalk6.default.bold(proxyUrl)}`);
1800
- if (devPortal) console.log(` ${import_chalk6.default.dim("Dev portal:")} ${import_chalk6.default.bold(devPortal)}`);
2026
+ console.log(` ${import_chalk10.default.dim("Proxy URL: ")} ${import_chalk10.default.bold(proxyUrl)}`);
2027
+ if (devPortal) console.log(` ${import_chalk10.default.dim("Dev portal:")} ${import_chalk10.default.bold(devPortal)}`);
1801
2028
  if (adminKey) {
1802
2029
  console.log();
1803
- console.log(` ${import_chalk6.default.dim("Consumer admin API key (dev):")}`);
1804
- console.log(` ${import_chalk6.default.bold.green(adminKey)}`);
1805
- console.log(import_chalk6.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
2030
+ console.log(` ${import_chalk10.default.dim("Consumer admin API key (dev):")}`);
2031
+ console.log(` ${import_chalk10.default.bold.green(adminKey)}`);
2032
+ console.log(import_chalk10.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
1806
2033
  const otherEnvs = Object.keys(keys).filter((e) => e !== "dev");
1807
2034
  if (otherEnvs.length) {
1808
- console.log(import_chalk6.default.dim(` (Separate keys were also created for: ${otherEnvs.join(", ")}.)`));
2035
+ console.log(import_chalk10.default.dim(` (Separate keys were also created for: ${otherEnvs.join(", ")}.)`));
1809
2036
  }
1810
2037
  }
1811
2038
  printCurlExample(proxyUrl, auth, adminKey, devPortal);
1812
2039
  console.log();
2040
+ await applyCreateToggles(name, opts);
2041
+ }
2042
+ async function applyCreateToggles(name, opts) {
2043
+ if (!opts.identified && !opts.iam) return;
2044
+ const { runIamToggle: runIamToggle2, runIdentifiedToggle: runIdentifiedToggle2 } = await Promise.resolve().then(() => (init_iam(), iam_exports));
2045
+ if (opts.identified) {
2046
+ await runIdentifiedToggle2(name, "require", { team: opts.team, json: opts.json });
2047
+ }
2048
+ if (opts.iam) {
2049
+ await runIamToggle2(name, "on", { team: opts.team, json: opts.json });
2050
+ }
1813
2051
  }
1814
2052
  async function runAnonymousCreate(opts) {
1815
2053
  const interactive = !!process.stdin.isTTY && !opts.json;
1816
2054
  if (!opts.json) {
1817
- console.log(import_chalk6.default.bold("\nCreate an API proxy"));
1818
- console.log(import_chalk6.default.dim("Not logged in \u2014 creating an anonymous proxy. You can claim it to your account within 30 days.\n"));
2055
+ console.log(import_chalk10.default.bold("\nCreate an API proxy"));
2056
+ console.log(import_chalk10.default.dim("Not logged in \u2014 creating an anonymous proxy. You can claim it to your account within 30 days.\n"));
1819
2057
  }
1820
2058
  let body = {};
1821
2059
  if (opts.config) {
@@ -1861,7 +2099,7 @@ async function runAnonymousCreate(opts) {
1861
2099
  message: "Target URL to forward requests to (e.g. https://httpbin.org):"
1862
2100
  }]);
1863
2101
  if (!isHttpUrl(url)) {
1864
- console.log(import_chalk6.default.yellow(" Enter a valid http(s) URL.\n"));
2102
+ console.log(import_chalk10.default.yellow(" Enter a valid http(s) URL.\n"));
1865
2103
  continue;
1866
2104
  }
1867
2105
  target = url.trim();
@@ -1888,7 +2126,7 @@ async function runAnonymousCreate(opts) {
1888
2126
  const { loadAnonCred: loadAnonCred2, saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2, cpFetch: cpFetch2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
1889
2127
  if (opts.newSession) clearAnonCred2();
1890
2128
  const cred = loadAnonCred2();
1891
- const spinner = !opts.json ? (0, import_ora4.default)(cred ? "Creating proxy (in your anonymous workspace)..." : "Creating proxy...").start() : null;
2129
+ const spinner = !opts.json ? (0, import_ora5.default)(cred ? "Creating proxy (in your anonymous workspace)..." : "Creating proxy...").start() : null;
1892
2130
  let result;
1893
2131
  try {
1894
2132
  if (cred) {
@@ -1902,7 +2140,7 @@ async function runAnonymousCreate(opts) {
1902
2140
  saveAnonCred2(result.cp_key, result.team_id, result.claim_code);
1903
2141
  }
1904
2142
  }
1905
- spinner?.succeed(import_chalk6.default.green("Proxy created!"));
2143
+ spinner?.succeed(import_chalk10.default.green("Proxy created!"));
1906
2144
  } catch (err) {
1907
2145
  spinner?.fail("Failed to create proxy.");
1908
2146
  throw err;
@@ -1924,36 +2162,37 @@ async function runAnonymousCreate(opts) {
1924
2162
  return;
1925
2163
  }
1926
2164
  console.log();
1927
- if (prodEndpoint) console.log(` ${import_chalk6.default.dim("Proxy URL: ")} ${import_chalk6.default.bold(prodEndpoint)}`);
1928
- if (result.portal) console.log(` ${import_chalk6.default.dim("Dev portal:")} ${import_chalk6.default.bold(result.portal)}`);
2165
+ if (prodEndpoint) console.log(` ${import_chalk10.default.dim("Proxy URL: ")} ${import_chalk10.default.bold(prodEndpoint)}`);
2166
+ if (result.portal) console.log(` ${import_chalk10.default.dim("Dev portal:")} ${import_chalk10.default.bold(result.portal)}`);
1929
2167
  if (apiKey) {
1930
2168
  console.log();
1931
- console.log(` ${import_chalk6.default.dim("API key:")}`);
1932
- console.log(` ${import_chalk6.default.bold.green(apiKey)}`);
1933
- console.log(import_chalk6.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
2169
+ console.log(` ${import_chalk10.default.dim("API key:")}`);
2170
+ console.log(` ${import_chalk10.default.bold.green(apiKey)}`);
2171
+ console.log(import_chalk10.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
1934
2172
  }
1935
2173
  if (prodEndpoint) printCurlExample(prodEndpoint, opts.auth || "api_key", apiKey, result.portal);
1936
2174
  const claimCode = result.claim_code || cred?.claim_code;
1937
2175
  if (claimCode) {
1938
2176
  console.log();
1939
- console.log(` ${import_chalk6.default.yellow("\u26A0 Anonymous \u2014 claim within 30 days or it expires.")} To claim these proxies:`);
1940
- console.log(` ${import_chalk6.default.cyan(`npx apiblaze login && npx apiblaze claim ${claimCode}`)}`);
2177
+ console.log(` ${import_chalk10.default.yellow("\u26A0 Anonymous \u2014 claim within 30 days or it expires.")} To claim these proxies:`);
2178
+ console.log(` ${import_chalk10.default.cyan(`npx apiblaze login && npx apiblaze claim ${claimCode}`)}`);
1941
2179
  } else if (result.claim_url) {
1942
2180
  console.log();
1943
- console.log(` ${import_chalk6.default.yellow("\u26A0 Anonymous proxy \u2014 claim it to your account within 30 days or it expires:")}`);
1944
- console.log(` ${import_chalk6.default.bold(result.claim_url)}`);
2181
+ console.log(` ${import_chalk10.default.yellow("\u26A0 Anonymous proxy \u2014 claim it to your account within 30 days or it expires:")}`);
2182
+ console.log(` ${import_chalk10.default.bold(result.claim_url)}`);
1945
2183
  }
1946
2184
  console.log();
2185
+ if (name) await applyCreateToggles(name, opts);
1947
2186
  }
1948
2187
 
1949
2188
  // src/commands/claim.ts
1950
- var import_chalk7 = __toESM(require("chalk"));
2189
+ var import_chalk11 = __toESM(require("chalk"));
1951
2190
  init_auth();
1952
2191
  init_anon_cred();
1953
2192
  init_api();
1954
2193
  init_types();
1955
2194
  function fail2(message) {
1956
- console.error(import_chalk7.default.red(`Error: ${message}`));
2195
+ console.error(import_chalk11.default.red(`Error: ${message}`));
1957
2196
  process.exit(1);
1958
2197
  }
1959
2198
  async function runClaim(claimCodeArg, opts) {
@@ -1989,9 +2228,9 @@ async function runClaim(claimCodeArg, opts) {
1989
2228
  const c = typeof err.body === "object" && err.body ? err.body.error : void 0;
1990
2229
  if (c === "already_claimed" || c === "not_claimable") {
1991
2230
  if (fromLocal) clearAnonCred();
1992
- console.log(import_chalk7.default.yellow("\nThis workspace was already claimed (e.g. on dashboard.apiblaze.com)."));
1993
- if (fromLocal) console.log(import_chalk7.default.dim(" Cleared the local anonymous key."));
1994
- console.log(import_chalk7.default.dim(" Your proxies are safe under your account \u2014 manage them with `apiblaze login`."));
2231
+ console.log(import_chalk11.default.yellow("\nThis workspace was already claimed (e.g. on dashboard.apiblaze.com)."));
2232
+ if (fromLocal) console.log(import_chalk11.default.dim(" Cleared the local anonymous key."));
2233
+ console.log(import_chalk11.default.dim(" Your proxies are safe under your account \u2014 manage them with `apiblaze login`."));
1995
2234
  return;
1996
2235
  }
1997
2236
  if (c === "expired") {
@@ -2009,20 +2248,20 @@ async function runClaim(claimCodeArg, opts) {
2009
2248
  console.log(JSON.stringify(res, null, 2));
2010
2249
  return;
2011
2250
  }
2012
- console.log(import_chalk7.default.green("\n\u2714 Claimed your anonymous workspace into your account."));
2251
+ console.log(import_chalk11.default.green("\n\u2714 Claimed your anonymous workspace into your account."));
2013
2252
  if (res.merged) {
2014
- console.log(` Merged ${import_chalk7.default.bold(String(res.proxy_count ?? ""))} prox${res.proxy_count === 1 ? "y" : "ies"} into team ${import_chalk7.default.bold(res.team_id)}.`);
2253
+ console.log(` Merged ${import_chalk11.default.bold(String(res.proxy_count ?? ""))} prox${res.proxy_count === 1 ? "y" : "ies"} into team ${import_chalk11.default.bold(res.team_id)}.`);
2015
2254
  } else {
2016
2255
  const label3 = res.team_name ? `\u201C${res.team_name}\u201D` : res.team_id;
2017
- console.log(` It's now your team ${import_chalk7.default.bold(label3)}. Switch to it with ${import_chalk7.default.cyan(`apiblaze team ${res.team_id}`)}.`);
2018
- console.log(import_chalk7.default.dim(` (Tip: name it with --name, or --team <name> to merge into an existing team instead.)`));
2256
+ console.log(` It's now your team ${import_chalk11.default.bold(label3)}. Switch to it with ${import_chalk11.default.cyan(`apiblaze team ${res.team_id}`)}.`);
2257
+ console.log(import_chalk11.default.dim(` (Tip: name it with --name, or --team <name> to merge into an existing team instead.)`));
2019
2258
  }
2020
2259
  console.log(`
2021
- Manage it at ${import_chalk7.default.cyan("https://dashboard.apiblaze.com/dashboard")}`);
2260
+ Manage it at ${import_chalk11.default.cyan("https://dashboard.apiblaze.com/dashboard")}`);
2022
2261
  }
2023
2262
 
2024
2263
  // src/commands/logout.ts
2025
- var import_chalk8 = __toESM(require("chalk"));
2264
+ var import_chalk12 = __toESM(require("chalk"));
2026
2265
  init_auth();
2027
2266
 
2028
2267
  // src/lib/consumer-auth.ts
@@ -2154,7 +2393,7 @@ async function runLogout(opts = {}) {
2154
2393
  const creds = loadCredentials();
2155
2394
  const consumer2 = loadConsumer();
2156
2395
  if (!creds && !consumer2) {
2157
- console.log(import_chalk8.default.yellow("You were not logged in."));
2396
+ console.log(import_chalk12.default.yellow("You were not logged in."));
2158
2397
  return;
2159
2398
  }
2160
2399
  let target;
@@ -2181,16 +2420,16 @@ async function runLogout(opts = {}) {
2181
2420
  }
2182
2421
  if (target === "producer" || target === "all") {
2183
2422
  clearCredentials();
2184
- console.log(import_chalk8.default.green(`\u2714 Logged out of API Producer${creds?.githubHandle ? ` (${creds.githubHandle})` : ""}.`));
2423
+ console.log(import_chalk12.default.green(`\u2714 Logged out of API Producer${creds?.githubHandle ? ` (${creds.githubHandle})` : ""}.`));
2185
2424
  }
2186
2425
  if (target === "consumer" || target === "all") {
2187
2426
  clearConsumer();
2188
- console.log(import_chalk8.default.green(`\u2714 Logged out of API Consumer${consumer2?.tenant ? ` (${consumer2.tenant})` : ""}.`));
2427
+ console.log(import_chalk12.default.green(`\u2714 Logged out of API Consumer${consumer2?.tenant ? ` (${consumer2.tenant})` : ""}.`));
2189
2428
  }
2190
2429
  }
2191
2430
 
2192
2431
  // src/commands/flush.ts
2193
- var import_chalk9 = __toESM(require("chalk"));
2432
+ var import_chalk13 = __toESM(require("chalk"));
2194
2433
  var fs6 = __toESM(require("fs"));
2195
2434
  var path4 = __toESM(require("path"));
2196
2435
  init_auth();
@@ -2198,22 +2437,22 @@ async function runFlush(opts) {
2198
2437
  const dir = getApiblazeDir();
2199
2438
  const entries2 = fs6.existsSync(dir) ? fs6.readdirSync(dir) : [];
2200
2439
  if (!entries2.length) {
2201
- console.log(import_chalk9.default.dim("Nothing to flush \u2014 no local apiblaze config found."));
2440
+ console.log(import_chalk13.default.dim("Nothing to flush \u2014 no local apiblaze config found."));
2202
2441
  return;
2203
2442
  }
2204
- console.log(import_chalk9.default.yellow("This removes ALL local apiblaze state from this machine:"));
2205
- console.log(import_chalk9.default.dim(" \u2022 login credentials & anonymous-workspace keys"));
2206
- console.log(import_chalk9.default.dim(" \u2022 saved apichats \u2014 their DP API keys and transcripts"));
2207
- console.log(import_chalk9.default.dim(" \u2022 your BYO LLM key and any cached secrets"));
2208
- console.log(import_chalk9.default.dim(` (${entries2.length} item(s) in ${dir})`));
2209
- console.log(import_chalk9.default.dim(" It does NOT delete server-side proxies/keys \u2014 those GC or are revoked separately."));
2443
+ console.log(import_chalk13.default.yellow("This removes ALL local apiblaze state from this machine:"));
2444
+ console.log(import_chalk13.default.dim(" \u2022 login credentials & anonymous-workspace keys"));
2445
+ console.log(import_chalk13.default.dim(" \u2022 saved apichats \u2014 their DP API keys and transcripts"));
2446
+ console.log(import_chalk13.default.dim(" \u2022 your BYO LLM key and any cached secrets"));
2447
+ console.log(import_chalk13.default.dim(` (${entries2.length} item(s) in ${dir})`));
2448
+ console.log(import_chalk13.default.dim(" It does NOT delete server-side proxies/keys \u2014 those GC or are revoked separately."));
2210
2449
  if (!opts.yes) {
2211
2450
  const { default: inquirer3 } = await import("inquirer");
2212
2451
  const { ok } = await inquirer3.prompt([
2213
2452
  { type: "confirm", name: "ok", message: "Delete all local state?", default: false }
2214
2453
  ]);
2215
2454
  if (!ok) {
2216
- console.log(import_chalk9.default.dim("Cancelled \u2014 nothing removed."));
2455
+ console.log(import_chalk13.default.dim("Cancelled \u2014 nothing removed."));
2217
2456
  return;
2218
2457
  }
2219
2458
  }
@@ -2223,14 +2462,14 @@ async function runFlush(opts) {
2223
2462
  fs6.rmSync(path4.join(dir, e), { recursive: true, force: true });
2224
2463
  removed++;
2225
2464
  } catch (err) {
2226
- console.log(import_chalk9.default.red(` \u2717 could not remove ${e}: ${err instanceof Error ? err.message : "error"}`));
2465
+ console.log(import_chalk13.default.red(` \u2717 could not remove ${e}: ${err instanceof Error ? err.message : "error"}`));
2227
2466
  }
2228
2467
  }
2229
- console.log(`${import_chalk9.default.green("\u2714")} Flushed \u2014 removed ${removed} item(s). You're fully logged out and clean.`);
2468
+ console.log(`${import_chalk13.default.green("\u2714")} Flushed \u2014 removed ${removed} item(s). You're fully logged out and clean.`);
2230
2469
  }
2231
2470
 
2232
2471
  // src/commands/whoami.ts
2233
- var import_chalk10 = __toESM(require("chalk"));
2472
+ var import_chalk14 = __toESM(require("chalk"));
2234
2473
  init_auth();
2235
2474
  init_anon_cred();
2236
2475
  async function runWhoami(opts = {}) {
@@ -2253,43 +2492,43 @@ async function runWhoami(opts = {}) {
2253
2492
  }, null, 2));
2254
2493
  return;
2255
2494
  }
2256
- console.log(import_chalk10.default.bold("API Producer"));
2495
+ console.log(import_chalk14.default.bold("API Producer"));
2257
2496
  if (!creds) {
2258
- console.log(import_chalk10.default.dim(" Not logged in. Run `apiblaze login`."));
2497
+ console.log(import_chalk14.default.dim(" Not logged in. Run `apiblaze login`."));
2259
2498
  } else {
2260
2499
  const who = creds.githubHandle ?? creds.email ?? creds.apiblazeUserId ?? "unknown";
2261
- console.log(` ${import_chalk10.default.cyan("Signed in as")} ${import_chalk10.default.bold(who)}`);
2500
+ console.log(` ${import_chalk14.default.cyan("Signed in as")} ${import_chalk14.default.bold(who)}`);
2262
2501
  if (creds.email && creds.email !== who) console.log(` Email: ${creds.email}`);
2263
- if (creds.teamName || creds.teamId) console.log(` Team: ${import_chalk10.default.bold(creds.teamName ?? creds.teamId)}`);
2264
- if (creds.activeTenant) console.log(` Tenant scope: ${import_chalk10.default.bold(creds.activeTenant)} ${import_chalk10.default.dim("(clear: `apiblaze tenant use --clear`)")}`);
2265
- if (Date.now() >= creds.expiresAt) console.log(import_chalk10.default.yellow(" \u26A0 Session expired \u2014 run `apiblaze login`."));
2502
+ if (creds.teamName || creds.teamId) console.log(` Team: ${import_chalk14.default.bold(creds.teamName ?? creds.teamId)}`);
2503
+ if (creds.activeTenant) console.log(` Tenant scope: ${import_chalk14.default.bold(creds.activeTenant)} ${import_chalk14.default.dim("(clear: `apiblaze tenant use --clear`)")}`);
2504
+ if (Date.now() >= creds.expiresAt) console.log(import_chalk14.default.yellow(" \u26A0 Session expired \u2014 run `apiblaze login`."));
2266
2505
  }
2267
2506
  if (anon) {
2268
2507
  const masked = `${anon.cp_key.slice(0, 11)}\u2026${anon.cp_key.slice(-4)}`;
2269
- console.log(import_chalk10.default.bold("\nAnonymous workspace") + import_chalk10.default.dim(" (created without an account \u2014 unclaimed)"));
2270
- console.log(` Team: ${import_chalk10.default.bold(anon.team_id)}`);
2271
- console.log(` CP key: ${import_chalk10.default.bold(masked)} ${import_chalk10.default.dim("controls the proxies you created anonymously")}`);
2508
+ console.log(import_chalk14.default.bold("\nAnonymous workspace") + import_chalk14.default.dim(" (created without an account \u2014 unclaimed)"));
2509
+ console.log(` Team: ${import_chalk14.default.bold(anon.team_id)}`);
2510
+ console.log(` CP key: ${import_chalk14.default.bold(masked)} ${import_chalk14.default.dim("controls the proxies you created anonymously")}`);
2272
2511
  console.log(
2273
- import_chalk10.default.dim(` Keep it: ${import_chalk10.default.cyan("apiblaze login")} then ${import_chalk10.default.cyan("apiblaze claim")}`) + (anon.claim_code ? import_chalk10.default.dim(` \xB7 from another machine: ${import_chalk10.default.cyan(`apiblaze claim ${anon.claim_code}`)}`) : "")
2512
+ import_chalk14.default.dim(` Keep it: ${import_chalk14.default.cyan("apiblaze login")} then ${import_chalk14.default.cyan("apiblaze claim")}`) + (anon.claim_code ? import_chalk14.default.dim(` \xB7 from another machine: ${import_chalk14.default.cyan(`apiblaze claim ${anon.claim_code}`)}`) : "")
2274
2513
  );
2275
- if (creds) console.log(import_chalk10.default.dim(" (you're logged in \u2014 `apiblaze claim` folds this workspace into your account)"));
2514
+ if (creds) console.log(import_chalk14.default.dim(" (you're logged in \u2014 `apiblaze claim` folds this workspace into your account)"));
2276
2515
  }
2277
- console.log(import_chalk10.default.bold("\nAPI Consumer"));
2516
+ console.log(import_chalk14.default.bold("\nAPI Consumer"));
2278
2517
  if (!consumer2) {
2279
- console.log(import_chalk10.default.dim(" Not logged in. Run `apiblaze consumer login`."));
2518
+ console.log(import_chalk14.default.dim(" Not logged in. Run `apiblaze consumer login`."));
2280
2519
  } else {
2281
- console.log(` ${import_chalk10.default.cyan("Signed in as")} ${import_chalk10.default.bold(consumer2.email ?? consumer2.tenant)}`);
2282
- console.log(` Tenant: ${import_chalk10.default.bold(consumer2.tenant)}`);
2283
- if (Date.now() >= consumer2.expiresAt) console.log(import_chalk10.default.yellow(" \u26A0 Token expired \u2014 it will refresh on next use, or re-run `apiblaze consumer login`."));
2520
+ console.log(` ${import_chalk14.default.cyan("Signed in as")} ${import_chalk14.default.bold(consumer2.email ?? consumer2.tenant)}`);
2521
+ console.log(` Tenant: ${import_chalk14.default.bold(consumer2.tenant)}`);
2522
+ if (Date.now() >= consumer2.expiresAt) console.log(import_chalk14.default.yellow(" \u26A0 Token expired \u2014 it will refresh on next use, or re-run `apiblaze consumer login`."));
2284
2523
  }
2285
2524
  }
2286
2525
 
2287
2526
  // src/commands/team.ts
2288
- var import_chalk11 = __toESM(require("chalk"));
2527
+ var import_chalk15 = __toESM(require("chalk"));
2289
2528
  init_auth();
2290
2529
  init_api();
2291
2530
  function fail3(message) {
2292
- console.error(import_chalk11.default.red(`Error: ${message}`));
2531
+ console.error(import_chalk15.default.red(`Error: ${message}`));
2293
2532
  process.exit(1);
2294
2533
  }
2295
2534
  async function runTeam(arg) {
@@ -2311,7 +2550,7 @@ async function runTeam(arg) {
2311
2550
  }
2312
2551
  } else if (teams.length === 1) {
2313
2552
  chosen = teams[0];
2314
- console.log(`${import_chalk11.default.cyan("\u2192")} You only have one team: ${import_chalk11.default.bold(chosen.name)}`);
2553
+ console.log(`${import_chalk15.default.cyan("\u2192")} You only have one team: ${import_chalk15.default.bold(chosen.name)}`);
2315
2554
  } else if (process.stdin.isTTY) {
2316
2555
  const { default: inquirer3 } = await import("inquirer");
2317
2556
  const { picked } = await inquirer3.prompt([{
@@ -2326,47 +2565,47 @@ async function runTeam(arg) {
2326
2565
  fail3(`Specify a team: \`apiblaze team <name|id>\`. Available: ${teams.map((t) => t.name).join(", ")}.`);
2327
2566
  }
2328
2567
  saveCredentials({ ...creds, teamId: chosen.teamId, teamName: chosen.name });
2329
- console.log(import_chalk11.default.green(`
2330
- \u2714 Active team: ${import_chalk11.default.bold(chosen.name)}`));
2568
+ console.log(import_chalk15.default.green(`
2569
+ \u2714 Active team: ${import_chalk15.default.bold(chosen.name)}`));
2331
2570
  }
2332
2571
 
2333
2572
  // src/commands/authz.ts
2334
- var import_chalk16 = __toESM(require("chalk"));
2573
+ var import_chalk18 = __toESM(require("chalk"));
2335
2574
  init_auth();
2336
2575
  init_api();
2337
2576
 
2338
2577
  // src/lib/agent-chat.ts
2339
2578
  var import_readline = __toESM(require("readline"));
2340
- var import_chalk14 = __toESM(require("chalk"));
2579
+ var import_chalk16 = __toESM(require("chalk"));
2341
2580
  init_api();
2342
2581
  init_admin();
2343
2582
  async function runAgentChatRepl(opts) {
2344
2583
  const { title, subtitle, endpoint, buildBody, seedPrompt, summarizeProposal, commands } = opts;
2345
- console.log("\n" + import_chalk14.default.cyan.bold(title));
2346
- if (subtitle) console.log(import_chalk14.default.dim(subtitle));
2584
+ console.log("\n" + import_chalk16.default.cyan.bold(title));
2585
+ if (subtitle) console.log(import_chalk16.default.dim(subtitle));
2347
2586
  const messages = [];
2348
2587
  const ctx = { proposal: null, lastData: null, log: (s) => console.log(s) };
2349
2588
  const cmdByName = new Map(commands.map((c) => [c.name, c]));
2350
2589
  const footer = () => {
2351
2590
  const parts = [
2352
- import_chalk14.default.dim("type to chat/refine"),
2353
- ...commands.map((c) => import_chalk14.default.dim(`/${c.name} ${c.describe}`)),
2354
- import_chalk14.default.dim("/show"),
2355
- import_chalk14.default.dim("/drop discard & exit")
2591
+ import_chalk16.default.dim("type to chat/refine"),
2592
+ ...commands.map((c) => import_chalk16.default.dim(`/${c.name} ${c.describe}`)),
2593
+ import_chalk16.default.dim("/show"),
2594
+ import_chalk16.default.dim("/drop discard & exit")
2356
2595
  ];
2357
- return " " + import_chalk14.default.dim("[ ") + parts.join(import_chalk14.default.dim(" \xB7 ")) + import_chalk14.default.dim(" ]");
2596
+ return " " + import_chalk16.default.dim("[ ") + parts.join(import_chalk16.default.dim(" \xB7 ")) + import_chalk16.default.dim(" ]");
2358
2597
  };
2359
2598
  async function turn(content) {
2360
2599
  messages.push({ role: "user", content });
2361
- process.stdout.write(import_chalk14.default.dim(" \u2026thinking\n"));
2600
+ process.stdout.write(import_chalk16.default.dim(" \u2026thinking\n"));
2362
2601
  const { status, data } = await agentCall(endpoint, "POST", { messages, ...buildBody(messages) });
2363
2602
  if (status === 402) {
2364
- console.log(import_chalk14.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
2603
+ console.log(import_chalk16.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
2365
2604
  messages.pop();
2366
2605
  return;
2367
2606
  }
2368
2607
  if (status >= 400 || !data) {
2369
- console.log(import_chalk14.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
2608
+ console.log(import_chalk16.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
2370
2609
  `));
2371
2610
  messages.pop();
2372
2611
  return;
@@ -2374,19 +2613,19 @@ async function runAgentChatRepl(opts) {
2374
2613
  ctx.lastData = data;
2375
2614
  const reply = data.reply ?? data.message ?? "(no reply)";
2376
2615
  messages.push({ role: "assistant", content: reply });
2377
- console.log("\n" + import_chalk14.default.cyan("agent \u203A ") + reply + "\n");
2616
+ console.log("\n" + import_chalk16.default.cyan("agent \u203A ") + reply + "\n");
2378
2617
  if (data.proposal) {
2379
2618
  ctx.proposal = data.proposal;
2380
2619
  const summary = summarizeProposal?.(data);
2381
- if (summary) console.log(import_chalk14.default.yellow(" " + summary));
2620
+ if (summary) console.log(import_chalk16.default.yellow(" " + summary));
2382
2621
  const ready = commands.filter((c) => c.needsProposal).map((c) => `/${c.name}`).join(" or ");
2383
- if (ready) console.log(import_chalk14.default.dim(` Ready \u2014 ${ready} to make it official, or keep refining.`));
2622
+ if (ready) console.log(import_chalk16.default.dim(` Ready \u2014 ${ready} to make it official, or keep refining.`));
2384
2623
  console.log();
2385
2624
  }
2386
2625
  const line = formatBilling(data.billing);
2387
2626
  if (line) {
2388
2627
  const llm2 = data.llm;
2389
- console.log(import_chalk14.default.dim(` ${line}${llm2?.model ? ` \xB7 ${llm2.model}` : ""}`));
2628
+ console.log(import_chalk16.default.dim(` ${line}${llm2?.model ? ` \xB7 ${llm2.model}` : ""}`));
2390
2629
  }
2391
2630
  }
2392
2631
  const rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout });
@@ -2394,26 +2633,26 @@ async function runAgentChatRepl(opts) {
2394
2633
  await turn(seedPrompt);
2395
2634
  for (; ; ) {
2396
2635
  console.log(footer());
2397
- const line = (await ask(import_chalk14.default.green("you \u203A "))).trim();
2636
+ const line = (await ask(import_chalk16.default.green("you \u203A "))).trim();
2398
2637
  if (!line) continue;
2399
2638
  if (line === "/drop" || line === "/exit" || line === "/quit") {
2400
- console.log(import_chalk14.default.dim(" Dropped \u2014 nothing applied.\n"));
2639
+ console.log(import_chalk16.default.dim(" Dropped \u2014 nothing applied.\n"));
2401
2640
  break;
2402
2641
  }
2403
2642
  if (line === "/show") {
2404
- if (!ctx.proposal) console.log(import_chalk14.default.dim(" No proposal yet \u2014 keep chatting until the agent proposes one.\n"));
2643
+ if (!ctx.proposal) console.log(import_chalk16.default.dim(" No proposal yet \u2014 keep chatting until the agent proposes one.\n"));
2405
2644
  else console.log("\n" + JSON.stringify(ctx.proposal, null, 2) + "\n");
2406
2645
  continue;
2407
2646
  }
2408
2647
  if (line.startsWith("/")) {
2409
2648
  const cmd = cmdByName.get(line.slice(1));
2410
2649
  if (!cmd) {
2411
- console.log(import_chalk14.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
2650
+ console.log(import_chalk16.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
2412
2651
  `));
2413
2652
  continue;
2414
2653
  }
2415
2654
  if (cmd.needsProposal && !ctx.proposal) {
2416
- console.log(import_chalk14.default.yellow(" No proposal yet \u2014 keep chatting until the agent generates one, then try again.\n"));
2655
+ console.log(import_chalk16.default.yellow(" No proposal yet \u2014 keep chatting until the agent generates one, then try again.\n"));
2417
2656
  continue;
2418
2657
  }
2419
2658
  await cmd.run(ctx);
@@ -2425,19 +2664,48 @@ async function runAgentChatRepl(opts) {
2425
2664
  }
2426
2665
 
2427
2666
  // src/lib/authz-apply.ts
2428
- var import_chalk15 = __toESM(require("chalk"));
2667
+ var import_chalk17 = __toESM(require("chalk"));
2429
2668
  init_api();
2669
+ async function gatherSampleIds(projectId, apiVersion, cap = 50) {
2670
+ try {
2671
+ const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
2672
+ const routes = routesRes?.data?.routes ?? [];
2673
+ const total = routes.reduce((n, r) => n + (Number(r.total) || 0), 0);
2674
+ if (!routes.length || total === 0) return { ids: [], total: 0 };
2675
+ const ordered = routes.filter((r) => r.route_hash).sort((a, b) => (Number(b.total) || 0) - (Number(a.total) || 0)).slice(0, 20);
2676
+ const ids = [];
2677
+ for (const r of ordered) {
2678
+ if (ids.length >= cap) break;
2679
+ const sRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes/${encodeURIComponent(r.route_hash)}/samples`, "GET");
2680
+ for (const s of sRes?.data?.samples ?? []) {
2681
+ if (ids.length >= cap) break;
2682
+ if (s.sample_id) ids.push(s.sample_id);
2683
+ }
2684
+ }
2685
+ return { ids, total };
2686
+ } catch {
2687
+ return { ids: [], total: 0 };
2688
+ }
2689
+ }
2690
+ function maybeWarnNoTraffic(total, projectArg) {
2691
+ if (total >= 3) return;
2692
+ console.log(import_chalk17.default.yellow(total === 0 ? " \u26A0 No captured traffic for this API yet." : ` \u26A0 Only ${total} captured request(s) so far.`));
2693
+ console.log(import_chalk17.default.dim(" The authorization agent designs much better models & rules from REAL requests"));
2694
+ console.log(import_chalk17.default.dim(" (it sees who is allowed vs denied). Send some through your proxy first \u2014"));
2695
+ console.log(import_chalk17.default.dim(` curl a few endpoints, or `) + import_chalk17.default.cyan("npx apiblaze dev") + import_chalk17.default.dim(` \u2014 then re-run.
2696
+ `));
2697
+ }
2430
2698
  async function applyProposal(opts) {
2431
2699
  const log = opts.log ?? ((s) => console.log(s));
2432
2700
  const { projectId, apiVersion, tenant: tenant2, proposal, enable } = opts;
2433
2701
  if (!enable) {
2434
2702
  const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant2)}`, "POST", proposal.model);
2435
2703
  if (m.status === 404) {
2436
- log(import_chalk15.default.red(" Authorization store not provisioned yet. Open the dashboard Authorization tab for this project once (it auto-provisions the store), then re-run.\n"));
2704
+ log(import_chalk17.default.red(" Authorization store not provisioned yet. Open the dashboard Authorization tab for this project once (it auto-provisions the store), then re-run.\n"));
2437
2705
  return { ok: 0, fail: 0, enabled: false, storeMissing: true };
2438
2706
  }
2439
2707
  if (m.status >= 400) {
2440
- log(import_chalk15.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
2708
+ log(import_chalk17.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
2441
2709
  `));
2442
2710
  return { ok: 0, fail: 0, enabled: false };
2443
2711
  }
@@ -2466,7 +2734,7 @@ async function applyProposal(opts) {
2466
2734
  if (enable) {
2467
2735
  const e = await agentCall(`/${projectId}/${apiVersion}/config`, "PATCH", { authorization: { enforce_authorization: true }, tenant: tenant2 });
2468
2736
  if (e.status >= 400) {
2469
- log(import_chalk15.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
2737
+ log(import_chalk17.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
2470
2738
  `));
2471
2739
  return { ok, fail: fail5, enabled: false };
2472
2740
  }
@@ -2492,18 +2760,20 @@ async function runAuthz(projectArg, apiVersionArg) {
2492
2760
  const r = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable, log: ctx.log });
2493
2761
  if (r.storeMissing) return;
2494
2762
  if (enable && r.enabled) {
2495
- ctx.log(import_chalk16.default.green(` \u2713 Enforcement ON \u2014 ${r.ok} route(s) + project switch on${r.fail ? `, ${r.fail} failed` : ""}.
2763
+ ctx.log(import_chalk18.default.green(` \u2713 Enforcement ON \u2014 ${r.ok} route(s) + project switch on${r.fail ? `, ${r.fail} failed` : ""}.
2496
2764
  `));
2497
2765
  } else if (!enable) {
2498
- ctx.log(import_chalk16.default.green(` \u2713 Published (shadow): model + ${r.ok} route(s)${r.fail ? `, ${r.fail} failed` : ""}. Review, then /enable.
2766
+ ctx.log(import_chalk18.default.green(` \u2713 Published (shadow): model + ${r.ok} route(s)${r.fail ? `, ${r.fail} failed` : ""}. Review, then /enable.
2499
2767
  `));
2500
2768
  }
2501
2769
  }
2770
+ const { ids: sampleIds, total: sampleTotal } = await gatherSampleIds(projectId, apiVersion);
2771
+ maybeWarnNoTraffic(sampleTotal, projectArg);
2502
2772
  await runAgentChatRepl({
2503
2773
  title: `Authorization assistant \u2014 ${projectId} ${apiVersion}`,
2504
- subtitle: `tenant ${tenant2} \xB7 discuss what authorization fits this API, then make it official.`,
2774
+ subtitle: sampleIds.length ? `tenant ${tenant2} \xB7 ${sampleIds.length} traffic sample(s) \xB7 discuss what fits, then make it official.` : `tenant ${tenant2} \xB7 discuss what authorization fits this API, then make it official.`,
2505
2775
  endpoint: `/projects/${projectId}/${apiVersion}/authz/chat`,
2506
- buildBody: () => ({ included_sample_ids: [], existing_model: null, existing_routes: [] }),
2776
+ buildBody: () => ({ included_sample_ids: sampleIds, existing_model: null, existing_routes: [] }),
2507
2777
  seedPrompt: "Analyze this API and tell me what authorization is feasible. If it is read-only, say so plainly. Then propose options \u2014 do not generate rules yet.",
2508
2778
  summarizeProposal: (data) => {
2509
2779
  const proposal = data.proposal;
@@ -2520,8 +2790,8 @@ async function runAuthz(projectArg, apiVersionArg) {
2520
2790
  }
2521
2791
 
2522
2792
  // src/commands/rule.ts
2523
- var import_chalk17 = __toESM(require("chalk"));
2524
- var import_ora5 = __toESM(require("ora"));
2793
+ var import_chalk19 = __toESM(require("chalk"));
2794
+ var import_ora6 = __toESM(require("ora"));
2525
2795
  init_auth();
2526
2796
  init_api();
2527
2797
  async function runRule(ruleText, projectArg, opts) {
@@ -2538,7 +2808,9 @@ async function runRule(ruleText, projectArg, opts) {
2538
2808
  const projectId = match.projectId;
2539
2809
  const apiVersion = opts.apiversion || match.apiVersion;
2540
2810
  const tenant2 = match.tenant || projectId;
2541
- const spinner = (0, import_ora5.default)("Designing the rule\u2026").start();
2811
+ const { ids: sampleIds, total: sampleTotal } = await gatherSampleIds(projectId, apiVersion);
2812
+ maybeWarnNoTraffic(sampleTotal, projectArg);
2813
+ const spinner = (0, import_ora6.default)(sampleIds.length ? `Designing the rule from ${sampleIds.length} traffic sample(s)\u2026` : "Designing the rule\u2026").start();
2542
2814
  const messages = [{
2543
2815
  role: "user",
2544
2816
  content: `Author this authorization rule and generate the COMPLETE proposal (OpenFGA model + per-route rules) NOW for every affected route. Do not ask clarifying questions; make reasonable assumptions and state them briefly. Rule: "${rule}"`
@@ -2546,7 +2818,7 @@ async function runRule(ruleText, projectArg, opts) {
2546
2818
  const { status, data } = await agentCall(
2547
2819
  `/projects/${projectId}/${apiVersion}/authz/chat`,
2548
2820
  "POST",
2549
- { messages, included_sample_ids: [], existing_model: null, existing_routes: [] }
2821
+ { messages, included_sample_ids: sampleIds, existing_model: null, existing_routes: [] }
2550
2822
  );
2551
2823
  if (status >= 400) {
2552
2824
  spinner.fail(`Authorization agent error (${status}): ${data?.error ?? ""}`);
@@ -2555,27 +2827,27 @@ async function runRule(ruleText, projectArg, opts) {
2555
2827
  const proposal = data?.proposal;
2556
2828
  if (!proposal || !Array.isArray(proposal.routes) || proposal.routes.length === 0) {
2557
2829
  spinner.fail("The agent couldn\u2019t turn that into a rule in one shot.");
2558
- if (data?.reply) console.log(import_chalk17.default.dim("\n " + String(data.reply).slice(0, 500) + "\n"));
2559
- console.log(import_chalk17.default.dim(" Try an interactive session for anything nuanced: ") + import_chalk17.default.cyan(`apiblaze agent authz ${projectArg}`));
2830
+ if (data?.reply) console.log(import_chalk19.default.dim("\n " + String(data.reply).slice(0, 500) + "\n"));
2831
+ console.log(import_chalk19.default.dim(" Try an interactive session for anything nuanced: ") + import_chalk19.default.cyan(`apiblaze agent authz ${projectArg}`));
2560
2832
  return;
2561
2833
  }
2562
2834
  spinner.succeed(`Rule designed \u2014 ${proposal.routes.length} route(s) affected.`);
2563
- if (data?.reply) console.log(import_chalk17.default.dim(" " + String(data.reply).slice(0, 240)));
2835
+ if (data?.reply) console.log(import_chalk19.default.dim(" " + String(data.reply).slice(0, 240)));
2564
2836
  const pub = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable: false });
2565
2837
  if (pub.storeMissing) return;
2566
- console.log(import_chalk17.default.green(` \u2713 Published in shadow mode \u2014 model + ${pub.ok} route(s)${pub.fail ? `, ${pub.fail} failed` : ""}.`));
2838
+ console.log(import_chalk19.default.green(` \u2713 Published in shadow mode \u2014 model + ${pub.ok} route(s)${pub.fail ? `, ${pub.fail} failed` : ""}.`));
2567
2839
  if (opts.enforce) {
2568
2840
  const enf = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable: true });
2569
2841
  if (enf.enabled) {
2570
- console.log(import_chalk17.default.green(` \u2713 Enforcement ON \u2014 ${enf.ok} route(s) + project switch on${enf.fail ? `, ${enf.fail} failed` : ""}.`));
2842
+ console.log(import_chalk19.default.green(` \u2713 Enforcement ON \u2014 ${enf.ok} route(s) + project switch on${enf.fail ? `, ${enf.fail} failed` : ""}.`));
2571
2843
  }
2572
2844
  } else {
2573
- console.log(import_chalk17.default.dim(` Review it (shadow logs), then enforce: `) + import_chalk17.default.cyan(`apiblaze rule "${rule}" ${projectArg} --enforce`));
2845
+ console.log(import_chalk19.default.dim(` Review it (shadow logs), then enforce: `) + import_chalk19.default.cyan(`apiblaze rule "${rule}" ${projectArg} --enforce`));
2574
2846
  }
2575
2847
  }
2576
2848
 
2577
2849
  // src/commands/openapi.ts
2578
- var import_chalk18 = __toESM(require("chalk"));
2850
+ var import_chalk20 = __toESM(require("chalk"));
2579
2851
  init_auth();
2580
2852
  init_api();
2581
2853
  async function runOpenapi(projectArg, apiVersionArg) {
@@ -2586,10 +2858,10 @@ async function runOpenapi(projectArg, apiVersionArg) {
2586
2858
  if (!match) throw new Error(`Project "${projectArg}" not found in your active team. Run \`apiblaze projects\`.`);
2587
2859
  const projectId = match.projectId;
2588
2860
  const apiVersion = apiVersionArg || match.apiVersion;
2589
- console.log(import_chalk18.default.dim("Gathering captured traffic samples\u2026"));
2861
+ console.log(import_chalk20.default.dim("Gathering captured traffic samples\u2026"));
2590
2862
  const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
2591
2863
  if (routesRes.status >= 400) {
2592
- console.log(import_chalk18.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
2864
+ console.log(import_chalk20.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
2593
2865
  return;
2594
2866
  }
2595
2867
  const routes = routesRes.data?.routes ?? (Array.isArray(routesRes.data) ? routesRes.data : []);
@@ -2602,25 +2874,25 @@ async function runOpenapi(projectArg, apiVersionArg) {
2602
2874
  }
2603
2875
  const sampleIds = [...ids];
2604
2876
  if (sampleIds.length === 0) {
2605
- console.log(import_chalk18.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
2877
+ console.log(import_chalk20.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
2606
2878
  return;
2607
2879
  }
2608
2880
  async function publish(ctx) {
2609
2881
  const patch = ctx.proposal.patch;
2610
2882
  if (!Array.isArray(patch) || patch.length === 0) {
2611
- ctx.log(import_chalk18.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
2883
+ ctx.log(import_chalk20.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
2612
2884
  return;
2613
2885
  }
2614
2886
  const specSource = ctx.lastData?.spec_source ?? "unknown";
2615
2887
  const endpoint = specSource === "github" ? "open-pr" : "publish-openapi";
2616
2888
  const pub = await agentCall(`/projects/${projectId}/${apiVersion}/samples/${endpoint}`, "POST", { patch, sample_ids_used: sampleIds });
2617
2889
  if (pub.status >= 400) {
2618
- ctx.log(import_chalk18.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
2890
+ ctx.log(import_chalk20.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
2619
2891
  `));
2620
2892
  return;
2621
2893
  }
2622
2894
  const prUrl = pub.data?.pr_url;
2623
- ctx.log(import_chalk18.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
2895
+ ctx.log(import_chalk20.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
2624
2896
  ` : " \u2713 Published updated OpenAPI spec.\n"));
2625
2897
  }
2626
2898
  await runAgentChatRepl({
@@ -2641,7 +2913,7 @@ async function runOpenapi(projectArg, apiVersionArg) {
2641
2913
  }
2642
2914
 
2643
2915
  // src/commands/mcp.ts
2644
- var import_chalk19 = __toESM(require("chalk"));
2916
+ var import_chalk21 = __toESM(require("chalk"));
2645
2917
  init_auth();
2646
2918
  init_api();
2647
2919
  async function runMcp(projectArg, apiVersionArg, opts) {
@@ -2658,11 +2930,11 @@ async function runMcp(projectArg, apiVersionArg, opts) {
2658
2930
  const spec2 = ctx.proposal;
2659
2931
  const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec: spec2 });
2660
2932
  if (pub.status >= 400) {
2661
- ctx.log(import_chalk19.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
2933
+ ctx.log(import_chalk21.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
2662
2934
  `));
2663
2935
  return;
2664
2936
  }
2665
- ctx.log(import_chalk19.default.green(` \u2713 Published MCP server \u2014 ${mcpHost}.mcp.apiblaze.com/${apiVersion}/${environment}.
2937
+ ctx.log(import_chalk21.default.green(` \u2713 Published MCP server \u2014 ${mcpHost}.mcp.apiblaze.com/${apiVersion}/${environment}.
2666
2938
  `));
2667
2939
  }
2668
2940
  await runAgentChatRepl({
@@ -2684,69 +2956,14 @@ async function runMcp(projectArg, apiVersionArg, opts) {
2684
2956
  }
2685
2957
 
2686
2958
  // src/commands/delete.ts
2687
- var import_chalk21 = __toESM(require("chalk"));
2688
- var import_ora6 = __toESM(require("ora"));
2959
+ var import_chalk22 = __toESM(require("chalk"));
2960
+ var import_ora7 = __toESM(require("ora"));
2689
2961
  init_admin();
2690
-
2691
- // src/lib/resolve.ts
2692
- var import_chalk20 = __toESM(require("chalk"));
2693
- init_auth();
2694
- init_api();
2695
- function requireAuth() {
2696
- const creds = loadCredentials();
2697
- if (!creds) {
2698
- console.error(import_chalk20.default.red("Not logged in. Run `apiblaze login` first."));
2699
- process.exit(1);
2700
- }
2701
- return creds;
2702
- }
2703
- async function resolveTeam(opt) {
2704
- const creds = requireAuth();
2705
- if (!opt) {
2706
- if (!creds.teamId) {
2707
- console.error(import_chalk20.default.red("No active team. Run `apiblaze login` or pass --team."));
2708
- process.exit(1);
2709
- }
2710
- return { teamId: creds.teamId, teamName: creds.teamName };
2711
- }
2712
- if (opt.startsWith("team_")) return { teamId: opt };
2713
- const teams = await getTeams().catch(() => []);
2714
- const match = teams.find((t) => t.name === opt || t.teamId === opt);
2715
- if (!match) {
2716
- console.error(import_chalk20.default.red(`Team "${opt}" not found.${teams.length ? " Available: " + teams.map((t) => t.name).join(", ") : ""}`));
2717
- process.exit(1);
2718
- }
2719
- return { teamId: match.teamId, teamName: match.name };
2720
- }
2721
- async function resolveProject(teamId, nameOrId, version2) {
2722
- const projects = await getProjects(teamId).catch(() => []);
2723
- const candidates = projects.filter(
2724
- (p) => p.projectId === nameOrId || p.projectName === nameOrId
2725
- );
2726
- if (candidates.length === 0) {
2727
- console.error(import_chalk20.default.red(`Project "${nameOrId}" not found in this team.`));
2728
- if (projects.length) console.error(import_chalk20.default.dim(" Known: " + projects.map((p) => p.projectName).join(", ")));
2729
- process.exit(1);
2730
- }
2731
- const chosen = version2 ? candidates.find((p) => p.apiVersion === version2) : candidates[0];
2732
- if (!chosen) {
2733
- console.error(import_chalk20.default.red(`Project "${nameOrId}" has no version ${version2}. Versions: ${candidates.map((p) => p.apiVersion).join(", ")}`));
2734
- process.exit(1);
2735
- }
2736
- return {
2737
- projectId: chosen.projectId,
2738
- projectName: chosen.projectName,
2739
- apiVersion: chosen.apiVersion,
2740
- teamId,
2741
- tenant: chosen.tenant
2742
- };
2743
- }
2744
-
2745
- // src/commands/delete.ts
2962
+ init_resolve();
2746
2963
  async function runDelete(project, version2, opts) {
2747
2964
  const { teamId } = await resolveTeam(opts.team);
2748
2965
  const proj2 = await resolveProject(teamId, project, version2);
2749
- const spinner = (0, import_ora6.default)("Checking delete impact...").start();
2966
+ const spinner = (0, import_ora7.default)("Checking delete impact...").start();
2750
2967
  let impact;
2751
2968
  try {
2752
2969
  impact = await admin({
@@ -2765,10 +2982,10 @@ async function runDelete(project, version2, opts) {
2765
2982
  return;
2766
2983
  }
2767
2984
  } else {
2768
- console.log(`${import_chalk21.default.red.bold("Delete")} ${import_chalk21.default.bold(proj2.projectName)} ${import_chalk21.default.dim("v" + proj2.apiVersion)} ${import_chalk21.default.dim("(" + proj2.projectId + ")")}`);
2985
+ console.log(`${import_chalk22.default.red.bold("Delete")} ${import_chalk22.default.bold(proj2.projectName)} ${import_chalk22.default.dim("v" + proj2.apiVersion)} ${import_chalk22.default.dim("(" + proj2.projectId + ")")}`);
2769
2986
  if (impact && typeof impact === "object") {
2770
2987
  const counts = impact.counts ?? impact.impact ?? impact;
2771
- console.log(import_chalk21.default.dim(" This removes (cascade): ") + import_chalk21.default.yellow(JSON.stringify(counts)));
2988
+ console.log(import_chalk22.default.dim(" This removes (cascade): ") + import_chalk22.default.yellow(JSON.stringify(counts)));
2772
2989
  }
2773
2990
  if (!opts.yes) {
2774
2991
  const { default: inquirer3 } = await import("inquirer");
@@ -2776,12 +2993,12 @@ async function runDelete(project, version2, opts) {
2776
2993
  { type: "confirm", name: "confirm", message: `Permanently delete ${proj2.projectName} v${proj2.apiVersion}? This cannot be undone.`, default: false }
2777
2994
  ]);
2778
2995
  if (!confirm) {
2779
- console.log(import_chalk21.default.dim("Aborted."));
2996
+ console.log(import_chalk22.default.dim("Aborted."));
2780
2997
  return;
2781
2998
  }
2782
2999
  }
2783
3000
  }
2784
- const s2 = (0, import_ora6.default)("Deleting...").start();
3001
+ const s2 = (0, import_ora7.default)("Deleting...").start();
2785
3002
  try {
2786
3003
  await admin({
2787
3004
  method: "DELETE",
@@ -2797,10 +3014,11 @@ async function runDelete(project, version2, opts) {
2797
3014
  }
2798
3015
 
2799
3016
  // src/commands/export.ts
2800
- var import_chalk22 = __toESM(require("chalk"));
3017
+ var import_chalk23 = __toESM(require("chalk"));
2801
3018
  var import_fs3 = require("fs");
2802
3019
  init_admin();
2803
3020
  init_auth();
3021
+ init_resolve();
2804
3022
  var DASHBOARD_BASE4 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
2805
3023
  async function runExport(projectArg, versionArg, opts) {
2806
3024
  const token = getAccessToken();
@@ -2815,7 +3033,7 @@ async function runExport(projectArg, versionArg, opts) {
2815
3033
  if (opts.noConsumers) q.set("include_consumers", "0");
2816
3034
  if (opts.keys) q.set("keys", opts.keys);
2817
3035
  const qs = q.toString() ? `?${q.toString()}` : "";
2818
- console.log(import_chalk22.default.dim(`Queuing ${target} export of ${projectId}@${version2}\u2026`));
3036
+ console.log(import_chalk23.default.dim(`Queuing ${target} export of ${projectId}@${version2}\u2026`));
2819
3037
  const job = await admin({
2820
3038
  path: `/projects/${projectId}/${version2}/export/${target}${qs}`,
2821
3039
  method: "GET",
@@ -2825,11 +3043,11 @@ async function runExport(projectArg, versionArg, opts) {
2825
3043
  const statusPath = `/projects/${projectId}/${version2}/export/jobs/${job.job_id}`;
2826
3044
  let status = "queued";
2827
3045
  const started = Date.now();
2828
- process.stdout.write(import_chalk22.default.dim("Building bundle"));
3046
+ process.stdout.write(import_chalk23.default.dim("Building bundle"));
2829
3047
  while (status !== "ready" && status !== "failed") {
2830
3048
  if (Date.now() - started > 10 * 6e4) throw new Error("Export timed out after 10 minutes.");
2831
3049
  await new Promise((r) => setTimeout(r, 3e3));
2832
- process.stdout.write(import_chalk22.default.dim("."));
3050
+ process.stdout.write(import_chalk23.default.dim("."));
2833
3051
  const st = await admin({ path: statusPath, method: "GET", summary: "poll export" });
2834
3052
  status = st?.status ?? "queued";
2835
3053
  if (status === "failed") {
@@ -2847,20 +3065,21 @@ async function runExport(projectArg, versionArg, opts) {
2847
3065
  if (!res.ok) throw new Error(`Download failed (${res.status}): ${await res.text().catch(() => "")}`);
2848
3066
  const buf = Buffer.from(await res.arrayBuffer());
2849
3067
  (0, import_fs3.writeFileSync)(outFile, buf);
2850
- console.log(import_chalk22.default.green(`\u2713 Saved ${outFile} (${(buf.length / 1024).toFixed(0)} KB)`));
2851
- if (job.decryption_key) console.log(import_chalk22.default.yellow(`Decryption key (shown once): ${job.decryption_key}`));
2852
- console.log(import_chalk22.default.dim("Read MIGRATION-REPORT.json inside the zip for the full fidelity ledger."));
2853
- if (target === "kong") console.log(import_chalk22.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
3068
+ console.log(import_chalk23.default.green(`\u2713 Saved ${outFile} (${(buf.length / 1024).toFixed(0)} KB)`));
3069
+ if (job.decryption_key) console.log(import_chalk23.default.yellow(`Decryption key (shown once): ${job.decryption_key}`));
3070
+ console.log(import_chalk23.default.dim("Read MIGRATION-REPORT.json inside the zip for the full fidelity ledger."));
3071
+ if (target === "kong") console.log(import_chalk23.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
2854
3072
  }
2855
3073
 
2856
3074
  // src/commands/config.ts
2857
- var import_chalk23 = __toESM(require("chalk"));
2858
- var import_ora7 = __toESM(require("ora"));
3075
+ var import_chalk24 = __toESM(require("chalk"));
3076
+ var import_ora8 = __toESM(require("ora"));
2859
3077
  init_admin();
3078
+ init_resolve();
2860
3079
  async function patchConfig(project, opts, body, summary) {
2861
3080
  const { teamId } = await resolveTeam(opts.team);
2862
3081
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
2863
- const spinner = (0, import_ora7.default)(summary + "...").start();
3082
+ const spinner = (0, import_ora8.default)(summary + "...").start();
2864
3083
  try {
2865
3084
  const out = await admin({
2866
3085
  method: "PATCH",
@@ -2877,7 +3096,7 @@ async function patchConfig(project, opts, body, summary) {
2877
3096
  }
2878
3097
  async function runTargetSet(project, opts) {
2879
3098
  if (!opts.url) {
2880
- console.error(import_chalk23.default.red("--url is required."));
3099
+ console.error(import_chalk24.default.red("--url is required."));
2881
3100
  process.exit(1);
2882
3101
  }
2883
3102
  const body = opts.env ? { environments: { [opts.env]: { target: opts.url } } } : { target_url: opts.url };
@@ -2890,43 +3109,45 @@ async function runThrottleSet(project, opts) {
2890
3109
  if (opts.quota !== void 0) throttling.proxyQuota = Number(opts.quota);
2891
3110
  if (opts.period !== void 0) {
2892
3111
  if (!["daily", "weekly", "monthly"].includes(opts.period)) {
2893
- console.error(import_chalk23.default.red("--period must be daily, weekly, or monthly."));
3112
+ console.error(import_chalk24.default.red("--period must be daily, weekly, or monthly."));
2894
3113
  process.exit(1);
2895
3114
  }
2896
3115
  throttling.quotaPeriod = opts.period;
2897
3116
  }
2898
3117
  if (Object.keys(throttling).length === 0) {
2899
- console.error(import_chalk23.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
3118
+ console.error(import_chalk24.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
2900
3119
  process.exit(1);
2901
3120
  }
2902
3121
  await patchConfig(project, opts, { throttling }, `Update throttling ${JSON.stringify(throttling)}`);
2903
3122
  }
2904
3123
  async function runRename(project, opts) {
2905
3124
  if (!opts.displayName) {
2906
- console.error(import_chalk23.default.red("--display-name is required."));
3125
+ console.error(import_chalk24.default.red("--display-name is required."));
2907
3126
  process.exit(1);
2908
3127
  }
2909
3128
  await patchConfig(project, opts, { display_name: opts.displayName }, `Rename \u2192 "${opts.displayName}"`);
2910
3129
  }
2911
3130
 
2912
3131
  // src/commands/config-browse.ts
2913
- var import_chalk32 = __toESM(require("chalk"));
2914
- var import_ora15 = __toESM(require("ora"));
3132
+ var import_chalk33 = __toESM(require("chalk"));
3133
+ var import_ora16 = __toESM(require("ora"));
2915
3134
  init_admin();
2916
3135
  init_auth();
3136
+ init_resolve();
2917
3137
 
2918
3138
  // src/commands/domain.ts
2919
- var import_chalk24 = __toESM(require("chalk"));
2920
- var import_ora8 = __toESM(require("ora"));
3139
+ var import_chalk25 = __toESM(require("chalk"));
3140
+ var import_ora9 = __toESM(require("ora"));
2921
3141
  init_admin();
3142
+ init_resolve();
2922
3143
  async function runDomainAdd(project, opts) {
2923
3144
  if (!opts.domain) {
2924
- console.error(import_chalk24.default.red("--domain is required."));
3145
+ console.error(import_chalk25.default.red("--domain is required."));
2925
3146
  process.exit(1);
2926
3147
  }
2927
3148
  const { teamId } = await resolveTeam(opts.team);
2928
3149
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
2929
- const spinner = (0, import_ora8.default)(`Registering ${opts.domain}...`).start();
3150
+ const spinner = (0, import_ora9.default)(`Registering ${opts.domain}...`).start();
2930
3151
  try {
2931
3152
  const out = await admin({
2932
3153
  method: "POST",
@@ -2940,9 +3161,9 @@ async function runDomainAdd(project, opts) {
2940
3161
  console.log(JSON.stringify(out));
2941
3162
  return;
2942
3163
  }
2943
- if (out?.cnameRecord) console.log(` ${import_chalk24.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
2944
- if (out?.dcv) console.log(` ${import_chalk24.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
2945
- if (out?.id) console.log(import_chalk24.default.dim(` domain id: ${out.id}`));
3164
+ if (out?.cnameRecord) console.log(` ${import_chalk25.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
3165
+ if (out?.dcv) console.log(` ${import_chalk25.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
3166
+ if (out?.id) console.log(import_chalk25.default.dim(` domain id: ${out.id}`));
2946
3167
  } catch (err) {
2947
3168
  spinner.fail("Domain registration failed.");
2948
3169
  throw err;
@@ -2962,14 +3183,14 @@ async function runDomainList(project, opts) {
2962
3183
  return;
2963
3184
  }
2964
3185
  if (!domains.length) {
2965
- console.log(import_chalk24.default.yellow("No custom domains."));
3186
+ console.log(import_chalk25.default.yellow("No custom domains."));
2966
3187
  return;
2967
3188
  }
2968
- for (const d of domains) console.log(` ${import_chalk24.default.bold(d.hostname)} ${import_chalk24.default.dim(d.status ?? "")} ${import_chalk24.default.dim(d.id ?? "")}`);
3189
+ for (const d of domains) console.log(` ${import_chalk25.default.bold(d.hostname)} ${import_chalk25.default.dim(d.status ?? "")} ${import_chalk25.default.dim(d.id ?? "")}`);
2969
3190
  }
2970
3191
  async function runDomainStatus(project, opts) {
2971
3192
  if (!opts.id) {
2972
- console.error(import_chalk24.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
3193
+ console.error(import_chalk25.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
2973
3194
  process.exit(1);
2974
3195
  }
2975
3196
  const { teamId } = await resolveTeam(opts.team);
@@ -2979,16 +3200,16 @@ async function runDomainStatus(project, opts) {
2979
3200
  path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}/status`,
2980
3201
  summary: `Check custom-domain status`
2981
3202
  });
2982
- console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk24.default.bold(out?.hostname ?? opts.id)}: ${import_chalk24.default.cyan(out?.status ?? "unknown")}`);
3203
+ console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk25.default.bold(out?.hostname ?? opts.id)}: ${import_chalk25.default.cyan(out?.status ?? "unknown")}`);
2983
3204
  }
2984
3205
  async function runDomainRemove(project, opts) {
2985
3206
  if (!opts.id) {
2986
- console.error(import_chalk24.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
3207
+ console.error(import_chalk25.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
2987
3208
  process.exit(1);
2988
3209
  }
2989
3210
  const { teamId } = await resolveTeam(opts.team);
2990
3211
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
2991
- const spinner = (0, import_ora8.default)("Removing domain...").start();
3212
+ const spinner = (0, import_ora9.default)("Removing domain...").start();
2992
3213
  try {
2993
3214
  await admin({
2994
3215
  method: "DELETE",
@@ -3005,7 +3226,7 @@ async function runDomainSetBase(project, opts) {
3005
3226
  const env = opts.env ?? "prod";
3006
3227
  const { teamId } = await resolveTeam(opts.team);
3007
3228
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
3008
- const spinner = (0, import_ora8.default)("Setting base domain...").start();
3229
+ const spinner = (0, import_ora9.default)("Setting base domain...").start();
3009
3230
  try {
3010
3231
  await admin({
3011
3232
  method: "PUT",
@@ -3021,28 +3242,29 @@ async function runDomainSetBase(project, opts) {
3021
3242
  }
3022
3243
 
3023
3244
  // src/commands/tenant.ts
3024
- var import_chalk27 = __toESM(require("chalk"));
3025
- var import_ora11 = __toESM(require("ora"));
3245
+ var import_chalk28 = __toESM(require("chalk"));
3246
+ var import_ora12 = __toESM(require("ora"));
3026
3247
  init_admin();
3248
+ init_resolve();
3027
3249
  init_auth();
3028
3250
  init_tenant_pick();
3029
3251
  async function runTenantUse(query, opts) {
3030
3252
  const creds = loadCredentials();
3031
3253
  if (!creds) {
3032
- console.error(import_chalk27.default.red("Not logged in. Run `apiblaze login` first."));
3254
+ console.error(import_chalk28.default.red("Not logged in. Run `apiblaze login` first."));
3033
3255
  process.exit(1);
3034
3256
  }
3035
3257
  if (opts.clear) {
3036
3258
  delete creds.activeTenant;
3037
3259
  saveCredentials(creds);
3038
- console.log(import_chalk27.default.green("Tenant scope cleared."));
3260
+ console.log(import_chalk28.default.green("Tenant scope cleared."));
3039
3261
  return;
3040
3262
  }
3041
3263
  const { teamId } = await resolveTeam(opts.team);
3042
3264
  const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
3043
3265
  if (!slug) process.exit(1);
3044
3266
  saveCredentials({ ...creds, activeTenant: slug });
3045
- console.log(import_chalk27.default.green(`Tenant scope set to ${import_chalk27.default.bold(slug)}.`) + import_chalk27.default.dim(" (clear with `apiblaze tenant use --clear`)"));
3267
+ console.log(import_chalk28.default.green(`Tenant scope set to ${import_chalk28.default.bold(slug)}.`) + import_chalk28.default.dim(" (clear with `apiblaze tenant use --clear`)"));
3046
3268
  }
3047
3269
  async function runTenantList(opts) {
3048
3270
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -3059,26 +3281,26 @@ async function runTenantList(opts) {
3059
3281
  return;
3060
3282
  }
3061
3283
  if (!tenants.length) {
3062
- console.log(import_chalk27.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
3284
+ console.log(import_chalk28.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
3063
3285
  return;
3064
3286
  }
3065
3287
  for (const t of tenants) {
3066
3288
  const name = typeof t === "string" ? t : t.tenant_name;
3067
- const display = typeof t === "string" ? "" : import_chalk27.default.dim(` ${t.display_name ?? ""}`);
3068
- console.log(` ${import_chalk27.default.bold(name)}${display}`);
3289
+ const display = typeof t === "string" ? "" : import_chalk28.default.dim(` ${t.display_name ?? ""}`);
3290
+ console.log(` ${import_chalk28.default.bold(name)}${display}`);
3069
3291
  }
3070
3292
  const total = out?.total ?? tenants.length;
3071
3293
  if (total > tenants.length) {
3072
- console.log(import_chalk27.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
3294
+ console.log(import_chalk28.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
3073
3295
  }
3074
3296
  }
3075
3297
  async function runTenantCreate(opts) {
3076
3298
  if (!opts.name) {
3077
- console.error(import_chalk27.default.red("--name (display name) is required."));
3299
+ console.error(import_chalk28.default.red("--name (display name) is required."));
3078
3300
  process.exit(1);
3079
3301
  }
3080
3302
  const { teamId } = await resolveTeam(opts.team);
3081
- const spinner = (0, import_ora11.default)("Creating tenant...").start();
3303
+ const spinner = (0, import_ora12.default)("Creating tenant...").start();
3082
3304
  try {
3083
3305
  const out = await admin({
3084
3306
  method: "POST",
@@ -3086,7 +3308,7 @@ async function runTenantCreate(opts) {
3086
3308
  body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
3087
3309
  summary: `Create tenant "${opts.name}"`
3088
3310
  });
3089
- spinner.succeed(`Created tenant ${import_chalk27.default.bold(out?.tenant_name ?? opts.name)}.`);
3311
+ spinner.succeed(`Created tenant ${import_chalk28.default.bold(out?.tenant_name ?? opts.name)}.`);
3090
3312
  if (opts.json) console.log(JSON.stringify(out));
3091
3313
  } catch (err) {
3092
3314
  spinner.fail("Tenant create failed.");
@@ -3095,12 +3317,12 @@ async function runTenantCreate(opts) {
3095
3317
  }
3096
3318
  async function runTenantAttach(project, opts) {
3097
3319
  if (!opts.tenant) {
3098
- console.error(import_chalk27.default.red("--tenant <slug> is required."));
3320
+ console.error(import_chalk28.default.red("--tenant <slug> is required."));
3099
3321
  process.exit(1);
3100
3322
  }
3101
3323
  const { teamId } = await resolveTeam(opts.team);
3102
3324
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
3103
- const spinner = (0, import_ora11.default)("Attaching tenant...").start();
3325
+ const spinner = (0, import_ora12.default)("Attaching tenant...").start();
3104
3326
  try {
3105
3327
  const out = await admin({
3106
3328
  method: "POST",
@@ -3123,11 +3345,11 @@ async function runTenantDelete(slug, opts) {
3123
3345
  { type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
3124
3346
  ]);
3125
3347
  if (!confirm) {
3126
- console.log(import_chalk27.default.dim("Aborted."));
3348
+ console.log(import_chalk28.default.dim("Aborted."));
3127
3349
  return;
3128
3350
  }
3129
3351
  }
3130
- const spinner = (0, import_ora11.default)("Deleting tenant...").start();
3352
+ const spinner = (0, import_ora12.default)("Deleting tenant...").start();
3131
3353
  try {
3132
3354
  await admin({
3133
3355
  method: "DELETE",
@@ -3142,13 +3364,13 @@ async function runTenantDelete(slug, opts) {
3142
3364
  }
3143
3365
  async function runTenantCors(opts) {
3144
3366
  if (!opts.tenant) {
3145
- console.error(import_chalk27.default.red("--tenant <slug> is required."));
3367
+ console.error(import_chalk28.default.red("--tenant <slug> is required."));
3146
3368
  process.exit(1);
3147
3369
  }
3148
3370
  const { teamId } = await resolveTeam(opts.team);
3149
3371
  const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
3150
3372
  const cors = origins.length ? { allowed_origins: origins } : null;
3151
- const spinner = (0, import_ora11.default)("Updating CORS...").start();
3373
+ const spinner = (0, import_ora12.default)("Updating CORS...").start();
3152
3374
  try {
3153
3375
  await admin({
3154
3376
  method: "PUT",
@@ -3164,11 +3386,12 @@ async function runTenantCors(opts) {
3164
3386
  }
3165
3387
 
3166
3388
  // src/commands/tenant-drill.ts
3167
- var import_chalk28 = __toESM(require("chalk"));
3168
- var import_ora12 = __toESM(require("ora"));
3389
+ var import_chalk29 = __toESM(require("chalk"));
3390
+ var import_ora13 = __toESM(require("ora"));
3169
3391
  var import_crypto = require("crypto");
3170
3392
  init_admin();
3171
3393
  init_auth();
3394
+ init_resolve();
3172
3395
  init_tenant_pick();
3173
3396
  var trailingComma = /\s*,\s*/;
3174
3397
  var parseList = (s) => s.split(trailingComma).map((x) => x.trim()).filter(Boolean);
@@ -3194,17 +3417,17 @@ async function validScopedTenant(teamId, query) {
3194
3417
  const next = { ...creds };
3195
3418
  delete next.activeTenant;
3196
3419
  saveCredentials2(next);
3197
- console.log(import_chalk28.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
3420
+ console.log(import_chalk29.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
3198
3421
  return void 0;
3199
3422
  }
3200
3423
  async function tenantHome(teamId, tenant2) {
3201
3424
  const { default: inquirer3 } = await import("inquirer");
3202
3425
  const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
3203
- console.log(import_chalk28.default.bold(`
3426
+ console.log(import_chalk29.default.bold(`
3204
3427
  Tenant ${tenant2}`));
3205
- console.log(import_chalk28.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
3428
+ console.log(import_chalk29.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
3206
3429
  for (; ; ) {
3207
- const spinner = (0, import_ora12.default)("Reading tenant state...").start();
3430
+ const spinner = (0, import_ora13.default)("Reading tenant state...").start();
3208
3431
  const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
3209
3432
  admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
3210
3433
  admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
@@ -3218,18 +3441,18 @@ Tenant ${tenant2}`));
3218
3441
  const nClients = Array.isArray(clients) ? clients.length : 0;
3219
3442
  const nOpaque = opaque?.opaque?.endpoint ? 1 : 0;
3220
3443
  const nLogin = nClients + nIssuers + nOpaque;
3221
- const onOff = (b) => b ? import_chalk28.default.green("on") : import_chalk28.default.dim("off");
3444
+ const onOff = (b) => b ? import_chalk29.default.green("on") : import_chalk29.default.dim("off");
3222
3445
  const { pick: pick2 } = await inquirer3.prompt([{
3223
3446
  type: "list",
3224
3447
  name: "pick",
3225
3448
  message: `Tenant ${tenant2}:`,
3226
3449
  pageSize: 12,
3227
3450
  choices: [
3228
- { name: `Login methods (${nLogin}) ${import_chalk28.default.dim("how your consumers sign in")}`, value: "login" },
3229
- { name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk28.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
3230
- { name: `Portal admins (${nEmails}) ${import_chalk28.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
3231
- new inquirer3.Separator(import_chalk28.default.dim(" Settings")),
3232
- { name: `CORS: ${cors?.cors ? import_chalk28.default.cyan(JSON.stringify(cors.cors)) : import_chalk28.default.dim("(unset)")} ${import_chalk28.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
3451
+ { name: `Login methods (${nLogin}) ${import_chalk29.default.dim("how your consumers sign in")}`, value: "login" },
3452
+ { name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk29.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
3453
+ { name: `Portal admins (${nEmails}) ${import_chalk29.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
3454
+ new inquirer3.Separator(import_chalk29.default.dim(" Settings")),
3455
+ { name: `CORS: ${cors?.cors ? import_chalk29.default.cyan(JSON.stringify(cors.cors)) : import_chalk29.default.dim("(unset)")} ${import_chalk29.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
3233
3456
  { name: "\u2190 Back", value: "back" }
3234
3457
  ]
3235
3458
  }]);
@@ -3242,7 +3465,7 @@ Tenant ${tenant2}`));
3242
3465
  case "iam": {
3243
3466
  const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
3244
3467
  await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
3245
- console.log(import_chalk28.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
3468
+ console.log(import_chalk29.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
3246
3469
  break;
3247
3470
  }
3248
3471
  case "cors": {
@@ -3255,11 +3478,11 @@ Tenant ${tenant2}`));
3255
3478
  if (v === "") break;
3256
3479
  const parsed = v === "null" ? null : safeJson(v);
3257
3480
  if (parsed === void 0) {
3258
- console.log(import_chalk28.default.yellow(" Not valid JSON \u2014 unchanged."));
3481
+ console.log(import_chalk29.default.yellow(" Not valid JSON \u2014 unchanged."));
3259
3482
  break;
3260
3483
  }
3261
3484
  await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
3262
- console.log(import_chalk28.default.green(" CORS updated."));
3485
+ console.log(import_chalk29.default.green(" CORS updated."));
3263
3486
  break;
3264
3487
  }
3265
3488
  case "emails":
@@ -3271,7 +3494,7 @@ Tenant ${tenant2}`));
3271
3494
  async function loginMethodsMenu(teamId, tenant2, base) {
3272
3495
  const { default: inquirer3 } = await import("inquirer");
3273
3496
  for (; ; ) {
3274
- const spinner = (0, import_ora12.default)("Loading login methods...").start();
3497
+ const spinner = (0, import_ora13.default)("Loading login methods...").start();
3275
3498
  const [rawClients, rawIssuers, rawOpaque] = await Promise.all([
3276
3499
  admin({ method: "GET", path: `${base}/app-clients`, summary: "List APIblaze-hosted logins" }).catch(() => []),
3277
3500
  admin({ method: "GET", path: `${base}/external-issuers`, summary: "List your-own-JWT logins" }).catch(() => null),
@@ -3283,15 +3506,15 @@ async function loginMethodsMenu(teamId, tenant2, base) {
3283
3506
  const choices = [];
3284
3507
  for (const c of appClients) {
3285
3508
  const nP = (c.providers ?? []).length || c.providers_count || 0;
3286
- choices.push({ name: `${import_chalk28.default.cyan("APIblaze-hosted")} ${import_chalk28.default.bold(c.name ?? c.clientId)} ${import_chalk28.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk28.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
3509
+ choices.push({ name: `${import_chalk29.default.cyan("APIblaze-hosted")} ${import_chalk29.default.bold(c.name ?? c.clientId)} ${import_chalk29.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk29.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
3287
3510
  }
3288
3511
  for (const i of issuers) {
3289
- choices.push({ name: `${import_chalk28.default.cyan("Your own \u2014 JWT ")} ${import_chalk28.default.bold(i.iss)} ${import_chalk28.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
3512
+ choices.push({ name: `${import_chalk29.default.cyan("Your own \u2014 JWT ")} ${import_chalk29.default.bold(i.iss)} ${import_chalk29.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
3290
3513
  }
3291
3514
  if (opaque?.endpoint) {
3292
- choices.push({ name: `${import_chalk28.default.cyan("Your own \u2014 opaque")} ${import_chalk28.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
3515
+ choices.push({ name: `${import_chalk29.default.cyan("Your own \u2014 opaque")} ${import_chalk29.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
3293
3516
  }
3294
- if (!choices.length) console.log(import_chalk28.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
3517
+ if (!choices.length) console.log(import_chalk29.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
3295
3518
  const { pick: pick2 } = await inquirer3.prompt([{
3296
3519
  type: "list",
3297
3520
  name: "pick",
@@ -3331,9 +3554,9 @@ async function addLoginMethod(teamId, tenant2, base) {
3331
3554
  message: "How do people log in?",
3332
3555
  pageSize: 8,
3333
3556
  choices: [
3334
- { name: `APIblaze hosted login page ${import_chalk28.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
3335
- { name: `Your own hosted login \u2014 JWT ${import_chalk28.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
3336
- { name: `Your own login \u2014 opaque token ${import_chalk28.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
3557
+ { name: `APIblaze hosted login page ${import_chalk29.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
3558
+ { name: `Your own hosted login \u2014 JWT ${import_chalk29.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
3559
+ { name: `Your own login \u2014 opaque token ${import_chalk29.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
3337
3560
  { name: "\u2190 Back", value: null }
3338
3561
  ]
3339
3562
  }]);
@@ -3357,8 +3580,8 @@ function safeJson(s) {
3357
3580
  async function emailsMenu(base, emails) {
3358
3581
  const { default: inquirer3 } = await import("inquirer");
3359
3582
  console.log();
3360
- if (!emails.length) console.log(import_chalk28.default.dim(" No consumer-admin emails."));
3361
- for (const e of emails) console.log(` ${import_chalk28.default.bold(e.email ?? e)} ${import_chalk28.default.dim(e.status ?? "")}`);
3583
+ if (!emails.length) console.log(import_chalk29.default.dim(" No consumer-admin emails."));
3584
+ for (const e of emails) console.log(` ${import_chalk29.default.bold(e.email ?? e)} ${import_chalk29.default.dim(e.status ?? "")}`);
3362
3585
  const { act } = await inquirer3.prompt([{
3363
3586
  type: "list",
3364
3587
  name: "act",
@@ -3373,7 +3596,7 @@ async function emailsMenu(base, emails) {
3373
3596
  if (act === "add") {
3374
3597
  const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
3375
3598
  await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
3376
- console.log(import_chalk28.default.green(` ${email} added.`));
3599
+ console.log(import_chalk29.default.green(` ${email} added.`));
3377
3600
  } else {
3378
3601
  const { e } = await inquirer3.prompt([{
3379
3602
  type: "list",
@@ -3383,7 +3606,7 @@ async function emailsMenu(base, emails) {
3383
3606
  }]);
3384
3607
  if (!e) return;
3385
3608
  await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
3386
- console.log(import_chalk28.default.green(` ${e} removed.`));
3609
+ console.log(import_chalk29.default.green(` ${e} removed.`));
3387
3610
  }
3388
3611
  }
3389
3612
  async function addIssuer(base) {
@@ -3404,19 +3627,19 @@ async function addIssuer(base) {
3404
3627
  body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
3405
3628
  summary: `Add external issuer ${a.iss.trim()}`
3406
3629
  });
3407
- console.log(import_chalk28.default.green(" JWT login method saved."));
3630
+ console.log(import_chalk29.default.green(" JWT login method saved."));
3408
3631
  }
3409
3632
  async function issuerHome(base, issuer) {
3410
3633
  const { default: inquirer3 } = await import("inquirer");
3411
3634
  console.log(`
3412
- ${import_chalk28.default.bold(issuer.iss)} ${import_chalk28.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
3635
+ ${import_chalk29.default.bold(issuer.iss)} ${import_chalk29.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
3413
3636
  const { act } = await inquirer3.prompt([{
3414
3637
  type: "list",
3415
3638
  name: "act",
3416
3639
  message: "This JWT login method:",
3417
3640
  choices: [
3418
3641
  { name: "Replace it (re-enter details)", value: "edit" },
3419
- { name: import_chalk28.default.red("Delete it"), value: "rm" },
3642
+ { name: import_chalk29.default.red("Delete it"), value: "rm" },
3420
3643
  { name: "\u2190 Back", value: "back" }
3421
3644
  ]
3422
3645
  }]);
@@ -3432,7 +3655,7 @@ async function issuerHome(base, issuer) {
3432
3655
  path: `${base}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
3433
3656
  summary: `Delete issuer ${issuer.iss}`
3434
3657
  });
3435
- console.log(import_chalk28.default.green(" Deleted."));
3658
+ console.log(import_chalk29.default.green(" Deleted."));
3436
3659
  }
3437
3660
  async function setOpaque(base, cur) {
3438
3661
  const { default: inquirer3 } = await import("inquirer");
@@ -3441,19 +3664,19 @@ async function setOpaque(base, cur) {
3441
3664
  { type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
3442
3665
  ]);
3443
3666
  await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
3444
- console.log(import_chalk28.default.green(" Opaque login method set."));
3667
+ console.log(import_chalk29.default.green(" Opaque login method set."));
3445
3668
  }
3446
3669
  async function opaqueHome(base, cur) {
3447
3670
  const { default: inquirer3 } = await import("inquirer");
3448
3671
  console.log(`
3449
- ${import_chalk28.default.bold(cur.endpoint)} ${import_chalk28.default.dim(cur.method ?? "GET")}`);
3672
+ ${import_chalk29.default.bold(cur.endpoint)} ${import_chalk29.default.dim(cur.method ?? "GET")}`);
3450
3673
  const { act } = await inquirer3.prompt([{
3451
3674
  type: "list",
3452
3675
  name: "act",
3453
3676
  message: "This opaque login method:",
3454
3677
  choices: [
3455
3678
  { name: "Replace it", value: "edit" },
3456
- { name: import_chalk28.default.red("Delete it"), value: "rm" },
3679
+ { name: import_chalk29.default.red("Delete it"), value: "rm" },
3457
3680
  { name: "\u2190 Back", value: "back" }
3458
3681
  ]
3459
3682
  }]);
@@ -3463,14 +3686,14 @@ async function opaqueHome(base, cur) {
3463
3686
  return;
3464
3687
  }
3465
3688
  await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
3466
- console.log(import_chalk28.default.green(" Deleted."));
3689
+ console.log(import_chalk29.default.green(" Deleted."));
3467
3690
  }
3468
3691
  async function clientHome(base, summary) {
3469
3692
  const { default: inquirer3 } = await import("inquirer");
3470
3693
  const id = summary.clientId ?? summary.client_id;
3471
3694
  const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
3472
3695
  for (; ; ) {
3473
- const spinner = (0, import_ora12.default)("Reading app client...").start();
3696
+ const spinner = (0, import_ora13.default)("Reading app client...").start();
3474
3697
  const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
3475
3698
  spinner.stop();
3476
3699
  const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
@@ -3482,13 +3705,13 @@ async function clientHome(base, summary) {
3482
3705
  message: `${c.name ?? id}:`,
3483
3706
  pageSize: 12,
3484
3707
  choices: [
3485
- { name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk28.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
3486
- { name: `Callback URLs: ${cb.length ? import_chalk28.default.cyan(cb.join(", ")) : import_chalk28.default.dim("(none)")}`, value: "callbacks" },
3487
- { name: `Scopes: ${scopes.length ? import_chalk28.default.cyan(scopes.join(" ")) : import_chalk28.default.dim("(defaults)")}`, value: "scopes" },
3708
+ { name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk29.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
3709
+ { name: `Callback URLs: ${cb.length ? import_chalk29.default.cyan(cb.join(", ")) : import_chalk29.default.dim("(none)")}`, value: "callbacks" },
3710
+ { name: `Scopes: ${scopes.length ? import_chalk29.default.cyan(scopes.join(" ")) : import_chalk29.default.dim("(defaults)")}`, value: "scopes" },
3488
3711
  { name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
3489
3712
  { name: "Reveal client secret", value: "secret" },
3490
3713
  { name: "Rotate client secret", value: "rotate" },
3491
- { name: import_chalk28.default.red("Delete this app client"), value: "delete" },
3714
+ { name: import_chalk29.default.red("Delete this app client"), value: "delete" },
3492
3715
  { name: "\u2190 Back", value: "back" }
3493
3716
  ]
3494
3717
  }]);
@@ -3501,13 +3724,13 @@ async function clientHome(base, summary) {
3501
3724
  case "callbacks": {
3502
3725
  const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
3503
3726
  await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
3504
- console.log(import_chalk28.default.green(" Callbacks updated."));
3727
+ console.log(import_chalk29.default.green(" Callbacks updated."));
3505
3728
  break;
3506
3729
  }
3507
3730
  case "scopes": {
3508
3731
  const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
3509
3732
  await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
3510
- console.log(import_chalk28.default.green(" Scopes updated."));
3733
+ console.log(import_chalk29.default.green(" Scopes updated."));
3511
3734
  break;
3512
3735
  }
3513
3736
  case "expiries": {
@@ -3522,14 +3745,14 @@ async function clientHome(base, summary) {
3522
3745
  body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
3523
3746
  summary: "Update token expiries"
3524
3747
  });
3525
- console.log(import_chalk28.default.green(" Expiries updated."));
3748
+ console.log(import_chalk29.default.green(" Expiries updated."));
3526
3749
  break;
3527
3750
  }
3528
3751
  case "secret": {
3529
3752
  const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
3530
3753
  if (!sure) break;
3531
3754
  const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
3532
- console.log(` ${import_chalk28.default.bold("client_secret")}: ${import_chalk28.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3755
+ console.log(` ${import_chalk29.default.bold("client_secret")}: ${import_chalk29.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3533
3756
  break;
3534
3757
  }
3535
3758
  case "rotate": {
@@ -3537,14 +3760,14 @@ async function clientHome(base, summary) {
3537
3760
  if (!sure) break;
3538
3761
  const fresh = randomSecret();
3539
3762
  await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
3540
- console.log(` New ${import_chalk28.default.bold("client_secret")}: ${import_chalk28.default.green(fresh)} ${import_chalk28.default.dim("(store it now)")}`);
3763
+ console.log(` New ${import_chalk29.default.bold("client_secret")}: ${import_chalk29.default.green(fresh)} ${import_chalk29.default.dim("(store it now)")}`);
3541
3764
  break;
3542
3765
  }
3543
3766
  case "delete": {
3544
3767
  const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Delete app client "${c.name ?? id}"? Consumers logged in through it will lose access.`, default: false }]);
3545
3768
  if (!sure) break;
3546
3769
  await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
3547
- console.log(import_chalk28.default.green(" App client deleted."));
3770
+ console.log(import_chalk29.default.green(" App client deleted."));
3548
3771
  return;
3549
3772
  }
3550
3773
  }
@@ -3565,15 +3788,15 @@ var DEFAULT_SCOPES2 = {
3565
3788
  async function providersMenu(cBase, clientLabel) {
3566
3789
  const { default: inquirer3 } = await import("inquirer");
3567
3790
  for (; ; ) {
3568
- const spinner = (0, import_ora12.default)("Loading providers...").start();
3791
+ const spinner = (0, import_ora13.default)("Loading providers...").start();
3569
3792
  const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
3570
3793
  spinner.stop();
3571
3794
  const providers = Array.isArray(raw) ? raw : [];
3572
3795
  console.log();
3573
3796
  for (const p of providers) {
3574
- console.log(` ${import_chalk28.default.bold(p.type)} ${import_chalk28.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
3797
+ console.log(` ${import_chalk29.default.bold(p.type)} ${import_chalk29.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
3575
3798
  }
3576
- if (!providers.length) console.log(import_chalk28.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
3799
+ if (!providers.length) console.log(import_chalk29.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
3577
3800
  const { act } = await inquirer3.prompt([{
3578
3801
  type: "list",
3579
3802
  name: "act",
@@ -3631,23 +3854,23 @@ async function providersMenu(cBase, clientLabel) {
3631
3854
  body.targetServerToken = routing;
3632
3855
  }
3633
3856
  await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
3634
- console.log(import_chalk28.default.green(` ${type} provider added.`));
3857
+ console.log(import_chalk29.default.green(` ${type} provider added.`));
3635
3858
  } else {
3636
3859
  const { p } = await inquirer3.prompt([{
3637
3860
  type: "list",
3638
3861
  name: "p",
3639
3862
  message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
3640
- choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk28.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
3863
+ choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk29.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
3641
3864
  }]);
3642
3865
  if (!p) continue;
3643
3866
  if (act === "rm") {
3644
3867
  const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Remove the ${p.type} provider? Consumers using it can no longer sign in.`, default: false }]);
3645
3868
  if (!sure) continue;
3646
3869
  await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
3647
- console.log(import_chalk28.default.green(` ${p.type} removed.`));
3870
+ console.log(import_chalk29.default.green(` ${p.type} removed.`));
3648
3871
  } else {
3649
3872
  const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
3650
- console.log(` ${import_chalk28.default.bold("client_secret")}: ${import_chalk28.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3873
+ console.log(` ${import_chalk29.default.bold("client_secret")}: ${import_chalk29.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
3651
3874
  }
3652
3875
  }
3653
3876
  }
@@ -3655,9 +3878,11 @@ async function providersMenu(cBase, clientLabel) {
3655
3878
 
3656
3879
  // src/commands/spec.ts
3657
3880
  var fs7 = __toESM(require("fs"));
3658
- var import_chalk29 = __toESM(require("chalk"));
3659
- var import_ora13 = __toESM(require("ora"));
3881
+ var import_chalk30 = __toESM(require("chalk"));
3882
+ var import_ora14 = __toESM(require("ora"));
3660
3883
  init_admin();
3884
+ init_api();
3885
+ init_resolve();
3661
3886
  async function runSpecGet(project, opts) {
3662
3887
  const { teamId } = await resolveTeam(opts.team);
3663
3888
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
@@ -3670,19 +3895,19 @@ async function runSpecGet(project, opts) {
3670
3895
  }
3671
3896
  async function runSpecSet(project, opts) {
3672
3897
  if (!opts.file) {
3673
- console.error(import_chalk29.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
3898
+ console.error(import_chalk30.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
3674
3899
  process.exit(1);
3675
3900
  }
3676
3901
  let specContent;
3677
3902
  try {
3678
3903
  specContent = fs7.readFileSync(opts.file, "utf-8");
3679
3904
  } catch {
3680
- console.error(import_chalk29.default.red(`Cannot read file: ${opts.file}`));
3905
+ console.error(import_chalk30.default.red(`Cannot read file: ${opts.file}`));
3681
3906
  process.exit(1);
3682
3907
  }
3683
3908
  const { teamId } = await resolveTeam(opts.team);
3684
3909
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
3685
- const spinner = (0, import_ora13.default)("Uploading spec...").start();
3910
+ const spinner = (0, import_ora14.default)("Uploading spec...").start();
3686
3911
  try {
3687
3912
  const out = await admin({
3688
3913
  method: "POST",
@@ -3690,24 +3915,91 @@ async function runSpecSet(project, opts) {
3690
3915
  body: { specContent },
3691
3916
  summary: `Set OpenAPI spec for ${proj2.projectName} v${proj2.apiVersion} from ${opts.file}`
3692
3917
  });
3693
- spinner.succeed(`Spec updated for ${proj2.projectName} v${proj2.apiVersion}.`);
3918
+ const report = out?.report;
3919
+ if (report?.reused || report && !report.changed) {
3920
+ spinner.succeed(`Spec unchanged for ${proj2.projectName} v${proj2.apiVersion} (identical content).`);
3921
+ } else {
3922
+ spinner.succeed(`Spec updated for ${proj2.projectName} v${proj2.apiVersion}${report?.summary ? `: ${report.summary}` : "."}`);
3923
+ }
3924
+ if (report) printSpecChangeReport(report, proj2.projectName);
3694
3925
  if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
3695
3926
  } catch (err) {
3696
3927
  spinner.fail("Spec update failed.");
3697
3928
  throw err;
3698
3929
  }
3699
3930
  }
3931
+ function printSpecChangeReport(report, projectName) {
3932
+ const r = report.routes;
3933
+ if (r) {
3934
+ for (const a of r.added) console.log(import_chalk30.default.green(` + ${a}`));
3935
+ for (const d of r.removed) console.log(import_chalk30.default.red(` \u2212 ${d}`));
3936
+ for (const rn of r.renamed ?? []) console.log(import_chalk30.default.yellow(` ~ ${rn.from} \u2192 ${rn.to}`));
3937
+ for (const m of r.modified ?? []) console.log(import_chalk30.default.dim(` \xB1 ${m} (params/schema changed)`));
3938
+ }
3939
+ if (report.applied?.mcp_regenerated) console.log(import_chalk30.default.dim(" MCP catalogue regenerated."));
3940
+ if (report.applied?.candidates_cleared) console.log(import_chalk30.default.dim(` ${report.applied.candidates_cleared} route candidate(s) now documented \u2014 cleared.`));
3941
+ for (const item of report.attention ?? []) {
3942
+ console.log(import_chalk30.default.yellow(` \u26A0 ${item.detail}`));
3943
+ if (item.suggestion?.carry_from && item.suggestion.carry_to) {
3944
+ console.log(import_chalk30.default.dim(` Fix: apiblaze spec carry ${projectName} --method ${item.suggestion.method} --from "${item.suggestion.carry_from}" --to "${item.suggestion.carry_to}"`));
3945
+ }
3946
+ }
3947
+ }
3948
+ async function runSpecCarry(project, opts) {
3949
+ if (!opts.method || !opts.from || !opts.to) {
3950
+ console.error(import_chalk30.default.red("--method, --from and --to are required."));
3951
+ process.exit(1);
3952
+ }
3953
+ const { teamId } = await resolveTeam(opts.team);
3954
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
3955
+ const spinner = (0, import_ora14.default)(`Carrying rules ${opts.from} \u2192 ${opts.to}...`).start();
3956
+ const res = await agentCall(`/projects/${proj2.projectId}/${proj2.apiVersion}/policies/route/carry`, "POST", {
3957
+ method: opts.method.toUpperCase(),
3958
+ from_resource: opts.from,
3959
+ to_resource: opts.to,
3960
+ ...opts.deleteSource ? { delete_source: true } : {}
3961
+ });
3962
+ if (res.status >= 200 && res.status < 300) {
3963
+ spinner.succeed(`Rules carried to ${opts.method.toUpperCase()} ${opts.to}${opts.deleteSource ? " (old rules deleted)" : " \u2014 old rules kept until you delete them"}.`);
3964
+ if (opts.json) console.log(JSON.stringify(res.data ?? { ok: true }));
3965
+ } else {
3966
+ spinner.fail(`Carry failed (${res.status}): ${res.data?.error ?? "unknown error"}`);
3967
+ process.exit(1);
3968
+ }
3969
+ }
3970
+ async function runSpecDeleteRule(project, opts) {
3971
+ if (!opts.method || !opts.route) {
3972
+ console.error(import_chalk30.default.red("--method and --route are required."));
3973
+ process.exit(1);
3974
+ }
3975
+ const { teamId } = await resolveTeam(opts.team);
3976
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
3977
+ const encoded = opts.route.replace(/\{/g, "%7B").replace(/\}/g, "%7D");
3978
+ const spinner = (0, import_ora14.default)(`Deleting rules for ${opts.method.toUpperCase()} ${opts.route}...`).start();
3979
+ const res = await agentCall(
3980
+ `/projects/${proj2.projectId}/${proj2.apiVersion}/policies/route/${opts.method.toUpperCase()}${encoded.startsWith("/") ? encoded : `/${encoded}`}`,
3981
+ "DELETE"
3982
+ );
3983
+ if (res.status >= 200 && res.status < 300 || res.status === 404) {
3984
+ spinner.succeed(`Rules deleted for ${opts.method.toUpperCase()} ${opts.route}.`);
3985
+ } else {
3986
+ spinner.fail(`Delete failed (${res.status}): ${res.data?.error ?? "unknown error"}`);
3987
+ process.exit(1);
3988
+ }
3989
+ }
3700
3990
 
3701
3991
  // src/commands/agent.ts
3702
- var import_chalk31 = __toESM(require("chalk"));
3703
- var import_ora14 = __toESM(require("ora"));
3992
+ var import_chalk32 = __toESM(require("chalk"));
3993
+ var import_ora15 = __toESM(require("ora"));
3704
3994
  init_auth();
3705
3995
  init_admin();
3996
+ init_resolve();
3706
3997
 
3707
3998
  // src/lib/tools.ts
3708
- var import_chalk30 = __toESM(require("chalk"));
3999
+ var import_chalk31 = __toESM(require("chalk"));
3709
4000
  init_admin();
3710
4001
  init_api();
4002
+ init_resolve();
3711
4003
  async function proj(teamId, name, version2) {
3712
4004
  return resolveProject(teamId, name, version2);
3713
4005
  }
@@ -3724,15 +4016,15 @@ var TOOLS = [
3724
4016
  const key = keys.dev ?? Object.values(keys)[0];
3725
4017
  const url = `https://${a.name}.abz.run/${version2}/dev`;
3726
4018
  const tryIt = buildTryItCurl(url, auth, key);
3727
- const lines = [` ${import_chalk30.default.dim("Proxy URL:")} ${import_chalk30.default.bold(url)}`];
3728
- if (res.devPortal) lines.push(` ${import_chalk30.default.dim("Dev portal:")} ${res.devPortal}`);
4019
+ const lines = [` ${import_chalk31.default.dim("Proxy URL:")} ${import_chalk31.default.bold(url)}`];
4020
+ if (res.devPortal) lines.push(` ${import_chalk31.default.dim("Dev portal:")} ${res.devPortal}`);
3729
4021
  const envs = Object.keys(keys);
3730
4022
  if (envs.length) {
3731
- lines.push("", ` ${import_chalk30.default.bold("API keys")} ${import_chalk30.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
4023
+ lines.push("", ` ${import_chalk31.default.bold("API keys")} ${import_chalk31.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
3732
4024
  const w = Math.max(...envs.map((e) => e.length));
3733
- for (const env of envs) lines.push(` ${import_chalk30.default.cyan(env.padEnd(w))} ${import_chalk30.default.green(keys[env])}`);
4025
+ for (const env of envs) lines.push(` ${import_chalk31.default.cyan(env.padEnd(w))} ${import_chalk31.default.green(keys[env])}`);
3734
4026
  }
3735
- if (tryIt) lines.push("", ` ${import_chalk30.default.dim("Try it:")}`, ` ${import_chalk30.default.cyan(tryIt)}`);
4027
+ if (tryIt) lines.push("", ` ${import_chalk31.default.dim("Try it:")}`, ` ${import_chalk31.default.cyan(tryIt)}`);
3736
4028
  return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
3737
4029
  }
3738
4030
  },
@@ -3888,23 +4180,23 @@ function truncate(value, max = 1500) {
3888
4180
  }
3889
4181
  function printCost(resp) {
3890
4182
  const line = formatBilling(resp.billing);
3891
- if (line) console.log(import_chalk31.default.magenta(` ${line}`) + import_chalk31.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
4183
+ if (line) console.log(import_chalk32.default.magenta(` ${line}`) + import_chalk32.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
3892
4184
  }
3893
4185
  async function runAgent(opts) {
3894
4186
  requireAuth();
3895
4187
  const { teamId, teamName } = await resolveTeam(opts.team);
3896
4188
  const { default: inquirer3 } = await import("inquirer");
3897
- console.log(import_chalk31.default.bold("APIblaze agent") + import_chalk31.default.dim(` \xB7 team ${teamName ?? teamId}`));
3898
- console.log(import_chalk31.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
4189
+ console.log(import_chalk32.default.bold("APIblaze agent") + import_chalk32.default.dim(` \xB7 team ${teamName ?? teamId}`));
4190
+ console.log(import_chalk32.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
3899
4191
  const history = [];
3900
4192
  while (true) {
3901
- const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk31.default.cyan("you") + " \u203A" }]);
4193
+ const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk32.default.cyan("you") + " \u203A" }]);
3902
4194
  const text = (input ?? "").trim();
3903
4195
  if (!text) continue;
3904
4196
  if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
3905
4197
  history.push({ role: "user", content: text });
3906
4198
  for (let step = 0; step < MAX_TOOL_STEPS; step++) {
3907
- const spinner = (0, import_ora14.default)({ text: "thinking...", color: "magenta" }).start();
4199
+ const spinner = (0, import_ora15.default)({ text: "thinking...", color: "magenta" }).start();
3908
4200
  let resp;
3909
4201
  try {
3910
4202
  resp = await callAgent(history, teamId);
@@ -3912,21 +4204,21 @@ async function runAgent(opts) {
3912
4204
  } catch (err) {
3913
4205
  spinner.stop();
3914
4206
  if (err instanceof ApiError && err.status === 402) {
3915
- console.log(import_chalk31.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
4207
+ console.log(import_chalk32.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
3916
4208
  break;
3917
4209
  }
3918
4210
  throw err;
3919
4211
  }
3920
4212
  history.push({ role: "assistant", content: resp.raw });
3921
4213
  printCost(resp);
3922
- if (resp.reply) console.log(import_chalk31.default.green("agent") + " \u203A " + resp.reply);
4214
+ if (resp.reply) console.log(import_chalk32.default.green("agent") + " \u203A " + resp.reply);
3923
4215
  if (!resp.action) break;
3924
4216
  const tool = findTool(resp.action.tool);
3925
4217
  if (!tool) {
3926
4218
  history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
3927
4219
  continue;
3928
4220
  }
3929
- const runSpinner = (0, import_ora14.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
4221
+ const runSpinner = (0, import_ora15.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
3930
4222
  try {
3931
4223
  const result = await tool.run(resp.action.args, { teamId });
3932
4224
  runSpinner.succeed(`${tool.name} \u2713`);
@@ -3944,11 +4236,11 @@ async function runAgent(opts) {
3944
4236
  }
3945
4237
  renderTrace();
3946
4238
  if (step === MAX_TOOL_STEPS - 1) {
3947
- console.log(import_chalk31.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
4239
+ console.log(import_chalk32.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
3948
4240
  }
3949
4241
  }
3950
4242
  }
3951
- console.log(import_chalk31.default.dim("\nBye."));
4243
+ console.log(import_chalk32.default.dim("\nBye."));
3952
4244
  }
3953
4245
 
3954
4246
  // src/commands/config-browse.ts
@@ -4187,11 +4479,11 @@ function dig(blob, dotted) {
4187
4479
  }
4188
4480
  var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
4189
4481
  function show(v) {
4190
- if (v === void 0) return import_chalk32.default.dim("(unset)");
4191
- if (v === null) return import_chalk32.default.dim("null");
4192
- if (typeof v === "object") return import_chalk32.default.cyan(JSON.stringify(v));
4193
- if (typeof v === "boolean") return v ? import_chalk32.default.green("on") : import_chalk32.default.red("off");
4194
- return import_chalk32.default.cyan(String(v));
4482
+ if (v === void 0) return import_chalk33.default.dim("(unset)");
4483
+ if (v === null) return import_chalk33.default.dim("null");
4484
+ if (typeof v === "object") return import_chalk33.default.cyan(JSON.stringify(v));
4485
+ if (typeof v === "boolean") return v ? import_chalk33.default.green("on") : import_chalk33.default.red("off");
4486
+ return import_chalk33.default.cyan(String(v));
4195
4487
  }
4196
4488
  function parseValue(raw) {
4197
4489
  if (raw === "true") return true;
@@ -4218,7 +4510,7 @@ async function fetchConfigBlob(proj2) {
4218
4510
  }
4219
4511
  async function patchSetting(proj2, s, value, cfg) {
4220
4512
  const body = s.toPatch(value, cfg);
4221
- const spinner = (0, import_ora15.default)(`Set ${s.key}...`).start();
4513
+ const spinner = (0, import_ora16.default)(`Set ${s.key}...`).start();
4222
4514
  try {
4223
4515
  await admin({
4224
4516
  method: "PATCH",
@@ -4233,10 +4525,10 @@ async function patchSetting(proj2, s, value, cfg) {
4233
4525
  }
4234
4526
  }
4235
4527
  var loginFirst = (what) => {
4236
- console.log(import_chalk32.default.yellow(`
4528
+ console.log(import_chalk33.default.yellow(`
4237
4529
  Log in first to ${what}.`));
4238
- console.log(import_chalk32.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
4239
- console.log(import_chalk32.default.dim(" anonymously and want to bring it into your account.\n"));
4530
+ console.log(import_chalk33.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
4531
+ console.log(import_chalk33.default.dim(" anonymously and want to bring it into your account.\n"));
4240
4532
  };
4241
4533
  async function runConfig(project, key, value, opts) {
4242
4534
  const creds = loadCredentials();
@@ -4253,9 +4545,9 @@ async function runConfig(project, key, value, opts) {
4253
4545
  }
4254
4546
  const setting = SETTINGS.find((s) => s.key === key);
4255
4547
  if (!setting) {
4256
- console.error(import_chalk32.default.red(`Unknown setting "${key}".`));
4257
- console.error(import_chalk32.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
4258
- console.error(import_chalk32.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
4548
+ console.error(import_chalk33.default.red(`Unknown setting "${key}".`));
4549
+ console.error(import_chalk33.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
4550
+ console.error(import_chalk33.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
4259
4551
  process.exit(1);
4260
4552
  }
4261
4553
  if (value === void 0) {
@@ -4270,7 +4562,7 @@ async function pickProject(teamId) {
4270
4562
  const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
4271
4563
  const projects = await getProjects2(teamId).catch(() => []);
4272
4564
  if (!projects.length) {
4273
- console.error(import_chalk32.default.red("No projects in this team. Create one: `npx apiblaze create`."));
4565
+ console.error(import_chalk33.default.red("No projects in this team. Create one: `npx apiblaze create`."));
4274
4566
  process.exit(1);
4275
4567
  }
4276
4568
  const { default: inquirer3 } = await import("inquirer");
@@ -4278,7 +4570,7 @@ async function pickProject(teamId) {
4278
4570
  type: "list",
4279
4571
  name: "picked",
4280
4572
  message: "Which project?",
4281
- choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk32.default.dim("v" + p.apiVersion)}`, value: p }))
4573
+ choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk33.default.dim("v" + p.apiVersion)}`, value: p }))
4282
4574
  }]);
4283
4575
  return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
4284
4576
  }
@@ -4289,25 +4581,25 @@ function printAll(proj2, cfg, json) {
4289
4581
  console.log(JSON.stringify(out, null, 2));
4290
4582
  return;
4291
4583
  }
4292
- console.log(import_chalk32.default.bold(`
4584
+ console.log(import_chalk33.default.bold(`
4293
4585
  ${proj2.projectName} v${proj2.apiVersion} \u2014 settings
4294
4586
  `));
4295
4587
  for (const group2 of SETTING_GROUPS) {
4296
- console.log(import_chalk32.default.bold(group2));
4588
+ console.log(import_chalk33.default.bold(group2));
4297
4589
  for (const s of SETTINGS.filter((x) => x.group === group2)) {
4298
- console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk32.default.dim(s.desc)}`);
4590
+ console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk33.default.dim(s.desc)}`);
4299
4591
  }
4300
4592
  console.log();
4301
4593
  }
4302
- console.log(import_chalk32.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
4594
+ console.log(import_chalk33.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
4303
4595
  }
4304
4596
  async function discoveryMenu(project) {
4305
4597
  const { default: inquirer3 } = await import("inquirer");
4306
- console.log(import_chalk32.default.bold(`
4598
+ console.log(import_chalk33.default.bold(`
4307
4599
  APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
4308
4600
  `));
4309
- console.log(import_chalk32.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
4310
- console.log(import_chalk32.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
4601
+ console.log(import_chalk33.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
4602
+ console.log(import_chalk33.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
4311
4603
  for (; ; ) {
4312
4604
  const { pick: pick2 } = await inquirer3.prompt([{
4313
4605
  type: "list",
@@ -4315,13 +4607,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
4315
4607
  message: "Explore:",
4316
4608
  pageSize: 20,
4317
4609
  choices: [
4318
- new inquirer3.Separator(import_chalk32.default.bold("\u2014 Settings \u2014")),
4610
+ new inquirer3.Separator(import_chalk33.default.bold("\u2014 Settings \u2014")),
4319
4611
  ...SETTING_GROUPS.map((g) => ({
4320
- name: `${g} ${import_chalk32.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
4612
+ name: `${g} ${import_chalk33.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
4321
4613
  value: { kind: "settings", g }
4322
4614
  })),
4323
- new inquirer3.Separator(import_chalk32.default.bold("\u2014 Features \u2014")),
4324
- ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk32.default.dim(f.desc)}`, value: { kind: "feature", f } })),
4615
+ new inquirer3.Separator(import_chalk33.default.bold("\u2014 Features \u2014")),
4616
+ ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk33.default.dim(f.desc)}`, value: { kind: "feature", f } })),
4325
4617
  new inquirer3.Separator(),
4326
4618
  { name: "Exit", value: { kind: "exit" } }
4327
4619
  ]
@@ -4330,24 +4622,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
4330
4622
  if (pick2.kind === "settings") {
4331
4623
  console.log();
4332
4624
  for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
4333
- console.log(` ${import_chalk32.default.bold(s.label.padEnd(28))} ${import_chalk32.default.dim(s.desc)}`);
4334
- console.log(` ${import_chalk32.default.dim(" key: " + s.key)}`);
4625
+ console.log(` ${import_chalk33.default.bold(s.label.padEnd(28))} ${import_chalk33.default.dim(s.desc)}`);
4626
+ console.log(` ${import_chalk33.default.dim(" key: " + s.key)}`);
4335
4627
  }
4336
4628
  loginFirst("view or change these settings");
4337
4629
  } else {
4338
4630
  const f = pick2.f;
4339
4631
  console.log(`
4340
- ${import_chalk32.default.bold(f.label)} \u2014 ${f.desc}`);
4632
+ ${import_chalk33.default.bold(f.label)} \u2014 ${f.desc}`);
4341
4633
  loginFirst(`use ${f.label.toLowerCase()}`);
4342
4634
  }
4343
4635
  }
4344
4636
  }
4345
4637
  async function navigator(proj2, cfg, opts) {
4346
4638
  const { default: inquirer3 } = await import("inquirer");
4347
- console.log(import_chalk32.default.bold(`
4639
+ console.log(import_chalk33.default.bold(`
4348
4640
  ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
4349
4641
  `));
4350
- console.log(import_chalk32.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
4642
+ console.log(import_chalk33.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
4351
4643
  let blob = cfg;
4352
4644
  for (; ; ) {
4353
4645
  const { pick: pick2 } = await inquirer3.prompt([{
@@ -4356,10 +4648,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
4356
4648
  message: "Where to?",
4357
4649
  pageSize: 20,
4358
4650
  choices: [
4359
- new inquirer3.Separator(import_chalk32.default.bold("\u2014 Settings \u2014")),
4651
+ new inquirer3.Separator(import_chalk33.default.bold("\u2014 Settings \u2014")),
4360
4652
  ...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
4361
- new inquirer3.Separator(import_chalk32.default.bold("\u2014 Features \u2014")),
4362
- ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk32.default.dim(f.desc)}`, value: { kind: f.go } })),
4653
+ new inquirer3.Separator(import_chalk33.default.bold("\u2014 Features \u2014")),
4654
+ ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk33.default.dim(f.desc)}`, value: { kind: f.go } })),
4363
4655
  new inquirer3.Separator(),
4364
4656
  { name: "Show all settings", value: { kind: "list" } },
4365
4657
  { name: "Exit", value: { kind: "exit" } }
@@ -4399,7 +4691,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
4399
4691
  }
4400
4692
  }
4401
4693
  } catch (err) {
4402
- console.error(import_chalk32.default.red(` ${err instanceof Error ? err.message : String(err)}`));
4694
+ console.error(import_chalk33.default.red(` ${err instanceof Error ? err.message : String(err)}`));
4403
4695
  }
4404
4696
  }
4405
4697
  }
@@ -4413,7 +4705,7 @@ async function settingsGroup(proj2, cfg, group2) {
4413
4705
  message: group2 + ":",
4414
4706
  pageSize: 16,
4415
4707
  choices: [
4416
- ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk32.default.dim(s2.desc)}`, value: s2 })),
4708
+ ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk33.default.dim(s2.desc)}`, value: s2 })),
4417
4709
  new inquirer3.Separator(),
4418
4710
  { name: "\u2190 Back", value: null }
4419
4711
  ]
@@ -4431,7 +4723,7 @@ async function settingsGroup(proj2, cfg, group2) {
4431
4723
  } else if (s.type === "number") {
4432
4724
  const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
4433
4725
  if (v === "" || Number.isNaN(Number(v))) {
4434
- console.log(import_chalk32.default.yellow(" Not a number \u2014 unchanged."));
4726
+ console.log(import_chalk33.default.yellow(" Not a number \u2014 unchanged."));
4435
4727
  continue;
4436
4728
  }
4437
4729
  value = Number(v);
@@ -4512,7 +4804,7 @@ async function buildCondition(phase) {
4512
4804
  const items = [];
4513
4805
  for (; ; ) {
4514
4806
  const a = await inquirer3.prompt([
4515
- { type: "input", name: "source", message: `Condition field ${import_chalk32.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
4807
+ { type: "input", name: "source", message: `Condition field ${import_chalk33.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
4516
4808
  { type: "list", name: "operator", message: "Operator:", choices: [
4517
4809
  "eq",
4518
4810
  "neq",
@@ -4543,7 +4835,7 @@ async function buildCondition(phase) {
4543
4835
  function showCondition(cond) {
4544
4836
  if (!Array.isArray(cond) || !cond.length) return "";
4545
4837
  const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
4546
- return import_chalk32.default.dim(` when ${s}`);
4838
+ return import_chalk33.default.dim(` when ${s}`);
4547
4839
  }
4548
4840
  async function transformsMenu(proj2) {
4549
4841
  const { default: inquirer3 } = await import("inquirer");
@@ -4552,12 +4844,12 @@ async function transformsMenu(proj2) {
4552
4844
  const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
4553
4845
  const rules = out?.rules ?? [];
4554
4846
  console.log();
4555
- if (!rules.length) console.log(import_chalk32.default.dim(" No transform rules yet."));
4847
+ if (!rules.length) console.log(import_chalk33.default.dim(" No transform rules yet."));
4556
4848
  for (const r of rules) {
4557
4849
  const a = r.action ?? {};
4558
4850
  const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
4559
- 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_chalk32.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
4560
- console.log(` ${r.enabled ? import_chalk32.default.green("\u25CF") : import_chalk32.default.dim("\u25CB")} ${import_chalk32.default.bold(r.name)} ${import_chalk32.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
4851
+ 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_chalk33.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
4852
+ console.log(` ${r.enabled ? import_chalk33.default.green("\u25CF") : import_chalk33.default.dim("\u25CB")} ${import_chalk33.default.bold(r.name)} ${import_chalk33.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
4561
4853
  }
4562
4854
  const { act } = await inquirer3.prompt([{
4563
4855
  type: "list",
@@ -4569,7 +4861,7 @@ async function transformsMenu(proj2) {
4569
4861
  { name: "Enable/disable a rule", value: "toggle" },
4570
4862
  { name: "Delete a rule", value: "delete" }
4571
4863
  ] : [],
4572
- { name: import_chalk32.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
4864
+ { name: import_chalk33.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
4573
4865
  { name: "\u2190 Back", value: "back" }
4574
4866
  ]
4575
4867
  }]);
@@ -4582,11 +4874,11 @@ async function transformsMenu(proj2) {
4582
4874
  }]);
4583
4875
  const body = parseValue(raw);
4584
4876
  if (!body || typeof body !== "object" || Array.isArray(body)) {
4585
- console.log(import_chalk32.default.yellow(" Not a JSON object \u2014 skipped."));
4877
+ console.log(import_chalk33.default.yellow(" Not a JSON object \u2014 skipped."));
4586
4878
  continue;
4587
4879
  }
4588
4880
  await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
4589
- console.log(import_chalk32.default.green(" Rule created."));
4881
+ console.log(import_chalk33.default.green(" Rule created."));
4590
4882
  continue;
4591
4883
  }
4592
4884
  if (act === "add") {
@@ -4602,7 +4894,7 @@ async function transformsMenu(proj2) {
4602
4894
  { name: "Remove a field", value: "remove" }
4603
4895
  ] }
4604
4896
  ]);
4605
- const fieldHint = import_chalk32.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
4897
+ const fieldHint = import_chalk33.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
4606
4898
  let action2;
4607
4899
  if (ans.kind === "hardcode") {
4608
4900
  const a = await inquirer3.prompt([
@@ -4631,7 +4923,7 @@ async function transformsMenu(proj2) {
4631
4923
  };
4632
4924
  }
4633
4925
  const condition = await buildCondition(ans.phase);
4634
- const spinner = (0, import_ora15.default)("Creating rule...").start();
4926
+ const spinner = (0, import_ora16.default)("Creating rule...").start();
4635
4927
  try {
4636
4928
  await admin({
4637
4929
  method: "POST",
@@ -4649,16 +4941,16 @@ async function transformsMenu(proj2) {
4649
4941
  type: "list",
4650
4942
  name: "rule",
4651
4943
  message: act === "toggle" ? "Which rule?" : "Delete which rule?",
4652
- choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk32.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
4944
+ choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk33.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
4653
4945
  }]);
4654
4946
  if (!rule) continue;
4655
4947
  if (act === "toggle") {
4656
4948
  const flipped = { ...rule, enabled: rule.enabled === false };
4657
4949
  await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
4658
- console.log(import_chalk32.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
4950
+ console.log(import_chalk33.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
4659
4951
  } else {
4660
4952
  await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
4661
- console.log(import_chalk32.default.green(` ${rule.name} deleted.`));
4953
+ console.log(import_chalk33.default.green(` ${rule.name} deleted.`));
4662
4954
  }
4663
4955
  }
4664
4956
  }
@@ -4670,9 +4962,9 @@ async function mappingsMenu(proj2) {
4670
4962
  const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
4671
4963
  const tables = out?.mappings ?? out?.tables ?? [];
4672
4964
  console.log();
4673
- if (!tables.length) console.log(import_chalk32.default.dim(" No mapping tables yet."));
4965
+ if (!tables.length) console.log(import_chalk33.default.dim(" No mapping tables yet."));
4674
4966
  for (const t of tables) {
4675
- console.log(` ${import_chalk32.default.bold(t.name)} ${import_chalk32.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
4967
+ console.log(` ${import_chalk33.default.bold(t.name)} ${import_chalk33.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
4676
4968
  }
4677
4969
  const { act } = await inquirer3.prompt([{
4678
4970
  type: "list",
@@ -4692,11 +4984,11 @@ async function mappingsMenu(proj2) {
4692
4984
  ]);
4693
4985
  const entries2 = parseValue(a.entries);
4694
4986
  if (!Array.isArray(entries2)) {
4695
- console.log(import_chalk32.default.yellow(" Entries must be a JSON array \u2014 not created."));
4987
+ console.log(import_chalk33.default.yellow(" Entries must be a JSON array \u2014 not created."));
4696
4988
  continue;
4697
4989
  }
4698
4990
  await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
4699
- console.log(import_chalk32.default.green(` Table "${a.name}" created.`));
4991
+ console.log(import_chalk33.default.green(` Table "${a.name}" created.`));
4700
4992
  } else {
4701
4993
  const { table } = await inquirer3.prompt([{
4702
4994
  type: "list",
@@ -4706,7 +4998,7 @@ async function mappingsMenu(proj2) {
4706
4998
  }]);
4707
4999
  if (!table) continue;
4708
5000
  await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
4709
- console.log(import_chalk32.default.green(` ${table.name} deleted.`));
5001
+ console.log(import_chalk33.default.green(` ${table.name} deleted.`));
4710
5002
  }
4711
5003
  }
4712
5004
  }
@@ -4717,14 +5009,14 @@ async function tenantsMenu(proj2, opts) {
4717
5009
  const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
4718
5010
  const tenants = out?.tenants ?? [];
4719
5011
  console.log();
4720
- if (!tenants.length) console.log(import_chalk32.default.dim(" No tenants attached (consumers use the default tenant)."));
4721
- for (const t of tenants) console.log(` ${import_chalk32.default.bold(t.tenant_name ?? t.name)} ${import_chalk32.default.dim(t.display_name ?? "")}`);
5012
+ if (!tenants.length) console.log(import_chalk33.default.dim(" No tenants attached (consumers use the default tenant)."));
5013
+ for (const t of tenants) console.log(` ${import_chalk33.default.bold(t.tenant_name ?? t.name)} ${import_chalk33.default.dim(t.display_name ?? "")}`);
4722
5014
  const { act } = await inquirer3.prompt([{
4723
5015
  type: "list",
4724
5016
  name: "act",
4725
5017
  message: "Tenants:",
4726
5018
  choices: [
4727
- { name: `Manage a tenant\u2026 ${import_chalk32.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
5019
+ { name: `Manage a tenant\u2026 ${import_chalk33.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
4728
5020
  { name: "Attach a tenant to this project", value: "attach" },
4729
5021
  ...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
4730
5022
  { name: "\u2190 Back", value: "back" }
@@ -4747,7 +5039,7 @@ async function tenantsMenu(proj2, opts) {
4747
5039
  }]);
4748
5040
  if (!t) continue;
4749
5041
  await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
4750
- console.log(import_chalk32.default.green(` Detached ${t.tenant_name ?? t.name}.`));
5042
+ console.log(import_chalk33.default.green(` Detached ${t.tenant_name ?? t.name}.`));
4751
5043
  }
4752
5044
  }
4753
5045
  }
@@ -4790,7 +5082,7 @@ async function specMenu(proj2, opts) {
4790
5082
  choices: [
4791
5083
  { name: "Print the stored spec", value: "get" },
4792
5084
  { name: "Refresh the spec from its source", value: "refresh" },
4793
- { name: import_chalk32.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
5085
+ { name: import_chalk33.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
4794
5086
  { name: "\u2190 Back", value: "back" }
4795
5087
  ]
4796
5088
  }]);
@@ -4798,7 +5090,7 @@ async function specMenu(proj2, opts) {
4798
5090
  if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
4799
5091
  else if (act === "refresh") {
4800
5092
  await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
4801
- console.log(import_chalk32.default.green(" Spec refresh triggered."));
5093
+ console.log(import_chalk33.default.green(" Spec refresh triggered."));
4802
5094
  } else await runOpenapi(proj2.projectName, proj2.apiVersion);
4803
5095
  }
4804
5096
  async function agentsMenu(proj2, opts) {
@@ -4823,16 +5115,17 @@ async function agentsMenu(proj2, opts) {
4823
5115
  }
4824
5116
 
4825
5117
  // src/commands/group.ts
4826
- var import_chalk33 = __toESM(require("chalk"));
4827
- var import_ora16 = __toESM(require("ora"));
5118
+ var import_chalk34 = __toESM(require("chalk"));
5119
+ var import_ora17 = __toESM(require("ora"));
4828
5120
  init_auth();
4829
5121
  init_admin();
5122
+ init_resolve();
4830
5123
  init_types();
4831
5124
  var DASHBOARD_BASE6 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
4832
5125
  var IAM_BASE = process.env.APIBLAZE_IAM_BASE || "https://iam.apiblaze.com";
4833
5126
  async function resolveTransport(opts) {
4834
- const producer = loadCredentials();
4835
- if (producer) {
5127
+ const producer2 = loadCredentials();
5128
+ if (producer2) {
4836
5129
  const { teamId } = await resolveTeam(void 0);
4837
5130
  let tenant2 = (opts.tenant ?? "").trim().toLowerCase();
4838
5131
  if (!tenant2) {
@@ -4913,11 +5206,11 @@ async function runGroupList(opts) {
4913
5206
  const t = await resolveTransport(opts);
4914
5207
  const groups = await listGroups(t);
4915
5208
  if (opts.json) return void console.log(JSON.stringify(groups));
4916
- if (!groups.length) return void console.log(import_chalk33.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
4917
- console.log(import_chalk33.default.dim(`Tenant ${t.tenant}:`));
5209
+ if (!groups.length) return void console.log(import_chalk34.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
5210
+ console.log(import_chalk34.default.dim(`Tenant ${t.tenant}:`));
4918
5211
  for (const g of groups) {
4919
- const sealed = g.name === "apiblaze_admins" ? import_chalk33.default.dim(" (admins \u2014 managed by allowlist)") : "";
4920
- console.log(` ${import_chalk33.default.bold(g.name)} ${import_chalk33.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
5212
+ const sealed = g.name === "apiblaze_admins" ? import_chalk34.default.dim(" (admins \u2014 managed by allowlist)") : "";
5213
+ console.log(` ${import_chalk34.default.bold(g.name)} ${import_chalk34.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
4921
5214
  }
4922
5215
  }
4923
5216
  async function runGroupCreate(name, opts) {
@@ -4942,10 +5235,10 @@ async function runGroupCreate(name, opts) {
4942
5235
  }
4943
5236
  }
4944
5237
  }
4945
- const spinner = (0, import_ora16.default)(`Creating group ${name}...`).start();
5238
+ const spinner = (0, import_ora17.default)(`Creating group ${name}...`).start();
4946
5239
  try {
4947
5240
  await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
4948
- spinner.succeed(`Group ${import_chalk33.default.bold(name)} created in ${t.tenant}.`);
5241
+ spinner.succeed(`Group ${import_chalk34.default.bold(name)} created in ${t.tenant}.`);
4949
5242
  } catch (e) {
4950
5243
  spinner.fail("Create failed.");
4951
5244
  throw e;
@@ -4954,10 +5247,10 @@ async function runGroupCreate(name, opts) {
4954
5247
  async function runGroupDelete(name, opts) {
4955
5248
  const t = await resolveTransport(opts);
4956
5249
  const g = await groupByName(t, name);
4957
- const spinner = (0, import_ora16.default)(`Deleting group ${name}...`).start();
5250
+ const spinner = (0, import_ora17.default)(`Deleting group ${name}...`).start();
4958
5251
  try {
4959
5252
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
4960
- spinner.succeed(`Group ${import_chalk33.default.bold(name)} deleted.`);
5253
+ spinner.succeed(`Group ${import_chalk34.default.bold(name)} deleted.`);
4961
5254
  } catch (e) {
4962
5255
  spinner.fail("Delete failed.");
4963
5256
  throw e;
@@ -4970,20 +5263,20 @@ async function runGroupMembers(name, opts) {
4970
5263
  const subs = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/subgroups`);
4971
5264
  if (opts.json) return void console.log(JSON.stringify({ ...detail, subgroups: subs?.subgroups ?? [] }));
4972
5265
  const members = detail?.members ?? [];
4973
- console.log(import_chalk33.default.bold(`${g.name}`) + import_chalk33.default.dim(` (${t.tenant})`));
4974
- for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk33.default.cyan("group-admin") : import_chalk33.default.dim("member")}`);
4975
- if (!members.length) console.log(import_chalk33.default.dim(" no members"));
5266
+ console.log(import_chalk34.default.bold(`${g.name}`) + import_chalk34.default.dim(` (${t.tenant})`));
5267
+ for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk34.default.cyan("group-admin") : import_chalk34.default.dim("member")}`);
5268
+ if (!members.length) console.log(import_chalk34.default.dim(" no members"));
4976
5269
  const sg = subs?.subgroups ?? [];
4977
- if (sg.length) console.log(import_chalk33.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
5270
+ if (sg.length) console.log(import_chalk34.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
4978
5271
  }
4979
5272
  async function runGroupAddUser(userRef, groupName, opts) {
4980
5273
  const t = await resolveTransport(opts);
4981
5274
  const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
4982
5275
  const role = opts.role === "admin" ? "admin" : "member";
4983
- const spinner = (0, import_ora16.default)(`Adding ${label2(u)} to ${g.name}...`).start();
5276
+ const spinner = (0, import_ora17.default)(`Adding ${label2(u)} to ${g.name}...`).start();
4984
5277
  try {
4985
5278
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
4986
- spinner.succeed(`${import_chalk33.default.bold(label2(u))} added to ${import_chalk33.default.bold(g.name)} as ${role}.`);
5279
+ spinner.succeed(`${import_chalk34.default.bold(label2(u))} added to ${import_chalk34.default.bold(g.name)} as ${role}.`);
4987
5280
  } catch (e) {
4988
5281
  spinner.fail("Add failed.");
4989
5282
  throw e;
@@ -4992,10 +5285,10 @@ async function runGroupAddUser(userRef, groupName, opts) {
4992
5285
  async function runGroupRemoveUser(userRef, groupName, opts) {
4993
5286
  const t = await resolveTransport(opts);
4994
5287
  const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
4995
- const spinner = (0, import_ora16.default)(`Removing ${label2(u)} from ${g.name}...`).start();
5288
+ const spinner = (0, import_ora17.default)(`Removing ${label2(u)} from ${g.name}...`).start();
4996
5289
  try {
4997
5290
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
4998
- spinner.succeed(`${import_chalk33.default.bold(label2(u))} removed from ${import_chalk33.default.bold(g.name)}.`);
5291
+ spinner.succeed(`${import_chalk34.default.bold(label2(u))} removed from ${import_chalk34.default.bold(g.name)}.`);
4999
5292
  } catch (e) {
5000
5293
  spinner.fail("Remove failed.");
5001
5294
  throw e;
@@ -5004,10 +5297,10 @@ async function runGroupRemoveUser(userRef, groupName, opts) {
5004
5297
  async function runGroupAddGroup(childName, parentName, opts) {
5005
5298
  const t = await resolveTransport(opts);
5006
5299
  const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
5007
- const spinner = (0, import_ora16.default)(`Nesting ${child.name} under ${parent.name}...`).start();
5300
+ const spinner = (0, import_ora17.default)(`Nesting ${child.name} under ${parent.name}...`).start();
5008
5301
  try {
5009
5302
  await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
5010
- spinner.succeed(`${import_chalk33.default.bold(child.name)} is now a subgroup of ${import_chalk33.default.bold(parent.name)}.`);
5303
+ spinner.succeed(`${import_chalk34.default.bold(child.name)} is now a subgroup of ${import_chalk34.default.bold(parent.name)}.`);
5011
5304
  } catch (e) {
5012
5305
  spinner.fail("Nest failed.");
5013
5306
  throw e;
@@ -5016,10 +5309,10 @@ async function runGroupAddGroup(childName, parentName, opts) {
5016
5309
  async function runGroupRemoveGroup(childName, parentName, opts) {
5017
5310
  const t = await resolveTransport(opts);
5018
5311
  const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
5019
- const spinner = (0, import_ora16.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
5312
+ const spinner = (0, import_ora17.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
5020
5313
  try {
5021
5314
  await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
5022
- spinner.succeed(`${import_chalk33.default.bold(child.name)} un-nested from ${import_chalk33.default.bold(parent.name)}.`);
5315
+ spinner.succeed(`${import_chalk34.default.bold(child.name)} un-nested from ${import_chalk34.default.bold(parent.name)}.`);
5023
5316
  } catch (e) {
5024
5317
  spinner.fail("Un-nest failed.");
5025
5318
  throw e;
@@ -5030,20 +5323,20 @@ async function runGroupUsers(opts) {
5030
5323
  const users = await listUsers(t);
5031
5324
  const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
5032
5325
  if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
5033
- console.log(import_chalk33.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
5034
- for (const u of users) console.log(` ${label2(u)} ${import_chalk33.default.dim(u.abz_sub)}`);
5035
- if (!users.length) console.log(import_chalk33.default.dim(" no users yet"));
5326
+ console.log(import_chalk34.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
5327
+ for (const u of users) console.log(` ${label2(u)} ${import_chalk34.default.dim(u.abz_sub)}`);
5328
+ if (!users.length) console.log(import_chalk34.default.dim(" no users yet"));
5036
5329
  const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
5037
5330
  if (fresh.length) {
5038
- console.log(import_chalk33.default.dim("Seen in traffic (not yet users):"));
5039
- for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk33.default.dim(o.source_label ?? "")}`);
5331
+ console.log(import_chalk34.default.dim("Seen in traffic (not yet users):"));
5332
+ for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk34.default.dim(o.source_label ?? "")}`);
5040
5333
  }
5041
5334
  }
5042
5335
 
5043
5336
  // src/commands/key.ts
5044
- var import_chalk34 = __toESM(require("chalk"));
5045
- var import_ora17 = __toESM(require("ora"));
5046
- init_admin();
5337
+ var import_chalk35 = __toESM(require("chalk"));
5338
+ var import_ora18 = __toESM(require("ora"));
5339
+ init_caller();
5047
5340
  async function runApikeysMenu(opts) {
5048
5341
  await runKeyList(opts);
5049
5342
  if (opts.json) return;
@@ -5058,8 +5351,9 @@ async function runApikeysMenu(opts) {
5058
5351
  await runKeyMint({ ...opts, desc: desc || void 0 });
5059
5352
  }
5060
5353
  async function runKeyList(opts) {
5061
- const { teamId, teamName } = await resolveTeam(opts.team);
5062
- const out = await admin({
5354
+ const caller = requireCaller();
5355
+ const { teamId, teamName } = await resolveActingTeam(caller, opts.team);
5356
+ const out = await producer(caller, {
5063
5357
  method: "GET",
5064
5358
  path: `/teams/${encodeURIComponent(teamId)}/developer-keys`,
5065
5359
  summary: `List developer keys for team ${teamName ?? teamId}`
@@ -5070,21 +5364,23 @@ async function runKeyList(opts) {
5070
5364
  return;
5071
5365
  }
5072
5366
  if (!keys.length) {
5073
- console.log(import_chalk34.default.yellow("No developer keys."));
5367
+ console.log(import_chalk35.default.yellow("No developer keys."));
5074
5368
  return;
5075
5369
  }
5076
5370
  for (const k of keys) {
5077
- console.log(` ${import_chalk34.default.bold(k.key_id ?? k.id)} ${import_chalk34.default.dim(k.description ?? "")} ${import_chalk34.default.dim(k.expires_at ?? "no expiry")}`);
5371
+ console.log(` ${import_chalk35.default.bold(k.key_id ?? k.id)} ${import_chalk35.default.dim(k.description ?? "")} ${import_chalk35.default.dim(k.expires_at ?? "no expiry")}`);
5078
5372
  }
5079
5373
  }
5080
5374
  async function runKeyMint(opts) {
5081
- const { teamId } = await resolveTeam(opts.team);
5375
+ if (process.env.ABZ_DEBUG_OPTS) console.error("DEBUG opts:", JSON.stringify(opts));
5376
+ const caller = requireCaller();
5377
+ const { teamId } = await resolveActingTeam(caller, opts.team);
5082
5378
  const body = { role: "consumer-admin" };
5083
5379
  if (opts.desc) body.description = opts.desc;
5084
5380
  if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
5085
- const spinner = (0, import_ora17.default)("Minting key...").start();
5381
+ const spinner = (0, import_ora18.default)("Minting key...").start();
5086
5382
  try {
5087
- const out = await admin({
5383
+ const out = await producer(caller, {
5088
5384
  method: "POST",
5089
5385
  path: `/teams/${encodeURIComponent(teamId)}/developer-keys`,
5090
5386
  body,
@@ -5095,19 +5391,20 @@ async function runKeyMint(opts) {
5095
5391
  console.log(JSON.stringify(out));
5096
5392
  return;
5097
5393
  }
5098
- console.log(` ${import_chalk34.default.bold("key_id")}: ${out?.key_id}`);
5099
- console.log(` ${import_chalk34.default.bold("key")}: ${import_chalk34.default.green(out?.key)} ${import_chalk34.default.dim("(shown once \u2014 store it now)")}`);
5100
- if (out?.expires_at) console.log(` ${import_chalk34.default.dim("expires:")} ${out.expires_at}`);
5394
+ console.log(` ${import_chalk35.default.bold("key_id")}: ${out?.key_id}`);
5395
+ console.log(` ${import_chalk35.default.bold("key")}: ${import_chalk35.default.green(out?.key)} ${import_chalk35.default.dim("(shown once \u2014 store it now)")}`);
5396
+ if (out?.expires_at) console.log(` ${import_chalk35.default.dim("expires:")} ${out.expires_at}`);
5101
5397
  } catch (err) {
5102
5398
  spinner.fail("Mint failed.");
5103
5399
  throw err;
5104
5400
  }
5105
5401
  }
5106
5402
  async function runKeyRevoke(keyId, opts) {
5107
- const { teamId } = await resolveTeam(opts.team);
5108
- const spinner = (0, import_ora17.default)("Revoking key...").start();
5403
+ const caller = requireCaller();
5404
+ const { teamId } = await resolveActingTeam(caller, opts.team);
5405
+ const spinner = (0, import_ora18.default)("Revoking key...").start();
5109
5406
  try {
5110
- await admin({
5407
+ await producer(caller, {
5111
5408
  method: "DELETE",
5112
5409
  path: `/teams/${encodeURIComponent(teamId)}/developer-keys/${encodeURIComponent(keyId)}`,
5113
5410
  summary: `Revoke developer key ${keyId}`
@@ -5119,12 +5416,116 @@ async function runKeyRevoke(keyId, opts) {
5119
5416
  }
5120
5417
  }
5121
5418
 
5419
+ // src/index.ts
5420
+ init_iam();
5421
+
5422
+ // src/commands/preapprove.ts
5423
+ var import_chalk36 = __toESM(require("chalk"));
5424
+ var import_ora19 = __toESM(require("ora"));
5425
+
5426
+ // src/lib/preapproval.ts
5427
+ init_auth();
5428
+ init_admin();
5429
+ init_resolve();
5430
+ init_types();
5431
+ var DASHBOARD_BASE7 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
5432
+ async function iamCall(tenant2, path8, method = "GET", body) {
5433
+ const token = getAccessToken();
5434
+ const res = await fetch(`${DASHBOARD_BASE7}/api/cli/iam`, {
5435
+ method: "POST",
5436
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
5437
+ body: JSON.stringify({ tenant: tenant2, path: path8, method, body })
5438
+ });
5439
+ const data = await res.json().catch(() => ({}));
5440
+ if (!res.ok) throw new ApiError(res.status, data?.details ?? data?.error ?? res.statusText, data);
5441
+ return data;
5442
+ }
5443
+ async function resolveProducerTenant(tenantOpt) {
5444
+ if (!loadCredentials()) throw new Error("Not authenticated. Run `apiblaze login` first.");
5445
+ const tenant2 = (tenantOpt ?? "").trim().toLowerCase();
5446
+ if (tenant2) return tenant2;
5447
+ const { teamId } = await resolveTeam(void 0);
5448
+ const out = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants`, summary: "List tenants" });
5449
+ const tenants = (out?.tenants ?? []).map((t) => String(t.tenant_name ?? t.name ?? t)).filter((t) => !t.startsWith("op"));
5450
+ if (tenants.length === 0) throw new Error("No tenants in your team yet. Create a proxy first, or pass --tenant.");
5451
+ if (tenants.length === 1) return tenants[0];
5452
+ const { default: inquirer3 } = await import("inquirer");
5453
+ const ans = await inquirer3.prompt([{ type: "list", name: "tenant", message: "Which tenant?", choices: tenants }]);
5454
+ return String(ans.tenant);
5455
+ }
5456
+ function classify(input) {
5457
+ const value = input.trim().replace(/^@/, "").toLowerCase();
5458
+ return { kind: value.includes("@") ? "email" : "domain", value };
5459
+ }
5460
+ async function addPreapprovalRule(tenant2, input, groups) {
5461
+ const { kind, value } = classify(input);
5462
+ return iamCall(tenant2, "/v1/admin/preapproval/rules", "POST", {
5463
+ kind,
5464
+ value,
5465
+ ...groups && groups.length ? { groups } : {}
5466
+ });
5467
+ }
5468
+ async function listPreapprovalRules(tenant2) {
5469
+ const out = await iamCall(tenant2, "/v1/admin/preapproval/rules", "GET");
5470
+ return out?.rules ?? [];
5471
+ }
5472
+ async function removePreapprovalRule(tenant2, input) {
5473
+ const { value } = classify(input);
5474
+ const rules = await listPreapprovalRules(tenant2);
5475
+ const hit = rules.find((r) => r.value.toLowerCase() === value);
5476
+ if (!hit) return { removed: false, value };
5477
+ await iamCall(tenant2, `/v1/admin/preapproval/rules/${encodeURIComponent(hit.id)}`, "DELETE");
5478
+ return { removed: true, value };
5479
+ }
5480
+
5481
+ // src/commands/preapprove.ts
5482
+ async function runPreapprove(who, opts) {
5483
+ const tenant2 = await resolveProducerTenant(opts.tenant);
5484
+ if (opts.list) {
5485
+ const rules = await listPreapprovalRules(tenant2);
5486
+ if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
5487
+ if (!rules.length) {
5488
+ console.log(import_chalk36.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
5489
+ console.log(import_chalk36.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
5490
+ return;
5491
+ }
5492
+ console.log(import_chalk36.default.dim(`Pre-approved for ${import_chalk36.default.bold(tenant2)}:`));
5493
+ for (const r of rules) {
5494
+ const tag = r.kind === "domain" ? import_chalk36.default.cyan("@" + r.value + " (whole domain)") : r.value;
5495
+ const grp = r.groups?.length ? import_chalk36.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
5496
+ console.log(` ${tag}${grp}`);
5497
+ }
5498
+ return;
5499
+ }
5500
+ if (!who) {
5501
+ throw new Error("Who? Pass an email or a domain: `apiblaze preapprove someone@acme.com` (or `apiblaze preapprove --list`).");
5502
+ }
5503
+ if (opts.remove) {
5504
+ const spinner2 = (0, import_ora19.default)(`Removing ${who} from ${tenant2}\u2026`).start();
5505
+ const { removed, value } = await removePreapprovalRule(tenant2, who);
5506
+ if (removed) spinner2.succeed(`${import_chalk36.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
5507
+ else spinner2.warn(`No pre-approval rule for ${import_chalk36.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
5508
+ return;
5509
+ }
5510
+ const spinner = (0, import_ora19.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
5511
+ try {
5512
+ const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
5513
+ const what = kind === "domain" ? `Anyone @${value}` : value;
5514
+ spinner.succeed(`${import_chalk36.default.bold(what)} can now sign in to ${import_chalk36.default.bold(tenant2)}.`);
5515
+ if (opts.group?.length) console.log(import_chalk36.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
5516
+ console.log(import_chalk36.default.dim(` See the full list: apiblaze preapprove --list`));
5517
+ } catch (err) {
5518
+ spinner.fail("Could not add the rule.");
5519
+ throw err;
5520
+ }
5521
+ }
5522
+
5122
5523
  // src/commands/apichat.ts
5123
5524
  var fs9 = __toESM(require("fs"));
5124
5525
  var path6 = __toESM(require("path"));
5125
5526
  var crypto2 = __toESM(require("crypto"));
5126
- var import_chalk36 = __toESM(require("chalk"));
5127
- var import_ora18 = __toESM(require("ora"));
5527
+ var import_chalk38 = __toESM(require("chalk"));
5528
+ var import_ora20 = __toESM(require("ora"));
5128
5529
  var import_yaml = require("yaml");
5129
5530
  init_auth();
5130
5531
  init_anon_cred();
@@ -5134,7 +5535,7 @@ init_admin();
5134
5535
  // src/commands/llm.ts
5135
5536
  var fs8 = __toESM(require("fs"));
5136
5537
  var path5 = __toESM(require("path"));
5137
- var import_chalk35 = __toESM(require("chalk"));
5538
+ var import_chalk37 = __toESM(require("chalk"));
5138
5539
  var import_inquirer2 = __toESM(require("inquirer"));
5139
5540
  init_auth();
5140
5541
  var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
@@ -5178,27 +5579,27 @@ async function runLlmSetKey(keyArg, opts) {
5178
5579
  }
5179
5580
  const existing = loadLlmConfig();
5180
5581
  saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
5181
- console.log(`${import_chalk35.default.green("\u2713")} Saved ${import_chalk35.default.bold(provider)} key ${maskSecret(key)}`);
5182
- console.log(import_chalk35.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
5183
- if (opts.model) console.log(import_chalk35.default.gray(` Model: ${opts.model}`));
5582
+ console.log(`${import_chalk37.default.green("\u2713")} Saved ${import_chalk37.default.bold(provider)} key ${maskSecret(key)}`);
5583
+ console.log(import_chalk37.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
5584
+ if (opts.model) console.log(import_chalk37.default.gray(` Model: ${opts.model}`));
5184
5585
  }
5185
5586
  async function runLlmShow() {
5186
5587
  const cfg = loadLlmConfig();
5187
5588
  if (!cfg) {
5188
- console.log(import_chalk35.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
5589
+ console.log(import_chalk37.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
5189
5590
  return;
5190
5591
  }
5191
- console.log(`Provider: ${import_chalk35.default.bold(cfg.provider)}`);
5592
+ console.log(`Provider: ${import_chalk37.default.bold(cfg.provider)}`);
5192
5593
  console.log(`Key: ${maskSecret(cfg.key)}`);
5193
5594
  if (cfg.model) console.log(`Model: ${cfg.model}`);
5194
- console.log(import_chalk35.default.gray(`Stored at ${LLM_PATH}`));
5595
+ console.log(import_chalk37.default.gray(`Stored at ${LLM_PATH}`));
5195
5596
  }
5196
5597
  async function runLlmClearKey() {
5197
5598
  try {
5198
5599
  fs8.unlinkSync(LLM_PATH);
5199
- console.log(`${import_chalk35.default.green("\u2713")} Removed local LLM key.`);
5600
+ console.log(`${import_chalk37.default.green("\u2713")} Removed local LLM key.`);
5200
5601
  } catch {
5201
- console.log(import_chalk35.default.gray("No LLM key was set."));
5602
+ console.log(import_chalk37.default.gray("No LLM key was set."));
5202
5603
  }
5203
5604
  }
5204
5605
 
@@ -5206,9 +5607,9 @@ async function runLlmClearKey() {
5206
5607
  init_trace();
5207
5608
  init_types();
5208
5609
  function fail4(message, hint) {
5209
- console.error(import_chalk36.default.red(`
5610
+ console.error(import_chalk38.default.red(`
5210
5611
  Error: ${message}`));
5211
- if (hint) console.error(import_chalk36.default.dim(hint));
5612
+ if (hint) console.error(import_chalk38.default.dim(hint));
5212
5613
  process.exit(1);
5213
5614
  }
5214
5615
  function normalizeName2(raw) {
@@ -5356,7 +5757,7 @@ async function resolveTargetAuth(spec2, opts) {
5356
5757
  "Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
5357
5758
  );
5358
5759
  }
5359
- if (sawOAuth) console.log(import_chalk36.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
5760
+ if (sawOAuth) console.log(import_chalk38.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
5360
5761
  return null;
5361
5762
  }
5362
5763
  if (candidates.length === 1 && !noneAllowed) return candidates[0];
@@ -5433,7 +5834,16 @@ async function provision(spec2, target, opts) {
5433
5834
  if (!base && spec2.info && typeof spec2.info.title === "string") base = normalizeName2(spec2.info.title);
5434
5835
  if (!base || base.length < 3) base = "apichat";
5435
5836
  let name = opts.name ? base : `${base}${salt()}`;
5436
- const spinner = (0, import_ora18.default)("Provisioning an api_key proxy...").start();
5837
+ const access = anon ? "open" : opts.access === "open" ? "open" : "invite";
5838
+ if (anon && opts.access === "invite") {
5839
+ console.log(import_chalk38.default.dim(" Note: --access invite needs an account to pre-approve people. Staying open for this anonymous proxy \u2014 run `apiblaze login`, then `apiblaze apichat --access invite`."));
5840
+ }
5841
+ const DUAL_AUTH = {
5842
+ mode: "authenticate",
5843
+ methods: ["api_key", "jwt"],
5844
+ ...access === "invite" ? { preapproved_users_only: true } : {}
5845
+ };
5846
+ const spinner = (0, import_ora20.default)("Provisioning an api_key proxy...").start();
5437
5847
  let result;
5438
5848
  for (let attempt = 0; attempt < 4; attempt++) {
5439
5849
  try {
@@ -5443,6 +5853,7 @@ async function provision(spec2, target, opts) {
5443
5853
  name,
5444
5854
  target_url: target,
5445
5855
  auth_type: "api_key",
5856
+ requests_auth: DUAL_AUTH,
5446
5857
  team_id: creds.teamId,
5447
5858
  ...opts.apiversion ? { api_version: opts.apiversion } : {}
5448
5859
  });
@@ -5454,6 +5865,7 @@ async function provision(spec2, target, opts) {
5454
5865
  target,
5455
5866
  target_url: target,
5456
5867
  auth_type: "api_key",
5868
+ requests_auth: DUAL_AUTH,
5457
5869
  ...opts.apiversion ? { api_version: opts.apiversion } : {}
5458
5870
  };
5459
5871
  if (cred) {
@@ -5510,10 +5922,21 @@ async function provision(spec2, target, opts) {
5510
5922
  if (!dpKey) fail4("The proxy was created but no api_key was returned \u2014 cannot authenticate the chat.");
5511
5923
  const mcpHost = result.mcpHost || `${projectId}.mcp.${anon ? "tryabz.run" : "abz.run"}`;
5512
5924
  const proxyUrl = Array.isArray(result.endpoints) && (result.endpoints.find((e) => e.includes(`/${environment}`)) || result.endpoints[0]) || (anon ? `https://${projectId}.tryabz.run/${version2}/${environment}` : `https://${projectId}.abz.run/${version2}/${environment}`);
5513
- return { projectId, version: version2, environment, dpKey, mcpHost, proxyUrl, anon };
5925
+ const tenant2 = typeof result.tenant === "string" ? result.tenant : void 0;
5926
+ if (access === "invite" && tenant2) {
5927
+ const email = loadCredentials()?.email;
5928
+ if (email) {
5929
+ try {
5930
+ await addPreapprovalRule(tenant2, email);
5931
+ } catch {
5932
+ console.log(import_chalk38.default.dim(` (Could not auto-approve your email for sign-in \u2014 add it later: apiblaze preapprove ${email})`));
5933
+ }
5934
+ }
5935
+ }
5936
+ return { projectId, version: version2, environment, dpKey, mcpHost, proxyUrl, anon, access, tenant: tenant2 };
5514
5937
  }
5515
5938
  async function writeTargetAuth(p, auth, secret) {
5516
- const spinner = (0, import_ora18.default)("Storing target credentials (encrypted)...").start();
5939
+ const spinner = (0, import_ora20.default)("Storing target credentials (encrypted)...").start();
5517
5940
  try {
5518
5941
  await cpPost(
5519
5942
  p.anon,
@@ -5535,7 +5958,7 @@ async function writeTargetAuth(p, auth, secret) {
5535
5958
  }
5536
5959
  }
5537
5960
  async function uploadSpec(p, specText, opts) {
5538
- const spinner = (0, import_ora18.default)("Uploading the spec...").start();
5961
+ const spinner = (0, import_ora20.default)("Uploading the spec...").start();
5539
5962
  let out;
5540
5963
  try {
5541
5964
  out = await cpPost(
@@ -5550,7 +5973,7 @@ async function uploadSpec(p, specText, opts) {
5550
5973
  throw err;
5551
5974
  }
5552
5975
  if (out && out.reused === true) {
5553
- console.log(import_chalk36.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
5976
+ console.log(import_chalk38.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
5554
5977
  } else if (out && out.changed === true && out.previous_spec_hash) {
5555
5978
  const interactive = !!process.stdin.isTTY && !opts.yes;
5556
5979
  if (interactive) {
@@ -5558,12 +5981,12 @@ async function uploadSpec(p, specText, opts) {
5558
5981
  const { go } = await inquirer3.prompt([
5559
5982
  { type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
5560
5983
  ]);
5561
- if (!go) console.log(import_chalk36.default.dim(" Keeping the existing MCP catalogue."));
5984
+ if (!go) console.log(import_chalk38.default.dim(" Keeping the existing MCP catalogue."));
5562
5985
  }
5563
5986
  }
5564
5987
  }
5565
5988
  async function publishMcp(p, spec2) {
5566
- const spinner = (0, import_ora18.default)("Publishing the MCP catalogue...").start();
5989
+ const spinner = (0, import_ora20.default)("Publishing the MCP catalogue...").start();
5567
5990
  try {
5568
5991
  const url = `https://${p.mcpHost}/${p.version}/${p.environment}/mcp/generate`;
5569
5992
  const res = await fetch(url, {
@@ -5599,14 +6022,14 @@ var revealAuth = false;
5599
6022
  function renderToolEvents(events, dpKey) {
5600
6023
  for (const e of events ?? []) {
5601
6024
  const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
5602
- const mark = ok ? import_chalk36.default.green("\u2713") : import_chalk36.default.red("\u2717");
5603
- console.log(` ${mark} ${import_chalk36.default.cyan(e.name)} ${import_chalk36.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
6025
+ const mark = ok ? import_chalk38.default.green("\u2713") : import_chalk38.default.red("\u2717");
6026
+ console.log(` ${mark} ${import_chalk38.default.cyan(e.name)} ${import_chalk38.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
5604
6027
  if (isVerbose() && e.method && e.url) {
5605
- console.log(import_chalk36.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
6028
+ console.log(import_chalk38.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
5606
6029
  if (dpKey) {
5607
6030
  const keyLine = ` -H 'X-API-Key: ${revealAuth ? dpKey : maskKey(dpKey)}'`;
5608
- const hint = revealAuth ? "" : import_chalk36.default.yellow(" \u2190 /showauth will reveal this");
5609
- console.log(import_chalk36.default.dim(keyLine) + hint);
6031
+ const hint = revealAuth ? "" : import_chalk38.default.yellow(" \u2190 /showauth will reveal this");
6032
+ console.log(import_chalk38.default.dim(keyLine) + hint);
5610
6033
  }
5611
6034
  }
5612
6035
  }
@@ -5615,9 +6038,9 @@ function billingLine(billing) {
5615
6038
  if (!billing || typeof billing.cents !== "number") return null;
5616
6039
  if (typeof billing.free_turns_remaining === "number") return null;
5617
6040
  const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
5618
- let line = import_chalk36.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk36.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
6041
+ let line = import_chalk38.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk38.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
5619
6042
  if (typeof billing.credits_remaining === "number") {
5620
- line += import_chalk36.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
6043
+ line += import_chalk38.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
5621
6044
  }
5622
6045
  return line;
5623
6046
  }
@@ -5625,21 +6048,21 @@ function freeBudgetWarning(billing, anon) {
5625
6048
  if (!anon || !billing) return null;
5626
6049
  if (typeof billing.free_turns_remaining === "number") {
5627
6050
  const left2 = billing.free_turns_remaining;
5628
- if (left2 <= 0) return import_chalk36.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
5629
- return import_chalk36.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left`);
6051
+ if (left2 <= 0) return import_chalk38.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
6052
+ return import_chalk38.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
5630
6053
  }
5631
6054
  if (typeof billing.free_remaining_cents !== "number") return null;
5632
6055
  const perTurn = Math.max(billing.cents || 0, 0.02);
5633
6056
  const left = Math.floor(billing.free_remaining_cents / perTurn);
5634
6057
  if (left > 8) return null;
5635
- if (left <= 0) return import_chalk36.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
5636
- return import_chalk36.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
6058
+ if (left <= 0) return import_chalk38.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
6059
+ return import_chalk38.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
5637
6060
  }
5638
6061
  function printAssistant(delta) {
5639
6062
  for (let i = delta.length - 1; i >= 0; i--) {
5640
6063
  const m = delta[i];
5641
6064
  if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
5642
- console.log("\n" + import_chalk36.default.green("assistant \u203A ") + m.content + "\n");
6065
+ console.log("\n" + import_chalk38.default.green("assistant \u203A ") + m.content + "\n");
5643
6066
  return;
5644
6067
  }
5645
6068
  }
@@ -5649,7 +6072,7 @@ async function replTurn(p, messages, userText) {
5649
6072
  const llm2 = loadLlmConfig();
5650
6073
  const turnId = crypto2.randomUUID();
5651
6074
  for (let round = 0; round < CLIENT_ROUND_CAP; round++) {
5652
- const spinner = (0, import_ora18.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
6075
+ const spinner = (0, import_ora20.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
5653
6076
  const body = {
5654
6077
  turn_id: turnId,
5655
6078
  messages,
@@ -5665,7 +6088,7 @@ async function replTurn(p, messages, userText) {
5665
6088
  });
5666
6089
  } catch (err) {
5667
6090
  spinner.fail("Network error.");
5668
- console.log(import_chalk36.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
6091
+ console.log(import_chalk38.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
5669
6092
  return;
5670
6093
  }
5671
6094
  let data = null;
@@ -5683,12 +6106,12 @@ async function replTurn(p, messages, userText) {
5683
6106
  return;
5684
6107
  }
5685
6108
  if (res.status === 401) {
5686
- console.log(import_chalk36.default.red(" The proxy rejected the API key (401). The key may have been revoked; re-run `apiblaze apichat` to re-provision."));
6109
+ console.log(import_chalk38.default.red(" The proxy rejected the API key (401). The key may have been revoked; re-run `apiblaze apichat` to re-provision."));
5687
6110
  return;
5688
6111
  }
5689
6112
  if (!res.ok || !data) {
5690
6113
  const err = data && data.error || `HTTP ${res.status}`;
5691
- console.log(import_chalk36.default.red(` Chat error: ${err}`));
6114
+ console.log(import_chalk38.default.red(` Chat error: ${err}`));
5692
6115
  return;
5693
6116
  }
5694
6117
  if (Array.isArray(data.delta)) {
@@ -5702,25 +6125,25 @@ async function replTurn(p, messages, userText) {
5702
6125
  if (warn) console.log(warn);
5703
6126
  if (!data.continue) return;
5704
6127
  }
5705
- console.log(import_chalk36.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
6128
+ console.log(import_chalk38.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
5706
6129
  }
5707
6130
  function renderUpsell(p, upsell) {
5708
6131
  const loggedIn = !!loadCredentials();
5709
6132
  if (upsell.reason === "CAPPED" && !loggedIn) {
5710
- console.log("\n" + import_chalk36.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
5711
- console.log(import_chalk36.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
6133
+ console.log("\n" + import_chalk38.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
6134
+ console.log(import_chalk38.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
5712
6135
  console.log();
5713
6136
  return;
5714
6137
  }
5715
- console.log("\n" + import_chalk36.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
6138
+ console.log("\n" + import_chalk38.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
5716
6139
  if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
5717
6140
  if (!loggedIn) {
5718
- console.log(import_chalk36.default.dim(" Options: `/login` for free login credit and higher limits, or `apiblaze llm set-key` to bring your own model key."));
6141
+ console.log(import_chalk38.default.dim(" Options: `/login` for more free chats and requests, or `apiblaze llm set-key` to bring your own model key."));
5719
6142
  } else {
5720
- console.log(import_chalk36.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
6143
+ console.log(import_chalk38.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
5721
6144
  }
5722
6145
  } else if (upsell.reason === "INFLIGHT") {
5723
- console.log(import_chalk36.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
6146
+ console.log(import_chalk38.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
5724
6147
  }
5725
6148
  console.log();
5726
6149
  }
@@ -5789,9 +6212,9 @@ async function openServerProxy(project) {
5789
6212
  const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
5790
6213
  let dpKey = prior?.dpKey;
5791
6214
  if (!dpKey) {
5792
- console.log(import_chalk36.default.dim(` Minting an API key for tenant ${import_chalk36.default.bold(tenant2)} to query project ${import_chalk36.default.bold(project.projectName)}\u2026`));
6215
+ console.log(import_chalk38.default.dim(` Minting an API key for tenant ${import_chalk38.default.bold(tenant2)} to query project ${import_chalk38.default.bold(project.projectName)}\u2026`));
5793
6216
  dpKey = await mintDurableProxyKey(project.teamId, tenant2);
5794
- console.log(` ${import_chalk36.default.green("\u2714")} API key: ${import_chalk36.default.dim(maskKey(dpKey))}`);
6217
+ console.log(` ${import_chalk38.default.green("\u2714")} API key: ${import_chalk38.default.dim(maskKey(dpKey))}`);
5795
6218
  }
5796
6219
  const p = {
5797
6220
  projectId: project.projectId,
@@ -5800,9 +6223,11 @@ async function openServerProxy(project) {
5800
6223
  dpKey,
5801
6224
  mcpHost,
5802
6225
  proxyUrl: `https://${project.projectId}.abz.run/${version2}/${environment}`,
5803
- anon: false
6226
+ anon: false,
6227
+ // Reusing an owned proxy: logged-in apichat doors default to invite-only.
6228
+ access: "invite"
5804
6229
  };
5805
- const spinner = (0, import_ora18.default)("Preparing the chat\u2026").start();
6230
+ const spinner = (0, import_ora20.default)("Preparing the chat\u2026").start();
5806
6231
  try {
5807
6232
  const raw = await admin({
5808
6233
  method: "GET",
@@ -5814,7 +6239,7 @@ async function openServerProxy(project) {
5814
6239
  if (spec2 && (spec2.paths || spec2.openapi)) {
5815
6240
  await publishMcp(p, spec2);
5816
6241
  } else {
5817
- console.log(import_chalk36.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
6242
+ console.log(import_chalk38.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
5818
6243
  }
5819
6244
  } catch (err) {
5820
6245
  spinner.fail("Could not open the proxy.");
@@ -5867,7 +6292,7 @@ async function noArgsMenu(opts) {
5867
6292
  const me = loadCredentials()?.apiblazeUserId;
5868
6293
  const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
5869
6294
  const choices = saved.map((a) => ({
5870
- name: `Chat with ${import_chalk36.default.bold(a.name)} ${import_chalk36.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
6295
+ name: `Chat with ${import_chalk38.default.bold(a.name)} ${import_chalk38.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
5871
6296
  value: { type: "existing", a }
5872
6297
  }));
5873
6298
  const creds = loadCredentials();
@@ -5877,14 +6302,14 @@ async function noArgsMenu(opts) {
5877
6302
  const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
5878
6303
  for (const pr of proxies) {
5879
6304
  choices.push({
5880
- name: `Chat with ${import_chalk36.default.bold(pr.projectName)} ${import_chalk36.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
6305
+ name: `Chat with ${import_chalk38.default.bold(pr.projectName)} ${import_chalk38.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
5881
6306
  value: { type: "server", project: pr }
5882
6307
  });
5883
6308
  }
5884
6309
  } catch {
5885
6310
  }
5886
6311
  }
5887
- choices.push({ name: import_chalk36.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
6312
+ choices.push({ name: import_chalk38.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
5888
6313
  const { pick: pick2 } = await inquirer3.prompt([
5889
6314
  { type: "list", name: "pick", message: "What would you like to do?", choices }
5890
6315
  ]);
@@ -5912,7 +6337,9 @@ async function noArgsMenu(opts) {
5912
6337
  dpKey: a.dpKey,
5913
6338
  mcpHost: a.mcpHost,
5914
6339
  proxyUrl: void 0,
5915
- anon: a.anon
6340
+ anon: a.anon,
6341
+ // Saved sessions predate the access field: mirror the provisioning default.
6342
+ access: a.anon ? "open" : "invite"
5916
6343
  };
5917
6344
  return { p, messages };
5918
6345
  }
@@ -5956,63 +6383,70 @@ async function noArgsMenu(opts) {
5956
6383
  async function runRepl(p, initialMessages) {
5957
6384
  const { default: inquirer3 } = await import("inquirer");
5958
6385
  const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
5959
- console.log("\n" + import_chalk36.default.cyan.bold("Chat with your API") + import_chalk36.default.dim(` \xB7 ${p.mcpHost}`));
5960
- if (messages.length) console.log(import_chalk36.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
6386
+ console.log("\n" + import_chalk38.default.cyan.bold("Chat with your API") + import_chalk38.default.dim(` \xB7 ${p.mcpHost}`));
6387
+ if (messages.length) console.log(import_chalk38.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
5961
6388
  const llm2 = loadLlmConfig();
5962
6389
  console.log(
5963
- import_chalk36.default.dim(
5964
- llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for credit \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
6390
+ import_chalk38.default.dim(
6391
+ llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for more free chats \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
5965
6392
  )
5966
6393
  );
5967
6394
  for (; ; ) {
5968
- const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk36.default.green("you \u203A") }]);
6395
+ const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk38.default.green("you \u203A") }]);
5969
6396
  const text = (input ?? "").trim();
5970
6397
  if (!text) continue;
5971
6398
  if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
5972
6399
  if (text === "/login") {
5973
6400
  try {
5974
6401
  await runLogin();
5975
- console.log(import_chalk36.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
6402
+ console.log(import_chalk38.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
5976
6403
  } catch (err) {
5977
- console.log(import_chalk36.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
6404
+ console.log(import_chalk38.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
5978
6405
  }
5979
6406
  continue;
5980
6407
  }
5981
6408
  if (text === "/claim") {
5982
- if (!loadCredentials()) {
5983
- console.log(import_chalk36.default.yellow(" Log in first: `/login`, then `/claim`."));
5984
- continue;
6409
+ const justLoggedIn = !loadCredentials();
6410
+ if (justLoggedIn) {
6411
+ console.log(import_chalk38.default.dim(" Logging in to claim your workspace\u2026"));
6412
+ try {
6413
+ await runLogin();
6414
+ } catch (err) {
6415
+ console.log(import_chalk38.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
6416
+ continue;
6417
+ }
6418
+ if (!loadCredentials()) continue;
5985
6419
  }
5986
6420
  try {
5987
- await runClaim(void 0, {});
6421
+ if (!justLoggedIn) await runClaim(void 0, { team: loadCredentials()?.teamId });
5988
6422
  if (p.mcpHost.endsWith(".mcp.tryabz.run")) {
5989
6423
  p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
5990
6424
  p.anon = false;
5991
6425
  claimApichat(p, loadCredentials()?.apiblazeUserId);
5992
- console.log(import_chalk36.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
6426
+ console.log(import_chalk38.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
5993
6427
  }
5994
6428
  } catch (err) {
5995
- console.log(import_chalk36.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
6429
+ console.log(import_chalk38.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
5996
6430
  }
5997
6431
  continue;
5998
6432
  }
5999
6433
  if (text === "/showauth") {
6000
6434
  revealAuth = !revealAuth;
6001
- console.log(import_chalk36.default.dim(revealAuth ? " The real API key will be shown in the next call's curl. /showauth again to re-mask." : " API key re-masked."));
6435
+ console.log(import_chalk38.default.dim(revealAuth ? " The real API key will be shown in the next call's curl. /showauth again to re-mask." : " API key re-masked."));
6002
6436
  continue;
6003
6437
  }
6004
6438
  if (text.startsWith("/")) {
6005
- console.log(import_chalk36.default.dim(" Commands: /login /claim /showauth /exit"));
6439
+ console.log(import_chalk38.default.dim(" Commands: /login /claim /showauth /exit"));
6006
6440
  continue;
6007
6441
  }
6008
6442
  await replTurn(p, messages, text);
6009
6443
  saveTranscript(p, messages);
6010
6444
  }
6011
- console.log(import_chalk36.default.dim("\nBye."));
6445
+ console.log(import_chalk38.default.dim("\nBye."));
6012
6446
  }
6013
6447
  async function runApichat(opts) {
6014
6448
  setVerbose(opts.verbose !== false);
6015
- console.log(import_chalk36.default.bold("\napichat \u2014 turn any API into a chat\n"));
6449
+ console.log(import_chalk38.default.bold("\napichat \u2014 turn any API into a chat\n"));
6016
6450
  if (!opts.openapispec && !opts.target) {
6017
6451
  if (!process.stdin.isTTY) {
6018
6452
  fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
@@ -6025,7 +6459,7 @@ async function runApichat(opts) {
6025
6459
  }
6026
6460
  const { spec: spec2, sourceUrl } = await loadSpec(opts);
6027
6461
  const target = resolveTarget(spec2, opts, sourceUrl);
6028
- console.log(` ${import_chalk36.default.dim("Target:")} ${import_chalk36.default.bold(target)}`);
6462
+ console.log(` ${import_chalk38.default.dim("Target:")} ${import_chalk38.default.bold(target)}`);
6029
6463
  const auth = await resolveTargetAuth(spec2, opts);
6030
6464
  if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
6031
6465
  fail4(
@@ -6034,7 +6468,7 @@ async function runApichat(opts) {
6034
6468
  );
6035
6469
  }
6036
6470
  const p = await provision(spec2, target, opts);
6037
- console.log(` ${import_chalk36.default.dim("Proxy: ")} ${import_chalk36.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
6471
+ console.log(` ${import_chalk38.default.dim("Proxy: ")} ${import_chalk38.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
6038
6472
  upsertApichat({
6039
6473
  name: p.projectId,
6040
6474
  target,
@@ -6053,24 +6487,33 @@ async function runApichat(opts) {
6053
6487
  const secret = await captureTargetSecret(auth, opts);
6054
6488
  if (secret) await writeTargetAuth(p, auth, secret);
6055
6489
  } else {
6056
- console.log(import_chalk36.default.dim(" Target auth: none required."));
6490
+ console.log(import_chalk38.default.dim(" Target auth: none required."));
6057
6491
  }
6058
6492
  const specText = JSON.stringify(spec2);
6059
6493
  await uploadSpec(p, specText, opts);
6060
6494
  const mcpUrl = await publishMcp(p, spec2);
6061
6495
  console.log();
6062
- if (p.proxyUrl) console.log(` ${import_chalk36.default.green("\u2713")} proxy ${import_chalk36.default.bold(p.proxyUrl)}`);
6063
- if (mcpUrl) console.log(` ${import_chalk36.default.green("\u2713")} mcp ${import_chalk36.default.bold(mcpUrl)}`);
6496
+ if (p.proxyUrl) console.log(` ${import_chalk38.default.green("\u2713")} proxy ${import_chalk38.default.bold(p.proxyUrl)}`);
6497
+ if (mcpUrl) {
6498
+ console.log(` ${import_chalk38.default.green("\u2713")} mcp ${import_chalk38.default.bold(mcpUrl)}`);
6499
+ if (p.access === "invite") {
6500
+ console.log(import_chalk38.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: invite \u2014 only you + emails you pre-approve"));
6501
+ console.log(import_chalk38.default.dim(` Let others in: apiblaze preapprove someone@company.com${p.tenant ? ` --tenant ${p.tenant}` : ""} (or re-run with --access open)`));
6502
+ } else {
6503
+ console.log(import_chalk38.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: open \u2014 anyone who signs in can call this API"));
6504
+ }
6505
+ }
6064
6506
  if (p.anon) {
6065
- console.log(import_chalk36.default.dim("\n Anonymous workspace \u2014 /claim inside the chat (after /login) to keep it beyond 30 days."));
6507
+ console.log(import_chalk38.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
6066
6508
  }
6067
6509
  await runRepl(p);
6068
6510
  }
6069
6511
 
6070
6512
  // src/commands/consumer.ts
6071
- var import_chalk37 = __toESM(require("chalk"));
6072
- var import_ora19 = __toESM(require("ora"));
6513
+ var import_chalk39 = __toESM(require("chalk"));
6514
+ var import_ora21 = __toESM(require("ora"));
6073
6515
  init_admin();
6516
+ init_resolve();
6074
6517
  var DEFAULT_SCOPE = "openid email profile offline_access";
6075
6518
  var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
6076
6519
  async function consumerFetch(creds, suffix, init) {
@@ -6089,7 +6532,7 @@ async function consumerFetch(creds, suffix, init) {
6089
6532
  function requireConsumer() {
6090
6533
  const c = loadConsumer();
6091
6534
  if (!c) {
6092
- console.error(import_chalk37.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
6535
+ console.error(import_chalk39.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
6093
6536
  process.exit(1);
6094
6537
  }
6095
6538
  return c;
@@ -6100,7 +6543,7 @@ async function runConsumerLogin(opts) {
6100
6543
  let clientId = opts.client;
6101
6544
  if (clientId) {
6102
6545
  if (!tenant2) {
6103
- console.error(import_chalk37.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
6546
+ console.error(import_chalk39.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
6104
6547
  process.exit(1);
6105
6548
  }
6106
6549
  } else {
@@ -6112,25 +6555,25 @@ async function runConsumerLogin(opts) {
6112
6555
  if (!picked) process.exit(1);
6113
6556
  tenant2 = picked;
6114
6557
  }
6115
- const s2 = (0, import_ora19.default)("Finding the login app...").start();
6558
+ const s2 = (0, import_ora21.default)("Finding the login app...").start();
6116
6559
  const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
6117
6560
  s2.stop();
6118
6561
  const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
6119
6562
  const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
6120
6563
  if (!pick2) {
6121
- console.error(import_chalk37.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
6564
+ console.error(import_chalk39.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
6122
6565
  process.exit(1);
6123
6566
  }
6124
6567
  clientId = pick2.client_id ?? pick2.clientId;
6125
6568
  }
6126
6569
  const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
6127
- console.log(`${import_chalk37.default.cyan("\u2192")} Logging in to ${import_chalk37.default.bold(tenant2)} as a consumer...`);
6570
+ console.log(`${import_chalk39.default.cyan("\u2192")} Logging in to ${import_chalk39.default.bold(tenant2)} as a consumer...`);
6128
6571
  const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
6129
6572
  console.log(`
6130
- Open: ${import_chalk37.default.underline(verificationUri)}`);
6131
- console.log(` Code: ${import_chalk37.default.bold(userCode)}
6573
+ Open: ${import_chalk39.default.underline(verificationUri)}`);
6574
+ console.log(` Code: ${import_chalk39.default.bold(userCode)}
6132
6575
  `);
6133
- console.log(import_chalk37.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
6576
+ console.log(import_chalk39.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
6134
6577
  }, portalResource);
6135
6578
  const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
6136
6579
  const creds = {
@@ -6145,7 +6588,7 @@ async function runConsumerLogin(opts) {
6145
6588
  obtainedAt: Date.now()
6146
6589
  };
6147
6590
  saveConsumer(creds);
6148
- console.log(import_chalk37.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
6591
+ console.log(import_chalk39.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
6149
6592
  }
6150
6593
  async function runConsumerTokens(opts) {
6151
6594
  const creds = requireConsumer();
@@ -6158,29 +6601,29 @@ async function runConsumerTokens(opts) {
6158
6601
  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));
6159
6602
  return;
6160
6603
  }
6161
- console.log(`${import_chalk37.default.cyan("Consumer")} ${import_chalk37.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk37.default.bold(fresh.tenant)}
6604
+ console.log(`${import_chalk39.default.cyan("Consumer")} ${import_chalk39.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk39.default.bold(fresh.tenant)}
6162
6605
  `);
6163
- console.log(`${import_chalk37.default.bold("access_token")} ${import_chalk37.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
6606
+ console.log(`${import_chalk39.default.bold("access_token")} ${import_chalk39.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
6164
6607
  ${fresh.accessToken}
6165
6608
  `);
6166
- if (fresh.idToken) console.log(`${import_chalk37.default.bold("id_token")} ${import_chalk37.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
6609
+ if (fresh.idToken) console.log(`${import_chalk39.default.bold("id_token")} ${import_chalk39.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
6167
6610
  ${fresh.idToken}
6168
6611
  `);
6169
- if (fresh.refreshToken) console.log(`${import_chalk37.default.bold("refresh_token")}
6612
+ if (fresh.refreshToken) console.log(`${import_chalk39.default.bold("refresh_token")}
6170
6613
  ${fresh.refreshToken}
6171
6614
  `);
6172
- console.log(import_chalk37.default.dim("These are your own tokens \u2014 keep them secret."));
6615
+ console.log(import_chalk39.default.dim("These are your own tokens \u2014 keep them secret."));
6173
6616
  }
6174
6617
  async function runConsumerApikeys(opts) {
6175
6618
  const creds = requireConsumer();
6176
6619
  const { default: inquirer3 } = await import("inquirer");
6177
- const spinner = (0, import_ora19.default)("Loading your API keys...").start();
6620
+ const spinner = (0, import_ora21.default)("Loading your API keys...").start();
6178
6621
  const list = await consumerFetch(creds, "/apikeys");
6179
6622
  const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
6180
6623
  spinner.stop();
6181
6624
  if (list.status >= 400) {
6182
- console.error(import_chalk37.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
6183
- if (list.status === 401) console.error(import_chalk37.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
6625
+ console.error(import_chalk39.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
6626
+ if (list.status === 401) console.error(import_chalk39.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
6184
6627
  process.exit(1);
6185
6628
  }
6186
6629
  const keys = list.data?.keys ?? [];
@@ -6188,16 +6631,16 @@ async function runConsumerApikeys(opts) {
6188
6631
  if (opts.json) {
6189
6632
  console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
6190
6633
  } else if (!keys.length) {
6191
- console.log(import_chalk37.default.yellow("No API keys yet."));
6634
+ console.log(import_chalk39.default.yellow("No API keys yet."));
6192
6635
  } else {
6193
6636
  for (const k of keys) {
6194
6637
  const clear = revealMap[k.environment]?.key;
6195
- const shown = clear ? import_chalk37.default.green(clear) : import_chalk37.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
6196
- const exp = k.expires_at ? import_chalk37.default.dim(`exp ${k.expires_at}`) : import_chalk37.default.dim("no expiry");
6197
- console.log(` ${import_chalk37.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk37.default.dim(k.description ?? "")}`);
6638
+ const shown = clear ? import_chalk39.default.green(clear) : import_chalk39.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
6639
+ const exp = k.expires_at ? import_chalk39.default.dim(`exp ${k.expires_at}`) : import_chalk39.default.dim("no expiry");
6640
+ console.log(` ${import_chalk39.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk39.default.dim(k.description ?? "")}`);
6198
6641
  }
6199
6642
  if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
6200
- console.log(import_chalk37.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
6643
+ console.log(import_chalk39.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
6201
6644
  }
6202
6645
  }
6203
6646
  if (opts.json) return;
@@ -6211,7 +6654,7 @@ async function runConsumerApikeys(opts) {
6211
6654
  const body = { environment: answers.environment };
6212
6655
  if (answers.description) body.description = answers.description;
6213
6656
  if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
6214
- const s2 = (0, import_ora19.default)("Creating key...").start();
6657
+ const s2 = (0, import_ora21.default)("Creating key...").start();
6215
6658
  const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
6216
6659
  if (created.status >= 400) {
6217
6660
  s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
@@ -6219,16 +6662,17 @@ async function runConsumerApikeys(opts) {
6219
6662
  }
6220
6663
  s2.succeed("Key created.");
6221
6664
  const key = created.data?.key ?? created.data?.fullKey;
6222
- if (key) console.log(` ${import_chalk37.default.green(key)} ${import_chalk37.default.dim("(shown once \u2014 store it now)")}`);
6223
- else console.log(import_chalk37.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
6665
+ if (key) console.log(` ${import_chalk39.default.green(key)} ${import_chalk39.default.dim("(shown once \u2014 store it now)")}`);
6666
+ else console.log(import_chalk39.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
6224
6667
  }
6225
6668
 
6226
6669
  // src/commands/sidecar.ts
6227
- var import_chalk38 = __toESM(require("chalk"));
6228
- var import_ora20 = __toESM(require("ora"));
6670
+ var import_chalk40 = __toESM(require("chalk"));
6671
+ var import_ora22 = __toESM(require("ora"));
6229
6672
  var fs10 = __toESM(require("fs"));
6230
6673
  var path7 = __toESM(require("path"));
6231
6674
  init_admin();
6675
+ init_resolve();
6232
6676
  init_auth();
6233
6677
  function detectNextProject(root) {
6234
6678
  const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs10.existsSync(path7.join(root, f)));
@@ -6266,18 +6710,18 @@ function upsertEnvLocal(root, token) {
6266
6710
  }
6267
6711
  function installSidecarPackage(root) {
6268
6712
  if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
6269
- console.log(` ${import_chalk38.default.green("\u2713")} apiblaze package already installed`);
6713
+ console.log(` ${import_chalk40.default.green("\u2713")} apiblaze package already installed`);
6270
6714
  return;
6271
6715
  }
6272
6716
  const has = (f) => fs10.existsSync(path7.join(root, f));
6273
6717
  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" };
6274
- const spinner = (0, import_ora20.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
6718
+ const spinner = (0, import_ora22.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
6275
6719
  try {
6276
6720
  const { execSync } = require("child_process");
6277
6721
  execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
6278
6722
  spinner.succeed("Installed apiblaze (the sidecar runtime).");
6279
6723
  } catch {
6280
- spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk38.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk38.default.cyan("npm run dev")}.`);
6724
+ spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk40.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk40.default.cyan("npm run dev")}.`);
6281
6725
  }
6282
6726
  }
6283
6727
  function readEnvKey(root) {
@@ -6416,7 +6860,7 @@ async function runAnonymousInit(root, router, opts) {
6416
6860
  const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
6417
6861
  const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
6418
6862
  if (opts.newSession) clearAnonCred2();
6419
- const spinner = (0, import_ora20.default)("Setting up a sidecar (no login needed)...").start();
6863
+ const spinner = (0, import_ora22.default)("Setting up a sidecar (no login needed)...").start();
6420
6864
  let out;
6421
6865
  try {
6422
6866
  out = await sidecarInitAnonymous2();
@@ -6428,29 +6872,29 @@ async function runAnonymousInit(root, router, opts) {
6428
6872
  if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
6429
6873
  const envState = upsertEnvLocal(root, out.token);
6430
6874
  ensureGitignored(root);
6431
- console.log(` ${import_chalk38.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6432
- console.log(` ${import_chalk38.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6875
+ console.log(` ${import_chalk40.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6876
+ console.log(` ${import_chalk40.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6433
6877
  installSidecarPackage(root);
6434
6878
  let inspectorPath = null;
6435
6879
  if (!opts.noInspector) {
6436
6880
  inspectorPath = generateInspector(root, router);
6437
- if (inspectorPath) console.log(` ${import_chalk38.default.green("\u2713")} inspector at ${inspectorPath}`);
6881
+ if (inspectorPath) console.log(` ${import_chalk40.default.green("\u2713")} inspector at ${inspectorPath}`);
6438
6882
  }
6439
6883
  console.log("");
6440
- console.log(import_chalk38.default.bold("Done (no account needed). What happens next:"));
6441
- console.log(` 1. ${import_chalk38.default.cyan("npm run dev")} and use your app.`);
6884
+ console.log(import_chalk40.default.bold("Done (no account needed). What happens next:"));
6885
+ console.log(` 1. ${import_chalk40.default.cyan("npm run dev")} and use your app.`);
6442
6886
  console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
6443
- console.log(` ${import_chalk38.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
6887
+ console.log(` ${import_chalk40.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
6444
6888
  console.log("");
6445
- console.log(import_chalk38.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
6446
- console.log(` ${import_chalk38.default.cyan("apiblaze login")} then ${import_chalk38.default.cyan("apiblaze claim")} ${import_chalk38.default.dim("(no code needed here)")}`);
6447
- console.log(import_chalk38.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
6889
+ console.log(import_chalk40.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
6890
+ console.log(` ${import_chalk40.default.cyan("apiblaze login")} then ${import_chalk40.default.cyan("apiblaze claim")} ${import_chalk40.default.dim("(no code needed here)")}`);
6891
+ console.log(import_chalk40.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
6448
6892
  }
6449
6893
  async function runSidecar(opts) {
6450
6894
  const root = path7.resolve(opts.dir ?? process.cwd());
6451
6895
  const detected = detectNextProject(root);
6452
6896
  if (!detected.found) {
6453
- console.log(import_chalk38.default.yellow(`No Next.js project detected in ${root}.`));
6897
+ console.log(import_chalk40.default.yellow(`No Next.js project detected in ${root}.`));
6454
6898
  console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
6455
6899
  return;
6456
6900
  }
@@ -6461,10 +6905,10 @@ async function runSidecar(opts) {
6461
6905
  if (!loadCredentials()) {
6462
6906
  upsertEnvLocal(root, readEnvKey(root));
6463
6907
  ensureGitignored(root);
6464
- console.log(` ${import_chalk38.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
6465
- console.log(` ${import_chalk38.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6908
+ console.log(` ${import_chalk40.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
6909
+ console.log(` ${import_chalk40.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6466
6910
  installSidecarPackage(root);
6467
- console.log(import_chalk38.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
6911
+ console.log(import_chalk40.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
6468
6912
  return;
6469
6913
  }
6470
6914
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -6473,7 +6917,7 @@ async function runSidecar(opts) {
6473
6917
  const mustMint = !existingKey || opts.rotate || switchingTeam;
6474
6918
  let token = existingKey ?? "";
6475
6919
  if (mustMint) {
6476
- const spinner = (0, import_ora20.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
6920
+ const spinner = (0, import_ora22.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
6477
6921
  try {
6478
6922
  const out = await admin({
6479
6923
  method: "POST",
@@ -6487,40 +6931,41 @@ async function runSidecar(opts) {
6487
6931
  throw err;
6488
6932
  }
6489
6933
  } else {
6490
- console.log(import_chalk38.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
6934
+ console.log(import_chalk40.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
6491
6935
  }
6492
6936
  const envState = upsertEnvLocal(root, token);
6493
6937
  ensureGitignored(root);
6494
- console.log(` ${import_chalk38.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6938
+ console.log(` ${import_chalk40.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6495
6939
  const wireState = wireInstrumentation(root);
6496
- console.log(` ${import_chalk38.default.green("\u2713")} instrumentation.ts ${wireState}`);
6940
+ console.log(` ${import_chalk40.default.green("\u2713")} instrumentation.ts ${wireState}`);
6497
6941
  installSidecarPackage(root);
6498
6942
  let inspectorPath = null;
6499
6943
  if (!opts.noInspector) {
6500
6944
  inspectorPath = generateInspector(root, detected.router);
6501
- if (inspectorPath) console.log(` ${import_chalk38.default.green("\u2713")} inspector at ${inspectorPath}`);
6945
+ if (inspectorPath) console.log(` ${import_chalk40.default.green("\u2713")} inspector at ${inspectorPath}`);
6502
6946
  }
6503
6947
  console.log("");
6504
- console.log(import_chalk38.default.bold("Done. What happens next:"));
6505
- console.log(` 1. ${import_chalk38.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
6506
- console.log(` 2. The origins your app calls appear as ${import_chalk38.default.bold("candidates")} \u2014 list them: ${import_chalk38.default.cyan("apiblaze sidecar")}`);
6507
- console.log(` 3. Approve the ones to route: ${import_chalk38.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
6948
+ console.log(import_chalk40.default.bold("Done. What happens next:"));
6949
+ console.log(` 1. ${import_chalk40.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
6950
+ console.log(` 2. The origins your app calls appear as ${import_chalk40.default.bold("candidates")} \u2014 list them: ${import_chalk40.default.cyan("apiblaze sidecar")}`);
6951
+ console.log(` 3. Approve the ones to route: ${import_chalk40.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
6508
6952
  console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
6509
- if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk38.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
6510
- if (switchingTeam) console.log(import_chalk38.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
6953
+ if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk40.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
6954
+ if (switchingTeam) console.log(import_chalk40.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
6511
6955
  console.log("");
6512
- console.log(import_chalk38.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
6513
- console.log(import_chalk38.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
6514
- console.log(import_chalk38.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
6956
+ console.log(import_chalk40.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
6957
+ console.log(import_chalk40.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
6958
+ console.log(import_chalk40.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
6515
6959
  console.log("");
6516
- console.log(import_chalk38.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
6517
- console.log(import_chalk38.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
6960
+ console.log(import_chalk40.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
6961
+ console.log(import_chalk40.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
6518
6962
  }
6519
6963
 
6520
6964
  // src/commands/origins.ts
6521
- var import_chalk39 = __toESM(require("chalk"));
6522
- var import_ora21 = __toESM(require("ora"));
6965
+ var import_chalk41 = __toESM(require("chalk"));
6966
+ var import_ora23 = __toESM(require("ora"));
6523
6967
  init_admin();
6968
+ init_resolve();
6524
6969
  init_auth();
6525
6970
  init_anon_cred();
6526
6971
  async function runOriginsList(opts) {
@@ -6528,7 +6973,7 @@ async function runOriginsList(opts) {
6528
6973
  if (!loadCredentials()) {
6529
6974
  const cred = loadAnonCred();
6530
6975
  if (!cred) {
6531
- console.log(import_chalk39.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
6976
+ console.log(import_chalk41.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
6532
6977
  return;
6533
6978
  }
6534
6979
  out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
@@ -6546,30 +6991,30 @@ async function runOriginsList(opts) {
6546
6991
  }
6547
6992
  const routed = out.routed ?? [];
6548
6993
  const candidates = out.candidates ?? [];
6549
- console.log(import_chalk39.default.bold(`
6994
+ console.log(import_chalk41.default.bold(`
6550
6995
  Routed through APIblaze (${routed.length})`));
6551
- if (!routed.length) console.log(import_chalk39.default.dim(" none yet"));
6552
- for (const r of routed) console.log(` ${import_chalk39.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk39.default.dim(`\u2192 ${r.project_id}`)}`);
6553
- console.log(import_chalk39.default.bold(`
6996
+ if (!routed.length) console.log(import_chalk41.default.dim(" none yet"));
6997
+ for (const r of routed) console.log(` ${import_chalk41.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk41.default.dim(`\u2192 ${r.project_id}`)}`);
6998
+ console.log(import_chalk41.default.bold(`
6554
6999
  Candidates \u2014 going direct, not yet approved (${candidates.length})`));
6555
- if (!candidates.length) console.log(import_chalk39.default.dim(" none \u2014 run your app to discover the origins it calls"));
7000
+ if (!candidates.length) console.log(import_chalk41.default.dim(" none \u2014 run your app to discover the origins it calls"));
6556
7001
  for (const c of candidates) {
6557
- console.log(` ${import_chalk39.default.yellow("\u25CB")} ${c.origin} ${import_chalk39.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
7002
+ console.log(` ${import_chalk41.default.yellow("\u25CB")} ${c.origin} ${import_chalk41.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
6558
7003
  }
6559
7004
  if (candidates.length) {
6560
- console.log(import_chalk39.default.dim(`
7005
+ console.log(import_chalk41.default.dim(`
6561
7006
  Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
6562
- console.log(import_chalk39.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
7007
+ console.log(import_chalk41.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
6563
7008
  }
6564
7009
  }
6565
7010
  async function runOriginsApprove(origin, opts) {
6566
7011
  if (!loadCredentials()) {
6567
7012
  const cred = loadAnonCred();
6568
7013
  if (!cred) {
6569
- console.error(import_chalk39.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
7014
+ console.error(import_chalk41.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
6570
7015
  process.exit(1);
6571
7016
  }
6572
- const spinner2 = (0, import_ora21.default)(`Approving ${origin} (anonymous)...`).start();
7017
+ const spinner2 = (0, import_ora23.default)(`Approving ${origin} (anonymous)...`).start();
6573
7018
  try {
6574
7019
  const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
6575
7020
  spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
@@ -6580,7 +7025,7 @@ async function runOriginsApprove(origin, opts) {
6580
7025
  return;
6581
7026
  }
6582
7027
  const { teamId } = await resolveTeam(opts.team);
6583
- const spinner = (0, import_ora21.default)(`Approving ${origin}...`).start();
7028
+ const spinner = (0, import_ora23.default)(`Approving ${origin}...`).start();
6584
7029
  try {
6585
7030
  const out = await admin({
6586
7031
  method: "POST",
@@ -6597,7 +7042,7 @@ async function runOriginsApprove(origin, opts) {
6597
7042
  }
6598
7043
  async function runOriginsDeny(origin, opts) {
6599
7044
  const { teamId } = await resolveTeam(opts.team);
6600
- const spinner = (0, import_ora21.default)(`Dismissing ${origin}...`).start();
7045
+ const spinner = (0, import_ora23.default)(`Dismissing ${origin}...`).start();
6601
7046
  try {
6602
7047
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
6603
7048
  spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
@@ -6608,7 +7053,7 @@ async function runOriginsDeny(origin, opts) {
6608
7053
  }
6609
7054
  async function runOriginsRemove(origin, opts) {
6610
7055
  const { teamId } = await resolveTeam(opts.team);
6611
- const spinner = (0, import_ora21.default)(`Removing the proxy for ${origin}...`).start();
7056
+ const spinner = (0, import_ora23.default)(`Removing the proxy for ${origin}...`).start();
6612
7057
  try {
6613
7058
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
6614
7059
  spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
@@ -6619,19 +7064,19 @@ async function runOriginsRemove(origin, opts) {
6619
7064
  }
6620
7065
 
6621
7066
  // src/commands/op.ts
6622
- var import_chalk40 = __toESM(require("chalk"));
7067
+ var import_chalk42 = __toESM(require("chalk"));
6623
7068
  init_auth();
6624
7069
  init_trace();
6625
7070
  init_types();
6626
7071
  var OPERATOR_EMAILS = /* @__PURE__ */ new Set(["julienpmjacquet@gmail.com", "chkev@umich.edu"]);
6627
- var DASHBOARD_BASE7 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
7072
+ var DASHBOARD_BASE8 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
6628
7073
  function isOperatorLogin() {
6629
7074
  const email = loadCredentials()?.email?.toLowerCase();
6630
7075
  return !!email && OPERATOR_EMAILS.has(email);
6631
7076
  }
6632
7077
  async function opCall(call) {
6633
7078
  const token = getAccessToken();
6634
- const res = await fetch(`${DASHBOARD_BASE7}/api/cli/op`, {
7079
+ const res = await fetch(`${DASHBOARD_BASE8}/api/cli/op`, {
6635
7080
  method: "POST",
6636
7081
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
6637
7082
  body: JSON.stringify({ path: call.path, method: call.method, body: call.body })
@@ -6652,82 +7097,82 @@ function printResidue(report, applied) {
6652
7097
  const up = report?.upstash ?? {};
6653
7098
  const fga = report?.fga ?? {};
6654
7099
  const ghosts = report?.ghosts ?? {};
6655
- console.log(import_chalk40.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
6656
- console.log(import_chalk40.default.bold("\n Upstash"));
7100
+ console.log(import_chalk42.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
7101
+ console.log(import_chalk42.default.bold("\n Upstash"));
6657
7102
  const orphans = up.orphans ?? [];
6658
- if (orphans.length === 0) console.log(import_chalk40.default.green(" no orphaned keys"));
6659
- for (const o of orphans) console.log(` ${import_chalk40.default.yellow(o.key)} ${import_chalk40.default.dim(`\u2014 ${o.reason}`)}`);
6660
- console.log(import_chalk40.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
7103
+ if (orphans.length === 0) console.log(import_chalk42.default.green(" no orphaned keys"));
7104
+ for (const o of orphans) console.log(` ${import_chalk42.default.yellow(o.key)} ${import_chalk42.default.dim(`\u2014 ${o.reason}`)}`);
7105
+ console.log(import_chalk42.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
6661
7106
  if (up.anon_wallet_detail) {
6662
7107
  const d = up.anon_wallet_detail;
6663
- console.log(import_chalk40.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)"}`));
7108
+ console.log(import_chalk42.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)"}`));
6664
7109
  }
6665
7110
  if (up.keyspace_census) {
6666
7111
  const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
6667
- console.log(import_chalk40.default.dim(` keyspace: ${census}`));
7112
+ console.log(import_chalk42.default.dim(` keyspace: ${census}`));
6668
7113
  }
6669
- if (up.unknown?.length) console.log(import_chalk40.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
6670
- if (applied) console.log(` ${import_chalk40.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
6671
- for (const e of up.errors ?? []) console.log(import_chalk40.default.red(` error: ${e}`));
6672
- console.log(import_chalk40.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
7114
+ if (up.unknown?.length) console.log(import_chalk42.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
7115
+ if (applied) console.log(` ${import_chalk42.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
7116
+ for (const e of up.errors ?? []) console.log(import_chalk42.default.red(` error: ${e}`));
7117
+ console.log(import_chalk42.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
6673
7118
  if (applied) {
6674
7119
  const swept = fga?.swept ?? [];
6675
- if (swept.length === 0) console.log(import_chalk40.default.green(" no orphaned stores"));
7120
+ if (swept.length === 0) console.log(import_chalk42.default.green(" no orphaned stores"));
6676
7121
  for (const s of swept) {
6677
7122
  console.log(
6678
- ` ${import_chalk40.default.yellow(s.store_id)} ${import_chalk40.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
7123
+ ` ${import_chalk42.default.yellow(s.store_id)} ${import_chalk42.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
6679
7124
  );
6680
7125
  }
6681
- if (fga?.remaining) console.log(import_chalk40.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
7126
+ if (fga?.remaining) console.log(import_chalk42.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
6682
7127
  const st = fga?.side_tables;
6683
- if (st) console.log(import_chalk40.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})` : ""}`));
7128
+ if (st) console.log(import_chalk42.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})` : ""}`));
6684
7129
  } else {
6685
7130
  const fgaOrphans = fga?.orphans ?? [];
6686
- if (fgaOrphans.length === 0) console.log(import_chalk40.default.green(" no orphaned stores"));
7131
+ if (fgaOrphans.length === 0) console.log(import_chalk42.default.green(" no orphaned stores"));
6687
7132
  for (const s of fgaOrphans) {
6688
7133
  const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
6689
- console.log(` ${import_chalk40.default.yellow(s.store_id)} ${import_chalk40.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
7134
+ console.log(` ${import_chalk42.default.yellow(s.store_id)} ${import_chalk42.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
6690
7135
  }
6691
- console.log(import_chalk40.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
7136
+ console.log(import_chalk42.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
6692
7137
  const st = fga?.side_tables;
6693
- if (st) console.log(import_chalk40.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`));
7138
+ if (st) console.log(import_chalk42.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`));
6694
7139
  }
6695
- for (const e of fga?.errors ?? []) console.log(import_chalk40.default.red(` error: ${e}`));
6696
- console.log(import_chalk40.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
7140
+ for (const e of fga?.errors ?? []) console.log(import_chalk42.default.red(` error: ${e}`));
7141
+ console.log(import_chalk42.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
6697
7142
  if (applied) {
6698
- if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk40.default.green(" no ghost tuples"));
6699
- else console.log(` ${import_chalk40.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk40.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
7143
+ if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk42.default.green(" no ghost tuples"));
7144
+ else console.log(` ${import_chalk42.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk42.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
6700
7145
  } else {
6701
7146
  const n = ghosts?.ghost_count ?? 0;
6702
- if (n === 0) console.log(import_chalk40.default.green(` no ghost tuples ${import_chalk40.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
7147
+ if (n === 0) console.log(import_chalk42.default.green(` no ghost tuples ${import_chalk42.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
6703
7148
  else {
6704
- console.log(import_chalk40.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
7149
+ console.log(import_chalk42.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
6705
7150
  for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
6706
- console.log(import_chalk40.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
7151
+ console.log(import_chalk42.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
6707
7152
  }
6708
- if (n > 20) console.log(import_chalk40.default.dim(` \u2026 and ${n - 20} more`));
7153
+ if (n > 20) console.log(import_chalk42.default.dim(` \u2026 and ${n - 20} more`));
6709
7154
  }
6710
7155
  }
6711
- for (const e of ghosts?.errors ?? []) console.log(import_chalk40.default.red(` error: ${e}`));
7156
+ for (const e of ghosts?.errors ?? []) console.log(import_chalk42.default.red(` error: ${e}`));
6712
7157
  console.log();
6713
7158
  }
6714
7159
  async function runOp(sub, opts = {}) {
6715
7160
  if (!loadCredentials()) {
6716
- console.log(import_chalk40.default.dim("Not logged in. Run `apiblaze login`."));
7161
+ console.log(import_chalk42.default.dim("Not logged in. Run `apiblaze login`."));
6717
7162
  return;
6718
7163
  }
6719
7164
  if (!isOperatorLogin()) {
6720
- console.log(import_chalk40.default.dim("`apiblaze op` is only available to platform operators."));
7165
+ console.log(import_chalk42.default.dim("`apiblaze op` is only available to platform operators."));
6721
7166
  return;
6722
7167
  }
6723
7168
  switch (sub) {
6724
7169
  case void 0:
6725
7170
  case "menu": {
6726
- console.log(import_chalk40.default.bold("\nOperator menu"));
6727
- console.log(` ${import_chalk40.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
6728
- console.log(` ${import_chalk40.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk40.default.dim("-y to skip")})`);
6729
- console.log(` ${import_chalk40.default.cyan("apiblaze op credits")} list credit wallets`);
6730
- console.log(import_chalk40.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
7171
+ console.log(import_chalk42.default.bold("\nOperator menu"));
7172
+ console.log(` ${import_chalk42.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
7173
+ console.log(` ${import_chalk42.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk42.default.dim("-y to skip")})`);
7174
+ console.log(` ${import_chalk42.default.cyan("apiblaze op credits")} list credit wallets`);
7175
+ console.log(import_chalk42.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
6731
7176
  `));
6732
7177
  return;
6733
7178
  }
@@ -6746,17 +7191,17 @@ async function runOp(sub, opts = {}) {
6746
7191
  const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
6747
7192
  printResidue(report, false);
6748
7193
  if (nUp + nFga + nGhost + nSide === 0) {
6749
- console.log(import_chalk40.default.green("Nothing to sweep."));
7194
+ console.log(import_chalk42.default.green("Nothing to sweep."));
6750
7195
  return;
6751
7196
  }
6752
7197
  if (!opts.yes) {
6753
7198
  const readline2 = await import("readline/promises");
6754
7199
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
6755
7200
  const answer = await rl.question(
6756
- import_chalk40.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: `)
7201
+ import_chalk42.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: `)
6757
7202
  );
6758
7203
  rl.close();
6759
- if (answer.trim() !== "sweep") return void console.log(import_chalk40.default.dim("Aborted."));
7204
+ if (answer.trim() !== "sweep") return void console.log(import_chalk42.default.dim("Aborted."));
6760
7205
  }
6761
7206
  const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
6762
7207
  if (opts.json) return void console.log(JSON.stringify(result, null, 2));
@@ -6767,15 +7212,15 @@ async function runOp(sub, opts = {}) {
6767
7212
  const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
6768
7213
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
6769
7214
  const accounts = data?.accounts ?? [];
6770
- if (accounts.length === 0) return void console.log(import_chalk40.default.dim("No credit wallets."));
7215
+ if (accounts.length === 0) return void console.log(import_chalk42.default.dim("No credit wallets."));
6771
7216
  for (const a of accounts) {
6772
7217
  const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
6773
- console.log(` ${import_chalk40.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk40.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk40.default.dim(" \u2014 anon") : ""}`);
7218
+ console.log(` ${import_chalk42.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk42.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk42.default.dim(" \u2014 anon") : ""}`);
6774
7219
  }
6775
7220
  return;
6776
7221
  }
6777
7222
  default:
6778
- console.log(import_chalk40.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
7223
+ console.log(import_chalk42.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
6779
7224
  }
6780
7225
  }
6781
7226
 
@@ -6808,7 +7253,7 @@ program.command("login").description("Authenticate with APIblaze").action(async
6808
7253
  process.exit(1);
6809
7254
  }
6810
7255
  });
6811
- program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.abz.run)").option("--target <url>", "Target URL to forward requests to").option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("--apiversion <version>", "API version to create (e.g. 2.0.0). Creating a new version of a proxy you own adds a version to the existing project.").option("--product <slug>", "Product tag to group this project under in the portal (team-scoped; anonymous create). Defaults to your team's existing/placeholder tag.").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--new-session", "Start a fresh anonymous session (do not group with prior anonymous creates)").option("--json", "Output machine-readable JSON (non-interactive)").action(async (opts) => {
7256
+ program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.abz.run)").option("--target <url>", "Target URL to forward requests to").option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("--identified", "Require every call to identify its end user (X-End-User-Id or a login token); unattributed calls are rejected").option("--iam", "Turn IAM on for the proxy's tenant so users & groups apply to identified calls").option("--apiversion <version>", "API version to create (e.g. 2.0.0). Creating a new version of a proxy you own adds a version to the existing project.").option("--product <slug>", "Product tag to group this project under in the portal (team-scoped; anonymous create). Defaults to your team's existing/placeholder tag.").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--new-session", "Start a fresh anonymous session (do not group with prior anonymous creates)").option("--json", "Output machine-readable JSON (non-interactive)").action(async (opts) => {
6812
7257
  try {
6813
7258
  await runCreate(opts);
6814
7259
  } catch (err) {
@@ -6821,7 +7266,7 @@ agent.command("authz").description("Chat to design and turn on access rules for
6821
7266
  program.command("rule").description("Author an object-level access rule in plain English, in one shot (billed per turn)").argument("<rule>", 'The rule in plain English, e.g. "users see only their own rows"').argument("<project>", "Project name or id").option("--enforce", "Turn enforcement on immediately (default: shadow-publish only)").option("--apiversion <version>", "API version (defaults to the project's)").action(action((rule, project, opts) => runRule(rule, project, opts)));
6822
7267
  agent.command("openapi").description("Chat to build your API spec from real traffic").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").action(action((project, apiVersion) => runOpenapi(project, apiVersion)));
6823
7268
  agent.command("mcp").description("Chat to build an MCP server for an API").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").option("--environment <env>", "Environment to publish (default: prod)").action(action((project, apiVersion, opts) => runMcp(project, apiVersion, opts)));
6824
- program.command("apichat").description("Turn any API into a chat: point at an OpenAPI spec and start asking questions (no login needed)").option("--openapispec <file|url>", "OpenAPI/Swagger spec to build from (JSON or YAML; file path or URL)").option("--target <url>", "Upstream base URL (overrides servers[0].url; enables spec discovery when --openapispec is omitted)").option("--name <name>", "Proxy name (defaults to the target host)").option("--apiversion <version>", "API version to create (e.g. 1.0.0)").option("--environment <env>", "Environment to chat against (default: prod anonymous / dev logged-in)").option("--target-auth-env <ENV_VAR>", "Read the upstream credential from this env var (CI-safe; required when there is no TTY and the API needs auth)").option("--force", "Proceed even if the API uses oauth2/openIdConnect target auth (you configure target auth yourself later)").option("-y, --yes", "Skip confirmation prompts").option("--no-verbose", "Hide the per-turn proxy curl trace (shown by default for apichat)").action(action((opts) => runApichat(opts)));
7269
+ program.command("apichat").description("Turn any API into a chat: point at an OpenAPI spec and start asking questions (no login needed)").option("--openapi <file|url>", "OpenAPI/Swagger spec to build from (JSON or YAML; file path or URL)").option("--openapispec <file|url>", "Alias for --openapi").option("--target <url>", "Upstream base URL (overrides servers[0].url; enables spec discovery when --openapi is omitted)").option("--name <name>", "Proxy name (defaults to the target host)").option("--apiversion <version>", "API version to create (e.g. 1.0.0)").option("--environment <env>", "Environment to chat against (default: prod anonymous / dev logged-in)").option("--access <mode>", 'Who can call this API once connected (e.g. via Claude): "open" = anyone who signs in, "invite" = only you + emails you pre-approve. Default: invite when logged in, open when anonymous.').option("--target-auth-env <ENV_VAR>", "Read the upstream credential from this env var (CI-safe; required when there is no TTY and the API needs auth)").option("--force", "Proceed even if the API uses oauth2/openIdConnect target auth (you configure target auth yourself later)").option("-y, --yes", "Skip confirmation prompts").option("--no-verbose", "Hide the per-turn proxy curl trace (shown by default for apichat)").action(action((opts) => runApichat({ ...opts, openapispec: opts.openapispec ?? opts.openapi })));
6825
7270
  var llm = program.command("llm").description("Manage a local LLM provider key for chat (optional \u2014 lifts model quality, bills your key)");
6826
7271
  llm.command("set-key").description("Store an LLM provider key locally (OpenRouter/Anthropic/DeepSeek/OpenAI)").argument("[key]", "The API key (omit to enter it hidden at a prompt)").option("--model <id>", "Model id to use with this key (e.g. anthropic/claude-haiku-4.5)").action(action((key, opts) => runLlmSetKey(key, opts)));
6827
7272
  llm.command("show").description("Show the locally stored LLM key (masked)").action(action(() => runLlmShow()));
@@ -6838,7 +7283,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
6838
7283
  try {
6839
7284
  const resolved = parseInt(port ?? opts.port, 10);
6840
7285
  if (Number.isNaN(resolved)) {
6841
- console.error(import_chalk41.default.red(`Invalid port: ${port ?? opts.port}`));
7286
+ console.error(import_chalk43.default.red(`Invalid port: ${port ?? opts.port}`));
6842
7287
  process.exit(1);
6843
7288
  }
6844
7289
  await runDev({ port: resolved, captureFile: opts.captureFile });
@@ -6907,6 +7352,7 @@ group.command("remove-user").description("Remove a user from a group").argument(
6907
7352
  group.command("add-group").description("Nest a group inside another (e.g. `group add-group reservationists admin`)").argument("<child>", "Child group name").argument("<parent>", "Parent group name").option("--tenant <slug>", "Tenant").action(action((child, parent, opts) => runGroupAddGroup(child, parent, opts)));
6908
7353
  group.command("remove-group").description("Un-nest a subgroup from its parent").argument("<child>", "Child group name").argument("<parent>", "Parent group name").option("--tenant <slug>", "Tenant").action(action((child, parent, opts) => runGroupRemoveGroup(child, parent, opts)));
6909
7354
  group.command("users").description("List the tenant user directory + identities seen in traffic").option("--tenant <slug>", "Tenant").option("--json", "Machine-readable output").action(action((opts) => runGroupUsers(opts)));
7355
+ program.command("preapprove").description("Allow an email or company domain to sign in to an access-restricted API (--list, --remove)").argument("[email-or-domain]", "Email (someone@acme.com) or whole company domain (acme.com)").option("--tenant <slug>", "Tenant (defaults to your team's only tenant, or prompts)").option("--list", "List the current pre-approval allow-list").option("--remove", "Remove the given email/domain from the allow-list").option("--group <names...>", "Group(s) to auto-assign the user to on first sign-in").option("--json", "Machine-readable output (with --list)").action(action((who, opts) => runPreapprove(who, opts)));
6910
7356
  var tenant = program.command("tenant").description("Manage tenants \u2014 bare command opens the interactive picker (settings, app clients, providers)").action(action(() => runTenantManage(void 0, {})));
6911
7357
  tenant.command("manage").description("Browse & edit one tenant: settings, login app clients, providers, issuers (search-first picker)").argument("[query]", "Search by tenant name/display name (omit to use your tenant scope or pick)").option("--tenant <slug>", "Exact tenant slug (skips the picker)").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantManage(query, opts)));
6912
7358
  tenant.command("use").description("Set the sticky tenant scope for future commands (search-first; --clear to unset)").argument("[query]", "Search by tenant name/display name").option("--clear", "Clear the tenant scope").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantUse(query, opts)));
@@ -6915,6 +7361,8 @@ tenant.command("create").description("Create a tenant in your team (tenant names
6915
7361
  tenant.command("attach").description("Attach a tenant to a proxy").argument("<project>", "Project name or id").requiredOption("--tenant <slug>", "Tenant slug to attach").option("--auth-config <id>", "Auth config id to bind").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTenantAttach(project, opts)));
6916
7362
  tenant.command("delete").description("Delete a tenant (full cascade)").argument("<slug>", "Tenant slug to delete").option("--team <id|name>", "Team (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").action(action((slug, opts) => runTenantDelete(slug, opts)));
6917
7363
  tenant.command("cors").description("Set the CORS allow-list for a tenant").requiredOption("--tenant <slug>", "Tenant slug").option("--origins <list>", 'Comma-separated origins (or "*"); empty clears').option("--team <id|name>", "Team (defaults to active team)").action(action((opts) => runTenantCors(opts)));
7364
+ program.command("iam").description("Turn users & groups on/off for a proxy's tenant (identified calls get their user's groups applied)").argument("<project>", "Project name or id").argument("<state>", "on | off").option("--tenant <name>", "Tenant to toggle (defaults to the project's tenant)").option("--team <id|name>", "Team (defaults to active team, or your anonymous workspace)").option("--apiversion <version>", "API version (defaults to the first)").option("--json", "Output machine-readable JSON").action(action((project, state, opts) => runIamToggle(project, state, opts)));
7365
+ program.command("identified").description("Require calls to identify their end user (X-End-User-Id or a login token) \u2014 or allow unattributed calls again").argument("<project>", "Project name or id").argument("<mode>", "require | allow-anon").option("--team <id|name>", "Team (defaults to active team, or your anonymous workspace)").option("--apiversion <version>", "API version (defaults to the first)").option("--json", "Output machine-readable JSON").action(action((project, mode, opts) => runIdentifiedToggle(project, mode, opts)));
6918
7366
  var apikeys = new import_commander.Command("apikeys").description("Producer control-plane API keys (list, then offer to create one)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runApikeysMenu(opts)));
6919
7367
  apikeys.command("list").description("List control-plane API keys in your team").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runKeyList(opts)));
6920
7368
  apikeys.command("mint").description("Create a control-plane API key (secret shown once)").option("--desc <text>", "Description").option("--expires-days <n>", "Expiry in days (default 90 server-side)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runKeyMint(opts)));
@@ -6925,10 +7373,12 @@ program.addCommand(op, { hidden: true });
6925
7373
  var spec = program.command("spec").description("View or update a proxy's OpenAPI spec (or build one by chatting: apiblaze agent openapi)");
6926
7374
  spec.command("get").description("Print the current OpenAPI document").argument("<project>", "Project name or id").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Compact JSON output").action(action((project, opts) => runSpecGet(project, opts)));
6927
7375
  spec.command("set").description("Replace the stored OpenAPI spec from a local file").argument("<project>", "Project name or id").requiredOption("--file <path>", "OpenAPI JSON or YAML file to upload").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runSpecSet(project, opts)));
7376
+ spec.command("carry").description("Copy a route's saved rules to the path a spec rename moved traffic to").argument("<project>", "Project name or id").requiredOption("--method <method>", "HTTP method of the route (GET, POST, ...)").requiredOption("--from <path>", "Old route path, e.g. /owners/{ownerId}").requiredOption("--to <path>", "New route path, e.g. /customers/{customerId}").option("--delete-source", "Also delete the old rules after carrying").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runSpecCarry(project, opts)));
7377
+ spec.command("delete-rule").description("Delete the saved rules for a route (e.g. an orphan left by a spec change)").argument("<project>", "Project name or id").requiredOption("--method <method>", "HTTP method of the route").requiredOption("--route <path>", "Route path, e.g. /owners/{ownerId}").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runSpecDeleteRule(project, opts)));
6928
7378
  var HELP_GROUPS = [
6929
7379
  { title: "Chat", commands: ["apichat", "agent"] },
6930
7380
  { title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
6931
- { title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
7381
+ { title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
6932
7382
  { title: "Data plane commands", commands: [
6933
7383
  { parent: "consumer", sub: "login" },
6934
7384
  { parent: "consumer", sub: "apikeys" }
@@ -6947,7 +7397,7 @@ function groupedCommandHelp() {
6947
7397
  const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
6948
7398
  return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
6949
7399
  }).filter(Boolean).join("\n");
6950
- return `${import_chalk41.default.bold(g.title)}
7400
+ return `${import_chalk43.default.bold(g.title)}
6951
7401
  ${rows}`;
6952
7402
  }).join("\n\n");
6953
7403
  }
@@ -6967,7 +7417,7 @@ Tips:
6967
7417
  \u2022 Run \`apiblaze <command> --help\` (e.g. \`apiblaze consumer --help\`) for sub-commands.
6968
7418
 
6969
7419
  Examples:
6970
- $ npx apiblaze apichat --openapispec https://pokeapi.co/openapi.yaml # chat with any API
7420
+ $ npx apiblaze apichat --openapi https://pokeapi.co/openapi.yaml # chat with any API
6971
7421
  $ npx apiblaze agent # just chat
6972
7422
  $ npx apiblaze create --target https://api.example.com # one-line API
6973
7423
  $ npx apiblaze dev 3000 # localhost \u2192 public URL
@@ -6981,14 +7431,14 @@ async function recoverStaleTeam() {
6981
7431
  const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
6982
7432
  const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
6983
7433
  if (!linked) {
6984
- console.error(import_chalk41.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
7434
+ console.error(import_chalk43.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
6985
7435
  return;
6986
7436
  }
6987
7437
  if (linked.teamId === creds.teamId) return;
6988
7438
  const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
6989
7439
  delete next.activeTenant;
6990
7440
  saveCredentials(next);
6991
- console.error(import_chalk41.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk41.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
7441
+ console.error(import_chalk43.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk43.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
6992
7442
  } catch {
6993
7443
  }
6994
7444
  }
@@ -6996,16 +7446,16 @@ async function printError(err) {
6996
7446
  if (err instanceof ApiError) {
6997
7447
  const data = err.body;
6998
7448
  const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
6999
- console.error(import_chalk41.default.red(`
7449
+ console.error(import_chalk43.default.red(`
7000
7450
  API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
7001
7451
  if (err.status === 403 || err.status === 404) {
7002
7452
  await recoverStaleTeam();
7003
7453
  }
7004
7454
  } else if (err instanceof Error) {
7005
- console.error(import_chalk41.default.red(`
7455
+ console.error(import_chalk43.default.red(`
7006
7456
  Error: ${err.message}`));
7007
7457
  } else {
7008
- console.error(import_chalk41.default.red("\nUnknown error"));
7458
+ console.error(import_chalk43.default.red("\nUnknown error"));
7009
7459
  }
7010
7460
  }
7011
7461
  program.parse(process.argv);