apiblaze 0.11.1 → 0.12.1
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 +921 -325
- 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.1";
|
|
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,14 +2704,37 @@ 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);
|
|
2732
|
+
const limit = opts.limit ?? (opts.q ? "50" : void 0);
|
|
2733
|
+
const qs = `?detail=1${opts.q ? `&q=${encodeURIComponent(opts.q)}` : ""}${limit ? `&limit=${encodeURIComponent(limit)}` : ""}`;
|
|
2594
2734
|
const out = await admin({
|
|
2595
2735
|
method: "GET",
|
|
2596
|
-
path: `/teams/${encodeURIComponent(teamId)}/tenants
|
|
2597
|
-
summary: `List tenants for team ${teamName ?? teamId}`
|
|
2736
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants${qs}`,
|
|
2737
|
+
summary: opts.q ? `Search tenants matching "${opts.q}"` : `List tenants for team ${teamName ?? teamId}`
|
|
2598
2738
|
});
|
|
2599
2739
|
const tenants = out?.tenants ?? [];
|
|
2600
2740
|
if (opts.json) {
|
|
@@ -2602,22 +2742,26 @@ async function runTenantList(opts) {
|
|
|
2602
2742
|
return;
|
|
2603
2743
|
}
|
|
2604
2744
|
if (!tenants.length) {
|
|
2605
|
-
console.log(
|
|
2745
|
+
console.log(import_chalk23.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
|
|
2606
2746
|
return;
|
|
2607
2747
|
}
|
|
2608
2748
|
for (const t of tenants) {
|
|
2609
2749
|
const name = typeof t === "string" ? t : t.tenant_name;
|
|
2610
|
-
const display = typeof t === "string" ? "" :
|
|
2611
|
-
console.log(` ${
|
|
2750
|
+
const display = typeof t === "string" ? "" : import_chalk23.default.dim(` ${t.display_name ?? ""}`);
|
|
2751
|
+
console.log(` ${import_chalk23.default.bold(name)}${display}`);
|
|
2752
|
+
}
|
|
2753
|
+
const total = out?.total ?? tenants.length;
|
|
2754
|
+
if (total > tenants.length) {
|
|
2755
|
+
console.log(import_chalk23.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
|
|
2612
2756
|
}
|
|
2613
2757
|
}
|
|
2614
2758
|
async function runTenantCreate(opts) {
|
|
2615
2759
|
if (!opts.name) {
|
|
2616
|
-
console.error(
|
|
2760
|
+
console.error(import_chalk23.default.red("--name (display name) is required."));
|
|
2617
2761
|
process.exit(1);
|
|
2618
2762
|
}
|
|
2619
2763
|
const { teamId } = await resolveTeam(opts.team);
|
|
2620
|
-
const spinner = (0,
|
|
2764
|
+
const spinner = (0, import_ora9.default)("Creating tenant...").start();
|
|
2621
2765
|
try {
|
|
2622
2766
|
const out = await admin({
|
|
2623
2767
|
method: "POST",
|
|
@@ -2625,7 +2769,7 @@ async function runTenantCreate(opts) {
|
|
|
2625
2769
|
body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
|
|
2626
2770
|
summary: `Create tenant "${opts.name}"`
|
|
2627
2771
|
});
|
|
2628
|
-
spinner.succeed(`Created tenant ${
|
|
2772
|
+
spinner.succeed(`Created tenant ${import_chalk23.default.bold(out?.tenant_name ?? opts.name)}.`);
|
|
2629
2773
|
if (opts.json) console.log(JSON.stringify(out));
|
|
2630
2774
|
} catch (err) {
|
|
2631
2775
|
spinner.fail("Tenant create failed.");
|
|
@@ -2634,12 +2778,12 @@ async function runTenantCreate(opts) {
|
|
|
2634
2778
|
}
|
|
2635
2779
|
async function runTenantAttach(project, opts) {
|
|
2636
2780
|
if (!opts.tenant) {
|
|
2637
|
-
console.error(
|
|
2781
|
+
console.error(import_chalk23.default.red("--tenant <slug> is required."));
|
|
2638
2782
|
process.exit(1);
|
|
2639
2783
|
}
|
|
2640
2784
|
const { teamId } = await resolveTeam(opts.team);
|
|
2641
2785
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2642
|
-
const spinner = (0,
|
|
2786
|
+
const spinner = (0, import_ora9.default)("Attaching tenant...").start();
|
|
2643
2787
|
try {
|
|
2644
2788
|
const out = await admin({
|
|
2645
2789
|
method: "POST",
|
|
@@ -2662,11 +2806,11 @@ async function runTenantDelete(slug, opts) {
|
|
|
2662
2806
|
{ type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
|
|
2663
2807
|
]);
|
|
2664
2808
|
if (!confirm) {
|
|
2665
|
-
console.log(
|
|
2809
|
+
console.log(import_chalk23.default.dim("Aborted."));
|
|
2666
2810
|
return;
|
|
2667
2811
|
}
|
|
2668
2812
|
}
|
|
2669
|
-
const spinner = (0,
|
|
2813
|
+
const spinner = (0, import_ora9.default)("Deleting tenant...").start();
|
|
2670
2814
|
try {
|
|
2671
2815
|
await admin({
|
|
2672
2816
|
method: "DELETE",
|
|
@@ -2681,13 +2825,13 @@ async function runTenantDelete(slug, opts) {
|
|
|
2681
2825
|
}
|
|
2682
2826
|
async function runTenantCors(opts) {
|
|
2683
2827
|
if (!opts.tenant) {
|
|
2684
|
-
console.error(
|
|
2828
|
+
console.error(import_chalk23.default.red("--tenant <slug> is required."));
|
|
2685
2829
|
process.exit(1);
|
|
2686
2830
|
}
|
|
2687
2831
|
const { teamId } = await resolveTeam(opts.team);
|
|
2688
2832
|
const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
2689
2833
|
const cors = origins.length ? { allowed_origins: origins } : null;
|
|
2690
|
-
const spinner = (0,
|
|
2834
|
+
const spinner = (0, import_ora9.default)("Updating CORS...").start();
|
|
2691
2835
|
try {
|
|
2692
2836
|
await admin({
|
|
2693
2837
|
method: "PUT",
|
|
@@ -2702,10 +2846,458 @@ async function runTenantCors(opts) {
|
|
|
2702
2846
|
}
|
|
2703
2847
|
}
|
|
2704
2848
|
|
|
2849
|
+
// src/commands/tenant-drill.ts
|
|
2850
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
2851
|
+
var import_ora10 = __toESM(require("ora"));
|
|
2852
|
+
var import_crypto = require("crypto");
|
|
2853
|
+
init_admin();
|
|
2854
|
+
init_auth();
|
|
2855
|
+
init_tenant_pick();
|
|
2856
|
+
init_api();
|
|
2857
|
+
var trailingComma = /\s*,\s*/;
|
|
2858
|
+
var parseList = (s) => s.split(trailingComma).map((x) => x.trim()).filter(Boolean);
|
|
2859
|
+
async function runTenantManage(query, opts) {
|
|
2860
|
+
const { teamId } = await resolveTeam(opts.team);
|
|
2861
|
+
const slug = opts.tenant ?? loadCredentialsTenant(query) ?? await pickTenant(teamId, { message: "Manage which tenant?", initialQuery: query, allowCreate: true });
|
|
2862
|
+
if (!slug) return;
|
|
2863
|
+
await tenantHome(teamId, slug);
|
|
2864
|
+
}
|
|
2865
|
+
function loadCredentialsTenant(query) {
|
|
2866
|
+
if (query) return void 0;
|
|
2867
|
+
return loadCredentials()?.activeTenant ?? void 0;
|
|
2868
|
+
}
|
|
2869
|
+
async function tenantHome(teamId, tenant2) {
|
|
2870
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
2871
|
+
const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
2872
|
+
console.log(import_chalk24.default.bold(`
|
|
2873
|
+
Tenant ${tenant2}`));
|
|
2874
|
+
console.log(import_chalk24.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
2875
|
+
for (; ; ) {
|
|
2876
|
+
const spinner = (0, import_ora10.default)("Reading tenant state...").start();
|
|
2877
|
+
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
2878
|
+
admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
|
|
2879
|
+
admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
|
|
2880
|
+
admin({ method: "GET", path: `${base}/admin-emails`, summary: "List consumer-admin emails" }).catch(() => null),
|
|
2881
|
+
admin({ method: "GET", path: `${base}/external-issuers`, summary: "List external issuers" }).catch(() => null),
|
|
2882
|
+
admin({ method: "GET", path: `${base}/opaque`, summary: "Read opaque validator" }).catch(() => null),
|
|
2883
|
+
admin({ method: "GET", path: `${base}/app-clients`, summary: "List app clients" }).catch(() => [])
|
|
2884
|
+
]).finally(() => spinner.stop());
|
|
2885
|
+
const nEmails = (emails?.admin_emails ?? []).length;
|
|
2886
|
+
const nIssuers = (issuers?.external_issuers ?? []).length;
|
|
2887
|
+
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
2888
|
+
const onOff = (b) => b ? import_chalk24.default.green("on") : import_chalk24.default.dim("off");
|
|
2889
|
+
const { pick: pick2 } = await inquirer2.prompt([{
|
|
2890
|
+
type: "list",
|
|
2891
|
+
name: "pick",
|
|
2892
|
+
message: `Tenant ${tenant2}:`,
|
|
2893
|
+
pageSize: 12,
|
|
2894
|
+
choices: [
|
|
2895
|
+
{ name: `App clients (${nClients}) ${import_chalk24.default.dim("OAuth clients your consumers log in through \u2014 providers live inside")}`, value: "clients" },
|
|
2896
|
+
{ name: `IAM enforcement: ${onOff(iam?.iam_enabled)} ${import_chalk24.default.dim("key/identity enforcement for this tenant")}`, value: "iam" },
|
|
2897
|
+
{ name: `CORS override: ${cors?.cors ? import_chalk24.default.cyan(JSON.stringify(cors.cors)) : import_chalk24.default.dim("(unset)")}`, value: "cors" },
|
|
2898
|
+
{ name: `Consumer-admin emails (${nEmails}) ${import_chalk24.default.dim("who may administer the tenant portal")}`, value: "emails" },
|
|
2899
|
+
{ name: `External JWT issuers (${nIssuers}) ${import_chalk24.default.dim("bring-your-own auth: trust tokens you already mint")}`, value: "issuers" },
|
|
2900
|
+
{ name: `Opaque-token validator: ${opaque?.opaque?.endpoint ? import_chalk24.default.cyan(opaque.opaque.endpoint) : import_chalk24.default.dim("(unset)")}`, value: "opaque" },
|
|
2901
|
+
{ name: "\u2190 Back", value: "back" }
|
|
2902
|
+
]
|
|
2903
|
+
}]);
|
|
2904
|
+
switch (pick2) {
|
|
2905
|
+
case "back":
|
|
2906
|
+
return;
|
|
2907
|
+
case "clients":
|
|
2908
|
+
await clientsMenu(teamId, tenant2, base);
|
|
2909
|
+
break;
|
|
2910
|
+
case "iam": {
|
|
2911
|
+
const { v } = await inquirer2.prompt([{ type: "confirm", name: "v", message: "Enable IAM enforcement?", default: !!iam?.iam_enabled }]);
|
|
2912
|
+
await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
2913
|
+
console.log(import_chalk24.default.green(` IAM enforcement ${v ? "enabled" : "disabled"}.`));
|
|
2914
|
+
break;
|
|
2915
|
+
}
|
|
2916
|
+
case "cors": {
|
|
2917
|
+
const { v } = await inquirer2.prompt([{
|
|
2918
|
+
type: "input",
|
|
2919
|
+
name: "v",
|
|
2920
|
+
message: 'CORS JSON (e.g. {"allow_all_origins":true} \u2014 "null" clears):',
|
|
2921
|
+
default: cors?.cors ? JSON.stringify(cors.cors) : ""
|
|
2922
|
+
}]);
|
|
2923
|
+
if (v === "") break;
|
|
2924
|
+
const parsed = v === "null" ? null : safeJson(v);
|
|
2925
|
+
if (parsed === void 0) {
|
|
2926
|
+
console.log(import_chalk24.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
2927
|
+
break;
|
|
2928
|
+
}
|
|
2929
|
+
await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
2930
|
+
console.log(import_chalk24.default.green(" CORS updated."));
|
|
2931
|
+
break;
|
|
2932
|
+
}
|
|
2933
|
+
case "emails":
|
|
2934
|
+
await emailsMenu(base, emails?.admin_emails ?? []);
|
|
2935
|
+
break;
|
|
2936
|
+
case "issuers":
|
|
2937
|
+
await issuersMenu(base, issuers?.external_issuers ?? []);
|
|
2938
|
+
break;
|
|
2939
|
+
case "opaque": {
|
|
2940
|
+
const cur = opaque?.opaque;
|
|
2941
|
+
const { mode } = await inquirer2.prompt([{
|
|
2942
|
+
type: "list",
|
|
2943
|
+
name: "mode",
|
|
2944
|
+
message: "Opaque-token validator:",
|
|
2945
|
+
choices: [
|
|
2946
|
+
{ name: cur ? "Replace it" : "Set one up", value: "set" },
|
|
2947
|
+
...cur ? [{ name: "Clear it", value: "clear" }] : [],
|
|
2948
|
+
{ name: "\u2190 Back", value: "back" }
|
|
2949
|
+
]
|
|
2950
|
+
}]);
|
|
2951
|
+
if (mode === "back") break;
|
|
2952
|
+
if (mode === "clear") {
|
|
2953
|
+
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
2954
|
+
console.log(import_chalk24.default.green(" Cleared."));
|
|
2955
|
+
break;
|
|
2956
|
+
}
|
|
2957
|
+
const a = await inquirer2.prompt([
|
|
2958
|
+
{ type: "input", name: "endpoint", message: "Introspection endpoint (https):", default: cur?.endpoint, validate: (s) => s.startsWith("https://") || "must be https" },
|
|
2959
|
+
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
2960
|
+
]);
|
|
2961
|
+
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
2962
|
+
console.log(import_chalk24.default.green(" Opaque validator set."));
|
|
2963
|
+
break;
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
function safeJson(s) {
|
|
2969
|
+
try {
|
|
2970
|
+
return JSON.parse(s);
|
|
2971
|
+
} catch {
|
|
2972
|
+
return void 0;
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
async function emailsMenu(base, emails) {
|
|
2976
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
2977
|
+
console.log();
|
|
2978
|
+
if (!emails.length) console.log(import_chalk24.default.dim(" No consumer-admin emails."));
|
|
2979
|
+
for (const e of emails) console.log(` ${import_chalk24.default.bold(e.email ?? e)} ${import_chalk24.default.dim(e.status ?? "")}`);
|
|
2980
|
+
const { act } = await inquirer2.prompt([{
|
|
2981
|
+
type: "list",
|
|
2982
|
+
name: "act",
|
|
2983
|
+
message: "Consumer-admin emails:",
|
|
2984
|
+
choices: [
|
|
2985
|
+
{ name: "Add an email", value: "add" },
|
|
2986
|
+
...emails.length ? [{ name: "Remove an email", value: "rm" }] : [],
|
|
2987
|
+
{ name: "\u2190 Back", value: "back" }
|
|
2988
|
+
]
|
|
2989
|
+
}]);
|
|
2990
|
+
if (act === "back") return;
|
|
2991
|
+
if (act === "add") {
|
|
2992
|
+
const { email } = await inquirer2.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
2993
|
+
await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
2994
|
+
console.log(import_chalk24.default.green(` ${email} added.`));
|
|
2995
|
+
} else {
|
|
2996
|
+
const { e } = await inquirer2.prompt([{
|
|
2997
|
+
type: "list",
|
|
2998
|
+
name: "e",
|
|
2999
|
+
message: "Remove which?",
|
|
3000
|
+
choices: [...emails.map((x) => ({ name: x.email ?? String(x), value: x.email ?? String(x) })), { name: "\u2190 Back", value: null }]
|
|
3001
|
+
}]);
|
|
3002
|
+
if (!e) return;
|
|
3003
|
+
await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
3004
|
+
console.log(import_chalk24.default.green(` ${e} removed.`));
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
async function issuersMenu(base, issuers) {
|
|
3008
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3009
|
+
console.log();
|
|
3010
|
+
if (!issuers.length) console.log(import_chalk24.default.dim(" No external issuers \u2014 consumers use APIblaze-issued tokens."));
|
|
3011
|
+
for (const i of issuers) console.log(` ${import_chalk24.default.bold(i.iss)} aud=${i.aud} ${import_chalk24.default.dim(i.sub_semantics ?? "")}`);
|
|
3012
|
+
const { act } = await inquirer2.prompt([{
|
|
3013
|
+
type: "list",
|
|
3014
|
+
name: "act",
|
|
3015
|
+
message: "External JWT issuers:",
|
|
3016
|
+
choices: [
|
|
3017
|
+
{ name: "Add / replace one issuer", value: "add" },
|
|
3018
|
+
...issuers.length ? [{ name: "Delete an issuer", value: "rm" }] : [],
|
|
3019
|
+
{ name: "\u2190 Back", value: "back" }
|
|
3020
|
+
]
|
|
3021
|
+
}]);
|
|
3022
|
+
if (act === "back") return;
|
|
3023
|
+
if (act === "add") {
|
|
3024
|
+
const a = await inquirer2.prompt([
|
|
3025
|
+
{ type: "input", name: "iss", message: "Issuer URL (iss):", validate: (s) => !!s.trim() || "required" },
|
|
3026
|
+
{ type: "input", name: "aud", message: "Audience (aud):", validate: (s) => !!s.trim() || "required" },
|
|
3027
|
+
{ type: "input", name: "jwks", message: "JWKS URL (empty = derive from issuer):" },
|
|
3028
|
+
{ type: "list", name: "sem", message: "Where is the end-user id?", choices: [
|
|
3029
|
+
{ name: "The token sub IS the end user (tenant-owned)", value: "tenant_owned" },
|
|
3030
|
+
{ name: "Extract it from a claim\u2026", value: "extract_from_claim" }
|
|
3031
|
+
] }
|
|
3032
|
+
]);
|
|
3033
|
+
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;
|
|
3034
|
+
await admin({
|
|
3035
|
+
method: "POST",
|
|
3036
|
+
path: `${base}/external-issuers`,
|
|
3037
|
+
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3038
|
+
summary: `Add external issuer ${a.iss.trim()}`
|
|
3039
|
+
});
|
|
3040
|
+
console.log(import_chalk24.default.green(" Issuer saved."));
|
|
3041
|
+
} else {
|
|
3042
|
+
const { i } = await inquirer2.prompt([{
|
|
3043
|
+
type: "list",
|
|
3044
|
+
name: "i",
|
|
3045
|
+
message: "Delete which issuer?",
|
|
3046
|
+
choices: [...issuers.map((x) => ({ name: `${x.iss} (aud=${x.aud})`, value: x })), { name: "\u2190 Back", value: null }]
|
|
3047
|
+
}]);
|
|
3048
|
+
if (!i) return;
|
|
3049
|
+
await admin({
|
|
3050
|
+
method: "DELETE",
|
|
3051
|
+
path: `${base}/external-issuers?iss=${encodeURIComponent(i.iss)}&aud=${encodeURIComponent(i.aud)}`,
|
|
3052
|
+
summary: `Delete issuer ${i.iss}`
|
|
3053
|
+
});
|
|
3054
|
+
console.log(import_chalk24.default.green(" Issuer deleted."));
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
async function clientsMenu(teamId, tenant2, base) {
|
|
3058
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3059
|
+
for (; ; ) {
|
|
3060
|
+
const spinner = (0, import_ora10.default)("Loading app clients...").start();
|
|
3061
|
+
const raw = await admin({ method: "GET", path: `${base}/app-clients`, summary: "List app clients" }).catch(() => []);
|
|
3062
|
+
spinner.stop();
|
|
3063
|
+
const clients = Array.isArray(raw) ? raw : [];
|
|
3064
|
+
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3065
|
+
type: "list",
|
|
3066
|
+
name: "pick",
|
|
3067
|
+
message: `App clients of ${tenant2}:`,
|
|
3068
|
+
pageSize: 15,
|
|
3069
|
+
choices: [
|
|
3070
|
+
...clients.map((c) => ({
|
|
3071
|
+
name: `${import_chalk24.default.bold(c.name ?? c.clientId)} ${import_chalk24.default.dim(`${c.clientId}${c.projectName ? ` \xB7 ${c.projectName}` : ""}`)}`,
|
|
3072
|
+
value: c
|
|
3073
|
+
})),
|
|
3074
|
+
...clients.length ? [] : [new inquirer2.Separator(import_chalk24.default.dim(" no app clients yet"))],
|
|
3075
|
+
{ name: "\uFF0B Create an app client\u2026", value: " create" },
|
|
3076
|
+
{ name: "\u2190 Back", value: " back" }
|
|
3077
|
+
]
|
|
3078
|
+
}]);
|
|
3079
|
+
if (pick2 === " back") return;
|
|
3080
|
+
if (pick2 === " create") {
|
|
3081
|
+
const projects = await getProjects(teamId).catch(() => []);
|
|
3082
|
+
if (!projects.length) {
|
|
3083
|
+
console.log(import_chalk24.default.yellow(" No projects in this team \u2014 create a proxy first."));
|
|
3084
|
+
continue;
|
|
3085
|
+
}
|
|
3086
|
+
const a = await inquirer2.prompt([
|
|
3087
|
+
{ type: "input", name: "name", message: "Client name:", validate: (s) => !!s.trim() || "required" },
|
|
3088
|
+
{ type: "list", name: "proj", message: "For which project?", choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk24.default.dim("v" + p.apiVersion)}`, value: p })) },
|
|
3089
|
+
{ type: "input", name: "callbacks", message: "Callback URLs (comma-separated, empty = none):" }
|
|
3090
|
+
]);
|
|
3091
|
+
const created = await admin({
|
|
3092
|
+
method: "POST",
|
|
3093
|
+
path: `${base}/app-clients`,
|
|
3094
|
+
body: {
|
|
3095
|
+
name: a.name.trim(),
|
|
3096
|
+
projectName: a.proj.projectName,
|
|
3097
|
+
apiVersion: a.proj.apiVersion,
|
|
3098
|
+
...a.callbacks.trim() ? { authorizedCallbackUrls: parseList(a.callbacks) } : {}
|
|
3099
|
+
},
|
|
3100
|
+
summary: `Create app client "${a.name.trim()}"`
|
|
3101
|
+
});
|
|
3102
|
+
console.log(import_chalk24.default.green(` App client created${created?.clientId ? ` (${created.clientId})` : ""}.`));
|
|
3103
|
+
continue;
|
|
3104
|
+
}
|
|
3105
|
+
await clientHome(base, pick2);
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
async function clientHome(base, summary) {
|
|
3109
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3110
|
+
const id = summary.clientId ?? summary.client_id;
|
|
3111
|
+
const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
|
|
3112
|
+
for (; ; ) {
|
|
3113
|
+
const spinner = (0, import_ora10.default)("Reading app client...").start();
|
|
3114
|
+
const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
|
|
3115
|
+
spinner.stop();
|
|
3116
|
+
const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
|
|
3117
|
+
const scopes = c.scopes ?? [];
|
|
3118
|
+
const nProviders = (c.providers ?? []).length;
|
|
3119
|
+
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3120
|
+
type: "list",
|
|
3121
|
+
name: "pick",
|
|
3122
|
+
message: `${c.name ?? id}:`,
|
|
3123
|
+
pageSize: 12,
|
|
3124
|
+
choices: [
|
|
3125
|
+
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk24.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
|
|
3126
|
+
{ name: `Callback URLs: ${cb.length ? import_chalk24.default.cyan(cb.join(", ")) : import_chalk24.default.dim("(none)")}`, value: "callbacks" },
|
|
3127
|
+
{ name: `Scopes: ${scopes.length ? import_chalk24.default.cyan(scopes.join(" ")) : import_chalk24.default.dim("(defaults)")}`, value: "scopes" },
|
|
3128
|
+
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
3129
|
+
{ name: "Reveal client secret", value: "secret" },
|
|
3130
|
+
{ name: "Rotate client secret", value: "rotate" },
|
|
3131
|
+
{ name: import_chalk24.default.red("Delete this app client"), value: "delete" },
|
|
3132
|
+
{ name: "\u2190 Back", value: "back" }
|
|
3133
|
+
]
|
|
3134
|
+
}]);
|
|
3135
|
+
switch (pick2) {
|
|
3136
|
+
case "back":
|
|
3137
|
+
return;
|
|
3138
|
+
case "providers":
|
|
3139
|
+
await providersMenu(cBase, c.name ?? id);
|
|
3140
|
+
break;
|
|
3141
|
+
case "callbacks": {
|
|
3142
|
+
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
3143
|
+
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
3144
|
+
console.log(import_chalk24.default.green(" Callbacks updated."));
|
|
3145
|
+
break;
|
|
3146
|
+
}
|
|
3147
|
+
case "scopes": {
|
|
3148
|
+
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
3149
|
+
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
3150
|
+
console.log(import_chalk24.default.green(" Scopes updated."));
|
|
3151
|
+
break;
|
|
3152
|
+
}
|
|
3153
|
+
case "expiries": {
|
|
3154
|
+
const a = await inquirer2.prompt([
|
|
3155
|
+
{ type: "input", name: "access", message: "Access token expiry (seconds):", default: String(c.accessTokenExpiry ?? 3600) },
|
|
3156
|
+
{ type: "input", name: "id", message: "ID token expiry (seconds):", default: String(c.idTokenExpiry ?? 3600) },
|
|
3157
|
+
{ type: "input", name: "refresh", message: "Refresh token expiry (seconds):", default: String(c.refreshTokenExpiry ?? 2592e3) }
|
|
3158
|
+
]);
|
|
3159
|
+
await admin({
|
|
3160
|
+
method: "PATCH",
|
|
3161
|
+
path: cBase,
|
|
3162
|
+
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
3163
|
+
summary: "Update token expiries"
|
|
3164
|
+
});
|
|
3165
|
+
console.log(import_chalk24.default.green(" Expiries updated."));
|
|
3166
|
+
break;
|
|
3167
|
+
}
|
|
3168
|
+
case "secret": {
|
|
3169
|
+
const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
3170
|
+
if (!sure) break;
|
|
3171
|
+
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
3172
|
+
console.log(` ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3173
|
+
break;
|
|
3174
|
+
}
|
|
3175
|
+
case "rotate": {
|
|
3176
|
+
const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: "Rotate the secret? Existing integrations using it will break.", default: false }]);
|
|
3177
|
+
if (!sure) break;
|
|
3178
|
+
const fresh = randomSecret();
|
|
3179
|
+
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
3180
|
+
console.log(` New ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(fresh)} ${import_chalk24.default.dim("(store it now)")}`);
|
|
3181
|
+
break;
|
|
3182
|
+
}
|
|
3183
|
+
case "delete": {
|
|
3184
|
+
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 }]);
|
|
3185
|
+
if (!sure) break;
|
|
3186
|
+
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
3187
|
+
console.log(import_chalk24.default.green(" App client deleted."));
|
|
3188
|
+
return;
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
function randomSecret() {
|
|
3194
|
+
const bytes = new Uint8Array(24);
|
|
3195
|
+
(0, import_crypto.randomFillSync)(bytes);
|
|
3196
|
+
return Buffer.from(bytes).toString("base64url");
|
|
3197
|
+
}
|
|
3198
|
+
var PROVIDER_TYPES = ["google", "github", "microsoft", "facebook", "auth0", "other"];
|
|
3199
|
+
var DEFAULT_SCOPES = {
|
|
3200
|
+
google: "openid email profile",
|
|
3201
|
+
microsoft: "openid email profile",
|
|
3202
|
+
github: "read:user user:email",
|
|
3203
|
+
facebook: "public_profile email"
|
|
3204
|
+
};
|
|
3205
|
+
async function providersMenu(cBase, clientLabel) {
|
|
3206
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3207
|
+
for (; ; ) {
|
|
3208
|
+
const spinner = (0, import_ora10.default)("Loading providers...").start();
|
|
3209
|
+
const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
|
|
3210
|
+
spinner.stop();
|
|
3211
|
+
const providers = Array.isArray(raw) ? raw : [];
|
|
3212
|
+
console.log();
|
|
3213
|
+
for (const p of providers) {
|
|
3214
|
+
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" : ""}`)}`);
|
|
3215
|
+
}
|
|
3216
|
+
if (!providers.length) console.log(import_chalk24.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
|
|
3217
|
+
const { act } = await inquirer2.prompt([{
|
|
3218
|
+
type: "list",
|
|
3219
|
+
name: "act",
|
|
3220
|
+
message: `Login providers of ${clientLabel}:`,
|
|
3221
|
+
choices: [
|
|
3222
|
+
{ name: "\uFF0B Add a provider", value: "add" },
|
|
3223
|
+
...providers.length ? [
|
|
3224
|
+
{ name: "Reveal a provider secret", value: "secret" },
|
|
3225
|
+
{ name: "Remove a provider", value: "rm" }
|
|
3226
|
+
] : [],
|
|
3227
|
+
{ name: "\u2190 Back", value: "back" }
|
|
3228
|
+
]
|
|
3229
|
+
}]);
|
|
3230
|
+
if (act === "back") return;
|
|
3231
|
+
if (act === "add") {
|
|
3232
|
+
const { type } = await inquirer2.prompt([{ type: "list", name: "type", message: "Provider:", choices: PROVIDER_TYPES }]);
|
|
3233
|
+
let body;
|
|
3234
|
+
if (type === "github") {
|
|
3235
|
+
const { managed } = await inquirer2.prompt([{
|
|
3236
|
+
type: "confirm",
|
|
3237
|
+
name: "managed",
|
|
3238
|
+
default: true,
|
|
3239
|
+
message: "Use the APIblaze-managed GitHub app (no credentials needed)?"
|
|
3240
|
+
}]);
|
|
3241
|
+
if (managed) body = { type, managed: true };
|
|
3242
|
+
}
|
|
3243
|
+
if (!body) {
|
|
3244
|
+
const a = await inquirer2.prompt([
|
|
3245
|
+
{ type: "input", name: "clientId", message: `${type} OAuth client id:`, validate: (s) => !!s.trim() || "required" },
|
|
3246
|
+
{ type: "password", name: "clientSecret", mask: "*", message: `${type} OAuth client secret:`, validate: (s) => s.length >= 6 && s.length <= 200 || "6\u2013200 chars" },
|
|
3247
|
+
...type === "auth0" || type === "other" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : [],
|
|
3248
|
+
{ type: "input", name: "scopes", message: "Scopes:", default: DEFAULT_SCOPES[type] ?? "" }
|
|
3249
|
+
]);
|
|
3250
|
+
body = {
|
|
3251
|
+
type,
|
|
3252
|
+
clientId: a.clientId.trim(),
|
|
3253
|
+
clientSecret: a.clientSecret,
|
|
3254
|
+
...a.domain?.trim() ? { domain: a.domain.trim() } : {},
|
|
3255
|
+
scopes: String(a.scopes).split(/[\s,]+/).filter(Boolean)
|
|
3256
|
+
};
|
|
3257
|
+
}
|
|
3258
|
+
const { routing } = await inquirer2.prompt([{
|
|
3259
|
+
type: "list",
|
|
3260
|
+
name: "routing",
|
|
3261
|
+
message: "What does your upstream receive?",
|
|
3262
|
+
choices: [
|
|
3263
|
+
{ name: "APIblaze token (recommended \u2014 provider stays an identity source)", value: null },
|
|
3264
|
+
{ name: `The ${type} access token`, value: "third_party_access_token" },
|
|
3265
|
+
{ name: `The ${type} id token`, value: "third_party_id_token" },
|
|
3266
|
+
{ name: "Nothing (strip auth)", value: "none" }
|
|
3267
|
+
]
|
|
3268
|
+
}]);
|
|
3269
|
+
if (routing) {
|
|
3270
|
+
body.tokenType = "thirdParty";
|
|
3271
|
+
body.targetServerToken = routing;
|
|
3272
|
+
}
|
|
3273
|
+
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
3274
|
+
console.log(import_chalk24.default.green(` ${type} provider added.`));
|
|
3275
|
+
} else {
|
|
3276
|
+
const { p } = await inquirer2.prompt([{
|
|
3277
|
+
type: "list",
|
|
3278
|
+
name: "p",
|
|
3279
|
+
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
3280
|
+
choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk24.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
|
|
3281
|
+
}]);
|
|
3282
|
+
if (!p) continue;
|
|
3283
|
+
if (act === "rm") {
|
|
3284
|
+
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 }]);
|
|
3285
|
+
if (!sure) continue;
|
|
3286
|
+
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
3287
|
+
console.log(import_chalk24.default.green(` ${p.type} removed.`));
|
|
3288
|
+
} else {
|
|
3289
|
+
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
3290
|
+
console.log(` ${import_chalk24.default.bold("client_secret")}: ${import_chalk24.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
|
|
2705
3296
|
// src/commands/spec.ts
|
|
2706
3297
|
var fs6 = __toESM(require("fs"));
|
|
2707
|
-
var
|
|
2708
|
-
var
|
|
3298
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
3299
|
+
var import_ora11 = __toESM(require("ora"));
|
|
3300
|
+
init_admin();
|
|
2709
3301
|
async function runSpecGet(project, opts) {
|
|
2710
3302
|
const { teamId } = await resolveTeam(opts.team);
|
|
2711
3303
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
@@ -2718,19 +3310,19 @@ async function runSpecGet(project, opts) {
|
|
|
2718
3310
|
}
|
|
2719
3311
|
async function runSpecSet(project, opts) {
|
|
2720
3312
|
if (!opts.file) {
|
|
2721
|
-
console.error(
|
|
3313
|
+
console.error(import_chalk25.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
2722
3314
|
process.exit(1);
|
|
2723
3315
|
}
|
|
2724
3316
|
let specContent;
|
|
2725
3317
|
try {
|
|
2726
3318
|
specContent = fs6.readFileSync(opts.file, "utf-8");
|
|
2727
3319
|
} catch {
|
|
2728
|
-
console.error(
|
|
3320
|
+
console.error(import_chalk25.default.red(`Cannot read file: ${opts.file}`));
|
|
2729
3321
|
process.exit(1);
|
|
2730
3322
|
}
|
|
2731
3323
|
const { teamId } = await resolveTeam(opts.team);
|
|
2732
3324
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2733
|
-
const spinner = (0,
|
|
3325
|
+
const spinner = (0, import_ora11.default)("Uploading spec...").start();
|
|
2734
3326
|
try {
|
|
2735
3327
|
const out = await admin({
|
|
2736
3328
|
method: "POST",
|
|
@@ -2747,12 +3339,13 @@ async function runSpecSet(project, opts) {
|
|
|
2747
3339
|
}
|
|
2748
3340
|
|
|
2749
3341
|
// src/commands/agent.ts
|
|
2750
|
-
var
|
|
2751
|
-
var
|
|
3342
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
3343
|
+
var import_ora12 = __toESM(require("ora"));
|
|
2752
3344
|
init_auth();
|
|
2753
3345
|
|
|
2754
3346
|
// src/lib/tools.ts
|
|
2755
|
-
var
|
|
3347
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
3348
|
+
init_admin();
|
|
2756
3349
|
init_api();
|
|
2757
3350
|
async function proj(teamId, name, version2) {
|
|
2758
3351
|
return resolveProject(teamId, name, version2);
|
|
@@ -2770,15 +3363,15 @@ var TOOLS = [
|
|
|
2770
3363
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
2771
3364
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
2772
3365
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
2773
|
-
const lines = [` ${
|
|
2774
|
-
if (res.devPortal) lines.push(` ${
|
|
3366
|
+
const lines = [` ${import_chalk26.default.dim("Proxy URL:")} ${import_chalk26.default.bold(url)}`];
|
|
3367
|
+
if (res.devPortal) lines.push(` ${import_chalk26.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
2775
3368
|
const envs = Object.keys(keys);
|
|
2776
3369
|
if (envs.length) {
|
|
2777
|
-
lines.push("", ` ${
|
|
3370
|
+
lines.push("", ` ${import_chalk26.default.bold("API keys")} ${import_chalk26.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
2778
3371
|
const w = Math.max(...envs.map((e) => e.length));
|
|
2779
|
-
for (const env of envs) lines.push(` ${
|
|
3372
|
+
for (const env of envs) lines.push(` ${import_chalk26.default.cyan(env.padEnd(w))} ${import_chalk26.default.green(keys[env])}`);
|
|
2780
3373
|
}
|
|
2781
|
-
if (tryIt) lines.push("", ` ${
|
|
3374
|
+
if (tryIt) lines.push("", ` ${import_chalk26.default.dim("Try it:")}`, ` ${import_chalk26.default.cyan(tryIt)}`);
|
|
2782
3375
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
2783
3376
|
}
|
|
2784
3377
|
},
|
|
@@ -2903,6 +3496,7 @@ function findTool(name) {
|
|
|
2903
3496
|
}
|
|
2904
3497
|
|
|
2905
3498
|
// src/commands/agent.ts
|
|
3499
|
+
init_trace();
|
|
2906
3500
|
init_types();
|
|
2907
3501
|
var DASHBOARD_BASE5 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
2908
3502
|
var MAX_TOOL_STEPS = 6;
|
|
@@ -2933,23 +3527,23 @@ function truncate(value, max = 1500) {
|
|
|
2933
3527
|
}
|
|
2934
3528
|
function printCost(llm) {
|
|
2935
3529
|
const usd = llm.cost > 0 ? `$${llm.cost.toFixed(4)}` : "<$0.0001";
|
|
2936
|
-
console.log(
|
|
3530
|
+
console.log(import_chalk27.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk27.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
|
|
2937
3531
|
}
|
|
2938
3532
|
async function runAgent(opts) {
|
|
2939
3533
|
requireAuth();
|
|
2940
3534
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
2941
3535
|
const { default: inquirer2 } = await import("inquirer");
|
|
2942
|
-
console.log(
|
|
2943
|
-
console.log(
|
|
3536
|
+
console.log(import_chalk27.default.bold("APIblaze agent") + import_chalk27.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
3537
|
+
console.log(import_chalk27.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
2944
3538
|
const history = [];
|
|
2945
3539
|
while (true) {
|
|
2946
|
-
const { input } = await inquirer2.prompt([{ type: "input", name: "input", message:
|
|
3540
|
+
const { input } = await inquirer2.prompt([{ type: "input", name: "input", message: import_chalk27.default.cyan("you") + " \u203A" }]);
|
|
2947
3541
|
const text = (input ?? "").trim();
|
|
2948
3542
|
if (!text) continue;
|
|
2949
3543
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
2950
3544
|
history.push({ role: "user", content: text });
|
|
2951
3545
|
for (let step = 0; step < MAX_TOOL_STEPS; step++) {
|
|
2952
|
-
const spinner = (0,
|
|
3546
|
+
const spinner = (0, import_ora12.default)({ text: "thinking...", color: "magenta" }).start();
|
|
2953
3547
|
let resp;
|
|
2954
3548
|
try {
|
|
2955
3549
|
resp = await callAgent(history, teamId);
|
|
@@ -2957,21 +3551,21 @@ async function runAgent(opts) {
|
|
|
2957
3551
|
} catch (err) {
|
|
2958
3552
|
spinner.stop();
|
|
2959
3553
|
if (err instanceof ApiError && err.status === 402) {
|
|
2960
|
-
console.log(
|
|
3554
|
+
console.log(import_chalk27.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
2961
3555
|
break;
|
|
2962
3556
|
}
|
|
2963
3557
|
throw err;
|
|
2964
3558
|
}
|
|
2965
3559
|
history.push({ role: "assistant", content: resp.raw });
|
|
2966
3560
|
printCost(resp.llm);
|
|
2967
|
-
if (resp.reply) console.log(
|
|
3561
|
+
if (resp.reply) console.log(import_chalk27.default.green("agent") + " \u203A " + resp.reply);
|
|
2968
3562
|
if (!resp.action) break;
|
|
2969
3563
|
const tool = findTool(resp.action.tool);
|
|
2970
3564
|
if (!tool) {
|
|
2971
3565
|
history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
|
|
2972
3566
|
continue;
|
|
2973
3567
|
}
|
|
2974
|
-
const runSpinner = (0,
|
|
3568
|
+
const runSpinner = (0, import_ora12.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
|
|
2975
3569
|
try {
|
|
2976
3570
|
const result = await tool.run(resp.action.args, { teamId });
|
|
2977
3571
|
runSpinner.succeed(`${tool.name} \u2713`);
|
|
@@ -2989,11 +3583,11 @@ async function runAgent(opts) {
|
|
|
2989
3583
|
}
|
|
2990
3584
|
renderTrace();
|
|
2991
3585
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
2992
|
-
console.log(
|
|
3586
|
+
console.log(import_chalk27.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
2993
3587
|
}
|
|
2994
3588
|
}
|
|
2995
3589
|
}
|
|
2996
|
-
console.log(
|
|
3590
|
+
console.log(import_chalk27.default.dim("\nBye."));
|
|
2997
3591
|
}
|
|
2998
3592
|
|
|
2999
3593
|
// src/commands/config-browse.ts
|
|
@@ -3140,7 +3734,7 @@ var SETTING_GROUPS = ["Basics", "Traffic & limits", "Access & auth", "Portal & M
|
|
|
3140
3734
|
var FEATURES = [
|
|
3141
3735
|
{ go: "transforms", label: "Transforms", desc: "Rewrite requests/responses (headers, body fields) without touching your upstream" },
|
|
3142
3736
|
{ 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: "
|
|
3737
|
+
{ go: "tenants", label: "Tenants", desc: "Consumer groups \u2014 portal, login app clients & providers, issuers, IAM, CORS" },
|
|
3144
3738
|
{ go: "domains", label: "Custom domains", desc: "Serve the proxy on your own hostname + choose what the bare URL serves" },
|
|
3145
3739
|
{ go: "spec", label: "OpenAPI spec & traffic", desc: "View the stored spec, refresh it from source, or build it from captured traffic" },
|
|
3146
3740
|
{ go: "agents", label: "AI agents", desc: "Chat to build your spec, design access rules, or publish an MCP server (billed per turn)" },
|
|
@@ -3156,11 +3750,11 @@ function dig(blob, dotted) {
|
|
|
3156
3750
|
}
|
|
3157
3751
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
3158
3752
|
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
|
|
3753
|
+
if (v === void 0) return import_chalk28.default.dim("(unset)");
|
|
3754
|
+
if (v === null) return import_chalk28.default.dim("null");
|
|
3755
|
+
if (typeof v === "object") return import_chalk28.default.cyan(JSON.stringify(v));
|
|
3756
|
+
if (typeof v === "boolean") return v ? import_chalk28.default.green("on") : import_chalk28.default.red("off");
|
|
3757
|
+
return import_chalk28.default.cyan(String(v));
|
|
3164
3758
|
}
|
|
3165
3759
|
function parseValue(raw) {
|
|
3166
3760
|
if (raw === "true") return true;
|
|
@@ -3187,7 +3781,7 @@ async function fetchConfigBlob(proj2) {
|
|
|
3187
3781
|
}
|
|
3188
3782
|
async function patchSetting(proj2, s, value, cfg) {
|
|
3189
3783
|
const body = s.toPatch(value, cfg);
|
|
3190
|
-
const spinner = (0,
|
|
3784
|
+
const spinner = (0, import_ora13.default)(`Set ${s.key}...`).start();
|
|
3191
3785
|
try {
|
|
3192
3786
|
await admin({
|
|
3193
3787
|
method: "PATCH",
|
|
@@ -3202,10 +3796,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
3202
3796
|
}
|
|
3203
3797
|
}
|
|
3204
3798
|
var loginFirst = (what) => {
|
|
3205
|
-
console.log(
|
|
3799
|
+
console.log(import_chalk28.default.yellow(`
|
|
3206
3800
|
Log in first to ${what}.`));
|
|
3207
|
-
console.log(
|
|
3208
|
-
console.log(
|
|
3801
|
+
console.log(import_chalk28.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
|
|
3802
|
+
console.log(import_chalk28.default.dim(" anonymously and want to bring it into your account.\n"));
|
|
3209
3803
|
};
|
|
3210
3804
|
async function runConfig(project, key, value, opts) {
|
|
3211
3805
|
const creds = loadCredentials();
|
|
@@ -3222,9 +3816,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
3222
3816
|
}
|
|
3223
3817
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
3224
3818
|
if (!setting) {
|
|
3225
|
-
console.error(
|
|
3226
|
-
console.error(
|
|
3227
|
-
console.error(
|
|
3819
|
+
console.error(import_chalk28.default.red(`Unknown setting "${key}".`));
|
|
3820
|
+
console.error(import_chalk28.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
|
|
3821
|
+
console.error(import_chalk28.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
|
|
3228
3822
|
process.exit(1);
|
|
3229
3823
|
}
|
|
3230
3824
|
if (value === void 0) {
|
|
@@ -3239,7 +3833,7 @@ async function pickProject(teamId) {
|
|
|
3239
3833
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
3240
3834
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
3241
3835
|
if (!projects.length) {
|
|
3242
|
-
console.error(
|
|
3836
|
+
console.error(import_chalk28.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
3243
3837
|
process.exit(1);
|
|
3244
3838
|
}
|
|
3245
3839
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -3247,7 +3841,7 @@ async function pickProject(teamId) {
|
|
|
3247
3841
|
type: "list",
|
|
3248
3842
|
name: "picked",
|
|
3249
3843
|
message: "Which project?",
|
|
3250
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
3844
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk28.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
3251
3845
|
}]);
|
|
3252
3846
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
3253
3847
|
}
|
|
@@ -3258,25 +3852,25 @@ function printAll(proj2, cfg, json) {
|
|
|
3258
3852
|
console.log(JSON.stringify(out, null, 2));
|
|
3259
3853
|
return;
|
|
3260
3854
|
}
|
|
3261
|
-
console.log(
|
|
3855
|
+
console.log(import_chalk28.default.bold(`
|
|
3262
3856
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
3263
3857
|
`));
|
|
3264
3858
|
for (const group of SETTING_GROUPS) {
|
|
3265
|
-
console.log(
|
|
3859
|
+
console.log(import_chalk28.default.bold(group));
|
|
3266
3860
|
for (const s of SETTINGS.filter((x) => x.group === group)) {
|
|
3267
|
-
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${
|
|
3861
|
+
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk28.default.dim(s.desc)}`);
|
|
3268
3862
|
}
|
|
3269
3863
|
console.log();
|
|
3270
3864
|
}
|
|
3271
|
-
console.log(
|
|
3865
|
+
console.log(import_chalk28.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
3272
3866
|
}
|
|
3273
3867
|
async function discoveryMenu(project) {
|
|
3274
3868
|
const { default: inquirer2 } = await import("inquirer");
|
|
3275
|
-
console.log(
|
|
3869
|
+
console.log(import_chalk28.default.bold(`
|
|
3276
3870
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
3277
3871
|
`));
|
|
3278
|
-
console.log(
|
|
3279
|
-
console.log(
|
|
3872
|
+
console.log(import_chalk28.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
|
|
3873
|
+
console.log(import_chalk28.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
|
|
3280
3874
|
for (; ; ) {
|
|
3281
3875
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3282
3876
|
type: "list",
|
|
@@ -3284,13 +3878,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
3284
3878
|
message: "Explore:",
|
|
3285
3879
|
pageSize: 20,
|
|
3286
3880
|
choices: [
|
|
3287
|
-
new inquirer2.Separator(
|
|
3881
|
+
new inquirer2.Separator(import_chalk28.default.bold("\u2014 Settings \u2014")),
|
|
3288
3882
|
...SETTING_GROUPS.map((g) => ({
|
|
3289
|
-
name: `${g} ${
|
|
3883
|
+
name: `${g} ${import_chalk28.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
3290
3884
|
value: { kind: "settings", g }
|
|
3291
3885
|
})),
|
|
3292
|
-
new inquirer2.Separator(
|
|
3293
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
3886
|
+
new inquirer2.Separator(import_chalk28.default.bold("\u2014 Features \u2014")),
|
|
3887
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk28.default.dim(f.desc)}`, value: { kind: "feature", f } })),
|
|
3294
3888
|
new inquirer2.Separator(),
|
|
3295
3889
|
{ name: "Exit", value: { kind: "exit" } }
|
|
3296
3890
|
]
|
|
@@ -3299,24 +3893,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
3299
3893
|
if (pick2.kind === "settings") {
|
|
3300
3894
|
console.log();
|
|
3301
3895
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
3302
|
-
console.log(` ${
|
|
3303
|
-
console.log(` ${
|
|
3896
|
+
console.log(` ${import_chalk28.default.bold(s.label.padEnd(28))} ${import_chalk28.default.dim(s.desc)}`);
|
|
3897
|
+
console.log(` ${import_chalk28.default.dim(" key: " + s.key)}`);
|
|
3304
3898
|
}
|
|
3305
3899
|
loginFirst("view or change these settings");
|
|
3306
3900
|
} else {
|
|
3307
3901
|
const f = pick2.f;
|
|
3308
3902
|
console.log(`
|
|
3309
|
-
${
|
|
3903
|
+
${import_chalk28.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
3310
3904
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
3311
3905
|
}
|
|
3312
3906
|
}
|
|
3313
3907
|
}
|
|
3314
3908
|
async function navigator(proj2, cfg, opts) {
|
|
3315
3909
|
const { default: inquirer2 } = await import("inquirer");
|
|
3316
|
-
console.log(
|
|
3910
|
+
console.log(import_chalk28.default.bold(`
|
|
3317
3911
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
3318
3912
|
`));
|
|
3319
|
-
console.log(
|
|
3913
|
+
console.log(import_chalk28.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
|
|
3320
3914
|
let blob = cfg;
|
|
3321
3915
|
for (; ; ) {
|
|
3322
3916
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
@@ -3325,10 +3919,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
3325
3919
|
message: "Where to?",
|
|
3326
3920
|
pageSize: 20,
|
|
3327
3921
|
choices: [
|
|
3328
|
-
new inquirer2.Separator(
|
|
3922
|
+
new inquirer2.Separator(import_chalk28.default.bold("\u2014 Settings \u2014")),
|
|
3329
3923
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
3330
|
-
new inquirer2.Separator(
|
|
3331
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
3924
|
+
new inquirer2.Separator(import_chalk28.default.bold("\u2014 Features \u2014")),
|
|
3925
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk28.default.dim(f.desc)}`, value: { kind: f.go } })),
|
|
3332
3926
|
new inquirer2.Separator(),
|
|
3333
3927
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
3334
3928
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -3368,7 +3962,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
3368
3962
|
}
|
|
3369
3963
|
}
|
|
3370
3964
|
} catch (err) {
|
|
3371
|
-
console.error(
|
|
3965
|
+
console.error(import_chalk28.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
3372
3966
|
}
|
|
3373
3967
|
}
|
|
3374
3968
|
}
|
|
@@ -3382,7 +3976,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
3382
3976
|
message: group + ":",
|
|
3383
3977
|
pageSize: 16,
|
|
3384
3978
|
choices: [
|
|
3385
|
-
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${
|
|
3979
|
+
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk28.default.dim(s2.desc)}`, value: s2 })),
|
|
3386
3980
|
new inquirer2.Separator(),
|
|
3387
3981
|
{ name: "\u2190 Back", value: null }
|
|
3388
3982
|
]
|
|
@@ -3400,7 +3994,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
3400
3994
|
} else if (s.type === "number") {
|
|
3401
3995
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
3402
3996
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
3403
|
-
console.log(
|
|
3997
|
+
console.log(import_chalk28.default.yellow(" Not a number \u2014 unchanged."));
|
|
3404
3998
|
continue;
|
|
3405
3999
|
}
|
|
3406
4000
|
value = Number(v);
|
|
@@ -3481,7 +4075,7 @@ async function buildCondition(phase) {
|
|
|
3481
4075
|
const items = [];
|
|
3482
4076
|
for (; ; ) {
|
|
3483
4077
|
const a = await inquirer2.prompt([
|
|
3484
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
4078
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk28.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
3485
4079
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
3486
4080
|
"eq",
|
|
3487
4081
|
"neq",
|
|
@@ -3512,7 +4106,7 @@ async function buildCondition(phase) {
|
|
|
3512
4106
|
function showCondition(cond) {
|
|
3513
4107
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
3514
4108
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
3515
|
-
return
|
|
4109
|
+
return import_chalk28.default.dim(` when ${s}`);
|
|
3516
4110
|
}
|
|
3517
4111
|
async function transformsMenu(proj2) {
|
|
3518
4112
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -3521,12 +4115,12 @@ async function transformsMenu(proj2) {
|
|
|
3521
4115
|
const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
|
|
3522
4116
|
const rules = out?.rules ?? [];
|
|
3523
4117
|
console.log();
|
|
3524
|
-
if (!rules.length) console.log(
|
|
4118
|
+
if (!rules.length) console.log(import_chalk28.default.dim(" No transform rules yet."));
|
|
3525
4119
|
for (const r of rules) {
|
|
3526
4120
|
const a = r.action ?? {};
|
|
3527
4121
|
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 ?
|
|
4122
|
+
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")}`) : ""}`;
|
|
4123
|
+
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
4124
|
}
|
|
3531
4125
|
const { act } = await inquirer2.prompt([{
|
|
3532
4126
|
type: "list",
|
|
@@ -3538,7 +4132,7 @@ async function transformsMenu(proj2) {
|
|
|
3538
4132
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
3539
4133
|
{ name: "Delete a rule", value: "delete" }
|
|
3540
4134
|
] : [],
|
|
3541
|
-
{ name:
|
|
4135
|
+
{ name: import_chalk28.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
3542
4136
|
{ name: "\u2190 Back", value: "back" }
|
|
3543
4137
|
]
|
|
3544
4138
|
}]);
|
|
@@ -3551,11 +4145,11 @@ async function transformsMenu(proj2) {
|
|
|
3551
4145
|
}]);
|
|
3552
4146
|
const body = parseValue(raw);
|
|
3553
4147
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
3554
|
-
console.log(
|
|
4148
|
+
console.log(import_chalk28.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
3555
4149
|
continue;
|
|
3556
4150
|
}
|
|
3557
4151
|
await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
|
|
3558
|
-
console.log(
|
|
4152
|
+
console.log(import_chalk28.default.green(" Rule created."));
|
|
3559
4153
|
continue;
|
|
3560
4154
|
}
|
|
3561
4155
|
if (act === "add") {
|
|
@@ -3571,7 +4165,7 @@ async function transformsMenu(proj2) {
|
|
|
3571
4165
|
{ name: "Remove a field", value: "remove" }
|
|
3572
4166
|
] }
|
|
3573
4167
|
]);
|
|
3574
|
-
const fieldHint =
|
|
4168
|
+
const fieldHint = import_chalk28.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
3575
4169
|
let action2;
|
|
3576
4170
|
if (ans.kind === "hardcode") {
|
|
3577
4171
|
const a = await inquirer2.prompt([
|
|
@@ -3602,7 +4196,7 @@ async function transformsMenu(proj2) {
|
|
|
3602
4196
|
};
|
|
3603
4197
|
}
|
|
3604
4198
|
const condition = await buildCondition(ans.phase);
|
|
3605
|
-
const spinner = (0,
|
|
4199
|
+
const spinner = (0, import_ora13.default)("Creating rule...").start();
|
|
3606
4200
|
try {
|
|
3607
4201
|
await admin({
|
|
3608
4202
|
method: "POST",
|
|
@@ -3620,16 +4214,16 @@ async function transformsMenu(proj2) {
|
|
|
3620
4214
|
type: "list",
|
|
3621
4215
|
name: "rule",
|
|
3622
4216
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
3623
|
-
choices: [...rules.map((r) => ({ name: `${r.name} ${
|
|
4217
|
+
choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk28.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
|
|
3624
4218
|
}]);
|
|
3625
4219
|
if (!rule) continue;
|
|
3626
4220
|
if (act === "toggle") {
|
|
3627
4221
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
3628
4222
|
await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
3629
|
-
console.log(
|
|
4223
|
+
console.log(import_chalk28.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
3630
4224
|
} else {
|
|
3631
4225
|
await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
3632
|
-
console.log(
|
|
4226
|
+
console.log(import_chalk28.default.green(` ${rule.name} deleted.`));
|
|
3633
4227
|
}
|
|
3634
4228
|
}
|
|
3635
4229
|
}
|
|
@@ -3641,9 +4235,9 @@ async function mappingsMenu(proj2) {
|
|
|
3641
4235
|
const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
|
|
3642
4236
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
3643
4237
|
console.log();
|
|
3644
|
-
if (!tables.length) console.log(
|
|
4238
|
+
if (!tables.length) console.log(import_chalk28.default.dim(" No mapping tables yet."));
|
|
3645
4239
|
for (const t of tables) {
|
|
3646
|
-
console.log(` ${
|
|
4240
|
+
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
4241
|
}
|
|
3648
4242
|
const { act } = await inquirer2.prompt([{
|
|
3649
4243
|
type: "list",
|
|
@@ -3663,11 +4257,11 @@ async function mappingsMenu(proj2) {
|
|
|
3663
4257
|
]);
|
|
3664
4258
|
const entries2 = parseValue(a.entries);
|
|
3665
4259
|
if (!Array.isArray(entries2)) {
|
|
3666
|
-
console.log(
|
|
4260
|
+
console.log(import_chalk28.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
3667
4261
|
continue;
|
|
3668
4262
|
}
|
|
3669
4263
|
await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
3670
|
-
console.log(
|
|
4264
|
+
console.log(import_chalk28.default.green(` Table "${a.name}" created.`));
|
|
3671
4265
|
} else {
|
|
3672
4266
|
const { table } = await inquirer2.prompt([{
|
|
3673
4267
|
type: "list",
|
|
@@ -3677,7 +4271,7 @@ async function mappingsMenu(proj2) {
|
|
|
3677
4271
|
}]);
|
|
3678
4272
|
if (!table) continue;
|
|
3679
4273
|
await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
3680
|
-
console.log(
|
|
4274
|
+
console.log(import_chalk28.default.green(` ${table.name} deleted.`));
|
|
3681
4275
|
}
|
|
3682
4276
|
}
|
|
3683
4277
|
}
|
|
@@ -3688,22 +4282,22 @@ async function tenantsMenu(proj2, opts) {
|
|
|
3688
4282
|
const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
|
|
3689
4283
|
const tenants = out?.tenants ?? [];
|
|
3690
4284
|
console.log();
|
|
3691
|
-
if (!tenants.length) console.log(
|
|
3692
|
-
for (const t of tenants) console.log(` ${
|
|
4285
|
+
if (!tenants.length) console.log(import_chalk28.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
4286
|
+
for (const t of tenants) console.log(` ${import_chalk28.default.bold(t.tenant_name ?? t.name)} ${import_chalk28.default.dim(t.display_name ?? "")}`);
|
|
3693
4287
|
const { act } = await inquirer2.prompt([{
|
|
3694
4288
|
type: "list",
|
|
3695
4289
|
name: "act",
|
|
3696
4290
|
message: "Tenants:",
|
|
3697
4291
|
choices: [
|
|
3698
|
-
{ name:
|
|
3699
|
-
|
|
3700
|
-
{ name:
|
|
4292
|
+
{ name: `Manage a tenant\u2026 ${import_chalk28.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
|
|
4293
|
+
{ name: "Attach a tenant to this project", value: "attach" },
|
|
4294
|
+
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
3701
4295
|
{ name: "\u2190 Back", value: "back" }
|
|
3702
4296
|
]
|
|
3703
4297
|
}]);
|
|
3704
4298
|
if (act === "back") return;
|
|
3705
|
-
if (act === "
|
|
3706
|
-
|
|
4299
|
+
if (act === "manage") {
|
|
4300
|
+
await runTenantManage(void 0, { team: opts.team });
|
|
3707
4301
|
continue;
|
|
3708
4302
|
}
|
|
3709
4303
|
if (act === "attach") {
|
|
@@ -3718,7 +4312,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
3718
4312
|
}]);
|
|
3719
4313
|
if (!t) continue;
|
|
3720
4314
|
await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
3721
|
-
console.log(
|
|
4315
|
+
console.log(import_chalk28.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
3722
4316
|
}
|
|
3723
4317
|
}
|
|
3724
4318
|
}
|
|
@@ -3761,7 +4355,7 @@ async function specMenu(proj2, opts) {
|
|
|
3761
4355
|
choices: [
|
|
3762
4356
|
{ name: "Print the stored spec", value: "get" },
|
|
3763
4357
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
3764
|
-
{ name:
|
|
4358
|
+
{ name: import_chalk28.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
3765
4359
|
{ name: "\u2190 Back", value: "back" }
|
|
3766
4360
|
]
|
|
3767
4361
|
}]);
|
|
@@ -3769,7 +4363,7 @@ async function specMenu(proj2, opts) {
|
|
|
3769
4363
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
3770
4364
|
else if (act === "refresh") {
|
|
3771
4365
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
3772
|
-
console.log(
|
|
4366
|
+
console.log(import_chalk28.default.green(" Spec refresh triggered."));
|
|
3773
4367
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
3774
4368
|
}
|
|
3775
4369
|
async function agentsMenu(proj2, opts) {
|
|
@@ -3794,8 +4388,9 @@ async function agentsMenu(proj2, opts) {
|
|
|
3794
4388
|
}
|
|
3795
4389
|
|
|
3796
4390
|
// src/commands/key.ts
|
|
3797
|
-
var
|
|
3798
|
-
var
|
|
4391
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
4392
|
+
var import_ora14 = __toESM(require("ora"));
|
|
4393
|
+
init_admin();
|
|
3799
4394
|
async function runApikeysMenu(opts) {
|
|
3800
4395
|
await runKeyList(opts);
|
|
3801
4396
|
if (opts.json) return;
|
|
@@ -3822,11 +4417,11 @@ async function runKeyList(opts) {
|
|
|
3822
4417
|
return;
|
|
3823
4418
|
}
|
|
3824
4419
|
if (!keys.length) {
|
|
3825
|
-
console.log(
|
|
4420
|
+
console.log(import_chalk29.default.yellow("No developer keys."));
|
|
3826
4421
|
return;
|
|
3827
4422
|
}
|
|
3828
4423
|
for (const k of keys) {
|
|
3829
|
-
console.log(` ${
|
|
4424
|
+
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
4425
|
}
|
|
3831
4426
|
}
|
|
3832
4427
|
async function runKeyMint(opts) {
|
|
@@ -3834,7 +4429,7 @@ async function runKeyMint(opts) {
|
|
|
3834
4429
|
const body = { role: "consumer-admin" };
|
|
3835
4430
|
if (opts.desc) body.description = opts.desc;
|
|
3836
4431
|
if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
|
|
3837
|
-
const spinner = (0,
|
|
4432
|
+
const spinner = (0, import_ora14.default)("Minting key...").start();
|
|
3838
4433
|
try {
|
|
3839
4434
|
const out = await admin({
|
|
3840
4435
|
method: "POST",
|
|
@@ -3847,9 +4442,9 @@ async function runKeyMint(opts) {
|
|
|
3847
4442
|
console.log(JSON.stringify(out));
|
|
3848
4443
|
return;
|
|
3849
4444
|
}
|
|
3850
|
-
console.log(` ${
|
|
3851
|
-
console.log(` ${
|
|
3852
|
-
if (out?.expires_at) console.log(` ${
|
|
4445
|
+
console.log(` ${import_chalk29.default.bold("key_id")}: ${out?.key_id}`);
|
|
4446
|
+
console.log(` ${import_chalk29.default.bold("key")}: ${import_chalk29.default.green(out?.key)} ${import_chalk29.default.dim("(shown once \u2014 store it now)")}`);
|
|
4447
|
+
if (out?.expires_at) console.log(` ${import_chalk29.default.dim("expires:")} ${out.expires_at}`);
|
|
3853
4448
|
} catch (err) {
|
|
3854
4449
|
spinner.fail("Mint failed.");
|
|
3855
4450
|
throw err;
|
|
@@ -3857,7 +4452,7 @@ async function runKeyMint(opts) {
|
|
|
3857
4452
|
}
|
|
3858
4453
|
async function runKeyRevoke(keyId, opts) {
|
|
3859
4454
|
const { teamId } = await resolveTeam(opts.team);
|
|
3860
|
-
const spinner = (0,
|
|
4455
|
+
const spinner = (0, import_ora14.default)("Revoking key...").start();
|
|
3861
4456
|
try {
|
|
3862
4457
|
await admin({
|
|
3863
4458
|
method: "DELETE",
|
|
@@ -3872,8 +4467,9 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
3872
4467
|
}
|
|
3873
4468
|
|
|
3874
4469
|
// src/commands/consumer.ts
|
|
3875
|
-
var
|
|
3876
|
-
var
|
|
4470
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
4471
|
+
var import_ora15 = __toESM(require("ora"));
|
|
4472
|
+
init_admin();
|
|
3877
4473
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
3878
4474
|
var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
|
|
3879
4475
|
async function consumerFetch(creds, suffix, init) {
|
|
@@ -3892,7 +4488,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
3892
4488
|
function requireConsumer() {
|
|
3893
4489
|
const c = loadConsumer();
|
|
3894
4490
|
if (!c) {
|
|
3895
|
-
console.error(
|
|
4491
|
+
console.error(import_chalk30.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
3896
4492
|
process.exit(1);
|
|
3897
4493
|
}
|
|
3898
4494
|
return c;
|
|
@@ -3903,48 +4499,37 @@ async function runConsumerLogin(opts) {
|
|
|
3903
4499
|
let clientId = opts.client;
|
|
3904
4500
|
if (clientId) {
|
|
3905
4501
|
if (!tenant2) {
|
|
3906
|
-
console.error(
|
|
4502
|
+
console.error(import_chalk30.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
3907
4503
|
process.exit(1);
|
|
3908
4504
|
}
|
|
3909
4505
|
} else {
|
|
3910
4506
|
requireAuth();
|
|
3911
4507
|
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
4508
|
if (!tenant2) {
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
}
|
|
4509
|
+
const { pickTenant: pickTenant2 } = await Promise.resolve().then(() => (init_tenant_pick(), tenant_pick_exports));
|
|
4510
|
+
const picked = await pickTenant2(teamId, { message: `Which tenant portal${teamName ? ` (team ${teamName})` : ""}?` });
|
|
4511
|
+
if (!picked) process.exit(1);
|
|
4512
|
+
tenant2 = picked;
|
|
3928
4513
|
}
|
|
3929
|
-
const s2 = (0,
|
|
4514
|
+
const s2 = (0, import_ora15.default)("Finding the login app...").start();
|
|
3930
4515
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
3931
4516
|
s2.stop();
|
|
3932
4517
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
3933
4518
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
3934
4519
|
if (!pick2) {
|
|
3935
|
-
console.error(
|
|
4520
|
+
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
4521
|
process.exit(1);
|
|
3937
4522
|
}
|
|
3938
4523
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
3939
4524
|
}
|
|
3940
4525
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
3941
|
-
console.log(`${
|
|
4526
|
+
console.log(`${import_chalk30.default.cyan("\u2192")} Logging in to ${import_chalk30.default.bold(tenant2)} as a consumer...`);
|
|
3942
4527
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
3943
4528
|
console.log(`
|
|
3944
|
-
Open: ${
|
|
3945
|
-
console.log(` Code: ${
|
|
4529
|
+
Open: ${import_chalk30.default.underline(verificationUri)}`);
|
|
4530
|
+
console.log(` Code: ${import_chalk30.default.bold(userCode)}
|
|
3946
4531
|
`);
|
|
3947
|
-
console.log(
|
|
4532
|
+
console.log(import_chalk30.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
3948
4533
|
}, portalResource);
|
|
3949
4534
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
3950
4535
|
const creds = {
|
|
@@ -3959,7 +4544,7 @@ async function runConsumerLogin(opts) {
|
|
|
3959
4544
|
obtainedAt: Date.now()
|
|
3960
4545
|
};
|
|
3961
4546
|
saveConsumer(creds);
|
|
3962
|
-
console.log(
|
|
4547
|
+
console.log(import_chalk30.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
3963
4548
|
}
|
|
3964
4549
|
async function runConsumerTokens(opts) {
|
|
3965
4550
|
const creds = requireConsumer();
|
|
@@ -3972,29 +4557,29 @@ async function runConsumerTokens(opts) {
|
|
|
3972
4557
|
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
4558
|
return;
|
|
3974
4559
|
}
|
|
3975
|
-
console.log(`${
|
|
4560
|
+
console.log(`${import_chalk30.default.cyan("Consumer")} ${import_chalk30.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk30.default.bold(fresh.tenant)}
|
|
3976
4561
|
`);
|
|
3977
|
-
console.log(`${
|
|
4562
|
+
console.log(`${import_chalk30.default.bold("access_token")} ${import_chalk30.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
3978
4563
|
${fresh.accessToken}
|
|
3979
4564
|
`);
|
|
3980
|
-
if (fresh.idToken) console.log(`${
|
|
4565
|
+
if (fresh.idToken) console.log(`${import_chalk30.default.bold("id_token")} ${import_chalk30.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
3981
4566
|
${fresh.idToken}
|
|
3982
4567
|
`);
|
|
3983
|
-
if (fresh.refreshToken) console.log(`${
|
|
4568
|
+
if (fresh.refreshToken) console.log(`${import_chalk30.default.bold("refresh_token")}
|
|
3984
4569
|
${fresh.refreshToken}
|
|
3985
4570
|
`);
|
|
3986
|
-
console.log(
|
|
4571
|
+
console.log(import_chalk30.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
3987
4572
|
}
|
|
3988
4573
|
async function runConsumerApikeys(opts) {
|
|
3989
4574
|
const creds = requireConsumer();
|
|
3990
4575
|
const { default: inquirer2 } = await import("inquirer");
|
|
3991
|
-
const spinner = (0,
|
|
4576
|
+
const spinner = (0, import_ora15.default)("Loading your API keys...").start();
|
|
3992
4577
|
const list = await consumerFetch(creds, "/apikeys");
|
|
3993
4578
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
3994
4579
|
spinner.stop();
|
|
3995
4580
|
if (list.status >= 400) {
|
|
3996
|
-
console.error(
|
|
3997
|
-
if (list.status === 401) console.error(
|
|
4581
|
+
console.error(import_chalk30.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
4582
|
+
if (list.status === 401) console.error(import_chalk30.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
3998
4583
|
process.exit(1);
|
|
3999
4584
|
}
|
|
4000
4585
|
const keys = list.data?.keys ?? [];
|
|
@@ -4002,16 +4587,16 @@ async function runConsumerApikeys(opts) {
|
|
|
4002
4587
|
if (opts.json) {
|
|
4003
4588
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
4004
4589
|
} else if (!keys.length) {
|
|
4005
|
-
console.log(
|
|
4590
|
+
console.log(import_chalk30.default.yellow("No API keys yet."));
|
|
4006
4591
|
} else {
|
|
4007
4592
|
for (const k of keys) {
|
|
4008
4593
|
const clear = revealMap[k.environment]?.key;
|
|
4009
|
-
const shown = clear ?
|
|
4010
|
-
const exp = k.expires_at ?
|
|
4011
|
-
console.log(` ${
|
|
4594
|
+
const shown = clear ? import_chalk30.default.green(clear) : import_chalk30.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
4595
|
+
const exp = k.expires_at ? import_chalk30.default.dim(`exp ${k.expires_at}`) : import_chalk30.default.dim("no expiry");
|
|
4596
|
+
console.log(` ${import_chalk30.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk30.default.dim(k.description ?? "")}`);
|
|
4012
4597
|
}
|
|
4013
4598
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
4014
|
-
console.log(
|
|
4599
|
+
console.log(import_chalk30.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
4015
4600
|
}
|
|
4016
4601
|
}
|
|
4017
4602
|
if (opts.json) return;
|
|
@@ -4025,7 +4610,7 @@ async function runConsumerApikeys(opts) {
|
|
|
4025
4610
|
const body = { environment: answers.environment };
|
|
4026
4611
|
if (answers.description) body.description = answers.description;
|
|
4027
4612
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
4028
|
-
const s2 = (0,
|
|
4613
|
+
const s2 = (0, import_ora15.default)("Creating key...").start();
|
|
4029
4614
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
4030
4615
|
if (created.status >= 400) {
|
|
4031
4616
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -4033,15 +4618,16 @@ async function runConsumerApikeys(opts) {
|
|
|
4033
4618
|
}
|
|
4034
4619
|
s2.succeed("Key created.");
|
|
4035
4620
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
4036
|
-
if (key) console.log(` ${
|
|
4037
|
-
else console.log(
|
|
4621
|
+
if (key) console.log(` ${import_chalk30.default.green(key)} ${import_chalk30.default.dim("(shown once \u2014 store it now)")}`);
|
|
4622
|
+
else console.log(import_chalk30.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
4038
4623
|
}
|
|
4039
4624
|
|
|
4040
4625
|
// src/commands/sidecar.ts
|
|
4041
|
-
var
|
|
4042
|
-
var
|
|
4626
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
4627
|
+
var import_ora16 = __toESM(require("ora"));
|
|
4043
4628
|
var fs7 = __toESM(require("fs"));
|
|
4044
4629
|
var path4 = __toESM(require("path"));
|
|
4630
|
+
init_admin();
|
|
4045
4631
|
init_auth();
|
|
4046
4632
|
function detectNextProject(root) {
|
|
4047
4633
|
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs7.existsSync(path4.join(root, f)));
|
|
@@ -4079,18 +4665,18 @@ function upsertEnvLocal(root, token) {
|
|
|
4079
4665
|
}
|
|
4080
4666
|
function installSidecarPackage(root) {
|
|
4081
4667
|
if (fs7.existsSync(path4.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
4082
|
-
console.log(` ${
|
|
4668
|
+
console.log(` ${import_chalk31.default.green("\u2713")} apiblaze package already installed`);
|
|
4083
4669
|
return;
|
|
4084
4670
|
}
|
|
4085
4671
|
const has = (f) => fs7.existsSync(path4.join(root, f));
|
|
4086
4672
|
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,
|
|
4673
|
+
const spinner = (0, import_ora16.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
4088
4674
|
try {
|
|
4089
4675
|
const { execSync } = require("child_process");
|
|
4090
4676
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
4091
4677
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
4092
4678
|
} catch {
|
|
4093
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
4679
|
+
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
4680
|
}
|
|
4095
4681
|
}
|
|
4096
4682
|
function readEnvKey(root) {
|
|
@@ -4229,7 +4815,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
4229
4815
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
4230
4816
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
4231
4817
|
if (opts.newSession) clearAnonCred2();
|
|
4232
|
-
const spinner = (0,
|
|
4818
|
+
const spinner = (0, import_ora16.default)("Setting up a sidecar (no login needed)...").start();
|
|
4233
4819
|
let out;
|
|
4234
4820
|
try {
|
|
4235
4821
|
out = await sidecarInitAnonymous2();
|
|
@@ -4241,29 +4827,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
4241
4827
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
4242
4828
|
const envState = upsertEnvLocal(root, out.token);
|
|
4243
4829
|
ensureGitignored(root);
|
|
4244
|
-
console.log(` ${
|
|
4245
|
-
console.log(` ${
|
|
4830
|
+
console.log(` ${import_chalk31.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
4831
|
+
console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
4246
4832
|
installSidecarPackage(root);
|
|
4247
4833
|
let inspectorPath = null;
|
|
4248
4834
|
if (!opts.noInspector) {
|
|
4249
4835
|
inspectorPath = generateInspector(root, router);
|
|
4250
|
-
if (inspectorPath) console.log(` ${
|
|
4836
|
+
if (inspectorPath) console.log(` ${import_chalk31.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
4251
4837
|
}
|
|
4252
4838
|
console.log("");
|
|
4253
|
-
console.log(
|
|
4254
|
-
console.log(` 1. ${
|
|
4839
|
+
console.log(import_chalk31.default.bold("Done (no account needed). What happens next:"));
|
|
4840
|
+
console.log(` 1. ${import_chalk31.default.cyan("npm run dev")} and use your app.`);
|
|
4255
4841
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
4256
|
-
console.log(` ${
|
|
4842
|
+
console.log(` ${import_chalk31.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
4257
4843
|
console.log("");
|
|
4258
|
-
console.log(
|
|
4259
|
-
console.log(` ${
|
|
4260
|
-
console.log(
|
|
4844
|
+
console.log(import_chalk31.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
4845
|
+
console.log(` ${import_chalk31.default.cyan("apiblaze login")} then ${import_chalk31.default.cyan("apiblaze claim")} ${import_chalk31.default.dim("(no code needed here)")}`);
|
|
4846
|
+
console.log(import_chalk31.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
4261
4847
|
}
|
|
4262
4848
|
async function runSidecar(opts) {
|
|
4263
4849
|
const root = path4.resolve(opts.dir ?? process.cwd());
|
|
4264
4850
|
const detected = detectNextProject(root);
|
|
4265
4851
|
if (!detected.found) {
|
|
4266
|
-
console.log(
|
|
4852
|
+
console.log(import_chalk31.default.yellow(`No Next.js project detected in ${root}.`));
|
|
4267
4853
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
4268
4854
|
return;
|
|
4269
4855
|
}
|
|
@@ -4274,10 +4860,10 @@ async function runSidecar(opts) {
|
|
|
4274
4860
|
if (!loadCredentials()) {
|
|
4275
4861
|
upsertEnvLocal(root, readEnvKey(root));
|
|
4276
4862
|
ensureGitignored(root);
|
|
4277
|
-
console.log(` ${
|
|
4278
|
-
console.log(` ${
|
|
4863
|
+
console.log(` ${import_chalk31.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
4864
|
+
console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
4279
4865
|
installSidecarPackage(root);
|
|
4280
|
-
console.log(
|
|
4866
|
+
console.log(import_chalk31.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
4281
4867
|
return;
|
|
4282
4868
|
}
|
|
4283
4869
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -4286,7 +4872,7 @@ async function runSidecar(opts) {
|
|
|
4286
4872
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
4287
4873
|
let token = existingKey ?? "";
|
|
4288
4874
|
if (mustMint) {
|
|
4289
|
-
const spinner = (0,
|
|
4875
|
+
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
4876
|
try {
|
|
4291
4877
|
const out = await admin({
|
|
4292
4878
|
method: "POST",
|
|
@@ -4300,39 +4886,40 @@ async function runSidecar(opts) {
|
|
|
4300
4886
|
throw err;
|
|
4301
4887
|
}
|
|
4302
4888
|
} else {
|
|
4303
|
-
console.log(
|
|
4889
|
+
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
4890
|
}
|
|
4305
4891
|
const envState = upsertEnvLocal(root, token);
|
|
4306
4892
|
ensureGitignored(root);
|
|
4307
|
-
console.log(` ${
|
|
4893
|
+
console.log(` ${import_chalk31.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
4308
4894
|
const wireState = wireInstrumentation(root);
|
|
4309
|
-
console.log(` ${
|
|
4895
|
+
console.log(` ${import_chalk31.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
4310
4896
|
installSidecarPackage(root);
|
|
4311
4897
|
let inspectorPath = null;
|
|
4312
4898
|
if (!opts.noInspector) {
|
|
4313
4899
|
inspectorPath = generateInspector(root, detected.router);
|
|
4314
|
-
if (inspectorPath) console.log(` ${
|
|
4900
|
+
if (inspectorPath) console.log(` ${import_chalk31.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
4315
4901
|
}
|
|
4316
4902
|
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: ${
|
|
4903
|
+
console.log(import_chalk31.default.bold("Done. What happens next:"));
|
|
4904
|
+
console.log(` 1. ${import_chalk31.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
4905
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk31.default.bold("candidates")} \u2014 list them: ${import_chalk31.default.cyan("apiblaze sidecar")}`);
|
|
4906
|
+
console.log(` 3. Approve the ones to route: ${import_chalk31.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
4321
4907
|
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(
|
|
4908
|
+
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)`);
|
|
4909
|
+
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
4910
|
console.log("");
|
|
4325
|
-
console.log(
|
|
4326
|
-
console.log(
|
|
4327
|
-
console.log(
|
|
4911
|
+
console.log(import_chalk31.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
4912
|
+
console.log(import_chalk31.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
4913
|
+
console.log(import_chalk31.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
4328
4914
|
console.log("");
|
|
4329
|
-
console.log(
|
|
4330
|
-
console.log(
|
|
4915
|
+
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."));
|
|
4916
|
+
console.log(import_chalk31.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
4331
4917
|
}
|
|
4332
4918
|
|
|
4333
4919
|
// src/commands/origins.ts
|
|
4334
|
-
var
|
|
4335
|
-
var
|
|
4920
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
4921
|
+
var import_ora17 = __toESM(require("ora"));
|
|
4922
|
+
init_admin();
|
|
4336
4923
|
init_auth();
|
|
4337
4924
|
init_anon_cred();
|
|
4338
4925
|
async function runOriginsList(opts) {
|
|
@@ -4340,7 +4927,7 @@ async function runOriginsList(opts) {
|
|
|
4340
4927
|
if (!loadCredentials()) {
|
|
4341
4928
|
const cred = loadAnonCred();
|
|
4342
4929
|
if (!cred) {
|
|
4343
|
-
console.log(
|
|
4930
|
+
console.log(import_chalk32.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
4344
4931
|
return;
|
|
4345
4932
|
}
|
|
4346
4933
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -4358,30 +4945,30 @@ async function runOriginsList(opts) {
|
|
|
4358
4945
|
}
|
|
4359
4946
|
const routed = out.routed ?? [];
|
|
4360
4947
|
const candidates = out.candidates ?? [];
|
|
4361
|
-
console.log(
|
|
4948
|
+
console.log(import_chalk32.default.bold(`
|
|
4362
4949
|
Routed through APIblaze (${routed.length})`));
|
|
4363
|
-
if (!routed.length) console.log(
|
|
4364
|
-
for (const r of routed) console.log(` ${
|
|
4365
|
-
console.log(
|
|
4950
|
+
if (!routed.length) console.log(import_chalk32.default.dim(" none yet"));
|
|
4951
|
+
for (const r of routed) console.log(` ${import_chalk32.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk32.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
4952
|
+
console.log(import_chalk32.default.bold(`
|
|
4366
4953
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
4367
|
-
if (!candidates.length) console.log(
|
|
4954
|
+
if (!candidates.length) console.log(import_chalk32.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
4368
4955
|
for (const c of candidates) {
|
|
4369
|
-
console.log(` ${
|
|
4956
|
+
console.log(` ${import_chalk32.default.yellow("\u25CB")} ${c.origin} ${import_chalk32.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
4370
4957
|
}
|
|
4371
4958
|
if (candidates.length) {
|
|
4372
|
-
console.log(
|
|
4959
|
+
console.log(import_chalk32.default.dim(`
|
|
4373
4960
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
4374
|
-
console.log(
|
|
4961
|
+
console.log(import_chalk32.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
4375
4962
|
}
|
|
4376
4963
|
}
|
|
4377
4964
|
async function runOriginsApprove(origin, opts) {
|
|
4378
4965
|
if (!loadCredentials()) {
|
|
4379
4966
|
const cred = loadAnonCred();
|
|
4380
4967
|
if (!cred) {
|
|
4381
|
-
console.error(
|
|
4968
|
+
console.error(import_chalk32.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
4382
4969
|
process.exit(1);
|
|
4383
4970
|
}
|
|
4384
|
-
const spinner2 = (0,
|
|
4971
|
+
const spinner2 = (0, import_ora17.default)(`Approving ${origin} (anonymous)...`).start();
|
|
4385
4972
|
try {
|
|
4386
4973
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
4387
4974
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -4392,7 +4979,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
4392
4979
|
return;
|
|
4393
4980
|
}
|
|
4394
4981
|
const { teamId } = await resolveTeam(opts.team);
|
|
4395
|
-
const spinner = (0,
|
|
4982
|
+
const spinner = (0, import_ora17.default)(`Approving ${origin}...`).start();
|
|
4396
4983
|
try {
|
|
4397
4984
|
const out = await admin({
|
|
4398
4985
|
method: "POST",
|
|
@@ -4409,7 +4996,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
4409
4996
|
}
|
|
4410
4997
|
async function runOriginsDeny(origin, opts) {
|
|
4411
4998
|
const { teamId } = await resolveTeam(opts.team);
|
|
4412
|
-
const spinner = (0,
|
|
4999
|
+
const spinner = (0, import_ora17.default)(`Dismissing ${origin}...`).start();
|
|
4413
5000
|
try {
|
|
4414
5001
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
4415
5002
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -4420,7 +5007,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
4420
5007
|
}
|
|
4421
5008
|
async function runOriginsRemove(origin, opts) {
|
|
4422
5009
|
const { teamId } = await resolveTeam(opts.team);
|
|
4423
|
-
const spinner = (0,
|
|
5010
|
+
const spinner = (0, import_ora17.default)(`Removing the proxy for ${origin}...`).start();
|
|
4424
5011
|
try {
|
|
4425
5012
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
4426
5013
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -4431,8 +5018,9 @@ async function runOriginsRemove(origin, opts) {
|
|
|
4431
5018
|
}
|
|
4432
5019
|
|
|
4433
5020
|
// src/commands/op.ts
|
|
4434
|
-
var
|
|
5021
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
4435
5022
|
init_auth();
|
|
5023
|
+
init_trace();
|
|
4436
5024
|
init_types();
|
|
4437
5025
|
var OPERATOR_EMAILS = /* @__PURE__ */ new Set(["julienpmjacquet@gmail.com", "chkev@umich.edu"]);
|
|
4438
5026
|
var DASHBOARD_BASE6 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
@@ -4462,53 +5050,53 @@ async function opCall(call) {
|
|
|
4462
5050
|
function printResidue(report, applied) {
|
|
4463
5051
|
const up = report?.upstash ?? {};
|
|
4464
5052
|
const fga = report?.fga ?? {};
|
|
4465
|
-
console.log(
|
|
4466
|
-
console.log(
|
|
5053
|
+
console.log(import_chalk33.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
5054
|
+
console.log(import_chalk33.default.bold("\n Upstash"));
|
|
4467
5055
|
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(
|
|
5056
|
+
if (orphans.length === 0) console.log(import_chalk33.default.green(" no orphaned keys"));
|
|
5057
|
+
for (const o of orphans) console.log(` ${import_chalk33.default.yellow(o.key)} ${import_chalk33.default.dim(`\u2014 ${o.reason}`)}`);
|
|
5058
|
+
console.log(import_chalk33.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
5059
|
+
if (up.unknown?.length) console.log(import_chalk33.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
5060
|
+
if (applied) console.log(` ${import_chalk33.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
5061
|
+
for (const e of up.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
|
|
5062
|
+
console.log(import_chalk33.default.bold("\n OpenFGA / Neon"));
|
|
4475
5063
|
if (applied) {
|
|
4476
5064
|
const swept = fga?.swept ?? [];
|
|
4477
|
-
if (swept.length === 0) console.log(
|
|
5065
|
+
if (swept.length === 0) console.log(import_chalk33.default.green(" no orphaned stores"));
|
|
4478
5066
|
for (const s of swept) {
|
|
4479
5067
|
console.log(
|
|
4480
|
-
` ${
|
|
5068
|
+
` ${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
5069
|
);
|
|
4482
5070
|
}
|
|
4483
|
-
if (fga?.remaining) console.log(
|
|
5071
|
+
if (fga?.remaining) console.log(import_chalk33.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
4484
5072
|
} else {
|
|
4485
5073
|
const fgaOrphans = fga?.orphans ?? [];
|
|
4486
|
-
if (fgaOrphans.length === 0) console.log(
|
|
5074
|
+
if (fgaOrphans.length === 0) console.log(import_chalk33.default.green(" no orphaned stores"));
|
|
4487
5075
|
for (const s of fgaOrphans) {
|
|
4488
5076
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
4489
|
-
console.log(` ${
|
|
5077
|
+
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
5078
|
}
|
|
4491
|
-
console.log(
|
|
5079
|
+
console.log(import_chalk33.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
4492
5080
|
}
|
|
4493
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
5081
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk33.default.red(` error: ${e}`));
|
|
4494
5082
|
console.log();
|
|
4495
5083
|
}
|
|
4496
5084
|
async function runOp(sub, opts = {}) {
|
|
4497
5085
|
if (!loadCredentials()) {
|
|
4498
|
-
console.log(
|
|
5086
|
+
console.log(import_chalk33.default.dim("Not logged in. Run `apiblaze login`."));
|
|
4499
5087
|
return;
|
|
4500
5088
|
}
|
|
4501
5089
|
if (!isOperatorLogin()) {
|
|
4502
|
-
console.log(
|
|
5090
|
+
console.log(import_chalk33.default.dim("`apiblaze op` is only available to platform operators."));
|
|
4503
5091
|
return;
|
|
4504
5092
|
}
|
|
4505
5093
|
switch (sub) {
|
|
4506
5094
|
case void 0:
|
|
4507
5095
|
case "menu": {
|
|
4508
|
-
console.log(
|
|
4509
|
-
console.log(` ${
|
|
4510
|
-
console.log(` ${
|
|
4511
|
-
console.log(` ${
|
|
5096
|
+
console.log(import_chalk33.default.bold("\nOperator menu"));
|
|
5097
|
+
console.log(` ${import_chalk33.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
5098
|
+
console.log(` ${import_chalk33.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk33.default.dim("-y to skip")})`);
|
|
5099
|
+
console.log(` ${import_chalk33.default.cyan("apiblaze op credits")} list credit wallets
|
|
4512
5100
|
`);
|
|
4513
5101
|
return;
|
|
4514
5102
|
}
|
|
@@ -4524,15 +5112,15 @@ async function runOp(sub, opts = {}) {
|
|
|
4524
5112
|
const nFga = report?.fga?.orphans?.length ?? 0;
|
|
4525
5113
|
printResidue(report, false);
|
|
4526
5114
|
if (nUp + nFga === 0) {
|
|
4527
|
-
console.log(
|
|
5115
|
+
console.log(import_chalk33.default.green("Nothing to sweep."));
|
|
4528
5116
|
return;
|
|
4529
5117
|
}
|
|
4530
5118
|
if (!opts.yes) {
|
|
4531
5119
|
const readline2 = await import("readline/promises");
|
|
4532
5120
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
4533
|
-
const answer = await rl.question(
|
|
5121
|
+
const answer = await rl.question(import_chalk33.default.red(`Delete ${nUp} Upstash key(s) + ${nFga} OpenFGA store(s)? Type 'sweep' to confirm: `));
|
|
4534
5122
|
rl.close();
|
|
4535
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
5123
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk33.default.dim("Aborted."));
|
|
4536
5124
|
}
|
|
4537
5125
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
4538
5126
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -4543,19 +5131,20 @@ async function runOp(sub, opts = {}) {
|
|
|
4543
5131
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
4544
5132
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
4545
5133
|
const accounts = data?.accounts ?? [];
|
|
4546
|
-
if (accounts.length === 0) return void console.log(
|
|
5134
|
+
if (accounts.length === 0) return void console.log(import_chalk33.default.dim("No credit wallets."));
|
|
4547
5135
|
for (const a of accounts) {
|
|
4548
5136
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
4549
|
-
console.log(` ${
|
|
5137
|
+
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
5138
|
}
|
|
4551
5139
|
return;
|
|
4552
5140
|
}
|
|
4553
5141
|
default:
|
|
4554
|
-
console.log(
|
|
5142
|
+
console.log(import_chalk33.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
4555
5143
|
}
|
|
4556
5144
|
}
|
|
4557
5145
|
|
|
4558
5146
|
// src/index.ts
|
|
5147
|
+
init_trace();
|
|
4559
5148
|
var program = new import_commander.Command();
|
|
4560
5149
|
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
5150
|
program.hook("preAction", () => {
|
|
@@ -4606,7 +5195,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
4606
5195
|
try {
|
|
4607
5196
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
4608
5197
|
if (Number.isNaN(resolved)) {
|
|
4609
|
-
console.error(
|
|
5198
|
+
console.error(import_chalk34.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
4610
5199
|
process.exit(1);
|
|
4611
5200
|
}
|
|
4612
5201
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -4664,8 +5253,10 @@ domain.command("list").description("List custom domains for a proxy").argument("
|
|
|
4664
5253
|
domain.command("status").description("Check a custom domain's validation status").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").option("--json", "Output machine-readable JSON").action(action((project, opts) => runDomainStatus(project, opts)));
|
|
4665
5254
|
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
5255
|
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
|
-
var tenant = program.command("tenant").description("Manage tenants \u2014
|
|
4668
|
-
tenant.command("
|
|
5256
|
+
var tenant = program.command("tenant").description("Manage tenants \u2014 bare command opens the interactive picker (settings, app clients, providers)").action(action(() => runTenantManage(void 0, {})));
|
|
5257
|
+
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)));
|
|
5258
|
+
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)));
|
|
5259
|
+
tenant.command("list").description("List tenants in your team (--q searches server-side)").option("--q <search>", "Filter by tenant name or display name").option("--limit <n>", "Page size (max 200)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runTenantList(opts)));
|
|
4669
5260
|
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
5261
|
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)));
|
|
4671
5262
|
tenant.command("delete").description("Delete a tenant (full cascade)").argument("<slug>", "Tenant slug to delete").option("--team <id|name>", "Team (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").action(action((slug, opts) => runTenantDelete(slug, opts)));
|
|
@@ -4702,7 +5293,7 @@ function groupedCommandHelp() {
|
|
|
4702
5293
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
4703
5294
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
4704
5295
|
}).filter(Boolean).join("\n");
|
|
4705
|
-
return `${
|
|
5296
|
+
return `${import_chalk34.default.bold(g.title)}
|
|
4706
5297
|
${rows}`;
|
|
4707
5298
|
}).join("\n\n");
|
|
4708
5299
|
}
|
|
@@ -4730,13 +5321,18 @@ Examples:
|
|
|
4730
5321
|
`);
|
|
4731
5322
|
function printError(err) {
|
|
4732
5323
|
if (err instanceof ApiError) {
|
|
4733
|
-
|
|
4734
|
-
|
|
5324
|
+
const data = err.body;
|
|
5325
|
+
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
5326
|
+
console.error(import_chalk34.default.red(`
|
|
5327
|
+
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
5328
|
+
if (err.status === 403 || err.status === 404) {
|
|
5329
|
+
console.error(import_chalk34.default.dim("If your team/tenant was recently deleted or recreated, re-run `apiblaze login` or switch with `apiblaze team`."));
|
|
5330
|
+
}
|
|
4735
5331
|
} else if (err instanceof Error) {
|
|
4736
|
-
console.error(
|
|
5332
|
+
console.error(import_chalk34.default.red(`
|
|
4737
5333
|
Error: ${err.message}`));
|
|
4738
5334
|
} else {
|
|
4739
|
-
console.error(
|
|
5335
|
+
console.error(import_chalk34.default.red("\nUnknown error"));
|
|
4740
5336
|
}
|
|
4741
5337
|
}
|
|
4742
5338
|
program.parse(process.argv);
|