apiblaze 0.17.21 → 0.17.23

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.
package/dist/index.js CHANGED
@@ -572,7 +572,7 @@ async function createTenantInteractive(teamId) {
572
572
  type: "confirm",
573
573
  name: "users",
574
574
  default: false,
575
- message: `Enable Users & groups? ${import_chalk23.default.dim(`(identity/key management at ${tenant2}.iam.apiblaze.com)`)}`
575
+ message: `Enable Users & groups? ${import_chalk23.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
576
576
  }]);
577
577
  if (users) {
578
578
  await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
@@ -700,10 +700,10 @@ var init_tenant_pick = __esm({
700
700
 
701
701
  // src/index.ts
702
702
  var import_commander = require("commander");
703
- var import_chalk38 = __toESM(require("chalk"));
703
+ var import_chalk39 = __toESM(require("chalk"));
704
704
 
705
705
  // package.json
706
- var version = "0.17.21";
706
+ var version = "0.17.23";
707
707
 
708
708
  // src/index.ts
709
709
  init_types();
@@ -1385,9 +1385,9 @@ function printTunnelEndpoints(restore, targets) {
1385
1385
  if (restore.length === 0) return;
1386
1386
  console.log(import_chalk4.default.bold("\nYour proxy is live at:"));
1387
1387
  for (const r of restore) {
1388
- const label2 = targets.find((t) => t.projectId === r.projectId)?.projectName ?? r.projectId;
1388
+ const label3 = targets.find((t) => t.projectId === r.projectId)?.projectName ?? r.projectId;
1389
1389
  console.log(`
1390
- ${import_chalk4.default.bold(label2)}`);
1390
+ ${import_chalk4.default.bold(label3)}`);
1391
1391
  const internalEnvs = Object.keys(r.environments ?? {}).filter((e) => isInternalTarget(r.environments[e]?.target));
1392
1392
  const envs = internalEnvs.includes("dev") ? ["dev"] : internalEnvs.length ? internalEnvs : ["dev"];
1393
1393
  for (const env of envs) {
@@ -2013,8 +2013,8 @@ async function runClaim(claimCodeArg, opts) {
2013
2013
  if (res.merged) {
2014
2014
  console.log(` Merged ${import_chalk7.default.bold(String(res.proxy_count ?? ""))} prox${res.proxy_count === 1 ? "y" : "ies"} into team ${import_chalk7.default.bold(res.team_id)}.`);
2015
2015
  } else {
2016
- const label2 = res.team_name ? `\u201C${res.team_name}\u201D` : res.team_id;
2017
- console.log(` It's now your team ${import_chalk7.default.bold(label2)}. Switch to it with ${import_chalk7.default.cyan(`apiblaze team ${res.team_id}`)}.`);
2016
+ const label3 = res.team_name ? `\u201C${res.team_name}\u201D` : res.team_id;
2017
+ console.log(` It's now your team ${import_chalk7.default.bold(label3)}. Switch to it with ${import_chalk7.default.cyan(`apiblaze team ${res.team_id}`)}.`);
2018
2018
  console.log(import_chalk7.default.dim(` (Tip: name it with --name, or --team <name> to merge into an existing team instead.)`));
2019
2019
  }
2020
2020
  console.log(`
@@ -3158,7 +3158,7 @@ Tenant ${tenant2}`));
3158
3158
  pageSize: 12,
3159
3159
  choices: [
3160
3160
  { name: `Login methods (${nLogin}) ${import_chalk26.default.dim("how your consumers sign in")}`, value: "login" },
3161
- { name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk26.default.dim(`identity & key management (${tenant2}.iam.apiblaze.com)`)}`, value: "iam" },
3161
+ { name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk26.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
3162
3162
  { name: `Portal admins (${nEmails}) ${import_chalk26.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
3163
3163
  new inquirer3.Separator(import_chalk26.default.dim(" Settings")),
3164
3164
  { name: `CORS: ${cors?.cors ? import_chalk26.default.cyan(JSON.stringify(cors.cors)) : import_chalk26.default.dim("(unset)")} ${import_chalk26.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
@@ -4224,9 +4224,9 @@ function printAll(proj2, cfg, json) {
4224
4224
  console.log(import_chalk30.default.bold(`
4225
4225
  ${proj2.projectName} v${proj2.apiVersion} \u2014 settings
4226
4226
  `));
4227
- for (const group of SETTING_GROUPS) {
4228
- console.log(import_chalk30.default.bold(group));
4229
- for (const s of SETTINGS.filter((x) => x.group === group)) {
4227
+ for (const group2 of SETTING_GROUPS) {
4228
+ console.log(import_chalk30.default.bold(group2));
4229
+ for (const s of SETTINGS.filter((x) => x.group === group2)) {
4230
4230
  console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk30.default.dim(s.desc)}`);
4231
4231
  }
4232
4232
  console.log();
@@ -4335,14 +4335,14 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
4335
4335
  }
4336
4336
  }
4337
4337
  }
