apiblaze 0.1.22 → 0.3.0

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 +33 -13
  2. package/dist/index.js +329 -6
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -11,7 +11,7 @@ npm install -g apiblaze
11
11
  Or run without installing:
12
12
 
13
13
  ```bash
14
- npx apiblaze dev
14
+ npx apiblaze --help
15
15
  ```
16
16
 
17
17
  ## Requirements
@@ -21,14 +21,29 @@ npx apiblaze dev
21
21
  ## Quick start
22
22
 
23
23
  ```bash
24
- # 1. Log in to your APIblaze account
25
- apiblaze login
24
+ # Create a proxy (no account needed)
25
+ npx apiblaze create --target https://api.example.com
26
+
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
26
35
 
27
- # 2. Start tunneling (default port 3000)
28
- apiblaze dev
36
+ # Or specify a port
37
+ npx apiblaze dev 3000
29
38
 
30
- # Or specify a different port
31
- apiblaze dev --port 8080
39
+ ```
40
+
41
+ ## Help
42
+
43
+ ```bash
44
+ apiblaze --help
45
+ apiblaze help create
46
+ apiblaze help dev
32
47
  ```
33
48
 
34
49
  ## Commands
@@ -36,20 +51,25 @@ apiblaze dev --port 8080
36
51
  | Command | Description |
37
52
  |---|---|
38
53
  | `apiblaze login` | Authenticate with your APIblaze account |
39
- | `apiblaze dev [--port <n>]` | Tunnel localhost projects through Cloudflare |
54
+ | `apiblaze create [options]` | Create a new API proxy (anonymous if not logged in) |
55
+ | `apiblaze claim [code]` | Claim an anonymously-created proxy into your team |
56
+ | `apiblaze projects` | List your team projects |
57
+ | `apiblaze dev [port]` | Start a dev tunnel for your localhost projects |
58
+ | `apiblaze team [team]` | Switch the active team |
59
+ | `apiblaze whoami` | Show the signed-in identity and active team |
60
+ | `apiblaze logout` | Sign out and remove stored credentials |
40
61
 
41
62
  ## How it works
42
63
 
43
64
  `apiblaze dev` automatically:
44
65
 
45
- 1. Finds all your APIblaze projects that target `localhost`
46
- 2. Downloads and starts a [Cloudflare Quick Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/) (no account required)
47
- 3. Registers the tunnel URL with APIblaze so incoming API requests are forwarded to your machine
48
- 4. Streams live traffic logs to your terminal in real-time
66
+ 1. Fetches your APIblaze projects that target `localhost` (or other internal targets)
67
+ 2. Registers a temporary dev tunnel with APIblaze
68
+ 3. Opens a secure connection and forwards incoming requests to your local server
69
+ 4. Streams live traffic logs to your terminal in real time
49
70
 
50
71
  On Ctrl+C the tunnel is cleanly deregistered.
51
72
 
52
73
  ## License
53
74
 
54
75
  MIT
