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.
- package/README.md +14 -0
- package/dist/index.js +600 -320
- 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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
95
|
-
|
|
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/
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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(
|
|
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(`${
|
|
253
|
-
console.log(` ${
|
|
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(`${
|
|
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(
|
|
296
|
+
spinner.succeed(import_chalk2.default.green("Authorized!"));
|
|
304
297
|
if (githubHandle) {
|
|
305
|
-
console.log(`${
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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(
|
|
321
|
+
console.log(import_chalk2.default.green("\n\u2714 Logged in successfully!"));
|
|
342
322
|
}
|
|
343
323
|
|
|
344
324
|
// src/commands/dev.ts
|
|
345
|
-
var
|
|
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
|
|
331
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
353
332
|
var METHOD_COLORS = {
|
|
354
|
-
GET:
|
|
355
|
-
POST:
|
|
356
|
-
PUT:
|
|
357
|
-
PATCH:
|
|
358
|
-
DELETE:
|
|
359
|
-
HEAD:
|
|
360
|
-
OPTIONS:
|
|
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()] ??
|
|
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
|
|
369
|
-
if (status >= 400) return
|
|
370
|
-
if (status >= 300) return
|
|
371
|
-
return
|
|
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
|
|
376
|
-
if (latency < 500) return
|
|
377
|
-
return
|
|
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
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
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(
|
|
833
|
+
console.error(import_chalk4.default.red("Not logged in. Run `apiblaze login` first."));
|
|
522
834
|
process.exit(1);
|
|
523
835
|
}
|
|
524
|
-
|
|
525
|
-
if (!
|
|
526
|
-
|
|
527
|
-
|
|
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
|
-
|
|
541
|
-
|
|
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 (
|
|
544
|
-
console.
|
|
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 ===
|
|
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 "${
|
|
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: `${
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
625
|
-
console.log(
|
|
626
|
-
console.log(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(`${
|
|
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(
|
|
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(`${
|
|
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(
|
|
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(` ${
|
|
1024
|
+
console.log(` ${import_chalk5.default.bold(p.projectName.padEnd(width))} ${import_chalk5.default.dim("v" + p.apiVersion)}`);
|
|
699
1025
|
}
|
|
700
|
-
console.log(
|
|
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
|
|
706
|
-
var
|
|
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(
|
|
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(` ${
|
|
1063
|
+
console.log(` ${import_chalk6.default.dim("Try it with curl:")}`);
|
|
740
1064
|
if (apiKey) {
|
|
741
|
-
console.log(` ${
|
|
742
|
-
console.log(` ${
|
|
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(` ${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
1132
|
+
console.log(import_chalk6.default.dim(" (could not verify availability; continuing)"));
|
|
809
1133
|
}
|
|
810
|
-
console.log(`${
|
|
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(
|
|
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(`${
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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(` ${
|
|
916
|
-
if (devPortal) console.log(` ${
|
|
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(` ${
|
|
920
|
-
console.log(` ${
|
|
921
|
-
console.log(
|
|
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(
|
|
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(
|
|
934
|
-
console.log(
|
|
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 =
|
|
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(
|
|
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(
|
|
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(` ${
|
|
1031
|
-
if (result.portal) console.log(` ${
|
|
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(` ${
|
|
1035
|
-
console.log(` ${
|
|
1036
|
-
console.log(
|
|
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(` ${
|
|
1042
|
-
console.log(` ${
|
|
1043
|
-
console.log(
|
|
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
|
|
1050
|
-
init_auth();
|
|
1051
|
-
init_api();
|
|
1338
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
1052
1339
|
function fail2(message) {
|
|
1053
|
-
console.error(
|
|
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(
|
|
1110
|
-
\u2714 Claimed into ${
|
|
1111
|
-
if (result.project_id) console.log(` Project: ${
|
|
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 ${
|
|
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
|
|
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(
|
|
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(
|
|
1417
|
+
console.log(import_chalk8.default.green(`\u2714 Logged out${who ? ` (${who})` : ""}.`));
|
|
1132
1418
|
}
|
|
1133
1419
|
|
|
1134
1420
|
// src/commands/whoami.ts
|
|
1135
|
-
var
|
|
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(
|
|
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(`${
|
|
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: ${
|
|
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(
|
|
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
|
|
1174
|
-
init_auth();
|
|
1175
|
-
init_api();
|
|
1458
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
1176
1459
|
function fail3(message) {
|
|
1177
|
-
console.error(
|
|
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(`${
|
|
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(
|
|
1215
|
-
\u2714 Active team: ${
|
|
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
|
|
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
|
|
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" +
|
|
1230
|
-
if (subtitle) console.log(
|
|
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
|
-
|
|
1237
|
-
...commands.map((c) =>
|
|
1238
|
-
|
|
1239
|
-
|
|
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 " " +
|
|
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(
|
|
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(
|
|
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(
|
|
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" +
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
1753
|
+
ctx.log(import_chalk14.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
1478
1754
|
`));
|
|
1479
1755
|
return;
|
|
1480
1756
|
}
|
|
1481
|
-
ctx.log(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
1905
|
+
console.error(import_chalk15.default.red(`
|
|
1626
1906
|
Error: ${err.message}`));
|
|
1627
1907
|
} else {
|
|
1628
|
-
console.error(
|
|
1908
|
+
console.error(import_chalk15.default.red("\nUnknown error"));
|
|
1629
1909
|
}
|
|
1630
1910
|
}
|
|
1631
1911
|
program.parse(process.argv);
|