apiblaze 0.15.1 → 0.16.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 +440 -345
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -457,6 +457,139 @@ 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
|
+
createTenantInteractive: () => createTenantInteractive
|
|
464
|
+
});
|
|
465
|
+
async function createTenantRow(teamId) {
|
|
466
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
467
|
+
for (; ; ) {
|
|
468
|
+
const { name } = await inquirer2.prompt([{
|
|
469
|
+
type: "input",
|
|
470
|
+
name: "name",
|
|
471
|
+
message: `Tenant name ${import_chalk22.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
|
|
472
|
+
validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
|
|
473
|
+
}]);
|
|
474
|
+
const slug = name.trim();
|
|
475
|
+
try {
|
|
476
|
+
const out = await admin({
|
|
477
|
+
method: "POST",
|
|
478
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants`,
|
|
479
|
+
body: { tenant_name: slug, display_name: slug },
|
|
480
|
+
summary: `Create tenant "${slug}"`
|
|
481
|
+
});
|
|
482
|
+
return out?.tenant_name ?? out?.tenant?.tenant_name ?? slug;
|
|
483
|
+
} catch (err) {
|
|
484
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
485
|
+
if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
|
|
486
|
+
console.log(import_chalk22.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
throw err;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
async function setUpLogin(teamId, tenant2) {
|
|
494
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
495
|
+
const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
496
|
+
const { provider } = await inquirer2.prompt([{
|
|
497
|
+
type: "list",
|
|
498
|
+
name: "provider",
|
|
499
|
+
message: "How do people log in?",
|
|
500
|
+
pageSize: 10,
|
|
501
|
+
choices: [
|
|
502
|
+
...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
|
|
503
|
+
new inquirer2.Separator(),
|
|
504
|
+
{ 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;
|
|
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
|
+
} catch (err) {
|
|
546
|
+
spinner.fail("Login setup failed (the tenant was still created).");
|
|
547
|
+
throw err;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
async function createTenantInteractive(teamId) {
|
|
551
|
+
const { default: inquirer2 } = await import("inquirer");
|
|
552
|
+
const tenant2 = await createTenantRow(teamId);
|
|
553
|
+
console.log(import_chalk22.default.green(` Tenant ${import_chalk22.default.bold(tenant2)} created.`));
|
|
554
|
+
const { users } = await inquirer2.prompt([{
|
|
555
|
+
type: "confirm",
|
|
556
|
+
name: "users",
|
|
557
|
+
default: false,
|
|
558
|
+
message: `Enable Users & groups? ${import_chalk22.default.dim(`(identity/key management at ${tenant2}.iam.apiblaze.com)`)}`
|
|
559
|
+
}]);
|
|
560
|
+
if (users) {
|
|
561
|
+
await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
|
|
562
|
+
});
|
|
563
|
+
console.log(import_chalk22.default.green(" Users & groups enabled."));
|
|
564
|
+
}
|
|
565
|
+
await setUpLogin(teamId, tenant2);
|
|
566
|
+
return tenant2;
|
|
567
|
+
}
|
|
568
|
+
var import_chalk22, import_ora8, PROVIDERS, DEFAULT_SCOPES;
|
|
569
|
+
var init_tenant_create = __esm({
|
|
570
|
+
"src/lib/tenant-create.ts"() {
|
|
571
|
+
"use strict";
|
|
572
|
+
import_chalk22 = __toESM(require("chalk"));
|
|
573
|
+
import_ora8 = __toESM(require("ora"));
|
|
574
|
+
init_admin();
|
|
575
|
+
PROVIDERS = [
|
|
576
|
+
{ id: "apiblaze", label: "APIblaze (via GitHub) \u2014 zero setup, recommended", own: false },
|
|
577
|
+
{ id: "google", label: "Google", own: true },
|
|
578
|
+
{ id: "github", label: "GitHub", own: true },
|
|
579
|
+
{ id: "microsoft", label: "Microsoft", own: true },
|
|
580
|
+
{ id: "facebook", label: "Facebook", own: true },
|
|
581
|
+
{ id: "auth0", label: "Auth0", own: true },
|
|
582
|
+
{ id: "other", label: "Custom (any OIDC provider)", own: true }
|
|
583
|
+
];
|
|
584
|
+
DEFAULT_SCOPES = {
|
|
585
|
+
google: "openid email profile",
|
|
586
|
+
microsoft: "openid email profile",
|
|
587
|
+
github: "read:user user:email",
|
|
588
|
+
facebook: "public_profile email"
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
|
|
460
593
|
// src/lib/tenant-pick.ts
|
|
461
594
|
var tenant_pick_exports = {};
|
|
462
595
|
__export(tenant_pick_exports, {
|
|
@@ -475,10 +608,10 @@ async function fetchPage(teamId, q) {
|
|
|
475
608
|
}
|
|
476
609
|
function label(t, defaultTenant, active) {
|
|
477
610
|
const tags = [
|
|
478
|
-
t.tenant_name === active ?
|
|
479
|
-
t.tenant_name === defaultTenant ?
|
|
611
|
+
t.tenant_name === active ? import_chalk23.default.cyan("active scope") : "",
|
|
612
|
+
t.tenant_name === defaultTenant ? import_chalk23.default.dim("team default") : ""
|
|
480
613
|
].filter(Boolean).join(", ");
|
|
481
|
-
const disp = t.display_name && t.display_name !== t.tenant_name ?
|
|
614
|
+
const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk23.default.dim(` ${t.display_name}`) : "";
|
|
482
615
|
return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
|
|
483
616
|
}
|
|
484
617
|
async function pickTenant(teamId, opts = {}) {
|
|
@@ -486,14 +619,14 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
486
619
|
const active = loadCredentials()?.activeTenant;
|
|
487
620
|
let q = opts.initialQuery ?? "";
|
|
488
621
|
for (; ; ) {
|
|
489
|
-
const spinner = (0,
|
|
622
|
+
const spinner = (0, import_ora9.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
|
|
490
623
|
const page = await fetchPage(teamId, q).finally(() => spinner.stop());
|
|
491
624
|
if (!page.total && !q) {
|
|
492
625
|
if (opts.allowCreate) {
|
|
493
626
|
const { make } = await inquirer2.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
|
|
494
627
|
if (make) return await createTenantInline(teamId);
|
|
495
628
|
}
|
|
496
|
-
console.error(
|
|
629
|
+
console.error(import_chalk23.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
497
630
|
return null;
|
|
498
631
|
}
|
|
499
632
|
const truncated = page.total > page.rows.length;
|
|
@@ -502,7 +635,7 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
502
635
|
value: t.tenant_name
|
|
503
636
|
}));
|
|
504
637
|
if (truncated || q) {
|
|
505
|
-
choices.push(new inquirer2.Separator(
|
|
638
|
+
choices.push(new inquirer2.Separator(import_chalk23.default.dim(
|
|
506
639
|
truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
|
|
507
640
|
)));
|
|
508
641
|
choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
|
|
@@ -533,41 +666,15 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
533
666
|
}
|
|
534
667
|
}
|
|
535
668
|
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
|
-
}
|
|
669
|
+
const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
|
|
670
|
+
return createTenantInteractive2(teamId);
|
|
564
671
|
}
|
|
565
|
-
var
|
|
672
|
+
var import_chalk23, import_ora9, PAGE;
|
|
566
673
|
var init_tenant_pick = __esm({
|
|
567
674
|
"src/lib/tenant-pick.ts"() {
|
|
568
675
|
"use strict";
|
|
569
|
-
|
|
570
|
-
|
|
676
|
+
import_chalk23 = __toESM(require("chalk"));
|
|
677
|
+
import_ora9 = __toESM(require("ora"));
|
|
571
678
|
init_admin();
|
|
572
679
|
init_auth();
|
|
573
680
|
PAGE = 15;
|
|
@@ -576,10 +683,10 @@ var init_tenant_pick = __esm({
|
|
|
576
683
|
|
|
577
684
|
// src/index.ts
|
|
578
685
|
var import_commander = require("commander");
|
|
579
|
-
var
|
|
686
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
580
687
|
|
|
581
688
|
// package.json
|
|
582
|
-
var version = "0.
|
|
689
|
+
var version = "0.16.0";
|
|
583
690
|
|
|
584
691
|
// src/index.ts
|
|
585
692
|
init_types();
|
|
@@ -2652,8 +2759,8 @@ async function runRename(project, opts) {
|
|
|
2652
2759
|
}
|
|
2653
2760
|
|
|
2654
2761
|
// src/commands/config-browse.ts
|
|
2655
|
-
var
|
|
2656
|
-
var
|
|
2762
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
2763
|
+
var import_ora14 = __toESM(require("ora"));
|
|
2657
2764
|
init_admin();
|
|
2658
2765
|
init_auth();
|
|
2659
2766
|
|
|
@@ -2763,28 +2870,28 @@ async function runDomainSetBase(project, opts) {
|
|
|
2763
2870
|
}
|
|
2764
2871
|
|
|
2765
2872
|
// src/commands/tenant.ts
|
|
2766
|
-
var
|
|
2767
|
-
var
|
|
2873
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
2874
|
+
var import_ora10 = __toESM(require("ora"));
|
|
2768
2875
|
init_admin();
|
|
2769
2876
|
init_auth();
|
|
2770
2877
|
init_tenant_pick();
|
|
2771
2878
|
async function runTenantUse(query, opts) {
|
|
2772
2879
|
const creds = loadCredentials();
|
|
2773
2880
|
if (!creds) {
|
|
2774
|
-
console.error(
|
|
2881
|
+
console.error(import_chalk24.default.red("Not logged in. Run `apiblaze login` first."));
|
|
2775
2882
|
process.exit(1);
|
|
2776
2883
|
}
|
|
2777
2884
|
if (opts.clear) {
|
|
2778
2885
|
delete creds.activeTenant;
|
|
2779
2886
|
saveCredentials(creds);
|
|
2780
|
-
console.log(
|
|
2887
|
+
console.log(import_chalk24.default.green("Tenant scope cleared."));
|
|
2781
2888
|
return;
|
|
2782
2889
|
}
|
|
2783
2890
|
const { teamId } = await resolveTeam(opts.team);
|
|
2784
2891
|
const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
|
|
2785
2892
|
if (!slug) process.exit(1);
|
|
2786
2893
|
saveCredentials({ ...creds, activeTenant: slug });
|
|
2787
|
-
console.log(
|
|
2894
|
+
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
2895
|
}
|
|
2789
2896
|
async function runTenantList(opts) {
|
|
2790
2897
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -2801,26 +2908,26 @@ async function runTenantList(opts) {
|
|
|
2801
2908
|
return;
|
|
2802
2909
|
}
|
|
2803
2910
|
if (!tenants.length) {
|
|
2804
|
-
console.log(
|
|
2911
|
+
console.log(import_chalk24.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
|
|
2805
2912
|
return;
|
|
2806
2913
|
}
|
|
2807
2914
|
for (const t of tenants) {
|
|
2808
2915
|
const name = typeof t === "string" ? t : t.tenant_name;
|
|
2809
|
-
const display = typeof t === "string" ? "" :
|
|
2810
|
-
console.log(` ${
|
|
2916
|
+
const display = typeof t === "string" ? "" : import_chalk24.default.dim(` ${t.display_name ?? ""}`);
|
|
2917
|
+
console.log(` ${import_chalk24.default.bold(name)}${display}`);
|
|
2811
2918
|
}
|
|
2812
2919
|
const total = out?.total ?? tenants.length;
|
|
2813
2920
|
if (total > tenants.length) {
|
|
2814
|
-
console.log(
|
|
2921
|
+
console.log(import_chalk24.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
|
|
2815
2922
|
}
|
|
2816
2923
|
}
|
|
2817
2924
|
async function runTenantCreate(opts) {
|
|
2818
2925
|
if (!opts.name) {
|
|
2819
|
-
console.error(
|
|
2926
|
+
console.error(import_chalk24.default.red("--name (display name) is required."));
|
|
2820
2927
|
process.exit(1);
|
|
2821
2928
|
}
|
|
2822
2929
|
const { teamId } = await resolveTeam(opts.team);
|
|
2823
|
-
const spinner = (0,
|
|
2930
|
+
const spinner = (0, import_ora10.default)("Creating tenant...").start();
|
|
2824
2931
|
try {
|
|
2825
2932
|
const out = await admin({
|
|
2826
2933
|
method: "POST",
|
|
@@ -2828,7 +2935,7 @@ async function runTenantCreate(opts) {
|
|
|
2828
2935
|
body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
|
|
2829
2936
|
summary: `Create tenant "${opts.name}"`
|
|
2830
2937
|
});
|
|
2831
|
-
spinner.succeed(`Created tenant ${
|
|
2938
|
+
spinner.succeed(`Created tenant ${import_chalk24.default.bold(out?.tenant_name ?? opts.name)}.`);
|
|
2832
2939
|
if (opts.json) console.log(JSON.stringify(out));
|
|
2833
2940
|
} catch (err) {
|
|
2834
2941
|
spinner.fail("Tenant create failed.");
|
|
@@ -2837,12 +2944,12 @@ async function runTenantCreate(opts) {
|
|
|
2837
2944
|
}
|
|
2838
2945
|
async function runTenantAttach(project, opts) {
|
|
2839
2946
|
if (!opts.tenant) {
|
|
2840
|
-
console.error(
|
|
2947
|
+
console.error(import_chalk24.default.red("--tenant <slug> is required."));
|
|
2841
2948
|
process.exit(1);
|
|
2842
2949
|
}
|
|
2843
2950
|
const { teamId } = await resolveTeam(opts.team);
|
|
2844
2951
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2845
|
-
const spinner = (0,
|
|
2952
|
+
const spinner = (0, import_ora10.default)("Attaching tenant...").start();
|
|
2846
2953
|
try {
|
|
2847
2954
|
const out = await admin({
|
|
2848
2955
|
method: "POST",
|
|
@@ -2865,11 +2972,11 @@ async function runTenantDelete(slug, opts) {
|
|
|
2865
2972
|
{ type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
|
|
2866
2973
|
]);
|
|
2867
2974
|
if (!confirm) {
|
|
2868
|
-
console.log(
|
|
2975
|
+
console.log(import_chalk24.default.dim("Aborted."));
|
|
2869
2976
|
return;
|
|
2870
2977
|
}
|
|
2871
2978
|
}
|
|
2872
|
-
const spinner = (0,
|
|
2979
|
+
const spinner = (0, import_ora10.default)("Deleting tenant...").start();
|
|
2873
2980
|
try {
|
|
2874
2981
|
await admin({
|
|
2875
2982
|
method: "DELETE",
|
|
@@ -2884,13 +2991,13 @@ async function runTenantDelete(slug, opts) {
|
|
|
2884
2991
|
}
|
|
2885
2992
|
async function runTenantCors(opts) {
|
|
2886
2993
|
if (!opts.tenant) {
|
|
2887
|
-
console.error(
|
|
2994
|
+
console.error(import_chalk24.default.red("--tenant <slug> is required."));
|
|
2888
2995
|
process.exit(1);
|
|
2889
2996
|
}
|
|
2890
2997
|
const { teamId } = await resolveTeam(opts.team);
|
|
2891
2998
|
const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
2892
2999
|
const cors = origins.length ? { allowed_origins: origins } : null;
|
|
2893
|
-
const spinner = (0,
|
|
3000
|
+
const spinner = (0, import_ora10.default)("Updating CORS...").start();
|
|
2894
3001
|
try {
|
|
2895
3002
|
await admin({
|
|
2896
3003
|
method: "PUT",
|
|
@@ -2906,13 +3013,12 @@ async function runTenantCors(opts) {
|
|
|
2906
3013
|
}
|
|
2907
3014
|
|
|
2908
3015
|
// src/commands/tenant-drill.ts
|
|
2909
|
-
var
|
|
2910
|
-
var
|
|
3016
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
3017
|
+
var import_ora11 = __toESM(require("ora"));
|
|
2911
3018
|
var import_crypto = require("crypto");
|
|
2912
3019
|
init_admin();
|
|
2913
3020
|
init_auth();
|
|
2914
3021
|
init_tenant_pick();
|
|
2915
|
-
init_api();
|
|
2916
3022
|
var trailingComma = /\s*,\s*/;
|
|
2917
3023
|
var parseList = (s) => s.split(trailingComma).map((x) => x.trim()).filter(Boolean);
|
|
2918
3024
|
async function runTenantManage(query, opts) {
|
|
@@ -2937,17 +3043,17 @@ async function validScopedTenant(teamId, query) {
|
|
|
2937
3043
|
const next = { ...creds };
|
|
2938
3044
|
delete next.activeTenant;
|
|
2939
3045
|
saveCredentials2(next);
|
|
2940
|
-
console.log(
|
|
3046
|
+
console.log(import_chalk25.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
|
|
2941
3047
|
return void 0;
|
|
2942
3048
|
}
|
|
2943
3049
|
async function tenantHome(teamId, tenant2) {
|
|
2944
3050
|
const { default: inquirer2 } = await import("inquirer");
|
|
2945
3051
|
const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
2946
|
-
console.log(
|
|
3052
|
+
console.log(import_chalk25.default.bold(`
|
|
2947
3053
|
Tenant ${tenant2}`));
|
|
2948
|
-
console.log(
|
|
3054
|
+
console.log(import_chalk25.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
2949
3055
|
for (; ; ) {
|
|
2950
|
-
const spinner = (0,
|
|
3056
|
+
const spinner = (0, import_ora11.default)("Reading tenant state...").start();
|
|
2951
3057
|
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
2952
3058
|
admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
|
|
2953
3059
|
admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
|
|
@@ -2959,19 +3065,20 @@ Tenant ${tenant2}`));
|
|
|
2959
3065
|
const nEmails = (emails?.admin_emails ?? []).length;
|
|
2960
3066
|
const nIssuers = (issuers?.external_issuers ?? []).length;
|
|
2961
3067
|
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
2962
|
-
const onOff = (b) => b ?
|
|
3068
|
+
const onOff = (b) => b ? import_chalk25.default.green("on") : import_chalk25.default.dim("off");
|
|
2963
3069
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
2964
3070
|
type: "list",
|
|
2965
3071
|
name: "pick",
|
|
2966
3072
|
message: `Tenant ${tenant2}:`,
|
|
2967
3073
|
pageSize: 12,
|
|
2968
3074
|
choices: [
|
|
2969
|
-
{ name: `
|
|
2970
|
-
{ name: `
|
|
2971
|
-
{ name: `
|
|
2972
|
-
{ name: `
|
|
2973
|
-
|
|
2974
|
-
{ name: `
|
|
3075
|
+
{ name: `Login methods (${nClients}) ${import_chalk25.default.dim("how your consumers sign in \u2014 providers live inside")}`, value: "clients" },
|
|
3076
|
+
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk25.default.dim(`identity & key management (${tenant2}.iam.apiblaze.com)`)}`, value: "iam" },
|
|
3077
|
+
{ name: `Your own hosted login \u2014 JWT (${nIssuers}) ${import_chalk25.default.dim("trust tokens your own identity provider issues")}`, value: "issuers" },
|
|
3078
|
+
{ name: `Portal admins (${nEmails}) ${import_chalk25.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
|
|
3079
|
+
new inquirer2.Separator(import_chalk25.default.dim(" Settings")),
|
|
3080
|
+
{ 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" },
|
|
3081
|
+
{ name: `Opaque-token login (advanced): ${opaque?.opaque?.endpoint ? import_chalk25.default.cyan(opaque.opaque.endpoint) : import_chalk25.default.dim("(unset)")}`, value: "opaque" },
|
|
2975
3082
|
{ name: "\u2190 Back", value: "back" }
|
|
2976
3083
|
]
|
|
2977
3084
|
}]);
|
|
@@ -2982,9 +3089,9 @@ Tenant ${tenant2}`));
|
|
|
2982
3089
|
await clientsMenu(teamId, tenant2, base);
|
|
2983
3090
|
break;
|
|
2984
3091
|
case "iam": {
|
|
2985
|
-
const { v } = await inquirer2.prompt([{ type: "confirm", name: "v", message: "Enable
|
|
3092
|
+
const { v } = await inquirer2.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
|
|
2986
3093
|
await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
2987
|
-
console.log(
|
|
3094
|
+
console.log(import_chalk25.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
|
|
2988
3095
|
break;
|
|
2989
3096
|
}
|
|
2990
3097
|
case "cors": {
|
|
@@ -2997,11 +3104,11 @@ Tenant ${tenant2}`));
|
|
|
2997
3104
|
if (v === "") break;
|
|
2998
3105
|
const parsed = v === "null" ? null : safeJson(v);
|
|
2999
3106
|
if (parsed === void 0) {
|
|
3000
|
-
console.log(
|
|
3107
|
+
console.log(import_chalk25.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
3001
3108
|
break;
|
|
3002
3109
|
}
|
|
3003
3110
|
await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
3004
|
-
console.log(
|
|
3111
|
+
console.log(import_chalk25.default.green(" CORS updated."));
|
|
3005
3112
|
break;
|
|
3006
3113
|
}
|
|
3007
3114
|
case "emails":
|
|
@@ -3025,7 +3132,7 @@ Tenant ${tenant2}`));
|
|
|
3025
3132
|
if (mode === "back") break;
|
|
3026
3133
|
if (mode === "clear") {
|
|
3027
3134
|
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
3028
|
-
console.log(
|
|
3135
|
+
console.log(import_chalk25.default.green(" Cleared."));
|
|
3029
3136
|
break;
|
|
3030
3137
|
}
|
|
3031
3138
|
const a = await inquirer2.prompt([
|
|
@@ -3033,7 +3140,7 @@ Tenant ${tenant2}`));
|
|
|
3033
3140
|
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
3034
3141
|
]);
|
|
3035
3142
|
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
3036
|
-
console.log(
|
|
3143
|
+
console.log(import_chalk25.default.green(" Opaque validator set."));
|
|
3037
3144
|
break;
|
|
3038
3145
|
}
|
|
3039
3146
|
}
|
|
@@ -3049,8 +3156,8 @@ function safeJson(s) {
|
|
|
3049
3156
|
async function emailsMenu(base, emails) {
|
|
3050
3157
|
const { default: inquirer2 } = await import("inquirer");
|
|
3051
3158
|
console.log();
|
|
3052
|
-
if (!emails.length) console.log(
|
|
3053
|
-
for (const e of emails) console.log(` ${
|
|
3159
|
+
if (!emails.length) console.log(import_chalk25.default.dim(" No consumer-admin emails."));
|
|
3160
|
+
for (const e of emails) console.log(` ${import_chalk25.default.bold(e.email ?? e)} ${import_chalk25.default.dim(e.status ?? "")}`);
|
|
3054
3161
|
const { act } = await inquirer2.prompt([{
|
|
3055
3162
|
type: "list",
|
|
3056
3163
|
name: "act",
|
|
@@ -3065,7 +3172,7 @@ async function emailsMenu(base, emails) {
|
|
|
3065
3172
|
if (act === "add") {
|
|
3066
3173
|
const { email } = await inquirer2.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
3067
3174
|
await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
3068
|
-
console.log(
|
|
3175
|
+
console.log(import_chalk25.default.green(` ${email} added.`));
|
|
3069
3176
|
} else {
|
|
3070
3177
|
const { e } = await inquirer2.prompt([{
|
|
3071
3178
|
type: "list",
|
|
@@ -3075,14 +3182,14 @@ async function emailsMenu(base, emails) {
|
|
|
3075
3182
|
}]);
|
|
3076
3183
|
if (!e) return;
|
|
3077
3184
|
await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
3078
|
-
console.log(
|
|
3185
|
+
console.log(import_chalk25.default.green(` ${e} removed.`));
|
|
3079
3186
|
}
|
|
3080
3187
|
}
|
|
3081
3188
|
async function issuersMenu(base, issuers) {
|
|
3082
3189
|
const { default: inquirer2 } = await import("inquirer");
|
|
3083
3190
|
console.log();
|
|
3084
|
-
if (!issuers.length) console.log(
|
|
3085
|
-
for (const i of issuers) console.log(` ${
|
|
3191
|
+
if (!issuers.length) console.log(import_chalk25.default.dim(" No external issuers \u2014 consumers use APIblaze-issued tokens."));
|
|
3192
|
+
for (const i of issuers) console.log(` ${import_chalk25.default.bold(i.iss)} aud=${i.aud} ${import_chalk25.default.dim(i.sub_semantics ?? "")}`);
|
|
3086
3193
|
const { act } = await inquirer2.prompt([{
|
|
3087
3194
|
type: "list",
|
|
3088
3195
|
name: "act",
|
|
@@ -3111,7 +3218,7 @@ async function issuersMenu(base, issuers) {
|
|
|
3111
3218
|
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3112
3219
|
summary: `Add external issuer ${a.iss.trim()}`
|
|
3113
3220
|
});
|
|
3114
|
-
console.log(
|
|
3221
|
+
console.log(import_chalk25.default.green(" Issuer saved."));
|
|
3115
3222
|
} else {
|
|
3116
3223
|
const { i } = await inquirer2.prompt([{
|
|
3117
3224
|
type: "list",
|
|
@@ -3125,59 +3232,47 @@ async function issuersMenu(base, issuers) {
|
|
|
3125
3232
|
path: `${base}/external-issuers?iss=${encodeURIComponent(i.iss)}&aud=${encodeURIComponent(i.aud)}`,
|
|
3126
3233
|
summary: `Delete issuer ${i.iss}`
|
|
3127
3234
|
});
|
|
3128
|
-
console.log(
|
|
3235
|
+
console.log(import_chalk25.default.green(" Issuer deleted."));
|
|
3129
3236
|
}
|
|
3130
3237
|
}
|
|
3131
3238
|
async function clientsMenu(teamId, tenant2, base) {
|
|
3132
3239
|
const { default: inquirer2 } = await import("inquirer");
|
|
3133
3240
|
for (; ; ) {
|
|
3134
|
-
const spinner = (0,
|
|
3241
|
+
const spinner = (0, import_ora11.default)("Loading app clients...").start();
|
|
3135
3242
|
const raw = await admin({ method: "GET", path: `${base}/app-clients`, summary: "List app clients" }).catch(() => []);
|
|
3136
3243
|
spinner.stop();
|
|
3137
3244
|
const clients = Array.isArray(raw) ? raw : [];
|
|
3138
3245
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3139
3246
|
type: "list",
|
|
3140
3247
|
name: "pick",
|
|
3141
|
-
message: `
|
|
3248
|
+
message: `Login methods for ${tenant2}:`,
|
|
3142
3249
|
pageSize: 15,
|
|
3143
3250
|
choices: [
|
|
3144
3251
|
...clients.map((c) => ({
|
|
3145
|
-
name: `${
|
|
3252
|
+
name: `${import_chalk25.default.bold(c.name ?? c.clientId)} ${import_chalk25.default.dim(`${c.clientId}${c.projectName ? ` \xB7 ${c.projectName}` : ""}`)}`,
|
|
3146
3253
|
value: c
|
|
3147
3254
|
})),
|
|
3148
|
-
...clients.length ? [] : [new inquirer2.Separator(
|
|
3149
|
-
{ name: "\uFF0B
|
|
3255
|
+
...clients.length ? [] : [new inquirer2.Separator(import_chalk25.default.dim(" no app clients yet"))],
|
|
3256
|
+
{ name: "\uFF0B Add a login method\u2026", value: " create" },
|
|
3150
3257
|
{ name: "\u2190 Back", value: " back" }
|
|
3151
3258
|
]
|
|
3152
3259
|
}]);
|
|
3153
3260
|
if (pick2 === " back") return;
|
|
3154
3261
|
if (pick2 === " create") {
|
|
3155
|
-
const projects = await getProjects(teamId).catch(() => []);
|
|
3156
3262
|
const a = await inquirer2.prompt([
|
|
3157
3263
|
{ type: "input", name: "name", message: "Client name:", validate: (s) => !!s.trim() || "required" },
|
|
3158
|
-
...projects.length ? [{
|
|
3159
|
-
type: "list",
|
|
3160
|
-
name: "proj",
|
|
3161
|
-
message: "Reference a project? (adds its portal/MCP token audiences)",
|
|
3162
|
-
choices: [
|
|
3163
|
-
...projects.map((p) => ({ name: `${p.projectName} ${import_chalk24.default.dim("v" + p.apiVersion)}`, value: p })),
|
|
3164
|
-
{ name: import_chalk24.default.dim("Skip \u2014 tenant-only client"), value: null }
|
|
3165
|
-
]
|
|
3166
|
-
}] : [],
|
|
3167
3264
|
{ type: "input", name: "callbacks", message: "Callback URLs (comma-separated, empty = none):" }
|
|
3168
3265
|
]);
|
|
3169
|
-
if (!projects.length) console.log(import_chalk24.default.dim(" (no projects in this team yet \u2014 creating a tenant-only client; you can reference a project later)"));
|
|
3170
3266
|
const created = await admin({
|
|
3171
3267
|
method: "POST",
|
|
3172
3268
|
path: `${base}/app-clients`,
|
|
3173
3269
|
body: {
|
|
3174
3270
|
name: a.name.trim(),
|
|
3175
|
-
...a.proj ? { projectName: a.proj.projectName, apiVersion: a.proj.apiVersion } : {},
|
|
3176
3271
|
...a.callbacks.trim() ? { authorizedCallbackUrls: parseList(a.callbacks) } : {}
|
|
3177
3272
|
},
|
|
3178
3273
|
summary: `Create app client "${a.name.trim()}"`
|
|
3179
3274
|
});
|
|
3180
|
-
console.log(
|
|
3275
|
+
console.log(import_chalk25.default.green(` Login method created${created?.clientId ? ` (${created.clientId})` : ""}.`));
|
|
3181
3276
|
continue;
|
|
3182
3277
|
}
|
|
3183
3278
|
await clientHome(base, pick2);
|
|
@@ -3188,7 +3283,7 @@ async function clientHome(base, summary) {
|
|
|
3188
3283
|
const id = summary.clientId ?? summary.client_id;
|
|
3189
3284
|
const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
|
|
3190
3285
|
for (; ; ) {
|
|
3191
|
-
const spinner = (0,
|
|
3286
|
+
const spinner = (0, import_ora11.default)("Reading app client...").start();
|
|
3192
3287
|
const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
|
|
3193
3288
|
spinner.stop();
|
|
3194
3289
|
const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
|
|
@@ -3200,13 +3295,13 @@ async function clientHome(base, summary) {
|
|
|
3200
3295
|
message: `${c.name ?? id}:`,
|
|
3201
3296
|
pageSize: 12,
|
|
3202
3297
|
choices: [
|
|
3203
|
-
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${
|
|
3204
|
-
{ name: `Callback URLs: ${cb.length ?
|
|
3205
|
-
{ name: `Scopes: ${scopes.length ?
|
|
3298
|
+
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk25.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
|
|
3299
|
+
{ name: `Callback URLs: ${cb.length ? import_chalk25.default.cyan(cb.join(", ")) : import_chalk25.default.dim("(none)")}`, value: "callbacks" },
|
|
3300
|
+
{ name: `Scopes: ${scopes.length ? import_chalk25.default.cyan(scopes.join(" ")) : import_chalk25.default.dim("(defaults)")}`, value: "scopes" },
|
|
3206
3301
|
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
3207
3302
|
{ name: "Reveal client secret", value: "secret" },
|
|
3208
3303
|
{ name: "Rotate client secret", value: "rotate" },
|
|
3209
|
-
{ name:
|
|
3304
|
+
{ name: import_chalk25.default.red("Delete this app client"), value: "delete" },
|
|
3210
3305
|
{ name: "\u2190 Back", value: "back" }
|
|
3211
3306
|
]
|
|
3212
3307
|
}]);
|
|
@@ -3219,13 +3314,13 @@ async function clientHome(base, summary) {
|
|
|
3219
3314
|
case "callbacks": {
|
|
3220
3315
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
3221
3316
|
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
3222
|
-
console.log(
|
|
3317
|
+
console.log(import_chalk25.default.green(" Callbacks updated."));
|
|
3223
3318
|
break;
|
|
3224
3319
|
}
|
|
3225
3320
|
case "scopes": {
|
|
3226
3321
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
3227
3322
|
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
3228
|
-
console.log(
|
|
3323
|
+
console.log(import_chalk25.default.green(" Scopes updated."));
|
|
3229
3324
|
break;
|
|
3230
3325
|
}
|
|
3231
3326
|
case "expiries": {
|
|
@@ -3240,14 +3335,14 @@ async function clientHome(base, summary) {
|
|
|
3240
3335
|
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
3241
3336
|
summary: "Update token expiries"
|
|
3242
3337
|
});
|
|
3243
|
-
console.log(
|
|
3338
|
+
console.log(import_chalk25.default.green(" Expiries updated."));
|
|
3244
3339
|
break;
|
|
3245
3340
|
}
|
|
3246
3341
|
case "secret": {
|
|
3247
3342
|
const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
3248
3343
|
if (!sure) break;
|
|
3249
3344
|
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
3250
|
-
console.log(` ${
|
|
3345
|
+
console.log(` ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3251
3346
|
break;
|
|
3252
3347
|
}
|
|
3253
3348
|
case "rotate": {
|
|
@@ -3255,14 +3350,14 @@ async function clientHome(base, summary) {
|
|
|
3255
3350
|
if (!sure) break;
|
|
3256
3351
|
const fresh = randomSecret();
|
|
3257
3352
|
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
3258
|
-
console.log(` New ${
|
|
3353
|
+
console.log(` New ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(fresh)} ${import_chalk25.default.dim("(store it now)")}`);
|
|
3259
3354
|
break;
|
|
3260
3355
|
}
|
|
3261
3356
|
case "delete": {
|
|
3262
3357
|
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 }]);
|
|
3263
3358
|
if (!sure) break;
|
|
3264
3359
|
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
3265
|
-
console.log(
|
|
3360
|
+
console.log(import_chalk25.default.green(" App client deleted."));
|
|
3266
3361
|
return;
|
|
3267
3362
|
}
|
|
3268
3363
|
}
|
|
@@ -3274,7 +3369,7 @@ function randomSecret() {
|
|
|
3274
3369
|
return Buffer.from(bytes).toString("base64url");
|
|
3275
3370
|
}
|
|
3276
3371
|
var PROVIDER_TYPES = ["google", "github", "microsoft", "facebook", "auth0", "other"];
|
|
3277
|
-
var
|
|
3372
|
+
var DEFAULT_SCOPES2 = {
|
|
3278
3373
|
google: "openid email profile",
|
|
3279
3374
|
microsoft: "openid email profile",
|
|
3280
3375
|
github: "read:user user:email",
|
|
@@ -3283,19 +3378,19 @@ var DEFAULT_SCOPES = {
|
|
|
3283
3378
|
async function providersMenu(cBase, clientLabel) {
|
|
3284
3379
|
const { default: inquirer2 } = await import("inquirer");
|
|
3285
3380
|
for (; ; ) {
|
|
3286
|
-
const spinner = (0,
|
|
3381
|
+
const spinner = (0, import_ora11.default)("Loading providers...").start();
|
|
3287
3382
|
const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
|
|
3288
3383
|
spinner.stop();
|
|
3289
3384
|
const providers = Array.isArray(raw) ? raw : [];
|
|
3290
3385
|
console.log();
|
|
3291
3386
|
for (const p of providers) {
|
|
3292
|
-
console.log(` ${
|
|
3387
|
+
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" : ""}`)}`);
|
|
3293
3388
|
}
|
|
3294
|
-
if (!providers.length) console.log(
|
|
3389
|
+
if (!providers.length) console.log(import_chalk25.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
|
|
3295
3390
|
const { act } = await inquirer2.prompt([{
|
|
3296
3391
|
type: "list",
|
|
3297
3392
|
name: "act",
|
|
3298
|
-
message: `
|
|
3393
|
+
message: `Sign-in providers for ${clientLabel}:`,
|
|
3299
3394
|
choices: [
|
|
3300
3395
|
{ name: "\uFF0B Add a provider", value: "add" },
|
|
3301
3396
|
...providers.length ? [
|
|
@@ -3323,7 +3418,7 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3323
3418
|
{ type: "input", name: "clientId", message: `${type} OAuth client id:`, validate: (s) => !!s.trim() || "required" },
|
|
3324
3419
|
{ type: "password", name: "clientSecret", mask: "*", message: `${type} OAuth client secret:`, validate: (s) => s.length >= 6 && s.length <= 200 || "6\u2013200 chars" },
|
|
3325
3420
|
...type === "auth0" || type === "other" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : [],
|
|
3326
|
-
{ type: "input", name: "scopes", message: "Scopes:", default:
|
|
3421
|
+
{ type: "input", name: "scopes", message: "Scopes:", default: DEFAULT_SCOPES2[type] ?? "" }
|
|
3327
3422
|
]);
|
|
3328
3423
|
body = {
|
|
3329
3424
|
type,
|
|
@@ -3349,23 +3444,23 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3349
3444
|
body.targetServerToken = routing;
|
|
3350
3445
|
}
|
|
3351
3446
|
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
3352
|
-
console.log(
|
|
3447
|
+
console.log(import_chalk25.default.green(` ${type} provider added.`));
|
|
3353
3448
|
} else {
|
|
3354
3449
|
const { p } = await inquirer2.prompt([{
|
|
3355
3450
|
type: "list",
|
|
3356
3451
|
name: "p",
|
|
3357
3452
|
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
3358
|
-
choices: [...providers.map((x) => ({ name: `${x.type} ${
|
|
3453
|
+
choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk25.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
|
|
3359
3454
|
}]);
|
|
3360
3455
|
if (!p) continue;
|
|
3361
3456
|
if (act === "rm") {
|
|
3362
3457
|
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 }]);
|
|
3363
3458
|
if (!sure) continue;
|
|
3364
3459
|
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
3365
|
-
console.log(
|
|
3460
|
+
console.log(import_chalk25.default.green(` ${p.type} removed.`));
|
|
3366
3461
|
} else {
|
|
3367
3462
|
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
3368
|
-
console.log(` ${
|
|
3463
|
+
console.log(` ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3369
3464
|
}
|
|
3370
3465
|
}
|
|
3371
3466
|
}
|
|
@@ -3373,8 +3468,8 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3373
3468
|
|
|
3374
3469
|
// src/commands/spec.ts
|
|
3375
3470
|
var fs6 = __toESM(require("fs"));
|
|
3376
|
-
var
|
|
3377
|
-
var
|
|
3471
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
3472
|
+
var import_ora12 = __toESM(require("ora"));
|
|
3378
3473
|
init_admin();
|
|
3379
3474
|
async function runSpecGet(project, opts) {
|
|
3380
3475
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3388,19 +3483,19 @@ async function runSpecGet(project, opts) {
|
|
|
3388
3483
|
}
|
|
3389
3484
|
async function runSpecSet(project, opts) {
|
|
3390
3485
|
if (!opts.file) {
|
|
3391
|
-
console.error(
|
|
3486
|
+
console.error(import_chalk26.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
3392
3487
|
process.exit(1);
|
|
3393
3488
|
}
|
|
3394
3489
|
let specContent;
|
|
3395
3490
|
try {
|
|
3396
3491
|
specContent = fs6.readFileSync(opts.file, "utf-8");
|
|
3397
3492
|
} catch {
|
|
3398
|
-
console.error(
|
|
3493
|
+
console.error(import_chalk26.default.red(`Cannot read file: ${opts.file}`));
|
|
3399
3494
|
process.exit(1);
|
|
3400
3495
|
}
|
|
3401
3496
|
const { teamId } = await resolveTeam(opts.team);
|
|
3402
3497
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
3403
|
-
const spinner = (0,
|
|
3498
|
+
const spinner = (0, import_ora12.default)("Uploading spec...").start();
|
|
3404
3499
|
try {
|
|
3405
3500
|
const out = await admin({
|
|
3406
3501
|
method: "POST",
|
|
@@ -3417,12 +3512,12 @@ async function runSpecSet(project, opts) {
|
|
|
3417
3512
|
}
|
|
3418
3513
|
|
|
3419
3514
|
// src/commands/agent.ts
|
|
3420
|
-
var
|
|
3421
|
-
var
|
|
3515
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
3516
|
+
var import_ora13 = __toESM(require("ora"));
|
|
3422
3517
|
init_auth();
|
|
3423
3518
|
|
|
3424
3519
|
// src/lib/tools.ts
|
|
3425
|
-
var
|
|
3520
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
3426
3521
|
init_admin();
|
|
3427
3522
|
init_api();
|
|
3428
3523
|
async function proj(teamId, name, version2) {
|
|
@@ -3441,15 +3536,15 @@ var TOOLS = [
|
|
|
3441
3536
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
3442
3537
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
3443
3538
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
3444
|
-
const lines = [` ${
|
|
3445
|
-
if (res.devPortal) lines.push(` ${
|
|
3539
|
+
const lines = [` ${import_chalk27.default.dim("Proxy URL:")} ${import_chalk27.default.bold(url)}`];
|
|
3540
|
+
if (res.devPortal) lines.push(` ${import_chalk27.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
3446
3541
|
const envs = Object.keys(keys);
|
|
3447
3542
|
if (envs.length) {
|
|
3448
|
-
lines.push("", ` ${
|
|
3543
|
+
lines.push("", ` ${import_chalk27.default.bold("API keys")} ${import_chalk27.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
3449
3544
|
const w = Math.max(...envs.map((e) => e.length));
|
|
3450
|
-
for (const env of envs) lines.push(` ${
|
|
3545
|
+
for (const env of envs) lines.push(` ${import_chalk27.default.cyan(env.padEnd(w))} ${import_chalk27.default.green(keys[env])}`);
|
|
3451
3546
|
}
|
|
3452
|
-
if (tryIt) lines.push("", ` ${
|
|
3547
|
+
if (tryIt) lines.push("", ` ${import_chalk27.default.dim("Try it:")}`, ` ${import_chalk27.default.cyan(tryIt)}`);
|
|
3453
3548
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
3454
3549
|
}
|
|
3455
3550
|
},
|
|
@@ -3605,23 +3700,23 @@ function truncate(value, max = 1500) {
|
|
|
3605
3700
|
}
|
|
3606
3701
|
function printCost(llm) {
|
|
3607
3702
|
const usd = llm.cost > 0 ? `$${llm.cost.toFixed(4)}` : "<$0.0001";
|
|
3608
|
-
console.log(
|
|
3703
|
+
console.log(import_chalk28.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk28.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
|
|
3609
3704
|
}
|
|
3610
3705
|
async function runAgent(opts) {
|
|
3611
3706
|
requireAuth();
|
|
3612
3707
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
3613
3708
|
const { default: inquirer2 } = await import("inquirer");
|
|
3614
|
-
console.log(
|
|
3615
|
-
console.log(
|
|
3709
|
+
console.log(import_chalk28.default.bold("APIblaze agent") + import_chalk28.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
3710
|
+
console.log(import_chalk28.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
3616
3711
|
const history = [];
|
|
3617
3712
|
while (true) {
|
|
3618
|
-
const { input } = await inquirer2.prompt([{ type: "input", name: "input", message:
|
|
3713
|
+
const { input } = await inquirer2.prompt([{ type: "input", name: "input", message: import_chalk28.default.cyan("you") + " \u203A" }]);
|
|
3619
3714
|
const text = (input ?? "").trim();
|
|
3620
3715
|
if (!text) continue;
|
|
3621
3716
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
3622
3717
|
history.push({ role: "user", content: text });
|
|
3623
3718
|
for (let step = 0; step < MAX_TOOL_STEPS; step++) {
|
|
3624
|
-
const spinner = (0,
|
|
3719
|
+
const spinner = (0, import_ora13.default)({ text: "thinking...", color: "magenta" }).start();
|
|
3625
3720
|
let resp;
|
|
3626
3721
|
try {
|
|
3627
3722
|
resp = await callAgent(history, teamId);
|
|
@@ -3629,21 +3724,21 @@ async function runAgent(opts) {
|
|
|
3629
3724
|
} catch (err) {
|
|
3630
3725
|
spinner.stop();
|
|
3631
3726
|
if (err instanceof ApiError && err.status === 402) {
|
|
3632
|
-
console.log(
|
|
3727
|
+
console.log(import_chalk28.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
3633
3728
|
break;
|
|
3634
3729
|
}
|
|
3635
3730
|
throw err;
|
|
3636
3731
|
}
|
|
3637
3732
|
history.push({ role: "assistant", content: resp.raw });
|
|
3638
3733
|
printCost(resp.llm);
|
|
3639
|
-
if (resp.reply) console.log(
|
|
3734
|
+
if (resp.reply) console.log(import_chalk28.default.green("agent") + " \u203A " + resp.reply);
|
|
3640
3735
|
if (!resp.action) break;
|
|
3641
3736
|
const tool = findTool(resp.action.tool);
|
|
3642
3737
|
if (!tool) {
|
|
3643
3738
|
history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
|
|
3644
3739
|
continue;
|
|
3645
3740
|
}
|
|
3646
|
-
const runSpinner = (0,
|
|
3741
|
+
const runSpinner = (0, import_ora13.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
|
|
3647
3742
|
try {
|
|
3648
3743
|
const result = await tool.run(resp.action.args, { teamId });
|
|
3649
3744
|
runSpinner.succeed(`${tool.name} \u2713`);
|
|
@@ -3661,11 +3756,11 @@ async function runAgent(opts) {
|
|
|
3661
3756
|
}
|
|
3662
3757
|
renderTrace();
|
|
3663
3758
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
3664
|
-
console.log(
|
|
3759
|
+
console.log(import_chalk28.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
3665
3760
|
}
|
|
3666
3761
|
}
|
|
3667
3762
|
}
|
|
3668
|
-
console.log(
|
|
3763
|
+
console.log(import_chalk28.default.dim("\nBye."));
|
|
3669
3764
|
}
|
|
3670
3765
|
|
|
3671
3766
|
// src/commands/config-browse.ts
|
|
@@ -3828,11 +3923,11 @@ function dig(blob, dotted) {
|
|
|
3828
3923
|
}
|
|
3829
3924
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
3830
3925
|
function show(v) {
|
|
3831
|
-
if (v === void 0) return
|
|
3832
|
-
if (v === null) return
|
|
3833
|
-
if (typeof v === "object") return
|
|
3834
|
-
if (typeof v === "boolean") return v ?
|
|
3835
|
-
return
|
|
3926
|
+
if (v === void 0) return import_chalk29.default.dim("(unset)");
|
|
3927
|
+
if (v === null) return import_chalk29.default.dim("null");
|
|
3928
|
+
if (typeof v === "object") return import_chalk29.default.cyan(JSON.stringify(v));
|
|
3929
|
+
if (typeof v === "boolean") return v ? import_chalk29.default.green("on") : import_chalk29.default.red("off");
|
|
3930
|
+
return import_chalk29.default.cyan(String(v));
|
|
3836
3931
|
}
|
|
3837
3932
|
function parseValue(raw) {
|
|
3838
3933
|
if (raw === "true") return true;
|
|
@@ -3859,7 +3954,7 @@ async function fetchConfigBlob(proj2) {
|
|
|
3859
3954
|
}
|
|
3860
3955
|
async function patchSetting(proj2, s, value, cfg) {
|
|
3861
3956
|
const body = s.toPatch(value, cfg);
|
|
3862
|
-
const spinner = (0,
|
|
3957
|
+
const spinner = (0, import_ora14.default)(`Set ${s.key}...`).start();
|
|
3863
3958
|
try {
|
|
3864
3959
|
await admin({
|
|
3865
3960
|
method: "PATCH",
|
|
@@ -3874,10 +3969,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
3874
3969
|
}
|
|
3875
3970
|
}
|
|
3876
3971
|
var loginFirst = (what) => {
|
|
3877
|
-
console.log(
|
|
3972
|
+
console.log(import_chalk29.default.yellow(`
|
|
3878
3973
|
Log in first to ${what}.`));
|
|
3879
|
-
console.log(
|
|
3880
|
-
console.log(
|
|
3974
|
+
console.log(import_chalk29.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
|
|
3975
|
+
console.log(import_chalk29.default.dim(" anonymously and want to bring it into your account.\n"));
|
|
3881
3976
|
};
|
|
3882
3977
|
async function runConfig(project, key, value, opts) {
|
|
3883
3978
|
const creds = loadCredentials();
|
|
@@ -3894,9 +3989,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
3894
3989
|
}
|
|
3895
3990
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
3896
3991
|
if (!setting) {
|
|
3897
|
-
console.error(
|
|
3898
|
-
console.error(
|
|
3899
|
-
console.error(
|
|
3992
|
+
console.error(import_chalk29.default.red(`Unknown setting "${key}".`));
|
|
3993
|
+
console.error(import_chalk29.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
|
|
3994
|
+
console.error(import_chalk29.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
|
|
3900
3995
|
process.exit(1);
|
|
3901
3996
|
}
|
|
3902
3997
|
if (value === void 0) {
|
|
@@ -3911,7 +4006,7 @@ async function pickProject(teamId) {
|
|
|
3911
4006
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
3912
4007
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
3913
4008
|
if (!projects.length) {
|
|
3914
|
-
console.error(
|
|
4009
|
+
console.error(import_chalk29.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
3915
4010
|
process.exit(1);
|
|
3916
4011
|
}
|
|
3917
4012
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -3919,7 +4014,7 @@ async function pickProject(teamId) {
|
|
|
3919
4014
|
type: "list",
|
|
3920
4015
|
name: "picked",
|
|
3921
4016
|
message: "Which project?",
|
|
3922
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
4017
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk29.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
3923
4018
|
}]);
|
|
3924
4019
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
3925
4020
|
}
|
|
@@ -3930,25 +4025,25 @@ function printAll(proj2, cfg, json) {
|
|
|
3930
4025
|
console.log(JSON.stringify(out, null, 2));
|
|
3931
4026
|
return;
|
|
3932
4027
|
}
|
|
3933
|
-
console.log(
|
|
4028
|
+
console.log(import_chalk29.default.bold(`
|
|
3934
4029
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
3935
4030
|
`));
|
|
3936
4031
|
for (const group of SETTING_GROUPS) {
|
|
3937
|
-
console.log(
|
|
4032
|
+
console.log(import_chalk29.default.bold(group));
|
|
3938
4033
|
for (const s of SETTINGS.filter((x) => x.group === group)) {
|
|
3939
|
-
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${
|
|
4034
|
+
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk29.default.dim(s.desc)}`);
|
|
3940
4035
|
}
|
|
3941
4036
|
console.log();
|
|
3942
4037
|
}
|
|
3943
|
-
console.log(
|
|
4038
|
+
console.log(import_chalk29.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
3944
4039
|
}
|
|
3945
4040
|
async function discoveryMenu(project) {
|
|
3946
4041
|
const { default: inquirer2 } = await import("inquirer");
|
|
3947
|
-
console.log(
|
|
4042
|
+
console.log(import_chalk29.default.bold(`
|
|
3948
4043
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
3949
4044
|
`));
|
|
3950
|
-
console.log(
|
|
3951
|
-
console.log(
|
|
4045
|
+
console.log(import_chalk29.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
|
|
4046
|
+
console.log(import_chalk29.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
|
|
3952
4047
|
for (; ; ) {
|
|
3953
4048
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3954
4049
|
type: "list",
|
|
@@ -3956,13 +4051,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
3956
4051
|
message: "Explore:",
|
|
3957
4052
|
pageSize: 20,
|
|
3958
4053
|
choices: [
|
|
3959
|
-
new inquirer2.Separator(
|
|
4054
|
+
new inquirer2.Separator(import_chalk29.default.bold("\u2014 Settings \u2014")),
|
|
3960
4055
|
...SETTING_GROUPS.map((g) => ({
|
|
3961
|
-
name: `${g} ${
|
|
4056
|
+
name: `${g} ${import_chalk29.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
3962
4057
|
value: { kind: "settings", g }
|
|
3963
4058
|
})),
|
|
3964
|
-
new inquirer2.Separator(
|
|
3965
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
4059
|
+
new inquirer2.Separator(import_chalk29.default.bold("\u2014 Features \u2014")),
|
|
4060
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk29.default.dim(f.desc)}`, value: { kind: "feature", f } })),
|
|
3966
4061
|
new inquirer2.Separator(),
|
|
3967
4062
|
{ name: "Exit", value: { kind: "exit" } }
|
|
3968
4063
|
]
|
|
@@ -3971,24 +4066,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
3971
4066
|
if (pick2.kind === "settings") {
|
|
3972
4067
|
console.log();
|
|
3973
4068
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
3974
|
-
console.log(` ${
|
|
3975
|
-
console.log(` ${
|
|
4069
|
+
console.log(` ${import_chalk29.default.bold(s.label.padEnd(28))} ${import_chalk29.default.dim(s.desc)}`);
|
|
4070
|
+
console.log(` ${import_chalk29.default.dim(" key: " + s.key)}`);
|
|
3976
4071
|
}
|
|
3977
4072
|
loginFirst("view or change these settings");
|
|
3978
4073
|
} else {
|
|
3979
4074
|
const f = pick2.f;
|
|
3980
4075
|
console.log(`
|
|
3981
|
-
${
|
|
4076
|
+
${import_chalk29.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
3982
4077
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
3983
4078
|
}
|
|
3984
4079
|
}
|
|
3985
4080
|
}
|
|
3986
4081
|
async function navigator(proj2, cfg, opts) {
|
|
3987
4082
|
const { default: inquirer2 } = await import("inquirer");
|
|
3988
|
-
console.log(
|
|
4083
|
+
console.log(import_chalk29.default.bold(`
|
|
3989
4084
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
3990
4085
|
`));
|
|
3991
|
-
console.log(
|
|
4086
|
+
console.log(import_chalk29.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
|
|
3992
4087
|
let blob = cfg;
|
|
3993
4088
|
for (; ; ) {
|
|
3994
4089
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
@@ -3997,10 +4092,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
3997
4092
|
message: "Where to?",
|
|
3998
4093
|
pageSize: 20,
|
|
3999
4094
|
choices: [
|
|
4000
|
-
new inquirer2.Separator(
|
|
4095
|
+
new inquirer2.Separator(import_chalk29.default.bold("\u2014 Settings \u2014")),
|
|
4001
4096
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
4002
|
-
new inquirer2.Separator(
|
|
4003
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
4097
|
+
new inquirer2.Separator(import_chalk29.default.bold("\u2014 Features \u2014")),
|
|
4098
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk29.default.dim(f.desc)}`, value: { kind: f.go } })),
|
|
4004
4099
|
new inquirer2.Separator(),
|
|
4005
4100
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
4006
4101
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -4040,7 +4135,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4040
4135
|
}
|
|
4041
4136
|
}
|
|
4042
4137
|
} catch (err) {
|
|
4043
|
-
console.error(
|
|
4138
|
+
console.error(import_chalk29.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
4044
4139
|
}
|
|
4045
4140
|
}
|
|
4046
4141
|
}
|
|
@@ -4054,7 +4149,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
4054
4149
|
message: group + ":",
|
|
4055
4150
|
pageSize: 16,
|
|
4056
4151
|
choices: [
|
|
4057
|
-
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${
|
|
4152
|
+
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk29.default.dim(s2.desc)}`, value: s2 })),
|
|
4058
4153
|
new inquirer2.Separator(),
|
|
4059
4154
|
{ name: "\u2190 Back", value: null }
|
|
4060
4155
|
]
|
|
@@ -4072,7 +4167,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
4072
4167
|
} else if (s.type === "number") {
|
|
4073
4168
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
4074
4169
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
4075
|
-
console.log(
|
|
4170
|
+
console.log(import_chalk29.default.yellow(" Not a number \u2014 unchanged."));
|
|
4076
4171
|
continue;
|
|
4077
4172
|
}
|
|
4078
4173
|
value = Number(v);
|
|
@@ -4153,7 +4248,7 @@ async function buildCondition(phase) {
|
|
|
4153
4248
|
const items = [];
|
|
4154
4249
|
for (; ; ) {
|
|
4155
4250
|
const a = await inquirer2.prompt([
|
|
4156
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
4251
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk29.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
4157
4252
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
4158
4253
|
"eq",
|
|
4159
4254
|
"neq",
|
|
@@ -4184,7 +4279,7 @@ async function buildCondition(phase) {
|
|
|
4184
4279
|
function showCondition(cond) {
|
|
4185
4280
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
4186
4281
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
4187
|
-
return
|
|
4282
|
+
return import_chalk29.default.dim(` when ${s}`);
|
|
4188
4283
|
}
|
|
4189
4284
|
async function transformsMenu(proj2) {
|
|
4190
4285
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -4193,12 +4288,12 @@ async function transformsMenu(proj2) {
|
|
|
4193
4288
|
const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
|
|
4194
4289
|
const rules = out?.rules ?? [];
|
|
4195
4290
|
console.log();
|
|
4196
|
-
if (!rules.length) console.log(
|
|
4291
|
+
if (!rules.length) console.log(import_chalk29.default.dim(" No transform rules yet."));
|
|
4197
4292
|
for (const r of rules) {
|
|
4198
4293
|
const a = r.action ?? {};
|
|
4199
4294
|
const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
|
|
4200
|
-
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 ?
|
|
4201
|
-
console.log(` ${r.enabled ?
|
|
4295
|
+
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")}`) : ""}`;
|
|
4296
|
+
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)}`);
|
|
4202
4297
|
}
|
|
4203
4298
|
const { act } = await inquirer2.prompt([{
|
|
4204
4299
|
type: "list",
|
|
@@ -4210,7 +4305,7 @@ async function transformsMenu(proj2) {
|
|
|
4210
4305
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
4211
4306
|
{ name: "Delete a rule", value: "delete" }
|
|
4212
4307
|
] : [],
|
|
4213
|
-
{ name:
|
|
4308
|
+
{ name: import_chalk29.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
4214
4309
|
{ name: "\u2190 Back", value: "back" }
|
|
4215
4310
|
]
|
|
4216
4311
|
}]);
|
|
@@ -4223,11 +4318,11 @@ async function transformsMenu(proj2) {
|
|
|
4223
4318
|
}]);
|
|
4224
4319
|
const body = parseValue(raw);
|
|
4225
4320
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
4226
|
-
console.log(
|
|
4321
|
+
console.log(import_chalk29.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
4227
4322
|
continue;
|
|
4228
4323
|
}
|
|
4229
4324
|
await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
|
|
4230
|
-
console.log(
|
|
4325
|
+
console.log(import_chalk29.default.green(" Rule created."));
|
|
4231
4326
|
continue;
|
|
4232
4327
|
}
|
|
4233
4328
|
if (act === "add") {
|
|
@@ -4243,7 +4338,7 @@ async function transformsMenu(proj2) {
|
|
|
4243
4338
|
{ name: "Remove a field", value: "remove" }
|
|
4244
4339
|
] }
|
|
4245
4340
|
]);
|
|
4246
|
-
const fieldHint =
|
|
4341
|
+
const fieldHint = import_chalk29.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
4247
4342
|
let action2;
|
|
4248
4343
|
if (ans.kind === "hardcode") {
|
|
4249
4344
|
const a = await inquirer2.prompt([
|
|
@@ -4274,7 +4369,7 @@ async function transformsMenu(proj2) {
|
|
|
4274
4369
|
};
|
|
4275
4370
|
}
|
|
4276
4371
|
const condition = await buildCondition(ans.phase);
|
|
4277
|
-
const spinner = (0,
|
|
4372
|
+
const spinner = (0, import_ora14.default)("Creating rule...").start();
|
|
4278
4373
|
try {
|
|
4279
4374
|
await admin({
|
|
4280
4375
|
method: "POST",
|
|
@@ -4292,16 +4387,16 @@ async function transformsMenu(proj2) {
|
|
|
4292
4387
|
type: "list",
|
|
4293
4388
|
name: "rule",
|
|
4294
4389
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
4295
|
-
choices: [...rules.map((r) => ({ name: `${r.name} ${
|
|
4390
|
+
choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk29.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
|
|
4296
4391
|
}]);
|
|
4297
4392
|
if (!rule) continue;
|
|
4298
4393
|
if (act === "toggle") {
|
|
4299
4394
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
4300
4395
|
await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
4301
|
-
console.log(
|
|
4396
|
+
console.log(import_chalk29.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
4302
4397
|
} else {
|
|
4303
4398
|
await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
4304
|
-
console.log(
|
|
4399
|
+
console.log(import_chalk29.default.green(` ${rule.name} deleted.`));
|
|
4305
4400
|
}
|
|
4306
4401
|
}
|
|
4307
4402
|
}
|
|
@@ -4313,9 +4408,9 @@ async function mappingsMenu(proj2) {
|
|
|
4313
4408
|
const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
|
|
4314
4409
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
4315
4410
|
console.log();
|
|
4316
|
-
if (!tables.length) console.log(
|
|
4411
|
+
if (!tables.length) console.log(import_chalk29.default.dim(" No mapping tables yet."));
|
|
4317
4412
|
for (const t of tables) {
|
|
4318
|
-
console.log(` ${
|
|
4413
|
+
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" : ""}`)}`);
|
|
4319
4414
|
}
|
|
4320
4415
|
const { act } = await inquirer2.prompt([{
|
|
4321
4416
|
type: "list",
|
|
@@ -4335,11 +4430,11 @@ async function mappingsMenu(proj2) {
|
|
|
4335
4430
|
]);
|
|
4336
4431
|
const entries2 = parseValue(a.entries);
|
|
4337
4432
|
if (!Array.isArray(entries2)) {
|
|
4338
|
-
console.log(
|
|
4433
|
+
console.log(import_chalk29.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
4339
4434
|
continue;
|
|
4340
4435
|
}
|
|
4341
4436
|
await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
4342
|
-
console.log(
|
|
4437
|
+
console.log(import_chalk29.default.green(` Table "${a.name}" created.`));
|
|
4343
4438
|
} else {
|
|
4344
4439
|
const { table } = await inquirer2.prompt([{
|
|
4345
4440
|
type: "list",
|
|
@@ -4349,7 +4444,7 @@ async function mappingsMenu(proj2) {
|
|
|
4349
4444
|
}]);
|
|
4350
4445
|
if (!table) continue;
|
|
4351
4446
|
await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
4352
|
-
console.log(
|
|
4447
|
+
console.log(import_chalk29.default.green(` ${table.name} deleted.`));
|
|
4353
4448
|
}
|
|
4354
4449
|
}
|
|
4355
4450
|
}
|
|
@@ -4360,14 +4455,14 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4360
4455
|
const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
|
|
4361
4456
|
const tenants = out?.tenants ?? [];
|
|
4362
4457
|
console.log();
|
|
4363
|
-
if (!tenants.length) console.log(
|
|
4364
|
-
for (const t of tenants) console.log(` ${
|
|
4458
|
+
if (!tenants.length) console.log(import_chalk29.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
4459
|
+
for (const t of tenants) console.log(` ${import_chalk29.default.bold(t.tenant_name ?? t.name)} ${import_chalk29.default.dim(t.display_name ?? "")}`);
|
|
4365
4460
|
const { act } = await inquirer2.prompt([{
|
|
4366
4461
|
type: "list",
|
|
4367
4462
|
name: "act",
|
|
4368
4463
|
message: "Tenants:",
|
|
4369
4464
|
choices: [
|
|
4370
|
-
{ name: `Manage a tenant\u2026 ${
|
|
4465
|
+
{ name: `Manage a tenant\u2026 ${import_chalk29.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
|
|
4371
4466
|
{ name: "Attach a tenant to this project", value: "attach" },
|
|
4372
4467
|
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
4373
4468
|
{ name: "\u2190 Back", value: "back" }
|
|
@@ -4390,7 +4485,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4390
4485
|
}]);
|
|
4391
4486
|
if (!t) continue;
|
|
4392
4487
|
await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
4393
|
-
console.log(
|
|
4488
|
+
console.log(import_chalk29.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
4394
4489
|
}
|
|
4395
4490
|
}
|
|
4396
4491
|
}
|
|
@@ -4433,7 +4528,7 @@ async function specMenu(proj2, opts) {
|
|
|
4433
4528
|
choices: [
|
|
4434
4529
|
{ name: "Print the stored spec", value: "get" },
|
|
4435
4530
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
4436
|
-
{ name:
|
|
4531
|
+
{ name: import_chalk29.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
4437
4532
|
{ name: "\u2190 Back", value: "back" }
|
|
4438
4533
|
]
|
|
4439
4534
|
}]);
|
|
@@ -4441,7 +4536,7 @@ async function specMenu(proj2, opts) {
|
|
|
4441
4536
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
4442
4537
|
else if (act === "refresh") {
|
|
4443
4538
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
4444
|
-
console.log(
|
|
4539
|
+
console.log(import_chalk29.default.green(" Spec refresh triggered."));
|
|
4445
4540
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
4446
4541
|
}
|
|
4447
4542
|
async function agentsMenu(proj2, opts) {
|
|
@@ -4466,8 +4561,8 @@ async function agentsMenu(proj2, opts) {
|
|
|
4466
4561
|
}
|
|
4467
4562
|
|
|
4468
4563
|
// src/commands/key.ts
|
|
4469
|
-
var
|
|
4470
|
-
var
|
|
4564
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
4565
|
+
var import_ora15 = __toESM(require("ora"));
|
|
4471
4566
|
init_admin();
|
|
4472
4567
|
async function runApikeysMenu(opts) {
|
|
4473
4568
|
await runKeyList(opts);
|
|
@@ -4495,11 +4590,11 @@ async function runKeyList(opts) {
|
|
|
4495
4590
|
return;
|
|
4496
4591
|
}
|
|
4497
4592
|
if (!keys.length) {
|
|
4498
|
-
console.log(
|
|
4593
|
+
console.log(import_chalk30.default.yellow("No developer keys."));
|
|
4499
4594
|
return;
|
|
4500
4595
|
}
|
|
4501
4596
|
for (const k of keys) {
|
|
4502
|
-
console.log(` ${
|
|
4597
|
+
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")}`);
|
|
4503
4598
|
}
|
|
4504
4599
|
}
|
|
4505
4600
|
async function runKeyMint(opts) {
|
|
@@ -4507,7 +4602,7 @@ async function runKeyMint(opts) {
|
|
|
4507
4602
|
const body = { role: "consumer-admin" };
|
|
4508
4603
|
if (opts.desc) body.description = opts.desc;
|
|
4509
4604
|
if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
|
|
4510
|
-
const spinner = (0,
|
|
4605
|
+
const spinner = (0, import_ora15.default)("Minting key...").start();
|
|
4511
4606
|
try {
|
|
4512
4607
|
const out = await admin({
|
|
4513
4608
|
method: "POST",
|
|
@@ -4520,9 +4615,9 @@ async function runKeyMint(opts) {
|
|
|
4520
4615
|
console.log(JSON.stringify(out));
|
|
4521
4616
|
return;
|
|
4522
4617
|
}
|
|
4523
|
-
console.log(` ${
|
|
4524
|
-
console.log(` ${
|
|
4525
|
-
if (out?.expires_at) console.log(` ${
|
|
4618
|
+
console.log(` ${import_chalk30.default.bold("key_id")}: ${out?.key_id}`);
|
|
4619
|
+
console.log(` ${import_chalk30.default.bold("key")}: ${import_chalk30.default.green(out?.key)} ${import_chalk30.default.dim("(shown once \u2014 store it now)")}`);
|
|
4620
|
+
if (out?.expires_at) console.log(` ${import_chalk30.default.dim("expires:")} ${out.expires_at}`);
|
|
4526
4621
|
} catch (err) {
|
|
4527
4622
|
spinner.fail("Mint failed.");
|
|
4528
4623
|
throw err;
|
|
@@ -4530,7 +4625,7 @@ async function runKeyMint(opts) {
|
|
|
4530
4625
|
}
|
|
4531
4626
|
async function runKeyRevoke(keyId, opts) {
|
|
4532
4627
|
const { teamId } = await resolveTeam(opts.team);
|
|
4533
|
-
const spinner = (0,
|
|
4628
|
+
const spinner = (0, import_ora15.default)("Revoking key...").start();
|
|
4534
4629
|
try {
|
|
4535
4630
|
await admin({
|
|
4536
4631
|
method: "DELETE",
|
|
@@ -4545,8 +4640,8 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
4545
4640
|
}
|
|
4546
4641
|
|
|
4547
4642
|
// src/commands/consumer.ts
|
|
4548
|
-
var
|
|
4549
|
-
var
|
|
4643
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
4644
|
+
var import_ora16 = __toESM(require("ora"));
|
|
4550
4645
|
init_admin();
|
|
4551
4646
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
4552
4647
|
var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
|
|
@@ -4566,7 +4661,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
4566
4661
|
function requireConsumer() {
|
|
4567
4662
|
const c = loadConsumer();
|
|
4568
4663
|
if (!c) {
|
|
4569
|
-
console.error(
|
|
4664
|
+
console.error(import_chalk31.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
4570
4665
|
process.exit(1);
|
|
4571
4666
|
}
|
|
4572
4667
|
return c;
|
|
@@ -4577,7 +4672,7 @@ async function runConsumerLogin(opts) {
|
|
|
4577
4672
|
let clientId = opts.client;
|
|
4578
4673
|
if (clientId) {
|
|
4579
4674
|
if (!tenant2) {
|
|
4580
|
-
console.error(
|
|
4675
|
+
console.error(import_chalk31.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
4581
4676
|
process.exit(1);
|
|
4582
4677
|
}
|
|
4583
4678
|
} else {
|
|
@@ -4589,25 +4684,25 @@ async function runConsumerLogin(opts) {
|
|
|
4589
4684
|
if (!picked) process.exit(1);
|
|
4590
4685
|
tenant2 = picked;
|
|
4591
4686
|
}
|
|
4592
|
-
const s2 = (0,
|
|
4687
|
+
const s2 = (0, import_ora16.default)("Finding the login app...").start();
|
|
4593
4688
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
4594
4689
|
s2.stop();
|
|
4595
4690
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
4596
4691
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
4597
4692
|
if (!pick2) {
|
|
4598
|
-
console.error(
|
|
4693
|
+
console.error(import_chalk31.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
4599
4694
|
process.exit(1);
|
|
4600
4695
|
}
|
|
4601
4696
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
4602
4697
|
}
|
|
4603
4698
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
4604
|
-
console.log(`${
|
|
4699
|
+
console.log(`${import_chalk31.default.cyan("\u2192")} Logging in to ${import_chalk31.default.bold(tenant2)} as a consumer...`);
|
|
4605
4700
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
4606
4701
|
console.log(`
|
|
4607
|
-
Open: ${
|
|
4608
|
-
console.log(` Code: ${
|
|
4702
|
+
Open: ${import_chalk31.default.underline(verificationUri)}`);
|
|
4703
|
+
console.log(` Code: ${import_chalk31.default.bold(userCode)}
|
|
4609
4704
|
`);
|
|
4610
|
-
console.log(
|
|
4705
|
+
console.log(import_chalk31.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
4611
4706
|
}, portalResource);
|
|
4612
4707
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
4613
4708
|
const creds = {
|
|
@@ -4622,7 +4717,7 @@ async function runConsumerLogin(opts) {
|
|
|
4622
4717
|
obtainedAt: Date.now()
|
|
4623
4718
|
};
|
|
4624
4719
|
saveConsumer(creds);
|
|
4625
|
-
console.log(
|
|
4720
|
+
console.log(import_chalk31.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
4626
4721
|
}
|
|
4627
4722
|
async function runConsumerTokens(opts) {
|
|
4628
4723
|
const creds = requireConsumer();
|
|
@@ -4635,29 +4730,29 @@ async function runConsumerTokens(opts) {
|
|
|
4635
4730
|
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));
|
|
4636
4731
|
return;
|
|
4637
4732
|
}
|
|
4638
|
-
console.log(`${
|
|
4733
|
+
console.log(`${import_chalk31.default.cyan("Consumer")} ${import_chalk31.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk31.default.bold(fresh.tenant)}
|
|
4639
4734
|
`);
|
|
4640
|
-
console.log(`${
|
|
4735
|
+
console.log(`${import_chalk31.default.bold("access_token")} ${import_chalk31.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
4641
4736
|
${fresh.accessToken}
|
|
4642
4737
|
`);
|
|
4643
|
-
if (fresh.idToken) console.log(`${
|
|
4738
|
+
if (fresh.idToken) console.log(`${import_chalk31.default.bold("id_token")} ${import_chalk31.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
4644
4739
|
${fresh.idToken}
|
|
4645
4740
|
`);
|
|
4646
|
-
if (fresh.refreshToken) console.log(`${
|
|
4741
|
+
if (fresh.refreshToken) console.log(`${import_chalk31.default.bold("refresh_token")}
|
|
4647
4742
|
${fresh.refreshToken}
|
|
4648
4743
|
`);
|
|
4649
|
-
console.log(
|
|
4744
|
+
console.log(import_chalk31.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
4650
4745
|
}
|
|
4651
4746
|
async function runConsumerApikeys(opts) {
|
|
4652
4747
|
const creds = requireConsumer();
|
|
4653
4748
|
const { default: inquirer2 } = await import("inquirer");
|
|
4654
|
-
const spinner = (0,
|
|
4749
|
+
const spinner = (0, import_ora16.default)("Loading your API keys...").start();
|
|
4655
4750
|
const list = await consumerFetch(creds, "/apikeys");
|
|
4656
4751
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
4657
4752
|
spinner.stop();
|
|
4658
4753
|
if (list.status >= 400) {
|
|
4659
|
-
console.error(
|
|
4660
|
-
if (list.status === 401) console.error(
|
|
4754
|
+
console.error(import_chalk31.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
4755
|
+
if (list.status === 401) console.error(import_chalk31.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
4661
4756
|
process.exit(1);
|
|
4662
4757
|
}
|
|
4663
4758
|
const keys = list.data?.keys ?? [];
|
|
@@ -4665,16 +4760,16 @@ async function runConsumerApikeys(opts) {
|
|
|
4665
4760
|
if (opts.json) {
|
|
4666
4761
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
4667
4762
|
} else if (!keys.length) {
|
|
4668
|
-
console.log(
|
|
4763
|
+
console.log(import_chalk31.default.yellow("No API keys yet."));
|
|
4669
4764
|
} else {
|
|
4670
4765
|
for (const k of keys) {
|
|
4671
4766
|
const clear = revealMap[k.environment]?.key;
|
|
4672
|
-
const shown = clear ?
|
|
4673
|
-
const exp = k.expires_at ?
|
|
4674
|
-
console.log(` ${
|
|
4767
|
+
const shown = clear ? import_chalk31.default.green(clear) : import_chalk31.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
4768
|
+
const exp = k.expires_at ? import_chalk31.default.dim(`exp ${k.expires_at}`) : import_chalk31.default.dim("no expiry");
|
|
4769
|
+
console.log(` ${import_chalk31.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk31.default.dim(k.description ?? "")}`);
|
|
4675
4770
|
}
|
|
4676
4771
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
4677
|
-
console.log(
|
|
4772
|
+
console.log(import_chalk31.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
4678
4773
|
}
|
|
4679
4774
|
}
|
|
4680
4775
|
if (opts.json) return;
|
|
@@ -4688,7 +4783,7 @@ async function runConsumerApikeys(opts) {
|
|
|
4688
4783
|
const body = { environment: answers.environment };
|
|
4689
4784
|
if (answers.description) body.description = answers.description;
|
|
4690
4785
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
4691
|
-
const s2 = (0,
|
|
4786
|
+
const s2 = (0, import_ora16.default)("Creating key...").start();
|
|
4692
4787
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
4693
4788
|
if (created.status >= 400) {
|
|
4694
4789
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -4696,13 +4791,13 @@ async function runConsumerApikeys(opts) {
|
|
|
4696
4791
|
}
|
|
4697
4792
|
s2.succeed("Key created.");
|
|
4698
4793
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
4699
|
-
if (key) console.log(` ${
|
|
4700
|
-
else console.log(
|
|
4794
|
+
if (key) console.log(` ${import_chalk31.default.green(key)} ${import_chalk31.default.dim("(shown once \u2014 store it now)")}`);
|
|
4795
|
+
else console.log(import_chalk31.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
4701
4796
|
}
|
|
4702
4797
|
|
|
4703
4798
|
// src/commands/sidecar.ts
|
|
4704
|
-
var
|
|
4705
|
-
var
|
|
4799
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
4800
|
+
var import_ora17 = __toESM(require("ora"));
|
|
4706
4801
|
var fs7 = __toESM(require("fs"));
|
|
4707
4802
|
var path4 = __toESM(require("path"));
|
|
4708
4803
|
init_admin();
|
|
@@ -4743,18 +4838,18 @@ function upsertEnvLocal(root, token) {
|
|
|
4743
4838
|
}
|
|
4744
4839
|
function installSidecarPackage(root) {
|
|
4745
4840
|
if (fs7.existsSync(path4.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
4746
|
-
console.log(` ${
|
|
4841
|
+
console.log(` ${import_chalk32.default.green("\u2713")} apiblaze package already installed`);
|
|
4747
4842
|
return;
|
|
4748
4843
|
}
|
|
4749
4844
|
const has = (f) => fs7.existsSync(path4.join(root, f));
|
|
4750
4845
|
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" };
|
|
4751
|
-
const spinner = (0,
|
|
4846
|
+
const spinner = (0, import_ora17.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
4752
4847
|
try {
|
|
4753
4848
|
const { execSync } = require("child_process");
|
|
4754
4849
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
4755
4850
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
4756
4851
|
} catch {
|
|
4757
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
4852
|
+
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")}.`);
|
|
4758
4853
|
}
|
|
4759
4854
|
}
|
|
4760
4855
|
function readEnvKey(root) {
|
|
@@ -4893,7 +4988,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
4893
4988
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
4894
4989
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
4895
4990
|
if (opts.newSession) clearAnonCred2();
|
|
4896
|
-
const spinner = (0,
|
|
4991
|
+
const spinner = (0, import_ora17.default)("Setting up a sidecar (no login needed)...").start();
|
|
4897
4992
|
let out;
|
|
4898
4993
|
try {
|
|
4899
4994
|
out = await sidecarInitAnonymous2();
|
|
@@ -4905,29 +5000,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
4905
5000
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
4906
5001
|
const envState = upsertEnvLocal(root, out.token);
|
|
4907
5002
|
ensureGitignored(root);
|
|
4908
|
-
console.log(` ${
|
|
4909
|
-
console.log(` ${
|
|
5003
|
+
console.log(` ${import_chalk32.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
5004
|
+
console.log(` ${import_chalk32.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
4910
5005
|
installSidecarPackage(root);
|
|
4911
5006
|
let inspectorPath = null;
|
|
4912
5007
|
if (!opts.noInspector) {
|
|
4913
5008
|
inspectorPath = generateInspector(root, router);
|
|
4914
|
-
if (inspectorPath) console.log(` ${
|
|
5009
|
+
if (inspectorPath) console.log(` ${import_chalk32.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
4915
5010
|
}
|
|
4916
5011
|
console.log("");
|
|
4917
|
-
console.log(
|
|
4918
|
-
console.log(` 1. ${
|
|
5012
|
+
console.log(import_chalk32.default.bold("Done (no account needed). What happens next:"));
|
|
5013
|
+
console.log(` 1. ${import_chalk32.default.cyan("npm run dev")} and use your app.`);
|
|
4919
5014
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
4920
|
-
console.log(` ${
|
|
5015
|
+
console.log(` ${import_chalk32.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
4921
5016
|
console.log("");
|
|
4922
|
-
console.log(
|
|
4923
|
-
console.log(` ${
|
|
4924
|
-
console.log(
|
|
5017
|
+
console.log(import_chalk32.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
5018
|
+
console.log(` ${import_chalk32.default.cyan("apiblaze login")} then ${import_chalk32.default.cyan("apiblaze claim")} ${import_chalk32.default.dim("(no code needed here)")}`);
|
|
5019
|
+
console.log(import_chalk32.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
4925
5020
|
}
|
|
4926
5021
|
async function runSidecar(opts) {
|
|
4927
5022
|
const root = path4.resolve(opts.dir ?? process.cwd());
|
|
4928
5023
|
const detected = detectNextProject(root);
|
|
4929
5024
|
if (!detected.found) {
|
|
4930
|
-
console.log(
|
|
5025
|
+
console.log(import_chalk32.default.yellow(`No Next.js project detected in ${root}.`));
|
|
4931
5026
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
4932
5027
|
return;
|
|
4933
5028
|
}
|
|
@@ -4938,10 +5033,10 @@ async function runSidecar(opts) {
|
|
|
4938
5033
|
if (!loadCredentials()) {
|
|
4939
5034
|
upsertEnvLocal(root, readEnvKey(root));
|
|
4940
5035
|
ensureGitignored(root);
|
|
4941
|
-
console.log(` ${
|
|
4942
|
-
console.log(` ${
|
|
5036
|
+
console.log(` ${import_chalk32.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
5037
|
+
console.log(` ${import_chalk32.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
4943
5038
|
installSidecarPackage(root);
|
|
4944
|
-
console.log(
|
|
5039
|
+
console.log(import_chalk32.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
4945
5040
|
return;
|
|
4946
5041
|
}
|
|
4947
5042
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -4950,7 +5045,7 @@ async function runSidecar(opts) {
|
|
|
4950
5045
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
4951
5046
|
let token = existingKey ?? "";
|
|
4952
5047
|
if (mustMint) {
|
|
4953
|
-
const spinner = (0,
|
|
5048
|
+
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();
|
|
4954
5049
|
try {
|
|
4955
5050
|
const out = await admin({
|
|
4956
5051
|
method: "POST",
|
|
@@ -4964,39 +5059,39 @@ async function runSidecar(opts) {
|
|
|
4964
5059
|
throw err;
|
|
4965
5060
|
}
|
|
4966
5061
|
} else {
|
|
4967
|
-
console.log(
|
|
5062
|
+
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).`));
|
|
4968
5063
|
}
|
|
4969
5064
|
const envState = upsertEnvLocal(root, token);
|
|
4970
5065
|
ensureGitignored(root);
|
|
4971
|
-
console.log(` ${
|
|
5066
|
+
console.log(` ${import_chalk32.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
4972
5067
|
const wireState = wireInstrumentation(root);
|
|
4973
|
-
console.log(` ${
|
|
5068
|
+
console.log(` ${import_chalk32.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
4974
5069
|
installSidecarPackage(root);
|
|
4975
5070
|
let inspectorPath = null;
|
|
4976
5071
|
if (!opts.noInspector) {
|
|
4977
5072
|
inspectorPath = generateInspector(root, detected.router);
|
|
4978
|
-
if (inspectorPath) console.log(` ${
|
|
5073
|
+
if (inspectorPath) console.log(` ${import_chalk32.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
4979
5074
|
}
|
|
4980
5075
|
console.log("");
|
|
4981
|
-
console.log(
|
|
4982
|
-
console.log(` 1. ${
|
|
4983
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
4984
|
-
console.log(` 3. Approve the ones to route: ${
|
|
5076
|
+
console.log(import_chalk32.default.bold("Done. What happens next:"));
|
|
5077
|
+
console.log(` 1. ${import_chalk32.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
5078
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk32.default.bold("candidates")} \u2014 list them: ${import_chalk32.default.cyan("apiblaze sidecar")}`);
|
|
5079
|
+
console.log(` 3. Approve the ones to route: ${import_chalk32.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
4985
5080
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
4986
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
4987
|
-
if (switchingTeam) console.log(
|
|
5081
|
+
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)`);
|
|
5082
|
+
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>\`.`));
|
|
4988
5083
|
console.log("");
|
|
4989
|
-
console.log(
|
|
4990
|
-
console.log(
|
|
4991
|
-
console.log(
|
|
5084
|
+
console.log(import_chalk32.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
5085
|
+
console.log(import_chalk32.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
5086
|
+
console.log(import_chalk32.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
4992
5087
|
console.log("");
|
|
4993
|
-
console.log(
|
|
4994
|
-
console.log(
|
|
5088
|
+
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."));
|
|
5089
|
+
console.log(import_chalk32.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
4995
5090
|
}
|
|
4996
5091
|
|
|
4997
5092
|
// src/commands/origins.ts
|
|
4998
|
-
var
|
|
4999
|
-
var
|
|
5093
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
5094
|
+
var import_ora18 = __toESM(require("ora"));
|
|
5000
5095
|
init_admin();
|
|
5001
5096
|
init_auth();
|
|
5002
5097
|
init_anon_cred();
|
|
@@ -5005,7 +5100,7 @@ async function runOriginsList(opts) {
|
|
|
5005
5100
|
if (!loadCredentials()) {
|
|
5006
5101
|
const cred = loadAnonCred();
|
|
5007
5102
|
if (!cred) {
|
|
5008
|
-
console.log(
|
|
5103
|
+
console.log(import_chalk33.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
5009
5104
|
return;
|
|
5010
5105
|
}
|
|
5011
5106
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -5023,30 +5118,30 @@ async function runOriginsList(opts) {
|
|
|
5023
5118
|
}
|
|
5024
5119
|
const routed = out.routed ?? [];
|
|
5025
5120
|
const candidates = out.candidates ?? [];
|
|
5026
|
-
console.log(
|
|
5121
|
+
console.log(import_chalk33.default.bold(`
|
|
5027
5122
|
Routed through APIblaze (${routed.length})`));
|
|
5028
|
-
if (!routed.length) console.log(
|
|
5029
|
-
for (const r of routed) console.log(` ${
|
|
5030
|
-
console.log(
|
|
5123
|
+
if (!routed.length) console.log(import_chalk33.default.dim(" none yet"));
|
|
5124
|
+
for (const r of routed) console.log(` ${import_chalk33.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk33.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
5125
|
+
console.log(import_chalk33.default.bold(`
|
|
5031
5126
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
5032
|
-
if (!candidates.length) console.log(
|
|
5127
|
+
if (!candidates.length) console.log(import_chalk33.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
5033
5128
|
for (const c of candidates) {
|
|
5034
|
-
console.log(` ${
|
|
5129
|
+
console.log(` ${import_chalk33.default.yellow("\u25CB")} ${c.origin} ${import_chalk33.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
5035
5130
|
}
|
|
5036
5131
|
if (candidates.length) {
|
|
5037
|
-
console.log(
|
|
5132
|
+
console.log(import_chalk33.default.dim(`
|
|
5038
5133
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
5039
|
-
console.log(
|
|
5134
|
+
console.log(import_chalk33.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
5040
5135
|
}
|
|
5041
5136
|
}
|
|
5042
5137
|
async function runOriginsApprove(origin, opts) {
|
|
5043
5138
|
if (!loadCredentials()) {
|
|
5044
5139
|
const cred = loadAnonCred();
|
|
5045
5140
|
if (!cred) {
|
|
5046
|
-
console.error(
|
|
5141
|
+
console.error(import_chalk33.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
5047
5142
|
process.exit(1);
|
|
5048
5143
|
}
|
|
5049
|
-
const spinner2 = (0,
|
|
5144
|
+
const spinner2 = (0, import_ora18.default)(`Approving ${origin} (anonymous)...`).start();
|
|
5050
5145
|
try {
|
|
5051
5146
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
5052
5147
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -5057,7 +5152,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
5057
5152
|
return;
|
|
5058
5153
|
}
|
|
5059
5154
|
const { teamId } = await resolveTeam(opts.team);
|
|
5060
|
-
const spinner = (0,
|
|
5155
|
+
const spinner = (0, import_ora18.default)(`Approving ${origin}...`).start();
|
|
5061
5156
|
try {
|
|
5062
5157
|
const out = await admin({
|
|
5063
5158
|
method: "POST",
|
|
@@ -5074,7 +5169,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
5074
5169
|
}
|
|
5075
5170
|
async function runOriginsDeny(origin, opts) {
|
|
5076
5171
|
const { teamId } = await resolveTeam(opts.team);
|
|
5077
|
-
const spinner = (0,
|
|
5172
|
+
const spinner = (0, import_ora18.default)(`Dismissing ${origin}...`).start();
|
|
5078
5173
|
try {
|
|
5079
5174
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
5080
5175
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -5085,7 +5180,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
5085
5180
|
}
|
|
5086
5181
|
async function runOriginsRemove(origin, opts) {
|
|
5087
5182
|
const { teamId } = await resolveTeam(opts.team);
|
|
5088
|
-
const spinner = (0,
|
|
5183
|
+
const spinner = (0, import_ora18.default)(`Removing the proxy for ${origin}...`).start();
|
|
5089
5184
|
try {
|
|
5090
5185
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
5091
5186
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -5096,7 +5191,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
5096
5191
|
}
|
|
5097
5192
|
|
|
5098
5193
|
// src/commands/op.ts
|
|
5099
|
-
var
|
|
5194
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
5100
5195
|
init_auth();
|
|
5101
5196
|
init_trace();
|
|
5102
5197
|
init_types();
|
|
@@ -5129,82 +5224,82 @@ function printResidue(report, applied) {
|
|
|
5129
5224
|
const up = report?.upstash ?? {};
|
|
5130
5225
|
const fga = report?.fga ?? {};
|
|
5131
5226
|
const ghosts = report?.ghosts ?? {};
|
|
5132
|
-
console.log(
|
|
5133
|
-
console.log(
|
|
5227
|
+
console.log(import_chalk34.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
5228
|
+
console.log(import_chalk34.default.bold("\n Upstash"));
|
|
5134
5229
|
const orphans = up.orphans ?? [];
|
|
5135
|
-
if (orphans.length === 0) console.log(
|
|
5136
|
-
for (const o of orphans) console.log(` ${
|
|
5137
|
-
console.log(
|
|
5230
|
+
if (orphans.length === 0) console.log(import_chalk34.default.green(" no orphaned keys"));
|
|
5231
|
+
for (const o of orphans) console.log(` ${import_chalk34.default.yellow(o.key)} ${import_chalk34.default.dim(`\u2014 ${o.reason}`)}`);
|
|
5232
|
+
console.log(import_chalk34.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
5138
5233
|
if (up.anon_wallet_detail) {
|
|
5139
5234
|
const d = up.anon_wallet_detail;
|
|
5140
|
-
console.log(
|
|
5235
|
+
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)"}`));
|
|
5141
5236
|
}
|
|
5142
5237
|
if (up.keyspace_census) {
|
|
5143
5238
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
5144
|
-
console.log(
|
|
5239
|
+
console.log(import_chalk34.default.dim(` keyspace: ${census}`));
|
|
5145
5240
|
}
|
|
5146
|
-
if (up.unknown?.length) console.log(
|
|
5147
|
-
if (applied) console.log(` ${
|
|
5148
|
-
for (const e of up.errors ?? []) console.log(
|
|
5149
|
-
console.log(
|
|
5241
|
+
if (up.unknown?.length) console.log(import_chalk34.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
5242
|
+
if (applied) console.log(` ${import_chalk34.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
5243
|
+
for (const e of up.errors ?? []) console.log(import_chalk34.default.red(` error: ${e}`));
|
|
5244
|
+
console.log(import_chalk34.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
5150
5245
|
if (applied) {
|
|
5151
5246
|
const swept = fga?.swept ?? [];
|
|
5152
|
-
if (swept.length === 0) console.log(
|
|
5247
|
+
if (swept.length === 0) console.log(import_chalk34.default.green(" no orphaned stores"));
|
|
5153
5248
|
for (const s of swept) {
|
|
5154
5249
|
console.log(
|
|
5155
|
-
` ${
|
|
5250
|
+
` ${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`)}`
|
|
5156
5251
|
);
|
|
5157
5252
|
}
|
|
5158
|
-
if (fga?.remaining) console.log(
|
|
5253
|
+
if (fga?.remaining) console.log(import_chalk34.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
5159
5254
|
const st = fga?.side_tables;
|
|
5160
|
-
if (st) console.log(
|
|
5255
|
+
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})` : ""}`));
|
|
5161
5256
|
} else {
|
|
5162
5257
|
const fgaOrphans = fga?.orphans ?? [];
|
|
5163
|
-
if (fgaOrphans.length === 0) console.log(
|
|
5258
|
+
if (fgaOrphans.length === 0) console.log(import_chalk34.default.green(" no orphaned stores"));
|
|
5164
5259
|
for (const s of fgaOrphans) {
|
|
5165
5260
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
5166
|
-
console.log(` ${
|
|
5261
|
+
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)`)}`);
|
|
5167
5262
|
}
|
|
5168
|
-
console.log(
|
|
5263
|
+
console.log(import_chalk34.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
5169
5264
|
const st = fga?.side_tables;
|
|
5170
|
-
if (st) console.log(
|
|
5265
|
+
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`));
|
|
5171
5266
|
}
|
|
5172
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
5173
|
-
console.log(
|
|
5267
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk34.default.red(` error: ${e}`));
|
|
5268
|
+
console.log(import_chalk34.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
5174
5269
|
if (applied) {
|
|
5175
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
5176
|
-
else console.log(` ${
|
|
5270
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk34.default.green(" no ghost tuples"));
|
|
5271
|
+
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)`)}`);
|
|
5177
5272
|
} else {
|
|
5178
5273
|
const n = ghosts?.ghost_count ?? 0;
|
|
5179
|
-
if (n === 0) console.log(
|
|
5274
|
+
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)`)}`));
|
|
5180
5275
|
else {
|
|
5181
|
-
console.log(
|
|
5276
|
+
console.log(import_chalk34.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
5182
5277
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
5183
|
-
console.log(
|
|
5278
|
+
console.log(import_chalk34.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
5184
5279
|
}
|
|
5185
|
-
if (n > 20) console.log(
|
|
5280
|
+
if (n > 20) console.log(import_chalk34.default.dim(` \u2026 and ${n - 20} more`));
|
|
5186
5281
|
}
|
|
5187
5282
|
}
|
|
5188
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
5283
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk34.default.red(` error: ${e}`));
|
|
5189
5284
|
console.log();
|
|
5190
5285
|
}
|
|
5191
5286
|
async function runOp(sub, opts = {}) {
|
|
5192
5287
|
if (!loadCredentials()) {
|
|
5193
|
-
console.log(
|
|
5288
|
+
console.log(import_chalk34.default.dim("Not logged in. Run `apiblaze login`."));
|
|
5194
5289
|
return;
|
|
5195
5290
|
}
|
|
5196
5291
|
if (!isOperatorLogin()) {
|
|
5197
|
-
console.log(
|
|
5292
|
+
console.log(import_chalk34.default.dim("`apiblaze op` is only available to platform operators."));
|
|
5198
5293
|
return;
|
|
5199
5294
|
}
|
|
5200
5295
|
switch (sub) {
|
|
5201
5296
|
case void 0:
|
|
5202
5297
|
case "menu": {
|
|
5203
|
-
console.log(
|
|
5204
|
-
console.log(` ${
|
|
5205
|
-
console.log(` ${
|
|
5206
|
-
console.log(` ${
|
|
5207
|
-
console.log(
|
|
5298
|
+
console.log(import_chalk34.default.bold("\nOperator menu"));
|
|
5299
|
+
console.log(` ${import_chalk34.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
5300
|
+
console.log(` ${import_chalk34.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk34.default.dim("-y to skip")})`);
|
|
5301
|
+
console.log(` ${import_chalk34.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
5302
|
+
console.log(import_chalk34.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
5208
5303
|
`));
|
|
5209
5304
|
return;
|
|
5210
5305
|
}
|
|
@@ -5223,17 +5318,17 @@ async function runOp(sub, opts = {}) {
|
|
|
5223
5318
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
5224
5319
|
printResidue(report, false);
|
|
5225
5320
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
5226
|
-
console.log(
|
|
5321
|
+
console.log(import_chalk34.default.green("Nothing to sweep."));
|
|
5227
5322
|
return;
|
|
5228
5323
|
}
|
|
5229
5324
|
if (!opts.yes) {
|
|
5230
5325
|
const readline2 = await import("readline/promises");
|
|
5231
5326
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
5232
5327
|
const answer = await rl.question(
|
|
5233
|
-
|
|
5328
|
+
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: `)
|
|
5234
5329
|
);
|
|
5235
5330
|
rl.close();
|
|
5236
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
5331
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk34.default.dim("Aborted."));
|
|
5237
5332
|
}
|
|
5238
5333
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
5239
5334
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -5244,15 +5339,15 @@ async function runOp(sub, opts = {}) {
|
|
|
5244
5339
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
5245
5340
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
5246
5341
|
const accounts = data?.accounts ?? [];
|
|
5247
|
-
if (accounts.length === 0) return void console.log(
|
|
5342
|
+
if (accounts.length === 0) return void console.log(import_chalk34.default.dim("No credit wallets."));
|
|
5248
5343
|
for (const a of accounts) {
|
|
5249
5344
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
5250
|
-
console.log(` ${
|
|
5345
|
+
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") : ""}`);
|
|
5251
5346
|
}
|
|
5252
5347
|
return;
|
|
5253
5348
|
}
|
|
5254
5349
|
default:
|
|
5255
|
-
console.log(
|
|
5350
|
+
console.log(import_chalk34.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
5256
5351
|
}
|
|
5257
5352
|
}
|
|
5258
5353
|
|
|
@@ -5309,7 +5404,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
5309
5404
|
try {
|
|
5310
5405
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
5311
5406
|
if (Number.isNaN(resolved)) {
|
|
5312
|
-
console.error(
|
|
5407
|
+
console.error(import_chalk35.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
5313
5408
|
process.exit(1);
|
|
5314
5409
|
}
|
|
5315
5410
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -5407,7 +5502,7 @@ function groupedCommandHelp() {
|
|
|
5407
5502
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
5408
5503
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
5409
5504
|
}).filter(Boolean).join("\n");
|
|
5410
|
-
return `${
|
|
5505
|
+
return `${import_chalk35.default.bold(g.title)}
|
|
5411
5506
|
${rows}`;
|
|
5412
5507
|
}).join("\n\n");
|
|
5413
5508
|
}
|
|
@@ -5440,14 +5535,14 @@ async function recoverStaleTeam() {
|
|
|
5440
5535
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
5441
5536
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
5442
5537
|
if (!linked) {
|
|
5443
|
-
console.error(
|
|
5538
|
+
console.error(import_chalk35.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
5444
5539
|
return;
|
|
5445
5540
|
}
|
|
5446
5541
|
if (linked.teamId === creds.teamId) return;
|
|
5447
5542
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
5448
5543
|
delete next.activeTenant;
|
|
5449
5544
|
saveCredentials(next);
|
|
5450
|
-
console.error(
|
|
5545
|
+
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.`));
|
|
5451
5546
|
} catch {
|
|
5452
5547
|
}
|
|
5453
5548
|
}
|
|
@@ -5455,16 +5550,16 @@ async function printError(err) {
|
|
|
5455
5550
|
if (err instanceof ApiError) {
|
|
5456
5551
|
const data = err.body;
|
|
5457
5552
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
5458
|
-
console.error(
|
|
5553
|
+
console.error(import_chalk35.default.red(`
|
|
5459
5554
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
5460
5555
|
if (err.status === 403 || err.status === 404) {
|
|
5461
5556
|
await recoverStaleTeam();
|
|
5462
5557
|
}
|
|
5463
5558
|
} else if (err instanceof Error) {
|
|
5464
|
-
console.error(
|
|
5559
|
+
console.error(import_chalk35.default.red(`
|
|
5465
5560
|
Error: ${err.message}`));
|
|
5466
5561
|
} else {
|
|
5467
|
-
console.error(
|
|
5562
|
+
console.error(import_chalk35.default.red("\nUnknown error"));
|
|
5468
5563
|
}
|
|
5469
5564
|
}
|
|
5470
5565
|
program.parse(process.argv);
|