55
- # npm-apiblaze
package/dist/index.js CHANGED
@@ -93,6 +93,7 @@ var init_auth = __esm({
93
93
  // src/lib/api.ts
94
94
  var api_exports = {};
95
95
  __export(api_exports, {
96
+ agentCall: () => agentCall,
96
97
  checkProxyName: () => checkProxyName,
97
98
  claimProxy: () => claimProxy,
98
99
  createProxy: () => createProxy,
@@ -147,6 +148,20 @@ async function apiFetch(path2, options = {}) {
147
148
  }
148
149
  return res.json();
149
150
  }
151
+ async function agentCall(path2, method, body) {
152
+ const token = getAccessToken();
153
+ const res = await fetch(`${DASHBOARD_BASE}/api/cli/agents`, {
154
+ method: "POST",
155
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
156
+ body: JSON.stringify({ path: path2, method, body })
157
+ });
158
+ let data = null;
159
+ try {
160
+ data = await res.json();
161
+ } catch {
162
+ }
163
+ return { status: res.status, data };
164
+ }
150
165
  async function getTeams() {
151
166
  const res = await apiFetch("/api/cli/teams");
152
167
  const raw = Array.isArray(res) ? res : res?.teams ?? res?.data ?? [];
@@ -203,10 +218,10 @@ var init_api = __esm({
203
218
 
204
219
  // src/index.ts
205
220
  var import_commander = require("commander");
206
- var import_chalk10 = __toESM(require("chalk"));
221
+ var import_chalk14 = __toESM(require("chalk"));
207
222
 
208
223
  // package.json
209
- var version = "0.1.22";
224
+ var version = "0.3.0";
210
225
 
211
226
  // src/index.ts
212
227
  init_types();
@@ -1200,6 +1215,290 @@ async function runTeam(arg) {
1200
1215
  \u2714 Active team: ${import_chalk9.default.bold(chosen.name)}`));
1201
1216
  }
1202
1217
 
1218
+ // src/commands/authz.ts
1219
+ var import_chalk11 = __toESM(require("chalk"));
1220
+ init_auth();
1221
+ init_api();
1222
+
1223
+ // src/lib/agent-chat.ts
1224
+ var import_readline = __toESM(require("readline"));
1225
+ var import_chalk10 = __toESM(require("chalk"));
1226
+ init_api();
1227
+ async function runAgentChatRepl(opts) {
1228
+ const { title, subtitle, endpoint, buildBody, seedPrompt, summarizeProposal, commands } = opts;
1229
+ console.log("\n" + import_chalk10.default.cyan.bold(title));
1230
+ if (subtitle) console.log(import_chalk10.default.dim(subtitle));
1231
+ const messages = [];
1232
+ const ctx = { proposal: null, lastData: null, log: (s) => console.log(s) };
1233
+ const cmdByName = new Map(commands.map((c) => [c.name, c]));
1234
+ const footer = () => {
1235
+ const parts = [
1236
+ import_chalk10.default.dim("type to chat/refine"),
1237
+ ...commands.map((c) => import_chalk10.default.dim(`/${c.name} ${c.describe}`)),
1238
+ import_chalk10.default.dim("/show"),
1239
+ import_chalk10.default.dim("/drop discard & exit")
1240
+ ];
1241
+ return " " + import_chalk10.default.dim("[ ") + parts.join(import_chalk10.default.dim(" \xB7 ")) + import_chalk10.default.dim(" ]");
1242
+ };
1243
+ async function turn(content) {
1244
+ messages.push({ role: "user", content });
1245
+ process.stdout.write(import_chalk10.default.dim(" \u2026thinking\n"));
1246
+ const { status, data } = await agentCall(endpoint, "POST", { messages, ...buildBody(messages) });
1247
+ if (status === 402) {
1248
+ console.log(import_chalk10.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
1249
+ messages.pop();
1250
+ return;
1251
+ }
1252
+ if (status >= 400 || !data) {
1253
+ console.log(import_chalk10.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
1254
+ `));
1255
+ messages.pop();
1256
+ return;
1257
+ }
1258
+ ctx.lastData = data;
1259
+ const reply = data.reply ?? data.message ?? "(no reply)";
1260
+ messages.push({ role: "assistant", content: reply });
1261
+ console.log("\n" + import_chalk10.default.cyan("agent \u203A ") + reply + "\n");
1262
+ if (data.proposal) {
1263
+ ctx.proposal = data.proposal;
1264
+ const summary = summarizeProposal?.(data);
1265
+ if (summary) console.log(import_chalk10.default.yellow(" " + summary));
1266
+ const ready = commands.filter((c) => c.needsProposal).map((c) => `/${c.name}`).join(" or ");
1267
+ if (ready) console.log(import_chalk10.default.dim(` Ready \u2014 ${ready} to make it official, or keep refining.`));
1268
+ console.log();
1269
+ }
1270
+ const llm = data.llm;
1271
+ if (llm) {
1272
+ const credits = data.credits_remaining;
1273
+ const left = typeof credits === "number" ? ` \xB7 $${(credits / 100).toFixed(2)} credit left` : "";
1274
+ console.log(import_chalk10.default.dim(` ~$${(llm.cost_estimate ?? 0).toFixed(4)} \xB7 ${llm.model ?? "?"}${left}`));
1275
+ }
1276
+ }
1277
+ const rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout });
1278
+ const ask = (q) => new Promise((res) => rl.question(q, res));
1279
+ await turn(seedPrompt);
1280
+ for (; ; ) {
1281
+ console.log(footer());
1282
+ const line = (await ask(import_chalk10.default.green("you \u203A "))).trim();
1283
+ if (!line) continue;
1284
+ if (line === "/drop" || line === "/exit" || line === "/quit") {
1285
+ console.log(import_chalk10.default.dim(" Dropped \u2014 nothing applied.\n"));
1286
+ break;
1287
+ }
1288
+ if (line === "/show") {
1289
+ if (!ctx.proposal) console.log(import_chalk10.default.dim(" No proposal yet \u2014 keep chatting until the agent proposes one.\n"));
1290
+ else console.log("\n" + JSON.stringify(ctx.proposal, null, 2) + "\n");
1291
+ continue;
1292
+ }
1293
+ if (line.startsWith("/")) {
1294
+ const cmd = cmdByName.get(line.slice(1));
1295
+ if (!cmd) {
1296
+ console.log(import_chalk10.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
1297
+ `));
1298
+ continue;
1299
+ }
1300
+ if (cmd.needsProposal && !ctx.proposal) {
1301
+ console.log(import_chalk10.default.yellow(" No proposal yet \u2014 keep chatting until the agent generates one, then try again.\n"));
1302
+ continue;
1303
+ }
1304
+ await cmd.run(ctx);
1305
+ continue;
1306
+ }
1307
+ await turn(line);
1308
+ }
1309
+ rl.close();
1310
+ }
1311
+
1312
+ // src/commands/authz.ts
1313
+ async function runAuthz(projectArg, apiVersionArg) {
1314
+ const creds = loadCredentials();
1315
+ if (!creds) throw new Error("Not authenticated. Run `apiblaze login` first.");
1316
+ const teamId = creds.teamId ?? "";
1317
+ const projects = await getProjects(teamId);
1318
+ const match = projects.find((p) => p.projectId === projectArg || p.projectName === projectArg);
1319
+ if (!match) {
1320
+ throw new Error(`Project "${projectArg}" not found in your active team (${creds.teamName ?? teamId}). Run \`apiblaze projects\`.`);
1321
+ }
1322
+ const projectId = match.projectId;
1323
+ const apiVersion = apiVersionArg || match.apiVersion;
1324
+ const tenant = match.tenant || projectId;
1325
+ async function apply(ctx, enable) {
1326
+ const proposal = ctx.proposal;
1327
+ if (!enable) {
1328
+ const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant)}`, "POST", proposal.model);
1329
+ if (m.status === 404) {
1330
+ ctx.log(import_chalk11.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"));
1331
+ return;
1332
+ }
1333
+ if (m.status >= 400) {
1334
+ ctx.log(import_chalk11.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
1335
+ `));
1336
+ return;
1337
+ }
1338
+ }
1339
+ let ok = 0;
1340
+ let fail4 = 0;
1341
+ for (const r of proposal.routes) {
1342
+ const resource = "/" + String(r.resource || "").replace(/^\/+/, "");
1343
+ const policiesBody = {
1344
+ rule_mode: r.rule_mode === "list" ? "list" : "check-write",
1345
+ on_request_read: Array.isArray(r.on_request_read) ? r.on_request_read : [],
1346
+ post_response_write: Array.isArray(r.post_response_write) ? r.post_response_write : [],
1347
+ list_objects_read: r.list_objects_read ?? null,
1348
+ authentication_config: { require_authentication: true },
1349
+ authorization_enabled: enable
1350
+ };
1351
+ const encoded = resource.replace(/\{/g, "%7B").replace(/\}/g, "%7D");
1352
+ const putPath = `/projects/${projectId}/${apiVersion}/policies/route/${r.method.toUpperCase()}${encoded}`;
1353
+ let res = await agentCall(putPath, "PUT", { ...policiesBody, resource });
1354
+ if (res.status === 404) {
1355
+ res = await agentCall(`/projects/${projectId}/${apiVersion}/policies/route`, "POST", { method: r.method.toUpperCase(), resource, ...policiesBody });
1356
+ }
1357
+ if (res.status >= 200 && res.status < 300) ok++;
1358
+ else fail4++;
1359
+ }
1360
+ if (enable) {
1361
+ const e = await agentCall(`/${projectId}/${apiVersion}/config`, "PATCH", { authorization: { enforce_authorization: true }, tenant });
1362
+ if (e.status >= 400) {
1363
+ ctx.log(import_chalk11.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
1364
+ `));
1365
+ return;
1366
+ }
1367
+ ctx.log(import_chalk11.default.green(` \u2713 Enforcement ON \u2014 ${ok} route(s) + project switch on${fail4 ? `, ${fail4} failed` : ""}.
1368
+ `));
1369
+ } else {
1370
+ ctx.log(import_chalk11.default.green(` \u2713 Published (shadow): model + ${ok} route(s)${fail4 ? `, ${fail4} failed` : ""}. Review, then /enable.
1371
+ `));
1372
+ }
1373
+ }
1374
+ await runAgentChatRepl({
1375
+ title: `Authorization assistant \u2014 ${projectId} ${apiVersion}`,
1376
+ subtitle: `tenant ${tenant} \xB7 discuss what authorization fits this API, then make it official.`,
1377
+ endpoint: `/projects/${projectId}/${apiVersion}/authz/chat`,
1378
+ buildBody: () => ({ included_sample_ids: [], existing_model: null, existing_routes: [] }),
1379
+ 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.",
1380
+ summarizeProposal: (data) => {
1381
+ const proposal = data.proposal;
1382
+ const sim = data.simulation;
1383
+ const cov = sim ? `, ${sim.routes_covered ?? 0} covered by rules` : "";
1384
+ const warn = sim?.blocked_without_backfill ? ` \u26A0 ${sim.blocked_without_backfill} currently-OK request(s) would be denied until tuples exist.` : "";
1385
+ return `Proposal ready: ${proposal.routes.length} route(s)${cov}.${warn}`;
1386
+ },
1387
+ commands: [
1388
+ { name: "publish", describe: "save in shadow mode", needsProposal: true, run: (ctx) => apply(ctx, false) },
1389
+ { name: "enable", describe: "turn on enforcement", needsProposal: true, run: (ctx) => apply(ctx, true) }
1390
+ ]
1391
+ });
1392
+ }
1393
+
1394
+ // src/commands/openapi.ts
1395
+ var import_chalk12 = __toESM(require("chalk"));
1396
+ init_auth();
1397
+ init_api();
1398
+ async function runOpenapi(projectArg, apiVersionArg) {
1399
+ const creds = loadCredentials();
1400
+ if (!creds) throw new Error("Not authenticated. Run `apiblaze login` first.");
1401
+ const projects = await getProjects(creds.teamId ?? "");
1402
+ const match = projects.find((p) => p.projectId === projectArg || p.projectName === projectArg);
1403
+ if (!match) throw new Error(`Project "${projectArg}" not found in your active team. Run \`apiblaze projects\`.`);
1404
+ const projectId = match.projectId;
1405
+ const apiVersion = apiVersionArg || match.apiVersion;
1406
+ console.log(import_chalk12.default.dim("Gathering captured traffic samples\u2026"));
1407
+ const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
1408
+ if (routesRes.status >= 400) {
1409
+ console.log(import_chalk12.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
1410
+ return;
1411
+ }
1412
+ const routes = routesRes.data?.routes ?? (Array.isArray(routesRes.data) ? routesRes.data : []);
1413
+ const hashes = routes.map((r) => r.route_hash).filter((h) => !!h);
1414
+ const ids = /* @__PURE__ */ new Set();
1415
+ for (const h of hashes) {
1416
+ const s = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes/${encodeURIComponent(h)}/samples`, "GET");
1417
+ const arr = s.data?.samples ?? [];
1418
+ for (const x of arr) if (x.sample_id) ids.add(x.sample_id);
1419
+ }
1420
+ const sampleIds = [...ids];
1421
+ if (sampleIds.length === 0) {
1422
+ console.log(import_chalk12.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
1423
+ return;
1424
+ }
1425
+ async function publish(ctx) {
1426
+ const patch = ctx.proposal.patch;
1427
+ if (!Array.isArray(patch) || patch.length === 0) {
1428
+ ctx.log(import_chalk12.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
1429
+ return;
1430
+ }
1431
+ const specSource = ctx.lastData?.spec_source ?? "unknown";
1432
+ const endpoint = specSource === "github" ? "open-pr" : "publish-openapi";
1433
+ const pub = await agentCall(`/projects/${projectId}/${apiVersion}/samples/${endpoint}`, "POST", { patch, sample_ids_used: sampleIds });
1434
+ if (pub.status >= 400) {
1435
+ ctx.log(import_chalk12.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
1436
+ `));
1437
+ return;
1438
+ }
1439
+ const prUrl = pub.data?.pr_url;
1440
+ ctx.log(import_chalk12.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
1441
+ ` : " \u2713 Published updated OpenAPI spec.\n"));
1442
+ }
1443
+ await runAgentChatRepl({
1444
+ title: `API-docs assistant \u2014 ${projectId} ${apiVersion}`,
1445
+ subtitle: `${sampleIds.length} captured sample(s) \xB7 discuss what to document, then make it official.`,
1446
+ endpoint: `/projects/${projectId}/${apiVersion}/openapi/chat`,
1447
+ buildBody: () => ({ included_sample_ids: sampleIds }),
1448
+ seedPrompt: "Compare the captured samples against the current spec and tell me what's missing or wrong. List the changes you'd make \u2014 don't produce a patch yet.",
1449
+ summarizeProposal: (data) => {
1450
+ const patch = data.proposal.patch;
1451
+ const n = Array.isArray(patch) ? patch.length : 0;
1452
+ return n === 0 ? "The spec already covers the observed traffic \u2014 nothing to publish." : `Patch ready: ${n} additive change(s) to the OpenAPI spec.`;
1453
+ },
1454
+ commands: [
1455
+ { name: "publish", describe: "publish / open PR", needsProposal: true, run: publish }
1456
+ ]
1457
+ });
1458
+ }
1459
+
1460
+ // src/commands/mcp.ts
1461
+ var import_chalk13 = __toESM(require("chalk"));
1462
+ init_auth();
1463
+ init_api();
1464
+ async function runMcp(projectArg, apiVersionArg, opts) {
1465
+ const creds = loadCredentials();
1466
+ if (!creds) throw new Error("Not authenticated. Run `apiblaze login` first.");
1467
+ const projects = await getProjects(creds.teamId ?? "");
1468
+ const match = projects.find((p) => p.projectId === projectArg || p.projectName === projectArg);
1469
+ if (!match) throw new Error(`Project "${projectArg}" not found in your active team. Run \`apiblaze projects\`.`);
1470
+ const projectId = match.projectId;
1471
+ const apiVersion = apiVersionArg || match.apiVersion;
1472
+ const environment = opts.environment || "prod";
1473
+ async function publish(ctx) {
1474
+ const spec = ctx.proposal;
1475
+ const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec });
1476
+ if (pub.status >= 400) {
1477
+ ctx.log(import_chalk13.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
1478
+ `));
1479
+ return;
1480
+ }
1481
+ ctx.log(import_chalk13.default.green(` \u2713 Published MCP server \u2014 ${projectId}.mcp.apiblaze.com/${apiVersion}/${environment}.
1482
+ `));
1483
+ }
1484
+ await runAgentChatRepl({
1485
+ title: `MCP builder \u2014 ${projectId} ${apiVersion} (${environment})`,
1486
+ subtitle: "discuss which routes to expose as tools, then make the catalogue official.",
1487
+ endpoint: `/projects/${projectId}/${apiVersion}/mcp/chat`,
1488
+ buildBody: () => ({ environment, included_sample_ids: [] }),
1489
+ 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.",
1490
+ summarizeProposal: (data) => {
1491
+ const spec = data.proposal;
1492
+ const tools = Array.isArray(spec.tools) ? spec.tools : [];
1493
+ const names = tools.slice(0, 12).map((t) => t.name ?? "(unnamed)").join(", ");
1494
+ return `Catalogue ready: ${tools.length} tool(s)${names ? ` \u2014 ${names}${tools.length > 12 ? ", \u2026" : ""}` : ""}.`;
1495
+ },
1496
+ commands: [
1497
+ { name: "publish", describe: `to ${projectId}.mcp.apiblaze.com`, needsProposal: true, run: publish }
1498
+ ]
1499
+ });
1500
+ }
1501
+
1203
1502
  // src/index.ts
1204
1503
  var program = new import_commander.Command();
1205
1504
  program.name("apiblaze").description("APIblaze CLI \u2014 create & manage API proxies and run dev tunnels").version(version);
@@ -1259,11 +1558,35 @@ program.command("projects").description("List the projects in your team").action
1259
1558
  process.exit(1);
1260
1559
  }
1261
1560
  });
1561
+ 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) => {
1562
+ try {
1563
+ await runAuthz(project, apiVersion);
1564
+ } catch (err) {
1565
+ printError(err);
1566
+ process.exit(1);
1567
+ }
1568
+ });
1569
+ 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) => {
1570
+ try {
1571
+ await runOpenapi(project, apiVersion);
1572
+ } catch (err) {
1573
+ printError(err);
1574
+ process.exit(1);
1575
+ }
1576
+ });
1577
+ 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) => {
1578
+ try {
1579
+ await runMcp(project, apiVersion, opts);
1580
+ } catch (err) {
1581
+ printError(err);
1582
+ process.exit(1);
1583
+ }
1584
+ });
1262
1585
  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").action(async (port, opts) => {
1263
1586
  try {
1264
1587
  const resolved = parseInt(port ?? opts.port, 10);
1265
1588
  if (Number.isNaN(resolved)) {
1266
- console.error(import_chalk10.default.red(`Invalid port: ${port ?? opts.port}`));
1589
+ console.error(import_chalk14.default.red(`Invalid port: ${port ?? opts.port}`));
1267
1590
  process.exit(1);
1268
1591
  }
1269
1592
  await runDev({ port: resolved });
@@ -1296,13 +1619,13 @@ Examples:
1296
1619
  );
1297
1620
  function printError(err) {
1298
1621
  if (err instanceof ApiError) {
1299
- console.error(import_chalk10.default.red(`
1622
+ console.error(import_chalk14.default.red(`
1300
1623
  API error (${err.status}): ${err.message}`));
1301
1624
  } else if (err instanceof Error) {
1302
- console.error(import_chalk10.default.red(`
1625
+ console.error(import_chalk14.default.red(`
1303
1626
  Error: ${err.message}`));
1304
1627
  } else {
1305
- console.error(import_chalk10.default.red("\nUnknown error"));
1628
+ console.error(import_chalk14.default.red("\nUnknown error"));
1306
1629
  }
1307
1630
  }
1308
1631
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.1.22",
3
+ "version": "0.3.0",
4
4
  "description": "Dev tunnel CLI for APIblaze — route localhost projects through your APIblaze endpoints",
5
5
  "keywords": [
6
6
  "apiblaze",
@@ -27,7 +27,7 @@
27
27
  "build:watch": "tsup --watch",
28
28
  "typecheck": "tsc --noEmit",
29
29
  "prepublishOnly": "npm run build && npm run typecheck",
30
- "release": "bash scripts/release.sh"
30
+ "release": "bash release.sh"
31
31
  },
32
32
  "dependencies": {
33
33
  "chalk": "^4.1.2",