apiblaze 0.21.11 → 0.21.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +877 -610
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9,9 +9,9 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
9
  var __esm = (fn, res) => function __init() {
10
10
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
11
  };
12
- var __export = (target, all) => {
12
+ var __export = (target2, all) => {
13
13
  for (var name in all)
14
- __defProp(target, name, { get: all[name], enumerable: true });
14
+ __defProp(target2, name, { get: all[name], enumerable: true });
15
15
  };
16
16
  var __copyProps = (to, from, except, desc) => {
17
17
  if (from && typeof from === "object" || typeof from === "function") {
@@ -21,12 +21,12 @@ var __copyProps = (to, from, except, desc) => {
21
21
  }
22
22
  return to;
23
23
  };
24
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
25
  // If the importer is in node compatibility mode or this is not an ESM
26
26
  // file that has been converted to a CommonJS file using a Babel-
27
27
  // compatible transform (i.e. "__esModule" has not been set), then set
28
28
  // "default" to the CommonJS "module.exports" for node compatibility.
29
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target2, "default", { value: mod, enumerable: true }) : target2,
30
30
  mod
31
31
  ));
32
32
 
@@ -524,7 +524,7 @@ function renderTrace() {
524
524
  const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
525
525
  const masked = maskBody(e.body);
526
526
  const hasBody = e.method !== "GET" && masked !== void 0;
527
- console.error(import_chalk6.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
527
+ console.error(import_chalk6.default.green(` curl -sS -X ${e.method} '${url}' \\`));
528
528
  console.error(import_chalk6.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
529
529
  if (hasBody) {
530
530
  console.error(import_chalk6.default.green(" -H 'Content-Type: application/json' \\"));
@@ -1123,7 +1123,7 @@ async function createTenantRow(teamId) {
1123
1123
  const { name } = await inquirer3.prompt([{
1124
1124
  type: "input",
1125
1125
  name: "name",
1126
- message: `Tenant name ${import_chalk45.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
1126
+ message: `Tenant name ${import_chalk46.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
1127
1127
  validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
1128
1128
  }]);
1129
1129
  const slug = name.trim();
@@ -1138,7 +1138,7 @@ async function createTenantRow(teamId) {
1138
1138
  } catch (err) {
1139
1139
  const msg = err instanceof Error ? err.message : String(err);
1140
1140
  if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
1141
- console.log(import_chalk45.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
1141
+ console.log(import_chalk46.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
1142
1142
  continue;
1143
1143
  }
1144
1144
  throw err;
@@ -1155,11 +1155,11 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
1155
1155
  pageSize: 10,
1156
1156
  choices: [
1157
1157
  ...PROVIDERS2.map((p) => ({ name: p.label, value: p.id })),
1158
- ...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk45.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
1158
+ ...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk46.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
1159
1159
  ]
1160
1160
  }]);
1161
1161
  if (!provider) {
1162
- console.log(import_chalk45.default.dim(" No login method yet \u2014 add one later under Login methods."));
1162
+ console.log(import_chalk46.default.dim(" No login method yet \u2014 add one later under Login methods."));
1163
1163
  return false;
1164
1164
  }
1165
1165
  const opt = PROVIDERS2.find((p) => p.id === provider);
@@ -1190,7 +1190,7 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
1190
1190
  if (a.callbacks.trim()) clientBody.authorizedCallbackUrls = a.callbacks.split(/\s*,\s*/).map((s) => s.trim()).filter(Boolean);
1191
1191
  if (a.scopes.trim() && opt.own) providerBody.scopes = a.scopes.split(/\s+/).filter(Boolean);
1192
1192
  }
1193
- const spinner = (0, import_ora17.default)("Setting up login...").start();
1193
+ const spinner = (0, import_ora18.default)("Setting up login...").start();
1194
1194
  try {
1195
1195
  const client = await admin({ method: "POST", path: `${base2}/app-clients`, body: clientBody, summary: `Create login for ${tenant2}` });
1196
1196
  const clientId = client?.clientId ?? client?.client_id;
@@ -1205,27 +1205,27 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
1205
1205
  async function createTenantInteractive(teamId) {
1206
1206
  const { default: inquirer3 } = await import("inquirer");
1207
1207
  const tenant2 = await createTenantRow(teamId);
1208
- console.log(import_chalk45.default.green(` Tenant ${import_chalk45.default.bold(tenant2)} created.`));
1208
+ console.log(import_chalk46.default.green(` Tenant ${import_chalk46.default.bold(tenant2)} created.`));
1209
1209
  const { users } = await inquirer3.prompt([{
1210
1210
  type: "confirm",
1211
1211
  name: "users",
1212
1212
  default: false,
1213
- message: `Enable Users & groups? ${import_chalk45.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
1213
+ message: `Enable Users & groups? ${import_chalk46.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
1214
1214
  }]);
1215
1215
  if (users) {
1216
1216
  await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
1217
1217
  });
1218
- console.log(import_chalk45.default.green(" Users & groups enabled."));
1218
+ console.log(import_chalk46.default.green(" Users & groups enabled."));
1219
1219
  }
1220
1220
  await addApiblazeHostedLogin(teamId, tenant2, { allowSkip: true });
1221
1221
  return tenant2;
1222
1222
  }
1223
- var import_chalk45, import_ora17, PROVIDERS2, DEFAULT_SCOPES;
1223
+ var import_chalk46, import_ora18, PROVIDERS2, DEFAULT_SCOPES;
1224
1224
  var init_tenant_create = __esm({
1225
1225
  "src/lib/tenant-create.ts"() {
1226
1226
  "use strict";
1227
- import_chalk45 = __toESM(require("chalk"));
1228
- import_ora17 = __toESM(require("ora"));
1227
+ import_chalk46 = __toESM(require("chalk"));
1228
+ import_ora18 = __toESM(require("ora"));
1229
1229
  init_admin();
1230
1230
  PROVIDERS2 = [
1231
1231
  { id: "apiblaze", label: "APIblaze (via GitHub) \u2014 zero setup, recommended", own: false },
@@ -1263,10 +1263,10 @@ async function fetchPage2(teamId, q) {
1263
1263
  }
1264
1264
  function label(t, defaultTenant, active) {
1265
1265
  const tags = [
1266
- t.tenant_name === active ? import_chalk46.default.cyan("active scope") : "",
1267
- t.tenant_name === defaultTenant ? import_chalk46.default.dim("team default") : ""
1266
+ t.tenant_name === active ? import_chalk47.default.cyan("active scope") : "",
1267
+ t.tenant_name === defaultTenant ? import_chalk47.default.dim("team default") : ""
1268
1268
  ].filter(Boolean).join(", ");
1269
- const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk46.default.dim(` ${t.display_name}`) : "";
1269
+ const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk47.default.dim(` ${t.display_name}`) : "";
1270
1270
  return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
1271
1271
  }
1272
1272
  async function pickTenant(teamId, opts = {}) {
@@ -1274,14 +1274,14 @@ async function pickTenant(teamId, opts = {}) {
1274
1274
  const active = loadCredentials()?.activeTenant;
1275
1275
  let q = opts.initialQuery ?? "";
1276
1276
  for (; ; ) {
1277
- const spinner = (0, import_ora18.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
1277
+ const spinner = (0, import_ora19.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
1278
1278
  const page = await fetchPage2(teamId, q).finally(() => spinner.stop());
1279
1279
  if (!page.total && !q) {
1280
1280
  if (opts.allowCreate) {
1281
1281
  const { make } = await inquirer3.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
1282
1282
  if (make) return await createTenantInline(teamId);
1283
1283
  }
1284
- console.error(import_chalk46.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
1284
+ console.error(import_chalk47.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
1285
1285
  return null;
1286
1286
  }
1287
1287
  const truncated = page.total > page.rows.length;
@@ -1290,7 +1290,7 @@ async function pickTenant(teamId, opts = {}) {
1290
1290
  value: t.tenant_name
1291
1291
  }));
1292
1292
  if (truncated || q) {
1293
- choices.push(new inquirer3.Separator(import_chalk46.default.dim(
1293
+ choices.push(new inquirer3.Separator(import_chalk47.default.dim(
1294
1294
  truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
1295
1295
  )));
1296
1296
  choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
@@ -1324,12 +1324,12 @@ async function createTenantInline(teamId) {
1324
1324
  const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
1325
1325
  return createTenantInteractive2(teamId);
1326
1326
  }
1327
- var import_chalk46, import_ora18, PAGE;
1327
+ var import_chalk47, import_ora19, PAGE;
1328
1328
  var init_tenant_pick = __esm({
1329
1329
  "src/lib/tenant-pick.ts"() {
1330
1330
  "use strict";
1331
- import_chalk46 = __toESM(require("chalk"));
1332
- import_ora18 = __toESM(require("ora"));
1331
+ import_chalk47 = __toESM(require("chalk"));
1332
+ import_ora19 = __toESM(require("ora"));
1333
1333
  init_admin();
1334
1334
  init_auth();
1335
1335
  PAGE = 15;
@@ -1339,10 +1339,10 @@ var init_tenant_pick = __esm({
1339
1339
  // src/index.ts
1340
1340
  var import_fs5 = __toESM(require("fs"));
1341
1341
  var import_commander = require("commander");
1342
- var import_chalk63 = __toESM(require("chalk"));
1342
+ var import_chalk64 = __toESM(require("chalk"));
1343
1343
 
1344
1344
  // package.json
1345
- var version = "0.21.11";
1345
+ var version = "0.21.13";
1346
1346
 
1347
1347
  // src/index.ts
1348
1348
  init_types();
@@ -1972,8 +1972,10 @@ function randomProxyName() {
1972
1972
  // src/lib/tunnel-client.ts
1973
1973
  var import_ws = __toESM(require("ws"));
1974
1974
  var CHUNK_BYTES = 512 * 1024;
1975
- var PING_INTERVAL_MS = 6e4;
1975
+ var PING_INTERVAL_MS = 3e4;
1976
+ var SILENCE_LIMIT_MS = 75e3;
1976
1977
  var MAX_RECONNECT_DELAY_MS = 15e3;
1978
+ var TOKEN_REFRESH_AFTER_MS = 24e4;
1977
1979
  var OFFLINE_CODES = /* @__PURE__ */ new Set(["ECONNREFUSED", "ECONNRESET", "ENOTFOUND", "EHOSTUNREACH"]);
1978
1980
  var STRIP_HEADERS = /* @__PURE__ */ new Set([
1979
1981
  "host",
@@ -1997,28 +1999,52 @@ function encodeBody(buf) {
1997
1999
  return { value: buf.toString("utf8"), encoding: "utf8" };
1998
2000
  }
1999
2001
  function startTunnelClient(opts) {
2000
- const target = `http://127.0.0.1:${opts.localPort}`;
2002
+ const target2 = `http://127.0.0.1:${opts.localPort}`;
2001
2003
  const inflight = /* @__PURE__ */ new Map();
2002
2004
  let ws = null;
2003
2005
  let closed = false;
2004
2006
  let reconnects = 0;
2005
2007
  let pingTimer;
2008
+ let connectUrl = opts.connectUrl;
2009
+ let token = opts.token;
2010
+ let tokenAt = Date.now();
2011
+ let tokenRefused = false;
2012
+ let lastFrameAt = Date.now();
2006
2013
  let capturing = false;
2007
2014
  function connect() {
2008
- const url = `${opts.connectUrl}?project=${encodeURIComponent(opts.projectId)}&token=${encodeURIComponent(opts.token)}`;
2015
+ const url = `${connectUrl}?project=${encodeURIComponent(opts.projectId)}&token=${encodeURIComponent(token)}`;
2009
2016
  const socket = new import_ws.default(url);
2010
2017
  ws = socket;
2011
2018
  socket.on("open", () => {
2012
2019
  reconnects = 0;
2020
+ tokenRefused = false;
2021
+ lastFrameAt = Date.now();
2013
2022
  opts.onStatus?.("connected");
2014
2023
  pingTimer = setInterval(() => {
2024
+ if (Date.now() - lastFrameAt > SILENCE_LIMIT_MS) {
2025
+ try {
2026
+ socket.terminate();
2027
+ } catch {
2028
+ }
2029
+ return;
2030
+ }
2015
2031
  try {
2016
2032
  socket.send("ping");
2017
2033
  } catch {
2018
2034
  }
2019
2035
  }, PING_INTERVAL_MS);
2020
2036
  });
2021
- socket.on("message", (data) => handleFrame(socket, data.toString()));
2037
+ socket.on("unexpected-response", (_req, res) => {
2038
+ if (res.statusCode === 401 || res.statusCode === 403) tokenRefused = true;
2039
+ try {
2040
+ socket.terminate();
2041
+ } catch {
2042
+ }
2043
+ });
2044
+ socket.on("message", (data) => {
2045
+ lastFrameAt = Date.now();
2046
+ handleFrame(socket, data.toString());
2047
+ });
2022
2048
  socket.on("close", () => {
2023
2049
  if (pingTimer) clearInterval(pingTimer);
2024
2050
  if (!closed) scheduleReconnect();
@@ -2031,9 +2057,26 @@ function startTunnelClient(opts) {
2031
2057
  reconnects++;
2032
2058
  opts.onStatus?.(`disconnected \u2014 reconnecting in ${Math.round(delay / 1e3)}s`);
2033
2059
  setTimeout(() => {
2034
- if (!closed) connect();
2060
+ void reconnect();
2035
2061
  }, delay);
2036
2062
  }
2063
+ async function reconnect() {
2064
+ if (closed) return;
2065
+ if (opts.refresh && (tokenRefused || Date.now() - tokenAt > TOKEN_REFRESH_AFTER_MS)) {
2066
+ try {
2067
+ const fresh = await opts.refresh();
2068
+ connectUrl = fresh.url;
2069
+ token = fresh.token;
2070
+ tokenAt = Date.now();
2071
+ tokenRefused = false;
2072
+ } catch (err) {
2073
+ opts.onStatus?.(`could not refresh the tunnel token (${err?.message ?? err}) \u2014 retrying`);
2074
+ scheduleReconnect();
2075
+ return;
2076
+ }
2077
+ }
2078
+ if (!closed) connect();
2079
+ }
2037
2080
  function handleFrame(socket, raw) {
2038
2081
  let frame;
2039
2082
  try {
@@ -2099,7 +2142,7 @@ function startTunnelClient(opts) {
2099
2142
  const init = { method: f.method, headers: stripHeaders(f.headers) };
2100
2143
  if (body.length) init.body = body;
2101
2144
  try {
2102
- const resp = await fetch(target + f.path, init);
2145
+ const resp = await fetch(target2 + f.path, init);
2103
2146
  const status2 = resp.status;
2104
2147
  const buf = Buffer.from(await resp.arrayBuffer());
2105
2148
  const headers = {};
@@ -2713,7 +2756,7 @@ function writeAgentInstruction(cli, spec2, dir = process.cwd()) {
2713
2756
  "asking for data the API can provide.",
2714
2757
  ...spec2.contextNote ? ["", spec2.contextNote] : [],
2715
2758
  "",
2716
- `_Added by \`apiblaze apichat\`. Remove this block, or run \`npx apiblaze apichat ${spec2.name} --remove-mcp ${cli}\`._`,
2759
+ `_Added by \`apiblaze apichat\`. Remove this block, or run \`npx apiblaze@latest apichat ${spec2.name} --remove-mcp ${cli}\`._`,
2717
2760
  m.end,
2718
2761
  ""
2719
2762
  ].join("\n");
@@ -2835,7 +2878,7 @@ async function installAndDemo(cli, spec2, getQuestion, log4 = console.log) {
2835
2878
  ${cli.label} exited with ${ans.status ?? "no status"} \u2014 the MCP stays installed.`));
2836
2879
  }
2837
2880
  const instructionFile = writeAgentInstruction(cli.kind, spec2);
2838
- const off = `npx apiblaze apichat ${spec2.name} --remove-mcp ${cli.kind}`;
2881
+ const off = `npx apiblaze@latest apichat ${spec2.name} --remove-mcp ${cli.kind}`;
2839
2882
  const mdName = cli.kind === "claude" ? "CLAUDE.md" : "AGENTS.md";
2840
2883
  log4("");
2841
2884
  log4(import_chalk11.default.bgRed.white.bold(` ${cli.label} will launch with your MCP to ${spec2.projectLabel} enabled from now on. `));
@@ -3028,8 +3071,8 @@ async function fetchText(url) {
3028
3071
  return null;
3029
3072
  }
3030
3073
  }
3031
- async function discoverSpec(target) {
3032
- const base2 = target.replace(/\/+$/, "");
3074
+ async function discoverSpec(target2) {
3075
+ const base2 = target2.replace(/\/+$/, "");
3033
3076
  for (const suffix of ["/openapi.json", "/openapi.yaml", "/swagger.json"]) {
3034
3077
  const url = base2 + suffix;
3035
3078
  const text = await fetchText(url);
@@ -3261,14 +3304,14 @@ async function cpPost(anon, path11, body, summary) {
3261
3304
  }
3262
3305
  return admin({ method: "POST", path: path11, body, summary });
3263
3306
  }
3264
- async function provision(spec2, target, opts) {
3307
+ async function provision(spec2, target2, opts) {
3265
3308
  const loggedIn = !!loadCredentials();
3266
3309
  const anon = !loggedIn;
3267
3310
  const salt = () => Math.random().toString(36).slice(2, 6);
3268
3311
  let base2 = opts.name ? normalizeName(opts.name) : "";
3269
3312
  if (!base2) {
3270
3313
  try {
3271
- const host = new URL(target).hostname;
3314
+ const host = new URL(target2).hostname;
3272
3315
  base2 = normalizeName(host.split(".")[0]);
3273
3316
  if (base2.length < 3) base2 = normalizeName(host);
3274
3317
  } catch {
@@ -3295,7 +3338,7 @@ async function provision(spec2, target, opts) {
3295
3338
  const creds = loadCredentials();
3296
3339
  result = await createProxy({
3297
3340
  name,
3298
- target_url: target,
3341
+ target_url: target2,
3299
3342
  auth_type: "api_key",
3300
3343
  requests_auth: DUAL_AUTH,
3301
3344
  team_id: creds.teamId,
@@ -3307,8 +3350,8 @@ async function provision(spec2, target, opts) {
3307
3350
  const body = {
3308
3351
  name,
3309
3352
  subdomain: name,
3310
- target,
3311
- target_url: target,
3353
+ target: target2,
3354
+ target_url: target2,
3312
3355
  auth_type: "api_key",
3313
3356
  requests_auth: DUAL_AUTH,
3314
3357
  ...opts.apiversion ? { api_version: opts.apiversion } : {}
@@ -4493,9 +4536,9 @@ async function startChat(p, messages, opts) {
4493
4536
  }
4494
4537
  await runRepl(p, messages);
4495
4538
  }
4496
- function isApiblazeControlPlaneTarget(target) {
4539
+ function isApiblazeControlPlaneTarget(target2) {
4497
4540
  try {
4498
- const h = new URL(target).hostname.toLowerCase();
4541
+ const h = new URL(target2).hostname.toLowerCase();
4499
4542
  return h === "api.mcp.apiblaze.com" || /^externalauth(-[a-z0-9]+)?\.mcp\.apiblaze\.com$/.test(h);
4500
4543
  } catch {
4501
4544
  return false;
@@ -4631,8 +4674,8 @@ async function runApichat(opts) {
4631
4674
  }
4632
4675
  }
4633
4676
  const { spec: spec2, sourceUrl } = await loadSpec(opts);
4634
- const target = resolveTarget(spec2, opts, sourceUrl);
4635
- console.log(` ${import_chalk13.default.dim("Target:")} ${import_chalk13.default.bold(target)}`);
4677
+ const target2 = resolveTarget(spec2, opts, sourceUrl);
4678
+ console.log(` ${import_chalk13.default.dim("Target:")} ${import_chalk13.default.bold(target2)}`);
4636
4679
  const auth = await resolveTargetAuth(spec2, opts);
4637
4680
  if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
4638
4681
  fail3(
@@ -4640,11 +4683,11 @@ async function runApichat(opts) {
4640
4683
  `Re-run with --target-auth-env <ENV_VAR> holding the ${auth.basic ? "base64(user:pass)" : "secret"}.`
4641
4684
  );
4642
4685
  }
4643
- const p = await provision(spec2, target, opts);
4686
+ const p = await provision(spec2, target2, opts);
4644
4687
  console.log(` ${import_chalk13.default.dim("Proxy: ")} ${import_chalk13.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
4645
4688
  upsertApichat({
4646
4689
  name: p.projectId,
4647
- target,
4690
+ target: target2,
4648
4691
  projectId: p.projectId,
4649
4692
  version: p.version,
4650
4693
  environment: p.environment,
@@ -5446,8 +5489,8 @@ function printEnvironments(o) {
5446
5489
  const rows = names.length ? names.map((e) => [`/${e}`, o.dev && e === "dev" ? `localhost:${o.dev.port} (tunnelled from this machine)` : envs[e]?.target ?? "\u2014"]) : o.dev ? [["/dev", `localhost:${o.dev.port} (tunnelled from this machine)`]] : [];
5447
5490
  if (!rows.length) return;
5448
5491
  const w = Math.max(...rows.map(([e]) => e.length));
5449
- for (const [env, target] of rows) {
5450
- console.log(` ${import_chalk17.default.cyan(env.padEnd(w))} ${import_chalk17.default.green("\u2192")} ${target}`);
5492
+ for (const [env, target2] of rows) {
5493
+ console.log(` ${import_chalk17.default.cyan(env.padEnd(w))} ${import_chalk17.default.green("\u2192")} ${target2}`);
5451
5494
  }
5452
5495
  }
5453
5496
  async function finishSetup(o) {
@@ -5895,9 +5938,9 @@ Create an ${B.thing}`));
5895
5938
  body.openapi = await loadOpenapiSource(opts.openapi);
5896
5939
  }
5897
5940
  if (opts.target && !isHttpUrl3(opts.target)) fail4("--target must be a valid http(s) URL.");
5898
- let target = opts.target?.trim() || (typeof body.target === "string" ? body.target : "") || (typeof body.target_url === "string" ? body.target_url : "");
5941
+ let target2 = opts.target?.trim() || (typeof body.target === "string" ? body.target : "") || (typeof body.target_url === "string" ? body.target_url : "");
5899
5942
  const hasOtherSource = !!(body.openapi || body.github);
5900
- if (!target && !hasOtherSource) {
5943
+ if (!target2 && !hasOtherSource) {
5901
5944
  if (interactive) {
5902
5945
  const { default: inquirer3 } = await import("inquirer");
5903
5946
  if (name === void 0) {
@@ -5922,7 +5965,7 @@ Create an ${B.thing}`));
5922
5965
  console.log(import_chalk17.default.yellow(" Enter a valid http(s) URL.\n"));
5923
5966
  continue;
5924
5967
  }
5925
- target = url.trim();
5968
+ target2 = url.trim();
5926
5969
  break;
5927
5970
  }
5928
5971
  } else {
@@ -5930,12 +5973,12 @@ Create an ${B.thing}`));
5930
5973
  }
5931
5974
  }
5932
5975
  if (name === void 0 && opts.auto) {
5933
- const nameSource = target || (typeof opts.openapi === "string" ? opts.openapi : void 0);
5976
+ const nameSource = target2 || (typeof opts.openapi === "string" ? opts.openapi : void 0);
5934
5977
  name = deriveNameFrom(nameSource) ?? randomProxyName();
5935
5978
  }
5936
- if (target) {
5937
- body.target = target;
5938
- body.target_url = target;
5979
+ if (target2) {
5980
+ body.target = target2;
5981
+ body.target_url = target2;
5939
5982
  }
5940
5983
  if (name) {
5941
5984
  body.name = name;
@@ -6146,13 +6189,13 @@ async function chooseName(opts, openapi, teamId, interactive) {
6146
6189
  }
6147
6190
  async function createDevProxy(args) {
6148
6191
  const { auth, name, port, openapi, authChoice, families, tenant: tenant2, teamId } = args;
6149
- const target = `http://localhost:${port}`;
6192
+ const target2 = `http://localhost:${port}`;
6150
6193
  const body = {
6151
6194
  name,
6152
6195
  subdomain: name,
6153
6196
  auth_type: authChoice.auth,
6154
6197
  ...authChoice.bodyPatch,
6155
- ...openapi ? { openapi, environments: { dev: { target, description: `localhost:${port} through the ${B.cli} dev tunnel` } } } : { target_url: target },
6198
+ ...openapi ? { openapi, environments: { dev: { target: target2, description: `localhost:${port} through the ${B.cli} dev tunnel` } } } : { target_url: target2 },
6156
6199
  ...tenant2 ? { tenant: tenant2 } : {},
6157
6200
  ...teamId ? { team_id: teamId } : {},
6158
6201
  ...families ? { secure: { families } } : {}
@@ -6162,7 +6205,7 @@ async function createDevProxy(args) {
6162
6205
  let keyAuth = auth.mode === "key" ? { apiKey: auth.apiKey } : void 0;
6163
6206
  let resolvedTeam = teamId;
6164
6207
  if (caller) {
6165
- result = await producer(caller, { method: "POST", path: "/projects", body, summary: `Create ${B.thingShort} ${name} \u2192 ${target}` });
6208
+ result = await producer(caller, { method: "POST", path: "/projects", body, summary: `Create ${B.thingShort} ${name} \u2192 ${target2}` });
6166
6209
  } else {
6167
6210
  result = await createProxyAnonymous(body);
6168
6211
  if (result.cp_key && result.team_id) {
@@ -6218,7 +6261,7 @@ async function runDev(options) {
6218
6261
  }
6219
6262
  let targets = [];
6220
6263
  const wantsNew = options.openapi !== void 0 || options.name !== void 0;
6221
- if (auth.mode !== "none" && !wantsNew) {
6264
+ if (auth.mode !== "none" && (!wantsNew || options.name !== void 0 && !options.project)) {
6222
6265
  const spinner = (0, import_ora7.default)("Looking for projects that point at this machine...").start();
6223
6266
  try {
6224
6267
  targets = await getLocalhostTargets(teamId, keyAuth);
@@ -6232,7 +6275,15 @@ async function runDev(options) {
6232
6275
  let selected = [];
6233
6276
  let created = null;
6234
6277
  let setup = null;
6235
- if (options.project) {
6278
+ const rerun = !options.project && options.name !== void 0 ? targets.find((t) => t.projectId?.toLowerCase() === normalizeName2(options.name) || t.projectName?.toLowerCase() === normalizeName2(options.name)) : void 0;
6279
+ if (rerun) {
6280
+ console.log(import_chalk18.default.dim(` "${rerun.projectName}" already points at this machine \u2014 reattaching the tunnel (spec and keys unchanged).`));
6281
+ selected = [rerun];
6282
+ } else if (wantsNew) {
6283
+ targets = [];
6284
+ }
6285
+ if (selected.length) {
6286
+ } else if (options.project) {
6236
6287
  const want = options.project.toLowerCase();
6237
6288
  const match = targets.find((t) => t.projectId?.toLowerCase() === want || t.projectName?.toLowerCase() === want);
6238
6289
  if (!match) {
@@ -6338,11 +6389,12 @@ async function runDev(options) {
6338
6389
  }
6339
6390
  let restore = [];
6340
6391
  let connect;
6392
+ const registerTargets = { targets: selected.map((t) => ({ projectId: t.projectId, tenantId: t.tenantId })) };
6341
6393
  {
6342
6394
  const spinner = (0, import_ora7.default)("Registering the tunnel...").start();
6343
6395
  try {
6344
- const result = await putDevTunnel({ targets: selected.map((t) => ({ projectId: t.projectId, tenantId: t.tenantId })) }, keyAuth);
6345
- restore = result.restore ?? [];
6396
+ const result = await putDevTunnel(registerTargets, keyAuth);
6397
+ restore = (result.restore ?? []).map((r) => unmarkRestore(r, options.port));
6346
6398
  connect = result.connect;
6347
6399
  spinner.stop();
6348
6400
  } catch (err) {
@@ -6360,6 +6412,13 @@ async function runDev(options) {
6360
6412
  (projectId) => startTunnelClient({
6361
6413
  connectUrl: connect.url,
6362
6414
  token: connect.token,
6415
+ // Re-register for a fresh token. PUT is idempotent on an already-tunnelled
6416
+ // project; its restore snapshot would be the marker, so it is IGNORED —
6417
+ // teardown keeps replaying the one taken at start.
6418
+ refresh: async () => {
6419
+ const again = await putDevTunnel(registerTargets, keyAuth);
6420
+ return { url: again.connect.url, token: again.connect.token };
6421
+ },
6363
6422
  projectId,
6364
6423
  localPort: options.port,
6365
6424
  onEntry: (entry) => say(formatLogLine(entry)),
@@ -6379,6 +6438,7 @@ async function runDev(options) {
6379
6438
  const localUp = await probeLocalServer(options.port);
6380
6439
  console.log();
6381
6440
  console.log(` ${import_chalk18.default.bold("Tunnel is up \u2014 leave this running. Ctrl-C stops it.")}`);
6441
+ console.log(import_chalk18.default.dim(` If it stops, run: npx ${B.cli} dev --port ${options.port} --project ${selected[0]?.projectName ?? "<name>"} --auto (reattaches; creates nothing)`));
6382
6442
  if (!localUp) console.log(import_chalk18.default.magenta(` \u26B2 Nothing listening on port ${options.port} yet \u2014 requests are captured until your server starts.`));
6383
6443
  if (options.captureFile) console.log(import_chalk18.default.gray(` Streaming full traffic to ${options.captureFile}`));
6384
6444
  console.log();
@@ -6398,9 +6458,30 @@ async function runDev(options) {
6398
6458
  }
6399
6459
  process.on("SIGINT", () => void cleanup());
6400
6460
  process.on("SIGTERM", () => void cleanup());
6461
+ process.on("SIGHUP", () => void cleanup());
6462
+ for (const stream of [process.stdout, process.stderr]) {
6463
+ stream.on("error", (err) => {
6464
+ if (err.code !== "EPIPE") throw err;
6465
+ });
6466
+ }
6401
6467
  await new Promise(() => {
6402
6468
  });
6403
6469
  }
6470
+ function unmarkRestore(r, port) {
6471
+ const envs = r.environments;
6472
+ if (!envs || typeof envs !== "object") return r;
6473
+ let changed = false;
6474
+ const next = {};
6475
+ for (const [stage, value] of Object.entries(envs)) {
6476
+ if (typeof value?.target === "string" && value.target.startsWith("devtunnel://")) {
6477
+ next[stage] = { ...value, target: `http://localhost:${port}` };
6478
+ changed = true;
6479
+ } else {
6480
+ next[stage] = value;
6481
+ }
6482
+ }
6483
+ return changed ? { ...r, environments: next } : r;
6484
+ }
6404
6485
 
6405
6486
  // src/commands/projects.ts
6406
6487
  var import_chalk19 = __toESM(require("chalk"));
@@ -6505,10 +6586,16 @@ business logic: the gateway sits in front of the code and enforces it.
6505
6586
  - They say yes to "can I read the code" \u2192 **Adopt**: scan their app, ask only what the scan
6506
6587
  could not find, then ship it through the Ship steps.
6507
6588
  - They have a backend, just wrote one, or ask you to BUILD one and then ship it \u2192 **Ship**.
6508
- No code yet? First write the smallest plain backend that serves the resources they named \u2014
6509
- no auth, no tenancy, no ownership checks (the gateway does those); stamp rows with the
6510
- \`x-abz-user-id\` header so ownership is visible \u2014 plus its \`openapi.yaml\`, start it on
6511
- their port, then continue at Ship \xA71. Do NOT build the Demo app for this. If they say "use
6589
+ No code yet? First write the smallest plain backend that serves the resources they named,
6590
+ plus its \`openapi.yaml\`, start it on their port, then continue at Ship \xA71. Do NOT build
6591
+ the Demo app for this. The gateway does auth, tenancy and item ownership; the backend does
6592
+ only these three things:
6593
+ - stamp each new row's \`owner\` with the \`x-abz-user-id\` header;
6594
+ - filter every collection GET to rows whose \`owner\` is that header (the gateway's rules
6595
+ lock single records, never lists \u2014 a list route has no one record to check);
6596
+ - answer 401 to any call whose \`x-target-api-key\` is not \`process.env.TARGET_SERVER_SECRET\`
6597
+ (generate it with \`openssl rand -hex 24\`, put it in \`.env.local\`, start the backend with
6598
+ it; Ship \xA73b makes the proxy send it). If they say "use
6512
6599
  ${cli} on this" or "ship it with ${cli}" and point at a backend, skip any pitch and run it.
6513
6600
 
6514
6601
  Tie-breaks: a question ("what can it do", "what is it") is always **Explain**, whatever else the
@@ -6628,12 +6715,11 @@ ONLY for what the scan could not find \u2014 one short list of questions, not an
6628
6715
  sign-in plumbing, not business logic. Then the widgets they ticked, each with its server route
6629
6716
  (\`npx ${cli} integration <name> --stack <their stack>\` for keys and chat; groups is the keys
6630
6717
  route with \`createApiblazeGroups\`).
6631
- - Backend behind a secret: the proxy must send it upstream. That is a dashboard setting
6632
- (Settings \u2192 Transforms); the CLI has no flag for it yet \u2014 say so, do not invent one. For a
6633
- local run through the tunnel, the backend can instead accept calls carrying
6634
- \`x-abz-user-id\`, which the gateway stamps on every call it forwards. Say plainly that while
6635
- the tunnel runs, \`localhost:<port>\` still answers to the old secret and to anyone who sends
6636
- that header \u2014 fine on a laptop, not on a server; in prod the proxy is the only caller.
6718
+ - Backend behind a secret: the proxy must send it upstream \u2014
6719
+ \`npx ${cli} transform set-header <name> <its header> --value-env <VAR> --secret\`. Until then
6720
+ anyone reaching \`localhost:<port>\` skips the gateway.
6721
+ - List routes: check each collection GET filters by owner; if it does not, say so \u2014 the
6722
+ gateway locks single records only.
6637
6723
  - Multi-tenant app \u2192 \`npx ${cli} identified <name> require\`, so no call arrives without a user.
6638
6724
  - Change nothing in their business logic. Report what you changed, file by file.
6639
6725
 
@@ -6648,7 +6734,10 @@ ONLY for what the scan could not find \u2014 one short list of questions, not an
6648
6734
  login. Their own OAuth app \u2192 \`--apikey --oauth '{"provider":"auth0","clientId":"\u2026","clientSecret":"\u2026"}'\`
6649
6735
  (both doors: keys for tenants, their login for people and agents). Shapes below.
6650
6736
  3. **Local code** \u2192 \`npx ${cli} dev --port <port> --openapi ./openapi.yaml --name <name> --auto <door flags>\`,
6651
- in the BACKGROUND, left running \u2014 it is the tunnel. **Deployed code** \u2192
6737
+ left running \u2014 it is the tunnel. Start it as its own detached process, never \`cmd &\` inside
6738
+ a shell call that ends: \`nohup npx ${cli} dev \u2026 > ${cli}-dev.log 2>&1 < /dev/null &\`, then
6739
+ read the log. If it stops, re-run the SAME command: the same \`--name\` reattaches and
6740
+ creates nothing. Never pick a new name to get it back \u2014 that is a second proxy. **Deployed code** \u2192
6652
6741
  \`npx ${cli} create --target <url-or-spec> --name <name> --auto <door flags>\`. Both print the
6653
6742
  URL, the MCP URL (the "agents:" line), what is locked, and the bootstrap key ONCE \u2014 that key
6654
6743
  goes in the frontend-server's env, never a browser, a repo or an agent config. The tenant is
@@ -6657,22 +6746,32 @@ ONLY for what the scan could not find \u2014 one short list of questions, not an
6657
6746
  the frontend-server's env (\`.env.local\` for Next.js). Then prove it with three curls and
6658
6747
  say the codes: no key \u2192 401; create as \`X-End-User-Id: ana\` \u2192 201; PATCH that id as
6659
6748
  \`ben\` \u2192 403.
6749
+ 3b. **Only the proxy reaches the backend.** If the backend checks \`x-target-api-key\`:
6750
+ \`npx ${cli} transform set-header <name> x-target-api-key --value-env TARGET_SERVER_SECRET --secret\`
6751
+ (export the variable from \`.env.local\` first). Prove it: the same list call through the
6752
+ proxy \u2192 200; \`curl localhost:<port>/<collection>\` with no header \u2192 401.
6660
6753
  4. **Prod** (local code only): \`npx ${cli} target <name> --env prod --url <prod-url>\`.
6661
- 5. **Limits.** \`npx ${cli} throttle <name> --end-user-rate 10 --quota 10000 --period daily\` \u2014
6662
- sane defaults; use the user's numbers if they gave any. The CLI echoes \`\u2714 Update throttling
6663
- {\u2026}\`. Prove it: 15 parallel GETs as one user \u2192 about 10\xD7200 and the rest 429; a different
6664
- user right after \u2192 200.
6754
+ 5. **Limits.** Every new proxy already allows 10 requests/s per person and 10,000 a day \u2014
6755
+ say so; that is "throttled". Only for other numbers:
6756
+ \`npx ${cli} throttle <name> --rate <n> --quota <n> --period daily\`. It works logged out;
6757
+ an anonymous workspace is capped at 10/s until claimed. Prove it: 15 parallel GETs as one
6758
+ user \u2192 about 10\xD7200 and the rest 429.
6665
6759
  5b. **Attribution.** \`--auto\` leaves unattributed calls allowed: the bootstrap key with no
6666
6760
  \`X-End-User-Id\` creates rows nobody owns. For a multi-tenant app run
6667
6761
  \`npx ${cli} identified <name> require\` \u2014 then every call must carry \`X-End-User-Id\` or a
6668
- login token. Say plainly that list routes (GET on a collection) return everyone's rows;
6669
- only item routes are locked to their creator.
6670
- 6. **Admin.** \`npx ${cli} admins add <email> --tenant <tenant>\` \u2014 ask for the email, never guess
6671
- it. If you cannot ask (a scripted run), skip it and end the report with
6672
- "Admin \u2014 none yet: what email should administer tenant <tenant>?"
6673
- 7. **Report** in the shape below, then ask: "Would you like me to add the self-serve API key
6674
- widget to your site?" and "Would you like to chat with your API now via your MCP?"
6675
- (yes \u2192 \`npx ${cli} apichat <name> --install-mcp claude\`).
6762
+ login token. Prove it: the key with no \`X-End-User-Id\` \u2192 403 \`identity_required\`.
6763
+ Lists are the backend's job (it filters by owner); say that the gateway locks records,
6764
+ the backend filters lists.
6765
+ 6. **Admin.** \`npx ${cli} admins add <email> --tenant <tenant>\` with the email they named. None
6766
+ named \u2192 \`admin@ninopizzas.com\`, the demo admin (works before that person ever logs in);
6767
+ say so, and that they can \`admins add\` their own email and \`admins remove\` that one.
6768
+ 7. **Report** in the shape below, then hand it over \u2014 they try it, you do not:
6769
+ - the full curl with the real key and URL, ready to paste:
6770
+ \`curl <APIBLAZE_URL>/<collection> -H "X-API-Key: <APIBLAZE_SERVER_KEY>" -H "X-End-User-Id: ana"\`
6771
+ (say the key is the server key \u2014 for trying it here, never for a browser or an agent);
6772
+ - the command to chat with their API: \`npx ${cli} apichat <name>\` (add
6773
+ \`--xenduserid ana\` when \xA75b is on).
6774
+ Then ask once: "Would you like me to add the self-serve API key widget to your site?"
6676
6775
 
6677
6776
  ## Report like this
6678
6777
 
@@ -6685,9 +6784,15 @@ Use the real values the CLI printed. Never invent a URL, a key, a rule or a limi
6685
6784
  API https://<name>.abz.run/1.0.0/dev your tenants, with API keys
6686
6785
  /dev \u2192 localhost:<port> tunnelled from this machine \u2014 leave it running
6687
6786
  /prod \u2192 <prod-url> your deploy
6688
- Try it: curl https://<name>.abz.run/1.0.0/dev/<collection> -H "X-API-Key: <key>" -H "X-End-User-Id: you"
6689
6787
  MCP <the "agents:" URL the CLI printed> agents, signing in with <provider>
6690
- Limits 10 requests/s per user \xB7 10,000 a day
6788
+ Limits 10 requests/s per person \xB7 10,000 a day
6789
+ Backend answers only to the proxy (x-target-api-key) \xB7 lists filtered by owner
6790
+ Admin <email>
6791
+
6792
+ Try it yourself:
6793
+ curl <APIBLAZE_URL>/<collection> -H "X-API-Key: <APIBLAZE_SERVER_KEY>" -H "X-End-User-Id: ana"
6794
+ Chat with it:
6795
+ npx ${cli} apichat <name> --xenduserid ana
6691
6796
 
6692
6797
  ## Sign-in shapes \u2014 the only ones \`--oauth\` accepts; never invent fields
6693
6798
 
@@ -6709,9 +6814,10 @@ key. Mint it with \`npx ${cli} apikeys mint --desc "keys widget"\` (no \`--tenan
6709
6814
  \`--tenant\` you get a consumer key, which is the wrong kind).
6710
6815
  Edit ONLY the frontend and the frontend-server. A key per TENANT comes from that widget \u2014 each
6711
6816
  of your customers mints their own, in their own tenant; the bootstrap key is only for your
6712
- frontend-server. A key bound to one person (an agent, a CI job):
6713
- \`npx ${cli} apikeys mint --tenant <tenant> --for <email>\`. That names a person by email,
6714
- which is a DIFFERENT identity from a bare \`X-End-User-Id\` string: rows created as "ana" via
6817
+ frontend-server. Three kinds of key for a tenant, one flag each: \`apikeys mint --tenant <tenant>\` +
6818
+ \`--backend\` (a server that names the user with \`X-End-User-Id\`), \`--client\` (cannot name a
6819
+ user) or \`--for <email>\` (bound to one person \u2014 an agent, a CI job). \`--for\` names a person by
6820
+ email, which is a DIFFERENT identity from a bare \`X-End-User-Id\` string: rows created as "ana" via
6715
6821
  the header are not owned by ana@example.com's key.
6716
6822
 
6717
6823
  ## Never
@@ -6722,13 +6828,16 @@ the header are not owned by ana@example.com's key.
6722
6828
  - Put ownership or tenancy checks into business logic. The gateway enforces them.
6723
6829
  - Ask for team ids, tenant ids or project ids the CLI already knows.
6724
6830
  - Delete anything. Deletion stays with the user: \`npx ${cli} delete <name>\` (interactive;
6725
- \`--yes\` skips the prompt). Its impact banner lists tenant-scoped counts even when the tenant
6831
+ \`--yes\` skips the prompt; logged in only \u2014 an unclaimed workspace is removed after 30 days). Its impact banner lists tenant-scoped counts even when the tenant
6726
6832
  is shared \u2014 those are lost only if this is the tenant's LAST proxy.
6727
6833
 
6728
6834
  ## Later
6729
6835
 
6730
6836
  - \`npx ${cli} rule <name>\` reviews or changes the rules; \`npx ${cli} config <name> authorization.enforce_authorization false\` turns enforcement off without losing them.
6731
6837
  - \`npx ${cli} apichat <name>\` chats with the API again; \`--install-mcp claude\` connects an agent.
6838
+ - \`npx ${cli} throttle <name> --rate <n>\` \xB7 \`identified <name> require|allow-anon\` \xB7
6839
+ \`transform <name> list|set-header|remove\` \xB7 \`target <name> --env prod --url <url>\` \u2014 all work
6840
+ logged out. \`npx ${cli} <command> --help\` for the flags; \`--verbose\` prints the API calls.
6732
6841
 
6733
6842
  ## No shell? The control-plane MCP
6734
6843
 
@@ -6744,14 +6853,14 @@ async function runSkill(opts = {}) {
6744
6853
  process.stdout.write(md);
6745
6854
  return;
6746
6855
  }
6747
- const target = import_path2.default.resolve(process.cwd(), SKILL_RELATIVE_PATH);
6748
- if (import_fs3.default.existsSync(target) && !opts.force) {
6856
+ const target2 = import_path2.default.resolve(process.cwd(), SKILL_RELATIVE_PATH);
6857
+ if (import_fs3.default.existsSync(target2) && !opts.force) {
6749
6858
  console.error(import_chalk20.default.yellow(`${SKILL_RELATIVE_PATH} already exists \u2014 re-run with --force to overwrite it.`));
6750
6859
  process.exit(1);
6751
6860
  }
6752
- import_fs3.default.mkdirSync(import_path2.default.dirname(target), { recursive: true });
6753
- import_fs3.default.writeFileSync(target, md, "utf8");
6754
- console.log(`${import_chalk20.default.green("\u2713")} Installed the ${import_chalk20.default.bold(SKILL_NAME)} skill: ${import_chalk20.default.bold(target)}`);
6861
+ import_fs3.default.mkdirSync(import_path2.default.dirname(target2), { recursive: true });
6862
+ import_fs3.default.writeFileSync(target2, md, "utf8");
6863
+ console.log(`${import_chalk20.default.green("\u2713")} Installed the ${import_chalk20.default.bold(SKILL_NAME)} skill: ${import_chalk20.default.bold(target2)}`);
6755
6864
  console.log(import_chalk20.default.dim(` Claude Code picks it up in this project. Say "ship this API" and it runs npx ${B.cli} dev / create for you.`));
6756
6865
  }
6757
6866
 
@@ -6842,12 +6951,12 @@ async function runLogout(opts = {}) {
6842
6951
  console.log(import_chalk22.default.yellow("You were not logged in."));
6843
6952
  return;
6844
6953
  }
6845
- let target;
6846
- if (opts.all) target = "all";
6847
- else if (opts.producer) target = "producer";
6848
- else if (opts.consumer) target = "consumer";
6849
- else if (creds && !consumer2) target = "producer";
6850
- else if (consumer2 && !creds) target = "consumer";
6954
+ let target2;
6955
+ if (opts.all) target2 = "all";
6956
+ else if (opts.producer) target2 = "producer";
6957
+ else if (opts.consumer) target2 = "consumer";
6958
+ else if (creds && !consumer2) target2 = "producer";
6959
+ else if (consumer2 && !creds) target2 = "consumer";
6851
6960
  else {
6852
6961
  const { default: inquirer3 } = await import("inquirer");
6853
6962
  const { chosen } = await inquirer3.prompt([
@@ -6862,13 +6971,13 @@ async function runLogout(opts = {}) {
6862
6971
  ]
6863
6972
  }
6864
6973
  ]);
6865
- target = chosen;
6974
+ target2 = chosen;
6866
6975
  }
6867
- if (target === "producer" || target === "all") {
6976
+ if (target2 === "producer" || target2 === "all") {
6868
6977
  clearCredentials();
6869
6978
  console.log(import_chalk22.default.green(`\u2714 Logged out of API Producer${creds?.githubHandle ? ` (${creds.githubHandle})` : ""}.`));
6870
6979
  }
6871
- if (target === "consumer" || target === "all") {
6980
+ if (target2 === "consumer" || target2 === "all") {
6872
6981
  clearConsumer();
6873
6982
  console.log(import_chalk22.default.green(`\u2714 Logged out of API Consumer${consumer2?.tenant ? ` (${consumer2.tenant})` : ""}.`));
6874
6983
  }
@@ -7881,17 +7990,17 @@ async function runExport(projectArg, versionArg, opts) {
7881
7990
  const match = await resolveProject(teamId, projectArg, versionArg);
7882
7991
  const projectId = match.projectId;
7883
7992
  const version2 = versionArg || match.apiVersion;
7884
- const target = opts.kong ? "kong" : "data";
7993
+ const target2 = opts.kong ? "kong" : "data";
7885
7994
  const q = new URLSearchParams();
7886
7995
  if (opts.secrets) q.set("include_secrets", "1");
7887
7996
  if (opts.noConsumers) q.set("include_consumers", "0");
7888
7997
  if (opts.keys) q.set("keys", opts.keys);
7889
7998
  const qs = q.toString() ? `?${q.toString()}` : "";
7890
- console.log(import_chalk36.default.dim(`Queuing ${target} export of ${projectId}@${version2}\u2026`));
7999
+ console.log(import_chalk36.default.dim(`Queuing ${target2} export of ${projectId}@${version2}\u2026`));
7891
8000
  const job = await admin({
7892
- path: `/projects/${projectId}/${version2}/export/${target}${qs}`,
8001
+ path: `/projects/${projectId}/${version2}/export/${target2}${qs}`,
7893
8002
  method: "GET",
7894
- summary: `export ${projectId} (${target})`
8003
+ summary: `export ${projectId} (${target2})`
7895
8004
  });
7896
8005
  if (!job?.job_id) throw new Error("Failed to queue export.");
7897
8006
  const statusPath = `/projects/${projectId}/${version2}/export/jobs/${job.job_id}`;
@@ -7910,7 +8019,7 @@ async function runExport(projectArg, versionArg, opts) {
7910
8019
  }
7911
8020
  }
7912
8021
  process.stdout.write("\n");
7913
- const outFile = opts.out || `apiblaze-export-${projectId}-${version2}-${target}.zip`;
8022
+ const outFile = opts.out || `apiblaze-export-${projectId}-${version2}-${target2}.zip`;
7914
8023
  const res = await fetch(`${DASHBOARD_BASE5}/api/cli/admin/download`, {
7915
8024
  method: "POST",
7916
8025
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
@@ -7922,7 +8031,7 @@ async function runExport(projectArg, versionArg, opts) {
7922
8031
  console.log(import_chalk36.default.green(`\u2713 Saved ${outFile} (${(buf.length / 1024).toFixed(0)} KB)`));
7923
8032
  if (job.decryption_key) console.log(import_chalk36.default.yellow(`Decryption key (shown once): ${job.decryption_key}`));
7924
8033
  console.log(import_chalk36.default.dim("Read MIGRATION-REPORT.json inside the zip for the full fidelity ledger."));
7925
- if (target === "kong") console.log(import_chalk36.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
8034
+ if (target2 === "kong") console.log(import_chalk36.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
7926
8035
  }
7927
8036
 
7928
8037
  // src/commands/recipe-search.ts
@@ -8301,8 +8410,8 @@ async function runRecipeInstall(nameArg, opts) {
8301
8410
  let reinstall = false;
8302
8411
  if (opts.into) {
8303
8412
  reinstall = true;
8304
- const target = normalizeName3(opts.into);
8305
- const proj2 = await resolveProjectVia(caller, teamId, target, opts.apiversion);
8413
+ const target2 = normalizeName3(opts.into);
8414
+ const proj2 = await resolveProjectVia(caller, teamId, target2, opts.apiversion);
8306
8415
  project = proj2.projectId;
8307
8416
  apiVersion = proj2.apiVersion;
8308
8417
  tenant2 = await resolveReinstallTenant(caller, project, apiVersion);
@@ -8385,14 +8494,14 @@ function buildSteps(inp) {
8385
8494
  label: `creating ${project}`,
8386
8495
  skip: reinstall,
8387
8496
  run: async () => {
8388
- const target = firstUpstream(recipe);
8389
- if (!target) throw new Error("this recipe carries no upstream URL, so there is nothing to create a proxy against");
8497
+ const target2 = firstUpstream(recipe);
8498
+ if (!target2) throw new Error("this recipe carries no upstream URL, so there is nothing to create a proxy against");
8390
8499
  const out = await producer(caller, {
8391
8500
  method: "POST",
8392
8501
  path: "/projects",
8393
8502
  body: {
8394
8503
  name: project,
8395
- target,
8504
+ target: target2,
8396
8505
  tenant: tenant2,
8397
8506
  product_slug: project,
8398
8507
  api_version: apiVersion,
@@ -10109,14 +10218,16 @@ async function runConsumerLogsList(opts) {
10109
10218
  // src/commands/config.ts
10110
10219
  var import_chalk43 = __toESM(require("chalk"));
10111
10220
  var import_ora15 = __toESM(require("ora"));
10112
- init_admin();
10113
- init_resolve();
10221
+ init_caller();
10222
+ init_paging();
10223
+ var resolveCallerMode = () => resolveCaller()?.mode ?? null;
10114
10224
  async function patchConfig(project, opts, body, summary) {
10115
- const { teamId } = await resolveTeam(opts.team);
10116
- const proj2 = await resolveProject(teamId, project, opts.apiversion);
10225
+ const caller = requireCaller();
10226
+ const { teamId } = await resolveActingTeam(caller, opts.team);
10227
+ const proj2 = await resolveProjectVia(caller, teamId, project, opts.apiversion);
10117
10228
  const spinner = (0, import_ora15.default)(summary + "...").start();
10118
10229
  try {
10119
- const out = await admin({
10230
+ const out = await producer(caller, {
10120
10231
  method: "PATCH",
10121
10232
  path: `/projects/${proj2.projectId}/${proj2.apiVersion}`,
10122
10233
  body,
@@ -10134,13 +10245,47 @@ async function runTargetSet(project, opts) {
10134
10245
  console.error(import_chalk43.default.red("--url is required."));
10135
10246
  process.exit(1);
10136
10247
  }
10137
- const body = opts.env ? { environments: { [opts.env]: { target: opts.url } } } : { target_url: opts.url };
10138
- await patchConfig(project, opts, body, `Set target${opts.env ? ` for env ${opts.env}` : ""} \u2192 ${opts.url}`);
10248
+ if (!opts.env) {
10249
+ await patchConfig(project, opts, { target_url: opts.url }, `Set target \u2192 ${opts.url}`);
10250
+ return;
10251
+ }
10252
+ const caller = requireCaller();
10253
+ const { teamId } = await resolveActingTeam(caller, opts.team);
10254
+ const proj2 = await resolveProjectVia(caller, teamId, project, opts.apiversion);
10255
+ const listing = await pageAll(
10256
+ (cursor) => producer(caller, {
10257
+ method: "GET",
10258
+ path: withPage(`/projects?team_id=${encodeURIComponent(teamId)}`, cursor),
10259
+ summary: `Read environments for ${proj2.projectName}`
10260
+ }),
10261
+ (b) => b?.projects
10262
+ );
10263
+ const row = listing.items.find((r) => r.project_id === proj2.projectId && r.api_version === proj2.apiVersion);
10264
+ const current = row?.config?.environments;
10265
+ const existing = current && typeof current === "object" && !Array.isArray(current) ? current : {};
10266
+ const prev = existing[opts.env];
10267
+ const environments = {
10268
+ ...existing,
10269
+ [opts.env]: { ...prev && typeof prev === "object" ? prev : {}, target: opts.url }
10270
+ };
10271
+ await patchConfig(project, opts, { environments }, `Set target for env ${opts.env} \u2192 ${opts.url}`);
10139
10272
  }
10140
10273
  async function runThrottleSet(project, opts) {
10141
10274
  const throttling = {};
10142
- if (opts.rate !== void 0) throttling.userRateLimit = Number(opts.rate);
10143
- if (opts.endUserRate !== void 0) throttling.endUserRateLimit = Number(opts.endUserRate);
10275
+ const rate = opts.rate ?? opts.endUserRate;
10276
+ if (opts.rate !== void 0 && opts.endUserRate !== void 0 && opts.rate !== opts.endUserRate) {
10277
+ console.error(import_chalk43.default.red("--rate and --end-user-rate are the same limit now; pass one of them."));
10278
+ process.exit(1);
10279
+ }
10280
+ if (rate !== void 0) {
10281
+ const n = Number(rate);
10282
+ if (!Number.isFinite(n) || n <= 0) {
10283
+ console.error(import_chalk43.default.red("--rate must be a positive number of requests per second."));
10284
+ process.exit(1);
10285
+ }
10286
+ throttling.userRateLimit = n;
10287
+ throttling.endUserRateLimit = n;
10288
+ }
10144
10289
  if (opts.quota !== void 0) throttling.proxyQuota = Number(opts.quota);
10145
10290
  if (opts.period !== void 0) {
10146
10291
  if (!["daily", "weekly", "monthly"].includes(opts.period)) {
@@ -10154,6 +10299,11 @@ async function runThrottleSet(project, opts) {
10154
10299
  process.exit(1);
10155
10300
  }
10156
10301
  await patchConfig(project, opts, { throttling }, `Update throttling ${JSON.stringify(throttling)}`);
10302
+ if (resolveCallerMode() === "anon" && !opts.json) {
10303
+ console.log(import_chalk43.default.dim(
10304
+ " Anonymous workspace: the rate is capped at 10 requests/s per person and the quota at the default until you claim it (`apiblaze login`, then `apiblaze claim`)."
10305
+ ));
10306
+ }
10157
10307
  }
10158
10308
  async function runRename(project, opts) {
10159
10309
  if (!opts.displayName) {
@@ -10163,17 +10313,110 @@ async function runRename(project, opts) {
10163
10313
  await patchConfig(project, opts, { display_name: opts.displayName }, `Rename \u2192 "${opts.displayName}"`);
10164
10314
  }
10165
10315
 
10316
+ // src/commands/transform.ts
10317
+ var import_chalk44 = __toESM(require("chalk"));
10318
+ var import_ora16 = __toESM(require("ora"));
10319
+ init_caller();
10320
+ var HEADER_NAME = /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/;
10321
+ var ruleNameFor = (header) => `set header ${header.toLowerCase()}`;
10322
+ async function target(project, opts) {
10323
+ const caller = requireCaller();
10324
+ const { teamId } = await resolveActingTeam(caller, opts.team);
10325
+ const proj2 = await resolveProjectVia(caller, teamId, project, opts.apiversion);
10326
+ const base2 = `/projects/${encodeURIComponent(proj2.projectId)}/${encodeURIComponent(proj2.apiVersion)}/transforms`;
10327
+ return { caller, proj: proj2, base: base2 };
10328
+ }
10329
+ async function listRules(t) {
10330
+ const out = await producer(t.caller, {
10331
+ method: "GET",
10332
+ path: t.base,
10333
+ summary: `List transforms for ${t.proj.projectName}`
10334
+ });
10335
+ return out?.rules ?? [];
10336
+ }
10337
+ function describe(r) {
10338
+ const a = r.action ?? { type: "?" };
10339
+ if (a.type === "hardcode") {
10340
+ const secret = !a.value || /^abzsec:|^__ABZSEC_|^•+$/.test(a.value);
10341
+ return `set ${a.destination} = ${secret ? "(secret)" : a.value}`;
10342
+ }
10343
+ if (a.type === "remove") return `remove ${a.field}`;
10344
+ return a.type;
10345
+ }
10346
+ async function runTransformList(project, opts) {
10347
+ const t = await target(project, opts);
10348
+ const rules = await listRules(t);
10349
+ if (opts.json) {
10350
+ console.log(JSON.stringify(rules, null, 2));
10351
+ return;
10352
+ }
10353
+ if (!rules.length) {
10354
+ console.log(import_chalk44.default.dim(`No transforms on ${t.proj.projectName} v${t.proj.apiVersion}.`));
10355
+ return;
10356
+ }
10357
+ for (const r of rules) {
10358
+ console.log(`${r.enabled ? import_chalk44.default.green("\u25CF") : import_chalk44.default.gray("\u25CB")} ${import_chalk44.default.bold(r.name)} ${import_chalk44.default.dim(`[${r.phase}] ${r.id}`)}`);
10359
+ console.log(` ${describe(r)}`);
10360
+ }
10361
+ }
10362
+ async function runTransformSetHeader(project, header, opts) {
10363
+ if (!HEADER_NAME.test(header)) throw new Error(`"${header}" is not a valid header name.`);
10364
+ if (opts.value === void 0 === (opts.valueEnv === void 0)) {
10365
+ throw new Error("Pass exactly one of --value <value> or --value-env <VAR> (reads the value from that environment variable, so it stays out of your shell history).");
10366
+ }
10367
+ const value = opts.value ?? process.env[opts.valueEnv];
10368
+ if (!value) throw new Error(`Environment variable ${opts.valueEnv} is empty or not set.`);
10369
+ const t = await target(project, opts);
10370
+ const name = ruleNameFor(header);
10371
+ const action2 = {
10372
+ type: "hardcode",
10373
+ destination: `header:${header}`,
10374
+ value,
10375
+ // Encrypted at rest and masked on every read; main-proxy decrypts it only
10376
+ // on the way to the upstream.
10377
+ ...opts.secret ? { value_secret: true } : {}
10378
+ };
10379
+ const existing = (await listRules(t)).find((r) => r.name === name && r.phase === "request");
10380
+ const summary = `${existing ? "Update" : "Add"} transform: send ${header} to the upstream`;
10381
+ const spinner = opts.json ? null : (0, import_ora16.default)(summary + "...").start();
10382
+ try {
10383
+ const out = await producer(t.caller, existing ? { method: "PUT", path: `${t.base}/${encodeURIComponent(existing.id)}`, body: { action: action2, enabled: true }, summary } : { method: "POST", path: t.base, body: { name, action: action2, phase: "request", enabled: true }, summary });
10384
+ spinner?.succeed(`${summary} \u2014 ${t.proj.projectName} v${t.proj.apiVersion}${opts.secret ? " (stored encrypted)" : ""}.`);
10385
+ if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
10386
+ else console.log(import_chalk44.default.dim(` Every call the proxy forwards now carries ${header}. Your backend can reject calls without it.`));
10387
+ } catch (err) {
10388
+ spinner?.fail(summary + " failed.");
10389
+ throw err;
10390
+ }
10391
+ }
10392
+ async function runTransformRemove(project, nameOrId, opts) {
10393
+ const t = await target(project, opts);
10394
+ const rules = await listRules(t);
10395
+ const want = nameOrId.toLowerCase();
10396
+ const rule = rules.find((r) => r.id === nameOrId || r.name.toLowerCase() === want || r.name === ruleNameFor(nameOrId));
10397
+ if (!rule) {
10398
+ throw new Error(`No transform "${nameOrId}" on ${t.proj.projectName}.` + (rules.length ? ` Transforms: ${rules.map((r) => r.name).join(", ")}` : ""));
10399
+ }
10400
+ await producer(t.caller, {
10401
+ method: "DELETE",
10402
+ path: `${t.base}/${encodeURIComponent(rule.id)}`,
10403
+ summary: `Remove transform "${rule.name}"`
10404
+ });
10405
+ if (opts.json) console.log(JSON.stringify({ ok: true, removed: rule.id }));
10406
+ else console.log(`${import_chalk44.default.green("\u2713")} Removed transform ${import_chalk44.default.bold(rule.name)} from ${t.proj.projectName}.`);
10407
+ }
10408
+
10166
10409
  // src/commands/config-browse.ts
10167
- var import_chalk52 = __toESM(require("chalk"));
10168
- var import_ora23 = __toESM(require("ora"));
10410
+ var import_chalk53 = __toESM(require("chalk"));
10411
+ var import_ora24 = __toESM(require("ora"));
10169
10412
  init_admin();
10170
10413
  init_paging();
10171
10414
  init_auth();
10172
10415
  init_resolve();
10173
10416
 
10174
10417
  // src/commands/domain.ts
10175
- var import_chalk44 = __toESM(require("chalk"));
10176
- var import_ora16 = __toESM(require("ora"));
10418
+ var import_chalk45 = __toESM(require("chalk"));
10419
+ var import_ora17 = __toESM(require("ora"));
10177
10420
  init_admin();
10178
10421
  init_resolve();
10179
10422
  var KINDS = ["api", "mcp", "portal", "auth"];
@@ -10185,7 +10428,7 @@ async function scopeFor(project, opts) {
10185
10428
  return { kind: "project", base: `/projects/${proj2.projectId}/domains`, label: `${proj2.projectName} v${proj2.apiVersion}`, apiVersion: proj2.apiVersion };
10186
10429
  }
10187
10430
  if (!opts.tenant) {
10188
- console.error(import_chalk44.default.red("Give a <project> (api / mcp domains) or --tenant <slug> (portal / auth domains)."));
10431
+ console.error(import_chalk45.default.red("Give a <project> (api / mcp domains) or --tenant <slug> (portal / auth domains)."));
10189
10432
  process.exit(1);
10190
10433
  }
10191
10434
  const tenant2 = opts.tenant.toLowerCase();
@@ -10199,35 +10442,35 @@ function describeKind(d) {
10199
10442
  return d?.target ? `${d.kind ?? "api"} \u2192 stands in for ${d.target}` : d?.kind ?? "api";
10200
10443
  }
10201
10444
  function statusColor2(s) {
10202
- if (s === "active") return import_chalk44.default.green(s);
10203
- if (s === "failed" || s === "missing" || s === "suspended") return import_chalk44.default.red(s);
10204
- return import_chalk44.default.yellow(s ?? "unknown");
10445
+ if (s === "active") return import_chalk45.default.green(s);
10446
+ if (s === "failed" || s === "missing" || s === "suspended") return import_chalk45.default.red(s);
10447
+ return import_chalk45.default.yellow(s ?? "unknown");
10205
10448
  }
10206
10449
  async function runDomainAdd(project, opts) {
10207
10450
  if (!opts.domain) {
10208
- console.error(import_chalk44.default.red("--domain is required."));
10451
+ console.error(import_chalk45.default.red("--domain is required."));
10209
10452
  process.exit(1);
10210
10453
  }
10211
10454
  const kind = (opts.kind ?? (project ? "api" : "")).toLowerCase();
10212
10455
  if (!KINDS.includes(kind)) {
10213
- console.error(import_chalk44.default.red(`--kind must be one of ${KINDS.join(", ")}${project ? "" : " (portal or auth for a --tenant)"}.`));
10456
+ console.error(import_chalk45.default.red(`--kind must be one of ${KINDS.join(", ")}${project ? "" : " (portal or auth for a --tenant)"}.`));
10214
10457
  process.exit(1);
10215
10458
  }
10216
10459
  const tenantKind = TENANT_KINDS.includes(kind);
10217
10460
  if (tenantKind && project) {
10218
- console.error(import_chalk44.default.red(`A ${kind} domain belongs to a tenant, not a proxy: apiblaze domain add --tenant <slug> --kind ${kind} --domain ${opts.domain}`));
10461
+ console.error(import_chalk45.default.red(`A ${kind} domain belongs to a tenant, not a proxy: apiblaze domain add --tenant <slug> --kind ${kind} --domain ${opts.domain}`));
10219
10462
  process.exit(1);
10220
10463
  }
10221
10464
  if (tenantKind && !opts.tenant) {
10222
- console.error(import_chalk44.default.red(`--tenant <slug> is required for a ${kind} domain.`));
10465
+ console.error(import_chalk45.default.red(`--tenant <slug> is required for a ${kind} domain.`));
10223
10466
  process.exit(1);
10224
10467
  }
10225
10468
  if (!tenantKind && !project) {
10226
- console.error(import_chalk44.default.red(`A ${kind} domain belongs to a proxy: apiblaze domain add <project> --kind ${kind} --domain ${opts.domain}`));
10469
+ console.error(import_chalk45.default.red(`A ${kind} domain belongs to a proxy: apiblaze domain add <project> --kind ${kind} --domain ${opts.domain}`));
10227
10470
  process.exit(1);
10228
10471
  }
10229
10472
  const scope = await scopeFor(project, opts);
10230
- const spinner = (0, import_ora16.default)(`Registering ${opts.domain}...`).start();
10473
+ const spinner = (0, import_ora17.default)(`Registering ${opts.domain}...`).start();
10231
10474
  try {
10232
10475
  const body = scope.kind === "project" ? { hostname: opts.domain, api_version: scope.apiVersion, kind, ...opts.tenant ? { tenant_name: opts.tenant.toLowerCase() } : {} } : { hostname: opts.domain, kind };
10233
10476
  const out = await admin({
@@ -10241,10 +10484,10 @@ async function runDomainAdd(project, opts) {
10241
10484
  console.log(JSON.stringify(out));
10242
10485
  return;
10243
10486
  }
10244
- if (out?.cnameRecord) console.log(` ${import_chalk44.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value}`);
10245
- if (out?.dcv) console.log(` ${import_chalk44.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
10246
- if (typeof out?.monthlyCents === "number") console.log(import_chalk44.default.dim(` billed $${(out.monthlyCents / 100).toFixed(2)}/month (first month pro rata)`));
10247
- if (out?.id) console.log(import_chalk44.default.dim(` domain id: ${out.id}`));
10487
+ if (out?.cnameRecord) console.log(` ${import_chalk45.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value}`);
10488
+ if (out?.dcv) console.log(` ${import_chalk45.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
10489
+ if (typeof out?.monthlyCents === "number") console.log(import_chalk45.default.dim(` billed $${(out.monthlyCents / 100).toFixed(2)}/month (first month pro rata)`));
10490
+ if (out?.id) console.log(import_chalk45.default.dim(` domain id: ${out.id}`));
10248
10491
  } catch (err) {
10249
10492
  spinner.fail("Domain registration failed.");
10250
10493
  throw err;
@@ -10263,17 +10506,17 @@ async function runDomainList(project, opts) {
10263
10506
  return;
10264
10507
  }
10265
10508
  if (!domains.length) {
10266
- console.log(import_chalk44.default.yellow("No custom domains."));
10509
+ console.log(import_chalk45.default.yellow("No custom domains."));
10267
10510
  return;
10268
10511
  }
10269
10512
  for (const d of domains) {
10270
- const pin = scope.kind === "project" && d.tenant ? import_chalk44.default.dim(` tenant ${d.tenant}`) : "";
10271
- console.log(` ${import_chalk44.default.bold(d.hostname)} ${statusColor2(d.status)} ${import_chalk44.default.dim(describeKind(d))}${pin} ${import_chalk44.default.dim(d.id ?? "")}`);
10513
+ const pin = scope.kind === "project" && d.tenant ? import_chalk45.default.dim(` tenant ${d.tenant}`) : "";
10514
+ console.log(` ${import_chalk45.default.bold(d.hostname)} ${statusColor2(d.status)} ${import_chalk45.default.dim(describeKind(d))}${pin} ${import_chalk45.default.dim(d.id ?? "")}`);
10272
10515
  }
10273
10516
  }
10274
10517
  async function runDomainStatus(project, opts) {
10275
10518
  if (!opts.id) {
10276
- console.error(import_chalk44.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
10519
+ console.error(import_chalk45.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
10277
10520
  process.exit(1);
10278
10521
  }
10279
10522
  const scope = await scopeFor(project, opts);
@@ -10286,23 +10529,23 @@ async function runDomainStatus(project, opts) {
10286
10529
  console.log(JSON.stringify(out));
10287
10530
  return;
10288
10531
  }
10289
- console.log(` ${import_chalk44.default.bold(out?.hostname ?? opts.id)}: ${statusColor2(out?.status)} ${import_chalk44.default.dim(describeKind(out))}`);
10532
+ console.log(` ${import_chalk45.default.bold(out?.hostname ?? opts.id)}: ${statusColor2(out?.status)} ${import_chalk45.default.dim(describeKind(out))}`);
10290
10533
  if (out?.status !== "active") {
10291
- if (out?.status === "suspended") console.log(import_chalk44.default.red(" Suspended for non-payment \u2014 add credit to restore it."));
10292
- else if (out?.status === "missing") console.log(import_chalk44.default.red(" No longer registered at Cloudflare \u2014 remove it and add it again."));
10534
+ if (out?.status === "suspended") console.log(import_chalk45.default.red(" Suspended for non-payment \u2014 add credit to restore it."));
10535
+ else if (out?.status === "missing") console.log(import_chalk45.default.red(" No longer registered at Cloudflare \u2014 remove it and add it again."));
10293
10536
  else {
10294
- if (out?.cnameRecord) console.log(` ${import_chalk44.default.cyan("CNAME")} ${out.cnameRecord.name} \u2192 ${out.cnameRecord.value}`);
10295
- if (out?.dcv) console.log(` ${import_chalk44.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
10537
+ if (out?.cnameRecord) console.log(` ${import_chalk45.default.cyan("CNAME")} ${out.cnameRecord.name} \u2192 ${out.cnameRecord.value}`);
10538
+ if (out?.dcv) console.log(` ${import_chalk45.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
10296
10539
  }
10297
10540
  }
10298
10541
  }
10299
10542
  async function runDomainRemove(project, opts) {
10300
10543
  if (!opts.id) {
10301
- console.error(import_chalk44.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
10544
+ console.error(import_chalk45.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
10302
10545
  process.exit(1);
10303
10546
  }
10304
10547
  const scope = await scopeFor(project, opts);
10305
- const spinner = (0, import_ora16.default)("Removing domain...").start();
10548
+ const spinner = (0, import_ora17.default)("Removing domain...").start();
10306
10549
  try {
10307
10550
  await admin({
10308
10551
  method: "DELETE",
@@ -10319,7 +10562,7 @@ async function runDomainSetBase(project, opts) {
10319
10562
  const env = opts.env ?? "prod";
10320
10563
  const { teamId } = await resolveTeam(opts.team);
10321
10564
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
10322
- const spinner = (0, import_ora16.default)("Setting base domain...").start();
10565
+ const spinner = (0, import_ora17.default)("Setting base domain...").start();
10323
10566
  try {
10324
10567
  await admin({
10325
10568
  method: "PUT",
@@ -10335,8 +10578,8 @@ async function runDomainSetBase(project, opts) {
10335
10578
  }
10336
10579
 
10337
10580
  // src/commands/tenant.ts
10338
- var import_chalk47 = __toESM(require("chalk"));
10339
- var import_ora19 = __toESM(require("ora"));
10581
+ var import_chalk48 = __toESM(require("chalk"));
10582
+ var import_ora20 = __toESM(require("ora"));
10340
10583
  init_admin();
10341
10584
  init_resolve();
10342
10585
  init_auth();
@@ -10345,20 +10588,20 @@ init_paging();
10345
10588
  async function runTenantUse(query, opts) {
10346
10589
  const creds = loadCredentials();
10347
10590
  if (!creds) {
10348
- console.error(import_chalk47.default.red("Not logged in. Run `apiblaze login` first."));
10591
+ console.error(import_chalk48.default.red("Not logged in. Run `apiblaze login` first."));
10349
10592
  process.exit(1);
10350
10593
  }
10351
10594
  if (opts.clear) {
10352
10595
  delete creds.activeTenant;
10353
10596
  saveCredentials(creds);
10354
- console.log(import_chalk47.default.green("Tenant scope cleared."));
10597
+ console.log(import_chalk48.default.green("Tenant scope cleared."));
10355
10598
  return;
10356
10599
  }
10357
10600
  const { teamId } = await resolveTeam(opts.team);
10358
10601
  const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
10359
10602
  if (!slug) process.exit(1);
10360
10603
  saveCredentials({ ...creds, activeTenant: slug });
10361
- console.log(import_chalk47.default.green(`Tenant scope set to ${import_chalk47.default.bold(slug)}.`) + import_chalk47.default.dim(" (clear with `apiblaze tenant use --clear`)"));
10604
+ console.log(import_chalk48.default.green(`Tenant scope set to ${import_chalk48.default.bold(slug)}.`) + import_chalk48.default.dim(" (clear with `apiblaze tenant use --clear`)"));
10362
10605
  }
10363
10606
  async function runTenantList(opts) {
10364
10607
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -10382,26 +10625,26 @@ async function runTenantList(opts) {
10382
10625
  return;
10383
10626
  }
10384
10627
  if (!tenants.length) {
10385
- console.log(import_chalk47.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
10628
+ console.log(import_chalk48.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
10386
10629
  return;
10387
10630
  }
10388
10631
  for (const t of tenants) {
10389
10632
  const name = typeof t === "string" ? t : t.tenant_name;
10390
- const display = typeof t === "string" ? "" : import_chalk47.default.dim(` ${t.display_name ?? ""}`);
10391
- console.log(` ${import_chalk47.default.bold(name)}${display}`);
10633
+ const display = typeof t === "string" ? "" : import_chalk48.default.dim(` ${t.display_name ?? ""}`);
10634
+ console.log(` ${import_chalk48.default.bold(name)}${display}`);
10392
10635
  }
10393
10636
  const total = out?.total ?? tenants.length;
10394
10637
  if (total > tenants.length) {
10395
- console.log(import_chalk47.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
10638
+ console.log(import_chalk48.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
10396
10639
  }
10397
10640
  }
10398
10641
  async function runTenantCreate(opts) {
10399
10642
  if (!opts.name) {
10400
- console.error(import_chalk47.default.red("--name (display name) is required."));
10643
+ console.error(import_chalk48.default.red("--name (display name) is required."));
10401
10644
  process.exit(1);
10402
10645
  }
10403
10646
  const { teamId } = await resolveTeam(opts.team);
10404
- const spinner = (0, import_ora19.default)("Creating tenant...").start();
10647
+ const spinner = (0, import_ora20.default)("Creating tenant...").start();
10405
10648
  try {
10406
10649
  const out = await admin({
10407
10650
  method: "POST",
@@ -10409,7 +10652,7 @@ async function runTenantCreate(opts) {
10409
10652
  body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
10410
10653
  summary: `Create tenant "${opts.name}"`
10411
10654
  });
10412
- spinner.succeed(`Created tenant ${import_chalk47.default.bold(out?.tenant_name ?? opts.name)}.`);
10655
+ spinner.succeed(`Created tenant ${import_chalk48.default.bold(out?.tenant_name ?? opts.name)}.`);
10413
10656
  if (opts.json) console.log(JSON.stringify(out));
10414
10657
  } catch (err) {
10415
10658
  spinner.fail("Tenant create failed.");
@@ -10418,12 +10661,12 @@ async function runTenantCreate(opts) {
10418
10661
  }
10419
10662
  async function runTenantAttach(project, opts) {
10420
10663
  if (!opts.tenant) {
10421
- console.error(import_chalk47.default.red("--tenant <slug> is required."));
10664
+ console.error(import_chalk48.default.red("--tenant <slug> is required."));
10422
10665
  process.exit(1);
10423
10666
  }
10424
10667
  const { teamId } = await resolveTeam(opts.team);
10425
10668
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
10426
- const spinner = (0, import_ora19.default)("Attaching tenant...").start();
10669
+ const spinner = (0, import_ora20.default)("Attaching tenant...").start();
10427
10670
  try {
10428
10671
  const out = await admin({
10429
10672
  method: "POST",
@@ -10452,11 +10695,11 @@ async function runTenantDelete(slug, opts) {
10452
10695
  }
10453
10696
  ]);
10454
10697
  if (!confirm2) {
10455
- console.log(import_chalk47.default.dim("Aborted."));
10698
+ console.log(import_chalk48.default.dim("Aborted."));
10456
10699
  return;
10457
10700
  }
10458
10701
  }
10459
- const spinner = (0, import_ora19.default)(hard ? "Trashing tenant..." : "Deleting tenant...").start();
10702
+ const spinner = (0, import_ora20.default)(hard ? "Trashing tenant..." : "Deleting tenant...").start();
10460
10703
  try {
10461
10704
  await admin({
10462
10705
  method: "DELETE",
@@ -10480,7 +10723,7 @@ async function runTenantDelete(slug, opts) {
10480
10723
  summary: `Purge tenant ${slug} (full cascade)`
10481
10724
  });
10482
10725
  } catch (err) {
10483
- console.log(import_chalk47.default.red("\u2716 Tenant purge failed to start."));
10726
+ console.log(import_chalk48.default.red("\u2716 Tenant purge failed to start."));
10484
10727
  throw err;
10485
10728
  }
10486
10729
  const job = await settleDelete(accepted, {
@@ -10496,18 +10739,18 @@ async function runTenantDelete(slug, opts) {
10496
10739
  ...accepted?.job_id ? { job_id: accepted.job_id, job_status: job?.status ?? "queued" } : {}
10497
10740
  }));
10498
10741
  } else if (opts.wait !== false) {
10499
- console.log(import_chalk47.default.green(`\u2714 Deleted tenant ${slug}.`));
10742
+ console.log(import_chalk48.default.green(`\u2714 Deleted tenant ${slug}.`));
10500
10743
  }
10501
10744
  }
10502
10745
  async function runTenantCors(opts) {
10503
10746
  if (!opts.tenant) {
10504
- console.error(import_chalk47.default.red("--tenant <slug> is required."));
10747
+ console.error(import_chalk48.default.red("--tenant <slug> is required."));
10505
10748
  process.exit(1);
10506
10749
  }
10507
10750
  const { teamId } = await resolveTeam(opts.team);
10508
10751
  const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
10509
10752
  const cors = origins.length ? { allowed_origins: origins } : null;
10510
- const spinner = (0, import_ora19.default)("Updating CORS...").start();
10753
+ const spinner = (0, import_ora20.default)("Updating CORS...").start();
10511
10754
  try {
10512
10755
  await admin({
10513
10756
  method: "PUT",
@@ -10523,8 +10766,8 @@ async function runTenantCors(opts) {
10523
10766
  }
10524
10767
 
10525
10768
  // src/commands/tenant-drill.ts
10526
- var import_chalk48 = __toESM(require("chalk"));
10527
- var import_ora20 = __toESM(require("ora"));
10769
+ var import_chalk49 = __toESM(require("chalk"));
10770
+ var import_ora21 = __toESM(require("ora"));
10528
10771
  var import_crypto = require("crypto");
10529
10772
  init_admin();
10530
10773
  init_auth();
@@ -10554,17 +10797,17 @@ async function validScopedTenant(teamId, query) {
10554
10797
  const next = { ...creds };
10555
10798
  delete next.activeTenant;
10556
10799
  saveCredentials2(next);
10557
- console.log(import_chalk48.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
10800
+ console.log(import_chalk49.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
10558
10801
  return void 0;
10559
10802
  }
10560
10803
  async function tenantHome(teamId, tenant2) {
10561
10804
  const { default: inquirer3 } = await import("inquirer");
10562
10805
  const base2 = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
10563
- console.log(import_chalk48.default.bold(`
10806
+ console.log(import_chalk49.default.bold(`
10564
10807
  Tenant ${tenant2}`));
10565
- console.log(import_chalk48.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
10808
+ console.log(import_chalk49.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
10566
10809
  for (; ; ) {
10567
- const spinner = (0, import_ora20.default)("Reading tenant state...").start();
10810
+ const spinner = (0, import_ora21.default)("Reading tenant state...").start();
10568
10811
  const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
10569
10812
  admin({ method: "GET", path: `${base2}/iam`, summary: "Read IAM toggle" }).catch(() => null),
10570
10813
  admin({ method: "GET", path: `${base2}/cors`, summary: "Read tenant CORS" }).catch(() => null),
@@ -10578,18 +10821,18 @@ Tenant ${tenant2}`));
10578
10821
  const nClients = Array.isArray(clients) ? clients.length : 0;
10579
10822
  const nOpaque = opaque?.opaque?.endpoint ? 1 : 0;
10580
10823
  const nLogin = nClients + nIssuers + nOpaque;
10581
- const onOff = (b) => b ? import_chalk48.default.green("on") : import_chalk48.default.dim("off");
10824
+ const onOff = (b) => b ? import_chalk49.default.green("on") : import_chalk49.default.dim("off");
10582
10825
  const { pick: pick2 } = await inquirer3.prompt([{
10583
10826
  type: "list",
10584
10827
  name: "pick",
10585
10828
  message: `Tenant ${tenant2}:`,
10586
10829
  pageSize: 12,
10587
10830
  choices: [
10588
- { name: `Login methods (${nLogin}) ${import_chalk48.default.dim("how your consumers sign in")}`, value: "login" },
10589
- { name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk48.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
10590
- { name: `Portal admins (${nEmails}) ${import_chalk48.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
10591
- new inquirer3.Separator(import_chalk48.default.dim(" Settings")),
10592
- { name: `CORS: ${cors?.cors ? import_chalk48.default.cyan(JSON.stringify(cors.cors)) : import_chalk48.default.dim("(unset)")} ${import_chalk48.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
10831
+ { name: `Login methods (${nLogin}) ${import_chalk49.default.dim("how your consumers sign in")}`, value: "login" },
10832
+ { name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk49.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
10833
+ { name: `Portal admins (${nEmails}) ${import_chalk49.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
10834
+ new inquirer3.Separator(import_chalk49.default.dim(" Settings")),
10835
+ { name: `CORS: ${cors?.cors ? import_chalk49.default.cyan(JSON.stringify(cors.cors)) : import_chalk49.default.dim("(unset)")} ${import_chalk49.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
10593
10836
  { name: "\u2190 Back", value: "back" }
10594
10837
  ]
10595
10838
  }]);
@@ -10602,7 +10845,7 @@ Tenant ${tenant2}`));
10602
10845
  case "iam": {
10603
10846
  const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
10604
10847
  await admin({ method: "PATCH", path: `${base2}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
10605
- console.log(import_chalk48.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
10848
+ console.log(import_chalk49.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
10606
10849
  break;
10607
10850
  }
10608
10851
  case "cors": {
@@ -10615,11 +10858,11 @@ Tenant ${tenant2}`));
10615
10858
  if (v === "") break;
10616
10859
  const parsed = v === "null" ? null : safeJson(v);
10617
10860
  if (parsed === void 0) {
10618
- console.log(import_chalk48.default.yellow(" Not valid JSON \u2014 unchanged."));
10861
+ console.log(import_chalk49.default.yellow(" Not valid JSON \u2014 unchanged."));
10619
10862
  break;
10620
10863
  }
10621
10864
  await admin({ method: "PUT", path: `${base2}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
10622
- console.log(import_chalk48.default.green(" CORS updated."));
10865
+ console.log(import_chalk49.default.green(" CORS updated."));
10623
10866
  break;
10624
10867
  }
10625
10868
  case "emails":
@@ -10631,7 +10874,7 @@ Tenant ${tenant2}`));
10631
10874
  async function loginMethodsMenu(teamId, tenant2, base2) {
10632
10875
  const { default: inquirer3 } = await import("inquirer");
10633
10876
  for (; ; ) {
10634
- const spinner = (0, import_ora20.default)("Loading login methods...").start();
10877
+ const spinner = (0, import_ora21.default)("Loading login methods...").start();
10635
10878
  const [rawClients, rawIssuers, rawOpaque] = await Promise.all([
10636
10879
  admin({ method: "GET", path: `${base2}/app-clients`, summary: "List APIblaze-hosted logins" }).catch(() => []),
10637
10880
  admin({ method: "GET", path: `${base2}/external-issuers`, summary: "List your-own-JWT logins" }).catch(() => null),
@@ -10643,15 +10886,15 @@ async function loginMethodsMenu(teamId, tenant2, base2) {
10643
10886
  const choices = [];
10644
10887
  for (const c of appClients) {
10645
10888
  const nP = (c.providers ?? []).length || c.providers_count || 0;
10646
- choices.push({ name: `${import_chalk48.default.cyan("APIblaze-hosted")} ${import_chalk48.default.bold(c.name ?? c.clientId)} ${import_chalk48.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk48.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
10889
+ choices.push({ name: `${import_chalk49.default.cyan("APIblaze-hosted")} ${import_chalk49.default.bold(c.name ?? c.clientId)} ${import_chalk49.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk49.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
10647
10890
  }
10648
10891
  for (const i of issuers) {
10649
- choices.push({ name: `${import_chalk48.default.cyan("Your own \u2014 JWT ")} ${import_chalk48.default.bold(i.iss)} ${import_chalk48.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
10892
+ choices.push({ name: `${import_chalk49.default.cyan("Your own \u2014 JWT ")} ${import_chalk49.default.bold(i.iss)} ${import_chalk49.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
10650
10893
  }
10651
10894
  if (opaque?.endpoint) {
10652
- choices.push({ name: `${import_chalk48.default.cyan("Your own \u2014 opaque")} ${import_chalk48.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
10895
+ choices.push({ name: `${import_chalk49.default.cyan("Your own \u2014 opaque")} ${import_chalk49.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
10653
10896
  }
10654
- if (!choices.length) console.log(import_chalk48.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
10897
+ if (!choices.length) console.log(import_chalk49.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
10655
10898
  const { pick: pick2 } = await inquirer3.prompt([{
10656
10899
  type: "list",
10657
10900
  name: "pick",
@@ -10691,9 +10934,9 @@ async function addLoginMethod(teamId, tenant2, base2) {
10691
10934
  message: "How do people log in?",
10692
10935
  pageSize: 8,
10693
10936
  choices: [
10694
- { name: `APIblaze hosted login page ${import_chalk48.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
10695
- { name: `Your own hosted login \u2014 JWT ${import_chalk48.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
10696
- { name: `Your own login \u2014 opaque token ${import_chalk48.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
10937
+ { name: `APIblaze hosted login page ${import_chalk49.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
10938
+ { name: `Your own hosted login \u2014 JWT ${import_chalk49.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
10939
+ { name: `Your own login \u2014 opaque token ${import_chalk49.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
10697
10940
  { name: "\u2190 Back", value: null }
10698
10941
  ]
10699
10942
  }]);
@@ -10717,8 +10960,8 @@ function safeJson(s) {
10717
10960
  async function emailsMenu(base2, emails) {
10718
10961
  const { default: inquirer3 } = await import("inquirer");
10719
10962
  console.log();
10720
- if (!emails.length) console.log(import_chalk48.default.dim(" No consumer-admin emails."));
10721
- for (const e of emails) console.log(` ${import_chalk48.default.bold(e.email ?? e)} ${import_chalk48.default.dim(e.status ?? "")}`);
10963
+ if (!emails.length) console.log(import_chalk49.default.dim(" No consumer-admin emails."));
10964
+ for (const e of emails) console.log(` ${import_chalk49.default.bold(e.email ?? e)} ${import_chalk49.default.dim(e.status ?? "")}`);
10722
10965
  const { act } = await inquirer3.prompt([{
10723
10966
  type: "list",
10724
10967
  name: "act",
@@ -10733,7 +10976,7 @@ async function emailsMenu(base2, emails) {
10733
10976
  if (act === "add") {
10734
10977
  const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
10735
10978
  await admin({ method: "POST", path: `${base2}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
10736
- console.log(import_chalk48.default.green(` ${email} added.`));
10979
+ console.log(import_chalk49.default.green(` ${email} added.`));
10737
10980
  } else {
10738
10981
  const { e } = await inquirer3.prompt([{
10739
10982
  type: "list",
@@ -10743,7 +10986,7 @@ async function emailsMenu(base2, emails) {
10743
10986
  }]);
10744
10987
  if (!e) return;
10745
10988
  await admin({ method: "DELETE", path: `${base2}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
10746
- console.log(import_chalk48.default.green(` ${e} removed.`));
10989
+ console.log(import_chalk49.default.green(` ${e} removed.`));
10747
10990
  }
10748
10991
  }
10749
10992
  async function addIssuer(base2) {
@@ -10764,19 +11007,19 @@ async function addIssuer(base2) {
10764
11007
  body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
10765
11008
  summary: `Add external issuer ${a.iss.trim()}`
10766
11009
  });
10767
- console.log(import_chalk48.default.green(" JWT login method saved."));
11010
+ console.log(import_chalk49.default.green(" JWT login method saved."));
10768
11011
  }
10769
11012
  async function issuerHome(base2, issuer) {
10770
11013
  const { default: inquirer3 } = await import("inquirer");
10771
11014
  console.log(`
10772
- ${import_chalk48.default.bold(issuer.iss)} ${import_chalk48.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
11015
+ ${import_chalk49.default.bold(issuer.iss)} ${import_chalk49.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
10773
11016
  const { act } = await inquirer3.prompt([{
10774
11017
  type: "list",
10775
11018
  name: "act",
10776
11019
  message: "This JWT login method:",
10777
11020
  choices: [
10778
11021
  { name: "Replace it (re-enter details)", value: "edit" },
10779
- { name: import_chalk48.default.red("Delete it"), value: "rm" },
11022
+ { name: import_chalk49.default.red("Delete it"), value: "rm" },
10780
11023
  { name: "\u2190 Back", value: "back" }
10781
11024
  ]
10782
11025
  }]);
@@ -10792,7 +11035,7 @@ async function issuerHome(base2, issuer) {
10792
11035
  path: `${base2}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
10793
11036
  summary: `Delete issuer ${issuer.iss}`
10794
11037
  });
10795
- console.log(import_chalk48.default.green(" Deleted."));
11038
+ console.log(import_chalk49.default.green(" Deleted."));
10796
11039
  }
10797
11040
  async function setOpaque(base2, cur) {
10798
11041
  const { default: inquirer3 } = await import("inquirer");
@@ -10801,19 +11044,19 @@ async function setOpaque(base2, cur) {
10801
11044
  { type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
10802
11045
  ]);
10803
11046
  await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
10804
- console.log(import_chalk48.default.green(" Opaque login method set."));
11047
+ console.log(import_chalk49.default.green(" Opaque login method set."));
10805
11048
  }
10806
11049
  async function opaqueHome(base2, cur) {
10807
11050
  const { default: inquirer3 } = await import("inquirer");
10808
11051
  console.log(`
10809
- ${import_chalk48.default.bold(cur.endpoint)} ${import_chalk48.default.dim(cur.method ?? "GET")}`);
11052
+ ${import_chalk49.default.bold(cur.endpoint)} ${import_chalk49.default.dim(cur.method ?? "GET")}`);
10810
11053
  const { act } = await inquirer3.prompt([{
10811
11054
  type: "list",
10812
11055
  name: "act",
10813
11056
  message: "This opaque login method:",
10814
11057
  choices: [
10815
11058
  { name: "Replace it", value: "edit" },
10816
- { name: import_chalk48.default.red("Delete it"), value: "rm" },
11059
+ { name: import_chalk49.default.red("Delete it"), value: "rm" },
10817
11060
  { name: "\u2190 Back", value: "back" }
10818
11061
  ]
10819
11062
  }]);
@@ -10823,14 +11066,14 @@ async function opaqueHome(base2, cur) {
10823
11066
  return;
10824
11067
  }
10825
11068
  await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
10826
- console.log(import_chalk48.default.green(" Deleted."));
11069
+ console.log(import_chalk49.default.green(" Deleted."));
10827
11070
  }
10828
11071
  async function clientHome(base2, summary) {
10829
11072
  const { default: inquirer3 } = await import("inquirer");
10830
11073
  const id = summary.clientId ?? summary.client_id;
10831
11074
  const cBase = `${base2}/app-clients/${encodeURIComponent(id)}`;
10832
11075
  for (; ; ) {
10833
- const spinner = (0, import_ora20.default)("Reading app client...").start();
11076
+ const spinner = (0, import_ora21.default)("Reading app client...").start();
10834
11077
  const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
10835
11078
  spinner.stop();
10836
11079
  const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
@@ -10842,13 +11085,13 @@ async function clientHome(base2, summary) {
10842
11085
  message: `${c.name ?? id}:`,
10843
11086
  pageSize: 12,
10844
11087
  choices: [
10845
- { name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk48.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
10846
- { name: `Callback URLs: ${cb.length ? import_chalk48.default.cyan(cb.join(", ")) : import_chalk48.default.dim("(none)")}`, value: "callbacks" },
10847
- { name: `Scopes: ${scopes.length ? import_chalk48.default.cyan(scopes.join(" ")) : import_chalk48.default.dim("(defaults)")}`, value: "scopes" },
11088
+ { name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk49.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
11089
+ { name: `Callback URLs: ${cb.length ? import_chalk49.default.cyan(cb.join(", ")) : import_chalk49.default.dim("(none)")}`, value: "callbacks" },
11090
+ { name: `Scopes: ${scopes.length ? import_chalk49.default.cyan(scopes.join(" ")) : import_chalk49.default.dim("(defaults)")}`, value: "scopes" },
10848
11091
  { name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
10849
11092
  { name: "Reveal client secret", value: "secret" },
10850
11093
  { name: "Rotate client secret", value: "rotate" },
10851
- { name: import_chalk48.default.red("Delete this app client"), value: "delete" },
11094
+ { name: import_chalk49.default.red("Delete this app client"), value: "delete" },
10852
11095
  { name: "\u2190 Back", value: "back" }
10853
11096
  ]
10854
11097
  }]);
@@ -10861,13 +11104,13 @@ async function clientHome(base2, summary) {
10861
11104
  case "callbacks": {
10862
11105
  const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
10863
11106
  await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
10864
- console.log(import_chalk48.default.green(" Callbacks updated."));
11107
+ console.log(import_chalk49.default.green(" Callbacks updated."));
10865
11108
  break;
10866
11109
  }
10867
11110
  case "scopes": {
10868
11111
  const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
10869
11112
  await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
10870
- console.log(import_chalk48.default.green(" Scopes updated."));
11113
+ console.log(import_chalk49.default.green(" Scopes updated."));
10871
11114
  break;
10872
11115
  }
10873
11116
  case "expiries": {
@@ -10882,14 +11125,14 @@ async function clientHome(base2, summary) {
10882
11125
  body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
10883
11126
  summary: "Update token expiries"
10884
11127
  });
10885
- console.log(import_chalk48.default.green(" Expiries updated."));
11128
+ console.log(import_chalk49.default.green(" Expiries updated."));
10886
11129
  break;
10887
11130
  }
10888
11131
  case "secret": {
10889
11132
  const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
10890
11133
  if (!sure) break;
10891
11134
  const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
10892
- console.log(` ${import_chalk48.default.bold("client_secret")}: ${import_chalk48.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
11135
+ console.log(` ${import_chalk49.default.bold("client_secret")}: ${import_chalk49.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
10893
11136
  break;
10894
11137
  }
10895
11138
  case "rotate": {
@@ -10897,14 +11140,14 @@ async function clientHome(base2, summary) {
10897
11140
  if (!sure) break;
10898
11141
  const fresh = randomSecret();
10899
11142
  await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
10900
- console.log(` New ${import_chalk48.default.bold("client_secret")}: ${import_chalk48.default.green(fresh)} ${import_chalk48.default.dim("(store it now)")}`);
11143
+ console.log(` New ${import_chalk49.default.bold("client_secret")}: ${import_chalk49.default.green(fresh)} ${import_chalk49.default.dim("(store it now)")}`);
10901
11144
  break;
10902
11145
  }
10903
11146
  case "delete": {
10904
11147
  const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Delete app client "${c.name ?? id}"? Consumers logged in through it will lose access.`, default: false }]);
10905
11148
  if (!sure) break;
10906
11149
  await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
10907
- console.log(import_chalk48.default.green(" App client deleted."));
11150
+ console.log(import_chalk49.default.green(" App client deleted."));
10908
11151
  return;
10909
11152
  }
10910
11153
  }
@@ -10925,15 +11168,15 @@ var DEFAULT_SCOPES2 = {
10925
11168
  async function providersMenu(cBase, clientLabel) {
10926
11169
  const { default: inquirer3 } = await import("inquirer");
10927
11170
  for (; ; ) {
10928
- const spinner = (0, import_ora20.default)("Loading providers...").start();
11171
+ const spinner = (0, import_ora21.default)("Loading providers...").start();
10929
11172
  const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
10930
11173
  spinner.stop();
10931
11174
  const providers = Array.isArray(raw) ? raw : [];
10932
11175
  console.log();
10933
11176
  for (const p of providers) {
10934
- console.log(` ${import_chalk48.default.bold(p.type)} ${import_chalk48.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
11177
+ console.log(` ${import_chalk49.default.bold(p.type)} ${import_chalk49.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
10935
11178
  }
10936
- if (!providers.length) console.log(import_chalk48.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
11179
+ if (!providers.length) console.log(import_chalk49.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
10937
11180
  const { act } = await inquirer3.prompt([{
10938
11181
  type: "list",
10939
11182
  name: "act",
@@ -10991,23 +11234,23 @@ async function providersMenu(cBase, clientLabel) {
10991
11234
  body.targetServerToken = routing;
10992
11235
  }
10993
11236
  await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
10994
- console.log(import_chalk48.default.green(` ${type} provider added.`));
11237
+ console.log(import_chalk49.default.green(` ${type} provider added.`));
10995
11238
  } else {
10996
11239
  const { p } = await inquirer3.prompt([{
10997
11240
  type: "list",
10998
11241
  name: "p",
10999
11242
  message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
11000
- choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk48.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
11243
+ choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk49.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
11001
11244
  }]);
11002
11245
  if (!p) continue;
11003
11246
  if (act === "rm") {
11004
11247
  const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Remove the ${p.type} provider? Consumers using it can no longer sign in.`, default: false }]);
11005
11248
  if (!sure) continue;
11006
11249
  await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
11007
- console.log(import_chalk48.default.green(` ${p.type} removed.`));
11250
+ console.log(import_chalk49.default.green(` ${p.type} removed.`));
11008
11251
  } else {
11009
11252
  const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
11010
- console.log(` ${import_chalk48.default.bold("client_secret")}: ${import_chalk48.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
11253
+ console.log(` ${import_chalk49.default.bold("client_secret")}: ${import_chalk49.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
11011
11254
  }
11012
11255
  }
11013
11256
  }
@@ -11015,8 +11258,8 @@ async function providersMenu(cBase, clientLabel) {
11015
11258
 
11016
11259
  // src/commands/spec.ts
11017
11260
  var fs12 = __toESM(require("fs"));
11018
- var import_chalk49 = __toESM(require("chalk"));
11019
- var import_ora21 = __toESM(require("ora"));
11261
+ var import_chalk50 = __toESM(require("chalk"));
11262
+ var import_ora22 = __toESM(require("ora"));
11020
11263
  init_admin();
11021
11264
  init_api();
11022
11265
  init_resolve();
@@ -11032,19 +11275,19 @@ async function runSpecGet(project, opts) {
11032
11275
  }
11033
11276
  async function runSpecSet(project, opts) {
11034
11277
  if (!opts.file) {
11035
- console.error(import_chalk49.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
11278
+ console.error(import_chalk50.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
11036
11279
  process.exit(1);
11037
11280
  }
11038
11281
  let specContent;
11039
11282
  try {
11040
11283
  specContent = fs12.readFileSync(opts.file, "utf-8");
11041
11284
  } catch {
11042
- console.error(import_chalk49.default.red(`Cannot read file: ${opts.file}`));
11285
+ console.error(import_chalk50.default.red(`Cannot read file: ${opts.file}`));
11043
11286
  process.exit(1);
11044
11287
  }
11045
11288
  const { teamId } = await resolveTeam(opts.team);
11046
11289
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
11047
- const spinner = (0, import_ora21.default)("Uploading spec...").start();
11290
+ const spinner = (0, import_ora22.default)("Uploading spec...").start();
11048
11291
  try {
11049
11292
  const out = await admin({
11050
11293
  method: "POST",
@@ -11068,28 +11311,28 @@ async function runSpecSet(project, opts) {
11068
11311
  function printSpecChangeReport(report, projectName) {
11069
11312
  const r = report.routes;
11070
11313
  if (r) {
11071
- for (const a of r.added) console.log(import_chalk49.default.green(` + ${a}`));
11072
- for (const d of r.removed) console.log(import_chalk49.default.red(` \u2212 ${d}`));
11073
- for (const rn of r.renamed ?? []) console.log(import_chalk49.default.yellow(` ~ ${rn.from} \u2192 ${rn.to}`));
11074
- for (const m of r.modified ?? []) console.log(import_chalk49.default.dim(` \xB1 ${m} (params/schema changed)`));
11314
+ for (const a of r.added) console.log(import_chalk50.default.green(` + ${a}`));
11315
+ for (const d of r.removed) console.log(import_chalk50.default.red(` \u2212 ${d}`));
11316
+ for (const rn of r.renamed ?? []) console.log(import_chalk50.default.yellow(` ~ ${rn.from} \u2192 ${rn.to}`));
11317
+ for (const m of r.modified ?? []) console.log(import_chalk50.default.dim(` \xB1 ${m} (params/schema changed)`));
11075
11318
  }
11076
- if (report.applied?.mcp_regenerated) console.log(import_chalk49.default.dim(" MCP catalogue regenerated."));
11077
- if (report.applied?.candidates_cleared) console.log(import_chalk49.default.dim(` ${report.applied.candidates_cleared} route candidate(s) now documented \u2014 cleared.`));
11319
+ if (report.applied?.mcp_regenerated) console.log(import_chalk50.default.dim(" MCP catalogue regenerated."));
11320
+ if (report.applied?.candidates_cleared) console.log(import_chalk50.default.dim(` ${report.applied.candidates_cleared} route candidate(s) now documented \u2014 cleared.`));
11078
11321
  for (const item of report.attention ?? []) {
11079
- console.log(import_chalk49.default.yellow(` \u26A0 ${item.detail}`));
11322
+ console.log(import_chalk50.default.yellow(` \u26A0 ${item.detail}`));
11080
11323
  if (item.suggestion?.carry_from && item.suggestion.carry_to) {
11081
- console.log(import_chalk49.default.dim(` Fix: apiblaze spec carry ${projectName} --method ${item.suggestion.method} --from "${item.suggestion.carry_from}" --to "${item.suggestion.carry_to}"`));
11324
+ console.log(import_chalk50.default.dim(` Fix: apiblaze spec carry ${projectName} --method ${item.suggestion.method} --from "${item.suggestion.carry_from}" --to "${item.suggestion.carry_to}"`));
11082
11325
  }
11083
11326
  }
11084
11327
  }
11085
11328
  async function runSpecCarry(project, opts) {
11086
11329
  if (!opts.method || !opts.from || !opts.to) {
11087
- console.error(import_chalk49.default.red("--method, --from and --to are required."));
11330
+ console.error(import_chalk50.default.red("--method, --from and --to are required."));
11088
11331
  process.exit(1);
11089
11332
  }
11090
11333
  const { teamId } = await resolveTeam(opts.team);
11091
11334
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
11092
- const spinner = (0, import_ora21.default)(`Carrying rules ${opts.from} \u2192 ${opts.to}...`).start();
11335
+ const spinner = (0, import_ora22.default)(`Carrying rules ${opts.from} \u2192 ${opts.to}...`).start();
11093
11336
  const res = await agentCall(`/projects/${proj2.projectId}/${proj2.apiVersion}/policies/route/carry`, "POST", {
11094
11337
  method: opts.method.toUpperCase(),
11095
11338
  from_resource: opts.from,
@@ -11106,13 +11349,13 @@ async function runSpecCarry(project, opts) {
11106
11349
  }
11107
11350
  async function runSpecDeleteRule(project, opts) {
11108
11351
  if (!opts.method || !opts.route) {
11109
- console.error(import_chalk49.default.red("--method and --route are required."));
11352
+ console.error(import_chalk50.default.red("--method and --route are required."));
11110
11353
  process.exit(1);
11111
11354
  }
11112
11355
  const { teamId } = await resolveTeam(opts.team);
11113
11356
  const proj2 = await resolveProject(teamId, project, opts.apiversion);
11114
11357
  const encoded = opts.route.replace(/\{/g, "%7B").replace(/\}/g, "%7D");
11115
- const spinner = (0, import_ora21.default)(`Deleting rules for ${opts.method.toUpperCase()} ${opts.route}...`).start();
11358
+ const spinner = (0, import_ora22.default)(`Deleting rules for ${opts.method.toUpperCase()} ${opts.route}...`).start();
11116
11359
  const res = await agentCall(
11117
11360
  `/projects/${proj2.projectId}/${proj2.apiVersion}/policies/route/${opts.method.toUpperCase()}${encoded.startsWith("/") ? encoded : `/${encoded}`}`,
11118
11361
  "DELETE"
@@ -11126,14 +11369,14 @@ async function runSpecDeleteRule(project, opts) {
11126
11369
  }
11127
11370
 
11128
11371
  // src/commands/agent.ts
11129
- var import_chalk51 = __toESM(require("chalk"));
11130
- var import_ora22 = __toESM(require("ora"));
11372
+ var import_chalk52 = __toESM(require("chalk"));
11373
+ var import_ora23 = __toESM(require("ora"));
11131
11374
  init_auth();
11132
11375
  init_admin();
11133
11376
  init_resolve();
11134
11377
 
11135
11378
  // src/lib/tools.ts
11136
- var import_chalk50 = __toESM(require("chalk"));
11379
+ var import_chalk51 = __toESM(require("chalk"));
11137
11380
  init_admin();
11138
11381
  init_api();
11139
11382
  init_resolve();
@@ -11164,15 +11407,15 @@ var TOOLS = [
11164
11407
  const key = keys.dev ?? Object.values(keys)[0];
11165
11408
  const url = `https://${a.name}.abz.run/${version2}/dev`;
11166
11409
  const tryIt = buildTryItCurl(url, auth, key, res.tenant);
11167
- const lines = [` ${import_chalk50.default.dim("Proxy URL:")} ${import_chalk50.default.bold(url)}`];
11168
- if (res.devPortal) lines.push(` ${import_chalk50.default.dim("Dev portal:")} ${res.devPortal}`);
11410
+ const lines = [` ${import_chalk51.default.dim("Proxy URL:")} ${import_chalk51.default.bold(url)}`];
11411
+ if (res.devPortal) lines.push(` ${import_chalk51.default.dim("Dev portal:")} ${res.devPortal}`);
11169
11412
  const envs = Object.keys(keys);
11170
11413
  if (envs.length) {
11171
- lines.push("", ` ${import_chalk50.default.bold("API keys")} ${import_chalk50.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
11414
+ lines.push("", ` ${import_chalk51.default.bold("API keys")} ${import_chalk51.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
11172
11415
  const w = Math.max(...envs.map((e) => e.length));
11173
- for (const env of envs) lines.push(` ${import_chalk50.default.cyan(env.padEnd(w))} ${import_chalk50.default.green(keys[env])}`);
11416
+ for (const env of envs) lines.push(` ${import_chalk51.default.cyan(env.padEnd(w))} ${import_chalk51.default.green(keys[env])}`);
11174
11417
  }
11175
- if (tryIt) lines.push("", ` ${import_chalk50.default.dim("Try it:")}`, ` ${import_chalk50.default.cyan(tryIt)}`);
11418
+ if (tryIt) lines.push("", ` ${import_chalk51.default.dim("Try it:")}`, ` ${import_chalk51.default.cyan(tryIt)}`);
11176
11419
  return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
11177
11420
  }
11178
11421
  },
@@ -11341,23 +11584,23 @@ function truncate(value, max = 1500) {
11341
11584
  }
11342
11585
  function printCost(resp) {
11343
11586
  const line = formatBilling(resp.billing);
11344
- if (line) console.log(import_chalk51.default.magenta(` ${line}`) + import_chalk51.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
11587
+ if (line) console.log(import_chalk52.default.magenta(` ${line}`) + import_chalk52.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
11345
11588
  }
11346
11589
  async function runAgent(opts) {
11347
11590
  requireAuth();
11348
11591
  const { teamId, teamName } = await resolveTeam(opts.team);
11349
11592
  const { default: inquirer3 } = await import("inquirer");
11350
- console.log(import_chalk51.default.bold("APIblaze agent") + import_chalk51.default.dim(` \xB7 team ${teamName ?? teamId}`));
11351
- console.log(import_chalk51.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
11593
+ console.log(import_chalk52.default.bold("APIblaze agent") + import_chalk52.default.dim(` \xB7 team ${teamName ?? teamId}`));
11594
+ console.log(import_chalk52.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
11352
11595
  const history = [];
11353
11596
  while (true) {
11354
- const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk51.default.cyan("you") + " \u203A" }]);
11597
+ const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk52.default.cyan("you") + " \u203A" }]);
11355
11598
  const text = (input ?? "").trim();
11356
11599
  if (!text) continue;
11357
11600
  if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
11358
11601
  history.push({ role: "user", content: text });
11359
11602
  for (let step = 0; step < MAX_TOOL_STEPS; step++) {
11360
- const spinner = (0, import_ora22.default)({ text: "thinking...", color: "magenta" }).start();
11603
+ const spinner = (0, import_ora23.default)({ text: "thinking...", color: "magenta" }).start();
11361
11604
  let resp;
11362
11605
  try {
11363
11606
  resp = await callAgent(history, teamId);
@@ -11365,21 +11608,21 @@ async function runAgent(opts) {
11365
11608
  } catch (err) {
11366
11609
  spinner.stop();
11367
11610
  if (err instanceof ApiError && err.status === 402) {
11368
- console.log(import_chalk51.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
11611
+ console.log(import_chalk52.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
11369
11612
  break;
11370
11613
  }
11371
11614
  throw err;
11372
11615
  }
11373
11616
  history.push({ role: "assistant", content: resp.raw });
11374
11617
  printCost(resp);
11375
- if (resp.reply) console.log(import_chalk51.default.green("agent") + " \u203A " + resp.reply);
11618
+ if (resp.reply) console.log(import_chalk52.default.green("agent") + " \u203A " + resp.reply);
11376
11619
  if (!resp.action) break;
11377
11620
  const tool = findTool(resp.action.tool);
11378
11621
  if (!tool) {
11379
11622
  history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
11380
11623
  continue;
11381
11624
  }
11382
- const runSpinner = (0, import_ora22.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
11625
+ const runSpinner = (0, import_ora23.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
11383
11626
  try {
11384
11627
  const result = await tool.run(resp.action.args, { teamId });
11385
11628
  runSpinner.succeed(`${tool.name} \u2713`);
@@ -11397,11 +11640,11 @@ async function runAgent(opts) {
11397
11640
  }
11398
11641
  renderTrace();
11399
11642
  if (step === MAX_TOOL_STEPS - 1) {
11400
- console.log(import_chalk51.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
11643
+ console.log(import_chalk52.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
11401
11644
  }
11402
11645
  }
11403
11646
  }
11404
- console.log(import_chalk51.default.dim("\nBye."));
11647
+ console.log(import_chalk52.default.dim("\nBye."));
11405
11648
  }
11406
11649
 
11407
11650
  // src/commands/config-browse.ts
@@ -11709,11 +11952,11 @@ function dig(blob, dotted) {
11709
11952
  }
11710
11953
  var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
11711
11954
  function show(v) {
11712
- if (v === void 0) return import_chalk52.default.dim("(unset)");
11713
- if (v === null) return import_chalk52.default.dim("null");
11714
- if (typeof v === "object") return import_chalk52.default.cyan(JSON.stringify(v));
11715
- if (typeof v === "boolean") return v ? import_chalk52.default.green("on") : import_chalk52.default.red("off");
11716
- return import_chalk52.default.cyan(String(v));
11955
+ if (v === void 0) return import_chalk53.default.dim("(unset)");
11956
+ if (v === null) return import_chalk53.default.dim("null");
11957
+ if (typeof v === "object") return import_chalk53.default.cyan(JSON.stringify(v));
11958
+ if (typeof v === "boolean") return v ? import_chalk53.default.green("on") : import_chalk53.default.red("off");
11959
+ return import_chalk53.default.cyan(String(v));
11717
11960
  }
11718
11961
  function parseValue(raw) {
11719
11962
  if (raw === "true") return true;
@@ -11748,7 +11991,7 @@ async function patchSetting(proj2, s, value, cfg) {
11748
11991
  if (!tenant2) throw new Error(`Could not tell which tenant ${proj2.projectName} serves \u2014 pass it via \`apiblaze config ${proj2.projectName} --tenant <slug>\` is not supported yet; run \`apiblaze projects\` and check the project has a tenant.`);
11749
11992
  body.tenant = tenant2;
11750
11993
  }
11751
- const spinner = (0, import_ora23.default)(`Set ${s.key}...`).start();
11994
+ const spinner = (0, import_ora24.default)(`Set ${s.key}...`).start();
11752
11995
  try {
11753
11996
  await admin({
11754
11997
  method: "PATCH",
@@ -11763,10 +12006,10 @@ async function patchSetting(proj2, s, value, cfg) {
11763
12006
  }
11764
12007
  }
11765
12008
  var loginFirst = (what) => {
11766
- console.log(import_chalk52.default.yellow(`
12009
+ console.log(import_chalk53.default.yellow(`
11767
12010
  Log in first to ${what}.`));
11768
- console.log(import_chalk52.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
11769
- console.log(import_chalk52.default.dim(" anonymously and want to bring it into your account.\n"));
12011
+ console.log(import_chalk53.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
12012
+ console.log(import_chalk53.default.dim(" anonymously and want to bring it into your account.\n"));
11770
12013
  };
11771
12014
  async function runConfig(project, key, value, opts) {
11772
12015
  const creds = loadCredentials();
@@ -11783,9 +12026,9 @@ async function runConfig(project, key, value, opts) {
11783
12026
  }
11784
12027
  const setting = SETTINGS.find((s) => s.key === key);
11785
12028
  if (!setting) {
11786
- console.error(import_chalk52.default.red(`Unknown setting "${key}".`));
11787
- console.error(import_chalk52.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
11788
- console.error(import_chalk52.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
12029
+ console.error(import_chalk53.default.red(`Unknown setting "${key}".`));
12030
+ console.error(import_chalk53.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
12031
+ console.error(import_chalk53.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
11789
12032
  process.exit(1);
11790
12033
  }
11791
12034
  if (value === void 0) {
@@ -11800,7 +12043,7 @@ async function pickProject(teamId) {
11800
12043
  const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
11801
12044
  const projects = await getProjects2(teamId).catch(() => []);
11802
12045
  if (!projects.length) {
11803
- console.error(import_chalk52.default.red("No projects in this team. Create one: `npx apiblaze create`."));
12046
+ console.error(import_chalk53.default.red("No projects in this team. Create one: `npx apiblaze create`."));
11804
12047
  process.exit(1);
11805
12048
  }
11806
12049
  const { default: inquirer3 } = await import("inquirer");
@@ -11808,7 +12051,7 @@ async function pickProject(teamId) {
11808
12051
  type: "list",
11809
12052
  name: "picked",
11810
12053
  message: "Which project?",
11811
- choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk52.default.dim("v" + p.apiVersion)}`, value: p }))
12054
+ choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk53.default.dim("v" + p.apiVersion)}`, value: p }))
11812
12055
  }]);
11813
12056
  return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
11814
12057
  }
@@ -11819,25 +12062,25 @@ function printAll(proj2, cfg, json) {
11819
12062
  console.log(JSON.stringify(out, null, 2));
11820
12063
  return;
11821
12064
  }
11822
- console.log(import_chalk52.default.bold(`
12065
+ console.log(import_chalk53.default.bold(`
11823
12066
  ${proj2.projectName} v${proj2.apiVersion} \u2014 settings
11824
12067
  `));
11825
12068
  for (const group2 of SETTING_GROUPS) {
11826
- console.log(import_chalk52.default.bold(group2));
12069
+ console.log(import_chalk53.default.bold(group2));
11827
12070
  for (const s of SETTINGS.filter((x) => x.group === group2)) {
11828
- console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk52.default.dim(s.desc)}`);
12071
+ console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk53.default.dim(s.desc)}`);
11829
12072
  }
11830
12073
  console.log();
11831
12074
  }
11832
- console.log(import_chalk52.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
12075
+ console.log(import_chalk53.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
11833
12076
  }
11834
12077
  async function discoveryMenu(project) {
11835
12078
  const { default: inquirer3 } = await import("inquirer");
11836
- console.log(import_chalk52.default.bold(`
12079
+ console.log(import_chalk53.default.bold(`
11837
12080
  APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
11838
12081
  `));
11839
- console.log(import_chalk52.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
11840
- console.log(import_chalk52.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
12082
+ console.log(import_chalk53.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
12083
+ console.log(import_chalk53.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
11841
12084
  for (; ; ) {
11842
12085
  const { pick: pick2 } = await inquirer3.prompt([{
11843
12086
  type: "list",
@@ -11845,13 +12088,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
11845
12088
  message: "Explore:",
11846
12089
  pageSize: 20,
11847
12090
  choices: [
11848
- new inquirer3.Separator(import_chalk52.default.bold("\u2014 Settings \u2014")),
12091
+ new inquirer3.Separator(import_chalk53.default.bold("\u2014 Settings \u2014")),
11849
12092
  ...SETTING_GROUPS.map((g) => ({
11850
- name: `${g} ${import_chalk52.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
12093
+ name: `${g} ${import_chalk53.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
11851
12094
  value: { kind: "settings", g }
11852
12095
  })),
11853
- new inquirer3.Separator(import_chalk52.default.bold("\u2014 Features \u2014")),
11854
- ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk52.default.dim(f.desc)}`, value: { kind: "feature", f } })),
12096
+ new inquirer3.Separator(import_chalk53.default.bold("\u2014 Features \u2014")),
12097
+ ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk53.default.dim(f.desc)}`, value: { kind: "feature", f } })),
11855
12098
  new inquirer3.Separator(),
11856
12099
  { name: "Exit", value: { kind: "exit" } }
11857
12100
  ]
@@ -11860,24 +12103,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
11860
12103
  if (pick2.kind === "settings") {
11861
12104
  console.log();
11862
12105
  for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
11863
- console.log(` ${import_chalk52.default.bold(s.label.padEnd(28))} ${import_chalk52.default.dim(s.desc)}`);
11864
- console.log(` ${import_chalk52.default.dim(" key: " + s.key)}`);
12106
+ console.log(` ${import_chalk53.default.bold(s.label.padEnd(28))} ${import_chalk53.default.dim(s.desc)}`);
12107
+ console.log(` ${import_chalk53.default.dim(" key: " + s.key)}`);
11865
12108
  }
11866
12109
  loginFirst("view or change these settings");
11867
12110
  } else {
11868
12111
  const f = pick2.f;
11869
12112
  console.log(`
11870
- ${import_chalk52.default.bold(f.label)} \u2014 ${f.desc}`);
12113
+ ${import_chalk53.default.bold(f.label)} \u2014 ${f.desc}`);
11871
12114
  loginFirst(`use ${f.label.toLowerCase()}`);
11872
12115
  }
11873
12116
  }
11874
12117
  }
11875
12118
  async function navigator(proj2, cfg, opts) {
11876
12119
  const { default: inquirer3 } = await import("inquirer");
11877
- console.log(import_chalk52.default.bold(`
12120
+ console.log(import_chalk53.default.bold(`
11878
12121
  ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
11879
12122
  `));
11880
- console.log(import_chalk52.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
12123
+ console.log(import_chalk53.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
11881
12124
  let blob = cfg;
11882
12125
  for (; ; ) {
11883
12126
  const { pick: pick2 } = await inquirer3.prompt([{
@@ -11886,10 +12129,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
11886
12129
  message: "Where to?",
11887
12130
  pageSize: 20,
11888
12131
  choices: [
11889
- new inquirer3.Separator(import_chalk52.default.bold("\u2014 Settings \u2014")),
12132
+ new inquirer3.Separator(import_chalk53.default.bold("\u2014 Settings \u2014")),
11890
12133
  ...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
11891
- new inquirer3.Separator(import_chalk52.default.bold("\u2014 Features \u2014")),
11892
- ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk52.default.dim(f.desc)}`, value: { kind: f.go } })),
12134
+ new inquirer3.Separator(import_chalk53.default.bold("\u2014 Features \u2014")),
12135
+ ...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk53.default.dim(f.desc)}`, value: { kind: f.go } })),
11893
12136
  new inquirer3.Separator(),
11894
12137
  { name: "Show all settings", value: { kind: "list" } },
11895
12138
  { name: "Exit", value: { kind: "exit" } }
@@ -11929,7 +12172,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
11929
12172
  }
11930
12173
  }
11931
12174
  } catch (err) {
11932
- console.error(import_chalk52.default.red(` ${err instanceof Error ? err.message : String(err)}`));
12175
+ console.error(import_chalk53.default.red(` ${err instanceof Error ? err.message : String(err)}`));
11933
12176
  }
11934
12177
  }
11935
12178
  }
@@ -11943,7 +12186,7 @@ async function settingsGroup(proj2, cfg, group2) {
11943
12186
  message: group2 + ":",
11944
12187
  pageSize: 16,
11945
12188
  choices: [
11946
- ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk52.default.dim(s2.desc)}`, value: s2 })),
12189
+ ...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk53.default.dim(s2.desc)}`, value: s2 })),
11947
12190
  new inquirer3.Separator(),
11948
12191
  { name: "\u2190 Back", value: null }
11949
12192
  ]
@@ -11961,7 +12204,7 @@ async function settingsGroup(proj2, cfg, group2) {
11961
12204
  } else if (s.type === "number") {
11962
12205
  const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
11963
12206
  if (v === "" || Number.isNaN(Number(v))) {
11964
- console.log(import_chalk52.default.yellow(" Not a number \u2014 unchanged."));
12207
+ console.log(import_chalk53.default.yellow(" Not a number \u2014 unchanged."));
11965
12208
  continue;
11966
12209
  }
11967
12210
  value = Number(v);
@@ -12042,7 +12285,7 @@ async function buildCondition(phase) {
12042
12285
  const items = [];
12043
12286
  for (; ; ) {
12044
12287
  const a = await inquirer3.prompt([
12045
- { type: "input", name: "source", message: `Condition field ${import_chalk52.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
12288
+ { type: "input", name: "source", message: `Condition field ${import_chalk53.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
12046
12289
  { type: "list", name: "operator", message: "Operator:", choices: [
12047
12290
  "eq",
12048
12291
  "neq",
@@ -12073,7 +12316,7 @@ async function buildCondition(phase) {
12073
12316
  function showCondition(cond) {
12074
12317
  if (!Array.isArray(cond) || !cond.length) return "";
12075
12318
  const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
12076
- return import_chalk52.default.dim(` when ${s}`);
12319
+ return import_chalk53.default.dim(` when ${s}`);
12077
12320
  }
12078
12321
  async function transformsMenu(proj2) {
12079
12322
  const { default: inquirer3 } = await import("inquirer");
@@ -12082,12 +12325,12 @@ async function transformsMenu(proj2) {
12082
12325
  const out = await admin({ method: "GET", path: base2, summary: "List transform rules" });
12083
12326
  const rules = out?.rules ?? [];
12084
12327
  console.log();
12085
- if (!rules.length) console.log(import_chalk52.default.dim(" No transform rules yet."));
12328
+ if (!rules.length) console.log(import_chalk53.default.dim(" No transform rules yet."));
12086
12329
  for (const r of rules) {
12087
12330
  const a = r.action ?? {};
12088
12331
  const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
12089
- const what = a.type === "hardcode" ? `${a.destination} = "${a.value}"` : a.type === "remove" ? `remove ${a.field}` : `${a.source} \u2192 ${a.destination}${a.lookup ? " (mapped)" : ""}${fns.length ? import_chalk52.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
12090
- console.log(` ${r.enabled ? import_chalk52.default.green("\u25CF") : import_chalk52.default.dim("\u25CB")} ${import_chalk52.default.bold(r.name)} ${import_chalk52.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
12332
+ const what = a.type === "hardcode" ? `${a.destination} = "${a.value}"` : a.type === "remove" ? `remove ${a.field}` : `${a.source} \u2192 ${a.destination}${a.lookup ? " (mapped)" : ""}${fns.length ? import_chalk53.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
12333
+ console.log(` ${r.enabled ? import_chalk53.default.green("\u25CF") : import_chalk53.default.dim("\u25CB")} ${import_chalk53.default.bold(r.name)} ${import_chalk53.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
12091
12334
  }
12092
12335
  const { act } = await inquirer3.prompt([{
12093
12336
  type: "list",
@@ -12099,7 +12342,7 @@ async function transformsMenu(proj2) {
12099
12342
  { name: "Enable/disable a rule", value: "toggle" },
12100
12343
  { name: "Delete a rule", value: "delete" }
12101
12344
  ] : [],
12102
- { name: import_chalk52.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
12345
+ { name: import_chalk53.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
12103
12346
  { name: "\u2190 Back", value: "back" }
12104
12347
  ]
12105
12348
  }]);
@@ -12112,11 +12355,11 @@ async function transformsMenu(proj2) {
12112
12355
  }]);
12113
12356
  const body = parseValue(raw);
12114
12357
  if (!body || typeof body !== "object" || Array.isArray(body)) {
12115
- console.log(import_chalk52.default.yellow(" Not a JSON object \u2014 skipped."));
12358
+ console.log(import_chalk53.default.yellow(" Not a JSON object \u2014 skipped."));
12116
12359
  continue;
12117
12360
  }
12118
12361
  await admin({ method: "POST", path: base2, body, summary: "Create transform rule (raw JSON)" });
12119
- console.log(import_chalk52.default.green(" Rule created."));
12362
+ console.log(import_chalk53.default.green(" Rule created."));
12120
12363
  continue;
12121
12364
  }
12122
12365
  if (act === "add") {
@@ -12132,7 +12375,7 @@ async function transformsMenu(proj2) {
12132
12375
  { name: "Remove a field", value: "remove" }
12133
12376
  ] }
12134
12377
  ]);
12135
- const fieldHint = import_chalk52.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
12378
+ const fieldHint = import_chalk53.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
12136
12379
  let action2;
12137
12380
  if (ans.kind === "hardcode") {
12138
12381
  const a = await inquirer3.prompt([
@@ -12161,7 +12404,7 @@ async function transformsMenu(proj2) {
12161
12404
  };
12162
12405
  }
12163
12406
  const condition = await buildCondition(ans.phase);
12164
- const spinner = (0, import_ora23.default)("Creating rule...").start();
12407
+ const spinner = (0, import_ora24.default)("Creating rule...").start();
12165
12408
  try {
12166
12409
  await admin({
12167
12410
  method: "POST",
@@ -12179,16 +12422,16 @@ async function transformsMenu(proj2) {
12179
12422
  type: "list",
12180
12423
  name: "rule",
12181
12424
  message: act === "toggle" ? "Which rule?" : "Delete which rule?",
12182
- choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk52.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
12425
+ choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk53.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
12183
12426
  }]);
12184
12427
  if (!rule) continue;
12185
12428
  if (act === "toggle") {
12186
12429
  const flipped = { ...rule, enabled: rule.enabled === false };
12187
12430
  await admin({ method: "PUT", path: `${base2}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
12188
- console.log(import_chalk52.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
12431
+ console.log(import_chalk53.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
12189
12432
  } else {
12190
12433
  await admin({ method: "DELETE", path: `${base2}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
12191
- console.log(import_chalk52.default.green(` ${rule.name} deleted.`));
12434
+ console.log(import_chalk53.default.green(` ${rule.name} deleted.`));
12192
12435
  }
12193
12436
  }
12194
12437
  }
@@ -12200,9 +12443,9 @@ async function mappingsMenu(proj2) {
12200
12443
  const out = await admin({ method: "GET", path: base2, summary: "List mapping tables" });
12201
12444
  const tables = out?.mappings ?? out?.tables ?? [];
12202
12445
  console.log();
12203
- if (!tables.length) console.log(import_chalk52.default.dim(" No mapping tables yet."));
12446
+ if (!tables.length) console.log(import_chalk53.default.dim(" No mapping tables yet."));
12204
12447
  for (const t of tables) {
12205
- console.log(` ${import_chalk52.default.bold(t.name)} ${import_chalk52.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
12448
+ console.log(` ${import_chalk53.default.bold(t.name)} ${import_chalk53.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
12206
12449
  }
12207
12450
  const { act } = await inquirer3.prompt([{
12208
12451
  type: "list",
@@ -12222,11 +12465,11 @@ async function mappingsMenu(proj2) {
12222
12465
  ]);
12223
12466
  const entries2 = parseValue(a.entries);
12224
12467
  if (!Array.isArray(entries2)) {
12225
- console.log(import_chalk52.default.yellow(" Entries must be a JSON array \u2014 not created."));
12468
+ console.log(import_chalk53.default.yellow(" Entries must be a JSON array \u2014 not created."));
12226
12469
  continue;
12227
12470
  }
12228
12471
  await admin({ method: "POST", path: base2, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
12229
- console.log(import_chalk52.default.green(` Table "${a.name}" created.`));
12472
+ console.log(import_chalk53.default.green(` Table "${a.name}" created.`));
12230
12473
  } else {
12231
12474
  const { table } = await inquirer3.prompt([{
12232
12475
  type: "list",
@@ -12236,7 +12479,7 @@ async function mappingsMenu(proj2) {
12236
12479
  }]);
12237
12480
  if (!table) continue;
12238
12481
  await admin({ method: "DELETE", path: `${base2}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
12239
- console.log(import_chalk52.default.green(` ${table.name} deleted.`));
12482
+ console.log(import_chalk53.default.green(` ${table.name} deleted.`));
12240
12483
  }
12241
12484
  }
12242
12485
  }
@@ -12247,14 +12490,14 @@ async function tenantsMenu(proj2, opts) {
12247
12490
  const out = await admin({ method: "GET", path: base2, summary: "List attached tenants" });
12248
12491
  const tenants = out?.tenants ?? [];
12249
12492
  console.log();
12250
- if (!tenants.length) console.log(import_chalk52.default.dim(" No tenants attached (consumers use the default tenant)."));
12251
- for (const t of tenants) console.log(` ${import_chalk52.default.bold(t.tenant_name ?? t.name)} ${import_chalk52.default.dim(t.display_name ?? "")}`);
12493
+ if (!tenants.length) console.log(import_chalk53.default.dim(" No tenants attached (consumers use the default tenant)."));
12494
+ for (const t of tenants) console.log(` ${import_chalk53.default.bold(t.tenant_name ?? t.name)} ${import_chalk53.default.dim(t.display_name ?? "")}`);
12252
12495
  const { act } = await inquirer3.prompt([{
12253
12496
  type: "list",
12254
12497
  name: "act",
12255
12498
  message: "Tenants:",
12256
12499
  choices: [
12257
- { name: `Manage a tenant\u2026 ${import_chalk52.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
12500
+ { name: `Manage a tenant\u2026 ${import_chalk53.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
12258
12501
  { name: "Attach a tenant to this project", value: "attach" },
12259
12502
  ...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
12260
12503
  { name: "\u2190 Back", value: "back" }
@@ -12277,7 +12520,7 @@ async function tenantsMenu(proj2, opts) {
12277
12520
  }]);
12278
12521
  if (!t) continue;
12279
12522
  await admin({ method: "DELETE", path: `${base2}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
12280
- console.log(import_chalk52.default.green(` Detached ${t.tenant_name ?? t.name}.`));
12523
+ console.log(import_chalk53.default.green(` Detached ${t.tenant_name ?? t.name}.`));
12281
12524
  }
12282
12525
  }
12283
12526
  }
@@ -12320,7 +12563,7 @@ async function specMenu(proj2, opts) {
12320
12563
  choices: [
12321
12564
  { name: "Print the stored spec", value: "get" },
12322
12565
  { name: "Refresh the spec from its source", value: "refresh" },
12323
- { name: import_chalk52.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
12566
+ { name: import_chalk53.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
12324
12567
  { name: "\u2190 Back", value: "back" }
12325
12568
  ]
12326
12569
  }]);
@@ -12328,7 +12571,7 @@ async function specMenu(proj2, opts) {
12328
12571
  if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
12329
12572
  else if (act === "refresh") {
12330
12573
  await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
12331
- console.log(import_chalk52.default.green(" Spec refresh triggered."));
12574
+ console.log(import_chalk53.default.green(" Spec refresh triggered."));
12332
12575
  } else await runOpenapi(proj2.projectName, proj2.apiVersion);
12333
12576
  }
12334
12577
  async function agentsMenu(proj2, opts) {
@@ -12353,8 +12596,8 @@ async function agentsMenu(proj2, opts) {
12353
12596
  }
12354
12597
 
12355
12598
  // src/commands/group.ts
12356
- var import_chalk53 = __toESM(require("chalk"));
12357
- var import_ora24 = __toESM(require("ora"));
12599
+ var import_chalk54 = __toESM(require("chalk"));
12600
+ var import_ora25 = __toESM(require("ora"));
12358
12601
  init_auth();
12359
12602
  init_admin();
12360
12603
  init_resolve();
@@ -12454,7 +12697,7 @@ async function userByRefOrCreate(t, ref) {
12454
12697
  if (!ref.includes("@")) throw e;
12455
12698
  const created = await t.call("/v1/admin/users", "POST", { email: ref.trim().toLowerCase() });
12456
12699
  if (!created?.abz_sub) throw e;
12457
- console.log(import_chalk53.default.dim(` ${ref} wasn't a user yet \u2014 created.`));
12700
+ console.log(import_chalk54.default.dim(` ${ref} wasn't a user yet \u2014 created.`));
12458
12701
  return { abz_sub: created.abz_sub, email: created.email ?? ref, display_name: null };
12459
12702
  }
12460
12703
  }
@@ -12462,11 +12705,11 @@ async function runGroupList(opts) {
12462
12705
  const t = await resolveTransport(opts);
12463
12706
  const groups = await listGroups(t);
12464
12707
  if (opts.json) return void console.log(JSON.stringify(groups));
12465
- if (!groups.length) return void console.log(import_chalk53.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
12466
- console.log(import_chalk53.default.dim(`Tenant ${t.tenant}:`));
12708
+ if (!groups.length) return void console.log(import_chalk54.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
12709
+ console.log(import_chalk54.default.dim(`Tenant ${t.tenant}:`));
12467
12710
  for (const g of groups) {
12468
- const sealed = g.name === "apiblaze_admins" ? import_chalk53.default.dim(" (admins \u2014 managed by allowlist)") : "";
12469
- console.log(` ${import_chalk53.default.bold(g.name)} ${import_chalk53.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
12711
+ const sealed = g.name === "apiblaze_admins" ? import_chalk54.default.dim(" (admins \u2014 managed by allowlist)") : "";
12712
+ console.log(` ${import_chalk54.default.bold(g.name)} ${import_chalk54.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
12470
12713
  }
12471
12714
  }
12472
12715
  async function runGroupCreate(name, opts) {
@@ -12491,10 +12734,10 @@ async function runGroupCreate(name, opts) {
12491
12734
  }
12492
12735
  }
12493
12736
  }
12494
- const spinner = (0, import_ora24.default)(`Creating group ${name}...`).start();
12737
+ const spinner = (0, import_ora25.default)(`Creating group ${name}...`).start();
12495
12738
  try {
12496
12739
  await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
12497
- spinner.succeed(`Group ${import_chalk53.default.bold(name)} created in ${t.tenant}.`);
12740
+ spinner.succeed(`Group ${import_chalk54.default.bold(name)} created in ${t.tenant}.`);
12498
12741
  } catch (e) {
12499
12742
  spinner.fail("Create failed.");
12500
12743
  throw e;
@@ -12503,10 +12746,10 @@ async function runGroupCreate(name, opts) {
12503
12746
  async function runGroupDelete(name, opts) {
12504
12747
  const t = await resolveTransport(opts);
12505
12748
  const g = await groupByName(t, name);
12506
- const spinner = (0, import_ora24.default)(`Deleting group ${name}...`).start();
12749
+ const spinner = (0, import_ora25.default)(`Deleting group ${name}...`).start();
12507
12750
  try {
12508
12751
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
12509
- spinner.succeed(`Group ${import_chalk53.default.bold(name)} deleted.`);
12752
+ spinner.succeed(`Group ${import_chalk54.default.bold(name)} deleted.`);
12510
12753
  } catch (e) {
12511
12754
  spinner.fail("Delete failed.");
12512
12755
  throw e;
@@ -12525,21 +12768,21 @@ async function runGroupMembers(name, opts) {
12525
12768
  return void console.log(JSON.stringify({ ...detail, members: walkedMembers.items, subgroups: subs?.subgroups ?? [] }));
12526
12769
  }
12527
12770
  const members = walkedMembers.items;
12528
- console.log(import_chalk53.default.bold(`${g.name}`) + import_chalk53.default.dim(` (${t.tenant})`));
12529
- for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk53.default.cyan("group-admin") : import_chalk53.default.dim("member")}`);
12530
- if (!members.length) console.log(import_chalk53.default.dim(" no members"));
12771
+ console.log(import_chalk54.default.bold(`${g.name}`) + import_chalk54.default.dim(` (${t.tenant})`));
12772
+ for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk54.default.cyan("group-admin") : import_chalk54.default.dim("member")}`);
12773
+ if (!members.length) console.log(import_chalk54.default.dim(" no members"));
12531
12774
  const sg = subs?.subgroups ?? [];
12532
- if (sg.length) console.log(import_chalk53.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
12775
+ if (sg.length) console.log(import_chalk54.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
12533
12776
  }
12534
12777
  async function runGroupAddUser(userRef, groupName, opts) {
12535
12778
  const t = await resolveTransport(opts);
12536
12779
  const g = await groupByName(t, groupName);
12537
12780
  const u = await userByRefOrCreate(t, userRef);
12538
12781
  const role = opts.role === "admin" ? "admin" : "member";
12539
- const spinner = (0, import_ora24.default)(`Adding ${label2(u)} to ${g.name}...`).start();
12782
+ const spinner = (0, import_ora25.default)(`Adding ${label2(u)} to ${g.name}...`).start();
12540
12783
  try {
12541
12784
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
12542
- spinner.succeed(`${import_chalk53.default.bold(label2(u))} added to ${import_chalk53.default.bold(g.name)} as ${role}.`);
12785
+ spinner.succeed(`${import_chalk54.default.bold(label2(u))} added to ${import_chalk54.default.bold(g.name)} as ${role}.`);
12543
12786
  } catch (e) {
12544
12787
  spinner.fail("Add failed.");
12545
12788
  throw e;
@@ -12548,10 +12791,10 @@ async function runGroupAddUser(userRef, groupName, opts) {
12548
12791
  async function runGroupRemoveUser(userRef, groupName, opts) {
12549
12792
  const t = await resolveTransport(opts);
12550
12793
  const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
12551
- const spinner = (0, import_ora24.default)(`Removing ${label2(u)} from ${g.name}...`).start();
12794
+ const spinner = (0, import_ora25.default)(`Removing ${label2(u)} from ${g.name}...`).start();
12552
12795
  try {
12553
12796
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
12554
- spinner.succeed(`${import_chalk53.default.bold(label2(u))} removed from ${import_chalk53.default.bold(g.name)}.`);
12797
+ spinner.succeed(`${import_chalk54.default.bold(label2(u))} removed from ${import_chalk54.default.bold(g.name)}.`);
12555
12798
  } catch (e) {
12556
12799
  spinner.fail("Remove failed.");
12557
12800
  throw e;
@@ -12560,10 +12803,10 @@ async function runGroupRemoveUser(userRef, groupName, opts) {
12560
12803
  async function runGroupAddGroup(childName, parentName, opts) {
12561
12804
  const t = await resolveTransport(opts);
12562
12805
  const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
12563
- const spinner = (0, import_ora24.default)(`Nesting ${child.name} under ${parent.name}...`).start();
12806
+ const spinner = (0, import_ora25.default)(`Nesting ${child.name} under ${parent.name}...`).start();
12564
12807
  try {
12565
12808
  await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
12566
- spinner.succeed(`${import_chalk53.default.bold(child.name)} is now a subgroup of ${import_chalk53.default.bold(parent.name)}.`);
12809
+ spinner.succeed(`${import_chalk54.default.bold(child.name)} is now a subgroup of ${import_chalk54.default.bold(parent.name)}.`);
12567
12810
  } catch (e) {
12568
12811
  spinner.fail("Nest failed.");
12569
12812
  throw e;
@@ -12572,10 +12815,10 @@ async function runGroupAddGroup(childName, parentName, opts) {
12572
12815
  async function runGroupRemoveGroup(childName, parentName, opts) {
12573
12816
  const t = await resolveTransport(opts);
12574
12817
  const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
12575
- const spinner = (0, import_ora24.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
12818
+ const spinner = (0, import_ora25.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
12576
12819
  try {
12577
12820
  await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
12578
- spinner.succeed(`${import_chalk53.default.bold(child.name)} un-nested from ${import_chalk53.default.bold(parent.name)}.`);
12821
+ spinner.succeed(`${import_chalk54.default.bold(child.name)} un-nested from ${import_chalk54.default.bold(parent.name)}.`);
12579
12822
  } catch (e) {
12580
12823
  spinner.fail("Un-nest failed.");
12581
12824
  throw e;
@@ -12586,13 +12829,13 @@ async function runGroupUsers(opts) {
12586
12829
  const users = await listUsers(t);
12587
12830
  const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
12588
12831
  if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
12589
- console.log(import_chalk53.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
12590
- for (const u of users) console.log(` ${label2(u)} ${import_chalk53.default.dim(u.abz_sub)}`);
12591
- if (!users.length) console.log(import_chalk53.default.dim(" no users yet"));
12832
+ console.log(import_chalk54.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
12833
+ for (const u of users) console.log(` ${label2(u)} ${import_chalk54.default.dim(u.abz_sub)}`);
12834
+ if (!users.length) console.log(import_chalk54.default.dim(" no users yet"));
12592
12835
  const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
12593
12836
  if (fresh.length) {
12594
- console.log(import_chalk53.default.dim("Seen in traffic (not yet users):"));
12595
- for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk53.default.dim(o.source_label ?? "")}`);
12837
+ console.log(import_chalk54.default.dim("Seen in traffic (not yet users):"));
12838
+ for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk54.default.dim(o.source_label ?? "")}`);
12596
12839
  }
12597
12840
  }
12598
12841
 
@@ -12601,22 +12844,22 @@ init_iam();
12601
12844
  init_admins();
12602
12845
 
12603
12846
  // src/commands/preapprove.ts
12604
- var import_chalk54 = __toESM(require("chalk"));
12605
- var import_ora25 = __toESM(require("ora"));
12847
+ var import_chalk55 = __toESM(require("chalk"));
12848
+ var import_ora26 = __toESM(require("ora"));
12606
12849
  async function runPreapprove(who, opts) {
12607
12850
  const tenant2 = await resolveProducerTenant(opts.tenant);
12608
12851
  if (opts.list) {
12609
12852
  const rules = await listPreapprovalRules(tenant2);
12610
12853
  if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
12611
12854
  if (!rules.length) {
12612
- console.log(import_chalk54.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
12613
- console.log(import_chalk54.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
12855
+ console.log(import_chalk55.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
12856
+ console.log(import_chalk55.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
12614
12857
  return;
12615
12858
  }
12616
- console.log(import_chalk54.default.dim(`Pre-approved for ${import_chalk54.default.bold(tenant2)}:`));
12859
+ console.log(import_chalk55.default.dim(`Pre-approved for ${import_chalk55.default.bold(tenant2)}:`));
12617
12860
  for (const r of rules) {
12618
- const tag = r.kind === "domain" ? import_chalk54.default.cyan("@" + r.value + " (whole domain)") : r.value;
12619
- const grp = r.groups?.length ? import_chalk54.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
12861
+ const tag = r.kind === "domain" ? import_chalk55.default.cyan("@" + r.value + " (whole domain)") : r.value;
12862
+ const grp = r.groups?.length ? import_chalk55.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
12620
12863
  console.log(` ${tag}${grp}`);
12621
12864
  }
12622
12865
  return;
@@ -12625,19 +12868,19 @@ async function runPreapprove(who, opts) {
12625
12868
  throw new Error("Who? Pass an email or a domain: `apiblaze preapprove someone@acme.com` (or `apiblaze preapprove --list`).");
12626
12869
  }
12627
12870
  if (opts.remove) {
12628
- const spinner2 = (0, import_ora25.default)(`Removing ${who} from ${tenant2}\u2026`).start();
12871
+ const spinner2 = (0, import_ora26.default)(`Removing ${who} from ${tenant2}\u2026`).start();
12629
12872
  const { removed, value } = await removePreapprovalRule(tenant2, who);
12630
- if (removed) spinner2.succeed(`${import_chalk54.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
12631
- else spinner2.warn(`No pre-approval rule for ${import_chalk54.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
12873
+ if (removed) spinner2.succeed(`${import_chalk55.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
12874
+ else spinner2.warn(`No pre-approval rule for ${import_chalk55.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
12632
12875
  return;
12633
12876
  }
12634
- const spinner = (0, import_ora25.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
12877
+ const spinner = (0, import_ora26.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
12635
12878
  try {
12636
12879
  const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
12637
12880
  const what = kind === "domain" ? `Anyone @${value}` : value;
12638
- spinner.succeed(`${import_chalk54.default.bold(what)} can now sign in to ${import_chalk54.default.bold(tenant2)}.`);
12639
- if (opts.group?.length) console.log(import_chalk54.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
12640
- console.log(import_chalk54.default.dim(` See the full list: apiblaze preapprove --list`));
12881
+ spinner.succeed(`${import_chalk55.default.bold(what)} can now sign in to ${import_chalk55.default.bold(tenant2)}.`);
12882
+ if (opts.group?.length) console.log(import_chalk55.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
12883
+ console.log(import_chalk55.default.dim(` See the full list: apiblaze preapprove --list`));
12641
12884
  } catch (err) {
12642
12885
  spinner.fail("Could not add the rule.");
12643
12886
  throw err;
@@ -12645,8 +12888,8 @@ async function runPreapprove(who, opts) {
12645
12888
  }
12646
12889
 
12647
12890
  // src/commands/consumer.ts
12648
- var import_chalk55 = __toESM(require("chalk"));
12649
- var import_ora26 = __toESM(require("ora"));
12891
+ var import_chalk56 = __toESM(require("chalk"));
12892
+ var import_ora27 = __toESM(require("ora"));
12650
12893
  init_admin();
12651
12894
  init_resolve();
12652
12895
  init_caller();
@@ -12668,7 +12911,7 @@ async function consumerFetch(creds, suffix, init) {
12668
12911
  function requireConsumer2() {
12669
12912
  const c = loadConsumer();
12670
12913
  if (!c) {
12671
- console.error(import_chalk55.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
12914
+ console.error(import_chalk56.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
12672
12915
  process.exit(1);
12673
12916
  }
12674
12917
  return c;
@@ -12679,7 +12922,7 @@ async function runConsumerLogin(opts) {
12679
12922
  let clientId = opts.client;
12680
12923
  if (clientId) {
12681
12924
  if (!tenant2) {
12682
- console.error(import_chalk55.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
12925
+ console.error(import_chalk56.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
12683
12926
  process.exit(1);
12684
12927
  }
12685
12928
  } else {
@@ -12691,24 +12934,24 @@ async function runConsumerLogin(opts) {
12691
12934
  if (!picked) process.exit(1);
12692
12935
  tenant2 = picked;
12693
12936
  }
12694
- const s2 = (0, import_ora26.default)("Finding the login app...").start();
12937
+ const s2 = (0, import_ora27.default)("Finding the login app...").start();
12695
12938
  const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
12696
12939
  s2.stop();
12697
12940
  const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
12698
12941
  const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
12699
12942
  if (!pick2) {
12700
- console.error(import_chalk55.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
12943
+ console.error(import_chalk56.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
12701
12944
  process.exit(1);
12702
12945
  }
12703
12946
  clientId = pick2.client_id ?? pick2.clientId;
12704
12947
  }
12705
- console.log(`${import_chalk55.default.cyan("\u2192")} Logging in to ${import_chalk55.default.bold(tenant2)} as a consumer...`);
12948
+ console.log(`${import_chalk56.default.cyan("\u2192")} Logging in to ${import_chalk56.default.bold(tenant2)} as a consumer...`);
12706
12949
  const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
12707
12950
  console.log(`
12708
- Open: ${import_chalk55.default.underline(verificationUri)}`);
12709
- console.log(` Code: ${import_chalk55.default.bold(userCode)}
12951
+ Open: ${import_chalk56.default.underline(verificationUri)}`);
12952
+ console.log(` Code: ${import_chalk56.default.bold(userCode)}
12710
12953
  `);
12711
- console.log(import_chalk55.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
12954
+ console.log(import_chalk56.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
12712
12955
  });
12713
12956
  const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
12714
12957
  const creds = {
@@ -12723,7 +12966,7 @@ async function runConsumerLogin(opts) {
12723
12966
  obtainedAt: Date.now()
12724
12967
  };
12725
12968
  saveConsumer(creds);
12726
- console.log(import_chalk55.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
12969
+ console.log(import_chalk56.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
12727
12970
  }
12728
12971
  async function runConsumerTokens(opts) {
12729
12972
  const creds = requireConsumer2();
@@ -12736,28 +12979,28 @@ async function runConsumerTokens(opts) {
12736
12979
  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));
12737
12980
  return;
12738
12981
  }
12739
- console.log(`${import_chalk55.default.cyan("Consumer")} ${import_chalk55.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk55.default.bold(fresh.tenant)}
12982
+ console.log(`${import_chalk56.default.cyan("Consumer")} ${import_chalk56.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk56.default.bold(fresh.tenant)}
12740
12983
  `);
12741
- console.log(`${import_chalk55.default.bold("access_token")} ${import_chalk55.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
12984
+ console.log(`${import_chalk56.default.bold("access_token")} ${import_chalk56.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
12742
12985
  ${fresh.accessToken}
12743
12986
  `);
12744
- if (fresh.idToken) console.log(`${import_chalk55.default.bold("id_token")} ${import_chalk55.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
12987
+ if (fresh.idToken) console.log(`${import_chalk56.default.bold("id_token")} ${import_chalk56.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
12745
12988
  ${fresh.idToken}
12746
12989
  `);
12747
- if (fresh.refreshToken) console.log(`${import_chalk55.default.bold("refresh_token")}
12990
+ if (fresh.refreshToken) console.log(`${import_chalk56.default.bold("refresh_token")}
12748
12991
  ${fresh.refreshToken}
12749
12992
  `);
12750
- console.log(import_chalk55.default.dim("These are your own tokens \u2014 keep them secret."));
12993
+ console.log(import_chalk56.default.dim("These are your own tokens \u2014 keep them secret."));
12751
12994
  }
12752
12995
  async function runConsumerApikeys(opts) {
12753
12996
  const creds = requireConsumer2();
12754
12997
  const { default: inquirer3 } = await import("inquirer");
12755
- const spinner = (0, import_ora26.default)("Loading your API keys...").start();
12998
+ const spinner = (0, import_ora27.default)("Loading your API keys...").start();
12756
12999
  const list = await consumerFetch(creds, withPage("/apikeys", null));
12757
13000
  if (list.status >= 400) {
12758
13001
  spinner.stop();
12759
- console.error(import_chalk55.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
12760
- if (list.status === 401) console.error(import_chalk55.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
13002
+ console.error(import_chalk56.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
13003
+ if (list.status === 401) console.error(import_chalk56.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
12761
13004
  process.exit(1);
12762
13005
  }
12763
13006
  let walkCreds = list.creds;
@@ -12786,16 +13029,16 @@ async function runConsumerApikeys(opts) {
12786
13029
  if (opts.json) {
12787
13030
  console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
12788
13031
  } else if (!keys.length) {
12789
- console.log(import_chalk55.default.yellow("No API keys yet."));
13032
+ console.log(import_chalk56.default.yellow("No API keys yet."));
12790
13033
  } else {
12791
13034
  for (const k of keys) {
12792
13035
  const clear = revealMap[k.environment]?.key;
12793
- const shown = clear ? import_chalk55.default.green(clear) : import_chalk55.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
12794
- const exp = k.expires_at ? import_chalk55.default.dim(`exp ${k.expires_at}`) : import_chalk55.default.dim("no expiry");
12795
- console.log(` ${import_chalk55.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk55.default.dim(k.description ?? "")}`);
13036
+ const shown = clear ? import_chalk56.default.green(clear) : import_chalk56.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
13037
+ const exp = k.expires_at ? import_chalk56.default.dim(`exp ${k.expires_at}`) : import_chalk56.default.dim("no expiry");
13038
+ console.log(` ${import_chalk56.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk56.default.dim(k.description ?? "")}`);
12796
13039
  }
12797
13040
  if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
12798
- console.log(import_chalk55.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
13041
+ console.log(import_chalk56.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
12799
13042
  }
12800
13043
  }
12801
13044
  if (opts.json) return;
@@ -12809,7 +13052,7 @@ async function runConsumerApikeys(opts) {
12809
13052
  const body = { environment: answers.environment };
12810
13053
  if (answers.description) body.description = answers.description;
12811
13054
  if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
12812
- const s2 = (0, import_ora26.default)("Creating key...").start();
13055
+ const s2 = (0, import_ora27.default)("Creating key...").start();
12813
13056
  const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
12814
13057
  if (created.status >= 400) {
12815
13058
  s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
@@ -12817,13 +13060,13 @@ async function runConsumerApikeys(opts) {
12817
13060
  }
12818
13061
  s2.succeed("Key created.");
12819
13062
  const key = created.data?.key ?? created.data?.fullKey;
12820
- if (key) console.log(` ${import_chalk55.default.green(key)} ${import_chalk55.default.dim("(shown once \u2014 store it now)")}`);
12821
- else console.log(import_chalk55.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
13063
+ if (key) console.log(` ${import_chalk56.default.green(key)} ${import_chalk56.default.dim("(shown once \u2014 store it now)")}`);
13064
+ else console.log(import_chalk56.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
12822
13065
  }
12823
13066
 
12824
13067
  // src/commands/sidecar.ts
12825
- var import_chalk56 = __toESM(require("chalk"));
12826
- var import_ora27 = __toESM(require("ora"));
13068
+ var import_chalk57 = __toESM(require("chalk"));
13069
+ var import_ora28 = __toESM(require("ora"));
12827
13070
  var fs13 = __toESM(require("fs"));
12828
13071
  var path10 = __toESM(require("path"));
12829
13072
  init_admin();
@@ -12865,18 +13108,18 @@ function upsertEnvLocal(root, token) {
12865
13108
  }
12866
13109
  function installSidecarPackage(root) {
12867
13110
  if (fs13.existsSync(path10.join(root, "node_modules", "apiblaze", "package.json"))) {
12868
- console.log(` ${import_chalk56.default.green("\u2713")} apiblaze package already installed`);
13111
+ console.log(` ${import_chalk57.default.green("\u2713")} apiblaze package already installed`);
12869
13112
  return;
12870
13113
  }
12871
13114
  const has = (f) => fs13.existsSync(path10.join(root, f));
12872
13115
  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" };
12873
- const spinner = (0, import_ora27.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
13116
+ const spinner = (0, import_ora28.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
12874
13117
  try {
12875
13118
  const { execSync } = require("child_process");
12876
13119
  execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
12877
13120
  spinner.succeed("Installed apiblaze (the sidecar runtime).");
12878
13121
  } catch {
12879
- spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk56.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk56.default.cyan("npm run dev")}.`);
13122
+ spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk57.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk57.default.cyan("npm run dev")}.`);
12880
13123
  }
12881
13124
  }
12882
13125
  function readEnvKey(root) {
@@ -13015,7 +13258,7 @@ async function runAnonymousInit(root, router, opts) {
13015
13258
  const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
13016
13259
  const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
13017
13260
  if (opts.newSession) clearAnonCred2();
13018
- const spinner = (0, import_ora27.default)("Setting up a sidecar (no login needed)...").start();
13261
+ const spinner = (0, import_ora28.default)("Setting up a sidecar (no login needed)...").start();
13019
13262
  let out;
13020
13263
  try {
13021
13264
  out = await sidecarInitAnonymous2();
@@ -13027,29 +13270,29 @@ async function runAnonymousInit(root, router, opts) {
13027
13270
  if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
13028
13271
  const envState = upsertEnvLocal(root, out.token);
13029
13272
  ensureGitignored(root);
13030
- console.log(` ${import_chalk56.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
13031
- console.log(` ${import_chalk56.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
13273
+ console.log(` ${import_chalk57.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
13274
+ console.log(` ${import_chalk57.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
13032
13275
  installSidecarPackage(root);
13033
13276
  let inspectorPath = null;
13034
13277
  if (!opts.noInspector) {
13035
13278
  inspectorPath = generateInspector(root, router);
13036
- if (inspectorPath) console.log(` ${import_chalk56.default.green("\u2713")} inspector at ${inspectorPath}`);
13279
+ if (inspectorPath) console.log(` ${import_chalk57.default.green("\u2713")} inspector at ${inspectorPath}`);
13037
13280
  }
13038
13281
  console.log("");
13039
- console.log(import_chalk56.default.bold("Done (no account needed). What happens next:"));
13040
- console.log(` 1. ${import_chalk56.default.cyan("npm run dev")} and use your app.`);
13282
+ console.log(import_chalk57.default.bold("Done (no account needed). What happens next:"));
13283
+ console.log(` 1. ${import_chalk57.default.cyan("npm run dev")} and use your app.`);
13041
13284
  console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
13042
- console.log(` ${import_chalk56.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
13285
+ console.log(` ${import_chalk57.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
13043
13286
  console.log("");
13044
- console.log(import_chalk56.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
13045
- console.log(` ${import_chalk56.default.cyan("apiblaze login")} then ${import_chalk56.default.cyan("apiblaze claim")} ${import_chalk56.default.dim("(no code needed here)")}`);
13046
- console.log(import_chalk56.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
13287
+ console.log(import_chalk57.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
13288
+ console.log(` ${import_chalk57.default.cyan("apiblaze login")} then ${import_chalk57.default.cyan("apiblaze claim")} ${import_chalk57.default.dim("(no code needed here)")}`);
13289
+ console.log(import_chalk57.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
13047
13290
  }
13048
13291
  async function runSidecar(opts) {
13049
13292
  const root = path10.resolve(opts.dir ?? process.cwd());
13050
13293
  const detected2 = detectNextProject(root);
13051
13294
  if (!detected2.found) {
13052
- console.log(import_chalk56.default.yellow(`No Next.js project detected in ${root}.`));
13295
+ console.log(import_chalk57.default.yellow(`No Next.js project detected in ${root}.`));
13053
13296
  console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
13054
13297
  return;
13055
13298
  }
@@ -13060,10 +13303,10 @@ async function runSidecar(opts) {
13060
13303
  if (!loadCredentials()) {
13061
13304
  upsertEnvLocal(root, readEnvKey(root));
13062
13305
  ensureGitignored(root);
13063
- console.log(` ${import_chalk56.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
13064
- console.log(` ${import_chalk56.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
13306
+ console.log(` ${import_chalk57.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
13307
+ console.log(` ${import_chalk57.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
13065
13308
  installSidecarPackage(root);
13066
- console.log(import_chalk56.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
13309
+ console.log(import_chalk57.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
13067
13310
  return;
13068
13311
  }
13069
13312
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -13072,7 +13315,7 @@ async function runSidecar(opts) {
13072
13315
  const mustMint = !existingKey || opts.rotate || switchingTeam;
13073
13316
  let token = existingKey ?? "";
13074
13317
  if (mustMint) {
13075
- const spinner = (0, import_ora27.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
13318
+ const spinner = (0, import_ora28.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
13076
13319
  try {
13077
13320
  const out = await admin({
13078
13321
  method: "POST",
@@ -13086,39 +13329,39 @@ async function runSidecar(opts) {
13086
13329
  throw err;
13087
13330
  }
13088
13331
  } else {
13089
- console.log(import_chalk56.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
13332
+ console.log(import_chalk57.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
13090
13333
  }
13091
13334
  const envState = upsertEnvLocal(root, token);
13092
13335
  ensureGitignored(root);
13093
- console.log(` ${import_chalk56.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
13336
+ console.log(` ${import_chalk57.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
13094
13337
  const wireState = wireInstrumentation(root);
13095
- console.log(` ${import_chalk56.default.green("\u2713")} instrumentation.ts ${wireState}`);
13338
+ console.log(` ${import_chalk57.default.green("\u2713")} instrumentation.ts ${wireState}`);
13096
13339
  installSidecarPackage(root);
13097
13340
  let inspectorPath = null;
13098
13341
  if (!opts.noInspector) {
13099
13342
  inspectorPath = generateInspector(root, detected2.router);
13100
- if (inspectorPath) console.log(` ${import_chalk56.default.green("\u2713")} inspector at ${inspectorPath}`);
13343
+ if (inspectorPath) console.log(` ${import_chalk57.default.green("\u2713")} inspector at ${inspectorPath}`);
13101
13344
  }
13102
13345
  console.log("");
13103
- console.log(import_chalk56.default.bold("Done. What happens next:"));
13104
- console.log(` 1. ${import_chalk56.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
13105
- console.log(` 2. The origins your app calls appear as ${import_chalk56.default.bold("candidates")} \u2014 list them: ${import_chalk56.default.cyan("apiblaze sidecar")}`);
13106
- console.log(` 3. Approve the ones to route: ${import_chalk56.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
13346
+ console.log(import_chalk57.default.bold("Done. What happens next:"));
13347
+ console.log(` 1. ${import_chalk57.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
13348
+ console.log(` 2. The origins your app calls appear as ${import_chalk57.default.bold("candidates")} \u2014 list them: ${import_chalk57.default.cyan("apiblaze sidecar")}`);
13349
+ console.log(` 3. Approve the ones to route: ${import_chalk57.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
13107
13350
  console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
13108
- if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk56.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path10.dirname(inspectorPath)} before shipping)`);
13109
- if (switchingTeam) console.log(import_chalk56.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
13351
+ if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk57.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path10.dirname(inspectorPath)} before shipping)`);
13352
+ if (switchingTeam) console.log(import_chalk57.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
13110
13353
  console.log("");
13111
- console.log(import_chalk56.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
13112
- console.log(import_chalk56.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
13113
- console.log(import_chalk56.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
13354
+ console.log(import_chalk57.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
13355
+ console.log(import_chalk57.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
13356
+ console.log(import_chalk57.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
13114
13357
  console.log("");
13115
- console.log(import_chalk56.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
13116
- console.log(import_chalk56.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
13358
+ console.log(import_chalk57.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
13359
+ console.log(import_chalk57.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
13117
13360
  }
13118
13361
 
13119
13362
  // src/commands/origins.ts
13120
- var import_chalk57 = __toESM(require("chalk"));
13121
- var import_ora28 = __toESM(require("ora"));
13363
+ var import_chalk58 = __toESM(require("chalk"));
13364
+ var import_ora29 = __toESM(require("ora"));
13122
13365
  init_admin();
13123
13366
  init_resolve();
13124
13367
  init_auth();
@@ -13128,7 +13371,7 @@ async function runOriginsList(opts) {
13128
13371
  if (!loadCredentials()) {
13129
13372
  const cred = loadAnonCred();
13130
13373
  if (!cred) {
13131
- console.log(import_chalk57.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
13374
+ console.log(import_chalk58.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
13132
13375
  return;
13133
13376
  }
13134
13377
  out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
@@ -13146,30 +13389,30 @@ async function runOriginsList(opts) {
13146
13389
  }
13147
13390
  const routed = out.routed ?? [];
13148
13391
  const candidates = out.candidates ?? [];
13149
- console.log(import_chalk57.default.bold(`
13392
+ console.log(import_chalk58.default.bold(`
13150
13393
  Routed through APIblaze (${routed.length})`));
13151
- if (!routed.length) console.log(import_chalk57.default.dim(" none yet"));
13152
- for (const r of routed) console.log(` ${import_chalk57.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk57.default.dim(`\u2192 ${r.project_id}`)}`);
13153
- console.log(import_chalk57.default.bold(`
13394
+ if (!routed.length) console.log(import_chalk58.default.dim(" none yet"));
13395
+ for (const r of routed) console.log(` ${import_chalk58.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk58.default.dim(`\u2192 ${r.project_id}`)}`);
13396
+ console.log(import_chalk58.default.bold(`
13154
13397
  Candidates \u2014 going direct, not yet approved (${candidates.length})`));
13155
- if (!candidates.length) console.log(import_chalk57.default.dim(" none \u2014 run your app to discover the origins it calls"));
13398
+ if (!candidates.length) console.log(import_chalk58.default.dim(" none \u2014 run your app to discover the origins it calls"));
13156
13399
  for (const c of candidates) {
13157
- console.log(` ${import_chalk57.default.yellow("\u25CB")} ${c.origin} ${import_chalk57.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
13400
+ console.log(` ${import_chalk58.default.yellow("\u25CB")} ${c.origin} ${import_chalk58.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
13158
13401
  }
13159
13402
  if (candidates.length) {
13160
- console.log(import_chalk57.default.dim(`
13403
+ console.log(import_chalk58.default.dim(`
13161
13404
  Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
13162
- console.log(import_chalk57.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
13405
+ console.log(import_chalk58.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
13163
13406
  }
13164
13407
  }
13165
13408
  async function runOriginsApprove(origin, opts) {
13166
13409
  if (!loadCredentials()) {
13167
13410
  const cred = loadAnonCred();
13168
13411
  if (!cred) {
13169
- console.error(import_chalk57.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
13412
+ console.error(import_chalk58.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
13170
13413
  process.exit(1);
13171
13414
  }
13172
- const spinner2 = (0, import_ora28.default)(`Approving ${origin} (anonymous)...`).start();
13415
+ const spinner2 = (0, import_ora29.default)(`Approving ${origin} (anonymous)...`).start();
13173
13416
  try {
13174
13417
  const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
13175
13418
  spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
@@ -13180,7 +13423,7 @@ async function runOriginsApprove(origin, opts) {
13180
13423
  return;
13181
13424
  }
13182
13425
  const { teamId } = await resolveTeam(opts.team);
13183
- const spinner = (0, import_ora28.default)(`Approving ${origin}...`).start();
13426
+ const spinner = (0, import_ora29.default)(`Approving ${origin}...`).start();
13184
13427
  try {
13185
13428
  const out = await admin({
13186
13429
  method: "POST",
@@ -13197,7 +13440,7 @@ async function runOriginsApprove(origin, opts) {
13197
13440
  }
13198
13441
  async function runOriginsDeny(origin, opts) {
13199
13442
  const { teamId } = await resolveTeam(opts.team);
13200
- const spinner = (0, import_ora28.default)(`Dismissing ${origin}...`).start();
13443
+ const spinner = (0, import_ora29.default)(`Dismissing ${origin}...`).start();
13201
13444
  try {
13202
13445
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
13203
13446
  spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
@@ -13208,7 +13451,7 @@ async function runOriginsDeny(origin, opts) {
13208
13451
  }
13209
13452
  async function runOriginsRemove(origin, opts) {
13210
13453
  const { teamId } = await resolveTeam(opts.team);
13211
- const spinner = (0, import_ora28.default)(`Removing the proxy for ${origin}...`).start();
13454
+ const spinner = (0, import_ora29.default)(`Removing the proxy for ${origin}...`).start();
13212
13455
  try {
13213
13456
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
13214
13457
  spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
@@ -13219,7 +13462,7 @@ async function runOriginsRemove(origin, opts) {
13219
13462
  }
13220
13463
 
13221
13464
  // src/commands/op.ts
13222
- var import_chalk62 = __toESM(require("chalk"));
13465
+ var import_chalk63 = __toESM(require("chalk"));
13223
13466
  init_auth();
13224
13467
 
13225
13468
  // src/lib/op-call.ts
@@ -13248,7 +13491,7 @@ async function opCall(call2) {
13248
13491
  }
13249
13492
 
13250
13493
  // src/commands/op-billing.ts
13251
- var import_chalk58 = __toESM(require("chalk"));
13494
+ var import_chalk59 = __toESM(require("chalk"));
13252
13495
  init_admin();
13253
13496
  var SANDBOX = {
13254
13497
  teamId: "team_1782844865835_zujrf",
@@ -13307,7 +13550,7 @@ async function rowsForRays(project, version2, tenant2, rays) {
13307
13550
  }
13308
13551
  function printDoors(data) {
13309
13552
  const checks = [];
13310
- console.log(import_chalk58.default.bold("\n Doors \u2014 is every way in metered?\n"));
13553
+ console.log(import_chalk59.default.bold("\n Doors \u2014 is every way in metered?\n"));
13311
13554
  const doors = data?.doors ?? [];
13312
13555
  const metered = doors.filter((d) => d.verdict === "metered");
13313
13556
  const allowed = doors.filter((d) => d.verdict === "allowed-free");
@@ -13316,29 +13559,29 @@ function printDoors(data) {
13316
13559
  const errs = data?.errors ?? [];
13317
13560
  const routeAuditBroke = errs.some((e) => e.startsWith("zone "));
13318
13561
  const devAuditBroke = errs.some((e) => e.startsWith("workers.dev audit"));
13319
- console.log(import_chalk58.default.dim(` ${metered.length} route(s) behind main-proxy (metered)`));
13320
- for (const d of metered) console.log(import_chalk58.default.green(` \u2713 ${d.pattern}`));
13321
- console.log(import_chalk58.default.dim(`
13562
+ console.log(import_chalk59.default.dim(` ${metered.length} route(s) behind main-proxy (metered)`));
13563
+ for (const d of metered) console.log(import_chalk59.default.green(` \u2713 ${d.pattern}`));
13564
+ console.log(import_chalk59.default.dim(`
13322
13565
  ${allowed.length} route(s) free ON PURPOSE`));
13323
13566
  for (const d of allowed) {
13324
- console.log(import_chalk58.default.cyan(` \u2022 ${d.pattern}`) + import_chalk58.default.dim(` \u2192 ${d.script}`));
13325
- console.log(import_chalk58.default.dim(` ${d.why}`));
13567
+ console.log(import_chalk59.default.cyan(` \u2022 ${d.pattern}`) + import_chalk59.default.dim(` \u2192 ${d.script}`));
13568
+ console.log(import_chalk59.default.dim(` ${d.why}`));
13326
13569
  }
13327
13570
  if (known.length) {
13328
- console.log(import_chalk58.default.yellow(`
13571
+ console.log(import_chalk59.default.yellow(`
13329
13572
  ${known.length} route(s) KNOWN OPEN \u2014 unmetered, not yet closed`));
13330
13573
  for (const d of known) {
13331
- console.log(import_chalk58.default.yellow(` ! ${d.pattern}`) + import_chalk58.default.dim(` \u2192 ${d.script}`));
13332
- console.log(import_chalk58.default.dim(` ${d.why}`));
13574
+ console.log(import_chalk59.default.yellow(` ! ${d.pattern}`) + import_chalk59.default.dim(` \u2192 ${d.script}`));
13575
+ console.log(import_chalk59.default.dim(` ${d.why}`));
13333
13576
  }
13334
13577
  checks.push({ name: "no known-open doors", status: "KNOWN", detail: `${known.length} unmetered route(s) still open \u2014 see above` });
13335
13578
  }
13336
13579
  if (stray.length) {
13337
- console.log(import_chalk58.default.red(`
13580
+ console.log(import_chalk59.default.red(`
13338
13581
  ${stray.length} STRAY route(s) \u2014 not main-proxy, not on the allowlist`));
13339
13582
  for (const d of stray) {
13340
- console.log(import_chalk58.default.red(` \u2717 ${d.pattern}`) + import_chalk58.default.dim(` \u2192 ${d.script}`));
13341
- console.log(import_chalk58.default.dim(` ${d.why}`));
13583
+ console.log(import_chalk59.default.red(` \u2717 ${d.pattern}`) + import_chalk59.default.dim(` \u2192 ${d.script}`));
13584
+ console.log(import_chalk59.default.dim(` ${d.why}`));
13342
13585
  }
13343
13586
  checks.push({ name: "no stray routes", status: "FAIL", detail: `${stray.length}: ${stray.map((s) => s.pattern).join(", ")}` });
13344
13587
  } else if (routeAuditBroke) {
@@ -13349,22 +13592,22 @@ function printDoors(data) {
13349
13592
  const wd = data?.workers_dev ?? {};
13350
13593
  const open = wd.enabled ?? [];
13351
13594
  if (open.length) {
13352
- console.log(import_chalk58.default.red(`
13595
+ console.log(import_chalk59.default.red(`
13353
13596
  ${open.length} of ${wd.total} worker(s) reachable on *.workers.dev`));
13354
- for (const s of open) console.log(import_chalk58.default.red(` \u2717 ${s.script}.workers.dev`) + import_chalk58.default.dim(` (enabled=${s.enabled} previews=${s.previews})`));
13355
- console.log(import_chalk58.default.dim(" A workers.dev hostname bypasses every CF route, WAF rule and the credit gate."));
13597
+ for (const s of open) console.log(import_chalk59.default.red(` \u2717 ${s.script}.workers.dev`) + import_chalk59.default.dim(` (enabled=${s.enabled} previews=${s.previews})`));
13598
+ console.log(import_chalk59.default.dim(" A workers.dev hostname bypasses every CF route, WAF rule and the credit gate."));
13356
13599
  checks.push({ name: "no workers.dev doors", status: "FAIL", detail: `${open.length} script(s) publicly reachable: ${open.map((s) => s.script).join(", ")}` });
13357
13600
  } else if (devAuditBroke || !wd.total) {
13358
13601
  checks.push({ name: "no workers.dev doors", status: "SKIP", detail: "script enumeration failed \u2014 NOT a pass, no subdomain was ever read. Needs a CF token with Workers Scripts:Read." });
13359
13602
  } else {
13360
- console.log(import_chalk58.default.green(`
13603
+ console.log(import_chalk59.default.green(`
13361
13604
  \u2713 0 of ${wd.total} workers reachable on *.workers.dev`));
13362
13605
  checks.push({ name: "no workers.dev doors", status: "PASS", detail: `all ${wd.total} scripts have workers.dev + previews disabled` });
13363
13606
  }
13364
- if (data?.how_to_fix) console.log(import_chalk58.default.yellow(`
13365
- ${data.reason}`) + import_chalk58.default.dim(`
13607
+ if (data?.how_to_fix) console.log(import_chalk59.default.yellow(`
13608
+ ${data.reason}`) + import_chalk59.default.dim(`
13366
13609
  ${data.how_to_fix}`));
13367
- else for (const e of errs) console.log(import_chalk58.default.red(`
13610
+ else for (const e of errs) console.log(import_chalk59.default.red(`
13368
13611
  audit error: ${e}`));
13369
13612
  if (errs.length) {
13370
13613
  checks.push({ name: "audit completeness", status: "FAIL", detail: `${errs.length} part(s) of the audit could not run \u2014 coverage is INCOMPLETE, and the checks they would have covered are SKIP above` });
@@ -13374,9 +13617,9 @@ function printDoors(data) {
13374
13617
  async function runMeter(readLedger, opts) {
13375
13618
  const checks = [];
13376
13619
  const N = Math.max(1, Math.min(10, opts.count ?? 3));
13377
- console.log(import_chalk58.default.bold("\n Meter \u2014 is 1 request charged exactly 1 request?\n"));
13620
+ console.log(import_chalk59.default.bold("\n Meter \u2014 is 1 request charged exactly 1 request?\n"));
13378
13621
  const snap = await readLedger();
13379
- console.log(import_chalk58.default.dim(` wallet ${snap.billing_account_id} \xB7 band count ${snap.band_count} \xB7 next request ${snap.next_request_cents}\xA2
13622
+ console.log(import_chalk59.default.dim(` wallet ${snap.billing_account_id} \xB7 band count ${snap.band_count} \xB7 next request ${snap.next_request_cents}\xA2
13380
13623
  `));
13381
13624
  const url = `https://${dpHost}/${SANDBOX.version}/${SANDBOX.environment}/`;
13382
13625
  const headers = opts.key ? { "X-API-Key": opts.key } : {};
@@ -13458,27 +13701,27 @@ async function runMeter(readLedger, opts) {
13458
13701
  return checks;
13459
13702
  }
13460
13703
  function printChecks(checks) {
13461
- console.log(import_chalk58.default.bold("\n Results\n"));
13462
- const mark = { PASS: import_chalk58.default.green(" PASS"), FAIL: import_chalk58.default.red(" FAIL"), SKIP: import_chalk58.default.dim(" SKIP"), KNOWN: import_chalk58.default.yellow(" KNOWN") };
13704
+ console.log(import_chalk59.default.bold("\n Results\n"));
13705
+ const mark = { PASS: import_chalk59.default.green(" PASS"), FAIL: import_chalk59.default.red(" FAIL"), SKIP: import_chalk59.default.dim(" SKIP"), KNOWN: import_chalk59.default.yellow(" KNOWN") };
13463
13706
  for (const ch of checks) {
13464
- console.log(` ${mark[ch.status]} ${import_chalk58.default.bold(ch.name)}`);
13465
- console.log(import_chalk58.default.dim(` ${ch.detail}`));
13707
+ console.log(` ${mark[ch.status]} ${import_chalk59.default.bold(ch.name)}`);
13708
+ console.log(import_chalk59.default.dim(` ${ch.detail}`));
13466
13709
  }
13467
13710
  const fails = checks.filter((c) => c.status === "FAIL").length;
13468
13711
  const skips = checks.filter((c) => c.status === "SKIP").length;
13469
13712
  const known = checks.filter((c) => c.status === "KNOWN").length;
13470
13713
  console.log("");
13471
13714
  const passes = checks.filter((c) => c.status === "PASS").length;
13472
- if (fails) console.log(import_chalk58.default.red(` ${fails} check(s) FAILED.`));
13473
- else if (passes) console.log(import_chalk58.default.green(` ${passes} check(s) passed, 0 failed.`));
13474
- else console.log(import_chalk58.default.yellow(" NOTHING WAS VERIFIED \u2014 every check was skipped."));
13475
- if (known) console.log(import_chalk58.default.yellow(` ${known} known-open issue(s) still outstanding.`));
13476
- if (skips) console.log(import_chalk58.default.dim(` ${skips} check(s) NOT RUN (see SKIP above) \u2014 those invariants are unverified.`));
13715
+ if (fails) console.log(import_chalk59.default.red(` ${fails} check(s) FAILED.`));
13716
+ else if (passes) console.log(import_chalk59.default.green(` ${passes} check(s) passed, 0 failed.`));
13717
+ else console.log(import_chalk59.default.yellow(" NOTHING WAS VERIFIED \u2014 every check was skipped."));
13718
+ if (known) console.log(import_chalk59.default.yellow(` ${known} known-open issue(s) still outstanding.`));
13719
+ if (skips) console.log(import_chalk59.default.dim(` ${skips} check(s) NOT RUN (see SKIP above) \u2014 those invariants are unverified.`));
13477
13720
  console.log("");
13478
13721
  }
13479
13722
 
13480
13723
  // src/commands/op-test.ts
13481
- var import_chalk59 = __toESM(require("chalk"));
13724
+ var import_chalk60 = __toESM(require("chalk"));
13482
13725
  init_auth();
13483
13726
  init_types();
13484
13727
  var DASHBOARD_BASE9 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
@@ -13503,10 +13746,10 @@ async function scrubSandboxKeys(timeoutS = 150) {
13503
13746
  try {
13504
13747
  const r = await runnerCall({ action: "gut", id: TEARDOWN_GUT_ID }, timeoutS);
13505
13748
  const first = r.results?.[0]?.detail ?? r.error ?? "";
13506
- log(import_chalk59.default.dim(`teardown: ${first || (r.ok ? "sandbox fixture keys scrubbed" : "scrub reported a problem")}`));
13749
+ log(import_chalk60.default.dim(`teardown: ${first || (r.ok ? "sandbox fixture keys scrubbed" : "scrub reported a problem")}`));
13507
13750
  return r.ok;
13508
13751
  } catch (e) {
13509
- log(import_chalk59.default.dim(`teardown: sandbox key scrub skipped (${e instanceof Error ? e.message : String(e)}) \u2014 leftovers expire within 14 days; scripts/purge-test-keys.mjs reaps them`));
13752
+ log(import_chalk60.default.dim(`teardown: sandbox key scrub skipped (${e instanceof Error ? e.message : String(e)}) \u2014 leftovers expire within 14 days; scripts/purge-test-keys.mjs reaps them`));
13510
13753
  return false;
13511
13754
  }
13512
13755
  }
@@ -13528,23 +13771,23 @@ async function runOpTest(o = {}) {
13528
13771
  const guts = o.skipGuts ? [] : listing.guts.filter((g) => g.id !== TEARDOWN_GUT_ID && matches(g.id, void 0, { ...o, group: void 0 }));
13529
13772
  const total = specs.length + guts.length;
13530
13773
  if (total === 0) {
13531
- log(import_chalk59.default.yellow("nothing matched"));
13774
+ log(import_chalk60.default.yellow("nothing matched"));
13532
13775
  process.exitCode = 2;
13533
13776
  return;
13534
13777
  }
13535
- log(import_chalk59.default.bold(`apiblaze op test \u2014 ${specs.length} spec(s) + ${guts.length} gut check(s) on sandbox ${listing.sandbox_team}`));
13778
+ log(import_chalk60.default.bold(`apiblaze op test \u2014 ${specs.length} spec(s) + ${guts.length} gut check(s) on sandbox ${listing.sandbox_team}`));
13536
13779
  const results = [];
13537
13780
  let failed = 0;
13538
13781
  const started = Date.now();
13539
13782
  const record = (r) => {
13540
13783
  results.push(r);
13541
13784
  if (!r.ok) failed += 1;
13542
- const mark = r.ok ? import_chalk59.default.green("PASS") : import_chalk59.default.red("FAIL");
13785
+ const mark = r.ok ? import_chalk60.default.green("PASS") : import_chalk60.default.red("FAIL");
13543
13786
  log(`[${mark}] ${r.id} \u2014 ${r.label} (${ms(r.duration_ms)})`);
13544
13787
  if (!r.ok) {
13545
- if (r.kind === "spec") for (const c of r.cases.filter((c2) => !c2.ok)) log(import_chalk59.default.red(` \u2717 ${c.name}: ${c.detail}`));
13546
- else if (r.error) log(import_chalk59.default.red(` \u2717 ${r.error}`));
13547
- else for (const x of r.results.filter((x2) => x2 && x2.ok === false)) log(import_chalk59.default.red(` \u2717 ${x.name ?? ""}: ${x.detail ?? JSON.stringify(x)}`));
13788
+ if (r.kind === "spec") for (const c of r.cases.filter((c2) => !c2.ok)) log(import_chalk60.default.red(` \u2717 ${c.name}: ${c.detail}`));
13789
+ else if (r.error) log(import_chalk60.default.red(` \u2717 ${r.error}`));
13790
+ else for (const x of r.results.filter((x2) => x2 && x2.ok === false)) log(import_chalk60.default.red(` \u2717 ${x.name ?? ""}: ${x.detail ?? JSON.stringify(x)}`));
13548
13791
  }
13549
13792
  };
13550
13793
  try {
@@ -13575,13 +13818,13 @@ async function runOpTest(o = {}) {
13575
13818
  }
13576
13819
  const summary = { ran: results.length, passed: results.length - failed, failed, duration_ms: Date.now() - started, sandbox_team: listing.sandbox_team };
13577
13820
  log("");
13578
- log(failed ? import_chalk59.default.red(`${summary.passed}/${summary.ran} passed, ${failed} failed (${ms(summary.duration_ms)})`) : import_chalk59.default.green(`${summary.passed}/${summary.ran} passed (${ms(summary.duration_ms)})`));
13821
+ log(failed ? import_chalk60.default.red(`${summary.passed}/${summary.ran} passed, ${failed} failed (${ms(summary.duration_ms)})`) : import_chalk60.default.green(`${summary.passed}/${summary.ran} passed (${ms(summary.duration_ms)})`));
13579
13822
  if (o.json) process.stdout.write(JSON.stringify({ ...summary, results }, null, 2) + "\n");
13580
13823
  if (failed) process.exitCode = 1;
13581
13824
  }
13582
13825
 
13583
13826
  // src/commands/op-consumer.ts
13584
- var import_chalk60 = __toESM(require("chalk"));
13827
+ var import_chalk61 = __toESM(require("chalk"));
13585
13828
  var SANDBOX_TENANT = "portalteststenant";
13586
13829
  var SANDBOX_PROXY = "portaltestproxypreapproved@2.7.0";
13587
13830
  function proxyUrlFor(spec2, path11 = "/get") {
@@ -13623,9 +13866,9 @@ async function runOpConsumer(o = {}) {
13623
13866
  const creds = loadConsumer();
13624
13867
  const done = (r) => {
13625
13868
  const out = { kind: "consumer", id: "consumer-door", label: "Consumer door: the stored consumer token opens its tenant's OAuth-door proxy", duration_ms: Date.now() - t0, ...r };
13626
- const mark = out.skipped ? import_chalk60.default.yellow("SKIP") : out.ok ? import_chalk60.default.green("PASS") : import_chalk60.default.red("FAIL");
13869
+ const mark = out.skipped ? import_chalk61.default.yellow("SKIP") : out.ok ? import_chalk61.default.green("PASS") : import_chalk61.default.red("FAIL");
13627
13870
  log2(`[${mark}] ${out.id} \u2014 ${out.label}${out.tenant ? ` (tenant ${out.tenant})` : ""}${out.url ? ` \u2192 ${out.url}` : ""}`);
13628
- for (const c of out.cases) if (!c.ok || out.skipped) log2((c.ok ? import_chalk60.default.dim : import_chalk60.default.red)(` ${c.ok ? "ok " : "\u2717"} ${c.name}: ${c.detail}`));
13871
+ for (const c of out.cases) if (!c.ok || out.skipped) log2((c.ok ? import_chalk61.default.dim : import_chalk61.default.red)(` ${c.ok ? "ok " : "\u2717"} ${c.name}: ${c.detail}`));
13629
13872
  if (o.json) process.stdout.write(JSON.stringify(out, null, 2) + "\n");
13630
13873
  if (!out.ok && !out.skipped) process.exitCode = 1;
13631
13874
  if (out.skipped) process.exitCode = 2;
@@ -13665,7 +13908,7 @@ async function runOpConsumer(o = {}) {
13665
13908
  }
13666
13909
 
13667
13910
  // src/commands/op-perf.ts
13668
- var import_chalk61 = __toESM(require("chalk"));
13911
+ var import_chalk62 = __toESM(require("chalk"));
13669
13912
  var RPS_DEFAULT = 100;
13670
13913
  var RPS_CAP = 300;
13671
13914
  var SECONDS_DEFAULT = 60;
@@ -13849,10 +14092,10 @@ async function readGateway(proxy, sent, signal) {
13849
14092
  }
13850
14093
  const left = deadline - Date.now();
13851
14094
  if (left <= 0) {
13852
- log3(import_chalk61.default.yellow(` gateway: not available \u2014 Analytics Engine had ${lastSeen}/${sent} rows for ${proxy} after ${GATEWAY_WAIT_MS / 1e3}s${lastErr ? ` (last error: ${lastErr})` : ""}`));
14095
+ log3(import_chalk62.default.yellow(` gateway: not available \u2014 Analytics Engine had ${lastSeen}/${sent} rows for ${proxy} after ${GATEWAY_WAIT_MS / 1e3}s${lastErr ? ` (last error: ${lastErr})` : ""}`));
13853
14096
  return null;
13854
14097
  }
13855
- log3(import_chalk61.default.dim(` waiting for the latency ledger\u2026 ${lastSeen}/${sent} rows for ${proxy}${lastErr ? ` (${lastErr})` : ""}`));
14098
+ log3(import_chalk62.default.dim(` waiting for the latency ledger\u2026 ${lastSeen}/${sent} rows for ${proxy}${lastErr ? ` (${lastErr})` : ""}`));
13856
14099
  await sleep2(Math.min(GATEWAY_POLL_MS, left), signal);
13857
14100
  }
13858
14101
  }
@@ -13861,7 +14104,7 @@ async function runOpPerf(o = {}) {
13861
14104
  const thresholds = thresholdsFrom(o);
13862
14105
  const ctrl = new AbortController();
13863
14106
  const onSigint = () => {
13864
- log3(import_chalk61.default.yellow("\ninterrupted \u2014 stopping the load and tearing down the fixture"));
14107
+ log3(import_chalk62.default.yellow("\ninterrupted \u2014 stopping the load and tearing down the fixture"));
13865
14108
  ctrl.abort();
13866
14109
  };
13867
14110
  process.once("SIGINT", onSigint);
@@ -13870,7 +14113,7 @@ async function runOpPerf(o = {}) {
13870
14113
  if (!o.key) throw new Error("--url needs --key (the fixture proxy's API key)");
13871
14114
  fixture = { url: o.url, key: o.key, proxy: proxyFromUrl(o.url), provisioned: false };
13872
14115
  } else {
13873
- log3(import_chalk61.default.dim("provisioning the perf fixture through the runner\u2026"));
14116
+ log3(import_chalk62.default.dim("provisioning the perf fixture through the runner\u2026"));
13874
14117
  const f = await runnerCall({ action: "perf-fixture" }, 120);
13875
14118
  if (!f?.url || !f?.key) throw new Error(`perf-fixture returned no url/key: ${JSON.stringify(f).slice(0, 200)}`);
13876
14119
  fixture = { url: f.url, key: f.key, proxy: f.proxy || proxyFromUrl(f.url), team: f.team, provisioned: true };
@@ -13878,7 +14121,7 @@ async function runOpPerf(o = {}) {
13878
14121
  try {
13879
14122
  const total = Math.round(rps * seconds);
13880
14123
  const intervalMs = 1e3 / rps;
13881
- log3(import_chalk61.default.bold(`apiblaze op test --perf \u2014 ${rps} rps \xD7 ${seconds}s (${total} requests) against ${fixture.url}${fixture.team ? ` (sandbox ${fixture.team})` : ""}`));
14124
+ log3(import_chalk62.default.bold(`apiblaze op test --perf \u2014 ${rps} rps \xD7 ${seconds}s (${total} requests) against ${fixture.url}${fixture.team ? ` (sandbox ${fixture.team})` : ""}`));
13882
14125
  const samples = [];
13883
14126
  const inflight = /* @__PURE__ */ new Set();
13884
14127
  let fired = 0;
@@ -13886,7 +14129,7 @@ async function runOpPerf(o = {}) {
13886
14129
  const progress = setInterval(() => {
13887
14130
  const m = summarize(samples, Date.now() - t0);
13888
14131
  const secs = (Date.now() - t0) / 1e3;
13889
- log3(import_chalk61.default.dim(` ${Math.round(secs)}s sent ${fired} ok ${m.ok} err ${m.errors} p50 ${fmtMs(m.p50)} p95 ${fmtMs(m.p95)} (${(fired / secs).toFixed(1)} rps)`));
14132
+ log3(import_chalk62.default.dim(` ${Math.round(secs)}s sent ${fired} ok ${m.ok} err ${m.errors} p50 ${fmtMs(m.p50)} p95 ${fmtMs(m.p95)} (${(fired / secs).toFixed(1)} rps)`));
13890
14133
  }, PROGRESS_EVERY_MS);
13891
14134
  const { sent, elapsedMs } = await schedule({
13892
14135
  total,
@@ -13900,21 +14143,21 @@ async function runOpPerf(o = {}) {
13900
14143
  }
13901
14144
  });
13902
14145
  clearInterval(progress);
13903
- if (inflight.size) log3(import_chalk61.default.dim(` load sent; waiting for ${inflight.size} in-flight response(s) (\u2264 ${REQUEST_TIMEOUT_MS / 1e3}s)\u2026`));
14146
+ if (inflight.size) log3(import_chalk62.default.dim(` load sent; waiting for ${inflight.size} in-flight response(s) (\u2264 ${REQUEST_TIMEOUT_MS / 1e3}s)\u2026`));
13904
14147
  await Promise.all([...inflight]);
13905
14148
  const client = summarize(samples, elapsedMs);
13906
14149
  log3("");
13907
- log3(` ${import_chalk61.default.bold("client ")} p50 ${fmtMs(client.p50).padStart(7)} p95 ${fmtMs(client.p95).padStart(7)} p99 ${fmtMs(client.p99).padStart(7)} errors ${client.error_rate_pct.toFixed(2)}% (${client.errors}/${client.sent}: ${client.non2xx} non-2xx, ${client.timeouts} timeouts, ${client.network} network) achieved ${client.rps_achieved} rps of ${rps}`);
14150
+ log3(` ${import_chalk62.default.bold("client ")} p50 ${fmtMs(client.p50).padStart(7)} p95 ${fmtMs(client.p95).padStart(7)} p99 ${fmtMs(client.p99).padStart(7)} errors ${client.error_rate_pct.toFixed(2)}% (${client.errors}/${client.sent}: ${client.non2xx} non-2xx, ${client.timeouts} timeouts, ${client.network} network) achieved ${client.rps_achieved} rps of ${rps}`);
13908
14151
  const gateway = ctrl.signal.aborted ? null : await readGateway(fixture.proxy, sent, ctrl.signal);
13909
14152
  if (gateway) {
13910
14153
  const extras = [
13911
14154
  gateway.kv_per_req !== void 0 ? `kv/req ${gateway.kv_per_req}` : "",
13912
14155
  gateway.redis_per_req !== void 0 ? `redis/req ${gateway.redis_per_req}` : ""
13913
14156
  ].filter(Boolean).join(" ");
13914
- log3(` ${import_chalk61.default.bold("gateway")} p50 ${fmtMs(gateway.p50).padStart(7)} p95 ${fmtMs(gateway.p95).padStart(7)}${gateway.p99 !== void 0 ? ` p99 ${fmtMs(gateway.p99).padStart(7)}` : ""} ${import_chalk61.default.dim(`(${gateway.requests} ledger rows${extras ? `, ${extras}` : ""})`)}`);
13915
- for (const b of gateway.blame ?? []) log3(import_chalk61.default.dim(` ${(b.share * 100).toFixed(1).padStart(5)}% p95 ${fmtMs(b.p95_ms).padStart(6)} ${b.feature} \u2192 ${b.dep}`));
14157
+ log3(` ${import_chalk62.default.bold("gateway")} p50 ${fmtMs(gateway.p50).padStart(7)} p95 ${fmtMs(gateway.p95).padStart(7)}${gateway.p99 !== void 0 ? ` p99 ${fmtMs(gateway.p99).padStart(7)}` : ""} ${import_chalk62.default.dim(`(${gateway.requests} ledger rows${extras ? `, ${extras}` : ""})`)}`);
14158
+ for (const b of gateway.blame ?? []) log3(import_chalk62.default.dim(` ${(b.share * 100).toFixed(1).padStart(5)}% p95 ${fmtMs(b.p95_ms).padStart(6)} ${b.feature} \u2192 ${b.dep}`));
13916
14159
  } else {
13917
- log3(` ${import_chalk61.default.bold("gateway")} ${import_chalk61.default.yellow("not available")} ${import_chalk61.default.dim("(gateway thresholds not evaluated)")}`);
14160
+ log3(` ${import_chalk62.default.bold("gateway")} ${import_chalk62.default.yellow("not available")} ${import_chalk62.default.dim("(gateway thresholds not evaluated)")}`);
13918
14161
  }
13919
14162
  const breaches = evaluate(client, gateway, thresholds);
13920
14163
  const aborted = ctrl.signal.aborted;
@@ -13922,7 +14165,7 @@ async function runOpPerf(o = {}) {
13922
14165
  log3("");
13923
14166
  const row = (metric, value, limit, unit) => {
13924
14167
  const hit = breaches.find((b) => b.metric === metric);
13925
- log3(`[${hit ? import_chalk61.default.red("FAIL") : import_chalk61.default.green("PASS")}] ${metric.padEnd(11)} ${String(value)}${unit} ${hit ? ">" : "\u2264"} ${limit}${unit}`);
14168
+ log3(`[${hit ? import_chalk62.default.red("FAIL") : import_chalk62.default.green("PASS")}] ${metric.padEnd(11)} ${String(value)}${unit} ${hit ? ">" : "\u2264"} ${limit}${unit}`);
13926
14169
  };
13927
14170
  row("client_p50", client.p50, thresholds.p50_ms, "ms");
13928
14171
  row("client_p95", client.p95, thresholds.p95_ms, "ms");
@@ -13931,12 +14174,12 @@ async function runOpPerf(o = {}) {
13931
14174
  row("gateway_p50", gateway.p50, thresholds.gateway_p50_ms, "ms");
13932
14175
  row("gateway_p95", gateway.p95, thresholds.gateway_p95_ms, "ms");
13933
14176
  } else {
13934
- log3(`[${import_chalk61.default.yellow("SKIP")}] gateway_p50 / gateway_p95 \u2014 ledger rows not available`);
14177
+ log3(`[${import_chalk62.default.yellow("SKIP")}] gateway_p50 / gateway_p95 \u2014 ledger rows not available`);
13935
14178
  }
13936
14179
  log3("");
13937
- if (aborted) log3(import_chalk61.default.red(`FAIL \u2014 interrupted after ${sent}/${total} requests`));
13938
- else if (ok) log3(import_chalk61.default.green(`PASS \u2014 ${client.sent} requests at ${client.rps_achieved} rps, no threshold breached`));
13939
- else log3(import_chalk61.default.red(`FAIL \u2014 ${breaches.length} threshold(s) breached: ${breaches.map((b) => `${b.metric} ${b.value} > ${b.limit}`).join(", ")}`));
14180
+ if (aborted) log3(import_chalk62.default.red(`FAIL \u2014 interrupted after ${sent}/${total} requests`));
14181
+ else if (ok) log3(import_chalk62.default.green(`PASS \u2014 ${client.sent} requests at ${client.rps_achieved} rps, no threshold breached`));
14182
+ else log3(import_chalk62.default.red(`FAIL \u2014 ${breaches.length} threshold(s) breached: ${breaches.map((b) => `${b.metric} ${b.value} > ${b.limit}`).join(", ")}`));
13940
14183
  if (o.json) {
13941
14184
  const doc = {
13942
14185
  kind: "perf",
@@ -13974,9 +14217,9 @@ async function runOpPerf(o = {}) {
13974
14217
  if (fixture.provisioned) {
13975
14218
  try {
13976
14219
  await runnerCall({ action: "perf-teardown", proxy: fixture.proxy }, 120);
13977
- log3(import_chalk61.default.dim(`fixture ${fixture.proxy} torn down`));
14220
+ log3(import_chalk62.default.dim(`fixture ${fixture.proxy} torn down`));
13978
14221
  } catch (e) {
13979
- log3(import_chalk61.default.red(`teardown of ${fixture.proxy} failed: ${e instanceof Error ? e.message : String(e)} \u2014 run \`apiblaze op residue\` / clean the sandbox`));
14222
+ log3(import_chalk62.default.red(`teardown of ${fixture.proxy} failed: ${e instanceof Error ? e.message : String(e)} \u2014 run \`apiblaze op residue\` / clean the sandbox`));
13980
14223
  process.exitCode = 1;
13981
14224
  }
13982
14225
  await scrubSandboxKeys(120);
@@ -14010,93 +14253,93 @@ var OP_COMMANDS = [
14010
14253
  function renderOpCommands() {
14011
14254
  const width = Math.max(...OP_COMMANDS.map((c) => c.cmd.length)) + 10;
14012
14255
  const lines = OP_COMMANDS.map((c) => {
14013
- const left = ` ${import_chalk62.default.cyan(`apiblaze ${c.cmd}`)}`;
14256
+ const left = ` ${import_chalk63.default.cyan(`apiblaze ${c.cmd}`)}`;
14014
14257
  const pad = " ".repeat(Math.max(1, width - c.cmd.length));
14015
- return `${left}${pad}${c.blurb}${c.extra ? " " + import_chalk62.default.dim(`(${c.extra})`) : ""}`;
14258
+ return `${left}${pad}${c.blurb}${c.extra ? " " + import_chalk63.default.dim(`(${c.extra})`) : ""}`;
14016
14259
  });
14017
14260
  return [
14018
- import_chalk62.default.bold("Operator commands"),
14261
+ import_chalk63.default.bold("Operator commands"),
14019
14262
  ...lines,
14020
14263
  "",
14021
- import_chalk62.default.dim(" Operators only. The gate is server-side (dashboard /api/cli/op checks the"),
14022
- import_chalk62.default.dim(" signed-in email, admin-api re-checks with operatorGate) \u2014 a patched CLI just"),
14023
- import_chalk62.default.dim(" gets 403s. Every op call is read-only except `op sweep`."),
14264
+ import_chalk63.default.dim(" Operators only. The gate is server-side (dashboard /api/cli/op checks the"),
14265
+ import_chalk63.default.dim(" signed-in email, admin-api re-checks with operatorGate) \u2014 a patched CLI just"),
14266
+ import_chalk63.default.dim(" gets 403s. Every op call is read-only except `op sweep`."),
14024
14267
  "",
14025
- import_chalk62.default.dim(" Not a CLI command: to prune all non-CP data run scripts/nuke-but-cp.sh --apply --sweep"),
14026
- import_chalk62.default.dim(" in the repo. Operator dashboards (dlq, thresholds, throttling, pricing, billing,"),
14027
- import_chalk62.default.dim(" agent-spend, teams, tests, leak-detection, lifecycle) live at /operator/* in the app.")
14268
+ import_chalk63.default.dim(" Not a CLI command: to prune all non-CP data run scripts/nuke-but-cp.sh --apply --sweep"),
14269
+ import_chalk63.default.dim(" in the repo. Operator dashboards (dlq, thresholds, throttling, pricing, billing,"),
14270
+ import_chalk63.default.dim(" agent-spend, teams, tests, leak-detection, lifecycle) live at /operator/* in the app.")
14028
14271
  ].join("\n");
14029
14272
  }
14030
14273
  function printResidue(report, applied) {
14031
14274
  const up = report?.upstash ?? {};
14032
14275
  const fga = report?.fga ?? {};
14033
14276
  const ghosts = report?.ghosts ?? {};
14034
- console.log(import_chalk62.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
14035
- console.log(import_chalk62.default.bold("\n Upstash"));
14277
+ console.log(import_chalk63.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
14278
+ console.log(import_chalk63.default.bold("\n Upstash"));
14036
14279
  const orphans = up.orphans ?? [];
14037
- if (orphans.length === 0) console.log(import_chalk62.default.green(" no orphaned keys"));
14038
- for (const o of orphans) console.log(` ${import_chalk62.default.yellow(o.key)} ${import_chalk62.default.dim(`\u2014 ${o.reason}`)}`);
14039
- console.log(import_chalk62.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
14280
+ if (orphans.length === 0) console.log(import_chalk63.default.green(" no orphaned keys"));
14281
+ for (const o of orphans) console.log(` ${import_chalk63.default.yellow(o.key)} ${import_chalk63.default.dim(`\u2014 ${o.reason}`)}`);
14282
+ console.log(import_chalk63.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
14040
14283
  if (up.anon_wallet_detail) {
14041
14284
  const d = up.anon_wallet_detail;
14042
- console.log(import_chalk62.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)"}`));
14285
+ console.log(import_chalk63.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)"}`));
14043
14286
  }
14044
14287
  if (up.keyspace_census) {
14045
14288
  const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
14046
- console.log(import_chalk62.default.dim(` keyspace: ${census}`));
14289
+ console.log(import_chalk63.default.dim(` keyspace: ${census}`));
14047
14290
  }
14048
- if (up.unknown?.length) console.log(import_chalk62.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
14049
- if (applied) console.log(` ${import_chalk62.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
14050
- for (const e of up.errors ?? []) console.log(import_chalk62.default.red(` error: ${e}`));
14051
- console.log(import_chalk62.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
14291
+ if (up.unknown?.length) console.log(import_chalk63.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
14292
+ if (applied) console.log(` ${import_chalk63.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
14293
+ for (const e of up.errors ?? []) console.log(import_chalk63.default.red(` error: ${e}`));
14294
+ console.log(import_chalk63.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
14052
14295
  if (applied) {
14053
14296
  const swept = fga?.swept ?? [];
14054
- if (swept.length === 0) console.log(import_chalk62.default.green(" no orphaned stores"));
14297
+ if (swept.length === 0) console.log(import_chalk63.default.green(" no orphaned stores"));
14055
14298
  for (const s of swept) {
14056
14299
  console.log(
14057
- ` ${import_chalk62.default.yellow(s.store_id)} ${import_chalk62.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
14300
+ ` ${import_chalk63.default.yellow(s.store_id)} ${import_chalk63.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
14058
14301
  );
14059
14302
  }
14060
- if (fga?.remaining) console.log(import_chalk62.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
14303
+ if (fga?.remaining) console.log(import_chalk63.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
14061
14304
  const st = fga?.side_tables;
14062
- if (st) console.log(import_chalk62.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})` : ""}`));
14305
+ if (st) console.log(import_chalk63.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})` : ""}`));
14063
14306
  } else {
14064
14307
  const fgaOrphans = fga?.orphans ?? [];
14065
- if (fgaOrphans.length === 0) console.log(import_chalk62.default.green(" no orphaned stores"));
14308
+ if (fgaOrphans.length === 0) console.log(import_chalk63.default.green(" no orphaned stores"));
14066
14309
  for (const s of fgaOrphans) {
14067
14310
  const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
14068
- console.log(` ${import_chalk62.default.yellow(s.store_id)} ${import_chalk62.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
14311
+ console.log(` ${import_chalk63.default.yellow(s.store_id)} ${import_chalk63.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
14069
14312
  }
14070
- console.log(import_chalk62.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
14313
+ console.log(import_chalk63.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
14071
14314
  const st = fga?.side_tables;
14072
- if (st) console.log(import_chalk62.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`));
14315
+ if (st) console.log(import_chalk63.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`));
14073
14316
  }
14074
- for (const e of fga?.errors ?? []) console.log(import_chalk62.default.red(` error: ${e}`));
14075
- console.log(import_chalk62.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
14317
+ for (const e of fga?.errors ?? []) console.log(import_chalk63.default.red(` error: ${e}`));
14318
+ console.log(import_chalk63.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
14076
14319
  if (applied) {
14077
- if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk62.default.green(" no ghost tuples"));
14078
- else console.log(` ${import_chalk62.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk62.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
14320
+ if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk63.default.green(" no ghost tuples"));
14321
+ else console.log(` ${import_chalk63.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk63.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
14079
14322
  } else {
14080
14323
  const n = ghosts?.ghost_count ?? 0;
14081
- if (n === 0) console.log(import_chalk62.default.green(` no ghost tuples ${import_chalk62.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
14324
+ if (n === 0) console.log(import_chalk63.default.green(` no ghost tuples ${import_chalk63.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
14082
14325
  else {
14083
- console.log(import_chalk62.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
14326
+ console.log(import_chalk63.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
14084
14327
  for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
14085
- console.log(import_chalk62.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
14328
+ console.log(import_chalk63.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
14086
14329
  }
14087
- if (n > 20) console.log(import_chalk62.default.dim(` \u2026 and ${n - 20} more`));
14330
+ if (n > 20) console.log(import_chalk63.default.dim(` \u2026 and ${n - 20} more`));
14088
14331
  }
14089
14332
  }
14090
- for (const e of ghosts?.errors ?? []) console.log(import_chalk62.default.red(` error: ${e}`));
14333
+ for (const e of ghosts?.errors ?? []) console.log(import_chalk63.default.red(` error: ${e}`));
14091
14334
  console.log();
14092
14335
  }
14093
14336
  async function runOp(sub, opts = {}, view) {
14094
14337
  if (!loadCredentials()) {
14095
- console.log(import_chalk62.default.dim("Not logged in. Run `apiblaze login`."));
14338
+ console.log(import_chalk63.default.dim("Not logged in. Run `apiblaze login`."));
14096
14339
  return;
14097
14340
  }
14098
14341
  if (!isOperatorLogin()) {
14099
- console.log(import_chalk62.default.dim("`apiblaze op` is only available to platform operators."));
14342
+ console.log(import_chalk63.default.dim("`apiblaze op` is only available to platform operators."));
14100
14343
  return;
14101
14344
  }
14102
14345
  switch (sub) {
@@ -14152,17 +14395,17 @@ async function runOp(sub, opts = {}, view) {
14152
14395
  const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
14153
14396
  printResidue(report, false);
14154
14397
  if (nUp + nFga + nGhost + nSide === 0) {
14155
- console.log(import_chalk62.default.green("Nothing to sweep."));
14398
+ console.log(import_chalk63.default.green("Nothing to sweep."));
14156
14399
  return;
14157
14400
  }
14158
14401
  if (!opts.yes) {
14159
14402
  const readline3 = await import("readline/promises");
14160
14403
  const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
14161
14404
  const answer = await rl.question(
14162
- import_chalk62.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: `)
14405
+ import_chalk63.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: `)
14163
14406
  );
14164
14407
  rl.close();
14165
- if (answer.trim() !== "sweep") return void console.log(import_chalk62.default.dim("Aborted."));
14408
+ if (answer.trim() !== "sweep") return void console.log(import_chalk63.default.dim("Aborted."));
14166
14409
  }
14167
14410
  const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
14168
14411
  if (opts.json) return void console.log(JSON.stringify(result, null, 2));
@@ -14172,25 +14415,25 @@ async function runOp(sub, opts = {}, view) {
14172
14415
  case "mark": {
14173
14416
  const label3 = (view ?? "").trim();
14174
14417
  if (!label3) {
14175
- console.log(import_chalk62.default.red("Give the change a name:") + import_chalk62.default.cyan(' apiblaze op mark "cached tenant count"'));
14418
+ console.log(import_chalk63.default.red("Give the change a name:") + import_chalk63.default.cyan(' apiblaze op mark "cached tenant count"'));
14176
14419
  return;
14177
14420
  }
14178
14421
  const res = await opCall({ method: "POST", path: "/operator/latency/mark", body: { label: label3 }, summary: "record change marker" });
14179
14422
  const ts = new Date(res?.marker?.ts ?? Date.now()).toISOString();
14180
- console.log(import_chalk62.default.green(`
14181
- Marked: `) + import_chalk62.default.bold(label3));
14182
- console.log(import_chalk62.default.dim(` ${ts}`));
14183
- console.log(import_chalk62.default.dim(` Once traffic has run on both sides, compare with: `) + import_chalk62.default.cyan("apiblaze op latency compare") + "\n");
14423
+ console.log(import_chalk63.default.green(`
14424
+ Marked: `) + import_chalk63.default.bold(label3));
14425
+ console.log(import_chalk63.default.dim(` ${ts}`));
14426
+ console.log(import_chalk63.default.dim(` Once traffic has run on both sides, compare with: `) + import_chalk63.default.cyan("apiblaze op latency compare") + "\n");
14184
14427
  return;
14185
14428
  }
14186
14429
  case "credits": {
14187
14430
  const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
14188
14431
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
14189
14432
  const accounts = data?.accounts ?? [];
14190
- if (accounts.length === 0) return void console.log(import_chalk62.default.dim("No credit wallets."));
14433
+ if (accounts.length === 0) return void console.log(import_chalk63.default.dim("No credit wallets."));
14191
14434
  for (const a of accounts) {
14192
14435
  const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
14193
- console.log(` ${import_chalk62.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk62.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk62.default.dim(" \u2014 anon") : ""}`);
14436
+ console.log(` ${import_chalk63.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk63.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk63.default.dim(" \u2014 anon") : ""}`);
14194
14437
  }
14195
14438
  return;
14196
14439
  }
@@ -14201,7 +14444,7 @@ async function runOp(sub, opts = {}, view) {
14201
14444
  case "billing": {
14202
14445
  const which = (view ?? "").trim().toLowerCase();
14203
14446
  if (which && which !== "doors" && which !== "meter") {
14204
- return void console.log(import_chalk62.default.red(`Unknown: apiblaze op billing ${which}. Use 'doors', 'meter', or neither for both.`));
14447
+ return void console.log(import_chalk63.default.red(`Unknown: apiblaze op billing ${which}. Use 'doors', 'meter', or neither for both.`));
14205
14448
  }
14206
14449
  const checks = [];
14207
14450
  let doorsData = null;
@@ -14270,29 +14513,29 @@ async function runOp(sub, opts = {}, view) {
14270
14513
  const data = await opCall({ method: "GET", path: `/operator/latency/grades${q}`, summary: "latency grades" });
14271
14514
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
14272
14515
  const t = data.thresholds_ms;
14273
- console.log(import_chalk62.default.bold("\nHow good was apiblaze itself?") + import_chalk62.default.dim(" (our overhead only \u2014 a slow customer API never counts against us)"));
14274
- console.log(import_chalk62.default.dim(` excellent <${t.excellent.replace("<", "")}ms \xB7 okay ${t.okay}ms \xB7 bad ${t.bad}ms \xB7 terrible ${t.terrible.replace(">=", "")}ms+
14516
+ console.log(import_chalk63.default.bold("\nHow good was apiblaze itself?") + import_chalk63.default.dim(" (our overhead only \u2014 a slow customer API never counts against us)"));
14517
+ console.log(import_chalk63.default.dim(` excellent <${t.excellent.replace("<", "")}ms \xB7 okay ${t.okay}ms \xB7 bad ${t.bad}ms \xB7 terrible ${t.terrible.replace(">=", "")}ms+
14275
14518
  `));
14276
- console.log(import_chalk62.default.dim(" date reqs excellent okay bad terrible"));
14519
+ console.log(import_chalk63.default.dim(" date reqs excellent okay bad terrible"));
14277
14520
  for (const d of data.days ?? []) {
14278
14521
  const p = d.pct;
14279
- const cell = (v, colour) => v > 0 ? colour(`${String(v).padStart(5)}%`) : import_chalk62.default.dim(`${String(v).padStart(5)}%`);
14522
+ const cell = (v, colour) => v > 0 ? colour(`${String(v).padStart(5)}%`) : import_chalk63.default.dim(`${String(v).padStart(5)}%`);
14280
14523
  console.log(
14281
- ` ${d.date} ${String(d.total).padStart(5)} ${cell(p.excellent, import_chalk62.default.green)} ${cell(p.okay, import_chalk62.default.cyan)} ${cell(p.bad, import_chalk62.default.yellow)} ${cell(p.terrible, import_chalk62.default.red)}`
14524
+ ` ${d.date} ${String(d.total).padStart(5)} ${cell(p.excellent, import_chalk63.default.green)} ${cell(p.okay, import_chalk63.default.cyan)} ${cell(p.bad, import_chalk63.default.yellow)} ${cell(p.terrible, import_chalk63.default.red)}`
14282
14525
  );
14283
14526
  }
14284
14527
  const cul = data.culprits ?? [];
14285
14528
  if (cul.length) {
14286
- console.log(import_chalk62.default.bold("\n Who caused the bad and terrible ones\n"));
14287
- console.log(import_chalk62.default.dim(" bad terrible feature \u2192 dependency"));
14529
+ console.log(import_chalk63.default.bold("\n Who caused the bad and terrible ones\n"));
14530
+ console.log(import_chalk63.default.dim(" bad terrible feature \u2192 dependency"));
14288
14531
  for (const r of cul.slice(0, 12)) {
14289
14532
  if (!r.bad && !r.terrible) continue;
14290
14533
  console.log(
14291
- ` ${String(r.bad).padStart(6)} ${import_chalk62.default.red(String(r.terrible).padStart(8))} ${import_chalk62.default.yellow(r.feature)} ${import_chalk62.default.dim("\u2192")} ${import_chalk62.default.cyan(r.dep)}`
14534
+ ` ${String(r.bad).padStart(6)} ${import_chalk63.default.red(String(r.terrible).padStart(8))} ${import_chalk63.default.yellow(r.feature)} ${import_chalk63.default.dim("\u2192")} ${import_chalk63.default.cyan(r.dep)}`
14292
14535
  );
14293
14536
  }
14294
14537
  }
14295
- if (data.caveat) console.log(import_chalk62.default.dim(`
14538
+ if (data.caveat) console.log(import_chalk63.default.dim(`
14296
14539
  \u26A0 ${data.caveat}
14297
14540
  `));
14298
14541
  return;
@@ -14301,24 +14544,24 @@ async function runOp(sub, opts = {}, view) {
14301
14544
  const data = await opCall({ method: "GET", path: `/operator/latency/compare${q}`, summary: "latency before/after" });
14302
14545
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
14303
14546
  const b = data.before, a = data.after, d = data.delta;
14304
- console.log(import_chalk62.default.bold(`
14305
- Before vs after: `) + import_chalk62.default.cyan(data.marker.label));
14306
- console.log(import_chalk62.default.dim(` marked ${new Date(data.marker.ts).toISOString()} \xB7 ${data.window_hours}h either side
14547
+ console.log(import_chalk63.default.bold(`
14548
+ Before vs after: `) + import_chalk63.default.cyan(data.marker.label));
14549
+ console.log(import_chalk63.default.dim(` marked ${new Date(data.marker.ts).toISOString()} \xB7 ${data.window_hours}h either side
14307
14550
  `));
14308
14551
  const row = (name, before, after, delta) => {
14309
14552
  const arrow = delta === 0 ? "=" : delta < 0 ? "\u2193" : "\u2191";
14310
14553
  const txt = `${String(before).padStart(6)}ms \u2192${String(after).padStart(7)}ms ${arrow}${Math.abs(delta)}ms`;
14311
- console.log(` ${name.padEnd(22)}${data.trustworthy ? delta <= 0 ? import_chalk62.default.green(txt) : import_chalk62.default.red(txt) : import_chalk62.default.dim(txt)}`);
14554
+ console.log(` ${name.padEnd(22)}${data.trustworthy ? delta <= 0 ? import_chalk63.default.green(txt) : import_chalk63.default.red(txt) : import_chalk63.default.dim(txt)}`);
14312
14555
  };
14313
- console.log(import_chalk62.default.dim(" metric before after change"));
14556
+ console.log(import_chalk63.default.dim(" metric before after change"));
14314
14557
  row("total p50", b.total_p50, a.total_p50, d.total_p50);
14315
14558
  row("total p95", b.total_p95, a.total_p95, d.total_p95);
14316
14559
  row("apiblaze overhead p50", b.gw_p50, a.gw_p50, d.gw_p50);
14317
14560
  row("apiblaze overhead p95", b.gw_p95, a.gw_p95, d.gw_p95);
14318
- console.log(import_chalk62.default.dim(`
14561
+ console.log(import_chalk63.default.dim(`
14319
14562
  requests: ${b.requests} before \xB7 ${a.requests} after`));
14320
14563
  for (const w of data.warnings ?? []) {
14321
- console.log((data.trustworthy ? import_chalk62.default.dim : import_chalk62.default.yellow)(` ${data.trustworthy ? "\xB7" : "\u26A0"} ${w}`));
14564
+ console.log((data.trustworthy ? import_chalk63.default.dim : import_chalk63.default.yellow)(` ${data.trustworthy ? "\xB7" : "\u26A0"} ${w}`));
14322
14565
  }
14323
14566
  console.log("");
14324
14567
  return;
@@ -14327,19 +14570,19 @@ Before vs after: `) + import_chalk62.default.cyan(data.marker.label));
14327
14570
  const data = await opCall({ method: "GET", path: `/operator/latency/slow${q}`, summary: "slowest requests" });
14328
14571
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
14329
14572
  const rows2 = data?.rows ?? [];
14330
- if (!rows2.length) return void console.log(import_chalk62.default.dim("No requests over the threshold in that window."));
14331
- console.log(import_chalk62.default.bold(`
14573
+ if (!rows2.length) return void console.log(import_chalk63.default.dim("No requests over the threshold in that window."));
14574
+ console.log(import_chalk63.default.bold(`
14332
14575
  Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
14333
14576
  `));
14334
- console.log(import_chalk62.default.dim(" total ours theirs blame request id"));
14577
+ console.log(import_chalk63.default.dim(" total ours theirs blame request id"));
14335
14578
  for (const r of rows2.slice(0, 30)) {
14336
14579
  console.log(
14337
- ` ${String(Math.round(r.duration_ms)).padStart(6)} ${String(Math.round(r.gateway_ms)).padStart(5)} ${String(Math.round(r.upstream_ttfb_ms)).padStart(6)} ${import_chalk62.default.yellow(`${r.slow_gw_feature || "-"}\u2192${r.slow_dep || "-"}`.padEnd(20))} ${import_chalk62.default.dim(r.request_id || "")}`
14580
+ ` ${String(Math.round(r.duration_ms)).padStart(6)} ${String(Math.round(r.gateway_ms)).padStart(5)} ${String(Math.round(r.upstream_ttfb_ms)).padStart(6)} ${import_chalk63.default.yellow(`${r.slow_gw_feature || "-"}\u2192${r.slow_dep || "-"}`.padEnd(20))} ${import_chalk63.default.dim(r.request_id || "")}`
14338
14581
  );
14339
14582
  }
14340
- console.log(import_chalk62.default.dim(`
14583
+ console.log(import_chalk63.default.dim(`
14341
14584
  The last column is the request id (Cloudflare calls it a "cf-ray"). Look one up with`));
14342
- console.log(import_chalk62.default.dim(` \`apiblaze logs\` for that request's exact per-feature breakdown \u2014 unsampled, unlike the table above.
14585
+ console.log(import_chalk63.default.dim(` \`apiblaze logs\` for that request's exact per-feature breakdown \u2014 unsampled, unlike the table above.
14343
14586
  `));
14344
14587
  return;
14345
14588
  }
@@ -14347,18 +14590,18 @@ Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
14347
14590
  const data = await opCall({ method: "GET", path: `/operator/latency/llm${q}`, summary: "llm latency" });
14348
14591
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
14349
14592
  const rows2 = data?.rows ?? [];
14350
- if (!rows2.length) return void console.log(import_chalk62.default.dim("No LLM traffic in that window."));
14351
- console.log(import_chalk62.default.bold(`
14593
+ if (!rows2.length) return void console.log(import_chalk63.default.dim("No LLM traffic in that window."));
14594
+ console.log(import_chalk63.default.bold(`
14352
14595
  LLM timing \u2014 last ${data.window_hours}h
14353
14596
  `));
14354
- console.log(import_chalk62.default.dim(" turns turn p95 ttfc p95 gen p95 reserve p95 in/out tokens p95 model"));
14597
+ console.log(import_chalk63.default.dim(" turns turn p95 ttfc p95 gen p95 reserve p95 in/out tokens p95 model"));
14355
14598
  for (const r of rows2) {
14356
14599
  const n = r.turns ?? r.requests ?? 0;
14357
14600
  console.log(
14358
14601
  ` ${String(Math.round(n)).padStart(8)} ${String(Math.round(r.turn_p95_ms ?? 0)).padStart(6)}ms ${String(Math.round(r.ttfc_p95_ms ?? 0)).padStart(6)}ms ${String(Math.round(r.gen_p95_ms ?? 0)).padStart(6)}ms ${String(Math.round(r.reserve_p95_ms ?? 0)).padStart(9)}ms ${String(Math.round(r.input_tokens_p95 ?? 0)).padStart(6)}/${String(Math.round(r.output_tokens_p95 ?? 0)).padEnd(6)} ${r.model || "-"}`
14359
14602
  );
14360
14603
  }
14361
- console.log(import_chalk62.default.dim(`
14604
+ console.log(import_chalk63.default.dim(`
14362
14605
  ${data.note}
14363
14606
  `));
14364
14607
  return;
@@ -14369,26 +14612,26 @@ LLM timing \u2014 last ${data.window_hours}h
14369
14612
  ]);
14370
14613
  if (opts.json) return void console.log(JSON.stringify({ blame, summary }, null, 2));
14371
14614
  const ov = summary?.apiblaze_overhead_ms ?? {};
14372
- console.log(import_chalk62.default.bold(`
14615
+ console.log(import_chalk63.default.bold(`
14373
14616
  Latency \u2014 last ${summary?.window_hours ?? "?"}h, ${Number(summary?.requests ?? 0).toLocaleString()} requests
14374
14617
  `));
14375
- console.log(` ${import_chalk62.default.bold("apiblaze overhead")} p50 ${String(ov.p50 ?? 0).padStart(5)}ms p95 ${String(ov.p95 ?? 0).padStart(6)}ms p99 ${String(ov.p99 ?? 0).padStart(6)}ms ${import_chalk62.default.dim("\u2190 ours")}`);
14376
- console.log(` ${import_chalk62.default.bold("upstream ttfb ")} ${" ".repeat(24)}p95 ${String(summary?.upstream_ttfb_p95_ms ?? 0).padStart(6)}ms ${import_chalk62.default.dim("\u2190 theirs")}`);
14377
- console.log(import_chalk62.default.dim(` (per-request percentiles \u2014 never subtract one from the other)
14618
+ console.log(` ${import_chalk63.default.bold("apiblaze overhead")} p50 ${String(ov.p50 ?? 0).padStart(5)}ms p95 ${String(ov.p95 ?? 0).padStart(6)}ms p99 ${String(ov.p99 ?? 0).padStart(6)}ms ${import_chalk63.default.dim("\u2190 ours")}`);
14619
+ console.log(` ${import_chalk63.default.bold("upstream ttfb ")} ${" ".repeat(24)}p95 ${String(summary?.upstream_ttfb_p95_ms ?? 0).padStart(6)}ms ${import_chalk63.default.dim("\u2190 theirs")}`);
14620
+ console.log(import_chalk63.default.dim(` (per-request percentiles \u2014 never subtract one from the other)
14378
14621
  `));
14379
14622
  const rows = blame?.blame ?? [];
14380
- if (!rows.length) return void console.log(import_chalk62.default.dim("No latency rows in that window."));
14381
- console.log(import_chalk62.default.bold(" Which feature ate the time, and what inside it\n"));
14382
- console.log(import_chalk62.default.dim(" share p95 feature \u2192 dependency"));
14623
+ if (!rows.length) return void console.log(import_chalk63.default.dim("No latency rows in that window."));
14624
+ console.log(import_chalk63.default.bold(" Which feature ate the time, and what inside it\n"));
14625
+ console.log(import_chalk63.default.dim(" share p95 feature \u2192 dependency"));
14383
14626
  for (const r of rows.slice(0, 15)) {
14384
14627
  const share = `${(r.share * 100).toFixed(1)}%`;
14385
- console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${import_chalk62.default.yellow(r.feature)} ${import_chalk62.default.dim("\u2192")} ${import_chalk62.default.cyan(r.dep)}`);
14628
+ console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${import_chalk63.default.yellow(r.feature)} ${import_chalk63.default.dim("\u2192")} ${import_chalk63.default.cyan(r.dep)}`);
14386
14629
  }
14387
14630
  console.log("");
14388
14631
  return;
14389
14632
  }
14390
14633
  default:
14391
- console.log(import_chalk62.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
14634
+ console.log(import_chalk63.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
14392
14635
  }
14393
14636
  }
14394
14637
 
@@ -14439,8 +14682,8 @@ agent.command("openapi").description("Chat to build your API spec from real traf
14439
14682
  agent.command("mcp").description("Chat to build an MCP server for an API").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").option("--environment <env>", "Environment to publish (default: prod)").action(action((project, apiVersion, opts) => runMcp(project, apiVersion, opts)));
14440
14683
  program.command("apichat [project]").description("Turn any API into a chat: point at an OpenAPI spec \u2014 or chat an EXISTING proxy by name (no login needed)").option("--target <url|file>", "What to chat with \u2014 pass ANY of: a target server base URL (spec auto-discovered at /openapi.json etc.), a local OpenAPI file (./openapi.yaml), or a remote OpenAPI URL (https://acme.com/openapi.yaml)").addOption(new import_commander.Option("--openapi <file|url>", "Deprecated alias \u2014 --target now detects spec files/URLs itself").hideHelp()).addOption(new import_commander.Option("--openapispec <file|url>", "Deprecated alias for --openapi").hideHelp()).option("--name <name>", "Proxy name (defaults to the target host)").option("--apiversion <version>", "API version to create (e.g. 1.0.0)").option("--environment <env>", "Environment to chat against (default: prod anonymous / dev logged-in)").option("--access <mode>", 'Who can call this API once connected (e.g. via Claude): "open" = anyone who signs in, "invite" = only you + emails you pre-approve. Default: invite when logged in, open when anonymous.').option("--target-auth-env <ENV_VAR>", "Read the upstream credential from this env var (CI-safe; required when there is no TTY and the API needs auth)").option("--force", "Proceed even if the API uses oauth2/openIdConnect target auth (you configure target auth yourself later)").option("-y, --yes", "Skip confirmation prompts").option("--tenant <slug>", "Tenant (consumer namespace: portal, login, users) for the new proxy; omit to be asked").option("--apikey <key>", "Use this API key for the proxy's door (api_key proxies). Without it, apichat detects the door and asks \u2014 or runs the consumer login for OAuth doors.").option("--xenduserid <id>", "Assert this end-user id (X-End-User-Id) \u2014 required by proxies with identified/pre-approved enforcement; you are asked for one when the proxy demands it.").option("--verbose", "Show the per-turn proxy curl trace (hidden by default)").option("-p, --prompt <question>", "One-shot question: answered through the external agent CLI after an MCP install, or by apichat itself (exits after answering when there is no TTY)").option("--install-mcp <cli>", "Install this proxy's MCP into an external agent CLI without asking: claude | codex. Also re-offers after an earlier decline.").option("--remove-mcp <cli>", "Disconnect this proxy from an external agent CLI (claude | codex): removes the MCP server and the standing instruction").action(action((project, opts) => {
14441
14684
  const looksLikeSource = !!project && (/^https?:\/\//i.test(project) || /\.(ya?ml|json)$/i.test(project) || import_fs5.default.existsSync(project));
14442
- const target = opts.target ?? (looksLikeSource ? project : void 0);
14443
- return runApichat({ ...opts, target, project: looksLikeSource ? void 0 : project, openapispec: opts.openapispec ?? opts.openapi });
14685
+ const target2 = opts.target ?? (looksLikeSource ? project : void 0);
14686
+ return runApichat({ ...opts, target: target2, project: looksLikeSource ? void 0 : project, openapispec: opts.openapispec ?? opts.openapi });
14444
14687
  }));
14445
14688
  var llm = program.command("llm").description("Manage a local LLM provider key for chat (optional \u2014 lifts model quality, bills your key)");
14446
14689
  llm.command("set-key").description("Store an LLM provider key locally (OpenRouter/Anthropic/DeepSeek/OpenAI)").argument("[key]", "The API key (omit to enter it hidden at a prompt)").option("--model <id>", "Model id to use with this key (e.g. anthropic/claude-haiku-4.5)").action(action((key, opts) => runLlmSetKey(key, opts)));
@@ -14458,7 +14701,7 @@ program.command("dev").description(`Put the code running on this machine behind
14458
14701
  try {
14459
14702
  const resolved = parseInt(port ?? opts.port, 10);
14460
14703
  if (Number.isNaN(resolved)) {
14461
- console.error(import_chalk63.default.red(`Invalid port: ${port ?? opts.port}`));
14704
+ console.error(import_chalk64.default.red(`Invalid port: ${port ?? opts.port}`));
14462
14705
  process.exit(1);
14463
14706
  }
14464
14707
  await runDev({ ...opts, port: resolved });
@@ -14521,7 +14764,14 @@ var logs = program.command("logs").description("API Producer view \u2014 stream
14521
14764
  logs.command("list").description("API Producer view \u2014 browse EVERY caller's past requests by day, then open one to see its 4 views").argument("<project>", "Project name or id").argument("[version]", "API version (defaults to the first match)").option("--tenant <slug>", "Tenant to read (auto when one is attached; prompted otherwise)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit ALL matching rows as NDJSON, newest first (non-interactive)").option("--plain", "Print day-grouped rows instead of prompting (automatic when piped)").option("--team <id|name>", "Team the project is in (defaults to active team)").action(action((project, version2, _o, cmd) => runLogsList(project, version2, cmd.optsWithGlobals())));
14522
14765
  program.command("config").description("Browse and change every proxy setting & feature (interactive; git-config-style get/set)").argument("[project]", "Project name or id (omit to pick interactively)").argument("[key]", "Setting key, e.g. throttling.proxyQuota (omit for the menu)").argument("[value]", "New value (omit to read the current value)").option("--list", "Print every setting and its current value").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version (defaults to the first match)").option("--json", "Machine-readable output (with --list or a key read)").action(action((project, key, value, opts) => runConfig(project, key, value, opts)));
14523
14766
  program.command("target").description("Change where a proxy forwards requests").argument("<project>", "Project name or id").requiredOption("--url <url>", "Target URL to forward to").option("--env <env>", "Environment to scope the target to (e.g. prod, dev)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version (defaults to the first match)").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTargetSet(project, opts)));
14524
- program.command("throttle").description("Set rate limits and quotas for a proxy").argument("<project>", "Project name or id").option("--rate <n>", "User rate limit (requests/sec)").option("--end-user-rate <n>", "Per-end-user rate limit (requests/sec)").option("--quota <n>", "Proxy quota (requests/period)").option("--period <p>", "Quota period: daily | weekly | monthly").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runThrottleSet(project, opts)));
14767
+ program.command("throttle").description("Set rate limits and quotas for a proxy (works in an anonymous workspace too; new proxies start at 10/s per person and 10,000/day)").argument("<project>", "Project name or id").option("--rate <n>", "Requests/sec per person (or per key when a call names no person). Anonymous workspaces: at most 10").option("--end-user-rate <n>", "Same as --rate (kept for older scripts)").option("--quota <n>", "Proxy quota (requests/period)").option("--period <p>", "Quota period: daily | weekly | monthly").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runThrottleSet(project, opts)));
14768
+ var transform = program.command("transform").description("Change requests on their way to your backend \u2014 e.g. send it a secret header so it only answers to the proxy");
14769
+ transform.command("list").description("List a proxy's transforms").argument("<project>", "Project name or id").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTransformList(project, opts)));
14770
+ transform.command("set-header").description("Send a header with a fixed value to the upstream on every call (replaces the value if already set)").argument("<project>", "Project name or id").argument("<header>", "Header name, e.g. x-target-api-key").option("--value <value>", "The header value").option("--value-env <VAR>", "Read the value from this environment variable (keeps secrets out of shell history)").option("--secret", "Store the value encrypted and never show it again").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").addHelpText("after", `
14771
+ Example \u2014 lock your backend to the proxy:
14772
+ export TARGET_SERVER_SECRET=$(openssl rand -hex 24) # also give it to your backend
14773
+ apiblaze transform set-header shop x-target-api-key --value-env TARGET_SERVER_SECRET --secret`).action(action((project, header, opts) => runTransformSetHeader(project, header, opts)));
14774
+ transform.command("remove").description("Remove a transform by name, id, or header name").argument("<project>", "Project name or id").argument("<rule>", "Rule name, id, or the header a set-header rule sets").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, rule, opts) => runTransformRemove(project, rule, opts)));
14525
14775
  program.command("rename").description("Change a proxy's display name").argument("<project>", "Project name or id").requiredOption("--display-name <name>", "New human-friendly display name").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runRename(project, opts)));
14526
14776
  var domain = program.command("domain").description("Use your own domain for your API, MCP server, developer portal or login pages (billed monthly)");
14527
14777
  domain.command("add").description("Add your own domain (shows the DNS records to set)").argument("[project]", "Project name or id (for api / mcp domains; omit with --tenant for portal / auth)").requiredOption("--domain <host>", "Custom hostname to add, e.g. api.example.com").option("--kind <kind>", "api (default) | mcp \u2014 on a project; portal | auth \u2014 on a --tenant").option("--tenant <slug>", "portal/auth: the tenant the domain belongs to. api/mcp: pin the domain to one of the project's tenants").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").addHelpText("after", `
@@ -14562,11 +14812,11 @@ admins.command("remove").description("Remove a tenant admin").argument("<email>"
14562
14812
  program.addCommand(admins);
14563
14813
  program.command("iam").description("Turn users & groups on/off for a proxy's tenant (identified calls get their user's groups applied)").argument("<project>", "Project name or id").argument("<state>", "on | off").option("--tenant <name>", "Tenant to toggle (defaults to the project's tenant)").option("--team <id|name>", "Team (defaults to active team, or your anonymous workspace)").option("--apiversion <version>", "API version (defaults to the first)").option("--json", "Output machine-readable JSON").action(action((project, state, opts) => runIamToggle(project, state, opts)));
14564
14814
  program.command("identified").description("Require calls to identify their end user (X-End-User-Id from a backend key, or a login token \u2014 a client key alone cannot) \u2014 or allow unattributed calls again").argument("<project>", "Project name or id").argument("<mode>", "require | allow-anon").option("--team <id|name>", "Team (defaults to active team, or your anonymous workspace)").option("--apiversion <version>", "API version (defaults to the first)").option("--json", "Output machine-readable JSON").action(action((project, mode, opts) => runIdentifiedToggle(project, mode, opts)));
14565
- var apikeys = new import_commander.Command("apikeys").description("Producer control-plane API keys (list, then offer to create one)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runApikeysMenu(opts)));
14815
+ var apikeys = new import_commander.Command("apikeys").description("API keys \u2014 for your team (control plane), or with `mint --tenant` for your API's callers: a backend key, a client key (--client) or a key bound to one person (--for)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runApikeysMenu(opts)));
14566
14816
  apikeys.command("list").description("List control-plane API keys in your team").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts, cmd) => runKeyList({ ...cmd.parent?.opts(), ...opts })));
14567
14817
  apikeys.command("mint").description("Create a control-plane API key (secret shown once) \u2014 or, with --tenant, a consumer key in that tenant: a backend key (default), a client key (--client) or a person-bound key (--for)").option("--desc <text>", "Description").option("--expires-days <n>", "Expiry in days (default 90 server-side)").option("--tenant <slug>", "Mint a CONSUMER key in this tenant instead of a control-plane key").option("--backend", `Backend key (default). ${HOLDER_HELP.backend}`).option("--client", `Client key. ${HOLDER_HELP.client} Needs --tenant.`).option("--for <email|id>", "Issue this key to a named person \u2014 calls made with it count as that person, and ownership rules trust it. The Users & Groups widget's admin lane accepts only such a key, one per admin. Needs --tenant.").option("--env <name>", "Environment the consumer key is valid for (with --tenant; default dev)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts, cmd) => runKeyMint({ ...cmd.parent?.opts(), ...opts })));
14568
14818
  apikeys.command("revoke").description("Revoke a control-plane API key").argument("<keyId>", "Key id (see `apikeys list`)").option("--team <id|name>", "Team (defaults to active team)").action(action((keyId, opts, cmd) => runKeyRevoke(keyId, { ...cmd.parent?.opts(), ...opts })));
14569
- program.addCommand(apikeys, { hidden: true });
14819
+ program.addCommand(apikeys);
14570
14820
  var op = new import_commander.Command("op").description("Operator menu (platform operators only)").argument("[action]", "latency | billing | mark | credits | residue | sweep | test | perf (omit for the menu)").argument("[view]", "for `latency`: slow | llm | compare | grades \xB7 for `billing`: doors | meter \xB7 for `mark`: the change name, quoted").option("-y, --yes", "Skip the sweep confirmation prompt").option("--json", "Output machine-readable JSON").option("--hours <n>", "Look-back window in hours (default 1)").option("--team <id>", "Filter to one team").option("--only <ids>", "for `test`: comma-separated spec/gut ids or prefixes").option("--group <name>", "for `test`: only specs whose group matches").option("--skip-guts", "for `test`: contract specs only").option("--guts-only", "for `test`: server-side gut checks only").option("--fail-fast", "for `test`: stop at the first failure").option("--timeout <s>", "for `test`: seconds to wait per check (default 150)").option("--perf", "for `test`: run the performance gate instead of the suite (fixed-rate load on a sandbox proxy)").option("--consumer", "for `test`: probe the data plane with the stored `consumer login` token instead of the suite").option("--path <p>", "for `test --consumer`: request path on the proxy (default /get)").option("--proxy <name@version>", "for `test --consumer`: the OAuth-door proxy to knock on (default: the sandbox proxy for a sandbox-tenant session)").option("--rps <n>", "for `test --perf`: requests per second (default 100, max 300)").option("--seconds <n>", "for `test --perf`: how long to send for (default 60, max 300)").option("--url <url>", "for `test --perf`: load this fixture URL instead of provisioning one (with --key; nothing is torn down)").option("--max-p50 <ms>", "for `test --perf`: client p50 threshold (default 250)").option("--max-p95 <ms>", "for `test --perf`: client p95 threshold (default 800)").option("--max-error-rate <pct>", "for `test --perf`: error-rate threshold in percent (default 0.5)").option("--max-gateway-p50 <ms>", "for `test --perf`: apiblaze-overhead p50 threshold (default 60)").option("--max-gateway-p95 <ms>", "for `test --perf`: apiblaze-overhead p95 threshold (default 250)").option("--project <name>", "Filter to one proxy").option("--api-version <v>", "Filter to one API version").option("--tenant <name>", "Filter to one tenant").option("--route <path>", "Filter to one route").option("--environment <env>", "Filter to one environment").option("--status-class <c>", "Filter to a status class (2xx/4xx/5xx)").option("--min-ms <n>", "For `latency slow`: minimum duration (default 1000)").option("--days <n>", "For `latency grades`: how many days back (default 7)").option("--excellent <ms>", "Grade threshold: under this is excellent (default 250)").option("--okay <ms>", "Grade threshold: under this is okay (default 500)").option("--bad <ms>", "Grade threshold: under this is bad, at/over is terrible (default 1000)").option("--key <key>", "For `billing meter`: a data-plane API key for the sandbox proxy (without it the served-request checks are SKIPPED) \xB7 for `test --perf --url`: the fixture's API key").option("--count <n>", "For `billing meter`: requests per check (default 3, max 10)").option("--mcp-host <host>", "For `billing meter`: an MCP host to check tools/call is charged once").action(action((sub, view, opts) => runOp(sub, opts, view))).addHelpText("after", () => `
14571
14821
  ${renderOpCommands()}
14572
14822
  `);
@@ -14577,10 +14827,10 @@ spec.command("set").description("Replace the stored OpenAPI spec from a local fi
14577
14827
  spec.command("carry").description("Copy a route's saved rules to the path a spec rename moved traffic to").argument("<project>", "Project name or id").requiredOption("--method <method>", "HTTP method of the route (GET, POST, ...)").requiredOption("--from <path>", "Old route path, e.g. /owners/{ownerId}").requiredOption("--to <path>", "New route path, e.g. /customers/{customerId}").option("--delete-source", "Also delete the old rules after carrying").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runSpecCarry(project, opts)));
14578
14828
  spec.command("delete-rule").description("Delete the saved rules for a route (e.g. an orphan left by a spec change)").argument("<project>", "Project name or id").requiredOption("--method <method>", "HTTP method of the route").requiredOption("--route <path>", "Route path, e.g. /owners/{ownerId}").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runSpecDeleteRule(project, opts)));
14579
14829
  var HELP_GROUPS = [
14580
- { title: "Chat", commands: ["docs", "apichat", "agent"] },
14581
- { title: "Setup", commands: ["dev", "create", "integration", "skill", "login", "init", "sidecar", "claim", "team", "whoami", "logout"] },
14830
+ { title: "Chat", commands: ["docs", "apichat", "agent", "llm"] },
14831
+ { title: "Setup", commands: ["dev", "create", "integration", "skill", "login", "init", "sidecar", "claim", "team", "whoami", "logout", "flush"] },
14582
14832
  { title: "Recipes \u2014 install a working setup, or publish yours", commands: ["search", "show", "install", "publish", "withdraw"] },
14583
- { title: "Control plane commands", commands: ["config", "projects", "logs", "tenant", "group", "admins", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
14833
+ { title: "Control plane commands", commands: ["config", "projects", "logs", "tenant", "group", "admins", "apikeys", "iam", "identified", "preapprove", "allowoauthregistration", "rule", "domain", "delete", "job", "target", "throttle", "transform", "rename", "spec", "export"] },
14584
14834
  { title: "Data plane commands", commands: [
14585
14835
  { parent: "consumer", sub: "login" },
14586
14836
  { parent: "consumer", sub: "apikeys" },
@@ -14606,7 +14856,7 @@ function groupedCommandHelp() {
14606
14856
  const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
14607
14857
  return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
14608
14858
  }).filter(Boolean).join("\n");
14609
- return `${import_chalk63.default.bold(g.title)}
14859
+ return `${import_chalk64.default.bold(g.title)}
14610
14860
  ${rows}`;
14611
14861
  }).join("\n\n");
14612
14862
  }
@@ -14617,7 +14867,7 @@ program.configureHelp({
14617
14867
  }
14618
14868
  });
14619
14869
  program.addHelpText("after", () => `
14620
- ${import_chalk63.default.bold("Don't like reading docs?")} Run ${import_chalk63.default.bgCyan.black.bold(` npx ${B.cli} docs `)} ${import_chalk63.default.cyan(`\u2014 ask an AI chatbot about ${B.product}, and let it act on your account.`)}
14870
+ ${import_chalk64.default.bold("Don't like reading docs?")} Run ${import_chalk64.default.bgCyan.black.bold(` npx ${B.cli} docs `)} ${import_chalk64.default.cyan(`\u2014 ask an AI chatbot about ${B.product}, and let it act on your account.`)}
14621
14871
 
14622
14872
  ${groupedCommandHelp()}
14623
14873
 
@@ -14653,14 +14903,14 @@ async function recoverStaleTeam() {
14653
14903
  const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
14654
14904
  const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
14655
14905
  if (!linked) {
14656
- console.error(import_chalk63.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
14906
+ console.error(import_chalk64.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
14657
14907
  return;
14658
14908
  }
14659
14909
  if (linked.teamId === creds.teamId) return;
14660
14910
  const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
14661
14911
  delete next.activeTenant;
14662
14912
  saveCredentials(next);
14663
- console.error(import_chalk63.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk63.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
14913
+ console.error(import_chalk64.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk64.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
14664
14914
  } catch {
14665
14915
  }
14666
14916
  }
@@ -14668,16 +14918,33 @@ async function printError(err) {
14668
14918
  if (err instanceof ApiError) {
14669
14919
  const data = err.body;
14670
14920
  const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
14671
- console.error(import_chalk63.default.red(`
14921
+ console.error(import_chalk64.default.red(`
14672
14922
  API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
14673
14923
  if (err.status === 403 || err.status === 404) {
14674
14924
  await recoverStaleTeam();
14675
14925
  }
14676
14926
  } else if (err instanceof Error) {
14677
- console.error(import_chalk63.default.red(`
14927
+ console.error(import_chalk64.default.red(`
14678
14928
  Error: ${err.message}`));
14679
14929
  } else {
14680
- console.error(import_chalk63.default.red("\nUnknown error"));
14930
+ console.error(import_chalk64.default.red("\nUnknown error"));
14681
14931
  }
14682
14932
  }
14933
+ function hintStaleCacheOnUnknownOption(cmd) {
14934
+ cmd.exitOverride((err) => {
14935
+ if (err.code === "commander.unknownOption") {
14936
+ console.error(import_chalk64.default.yellow(
14937
+ `
14938
+ This is ${B.cli} v${version}. If that option is documented but this build
14939
+ does not have it, your npx cache is serving an older copy \u2014 re-run as:
14940
+ `
14941
+ ));
14942
+ console.error(import_chalk64.default.cyan(` npx ${B.cli}@latest ${process.argv.slice(2).join(" ")}
14943
+ `));
14944
+ }
14945
+ process.exit(err.exitCode);
14946
+ });
14947
+ for (const sub of cmd.commands) hintStaleCacheOnUnknownOption(sub);
14948
+ }
14949
+ hintStaleCacheOnUnknownOption(program);
14683
14950
  program.parse(process.argv);