apiblaze 0.18.8 → 0.18.10

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 +50 -11
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -703,7 +703,7 @@ var import_commander = require("commander");
703
703
  var import_chalk41 = __toESM(require("chalk"));
704
704
 
705
705
  // package.json
706
- var version = "0.18.8";
706
+ var version = "0.18.10";
707
707
 
708
708
  // src/index.ts
709
709
  init_types();
@@ -2427,6 +2427,35 @@ async function runAgentChatRepl(opts) {
2427
2427
  // src/lib/authz-apply.ts
2428
2428
  var import_chalk15 = __toESM(require("chalk"));
2429
2429
  init_api();
2430
+ async function gatherSampleIds(projectId, apiVersion, cap = 50) {
2431
+ try {
2432
+ const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
2433
+ const routes = routesRes?.data?.routes ?? [];
2434
+ const total = routes.reduce((n, r) => n + (Number(r.total) || 0), 0);
2435
+ if (!routes.length || total === 0) return { ids: [], total: 0 };
2436
+ const ordered = routes.filter((r) => r.route_hash).sort((a, b) => (Number(b.total) || 0) - (Number(a.total) || 0)).slice(0, 20);
2437
+ const ids = [];
2438
+ for (const r of ordered) {
2439
+ if (ids.length >= cap) break;
2440
+ const sRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes/${encodeURIComponent(r.route_hash)}/samples`, "GET");
2441
+ for (const s of sRes?.data?.samples ?? []) {
2442
+ if (ids.length >= cap) break;
2443
+ if (s.sample_id) ids.push(s.sample_id);
2444
+ }
2445
+ }
2446
+ return { ids, total };
2447
+ } catch {
2448
+ return { ids: [], total: 0 };
2449
+ }
2450
+ }
2451
+ function maybeWarnNoTraffic(total, projectArg) {
2452
+ if (total >= 3) return;
2453
+ console.log(import_chalk15.default.yellow(total === 0 ? " \u26A0 No captured traffic for this API yet." : ` \u26A0 Only ${total} captured request(s) so far.`));
2454
+ console.log(import_chalk15.default.dim(" The authorization agent designs much better models & rules from REAL requests"));
2455
+ console.log(import_chalk15.default.dim(" (it sees who is allowed vs denied). Send some through your proxy first \u2014"));
2456
+ console.log(import_chalk15.default.dim(` curl a few endpoints, or `) + import_chalk15.default.cyan("npx apiblaze dev") + import_chalk15.default.dim(` \u2014 then re-run.
2457
+ `));
2458
+ }
2430
2459
  async function applyProposal(opts) {
2431
2460
  const log = opts.log ?? ((s) => console.log(s));
2432
2461
  const { projectId, apiVersion, tenant: tenant2, proposal, enable } = opts;
@@ -2499,11 +2528,13 @@ async function runAuthz(projectArg, apiVersionArg) {
2499
2528
  `));
2500
2529
  }
2501
2530
  }
