apiblaze 0.1.10 → 0.1.13
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/dist/index.js +184 -205
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -69,9 +69,6 @@ function getAccessToken() {
|
|
|
69
69
|
}
|
|
70
70
|
return creds.accessToken;
|
|
71
71
|
}
|
|
72
|
-
function getApiblazeDir() {
|
|
73
|
-
return APIBLAZE_DIR;
|
|
74
|
-
}
|
|
75
72
|
var fs, os, path, APIBLAZE_DIR, CREDENTIALS_PATH;
|
|
76
73
|
var init_auth = __esm({
|
|
77
74
|
"src/lib/auth.ts"() {
|
|
@@ -95,9 +92,9 @@ __export(api_exports, {
|
|
|
95
92
|
getTeams: () => getTeams,
|
|
96
93
|
putDevTunnel: () => putDevTunnel
|
|
97
94
|
});
|
|
98
|
-
async function apiFetch(
|
|
95
|
+
async function apiFetch(path2, options = {}) {
|
|
99
96
|
const token = getAccessToken();
|
|
100
|
-
const url = `${DASHBOARD_BASE}${
|
|
97
|
+
const url = `${DASHBOARD_BASE}${path2}`;
|
|
101
98
|
const res = await fetch(url, {
|
|
102
99
|
...options,
|
|
103
100
|
headers: {
|
|
@@ -150,8 +147,11 @@ async function putDevTunnel(payload) {
|
|
|
150
147
|
body: JSON.stringify(payload)
|
|
151
148
|
});
|
|
152
149
|
}
|
|
153
|
-
async function deleteDevTunnel() {
|
|
154
|
-
return apiFetch("/api/cli/dev-tunnel", {
|
|
150
|
+
async function deleteDevTunnel(restore) {
|
|
151
|
+
return apiFetch("/api/cli/dev-tunnel", {
|
|
152
|
+
method: "DELETE",
|
|
153
|
+
body: JSON.stringify({ restore })
|
|
154
|
+
});
|
|
155
155
|
}
|
|
156
156
|
var DASHBOARD_BASE;
|
|
157
157
|
var init_api = __esm({
|
|
@@ -285,129 +285,13 @@ ${import_chalk.default.cyan("\u2192")} Team: ${import_chalk.default.bold(teamNam
|
|
|
285
285
|
|
|
286
286
|
// src/commands/dev.ts
|
|
287
287
|
var import_chalk3 = __toESM(require("chalk"));
|
|
288
|
-
var
|
|
288
|
+
var import_ora2 = __toESM(require("ora"));
|
|
289
289
|
var import_inquirer = __toESM(require("inquirer"));
|
|
290
290
|
init_auth();
|
|
291
291
|
init_api();
|
|
292
292
|
|
|
293
|
-
// src/lib/cloudflared.ts
|
|
294
|
-
var fs2 = __toESM(require("fs"));
|
|
295
|
-
var path2 = __toESM(require("path"));
|
|
296
|
-
var readline = __toESM(require("readline"));
|
|
297
|
-
var import_child_process = require("child_process");
|
|
298
|
-
var import_ora2 = __toESM(require("ora"));
|
|
299
|
-
init_auth();
|
|
300
|
-
var TUNNEL_URL_RE = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/;
|
|
301
|
-
var DOWNLOAD_TIMEOUT_MS = 3e4;
|
|
302
|
-
var TUNNEL_START_TIMEOUT_MS = 3e4;
|
|
303
|
-
function getCloudflaredFilename() {
|
|
304
|
-
const platform = process.platform;
|
|
305
|
-
const arch = process.arch;
|
|
306
|
-
if (platform === "darwin" && arch === "arm64") return "cloudflared-darwin-arm64";
|
|
307
|
-
if (platform === "darwin" && arch === "x64") return "cloudflared-darwin-amd64";
|
|
308
|
-
if (platform === "linux" && arch === "arm64") return "cloudflared-linux-arm64";
|
|
309
|
-
if (platform === "linux" && arch === "x64") return "cloudflared-linux-amd64";
|
|
310
|
-
if (platform === "win32" && arch === "x64") return "cloudflared-windows-amd64.exe";
|
|
311
|
-
throw new Error(`Unsupported platform: ${platform} ${arch}`);
|
|
312
|
-
}
|
|
313
|
-
function getCloudflaredBinPath() {
|
|
314
|
-
const filename = process.platform === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
315
|
-
return path2.join(getApiblazeDir(), "bin", filename);
|
|
316
|
-
}
|
|
317
|
-
function isExecutable(filePath) {
|
|
318
|
-
try {
|
|
319
|
-
fs2.accessSync(filePath, fs2.constants.X_OK);
|
|
320
|
-
return true;
|
|
321
|
-
} catch {
|
|
322
|
-
return false;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
async function downloadCloudflared(binPath) {
|
|
326
|
-
const filename = getCloudflaredFilename();
|
|
327
|
-
const url = `https://github.com/cloudflare/cloudflared/releases/latest/download/${filename}`;
|
|
328
|
-
const res = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
|
|
329
|
-
if (!res.ok || !res.body) {
|
|
330
|
-
throw new Error(`Failed to download cloudflared: ${res.status} ${res.statusText}`);
|
|
331
|
-
}
|
|
332
|
-
fs2.mkdirSync(path2.dirname(binPath), { recursive: true });
|
|
333
|
-
const tmpPath = `${binPath}.tmp`;
|
|
334
|
-
const writer = fs2.createWriteStream(tmpPath);
|
|
335
|
-
const reader = res.body.getReader();
|
|
336
|
-
try {
|
|
337
|
-
while (true) {
|
|
338
|
-
const { done, value } = await reader.read();
|
|
339
|
-
if (done) break;
|
|
340
|
-
await new Promise((resolve, reject) => {
|
|
341
|
-
writer.write(value, (err) => err ? reject(err) : resolve());
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
} finally {
|
|
345
|
-
reader.releaseLock();
|
|
346
|
-
}
|
|
347
|
-
await new Promise((resolve, reject) => {
|
|
348
|
-
writer.end((err) => err ? reject(err) : resolve());
|
|
349
|
-
});
|
|
350
|
-
fs2.renameSync(tmpPath, binPath);
|
|
351
|
-
if (process.platform !== "win32") {
|
|
352
|
-
fs2.chmodSync(binPath, 493);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
async function ensureCloudflared() {
|
|
356
|
-
const binPath = getCloudflaredBinPath();
|
|
357
|
-
if (fs2.existsSync(binPath) && isExecutable(binPath)) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
const spinner = (0, import_ora2.default)("Downloading cloudflared...").start();
|
|
361
|
-
try {
|
|
362
|
-
await downloadCloudflared(binPath);
|
|
363
|
-
spinner.succeed("cloudflared downloaded.");
|
|
364
|
-
} catch (err) {
|
|
365
|
-
spinner.fail("Failed to download cloudflared.");
|
|
366
|
-
throw err;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
function spawnCloudflared(port) {
|
|
370
|
-
return new Promise((resolve, reject) => {
|
|
371
|
-
const binPath = getCloudflaredBinPath();
|
|
372
|
-
const proc = (0, import_child_process.spawn)(binPath, ["tunnel", "--url", `http://localhost:${port}`], {
|
|
373
|
-
stdio: ["ignore", "ignore", "pipe"]
|
|
374
|
-
});
|
|
375
|
-
const timer = setTimeout(() => {
|
|
376
|
-
proc.kill();
|
|
377
|
-
reject(new Error("Timed out waiting for cloudflared tunnel URL (30s). Is something already running on the port?"));
|
|
378
|
-
}, TUNNEL_START_TIMEOUT_MS);
|
|
379
|
-
const rl = readline.createInterface({ input: proc.stderr });
|
|
380
|
-
rl.on("line", (line) => {
|
|
381
|
-
const match = line.match(TUNNEL_URL_RE);
|
|
382
|
-
if (match) {
|
|
383
|
-
clearTimeout(timer);
|
|
384
|
-
rl.close();
|
|
385
|
-
resolve({ process: proc, tunnelUrl: match[0] });
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
proc.on("error", (err) => {
|
|
389
|
-
clearTimeout(timer);
|
|
390
|
-
reject(new Error(`Failed to start cloudflared: ${err.message}`));
|
|
391
|
-
});
|
|
392
|
-
proc.on("exit", (code) => {
|
|
393
|
-
clearTimeout(timer);
|
|
394
|
-
if (code !== null && code !== 0) {
|
|
395
|
-
reject(new Error(`cloudflared exited with code ${code}`));
|
|
396
|
-
}
|
|
397
|
-
});
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
function killCloudflared(proc) {
|
|
401
|
-
try {
|
|
402
|
-
proc.kill("SIGTERM");
|
|
403
|
-
} catch {
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
293
|
// src/lib/traffic.ts
|
|
408
|
-
var import_ws = __toESM(require("ws"));
|
|
409
294
|
var import_chalk2 = __toESM(require("chalk"));
|
|
410
|
-
init_auth();
|
|
411
295
|
var METHOD_COLORS = {
|
|
412
296
|
GET: import_chalk2.default.cyan,
|
|
413
297
|
POST: import_chalk2.default.green,
|
|
@@ -439,35 +323,136 @@ function formatLogLine(entry) {
|
|
|
439
323
|
const ts = now.toTimeString().slice(0, 8);
|
|
440
324
|
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)})`)}`;
|
|
441
325
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
326
|
+
|
|
327
|
+
// src/lib/tunnel-client.ts
|
|
328
|
+
var import_ws = __toESM(require("ws"));
|
|
329
|
+
var CHUNK_BYTES = 512 * 1024;
|
|
330
|
+
var PING_INTERVAL_MS = 6e4;
|
|
331
|
+
var MAX_RECONNECT_DELAY_MS = 15e3;
|
|
332
|
+
var STRIP_HEADERS = /* @__PURE__ */ new Set([
|
|
333
|
+
"host",
|
|
334
|
+
"content-length",
|
|
335
|
+
"connection",
|
|
336
|
+
"keep-alive",
|
|
337
|
+
"transfer-encoding",
|
|
338
|
+
"upgrade",
|
|
339
|
+
"proxy-connection",
|
|
340
|
+
"expect"
|
|
341
|
+
]);
|
|
342
|
+
function stripHeaders(headers) {
|
|
343
|
+
const out = {};
|
|
344
|
+
for (const key of Object.keys(headers)) {
|
|
345
|
+
if (!STRIP_HEADERS.has(key.toLowerCase())) out[key] = headers[key];
|
|
346
|
+
}
|
|
347
|
+
return out;
|
|
348
|
+
}
|
|
349
|
+
function startTunnelClient(opts) {
|
|
350
|
+
const target = `http://127.0.0.1:${opts.localPort}`;
|
|
351
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
352
|
+
let ws = null;
|
|
353
|
+
let closed = false;
|
|
354
|
+
let reconnects = 0;
|
|
355
|
+
let pingTimer;
|
|
447
356
|
function connect() {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
357
|
+
const url = `${opts.connectUrl}?project=${encodeURIComponent(opts.projectId)}&token=${encodeURIComponent(opts.token)}`;
|
|
358
|
+
const socket = new import_ws.default(url);
|
|
359
|
+
ws = socket;
|
|
360
|
+
socket.on("open", () => {
|
|
361
|
+
reconnects = 0;
|
|
362
|
+
opts.onStatus?.("connected");
|
|
363
|
+
pingTimer = setInterval(() => {
|
|
364
|
+
try {
|
|
365
|
+
socket.send("ping");
|
|
366
|
+
} catch {
|
|
367
|
+
}
|
|
368
|
+
}, PING_INTERVAL_MS);
|
|
455
369
|
});
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
} else {
|
|
461
|
-
console.warn(import_chalk2.default.yellow("\nTraffic stream disconnected. Stop and restart `apiblaze dev` to reconnect."));
|
|
462
|
-
}
|
|
370
|
+
socket.on("message", (data) => handleFrame(socket, data.toString()));
|
|
371
|
+
socket.on("close", () => {
|
|
372
|
+
if (pingTimer) clearInterval(pingTimer);
|
|
373
|
+
if (!closed) scheduleReconnect();
|
|
463
374
|
});
|
|
464
|
-
|
|
465
|
-
console.warn(import_chalk2.default.yellow(`
|
|
466
|
-
Traffic stream error: ${err.message}`));
|
|
375
|
+
socket.on("error", () => {
|
|
467
376
|
});
|
|
468
|
-
return ws;
|
|
469
377
|
}
|
|
470
|
-
|
|
378
|
+
function scheduleReconnect() {
|
|
379
|
+
const delay = Math.min(1e3 * 2 ** reconnects, MAX_RECONNECT_DELAY_MS);
|
|
380
|
+
reconnects++;
|
|
381
|
+
opts.onStatus?.(`disconnected \u2014 reconnecting in ${Math.round(delay / 1e3)}s`);
|
|
382
|
+
setTimeout(() => {
|
|
383
|
+
if (!closed) connect();
|
|
384
|
+
}, delay);
|
|
385
|
+
}
|
|
386
|
+
function handleFrame(socket, raw) {
|
|
387
|
+
let frame;
|
|
388
|
+
try {
|
|
389
|
+
frame = JSON.parse(raw);
|
|
390
|
+
} catch {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (frame.type === "req") {
|
|
394
|
+
inflight.set(frame.id, {
|
|
395
|
+
method: frame.method,
|
|
396
|
+
path: frame.path,
|
|
397
|
+
headers: frame.headers ?? {},
|
|
398
|
+
chunks: [],
|
|
399
|
+
start: Date.now()
|
|
400
|
+
});
|
|
401
|
+
if (frame.bodyLen === 0) finish(socket, frame.id);
|
|
402
|
+
} else if (frame.type === "chunk") {
|
|
403
|
+
const f = inflight.get(frame.id);
|
|
404
|
+
if (!f) return;
|
|
405
|
+
f.chunks.push(Buffer.from(frame.data ?? "", "base64"));
|
|
406
|
+
if (frame.final) finish(socket, frame.id);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function finish(socket, id) {
|
|
410
|
+
const f = inflight.get(id);
|
|
411
|
+
if (!f) return;
|
|
412
|
+
inflight.delete(id);
|
|
413
|
+
void forward(socket, id, f);
|
|
414
|
+
}
|
|
415
|
+
async function forward(socket, id, f) {
|
|
416
|
+
const body = Buffer.concat(f.chunks);
|
|
417
|
+
const init = { method: f.method, headers: stripHeaders(f.headers) };
|
|
418
|
+
if (body.length) init.body = body;
|
|
419
|
+
let status = 502;
|
|
420
|
+
try {
|
|
421
|
+
const resp = await fetch(target + f.path, init);
|
|
422
|
+
status = resp.status;
|
|
423
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
424
|
+
const headers = {};
|
|
425
|
+
resp.headers.forEach((value, key) => {
|
|
426
|
+
if (!STRIP_HEADERS.has(key.toLowerCase())) headers[key] = value;
|
|
427
|
+
});
|
|
428
|
+
send(socket, { id, type: "res", status, headers, bodyLen: buf.length });
|
|
429
|
+
for (let off = 0, seq = 0; off < buf.length; off += CHUNK_BYTES) {
|
|
430
|
+
const slice = buf.subarray(off, Math.min(off + CHUNK_BYTES, buf.length));
|
|
431
|
+
send(socket, { id, type: "chunk", seq: seq++, data: slice.toString("base64"), final: off + CHUNK_BYTES >= buf.length });
|
|
432
|
+
}
|
|
433
|
+
} catch (err) {
|
|
434
|
+
const message = err?.cause?.message || err?.message || String(err);
|
|
435
|
+
send(socket, { id, type: "err", message });
|
|
436
|
+
}
|
|
437
|
+
opts.onEntry({ method: f.method, path: f.path, status, latency: Date.now() - f.start });
|
|
438
|
+
}
|
|
439
|
+
function send(socket, frame) {
|
|
440
|
+
try {
|
|
441
|
+
socket.send(JSON.stringify(frame));
|
|
442
|
+
} catch {
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
connect();
|
|
446
|
+
return {
|
|
447
|
+
close() {
|
|
448
|
+
closed = true;
|
|
449
|
+
if (pingTimer) clearInterval(pingTimer);
|
|
450
|
+
try {
|
|
451
|
+
ws?.close();
|
|
452
|
+
} catch {
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
};
|
|
471
456
|
}
|
|
472
457
|
|
|
473
458
|
// src/commands/dev.ts
|
|
@@ -502,7 +487,7 @@ async function runDev(options) {
|
|
|
502
487
|
}
|
|
503
488
|
let targets;
|
|
504
489
|
{
|
|
505
|
-
const spinner = (0,
|
|
490
|
+
const spinner = (0, import_ora2.default)("Fetching your localhost projects...").start();
|
|
506
491
|
try {
|
|
507
492
|
targets = await getLocalhostTargets(teamId);
|
|
508
493
|
spinner.stop();
|
|
@@ -512,8 +497,8 @@ async function runDev(options) {
|
|
|
512
497
|
}
|
|
513
498
|
}
|
|
514
499
|
if (targets.length === 0) {
|
|
515
|
-
console.log(import_chalk3.default.yellow("No projects found with
|
|
516
|
-
console.log("Set a project's upstream target to localhost in your APIblaze dashboard, then try again.");
|
|
500
|
+
console.log(import_chalk3.default.yellow("No projects found with an internal target."));
|
|
501
|
+
console.log("Set a project's upstream target to localhost or a private IP in your APIblaze dashboard, then try again.");
|
|
517
502
|
process.exit(0);
|
|
518
503
|
}
|
|
519
504
|
let selectedTargets;
|
|
@@ -521,7 +506,7 @@ async function runDev(options) {
|
|
|
521
506
|
const { confirmed } = await import_inquirer.default.prompt([{
|
|
522
507
|
type: "confirm",
|
|
523
508
|
name: "confirmed",
|
|
524
|
-
message: `Found 1 project
|
|
509
|
+
message: `Found 1 project with an internal target \u2014 tunnel "${import_chalk3.default.bold(targets[0].projectName)}" (${targets[0].tenantName})?`,
|
|
525
510
|
default: true
|
|
526
511
|
}]);
|
|
527
512
|
if (!confirmed) {
|
|
@@ -533,7 +518,7 @@ async function runDev(options) {
|
|
|
533
518
|
const { chosen } = await import_inquirer.default.prompt([{
|
|
534
519
|
type: "checkbox",
|
|
535
520
|
name: "chosen",
|
|
536
|
-
message: `Found ${targets.length} projects
|
|
521
|
+
message: `Found ${targets.length} projects with an internal target \u2014 select which to tunnel:`,
|
|
537
522
|
choices: targets.map((t) => ({
|
|
538
523
|
name: `${import_chalk3.default.bold(t.projectName)} (${t.tenantName}) \u2014 ${t.target}`,
|
|
539
524
|
value: t,
|
|
@@ -551,66 +536,55 @@ async function runDev(options) {
|
|
|
551
536
|
Tunneling ${selectedTargets.length} project(s) to localhost:${options.port}
|
|
552
537
|
`)
|
|
553
538
|
);
|
|
554
|
-
|
|
555
|
-
let
|
|
556
|
-
let tunnelUrl;
|
|
557
|
-
{
|
|
558
|
-
const spinner = (0, import_ora3.default)("Starting Cloudflare tunnel...").start();
|
|
559
|
-
try {
|
|
560
|
-
({ process: cfProcess, tunnelUrl } = await spawnCloudflared(options.port));
|
|
561
|
-
spinner.succeed(`Tunnel active: ${import_chalk3.default.bold.cyan(tunnelUrl)}`);
|
|
562
|
-
} catch (err) {
|
|
563
|
-
spinner.fail("Failed to start cloudflared tunnel.");
|
|
564
|
-
throw err;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
539
|
+
let restore = [];
|
|
540
|
+
let connect;
|
|
567
541
|
{
|
|
568
|
-
const spinner = (0,
|
|
569
|
-
let wsUrl;
|
|
542
|
+
const spinner = (0, import_ora2.default)("Registering tunnel with APIblaze...").start();
|
|
570
543
|
try {
|
|
571
544
|
const result = await putDevTunnel({
|
|
572
|
-
tunnelUrl,
|
|
573
545
|
targets: selectedTargets.map((t) => ({ projectId: t.projectId, tenantId: t.tenantId }))
|
|
574
546
|
});
|
|
575
|
-
|
|
576
|
-
|
|
547
|
+
restore = result.restore ?? [];
|
|
548
|
+
connect = result.connect;
|
|
549
|
+
spinner.succeed("Tunnel registered.");
|
|
577
550
|
} catch (err) {
|
|
578
551
|
spinner.fail("Failed to register tunnel.");
|
|
579
|
-
killCloudflared(cfProcess);
|
|
580
552
|
throw err;
|
|
581
553
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
}
|
|
604
|
-
process.on("SIGINT", () => void cleanup());
|
|
605
|
-
process.on("SIGTERM", () => void cleanup());
|
|
606
|
-
await new Promise(() => {
|
|
554
|
+
}
|
|
555
|
+
const clients = connect.projects.map(
|
|
556
|
+
(projectId) => startTunnelClient({
|
|
557
|
+
connectUrl: connect.url,
|
|
558
|
+
token: connect.token,
|
|
559
|
+
projectId,
|
|
560
|
+
localPort: options.port,
|
|
561
|
+
onEntry: (entry) => console.log(formatLogLine(entry)),
|
|
562
|
+
onStatus: (status) => console.log(import_chalk3.default.gray(`[${projectId}] ${status}`))
|
|
563
|
+
})
|
|
564
|
+
);
|
|
565
|
+
console.log("\n" + import_chalk3.default.gray("\u2500".repeat(60)));
|
|
566
|
+
console.log(import_chalk3.default.bold("Live traffic") + import_chalk3.default.gray(" (Ctrl+C to stop)"));
|
|
567
|
+
console.log(import_chalk3.default.gray("\u2500".repeat(60)) + "\n");
|
|
568
|
+
let isCleaningUp = false;
|
|
569
|
+
async function cleanup() {
|
|
570
|
+
if (isCleaningUp) return;
|
|
571
|
+
isCleaningUp = true;
|
|
572
|
+
console.log(import_chalk3.default.gray("\n\nShutting down..."));
|
|
573
|
+
for (const client of clients) client.close();
|
|
574
|
+
await deleteDevTunnel(restore).catch(() => {
|
|
607
575
|
});
|
|
576
|
+
console.log(import_chalk3.default.green("Tunnel stopped."));
|
|
577
|
+
process.exit(0);
|
|
608
578
|
}
|
|
579
|
+
process.on("SIGINT", () => void cleanup());
|
|
580
|
+
process.on("SIGTERM", () => void cleanup());
|
|
581
|
+
await new Promise(() => {
|
|
582
|
+
});
|
|
609
583
|
}
|
|
610
584
|
|
|
611
585
|
// src/commands/projects.ts
|
|
612
586
|
var import_chalk4 = __toESM(require("chalk"));
|
|
613
|
-
var
|
|
587
|
+
var import_ora3 = __toESM(require("ora"));
|
|
614
588
|
init_auth();
|
|
615
589
|
init_api();
|
|
616
590
|
async function runProjects() {
|
|
@@ -647,7 +621,7 @@ async function runProjects() {
|
|
|
647
621
|
}
|
|
648
622
|
console.log(`${import_chalk4.default.cyan("\u2192")} Team: ${import_chalk4.default.bold(teamName ?? teamId)}
|
|
649
623
|
`);
|
|
650
|
-
const spinner = (0,
|
|
624
|
+
const spinner = (0, import_ora3.default)("Fetching projects...").start();
|
|
651
625
|
let projects;
|
|
652
626
|
try {
|
|
653
627
|
projects = await getProjects(teamId);
|
|
@@ -670,7 +644,7 @@ ${projects.length} project${projects.length === 1 ? "" : "s"}`));
|
|
|
670
644
|
|
|
671
645
|
// src/commands/create.ts
|
|
672
646
|
var import_chalk5 = __toESM(require("chalk"));
|
|
673
|
-
var
|
|
647
|
+
var import_ora4 = __toESM(require("ora"));
|
|
674
648
|
init_auth();
|
|
675
649
|
init_api();
|
|
676
650
|
function normalizeName(raw) {
|
|
@@ -749,7 +723,7 @@ async function runCreate(opts = {}) {
|
|
|
749
723
|
console.log(import_chalk5.default.yellow(" Name must be at least 3 characters (letters and digits only).\n"));
|
|
750
724
|
continue;
|
|
751
725
|
}
|
|
752
|
-
const spinner2 = (0,
|
|
726
|
+
const spinner2 = (0, import_ora4.default)("Checking availability...").start();
|
|
753
727
|
try {
|
|
754
728
|
const check = await checkProxyName(name, teamId);
|
|
755
729
|
spinner2.stop();
|
|
@@ -805,7 +779,7 @@ async function runCreate(opts = {}) {
|
|
|
805
779
|
return;
|
|
806
780
|
}
|
|
807
781
|
}
|
|
808
|
-
const spinner = !opts.json ? (0,
|
|
782
|
+
const spinner = !opts.json ? (0, import_ora4.default)("Creating proxy (tenant, keys, dev portal)...").start() : null;
|
|
809
783
|
let result;
|
|
810
784
|
try {
|
|
811
785
|
result = await createProxy({ name, target_url: targetUrl, auth_type: auth, team_id: teamId });
|
|
@@ -895,7 +869,7 @@ async function runTeam(arg) {
|
|
|
895
869
|
|
|
896
870
|
// src/index.ts
|
|
897
871
|
var program = new import_commander.Command();
|
|
898
|
-
program.name("apiblaze").description("APIblaze dev tunnel CLI").version("0.1.
|
|
872
|
+
program.name("apiblaze").description("APIblaze dev tunnel CLI").version("0.1.11");
|
|
899
873
|
program.command("login").description("Authenticate with APIblaze").action(async () => {
|
|
900
874
|
try {
|
|
901
875
|
await runLogin();
|
|
@@ -928,9 +902,14 @@ program.command("projects").description("List the projects in your team").action
|
|
|
928
902
|
process.exit(1);
|
|
929
903
|
}
|
|
930
904
|
});
|
|
931
|
-
program.command("dev").description("Start a dev tunnel for your localhost projects").option("-p, --port <number>", "Local port to tunnel", "3000").action(async (opts) => {
|
|
905
|
+
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) => {
|
|
932
906
|
try {
|
|
933
|
-
|
|
907
|
+
const resolved = parseInt(port ?? opts.port, 10);
|
|
908
|
+
if (Number.isNaN(resolved)) {
|
|
909
|
+
console.error(import_chalk7.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
910
|
+
process.exit(1);
|
|
911
|
+
}
|
|
912
|
+
await runDev({ port: resolved });
|
|
934
913
|
} catch (err) {
|
|
935
914
|
printError(err);
|
|
936
915
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apiblaze",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Dev tunnel CLI for APIblaze — route localhost projects through
|
|
3
|
+
"version": "0.1.13",
|
|
4
|
+
"description": "Dev tunnel CLI for APIblaze — route localhost projects through your APIblaze endpoints",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"apiblaze",
|
|
7
7
|
"dev-tunnel",
|