apiblaze 0.3.0 → 0.3.2

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 +14 -0
  2. package/dist/index.js +600 -320
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,13 +6,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __esm = (fn, res) => function __init() {
10
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
- };
12
- var __export = (target, all) => {
13
- for (var name in all)
14
- __defProp(target, name, { get: all[name], enumerable: true });
15
- };
16
9
  var __copyProps = (to, from, except, desc) => {
17
10
  if (from && typeof from === "object" || typeof from === "function") {
18
11
  for (let key of __getOwnPropNames(from))
@@ -30,23 +23,33 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
23
  mod
31
24
  ));
32
25
 
26
+ // src/index.ts
27
+ var import_commander = require("commander");
28
+ var import_chalk15 = __toESM(require("chalk"));
29
+
30
+ // package.json
31
+ var version = "0.3.2";
32
+
33
33
  // src/types.ts
34
- var ApiError;
35
- var init_types = __esm({
36
- "src/types.ts"() {
37
- "use strict";
38
- ApiError = class extends Error {
39
- constructor(status, message, body) {
40
- super(message);
41
- this.status = status;
42
- this.body = body;
43
- this.name = "ApiError";
44
- }
45
- };
34
+ var ApiError = class extends Error {
35
+ constructor(status, message, body) {
36
+ super(message);
37
+ this.status = status;
38
+ this.body = body;
39
+ this.name = "ApiError";
46
40
  }
47
- });
41
+ };
42
+
43
+ // src/commands/login.ts
44
+ var import_chalk2 = __toESM(require("chalk"));
45
+ var import_ora = __toESM(require("ora"));
48
46
 
49
47
  // src/lib/auth.ts
48
+ var fs = __toESM(require("fs"));
49
+ var os = __toESM(require("os"));
50
+ var path = __toESM(require("path"));
51
+ var APIBLAZE_DIR = path.join(os.homedir(), ".apiblaze");
52
+ var CREDENTIALS_PATH = path.join(APIBLAZE_DIR, "credentials.json");
50
53
  function saveCredentials(creds) {
51
54
  fs.mkdirSync(APIBLAZE_DIR, { recursive: true });
52
55
  fs.writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), "utf-8");
@@ -78,32 +81,13 @@ function getAccessToken() {
78
81
  }
79
82
  return creds.accessToken;
80
83
  }
81
- var fs, os, path, APIBLAZE_DIR, CREDENTIALS_PATH;
82
- var init_auth = __esm({
83
- "src/lib/auth.ts"() {
84
- "use strict";
85
- fs = __toESM(require("fs"));
86
- os = __toESM(require("os"));
87
- path = __toESM(require("path"));
88
- APIBLAZE_DIR = path.join(os.homedir(), ".apiblaze");
89
- CREDENTIALS_PATH = path.join(APIBLAZE_DIR, "credentials.json");
90
- }
91
- });
84
+
85
+ // src/lib/team.ts
86
+ var import_chalk = __toESM(require("chalk"));
92
87
 
93
88
  // src/lib/api.ts
