apiblaze 0.17.1 → 0.17.4

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 +111 -78
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -204,10 +204,10 @@ async function createProxy(payload) {
204
204
  body: JSON.stringify(payload)
205
205
  });
206
206
  }
207
- async function claimTeam(code, teamId) {
207
+ async function claimTeam(code, teamId, name) {
208
208
  return apiFetch("/api/cli/claim-team", {
209
209
  method: "POST",
210
- body: JSON.stringify({ code, ...teamId ? { team_id: teamId } : {} })
210
+ body: JSON.stringify({ code, ...teamId ? { team_id: teamId } : {}, ...name ? { name } : {} })
211
211
  });
212
212
  }
213
213
  async function sidecarInitAnonymous() {
@@ -351,6 +351,9 @@ async function cpFetch(cpKey, apiPath, opts = {}) {
351
351
  parsed = text;
352
352
  }
353
353
  if (!res.ok) {
354
+ if (res.status === 401 && loadAnonCred()?.cp_key === cpKey) {
355
+ clearAnonCred();
356
+ }
354
357
  const msg = parsed && typeof parsed === "object" && (parsed.details ?? parsed.error) || res.statusText;
355
358
  throw new ApiError(res.status, typeof msg === "string" ? msg : JSON.stringify(msg), parsed);
356
359
  }
@@ -383,32 +386,32 @@ function maskBody(body) {
383
386
  }
384
387
  function renderTrace() {
385
388
  if (!verbose || entries.length === 0) return;
386
- console.log(import_chalk15.default.dim("\n" + "\u2500".repeat(64)));
387
- console.log(import_chalk15.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
389
+ console.log(import_chalk11.default.dim("\n" + "\u2500".repeat(64)));
390
+ console.log(import_chalk11.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
388
391
  console.log(
389
- import_chalk15.default.dim("The same thing on the official API \u2014 copy/paste with your control-plane key\n(get one from the Developers section of dashboard.apiblaze.com, then\n`export APIBLAZE_CONTROLPLANE_APIKEY=sk_...`).\nFull API reference: https://api.apiblaze.com/openapi.json\n")
392
+ import_chalk11.default.dim("The same thing on the official API \u2014 copy/paste with your control-plane key\n(get one from the Developers section of dashboard.apiblaze.com, then\n`export APIBLAZE_CONTROLPLANE_APIKEY=sk_...`).\nFull API reference: https://api.apiblaze.com/openapi.json\n")
390
393
  );
391
394
  entries.forEach((e, i) => {
392
- const n = entries.length > 1 ? import_chalk15.default.bold(`${i + 1}. `) : "";
393
- if (e.summary) console.log(`${n}${import_chalk15.default.cyan(e.summary)}${e.status ? import_chalk15.default.dim(` (HTTP ${e.status})`) : ""}`);
395
+ const n = entries.length > 1 ? import_chalk11.default.bold(`${i + 1}. `) : "";
396
+ if (e.summary) console.log(`${n}${import_chalk11.default.cyan(e.summary)}${e.status ? import_chalk11.default.dim(` (HTTP ${e.status})`) : ""}`);
394
397
  const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
395
398
  const masked = maskBody(e.body);
396
399
  const hasBody = e.method !== "GET" && masked !== void 0;
397
- console.log(import_chalk15.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
398
- console.log(import_chalk15.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
400
+ console.log(import_chalk11.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
401
+ console.log(import_chalk11.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
399
402
  if (hasBody) {
400
- console.log(import_chalk15.default.green(" -H 'Content-Type: application/json' \\"));
401
- console.log(import_chalk15.default.green(` -d '${masked}'`));
403
+ console.log(import_chalk11.default.green(" -H 'Content-Type: application/json' \\"));
404
+ console.log(import_chalk11.default.green(` -d '${masked}'`));
402
405
  }
403
406
  if (i < entries.length - 1) console.log();
404
407
  });
405
408
  entries.length = 0;
406
409
  }
407
- var import_chalk15, CONTROL_API_VERSION, verbose, entries, SECRET_KEY;
410
+ var import_chalk11, CONTROL_API_VERSION, verbose, entries, SECRET_KEY;
408
411
  var init_trace = __esm({
409
412
  "src/lib/trace.ts"() {
410
413
  "use strict";
411
- import_chalk15 = __toESM(require("chalk"));
414
+ import_chalk11 = __toESM(require("chalk"));
412
415
  CONTROL_API_VERSION = "1.0.0";
413
416
  verbose = false;
414
417
  entries = [];
@@ -437,19 +440,22 @@ async function admin(call) {
437
440
  }
438
441
  return data;
439
442
  }
443
+ function formatBilling(billing) {
444
+ const b = billing;
445
+ if (!b || typeof b.charged_cents !== "number") return null;
446
+ const usd = (b.charged_cents / 100).toFixed(2);
447
+ const rem = typeof b.credits_remaining === "number" ? ` \xB7 $${(b.credits_remaining / 100).toFixed(2)} credit left` : "";
448
+ return `\u{1F4B3} Charged $${usd}${rem}`;
449
+ }
440
450
  function maybePrintBilling(data) {
441
- const b = data?.billing;
442
- if (b && typeof b.charged_cents === "number") {
443
- const usd = (b.charged_cents / 100).toFixed(2);
444
- const rem = typeof b.credits_remaining === "number" ? ` \xB7 $${(b.credits_remaining / 100).toFixed(2)} credit left` : "";
445
- console.log(import_chalk16.default.magenta(` \u{1F4B3} Charged $${usd}${rem}`));
446
- }
451
+ const line = formatBilling(data?.billing);
452
+ if (line) console.log(import_chalk12.default.magenta(` ${line}`));
447
453
  }
448
- var import_chalk16, DASHBOARD_BASE3;
454
+ var import_chalk12, DASHBOARD_BASE3;
449
455
  var init_admin = __esm({
450
456
  "src/lib/admin.ts"() {
451
457
  "use strict";
452
- import_chalk16 = __toESM(require("chalk"));
458
+ import_chalk12 = __toESM(require("chalk"));
453
459
  init_auth();
454
460
  init_trace();
455
461
  init_types();
@@ -687,7 +693,7 @@ var import_commander = require("commander");
687
693
  var import_chalk35 = __toESM(require("chalk"));
688
694
 
689
695
  // package.json
690
- var version = "0.17.1";
696
+ var version = "0.17.4";
691
697
 
692
698
  // src/index.ts
693
699
  init_types();
@@ -825,6 +831,12 @@ async function offerClaimPendingAnon() {
825
831
  console.log(import_chalk2.default.green(` \u2714 Claimed your anonymous proxies into your account (team ${import_chalk2.default.bold(t.team_id)}).`));
826
832
  } catch (err) {
827
833
  const { ApiError: ApiError2 } = await Promise.resolve().then(() => (init_types(), types_exports));
834
+ const code = err instanceof ApiError2 && typeof err.body === "object" && err.body ? err.body.error : void 0;
835
+ if (code === "already_claimed" || code === "not_claimable") {
836
+ clearAnonCred2();
837
+ console.log(import_chalk2.default.dim(" Those anonymous proxies were already claimed \u2014 nothing to do."));
838
+ return;
839
+ }
828
840
  const why = err instanceof ApiError2 ? err.message : err instanceof Error ? err.message : "error";
829
841
  console.log(import_chalk2.default.yellow(` Couldn't auto-claim (${why}). Run \`apiblaze claim\` to retry.`));
830
842
  }
@@ -1914,10 +1926,8 @@ async function runAnonymousCreate(opts) {
1914
1926
  const claimCode = result.claim_code || cred?.claim_code;
1915
1927
  if (claimCode) {
1916
1928
  console.log();
1917
- console.log(` ${import_chalk6.default.yellow("\u26A0 Anonymous \u2014 claim within 30 days or it expires.")} Everything you create`);
1918
- console.log(` with the CP key shares ONE workspace. To keep it all in one shot:`);
1919
- console.log(` ${import_chalk6.default.cyan("apiblaze login")} ${import_chalk6.default.dim("(prompts to claim your workspace into your account)")}`);
1920
- console.log(import_chalk6.default.dim(` From another machine: apiblaze claim ${claimCode} (add --team <name> to merge into an existing team)`));
1929
+ console.log(` ${import_chalk6.default.yellow("\u26A0 Anonymous \u2014 claim within 30 days or it expires.")} To claim these proxies:`);
1930
+ console.log(` ${import_chalk6.default.cyan(`npx apiblaze login && npx apiblaze claim ${claimCode}`)}`);
1921
1931
  } else if (result.claim_url) {
1922
1932
  console.log();
1923
1933
  console.log(` ${import_chalk6.default.yellow("\u26A0 Anonymous proxy \u2014 claim it to your account within 30 days or it expires:")}`);
@@ -1963,13 +1973,21 @@ async function runClaim(claimCodeArg, opts) {
1963
1973
  }
1964
1974
  let res;
1965
1975
  try {
1966
- res = await claimTeam(claimCode, targetTeamId);
1976
+ res = await claimTeam(claimCode, targetTeamId, opts.name);
1967
1977
  } catch (err) {
1968
1978
  if (err instanceof ApiError) {
1969
1979
  const c = typeof err.body === "object" && err.body ? err.body.error : void 0;
1970
- if (c === "already_claimed") fail2("That claim code has already been used.");
1971
- if (c === "expired") fail2("That claim code has expired. Create a fresh setup with `apiblaze create`/`init`.");
1972
- if (c === "not_claimable") fail2("That workspace can no longer be claimed (it already belongs to an account).");
1980
+ if (c === "already_claimed" || c === "not_claimable") {
1981
+ if (fromLocal) clearAnonCred();
1982
+ console.log(import_chalk7.default.yellow("\nThis workspace was already claimed (e.g. on dashboard.apiblaze.com)."));
1983
+ if (fromLocal) console.log(import_chalk7.default.dim(" Cleared the local anonymous key."));
1984
+ console.log(import_chalk7.default.dim(" Your proxies are safe under your account \u2014 manage them with `apiblaze login`."));
1985
+ return;
1986
+ }
1987
+ if (c === "expired") {
1988
+ if (fromLocal) clearAnonCred();
1989
+ fail2("That claim code has expired. Create a fresh setup with `apiblaze create`/`init`.");
1990
+ }
1973
1991
  if (err.status === 404) fail2("That claim code isn't valid \u2014 check it and try again.");
1974
1992
  if (err.status === 403) fail2(`You're not a member of team "${opts.team}".`);
1975
1993
  fail2(`Could not claim: ${err.message}`);
@@ -1985,8 +2003,9 @@ async function runClaim(claimCodeArg, opts) {
1985
2003
  if (res.merged) {
1986
2004
  console.log(` Merged ${import_chalk7.default.bold(String(res.proxy_count ?? ""))} prox${res.proxy_count === 1 ? "y" : "ies"} into team ${import_chalk7.default.bold(res.team_id)}.`);
1987
2005
  } else {
1988
- console.log(` It's now your team ${import_chalk7.default.bold(res.team_id)}. Switch to it with ${import_chalk7.default.cyan(`apiblaze team ${res.team_id}`)}.`);
1989
- console.log(import_chalk7.default.dim(` (Tip: re-run with --team <name> to merge into an existing team instead.)`));
2006
+ const label2 = res.team_name ? `\u201C${res.team_name}\u201D` : res.team_id;
2007
+ console.log(` It's now your team ${import_chalk7.default.bold(label2)}. Switch to it with ${import_chalk7.default.cyan(`apiblaze team ${res.team_id}`)}.`);
2008
+ console.log(import_chalk7.default.dim(` (Tip: name it with --name, or --team <name> to merge into an existing team instead.)`));
1990
2009
  }
1991
2010
  console.log(`
1992
2011
  Manage it at ${import_chalk7.default.cyan("https://dashboard.apiblaze.com/dashboard")}`);
@@ -2163,9 +2182,11 @@ async function runLogout(opts = {}) {
2163
2182
  // src/commands/whoami.ts
2164
2183
  var import_chalk9 = __toESM(require("chalk"));
2165
2184
  init_auth();
2185
+ init_anon_cred();
2166
2186
  async function runWhoami(opts = {}) {
2167
2187
  const creds = loadCredentials();
2168
2188
  const consumer2 = loadConsumer();
2189
+ const anon = loadAnonCred();
2169
2190
  if (opts.json) {
2170
2191
  console.log(JSON.stringify({
2171
2192
  producer: creds ? {
@@ -2177,7 +2198,8 @@ async function runWhoami(opts = {}) {
2177
2198
  teamId: creds.teamId ?? null,
2178
2199
  teamName: creds.teamName ?? null
2179
2200
  } : { loggedIn: false },
2180
- consumer: consumer2 ? { loggedIn: true, expired: Date.now() >= consumer2.expiresAt, tenant: consumer2.tenant, email: consumer2.email ?? null } : { loggedIn: false }
2201
+ consumer: consumer2 ? { loggedIn: true, expired: Date.now() >= consumer2.expiresAt, tenant: consumer2.tenant, email: consumer2.email ?? null } : { loggedIn: false },
2202
+ anonymous: anon ? { exists: true, teamId: anon.team_id, claimCode: anon.claim_code ?? null, cpKeyPrefix: anon.cp_key.slice(0, 11) } : { exists: false }
2181
2203
  }, null, 2));
2182
2204
  return;
2183
2205
  }
@@ -2192,6 +2214,16 @@ async function runWhoami(opts = {}) {
2192
2214
  if (creds.activeTenant) console.log(` Tenant scope: ${import_chalk9.default.bold(creds.activeTenant)} ${import_chalk9.default.dim("(clear: `apiblaze tenant use --clear`)")}`);
2193
2215
  if (Date.now() >= creds.expiresAt) console.log(import_chalk9.default.yellow(" \u26A0 Session expired \u2014 run `apiblaze login`."));
2194
2216
  }
2217
+ if (anon) {
2218
+ const masked = `${anon.cp_key.slice(0, 11)}\u2026${anon.cp_key.slice(-4)}`;
2219
+ console.log(import_chalk9.default.bold("\nAnonymous workspace") + import_chalk9.default.dim(" (created without an account \u2014 unclaimed)"));
2220
+ console.log(` Team: ${import_chalk9.default.bold(anon.team_id)}`);
2221
+ console.log(` CP key: ${import_chalk9.default.bold(masked)} ${import_chalk9.default.dim("controls the proxies you created anonymously")}`);
2222
+ console.log(
2223
+ import_chalk9.default.dim(` Keep it: ${import_chalk9.default.cyan("apiblaze login")} then ${import_chalk9.default.cyan("apiblaze claim")}`) + (anon.claim_code ? import_chalk9.default.dim(` \xB7 from another machine: ${import_chalk9.default.cyan(`apiblaze claim ${anon.claim_code}`)}`) : "")
2224
+ );
2225
+ if (creds) console.log(import_chalk9.default.dim(" (you're logged in \u2014 `apiblaze claim` folds this workspace into your account)"));
2226
+ }
2195
2227
  console.log(import_chalk9.default.bold("\nAPI Consumer"));
2196
2228
  if (!consumer2) {
2197
2229
  console.log(import_chalk9.default.dim(" Not logged in. Run `apiblaze consumer login`."));
@@ -2249,41 +2281,42 @@ async function runTeam(arg) {
2249
2281
  }
2250
2282
 
2251
2283
  // src/commands/authz.ts
2252
- var import_chalk12 = __toESM(require("chalk"));
2284
+ var import_chalk14 = __toESM(require("chalk"));
2253
2285
  init_auth();
2254
2286
  init_api();
2255
2287
 
2256
2288
  // src/lib/agent-chat.ts
2257
2289
  var import_readline = __toESM(require("readline"));
2258
- var import_chalk11 = __toESM(require("chalk"));
2290
+ var import_chalk13 = __toESM(require("chalk"));
2259
2291
  init_api();
2292
+ init_admin();
2260
2293
  async function runAgentChatRepl(opts) {
2261
2294
  const { title, subtitle, endpoint, buildBody, seedPrompt, summarizeProposal, commands } = opts;
2262
- console.log("\n" + import_chalk11.default.cyan.bold(title));
2263
- if (subtitle) console.log(import_chalk11.default.dim(subtitle));
2295
+ console.log("\n" + import_chalk13.default.cyan.bold(title));
2296
+ if (subtitle) console.log(import_chalk13.default.dim(subtitle));
2264
2297
  const messages = [];
2265
2298
  const ctx = { proposal: null, lastData: null, log: (s) => console.log(s) };
2266
2299
  const cmdByName = new Map(commands.map((c) => [c.name, c]));
2267
2300
  const footer = () => {
2268
2301
  const parts = [
2269
- import_chalk11.default.dim("type to chat/refine"),
2270
- ...commands.map((c) => import_chalk11.default.dim(`/${c.name} ${c.describe}`)),
2271
- import_chalk11.default.dim("/show"),
2272
- import_chalk11.default.dim("/drop discard & exit")
2302
+ import_chalk13.default.dim("type to chat/refine"),
2303
+ ...commands.map((c) => import_chalk13.default.dim(`/${c.name} ${c.describe}`)),
2304
+ import_chalk13.default.dim("/show"),
2305
+ import_chalk13.default.dim("/drop discard & exit")
2273
2306
  ];
2274
- return " " + import_chalk11.default.dim("[ ") + parts.join(import_chalk11.default.dim(" \xB7 ")) + import_chalk11.default.dim(" ]");
2307
+ return " " + import_chalk13.default.dim("[ ") + parts.join(import_chalk13.default.dim(" \xB7 ")) + import_chalk13.default.dim(" ]");
2275
2308
  };
2276
2309
  async function turn(content) {
2277
2310
  messages.push({ role: "user", content });
2278
- process.stdout.write(import_chalk11.default.dim(" \u2026thinking\n"));
2311
+ process.stdout.write(import_chalk13.default.dim(" \u2026thinking\n"));
2279
2312
  const { status, data } = await agentCall(endpoint, "POST", { messages, ...buildBody(messages) });
2280
2313
  if (status === 402) {
2281
- console.log(import_chalk11.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
2314
+ console.log(import_chalk13.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
2282
2315
  messages.pop();
2283
2316
  return;
2284
2317
  }
2285
2318
  if (status >= 400 || !data) {
2286
- console.log(import_chalk11.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
2319
+ console.log(import_chalk13.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
2287
2320
  `));
2288
2321
  messages.pop();
2289
2322
  return;
@@ -2291,20 +2324,19 @@ async function runAgentChatRepl(opts) {
2291
2324
  ctx.lastData = data;
2292
2325
  const reply = data.reply ?? data.message ?? "(no reply)";
2293
2326
  messages.push({ role: "assistant", content: reply });
2294
- console.log("\n" + import_chalk11.default.cyan("agent \u203A ") + reply + "\n");
2327
+ console.log("\n" + import_chalk13.default.cyan("agent \u203A ") + reply + "\n");
2295
2328
  if (data.proposal) {
2296
2329
  ctx.proposal = data.proposal;
2297
2330
  const summary = summarizeProposal?.(data);
2298
- if (summary) console.log(import_chalk11.default.yellow(" " + summary));
2331
+ if (summary) console.log(import_chalk13.default.yellow(" " + summary));
2299
2332
  const ready = commands.filter((c) => c.needsProposal).map((c) => `/${c.name}`).join(" or ");
2300
- if (ready) console.log(import_chalk11.default.dim(` Ready \u2014 ${ready} to make it official, or keep refining.`));
2333
+ if (ready) console.log(import_chalk13.default.dim(` Ready \u2014 ${ready} to make it official, or keep refining.`));
2301
2334
  console.log();
2302
2335
  }
2303
- const llm = data.llm;
2304
- if (llm) {
2305
- const credits = data.credits_remaining;
2306
- const left = typeof credits === "number" ? ` \xB7 $${(credits / 100).toFixed(2)} credit left` : "";
2307
- console.log(import_chalk11.default.dim(` ~$${(llm.cost_estimate ?? 0).toFixed(4)} \xB7 ${llm.model ?? "?"}${left}`));
2336
+ const line = formatBilling(data.billing);
2337
+ if (line) {
2338
+ const llm = data.llm;
2339
+ console.log(import_chalk13.default.dim(` ${line}${llm?.model ? ` \xB7 ${llm.model}` : ""}`));
2308
2340
  }
2309
2341
  }
2310
2342
  const rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout });
@@ -2312,26 +2344,26 @@ async function runAgentChatRepl(opts) {
2312
2344
  await turn(seedPrompt);
2313
2345
  for (; ; ) {
2314
2346
  console.log(footer());
2315
- const line = (await ask(import_chalk11.default.green("you \u203A "))).trim();
2347
+ const line = (await ask(import_chalk13.default.green("you \u203A "))).trim();
2316
2348
  if (!line) continue;
2317
2349
  if (line === "/drop" || line === "/exit" || line === "/quit") {
2318
- console.log(import_chalk11.default.dim(" Dropped \u2014 nothing applied.\n"));
2350
+ console.log(import_chalk13.default.dim(" Dropped \u2014 nothing applied.\n"));
2319
2351
  break;
2320
2352
  }
2321
2353
  if (line === "/show") {
2322
- if (!ctx.proposal) console.log(import_chalk11.default.dim(" No proposal yet \u2014 keep chatting until the agent proposes one.\n"));
2354
+ if (!ctx.proposal) console.log(import_chalk13.default.dim(" No proposal yet \u2014 keep chatting until the agent proposes one.\n"));
2323
2355
  else console.log("\n" + JSON.stringify(ctx.proposal, null, 2) + "\n");
2324
2356
  continue;
2325
2357
  }
2326
2358
  if (line.startsWith("/")) {
2327
2359
  const cmd = cmdByName.get(line.slice(1));
2328
2360
  if (!cmd) {
2329
- console.log(import_chalk11.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
2361
+ console.log(import_chalk13.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
2330
2362
  `));
2331
2363
  continue;
2332
2364
  }
2333
2365
  if (cmd.needsProposal && !ctx.proposal) {
2334
- console.log(import_chalk11.default.yellow(" No proposal yet \u2014 keep chatting until the agent generates one, then try again.\n"));
2366
+ console.log(import_chalk13.default.yellow(" No proposal yet \u2014 keep chatting until the agent generates one, then try again.\n"));
2335
2367
  continue;
2336
2368
  }
2337
2369
  await cmd.run(ctx);
@@ -2360,11 +2392,11 @@ async function runAuthz(projectArg, apiVersionArg) {
2360
2392
  if (!enable) {
2361
2393
  const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant2)}`, "POST", proposal.model);
2362
2394
  if (m.status === 404) {
2363
- ctx.log(import_chalk12.default.red(" Authorization store not provisioned yet. Open the dashboard Authorization tab for this project once (it auto-provisions the store), then re-run.\n"));
2395
+ ctx.log(import_chalk14.default.red(" Authorization store not provisioned yet. Open the dashboard Authorization tab for this project once (it auto-provisions the store), then re-run.\n"));
2364
2396
  return;
2365
2397
  }
2366
2398
  if (m.status >= 400) {
2367
- ctx.log(import_chalk12.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
2399
+ ctx.log(import_chalk14.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
2368
2400
  `));
2369
2401
  return;
2370
2402
  }
@@ -2393,14 +2425,14 @@ async function runAuthz(projectArg, apiVersionArg) {
2393
2425
  if (enable) {
2394
2426
  const e = await agentCall(`/${projectId}/${apiVersion}/config`, "PATCH", { authorization: { enforce_authorization: true }, tenant: tenant2 });
2395
2427
  if (e.status >= 400) {
2396
- ctx.log(import_chalk12.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
2428
+ ctx.log(import_chalk14.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
2397
2429
  `));
2398
2430
  return;
2399
2431
  }
2400
- ctx.log(import_chalk12.default.green(` \u2713 Enforcement ON \u2014 ${ok} route(s) + project switch on${fail4 ? `, ${fail4} failed` : ""}.
2432
+ ctx.log(import_chalk14.default.green(` \u2713 Enforcement ON \u2014 ${ok} route(s) + project switch on${fail4 ? `, ${fail4} failed` : ""}.
2401
2433
  `));
2402
2434
  } else {
2403
- ctx.log(import_chalk12.default.green(` \u2713 Published (shadow): model + ${ok} route(s)${fail4 ? `, ${fail4} failed` : ""}. Review, then /enable.
2435
+ ctx.log(import_chalk14.default.green(` \u2713 Published (shadow): model + ${ok} route(s)${fail4 ? `, ${fail4} failed` : ""}. Review, then /enable.
2404
2436
  `));
2405
2437
  }
2406
2438
  }
@@ -2425,7 +2457,7 @@ async function runAuthz(projectArg, apiVersionArg) {
2425
2457
  }
2426
2458
 
2427
2459
  // src/commands/openapi.ts
2428
- var import_chalk13 = __toESM(require("chalk"));
2460
+ var import_chalk15 = __toESM(require("chalk"));
2429
2461
  init_auth();
2430
2462
  init_api();
2431
2463
  async function runOpenapi(projectArg, apiVersionArg) {
@@ -2436,10 +2468,10 @@ async function runOpenapi(projectArg, apiVersionArg) {
2436
2468
  if (!match) throw new Error(`Project "${projectArg}" not found in your active team. Run \`apiblaze projects\`.`);
2437
2469
  const projectId = match.projectId;
2438
2470
  const apiVersion = apiVersionArg || match.apiVersion;
2439
- console.log(import_chalk13.default.dim("Gathering captured traffic samples\u2026"));
2471
+ console.log(import_chalk15.default.dim("Gathering captured traffic samples\u2026"));
2440
2472
  const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
2441
2473
  if (routesRes.status >= 400) {
2442
- console.log(import_chalk13.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
2474
+ console.log(import_chalk15.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
2443
2475
  return;
2444
2476
  }
2445
2477
  const routes = routesRes.data?.routes ?? (Array.isArray(routesRes.data) ? routesRes.data : []);
@@ -2452,25 +2484,25 @@ async function runOpenapi(projectArg, apiVersionArg) {
2452
2484
  }
2453
2485
  const sampleIds = [...ids];
2454
2486
  if (sampleIds.length === 0) {
2455
- console.log(import_chalk13.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
2487
+ console.log(import_chalk15.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
2456
2488
  return;
2457
2489
  }
2458
2490
  async function publish(ctx) {
2459
2491
  const patch = ctx.proposal.patch;
2460
2492
  if (!Array.isArray(patch) || patch.length === 0) {
2461
- ctx.log(import_chalk13.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
2493
+ ctx.log(import_chalk15.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
2462
2494
  return;
2463
2495
  }
2464
2496
  const specSource = ctx.lastData?.spec_source ?? "unknown";
2465
2497
  const endpoint = specSource === "github" ? "open-pr" : "publish-openapi";
2466
2498
  const pub = await agentCall(`/projects/${projectId}/${apiVersion}/samples/${endpoint}`, "POST", { patch, sample_ids_used: sampleIds });
2467
2499
  if (pub.status >= 400) {
2468
- ctx.log(import_chalk13.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
2500
+ ctx.log(import_chalk15.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
2469
2501
  `));
2470
2502
  return;
2471
2503
  }
2472
2504
  const prUrl = pub.data?.pr_url;
2473
- ctx.log(import_chalk13.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
2505
+ ctx.log(import_chalk15.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
2474
2506
  ` : " \u2713 Published updated OpenAPI spec.\n"));
2475
2507
  }
2476
2508
  await runAgentChatRepl({
@@ -2491,7 +2523,7 @@ async function runOpenapi(projectArg, apiVersionArg) {
2491
2523
  }
2492
2524
 
2493
2525
  // src/commands/mcp.ts
2494
- var import_chalk14 = __toESM(require("chalk"));
2526
+ var import_chalk16 = __toESM(require("chalk"));
2495
2527
  init_auth();
2496
2528
  init_api();
2497
2529
  async function runMcp(projectArg, apiVersionArg, opts) {
@@ -2508,11 +2540,11 @@ async function runMcp(projectArg, apiVersionArg, opts) {
2508
2540
  const spec2 = ctx.proposal;
2509
2541
  const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec: spec2 });
2510
2542
  if (pub.status >= 400) {
2511
- ctx.log(import_chalk14.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
2543
+ ctx.log(import_chalk16.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
2512
2544
  `));
2513
2545
  return;
2514
2546
  }
2515
- ctx.log(import_chalk14.default.green(` \u2713 Published MCP server \u2014 ${mcpHost}.mcp.apiblaze.com/${apiVersion}/${environment}.
2547
+ ctx.log(import_chalk16.default.green(` \u2713 Published MCP server \u2014 ${mcpHost}.mcp.apiblaze.com/${apiVersion}/${environment}.
2516
2548
  `));
2517
2549
  }
2518
2550
  await runAgentChatRepl({
@@ -3552,6 +3584,7 @@ async function runSpecSet(project, opts) {
3552
3584
  var import_chalk28 = __toESM(require("chalk"));
3553
3585
  var import_ora13 = __toESM(require("ora"));
3554
3586
  init_auth();
3587
+ init_admin();
3555
3588
 
3556
3589
  // src/lib/tools.ts
3557
3590
  var import_chalk27 = __toESM(require("chalk"));
@@ -3735,9 +3768,9 @@ function truncate(value, max = 1500) {
3735
3768
  if (s.length <= max) return s;
3736
3769
  return s.slice(0, max) + `\u2026 [truncated ${s.length - max} chars]`;
3737
3770
  }
3738
- function printCost(llm) {
3739
- const usd = llm.cost > 0 ? `$${llm.cost.toFixed(4)}` : "<$0.0001";
3740
- console.log(import_chalk28.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk28.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
3771
+ function printCost(resp) {
3772
+ const line = formatBilling(resp.billing);
3773
+ if (line) console.log(import_chalk28.default.magenta(` ${line}`) + import_chalk28.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
3741
3774
  }
3742
3775
  async function runAgent(opts) {
3743
3776
  requireAuth();
@@ -3767,7 +3800,7 @@ async function runAgent(opts) {
3767
3800
  throw err;
3768
3801
  }
3769
3802
  history.push({ role: "assistant", content: resp.raw });
3770
- printCost(resp.llm);
3803
+ printCost(resp);
3771
3804
  if (resp.reply) console.log(import_chalk28.default.green("agent") + " \u203A " + resp.reply);
3772
3805
  if (!resp.action) break;
3773
3806
  const tool = findTool(resp.action.tool);
@@ -5461,7 +5494,7 @@ program.command("whoami").description("Show who you are \u2014 both API Producer
5461
5494
  process.exit(1);
5462
5495
  }
5463
5496
  });
5464
- program.command("claim").description("Claim your anonymous workspace into your account (requires login)").argument("[code]", "Claim code from `create`/`init` output (omit to claim this machine's workspace)").option("--team <id|name>", "Merge into this existing team (default: promote to a new team)").option("--json", "Output machine-readable JSON (non-interactive)").action(async (code, opts) => {
5497
+ program.command("claim").description("Claim your anonymous workspace into your account (requires login)").argument("[code]", "Claim code from `create`/`init` output (omit to claim this machine's workspace)").option("--team <id|name>", "Merge into this existing team (default: promote to a new team)").option("--name <name>", "Name for the new team (promote only; defaults to your proxy's target host)").option("--json", "Output machine-readable JSON (non-interactive)").action(async (code, opts) => {
5465
5498
  try {
5466
5499
  await runClaim(code, opts);
5467
5500
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.17.1",
3
+ "version": "0.17.4",
4
4
  "description": "APIblaze CLI + sidecar — manage API proxies, run dev tunnels, and route a Next.js app's egress through APIblaze with one command",
5
5
  "keywords": [
6
6
  "apiblaze",