apiblaze 0.19.1 → 0.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +386 -305
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -577,10 +577,10 @@ async function resolveProjectVia(caller, teamId, nameOrId, version2) {
577
577
  summary: `List projects for ${teamId}`
578
578
  });
579
579
  const rows = out?.projects ?? [];
580
- const candidates = rows.filter((r) => r.project_id === nameOrId || r.project_name === nameOrId);
580
+ const candidates = rows.filter((r) => r.project_id === nameOrId || r.project_display_name === nameOrId || r.display_name === nameOrId);
581
581
  if (!candidates.length) {
582
582
  throw new Error(
583
- `Project "${nameOrId}" not found in this team.` + (rows.length ? ` Known: ${rows.map((r) => r.project_name).join(", ")}` : "")
583
+ `Project "${nameOrId}" not found in this team.` + (rows.length ? ` Known: ${rows.map((r) => r.project_id).join(", ")}` : "")
584
584
  );
585
585
  }
586
586
  const chosen = version2 ? candidates.find((r) => r.api_version === version2) : candidates[0];
@@ -590,11 +590,12 @@ async function resolveProjectVia(caller, teamId, nameOrId, version2) {
590
590
  );
591
591
  }
592
592
  return {
593
+ // A proxy is addressed by its project_id (that IS its name); display_name is cosmetic.
593
594
  projectId: chosen.project_id,
594
- projectName: chosen.project_name,
595
+ projectName: chosen.project_display_name ?? chosen.display_name ?? chosen.project_id,
595
596
  apiVersion: chosen.api_version,
596
597
  teamId,
597
- tenant: chosen.tenant ?? chosen.config?.tenant
598
+ tenant: chosen.config?.tenant ?? chosen.config?.default_tenant
598
599
  };
599
600
  }