94
- var api_exports = {};
95
- __export(api_exports, {
96
- agentCall: () => agentCall,
97
- checkProxyName: () => checkProxyName,
98
- claimProxy: () => claimProxy,
99
- createProxy: () => createProxy,
100
- createProxyAnonymous: () => createProxyAnonymous,
101
- deleteDevTunnel: () => deleteDevTunnel,
102
- getLocalhostTargets: () => getLocalhostTargets,
103
- getProjects: () => getProjects,
104
- getTeams: () => getTeams,
105
- putDevTunnel: () => putDevTunnel
106
- });
89
+ var DASHBOARD_BASE = "https://dashboard.apiblaze.com";
90
+ var PUBLIC_API_BASE = "https://api.apiblaze.com";
107
91
  async function createProxyAnonymous(body) {
108
92
  const res = await fetch(`${PUBLIC_API_BASE}/proxy`, {
109
93
  method: "POST",
@@ -205,32 +189,41 @@ async function deleteDevTunnel(restore) {
205
189
  body: JSON.stringify({ restore })
206
190
  });
207
191
  }
208
- var DASHBOARD_BASE, PUBLIC_API_BASE;
209
- var init_api = __esm({
210
- "src/lib/api.ts"() {
211
- "use strict";
212
- init_auth();
213
- init_types();
214
- DASHBOARD_BASE = "https://dashboard.apiblaze.com";
215
- PUBLIC_API_BASE = "https://api.apiblaze.com";
216
- }
217
- });
218
192
 
219
- // src/index.ts
220
- var import_commander = require("commander");
221
- var import_chalk14 = __toESM(require("chalk"));
222
-
223
- // package.json
224
- var version = "0.3.0";
225
-
226
- // src/index.ts
227
- init_types();
193
+ // src/lib/team.ts
194
+ async function resolveLinkedTeam(opts) {
195
+ let teams;
196
+ try {
197
+ teams = await getTeams();
198
+ } catch {
199
+ return opts.preferredId ? { teamId: opts.preferredId } : null;
200
+ }
201
+ if (teams.length === 0) return null;
202
+ const preferred = opts.preferredId ? teams.find((t) => t.teamId === opts.preferredId) : void 0;
203
+ if (preferred) return { teamId: preferred.teamId, teamName: preferred.name };
204
+ if (opts.preferredId) {
205
+ console.log(import_chalk.default.yellow("\nYour previously linked team is no longer available."));
206
+ }
207
+ if (teams.length === 1) {
208
+ console.log(`${import_chalk.default.cyan("\u2192")} Linking to your team ${import_chalk.default.bold(teams[0].name)}.`);
209
+ return { teamId: teams[0].teamId, teamName: teams[0].name };
210
+ }
211
+ if (!opts.interactive) {
212
+ console.log(import_chalk.default.yellow(`Linking to "${teams[0].name}" \u2014 pass --team to choose another.`));
213
+ return { teamId: teams[0].teamId, teamName: teams[0].name };
214
+ }
215
+ const { default: inquirer2 } = await import("inquirer");
216
+ const { chosen } = await inquirer2.prompt([{
217
+ type: "list",
218
+ name: "chosen",
219
+ message: "Which team do you want to link to?",
220
+ choices: teams.map((t) => ({ name: t.name, value: t.teamId }))
221
+ }]);
222
+ const picked = teams.find((t) => t.teamId === chosen);
223
+ return { teamId: picked.teamId, teamName: picked.name };
224
+ }
228
225
 
229
226
  // src/commands/login.ts
230
- var import_chalk = __toESM(require("chalk"));
231
- var import_ora = __toESM(require("ora"));
232
- init_auth();
233
- init_api();
234
227
  var DASHBOARD_BASE2 = "https://dashboard.apiblaze.com";
235
228
  function openBrowser(url) {
236
229
  const { exec } = require("child_process");
@@ -242,17 +235,17 @@ function decodeJWTPayload(token) {
242
235
  return JSON.parse(Buffer.from(part, "base64url").toString());
243
236
  }
244
237
  async function runLogin() {
245
- console.log(import_chalk.default.bold("\nLogging in to APIblaze...\n"));
238
+ console.log(import_chalk2.default.bold("\nLogging in to APIblaze...\n"));
246
239
  const codeRes = await fetch(`${DASHBOARD_BASE2}/api/device/code`, { method: "POST" });
247
240
  if (!codeRes.ok) {
248
241
  throw new Error(`Failed to start login: ${codeRes.status}`);
249
242
  }
250
243
  const deviceAuth = await codeRes.json();
251
244
  const loginUrl = deviceAuth.verification_uri_complete ?? deviceAuth.verification_uri;
252
- console.log(`${import_chalk.default.cyan("\u2192")} Open this URL in your browser to confirm login:`);
253
- console.log(` ${import_chalk.default.bold.underline(loginUrl)}
245
+ console.log(`${import_chalk2.default.cyan("\u2192")} Open this URL in your browser to confirm login:`);
246
+ console.log(` ${import_chalk2.default.bold.underline(loginUrl)}
254
247
  `);
255
- console.log(`${import_chalk.default.cyan("\u2192")} Your code: ${import_chalk.default.bold(deviceAuth.user_code)}
248
+ console.log(`${import_chalk2.default.cyan("\u2192")} Your code: ${import_chalk2.default.bold(deviceAuth.user_code)}
256
249
  `);
257
250
  openBrowser(loginUrl);
258
251
  const spinner = (0, import_ora.default)("Waiting for authorization in browser...").start();
@@ -300,33 +293,20 @@ async function runLogin() {
300
293
  email,
301
294
  teamId: defaultTeamId ?? void 0
302
295
  });
303
- spinner.succeed(import_chalk.default.green("Authorized!"));
296
+ spinner.succeed(import_chalk2.default.green("Authorized!"));
304
297
  if (githubHandle) {
305
- console.log(`${import_chalk.default.cyan("\u2192")} Logged in as ${import_chalk.default.bold("@" + githubHandle)}`);
298
+ console.log(`${import_chalk2.default.cyan("\u2192")} Logged in as ${import_chalk2.default.bold("@" + githubHandle)}`);
306
299
  }
307
300
  let teamId = defaultTeamId ?? void 0;
308
301
  let teamName;
309
- try {
310
- const teams = await getTeams();
311
- if (teams.length === 0) {
312
- console.log(import_chalk.default.yellow("\nNo teams found \u2014 you can still use apiblaze dev with a personal workspace."));
313
- } else if (teams.length === 1) {
314
- teamId = teams[0].teamId;
315
- teamName = teams[0].name;
316
- console.log(`
317
- ${import_chalk.default.cyan("\u2192")} Team: ${import_chalk.default.bold(teamName)}`);
318
- } else {
319
- const { default: inquirer2 } = await import("inquirer");
320
- const { chosen } = await inquirer2.prompt([{
321
- type: "list",
322
- name: "chosen",
323
- message: "Which team do you want to use?",
324
- choices: teams.map((t) => ({ name: t.name, value: t.teamId }))
325
- }]);
326
- teamId = chosen;
327
- teamName = teams.find((t) => t.teamId === chosen)?.name;
328
- }
329
- } catch {
302
+ const linked = await resolveLinkedTeam({ preferredId: defaultTeamId ?? void 0, interactive: !!process.stdin.isTTY });
303
+ if (linked) {
304
+ teamId = linked.teamId;
305
+ teamName = linked.teamName;
306
+ if (teamName) console.log(`
307
+ ${import_chalk2.default.cyan("\u2192")} Team: ${import_chalk2.default.bold(teamName)}`);
308
+ } else {
309
+ console.log(import_chalk2.default.yellow("\nNo teams found \u2014 your personal workspace will be created on first use."));
330
310
  }
331
311
  saveCredentials({
332
312
  accessToken,
@@ -338,48 +318,254 @@ ${import_chalk.default.cyan("\u2192")} Team: ${import_chalk.default.bold(teamNam
338
318
  teamId,
339
319
  teamName
340
320
  });
341
- console.log(import_chalk.default.green("\n\u2714 Logged in successfully!"));
321
+ console.log(import_chalk2.default.green("\n\u2714 Logged in successfully!"));
342
322
  }
343
323
 
344
324
  // src/commands/dev.ts
345
- var import_chalk3 = __toESM(require("chalk"));
325
+ var import_fs = __toESM(require("fs"));
326
+ var import_chalk4 = __toESM(require("chalk"));
346
327
  var import_ora2 = __toESM(require("ora"));
347
328
  var import_inquirer = __toESM(require("inquirer"));
348
- init_auth();
349
- init_api();
350
329
 
351
330
  // src/lib/traffic.ts
352
- var import_chalk2 = __toESM(require("chalk"));
331
+ var import_chalk3 = __toESM(require("chalk"));
353
332
  var METHOD_COLORS = {
354
- GET: import_chalk2.default.cyan,
355
- POST: import_chalk2.default.green,
356
- PUT: import_chalk2.default.yellow,
357
- PATCH: import_chalk2.default.magenta,
358
- DELETE: import_chalk2.default.red,
359
- HEAD: import_chalk2.default.blue,
360
- OPTIONS: import_chalk2.default.gray
333
+ GET: import_chalk3.default.cyan,
334
+ POST: import_chalk3.default.green,
335
+ PUT: import_chalk3.default.yellow,
336
+ PATCH: import_chalk3.default.magenta,
337
+ DELETE: import_chalk3.default.red,
338
+ HEAD: import_chalk3.default.blue,
339
+ OPTIONS: import_chalk3.default.gray
361
340
  };
362
341
  function colorMethod(method) {
363
- const colorFn = METHOD_COLORS[method.toUpperCase()] ?? import_chalk2.default.white;
342
+ const colorFn = METHOD_COLORS[method.toUpperCase()] ?? import_chalk3.default.white;
364
343
  return colorFn(method.padEnd(7));
365
344
  }
366
345
  function colorStatus(status) {
367
346
  const s = String(status);
368
- if (status >= 500) return import_chalk2.default.red(s);
369
- if (status >= 400) return import_chalk2.default.yellow(s);
370
- if (status >= 300) return import_chalk2.default.cyan(s);
371
- return import_chalk2.default.green(s);
347
+ if (status >= 500) return import_chalk3.default.red(s);
348
+ if (status >= 400) return import_chalk3.default.yellow(s);
349
+ if (status >= 300) return import_chalk3.default.cyan(s);
350
+ return import_chalk3.default.green(s);
372
351
  }
373
352
  function colorLatency(latency) {
374
353
  const s = `${latency}ms`;
375
- if (latency < 100) return import_chalk2.default.green(s);
376
- if (latency < 500) return import_chalk2.default.yellow(s);
377
- return import_chalk2.default.red(s);
354
+ if (latency < 100) return import_chalk3.default.green(s);
355
+ if (latency < 500) return import_chalk3.default.yellow(s);
356
+ return import_chalk3.default.red(s);
357
+ }
358
+ function timestamp() {
359
+ return (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8);
378
360
  }
379
361
  function formatLogLine(entry) {
380
- const now = /* @__PURE__ */ new Date();
381
- const ts = now.toTimeString().slice(0, 8);
382
- return `${import_chalk2.default.gray(`[${ts}]`)} ${colorMethod(entry.method)} ${import_chalk2.default.white(entry.path)} ${import_chalk2.default.gray("\u2192")} ${colorStatus(entry.status)} ${import_chalk2.default.gray(`(${colorLatency(entry.latency)})`)}`;
362
+ return `${import_chalk3.default.gray(`[${timestamp()}]`)} ${colorMethod(entry.method)} ${import_chalk3.default.white(entry.path)} ${import_chalk3.default.gray("\u2192")} ${colorStatus(entry.status)} ${import_chalk3.default.gray(`(${colorLatency(entry.latency)})`)}`;
363
+ }
364
+ var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
365
+ "authorization",
366
+ "proxy-authorization",
367
+ "cookie",
368
+ "set-cookie",
369
+ "x-api-key",
370
+ "api-key",
371
+ "apikey",
372
+ "x-auth-token",
373
+ "x-access-token",
374
+ "x-refresh-token",
375
+ "x-amz-security-token",
376
+ "x-csrf-token"
377
+ ]);
378
+ var SENSITIVE_QUERY = /* @__PURE__ */ new Set([
379
+ "api_key",
380
+ "apikey",
381
+ "key",
382
+ "token",
383
+ "access_token",
384
+ "refresh_token",
385
+ "id_token",
386
+ "secret",
387
+ "client_secret",
388
+ "password",
389
+ "sig",
390
+ "signature"
391
+ ]);
392
+ function maskSecret(value) {
393
+ const v = value.trim();
394
+ if (v.length <= 8) return "\u2022\u2022\u2022\u2022";
395
+ const head = v.slice(0, 4);
396
+ const tail = v.slice(-4);
397
+ return `${head}\u2026${tail} ${import_chalk3.default.gray(`(${v.length} chars, masked)`)}`;
398
+ }
399
+ function base64UrlDecode(seg) {
400
+ try {
401
+ const pad = seg.length % 4 === 0 ? "" : "=".repeat(4 - seg.length % 4);
402
+ const b64 = seg.replace(/-/g, "+").replace(/_/g, "/") + pad;
403
+ return Buffer.from(b64, "base64").toString("utf8");
404
+ } catch {
405
+ return null;
406
+ }
407
+ }
408
+ function decodeJwt(token) {
409
+ const parts = token.trim().split(".");
410
+ if (parts.length !== 3) return null;
411
+ const headerRaw = base64UrlDecode(parts[0]);
412
+ const payloadRaw = base64UrlDecode(parts[1]);
413
+ if (!headerRaw || !payloadRaw) return null;
414
+ try {
415
+ const header = JSON.parse(headerRaw);
416
+ const payload = JSON.parse(payloadRaw);
417
+ if (!header || typeof header !== "object" || !("alg" in header)) return null;
418
+ return { header, payload };
419
+ } catch {
420
+ return null;
421
+ }
422
+ }
423
+ function maskPath(path2) {
424
+ const q = path2.indexOf("?");
425
+ if (q < 0) return path2;
426
+ const base = path2.slice(0, q);
427
+ const query = path2.slice(q + 1);
428
+ const masked = query.split("&").map((pair) => {
429
+ const eq = pair.indexOf("=");
430
+ if (eq < 0) return pair;
431
+ const name = pair.slice(0, eq);
432
+ const val = pair.slice(eq + 1);
433
+ if (val && SENSITIVE_QUERY.has(decodeURIComponent(name).toLowerCase())) {
434
+ return `${name}=${maskSecret(decodeURIComponent(val))}`;
435
+ }
436
+ return pair;
437
+ }).join("&");
438
+ return `${base}?${masked}`;
439
+ }
440
+ function formatHeaderLines(name, value) {
441
+ const lower = name.toLowerCase();
442
+ if (lower === "authorization" || lower === "proxy-authorization") {
443
+ const m = /^Bearer\s+(.+)$/i.exec(value.trim());
444
+ if (m) {
445
+ const token = m[1];
446
+ const jwt = decodeJwt(token);
447
+ if (jwt) {
448
+ return [
449
+ ` ${import_chalk3.default.dim(name)}: Bearer ${maskSecret(token)} ${import_chalk3.default.cyan("(JWT)")}`,
450
+ ` ${import_chalk3.default.gray("\u251C header: ")} ${import_chalk3.default.gray(JSON.stringify(jwt.header))}`,
451
+ ` ${import_chalk3.default.gray("\u2514 payload:")} ${import_chalk3.default.gray(JSON.stringify(jwt.payload))}`
452
+ ];
453
+ }
454
+ return [` ${import_chalk3.default.dim(name)}: Bearer ${maskSecret(token)}`];
455
+ }
456
+ return [` ${import_chalk3.default.dim(name)}: ${maskSecret(value)}`];
457
+ }
458
+ if (SENSITIVE_HEADERS.has(lower)) {
459
+ return [` ${import_chalk3.default.dim(name)}: ${maskSecret(value)}`];
460
+ }
461
+ return [` ${import_chalk3.default.dim(name)}: ${value}`];
462
+ }
463
+ function formatBody(body, contentType) {
464
+ if (body.length === 0) return import_chalk3.default.gray(" (empty)");
465
+ if (body.includes(0)) return import_chalk3.default.gray(` <binary, ${body.length} bytes>`);
466
+ let text = body.toString("utf8");
467
+ const ct = (contentType ?? "").toLowerCase();
468
+ if (ct.includes("json") || /^[[{]/.test(text.trim())) {
469
+ try {
470
+ text = JSON.stringify(JSON.parse(text), null, 2);
471
+ } catch {
472
+ }
473
+ }
474
+ const MAX = 4e3;
475
+ const truncated = text.length > MAX;
476
+ const shown = truncated ? text.slice(0, MAX) : text;
477
+ const indented = shown.split("\n").map((l) => ` ${l}`).join("\n");
478
+ return indented + (truncated ? import_chalk3.default.gray(`
479
+ \u2026 (${text.length - MAX} more bytes \u2014 see --capture-file for full)`) : "");
480
+ }
481
+ function formatCapturedRequest(req, note) {
482
+ const headerLines = Object.keys(req.headers).flatMap((name) => formatHeaderLines(name, req.headers[name]));
483
+ const contentType = Object.keys(req.headers).find((k) => k.toLowerCase() === "content-type");
484
+ return [
485
+ `${import_chalk3.default.gray(`[${timestamp()}]`)} ${import_chalk3.default.magenta("\u26B2 CAPTURED")} ${colorMethod(req.method)} ${import_chalk3.default.white(maskPath(req.path))} ${import_chalk3.default.gray(`\u2014 ${note}`)}`,
486
+ import_chalk3.default.bold(" Headers:"),
487
+ ...headerLines,
488
+ import_chalk3.default.bold(" Body:"),
489
+ formatBody(req.body, contentType ? req.headers[contentType] : void 0),
490
+ ""
491
+ ].join("\n");
492
+ }
493
+
494
+ // src/lib/random-name.ts
495
+ var ADJECTIVES = [
496
+ "amber",
497
+ "azure",
498
+ "brave",
499
+ "bright",
500
+ "calm",
501
+ "clever",
502
+ "cosmic",
503
+ "crisp",
504
+ "eager",
505
+ "fancy",
506
+ "gentle",
507
+ "happy",
508
+ "jolly",
509
+ "keen",
510
+ "lively",
511
+ "lucky",
512
+ "mellow",
513
+ "merry",
514
+ "nimble",
515
+ "noble",
516
+ "plucky",
517
+ "quiet",
518
+ "rapid",
519
+ "shiny",
520
+ "smooth",
521
+ "snappy",
522
+ "sunny",
523
+ "swift",
524
+ "tidy",
525
+ "vivid",
526
+ "witty",
527
+ "zesty"
528
+ ];
529
+ var NOUNS = [
530
+ "badger",
531
+ "beacon",
532
+ "cedar",
533
+ "comet",
534
+ "dolphin",
535
+ "ember",
536
+ "falcon",
537
+ "finch",
538
+ "harbor",
539
+ "heron",
540
+ "koala",
541
+ "lark",
542
+ "lemur",
543
+ "maple",
544
+ "meadow",
545
+ "otter",
546
+ "panda",
547
+ "pebble",
548
+ "puffin",
549
+ "quail",
550
+ "raccoon",
551
+ "river",
552
+ "robin",
553
+ "sparrow",
554
+ "spruce",
555
+ "tiger",
556
+ "turtle",
557
+ "walrus",
558
+ "willow",
559
+ "wombat",
560
+ "yak",
561
+ "zebra"
562
+ ];
563
+ function pick(list) {
564
+ return list[Math.floor(Math.random() * list.length)];
565
+ }
566
+ function randomProxyName() {
567
+ const digits = String(Math.floor(Math.random() * 100)).padStart(2, "0");
568
+ return `${pick(ADJECTIVES)}${pick(NOUNS)}${digits}`;
383
569
  }
384
570
 
385
571
  // src/lib/tunnel-client.ts
@@ -387,6 +573,7 @@ var import_ws = __toESM(require("ws"));
387
573
  var CHUNK_BYTES = 512 * 1024;
388
574
  var PING_INTERVAL_MS = 6e4;
389
575
  var MAX_RECONNECT_DELAY_MS = 15e3;
576
+ var OFFLINE_CODES = /* @__PURE__ */ new Set(["ECONNREFUSED", "ECONNRESET", "ENOTFOUND", "EHOSTUNREACH"]);
390
577
  var STRIP_HEADERS = /* @__PURE__ */ new Set([
391
578
  "host",
392
579
  "content-length",
@@ -404,6 +591,10 @@ function stripHeaders(headers) {
404
591
  }
405
592
  return out;
406
593
  }
594
+ function encodeBody(buf) {
595
+ if (buf.includes(0)) return { value: buf.toString("base64"), encoding: "base64" };
596
+ return { value: buf.toString("utf8"), encoding: "utf8" };
597
+ }
407
598
  function startTunnelClient(opts) {
408
599
  const target = `http://127.0.0.1:${opts.localPort}`;
409
600
  const inflight = /* @__PURE__ */ new Map();
@@ -411,6 +602,7 @@ function startTunnelClient(opts) {
411
602
  let closed = false;
412
603
  let reconnects = 0;
413
604
  let pingTimer;
605
+ let capturing = false;
414
606
  function connect() {
415
607
  const url = `${opts.connectUrl}?project=${encodeURIComponent(opts.projectId)}&token=${encodeURIComponent(opts.token)}`;
416
608
  const socket = new import_ws.default(url);
@@ -470,30 +662,80 @@ function startTunnelClient(opts) {
470
662
  inflight.delete(id);
471
663
  void forward(socket, id, f);
472
664
  }
665
+ function sendResponse(socket, id, status, headers, body) {
666
+ send(socket, { id, type: "res", status, headers, bodyLen: body.length });
667
+ if (body.length === 0) {
668
+ send(socket, { id, type: "chunk", seq: 0, data: "", final: true });
669
+ return;
670
+ }
671
+ for (let off = 0, seq = 0; off < body.length; off += CHUNK_BYTES) {
672
+ const slice = body.subarray(off, Math.min(off + CHUNK_BYTES, body.length));
673
+ send(socket, { id, type: "chunk", seq: seq++, data: slice.toString("base64"), final: off + CHUNK_BYTES >= body.length });
674
+ }
675
+ }
676
+ function record(f, reqBody, status, captured, resHeaders, resBody) {
677
+ if (!opts.onRecord) return;
678
+ const req = encodeBody(reqBody);
679
+ const res = resBody ? encodeBody(resBody) : void 0;
680
+ opts.onRecord({
681
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
682
+ projectId: opts.projectId,
683
+ captured,
684
+ method: f.method,
685
+ path: f.path,
686
+ requestHeaders: f.headers,
687
+ requestBody: reqBody.length ? req.value : void 0,
688
+ requestBodyEncoding: reqBody.length ? req.encoding : void 0,
689
+ status,
690
+ responseHeaders: resHeaders,
691
+ responseBody: res?.value,
692
+ responseBodyEncoding: res?.encoding,
693
+ latencyMs: Date.now() - f.start
694
+ });
695
+ }
473
696
  async function forward(socket, id, f) {
474
697
  const body = Buffer.concat(f.chunks);
475
698
  const init = { method: f.method, headers: stripHeaders(f.headers) };
476
699
  if (body.length) init.body = body;
477
- let status = 502;
478
700
  try {
479
701
  const resp = await fetch(target + f.path, init);
480
- status = resp.status;
702
+ const status = resp.status;
481
703
  const buf = Buffer.from(await resp.arrayBuffer());
482
704
  const headers = {};
483
705
  resp.headers.forEach((value, key) => {
484
706
  if (!STRIP_HEADERS.has(key.toLowerCase())) headers[key] = value;
485
707
  });
486
- send(socket, { id, type: "res", status, headers, bodyLen: buf.length });
487
- for (let off = 0, seq = 0; off < buf.length; off += CHUNK_BYTES) {
488
- const slice = buf.subarray(off, Math.min(off + CHUNK_BYTES, buf.length));
489
- send(socket, { id, type: "chunk", seq: seq++, data: slice.toString("base64"), final: off + CHUNK_BYTES >= buf.length });
708
+ if (capturing) {
709
+ capturing = false;
710
+ opts.onResume?.();
490
711
  }
712
+ sendResponse(socket, id, status, headers, buf);
713
+ opts.onEntry({ method: f.method, path: f.path, status, latency: Date.now() - f.start });
714
+ record(f, body, status, false, headers, buf);
491
715
  } catch (err) {
492
716
  const code = err?.cause?.code;
493
- const message = code === "ECONNREFUSED" || code === "ECONNRESET" || code === "ENOTFOUND" || code === "EHOSTUNREACH" ? `No local server reachable at ${target} \u2014 is your dev server running on port ${opts.localPort}?` : err?.cause?.message || err?.message || String(err);
717
+ if (OFFLINE_CODES.has(code)) {
718
+ if (!capturing) {
719
+ capturing = true;
720
+ opts.onCaptureStart?.();
721
+ }
722
+ const note = `no local server on port ${opts.localPort}`;
723
+ const payload = Buffer.from(JSON.stringify({
724
+ apiblaze_dev: "captured",
725
+ message: `No local server on port ${opts.localPort} \u2014 request captured by \`apiblaze dev\`. Start your server and resend to forward it.`,
726
+ request: { method: f.method, path: f.path }
727
+ }, null, 2));
728
+ const headers = { "content-type": "application/json", "x-apiblaze-dev": "captured" };
729
+ sendResponse(socket, id, 200, headers, payload);
730
+ opts.onCapture?.({ method: f.method, path: f.path, headers: f.headers, body }, note);
731
+ record(f, body, 200, true, headers, payload);
732
+ return;
733
+ }
734
+ const message = err?.cause?.message || err?.message || String(err);
494
735
  send(socket, { id, type: "err", message });
736
+ opts.onEntry({ method: f.method, path: f.path, status: 502, latency: Date.now() - f.start });
737
+ record(f, body, 502, false);
495
738
  }
496
- opts.onEntry({ method: f.method, path: f.path, status, latency: Date.now() - f.start });
497
739
  }
498
740
  function send(socket, frame) {
499
741
  try {
@@ -515,34 +757,93 @@ function startTunnelClient(opts) {
515
757
  }
516
758
 
517
759
  // src/commands/dev.ts
760
+ async function offerAutoCreate(teamId, port) {
761
+ if (!process.stdin.isTTY) {
762
+ console.log(import_chalk4.default.yellow("No projects found with an internal target."));
763
+ console.log("Point a project at localhost in the dashboard, or run `apiblaze dev` in an interactive terminal to create one automatically.");
764
+ return null;
765
+ }
766
+ const { create } = await import_inquirer.default.prompt([{
767
+ type: "confirm",
768
+ name: "create",
769
+ message: `No project points at this machine. Create a quick dev proxy \u2192 ${import_chalk4.default.bold(`http://localhost:${port}`)} and tunnel it?`,
770
+ default: true
771
+ }]);
772
+ if (!create) return null;
773
+ const { auth } = await import_inquirer.default.prompt([{
774
+ type: "list",
775
+ name: "auth",
776
+ message: "How should callers authenticate to the new proxy?",
777
+ choices: [
778
+ { name: "none \u2014 open to anyone with the URL (simplest)", value: "none" },
779
+ { name: "api_key \u2014 callers must send an X-API-Key header", value: "api_key" }
780
+ ],
781
+ default: "none"
782
+ }]);
783
+ let name = randomProxyName();
784
+ for (let i = 0; i < 8; i++) {
785
+ const check = await checkProxyName(name, teamId).catch(() => null);
786
+ if (!check || check.canUseProjectName && check.canUseApiVersion) break;
787
+ name = randomProxyName();
788
+ }
789
+ const spinner = (0, import_ora2.default)(`Creating dev proxy "${name}"...`).start();
790
+ let result;
791
+ try {
792
+ result = await createProxy({ name, target_url: `http://localhost:${port}`, auth_type: auth, team_id: teamId });
793
+ spinner.succeed(import_chalk4.default.green(`Created dev proxy "${name}".`));
794
+ } catch (err) {
795
+ spinner.fail("Failed to create the dev proxy.");
796
+ throw err;
797
+ }
798
+ const version2 = result.api_version || "1.0.0";
799
+ const endpoint = `https://${name}.apiblaze.com/${version2}/dev`;
800
+ console.log(` ${import_chalk4.default.dim("Endpoint:")} ${import_chalk4.default.bold(endpoint)}`);
801
+ if (auth === "api_key") {
802
+ const key = result.api_keys?.dev ?? Object.values(result.api_keys ?? {})[0];
803
+ if (key) {
804
+ console.log(` ${import_chalk4.default.dim("API key (dev):")} ${import_chalk4.default.bold.green(key)}`);
805
+ console.log(import_chalk4.default.dim(" Send it as the X-API-Key header. It may not be shown again."));
806
+ }
807
+ }
808
+ const targets = await getLocalhostTargets(teamId).catch(() => []);
809
+ const created = targets.find((t) => t.projectId === result.project_id);
810
+ if (!created) {
811
+ console.log(import_chalk4.default.yellow(" Proxy created, but it did not appear as a localhost target \u2014 try `apiblaze dev` again."));
812
+ return null;
813
+ }
814
+ return created;
815
+ }
816
+ async function probeLocalServer(port) {
817
+ const controller = new AbortController();
818
+ const timer = setTimeout(() => controller.abort(), 1500);
819
+ try {
820
+ await fetch(`http://127.0.0.1:${port}/`, { method: "HEAD", signal: controller.signal });
821
+ return true;
822
+ } catch (err) {
823
+ if (err?.name === "AbortError") return true;
824
+ const code = err?.cause?.code;
825
+ return !(code === "ECONNREFUSED" || code === "ENOTFOUND" || code === "EHOSTUNREACH");
826
+ } finally {
827
+ clearTimeout(timer);
828
+ }
829
+ }
518
830
  async function runDev(options) {
519
831
  const creds = loadCredentials();
520
832
  if (!creds) {
521
- console.error(import_chalk3.default.red("Not logged in. Run `apiblaze login` first."));
833
+ console.error(import_chalk4.default.red("Not logged in. Run `apiblaze login` first."));
522
834
  process.exit(1);
523
835
  }
524
- let teamId = creds.teamId;
525
- if (!teamId) {
526
- const { getTeams: getTeams2 } = await Promise.resolve().then(() => (init_api(), api_exports));
527
- const teams = await getTeams2().catch(() => []);
528
- if (teams.length === 1) {
529
- teamId = teams[0].teamId;
530
- } else if (teams.length > 1) {
531
- const { chosen } = await import_inquirer.default.prompt([{
532
- type: "list",
533
- name: "chosen",
534
- message: "Which team?",
535
- choices: teams.map((t) => ({ name: t.name, value: t.teamId }))
536
- }]);
537
- teamId = chosen;
538
- }
836
+ const linked = await resolveLinkedTeam({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
837
+ if (!linked) {
838
+ console.error(import_chalk4.default.red("No team available. Run `apiblaze login` to set up your team."));
839
+ process.exit(1);
539
840
  }
540
- if (creds.teamName) {
541
- console.log(`${import_chalk3.default.cyan("\u2192")} Team: ${import_chalk3.default.bold(creds.teamName)}`);
841
+ const teamId = linked.teamId;
842
+ if (linked.teamId !== creds.teamId || linked.teamName !== creds.teamName) {
843
+ saveCredentials({ ...creds, teamId: linked.teamId, teamName: linked.teamName });
542
844
  }
543
- if (!teamId) {
544
- console.error(import_chalk3.default.red("No team selected. Run `apiblaze login` to set up your team."));
545
- process.exit(1);
845
+ if (linked.teamName) {
846
+ console.log(`${import_chalk4.default.cyan("\u2192")} Team: ${import_chalk4.default.bold(linked.teamName)}`);
546
847
  }
547
848
  let targets;
548
849
  {
@@ -555,17 +856,19 @@ async function runDev(options) {
555
856
  throw err;
556
857
  }
557
858
  }
558
- if (targets.length === 0) {
559
- console.log(import_chalk3.default.yellow("No projects found with an internal target."));
560
- console.log("Set a project's upstream target to localhost or a private IP in your APIblaze dashboard, then try again.");
561
- process.exit(0);
562
- }
563
859
  let selectedTargets;
564
- if (targets.length === 1) {
860
+ if (targets.length === 0) {
861
+ const created = await offerAutoCreate(teamId, options.port);
862
+ if (!created) {
863
+ console.log("Set a project's upstream target to localhost or a private IP, then try again.");
864
+ process.exit(0);
865
+ }
866
+ selectedTargets = [created];
867
+ } else if (targets.length === 1) {
565
868
  const { confirmed } = await import_inquirer.default.prompt([{
566
869
  type: "confirm",
567
870
  name: "confirmed",
568
- message: `Found 1 project with an internal target \u2014 tunnel "${import_chalk3.default.bold(targets[0].projectName)}" (${targets[0].tenantName})?`,
871
+ message: `Found 1 project with an internal target \u2014 tunnel "${import_chalk4.default.bold(targets[0].projectName)}" (${targets[0].tenantName})?`,
569
872
  default: true
570
873
  }]);
571
874
  if (!confirmed) {
@@ -581,7 +884,7 @@ async function runDev(options) {
581
884
  message: `Found ${targets.length} projects with an internal target \u2014 pick one to tunnel:`,
582
885
  choices: [
583
886
  ...targets.map((t) => ({
584
- name: `${import_chalk3.default.bold(t.projectName)} (${t.tenantName}) \u2014 ${t.target}`,
887
+ name: `${import_chalk4.default.bold(t.projectName)} (${t.tenantName}) \u2014 ${t.target}`,
585
888
  value: t
586
889
  })),
587
890
  new import_inquirer.default.Separator(),
@@ -591,10 +894,18 @@ async function runDev(options) {
591
894
  selectedTargets = chosen === ALL ? targets : [chosen];
592
895
  }
593
896
  console.log(
594
- import_chalk3.default.green(`
897
+ import_chalk4.default.green(`
595
898
  Tunneling ${selectedTargets.length} project(s) to localhost:${options.port}
596
899
  `)
597
900
  );
901
+ let recordSink;
902
+ let captureStream;
903
+ if (options.captureFile) {
904
+ captureStream = import_fs.default.createWriteStream(options.captureFile, { flags: "a" });
905
+ recordSink = (r) => captureStream.write(JSON.stringify(r) + "\n");
906
+ console.log(import_chalk4.default.gray(`Streaming full traffic to ${options.captureFile}
907
+ `));
908
+ }
598
909
  let restore = [];
599
910
  let connect;
600
911
  {
@@ -618,21 +929,38 @@ Tunneling ${selectedTargets.length} project(s) to localhost:${options.port}
618
929
  projectId,
619
930
  localPort: options.port,
620
931
  onEntry: (entry) => console.log(formatLogLine(entry)),
621
- onStatus: (status) => console.log(import_chalk3.default.gray(`[${projectId}] ${status}`))
932
+ onStatus: (status) => console.log(import_chalk4.default.gray(`[${projectId}] ${status}`)),
933
+ onCapture: (req, note) => console.log(formatCapturedRequest(req, note)),
934
+ onCaptureStart: () => console.log(
935
+ import_chalk4.default.magenta(`
936
+ \u26B2 No local server on port ${options.port} yet \u2014 capturing requests below. Start your server and they'll forward automatically.
937
+ `)
938
+ ),
939
+ onResume: () => console.log(
940
+ import_chalk4.default.green(`
941
+ \u2713 Local server detected on port ${options.port} \u2014 forwarding resumed.
942
+ `)
943
+ ),
944
+ onRecord: recordSink
622
945
  })
623
946
  );
624
- console.log("\n" + import_chalk3.default.gray("\u2500".repeat(60)));
625
- console.log(import_chalk3.default.bold("Live traffic") + import_chalk3.default.gray(" (Ctrl+C to stop)"));
626
- console.log(import_chalk3.default.gray("\u2500".repeat(60)) + "\n");
947
+ const localUp = await probeLocalServer(options.port);
948
+ console.log("\n" + import_chalk4.default.gray("\u2500".repeat(60)));
949
+ console.log(import_chalk4.default.bold("Live traffic") + import_chalk4.default.gray(" (Ctrl+C to stop)"));
950
+ console.log(
951
+ localUp ? import_chalk4.default.green(`\u2713 Local server detected on port ${options.port} \u2014 forwarding live.`) : import_chalk4.default.magenta(`\u26B2 Nothing listening on port ${options.port} yet \u2014 requests will be captured until your server starts.`)
952
+ );
953
+ console.log(import_chalk4.default.gray("\u2500".repeat(60)) + "\n");
627
954
  let isCleaningUp = false;
628
955
  async function cleanup() {
629
956
  if (isCleaningUp) return;
630
957
  isCleaningUp = true;
631
- console.log(import_chalk3.default.gray("\n\nShutting down..."));
958
+ console.log(import_chalk4.default.gray("\n\nShutting down..."));
632
959
  for (const client of clients) client.close();
960
+ captureStream?.end();
633
961
  await deleteDevTunnel(restore).catch(() => {
634
962
  });
635
- console.log(import_chalk3.default.green("Tunnel stopped."));
963
+ console.log(import_chalk4.default.green("Tunnel stopped."));
636
964
  process.exit(0);
637
965
  }
638
966
  process.on("SIGINT", () => void cleanup());
@@ -642,18 +970,16 @@ Tunneling ${selectedTargets.length} project(s) to localhost:${options.port}
642
970
  }
643
971
 
644
972
  // src/commands/projects.ts
645
- var import_chalk4 = __toESM(require("chalk"));
973
+ var import_chalk5 = __toESM(require("chalk"));
646
974
  var import_ora3 = __toESM(require("ora"));
647
- init_auth();
648
- init_api();
649
975
  async function runProjects() {
650
976
  const creds = loadCredentials();
651
977
  if (!creds) {
652
- console.error(import_chalk4.default.red("Not logged in. Run `apiblaze login` first."));
978
+ console.error(import_chalk5.default.red("Not logged in. Run `apiblaze login` first."));
653
979
  process.exit(1);
654
980
  }
655
981
  if (creds.githubHandle) {
656
- console.log(`${import_chalk4.default.cyan("\u2192")} Logged in as ${import_chalk4.default.bold("@" + creds.githubHandle)}`);
982
+ console.log(`${import_chalk5.default.cyan("\u2192")} Logged in as ${import_chalk5.default.bold("@" + creds.githubHandle)}`);
657
983
  }
658
984
  let teamId = creds.teamId;
659
985
  let teamName = creds.teamName;
@@ -675,10 +1001,10 @@ async function runProjects() {
675
1001
  }
676
1002
  }
677
1003
  if (!teamId) {
678
- console.error(import_chalk4.default.red("No team found. Run `apiblaze login` to set up your team."));
1004
+ console.error(import_chalk5.default.red("No team found. Run `apiblaze login` to set up your team."));
679
1005
  process.exit(1);
680
1006
  }
681
- console.log(`${import_chalk4.default.cyan("\u2192")} Team: ${import_chalk4.default.bold(teamName ?? teamId)}
1007
+ console.log(`${import_chalk5.default.cyan("\u2192")} Team: ${import_chalk5.default.bold(teamName ?? teamId)}
682
1008
  `);
683
1009
  const spinner = (0, import_ora3.default)("Fetching projects...").start();
684
1010
  let projects;
@@ -690,23 +1016,21 @@ async function runProjects() {
690
1016
  throw err;
691
1017
  }
692
1018
  if (projects.length === 0) {
693
- console.log(import_chalk4.default.yellow("No projects found for this team."));
1019
+ console.log(import_chalk5.default.yellow("No projects found for this team."));
694
1020
  return;
695
1021
  }
696
1022
  const width = Math.max(...projects.map((p) => p.projectName.length));
697
1023
  for (const p of projects) {
698
- console.log(` ${import_chalk4.default.bold(p.projectName.padEnd(width))} ${import_chalk4.default.dim("v" + p.apiVersion)}`);
1024
+ console.log(` ${import_chalk5.default.bold(p.projectName.padEnd(width))} ${import_chalk5.default.dim("v" + p.apiVersion)}`);
699
1025
  }
700
- console.log(import_chalk4.default.dim(`
1026
+ console.log(import_chalk5.default.dim(`
701
1027
  ${projects.length} project${projects.length === 1 ? "" : "s"}`));
702
1028
  }
703
1029
 
704
1030
  // src/commands/create.ts
705
- var import_fs = __toESM(require("fs"));
706
- var import_chalk5 = __toESM(require("chalk"));
1031
+ var import_fs2 = __toESM(require("fs"));
1032
+ var import_chalk6 = __toESM(require("chalk"));
707
1033
  var import_ora4 = __toESM(require("ora"));
708
- init_auth();
709
- init_api();
710
1034
  function normalizeName(raw) {
711
1035
  return (raw || "").toLowerCase().replace(/[^a-z0-9]/g, "");
712
1036
  }
@@ -731,17 +1055,17 @@ function stripTenantFromPortal(devPortal) {
731
1055
  }
732
1056
  }
733
1057
  function fail(message) {
734
- console.error(import_chalk5.default.red(`Error: ${message}`));
1058
+ console.error(import_chalk6.default.red(`Error: ${message}`));
735
1059
  process.exit(1);
736
1060
  }
737
1061
  function printCurlExample(url, apiKey) {
738
1062
  console.log();
739
- console.log(` ${import_chalk5.default.dim("Try it with curl:")}`);
1063
+ console.log(` ${import_chalk6.default.dim("Try it with curl:")}`);
740
1064
  if (apiKey) {
741
- console.log(` ${import_chalk5.default.cyan(`curl ${url} \\`)}`);
742
- console.log(` ${import_chalk5.default.cyan(` -H "X-API-Key: ${apiKey}"`)}`);
1065
+ console.log(` ${import_chalk6.default.cyan(`curl ${url} \\`)}`);
1066
+ console.log(` ${import_chalk6.default.cyan(` -H "X-API-Key: ${apiKey}"`)}`);
743
1067
  } else {
744
- console.log(` ${import_chalk5.default.cyan(`curl ${url}`)}`);
1068
+ console.log(` ${import_chalk6.default.cyan(`curl ${url}`)}`);
745
1069
  }
746
1070
  }
747
1071
  var VALID_AUTH = ["api_key", "none", "oauth"];
@@ -771,7 +1095,7 @@ async function runCreate(opts = {}) {
771
1095
  teamId = match.teamId;
772
1096
  }
773
1097
  }
774
- if (!opts.json) console.log(import_chalk5.default.bold("\nCreate an API proxy\n"));
1098
+ if (!opts.json) console.log(import_chalk6.default.bold("\nCreate an API proxy\n"));
775
1099
  let name = "";
776
1100
  if (opts.name !== void 0) {
777
1101
  name = normalizeName(opts.name);
@@ -791,7 +1115,7 @@ async function runCreate(opts = {}) {
791
1115
  }]);
792
1116
  name = normalizeName(rawName);
793
1117
  if (name.length < 3) {
794
- console.log(import_chalk5.default.yellow(" Name must be at least 3 characters (letters and digits only).\n"));
1118
+ console.log(import_chalk6.default.yellow(" Name must be at least 3 characters (letters and digits only).\n"));
795
1119
  continue;
796
1120
  }
797
1121
  const spinner2 = (0, import_ora4.default)("Checking availability...").start();
@@ -799,15 +1123,15 @@ async function runCreate(opts = {}) {
799
1123
  const check = await checkProxyName(name, teamId, opts.apiversion);
800
1124
  spinner2.stop();
801
1125
  if (!check.canUseProjectName || !check.canUseApiVersion) {
802
- console.log(import_chalk5.default.yellow(` "${name}" is not available${check.message ? ` \u2014 ${check.message}` : ""}. Try another.
1126
+ console.log(import_chalk6.default.yellow(` "${name}" is not available${check.message ? ` \u2014 ${check.message}` : ""}. Try another.
803
1127
  `));
804
1128
  continue;
805
1129
  }
806
1130
  } catch {
807
1131
  spinner2.stop();
808
- console.log(import_chalk5.default.dim(" (could not verify availability; continuing)"));
1132
+ console.log(import_chalk6.default.dim(" (could not verify availability; continuing)"));
809
1133
  }
810
- console.log(`${import_chalk5.default.cyan("\u2192")} Your API will live at ${import_chalk5.default.bold(`https://${name}.apiblaze.com`)}
1134
+ console.log(`${import_chalk6.default.cyan("\u2192")} Your API will live at ${import_chalk6.default.bold(`https://${name}.apiblaze.com`)}
811
1135
  `);
812
1136
  break;
813
1137
  }
@@ -827,7 +1151,7 @@ async function runCreate(opts = {}) {
827
1151
  message: "Target URL to forward requests to (e.g. https://httpbin.org):"
828
1152
  }]);
829
1153
  if (!isHttpUrl(url)) {
830
- console.log(import_chalk5.default.yellow(" Enter a valid http(s) URL.\n"));
1154
+ console.log(import_chalk6.default.yellow(" Enter a valid http(s) URL.\n"));
831
1155
  continue;
832
1156
  }
833
1157
  targetUrl = url.trim();
@@ -838,7 +1162,7 @@ async function runCreate(opts = {}) {
838
1162
  }
839
1163
  if (interactive && !opts.yes) {
840
1164
  const { default: inquirer2 } = await import("inquirer");
841
- console.log(`${import_chalk5.default.cyan("\u2192")} Auth: ${import_chalk5.default.bold(auth)}${auth === "api_key" ? " \u2014 consumers send an X-API-Key header" : ""}`);
1165
+ console.log(`${import_chalk6.default.cyan("\u2192")} Auth: ${import_chalk6.default.bold(auth)}${auth === "api_key" ? " \u2014 consumers send an X-API-Key header" : ""}`);
842
1166
  const { ok } = await inquirer2.prompt([{
843
1167
  type: "confirm",
844
1168
  name: "ok",
@@ -846,7 +1170,7 @@ async function runCreate(opts = {}) {
846
1170
  default: true
847
1171
  }]);
848
1172
  if (!ok) {
849
- console.log(import_chalk5.default.yellow("Cancelled."));
1173
+ console.log(import_chalk6.default.yellow("Cancelled."));
850
1174
  return;
851
1175
  }
852
1176
  }
@@ -854,45 +1178,10 @@ async function runCreate(opts = {}) {
854
1178
  let result;
855
1179
  try {
856
1180
  result = await createProxy({ name, target_url: targetUrl, auth_type: auth, team_id: teamId, ...opts.apiversion ? { api_version: opts.apiversion } : {} });
857
- spinner?.succeed(import_chalk5.default.green("Proxy created!"));
1181
+ spinner?.succeed(import_chalk6.default.green("Proxy created!"));
858
1182
  } catch (err) {
859
- const e = err;
860
- const ownedByYou = e?.status === 409 && e?.body?.reason === "slug_owned_by_your_other_team" && !opts.team;
861
1183
  spinner?.fail("Failed to create proxy.");
862
- if (ownedByYou) {
863
- const ownerId = e.body.owning_team_id;
864
- const ownerName = e.body.owning_team_name || ownerId;
865
- if (!opts.json) console.log(import_chalk5.default.yellow(`
866
- You already own "${e.body.product_slug}" under your team "${ownerName}".`));
867
- let doSwitch = false;
868
- if (interactive && !opts.yes) {
869
- const { default: inquirer2 } = await import("inquirer");
870
- const { ok } = await inquirer2.prompt([{
871
- type: "confirm",
872
- name: "ok",
873
- message: `Switch to team "${ownerName}" and create "${name}" there?`,
874
- default: true
875
- }]);
876
- doSwitch = ok;
877
- }
878
- if (!doSwitch) {
879
- if (!opts.json) console.log(import_chalk5.default.dim(` Run \`apiblaze team ${ownerId}\` to switch, then retry \u2014 or pick another --name.`));
880
- throw err;
881
- }
882
- const creds2 = loadCredentials();
883
- if (creds2) saveCredentials({ ...creds2, teamId: ownerId, teamName: ownerName });
884
- teamId = ownerId;
885
- const spinner2 = !opts.json ? (0, import_ora4.default)(`Creating proxy under "${ownerName}"...`).start() : null;
886
- try {
887
- result = await createProxy({ name, target_url: targetUrl, auth_type: auth, team_id: ownerId, ...opts.apiversion ? { api_version: opts.apiversion } : {} });
888
- spinner2?.succeed(import_chalk5.default.green(`Proxy created \u2014 active team switched to "${ownerName}".`));
889
- } catch (err2) {
890
- spinner2?.fail("Failed to create proxy after switching team.");
891
- throw err2;
892
- }
893
- } else {
894
- throw err;
895
- }
1184
+ throw err;
896
1185
  }
897
1186
  const version2 = result.api_version || "1.0.0";
898
1187
  const keys = result.api_keys ?? {};
@@ -912,16 +1201,16 @@ async function runCreate(opts = {}) {
912
1201
  return;
913
1202
  }
914
1203
  console.log();
915
- console.log(` ${import_chalk5.default.dim("Proxy URL: ")} ${import_chalk5.default.bold(proxyUrl)}`);
916
- if (devPortal) console.log(` ${import_chalk5.default.dim("Dev portal:")} ${import_chalk5.default.bold(devPortal)}`);
1204
+ console.log(` ${import_chalk6.default.dim("Proxy URL: ")} ${import_chalk6.default.bold(proxyUrl)}`);
1205
+ if (devPortal) console.log(` ${import_chalk6.default.dim("Dev portal:")} ${import_chalk6.default.bold(devPortal)}`);
917
1206
  if (adminKey) {
918
1207
  console.log();
919
- console.log(` ${import_chalk5.default.dim("Consumer admin API key (dev):")}`);
920
- console.log(` ${import_chalk5.default.bold.green(adminKey)}`);
921
- console.log(import_chalk5.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
1208
+ console.log(` ${import_chalk6.default.dim("Consumer admin API key (dev):")}`);
1209
+ console.log(` ${import_chalk6.default.bold.green(adminKey)}`);
1210
+ console.log(import_chalk6.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
922
1211
  const otherEnvs = Object.keys(keys).filter((e) => e !== "dev");
923
1212
  if (otherEnvs.length) {
924
- console.log(import_chalk5.default.dim(` (Separate keys were also created for: ${otherEnvs.join(", ")}.)`));
1213
+ console.log(import_chalk6.default.dim(` (Separate keys were also created for: ${otherEnvs.join(", ")}.)`));
925
1214
  }
926
1215
  }
927
1216
  printCurlExample(proxyUrl, auth === "none" ? void 0 : adminKey);
@@ -930,14 +1219,14 @@ async function runCreate(opts = {}) {
930
1219
  async function runAnonymousCreate(opts) {
931
1220
  const interactive = !!process.stdin.isTTY && !opts.json;
932
1221
  if (!opts.json) {
933
- console.log(import_chalk5.default.bold("\nCreate an API proxy"));
934
- console.log(import_chalk5.default.dim("Not logged in \u2014 creating an anonymous proxy. You can claim it to your account within 30 days.\n"));
1222
+ console.log(import_chalk6.default.bold("\nCreate an API proxy"));
1223
+ console.log(import_chalk6.default.dim("Not logged in \u2014 creating an anonymous proxy. You can claim it to your account within 30 days.\n"));
935
1224
  }
936
1225
  let body = {};
937
1226
  if (opts.config) {
938
1227
  let raw = "";
939
1228
  try {
940
- raw = import_fs.default.readFileSync(opts.config, "utf8");
1229
+ raw = import_fs2.default.readFileSync(opts.config, "utf8");
941
1230
  } catch {
942
1231
  fail(`Cannot read --config file: ${opts.config}`);
943
1232
  }
@@ -977,7 +1266,7 @@ async function runAnonymousCreate(opts) {
977
1266
  message: "Target URL to forward requests to (e.g. https://httpbin.org):"
978
1267
  }]);
979
1268
  if (!isHttpUrl(url)) {
980
- console.log(import_chalk5.default.yellow(" Enter a valid http(s) URL.\n"));
1269
+ console.log(import_chalk6.default.yellow(" Enter a valid http(s) URL.\n"));
981
1270
  continue;
982
1271
  }
983
1272
  target = url.trim();
@@ -1005,7 +1294,7 @@ async function runAnonymousCreate(opts) {
1005
1294
  let result;
1006
1295
  try {
1007
1296
  result = await createProxyAnonymous(body);
1008
- spinner?.succeed(import_chalk5.default.green("Proxy created!"));
1297
+ spinner?.succeed(import_chalk6.default.green("Proxy created!"));
1009
1298
  } catch (err) {
1010
1299
  spinner?.fail("Failed to create proxy.");
1011
1300
  throw err;
@@ -1027,30 +1316,28 @@ async function runAnonymousCreate(opts) {
1027
1316
  return;
1028
1317
  }
1029
1318
  console.log();
1030
- if (prodEndpoint) console.log(` ${import_chalk5.default.dim("Proxy URL: ")} ${import_chalk5.default.bold(prodEndpoint)}`);
1031
- if (result.portal) console.log(` ${import_chalk5.default.dim("Dev portal:")} ${import_chalk5.default.bold(result.portal)}`);
1319
+ if (prodEndpoint) console.log(` ${import_chalk6.default.dim("Proxy URL: ")} ${import_chalk6.default.bold(prodEndpoint)}`);
1320
+ if (result.portal) console.log(` ${import_chalk6.default.dim("Dev portal:")} ${import_chalk6.default.bold(result.portal)}`);
1032
1321
  if (apiKey) {
1033
1322
  console.log();
1034
- console.log(` ${import_chalk5.default.dim("API key:")}`);
1035
- console.log(` ${import_chalk5.default.bold.green(apiKey)}`);
1036
- console.log(import_chalk5.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
1323
+ console.log(` ${import_chalk6.default.dim("API key:")}`);
1324
+ console.log(` ${import_chalk6.default.bold.green(apiKey)}`);
1325
+ console.log(import_chalk6.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
1037
1326
  }
1038
1327
  if (prodEndpoint) printCurlExample(prodEndpoint, apiKey);
1039
1328
  if (result.claim_url) {
1040
1329
  console.log();
1041
- console.log(` ${import_chalk5.default.yellow("\u26A0 Anonymous proxy \u2014 claim it to your account within 30 days or it expires:")}`);
1042
- console.log(` ${import_chalk5.default.bold(result.claim_url)}`);
1043
- console.log(import_chalk5.default.dim(" (or run `apiblaze login`, then re-run `apiblaze create` to create it under your account)"));
1330
+ console.log(` ${import_chalk6.default.yellow("\u26A0 Anonymous proxy \u2014 claim it to your account within 30 days or it expires:")}`);
1331
+ console.log(` ${import_chalk6.default.bold(result.claim_url)}`);
1332
+ console.log(import_chalk6.default.dim(" (or run `apiblaze login`, then re-run `apiblaze create` to create it under your account)"));
1044
1333
  }
1045
1334
  console.log();
1046
1335
  }
1047
1336
 
1048
1337
  // src/commands/claim.ts
1049
- var import_chalk6 = __toESM(require("chalk"));
1050
- init_auth();
1051
- init_api();
1338
+ var import_chalk7 = __toESM(require("chalk"));
1052
1339
  function fail2(message) {
1053
- console.error(import_chalk6.default.red(`Error: ${message}`));
1340
+ console.error(import_chalk7.default.red(`Error: ${message}`));
1054
1341
  process.exit(1);
1055
1342
  }
1056
1343
  async function runClaim(claimCodeArg, opts) {
@@ -1106,41 +1393,39 @@ async function runClaim(claimCodeArg, opts) {
1106
1393
  console.log(JSON.stringify(result, null, 2));
1107
1394
  return;
1108
1395
  }
1109
- console.log(import_chalk6.default.green(`
1110
- \u2714 Claimed into ${import_chalk6.default.bold(teamName)}.`));
1111
- if (result.project_id) console.log(` Project: ${import_chalk6.default.bold(result.project_id)}`);
1396
+ console.log(import_chalk7.default.green(`
1397
+ \u2714 Claimed into ${import_chalk7.default.bold(teamName)}.`));
1398
+ if (result.project_id) console.log(` Project: ${import_chalk7.default.bold(result.project_id)}`);
1112
1399
  if (result.endpoints?.length) {
1113
1400
  console.log(" Endpoints:");
1114
1401
  for (const e of result.endpoints) console.log(` ${e}`);
1115
1402
  }
1116
1403
  console.log(`
1117
- Manage it at ${import_chalk6.default.cyan("https://dashboard.apiblaze.com/dashboard")}`);
1404
+ Manage it at ${import_chalk7.default.cyan("https://dashboard.apiblaze.com/dashboard")}`);
1118
1405
  }
1119
1406
 
1120
1407
  // src/commands/logout.ts
1121
- var import_chalk7 = __toESM(require("chalk"));
1122
- init_auth();
1408
+ var import_chalk8 = __toESM(require("chalk"));
1123
1409
  async function runLogout() {
1124
1410
  const creds = loadCredentials();
1125
1411
  const removed = clearCredentials();
1126
1412
  if (!removed) {
1127
- console.log(import_chalk7.default.yellow("You were not logged in."));
1413
+ console.log(import_chalk8.default.yellow("You were not logged in."));
1128
1414
  return;
1129
1415
  }
1130
1416
  const who = creds?.githubHandle ?? creds?.email;
1131
- console.log(import_chalk7.default.green(`\u2714 Logged out${who ? ` (${who})` : ""}.`));
1417
+ console.log(import_chalk8.default.green(`\u2714 Logged out${who ? ` (${who})` : ""}.`));
1132
1418
  }
1133
1419
 
1134
1420
  // src/commands/whoami.ts
1135
- var import_chalk8 = __toESM(require("chalk"));
1136
- init_auth();
1421
+ var import_chalk9 = __toESM(require("chalk"));
1137
1422
  async function runWhoami(opts = {}) {
1138
1423
  const creds = loadCredentials();
1139
1424
  if (!creds) {
1140
1425
  if (opts.json) {
1141
1426
  console.log(JSON.stringify({ loggedIn: false }, null, 2));
1142
1427
  } else {
1143
- console.log(import_chalk8.default.yellow("Not logged in. Run `apiblaze login` first."));
1428
+ console.log(import_chalk9.default.yellow("Not logged in. Run `apiblaze login` first."));
1144
1429
  }
1145
1430
  return;
1146
1431
  }
@@ -1158,23 +1443,21 @@ async function runWhoami(opts = {}) {
1158
1443
  return;
1159
1444
  }
1160
1445
  const who = creds.githubHandle ?? creds.email ?? creds.apiblazeUserId ?? "unknown";
1161
- console.log(`${import_chalk8.default.cyan("Signed in as")} ${import_chalk8.default.bold(who)}`);
1446
+ console.log(`${import_chalk9.default.cyan("Signed in as")} ${import_chalk9.default.bold(who)}`);
1162
1447
  if (creds.email && creds.email !== who) console.log(` Email: ${creds.email}`);
1163
1448
  if (creds.apiblazeUserId) console.log(` User ID: ${creds.apiblazeUserId}`);
1164
1449
  if (creds.teamName || creds.teamId) {
1165
- console.log(` Team: ${import_chalk8.default.bold(creds.teamName ?? creds.teamId)}${creds.teamName && creds.teamId ? import_chalk8.default.gray(` (${creds.teamId})`) : ""}`);
1450
+ console.log(` Team: ${import_chalk9.default.bold(creds.teamName ?? creds.teamId)}${creds.teamName && creds.teamId ? import_chalk9.default.gray(` (${creds.teamId})`) : ""}`);
1166
1451
  }
1167
1452
  if (expired) {
1168
- console.log(import_chalk8.default.yellow("\n\u26A0 Session expired. Run `apiblaze login` to re-authenticate."));
1453
+ console.log(import_chalk9.default.yellow("\n\u26A0 Session expired. Run `apiblaze login` to re-authenticate."));
1169
1454
  }
1170
1455
  }
1171
1456
 
1172
1457
  // src/commands/team.ts
1173
- var import_chalk9 = __toESM(require("chalk"));
1174
- init_auth();
1175
- init_api();
1458
+ var import_chalk10 = __toESM(require("chalk"));
1176
1459
  function fail3(message) {
1177
- console.error(import_chalk9.default.red(`Error: ${message}`));
1460
+ console.error(import_chalk10.default.red(`Error: ${message}`));
1178
1461
  process.exit(1);
1179
1462
  }
1180
1463
  async function runTeam(arg) {
@@ -1196,7 +1479,7 @@ async function runTeam(arg) {
1196
1479
  }
1197
1480
  } else if (teams.length === 1) {
1198
1481
  chosen = teams[0];
1199
- console.log(`${import_chalk9.default.cyan("\u2192")} You only have one team: ${import_chalk9.default.bold(chosen.name)}`);
1482
+ console.log(`${import_chalk10.default.cyan("\u2192")} You only have one team: ${import_chalk10.default.bold(chosen.name)}`);
1200
1483
  } else if (process.stdin.isTTY) {
1201
1484
  const { default: inquirer2 } = await import("inquirer");
1202
1485
  const { picked } = await inquirer2.prompt([{
@@ -1211,46 +1494,43 @@ async function runTeam(arg) {
1211
1494
  fail3(`Specify a team: \`apiblaze team <name|id>\`. Available: ${teams.map((t) => t.name).join(", ")}.`);
1212
1495
  }
1213
1496
  saveCredentials({ ...creds, teamId: chosen.teamId, teamName: chosen.name });
1214
- console.log(import_chalk9.default.green(`
1215
- \u2714 Active team: ${import_chalk9.default.bold(chosen.name)}`));
1497
+ console.log(import_chalk10.default.green(`
1498
+ \u2714 Active team: ${import_chalk10.default.bold(chosen.name)}`));
1216
1499
  }
1217
1500
 
1218
1501
  // src/commands/authz.ts
1219
- var import_chalk11 = __toESM(require("chalk"));
1220
- init_auth();
1221
- init_api();
1502
+ var import_chalk12 = __toESM(require("chalk"));
1222
1503
 
1223
1504
  // src/lib/agent-chat.ts
1224
1505
  var import_readline = __toESM(require("readline"));
1225
- var import_chalk10 = __toESM(require("chalk"));
1226
- init_api();
1506
+ var import_chalk11 = __toESM(require("chalk"));
1227
1507
  async function runAgentChatRepl(opts) {
1228
1508
  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));
1509
+ console.log("\n" + import_chalk11.default.cyan.bold(title));
1510
+ if (subtitle) console.log(import_chalk11.default.dim(subtitle));
1231
1511
  const messages = [];
1232
1512
  const ctx = { proposal: null, lastData: null, log: (s) => console.log(s) };
1233
1513
  const cmdByName = new Map(commands.map((c) => [c.name, c]));
1234
1514
  const footer = () => {
1235
1515
  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")
1516
+ import_chalk11.default.dim("type to chat/refine"),
1517
+ ...commands.map((c) => import_chalk11.default.dim(`/${c.name} ${c.describe}`)),
1518
+ import_chalk11.default.dim("/show"),
1519
+ import_chalk11.default.dim("/drop discard & exit")
1240
1520
  ];
1241
- return " " + import_chalk10.default.dim("[ ") + parts.join(import_chalk10.default.dim(" \xB7 ")) + import_chalk10.default.dim(" ]");
1521
+ return " " + import_chalk11.default.dim("[ ") + parts.join(import_chalk11.default.dim(" \xB7 ")) + import_chalk11.default.dim(" ]");
1242
1522
  };
1243
1523
  async function turn(content) {
1244
1524
  messages.push({ role: "user", content });
1245
- process.stdout.write(import_chalk10.default.dim(" \u2026thinking\n"));
1525
+ process.stdout.write(import_chalk11.default.dim(" \u2026thinking\n"));
1246
1526
  const { status, data } = await agentCall(endpoint, "POST", { messages, ...buildBody(messages) });
1247
1527
  if (status === 402) {
1248
- console.log(import_chalk10.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
1528
+ console.log(import_chalk11.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
1249
1529
  messages.pop();
1250
1530
  return;
1251
1531
  }
1252
1532
  if (status >= 400 || !data) {
1253
- console.log(import_chalk10.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
1533
+ console.log(import_chalk11.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
1254
1534
  `));
1255
1535
  messages.pop();
1256
1536
  return;
@@ -1258,20 +1538,20 @@ async function runAgentChatRepl(opts) {
1258
1538
  ctx.lastData = data;
1259
1539
  const reply = data.reply ?? data.message ?? "(no reply)";
1260
1540
  messages.push({ role: "assistant", content: reply });
1261
- console.log("\n" + import_chalk10.default.cyan("agent \u203A ") + reply + "\n");
1541
+ console.log("\n" + import_chalk11.default.cyan("agent \u203A ") + reply + "\n");
1262
1542
  if (data.proposal) {
1263
1543
  ctx.proposal = data.proposal;
1264
1544
  const summary = summarizeProposal?.(data);
1265
- if (summary) console.log(import_chalk10.default.yellow(" " + summary));
1545
+ if (summary) console.log(import_chalk11.default.yellow(" " + summary));
1266
1546
  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.`));
1547
+ if (ready) console.log(import_chalk11.default.dim(` Ready \u2014 ${ready} to make it official, or keep refining.`));
1268
1548
  console.log();
1269
1549
  }
1270
1550
  const llm = data.llm;
1271
1551
  if (llm) {
1272
1552
  const credits = data.credits_remaining;
1273
1553
  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}`));
1554
+ console.log(import_chalk11.default.dim(` ~$${(llm.cost_estimate ?? 0).toFixed(4)} \xB7 ${llm.model ?? "?"}${left}`));
1275
1555
  }
1276
1556
  }
1277
1557
  const rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout });
@@ -1279,26 +1559,26 @@ async function runAgentChatRepl(opts) {
1279
1559
  await turn(seedPrompt);
1280
1560
  for (; ; ) {
1281
1561
  console.log(footer());
1282
- const line = (await ask(import_chalk10.default.green("you \u203A "))).trim();
1562
+ const line = (await ask(import_chalk11.default.green("you \u203A "))).trim();
1283
1563
  if (!line) continue;
1284
1564
  if (line === "/drop" || line === "/exit" || line === "/quit") {
1285
- console.log(import_chalk10.default.dim(" Dropped \u2014 nothing applied.\n"));
1565
+ console.log(import_chalk11.default.dim(" Dropped \u2014 nothing applied.\n"));
1286
1566
  break;
1287
1567
  }
1288
1568
  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"));
1569
+ if (!ctx.proposal) console.log(import_chalk11.default.dim(" No proposal yet \u2014 keep chatting until the agent proposes one.\n"));
1290
1570
  else console.log("\n" + JSON.stringify(ctx.proposal, null, 2) + "\n");
1291
1571
  continue;
1292
1572
  }
1293
1573
  if (line.startsWith("/")) {
1294
1574
  const cmd = cmdByName.get(line.slice(1));
1295
1575
  if (!cmd) {
1296
- console.log(import_chalk10.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
1576
+ console.log(import_chalk11.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
1297
1577
  `));
1298
1578
  continue;
1299
1579
  }
1300
1580
  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"));
1581
+ console.log(import_chalk11.default.yellow(" No proposal yet \u2014 keep chatting until the agent generates one, then try again.\n"));
1302
1582
  continue;
1303
1583
  }
1304
1584
  await cmd.run(ctx);
@@ -1327,11 +1607,11 @@ async function runAuthz(projectArg, apiVersionArg) {
1327
1607
  if (!enable) {
1328
1608
  const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant)}`, "POST", proposal.model);
1329
1609
  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"));
1610
+ 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"));
1331
1611
  return;
1332
1612
  }
1333
1613
  if (m.status >= 400) {
1334
- ctx.log(import_chalk11.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
1614
+ ctx.log(import_chalk12.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
1335
1615
  `));
1336
1616
  return;
1337
1617
  }
@@ -1360,14 +1640,14 @@ async function runAuthz(projectArg, apiVersionArg) {
1360
1640
  if (enable) {
1361
1641
  const e = await agentCall(`/${projectId}/${apiVersion}/config`, "PATCH", { authorization: { enforce_authorization: true }, tenant });
1362
1642
  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.
1643
+ 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.
1364
1644
  `));
1365
1645
  return;
1366
1646
  }
1367
- ctx.log(import_chalk11.default.green(` \u2713 Enforcement ON \u2014 ${ok} route(s) + project switch on${fail4 ? `, ${fail4} failed` : ""}.
1647
+ ctx.log(import_chalk12.default.green(` \u2713 Enforcement ON \u2014 ${ok} route(s) + project switch on${fail4 ? `, ${fail4} failed` : ""}.
1368
1648
  `));
1369
1649
  } else {
1370
- ctx.log(import_chalk11.default.green(` \u2713 Published (shadow): model + ${ok} route(s)${fail4 ? `, ${fail4} failed` : ""}. Review, then /enable.
1650
+ ctx.log(import_chalk12.default.green(` \u2713 Published (shadow): model + ${ok} route(s)${fail4 ? `, ${fail4} failed` : ""}. Review, then /enable.
1371
1651
  `));
1372
1652
  }
1373
1653
  }
@@ -1392,9 +1672,7 @@ async function runAuthz(projectArg, apiVersionArg) {
1392
1672
  }
1393
1673
 
1394
1674
  // src/commands/openapi.ts
1395
- var import_chalk12 = __toESM(require("chalk"));
1396
- init_auth();
1397
- init_api();
1675
+ var import_chalk13 = __toESM(require("chalk"));
1398
1676
  async function runOpenapi(projectArg, apiVersionArg) {
1399
1677
  const creds = loadCredentials();
1400
1678
  if (!creds) throw new Error("Not authenticated. Run `apiblaze login` first.");
@@ -1403,10 +1681,10 @@ async function runOpenapi(projectArg, apiVersionArg) {
1403
1681
  if (!match) throw new Error(`Project "${projectArg}" not found in your active team. Run \`apiblaze projects\`.`);
1404
1682
  const projectId = match.projectId;
1405
1683
  const apiVersion = apiVersionArg || match.apiVersion;
1406
- console.log(import_chalk12.default.dim("Gathering captured traffic samples\u2026"));
1684
+ console.log(import_chalk13.default.dim("Gathering captured traffic samples\u2026"));
1407
1685
  const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
1408
1686
  if (routesRes.status >= 400) {
1409
- console.log(import_chalk12.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
1687
+ console.log(import_chalk13.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
1410
1688
  return;
1411
1689
  }
1412
1690
  const routes = routesRes.data?.routes ?? (Array.isArray(routesRes.data) ? routesRes.data : []);
@@ -1419,25 +1697,25 @@ async function runOpenapi(projectArg, apiVersionArg) {
1419
1697
  }
1420
1698
  const sampleIds = [...ids];
1421
1699
  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."));
1700
+ console.log(import_chalk13.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
1423
1701
  return;
1424
1702
  }
1425
1703
  async function publish(ctx) {
1426
1704
  const patch = ctx.proposal.patch;
1427
1705
  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"));
1706
+ ctx.log(import_chalk13.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
1429
1707
  return;
1430
1708
  }
1431
1709
  const specSource = ctx.lastData?.spec_source ?? "unknown";
1432
1710
  const endpoint = specSource === "github" ? "open-pr" : "publish-openapi";
1433
1711
  const pub = await agentCall(`/projects/${projectId}/${apiVersion}/samples/${endpoint}`, "POST", { patch, sample_ids_used: sampleIds });
1434
1712
  if (pub.status >= 400) {
1435
- ctx.log(import_chalk12.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
1713
+ ctx.log(import_chalk13.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
1436
1714
  `));
1437
1715
  return;
1438
1716
  }
1439
1717
  const prUrl = pub.data?.pr_url;
1440
- ctx.log(import_chalk12.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
1718
+ ctx.log(import_chalk13.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
1441
1719
  ` : " \u2713 Published updated OpenAPI spec.\n"));
1442
1720
  }
1443
1721
  await runAgentChatRepl({
@@ -1458,9 +1736,7 @@ async function runOpenapi(projectArg, apiVersionArg) {
1458
1736
  }
1459
1737
 
1460
1738
  // src/commands/mcp.ts
1461
- var import_chalk13 = __toESM(require("chalk"));
1462
- init_auth();
1463
- init_api();
1739
+ var import_chalk14 = __toESM(require("chalk"));
1464
1740
  async function runMcp(projectArg, apiVersionArg, opts) {
1465
1741
  const creds = loadCredentials();
1466
1742
  if (!creds) throw new Error("Not authenticated. Run `apiblaze login` first.");
@@ -1474,11 +1750,11 @@ async function runMcp(projectArg, apiVersionArg, opts) {
1474
1750
  const spec = ctx.proposal;
1475
1751
  const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec });
1476
1752
  if (pub.status >= 400) {
1477
- ctx.log(import_chalk13.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
1753
+ ctx.log(import_chalk14.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
1478
1754
  `));
1479
1755
  return;
1480
1756
  }
1481
- ctx.log(import_chalk13.default.green(` \u2713 Published MCP server \u2014 ${projectId}.mcp.apiblaze.com/${apiVersion}/${environment}.
1757
+ ctx.log(import_chalk14.default.green(` \u2713 Published MCP server \u2014 ${projectId}.mcp.apiblaze.com/${apiVersion}/${environment}.
1482
1758
  `));
1483
1759
  }
1484
1760
  await runAgentChatRepl({
@@ -1510,7 +1786,7 @@ program.command("login").description("Authenticate with APIblaze").action(async
1510
1786
  process.exit(1);
1511
1787
  }
1512
1788
  });
1513
- 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 slug (anonymous create; defaults to the proxy name)").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) => {
1789
+ 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) => {
1514
1790
  try {
1515
1791
  await runCreate(opts);
1516
1792
  } catch (err) {
@@ -1582,14 +1858,14 @@ program.command("mcp").description("Design an MCP server from the spec + traffic
1582
1858
  process.exit(1);
1583
1859
  }
1584
1860
  });
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) => {
1861
+ 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) => {
1586
1862
  try {
1587
1863
  const resolved = parseInt(port ?? opts.port, 10);
1588
1864
  if (Number.isNaN(resolved)) {
1589
- console.error(import_chalk14.default.red(`Invalid port: ${port ?? opts.port}`));
1865
+ console.error(import_chalk15.default.red(`Invalid port: ${port ?? opts.port}`));
1590
1866
  process.exit(1);
1591
1867
  }
1592
- await runDev({ port: resolved });
1868
+ await runDev({ port: resolved, captureFile: opts.captureFile });
1593
1869
  } catch (err) {
1594
1870
  printError(err);
1595
1871
  process.exit(1);
@@ -1609,23 +1885,27 @@ Examples:
1609
1885
  $ npx apiblaze login
1610
1886
  $ npx apiblaze create --name myapi --target https://api.example.com --auth api_key
1611
1887
 
1888
+ # Dev tunnel \u2014 auto-creates a proxy if none point here, and captures
1889
+ # traffic (full headers + body, secrets masked) until your server is up:
1890
+ $ npx apiblaze dev 3000
1891
+ $ npx apiblaze dev 3000 --capture-file traffic.jsonl
1892
+
1612
1893
  # Manage:
1613
1894
  $ npx apiblaze whoami
1614
1895
  $ npx apiblaze projects
1615
1896
  $ npx apiblaze team
1616
- $ npx apiblaze dev 3000
1617
1897
  $ npx apiblaze logout
1618
1898
  `
1619
1899
  );
1620
1900
  function printError(err) {
1621
1901
  if (err instanceof ApiError) {
1622
- console.error(import_chalk14.default.red(`
1902
+ console.error(import_chalk15.default.red(`
1623
1903
  API error (${err.status}): ${err.message}`));
1624
1904
  } else if (err instanceof Error) {
1625
- console.error(import_chalk14.default.red(`
1905
+ console.error(import_chalk15.default.red(`
1626
1906
  Error: ${err.message}`));
1627
1907
  } else {
1628
- console.error(import_chalk14.default.red("\nUnknown error"));
1908
+ console.error(import_chalk15.default.red("\nUnknown error"));
1629
1909
  }
1630
1910
  }
1631
1911
  program.parse(process.argv);