apiblaze 0.21.44 → 0.21.46

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 +671 -525
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__
34
34
  var version;
35
35
  var init_package = __esm({
36
36
  "package.json"() {
37
- version = "0.21.44";
37
+ version = "0.21.46";
38
38
  }
39
39
  });
40
40
 
@@ -2996,12 +2996,12 @@ function openBrowser2(url) {
2996
2996
  } catch {
2997
2997
  }
2998
2998
  }
2999
- async function deviceLogin(clientId, scope, onPrompt, resource) {
2999
+ async function deviceLogin(clientId, scope2, onPrompt, resource) {
3000
3000
  const { verifier, challenge } = pkce();
3001
3001
  const startRes = await fetch(`${AUTH_BASE}/device_authorization`, {
3002
3002
  method: "POST",
3003
3003
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
3004
- body: new URLSearchParams({ client_id: clientId, scope, code_challenge: challenge, code_challenge_method: "S256", ...resource ? { resource } : {} })
3004
+ body: new URLSearchParams({ client_id: clientId, scope: scope2, code_challenge: challenge, code_challenge_method: "S256", ...resource ? { resource } : {} })
3005
3005
  });
3006
3006
  const start = await startRes.json().catch(() => ({}));
3007
3007
  if (!startRes.ok) {
@@ -8923,7 +8923,7 @@ async function claimJourney(work, checks, env) {
8923
8923
  if (res.claimed) {
8924
8924
  try {
8925
8925
  await admin({ method: "DELETE", path: `/teams/${encodeURIComponent(teamId)}`, summary: "delete the promoted demo team" });
8926
- for (let i = 0; i < 60 && !gone; i++) {
8926
+ for (let i = 0; i < 180 && !gone; i++) {
8927
8927
  try {
8928
8928
  await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}` });
8929
8929
  } catch (e) {
@@ -8931,7 +8931,7 @@ async function claimJourney(work, checks, env) {
8931
8931
  }
8932
8932
  if (!gone) await new Promise((r) => setTimeout(r, 1e3));
8933
8933
  }
8934
- detail = gone ? "cascade done" : "still present after 60 s";
8934
+ detail = gone ? "cascade done" : "still present after 180 s";
8935
8935
  } catch (e) {
8936
8936
  detail = `delete \u2192 ${fail7(e).status} ${fail7(e).error}`;
8937
8937
  }
@@ -8949,7 +8949,7 @@ async function runOpAnon(o = {}) {
8949
8949
  const log4 = (s) => {
8950
8950
  if (!o.json) console.log(s);
8951
8951
  };
8952
- log4(import_chalk70.default.bold(`${B.cli} op anon \u2014 a first-time visitor's demo, against production`));
8952
+ log4(import_chalk71.default.bold(`${B.cli} op anon \u2014 a first-time visitor's demo, against production`));
8953
8953
  try {
8954
8954
  await journey("first", work, home, checks);
8955
8955
  const env = await journey("rerun", work, home, checks);
@@ -8972,18 +8972,18 @@ async function runOpAnon(o = {}) {
8972
8972
  checks.push({ id: "stop", ok: stop.code === 0 && portsFree, detail: `exit ${stop.code}${portsFree ? "" : " \xB7 ports still answering"}` });
8973
8973
  import_fs10.default.rmSync(root, { recursive: true, force: true });
8974
8974
  }
8975
- for (const c of checks) log4(` ${c.skipped ? import_chalk70.default.yellow("\u2013") : c.ok ? import_chalk70.default.green("\u2713") : import_chalk70.default.red("\u2717")} ${c.id.padEnd(24)} ${import_chalk70.default.dim(c.detail)}`);
8975
+ for (const c of checks) log4(` ${c.skipped ? import_chalk71.default.yellow("\u2013") : c.ok ? import_chalk71.default.green("\u2713") : import_chalk71.default.red("\u2717")} ${c.id.padEnd(24)} ${import_chalk71.default.dim(c.detail)}`);
8976
8976
  const failed = checks.filter((c) => !c.ok).length;
8977
8977
  const skipped = checks.filter((c) => c.skipped).length;
8978
8978
  const passed = checks.length - failed - skipped;
8979
8979
  const secs = Math.round((Date.now() - t0) / 1e3);
8980
8980
  const tail = `${skipped ? `, ${skipped} skipped` : ""} (${secs} s)`;
8981
- log4(failed ? import_chalk70.default.red(`${passed}/${checks.length - skipped} passed, ${failed} failed${tail}`) : import_chalk70.default.green(`${passed}/${checks.length - skipped} passed${tail}`));
8981
+ log4(failed ? import_chalk71.default.red(`${passed}/${checks.length - skipped} passed, ${failed} failed${tail}`) : import_chalk71.default.green(`${passed}/${checks.length - skipped} passed${tail}`));
8982
8982
  if (o.json) process.stdout.write(JSON.stringify({ ok: !failed, checks }, null, 2) + "\n");
8983
8983
  if (failed) process.exitCode = 1;
8984
8984
  return { ok: !failed, checks };
8985
8985
  }
8986
- var import_fs10, import_os3, import_path7, import_child_process6, import_chalk70, SECRET, SANDBOX_TEAM;
8986
+ var import_fs10, import_os3, import_path7, import_child_process6, import_chalk71, SECRET, SANDBOX_TEAM;
8987
8987
  var init_op_anon = __esm({
8988
8988
  "src/commands/op-anon.ts"() {
8989
8989
  "use strict";
@@ -8991,7 +8991,7 @@ var init_op_anon = __esm({
8991
8991
  import_os3 = __toESM(require("os"));
8992
8992
  import_path7 = __toESM(require("path"));
8993
8993
  import_child_process6 = require("child_process");
8994
- import_chalk70 = __toESM(require("chalk"));
8994
+ import_chalk71 = __toESM(require("chalk"));
8995
8995
  init_brand();
8996
8996
  init_api();
8997
8997
  init_admin();
@@ -9004,7 +9004,7 @@ var init_op_anon = __esm({
9004
9004
  // src/index.ts
9005
9005
  var import_fs11 = __toESM(require("fs"));
9006
9006
  var import_commander = require("commander");
9007
- var import_chalk72 = __toESM(require("chalk"));
9007
+ var import_chalk73 = __toESM(require("chalk"));
9008
9008
  init_package();
9009
9009
  init_cli_run_context();
9010
9010
  init_version_guard();
@@ -9108,13 +9108,26 @@ async function defaultName(openapi, teamId) {
9108
9108
  }
9109
9109
  return randomProxyName();
9110
9110
  }
9111
+ function saltedName(name) {
9112
+ return `${name}${Math.random().toString(36).slice(2, 6)}`;
9113
+ }
9111
9114
  async function chooseName(opts, openapi, teamId, interactive2) {
9112
9115
  if (opts.name !== void 0) {
9113
9116
  const name = normalizeName2(opts.name);
9114
9117
  if (name.length < 3) throw new Error(`${cap(B.thingShort)} name must be at least 3 characters (letters and digits only).`);
9115
9118
  const check = await checkProxyName(name, teamId).catch(() => null);
9116
9119
  if (check && (!check.canUseProjectName || !check.canUseApiVersion)) {
9117
- throw new Error(`${cap(B.thingShort)} name "${name}" is not available${check.message ? ` \u2014 ${check.message}` : ""}.`);
9120
+ if (opts.auto || !interactive2) {
9121
+ for (let attempt = 0; attempt < 4; attempt++) {
9122
+ const candidate = saltedName(name);
9123
+ const again = await checkProxyName(candidate, teamId).catch(() => null);
9124
+ if (!again || again.canUseProjectName && again.canUseApiVersion) {
9125
+ console.log(import_chalk20.default.dim(` "${name}" is taken (names are global) \u2014 using "${candidate}" instead.`));
9126
+ return candidate;
9127
+ }
9128
+ }
9129
+ }
9130
+ throw new Error(`${cap(B.thingShort)} name "${name}" is not available${check.message ? ` \u2014 ${check.message}` : ""}. Omit --name and a free one is chosen for you.`);
9118
9131
  }
9119
9132
  return name;
9120
9133
  }
@@ -9972,7 +9985,6 @@ their own app).
9972
9985
  }
9973
9986
  function shipMarkdown() {
9974
9987
  const legacy = legacySkillMarkdown();
9975
- const at = legacy.indexOf("## Adopt");
9976
9988
  const backendRules = `## What their backend does \u2014 and nothing else
9977
9989
 
9978
9990
  The gateway does sign-in, keys, tenancy and single-record ownership. The backend does only:
@@ -9993,7 +10005,7 @@ No code yet? Write the smallest plain backend that does exactly that, plus its \
9993
10005
  return withLatest(`# Put ${B.product} in front of their app
9994
10006
 
9995
10007
  ${backendRules}${blockedMarkdown()}
9996
- ${at >= 0 ? legacy.slice(at) : legacy}`);
10008
+ ${legacy}`);
9997
10009
  }
9998
10010
  function skillMarkdown() {
9999
10011
  return `${heroMarkdown()}
@@ -10003,121 +10015,7 @@ function legacySkillMarkdown() {
10003
10015
  const P = B.product;
10004
10016
  const cli = B.cli;
10005
10017
  const LIB = "apiblaze";
10006
- const other = BRAND === "mcpblaze" ? "APIblaze" : "MCPblaze";
10007
- const doors = BRAND === "mcpblaze" ? `${P} ships your code to agents (an MCP server); ${other} ships the same code to users (API keys + sign-in).` : `${P} ships your code to users (API keys + sign-in); ${other} ships the same code to agents (an MCP server).`;
10008
- return `---
10009
- name: ${SKILL_NAME}
10010
- description: Use when the user asks what ${P} (or ${cli}) does or can do for them; says yes to a demo or asks to see / try / demo it; wants to ship, share, expose or make an API public; says "serve my backend behind a proxy and MCP"; wants auth, API keys, sign-in, multi-tenant registration, users and groups, rate limits (throttling), quotas or spend caps; wants agents to use the API through an MCP; or says users can reach each other's records or admin functions. Puts the ${P} gateway in front of the API instead of hand-rolling auth and tenancy in the backend.
10011
- ---
10012
-
10013
- # Ship safely with ${P}
10014
-
10015
- One gateway, two doors. ${doors}
10016
- Same rules, one command. Nothing about auth, tenancy or ownership goes into the backend's
10017
- business logic: the gateway sits in front of the code and enforces it.
10018
-
10019
- ## Which mode \u2014 decide this first
10020
-
10021
- - They ask what ${P} does or can do \u2192 **Explain**. Glance at the workspace first (see
10022
- Explain), answer, then offer either the demo or their own app. Run no \`npx ${cli}\`
10023
- command \u2014 the whole answer is in this file.
10024
- - They say yes to the demo, or ask to see / try / demo it \u2192 **Demo app**. It lives in its own
10025
- folder and never touches their code.
10026
- - They say yes to "can I read the code" \u2192 **Adopt**: scan their app, ask only what the scan
10027
- could not find, then ship it through the Ship steps.
10028
- - They have a backend, just wrote one, or ask you to BUILD one and then ship it \u2192 **Ship**.
10029
- No code yet? First write the smallest plain backend that serves the resources they named,
10030
- plus its \`openapi.yaml\`, start it on their port, then continue at Ship \xA71. Do NOT build
10031
- the Demo app for this. The gateway does auth, tenancy and item ownership; the backend does
10032
- only these three things:
10033
- - stamp each new row's \`tenant\` with the \`x-abz-tenant-id\` header and its \`owner\` with
10034
- the \`x-abz-user-id\` header;
10035
- - filter every collection GET to rows whose \`tenant\` AND \`owner\` match those headers (the
10036
- gateway's rules lock single records, never lists \u2014 a list route has no one record to
10037
- check). Both, always: a user id is not unique across tenants ("ana" at one customer and
10038
- "ana" at another are two people);
10039
- - answer 401 to any call whose \`x-target-api-key\` is not \`process.env.TARGET_SERVER_SECRET\`
10040
- (generate it with \`openssl rand -hex 24\`, put it in \`.env.local\`, start the backend with
10041
- it; Ship \xA73b makes the proxy send it). If they say "use
10042
- ${cli} on this" or "ship it with ${cli}" and point at a backend, skip any pitch and run it.
10043
-
10044
- Tie-breaks: a question ("what can it do", "what is it") is always **Explain**, whatever else the
10045
- message names. Mentioning \`npx ${cli} skills\` is NOT a request to run anything \u2014 it is the
10046
- command that installed this file. If a message fits two modes, the earliest bullet wins.
10047
-
10048
- ## Explain
10049
-
10050
- Say this, in these words or close to them, then stop:
10051
-
10052
- "${P} puts a gateway in front of your backend in one command: a proxy (every request passes
10053
- through it first) and an MCP server (the same API, callable by AI agents like Claude). No auth
10054
- or ownership logic goes into your code \u2014 your backend only has to trust one header. Each of
10055
- your customers \u2014 a tenant \u2014 gets their own API key, created from a widget you drop into your
10056
- site. People and agents sign in with hosted GitHub, or your own OAuth app. Each user can only
10057
- touch the records they created; those rules come from your OpenAPI spec (the file listing your
10058
- endpoints), and if you have none I draft one from your routes and show it to you first. One
10059
- more command sets a rate limit, a daily quota and a spend cap. In dev it tunnels to localhost;
10060
- in prod it points at your deploy."
10061
-
10062
- Before answering, look at the workspace \u2014 file names and \`package.json\` only, no reading of
10063
- code yet. It "has an app" if ANY of these is present: an \`openapi.*\` / \`swagger.*\` file; a
10064
- server framework (express, fastify, hono, koa, nest, fastapi, flask, django, rails, gin, chi,
10065
- net/http); or a frontend (next, react, vue, svelte, remix, nuxt) whose \`.env*\` file NAMES an
10066
- API base-URL variable (\`API_URL\`, \`NEXT_PUBLIC_API_URL\`, \`VITE_API_URL\`, or similar) \u2014
10067
- \`grep -l\` the env files, open nothing else. Then end with exactly one of these two lines:
10068
-
10069
- - No app \u2192 **"Would you like me to spin up a demo app?"**
10070
- - An app \u2192 **"I see you have an app already coded. Can I read the code to see how to ready it
10071
- for human and agent traffic with ${P}?"**
10072
-
10073
- ## Demo app \u2014 the same reservation system, before and after
10074
-
10075
- One Next.js app on port 3000. Two routes, same UI, same data: \`/before\` is how most apps
10076
- ship; \`/\` is the same thing behind ${P}. Narrate each step in one line and show every
10077
- \`npx ${cli}\` command \u2014 the commands are the product. Everything goes in \`${cli}-demo/\`.
10078
-
10079
- 1. **Scaffold.** \`npx create-next-app@latest ${cli}-demo\` (TypeScript, App Router). Then
10080
- \`npm i hono ${LIB}\`.
10081
- 2. **The backend.** Hono mounted at \`app/api/[[...route]]/route.ts\` (\`import { handle } from
10082
- 'hono/vercel'\`). Three resources \u2014 restaurants, tables, reservations \u2014 in an in-memory Map,
10083
- every row stamped with \`owner\` = the \`x-abz-user-id\` header (${P} sets it on every call it
10084
- forwards). Every route requires \`X-Api-Secret: demo-secret\` OR an \`x-abz-user-id\` header \u2014
10085
- the first is how /before gets in, the second is how the gateway gets in. Write
10086
- \`openapi.yaml\` at the repo root: every path and method, and the \`id\` each POST returns \u2014
10087
- the ownership rules are read from it.
10088
- 3. **/before.** \`app/before/page.tsx\`: list and create reservations through one server route,
10089
- \`app/before/api/route.ts\`, which holds \`demo-secret\` and calls \`/api/...\` directly. Put the
10090
- caption on the page: "Before \u2014 one shared secret, anyone holding it can do anything."
10091
- 4. **Put ${P} in front.** In the BACKGROUND, and leave it running \u2014 it is the tunnel:
10092
- \`npx ${cli} dev --port 3000 --openapi ./openapi.yaml --name demo --auto\`
10093
- It prints the API URL, the MCP URL, the tenant, and the bootstrap key ONCE. Write them to
10094
- \`.env.local\` as \`APIBLAZE_URL\`, \`APIBLAZE_SERVER_KEY\`, \`APIBLAZE_TENANT\`. \`--auto\` locks
10095
- all three resources to their creator \u2014 say so.
10096
- 5. **Users and groups.** Turn them on and make the group the demo is about:
10097
- \`npx ${cli} iam demo on\`
10098
- \`npx ${cli} group create reservationists --tenant <tenant>\`
10099
- \`npx ${cli} group add-user <the demo user> reservationists --tenant <tenant>\`
10100
- \`npx ${cli} admins add <the user's email> --tenant <tenant>\` (so the widget has an admin).
10101
- Optional, billed per turn: \`npx ${cli} rule demo "only members of reservationists can create
10102
- or change reservations"\`. If the user is logged out or their wallet has nothing available
10103
- (\`npx ${cli} billing balance\`), keep the free creator-lock rules and say so \u2014 do not pretend
10104
- the group rule applied.
10105
- 6. **/.** \`app/page.tsx\`: the same UI, plus a **user picker** (ana \xB7 ben \xB7 admin \u2014 a select, no
10106
- real login; the demo is about authorization, not building sign-in). Its server route calls
10107
- \`process.env.APIBLAZE_URL\` with \`X-API-Key: APIBLAZE_SERVER_KEY\` and \`X-End-User-Id: <the
10108
- picked user>\`. Add the three widgets from \`${LIB}/react\`, each with its server route:
10109
- - \`<ApiKeyWidget />\` + \`app/api/apiblaze/keys/route.ts\` (\`createApiblazeKeys\` from \`${LIB}/server\`)
10110
- - \`<UsersGroupsWidget />\` + \`app/api/apiblaze/groups/route.ts\` (\`createApiblazeGroups\` \u2014
10111
- the keys route with one word changed)
10112
- - \`<ChatWidget />\` + \`app/api/apiblaze/chat/route.ts\` (\`createApiblazeChat\`)
10113
- Exact snippets for the keys and chat pair: \`npx ${cli} integration demo --stack nextjs\`.
10114
- Caption: "After \u2014 a key per tenant, every call names its user, and ${P} decides."
10115
- 7. **Prove it, in the browser.** \`npm run dev\`, open \`http://localhost:3000/before\` then
10116
- \`http://localhost:3000/\`. On \`/\`: create a reservation as ana, switch to ben, try to cancel
10117
- it \u2014 show the 403 in the UI. Open the groups widget: reservationists is there. Ask the chat
10118
- widget to list reservations. Then say what each \`npx ${cli}\` command did, in order.
10119
-
10120
- ## Adopt \u2014 reading their code first
10018
+ return `## Adopt \u2014 reading their code first
10121
10019
 
10122
10020
  They said yes. Scan properly now (routes, env files, auth, network calls), classify, then ask
10123
10021
  ONLY for what the scan could not find \u2014 one short list of questions, not an interview.
@@ -10178,12 +10076,14 @@ ONLY for what the scan could not find \u2014 one short list of questions, not an
10178
10076
  the report. Only ask "where do your users sign in today?" when they mention an existing
10179
10077
  login. Their own OAuth app \u2192 \`--apikey --oauth '{"provider":"auth0","clientId":"\u2026","clientSecret":"\u2026"}'\`
10180
10078
  (both doors: keys for tenants, their login for people and agents). Shapes below.
10181
- 3. **Local code** \u2192 \`npx ${cli} dev --port <port> --openapi ./openapi.yaml --name <name> --auto <door flags>\`,
10079
+ 3. **Local code** \u2192 \`npx ${cli} dev --port <port> --openapi ./openapi.yaml --auto <door flags>\`,
10182
10080
  left running \u2014 it is the tunnel. Start it as its own detached process, never \`cmd &\` inside
10183
10081
  a shell call that ends: \`nohup npx ${cli} dev \u2026 > ${cli}-dev.log 2>&1 < /dev/null &\`, then
10184
- read the log. If it stops, re-run the SAME command: the same \`--name\` reattaches and
10185
- creates nothing. Never pick a new name to get it back \u2014 that is a second proxy. **Deployed code** \u2192
10186
- \`npx ${cli} create --target <url-or-spec> --name <name> --auto <door flags>\`. Both print the
10082
+ read the log. Leave \`--name\` out: names are global, so a free one is chosen and printed
10083
+ (\`--name\` only for a name the user asked for; a taken one is salted). If the tunnel stops,
10084
+ run the \`--project <name>\` line it printed: that reattaches and creates nothing. Never start
10085
+ a new name to get it back \u2014 that is a second proxy. **Deployed code** \u2192
10086
+ \`npx ${cli} create --target <url-or-spec> --auto <door flags>\`. Both print the
10187
10087
  URL, the MCP URL (the "agents:" line) and what is locked. Run by you (no terminal), \`dev\`
10188
10088
  saves the bootstrap key to \`.env.local\` as \`APIBLAZE_SERVER_KEY\` and never prints it;
10189
10089
  \`create\` prints it ONCE \u2014 save it there yourself. Either way say only "saved your key to
@@ -11492,8 +11392,13 @@ function withOverride(cat, tool, flags) {
11492
11392
  for (const k of ["readOnly", "destructive", "idempotent", "confirm", "alwaysAsync"]) {
11493
11393
  if (typeof flags[k] === "boolean") patch[k] = flags[k];
11494
11394
  }
11395
+ if (typeof flags.title === "string") {
11396
+ const t = flags.title.trim().slice(0, 120);
11397
+ if (!t) throw new Error('--title needs some text (a short human label, e.g. "List pets").');
11398
+ patch.title = t;
11399
+ }
11495
11400
  if (Object.keys(patch).length === 0) {
11496
- throw new Error("Nothing to set. Pass --read-only, --destructive, --idempotent, --confirm, --always-async (or their --no-\u2026 forms).");
11401
+ throw new Error("Nothing to set. Pass --title, --read-only, --destructive, --idempotent, --confirm, --always-async (or their --no-\u2026 forms).");
11497
11402
  }
11498
11403
  return { ...cat, overrides: { ...cat.overrides ?? {}, [tool]: { ...cat.overrides?.[tool] ?? {}, ...patch } } };
11499
11404
  }
@@ -14132,15 +14037,15 @@ async function runDomainAdd(project, opts) {
14132
14037
  console.error(import_chalk50.default.red(`A ${kind} domain belongs to a proxy: apiblaze domain add <project> --kind ${kind} --domain ${opts.domain}`));
14133
14038
  process.exit(1);
14134
14039
  }
14135
- const scope = await scopeFor(project, opts);
14040
+ const scope2 = await scopeFor(project, opts);
14136
14041
  const spinner = (0, import_ora20.default)(`Registering ${opts.domain}...`).start();
14137
14042
  try {
14138
- const body = scope.kind === "project" ? { hostname: opts.domain, api_version: scope.apiVersion, kind, ...opts.tenant ? { tenant_name: opts.tenant.toLowerCase() } : {} } : { hostname: opts.domain, kind };
14043
+ const body = scope2.kind === "project" ? { hostname: opts.domain, api_version: scope2.apiVersion, kind, ...opts.tenant ? { tenant_name: opts.tenant.toLowerCase() } : {} } : { hostname: opts.domain, kind };
14139
14044
  const out = await admin({
14140
14045
  method: "POST",
14141
- path: scope.base,
14046
+ path: scope2.base,
14142
14047
  body,
14143
- summary: `Add custom ${kind} domain ${opts.domain} to ${scope.label}${scope.kind === "project" && opts.tenant ? ` (tenant ${opts.tenant})` : ""}`
14048
+ summary: `Add custom ${kind} domain ${opts.domain} to ${scope2.label}${scope2.kind === "project" && opts.tenant ? ` (tenant ${opts.tenant})` : ""}`
14144
14049
  });
14145
14050
  spinner.succeed(`Registered ${out?.hostname ?? opts.domain} (${describeKind(out)}). Add these DNS records, then run \`apiblaze domain status\`:`);
14146
14051
  if (opts.json) {
@@ -14157,11 +14062,11 @@ async function runDomainAdd(project, opts) {
14157
14062
  }
14158
14063
  }
14159
14064
  async function runDomainList(project, opts) {
14160
- const scope = await scopeFor(project, opts);
14065
+ const scope2 = await scopeFor(project, opts);
14161
14066
  const out = await admin({
14162
14067
  method: "GET",
14163
- path: scope.kind === "project" ? `${scope.base}?api_version=${encodeURIComponent(scope.apiVersion)}` : scope.base,
14164
- summary: `List custom domains for ${scope.label}`
14068
+ path: scope2.kind === "project" ? `${scope2.base}?api_version=${encodeURIComponent(scope2.apiVersion)}` : scope2.base,
14069
+ summary: `List custom domains for ${scope2.label}`
14165
14070
  });
14166
14071
  const domains = out?.domains ?? [];
14167
14072
  if (opts.json) {
@@ -14173,7 +14078,7 @@ async function runDomainList(project, opts) {
14173
14078
  return;
14174
14079
  }
14175
14080
  for (const d of domains) {
14176
- const pin = scope.kind === "project" && d.tenant ? import_chalk50.default.dim(` tenant ${d.tenant}`) : "";
14081
+ const pin = scope2.kind === "project" && d.tenant ? import_chalk50.default.dim(` tenant ${d.tenant}`) : "";
14177
14082
  console.log(` ${import_chalk50.default.bold(d.hostname)} ${statusColor2(d.status)} ${import_chalk50.default.dim(describeKind(d))}${pin} ${import_chalk50.default.dim(d.id ?? "")}`);
14178
14083
  }
14179
14084
  }
@@ -14182,10 +14087,10 @@ async function runDomainStatus(project, opts) {
14182
14087
  console.error(import_chalk50.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
14183
14088
  process.exit(1);
14184
14089
  }
14185
- const scope = await scopeFor(project, opts);
14090
+ const scope2 = await scopeFor(project, opts);
14186
14091
  const out = await admin({
14187
14092
  method: "GET",
14188
- path: `${scope.base}/${encodeURIComponent(opts.id)}/status`,
14093
+ path: `${scope2.base}/${encodeURIComponent(opts.id)}/status`,
14189
14094
  summary: `Check custom-domain status`
14190
14095
  });
14191
14096
  if (opts.json) {
@@ -14207,12 +14112,12 @@ async function runDomainRemove(project, opts) {
14207
14112
  console.error(import_chalk50.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
14208
14113
  process.exit(1);
14209
14114
  }
14210
- const scope = await scopeFor(project, opts);
14115
+ const scope2 = await scopeFor(project, opts);
14211
14116
  const spinner = (0, import_ora20.default)("Removing domain...").start();
14212
14117
  try {
14213
14118
  await admin({
14214
14119
  method: "DELETE",
14215
- path: `${scope.base}/${encodeURIComponent(opts.id)}`,
14120
+ path: `${scope2.base}/${encodeURIComponent(opts.id)}`,
14216
14121
  summary: `Remove custom domain ${opts.id}`
14217
14122
  });
14218
14123
  spinner.succeed("Removed. Billing for it stops now.");
@@ -14828,7 +14733,8 @@ function randomSecret() {
14828
14733
  (0, import_crypto2.randomFillSync)(bytes);
14829
14734
  return Buffer.from(bytes).toString("base64url");
14830
14735
  }
14831
- var PROVIDER_TYPES = ["google", "github", "microsoft", "facebook", "auth0", "other"];
14736
+ var PROVIDER_TYPES = ["google", "github", "microsoft", "facebook", "auth0", "oidc"];
14737
+ var PASSWORD_TYPE = "password";
14832
14738
  var DEFAULT_SCOPES2 = {
14833
14739
  google: "openid email profile",
14834
14740
  microsoft: "openid email profile",
@@ -14862,8 +14768,15 @@ async function providersMenu(cBase, clientLabel) {
14862
14768
  }]);
14863
14769
  if (act === "back") return;
14864
14770
  if (act === "add") {
14865
- const { type } = await inquirer3.prompt([{ type: "list", name: "type", message: "Provider:", choices: PROVIDER_TYPES }]);
14771
+ const showPassword = providers.some((p) => String(p.type).toLowerCase() === PASSWORD_TYPE) || process.env.APIBLAZE_ADVANCED === "1";
14772
+ const typeChoices = showPassword ? [...PROVIDER_TYPES, { name: "password (APIblaze-hosted username & password \u2014 reviewer / test accounts)", value: PASSWORD_TYPE }] : PROVIDER_TYPES;
14773
+ const { type } = await inquirer3.prompt([{ type: "list", name: "type", message: "Provider:", choices: typeChoices }]);
14866
14774
  let body;
14775
+ if (type === PASSWORD_TYPE) {
14776
+ await admin({ method: "POST", path: `${cBase}/providers`, body: { type: PASSWORD_TYPE }, summary: "Enable username & password sign-in" });
14777
+ console.log(import_chalk54.default.green(" Username & password sign-in enabled. Add people: apiblaze tenant password add <username>"));
14778
+ continue;
14779
+ }
14867
14780
  if (type === "github") {
14868
14781
  const { managed } = await inquirer3.prompt([{
14869
14782
  type: "confirm",
@@ -14877,7 +14790,7 @@ async function providersMenu(cBase, clientLabel) {
14877
14790
  const a = await inquirer3.prompt([
14878
14791
  { type: "input", name: "clientId", message: `${type} OAuth client id:`, validate: (s) => !!s.trim() || "required" },
14879
14792
  { type: "password", name: "clientSecret", mask: "*", message: `${type} OAuth client secret:`, validate: (s) => s.length >= 6 && s.length <= 200 || "6\u2013200 chars" },
14880
- ...type === "auth0" || type === "other" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : [],
14793
+ ...type === "auth0" || type === "oidc" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : [],
14881
14794
  { type: "input", name: "scopes", message: "Scopes:", default: DEFAULT_SCOPES2[type] ?? "" }
14882
14795
  ]);
14883
14796
  body = {
@@ -16323,9 +16236,230 @@ async function agentsMenu(proj2, opts) {
16323
16236
  else await runMcp(proj2.projectName, proj2.apiVersion, {});
16324
16237
  }
16325
16238
 
16326
- // src/commands/group.ts
16239
+ // src/commands/tenant-password.ts
16327
16240
  var import_chalk59 = __toESM(require("chalk"));
16328
16241
  var import_ora28 = __toESM(require("ora"));
16242
+ init_admin();
16243
+ init_api();
16244
+ init_resolve();
16245
+ init_auth();
16246
+ init_tenant_pick();
16247
+ async function teamHasTenant(teamId, slug) {
16248
+ const out = await admin({
16249
+ method: "GET",
16250
+ path: `/teams/${encodeURIComponent(teamId)}/tenants?limit=200&q=${encodeURIComponent(slug)}`,
16251
+ summary: `Look for tenant ${slug}`
16252
+ }).catch(() => null);
16253
+ const names = (out?.tenants ?? []).map((t) => typeof t === "string" ? t : t.tenant_name ?? "");
16254
+ return names.includes(slug);
16255
+ }
16256
+ async function teamForTenant(slug) {
16257
+ const { teamId: active } = await resolveTeam(void 0);
16258
+ if (await teamHasTenant(active, slug)) return active;
16259
+ const teams = (await getTeams().catch(() => [])).filter((t) => t.teamId !== active);
16260
+ for (const t of teams) {
16261
+ if (await teamHasTenant(t.teamId, slug)) {
16262
+ console.error(import_chalk59.default.dim(` (tenant ${slug} is on team ${t.name})`));
16263
+ return t.teamId;
16264
+ }
16265
+ }
16266
+ console.error(import_chalk59.default.red(`No tenant "${slug}" on any of your teams.`) + import_chalk59.default.dim(" (apiblaze tenant list)"));
16267
+ process.exit(1);
16268
+ }
16269
+ async function scope(opts, message, positional) {
16270
+ const named = (positional ?? opts.tenant)?.trim();
16271
+ let teamId;
16272
+ let tenant2;
16273
+ if (named) {
16274
+ tenant2 = named;
16275
+ teamId = await teamForTenant(named);
16276
+ } else {
16277
+ ({ teamId } = await resolveTeam(void 0));
16278
+ tenant2 = loadCredentials()?.activeTenant || null;
16279
+ if (!tenant2) tenant2 = await pickTenant(teamId, { message });
16280
+ }
16281
+ if (!tenant2) process.exit(1);
16282
+ return { teamId, tenant: tenant2, base: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}` };
16283
+ }
16284
+ async function serverSupportsPasswordProvider(s) {
16285
+ try {
16286
+ await admin({ method: "GET", path: `${s.base}/password-accounts`, summary: "Check server capabilities" });
16287
+ return true;
16288
+ } catch (e) {
16289
+ return !/\b404\b|not found/i.test(e instanceof Error ? e.message : String(e));
16290
+ }
16291
+ }
16292
+ async function defaultAppClient(s) {
16293
+ const auth = await admin({ method: "GET", path: `${s.base}/auth`, summary: "Read tenant login config" }).catch(() => null);
16294
+ let defaultId = auth?.default_app_client_id ?? null;
16295
+ if (!defaultId) {
16296
+ const clients = await admin({ method: "GET", path: `${s.base}/app-clients`, summary: "List app clients" }).catch(() => null);
16297
+ const list = Array.isArray(clients) ? clients : Array.isArray(clients?.app_clients) ? clients.app_clients : [];
16298
+ defaultId = list.length === 1 ? list[0].id ?? list[0].client_id ?? list[0].clientId ?? null : null;
16299
+ }
16300
+ if (!defaultId) return null;
16301
+ const providers = await admin({ method: "GET", path: `${s.base}/app-clients/${encodeURIComponent(defaultId)}/providers`, summary: "List login providers" }).catch(() => []);
16302
+ return { clientId: defaultId, providers: Array.isArray(providers) ? providers : [] };
16303
+ }
16304
+ async function runTenantPasswordEnable(tenantArg2, opts) {
16305
+ const s = await scope(opts, "Enable username & password sign-in on which tenant?", tenantArg2);
16306
+ if (!await serverSupportsPasswordProvider(s)) {
16307
+ console.error(import_chalk59.default.red("This APIblaze deployment does not offer username & password sign-in yet."));
16308
+ process.exit(1);
16309
+ }
16310
+ const spinner = (0, import_ora28.default)("Checking the tenant's default app client...").start();
16311
+ const dc = await defaultAppClient(s);
16312
+ if (!dc) {
16313
+ spinner.fail(`Tenant ${s.tenant} has no login yet.`);
16314
+ console.error(import_chalk59.default.dim(` Attach it to a proxy with a sign-in first: apiblaze tenant attach <project> --tenant ${s.tenant}`));
16315
+ process.exit(1);
16316
+ }
16317
+ const existing = dc.providers.find((p) => String(p.type).toLowerCase() === "password");
16318
+ if (existing) {
16319
+ spinner.succeed(`Username & password sign-in is already enabled on ${s.tenant}.`);
16320
+ if (opts.json) console.log(JSON.stringify({ enabled: true, provider_id: existing.id, client_id: dc.clientId }));
16321
+ else console.log(import_chalk59.default.dim(` Add people: apiblaze tenant password add <username> ${s.tenant}`));
16322
+ return;
16323
+ }
16324
+ spinner.text = "Adding the provider...";
16325
+ const created = await admin({ method: "POST", path: `${s.base}/app-clients/${encodeURIComponent(dc.clientId)}/providers`, body: { type: "password" }, summary: "Enable username & password sign-in" });
16326
+ spinner.succeed(`Username & password sign-in enabled on ${import_chalk59.default.bold(s.tenant)}.`);
16327
+ if (opts.json) {
16328
+ console.log(JSON.stringify({ enabled: true, provider_id: created?.id ?? created?.provider?.id ?? null, client_id: dc.clientId }));
16329
+ return;
16330
+ }
16331
+ console.log(import_chalk59.default.dim(` The hosted login page now shows a username & password form next to the other sign-in buttons.
16332
+ Add people: apiblaze tenant password add <username> ${s.tenant}`));
16333
+ }
16334
+ async function runTenantPasswordDisable(tenantArg2, opts) {
16335
+ const s = await scope(opts, "Disable username & password sign-in on which tenant?", tenantArg2);
16336
+ const dc = await defaultAppClient(s);
16337
+ const existing = dc?.providers.find((p) => String(p.type).toLowerCase() === "password");
16338
+ if (!dc || !existing) {
16339
+ console.log(import_chalk59.default.dim(`Username & password sign-in is not enabled on ${s.tenant}.`));
16340
+ return;
16341
+ }
16342
+ if (!opts.yes) {
16343
+ const { default: inquirer3 } = await import("inquirer");
16344
+ const { ok } = await inquirer3.prompt([{ type: "confirm", name: "ok", default: false, message: `Remove username & password sign-in from ${s.tenant}? Every password account on it is deleted too.` }]);
16345
+ if (!ok) return;
16346
+ }
16347
+ await admin({ method: "DELETE", path: `${s.base}/app-clients/${encodeURIComponent(dc.clientId)}/providers/${encodeURIComponent(existing.id)}`, summary: "Disable username & password sign-in" });
16348
+ console.log(import_chalk59.default.green(`Username & password sign-in removed from ${s.tenant}.`));
16349
+ if (opts.json) console.log(JSON.stringify({ enabled: false }));
16350
+ }
16351
+ function printAccount(a) {
16352
+ const flags = [a.disabled ? import_chalk59.default.red("blocked") : import_chalk59.default.green("active"), a.locked_until ? import_chalk59.default.yellow(`locked until ${a.locked_until}`) : "", a.purpose === "reviewer" ? import_chalk59.default.cyan("reviewer") : ""].filter(Boolean).join(" \xB7 ");
16353
+ console.log(` ${import_chalk59.default.bold(a.username)} ${flags}${a.email ? import_chalk59.default.dim(` ${a.email}`) : ""}${a.last_login_at ? import_chalk59.default.dim(` last login ${a.last_login_at}`) : ""}`);
16354
+ }
16355
+ async function runTenantPasswordList(tenantArg2, opts) {
16356
+ const s = await scope(opts, "List password accounts on which tenant?", tenantArg2);
16357
+ const out = await admin({ method: "GET", path: `${s.base}/password-accounts`, summary: "List password accounts" });
16358
+ if (opts.json) {
16359
+ console.log(JSON.stringify(out, null, 2));
16360
+ return;
16361
+ }
16362
+ if (!out?.enabled) {
16363
+ console.log(import_chalk59.default.dim(`Username & password sign-in is not enabled on ${s.tenant}. Enable: apiblaze tenant password enable ${s.tenant}`));
16364
+ return;
16365
+ }
16366
+ const accounts = out.accounts ?? [];
16367
+ if (!accounts.length) {
16368
+ console.log(import_chalk59.default.dim(` No password accounts yet on ${s.tenant}. Add one: apiblaze tenant password add <username> ${s.tenant}`));
16369
+ return;
16370
+ }
16371
+ console.log(import_chalk59.default.dim(` ${s.tenant}`));
16372
+ for (const a of accounts) printAccount(a);
16373
+ }
16374
+ async function readPasswordFromStdin() {
16375
+ const chunks = [];
16376
+ for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
16377
+ return Buffer.concat(chunks).toString("utf8").replace(/\r?\n$/, "");
16378
+ }
16379
+ function revealOnce(s, username, password) {
16380
+ console.log();
16381
+ console.log(import_chalk59.default.bold(" Password (shown once, never stored in clear):"));
16382
+ console.log(` ${import_chalk59.default.cyan(username)} ${import_chalk59.default.bgWhite.black(` ${password} `)}`);
16383
+ console.log(import_chalk59.default.dim(` New one any time: apiblaze tenant password rotate ${username} ${s.tenant}`));
16384
+ console.log();
16385
+ }
16386
+ async function runTenantPasswordAdd(username, tenantArg2, opts) {
16387
+ const s = await scope(opts, "Add the account on which tenant?", tenantArg2);
16388
+ const body = { username };
16389
+ if (opts.email) body.email = opts.email;
16390
+ if (opts.displayName) body.displayName = opts.displayName;
16391
+ if (opts.purpose) body.purpose = opts.purpose;
16392
+ if (opts.passwordStdin) body.password = await readPasswordFromStdin();
16393
+ const r = await admin({ method: "POST", path: `${s.base}/password-accounts`, body, summary: `Add password account ${username}` });
16394
+ if (opts.json) {
16395
+ console.log(JSON.stringify(r));
16396
+ return;
16397
+ }
16398
+ console.log(import_chalk59.default.green(`Account ${import_chalk59.default.bold(r.account?.username ?? username)} created on ${s.tenant}.`));
16399
+ if (r.adopts?.abz_sub) console.log(import_chalk59.default.yellow(` This login signs in AS the existing user ${r.adopts.abz_sub} (same email, pre-provisioned on this tenant).`));
16400
+ if (!opts.passwordStdin && r.password) revealOnce(s, r.account?.username ?? username, r.password);
16401
+ }
16402
+ async function findAccount(s, username) {
16403
+ const out = await admin({ method: "GET", path: `${s.base}/password-accounts`, summary: "List password accounts" });
16404
+ const a = (out?.accounts ?? []).find((x) => x.username === username.trim().toLowerCase() || x.id === username);
16405
+ if (!a) {
16406
+ console.error(import_chalk59.default.red(`No password account "${username}" on ${s.tenant}.`));
16407
+ process.exit(1);
16408
+ }
16409
+ return a;
16410
+ }
16411
+ async function runTenantPasswordRotate(username, tenantArg2, opts) {
16412
+ const s = await scope(opts, "Rotate the password on which tenant?", tenantArg2);
16413
+ const a = await findAccount(s, username);
16414
+ const body = {};
16415
+ if (opts.passwordStdin) body.password = await readPasswordFromStdin();
16416
+ const r = await admin({ method: "POST", path: `${s.base}/password-accounts/${encodeURIComponent(a.id)}/rotate`, body, summary: `Rotate password for ${a.username}` });
16417
+ if (opts.json) {
16418
+ console.log(JSON.stringify(r));
16419
+ return;
16420
+ }
16421
+ console.log(import_chalk59.default.green(`Password rotated for ${import_chalk59.default.bold(a.username)}. Existing sessions end at their next refresh.`));
16422
+ if (!opts.passwordStdin && r.password) revealOnce(s, a.username, r.password);
16423
+ }
16424
+ async function runTenantPasswordSetBlocked(username, tenantArg2, blocked, opts) {
16425
+ const s = await scope(opts, `${blocked ? "Block" : "Unblock"} the account on which tenant?`, tenantArg2);
16426
+ const a = await findAccount(s, username);
16427
+ const r = await admin({ method: "PATCH", path: `${s.base}/password-accounts/${encodeURIComponent(a.id)}`, body: { disabled: blocked }, summary: `${blocked ? "Block" : "Unblock"} ${a.username}` });
16428
+ if (opts.json) {
16429
+ console.log(JSON.stringify(r));
16430
+ return;
16431
+ }
16432
+ console.log(import_chalk59.default.green(`${a.username} ${blocked ? "blocked: sign-in refused until unblocked" : "unblocked"}.`));
16433
+ }
16434
+ async function runTenantPasswordUnlock(username, tenantArg2, opts) {
16435
+ const s = await scope(opts, "Unlock the account on which tenant?", tenantArg2);
16436
+ const a = await findAccount(s, username);
16437
+ const r = await admin({ method: "POST", path: `${s.base}/password-accounts/${encodeURIComponent(a.id)}/unlock`, body: {}, summary: `Unlock ${a.username}` });
16438
+ if (opts.json) {
16439
+ console.log(JSON.stringify(r));
16440
+ return;
16441
+ }
16442
+ console.log(import_chalk59.default.green(`${a.username} unlocked.`));
16443
+ }
16444
+ async function runTenantPasswordRemove(username, tenantArg2, opts) {
16445
+ const s = await scope(opts, "Remove the account from which tenant?", tenantArg2);
16446
+ const a = await findAccount(s, username);
16447
+ if (!opts.yes) {
16448
+ const { default: inquirer3 } = await import("inquirer");
16449
+ const { ok } = await inquirer3.prompt([{ type: "confirm", name: "ok", default: false, message: `Remove password account ${a.username}?` }]);
16450
+ if (!ok) return;
16451
+ }
16452
+ const r = await admin({ method: "DELETE", path: `${s.base}/password-accounts/${encodeURIComponent(a.id)}`, summary: `Remove ${a.username}` });
16453
+ if (opts.json) {
16454
+ console.log(JSON.stringify(r));
16455
+ return;
16456
+ }
16457
+ console.log(import_chalk59.default.green(`${a.username} removed.`));
16458
+ }
16459
+
16460
+ // src/commands/group.ts
16461
+ var import_chalk60 = __toESM(require("chalk"));
16462
+ var import_ora29 = __toESM(require("ora"));
16329
16463
  init_auth();
16330
16464
  init_consumer_auth();
16331
16465
  init_admin();
@@ -16427,7 +16561,7 @@ async function userByRefOrCreate(t, ref) {
16427
16561
  if (!ref.includes("@")) throw e;
16428
16562
  const created = await t.call("/v1/admin/users", "POST", { email: ref.trim().toLowerCase() });
16429
16563
  if (!created?.abz_sub) throw e;
16430
- console.log(import_chalk59.default.dim(` ${ref} wasn't a user yet \u2014 created.`));
16564
+ console.log(import_chalk60.default.dim(` ${ref} wasn't a user yet \u2014 created.`));
16431
16565
  return { abz_sub: created.abz_sub, email: created.email ?? ref, display_name: null };
16432
16566
  }
16433
16567
  }
@@ -16435,11 +16569,11 @@ async function runGroupList(opts) {
16435
16569
  const t = await resolveTransport(opts);
16436
16570
  const groups = await listGroups(t);
16437
16571
  if (opts.json) return void console.log(JSON.stringify(groups));
16438
- if (!groups.length) return void console.log(import_chalk59.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
16439
- console.log(import_chalk59.default.dim(`Tenant ${t.tenant}:`));
16572
+ if (!groups.length) return void console.log(import_chalk60.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
16573
+ console.log(import_chalk60.default.dim(`Tenant ${t.tenant}:`));
16440
16574
  for (const g of groups) {
16441
- const sealed = g.name === "apiblaze_admins" ? import_chalk59.default.dim(" (admins \u2014 managed by allowlist)") : "";
16442
- console.log(` ${import_chalk59.default.bold(g.name)} ${import_chalk59.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
16575
+ const sealed = g.name === "apiblaze_admins" ? import_chalk60.default.dim(" (admins \u2014 managed by allowlist)") : "";
16576
+ console.log(` ${import_chalk60.default.bold(g.name)} ${import_chalk60.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
16443
16577
  }
16444
16578
  }
16445
16579
  async function runGroupCreate(name, opts) {
@@ -16464,10 +16598,10 @@ async function runGroupCreate(name, opts) {
16464
16598
  }
16465
16599
  }
16466
16600
  }
16467
- const spinner = (0, import_ora28.default)(`Creating group ${name}...`).start();
16601
+ const spinner = (0, import_ora29.default)(`Creating group ${name}...`).start();
16468
16602
  try {
16469
16603
  await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
16470
- spinner.succeed(`Group ${import_chalk59.default.bold(name)} created in ${t.tenant}.`);
16604
+ spinner.succeed(`Group ${import_chalk60.default.bold(name)} created in ${t.tenant}.`);
16471
16605
  } catch (e) {
16472
16606
  spinner.fail("Create failed.");
16473
16607
  throw e;
@@ -16476,10 +16610,10 @@ async function runGroupCreate(name, opts) {
16476
16610
  async function runGroupDelete(name, opts) {
16477
16611
  const t = await resolveTransport(opts);
16478
16612
  const g = await groupByName(t, name);
16479
- const spinner = (0, import_ora28.default)(`Deleting group ${name}...`).start();
16613
+ const spinner = (0, import_ora29.default)(`Deleting group ${name}...`).start();
16480
16614
  try {
16481
16615
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
16482
- spinner.succeed(`Group ${import_chalk59.default.bold(name)} deleted.`);
16616
+ spinner.succeed(`Group ${import_chalk60.default.bold(name)} deleted.`);
16483
16617
  } catch (e) {
16484
16618
  spinner.fail("Delete failed.");
16485
16619
  throw e;
@@ -16498,21 +16632,21 @@ async function runGroupMembers(name, opts) {
16498
16632
  return void console.log(JSON.stringify({ ...detail, members: walkedMembers.items, subgroups: subs?.subgroups ?? [] }));
16499
16633
  }
16500
16634
  const members = walkedMembers.items;
16501
- console.log(import_chalk59.default.bold(`${g.name}`) + import_chalk59.default.dim(` (${t.tenant})`));
16502
- for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk59.default.cyan("group-admin") : import_chalk59.default.dim("member")}`);
16503
- if (!members.length) console.log(import_chalk59.default.dim(" no members"));
16635
+ console.log(import_chalk60.default.bold(`${g.name}`) + import_chalk60.default.dim(` (${t.tenant})`));
16636
+ for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk60.default.cyan("group-admin") : import_chalk60.default.dim("member")}`);
16637
+ if (!members.length) console.log(import_chalk60.default.dim(" no members"));
16504
16638
  const sg = subs?.subgroups ?? [];
16505
- if (sg.length) console.log(import_chalk59.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
16639
+ if (sg.length) console.log(import_chalk60.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
16506
16640
  }
16507
16641
  async function runGroupAddUser(userRef, groupName, opts) {
16508
16642
  const t = await resolveTransport(opts);
16509
16643
  const g = await groupByName(t, groupName);
16510
16644
  const u = await userByRefOrCreate(t, userRef);
16511
16645
  const role = opts.role === "admin" ? "admin" : "member";
16512
- const spinner = (0, import_ora28.default)(`Adding ${label2(u)} to ${g.name}...`).start();
16646
+ const spinner = (0, import_ora29.default)(`Adding ${label2(u)} to ${g.name}...`).start();
16513
16647
  try {
16514
16648
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
16515
- spinner.succeed(`${import_chalk59.default.bold(label2(u))} added to ${import_chalk59.default.bold(g.name)} as ${role}.`);
16649
+ spinner.succeed(`${import_chalk60.default.bold(label2(u))} added to ${import_chalk60.default.bold(g.name)} as ${role}.`);
16516
16650
  } catch (e) {
16517
16651
  spinner.fail("Add failed.");
16518
16652
  throw e;
@@ -16521,10 +16655,10 @@ async function runGroupAddUser(userRef, groupName, opts) {
16521
16655
  async function runGroupRemoveUser(userRef, groupName, opts) {
16522
16656
  const t = await resolveTransport(opts);
16523
16657
  const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
16524
- const spinner = (0, import_ora28.default)(`Removing ${label2(u)} from ${g.name}...`).start();
16658
+ const spinner = (0, import_ora29.default)(`Removing ${label2(u)} from ${g.name}...`).start();
16525
16659
  try {
16526
16660
  await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
16527
- spinner.succeed(`${import_chalk59.default.bold(label2(u))} removed from ${import_chalk59.default.bold(g.name)}.`);
16661
+ spinner.succeed(`${import_chalk60.default.bold(label2(u))} removed from ${import_chalk60.default.bold(g.name)}.`);
16528
16662
  } catch (e) {
16529
16663
  spinner.fail("Remove failed.");
16530
16664
  throw e;
@@ -16533,10 +16667,10 @@ async function runGroupRemoveUser(userRef, groupName, opts) {
16533
16667
  async function runGroupAddGroup(childName, parentName, opts) {
16534
16668
  const t = await resolveTransport(opts);
16535
16669
  const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
16536
- const spinner = (0, import_ora28.default)(`Nesting ${child.name} under ${parent.name}...`).start();
16670
+ const spinner = (0, import_ora29.default)(`Nesting ${child.name} under ${parent.name}...`).start();
16537
16671
  try {
16538
16672
  await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
16539
- spinner.succeed(`${import_chalk59.default.bold(child.name)} is now a subgroup of ${import_chalk59.default.bold(parent.name)}.`);
16673
+ spinner.succeed(`${import_chalk60.default.bold(child.name)} is now a subgroup of ${import_chalk60.default.bold(parent.name)}.`);
16540
16674
  } catch (e) {
16541
16675
  spinner.fail("Nest failed.");
16542
16676
  throw e;
@@ -16545,10 +16679,10 @@ async function runGroupAddGroup(childName, parentName, opts) {
16545
16679
  async function runGroupRemoveGroup(childName, parentName, opts) {
16546
16680
  const t = await resolveTransport(opts);
16547
16681
  const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
16548
- const spinner = (0, import_ora28.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
16682
+ const spinner = (0, import_ora29.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
16549
16683
  try {
16550
16684
  await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
16551
- spinner.succeed(`${import_chalk59.default.bold(child.name)} un-nested from ${import_chalk59.default.bold(parent.name)}.`);
16685
+ spinner.succeed(`${import_chalk60.default.bold(child.name)} un-nested from ${import_chalk60.default.bold(parent.name)}.`);
16552
16686
  } catch (e) {
16553
16687
  spinner.fail("Un-nest failed.");
16554
16688
  throw e;
@@ -16559,13 +16693,13 @@ async function runGroupUsers(opts) {
16559
16693
  const users = await listUsers(t);
16560
16694
  const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
16561
16695
  if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
16562
- console.log(import_chalk59.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
16563
- for (const u of users) console.log(` ${label2(u)} ${import_chalk59.default.dim(u.abz_sub)}`);
16564
- if (!users.length) console.log(import_chalk59.default.dim(" no users yet"));
16696
+ console.log(import_chalk60.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
16697
+ for (const u of users) console.log(` ${label2(u)} ${import_chalk60.default.dim(u.abz_sub)}`);
16698
+ if (!users.length) console.log(import_chalk60.default.dim(" no users yet"));
16565
16699
  const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
16566
16700
  if (fresh.length) {
16567
- console.log(import_chalk59.default.dim("Seen in traffic (not yet users):"));
16568
- for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk59.default.dim(o.source_label ?? "")}`);
16701
+ console.log(import_chalk60.default.dim("Seen in traffic (not yet users):"));
16702
+ for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk60.default.dim(o.source_label ?? "")}`);
16569
16703
  }
16570
16704
  }
16571
16705
 
@@ -16575,8 +16709,8 @@ init_iam();
16575
16709
  init_admins();
16576
16710
 
16577
16711
  // src/commands/preapprove.ts
16578
- var import_chalk60 = __toESM(require("chalk"));
16579
- var import_ora29 = __toESM(require("ora"));
16712
+ var import_chalk61 = __toESM(require("chalk"));
16713
+ var import_ora30 = __toESM(require("ora"));
16580
16714
  init_preapproval();
16581
16715
  async function runPreapprove(who, opts) {
16582
16716
  const tenant2 = await resolveProducerTenant(opts.tenant);
@@ -16584,14 +16718,14 @@ async function runPreapprove(who, opts) {
16584
16718
  const rules = await listPreapprovalRules(tenant2);
16585
16719
  if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
16586
16720
  if (!rules.length) {
16587
- console.log(import_chalk60.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
16588
- console.log(import_chalk60.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
16721
+ console.log(import_chalk61.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
16722
+ console.log(import_chalk61.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
16589
16723
  return;
16590
16724
  }
16591
- console.log(import_chalk60.default.dim(`Pre-approved for ${import_chalk60.default.bold(tenant2)}:`));
16725
+ console.log(import_chalk61.default.dim(`Pre-approved for ${import_chalk61.default.bold(tenant2)}:`));
16592
16726
  for (const r of rules) {
16593
- const tag = r.kind === "domain" ? import_chalk60.default.cyan("@" + r.value + " (whole domain)") : r.value;
16594
- const grp = r.groups?.length ? import_chalk60.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
16727
+ const tag = r.kind === "domain" ? import_chalk61.default.cyan("@" + r.value + " (whole domain)") : r.value;
16728
+ const grp = r.groups?.length ? import_chalk61.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
16595
16729
  console.log(` ${tag}${grp}`);
16596
16730
  }
16597
16731
  return;
@@ -16600,19 +16734,19 @@ async function runPreapprove(who, opts) {
16600
16734
  throw new Error("Who? Pass an email or a domain: `apiblaze preapprove someone@acme.com` (or `apiblaze preapprove --list`).");
16601
16735
  }
16602
16736
  if (opts.remove) {
16603
- const spinner2 = (0, import_ora29.default)(`Removing ${who} from ${tenant2}\u2026`).start();
16737
+ const spinner2 = (0, import_ora30.default)(`Removing ${who} from ${tenant2}\u2026`).start();
16604
16738
  const { removed, value } = await removePreapprovalRule(tenant2, who);
16605
- if (removed) spinner2.succeed(`${import_chalk60.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
16606
- else spinner2.warn(`No pre-approval rule for ${import_chalk60.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
16739
+ if (removed) spinner2.succeed(`${import_chalk61.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
16740
+ else spinner2.warn(`No pre-approval rule for ${import_chalk61.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
16607
16741
  return;
16608
16742
  }
16609
- const spinner = (0, import_ora29.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
16743
+ const spinner = (0, import_ora30.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
16610
16744
  try {
16611
16745
  const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
16612
16746
  const what = kind === "domain" ? `Anyone @${value}` : value;
16613
- spinner.succeed(`${import_chalk60.default.bold(what)} can now sign in to ${import_chalk60.default.bold(tenant2)}.`);
16614
- if (opts.group?.length) console.log(import_chalk60.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
16615
- console.log(import_chalk60.default.dim(` See the full list: apiblaze preapprove --list`));
16747
+ spinner.succeed(`${import_chalk61.default.bold(what)} can now sign in to ${import_chalk61.default.bold(tenant2)}.`);
16748
+ if (opts.group?.length) console.log(import_chalk61.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
16749
+ console.log(import_chalk61.default.dim(` See the full list: apiblaze preapprove --list`));
16616
16750
  } catch (err) {
16617
16751
  spinner.fail("Could not add the rule.");
16618
16752
  throw err;
@@ -16624,8 +16758,8 @@ init_apichat();
16624
16758
  init_llm();
16625
16759
 
16626
16760
  // src/commands/consumer.ts
16627
- var import_chalk61 = __toESM(require("chalk"));
16628
- var import_ora30 = __toESM(require("ora"));
16761
+ var import_chalk62 = __toESM(require("chalk"));
16762
+ var import_ora31 = __toESM(require("ora"));
16629
16763
  init_admin();
16630
16764
  init_resolve();
16631
16765
  init_caller();
@@ -16648,7 +16782,7 @@ async function consumerFetch(creds, suffix, init) {
16648
16782
  function requireConsumer2() {
16649
16783
  const c = loadConsumer();
16650
16784
  if (!c) {
16651
- console.error(import_chalk61.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
16785
+ console.error(import_chalk62.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
16652
16786
  process.exit(1);
16653
16787
  }
16654
16788
  return c;
@@ -16659,7 +16793,7 @@ async function runConsumerLogin(opts) {
16659
16793
  let clientId = opts.client;
16660
16794
  if (clientId) {
16661
16795
  if (!tenant2) {
16662
- console.error(import_chalk61.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
16796
+ console.error(import_chalk62.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
16663
16797
  process.exit(1);
16664
16798
  }
16665
16799
  } else {
@@ -16671,24 +16805,24 @@ async function runConsumerLogin(opts) {
16671
16805
  if (!picked) process.exit(1);
16672
16806
  tenant2 = picked;
16673
16807
  }
16674
- const s2 = (0, import_ora30.default)("Finding the login app...").start();
16808
+ const s2 = (0, import_ora31.default)("Finding the login app...").start();
16675
16809
  const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
16676
16810
  s2.stop();
16677
16811
  const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
16678
16812
  const pick3 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
16679
16813
  if (!pick3) {
16680
- console.error(import_chalk61.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
16814
+ console.error(import_chalk62.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
16681
16815
  process.exit(1);
16682
16816
  }
16683
16817
  clientId = pick3.client_id ?? pick3.clientId;
16684
16818
  }
16685
- console.log(`${import_chalk61.default.cyan("\u2192")} Logging in to ${import_chalk61.default.bold(tenant2)} as a consumer...`);
16819
+ console.log(`${import_chalk62.default.cyan("\u2192")} Logging in to ${import_chalk62.default.bold(tenant2)} as a consumer...`);
16686
16820
  const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
16687
16821
  console.log(`
16688
- Open: ${import_chalk61.default.underline(verificationUri)}`);
16689
- console.log(` Code: ${import_chalk61.default.bold(userCode)}
16822
+ Open: ${import_chalk62.default.underline(verificationUri)}`);
16823
+ console.log(` Code: ${import_chalk62.default.bold(userCode)}
16690
16824
  `);
16691
- console.log(import_chalk61.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
16825
+ console.log(import_chalk62.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
16692
16826
  });
16693
16827
  const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
16694
16828
  const creds = {
@@ -16703,7 +16837,7 @@ async function runConsumerLogin(opts) {
16703
16837
  obtainedAt: Date.now()
16704
16838
  };
16705
16839
  saveConsumer(creds);
16706
- console.log(import_chalk61.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
16840
+ console.log(import_chalk62.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
16707
16841
  }
16708
16842
  async function runConsumerTokens(opts) {
16709
16843
  const creds = requireConsumer2();
@@ -16716,28 +16850,28 @@ async function runConsumerTokens(opts) {
16716
16850
  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));
16717
16851
  return;
16718
16852
  }
16719
- console.log(`${import_chalk61.default.cyan("Consumer")} ${import_chalk61.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk61.default.bold(fresh.tenant)}
16853
+ console.log(`${import_chalk62.default.cyan("Consumer")} ${import_chalk62.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk62.default.bold(fresh.tenant)}
16720
16854
  `);
16721
- console.log(`${import_chalk61.default.bold("access_token")} ${import_chalk61.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
16855
+ console.log(`${import_chalk62.default.bold("access_token")} ${import_chalk62.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
16722
16856
  ${fresh.accessToken}
16723
16857
  `);
16724
- if (fresh.idToken) console.log(`${import_chalk61.default.bold("id_token")} ${import_chalk61.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
16858
+ if (fresh.idToken) console.log(`${import_chalk62.default.bold("id_token")} ${import_chalk62.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
16725
16859
  ${fresh.idToken}
16726
16860
  `);
16727
- if (fresh.refreshToken) console.log(`${import_chalk61.default.bold("refresh_token")}
16861
+ if (fresh.refreshToken) console.log(`${import_chalk62.default.bold("refresh_token")}
16728
16862
  ${fresh.refreshToken}
16729
16863
  `);
16730
- console.log(import_chalk61.default.dim("These are your own tokens \u2014 keep them secret."));
16864
+ console.log(import_chalk62.default.dim("These are your own tokens \u2014 keep them secret."));
16731
16865
  }
16732
16866
  async function runConsumerApikeys(opts) {
16733
16867
  const creds = requireConsumer2();
16734
16868
  const { default: inquirer3 } = await import("inquirer");
16735
- const spinner = (0, import_ora30.default)("Loading your API keys...").start();
16869
+ const spinner = (0, import_ora31.default)("Loading your API keys...").start();
16736
16870
  const list = await consumerFetch(creds, withPage("/apikeys", null));
16737
16871
  if (list.status >= 400) {
16738
16872
  spinner.stop();
16739
- console.error(import_chalk61.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
16740
- if (list.status === 401) console.error(import_chalk61.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
16873
+ console.error(import_chalk62.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
16874
+ if (list.status === 401) console.error(import_chalk62.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
16741
16875
  process.exit(1);
16742
16876
  }
16743
16877
  let walkCreds = list.creds;
@@ -16766,16 +16900,16 @@ async function runConsumerApikeys(opts) {
16766
16900
  if (opts.json) {
16767
16901
  console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
16768
16902
  } else if (!keys.length) {
16769
- console.log(import_chalk61.default.yellow("No API keys yet."));
16903
+ console.log(import_chalk62.default.yellow("No API keys yet."));
16770
16904
  } else {
16771
16905
  for (const k of keys) {
16772
16906
  const clear = revealMap[k.environment]?.key;
16773
- const shown = clear ? import_chalk61.default.green(clear) : import_chalk61.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
16774
- const exp = k.expires_at ? import_chalk61.default.dim(`exp ${k.expires_at}`) : import_chalk61.default.dim("no expiry");
16775
- console.log(` ${import_chalk61.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk61.default.dim(k.description ?? "")}`);
16907
+ const shown = clear ? import_chalk62.default.green(clear) : import_chalk62.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
16908
+ const exp = k.expires_at ? import_chalk62.default.dim(`exp ${k.expires_at}`) : import_chalk62.default.dim("no expiry");
16909
+ console.log(` ${import_chalk62.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk62.default.dim(k.description ?? "")}`);
16776
16910
  }
16777
16911
  if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
16778
- console.log(import_chalk61.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
16912
+ console.log(import_chalk62.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
16779
16913
  }
16780
16914
  }
16781
16915
  if (opts.json) return;
@@ -16789,7 +16923,7 @@ async function runConsumerApikeys(opts) {
16789
16923
  const body = { environment: answers.environment };
16790
16924
  if (answers.description) body.description = answers.description;
16791
16925
  if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
16792
- const s2 = (0, import_ora30.default)("Creating key...").start();
16926
+ const s2 = (0, import_ora31.default)("Creating key...").start();
16793
16927
  const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
16794
16928
  if (created.status >= 400) {
16795
16929
  s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
@@ -16797,13 +16931,13 @@ async function runConsumerApikeys(opts) {
16797
16931
  }
16798
16932
  s2.succeed("Key created.");
16799
16933
  const key = created.data?.key ?? created.data?.fullKey;
16800
- if (key) console.log(` ${import_chalk61.default.green(key)} ${import_chalk61.default.dim("(shown once \u2014 store it now)")}`);
16801
- else console.log(import_chalk61.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
16934
+ if (key) console.log(` ${import_chalk62.default.green(key)} ${import_chalk62.default.dim("(shown once \u2014 store it now)")}`);
16935
+ else console.log(import_chalk62.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
16802
16936
  }
16803
16937
 
16804
16938
  // src/commands/sidecar.ts
16805
- var import_chalk62 = __toESM(require("chalk"));
16806
- var import_ora31 = __toESM(require("ora"));
16939
+ var import_chalk63 = __toESM(require("chalk"));
16940
+ var import_ora32 = __toESM(require("ora"));
16807
16941
  var fs18 = __toESM(require("fs"));
16808
16942
  var path14 = __toESM(require("path"));
16809
16943
  init_admin();
@@ -16845,18 +16979,18 @@ function upsertEnvLocal(root, token) {
16845
16979
  }
16846
16980
  function installSidecarPackage(root) {
16847
16981
  if (fs18.existsSync(path14.join(root, "node_modules", "apiblaze", "package.json"))) {
16848
- console.log(` ${import_chalk62.default.green("\u2713")} apiblaze package already installed`);
16982
+ console.log(` ${import_chalk63.default.green("\u2713")} apiblaze package already installed`);
16849
16983
  return;
16850
16984
  }
16851
16985
  const has = (f) => fs18.existsSync(path14.join(root, f));
16852
16986
  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" };
16853
- const spinner = (0, import_ora31.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
16987
+ const spinner = (0, import_ora32.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
16854
16988
  try {
16855
16989
  const { execSync } = require("child_process");
16856
16990
  execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
16857
16991
  spinner.succeed("Installed apiblaze (the sidecar runtime).");
16858
16992
  } catch {
16859
- spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk62.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk62.default.cyan("npm run dev")}.`);
16993
+ spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk63.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk63.default.cyan("npm run dev")}.`);
16860
16994
  }
16861
16995
  }
16862
16996
  function readEnvKey(root) {
@@ -16999,7 +17133,7 @@ async function runAnonymousInit(root, router, opts) {
16999
17133
  const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
17000
17134
  const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
17001
17135
  if (opts.newSession) clearAnonCred2();
17002
- const spinner = (0, import_ora31.default)("Setting up a sidecar (no login needed)...").start();
17136
+ const spinner = (0, import_ora32.default)("Setting up a sidecar (no login needed)...").start();
17003
17137
  let out;
17004
17138
  try {
17005
17139
  out = await sidecarInitAnonymous2();
@@ -17011,29 +17145,29 @@ async function runAnonymousInit(root, router, opts) {
17011
17145
  if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
17012
17146
  const envState = upsertEnvLocal(root, out.token);
17013
17147
  ensureGitignored(root);
17014
- console.log(` ${import_chalk62.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
17015
- console.log(` ${import_chalk62.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
17148
+ console.log(` ${import_chalk63.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
17149
+ console.log(` ${import_chalk63.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
17016
17150
  installSidecarPackage(root);
17017
17151
  let inspectorPath = null;
17018
17152
  if (!opts.noInspector) {
17019
17153
  inspectorPath = generateInspector(root, router);
17020
- if (inspectorPath) console.log(` ${import_chalk62.default.green("\u2713")} inspector at ${inspectorPath}`);
17154
+ if (inspectorPath) console.log(` ${import_chalk63.default.green("\u2713")} inspector at ${inspectorPath}`);
17021
17155
  }
17022
17156
  console.log("");
17023
- console.log(import_chalk62.default.bold("Done (no account needed). What happens next:"));
17024
- console.log(` 1. ${import_chalk62.default.cyan("npm run dev")} and use your app.`);
17157
+ console.log(import_chalk63.default.bold("Done (no account needed). What happens next:"));
17158
+ console.log(` 1. ${import_chalk63.default.cyan("npm run dev")} and use your app.`);
17025
17159
  console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
17026
- console.log(` ${import_chalk62.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
17160
+ console.log(` ${import_chalk63.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
17027
17161
  console.log("");
17028
- console.log(import_chalk62.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
17029
- console.log(` ${import_chalk62.default.cyan("apiblaze login")} then ${import_chalk62.default.cyan("apiblaze claim")} ${import_chalk62.default.dim("(no code needed here)")}`);
17030
- console.log(import_chalk62.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
17162
+ console.log(import_chalk63.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
17163
+ console.log(` ${import_chalk63.default.cyan("apiblaze login")} then ${import_chalk63.default.cyan("apiblaze claim")} ${import_chalk63.default.dim("(no code needed here)")}`);
17164
+ console.log(import_chalk63.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
17031
17165
  }
17032
17166
  async function runSidecar(opts) {
17033
17167
  const root = path14.resolve(opts.dir ?? process.cwd());
17034
17168
  const detected2 = detectNextProject(root);
17035
17169
  if (!detected2.found) {
17036
- console.log(import_chalk62.default.yellow(`No Next.js project detected in ${root}.`));
17170
+ console.log(import_chalk63.default.yellow(`No Next.js project detected in ${root}.`));
17037
17171
  console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
17038
17172
  return;
17039
17173
  }
@@ -17044,10 +17178,10 @@ async function runSidecar(opts) {
17044
17178
  if (!loadCredentials()) {
17045
17179
  upsertEnvLocal(root, readEnvKey(root));
17046
17180
  ensureGitignored(root);
17047
- console.log(` ${import_chalk62.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
17048
- console.log(` ${import_chalk62.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
17181
+ console.log(` ${import_chalk63.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
17182
+ console.log(` ${import_chalk63.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
17049
17183
  installSidecarPackage(root);
17050
- console.log(import_chalk62.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
17184
+ console.log(import_chalk63.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
17051
17185
  return;
17052
17186
  }
17053
17187
  const { teamId, teamName } = await resolveTeam(opts.team);
@@ -17056,7 +17190,7 @@ async function runSidecar(opts) {
17056
17190
  const mustMint = !existingKey || opts.rotate || switchingTeam;
17057
17191
  let token = existingKey ?? "";
17058
17192
  if (mustMint) {
17059
- const spinner = (0, import_ora31.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
17193
+ const spinner = (0, import_ora32.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
17060
17194
  try {
17061
17195
  const out = await admin({
17062
17196
  method: "POST",
@@ -17070,39 +17204,39 @@ async function runSidecar(opts) {
17070
17204
  throw err;
17071
17205
  }
17072
17206
  } else {
17073
- console.log(import_chalk62.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
17207
+ console.log(import_chalk63.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
17074
17208
  }
17075
17209
  const envState = upsertEnvLocal(root, token);
17076
17210
  ensureGitignored(root);
17077
- console.log(` ${import_chalk62.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
17211
+ console.log(` ${import_chalk63.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
17078
17212
  const wireState = wireInstrumentation(root);
17079
- console.log(` ${import_chalk62.default.green("\u2713")} instrumentation.ts ${wireState}`);
17213
+ console.log(` ${import_chalk63.default.green("\u2713")} instrumentation.ts ${wireState}`);
17080
17214
  installSidecarPackage(root);
17081
17215
  let inspectorPath = null;
17082
17216
  if (!opts.noInspector) {
17083
17217
  inspectorPath = generateInspector(root, detected2.router);
17084
- if (inspectorPath) console.log(` ${import_chalk62.default.green("\u2713")} inspector at ${inspectorPath}`);
17218
+ if (inspectorPath) console.log(` ${import_chalk63.default.green("\u2713")} inspector at ${inspectorPath}`);
17085
17219
  }
17086
17220
  console.log("");
17087
- console.log(import_chalk62.default.bold("Done. What happens next:"));
17088
- console.log(` 1. ${import_chalk62.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
17089
- console.log(` 2. The origins your app calls appear as ${import_chalk62.default.bold("candidates")} \u2014 list them: ${import_chalk62.default.cyan("apiblaze sidecar")}`);
17090
- console.log(` 3. Approve the ones to route: ${import_chalk62.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
17221
+ console.log(import_chalk63.default.bold("Done. What happens next:"));
17222
+ console.log(` 1. ${import_chalk63.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
17223
+ console.log(` 2. The origins your app calls appear as ${import_chalk63.default.bold("candidates")} \u2014 list them: ${import_chalk63.default.cyan("apiblaze sidecar")}`);
17224
+ console.log(` 3. Approve the ones to route: ${import_chalk63.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
17091
17225
  console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
17092
- if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk62.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path14.dirname(inspectorPath)} before shipping)`);
17093
- if (switchingTeam) console.log(import_chalk62.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
17226
+ if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk63.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path14.dirname(inspectorPath)} before shipping)`);
17227
+ if (switchingTeam) console.log(import_chalk63.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
17094
17228
  console.log("");
17095
- console.log(import_chalk62.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
17096
- console.log(import_chalk62.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
17097
- console.log(import_chalk62.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
17229
+ console.log(import_chalk63.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
17230
+ console.log(import_chalk63.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
17231
+ console.log(import_chalk63.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
17098
17232
  console.log("");
17099
- console.log(import_chalk62.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
17100
- console.log(import_chalk62.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
17233
+ console.log(import_chalk63.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
17234
+ console.log(import_chalk63.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
17101
17235
  }
17102
17236
 
17103
17237
  // src/commands/origins.ts
17104
- var import_chalk63 = __toESM(require("chalk"));
17105
- var import_ora32 = __toESM(require("ora"));
17238
+ var import_chalk64 = __toESM(require("chalk"));
17239
+ var import_ora33 = __toESM(require("ora"));
17106
17240
  init_admin();
17107
17241
  init_resolve();
17108
17242
  init_auth();
@@ -17112,7 +17246,7 @@ async function runOriginsList(opts) {
17112
17246
  if (!loadCredentials()) {
17113
17247
  const cred = loadAnonCred();
17114
17248
  if (!cred) {
17115
- console.log(import_chalk63.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
17249
+ console.log(import_chalk64.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
17116
17250
  return;
17117
17251
  }
17118
17252
  out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
@@ -17130,30 +17264,30 @@ async function runOriginsList(opts) {
17130
17264
  }
17131
17265
  const routed = out.routed ?? [];
17132
17266
  const candidates = out.candidates ?? [];
17133
- console.log(import_chalk63.default.bold(`
17267
+ console.log(import_chalk64.default.bold(`
17134
17268
  Routed through APIblaze (${routed.length})`));
17135
- if (!routed.length) console.log(import_chalk63.default.dim(" none yet"));
17136
- for (const r of routed) console.log(` ${import_chalk63.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk63.default.dim(`\u2192 ${r.project_id}`)}`);
17137
- console.log(import_chalk63.default.bold(`
17269
+ if (!routed.length) console.log(import_chalk64.default.dim(" none yet"));
17270
+ for (const r of routed) console.log(` ${import_chalk64.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk64.default.dim(`\u2192 ${r.project_id}`)}`);
17271
+ console.log(import_chalk64.default.bold(`
17138
17272
  Candidates \u2014 going direct, not yet approved (${candidates.length})`));
17139
- if (!candidates.length) console.log(import_chalk63.default.dim(" none \u2014 run your app to discover the origins it calls"));
17273
+ if (!candidates.length) console.log(import_chalk64.default.dim(" none \u2014 run your app to discover the origins it calls"));
17140
17274
  for (const c of candidates) {
17141
- console.log(` ${import_chalk63.default.yellow("\u25CB")} ${c.origin} ${import_chalk63.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
17275
+ console.log(` ${import_chalk64.default.yellow("\u25CB")} ${c.origin} ${import_chalk64.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
17142
17276
  }
17143
17277
  if (candidates.length) {
17144
- console.log(import_chalk63.default.dim(`
17278
+ console.log(import_chalk64.default.dim(`
17145
17279
  Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
17146
- console.log(import_chalk63.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
17280
+ console.log(import_chalk64.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
17147
17281
  }
17148
17282
  }
17149
17283
  async function runOriginsApprove(origin, opts) {
17150
17284
  if (!loadCredentials()) {
17151
17285
  const cred = loadAnonCred();
17152
17286
  if (!cred) {
17153
- console.error(import_chalk63.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
17287
+ console.error(import_chalk64.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
17154
17288
  process.exit(1);
17155
17289
  }
17156
- const spinner2 = (0, import_ora32.default)(`Approving ${origin} (anonymous)...`).start();
17290
+ const spinner2 = (0, import_ora33.default)(`Approving ${origin} (anonymous)...`).start();
17157
17291
  try {
17158
17292
  const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
17159
17293
  spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
@@ -17164,7 +17298,7 @@ async function runOriginsApprove(origin, opts) {
17164
17298
  return;
17165
17299
  }
17166
17300
  const { teamId } = await resolveTeam(opts.team);
17167
- const spinner = (0, import_ora32.default)(`Approving ${origin}...`).start();
17301
+ const spinner = (0, import_ora33.default)(`Approving ${origin}...`).start();
17168
17302
  try {
17169
17303
  const out = await admin({
17170
17304
  method: "POST",
@@ -17181,7 +17315,7 @@ async function runOriginsApprove(origin, opts) {
17181
17315
  }
17182
17316
  async function runOriginsDeny(origin, opts) {
17183
17317
  const { teamId } = await resolveTeam(opts.team);
17184
- const spinner = (0, import_ora32.default)(`Dismissing ${origin}...`).start();
17318
+ const spinner = (0, import_ora33.default)(`Dismissing ${origin}...`).start();
17185
17319
  try {
17186
17320
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
17187
17321
  spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
@@ -17192,7 +17326,7 @@ async function runOriginsDeny(origin, opts) {
17192
17326
  }
17193
17327
  async function runOriginsRemove(origin, opts) {
17194
17328
  const { teamId } = await resolveTeam(opts.team);
17195
- const spinner = (0, import_ora32.default)(`Removing the proxy for ${origin}...`).start();
17329
+ const spinner = (0, import_ora33.default)(`Removing the proxy for ${origin}...`).start();
17196
17330
  try {
17197
17331
  await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
17198
17332
  spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
@@ -17203,7 +17337,7 @@ async function runOriginsRemove(origin, opts) {
17203
17337
  }
17204
17338
 
17205
17339
  // src/commands/op.ts
17206
- var import_chalk71 = __toESM(require("chalk"));
17340
+ var import_chalk72 = __toESM(require("chalk"));
17207
17341
  init_auth();
17208
17342
 
17209
17343
  // src/lib/op-call.ts
@@ -17232,7 +17366,7 @@ async function opCall(call3) {
17232
17366
  }
17233
17367
 
17234
17368
  // src/commands/op-money.ts
17235
- var import_chalk64 = __toESM(require("chalk"));
17369
+ var import_chalk65 = __toESM(require("chalk"));
17236
17370
  init_admin();
17237
17371
  init_types();
17238
17372
  var money2 = (cents2) => typeof cents2 === "number" ? `$${usdStr(cents2)}` : "\u2014";
@@ -17254,60 +17388,60 @@ async function opWalletList(opts) {
17254
17388
  const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list wallets" });
17255
17389
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
17256
17390
  const accounts = data?.accounts ?? [];
17257
- if (accounts.length === 0) return void console.log(import_chalk64.default.dim("No wallets."));
17258
- console.log(import_chalk64.default.dim(`
17391
+ if (accounts.length === 0) return void console.log(import_chalk65.default.dim("No wallets."));
17392
+ console.log(import_chalk65.default.dim(`
17259
17393
  ${"available".padStart(10)} ${"in use".padStart(9)} ${"grace".padStart(8)} ${"status".padEnd(11)} ${"gen".padStart(3)} wallet`));
17260
17394
  for (const a of accounts) {
17261
- const flags = [a.frozen ? import_chalk64.default.red("FROZEN") : "", a.closed ? import_chalk64.default.red("closed") : "", a.terms && a.terms !== "prepaid" ? import_chalk64.default.yellow(a.terms) : ""].filter(Boolean).join(" ");
17262
- const who = a.owner_email ? import_chalk64.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk64.default.dim(" \u2014 anon") : a.name ? import_chalk64.default.dim(` \u2014 ${a.name}`) : "";
17395
+ const flags = [a.frozen ? import_chalk65.default.red("FROZEN") : "", a.closed ? import_chalk65.default.red("closed") : "", a.terms && a.terms !== "prepaid" ? import_chalk65.default.yellow(a.terms) : ""].filter(Boolean).join(" ");
17396
+ const who = a.owner_email ? import_chalk65.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk65.default.dim(" \u2014 anon") : a.name ? import_chalk65.default.dim(` \u2014 ${a.name}`) : "";
17263
17397
  console.log(
17264
- ` ${import_chalk64.default.bold(money2(a.available_cents ?? a.balance_cents).padStart(10))} ${money2(a.in_use_cents ?? 0).padStart(9)} ${money2(a.grace_used_cents ?? 0).padStart(8)} ${String(a.status ?? "").padEnd(11)} ${String(a.generation ?? "").padStart(3)} ${a.walletId}${who}${flags ? " " + flags : ""}`
17398
+ ` ${import_chalk65.default.bold(money2(a.available_cents ?? a.balance_cents).padStart(10))} ${money2(a.in_use_cents ?? 0).padStart(9)} ${money2(a.grace_used_cents ?? 0).padStart(8)} ${String(a.status ?? "").padEnd(11)} ${String(a.generation ?? "").padStart(3)} ${a.walletId}${who}${flags ? " " + flags : ""}`
17265
17399
  );
17266
17400
  }
17267
- if (data?.truncated) console.log(import_chalk64.default.yellow(`
17401
+ if (data?.truncated) console.log(import_chalk65.default.yellow(`
17268
17402
  List truncated \u2014 inspect one wallet with: apiblaze op wallet inspect <wallet>`));
17269
17403
  console.log("");
17270
17404
  }
17271
17405
  async function opWalletInspect(ref, opts) {
17272
- if (!ref) return void console.log(import_chalk64.default.red("Which wallet?") + import_chalk64.default.cyan(" apiblaze op wallet inspect <wallet id | team id>"));
17406
+ if (!ref) return void console.log(import_chalk65.default.red("Which wallet?") + import_chalk65.default.cyan(" apiblaze op wallet inspect <wallet id | team id>"));
17273
17407
  const ba = await resolveWalletId(ref);
17274
17408
  const w = await opCall({ method: "GET", path: `/operator/wallets/${encodeURIComponent(ba)}`, summary: `inspect wallet ${ba}` });
17275
17409
  if (opts.json) return void console.log(JSON.stringify(w, null, 2));
17276
17410
  const st = w?.state ?? {};
17277
- console.log(import_chalk64.default.bold(`
17278
- Wallet ${w?.billing_account_id ?? ba}`) + (ref !== ba ? import_chalk64.default.dim(` (team ${ref})`) : ""));
17279
- console.log(` ${"status".padEnd(16)} ${w?.status ?? "?"}${w?.frozen ? import_chalk64.default.red(" FROZEN") : ""}${w?.closed ? import_chalk64.default.red(" closed") : ""}${w?.terms && w.terms !== "prepaid" ? import_chalk64.default.yellow(` ${w.terms}`) : ""}`);
17280
- console.log(` ${"available".padEnd(16)} ${import_chalk64.default.bold(money2(w?.available_cents ?? w?.balance_cents ?? w?.balance))}`);
17281
- console.log(` ${"in use".padEnd(16)} ${money2(w?.in_use_cents)}${import_chalk64.default.dim(" \u03A3 floats + budgets + reserves out at the shards")}`);
17411
+ console.log(import_chalk65.default.bold(`
17412
+ Wallet ${w?.billing_account_id ?? ba}`) + (ref !== ba ? import_chalk65.default.dim(` (team ${ref})`) : ""));
17413
+ console.log(` ${"status".padEnd(16)} ${w?.status ?? "?"}${w?.frozen ? import_chalk65.default.red(" FROZEN") : ""}${w?.closed ? import_chalk65.default.red(" closed") : ""}${w?.terms && w.terms !== "prepaid" ? import_chalk65.default.yellow(` ${w.terms}`) : ""}`);
17414
+ console.log(` ${"available".padEnd(16)} ${import_chalk65.default.bold(money2(w?.available_cents ?? w?.balance_cents ?? w?.balance))}`);
17415
+ console.log(` ${"in use".padEnd(16)} ${money2(w?.in_use_cents)}${import_chalk65.default.dim(" \u03A3 floats + budgets + reserves out at the shards")}`);
17282
17416
  console.log(` ${"grace".padEnd(16)} ${money2(w?.grace_used_cents)} of ${money2(w?.grace_limit_cents)}`);
17283
17417
  console.log(` ${"epoch / gen".padEnd(16)} ${st.epoch ?? "?"} / ${st.generation ?? w?.generation ?? "?"}`);
17284
17418
  const unsent = unsentLedgerEntries(st);
17285
17419
  const lastSend = lastLedgerSendAtMs(st);
17286
- console.log(` ${"unsent ledger".padEnd(16)} ${unsent ?? "\u2014"} entries${import_chalk64.default.dim(" made by the wallet, not yet sent to the ledger")}`);
17287
- console.log(` ${"last ledger send".padEnd(16)} ${lastSend ? new Date(lastSend).toISOString() : import_chalk64.default.dim("never")}`);
17420
+ console.log(` ${"unsent ledger".padEnd(16)} ${unsent ?? "\u2014"} entries${import_chalk65.default.dim(" made by the wallet, not yet sent to the ledger")}`);
17421
+ console.log(` ${"last ledger send".padEnd(16)} ${lastSend ? new Date(lastSend).toISOString() : import_chalk65.default.dim("never")}`);
17288
17422
  if (st.counters && typeof st.counters === "object") {
17289
17423
  const c = Object.entries(st.counters).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
17290
- if (c) console.log(import_chalk64.default.dim(` counters ${c}`));
17424
+ if (c) console.log(import_chalk65.default.dim(` counters ${c}`));
17291
17425
  }
17292
17426
  const shards = st.shards ?? [];
17293
17427
  if (shards.length) {
17294
- console.log(import_chalk64.default.bold(`
17428
+ console.log(import_chalk65.default.bold(`
17295
17429
  Shards (${shards.length})`));
17296
- console.log(import_chalk64.default.dim(` ${"shard".padEnd(14)} ${"team".padEnd(26)} ${"float".padStart(9)} ${"budget".padStart(9)} ${"acq/s".padStart(6)} ${"adm/s".padStart(6)} expires`));
17430
+ console.log(import_chalk65.default.dim(` ${"shard".padEnd(14)} ${"team".padEnd(26)} ${"float".padStart(9)} ${"budget".padStart(9)} ${"acq/s".padStart(6)} ${"adm/s".padStart(6)} expires`));
17297
17431
  for (const s of shards) {
17298
17432
  const exp = s.expiresAtMs ? `${Math.max(0, Math.round((s.expiresAtMs - Date.now()) / 1e3))}s` : "\u2014";
17299
17433
  console.log(
17300
- ` ${String(s.shardId ?? "").slice(0, 14).padEnd(14)} ${String(s.teamId ?? "").slice(0, 26).padEnd(26)} ${String(s.floatUnits ?? 0).padStart(9)} ${String(s.budgetUnits ?? 0).padStart(9)} ${String(s.acquiresPerS ?? 0).padStart(6)} ${String(s.admittedPerS ?? 0).padStart(6)} ${exp}${s.degraded ? import_chalk64.default.red(" degraded") : ""}`
17434
+ ` ${String(s.shardId ?? "").slice(0, 14).padEnd(14)} ${String(s.teamId ?? "").slice(0, 26).padEnd(26)} ${String(s.floatUnits ?? 0).padStart(9)} ${String(s.budgetUnits ?? 0).padStart(9)} ${String(s.acquiresPerS ?? 0).padStart(6)} ${String(s.admittedPerS ?? 0).padStart(6)} ${exp}${s.degraded ? import_chalk65.default.red(" degraded") : ""}`
17301
17435
  );
17302
17436
  }
17303
- console.log(import_chalk64.default.dim(" float/budget in ledger units (10,000 per cent)"));
17437
+ console.log(import_chalk65.default.dim(" float/budget in ledger units (10,000 per cent)"));
17304
17438
  } else {
17305
- console.log(import_chalk64.default.dim("\n No live shards (no traffic in the last window)."));
17439
+ console.log(import_chalk65.default.dim("\n No live shards (no traffic in the last window)."));
17306
17440
  }
17307
17441
  const teams = w?.teams ?? [];
17308
- if (teams.length) console.log(import_chalk64.default.dim(`
17442
+ if (teams.length) console.log(import_chalk65.default.dim(`
17309
17443
  Teams on this wallet: ${teams.map((t) => typeof t === "string" ? t : t.team_id ?? t.teamId ?? JSON.stringify(t)).join(", ")}`));
17310
- console.log(import_chalk64.default.dim(`
17444
+ console.log(import_chalk65.default.dim(`
17311
17445
  Act on it (dashboard only): ${walletPage(ba)}
17312
17446
  `));
17313
17447
  }
@@ -17336,11 +17470,11 @@ function opWalletMutation(action2, ref, opts) {
17336
17470
  const target2 = ref ?? "<wallet>";
17337
17471
  const url = ref && !isTeamId(ref) ? walletPage(ref) : `${DASHBOARD_BASE10}/operator/wallets`;
17338
17472
  if (opts.json) return void console.log(JSON.stringify({ action: action2, wallet: ref ?? null, dashboard_only: true, url, requires: need ?? "a reason" }));
17339
- console.log(import_chalk64.default.yellow(`
17473
+ console.log(import_chalk65.default.yellow(`
17340
17474
  \`op wallet ${action2}\` is dashboard-only \u2014 it writes an operator_audit row with YOUR reason before touching the root.`));
17341
- console.log(` ${import_chalk64.default.cyan(url)}`);
17342
- console.log(import_chalk64.default.dim(` ${target2}: needs ${need ?? "a reason (required)"}`));
17343
- if (ref && isTeamId(ref)) console.log(import_chalk64.default.dim(` (that is a team id \u2014 \`apiblaze op wallet inspect ${ref}\` prints its wallet id and page)`));
17475
+ console.log(` ${import_chalk65.default.cyan(url)}`);
17476
+ console.log(import_chalk65.default.dim(` ${target2}: needs ${need ?? "a reason (required)"}`));
17477
+ if (ref && isTeamId(ref)) console.log(import_chalk65.default.dim(` (that is a team id \u2014 \`apiblaze op wallet inspect ${ref}\` prints its wallet id and page)`));
17344
17478
  console.log("");
17345
17479
  }
17346
17480
  async function opMoneyExposure(opts) {
@@ -17348,24 +17482,24 @@ async function opMoneyExposure(opts) {
17348
17482
  const data = await opCall({ method: "GET", path: `/operator/exposure${qs}`, summary: "money exposure" });
17349
17483
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
17350
17484
  const m = data?.money;
17351
- console.log(import_chalk64.default.bold("\n Exposure") + import_chalk64.default.dim(` source: ${data?.source ?? "?"}${data?.source === "ae" ? " (Analytics Engine fallback \u2014 approximate; the root did not answer)" : " (the root \u2014 exact)"}`));
17352
- if (data?.money_error) console.log(import_chalk64.default.yellow(` root error: ${data.money_error}`));
17485
+ console.log(import_chalk65.default.bold("\n Exposure") + import_chalk65.default.dim(` source: ${data?.source ?? "?"}${data?.source === "ae" ? " (Analytics Engine fallback \u2014 approximate; the root did not answer)" : " (the root \u2014 exact)"}`));
17486
+ if (data?.money_error) console.log(import_chalk65.default.yellow(` root error: ${data.money_error}`));
17353
17487
  if (m) {
17354
- console.log(` ${"in use".padEnd(14)} ${import_chalk64.default.bold(money2(m.total_in_use_cents))}${import_chalk64.default.dim(" customer money out at the shards, pre-paid (\u2264 0.15 \xD7 unallocated)")}`);
17355
- console.log(` ${"grace used".padEnd(14)} ${money2(m.total_grace_used_cents)}${import_chalk64.default.dim(" platform loss bound: \u03A3 grace + chargebacks")}`);
17488
+ console.log(` ${"in use".padEnd(14)} ${import_chalk65.default.bold(money2(m.total_in_use_cents))}${import_chalk65.default.dim(" customer money out at the shards, pre-paid (\u2264 0.15 \xD7 unallocated)")}`);
17489
+ console.log(` ${"grace used".padEnd(14)} ${money2(m.total_grace_used_cents)}${import_chalk65.default.dim(" platform loss bound: \u03A3 grace + chargebacks")}`);
17356
17490
  const wallets = m.wallets ?? [];
17357
17491
  if (wallets.length) {
17358
- console.log(import_chalk64.default.dim(`
17492
+ console.log(import_chalk65.default.dim(`
17359
17493
  ${"in use".padStart(10)} ${"grace".padStart(8)} wallet`));
17360
17494
  for (const w of wallets.slice(0, 40)) {
17361
- console.log(` ${money2(w.in_use_cents).padStart(10)} ${money2(w.grace_used_cents ?? (typeof w.grace_used_units === "number" ? w.grace_used_units / 1e4 : void 0)).padStart(8)} ${w.billing_account_id ?? w.walletId}${w.frozen ? import_chalk64.default.red(" FROZEN") : ""}`);
17495
+ console.log(` ${money2(w.in_use_cents).padStart(10)} ${money2(w.grace_used_cents ?? (typeof w.grace_used_units === "number" ? w.grace_used_units / 1e4 : void 0)).padStart(8)} ${w.billing_account_id ?? w.walletId}${w.frozen ? import_chalk65.default.red(" FROZEN") : ""}`);
17362
17496
  }
17363
- if (wallets.length > 40) console.log(import_chalk64.default.dim(` \u2026 ${wallets.length - 40} more (--json)`));
17497
+ if (wallets.length > 40) console.log(import_chalk65.default.dim(` \u2026 ${wallets.length - 40} more (--json)`));
17364
17498
  }
17365
17499
  }
17366
17500
  const ae = data?.ae;
17367
17501
  if (ae && typeof ae === "object") {
17368
- console.log(import_chalk64.default.bold("\n Analytics Engine view") + import_chalk64.default.dim(" admission by gate source, last window"));
17502
+ console.log(import_chalk65.default.bold("\n Analytics Engine view") + import_chalk65.default.dim(" admission by gate source, last window"));
17369
17503
  for (const [k, v] of Object.entries(ae)) console.log(` ${k.padEnd(18)} ${typeof v === "object" ? JSON.stringify(v) : String(v)}`);
17370
17504
  }
17371
17505
  console.log("");
@@ -17379,24 +17513,24 @@ async function opIncidentStatus(opts) {
17379
17513
  const data = await opCall({ method: "GET", path: "/operator/incident/degraded-mode", summary: "degraded-mode switch" });
17380
17514
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
17381
17515
  const mode = String(data?.mode ?? "allow");
17382
- const colour = mode === "allow" ? import_chalk64.default.green : mode === "deny" ? import_chalk64.default.red : import_chalk64.default.yellow;
17383
- console.log(import_chalk64.default.bold("\n Degraded mode: ") + colour(mode) + import_chalk64.default.dim(` \u2014 ${MODE_WORDS[mode] ?? ""}`));
17384
- if (data?.reason) console.log(import_chalk64.default.dim(` reason: ${data.reason}`));
17385
- if (data?.actor || data?.at) console.log(import_chalk64.default.dim(` set by ${data?.actor ?? "?"}${data?.at ? ` at ${data.at}` : ""}`));
17386
- console.log(import_chalk64.default.dim(`
17516
+ const colour = mode === "allow" ? import_chalk65.default.green : mode === "deny" ? import_chalk65.default.red : import_chalk65.default.yellow;
17517
+ console.log(import_chalk65.default.bold("\n Degraded mode: ") + colour(mode) + import_chalk65.default.dim(` \u2014 ${MODE_WORDS[mode] ?? ""}`));
17518
+ if (data?.reason) console.log(import_chalk65.default.dim(` reason: ${data.reason}`));
17519
+ if (data?.actor || data?.at) console.log(import_chalk65.default.dim(` set by ${data?.actor ?? "?"}${data?.at ? ` at ${data.at}` : ""}`));
17520
+ console.log(import_chalk65.default.dim(`
17387
17521
  Change it (dashboard only, reason required): ${incidentPage()}
17388
17522
  `));
17389
17523
  }
17390
17524
  function opIncidentMutation(mode, opts) {
17391
17525
  if (opts.json) return void console.log(JSON.stringify({ action: `incident ${mode}`, dashboard_only: true, url: incidentPage(), requires: "a reason (required); the switch is written to operator_audit, then KV" }));
17392
- console.log(import_chalk64.default.yellow(`
17526
+ console.log(import_chalk65.default.yellow(`
17393
17527
  \`op incident ${mode}\` is dashboard-only \u2014 ${MODE_WORDS[mode] ?? mode}.`));
17394
- console.log(` ${import_chalk64.default.cyan(incidentPage())}`);
17395
- console.log(import_chalk64.default.dim(" Needs a reason (required); it is written to operator_audit before the switch flips. Check with: apiblaze op incident status\n"));
17528
+ console.log(` ${import_chalk65.default.cyan(incidentPage())}`);
17529
+ console.log(import_chalk65.default.dim(" Needs a reason (required); it is written to operator_audit before the switch flips. Check with: apiblaze op incident status\n"));
17396
17530
  }
17397
17531
 
17398
17532
  // src/commands/op-ceiling.ts
17399
- var import_chalk65 = __toESM(require("chalk"));
17533
+ var import_chalk66 = __toESM(require("chalk"));
17400
17534
  init_types();
17401
17535
  var STATUSES = ["pending", "decided", "all"];
17402
17536
  var rpsStr = (n) => typeof n === "number" && Number.isFinite(n) ? n.toLocaleString("en-US") : "\u2014";
@@ -17410,7 +17544,7 @@ function ageStr(iso, now = Date.now()) {
17410
17544
  return `${Math.floor(s / 86400)}d`;
17411
17545
  }
17412
17546
  var trunc = (s, n) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
17413
- var STATUS_COLOUR = { pending: import_chalk65.default.yellow, approved: import_chalk65.default.green, denied: import_chalk65.default.red, expired: import_chalk65.default.dim };
17547
+ var STATUS_COLOUR = { pending: import_chalk66.default.yellow, approved: import_chalk66.default.green, denied: import_chalk66.default.red, expired: import_chalk66.default.dim };
17414
17548
  function printDecideError(err, id, opts) {
17415
17549
  if (!(err instanceof ApiError) || ![400, 404, 409].includes(err.status)) return false;
17416
17550
  const body = err.body ?? {};
@@ -17423,12 +17557,12 @@ function printDecideError(err, id, opts) {
17423
17557
  if (err.status === 409) {
17424
17558
  const d = body.request;
17425
17559
  const by = d?.decided_by ? ` by ${d.decided_by}${d.decided_at ? ` at ${d.decided_at}` : ""}` : "";
17426
- console.log(import_chalk65.default.red(` Ceiling request ${id} was not changed: ${why}${by}.`));
17427
- console.log(import_chalk65.default.dim(" See the decision: apiblaze op ceiling list --status decided"));
17560
+ console.log(import_chalk66.default.red(` Ceiling request ${id} was not changed: ${why}${by}.`));
17561
+ console.log(import_chalk66.default.dim(" See the decision: apiblaze op ceiling list --status decided"));
17428
17562
  } else if (err.status === 404) {
17429
- console.log(import_chalk65.default.red(` Not found: ${why.replace(/^Not found:\s*/i, "")}.`) + import_chalk65.default.dim(" (ids: apiblaze op ceiling list)"));
17563
+ console.log(import_chalk66.default.red(` Not found: ${why.replace(/^Not found:\s*/i, "")}.`) + import_chalk66.default.dim(" (ids: apiblaze op ceiling list)"));
17430
17564
  } else {
17431
- console.log(import_chalk65.default.red(` Ceiling request ${id} not decided: ${why}.`));
17565
+ console.log(import_chalk66.default.red(` Ceiling request ${id} not decided: ${why}.`));
17432
17566
  }
17433
17567
  return true;
17434
17568
  }
@@ -17436,31 +17570,31 @@ async function opCeilingList(opts) {
17436
17570
  const status2 = String(opts.status ?? "pending").trim().toLowerCase();
17437
17571
  if (!STATUSES.includes(status2)) {
17438
17572
  process.exitCode = 1;
17439
- return void console.log(import_chalk65.default.red(`--status must be one of ${STATUSES.join(" | ")}.`));
17573
+ return void console.log(import_chalk66.default.red(`--status must be one of ${STATUSES.join(" | ")}.`));
17440
17574
  }
17441
17575
  const data = await opCall(
17442
17576
  { method: "GET", path: `/operator/ceiling-requests?status=${status2}`, summary: `${status2} ceiling requests` }
17443
17577
  );
17444
17578
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
17445
17579
  const rows = data?.requests ?? [];
17446
- console.log(import_chalk65.default.bold("\n Maximum-rate requests") + import_chalk65.default.dim(` ${status2} \xB7 all consumers together, per project`));
17447
- if (!rows.length) return void console.log(import_chalk65.default.dim(` No ${status2 === "all" ? "" : `${status2} `}requests.
17580
+ console.log(import_chalk66.default.bold("\n Maximum-rate requests") + import_chalk66.default.dim(` ${status2} \xB7 all consumers together, per project`));
17581
+ if (!rows.length) return void console.log(import_chalk66.default.dim(` No ${status2 === "all" ? "" : `${status2} `}requests.
17448
17582
  `));
17449
- console.log(import_chalk65.default.dim(` ${"id".padEnd(10)} ${"project/version".padEnd(26)} ${"team".padEnd(16)} ${"current \u2192 requested req/s".padEnd(25)} ${"age".padStart(4)} ${"requested by".padEnd(24)} ${"status".padEnd(8)} use case`));
17583
+ console.log(import_chalk66.default.dim(` ${"id".padEnd(10)} ${"project/version".padEnd(26)} ${"team".padEnd(16)} ${"current \u2192 requested req/s".padEnd(25)} ${"age".padStart(4)} ${"requested by".padEnd(24)} ${"status".padEnd(8)} use case`));
17450
17584
  for (const r of rows) {
17451
17585
  const rate = `${rpsStr(r.current_ceiling_rps)} \u2192 ${rpsStr(r.requested_rps)}`;
17452
- const granted = r.status === "approved" && typeof r.granted_rps === "number" && r.granted_rps !== r.requested_rps ? import_chalk65.default.dim(` (granted ${rpsStr(r.granted_rps)})`) : "";
17586
+ const granted = r.status === "approved" && typeof r.granted_rps === "number" && r.granted_rps !== r.requested_rps ? import_chalk66.default.dim(` (granted ${rpsStr(r.granted_rps)})`) : "";
17453
17587
  const colour = STATUS_COLOUR[r.status] ?? ((s) => s);
17454
17588
  console.log(
17455
- ` ${trunc(String(r.id), 10).padEnd(10)} ${trunc(`${r.project_id}/${r.api_version}`, 26).padEnd(26)} ${trunc(r.team_id ?? "\u2014", 16).padEnd(16)} ${rate.padEnd(25)} ${ageStr(r.created_at).padStart(4)} ${trunc(r.requested_by ?? "\u2014", 24).padEnd(24)} ${colour(r.status.padEnd(8))} ${import_chalk65.default.dim(trunc((r.use_case ?? "").replace(/\s+/g, " "), 48))}${granted}`
17589
+ ` ${trunc(String(r.id), 10).padEnd(10)} ${trunc(`${r.project_id}/${r.api_version}`, 26).padEnd(26)} ${trunc(r.team_id ?? "\u2014", 16).padEnd(16)} ${rate.padEnd(25)} ${ageStr(r.created_at).padStart(4)} ${trunc(r.requested_by ?? "\u2014", 24).padEnd(24)} ${colour(r.status.padEnd(8))} ${import_chalk66.default.dim(trunc((r.use_case ?? "").replace(/\s+/g, " "), 48))}${granted}`
17456
17590
  );
17457
17591
  if (r.status !== "pending" && (r.decided_by || r.decision_note)) {
17458
- console.log(import_chalk65.default.dim(` ${"".padEnd(10)} ${r.decided_by ?? "?"}${r.decided_at ? ` \xB7 ${ageStr(r.decided_at)} ago` : ""}${r.decision_note ? ` \xB7 ${trunc(r.decision_note, 80)}` : ""}`));
17592
+ console.log(import_chalk66.default.dim(` ${"".padEnd(10)} ${r.decided_by ?? "?"}${r.decided_at ? ` \xB7 ${ageStr(r.decided_at)} ago` : ""}${r.decision_note ? ` \xB7 ${trunc(r.decision_note, 80)}` : ""}`));
17459
17593
  }
17460
17594
  }
17461
17595
  if (status2 === "pending") {
17462
17596
  const presets = (data?.presets_rps ?? []).map(rpsStr).join(" / ");
17463
- console.log(import_chalk65.default.dim(`
17597
+ console.log(import_chalk66.default.dim(`
17464
17598
  Decide: apiblaze op ceiling approve <id> [--rps N${presets ? ` e.g. ${presets}` : ""}] [--note "\u2026"] \xB7 apiblaze op ceiling deny <id> --note "why"`));
17465
17599
  }
17466
17600
  console.log("");
@@ -17468,23 +17602,23 @@ async function opCeilingList(opts) {
17468
17602
  async function opCeilingDecide(decision, id, opts) {
17469
17603
  if (!id) {
17470
17604
  process.exitCode = 1;
17471
- return void console.log(import_chalk65.default.red("Which request?") + import_chalk65.default.cyan(` apiblaze op ceiling ${decision} <id>${decision === "deny" ? ' --note "why"' : ' [--rps N] [--note "\u2026"]'}`) + import_chalk65.default.dim(" (ids: apiblaze op ceiling list)"));
17605
+ return void console.log(import_chalk66.default.red("Which request?") + import_chalk66.default.cyan(` apiblaze op ceiling ${decision} <id>${decision === "deny" ? ' --note "why"' : ' [--rps N] [--note "\u2026"]'}`) + import_chalk66.default.dim(" (ids: apiblaze op ceiling list)"));
17472
17606
  }
17473
17607
  const note = typeof opts.note === "string" ? opts.note.trim() : "";
17474
17608
  if (decision === "deny" && !note) {
17475
17609
  process.exitCode = 1;
17476
- return void console.log(import_chalk65.default.red("A denial needs a reason the producer will read:") + import_chalk65.default.cyan(` apiblaze op ceiling deny ${id} --note "why"`));
17610
+ return void console.log(import_chalk66.default.red("A denial needs a reason the producer will read:") + import_chalk66.default.cyan(` apiblaze op ceiling deny ${id} --note "why"`));
17477
17611
  }
17478
17612
  let rps;
17479
17613
  if (opts.rps !== void 0 && opts.rps !== null && opts.rps !== "") {
17480
17614
  if (decision === "deny") {
17481
17615
  process.exitCode = 1;
17482
- return void console.log(import_chalk65.default.red("--rps only applies to approve."));
17616
+ return void console.log(import_chalk66.default.red("--rps only applies to approve."));
17483
17617
  }
17484
17618
  rps = Number(String(opts.rps).replace(/[,_]/g, ""));
17485
17619
  if (!Number.isInteger(rps) || rps <= 0) {
17486
17620
  process.exitCode = 1;
17487
- return void console.log(import_chalk65.default.red("--rps must be a whole number of requests per second (e.g. 5000)."));
17621
+ return void console.log(import_chalk66.default.red("--rps must be a whole number of requests per second (e.g. 5000)."));
17488
17622
  }
17489
17623
  }
17490
17624
  const body = { decision };
@@ -17509,19 +17643,19 @@ async function opCeilingDecide(decision, id, opts) {
17509
17643
  const where = r ? `${r.project_id}/${r.api_version}` : `request ${id}`;
17510
17644
  if (decision === "approve") {
17511
17645
  const to = res?.limits?.ceilingRps ?? res?.request?.granted_rps ?? rps ?? r?.requested_rps;
17512
- console.log(import_chalk65.default.green(`
17646
+ console.log(import_chalk66.default.green(`
17513
17647
  project ${where}: maximum rate ${rpsStr(from)} \u2192 ${rpsStr(to)} req/s`));
17514
- if (r?.requested_rps !== void 0 && to !== void 0 && to !== r.requested_rps) console.log(import_chalk65.default.dim(` (asked for ${rpsStr(r.requested_rps)} req/s)`));
17648
+ if (r?.requested_rps !== void 0 && to !== void 0 && to !== r.requested_rps) console.log(import_chalk66.default.dim(` (asked for ${rpsStr(r.requested_rps)} req/s)`));
17515
17649
  } else {
17516
- console.log(import_chalk65.default.yellow(`
17517
- project ${where}: request for ${rpsStr(r?.requested_rps)} req/s denied`) + (typeof from === "number" ? import_chalk65.default.dim(` \u2014 maximum rate stays ${rpsStr(from)} req/s`) : ""));
17650
+ console.log(import_chalk66.default.yellow(`
17651
+ project ${where}: request for ${rpsStr(r?.requested_rps)} req/s denied`) + (typeof from === "number" ? import_chalk66.default.dim(` \u2014 maximum rate stays ${rpsStr(from)} req/s`) : ""));
17518
17652
  }
17519
- if (note) console.log(import_chalk65.default.dim(` note: ${note}`));
17653
+ if (note) console.log(import_chalk66.default.dim(` note: ${note}`));
17520
17654
  console.log("");
17521
17655
  }
17522
17656
 
17523
17657
  // src/commands/op-ledger.ts
17524
- var import_chalk66 = __toESM(require("chalk"));
17658
+ var import_chalk67 = __toESM(require("chalk"));
17525
17659
  init_admin();
17526
17660
  init_types();
17527
17661
  var UNITS_PER_CENT = 1e4;
@@ -17548,7 +17682,7 @@ function parseLimit(raw, max) {
17548
17682
  function fail6(opts, msg) {
17549
17683
  process.exitCode = 1;
17550
17684
  if (opts.json) return void console.log(JSON.stringify({ ok: false, error: msg }));
17551
- console.log(import_chalk66.default.red(msg));
17685
+ console.log(import_chalk67.default.red(msg));
17552
17686
  }
17553
17687
  function printLedgerError(err, what, opts) {
17554
17688
  if (!(err instanceof ApiError) || ![400, 403, 404, 500, 502, 503].includes(err.status)) return false;
@@ -17559,9 +17693,9 @@ function printLedgerError(err, what, opts) {
17559
17693
  return true;
17560
17694
  }
17561
17695
  const code = body.error && body.error !== err.message ? ` ${body.error}` : "";
17562
- console.log(import_chalk66.default.red(` ${what} failed (${err.status}${code}): ${body.details ?? err.message}`));
17563
- if (err.status === 503 && body.error === "money_unreachable") console.log(import_chalk66.default.dim(" The money worker did not answer \u2014 retry in a minute; nothing was changed."));
17564
- if (err.status === 503 && body.error === "audit_unavailable") console.log(import_chalk66.default.dim(" The operator_audit row could not be written, so the action was refused."));
17696
+ console.log(import_chalk67.default.red(` ${what} failed (${err.status}${code}): ${body.details ?? err.message}`));
17697
+ if (err.status === 503 && body.error === "money_unreachable") console.log(import_chalk67.default.dim(" The money worker did not answer \u2014 retry in a minute; nothing was changed."));
17698
+ if (err.status === 503 && body.error === "audit_unavailable") console.log(import_chalk67.default.dim(" The operator_audit row could not be written, so the action was refused."));
17565
17699
  return true;
17566
17700
  }
17567
17701
  async function call(opts, what, req) {
@@ -17574,33 +17708,33 @@ async function call(opts, what, req) {
17574
17708
  }
17575
17709
  function renderLedger2(data, opts = {}) {
17576
17710
  const out = [];
17577
- out.push(import_chalk66.default.bold(`
17578
- Ledger \u2014 ${data.billing_account_id}`) + import_chalk66.default.dim(` money_ledger, oldest first${opts.since ? ` since ${opts.since}` : ""}${opts.kind ? ` \xB7 kind=${opts.kind}` : ""}`));
17711
+ out.push(import_chalk67.default.bold(`
17712
+ Ledger \u2014 ${data.billing_account_id}`) + import_chalk67.default.dim(` money_ledger, oldest first${opts.since ? ` since ${opts.since}` : ""}${opts.kind ? ` \xB7 kind=${opts.kind}` : ""}`));
17579
17713
  const h = data.head;
17580
17714
  if (h) {
17581
- const gap = h.contiguous_to < h.head_seq ? import_chalk66.default.yellow(` GAP: ${h.head_seq - h.contiguous_to} seq missing after ${h.contiguous_to}`) : import_chalk66.default.green(" contiguous");
17715
+ const gap = h.contiguous_to < h.head_seq ? import_chalk67.default.yellow(` GAP: ${h.head_seq - h.contiguous_to} seq missing after ${h.contiguous_to}`) : import_chalk67.default.green(" contiguous");
17582
17716
  out.push(` head gen ${h.generation} \xB7 seq ${h.head_seq} \xB7 balance ${money3(cents(h.head_balance))} \xB7 at ${ts(h.head_ts)} \xB7 contiguous to ${h.contiguous_to}${gap}`);
17583
17717
  } else {
17584
- out.push(import_chalk66.default.dim(" head none \u2014 Neon has no row for this wallet yet"));
17718
+ out.push(import_chalk67.default.dim(" head none \u2014 Neon has no row for this wallet yet"));
17585
17719
  }
17586
17720
  const rows = data.rows ?? [];
17587
17721
  if (!rows.length) {
17588
- out.push(import_chalk66.default.dim(" No rows."));
17722
+ out.push(import_chalk67.default.dim(" No rows."));
17589
17723
  return out;
17590
17724
  }
17591
- out.push(import_chalk66.default.dim(` ${"gen".padStart(3)} ${"seq".padStart(13)} ${"ts".padEnd(19)} ${"class".padEnd(5)} ${"kind".padEnd(18)} ${"amount".padStart(12)} ${"balance after".padStart(13)} flag`));
17725
+ out.push(import_chalk67.default.dim(` ${"gen".padStart(3)} ${"seq".padStart(13)} ${"ts".padEnd(19)} ${"class".padEnd(5)} ${"kind".padEnd(18)} ${"amount".padStart(12)} ${"balance after".padStart(13)} flag`));
17592
17726
  for (const r of rows) {
17593
17727
  const c = typeof r.units_cents === "number" ? r.units_cents : cents(r.units);
17594
17728
  const amt = signedMoney2(c);
17595
17729
  const bal = typeof r.balance_after_cents === "number" ? r.balance_after_cents : cents(r.balance_after);
17596
- const flag = r.flag ? import_chalk66.default.red(r.flag) : "";
17597
- const n = r.class === "flow" && (r.count ?? 0) > 1 ? import_chalk66.default.dim(` \xD7${r.count}`) : "";
17730
+ const flag = r.flag ? import_chalk67.default.red(r.flag) : "";
17731
+ const n = r.class === "flow" && (r.count ?? 0) > 1 ? import_chalk67.default.dim(` \xD7${r.count}`) : "";
17598
17732
  out.push(
17599
- ` ${String(r.generation).padStart(3)} ${seqRange(r).padStart(13)} ${ts(r.ts_to).padEnd(19)} ${r.class.padEnd(5)} ${trunc2(String(r.kind ?? ""), 18).padEnd(18)} ${(typeof c === "number" && c < 0 ? import_chalk66.default.red : import_chalk66.default.green)(amt.padStart(12))} ${money3(bal).padStart(13)} ${flag}${n}`
17733
+ ` ${String(r.generation).padStart(3)} ${seqRange(r).padStart(13)} ${ts(r.ts_to).padEnd(19)} ${r.class.padEnd(5)} ${trunc2(String(r.kind ?? ""), 18).padEnd(18)} ${(typeof c === "number" && c < 0 ? import_chalk67.default.red : import_chalk67.default.green)(amt.padStart(12))} ${money3(bal).padStart(13)} ${flag}${n}`
17600
17734
  );
17601
17735
  }
17602
- out.push(import_chalk66.default.bold(` ${"\u03A3".padStart(17)} ${"".padEnd(19)} ${"".padEnd(5)} ${"".padEnd(18)} ${signedMoney2(cents(data.sum_units)).padStart(12)}`));
17603
- if (data.partial) out.push(import_chalk66.default.yellow(` More than ${data.limit} rows in the window \u2014 narrow --since / --kind or raise --limit (\u2264 1000) before trusting the sum.`));
17736
+ out.push(import_chalk67.default.bold(` ${"\u03A3".padStart(17)} ${"".padEnd(19)} ${"".padEnd(5)} ${"".padEnd(18)} ${signedMoney2(cents(data.sum_units)).padStart(12)}`));
17737
+ if (data.partial) out.push(import_chalk67.default.yellow(` More than ${data.limit} rows in the window \u2014 narrow --since / --kind or raise --limit (\u2264 1000) before trusting the sum.`));
17604
17738
  return out;
17605
17739
  }
17606
17740
  async function opLedgerShow(ref, opts) {
@@ -17625,25 +17759,25 @@ async function opLedgerLag(opts) {
17625
17759
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
17626
17760
  const newest = data.newest_received_at;
17627
17761
  const ageS = newest ? Math.max(0, Math.round((Date.now() - Date.parse(newest)) / 1e3)) : null;
17628
- console.log(import_chalk66.default.bold("\n Ledger lag") + import_chalk66.default.dim(" money_ledger health (gaps, flags, quarantine)"));
17629
- console.log(` ${"newest row".padEnd(16)} ${newest ? `${ts(newest)} (${ageS}s ago)` : import_chalk66.default.dim("none")}`);
17630
- console.log(` ${"gaps".padEnd(16)} ${(data.gap_count ? import_chalk66.default.yellow : import_chalk66.default.green)(String(data.gap_count))}${import_chalk66.default.dim(" wallets whose contiguous_to < head_seq")}`);
17631
- console.log(` ${"flagged 24h".padEnd(16)} ${(data.flagged_24h?.count ? import_chalk66.default.red : import_chalk66.default.green)(String(data.flagged_24h?.count ?? 0))}${import_chalk66.default.dim(" mismatch flags on rows received in the last 24 h")}`);
17632
- console.log(` ${"quarantine 24h".padEnd(16)} ${(data.quarantine_24h ? import_chalk66.default.red : import_chalk66.default.green)(String(data.quarantine_24h))}${import_chalk66.default.dim(" apiblaze op ledger quarantine")}`);
17762
+ console.log(import_chalk67.default.bold("\n Ledger lag") + import_chalk67.default.dim(" money_ledger health (gaps, flags, quarantine)"));
17763
+ console.log(` ${"newest row".padEnd(16)} ${newest ? `${ts(newest)} (${ageS}s ago)` : import_chalk67.default.dim("none")}`);
17764
+ console.log(` ${"gaps".padEnd(16)} ${(data.gap_count ? import_chalk67.default.yellow : import_chalk67.default.green)(String(data.gap_count))}${import_chalk67.default.dim(" wallets whose contiguous_to < head_seq")}`);
17765
+ console.log(` ${"flagged 24h".padEnd(16)} ${(data.flagged_24h?.count ? import_chalk67.default.red : import_chalk67.default.green)(String(data.flagged_24h?.count ?? 0))}${import_chalk67.default.dim(" mismatch flags on rows received in the last 24 h")}`);
17766
+ console.log(` ${"quarantine 24h".padEnd(16)} ${(data.quarantine_24h ? import_chalk67.default.red : import_chalk67.default.green)(String(data.quarantine_24h))}${import_chalk67.default.dim(" apiblaze op ledger quarantine")}`);
17633
17767
  if (data.gaps?.length) {
17634
- console.log(import_chalk66.default.bold("\n Gaps") + import_chalk66.default.dim(" (biggest first)"));
17635
- console.log(import_chalk66.default.dim(` ${"missing".padStart(7)} ${"gen".padStart(3)} ${"contig \u2192 head".padEnd(17)} ${"head at".padEnd(19)} wallet`));
17768
+ console.log(import_chalk67.default.bold("\n Gaps") + import_chalk67.default.dim(" (biggest first)"));
17769
+ console.log(import_chalk67.default.dim(` ${"missing".padStart(7)} ${"gen".padStart(3)} ${"contig \u2192 head".padEnd(17)} ${"head at".padEnd(19)} wallet`));
17636
17770
  for (const g of data.gaps.slice(0, 40)) {
17637
- console.log(` ${import_chalk66.default.yellow(String(g.missing).padStart(7))} ${String(g.generation).padStart(3)} ${`${g.contiguous_to} \u2192 ${g.head_seq}`.padEnd(17)} ${ts(g.head_ts).padEnd(19)} ${g.billing_account_id}`);
17771
+ console.log(` ${import_chalk67.default.yellow(String(g.missing).padStart(7))} ${String(g.generation).padStart(3)} ${`${g.contiguous_to} \u2192 ${g.head_seq}`.padEnd(17)} ${ts(g.head_ts).padEnd(19)} ${g.billing_account_id}`);
17638
17772
  }
17639
- if (data.gaps.length > 40) console.log(import_chalk66.default.dim(` \u2026 ${data.gaps.length - 40} more (--json)`));
17640
- console.log(import_chalk66.default.dim(" A gap the queue is not about to fill (> 30 min): apiblaze op ledger cut <wallet>, or backfill (\xA76.4)."));
17773
+ if (data.gaps.length > 40) console.log(import_chalk67.default.dim(` \u2026 ${data.gaps.length - 40} more (--json)`));
17774
+ console.log(import_chalk67.default.dim(" A gap the queue is not about to fill (> 30 min): apiblaze op ledger cut <wallet>, or backfill (\xA76.4)."));
17641
17775
  }
17642
17776
  const flagged = data.flagged_24h?.rows ?? [];
17643
17777
  if (flagged.length) {
17644
- console.log(import_chalk66.default.bold("\n Flagged rows (24 h)"));
17645
- for (const f of flagged.slice(0, 40)) console.log(` ${ts(f.received_at)} gen ${f.generation} seq ${seqRange(f).padEnd(11)} ${trunc2(f.kind, 14).padEnd(14)} ${import_chalk66.default.red(f.flag)} ${f.billing_account_id}`);
17646
- if (flagged.length > 40) console.log(import_chalk66.default.dim(` \u2026 ${flagged.length - 40} more (--json)`));
17778
+ console.log(import_chalk67.default.bold("\n Flagged rows (24 h)"));
17779
+ for (const f of flagged.slice(0, 40)) console.log(` ${ts(f.received_at)} gen ${f.generation} seq ${seqRange(f).padEnd(11)} ${trunc2(f.kind, 14).padEnd(14)} ${import_chalk67.default.red(f.flag)} ${f.billing_account_id}`);
17780
+ if (flagged.length > 40) console.log(import_chalk67.default.dim(` \u2026 ${flagged.length - 40} more (--json)`));
17647
17781
  }
17648
17782
  console.log("");
17649
17783
  }
@@ -17656,14 +17790,14 @@ async function opLedgerQuarantine(opts) {
17656
17790
  const data = await call(opts, "Quarantine read", { method: "GET", path: `/operator/ledger/quarantine${qs.toString() ? `?${qs}` : ""}`, summary: "ledger quarantine" });
17657
17791
  if (!data) return;
17658
17792
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
17659
- console.log(import_chalk66.default.bold("\n Ledger quarantine") + import_chalk66.default.dim(` newest first${data.ba ? ` \xB7 ${data.ba}` : ""} \xB7 ${data.count_24h} in the last 24 h`));
17793
+ console.log(import_chalk67.default.bold("\n Ledger quarantine") + import_chalk67.default.dim(` newest first${data.ba ? ` \xB7 ${data.ba}` : ""} \xB7 ${data.count_24h} in the last 24 h`));
17660
17794
  const rows = data.rows ?? [];
17661
- if (!rows.length) return void console.log(import_chalk66.default.green(" Empty.\n"));
17662
- console.log(import_chalk66.default.dim(` ${"received".padEnd(19)} ${"reason".padEnd(10)} ${"gen".padStart(3)} ${"seq".padStart(13)} wallet`));
17795
+ if (!rows.length) return void console.log(import_chalk67.default.green(" Empty.\n"));
17796
+ console.log(import_chalk67.default.dim(` ${"received".padEnd(19)} ${"reason".padEnd(10)} ${"gen".padStart(3)} ${"seq".padStart(13)} wallet`));
17663
17797
  for (const r of rows) {
17664
- console.log(` ${ts(r.received_at).padEnd(19)} ${import_chalk66.default.red(trunc2(String(r.reason), 10).padEnd(10))} ${String(r.generation).padStart(3)} ${seqRange(r).padStart(13)} ${r.billing_account_id}${r.existing ? import_chalk66.default.dim(" (differs from the stored row)") : ""}`);
17798
+ console.log(` ${ts(r.received_at).padEnd(19)} ${import_chalk67.default.red(trunc2(String(r.reason), 10).padEnd(10))} ${String(r.generation).padStart(3)} ${seqRange(r).padStart(13)} ${r.billing_account_id}${r.existing ? import_chalk67.default.dim(" (differs from the stored row)") : ""}`);
17665
17799
  }
17666
- console.log(import_chalk66.default.dim("\n Both versions: --json. The fix is always wallet-side (code fix, and an `adjust` if money was wrong) \u2014 never an edit of Neon (\xA76.6).\n"));
17800
+ console.log(import_chalk67.default.dim("\n Both versions: --json. The fix is always wallet-side (code fix, and an `adjust` if money was wrong) \u2014 never an edit of Neon (\xA76.6).\n"));
17667
17801
  }
17668
17802
  async function opLedgerCut(ref, opts) {
17669
17803
  if (!ref) return fail6(opts, 'Which wallet? apiblaze op ledger cut <wallet id | team id> [--reason "\u2026"]');
@@ -17674,9 +17808,9 @@ async function opLedgerCut(ref, opts) {
17674
17808
  const res = await call(opts, "Ledger cut", { method: "POST", path: "/operator/ledger/cut", body, summary: `cut ledger for ${ba}` });
17675
17809
  if (!res) return;
17676
17810
  if (opts.json) return void console.log(JSON.stringify(res, null, 2));
17677
- console.log(import_chalk66.default.green(`
17811
+ console.log(import_chalk67.default.green(`
17678
17812
  ${res.billing_account_id ?? ba}: cut and sent ${res.rows ?? 0} row${res.rows === 1 ? "" : "s"}.`));
17679
- console.log(import_chalk66.default.dim(` They land in Neon within seconds: apiblaze op ledger show ${ba}
17813
+ console.log(import_chalk67.default.dim(` They land in Neon within seconds: apiblaze op ledger show ${ba}
17680
17814
  `));
17681
17815
  }
17682
17816
  async function opLedgerBackfill(opts) {
@@ -17692,33 +17826,33 @@ async function opLedgerBackfill(opts) {
17692
17826
  const res = await call(opts, "Ledger backfill", { method: "POST", path: "/operator/ledger/backfill", body, summary: `ledger backfill since ${new Date(sinceMs).toISOString()}` });
17693
17827
  if (!res) return;
17694
17828
  if (opts.json) return void console.log(JSON.stringify(res, null, 2));
17695
- console.log(import_chalk66.default.bold(`
17696
- Ledger backfill since ${new Date(res.since_ms ?? sinceMs).toISOString()}`) + import_chalk66.default.dim(" verbatim re-send of stored cuts, never a re-cut"));
17829
+ console.log(import_chalk67.default.bold(`
17830
+ Ledger backfill since ${new Date(res.since_ms ?? sinceMs).toISOString()}`) + import_chalk67.default.dim(" verbatim re-send of stored cuts, never a re-cut"));
17697
17831
  console.log(` ${"wallets checked".padEnd(16)} ${res.wallets_checked}`);
17698
17832
  console.log(` ${"resent".padEnd(16)} ${res.resent_rows} row${res.resent_rows === 1 ? "" : "s"} across ${res.resent_wallets} wallet${res.resent_wallets === 1 ? "" : "s"}`);
17699
17833
  console.log(` ${"in sync".padEnd(16)} ${res.in_sync}`);
17700
- console.log(` ${"uncut".padEnd(16)} ${res.uncut}${import_chalk66.default.dim(" entries not cut yet \u2014 reported, not cut (op ledger cut <wallet>)")}`);
17701
- console.log(` ${"neon ahead".padEnd(16)} ${(res.neon_ahead ? import_chalk66.default.yellow : (s) => s)(String(res.neon_ahead))}`);
17702
- console.log(` ${"unrecoverable".padEnd(16)} ${(res.unrecoverable ? import_chalk66.default.red : (s) => s)(String(res.unrecoverable))}${import_chalk66.default.dim(" the wallet no longer keeps the cuts after Neon's head (> 30 d)")}`);
17703
- console.log(` ${"errors".padEnd(16)} ${(res.errors ? import_chalk66.default.red : (s) => s)(String(res.errors))}`);
17834
+ console.log(` ${"uncut".padEnd(16)} ${res.uncut}${import_chalk67.default.dim(" entries not cut yet \u2014 reported, not cut (op ledger cut <wallet>)")}`);
17835
+ console.log(` ${"neon ahead".padEnd(16)} ${(res.neon_ahead ? import_chalk67.default.yellow : (s) => s)(String(res.neon_ahead))}`);
17836
+ console.log(` ${"unrecoverable".padEnd(16)} ${(res.unrecoverable ? import_chalk67.default.red : (s) => s)(String(res.unrecoverable))}${import_chalk67.default.dim(" the wallet no longer keeps the cuts after Neon's head (> 30 d)")}`);
17837
+ console.log(` ${"errors".padEnd(16)} ${(res.errors ? import_chalk67.default.red : (s) => s)(String(res.errors))}`);
17704
17838
  const notable = (res.results ?? []).filter((r) => r.action !== "in_sync");
17705
17839
  if (notable.length) {
17706
- console.log(import_chalk66.default.dim(`
17840
+ console.log(import_chalk67.default.dim(`
17707
17841
  ${"action".padEnd(10)} ${"wallet gen/seq/cut".padEnd(20)} ${"neon gen/head/contig".padEnd(20)} wallet`));
17708
17842
  for (const r of notable.slice(0, 60)) {
17709
17843
  const w = r.wallet ? `${r.wallet.generation}/${r.wallet.seq}/${r.wallet.cut_to}` : "\u2014";
17710
17844
  const n = r.neon ? `${r.neon.generation}/${r.neon.head_seq}/${r.neon.contiguous_to}` : "\u2014";
17711
- const colour = r.action === "error" || r.unrecoverable ? import_chalk66.default.red : r.action === "resent" ? import_chalk66.default.green : import_chalk66.default.yellow;
17845
+ const colour = r.action === "error" || r.unrecoverable ? import_chalk67.default.red : r.action === "resent" ? import_chalk67.default.green : import_chalk67.default.yellow;
17712
17846
  const note = [r.resent !== void 0 ? `resent ${r.resent}` : "", r.unrecoverable ? `UNRECOVERABLE (oldest kept seq ${r.oldest_kept_seq ?? "?"})` : "", r.error ?? ""].filter(Boolean).join(" \xB7 ");
17713
- console.log(` ${colour(r.action.padEnd(10))} ${w.padEnd(20)} ${n.padEnd(20)} ${r.billing_account_id}${note ? import_chalk66.default.dim(` ${note}`) : ""}`);
17847
+ console.log(` ${colour(r.action.padEnd(10))} ${w.padEnd(20)} ${n.padEnd(20)} ${r.billing_account_id}${note ? import_chalk67.default.dim(` ${note}`) : ""}`);
17714
17848
  }
17715
- if (notable.length > 60) console.log(import_chalk66.default.dim(` \u2026 ${notable.length - 60} more (--json)`));
17849
+ if (notable.length > 60) console.log(import_chalk67.default.dim(` \u2026 ${notable.length - 60} more (--json)`));
17716
17850
  }
17717
17851
  console.log("");
17718
17852
  }
17719
17853
 
17720
17854
  // src/commands/op-test.ts
17721
- var import_chalk67 = __toESM(require("chalk"));
17855
+ var import_chalk68 = __toESM(require("chalk"));
17722
17856
  init_auth();
17723
17857
  init_types();
17724
17858
  var DASHBOARD_BASE11 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
@@ -17743,10 +17877,10 @@ async function scrubSandboxKeys(timeoutS = 150) {
17743
17877
  try {
17744
17878
  const r = await runnerCall({ action: "gut", id: TEARDOWN_GUT_ID }, timeoutS);
17745
17879
  const first = r.results?.[0]?.detail ?? r.error ?? "";
17746
- log(import_chalk67.default.dim(`teardown: ${first || (r.ok ? "sandbox fixture keys scrubbed" : "scrub reported a problem")}`));
17880
+ log(import_chalk68.default.dim(`teardown: ${first || (r.ok ? "sandbox fixture keys scrubbed" : "scrub reported a problem")}`));
17747
17881
  return r.ok;
17748
17882
  } catch (e) {
17749
- log(import_chalk67.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`));
17883
+ log(import_chalk68.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`));
17750
17884
  return false;
17751
17885
  }
17752
17886
  }
@@ -17768,23 +17902,23 @@ async function runOpTest(o = {}) {
17768
17902
  const guts = o.skipGuts ? [] : listing.guts.filter((g) => g.id !== TEARDOWN_GUT_ID && matches(g.id, void 0, { ...o, group: void 0 }));
17769
17903
  const total = specs.length + guts.length;
17770
17904
  if (total === 0) {
17771
- log(import_chalk67.default.yellow("nothing matched"));
17905
+ log(import_chalk68.default.yellow("nothing matched"));
17772
17906
  process.exitCode = 2;
17773
17907
  return;
17774
17908
  }
17775
- log(import_chalk67.default.bold(`apiblaze op test \u2014 ${specs.length} spec(s) + ${guts.length} gut check(s) on sandbox ${listing.sandbox_team}`));
17909
+ log(import_chalk68.default.bold(`apiblaze op test \u2014 ${specs.length} spec(s) + ${guts.length} gut check(s) on sandbox ${listing.sandbox_team}`));
17776
17910
  const results = [];
17777
17911
  let failed = 0;
17778
17912
  const started = Date.now();
17779
17913
  const record = (r) => {
17780
17914
  results.push(r);
17781
17915
  if (!r.ok) failed += 1;
17782
- const mark = r.ok ? import_chalk67.default.green("PASS") : import_chalk67.default.red("FAIL");
17916
+ const mark = r.ok ? import_chalk68.default.green("PASS") : import_chalk68.default.red("FAIL");
17783
17917
  log(`[${mark}] ${r.id} \u2014 ${r.label} (${ms(r.duration_ms)})`);
17784
17918
  if (!r.ok) {
17785
- if (r.kind === "spec") for (const c of r.cases.filter((c2) => !c2.ok)) log(import_chalk67.default.red(` \u2717 ${c.name}: ${c.detail}`));
17786
- else if (r.error) log(import_chalk67.default.red(` \u2717 ${r.error}`));
17787
- else for (const x of r.results.filter((x2) => x2 && x2.ok === false)) log(import_chalk67.default.red(` \u2717 ${x.name ?? ""}: ${x.detail ?? JSON.stringify(x)}`));
17919
+ if (r.kind === "spec") for (const c of r.cases.filter((c2) => !c2.ok)) log(import_chalk68.default.red(` \u2717 ${c.name}: ${c.detail}`));
17920
+ else if (r.error) log(import_chalk68.default.red(` \u2717 ${r.error}`));
17921
+ else for (const x of r.results.filter((x2) => x2 && x2.ok === false)) log(import_chalk68.default.red(` \u2717 ${x.name ?? ""}: ${x.detail ?? JSON.stringify(x)}`));
17788
17922
  }
17789
17923
  };
17790
17924
  try {
@@ -17815,13 +17949,13 @@ async function runOpTest(o = {}) {
17815
17949
  }
17816
17950
  const summary = { ran: results.length, passed: results.length - failed, failed, duration_ms: Date.now() - started, sandbox_team: listing.sandbox_team };
17817
17951
  log("");
17818
- log(failed ? import_chalk67.default.red(`${summary.passed}/${summary.ran} passed, ${failed} failed (${ms(summary.duration_ms)})`) : import_chalk67.default.green(`${summary.passed}/${summary.ran} passed (${ms(summary.duration_ms)})`));
17952
+ log(failed ? import_chalk68.default.red(`${summary.passed}/${summary.ran} passed, ${failed} failed (${ms(summary.duration_ms)})`) : import_chalk68.default.green(`${summary.passed}/${summary.ran} passed (${ms(summary.duration_ms)})`));
17819
17953
  if (o.json) process.stdout.write(JSON.stringify({ ...summary, results }, null, 2) + "\n");
17820
17954
  if (failed) process.exitCode = 1;
17821
17955
  }
17822
17956
 
17823
17957
  // src/commands/op-consumer.ts
17824
- var import_chalk68 = __toESM(require("chalk"));
17958
+ var import_chalk69 = __toESM(require("chalk"));
17825
17959
  init_consumer_auth();
17826
17960
  var SANDBOX_TENANT = "portalteststenant";
17827
17961
  var SANDBOX_PROXY = "portaltestproxypreapproved@2.7.0";
@@ -17864,9 +17998,9 @@ async function runOpConsumer(o = {}) {
17864
17998
  const creds = loadConsumer();
17865
17999
  const done = (r) => {
17866
18000
  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 };
17867
- const mark = out.skipped ? import_chalk68.default.yellow("SKIP") : out.ok ? import_chalk68.default.green("PASS") : import_chalk68.default.red("FAIL");
18001
+ const mark = out.skipped ? import_chalk69.default.yellow("SKIP") : out.ok ? import_chalk69.default.green("PASS") : import_chalk69.default.red("FAIL");
17868
18002
  log2(`[${mark}] ${out.id} \u2014 ${out.label}${out.tenant ? ` (tenant ${out.tenant})` : ""}${out.url ? ` \u2192 ${out.url}` : ""}`);
17869
- for (const c of out.cases) if (!c.ok || out.skipped) log2((c.ok ? import_chalk68.default.dim : import_chalk68.default.red)(` ${c.ok ? "ok " : "\u2717"} ${c.name}: ${c.detail}`));
18003
+ for (const c of out.cases) if (!c.ok || out.skipped) log2((c.ok ? import_chalk69.default.dim : import_chalk69.default.red)(` ${c.ok ? "ok " : "\u2717"} ${c.name}: ${c.detail}`));
17870
18004
  if (o.json) process.stdout.write(JSON.stringify(out, null, 2) + "\n");
17871
18005
  if (!out.ok && !out.skipped) process.exitCode = 1;
17872
18006
  if (out.skipped) process.exitCode = 2;
@@ -17906,7 +18040,7 @@ async function runOpConsumer(o = {}) {
17906
18040
  }
17907
18041
 
17908
18042
  // src/commands/op-perf.ts
17909
- var import_chalk69 = __toESM(require("chalk"));
18043
+ var import_chalk70 = __toESM(require("chalk"));
17910
18044
  var RPS_DEFAULT = 100;
17911
18045
  var RPS_CAP = 300;
17912
18046
  var SECONDS_DEFAULT = 60;
@@ -18090,10 +18224,10 @@ async function readGateway(proxy, sent, signal) {
18090
18224
  }
18091
18225
  const left = deadline - Date.now();
18092
18226
  if (left <= 0) {
18093
- log3(import_chalk69.default.yellow(` gateway: not available \u2014 Analytics Engine had ${lastSeen}/${sent} rows for ${proxy} after ${GATEWAY_WAIT_MS / 1e3}s${lastErr ? ` (last error: ${lastErr})` : ""}`));
18227
+ log3(import_chalk70.default.yellow(` gateway: not available \u2014 Analytics Engine had ${lastSeen}/${sent} rows for ${proxy} after ${GATEWAY_WAIT_MS / 1e3}s${lastErr ? ` (last error: ${lastErr})` : ""}`));
18094
18228
  return null;
18095
18229
  }
18096
- log3(import_chalk69.default.dim(` waiting for the latency ledger\u2026 ${lastSeen}/${sent} rows for ${proxy}${lastErr ? ` (${lastErr})` : ""}`));
18230
+ log3(import_chalk70.default.dim(` waiting for the latency ledger\u2026 ${lastSeen}/${sent} rows for ${proxy}${lastErr ? ` (${lastErr})` : ""}`));
18097
18231
  await sleep(Math.min(GATEWAY_POLL_MS, left), signal);
18098
18232
  }
18099
18233
  }
@@ -18102,7 +18236,7 @@ async function runOpPerf(o = {}) {
18102
18236
  const thresholds = thresholdsFrom(o);
18103
18237
  const ctrl = new AbortController();
18104
18238
  const onSigint = () => {
18105
- log3(import_chalk69.default.yellow("\ninterrupted \u2014 stopping the load and tearing down the fixture"));
18239
+ log3(import_chalk70.default.yellow("\ninterrupted \u2014 stopping the load and tearing down the fixture"));
18106
18240
  ctrl.abort();
18107
18241
  };
18108
18242
  process.once("SIGINT", onSigint);
@@ -18111,7 +18245,7 @@ async function runOpPerf(o = {}) {
18111
18245
  if (!o.key) throw new Error("--url needs --key (the fixture proxy's API key)");
18112
18246
  fixture = { url: o.url, key: o.key, proxy: proxyFromUrl(o.url), provisioned: false };
18113
18247
  } else {
18114
- log3(import_chalk69.default.dim("provisioning the perf fixture through the runner\u2026"));
18248
+ log3(import_chalk70.default.dim("provisioning the perf fixture through the runner\u2026"));
18115
18249
  const f = await runnerCall({ action: "perf-fixture" }, 120);
18116
18250
  if (!f?.url || !f?.key) throw new Error(`perf-fixture returned no url/key: ${JSON.stringify(f).slice(0, 200)}`);
18117
18251
  fixture = { url: f.url, key: f.key, proxy: f.proxy || proxyFromUrl(f.url), team: f.team, provisioned: true };
@@ -18119,7 +18253,7 @@ async function runOpPerf(o = {}) {
18119
18253
  try {
18120
18254
  const total = Math.round(rps * seconds);
18121
18255
  const intervalMs = 1e3 / rps;
18122
- log3(import_chalk69.default.bold(`apiblaze op test --perf \u2014 ${rps} rps \xD7 ${seconds}s (${total} requests) against ${fixture.url}${fixture.team ? ` (sandbox ${fixture.team})` : ""}`));
18256
+ log3(import_chalk70.default.bold(`apiblaze op test --perf \u2014 ${rps} rps \xD7 ${seconds}s (${total} requests) against ${fixture.url}${fixture.team ? ` (sandbox ${fixture.team})` : ""}`));
18123
18257
  const samples = [];
18124
18258
  const inflight = /* @__PURE__ */ new Set();
18125
18259
  let fired = 0;
@@ -18127,7 +18261,7 @@ async function runOpPerf(o = {}) {
18127
18261
  const progress = setInterval(() => {
18128
18262
  const m = summarize(samples, Date.now() - t0);
18129
18263
  const secs = (Date.now() - t0) / 1e3;
18130
- log3(import_chalk69.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)`));
18264
+ log3(import_chalk70.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)`));
18131
18265
  }, PROGRESS_EVERY_MS);
18132
18266
  const { sent, elapsedMs } = await schedule({
18133
18267
  total,
@@ -18141,21 +18275,21 @@ async function runOpPerf(o = {}) {
18141
18275
  }
18142
18276
  });
18143
18277
  clearInterval(progress);
18144
- if (inflight.size) log3(import_chalk69.default.dim(` load sent; waiting for ${inflight.size} in-flight response(s) (\u2264 ${REQUEST_TIMEOUT_MS / 1e3}s)\u2026`));
18278
+ if (inflight.size) log3(import_chalk70.default.dim(` load sent; waiting for ${inflight.size} in-flight response(s) (\u2264 ${REQUEST_TIMEOUT_MS / 1e3}s)\u2026`));
18145
18279
  await Promise.all([...inflight]);
18146
18280
  const client = summarize(samples, elapsedMs);
18147
18281
  log3("");
18148
- log3(` ${import_chalk69.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}`);
18282
+ log3(` ${import_chalk70.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}`);
18149
18283
  const gateway = ctrl.signal.aborted ? null : await readGateway(fixture.proxy, sent, ctrl.signal);
18150
18284
  if (gateway) {
18151
18285
  const extras = [
18152
18286
  gateway.kv_per_req !== void 0 ? `kv/req ${gateway.kv_per_req}` : "",
18153
18287
  gateway.redis_per_req !== void 0 ? `redis/req ${gateway.redis_per_req}` : ""
18154
18288
  ].filter(Boolean).join(" ");
18155
- log3(` ${import_chalk69.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_chalk69.default.dim(`(${gateway.requests} ledger rows${extras ? `, ${extras}` : ""})`)}`);
18156
- for (const b of gateway.blame ?? []) log3(import_chalk69.default.dim(` ${(b.share * 100).toFixed(1).padStart(5)}% p95 ${fmtMs(b.p95_ms).padStart(6)} ${b.feature} \u2192 ${b.dep}`));
18289
+ log3(` ${import_chalk70.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_chalk70.default.dim(`(${gateway.requests} ledger rows${extras ? `, ${extras}` : ""})`)}`);
18290
+ for (const b of gateway.blame ?? []) log3(import_chalk70.default.dim(` ${(b.share * 100).toFixed(1).padStart(5)}% p95 ${fmtMs(b.p95_ms).padStart(6)} ${b.feature} \u2192 ${b.dep}`));
18157
18291
  } else {
18158
- log3(` ${import_chalk69.default.bold("gateway")} ${import_chalk69.default.yellow("not available")} ${import_chalk69.default.dim("(gateway thresholds not evaluated)")}`);
18292
+ log3(` ${import_chalk70.default.bold("gateway")} ${import_chalk70.default.yellow("not available")} ${import_chalk70.default.dim("(gateway thresholds not evaluated)")}`);
18159
18293
  }
18160
18294
  const breaches = evaluate(client, gateway, thresholds);
18161
18295
  const aborted = ctrl.signal.aborted;
@@ -18163,7 +18297,7 @@ async function runOpPerf(o = {}) {
18163
18297
  log3("");
18164
18298
  const row = (metric, value, limit, unit) => {
18165
18299
  const hit = breaches.find((b) => b.metric === metric);
18166
- log3(`[${hit ? import_chalk69.default.red("FAIL") : import_chalk69.default.green("PASS")}] ${metric.padEnd(11)} ${String(value)}${unit} ${hit ? ">" : "\u2264"} ${limit}${unit}`);
18300
+ log3(`[${hit ? import_chalk70.default.red("FAIL") : import_chalk70.default.green("PASS")}] ${metric.padEnd(11)} ${String(value)}${unit} ${hit ? ">" : "\u2264"} ${limit}${unit}`);
18167
18301
  };
18168
18302
  row("client_p50", client.p50, thresholds.p50_ms, "ms");
18169
18303
  row("client_p95", client.p95, thresholds.p95_ms, "ms");
@@ -18172,12 +18306,12 @@ async function runOpPerf(o = {}) {
18172
18306
  row("gateway_p50", gateway.p50, thresholds.gateway_p50_ms, "ms");
18173
18307
  row("gateway_p95", gateway.p95, thresholds.gateway_p95_ms, "ms");
18174
18308
  } else {
18175
- log3(`[${import_chalk69.default.yellow("SKIP")}] gateway_p50 / gateway_p95 \u2014 ledger rows not available`);
18309
+ log3(`[${import_chalk70.default.yellow("SKIP")}] gateway_p50 / gateway_p95 \u2014 ledger rows not available`);
18176
18310
  }
18177
18311
  log3("");
18178
- if (aborted) log3(import_chalk69.default.red(`FAIL \u2014 interrupted after ${sent}/${total} requests`));
18179
- else if (ok) log3(import_chalk69.default.green(`PASS \u2014 ${client.sent} requests at ${client.rps_achieved} rps, no threshold breached`));
18180
- else log3(import_chalk69.default.red(`FAIL \u2014 ${breaches.length} threshold(s) breached: ${breaches.map((b) => `${b.metric} ${b.value} > ${b.limit}`).join(", ")}`));
18312
+ if (aborted) log3(import_chalk70.default.red(`FAIL \u2014 interrupted after ${sent}/${total} requests`));
18313
+ else if (ok) log3(import_chalk70.default.green(`PASS \u2014 ${client.sent} requests at ${client.rps_achieved} rps, no threshold breached`));
18314
+ else log3(import_chalk70.default.red(`FAIL \u2014 ${breaches.length} threshold(s) breached: ${breaches.map((b) => `${b.metric} ${b.value} > ${b.limit}`).join(", ")}`));
18181
18315
  if (o.json) {
18182
18316
  const doc = {
18183
18317
  kind: "perf",
@@ -18215,9 +18349,9 @@ async function runOpPerf(o = {}) {
18215
18349
  if (fixture.provisioned) {
18216
18350
  try {
18217
18351
  await runnerCall({ action: "perf-teardown", proxy: fixture.proxy }, 120);
18218
- log3(import_chalk69.default.dim(`fixture ${fixture.proxy} torn down`));
18352
+ log3(import_chalk70.default.dim(`fixture ${fixture.proxy} torn down`));
18219
18353
  } catch (e) {
18220
- log3(import_chalk69.default.red(`teardown of ${fixture.proxy} failed: ${e instanceof Error ? e.message : String(e)} \u2014 run \`apiblaze op residue\` / clean the sandbox`));
18354
+ log3(import_chalk70.default.red(`teardown of ${fixture.proxy} failed: ${e instanceof Error ? e.message : String(e)} \u2014 run \`apiblaze op residue\` / clean the sandbox`));
18221
18355
  process.exitCode = 1;
18222
18356
  }
18223
18357
  await scrubSandboxKeys(120);
@@ -18267,79 +18401,79 @@ var OP_COMMANDS = [
18267
18401
  function renderOpCommands() {
18268
18402
  const width = Math.max(...OP_COMMANDS.map((c) => c.cmd.length)) + 10;
18269
18403
  const lines = OP_COMMANDS.map((c) => {
18270
- const left = ` ${import_chalk71.default.cyan(`apiblaze ${c.cmd}`)}`;
18404
+ const left = ` ${import_chalk72.default.cyan(`apiblaze ${c.cmd}`)}`;
18271
18405
  const pad = " ".repeat(Math.max(1, width - c.cmd.length));
18272
- return `${left}${pad}${c.blurb}${c.extra ? " " + import_chalk71.default.dim(`(${c.extra})`) : ""}`;
18406
+ return `${left}${pad}${c.blurb}${c.extra ? " " + import_chalk72.default.dim(`(${c.extra})`) : ""}`;
18273
18407
  });
18274
18408
  return [
18275
- import_chalk71.default.bold("Operator commands"),
18409
+ import_chalk72.default.bold("Operator commands"),
18276
18410
  ...lines,
18277
18411
  "",
18278
- import_chalk71.default.dim(" Operators only. The gate is server-side (dashboard /api/cli/op checks the"),
18279
- import_chalk71.default.dim(" signed-in email, admin-api re-checks with operatorGate) \u2014 a patched CLI just"),
18280
- import_chalk71.default.dim(" gets 403s. Every op call is read-only except `op sweep`."),
18412
+ import_chalk72.default.dim(" Operators only. The gate is server-side (dashboard /api/cli/op checks the"),
18413
+ import_chalk72.default.dim(" signed-in email, admin-api re-checks with operatorGate) \u2014 a patched CLI just"),
18414
+ import_chalk72.default.dim(" gets 403s. Every op call is read-only except `op sweep`."),
18281
18415
  "",
18282
- import_chalk71.default.dim(" Not a CLI command: to prune all non-CP data run scripts/nuke-but-cp.sh --apply --sweep"),
18283
- import_chalk71.default.dim(" in the repo. Wallet and incident MUTATIONS are dashboard-only (/operator/wallets/<id>,"),
18284
- import_chalk71.default.dim(" /operator/incident) \u2014 they need a typed reason and an audit row. Billing conformance"),
18285
- import_chalk71.default.dim(" (every door metered, 1 request = 1 charge) runs inside `op test`. Operator dashboards"),
18286
- import_chalk71.default.dim(" (dlq, thresholds, limits, pricing, agent-spend, teams, tests, leak-detection,"),
18287
- import_chalk71.default.dim(" lifecycle) live at /operator/* in the app.")
18416
+ import_chalk72.default.dim(" Not a CLI command: to prune all non-CP data run scripts/nuke-but-cp.sh --apply --sweep"),
18417
+ import_chalk72.default.dim(" in the repo. Wallet and incident MUTATIONS are dashboard-only (/operator/wallets/<id>,"),
18418
+ import_chalk72.default.dim(" /operator/incident) \u2014 they need a typed reason and an audit row. Billing conformance"),
18419
+ import_chalk72.default.dim(" (every door metered, 1 request = 1 charge) runs inside `op test`. Operator dashboards"),
18420
+ import_chalk72.default.dim(" (dlq, thresholds, limits, pricing, agent-spend, teams, tests, leak-detection,"),
18421
+ import_chalk72.default.dim(" lifecycle) live at /operator/* in the app.")
18288
18422
  ].join("\n");
18289
18423
  }
18290
18424
  function printResidue(report, applied) {
18291
18425
  const fga = report?.fga ?? {};
18292
18426
  const ghosts = report?.ghosts ?? {};
18293
- console.log(import_chalk71.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
18294
- console.log(import_chalk71.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
18427
+ console.log(import_chalk72.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
18428
+ console.log(import_chalk72.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
18295
18429
  if (applied) {
18296
18430
  const swept = fga?.swept ?? [];
18297
- if (swept.length === 0) console.log(import_chalk71.default.green(" no orphaned stores"));
18431
+ if (swept.length === 0) console.log(import_chalk72.default.green(" no orphaned stores"));
18298
18432
  for (const s of swept) {
18299
18433
  console.log(
18300
- ` ${import_chalk71.default.yellow(s.store_id)} ${import_chalk71.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
18434
+ ` ${import_chalk72.default.yellow(s.store_id)} ${import_chalk72.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
18301
18435
  );
18302
18436
  }
18303
- if (fga?.remaining) console.log(import_chalk71.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
18437
+ if (fga?.remaining) console.log(import_chalk72.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
18304
18438
  const st = fga?.side_tables;
18305
- if (st) console.log(import_chalk71.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})` : ""}`));
18439
+ if (st) console.log(import_chalk72.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})` : ""}`));
18306
18440
  } else {
18307
18441
  const fgaOrphans = fga?.orphans ?? [];
18308
- if (fgaOrphans.length === 0) console.log(import_chalk71.default.green(" no orphaned stores"));
18442
+ if (fgaOrphans.length === 0) console.log(import_chalk72.default.green(" no orphaned stores"));
18309
18443
  for (const s of fgaOrphans) {
18310
18444
  const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
18311
- console.log(` ${import_chalk71.default.yellow(s.store_id)} ${import_chalk71.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
18445
+ console.log(` ${import_chalk72.default.yellow(s.store_id)} ${import_chalk72.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
18312
18446
  }
18313
- console.log(import_chalk71.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
18447
+ console.log(import_chalk72.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
18314
18448
  const st = fga?.side_tables;
18315
- if (st) console.log(import_chalk71.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`));
18449
+ if (st) console.log(import_chalk72.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`));
18316
18450
  }
18317
- for (const e of fga?.errors ?? []) console.log(import_chalk71.default.red(` error: ${e}`));
18318
- console.log(import_chalk71.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
18451
+ for (const e of fga?.errors ?? []) console.log(import_chalk72.default.red(` error: ${e}`));
18452
+ console.log(import_chalk72.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
18319
18453
  if (applied) {
18320
- if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk71.default.green(" no ghost tuples"));
18321
- else console.log(` ${import_chalk71.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk71.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
18454
+ if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk72.default.green(" no ghost tuples"));
18455
+ else console.log(` ${import_chalk72.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk72.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
18322
18456
  } else {
18323
18457
  const n = ghosts?.ghost_count ?? 0;
18324
- if (n === 0) console.log(import_chalk71.default.green(` no ghost tuples ${import_chalk71.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
18458
+ if (n === 0) console.log(import_chalk72.default.green(` no ghost tuples ${import_chalk72.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
18325
18459
  else {
18326
- console.log(import_chalk71.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
18460
+ console.log(import_chalk72.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
18327
18461
  for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
18328
- console.log(import_chalk71.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
18462
+ console.log(import_chalk72.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
18329
18463
  }
18330
- if (n > 20) console.log(import_chalk71.default.dim(` \u2026 and ${n - 20} more`));
18464
+ if (n > 20) console.log(import_chalk72.default.dim(` \u2026 and ${n - 20} more`));
18331
18465
  }
18332
18466
  }
18333
- for (const e of ghosts?.errors ?? []) console.log(import_chalk71.default.red(` error: ${e}`));
18467
+ for (const e of ghosts?.errors ?? []) console.log(import_chalk72.default.red(` error: ${e}`));
18334
18468
  console.log();
18335
18469
  }
18336
18470
  async function runOp(sub, opts = {}, view, arg) {
18337
18471
  if (!loadCredentials()) {
18338
- console.log(import_chalk71.default.dim("Not logged in. Run `apiblaze login`."));
18472
+ console.log(import_chalk72.default.dim("Not logged in. Run `apiblaze login`."));
18339
18473
  return;
18340
18474
  }
18341
18475
  if (!isOperatorLogin()) {
18342
- console.log(import_chalk71.default.dim("`apiblaze op` is only available to platform operators."));
18476
+ console.log(import_chalk72.default.dim("`apiblaze op` is only available to platform operators."));
18343
18477
  return;
18344
18478
  }
18345
18479
  switch (sub) {
@@ -18408,11 +18542,11 @@ async function runOp(sub, opts = {}, view, arg) {
18408
18542
  }
18409
18543
  if (opts.json) return void console.log(JSON.stringify(out, null, 2));
18410
18544
  console.log("");
18411
- console.log(` Minimum CLI version: ${import_chalk71.default.bold(out.min_version)}${out.min_version === "0.0.0" ? import_chalk71.default.dim(" (no minimum)") : ""}`);
18412
- console.log(` Refuse unversioned: ${out.reject_unversioned ? import_chalk71.default.yellow("yes \u2014 CLIs released before 2026-09-27 get a 426 (cheap refusals; they keep retrying)") : "no"}`);
18413
- console.log(` Message: ${out.message ?? import_chalk71.default.dim("(default)")}`);
18414
- if (out.updated_at) console.log(import_chalk71.default.dim(` Updated ${out.updated_at}${out.updated_by ? ` by ${out.updated_by}` : ""}`));
18415
- console.log(import_chalk71.default.dim(" Takes effect within ~1 minute on the CLI lane; running `dev` sessions re-check every 10 minutes."));
18545
+ console.log(` Minimum CLI version: ${import_chalk72.default.bold(out.min_version)}${out.min_version === "0.0.0" ? import_chalk72.default.dim(" (no minimum)") : ""}`);
18546
+ console.log(` Refuse unversioned: ${out.reject_unversioned ? import_chalk72.default.yellow("yes \u2014 CLIs released before 2026-09-27 get a 426 (cheap refusals; they keep retrying)") : "no"}`);
18547
+ console.log(` Message: ${out.message ?? import_chalk72.default.dim("(default)")}`);
18548
+ if (out.updated_at) console.log(import_chalk72.default.dim(` Updated ${out.updated_at}${out.updated_by ? ` by ${out.updated_by}` : ""}`));
18549
+ console.log(import_chalk72.default.dim(" Takes effect within ~1 minute on the CLI lane; running `dev` sessions re-check every 10 minutes."));
18416
18550
  console.log("");
18417
18551
  return;
18418
18552
  }
@@ -18430,17 +18564,17 @@ async function runOp(sub, opts = {}, view, arg) {
18430
18564
  const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
18431
18565
  printResidue(report, false);
18432
18566
  if (nFga + nGhost + nSide === 0) {
18433
- console.log(import_chalk71.default.green("Nothing to sweep."));
18567
+ console.log(import_chalk72.default.green("Nothing to sweep."));
18434
18568
  return;
18435
18569
  }
18436
18570
  if (!opts.yes) {
18437
18571
  const readline3 = await import("readline/promises");
18438
18572
  const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
18439
18573
  const answer = await rl.question(
18440
- import_chalk71.default.red(`Delete ${nFga} OpenFGA store(s) + ${nGhost} ghost tuple(s) + ${nSide} Neon side-table row(s)? Type 'sweep' to confirm: `)
18574
+ import_chalk72.default.red(`Delete ${nFga} OpenFGA store(s) + ${nGhost} ghost tuple(s) + ${nSide} Neon side-table row(s)? Type 'sweep' to confirm: `)
18441
18575
  );
18442
18576
  rl.close();
18443
- if (answer.trim() !== "sweep") return void console.log(import_chalk71.default.dim("Aborted."));
18577
+ if (answer.trim() !== "sweep") return void console.log(import_chalk72.default.dim("Aborted."));
18444
18578
  }
18445
18579
  const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
18446
18580
  if (opts.json) return void console.log(JSON.stringify(result, null, 2));
@@ -18450,15 +18584,15 @@ async function runOp(sub, opts = {}, view, arg) {
18450
18584
  case "mark": {
18451
18585
  const label3 = (view ?? "").trim();
18452
18586
  if (!label3) {
18453
- console.log(import_chalk71.default.red("Give the change a name:") + import_chalk71.default.cyan(' apiblaze op mark "cached tenant count"'));
18587
+ console.log(import_chalk72.default.red("Give the change a name:") + import_chalk72.default.cyan(' apiblaze op mark "cached tenant count"'));
18454
18588
  return;
18455
18589
  }
18456
18590
  const res = await opCall({ method: "POST", path: "/operator/latency/mark", body: { label: label3 }, summary: "record change marker" });
18457
18591
  const ts2 = new Date(res?.marker?.ts ?? Date.now()).toISOString();
18458
- console.log(import_chalk71.default.green(`
18459
- Marked: `) + import_chalk71.default.bold(label3));
18460
- console.log(import_chalk71.default.dim(` ${ts2}`));
18461
- console.log(import_chalk71.default.dim(` Once traffic has run on both sides, compare with: `) + import_chalk71.default.cyan("apiblaze op latency compare") + "\n");
18592
+ console.log(import_chalk72.default.green(`
18593
+ Marked: `) + import_chalk72.default.bold(label3));
18594
+ console.log(import_chalk72.default.dim(` ${ts2}`));
18595
+ console.log(import_chalk72.default.dim(` Once traffic has run on both sides, compare with: `) + import_chalk72.default.cyan("apiblaze op latency compare") + "\n");
18462
18596
  return;
18463
18597
  }
18464
18598
  // ── MONEY (specs/TQC §11 "Operator console") — commands/op-money.ts ────
@@ -18469,14 +18603,14 @@ async function runOp(sub, opts = {}, view, arg) {
18469
18603
  if (which === "list") return void await opWalletList(opts);
18470
18604
  if (which === "inspect") return void await opWalletInspect(arg, opts);
18471
18605
  if (which in WALLET_MUTATIONS) return void opWalletMutation(which, arg, opts);
18472
- return void console.log(import_chalk71.default.red(`Unknown: apiblaze op wallet ${which}. Use list | inspect <wallet> | ${Object.keys(WALLET_MUTATIONS).join(" | ")}.`));
18606
+ return void console.log(import_chalk72.default.red(`Unknown: apiblaze op wallet ${which}. Use list | inspect <wallet> | ${Object.keys(WALLET_MUTATIONS).join(" | ")}.`));
18473
18607
  }
18474
18608
  case "money": {
18475
18609
  const which = (view ?? "").trim().toLowerCase();
18476
18610
  if (which === "exposure") return void await opMoneyExposure(opts);
18477
18611
  if (which === "journal") return void await opLedgerShow(arg, opts);
18478
18612
  process.exitCode = 1;
18479
- return void console.log(import_chalk71.default.red(`Unknown: apiblaze op money ${which}. Use exposure; the money ledger is`) + import_chalk71.default.cyan(" apiblaze op ledger show <wallet> | lag") + import_chalk71.default.red("."));
18613
+ return void console.log(import_chalk72.default.red(`Unknown: apiblaze op money ${which}. Use exposure; the money ledger is`) + import_chalk72.default.cyan(" apiblaze op ledger show <wallet> | lag") + import_chalk72.default.red("."));
18480
18614
  }
18481
18615
  // ── MONEY LEDGER (specs/noreconciliations.MD §6/§7) — commands/op-ledger.ts ──
18482
18616
  case "ledger": {
@@ -18487,20 +18621,20 @@ async function runOp(sub, opts = {}, view, arg) {
18487
18621
  if (which === "cut") return void await opLedgerCut(arg, opts);
18488
18622
  if (which === "backfill") return void await opLedgerBackfill(opts);
18489
18623
  process.exitCode = 1;
18490
- return void console.log(import_chalk71.default.red(`Unknown: apiblaze op ledger ${which}. Use show <wallet> | lag | quarantine | cut <wallet> | backfill --since <ISO|ms>.`));
18624
+ return void console.log(import_chalk72.default.red(`Unknown: apiblaze op ledger ${which}. Use show <wallet> | lag | quarantine | cut <wallet> | backfill --since <ISO|ms>.`));
18491
18625
  }
18492
18626
  // ── MAXIMUM RATE (project ceiling) requests — commands/op-ceiling.ts ──
18493
18627
  case "ceiling": {
18494
18628
  const which = (view ?? "list").trim().toLowerCase();
18495
18629
  if (which === "list") return void await opCeilingList(opts);
18496
18630
  if (which === "approve" || which === "deny") return void await opCeilingDecide(which, arg, opts);
18497
- return void console.log(import_chalk71.default.red(`Unknown: apiblaze op ceiling ${which}. Use list | approve <id> | deny <id> --note "why".`));
18631
+ return void console.log(import_chalk72.default.red(`Unknown: apiblaze op ceiling ${which}. Use list | approve <id> | deny <id> --note "why".`));
18498
18632
  }
18499
18633
  case "incident": {
18500
18634
  const which = (view ?? "status").trim().toLowerCase();
18501
18635
  if (which === "status" || which === "degraded-mode") return void await opIncidentStatus(opts);
18502
18636
  if (which === "allow" || which === "deny" || which === "extend") return void opIncidentMutation(which, opts);
18503
- return void console.log(import_chalk71.default.red(`Unknown: apiblaze op incident ${which}. Use status | allow | deny | extend.`));
18637
+ return void console.log(import_chalk72.default.red(`Unknown: apiblaze op incident ${which}. Use status | allow | deny | extend.`));
18504
18638
  }
18505
18639
  // ── LATENCY (specs/latency/ §7.4) ─────────────────────────────────────
18506
18640
  // The blame table first: level 1 (which feature) and level 2 (what inside
@@ -18550,29 +18684,29 @@ async function runOp(sub, opts = {}, view, arg) {
18550
18684
  const data = await opCall({ method: "GET", path: `/operator/latency/grades${q}`, summary: "latency grades" });
18551
18685
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
18552
18686
  const t = data.thresholds_ms;
18553
- console.log(import_chalk71.default.bold("\nHow good was apiblaze itself?") + import_chalk71.default.dim(" (our overhead only \u2014 a slow customer API never counts against us)"));
18554
- console.log(import_chalk71.default.dim(` excellent <${t.excellent.replace("<", "")}ms \xB7 okay ${t.okay}ms \xB7 bad ${t.bad}ms \xB7 terrible ${t.terrible.replace(">=", "")}ms+
18687
+ console.log(import_chalk72.default.bold("\nHow good was apiblaze itself?") + import_chalk72.default.dim(" (our overhead only \u2014 a slow customer API never counts against us)"));
18688
+ console.log(import_chalk72.default.dim(` excellent <${t.excellent.replace("<", "")}ms \xB7 okay ${t.okay}ms \xB7 bad ${t.bad}ms \xB7 terrible ${t.terrible.replace(">=", "")}ms+
18555
18689
  `));
18556
- console.log(import_chalk71.default.dim(" date reqs excellent okay bad terrible"));
18690
+ console.log(import_chalk72.default.dim(" date reqs excellent okay bad terrible"));
18557
18691
  for (const d of data.days ?? []) {
18558
18692
  const p = d.pct;
18559
- const cell = (v, colour) => v > 0 ? colour(`${String(v).padStart(5)}%`) : import_chalk71.default.dim(`${String(v).padStart(5)}%`);
18693
+ const cell = (v, colour) => v > 0 ? colour(`${String(v).padStart(5)}%`) : import_chalk72.default.dim(`${String(v).padStart(5)}%`);
18560
18694
  console.log(
18561
- ` ${d.date} ${String(d.total).padStart(5)} ${cell(p.excellent, import_chalk71.default.green)} ${cell(p.okay, import_chalk71.default.cyan)} ${cell(p.bad, import_chalk71.default.yellow)} ${cell(p.terrible, import_chalk71.default.red)}`
18695
+ ` ${d.date} ${String(d.total).padStart(5)} ${cell(p.excellent, import_chalk72.default.green)} ${cell(p.okay, import_chalk72.default.cyan)} ${cell(p.bad, import_chalk72.default.yellow)} ${cell(p.terrible, import_chalk72.default.red)}`
18562
18696
  );
18563
18697
  }
18564
18698
  const cul = data.culprits ?? [];
18565
18699
  if (cul.length) {
18566
- console.log(import_chalk71.default.bold("\n Who caused the bad and terrible ones\n"));
18567
- console.log(import_chalk71.default.dim(" bad terrible feature \u2192 dependency"));
18700
+ console.log(import_chalk72.default.bold("\n Who caused the bad and terrible ones\n"));
18701
+ console.log(import_chalk72.default.dim(" bad terrible feature \u2192 dependency"));
18568
18702
  for (const r of cul.slice(0, 12)) {
18569
18703
  if (!r.bad && !r.terrible) continue;
18570
18704
  console.log(
18571
- ` ${String(r.bad).padStart(6)} ${import_chalk71.default.red(String(r.terrible).padStart(8))} ${import_chalk71.default.yellow(r.feature)} ${import_chalk71.default.dim("\u2192")} ${import_chalk71.default.cyan(r.dep)}`
18705
+ ` ${String(r.bad).padStart(6)} ${import_chalk72.default.red(String(r.terrible).padStart(8))} ${import_chalk72.default.yellow(r.feature)} ${import_chalk72.default.dim("\u2192")} ${import_chalk72.default.cyan(r.dep)}`
18572
18706
  );
18573
18707
  }
18574
18708
  }
18575
- if (data.caveat) console.log(import_chalk71.default.dim(`
18709
+ if (data.caveat) console.log(import_chalk72.default.dim(`
18576
18710
  \u26A0 ${data.caveat}
18577
18711
  `));
18578
18712
  return;
@@ -18581,24 +18715,24 @@ async function runOp(sub, opts = {}, view, arg) {
18581
18715
  const data = await opCall({ method: "GET", path: `/operator/latency/compare${q}`, summary: "latency before/after" });
18582
18716
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
18583
18717
  const b = data.before, a = data.after, d = data.delta;
18584
- console.log(import_chalk71.default.bold(`
18585
- Before vs after: `) + import_chalk71.default.cyan(data.marker.label));
18586
- console.log(import_chalk71.default.dim(` marked ${new Date(data.marker.ts).toISOString()} \xB7 ${data.window_hours}h either side
18718
+ console.log(import_chalk72.default.bold(`
18719
+ Before vs after: `) + import_chalk72.default.cyan(data.marker.label));
18720
+ console.log(import_chalk72.default.dim(` marked ${new Date(data.marker.ts).toISOString()} \xB7 ${data.window_hours}h either side
18587
18721
  `));
18588
18722
  const row = (name, before, after, delta) => {
18589
18723
  const arrow = delta === 0 ? "=" : delta < 0 ? "\u2193" : "\u2191";
18590
18724
  const txt = `${String(before).padStart(6)}ms \u2192${String(after).padStart(7)}ms ${arrow}${Math.abs(delta)}ms`;
18591
- console.log(` ${name.padEnd(22)}${data.trustworthy ? delta <= 0 ? import_chalk71.default.green(txt) : import_chalk71.default.red(txt) : import_chalk71.default.dim(txt)}`);
18725
+ console.log(` ${name.padEnd(22)}${data.trustworthy ? delta <= 0 ? import_chalk72.default.green(txt) : import_chalk72.default.red(txt) : import_chalk72.default.dim(txt)}`);
18592
18726
  };
18593
- console.log(import_chalk71.default.dim(" metric before after change"));
18727
+ console.log(import_chalk72.default.dim(" metric before after change"));
18594
18728
  row("total p50", b.total_p50, a.total_p50, d.total_p50);
18595
18729
  row("total p95", b.total_p95, a.total_p95, d.total_p95);
18596
18730
  row("apiblaze overhead p50", b.gw_p50, a.gw_p50, d.gw_p50);
18597
18731
  row("apiblaze overhead p95", b.gw_p95, a.gw_p95, d.gw_p95);
18598
- console.log(import_chalk71.default.dim(`
18732
+ console.log(import_chalk72.default.dim(`
18599
18733
  requests: ${b.requests} before \xB7 ${a.requests} after`));
18600
18734
  for (const w of data.warnings ?? []) {
18601
- console.log((data.trustworthy ? import_chalk71.default.dim : import_chalk71.default.yellow)(` ${data.trustworthy ? "\xB7" : "\u26A0"} ${w}`));
18735
+ console.log((data.trustworthy ? import_chalk72.default.dim : import_chalk72.default.yellow)(` ${data.trustworthy ? "\xB7" : "\u26A0"} ${w}`));
18602
18736
  }
18603
18737
  console.log("");
18604
18738
  return;
@@ -18607,19 +18741,19 @@ Before vs after: `) + import_chalk71.default.cyan(data.marker.label));
18607
18741
  const data = await opCall({ method: "GET", path: `/operator/latency/slow${q}`, summary: "slowest requests" });
18608
18742
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
18609
18743
  const rows2 = data?.rows ?? [];
18610
- if (!rows2.length) return void console.log(import_chalk71.default.dim("No requests over the threshold in that window."));
18611
- console.log(import_chalk71.default.bold(`
18744
+ if (!rows2.length) return void console.log(import_chalk72.default.dim("No requests over the threshold in that window."));
18745
+ console.log(import_chalk72.default.bold(`
18612
18746
  Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
18613
18747
  `));
18614
- console.log(import_chalk71.default.dim(" total ours theirs blame request id"));
18748
+ console.log(import_chalk72.default.dim(" total ours theirs blame request id"));
18615
18749
  for (const r of rows2.slice(0, 30)) {
18616
18750
  console.log(
18617
- ` ${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_chalk71.default.yellow(`${r.slow_gw_feature || "-"}\u2192${r.slow_dep || "-"}`.padEnd(20))} ${import_chalk71.default.dim(r.request_id || "")}`
18751
+ ` ${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_chalk72.default.yellow(`${r.slow_gw_feature || "-"}\u2192${r.slow_dep || "-"}`.padEnd(20))} ${import_chalk72.default.dim(r.request_id || "")}`
18618
18752
  );
18619
18753
  }
18620
- console.log(import_chalk71.default.dim(`
18754
+ console.log(import_chalk72.default.dim(`
18621
18755
  The last column is the request id (Cloudflare calls it a "cf-ray"). Look one up with`));
18622
- console.log(import_chalk71.default.dim(` \`apiblaze logs\` for that request's exact per-feature breakdown \u2014 unsampled, unlike the table above.
18756
+ console.log(import_chalk72.default.dim(` \`apiblaze logs\` for that request's exact per-feature breakdown \u2014 unsampled, unlike the table above.
18623
18757
  `));
18624
18758
  return;
18625
18759
  }
@@ -18627,18 +18761,18 @@ Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
18627
18761
  const data = await opCall({ method: "GET", path: `/operator/latency/llm${q}`, summary: "llm latency" });
18628
18762
  if (opts.json) return void console.log(JSON.stringify(data, null, 2));
18629
18763
  const rows2 = data?.rows ?? [];
18630
- if (!rows2.length) return void console.log(import_chalk71.default.dim("No LLM traffic in that window."));
18631
- console.log(import_chalk71.default.bold(`
18764
+ if (!rows2.length) return void console.log(import_chalk72.default.dim("No LLM traffic in that window."));
18765
+ console.log(import_chalk72.default.bold(`
18632
18766
  LLM timing \u2014 last ${data.window_hours}h
18633
18767
  `));
18634
- console.log(import_chalk71.default.dim(" turns turn p95 ttfc p95 gen p95 reserve p95 in/out tokens p95 model"));
18768
+ console.log(import_chalk72.default.dim(" turns turn p95 ttfc p95 gen p95 reserve p95 in/out tokens p95 model"));
18635
18769
  for (const r of rows2) {
18636
18770
  const n = r.turns ?? r.requests ?? 0;
18637
18771
  console.log(
18638
18772
  ` ${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 || "-"}`
18639
18773
  );
18640
18774
  }
18641
- console.log(import_chalk71.default.dim(`
18775
+ console.log(import_chalk72.default.dim(`
18642
18776
  ${data.note}
18643
18777
  `));
18644
18778
  return;
@@ -18649,26 +18783,26 @@ LLM timing \u2014 last ${data.window_hours}h
18649
18783
  ]);
18650
18784
  if (opts.json) return void console.log(JSON.stringify({ blame, summary }, null, 2));
18651
18785
  const ov = summary?.apiblaze_overhead_ms ?? {};
18652
- console.log(import_chalk71.default.bold(`
18786
+ console.log(import_chalk72.default.bold(`
18653
18787
  Latency \u2014 last ${summary?.window_hours ?? "?"}h, ${Number(summary?.requests ?? 0).toLocaleString()} requests
18654
18788
  `));
18655
- console.log(` ${import_chalk71.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_chalk71.default.dim("\u2190 ours")}`);
18656
- console.log(` ${import_chalk71.default.bold("upstream ttfb ")} ${" ".repeat(24)}p95 ${String(summary?.upstream_ttfb_p95_ms ?? 0).padStart(6)}ms ${import_chalk71.default.dim("\u2190 theirs")}`);
18657
- console.log(import_chalk71.default.dim(` (per-request percentiles \u2014 never subtract one from the other)
18789
+ console.log(` ${import_chalk72.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_chalk72.default.dim("\u2190 ours")}`);
18790
+ console.log(` ${import_chalk72.default.bold("upstream ttfb ")} ${" ".repeat(24)}p95 ${String(summary?.upstream_ttfb_p95_ms ?? 0).padStart(6)}ms ${import_chalk72.default.dim("\u2190 theirs")}`);
18791
+ console.log(import_chalk72.default.dim(` (per-request percentiles \u2014 never subtract one from the other)
18658
18792
  `));
18659
18793
  const rows = blame?.blame ?? [];
18660
- if (!rows.length) return void console.log(import_chalk71.default.dim("No latency rows in that window."));
18661
- console.log(import_chalk71.default.bold(" Which feature ate the time, and what inside it\n"));
18662
- console.log(import_chalk71.default.dim(" share p95 feature \u2192 dependency"));
18794
+ if (!rows.length) return void console.log(import_chalk72.default.dim("No latency rows in that window."));
18795
+ console.log(import_chalk72.default.bold(" Which feature ate the time, and what inside it\n"));
18796
+ console.log(import_chalk72.default.dim(" share p95 feature \u2192 dependency"));
18663
18797
  for (const r of rows.slice(0, 15)) {
18664
18798
  const share = `${(r.share * 100).toFixed(1)}%`;
18665
- console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${import_chalk71.default.yellow(r.feature)} ${import_chalk71.default.dim("\u2192")} ${import_chalk71.default.cyan(r.dep)}`);
18799
+ console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${import_chalk72.default.yellow(r.feature)} ${import_chalk72.default.dim("\u2192")} ${import_chalk72.default.cyan(r.dep)}`);
18666
18800
  }
18667
18801
  console.log("");
18668
18802
  return;
18669
18803
  }
18670
18804
  default:
18671
- console.log(import_chalk71.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
18805
+ console.log(import_chalk72.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
18672
18806
  }
18673
18807
  }
18674
18808
  function printDistance(data) {
@@ -18676,17 +18810,17 @@ function printDistance(data) {
18676
18810
  const p = (v) => `${n(v).toFixed(1)}%`;
18677
18811
  const msf = (v) => `${Math.round(n(v))}ms`;
18678
18812
  const thr = n(data?.threshold_ms) || 50;
18679
- const scope = data?.team ? ` \xB7 team ${data.team}` : "";
18680
- console.log(import_chalk71.default.bold(`
18681
- Durable Object distance \u2014 last ${data?.window_hours ?? "?"}h${scope}, ${n(data?.total_requests).toLocaleString()} requests, ${n(data?.do_calls).toLocaleString()} DO calls
18813
+ const scope2 = data?.team ? ` \xB7 team ${data.team}` : "";
18814
+ console.log(import_chalk72.default.bold(`
18815
+ Durable Object distance \u2014 last ${data?.window_hours ?? "?"}h${scope2}, ${n(data?.total_requests).toLocaleString()} requests, ${n(data?.do_calls).toLocaleString()} DO calls
18682
18816
  `));
18683
18817
  console.log(
18684
- ` ${import_chalk71.default.bold(p(data?.requests_with_do_call_pct))} of requests made a Durable Object call; ${import_chalk71.default.bold(p(data?.far_pct))} of those crossed colos; ${import_chalk71.default.bold((n(data?.problem_pct?.of_all_requests) >= 1 ? import_chalk71.default.red : import_chalk71.default.green)(p(data?.problem_pct?.of_all_requests)))} of all requests paid > ${thr} ms of distance.`
18818
+ ` ${import_chalk72.default.bold(p(data?.requests_with_do_call_pct))} of requests made a Durable Object call; ${import_chalk72.default.bold(p(data?.far_pct))} of those crossed colos; ${import_chalk72.default.bold((n(data?.problem_pct?.of_all_requests) >= 1 ? import_chalk72.default.red : import_chalk72.default.green)(p(data?.problem_pct?.of_all_requests)))} of all requests paid > ${thr} ms of distance.`
18685
18819
  );
18686
- console.log(import_chalk71.default.dim(` (${p(data?.problem_pct?.of_do_calls)} of DO calls over ${thr} ms network \xB7 entry or object colo unknown on ${p(data?.unknown_pct)} of calls)
18820
+ console.log(import_chalk72.default.dim(` (${p(data?.problem_pct?.of_do_calls)} of DO calls over ${thr} ms network \xB7 entry or object colo unknown on ${p(data?.unknown_pct)} of calls)
18687
18821
  `));
18688
18822
  const nm = data?.network_ms ?? {};
18689
- console.log(import_chalk71.default.dim(" network ms calls p50 p95 p99 >thr"));
18823
+ console.log(import_chalk72.default.dim(" network ms calls p50 p95 p99 >thr"));
18690
18824
  const line = (label3, r) => {
18691
18825
  if (!r) return;
18692
18826
  console.log(
@@ -18700,32 +18834,32 @@ Durable Object distance \u2014 last ${data?.window_hours ?? "?"}h${scope}, ${n(d
18700
18834
  line("kind: throttle", nm.throttle);
18701
18835
  const modes = data?.by_mode ?? [];
18702
18836
  if (modes.length) {
18703
- console.log(import_chalk71.default.dim("\n mode calls share p95 >thr"));
18837
+ console.log(import_chalk72.default.dim("\n mode calls share p95 >thr"));
18704
18838
  for (const m of modes) {
18705
18839
  console.log(` ${String(m.mode).padEnd(14)} ${String(n(m.calls).toLocaleString()).padStart(9)} ${p(m.share_pct).padStart(6)} ${msf(m.p95_ms).padStart(7)} ${String(n(m.problem_calls).toLocaleString()).padStart(5)}`);
18706
18840
  }
18707
18841
  }
18708
18842
  const routes = data?.routes ?? [];
18709
18843
  if (routes.length) {
18710
- console.log(import_chalk71.default.bold("\n Routes (worker colo \u2192 object colo)") + import_chalk71.default.dim(` added ms = network above the same-colo p50 (${msf(data?.same_colo_p50_baseline_ms)}), summed`));
18711
- console.log(import_chalk71.default.dim(" route calls p50 p95 added ms"));
18844
+ console.log(import_chalk72.default.bold("\n Routes (worker colo \u2192 object colo)") + import_chalk72.default.dim(` added ms = network above the same-colo p50 (${msf(data?.same_colo_p50_baseline_ms)}), summed`));
18845
+ console.log(import_chalk72.default.dim(" route calls p50 p95 added ms"));
18712
18846
  for (const r of routes) {
18713
18847
  const cross = r.worker_colo !== r.object_colo;
18714
18848
  const route = `${r.worker_colo} \u2192 ${r.object_colo}`;
18715
- console.log(` ${(cross ? import_chalk71.default.yellow : import_chalk71.default.dim)(route.padEnd(16))} ${String(n(r.calls).toLocaleString()).padStart(8)} ${msf(r.p50_ms).padStart(7)} ${msf(r.p95_ms).padStart(7)} ${String(n(r.added_ms_total).toLocaleString()).padStart(12)}`);
18849
+ console.log(` ${(cross ? import_chalk72.default.yellow : import_chalk72.default.dim)(route.padEnd(16))} ${String(n(r.calls).toLocaleString()).padStart(8)} ${msf(r.p50_ms).padStart(7)} ${msf(r.p95_ms).padStart(7)} ${String(n(r.added_ms_total).toLocaleString()).padStart(12)}`);
18716
18850
  }
18717
18851
  }
18718
18852
  const teams = data?.teams ?? [];
18719
18853
  if (teams.length) {
18720
- console.log(import_chalk71.default.bold(`
18854
+ console.log(import_chalk72.default.bold(`
18721
18855
  Teams paying the most distance (> ${thr} ms)`));
18722
- console.log(import_chalk71.default.dim(" >thr calls of calls object colo worker colos team"));
18856
+ console.log(import_chalk72.default.dim(" >thr calls of calls object colo worker colos team"));
18723
18857
  for (const t of teams) {
18724
18858
  const w = (t.top_worker_colos ?? []).map((x) => `${x.colo} ${n(x.problem_calls).toLocaleString()}`).join(", ");
18725
- console.log(` ${String(n(t.problem_calls).toLocaleString()).padStart(10)} ${p(t.problem_pct).padStart(8)} ${String(t.dominant_object_colo).padEnd(11)} ${w.padEnd(24)} ${import_chalk71.default.dim(t.team)}`);
18859
+ console.log(` ${String(n(t.problem_calls).toLocaleString()).padStart(10)} ${p(t.problem_pct).padStart(8)} ${String(t.dominant_object_colo).padEnd(11)} ${w.padEnd(24)} ${import_chalk72.default.dim(t.team)}`);
18726
18860
  }
18727
18861
  } else if (!n(data?.do_calls)) {
18728
- console.log(import_chalk71.default.dim("\n No Durable Object calls in that window (warm leases write no gate rows)."));
18862
+ console.log(import_chalk72.default.dim("\n No Durable Object calls in that window (warm leases write no gate rows)."));
18729
18863
  }
18730
18864
  console.log("");
18731
18865
  }
@@ -18791,7 +18925,7 @@ agent.command("authz").description("Chat to design and turn on access rules for
18791
18925
  program.command("rule").description("Lock resources to the person who created them \u2014 or describe any access rule in plain English (that one is billed per turn)").argument("<project>", "Project name or id").argument("[sentence]", 'Optional rule in plain English, e.g. "users see only their own rows". Omit to pick from what the spec can protect.').option("--enforce", "Turn enforcement on right after saving (default: save in shadow mode, then ask)").option("--apiversion <version>", "API version (defaults to the project's)").action(action((project, sentence, opts) => runRule(project, sentence, opts)));
18792
18926
  agent.command("openapi").description("Chat to build your API spec from real traffic").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").action(action((project, apiVersion) => runOpenapi(project, apiVersion)));
18793
18927
  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)));
18794
- var mcpCmd = program.command("mcp").description("Optional tweaks to a proxy's MCP server: prompts, per-tool behavior (read-only / destructive / confirm / always-async), UI resource");
18928
+ var mcpCmd = program.command("mcp").description("Optional tweaks to a proxy's MCP server: prompts, per-tool behavior (title / read-only / destructive / confirm / always-async), UI resource");
18795
18929
  var mcpCommon = (c) => c.option("--apiversion <version>", "API version (defaults to the project's)").option("--environment <env>", "Catalogue environment (default: prod)").option("--json", "Output machine-readable JSON");
18796
18930
  var mcpPrompts = mcpCmd.command("prompts").description("Prompts the MCP server offers (drafted by the MCP agent; editable here)");
18797
18931
  mcpCommon(mcpPrompts.command("list").description("List the prompts").argument("<project>", "Project name or id")).action(action((project, opts) => runMcpPrompts("list", project, void 0, opts)));
@@ -18800,7 +18934,7 @@ mcpCommon(mcpPrompts.command("set").description("Add or replace a prompt from a
18800
18934
  mcpCommon(mcpPrompts.command("rm").description("Remove a prompt").argument("<project>", "Project name or id").argument("<name>", "Prompt name")).action(action((project, name, opts) => runMcpPrompts("rm", project, name, opts)));
18801
18935
  var mcpOps = mcpCmd.command("ops").description("Per-tool behavior. Default from the HTTP method: GET/HEAD/OPTIONS read-only, PUT/DELETE idempotent, DELETE destructive + asks to confirm");
18802
18936
  mcpCommon(mcpOps.command("list").description("Every tool with its effective behavior (overrides marked)").argument("<project>", "Project name or id")).action(action((project, opts) => runMcpOps("list", project, void 0, opts)));
18803
- mcpCommon(mcpOps.command("set").description("Override one tool").argument("<project>", "Project name or id").argument("<tool>", "Tool name").option("--read-only", "Safe to call, changes nothing (e.g. a POST search)").option("--no-read-only", "Not read-only").option("--destructive", "Changes or deletes data (e.g. a GET with side effects)").option("--no-destructive", "Not destructive").option("--idempotent", "Repeating it has no extra effect").option("--no-idempotent", "Not idempotent").option("--confirm", "Ask the user to confirm before running").option("--no-confirm", "Never ask to confirm").option("--always-async", "Return a task handle immediately (long jobs)").option("--no-always-async", "Wait for the result (default)")).action(action((project, tool, opts) => runMcpOps("set", project, tool, opts)));
18937
+ mcpCommon(mcpOps.command("set").description("Override one tool").argument("<project>", "Project name or id").argument("<tool>", "Tool name").option("--title <text>", "Human label clients and directories show (default: the OpenAPI summary, else the name spelled out)").option("--read-only", "Safe to call, changes nothing (e.g. a POST search)").option("--no-read-only", "Not read-only").option("--destructive", "Changes or deletes data (e.g. a GET with side effects)").option("--no-destructive", "Not destructive").option("--idempotent", "Repeating it has no extra effect").option("--no-idempotent", "Not idempotent").option("--confirm", "Ask the user to confirm before running").option("--no-confirm", "Never ask to confirm").option("--always-async", "Return a task handle immediately (long jobs)").option("--no-always-async", "Wait for the result (default)")).action(action((project, tool, opts) => runMcpOps("set", project, tool, opts)));
18804
18938
  mcpCommon(mcpOps.command("reset").description("Back to the automatic default for one tool").argument("<project>", "Project name or id").argument("<tool>", "Tool name")).action(action((project, tool, opts) => runMcpOps("reset", project, tool, opts)));
18805
18939
  mcpCommon(mcpCmd.command("ui").description("Print a tool's MCP Apps UI resource (ui://{project}/{tool}) \u2014 the automatic viewer").argument("<project>", "Project name or id").argument("<tool>", "Tool name")).action(action((project, tool, opts) => runMcpUi(project, tool, opts)));
18806
18940
  program.command("apichat [project]").alias("chat").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) => {
@@ -18824,7 +18958,7 @@ program.command("dev").description(`Put the app running on THIS machine on the i
18824
18958
  try {
18825
18959
  const resolved = parseInt(port ?? opts.port, 10);
18826
18960
  if (Number.isNaN(resolved)) {
18827
- console.error(import_chalk72.default.red(`Invalid port: ${port ?? opts.port}`));
18961
+ console.error(import_chalk73.default.red(`Invalid port: ${port ?? opts.port}`));
18828
18962
  process.exit(1);
18829
18963
  }
18830
18964
  await runDev({ ...opts, port: resolved });
@@ -18936,6 +19070,18 @@ program.command("preapprove").description("Allow an email or company domain to s
18936
19070
  var tenant = program.command("tenant").description("Manage tenants \u2014 bare command opens the interactive picker (settings, OAuth sign-in providers, app clients)").action(action(() => runTenantManage(void 0, {})));
18937
19071
  tenant.command("manage").description("Browse & edit one tenant: settings, OAuth sign-in providers (swap or add one \u2014 this is how you change sign-in after create), login app clients, issuers (search-first picker)").argument("[query]", "Search by tenant name/display name (omit to use your tenant scope or pick)").option("--tenant <slug>", "Exact tenant slug (skips the picker)").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantManage(query, opts)));
18938
19072
  tenant.command("use").description("Set the sticky tenant scope for future commands (search-first; --clear to unset)").argument("[query]", "Search by tenant name/display name").option("--clear", "Clear the tenant scope").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantUse(query, opts)));
19073
+ var tenantPassword = tenant.command("password").description("APIblaze-hosted username & password sign-in on a tenant (reviewer / test accounts; advanced)");
19074
+ var tenantArg = (cmd) => cmd.argument("[tenant]", "Tenant (defaults to your tenant scope, else a picker)").option("--tenant <slug>", "Same as the positional").option("--json", "Output machine-readable JSON");
19075
+ tenantArg(tenantPassword.command("enable").description("Add username & password sign-in to the tenant's login (idempotent)")).action(action((t, opts) => runTenantPasswordEnable(t, opts)));
19076
+ tenantArg(tenantPassword.command("disable").description("Remove username & password sign-in from the tenant (deletes its accounts)")).option("-y, --yes", "Skip the confirmation").action(action((t, opts) => runTenantPasswordDisable(t, opts)));
19077
+ tenantArg(tenantPassword.command("list").description("List the accounts")).action(action((t, opts) => runTenantPasswordList(t, opts)));
19078
+ var accountCmd = (name, desc) => tenantArg(tenantPassword.command(name).description(desc).argument("<username>", "3\u201364 chars: a\u2013z 0\u20139 . _ -"));
19079
+ accountCmd("add", "Create an account; the password is generated and printed once (or read from stdin with --password-stdin)").option("--email <email>", "Optional: lets a pre-provisioned user/admin with this address adopt the login").option("--display-name <name>", "Display name").option("--purpose <user|reviewer>", "`reviewer` marks a directory test account").option("--password-stdin", "Read the password from stdin instead of generating one").action(action((username, t, opts) => runTenantPasswordAdd(username, t, opts)));
19080
+ accountCmd("rotate", "New password (generated, printed once; or --password-stdin); existing sessions end at their next refresh").option("--password-stdin", "Read the new password from stdin").action(action((username, t, opts) => runTenantPasswordRotate(username, t, opts)));
19081
+ accountCmd("block", "Refuse sign-in for an account (keeps it)").action(action((username, t, opts) => runTenantPasswordSetBlocked(username, t, true, opts)));
19082
+ accountCmd("unblock", "Allow sign-in again").action(action((username, t, opts) => runTenantPasswordSetBlocked(username, t, false, opts)));
19083
+ accountCmd("unlock", "Clear the lockout after repeated failed sign-ins").action(action((username, t, opts) => runTenantPasswordUnlock(username, t, opts)));
19084
+ accountCmd("remove", "Delete an account").option("-y, --yes", "Skip the confirmation").action(action((username, t, opts) => runTenantPasswordRemove(username, t, opts)));
18939
19085
  tenant.command("list").description("List tenants in your team (--q searches server-side)").option("--q <search>", "Filter by tenant name or display name").option("--limit <n>", "Page size (max 200)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantList(opts)));
18940
19086
  tenant.command("create").description("Create a tenant in your team (tenant names are lowercase alphanumeric and globally unique)").requiredOption("--name <name>", "Tenant name \u2014 becomes the slug ({name}.portal.apiblaze.com) unless --slug overrides; also the display label").option("--slug <tenant_name>", "Explicit tenant slug when it should differ from --name").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantCreate(opts)));
18941
19087
  tenant.command("attach").description("Attach a tenant to a proxy").argument("<project>", "Project name or id").requiredOption("--tenant <slug>", "Tenant slug to attach").option("--auth-config <id>", "Auth config id to bind").option("--no-login", "Do not give the tenant a dev-portal sign-in (by default it gets the zero-config GitHub one, unless it already has one)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTenantAttach(project, opts)));
@@ -18994,7 +19140,7 @@ function groupedCommandHelp() {
18994
19140
  const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
18995
19141
  return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
18996
19142
  }).filter(Boolean).join("\n");
18997
- return `${import_chalk72.default.bold(g.title)}
19143
+ return `${import_chalk73.default.bold(g.title)}
18998
19144
  ${rows}`;
18999
19145
  }).join("\n\n");
19000
19146
  }
@@ -19007,7 +19153,7 @@ program.configureHelp({
19007
19153
  program.addHelpText("before", () => isBetaCached() ? `${betaNotice()}
19008
19154
  ` : "");
19009
19155
  program.addHelpText("after", () => `
19010
- ${import_chalk72.default.bold("Don't like reading docs?")} Run ${import_chalk72.default.bgCyan.black.bold(` npx ${B.cli} docs `)} ${import_chalk72.default.cyan(`\u2014 ask an AI chatbot about ${B.product}, and let it act on your account.`)}
19156
+ ${import_chalk73.default.bold("Don't like reading docs?")} Run ${import_chalk73.default.bgCyan.black.bold(` npx ${B.cli} docs `)} ${import_chalk73.default.cyan(`\u2014 ask an AI chatbot about ${B.product}, and let it act on your account.`)}
19011
19157
 
19012
19158
  ${groupedCommandHelp()}
19013
19159
 
@@ -19044,14 +19190,14 @@ async function recoverStaleTeam() {
19044
19190
  const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
19045
19191
  const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
19046
19192
  if (!linked) {
19047
- console.error(import_chalk72.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
19193
+ console.error(import_chalk73.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
19048
19194
  return;
19049
19195
  }
19050
19196
  if (linked.teamId === creds.teamId) return;
19051
19197
  const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
19052
19198
  delete next.activeTenant;
19053
19199
  saveCredentials(next);
19054
- console.error(import_chalk72.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk72.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
19200
+ console.error(import_chalk73.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk73.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
19055
19201
  } catch {
19056
19202
  }
19057
19203
  }
@@ -19063,28 +19209,28 @@ async function printError(err) {
19063
19209
  if (err instanceof ApiError) {
19064
19210
  const data = err.body;
19065
19211
  const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
19066
- console.error(import_chalk72.default.red(`
19212
+ console.error(import_chalk73.default.red(`
19067
19213
  API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
19068
19214
  if (err.status === 403 || err.status === 404) {
19069
19215
  await recoverStaleTeam();
19070
19216
  }
19071
19217
  } else if (err instanceof Error) {
19072
- console.error(import_chalk72.default.red(`
19218
+ console.error(import_chalk73.default.red(`
19073
19219
  Error: ${err.message}`));
19074
19220
  } else {
19075
- console.error(import_chalk72.default.red("\nUnknown error"));
19221
+ console.error(import_chalk73.default.red("\nUnknown error"));
19076
19222
  }
19077
19223
  }
19078
19224
  function hintStaleCacheOnUnknownOption(cmd) {
19079
19225
  cmd.exitOverride((err) => {
19080
19226
  if (err.code === "commander.unknownOption") {
19081
- console.error(import_chalk72.default.yellow(
19227
+ console.error(import_chalk73.default.yellow(
19082
19228
  `
19083
19229
  This is ${B.cli} v${version}. If that option is documented but this build
19084
19230
  does not have it, your npx cache is serving an older copy \u2014 re-run as:
19085
19231
  `
19086
19232
  ));
19087
- console.error(import_chalk72.default.cyan(` npx ${B.cli}@latest ${process.argv.slice(2).join(" ")}
19233
+ console.error(import_chalk73.default.cyan(` npx ${B.cli}@latest ${process.argv.slice(2).join(" ")}
19088
19234
  `));
19089
19235
  }
19090
19236
  process.exit(err.exitCode);