apiblaze 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +905 -320
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -300,12 +300,199 @@ var init_anon_cred = __esm({
|
|
|
300
300
|
}
|
|
301
301
|
});
|
|
302
302
|
|
|
303
|
+
// src/lib/trace.ts
|
|
304
|
+
function setVerbose(v) {
|
|
305
|
+
verbose = v;
|
|
306
|
+
}
|
|
307
|
+
function recordCall(e) {
|
|
308
|
+
if (verbose) entries.push(e);
|
|
309
|
+
}
|
|
310
|
+
function maskBody(body) {
|
|
311
|
+
if (body === void 0) return void 0;
|
|
312
|
+
return JSON.stringify(body, (k, v) => SECRET_KEY.test(k) && typeof v === "string" ? "***" : v);
|
|
313
|
+
}
|
|
314
|
+
function renderTrace() {
|
|
315
|
+
if (!verbose || entries.length === 0) return;
|
|
316
|
+
console.log(import_chalk15.default.dim("\n" + "\u2500".repeat(64)));
|
|
317
|
+
console.log(import_chalk15.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
|
|
318
|
+
console.log(
|
|
319
|
+
import_chalk15.default.dim("The same thing on the official API \u2014 copy/paste with your control-plane key\n(get one from the Developers section of dashboard.apiblaze.com, then\n`export APIBLAZE_CONTROLPLANE_APIKEY=sk_...`).\nFull API reference: https://api.apiblaze.com/openapi.json\n")
|
|
320
|
+
);
|
|
321
|
+
entries.forEach((e, i) => {
|
|
322
|
+
const n = entries.length > 1 ? import_chalk15.default.bold(`${i + 1}. `) : "";
|
|
323
|
+
if (e.summary) console.log(`${n}${import_chalk15.default.cyan(e.summary)}${e.status ? import_chalk15.default.dim(` (HTTP ${e.status})`) : ""}`);
|
|
324
|
+
const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
|
|
325
|
+
const masked = maskBody(e.body);
|
|
326
|
+
const hasBody = e.method !== "GET" && masked !== void 0;
|
|
327
|
+
console.log(import_chalk15.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
|
|
328
|
+
console.log(import_chalk15.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
|
|
329
|
+
if (hasBody) {
|
|
330
|
+
console.log(import_chalk15.default.green(" -H 'Content-Type: application/json' \\"));
|
|
331
|
+
console.log(import_chalk15.default.green(` -d '${masked}'`));
|
|
332
|
+
}
|
|
333
|
+
if (i < entries.length - 1) console.log();
|
|
334
|
+
});
|
|
335
|
+
entries.length = 0;
|
|
336
|
+
}
|
|
337
|
+
var import_chalk15, CONTROL_API_VERSION, verbose, entries, SECRET_KEY;
|
|
338
|
+
var init_trace = __esm({
|
|
339
|
+
"src/lib/trace.ts"() {
|
|
340
|
+
"use strict";
|
|
341
|
+
import_chalk15 = __toESM(require("chalk"));
|
|
342
|
+
CONTROL_API_VERSION = "1.0.0";
|
|
343
|
+
verbose = false;
|
|
344
|
+
entries = [];
|
|
345
|
+
SECRET_KEY = /secret|token|password|api[_-]?key|client_secret/i;
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// src/lib/admin.ts
|
|
350
|
+
async function admin(call) {
|
|
351
|
+
const token = getAccessToken();
|
|
352
|
+
const res = await fetch(`${DASHBOARD_BASE3}/api/cli/admin`, {
|
|
353
|
+
method: "POST",
|
|
354
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
355
|
+
body: JSON.stringify({ path: call.path, method: call.method, body: call.body })
|
|
356
|
+
});
|
|
357
|
+
let data = null;
|
|
358
|
+
try {
|
|
359
|
+
data = await res.json();
|
|
360
|
+
} catch {
|
|
361
|
+
}
|
|
362
|
+
recordCall({ method: call.method, path: call.path, body: call.body, status: res.status, summary: call.summary });
|
|
363
|
+
maybePrintBilling(data);
|
|
364
|
+
if (!res.ok) {
|
|
365
|
+
const msg = data?.details ?? data?.error ?? res.statusText;
|
|
366
|
+
throw new ApiError(res.status, typeof msg === "string" ? msg : JSON.stringify(msg), data);
|
|
367
|
+
}
|
|
368
|
+
return data;
|
|
369
|
+
}
|
|
370
|
+
function maybePrintBilling(data) {
|
|
371
|
+
const b = data?.billing;
|
|
372
|
+
if (b && typeof b.charged_cents === "number") {
|
|
373
|
+
const usd = (b.charged_cents / 100).toFixed(2);
|
|
374
|
+
const rem = typeof b.credits_remaining === "number" ? ` \xB7 $${(b.credits_remaining / 100).toFixed(2)} credit left` : "";
|
|
375
|
+
console.log(import_chalk16.default.magenta(` \u{1F4B3} Charged $${usd}${rem}`));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
var import_chalk16, DASHBOARD_BASE3;
|
|
379
|
+
var init_admin = __esm({
|
|
380
|
+
"src/lib/admin.ts"() {
|
|
381
|
+
"use strict";
|
|
382
|
+
import_chalk16 = __toESM(require("chalk"));
|
|
383
|
+
init_auth();
|
|
384
|
+
init_trace();
|
|
385
|
+
init_types();
|
|
386
|
+
DASHBOARD_BASE3 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// src/lib/tenant-pick.ts
|
|
391
|
+
var tenant_pick_exports = {};
|
|
392
|
+
__export(tenant_pick_exports, {
|
|
393
|
+
pickTenant: () => pickTenant
|
|
394
|
+
});
|
|
395
|
+
async function fetchPage(teamId, q) {
|
|
396
|
+
const out = await admin({
|
|
397
|
+
method: "GET",
|
|
398
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants?detail=1&limit=${PAGE}${q ? `&q=${encodeURIComponent(q)}` : ""}`,
|
|
399
|
+
summary: q ? `Search tenants matching "${q}"` : "List tenants (first page)"
|
|
400
|
+
});
|
|
401
|
+
const rows = (out?.tenants ?? []).map(
|
|
402
|
+
(t) => typeof t === "string" ? { tenant_name: t } : t
|
|
403
|
+
);
|
|
404
|
+
return { rows, total: out?.total ?? rows.length, defaultTenant: out?.default_tenant ?? null };
|
|
405
|
+
}
|
|
406
|
+
function label(t, defaultTenant, active) {
|
|
407
|
+
const tags = [
|
|
408
|
+
t.tenant_name === active ? import_chalk22.default.cyan("active scope") : "",
|
|
409
|
+
t.tenant_name === defaultTenant ? import_chalk22.default.dim("team default") : ""
|
|
410
|
+
].filter(Boolean).join(", ");
|
|
411
|
+
const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk22.default.dim(` ${t.display_name}`) : "";
|
|
412
|
+
return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
|
|
413
|
+
}
|
|
414
|
+
async function pickTenant(teamId, opts = {}) {
|
|
415
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
416
|
+
const active = loadCredentials()?.activeTenant;
|
|
417
|
+
let q = opts.initialQuery ?? "";
|
|
418
|
+
for (; ; ) {
|
|
419
|
+
const spinner = (0, import_ora8.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
|
|
420
|
+
const page = await fetchPage(teamId, q).finally(() => spinner.stop());
|
|
421
|
+
if (!page.total && !q) {
|
|
422
|
+
if (opts.allowCreate) {
|
|
423
|
+
const { make } = await inquirer2.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
|
|
424
|
+
if (make) return await createTenantInline(teamId);
|
|
425
|
+
}
|
|
426
|
+
console.error(import_chalk22.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
const truncated = page.total > page.rows.length;
|
|
430
|
+
const choices = page.rows.map((t) => ({
|
|
431
|
+
name: label(t, page.defaultTenant, active),
|
|
432
|
+
value: t.tenant_name
|
|
433
|
+
}));
|
|
434
|
+
if (truncated || q) {
|
|
435
|
+
choices.push(new inquirer2.Separator(import_chalk22.default.dim(
|
|
436
|
+
truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
|
|
437
|
+
)));
|
|
438
|
+
choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
|
|
439
|
+
}
|
|
440
|
+
if (q) choices.push({ name: "\u21BA Show all (clear search)", value: "\0clear" });
|
|
441
|
+
if (opts.allowCreate) choices.push({ name: "\uFF0B Create a new tenant\u2026", value: "\0create" });
|
|
442
|
+
if (opts.allowBack) choices.push({ name: "\u2190 Back", value: "\0back" });
|
|
443
|
+
const { picked } = await inquirer2.prompt([{
|
|
444
|
+
type: "list",
|
|
445
|
+
name: "picked",
|
|
446
|
+
pageSize: PAGE + 5,
|
|
447
|
+
message: opts.message ?? "Which tenant?",
|
|
448
|
+
default: active && page.rows.some((t) => t.tenant_name === active) ? active : void 0,
|
|
449
|
+
choices
|
|
450
|
+
}]);
|
|
451
|
+
if (picked === "\0back") return null;
|
|
452
|
+
if (picked === "\0clear") {
|
|
453
|
+
q = "";
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (picked === "\0create") return await createTenantInline(teamId);
|
|
457
|
+
if (picked === "\0search") {
|
|
458
|
+
const { nq } = await inquirer2.prompt([{ type: "input", name: "nq", message: "Search (name or display name):", default: q }]);
|
|
459
|
+
q = String(nq ?? "").trim();
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
return picked;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
async function createTenantInline(teamId) {
|
|
466
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
467
|
+
const { display } = await inquirer2.prompt([{ type: "input", name: "display", message: "Display name for the new tenant:", validate: (s) => !!s.trim() || "required" }]);
|
|
468
|
+
const out = await admin({
|
|
469
|
+
method: "POST",
|
|
470
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants`,
|
|
471
|
+
body: { display_name: display.trim() },
|
|
472
|
+
summary: `Create tenant "${display.trim()}"`
|
|
473
|
+
});
|
|
474
|
+
const slug = out?.tenant_name ?? out?.tenant?.tenant_name;
|
|
475
|
+
console.log(import_chalk22.default.green(` Tenant ${import_chalk22.default.bold(slug)} created.`));
|
|
476
|
+
return slug;
|
|
477
|
+
}
|
|
478
|
+
var import_chalk22, import_ora8, PAGE;
|
|
479
|
+
var init_tenant_pick = __esm({
|
|
480
|
+
"src/lib/tenant-pick.ts"() {
|
|
481
|
+
"use strict";
|
|
482
|
+
import_chalk22 = __toESM(require("chalk"));
|
|
483
|
+
import_ora8 = __toESM(require("ora"));
|
|
484
|
+
init_admin();
|
|
485
|
+
init_auth();
|
|
486
|
+
PAGE = 15;
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
|
|
303
490
|
// src/index.ts
|
|
304
491
|
var import_commander = require("commander");
|
|
305
|
-
var
|
|
492
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
306
493
|
|
|
307
494
|
// package.json
|
|
308
|
-
var version = "0.
|
|
495
|
+
var version = "0.12.0";
|
|
309
496
|
|
|
310
497
|
// src/index.ts
|
|
311
498
|
init_types();
|
|
@@ -1009,9 +1196,9 @@ function printTunnelEndpoints(restore, targets) {
|
|
|
1009
1196
|
if (restore.length === 0) return;
|
|
1010
1197
|
console.log(import_chalk4.default.bold("\nYour proxy is live at:"));
|
|
1011
1198
|
for (const r of restore) {
|
|
1012
|
-
const
|
|
1199
|
+
const label2 = targets.find((t) => t.projectId === r.projectId)?.projectName ?? r.projectId;
|
|
1013
1200
|
console.log(`
|
|
1014
|
-
${import_chalk4.default.bold(
|
|
1201
|
+
${import_chalk4.default.bold(label2)}`);
|
|
1015
1202
|
const internalEnvs = Object.keys(r.environments ?? {}).filter((e) => isInternalTarget(r.environments[e]?.target));
|
|
1016
1203
|
const envs = internalEnvs.includes("dev") ? ["dev"] : internalEnvs.length ? internalEnvs : ["dev"];
|
|
1017
1204
|
for (const env of envs) {
|
|
@@ -1835,6 +2022,7 @@ async function runWhoami(opts = {}) {
|
|
|
1835
2022
|
console.log(` ${import_chalk9.default.cyan("Signed in as")} ${import_chalk9.default.bold(who)}`);
|
|
1836
2023
|
if (creds.email && creds.email !== who) console.log(` Email: ${creds.email}`);
|
|
1837
2024
|
if (creds.teamName || creds.teamId) console.log(` Team: ${import_chalk9.default.bold(creds.teamName ?? creds.teamId)}`);
|
|
2025
|
+
if (creds.activeTenant) console.log(` Tenant scope: ${import_chalk9.default.bold(creds.activeTenant)} ${import_chalk9.default.dim("(clear: `apiblaze tenant use --clear`)")}`);
|
|
1838
2026
|
if (Date.now() >= creds.expiresAt) console.log(import_chalk9.default.yellow(" \u26A0 Session expired \u2014 run `apiblaze login`."));
|
|
1839
2027
|
}
|
|
1840
2028
|
console.log(import_chalk9.default.bold("\nAPI Consumer"));
|
|
@@ -2181,82 +2369,7 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
2181
2369
|
// src/commands/delete.ts
|
|
2182
2370
|
var import_chalk18 = __toESM(require("chalk"));
|
|
2183
2371
|
var import_ora5 = __toESM(require("ora"));
|
|
2184
|
-
|
|
2185
|
-
// src/lib/admin.ts
|
|
2186
|
-
var import_chalk16 = __toESM(require("chalk"));
|
|
2187
|
-
init_auth();
|
|
2188
|
-
|
|
2189
|
-
// src/lib/trace.ts
|
|
2190
|
-
var import_chalk15 = __toESM(require("chalk"));
|
|
2191
|
-
var CONTROL_API_VERSION = "1.0.0";
|
|
2192
|
-
var verbose = false;
|
|
2193
|
-
var entries = [];
|
|
2194
|
-
function setVerbose(v) {
|
|
2195
|
-
verbose = v;
|
|
2196
|
-
}
|
|
2197
|
-
function recordCall(e) {
|
|
2198
|
-
if (verbose) entries.push(e);
|
|
2199
|
-
}
|
|
2200
|
-
var SECRET_KEY = /secret|token|password|api[_-]?key|client_secret/i;
|
|
2201
|
-
function maskBody(body) {
|
|
2202
|
-
if (body === void 0) return void 0;
|
|
2203
|
-
return JSON.stringify(body, (k, v) => SECRET_KEY.test(k) && typeof v === "string" ? "***" : v);
|
|
2204
|
-
}
|
|
2205
|
-
function renderTrace() {
|
|
2206
|
-
if (!verbose || entries.length === 0) return;
|
|
2207
|
-
console.log(import_chalk15.default.dim("\n" + "\u2500".repeat(64)));
|
|
2208
|
-
console.log(import_chalk15.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
|
|
2209
|
-
console.log(
|
|
2210
|
-
import_chalk15.default.dim("The same thing on the official API \u2014 copy/paste with your control-plane key\n(get one from the Developers section of dashboard.apiblaze.com, then\n`export APIBLAZE_CONTROLPLANE_APIKEY=sk_...`).\nFull API reference: https://api.apiblaze.com/openapi.json\n")
|
|
2211
|
-
);
|
|
2212
|
-
entries.forEach((e, i) => {
|
|
2213
|
-
const n = entries.length > 1 ? import_chalk15.default.bold(`${i + 1}. `) : "";
|
|
2214
|
-
if (e.summary) console.log(`${n}${import_chalk15.default.cyan(e.summary)}${e.status ? import_chalk15.default.dim(` (HTTP ${e.status})`) : ""}`);
|
|
2215
|
-
const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
|
|
2216
|
-
const masked = maskBody(e.body);
|
|
2217
|
-
const hasBody = e.method !== "GET" && masked !== void 0;
|
|
2218
|
-
console.log(import_chalk15.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
|
|
2219
|
-
console.log(import_chalk15.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
|
|
2220
|
-
if (hasBody) {
|
|
2221
|
-
console.log(import_chalk15.default.green(" -H 'Content-Type: application/json' \\"));
|
|
2222
|
-
console.log(import_chalk15.default.green(` -d '${masked}'`));
|
|
2223
|
-
}
|
|
2224
|
-
if (i < entries.length - 1) console.log();
|
|
2225
|
-
});
|
|
2226
|
-
entries.length = 0;
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
|
-
// src/lib/admin.ts
|
|
2230
|
-
init_types();
|
|
2231
|
-
var DASHBOARD_BASE3 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
2232
|
-
async function admin(call) {
|
|
2233
|
-
const token = getAccessToken();
|
|
2234
|
-
const res = await fetch(`${DASHBOARD_BASE3}/api/cli/admin`, {
|
|
2235
|
-
method: "POST",
|
|
2236
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
2237
|
-
body: JSON.stringify({ path: call.path, method: call.method, body: call.body })
|
|
2238
|
-
});
|
|
2239
|
-
let data = null;
|
|
2240
|
-
try {
|
|
2241
|
-
data = await res.json();
|
|
2242
|
-
} catch {
|
|
2243
|
-
}
|
|
2244
|
-
recordCall({ method: call.method, path: call.path, body: call.body, status: res.status, summary: call.summary });
|
|
2245
|
-
maybePrintBilling(data);
|
|
2246
|
-
if (!res.ok) {
|
|
2247
|
-
const msg = data?.details ?? data?.error ?? res.statusText;
|
|
2248
|
-
throw new ApiError(res.status, typeof msg === "string" ? msg : JSON.stringify(msg), data);
|
|
2249
|
-
}
|
|
2250
|
-
return data;
|
|
2251
|
-
}
|
|
2252
|
-
function maybePrintBilling(data) {
|
|
2253
|
-
const b = data?.billing;
|
|
2254
|
-
if (b && typeof b.charged_cents === "number") {
|
|
2255
|
-
const usd = (b.charged_cents / 100).toFixed(2);
|
|
2256
|
-
const rem = typeof b.credits_remaining === "number" ? ` \xB7 $${(b.credits_remaining / 100).toFixed(2)} credit left` : "";
|
|
2257
|
-
console.log(import_chalk16.default.magenta(` \u{1F4B3} Charged $${usd}${rem}`));
|
|
2258
|
-
}
|
|
2259
|
-
}
|
|
2372
|
+
init_admin();
|
|
2260
2373
|
|
|
2261
2374
|
// src/lib/resolve.ts
|
|
2262
2375
|
var import_chalk17 = __toESM(require("chalk"));
|
|
@@ -2369,6 +2482,7 @@ async function runDelete(project, version2, opts) {
|
|
|
2369
2482
|
// src/commands/export.ts
|
|
2370
2483
|
var import_chalk19 = __toESM(require("chalk"));
|
|
2371
2484
|
var import_fs3 = require("fs");
|
|
2485
|
+
init_admin();
|
|
2372
2486
|
init_auth();
|
|
2373
2487
|
var DASHBOARD_BASE4 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
2374
2488
|
async function runExport(projectArg, versionArg, opts) {
|
|
@@ -2425,6 +2539,7 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
2425
2539
|
// src/commands/config.ts
|
|
2426
2540
|
var import_chalk20 = __toESM(require("chalk"));
|
|
2427
2541
|
var import_ora6 = __toESM(require("ora"));
|
|
2542
|
+
init_admin();
|
|
2428
2543
|
async function patchConfig(project, opts, body, summary) {
|
|
2429
2544
|
const { teamId } = await resolveTeam(opts.team);
|
|
2430
2545
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
@@ -2478,13 +2593,15 @@ async function runRename(project, opts) {
|
|
|
2478
2593
|
}
|
|
2479
2594
|
|
|
2480
2595
|
// src/commands/config-browse.ts
|
|
2481
|
-
var
|
|
2482
|
-
var
|
|
2596
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
2597
|
+
var import_ora13 = __toESM(require("ora"));
|
|
2598
|
+
init_admin();
|
|
2483
2599
|
init_auth();
|
|
2484
2600
|
|
|
2485
2601
|
// src/commands/domain.ts
|
|
2486
2602
|
var import_chalk21 = __toESM(require("chalk"));
|
|
2487
2603
|
var import_ora7 = __toESM(require("ora"));
|
|
2604
|
+
init_admin();
|
|
2488
2605
|
async function runDomainAdd(project, opts) {
|
|
2489
2606
|
if (!opts.domain) {
|
|
2490
2607
|
console.error(import_chalk21.default.red("--domain is required."));
|
|
@@ -2587,8 +2704,29 @@ async function runDomainSetBase(project, opts) {
|
|
|
2587
2704
|
}
|
|
2588
2705
|
|
|
2589
2706
|
// src/commands/tenant.ts
|
|
2590
|
-
var
|
|
2591
|
-
var
|
|
2707
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
2708
|
+
var import_ora9 = __toESM(require("ora"));
|
|
2709
|
+
init_admin();
|
|
2710
|
+
init_auth();
|
|
2711
|
+
init_tenant_pick();
|
|
2712
|
+
async function runTenantUse(query, opts) {
|
|
2713
|
+
const creds = loadCredentials();
|
|
2714
|
+
if (!creds) {
|
|
2715
|
+
console.error(import_chalk23.default.red("Not logged in. Run `apiblaze login` first."));
|
|
2716
|
+
process.exit(1);
|
|
2717
|
+
}
|
|
2718
|
+
if (opts.clear) {
|
|
2719
|
+
delete creds.activeTenant;
|
|
2720
|
+
saveCredentials(creds);
|
|
2721
|
+
console.log(import_chalk23.default.green("Tenant scope cleared."));
|
|
2722
|
+
return;
|
|
2723
|
+
}
|
|
2724
|
+
const { teamId } = await resolveTeam(opts.team);
|
|
2725
|
+
const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
|
|
2726
|
+
if (!slug) process.exit(1);
|
|
2727
|
+
saveCredentials({ ...creds, activeTenant: slug });
|
|
2728
|
+
console.log(import_chalk23.default.green(`Tenant scope set to ${import_chalk23.default.bold(slug)}.`) + import_chalk23.default.dim(" (clear with `apiblaze tenant use --clear`)"));
|
|
2729
|
+
}
|
|
2592
2730
|
async function runTenantList(opts) {
|
|
2593
2731
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
2594
2732
|
const out = await admin({
|
|
@@ -2602,22 +2740,22 @@ async function runTenantList(opts) {
|
|
|
2602
2740
|
return;
|
|
2603
2741
|
}
|
|
2604
2742
|
if (!tenants.length) {
|
|
2605
|
-
console.log(
|
|
2743
|
+
console.log(import_chalk23.default.yellow("No tenants."));
|
|
2606
2744
|
return;
|
|
2607
2745
|
}
|
|
2608
2746
|
for (const t of tenants) {
|
|
2609
2747
|
const name = typeof t === "string" ? t : t.tenant_name;
|
|
2610
|
-
const display = typeof t === "string" ? "" :
|
|
2611
|
-
console.log(` ${
|
|
2748
|
+
const display = typeof t === "string" ? "" : import_chalk23.default.dim(` ${t.display_name ?? ""}`);
|
|
2749
|
+
console.log(` ${import_chalk23.default.bold(name)}${display}`);
|
|
2612
2750
|
}
|
|
2613
2751
|
}
|
|
2614
2752
|
async function runTenantCreate(opts) {
|
|
2615
2753
|
if (!opts.name) {
|
|
2616
|
-
console.error(
|
|
2754
|
+
console.error(import_chalk23.default.red("--name (display name) is required."));
|
|
2617
2755
|
process.exit(1);
|
|
2618
2756
|
}
|
|
2619
2757
|
const { teamId } = await resolveTeam(opts.team);
|
|
2620
|
-
const spinner = (0,
|
|
2758
|
+
const spinner = (0, import_ora9.default)("Creating tenant...").start();
|
|
2621
2759
|
try {
|
|
2622
2760
|
const out = await admin({
|
|
2623
2761
|
method: "POST",
|
|
@@ -2625,7 +2763,7 @@ async function runTenantCreate(opts) {
|
|
|
2625
2763
|
body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
|
|
2626
2764
|
summary: `Create tenant "${opts.name}"`
|
|
2627
2765
|
});
|
|
2628
|
-
spinner.succeed(`Created tenant ${
|
|
2766
|
+
spinner.succeed(`Created tenant ${import_chalk23.default.bold(out?.tenant_name ?? opts.name)}.`);
|
|
2629
2767
|
if (opts.json) console.log(JSON.stringify(out));
|
|
2630
2768
|
} catch (err) {
|
|
2631
2769
|
spinner.fail("Tenant create failed.");
|
|
@@ -2634,12 +2772,12 @@ async function runTenantCreate(opts) {
|
|
|
2634
2772
|
}
|
|
2635
2773
|
async function runTenantAttach(project, opts) {
|
|
2636
2774
|
if (!opts.tenant) {
|
|
2637
|
-
console.error(
|
|
2775
|
+
console.error(import_chalk23.default.red("--tenant <slug> is required."));
|
|
2638
2776
|
process.exit(1);
|
|
2639
2777
|
}
|
|
2640
2778
|
const { teamId } = await resolveTeam(opts.team);
|
|
2641
2779
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2642
|
-
const spinner = (0,
|
|
2780
|
+
const spinner = (0, import_ora9.default)("Attaching tenant...").start();
|
|
2643
2781
|
try {
|
|
2644
2782
|
const out = await admin({
|
|
2645
2783
|
method: "POST",
|
|
@@ -2662,11 +2800,11 @@ async function runTenantDelete(slug, opts) {
|
|
|
2662
2800
|
{ type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
|
|
2663
2801
|
]);
|
|
2664
2802
|
if (!confirm) {
|
|
2665
|
-
console.log(
|
|
2803
|
+
console.log(import_chalk23.default.dim("Aborted."));
|
|
2666
2804
|
return;
|
|
2667
2805
|
}
|
|
2668
2806
|
}
|
|
2669
|
-
const spinner = (0,
|
|
2807
|
+
const spinner = (0, import_ora9.default)("Deleting tenant...").start();
|
|
2670
2808
|
try {
|
|
2671
2809
|
await admin({
|
|
2672
2810
|
method: "DELETE",
|
|
@@ -2681,13 +2819,13 @@ async function runTenantDelete(slug, opts) {
|
|
|
2681
2819
|
}
|
|
2682
2820
|
async function runTenantCors(opts) {
|
|
2683
2821
|
if (!opts.tenant) {
|
|
2684
|
-
console.error(
|
|
2822
|
+
console.error(import_chalk23.default.red("--tenant <slug> is required."));
|
|
2685
2823
|
process.exit(1);
|
|
2686
2824
|
}
|
|
2687
2825
|
const { teamId } = await resolveTeam(opts.team);
|
|
2688
2826
|
const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
2689
2827
|
const cors = origins.length ? { allowed_origins: origins } : null;
|
|
2690
|
-
const spinner = (0,
|
|
2828
|
+
const spinner = (0, import_ora9.default)("Updating CORS...").start();
|
|
2691
2829
|
try {
|
|
2692
2830
|
await admin({
|
|
2693
2831
|
method: "PUT",
|
|
@@ -2702,10 +2840,458 @@ async function runTenantCors(opts) {
|
|
|
2702
2840
|
}
|
|
2703
2841
|
}
|
|
2704
2842
|
|
|
2843
|
+
// src/commands/tenant-drill.ts
|
|
2844
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
2845
|
+
var import_ora10 = __toESM(require("ora"));
|
|
2846
|
+
var import_crypto = require("crypto");
|
|
2847
|
+
init_admin();
|
|
2848
|
+
init_auth();
|
|
2849
|
+
init_tenant_pick();
|
|
2850
|
+
init_api();
|
|
2851
|
+
var trailingComma = /\s*,\s*/;
|
|
2852
|
+
var parseList = (s) => s.split(trailingComma).map((x) => x.trim()).filter(Boolean);
|
|
2853
|
+
async function runTenantManage(query, opts) {
|
|
2854
|
+
const { teamId } = await resolveTeam(opts.team);
|
|
2855
|
+
const slug = opts.tenant ?? loadCredentialsTenant(query) ?? await pickTenant(teamId, { message: "Manage which tenant?", initialQuery: query, allowCreate: true });
|
|
2856
|
+
if (!slug) return;
|
|
2857
|
+
await tenantHome(teamId, slug);
|
|
2858
|
+
}
|
|
2859
|
+
function loadCredentialsTenant(query) {
|
|
2860
|
+
if (query) return void 0;
|
|
2861
|
+
return loadCredentials()?.activeTenant ?? void 0;
|
|
2862
|
+
}
|
|
2863
|
+
async function tenantHome(teamId, tenant2) {
|
|
2864
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
2865
|
+
const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
2866
|
+
console.log(import_chalk24.default.bold(`
|
|
2867
|
+
Tenant ${tenant2}`));
|
|
2868
|
+
console.log(import_chalk24.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
2869
|
+
for (; ; ) {
|
|
2870
|
+
const spinner = (0, import_ora10.default)("Reading tenant state...").start();
|
|
2871
|
+
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
2872
|
+
admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
|
|
2873
|
+
admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
|
|
2874
|
+
admin({ method: "GET", path: `${base}/admin-emails`, summary: "List consumer-admin emails" }).catch(() => null),
|
|
2875
|
+
admin({ method: "GET", path: `${base}/external-issuers`, summary: "List external issuers" }).catch(() => null),
|
|
2876
|
+
admin({ method: "GET", path: `${base}/opaque`, summary: "Read opaque validator" }).catch(() => null),
|
|
2877
|
+
admin({ method: "GET", path: `${base}/app-clients`, summary: "List app clients" }).catch(() => [])
|
|
2878
|
+
]).finally(() => spinner.stop());
|
|
2879
|
+
const nEmails = (emails?.admin_emails ?? []).length;
|
|
2880
|
+
const nIssuers = (issuers?.external_issuers ?? []).length;
|
|
2881
|
+
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
2882
|
+
const onOff = (b) => b ? import_chalk24.default.green("on") : import_chalk24.default.dim("off");
|
|
2883
|
+
const { pick: pick2 } = await inquirer2.prompt([{
|
|
2884
|
+
type: "list",
|
|
2885
|
+
name: "pick",
|
|
2886
|
+
message: `Tenant ${tenant2}:`,
|
|
2887
|
+
pageSize: 12,
|
|
2888
|
+
choices: [
|
|
2889
|
+
{ name: `App clients (${nClients}) ${import_chalk24.default.dim("OAuth clients your consumers log in through \u2014 providers live inside")}`, value: "clients" },
|
|
2890
|
+
{ name: `IAM enforcement: ${onOff(iam?.iam_enabled)} ${import_chalk24.default.dim("key/identity enforcement for this tenant")}`, value: "iam" },
|
|
2891
|
+
{ name: `CORS override: ${cors?.cors ? import_chalk24.default.cyan(JSON.stringify(cors.cors)) : import_chalk24.default.dim("(unset)")}`, value: "cors" },
|
|
2892
|
+
{ name: `Consumer-admin emails (${nEmails}) ${import_chalk24.default.dim("who may administer the tenant portal")}`, value: "emails" },
|
|
2893
|
+
{ name: `External JWT issuers (${nIssuers}) ${import_chalk24.default.dim("bring-your-own auth: trust tokens you already mint")}`, value: "issuers" },
|
|
2894
|
+
{ name: `Opaque-token validator: ${opaque?.opaque?.endpoint ? import_chalk24.default.cyan(opaque.opaque.endpoint) : import_chalk24.default.dim("(unset)")}`, value: "opaque" },
|
|
2895
|
+
{ name: "\u2190 Back", value: "back" }
|
|
2896
|
+
]
|
|
2897
|
+
}]);
|
|
2898
|
+
switch (pick2) {
|
|
2899
|
+
case "back":
|
|
2900
|
+
return;
|
|
2901
|
+
case "clients":
|
|
2902
|
+
await clientsMenu(teamId, tenant2, base);
|
|
2903
|
+
break;
|
|
2904
|
+
case "iam": {
|
|
2905
|
+
const { v } = await inquirer2.prompt([{ type: "confirm", name: "v", message: "Enable IAM enforcement?", default: !!iam?.iam_enabled }]);
|
|
2906
|
+
await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
2907
|
+
console.log(import_chalk24.default.green(` IAM enforcement ${v ? "enabled" : "disabled"}.`));
|
|
2908
|
+
break;
|
|
2909
|
+
}
|
|
2910
|
+
case "cors": {
|
|
2911
|
+
const { v } = await inquirer2.prompt([{
|
|
2912
|
+
type: "input",
|
|
2913
|
+
name: "v",
|
|
2914
|
+
message: 'CORS JSON (e.g. {"allow_all_origins":true} \u2014 "null" clears):',
|
|
2915
|
+
default: cors?.cors ? JSON.stringify(cors.cors) : ""
|
|
2916
|
+
}]);
|
|
2917
|
+
if (v === "") break;
|
|
2918
|
+
const parsed = v === "null" ? null : safeJson(v);
|
|
2919
|
+
if (parsed === void 0) {
|
|
2920
|
+
console.log(import_chalk24.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
2921
|
+
break;
|
|
2922
|
+
}
|
|
2923
|
+
await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
2924
|
+
console.log(import_chalk24.default.green(" CORS updated."));
|
|
2925
|
+
break;
|
|
2926
|
+
}
|
|
2927
|
+
case "emails":
|
|
2928
|
+
await emailsMenu(base, emails?.admin_emails ?? []);
|
|
2929
|
+
break;
|
|
2930
|
+
case "issuers":
|
|
2931
|
+
await issuersMenu(base, issuers?.external_issuers ?? []);
|
|
2932
|
+
break;
|
|
2933
|
+
case "opaque": {
|
|
2934
|
+
const cur = opaque?.opaque;
|
|
2935
|
+
const { mode } = await inquirer2.prompt([{
|
|
2936
|
+
type: "list",
|
|
2937
|
+
name: "mode",
|
|
2938
|
+
message: "Opaque-token validator:",
|
|
2939
|
+
choices: [
|
|
2940
|
+
{ name: cur ? "Replace it" : "Set one up", value: "set" },
|
|
2941
|
+
...cur ? [{ name: "Clear it", value: "clear" }] : [],
|
|
2942
|
+
{ name: "\u2190 Back", value: "back" }
|
|
2943
|
+
]
|
|
2944
|
+
}]);
|
|
2945
|
+
if (mode === "back") break;
|
|
2946
|
+
if (mode === "clear") {
|
|
2947
|
+
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
2948
|
+
console.log(import_chalk24.default.green(" Cleared."));
|
|
2949
|
+
break;
|
|
2950
|
+
}
|
|
2951
|
+
const a = await inquirer2.prompt([
|
|
2952
|
+
{ type: "input", name: "endpoint", message: "Introspection endpoint (https):", default: cur?.endpoint, validate: (s) => s.startsWith("https://") || "must be https" },
|
|
2953
|
+
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
2954
|
+
]);
|
|
2955
|
+
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
2956
|
+
console.log(import_chalk24.default.green(" Opaque validator set."));
|
|
2957
|
+
break;
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
function safeJson(s) {
|
|
2963
|
+
try {
|
|
2964
|
+
return JSON.parse(s);
|
|
2965
|
+
} catch {
|
|
2966
|
+
return void 0;
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
async function emailsMenu(base, emails) {
|
|
2970
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
2971
|
+
console.log();
|
|
2972
|
+
if (!emails.length) console.log(import_chalk24.default.dim(" No consumer-admin emails."));
|
|
2973
|
+
for (const e of emails) console.log(` ${import_chalk24.default.bold(e.email ?? e)} ${import_chalk24.default.dim(e.status ?? "")}`);
|
|
2974
|
+
const { act } = await inquirer2.prompt([{
|
|
2975
|
+
type: "list",
|
|
2976
|
+
name: "act",
|
|
2977
|
+
message: "Consumer-admin emails:",
|
|
2978
|
+
choices: [
|
|
2979
|
+
{ name: "Add an email", value: "add" },
|
|
2980
|
+
...emails.length ? [{ name: "Remove an email", value: "rm" }] : [],
|
|
2981
|
+
{ name: "\u2190 Back", value: "back" }
|
|
2982
|
+
]
|
|
2983
|
+
}]);
|
|
2984
|
+
if (act === "back") return;
|
|
2985
|
+
if (act === "add") {
|
|
2986
|
+
const { email } = await inquirer2.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
2987
|
+
await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
2988
|
+
console.log(import_chalk24.default.green(` ${email} added.`));
|
|
2989
|
+
} else {
|
|
2990
|
+
const { e } = await inquirer2.prompt([{
|
|
2991
|
+
type: "list",
|
|
2992
|
+
name: "e",
|
|
2993
|
+
message: "Remove which?",
|
|
2994
|
+
choices: [...emails.map((x) => ({ name: x.email ?? String(x), value: x.email ?? String(x) })), { name: "\u2190 Back", value: null }]
|
|
2995
|
+
}]);
|
|
2996
|
+
if (!e) return;
|
|
2997
|
+
await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
2998
|
+
console.log(import_chalk24.default.green(` ${e} removed.`));
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
async function issuersMenu(base, issuers) {
|
|
3002
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3003
|
+
console.log();
|
|
3004
|
+
if (!issuers.length) console.log(import_chalk24.default.dim(" No external issuers \u2014 consumers use APIblaze-issued tokens."));
|
|
3005
|
+
for (const i of issuers) console.log(` ${import_chalk24.default.bold(i.iss)} aud=${i.aud} ${import_chalk24.default.dim(i.sub_semantics ?? "")}`);
|
|
3006
|
+
const { act } = await inquirer2.prompt([{
|
|
3007
|
+
type: "list",
|
|
3008
|
+
name: "act",
|
|
3009
|
+
message: "External JWT issuers:",
|
|
3010
|
+
choices: [
|
|
3011
|
+
{ name: "Add / replace one issuer", value: "add" },
|
|
3012
|
+
...issuers.length ? [{ name: "Delete an issuer", value: "rm" }] : [],
|
|
3013
|
+
{ name: "\u2190 Back", value: "back" }
|
|
3014
|
+
]
|
|
3015
|
+
}]);
|
|
3016
|
+
if (act === "back") return;
|
|
3017
|
+
if (act === "add") {
|
|
3018
|
+
const a = await inquirer2.prompt([
|
|
3019
|
+
{ type: "input", name: "iss", message: "Issuer URL (iss):", validate: (s) => !!s.trim() || "required" },
|
|
3020
|
+
{ type: "input", name: "aud", message: "Audience (aud):", validate: (s) => !!s.trim() || "required" },
|
|
3021
|
+
{ type: "input", name: "jwks", message: "JWKS URL (empty = derive from issuer):" },
|
|
3022
|
+
{ type: "list", name: "sem", message: "Where is the end-user id?", choices: [
|
|
3023
|
+
{ name: "The token sub IS the end user (tenant-owned)", value: "tenant_owned" },
|
|
3024
|
+
{ name: "Extract it from a claim\u2026", value: "extract_from_claim" }
|
|
3025
|
+
] }
|
|
3026
|
+
]);
|
|
3027
|
+
const claim = a.sem === "extract_from_claim" ? (await inquirer2.prompt([{ type: "input", name: "c", message: "Claim name:", validate: (s) => !!s.trim() || "required" }])).c : void 0;
|
|
3028
|
+
await admin({
|
|
3029
|
+
method: "POST",
|
|
3030
|
+
path: `${base}/external-issuers`,
|
|
3031
|
+
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3032
|
+
summary: `Add external issuer ${a.iss.trim()}`
|
|
3033
|
+
});
|
|
3034
|
+
console.log(import_chalk24.default.green(" Issuer saved."));
|
|
3035
|
+
} else {
|
|
3036
|
+
const { i } = await inquirer2.prompt([{
|
|
3037
|
+
type: "list",
|
|
3038
|
+
name: "i",
|
|
3039
|
+
message: "Delete which issuer?",
|
|
3040
|
+
choices: [...issuers.map((x) => ({ name: `${x.iss} (aud=${x.aud})`, value: x })), { name: "\u2190 Back", value: null }]
|
|
3041
|
+
}]);
|
|
3042
|
+
if (!i) return;
|
|
3043
|
+
await admin({
|
|
3044
|
+
method: "DELETE",
|
|
3045
|
+
path: `${base}/external-issuers?iss=${encodeURIComponent(i.iss)}&aud=${encodeURIComponent(i.aud)}`,
|
|
3046
|
+
summary: `Delete issuer ${i.iss}`
|
|
3047
|
+
});
|
|
3048
|
+
console.log(import_chalk24.default.green(" Issuer deleted."));
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
async function clientsMenu(teamId, tenant2, base) {
|
|
3052
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3053
|
+
for (; ; ) {
|
|
3054
|
+
const spinner = (0, import_ora10.default)("Loading app clients...").start();
|
|
3055
|
+
const raw = await admin({ method: "GET", path: `${base}/app-clients`, summary: "List app clients" }).catch(() => []);
|
|
3056
|
+
spinner.stop();
|
|
3057
|
+
const clients = Array.isArray(raw) ? raw : [];
|
|
3058
|
+
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3059
|
+
type: "list",
|
|
3060
|
+
name: "pick",
|
|
3061
|
+
message: `App clients of ${tenant2}:`,
|
|
3062
|
+
pageSize: 15,
|
|
3063
|
+
choices: [
|
|
3064
|
+
...clients.map((c) => ({
|
|
3065
|
+
name: `${import_chalk24.default.bold(c.name ?? c.clientId)} ${import_chalk24.default.dim(`${c.clientId}${c.projectName ? ` \xB7 ${c.projectName}` : ""}`)}`,
|
|
3066
|
+
value: c
|
|
3067
|
+
})),
|
|
3068
|
+
...clients.length ? [] : [new inquirer2.Separator(import_chalk24.default.dim(" no app clients yet"))],
|
|
3069
|
+
{ name: "\uFF0B Create an app client\u2026", value: " create" },
|
|
3070
|
+
{ name: "\u2190 Back", value: " back" }
|
|
3071
|
+
]
|
|
3072
|
+
}]);
|
|
3073
|
+
if (pick2 === " back") return;
|
|
3074
|
+
if (pick2 === " create") {
|
|
3075
|
+
const projects = await getProjects(teamId).catch(() => []);
|
|
3076
|
+
if (!projects.length) {
|
|
3077
|
+
console.log(import_chalk24.default.yellow(" No projects in this team \u2014 create a proxy first."));
|
|
3078
|
+
continue;
|
|
3079
|
+
}
|
|
3080
|
+
const a = await inquirer2.prompt([
|
|
3081
|
+
{ type: "input", name: "name", message: "Client name:", validate: (s) => !!s.trim() || "required" },
|
|
3082
|
+
{ type: "list", name: "proj", message: "For which project?", choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk24.default.dim("v" + p.apiVersion)}`, value: p })) },
|
|
3083
|
+
{ type: "input", name: "callbacks", message: "Callback URLs (comma-separated, empty = none):" }
|
|
3084
|
+
]);
|
|
3085
|
+
const created = await admin({
|
|
3086
|
+
method: "POST",
|
|
3087
|
+
path: `${base}/app-clients`,
|
|
3088
|
+
body: {
|
|
3089
|
+
name: a.name.trim(),
|
|
3090
|
+
projectName: a.proj.projectName,
|
|
3091
|
+
apiVersion: a.proj.apiVersion,
|
|
3092
|
+
...a.callbacks.trim() ? { authorizedCallbackUrls: parseList(a.callbacks) } : {}
|
|
3093
|
+
},
|
|
3094
|
+
summary: `Create app client "${a.name.trim()}"`
|
|
3095
|
+
});
|
|
3096
|
+
console.log(import_chalk24.default.green(` App client created${created?.clientId ? ` (${created.clientId})` : ""}.`));
|
|
3097
|
+
continue;
|
|
3098
|
+
}
|
|
3099
|
+
await clientHome(base, pick2);
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
async function clientHome(base, summary) {
|
|
3103
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3104
|
+
const id = summary.clientId ?? summary.client_id;
|
|
3105
|
+
const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
|
|
3106
|
+
for (; ; ) {
|
|
3107
|
+
const spinner = (0, import_ora10.default)("Reading app client...").start();
|
|
3108
|
+
const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
|
|
3109
|
+
spinner.stop();
|
|
3110
|
+
const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
|
|
3111
|
+
const scopes = c.scopes ?? [];
|
|
3112
|
+
const nProviders = (c.providers ?? []).length;
|
|
3113
|
+
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3114
|
+
type: "list",
|
|
3115
|
+
name: "pick",
|
|
3116
|
+
message: `${c.name ?? id}:`,
|
|
3117
|
+
pageSize: 12,
|
|
3118
|
+
choices: [
|
|
3119
|
+
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk24.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
|
|
3120
|
+
{ name: `Callback URLs: ${cb.length ? import_chalk24.default.cyan(cb.join(", ")) : import_chalk24.default.dim("(none)")}`, value: "callbacks" },
|
|
3121
|
+
{ name: `Scopes: ${scopes.length ? import_chalk24.default.cyan(scopes.join(" ")) : import_chalk24.default.dim("(defaults)")}`, value: "scopes" },
|
|
3122
|
+
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
3123
|
+
{ name: "Reveal client secret", value: "secret" },
|
|
3124
|
+
{ name: "Rotate client secret", value: "rotate" },
|
|
3125
|
+
{ name: import_chalk24.default.red("Delete this app client"), value: "delete" },
|
|
3126
|
+
{ name: "\u2190 Back", value: "back" }
|
|
3127
|
+
]
|
|
3128
|
+
}]);
|
|
3129
|
+
switch (pick2) {
|
|
3130
|
+
case "back":
|
|
3131
|
+
return;
|
|
3132
|
+
case "providers":
|
|
3133
|
+
await providersMenu(cBase, c.name ?? id);
|
|
3134
|
+
break;
|
|
3135
|
+
case "callbacks": {
|
|
3136
|
+
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
3137
|
+
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
3138
|
+
console.log(import_chalk24.default.green(" Callbacks updated."));
|
|
3139
|
+
break;
|
|
3140
|
+
}
|
|
3141
|
+
case "scopes": {
|
|
3142
|
+
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
3143
|
+
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
3144
|
+
console.log(import_chalk24.default.green(" Scopes updated."));
|
|
3145
|
+
break;
|
|
3146
|
+
}
|
|
3147
|
+
case "expiries": {
|
|
3148
|
+
const a = await inquirer2.prompt([
|
|
3149
|
+
{ type: "input", name: "access", message: "Access token expiry (seconds):", default: String(c.accessTokenExpiry ?? 3600) },
|
|
3150
|
+
{ type: "input", name: "id", message: "ID token expiry (seconds):", default: String(c.idTokenExpiry ?? 3600) },
|
|
3151
|
+
{ type: "input", name: "refresh", message: "Refresh token expiry (seconds):", default: String(c.refreshTokenExpiry ?? 2592e3) }
|
|
3152
|
+
]);
|
|
3153
|
+
await admin({
|
|
3154
|
+
method: "PATCH",
|
|
3155
|
+
path: cBase,
|
|
3156
|
+
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
3157
|
+
summary: "Update token expiries"
|
|
3158
|
+
});
|
|
3159
|
+
console.log(import_chalk24.default.green(" Expiries updated."));
|
|
3160
|
+
break;
|
|
3161
|
+
}
|
|
3162
|
+
case "secret": {
|
|
3163
|
+
const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
3164
|
+
if (!sure) break;
|
|
3165
|
+
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
3166
|
+
console.log(` ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3167
|
+
break;
|
|
3168
|
+
}
|
|
3169
|
+
case "rotate": {
|
|
3170
|
+
const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: "Rotate the secret? Existing integrations using it will break.", default: false }]);
|
|
3171
|
+
if (!sure) break;
|
|
3172
|
+
const fresh = randomSecret();
|
|
3173
|
+
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
3174
|
+
console.log(` New ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(fresh)} ${import_chalk24.default.dim("(store it now)")}`);
|
|
3175
|
+
break;
|
|
3176
|
+
}
|
|
3177
|
+
case "delete": {
|
|
3178
|
+
const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: `Delete app client "${c.name ?? id}"? Consumers logged in through it will lose access.`, default: false }]);
|
|
3179
|
+
if (!sure) break;
|
|
3180
|
+
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
3181
|
+
console.log(import_chalk24.default.green(" App client deleted."));
|
|
3182
|
+
return;
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
function randomSecret() {
|
|
3188
|
+
const bytes = new Uint8Array(24);
|
|
3189
|
+
(0, import_crypto.randomFillSync)(bytes);
|
|
3190
|
+
return Buffer.from(bytes).toString("base64url");
|
|
3191
|
+
}
|
|
3192
|
+
var PROVIDER_TYPES = ["google", "github", "microsoft", "facebook", "auth0", "other"];
|
|
3193
|
+
var DEFAULT_SCOPES = {
|
|
3194
|
+
google: "openid email profile",
|
|
3195
|
+
microsoft: "openid email profile",
|
|
3196
|
+
github: "read:user user:email",
|
|
3197
|
+
facebook: "public_profile email"
|
|
3198
|
+
};
|
|
3199
|
+
async function providersMenu(cBase, clientLabel) {
|
|
3200
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3201
|
+
for (; ; ) {
|
|
3202
|
+
const spinner = (0, import_ora10.default)("Loading providers...").start();
|
|
3203
|
+
const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
|
|
3204
|
+
spinner.stop();
|
|
3205
|
+
const providers = Array.isArray(raw) ? raw : [];
|
|
3206
|
+
console.log();
|
|
3207
|
+
for (const p of providers) {
|
|
3208
|
+
console.log(` ${import_chalk24.default.bold(p.type)} ${import_chalk24.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
|
|
3209
|
+
}
|
|
3210
|
+
if (!providers.length) console.log(import_chalk24.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
|
|
3211
|
+
const { act } = await inquirer2.prompt([{
|
|
3212
|
+
type: "list",
|
|
3213
|
+
name: "act",
|
|
3214
|
+
message: `Login providers of ${clientLabel}:`,
|
|
3215
|
+
choices: [
|
|
3216
|
+
{ name: "\uFF0B Add a provider", value: "add" },
|
|
3217
|
+
...providers.length ? [
|
|
3218
|
+
{ name: "Reveal a provider secret", value: "secret" },
|
|
3219
|
+
{ name: "Remove a provider", value: "rm" }
|
|
3220
|
+
] : [],
|
|
3221
|
+
{ name: "\u2190 Back", value: "back" }
|
|
3222
|
+
]
|
|
3223
|
+
}]);
|
|
3224
|
+
if (act === "back") return;
|
|
3225
|
+
if (act === "add") {
|
|
3226
|
+
const { type } = await inquirer2.prompt([{ type: "list", name: "type", message: "Provider:", choices: PROVIDER_TYPES }]);
|
|
3227
|
+
let body;
|
|
3228
|
+
if (type === "github") {
|
|
3229
|
+
const { managed } = await inquirer2.prompt([{
|
|
3230
|
+
type: "confirm",
|
|
3231
|
+
name: "managed",
|
|
3232
|
+
default: true,
|
|
3233
|
+
message: "Use the APIblaze-managed GitHub app (no credentials needed)?"
|
|
3234
|
+
}]);
|
|
3235
|
+
if (managed) body = { type, managed: true };
|
|
3236
|
+
}
|
|
3237
|
+
if (!body) {
|
|
3238
|
+
const a = await inquirer2.prompt([
|
|
3239
|
+
{ type: "input", name: "clientId", message: `${type} OAuth client id:`, validate: (s) => !!s.trim() || "required" },
|
|
3240
|
+
{ type: "password", name: "clientSecret", mask: "*", message: `${type} OAuth client secret:`, validate: (s) => s.length >= 6 && s.length <= 200 || "6\u2013200 chars" },
|
|
3241
|
+
...type === "auth0" || type === "other" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : [],
|
|
3242
|
+
{ type: "input", name: "scopes", message: "Scopes:", default: DEFAULT_SCOPES[type] ?? "" }
|
|
3243
|
+
]);
|
|
3244
|
+
body = {
|
|
3245
|
+
type,
|
|
3246
|
+
clientId: a.clientId.trim(),
|
|
3247
|
+
clientSecret: a.clientSecret,
|
|
3248
|
+
...a.domain?.trim() ? { domain: a.domain.trim() } : {},
|
|
3249
|
+
scopes: String(a.scopes).split(/[\s,]+/).filter(Boolean)
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
3252
|
+
const { routing } = await inquirer2.prompt([{
|
|
3253
|
+
type: "list",
|
|
3254
|
+
name: "routing",
|
|
3255
|
+
message: "What does your upstream receive?",
|
|
3256
|
+
choices: [
|
|
3257
|
+
{ name: "APIblaze token (recommended \u2014 provider stays an identity source)", value: null },
|
|
3258
|
+
{ name: `The ${type} access token`, value: "third_party_access_token" },
|
|
3259
|
+
{ name: `The ${type} id token`, value: "third_party_id_token" },
|
|
3260
|
+
{ name: "Nothing (strip auth)", value: "none" }
|
|
3261
|
+
]
|
|
3262
|
+
}]);
|
|
3263
|
+
if (routing) {
|
|
3264
|
+
body.tokenType = "thirdParty";
|
|
3265
|
+
body.targetServerToken = routing;
|
|
3266
|
+
}
|
|
3267
|
+
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
3268
|
+
console.log(import_chalk24.default.green(` ${type} provider added.`));
|
|
3269
|
+
} else {
|
|
3270
|
+
const { p } = await inquirer2.prompt([{
|
|
3271
|
+
type: "list",
|
|
3272
|
+
name: "p",
|
|
3273
|
+
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
3274
|
+
choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk24.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
|
|
3275
|
+
}]);
|
|
3276
|
+
if (!p) continue;
|
|
3277
|
+
if (act === "rm") {
|
|
3278
|
+
const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: `Remove the ${p.type} provider? Consumers using it can no longer sign in.`, default: false }]);
|
|
3279
|
+
if (!sure) continue;
|
|
3280
|
+
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
3281
|
+
console.log(import_chalk24.default.green(` ${p.type} removed.`));
|
|
3282
|
+
} else {
|
|
3283
|
+
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
3284
|
+
console.log(` ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
|
|
2705
3290
|
// src/commands/spec.ts
|
|
2706
3291
|
var fs6 = __toESM(require("fs"));
|
|
2707
|
-
var
|
|
2708
|
-
var
|
|
3292
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
3293
|
+
var import_ora11 = __toESM(require("ora"));
|
|
3294
|
+
init_admin();
|
|
2709
3295
|
async function runSpecGet(project, opts) {
|
|
2710
3296
|
const { teamId } = await resolveTeam(opts.team);
|
|
2711
3297
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
@@ -2718,19 +3304,19 @@ async function runSpecGet(project, opts) {
|
|
|
2718
3304
|
}
|
|
2719
3305
|
async function runSpecSet(project, opts) {
|
|
2720
3306
|
if (!opts.file) {
|
|
2721
|
-
console.error(
|
|
3307
|
+
console.error(import_chalk25.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
2722
3308
|
process.exit(1);
|
|
2723
3309
|
}
|
|
2724
3310
|
let specContent;
|
|
2725
3311
|
try {
|
|
2726
3312
|
specContent = fs6.readFileSync(opts.file, "utf-8");
|
|
2727
3313
|
} catch {
|
|
2728
|
-
console.error(
|
|
3314
|
+
console.error(import_chalk25.default.red(`Cannot read file: ${opts.file}`));
|
|
2729
3315
|
process.exit(1);
|
|
2730
3316
|
}
|
|
2731
3317
|
const { teamId } = await resolveTeam(opts.team);
|
|
2732
3318
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2733
|
-
const spinner = (0,
|
|
3319
|
+
const spinner = (0, import_ora11.default)("Uploading spec...").start();
|
|
2734
3320
|
try {
|
|
2735
3321
|
const out = await admin({
|
|
2736
3322
|
method: "POST",
|
|
@@ -2747,12 +3333,13 @@ async function runSpecSet(project, opts) {
|
|
|
2747
3333
|
}
|
|
2748
3334
|
|
|
2749
3335
|
// src/commands/agent.ts
|
|
2750
|
-
var
|
|
2751
|
-
var
|
|
3336
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
3337
|
+
var import_ora12 = __toESM(require("ora"));
|
|
2752
3338
|
init_auth();
|
|
2753
3339
|
|
|
2754
3340
|
// src/lib/tools.ts
|
|
2755
|
-
var
|
|
3341
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
3342
|
+
init_admin();
|
|
2756
3343
|
init_api();
|
|
2757
3344
|
async function proj(teamId, name, version2) {
|
|
2758
3345
|
return resolveProject(teamId, name, version2);
|
|
@@ -2770,15 +3357,15 @@ var TOOLS = [
|
|
|
2770
3357
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
2771
3358
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
2772
3359
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
2773
|
-
const lines = [` ${
|
|
2774
|
-
if (res.devPortal) lines.push(` ${
|
|
3360
|
+
const lines = [` ${import_chalk26.default.dim("Proxy URL:")} ${import_chalk26.default.bold(url)}`];
|
|
3361
|
+
if (res.devPortal) lines.push(` ${import_chalk26.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
2775
3362
|
const envs = Object.keys(keys);
|
|
2776
3363
|
if (envs.length) {
|
|
2777
|
-
lines.push("", ` ${
|
|
3364
|
+
lines.push("", ` ${import_chalk26.default.bold("API keys")} ${import_chalk26.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
2778
3365
|
const w = Math.max(...envs.map((e) => e.length));
|
|
2779
|
-
for (const env of envs) lines.push(` ${
|
|
3366
|
+
for (const env of envs) lines.push(` ${import_chalk26.default.cyan(env.padEnd(w))} ${import_chalk26.default.green(keys[env])}`);
|
|
2780
3367
|
}
|
|
2781
|
-
if (tryIt) lines.push("", ` ${
|
|
3368
|
+
if (tryIt) lines.push("", ` ${import_chalk26.default.dim("Try it:")}`, ` ${import_chalk26.default.cyan(tryIt)}`);
|
|
2782
3369
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
2783
3370
|
}
|
|
2784
3371
|
},
|
|
@@ -2903,6 +3490,7 @@ function findTool(name) {
|
|
|
2903
3490
|
}
|
|
2904
3491
|
|
|
2905
3492
|
// src/commands/agent.ts
|
|
3493
|
+
init_trace();
|
|
2906
3494
|
init_types();
|
|
2907
3495
|
var DASHBOARD_BASE5 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
2908
3496
|
var MAX_TOOL_STEPS = 6;
|
|
@@ -2933,23 +3521,23 @@ function truncate(value, max = 1500) {
|
|
|
2933
3521
|
}
|
|
2934
3522
|
function printCost(llm) {
|
|
2935
3523
|
const usd = llm.cost > 0 ? `$${llm.cost.toFixed(4)}` : "<$0.0001";
|
|
2936
|
-
console.log(
|
|
3524
|
+
console.log(import_chalk27.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk27.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
|
|
2937
3525
|
}
|
|
2938
3526
|
async function runAgent(opts) {
|
|
2939
3527
|
requireAuth();
|
|
2940
3528
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
2941
3529
|
const { default: inquirer2 } = await import("inquirer");
|
|
2942
|
-
console.log(
|
|
2943
|
-
console.log(
|
|
3530
|
+
console.log(import_chalk27.default.bold("APIblaze agent") + import_chalk27.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
3531
|
+
console.log(import_chalk27.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
2944
3532
|
const history = [];
|
|
2945
3533
|
while (true) {
|
|
2946
|
-
const { input } = await inquirer2.prompt([{ type: "input", name: "input", message:
|
|
3534
|
+
const { input } = await inquirer2.prompt([{ type: "input", name: "input", message: import_chalk27.default.cyan("you") + " \u203A" }]);
|
|
2947
3535
|
const text = (input ?? "").trim();
|
|
2948
3536
|
if (!text) continue;
|
|
2949
3537
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
2950
3538
|
history.push({ role: "user", content: text });
|
|
2951
3539
|
for (let step = 0; step < MAX_TOOL_STEPS; step++) {
|
|
2952
|
-
const spinner = (0,
|
|
3540
|
+
const spinner = (0, import_ora12.default)({ text: "thinking...", color: "magenta" }).start();
|
|
2953
3541
|
let resp;
|
|
2954
3542
|
try {
|
|
2955
3543
|
resp = await callAgent(history, teamId);
|
|
@@ -2957,21 +3545,21 @@ async function runAgent(opts) {
|
|
|
2957
3545
|
} catch (err) {
|
|
2958
3546
|
spinner.stop();
|
|
2959
3547
|
if (err instanceof ApiError && err.status === 402) {
|
|
2960
|
-
console.log(
|
|
3548
|
+
console.log(import_chalk27.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
2961
3549
|
break;
|
|
2962
3550
|
}
|
|
2963
3551
|
throw err;
|
|
2964
3552
|
}
|
|
2965
3553
|
history.push({ role: "assistant", content: resp.raw });
|
|
2966
3554
|
printCost(resp.llm);
|
|
2967
|
-
if (resp.reply) console.log(
|
|
3555
|
+
if (resp.reply) console.log(import_chalk27.default.green("agent") + " \u203A " + resp.reply);
|
|
2968
3556
|
if (!resp.action) break;
|
|
2969
3557
|
const tool = findTool(resp.action.tool);
|
|
2970
3558
|
if (!tool) {
|
|
2971
3559
|
history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
|
|
2972
3560
|
continue;
|
|
2973
3561
|
}
|
|
2974
|
-
const runSpinner = (0,
|
|
3562
|
+
const runSpinner = (0, import_ora12.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
|
|
2975
3563
|
try {
|
|
2976
3564
|
const result = await tool.run(resp.action.args, { teamId });
|
|
2977
3565
|
runSpinner.succeed(`${tool.name} \u2713`);
|
|
@@ -2989,11 +3577,11 @@ async function runAgent(opts) {
|
|
|
2989
3577
|
}
|
|
2990
3578
|
renderTrace();
|
|
2991
3579
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
2992
|
-
console.log(
|
|
3580
|
+
console.log(import_chalk27.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
2993
3581
|
}
|
|
2994
3582
|
}
|
|
2995
3583
|
}
|
|
2996
|
-
console.log(
|
|
3584
|
+
console.log(import_chalk27.default.dim("\nBye."));
|
|
2997
3585
|
}
|
|
2998
3586
|
|
|
2999
3587
|
// src/commands/config-browse.ts
|
|
@@ -3140,7 +3728,7 @@ var SETTING_GROUPS = ["Basics", "Traffic & limits", "Access & auth", "Portal & M
|
|
|
3140
3728
|
var FEATURES = [
|
|
3141
3729
|
{ go: "transforms", label: "Transforms", desc: "Rewrite requests/responses (headers, body fields) without touching your upstream" },
|
|
3142
3730
|
{ go: "mappings", label: "Mapping tables", desc: "Reusable value-mapping tables used by map transforms (values can be hidden/encrypted)" },
|
|
3143
|
-
{ go: "tenants", label: "Tenants", desc: "
|
|
3731
|
+
{ go: "tenants", label: "Tenants", desc: "Consumer groups \u2014 portal, login app clients & providers, issuers, IAM, CORS" },
|
|
3144
3732
|
{ go: "domains", label: "Custom domains", desc: "Serve the proxy on your own hostname + choose what the bare URL serves" },
|
|
3145
3733
|
{ go: "spec", label: "OpenAPI spec & traffic", desc: "View the stored spec, refresh it from source, or build it from captured traffic" },
|
|
3146
3734
|
{ go: "agents", label: "AI agents", desc: "Chat to build your spec, design access rules, or publish an MCP server (billed per turn)" },
|
|
@@ -3156,11 +3744,11 @@ function dig(blob, dotted) {
|
|
|
3156
3744
|
}
|
|
3157
3745
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
3158
3746
|
function show(v) {
|
|
3159
|
-
if (v === void 0) return
|
|
3160
|
-
if (v === null) return
|
|
3161
|
-
if (typeof v === "object") return
|
|
3162
|
-
if (typeof v === "boolean") return v ?
|
|
3163
|
-
return
|
|
3747
|
+
if (v === void 0) return import_chalk28.default.dim("(unset)");
|
|
3748
|
+
if (v === null) return import_chalk28.default.dim("null");
|
|
3749
|
+
if (typeof v === "object") return import_chalk28.default.cyan(JSON.stringify(v));
|
|
3750
|
+
if (typeof v === "boolean") return v ? import_chalk28.default.green("on") : import_chalk28.default.red("off");
|
|
3751
|
+
return import_chalk28.default.cyan(String(v));
|
|
3164
3752
|
}
|
|
3165
3753
|
function parseValue(raw) {
|
|
3166
3754
|
if (raw === "true") return true;
|
|
@@ -3187,7 +3775,7 @@ async function fetchConfigBlob(proj2) {
|
|
|
3187
3775
|
}
|
|
3188
3776
|
async function patchSetting(proj2, s, value, cfg) {
|
|
3189
3777
|
const body = s.toPatch(value, cfg);
|
|
3190
|
-
const spinner = (0,
|
|
3778
|
+
const spinner = (0, import_ora13.default)(`Set ${s.key}...`).start();
|
|
3191
3779
|
try {
|
|
3192
3780
|
await admin({
|
|
3193
3781
|
method: "PATCH",
|
|
@@ -3202,10 +3790,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
3202
3790
|
}
|
|
3203
3791
|
}
|
|
3204
3792
|
var loginFirst = (what) => {
|
|
3205
|
-
console.log(
|
|
3793
|
+
console.log(import_chalk28.default.yellow(`
|
|
3206
3794
|
Log in first to ${what}.`));
|
|
3207
|
-
console.log(
|
|
3208
|
-
console.log(
|
|
3795
|
+
console.log(import_chalk28.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
|
|
3796
|
+
console.log(import_chalk28.default.dim(" anonymously and want to bring it into your account.\n"));
|
|
3209
3797
|
};
|
|
3210
3798
|
async function runConfig(project, key, value, opts) {
|
|
3211
3799
|
const creds = loadCredentials();
|
|
@@ -3222,9 +3810,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
3222
3810
|
}
|
|
3223
3811
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
3224
3812
|
if (!setting) {
|
|
3225
|
-
console.error(
|
|
3226
|
-
console.error(
|
|
3227
|
-
console.error(
|
|
3813
|
+
console.error(import_chalk28.default.red(`Unknown setting "${key}".`));
|
|
3814
|
+
console.error(import_chalk28.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
|
|
3815
|
+
console.error(import_chalk28.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
|
|
3228
3816
|
process.exit(1);
|
|
3229
3817
|
}
|
|
3230
3818
|
if (value === void 0) {
|
|
@@ -3239,7 +3827,7 @@ async function pickProject(teamId) {
|
|
|
3239
3827
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
3240
3828
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
3241
3829
|
if (!projects.length) {
|
|
3242
|
-
console.error(
|
|
3830
|
+
console.error(import_chalk28.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
3243
3831
|
process.exit(1);
|
|
3244
3832
|
}
|
|
3245
3833
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -3247,7 +3835,7 @@ async function pickProject(teamId) {
|
|
|
3247
3835
|
type: "list",
|
|
3248
3836
|
name: "picked",
|
|
3249
3837
|
message: "Which project?",
|
|
3250
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
3838
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk28.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
3251
3839
|
}]);
|
|
3252
3840
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
3253
3841
|
}
|
|
@@ -3258,25 +3846,25 @@ function printAll(proj2, cfg, json) {
|
|
|
3258
3846
|
console.log(JSON.stringify(out, null, 2));
|
|
3259
3847
|
return;
|
|
3260
3848
|
}
|
|
3261
|
-
console.log(
|
|
3849
|
+
console.log(import_chalk28.default.bold(`
|
|
3262
3850
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
3263
3851
|
`));
|
|
3264
3852
|
for (const group of SETTING_GROUPS) {
|
|
3265
|
-
console.log(
|
|
3853
|
+
console.log(import_chalk28.default.bold(group));
|
|
3266
3854
|
for (const s of SETTINGS.filter((x) => x.group === group)) {
|
|
3267
|
-
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${
|
|
3855
|
+
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk28.default.dim(s.desc)}`);
|
|
3268
3856
|
}
|
|
3269
3857
|
console.log();
|
|
3270
3858
|
}
|
|
3271
|
-
console.log(
|
|
3859
|
+
console.log(import_chalk28.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
3272
3860
|
}
|
|
3273
3861
|
async function discoveryMenu(project) {
|
|
3274
3862
|
const { default: inquirer2 } = await import("inquirer");
|
|
3275
|
-
console.log(
|
|
3863
|
+
console.log(import_chalk28.default.bold(`
|
|
3276
3864
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
3277
3865
|
`));
|
|
3278
|
-
console.log(
|
|
3279
|
-
console.log(
|
|
3866
|
+
console.log(import_chalk28.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
|
|
3867
|
+
console.log(import_chalk28.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
|
|
3280
3868
|
for (; ; ) {
|
|
3281
3869
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3282
3870
|
type: "list",
|
|
@@ -3284,13 +3872,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
3284
3872
|
message: "Explore:",
|
|
3285
3873
|
pageSize: 20,
|
|
3286
3874
|
choices: [
|
|
3287
|
-
new inquirer2.Separator(
|
|
3875
|
+
new inquirer2.Separator(import_chalk28.default.bold("\u2014 Settings \u2014")),
|
|
3288
3876
|
...SETTING_GROUPS.map((g) => ({
|
|
3289
|
-
name: `${g} ${
|
|
3877
|
+
name: `${g} ${import_chalk28.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
3290
3878
|
value: { kind: "settings", g }
|
|
3291
3879
|
})),
|
|
3292
|
-
new inquirer2.Separator(
|
|
3293
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
3880
|
+
new inquirer2.Separator(import_chalk28.default.bold("\u2014 Features \u2014")),
|
|
3881
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk28.default.dim(f.desc)}`, value: { kind: "feature", f } })),
|
|
3294
3882
|
new inquirer2.Separator(),
|
|
3295
3883
|
{ name: "Exit", value: { kind: "exit" } }
|
|
3296
3884
|
]
|
|
@@ -3299,24 +3887,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
3299
3887
|
if (pick2.kind === "settings") {
|
|
3300
3888
|
console.log();
|
|
3301
3889
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
3302
|
-
console.log(` ${
|
|
3303
|
-
console.log(` ${
|
|
3890
|
+
console.log(` ${import_chalk28.default.bold(s.label.padEnd(28))} ${import_chalk28.default.dim(s.desc)}`);
|
|
3891
|
+
console.log(` ${import_chalk28.default.dim(" key: " + s.key)}`);
|
|
3304
3892
|
}
|
|
3305
3893
|
loginFirst("view or change these settings");
|
|
3306
3894
|
} else {
|
|
3307
3895
|
const f = pick2.f;
|
|
3308
3896
|
console.log(`
|
|
3309
|
-
${
|
|
3897
|
+
${import_chalk28.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
3310
3898
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
3311
3899
|
}
|
|
3312
3900
|
}
|
|
3313
3901
|
}
|
|
3314
3902
|
async function navigator(proj2, cfg, opts) {
|
|
3315
3903
|
const { default: inquirer2 } = await import("inquirer");
|
|
3316
|
-
console.log(
|
|
3904
|
+
console.log(import_chalk28.default.bold(`
|
|
3317
3905
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
3318
3906
|
`));
|
|
3319
|
-
console.log(
|
|
3907
|
+
console.log(import_chalk28.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
|
|
3320
3908
|
let blob = cfg;
|
|
3321
3909
|
for (; ; ) {
|
|
3322
3910
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
@@ -3325,10 +3913,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
3325
3913
|
message: "Where to?",
|
|
3326
3914
|
pageSize: 20,
|
|
3327
3915
|
choices: [
|
|
3328
|
-
new inquirer2.Separator(
|
|
3916
|
+
new inquirer2.Separator(import_chalk28.default.bold("\u2014 Settings \u2014")),
|
|
3329
3917
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
3330
|
-
new inquirer2.Separator(
|
|
3331
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
3918
|
+
new inquirer2.Separator(import_chalk28.default.bold("\u2014 Features \u2014")),
|
|
3919
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk28.default.dim(f.desc)}`, value: { kind: f.go } })),
|
|
3332
3920
|
new inquirer2.Separator(),
|
|
3333
3921
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
3334
3922
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -3368,7 +3956,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
3368
3956
|
}
|
|
3369
3957
|
}
|
|
3370
3958
|
} catch (err) {
|
|
3371
|
-
console.error(
|
|
3959
|
+
console.error(import_chalk28.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
3372
3960
|
}
|
|
3373
3961
|
}
|
|
3374
3962
|
}
|
|
@@ -3382,7 +3970,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
3382
3970
|
message: group + ":",
|
|
3383
3971
|
pageSize: 16,
|
|
3384
3972
|
choices: [
|
|
3385
|
-
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${
|
|
3973
|
+
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk28.default.dim(s2.desc)}`, value: s2 })),
|
|
3386
3974
|
new inquirer2.Separator(),
|
|
3387
3975
|
{ name: "\u2190 Back", value: null }
|
|
3388
3976
|
]
|
|
@@ -3400,7 +3988,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
3400
3988
|
} else if (s.type === "number") {
|
|
3401
3989
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
3402
3990
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
3403
|
-
console.log(
|
|
3991
|
+
console.log(import_chalk28.default.yellow(" Not a number \u2014 unchanged."));
|
|
3404
3992
|
continue;
|
|
3405
3993
|
}
|
|
3406
3994
|
value = Number(v);
|
|
@@ -3481,7 +4069,7 @@ async function buildCondition(phase) {
|
|
|
3481
4069
|
const items = [];
|
|
3482
4070
|
for (; ; ) {
|
|
3483
4071
|
const a = await inquirer2.prompt([
|
|
3484
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
4072
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk28.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
3485
4073
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
3486
4074
|
"eq",
|
|
3487
4075
|
"neq",
|
|
@@ -3512,7 +4100,7 @@ async function buildCondition(phase) {
|
|
|
3512
4100
|
function showCondition(cond) {
|
|
3513
4101
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
3514
4102
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
3515
|
-
return
|
|
4103
|
+
return import_chalk28.default.dim(` when ${s}`);
|
|
3516
4104
|
}
|
|
3517
4105
|
async function transformsMenu(proj2) {
|
|
3518
4106
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -3521,12 +4109,12 @@ async function transformsMenu(proj2) {
|
|
|
3521
4109
|
const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
|
|
3522
4110
|
const rules = out?.rules ?? [];
|
|
3523
4111
|
console.log();
|
|
3524
|
-
if (!rules.length) console.log(
|
|
4112
|
+
if (!rules.length) console.log(import_chalk28.default.dim(" No transform rules yet."));
|
|
3525
4113
|
for (const r of rules) {
|
|
3526
4114
|
const a = r.action ?? {};
|
|
3527
4115
|
const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
|
|
3528
|
-
const what = a.type === "hardcode" ? `${a.destination} = "${a.value}"` : a.type === "remove" ? `remove ${a.field}` : `${a.source} \u2192 ${a.destination}${a.lookup ? " (mapped)" : ""}${fns.length ?
|
|
3529
|
-
console.log(` ${r.enabled ?
|
|
4116
|
+
const what = a.type === "hardcode" ? `${a.destination} = "${a.value}"` : a.type === "remove" ? `remove ${a.field}` : `${a.source} \u2192 ${a.destination}${a.lookup ? " (mapped)" : ""}${fns.length ? import_chalk28.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
|
|
4117
|
+
console.log(` ${r.enabled ? import_chalk28.default.green("\u25CF") : import_chalk28.default.dim("\u25CB")} ${import_chalk28.default.bold(r.name)} ${import_chalk28.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
|
|
3530
4118
|
}
|
|
3531
4119
|
const { act } = await inquirer2.prompt([{
|
|
3532
4120
|
type: "list",
|
|
@@ -3538,7 +4126,7 @@ async function transformsMenu(proj2) {
|
|
|
3538
4126
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
3539
4127
|
{ name: "Delete a rule", value: "delete" }
|
|
3540
4128
|
] : [],
|
|
3541
|
-
{ name:
|
|
4129
|
+
{ name: import_chalk28.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
3542
4130
|
{ name: "\u2190 Back", value: "back" }
|
|
3543
4131
|
]
|
|
3544
4132
|
}]);
|
|
@@ -3551,11 +4139,11 @@ async function transformsMenu(proj2) {
|
|
|
3551
4139
|
}]);
|
|
3552
4140
|
const body = parseValue(raw);
|
|
3553
4141
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
3554
|
-
console.log(
|
|
4142
|
+
console.log(import_chalk28.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
3555
4143
|
continue;
|
|
3556
4144
|
}
|
|
3557
4145
|
await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
|
|
3558
|
-
console.log(
|
|
4146
|
+
console.log(import_chalk28.default.green(" Rule created."));
|
|
3559
4147
|
continue;
|
|
3560
4148
|
}
|
|
3561
4149
|
if (act === "add") {
|
|
@@ -3571,7 +4159,7 @@ async function transformsMenu(proj2) {
|
|
|
3571
4159
|
{ name: "Remove a field", value: "remove" }
|
|
3572
4160
|
] }
|
|
3573
4161
|
]);
|
|
3574
|
-
const fieldHint =
|
|
4162
|
+
const fieldHint = import_chalk28.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
3575
4163
|
let action2;
|
|
3576
4164
|
if (ans.kind === "hardcode") {
|
|
3577
4165
|
const a = await inquirer2.prompt([
|
|
@@ -3602,7 +4190,7 @@ async function transformsMenu(proj2) {
|
|
|
3602
4190
|
};
|
|
3603
4191
|
}
|
|
3604
4192
|
const condition = await buildCondition(ans.phase);
|
|
3605
|
-
const spinner = (0,
|
|
4193
|
+
const spinner = (0, import_ora13.default)("Creating rule...").start();
|
|
3606
4194
|
try {
|
|
3607
4195
|
await admin({
|
|
3608
4196
|
method: "POST",
|
|
@@ -3620,16 +4208,16 @@ async function transformsMenu(proj2) {
|
|
|
3620
4208
|
type: "list",
|
|
3621
4209
|
name: "rule",
|
|
3622
4210
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
3623
|
-
choices: [...rules.map((r) => ({ name: `${r.name} ${
|
|
4211
|
+
choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk28.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
|
|
3624
4212
|
}]);
|
|
3625
4213
|
if (!rule) continue;
|
|
3626
4214
|
if (act === "toggle") {
|
|
3627
4215
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
3628
4216
|
await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
3629
|
-
console.log(
|
|
4217
|
+
console.log(import_chalk28.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
3630
4218
|
} else {
|
|
3631
4219
|
await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
3632
|
-
console.log(
|
|
4220
|
+
console.log(import_chalk28.default.green(` ${rule.name} deleted.`));
|
|
3633
4221
|
}
|
|
3634
4222
|
}
|
|
3635
4223
|
}
|
|
@@ -3641,9 +4229,9 @@ async function mappingsMenu(proj2) {
|
|
|
3641
4229
|
const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
|
|
3642
4230
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
3643
4231
|
console.log();
|
|
3644
|
-
if (!tables.length) console.log(
|
|
4232
|
+
if (!tables.length) console.log(import_chalk28.default.dim(" No mapping tables yet."));
|
|
3645
4233
|
for (const t of tables) {
|
|
3646
|
-
console.log(` ${
|
|
4234
|
+
console.log(` ${import_chalk28.default.bold(t.name)} ${import_chalk28.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
|
|
3647
4235
|
}
|
|
3648
4236
|
const { act } = await inquirer2.prompt([{
|
|
3649
4237
|
type: "list",
|
|
@@ -3663,11 +4251,11 @@ async function mappingsMenu(proj2) {
|
|
|
3663
4251
|
]);
|
|
3664
4252
|
const entries2 = parseValue(a.entries);
|
|
3665
4253
|
if (!Array.isArray(entries2)) {
|
|
3666
|
-
console.log(
|
|
4254
|
+
console.log(import_chalk28.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
3667
4255
|
continue;
|
|
3668
4256
|
}
|
|
3669
4257
|
await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
3670
|
-
console.log(
|
|
4258
|
+
console.log(import_chalk28.default.green(` Table "${a.name}" created.`));
|
|
3671
4259
|
} else {
|
|
3672
4260
|
const { table } = await inquirer2.prompt([{
|
|
3673
4261
|
type: "list",
|
|
@@ -3677,7 +4265,7 @@ async function mappingsMenu(proj2) {
|
|
|
3677
4265
|
}]);
|
|
3678
4266
|
if (!table) continue;
|
|
3679
4267
|
await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
3680
|
-
console.log(
|
|
4268
|
+
console.log(import_chalk28.default.green(` ${table.name} deleted.`));
|
|
3681
4269
|
}
|
|
3682
4270
|
}
|
|
3683
4271
|
}
|
|
@@ -3688,22 +4276,22 @@ async function tenantsMenu(proj2, opts) {
|
|
|
3688
4276
|
const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
|
|
3689
4277
|
const tenants = out?.tenants ?? [];
|
|
3690
4278
|
console.log();
|
|
3691
|
-
if (!tenants.length) console.log(
|
|
3692
|
-
for (const t of tenants) console.log(` ${
|
|
4279
|
+
if (!tenants.length) console.log(import_chalk28.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
4280
|
+
for (const t of tenants) console.log(` ${import_chalk28.default.bold(t.tenant_name ?? t.name)} ${import_chalk28.default.dim(t.display_name ?? "")}`);
|
|
3693
4281
|
const { act } = await inquirer2.prompt([{
|
|
3694
4282
|
type: "list",
|
|
3695
4283
|
name: "act",
|
|
3696
4284
|
message: "Tenants:",
|
|
3697
4285
|
choices: [
|
|
3698
|
-
{ name:
|
|
3699
|
-
|
|
3700
|
-
{ name:
|
|
4286
|
+
{ name: `Manage a tenant\u2026 ${import_chalk28.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
|
|
4287
|
+
{ name: "Attach a tenant to this project", value: "attach" },
|
|
4288
|
+
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
3701
4289
|
{ name: "\u2190 Back", value: "back" }
|
|
3702
4290
|
]
|
|
3703
4291
|
}]);
|
|
3704
4292
|
if (act === "back") return;
|
|
3705
|
-
if (act === "
|
|
3706
|
-
|
|
4293
|
+
if (act === "manage") {
|
|
4294
|
+
await runTenantManage(void 0, { team: opts.team });
|
|
3707
4295
|
continue;
|
|
3708
4296
|
}
|
|
3709
4297
|
if (act === "attach") {
|
|
@@ -3718,7 +4306,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
3718
4306
|
}]);
|
|
3719
4307
|
if (!t) continue;
|
|
3720
4308
|
await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
3721
|
-
console.log(
|
|
4309
|
+
console.log(import_chalk28.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
3722
4310
|
}
|
|
3723
4311
|
}
|
|
3724
4312
|
}
|
|
@@ -3761,7 +4349,7 @@ async function specMenu(proj2, opts) {
|
|
|
3761
4349
|
choices: [
|
|
3762
4350
|
{ name: "Print the stored spec", value: "get" },
|
|
3763
4351
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
3764
|
-
{ name:
|
|
4352
|
+
{ name: import_chalk28.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
3765
4353
|
{ name: "\u2190 Back", value: "back" }
|
|
3766
4354
|
]
|
|
3767
4355
|
}]);
|
|
@@ -3769,7 +4357,7 @@ async function specMenu(proj2, opts) {
|
|
|
3769
4357
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
3770
4358
|
else if (act === "refresh") {
|
|
3771
4359
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
3772
|
-
console.log(
|
|
4360
|
+
console.log(import_chalk28.default.green(" Spec refresh triggered."));
|
|
3773
4361
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
3774
4362
|
}
|
|
3775
4363
|
async function agentsMenu(proj2, opts) {
|
|
@@ -3794,8 +4382,9 @@ async function agentsMenu(proj2, opts) {
|
|
|
3794
4382
|
}
|
|
3795
4383
|
|
|
3796
4384
|
// src/commands/key.ts
|
|
3797
|
-
var
|
|
3798
|
-
var
|
|
4385
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
4386
|
+
var import_ora14 = __toESM(require("ora"));
|
|
4387
|
+
init_admin();
|
|
3799
4388
|
async function runApikeysMenu(opts) {
|
|
3800
4389
|
await runKeyList(opts);
|
|
3801
4390
|
if (opts.json) return;
|
|
@@ -3822,11 +4411,11 @@ async function runKeyList(opts) {
|
|
|
3822
4411
|
return;
|
|
3823
4412
|
}
|
|
3824
4413
|
if (!keys.length) {
|
|
3825
|
-
console.log(
|
|
4414
|
+
console.log(import_chalk29.default.yellow("No developer keys."));
|
|
3826
4415
|
return;
|
|
3827
4416
|
}
|
|
3828
4417
|
for (const k of keys) {
|
|
3829
|
-
console.log(` ${
|
|
4418
|
+
console.log(` ${import_chalk29.default.bold(k.key_id ?? k.id)} ${import_chalk29.default.dim(k.description ?? "")} ${import_chalk29.default.dim(k.expires_at ?? "no expiry")}`);
|
|
3830
4419
|
}
|
|
3831
4420
|
}
|
|
3832
4421
|
async function runKeyMint(opts) {
|
|
@@ -3834,7 +4423,7 @@ async function runKeyMint(opts) {
|
|
|
3834
4423
|
const body = { role: "consumer-admin" };
|
|
3835
4424
|
if (opts.desc) body.description = opts.desc;
|
|
3836
4425
|
if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
|
|
3837
|
-
const spinner = (0,
|
|
4426
|
+
const spinner = (0, import_ora14.default)("Minting key...").start();
|
|
3838
4427
|
try {
|
|
3839
4428
|
const out = await admin({
|
|
3840
4429
|
method: "POST",
|
|
@@ -3847,9 +4436,9 @@ async function runKeyMint(opts) {
|
|
|
3847
4436
|
console.log(JSON.stringify(out));
|
|
3848
4437
|
return;
|
|
3849
4438
|
}
|
|
3850
|
-
console.log(` ${
|
|
3851
|
-
console.log(` ${
|
|
3852
|
-
if (out?.expires_at) console.log(` ${
|
|
4439
|
+
console.log(` ${import_chalk29.default.bold("key_id")}: ${out?.key_id}`);
|
|
4440
|
+
console.log(` ${import_chalk29.default.bold("key")}: ${import_chalk29.default.green(out?.key)} ${import_chalk29.default.dim("(shown once \u2014 store it now)")}`);
|
|
4441
|
+
if (out?.expires_at) console.log(` ${import_chalk29.default.dim("expires:")} ${out.expires_at}`);
|
|
3853
4442
|
} catch (err) {
|
|
3854
4443
|
spinner.fail("Mint failed.");
|
|
3855
4444
|
throw err;
|
|
@@ -3857,7 +4446,7 @@ async function runKeyMint(opts) {
|
|
|
3857
4446
|
}
|
|
3858
4447
|
async function runKeyRevoke(keyId, opts) {
|
|
3859
4448
|
const { teamId } = await resolveTeam(opts.team);
|
|
3860
|
-
const spinner = (0,
|
|
4449
|
+
const spinner = (0, import_ora14.default)("Revoking key...").start();
|
|
3861
4450
|
try {
|
|
3862
4451
|
await admin({
|
|
3863
4452
|
method: "DELETE",
|
|
@@ -3872,8 +4461,9 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
3872
4461
|
}
|
|
3873
4462
|
|
|
3874
4463
|
// src/commands/consumer.ts
|
|
3875
|
-
var
|
|
3876
|
-
var
|
|
4464
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
4465
|
+
var import_ora15 = __toESM(require("ora"));
|
|
4466
|
+
init_admin();
|
|
3877
4467
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
3878
4468
|
var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
|
|
3879
4469
|
async function consumerFetch(creds, suffix, init) {
|
|
@@ -3892,7 +4482,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
3892
4482
|
function requireConsumer() {
|
|
3893
4483
|
const c = loadConsumer();
|
|
3894
4484
|
if (!c) {
|
|
3895
|
-
console.error(
|
|
4485
|
+
console.error(import_chalk30.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
3896
4486
|
process.exit(1);
|
|
3897
4487
|
}
|
|
3898
4488
|
return c;
|
|
@@ -3903,48 +4493,37 @@ async function runConsumerLogin(opts) {
|
|
|
3903
4493
|
let clientId = opts.client;
|
|
3904
4494
|
if (clientId) {
|
|
3905
4495
|
if (!tenant2) {
|
|
3906
|
-
console.error(
|
|
4496
|
+
console.error(import_chalk30.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
3907
4497
|
process.exit(1);
|
|
3908
4498
|
}
|
|
3909
4499
|
} else {
|
|
3910
4500
|
requireAuth();
|
|
3911
4501
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
3912
|
-
const spinner = (0, import_ora13.default)("Loading your tenants...").start();
|
|
3913
|
-
const tdata = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants?detail=1`, summary: `List tenants for ${teamName ?? teamId}` });
|
|
3914
|
-
spinner.stop();
|
|
3915
|
-
const tenants = (tdata?.tenants ?? []).map(
|
|
3916
|
-
(t) => typeof t === "string" ? { tenant_name: t } : t
|
|
3917
|
-
);
|
|
3918
|
-
if (!tenants.length) {
|
|
3919
|
-
console.error(import_chalk28.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
3920
|
-
process.exit(1);
|
|
3921
|
-
}
|
|
3922
4502
|
if (!tenant2) {
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
}
|
|
4503
|
+
const { pickTenant: pickTenant2 } = await Promise.resolve().then(() => (init_tenant_pick(), tenant_pick_exports));
|
|
4504
|
+
const picked = await pickTenant2(teamId, { message: `Which tenant portal${teamName ? ` (team ${teamName})` : ""}?` });
|
|
4505
|
+
if (!picked) process.exit(1);
|
|
4506
|
+
tenant2 = picked;
|
|
3928
4507
|
}
|
|
3929
|
-
const s2 = (0,
|
|
4508
|
+
const s2 = (0, import_ora15.default)("Finding the login app...").start();
|
|
3930
4509
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
3931
4510
|
s2.stop();
|
|
3932
4511
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
3933
4512
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
3934
4513
|
if (!pick2) {
|
|
3935
|
-
console.error(
|
|
4514
|
+
console.error(import_chalk30.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
3936
4515
|
process.exit(1);
|
|
3937
4516
|
}
|
|
3938
4517
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
3939
4518
|
}
|
|
3940
4519
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
3941
|
-
console.log(`${
|
|
4520
|
+
console.log(`${import_chalk30.default.cyan("\u2192")} Logging in to ${import_chalk30.default.bold(tenant2)} as a consumer...`);
|
|
3942
4521
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
3943
4522
|
console.log(`
|
|
3944
|
-
Open: ${
|
|
3945
|
-
console.log(` Code: ${
|
|
4523
|
+
Open: ${import_chalk30.default.underline(verificationUri)}`);
|
|
4524
|
+
console.log(` Code: ${import_chalk30.default.bold(userCode)}
|
|
3946
4525
|
`);
|
|
3947
|
-
console.log(
|
|
4526
|
+
console.log(import_chalk30.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
3948
4527
|
}, portalResource);
|
|
3949
4528
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
3950
4529
|
const creds = {
|
|
@@ -3959,7 +4538,7 @@ async function runConsumerLogin(opts) {
|
|
|
3959
4538
|
obtainedAt: Date.now()
|
|
3960
4539
|
};
|
|
3961
4540
|
saveConsumer(creds);
|
|
3962
|
-
console.log(
|
|
4541
|
+
console.log(import_chalk30.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
3963
4542
|
}
|
|
3964
4543
|
async function runConsumerTokens(opts) {
|
|
3965
4544
|
const creds = requireConsumer();
|
|
@@ -3972,29 +4551,29 @@ async function runConsumerTokens(opts) {
|
|
|
3972
4551
|
console.log(JSON.stringify({ tenant: fresh.tenant, access_token: fresh.accessToken, refresh_token: fresh.refreshToken, id_token: fresh.idToken, expires_at: new Date(fresh.expiresAt).toISOString() }, null, 2));
|
|
3973
4552
|
return;
|
|
3974
4553
|
}
|
|
3975
|
-
console.log(`${
|
|
4554
|
+
console.log(`${import_chalk30.default.cyan("Consumer")} ${import_chalk30.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk30.default.bold(fresh.tenant)}
|
|
3976
4555
|
`);
|
|
3977
|
-
console.log(`${
|
|
4556
|
+
console.log(`${import_chalk30.default.bold("access_token")} ${import_chalk30.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
3978
4557
|
${fresh.accessToken}
|
|
3979
4558
|
`);
|
|
3980
|
-
if (fresh.idToken) console.log(`${
|
|
4559
|
+
if (fresh.idToken) console.log(`${import_chalk30.default.bold("id_token")} ${import_chalk30.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
3981
4560
|
${fresh.idToken}
|
|
3982
4561
|
`);
|
|
3983
|
-
if (fresh.refreshToken) console.log(`${
|
|
4562
|
+
if (fresh.refreshToken) console.log(`${import_chalk30.default.bold("refresh_token")}
|
|
3984
4563
|
${fresh.refreshToken}
|
|
3985
4564
|
`);
|
|
3986
|
-
console.log(
|
|
4565
|
+
console.log(import_chalk30.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
3987
4566
|
}
|
|
3988
4567
|
async function runConsumerApikeys(opts) {
|
|
3989
4568
|
const creds = requireConsumer();
|
|
3990
4569
|
const { default: inquirer2 } = await import("inquirer");
|
|
3991
|
-
const spinner = (0,
|
|
4570
|
+
const spinner = (0, import_ora15.default)("Loading your API keys...").start();
|
|
3992
4571
|
const list = await consumerFetch(creds, "/apikeys");
|
|
3993
4572
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
3994
4573
|
spinner.stop();
|
|
3995
4574
|
if (list.status >= 400) {
|
|
3996
|
-
console.error(
|
|
3997
|
-
if (list.status === 401) console.error(
|
|
4575
|
+
console.error(import_chalk30.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
4576
|
+
if (list.status === 401) console.error(import_chalk30.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
3998
4577
|
process.exit(1);
|
|
3999
4578
|
}
|
|
4000
4579
|
const keys = list.data?.keys ?? [];
|
|
@@ -4002,16 +4581,16 @@ async function runConsumerApikeys(opts) {
|
|
|
4002
4581
|
if (opts.json) {
|
|
4003
4582
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
4004
4583
|
} else if (!keys.length) {
|
|
4005
|
-
console.log(
|
|
4584
|
+
console.log(import_chalk30.default.yellow("No API keys yet."));
|
|
4006
4585
|
} else {
|
|
4007
4586
|
for (const k of keys) {
|
|
4008
4587
|
const clear = revealMap[k.environment]?.key;
|
|
4009
|
-
const shown = clear ?
|
|
4010
|
-
const exp = k.expires_at ?
|
|
4011
|
-
console.log(` ${
|
|
4588
|
+
const shown = clear ? import_chalk30.default.green(clear) : import_chalk30.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
4589
|
+
const exp = k.expires_at ? import_chalk30.default.dim(`exp ${k.expires_at}`) : import_chalk30.default.dim("no expiry");
|
|
4590
|
+
console.log(` ${import_chalk30.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk30.default.dim(k.description ?? "")}`);
|
|
4012
4591
|
}
|
|
4013
4592
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
4014
|
-
console.log(
|
|
4593
|
+
console.log(import_chalk30.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
4015
4594
|
}
|
|
4016
4595
|
}
|
|
4017
4596
|
if (opts.json) return;
|
|
@@ -4025,7 +4604,7 @@ async function runConsumerApikeys(opts) {
|
|
|
4025
4604
|
const body = { environment: answers.environment };
|
|
4026
4605
|
if (answers.description) body.description = answers.description;
|
|
4027
4606
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
4028
|
-
const s2 = (0,
|
|
4607
|
+
const s2 = (0, import_ora15.default)("Creating key...").start();
|
|
4029
4608
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
4030
4609
|
if (created.status >= 400) {
|
|
4031
4610
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -4033,15 +4612,16 @@ async function runConsumerApikeys(opts) {
|
|
|
4033
4612
|
}
|
|
4034
4613
|
s2.succeed("Key created.");
|
|
4035
4614
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
4036
|
-
if (key) console.log(` ${
|
|
4037
|
-
else console.log(
|
|
4615
|
+
if (key) console.log(` ${import_chalk30.default.green(key)} ${import_chalk30.default.dim("(shown once \u2014 store it now)")}`);
|
|
4616
|
+
else console.log(import_chalk30.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
4038
4617
|
}
|
|
4039
4618
|
|
|
4040
4619
|
// src/commands/sidecar.ts
|
|
4041
|
-
var
|
|
4042
|
-
var
|
|
4620
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
4621
|
+
var import_ora16 = __toESM(require("ora"));
|
|
4043
4622
|
var fs7 = __toESM(require("fs"));
|
|
4044
4623
|
var path4 = __toESM(require("path"));
|
|
4624
|
+
init_admin();
|
|
4045
4625
|
init_auth();
|
|
4046
4626
|
function detectNextProject(root) {
|
|
4047
4627
|
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs7.existsSync(path4.join(root, f)));
|
|
@@ -4079,18 +4659,18 @@ function upsertEnvLocal(root, token) {
|
|
|
4079
4659
|
}
|
|
4080
4660
|
function installSidecarPackage(root) {
|
|
4081
4661
|
if (fs7.existsSync(path4.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
4082
|
-
console.log(` ${
|
|
4662
|
+
console.log(` ${import_chalk31.default.green("\u2713")} apiblaze package already installed`);
|
|
4083
4663
|
return;
|
|
4084
4664
|
}
|
|
4085
4665
|
const has = (f) => fs7.existsSync(path4.join(root, f));
|
|
4086
4666
|
const pm = has("bun.lockb") || has("bun.lock") ? { cmd: "bun", add: "add" } : has("pnpm-lock.yaml") ? { cmd: "pnpm", add: "add" } : has("yarn.lock") ? { cmd: "yarn", add: "add" } : { cmd: "npm", add: "install" };
|
|
4087
|
-
const spinner = (0,
|
|
4667
|
+
const spinner = (0, import_ora16.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
4088
4668
|
try {
|
|
4089
4669
|
const { execSync } = require("child_process");
|
|
4090
4670
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
4091
4671
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
4092
4672
|
} catch {
|
|
4093
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
4673
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk31.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk31.default.cyan("npm run dev")}.`);
|
|
4094
4674
|
}
|
|
4095
4675
|
}
|
|
4096
4676
|
function readEnvKey(root) {
|
|
@@ -4229,7 +4809,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
4229
4809
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
4230
4810
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
4231
4811
|
if (opts.newSession) clearAnonCred2();
|
|
4232
|
-
const spinner = (0,
|
|
4812
|
+
const spinner = (0, import_ora16.default)("Setting up a sidecar (no login needed)...").start();
|
|
4233
4813
|
let out;
|
|
4234
4814
|
try {
|
|
4235
4815
|
out = await sidecarInitAnonymous2();
|
|
@@ -4241,29 +4821,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
4241
4821
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
4242
4822
|
const envState = upsertEnvLocal(root, out.token);
|
|
4243
4823
|
ensureGitignored(root);
|
|
4244
|
-
console.log(` ${
|
|
4245
|
-
console.log(` ${
|
|
4824
|
+
console.log(` ${import_chalk31.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
4825
|
+
console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
4246
4826
|
installSidecarPackage(root);
|
|
4247
4827
|
let inspectorPath = null;
|
|
4248
4828
|
if (!opts.noInspector) {
|
|
4249
4829
|
inspectorPath = generateInspector(root, router);
|
|
4250
|
-
if (inspectorPath) console.log(` ${
|
|
4830
|
+
if (inspectorPath) console.log(` ${import_chalk31.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
4251
4831
|
}
|
|
4252
4832
|
console.log("");
|
|
4253
|
-
console.log(
|
|
4254
|
-
console.log(` 1. ${
|
|
4833
|
+
console.log(import_chalk31.default.bold("Done (no account needed). What happens next:"));
|
|
4834
|
+
console.log(` 1. ${import_chalk31.default.cyan("npm run dev")} and use your app.`);
|
|
4255
4835
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
4256
|
-
console.log(` ${
|
|
4836
|
+
console.log(` ${import_chalk31.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
4257
4837
|
console.log("");
|
|
4258
|
-
console.log(
|
|
4259
|
-
console.log(` ${
|
|
4260
|
-
console.log(
|
|
4838
|
+
console.log(import_chalk31.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
4839
|
+
console.log(` ${import_chalk31.default.cyan("apiblaze login")} then ${import_chalk31.default.cyan("apiblaze claim")} ${import_chalk31.default.dim("(no code needed here)")}`);
|
|
4840
|
+
console.log(import_chalk31.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
4261
4841
|
}
|
|
4262
4842
|
async function runSidecar(opts) {
|
|
4263
4843
|
const root = path4.resolve(opts.dir ?? process.cwd());
|
|
4264
4844
|
const detected = detectNextProject(root);
|
|
4265
4845
|
if (!detected.found) {
|
|
4266
|
-
console.log(
|
|
4846
|
+
console.log(import_chalk31.default.yellow(`No Next.js project detected in ${root}.`));
|
|
4267
4847
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
4268
4848
|
return;
|
|
4269
4849
|
}
|
|
@@ -4274,10 +4854,10 @@ async function runSidecar(opts) {
|
|
|
4274
4854
|
if (!loadCredentials()) {
|
|
4275
4855
|
upsertEnvLocal(root, readEnvKey(root));
|
|
4276
4856
|
ensureGitignored(root);
|
|
4277
|
-
console.log(` ${
|
|
4278
|
-
console.log(` ${
|
|
4857
|
+
console.log(` ${import_chalk31.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
4858
|
+
console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
4279
4859
|
installSidecarPackage(root);
|
|
4280
|
-
console.log(
|
|
4860
|
+
console.log(import_chalk31.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
4281
4861
|
return;
|
|
4282
4862
|
}
|
|
4283
4863
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -4286,7 +4866,7 @@ async function runSidecar(opts) {
|
|
|
4286
4866
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
4287
4867
|
let token = existingKey ?? "";
|
|
4288
4868
|
if (mustMint) {
|
|
4289
|
-
const spinner = (0,
|
|
4869
|
+
const spinner = (0, import_ora16.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
|
|
4290
4870
|
try {
|
|
4291
4871
|
const out = await admin({
|
|
4292
4872
|
method: "POST",
|
|
@@ -4300,39 +4880,40 @@ async function runSidecar(opts) {
|
|
|
4300
4880
|
throw err;
|
|
4301
4881
|
}
|
|
4302
4882
|
} else {
|
|
4303
|
-
console.log(
|
|
4883
|
+
console.log(import_chalk31.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
4304
4884
|
}
|
|
4305
4885
|
const envState = upsertEnvLocal(root, token);
|
|
4306
4886
|
ensureGitignored(root);
|
|
4307
|
-
console.log(` ${
|
|
4887
|
+
console.log(` ${import_chalk31.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
4308
4888
|
const wireState = wireInstrumentation(root);
|
|
4309
|
-
console.log(` ${
|
|
4889
|
+
console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
4310
4890
|
installSidecarPackage(root);
|
|
4311
4891
|
let inspectorPath = null;
|
|
4312
4892
|
if (!opts.noInspector) {
|
|
4313
4893
|
inspectorPath = generateInspector(root, detected.router);
|
|
4314
|
-
if (inspectorPath) console.log(` ${
|
|
4894
|
+
if (inspectorPath) console.log(` ${import_chalk31.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
4315
4895
|
}
|
|
4316
4896
|
console.log("");
|
|
4317
|
-
console.log(
|
|
4318
|
-
console.log(` 1. ${
|
|
4319
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
4320
|
-
console.log(` 3. Approve the ones to route: ${
|
|
4897
|
+
console.log(import_chalk31.default.bold("Done. What happens next:"));
|
|
4898
|
+
console.log(` 1. ${import_chalk31.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
4899
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk31.default.bold("candidates")} \u2014 list them: ${import_chalk31.default.cyan("apiblaze sidecar")}`);
|
|
4900
|
+
console.log(` 3. Approve the ones to route: ${import_chalk31.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
4321
4901
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
4322
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
4323
|
-
if (switchingTeam) console.log(
|
|
4902
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk31.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path4.dirname(inspectorPath)} before shipping)`);
|
|
4903
|
+
if (switchingTeam) console.log(import_chalk31.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
4324
4904
|
console.log("");
|
|
4325
|
-
console.log(
|
|
4326
|
-
console.log(
|
|
4327
|
-
console.log(
|
|
4905
|
+
console.log(import_chalk31.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
4906
|
+
console.log(import_chalk31.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
4907
|
+
console.log(import_chalk31.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
4328
4908
|
console.log("");
|
|
4329
|
-
console.log(
|
|
4330
|
-
console.log(
|
|
4909
|
+
console.log(import_chalk31.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
4910
|
+
console.log(import_chalk31.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
4331
4911
|
}
|
|
4332
4912
|
|
|
4333
4913
|
// src/commands/origins.ts
|
|
4334
|
-
var
|
|
4335
|
-
var
|
|
4914
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
4915
|
+
var import_ora17 = __toESM(require("ora"));
|
|
4916
|
+
init_admin();
|
|
4336
4917
|
init_auth();
|
|
4337
4918
|
init_anon_cred();
|
|
4338
4919
|
async function runOriginsList(opts) {
|
|
@@ -4340,7 +4921,7 @@ async function runOriginsList(opts) {
|
|
|
4340
4921
|
if (!loadCredentials()) {
|
|
4341
4922
|
const cred = loadAnonCred();
|
|
4342
4923
|
if (!cred) {
|
|
4343
|
-
console.log(
|
|
4924
|
+
console.log(import_chalk32.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
4344
4925
|
return;
|
|
4345
4926
|
}
|
|
4346
4927
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -4358,30 +4939,30 @@ async function runOriginsList(opts) {
|
|
|
4358
4939
|
}
|
|
4359
4940
|
const routed = out.routed ?? [];
|
|
4360
4941
|
const candidates = out.candidates ?? [];
|
|
4361
|
-
console.log(
|
|
4942
|
+
console.log(import_chalk32.default.bold(`
|
|
4362
4943
|
Routed through APIblaze (${routed.length})`));
|
|
4363
|
-
if (!routed.length) console.log(
|
|
4364
|
-
for (const r of routed) console.log(` ${
|
|
4365
|
-
console.log(
|
|
4944
|
+
if (!routed.length) console.log(import_chalk32.default.dim(" none yet"));
|
|
4945
|
+
for (const r of routed) console.log(` ${import_chalk32.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk32.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
4946
|
+
console.log(import_chalk32.default.bold(`
|
|
4366
4947
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
4367
|
-
if (!candidates.length) console.log(
|
|
4948
|
+
if (!candidates.length) console.log(import_chalk32.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
4368
4949
|
for (const c of candidates) {
|
|
4369
|
-
console.log(` ${
|
|
4950
|
+
console.log(` ${import_chalk32.default.yellow("\u25CB")} ${c.origin} ${import_chalk32.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
4370
4951
|
}
|
|
4371
4952
|
if (candidates.length) {
|
|
4372
|
-
console.log(
|
|
4953
|
+
console.log(import_chalk32.default.dim(`
|
|
4373
4954
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
4374
|
-
console.log(
|
|
4955
|
+
console.log(import_chalk32.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
4375
4956
|
}
|
|
4376
4957
|
}
|
|
4377
4958
|
async function runOriginsApprove(origin, opts) {
|
|
4378
4959
|
if (!loadCredentials()) {
|
|
4379
4960
|
const cred = loadAnonCred();
|
|
4380
4961
|
if (!cred) {
|
|
4381
|
-
console.error(
|
|
4962
|
+
console.error(import_chalk32.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
4382
4963
|
process.exit(1);
|
|
4383
4964
|
}
|
|
4384
|
-
const spinner2 = (0,
|
|
4965
|
+
const spinner2 = (0, import_ora17.default)(`Approving ${origin} (anonymous)...`).start();
|
|
4385
4966
|
try {
|
|
4386
4967
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
4387
4968
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -4392,7 +4973,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
4392
4973
|
return;
|
|
4393
4974
|
}
|
|
4394
4975
|
const { teamId } = await resolveTeam(opts.team);
|
|
4395
|
-
const spinner = (0,
|
|
4976
|
+
const spinner = (0, import_ora17.default)(`Approving ${origin}...`).start();
|
|
4396
4977
|
try {
|
|
4397
4978
|
const out = await admin({
|
|
4398
4979
|
method: "POST",
|
|
@@ -4409,7 +4990,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
4409
4990
|
}
|
|
4410
4991
|
async function runOriginsDeny(origin, opts) {
|
|
4411
4992
|
const { teamId } = await resolveTeam(opts.team);
|
|
4412
|
-
const spinner = (0,
|
|
4993
|
+
const spinner = (0, import_ora17.default)(`Dismissing ${origin}...`).start();
|
|
4413
4994
|
try {
|
|
4414
4995
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
4415
4996
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -4420,7 +5001,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
4420
5001
|
}
|
|
4421
5002
|
async function runOriginsRemove(origin, opts) {
|
|
4422
5003
|
const { teamId } = await resolveTeam(opts.team);
|
|
4423
|
-
const spinner = (0,
|
|
5004
|
+
const spinner = (0, import_ora17.default)(`Removing the proxy for ${origin}...`).start();
|
|
4424
5005
|
try {
|
|
4425
5006
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
4426
5007
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -4431,8 +5012,9 @@ async function runOriginsRemove(origin, opts) {
|
|
|
4431
5012
|
}
|
|
4432
5013
|
|
|
4433
5014
|
// src/commands/op.ts
|
|
4434
|
-
var
|
|
5015
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
4435
5016
|
init_auth();
|
|
5017
|
+
init_trace();
|
|
4436
5018
|
init_types();
|
|
4437
5019
|
var OPERATOR_EMAILS = /* @__PURE__ */ new Set(["julienpmjacquet@gmail.com", "chkev@umich.edu"]);
|
|
4438
5020
|
var DASHBOARD_BASE6 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
@@ -4462,53 +5044,53 @@ async function opCall(call) {
|
|
|
4462
5044
|
function printResidue(report, applied) {
|
|
4463
5045
|
const up = report?.upstash ?? {};
|
|
4464
5046
|
const fga = report?.fga ?? {};
|
|
4465
|
-
console.log(
|
|
4466
|
-
console.log(
|
|
5047
|
+
console.log(import_chalk33.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
5048
|
+
console.log(import_chalk33.default.bold("\n Upstash"));
|
|
4467
5049
|
const orphans = up.orphans ?? [];
|
|
4468
|
-
if (orphans.length === 0) console.log(
|
|
4469
|
-
for (const o of orphans) console.log(` ${
|
|
4470
|
-
console.log(
|
|
4471
|
-
if (up.unknown?.length) console.log(
|
|
4472
|
-
if (applied) console.log(` ${
|
|
4473
|
-
for (const e of up.errors ?? []) console.log(
|
|
4474
|
-
console.log(
|
|
5050
|
+
if (orphans.length === 0) console.log(import_chalk33.default.green(" no orphaned keys"));
|
|
5051
|
+
for (const o of orphans) console.log(` ${import_chalk33.default.yellow(o.key)} ${import_chalk33.default.dim(`\u2014 ${o.reason}`)}`);
|
|
5052
|
+
console.log(import_chalk33.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
5053
|
+
if (up.unknown?.length) console.log(import_chalk33.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
5054
|
+
if (applied) console.log(` ${import_chalk33.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
5055
|
+
for (const e of up.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
|
|
5056
|
+
console.log(import_chalk33.default.bold("\n OpenFGA / Neon"));
|
|
4475
5057
|
if (applied) {
|
|
4476
5058
|
const swept = fga?.swept ?? [];
|
|
4477
|
-
if (swept.length === 0) console.log(
|
|
5059
|
+
if (swept.length === 0) console.log(import_chalk33.default.green(" no orphaned stores"));
|
|
4478
5060
|
for (const s of swept) {
|
|
4479
5061
|
console.log(
|
|
4480
|
-
` ${
|
|
5062
|
+
` ${import_chalk33.default.yellow(s.store_id)} ${import_chalk33.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
4481
5063
|
);
|
|
4482
5064
|
}
|
|
4483
|
-
if (fga?.remaining) console.log(
|
|
5065
|
+
if (fga?.remaining) console.log(import_chalk33.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
4484
5066
|
} else {
|
|
4485
5067
|
const fgaOrphans = fga?.orphans ?? [];
|
|
4486
|
-
if (fgaOrphans.length === 0) console.log(
|
|
5068
|
+
if (fgaOrphans.length === 0) console.log(import_chalk33.default.green(" no orphaned stores"));
|
|
4487
5069
|
for (const s of fgaOrphans) {
|
|
4488
5070
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
4489
|
-
console.log(` ${
|
|
5071
|
+
console.log(` ${import_chalk33.default.yellow(s.store_id)} ${import_chalk33.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
4490
5072
|
}
|
|
4491
|
-
console.log(
|
|
5073
|
+
console.log(import_chalk33.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
4492
5074
|
}
|
|
4493
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
5075
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
|
|
4494
5076
|
console.log();
|
|
4495
5077
|
}
|
|
4496
5078
|
async function runOp(sub, opts = {}) {
|
|
4497
5079
|
if (!loadCredentials()) {
|
|
4498
|
-
console.log(
|
|
5080
|
+
console.log(import_chalk33.default.dim("Not logged in. Run `apiblaze login`."));
|
|
4499
5081
|
return;
|
|
4500
5082
|
}
|
|
4501
5083
|
if (!isOperatorLogin()) {
|
|
4502
|
-
console.log(
|
|
5084
|
+
console.log(import_chalk33.default.dim("`apiblaze op` is only available to platform operators."));
|
|
4503
5085
|
return;
|
|
4504
5086
|
}
|
|
4505
5087
|
switch (sub) {
|
|
4506
5088
|
case void 0:
|
|
4507
5089
|
case "menu": {
|
|
4508
|
-
console.log(
|
|
4509
|
-
console.log(` ${
|
|
4510
|
-
console.log(` ${
|
|
4511
|
-
console.log(` ${
|
|
5090
|
+
console.log(import_chalk33.default.bold("\nOperator menu"));
|
|
5091
|
+
console.log(` ${import_chalk33.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
5092
|
+
console.log(` ${import_chalk33.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk33.default.dim("-y to skip")})`);
|
|
5093
|
+
console.log(` ${import_chalk33.default.cyan("apiblaze op credits")} list credit wallets
|
|
4512
5094
|
`);
|
|
4513
5095
|
return;
|
|
4514
5096
|
}
|
|
@@ -4524,15 +5106,15 @@ async function runOp(sub, opts = {}) {
|
|
|
4524
5106
|
const nFga = report?.fga?.orphans?.length ?? 0;
|
|
4525
5107
|
printResidue(report, false);
|
|
4526
5108
|
if (nUp + nFga === 0) {
|
|
4527
|
-
console.log(
|
|
5109
|
+
console.log(import_chalk33.default.green("Nothing to sweep."));
|
|
4528
5110
|
return;
|
|
4529
5111
|
}
|
|
4530
5112
|
if (!opts.yes) {
|
|
4531
5113
|
const readline2 = await import("readline/promises");
|
|
4532
5114
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
4533
|
-
const answer = await rl.question(
|
|
5115
|
+
const answer = await rl.question(import_chalk33.default.red(`Delete ${nUp} Upstash key(s) + ${nFga} OpenFGA store(s)? Type 'sweep' to confirm: `));
|
|
4534
5116
|
rl.close();
|
|
4535
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
5117
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk33.default.dim("Aborted."));
|
|
4536
5118
|
}
|
|
4537
5119
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
4538
5120
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -4543,19 +5125,20 @@ async function runOp(sub, opts = {}) {
|
|
|
4543
5125
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
4544
5126
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
4545
5127
|
const accounts = data?.accounts ?? [];
|
|
4546
|
-
if (accounts.length === 0) return void console.log(
|
|
5128
|
+
if (accounts.length === 0) return void console.log(import_chalk33.default.dim("No credit wallets."));
|
|
4547
5129
|
for (const a of accounts) {
|
|
4548
5130
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
4549
|
-
console.log(` ${
|
|
5131
|
+
console.log(` ${import_chalk33.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk33.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk33.default.dim(" \u2014 anon") : ""}`);
|
|
4550
5132
|
}
|
|
4551
5133
|
return;
|
|
4552
5134
|
}
|
|
4553
5135
|
default:
|
|
4554
|
-
console.log(
|
|
5136
|
+
console.log(import_chalk33.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
4555
5137
|
}
|
|
4556
5138
|
}
|
|
4557
5139
|
|
|
4558
5140
|
// src/index.ts
|
|
5141
|
+
init_trace();
|
|
4559
5142
|
var program = new import_commander.Command();
|
|
4560
5143
|
program.name("apiblaze").description("APIblaze CLI \u2014 create & manage API proxies and run dev tunnels").version(version).option("-v, --verbose", "Print the exact series of API calls each command makes (curl-equivalent you could run yourself)");
|
|
4561
5144
|
program.hook("preAction", () => {
|
|
@@ -4606,7 +5189,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
4606
5189
|
try {
|
|
4607
5190
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
4608
5191
|
if (Number.isNaN(resolved)) {
|
|
4609
|
-
console.error(
|
|
5192
|
+
console.error(import_chalk34.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
4610
5193
|
process.exit(1);
|
|
4611
5194
|
}
|
|
4612
5195
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -4665,6 +5248,8 @@ domain.command("status").description("Check a custom domain's validation status"
|
|
|
4665
5248
|
domain.command("rm").description("Remove a custom domain").argument("<project>", "Project name or id").requiredOption("--id <domainId>", "Domain id (see `domain list`)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainRemove(project, opts)));
|
|
4666
5249
|
domain.command("set-base").description("Choose which version/environment your main URL serves").argument("<project>", "Project name or id").option("--env <env>", "Environment (default: prod)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runDomainSetBase(project, opts)));
|
|
4667
5250
|
var tenant = program.command("tenant").description("Manage tenants \u2014 separate groups of your API's users");
|
|
5251
|
+
tenant.command("manage").description("Browse & edit one tenant: settings, login app clients, providers, issuers (search-first picker)").argument("[query]", "Search by tenant name/display name (omit to use your tenant scope or pick)").option("--tenant <slug>", "Exact tenant slug (skips the picker)").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantManage(query, opts)));
|
|
5252
|
+
tenant.command("use").description("Set the sticky tenant scope for future commands (search-first; --clear to unset)").argument("[query]", "Search by tenant name/display name").option("--clear", "Clear the tenant scope").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantUse(query, opts)));
|
|
4668
5253
|
tenant.command("list").description("List tenants in your team").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantList(opts)));
|
|
4669
5254
|
tenant.command("create").description("Create a tenant in your team").requiredOption("--name <display>", "Display name").option("--slug <tenant_name>", "Explicit tenant slug (generated if omitted)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantCreate(opts)));
|
|
4670
5255
|
tenant.command("attach").description("Attach a tenant to a proxy").argument("<project>", "Project name or id").requiredOption("--tenant <slug>", "Tenant slug to attach").option("--auth-config <id>", "Auth config id to bind").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTenantAttach(project, opts)));
|
|
@@ -4702,7 +5287,7 @@ function groupedCommandHelp() {
|
|
|
4702
5287
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
4703
5288
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
4704
5289
|
}).filter(Boolean).join("\n");
|
|
4705
|
-
return `${
|
|
5290
|
+
return `${import_chalk34.default.bold(g.title)}
|
|
4706
5291
|
${rows}`;
|
|
4707
5292
|
}).join("\n\n");
|
|
4708
5293
|
}
|
|
@@ -4730,13 +5315,13 @@ Examples:
|
|
|
4730
5315
|
`);
|
|
4731
5316
|
function printError(err) {
|
|
4732
5317
|
if (err instanceof ApiError) {
|
|
4733
|
-
console.error(
|
|
5318
|
+
console.error(import_chalk34.default.red(`
|
|
4734
5319
|
API error (${err.status}): ${err.message}`));
|
|
4735
5320
|
} else if (err instanceof Error) {
|
|
4736
|
-
console.error(
|
|
5321
|
+
console.error(import_chalk34.default.red(`
|
|
4737
5322
|
Error: ${err.message}`));
|
|
4738
5323
|
} else {
|
|
4739
|
-
console.error(
|
|
5324
|
+
console.error(import_chalk34.default.red("\nUnknown error"));
|
|
4740
5325
|
}
|
|
4741
5326
|
}
|
|
4742
5327
|
program.parse(process.argv);
|