2531
+ const { ids: sampleIds, total: sampleTotal } = await gatherSampleIds(projectId, apiVersion);
2532
+ maybeWarnNoTraffic(sampleTotal, projectArg);
2502
2533
  await runAgentChatRepl({
2503
2534
  title: `Authorization assistant \u2014 ${projectId} ${apiVersion}`,
2504
- subtitle: `tenant ${tenant2} \xB7 discuss what authorization fits this API, then make it official.`,
2535
+ subtitle: sampleIds.length ? `tenant ${tenant2} \xB7 ${sampleIds.length} traffic sample(s) \xB7 discuss what fits, then make it official.` : `tenant ${tenant2} \xB7 discuss what authorization fits this API, then make it official.`,
2505
2536
  endpoint: `/projects/${projectId}/${apiVersion}/authz/chat`,
2506
- buildBody: () => ({ included_sample_ids: [], existing_model: null, existing_routes: [] }),
2537
+ buildBody: () => ({ included_sample_ids: sampleIds, existing_model: null, existing_routes: [] }),
2507
2538
  seedPrompt: "Analyze this API and tell me what authorization is feasible. If it is read-only, say so plainly. Then propose options \u2014 do not generate rules yet.",
2508
2539
  summarizeProposal: (data) => {
2509
2540
  const proposal = data.proposal;
@@ -2538,7 +2569,9 @@ async function runRule(ruleText, projectArg, opts) {
2538
2569
  const projectId = match.projectId;
2539
2570
  const apiVersion = opts.apiversion || match.apiVersion;
2540
2571
  const tenant2 = match.tenant || projectId;
2541
- const spinner = (0, import_ora5.default)("Designing the rule\u2026").start();
2572
+ const { ids: sampleIds, total: sampleTotal } = await gatherSampleIds(projectId, apiVersion);
2573
+ maybeWarnNoTraffic(sampleTotal, projectArg);
2574
+ const spinner = (0, import_ora5.default)(sampleIds.length ? `Designing the rule from ${sampleIds.length} traffic sample(s)\u2026` : "Designing the rule\u2026").start();
2542
2575
  const messages = [{
2543
2576
  role: "user",
2544
2577
  content: `Author this authorization rule and generate the COMPLETE proposal (OpenFGA model + per-route rules) NOW for every affected route. Do not ask clarifying questions; make reasonable assumptions and state them briefly. Rule: "${rule}"`
@@ -2546,7 +2579,7 @@ async function runRule(ruleText, projectArg, opts) {
2546
2579
  const { status, data } = await agentCall(
2547
2580
  `/projects/${projectId}/${apiVersion}/authz/chat`,
2548
2581
  "POST",
2549
- { messages, included_sample_ids: [], existing_model: null, existing_routes: [] }
2582
+ { messages, included_sample_ids: sampleIds, existing_model: null, existing_routes: [] }
2550
2583
  );
2551
2584
  if (status >= 400) {
2552
2585
  spinner.fail(`Authorization agent error (${status}): ${data?.error ?? ""}`);
@@ -5626,7 +5659,7 @@ function freeBudgetWarning(billing, anon) {
5626
5659
  if (typeof billing.free_turns_remaining === "number") {
5627
5660
  const left2 = billing.free_turns_remaining;
5628
5661
  if (left2 <= 0) return import_chalk36.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
5629
- return import_chalk36.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left`);
5662
+ return import_chalk36.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
5630
5663
  }
5631
5664
  if (typeof billing.free_remaining_cents !== "number") return null;
5632
5665
  const perTurn = Math.max(billing.cents || 0, 0.02);
@@ -5715,7 +5748,7 @@ function renderUpsell(p, upsell) {
5715
5748
  console.log("\n" + import_chalk36.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
5716
5749
  if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
5717
5750
  if (!loggedIn) {
5718
- console.log(import_chalk36.default.dim(" Options: `/login` for free login credit and higher limits, or `apiblaze llm set-key` to bring your own model key."));
5751
+ console.log(import_chalk36.default.dim(" Options: `/login` for more free chats and requests, or `apiblaze llm set-key` to bring your own model key."));
5719
5752
  } else {
5720
5753
  console.log(import_chalk36.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
5721
5754
  }
@@ -5961,7 +5994,7 @@ async function runRepl(p, initialMessages) {
5961
5994
  const llm2 = loadLlmConfig();
5962
5995
  console.log(
5963
5996
  import_chalk36.default.dim(
5964
- llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for credit \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
5997
+ llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for more free chats \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
5965
5998
  )
5966
5999
  );
5967
6000
  for (; ; ) {
@@ -5980,8 +6013,14 @@ async function runRepl(p, initialMessages) {
5980
6013
  }
5981
6014
  if (text === "/claim") {
5982
6015
  if (!loadCredentials()) {
5983
- console.log(import_chalk36.default.yellow(" Log in first: `/login`, then `/claim`."));
5984
- continue;
6016
+ console.log(import_chalk36.default.dim(" Logging in to claim your workspace\u2026"));
6017
+ try {
6018
+ await runLogin();
6019
+ } catch (err) {
6020
+ console.log(import_chalk36.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
6021
+ continue;
6022
+ }
6023
+ if (!loadCredentials()) continue;
5985
6024
  }
5986
6025
  try {
5987
6026
  await runClaim(void 0, {});
@@ -6062,7 +6101,7 @@ async function runApichat(opts) {
6062
6101
  if (p.proxyUrl) console.log(` ${import_chalk36.default.green("\u2713")} proxy ${import_chalk36.default.bold(p.proxyUrl)}`);
6063
6102
  if (mcpUrl) console.log(` ${import_chalk36.default.green("\u2713")} mcp ${import_chalk36.default.bold(mcpUrl)}`);
6064
6103
  if (p.anon) {
6065
- console.log(import_chalk36.default.dim("\n Anonymous workspace \u2014 /claim inside the chat (after /login) to keep it beyond 30 days."));
6104
+ console.log(import_chalk36.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
6066
6105
  }
6067
6106
  await runRepl(p);
6068
6107
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.18.8",
4
- "description": "APIblaze CLI \u2014 Chat with your APIs, Manage your API keys, users and groups with the APIblaze serverless proxy",
3
+ "version": "0.18.10",
4
+ "description": "APIblaze CLI Chat with your APIs, Manage your API keys, users and groups with the APIblaze serverless proxy",
5
5
  "keywords": [
6
6
  "apiblaze",
7
7
  "dev-tunnel",