apiblaze 0.1.4 → 0.1.6
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 +148 -88
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -89,12 +89,13 @@ var api_exports = {};
|
|
|
89
89
|
__export(api_exports, {
|
|
90
90
|
deleteDevTunnel: () => deleteDevTunnel,
|
|
91
91
|
getLocalhostTargets: () => getLocalhostTargets,
|
|
92
|
+
getProjects: () => getProjects,
|
|
92
93
|
getTeams: () => getTeams,
|
|
93
94
|
putDevTunnel: () => putDevTunnel
|
|
94
95
|
});
|
|
95
96
|
async function apiFetch(path3, options = {}) {
|
|
96
97
|
const token = getAccessToken();
|
|
97
|
-
const url = `${
|
|
98
|
+
const url = `${DASHBOARD_BASE}${path3}`;
|
|
98
99
|
const res = await fetch(url, {
|
|
99
100
|
...options,
|
|
100
101
|
headers: {
|
|
@@ -118,132 +119,121 @@ async function apiFetch(path3, options = {}) {
|
|
|
118
119
|
return res.json();
|
|
119
120
|
}
|
|
120
121
|
async function getTeams() {
|
|
121
|
-
|
|
122
|
+
const res = await apiFetch("/api/cli/teams");
|
|
123
|
+
const raw = Array.isArray(res) ? res : res?.teams ?? res?.data ?? [];
|
|
124
|
+
return raw.map((t) => {
|
|
125
|
+
const teamId = t.teamId ?? t.team_id;
|
|
126
|
+
return { teamId: teamId ?? "", name: t.name ?? teamId ?? "" };
|
|
127
|
+
}).filter((t) => t.teamId);
|
|
122
128
|
}
|
|
123
129
|
async function getLocalhostTargets(teamId) {
|
|
124
|
-
return apiFetch(`/
|
|
130
|
+
return apiFetch(`/api/cli/localhost-targets?team_id=${encodeURIComponent(teamId)}`);
|
|
131
|
+
}
|
|
132
|
+
async function getProjects(teamId) {
|
|
133
|
+
return apiFetch(`/api/cli/projects?team_id=${encodeURIComponent(teamId)}`);
|
|
125
134
|
}
|
|
126
135
|
async function putDevTunnel(payload) {
|
|
127
|
-
return apiFetch("/dev-tunnel", {
|
|
136
|
+
return apiFetch("/api/cli/dev-tunnel", {
|
|
128
137
|
method: "PUT",
|
|
129
138
|
body: JSON.stringify(payload)
|
|
130
139
|
});
|
|
131
140
|
}
|
|
132
141
|
async function deleteDevTunnel() {
|
|
133
|
-
return apiFetch("/dev-tunnel", { method: "DELETE" });
|
|
142
|
+
return apiFetch("/api/cli/dev-tunnel", { method: "DELETE" });
|
|
134
143
|
}
|
|
135
|
-
var
|
|
144
|
+
var DASHBOARD_BASE;
|
|
136
145
|
var init_api = __esm({
|
|
137
146
|
"src/lib/api.ts"() {
|
|
138
147
|
"use strict";
|
|
139
148
|
init_auth();
|
|
140
149
|
init_types();
|
|
141
|
-
|
|
150
|
+
DASHBOARD_BASE = "https://dashboard.apiblaze.com";
|
|
142
151
|
}
|
|
143
152
|
});
|
|
144
153
|
|
|
145
154
|
// src/index.ts
|
|
146
155
|
var import_commander = require("commander");
|
|
147
|
-
var
|
|
156
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
148
157
|
init_types();
|
|
149
158
|
|
|
150
159
|
// src/commands/login.ts
|
|
151
160
|
var import_chalk = __toESM(require("chalk"));
|
|
152
161
|
var import_ora = __toESM(require("ora"));
|
|
153
|
-
var crypto = __toESM(require("crypto"));
|
|
154
162
|
init_auth();
|
|
155
163
|
init_api();
|
|
156
|
-
var
|
|
157
|
-
var CLIENT_ID = "emdE4-Pt9LGOAXL5MA1zEQ";
|
|
158
|
-
var SCOPE = "openid email profile offline_access";
|
|
159
|
-
function generateCodeVerifier() {
|
|
160
|
-
return crypto.randomBytes(32).toString("base64url");
|
|
161
|
-
}
|
|
162
|
-
function generateCodeChallenge(verifier) {
|
|
163
|
-
return crypto.createHash("sha256").update(verifier).digest("base64url");
|
|
164
|
-
}
|
|
164
|
+
var DASHBOARD_BASE2 = "https://dashboard.apiblaze.com";
|
|
165
165
|
function openBrowser(url) {
|
|
166
166
|
const { exec } = require("child_process");
|
|
167
167
|
const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
168
168
|
exec(cmd);
|
|
169
169
|
}
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
174
|
-
body: new URLSearchParams({
|
|
175
|
-
client_id: CLIENT_ID,
|
|
176
|
-
scope: SCOPE,
|
|
177
|
-
code_challenge: codeChallenge,
|
|
178
|
-
code_challenge_method: "S256"
|
|
179
|
-
})
|
|
180
|
-
});
|
|
181
|
-
if (!res.ok) {
|
|
182
|
-
const body = await res.text();
|
|
183
|
-
throw new Error(`Failed to start device flow: ${res.status} ${body}`);
|
|
184
|
-
}
|
|
185
|
-
return res.json();
|
|
170
|
+
function decodeJWTPayload(token) {
|
|
171
|
+
const part = token.split(".")[1];
|
|
172
|
+
return JSON.parse(Buffer.from(part, "base64url").toString());
|
|
186
173
|
}
|
|
187
|
-
async function
|
|
188
|
-
|
|
189
|
-
|
|
174
|
+
async function runLogin() {
|
|
175
|
+
console.log(import_chalk.default.bold("\nLogging in to APIblaze...\n"));
|
|
176
|
+
const codeRes = await fetch(`${DASHBOARD_BASE2}/api/device/code`, { method: "POST" });
|
|
177
|
+
if (!codeRes.ok) {
|
|
178
|
+
throw new Error(`Failed to start login: ${codeRes.status}`);
|
|
179
|
+
}
|
|
180
|
+
const deviceAuth = await codeRes.json();
|
|
181
|
+
console.log(`${import_chalk.default.cyan("\u2192")} Open this URL in your browser to confirm login:`);
|
|
182
|
+
console.log(` ${import_chalk.default.bold.underline(deviceAuth.verification_uri)}
|
|
183
|
+
`);
|
|
184
|
+
console.log(`${import_chalk.default.cyan("\u2192")} Your code: ${import_chalk.default.bold(deviceAuth.user_code)}
|
|
185
|
+
`);
|
|
186
|
+
openBrowser(deviceAuth.verification_uri_complete ?? deviceAuth.verification_uri);
|
|
187
|
+
const spinner = (0, import_ora.default)("Waiting for authorization in browser...").start();
|
|
188
|
+
let accessToken;
|
|
189
|
+
let pollInterval = (deviceAuth.interval ?? 5) * 1e3;
|
|
190
190
|
while (true) {
|
|
191
191
|
await new Promise((r) => setTimeout(r, pollInterval));
|
|
192
|
-
const
|
|
192
|
+
const tokenRes = await fetch(`${DASHBOARD_BASE2}/api/device/token`, {
|
|
193
193
|
method: "POST",
|
|
194
|
-
headers: { "Content-Type": "application/
|
|
195
|
-
body:
|
|
196
|
-
grant_type: DEVICE_GRANT,
|
|
197
|
-
device_code: deviceCode,
|
|
198
|
-
client_id: CLIENT_ID,
|
|
199
|
-
code_verifier: codeVerifier
|
|
200
|
-
})
|
|
194
|
+
headers: { "Content-Type": "application/json" },
|
|
195
|
+
body: JSON.stringify({ device_code: deviceAuth.device_code })
|
|
201
196
|
});
|
|
202
|
-
const body = await
|
|
203
|
-
if (
|
|
204
|
-
|
|
197
|
+
const body = await tokenRes.json();
|
|
198
|
+
if (body.access_token) {
|
|
199
|
+
accessToken = body.access_token;
|
|
200
|
+
break;
|
|
205
201
|
}
|
|
206
202
|
if (body.error === "authorization_pending") {
|
|
207
203
|
continue;
|
|
208
204
|
} else if (body.error === "slow_down") {
|
|
209
205
|
pollInterval += 5e3;
|
|
210
206
|
} else if (body.error === "expired_token") {
|
|
207
|
+
spinner.fail("Login failed.");
|
|
211
208
|
throw new Error("Device code expired. Run `apiblaze login` again.");
|
|
212
209
|
} else if (body.error === "access_denied") {
|
|
210
|
+
spinner.fail("Login failed.");
|
|
213
211
|
throw new Error("Login was denied or cancelled.");
|
|
214
212
|
} else {
|
|
213
|
+
spinner.fail("Login failed.");
|
|
215
214
|
throw new Error(`Unexpected error during login: ${body.error ?? "unknown"}`);
|
|
216
215
|
}
|
|
217
216
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
const verifyUrl = deviceAuth.verification_uri_complete ?? deviceAuth.verification_uri;
|
|
225
|
-
console.log(`${import_chalk.default.cyan("\u2192")} Open this URL in your browser to confirm login:`);
|
|
226
|
-
console.log(` ${import_chalk.default.bold.underline(verifyUrl)}
|
|
227
|
-
`);
|
|
228
|
-
console.log(`${import_chalk.default.cyan("\u2192")} Your code: ${import_chalk.default.bold(deviceAuth.user_code)}
|
|
229
|
-
`);
|
|
230
|
-
openBrowser(verifyUrl);
|
|
231
|
-
const spinner = (0, import_ora.default)("Waiting for authorization in browser...").start();
|
|
232
|
-
let token;
|
|
233
|
-
try {
|
|
234
|
-
token = await pollForToken(deviceAuth.device_code, codeVerifier, deviceAuth.interval);
|
|
235
|
-
} catch (err) {
|
|
236
|
-
spinner.fail("Login failed.");
|
|
237
|
-
throw err;
|
|
238
|
-
}
|
|
217
|
+
const jwtPayload = decodeJWTPayload(accessToken);
|
|
218
|
+
const expiresAt = jwtPayload.exp * 1e3;
|
|
219
|
+
const apiblazeUserId = jwtPayload.sub;
|
|
220
|
+
const githubHandle = jwtPayload.handle;
|
|
221
|
+
const email = jwtPayload.email;
|
|
222
|
+
const defaultTeamId = jwtPayload.defaultTeamId;
|
|
239
223
|
saveCredentials({
|
|
240
|
-
accessToken
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
224
|
+
accessToken,
|
|
225
|
+
expiresAt,
|
|
226
|
+
tokenType: "Bearer",
|
|
227
|
+
apiblazeUserId,
|
|
228
|
+
githubHandle,
|
|
229
|
+
email,
|
|
230
|
+
teamId: defaultTeamId ?? void 0
|
|
244
231
|
});
|
|
245
232
|
spinner.succeed(import_chalk.default.green("Authorized!"));
|
|
246
|
-
|
|
233
|
+
if (githubHandle) {
|
|
234
|
+
console.log(`${import_chalk.default.cyan("\u2192")} Logged in as ${import_chalk.default.bold("@" + githubHandle)}${apiblazeUserId ? import_chalk.default.dim(` (${apiblazeUserId})`) : ""}`);
|
|
235
|
+
}
|
|
236
|
+
let teamId = defaultTeamId ?? void 0;
|
|
247
237
|
let teamName;
|
|
248
238
|
try {
|
|
249
239
|
const teams = await getTeams();
|
|
@@ -253,7 +243,7 @@ async function runLogin() {
|
|
|
253
243
|
teamId = teams[0].teamId;
|
|
254
244
|
teamName = teams[0].name;
|
|
255
245
|
console.log(`
|
|
256
|
-
${import_chalk.default.cyan("\u2192")}
|
|
246
|
+
${import_chalk.default.cyan("\u2192")} Team: ${import_chalk.default.bold(teamName)}`);
|
|
257
247
|
} else {
|
|
258
248
|
const { default: inquirer2 } = await import("inquirer");
|
|
259
249
|
const { chosen } = await inquirer2.prompt([{
|
|
@@ -267,16 +257,16 @@ ${import_chalk.default.cyan("\u2192")} Using team: ${import_chalk.default.bold(t
|
|
|
267
257
|
}
|
|
268
258
|
} catch {
|
|
269
259
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
260
|
+
saveCredentials({
|
|
261
|
+
accessToken,
|
|
262
|
+
expiresAt,
|
|
263
|
+
tokenType: "Bearer",
|
|
264
|
+
apiblazeUserId,
|
|
265
|
+
githubHandle,
|
|
266
|
+
email,
|
|
267
|
+
teamId,
|
|
268
|
+
teamName
|
|
269
|
+
});
|
|
280
270
|
console.log(import_chalk.default.green("\n\u2714 Logged in successfully!"));
|
|
281
271
|
}
|
|
282
272
|
|
|
@@ -605,6 +595,68 @@ Tunneling ${selectedTargets.length} project(s) to localhost:${options.port}
|
|
|
605
595
|
}
|
|
606
596
|
}
|
|
607
597
|
|
|
598
|
+
// src/commands/projects.ts
|
|
599
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
600
|
+
var import_ora4 = __toESM(require("ora"));
|
|
601
|
+
init_auth();
|
|
602
|
+
init_api();
|
|
603
|
+
async function runProjects() {
|
|
604
|
+
const creds = loadCredentials();
|
|
605
|
+
if (!creds) {
|
|
606
|
+
console.error(import_chalk4.default.red("Not logged in. Run `apiblaze login` first."));
|
|
607
|
+
process.exit(1);
|
|
608
|
+
}
|
|
609
|
+
if (creds.githubHandle) {
|
|
610
|
+
console.log(
|
|
611
|
+
`${import_chalk4.default.cyan("\u2192")} Logged in as ${import_chalk4.default.bold("@" + creds.githubHandle)}` + (creds.apiblazeUserId ? import_chalk4.default.dim(` (${creds.apiblazeUserId})`) : "")
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
let teamId = creds.teamId;
|
|
615
|
+
let teamName = creds.teamName;
|
|
616
|
+
if (!teamId) {
|
|
617
|
+
const teams = await getTeams().catch(() => []);
|
|
618
|
+
if (teams.length === 1) {
|
|
619
|
+
teamId = teams[0].teamId;
|
|
620
|
+
teamName = teams[0].name;
|
|
621
|
+
} else if (teams.length > 1) {
|
|
622
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
623
|
+
const { chosen } = await inquirer2.prompt([{
|
|
624
|
+
type: "list",
|
|
625
|
+
name: "chosen",
|
|
626
|
+
message: "Which team do you want to use?",
|
|
627
|
+
choices: teams.map((t) => ({ name: t.name, value: t.teamId }))
|
|
628
|
+
}]);
|
|
629
|
+
teamId = chosen;
|
|
630
|
+
teamName = teams.find((t) => t.teamId === chosen)?.name;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
if (!teamId) {
|
|
634
|
+
console.error(import_chalk4.default.red("No team found. Run `apiblaze login` to set up your team."));
|
|
635
|
+
process.exit(1);
|
|
636
|
+
}
|
|
637
|
+
console.log(`${import_chalk4.default.cyan("\u2192")} Team: ${import_chalk4.default.bold(teamName ?? teamId)} ${import_chalk4.default.dim(teamId)}
|
|
638
|
+
`);
|
|
639
|
+
const spinner = (0, import_ora4.default)("Fetching projects...").start();
|
|
640
|
+
let projects;
|
|
641
|
+
try {
|
|
642
|
+
projects = await getProjects(teamId);
|
|
643
|
+
spinner.stop();
|
|
644
|
+
} catch (err) {
|
|
645
|
+
spinner.fail("Failed to fetch projects.");
|
|
646
|
+
throw err;
|
|
647
|
+
}
|
|
648
|
+
if (projects.length === 0) {
|
|
649
|
+
console.log(import_chalk4.default.yellow("No projects found for this team."));
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
for (const p of projects) {
|
|
653
|
+
console.log(`${import_chalk4.default.bold(p.projectName)} ${import_chalk4.default.dim(p.projectId)}`);
|
|
654
|
+
console.log(` ${import_chalk4.default.dim("version:")} ${p.apiVersion} ${import_chalk4.default.dim("team:")} ${p.teamId}`);
|
|
655
|
+
}
|
|
656
|
+
console.log(import_chalk4.default.dim(`
|
|
657
|
+
${projects.length} project(s).`));
|
|
658
|
+
}
|
|
659
|
+
|
|
608
660
|
// src/index.ts
|
|
609
661
|
var program = new import_commander.Command();
|
|
610
662
|
program.name("apiblaze").description("APIblaze dev tunnel CLI").version("0.1.0");
|
|
@@ -616,6 +668,14 @@ program.command("login").description("Authenticate with APIblaze").action(async
|
|
|
616
668
|
process.exit(1);
|
|
617
669
|
}
|
|
618
670
|
});
|
|
671
|
+
program.command("projects").description("List the projects in your team").action(async () => {
|
|
672
|
+
try {
|
|
673
|
+
await runProjects();
|
|
674
|
+
} catch (err) {
|
|
675
|
+
printError(err);
|
|
676
|
+
process.exit(1);
|
|
677
|
+
}
|
|
678
|
+
});
|
|
619
679
|
program.command("dev").description("Start a dev tunnel for your localhost projects").option("-p, --port <number>", "Local port to tunnel", "3000").action(async (opts) => {
|
|
620
680
|
try {
|
|
621
681
|
await runDev({ port: parseInt(opts.port, 10) });
|
|
@@ -626,13 +686,13 @@ program.command("dev").description("Start a dev tunnel for your localhost projec
|
|
|
626
686
|
});
|
|
627
687
|
function printError(err) {
|
|
628
688
|
if (err instanceof ApiError) {
|
|
629
|
-
console.error(
|
|
689
|
+
console.error(import_chalk5.default.red(`
|
|
630
690
|
API error (${err.status}): ${err.message}`));
|
|
631
691
|
} else if (err instanceof Error) {
|
|
632
|
-
console.error(
|
|
692
|
+
console.error(import_chalk5.default.red(`
|
|
633
693
|
Error: ${err.message}`));
|
|
634
694
|
} else {
|
|
635
|
-
console.error(
|
|
695
|
+
console.error(import_chalk5.default.red("\nUnknown error"));
|
|
636
696
|
}
|
|
637
697
|
}
|
|
638
698
|
program.parse(process.argv);
|