apiblaze 0.3.8 → 0.4.1

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 (3) hide show
  1. package/README.md +83 -35
  2. package/dist/index.js +867 -74
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,66 +1,114 @@
1
1
  # apiblaze
2
2
 
3
- CLI for [APIblaze](https://apiblaze.com) — instantly tunnel localhost to your APIblaze projects during development.
3
+ CLI for [APIblaze](https://apiblaze.com) — turn any backend into a managed API: add a key, auth, rate limits, a spec, and your own domain, in seconds.
4
4
 
5
- ## Installation
5
+ ## Install
6
6
 
7
7
  ```bash
8
- npm install -g apiblaze
8
+ npx apiblaze --help # run without installing
9
+ npm install -g apiblaze # or install globally
9
10
  ```
10
11
 
11
- Or run without installing:
12
+ Requires Node.js 18+.
13
+
14
+ ## Quick start
15
+
16
+ The easiest way is to just chat with it:
12
17
 
13
18
  ```bash
14
- npx apiblaze --help
19
+ npx apiblaze agent
15
20
  ```
16
21
 
17
- ## Requirements
18
-
19
- - Node.js 18+
22
+ > Talk in plain English — "make an API for httpbin.org", "rate-limit it to 50/sec",
23
+ > "give me a key". It does the work and shows what each step costs.
20
24
 
21
- ## Quick start
25
+ Prefer commands? A few one-liners:
22
26
 
23
27
  ```bash
24
- # Create a proxy (no account needed)
28
+ # Make an API in one line — no account needed (prints a claim URL)
25
29
  npx apiblaze create --target https://api.example.com
26
30
 
27
- # Optional: sign in if you want it under your team
28
- npx apiblaze login
29
-
30
- # Create a proxy under your team
31
- npx apiblaze create --name myapi --target https://api.example.com --auth api_key
32
-
33
- # Start a dev tunnel (defaults to port 3000)
34
- npx apiblaze dev
35
-
36
- # Or specify a port
31
+ # Run your localhost through a public URL
37
32
  npx apiblaze dev 3000
38
33
 
39
- # Stream full request/response traffic to a file (JSON lines)
40
- npx apiblaze dev 3000 --capture-file traffic.jsonl
41
-
34
+ # Sign in to manage APIs under your team
35
+ npx apiblaze login
42
36
  ```
43
37
 
44
38
  ## Help
45
39
 
46
40
  ```bash
47
- apiblaze --help
48
- apiblaze help create
49
- apiblaze help dev
41
+ apiblaze --help # all commands
42
+ apiblaze help create # help for one command
43
+ apiblaze domain --help # subcommands of a group (domain/tenant/key/spec)
44
+ ```
45
+
46
+ Add `--verbose` (or `-v`) to **any** command to print the exact series of API
47
+ calls it makes — as copy-pasteable `curl` you could run yourself with your own
48
+ login token, plus the underlying admin-api leaf each one drives:
49
+
50
+ ```bash
51
+ apiblaze throttle myapi --rate 50 --verbose
50
52
  ```
51
53
 
52
54
  ## Commands
53
55
 
54
- | Command | Description |
56
+ ### Chat (the easy way)
57
+
58
+ | Command | What it does |
59
+ |---|---|
60
+ | `apiblaze agent` | Chat about anything — create, configure, and inspect your APIs |
61
+ | `apiblaze agent openapi <project>` | Chat to build your API spec from real traffic |
62
+ | `apiblaze agent authz <project>` | Chat to design and turn on access rules |
63
+ | `apiblaze agent mcp <project>` | Chat to build an MCP server for your API |
64
+
65
+ Every chat turn shows its cost.
66
+
67
+ ### Getting started
68
+
69
+ | Command | What it does |
70
+ |---|---|
71
+ | `apiblaze create --target <url>` | Make an API from a backend (no account needed) |
72
+ | `apiblaze dev [port]` | Put your localhost behind a public URL |
73
+ | `apiblaze login` / `logout` | Sign in / out |
74
+ | `apiblaze whoami` | Who am I, and which team |
75
+ | `apiblaze team [name]` | Switch team |
76
+ | `apiblaze projects` | List your APIs |
77
+ | `apiblaze claim [code]` | Claim an API you made before signing in |
78
+
79
+ ### Manage an API
80
+
81
+ | Command | What it does |
82
+ |---|---|
83
+ | `apiblaze target <project> --url <url> [--env <e>]` | Change where it forwards requests |
84
+ | `apiblaze throttle <project> [--rate n] [--quota n] [--period daily\|weekly\|monthly]` | Set rate limits and quotas |
85
+ | `apiblaze rename <project> --display-name <name>` | Rename it |
86
+ | `apiblaze spec get <project>` | Print its OpenAPI spec |
87
+ | `apiblaze spec set <project> --file <path>` | Replace its OpenAPI spec from a file |
88
+ | `apiblaze delete <project>` | Delete it and everything under it (asks first) |
89
+
90
+ ### Your own domain
91
+
92
+ | Command | What it does |
93
+ |---|---|
94
+ | `apiblaze domain add <project> --domain <host>` | Add your domain (shows the DNS records to set) |
95
+ | `apiblaze domain status <project> --id <id>` | Check if it's verified yet |
96
+ | `apiblaze domain list <project>` / `rm <project> --id <id>` | List / remove |
97
+ | `apiblaze domain set-base <project> [--env <e>]` | Pick which version/env your main URL serves |
98
+
99
+ ### Users & keys
100
+
101
+ | Command | What it does |
55
102
  |---|---|
56
- | `apiblaze login` | Authenticate with your APIblaze account |
57
- | `apiblaze create [options]` | Create a new API proxy (anonymous if not logged in) |
58
- | `apiblaze claim [code]` | Claim an anonymously-created proxy into your team |
59
- | `apiblaze projects` | List your team projects |
60
- | `apiblaze dev [port]` | Start a dev tunnel for your localhost projects |
61
- | `apiblaze team [team]` | Switch the active team |
62
- | `apiblaze whoami` | Show the signed-in identity and active team |
63
- | `apiblaze logout` | Sign out and remove stored credentials |
103
+ | `apiblaze tenant create --name <name>` | Create a tenant (a separate group of your API's users) |
104
+ | `apiblaze tenant attach <project> --tenant <slug>` | Give a proxy its own set of users |
105
+ | `apiblaze tenant cors --tenant <slug> --origins <a,b>` | Set which websites can call it |
106
+ | `apiblaze tenant list` / `delete <slug>` | List / delete |
107
+ | `apiblaze key mint [--desc <text>]` | Make a key to manage your account from scripts |
108
+ | `apiblaze key list` / `revoke <id>` | List / revoke |
109
+
110
+ > See what any command does under the hood with `--verbose`. Most management
111
+ > commands take `--team`, `--apiversion`, and `--json`.
64
112
 
65
113
  ## How it works
66
114
 
package/dist/index.js CHANGED
@@ -8,9 +8,9 @@ var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
9
  var __copyProps = (to, from, except, desc) => {
10
10
  if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ for (let key2 of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key2) && key2 !== except)
13
+ __defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
14
14
  }
15
15
  return to;
16
16
  };
@@ -25,10 +25,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/index.ts
27
27
  var import_commander = require("commander");
28
- var import_chalk15 = __toESM(require("chalk"));
28
+ var import_chalk25 = __toESM(require("chalk"));
29
29
 
30
30
  // package.json
31
- var version = "0.3.8";
31
+ var version = "0.4.1";
32
32
 
33
33
  // src/types.ts
34
34
  var ApiError = class extends Error {
@@ -586,8 +586,8 @@ var STRIP_HEADERS = /* @__PURE__ */ new Set([
586
586
  ]);
587
587
  function stripHeaders(headers) {
588
588
  const out = {};
589
- for (const key of Object.keys(headers)) {
590
- if (!STRIP_HEADERS.has(key.toLowerCase())) out[key] = headers[key];
589
+ for (const key2 of Object.keys(headers)) {
590
+ if (!STRIP_HEADERS.has(key2.toLowerCase())) out[key2] = headers[key2];
591
591
  }
592
592
  return out;
593
593
  }
@@ -702,8 +702,8 @@ function startTunnelClient(opts) {
702
702
  const status = resp.status;
703
703
  const buf = Buffer.from(await resp.arrayBuffer());
704
704
  const headers = {};
705
- resp.headers.forEach((value, key) => {
706
- if (!STRIP_HEADERS.has(key.toLowerCase())) headers[key] = value;
705
+ resp.headers.forEach((value, key2) => {
706
+ if (!STRIP_HEADERS.has(key2.toLowerCase())) headers[key2] = value;
707
707
  });
708
708
  if (capturing) {
709
709
  capturing = false;
@@ -822,9 +822,9 @@ async function offerAutoCreate(teamId, port) {
822
822
  throw err;
823
823
  }
824
824
  if (auth === "api_key") {
825
- const key = result.api_keys?.dev ?? Object.values(result.api_keys ?? {})[0];
826
- if (key) {
827
- console.log(` ${import_chalk4.default.dim("API key (dev):")} ${import_chalk4.default.bold.green(key)}`);
825
+ const key2 = result.api_keys?.dev ?? Object.values(result.api_keys ?? {})[0];
826
+ if (key2) {
827
+ console.log(` ${import_chalk4.default.dim("API key (dev):")} ${import_chalk4.default.bold.green(key2)}`);
828
828
  console.log(import_chalk4.default.dim(" Send it as the X-API-Key header. It may not be shown again."));
829
829
  }
830
830
  }
@@ -1647,11 +1647,11 @@ async function runAuthz(projectArg, apiVersionArg) {
1647
1647
  }
1648
1648
  const projectId = match.projectId;
1649
1649
  const apiVersion = apiVersionArg || match.apiVersion;
1650
- const tenant = match.tenant || projectId;
1650
+ const tenant2 = match.tenant || projectId;
1651
1651
  async function apply(ctx, enable) {
1652
1652
  const proposal = ctx.proposal;
1653
1653
  if (!enable) {
1654
- const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant)}`, "POST", proposal.model);
1654
+ const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant2)}`, "POST", proposal.model);
1655
1655
  if (m.status === 404) {
1656
1656
  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"));
1657
1657
  return;
@@ -1684,7 +1684,7 @@ async function runAuthz(projectArg, apiVersionArg) {
1684
1684
  else fail4++;
1685
1685
  }
1686
1686
  if (enable) {
1687
- const e = await agentCall(`/${projectId}/${apiVersion}/config`, "PATCH", { authorization: { enforce_authorization: true }, tenant });
1687
+ const e = await agentCall(`/${projectId}/${apiVersion}/config`, "PATCH", { authorization: { enforce_authorization: true }, tenant: tenant2 });
1688
1688
  if (e.status >= 400) {
1689
1689
  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.
1690
1690
  `));
@@ -1699,7 +1699,7 @@ async function runAuthz(projectArg, apiVersionArg) {
1699
1699
  }
1700
1700
  await runAgentChatRepl({
1701
1701
  title: `Authorization assistant \u2014 ${projectId} ${apiVersion}`,
1702
- subtitle: `tenant ${tenant} \xB7 discuss what authorization fits this API, then make it official.`,
1702
+ subtitle: `tenant ${tenant2} \xB7 discuss what authorization fits this API, then make it official.`,
1703
1703
  endpoint: `/projects/${projectId}/${apiVersion}/authz/chat`,
1704
1704
  buildBody: () => ({ included_sample_ids: [], existing_model: null, existing_routes: [] }),
1705
1705
  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.",
@@ -1794,8 +1794,8 @@ async function runMcp(projectArg, apiVersionArg, opts) {
1794
1794
  const environment = opts.environment || "prod";
1795
1795
  const mcpHost = match.tenant ? `${projectId}-${match.tenant}` : projectId;
1796
1796
  async function publish(ctx) {
1797
- const spec = ctx.proposal;
1798
- const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec });
1797
+ const spec2 = ctx.proposal;
1798
+ const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec: spec2 });
1799
1799
  if (pub.status >= 400) {
1800
1800
  ctx.log(import_chalk14.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
1801
1801
  `));
@@ -1811,8 +1811,8 @@ async function runMcp(projectArg, apiVersionArg, opts) {
1811
1811
  buildBody: () => ({ environment, included_sample_ids: [] }),
1812
1812
  seedPrompt: "Look at this API's routes and recommend which should become MCP tools, with good names and descriptions. Don't finalize yet \u2014 explain first.",
1813
1813
  summarizeProposal: (data) => {
1814
- const spec = data.proposal;
1815
- const tools = Array.isArray(spec.tools) ? spec.tools : [];
1814
+ const spec2 = data.proposal;
1815
+ const tools = Array.isArray(spec2.tools) ? spec2.tools : [];
1816
1816
  const names = tools.slice(0, 12).map((t) => t.name ?? "(unnamed)").join(", ");
1817
1817
  return `Catalogue ready: ${tools.length} tool(s)${names ? ` \u2014 ${names}${tools.length > 12 ? ", \u2026" : ""}` : ""}.`;
1818
1818
  },
@@ -1822,137 +1822,930 @@ async function runMcp(projectArg, apiVersionArg, opts) {
1822
1822
  });
1823
1823
  }
1824
1824
 
1825
- // src/index.ts
1826
- var program = new import_commander.Command();
1827
- program.name("apiblaze").description("APIblaze CLI \u2014 create & manage API proxies and run dev tunnels").version(version);
1828
- program.command("login").description("Authenticate with APIblaze").action(async () => {
1825
+ // src/commands/delete.ts
1826
+ var import_chalk18 = __toESM(require("chalk"));
1827
+ var import_ora5 = __toESM(require("ora"));
1828
+
1829
+ // src/lib/admin.ts
1830
+ var import_chalk16 = __toESM(require("chalk"));
1831
+
1832
+ // src/lib/trace.ts
1833
+ var import_chalk15 = __toESM(require("chalk"));
1834
+ var verbose = false;
1835
+ var entries = [];
1836
+ function setVerbose(v) {
1837
+ verbose = v;
1838
+ }
1839
+ function recordCall(e) {
1840
+ if (verbose) entries.push(e);
1841
+ }
1842
+ var SECRET_KEY = /secret|token|password|api[_-]?key|client_secret/i;
1843
+ function maskBody(body) {
1844
+ if (body === void 0) return void 0;
1845
+ return JSON.stringify(body, (k, v) => SECRET_KEY.test(k) && typeof v === "string" ? "***" : v);
1846
+ }
1847
+ function renderTrace() {
1848
+ if (!verbose || entries.length === 0) return;
1849
+ console.log(import_chalk15.default.dim("\n" + "\u2500".repeat(64)));
1850
+ console.log(import_chalk15.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
1851
+ console.log(
1852
+ import_chalk15.default.dim("Copy/paste the curl below to do it yourself \u2014 it uses your own login token.\n")
1853
+ );
1854
+ entries.forEach((e, i) => {
1855
+ const n = entries.length > 1 ? import_chalk15.default.bold(`${i + 1}. `) : "";
1856
+ if (e.summary) console.log(`${n}${import_chalk15.default.cyan(e.summary)}${e.status ? import_chalk15.default.dim(` (HTTP ${e.status})`) : ""}`);
1857
+ const masked = maskBody(e.body);
1858
+ const payload = JSON.stringify({ path: e.path, method: e.method, ...masked ? { body: JSON.parse(masked) } : {} });
1859
+ console.log(import_chalk15.default.green(" curl -sS -X POST https://dashboard.apiblaze.com/api/cli/admin \\"));
1860
+ console.log(import_chalk15.default.green(' -H "Authorization: Bearer $(jq -r .accessToken ~/.apiblaze/credentials.json)" \\'));
1861
+ console.log(import_chalk15.default.green(" -H 'Content-Type: application/json' \\"));
1862
+ console.log(import_chalk15.default.green(` -d '${payload}'`));
1863
+ console.log(
1864
+ import_chalk15.default.dim(
1865
+ ` # \u2192 admin-api leaf (Lane 3, X-User-Assertion minted server-side): ${e.method} ${e.path}`
1866
+ )
1867
+ );
1868
+ if (i < entries.length - 1) console.log();
1869
+ });
1870
+ }
1871
+
1872
+ // src/lib/admin.ts
1873
+ var DASHBOARD_BASE3 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
1874
+ async function admin(call) {
1875
+ const token = getAccessToken();
1876
+ const res = await fetch(`${DASHBOARD_BASE3}/api/cli/admin`, {
1877
+ method: "POST",
1878
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
1879
+ body: JSON.stringify({ path: call.path, method: call.method, body: call.body })
1880
+ });
1881
+ let data = null;
1829
1882
  try {
1830
- await runLogin();
1883
+ data = await res.json();
1884
+ } catch {
1885
+ }
1886
+ recordCall({ method: call.method, path: call.path, body: call.body, status: res.status, summary: call.summary });
1887
+ maybePrintBilling(data);
1888
+ if (!res.ok) {
1889
+ const msg = data?.details ?? data?.error ?? res.statusText;
1890
+ throw new ApiError(res.status, typeof msg === "string" ? msg : JSON.stringify(msg), data);
1891
+ }
1892
+ return data;
1893
+ }
1894
+ function maybePrintBilling(data) {
1895
+ const b = data?.billing;
1896
+ if (b && typeof b.charged_cents === "number") {
1897
+ const usd = (b.charged_cents / 100).toFixed(2);
1898
+ const rem = typeof b.credits_remaining === "number" ? ` \xB7 $${(b.credits_remaining / 100).toFixed(2)} credit left` : "";
1899
+ console.log(import_chalk16.default.magenta(` \u{1F4B3} Charged $${usd}${rem}`));
1900
+ }
1901
+ }
1902
+
1903
+ // src/lib/resolve.ts
1904
+ var import_chalk17 = __toESM(require("chalk"));
1905
+ function requireAuth() {
1906
+ const creds = loadCredentials();
1907
+ if (!creds) {
1908
+ console.error(import_chalk17.default.red("Not logged in. Run `apiblaze login` first."));
1909
+ process.exit(1);
1910
+ }
1911
+ return creds;
1912
+ }
1913
+ async function resolveTeam(opt) {
1914
+ const creds = requireAuth();
1915
+ if (!opt) {
1916
+ if (!creds.teamId) {
1917
+ console.error(import_chalk17.default.red("No active team. Run `apiblaze login` or pass --team."));
1918
+ process.exit(1);
1919
+ }
1920
+ return { teamId: creds.teamId, teamName: creds.teamName };
1921
+ }
1922
+ if (opt.startsWith("team_")) return { teamId: opt };
1923
+ const teams = await getTeams().catch(() => []);
1924
+ const match = teams.find((t) => t.name === opt || t.teamId === opt);
1925
+ if (!match) {
1926
+ console.error(import_chalk17.default.red(`Team "${opt}" not found.${teams.length ? " Available: " + teams.map((t) => t.name).join(", ") : ""}`));
1927
+ process.exit(1);
1928
+ }
1929
+ return { teamId: match.teamId, teamName: match.name };
1930
+ }
1931
+ async function resolveProject(teamId, nameOrId, version2) {
1932
+ const projects = await getProjects(teamId).catch(() => []);
1933
+ const candidates = projects.filter(
1934
+ (p) => p.projectId === nameOrId || p.projectName === nameOrId
1935
+ );
1936
+ if (candidates.length === 0) {
1937
+ console.error(import_chalk17.default.red(`Project "${nameOrId}" not found in this team.`));
1938
+ if (projects.length) console.error(import_chalk17.default.dim(" Known: " + projects.map((p) => p.projectName).join(", ")));
1939
+ process.exit(1);
1940
+ }
1941
+ const chosen = version2 ? candidates.find((p) => p.apiVersion === version2) : candidates[0];
1942
+ if (!chosen) {
1943
+ console.error(import_chalk17.default.red(`Project "${nameOrId}" has no version ${version2}. Versions: ${candidates.map((p) => p.apiVersion).join(", ")}`));
1944
+ process.exit(1);
1945
+ }
1946
+ return {
1947
+ projectId: chosen.projectId,
1948
+ projectName: chosen.projectName,
1949
+ apiVersion: chosen.apiVersion,
1950
+ teamId,
1951
+ tenant: chosen.tenant
1952
+ };
1953
+ }
1954
+
1955
+ // src/commands/delete.ts
1956
+ async function runDelete(project, version2, opts) {
1957
+ const { teamId } = await resolveTeam(opts.team);
1958
+ const proj2 = await resolveProject(teamId, project, version2);
1959
+ const spinner = (0, import_ora5.default)("Checking delete impact...").start();
1960
+ let impact;
1961
+ try {
1962
+ impact = await admin({
1963
+ method: "GET",
1964
+ path: `/projects/${proj2.projectId}/${proj2.apiVersion}/delete-impact`,
1965
+ summary: `Preview what deleting ${proj2.projectName} v${proj2.apiVersion} removes`
1966
+ });
1967
+ spinner.stop();
1831
1968
  } catch (err) {
1832
- printError(err);
1969
+ spinner.stop();
1970
+ impact = null;
1971
+ }
1972
+ if (opts.json) {
1973
+ if (!opts.yes) {
1974
+ console.log(JSON.stringify({ project_id: proj2.projectId, api_version: proj2.apiVersion, impact, deleted: false, hint: "pass --yes to delete" }));
1975
+ return;
1976
+ }
1977
+ } else {
1978
+ console.log(`${import_chalk18.default.red.bold("Delete")} ${import_chalk18.default.bold(proj2.projectName)} ${import_chalk18.default.dim("v" + proj2.apiVersion)} ${import_chalk18.default.dim("(" + proj2.projectId + ")")}`);
1979
+ if (impact && typeof impact === "object") {
1980
+ const counts = impact.counts ?? impact.impact ?? impact;
1981
+ console.log(import_chalk18.default.dim(" This removes (cascade): ") + import_chalk18.default.yellow(JSON.stringify(counts)));
1982
+ }
1983
+ if (!opts.yes) {
1984
+ const { default: inquirer2 } = await import("inquirer");
1985
+ const { confirm } = await inquirer2.prompt([
1986
+ { type: "confirm", name: "confirm", message: `Permanently delete ${proj2.projectName} v${proj2.apiVersion}? This cannot be undone.`, default: false }
1987
+ ]);
1988
+ if (!confirm) {
1989
+ console.log(import_chalk18.default.dim("Aborted."));
1990
+ return;
1991
+ }
1992
+ }
1993
+ }
1994
+ const s2 = (0, import_ora5.default)("Deleting...").start();
1995
+ try {
1996
+ await admin({
1997
+ method: "DELETE",
1998
+ path: `/${proj2.projectId}/${proj2.apiVersion}`,
1999
+ summary: `Delete proxy ${proj2.projectName} v${proj2.apiVersion} (full cascade)`
2000
+ });
2001
+ s2.succeed(`Deleted ${proj2.projectName} v${proj2.apiVersion}.`);
2002
+ } catch (err) {
2003
+ s2.fail("Delete failed.");
2004
+ throw err;
2005
+ }
2006
+ if (opts.json) console.log(JSON.stringify({ project_id: proj2.projectId, api_version: proj2.apiVersion, deleted: true }));
2007
+ }
2008
+
2009
+ // src/commands/config.ts
2010
+ var import_chalk19 = __toESM(require("chalk"));
2011
+ var import_ora6 = __toESM(require("ora"));
2012
+ async function patchConfig(project, opts, body, summary) {
2013
+ const { teamId } = await resolveTeam(opts.team);
2014
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
2015
+ const spinner = (0, import_ora6.default)(summary + "...").start();
2016
+ try {
2017
+ const out = await admin({
2018
+ method: "PATCH",
2019
+ path: `/${proj2.projectId}/${proj2.apiVersion}/config`,
2020
+ body,
2021
+ summary
2022
+ });
2023
+ spinner.succeed(`${summary} \u2014 ${proj2.projectName} v${proj2.apiVersion}.`);
2024
+ if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
2025
+ } catch (err) {
2026
+ spinner.fail(summary + " failed.");
2027
+ throw err;
2028
+ }
2029
+ }
2030
+ async function runTargetSet(project, opts) {
2031
+ if (!opts.url) {
2032
+ console.error(import_chalk19.default.red("--url is required."));
1833
2033
  process.exit(1);
1834
2034
  }
1835
- });
1836
- program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.apiblaze.com)").option("--target <url>", "Target URL to forward requests to").option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("--apiversion <version>", "API version to create (e.g. 2.0.0). Creating a new version of a proxy you own adds a version to the existing project.").option("--tenant <slug>", "Tenant slug (anonymous create; generated if omitted)").option("--product <slug>", "Product tag to group this project under in the portal (team-scoped; anonymous create). Defaults to your team's existing/placeholder tag.").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--json", "Output machine-readable JSON (non-interactive)").action(async (opts) => {
2035
+ const body = opts.env ? { environments: { [opts.env]: { target: opts.url } } } : { target_url: opts.url };
2036
+ await patchConfig(project, opts, body, `Set target${opts.env ? ` for env ${opts.env}` : ""} \u2192 ${opts.url}`);
2037
+ }
2038
+ async function runThrottleSet(project, opts) {
2039
+ const throttling = {};
2040
+ if (opts.rate !== void 0) throttling.userRateLimit = Number(opts.rate);
2041
+ if (opts.endUserRate !== void 0) throttling.endUserRateLimit = Number(opts.endUserRate);
2042
+ if (opts.quota !== void 0) throttling.proxyQuota = Number(opts.quota);
2043
+ if (opts.period !== void 0) {
2044
+ if (!["daily", "weekly", "monthly"].includes(opts.period)) {
2045
+ console.error(import_chalk19.default.red("--period must be daily, weekly, or monthly."));
2046
+ process.exit(1);
2047
+ }
2048
+ throttling.quotaPeriod = opts.period;
2049
+ }
2050
+ if (Object.keys(throttling).length === 0) {
2051
+ console.error(import_chalk19.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
2052
+ process.exit(1);
2053
+ }
2054
+ await patchConfig(project, opts, { throttling }, `Update throttling ${JSON.stringify(throttling)}`);
2055
+ }
2056
+ async function runRename(project, opts) {
2057
+ if (!opts.displayName) {
2058
+ console.error(import_chalk19.default.red("--display-name is required."));
2059
+ process.exit(1);
2060
+ }
2061
+ await patchConfig(project, opts, { display_name: opts.displayName }, `Rename \u2192 "${opts.displayName}"`);
2062
+ }
2063
+
2064
+ // src/commands/domain.ts
2065
+ var import_chalk20 = __toESM(require("chalk"));
2066
+ var import_ora7 = __toESM(require("ora"));
2067
+ async function runDomainAdd(project, opts) {
2068
+ if (!opts.domain) {
2069
+ console.error(import_chalk20.default.red("--domain is required."));
2070
+ process.exit(1);
2071
+ }
2072
+ const { teamId } = await resolveTeam(opts.team);
2073
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
2074
+ const spinner = (0, import_ora7.default)(`Registering ${opts.domain}...`).start();
1837
2075
  try {
1838
- await runCreate(opts);
2076
+ const out = await admin({
2077
+ method: "POST",
2078
+ path: `/projects/${proj2.projectId}/cf-domains`,
2079
+ body: { hostname: opts.domain, api_version: proj2.apiVersion, ...opts.tenant ? { tenant_name: opts.tenant } : {} },
2080
+ summary: `Add custom domain ${opts.domain} to ${proj2.projectName} v${proj2.apiVersion}`
2081
+ });
2082
+ spinner.succeed(`Registered ${opts.domain}. Add these DNS records, then run \`apiblaze domain status\`:`);
2083
+ if (opts.json) {
2084
+ console.log(JSON.stringify(out));
2085
+ return;
2086
+ }
2087
+ if (out?.cnameRecord) console.log(` ${import_chalk20.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
2088
+ if (out?.dcv) console.log(` ${import_chalk20.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
2089
+ if (out?.id) console.log(import_chalk20.default.dim(` domain id: ${out.id}`));
1839
2090
  } catch (err) {
1840
- printError(err);
2091
+ spinner.fail("Domain registration failed.");
2092
+ throw err;
2093
+ }
2094
+ }
2095
+ async function runDomainList(project, opts) {
2096
+ const { teamId } = await resolveTeam(opts.team);
2097
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
2098
+ const out = await admin({
2099
+ method: "GET",
2100
+ path: `/projects/${proj2.projectId}/cf-domains?api_version=${encodeURIComponent(proj2.apiVersion)}`,
2101
+ summary: `List custom domains for ${proj2.projectName} v${proj2.apiVersion}`
2102
+ });
2103
+ const domains = out?.domains ?? [];
2104
+ if (opts.json) {
2105
+ console.log(JSON.stringify(domains));
2106
+ return;
2107
+ }
2108
+ if (!domains.length) {
2109
+ console.log(import_chalk20.default.yellow("No custom domains."));
2110
+ return;
2111
+ }
2112
+ for (const d of domains) console.log(` ${import_chalk20.default.bold(d.hostname)} ${import_chalk20.default.dim(d.status ?? "")} ${import_chalk20.default.dim(d.id ?? "")}`);
2113
+ }
2114
+ async function runDomainStatus(project, opts) {
2115
+ if (!opts.id) {
2116
+ console.error(import_chalk20.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
1841
2117
  process.exit(1);
1842
2118
  }
1843
- });
1844
- program.command("logout").description("Sign out \u2014 remove stored credentials from this machine").action(async () => {
2119
+ const { teamId } = await resolveTeam(opts.team);
2120
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
2121
+ const out = await admin({
2122
+ method: "GET",
2123
+ path: `/projects/${proj2.projectId}/cf-domains/${encodeURIComponent(opts.id)}/status`,
2124
+ summary: `Check custom-domain status`
2125
+ });
2126
+ console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk20.default.bold(out?.hostname ?? opts.id)}: ${import_chalk20.default.cyan(out?.status ?? "unknown")}`);
2127
+ }
2128
+ async function runDomainRemove(project, opts) {
2129
+ if (!opts.id) {
2130
+ console.error(import_chalk20.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
2131
+ process.exit(1);
2132
+ }
2133
+ const { teamId } = await resolveTeam(opts.team);
2134
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
2135
+ const spinner = (0, import_ora7.default)("Removing domain...").start();
1845
2136
  try {
1846
- await runLogout();
2137
+ await admin({
2138
+ method: "DELETE",
2139
+ path: `/projects/${proj2.projectId}/cf-domains/${encodeURIComponent(opts.id)}`,
2140
+ summary: `Remove custom domain ${opts.id}`
2141
+ });
2142
+ spinner.succeed("Removed.");
1847
2143
  } catch (err) {
1848
- printError(err);
2144
+ spinner.fail("Remove failed.");
2145
+ throw err;
2146
+ }
2147
+ }
2148
+ async function runDomainSetBase(project, opts) {
2149
+ const env = opts.env ?? "prod";
2150
+ const { teamId } = await resolveTeam(opts.team);
2151
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
2152
+ const spinner = (0, import_ora7.default)("Setting base domain...").start();
2153
+ try {
2154
+ await admin({
2155
+ method: "PUT",
2156
+ path: `/projects/${proj2.projectId}/base-domain`,
2157
+ body: { api_version: proj2.apiVersion, environment: env },
2158
+ summary: `Point bare hostname \u2192 v${proj2.apiVersion} / ${env}`
2159
+ });
2160
+ spinner.succeed(`Bare hostname now serves ${proj2.projectName} v${proj2.apiVersion} (${env}).`);
2161
+ } catch (err) {
2162
+ spinner.fail("set-base failed.");
2163
+ throw err;
2164
+ }
2165
+ }
2166
+
2167
+ // src/commands/tenant.ts
2168
+ var import_chalk21 = __toESM(require("chalk"));
2169
+ var import_ora8 = __toESM(require("ora"));
2170
+ async function runTenantList(opts) {
2171
+ const { teamId, teamName } = await resolveTeam(opts.team);
2172
+ const out = await admin({
2173
+ method: "GET",
2174
+ path: `/teams/${encodeURIComponent(teamId)}/tenants?detail=1`,
2175
+ summary: `List tenants for team ${teamName ?? teamId}`
2176
+ });
2177
+ const tenants = out?.tenants ?? [];
2178
+ if (opts.json) {
2179
+ console.log(JSON.stringify(tenants));
2180
+ return;
2181
+ }
2182
+ if (!tenants.length) {
2183
+ console.log(import_chalk21.default.yellow("No tenants."));
2184
+ return;
2185
+ }
2186
+ for (const t of tenants) {
2187
+ const name = typeof t === "string" ? t : t.tenant_name;
2188
+ const display = typeof t === "string" ? "" : import_chalk21.default.dim(` ${t.display_name ?? ""}`);
2189
+ console.log(` ${import_chalk21.default.bold(name)}${display}`);
2190
+ }
2191
+ }
2192
+ async function runTenantCreate(opts) {
2193
+ if (!opts.name) {
2194
+ console.error(import_chalk21.default.red("--name (display name) is required."));
2195
+ process.exit(1);
2196
+ }
2197
+ const { teamId } = await resolveTeam(opts.team);
2198
+ const spinner = (0, import_ora8.default)("Creating tenant...").start();
2199
+ try {
2200
+ const out = await admin({
2201
+ method: "POST",
2202
+ path: `/teams/${encodeURIComponent(teamId)}/tenants`,
2203
+ body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
2204
+ summary: `Create tenant "${opts.name}"`
2205
+ });
2206
+ spinner.succeed(`Created tenant ${import_chalk21.default.bold(out?.tenant_name ?? opts.name)}.`);
2207
+ if (opts.json) console.log(JSON.stringify(out));
2208
+ } catch (err) {
2209
+ spinner.fail("Tenant create failed.");
2210
+ throw err;
2211
+ }
2212
+ }
2213
+ async function runTenantAttach(project, opts) {
2214
+ if (!opts.tenant) {
2215
+ console.error(import_chalk21.default.red("--tenant <slug> is required."));
2216
+ process.exit(1);
2217
+ }
2218
+ const { teamId } = await resolveTeam(opts.team);
2219
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
2220
+ const spinner = (0, import_ora8.default)("Attaching tenant...").start();
2221
+ try {
2222
+ const out = await admin({
2223
+ method: "POST",
2224
+ path: `/projects/${proj2.projectId}/${proj2.apiVersion}/tenants`,
2225
+ body: { tenant_name: opts.tenant, ...opts.authConfig ? { auth_config_id: opts.authConfig } : {} },
2226
+ summary: `Attach tenant ${opts.tenant} \u2192 ${proj2.projectName} v${proj2.apiVersion}`
2227
+ });
2228
+ spinner.succeed(`Attached ${opts.tenant} to ${proj2.projectName} v${proj2.apiVersion}.`);
2229
+ if (opts.json) console.log(JSON.stringify(out));
2230
+ } catch (err) {
2231
+ spinner.fail("Attach failed.");
2232
+ throw err;
2233
+ }
2234
+ }
2235
+ async function runTenantDelete(slug, opts) {
2236
+ const { teamId } = await resolveTeam(opts.team);
2237
+ if (!opts.yes && !opts.json) {
2238
+ const { default: inquirer2 } = await import("inquirer");
2239
+ const { confirm } = await inquirer2.prompt([
2240
+ { type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
2241
+ ]);
2242
+ if (!confirm) {
2243
+ console.log(import_chalk21.default.dim("Aborted."));
2244
+ return;
2245
+ }
2246
+ }
2247
+ const spinner = (0, import_ora8.default)("Deleting tenant...").start();
2248
+ try {
2249
+ await admin({
2250
+ method: "DELETE",
2251
+ path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(slug)}`,
2252
+ summary: `Delete tenant ${slug} (full cascade)`
2253
+ });
2254
+ spinner.succeed(`Deleted tenant ${slug}.`);
2255
+ } catch (err) {
2256
+ spinner.fail("Tenant delete failed.");
2257
+ throw err;
2258
+ }
2259
+ }
2260
+ async function runTenantCors(opts) {
2261
+ if (!opts.tenant) {
2262
+ console.error(import_chalk21.default.red("--tenant <slug> is required."));
2263
+ process.exit(1);
2264
+ }
2265
+ const { teamId } = await resolveTeam(opts.team);
2266
+ const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
2267
+ const cors = origins.length ? { allowed_origins: origins } : null;
2268
+ const spinner = (0, import_ora8.default)("Updating CORS...").start();
2269
+ try {
2270
+ await admin({
2271
+ method: "PUT",
2272
+ path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(opts.tenant)}/cors`,
2273
+ body: { cors },
2274
+ summary: `Set CORS for tenant ${opts.tenant} \u2192 ${origins.length ? origins.join(", ") : "(cleared)"}`
2275
+ });
2276
+ spinner.succeed(`CORS updated for ${opts.tenant}.`);
2277
+ } catch (err) {
2278
+ spinner.fail("CORS update failed.");
2279
+ throw err;
2280
+ }
2281
+ }
2282
+
2283
+ // src/commands/key.ts
2284
+ var import_chalk22 = __toESM(require("chalk"));
2285
+ var import_ora9 = __toESM(require("ora"));
2286
+ async function runKeyList(opts) {
2287
+ const { teamId, teamName } = await resolveTeam(opts.team);
2288
+ const out = await admin({
2289
+ method: "GET",
2290
+ path: `/teams/${encodeURIComponent(teamId)}/developer-keys`,
2291
+ summary: `List developer keys for team ${teamName ?? teamId}`
2292
+ });
2293
+ const keys = out?.keys ?? [];
2294
+ if (opts.json) {
2295
+ console.log(JSON.stringify(keys));
2296
+ return;
2297
+ }
2298
+ if (!keys.length) {
2299
+ console.log(import_chalk22.default.yellow("No developer keys."));
2300
+ return;
2301
+ }
2302
+ for (const k of keys) {
2303
+ console.log(` ${import_chalk22.default.bold(k.key_id ?? k.id)} ${import_chalk22.default.dim(k.description ?? "")} ${import_chalk22.default.dim(k.expires_at ?? "no expiry")}`);
2304
+ }
2305
+ }
2306
+ async function runKeyMint(opts) {
2307
+ const { teamId } = await resolveTeam(opts.team);
2308
+ const body = { role: "consumer-admin" };
2309
+ if (opts.desc) body.description = opts.desc;
2310
+ if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
2311
+ const spinner = (0, import_ora9.default)("Minting key...").start();
2312
+ try {
2313
+ const out = await admin({
2314
+ method: "POST",
2315
+ path: `/teams/${encodeURIComponent(teamId)}/developer-keys`,
2316
+ body,
2317
+ summary: `Mint a consumer-admin developer key`
2318
+ });
2319
+ spinner.succeed("Key minted.");
2320
+ if (opts.json) {
2321
+ console.log(JSON.stringify(out));
2322
+ return;
2323
+ }
2324
+ console.log(` ${import_chalk22.default.bold("key_id")}: ${out?.key_id}`);
2325
+ console.log(` ${import_chalk22.default.bold("key")}: ${import_chalk22.default.green(out?.key)} ${import_chalk22.default.dim("(shown once \u2014 store it now)")}`);
2326
+ if (out?.expires_at) console.log(` ${import_chalk22.default.dim("expires:")} ${out.expires_at}`);
2327
+ } catch (err) {
2328
+ spinner.fail("Mint failed.");
2329
+ throw err;
2330
+ }
2331
+ }
2332
+ async function runKeyRevoke(keyId, opts) {
2333
+ const { teamId } = await resolveTeam(opts.team);
2334
+ const spinner = (0, import_ora9.default)("Revoking key...").start();
2335
+ try {
2336
+ await admin({
2337
+ method: "DELETE",
2338
+ path: `/teams/${encodeURIComponent(teamId)}/developer-keys/${encodeURIComponent(keyId)}`,
2339
+ summary: `Revoke developer key ${keyId}`
2340
+ });
2341
+ spinner.succeed(`Revoked ${keyId}.`);
2342
+ } catch (err) {
2343
+ spinner.fail("Revoke failed.");
2344
+ throw err;
2345
+ }
2346
+ }
2347
+
2348
+ // src/commands/spec.ts
2349
+ var fs4 = __toESM(require("fs"));
2350
+ var import_chalk23 = __toESM(require("chalk"));
2351
+ var import_ora10 = __toESM(require("ora"));
2352
+ async function runSpecGet(project, opts) {
2353
+ const { teamId } = await resolveTeam(opts.team);
2354
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
2355
+ const out = await admin({
2356
+ method: "GET",
2357
+ path: `/projects/${proj2.projectId}/${proj2.apiVersion}/openapi`,
2358
+ summary: `Get OpenAPI spec for ${proj2.projectName} v${proj2.apiVersion}`
2359
+ });
2360
+ console.log(JSON.stringify(out, null, opts.json ? 0 : 2));
2361
+ }
2362
+ async function runSpecSet(project, opts) {
2363
+ if (!opts.file) {
2364
+ console.error(import_chalk23.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
2365
+ process.exit(1);
2366
+ }
2367
+ let specContent;
2368
+ try {
2369
+ specContent = fs4.readFileSync(opts.file, "utf-8");
2370
+ } catch {
2371
+ console.error(import_chalk23.default.red(`Cannot read file: ${opts.file}`));
1849
2372
  process.exit(1);
1850
2373
  }
2374
+ const { teamId } = await resolveTeam(opts.team);
2375
+ const proj2 = await resolveProject(teamId, project, opts.apiversion);
2376
+ const spinner = (0, import_ora10.default)("Uploading spec...").start();
2377
+ try {
2378
+ const out = await admin({
2379
+ method: "POST",
2380
+ path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`,
2381
+ body: { specContent },
2382
+ summary: `Set OpenAPI spec for ${proj2.projectName} v${proj2.apiVersion} from ${opts.file}`
2383
+ });
2384
+ spinner.succeed(`Spec updated for ${proj2.projectName} v${proj2.apiVersion}.`);
2385
+ if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
2386
+ } catch (err) {
2387
+ spinner.fail("Spec update failed.");
2388
+ throw err;
2389
+ }
2390
+ }
2391
+
2392
+ // src/commands/agent.ts
2393
+ var import_chalk24 = __toESM(require("chalk"));
2394
+ var import_ora11 = __toESM(require("ora"));
2395
+
2396
+ // src/lib/tools.ts
2397
+ async function proj(teamId, name, version2) {
2398
+ return resolveProject(teamId, name, version2);
2399
+ }
2400
+ var TOOLS = [
2401
+ {
2402
+ name: "list_projects",
2403
+ description: "List the proxies (projects) in the active team.",
2404
+ params: {},
2405
+ run: async (_a, { teamId }) => getProjects(teamId)
2406
+ },
2407
+ {
2408
+ name: "set_target",
2409
+ description: "Set a proxy's target URL. Use env to scope to one environment (e.g. prod).",
2410
+ params: { project: "project name or id", url: "target URL", env: "optional environment" },
2411
+ run: async (a, { teamId }) => {
2412
+ const p = await proj(teamId, a.project, a.apiversion);
2413
+ const body = a.env ? { environments: { [a.env]: { target: a.url } } } : { target_url: a.url };
2414
+ return admin({ method: "PATCH", path: `/${p.projectId}/${p.apiVersion}/config`, body, summary: `Set target for ${p.projectName}` });
2415
+ }
2416
+ },
2417
+ {
2418
+ name: "set_throttle",
2419
+ description: "Set per-proxy throttling. rate=req/sec, quota=req/period, period=daily|weekly|monthly.",
2420
+ params: { project: "project name or id", rate: "optional req/sec", quota: "optional req/period", period: "optional daily|weekly|monthly" },
2421
+ run: async (a, { teamId }) => {
2422
+ const p = await proj(teamId, a.project, a.apiversion);
2423
+ const throttling = {};
2424
+ if (a.rate != null) throttling.userRateLimit = Number(a.rate);
2425
+ if (a.quota != null) throttling.proxyQuota = Number(a.quota);
2426
+ if (a.period) throttling.quotaPeriod = a.period;
2427
+ return admin({ method: "PATCH", path: `/${p.projectId}/${p.apiVersion}/config`, body: { throttling }, summary: `Throttle ${p.projectName}` });
2428
+ }
2429
+ },
2430
+ {
2431
+ name: "rename_proxy",
2432
+ description: "Change a proxy's display name.",
2433
+ params: { project: "project name or id", display_name: "new display name" },
2434
+ run: async (a, { teamId }) => {
2435
+ const p = await proj(teamId, a.project, a.apiversion);
2436
+ return admin({ method: "PATCH", path: `/${p.projectId}/${p.apiVersion}/config`, body: { display_name: a.display_name }, summary: `Rename ${p.projectName}` });
2437
+ }
2438
+ },
2439
+ {
2440
+ name: "delete_proxy",
2441
+ description: "Delete a proxy (full cascade). Irreversible \u2014 confirm with the user first.",
2442
+ params: { project: "project name or id", version: "optional api version" },
2443
+ run: async (a, { teamId }) => {
2444
+ const p = await proj(teamId, a.project, a.version);
2445
+ return admin({ method: "DELETE", path: `/${p.projectId}/${p.apiVersion}`, summary: `Delete ${p.projectName} (cascade)` });
2446
+ }
2447
+ },
2448
+ {
2449
+ name: "list_tenants",
2450
+ description: "List tenants in the active team.",
2451
+ params: {},
2452
+ run: async (_a, { teamId }) => admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants?detail=1`, summary: "List tenants" })
2453
+ },
2454
+ {
2455
+ name: "create_tenant",
2456
+ description: "Create a tenant (consumer scope) in the active team.",
2457
+ params: { name: "display name", slug: "optional explicit slug" },
2458
+ run: async (a, { teamId }) => admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/tenants`, body: { display_name: a.name, ...a.slug ? { tenant_name: a.slug } : {} }, summary: `Create tenant ${a.name}` })
2459
+ },
2460
+ {
2461
+ name: "attach_tenant",
2462
+ description: "Attach a tenant to a proxy.",
2463
+ params: { project: "project name or id", tenant: "tenant slug" },
2464
+ run: async (a, { teamId }) => {
2465
+ const p = await proj(teamId, a.project, a.apiversion);
2466
+ return admin({ method: "POST", path: `/projects/${p.projectId}/${p.apiVersion}/tenants`, body: { tenant_name: a.tenant }, summary: `Attach ${a.tenant} \u2192 ${p.projectName}` });
2467
+ }
2468
+ },
2469
+ {
2470
+ name: "list_keys",
2471
+ description: "List control-plane developer keys for the active team.",
2472
+ params: {},
2473
+ run: async (_a, { teamId }) => admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/developer-keys`, summary: "List developer keys" })
2474
+ },
2475
+ {
2476
+ name: "mint_key",
2477
+ description: "Mint a consumer-admin developer key (secret returned once).",
2478
+ params: { desc: "optional description", expires_days: "optional expiry in days" },
2479
+ run: async (a, { teamId }) => {
2480
+ const body = { role: "consumer-admin" };
2481
+ if (a.desc) body.description = a.desc;
2482
+ if (a.expires_days) body.expires_in_seconds = Number(a.expires_days) * 86400;
2483
+ return admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/developer-keys`, body, summary: "Mint developer key" });
2484
+ }
2485
+ },
2486
+ {
2487
+ name: "list_domains",
2488
+ description: "List custom domains for a proxy.",
2489
+ params: { project: "project name or id" },
2490
+ run: async (a, { teamId }) => {
2491
+ const p = await proj(teamId, a.project, a.apiversion);
2492
+ return admin({ method: "GET", path: `/projects/${p.projectId}/cf-domains?api_version=${encodeURIComponent(p.apiVersion)}`, summary: `List domains for ${p.projectName}` });
2493
+ }
2494
+ },
2495
+ {
2496
+ name: "set_base_domain",
2497
+ description: "Point the bare hostname of a proxy at a (version, environment).",
2498
+ params: { project: "project name or id", env: "environment (default prod)" },
2499
+ run: async (a, { teamId }) => {
2500
+ const p = await proj(teamId, a.project, a.apiversion);
2501
+ return admin({ method: "PUT", path: `/projects/${p.projectId}/base-domain`, body: { api_version: p.apiVersion, environment: a.env ?? "prod" }, summary: `Base domain \u2192 ${p.projectName}` });
2502
+ }
2503
+ },
2504
+ {
2505
+ name: "get_spec",
2506
+ description: "Fetch the OpenAPI spec of a proxy.",
2507
+ params: { project: "project name or id" },
2508
+ run: async (a, { teamId }) => {
2509
+ const p = await proj(teamId, a.project, a.apiversion);
2510
+ return admin({ method: "GET", path: `/projects/${p.projectId}/${p.apiVersion}/openapi`, summary: `Get spec for ${p.projectName}` });
2511
+ }
2512
+ }
2513
+ ];
2514
+ function toolCatalogue() {
2515
+ return TOOLS.map(({ name, description, params }) => ({ name, description, params }));
2516
+ }
2517
+ function findTool(name) {
2518
+ return TOOLS.find((t) => t.name === name);
2519
+ }
2520
+
2521
+ // src/commands/agent.ts
2522
+ var DASHBOARD_BASE4 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
2523
+ var MAX_TOOL_STEPS = 6;
2524
+ async function callAgent(messages, teamId) {
2525
+ const token = getAccessToken();
2526
+ const res = await fetch(`${DASHBOARD_BASE4}/api/cli/agent`, {
2527
+ method: "POST",
2528
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
2529
+ body: JSON.stringify({ messages: messages.slice(-20), tools: toolCatalogue(), team_id: teamId })
2530
+ });
2531
+ let data = null;
2532
+ try {
2533
+ data = await res.json();
2534
+ } catch {
2535
+ }
2536
+ if (!res.ok) throw new ApiError(res.status, data?.error ?? res.statusText, data);
2537
+ return data;
2538
+ }
2539
+ function truncate(value, max = 1500) {
2540
+ let s;
2541
+ try {
2542
+ s = typeof value === "string" ? value : JSON.stringify(value);
2543
+ } catch {
2544
+ s = String(value);
2545
+ }
2546
+ if (s.length <= max) return s;
2547
+ return s.slice(0, max) + `\u2026 [truncated ${s.length - max} chars]`;
2548
+ }
2549
+ function printCost(llm) {
2550
+ const usd = llm.cost > 0 ? `$${llm.cost.toFixed(4)}` : "<$0.0001";
2551
+ console.log(import_chalk24.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk24.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
2552
+ }
2553
+ async function runAgent(opts) {
2554
+ requireAuth();
2555
+ const { teamId, teamName } = await resolveTeam(opts.team);
2556
+ const { default: inquirer2 } = await import("inquirer");
2557
+ console.log(import_chalk24.default.bold("APIblaze agent") + import_chalk24.default.dim(` \xB7 team ${teamName ?? teamId}`));
2558
+ console.log(import_chalk24.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
2559
+ const history = [];
2560
+ while (true) {
2561
+ const { input } = await inquirer2.prompt([{ type: "input", name: "input", message: import_chalk24.default.cyan("you") + " \u203A" }]);
2562
+ const text = (input ?? "").trim();
2563
+ if (!text) continue;
2564
+ if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
2565
+ history.push({ role: "user", content: text });
2566
+ for (let step = 0; step < MAX_TOOL_STEPS; step++) {
2567
+ const spinner = (0, import_ora11.default)({ text: "thinking...", color: "magenta" }).start();
2568
+ let resp;
2569
+ try {
2570
+ resp = await callAgent(history, teamId);
2571
+ spinner.stop();
2572
+ } catch (err) {
2573
+ spinner.stop();
2574
+ if (err instanceof ApiError && err.status === 402) {
2575
+ console.log(import_chalk24.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
2576
+ break;
2577
+ }
2578
+ throw err;
2579
+ }
2580
+ history.push({ role: "assistant", content: resp.raw });
2581
+ printCost(resp.llm);
2582
+ if (resp.reply) console.log(import_chalk24.default.green("agent") + " \u203A " + resp.reply);
2583
+ if (!resp.action) break;
2584
+ const tool = findTool(resp.action.tool);
2585
+ if (!tool) {
2586
+ history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
2587
+ continue;
2588
+ }
2589
+ const runSpinner = (0, import_ora11.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
2590
+ try {
2591
+ const result = await tool.run(resp.action.args, { teamId });
2592
+ runSpinner.succeed(`${tool.name} \u2713`);
2593
+ history.push({ role: "user", content: `TOOL_RESULT ${tool.name}: ${truncate(result)}` });
2594
+ } catch (err) {
2595
+ runSpinner.fail(`${tool.name} failed`);
2596
+ const msg = err instanceof Error ? err.message : String(err);
2597
+ history.push({ role: "user", content: `TOOL_RESULT ${tool.name}: error \u2014 ${truncate(msg, 400)}` });
2598
+ }
2599
+ if (step === MAX_TOOL_STEPS - 1) {
2600
+ console.log(import_chalk24.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
2601
+ }
2602
+ }
2603
+ }
2604
+ console.log(import_chalk24.default.dim("\nBye."));
2605
+ }
2606
+
2607
+ // src/index.ts
2608
+ var program = new import_commander.Command();
2609
+ program.name("apiblaze").description("APIblaze CLI \u2014 create & manage API proxies and run dev tunnels").version(version).option("-v, --verbose", "Print the exact series of API calls each command makes (curl-equivalent you could run yourself)");
2610
+ program.hook("preAction", () => {
2611
+ if (program.opts().verbose) setVerbose(true);
1851
2612
  });
1852
- program.command("whoami").description("Show the signed-in identity and active team").option("--json", "Output machine-readable JSON").action(async (opts) => {
2613
+ process.on("exit", () => renderTrace());
2614
+ function action(fn) {
2615
+ return async (...args) => {
2616
+ try {
2617
+ await fn(...args);
2618
+ } catch (err) {
2619
+ printError(err);
2620
+ process.exit(1);
2621
+ }
2622
+ };
2623
+ }
2624
+ program.command("login").description("Authenticate with APIblaze").action(async () => {
1853
2625
  try {
1854
- await runWhoami(opts);
2626
+ await runLogin();
1855
2627
  } catch (err) {
1856
2628
  printError(err);
1857
2629
  process.exit(1);
1858
2630
  }
1859
2631
  });
1860
- program.command("claim").description("Claim an anonymously-created proxy into one of your teams (requires login)").argument("[code]", "Claim code from `create` output / claim URL (prompted if omitted)").option("--team <id|name>", "Team to claim into (defaults to your active team)").option("--json", "Output machine-readable JSON (non-interactive)").action(async (code, opts) => {
2632
+ program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.apiblaze.com)").option("--target <url>", "Target URL to forward requests to").option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("--apiversion <version>", "API version to create (e.g. 2.0.0). Creating a new version of a proxy you own adds a version to the existing project.").option("--tenant <slug>", "Tenant slug (anonymous create; generated if omitted)").option("--product <slug>", "Product tag to group this project under in the portal (team-scoped; anonymous create). Defaults to your team's existing/placeholder tag.").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--json", "Output machine-readable JSON (non-interactive)").action(async (opts) => {
1861
2633
  try {
1862
- await runClaim(code, opts);
2634
+ await runCreate(opts);
1863
2635
  } catch (err) {
1864
2636
  printError(err);
1865
2637
  process.exit(1);
1866
2638
  }
1867
2639
  });
1868
- program.command("team").description("Switch the active team").argument("[team]", "Team name or id to switch to (omit to choose interactively)").action(async (team) => {
2640
+ var agent = program.command("agent").description("Chat with an assistant that builds and runs your APIs (billed per turn)").option("--team <id|name>", "Team to work in (defaults to your active team)").action(action((opts) => runAgent(opts)));
2641
+ agent.command("authz").description("Chat to design and turn on access rules for an API").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").action(action((project, apiVersion) => runAuthz(project, apiVersion)));
2642
+ 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)));
2643
+ 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)));
2644
+ program.command("dev").description("Put your localhost behind a public URL (dev tunnel)").argument("[port]", "Local port to tunnel (positional; overrides --port)").option("-p, --port <number>", "Local port to tunnel", "3000").option("-o, --capture-file <path>", "Stream full request/response traffic to a file (JSON lines)").action(async (port, opts) => {
1869
2645
  try {
1870
- await runTeam(team);
2646
+ const resolved = parseInt(port ?? opts.port, 10);
2647
+ if (Number.isNaN(resolved)) {
2648
+ console.error(import_chalk25.default.red(`Invalid port: ${port ?? opts.port}`));
2649
+ process.exit(1);
2650
+ }
2651
+ await runDev({ port: resolved, captureFile: opts.captureFile });
1871
2652
  } catch (err) {
1872
2653
  printError(err);
1873
2654
  process.exit(1);
1874
2655
  }
1875
2656
  });
1876
- program.command("projects").description("List the projects in your team").action(async () => {
2657
+ program.command("logout").description("Sign out \u2014 remove stored credentials from this machine").action(async () => {
1877
2658
  try {
1878
- await runProjects();
2659
+ await runLogout();
1879
2660
  } catch (err) {
1880
2661
  printError(err);
1881
2662
  process.exit(1);
1882
2663
  }
1883
2664
  });
1884
- program.command("authz").description("Design API authorization interactively (chat), then publish + enable it").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[apiVersion]", "API version (defaults to the project's version)").action(async (project, apiVersion) => {
2665
+ program.command("whoami").description("Show the signed-in identity and active team").option("--json", "Output machine-readable JSON").action(async (opts) => {
1885
2666
  try {
1886
- await runAuthz(project, apiVersion);
2667
+ await runWhoami(opts);
1887
2668
  } catch (err) {
1888
2669
  printError(err);
1889
2670
  process.exit(1);
1890
2671
  }
1891
2672
  });
1892
- program.command("openapi").description("Design your OpenAPI spec from captured traffic interactively (chat), then publish it").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[apiVersion]", "API version (defaults to the project's version)").action(async (project, apiVersion) => {
2673
+ program.command("claim").description("Claim an anonymously-created proxy into one of your teams (requires login)").argument("[code]", "Claim code from `create` output / claim URL (prompted if omitted)").option("--team <id|name>", "Team to claim into (defaults to your active team)").option("--json", "Output machine-readable JSON (non-interactive)").action(async (code, opts) => {
1893
2674
  try {
1894
- await runOpenapi(project, apiVersion);
2675
+ await runClaim(code, opts);
1895
2676
  } catch (err) {
1896
2677
  printError(err);
1897
2678
  process.exit(1);
1898
2679
  }
1899
2680
  });
1900
- program.command("mcp").description("Design an MCP server from the spec + traffic interactively (chat), then publish it").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[apiVersion]", "API version (defaults to the project's version)").option("--environment <env>", "Environment to publish (default: prod)").action(async (project, apiVersion, opts) => {
2681
+ program.command("team").description("Switch the active team").argument("[team]", "Team name or id to switch to (omit to choose interactively)").action(async (team) => {
1901
2682
  try {
1902
- await runMcp(project, apiVersion, opts);
2683
+ await runTeam(team);
1903
2684
  } catch (err) {
1904
2685
  printError(err);
1905
2686
  process.exit(1);
1906
2687
  }
1907
2688
  });
1908
- program.command("dev").description("Start a dev tunnel for your localhost projects").argument("[port]", "Local port to tunnel (positional; overrides --port)").option("-p, --port <number>", "Local port to tunnel", "3000").option("-o, --capture-file <path>", "Stream full request/response traffic to a file (JSON lines)").action(async (port, opts) => {
2689
+ program.command("projects").description("List the projects in your team").action(async () => {
1909
2690
  try {
1910
- const resolved = parseInt(port ?? opts.port, 10);
1911
- if (Number.isNaN(resolved)) {
1912
- console.error(import_chalk15.default.red(`Invalid port: ${port ?? opts.port}`));
1913
- process.exit(1);
1914
- }
1915
- await runDev({ port: resolved, captureFile: opts.captureFile });
2691
+ await runProjects();
1916
2692
  } catch (err) {
1917
2693
  printError(err);
1918
2694
  process.exit(1);
1919
2695
  }
1920
2696
  });
2697
+ program.command("delete").description("Delete a proxy and everything under it (asks first)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--team <id|name>", "Team the project is in (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").option("--json", "Output machine-readable JSON").action(action((project, version2, opts) => runDelete(project, version2, opts)));
2698
+ program.command("target").description("Change where a proxy forwards requests").argument("<project>", "Project name or id").requiredOption("--url <url>", "Target URL to forward to").option("--env <env>", "Environment to scope the target to (e.g. prod, dev)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version (defaults to the first match)").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTargetSet(project, opts)));
2699
+ program.command("throttle").description("Set rate limits and quotas for a proxy").argument("<project>", "Project name or id").option("--rate <n>", "User rate limit (requests/sec)").option("--end-user-rate <n>", "Per-end-user rate limit (requests/sec)").option("--quota <n>", "Proxy quota (requests/period)").option("--period <p>", "Quota period: daily | weekly | monthly").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runThrottleSet(project, opts)));
2700
+ program.command("rename").description("Change a proxy's display name").argument("<project>", "Project name or id").requiredOption("--display-name <name>", "New human-friendly display name").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runRename(project, opts)));
2701
+ var domain = program.command("domain").description("Use your own domain for a proxy");
2702
+ domain.command("add").description("Add your own domain (shows the DNS records to set)").argument("<project>", "Project name or id").requiredOption("--domain <host>", "Custom hostname to add").option("--tenant <slug>", "Tenant to scope the domain to").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainAdd(project, opts)));
2703
+ domain.command("list").description("List custom domains for a proxy").argument("<project>", "Project name or id").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainList(project, opts)));
2704
+ domain.command("status").description("Check a custom domain's validation status").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainStatus(project, opts)));
2705
+ domain.command("rm").description("Remove a custom domain").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainRemove(project, opts)));
2706
+ domain.command("set-base").description("Choose which version/environment your main URL serves").argument("<project>", "Project name or id").option("--env <env>", "Environment (default: prod)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainSetBase(project, opts)));
2707
+ var tenant = program.command("tenant").description("Manage tenants \u2014 separate groups of your API's users");
2708
+ tenant.command("list").description("List tenants in your team").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantList(opts)));
2709
+ tenant.command("create").description("Create a tenant in your team").requiredOption("--name <display>", "Display name").option("--slug <tenant_name>", "Explicit tenant slug (generated if omitted)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantCreate(opts)));
2710
+ 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("--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)));
2711
+ tenant.command("delete").description("Delete a tenant (full cascade)").argument("<slug>", "Tenant slug to delete").option("--team <id|name>", "Team (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").action(action((slug, opts) => runTenantDelete(slug, opts)));
2712
+ tenant.command("cors").description("Set the CORS allow-list for a tenant").requiredOption("--tenant <slug>", "Tenant slug").option("--origins <list>", 'Comma-separated origins (or "*"); empty clears').option("--team <id|name>", "Team (defaults to active team)").action(action((opts) => runTenantCors(opts)));
2713
+ var key = program.command("key").description("Create keys to manage your account from scripts or curl");
2714
+ key.command("list").description("List developer keys in your team").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runKeyList(opts)));
2715
+ key.command("mint").description("Mint a consumer-admin developer key (secret shown once)").option("--desc <text>", "Description").option("--expires-days <n>", "Expiry in days (default 90 server-side)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runKeyMint(opts)));
2716
+ key.command("revoke").description("Revoke a developer key").argument("<keyId>", "Key id (see `key list`)").option("--team <id|name>", "Team (defaults to active team)").action(action((keyId, opts) => runKeyRevoke(keyId, opts)));
2717
+ var spec = program.command("spec").description("View or update a proxy's OpenAPI spec (or build one by chatting: apiblaze agent openapi)");
2718
+ spec.command("get").description("Print the current OpenAPI document").argument("<project>", "Project name or id").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Compact JSON output").action(action((project, opts) => runSpecGet(project, opts)));
2719
+ spec.command("set").description("Replace the stored OpenAPI spec from a local file").argument("<project>", "Project name or id").requiredOption("--file <path>", "OpenAPI JSON or YAML file to upload").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runSpecSet(project, opts)));
1921
2720
  program.addHelpText(
1922
2721
  "after",
1923
2722
  `
1924
2723
  Examples:
1925
- # No account needed \u2014 create an anonymous proxy, get a claim URL:
1926
- $ npx apiblaze create --target https://api.example.com
2724
+ # Just talk to it \u2014 create, configure, inspect your APIs by chatting:
2725
+ $ npx apiblaze agent
1927
2726
 
1928
- # Non-interactive (CI / scripts):
1929
- $ npx apiblaze create --target https://api.example.com --name myapi --json
1930
-
1931
- # Sign in, then create under your team:
1932
- $ npx apiblaze login
1933
- $ npx apiblaze create --name myapi --target https://api.example.com --auth api_key
2727
+ # Make an API in one line (no account needed; prints a claim URL):
2728
+ $ npx apiblaze create --target https://api.example.com
1934
2729
 
1935
- # Dev tunnel \u2014 auto-creates a proxy if none point here, and captures
1936
- # traffic (full headers + body, secrets masked) until your server is up:
2730
+ # Run your localhost through a public URL:
1937
2731
  $ npx apiblaze dev 3000
1938
- $ npx apiblaze dev 3000 --capture-file traffic.jsonl
1939
2732
 
1940
- # Manage:
1941
- $ npx apiblaze whoami
1942
- $ npx apiblaze projects
1943
- $ npx apiblaze team
1944
- $ npx apiblaze logout
2733
+ # Or drive it yourself (add --verbose to see the equivalent API calls):
2734
+ $ npx apiblaze target myapi --url https://api.example.com
2735
+ $ npx apiblaze throttle myapi --rate 50
2736
+ $ npx apiblaze domain add myapi --domain api.mysite.com
2737
+ $ npx apiblaze delete myapi --verbose
1945
2738
  `
1946
2739
  );
1947
2740
  function printError(err) {
1948
2741
  if (err instanceof ApiError) {
1949
- console.error(import_chalk15.default.red(`
2742
+ console.error(import_chalk25.default.red(`
1950
2743
  API error (${err.status}): ${err.message}`));
1951
2744
  } else if (err instanceof Error) {
1952
- console.error(import_chalk15.default.red(`
2745
+ console.error(import_chalk25.default.red(`
1953
2746
  Error: ${err.message}`));
1954
2747
  } else {
1955
- console.error(import_chalk15.default.red("\nUnknown error"));
2748
+ console.error(import_chalk25.default.red("\nUnknown error"));
1956
2749
  }
1957
2750
  }
1958
2751
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.3.8",
3
+ "version": "0.4.1",
4
4
  "description": "Dev tunnel CLI for APIblaze — route localhost projects through your APIblaze endpoints",
5
5
  "keywords": [
6
6
  "apiblaze",