apiblaze 0.15.2 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +575 -430
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -457,6 +457,140 @@ var init_admin = __esm({
|
|
|
457
457
|
}
|
|
458
458
|
});
|
|
459
459
|
|
|
460
|
+
// src/lib/tenant-create.ts
|
|
461
|
+
var tenant_create_exports = {};
|
|
462
|
+
__export(tenant_create_exports, {
|
|
463
|
+
addApiblazeHostedLogin: () => addApiblazeHostedLogin,
|
|
464
|
+
createTenantInteractive: () => createTenantInteractive
|
|
465
|
+
});
|
|
466
|
+
async function createTenantRow(teamId) {
|
|
467
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
468
|
+
for (; ; ) {
|
|
469
|
+
const { name } = await inquirer2.prompt([{
|
|
470
|
+
type: "input",
|
|
471
|
+
name: "name",
|
|
472
|
+
message: `Tenant name ${import_chalk22.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
|
|
473
|
+
validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
|
|
474
|
+
}]);
|
|
475
|
+
const slug = name.trim();
|
|
476
|
+
try {
|
|
477
|
+
const out = await admin({
|
|
478
|
+
method: "POST",
|
|
479
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants`,
|
|
480
|
+
body: { tenant_name: slug, display_name: slug },
|
|
481
|
+
summary: `Create tenant "${slug}"`
|
|
482
|
+
});
|
|
483
|
+
return out?.tenant_name ?? out?.tenant?.tenant_name ?? slug;
|
|
484
|
+
} catch (err) {
|
|
485
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
486
|
+
if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
|
|
487
|
+
console.log(import_chalk22.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
throw err;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
495
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
496
|
+
const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
497
|
+
const { provider } = await inquirer2.prompt([{
|
|
498
|
+
type: "list",
|
|
499
|
+
name: "provider",
|
|
500
|
+
message: "Which login provider?",
|
|
501
|
+
pageSize: 10,
|
|
502
|
+
choices: [
|
|
503
|
+
...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
|
|
504
|
+
...opts.allowSkip ? [new inquirer2.Separator(), { name: import_chalk22.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
|
|
505
|
+
]
|
|
506
|
+
}]);
|
|
507
|
+
if (!provider) {
|
|
508
|
+
console.log(import_chalk22.default.dim(" No login method yet \u2014 add one later under Login methods."));
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
const opt = PROVIDERS.find((p) => p.id === provider);
|
|
512
|
+
let providerBody;
|
|
513
|
+
if (!opt.own) {
|
|
514
|
+
providerBody = { type: "github", managed: true };
|
|
515
|
+
} else {
|
|
516
|
+
const a = await inquirer2.prompt([
|
|
517
|
+
{ type: "input", name: "clientId", message: `${opt.label} OAuth client id:`, validate: (s) => !!s.trim() || "required" },
|
|
518
|
+
{ type: "password", name: "clientSecret", mask: "*", message: `${opt.label} OAuth client secret:`, validate: (s) => s.length >= 6 && s.length <= 200 || "6\u2013200 chars" },
|
|
519
|
+
...provider === "auth0" || provider === "other" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : []
|
|
520
|
+
]);
|
|
521
|
+
providerBody = {
|
|
522
|
+
type: provider,
|
|
523
|
+
clientId: a.clientId.trim(),
|
|
524
|
+
clientSecret: a.clientSecret,
|
|
525
|
+
...a.domain?.trim() ? { domain: a.domain.trim() } : {},
|
|
526
|
+
scopes: (DEFAULT_SCOPES[provider] ?? "openid email profile").split(/\s+/).filter(Boolean)
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
const { advanced } = await inquirer2.prompt([{ type: "confirm", name: "advanced", message: "Configure advanced settings (callback URLs, scopes)?", default: false }]);
|
|
530
|
+
const clientBody = { name: `${tenant2}-login` };
|
|
531
|
+
if (advanced) {
|
|
532
|
+
const a = await inquirer2.prompt([
|
|
533
|
+
{ type: "input", name: "callbacks", message: "Callback URLs (comma-separated, empty = none):" },
|
|
534
|
+
{ type: "input", name: "scopes", message: `Provider scopes:`, default: (providerBody.scopes ?? []).join(" ") }
|
|
535
|
+
]);
|
|
536
|
+
if (a.callbacks.trim()) clientBody.authorizedCallbackUrls = a.callbacks.split(/\s*,\s*/).map((s) => s.trim()).filter(Boolean);
|
|
537
|
+
if (a.scopes.trim() && opt.own) providerBody.scopes = a.scopes.split(/\s+/).filter(Boolean);
|
|
538
|
+
}
|
|
539
|
+
const spinner = (0, import_ora8.default)("Setting up login...").start();
|
|
540
|
+
try {
|
|
541
|
+
const client = await admin({ method: "POST", path: `${base}/app-clients`, body: clientBody, summary: `Create login for ${tenant2}` });
|
|
542
|
+
const clientId = client?.clientId ?? client?.client_id;
|
|
543
|
+
await admin({ method: "POST", path: `${base}/app-clients/${encodeURIComponent(clientId)}/providers`, body: providerBody, summary: `Add ${provider} provider` });
|
|
544
|
+
spinner.succeed(opt.own ? `${opt.label} login is ready.` : "APIblaze-hosted GitHub login is ready.");
|
|
545
|
+
return true;
|
|
546
|
+
} catch (err) {
|
|
547
|
+
spinner.fail("Login setup failed.");
|
|
548
|
+
throw err;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async function createTenantInteractive(teamId) {
|
|
552
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
553
|
+
const tenant2 = await createTenantRow(teamId);
|
|
554
|
+
console.log(import_chalk22.default.green(` Tenant ${import_chalk22.default.bold(tenant2)} created.`));
|
|
555
|
+
const { users } = await inquirer2.prompt([{
|
|
556
|
+
type: "confirm",
|
|
557
|
+
name: "users",
|
|
558
|
+
default: false,
|
|
559
|
+
message: `Enable Users & groups? ${import_chalk22.default.dim(`(identity/key management at ${tenant2}.iam.apiblaze.com)`)}`
|
|
560
|
+
}]);
|
|
561
|
+
if (users) {
|
|
562
|
+
await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
|
|
563
|
+
});
|
|
564
|
+
console.log(import_chalk22.default.green(" Users & groups enabled."));
|
|
565
|
+
}
|
|
566
|
+
await addApiblazeHostedLogin(teamId, tenant2, { allowSkip: true });
|
|
567
|
+
return tenant2;
|
|
568
|
+
}
|
|
569
|
+
var import_chalk22, import_ora8, PROVIDERS, DEFAULT_SCOPES;
|
|
570
|
+
var init_tenant_create = __esm({
|
|
571
|
+
"src/lib/tenant-create.ts"() {
|
|
572
|
+
"use strict";
|
|
573
|
+
import_chalk22 = __toESM(require("chalk"));
|
|
574
|
+
import_ora8 = __toESM(require("ora"));
|
|
575
|
+
init_admin();
|
|
576
|
+
PROVIDERS = [
|
|
577
|
+
{ id: "apiblaze", label: "APIblaze (via GitHub) \u2014 zero setup, recommended", own: false },
|
|
578
|
+
{ id: "google", label: "Google", own: true },
|
|
579
|
+
{ id: "github", label: "GitHub", own: true },
|
|
580
|
+
{ id: "microsoft", label: "Microsoft", own: true },
|
|
581
|
+
{ id: "facebook", label: "Facebook", own: true },
|
|
582
|
+
{ id: "auth0", label: "Auth0", own: true },
|
|
583
|
+
{ id: "other", label: "Custom (any OIDC provider)", own: true }
|
|
584
|
+
];
|
|
585
|
+
DEFAULT_SCOPES = {
|
|
586
|
+
google: "openid email profile",
|
|
587
|
+
microsoft: "openid email profile",
|
|
588
|
+
github: "read:user user:email",
|
|
589
|
+
facebook: "public_profile email"
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
|
|
460
594
|
// src/lib/tenant-pick.ts
|
|
461
595
|
var tenant_pick_exports = {};
|
|
462
596
|
__export(tenant_pick_exports, {
|
|
@@ -475,10 +609,10 @@ async function fetchPage(teamId, q) {
|
|
|
475
609
|
}
|
|
476
610
|
function label(t, defaultTenant, active) {
|
|
477
611
|
const tags = [
|
|
478
|
-
t.tenant_name === active ?
|
|
479
|
-
t.tenant_name === defaultTenant ?
|
|
612
|
+
t.tenant_name === active ? import_chalk23.default.cyan("active scope") : "",
|
|
613
|
+
t.tenant_name === defaultTenant ? import_chalk23.default.dim("team default") : ""
|
|
480
614
|
].filter(Boolean).join(", ");
|
|
481
|
-
const disp = t.display_name && t.display_name !== t.tenant_name ?
|
|
615
|
+
const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk23.default.dim(` ${t.display_name}`) : "";
|
|
482
616
|
return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
|
|
483
617
|
}
|
|
484
618
|
async function pickTenant(teamId, opts = {}) {
|
|
@@ -486,14 +620,14 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
486
620
|
const active = loadCredentials()?.activeTenant;
|
|
487
621
|
let q = opts.initialQuery ?? "";
|
|
488
622
|
for (; ; ) {
|
|
489
|
-
const spinner = (0,
|
|
623
|
+
const spinner = (0, import_ora9.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
|
|
490
624
|
const page = await fetchPage(teamId, q).finally(() => spinner.stop());
|
|
491
625
|
if (!page.total && !q) {
|
|
492
626
|
if (opts.allowCreate) {
|
|
493
627
|
const { make } = await inquirer2.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
|
|
494
628
|
if (make) return await createTenantInline(teamId);
|
|
495
629
|
}
|
|
496
|
-
console.error(
|
|
630
|
+
console.error(import_chalk23.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
497
631
|
return null;
|
|
498
632
|
}
|
|
499
633
|
const truncated = page.total > page.rows.length;
|
|
@@ -502,7 +636,7 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
502
636
|
value: t.tenant_name
|
|
503
637
|
}));
|
|
504
638
|
if (truncated || q) {
|
|
505
|
-
choices.push(new inquirer2.Separator(
|
|
639
|
+
choices.push(new inquirer2.Separator(import_chalk23.default.dim(
|
|
506
640
|
truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
|
|
507
641
|
)));
|
|
508
642
|
choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
|
|
@@ -533,41 +667,15 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
533
667
|
}
|
|
534
668
|
}
|
|
535
669
|
async function createTenantInline(teamId) {
|
|
536
|
-
const {
|
|
537
|
-
|
|
538
|
-
const { name } = await inquirer2.prompt([{
|
|
539
|
-
type: "input",
|
|
540
|
-
name: "name",
|
|
541
|
-
message: `Tenant name ${import_chalk22.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
|
|
542
|
-
validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
|
|
543
|
-
}]);
|
|
544
|
-
const slugInput = name.trim();
|
|
545
|
-
try {
|
|
546
|
-
const out = await admin({
|
|
547
|
-
method: "POST",
|
|
548
|
-
path: `/teams/${encodeURIComponent(teamId)}/tenants`,
|
|
549
|
-
body: { tenant_name: slugInput, display_name: slugInput },
|
|
550
|
-
summary: `Create tenant "${slugInput}"`
|
|
551
|
-
});
|
|
552
|
-
const slug = out?.tenant_name ?? out?.tenant?.tenant_name ?? slugInput;
|
|
553
|
-
console.log(import_chalk22.default.green(` Tenant ${import_chalk22.default.bold(slug)} created.`));
|
|
554
|
-
return slug;
|
|
555
|
-
} catch (err) {
|
|
556
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
557
|
-
if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
|
|
558
|
-
console.log(import_chalk22.default.yellow(` "${slugInput}" is not available (tenant names are global): ${msg}`));
|
|
559
|
-
continue;
|
|
560
|
-
}
|
|
561
|
-
throw err;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
670
|
+
const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
|
|
671
|
+
return createTenantInteractive2(teamId);
|
|
564
672
|
}
|
|
565
|
-
var
|
|
673
|
+
var import_chalk23, import_ora9, PAGE;
|
|
566
674
|
var init_tenant_pick = __esm({
|
|
567
675
|
"src/lib/tenant-pick.ts"() {
|
|
568
676
|
"use strict";
|
|
569
|
-
|
|
570
|
-
|
|
677
|
+
import_chalk23 = __toESM(require("chalk"));
|
|
678
|
+
import_ora9 = __toESM(require("ora"));
|
|
571
679
|
init_admin();
|
|
572
680
|
init_auth();
|
|
573
681
|
PAGE = 15;
|
|
@@ -576,10 +684,10 @@ var init_tenant_pick = __esm({
|
|
|
576
684
|
|
|
577
685
|
// src/index.ts
|
|
578
686
|
var import_commander = require("commander");
|
|
579
|
-
var
|
|
687
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
580
688
|
|
|
581
689
|
// package.json
|
|
582
|
-
var version = "0.
|
|
690
|
+
var version = "0.17.0";
|
|
583
691
|
|
|
584
692
|
// src/index.ts
|
|
585
693
|
init_types();
|
|
@@ -2652,8 +2760,8 @@ async function runRename(project, opts) {
|
|
|
2652
2760
|
}
|
|
2653
2761
|
|
|
2654
2762
|
// src/commands/config-browse.ts
|
|
2655
|
-
var
|
|
2656
|
-
var
|
|
2763
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
2764
|
+
var import_ora14 = __toESM(require("ora"));
|
|
2657
2765
|
init_admin();
|
|
2658
2766
|
init_auth();
|
|
2659
2767
|
|
|
@@ -2763,28 +2871,28 @@ async function runDomainSetBase(project, opts) {
|
|
|
2763
2871
|
}
|
|
2764
2872
|
|
|
2765
2873
|
// src/commands/tenant.ts
|
|
2766
|
-
var
|
|
2767
|
-
var
|
|
2874
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
2875
|
+
var import_ora10 = __toESM(require("ora"));
|
|
2768
2876
|
init_admin();
|
|
2769
2877
|
init_auth();
|
|
2770
2878
|
init_tenant_pick();
|
|
2771
2879
|
async function runTenantUse(query, opts) {
|
|
2772
2880
|
const creds = loadCredentials();
|
|
2773
2881
|
if (!creds) {
|
|
2774
|
-
console.error(
|
|
2882
|
+
console.error(import_chalk24.default.red("Not logged in. Run `apiblaze login` first."));
|
|
2775
2883
|
process.exit(1);
|
|
2776
2884
|
}
|
|
2777
2885
|
if (opts.clear) {
|
|
2778
2886
|
delete creds.activeTenant;
|
|
2779
2887
|
saveCredentials(creds);
|
|
2780
|
-
console.log(
|
|
2888
|
+
console.log(import_chalk24.default.green("Tenant scope cleared."));
|
|
2781
2889
|
return;
|
|
2782
2890
|
}
|
|
2783
2891
|
const { teamId } = await resolveTeam(opts.team);
|
|
2784
2892
|
const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
|
|
2785
2893
|
if (!slug) process.exit(1);
|
|
2786
2894
|
saveCredentials({ ...creds, activeTenant: slug });
|
|
2787
|
-
console.log(
|
|
2895
|
+
console.log(import_chalk24.default.green(`Tenant scope set to ${import_chalk24.default.bold(slug)}.`) + import_chalk24.default.dim(" (clear with `apiblaze tenant use --clear`)"));
|
|
2788
2896
|
}
|
|
2789
2897
|
async function runTenantList(opts) {
|
|
2790
2898
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -2801,26 +2909,26 @@ async function runTenantList(opts) {
|
|
|
2801
2909
|
return;
|
|
2802
2910
|
}
|
|
2803
2911
|
if (!tenants.length) {
|
|
2804
|
-
console.log(
|
|
2912
|
+
console.log(import_chalk24.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
|
|
2805
2913
|
return;
|
|
2806
2914
|
}
|
|
2807
2915
|
for (const t of tenants) {
|
|
2808
2916
|
const name = typeof t === "string" ? t : t.tenant_name;
|
|
2809
|
-
const display = typeof t === "string" ? "" :
|
|
2810
|
-
console.log(` ${
|
|
2917
|
+
const display = typeof t === "string" ? "" : import_chalk24.default.dim(` ${t.display_name ?? ""}`);
|
|
2918
|
+
console.log(` ${import_chalk24.default.bold(name)}${display}`);
|
|
2811
2919
|
}
|
|
2812
2920
|
const total = out?.total ?? tenants.length;
|
|
2813
2921
|
if (total > tenants.length) {
|
|
2814
|
-
console.log(
|
|
2922
|
+
console.log(import_chalk24.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
|
|
2815
2923
|
}
|
|
2816
2924
|
}
|
|
2817
2925
|
async function runTenantCreate(opts) {
|
|
2818
2926
|
if (!opts.name) {
|
|
2819
|
-
console.error(
|
|
2927
|
+
console.error(import_chalk24.default.red("--name (display name) is required."));
|
|
2820
2928
|
process.exit(1);
|
|
2821
2929
|
}
|
|
2822
2930
|
const { teamId } = await resolveTeam(opts.team);
|
|
2823
|
-
const spinner = (0,
|
|
2931
|
+
const spinner = (0, import_ora10.default)("Creating tenant...").start();
|
|
2824
2932
|
try {
|
|
2825
2933
|
const out = await admin({
|
|
2826
2934
|
method: "POST",
|
|
@@ -2828,7 +2936,7 @@ async function runTenantCreate(opts) {
|
|
|
2828
2936
|
body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
|
|
2829
2937
|
summary: `Create tenant "${opts.name}"`
|
|
2830
2938
|
});
|
|
2831
|
-
spinner.succeed(`Created tenant ${
|
|
2939
|
+
spinner.succeed(`Created tenant ${import_chalk24.default.bold(out?.tenant_name ?? opts.name)}.`);
|
|
2832
2940
|
if (opts.json) console.log(JSON.stringify(out));
|
|
2833
2941
|
} catch (err) {
|
|
2834
2942
|
spinner.fail("Tenant create failed.");
|
|
@@ -2837,12 +2945,12 @@ async function runTenantCreate(opts) {
|
|
|
2837
2945
|
}
|
|
2838
2946
|
async function runTenantAttach(project, opts) {
|
|
2839
2947
|
if (!opts.tenant) {
|
|
2840
|
-
console.error(
|
|
2948
|
+
console.error(import_chalk24.default.red("--tenant <slug> is required."));
|
|
2841
2949
|
process.exit(1);
|
|
2842
2950
|
}
|
|
2843
2951
|
const { teamId } = await resolveTeam(opts.team);
|
|
2844
2952
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2845
|
-
const spinner = (0,
|
|
2953
|
+
const spinner = (0, import_ora10.default)("Attaching tenant...").start();
|
|
2846
2954
|
try {
|
|
2847
2955
|
const out = await admin({
|
|
2848
2956
|
method: "POST",
|
|
@@ -2865,11 +2973,11 @@ async function runTenantDelete(slug, opts) {
|
|
|
2865
2973
|
{ type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
|
|
2866
2974
|
]);
|
|
2867
2975
|
if (!confirm) {
|
|
2868
|
-
console.log(
|
|
2976
|
+
console.log(import_chalk24.default.dim("Aborted."));
|
|
2869
2977
|
return;
|
|
2870
2978
|
}
|
|
2871
2979
|
}
|
|
2872
|
-
const spinner = (0,
|
|
2980
|
+
const spinner = (0, import_ora10.default)("Deleting tenant...").start();
|
|
2873
2981
|
try {
|
|
2874
2982
|
await admin({
|
|
2875
2983
|
method: "DELETE",
|
|
@@ -2884,13 +2992,13 @@ async function runTenantDelete(slug, opts) {
|
|
|
2884
2992
|
}
|
|
2885
2993
|
async function runTenantCors(opts) {
|
|
2886
2994
|
if (!opts.tenant) {
|
|
2887
|
-
console.error(
|
|
2995
|
+
console.error(import_chalk24.default.red("--tenant <slug> is required."));
|
|
2888
2996
|
process.exit(1);
|
|
2889
2997
|
}
|
|
2890
2998
|
const { teamId } = await resolveTeam(opts.team);
|
|
2891
2999
|
const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
2892
3000
|
const cors = origins.length ? { allowed_origins: origins } : null;
|
|
2893
|
-
const spinner = (0,
|
|
3001
|
+
const spinner = (0, import_ora10.default)("Updating CORS...").start();
|
|
2894
3002
|
try {
|
|
2895
3003
|
await admin({
|
|
2896
3004
|
method: "PUT",
|
|
@@ -2906,8 +3014,8 @@ async function runTenantCors(opts) {
|
|
|
2906
3014
|
}
|
|
2907
3015
|
|
|
2908
3016
|
// src/commands/tenant-drill.ts
|
|
2909
|
-
var
|
|
2910
|
-
var
|
|
3017
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
3018
|
+
var import_ora11 = __toESM(require("ora"));
|
|
2911
3019
|
var import_crypto = require("crypto");
|
|
2912
3020
|
init_admin();
|
|
2913
3021
|
init_auth();
|
|
@@ -2936,17 +3044,17 @@ async function validScopedTenant(teamId, query) {
|
|
|
2936
3044
|
const next = { ...creds };
|
|
2937
3045
|
delete next.activeTenant;
|
|
2938
3046
|
saveCredentials2(next);
|
|
2939
|
-
console.log(
|
|
3047
|
+
console.log(import_chalk25.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
|
|
2940
3048
|
return void 0;
|
|
2941
3049
|
}
|
|
2942
3050
|
async function tenantHome(teamId, tenant2) {
|
|
2943
3051
|
const { default: inquirer2 } = await import("inquirer");
|
|
2944
3052
|
const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
2945
|
-
console.log(
|
|
3053
|
+
console.log(import_chalk25.default.bold(`
|
|
2946
3054
|
Tenant ${tenant2}`));
|
|
2947
|
-
console.log(
|
|
3055
|
+
console.log(import_chalk25.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
2948
3056
|
for (; ; ) {
|
|
2949
|
-
const spinner = (0,
|
|
3057
|
+
const spinner = (0, import_ora11.default)("Reading tenant state...").start();
|
|
2950
3058
|
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
2951
3059
|
admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
|
|
2952
3060
|
admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
|
|
@@ -2958,32 +3066,33 @@ Tenant ${tenant2}`));
|
|
|
2958
3066
|
const nEmails = (emails?.admin_emails ?? []).length;
|
|
2959
3067
|
const nIssuers = (issuers?.external_issuers ?? []).length;
|
|
2960
3068
|
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
2961
|
-
const
|
|
3069
|
+
const nOpaque = opaque?.opaque?.endpoint ? 1 : 0;
|
|
3070
|
+
const nLogin = nClients + nIssuers + nOpaque;
|
|
3071
|
+
const onOff = (b) => b ? import_chalk25.default.green("on") : import_chalk25.default.dim("off");
|
|
2962
3072
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
2963
3073
|
type: "list",
|
|
2964
3074
|
name: "pick",
|
|
2965
3075
|
message: `Tenant ${tenant2}:`,
|
|
2966
3076
|
pageSize: 12,
|
|
2967
3077
|
choices: [
|
|
2968
|
-
{ name: `
|
|
2969
|
-
{ name: `
|
|
2970
|
-
{ name: `
|
|
2971
|
-
|
|
2972
|
-
{ name: `
|
|
2973
|
-
{ name: `Opaque-token validator: ${opaque?.opaque?.endpoint ? import_chalk24.default.cyan(opaque.opaque.endpoint) : import_chalk24.default.dim("(unset)")}`, value: "opaque" },
|
|
3078
|
+
{ name: `Login methods (${nLogin}) ${import_chalk25.default.dim("how your consumers sign in")}`, value: "login" },
|
|
3079
|
+
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk25.default.dim(`identity & key management (${tenant2}.iam.apiblaze.com)`)}`, value: "iam" },
|
|
3080
|
+
{ name: `Portal admins (${nEmails}) ${import_chalk25.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
|
|
3081
|
+
new inquirer2.Separator(import_chalk25.default.dim(" Settings")),
|
|
3082
|
+
{ name: `CORS: ${cors?.cors ? import_chalk25.default.cyan(JSON.stringify(cors.cors)) : import_chalk25.default.dim("(unset)")} ${import_chalk25.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
|
|
2974
3083
|
{ name: "\u2190 Back", value: "back" }
|
|
2975
3084
|
]
|
|
2976
3085
|
}]);
|
|
2977
3086
|
switch (pick2) {
|
|
2978
3087
|
case "back":
|
|
2979
3088
|
return;
|
|
2980
|
-
case "
|
|
2981
|
-
await
|
|
3089
|
+
case "login":
|
|
3090
|
+
await loginMethodsMenu(teamId, tenant2, base);
|
|
2982
3091
|
break;
|
|
2983
3092
|
case "iam": {
|
|
2984
|
-
const { v } = await inquirer2.prompt([{ type: "confirm", name: "v", message: "Enable
|
|
3093
|
+
const { v } = await inquirer2.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
|
|
2985
3094
|
await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
2986
|
-
console.log(
|
|
3095
|
+
console.log(import_chalk25.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
|
|
2987
3096
|
break;
|
|
2988
3097
|
}
|
|
2989
3098
|
case "cors": {
|
|
@@ -2996,48 +3105,98 @@ Tenant ${tenant2}`));
|
|
|
2996
3105
|
if (v === "") break;
|
|
2997
3106
|
const parsed = v === "null" ? null : safeJson(v);
|
|
2998
3107
|
if (parsed === void 0) {
|
|
2999
|
-
console.log(
|
|
3108
|
+
console.log(import_chalk25.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
3000
3109
|
break;
|
|
3001
3110
|
}
|
|
3002
3111
|
await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
3003
|
-
console.log(
|
|
3112
|
+
console.log(import_chalk25.default.green(" CORS updated."));
|
|
3004
3113
|
break;
|
|
3005
3114
|
}
|
|
3006
3115
|
case "emails":
|
|
3007
3116
|
await emailsMenu(base, emails?.admin_emails ?? []);
|
|
3008
3117
|
break;
|
|
3009
|
-
case "issuers":
|
|
3010
|
-
await issuersMenu(base, issuers?.external_issuers ?? []);
|
|
3011
|
-
break;
|
|
3012
|
-
case "opaque": {
|
|
3013
|
-
const cur = opaque?.opaque;
|
|
3014
|
-
const { mode } = await inquirer2.prompt([{
|
|
3015
|
-
type: "list",
|
|
3016
|
-
name: "mode",
|
|
3017
|
-
message: "Opaque-token validator:",
|
|
3018
|
-
choices: [
|
|
3019
|
-
{ name: cur ? "Replace it" : "Set one up", value: "set" },
|
|
3020
|
-
...cur ? [{ name: "Clear it", value: "clear" }] : [],
|
|
3021
|
-
{ name: "\u2190 Back", value: "back" }
|
|
3022
|
-
]
|
|
3023
|
-
}]);
|
|
3024
|
-
if (mode === "back") break;
|
|
3025
|
-
if (mode === "clear") {
|
|
3026
|
-
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
3027
|
-
console.log(import_chalk24.default.green(" Cleared."));
|
|
3028
|
-
break;
|
|
3029
|
-
}
|
|
3030
|
-
const a = await inquirer2.prompt([
|
|
3031
|
-
{ type: "input", name: "endpoint", message: "Introspection endpoint (https):", default: cur?.endpoint, validate: (s) => s.startsWith("https://") || "must be https" },
|
|
3032
|
-
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
3033
|
-
]);
|
|
3034
|
-
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
3035
|
-
console.log(import_chalk24.default.green(" Opaque validator set."));
|
|
3036
|
-
break;
|
|
3037
|
-
}
|
|
3038
3118
|
}
|
|
3039
3119
|
}
|
|
3040
3120
|
}
|
|
3121
|
+
async function loginMethodsMenu(teamId, tenant2, base) {
|
|
3122
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3123
|
+
for (; ; ) {
|
|
3124
|
+
const spinner = (0, import_ora11.default)("Loading login methods...").start();
|
|
3125
|
+
const [rawClients, rawIssuers, rawOpaque] = await Promise.all([
|
|
3126
|
+
admin({ method: "GET", path: `${base}/app-clients`, summary: "List APIblaze-hosted logins" }).catch(() => []),
|
|
3127
|
+
admin({ method: "GET", path: `${base}/external-issuers`, summary: "List your-own-JWT logins" }).catch(() => null),
|
|
3128
|
+
admin({ method: "GET", path: `${base}/opaque`, summary: "Read opaque login" }).catch(() => null)
|
|
3129
|
+
]).finally(() => spinner.stop());
|
|
3130
|
+
const appClients = Array.isArray(rawClients) ? rawClients : [];
|
|
3131
|
+
const issuers = rawIssuers?.external_issuers ?? [];
|
|
3132
|
+
const opaque = rawOpaque?.opaque ?? null;
|
|
3133
|
+
const choices = [];
|
|
3134
|
+
for (const c of appClients) {
|
|
3135
|
+
const nP = (c.providers ?? []).length || c.providers_count || 0;
|
|
3136
|
+
choices.push({ name: `${import_chalk25.default.cyan("APIblaze-hosted")} ${import_chalk25.default.bold(c.name ?? c.clientId)} ${import_chalk25.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk25.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
|
|
3137
|
+
}
|
|
3138
|
+
for (const i of issuers) {
|
|
3139
|
+
choices.push({ name: `${import_chalk25.default.cyan("Your own \u2014 JWT ")} ${import_chalk25.default.bold(i.iss)} ${import_chalk25.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
|
|
3140
|
+
}
|
|
3141
|
+
if (opaque?.endpoint) {
|
|
3142
|
+
choices.push({ name: `${import_chalk25.default.cyan("Your own \u2014 opaque")} ${import_chalk25.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
|
|
3143
|
+
}
|
|
3144
|
+
if (!choices.length) console.log(import_chalk25.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
|
|
3145
|
+
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3146
|
+
type: "list",
|
|
3147
|
+
name: "pick",
|
|
3148
|
+
message: `Login methods for ${tenant2}:`,
|
|
3149
|
+
pageSize: 15,
|
|
3150
|
+
choices: [
|
|
3151
|
+
...choices,
|
|
3152
|
+
new inquirer2.Separator(),
|
|
3153
|
+
{ name: "\uFF0B Add a login method\u2026", value: { kind: "add" } },
|
|
3154
|
+
{ name: "\u2190 Back", value: { kind: "back" } }
|
|
3155
|
+
]
|
|
3156
|
+
}]);
|
|
3157
|
+
if (pick2.kind === "back") return;
|
|
3158
|
+
if (pick2.kind === "add") {
|
|
3159
|
+
await addLoginMethod(teamId, tenant2, base);
|
|
3160
|
+
continue;
|
|
3161
|
+
}
|
|
3162
|
+
if (pick2.kind === "client") {
|
|
3163
|
+
await clientHome(base, pick2.item);
|
|
3164
|
+
continue;
|
|
3165
|
+
}
|
|
3166
|
+
if (pick2.kind === "issuer") {
|
|
3167
|
+
await issuerHome(base, pick2.item);
|
|
3168
|
+
continue;
|
|
3169
|
+
}
|
|
3170
|
+
if (pick2.kind === "opaque") {
|
|
3171
|
+
await opaqueHome(base, pick2.item);
|
|
3172
|
+
continue;
|
|
3173
|
+
}
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
async function addLoginMethod(teamId, tenant2, base) {
|
|
3177
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3178
|
+
const { kind } = await inquirer2.prompt([{
|
|
3179
|
+
type: "list",
|
|
3180
|
+
name: "kind",
|
|
3181
|
+
message: "How do people log in?",
|
|
3182
|
+
pageSize: 8,
|
|
3183
|
+
choices: [
|
|
3184
|
+
{ name: `APIblaze hosted login page ${import_chalk25.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
|
|
3185
|
+
{ name: `Your own hosted login \u2014 JWT ${import_chalk25.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
|
|
3186
|
+
{ name: `Your own login \u2014 opaque token ${import_chalk25.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
|
|
3187
|
+
{ name: "\u2190 Back", value: null }
|
|
3188
|
+
]
|
|
3189
|
+
}]);
|
|
3190
|
+
if (!kind) return;
|
|
3191
|
+
if (kind === "apiblaze") {
|
|
3192
|
+
const { addApiblazeHostedLogin: addApiblazeHostedLogin2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
|
|
3193
|
+
await addApiblazeHostedLogin2(teamId, tenant2);
|
|
3194
|
+
} else if (kind === "jwt") {
|
|
3195
|
+
await addIssuer(base);
|
|
3196
|
+
} else {
|
|
3197
|
+
await setOpaque(base, null);
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3041
3200
|
function safeJson(s) {
|
|
3042
3201
|
try {
|
|
3043
3202
|
return JSON.parse(s);
|
|
@@ -3048,8 +3207,8 @@ function safeJson(s) {
|
|
|
3048
3207
|
async function emailsMenu(base, emails) {
|
|
3049
3208
|
const { default: inquirer2 } = await import("inquirer");
|
|
3050
3209
|
console.log();
|
|
3051
|
-
if (!emails.length) console.log(
|
|
3052
|
-
for (const e of emails) console.log(` ${
|
|
3210
|
+
if (!emails.length) console.log(import_chalk25.default.dim(" No consumer-admin emails."));
|
|
3211
|
+
for (const e of emails) console.log(` ${import_chalk25.default.bold(e.email ?? e)} ${import_chalk25.default.dim(e.status ?? "")}`);
|
|
3053
3212
|
const { act } = await inquirer2.prompt([{
|
|
3054
3213
|
type: "list",
|
|
3055
3214
|
name: "act",
|
|
@@ -3064,7 +3223,7 @@ async function emailsMenu(base, emails) {
|
|
|
3064
3223
|
if (act === "add") {
|
|
3065
3224
|
const { email } = await inquirer2.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
3066
3225
|
await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
3067
|
-
console.log(
|
|
3226
|
+
console.log(import_chalk25.default.green(` ${email} added.`));
|
|
3068
3227
|
} else {
|
|
3069
3228
|
const { e } = await inquirer2.prompt([{
|
|
3070
3229
|
type: "list",
|
|
@@ -3074,108 +3233,94 @@ async function emailsMenu(base, emails) {
|
|
|
3074
3233
|
}]);
|
|
3075
3234
|
if (!e) return;
|
|
3076
3235
|
await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
3077
|
-
console.log(
|
|
3236
|
+
console.log(import_chalk25.default.green(` ${e} removed.`));
|
|
3078
3237
|
}
|
|
3079
3238
|
}
|
|
3080
|
-
async function
|
|
3239
|
+
async function addIssuer(base) {
|
|
3081
3240
|
const { default: inquirer2 } = await import("inquirer");
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3241
|
+
const a = await inquirer2.prompt([
|
|
3242
|
+
{ type: "input", name: "iss", message: "Issuer URL (iss):", validate: (s) => !!s.trim() || "required" },
|
|
3243
|
+
{ type: "input", name: "aud", message: "Audience (aud):", validate: (s) => !!s.trim() || "required" },
|
|
3244
|
+
{ type: "input", name: "jwks", message: "JWKS URL (empty = derive from issuer):" },
|
|
3245
|
+
{ type: "list", name: "sem", message: "Where is the end-user id?", choices: [
|
|
3246
|
+
{ name: "The token sub IS the end user (tenant-owned)", value: "tenant_owned" },
|
|
3247
|
+
{ name: "Extract it from a claim\u2026", value: "extract_from_claim" }
|
|
3248
|
+
] }
|
|
3249
|
+
]);
|
|
3250
|
+
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;
|
|
3251
|
+
await admin({
|
|
3252
|
+
method: "POST",
|
|
3253
|
+
path: `${base}/external-issuers`,
|
|
3254
|
+
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3255
|
+
summary: `Add external issuer ${a.iss.trim()}`
|
|
3256
|
+
});
|
|
3257
|
+
console.log(import_chalk25.default.green(" JWT login method saved."));
|
|
3258
|
+
}
|
|
3259
|
+
async function issuerHome(base, issuer) {
|
|
3260
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3261
|
+
console.log(`
|
|
3262
|
+
${import_chalk25.default.bold(issuer.iss)} ${import_chalk25.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
|
|
3085
3263
|
const { act } = await inquirer2.prompt([{
|
|
3086
3264
|
type: "list",
|
|
3087
3265
|
name: "act",
|
|
3088
|
-
message: "
|
|
3266
|
+
message: "This JWT login method:",
|
|
3089
3267
|
choices: [
|
|
3090
|
-
{ name: "
|
|
3091
|
-
|
|
3268
|
+
{ name: "Replace it (re-enter details)", value: "edit" },
|
|
3269
|
+
{ name: import_chalk25.default.red("Delete it"), value: "rm" },
|
|
3092
3270
|
{ name: "\u2190 Back", value: "back" }
|
|
3093
3271
|
]
|
|
3094
3272
|
}]);
|
|
3095
3273
|
if (act === "back") return;
|
|
3096
|
-
if (act === "
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
{ type: "input", name: "aud", message: "Audience (aud):", validate: (s) => !!s.trim() || "required" },
|
|
3100
|
-
{ type: "input", name: "jwks", message: "JWKS URL (empty = derive from issuer):" },
|
|
3101
|
-
{ type: "list", name: "sem", message: "Where is the end-user id?", choices: [
|
|
3102
|
-
{ name: "The token sub IS the end user (tenant-owned)", value: "tenant_owned" },
|
|
3103
|
-
{ name: "Extract it from a claim\u2026", value: "extract_from_claim" }
|
|
3104
|
-
] }
|
|
3105
|
-
]);
|
|
3106
|
-
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;
|
|
3107
|
-
await admin({
|
|
3108
|
-
method: "POST",
|
|
3109
|
-
path: `${base}/external-issuers`,
|
|
3110
|
-
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3111
|
-
summary: `Add external issuer ${a.iss.trim()}`
|
|
3112
|
-
});
|
|
3113
|
-
console.log(import_chalk24.default.green(" Issuer saved."));
|
|
3114
|
-
} else {
|
|
3115
|
-
const { i } = await inquirer2.prompt([{
|
|
3116
|
-
type: "list",
|
|
3117
|
-
name: "i",
|
|
3118
|
-
message: "Delete which issuer?",
|
|
3119
|
-
choices: [...issuers.map((x) => ({ name: `${x.iss} (aud=${x.aud})`, value: x })), { name: "\u2190 Back", value: null }]
|
|
3120
|
-
}]);
|
|
3121
|
-
if (!i) return;
|
|
3122
|
-
await admin({
|
|
3123
|
-
method: "DELETE",
|
|
3124
|
-
path: `${base}/external-issuers?iss=${encodeURIComponent(i.iss)}&aud=${encodeURIComponent(i.aud)}`,
|
|
3125
|
-
summary: `Delete issuer ${i.iss}`
|
|
3126
|
-
});
|
|
3127
|
-
console.log(import_chalk24.default.green(" Issuer deleted."));
|
|
3274
|
+
if (act === "edit") {
|
|
3275
|
+
await addIssuer(base);
|
|
3276
|
+
return;
|
|
3128
3277
|
}
|
|
3278
|
+
const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: `Delete the JWT login method for ${issuer.iss}? Consumers using it can no longer sign in.`, default: false }]);
|
|
3279
|
+
if (!sure) return;
|
|
3280
|
+
await admin({
|
|
3281
|
+
method: "DELETE",
|
|
3282
|
+
path: `${base}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
|
|
3283
|
+
summary: `Delete issuer ${issuer.iss}`
|
|
3284
|
+
});
|
|
3285
|
+
console.log(import_chalk25.default.green(" Deleted."));
|
|
3129
3286
|
}
|
|
3130
|
-
async function
|
|
3287
|
+
async function setOpaque(base, cur) {
|
|
3131
3288
|
const { default: inquirer2 } = await import("inquirer");
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
]);
|
|
3158
|
-
const created = await admin({
|
|
3159
|
-
method: "POST",
|
|
3160
|
-
path: `${base}/app-clients`,
|
|
3161
|
-
body: {
|
|
3162
|
-
name: a.name.trim(),
|
|
3163
|
-
...a.callbacks.trim() ? { authorizedCallbackUrls: parseList(a.callbacks) } : {}
|
|
3164
|
-
},
|
|
3165
|
-
summary: `Create app client "${a.name.trim()}"`
|
|
3166
|
-
});
|
|
3167
|
-
console.log(import_chalk24.default.green(` App client created${created?.clientId ? ` (${created.clientId})` : ""}.`));
|
|
3168
|
-
continue;
|
|
3169
|
-
}
|
|
3170
|
-
await clientHome(base, pick2);
|
|
3289
|
+
const a = await inquirer2.prompt([
|
|
3290
|
+
{ type: "input", name: "endpoint", message: "Introspection endpoint (https):", default: cur?.endpoint, validate: (s) => s.startsWith("https://") || "must be https" },
|
|
3291
|
+
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
3292
|
+
]);
|
|
3293
|
+
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
3294
|
+
console.log(import_chalk25.default.green(" Opaque login method set."));
|
|
3295
|
+
}
|
|
3296
|
+
async function opaqueHome(base, cur) {
|
|
3297
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
3298
|
+
console.log(`
|
|
3299
|
+
${import_chalk25.default.bold(cur.endpoint)} ${import_chalk25.default.dim(cur.method ?? "GET")}`);
|
|
3300
|
+
const { act } = await inquirer2.prompt([{
|
|
3301
|
+
type: "list",
|
|
3302
|
+
name: "act",
|
|
3303
|
+
message: "This opaque login method:",
|
|
3304
|
+
choices: [
|
|
3305
|
+
{ name: "Replace it", value: "edit" },
|
|
3306
|
+
{ name: import_chalk25.default.red("Delete it"), value: "rm" },
|
|
3307
|
+
{ name: "\u2190 Back", value: "back" }
|
|
3308
|
+
]
|
|
3309
|
+
}]);
|
|
3310
|
+
if (act === "back") return;
|
|
3311
|
+
if (act === "edit") {
|
|
3312
|
+
await setOpaque(base, cur);
|
|
3313
|
+
return;
|
|
3171
3314
|
}
|
|
3315
|
+
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
3316
|
+
console.log(import_chalk25.default.green(" Deleted."));
|
|
3172
3317
|
}
|
|
3173
3318
|
async function clientHome(base, summary) {
|
|
3174
3319
|
const { default: inquirer2 } = await import("inquirer");
|
|
3175
3320
|
const id = summary.clientId ?? summary.client_id;
|
|
3176
3321
|
const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
|
|
3177
3322
|
for (; ; ) {
|
|
3178
|
-
const spinner = (0,
|
|
3323
|
+
const spinner = (0, import_ora11.default)("Reading app client...").start();
|
|
3179
3324
|
const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
|
|
3180
3325
|
spinner.stop();
|
|
3181
3326
|
const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
|
|
@@ -3187,13 +3332,13 @@ async function clientHome(base, summary) {
|
|
|
3187
3332
|
message: `${c.name ?? id}:`,
|
|
3188
3333
|
pageSize: 12,
|
|
3189
3334
|
choices: [
|
|
3190
|
-
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${
|
|
3191
|
-
{ name: `Callback URLs: ${cb.length ?
|
|
3192
|
-
{ name: `Scopes: ${scopes.length ?
|
|
3335
|
+
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk25.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
|
|
3336
|
+
{ name: `Callback URLs: ${cb.length ? import_chalk25.default.cyan(cb.join(", ")) : import_chalk25.default.dim("(none)")}`, value: "callbacks" },
|
|
3337
|
+
{ name: `Scopes: ${scopes.length ? import_chalk25.default.cyan(scopes.join(" ")) : import_chalk25.default.dim("(defaults)")}`, value: "scopes" },
|
|
3193
3338
|
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
3194
3339
|
{ name: "Reveal client secret", value: "secret" },
|
|
3195
3340
|
{ name: "Rotate client secret", value: "rotate" },
|
|
3196
|
-
{ name:
|
|
3341
|
+
{ name: import_chalk25.default.red("Delete this app client"), value: "delete" },
|
|
3197
3342
|
{ name: "\u2190 Back", value: "back" }
|
|
3198
3343
|
]
|
|
3199
3344
|
}]);
|
|
@@ -3206,13 +3351,13 @@ async function clientHome(base, summary) {
|
|
|
3206
3351
|
case "callbacks": {
|
|
3207
3352
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
3208
3353
|
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
3209
|
-
console.log(
|
|
3354
|
+
console.log(import_chalk25.default.green(" Callbacks updated."));
|
|
3210
3355
|
break;
|
|
3211
3356
|
}
|
|
3212
3357
|
case "scopes": {
|
|
3213
3358
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
3214
3359
|
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
3215
|
-
console.log(
|
|
3360
|
+
console.log(import_chalk25.default.green(" Scopes updated."));
|
|
3216
3361
|
break;
|
|
3217
3362
|
}
|
|
3218
3363
|
case "expiries": {
|
|
@@ -3227,14 +3372,14 @@ async function clientHome(base, summary) {
|
|
|
3227
3372
|
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
3228
3373
|
summary: "Update token expiries"
|
|
3229
3374
|
});
|
|
3230
|
-
console.log(
|
|
3375
|
+
console.log(import_chalk25.default.green(" Expiries updated."));
|
|
3231
3376
|
break;
|
|
3232
3377
|
}
|
|
3233
3378
|
case "secret": {
|
|
3234
3379
|
const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
3235
3380
|
if (!sure) break;
|
|
3236
3381
|
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
3237
|
-
console.log(` ${
|
|
3382
|
+
console.log(` ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3238
3383
|
break;
|
|
3239
3384
|
}
|
|
3240
3385
|
case "rotate": {
|
|
@@ -3242,14 +3387,14 @@ async function clientHome(base, summary) {
|
|
|
3242
3387
|
if (!sure) break;
|
|
3243
3388
|
const fresh = randomSecret();
|
|
3244
3389
|
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
3245
|
-
console.log(` New ${
|
|
3390
|
+
console.log(` New ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(fresh)} ${import_chalk25.default.dim("(store it now)")}`);
|
|
3246
3391
|
break;
|
|
3247
3392
|
}
|
|
3248
3393
|
case "delete": {
|
|
3249
3394
|
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 }]);
|
|
3250
3395
|
if (!sure) break;
|
|
3251
3396
|
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
3252
|
-
console.log(
|
|
3397
|
+
console.log(import_chalk25.default.green(" App client deleted."));
|
|
3253
3398
|
return;
|
|
3254
3399
|
}
|
|
3255
3400
|
}
|
|
@@ -3261,7 +3406,7 @@ function randomSecret() {
|
|
|
3261
3406
|
return Buffer.from(bytes).toString("base64url");
|
|
3262
3407
|
}
|
|
3263
3408
|
var PROVIDER_TYPES = ["google", "github", "microsoft", "facebook", "auth0", "other"];
|
|
3264
|
-
var
|
|
3409
|
+
var DEFAULT_SCOPES2 = {
|
|
3265
3410
|
google: "openid email profile",
|
|
3266
3411
|
microsoft: "openid email profile",
|
|
3267
3412
|
github: "read:user user:email",
|
|
@@ -3270,19 +3415,19 @@ var DEFAULT_SCOPES = {
|
|
|
3270
3415
|
async function providersMenu(cBase, clientLabel) {
|
|
3271
3416
|
const { default: inquirer2 } = await import("inquirer");
|
|
3272
3417
|
for (; ; ) {
|
|
3273
|
-
const spinner = (0,
|
|
3418
|
+
const spinner = (0, import_ora11.default)("Loading providers...").start();
|
|
3274
3419
|
const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
|
|
3275
3420
|
spinner.stop();
|
|
3276
3421
|
const providers = Array.isArray(raw) ? raw : [];
|
|
3277
3422
|
console.log();
|
|
3278
3423
|
for (const p of providers) {
|
|
3279
|
-
console.log(` ${
|
|
3424
|
+
console.log(` ${import_chalk25.default.bold(p.type)} ${import_chalk25.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
|
|
3280
3425
|
}
|
|
3281
|
-
if (!providers.length) console.log(
|
|
3426
|
+
if (!providers.length) console.log(import_chalk25.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
|
|
3282
3427
|
const { act } = await inquirer2.prompt([{
|
|
3283
3428
|
type: "list",
|
|
3284
3429
|
name: "act",
|
|
3285
|
-
message: `
|
|
3430
|
+
message: `Sign-in providers for ${clientLabel}:`,
|
|
3286
3431
|
choices: [
|
|
3287
3432
|
{ name: "\uFF0B Add a provider", value: "add" },
|
|
3288
3433
|
...providers.length ? [
|
|
@@ -3310,7 +3455,7 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3310
3455
|
{ type: "input", name: "clientId", message: `${type} OAuth client id:`, validate: (s) => !!s.trim() || "required" },
|
|
3311
3456
|
{ type: "password", name: "clientSecret", mask: "*", message: `${type} OAuth client secret:`, validate: (s) => s.length >= 6 && s.length <= 200 || "6\u2013200 chars" },
|
|
3312
3457
|
...type === "auth0" || type === "other" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : [],
|
|
3313
|
-
{ type: "input", name: "scopes", message: "Scopes:", default:
|
|
3458
|
+
{ type: "input", name: "scopes", message: "Scopes:", default: DEFAULT_SCOPES2[type] ?? "" }
|
|
3314
3459
|
]);
|
|
3315
3460
|
body = {
|
|
3316
3461
|
type,
|
|
@@ -3336,23 +3481,23 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3336
3481
|
body.targetServerToken = routing;
|
|
3337
3482
|
}
|
|
3338
3483
|
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
3339
|
-
console.log(
|
|
3484
|
+
console.log(import_chalk25.default.green(` ${type} provider added.`));
|
|
3340
3485
|
} else {
|
|
3341
3486
|
const { p } = await inquirer2.prompt([{
|
|
3342
3487
|
type: "list",
|
|
3343
3488
|
name: "p",
|
|
3344
3489
|
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
3345
|
-
choices: [...providers.map((x) => ({ name: `${x.type} ${
|
|
3490
|
+
choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk25.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
|
|
3346
3491
|
}]);
|
|
3347
3492
|
if (!p) continue;
|
|
3348
3493
|
if (act === "rm") {
|
|
3349
3494
|
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 }]);
|
|
3350
3495
|
if (!sure) continue;
|
|
3351
3496
|
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
3352
|
-
console.log(
|
|
3497
|
+
console.log(import_chalk25.default.green(` ${p.type} removed.`));
|
|
3353
3498
|
} else {
|
|
3354
3499
|
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
3355
|
-
console.log(` ${
|
|
3500
|
+
console.log(` ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3356
3501
|
}
|
|
3357
3502
|
}
|
|
3358
3503
|
}
|
|
@@ -3360,8 +3505,8 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3360
3505
|
|
|
3361
3506
|
// src/commands/spec.ts
|
|
3362
3507
|
var fs6 = __toESM(require("fs"));
|
|
3363
|
-
var
|
|
3364
|
-
var
|
|
3508
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
3509
|
+
var import_ora12 = __toESM(require("ora"));
|
|
3365
3510
|
init_admin();
|
|
3366
3511
|
async function runSpecGet(project, opts) {
|
|
3367
3512
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3375,19 +3520,19 @@ async function runSpecGet(project, opts) {
|
|
|
3375
3520
|
}
|
|
3376
3521
|
async function runSpecSet(project, opts) {
|
|
3377
3522
|
if (!opts.file) {
|
|
3378
|
-
console.error(
|
|
3523
|
+
console.error(import_chalk26.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
3379
3524
|
process.exit(1);
|
|
3380
3525
|
}
|
|
3381
3526
|
let specContent;
|
|
3382
3527
|
try {
|
|
3383
3528
|
specContent = fs6.readFileSync(opts.file, "utf-8");
|
|
3384
3529
|
} catch {
|
|
3385
|
-
console.error(
|
|
3530
|
+
console.error(import_chalk26.default.red(`Cannot read file: ${opts.file}`));
|
|
3386
3531
|
process.exit(1);
|
|
3387
3532
|
}
|
|
3388
3533
|
const { teamId } = await resolveTeam(opts.team);
|
|
3389
3534
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
3390
|
-
const spinner = (0,
|
|
3535
|
+
const spinner = (0, import_ora12.default)("Uploading spec...").start();
|
|
3391
3536
|
try {
|
|
3392
3537
|
const out = await admin({
|
|
3393
3538
|
method: "POST",
|
|
@@ -3404,12 +3549,12 @@ async function runSpecSet(project, opts) {
|
|
|
3404
3549
|
}
|
|
3405
3550
|
|
|
3406
3551
|
// src/commands/agent.ts
|
|
3407
|
-
var
|
|
3408
|
-
var
|
|
3552
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
3553
|
+
var import_ora13 = __toESM(require("ora"));
|
|
3409
3554
|
init_auth();
|
|
3410
3555
|
|
|
3411
3556
|
// src/lib/tools.ts
|
|
3412
|
-
var
|
|
3557
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
3413
3558
|
init_admin();
|
|
3414
3559
|
init_api();
|
|
3415
3560
|
async function proj(teamId, name, version2) {
|
|
@@ -3428,15 +3573,15 @@ var TOOLS = [
|
|
|
3428
3573
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
3429
3574
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
3430
3575
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
3431
|
-
const lines = [` ${
|
|
3432
|
-
if (res.devPortal) lines.push(` ${
|
|
3576
|
+
const lines = [` ${import_chalk27.default.dim("Proxy URL:")} ${import_chalk27.default.bold(url)}`];
|
|
3577
|
+
if (res.devPortal) lines.push(` ${import_chalk27.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
3433
3578
|
const envs = Object.keys(keys);
|
|
3434
3579
|
if (envs.length) {
|
|
3435
|
-
lines.push("", ` ${
|
|
3580
|
+
lines.push("", ` ${import_chalk27.default.bold("API keys")} ${import_chalk27.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
3436
3581
|
const w = Math.max(...envs.map((e) => e.length));
|
|
3437
|
-
for (const env of envs) lines.push(` ${
|
|
3582
|
+
for (const env of envs) lines.push(` ${import_chalk27.default.cyan(env.padEnd(w))} ${import_chalk27.default.green(keys[env])}`);
|
|
3438
3583
|
}
|
|
3439
|
-
if (tryIt) lines.push("", ` ${
|
|
3584
|
+
if (tryIt) lines.push("", ` ${import_chalk27.default.dim("Try it:")}`, ` ${import_chalk27.default.cyan(tryIt)}`);
|
|
3440
3585
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
3441
3586
|
}
|
|
3442
3587
|
},
|
|
@@ -3592,23 +3737,23 @@ function truncate(value, max = 1500) {
|
|
|
3592
3737
|
}
|
|
3593
3738
|
function printCost(llm) {
|
|
3594
3739
|
const usd = llm.cost > 0 ? `$${llm.cost.toFixed(4)}` : "<$0.0001";
|
|
3595
|
-
console.log(
|
|
3740
|
+
console.log(import_chalk28.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk28.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
|
|
3596
3741
|
}
|
|
3597
3742
|
async function runAgent(opts) {
|
|
3598
3743
|
requireAuth();
|
|
3599
3744
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
3600
3745
|
const { default: inquirer2 } = await import("inquirer");
|
|
3601
|
-
console.log(
|
|
3602
|
-
console.log(
|
|
3746
|
+
console.log(import_chalk28.default.bold("APIblaze agent") + import_chalk28.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
3747
|
+
console.log(import_chalk28.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
3603
3748
|
const history = [];
|
|
3604
3749
|
while (true) {
|
|
3605
|
-
const { input } = await inquirer2.prompt([{ type: "input", name: "input", message:
|
|
3750
|
+
const { input } = await inquirer2.prompt([{ type: "input", name: "input", message: import_chalk28.default.cyan("you") + " \u203A" }]);
|
|
3606
3751
|
const text = (input ?? "").trim();
|
|
3607
3752
|
if (!text) continue;
|
|
3608
3753
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
3609
3754
|
history.push({ role: "user", content: text });
|
|
3610
3755
|
for (let step = 0; step < MAX_TOOL_STEPS; step++) {
|
|
3611
|
-
const spinner = (0,
|
|
3756
|
+
const spinner = (0, import_ora13.default)({ text: "thinking...", color: "magenta" }).start();
|
|
3612
3757
|
let resp;
|
|
3613
3758
|
try {
|
|
3614
3759
|
resp = await callAgent(history, teamId);
|
|
@@ -3616,21 +3761,21 @@ async function runAgent(opts) {
|
|
|
3616
3761
|
} catch (err) {
|
|
3617
3762
|
spinner.stop();
|
|
3618
3763
|
if (err instanceof ApiError && err.status === 402) {
|
|
3619
|
-
console.log(
|
|
3764
|
+
console.log(import_chalk28.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
3620
3765
|
break;
|
|
3621
3766
|
}
|
|
3622
3767
|
throw err;
|
|
3623
3768
|
}
|
|
3624
3769
|
history.push({ role: "assistant", content: resp.raw });
|
|
3625
3770
|
printCost(resp.llm);
|
|
3626
|
-
if (resp.reply) console.log(
|
|
3771
|
+
if (resp.reply) console.log(import_chalk28.default.green("agent") + " \u203A " + resp.reply);
|
|
3627
3772
|
if (!resp.action) break;
|
|
3628
3773
|
const tool = findTool(resp.action.tool);
|
|
3629
3774
|
if (!tool) {
|
|
3630
3775
|
history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
|
|
3631
3776
|
continue;
|
|
3632
3777
|
}
|
|
3633
|
-
const runSpinner = (0,
|
|
3778
|
+
const runSpinner = (0, import_ora13.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
|
|
3634
3779
|
try {
|
|
3635
3780
|
const result = await tool.run(resp.action.args, { teamId });
|
|
3636
3781
|
runSpinner.succeed(`${tool.name} \u2713`);
|
|
@@ -3648,11 +3793,11 @@ async function runAgent(opts) {
|
|
|
3648
3793
|
}
|
|
3649
3794
|
renderTrace();
|
|
3650
3795
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
3651
|
-
console.log(
|
|
3796
|
+
console.log(import_chalk28.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
3652
3797
|
}
|
|
3653
3798
|
}
|
|
3654
3799
|
}
|
|
3655
|
-
console.log(
|
|
3800
|
+
console.log(import_chalk28.default.dim("\nBye."));
|
|
3656
3801
|
}
|
|
3657
3802
|
|
|
3658
3803
|
// src/commands/config-browse.ts
|
|
@@ -3815,11 +3960,11 @@ function dig(blob, dotted) {
|
|
|
3815
3960
|
}
|
|
3816
3961
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
3817
3962
|
function show(v) {
|
|
3818
|
-
if (v === void 0) return
|
|
3819
|
-
if (v === null) return
|
|
3820
|
-
if (typeof v === "object") return
|
|
3821
|
-
if (typeof v === "boolean") return v ?
|
|
3822
|
-
return
|
|
3963
|
+
if (v === void 0) return import_chalk29.default.dim("(unset)");
|
|
3964
|
+
if (v === null) return import_chalk29.default.dim("null");
|
|
3965
|
+
if (typeof v === "object") return import_chalk29.default.cyan(JSON.stringify(v));
|
|
3966
|
+
if (typeof v === "boolean") return v ? import_chalk29.default.green("on") : import_chalk29.default.red("off");
|
|
3967
|
+
return import_chalk29.default.cyan(String(v));
|
|
3823
3968
|
}
|
|
3824
3969
|
function parseValue(raw) {
|
|
3825
3970
|
if (raw === "true") return true;
|
|
@@ -3846,7 +3991,7 @@ async function fetchConfigBlob(proj2) {
|
|
|
3846
3991
|
}
|
|
3847
3992
|
async function patchSetting(proj2, s, value, cfg) {
|
|
3848
3993
|
const body = s.toPatch(value, cfg);
|
|
3849
|
-
const spinner = (0,
|
|
3994
|
+
const spinner = (0, import_ora14.default)(`Set ${s.key}...`).start();
|
|
3850
3995
|
try {
|
|
3851
3996
|
await admin({
|
|
3852
3997
|
method: "PATCH",
|
|
@@ -3861,10 +4006,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
3861
4006
|
}
|
|
3862
4007
|
}
|
|
3863
4008
|
var loginFirst = (what) => {
|
|
3864
|
-
console.log(
|
|
4009
|
+
console.log(import_chalk29.default.yellow(`
|
|
3865
4010
|
Log in first to ${what}.`));
|
|
3866
|
-
console.log(
|
|
3867
|
-
console.log(
|
|
4011
|
+
console.log(import_chalk29.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
|
|
4012
|
+
console.log(import_chalk29.default.dim(" anonymously and want to bring it into your account.\n"));
|
|
3868
4013
|
};
|
|
3869
4014
|
async function runConfig(project, key, value, opts) {
|
|
3870
4015
|
const creds = loadCredentials();
|
|
@@ -3881,9 +4026,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
3881
4026
|
}
|
|
3882
4027
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
3883
4028
|
if (!setting) {
|
|
3884
|
-
console.error(
|
|
3885
|
-
console.error(
|
|
3886
|
-
console.error(
|
|
4029
|
+
console.error(import_chalk29.default.red(`Unknown setting "${key}".`));
|
|
4030
|
+
console.error(import_chalk29.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
|
|
4031
|
+
console.error(import_chalk29.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
|
|
3887
4032
|
process.exit(1);
|
|
3888
4033
|
}
|
|
3889
4034
|
if (value === void 0) {
|
|
@@ -3898,7 +4043,7 @@ async function pickProject(teamId) {
|
|
|
3898
4043
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
3899
4044
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
3900
4045
|
if (!projects.length) {
|
|
3901
|
-
console.error(
|
|
4046
|
+
console.error(import_chalk29.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
3902
4047
|
process.exit(1);
|
|
3903
4048
|
}
|
|
3904
4049
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -3906,7 +4051,7 @@ async function pickProject(teamId) {
|
|
|
3906
4051
|
type: "list",
|
|
3907
4052
|
name: "picked",
|
|
3908
4053
|
message: "Which project?",
|
|
3909
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
4054
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk29.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
3910
4055
|
}]);
|
|
3911
4056
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
3912
4057
|
}
|
|
@@ -3917,25 +4062,25 @@ function printAll(proj2, cfg, json) {
|
|
|
3917
4062
|
console.log(JSON.stringify(out, null, 2));
|
|
3918
4063
|
return;
|
|
3919
4064
|
}
|
|
3920
|
-
console.log(
|
|
4065
|
+
console.log(import_chalk29.default.bold(`
|
|
3921
4066
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
3922
4067
|
`));
|
|
3923
4068
|
for (const group of SETTING_GROUPS) {
|
|
3924
|
-
console.log(
|
|
4069
|
+
console.log(import_chalk29.default.bold(group));
|
|
3925
4070
|
for (const s of SETTINGS.filter((x) => x.group === group)) {
|
|
3926
|
-
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${
|
|
4071
|
+
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk29.default.dim(s.desc)}`);
|
|
3927
4072
|
}
|
|
3928
4073
|
console.log();
|
|
3929
4074
|
}
|
|
3930
|
-
console.log(
|
|
4075
|
+
console.log(import_chalk29.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
3931
4076
|
}
|
|
3932
4077
|
async function discoveryMenu(project) {
|
|
3933
4078
|
const { default: inquirer2 } = await import("inquirer");
|
|
3934
|
-
console.log(
|
|
4079
|
+
console.log(import_chalk29.default.bold(`
|
|
3935
4080
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
3936
4081
|
`));
|
|
3937
|
-
console.log(
|
|
3938
|
-
console.log(
|
|
4082
|
+
console.log(import_chalk29.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
|
|
4083
|
+
console.log(import_chalk29.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
|
|
3939
4084
|
for (; ; ) {
|
|
3940
4085
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3941
4086
|
type: "list",
|
|
@@ -3943,13 +4088,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
3943
4088
|
message: "Explore:",
|
|
3944
4089
|
pageSize: 20,
|
|
3945
4090
|
choices: [
|
|
3946
|
-
new inquirer2.Separator(
|
|
4091
|
+
new inquirer2.Separator(import_chalk29.default.bold("\u2014 Settings \u2014")),
|
|
3947
4092
|
...SETTING_GROUPS.map((g) => ({
|
|
3948
|
-
name: `${g} ${
|
|
4093
|
+
name: `${g} ${import_chalk29.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
3949
4094
|
value: { kind: "settings", g }
|
|
3950
4095
|
})),
|
|
3951
|
-
new inquirer2.Separator(
|
|
3952
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
4096
|
+
new inquirer2.Separator(import_chalk29.default.bold("\u2014 Features \u2014")),
|
|
4097
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk29.default.dim(f.desc)}`, value: { kind: "feature", f } })),
|
|
3953
4098
|
new inquirer2.Separator(),
|
|
3954
4099
|
{ name: "Exit", value: { kind: "exit" } }
|
|
3955
4100
|
]
|
|
@@ -3958,24 +4103,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
3958
4103
|
if (pick2.kind === "settings") {
|
|
3959
4104
|
console.log();
|
|
3960
4105
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
3961
|
-
console.log(` ${
|
|
3962
|
-
console.log(` ${
|
|
4106
|
+
console.log(` ${import_chalk29.default.bold(s.label.padEnd(28))} ${import_chalk29.default.dim(s.desc)}`);
|
|
4107
|
+
console.log(` ${import_chalk29.default.dim(" key: " + s.key)}`);
|
|
3963
4108
|
}
|
|
3964
4109
|
loginFirst("view or change these settings");
|
|
3965
4110
|
} else {
|
|
3966
4111
|
const f = pick2.f;
|
|
3967
4112
|
console.log(`
|
|
3968
|
-
${
|
|
4113
|
+
${import_chalk29.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
3969
4114
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
3970
4115
|
}
|
|
3971
4116
|
}
|
|
3972
4117
|
}
|
|
3973
4118
|
async function navigator(proj2, cfg, opts) {
|
|
3974
4119
|
const { default: inquirer2 } = await import("inquirer");
|
|
3975
|
-
console.log(
|
|
4120
|
+
console.log(import_chalk29.default.bold(`
|
|
3976
4121
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
3977
4122
|
`));
|
|
3978
|
-
console.log(
|
|
4123
|
+
console.log(import_chalk29.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
|
|
3979
4124
|
let blob = cfg;
|
|
3980
4125
|
for (; ; ) {
|
|
3981
4126
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
@@ -3984,10 +4129,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
3984
4129
|
message: "Where to?",
|
|
3985
4130
|
pageSize: 20,
|
|
3986
4131
|
choices: [
|
|
3987
|
-
new inquirer2.Separator(
|
|
4132
|
+
new inquirer2.Separator(import_chalk29.default.bold("\u2014 Settings \u2014")),
|
|
3988
4133
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
3989
|
-
new inquirer2.Separator(
|
|
3990
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
4134
|
+
new inquirer2.Separator(import_chalk29.default.bold("\u2014 Features \u2014")),
|
|
4135
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk29.default.dim(f.desc)}`, value: { kind: f.go } })),
|
|
3991
4136
|
new inquirer2.Separator(),
|
|
3992
4137
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
3993
4138
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -4027,7 +4172,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4027
4172
|
}
|
|
4028
4173
|
}
|
|
4029
4174
|
} catch (err) {
|
|
4030
|
-
console.error(
|
|
4175
|
+
console.error(import_chalk29.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
4031
4176
|
}
|
|
4032
4177
|
}
|
|
4033
4178
|
}
|
|
@@ -4041,7 +4186,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
4041
4186
|
message: group + ":",
|
|
4042
4187
|
pageSize: 16,
|
|
4043
4188
|
choices: [
|
|
4044
|
-
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${
|
|
4189
|
+
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk29.default.dim(s2.desc)}`, value: s2 })),
|
|
4045
4190
|
new inquirer2.Separator(),
|
|
4046
4191
|
{ name: "\u2190 Back", value: null }
|
|
4047
4192
|
]
|
|
@@ -4059,7 +4204,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
4059
4204
|
} else if (s.type === "number") {
|
|
4060
4205
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
4061
4206
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
4062
|
-
console.log(
|
|
4207
|
+
console.log(import_chalk29.default.yellow(" Not a number \u2014 unchanged."));
|
|
4063
4208
|
continue;
|
|
4064
4209
|
}
|
|
4065
4210
|
value = Number(v);
|
|
@@ -4140,7 +4285,7 @@ async function buildCondition(phase) {
|
|
|
4140
4285
|
const items = [];
|
|
4141
4286
|
for (; ; ) {
|
|
4142
4287
|
const a = await inquirer2.prompt([
|
|
4143
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
4288
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk29.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
4144
4289
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
4145
4290
|
"eq",
|
|
4146
4291
|
"neq",
|
|
@@ -4171,7 +4316,7 @@ async function buildCondition(phase) {
|
|
|
4171
4316
|
function showCondition(cond) {
|
|
4172
4317
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
4173
4318
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
4174
|
-
return
|
|
4319
|
+
return import_chalk29.default.dim(` when ${s}`);
|
|
4175
4320
|
}
|
|
4176
4321
|
async function transformsMenu(proj2) {
|
|
4177
4322
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -4180,12 +4325,12 @@ async function transformsMenu(proj2) {
|
|
|
4180
4325
|
const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
|
|
4181
4326
|
const rules = out?.rules ?? [];
|
|
4182
4327
|
console.log();
|
|
4183
|
-
if (!rules.length) console.log(
|
|
4328
|
+
if (!rules.length) console.log(import_chalk29.default.dim(" No transform rules yet."));
|
|
4184
4329
|
for (const r of rules) {
|
|
4185
4330
|
const a = r.action ?? {};
|
|
4186
4331
|
const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
|
|
4187
|
-
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 ?
|
|
4188
|
-
console.log(` ${r.enabled ?
|
|
4332
|
+
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_chalk29.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
|
|
4333
|
+
console.log(` ${r.enabled ? import_chalk29.default.green("\u25CF") : import_chalk29.default.dim("\u25CB")} ${import_chalk29.default.bold(r.name)} ${import_chalk29.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
|
|
4189
4334
|
}
|
|
4190
4335
|
const { act } = await inquirer2.prompt([{
|
|
4191
4336
|
type: "list",
|
|
@@ -4197,7 +4342,7 @@ async function transformsMenu(proj2) {
|
|
|
4197
4342
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
4198
4343
|
{ name: "Delete a rule", value: "delete" }
|
|
4199
4344
|
] : [],
|
|
4200
|
-
{ name:
|
|
4345
|
+
{ name: import_chalk29.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
4201
4346
|
{ name: "\u2190 Back", value: "back" }
|
|
4202
4347
|
]
|
|
4203
4348
|
}]);
|
|
@@ -4210,11 +4355,11 @@ async function transformsMenu(proj2) {
|
|
|
4210
4355
|
}]);
|
|
4211
4356
|
const body = parseValue(raw);
|
|
4212
4357
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
4213
|
-
console.log(
|
|
4358
|
+
console.log(import_chalk29.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
4214
4359
|
continue;
|
|
4215
4360
|
}
|
|
4216
4361
|
await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
|
|
4217
|
-
console.log(
|
|
4362
|
+
console.log(import_chalk29.default.green(" Rule created."));
|
|
4218
4363
|
continue;
|
|
4219
4364
|
}
|
|
4220
4365
|
if (act === "add") {
|
|
@@ -4230,7 +4375,7 @@ async function transformsMenu(proj2) {
|
|
|
4230
4375
|
{ name: "Remove a field", value: "remove" }
|
|
4231
4376
|
] }
|
|
4232
4377
|
]);
|
|
4233
|
-
const fieldHint =
|
|
4378
|
+
const fieldHint = import_chalk29.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
4234
4379
|
let action2;
|
|
4235
4380
|
if (ans.kind === "hardcode") {
|
|
4236
4381
|
const a = await inquirer2.prompt([
|
|
@@ -4261,7 +4406,7 @@ async function transformsMenu(proj2) {
|
|
|
4261
4406
|
};
|
|
4262
4407
|
}
|
|
4263
4408
|
const condition = await buildCondition(ans.phase);
|
|
4264
|
-
const spinner = (0,
|
|
4409
|
+
const spinner = (0, import_ora14.default)("Creating rule...").start();
|
|
4265
4410
|
try {
|
|
4266
4411
|
await admin({
|
|
4267
4412
|
method: "POST",
|
|
@@ -4279,16 +4424,16 @@ async function transformsMenu(proj2) {
|
|
|
4279
4424
|
type: "list",
|
|
4280
4425
|
name: "rule",
|
|
4281
4426
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
4282
|
-
choices: [...rules.map((r) => ({ name: `${r.name} ${
|
|
4427
|
+
choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk29.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
|
|
4283
4428
|
}]);
|
|
4284
4429
|
if (!rule) continue;
|
|
4285
4430
|
if (act === "toggle") {
|
|
4286
4431
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
4287
4432
|
await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
4288
|
-
console.log(
|
|
4433
|
+
console.log(import_chalk29.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
4289
4434
|
} else {
|
|
4290
4435
|
await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
4291
|
-
console.log(
|
|
4436
|
+
console.log(import_chalk29.default.green(` ${rule.name} deleted.`));
|
|
4292
4437
|
}
|
|
4293
4438
|
}
|
|
4294
4439
|
}
|
|
@@ -4300,9 +4445,9 @@ async function mappingsMenu(proj2) {
|
|
|
4300
4445
|
const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
|
|
4301
4446
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
4302
4447
|
console.log();
|
|
4303
|
-
if (!tables.length) console.log(
|
|
4448
|
+
if (!tables.length) console.log(import_chalk29.default.dim(" No mapping tables yet."));
|
|
4304
4449
|
for (const t of tables) {
|
|
4305
|
-
console.log(` ${
|
|
4450
|
+
console.log(` ${import_chalk29.default.bold(t.name)} ${import_chalk29.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
|
|
4306
4451
|
}
|
|
4307
4452
|
const { act } = await inquirer2.prompt([{
|
|
4308
4453
|
type: "list",
|
|
@@ -4322,11 +4467,11 @@ async function mappingsMenu(proj2) {
|
|
|
4322
4467
|
]);
|
|
4323
4468
|
const entries2 = parseValue(a.entries);
|
|
4324
4469
|
if (!Array.isArray(entries2)) {
|
|
4325
|
-
console.log(
|
|
4470
|
+
console.log(import_chalk29.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
4326
4471
|
continue;
|
|
4327
4472
|
}
|
|
4328
4473
|
await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
4329
|
-
console.log(
|
|
4474
|
+
console.log(import_chalk29.default.green(` Table "${a.name}" created.`));
|
|
4330
4475
|
} else {
|
|
4331
4476
|
const { table } = await inquirer2.prompt([{
|
|
4332
4477
|
type: "list",
|
|
@@ -4336,7 +4481,7 @@ async function mappingsMenu(proj2) {
|
|
|
4336
4481
|
}]);
|
|
4337
4482
|
if (!table) continue;
|
|
4338
4483
|
await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
4339
|
-
console.log(
|
|
4484
|
+
console.log(import_chalk29.default.green(` ${table.name} deleted.`));
|
|
4340
4485
|
}
|
|
4341
4486
|
}
|
|
4342
4487
|
}
|
|
@@ -4347,14 +4492,14 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4347
4492
|
const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
|
|
4348
4493
|
const tenants = out?.tenants ?? [];
|
|
4349
4494
|
console.log();
|
|
4350
|
-
if (!tenants.length) console.log(
|
|
4351
|
-
for (const t of tenants) console.log(` ${
|
|
4495
|
+
if (!tenants.length) console.log(import_chalk29.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
4496
|
+
for (const t of tenants) console.log(` ${import_chalk29.default.bold(t.tenant_name ?? t.name)} ${import_chalk29.default.dim(t.display_name ?? "")}`);
|
|
4352
4497
|
const { act } = await inquirer2.prompt([{
|
|
4353
4498
|
type: "list",
|
|
4354
4499
|
name: "act",
|
|
4355
4500
|
message: "Tenants:",
|
|
4356
4501
|
choices: [
|
|
4357
|
-
{ name: `Manage a tenant\u2026 ${
|
|
4502
|
+
{ name: `Manage a tenant\u2026 ${import_chalk29.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
|
|
4358
4503
|
{ name: "Attach a tenant to this project", value: "attach" },
|
|
4359
4504
|
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
4360
4505
|
{ name: "\u2190 Back", value: "back" }
|
|
@@ -4377,7 +4522,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4377
4522
|
}]);
|
|
4378
4523
|
if (!t) continue;
|
|
4379
4524
|
await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
4380
|
-
console.log(
|
|
4525
|
+
console.log(import_chalk29.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
4381
4526
|
}
|
|
4382
4527
|
}
|
|
4383
4528
|
}
|
|
@@ -4420,7 +4565,7 @@ async function specMenu(proj2, opts) {
|
|
|
4420
4565
|
choices: [
|
|
4421
4566
|
{ name: "Print the stored spec", value: "get" },
|
|
4422
4567
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
4423
|
-
{ name:
|
|
4568
|
+
{ name: import_chalk29.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
4424
4569
|
{ name: "\u2190 Back", value: "back" }
|
|
4425
4570
|
]
|
|
4426
4571
|
}]);
|
|
@@ -4428,7 +4573,7 @@ async function specMenu(proj2, opts) {
|
|
|
4428
4573
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
4429
4574
|
else if (act === "refresh") {
|
|
4430
4575
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
4431
|
-
console.log(
|
|
4576
|
+
console.log(import_chalk29.default.green(" Spec refresh triggered."));
|
|
4432
4577
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
4433
4578
|
}
|
|
4434
4579
|
async function agentsMenu(proj2, opts) {
|
|
@@ -4453,8 +4598,8 @@ async function agentsMenu(proj2, opts) {
|
|
|
4453
4598
|
}
|
|
4454
4599
|
|
|
4455
4600
|
// src/commands/key.ts
|
|
4456
|
-
var
|
|
4457
|
-
var
|
|
4601
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
4602
|
+
var import_ora15 = __toESM(require("ora"));
|
|
4458
4603
|
init_admin();
|
|
4459
4604
|
async function runApikeysMenu(opts) {
|
|
4460
4605
|
await runKeyList(opts);
|
|
@@ -4482,11 +4627,11 @@ async function runKeyList(opts) {
|
|
|
4482
4627
|
return;
|
|
4483
4628
|
}
|
|
4484
4629
|
if (!keys.length) {
|
|
4485
|
-
console.log(
|
|
4630
|
+
console.log(import_chalk30.default.yellow("No developer keys."));
|
|
4486
4631
|
return;
|
|
4487
4632
|
}
|
|
4488
4633
|
for (const k of keys) {
|
|
4489
|
-
console.log(` ${
|
|
4634
|
+
console.log(` ${import_chalk30.default.bold(k.key_id ?? k.id)} ${import_chalk30.default.dim(k.description ?? "")} ${import_chalk30.default.dim(k.expires_at ?? "no expiry")}`);
|
|
4490
4635
|
}
|
|
4491
4636
|
}
|
|
4492
4637
|
async function runKeyMint(opts) {
|
|
@@ -4494,7 +4639,7 @@ async function runKeyMint(opts) {
|
|
|
4494
4639
|
const body = { role: "consumer-admin" };
|
|
4495
4640
|
if (opts.desc) body.description = opts.desc;
|
|
4496
4641
|
if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
|
|
4497
|
-
const spinner = (0,
|
|
4642
|
+
const spinner = (0, import_ora15.default)("Minting key...").start();
|
|
4498
4643
|
try {
|
|
4499
4644
|
const out = await admin({
|
|
4500
4645
|
method: "POST",
|
|
@@ -4507,9 +4652,9 @@ async function runKeyMint(opts) {
|
|
|
4507
4652
|
console.log(JSON.stringify(out));
|
|
4508
4653
|
return;
|
|
4509
4654
|
}
|
|
4510
|
-
console.log(` ${
|
|
4511
|
-
console.log(` ${
|
|
4512
|
-
if (out?.expires_at) console.log(` ${
|
|
4655
|
+
console.log(` ${import_chalk30.default.bold("key_id")}: ${out?.key_id}`);
|
|
4656
|
+
console.log(` ${import_chalk30.default.bold("key")}: ${import_chalk30.default.green(out?.key)} ${import_chalk30.default.dim("(shown once \u2014 store it now)")}`);
|
|
4657
|
+
if (out?.expires_at) console.log(` ${import_chalk30.default.dim("expires:")} ${out.expires_at}`);
|
|
4513
4658
|
} catch (err) {
|
|
4514
4659
|
spinner.fail("Mint failed.");
|
|
4515
4660
|
throw err;
|
|
@@ -4517,7 +4662,7 @@ async function runKeyMint(opts) {
|
|
|
4517
4662
|
}
|
|
4518
4663
|
async function runKeyRevoke(keyId, opts) {
|
|
4519
4664
|
const { teamId } = await resolveTeam(opts.team);
|
|
4520
|
-
const spinner = (0,
|
|
4665
|
+
const spinner = (0, import_ora15.default)("Revoking key...").start();
|
|
4521
4666
|
try {
|
|
4522
4667
|
await admin({
|
|
4523
4668
|
method: "DELETE",
|
|
@@ -4532,8 +4677,8 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
4532
4677
|
}
|
|
4533
4678
|
|
|
4534
4679
|
// src/commands/consumer.ts
|
|
4535
|
-
var
|
|
4536
|
-
var
|
|
4680
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
4681
|
+
var import_ora16 = __toESM(require("ora"));
|
|
4537
4682
|
init_admin();
|
|
4538
4683
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
4539
4684
|
var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
|
|
@@ -4553,7 +4698,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
4553
4698
|
function requireConsumer() {
|
|
4554
4699
|
const c = loadConsumer();
|
|
4555
4700
|
if (!c) {
|
|
4556
|
-
console.error(
|
|
4701
|
+
console.error(import_chalk31.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
4557
4702
|
process.exit(1);
|
|
4558
4703
|
}
|
|
4559
4704
|
return c;
|
|
@@ -4564,7 +4709,7 @@ async function runConsumerLogin(opts) {
|
|
|
4564
4709
|
let clientId = opts.client;
|
|
4565
4710
|
if (clientId) {
|
|
4566
4711
|
if (!tenant2) {
|
|
4567
|
-
console.error(
|
|
4712
|
+
console.error(import_chalk31.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
4568
4713
|
process.exit(1);
|
|
4569
4714
|
}
|
|
4570
4715
|
} else {
|
|
@@ -4576,25 +4721,25 @@ async function runConsumerLogin(opts) {
|
|
|
4576
4721
|
if (!picked) process.exit(1);
|
|
4577
4722
|
tenant2 = picked;
|
|
4578
4723
|
}
|
|
4579
|
-
const s2 = (0,
|
|
4724
|
+
const s2 = (0, import_ora16.default)("Finding the login app...").start();
|
|
4580
4725
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
4581
4726
|
s2.stop();
|
|
4582
4727
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
4583
4728
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
4584
4729
|
if (!pick2) {
|
|
4585
|
-
console.error(
|
|
4730
|
+
console.error(import_chalk31.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
4586
4731
|
process.exit(1);
|
|
4587
4732
|
}
|
|
4588
4733
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
4589
4734
|
}
|
|
4590
4735
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
4591
|
-
console.log(`${
|
|
4736
|
+
console.log(`${import_chalk31.default.cyan("\u2192")} Logging in to ${import_chalk31.default.bold(tenant2)} as a consumer...`);
|
|
4592
4737
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
4593
4738
|
console.log(`
|
|
4594
|
-
Open: ${
|
|
4595
|
-
console.log(` Code: ${
|
|
4739
|
+
Open: ${import_chalk31.default.underline(verificationUri)}`);
|
|
4740
|
+
console.log(` Code: ${import_chalk31.default.bold(userCode)}
|
|
4596
4741
|
`);
|
|
4597
|
-
console.log(
|
|
4742
|
+
console.log(import_chalk31.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
4598
4743
|
}, portalResource);
|
|
4599
4744
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
4600
4745
|
const creds = {
|
|
@@ -4609,7 +4754,7 @@ async function runConsumerLogin(opts) {
|
|
|
4609
4754
|
obtainedAt: Date.now()
|
|
4610
4755
|
};
|
|
4611
4756
|
saveConsumer(creds);
|
|
4612
|
-
console.log(
|
|
4757
|
+
console.log(import_chalk31.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
4613
4758
|
}
|
|
4614
4759
|
async function runConsumerTokens(opts) {
|
|
4615
4760
|
const creds = requireConsumer();
|
|
@@ -4622,29 +4767,29 @@ async function runConsumerTokens(opts) {
|
|
|
4622
4767
|
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));
|
|
4623
4768
|
return;
|
|
4624
4769
|
}
|
|
4625
|
-
console.log(`${
|
|
4770
|
+
console.log(`${import_chalk31.default.cyan("Consumer")} ${import_chalk31.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk31.default.bold(fresh.tenant)}
|
|
4626
4771
|
`);
|
|
4627
|
-
console.log(`${
|
|
4772
|
+
console.log(`${import_chalk31.default.bold("access_token")} ${import_chalk31.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
4628
4773
|
${fresh.accessToken}
|
|
4629
4774
|
`);
|
|
4630
|
-
if (fresh.idToken) console.log(`${
|
|
4775
|
+
if (fresh.idToken) console.log(`${import_chalk31.default.bold("id_token")} ${import_chalk31.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
4631
4776
|
${fresh.idToken}
|
|
4632
4777
|
`);
|
|
4633
|
-
if (fresh.refreshToken) console.log(`${
|
|
4778
|
+
if (fresh.refreshToken) console.log(`${import_chalk31.default.bold("refresh_token")}
|
|
4634
4779
|
${fresh.refreshToken}
|
|
4635
4780
|
`);
|
|
4636
|
-
console.log(
|
|
4781
|
+
console.log(import_chalk31.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
4637
4782
|
}
|
|
4638
4783
|
async function runConsumerApikeys(opts) {
|
|
4639
4784
|
const creds = requireConsumer();
|
|
4640
4785
|
const { default: inquirer2 } = await import("inquirer");
|
|
4641
|
-
const spinner = (0,
|
|
4786
|
+
const spinner = (0, import_ora16.default)("Loading your API keys...").start();
|
|
4642
4787
|
const list = await consumerFetch(creds, "/apikeys");
|
|
4643
4788
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
4644
4789
|
spinner.stop();
|
|
4645
4790
|
if (list.status >= 400) {
|
|
4646
|
-
console.error(
|
|
4647
|
-
if (list.status === 401) console.error(
|
|
4791
|
+
console.error(import_chalk31.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
4792
|
+
if (list.status === 401) console.error(import_chalk31.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
4648
4793
|
process.exit(1);
|
|
4649
4794
|
}
|
|
4650
4795
|
const keys = list.data?.keys ?? [];
|
|
@@ -4652,16 +4797,16 @@ async function runConsumerApikeys(opts) {
|
|
|
4652
4797
|
if (opts.json) {
|
|
4653
4798
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
4654
4799
|
} else if (!keys.length) {
|
|
4655
|
-
console.log(
|
|
4800
|
+
console.log(import_chalk31.default.yellow("No API keys yet."));
|
|
4656
4801
|
} else {
|
|
4657
4802
|
for (const k of keys) {
|
|
4658
4803
|
const clear = revealMap[k.environment]?.key;
|
|
4659
|
-
const shown = clear ?
|
|
4660
|
-
const exp = k.expires_at ?
|
|
4661
|
-
console.log(` ${
|
|
4804
|
+
const shown = clear ? import_chalk31.default.green(clear) : import_chalk31.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
4805
|
+
const exp = k.expires_at ? import_chalk31.default.dim(`exp ${k.expires_at}`) : import_chalk31.default.dim("no expiry");
|
|
4806
|
+
console.log(` ${import_chalk31.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk31.default.dim(k.description ?? "")}`);
|
|
4662
4807
|
}
|
|
4663
4808
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
4664
|
-
console.log(
|
|
4809
|
+
console.log(import_chalk31.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
4665
4810
|
}
|
|
4666
4811
|
}
|
|
4667
4812
|
if (opts.json) return;
|
|
@@ -4675,7 +4820,7 @@ async function runConsumerApikeys(opts) {
|
|
|
4675
4820
|
const body = { environment: answers.environment };
|
|
4676
4821
|
if (answers.description) body.description = answers.description;
|
|
4677
4822
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
4678
|
-
const s2 = (0,
|
|
4823
|
+
const s2 = (0, import_ora16.default)("Creating key...").start();
|
|
4679
4824
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
4680
4825
|
if (created.status >= 400) {
|
|
4681
4826
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -4683,13 +4828,13 @@ async function runConsumerApikeys(opts) {
|
|
|
4683
4828
|
}
|
|
4684
4829
|
s2.succeed("Key created.");
|
|
4685
4830
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
4686
|
-
if (key) console.log(` ${
|
|
4687
|
-
else console.log(
|
|
4831
|
+
if (key) console.log(` ${import_chalk31.default.green(key)} ${import_chalk31.default.dim("(shown once \u2014 store it now)")}`);
|
|
4832
|
+
else console.log(import_chalk31.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
4688
4833
|
}
|
|
4689
4834
|
|
|
4690
4835
|
// src/commands/sidecar.ts
|
|
4691
|
-
var
|
|
4692
|
-
var
|
|
4836
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
4837
|
+
var import_ora17 = __toESM(require("ora"));
|
|
4693
4838
|
var fs7 = __toESM(require("fs"));
|
|
4694
4839
|
var path4 = __toESM(require("path"));
|
|
4695
4840
|
init_admin();
|
|
@@ -4730,18 +4875,18 @@ function upsertEnvLocal(root, token) {
|
|
|
4730
4875
|
}
|
|
4731
4876
|
function installSidecarPackage(root) {
|
|
4732
4877
|
if (fs7.existsSync(path4.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
4733
|
-
console.log(` ${
|
|
4878
|
+
console.log(` ${import_chalk32.default.green("\u2713")} apiblaze package already installed`);
|
|
4734
4879
|
return;
|
|
4735
4880
|
}
|
|
4736
4881
|
const has = (f) => fs7.existsSync(path4.join(root, f));
|
|
4737
4882
|
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" };
|
|
4738
|
-
const spinner = (0,
|
|
4883
|
+
const spinner = (0, import_ora17.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
4739
4884
|
try {
|
|
4740
4885
|
const { execSync } = require("child_process");
|
|
4741
4886
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
4742
4887
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
4743
4888
|
} catch {
|
|
4744
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
4889
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk32.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk32.default.cyan("npm run dev")}.`);
|
|
4745
4890
|
}
|
|
4746
4891
|
}
|
|
4747
4892
|
function readEnvKey(root) {
|
|
@@ -4880,7 +5025,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
4880
5025
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
4881
5026
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
4882
5027
|
if (opts.newSession) clearAnonCred2();
|
|
4883
|
-
const spinner = (0,
|
|
5028
|
+
const spinner = (0, import_ora17.default)("Setting up a sidecar (no login needed)...").start();
|
|
4884
5029
|
let out;
|
|
4885
5030
|
try {
|
|
4886
5031
|
out = await sidecarInitAnonymous2();
|
|
@@ -4892,29 +5037,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
4892
5037
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
4893
5038
|
const envState = upsertEnvLocal(root, out.token);
|
|
4894
5039
|
ensureGitignored(root);
|
|
4895
|
-
console.log(` ${
|
|
4896
|
-
console.log(` ${
|
|
5040
|
+
console.log(` ${import_chalk32.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
5041
|
+
console.log(` ${import_chalk32.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
4897
5042
|
installSidecarPackage(root);
|
|
4898
5043
|
let inspectorPath = null;
|
|
4899
5044
|
if (!opts.noInspector) {
|
|
4900
5045
|
inspectorPath = generateInspector(root, router);
|
|
4901
|
-
if (inspectorPath) console.log(` ${
|
|
5046
|
+
if (inspectorPath) console.log(` ${import_chalk32.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
4902
5047
|
}
|
|
4903
5048
|
console.log("");
|
|
4904
|
-
console.log(
|
|
4905
|
-
console.log(` 1. ${
|
|
5049
|
+
console.log(import_chalk32.default.bold("Done (no account needed). What happens next:"));
|
|
5050
|
+
console.log(` 1. ${import_chalk32.default.cyan("npm run dev")} and use your app.`);
|
|
4906
5051
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
4907
|
-
console.log(` ${
|
|
5052
|
+
console.log(` ${import_chalk32.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
4908
5053
|
console.log("");
|
|
4909
|
-
console.log(
|
|
4910
|
-
console.log(` ${
|
|
4911
|
-
console.log(
|
|
5054
|
+
console.log(import_chalk32.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
5055
|
+
console.log(` ${import_chalk32.default.cyan("apiblaze login")} then ${import_chalk32.default.cyan("apiblaze claim")} ${import_chalk32.default.dim("(no code needed here)")}`);
|
|
5056
|
+
console.log(import_chalk32.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
4912
5057
|
}
|
|
4913
5058
|
async function runSidecar(opts) {
|
|
4914
5059
|
const root = path4.resolve(opts.dir ?? process.cwd());
|
|
4915
5060
|
const detected = detectNextProject(root);
|
|
4916
5061
|
if (!detected.found) {
|
|
4917
|
-
console.log(
|
|
5062
|
+
console.log(import_chalk32.default.yellow(`No Next.js project detected in ${root}.`));
|
|
4918
5063
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
4919
5064
|
return;
|
|
4920
5065
|
}
|
|
@@ -4925,10 +5070,10 @@ async function runSidecar(opts) {
|
|
|
4925
5070
|
if (!loadCredentials()) {
|
|
4926
5071
|
upsertEnvLocal(root, readEnvKey(root));
|
|
4927
5072
|
ensureGitignored(root);
|
|
4928
|
-
console.log(` ${
|
|
4929
|
-
console.log(` ${
|
|
5073
|
+
console.log(` ${import_chalk32.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
5074
|
+
console.log(` ${import_chalk32.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
4930
5075
|
installSidecarPackage(root);
|
|
4931
|
-
console.log(
|
|
5076
|
+
console.log(import_chalk32.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
4932
5077
|
return;
|
|
4933
5078
|
}
|
|
4934
5079
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -4937,7 +5082,7 @@ async function runSidecar(opts) {
|
|
|
4937
5082
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
4938
5083
|
let token = existingKey ?? "";
|
|
4939
5084
|
if (mustMint) {
|
|
4940
|
-
const spinner = (0,
|
|
5085
|
+
const spinner = (0, import_ora17.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
|
|
4941
5086
|
try {
|
|
4942
5087
|
const out = await admin({
|
|
4943
5088
|
method: "POST",
|
|
@@ -4951,39 +5096,39 @@ async function runSidecar(opts) {
|
|
|
4951
5096
|
throw err;
|
|
4952
5097
|
}
|
|
4953
5098
|
} else {
|
|
4954
|
-
console.log(
|
|
5099
|
+
console.log(import_chalk32.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
4955
5100
|
}
|
|
4956
5101
|
const envState = upsertEnvLocal(root, token);
|
|
4957
5102
|
ensureGitignored(root);
|
|
4958
|
-
console.log(` ${
|
|
5103
|
+
console.log(` ${import_chalk32.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
4959
5104
|
const wireState = wireInstrumentation(root);
|
|
4960
|
-
console.log(` ${
|
|
5105
|
+
console.log(` ${import_chalk32.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
4961
5106
|
installSidecarPackage(root);
|
|
4962
5107
|
let inspectorPath = null;
|
|
4963
5108
|
if (!opts.noInspector) {
|
|
4964
5109
|
inspectorPath = generateInspector(root, detected.router);
|
|
4965
|
-
if (inspectorPath) console.log(` ${
|
|
5110
|
+
if (inspectorPath) console.log(` ${import_chalk32.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
4966
5111
|
}
|
|
4967
5112
|
console.log("");
|
|
4968
|
-
console.log(
|
|
4969
|
-
console.log(` 1. ${
|
|
4970
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
4971
|
-
console.log(` 3. Approve the ones to route: ${
|
|
5113
|
+
console.log(import_chalk32.default.bold("Done. What happens next:"));
|
|
5114
|
+
console.log(` 1. ${import_chalk32.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
5115
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk32.default.bold("candidates")} \u2014 list them: ${import_chalk32.default.cyan("apiblaze sidecar")}`);
|
|
5116
|
+
console.log(` 3. Approve the ones to route: ${import_chalk32.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
4972
5117
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
4973
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
4974
|
-
if (switchingTeam) console.log(
|
|
5118
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk32.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path4.dirname(inspectorPath)} before shipping)`);
|
|
5119
|
+
if (switchingTeam) console.log(import_chalk32.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
4975
5120
|
console.log("");
|
|
4976
|
-
console.log(
|
|
4977
|
-
console.log(
|
|
4978
|
-
console.log(
|
|
5121
|
+
console.log(import_chalk32.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
5122
|
+
console.log(import_chalk32.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
5123
|
+
console.log(import_chalk32.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
4979
5124
|
console.log("");
|
|
4980
|
-
console.log(
|
|
4981
|
-
console.log(
|
|
5125
|
+
console.log(import_chalk32.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
5126
|
+
console.log(import_chalk32.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
4982
5127
|
}
|
|
4983
5128
|
|
|
4984
5129
|
// src/commands/origins.ts
|
|
4985
|
-
var
|
|
4986
|
-
var
|
|
5130
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
5131
|
+
var import_ora18 = __toESM(require("ora"));
|
|
4987
5132
|
init_admin();
|
|
4988
5133
|
init_auth();
|
|
4989
5134
|
init_anon_cred();
|
|
@@ -4992,7 +5137,7 @@ async function runOriginsList(opts) {
|
|
|
4992
5137
|
if (!loadCredentials()) {
|
|
4993
5138
|
const cred = loadAnonCred();
|
|
4994
5139
|
if (!cred) {
|
|
4995
|
-
console.log(
|
|
5140
|
+
console.log(import_chalk33.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
4996
5141
|
return;
|
|
4997
5142
|
}
|
|
4998
5143
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -5010,30 +5155,30 @@ async function runOriginsList(opts) {
|
|
|
5010
5155
|
}
|
|
5011
5156
|
const routed = out.routed ?? [];
|
|
5012
5157
|
const candidates = out.candidates ?? [];
|
|
5013
|
-
console.log(
|
|
5158
|
+
console.log(import_chalk33.default.bold(`
|
|
5014
5159
|
Routed through APIblaze (${routed.length})`));
|
|
5015
|
-
if (!routed.length) console.log(
|
|
5016
|
-
for (const r of routed) console.log(` ${
|
|
5017
|
-
console.log(
|
|
5160
|
+
if (!routed.length) console.log(import_chalk33.default.dim(" none yet"));
|
|
5161
|
+
for (const r of routed) console.log(` ${import_chalk33.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk33.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
5162
|
+
console.log(import_chalk33.default.bold(`
|
|
5018
5163
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
5019
|
-
if (!candidates.length) console.log(
|
|
5164
|
+
if (!candidates.length) console.log(import_chalk33.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
5020
5165
|
for (const c of candidates) {
|
|
5021
|
-
console.log(` ${
|
|
5166
|
+
console.log(` ${import_chalk33.default.yellow("\u25CB")} ${c.origin} ${import_chalk33.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
5022
5167
|
}
|
|
5023
5168
|
if (candidates.length) {
|
|
5024
|
-
console.log(
|
|
5169
|
+
console.log(import_chalk33.default.dim(`
|
|
5025
5170
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
5026
|
-
console.log(
|
|
5171
|
+
console.log(import_chalk33.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
5027
5172
|
}
|
|
5028
5173
|
}
|
|
5029
5174
|
async function runOriginsApprove(origin, opts) {
|
|
5030
5175
|
if (!loadCredentials()) {
|
|
5031
5176
|
const cred = loadAnonCred();
|
|
5032
5177
|
if (!cred) {
|
|
5033
|
-
console.error(
|
|
5178
|
+
console.error(import_chalk33.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
5034
5179
|
process.exit(1);
|
|
5035
5180
|
}
|
|
5036
|
-
const spinner2 = (0,
|
|
5181
|
+
const spinner2 = (0, import_ora18.default)(`Approving ${origin} (anonymous)...`).start();
|
|
5037
5182
|
try {
|
|
5038
5183
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
5039
5184
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -5044,7 +5189,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
5044
5189
|
return;
|
|
5045
5190
|
}
|
|
5046
5191
|
const { teamId } = await resolveTeam(opts.team);
|
|
5047
|
-
const spinner = (0,
|
|
5192
|
+
const spinner = (0, import_ora18.default)(`Approving ${origin}...`).start();
|
|
5048
5193
|
try {
|
|
5049
5194
|
const out = await admin({
|
|
5050
5195
|
method: "POST",
|
|
@@ -5061,7 +5206,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
5061
5206
|
}
|
|
5062
5207
|
async function runOriginsDeny(origin, opts) {
|
|
5063
5208
|
const { teamId } = await resolveTeam(opts.team);
|
|
5064
|
-
const spinner = (0,
|
|
5209
|
+
const spinner = (0, import_ora18.default)(`Dismissing ${origin}...`).start();
|
|
5065
5210
|
try {
|
|
5066
5211
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
5067
5212
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -5072,7 +5217,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
5072
5217
|
}
|
|
5073
5218
|
async function runOriginsRemove(origin, opts) {
|
|
5074
5219
|
const { teamId } = await resolveTeam(opts.team);
|
|
5075
|
-
const spinner = (0,
|
|
5220
|
+
const spinner = (0, import_ora18.default)(`Removing the proxy for ${origin}...`).start();
|
|
5076
5221
|
try {
|
|
5077
5222
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
5078
5223
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -5083,7 +5228,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
5083
5228
|
}
|
|
5084
5229
|
|
|
5085
5230
|
// src/commands/op.ts
|
|
5086
|
-
var
|
|
5231
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
5087
5232
|
init_auth();
|
|
5088
5233
|
init_trace();
|
|
5089
5234
|
init_types();
|
|
@@ -5116,82 +5261,82 @@ function printResidue(report, applied) {
|
|
|
5116
5261
|
const up = report?.upstash ?? {};
|
|
5117
5262
|
const fga = report?.fga ?? {};
|
|
5118
5263
|
const ghosts = report?.ghosts ?? {};
|
|
5119
|
-
console.log(
|
|
5120
|
-
console.log(
|
|
5264
|
+
console.log(import_chalk34.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
5265
|
+
console.log(import_chalk34.default.bold("\n Upstash"));
|
|
5121
5266
|
const orphans = up.orphans ?? [];
|
|
5122
|
-
if (orphans.length === 0) console.log(
|
|
5123
|
-
for (const o of orphans) console.log(` ${
|
|
5124
|
-
console.log(
|
|
5267
|
+
if (orphans.length === 0) console.log(import_chalk34.default.green(" no orphaned keys"));
|
|
5268
|
+
for (const o of orphans) console.log(` ${import_chalk34.default.yellow(o.key)} ${import_chalk34.default.dim(`\u2014 ${o.reason}`)}`);
|
|
5269
|
+
console.log(import_chalk34.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
5125
5270
|
if (up.anon_wallet_detail) {
|
|
5126
5271
|
const d = up.anon_wallet_detail;
|
|
5127
|
-
console.log(
|
|
5272
|
+
console.log(import_chalk34.default.dim(` anon wallets: ${d.count} ($${(d.total_cents / 100).toFixed(2)}), ${d.no_ttl} with NO TTL${d.no_ttl ? " \u26A0" : " (all self-expire)"}`));
|
|
5128
5273
|
}
|
|
5129
5274
|
if (up.keyspace_census) {
|
|
5130
5275
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
5131
|
-
console.log(
|
|
5276
|
+
console.log(import_chalk34.default.dim(` keyspace: ${census}`));
|
|
5132
5277
|
}
|
|
5133
|
-
if (up.unknown?.length) console.log(
|
|
5134
|
-
if (applied) console.log(` ${
|
|
5135
|
-
for (const e of up.errors ?? []) console.log(
|
|
5136
|
-
console.log(
|
|
5278
|
+
if (up.unknown?.length) console.log(import_chalk34.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
5279
|
+
if (applied) console.log(` ${import_chalk34.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
5280
|
+
for (const e of up.errors ?? []) console.log(import_chalk34.default.red(` error: ${e}`));
|
|
5281
|
+
console.log(import_chalk34.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
5137
5282
|
if (applied) {
|
|
5138
5283
|
const swept = fga?.swept ?? [];
|
|
5139
|
-
if (swept.length === 0) console.log(
|
|
5284
|
+
if (swept.length === 0) console.log(import_chalk34.default.green(" no orphaned stores"));
|
|
5140
5285
|
for (const s of swept) {
|
|
5141
5286
|
console.log(
|
|
5142
|
-
` ${
|
|
5287
|
+
` ${import_chalk34.default.yellow(s.store_id)} ${import_chalk34.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
5143
5288
|
);
|
|
5144
5289
|
}
|
|
5145
|
-
if (fga?.remaining) console.log(
|
|
5290
|
+
if (fga?.remaining) console.log(import_chalk34.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
5146
5291
|
const st = fga?.side_tables;
|
|
5147
|
-
if (st) console.log(
|
|
5292
|
+
if (st) console.log(import_chalk34.default.dim(` Neon side-tables purged: ${st.soft_deleted_stores} store records, ${st.orphan_models} models, ${st.orphan_changelog} changelog rows${st.error ? ` (${st.error})` : ""}`));
|
|
5148
5293
|
} else {
|
|
5149
5294
|
const fgaOrphans = fga?.orphans ?? [];
|
|
5150
|
-
if (fgaOrphans.length === 0) console.log(
|
|
5295
|
+
if (fgaOrphans.length === 0) console.log(import_chalk34.default.green(" no orphaned stores"));
|
|
5151
5296
|
for (const s of fgaOrphans) {
|
|
5152
5297
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
5153
|
-
console.log(` ${
|
|
5298
|
+
console.log(` ${import_chalk34.default.yellow(s.store_id)} ${import_chalk34.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
5154
5299
|
}
|
|
5155
|
-
console.log(
|
|
5300
|
+
console.log(import_chalk34.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
5156
5301
|
const st = fga?.side_tables;
|
|
5157
|
-
if (st) console.log(
|
|
5302
|
+
if (st) console.log(import_chalk34.default.dim(` Neon side-table residue: ${st.soft_deleted_stores} soft-deleted store records, ${st.orphan_models} orphan models, ${st.orphan_changelog} orphan changelog rows`));
|
|
5158
5303
|
}
|
|
5159
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
5160
|
-
console.log(
|
|
5304
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk34.default.red(` error: ${e}`));
|
|
5305
|
+
console.log(import_chalk34.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
5161
5306
|
if (applied) {
|
|
5162
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
5163
|
-
else console.log(` ${
|
|
5307
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk34.default.green(" no ghost tuples"));
|
|
5308
|
+
else console.log(` ${import_chalk34.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk34.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
5164
5309
|
} else {
|
|
5165
5310
|
const n = ghosts?.ghost_count ?? 0;
|
|
5166
|
-
if (n === 0) console.log(
|
|
5311
|
+
if (n === 0) console.log(import_chalk34.default.green(` no ghost tuples ${import_chalk34.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
5167
5312
|
else {
|
|
5168
|
-
console.log(
|
|
5313
|
+
console.log(import_chalk34.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
5169
5314
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
5170
|
-
console.log(
|
|
5315
|
+
console.log(import_chalk34.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
5171
5316
|
}
|
|
5172
|
-
if (n > 20) console.log(
|
|
5317
|
+
if (n > 20) console.log(import_chalk34.default.dim(` \u2026 and ${n - 20} more`));
|
|
5173
5318
|
}
|
|
5174
5319
|
}
|
|
5175
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
5320
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk34.default.red(` error: ${e}`));
|
|
5176
5321
|
console.log();
|
|
5177
5322
|
}
|
|
5178
5323
|
async function runOp(sub, opts = {}) {
|
|
5179
5324
|
if (!loadCredentials()) {
|
|
5180
|
-
console.log(
|
|
5325
|
+
console.log(import_chalk34.default.dim("Not logged in. Run `apiblaze login`."));
|
|
5181
5326
|
return;
|
|
5182
5327
|
}
|
|
5183
5328
|
if (!isOperatorLogin()) {
|
|
5184
|
-
console.log(
|
|
5329
|
+
console.log(import_chalk34.default.dim("`apiblaze op` is only available to platform operators."));
|
|
5185
5330
|
return;
|
|
5186
5331
|
}
|
|
5187
5332
|
switch (sub) {
|
|
5188
5333
|
case void 0:
|
|
5189
5334
|
case "menu": {
|
|
5190
|
-
console.log(
|
|
5191
|
-
console.log(` ${
|
|
5192
|
-
console.log(` ${
|
|
5193
|
-
console.log(` ${
|
|
5194
|
-
console.log(
|
|
5335
|
+
console.log(import_chalk34.default.bold("\nOperator menu"));
|
|
5336
|
+
console.log(` ${import_chalk34.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
5337
|
+
console.log(` ${import_chalk34.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk34.default.dim("-y to skip")})`);
|
|
5338
|
+
console.log(` ${import_chalk34.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
5339
|
+
console.log(import_chalk34.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
5195
5340
|
`));
|
|
5196
5341
|
return;
|
|
5197
5342
|
}
|
|
@@ -5210,17 +5355,17 @@ async function runOp(sub, opts = {}) {
|
|
|
5210
5355
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
5211
5356
|
printResidue(report, false);
|
|
5212
5357
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
5213
|
-
console.log(
|
|
5358
|
+
console.log(import_chalk34.default.green("Nothing to sweep."));
|
|
5214
5359
|
return;
|
|
5215
5360
|
}
|
|
5216
5361
|
if (!opts.yes) {
|
|
5217
5362
|
const readline2 = await import("readline/promises");
|
|
5218
5363
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
5219
5364
|
const answer = await rl.question(
|
|
5220
|
-
|
|
5365
|
+
import_chalk34.default.red(`Delete ${nUp} Upstash key(s) + ${nFga} OpenFGA store(s) + ${nGhost} ghost tuple(s) + ${nSide} Neon side-table row(s)? Type 'sweep' to confirm: `)
|
|
5221
5366
|
);
|
|
5222
5367
|
rl.close();
|
|
5223
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
5368
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk34.default.dim("Aborted."));
|
|
5224
5369
|
}
|
|
5225
5370
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
5226
5371
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -5231,15 +5376,15 @@ async function runOp(sub, opts = {}) {
|
|
|
5231
5376
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
5232
5377
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
5233
5378
|
const accounts = data?.accounts ?? [];
|
|
5234
|
-
if (accounts.length === 0) return void console.log(
|
|
5379
|
+
if (accounts.length === 0) return void console.log(import_chalk34.default.dim("No credit wallets."));
|
|
5235
5380
|
for (const a of accounts) {
|
|
5236
5381
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
5237
|
-
console.log(` ${
|
|
5382
|
+
console.log(` ${import_chalk34.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk34.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk34.default.dim(" \u2014 anon") : ""}`);
|
|
5238
5383
|
}
|
|
5239
5384
|
return;
|
|
5240
5385
|
}
|
|
5241
5386
|
default:
|
|
5242
|
-
console.log(
|
|
5387
|
+
console.log(import_chalk34.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
5243
5388
|
}
|
|
5244
5389
|
}
|
|
5245
5390
|
|
|
@@ -5296,7 +5441,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
5296
5441
|
try {
|
|
5297
5442
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
5298
5443
|
if (Number.isNaN(resolved)) {
|
|
5299
|
-
console.error(
|
|
5444
|
+
console.error(import_chalk35.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
5300
5445
|
process.exit(1);
|
|
5301
5446
|
}
|
|
5302
5447
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -5394,7 +5539,7 @@ function groupedCommandHelp() {
|
|
|
5394
5539
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
5395
5540
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
5396
5541
|
}).filter(Boolean).join("\n");
|
|
5397
|
-
return `${
|
|
5542
|
+
return `${import_chalk35.default.bold(g.title)}
|
|
5398
5543
|
${rows}`;
|
|
5399
5544
|
}).join("\n\n");
|
|
5400
5545
|
}
|
|
@@ -5427,14 +5572,14 @@ async function recoverStaleTeam() {
|
|
|
5427
5572
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
5428
5573
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
5429
5574
|
if (!linked) {
|
|
5430
|
-
console.error(
|
|
5575
|
+
console.error(import_chalk35.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
5431
5576
|
return;
|
|
5432
5577
|
}
|
|
5433
5578
|
if (linked.teamId === creds.teamId) return;
|
|
5434
5579
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
5435
5580
|
delete next.activeTenant;
|
|
5436
5581
|
saveCredentials(next);
|
|
5437
|
-
console.error(
|
|
5582
|
+
console.error(import_chalk35.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk35.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
5438
5583
|
} catch {
|
|
5439
5584
|
}
|
|
5440
5585
|
}
|
|
@@ -5442,16 +5587,16 @@ async function printError(err) {
|
|
|
5442
5587
|
if (err instanceof ApiError) {
|
|
5443
5588
|
const data = err.body;
|
|
5444
5589
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
5445
|
-
console.error(
|
|
5590
|
+
console.error(import_chalk35.default.red(`
|
|
5446
5591
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
5447
5592
|
if (err.status === 403 || err.status === 404) {
|
|
5448
5593
|
await recoverStaleTeam();
|
|
5449
5594
|
}
|
|
5450
5595
|
} else if (err instanceof Error) {
|
|
5451
|
-
console.error(
|
|
5596
|
+
console.error(import_chalk35.default.red(`
|
|
5452
5597
|
Error: ${err.message}`));
|
|
5453
5598
|
} else {
|
|
5454
|
-
console.error(
|
|
5599
|
+
console.error(import_chalk35.default.red("\nUnknown error"));
|
|
5455
5600
|
}
|
|
5456
5601
|
}
|
|
5457
5602
|
program.parse(process.argv);
|