600
601
  var init_caller = __esm({
@@ -626,15 +627,16 @@ async function runIamToggle(project, state, opts) {
626
627
  if (!tenant2) {
627
628
  throw new Error("Could not resolve the tenant for this project. Pass --tenant <name>.");
628
629
  }
629
- const spinner = (0, import_ora4.default)(`Turning IAM ${state} for tenant ${tenant2}...`).start();
630
+ const spinner = opts.quiet ? null : (0, import_ora4.default)(`Turning IAM ${state} for tenant ${tenant2}...`).start();
630
631
  try {
631
632
  const out = await producer(caller, {
632
633
  method: "PATCH",
633
634
  path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`,
634
- body: { iam_enabled: enabled },
635
+ body: { enabled },
635
636
  summary: `IAM ${state} for tenant ${tenant2}`
636
637
  });
637
- spinner.succeed(`IAM is ${import_chalk9.default.bold(state)} for tenant ${tenant2}.`);
638
+ if (opts.quiet) return;
639
+ spinner?.succeed(`IAM is ${import_chalk9.default.bold(state)} for tenant ${tenant2}.`);
638
640
  if (opts.json) console.log(JSON.stringify(out ?? { iam_enabled: enabled }));
639
641
  else if (enabled) {
640
642
  console.log(import_chalk9.default.dim(" Groups now apply to identified calls. Identify callers with"));
@@ -642,7 +644,7 @@ async function runIamToggle(project, state, opts) {
642
644
  console.log(import_chalk9.default.dim(` \`apiblaze identified ${project} require\` to reject unattributed traffic.`));
643
645
  }
644
646
  } catch (err) {
645
- spinner.fail(`IAM ${state} failed.`);
647
+ spinner?.fail(`IAM ${state} failed.`);
646
648
  throw err;
647
649
  }
648
650
  }
@@ -654,7 +656,7 @@ async function runIdentifiedToggle(project, mode, opts) {
654
656
  const caller = requireCaller();
655
657
  const { teamId } = await resolveActingTeam(caller, opts.team);
656
658
  const proj2 = await resolveProjectVia(caller, teamId, project, opts.apiversion);
657
- const spinner = (0, import_ora4.default)(
659
+ const spinner = opts.quiet ? null : (0, import_ora4.default)(
658
660
  require2 ? "Requiring identified traffic..." : "Allowing unattributed traffic..."
659
661
  ).start();
660
662
  try {
@@ -681,12 +683,13 @@ async function runIdentifiedToggle(project, mode, opts) {
681
683
  body: { requests_policy: merged },
682
684
  summary: `Set identified-traffic policy (${mode}) on ${proj2.projectName}`
683
685
  });
684
- spinner.succeed(
686
+ if (opts.quiet) return;
687
+ spinner?.succeed(
685
688
  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
689
  );
687
690
  if (opts.json) console.log(JSON.stringify(out ?? { identified_traffic_only: require2 }));
688
691
  } catch (err) {
689
- spinner.fail("Policy update failed.");
692
+ spinner?.fail("Policy update failed.");
690
693
  throw err;
691
694
  }
692
695
  }
@@ -736,7 +739,7 @@ async function createTenantRow(teamId) {
736
739
  }
737
740
  async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
738
741
  const { default: inquirer3 } = await import("inquirer");
739
- const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
742
+ const base2 = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
740
743
  const { provider } = await inquirer3.prompt([{
741
744
  type: "list",
742
745
  name: "provider",
@@ -781,9 +784,9 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
781
784
  }
782
785
  const spinner = (0, import_ora10.default)("Setting up login...").start();
783
786
  try {
784
- const client = await admin({ method: "POST", path: `${base}/app-clients`, body: clientBody, summary: `Create login for ${tenant2}` });
787
+ const client = await admin({ method: "POST", path: `${base2}/app-clients`, body: clientBody, summary: `Create login for ${tenant2}` });
785
788
  const clientId = client?.clientId ?? client?.client_id;
786
- await admin({ method: "POST", path: `${base}/app-clients/${encodeURIComponent(clientId)}/providers`, body: providerBody, summary: `Add ${provider} provider` });
789
+ await admin({ method: "POST", path: `${base2}/app-clients/${encodeURIComponent(clientId)}/providers`, body: providerBody, summary: `Add ${provider} provider` });
787
790
  spinner.succeed(opt.own ? `${opt.label} login is ready.` : "APIblaze-hosted GitHub login is ready.");
788
791
  return true;
789
792
  } catch (err) {
@@ -927,10 +930,10 @@ var init_tenant_pick = __esm({
927
930
 
928
931
  // src/index.ts
929
932
  var import_commander = require("commander");
930
- var import_chalk43 = __toESM(require("chalk"));
933
+ var import_chalk44 = __toESM(require("chalk"));
931
934
 
932
935
  // package.json
933
- var version = "0.19.1";
936
+ var version = "0.19.2";
934
937
 
935
938
  // src/index.ts
936
939
  init_types();
@@ -1183,7 +1186,7 @@ function decodeJwt(token) {
1183
1186
  function maskPath(path8) {
1184
1187
  const q = path8.indexOf("?");
1185
1188
  if (q < 0) return path8;
1186
- const base = path8.slice(0, q);
1189
+ const base2 = path8.slice(0, q);
1187
1190
  const query = path8.slice(q + 1);
1188
1191
  const masked = query.split("&").map((pair) => {
1189
1192
  const eq = pair.indexOf("=");
@@ -1195,7 +1198,7 @@ function maskPath(path8) {
1195
1198
  }
1196
1199
  return pair;
1197
1200
  }).join("&");
1198
- return `${base}?${masked}`;
1201
+ return `${base2}?${masked}`;
1199
1202
  }
1200
1203
  function formatHeaderLines(name, value) {
1201
1204
  const lower = name.toLowerCase();
@@ -2010,6 +2013,7 @@ async function runCreate(opts = {}) {
2010
2013
  const adminKey = keys.dev ?? Object.values(keys)[0];
2011
2014
  const proxyUrl = `https://${name}.abz.run/${version2}/dev`;
2012
2015
  const devPortal = result.devPortal ? stripTenantFromPortal(result.devPortal) : void 0;
2016
+ await applyCreateToggles(name, opts);
2013
2017
  if (opts.json) {
2014
2018
  process.stdout.write(JSON.stringify({
2015
2019
  project_id: result.project_id,
@@ -2037,17 +2041,13 @@ async function runCreate(opts = {}) {
2037
2041
  }
2038
2042
  printCurlExample(proxyUrl, auth, adminKey, devPortal);
2039
2043
  console.log();
2040
- await applyCreateToggles(name, opts);
2041
2044
  }
2042
2045
  async function applyCreateToggles(name, opts) {
2043
2046
  if (!opts.identified && !opts.iam) return;
2044
2047
  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
- }
2048
+ const toggleOpts = { team: opts.team, json: opts.json, quiet: opts.json };
2049
+ if (opts.identified) await runIdentifiedToggle2(name, "require", toggleOpts);
2050
+ if (opts.iam) await runIamToggle2(name, "on", toggleOpts);
2051
2051
  }
2052
2052
  async function runAnonymousCreate(opts) {
2053
2053
  const interactive = !!process.stdin.isTTY && !opts.json;
@@ -2149,6 +2149,7 @@ async function runAnonymousCreate(opts) {
2149
2149
  const keys = result.api_keys ?? {};
2150
2150
  const apiKey = result.apiKey ?? keys.prod ?? Object.values(keys)[0];
2151
2151
  const prodEndpoint = (result.endpoints || []).find((e) => e.endsWith("/prod")) || (result.endpoints || [])[0];
2152
+ if (name) await applyCreateToggles(name, opts);
2152
2153
  if (opts.json) {
2153
2154
  process.stdout.write(JSON.stringify({
2154
2155
  project_id: result.project_id,
@@ -2182,7 +2183,6 @@ async function runAnonymousCreate(opts) {
2182
2183
  console.log(` ${import_chalk10.default.bold(result.claim_url)}`);
2183
2184
  }
2184
2185
  console.log();
2185
- if (name) await applyCreateToggles(name, opts);
2186
2186
  }
2187
2187
 
2188
2188
  // src/commands/claim.ts
@@ -3422,19 +3422,19 @@ async function validScopedTenant(teamId, query) {
3422
3422
  }
3423
3423
  async function tenantHome(teamId, tenant2) {
3424
3424
  const { default: inquirer3 } = await import("inquirer");
3425
- const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
3425
+ const base2 = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
3426
3426
  console.log(import_chalk29.default.bold(`
3427
3427
  Tenant ${tenant2}`));
3428
3428
  console.log(import_chalk29.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
3429
3429
  for (; ; ) {
3430
3430
  const spinner = (0, import_ora13.default)("Reading tenant state...").start();
3431
3431
  const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
3432
- admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
3433
- admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
3434
- admin({ method: "GET", path: `${base}/admin-emails`, summary: "List consumer-admin emails" }).catch(() => null),
3435
- admin({ method: "GET", path: `${base}/external-issuers`, summary: "List external issuers" }).catch(() => null),
3436
- admin({ method: "GET", path: `${base}/opaque`, summary: "Read opaque validator" }).catch(() => null),
3437
- admin({ method: "GET", path: `${base}/app-clients`, summary: "List app clients" }).catch(() => [])
3432
+ admin({ method: "GET", path: `${base2}/iam`, summary: "Read IAM toggle" }).catch(() => null),
3433
+ admin({ method: "GET", path: `${base2}/cors`, summary: "Read tenant CORS" }).catch(() => null),
3434
+ admin({ method: "GET", path: `${base2}/admin-emails`, summary: "List consumer-admin emails" }).catch(() => null),
3435
+ admin({ method: "GET", path: `${base2}/external-issuers`, summary: "List external issuers" }).catch(() => null),
3436
+ admin({ method: "GET", path: `${base2}/opaque`, summary: "Read opaque validator" }).catch(() => null),
3437
+ admin({ method: "GET", path: `${base2}/app-clients`, summary: "List app clients" }).catch(() => [])
3438
3438
  ]).finally(() => spinner.stop());
3439
3439
  const nEmails = (emails?.admin_emails ?? []).length;
3440
3440
  const nIssuers = (issuers?.external_issuers ?? []).length;
@@ -3460,11 +3460,11 @@ Tenant ${tenant2}`));
3460
3460
  case "back":
3461
3461
  return;
3462
3462
  case "login":
3463
- await loginMethodsMenu(teamId, tenant2, base);
3463
+ await loginMethodsMenu(teamId, tenant2, base2);
3464
3464
  break;
3465
3465
  case "iam": {
3466
3466
  const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
3467
- await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
3467
+ await admin({ method: "PATCH", path: `${base2}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
3468
3468
  console.log(import_chalk29.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
3469
3469
  break;
3470
3470
  }
@@ -3481,24 +3481,24 @@ Tenant ${tenant2}`));
3481
3481
  console.log(import_chalk29.default.yellow(" Not valid JSON \u2014 unchanged."));
3482
3482
  break;
3483
3483
  }
3484
- await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
3484
+ await admin({ method: "PUT", path: `${base2}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
3485
3485
  console.log(import_chalk29.default.green(" CORS updated."));
3486
3486
  break;
3487
3487
  }
3488
3488
  case "emails":
3489
- await emailsMenu(base, emails?.admin_emails ?? []);
3489
+ await emailsMenu(base2, emails?.admin_emails ?? []);
3490
3490
  break;
3491
3491
  }
3492
3492
  }
3493
3493
  }
3494
- async function loginMethodsMenu(teamId, tenant2, base) {
3494
+ async function loginMethodsMenu(teamId, tenant2, base2) {
3495
3495
  const { default: inquirer3 } = await import("inquirer");
3496
3496
  for (; ; ) {
3497
3497
  const spinner = (0, import_ora13.default)("Loading login methods...").start();
3498
3498
  const [rawClients, rawIssuers, rawOpaque] = await Promise.all([
3499
- admin({ method: "GET", path: `${base}/app-clients`, summary: "List APIblaze-hosted logins" }).catch(() => []),
3500
- admin({ method: "GET", path: `${base}/external-issuers`, summary: "List your-own-JWT logins" }).catch(() => null),
3501
- admin({ method: "GET", path: `${base}/opaque`, summary: "Read opaque login" }).catch(() => null)
3499
+ admin({ method: "GET", path: `${base2}/app-clients`, summary: "List APIblaze-hosted logins" }).catch(() => []),
3500
+ admin({ method: "GET", path: `${base2}/external-issuers`, summary: "List your-own-JWT logins" }).catch(() => null),
3501
+ admin({ method: "GET", path: `${base2}/opaque`, summary: "Read opaque login" }).catch(() => null)
3502
3502
  ]).finally(() => spinner.stop());
3503
3503
  const appClients = Array.isArray(rawClients) ? rawClients : [];
3504
3504
  const issuers = rawIssuers?.external_issuers ?? [];
@@ -3529,24 +3529,24 @@ async function loginMethodsMenu(teamId, tenant2, base) {
3529
3529
  }]);
3530
3530
  if (pick2.kind === "back") return;
3531
3531
  if (pick2.kind === "add") {
3532
- await addLoginMethod(teamId, tenant2, base);
3532
+ await addLoginMethod(teamId, tenant2, base2);
3533
3533
  continue;
3534
3534
  }
3535
3535
  if (pick2.kind === "client") {
3536
- await clientHome(base, pick2.item);
3536
+ await clientHome(base2, pick2.item);
3537
3537
  continue;
3538
3538
  }
3539
3539
  if (pick2.kind === "issuer") {
3540
- await issuerHome(base, pick2.item);
3540
+ await issuerHome(base2, pick2.item);
3541
3541
  continue;
3542
3542
  }
3543
3543
  if (pick2.kind === "opaque") {
3544
- await opaqueHome(base, pick2.item);
3544
+ await opaqueHome(base2, pick2.item);
3545
3545
  continue;
3546
3546
  }
3547
3547
  }
3548
3548
  }
3549
- async function addLoginMethod(teamId, tenant2, base) {
3549
+ async function addLoginMethod(teamId, tenant2, base2) {
3550
3550
  const { default: inquirer3 } = await import("inquirer");
3551
3551
  const { kind } = await inquirer3.prompt([{
3552
3552
  type: "list",
@@ -3565,9 +3565,9 @@ async function addLoginMethod(teamId, tenant2, base) {
3565
3565
  const { addApiblazeHostedLogin: addApiblazeHostedLogin2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
3566
3566
  await addApiblazeHostedLogin2(teamId, tenant2);
3567
3567
  } else if (kind === "jwt") {
3568
- await addIssuer(base);
3568
+ await addIssuer(base2);
3569
3569
  } else {
3570
- await setOpaque(base, null);
3570
+ await setOpaque(base2, null);
3571
3571
  }
3572
3572
  }
3573
3573
  function safeJson(s) {
@@ -3577,7 +3577,7 @@ function safeJson(s) {
3577
3577
  return void 0;
3578
3578
  }
3579
3579
  }
3580
- async function emailsMenu(base, emails) {
3580
+ async function emailsMenu(base2, emails) {
3581
3581
  const { default: inquirer3 } = await import("inquirer");
3582
3582
  console.log();
3583
3583
  if (!emails.length) console.log(import_chalk29.default.dim(" No consumer-admin emails."));
@@ -3595,7 +3595,7 @@ async function emailsMenu(base, emails) {
3595
3595
  if (act === "back") return;
3596
3596
  if (act === "add") {
3597
3597
  const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
3598
- await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
3598
+ await admin({ method: "POST", path: `${base2}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
3599
3599
  console.log(import_chalk29.default.green(` ${email} added.`));
3600
3600
  } else {
3601
3601
  const { e } = await inquirer3.prompt([{
@@ -3605,11 +3605,11 @@ async function emailsMenu(base, emails) {
3605
3605
  choices: [...emails.map((x) => ({ name: x.email ?? String(x), value: x.email ?? String(x) })), { name: "\u2190 Back", value: null }]
3606
3606
  }]);
3607
3607
  if (!e) return;
3608
- await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
3608
+ await admin({ method: "DELETE", path: `${base2}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
3609
3609
  console.log(import_chalk29.default.green(` ${e} removed.`));
3610
3610
  }
3611
3611
  }
3612
- async function addIssuer(base) {
3612
+ async function addIssuer(base2) {
3613
3613
  const { default: inquirer3 } = await import("inquirer");
3614
3614
  const a = await inquirer3.prompt([
3615
3615
  { type: "input", name: "iss", message: "Issuer URL (iss):", validate: (s) => !!s.trim() || "required" },
@@ -3623,13 +3623,13 @@ async function addIssuer(base) {
3623
3623
  const claim = a.sem === "extract_from_claim" ? (await inquirer3.prompt([{ type: "input", name: "c", message: "Claim name:", validate: (s) => !!s.trim() || "required" }])).c : void 0;
3624
3624
  await admin({
3625
3625
  method: "POST",
3626
- path: `${base}/external-issuers`,
3626
+ path: `${base2}/external-issuers`,
3627
3627
  body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
3628
3628
  summary: `Add external issuer ${a.iss.trim()}`
3629
3629
  });
3630
3630
  console.log(import_chalk29.default.green(" JWT login method saved."));
3631
3631
  }
3632
- async function issuerHome(base, issuer) {
3632
+ async function issuerHome(base2, issuer) {
3633
3633
  const { default: inquirer3 } = await import("inquirer");
3634
3634
  console.log(`
3635
3635
  ${import_chalk29.default.bold(issuer.iss)} ${import_chalk29.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
@@ -3645,28 +3645,28 @@ async function issuerHome(base, issuer) {
3645
3645
  }]);
3646
3646
  if (act === "back") return;
3647
3647
  if (act === "edit") {
3648
- await addIssuer(base);
3648
+ await addIssuer(base2);
3649
3649
  return;
3650
3650
  }
3651
3651
  const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Delete the JWT login method for ${issuer.iss}? Consumers using it can no longer sign in.`, default: false }]);
3652
3652
  if (!sure) return;
3653
3653
  await admin({
3654
3654
  method: "DELETE",
3655
- path: `${base}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
3655
+ path: `${base2}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
3656
3656
  summary: `Delete issuer ${issuer.iss}`
3657
3657
  });
3658
3658
  console.log(import_chalk29.default.green(" Deleted."));
3659
3659
  }
3660
- async function setOpaque(base, cur) {
3660
+ async function setOpaque(base2, cur) {
3661
3661
  const { default: inquirer3 } = await import("inquirer");
3662
3662
  const a = await inquirer3.prompt([
3663
3663
  { type: "input", name: "endpoint", message: "Introspection endpoint (https):", default: cur?.endpoint, validate: (s) => s.startsWith("https://") || "must be https" },
3664
3664
  { type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
3665
3665
  ]);
3666
- await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
3666
+ await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
3667
3667
  console.log(import_chalk29.default.green(" Opaque login method set."));
3668
3668
  }
3669
- async function opaqueHome(base, cur) {
3669
+ async function opaqueHome(base2, cur) {
3670
3670
  const { default: inquirer3 } = await import("inquirer");
3671
3671
  console.log(`
3672
3672
  ${import_chalk29.default.bold(cur.endpoint)} ${import_chalk29.default.dim(cur.method ?? "GET")}`);
@@ -3682,16 +3682,16 @@ async function opaqueHome(base, cur) {
3682
3682
  }]);
3683
3683
  if (act === "back") return;
3684
3684
  if (act === "edit") {
3685
- await setOpaque(base, cur);
3685
+ await setOpaque(base2, cur);
3686
3686
  return;
3687
3687
  }
3688
- await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
3688
+ await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
3689
3689
  console.log(import_chalk29.default.green(" Deleted."));
3690
3690
  }
3691
- async function clientHome(base, summary) {
3691
+ async function clientHome(base2, summary) {
3692
3692
  const { default: inquirer3 } = await import("inquirer");
3693
3693
  const id = summary.clientId ?? summary.client_id;
3694
- const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
3694
+ const cBase = `${base2}/app-clients/${encodeURIComponent(id)}`;
3695
3695
  for (; ; ) {
3696
3696
  const spinner = (0, import_ora13.default)("Reading app client...").start();
3697
3697
  const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
@@ -4839,9 +4839,9 @@ function showCondition(cond) {
4839
4839
  }
4840
4840
  async function transformsMenu(proj2) {
4841
4841
  const { default: inquirer3 } = await import("inquirer");
4842
- const base = `/projects/${proj2.projectId}/${proj2.apiVersion}/transforms`;
4842
+ const base2 = `/projects/${proj2.projectId}/${proj2.apiVersion}/transforms`;
4843
4843
  for (; ; ) {
4844
- const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
4844
+ const out = await admin({ method: "GET", path: base2, summary: "List transform rules" });
4845
4845
  const rules = out?.rules ?? [];
4846
4846
  console.log();
4847
4847
  if (!rules.length) console.log(import_chalk33.default.dim(" No transform rules yet."));
@@ -4877,7 +4877,7 @@ async function transformsMenu(proj2) {
4877
4877
  console.log(import_chalk33.default.yellow(" Not a JSON object \u2014 skipped."));
4878
4878
  continue;
4879
4879
  }
4880
- await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
4880
+ await admin({ method: "POST", path: base2, body, summary: "Create transform rule (raw JSON)" });
4881
4881
  console.log(import_chalk33.default.green(" Rule created."));
4882
4882
  continue;
4883
4883
  }
@@ -4927,7 +4927,7 @@ async function transformsMenu(proj2) {
4927
4927
  try {
4928
4928
  await admin({
4929
4929
  method: "POST",
4930
- path: base,
4930
+ path: base2,
4931
4931
  body: { name: ans.name, phase: ans.phase, enabled: true, action: action2, ...condition ? { condition } : {} },
4932
4932
  summary: `Create transform "${ans.name}"`
4933
4933
  });
@@ -4946,10 +4946,10 @@ async function transformsMenu(proj2) {
4946
4946
  if (!rule) continue;
4947
4947
  if (act === "toggle") {
4948
4948
  const flipped = { ...rule, enabled: rule.enabled === false };
4949
- await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
4949
+ await admin({ method: "PUT", path: `${base2}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
4950
4950
  console.log(import_chalk33.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
4951
4951
  } else {
4952
- await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
4952
+ await admin({ method: "DELETE", path: `${base2}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
4953
4953
  console.log(import_chalk33.default.green(` ${rule.name} deleted.`));
4954
4954
  }
4955
4955
  }
@@ -4957,9 +4957,9 @@ async function transformsMenu(proj2) {
4957
4957
  }
4958
4958
  async function mappingsMenu(proj2) {
4959
4959
  const { default: inquirer3 } = await import("inquirer");
4960
- const base = `/projects/${proj2.projectId}/${proj2.apiVersion}/mappings`;
4960
+ const base2 = `/projects/${proj2.projectId}/${proj2.apiVersion}/mappings`;
4961
4961
  for (; ; ) {
4962
- const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
4962
+ const out = await admin({ method: "GET", path: base2, summary: "List mapping tables" });
4963
4963
  const tables = out?.mappings ?? out?.tables ?? [];
4964
4964
  console.log();
4965
4965
  if (!tables.length) console.log(import_chalk33.default.dim(" No mapping tables yet."));
@@ -4987,7 +4987,7 @@ async function mappingsMenu(proj2) {
4987
4987
  console.log(import_chalk33.default.yellow(" Entries must be a JSON array \u2014 not created."));
4988
4988
  continue;
4989
4989
  }
4990
- await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
4990
+ await admin({ method: "POST", path: base2, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
4991
4991
  console.log(import_chalk33.default.green(` Table "${a.name}" created.`));
4992
4992
  } else {
4993
4993
  const { table } = await inquirer3.prompt([{
@@ -4997,16 +4997,16 @@ async function mappingsMenu(proj2) {
4997
4997
  choices: [...tables.map((t) => ({ name: t.name, value: t })), { name: "\u2190 Back", value: null }]
4998
4998
  }]);
4999
4999
  if (!table) continue;
5000
- await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
5000
+ await admin({ method: "DELETE", path: `${base2}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
5001
5001
  console.log(import_chalk33.default.green(` ${table.name} deleted.`));
5002
5002
  }
5003
5003
  }
5004
5004
  }
5005
5005
  async function tenantsMenu(proj2, opts) {
5006
5006
  const { default: inquirer3 } = await import("inquirer");
5007
- const base = `/projects/${proj2.projectId}/${proj2.apiVersion}/tenants`;
5007
+ const base2 = `/projects/${proj2.projectId}/${proj2.apiVersion}/tenants`;
5008
5008
  for (; ; ) {
5009
- const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
5009
+ const out = await admin({ method: "GET", path: base2, summary: "List attached tenants" });
5010
5010
  const tenants = out?.tenants ?? [];
5011
5011
  console.log();
5012
5012
  if (!tenants.length) console.log(import_chalk33.default.dim(" No tenants attached (consumers use the default tenant)."));
@@ -5038,7 +5038,7 @@ async function tenantsMenu(proj2, opts) {
5038
5038
  choices: [...tenants.map((x) => ({ name: x.tenant_name ?? x.name, value: x })), { name: "\u2190 Back", value: null }]
5039
5039
  }]);
5040
5040
  if (!t) continue;
5041
- await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
5041
+ await admin({ method: "DELETE", path: `${base2}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
5042
5042
  console.log(import_chalk33.default.green(` Detached ${t.tenant_name ?? t.name}.`));
5043
5043
  }
5044
5044
  }
@@ -5418,9 +5418,85 @@ async function runKeyRevoke(keyId, opts) {
5418
5418
  // src/index.ts
5419
5419
  init_iam();
5420
5420
 
5421
- // src/commands/preapprove.ts
5421
+ // src/commands/admins.ts
5422
5422
  var import_chalk36 = __toESM(require("chalk"));
5423
5423
  var import_ora19 = __toESM(require("ora"));
5424
+ init_caller();
5425
+ function requireTenant(opts) {
5426
+ const t = (opts.tenant ?? "").trim();
5427
+ if (!t) {
5428
+ throw new Error("--tenant <slug> is required (e.g. --tenant nino).");
5429
+ }
5430
+ return t;
5431
+ }
5432
+ function base(teamId, tenant2) {
5433
+ return `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/admin-emails`;
5434
+ }
5435
+ async function runAdminsList(opts) {
5436
+ const caller = requireCaller();
5437
+ const { teamId } = await resolveActingTeam(caller, opts.team);
5438
+ const tenant2 = requireTenant(opts);
5439
+ const out = await producer(caller, {
5440
+ method: "GET",
5441
+ path: base(teamId, tenant2),
5442
+ summary: `List admins for tenant ${tenant2}`
5443
+ });
5444
+ const admins2 = out?.admin_emails ?? out?.admins ?? [];
5445
+ if (opts.json) {
5446
+ console.log(JSON.stringify(admins2));
5447
+ return;
5448
+ }
5449
+ if (!admins2.length) {
5450
+ console.log(import_chalk36.default.yellow(`No admins for tenant ${tenant2} yet.`));
5451
+ return;
5452
+ }
5453
+ for (const a of admins2) {
5454
+ const email = typeof a === "string" ? a : a.email;
5455
+ const flags = [a.active === false ? "pending" : null, a.pinned ? "pinned" : null].filter(Boolean).join(", ");
5456
+ console.log(` ${import_chalk36.default.bold(email)}${flags ? import_chalk36.default.dim(` (${flags})`) : ""}`);
5457
+ }
5458
+ }
5459
+ async function runAdminsAdd(email, opts) {
5460
+ const caller = requireCaller();
5461
+ const { teamId } = await resolveActingTeam(caller, opts.team);
5462
+ const tenant2 = requireTenant(opts);
5463
+ const spinner = opts.json ? null : (0, import_ora19.default)(`Adding ${email} as admin of ${tenant2}...`).start();
5464
+ try {
5465
+ const out = await producer(caller, {
5466
+ method: "POST",
5467
+ path: base(teamId, tenant2),
5468
+ body: { email },
5469
+ summary: `Add ${email} to tenant ${tenant2} admins`
5470
+ });
5471
+ spinner?.succeed(`${import_chalk36.default.bold(email)} is now an admin of ${import_chalk36.default.bold(tenant2)}.`);
5472
+ if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
5473
+ else console.log(import_chalk36.default.dim(" Reload the Users & Groups widget \u2014 access flips from \u201Cpending\u201D to ready."));
5474
+ } catch (err) {
5475
+ spinner?.fail("Add failed.");
5476
+ throw err;
5477
+ }
5478
+ }
5479
+ async function runAdminsRemove(email, opts) {
5480
+ const caller = requireCaller();
5481
+ const { teamId } = await resolveActingTeam(caller, opts.team);
5482
+ const tenant2 = requireTenant(opts);
5483
+ const spinner = opts.json ? null : (0, import_ora19.default)(`Removing ${email} from ${tenant2} admins...`).start();
5484
+ try {
5485
+ await producer(caller, {
5486
+ method: "DELETE",
5487
+ path: `${base(teamId, tenant2)}/${encodeURIComponent(email)}`,
5488
+ summary: `Remove ${email} from tenant ${tenant2} admins`
5489
+ });
5490
+ spinner?.succeed(`Removed ${import_chalk36.default.bold(email)} from ${import_chalk36.default.bold(tenant2)} admins.`);
5491
+ } catch (err) {
5492
+ spinner?.fail("Remove failed.");
5493
+ throw err;
5494
+ }
5495
+ }
5496
+
5497
+ // src/commands/preapprove.ts
5498
+ var import_chalk37 = __toESM(require("chalk"));
5499
+ var import_ora20 = __toESM(require("ora"));
5424
5500
 
5425
5501
  // src/lib/preapproval.ts
5426
5502
  init_auth();
@@ -5484,14 +5560,14 @@ async function runPreapprove(who, opts) {
5484
5560
  const rules = await listPreapprovalRules(tenant2);
5485
5561
  if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
5486
5562
  if (!rules.length) {
5487
- console.log(import_chalk36.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
5488
- console.log(import_chalk36.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
5563
+ console.log(import_chalk37.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
5564
+ console.log(import_chalk37.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
5489
5565
  return;
5490
5566
  }
5491
- console.log(import_chalk36.default.dim(`Pre-approved for ${import_chalk36.default.bold(tenant2)}:`));
5567
+ console.log(import_chalk37.default.dim(`Pre-approved for ${import_chalk37.default.bold(tenant2)}:`));
5492
5568
  for (const r of rules) {
5493
- const tag = r.kind === "domain" ? import_chalk36.default.cyan("@" + r.value + " (whole domain)") : r.value;
5494
- const grp = r.groups?.length ? import_chalk36.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
5569
+ const tag = r.kind === "domain" ? import_chalk37.default.cyan("@" + r.value + " (whole domain)") : r.value;
5570
+ const grp = r.groups?.length ? import_chalk37.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
5495
5571
  console.log(` ${tag}${grp}`);
5496
5572
  }
5497
5573
  return;
@@ -5500,19 +5576,19 @@ async function runPreapprove(who, opts) {
5500
5576
  throw new Error("Who? Pass an email or a domain: `apiblaze preapprove someone@acme.com` (or `apiblaze preapprove --list`).");
5501
5577
  }
5502
5578
  if (opts.remove) {
5503
- const spinner2 = (0, import_ora19.default)(`Removing ${who} from ${tenant2}\u2026`).start();
5579
+ const spinner2 = (0, import_ora20.default)(`Removing ${who} from ${tenant2}\u2026`).start();
5504
5580
  const { removed, value } = await removePreapprovalRule(tenant2, who);
5505
- if (removed) spinner2.succeed(`${import_chalk36.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
5506
- else spinner2.warn(`No pre-approval rule for ${import_chalk36.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
5581
+ if (removed) spinner2.succeed(`${import_chalk37.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
5582
+ else spinner2.warn(`No pre-approval rule for ${import_chalk37.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
5507
5583
  return;
5508
5584
  }
5509
- const spinner = (0, import_ora19.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
5585
+ const spinner = (0, import_ora20.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
5510
5586
  try {
5511
5587
  const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
5512
5588
  const what = kind === "domain" ? `Anyone @${value}` : value;
5513
- spinner.succeed(`${import_chalk36.default.bold(what)} can now sign in to ${import_chalk36.default.bold(tenant2)}.`);
5514
- if (opts.group?.length) console.log(import_chalk36.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
5515
- console.log(import_chalk36.default.dim(` See the full list: apiblaze preapprove --list`));
5589
+ spinner.succeed(`${import_chalk37.default.bold(what)} can now sign in to ${import_chalk37.default.bold(tenant2)}.`);
5590
+ if (opts.group?.length) console.log(import_chalk37.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
5591
+ console.log(import_chalk37.default.dim(` See the full list: apiblaze preapprove --list`));
5516
5592
  } catch (err) {
5517
5593
  spinner.fail("Could not add the rule.");
5518
5594
  throw err;
@@ -5523,8 +5599,8 @@ async function runPreapprove(who, opts) {
5523
5599
  var fs9 = __toESM(require("fs"));
5524
5600
  var path6 = __toESM(require("path"));
5525
5601
  var crypto2 = __toESM(require("crypto"));
5526
- var import_chalk38 = __toESM(require("chalk"));
5527
- var import_ora20 = __toESM(require("ora"));
5602
+ var import_chalk39 = __toESM(require("chalk"));
5603
+ var import_ora21 = __toESM(require("ora"));
5528
5604
  var import_yaml = require("yaml");
5529
5605
  init_auth();
5530
5606
  init_anon_cred();
@@ -5534,7 +5610,7 @@ init_admin();
5534
5610
  // src/commands/llm.ts
5535
5611
  var fs8 = __toESM(require("fs"));
5536
5612
  var path5 = __toESM(require("path"));
5537
- var import_chalk37 = __toESM(require("chalk"));
5613
+ var import_chalk38 = __toESM(require("chalk"));
5538
5614
  var import_inquirer2 = __toESM(require("inquirer"));
5539
5615
  init_auth();
5540
5616
  var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
@@ -5578,27 +5654,27 @@ async function runLlmSetKey(keyArg, opts) {
5578
5654
  }
5579
5655
  const existing = loadLlmConfig();
5580
5656
  saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
5581
- console.log(`${import_chalk37.default.green("\u2713")} Saved ${import_chalk37.default.bold(provider)} key ${maskSecret(key)}`);
5582
- console.log(import_chalk37.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
5583
- if (opts.model) console.log(import_chalk37.default.gray(` Model: ${opts.model}`));
5657
+ console.log(`${import_chalk38.default.green("\u2713")} Saved ${import_chalk38.default.bold(provider)} key ${maskSecret(key)}`);
5658
+ console.log(import_chalk38.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
5659
+ if (opts.model) console.log(import_chalk38.default.gray(` Model: ${opts.model}`));
5584
5660
  }
5585
5661
  async function runLlmShow() {
5586
5662
  const cfg = loadLlmConfig();
5587
5663
  if (!cfg) {
5588
- console.log(import_chalk37.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
5664
+ console.log(import_chalk38.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
5589
5665
  return;
5590
5666
  }
5591
- console.log(`Provider: ${import_chalk37.default.bold(cfg.provider)}`);
5667
+ console.log(`Provider: ${import_chalk38.default.bold(cfg.provider)}`);
5592
5668
  console.log(`Key: ${maskSecret(cfg.key)}`);
5593
5669
  if (cfg.model) console.log(`Model: ${cfg.model}`);
5594
- console.log(import_chalk37.default.gray(`Stored at ${LLM_PATH}`));
5670
+ console.log(import_chalk38.default.gray(`Stored at ${LLM_PATH}`));
5595
5671
  }
5596
5672
  async function runLlmClearKey() {
5597
5673
  try {
5598
5674
  fs8.unlinkSync(LLM_PATH);
5599
- console.log(`${import_chalk37.default.green("\u2713")} Removed local LLM key.`);
5675
+ console.log(`${import_chalk38.default.green("\u2713")} Removed local LLM key.`);
5600
5676
  } catch {
5601
- console.log(import_chalk37.default.gray("No LLM key was set."));
5677
+ console.log(import_chalk38.default.gray("No LLM key was set."));
5602
5678
  }
5603
5679
  }
5604
5680
 
@@ -5606,9 +5682,9 @@ async function runLlmClearKey() {
5606
5682
  init_trace();
5607
5683
  init_types();
5608
5684
  function fail4(message, hint) {
5609
- console.error(import_chalk38.default.red(`
5685
+ console.error(import_chalk39.default.red(`
5610
5686
  Error: ${message}`));
5611
- if (hint) console.error(import_chalk38.default.dim(hint));
5687
+ if (hint) console.error(import_chalk39.default.dim(hint));
5612
5688
  process.exit(1);
5613
5689
  }
5614
5690
  function normalizeName2(raw) {
@@ -5650,9 +5726,9 @@ async function fetchText(url) {
5650
5726
  }
5651
5727
  }
5652
5728
  async function discoverSpec(target) {
5653
- const base = target.replace(/\/+$/, "");
5729
+ const base2 = target.replace(/\/+$/, "");
5654
5730
  for (const suffix of ["/openapi.json", "/openapi.yaml", "/swagger.json"]) {
5655
- const url = base + suffix;
5731
+ const url = base2 + suffix;
5656
5732
  const text = await fetchText(url);
5657
5733
  if (text) {
5658
5734
  try {
@@ -5756,7 +5832,7 @@ async function resolveTargetAuth(spec2, opts) {
5756
5832
  "Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
5757
5833
  );
5758
5834
  }
5759
- if (sawOAuth) console.log(import_chalk38.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
5835
+ if (sawOAuth) console.log(import_chalk39.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
5760
5836
  return null;
5761
5837
  }
5762
5838
  if (candidates.length === 1 && !noneAllowed) return candidates[0];
@@ -5821,28 +5897,28 @@ async function provision(spec2, target, opts) {
5821
5897
  const loggedIn = !!loadCredentials();
5822
5898
  const anon = !loggedIn;
5823
5899
  const salt = () => Math.random().toString(36).slice(2, 6);
5824
- let base = opts.name ? normalizeName2(opts.name) : "";
5825
- if (!base) {
5900
+ let base2 = opts.name ? normalizeName2(opts.name) : "";
5901
+ if (!base2) {
5826
5902
  try {
5827
5903
  const host = new URL(target).hostname;
5828
- base = normalizeName2(host.split(".")[0]);
5829
- if (base.length < 3) base = normalizeName2(host);
5904
+ base2 = normalizeName2(host.split(".")[0]);
5905
+ if (base2.length < 3) base2 = normalizeName2(host);
5830
5906
  } catch {
5831
5907
  }
5832
5908
  }
5833
- if (!base && spec2.info && typeof spec2.info.title === "string") base = normalizeName2(spec2.info.title);
5834
- if (!base || base.length < 3) base = "apichat";
5835
- let name = opts.name ? base : `${base}${salt()}`;
5909
+ if (!base2 && spec2.info && typeof spec2.info.title === "string") base2 = normalizeName2(spec2.info.title);
5910
+ if (!base2 || base2.length < 3) base2 = "apichat";
5911
+ let name = opts.name ? base2 : `${base2}${salt()}`;
5836
5912
  const access = anon ? "open" : opts.access === "open" ? "open" : "invite";
5837
5913
  if (anon && opts.access === "invite") {
5838
- 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`."));
5914
+ console.log(import_chalk39.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`."));
5839
5915
  }
5840
5916
  const DUAL_AUTH = {
5841
5917
  mode: "authenticate",
5842
5918
  methods: ["api_key", "jwt"],
5843
5919
  ...access === "invite" ? { preapproved_users_only: true } : {}
5844
5920
  };
5845
- const spinner = (0, import_ora20.default)("Provisioning an api_key proxy...").start();
5921
+ const spinner = (0, import_ora21.default)("Provisioning an api_key proxy...").start();
5846
5922
  let result;
5847
5923
  for (let attempt = 0; attempt < 4; attempt++) {
5848
5924
  try {
@@ -5874,13 +5950,13 @@ async function provision(spec2, target, opts) {
5874
5950
  if (result.cp_key && result.team_id) saveAnonCred(result.cp_key, result.team_id, result.claim_code);
5875
5951
  }
5876
5952
  }
5877
- spinner.succeed(`Proxy provisioned${name !== base ? ` as "${name}"` : ""}.`);
5953
+ spinner.succeed(`Proxy provisioned${name !== base2 ? ` as "${name}"` : ""}.`);
5878
5954
  break;
5879
5955
  } catch (err) {
5880
5956
  const status = err instanceof ApiError ? err.status : void 0;
5881
5957
  const collision = err instanceof ApiError && (err.status === 409 || /exist|taken|available/i.test(err.message));
5882
5958
  if (collision && attempt < 3) {
5883
- name = `${base}${salt()}`;
5959
+ name = `${base2}${salt()}`;
5884
5960
  continue;
5885
5961
  }
5886
5962
  if (status === 401) {
@@ -5928,14 +6004,14 @@ async function provision(spec2, target, opts) {
5928
6004
  try {
5929
6005
  await addPreapprovalRule(tenant2, email);
5930
6006
  } catch {
5931
- console.log(import_chalk38.default.dim(` (Could not auto-approve your email for sign-in \u2014 add it later: apiblaze preapprove ${email})`));
6007
+ console.log(import_chalk39.default.dim(` (Could not auto-approve your email for sign-in \u2014 add it later: apiblaze preapprove ${email})`));
5932
6008
  }
5933
6009
  }
5934
6010
  }
5935
6011
  return { projectId, version: version2, environment, dpKey, mcpHost, proxyUrl, anon, access, tenant: tenant2 };
5936
6012
  }
5937
6013
  async function writeTargetAuth(p, auth, secret) {
5938
- const spinner = (0, import_ora20.default)("Storing target credentials (encrypted)...").start();
6014
+ const spinner = (0, import_ora21.default)("Storing target credentials (encrypted)...").start();
5939
6015
  try {
5940
6016
  await cpPost(
5941
6017
  p.anon,
@@ -5957,7 +6033,7 @@ async function writeTargetAuth(p, auth, secret) {
5957
6033
  }
5958
6034
  }
5959
6035
  async function uploadSpec(p, specText, opts) {
5960
- const spinner = (0, import_ora20.default)("Uploading the spec...").start();
6036
+ const spinner = (0, import_ora21.default)("Uploading the spec...").start();
5961
6037
  let out;
5962
6038
  try {
5963
6039
  out = await cpPost(
@@ -5972,7 +6048,7 @@ async function uploadSpec(p, specText, opts) {
5972
6048
  throw err;
5973
6049
  }
5974
6050
  if (out && out.reused === true) {
5975
- console.log(import_chalk38.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
6051
+ console.log(import_chalk39.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
5976
6052
  } else if (out && out.changed === true && out.previous_spec_hash) {
5977
6053
  const interactive = !!process.stdin.isTTY && !opts.yes;
5978
6054
  if (interactive) {
@@ -5980,12 +6056,12 @@ async function uploadSpec(p, specText, opts) {
5980
6056
  const { go } = await inquirer3.prompt([
5981
6057
  { type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
5982
6058
  ]);
5983
- if (!go) console.log(import_chalk38.default.dim(" Keeping the existing MCP catalogue."));
6059
+ if (!go) console.log(import_chalk39.default.dim(" Keeping the existing MCP catalogue."));
5984
6060
  }
5985
6061
  }
5986
6062
  }
5987
6063
  async function publishMcp(p, spec2) {
5988
- const spinner = (0, import_ora20.default)("Publishing the MCP catalogue...").start();
6064
+ const spinner = (0, import_ora21.default)("Publishing the MCP catalogue...").start();
5989
6065
  try {
5990
6066
  const url = `https://${p.mcpHost}/${p.version}/${p.environment}/mcp/generate`;
5991
6067
  const res = await fetch(url, {
@@ -6021,14 +6097,14 @@ var revealAuth = false;
6021
6097
  function renderToolEvents(events, dpKey) {
6022
6098
  for (const e of events ?? []) {
6023
6099
  const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
6024
- const mark = ok ? import_chalk38.default.green("\u2713") : import_chalk38.default.red("\u2717");
6025
- console.log(` ${mark} ${import_chalk38.default.cyan(e.name)} ${import_chalk38.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
6100
+ const mark = ok ? import_chalk39.default.green("\u2713") : import_chalk39.default.red("\u2717");
6101
+ console.log(` ${mark} ${import_chalk39.default.cyan(e.name)} ${import_chalk39.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
6026
6102
  if (isVerbose() && e.method && e.url) {
6027
- console.log(import_chalk38.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
6103
+ console.log(import_chalk39.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
6028
6104
  if (dpKey) {
6029
6105
  const keyLine = ` -H 'X-API-Key: ${revealAuth ? dpKey : maskKey(dpKey)}'`;
6030
- const hint = revealAuth ? "" : import_chalk38.default.yellow(" \u2190 /showauth will reveal this");
6031
- console.log(import_chalk38.default.dim(keyLine) + hint);
6106
+ const hint = revealAuth ? "" : import_chalk39.default.yellow(" \u2190 /showauth will reveal this");
6107
+ console.log(import_chalk39.default.dim(keyLine) + hint);
6032
6108
  }
6033
6109
  }
6034
6110
  }
@@ -6037,9 +6113,9 @@ function billingLine(billing) {
6037
6113
  if (!billing || typeof billing.cents !== "number") return null;
6038
6114
  if (typeof billing.free_turns_remaining === "number") return null;
6039
6115
  const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
6040
- let line = import_chalk38.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk38.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
6116
+ let line = import_chalk39.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk39.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
6041
6117
  if (typeof billing.credits_remaining === "number") {
6042
- line += import_chalk38.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
6118
+ line += import_chalk39.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
6043
6119
  }
6044
6120
  return line;
6045
6121
  }
@@ -6047,21 +6123,21 @@ function freeBudgetWarning(billing, anon) {
6047
6123
  if (!anon || !billing) return null;
6048
6124
  if (typeof billing.free_turns_remaining === "number") {
6049
6125
  const left2 = billing.free_turns_remaining;
6050
- if (left2 <= 0) return import_chalk38.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
6051
- return import_chalk38.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
6126
+ if (left2 <= 0) return import_chalk39.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
6127
+ return import_chalk39.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
6052
6128
  }
6053
6129
  if (typeof billing.free_remaining_cents !== "number") return null;
6054
6130
  const perTurn = Math.max(billing.cents || 0, 0.02);
6055
6131
  const left = Math.floor(billing.free_remaining_cents / perTurn);
6056
6132
  if (left > 8) return null;
6057
- if (left <= 0) return import_chalk38.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
6058
- return import_chalk38.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
6133
+ if (left <= 0) return import_chalk39.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
6134
+ return import_chalk39.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
6059
6135
  }
6060
6136
  function printAssistant(delta) {
6061
6137
  for (let i = delta.length - 1; i >= 0; i--) {
6062
6138
  const m = delta[i];
6063
6139
  if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
6064
- console.log("\n" + import_chalk38.default.green("assistant \u203A ") + m.content + "\n");
6140
+ console.log("\n" + import_chalk39.default.green("assistant \u203A ") + m.content + "\n");
6065
6141
  return;
6066
6142
  }
6067
6143
  }
@@ -6071,7 +6147,7 @@ async function replTurn(p, messages, userText) {
6071
6147
  const llm2 = loadLlmConfig();
6072
6148
  const turnId = crypto2.randomUUID();
6073
6149
  for (let round = 0; round < CLIENT_ROUND_CAP; round++) {
6074
- const spinner = (0, import_ora20.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
6150
+ const spinner = (0, import_ora21.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
6075
6151
  const body = {
6076
6152
  turn_id: turnId,
6077
6153
  messages,
@@ -6087,7 +6163,7 @@ async function replTurn(p, messages, userText) {
6087
6163
  });
6088
6164
  } catch (err) {
6089
6165
  spinner.fail("Network error.");
6090
- console.log(import_chalk38.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
6166
+ console.log(import_chalk39.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
6091
6167
  return;
6092
6168
  }
6093
6169
  let data = null;
@@ -6105,12 +6181,12 @@ async function replTurn(p, messages, userText) {
6105
6181
  return;
6106
6182
  }
6107
6183
  if (res.status === 401) {
6108
- 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."));
6184
+ console.log(import_chalk39.default.red(" The proxy rejected the API key (401). The key may have been revoked; re-run `apiblaze apichat` to re-provision."));
6109
6185
  return;
6110
6186
  }
6111
6187
  if (!res.ok || !data) {
6112
6188
  const err = data && data.error || `HTTP ${res.status}`;
6113
- console.log(import_chalk38.default.red(` Chat error: ${err}`));
6189
+ console.log(import_chalk39.default.red(` Chat error: ${err}`));
6114
6190
  return;
6115
6191
  }
6116
6192
  if (Array.isArray(data.delta)) {
@@ -6124,25 +6200,25 @@ async function replTurn(p, messages, userText) {
6124
6200
  if (warn) console.log(warn);
6125
6201
  if (!data.continue) return;
6126
6202
  }
6127
- console.log(import_chalk38.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
6203
+ console.log(import_chalk39.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
6128
6204
  }
6129
6205
  function renderUpsell(p, upsell) {
6130
6206
  const loggedIn = !!loadCredentials();
6131
6207
  if (upsell.reason === "CAPPED" && !loggedIn) {
6132
- console.log("\n" + import_chalk38.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
6133
- 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.)"));
6208
+ console.log("\n" + import_chalk39.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
6209
+ console.log(import_chalk39.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
6134
6210
  console.log();
6135
6211
  return;
6136
6212
  }
6137
- console.log("\n" + import_chalk38.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
6213
+ console.log("\n" + import_chalk39.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
6138
6214
  if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
6139
6215
  if (!loggedIn) {
6140
- 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."));
6216
+ console.log(import_chalk39.default.dim(" Options: `/login` for more free chats and requests, or `apiblaze llm set-key` to bring your own model key."));
6141
6217
  } else {
6142
- 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)."));
6218
+ console.log(import_chalk39.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
6143
6219
  }
6144
6220
  } else if (upsell.reason === "INFLIGHT") {
6145
- console.log(import_chalk38.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
6221
+ console.log(import_chalk39.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
6146
6222
  }
6147
6223
  console.log();
6148
6224
  }
@@ -6211,9 +6287,9 @@ async function openServerProxy(project) {
6211
6287
  const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
6212
6288
  let dpKey = prior?.dpKey;
6213
6289
  if (!dpKey) {
6214
- 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`));
6290
+ console.log(import_chalk39.default.dim(` Minting an API key for tenant ${import_chalk39.default.bold(tenant2)} to query project ${import_chalk39.default.bold(project.projectName)}\u2026`));
6215
6291
  dpKey = await mintDurableProxyKey(project.teamId, tenant2);
6216
- console.log(` ${import_chalk38.default.green("\u2714")} API key: ${import_chalk38.default.dim(maskKey(dpKey))}`);
6292
+ console.log(` ${import_chalk39.default.green("\u2714")} API key: ${import_chalk39.default.dim(maskKey(dpKey))}`);
6217
6293
  }
6218
6294
  const p = {
6219
6295
  projectId: project.projectId,
@@ -6226,7 +6302,7 @@ async function openServerProxy(project) {
6226
6302
  // Reusing an owned proxy: logged-in apichat doors default to invite-only.
6227
6303
  access: "invite"
6228
6304
  };
6229
- const spinner = (0, import_ora20.default)("Preparing the chat\u2026").start();
6305
+ const spinner = (0, import_ora21.default)("Preparing the chat\u2026").start();
6230
6306
  try {
6231
6307
  const raw = await admin({
6232
6308
  method: "GET",
@@ -6238,7 +6314,7 @@ async function openServerProxy(project) {
6238
6314
  if (spec2 && (spec2.paths || spec2.openapi)) {
6239
6315
  await publishMcp(p, spec2);
6240
6316
  } else {
6241
- 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`."));
6317
+ console.log(import_chalk39.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
6242
6318
  }
6243
6319
  } catch (err) {
6244
6320
  spinner.fail("Could not open the proxy.");
@@ -6291,7 +6367,7 @@ async function noArgsMenu(opts) {
6291
6367
  const me = loadCredentials()?.apiblazeUserId;
6292
6368
  const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
6293
6369
  const choices = saved.map((a) => ({
6294
- 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` : ""}`)}`,
6370
+ name: `Chat with ${import_chalk39.default.bold(a.name)} ${import_chalk39.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
6295
6371
  value: { type: "existing", a }
6296
6372
  }));
6297
6373
  const creds = loadCredentials();
@@ -6301,14 +6377,14 @@ async function noArgsMenu(opts) {
6301
6377
  const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
6302
6378
  for (const pr of proxies) {
6303
6379
  choices.push({
6304
- name: `Chat with ${import_chalk38.default.bold(pr.projectName)} ${import_chalk38.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
6380
+ name: `Chat with ${import_chalk39.default.bold(pr.projectName)} ${import_chalk39.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
6305
6381
  value: { type: "server", project: pr }
6306
6382
  });
6307
6383
  }
6308
6384
  } catch {
6309
6385
  }
6310
6386
  }
6311
- choices.push({ name: import_chalk38.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
6387
+ choices.push({ name: import_chalk39.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
6312
6388
  const { pick: pick2 } = await inquirer3.prompt([
6313
6389
  { type: "list", name: "pick", message: "What would you like to do?", choices }
6314
6390
  ]);
@@ -6382,36 +6458,36 @@ async function noArgsMenu(opts) {
6382
6458
  async function runRepl(p, initialMessages) {
6383
6459
  const { default: inquirer3 } = await import("inquirer");
6384
6460
  const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
6385
- console.log("\n" + import_chalk38.default.cyan.bold("Chat with your API") + import_chalk38.default.dim(` \xB7 ${p.mcpHost}`));
6386
- if (messages.length) console.log(import_chalk38.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
6461
+ console.log("\n" + import_chalk39.default.cyan.bold("Chat with your API") + import_chalk39.default.dim(` \xB7 ${p.mcpHost}`));
6462
+ if (messages.length) console.log(import_chalk39.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
6387
6463
  const llm2 = loadLlmConfig();
6388
6464
  console.log(
6389
- import_chalk38.default.dim(
6465
+ import_chalk39.default.dim(
6390
6466
  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."
6391
6467
  )
6392
6468
  );
6393
6469
  for (; ; ) {
6394
- const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk38.default.green("you \u203A") }]);
6470
+ const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk39.default.green("you \u203A") }]);
6395
6471
  const text = (input ?? "").trim();
6396
6472
  if (!text) continue;
6397
6473
  if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
6398
6474
  if (text === "/login") {
6399
6475
  try {
6400
6476
  await runLogin();
6401
- console.log(import_chalk38.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
6477
+ console.log(import_chalk39.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
6402
6478
  } catch (err) {
6403
- console.log(import_chalk38.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
6479
+ console.log(import_chalk39.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
6404
6480
  }
6405
6481
  continue;
6406
6482
  }
6407
6483
  if (text === "/claim") {
6408
6484
  const justLoggedIn = !loadCredentials();
6409
6485
  if (justLoggedIn) {
6410
- console.log(import_chalk38.default.dim(" Logging in to claim your workspace\u2026"));
6486
+ console.log(import_chalk39.default.dim(" Logging in to claim your workspace\u2026"));
6411
6487
  try {
6412
6488
  await runLogin();
6413
6489
  } catch (err) {
6414
- console.log(import_chalk38.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
6490
+ console.log(import_chalk39.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
6415
6491
  continue;
6416
6492
  }
6417
6493
  if (!loadCredentials()) continue;
@@ -6422,30 +6498,30 @@ async function runRepl(p, initialMessages) {
6422
6498
  p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
6423
6499
  p.anon = false;
6424
6500
  claimApichat(p, loadCredentials()?.apiblazeUserId);
6425
- console.log(import_chalk38.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
6501
+ console.log(import_chalk39.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
6426
6502
  }
6427
6503
  } catch (err) {
6428
- console.log(import_chalk38.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
6504
+ console.log(import_chalk39.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
6429
6505
  }
6430
6506
  continue;
6431
6507
  }
6432
6508
  if (text === "/showauth") {
6433
6509
  revealAuth = !revealAuth;
6434
- 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."));
6510
+ console.log(import_chalk39.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
6511
  continue;
6436
6512
  }
6437
6513
  if (text.startsWith("/")) {
6438
- console.log(import_chalk38.default.dim(" Commands: /login /claim /showauth /exit"));
6514
+ console.log(import_chalk39.default.dim(" Commands: /login /claim /showauth /exit"));
6439
6515
  continue;
6440
6516
  }
6441
6517
  await replTurn(p, messages, text);
6442
6518
  saveTranscript(p, messages);
6443
6519
  }
6444
- console.log(import_chalk38.default.dim("\nBye."));
6520
+ console.log(import_chalk39.default.dim("\nBye."));
6445
6521
  }
6446
6522
  async function runApichat(opts) {
6447
6523
  setVerbose(opts.verbose !== false);
6448
- console.log(import_chalk38.default.bold("\napichat \u2014 turn any API into a chat\n"));
6524
+ console.log(import_chalk39.default.bold("\napichat \u2014 turn any API into a chat\n"));
6449
6525
  if (!opts.openapispec && !opts.target) {
6450
6526
  if (!process.stdin.isTTY) {
6451
6527
  fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
@@ -6458,7 +6534,7 @@ async function runApichat(opts) {
6458
6534
  }
6459
6535
  const { spec: spec2, sourceUrl } = await loadSpec(opts);
6460
6536
  const target = resolveTarget(spec2, opts, sourceUrl);
6461
- console.log(` ${import_chalk38.default.dim("Target:")} ${import_chalk38.default.bold(target)}`);
6537
+ console.log(` ${import_chalk39.default.dim("Target:")} ${import_chalk39.default.bold(target)}`);
6462
6538
  const auth = await resolveTargetAuth(spec2, opts);
6463
6539
  if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
6464
6540
  fail4(
@@ -6467,7 +6543,7 @@ async function runApichat(opts) {
6467
6543
  );
6468
6544
  }
6469
6545
  const p = await provision(spec2, target, opts);
6470
- console.log(` ${import_chalk38.default.dim("Proxy: ")} ${import_chalk38.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
6546
+ console.log(` ${import_chalk39.default.dim("Proxy: ")} ${import_chalk39.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
6471
6547
  upsertApichat({
6472
6548
  name: p.projectId,
6473
6549
  target,
@@ -6486,31 +6562,31 @@ async function runApichat(opts) {
6486
6562
  const secret = await captureTargetSecret(auth, opts);
6487
6563
  if (secret) await writeTargetAuth(p, auth, secret);
6488
6564
  } else {
6489
- console.log(import_chalk38.default.dim(" Target auth: none required."));
6565
+ console.log(import_chalk39.default.dim(" Target auth: none required."));
6490
6566
  }
6491
6567
  const specText = JSON.stringify(spec2);
6492
6568
  await uploadSpec(p, specText, opts);
6493
6569
  const mcpUrl = await publishMcp(p, spec2);
6494
6570
  console.log();
6495
- if (p.proxyUrl) console.log(` ${import_chalk38.default.green("\u2713")} proxy ${import_chalk38.default.bold(p.proxyUrl)}`);
6571
+ if (p.proxyUrl) console.log(` ${import_chalk39.default.green("\u2713")} proxy ${import_chalk39.default.bold(p.proxyUrl)}`);
6496
6572
  if (mcpUrl) {
6497
- console.log(` ${import_chalk38.default.green("\u2713")} mcp ${import_chalk38.default.bold(mcpUrl)}`);
6573
+ console.log(` ${import_chalk39.default.green("\u2713")} mcp ${import_chalk39.default.bold(mcpUrl)}`);
6498
6574
  if (p.access === "invite") {
6499
- console.log(import_chalk38.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: invite \u2014 only you + emails you pre-approve"));
6500
- 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)`));
6575
+ console.log(import_chalk39.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: invite \u2014 only you + emails you pre-approve"));
6576
+ console.log(import_chalk39.default.dim(` Let others in: apiblaze preapprove someone@company.com${p.tenant ? ` --tenant ${p.tenant}` : ""} (or re-run with --access open)`));
6501
6577
  } else {
6502
- console.log(import_chalk38.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: open \u2014 anyone who signs in can call this API"));
6578
+ console.log(import_chalk39.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: open \u2014 anyone who signs in can call this API"));
6503
6579
  }
6504
6580
  }
6505
6581
  if (p.anon) {
6506
- console.log(import_chalk38.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
6582
+ console.log(import_chalk39.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
6507
6583
  }
6508
6584
  await runRepl(p);
6509
6585
  }
6510
6586
 
6511
6587
  // src/commands/consumer.ts
6512
- var import_chalk39 = __toESM(require("chalk"));
6513
- var import_ora21 = __toESM(require("ora"));
6588
+ var import_chalk40 = __toESM(require("chalk"));
6589
+ var import_ora22 = __toESM(require("ora"));
6514
6590
  init_admin();
6515
6591
  init_resolve();
6516
6592
  var DEFAULT_SCOPE = "openid email profile offline_access";
@@ -6531,7 +6607,7 @@ async function consumerFetch(creds, suffix, init) {
6531
6607
  function requireConsumer() {
6532
6608
  const c = loadConsumer();
6533
6609
  if (!c) {
6534
- console.error(import_chalk39.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
6610
+ console.error(import_chalk40.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
6535
6611
  process.exit(1);
6536
6612
  }
6537
6613
  return c;
@@ -6542,7 +6618,7 @@ async function runConsumerLogin(opts) {
6542
6618
  let clientId = opts.client;
6543
6619
  if (clientId) {
6544
6620
  if (!tenant2) {
6545
- console.error(import_chalk39.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
6621
+ console.error(import_chalk40.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
6546
6622
  process.exit(1);
6547
6623
  }
6548
6624
  } else {
@@ -6554,25 +6630,25 @@ async function runConsumerLogin(opts) {
6554
6630
  if (!picked) process.exit(1);
6555
6631
  tenant2 = picked;
6556
6632
  }
6557
- const s2 = (0, import_ora21.default)("Finding the login app...").start();
6633
+ const s2 = (0, import_ora22.default)("Finding the login app...").start();
6558
6634
  const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
6559
6635
  s2.stop();
6560
6636
  const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
6561
6637
  const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
6562
6638
  if (!pick2) {
6563
- console.error(import_chalk39.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
6639
+ console.error(import_chalk40.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
6564
6640
  process.exit(1);
6565
6641
  }
6566
6642
  clientId = pick2.client_id ?? pick2.clientId;
6567
6643
  }
6568
6644
  const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
6569
- console.log(`${import_chalk39.default.cyan("\u2192")} Logging in to ${import_chalk39.default.bold(tenant2)} as a consumer...`);
6645
+ console.log(`${import_chalk40.default.cyan("\u2192")} Logging in to ${import_chalk40.default.bold(tenant2)} as a consumer...`);
6570
6646
  const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
6571
6647
  console.log(`
6572
- Open: ${import_chalk39.default.underline(verificationUri)}`);
6573
- console.log(` Code: ${import_chalk39.default.bold(userCode)}
6648
+ Open: ${import_chalk40.default.underline(verificationUri)}`);
6649
+ console.log(` Code: ${import_chalk40.default.bold(userCode)}
6574
6650
  `);
6575
- console.log(import_chalk39.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
6651
+ console.log(import_chalk40.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
6576
6652
  }, portalResource);
6577
6653
  const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
6578
6654
  const creds = {
@@ -6587,7 +6663,7 @@ async function runConsumerLogin(opts) {
6587
6663
  obtainedAt: Date.now()
6588
6664
  };
6589
6665
  saveConsumer(creds);
6590
- console.log(import_chalk39.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
6666
+ console.log(import_chalk40.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
6591
6667
  }
6592
6668
  async function runConsumerTokens(opts) {
6593
6669
  const creds = requireConsumer();
@@ -6600,29 +6676,29 @@ async function runConsumerTokens(opts) {
6600
6676
  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));
6601
6677
  return;
6602
6678
  }
6603
- console.log(`${import_chalk39.default.cyan("Consumer")} ${import_chalk39.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk39.default.bold(fresh.tenant)}
6679
+ console.log(`${import_chalk40.default.cyan("Consumer")} ${import_chalk40.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk40.default.bold(fresh.tenant)}
6604
6680
  `);
6605
- console.log(`${import_chalk39.default.bold("access_token")} ${import_chalk39.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
6681
+ console.log(`${import_chalk40.default.bold("access_token")} ${import_chalk40.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
6606
6682
  ${fresh.accessToken}
6607
6683
  `);
6608
- if (fresh.idToken) console.log(`${import_chalk39.default.bold("id_token")} ${import_chalk39.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
6684
+ if (fresh.idToken) console.log(`${import_chalk40.default.bold("id_token")} ${import_chalk40.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
6609
6685
  ${fresh.idToken}
6610
6686
  `);
6611
- if (fresh.refreshToken) console.log(`${import_chalk39.default.bold("refresh_token")}
6687
+ if (fresh.refreshToken) console.log(`${import_chalk40.default.bold("refresh_token")}
6612
6688
  ${fresh.refreshToken}
6613
6689
  `);
6614
- console.log(import_chalk39.default.dim("These are your own tokens \u2014 keep them secret."));
6690
+ console.log(import_chalk40.default.dim("These are your own tokens \u2014 keep them secret."));
6615
6691
  }
6616
6692
  async function runConsumerApikeys(opts) {
6617
6693
  const creds = requireConsumer();
6618
6694
  const { default: inquirer3 } = await import("inquirer");
6619
- const spinner = (0, import_ora21.default)("Loading your API keys...").start();
6695
+ const spinner = (0, import_ora22.default)("Loading your API keys...").start();
6620
6696
  const list = await consumerFetch(creds, "/apikeys");
6621
6697
  const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
6622
6698
  spinner.stop();
6623
6699
  if (list.status >= 400) {
6624
- console.error(import_chalk39.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
6625
- if (list.status === 401) console.error(import_chalk39.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
6700
+ console.error(import_chalk40.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
6701
+ if (list.status === 401) console.error(import_chalk40.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
6626
6702
  process.exit(1);
6627
6703
  }
6628
6704
  const keys = list.data?.keys ?? [];
@@ -6630,16 +6706,16 @@ async function runConsumerApikeys(opts) {
6630
6706
  if (opts.json) {
6631
6707
  console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
6632
6708
  } else if (!keys.length) {
6633
- console.log(import_chalk39.default.yellow("No API keys yet."));
6709
+ console.log(import_chalk40.default.yellow("No API keys yet."));
6634
6710
  } else {
6635
6711
  for (const k of keys) {
6636
6712
  const clear = revealMap[k.environment]?.key;
6637
- const shown = clear ? import_chalk39.default.green(clear) : import_chalk39.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
6638
- const exp = k.expires_at ? import_chalk39.default.dim(`exp ${k.expires_at}`) : import_chalk39.default.dim("no expiry");
6639
- console.log(` ${import_chalk39.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk39.default.dim(k.description ?? "")}`);
6713
+ const shown = clear ? import_chalk40.default.green(clear) : import_chalk40.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
6714
+ const exp = k.expires_at ? import_chalk40.default.dim(`exp ${k.expires_at}`) : import_chalk40.default.dim("no expiry");
6715
+ console.log(` ${import_chalk40.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk40.default.dim(k.description ?? "")}`);
6640
6716
  }
6641
6717
  if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
6642
- console.log(import_chalk39.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
6718
+ console.log(import_chalk40.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
6643
6719
  }
6644
6720
  }
6645
6721
  if (opts.json) return;
@@ -6653,7 +6729,7 @@ async function runConsumerApikeys(opts) {
6653
6729
  const body = { environment: answers.environment };
6654
6730
  if (answers.description) body.description = answers.description;
6655
6731
  if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
6656
- const s2 = (0, import_ora21.default)("Creating key...").start();
6732
+ const s2 = (0, import_ora22.default)("Creating key...").start();
6657
6733
  const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
6658
6734
  if (created.status >= 400) {
6659
6735
  s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
@@ -6661,13 +6737,13 @@ async function runConsumerApikeys(opts) {
6661
6737
  }
6662
6738
  s2.succeed("Key created.");
6663
6739
  const key = created.data?.key ?? created.data?.fullKey;
6664
- if (key) console.log(` ${import_chalk39.default.green(key)} ${import_chalk39.default.dim("(shown once \u2014 store it now)")}`);
6665
- else console.log(import_chalk39.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
6740
+ if (key) console.log(` ${import_chalk40.default.green(key)} ${import_chalk40.default.dim("(shown once \u2014 store it now)")}`);
6741
+ else console.log(import_chalk40.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
6666
6742
  }
6667
6743
 
6668
6744
  // src/commands/sidecar.ts
6669
- var import_chalk40 = __toESM(require("chalk"));
6670
- var import_ora22 = __toESM(require("ora"));
6745
+ var import_chalk41 = __toESM(require("chalk"));
6746
+ var import_ora23 = __toESM(require("ora"));
6671
6747
  var fs10 = __toESM(require("fs"));
6672
6748
  var path7 = __toESM(require("path"));
6673
6749
  init_admin();
@@ -6709,18 +6785,18 @@ function upsertEnvLocal(root, token) {
6709
6785
  }
6710
6786
  function installSidecarPackage(root) {
6711
6787
  if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
6712
- console.log(` ${import_chalk40.default.green("\u2713")} apiblaze package already installed`);
6788
+ console.log(` ${import_chalk41.default.green("\u2713")} apiblaze package already installed`);
6713
6789
  return;
6714
6790
  }
6715
6791
  const has = (f) => fs10.existsSync(path7.join(root, f));
6716
6792
  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" };
6717
- const spinner = (0, import_ora22.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
6793
+ const spinner = (0, import_ora23.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
6718
6794
  try {
6719
6795
  const { execSync } = require("child_process");
6720
6796
  execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
6721
6797
  spinner.succeed("Installed apiblaze (the sidecar runtime).");
6722
6798
  } catch {
6723
- 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")}.`);
6799
+ spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk41.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk41.default.cyan("npm run dev")}.`);
6724
6800
  }
6725
6801
  }
6726
6802
  function readEnvKey(root) {
@@ -6845,8 +6921,8 @@ function generateInspector(root, router) {
6845
6921
  fs10.writeFileSync(f2, INSPECTOR_PAGE);
6846
6922
  return path7.relative(root, f2);
6847
6923
  }
6848
- const base = fs10.existsSync(path7.join(root, "src", "app")) ? path7.join(root, "src", "app") : path7.join(root, "app");
6849
- const dir = path7.join(base, "abz-inspector");
6924
+ const base2 = fs10.existsSync(path7.join(root, "src", "app")) ? path7.join(root, "src", "app") : path7.join(root, "app");
6925
+ const dir = path7.join(base2, "abz-inspector");
6850
6926
  fs10.mkdirSync(dir, { recursive: true });
6851
6927
  const f = path7.join(dir, "page.tsx");
6852
6928
  fs10.writeFileSync(f, INSPECTOR_PAGE);
@@ -6859,7 +6935,7 @@ async function runAnonymousInit(root, router, opts) {
6859
6935
  const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
6860
6936
  const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
6861
6937
  if (opts.newSession) clearAnonCred2();
6862
- const spinner = (0, import_ora22.default)("Setting up a sidecar (no login needed)...").start();
6938
+ const spinner = (0, import_ora23.default)("Setting up a sidecar (no login needed)...").start();
6863
6939
  let out;
6864
6940
  try {
6865
6941
  out = await sidecarInitAnonymous2();
@@ -6871,29 +6947,29 @@ async function runAnonymousInit(root, router, opts) {
6871
6947
  if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
6872
6948
  const envState = upsertEnvLocal(root, out.token);
6873
6949
  ensureGitignored(root);
6874
- console.log(` ${import_chalk40.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6875
- console.log(` ${import_chalk40.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6950
+ console.log(` ${import_chalk41.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6951
+ console.log(` ${import_chalk41.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6876
6952
  installSidecarPackage(root);
6877
6953
  let inspectorPath = null;
6878
6954
  if (!opts.noInspector) {
6879
6955
  inspectorPath = generateInspector(root, router);
6880
- if (inspectorPath) console.log(` ${import_chalk40.default.green("\u2713")} inspector at ${inspectorPath}`);
6956
+ if (inspectorPath) console.log(` ${import_chalk41.default.green("\u2713")} inspector at ${inspectorPath}`);
6881
6957
  }
6882
6958
  console.log("");
6883
- console.log(import_chalk40.default.bold("Done (no account needed). What happens next:"));
6884
- console.log(` 1. ${import_chalk40.default.cyan("npm run dev")} and use your app.`);
6959
+ console.log(import_chalk41.default.bold("Done (no account needed). What happens next:"));
6960
+ console.log(` 1. ${import_chalk41.default.cyan("npm run dev")} and use your app.`);
6885
6961
  console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
6886
- console.log(` ${import_chalk40.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
6962
+ console.log(` ${import_chalk41.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
6887
6963
  console.log("");
6888
- console.log(import_chalk40.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
6889
- console.log(` ${import_chalk40.default.cyan("apiblaze login")} then ${import_chalk40.default.cyan("apiblaze claim")} ${import_chalk40.default.dim("(no code needed here)")}`);
6890
- console.log(import_chalk40.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
6964
+ console.log(import_chalk41.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
6965
+ console.log(` ${import_chalk41.default.cyan("apiblaze login")} then ${import_chalk41.default.cyan("apiblaze claim")} ${import_chalk41.default.dim("(no code needed here)")}`);
6966
+ console.log(import_chalk41.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
6891
6967
  }
6892
6968
  async function runSidecar(opts) {
6893
6969
  const root = path7.resolve(opts.dir ?? process.cwd());
6894
6970
  const detected = detectNextProject(root);
6895
6971
  if (!detected.found) {
6896
- console.log(import_chalk40.default.yellow(`No Next.js project detected in ${root}.`));
6972
+ console.log(import_chalk41.default.yellow(`No Next.js project detected in ${root}.`));
6897
6973
  console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
6898
6974
  return;
6899
6975
  }
@@ -6904,10 +6980,10 @@ async function runSidecar(opts) {
6904
6980
  if (!loadCredentials()) {
6905
6981
  upsertEnvLocal(root, readEnvKey(root));
6906
6982
  ensureGitignored(root);
6907
- console.log(` ${import_chalk40.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
6908
- console.log(` ${import_chalk40.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6983
+ console.log(` ${import_chalk41.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
6984
+ console.log(` ${import_chalk41.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6909
6985
  installSidecarPackage(root);
6910
- console.log(import_chalk40.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
6986
+ console.log(import_chalk41.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
6911
6987
  return;
6912
6988
  }
6913
6989
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -6916,7 +6992,7 @@ async function runSidecar(opts) {
6916
6992
  const mustMint = !existingKey || opts.rotate || switchingTeam;
6917
6993
  let token = existingKey ?? "";
6918
6994
  if (mustMint) {
6919
- 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();
6995
+ const spinner = (0, import_ora23.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
6920
6996
  try {
6921
6997
  const out = await admin({
6922
6998
  method: "POST",
@@ -6930,39 +7006,39 @@ async function runSidecar(opts) {
6930
7006
  throw err;
6931
7007
  }
6932
7008
  } else {
6933
- 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).`));
7009
+ console.log(import_chalk41.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
6934
7010
  }
6935
7011
  const envState = upsertEnvLocal(root, token);
6936
7012
  ensureGitignored(root);
6937
- console.log(` ${import_chalk40.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
7013
+ console.log(` ${import_chalk41.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6938
7014
  const wireState = wireInstrumentation(root);
6939
- console.log(` ${import_chalk40.default.green("\u2713")} instrumentation.ts ${wireState}`);
7015
+ console.log(` ${import_chalk41.default.green("\u2713")} instrumentation.ts ${wireState}`);
6940
7016
  installSidecarPackage(root);
6941
7017
  let inspectorPath = null;
6942
7018
  if (!opts.noInspector) {
6943
7019
  inspectorPath = generateInspector(root, detected.router);
6944
- if (inspectorPath) console.log(` ${import_chalk40.default.green("\u2713")} inspector at ${inspectorPath}`);
7020
+ if (inspectorPath) console.log(` ${import_chalk41.default.green("\u2713")} inspector at ${inspectorPath}`);
6945
7021
  }
6946
7022
  console.log("");
6947
- console.log(import_chalk40.default.bold("Done. What happens next:"));
6948
- console.log(` 1. ${import_chalk40.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
6949
- console.log(` 2. The origins your app calls appear as ${import_chalk40.default.bold("candidates")} \u2014 list them: ${import_chalk40.default.cyan("apiblaze sidecar")}`);
6950
- console.log(` 3. Approve the ones to route: ${import_chalk40.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
7023
+ console.log(import_chalk41.default.bold("Done. What happens next:"));
7024
+ console.log(` 1. ${import_chalk41.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
7025
+ console.log(` 2. The origins your app calls appear as ${import_chalk41.default.bold("candidates")} \u2014 list them: ${import_chalk41.default.cyan("apiblaze sidecar")}`);
7026
+ console.log(` 3. Approve the ones to route: ${import_chalk41.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
6951
7027
  console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
6952
- 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)`);
6953
- 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>\`.`));
7028
+ if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk41.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
7029
+ if (switchingTeam) console.log(import_chalk41.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
6954
7030
  console.log("");
6955
- console.log(import_chalk40.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
6956
- console.log(import_chalk40.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
6957
- console.log(import_chalk40.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
7031
+ console.log(import_chalk41.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
7032
+ console.log(import_chalk41.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
7033
+ console.log(import_chalk41.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
6958
7034
  console.log("");
6959
- 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."));
6960
- console.log(import_chalk40.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
7035
+ console.log(import_chalk41.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
7036
+ console.log(import_chalk41.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
6961
7037
  }
6962
7038
 
6963
7039
  // src/commands/origins.ts
6964
- var import_chalk41 = __toESM(require("chalk"));
6965
- var import_ora23 = __toESM(require("ora"));
7040
+ var import_chalk42 = __toESM(require("chalk"));
7041
+ var import_ora24 = __toESM(require("ora"));
6966
7042
  init_admin();
6967
7043
  init_resolve();
6968
7044
  init_auth();
@@ -6972,7 +7048,7 @@ async function runOriginsList(opts) {
6972
7048
  if (!loadCredentials()) {
6973
7049
  const cred = loadAnonCred();
6974
7050
  if (!cred) {
6975
- console.log(import_chalk41.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
7051
+ console.log(import_chalk42.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
6976
7052
  return;
6977
7053
  }
6978
7054
  out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
@@ -6990,30 +7066,30 @@ async function runOriginsList(opts) {
6990
7066
  }
6991
7067
  const routed = out.routed ?? [];
6992
7068
  const candidates = out.candidates ?? [];
6993
- console.log(import_chalk41.default.bold(`
7069
+ console.log(import_chalk42.default.bold(`
6994
7070
  Routed through APIblaze (${routed.length})`));
6995
- if (!routed.length) console.log(import_chalk41.default.dim(" none yet"));
6996
- for (const r of routed) console.log(` ${import_chalk41.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk41.default.dim(`\u2192 ${r.project_id}`)}`);
6997
- console.log(import_chalk41.default.bold(`
7071
+ if (!routed.length) console.log(import_chalk42.default.dim(" none yet"));
7072
+ for (const r of routed) console.log(` ${import_chalk42.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk42.default.dim(`\u2192 ${r.project_id}`)}`);
7073
+ console.log(import_chalk42.default.bold(`
6998
7074
  Candidates \u2014 going direct, not yet approved (${candidates.length})`));
6999
- if (!candidates.length) console.log(import_chalk41.default.dim(" none \u2014 run your app to discover the origins it calls"));
7075
+ if (!candidates.length) console.log(import_chalk42.default.dim(" none \u2014 run your app to discover the origins it calls"));
7000
7076
  for (const c of candidates) {
7001
- console.log(` ${import_chalk41.default.yellow("\u25CB")} ${c.origin} ${import_chalk41.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
7077
+ console.log(` ${import_chalk42.default.yellow("\u25CB")} ${c.origin} ${import_chalk42.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
7002
7078
  }
7003
7079
  if (candidates.length) {
7004
- console.log(import_chalk41.default.dim(`
7080
+ console.log(import_chalk42.default.dim(`
7005
7081
  Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
7006
- console.log(import_chalk41.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
7082
+ console.log(import_chalk42.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
7007
7083
  }
7008
7084
  }
7009
7085
  async function runOriginsApprove(origin, opts) {
7010
7086
  if (!loadCredentials()) {
7011
7087
  const cred = loadAnonCred();
7012
7088
  if (!cred) {
7013
- console.error(import_chalk41.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
7089
+ console.error(import_chalk42.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
7014
7090
  process.exit(1);
7015
7091
  }
7016
- const spinner2 = (0, import_ora23.default)(`Approving ${origin} (anonymous)...`).start();
7092
+ const spinner2 = (0, import_ora24.default)(`Approving ${origin} (anonymous)...`).start();
7017
7093
  try {
7018
7094
  const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
7019
7095
  spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
@@ -7024,7 +7100,7 @@ async function runOriginsApprove(origin, opts) {
7024
7100
  return;
7025
7101
  }
7026
7102
  const { teamId } = await resolveTeam(opts.team);
7027
- const spinner = (0, import_ora23.default)(`Approving ${origin}...`).start();
7103
+ const spinner = (0, import_ora24.default)(`Approving ${origin}...`).start();
7028
7104
  try {
7029
7105
  const out = await admin({
7030
7106
  method: "POST",
@@ -7041,7 +7117,7 @@ async function runOriginsApprove(origin, opts) {
7041
7117
  }
7042
7118
  async function runOriginsDeny(origin, opts) {
7043
7119
  const { teamId } = await resolveTeam(opts.team);
7044
- const spinner = (0, import_ora23.default)(`Dismissing ${origin}...`).start();
7120
+ const spinner = (0, import_ora24.default)(`Dismissing ${origin}...`).start();
7045
7121
  try {
7046
7122
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
7047
7123
  spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
@@ -7052,7 +7128,7 @@ async function runOriginsDeny(origin, opts) {
7052
7128
  }
7053
7129
  async function runOriginsRemove(origin, opts) {
7054
7130
  const { teamId } = await resolveTeam(opts.team);
7055
- const spinner = (0, import_ora23.default)(`Removing the proxy for ${origin}...`).start();
7131
+ const spinner = (0, import_ora24.default)(`Removing the proxy for ${origin}...`).start();
7056
7132
  try {
7057
7133
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
7058
7134
  spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
@@ -7063,7 +7139,7 @@ async function runOriginsRemove(origin, opts) {
7063
7139
  }
7064
7140
 
7065
7141
  // src/commands/op.ts
7066
- var import_chalk42 = __toESM(require("chalk"));
7142
+ var import_chalk43 = __toESM(require("chalk"));
7067
7143
  init_auth();
7068
7144
  init_trace();
7069
7145
  init_types();
@@ -7096,82 +7172,82 @@ function printResidue(report, applied) {
7096
7172
  const up = report?.upstash ?? {};
7097
7173
  const fga = report?.fga ?? {};
7098
7174
  const ghosts = report?.ghosts ?? {};
7099
- console.log(import_chalk42.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
7100
- console.log(import_chalk42.default.bold("\n Upstash"));
7175
+ console.log(import_chalk43.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
7176
+ console.log(import_chalk43.default.bold("\n Upstash"));
7101
7177
  const orphans = up.orphans ?? [];
7102
- if (orphans.length === 0) console.log(import_chalk42.default.green(" no orphaned keys"));
7103
- for (const o of orphans) console.log(` ${import_chalk42.default.yellow(o.key)} ${import_chalk42.default.dim(`\u2014 ${o.reason}`)}`);
7104
- console.log(import_chalk42.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
7178
+ if (orphans.length === 0) console.log(import_chalk43.default.green(" no orphaned keys"));
7179
+ for (const o of orphans) console.log(` ${import_chalk43.default.yellow(o.key)} ${import_chalk43.default.dim(`\u2014 ${o.reason}`)}`);
7180
+ console.log(import_chalk43.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
7105
7181
  if (up.anon_wallet_detail) {
7106
7182
  const d = up.anon_wallet_detail;
7107
- 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)"}`));
7183
+ console.log(import_chalk43.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
7184
  }
7109
7185
  if (up.keyspace_census) {
7110
7186
  const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
7111
- console.log(import_chalk42.default.dim(` keyspace: ${census}`));
7187
+ console.log(import_chalk43.default.dim(` keyspace: ${census}`));
7112
7188
  }
7113
- if (up.unknown?.length) console.log(import_chalk42.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
7114
- if (applied) console.log(` ${import_chalk42.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
7115
- for (const e of up.errors ?? []) console.log(import_chalk42.default.red(` error: ${e}`));
7116
- console.log(import_chalk42.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
7189
+ if (up.unknown?.length) console.log(import_chalk43.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
7190
+ if (applied) console.log(` ${import_chalk43.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
7191
+ for (const e of up.errors ?? []) console.log(import_chalk43.default.red(` error: ${e}`));
7192
+ console.log(import_chalk43.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
7117
7193
  if (applied) {
7118
7194
  const swept = fga?.swept ?? [];
7119
- if (swept.length === 0) console.log(import_chalk42.default.green(" no orphaned stores"));
7195
+ if (swept.length === 0) console.log(import_chalk43.default.green(" no orphaned stores"));
7120
7196
  for (const s of swept) {
7121
7197
  console.log(
7122
- ` ${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`)}`
7198
+ ` ${import_chalk43.default.yellow(s.store_id)} ${import_chalk43.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
7123
7199
  );
7124
7200
  }
7125
- if (fga?.remaining) console.log(import_chalk42.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
7201
+ if (fga?.remaining) console.log(import_chalk43.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
7126
7202
  const st = fga?.side_tables;
7127
- 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})` : ""}`));
7203
+ if (st) console.log(import_chalk43.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
7204
  } else {
7129
7205
  const fgaOrphans = fga?.orphans ?? [];
7130
- if (fgaOrphans.length === 0) console.log(import_chalk42.default.green(" no orphaned stores"));
7206
+ if (fgaOrphans.length === 0) console.log(import_chalk43.default.green(" no orphaned stores"));
7131
7207
  for (const s of fgaOrphans) {
7132
7208
  const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
7133
- 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)`)}`);
7209
+ console.log(` ${import_chalk43.default.yellow(s.store_id)} ${import_chalk43.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
7134
7210
  }
7135
- console.log(import_chalk42.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
7211
+ console.log(import_chalk43.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
7136
7212
  const st = fga?.side_tables;
7137
- 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`));
7213
+ if (st) console.log(import_chalk43.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
7214
  }
7139
- for (const e of fga?.errors ?? []) console.log(import_chalk42.default.red(` error: ${e}`));
7140
- console.log(import_chalk42.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
7215
+ for (const e of fga?.errors ?? []) console.log(import_chalk43.default.red(` error: ${e}`));
7216
+ console.log(import_chalk43.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
7141
7217
  if (applied) {
7142
- if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk42.default.green(" no ghost tuples"));
7143
- 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)`)}`);
7218
+ if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk43.default.green(" no ghost tuples"));
7219
+ else console.log(` ${import_chalk43.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk43.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
7144
7220
  } else {
7145
7221
  const n = ghosts?.ghost_count ?? 0;
7146
- 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)`)}`));
7222
+ if (n === 0) console.log(import_chalk43.default.green(` no ghost tuples ${import_chalk43.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
7147
7223
  else {
7148
- console.log(import_chalk42.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
7224
+ console.log(import_chalk43.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
7149
7225
  for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
7150
- console.log(import_chalk42.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
7226
+ console.log(import_chalk43.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
7151
7227
  }
7152
- if (n > 20) console.log(import_chalk42.default.dim(` \u2026 and ${n - 20} more`));
7228
+ if (n > 20) console.log(import_chalk43.default.dim(` \u2026 and ${n - 20} more`));
7153
7229
  }
7154
7230
  }
7155
- for (const e of ghosts?.errors ?? []) console.log(import_chalk42.default.red(` error: ${e}`));
7231
+ for (const e of ghosts?.errors ?? []) console.log(import_chalk43.default.red(` error: ${e}`));
7156
7232
  console.log();
7157
7233
  }
7158
7234
  async function runOp(sub, opts = {}) {
7159
7235
  if (!loadCredentials()) {
7160
- console.log(import_chalk42.default.dim("Not logged in. Run `apiblaze login`."));
7236
+ console.log(import_chalk43.default.dim("Not logged in. Run `apiblaze login`."));
7161
7237
  return;
7162
7238
  }
7163
7239
  if (!isOperatorLogin()) {
7164
- console.log(import_chalk42.default.dim("`apiblaze op` is only available to platform operators."));
7240
+ console.log(import_chalk43.default.dim("`apiblaze op` is only available to platform operators."));
7165
7241
  return;
7166
7242
  }
7167
7243
  switch (sub) {
7168
7244
  case void 0:
7169
7245
  case "menu": {
7170
- console.log(import_chalk42.default.bold("\nOperator menu"));
7171
- console.log(` ${import_chalk42.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
7172
- console.log(` ${import_chalk42.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk42.default.dim("-y to skip")})`);
7173
- console.log(` ${import_chalk42.default.cyan("apiblaze op credits")} list credit wallets`);
7174
- console.log(import_chalk42.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
7246
+ console.log(import_chalk43.default.bold("\nOperator menu"));
7247
+ console.log(` ${import_chalk43.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
7248
+ console.log(` ${import_chalk43.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk43.default.dim("-y to skip")})`);
7249
+ console.log(` ${import_chalk43.default.cyan("apiblaze op credits")} list credit wallets`);
7250
+ console.log(import_chalk43.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
7175
7251
  `));
7176
7252
  return;
7177
7253
  }
@@ -7190,17 +7266,17 @@ async function runOp(sub, opts = {}) {
7190
7266
  const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
7191
7267
  printResidue(report, false);
7192
7268
  if (nUp + nFga + nGhost + nSide === 0) {
7193
- console.log(import_chalk42.default.green("Nothing to sweep."));
7269
+ console.log(import_chalk43.default.green("Nothing to sweep."));
7194
7270
  return;
7195
7271
  }
7196
7272
  if (!opts.yes) {
7197
7273
  const readline2 = await import("readline/promises");
7198
7274
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
7199
7275
  const answer = await rl.question(
7200
- 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: `)
7276
+ import_chalk43.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
7277
  );
7202
7278
  rl.close();
7203
- if (answer.trim() !== "sweep") return void console.log(import_chalk42.default.dim("Aborted."));
7279
+ if (answer.trim() !== "sweep") return void console.log(import_chalk43.default.dim("Aborted."));
7204
7280
  }
7205
7281
  const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
7206
7282
  if (opts.json) return void console.log(JSON.stringify(result, null, 2));
@@ -7211,15 +7287,15 @@ async function runOp(sub, opts = {}) {
7211
7287
  const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
7212
7288
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
7213
7289
  const accounts = data?.accounts ?? [];
7214
- if (accounts.length === 0) return void console.log(import_chalk42.default.dim("No credit wallets."));
7290
+ if (accounts.length === 0) return void console.log(import_chalk43.default.dim("No credit wallets."));
7215
7291
  for (const a of accounts) {
7216
7292
  const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
7217
- 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") : ""}`);
7293
+ console.log(` ${import_chalk43.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk43.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk43.default.dim(" \u2014 anon") : ""}`);
7218
7294
  }
7219
7295
  return;
7220
7296
  }
7221
7297
  default:
7222
- console.log(import_chalk42.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
7298
+ console.log(import_chalk43.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
7223
7299
  }
7224
7300
  }
7225
7301
 
@@ -7282,7 +7358,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
7282
7358
  try {
7283
7359
  const resolved = parseInt(port ?? opts.port, 10);
7284
7360
  if (Number.isNaN(resolved)) {
7285
- console.error(import_chalk43.default.red(`Invalid port: ${port ?? opts.port}`));
7361
+ console.error(import_chalk44.default.red(`Invalid port: ${port ?? opts.port}`));
7286
7362
  process.exit(1);
7287
7363
  }
7288
7364
  await runDev({ port: resolved, captureFile: opts.captureFile });
@@ -7360,6 +7436,11 @@ tenant.command("create").description("Create a tenant in your team (tenant names
7360
7436
  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)));
7361
7437
  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)));
7362
7438
  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)));
7439
+ var admins = new import_commander.Command("admins").description("Manage who can administer a tenant's users & groups (the first-admin bootstrap the widget needs)").option("--tenant <slug>", "Tenant slug (e.g. nino)").option("--team <id|name>", "Team (defaults to active team, or your anonymous workspace)").option("--json", "Output machine-readable JSON").action(action((opts) => runAdminsList(opts)));
7440
+ admins.command("list").description("List a tenant's admins").option("--tenant <slug>", "Tenant slug").option("--team <id|name>", "Team").option("--json", "JSON").action(action((opts, cmd) => runAdminsList({ ...cmd.parent?.opts(), ...opts })));
7441
+ admins.command("add").description("Add an email as a tenant admin (e.g. yourself, to start)").argument("<email>", "Email to grant admin").option("--tenant <slug>", "Tenant slug").option("--team <id|name>", "Team").option("--json", "JSON").action(action((email, opts, cmd) => runAdminsAdd(email, { ...cmd.parent?.opts(), ...opts })));
7442
+ admins.command("remove").description("Remove a tenant admin").argument("<email>", "Email to remove").option("--tenant <slug>", "Tenant slug").option("--team <id|name>", "Team").option("--json", "JSON").action(action((email, opts, cmd) => runAdminsRemove(email, { ...cmd.parent?.opts(), ...opts })));
7443
+ program.addCommand(admins);
7363
7444
  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)));
7364
7445
  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)));
7365
7446
  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)));
@@ -7377,7 +7458,7 @@ spec.command("delete-rule").description("Delete the saved rules for a route (e.g
7377
7458
  var HELP_GROUPS = [
7378
7459
  { title: "Chat", commands: ["apichat", "agent"] },
7379
7460
  { title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
7380
- { title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
7461
+ { title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "admins", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
7381
7462
  { title: "Data plane commands", commands: [
7382
7463
  { parent: "consumer", sub: "login" },
7383
7464
  { parent: "consumer", sub: "apikeys" }
@@ -7396,7 +7477,7 @@ function groupedCommandHelp() {
7396
7477
  const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
7397
7478
  return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
7398
7479
  }).filter(Boolean).join("\n");
7399
- return `${import_chalk43.default.bold(g.title)}
7480
+ return `${import_chalk44.default.bold(g.title)}
7400
7481
  ${rows}`;
7401
7482
  }).join("\n\n");
7402
7483
  }
@@ -7430,14 +7511,14 @@ async function recoverStaleTeam() {
7430
7511
  const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
7431
7512
  const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
7432
7513
  if (!linked) {
7433
- console.error(import_chalk43.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
7514
+ console.error(import_chalk44.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
7434
7515
  return;
7435
7516
  }
7436
7517
  if (linked.teamId === creds.teamId) return;
7437
7518
  const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
7438
7519
  delete next.activeTenant;
7439
7520
  saveCredentials(next);
7440
- 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.`));
7521
+ console.error(import_chalk44.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk44.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
7441
7522
  } catch {
7442
7523
  }
7443
7524
  }
@@ -7445,16 +7526,16 @@ async function printError(err) {
7445
7526
  if (err instanceof ApiError) {
7446
7527
  const data = err.body;
7447
7528
  const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
7448
- console.error(import_chalk43.default.red(`
7529
+ console.error(import_chalk44.default.red(`
7449
7530
  API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
7450
7531
  if (err.status === 403 || err.status === 404) {
7451
7532
  await recoverStaleTeam();
7452
7533
  }
7453
7534
  } else if (err instanceof Error) {
7454
- console.error(import_chalk43.default.red(`
7535
+ console.error(import_chalk44.default.red(`
7455
7536
  Error: ${err.message}`));
7456
7537
  } else {
7457
- console.error(import_chalk43.default.red("\nUnknown error"));
7538
+ console.error(import_chalk44.default.red("\nUnknown error"));
7458
7539
  }
7459
7540
  }
7460
7541
  program.parse(process.argv);