apiblaze 0.15.2 → 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 -332
- 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,8 +3013,8 @@ 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();
|
|
@@ -2936,17 +3043,17 @@ async function validScopedTenant(teamId, query) {
|
|
|
2936
3043
|
const next = { ...creds };
|
|
2937
3044
|
delete next.activeTenant;
|
|
2938
3045
|
saveCredentials2(next);
|
|
2939
|
-
console.log(
|
|
3046
|
+
console.log(import_chalk25.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
|
|
2940
3047
|
return void 0;
|
|
2941
3048
|
}
|
|
2942
3049
|
async function tenantHome(teamId, tenant2) {
|
|
2943
3050
|
const { default: inquirer2 } = await import("inquirer");
|
|
2944
3051
|
const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
2945
|
-
console.log(
|
|
3052
|
+
console.log(import_chalk25.default.bold(`
|
|
2946
3053
|
Tenant ${tenant2}`));
|
|
2947
|
-
console.log(
|
|
3054
|
+
console.log(import_chalk25.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
2948
3055
|
for (; ; ) {
|
|
2949
|
-
const spinner = (0,
|
|
3056
|
+
const spinner = (0, import_ora11.default)("Reading tenant state...").start();
|
|
2950
3057
|
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
2951
3058
|
admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
|
|
2952
3059
|
admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
|
|
@@ -2958,19 +3065,20 @@ Tenant ${tenant2}`));
|
|
|
2958
3065
|
const nEmails = (emails?.admin_emails ?? []).length;
|
|
2959
3066
|
const nIssuers = (issuers?.external_issuers ?? []).length;
|
|
2960
3067
|
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
2961
|
-
const onOff = (b) => b ?
|
|
3068
|
+
const onOff = (b) => b ? import_chalk25.default.green("on") : import_chalk25.default.dim("off");
|
|
2962
3069
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
2963
3070
|
type: "list",
|
|
2964
3071
|
name: "pick",
|
|
2965
3072
|
message: `Tenant ${tenant2}:`,
|
|
2966
3073
|
pageSize: 12,
|
|
2967
3074
|
choices: [
|
|
2968
|
-
{ name: `
|
|
2969
|
-
{ name: `
|
|
2970
|
-
{ name: `
|
|
2971
|
-
{ name: `
|
|
2972
|
-
|
|
2973
|
-
{ 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" },
|
|
2974
3082
|
{ name: "\u2190 Back", value: "back" }
|
|
2975
3083
|
]
|
|
2976
3084
|
}]);
|
|
@@ -2981,9 +3089,9 @@ Tenant ${tenant2}`));
|
|
|
2981
3089
|
await clientsMenu(teamId, tenant2, base);
|
|
2982
3090
|
break;
|
|
2983
3091
|
case "iam": {
|
|
2984
|
-
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 }]);
|
|
2985
3093
|
await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
2986
|
-
console.log(
|
|
3094
|
+
console.log(import_chalk25.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
|
|
2987
3095
|
break;
|
|
2988
3096
|
}
|
|
2989
3097
|
case "cors": {
|
|
@@ -2996,11 +3104,11 @@ Tenant ${tenant2}`));
|
|
|
2996
3104
|
if (v === "") break;
|
|
2997
3105
|
const parsed = v === "null" ? null : safeJson(v);
|
|
2998
3106
|
if (parsed === void 0) {
|
|
2999
|
-
console.log(
|
|
3107
|
+
console.log(import_chalk25.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
3000
3108
|
break;
|
|
3001
3109
|
}
|
|
3002
3110
|
await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
3003
|
-
console.log(
|
|
3111
|
+
console.log(import_chalk25.default.green(" CORS updated."));
|
|
3004
3112
|
break;
|
|
3005
3113
|
}
|
|
3006
3114
|
case "emails":
|
|
@@ -3024,7 +3132,7 @@ Tenant ${tenant2}`));
|
|
|
3024
3132
|
if (mode === "back") break;
|
|
3025
3133
|
if (mode === "clear") {
|
|
3026
3134
|
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
3027
|
-
console.log(
|
|
3135
|
+
console.log(import_chalk25.default.green(" Cleared."));
|
|
3028
3136
|
break;
|
|
3029
3137
|
}
|
|
3030
3138
|
const a = await inquirer2.prompt([
|
|
@@ -3032,7 +3140,7 @@ Tenant ${tenant2}`));
|
|
|
3032
3140
|
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
3033
3141
|
]);
|
|
3034
3142
|
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
3035
|
-
console.log(
|
|
3143
|
+
console.log(import_chalk25.default.green(" Opaque validator set."));
|
|
3036
3144
|
break;
|
|
3037
3145
|
}
|
|
3038
3146
|
}
|
|
@@ -3048,8 +3156,8 @@ function safeJson(s) {
|
|
|
3048
3156
|
async function emailsMenu(base, emails) {
|
|
3049
3157
|
const { default: inquirer2 } = await import("inquirer");
|
|
3050
3158
|
console.log();
|
|
3051
|
-
if (!emails.length) console.log(
|
|
3052
|
-
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 ?? "")}`);
|
|
3053
3161
|
const { act } = await inquirer2.prompt([{
|
|
3054
3162
|
type: "list",
|
|
3055
3163
|
name: "act",
|
|
@@ -3064,7 +3172,7 @@ async function emailsMenu(base, emails) {
|
|
|
3064
3172
|
if (act === "add") {
|
|
3065
3173
|
const { email } = await inquirer2.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
3066
3174
|
await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
3067
|
-
console.log(
|
|
3175
|
+
console.log(import_chalk25.default.green(` ${email} added.`));
|
|
3068
3176
|
} else {
|
|
3069
3177
|
const { e } = await inquirer2.prompt([{
|
|
3070
3178
|
type: "list",
|
|
@@ -3074,14 +3182,14 @@ async function emailsMenu(base, emails) {
|
|
|
3074
3182
|
}]);
|
|
3075
3183
|
if (!e) return;
|
|
3076
3184
|
await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
3077
|
-
console.log(
|
|
3185
|
+
console.log(import_chalk25.default.green(` ${e} removed.`));
|
|
3078
3186
|
}
|
|
3079
3187
|
}
|
|
3080
3188
|
async function issuersMenu(base, issuers) {
|
|
3081
3189
|
const { default: inquirer2 } = await import("inquirer");
|
|
3082
3190
|
console.log();
|
|
3083
|
-
if (!issuers.length) console.log(
|
|
3084
|
-
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 ?? "")}`);
|
|
3085
3193
|
const { act } = await inquirer2.prompt([{
|
|
3086
3194
|
type: "list",
|
|
3087
3195
|
name: "act",
|
|
@@ -3110,7 +3218,7 @@ async function issuersMenu(base, issuers) {
|
|
|
3110
3218
|
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3111
3219
|
summary: `Add external issuer ${a.iss.trim()}`
|
|
3112
3220
|
});
|
|
3113
|
-
console.log(
|
|
3221
|
+
console.log(import_chalk25.default.green(" Issuer saved."));
|
|
3114
3222
|
} else {
|
|
3115
3223
|
const { i } = await inquirer2.prompt([{
|
|
3116
3224
|
type: "list",
|
|
@@ -3124,28 +3232,28 @@ async function issuersMenu(base, issuers) {
|
|
|
3124
3232
|
path: `${base}/external-issuers?iss=${encodeURIComponent(i.iss)}&aud=${encodeURIComponent(i.aud)}`,
|
|
3125
3233
|
summary: `Delete issuer ${i.iss}`
|
|
3126
3234
|
});
|
|
3127
|
-
console.log(
|
|
3235
|
+
console.log(import_chalk25.default.green(" Issuer deleted."));
|
|
3128
3236
|
}
|
|
3129
3237
|
}
|
|
3130
3238
|
async function clientsMenu(teamId, tenant2, base) {
|
|
3131
3239
|
const { default: inquirer2 } = await import("inquirer");
|
|
3132
3240
|
for (; ; ) {
|
|
3133
|
-
const spinner = (0,
|
|
3241
|
+
const spinner = (0, import_ora11.default)("Loading app clients...").start();
|
|
3134
3242
|
const raw = await admin({ method: "GET", path: `${base}/app-clients`, summary: "List app clients" }).catch(() => []);
|
|
3135
3243
|
spinner.stop();
|
|
3136
3244
|
const clients = Array.isArray(raw) ? raw : [];
|
|
3137
3245
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3138
3246
|
type: "list",
|
|
3139
3247
|
name: "pick",
|
|
3140
|
-
message: `
|
|
3248
|
+
message: `Login methods for ${tenant2}:`,
|
|
3141
3249
|
pageSize: 15,
|
|
3142
3250
|
choices: [
|
|
3143
3251
|
...clients.map((c) => ({
|
|
3144
|
-
name: `${
|
|
3252
|
+
name: `${import_chalk25.default.bold(c.name ?? c.clientId)} ${import_chalk25.default.dim(`${c.clientId}${c.projectName ? ` \xB7 ${c.projectName}` : ""}`)}`,
|
|
3145
3253
|
value: c
|
|
3146
3254
|
})),
|
|
3147
|
-
...clients.length ? [] : [new inquirer2.Separator(
|
|
3148
|
-
{ 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" },
|
|
3149
3257
|
{ name: "\u2190 Back", value: " back" }
|
|
3150
3258
|
]
|
|
3151
3259
|
}]);
|
|
@@ -3164,7 +3272,7 @@ async function clientsMenu(teamId, tenant2, base) {
|
|
|
3164
3272
|
},
|
|
3165
3273
|
summary: `Create app client "${a.name.trim()}"`
|
|
3166
3274
|
});
|
|
3167
|
-
console.log(
|
|
3275
|
+
console.log(import_chalk25.default.green(` Login method created${created?.clientId ? ` (${created.clientId})` : ""}.`));
|
|
3168
3276
|
continue;
|
|
3169
3277
|
}
|
|
3170
3278
|
await clientHome(base, pick2);
|
|
@@ -3175,7 +3283,7 @@ async function clientHome(base, summary) {
|
|
|
3175
3283
|
const id = summary.clientId ?? summary.client_id;
|
|
3176
3284
|
const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
|
|
3177
3285
|
for (; ; ) {
|
|
3178
|
-
const spinner = (0,
|
|
3286
|
+
const spinner = (0, import_ora11.default)("Reading app client...").start();
|
|
3179
3287
|
const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
|
|
3180
3288
|
spinner.stop();
|
|
3181
3289
|
const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
|
|
@@ -3187,13 +3295,13 @@ async function clientHome(base, summary) {
|
|
|
3187
3295
|
message: `${c.name ?? id}:`,
|
|
3188
3296
|
pageSize: 12,
|
|
3189
3297
|
choices: [
|
|
3190
|
-
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${
|
|
3191
|
-
{ name: `Callback URLs: ${cb.length ?
|
|
3192
|
-
{ 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" },
|
|
3193
3301
|
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
3194
3302
|
{ name: "Reveal client secret", value: "secret" },
|
|
3195
3303
|
{ name: "Rotate client secret", value: "rotate" },
|
|
3196
|
-
{ name:
|
|
3304
|
+
{ name: import_chalk25.default.red("Delete this app client"), value: "delete" },
|
|
3197
3305
|
{ name: "\u2190 Back", value: "back" }
|
|
3198
3306
|
]
|
|
3199
3307
|
}]);
|
|
@@ -3206,13 +3314,13 @@ async function clientHome(base, summary) {
|
|
|
3206
3314
|
case "callbacks": {
|
|
3207
3315
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
3208
3316
|
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
3209
|
-
console.log(
|
|
3317
|
+
console.log(import_chalk25.default.green(" Callbacks updated."));
|
|
3210
3318
|
break;
|
|
3211
3319
|
}
|
|
3212
3320
|
case "scopes": {
|
|
3213
3321
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
3214
3322
|
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
3215
|
-
console.log(
|
|
3323
|
+
console.log(import_chalk25.default.green(" Scopes updated."));
|
|
3216
3324
|
break;
|
|
3217
3325
|
}
|
|
3218
3326
|
case "expiries": {
|
|
@@ -3227,14 +3335,14 @@ async function clientHome(base, summary) {
|
|
|
3227
3335
|
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
3228
3336
|
summary: "Update token expiries"
|
|
3229
3337
|
});
|
|
3230
|
-
console.log(
|
|
3338
|
+
console.log(import_chalk25.default.green(" Expiries updated."));
|
|
3231
3339
|
break;
|
|
3232
3340
|
}
|
|
3233
3341
|
case "secret": {
|
|
3234
3342
|
const { sure } = await inquirer2.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
3235
3343
|
if (!sure) break;
|
|
3236
3344
|
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
3237
|
-
console.log(` ${
|
|
3345
|
+
console.log(` ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3238
3346
|
break;
|
|
3239
3347
|
}
|
|
3240
3348
|
case "rotate": {
|
|
@@ -3242,14 +3350,14 @@ async function clientHome(base, summary) {
|
|
|
3242
3350
|
if (!sure) break;
|
|
3243
3351
|
const fresh = randomSecret();
|
|
3244
3352
|
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
3245
|
-
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)")}`);
|
|
3246
3354
|
break;
|
|
3247
3355
|
}
|
|
3248
3356
|
case "delete": {
|
|
3249
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 }]);
|
|
3250
3358
|
if (!sure) break;
|
|
3251
3359
|
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
3252
|
-
console.log(
|
|
3360
|
+
console.log(import_chalk25.default.green(" App client deleted."));
|
|
3253
3361
|
return;
|
|
3254
3362
|
}
|
|
3255
3363
|
}
|
|
@@ -3261,7 +3369,7 @@ function randomSecret() {
|
|
|
3261
3369
|
return Buffer.from(bytes).toString("base64url");
|
|
3262
3370
|
}
|
|
3263
3371
|
var PROVIDER_TYPES = ["google", "github", "microsoft", "facebook", "auth0", "other"];
|
|
3264
|
-
var
|
|
3372
|
+
var DEFAULT_SCOPES2 = {
|
|
3265
3373
|
google: "openid email profile",
|
|
3266
3374
|
microsoft: "openid email profile",
|
|
3267
3375
|
github: "read:user user:email",
|
|
@@ -3270,19 +3378,19 @@ var DEFAULT_SCOPES = {
|
|
|
3270
3378
|
async function providersMenu(cBase, clientLabel) {
|
|
3271
3379
|
const { default: inquirer2 } = await import("inquirer");
|
|
3272
3380
|
for (; ; ) {
|
|
3273
|
-
const spinner = (0,
|
|
3381
|
+
const spinner = (0, import_ora11.default)("Loading providers...").start();
|
|
3274
3382
|
const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
|
|
3275
3383
|
spinner.stop();
|
|
3276
3384
|
const providers = Array.isArray(raw) ? raw : [];
|
|
3277
3385
|
console.log();
|
|
3278
3386
|
for (const p of providers) {
|
|
3279
|
-
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" : ""}`)}`);
|
|
3280
3388
|
}
|
|
3281
|
-
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."));
|
|
3282
3390
|
const { act } = await inquirer2.prompt([{
|
|
3283
3391
|
type: "list",
|
|
3284
3392
|
name: "act",
|
|
3285
|
-
message: `
|
|
3393
|
+
message: `Sign-in providers for ${clientLabel}:`,
|
|
3286
3394
|
choices: [
|
|
3287
3395
|
{ name: "\uFF0B Add a provider", value: "add" },
|
|
3288
3396
|
...providers.length ? [
|
|
@@ -3310,7 +3418,7 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3310
3418
|
{ type: "input", name: "clientId", message: `${type} OAuth client id:`, validate: (s) => !!s.trim() || "required" },
|
|
3311
3419
|
{ type: "password", name: "clientSecret", mask: "*", message: `${type} OAuth client secret:`, validate: (s) => s.length >= 6 && s.length <= 200 || "6\u2013200 chars" },
|
|
3312
3420
|
...type === "auth0" || type === "other" ? [{ type: "input", name: "domain", message: "Issuer / domain (e.g. your-tenant.auth0.com):" }] : [],
|
|
3313
|
-
{ type: "input", name: "scopes", message: "Scopes:", default:
|
|
3421
|
+
{ type: "input", name: "scopes", message: "Scopes:", default: DEFAULT_SCOPES2[type] ?? "" }
|
|
3314
3422
|
]);
|
|
3315
3423
|
body = {
|
|
3316
3424
|
type,
|
|
@@ -3336,23 +3444,23 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3336
3444
|
body.targetServerToken = routing;
|
|
3337
3445
|
}
|
|
3338
3446
|
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
3339
|
-
console.log(
|
|
3447
|
+
console.log(import_chalk25.default.green(` ${type} provider added.`));
|
|
3340
3448
|
} else {
|
|
3341
3449
|
const { p } = await inquirer2.prompt([{
|
|
3342
3450
|
type: "list",
|
|
3343
3451
|
name: "p",
|
|
3344
3452
|
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
3345
|
-
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 }]
|
|
3346
3454
|
}]);
|
|
3347
3455
|
if (!p) continue;
|
|
3348
3456
|
if (act === "rm") {
|
|
3349
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 }]);
|
|
3350
3458
|
if (!sure) continue;
|
|
3351
3459
|
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
3352
|
-
console.log(
|
|
3460
|
+
console.log(import_chalk25.default.green(` ${p.type} removed.`));
|
|
3353
3461
|
} else {
|
|
3354
3462
|
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
3355
|
-
console.log(` ${
|
|
3463
|
+
console.log(` ${import_chalk25.default.bold("client_secret")}: ${import_chalk25.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3356
3464
|
}
|
|
3357
3465
|
}
|
|
3358
3466
|
}
|
|
@@ -3360,8 +3468,8 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3360
3468
|
|
|
3361
3469
|
// src/commands/spec.ts
|
|
3362
3470
|
var fs6 = __toESM(require("fs"));
|
|
3363
|
-
var
|
|
3364
|
-
var
|
|
3471
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
3472
|
+
var import_ora12 = __toESM(require("ora"));
|
|
3365
3473
|
init_admin();
|
|
3366
3474
|
async function runSpecGet(project, opts) {
|
|
3367
3475
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3375,19 +3483,19 @@ async function runSpecGet(project, opts) {
|
|
|
3375
3483
|
}
|
|
3376
3484
|
async function runSpecSet(project, opts) {
|
|
3377
3485
|
if (!opts.file) {
|
|
3378
|
-
console.error(
|
|
3486
|
+
console.error(import_chalk26.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
3379
3487
|
process.exit(1);
|
|
3380
3488
|
}
|
|
3381
3489
|
let specContent;
|
|
3382
3490
|
try {
|
|
3383
3491
|
specContent = fs6.readFileSync(opts.file, "utf-8");
|
|
3384
3492
|
} catch {
|
|
3385
|
-
console.error(
|
|
3493
|
+
console.error(import_chalk26.default.red(`Cannot read file: ${opts.file}`));
|
|
3386
3494
|
process.exit(1);
|
|
3387
3495
|
}
|
|
3388
3496
|
const { teamId } = await resolveTeam(opts.team);
|
|
3389
3497
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
3390
|
-
const spinner = (0,
|
|
3498
|
+
const spinner = (0, import_ora12.default)("Uploading spec...").start();
|
|
3391
3499
|
try {
|
|
3392
3500
|
const out = await admin({
|
|
3393
3501
|
method: "POST",
|
|
@@ -3404,12 +3512,12 @@ async function runSpecSet(project, opts) {
|
|
|
3404
3512
|
}
|
|
3405
3513
|
|
|
3406
3514
|
// src/commands/agent.ts
|
|
3407
|
-
var
|
|
3408
|
-
var
|
|
3515
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
3516
|
+
var import_ora13 = __toESM(require("ora"));
|
|
3409
3517
|
init_auth();
|
|
3410
3518
|
|
|
3411
3519
|
// src/lib/tools.ts
|
|
3412
|
-
var
|
|
3520
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
3413
3521
|
init_admin();
|
|
3414
3522
|
init_api();
|
|
3415
3523
|
async function proj(teamId, name, version2) {
|
|
@@ -3428,15 +3536,15 @@ var TOOLS = [
|
|
|
3428
3536
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
3429
3537
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
3430
3538
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
3431
|
-
const lines = [` ${
|
|
3432
|
-
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}`);
|
|
3433
3541
|
const envs = Object.keys(keys);
|
|
3434
3542
|
if (envs.length) {
|
|
3435
|
-
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):")}`);
|
|
3436
3544
|
const w = Math.max(...envs.map((e) => e.length));
|
|
3437
|
-
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])}`);
|
|
3438
3546
|
}
|
|
3439
|
-
if (tryIt) lines.push("", ` ${
|
|
3547
|
+
if (tryIt) lines.push("", ` ${import_chalk27.default.dim("Try it:")}`, ` ${import_chalk27.default.cyan(tryIt)}`);
|
|
3440
3548
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
3441
3549
|
}
|
|
3442
3550
|
},
|
|
@@ -3592,23 +3700,23 @@ function truncate(value, max = 1500) {
|
|
|
3592
3700
|
}
|
|
3593
3701
|
function printCost(llm) {
|
|
3594
3702
|
const usd = llm.cost > 0 ? `$${llm.cost.toFixed(4)}` : "<$0.0001";
|
|
3595
|
-
console.log(
|
|
3703
|
+
console.log(import_chalk28.default.magenta(` \u{1F4B3} ${usd}`) + import_chalk28.default.dim(` (${llm.model}, ${llm.total_tokens} tok)`));
|
|
3596
3704
|
}
|
|
3597
3705
|
async function runAgent(opts) {
|
|
3598
3706
|
requireAuth();
|
|
3599
3707
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
3600
3708
|
const { default: inquirer2 } = await import("inquirer");
|
|
3601
|
-
console.log(
|
|
3602
|
-
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'));
|
|
3603
3711
|
const history = [];
|
|
3604
3712
|
while (true) {
|
|
3605
|
-
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" }]);
|
|
3606
3714
|
const text = (input ?? "").trim();
|
|
3607
3715
|
if (!text) continue;
|
|
3608
3716
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
3609
3717
|
history.push({ role: "user", content: text });
|
|
3610
3718
|
for (let step = 0; step < MAX_TOOL_STEPS; step++) {
|
|
3611
|
-
const spinner = (0,
|
|
3719
|
+
const spinner = (0, import_ora13.default)({ text: "thinking...", color: "magenta" }).start();
|
|
3612
3720
|
let resp;
|
|
3613
3721
|
try {
|
|
3614
3722
|
resp = await callAgent(history, teamId);
|
|
@@ -3616,21 +3724,21 @@ async function runAgent(opts) {
|
|
|
3616
3724
|
} catch (err) {
|
|
3617
3725
|
spinner.stop();
|
|
3618
3726
|
if (err instanceof ApiError && err.status === 402) {
|
|
3619
|
-
console.log(
|
|
3727
|
+
console.log(import_chalk28.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
3620
3728
|
break;
|
|
3621
3729
|
}
|
|
3622
3730
|
throw err;
|
|
3623
3731
|
}
|
|
3624
3732
|
history.push({ role: "assistant", content: resp.raw });
|
|
3625
3733
|
printCost(resp.llm);
|
|
3626
|
-
if (resp.reply) console.log(
|
|
3734
|
+
if (resp.reply) console.log(import_chalk28.default.green("agent") + " \u203A " + resp.reply);
|
|
3627
3735
|
if (!resp.action) break;
|
|
3628
3736
|
const tool = findTool(resp.action.tool);
|
|
3629
3737
|
if (!tool) {
|
|
3630
3738
|
history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
|
|
3631
3739
|
continue;
|
|
3632
3740
|
}
|
|
3633
|
-
const runSpinner = (0,
|
|
3741
|
+
const runSpinner = (0, import_ora13.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
|
|
3634
3742
|
try {
|
|
3635
3743
|
const result = await tool.run(resp.action.args, { teamId });
|
|
3636
3744
|
runSpinner.succeed(`${tool.name} \u2713`);
|
|
@@ -3648,11 +3756,11 @@ async function runAgent(opts) {
|
|
|
3648
3756
|
}
|
|
3649
3757
|
renderTrace();
|
|
3650
3758
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
3651
|
-
console.log(
|
|
3759
|
+
console.log(import_chalk28.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
3652
3760
|
}
|
|
3653
3761
|
}
|
|
3654
3762
|
}
|
|
3655
|
-
console.log(
|
|
3763
|
+
console.log(import_chalk28.default.dim("\nBye."));
|
|
3656
3764
|
}
|
|
3657
3765
|
|
|
3658
3766
|
// src/commands/config-browse.ts
|
|
@@ -3815,11 +3923,11 @@ function dig(blob, dotted) {
|
|
|
3815
3923
|
}
|
|
3816
3924
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
3817
3925
|
function show(v) {
|
|
3818
|
-
if (v === void 0) return
|
|
3819
|
-
if (v === null) return
|
|
3820
|
-
if (typeof v === "object") return
|
|
3821
|
-
if (typeof v === "boolean") return v ?
|
|
3822
|
-
return
|
|
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));
|
|
3823
3931
|
}
|
|
3824
3932
|
function parseValue(raw) {
|
|
3825
3933
|
if (raw === "true") return true;
|
|
@@ -3846,7 +3954,7 @@ async function fetchConfigBlob(proj2) {
|
|
|
3846
3954
|
}
|
|
3847
3955
|
async function patchSetting(proj2, s, value, cfg) {
|
|
3848
3956
|
const body = s.toPatch(value, cfg);
|
|
3849
|
-
const spinner = (0,
|
|
3957
|
+
const spinner = (0, import_ora14.default)(`Set ${s.key}...`).start();
|
|
3850
3958
|
try {
|
|
3851
3959
|
await admin({
|
|
3852
3960
|
method: "PATCH",
|
|
@@ -3861,10 +3969,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
3861
3969
|
}
|
|
3862
3970
|
}
|
|
3863
3971
|
var loginFirst = (what) => {
|
|
3864
|
-
console.log(
|
|
3972
|
+
console.log(import_chalk29.default.yellow(`
|
|
3865
3973
|
Log in first to ${what}.`));
|
|
3866
|
-
console.log(
|
|
3867
|
-
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"));
|
|
3868
3976
|
};
|
|
3869
3977
|
async function runConfig(project, key, value, opts) {
|
|
3870
3978
|
const creds = loadCredentials();
|
|
@@ -3881,9 +3989,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
3881
3989
|
}
|
|
3882
3990
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
3883
3991
|
if (!setting) {
|
|
3884
|
-
console.error(
|
|
3885
|
-
console.error(
|
|
3886
|
-
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>`.)"));
|
|
3887
3995
|
process.exit(1);
|
|
3888
3996
|
}
|
|
3889
3997
|
if (value === void 0) {
|
|
@@ -3898,7 +4006,7 @@ async function pickProject(teamId) {
|
|
|
3898
4006
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
3899
4007
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
3900
4008
|
if (!projects.length) {
|
|
3901
|
-
console.error(
|
|
4009
|
+
console.error(import_chalk29.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
3902
4010
|
process.exit(1);
|
|
3903
4011
|
}
|
|
3904
4012
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -3906,7 +4014,7 @@ async function pickProject(teamId) {
|
|
|
3906
4014
|
type: "list",
|
|
3907
4015
|
name: "picked",
|
|
3908
4016
|
message: "Which project?",
|
|
3909
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
4017
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk29.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
3910
4018
|
}]);
|
|
3911
4019
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
3912
4020
|
}
|
|
@@ -3917,25 +4025,25 @@ function printAll(proj2, cfg, json) {
|
|
|
3917
4025
|
console.log(JSON.stringify(out, null, 2));
|
|
3918
4026
|
return;
|
|
3919
4027
|
}
|
|
3920
|
-
console.log(
|
|
4028
|
+
console.log(import_chalk29.default.bold(`
|
|
3921
4029
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
3922
4030
|
`));
|
|
3923
4031
|
for (const group of SETTING_GROUPS) {
|
|
3924
|
-
console.log(
|
|
4032
|
+
console.log(import_chalk29.default.bold(group));
|
|
3925
4033
|
for (const s of SETTINGS.filter((x) => x.group === group)) {
|
|
3926
|
-
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)}`);
|
|
3927
4035
|
}
|
|
3928
4036
|
console.log();
|
|
3929
4037
|
}
|
|
3930
|
-
console.log(
|
|
4038
|
+
console.log(import_chalk29.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
3931
4039
|
}
|
|
3932
4040
|
async function discoveryMenu(project) {
|
|
3933
4041
|
const { default: inquirer2 } = await import("inquirer");
|
|
3934
|
-
console.log(
|
|
4042
|
+
console.log(import_chalk29.default.bold(`
|
|
3935
4043
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
3936
4044
|
`));
|
|
3937
|
-
console.log(
|
|
3938
|
-
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"));
|
|
3939
4047
|
for (; ; ) {
|
|
3940
4048
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
3941
4049
|
type: "list",
|
|
@@ -3943,13 +4051,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
3943
4051
|
message: "Explore:",
|
|
3944
4052
|
pageSize: 20,
|
|
3945
4053
|
choices: [
|
|
3946
|
-
new inquirer2.Separator(
|
|
4054
|
+
new inquirer2.Separator(import_chalk29.default.bold("\u2014 Settings \u2014")),
|
|
3947
4055
|
...SETTING_GROUPS.map((g) => ({
|
|
3948
|
-
name: `${g} ${
|
|
4056
|
+
name: `${g} ${import_chalk29.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
3949
4057
|
value: { kind: "settings", g }
|
|
3950
4058
|
})),
|
|
3951
|
-
new inquirer2.Separator(
|
|
3952
|
-
...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 } })),
|
|
3953
4061
|
new inquirer2.Separator(),
|
|
3954
4062
|
{ name: "Exit", value: { kind: "exit" } }
|
|
3955
4063
|
]
|
|
@@ -3958,24 +4066,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
3958
4066
|
if (pick2.kind === "settings") {
|
|
3959
4067
|
console.log();
|
|
3960
4068
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
3961
|
-
console.log(` ${
|
|
3962
|
-
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)}`);
|
|
3963
4071
|
}
|
|
3964
4072
|
loginFirst("view or change these settings");
|
|
3965
4073
|
} else {
|
|
3966
4074
|
const f = pick2.f;
|
|
3967
4075
|
console.log(`
|
|
3968
|
-
${
|
|
4076
|
+
${import_chalk29.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
3969
4077
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
3970
4078
|
}
|
|
3971
4079
|
}
|
|
3972
4080
|
}
|
|
3973
4081
|
async function navigator(proj2, cfg, opts) {
|
|
3974
4082
|
const { default: inquirer2 } = await import("inquirer");
|
|
3975
|
-
console.log(
|
|
4083
|
+
console.log(import_chalk29.default.bold(`
|
|
3976
4084
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
3977
4085
|
`));
|
|
3978
|
-
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"));
|
|
3979
4087
|
let blob = cfg;
|
|
3980
4088
|
for (; ; ) {
|
|
3981
4089
|
const { pick: pick2 } = await inquirer2.prompt([{
|
|
@@ -3984,10 +4092,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
3984
4092
|
message: "Where to?",
|
|
3985
4093
|
pageSize: 20,
|
|
3986
4094
|
choices: [
|
|
3987
|
-
new inquirer2.Separator(
|
|
4095
|
+
new inquirer2.Separator(import_chalk29.default.bold("\u2014 Settings \u2014")),
|
|
3988
4096
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
3989
|
-
new inquirer2.Separator(
|
|
3990
|
-
...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 } })),
|
|
3991
4099
|
new inquirer2.Separator(),
|
|
3992
4100
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
3993
4101
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -4027,7 +4135,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4027
4135
|
}
|
|
4028
4136
|
}
|
|
4029
4137
|
} catch (err) {
|
|
4030
|
-
console.error(
|
|
4138
|
+
console.error(import_chalk29.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
4031
4139
|
}
|
|
4032
4140
|
}
|
|
4033
4141
|
}
|
|
@@ -4041,7 +4149,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
4041
4149
|
message: group + ":",
|
|
4042
4150
|
pageSize: 16,
|
|
4043
4151
|
choices: [
|
|
4044
|
-
...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 })),
|
|
4045
4153
|
new inquirer2.Separator(),
|
|
4046
4154
|
{ name: "\u2190 Back", value: null }
|
|
4047
4155
|
]
|
|
@@ -4059,7 +4167,7 @@ async function settingsGroup(proj2, cfg, group) {
|
|
|
4059
4167
|
} else if (s.type === "number") {
|
|
4060
4168
|
const { v } = await inquirer2.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
4061
4169
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
4062
|
-
console.log(
|
|
4170
|
+
console.log(import_chalk29.default.yellow(" Not a number \u2014 unchanged."));
|
|
4063
4171
|
continue;
|
|
4064
4172
|
}
|
|
4065
4173
|
value = Number(v);
|
|
@@ -4140,7 +4248,7 @@ async function buildCondition(phase) {
|
|
|
4140
4248
|
const items = [];
|
|
4141
4249
|
for (; ; ) {
|
|
4142
4250
|
const a = await inquirer2.prompt([
|
|
4143
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
4251
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk29.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
4144
4252
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
4145
4253
|
"eq",
|
|
4146
4254
|
"neq",
|
|
@@ -4171,7 +4279,7 @@ async function buildCondition(phase) {
|
|
|
4171
4279
|
function showCondition(cond) {
|
|
4172
4280
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
4173
4281
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
4174
|
-
return
|
|
4282
|
+
return import_chalk29.default.dim(` when ${s}`);
|
|
4175
4283
|
}
|
|
4176
4284
|
async function transformsMenu(proj2) {
|
|
4177
4285
|
const { default: inquirer2 } = await import("inquirer");
|
|
@@ -4180,12 +4288,12 @@ async function transformsMenu(proj2) {
|
|
|
4180
4288
|
const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
|
|
4181
4289
|
const rules = out?.rules ?? [];
|
|
4182
4290
|
console.log();
|
|
4183
|
-
if (!rules.length) console.log(
|
|
4291
|
+
if (!rules.length) console.log(import_chalk29.default.dim(" No transform rules yet."));
|
|
4184
4292
|
for (const r of rules) {
|
|
4185
4293
|
const a = r.action ?? {};
|
|
4186
4294
|
const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
|
|
4187
|
-
const what = a.type === "hardcode" ? `${a.destination} = "${a.value}"` : a.type === "remove" ? `remove ${a.field}` : `${a.source} \u2192 ${a.destination}${a.lookup ? " (mapped)" : ""}${fns.length ?
|
|
4188
|
-
console.log(` ${r.enabled ?
|
|
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)}`);
|
|
4189
4297
|
}
|
|
4190
4298
|
const { act } = await inquirer2.prompt([{
|
|
4191
4299
|
type: "list",
|
|
@@ -4197,7 +4305,7 @@ async function transformsMenu(proj2) {
|
|
|
4197
4305
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
4198
4306
|
{ name: "Delete a rule", value: "delete" }
|
|
4199
4307
|
] : [],
|
|
4200
|
-
{ name:
|
|
4308
|
+
{ name: import_chalk29.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
4201
4309
|
{ name: "\u2190 Back", value: "back" }
|
|
4202
4310
|
]
|
|
4203
4311
|
}]);
|
|
@@ -4210,11 +4318,11 @@ async function transformsMenu(proj2) {
|
|
|
4210
4318
|
}]);
|
|
4211
4319
|
const body = parseValue(raw);
|
|
4212
4320
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
4213
|
-
console.log(
|
|
4321
|
+
console.log(import_chalk29.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
4214
4322
|
continue;
|
|
4215
4323
|
}
|
|
4216
4324
|
await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
|
|
4217
|
-
console.log(
|
|
4325
|
+
console.log(import_chalk29.default.green(" Rule created."));
|
|
4218
4326
|
continue;
|
|
4219
4327
|
}
|
|
4220
4328
|
if (act === "add") {
|
|
@@ -4230,7 +4338,7 @@ async function transformsMenu(proj2) {
|
|
|
4230
4338
|
{ name: "Remove a field", value: "remove" }
|
|
4231
4339
|
] }
|
|
4232
4340
|
]);
|
|
4233
|
-
const fieldHint =
|
|
4341
|
+
const fieldHint = import_chalk29.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
4234
4342
|
let action2;
|
|
4235
4343
|
if (ans.kind === "hardcode") {
|
|
4236
4344
|
const a = await inquirer2.prompt([
|
|
@@ -4261,7 +4369,7 @@ async function transformsMenu(proj2) {
|
|
|
4261
4369
|
};
|
|
4262
4370
|
}
|
|
4263
4371
|
const condition = await buildCondition(ans.phase);
|
|
4264
|
-
const spinner = (0,
|
|
4372
|
+
const spinner = (0, import_ora14.default)("Creating rule...").start();
|
|
4265
4373
|
try {
|
|
4266
4374
|
await admin({
|
|
4267
4375
|
method: "POST",
|
|
@@ -4279,16 +4387,16 @@ async function transformsMenu(proj2) {
|
|
|
4279
4387
|
type: "list",
|
|
4280
4388
|
name: "rule",
|
|
4281
4389
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
4282
|
-
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 }]
|
|
4283
4391
|
}]);
|
|
4284
4392
|
if (!rule) continue;
|
|
4285
4393
|
if (act === "toggle") {
|
|
4286
4394
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
4287
4395
|
await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
4288
|
-
console.log(
|
|
4396
|
+
console.log(import_chalk29.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
4289
4397
|
} else {
|
|
4290
4398
|
await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
4291
|
-
console.log(
|
|
4399
|
+
console.log(import_chalk29.default.green(` ${rule.name} deleted.`));
|
|
4292
4400
|
}
|
|
4293
4401
|
}
|
|
4294
4402
|
}
|
|
@@ -4300,9 +4408,9 @@ async function mappingsMenu(proj2) {
|
|
|
4300
4408
|
const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
|
|
4301
4409
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
4302
4410
|
console.log();
|
|
4303
|
-
if (!tables.length) console.log(
|
|
4411
|
+
if (!tables.length) console.log(import_chalk29.default.dim(" No mapping tables yet."));
|
|
4304
4412
|
for (const t of tables) {
|
|
4305
|
-
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" : ""}`)}`);
|
|
4306
4414
|
}
|
|
4307
4415
|
const { act } = await inquirer2.prompt([{
|
|
4308
4416
|
type: "list",
|
|
@@ -4322,11 +4430,11 @@ async function mappingsMenu(proj2) {
|
|
|
4322
4430
|
]);
|
|
4323
4431
|
const entries2 = parseValue(a.entries);
|
|
4324
4432
|
if (!Array.isArray(entries2)) {
|
|
4325
|
-
console.log(
|
|
4433
|
+
console.log(import_chalk29.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
4326
4434
|
continue;
|
|
4327
4435
|
}
|
|
4328
4436
|
await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
4329
|
-
console.log(
|
|
4437
|
+
console.log(import_chalk29.default.green(` Table "${a.name}" created.`));
|
|
4330
4438
|
} else {
|
|
4331
4439
|
const { table } = await inquirer2.prompt([{
|
|
4332
4440
|
type: "list",
|
|
@@ -4336,7 +4444,7 @@ async function mappingsMenu(proj2) {
|
|
|
4336
4444
|
}]);
|
|
4337
4445
|
if (!table) continue;
|
|
4338
4446
|
await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
4339
|
-
console.log(
|
|
4447
|
+
console.log(import_chalk29.default.green(` ${table.name} deleted.`));
|
|
4340
4448
|
}
|
|
4341
4449
|
}
|
|
4342
4450
|
}
|
|
@@ -4347,14 +4455,14 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4347
4455
|
const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
|
|
4348
4456
|
const tenants = out?.tenants ?? [];
|
|
4349
4457
|
console.log();
|
|
4350
|
-
if (!tenants.length) console.log(
|
|
4351
|
-
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 ?? "")}`);
|
|
4352
4460
|
const { act } = await inquirer2.prompt([{
|
|
4353
4461
|
type: "list",
|
|
4354
4462
|
name: "act",
|
|
4355
4463
|
message: "Tenants:",
|
|
4356
4464
|
choices: [
|
|
4357
|
-
{ 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" },
|
|
4358
4466
|
{ name: "Attach a tenant to this project", value: "attach" },
|
|
4359
4467
|
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
4360
4468
|
{ name: "\u2190 Back", value: "back" }
|
|
@@ -4377,7 +4485,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4377
4485
|
}]);
|
|
4378
4486
|
if (!t) continue;
|
|
4379
4487
|
await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
4380
|
-
console.log(
|
|
4488
|
+
console.log(import_chalk29.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
4381
4489
|
}
|
|
4382
4490
|
}
|
|
4383
4491
|
}
|
|
@@ -4420,7 +4528,7 @@ async function specMenu(proj2, opts) {
|
|
|
4420
4528
|
choices: [
|
|
4421
4529
|
{ name: "Print the stored spec", value: "get" },
|
|
4422
4530
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
4423
|
-
{ name:
|
|
4531
|
+
{ name: import_chalk29.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
4424
4532
|
{ name: "\u2190 Back", value: "back" }
|
|
4425
4533
|
]
|
|
4426
4534
|
}]);
|
|
@@ -4428,7 +4536,7 @@ async function specMenu(proj2, opts) {
|
|
|
4428
4536
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
4429
4537
|
else if (act === "refresh") {
|
|
4430
4538
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
4431
|
-
console.log(
|
|
4539
|
+
console.log(import_chalk29.default.green(" Spec refresh triggered."));
|
|
4432
4540
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
4433
4541
|
}
|
|
4434
4542
|
async function agentsMenu(proj2, opts) {
|
|
@@ -4453,8 +4561,8 @@ async function agentsMenu(proj2, opts) {
|
|
|
4453
4561
|
}
|
|
4454
4562
|
|
|
4455
4563
|
// src/commands/key.ts
|
|
4456
|
-
var
|
|
4457
|
-
var
|
|
4564
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
4565
|
+
var import_ora15 = __toESM(require("ora"));
|
|
4458
4566
|
init_admin();
|
|
4459
4567
|
async function runApikeysMenu(opts) {
|
|
4460
4568
|
await runKeyList(opts);
|
|
@@ -4482,11 +4590,11 @@ async function runKeyList(opts) {
|
|
|
4482
4590
|
return;
|
|
4483
4591
|
}
|
|
4484
4592
|
if (!keys.length) {
|
|
4485
|
-
console.log(
|
|
4593
|
+
console.log(import_chalk30.default.yellow("No developer keys."));
|
|
4486
4594
|
return;
|
|
4487
4595
|
}
|
|
4488
4596
|
for (const k of keys) {
|
|
4489
|
-
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")}`);
|
|
4490
4598
|
}
|
|
4491
4599
|
}
|
|
4492
4600
|
async function runKeyMint(opts) {
|
|
@@ -4494,7 +4602,7 @@ async function runKeyMint(opts) {
|
|
|
4494
4602
|
const body = { role: "consumer-admin" };
|
|
4495
4603
|
if (opts.desc) body.description = opts.desc;
|
|
4496
4604
|
if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
|
|
4497
|
-
const spinner = (0,
|
|
4605
|
+
const spinner = (0, import_ora15.default)("Minting key...").start();
|
|
4498
4606
|
try {
|
|
4499
4607
|
const out = await admin({
|
|
4500
4608
|
method: "POST",
|
|
@@ -4507,9 +4615,9 @@ async function runKeyMint(opts) {
|
|
|
4507
4615
|
console.log(JSON.stringify(out));
|
|
4508
4616
|
return;
|
|
4509
4617
|
}
|
|
4510
|
-
console.log(` ${
|
|
4511
|
-
console.log(` ${
|
|
4512
|
-
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}`);
|
|
4513
4621
|
} catch (err) {
|
|
4514
4622
|
spinner.fail("Mint failed.");
|
|
4515
4623
|
throw err;
|
|
@@ -4517,7 +4625,7 @@ async function runKeyMint(opts) {
|
|
|
4517
4625
|
}
|
|
4518
4626
|
async function runKeyRevoke(keyId, opts) {
|
|
4519
4627
|
const { teamId } = await resolveTeam(opts.team);
|
|
4520
|
-
const spinner = (0,
|
|
4628
|
+
const spinner = (0, import_ora15.default)("Revoking key...").start();
|
|
4521
4629
|
try {
|
|
4522
4630
|
await admin({
|
|
4523
4631
|
method: "DELETE",
|
|
@@ -4532,8 +4640,8 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
4532
4640
|
}
|
|
4533
4641
|
|
|
4534
4642
|
// src/commands/consumer.ts
|
|
4535
|
-
var
|
|
4536
|
-
var
|
|
4643
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
4644
|
+
var import_ora16 = __toESM(require("ora"));
|
|
4537
4645
|
init_admin();
|
|
4538
4646
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
4539
4647
|
var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
|
|
@@ -4553,7 +4661,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
4553
4661
|
function requireConsumer() {
|
|
4554
4662
|
const c = loadConsumer();
|
|
4555
4663
|
if (!c) {
|
|
4556
|
-
console.error(
|
|
4664
|
+
console.error(import_chalk31.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
4557
4665
|
process.exit(1);
|
|
4558
4666
|
}
|
|
4559
4667
|
return c;
|
|
@@ -4564,7 +4672,7 @@ async function runConsumerLogin(opts) {
|
|
|
4564
4672
|
let clientId = opts.client;
|
|
4565
4673
|
if (clientId) {
|
|
4566
4674
|
if (!tenant2) {
|
|
4567
|
-
console.error(
|
|
4675
|
+
console.error(import_chalk31.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
4568
4676
|
process.exit(1);
|
|
4569
4677
|
}
|
|
4570
4678
|
} else {
|
|
@@ -4576,25 +4684,25 @@ async function runConsumerLogin(opts) {
|
|
|
4576
4684
|
if (!picked) process.exit(1);
|
|
4577
4685
|
tenant2 = picked;
|
|
4578
4686
|
}
|
|
4579
|
-
const s2 = (0,
|
|
4687
|
+
const s2 = (0, import_ora16.default)("Finding the login app...").start();
|
|
4580
4688
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
4581
4689
|
s2.stop();
|
|
4582
4690
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
4583
4691
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
4584
4692
|
if (!pick2) {
|
|
4585
|
-
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).`));
|
|
4586
4694
|
process.exit(1);
|
|
4587
4695
|
}
|
|
4588
4696
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
4589
4697
|
}
|
|
4590
4698
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
4591
|
-
console.log(`${
|
|
4699
|
+
console.log(`${import_chalk31.default.cyan("\u2192")} Logging in to ${import_chalk31.default.bold(tenant2)} as a consumer...`);
|
|
4592
4700
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
4593
4701
|
console.log(`
|
|
4594
|
-
Open: ${
|
|
4595
|
-
console.log(` Code: ${
|
|
4702
|
+
Open: ${import_chalk31.default.underline(verificationUri)}`);
|
|
4703
|
+
console.log(` Code: ${import_chalk31.default.bold(userCode)}
|
|
4596
4704
|
`);
|
|
4597
|
-
console.log(
|
|
4705
|
+
console.log(import_chalk31.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
4598
4706
|
}, portalResource);
|
|
4599
4707
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
4600
4708
|
const creds = {
|
|
@@ -4609,7 +4717,7 @@ async function runConsumerLogin(opts) {
|
|
|
4609
4717
|
obtainedAt: Date.now()
|
|
4610
4718
|
};
|
|
4611
4719
|
saveConsumer(creds);
|
|
4612
|
-
console.log(
|
|
4720
|
+
console.log(import_chalk31.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
4613
4721
|
}
|
|
4614
4722
|
async function runConsumerTokens(opts) {
|
|
4615
4723
|
const creds = requireConsumer();
|
|
@@ -4622,29 +4730,29 @@ async function runConsumerTokens(opts) {
|
|
|
4622
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));
|
|
4623
4731
|
return;
|
|
4624
4732
|
}
|
|
4625
|
-
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)}
|
|
4626
4734
|
`);
|
|
4627
|
-
console.log(`${
|
|
4735
|
+
console.log(`${import_chalk31.default.bold("access_token")} ${import_chalk31.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
4628
4736
|
${fresh.accessToken}
|
|
4629
4737
|
`);
|
|
4630
|
-
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) ?? "?"))}
|
|
4631
4739
|
${fresh.idToken}
|
|
4632
4740
|
`);
|
|
4633
|
-
if (fresh.refreshToken) console.log(`${
|
|
4741
|
+
if (fresh.refreshToken) console.log(`${import_chalk31.default.bold("refresh_token")}
|
|
4634
4742
|
${fresh.refreshToken}
|
|
4635
4743
|
`);
|
|
4636
|
-
console.log(
|
|
4744
|
+
console.log(import_chalk31.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
4637
4745
|
}
|
|
4638
4746
|
async function runConsumerApikeys(opts) {
|
|
4639
4747
|
const creds = requireConsumer();
|
|
4640
4748
|
const { default: inquirer2 } = await import("inquirer");
|
|
4641
|
-
const spinner = (0,
|
|
4749
|
+
const spinner = (0, import_ora16.default)("Loading your API keys...").start();
|
|
4642
4750
|
const list = await consumerFetch(creds, "/apikeys");
|
|
4643
4751
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
4644
4752
|
spinner.stop();
|
|
4645
4753
|
if (list.status >= 400) {
|
|
4646
|
-
console.error(
|
|
4647
|
-
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."));
|
|
4648
4756
|
process.exit(1);
|
|
4649
4757
|
}
|
|
4650
4758
|
const keys = list.data?.keys ?? [];
|
|
@@ -4652,16 +4760,16 @@ async function runConsumerApikeys(opts) {
|
|
|
4652
4760
|
if (opts.json) {
|
|
4653
4761
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
4654
4762
|
} else if (!keys.length) {
|
|
4655
|
-
console.log(
|
|
4763
|
+
console.log(import_chalk31.default.yellow("No API keys yet."));
|
|
4656
4764
|
} else {
|
|
4657
4765
|
for (const k of keys) {
|
|
4658
4766
|
const clear = revealMap[k.environment]?.key;
|
|
4659
|
-
const shown = clear ?
|
|
4660
|
-
const exp = k.expires_at ?
|
|
4661
|
-
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 ?? "")}`);
|
|
4662
4770
|
}
|
|
4663
4771
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
4664
|
-
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.)"));
|
|
4665
4773
|
}
|
|
4666
4774
|
}
|
|
4667
4775
|
if (opts.json) return;
|
|
@@ -4675,7 +4783,7 @@ async function runConsumerApikeys(opts) {
|
|
|
4675
4783
|
const body = { environment: answers.environment };
|
|
4676
4784
|
if (answers.description) body.description = answers.description;
|
|
4677
4785
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
4678
|
-
const s2 = (0,
|
|
4786
|
+
const s2 = (0, import_ora16.default)("Creating key...").start();
|
|
4679
4787
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
4680
4788
|
if (created.status >= 400) {
|
|
4681
4789
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -4683,13 +4791,13 @@ async function runConsumerApikeys(opts) {
|
|
|
4683
4791
|
}
|
|
4684
4792
|
s2.succeed("Key created.");
|
|
4685
4793
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
4686
|
-
if (key) console.log(` ${
|
|
4687
|
-
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."));
|
|
4688
4796
|
}
|
|
4689
4797
|
|
|
4690
4798
|
// src/commands/sidecar.ts
|
|
4691
|
-
var
|
|
4692
|
-
var
|
|
4799
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
4800
|
+
var import_ora17 = __toESM(require("ora"));
|
|
4693
4801
|
var fs7 = __toESM(require("fs"));
|
|
4694
4802
|
var path4 = __toESM(require("path"));
|
|
4695
4803
|
init_admin();
|
|
@@ -4730,18 +4838,18 @@ function upsertEnvLocal(root, token) {
|
|
|
4730
4838
|
}
|
|
4731
4839
|
function installSidecarPackage(root) {
|
|
4732
4840
|
if (fs7.existsSync(path4.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
4733
|
-
console.log(` ${
|
|
4841
|
+
console.log(` ${import_chalk32.default.green("\u2713")} apiblaze package already installed`);
|
|
4734
4842
|
return;
|
|
4735
4843
|
}
|
|
4736
4844
|
const has = (f) => fs7.existsSync(path4.join(root, f));
|
|
4737
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" };
|
|
4738
|
-
const spinner = (0,
|
|
4846
|
+
const spinner = (0, import_ora17.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
4739
4847
|
try {
|
|
4740
4848
|
const { execSync } = require("child_process");
|
|
4741
4849
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
4742
4850
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
4743
4851
|
} catch {
|
|
4744
|
-
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")}.`);
|
|
4745
4853
|
}
|
|
4746
4854
|
}
|
|
4747
4855
|
function readEnvKey(root) {
|
|
@@ -4880,7 +4988,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
4880
4988
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
4881
4989
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
4882
4990
|
if (opts.newSession) clearAnonCred2();
|
|
4883
|
-
const spinner = (0,
|
|
4991
|
+
const spinner = (0, import_ora17.default)("Setting up a sidecar (no login needed)...").start();
|
|
4884
4992
|
let out;
|
|
4885
4993
|
try {
|
|
4886
4994
|
out = await sidecarInitAnonymous2();
|
|
@@ -4892,29 +5000,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
4892
5000
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
4893
5001
|
const envState = upsertEnvLocal(root, out.token);
|
|
4894
5002
|
ensureGitignored(root);
|
|
4895
|
-
console.log(` ${
|
|
4896
|
-
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)}`);
|
|
4897
5005
|
installSidecarPackage(root);
|
|
4898
5006
|
let inspectorPath = null;
|
|
4899
5007
|
if (!opts.noInspector) {
|
|
4900
5008
|
inspectorPath = generateInspector(root, router);
|
|
4901
|
-
if (inspectorPath) console.log(` ${
|
|
5009
|
+
if (inspectorPath) console.log(` ${import_chalk32.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
4902
5010
|
}
|
|
4903
5011
|
console.log("");
|
|
4904
|
-
console.log(
|
|
4905
|
-
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.`);
|
|
4906
5014
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
4907
|
-
console.log(` ${
|
|
5015
|
+
console.log(` ${import_chalk32.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
4908
5016
|
console.log("");
|
|
4909
|
-
console.log(
|
|
4910
|
-
console.log(` ${
|
|
4911
|
-
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`));
|
|
4912
5020
|
}
|
|
4913
5021
|
async function runSidecar(opts) {
|
|
4914
5022
|
const root = path4.resolve(opts.dir ?? process.cwd());
|
|
4915
5023
|
const detected = detectNextProject(root);
|
|
4916
5024
|
if (!detected.found) {
|
|
4917
|
-
console.log(
|
|
5025
|
+
console.log(import_chalk32.default.yellow(`No Next.js project detected in ${root}.`));
|
|
4918
5026
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
4919
5027
|
return;
|
|
4920
5028
|
}
|
|
@@ -4925,10 +5033,10 @@ async function runSidecar(opts) {
|
|
|
4925
5033
|
if (!loadCredentials()) {
|
|
4926
5034
|
upsertEnvLocal(root, readEnvKey(root));
|
|
4927
5035
|
ensureGitignored(root);
|
|
4928
|
-
console.log(` ${
|
|
4929
|
-
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)}`);
|
|
4930
5038
|
installSidecarPackage(root);
|
|
4931
|
-
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."));
|
|
4932
5040
|
return;
|
|
4933
5041
|
}
|
|
4934
5042
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -4937,7 +5045,7 @@ async function runSidecar(opts) {
|
|
|
4937
5045
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
4938
5046
|
let token = existingKey ?? "";
|
|
4939
5047
|
if (mustMint) {
|
|
4940
|
-
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();
|
|
4941
5049
|
try {
|
|
4942
5050
|
const out = await admin({
|
|
4943
5051
|
method: "POST",
|
|
@@ -4951,39 +5059,39 @@ async function runSidecar(opts) {
|
|
|
4951
5059
|
throw err;
|
|
4952
5060
|
}
|
|
4953
5061
|
} else {
|
|
4954
|
-
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).`));
|
|
4955
5063
|
}
|
|
4956
5064
|
const envState = upsertEnvLocal(root, token);
|
|
4957
5065
|
ensureGitignored(root);
|
|
4958
|
-
console.log(` ${
|
|
5066
|
+
console.log(` ${import_chalk32.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
4959
5067
|
const wireState = wireInstrumentation(root);
|
|
4960
|
-
console.log(` ${
|
|
5068
|
+
console.log(` ${import_chalk32.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
4961
5069
|
installSidecarPackage(root);
|
|
4962
5070
|
let inspectorPath = null;
|
|
4963
5071
|
if (!opts.noInspector) {
|
|
4964
5072
|
inspectorPath = generateInspector(root, detected.router);
|
|
4965
|
-
if (inspectorPath) console.log(` ${
|
|
5073
|
+
if (inspectorPath) console.log(` ${import_chalk32.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
4966
5074
|
}
|
|
4967
5075
|
console.log("");
|
|
4968
|
-
console.log(
|
|
4969
|
-
console.log(` 1. ${
|
|
4970
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
4971
|
-
console.log(` 3. Approve the ones to route: ${
|
|
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)`);
|
|
4972
5080
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
4973
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
4974
|
-
if (switchingTeam) console.log(
|
|
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>\`.`));
|
|
4975
5083
|
console.log("");
|
|
4976
|
-
console.log(
|
|
4977
|
-
console.log(
|
|
4978
|
-
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)."));
|
|
4979
5087
|
console.log("");
|
|
4980
|
-
console.log(
|
|
4981
|
-
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."));
|
|
4982
5090
|
}
|
|
4983
5091
|
|
|
4984
5092
|
// src/commands/origins.ts
|
|
4985
|
-
var
|
|
4986
|
-
var
|
|
5093
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
5094
|
+
var import_ora18 = __toESM(require("ora"));
|
|
4987
5095
|
init_admin();
|
|
4988
5096
|
init_auth();
|
|
4989
5097
|
init_anon_cred();
|
|
@@ -4992,7 +5100,7 @@ async function runOriginsList(opts) {
|
|
|
4992
5100
|
if (!loadCredentials()) {
|
|
4993
5101
|
const cred = loadAnonCred();
|
|
4994
5102
|
if (!cred) {
|
|
4995
|
-
console.log(
|
|
5103
|
+
console.log(import_chalk33.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
4996
5104
|
return;
|
|
4997
5105
|
}
|
|
4998
5106
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -5010,30 +5118,30 @@ async function runOriginsList(opts) {
|
|
|
5010
5118
|
}
|
|
5011
5119
|
const routed = out.routed ?? [];
|
|
5012
5120
|
const candidates = out.candidates ?? [];
|
|
5013
|
-
console.log(
|
|
5121
|
+
console.log(import_chalk33.default.bold(`
|
|
5014
5122
|
Routed through APIblaze (${routed.length})`));
|
|
5015
|
-
if (!routed.length) console.log(
|
|
5016
|
-
for (const r of routed) console.log(` ${
|
|
5017
|
-
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(`
|
|
5018
5126
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
5019
|
-
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"));
|
|
5020
5128
|
for (const c of candidates) {
|
|
5021
|
-
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}`)}`);
|
|
5022
5130
|
}
|
|
5023
5131
|
if (candidates.length) {
|
|
5024
|
-
console.log(
|
|
5132
|
+
console.log(import_chalk33.default.dim(`
|
|
5025
5133
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
5026
|
-
console.log(
|
|
5134
|
+
console.log(import_chalk33.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
5027
5135
|
}
|
|
5028
5136
|
}
|
|
5029
5137
|
async function runOriginsApprove(origin, opts) {
|
|
5030
5138
|
if (!loadCredentials()) {
|
|
5031
5139
|
const cred = loadAnonCred();
|
|
5032
5140
|
if (!cred) {
|
|
5033
|
-
console.error(
|
|
5141
|
+
console.error(import_chalk33.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
5034
5142
|
process.exit(1);
|
|
5035
5143
|
}
|
|
5036
|
-
const spinner2 = (0,
|
|
5144
|
+
const spinner2 = (0, import_ora18.default)(`Approving ${origin} (anonymous)...`).start();
|
|
5037
5145
|
try {
|
|
5038
5146
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
5039
5147
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -5044,7 +5152,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
5044
5152
|
return;
|
|
5045
5153
|
}
|
|
5046
5154
|
const { teamId } = await resolveTeam(opts.team);
|
|
5047
|
-
const spinner = (0,
|
|
5155
|
+
const spinner = (0, import_ora18.default)(`Approving ${origin}...`).start();
|
|
5048
5156
|
try {
|
|
5049
5157
|
const out = await admin({
|
|
5050
5158
|
method: "POST",
|
|
@@ -5061,7 +5169,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
5061
5169
|
}
|
|
5062
5170
|
async function runOriginsDeny(origin, opts) {
|
|
5063
5171
|
const { teamId } = await resolveTeam(opts.team);
|
|
5064
|
-
const spinner = (0,
|
|
5172
|
+
const spinner = (0, import_ora18.default)(`Dismissing ${origin}...`).start();
|
|
5065
5173
|
try {
|
|
5066
5174
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
5067
5175
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -5072,7 +5180,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
5072
5180
|
}
|
|
5073
5181
|
async function runOriginsRemove(origin, opts) {
|
|
5074
5182
|
const { teamId } = await resolveTeam(opts.team);
|
|
5075
|
-
const spinner = (0,
|
|
5183
|
+
const spinner = (0, import_ora18.default)(`Removing the proxy for ${origin}...`).start();
|
|
5076
5184
|
try {
|
|
5077
5185
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
5078
5186
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -5083,7 +5191,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
5083
5191
|
}
|
|
5084
5192
|
|
|
5085
5193
|
// src/commands/op.ts
|
|
5086
|
-
var
|
|
5194
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
5087
5195
|
init_auth();
|
|
5088
5196
|
init_trace();
|
|
5089
5197
|
init_types();
|
|
@@ -5116,82 +5224,82 @@ function printResidue(report, applied) {
|
|
|
5116
5224
|
const up = report?.upstash ?? {};
|
|
5117
5225
|
const fga = report?.fga ?? {};
|
|
5118
5226
|
const ghosts = report?.ghosts ?? {};
|
|
5119
|
-
console.log(
|
|
5120
|
-
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"));
|
|
5121
5229
|
const orphans = up.orphans ?? [];
|
|
5122
|
-
if (orphans.length === 0) console.log(
|
|
5123
|
-
for (const o of orphans) console.log(` ${
|
|
5124
|
-
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}`));
|
|
5125
5233
|
if (up.anon_wallet_detail) {
|
|
5126
5234
|
const d = up.anon_wallet_detail;
|
|
5127
|
-
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)"}`));
|
|
5128
5236
|
}
|
|
5129
5237
|
if (up.keyspace_census) {
|
|
5130
5238
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
5131
|
-
console.log(
|
|
5239
|
+
console.log(import_chalk34.default.dim(` keyspace: ${census}`));
|
|
5132
5240
|
}
|
|
5133
|
-
if (up.unknown?.length) console.log(
|
|
5134
|
-
if (applied) console.log(` ${
|
|
5135
|
-
for (const e of up.errors ?? []) console.log(
|
|
5136
|
-
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"));
|
|
5137
5245
|
if (applied) {
|
|
5138
5246
|
const swept = fga?.swept ?? [];
|
|
5139
|
-
if (swept.length === 0) console.log(
|
|
5247
|
+
if (swept.length === 0) console.log(import_chalk34.default.green(" no orphaned stores"));
|
|
5140
5248
|
for (const s of swept) {
|
|
5141
5249
|
console.log(
|
|
5142
|
-
` ${
|
|
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`)}`
|
|
5143
5251
|
);
|
|
5144
5252
|
}
|
|
5145
|
-
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`));
|
|
5146
5254
|
const st = fga?.side_tables;
|
|
5147
|
-
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})` : ""}`));
|
|
5148
5256
|
} else {
|
|
5149
5257
|
const fgaOrphans = fga?.orphans ?? [];
|
|
5150
|
-
if (fgaOrphans.length === 0) console.log(
|
|
5258
|
+
if (fgaOrphans.length === 0) console.log(import_chalk34.default.green(" no orphaned stores"));
|
|
5151
5259
|
for (const s of fgaOrphans) {
|
|
5152
5260
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
5153
|
-
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)`)}`);
|
|
5154
5262
|
}
|
|
5155
|
-
console.log(
|
|
5263
|
+
console.log(import_chalk34.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
5156
5264
|
const st = fga?.side_tables;
|
|
5157
|
-
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`));
|
|
5158
5266
|
}
|
|
5159
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
5160
|
-
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"));
|
|
5161
5269
|
if (applied) {
|
|
5162
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
5163
|
-
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)`)}`);
|
|
5164
5272
|
} else {
|
|
5165
5273
|
const n = ghosts?.ghost_count ?? 0;
|
|
5166
|
-
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)`)}`));
|
|
5167
5275
|
else {
|
|
5168
|
-
console.log(
|
|
5276
|
+
console.log(import_chalk34.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
5169
5277
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
5170
|
-
console.log(
|
|
5278
|
+
console.log(import_chalk34.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
5171
5279
|
}
|
|
5172
|
-
if (n > 20) console.log(
|
|
5280
|
+
if (n > 20) console.log(import_chalk34.default.dim(` \u2026 and ${n - 20} more`));
|
|
5173
5281
|
}
|
|
5174
5282
|
}
|
|
5175
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
5283
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk34.default.red(` error: ${e}`));
|
|
5176
5284
|
console.log();
|
|
5177
5285
|
}
|
|
5178
5286
|
async function runOp(sub, opts = {}) {
|
|
5179
5287
|
if (!loadCredentials()) {
|
|
5180
|
-
console.log(
|
|
5288
|
+
console.log(import_chalk34.default.dim("Not logged in. Run `apiblaze login`."));
|
|
5181
5289
|
return;
|
|
5182
5290
|
}
|
|
5183
5291
|
if (!isOperatorLogin()) {
|
|
5184
|
-
console.log(
|
|
5292
|
+
console.log(import_chalk34.default.dim("`apiblaze op` is only available to platform operators."));
|
|
5185
5293
|
return;
|
|
5186
5294
|
}
|
|
5187
5295
|
switch (sub) {
|
|
5188
5296
|
case void 0:
|
|
5189
5297
|
case "menu": {
|
|
5190
|
-
console.log(
|
|
5191
|
-
console.log(` ${
|
|
5192
|
-
console.log(` ${
|
|
5193
|
-
console.log(` ${
|
|
5194
|
-
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)
|
|
5195
5303
|
`));
|
|
5196
5304
|
return;
|
|
5197
5305
|
}
|
|
@@ -5210,17 +5318,17 @@ async function runOp(sub, opts = {}) {
|
|
|
5210
5318
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
5211
5319
|
printResidue(report, false);
|
|
5212
5320
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
5213
|
-
console.log(
|
|
5321
|
+
console.log(import_chalk34.default.green("Nothing to sweep."));
|
|
5214
5322
|
return;
|
|
5215
5323
|
}
|
|
5216
5324
|
if (!opts.yes) {
|
|
5217
5325
|
const readline2 = await import("readline/promises");
|
|
5218
5326
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
5219
5327
|
const answer = await rl.question(
|
|
5220
|
-
|
|
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: `)
|
|
5221
5329
|
);
|
|
5222
5330
|
rl.close();
|
|
5223
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
5331
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk34.default.dim("Aborted."));
|
|
5224
5332
|
}
|
|
5225
5333
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
5226
5334
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -5231,15 +5339,15 @@ async function runOp(sub, opts = {}) {
|
|
|
5231
5339
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
5232
5340
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
5233
5341
|
const accounts = data?.accounts ?? [];
|
|
5234
|
-
if (accounts.length === 0) return void console.log(
|
|
5342
|
+
if (accounts.length === 0) return void console.log(import_chalk34.default.dim("No credit wallets."));
|
|
5235
5343
|
for (const a of accounts) {
|
|
5236
5344
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
5237
|
-
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") : ""}`);
|
|
5238
5346
|
}
|
|
5239
5347
|
return;
|
|
5240
5348
|
}
|
|
5241
5349
|
default:
|
|
5242
|
-
console.log(
|
|
5350
|
+
console.log(import_chalk34.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
5243
5351
|
}
|
|
5244
5352
|
}
|
|
5245
5353
|
|
|
@@ -5296,7 +5404,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
5296
5404
|
try {
|
|
5297
5405
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
5298
5406
|
if (Number.isNaN(resolved)) {
|
|
5299
|
-
console.error(
|
|
5407
|
+
console.error(import_chalk35.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
5300
5408
|
process.exit(1);
|
|
5301
5409
|
}
|
|
5302
5410
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -5394,7 +5502,7 @@ function groupedCommandHelp() {
|
|
|
5394
5502
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
5395
5503
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
5396
5504
|
}).filter(Boolean).join("\n");
|
|
5397
|
-
return `${
|
|
5505
|
+
return `${import_chalk35.default.bold(g.title)}
|
|
5398
5506
|
${rows}`;
|
|
5399
5507
|
}).join("\n\n");
|
|
5400
5508
|
}
|
|
@@ -5427,14 +5535,14 @@ async function recoverStaleTeam() {
|
|
|
5427
5535
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
5428
5536
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
5429
5537
|
if (!linked) {
|
|
5430
|
-
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."));
|
|
5431
5539
|
return;
|
|
5432
5540
|
}
|
|
5433
5541
|
if (linked.teamId === creds.teamId) return;
|
|
5434
5542
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
5435
5543
|
delete next.activeTenant;
|
|
5436
5544
|
saveCredentials(next);
|
|
5437
|
-
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.`));
|
|
5438
5546
|
} catch {
|
|
5439
5547
|
}
|
|
5440
5548
|
}
|
|
@@ -5442,16 +5550,16 @@ async function printError(err) {
|
|
|
5442
5550
|
if (err instanceof ApiError) {
|
|
5443
5551
|
const data = err.body;
|
|
5444
5552
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
5445
|
-
console.error(
|
|
5553
|
+
console.error(import_chalk35.default.red(`
|
|
5446
5554
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
5447
5555
|
if (err.status === 403 || err.status === 404) {
|
|
5448
5556
|
await recoverStaleTeam();
|
|
5449
5557
|
}
|
|
5450
5558
|
} else if (err instanceof Error) {
|
|
5451
|
-
console.error(
|
|
5559
|
+
console.error(import_chalk35.default.red(`
|
|
5452
5560
|
Error: ${err.message}`));
|
|
5453
5561
|
} else {
|
|
5454
|
-
console.error(
|
|
5562
|
+
console.error(import_chalk35.default.red("\nUnknown error"));
|
|
5455
5563
|
}
|
|
5456
5564
|
}
|
|
5457
5565
|
program.parse(process.argv);
|