4338
- async function settingsGroup(proj2, cfg, group) {
4338
+ async function settingsGroup(proj2, cfg, group2) {
4339
4339
  const { default: inquirer3 } = await import("inquirer");
4340
4340
  for (; ; ) {
4341
- const items = SETTINGS.filter((s2) => s2.group === group);
4341
+ const items = SETTINGS.filter((s2) => s2.group === group2);
4342
4342
  const { pick: pick2 } = await inquirer3.prompt([{
4343
4343
  type: "list",
4344
4344
  name: "pick",
4345
- message: group + ":",
4345
+ message: group2 + ":",
4346
4346
  pageSize: 16,
4347
4347
  choices: [
4348
4348
  ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk30.default.dim(s2.desc)}`, value: s2 })),
@@ -4754,9 +4754,227 @@ async function agentsMenu(proj2, opts) {
4754
4754
  else await runMcp(proj2.projectName, proj2.apiVersion, {});
4755
4755
  }
4756
4756
 
4757
- // src/commands/key.ts
4757
+ // src/commands/group.ts
4758
4758
  var import_chalk31 = __toESM(require("chalk"));
4759
4759
  var import_ora15 = __toESM(require("ora"));
4760
+ init_auth();
4761
+ init_admin();
4762
+ init_types();
4763
+ var DASHBOARD_BASE6 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
4764
+ var IAM_BASE = process.env.APIBLAZE_IAM_BASE || "https://iam.apiblaze.com";
4765
+ async function resolveTransport(opts) {
4766
+ const producer = loadCredentials();
4767
+ if (producer) {
4768
+ const { teamId } = await resolveTeam(void 0);
4769
+ let tenant2 = (opts.tenant ?? "").trim().toLowerCase();
4770
+ if (!tenant2) {
4771
+ const out = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants`, summary: "List tenants" });
4772
+ const tenants = (out?.tenants ?? []).map((t) => String(t.tenant_name ?? t.name ?? t)).filter((t) => !t.startsWith("op"));
4773
+ if (tenants.length === 0) throw new Error("No tenants in your team yet. Create a proxy first, or pass --tenant.");
4774
+ if (tenants.length === 1) tenant2 = tenants[0];
4775
+ else {
4776
+ const { default: inquirer3 } = await import("inquirer");
4777
+ ({ tenant: tenant2 } = await inquirer3.prompt([{ type: "list", name: "tenant", message: "Which tenant?", choices: tenants }]));
4778
+ }
4779
+ }
4780
+ const token = getAccessToken();
4781
+ return {
4782
+ tenant: tenant2,
4783
+ call: async (path8, method = "GET", body) => {
4784
+ const res = await fetch(`${DASHBOARD_BASE6}/api/cli/iam`, {
4785
+ method: "POST",
4786
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
4787
+ body: JSON.stringify({ tenant: tenant2, path: path8, method, body })
4788
+ });
4789
+ const data = await res.json().catch(() => ({}));
4790
+ if (!res.ok) throw new ApiError(res.status, data?.details ?? data?.error ?? res.statusText, data);
4791
+ return data;
4792
+ }
4793
+ };
4794
+ }
4795
+ const consumer2 = loadConsumer();
4796
+ if (consumer2) {
4797
+ const fresh = await validConsumerToken(consumer2) ?? consumer2;
4798
+ if (fresh !== consumer2) saveConsumer(fresh);
4799
+ if (opts.tenant && opts.tenant.toLowerCase() !== fresh.tenant) {
4800
+ throw new Error(`Your consumer login is for tenant "${fresh.tenant}". Log in to "${opts.tenant}" first (apiblaze consumer login --tenant ${opts.tenant}).`);
4801
+ }
4802
+ return {
4803
+ tenant: fresh.tenant,
4804
+ selfEmail: fresh.email,
4805
+ call: async (path8, method = "GET", body) => {
4806
+ const res = await fetch(`${IAM_BASE}${path8}`, {
4807
+ method,
4808
+ headers: {
4809
+ "Content-Type": "application/json",
4810
+ Authorization: `Bearer ${fresh.accessToken}`
4811
+ },
4812
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
4813
+ });
4814
+ if (res.status === 204) return {};
4815
+ const data = await res.json().catch(() => ({}));
4816
+ if (!res.ok) throw new ApiError(res.status, data?.error?.message ?? data?.details ?? data?.error ?? res.statusText, data);
4817
+ return data;
4818
+ }
4819
+ };
4820
+ }
4821
+ throw new Error("Not authenticated. Producers: `apiblaze login`. Tenant admins: `apiblaze consumer login`.");
4822
+ }
4823
+ async function listGroups(t) {
4824
+ const out = await t.call("/v1/admin/groups");
4825
+ return out?.groups ?? [];
4826
+ }
4827
+ async function groupByName(t, name) {
4828
+ const g = (await listGroups(t)).find((x) => x.name === name || x.id === name);
4829
+ if (!g) throw new Error(`Group "${name}" not found. Run \`apiblaze group list\`.`);
4830
+ return g;
4831
+ }
4832
+ async function listUsers(t) {
4833
+ const out = await t.call("/v1/admin/users");
4834
+ return out?.users ?? [];
4835
+ }
4836
+ async function userByRef(t, ref) {
4837
+ const users = await listUsers(t);
4838
+ const r = ref.toLowerCase();
4839
+ const u = users.find((x) => x.abz_sub === ref || (x.email ?? "").toLowerCase() === r || (x.display_name ?? "").toLowerCase() === r);
4840
+ if (!u) throw new Error(`User "${ref}" not found in tenant. Run \`apiblaze group users\` to see the directory (or provision them from Seen-in-traffic in the dashboard).`);
4841
+ return u;
4842
+ }
4843
+ var label2 = (u) => u.display_name || u.email || u.abz_sub;
4844
+ async function runGroupList(opts) {
4845
+ const t = await resolveTransport(opts);
4846
+ const groups = await listGroups(t);
4847
+ if (opts.json) return void console.log(JSON.stringify(groups));
4848
+ if (!groups.length) return void console.log(import_chalk31.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
4849
+ console.log(import_chalk31.default.dim(`Tenant ${t.tenant}:`));
4850
+ for (const g of groups) {
4851
+ const sealed = g.name === "apiblaze_admins" ? import_chalk31.default.dim(" (admins \u2014 managed by allowlist)") : "";
4852
+ console.log(` ${import_chalk31.default.bold(g.name)} ${import_chalk31.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
4853
+ }
4854
+ }
4855
+ async function runGroupCreate(name, opts) {
4856
+ const t = await resolveTransport(opts);
4857
+ let adminSub;
4858
+ if (opts.admin) adminSub = (await userByRef(t, opts.admin)).abz_sub;
4859
+ else {
4860
+ const users = await listUsers(t);
4861
+ if (t.selfEmail) adminSub = users.find((u) => (u.email ?? "").toLowerCase() === t.selfEmail.toLowerCase())?.abz_sub;
4862
+ if (!adminSub) {
4863
+ if (!users.length) throw new Error("The tenant has no users yet, and a group needs an initial group-admin. Add a user first (widget/portal login or Seen-in-traffic), or pass --admin <user>.");
4864
+ if (users.length === 1) adminSub = users[0].abz_sub;
4865
+ else {
4866
+ const { default: inquirer3 } = await import("inquirer");
4867
+ const { pick: pick2 } = await inquirer3.prompt([{
4868
+ type: "list",
4869
+ name: "pick",
4870
+ message: `Initial group-admin for "${name}"?`,
4871
+ choices: users.map((u) => ({ name: label2(u), value: u.abz_sub }))
4872
+ }]);
4873
+ adminSub = pick2;
4874
+ }
4875
+ }
4876
+ }
4877
+ const spinner = (0, import_ora15.default)(`Creating group ${name}...`).start();
4878
+ try {
4879
+ await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
4880
+ spinner.succeed(`Group ${import_chalk31.default.bold(name)} created in ${t.tenant}.`);
4881
+ } catch (e) {
4882
+ spinner.fail("Create failed.");
4883
+ throw e;
4884
+ }
4885
+ }
4886
+ async function runGroupDelete(name, opts) {
4887
+ const t = await resolveTransport(opts);
4888
+ const g = await groupByName(t, name);
4889
+ const spinner = (0, import_ora15.default)(`Deleting group ${name}...`).start();
4890
+ try {
4891
+ await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
4892
+ spinner.succeed(`Group ${import_chalk31.default.bold(name)} deleted.`);
4893
+ } catch (e) {
4894
+ spinner.fail("Delete failed.");
4895
+ throw e;
4896
+ }
4897
+ }
4898
+ async function runGroupMembers(name, opts) {
4899
+ const t = await resolveTransport(opts);
4900
+ const g = await groupByName(t, name);
4901
+ const detail = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`);
4902
+ const subs = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/subgroups`);
4903
+ if (opts.json) return void console.log(JSON.stringify({ ...detail, subgroups: subs?.subgroups ?? [] }));
4904
+ const members = detail?.members ?? [];
4905
+ console.log(import_chalk31.default.bold(`${g.name}`) + import_chalk31.default.dim(` (${t.tenant})`));
4906
+ for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk31.default.cyan("group-admin") : import_chalk31.default.dim("member")}`);
4907
+ if (!members.length) console.log(import_chalk31.default.dim(" no members"));
4908
+ const sg = subs?.subgroups ?? [];
4909
+ if (sg.length) console.log(import_chalk31.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
4910
+ }
4911
+ async function runGroupAddUser(userRef, groupName, opts) {
4912
+ const t = await resolveTransport(opts);
4913
+ const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
4914
+ const role = opts.role === "admin" ? "admin" : "member";
4915
+ const spinner = (0, import_ora15.default)(`Adding ${label2(u)} to ${g.name}...`).start();
4916
+ try {
4917
+ await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
4918
+ spinner.succeed(`${import_chalk31.default.bold(label2(u))} added to ${import_chalk31.default.bold(g.name)} as ${role}.`);
4919
+ } catch (e) {
4920
+ spinner.fail("Add failed.");
4921
+ throw e;
4922
+ }
4923
+ }
4924
+ async function runGroupRemoveUser(userRef, groupName, opts) {
4925
+ const t = await resolveTransport(opts);
4926
+ const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
4927
+ const spinner = (0, import_ora15.default)(`Removing ${label2(u)} from ${g.name}...`).start();
4928
+ try {
4929
+ await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
4930
+ spinner.succeed(`${import_chalk31.default.bold(label2(u))} removed from ${import_chalk31.default.bold(g.name)}.`);
4931
+ } catch (e) {
4932
+ spinner.fail("Remove failed.");
4933
+ throw e;
4934
+ }
4935
+ }
4936
+ async function runGroupAddGroup(childName, parentName, opts) {
4937
+ const t = await resolveTransport(opts);
4938
+ const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
4939
+ const spinner = (0, import_ora15.default)(`Nesting ${child.name} under ${parent.name}...`).start();
4940
+ try {
4941
+ await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
4942
+ spinner.succeed(`${import_chalk31.default.bold(child.name)} is now a subgroup of ${import_chalk31.default.bold(parent.name)}.`);
4943
+ } catch (e) {
4944
+ spinner.fail("Nest failed.");
4945
+ throw e;
4946
+ }
4947
+ }
4948
+ async function runGroupRemoveGroup(childName, parentName, opts) {
4949
+ const t = await resolveTransport(opts);
4950
+ const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
4951
+ const spinner = (0, import_ora15.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
4952
+ try {
4953
+ await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
4954
+ spinner.succeed(`${import_chalk31.default.bold(child.name)} un-nested from ${import_chalk31.default.bold(parent.name)}.`);
4955
+ } catch (e) {
4956
+ spinner.fail("Un-nest failed.");
4957
+ throw e;
4958
+ }
4959
+ }
4960
+ async function runGroupUsers(opts) {
4961
+ const t = await resolveTransport(opts);
4962
+ const users = await listUsers(t);
4963
+ const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
4964
+ if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
4965
+ console.log(import_chalk31.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
4966
+ for (const u of users) console.log(` ${label2(u)} ${import_chalk31.default.dim(u.abz_sub)}`);
4967
+ if (!users.length) console.log(import_chalk31.default.dim(" no users yet"));
4968
+ const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
4969
+ if (fresh.length) {
4970
+ console.log(import_chalk31.default.dim("Seen in traffic (not yet users):"));
4971
+ for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk31.default.dim(o.source_label ?? "")}`);
4972
+ }
4973
+ }
4974
+
4975
+ // src/commands/key.ts
4976
+ var import_chalk32 = __toESM(require("chalk"));
4977
+ var import_ora16 = __toESM(require("ora"));
4760
4978
  init_admin();
4761
4979
  async function runApikeysMenu(opts) {
4762
4980
  await runKeyList(opts);
@@ -4784,11 +5002,11 @@ async function runKeyList(opts) {
4784
5002
  return;
4785
5003
  }
4786
5004
  if (!keys.length) {
4787
- console.log(import_chalk31.default.yellow("No developer keys."));
5005
+ console.log(import_chalk32.default.yellow("No developer keys."));
4788
5006
  return;
4789
5007
  }
4790
5008
  for (const k of keys) {
4791
- console.log(` ${import_chalk31.default.bold(k.key_id ?? k.id)} ${import_chalk31.default.dim(k.description ?? "")} ${import_chalk31.default.dim(k.expires_at ?? "no expiry")}`);
5009
+ console.log(` ${import_chalk32.default.bold(k.key_id ?? k.id)} ${import_chalk32.default.dim(k.description ?? "")} ${import_chalk32.default.dim(k.expires_at ?? "no expiry")}`);
4792
5010
  }
4793
5011
  }
4794
5012
  async function runKeyMint(opts) {
@@ -4796,7 +5014,7 @@ async function runKeyMint(opts) {
4796
5014
  const body = { role: "consumer-admin" };
4797
5015
  if (opts.desc) body.description = opts.desc;
4798
5016
  if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
4799
- const spinner = (0, import_ora15.default)("Minting key...").start();
5017
+ const spinner = (0, import_ora16.default)("Minting key...").start();
4800
5018
  try {
4801
5019
  const out = await admin({
4802
5020
  method: "POST",
@@ -4809,9 +5027,9 @@ async function runKeyMint(opts) {
4809
5027
  console.log(JSON.stringify(out));
4810
5028
  return;
4811
5029
  }
4812
- console.log(` ${import_chalk31.default.bold("key_id")}: ${out?.key_id}`);
4813
- console.log(` ${import_chalk31.default.bold("key")}: ${import_chalk31.default.green(out?.key)} ${import_chalk31.default.dim("(shown once \u2014 store it now)")}`);
4814
- if (out?.expires_at) console.log(` ${import_chalk31.default.dim("expires:")} ${out.expires_at}`);
5030
+ console.log(` ${import_chalk32.default.bold("key_id")}: ${out?.key_id}`);
5031
+ console.log(` ${import_chalk32.default.bold("key")}: ${import_chalk32.default.green(out?.key)} ${import_chalk32.default.dim("(shown once \u2014 store it now)")}`);
5032
+ if (out?.expires_at) console.log(` ${import_chalk32.default.dim("expires:")} ${out.expires_at}`);
4815
5033
  } catch (err) {
4816
5034
  spinner.fail("Mint failed.");
4817
5035
  throw err;
@@ -4819,7 +5037,7 @@ async function runKeyMint(opts) {
4819
5037
  }
4820
5038
  async function runKeyRevoke(keyId, opts) {
4821
5039
  const { teamId } = await resolveTeam(opts.team);
4822
- const spinner = (0, import_ora15.default)("Revoking key...").start();
5040
+ const spinner = (0, import_ora16.default)("Revoking key...").start();
4823
5041
  try {
4824
5042
  await admin({
4825
5043
  method: "DELETE",
@@ -4837,8 +5055,8 @@ async function runKeyRevoke(keyId, opts) {
4837
5055
  var fs9 = __toESM(require("fs"));
4838
5056
  var path6 = __toESM(require("path"));
4839
5057
  var crypto2 = __toESM(require("crypto"));
4840
- var import_chalk33 = __toESM(require("chalk"));
4841
- var import_ora16 = __toESM(require("ora"));
5058
+ var import_chalk34 = __toESM(require("chalk"));
5059
+ var import_ora17 = __toESM(require("ora"));
4842
5060
  var import_yaml = require("yaml");
4843
5061
  init_auth();
4844
5062
  init_anon_cred();
@@ -4848,7 +5066,7 @@ init_admin();
4848
5066
  // src/commands/llm.ts
4849
5067
  var fs8 = __toESM(require("fs"));
4850
5068
  var path5 = __toESM(require("path"));
4851
- var import_chalk32 = __toESM(require("chalk"));
5069
+ var import_chalk33 = __toESM(require("chalk"));
4852
5070
  var import_inquirer2 = __toESM(require("inquirer"));
4853
5071
  init_auth();
4854
5072
  var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
@@ -4892,27 +5110,27 @@ async function runLlmSetKey(keyArg, opts) {
4892
5110
  }
4893
5111
  const existing = loadLlmConfig();
4894
5112
  saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
4895
- console.log(`${import_chalk32.default.green("\u2713")} Saved ${import_chalk32.default.bold(provider)} key ${maskSecret(key)}`);
4896
- console.log(import_chalk32.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
4897
- if (opts.model) console.log(import_chalk32.default.gray(` Model: ${opts.model}`));
5113
+ console.log(`${import_chalk33.default.green("\u2713")} Saved ${import_chalk33.default.bold(provider)} key ${maskSecret(key)}`);
5114
+ console.log(import_chalk33.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
5115
+ if (opts.model) console.log(import_chalk33.default.gray(` Model: ${opts.model}`));
4898
5116
  }
4899
5117
  async function runLlmShow() {
4900
5118
  const cfg = loadLlmConfig();
4901
5119
  if (!cfg) {
4902
- console.log(import_chalk32.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
5120
+ console.log(import_chalk33.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
4903
5121
  return;
4904
5122
  }
4905
- console.log(`Provider: ${import_chalk32.default.bold(cfg.provider)}`);
5123
+ console.log(`Provider: ${import_chalk33.default.bold(cfg.provider)}`);
4906
5124
  console.log(`Key: ${maskSecret(cfg.key)}`);
4907
5125
  if (cfg.model) console.log(`Model: ${cfg.model}`);
4908
- console.log(import_chalk32.default.gray(`Stored at ${LLM_PATH}`));
5126
+ console.log(import_chalk33.default.gray(`Stored at ${LLM_PATH}`));
4909
5127
  }
4910
5128
  async function runLlmClearKey() {
4911
5129
  try {
4912
5130
  fs8.unlinkSync(LLM_PATH);
4913
- console.log(`${import_chalk32.default.green("\u2713")} Removed local LLM key.`);
5131
+ console.log(`${import_chalk33.default.green("\u2713")} Removed local LLM key.`);
4914
5132
  } catch {
4915
- console.log(import_chalk32.default.gray("No LLM key was set."));
5133
+ console.log(import_chalk33.default.gray("No LLM key was set."));
4916
5134
  }
4917
5135
  }
4918
5136
 
@@ -4920,9 +5138,9 @@ async function runLlmClearKey() {
4920
5138
  init_trace();
4921
5139
  init_types();
4922
5140
  function fail4(message, hint) {
4923
- console.error(import_chalk33.default.red(`
5141
+ console.error(import_chalk34.default.red(`
4924
5142
  Error: ${message}`));
4925
- if (hint) console.error(import_chalk33.default.dim(hint));
5143
+ if (hint) console.error(import_chalk34.default.dim(hint));
4926
5144
  process.exit(1);
4927
5145
  }
4928
5146
  function normalizeName2(raw) {
@@ -5070,7 +5288,7 @@ async function resolveTargetAuth(spec2, opts) {
5070
5288
  "Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
5071
5289
  );
5072
5290
  }
5073
- if (sawOAuth) console.log(import_chalk33.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
5291
+ if (sawOAuth) console.log(import_chalk34.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
5074
5292
  return null;
5075
5293
  }
5076
5294
  if (candidates.length === 1 && !noneAllowed) return candidates[0];
@@ -5147,7 +5365,7 @@ async function provision(spec2, target, opts) {
5147
5365
  if (!base && spec2.info && typeof spec2.info.title === "string") base = normalizeName2(spec2.info.title);
5148
5366
  if (!base || base.length < 3) base = "apichat";
5149
5367
  let name = opts.name ? base : `${base}${salt()}`;
5150
- const spinner = (0, import_ora16.default)("Provisioning an api_key proxy...").start();
5368
+ const spinner = (0, import_ora17.default)("Provisioning an api_key proxy...").start();
5151
5369
  let result;
5152
5370
  for (let attempt = 0; attempt < 4; attempt++) {
5153
5371
  try {
@@ -5227,7 +5445,7 @@ async function provision(spec2, target, opts) {
5227
5445
  return { projectId, version: version2, environment, dpKey, mcpHost, proxyUrl, anon };
5228
5446
  }
5229
5447
  async function writeTargetAuth(p, auth, secret) {
5230
- const spinner = (0, import_ora16.default)("Storing target credentials (encrypted)...").start();
5448
+ const spinner = (0, import_ora17.default)("Storing target credentials (encrypted)...").start();
5231
5449
  try {
5232
5450
  await cpPost(
5233
5451
  p.anon,
@@ -5249,7 +5467,7 @@ async function writeTargetAuth(p, auth, secret) {
5249
5467
  }
5250
5468
  }
5251
5469
  async function uploadSpec(p, specText, opts) {
5252
- const spinner = (0, import_ora16.default)("Uploading the spec...").start();
5470
+ const spinner = (0, import_ora17.default)("Uploading the spec...").start();
5253
5471
  let out;
5254
5472
  try {
5255
5473
  out = await cpPost(
@@ -5264,7 +5482,7 @@ async function uploadSpec(p, specText, opts) {
5264
5482
  throw err;
5265
5483
  }
5266
5484
  if (out && out.reused === true) {
5267
- console.log(import_chalk33.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
5485
+ console.log(import_chalk34.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
5268
5486
  } else if (out && out.changed === true && out.previous_spec_hash) {
5269
5487
  const interactive = !!process.stdin.isTTY && !opts.yes;
5270
5488
  if (interactive) {
@@ -5272,12 +5490,12 @@ async function uploadSpec(p, specText, opts) {
5272
5490
  const { go } = await inquirer3.prompt([
5273
5491
  { type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
5274
5492
  ]);
5275
- if (!go) console.log(import_chalk33.default.dim(" Keeping the existing MCP catalogue."));
5493
+ if (!go) console.log(import_chalk34.default.dim(" Keeping the existing MCP catalogue."));
5276
5494
  }
5277
5495
  }
5278
5496
  }
5279
5497
  async function publishMcp(p, spec2) {
5280
- const spinner = (0, import_ora16.default)("Publishing the MCP catalogue...").start();
5498
+ const spinner = (0, import_ora17.default)("Publishing the MCP catalogue...").start();
5281
5499
  try {
5282
5500
  const url = `https://${p.mcpHost}/${p.version}/${p.environment}/mcp/generate`;
5283
5501
  const res = await fetch(url, {
@@ -5313,14 +5531,14 @@ var revealAuth = false;
5313
5531
  function renderToolEvents(events, dpKey) {
5314
5532
  for (const e of events ?? []) {
5315
5533
  const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
5316
- const mark = ok ? import_chalk33.default.green("\u2713") : import_chalk33.default.red("\u2717");
5317
- console.log(` ${mark} ${import_chalk33.default.cyan(e.name)} ${import_chalk33.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
5534
+ const mark = ok ? import_chalk34.default.green("\u2713") : import_chalk34.default.red("\u2717");
5535
+ console.log(` ${mark} ${import_chalk34.default.cyan(e.name)} ${import_chalk34.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
5318
5536
  if (isVerbose() && e.method && e.url) {
5319
- console.log(import_chalk33.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
5537
+ console.log(import_chalk34.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
5320
5538
  if (dpKey) {
5321
5539
  const keyLine = ` -H 'X-API-Key: ${revealAuth ? dpKey : maskKey(dpKey)}'`;
5322
- const hint = revealAuth ? "" : import_chalk33.default.yellow(" \u2190 /showauth will reveal this");
5323
- console.log(import_chalk33.default.dim(keyLine) + hint);
5540
+ const hint = revealAuth ? "" : import_chalk34.default.yellow(" \u2190 /showauth will reveal this");
5541
+ console.log(import_chalk34.default.dim(keyLine) + hint);
5324
5542
  }
5325
5543
  }
5326
5544
  }
@@ -5328,9 +5546,9 @@ function renderToolEvents(events, dpKey) {
5328
5546
  function billingLine(billing) {
5329
5547
  if (!billing || typeof billing.cents !== "number") return null;
5330
5548
  const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
5331
- let line = import_chalk33.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk33.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
5549
+ let line = import_chalk34.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk34.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
5332
5550
  if (typeof billing.credits_remaining === "number") {
5333
- line += import_chalk33.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
5551
+ line += import_chalk34.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
5334
5552
  }
5335
5553
  return line;
5336
5554
  }
@@ -5339,14 +5557,14 @@ function freeBudgetWarning(billing, anon) {
5339
5557
  const perTurn = Math.max(billing.cents || 0, 0.02);
5340
5558
  const left = Math.floor(billing.free_remaining_cents / perTurn);
5341
5559
  if (left > 8) return null;
5342
- if (left <= 0) return import_chalk33.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
5343
- return import_chalk33.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
5560
+ if (left <= 0) return import_chalk34.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
5561
+ return import_chalk34.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
5344
5562
  }
5345
5563
  function printAssistant(delta) {
5346
5564
  for (let i = delta.length - 1; i >= 0; i--) {
5347
5565
  const m = delta[i];
5348
5566
  if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
5349
- console.log("\n" + import_chalk33.default.green("assistant \u203A ") + m.content + "\n");
5567
+ console.log("\n" + import_chalk34.default.green("assistant \u203A ") + m.content + "\n");
5350
5568
  return;
5351
5569
  }
5352
5570
  }
@@ -5356,7 +5574,7 @@ async function replTurn(p, messages, userText) {
5356
5574
  const llm2 = loadLlmConfig();
5357
5575
  const turnId = crypto2.randomUUID();
5358
5576
  for (let round = 0; round < CLIENT_ROUND_CAP; round++) {
5359
- const spinner = (0, import_ora16.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
5577
+ const spinner = (0, import_ora17.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
5360
5578
  const body = {
5361
5579
  turn_id: turnId,
5362
5580
  messages,
@@ -5372,7 +5590,7 @@ async function replTurn(p, messages, userText) {
5372
5590
  });
5373
5591
  } catch (err) {
5374
5592
  spinner.fail("Network error.");
5375
- console.log(import_chalk33.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
5593
+ console.log(import_chalk34.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
5376
5594
  return;
5377
5595
  }
5378
5596
  let data = null;
@@ -5390,12 +5608,12 @@ async function replTurn(p, messages, userText) {
5390
5608
  return;
5391
5609
  }
5392
5610
  if (res.status === 401) {
5393
- console.log(import_chalk33.default.red(" The proxy rejected the API key (401). The key may have been revoked; re-run `apiblaze apichat` to re-provision."));
5611
+ console.log(import_chalk34.default.red(" The proxy rejected the API key (401). The key may have been revoked; re-run `apiblaze apichat` to re-provision."));
5394
5612
  return;
5395
5613
  }
5396
5614
  if (!res.ok || !data) {
5397
5615
  const err = data && data.error || `HTTP ${res.status}`;
5398
- console.log(import_chalk33.default.red(` Chat error: ${err}`));
5616
+ console.log(import_chalk34.default.red(` Chat error: ${err}`));
5399
5617
  return;
5400
5618
  }
5401
5619
  if (Array.isArray(data.delta)) {
@@ -5409,19 +5627,25 @@ async function replTurn(p, messages, userText) {
5409
5627
  if (warn) console.log(warn);
5410
5628
  if (!data.continue) return;
5411
5629
  }
5412
- console.log(import_chalk33.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
5630
+ console.log(import_chalk34.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
5413
5631
  }
5414
5632
  function renderUpsell(p, upsell) {
5415
5633
  const loggedIn = !!loadCredentials();
5416
- console.log("\n" + import_chalk33.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
5634
+ if (upsell.reason === "CAPPED" && !loggedIn) {
5635
+ console.log("\n" + import_chalk34.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
5636
+ console.log(import_chalk34.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
5637
+ console.log();
5638
+ return;
5639
+ }
5640
+ console.log("\n" + import_chalk34.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
5417
5641
  if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
5418
5642
  if (!loggedIn) {
5419
- console.log(import_chalk33.default.dim(" Options: `/login` for free login credit and higher limits, or `apiblaze llm set-key` to bring your own model key."));
5643
+ console.log(import_chalk34.default.dim(" Options: `/login` for free login credit and higher limits, or `apiblaze llm set-key` to bring your own model key."));
5420
5644
  } else {
5421
- console.log(import_chalk33.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
5645
+ console.log(import_chalk34.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
5422
5646
  }
5423
5647
  } else if (upsell.reason === "INFLIGHT") {
5424
- console.log(import_chalk33.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
5648
+ console.log(import_chalk34.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
5425
5649
  }
5426
5650
  console.log();
5427
5651
  }
@@ -5490,9 +5714,9 @@ async function openServerProxy(project) {
5490
5714
  const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
5491
5715
  let dpKey = prior?.dpKey;
5492
5716
  if (!dpKey) {
5493
- console.log(import_chalk33.default.dim(` Minting an API key for tenant ${import_chalk33.default.bold(tenant2)} to query project ${import_chalk33.default.bold(project.projectName)}\u2026`));
5717
+ console.log(import_chalk34.default.dim(` Minting an API key for tenant ${import_chalk34.default.bold(tenant2)} to query project ${import_chalk34.default.bold(project.projectName)}\u2026`));
5494
5718
  dpKey = await mintDurableProxyKey(project.teamId, tenant2);
5495
- console.log(` ${import_chalk33.default.green("\u2714")} API key: ${import_chalk33.default.dim(maskKey(dpKey))}`);
5719
+ console.log(` ${import_chalk34.default.green("\u2714")} API key: ${import_chalk34.default.dim(maskKey(dpKey))}`);
5496
5720
  }
5497
5721
  const p = {
5498
5722
  projectId: project.projectId,
@@ -5503,7 +5727,7 @@ async function openServerProxy(project) {
5503
5727
  proxyUrl: `https://${project.projectId}.abz.run/${version2}/${environment}`,
5504
5728
  anon: false
5505
5729
  };
5506
- const spinner = (0, import_ora16.default)("Preparing the chat\u2026").start();
5730
+ const spinner = (0, import_ora17.default)("Preparing the chat\u2026").start();
5507
5731
  try {
5508
5732
  const raw = await admin({
5509
5733
  method: "GET",
@@ -5515,7 +5739,7 @@ async function openServerProxy(project) {
5515
5739
  if (spec2 && (spec2.paths || spec2.openapi)) {
5516
5740
  await publishMcp(p, spec2);
5517
5741
  } else {
5518
- console.log(import_chalk33.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
5742
+ console.log(import_chalk34.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
5519
5743
  }
5520
5744
  } catch (err) {
5521
5745
  spinner.fail("Could not open the proxy.");
@@ -5568,7 +5792,7 @@ async function noArgsMenu(opts) {
5568
5792
  const me = loadCredentials()?.apiblazeUserId;
5569
5793
  const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
5570
5794
  const choices = saved.map((a) => ({
5571
- name: `Chat with ${import_chalk33.default.bold(a.name)} ${import_chalk33.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
5795
+ name: `Chat with ${import_chalk34.default.bold(a.name)} ${import_chalk34.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
5572
5796
  value: { type: "existing", a }
5573
5797
  }));
5574
5798
  const creds = loadCredentials();
@@ -5578,14 +5802,14 @@ async function noArgsMenu(opts) {
5578
5802
  const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
5579
5803
  for (const pr of proxies) {
5580
5804
  choices.push({
5581
- name: `Chat with ${import_chalk33.default.bold(pr.projectName)} ${import_chalk33.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
5805
+ name: `Chat with ${import_chalk34.default.bold(pr.projectName)} ${import_chalk34.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
5582
5806
  value: { type: "server", project: pr }
5583
5807
  });
5584
5808
  }
5585
5809
  } catch {
5586
5810
  }
5587
5811
  }
5588
- choices.push({ name: import_chalk33.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
5812
+ choices.push({ name: import_chalk34.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
5589
5813
  const { pick: pick2 } = await inquirer3.prompt([
5590
5814
  { type: "list", name: "pick", message: "What would you like to do?", choices }
5591
5815
  ]);
@@ -5657,31 +5881,31 @@ async function noArgsMenu(opts) {
5657
5881
  async function runRepl(p, initialMessages) {
5658
5882
  const { default: inquirer3 } = await import("inquirer");
5659
5883
  const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
5660
- console.log("\n" + import_chalk33.default.cyan.bold("Chat with your API") + import_chalk33.default.dim(` \xB7 ${p.mcpHost}`));
5661
- if (messages.length) console.log(import_chalk33.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
5884
+ console.log("\n" + import_chalk34.default.cyan.bold("Chat with your API") + import_chalk34.default.dim(` \xB7 ${p.mcpHost}`));
5885
+ if (messages.length) console.log(import_chalk34.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
5662
5886
  const llm2 = loadLlmConfig();
5663
5887
  console.log(
5664
- import_chalk33.default.dim(
5888
+ import_chalk34.default.dim(
5665
5889
  llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for credit \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
5666
5890
  )
5667
5891
  );
5668
5892
  for (; ; ) {
5669
- const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk33.default.green("you \u203A") }]);
5893
+ const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk34.default.green("you \u203A") }]);
5670
5894
  const text = (input ?? "").trim();
5671
5895
  if (!text) continue;
5672
5896
  if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
5673
5897
  if (text === "/login") {
5674
5898
  try {
5675
5899
  await runLogin();
5676
- console.log(import_chalk33.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
5900
+ console.log(import_chalk34.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
5677
5901
  } catch (err) {
5678
- console.log(import_chalk33.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
5902
+ console.log(import_chalk34.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
5679
5903
  }
5680
5904
  continue;
5681
5905
  }
5682
5906
  if (text === "/claim") {
5683
5907
  if (!loadCredentials()) {
5684
- console.log(import_chalk33.default.yellow(" Log in first: `/login`, then `/claim`."));
5908
+ console.log(import_chalk34.default.yellow(" Log in first: `/login`, then `/claim`."));
5685
5909
  continue;
5686
5910
  }
5687
5911
  try {
@@ -5690,30 +5914,30 @@ async function runRepl(p, initialMessages) {
5690
5914
  p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
5691
5915
  p.anon = false;
5692
5916
  claimApichat(p, loadCredentials()?.apiblazeUserId);
5693
- console.log(import_chalk33.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
5917
+ console.log(import_chalk34.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
5694
5918
  }
5695
5919
  } catch (err) {
5696
- console.log(import_chalk33.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
5920
+ console.log(import_chalk34.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
5697
5921
  }
5698
5922
  continue;
5699
5923
  }
5700
5924
  if (text === "/showauth") {
5701
5925
  revealAuth = !revealAuth;
5702
- console.log(import_chalk33.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."));
5926
+ console.log(import_chalk34.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."));
5703
5927
  continue;
5704
5928
  }
5705
5929
  if (text.startsWith("/")) {
5706
- console.log(import_chalk33.default.dim(" Commands: /login /claim /showauth /exit"));
5930
+ console.log(import_chalk34.default.dim(" Commands: /login /claim /showauth /exit"));
5707
5931
  continue;
5708
5932
  }
5709
5933
  await replTurn(p, messages, text);
5710
5934
  saveTranscript(p, messages);
5711
5935
  }
5712
- console.log(import_chalk33.default.dim("\nBye."));
5936
+ console.log(import_chalk34.default.dim("\nBye."));
5713
5937
  }
5714
5938
  async function runApichat(opts) {
5715
5939
  setVerbose(opts.verbose !== false);
5716
- console.log(import_chalk33.default.bold("\napichat \u2014 turn any API into a chat\n"));
5940
+ console.log(import_chalk34.default.bold("\napichat \u2014 turn any API into a chat\n"));
5717
5941
  if (!opts.openapispec && !opts.target) {
5718
5942
  if (!process.stdin.isTTY) {
5719
5943
  fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
@@ -5726,7 +5950,7 @@ async function runApichat(opts) {
5726
5950
  }
5727
5951
  const { spec: spec2, sourceUrl } = await loadSpec(opts);
5728
5952
  const target = resolveTarget(spec2, opts, sourceUrl);
5729
- console.log(` ${import_chalk33.default.dim("Target:")} ${import_chalk33.default.bold(target)}`);
5953
+ console.log(` ${import_chalk34.default.dim("Target:")} ${import_chalk34.default.bold(target)}`);
5730
5954
  const auth = await resolveTargetAuth(spec2, opts);
5731
5955
  if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
5732
5956
  fail4(
@@ -5735,7 +5959,7 @@ async function runApichat(opts) {
5735
5959
  );
5736
5960
  }
5737
5961
  const p = await provision(spec2, target, opts);
5738
- console.log(` ${import_chalk33.default.dim("Proxy: ")} ${import_chalk33.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
5962
+ console.log(` ${import_chalk34.default.dim("Proxy: ")} ${import_chalk34.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
5739
5963
  upsertApichat({
5740
5964
  name: p.projectId,
5741
5965
  target,
@@ -5754,23 +5978,23 @@ async function runApichat(opts) {
5754
5978
  const secret = await captureTargetSecret(auth, opts);
5755
5979
  if (secret) await writeTargetAuth(p, auth, secret);
5756
5980
  } else {
5757
- console.log(import_chalk33.default.dim(" Target auth: none required."));
5981
+ console.log(import_chalk34.default.dim(" Target auth: none required."));
5758
5982
  }
5759
5983
  const specText = JSON.stringify(spec2);
5760
5984
  await uploadSpec(p, specText, opts);
5761
5985
  const mcpUrl = await publishMcp(p, spec2);
5762
5986
  console.log();
5763
- if (p.proxyUrl) console.log(` ${import_chalk33.default.green("\u2713")} proxy ${import_chalk33.default.bold(p.proxyUrl)}`);
5764
- if (mcpUrl) console.log(` ${import_chalk33.default.green("\u2713")} mcp ${import_chalk33.default.bold(mcpUrl)}`);
5987
+ if (p.proxyUrl) console.log(` ${import_chalk34.default.green("\u2713")} proxy ${import_chalk34.default.bold(p.proxyUrl)}`);
5988
+ if (mcpUrl) console.log(` ${import_chalk34.default.green("\u2713")} mcp ${import_chalk34.default.bold(mcpUrl)}`);
5765
5989
  if (p.anon) {
5766
- console.log(import_chalk33.default.dim("\n Anonymous workspace \u2014 /claim inside the chat (after /login) to keep it beyond 30 days."));
5990
+ console.log(import_chalk34.default.dim("\n Anonymous workspace \u2014 /claim inside the chat (after /login) to keep it beyond 30 days."));
5767
5991
  }
5768
5992
  await runRepl(p);
5769
5993
  }
5770
5994
 
5771
5995
  // src/commands/consumer.ts
5772
- var import_chalk34 = __toESM(require("chalk"));
5773
- var import_ora17 = __toESM(require("ora"));
5996
+ var import_chalk35 = __toESM(require("chalk"));
5997
+ var import_ora18 = __toESM(require("ora"));
5774
5998
  init_admin();
5775
5999
  var DEFAULT_SCOPE = "openid email profile offline_access";
5776
6000
  var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
@@ -5790,7 +6014,7 @@ async function consumerFetch(creds, suffix, init) {
5790
6014
  function requireConsumer() {
5791
6015
  const c = loadConsumer();
5792
6016
  if (!c) {
5793
- console.error(import_chalk34.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
6017
+ console.error(import_chalk35.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
5794
6018
  process.exit(1);
5795
6019
  }
5796
6020
  return c;
@@ -5801,7 +6025,7 @@ async function runConsumerLogin(opts) {
5801
6025
  let clientId = opts.client;
5802
6026
  if (clientId) {
5803
6027
  if (!tenant2) {
5804
- console.error(import_chalk34.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
6028
+ console.error(import_chalk35.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
5805
6029
  process.exit(1);
5806
6030
  }
5807
6031
  } else {
@@ -5813,25 +6037,25 @@ async function runConsumerLogin(opts) {
5813
6037
  if (!picked) process.exit(1);
5814
6038
  tenant2 = picked;
5815
6039
  }
5816
- const s2 = (0, import_ora17.default)("Finding the login app...").start();
6040
+ const s2 = (0, import_ora18.default)("Finding the login app...").start();
5817
6041
  const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
5818
6042
  s2.stop();
5819
6043
  const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
5820
6044
  const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
5821
6045
  if (!pick2) {
5822
- console.error(import_chalk34.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
6046
+ console.error(import_chalk35.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
5823
6047
  process.exit(1);
5824
6048
  }
5825
6049
  clientId = pick2.client_id ?? pick2.clientId;
5826
6050
  }
5827
6051
  const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
5828
- console.log(`${import_chalk34.default.cyan("\u2192")} Logging in to ${import_chalk34.default.bold(tenant2)} as a consumer...`);
6052
+ console.log(`${import_chalk35.default.cyan("\u2192")} Logging in to ${import_chalk35.default.bold(tenant2)} as a consumer...`);
5829
6053
  const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
5830
6054
  console.log(`
5831
- Open: ${import_chalk34.default.underline(verificationUri)}`);
5832
- console.log(` Code: ${import_chalk34.default.bold(userCode)}
6055
+ Open: ${import_chalk35.default.underline(verificationUri)}`);
6056
+ console.log(` Code: ${import_chalk35.default.bold(userCode)}
5833
6057
  `);
5834
- console.log(import_chalk34.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
6058
+ console.log(import_chalk35.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
5835
6059
  }, portalResource);
5836
6060
  const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
5837
6061
  const creds = {
@@ -5846,7 +6070,7 @@ async function runConsumerLogin(opts) {
5846
6070
  obtainedAt: Date.now()
5847
6071
  };
5848
6072
  saveConsumer(creds);
5849
- console.log(import_chalk34.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
6073
+ console.log(import_chalk35.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
5850
6074
  }
5851
6075
  async function runConsumerTokens(opts) {
5852
6076
  const creds = requireConsumer();
@@ -5859,29 +6083,29 @@ async function runConsumerTokens(opts) {
5859
6083
  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));
5860
6084
  return;
5861
6085
  }
5862
- console.log(`${import_chalk34.default.cyan("Consumer")} ${import_chalk34.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk34.default.bold(fresh.tenant)}
6086
+ console.log(`${import_chalk35.default.cyan("Consumer")} ${import_chalk35.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk35.default.bold(fresh.tenant)}
5863
6087
  `);
5864
- console.log(`${import_chalk34.default.bold("access_token")} ${import_chalk34.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
6088
+ console.log(`${import_chalk35.default.bold("access_token")} ${import_chalk35.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
5865
6089
  ${fresh.accessToken}
5866
6090
  `);
5867
- if (fresh.idToken) console.log(`${import_chalk34.default.bold("id_token")} ${import_chalk34.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
6091
+ if (fresh.idToken) console.log(`${import_chalk35.default.bold("id_token")} ${import_chalk35.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
5868
6092
  ${fresh.idToken}
5869
6093
  `);
5870
- if (fresh.refreshToken) console.log(`${import_chalk34.default.bold("refresh_token")}
6094
+ if (fresh.refreshToken) console.log(`${import_chalk35.default.bold("refresh_token")}
5871
6095
  ${fresh.refreshToken}
5872
6096
  `);
5873
- console.log(import_chalk34.default.dim("These are your own tokens \u2014 keep them secret."));
6097
+ console.log(import_chalk35.default.dim("These are your own tokens \u2014 keep them secret."));
5874
6098
  }
5875
6099
  async function runConsumerApikeys(opts) {
5876
6100
  const creds = requireConsumer();
5877
6101
  const { default: inquirer3 } = await import("inquirer");
5878
- const spinner = (0, import_ora17.default)("Loading your API keys...").start();
6102
+ const spinner = (0, import_ora18.default)("Loading your API keys...").start();
5879
6103
  const list = await consumerFetch(creds, "/apikeys");
5880
6104
  const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
5881
6105
  spinner.stop();
5882
6106
  if (list.status >= 400) {
5883
- console.error(import_chalk34.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
5884
- if (list.status === 401) console.error(import_chalk34.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
6107
+ console.error(import_chalk35.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
6108
+ if (list.status === 401) console.error(import_chalk35.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
5885
6109
  process.exit(1);
5886
6110
  }
5887
6111
  const keys = list.data?.keys ?? [];
@@ -5889,16 +6113,16 @@ async function runConsumerApikeys(opts) {
5889
6113
  if (opts.json) {
5890
6114
  console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
5891
6115
  } else if (!keys.length) {
5892
- console.log(import_chalk34.default.yellow("No API keys yet."));
6116
+ console.log(import_chalk35.default.yellow("No API keys yet."));
5893
6117
  } else {
5894
6118
  for (const k of keys) {
5895
6119
  const clear = revealMap[k.environment]?.key;
5896
- const shown = clear ? import_chalk34.default.green(clear) : import_chalk34.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
5897
- const exp = k.expires_at ? import_chalk34.default.dim(`exp ${k.expires_at}`) : import_chalk34.default.dim("no expiry");
5898
- console.log(` ${import_chalk34.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk34.default.dim(k.description ?? "")}`);
6120
+ const shown = clear ? import_chalk35.default.green(clear) : import_chalk35.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
6121
+ const exp = k.expires_at ? import_chalk35.default.dim(`exp ${k.expires_at}`) : import_chalk35.default.dim("no expiry");
6122
+ console.log(` ${import_chalk35.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk35.default.dim(k.description ?? "")}`);
5899
6123
  }
5900
6124
  if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
5901
- console.log(import_chalk34.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
6125
+ console.log(import_chalk35.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
5902
6126
  }
5903
6127
  }
5904
6128
  if (opts.json) return;
@@ -5912,7 +6136,7 @@ async function runConsumerApikeys(opts) {
5912
6136
  const body = { environment: answers.environment };
5913
6137
  if (answers.description) body.description = answers.description;
5914
6138
  if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
5915
- const s2 = (0, import_ora17.default)("Creating key...").start();
6139
+ const s2 = (0, import_ora18.default)("Creating key...").start();
5916
6140
  const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
5917
6141
  if (created.status >= 400) {
5918
6142
  s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
@@ -5920,13 +6144,13 @@ async function runConsumerApikeys(opts) {
5920
6144
  }
5921
6145
  s2.succeed("Key created.");
5922
6146
  const key = created.data?.key ?? created.data?.fullKey;
5923
- if (key) console.log(` ${import_chalk34.default.green(key)} ${import_chalk34.default.dim("(shown once \u2014 store it now)")}`);
5924
- else console.log(import_chalk34.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
6147
+ if (key) console.log(` ${import_chalk35.default.green(key)} ${import_chalk35.default.dim("(shown once \u2014 store it now)")}`);
6148
+ else console.log(import_chalk35.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
5925
6149
  }
5926
6150
 
5927
6151
  // src/commands/sidecar.ts
5928
- var import_chalk35 = __toESM(require("chalk"));
5929
- var import_ora18 = __toESM(require("ora"));
6152
+ var import_chalk36 = __toESM(require("chalk"));
6153
+ var import_ora19 = __toESM(require("ora"));
5930
6154
  var fs10 = __toESM(require("fs"));
5931
6155
  var path7 = __toESM(require("path"));
5932
6156
  init_admin();
@@ -5967,18 +6191,18 @@ function upsertEnvLocal(root, token) {
5967
6191
  }
5968
6192
  function installSidecarPackage(root) {
5969
6193
  if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
5970
- console.log(` ${import_chalk35.default.green("\u2713")} apiblaze package already installed`);
6194
+ console.log(` ${import_chalk36.default.green("\u2713")} apiblaze package already installed`);
5971
6195
  return;
5972
6196
  }
5973
6197
  const has = (f) => fs10.existsSync(path7.join(root, f));
5974
6198
  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" };
5975
- const spinner = (0, import_ora18.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
6199
+ const spinner = (0, import_ora19.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
5976
6200
  try {
5977
6201
  const { execSync } = require("child_process");
5978
6202
  execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
5979
6203
  spinner.succeed("Installed apiblaze (the sidecar runtime).");
5980
6204
  } catch {
5981
- spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk35.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk35.default.cyan("npm run dev")}.`);
6205
+ spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk36.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk36.default.cyan("npm run dev")}.`);
5982
6206
  }
5983
6207
  }
5984
6208
  function readEnvKey(root) {
@@ -6117,7 +6341,7 @@ async function runAnonymousInit(root, router, opts) {
6117
6341
  const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
6118
6342
  const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
6119
6343
  if (opts.newSession) clearAnonCred2();
6120
- const spinner = (0, import_ora18.default)("Setting up a sidecar (no login needed)...").start();
6344
+ const spinner = (0, import_ora19.default)("Setting up a sidecar (no login needed)...").start();
6121
6345
  let out;
6122
6346
  try {
6123
6347
  out = await sidecarInitAnonymous2();
@@ -6129,29 +6353,29 @@ async function runAnonymousInit(root, router, opts) {
6129
6353
  if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
6130
6354
  const envState = upsertEnvLocal(root, out.token);
6131
6355
  ensureGitignored(root);
6132
- console.log(` ${import_chalk35.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6133
- console.log(` ${import_chalk35.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6356
+ console.log(` ${import_chalk36.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6357
+ console.log(` ${import_chalk36.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6134
6358
  installSidecarPackage(root);
6135
6359
  let inspectorPath = null;
6136
6360
  if (!opts.noInspector) {
6137
6361
  inspectorPath = generateInspector(root, router);
6138
- if (inspectorPath) console.log(` ${import_chalk35.default.green("\u2713")} inspector at ${inspectorPath}`);
6362
+ if (inspectorPath) console.log(` ${import_chalk36.default.green("\u2713")} inspector at ${inspectorPath}`);
6139
6363
  }
6140
6364
  console.log("");
6141
- console.log(import_chalk35.default.bold("Done (no account needed). What happens next:"));
6142
- console.log(` 1. ${import_chalk35.default.cyan("npm run dev")} and use your app.`);
6365
+ console.log(import_chalk36.default.bold("Done (no account needed). What happens next:"));
6366
+ console.log(` 1. ${import_chalk36.default.cyan("npm run dev")} and use your app.`);
6143
6367
  console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
6144
- console.log(` ${import_chalk35.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
6368
+ console.log(` ${import_chalk36.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
6145
6369
  console.log("");
6146
- console.log(import_chalk35.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
6147
- console.log(` ${import_chalk35.default.cyan("apiblaze login")} then ${import_chalk35.default.cyan("apiblaze claim")} ${import_chalk35.default.dim("(no code needed here)")}`);
6148
- console.log(import_chalk35.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
6370
+ console.log(import_chalk36.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
6371
+ console.log(` ${import_chalk36.default.cyan("apiblaze login")} then ${import_chalk36.default.cyan("apiblaze claim")} ${import_chalk36.default.dim("(no code needed here)")}`);
6372
+ console.log(import_chalk36.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
6149
6373
  }
6150
6374
  async function runSidecar(opts) {
6151
6375
  const root = path7.resolve(opts.dir ?? process.cwd());
6152
6376
  const detected = detectNextProject(root);
6153
6377
  if (!detected.found) {
6154
- console.log(import_chalk35.default.yellow(`No Next.js project detected in ${root}.`));
6378
+ console.log(import_chalk36.default.yellow(`No Next.js project detected in ${root}.`));
6155
6379
  console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
6156
6380
  return;
6157
6381
  }
@@ -6162,10 +6386,10 @@ async function runSidecar(opts) {
6162
6386
  if (!loadCredentials()) {
6163
6387
  upsertEnvLocal(root, readEnvKey(root));
6164
6388
  ensureGitignored(root);
6165
- console.log(` ${import_chalk35.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
6166
- console.log(` ${import_chalk35.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6389
+ console.log(` ${import_chalk36.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
6390
+ console.log(` ${import_chalk36.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
6167
6391
  installSidecarPackage(root);
6168
- console.log(import_chalk35.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
6392
+ console.log(import_chalk36.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
6169
6393
  return;
6170
6394
  }
6171
6395
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -6174,7 +6398,7 @@ async function runSidecar(opts) {
6174
6398
  const mustMint = !existingKey || opts.rotate || switchingTeam;
6175
6399
  let token = existingKey ?? "";
6176
6400
  if (mustMint) {
6177
- const spinner = (0, import_ora18.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
6401
+ const spinner = (0, import_ora19.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
6178
6402
  try {
6179
6403
  const out = await admin({
6180
6404
  method: "POST",
@@ -6188,39 +6412,39 @@ async function runSidecar(opts) {
6188
6412
  throw err;
6189
6413
  }
6190
6414
  } else {
6191
- console.log(import_chalk35.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
6415
+ console.log(import_chalk36.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
6192
6416
  }
6193
6417
  const envState = upsertEnvLocal(root, token);
6194
6418
  ensureGitignored(root);
6195
- console.log(` ${import_chalk35.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6419
+ console.log(` ${import_chalk36.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
6196
6420
  const wireState = wireInstrumentation(root);
6197
- console.log(` ${import_chalk35.default.green("\u2713")} instrumentation.ts ${wireState}`);
6421
+ console.log(` ${import_chalk36.default.green("\u2713")} instrumentation.ts ${wireState}`);
6198
6422
  installSidecarPackage(root);
6199
6423
  let inspectorPath = null;
6200
6424
  if (!opts.noInspector) {
6201
6425
  inspectorPath = generateInspector(root, detected.router);
6202
- if (inspectorPath) console.log(` ${import_chalk35.default.green("\u2713")} inspector at ${inspectorPath}`);
6426
+ if (inspectorPath) console.log(` ${import_chalk36.default.green("\u2713")} inspector at ${inspectorPath}`);
6203
6427
  }
6204
6428
  console.log("");
6205
- console.log(import_chalk35.default.bold("Done. What happens next:"));
6206
- console.log(` 1. ${import_chalk35.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
6207
- console.log(` 2. The origins your app calls appear as ${import_chalk35.default.bold("candidates")} \u2014 list them: ${import_chalk35.default.cyan("apiblaze sidecar")}`);
6208
- console.log(` 3. Approve the ones to route: ${import_chalk35.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
6429
+ console.log(import_chalk36.default.bold("Done. What happens next:"));
6430
+ console.log(` 1. ${import_chalk36.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
6431
+ console.log(` 2. The origins your app calls appear as ${import_chalk36.default.bold("candidates")} \u2014 list them: ${import_chalk36.default.cyan("apiblaze sidecar")}`);
6432
+ console.log(` 3. Approve the ones to route: ${import_chalk36.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
6209
6433
  console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
6210
- if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk35.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
6211
- if (switchingTeam) console.log(import_chalk35.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
6434
+ if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk36.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
6435
+ if (switchingTeam) console.log(import_chalk36.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
6212
6436
  console.log("");
6213
- console.log(import_chalk35.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
6214
- console.log(import_chalk35.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
6215
- console.log(import_chalk35.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
6437
+ console.log(import_chalk36.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
6438
+ console.log(import_chalk36.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
6439
+ console.log(import_chalk36.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
6216
6440
  console.log("");
6217
- console.log(import_chalk35.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
6218
- console.log(import_chalk35.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
6441
+ console.log(import_chalk36.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
6442
+ console.log(import_chalk36.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
6219
6443
  }
6220
6444
 
6221
6445
  // src/commands/origins.ts
6222
- var import_chalk36 = __toESM(require("chalk"));
6223
- var import_ora19 = __toESM(require("ora"));
6446
+ var import_chalk37 = __toESM(require("chalk"));
6447
+ var import_ora20 = __toESM(require("ora"));
6224
6448
  init_admin();
6225
6449
  init_auth();
6226
6450
  init_anon_cred();
@@ -6229,7 +6453,7 @@ async function runOriginsList(opts) {
6229
6453
  if (!loadCredentials()) {
6230
6454
  const cred = loadAnonCred();
6231
6455
  if (!cred) {
6232
- console.log(import_chalk36.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
6456
+ console.log(import_chalk37.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
6233
6457
  return;
6234
6458
  }
6235
6459
  out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
@@ -6247,30 +6471,30 @@ async function runOriginsList(opts) {
6247
6471
  }
6248
6472
  const routed = out.routed ?? [];
6249
6473
  const candidates = out.candidates ?? [];
6250
- console.log(import_chalk36.default.bold(`
6474
+ console.log(import_chalk37.default.bold(`
6251
6475
  Routed through APIblaze (${routed.length})`));
6252
- if (!routed.length) console.log(import_chalk36.default.dim(" none yet"));
6253
- for (const r of routed) console.log(` ${import_chalk36.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk36.default.dim(`\u2192 ${r.project_id}`)}`);
6254
- console.log(import_chalk36.default.bold(`
6476
+ if (!routed.length) console.log(import_chalk37.default.dim(" none yet"));
6477
+ for (const r of routed) console.log(` ${import_chalk37.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk37.default.dim(`\u2192 ${r.project_id}`)}`);
6478
+ console.log(import_chalk37.default.bold(`
6255
6479
  Candidates \u2014 going direct, not yet approved (${candidates.length})`));
6256
- if (!candidates.length) console.log(import_chalk36.default.dim(" none \u2014 run your app to discover the origins it calls"));
6480
+ if (!candidates.length) console.log(import_chalk37.default.dim(" none \u2014 run your app to discover the origins it calls"));
6257
6481
  for (const c of candidates) {
6258
- console.log(` ${import_chalk36.default.yellow("\u25CB")} ${c.origin} ${import_chalk36.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
6482
+ console.log(` ${import_chalk37.default.yellow("\u25CB")} ${c.origin} ${import_chalk37.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
6259
6483
  }
6260
6484
  if (candidates.length) {
6261
- console.log(import_chalk36.default.dim(`
6485
+ console.log(import_chalk37.default.dim(`
6262
6486
  Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
6263
- console.log(import_chalk36.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
6487
+ console.log(import_chalk37.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
6264
6488
  }
6265
6489
  }
6266
6490
  async function runOriginsApprove(origin, opts) {
6267
6491
  if (!loadCredentials()) {
6268
6492
  const cred = loadAnonCred();
6269
6493
  if (!cred) {
6270
- console.error(import_chalk36.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
6494
+ console.error(import_chalk37.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
6271
6495
  process.exit(1);
6272
6496
  }
6273
- const spinner2 = (0, import_ora19.default)(`Approving ${origin} (anonymous)...`).start();
6497
+ const spinner2 = (0, import_ora20.default)(`Approving ${origin} (anonymous)...`).start();
6274
6498
  try {
6275
6499
  const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
6276
6500
  spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
@@ -6281,7 +6505,7 @@ async function runOriginsApprove(origin, opts) {
6281
6505
  return;
6282
6506
  }
6283
6507
  const { teamId } = await resolveTeam(opts.team);
6284
- const spinner = (0, import_ora19.default)(`Approving ${origin}...`).start();
6508
+ const spinner = (0, import_ora20.default)(`Approving ${origin}...`).start();
6285
6509
  try {
6286
6510
  const out = await admin({
6287
6511
  method: "POST",
@@ -6298,7 +6522,7 @@ async function runOriginsApprove(origin, opts) {
6298
6522
  }
6299
6523
  async function runOriginsDeny(origin, opts) {
6300
6524
  const { teamId } = await resolveTeam(opts.team);
6301
- const spinner = (0, import_ora19.default)(`Dismissing ${origin}...`).start();
6525
+ const spinner = (0, import_ora20.default)(`Dismissing ${origin}...`).start();
6302
6526
  try {
6303
6527
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
6304
6528
  spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
@@ -6309,7 +6533,7 @@ async function runOriginsDeny(origin, opts) {
6309
6533
  }
6310
6534
  async function runOriginsRemove(origin, opts) {
6311
6535
  const { teamId } = await resolveTeam(opts.team);
6312
- const spinner = (0, import_ora19.default)(`Removing the proxy for ${origin}...`).start();
6536
+ const spinner = (0, import_ora20.default)(`Removing the proxy for ${origin}...`).start();
6313
6537
  try {
6314
6538
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
6315
6539
  spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
@@ -6320,19 +6544,19 @@ async function runOriginsRemove(origin, opts) {
6320
6544
  }
6321
6545
 
6322
6546
  // src/commands/op.ts
6323
- var import_chalk37 = __toESM(require("chalk"));
6547
+ var import_chalk38 = __toESM(require("chalk"));
6324
6548
  init_auth();
6325
6549
  init_trace();
6326
6550
  init_types();
6327
6551
  var OPERATOR_EMAILS = /* @__PURE__ */ new Set(["julienpmjacquet@gmail.com", "chkev@umich.edu"]);
6328
- var DASHBOARD_BASE6 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
6552
+ var DASHBOARD_BASE7 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
6329
6553
  function isOperatorLogin() {
6330
6554
  const email = loadCredentials()?.email?.toLowerCase();
6331
6555
  return !!email && OPERATOR_EMAILS.has(email);
6332
6556
  }
6333
6557
  async function opCall(call) {
6334
6558
  const token = getAccessToken();
6335
- const res = await fetch(`${DASHBOARD_BASE6}/api/cli/op`, {
6559
+ const res = await fetch(`${DASHBOARD_BASE7}/api/cli/op`, {
6336
6560
  method: "POST",
6337
6561
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
6338
6562
  body: JSON.stringify({ path: call.path, method: call.method, body: call.body })
@@ -6353,82 +6577,82 @@ function printResidue(report, applied) {
6353
6577
  const up = report?.upstash ?? {};
6354
6578
  const fga = report?.fga ?? {};
6355
6579
  const ghosts = report?.ghosts ?? {};
6356
- console.log(import_chalk37.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
6357
- console.log(import_chalk37.default.bold("\n Upstash"));
6580
+ console.log(import_chalk38.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
6581
+ console.log(import_chalk38.default.bold("\n Upstash"));
6358
6582
  const orphans = up.orphans ?? [];
6359
- if (orphans.length === 0) console.log(import_chalk37.default.green(" no orphaned keys"));
6360
- for (const o of orphans) console.log(` ${import_chalk37.default.yellow(o.key)} ${import_chalk37.default.dim(`\u2014 ${o.reason}`)}`);
6361
- console.log(import_chalk37.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
6583
+ if (orphans.length === 0) console.log(import_chalk38.default.green(" no orphaned keys"));
6584
+ for (const o of orphans) console.log(` ${import_chalk38.default.yellow(o.key)} ${import_chalk38.default.dim(`\u2014 ${o.reason}`)}`);
6585
+ console.log(import_chalk38.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
6362
6586
  if (up.anon_wallet_detail) {
6363
6587
  const d = up.anon_wallet_detail;
6364
- console.log(import_chalk37.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)"}`));
6588
+ console.log(import_chalk38.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)"}`));
6365
6589
  }
6366
6590
  if (up.keyspace_census) {
6367
6591
  const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
6368
- console.log(import_chalk37.default.dim(` keyspace: ${census}`));
6592
+ console.log(import_chalk38.default.dim(` keyspace: ${census}`));
6369
6593
  }
6370
- if (up.unknown?.length) console.log(import_chalk37.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
6371
- if (applied) console.log(` ${import_chalk37.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
6372
- for (const e of up.errors ?? []) console.log(import_chalk37.default.red(` error: ${e}`));
6373
- console.log(import_chalk37.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
6594
+ if (up.unknown?.length) console.log(import_chalk38.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
6595
+ if (applied) console.log(` ${import_chalk38.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
6596
+ for (const e of up.errors ?? []) console.log(import_chalk38.default.red(` error: ${e}`));
6597
+ console.log(import_chalk38.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
6374
6598
  if (applied) {
6375
6599
  const swept = fga?.swept ?? [];
6376
- if (swept.length === 0) console.log(import_chalk37.default.green(" no orphaned stores"));
6600
+ if (swept.length === 0) console.log(import_chalk38.default.green(" no orphaned stores"));
6377
6601
  for (const s of swept) {
6378
6602
  console.log(
6379
- ` ${import_chalk37.default.yellow(s.store_id)} ${import_chalk37.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
6603
+ ` ${import_chalk38.default.yellow(s.store_id)} ${import_chalk38.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
6380
6604
  );
6381
6605
  }
6382
- if (fga?.remaining) console.log(import_chalk37.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
6606
+ if (fga?.remaining) console.log(import_chalk38.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
6383
6607
  const st = fga?.side_tables;
6384
- if (st) console.log(import_chalk37.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})` : ""}`));
6608
+ if (st) console.log(import_chalk38.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})` : ""}`));
6385
6609
  } else {
6386
6610
  const fgaOrphans = fga?.orphans ?? [];
6387
- if (fgaOrphans.length === 0) console.log(import_chalk37.default.green(" no orphaned stores"));
6611
+ if (fgaOrphans.length === 0) console.log(import_chalk38.default.green(" no orphaned stores"));
6388
6612
  for (const s of fgaOrphans) {
6389
6613
  const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
6390
- console.log(` ${import_chalk37.default.yellow(s.store_id)} ${import_chalk37.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
6614
+ console.log(` ${import_chalk38.default.yellow(s.store_id)} ${import_chalk38.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
6391
6615
  }
6392
- console.log(import_chalk37.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
6616
+ console.log(import_chalk38.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
6393
6617
  const st = fga?.side_tables;
6394
- if (st) console.log(import_chalk37.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`));
6618
+ if (st) console.log(import_chalk38.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`));
6395
6619
  }
6396
- for (const e of fga?.errors ?? []) console.log(import_chalk37.default.red(` error: ${e}`));
6397
- console.log(import_chalk37.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
6620
+ for (const e of fga?.errors ?? []) console.log(import_chalk38.default.red(` error: ${e}`));
6621
+ console.log(import_chalk38.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
6398
6622
  if (applied) {
6399
- if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk37.default.green(" no ghost tuples"));
6400
- else console.log(` ${import_chalk37.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk37.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
6623
+ if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk38.default.green(" no ghost tuples"));
6624
+ else console.log(` ${import_chalk38.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk38.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
6401
6625
  } else {
6402
6626
  const n = ghosts?.ghost_count ?? 0;
6403
- if (n === 0) console.log(import_chalk37.default.green(` no ghost tuples ${import_chalk37.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
6627
+ if (n === 0) console.log(import_chalk38.default.green(` no ghost tuples ${import_chalk38.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
6404
6628
  else {
6405
- console.log(import_chalk37.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
6629
+ console.log(import_chalk38.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
6406
6630
  for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
6407
- console.log(import_chalk37.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
6631
+ console.log(import_chalk38.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
6408
6632
  }
6409
- if (n > 20) console.log(import_chalk37.default.dim(` \u2026 and ${n - 20} more`));
6633
+ if (n > 20) console.log(import_chalk38.default.dim(` \u2026 and ${n - 20} more`));
6410
6634
  }
6411
6635
  }
6412
- for (const e of ghosts?.errors ?? []) console.log(import_chalk37.default.red(` error: ${e}`));
6636
+ for (const e of ghosts?.errors ?? []) console.log(import_chalk38.default.red(` error: ${e}`));
6413
6637
  console.log();
6414
6638
  }
6415
6639
  async function runOp(sub, opts = {}) {
6416
6640
  if (!loadCredentials()) {
6417
- console.log(import_chalk37.default.dim("Not logged in. Run `apiblaze login`."));
6641
+ console.log(import_chalk38.default.dim("Not logged in. Run `apiblaze login`."));
6418
6642
  return;
6419
6643
  }
6420
6644
  if (!isOperatorLogin()) {
6421
- console.log(import_chalk37.default.dim("`apiblaze op` is only available to platform operators."));
6645
+ console.log(import_chalk38.default.dim("`apiblaze op` is only available to platform operators."));
6422
6646
  return;
6423
6647
  }
6424
6648
  switch (sub) {
6425
6649
  case void 0:
6426
6650
  case "menu": {
6427
- console.log(import_chalk37.default.bold("\nOperator menu"));
6428
- console.log(` ${import_chalk37.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
6429
- console.log(` ${import_chalk37.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk37.default.dim("-y to skip")})`);
6430
- console.log(` ${import_chalk37.default.cyan("apiblaze op credits")} list credit wallets`);
6431
- console.log(import_chalk37.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
6651
+ console.log(import_chalk38.default.bold("\nOperator menu"));
6652
+ console.log(` ${import_chalk38.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
6653
+ console.log(` ${import_chalk38.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk38.default.dim("-y to skip")})`);
6654
+ console.log(` ${import_chalk38.default.cyan("apiblaze op credits")} list credit wallets`);
6655
+ console.log(import_chalk38.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
6432
6656
  `));
6433
6657
  return;
6434
6658
  }
@@ -6447,17 +6671,17 @@ async function runOp(sub, opts = {}) {
6447
6671
  const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
6448
6672
  printResidue(report, false);
6449
6673
  if (nUp + nFga + nGhost + nSide === 0) {
6450
- console.log(import_chalk37.default.green("Nothing to sweep."));
6674
+ console.log(import_chalk38.default.green("Nothing to sweep."));
6451
6675
  return;
6452
6676
  }
6453
6677
  if (!opts.yes) {
6454
6678
  const readline2 = await import("readline/promises");
6455
6679
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
6456
6680
  const answer = await rl.question(
6457
- import_chalk37.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: `)
6681
+ import_chalk38.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: `)
6458
6682
  );
6459
6683
  rl.close();
6460
- if (answer.trim() !== "sweep") return void console.log(import_chalk37.default.dim("Aborted."));
6684
+ if (answer.trim() !== "sweep") return void console.log(import_chalk38.default.dim("Aborted."));
6461
6685
  }
6462
6686
  const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
6463
6687
  if (opts.json) return void console.log(JSON.stringify(result, null, 2));
@@ -6468,15 +6692,15 @@ async function runOp(sub, opts = {}) {
6468
6692
  const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
6469
6693
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
6470
6694
  const accounts = data?.accounts ?? [];
6471
- if (accounts.length === 0) return void console.log(import_chalk37.default.dim("No credit wallets."));
6695
+ if (accounts.length === 0) return void console.log(import_chalk38.default.dim("No credit wallets."));
6472
6696
  for (const a of accounts) {
6473
6697
  const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
6474
- console.log(` ${import_chalk37.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk37.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk37.default.dim(" \u2014 anon") : ""}`);
6698
+ console.log(` ${import_chalk38.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk38.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk38.default.dim(" \u2014 anon") : ""}`);
6475
6699
  }
6476
6700
  return;
6477
6701
  }
6478
6702
  default:
6479
- console.log(import_chalk37.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
6703
+ console.log(import_chalk38.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
6480
6704
  }
6481
6705
  }
6482
6706
 
@@ -6538,7 +6762,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
6538
6762
  try {
6539
6763
  const resolved = parseInt(port ?? opts.port, 10);
6540
6764
  if (Number.isNaN(resolved)) {
6541
- console.error(import_chalk38.default.red(`Invalid port: ${port ?? opts.port}`));
6765
+ console.error(import_chalk39.default.red(`Invalid port: ${port ?? opts.port}`));
6542
6766
  process.exit(1);
6543
6767
  }
6544
6768
  await runDev({ port: resolved, captureFile: opts.captureFile });
@@ -6597,6 +6821,16 @@ domain.command("list").description("List custom domains for a proxy").argument("
6597
6821
  domain.command("status").description("Check a custom domain's validation status").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainStatus(project, opts)));
6598
6822
  domain.command("rm").description("Remove a custom domain").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainRemove(project, opts)));
6599
6823
  domain.command("set-base").description("Choose which version/environment your main URL serves").argument("<project>", "Project name or id").option("--env <env>", "Environment (default: prod)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainSetBase(project, opts)));
6824
+ var group = program.command("group").description("Manage a tenant's users & groups \u2014 bare command lists groups").action(action(() => runGroupList({})));
6825
+ group.command("list").description("List groups").option("--tenant <slug>", "Tenant").option("--json", "Machine-readable output").action(action((opts) => runGroupList(opts)));
6826
+ group.command("create").description("Create a group (e.g. `group create admin`)").argument("<name>", "Group name").option("--admin <user>", "Initial group-admin (email, name, or user id)").option("--tenant <slug>", "Tenant").action(action((name, opts) => runGroupCreate(name, opts)));
6827
+ group.command("delete").description("Delete a group").argument("<name>", "Group name").option("--tenant <slug>", "Tenant").action(action((name, opts) => runGroupDelete(name, opts)));
6828
+ group.command("members").description("Show a group's members and subgroups").argument("<name>", "Group name").option("--tenant <slug>", "Tenant").option("--json", "Machine-readable output").action(action((name, opts) => runGroupMembers(name, opts)));
6829
+ group.command("add-user").description("Add a user to a group (e.g. `group add-user john admin`)").argument("<user>", "User (email, name, or user id)").argument("<group>", "Group name").option("--role <role>", "member | admin (group-admin)", "member").option("--tenant <slug>", "Tenant").action(action((user, grp, opts) => runGroupAddUser(user, grp, opts)));
6830
+ group.command("remove-user").description("Remove a user from a group").argument("<user>", "User (email, name, or user id)").argument("<group>", "Group name").option("--tenant <slug>", "Tenant").action(action((user, grp, opts) => runGroupRemoveUser(user, grp, opts)));
6831
+ group.command("add-group").description("Nest a group inside another (e.g. `group add-group reservationists admin`)").argument("<child>", "Child group name").argument("<parent>", "Parent group name").option("--tenant <slug>", "Tenant").action(action((child, parent, opts) => runGroupAddGroup(child, parent, opts)));
6832
+ group.command("remove-group").description("Un-nest a subgroup from its parent").argument("<child>", "Child group name").argument("<parent>", "Parent group name").option("--tenant <slug>", "Tenant").action(action((child, parent, opts) => runGroupRemoveGroup(child, parent, opts)));
6833
+ group.command("users").description("List the tenant user directory + identities seen in traffic").option("--tenant <slug>", "Tenant").option("--json", "Machine-readable output").action(action((opts) => runGroupUsers(opts)));
6600
6834
  var tenant = program.command("tenant").description("Manage tenants \u2014 bare command opens the interactive picker (settings, app clients, providers)").action(action(() => runTenantManage(void 0, {})));
6601
6835
  tenant.command("manage").description("Browse & edit one tenant: settings, login app clients, providers, issuers (search-first picker)").argument("[query]", "Search by tenant name/display name (omit to use your tenant scope or pick)").option("--tenant <slug>", "Exact tenant slug (skips the picker)").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantManage(query, opts)));
6602
6836
  tenant.command("use").description("Set the sticky tenant scope for future commands (search-first; --clear to unset)").argument("[query]", "Search by tenant name/display name").option("--clear", "Clear the tenant scope").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantUse(query, opts)));
@@ -6618,7 +6852,7 @@ spec.command("set").description("Replace the stored OpenAPI spec from a local fi
6618
6852
  var HELP_GROUPS = [
6619
6853
  { title: "Chat", commands: ["apichat", "agent"] },
6620
6854
  { title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
6621
- { title: "Control plane commands", commands: ["config", "projects", "tenant", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
6855
+ { title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
6622
6856
  { title: "Data plane commands", commands: [
6623
6857
  { parent: "consumer", sub: "login" },
6624
6858
  { parent: "consumer", sub: "apikeys" }
@@ -6637,7 +6871,7 @@ function groupedCommandHelp() {
6637
6871
  const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
6638
6872
  return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
6639
6873
  }).filter(Boolean).join("\n");
6640
- return `${import_chalk38.default.bold(g.title)}
6874
+ return `${import_chalk39.default.bold(g.title)}
6641
6875
  ${rows}`;
6642
6876
  }).join("\n\n");
6643
6877
  }
@@ -6671,14 +6905,14 @@ async function recoverStaleTeam() {
6671
6905
  const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
6672
6906
  const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
6673
6907
  if (!linked) {
6674
- console.error(import_chalk38.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
6908
+ console.error(import_chalk39.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
6675
6909
  return;
6676
6910
  }
6677
6911
  if (linked.teamId === creds.teamId) return;
6678
6912
  const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
6679
6913
  delete next.activeTenant;
6680
6914
  saveCredentials(next);
6681
- console.error(import_chalk38.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk38.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
6915
+ console.error(import_chalk39.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk39.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
6682
6916
  } catch {
6683
6917
  }
6684
6918
  }
@@ -6686,16 +6920,16 @@ async function printError(err) {
6686
6920
  if (err instanceof ApiError) {
6687
6921
  const data = err.body;
6688
6922
  const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
6689
- console.error(import_chalk38.default.red(`
6923
+ console.error(import_chalk39.default.red(`
6690
6924
  API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
6691
6925
  if (err.status === 403 || err.status === 404) {
6692
6926
  await recoverStaleTeam();
6693
6927
  }
6694
6928
  } else if (err instanceof Error) {
6695
- console.error(import_chalk38.default.red(`
6929
+ console.error(import_chalk39.default.red(`
6696
6930
  Error: ${err.message}`));
6697
6931
  } else {
6698
- console.error(import_chalk38.default.red("\nUnknown error"));
6932
+ console.error(import_chalk39.default.red("\nUnknown error"));
6699
6933
  }
6700
6934
  }
6701
6935
  program.parse(process.argv);