apiblaze 0.18.8 → 0.19.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/README.md +1 -1
- package/dist/index.js +1125 -675
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -393,32 +393,32 @@ function maskBody(body) {
|
|
|
393
393
|
}
|
|
394
394
|
function renderTrace() {
|
|
395
395
|
if (!verbose || entries.length === 0) return;
|
|
396
|
-
console.log(
|
|
397
|
-
console.log(
|
|
396
|
+
console.log(import_chalk6.default.dim("\n" + "\u2500".repeat(64)));
|
|
397
|
+
console.log(import_chalk6.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
|
|
398
398
|
console.log(
|
|
399
|
-
|
|
399
|
+
import_chalk6.default.dim("The same thing on the official API \u2014 copy/paste with your control-plane key\n(get one from the Developers section of dashboard.apiblaze.com, then\n`export APIBLAZE_CONTROLPLANE_APIKEY=sk_...`).\nFull API reference: https://api.apiblaze.com/openapi.json\n")
|
|
400
400
|
);
|
|
401
401
|
entries.forEach((e, i) => {
|
|
402
|
-
const n = entries.length > 1 ?
|
|
403
|
-
if (e.summary) console.log(`${n}${
|
|
402
|
+
const n = entries.length > 1 ? import_chalk6.default.bold(`${i + 1}. `) : "";
|
|
403
|
+
if (e.summary) console.log(`${n}${import_chalk6.default.cyan(e.summary)}${e.status ? import_chalk6.default.dim(` (HTTP ${e.status})`) : ""}`);
|
|
404
404
|
const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
|
|
405
405
|
const masked = maskBody(e.body);
|
|
406
406
|
const hasBody = e.method !== "GET" && masked !== void 0;
|
|
407
|
-
console.log(
|
|
408
|
-
console.log(
|
|
407
|
+
console.log(import_chalk6.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
|
|
408
|
+
console.log(import_chalk6.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
|
|
409
409
|
if (hasBody) {
|
|
410
|
-
console.log(
|
|
411
|
-
console.log(
|
|
410
|
+
console.log(import_chalk6.default.green(" -H 'Content-Type: application/json' \\"));
|
|
411
|
+
console.log(import_chalk6.default.green(` -d '${masked}'`));
|
|
412
412
|
}
|
|
413
413
|
if (i < entries.length - 1) console.log();
|
|
414
414
|
});
|
|
415
415
|
entries.length = 0;
|
|
416
416
|
}
|
|
417
|
-
var
|
|
417
|
+
var import_chalk6, CONTROL_API_VERSION, verbose, entries, SECRET_KEY;
|
|
418
418
|
var init_trace = __esm({
|
|
419
419
|
"src/lib/trace.ts"() {
|
|
420
420
|
"use strict";
|
|
421
|
-
|
|
421
|
+
import_chalk6 = __toESM(require("chalk"));
|
|
422
422
|
CONTROL_API_VERSION = "1.0.0";
|
|
423
423
|
verbose = false;
|
|
424
424
|
entries = [];
|
|
@@ -459,13 +459,13 @@ function formatBilling(billing) {
|
|
|
459
459
|
}
|
|
460
460
|
function maybePrintBilling(data) {
|
|
461
461
|
const line = formatBilling(data?.billing);
|
|
462
|
-
if (line) console.log(
|
|
462
|
+
if (line) console.log(import_chalk7.default.magenta(` ${line}`));
|
|
463
463
|
}
|
|
464
|
-
var
|
|
464
|
+
var import_chalk7, DASHBOARD_BASE3;
|
|
465
465
|
var init_admin = __esm({
|
|
466
466
|
"src/lib/admin.ts"() {
|
|
467
467
|
"use strict";
|
|
468
|
-
|
|
468
|
+
import_chalk7 = __toESM(require("chalk"));
|
|
469
469
|
init_auth();
|
|
470
470
|
init_trace();
|
|
471
471
|
init_types();
|
|
@@ -473,6 +473,233 @@ var init_admin = __esm({
|
|
|
473
473
|
}
|
|
474
474
|
});
|
|
475
475
|
|
|
476
|
+
// src/lib/resolve.ts
|
|
477
|
+
function requireAuth() {
|
|
478
|
+
const creds = loadCredentials();
|
|
479
|
+
if (!creds) {
|
|
480
|
+
console.error(import_chalk8.default.red("Not logged in. Run `apiblaze login` first."));
|
|
481
|
+
process.exit(1);
|
|
482
|
+
}
|
|
483
|
+
return creds;
|
|
484
|
+
}
|
|
485
|
+
async function resolveTeam(opt) {
|
|
486
|
+
const creds = requireAuth();
|
|
487
|
+
if (!opt) {
|
|
488
|
+
if (!creds.teamId) {
|
|
489
|
+
console.error(import_chalk8.default.red("No active team. Run `apiblaze login` or pass --team."));
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
return { teamId: creds.teamId, teamName: creds.teamName };
|
|
493
|
+
}
|
|
494
|
+
if (opt.startsWith("team_")) return { teamId: opt };
|
|
495
|
+
const teams = await getTeams().catch(() => []);
|
|
496
|
+
const match = teams.find((t) => t.name === opt || t.teamId === opt);
|
|
497
|
+
if (!match) {
|
|
498
|
+
console.error(import_chalk8.default.red(`Team "${opt}" not found.${teams.length ? " Available: " + teams.map((t) => t.name).join(", ") : ""}`));
|
|
499
|
+
process.exit(1);
|
|
500
|
+
}
|
|
501
|
+
return { teamId: match.teamId, teamName: match.name };
|
|
502
|
+
}
|
|
503
|
+
async function resolveProject(teamId, nameOrId, version2) {
|
|
504
|
+
const projects = await getProjects(teamId).catch(() => []);
|
|
505
|
+
const candidates = projects.filter(
|
|
506
|
+
(p) => p.projectId === nameOrId || p.projectName === nameOrId
|
|
507
|
+
);
|
|
508
|
+
if (candidates.length === 0) {
|
|
509
|
+
console.error(import_chalk8.default.red(`Project "${nameOrId}" not found in this team.`));
|
|
510
|
+
if (projects.length) console.error(import_chalk8.default.dim(" Known: " + projects.map((p) => p.projectName).join(", ")));
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
const chosen = version2 ? candidates.find((p) => p.apiVersion === version2) : candidates[0];
|
|
514
|
+
if (!chosen) {
|
|
515
|
+
console.error(import_chalk8.default.red(`Project "${nameOrId}" has no version ${version2}. Versions: ${candidates.map((p) => p.apiVersion).join(", ")}`));
|
|
516
|
+
process.exit(1);
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
projectId: chosen.projectId,
|
|
520
|
+
projectName: chosen.projectName,
|
|
521
|
+
apiVersion: chosen.apiVersion,
|
|
522
|
+
teamId,
|
|
523
|
+
tenant: chosen.tenant
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
var import_chalk8;
|
|
527
|
+
var init_resolve = __esm({
|
|
528
|
+
"src/lib/resolve.ts"() {
|
|
529
|
+
"use strict";
|
|
530
|
+
import_chalk8 = __toESM(require("chalk"));
|
|
531
|
+
init_auth();
|
|
532
|
+
init_api();
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
// src/lib/caller.ts
|
|
537
|
+
function resolveCaller() {
|
|
538
|
+
const creds = loadCredentials();
|
|
539
|
+
if (creds && Date.now() < creds.expiresAt) return { mode: "session" };
|
|
540
|
+
const anon = loadAnonCred();
|
|
541
|
+
if (anon?.cp_key) return { mode: "anon", cpKey: anon.cp_key, teamId: anon.team_id };
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
function requireCaller() {
|
|
545
|
+
const caller = resolveCaller();
|
|
546
|
+
if (!caller) {
|
|
547
|
+
throw new Error(
|
|
548
|
+
"No credential. Run `apiblaze login`, or `apiblaze create` first (an anonymous workspace works too \u2014 its key drives these commands until you claim it)."
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
return caller;
|
|
552
|
+
}
|
|
553
|
+
async function resolveActingTeam(caller, optTeam) {
|
|
554
|
+
if (caller.mode === "anon") {
|
|
555
|
+
if (optTeam && optTeam !== caller.teamId) {
|
|
556
|
+
throw new Error(
|
|
557
|
+
`--team is not available in an anonymous workspace (your team is ${caller.teamId}). Run \`apiblaze login\` to act on other teams.`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
return { teamId: caller.teamId };
|
|
561
|
+
}
|
|
562
|
+
return resolveTeam(optTeam);
|
|
563
|
+
}
|
|
564
|
+
async function producer(caller, call) {
|
|
565
|
+
if (caller.mode === "session") return admin(call);
|
|
566
|
+
const out = await cpFetch(caller.cpKey, call.path, {
|
|
567
|
+
method: call.method,
|
|
568
|
+
...call.body !== void 0 ? { body: JSON.stringify(call.body) } : {}
|
|
569
|
+
});
|
|
570
|
+
recordCall({ method: call.method, path: call.path, body: call.body, status: 200, summary: call.summary });
|
|
571
|
+
return out;
|
|
572
|
+
}
|
|
573
|
+
async function resolveProjectVia(caller, teamId, nameOrId, version2) {
|
|
574
|
+
const out = await producer(caller, {
|
|
575
|
+
method: "GET",
|
|
576
|
+
path: `/projects?team_id=${encodeURIComponent(teamId)}`,
|
|
577
|
+
summary: `List projects for ${teamId}`
|
|
578
|
+
});
|
|
579
|
+
const rows = out?.projects ?? [];
|
|
580
|
+
const candidates = rows.filter((r) => r.project_id === nameOrId || r.project_name === nameOrId);
|
|
581
|
+
if (!candidates.length) {
|
|
582
|
+
throw new Error(
|
|
583
|
+
`Project "${nameOrId}" not found in this team.` + (rows.length ? ` Known: ${rows.map((r) => r.project_name).join(", ")}` : "")
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
const chosen = version2 ? candidates.find((r) => r.api_version === version2) : candidates[0];
|
|
587
|
+
if (!chosen) {
|
|
588
|
+
throw new Error(
|
|
589
|
+
`Project "${nameOrId}" has no version ${version2}. Versions: ${candidates.map((r) => r.api_version).join(", ")}`
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
return {
|
|
593
|
+
projectId: chosen.project_id,
|
|
594
|
+
projectName: chosen.project_name,
|
|
595
|
+
apiVersion: chosen.api_version,
|
|
596
|
+
teamId,
|
|
597
|
+
tenant: chosen.tenant ?? chosen.config?.tenant
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
var init_caller = __esm({
|
|
601
|
+
"src/lib/caller.ts"() {
|
|
602
|
+
"use strict";
|
|
603
|
+
init_auth();
|
|
604
|
+
init_anon_cred();
|
|
605
|
+
init_admin();
|
|
606
|
+
init_trace();
|
|
607
|
+
init_resolve();
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
// src/commands/iam.ts
|
|
612
|
+
var iam_exports = {};
|
|
613
|
+
__export(iam_exports, {
|
|
614
|
+
runIamToggle: () => runIamToggle,
|
|
615
|
+
runIdentifiedToggle: () => runIdentifiedToggle
|
|
616
|
+
});
|
|
617
|
+
async function runIamToggle(project, state, opts) {
|
|
618
|
+
if (state !== "on" && state !== "off") {
|
|
619
|
+
throw new Error(`Invalid state "${state}". Use: apiblaze iam <project> on|off`);
|
|
620
|
+
}
|
|
621
|
+
const enabled = state === "on";
|
|
622
|
+
const caller = requireCaller();
|
|
623
|
+
const { teamId } = await resolveActingTeam(caller, opts.team);
|
|
624
|
+
const proj2 = await resolveProjectVia(caller, teamId, project, opts.apiversion);
|
|
625
|
+
const tenant2 = opts.tenant ?? proj2.tenant;
|
|
626
|
+
if (!tenant2) {
|
|
627
|
+
throw new Error("Could not resolve the tenant for this project. Pass --tenant <name>.");
|
|
628
|
+
}
|
|
629
|
+
const spinner = (0, import_ora4.default)(`Turning IAM ${state} for tenant ${tenant2}...`).start();
|
|
630
|
+
try {
|
|
631
|
+
const out = await producer(caller, {
|
|
632
|
+
method: "PATCH",
|
|
633
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`,
|
|
634
|
+
body: { iam_enabled: enabled },
|
|
635
|
+
summary: `IAM ${state} for tenant ${tenant2}`
|
|
636
|
+
});
|
|
637
|
+
spinner.succeed(`IAM is ${import_chalk9.default.bold(state)} for tenant ${tenant2}.`);
|
|
638
|
+
if (opts.json) console.log(JSON.stringify(out ?? { iam_enabled: enabled }));
|
|
639
|
+
else if (enabled) {
|
|
640
|
+
console.log(import_chalk9.default.dim(" Groups now apply to identified calls. Identify callers with"));
|
|
641
|
+
console.log(import_chalk9.default.dim(" X-End-User-Id, a login token, or per-user keys \u2014 and consider"));
|
|
642
|
+
console.log(import_chalk9.default.dim(` \`apiblaze identified ${project} require\` to reject unattributed traffic.`));
|
|
643
|
+
}
|
|
644
|
+
} catch (err) {
|
|
645
|
+
spinner.fail(`IAM ${state} failed.`);
|
|
646
|
+
throw err;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
async function runIdentifiedToggle(project, mode, opts) {
|
|
650
|
+
if (mode !== "require" && mode !== "allow-anon") {
|
|
651
|
+
throw new Error(`Invalid mode "${mode}". Use: apiblaze identified <project> require|allow-anon`);
|
|
652
|
+
}
|
|
653
|
+
const require2 = mode === "require";
|
|
654
|
+
const caller = requireCaller();
|
|
655
|
+
const { teamId } = await resolveActingTeam(caller, opts.team);
|
|
656
|
+
const proj2 = await resolveProjectVia(caller, teamId, project, opts.apiversion);
|
|
657
|
+
const spinner = (0, import_ora4.default)(
|
|
658
|
+
require2 ? "Requiring identified traffic..." : "Allowing unattributed traffic..."
|
|
659
|
+
).start();
|
|
660
|
+
try {
|
|
661
|
+
const listing = await producer(caller, {
|
|
662
|
+
method: "GET",
|
|
663
|
+
path: `/projects?team_id=${encodeURIComponent(teamId)}`,
|
|
664
|
+
summary: `Read settings for ${proj2.projectName}`
|
|
665
|
+
});
|
|
666
|
+
const row = (listing?.projects ?? []).find(
|
|
667
|
+
(r) => r.project_id === proj2.projectId && r.api_version === proj2.apiVersion
|
|
668
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
669
|
+
) ?? (listing?.projects ?? []).find((r) => r.project_id === proj2.projectId);
|
|
670
|
+
const current = row?.config?.requests_policy ?? {};
|
|
671
|
+
const merged = {
|
|
672
|
+
mode: current.mode ?? "passthrough",
|
|
673
|
+
methods: current.methods?.length ? current.methods : ["api_key"],
|
|
674
|
+
...current,
|
|
675
|
+
identified_traffic_only: require2,
|
|
676
|
+
require_x_end_user_id: require2
|
|
677
|
+
};
|
|
678
|
+
const out = await producer(caller, {
|
|
679
|
+
method: "PATCH",
|
|
680
|
+
path: `/projects/${proj2.projectId}/${proj2.apiVersion}`,
|
|
681
|
+
body: { requests_policy: merged },
|
|
682
|
+
summary: `Set identified-traffic policy (${mode}) on ${proj2.projectName}`
|
|
683
|
+
});
|
|
684
|
+
spinner.succeed(
|
|
685
|
+
require2 ? `${proj2.projectName} now rejects calls that don't identify an end user.` : `${proj2.projectName} accepts unattributed calls again (they pass with no groups).`
|
|
686
|
+
);
|
|
687
|
+
if (opts.json) console.log(JSON.stringify(out ?? { identified_traffic_only: require2 }));
|
|
688
|
+
} catch (err) {
|
|
689
|
+
spinner.fail("Policy update failed.");
|
|
690
|
+
throw err;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
var import_chalk9, import_ora4;
|
|
694
|
+
var init_iam = __esm({
|
|
695
|
+
"src/commands/iam.ts"() {
|
|
696
|
+
"use strict";
|
|
697
|
+
import_chalk9 = __toESM(require("chalk"));
|
|
698
|
+
import_ora4 = __toESM(require("ora"));
|
|
699
|
+
init_caller();
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
|
|
476
703
|
// src/lib/tenant-create.ts
|
|
477
704
|
var tenant_create_exports = {};
|
|
478
705
|
__export(tenant_create_exports, {
|
|
@@ -485,7 +712,7 @@ async function createTenantRow(teamId) {
|
|
|
485
712
|
const { name } = await inquirer3.prompt([{
|
|
486
713
|
type: "input",
|
|
487
714
|
name: "name",
|
|
488
|
-
message: `Tenant name ${
|
|
715
|
+
message: `Tenant name ${import_chalk26.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
|
|
489
716
|
validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
|
|
490
717
|
}]);
|
|
491
718
|
const slug = name.trim();
|
|
@@ -500,7 +727,7 @@ async function createTenantRow(teamId) {
|
|
|
500
727
|
} catch (err) {
|
|
501
728
|
const msg = err instanceof Error ? err.message : String(err);
|
|
502
729
|
if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
|
|
503
|
-
console.log(
|
|
730
|
+
console.log(import_chalk26.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
|
|
504
731
|
continue;
|
|
505
732
|
}
|
|
506
733
|
throw err;
|
|
@@ -517,11 +744,11 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
517
744
|
pageSize: 10,
|
|
518
745
|
choices: [
|
|
519
746
|
...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
|
|
520
|
-
...opts.allowSkip ? [new inquirer3.Separator(), { name:
|
|
747
|
+
...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk26.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
|
|
521
748
|
]
|
|
522
749
|
}]);
|
|
523
750
|
if (!provider) {
|
|
524
|
-
console.log(
|
|
751
|
+
console.log(import_chalk26.default.dim(" No login method yet \u2014 add one later under Login methods."));
|
|
525
752
|
return false;
|
|
526
753
|
}
|
|
527
754
|
const opt = PROVIDERS.find((p) => p.id === provider);
|
|
@@ -552,7 +779,7 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
552
779
|
if (a.callbacks.trim()) clientBody.authorizedCallbackUrls = a.callbacks.split(/\s*,\s*/).map((s) => s.trim()).filter(Boolean);
|
|
553
780
|
if (a.scopes.trim() && opt.own) providerBody.scopes = a.scopes.split(/\s+/).filter(Boolean);
|
|
554
781
|
}
|
|
555
|
-
const spinner = (0,
|
|
782
|
+
const spinner = (0, import_ora10.default)("Setting up login...").start();
|
|
556
783
|
try {
|
|
557
784
|
const client = await admin({ method: "POST", path: `${base}/app-clients`, body: clientBody, summary: `Create login for ${tenant2}` });
|
|
558
785
|
const clientId = client?.clientId ?? client?.client_id;
|
|
@@ -567,27 +794,27 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
567
794
|
async function createTenantInteractive(teamId) {
|
|
568
795
|
const { default: inquirer3 } = await import("inquirer");
|
|
569
796
|
const tenant2 = await createTenantRow(teamId);
|
|
570
|
-
console.log(
|
|
797
|
+
console.log(import_chalk26.default.green(` Tenant ${import_chalk26.default.bold(tenant2)} created.`));
|
|
571
798
|
const { users } = await inquirer3.prompt([{
|
|
572
799
|
type: "confirm",
|
|
573
800
|
name: "users",
|
|
574
801
|
default: false,
|
|
575
|
-
message: `Enable Users & groups? ${
|
|
802
|
+
message: `Enable Users & groups? ${import_chalk26.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
|
|
576
803
|
}]);
|
|
577
804
|
if (users) {
|
|
578
805
|
await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
|
|
579
806
|
});
|
|
580
|
-
console.log(
|
|
807
|
+
console.log(import_chalk26.default.green(" Users & groups enabled."));
|
|
581
808
|
}
|
|
582
809
|
await addApiblazeHostedLogin(teamId, tenant2, { allowSkip: true });
|
|
583
810
|
return tenant2;
|
|
584
811
|
}
|
|
585
|
-
var
|
|
812
|
+
var import_chalk26, import_ora10, PROVIDERS, DEFAULT_SCOPES;
|
|
586
813
|
var init_tenant_create = __esm({
|
|
587
814
|
"src/lib/tenant-create.ts"() {
|
|
588
815
|
"use strict";
|
|
589
|
-
|
|
590
|
-
|
|
816
|
+
import_chalk26 = __toESM(require("chalk"));
|
|
817
|
+
import_ora10 = __toESM(require("ora"));
|
|
591
818
|
init_admin();
|
|
592
819
|
PROVIDERS = [
|
|
593
820
|
{ id: "apiblaze", label: "APIblaze (via GitHub) \u2014 zero setup, recommended", own: false },
|
|
@@ -625,10 +852,10 @@ async function fetchPage(teamId, q) {
|
|
|
625
852
|
}
|
|
626
853
|
function label(t, defaultTenant, active) {
|
|
627
854
|
const tags = [
|
|
628
|
-
t.tenant_name === active ?
|
|
629
|
-
t.tenant_name === defaultTenant ?
|
|
855
|
+
t.tenant_name === active ? import_chalk27.default.cyan("active scope") : "",
|
|
856
|
+
t.tenant_name === defaultTenant ? import_chalk27.default.dim("team default") : ""
|
|
630
857
|
].filter(Boolean).join(", ");
|
|
631
|
-
const disp = t.display_name && t.display_name !== t.tenant_name ?
|
|
858
|
+
const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk27.default.dim(` ${t.display_name}`) : "";
|
|
632
859
|
return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
|
|
633
860
|
}
|
|
634
861
|
async function pickTenant(teamId, opts = {}) {
|
|
@@ -636,14 +863,14 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
636
863
|
const active = loadCredentials()?.activeTenant;
|
|
637
864
|
let q = opts.initialQuery ?? "";
|
|
638
865
|
for (; ; ) {
|
|
639
|
-
const spinner = (0,
|
|
866
|
+
const spinner = (0, import_ora11.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
|
|
640
867
|
const page = await fetchPage(teamId, q).finally(() => spinner.stop());
|
|
641
868
|
if (!page.total && !q) {
|
|
642
869
|
if (opts.allowCreate) {
|
|
643
870
|
const { make } = await inquirer3.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
|
|
644
871
|
if (make) return await createTenantInline(teamId);
|
|
645
872
|
}
|
|
646
|
-
console.error(
|
|
873
|
+
console.error(import_chalk27.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
647
874
|
return null;
|
|
648
875
|
}
|
|
649
876
|
const truncated = page.total > page.rows.length;
|
|
@@ -652,7 +879,7 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
652
879
|
value: t.tenant_name
|
|
653
880
|
}));
|
|
654
881
|
if (truncated || q) {
|
|
655
|
-
choices.push(new inquirer3.Separator(
|
|
882
|
+
choices.push(new inquirer3.Separator(import_chalk27.default.dim(
|
|
656
883
|
truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
|
|
657
884
|
)));
|
|
658
885
|
choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
|
|
@@ -686,12 +913,12 @@ async function createTenantInline(teamId) {
|
|
|
686
913
|
const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
|
|
687
914
|
return createTenantInteractive2(teamId);
|
|
688
915
|
}
|
|
689
|
-
var
|
|
916
|
+
var import_chalk27, import_ora11, PAGE;
|
|
690
917
|
var init_tenant_pick = __esm({
|
|
691
918
|
"src/lib/tenant-pick.ts"() {
|
|
692
919
|
"use strict";
|
|
693
|
-
|
|
694
|
-
|
|
920
|
+
import_chalk27 = __toESM(require("chalk"));
|
|
921
|
+
import_ora11 = __toESM(require("ora"));
|
|
695
922
|
init_admin();
|
|
696
923
|
init_auth();
|
|
697
924
|
PAGE = 15;
|
|
@@ -700,10 +927,10 @@ var init_tenant_pick = __esm({
|
|
|
700
927
|
|
|
701
928
|
// src/index.ts
|
|
702
929
|
var import_commander = require("commander");
|
|
703
|
-
var
|
|
930
|
+
var import_chalk43 = __toESM(require("chalk"));
|
|
704
931
|
|
|
705
932
|
// package.json
|
|
706
|
-
var version = "0.
|
|
933
|
+
var version = "0.19.0";
|
|
707
934
|
|
|
708
935
|
// src/index.ts
|
|
709
936
|
init_types();
|
|
@@ -836,9 +1063,9 @@ async function offerClaimPendingAnon() {
|
|
|
836
1063
|
}
|
|
837
1064
|
try {
|
|
838
1065
|
const { claimTeam: claimTeam2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
839
|
-
const t = await claimTeam2(cred.claim_code);
|
|
1066
|
+
const t = await claimTeam2(cred.claim_code, loadCredentials()?.teamId);
|
|
840
1067
|
clearAnonCred2();
|
|
841
|
-
console.log(import_chalk2.default.green(` \u2714 Claimed your anonymous proxies into your account (team ${import_chalk2.default.bold(t.team_id)}).`));
|
|
1068
|
+
console.log(import_chalk2.default.green(` \u2714 Claimed your anonymous proxies into your account (team ${import_chalk2.default.bold(t.team_name || t.team_id)}).`));
|
|
842
1069
|
} catch (err) {
|
|
843
1070
|
const { ApiError: ApiError2 } = await Promise.resolve().then(() => (init_types(), types_exports));
|
|
844
1071
|
const code = err instanceof ApiError2 && typeof err.body === "object" && err.body ? err.body.error : void 0;
|
|
@@ -1613,8 +1840,8 @@ ${projects.length} project${projects.length === 1 ? "" : "s"}`));
|
|
|
1613
1840
|
|
|
1614
1841
|
// src/commands/create.ts
|
|
1615
1842
|
var import_fs2 = __toESM(require("fs"));
|
|
1616
|
-
var
|
|
1617
|
-
var
|
|
1843
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
1844
|
+
var import_ora5 = __toESM(require("ora"));
|
|
1618
1845
|
init_auth();
|
|
1619
1846
|
init_api();
|
|
1620
1847
|
function normalizeName(raw) {
|
|
@@ -1641,7 +1868,7 @@ function stripTenantFromPortal(devPortal) {
|
|
|
1641
1868
|
}
|
|
1642
1869
|
}
|
|
1643
1870
|
function fail(message) {
|
|
1644
|
-
console.error(
|
|
1871
|
+
console.error(import_chalk10.default.red(`Error: ${message}`));
|
|
1645
1872
|
process.exit(1);
|
|
1646
1873
|
}
|
|
1647
1874
|
function buildTryItCurl(url, authType, apiKey) {
|
|
@@ -1656,11 +1883,11 @@ function printCurlExample(url, authType, apiKey, devPortal) {
|
|
|
1656
1883
|
const curl = buildTryItCurl(url, authType, apiKey);
|
|
1657
1884
|
console.log();
|
|
1658
1885
|
if (curl) {
|
|
1659
|
-
console.log(` ${
|
|
1660
|
-
console.log(` ${
|
|
1886
|
+
console.log(` ${import_chalk10.default.dim("Try it \u2014 copy/paste:")}`);
|
|
1887
|
+
console.log(` ${import_chalk10.default.cyan(curl)}`);
|
|
1661
1888
|
} else if (authType === "oauth") {
|
|
1662
|
-
console.log(` ${
|
|
1663
|
-
console.log(` ${
|
|
1889
|
+
console.log(` ${import_chalk10.default.dim("Try it:")} this proxy uses OAuth \u2014 sign in at ${import_chalk10.default.bold(devPortal ?? "the dev portal")} to get a token,`);
|
|
1890
|
+
console.log(` ${import_chalk10.default.dim(`then call ${url} with`)} ${import_chalk10.default.cyan('-H "Authorization: Bearer <token>"')}`);
|
|
1664
1891
|
}
|
|
1665
1892
|
}
|
|
1666
1893
|
var VALID_AUTH = ["api_key", "none", "oauth"];
|
|
@@ -1690,7 +1917,7 @@ async function runCreate(opts = {}) {
|
|
|
1690
1917
|
teamId = match.teamId;
|
|
1691
1918
|
}
|
|
1692
1919
|
}
|
|
1693
|
-
if (!opts.json) console.log(
|
|
1920
|
+
if (!opts.json) console.log(import_chalk10.default.bold("\nCreate an API proxy\n"));
|
|
1694
1921
|
let name = "";
|
|
1695
1922
|
if (opts.name !== void 0) {
|
|
1696
1923
|
name = normalizeName(opts.name);
|
|
@@ -1710,23 +1937,23 @@ async function runCreate(opts = {}) {
|
|
|
1710
1937
|
}]);
|
|
1711
1938
|
name = normalizeName(rawName);
|
|
1712
1939
|
if (name.length < 3) {
|
|
1713
|
-
console.log(
|
|
1940
|
+
console.log(import_chalk10.default.yellow(" Name must be at least 3 characters (letters and digits only).\n"));
|
|
1714
1941
|
continue;
|
|
1715
1942
|
}
|
|
1716
|
-
const spinner2 = (0,
|
|
1943
|
+
const spinner2 = (0, import_ora5.default)("Checking availability...").start();
|
|
1717
1944
|
try {
|
|
1718
1945
|
const check = await checkProxyName(name, teamId, opts.apiversion);
|
|
1719
1946
|
spinner2.stop();
|
|
1720
1947
|
if (!check.canUseProjectName || !check.canUseApiVersion) {
|
|
1721
|
-
console.log(
|
|
1948
|
+
console.log(import_chalk10.default.yellow(` "${name}" is not available${check.message ? ` \u2014 ${check.message}` : ""}. Try another.
|
|
1722
1949
|
`));
|
|
1723
1950
|
continue;
|
|
1724
1951
|
}
|
|
1725
1952
|
} catch {
|
|
1726
1953
|
spinner2.stop();
|
|
1727
|
-
console.log(
|
|
1954
|
+
console.log(import_chalk10.default.dim(" (could not verify availability; continuing)"));
|
|
1728
1955
|
}
|
|
1729
|
-
console.log(`${
|
|
1956
|
+
console.log(`${import_chalk10.default.cyan("\u2192")} Your API will live at ${import_chalk10.default.bold(`https://${name}.abz.run`)}
|
|
1730
1957
|
`);
|
|
1731
1958
|
break;
|
|
1732
1959
|
}
|
|
@@ -1746,7 +1973,7 @@ async function runCreate(opts = {}) {
|
|
|
1746
1973
|
message: "Target URL to forward requests to (e.g. https://httpbin.org):"
|
|
1747
1974
|
}]);
|
|
1748
1975
|
if (!isHttpUrl(url)) {
|
|
1749
|
-
console.log(
|
|
1976
|
+
console.log(import_chalk10.default.yellow(" Enter a valid http(s) URL.\n"));
|
|
1750
1977
|
continue;
|
|
1751
1978
|
}
|
|
1752
1979
|
targetUrl = url.trim();
|
|
@@ -1757,7 +1984,7 @@ async function runCreate(opts = {}) {
|
|
|
1757
1984
|
}
|
|
1758
1985
|
if (interactive && !opts.yes) {
|
|
1759
1986
|
const { default: inquirer3 } = await import("inquirer");
|
|
1760
|
-
console.log(`${
|
|
1987
|
+
console.log(`${import_chalk10.default.cyan("\u2192")} Auth: ${import_chalk10.default.bold(auth)}${auth === "api_key" ? " \u2014 consumers send an X-API-Key header" : ""}`);
|
|
1761
1988
|
const { ok } = await inquirer3.prompt([{
|
|
1762
1989
|
type: "confirm",
|
|
1763
1990
|
name: "ok",
|
|
@@ -1765,15 +1992,15 @@ async function runCreate(opts = {}) {
|
|
|
1765
1992
|
default: true
|
|
1766
1993
|
}]);
|
|
1767
1994
|
if (!ok) {
|
|
1768
|
-
console.log(
|
|
1995
|
+
console.log(import_chalk10.default.yellow("Cancelled."));
|
|
1769
1996
|
return;
|
|
1770
1997
|
}
|
|
1771
1998
|
}
|
|
1772
|
-
const spinner = !opts.json ? (0,
|
|
1999
|
+
const spinner = !opts.json ? (0, import_ora5.default)("Creating proxy (tenant, keys, dev portal)...").start() : null;
|
|
1773
2000
|
let result;
|
|
1774
2001
|
try {
|
|
1775
2002
|
result = await createProxy({ name, target_url: targetUrl, auth_type: auth, team_id: teamId, ...opts.apiversion ? { api_version: opts.apiversion } : {} });
|
|
1776
|
-
spinner?.succeed(
|
|
2003
|
+
spinner?.succeed(import_chalk10.default.green("Proxy created!"));
|
|
1777
2004
|
} catch (err) {
|
|
1778
2005
|
spinner?.fail("Failed to create proxy.");
|
|
1779
2006
|
throw err;
|
|
@@ -1796,26 +2023,37 @@ async function runCreate(opts = {}) {
|
|
|
1796
2023
|
return;
|
|
1797
2024
|
}
|
|
1798
2025
|
console.log();
|
|
1799
|
-
console.log(` ${
|
|
1800
|
-
if (devPortal) console.log(` ${
|
|
2026
|
+
console.log(` ${import_chalk10.default.dim("Proxy URL: ")} ${import_chalk10.default.bold(proxyUrl)}`);
|
|
2027
|
+
if (devPortal) console.log(` ${import_chalk10.default.dim("Dev portal:")} ${import_chalk10.default.bold(devPortal)}`);
|
|
1801
2028
|
if (adminKey) {
|
|
1802
2029
|
console.log();
|
|
1803
|
-
console.log(` ${
|
|
1804
|
-
console.log(` ${
|
|
1805
|
-
console.log(
|
|
2030
|
+
console.log(` ${import_chalk10.default.dim("Consumer admin API key (dev):")}`);
|
|
2031
|
+
console.log(` ${import_chalk10.default.bold.green(adminKey)}`);
|
|
2032
|
+
console.log(import_chalk10.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
|
|
1806
2033
|
const otherEnvs = Object.keys(keys).filter((e) => e !== "dev");
|
|
1807
2034
|
if (otherEnvs.length) {
|
|
1808
|
-
console.log(
|
|
2035
|
+
console.log(import_chalk10.default.dim(` (Separate keys were also created for: ${otherEnvs.join(", ")}.)`));
|
|
1809
2036
|
}
|
|
1810
2037
|
}
|
|
1811
2038
|
printCurlExample(proxyUrl, auth, adminKey, devPortal);
|
|
1812
2039
|
console.log();
|
|
2040
|
+
await applyCreateToggles(name, opts);
|
|
2041
|
+
}
|
|
2042
|
+
async function applyCreateToggles(name, opts) {
|
|
2043
|
+
if (!opts.identified && !opts.iam) return;
|
|
2044
|
+
const { runIamToggle: runIamToggle2, runIdentifiedToggle: runIdentifiedToggle2 } = await Promise.resolve().then(() => (init_iam(), iam_exports));
|
|
2045
|
+
if (opts.identified) {
|
|
2046
|
+
await runIdentifiedToggle2(name, "require", { team: opts.team, json: opts.json });
|
|
2047
|
+
}
|
|
2048
|
+
if (opts.iam) {
|
|
2049
|
+
await runIamToggle2(name, "on", { team: opts.team, json: opts.json });
|
|
2050
|
+
}
|
|
1813
2051
|
}
|
|
1814
2052
|
async function runAnonymousCreate(opts) {
|
|
1815
2053
|
const interactive = !!process.stdin.isTTY && !opts.json;
|
|
1816
2054
|
if (!opts.json) {
|
|
1817
|
-
console.log(
|
|
1818
|
-
console.log(
|
|
2055
|
+
console.log(import_chalk10.default.bold("\nCreate an API proxy"));
|
|
2056
|
+
console.log(import_chalk10.default.dim("Not logged in \u2014 creating an anonymous proxy. You can claim it to your account within 30 days.\n"));
|
|
1819
2057
|
}
|
|
1820
2058
|
let body = {};
|
|
1821
2059
|
if (opts.config) {
|
|
@@ -1861,7 +2099,7 @@ async function runAnonymousCreate(opts) {
|
|
|
1861
2099
|
message: "Target URL to forward requests to (e.g. https://httpbin.org):"
|
|
1862
2100
|
}]);
|
|
1863
2101
|
if (!isHttpUrl(url)) {
|
|
1864
|
-
console.log(
|
|
2102
|
+
console.log(import_chalk10.default.yellow(" Enter a valid http(s) URL.\n"));
|
|
1865
2103
|
continue;
|
|
1866
2104
|
}
|
|
1867
2105
|
target = url.trim();
|
|
@@ -1888,7 +2126,7 @@ async function runAnonymousCreate(opts) {
|
|
|
1888
2126
|
const { loadAnonCred: loadAnonCred2, saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2, cpFetch: cpFetch2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
1889
2127
|
if (opts.newSession) clearAnonCred2();
|
|
1890
2128
|
const cred = loadAnonCred2();
|
|
1891
|
-
const spinner = !opts.json ? (0,
|
|
2129
|
+
const spinner = !opts.json ? (0, import_ora5.default)(cred ? "Creating proxy (in your anonymous workspace)..." : "Creating proxy...").start() : null;
|
|
1892
2130
|
let result;
|
|
1893
2131
|
try {
|
|
1894
2132
|
if (cred) {
|
|
@@ -1902,7 +2140,7 @@ async function runAnonymousCreate(opts) {
|
|
|
1902
2140
|
saveAnonCred2(result.cp_key, result.team_id, result.claim_code);
|
|
1903
2141
|
}
|
|
1904
2142
|
}
|
|
1905
|
-
spinner?.succeed(
|
|
2143
|
+
spinner?.succeed(import_chalk10.default.green("Proxy created!"));
|
|
1906
2144
|
} catch (err) {
|
|
1907
2145
|
spinner?.fail("Failed to create proxy.");
|
|
1908
2146
|
throw err;
|
|
@@ -1924,36 +2162,37 @@ async function runAnonymousCreate(opts) {
|
|
|
1924
2162
|
return;
|
|
1925
2163
|
}
|
|
1926
2164
|
console.log();
|
|
1927
|
-
if (prodEndpoint) console.log(` ${
|
|
1928
|
-
if (result.portal) console.log(` ${
|
|
2165
|
+
if (prodEndpoint) console.log(` ${import_chalk10.default.dim("Proxy URL: ")} ${import_chalk10.default.bold(prodEndpoint)}`);
|
|
2166
|
+
if (result.portal) console.log(` ${import_chalk10.default.dim("Dev portal:")} ${import_chalk10.default.bold(result.portal)}`);
|
|
1929
2167
|
if (apiKey) {
|
|
1930
2168
|
console.log();
|
|
1931
|
-
console.log(` ${
|
|
1932
|
-
console.log(` ${
|
|
1933
|
-
console.log(
|
|
2169
|
+
console.log(` ${import_chalk10.default.dim("API key:")}`);
|
|
2170
|
+
console.log(` ${import_chalk10.default.bold.green(apiKey)}`);
|
|
2171
|
+
console.log(import_chalk10.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
|
|
1934
2172
|
}
|
|
1935
2173
|
if (prodEndpoint) printCurlExample(prodEndpoint, opts.auth || "api_key", apiKey, result.portal);
|
|
1936
2174
|
const claimCode = result.claim_code || cred?.claim_code;
|
|
1937
2175
|
if (claimCode) {
|
|
1938
2176
|
console.log();
|
|
1939
|
-
console.log(` ${
|
|
1940
|
-
console.log(` ${
|
|
2177
|
+
console.log(` ${import_chalk10.default.yellow("\u26A0 Anonymous \u2014 claim within 30 days or it expires.")} To claim these proxies:`);
|
|
2178
|
+
console.log(` ${import_chalk10.default.cyan(`npx apiblaze login && npx apiblaze claim ${claimCode}`)}`);
|
|
1941
2179
|
} else if (result.claim_url) {
|
|
1942
2180
|
console.log();
|
|
1943
|
-
console.log(` ${
|
|
1944
|
-
console.log(` ${
|
|
2181
|
+
console.log(` ${import_chalk10.default.yellow("\u26A0 Anonymous proxy \u2014 claim it to your account within 30 days or it expires:")}`);
|
|
2182
|
+
console.log(` ${import_chalk10.default.bold(result.claim_url)}`);
|
|
1945
2183
|
}
|
|
1946
2184
|
console.log();
|
|
2185
|
+
if (name) await applyCreateToggles(name, opts);
|
|
1947
2186
|
}
|
|
1948
2187
|
|
|
1949
2188
|
// src/commands/claim.ts
|
|
1950
|
-
var
|
|
2189
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
1951
2190
|
init_auth();
|
|
1952
2191
|
init_anon_cred();
|
|
1953
2192
|
init_api();
|
|
1954
2193
|
init_types();
|
|
1955
2194
|
function fail2(message) {
|
|
1956
|
-
console.error(
|
|
2195
|
+
console.error(import_chalk11.default.red(`Error: ${message}`));
|
|
1957
2196
|
process.exit(1);
|
|
1958
2197
|
}
|
|
1959
2198
|
async function runClaim(claimCodeArg, opts) {
|
|
@@ -1989,9 +2228,9 @@ async function runClaim(claimCodeArg, opts) {
|
|
|
1989
2228
|
const c = typeof err.body === "object" && err.body ? err.body.error : void 0;
|
|
1990
2229
|
if (c === "already_claimed" || c === "not_claimable") {
|
|
1991
2230
|
if (fromLocal) clearAnonCred();
|
|
1992
|
-
console.log(
|
|
1993
|
-
if (fromLocal) console.log(
|
|
1994
|
-
console.log(
|
|
2231
|
+
console.log(import_chalk11.default.yellow("\nThis workspace was already claimed (e.g. on dashboard.apiblaze.com)."));
|
|
2232
|
+
if (fromLocal) console.log(import_chalk11.default.dim(" Cleared the local anonymous key."));
|
|
2233
|
+
console.log(import_chalk11.default.dim(" Your proxies are safe under your account \u2014 manage them with `apiblaze login`."));
|
|
1995
2234
|
return;
|
|
1996
2235
|
}
|
|
1997
2236
|
if (c === "expired") {
|
|
@@ -2009,20 +2248,20 @@ async function runClaim(claimCodeArg, opts) {
|
|
|
2009
2248
|
console.log(JSON.stringify(res, null, 2));
|
|
2010
2249
|
return;
|
|
2011
2250
|
}
|
|
2012
|
-
console.log(
|
|
2251
|
+
console.log(import_chalk11.default.green("\n\u2714 Claimed your anonymous workspace into your account."));
|
|
2013
2252
|
if (res.merged) {
|
|
2014
|
-
console.log(` Merged ${
|
|
2253
|
+
console.log(` Merged ${import_chalk11.default.bold(String(res.proxy_count ?? ""))} prox${res.proxy_count === 1 ? "y" : "ies"} into team ${import_chalk11.default.bold(res.team_id)}.`);
|
|
2015
2254
|
} else {
|
|
2016
2255
|
const label3 = res.team_name ? `\u201C${res.team_name}\u201D` : res.team_id;
|
|
2017
|
-
console.log(` It's now your team ${
|
|
2018
|
-
console.log(
|
|
2256
|
+
console.log(` It's now your team ${import_chalk11.default.bold(label3)}. Switch to it with ${import_chalk11.default.cyan(`apiblaze team ${res.team_id}`)}.`);
|
|
2257
|
+
console.log(import_chalk11.default.dim(` (Tip: name it with --name, or --team <name> to merge into an existing team instead.)`));
|
|
2019
2258
|
}
|
|
2020
2259
|
console.log(`
|
|
2021
|
-
Manage it at ${
|
|
2260
|
+
Manage it at ${import_chalk11.default.cyan("https://dashboard.apiblaze.com/dashboard")}`);
|
|
2022
2261
|
}
|
|
2023
2262
|
|
|
2024
2263
|
// src/commands/logout.ts
|
|
2025
|
-
var
|
|
2264
|
+
var import_chalk12 = __toESM(require("chalk"));
|
|
2026
2265
|
init_auth();
|
|
2027
2266
|
|
|
2028
2267
|
// src/lib/consumer-auth.ts
|
|
@@ -2154,7 +2393,7 @@ async function runLogout(opts = {}) {
|
|
|
2154
2393
|
const creds = loadCredentials();
|
|
2155
2394
|
const consumer2 = loadConsumer();
|
|
2156
2395
|
if (!creds && !consumer2) {
|
|
2157
|
-
console.log(
|
|
2396
|
+
console.log(import_chalk12.default.yellow("You were not logged in."));
|
|
2158
2397
|
return;
|
|
2159
2398
|
}
|
|
2160
2399
|
let target;
|
|
@@ -2181,16 +2420,16 @@ async function runLogout(opts = {}) {
|
|
|
2181
2420
|
}
|
|
2182
2421
|
if (target === "producer" || target === "all") {
|
|
2183
2422
|
clearCredentials();
|
|
2184
|
-
console.log(
|
|
2423
|
+
console.log(import_chalk12.default.green(`\u2714 Logged out of API Producer${creds?.githubHandle ? ` (${creds.githubHandle})` : ""}.`));
|
|
2185
2424
|
}
|
|
2186
2425
|
if (target === "consumer" || target === "all") {
|
|
2187
2426
|
clearConsumer();
|
|
2188
|
-
console.log(
|
|
2427
|
+
console.log(import_chalk12.default.green(`\u2714 Logged out of API Consumer${consumer2?.tenant ? ` (${consumer2.tenant})` : ""}.`));
|
|
2189
2428
|
}
|
|
2190
2429
|
}
|
|
2191
2430
|
|
|
2192
2431
|
// src/commands/flush.ts
|
|
2193
|
-
var
|
|
2432
|
+
var import_chalk13 = __toESM(require("chalk"));
|
|
2194
2433
|
var fs6 = __toESM(require("fs"));
|
|
2195
2434
|
var path4 = __toESM(require("path"));
|
|
2196
2435
|
init_auth();
|
|
@@ -2198,22 +2437,22 @@ async function runFlush(opts) {
|
|
|
2198
2437
|
const dir = getApiblazeDir();
|
|
2199
2438
|
const entries2 = fs6.existsSync(dir) ? fs6.readdirSync(dir) : [];
|
|
2200
2439
|
if (!entries2.length) {
|
|
2201
|
-
console.log(
|
|
2440
|
+
console.log(import_chalk13.default.dim("Nothing to flush \u2014 no local apiblaze config found."));
|
|
2202
2441
|
return;
|
|
2203
2442
|
}
|
|
2204
|
-
console.log(
|
|
2205
|
-
console.log(
|
|
2206
|
-
console.log(
|
|
2207
|
-
console.log(
|
|
2208
|
-
console.log(
|
|
2209
|
-
console.log(
|
|
2443
|
+
console.log(import_chalk13.default.yellow("This removes ALL local apiblaze state from this machine:"));
|
|
2444
|
+
console.log(import_chalk13.default.dim(" \u2022 login credentials & anonymous-workspace keys"));
|
|
2445
|
+
console.log(import_chalk13.default.dim(" \u2022 saved apichats \u2014 their DP API keys and transcripts"));
|
|
2446
|
+
console.log(import_chalk13.default.dim(" \u2022 your BYO LLM key and any cached secrets"));
|
|
2447
|
+
console.log(import_chalk13.default.dim(` (${entries2.length} item(s) in ${dir})`));
|
|
2448
|
+
console.log(import_chalk13.default.dim(" It does NOT delete server-side proxies/keys \u2014 those GC or are revoked separately."));
|
|
2210
2449
|
if (!opts.yes) {
|
|
2211
2450
|
const { default: inquirer3 } = await import("inquirer");
|
|
2212
2451
|
const { ok } = await inquirer3.prompt([
|
|
2213
2452
|
{ type: "confirm", name: "ok", message: "Delete all local state?", default: false }
|
|
2214
2453
|
]);
|
|
2215
2454
|
if (!ok) {
|
|
2216
|
-
console.log(
|
|
2455
|
+
console.log(import_chalk13.default.dim("Cancelled \u2014 nothing removed."));
|
|
2217
2456
|
return;
|
|
2218
2457
|
}
|
|
2219
2458
|
}
|
|
@@ -2223,14 +2462,14 @@ async function runFlush(opts) {
|
|
|
2223
2462
|
fs6.rmSync(path4.join(dir, e), { recursive: true, force: true });
|
|
2224
2463
|
removed++;
|
|
2225
2464
|
} catch (err) {
|
|
2226
|
-
console.log(
|
|
2465
|
+
console.log(import_chalk13.default.red(` \u2717 could not remove ${e}: ${err instanceof Error ? err.message : "error"}`));
|
|
2227
2466
|
}
|
|
2228
2467
|
}
|
|
2229
|
-
console.log(`${
|
|
2468
|
+
console.log(`${import_chalk13.default.green("\u2714")} Flushed \u2014 removed ${removed} item(s). You're fully logged out and clean.`);
|
|
2230
2469
|
}
|
|
2231
2470
|
|
|
2232
2471
|
// src/commands/whoami.ts
|
|
2233
|
-
var
|
|
2472
|
+
var import_chalk14 = __toESM(require("chalk"));
|
|
2234
2473
|
init_auth();
|
|
2235
2474
|
init_anon_cred();
|
|
2236
2475
|
async function runWhoami(opts = {}) {
|
|
@@ -2253,43 +2492,43 @@ async function runWhoami(opts = {}) {
|
|
|
2253
2492
|
}, null, 2));
|
|
2254
2493
|
return;
|
|
2255
2494
|
}
|
|
2256
|
-
console.log(
|
|
2495
|
+
console.log(import_chalk14.default.bold("API Producer"));
|
|
2257
2496
|
if (!creds) {
|
|
2258
|
-
console.log(
|
|
2497
|
+
console.log(import_chalk14.default.dim(" Not logged in. Run `apiblaze login`."));
|
|
2259
2498
|
} else {
|
|
2260
2499
|
const who = creds.githubHandle ?? creds.email ?? creds.apiblazeUserId ?? "unknown";
|
|
2261
|
-
console.log(` ${
|
|
2500
|
+
console.log(` ${import_chalk14.default.cyan("Signed in as")} ${import_chalk14.default.bold(who)}`);
|
|
2262
2501
|
if (creds.email && creds.email !== who) console.log(` Email: ${creds.email}`);
|
|
2263
|
-
if (creds.teamName || creds.teamId) console.log(` Team: ${
|
|
2264
|
-
if (creds.activeTenant) console.log(` Tenant scope: ${
|
|
2265
|
-
if (Date.now() >= creds.expiresAt) console.log(
|
|
2502
|
+
if (creds.teamName || creds.teamId) console.log(` Team: ${import_chalk14.default.bold(creds.teamName ?? creds.teamId)}`);
|
|
2503
|
+
if (creds.activeTenant) console.log(` Tenant scope: ${import_chalk14.default.bold(creds.activeTenant)} ${import_chalk14.default.dim("(clear: `apiblaze tenant use --clear`)")}`);
|
|
2504
|
+
if (Date.now() >= creds.expiresAt) console.log(import_chalk14.default.yellow(" \u26A0 Session expired \u2014 run `apiblaze login`."));
|
|
2266
2505
|
}
|
|
2267
2506
|
if (anon) {
|
|
2268
2507
|
const masked = `${anon.cp_key.slice(0, 11)}\u2026${anon.cp_key.slice(-4)}`;
|
|
2269
|
-
console.log(
|
|
2270
|
-
console.log(` Team: ${
|
|
2271
|
-
console.log(` CP key: ${
|
|
2508
|
+
console.log(import_chalk14.default.bold("\nAnonymous workspace") + import_chalk14.default.dim(" (created without an account \u2014 unclaimed)"));
|
|
2509
|
+
console.log(` Team: ${import_chalk14.default.bold(anon.team_id)}`);
|
|
2510
|
+
console.log(` CP key: ${import_chalk14.default.bold(masked)} ${import_chalk14.default.dim("controls the proxies you created anonymously")}`);
|
|
2272
2511
|
console.log(
|
|
2273
|
-
|
|
2512
|
+
import_chalk14.default.dim(` Keep it: ${import_chalk14.default.cyan("apiblaze login")} then ${import_chalk14.default.cyan("apiblaze claim")}`) + (anon.claim_code ? import_chalk14.default.dim(` \xB7 from another machine: ${import_chalk14.default.cyan(`apiblaze claim ${anon.claim_code}`)}`) : "")
|
|
2274
2513
|
);
|
|
2275
|
-
if (creds) console.log(
|
|
2514
|
+
if (creds) console.log(import_chalk14.default.dim(" (you're logged in \u2014 `apiblaze claim` folds this workspace into your account)"));
|
|
2276
2515
|
}
|
|
2277
|
-
console.log(
|
|
2516
|
+
console.log(import_chalk14.default.bold("\nAPI Consumer"));
|
|
2278
2517
|
if (!consumer2) {
|
|
2279
|
-
console.log(
|
|
2518
|
+
console.log(import_chalk14.default.dim(" Not logged in. Run `apiblaze consumer login`."));
|
|
2280
2519
|
} else {
|
|
2281
|
-
console.log(` ${
|
|
2282
|
-
console.log(` Tenant: ${
|
|
2283
|
-
if (Date.now() >= consumer2.expiresAt) console.log(
|
|
2520
|
+
console.log(` ${import_chalk14.default.cyan("Signed in as")} ${import_chalk14.default.bold(consumer2.email ?? consumer2.tenant)}`);
|
|
2521
|
+
console.log(` Tenant: ${import_chalk14.default.bold(consumer2.tenant)}`);
|
|
2522
|
+
if (Date.now() >= consumer2.expiresAt) console.log(import_chalk14.default.yellow(" \u26A0 Token expired \u2014 it will refresh on next use, or re-run `apiblaze consumer login`."));
|
|
2284
2523
|
}
|
|
2285
2524
|
}
|
|
2286
2525
|
|
|
2287
2526
|
// src/commands/team.ts
|
|
2288
|
-
var
|
|
2527
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
2289
2528
|
init_auth();
|
|
2290
2529
|
init_api();
|
|
2291
2530
|
function fail3(message) {
|
|
2292
|
-
console.error(
|
|
2531
|
+
console.error(import_chalk15.default.red(`Error: ${message}`));
|
|
2293
2532
|
process.exit(1);
|
|
2294
2533
|
}
|
|
2295
2534
|
async function runTeam(arg) {
|
|
@@ -2311,7 +2550,7 @@ async function runTeam(arg) {
|
|
|
2311
2550
|
}
|
|
2312
2551
|
} else if (teams.length === 1) {
|
|
2313
2552
|
chosen = teams[0];
|
|
2314
|
-
console.log(`${
|
|
2553
|
+
console.log(`${import_chalk15.default.cyan("\u2192")} You only have one team: ${import_chalk15.default.bold(chosen.name)}`);
|
|
2315
2554
|
} else if (process.stdin.isTTY) {
|
|
2316
2555
|
const { default: inquirer3 } = await import("inquirer");
|
|
2317
2556
|
const { picked } = await inquirer3.prompt([{
|
|
@@ -2326,47 +2565,47 @@ async function runTeam(arg) {
|
|
|
2326
2565
|
fail3(`Specify a team: \`apiblaze team <name|id>\`. Available: ${teams.map((t) => t.name).join(", ")}.`);
|
|
2327
2566
|
}
|
|
2328
2567
|
saveCredentials({ ...creds, teamId: chosen.teamId, teamName: chosen.name });
|
|
2329
|
-
console.log(
|
|
2330
|
-
\u2714 Active team: ${
|
|
2568
|
+
console.log(import_chalk15.default.green(`
|
|
2569
|
+
\u2714 Active team: ${import_chalk15.default.bold(chosen.name)}`));
|
|
2331
2570
|
}
|
|
2332
2571
|
|
|
2333
2572
|
// src/commands/authz.ts
|
|
2334
|
-
var
|
|
2573
|
+
var import_chalk18 = __toESM(require("chalk"));
|
|
2335
2574
|
init_auth();
|
|
2336
2575
|
init_api();
|
|
2337
2576
|
|
|
2338
2577
|
// src/lib/agent-chat.ts
|
|
2339
2578
|
var import_readline = __toESM(require("readline"));
|
|
2340
|
-
var
|
|
2579
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
2341
2580
|
init_api();
|
|
2342
2581
|
init_admin();
|
|
2343
2582
|
async function runAgentChatRepl(opts) {
|
|
2344
2583
|
const { title, subtitle, endpoint, buildBody, seedPrompt, summarizeProposal, commands } = opts;
|
|
2345
|
-
console.log("\n" +
|
|
2346
|
-
if (subtitle) console.log(
|
|
2584
|
+
console.log("\n" + import_chalk16.default.cyan.bold(title));
|
|
2585
|
+
if (subtitle) console.log(import_chalk16.default.dim(subtitle));
|
|
2347
2586
|
const messages = [];
|
|
2348
2587
|
const ctx = { proposal: null, lastData: null, log: (s) => console.log(s) };
|
|
2349
2588
|
const cmdByName = new Map(commands.map((c) => [c.name, c]));
|
|
2350
2589
|
const footer = () => {
|
|
2351
2590
|
const parts = [
|
|
2352
|
-
|
|
2353
|
-
...commands.map((c) =>
|
|
2354
|
-
|
|
2355
|
-
|
|
2591
|
+
import_chalk16.default.dim("type to chat/refine"),
|
|
2592
|
+
...commands.map((c) => import_chalk16.default.dim(`/${c.name} ${c.describe}`)),
|
|
2593
|
+
import_chalk16.default.dim("/show"),
|
|
2594
|
+
import_chalk16.default.dim("/drop discard & exit")
|
|
2356
2595
|
];
|
|
2357
|
-
return " " +
|
|
2596
|
+
return " " + import_chalk16.default.dim("[ ") + parts.join(import_chalk16.default.dim(" \xB7 ")) + import_chalk16.default.dim(" ]");
|
|
2358
2597
|
};
|
|
2359
2598
|
async function turn(content) {
|
|
2360
2599
|
messages.push({ role: "user", content });
|
|
2361
|
-
process.stdout.write(
|
|
2600
|
+
process.stdout.write(import_chalk16.default.dim(" \u2026thinking\n"));
|
|
2362
2601
|
const { status, data } = await agentCall(endpoint, "POST", { messages, ...buildBody(messages) });
|
|
2363
2602
|
if (status === 402) {
|
|
2364
|
-
console.log(
|
|
2603
|
+
console.log(import_chalk16.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
|
|
2365
2604
|
messages.pop();
|
|
2366
2605
|
return;
|
|
2367
2606
|
}
|
|
2368
2607
|
if (status >= 400 || !data) {
|
|
2369
|
-
console.log(
|
|
2608
|
+
console.log(import_chalk16.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
|
|
2370
2609
|
`));
|
|
2371
2610
|
messages.pop();
|
|
2372
2611
|
return;
|
|
@@ -2374,19 +2613,19 @@ async function runAgentChatRepl(opts) {
|
|
|
2374
2613
|
ctx.lastData = data;
|
|
2375
2614
|
const reply = data.reply ?? data.message ?? "(no reply)";
|
|
2376
2615
|
messages.push({ role: "assistant", content: reply });
|
|
2377
|
-
console.log("\n" +
|
|
2616
|
+
console.log("\n" + import_chalk16.default.cyan("agent \u203A ") + reply + "\n");
|
|
2378
2617
|
if (data.proposal) {
|
|
2379
2618
|
ctx.proposal = data.proposal;
|
|
2380
2619
|
const summary = summarizeProposal?.(data);
|
|
2381
|
-
if (summary) console.log(
|
|
2620
|
+
if (summary) console.log(import_chalk16.default.yellow(" " + summary));
|
|
2382
2621
|
const ready = commands.filter((c) => c.needsProposal).map((c) => `/${c.name}`).join(" or ");
|
|
2383
|
-
if (ready) console.log(
|
|
2622
|
+
if (ready) console.log(import_chalk16.default.dim(` Ready \u2014 ${ready} to make it official, or keep refining.`));
|
|
2384
2623
|
console.log();
|
|
2385
2624
|
}
|
|
2386
2625
|
const line = formatBilling(data.billing);
|
|
2387
2626
|
if (line) {
|
|
2388
2627
|
const llm2 = data.llm;
|
|
2389
|
-
console.log(
|
|
2628
|
+
console.log(import_chalk16.default.dim(` ${line}${llm2?.model ? ` \xB7 ${llm2.model}` : ""}`));
|
|
2390
2629
|
}
|
|
2391
2630
|
}
|
|
2392
2631
|
const rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -2394,26 +2633,26 @@ async function runAgentChatRepl(opts) {
|
|
|
2394
2633
|
await turn(seedPrompt);
|
|
2395
2634
|
for (; ; ) {
|
|
2396
2635
|
console.log(footer());
|
|
2397
|
-
const line = (await ask(
|
|
2636
|
+
const line = (await ask(import_chalk16.default.green("you \u203A "))).trim();
|
|
2398
2637
|
if (!line) continue;
|
|
2399
2638
|
if (line === "/drop" || line === "/exit" || line === "/quit") {
|
|
2400
|
-
console.log(
|
|
2639
|
+
console.log(import_chalk16.default.dim(" Dropped \u2014 nothing applied.\n"));
|
|
2401
2640
|
break;
|
|
2402
2641
|
}
|
|
2403
2642
|
if (line === "/show") {
|
|
2404
|
-
if (!ctx.proposal) console.log(
|
|
2643
|
+
if (!ctx.proposal) console.log(import_chalk16.default.dim(" No proposal yet \u2014 keep chatting until the agent proposes one.\n"));
|
|
2405
2644
|
else console.log("\n" + JSON.stringify(ctx.proposal, null, 2) + "\n");
|
|
2406
2645
|
continue;
|
|
2407
2646
|
}
|
|
2408
2647
|
if (line.startsWith("/")) {
|
|
2409
2648
|
const cmd = cmdByName.get(line.slice(1));
|
|
2410
2649
|
if (!cmd) {
|
|
2411
|
-
console.log(
|
|
2650
|
+
console.log(import_chalk16.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
|
|
2412
2651
|
`));
|
|
2413
2652
|
continue;
|
|
2414
2653
|
}
|
|
2415
2654
|
if (cmd.needsProposal && !ctx.proposal) {
|
|
2416
|
-
console.log(
|
|
2655
|
+
console.log(import_chalk16.default.yellow(" No proposal yet \u2014 keep chatting until the agent generates one, then try again.\n"));
|
|
2417
2656
|
continue;
|
|
2418
2657
|
}
|
|
2419
2658
|
await cmd.run(ctx);
|
|
@@ -2425,19 +2664,48 @@ async function runAgentChatRepl(opts) {
|
|
|
2425
2664
|
}
|
|
2426
2665
|
|
|
2427
2666
|
// src/lib/authz-apply.ts
|
|
2428
|
-
var
|
|
2667
|
+
var import_chalk17 = __toESM(require("chalk"));
|
|
2429
2668
|
init_api();
|
|
2669
|
+
async function gatherSampleIds(projectId, apiVersion, cap = 50) {
|
|
2670
|
+
try {
|
|
2671
|
+
const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
|
|
2672
|
+
const routes = routesRes?.data?.routes ?? [];
|
|
2673
|
+
const total = routes.reduce((n, r) => n + (Number(r.total) || 0), 0);
|
|
2674
|
+
if (!routes.length || total === 0) return { ids: [], total: 0 };
|
|
2675
|
+
const ordered = routes.filter((r) => r.route_hash).sort((a, b) => (Number(b.total) || 0) - (Number(a.total) || 0)).slice(0, 20);
|
|
2676
|
+
const ids = [];
|
|
2677
|
+
for (const r of ordered) {
|
|
2678
|
+
if (ids.length >= cap) break;
|
|
2679
|
+
const sRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes/${encodeURIComponent(r.route_hash)}/samples`, "GET");
|
|
2680
|
+
for (const s of sRes?.data?.samples ?? []) {
|
|
2681
|
+
if (ids.length >= cap) break;
|
|
2682
|
+
if (s.sample_id) ids.push(s.sample_id);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
return { ids, total };
|
|
2686
|
+
} catch {
|
|
2687
|
+
return { ids: [], total: 0 };
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
function maybeWarnNoTraffic(total, projectArg) {
|
|
2691
|
+
if (total >= 3) return;
|
|
2692
|
+
console.log(import_chalk17.default.yellow(total === 0 ? " \u26A0 No captured traffic for this API yet." : ` \u26A0 Only ${total} captured request(s) so far.`));
|
|
2693
|
+
console.log(import_chalk17.default.dim(" The authorization agent designs much better models & rules from REAL requests"));
|
|
2694
|
+
console.log(import_chalk17.default.dim(" (it sees who is allowed vs denied). Send some through your proxy first \u2014"));
|
|
2695
|
+
console.log(import_chalk17.default.dim(` curl a few endpoints, or `) + import_chalk17.default.cyan("npx apiblaze dev") + import_chalk17.default.dim(` \u2014 then re-run.
|
|
2696
|
+
`));
|
|
2697
|
+
}
|
|
2430
2698
|
async function applyProposal(opts) {
|
|
2431
2699
|
const log = opts.log ?? ((s) => console.log(s));
|
|
2432
2700
|
const { projectId, apiVersion, tenant: tenant2, proposal, enable } = opts;
|
|
2433
2701
|
if (!enable) {
|
|
2434
2702
|
const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant2)}`, "POST", proposal.model);
|
|
2435
2703
|
if (m.status === 404) {
|
|
2436
|
-
log(
|
|
2704
|
+
log(import_chalk17.default.red(" Authorization store not provisioned yet. Open the dashboard Authorization tab for this project once (it auto-provisions the store), then re-run.\n"));
|
|
2437
2705
|
return { ok: 0, fail: 0, enabled: false, storeMissing: true };
|
|
2438
2706
|
}
|
|
2439
2707
|
if (m.status >= 400) {
|
|
2440
|
-
log(
|
|
2708
|
+
log(import_chalk17.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
|
|
2441
2709
|
`));
|
|
2442
2710
|
return { ok: 0, fail: 0, enabled: false };
|
|
2443
2711
|
}
|
|
@@ -2466,7 +2734,7 @@ async function applyProposal(opts) {
|
|
|
2466
2734
|
if (enable) {
|
|
2467
2735
|
const e = await agentCall(`/${projectId}/${apiVersion}/config`, "PATCH", { authorization: { enforce_authorization: true }, tenant: tenant2 });
|
|
2468
2736
|
if (e.status >= 400) {
|
|
2469
|
-
log(
|
|
2737
|
+
log(import_chalk17.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
|
|
2470
2738
|
`));
|
|
2471
2739
|
return { ok, fail: fail5, enabled: false };
|
|
2472
2740
|
}
|
|
@@ -2492,18 +2760,20 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
2492
2760
|
const r = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable, log: ctx.log });
|
|
2493
2761
|
if (r.storeMissing) return;
|
|
2494
2762
|
if (enable && r.enabled) {
|
|
2495
|
-
ctx.log(
|
|
2763
|
+
ctx.log(import_chalk18.default.green(` \u2713 Enforcement ON \u2014 ${r.ok} route(s) + project switch on${r.fail ? `, ${r.fail} failed` : ""}.
|
|
2496
2764
|
`));
|
|
2497
2765
|
} else if (!enable) {
|
|
2498
|
-
ctx.log(
|
|
2766
|
+
ctx.log(import_chalk18.default.green(` \u2713 Published (shadow): model + ${r.ok} route(s)${r.fail ? `, ${r.fail} failed` : ""}. Review, then /enable.
|
|
2499
2767
|
`));
|
|
2500
2768
|
}
|
|
2501
2769
|
}
|
|
2770
|
+
const { ids: sampleIds, total: sampleTotal } = await gatherSampleIds(projectId, apiVersion);
|
|
2771
|
+
maybeWarnNoTraffic(sampleTotal, projectArg);
|
|
2502
2772
|
await runAgentChatRepl({
|
|
2503
2773
|
title: `Authorization assistant \u2014 ${projectId} ${apiVersion}`,
|
|
2504
|
-
subtitle: `tenant ${tenant2} \xB7 discuss what authorization fits this API, then make it official.`,
|
|
2774
|
+
subtitle: sampleIds.length ? `tenant ${tenant2} \xB7 ${sampleIds.length} traffic sample(s) \xB7 discuss what fits, then make it official.` : `tenant ${tenant2} \xB7 discuss what authorization fits this API, then make it official.`,
|
|
2505
2775
|
endpoint: `/projects/${projectId}/${apiVersion}/authz/chat`,
|
|
2506
|
-
buildBody: () => ({ included_sample_ids:
|
|
2776
|
+
buildBody: () => ({ included_sample_ids: sampleIds, existing_model: null, existing_routes: [] }),
|
|
2507
2777
|
seedPrompt: "Analyze this API and tell me what authorization is feasible. If it is read-only, say so plainly. Then propose options \u2014 do not generate rules yet.",
|
|
2508
2778
|
summarizeProposal: (data) => {
|
|
2509
2779
|
const proposal = data.proposal;
|
|
@@ -2520,8 +2790,8 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
2520
2790
|
}
|
|
2521
2791
|
|
|
2522
2792
|
// src/commands/rule.ts
|
|
2523
|
-
var
|
|
2524
|
-
var
|
|
2793
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
2794
|
+
var import_ora6 = __toESM(require("ora"));
|
|
2525
2795
|
init_auth();
|
|
2526
2796
|
init_api();
|
|
2527
2797
|
async function runRule(ruleText, projectArg, opts) {
|
|
@@ -2538,7 +2808,9 @@ async function runRule(ruleText, projectArg, opts) {
|
|
|
2538
2808
|
const projectId = match.projectId;
|
|
2539
2809
|
const apiVersion = opts.apiversion || match.apiVersion;
|
|
2540
2810
|
const tenant2 = match.tenant || projectId;
|
|
2541
|
-
const
|
|
2811
|
+
const { ids: sampleIds, total: sampleTotal } = await gatherSampleIds(projectId, apiVersion);
|
|
2812
|
+
maybeWarnNoTraffic(sampleTotal, projectArg);
|
|
2813
|
+
const spinner = (0, import_ora6.default)(sampleIds.length ? `Designing the rule from ${sampleIds.length} traffic sample(s)\u2026` : "Designing the rule\u2026").start();
|
|
2542
2814
|
const messages = [{
|
|
2543
2815
|
role: "user",
|
|
2544
2816
|
content: `Author this authorization rule and generate the COMPLETE proposal (OpenFGA model + per-route rules) NOW for every affected route. Do not ask clarifying questions; make reasonable assumptions and state them briefly. Rule: "${rule}"`
|
|
@@ -2546,7 +2818,7 @@ async function runRule(ruleText, projectArg, opts) {
|
|
|
2546
2818
|
const { status, data } = await agentCall(
|
|
2547
2819
|
`/projects/${projectId}/${apiVersion}/authz/chat`,
|
|
2548
2820
|
"POST",
|
|
2549
|
-
{ messages, included_sample_ids:
|
|
2821
|
+
{ messages, included_sample_ids: sampleIds, existing_model: null, existing_routes: [] }
|
|
2550
2822
|
);
|
|
2551
2823
|
if (status >= 400) {
|
|
2552
2824
|
spinner.fail(`Authorization agent error (${status}): ${data?.error ?? ""}`);
|
|
@@ -2555,27 +2827,27 @@ async function runRule(ruleText, projectArg, opts) {
|
|
|
2555
2827
|
const proposal = data?.proposal;
|
|
2556
2828
|
if (!proposal || !Array.isArray(proposal.routes) || proposal.routes.length === 0) {
|
|
2557
2829
|
spinner.fail("The agent couldn\u2019t turn that into a rule in one shot.");
|
|
2558
|
-
if (data?.reply) console.log(
|
|
2559
|
-
console.log(
|
|
2830
|
+
if (data?.reply) console.log(import_chalk19.default.dim("\n " + String(data.reply).slice(0, 500) + "\n"));
|
|
2831
|
+
console.log(import_chalk19.default.dim(" Try an interactive session for anything nuanced: ") + import_chalk19.default.cyan(`apiblaze agent authz ${projectArg}`));
|
|
2560
2832
|
return;
|
|
2561
2833
|
}
|
|
2562
2834
|
spinner.succeed(`Rule designed \u2014 ${proposal.routes.length} route(s) affected.`);
|
|
2563
|
-
if (data?.reply) console.log(
|
|
2835
|
+
if (data?.reply) console.log(import_chalk19.default.dim(" " + String(data.reply).slice(0, 240)));
|
|
2564
2836
|
const pub = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable: false });
|
|
2565
2837
|
if (pub.storeMissing) return;
|
|
2566
|
-
console.log(
|
|
2838
|
+
console.log(import_chalk19.default.green(` \u2713 Published in shadow mode \u2014 model + ${pub.ok} route(s)${pub.fail ? `, ${pub.fail} failed` : ""}.`));
|
|
2567
2839
|
if (opts.enforce) {
|
|
2568
2840
|
const enf = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable: true });
|
|
2569
2841
|
if (enf.enabled) {
|
|
2570
|
-
console.log(
|
|
2842
|
+
console.log(import_chalk19.default.green(` \u2713 Enforcement ON \u2014 ${enf.ok} route(s) + project switch on${enf.fail ? `, ${enf.fail} failed` : ""}.`));
|
|
2571
2843
|
}
|
|
2572
2844
|
} else {
|
|
2573
|
-
console.log(
|
|
2845
|
+
console.log(import_chalk19.default.dim(` Review it (shadow logs), then enforce: `) + import_chalk19.default.cyan(`apiblaze rule "${rule}" ${projectArg} --enforce`));
|
|
2574
2846
|
}
|
|
2575
2847
|
}
|
|
2576
2848
|
|
|
2577
2849
|
// src/commands/openapi.ts
|
|
2578
|
-
var
|
|
2850
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
2579
2851
|
init_auth();
|
|
2580
2852
|
init_api();
|
|
2581
2853
|
async function runOpenapi(projectArg, apiVersionArg) {
|
|
@@ -2586,10 +2858,10 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
2586
2858
|
if (!match) throw new Error(`Project "${projectArg}" not found in your active team. Run \`apiblaze projects\`.`);
|
|
2587
2859
|
const projectId = match.projectId;
|
|
2588
2860
|
const apiVersion = apiVersionArg || match.apiVersion;
|
|
2589
|
-
console.log(
|
|
2861
|
+
console.log(import_chalk20.default.dim("Gathering captured traffic samples\u2026"));
|
|
2590
2862
|
const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
|
|
2591
2863
|
if (routesRes.status >= 400) {
|
|
2592
|
-
console.log(
|
|
2864
|
+
console.log(import_chalk20.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
|
|
2593
2865
|
return;
|
|
2594
2866
|
}
|
|
2595
2867
|
const routes = routesRes.data?.routes ?? (Array.isArray(routesRes.data) ? routesRes.data : []);
|
|
@@ -2602,25 +2874,25 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
2602
2874
|
}
|
|
2603
2875
|
const sampleIds = [...ids];
|
|
2604
2876
|
if (sampleIds.length === 0) {
|
|
2605
|
-
console.log(
|
|
2877
|
+
console.log(import_chalk20.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
|
|
2606
2878
|
return;
|
|
2607
2879
|
}
|
|
2608
2880
|
async function publish(ctx) {
|
|
2609
2881
|
const patch = ctx.proposal.patch;
|
|
2610
2882
|
if (!Array.isArray(patch) || patch.length === 0) {
|
|
2611
|
-
ctx.log(
|
|
2883
|
+
ctx.log(import_chalk20.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
|
|
2612
2884
|
return;
|
|
2613
2885
|
}
|
|
2614
2886
|
const specSource = ctx.lastData?.spec_source ?? "unknown";
|
|
2615
2887
|
const endpoint = specSource === "github" ? "open-pr" : "publish-openapi";
|
|
2616
2888
|
const pub = await agentCall(`/projects/${projectId}/${apiVersion}/samples/${endpoint}`, "POST", { patch, sample_ids_used: sampleIds });
|
|
2617
2889
|
if (pub.status >= 400) {
|
|
2618
|
-
ctx.log(
|
|
2890
|
+
ctx.log(import_chalk20.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
2619
2891
|
`));
|
|
2620
2892
|
return;
|
|
2621
2893
|
}
|
|
2622
2894
|
const prUrl = pub.data?.pr_url;
|
|
2623
|
-
ctx.log(
|
|
2895
|
+
ctx.log(import_chalk20.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
|
|
2624
2896
|
` : " \u2713 Published updated OpenAPI spec.\n"));
|
|
2625
2897
|
}
|
|
2626
2898
|
await runAgentChatRepl({
|
|
@@ -2641,7 +2913,7 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
2641
2913
|
}
|
|
2642
2914
|
|
|
2643
2915
|
// src/commands/mcp.ts
|
|
2644
|
-
var
|
|
2916
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
2645
2917
|
init_auth();
|
|
2646
2918
|
init_api();
|
|
2647
2919
|
async function runMcp(projectArg, apiVersionArg, opts) {
|
|
@@ -2658,11 +2930,11 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
2658
2930
|
const spec2 = ctx.proposal;
|
|
2659
2931
|
const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec: spec2 });
|
|
2660
2932
|
if (pub.status >= 400) {
|
|
2661
|
-
ctx.log(
|
|
2933
|
+
ctx.log(import_chalk21.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
2662
2934
|
`));
|
|
2663
2935
|
return;
|
|
2664
2936
|
}
|
|
2665
|
-
ctx.log(
|
|
2937
|
+
ctx.log(import_chalk21.default.green(` \u2713 Published MCP server \u2014 ${mcpHost}.mcp.apiblaze.com/${apiVersion}/${environment}.
|
|
2666
2938
|
`));
|
|
2667
2939
|
}
|
|
2668
2940
|
await runAgentChatRepl({
|
|
@@ -2684,69 +2956,14 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
2684
2956
|
}
|
|
2685
2957
|
|
|
2686
2958
|
// src/commands/delete.ts
|
|
2687
|
-
var
|
|
2688
|
-
var
|
|
2959
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
2960
|
+
var import_ora7 = __toESM(require("ora"));
|
|
2689
2961
|
init_admin();
|
|
2690
|
-
|
|
2691
|
-
// src/lib/resolve.ts
|
|
2692
|
-
var import_chalk20 = __toESM(require("chalk"));
|
|
2693
|
-
init_auth();
|
|
2694
|
-
init_api();
|
|
2695
|
-
function requireAuth() {
|
|
2696
|
-
const creds = loadCredentials();
|
|
2697
|
-
if (!creds) {
|
|
2698
|
-
console.error(import_chalk20.default.red("Not logged in. Run `apiblaze login` first."));
|
|
2699
|
-
process.exit(1);
|
|
2700
|
-
}
|
|
2701
|
-
return creds;
|
|
2702
|
-
}
|
|
2703
|
-
async function resolveTeam(opt) {
|
|
2704
|
-
const creds = requireAuth();
|
|
2705
|
-
if (!opt) {
|
|
2706
|
-
if (!creds.teamId) {
|
|
2707
|
-
console.error(import_chalk20.default.red("No active team. Run `apiblaze login` or pass --team."));
|
|
2708
|
-
process.exit(1);
|
|
2709
|
-
}
|
|
2710
|
-
return { teamId: creds.teamId, teamName: creds.teamName };
|
|
2711
|
-
}
|
|
2712
|
-
if (opt.startsWith("team_")) return { teamId: opt };
|
|
2713
|
-
const teams = await getTeams().catch(() => []);
|
|
2714
|
-
const match = teams.find((t) => t.name === opt || t.teamId === opt);
|
|
2715
|
-
if (!match) {
|
|
2716
|
-
console.error(import_chalk20.default.red(`Team "${opt}" not found.${teams.length ? " Available: " + teams.map((t) => t.name).join(", ") : ""}`));
|
|
2717
|
-
process.exit(1);
|
|
2718
|
-
}
|
|
2719
|
-
return { teamId: match.teamId, teamName: match.name };
|
|
2720
|
-
}
|
|
2721
|
-
async function resolveProject(teamId, nameOrId, version2) {
|
|
2722
|
-
const projects = await getProjects(teamId).catch(() => []);
|
|
2723
|
-
const candidates = projects.filter(
|
|
2724
|
-
(p) => p.projectId === nameOrId || p.projectName === nameOrId
|
|
2725
|
-
);
|
|
2726
|
-
if (candidates.length === 0) {
|
|
2727
|
-
console.error(import_chalk20.default.red(`Project "${nameOrId}" not found in this team.`));
|
|
2728
|
-
if (projects.length) console.error(import_chalk20.default.dim(" Known: " + projects.map((p) => p.projectName).join(", ")));
|
|
2729
|
-
process.exit(1);
|
|
2730
|
-
}
|
|
2731
|
-
const chosen = version2 ? candidates.find((p) => p.apiVersion === version2) : candidates[0];
|
|
2732
|
-
if (!chosen) {
|
|
2733
|
-
console.error(import_chalk20.default.red(`Project "${nameOrId}" has no version ${version2}. Versions: ${candidates.map((p) => p.apiVersion).join(", ")}`));
|
|
2734
|
-
process.exit(1);
|
|
2735
|
-
}
|
|
2736
|
-
return {
|
|
2737
|
-
projectId: chosen.projectId,
|
|
2738
|
-
projectName: chosen.projectName,
|
|
2739
|
-
apiVersion: chosen.apiVersion,
|
|
2740
|
-
teamId,
|
|
2741
|
-
tenant: chosen.tenant
|
|
2742
|
-
};
|
|
2743
|
-
}
|
|
2744
|
-
|
|
2745
|
-
// src/commands/delete.ts
|
|
2962
|
+
init_resolve();
|
|
2746
2963
|
async function runDelete(project, version2, opts) {
|
|
2747
2964
|
const { teamId } = await resolveTeam(opts.team);
|
|
2748
2965
|
const proj2 = await resolveProject(teamId, project, version2);
|
|
2749
|
-
const spinner = (0,
|
|
2966
|
+
const spinner = (0, import_ora7.default)("Checking delete impact...").start();
|
|
2750
2967
|
let impact;
|
|
2751
2968
|
try {
|
|
2752
2969
|
impact = await admin({
|
|
@@ -2765,10 +2982,10 @@ async function runDelete(project, version2, opts) {
|
|
|
2765
2982
|
return;
|
|
2766
2983
|
}
|
|
2767
2984
|
} else {
|
|
2768
|
-
console.log(`${
|
|
2985
|
+
console.log(`${import_chalk22.default.red.bold("Delete")} ${import_chalk22.default.bold(proj2.projectName)} ${import_chalk22.default.dim("v" + proj2.apiVersion)} ${import_chalk22.default.dim("(" + proj2.projectId + ")")}`);
|
|
2769
2986
|
if (impact && typeof impact === "object") {
|
|
2770
2987
|
const counts = impact.counts ?? impact.impact ?? impact;
|
|
2771
|
-
console.log(
|
|
2988
|
+
console.log(import_chalk22.default.dim(" This removes (cascade): ") + import_chalk22.default.yellow(JSON.stringify(counts)));
|
|
2772
2989
|
}
|
|
2773
2990
|
if (!opts.yes) {
|
|
2774
2991
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -2776,12 +2993,12 @@ async function runDelete(project, version2, opts) {
|
|
|
2776
2993
|
{ type: "confirm", name: "confirm", message: `Permanently delete ${proj2.projectName} v${proj2.apiVersion}? This cannot be undone.`, default: false }
|
|
2777
2994
|
]);
|
|
2778
2995
|
if (!confirm) {
|
|
2779
|
-
console.log(
|
|
2996
|
+
console.log(import_chalk22.default.dim("Aborted."));
|
|
2780
2997
|
return;
|
|
2781
2998
|
}
|
|
2782
2999
|
}
|
|
2783
3000
|
}
|
|
2784
|
-
const s2 = (0,
|
|
3001
|
+
const s2 = (0, import_ora7.default)("Deleting...").start();
|
|
2785
3002
|
try {
|
|
2786
3003
|
await admin({
|
|
2787
3004
|
method: "DELETE",
|
|
@@ -2797,10 +3014,11 @@ async function runDelete(project, version2, opts) {
|
|
|
2797
3014
|
}
|
|
2798
3015
|
|
|
2799
3016
|
// src/commands/export.ts
|
|
2800
|
-
var
|
|
3017
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
2801
3018
|
var import_fs3 = require("fs");
|
|
2802
3019
|
init_admin();
|
|
2803
3020
|
init_auth();
|
|
3021
|
+
init_resolve();
|
|
2804
3022
|
var DASHBOARD_BASE4 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
2805
3023
|
async function runExport(projectArg, versionArg, opts) {
|
|
2806
3024
|
const token = getAccessToken();
|
|
@@ -2815,7 +3033,7 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
2815
3033
|
if (opts.noConsumers) q.set("include_consumers", "0");
|
|
2816
3034
|
if (opts.keys) q.set("keys", opts.keys);
|
|
2817
3035
|
const qs = q.toString() ? `?${q.toString()}` : "";
|
|
2818
|
-
console.log(
|
|
3036
|
+
console.log(import_chalk23.default.dim(`Queuing ${target} export of ${projectId}@${version2}\u2026`));
|
|
2819
3037
|
const job = await admin({
|
|
2820
3038
|
path: `/projects/${projectId}/${version2}/export/${target}${qs}`,
|
|
2821
3039
|
method: "GET",
|
|
@@ -2825,11 +3043,11 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
2825
3043
|
const statusPath = `/projects/${projectId}/${version2}/export/jobs/${job.job_id}`;
|
|
2826
3044
|
let status = "queued";
|
|
2827
3045
|
const started = Date.now();
|
|
2828
|
-
process.stdout.write(
|
|
3046
|
+
process.stdout.write(import_chalk23.default.dim("Building bundle"));
|
|
2829
3047
|
while (status !== "ready" && status !== "failed") {
|
|
2830
3048
|
if (Date.now() - started > 10 * 6e4) throw new Error("Export timed out after 10 minutes.");
|
|
2831
3049
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
2832
|
-
process.stdout.write(
|
|
3050
|
+
process.stdout.write(import_chalk23.default.dim("."));
|
|
2833
3051
|
const st = await admin({ path: statusPath, method: "GET", summary: "poll export" });
|
|
2834
3052
|
status = st?.status ?? "queued";
|
|
2835
3053
|
if (status === "failed") {
|
|
@@ -2847,20 +3065,21 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
2847
3065
|
if (!res.ok) throw new Error(`Download failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
2848
3066
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
2849
3067
|
(0, import_fs3.writeFileSync)(outFile, buf);
|
|
2850
|
-
console.log(
|
|
2851
|
-
if (job.decryption_key) console.log(
|
|
2852
|
-
console.log(
|
|
2853
|
-
if (target === "kong") console.log(
|
|
3068
|
+
console.log(import_chalk23.default.green(`\u2713 Saved ${outFile} (${(buf.length / 1024).toFixed(0)} KB)`));
|
|
3069
|
+
if (job.decryption_key) console.log(import_chalk23.default.yellow(`Decryption key (shown once): ${job.decryption_key}`));
|
|
3070
|
+
console.log(import_chalk23.default.dim("Read MIGRATION-REPORT.json inside the zip for the full fidelity ledger."));
|
|
3071
|
+
if (target === "kong") console.log(import_chalk23.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
|
|
2854
3072
|
}
|
|
2855
3073
|
|
|
2856
3074
|
// src/commands/config.ts
|
|
2857
|
-
var
|
|
2858
|
-
var
|
|
3075
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
3076
|
+
var import_ora8 = __toESM(require("ora"));
|
|
2859
3077
|
init_admin();
|
|
3078
|
+
init_resolve();
|
|
2860
3079
|
async function patchConfig(project, opts, body, summary) {
|
|
2861
3080
|
const { teamId } = await resolveTeam(opts.team);
|
|
2862
3081
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2863
|
-
const spinner = (0,
|
|
3082
|
+
const spinner = (0, import_ora8.default)(summary + "...").start();
|
|
2864
3083
|
try {
|
|
2865
3084
|
const out = await admin({
|
|
2866
3085
|
method: "PATCH",
|
|
@@ -2877,7 +3096,7 @@ async function patchConfig(project, opts, body, summary) {
|
|
|
2877
3096
|
}
|
|
2878
3097
|
async function runTargetSet(project, opts) {
|
|
2879
3098
|
if (!opts.url) {
|
|
2880
|
-
console.error(
|
|
3099
|
+
console.error(import_chalk24.default.red("--url is required."));
|
|
2881
3100
|
process.exit(1);
|
|
2882
3101
|
}
|
|
2883
3102
|
const body = opts.env ? { environments: { [opts.env]: { target: opts.url } } } : { target_url: opts.url };
|
|
@@ -2890,43 +3109,45 @@ async function runThrottleSet(project, opts) {
|
|
|
2890
3109
|
if (opts.quota !== void 0) throttling.proxyQuota = Number(opts.quota);
|
|
2891
3110
|
if (opts.period !== void 0) {
|
|
2892
3111
|
if (!["daily", "weekly", "monthly"].includes(opts.period)) {
|
|
2893
|
-
console.error(
|
|
3112
|
+
console.error(import_chalk24.default.red("--period must be daily, weekly, or monthly."));
|
|
2894
3113
|
process.exit(1);
|
|
2895
3114
|
}
|
|
2896
3115
|
throttling.quotaPeriod = opts.period;
|
|
2897
3116
|
}
|
|
2898
3117
|
if (Object.keys(throttling).length === 0) {
|
|
2899
|
-
console.error(
|
|
3118
|
+
console.error(import_chalk24.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
|
|
2900
3119
|
process.exit(1);
|
|
2901
3120
|
}
|
|
2902
3121
|
await patchConfig(project, opts, { throttling }, `Update throttling ${JSON.stringify(throttling)}`);
|
|
2903
3122
|
}
|
|
2904
3123
|
async function runRename(project, opts) {
|
|
2905
3124
|
if (!opts.displayName) {
|
|
2906
|
-
console.error(
|
|
3125
|
+
console.error(import_chalk24.default.red("--display-name is required."));
|
|
2907
3126
|
process.exit(1);
|
|
2908
3127
|
}
|
|
2909
3128
|
await patchConfig(project, opts, { display_name: opts.displayName }, `Rename \u2192 "${opts.displayName}"`);
|
|
2910
3129
|
}
|
|
2911
3130
|
|
|
2912
3131
|
// src/commands/config-browse.ts
|
|
2913
|
-
var
|
|
2914
|
-
var
|
|
3132
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
3133
|
+
var import_ora16 = __toESM(require("ora"));
|
|
2915
3134
|
init_admin();
|
|
2916
3135
|
init_auth();
|
|
3136
|
+
init_resolve();
|
|
2917
3137
|
|
|
2918
3138
|
// src/commands/domain.ts
|
|
2919
|
-
var
|
|
2920
|
-
var
|
|
3139
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
3140
|
+
var import_ora9 = __toESM(require("ora"));
|
|
2921
3141
|
init_admin();
|
|
3142
|
+
init_resolve();
|
|
2922
3143
|
async function runDomainAdd(project, opts) {
|
|
2923
3144
|
if (!opts.domain) {
|
|
2924
|
-
console.error(
|
|
3145
|
+
console.error(import_chalk25.default.red("--domain is required."));
|
|
2925
3146
|
process.exit(1);
|
|
2926
3147
|
}
|
|
2927
3148
|
const { teamId } = await resolveTeam(opts.team);
|
|
2928
3149
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2929
|
-
const spinner = (0,
|
|
3150
|
+
const spinner = (0, import_ora9.default)(`Registering ${opts.domain}...`).start();
|
|
2930
3151
|
try {
|
|
2931
3152
|
const out = await admin({
|
|
2932
3153
|
method: "POST",
|
|
@@ -2940,9 +3161,9 @@ async function runDomainAdd(project, opts) {
|
|
|
2940
3161
|
console.log(JSON.stringify(out));
|
|
2941
3162
|
return;
|
|
2942
3163
|
}
|
|
2943
|
-
if (out?.cnameRecord) console.log(` ${
|
|
2944
|
-
if (out?.dcv) console.log(` ${
|
|
2945
|
-
if (out?.id) console.log(
|
|
3164
|
+
if (out?.cnameRecord) console.log(` ${import_chalk25.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
|
|
3165
|
+
if (out?.dcv) console.log(` ${import_chalk25.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
|
|
3166
|
+
if (out?.id) console.log(import_chalk25.default.dim(` domain id: ${out.id}`));
|
|
2946
3167
|
} catch (err) {
|
|
2947
3168
|
spinner.fail("Domain registration failed.");
|
|
2948
3169
|
throw err;
|
|
@@ -2962,14 +3183,14 @@ async function runDomainList(project, opts) {
|
|
|
2962
3183
|
return;
|
|
2963
3184
|
}
|
|
2964
3185
|
if (!domains.length) {
|
|
2965
|
-
console.log(
|
|
3186
|
+
console.log(import_chalk25.default.yellow("No custom domains."));
|
|
2966
3187
|
return;
|
|
2967
3188
|
}
|
|
2968
|
-
for (const d of domains) console.log(` ${
|
|
3189
|
+
for (const d of domains) console.log(` ${import_chalk25.default.bold(d.hostname)} ${import_chalk25.default.dim(d.status ?? "")} ${import_chalk25.default.dim(d.id ?? "")}`);
|
|
2969
3190
|
}
|
|
2970
3191
|
async function runDomainStatus(project, opts) {
|
|
2971
3192
|
if (!opts.id) {
|
|
2972
|
-
console.error(
|
|
3193
|
+
console.error(import_chalk25.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
2973
3194
|
process.exit(1);
|
|
2974
3195
|
}
|
|
2975
3196
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -2979,16 +3200,16 @@ async function runDomainStatus(project, opts) {
|
|
|
2979
3200
|
path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}/status`,
|
|
2980
3201
|
summary: `Check custom-domain status`
|
|
2981
3202
|
});
|
|
2982
|
-
console.log(opts.json ? JSON.stringify(out) : ` ${
|
|
3203
|
+
console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk25.default.bold(out?.hostname ?? opts.id)}: ${import_chalk25.default.cyan(out?.status ?? "unknown")}`);
|
|
2983
3204
|
}
|
|
2984
3205
|
async function runDomainRemove(project, opts) {
|
|
2985
3206
|
if (!opts.id) {
|
|
2986
|
-
console.error(
|
|
3207
|
+
console.error(import_chalk25.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
2987
3208
|
process.exit(1);
|
|
2988
3209
|
}
|
|
2989
3210
|
const { teamId } = await resolveTeam(opts.team);
|
|
2990
3211
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
2991
|
-
const spinner = (0,
|
|
3212
|
+
const spinner = (0, import_ora9.default)("Removing domain...").start();
|
|
2992
3213
|
try {
|
|
2993
3214
|
await admin({
|
|
2994
3215
|
method: "DELETE",
|
|
@@ -3005,7 +3226,7 @@ async function runDomainSetBase(project, opts) {
|
|
|
3005
3226
|
const env = opts.env ?? "prod";
|
|
3006
3227
|
const { teamId } = await resolveTeam(opts.team);
|
|
3007
3228
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
3008
|
-
const spinner = (0,
|
|
3229
|
+
const spinner = (0, import_ora9.default)("Setting base domain...").start();
|
|
3009
3230
|
try {
|
|
3010
3231
|
await admin({
|
|
3011
3232
|
method: "PUT",
|
|
@@ -3021,28 +3242,29 @@ async function runDomainSetBase(project, opts) {
|
|
|
3021
3242
|
}
|
|
3022
3243
|
|
|
3023
3244
|
// src/commands/tenant.ts
|
|
3024
|
-
var
|
|
3025
|
-
var
|
|
3245
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
3246
|
+
var import_ora12 = __toESM(require("ora"));
|
|
3026
3247
|
init_admin();
|
|
3248
|
+
init_resolve();
|
|
3027
3249
|
init_auth();
|
|
3028
3250
|
init_tenant_pick();
|
|
3029
3251
|
async function runTenantUse(query, opts) {
|
|
3030
3252
|
const creds = loadCredentials();
|
|
3031
3253
|
if (!creds) {
|
|
3032
|
-
console.error(
|
|
3254
|
+
console.error(import_chalk28.default.red("Not logged in. Run `apiblaze login` first."));
|
|
3033
3255
|
process.exit(1);
|
|
3034
3256
|
}
|
|
3035
3257
|
if (opts.clear) {
|
|
3036
3258
|
delete creds.activeTenant;
|
|
3037
3259
|
saveCredentials(creds);
|
|
3038
|
-
console.log(
|
|
3260
|
+
console.log(import_chalk28.default.green("Tenant scope cleared."));
|
|
3039
3261
|
return;
|
|
3040
3262
|
}
|
|
3041
3263
|
const { teamId } = await resolveTeam(opts.team);
|
|
3042
3264
|
const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
|
|
3043
3265
|
if (!slug) process.exit(1);
|
|
3044
3266
|
saveCredentials({ ...creds, activeTenant: slug });
|
|
3045
|
-
console.log(
|
|
3267
|
+
console.log(import_chalk28.default.green(`Tenant scope set to ${import_chalk28.default.bold(slug)}.`) + import_chalk28.default.dim(" (clear with `apiblaze tenant use --clear`)"));
|
|
3046
3268
|
}
|
|
3047
3269
|
async function runTenantList(opts) {
|
|
3048
3270
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -3059,26 +3281,26 @@ async function runTenantList(opts) {
|
|
|
3059
3281
|
return;
|
|
3060
3282
|
}
|
|
3061
3283
|
if (!tenants.length) {
|
|
3062
|
-
console.log(
|
|
3284
|
+
console.log(import_chalk28.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
|
|
3063
3285
|
return;
|
|
3064
3286
|
}
|
|
3065
3287
|
for (const t of tenants) {
|
|
3066
3288
|
const name = typeof t === "string" ? t : t.tenant_name;
|
|
3067
|
-
const display = typeof t === "string" ? "" :
|
|
3068
|
-
console.log(` ${
|
|
3289
|
+
const display = typeof t === "string" ? "" : import_chalk28.default.dim(` ${t.display_name ?? ""}`);
|
|
3290
|
+
console.log(` ${import_chalk28.default.bold(name)}${display}`);
|
|
3069
3291
|
}
|
|
3070
3292
|
const total = out?.total ?? tenants.length;
|
|
3071
3293
|
if (total > tenants.length) {
|
|
3072
|
-
console.log(
|
|
3294
|
+
console.log(import_chalk28.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
|
|
3073
3295
|
}
|
|
3074
3296
|
}
|
|
3075
3297
|
async function runTenantCreate(opts) {
|
|
3076
3298
|
if (!opts.name) {
|
|
3077
|
-
console.error(
|
|
3299
|
+
console.error(import_chalk28.default.red("--name (display name) is required."));
|
|
3078
3300
|
process.exit(1);
|
|
3079
3301
|
}
|
|
3080
3302
|
const { teamId } = await resolveTeam(opts.team);
|
|
3081
|
-
const spinner = (0,
|
|
3303
|
+
const spinner = (0, import_ora12.default)("Creating tenant...").start();
|
|
3082
3304
|
try {
|
|
3083
3305
|
const out = await admin({
|
|
3084
3306
|
method: "POST",
|
|
@@ -3086,7 +3308,7 @@ async function runTenantCreate(opts) {
|
|
|
3086
3308
|
body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
|
|
3087
3309
|
summary: `Create tenant "${opts.name}"`
|
|
3088
3310
|
});
|
|
3089
|
-
spinner.succeed(`Created tenant ${
|
|
3311
|
+
spinner.succeed(`Created tenant ${import_chalk28.default.bold(out?.tenant_name ?? opts.name)}.`);
|
|
3090
3312
|
if (opts.json) console.log(JSON.stringify(out));
|
|
3091
3313
|
} catch (err) {
|
|
3092
3314
|
spinner.fail("Tenant create failed.");
|
|
@@ -3095,12 +3317,12 @@ async function runTenantCreate(opts) {
|
|
|
3095
3317
|
}
|
|
3096
3318
|
async function runTenantAttach(project, opts) {
|
|
3097
3319
|
if (!opts.tenant) {
|
|
3098
|
-
console.error(
|
|
3320
|
+
console.error(import_chalk28.default.red("--tenant <slug> is required."));
|
|
3099
3321
|
process.exit(1);
|
|
3100
3322
|
}
|
|
3101
3323
|
const { teamId } = await resolveTeam(opts.team);
|
|
3102
3324
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
3103
|
-
const spinner = (0,
|
|
3325
|
+
const spinner = (0, import_ora12.default)("Attaching tenant...").start();
|
|
3104
3326
|
try {
|
|
3105
3327
|
const out = await admin({
|
|
3106
3328
|
method: "POST",
|
|
@@ -3123,11 +3345,11 @@ async function runTenantDelete(slug, opts) {
|
|
|
3123
3345
|
{ type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
|
|
3124
3346
|
]);
|
|
3125
3347
|
if (!confirm) {
|
|
3126
|
-
console.log(
|
|
3348
|
+
console.log(import_chalk28.default.dim("Aborted."));
|
|
3127
3349
|
return;
|
|
3128
3350
|
}
|
|
3129
3351
|
}
|
|
3130
|
-
const spinner = (0,
|
|
3352
|
+
const spinner = (0, import_ora12.default)("Deleting tenant...").start();
|
|
3131
3353
|
try {
|
|
3132
3354
|
await admin({
|
|
3133
3355
|
method: "DELETE",
|
|
@@ -3142,13 +3364,13 @@ async function runTenantDelete(slug, opts) {
|
|
|
3142
3364
|
}
|
|
3143
3365
|
async function runTenantCors(opts) {
|
|
3144
3366
|
if (!opts.tenant) {
|
|
3145
|
-
console.error(
|
|
3367
|
+
console.error(import_chalk28.default.red("--tenant <slug> is required."));
|
|
3146
3368
|
process.exit(1);
|
|
3147
3369
|
}
|
|
3148
3370
|
const { teamId } = await resolveTeam(opts.team);
|
|
3149
3371
|
const origins = (opts.origins ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
3150
3372
|
const cors = origins.length ? { allowed_origins: origins } : null;
|
|
3151
|
-
const spinner = (0,
|
|
3373
|
+
const spinner = (0, import_ora12.default)("Updating CORS...").start();
|
|
3152
3374
|
try {
|
|
3153
3375
|
await admin({
|
|
3154
3376
|
method: "PUT",
|
|
@@ -3164,11 +3386,12 @@ async function runTenantCors(opts) {
|
|
|
3164
3386
|
}
|
|
3165
3387
|
|
|
3166
3388
|
// src/commands/tenant-drill.ts
|
|
3167
|
-
var
|
|
3168
|
-
var
|
|
3389
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
3390
|
+
var import_ora13 = __toESM(require("ora"));
|
|
3169
3391
|
var import_crypto = require("crypto");
|
|
3170
3392
|
init_admin();
|
|
3171
3393
|
init_auth();
|
|
3394
|
+
init_resolve();
|
|
3172
3395
|
init_tenant_pick();
|
|
3173
3396
|
var trailingComma = /\s*,\s*/;
|
|
3174
3397
|
var parseList = (s) => s.split(trailingComma).map((x) => x.trim()).filter(Boolean);
|
|
@@ -3194,17 +3417,17 @@ async function validScopedTenant(teamId, query) {
|
|
|
3194
3417
|
const next = { ...creds };
|
|
3195
3418
|
delete next.activeTenant;
|
|
3196
3419
|
saveCredentials2(next);
|
|
3197
|
-
console.log(
|
|
3420
|
+
console.log(import_chalk29.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
|
|
3198
3421
|
return void 0;
|
|
3199
3422
|
}
|
|
3200
3423
|
async function tenantHome(teamId, tenant2) {
|
|
3201
3424
|
const { default: inquirer3 } = await import("inquirer");
|
|
3202
3425
|
const base = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
3203
|
-
console.log(
|
|
3426
|
+
console.log(import_chalk29.default.bold(`
|
|
3204
3427
|
Tenant ${tenant2}`));
|
|
3205
|
-
console.log(
|
|
3428
|
+
console.log(import_chalk29.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
3206
3429
|
for (; ; ) {
|
|
3207
|
-
const spinner = (0,
|
|
3430
|
+
const spinner = (0, import_ora13.default)("Reading tenant state...").start();
|
|
3208
3431
|
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
3209
3432
|
admin({ method: "GET", path: `${base}/iam`, summary: "Read IAM toggle" }).catch(() => null),
|
|
3210
3433
|
admin({ method: "GET", path: `${base}/cors`, summary: "Read tenant CORS" }).catch(() => null),
|
|
@@ -3218,18 +3441,18 @@ Tenant ${tenant2}`));
|
|
|
3218
3441
|
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
3219
3442
|
const nOpaque = opaque?.opaque?.endpoint ? 1 : 0;
|
|
3220
3443
|
const nLogin = nClients + nIssuers + nOpaque;
|
|
3221
|
-
const onOff = (b) => b ?
|
|
3444
|
+
const onOff = (b) => b ? import_chalk29.default.green("on") : import_chalk29.default.dim("off");
|
|
3222
3445
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
3223
3446
|
type: "list",
|
|
3224
3447
|
name: "pick",
|
|
3225
3448
|
message: `Tenant ${tenant2}:`,
|
|
3226
3449
|
pageSize: 12,
|
|
3227
3450
|
choices: [
|
|
3228
|
-
{ name: `Login methods (${nLogin}) ${
|
|
3229
|
-
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${
|
|
3230
|
-
{ name: `Portal admins (${nEmails}) ${
|
|
3231
|
-
new inquirer3.Separator(
|
|
3232
|
-
{ name: `CORS: ${cors?.cors ?
|
|
3451
|
+
{ name: `Login methods (${nLogin}) ${import_chalk29.default.dim("how your consumers sign in")}`, value: "login" },
|
|
3452
|
+
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk29.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
|
|
3453
|
+
{ name: `Portal admins (${nEmails}) ${import_chalk29.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
|
|
3454
|
+
new inquirer3.Separator(import_chalk29.default.dim(" Settings")),
|
|
3455
|
+
{ name: `CORS: ${cors?.cors ? import_chalk29.default.cyan(JSON.stringify(cors.cors)) : import_chalk29.default.dim("(unset)")} ${import_chalk29.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
|
|
3233
3456
|
{ name: "\u2190 Back", value: "back" }
|
|
3234
3457
|
]
|
|
3235
3458
|
}]);
|
|
@@ -3242,7 +3465,7 @@ Tenant ${tenant2}`));
|
|
|
3242
3465
|
case "iam": {
|
|
3243
3466
|
const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
|
|
3244
3467
|
await admin({ method: "PATCH", path: `${base}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
3245
|
-
console.log(
|
|
3468
|
+
console.log(import_chalk29.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
|
|
3246
3469
|
break;
|
|
3247
3470
|
}
|
|
3248
3471
|
case "cors": {
|
|
@@ -3255,11 +3478,11 @@ Tenant ${tenant2}`));
|
|
|
3255
3478
|
if (v === "") break;
|
|
3256
3479
|
const parsed = v === "null" ? null : safeJson(v);
|
|
3257
3480
|
if (parsed === void 0) {
|
|
3258
|
-
console.log(
|
|
3481
|
+
console.log(import_chalk29.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
3259
3482
|
break;
|
|
3260
3483
|
}
|
|
3261
3484
|
await admin({ method: "PUT", path: `${base}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
3262
|
-
console.log(
|
|
3485
|
+
console.log(import_chalk29.default.green(" CORS updated."));
|
|
3263
3486
|
break;
|
|
3264
3487
|
}
|
|
3265
3488
|
case "emails":
|
|
@@ -3271,7 +3494,7 @@ Tenant ${tenant2}`));
|
|
|
3271
3494
|
async function loginMethodsMenu(teamId, tenant2, base) {
|
|
3272
3495
|
const { default: inquirer3 } = await import("inquirer");
|
|
3273
3496
|
for (; ; ) {
|
|
3274
|
-
const spinner = (0,
|
|
3497
|
+
const spinner = (0, import_ora13.default)("Loading login methods...").start();
|
|
3275
3498
|
const [rawClients, rawIssuers, rawOpaque] = await Promise.all([
|
|
3276
3499
|
admin({ method: "GET", path: `${base}/app-clients`, summary: "List APIblaze-hosted logins" }).catch(() => []),
|
|
3277
3500
|
admin({ method: "GET", path: `${base}/external-issuers`, summary: "List your-own-JWT logins" }).catch(() => null),
|
|
@@ -3283,15 +3506,15 @@ async function loginMethodsMenu(teamId, tenant2, base) {
|
|
|
3283
3506
|
const choices = [];
|
|
3284
3507
|
for (const c of appClients) {
|
|
3285
3508
|
const nP = (c.providers ?? []).length || c.providers_count || 0;
|
|
3286
|
-
choices.push({ name: `${
|
|
3509
|
+
choices.push({ name: `${import_chalk29.default.cyan("APIblaze-hosted")} ${import_chalk29.default.bold(c.name ?? c.clientId)} ${import_chalk29.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk29.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
|
|
3287
3510
|
}
|
|
3288
3511
|
for (const i of issuers) {
|
|
3289
|
-
choices.push({ name: `${
|
|
3512
|
+
choices.push({ name: `${import_chalk29.default.cyan("Your own \u2014 JWT ")} ${import_chalk29.default.bold(i.iss)} ${import_chalk29.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
|
|
3290
3513
|
}
|
|
3291
3514
|
if (opaque?.endpoint) {
|
|
3292
|
-
choices.push({ name: `${
|
|
3515
|
+
choices.push({ name: `${import_chalk29.default.cyan("Your own \u2014 opaque")} ${import_chalk29.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
|
|
3293
3516
|
}
|
|
3294
|
-
if (!choices.length) console.log(
|
|
3517
|
+
if (!choices.length) console.log(import_chalk29.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
|
|
3295
3518
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
3296
3519
|
type: "list",
|
|
3297
3520
|
name: "pick",
|
|
@@ -3331,9 +3554,9 @@ async function addLoginMethod(teamId, tenant2, base) {
|
|
|
3331
3554
|
message: "How do people log in?",
|
|
3332
3555
|
pageSize: 8,
|
|
3333
3556
|
choices: [
|
|
3334
|
-
{ name: `APIblaze hosted login page ${
|
|
3335
|
-
{ name: `Your own hosted login \u2014 JWT ${
|
|
3336
|
-
{ name: `Your own login \u2014 opaque token ${
|
|
3557
|
+
{ name: `APIblaze hosted login page ${import_chalk29.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
|
|
3558
|
+
{ name: `Your own hosted login \u2014 JWT ${import_chalk29.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
|
|
3559
|
+
{ name: `Your own login \u2014 opaque token ${import_chalk29.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
|
|
3337
3560
|
{ name: "\u2190 Back", value: null }
|
|
3338
3561
|
]
|
|
3339
3562
|
}]);
|
|
@@ -3357,8 +3580,8 @@ function safeJson(s) {
|
|
|
3357
3580
|
async function emailsMenu(base, emails) {
|
|
3358
3581
|
const { default: inquirer3 } = await import("inquirer");
|
|
3359
3582
|
console.log();
|
|
3360
|
-
if (!emails.length) console.log(
|
|
3361
|
-
for (const e of emails) console.log(` ${
|
|
3583
|
+
if (!emails.length) console.log(import_chalk29.default.dim(" No consumer-admin emails."));
|
|
3584
|
+
for (const e of emails) console.log(` ${import_chalk29.default.bold(e.email ?? e)} ${import_chalk29.default.dim(e.status ?? "")}`);
|
|
3362
3585
|
const { act } = await inquirer3.prompt([{
|
|
3363
3586
|
type: "list",
|
|
3364
3587
|
name: "act",
|
|
@@ -3373,7 +3596,7 @@ async function emailsMenu(base, emails) {
|
|
|
3373
3596
|
if (act === "add") {
|
|
3374
3597
|
const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
3375
3598
|
await admin({ method: "POST", path: `${base}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
3376
|
-
console.log(
|
|
3599
|
+
console.log(import_chalk29.default.green(` ${email} added.`));
|
|
3377
3600
|
} else {
|
|
3378
3601
|
const { e } = await inquirer3.prompt([{
|
|
3379
3602
|
type: "list",
|
|
@@ -3383,7 +3606,7 @@ async function emailsMenu(base, emails) {
|
|
|
3383
3606
|
}]);
|
|
3384
3607
|
if (!e) return;
|
|
3385
3608
|
await admin({ method: "DELETE", path: `${base}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
3386
|
-
console.log(
|
|
3609
|
+
console.log(import_chalk29.default.green(` ${e} removed.`));
|
|
3387
3610
|
}
|
|
3388
3611
|
}
|
|
3389
3612
|
async function addIssuer(base) {
|
|
@@ -3404,19 +3627,19 @@ async function addIssuer(base) {
|
|
|
3404
3627
|
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3405
3628
|
summary: `Add external issuer ${a.iss.trim()}`
|
|
3406
3629
|
});
|
|
3407
|
-
console.log(
|
|
3630
|
+
console.log(import_chalk29.default.green(" JWT login method saved."));
|
|
3408
3631
|
}
|
|
3409
3632
|
async function issuerHome(base, issuer) {
|
|
3410
3633
|
const { default: inquirer3 } = await import("inquirer");
|
|
3411
3634
|
console.log(`
|
|
3412
|
-
${
|
|
3635
|
+
${import_chalk29.default.bold(issuer.iss)} ${import_chalk29.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
|
|
3413
3636
|
const { act } = await inquirer3.prompt([{
|
|
3414
3637
|
type: "list",
|
|
3415
3638
|
name: "act",
|
|
3416
3639
|
message: "This JWT login method:",
|
|
3417
3640
|
choices: [
|
|
3418
3641
|
{ name: "Replace it (re-enter details)", value: "edit" },
|
|
3419
|
-
{ name:
|
|
3642
|
+
{ name: import_chalk29.default.red("Delete it"), value: "rm" },
|
|
3420
3643
|
{ name: "\u2190 Back", value: "back" }
|
|
3421
3644
|
]
|
|
3422
3645
|
}]);
|
|
@@ -3432,7 +3655,7 @@ async function issuerHome(base, issuer) {
|
|
|
3432
3655
|
path: `${base}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
|
|
3433
3656
|
summary: `Delete issuer ${issuer.iss}`
|
|
3434
3657
|
});
|
|
3435
|
-
console.log(
|
|
3658
|
+
console.log(import_chalk29.default.green(" Deleted."));
|
|
3436
3659
|
}
|
|
3437
3660
|
async function setOpaque(base, cur) {
|
|
3438
3661
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -3441,19 +3664,19 @@ async function setOpaque(base, cur) {
|
|
|
3441
3664
|
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
3442
3665
|
]);
|
|
3443
3666
|
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
3444
|
-
console.log(
|
|
3667
|
+
console.log(import_chalk29.default.green(" Opaque login method set."));
|
|
3445
3668
|
}
|
|
3446
3669
|
async function opaqueHome(base, cur) {
|
|
3447
3670
|
const { default: inquirer3 } = await import("inquirer");
|
|
3448
3671
|
console.log(`
|
|
3449
|
-
${
|
|
3672
|
+
${import_chalk29.default.bold(cur.endpoint)} ${import_chalk29.default.dim(cur.method ?? "GET")}`);
|
|
3450
3673
|
const { act } = await inquirer3.prompt([{
|
|
3451
3674
|
type: "list",
|
|
3452
3675
|
name: "act",
|
|
3453
3676
|
message: "This opaque login method:",
|
|
3454
3677
|
choices: [
|
|
3455
3678
|
{ name: "Replace it", value: "edit" },
|
|
3456
|
-
{ name:
|
|
3679
|
+
{ name: import_chalk29.default.red("Delete it"), value: "rm" },
|
|
3457
3680
|
{ name: "\u2190 Back", value: "back" }
|
|
3458
3681
|
]
|
|
3459
3682
|
}]);
|
|
@@ -3463,14 +3686,14 @@ async function opaqueHome(base, cur) {
|
|
|
3463
3686
|
return;
|
|
3464
3687
|
}
|
|
3465
3688
|
await admin({ method: "PUT", path: `${base}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
3466
|
-
console.log(
|
|
3689
|
+
console.log(import_chalk29.default.green(" Deleted."));
|
|
3467
3690
|
}
|
|
3468
3691
|
async function clientHome(base, summary) {
|
|
3469
3692
|
const { default: inquirer3 } = await import("inquirer");
|
|
3470
3693
|
const id = summary.clientId ?? summary.client_id;
|
|
3471
3694
|
const cBase = `${base}/app-clients/${encodeURIComponent(id)}`;
|
|
3472
3695
|
for (; ; ) {
|
|
3473
|
-
const spinner = (0,
|
|
3696
|
+
const spinner = (0, import_ora13.default)("Reading app client...").start();
|
|
3474
3697
|
const c = await admin({ method: "GET", path: cBase, summary: `Read app client ${id}` }).catch(() => summary);
|
|
3475
3698
|
spinner.stop();
|
|
3476
3699
|
const cb = c.authorizedCallbackUrls ?? c.authorized_callback_urls ?? [];
|
|
@@ -3482,13 +3705,13 @@ async function clientHome(base, summary) {
|
|
|
3482
3705
|
message: `${c.name ?? id}:`,
|
|
3483
3706
|
pageSize: 12,
|
|
3484
3707
|
choices: [
|
|
3485
|
-
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${
|
|
3486
|
-
{ name: `Callback URLs: ${cb.length ?
|
|
3487
|
-
{ name: `Scopes: ${scopes.length ?
|
|
3708
|
+
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk29.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
|
|
3709
|
+
{ name: `Callback URLs: ${cb.length ? import_chalk29.default.cyan(cb.join(", ")) : import_chalk29.default.dim("(none)")}`, value: "callbacks" },
|
|
3710
|
+
{ name: `Scopes: ${scopes.length ? import_chalk29.default.cyan(scopes.join(" ")) : import_chalk29.default.dim("(defaults)")}`, value: "scopes" },
|
|
3488
3711
|
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
3489
3712
|
{ name: "Reveal client secret", value: "secret" },
|
|
3490
3713
|
{ name: "Rotate client secret", value: "rotate" },
|
|
3491
|
-
{ name:
|
|
3714
|
+
{ name: import_chalk29.default.red("Delete this app client"), value: "delete" },
|
|
3492
3715
|
{ name: "\u2190 Back", value: "back" }
|
|
3493
3716
|
]
|
|
3494
3717
|
}]);
|
|
@@ -3501,13 +3724,13 @@ async function clientHome(base, summary) {
|
|
|
3501
3724
|
case "callbacks": {
|
|
3502
3725
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
3503
3726
|
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
3504
|
-
console.log(
|
|
3727
|
+
console.log(import_chalk29.default.green(" Callbacks updated."));
|
|
3505
3728
|
break;
|
|
3506
3729
|
}
|
|
3507
3730
|
case "scopes": {
|
|
3508
3731
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
3509
3732
|
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
3510
|
-
console.log(
|
|
3733
|
+
console.log(import_chalk29.default.green(" Scopes updated."));
|
|
3511
3734
|
break;
|
|
3512
3735
|
}
|
|
3513
3736
|
case "expiries": {
|
|
@@ -3522,14 +3745,14 @@ async function clientHome(base, summary) {
|
|
|
3522
3745
|
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
3523
3746
|
summary: "Update token expiries"
|
|
3524
3747
|
});
|
|
3525
|
-
console.log(
|
|
3748
|
+
console.log(import_chalk29.default.green(" Expiries updated."));
|
|
3526
3749
|
break;
|
|
3527
3750
|
}
|
|
3528
3751
|
case "secret": {
|
|
3529
3752
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
3530
3753
|
if (!sure) break;
|
|
3531
3754
|
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
3532
|
-
console.log(` ${
|
|
3755
|
+
console.log(` ${import_chalk29.default.bold("client_secret")}: ${import_chalk29.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3533
3756
|
break;
|
|
3534
3757
|
}
|
|
3535
3758
|
case "rotate": {
|
|
@@ -3537,14 +3760,14 @@ async function clientHome(base, summary) {
|
|
|
3537
3760
|
if (!sure) break;
|
|
3538
3761
|
const fresh = randomSecret();
|
|
3539
3762
|
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
3540
|
-
console.log(` New ${
|
|
3763
|
+
console.log(` New ${import_chalk29.default.bold("client_secret")}: ${import_chalk29.default.green(fresh)} ${import_chalk29.default.dim("(store it now)")}`);
|
|
3541
3764
|
break;
|
|
3542
3765
|
}
|
|
3543
3766
|
case "delete": {
|
|
3544
3767
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Delete app client "${c.name ?? id}"? Consumers logged in through it will lose access.`, default: false }]);
|
|
3545
3768
|
if (!sure) break;
|
|
3546
3769
|
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
3547
|
-
console.log(
|
|
3770
|
+
console.log(import_chalk29.default.green(" App client deleted."));
|
|
3548
3771
|
return;
|
|
3549
3772
|
}
|
|
3550
3773
|
}
|
|
@@ -3565,15 +3788,15 @@ var DEFAULT_SCOPES2 = {
|
|
|
3565
3788
|
async function providersMenu(cBase, clientLabel) {
|
|
3566
3789
|
const { default: inquirer3 } = await import("inquirer");
|
|
3567
3790
|
for (; ; ) {
|
|
3568
|
-
const spinner = (0,
|
|
3791
|
+
const spinner = (0, import_ora13.default)("Loading providers...").start();
|
|
3569
3792
|
const raw = await admin({ method: "GET", path: `${cBase}/providers`, summary: "List login providers" }).catch(() => []);
|
|
3570
3793
|
spinner.stop();
|
|
3571
3794
|
const providers = Array.isArray(raw) ? raw : [];
|
|
3572
3795
|
console.log();
|
|
3573
3796
|
for (const p of providers) {
|
|
3574
|
-
console.log(` ${
|
|
3797
|
+
console.log(` ${import_chalk29.default.bold(p.type)} ${import_chalk29.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
|
|
3575
3798
|
}
|
|
3576
|
-
if (!providers.length) console.log(
|
|
3799
|
+
if (!providers.length) console.log(import_chalk29.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
|
|
3577
3800
|
const { act } = await inquirer3.prompt([{
|
|
3578
3801
|
type: "list",
|
|
3579
3802
|
name: "act",
|
|
@@ -3631,23 +3854,23 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3631
3854
|
body.targetServerToken = routing;
|
|
3632
3855
|
}
|
|
3633
3856
|
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
3634
|
-
console.log(
|
|
3857
|
+
console.log(import_chalk29.default.green(` ${type} provider added.`));
|
|
3635
3858
|
} else {
|
|
3636
3859
|
const { p } = await inquirer3.prompt([{
|
|
3637
3860
|
type: "list",
|
|
3638
3861
|
name: "p",
|
|
3639
3862
|
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
3640
|
-
choices: [...providers.map((x) => ({ name: `${x.type} ${
|
|
3863
|
+
choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk29.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
|
|
3641
3864
|
}]);
|
|
3642
3865
|
if (!p) continue;
|
|
3643
3866
|
if (act === "rm") {
|
|
3644
3867
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: `Remove the ${p.type} provider? Consumers using it can no longer sign in.`, default: false }]);
|
|
3645
3868
|
if (!sure) continue;
|
|
3646
3869
|
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
3647
|
-
console.log(
|
|
3870
|
+
console.log(import_chalk29.default.green(` ${p.type} removed.`));
|
|
3648
3871
|
} else {
|
|
3649
3872
|
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
3650
|
-
console.log(` ${
|
|
3873
|
+
console.log(` ${import_chalk29.default.bold("client_secret")}: ${import_chalk29.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3651
3874
|
}
|
|
3652
3875
|
}
|
|
3653
3876
|
}
|
|
@@ -3655,9 +3878,11 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3655
3878
|
|
|
3656
3879
|
// src/commands/spec.ts
|
|
3657
3880
|
var fs7 = __toESM(require("fs"));
|
|
3658
|
-
var
|
|
3659
|
-
var
|
|
3881
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
3882
|
+
var import_ora14 = __toESM(require("ora"));
|
|
3660
3883
|
init_admin();
|
|
3884
|
+
init_api();
|
|
3885
|
+
init_resolve();
|
|
3661
3886
|
async function runSpecGet(project, opts) {
|
|
3662
3887
|
const { teamId } = await resolveTeam(opts.team);
|
|
3663
3888
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
@@ -3670,19 +3895,19 @@ async function runSpecGet(project, opts) {
|
|
|
3670
3895
|
}
|
|
3671
3896
|
async function runSpecSet(project, opts) {
|
|
3672
3897
|
if (!opts.file) {
|
|
3673
|
-
console.error(
|
|
3898
|
+
console.error(import_chalk30.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
3674
3899
|
process.exit(1);
|
|
3675
3900
|
}
|
|
3676
3901
|
let specContent;
|
|
3677
3902
|
try {
|
|
3678
3903
|
specContent = fs7.readFileSync(opts.file, "utf-8");
|
|
3679
3904
|
} catch {
|
|
3680
|
-
console.error(
|
|
3905
|
+
console.error(import_chalk30.default.red(`Cannot read file: ${opts.file}`));
|
|
3681
3906
|
process.exit(1);
|
|
3682
3907
|
}
|
|
3683
3908
|
const { teamId } = await resolveTeam(opts.team);
|
|
3684
3909
|
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
3685
|
-
const spinner = (0,
|
|
3910
|
+
const spinner = (0, import_ora14.default)("Uploading spec...").start();
|
|
3686
3911
|
try {
|
|
3687
3912
|
const out = await admin({
|
|
3688
3913
|
method: "POST",
|
|
@@ -3690,24 +3915,91 @@ async function runSpecSet(project, opts) {
|
|
|
3690
3915
|
body: { specContent },
|
|
3691
3916
|
summary: `Set OpenAPI spec for ${proj2.projectName} v${proj2.apiVersion} from ${opts.file}`
|
|
3692
3917
|
});
|
|
3693
|
-
|
|
3918
|
+
const report = out?.report;
|
|
3919
|
+
if (report?.reused || report && !report.changed) {
|
|
3920
|
+
spinner.succeed(`Spec unchanged for ${proj2.projectName} v${proj2.apiVersion} (identical content).`);
|
|
3921
|
+
} else {
|
|
3922
|
+
spinner.succeed(`Spec updated for ${proj2.projectName} v${proj2.apiVersion}${report?.summary ? `: ${report.summary}` : "."}`);
|
|
3923
|
+
}
|
|
3924
|
+
if (report) printSpecChangeReport(report, proj2.projectName);
|
|
3694
3925
|
if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
|
|
3695
3926
|
} catch (err) {
|
|
3696
3927
|
spinner.fail("Spec update failed.");
|
|
3697
3928
|
throw err;
|
|
3698
3929
|
}
|
|
3699
3930
|
}
|
|
3931
|
+
function printSpecChangeReport(report, projectName) {
|
|
3932
|
+
const r = report.routes;
|
|
3933
|
+
if (r) {
|
|
3934
|
+
for (const a of r.added) console.log(import_chalk30.default.green(` + ${a}`));
|
|
3935
|
+
for (const d of r.removed) console.log(import_chalk30.default.red(` \u2212 ${d}`));
|
|
3936
|
+
for (const rn of r.renamed ?? []) console.log(import_chalk30.default.yellow(` ~ ${rn.from} \u2192 ${rn.to}`));
|
|
3937
|
+
for (const m of r.modified ?? []) console.log(import_chalk30.default.dim(` \xB1 ${m} (params/schema changed)`));
|
|
3938
|
+
}
|
|
3939
|
+
if (report.applied?.mcp_regenerated) console.log(import_chalk30.default.dim(" MCP catalogue regenerated."));
|
|
3940
|
+
if (report.applied?.candidates_cleared) console.log(import_chalk30.default.dim(` ${report.applied.candidates_cleared} route candidate(s) now documented \u2014 cleared.`));
|
|
3941
|
+
for (const item of report.attention ?? []) {
|
|
3942
|
+
console.log(import_chalk30.default.yellow(` \u26A0 ${item.detail}`));
|
|
3943
|
+
if (item.suggestion?.carry_from && item.suggestion.carry_to) {
|
|
3944
|
+
console.log(import_chalk30.default.dim(` Fix: apiblaze spec carry ${projectName} --method ${item.suggestion.method} --from "${item.suggestion.carry_from}" --to "${item.suggestion.carry_to}"`));
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
async function runSpecCarry(project, opts) {
|
|
3949
|
+
if (!opts.method || !opts.from || !opts.to) {
|
|
3950
|
+
console.error(import_chalk30.default.red("--method, --from and --to are required."));
|
|
3951
|
+
process.exit(1);
|
|
3952
|
+
}
|
|
3953
|
+
const { teamId } = await resolveTeam(opts.team);
|
|
3954
|
+
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
3955
|
+
const spinner = (0, import_ora14.default)(`Carrying rules ${opts.from} \u2192 ${opts.to}...`).start();
|
|
3956
|
+
const res = await agentCall(`/projects/${proj2.projectId}/${proj2.apiVersion}/policies/route/carry`, "POST", {
|
|
3957
|
+
method: opts.method.toUpperCase(),
|
|
3958
|
+
from_resource: opts.from,
|
|
3959
|
+
to_resource: opts.to,
|
|
3960
|
+
...opts.deleteSource ? { delete_source: true } : {}
|
|
3961
|
+
});
|
|
3962
|
+
if (res.status >= 200 && res.status < 300) {
|
|
3963
|
+
spinner.succeed(`Rules carried to ${opts.method.toUpperCase()} ${opts.to}${opts.deleteSource ? " (old rules deleted)" : " \u2014 old rules kept until you delete them"}.`);
|
|
3964
|
+
if (opts.json) console.log(JSON.stringify(res.data ?? { ok: true }));
|
|
3965
|
+
} else {
|
|
3966
|
+
spinner.fail(`Carry failed (${res.status}): ${res.data?.error ?? "unknown error"}`);
|
|
3967
|
+
process.exit(1);
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
async function runSpecDeleteRule(project, opts) {
|
|
3971
|
+
if (!opts.method || !opts.route) {
|
|
3972
|
+
console.error(import_chalk30.default.red("--method and --route are required."));
|
|
3973
|
+
process.exit(1);
|
|
3974
|
+
}
|
|
3975
|
+
const { teamId } = await resolveTeam(opts.team);
|
|
3976
|
+
const proj2 = await resolveProject(teamId, project, opts.apiversion);
|
|
3977
|
+
const encoded = opts.route.replace(/\{/g, "%7B").replace(/\}/g, "%7D");
|
|
3978
|
+
const spinner = (0, import_ora14.default)(`Deleting rules for ${opts.method.toUpperCase()} ${opts.route}...`).start();
|
|
3979
|
+
const res = await agentCall(
|
|
3980
|
+
`/projects/${proj2.projectId}/${proj2.apiVersion}/policies/route/${opts.method.toUpperCase()}${encoded.startsWith("/") ? encoded : `/${encoded}`}`,
|
|
3981
|
+
"DELETE"
|
|
3982
|
+
);
|
|
3983
|
+
if (res.status >= 200 && res.status < 300 || res.status === 404) {
|
|
3984
|
+
spinner.succeed(`Rules deleted for ${opts.method.toUpperCase()} ${opts.route}.`);
|
|
3985
|
+
} else {
|
|
3986
|
+
spinner.fail(`Delete failed (${res.status}): ${res.data?.error ?? "unknown error"}`);
|
|
3987
|
+
process.exit(1);
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3700
3990
|
|
|
3701
3991
|
// src/commands/agent.ts
|
|
3702
|
-
var
|
|
3703
|
-
var
|
|
3992
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
3993
|
+
var import_ora15 = __toESM(require("ora"));
|
|
3704
3994
|
init_auth();
|
|
3705
3995
|
init_admin();
|
|
3996
|
+
init_resolve();
|
|
3706
3997
|
|
|
3707
3998
|
// src/lib/tools.ts
|
|
3708
|
-
var
|
|
3999
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
3709
4000
|
init_admin();
|
|
3710
4001
|
init_api();
|
|
4002
|
+
init_resolve();
|
|
3711
4003
|
async function proj(teamId, name, version2) {
|
|
3712
4004
|
return resolveProject(teamId, name, version2);
|
|
3713
4005
|
}
|
|
@@ -3724,15 +4016,15 @@ var TOOLS = [
|
|
|
3724
4016
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
3725
4017
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
3726
4018
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
3727
|
-
const lines = [` ${
|
|
3728
|
-
if (res.devPortal) lines.push(` ${
|
|
4019
|
+
const lines = [` ${import_chalk31.default.dim("Proxy URL:")} ${import_chalk31.default.bold(url)}`];
|
|
4020
|
+
if (res.devPortal) lines.push(` ${import_chalk31.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
3729
4021
|
const envs = Object.keys(keys);
|
|
3730
4022
|
if (envs.length) {
|
|
3731
|
-
lines.push("", ` ${
|
|
4023
|
+
lines.push("", ` ${import_chalk31.default.bold("API keys")} ${import_chalk31.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
3732
4024
|
const w = Math.max(...envs.map((e) => e.length));
|
|
3733
|
-
for (const env of envs) lines.push(` ${
|
|
4025
|
+
for (const env of envs) lines.push(` ${import_chalk31.default.cyan(env.padEnd(w))} ${import_chalk31.default.green(keys[env])}`);
|
|
3734
4026
|
}
|
|
3735
|
-
if (tryIt) lines.push("", ` ${
|
|
4027
|
+
if (tryIt) lines.push("", ` ${import_chalk31.default.dim("Try it:")}`, ` ${import_chalk31.default.cyan(tryIt)}`);
|
|
3736
4028
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
3737
4029
|
}
|
|
3738
4030
|
},
|
|
@@ -3888,23 +4180,23 @@ function truncate(value, max = 1500) {
|
|
|
3888
4180
|
}
|
|
3889
4181
|
function printCost(resp) {
|
|
3890
4182
|
const line = formatBilling(resp.billing);
|
|
3891
|
-
if (line) console.log(
|
|
4183
|
+
if (line) console.log(import_chalk32.default.magenta(` ${line}`) + import_chalk32.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
|
|
3892
4184
|
}
|
|
3893
4185
|
async function runAgent(opts) {
|
|
3894
4186
|
requireAuth();
|
|
3895
4187
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
3896
4188
|
const { default: inquirer3 } = await import("inquirer");
|
|
3897
|
-
console.log(
|
|
3898
|
-
console.log(
|
|
4189
|
+
console.log(import_chalk32.default.bold("APIblaze agent") + import_chalk32.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
4190
|
+
console.log(import_chalk32.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
3899
4191
|
const history = [];
|
|
3900
4192
|
while (true) {
|
|
3901
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
4193
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk32.default.cyan("you") + " \u203A" }]);
|
|
3902
4194
|
const text = (input ?? "").trim();
|
|
3903
4195
|
if (!text) continue;
|
|
3904
4196
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
3905
4197
|
history.push({ role: "user", content: text });
|
|
3906
4198
|
for (let step = 0; step < MAX_TOOL_STEPS; step++) {
|
|
3907
|
-
const spinner = (0,
|
|
4199
|
+
const spinner = (0, import_ora15.default)({ text: "thinking...", color: "magenta" }).start();
|
|
3908
4200
|
let resp;
|
|
3909
4201
|
try {
|
|
3910
4202
|
resp = await callAgent(history, teamId);
|
|
@@ -3912,21 +4204,21 @@ async function runAgent(opts) {
|
|
|
3912
4204
|
} catch (err) {
|
|
3913
4205
|
spinner.stop();
|
|
3914
4206
|
if (err instanceof ApiError && err.status === 402) {
|
|
3915
|
-
console.log(
|
|
4207
|
+
console.log(import_chalk32.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
3916
4208
|
break;
|
|
3917
4209
|
}
|
|
3918
4210
|
throw err;
|
|
3919
4211
|
}
|
|
3920
4212
|
history.push({ role: "assistant", content: resp.raw });
|
|
3921
4213
|
printCost(resp);
|
|
3922
|
-
if (resp.reply) console.log(
|
|
4214
|
+
if (resp.reply) console.log(import_chalk32.default.green("agent") + " \u203A " + resp.reply);
|
|
3923
4215
|
if (!resp.action) break;
|
|
3924
4216
|
const tool = findTool(resp.action.tool);
|
|
3925
4217
|
if (!tool) {
|
|
3926
4218
|
history.push({ role: "user", content: `TOOL_RESULT ${resp.action.tool}: error \u2014 unknown tool` });
|
|
3927
4219
|
continue;
|
|
3928
4220
|
}
|
|
3929
|
-
const runSpinner = (0,
|
|
4221
|
+
const runSpinner = (0, import_ora15.default)({ text: `running ${tool.name}...`, color: "cyan" }).start();
|
|
3930
4222
|
try {
|
|
3931
4223
|
const result = await tool.run(resp.action.args, { teamId });
|
|
3932
4224
|
runSpinner.succeed(`${tool.name} \u2713`);
|
|
@@ -3944,11 +4236,11 @@ async function runAgent(opts) {
|
|
|
3944
4236
|
}
|
|
3945
4237
|
renderTrace();
|
|
3946
4238
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
3947
|
-
console.log(
|
|
4239
|
+
console.log(import_chalk32.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
3948
4240
|
}
|
|
3949
4241
|
}
|
|
3950
4242
|
}
|
|
3951
|
-
console.log(
|
|
4243
|
+
console.log(import_chalk32.default.dim("\nBye."));
|
|
3952
4244
|
}
|
|
3953
4245
|
|
|
3954
4246
|
// src/commands/config-browse.ts
|
|
@@ -4187,11 +4479,11 @@ function dig(blob, dotted) {
|
|
|
4187
4479
|
}
|
|
4188
4480
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
4189
4481
|
function show(v) {
|
|
4190
|
-
if (v === void 0) return
|
|
4191
|
-
if (v === null) return
|
|
4192
|
-
if (typeof v === "object") return
|
|
4193
|
-
if (typeof v === "boolean") return v ?
|
|
4194
|
-
return
|
|
4482
|
+
if (v === void 0) return import_chalk33.default.dim("(unset)");
|
|
4483
|
+
if (v === null) return import_chalk33.default.dim("null");
|
|
4484
|
+
if (typeof v === "object") return import_chalk33.default.cyan(JSON.stringify(v));
|
|
4485
|
+
if (typeof v === "boolean") return v ? import_chalk33.default.green("on") : import_chalk33.default.red("off");
|
|
4486
|
+
return import_chalk33.default.cyan(String(v));
|
|
4195
4487
|
}
|
|
4196
4488
|
function parseValue(raw) {
|
|
4197
4489
|
if (raw === "true") return true;
|
|
@@ -4218,7 +4510,7 @@ async function fetchConfigBlob(proj2) {
|
|
|
4218
4510
|
}
|
|
4219
4511
|
async function patchSetting(proj2, s, value, cfg) {
|
|
4220
4512
|
const body = s.toPatch(value, cfg);
|
|
4221
|
-
const spinner = (0,
|
|
4513
|
+
const spinner = (0, import_ora16.default)(`Set ${s.key}...`).start();
|
|
4222
4514
|
try {
|
|
4223
4515
|
await admin({
|
|
4224
4516
|
method: "PATCH",
|
|
@@ -4233,10 +4525,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
4233
4525
|
}
|
|
4234
4526
|
}
|
|
4235
4527
|
var loginFirst = (what) => {
|
|
4236
|
-
console.log(
|
|
4528
|
+
console.log(import_chalk33.default.yellow(`
|
|
4237
4529
|
Log in first to ${what}.`));
|
|
4238
|
-
console.log(
|
|
4239
|
-
console.log(
|
|
4530
|
+
console.log(import_chalk33.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
|
|
4531
|
+
console.log(import_chalk33.default.dim(" anonymously and want to bring it into your account.\n"));
|
|
4240
4532
|
};
|
|
4241
4533
|
async function runConfig(project, key, value, opts) {
|
|
4242
4534
|
const creds = loadCredentials();
|
|
@@ -4253,9 +4545,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
4253
4545
|
}
|
|
4254
4546
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
4255
4547
|
if (!setting) {
|
|
4256
|
-
console.error(
|
|
4257
|
-
console.error(
|
|
4258
|
-
console.error(
|
|
4548
|
+
console.error(import_chalk33.default.red(`Unknown setting "${key}".`));
|
|
4549
|
+
console.error(import_chalk33.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
|
|
4550
|
+
console.error(import_chalk33.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
|
|
4259
4551
|
process.exit(1);
|
|
4260
4552
|
}
|
|
4261
4553
|
if (value === void 0) {
|
|
@@ -4270,7 +4562,7 @@ async function pickProject(teamId) {
|
|
|
4270
4562
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
4271
4563
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
4272
4564
|
if (!projects.length) {
|
|
4273
|
-
console.error(
|
|
4565
|
+
console.error(import_chalk33.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
4274
4566
|
process.exit(1);
|
|
4275
4567
|
}
|
|
4276
4568
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -4278,7 +4570,7 @@ async function pickProject(teamId) {
|
|
|
4278
4570
|
type: "list",
|
|
4279
4571
|
name: "picked",
|
|
4280
4572
|
message: "Which project?",
|
|
4281
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
4573
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk33.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
4282
4574
|
}]);
|
|
4283
4575
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
4284
4576
|
}
|
|
@@ -4289,25 +4581,25 @@ function printAll(proj2, cfg, json) {
|
|
|
4289
4581
|
console.log(JSON.stringify(out, null, 2));
|
|
4290
4582
|
return;
|
|
4291
4583
|
}
|
|
4292
|
-
console.log(
|
|
4584
|
+
console.log(import_chalk33.default.bold(`
|
|
4293
4585
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
4294
4586
|
`));
|
|
4295
4587
|
for (const group2 of SETTING_GROUPS) {
|
|
4296
|
-
console.log(
|
|
4588
|
+
console.log(import_chalk33.default.bold(group2));
|
|
4297
4589
|
for (const s of SETTINGS.filter((x) => x.group === group2)) {
|
|
4298
|
-
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${
|
|
4590
|
+
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk33.default.dim(s.desc)}`);
|
|
4299
4591
|
}
|
|
4300
4592
|
console.log();
|
|
4301
4593
|
}
|
|
4302
|
-
console.log(
|
|
4594
|
+
console.log(import_chalk33.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
4303
4595
|
}
|
|
4304
4596
|
async function discoveryMenu(project) {
|
|
4305
4597
|
const { default: inquirer3 } = await import("inquirer");
|
|
4306
|
-
console.log(
|
|
4598
|
+
console.log(import_chalk33.default.bold(`
|
|
4307
4599
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
4308
4600
|
`));
|
|
4309
|
-
console.log(
|
|
4310
|
-
console.log(
|
|
4601
|
+
console.log(import_chalk33.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
|
|
4602
|
+
console.log(import_chalk33.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
|
|
4311
4603
|
for (; ; ) {
|
|
4312
4604
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
4313
4605
|
type: "list",
|
|
@@ -4315,13 +4607,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
4315
4607
|
message: "Explore:",
|
|
4316
4608
|
pageSize: 20,
|
|
4317
4609
|
choices: [
|
|
4318
|
-
new inquirer3.Separator(
|
|
4610
|
+
new inquirer3.Separator(import_chalk33.default.bold("\u2014 Settings \u2014")),
|
|
4319
4611
|
...SETTING_GROUPS.map((g) => ({
|
|
4320
|
-
name: `${g} ${
|
|
4612
|
+
name: `${g} ${import_chalk33.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
4321
4613
|
value: { kind: "settings", g }
|
|
4322
4614
|
})),
|
|
4323
|
-
new inquirer3.Separator(
|
|
4324
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
4615
|
+
new inquirer3.Separator(import_chalk33.default.bold("\u2014 Features \u2014")),
|
|
4616
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk33.default.dim(f.desc)}`, value: { kind: "feature", f } })),
|
|
4325
4617
|
new inquirer3.Separator(),
|
|
4326
4618
|
{ name: "Exit", value: { kind: "exit" } }
|
|
4327
4619
|
]
|
|
@@ -4330,24 +4622,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
4330
4622
|
if (pick2.kind === "settings") {
|
|
4331
4623
|
console.log();
|
|
4332
4624
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
4333
|
-
console.log(` ${
|
|
4334
|
-
console.log(` ${
|
|
4625
|
+
console.log(` ${import_chalk33.default.bold(s.label.padEnd(28))} ${import_chalk33.default.dim(s.desc)}`);
|
|
4626
|
+
console.log(` ${import_chalk33.default.dim(" key: " + s.key)}`);
|
|
4335
4627
|
}
|
|
4336
4628
|
loginFirst("view or change these settings");
|
|
4337
4629
|
} else {
|
|
4338
4630
|
const f = pick2.f;
|
|
4339
4631
|
console.log(`
|
|
4340
|
-
${
|
|
4632
|
+
${import_chalk33.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
4341
4633
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
4342
4634
|
}
|
|
4343
4635
|
}
|
|
4344
4636
|
}
|
|
4345
4637
|
async function navigator(proj2, cfg, opts) {
|
|
4346
4638
|
const { default: inquirer3 } = await import("inquirer");
|
|
4347
|
-
console.log(
|
|
4639
|
+
console.log(import_chalk33.default.bold(`
|
|
4348
4640
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
4349
4641
|
`));
|
|
4350
|
-
console.log(
|
|
4642
|
+
console.log(import_chalk33.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
|
|
4351
4643
|
let blob = cfg;
|
|
4352
4644
|
for (; ; ) {
|
|
4353
4645
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
@@ -4356,10 +4648,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4356
4648
|
message: "Where to?",
|
|
4357
4649
|
pageSize: 20,
|
|
4358
4650
|
choices: [
|
|
4359
|
-
new inquirer3.Separator(
|
|
4651
|
+
new inquirer3.Separator(import_chalk33.default.bold("\u2014 Settings \u2014")),
|
|
4360
4652
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
4361
|
-
new inquirer3.Separator(
|
|
4362
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
4653
|
+
new inquirer3.Separator(import_chalk33.default.bold("\u2014 Features \u2014")),
|
|
4654
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk33.default.dim(f.desc)}`, value: { kind: f.go } })),
|
|
4363
4655
|
new inquirer3.Separator(),
|
|
4364
4656
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
4365
4657
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -4399,7 +4691,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4399
4691
|
}
|
|
4400
4692
|
}
|
|
4401
4693
|
} catch (err) {
|
|
4402
|
-
console.error(
|
|
4694
|
+
console.error(import_chalk33.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
4403
4695
|
}
|
|
4404
4696
|
}
|
|
4405
4697
|
}
|
|
@@ -4413,7 +4705,7 @@ async function settingsGroup(proj2, cfg, group2) {
|
|
|
4413
4705
|
message: group2 + ":",
|
|
4414
4706
|
pageSize: 16,
|
|
4415
4707
|
choices: [
|
|
4416
|
-
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${
|
|
4708
|
+
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk33.default.dim(s2.desc)}`, value: s2 })),
|
|
4417
4709
|
new inquirer3.Separator(),
|
|
4418
4710
|
{ name: "\u2190 Back", value: null }
|
|
4419
4711
|
]
|
|
@@ -4431,7 +4723,7 @@ async function settingsGroup(proj2, cfg, group2) {
|
|
|
4431
4723
|
} else if (s.type === "number") {
|
|
4432
4724
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
4433
4725
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
4434
|
-
console.log(
|
|
4726
|
+
console.log(import_chalk33.default.yellow(" Not a number \u2014 unchanged."));
|
|
4435
4727
|
continue;
|
|
4436
4728
|
}
|
|
4437
4729
|
value = Number(v);
|
|
@@ -4512,7 +4804,7 @@ async function buildCondition(phase) {
|
|
|
4512
4804
|
const items = [];
|
|
4513
4805
|
for (; ; ) {
|
|
4514
4806
|
const a = await inquirer3.prompt([
|
|
4515
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
4807
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk33.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
4516
4808
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
4517
4809
|
"eq",
|
|
4518
4810
|
"neq",
|
|
@@ -4543,7 +4835,7 @@ async function buildCondition(phase) {
|
|
|
4543
4835
|
function showCondition(cond) {
|
|
4544
4836
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
4545
4837
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
4546
|
-
return
|
|
4838
|
+
return import_chalk33.default.dim(` when ${s}`);
|
|
4547
4839
|
}
|
|
4548
4840
|
async function transformsMenu(proj2) {
|
|
4549
4841
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -4552,12 +4844,12 @@ async function transformsMenu(proj2) {
|
|
|
4552
4844
|
const out = await admin({ method: "GET", path: base, summary: "List transform rules" });
|
|
4553
4845
|
const rules = out?.rules ?? [];
|
|
4554
4846
|
console.log();
|
|
4555
|
-
if (!rules.length) console.log(
|
|
4847
|
+
if (!rules.length) console.log(import_chalk33.default.dim(" No transform rules yet."));
|
|
4556
4848
|
for (const r of rules) {
|
|
4557
4849
|
const a = r.action ?? {};
|
|
4558
4850
|
const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
|
|
4559
|
-
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 ?
|
|
4560
|
-
console.log(` ${r.enabled ?
|
|
4851
|
+
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_chalk33.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
|
|
4852
|
+
console.log(` ${r.enabled ? import_chalk33.default.green("\u25CF") : import_chalk33.default.dim("\u25CB")} ${import_chalk33.default.bold(r.name)} ${import_chalk33.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
|
|
4561
4853
|
}
|
|
4562
4854
|
const { act } = await inquirer3.prompt([{
|
|
4563
4855
|
type: "list",
|
|
@@ -4569,7 +4861,7 @@ async function transformsMenu(proj2) {
|
|
|
4569
4861
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
4570
4862
|
{ name: "Delete a rule", value: "delete" }
|
|
4571
4863
|
] : [],
|
|
4572
|
-
{ name:
|
|
4864
|
+
{ name: import_chalk33.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
4573
4865
|
{ name: "\u2190 Back", value: "back" }
|
|
4574
4866
|
]
|
|
4575
4867
|
}]);
|
|
@@ -4582,11 +4874,11 @@ async function transformsMenu(proj2) {
|
|
|
4582
4874
|
}]);
|
|
4583
4875
|
const body = parseValue(raw);
|
|
4584
4876
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
4585
|
-
console.log(
|
|
4877
|
+
console.log(import_chalk33.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
4586
4878
|
continue;
|
|
4587
4879
|
}
|
|
4588
4880
|
await admin({ method: "POST", path: base, body, summary: "Create transform rule (raw JSON)" });
|
|
4589
|
-
console.log(
|
|
4881
|
+
console.log(import_chalk33.default.green(" Rule created."));
|
|
4590
4882
|
continue;
|
|
4591
4883
|
}
|
|
4592
4884
|
if (act === "add") {
|
|
@@ -4602,7 +4894,7 @@ async function transformsMenu(proj2) {
|
|
|
4602
4894
|
{ name: "Remove a field", value: "remove" }
|
|
4603
4895
|
] }
|
|
4604
4896
|
]);
|
|
4605
|
-
const fieldHint =
|
|
4897
|
+
const fieldHint = import_chalk33.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
4606
4898
|
let action2;
|
|
4607
4899
|
if (ans.kind === "hardcode") {
|
|
4608
4900
|
const a = await inquirer3.prompt([
|
|
@@ -4631,7 +4923,7 @@ async function transformsMenu(proj2) {
|
|
|
4631
4923
|
};
|
|
4632
4924
|
}
|
|
4633
4925
|
const condition = await buildCondition(ans.phase);
|
|
4634
|
-
const spinner = (0,
|
|
4926
|
+
const spinner = (0, import_ora16.default)("Creating rule...").start();
|
|
4635
4927
|
try {
|
|
4636
4928
|
await admin({
|
|
4637
4929
|
method: "POST",
|
|
@@ -4649,16 +4941,16 @@ async function transformsMenu(proj2) {
|
|
|
4649
4941
|
type: "list",
|
|
4650
4942
|
name: "rule",
|
|
4651
4943
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
4652
|
-
choices: [...rules.map((r) => ({ name: `${r.name} ${
|
|
4944
|
+
choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk33.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
|
|
4653
4945
|
}]);
|
|
4654
4946
|
if (!rule) continue;
|
|
4655
4947
|
if (act === "toggle") {
|
|
4656
4948
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
4657
4949
|
await admin({ method: "PUT", path: `${base}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
4658
|
-
console.log(
|
|
4950
|
+
console.log(import_chalk33.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
4659
4951
|
} else {
|
|
4660
4952
|
await admin({ method: "DELETE", path: `${base}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
4661
|
-
console.log(
|
|
4953
|
+
console.log(import_chalk33.default.green(` ${rule.name} deleted.`));
|
|
4662
4954
|
}
|
|
4663
4955
|
}
|
|
4664
4956
|
}
|
|
@@ -4670,9 +4962,9 @@ async function mappingsMenu(proj2) {
|
|
|
4670
4962
|
const out = await admin({ method: "GET", path: base, summary: "List mapping tables" });
|
|
4671
4963
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
4672
4964
|
console.log();
|
|
4673
|
-
if (!tables.length) console.log(
|
|
4965
|
+
if (!tables.length) console.log(import_chalk33.default.dim(" No mapping tables yet."));
|
|
4674
4966
|
for (const t of tables) {
|
|
4675
|
-
console.log(` ${
|
|
4967
|
+
console.log(` ${import_chalk33.default.bold(t.name)} ${import_chalk33.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
|
|
4676
4968
|
}
|
|
4677
4969
|
const { act } = await inquirer3.prompt([{
|
|
4678
4970
|
type: "list",
|
|
@@ -4692,11 +4984,11 @@ async function mappingsMenu(proj2) {
|
|
|
4692
4984
|
]);
|
|
4693
4985
|
const entries2 = parseValue(a.entries);
|
|
4694
4986
|
if (!Array.isArray(entries2)) {
|
|
4695
|
-
console.log(
|
|
4987
|
+
console.log(import_chalk33.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
4696
4988
|
continue;
|
|
4697
4989
|
}
|
|
4698
4990
|
await admin({ method: "POST", path: base, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
4699
|
-
console.log(
|
|
4991
|
+
console.log(import_chalk33.default.green(` Table "${a.name}" created.`));
|
|
4700
4992
|
} else {
|
|
4701
4993
|
const { table } = await inquirer3.prompt([{
|
|
4702
4994
|
type: "list",
|
|
@@ -4706,7 +4998,7 @@ async function mappingsMenu(proj2) {
|
|
|
4706
4998
|
}]);
|
|
4707
4999
|
if (!table) continue;
|
|
4708
5000
|
await admin({ method: "DELETE", path: `${base}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
4709
|
-
console.log(
|
|
5001
|
+
console.log(import_chalk33.default.green(` ${table.name} deleted.`));
|
|
4710
5002
|
}
|
|
4711
5003
|
}
|
|
4712
5004
|
}
|
|
@@ -4717,14 +5009,14 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4717
5009
|
const out = await admin({ method: "GET", path: base, summary: "List attached tenants" });
|
|
4718
5010
|
const tenants = out?.tenants ?? [];
|
|
4719
5011
|
console.log();
|
|
4720
|
-
if (!tenants.length) console.log(
|
|
4721
|
-
for (const t of tenants) console.log(` ${
|
|
5012
|
+
if (!tenants.length) console.log(import_chalk33.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
5013
|
+
for (const t of tenants) console.log(` ${import_chalk33.default.bold(t.tenant_name ?? t.name)} ${import_chalk33.default.dim(t.display_name ?? "")}`);
|
|
4722
5014
|
const { act } = await inquirer3.prompt([{
|
|
4723
5015
|
type: "list",
|
|
4724
5016
|
name: "act",
|
|
4725
5017
|
message: "Tenants:",
|
|
4726
5018
|
choices: [
|
|
4727
|
-
{ name: `Manage a tenant\u2026 ${
|
|
5019
|
+
{ name: `Manage a tenant\u2026 ${import_chalk33.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
|
|
4728
5020
|
{ name: "Attach a tenant to this project", value: "attach" },
|
|
4729
5021
|
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
4730
5022
|
{ name: "\u2190 Back", value: "back" }
|
|
@@ -4747,7 +5039,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
4747
5039
|
}]);
|
|
4748
5040
|
if (!t) continue;
|
|
4749
5041
|
await admin({ method: "DELETE", path: `${base}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
4750
|
-
console.log(
|
|
5042
|
+
console.log(import_chalk33.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
4751
5043
|
}
|
|
4752
5044
|
}
|
|
4753
5045
|
}
|
|
@@ -4790,7 +5082,7 @@ async function specMenu(proj2, opts) {
|
|
|
4790
5082
|
choices: [
|
|
4791
5083
|
{ name: "Print the stored spec", value: "get" },
|
|
4792
5084
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
4793
|
-
{ name:
|
|
5085
|
+
{ name: import_chalk33.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
4794
5086
|
{ name: "\u2190 Back", value: "back" }
|
|
4795
5087
|
]
|
|
4796
5088
|
}]);
|
|
@@ -4798,7 +5090,7 @@ async function specMenu(proj2, opts) {
|
|
|
4798
5090
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
4799
5091
|
else if (act === "refresh") {
|
|
4800
5092
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
4801
|
-
console.log(
|
|
5093
|
+
console.log(import_chalk33.default.green(" Spec refresh triggered."));
|
|
4802
5094
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
4803
5095
|
}
|
|
4804
5096
|
async function agentsMenu(proj2, opts) {
|
|
@@ -4823,16 +5115,17 @@ async function agentsMenu(proj2, opts) {
|
|
|
4823
5115
|
}
|
|
4824
5116
|
|
|
4825
5117
|
// src/commands/group.ts
|
|
4826
|
-
var
|
|
4827
|
-
var
|
|
5118
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
5119
|
+
var import_ora17 = __toESM(require("ora"));
|
|
4828
5120
|
init_auth();
|
|
4829
5121
|
init_admin();
|
|
5122
|
+
init_resolve();
|
|
4830
5123
|
init_types();
|
|
4831
5124
|
var DASHBOARD_BASE6 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
4832
5125
|
var IAM_BASE = process.env.APIBLAZE_IAM_BASE || "https://iam.apiblaze.com";
|
|
4833
5126
|
async function resolveTransport(opts) {
|
|
4834
|
-
const
|
|
4835
|
-
if (
|
|
5127
|
+
const producer2 = loadCredentials();
|
|
5128
|
+
if (producer2) {
|
|
4836
5129
|
const { teamId } = await resolveTeam(void 0);
|
|
4837
5130
|
let tenant2 = (opts.tenant ?? "").trim().toLowerCase();
|
|
4838
5131
|
if (!tenant2) {
|
|
@@ -4913,11 +5206,11 @@ async function runGroupList(opts) {
|
|
|
4913
5206
|
const t = await resolveTransport(opts);
|
|
4914
5207
|
const groups = await listGroups(t);
|
|
4915
5208
|
if (opts.json) return void console.log(JSON.stringify(groups));
|
|
4916
|
-
if (!groups.length) return void console.log(
|
|
4917
|
-
console.log(
|
|
5209
|
+
if (!groups.length) return void console.log(import_chalk34.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
|
|
5210
|
+
console.log(import_chalk34.default.dim(`Tenant ${t.tenant}:`));
|
|
4918
5211
|
for (const g of groups) {
|
|
4919
|
-
const sealed = g.name === "apiblaze_admins" ?
|
|
4920
|
-
console.log(` ${
|
|
5212
|
+
const sealed = g.name === "apiblaze_admins" ? import_chalk34.default.dim(" (admins \u2014 managed by allowlist)") : "";
|
|
5213
|
+
console.log(` ${import_chalk34.default.bold(g.name)} ${import_chalk34.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
|
|
4921
5214
|
}
|
|
4922
5215
|
}
|
|
4923
5216
|
async function runGroupCreate(name, opts) {
|
|
@@ -4942,10 +5235,10 @@ async function runGroupCreate(name, opts) {
|
|
|
4942
5235
|
}
|
|
4943
5236
|
}
|
|
4944
5237
|
}
|
|
4945
|
-
const spinner = (0,
|
|
5238
|
+
const spinner = (0, import_ora17.default)(`Creating group ${name}...`).start();
|
|
4946
5239
|
try {
|
|
4947
5240
|
await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
|
|
4948
|
-
spinner.succeed(`Group ${
|
|
5241
|
+
spinner.succeed(`Group ${import_chalk34.default.bold(name)} created in ${t.tenant}.`);
|
|
4949
5242
|
} catch (e) {
|
|
4950
5243
|
spinner.fail("Create failed.");
|
|
4951
5244
|
throw e;
|
|
@@ -4954,10 +5247,10 @@ async function runGroupCreate(name, opts) {
|
|
|
4954
5247
|
async function runGroupDelete(name, opts) {
|
|
4955
5248
|
const t = await resolveTransport(opts);
|
|
4956
5249
|
const g = await groupByName(t, name);
|
|
4957
|
-
const spinner = (0,
|
|
5250
|
+
const spinner = (0, import_ora17.default)(`Deleting group ${name}...`).start();
|
|
4958
5251
|
try {
|
|
4959
5252
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
|
|
4960
|
-
spinner.succeed(`Group ${
|
|
5253
|
+
spinner.succeed(`Group ${import_chalk34.default.bold(name)} deleted.`);
|
|
4961
5254
|
} catch (e) {
|
|
4962
5255
|
spinner.fail("Delete failed.");
|
|
4963
5256
|
throw e;
|
|
@@ -4970,20 +5263,20 @@ async function runGroupMembers(name, opts) {
|
|
|
4970
5263
|
const subs = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/subgroups`);
|
|
4971
5264
|
if (opts.json) return void console.log(JSON.stringify({ ...detail, subgroups: subs?.subgroups ?? [] }));
|
|
4972
5265
|
const members = detail?.members ?? [];
|
|
4973
|
-
console.log(
|
|
4974
|
-
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ?
|
|
4975
|
-
if (!members.length) console.log(
|
|
5266
|
+
console.log(import_chalk34.default.bold(`${g.name}`) + import_chalk34.default.dim(` (${t.tenant})`));
|
|
5267
|
+
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk34.default.cyan("group-admin") : import_chalk34.default.dim("member")}`);
|
|
5268
|
+
if (!members.length) console.log(import_chalk34.default.dim(" no members"));
|
|
4976
5269
|
const sg = subs?.subgroups ?? [];
|
|
4977
|
-
if (sg.length) console.log(
|
|
5270
|
+
if (sg.length) console.log(import_chalk34.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
|
|
4978
5271
|
}
|
|
4979
5272
|
async function runGroupAddUser(userRef, groupName, opts) {
|
|
4980
5273
|
const t = await resolveTransport(opts);
|
|
4981
5274
|
const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
|
|
4982
5275
|
const role = opts.role === "admin" ? "admin" : "member";
|
|
4983
|
-
const spinner = (0,
|
|
5276
|
+
const spinner = (0, import_ora17.default)(`Adding ${label2(u)} to ${g.name}...`).start();
|
|
4984
5277
|
try {
|
|
4985
5278
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
|
|
4986
|
-
spinner.succeed(`${
|
|
5279
|
+
spinner.succeed(`${import_chalk34.default.bold(label2(u))} added to ${import_chalk34.default.bold(g.name)} as ${role}.`);
|
|
4987
5280
|
} catch (e) {
|
|
4988
5281
|
spinner.fail("Add failed.");
|
|
4989
5282
|
throw e;
|
|
@@ -4992,10 +5285,10 @@ async function runGroupAddUser(userRef, groupName, opts) {
|
|
|
4992
5285
|
async function runGroupRemoveUser(userRef, groupName, opts) {
|
|
4993
5286
|
const t = await resolveTransport(opts);
|
|
4994
5287
|
const [g, u] = [await groupByName(t, groupName), await userByRef(t, userRef)];
|
|
4995
|
-
const spinner = (0,
|
|
5288
|
+
const spinner = (0, import_ora17.default)(`Removing ${label2(u)} from ${g.name}...`).start();
|
|
4996
5289
|
try {
|
|
4997
5290
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
|
|
4998
|
-
spinner.succeed(`${
|
|
5291
|
+
spinner.succeed(`${import_chalk34.default.bold(label2(u))} removed from ${import_chalk34.default.bold(g.name)}.`);
|
|
4999
5292
|
} catch (e) {
|
|
5000
5293
|
spinner.fail("Remove failed.");
|
|
5001
5294
|
throw e;
|
|
@@ -5004,10 +5297,10 @@ async function runGroupRemoveUser(userRef, groupName, opts) {
|
|
|
5004
5297
|
async function runGroupAddGroup(childName, parentName, opts) {
|
|
5005
5298
|
const t = await resolveTransport(opts);
|
|
5006
5299
|
const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
|
|
5007
|
-
const spinner = (0,
|
|
5300
|
+
const spinner = (0, import_ora17.default)(`Nesting ${child.name} under ${parent.name}...`).start();
|
|
5008
5301
|
try {
|
|
5009
5302
|
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
|
|
5010
|
-
spinner.succeed(`${
|
|
5303
|
+
spinner.succeed(`${import_chalk34.default.bold(child.name)} is now a subgroup of ${import_chalk34.default.bold(parent.name)}.`);
|
|
5011
5304
|
} catch (e) {
|
|
5012
5305
|
spinner.fail("Nest failed.");
|
|
5013
5306
|
throw e;
|
|
@@ -5016,10 +5309,10 @@ async function runGroupAddGroup(childName, parentName, opts) {
|
|
|
5016
5309
|
async function runGroupRemoveGroup(childName, parentName, opts) {
|
|
5017
5310
|
const t = await resolveTransport(opts);
|
|
5018
5311
|
const [child, parent] = [await groupByName(t, childName), await groupByName(t, parentName)];
|
|
5019
|
-
const spinner = (0,
|
|
5312
|
+
const spinner = (0, import_ora17.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
|
|
5020
5313
|
try {
|
|
5021
5314
|
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
|
|
5022
|
-
spinner.succeed(`${
|
|
5315
|
+
spinner.succeed(`${import_chalk34.default.bold(child.name)} un-nested from ${import_chalk34.default.bold(parent.name)}.`);
|
|
5023
5316
|
} catch (e) {
|
|
5024
5317
|
spinner.fail("Un-nest failed.");
|
|
5025
5318
|
throw e;
|
|
@@ -5030,20 +5323,20 @@ async function runGroupUsers(opts) {
|
|
|
5030
5323
|
const users = await listUsers(t);
|
|
5031
5324
|
const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
|
|
5032
5325
|
if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
|
|
5033
|
-
console.log(
|
|
5034
|
-
for (const u of users) console.log(` ${label2(u)} ${
|
|
5035
|
-
if (!users.length) console.log(
|
|
5326
|
+
console.log(import_chalk34.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
|
|
5327
|
+
for (const u of users) console.log(` ${label2(u)} ${import_chalk34.default.dim(u.abz_sub)}`);
|
|
5328
|
+
if (!users.length) console.log(import_chalk34.default.dim(" no users yet"));
|
|
5036
5329
|
const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
|
|
5037
5330
|
if (fresh.length) {
|
|
5038
|
-
console.log(
|
|
5039
|
-
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${
|
|
5331
|
+
console.log(import_chalk34.default.dim("Seen in traffic (not yet users):"));
|
|
5332
|
+
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk34.default.dim(o.source_label ?? "")}`);
|
|
5040
5333
|
}
|
|
5041
5334
|
}
|
|
5042
5335
|
|
|
5043
5336
|
// src/commands/key.ts
|
|
5044
|
-
var
|
|
5045
|
-
var
|
|
5046
|
-
|
|
5337
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
5338
|
+
var import_ora18 = __toESM(require("ora"));
|
|
5339
|
+
init_caller();
|
|
5047
5340
|
async function runApikeysMenu(opts) {
|
|
5048
5341
|
await runKeyList(opts);
|
|
5049
5342
|
if (opts.json) return;
|
|
@@ -5058,8 +5351,9 @@ async function runApikeysMenu(opts) {
|
|
|
5058
5351
|
await runKeyMint({ ...opts, desc: desc || void 0 });
|
|
5059
5352
|
}
|
|
5060
5353
|
async function runKeyList(opts) {
|
|
5061
|
-
const
|
|
5062
|
-
const
|
|
5354
|
+
const caller = requireCaller();
|
|
5355
|
+
const { teamId, teamName } = await resolveActingTeam(caller, opts.team);
|
|
5356
|
+
const out = await producer(caller, {
|
|
5063
5357
|
method: "GET",
|
|
5064
5358
|
path: `/teams/${encodeURIComponent(teamId)}/developer-keys`,
|
|
5065
5359
|
summary: `List developer keys for team ${teamName ?? teamId}`
|
|
@@ -5070,21 +5364,23 @@ async function runKeyList(opts) {
|
|
|
5070
5364
|
return;
|
|
5071
5365
|
}
|
|
5072
5366
|
if (!keys.length) {
|
|
5073
|
-
console.log(
|
|
5367
|
+
console.log(import_chalk35.default.yellow("No developer keys."));
|
|
5074
5368
|
return;
|
|
5075
5369
|
}
|
|
5076
5370
|
for (const k of keys) {
|
|
5077
|
-
console.log(` ${
|
|
5371
|
+
console.log(` ${import_chalk35.default.bold(k.key_id ?? k.id)} ${import_chalk35.default.dim(k.description ?? "")} ${import_chalk35.default.dim(k.expires_at ?? "no expiry")}`);
|
|
5078
5372
|
}
|
|
5079
5373
|
}
|
|
5080
5374
|
async function runKeyMint(opts) {
|
|
5081
|
-
|
|
5375
|
+
if (process.env.ABZ_DEBUG_OPTS) console.error("DEBUG opts:", JSON.stringify(opts));
|
|
5376
|
+
const caller = requireCaller();
|
|
5377
|
+
const { teamId } = await resolveActingTeam(caller, opts.team);
|
|
5082
5378
|
const body = { role: "consumer-admin" };
|
|
5083
5379
|
if (opts.desc) body.description = opts.desc;
|
|
5084
5380
|
if (opts.expiresDays) body.expires_in_seconds = Number(opts.expiresDays) * 24 * 60 * 60;
|
|
5085
|
-
const spinner = (0,
|
|
5381
|
+
const spinner = (0, import_ora18.default)("Minting key...").start();
|
|
5086
5382
|
try {
|
|
5087
|
-
const out = await
|
|
5383
|
+
const out = await producer(caller, {
|
|
5088
5384
|
method: "POST",
|
|
5089
5385
|
path: `/teams/${encodeURIComponent(teamId)}/developer-keys`,
|
|
5090
5386
|
body,
|
|
@@ -5095,19 +5391,20 @@ async function runKeyMint(opts) {
|
|
|
5095
5391
|
console.log(JSON.stringify(out));
|
|
5096
5392
|
return;
|
|
5097
5393
|
}
|
|
5098
|
-
console.log(` ${
|
|
5099
|
-
console.log(` ${
|
|
5100
|
-
if (out?.expires_at) console.log(` ${
|
|
5394
|
+
console.log(` ${import_chalk35.default.bold("key_id")}: ${out?.key_id}`);
|
|
5395
|
+
console.log(` ${import_chalk35.default.bold("key")}: ${import_chalk35.default.green(out?.key)} ${import_chalk35.default.dim("(shown once \u2014 store it now)")}`);
|
|
5396
|
+
if (out?.expires_at) console.log(` ${import_chalk35.default.dim("expires:")} ${out.expires_at}`);
|
|
5101
5397
|
} catch (err) {
|
|
5102
5398
|
spinner.fail("Mint failed.");
|
|
5103
5399
|
throw err;
|
|
5104
5400
|
}
|
|
5105
5401
|
}
|
|
5106
5402
|
async function runKeyRevoke(keyId, opts) {
|
|
5107
|
-
const
|
|
5108
|
-
const
|
|
5403
|
+
const caller = requireCaller();
|
|
5404
|
+
const { teamId } = await resolveActingTeam(caller, opts.team);
|
|
5405
|
+
const spinner = (0, import_ora18.default)("Revoking key...").start();
|
|
5109
5406
|
try {
|
|
5110
|
-
await
|
|
5407
|
+
await producer(caller, {
|
|
5111
5408
|
method: "DELETE",
|
|
5112
5409
|
path: `/teams/${encodeURIComponent(teamId)}/developer-keys/${encodeURIComponent(keyId)}`,
|
|
5113
5410
|
summary: `Revoke developer key ${keyId}`
|
|
@@ -5119,12 +5416,116 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
5119
5416
|
}
|
|
5120
5417
|
}
|
|
5121
5418
|
|
|
5419
|
+
// src/index.ts
|
|
5420
|
+
init_iam();
|
|
5421
|
+
|
|
5422
|
+
// src/commands/preapprove.ts
|
|
5423
|
+
var import_chalk36 = __toESM(require("chalk"));
|
|
5424
|
+
var import_ora19 = __toESM(require("ora"));
|
|
5425
|
+
|
|
5426
|
+
// src/lib/preapproval.ts
|
|
5427
|
+
init_auth();
|
|
5428
|
+
init_admin();
|
|
5429
|
+
init_resolve();
|
|
5430
|
+
init_types();
|
|
5431
|
+
var DASHBOARD_BASE7 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
5432
|
+
async function iamCall(tenant2, path8, method = "GET", body) {
|
|
5433
|
+
const token = getAccessToken();
|
|
5434
|
+
const res = await fetch(`${DASHBOARD_BASE7}/api/cli/iam`, {
|
|
5435
|
+
method: "POST",
|
|
5436
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
5437
|
+
body: JSON.stringify({ tenant: tenant2, path: path8, method, body })
|
|
5438
|
+
});
|
|
5439
|
+
const data = await res.json().catch(() => ({}));
|
|
5440
|
+
if (!res.ok) throw new ApiError(res.status, data?.details ?? data?.error ?? res.statusText, data);
|
|
5441
|
+
return data;
|
|
5442
|
+
}
|
|
5443
|
+
async function resolveProducerTenant(tenantOpt) {
|
|
5444
|
+
if (!loadCredentials()) throw new Error("Not authenticated. Run `apiblaze login` first.");
|
|
5445
|
+
const tenant2 = (tenantOpt ?? "").trim().toLowerCase();
|
|
5446
|
+
if (tenant2) return tenant2;
|
|
5447
|
+
const { teamId } = await resolveTeam(void 0);
|
|
5448
|
+
const out = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants`, summary: "List tenants" });
|
|
5449
|
+
const tenants = (out?.tenants ?? []).map((t) => String(t.tenant_name ?? t.name ?? t)).filter((t) => !t.startsWith("op"));
|
|
5450
|
+
if (tenants.length === 0) throw new Error("No tenants in your team yet. Create a proxy first, or pass --tenant.");
|
|
5451
|
+
if (tenants.length === 1) return tenants[0];
|
|
5452
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
5453
|
+
const ans = await inquirer3.prompt([{ type: "list", name: "tenant", message: "Which tenant?", choices: tenants }]);
|
|
5454
|
+
return String(ans.tenant);
|
|
5455
|
+
}
|
|
5456
|
+
function classify(input) {
|
|
5457
|
+
const value = input.trim().replace(/^@/, "").toLowerCase();
|
|
5458
|
+
return { kind: value.includes("@") ? "email" : "domain", value };
|
|
5459
|
+
}
|
|
5460
|
+
async function addPreapprovalRule(tenant2, input, groups) {
|
|
5461
|
+
const { kind, value } = classify(input);
|
|
5462
|
+
return iamCall(tenant2, "/v1/admin/preapproval/rules", "POST", {
|
|
5463
|
+
kind,
|
|
5464
|
+
value,
|
|
5465
|
+
...groups && groups.length ? { groups } : {}
|
|
5466
|
+
});
|
|
5467
|
+
}
|
|
5468
|
+
async function listPreapprovalRules(tenant2) {
|
|
5469
|
+
const out = await iamCall(tenant2, "/v1/admin/preapproval/rules", "GET");
|
|
5470
|
+
return out?.rules ?? [];
|
|
5471
|
+
}
|
|
5472
|
+
async function removePreapprovalRule(tenant2, input) {
|
|
5473
|
+
const { value } = classify(input);
|
|
5474
|
+
const rules = await listPreapprovalRules(tenant2);
|
|
5475
|
+
const hit = rules.find((r) => r.value.toLowerCase() === value);
|
|
5476
|
+
if (!hit) return { removed: false, value };
|
|
5477
|
+
await iamCall(tenant2, `/v1/admin/preapproval/rules/${encodeURIComponent(hit.id)}`, "DELETE");
|
|
5478
|
+
return { removed: true, value };
|
|
5479
|
+
}
|
|
5480
|
+
|
|
5481
|
+
// src/commands/preapprove.ts
|
|
5482
|
+
async function runPreapprove(who, opts) {
|
|
5483
|
+
const tenant2 = await resolveProducerTenant(opts.tenant);
|
|
5484
|
+
if (opts.list) {
|
|
5485
|
+
const rules = await listPreapprovalRules(tenant2);
|
|
5486
|
+
if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
|
|
5487
|
+
if (!rules.length) {
|
|
5488
|
+
console.log(import_chalk36.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
|
|
5489
|
+
console.log(import_chalk36.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
|
|
5490
|
+
return;
|
|
5491
|
+
}
|
|
5492
|
+
console.log(import_chalk36.default.dim(`Pre-approved for ${import_chalk36.default.bold(tenant2)}:`));
|
|
5493
|
+
for (const r of rules) {
|
|
5494
|
+
const tag = r.kind === "domain" ? import_chalk36.default.cyan("@" + r.value + " (whole domain)") : r.value;
|
|
5495
|
+
const grp = r.groups?.length ? import_chalk36.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
|
|
5496
|
+
console.log(` ${tag}${grp}`);
|
|
5497
|
+
}
|
|
5498
|
+
return;
|
|
5499
|
+
}
|
|
5500
|
+
if (!who) {
|
|
5501
|
+
throw new Error("Who? Pass an email or a domain: `apiblaze preapprove someone@acme.com` (or `apiblaze preapprove --list`).");
|
|
5502
|
+
}
|
|
5503
|
+
if (opts.remove) {
|
|
5504
|
+
const spinner2 = (0, import_ora19.default)(`Removing ${who} from ${tenant2}\u2026`).start();
|
|
5505
|
+
const { removed, value } = await removePreapprovalRule(tenant2, who);
|
|
5506
|
+
if (removed) spinner2.succeed(`${import_chalk36.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
|
|
5507
|
+
else spinner2.warn(`No pre-approval rule for ${import_chalk36.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
|
|
5508
|
+
return;
|
|
5509
|
+
}
|
|
5510
|
+
const spinner = (0, import_ora19.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
|
|
5511
|
+
try {
|
|
5512
|
+
const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
|
|
5513
|
+
const what = kind === "domain" ? `Anyone @${value}` : value;
|
|
5514
|
+
spinner.succeed(`${import_chalk36.default.bold(what)} can now sign in to ${import_chalk36.default.bold(tenant2)}.`);
|
|
5515
|
+
if (opts.group?.length) console.log(import_chalk36.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
|
|
5516
|
+
console.log(import_chalk36.default.dim(` See the full list: apiblaze preapprove --list`));
|
|
5517
|
+
} catch (err) {
|
|
5518
|
+
spinner.fail("Could not add the rule.");
|
|
5519
|
+
throw err;
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
|
|
5122
5523
|
// src/commands/apichat.ts
|
|
5123
5524
|
var fs9 = __toESM(require("fs"));
|
|
5124
5525
|
var path6 = __toESM(require("path"));
|
|
5125
5526
|
var crypto2 = __toESM(require("crypto"));
|
|
5126
|
-
var
|
|
5127
|
-
var
|
|
5527
|
+
var import_chalk38 = __toESM(require("chalk"));
|
|
5528
|
+
var import_ora20 = __toESM(require("ora"));
|
|
5128
5529
|
var import_yaml = require("yaml");
|
|
5129
5530
|
init_auth();
|
|
5130
5531
|
init_anon_cred();
|
|
@@ -5134,7 +5535,7 @@ init_admin();
|
|
|
5134
5535
|
// src/commands/llm.ts
|
|
5135
5536
|
var fs8 = __toESM(require("fs"));
|
|
5136
5537
|
var path5 = __toESM(require("path"));
|
|
5137
|
-
var
|
|
5538
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
5138
5539
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
5139
5540
|
init_auth();
|
|
5140
5541
|
var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
|
|
@@ -5178,27 +5579,27 @@ async function runLlmSetKey(keyArg, opts) {
|
|
|
5178
5579
|
}
|
|
5179
5580
|
const existing = loadLlmConfig();
|
|
5180
5581
|
saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
|
|
5181
|
-
console.log(`${
|
|
5182
|
-
console.log(
|
|
5183
|
-
if (opts.model) console.log(
|
|
5582
|
+
console.log(`${import_chalk37.default.green("\u2713")} Saved ${import_chalk37.default.bold(provider)} key ${maskSecret(key)}`);
|
|
5583
|
+
console.log(import_chalk37.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
|
|
5584
|
+
if (opts.model) console.log(import_chalk37.default.gray(` Model: ${opts.model}`));
|
|
5184
5585
|
}
|
|
5185
5586
|
async function runLlmShow() {
|
|
5186
5587
|
const cfg = loadLlmConfig();
|
|
5187
5588
|
if (!cfg) {
|
|
5188
|
-
console.log(
|
|
5589
|
+
console.log(import_chalk37.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
|
|
5189
5590
|
return;
|
|
5190
5591
|
}
|
|
5191
|
-
console.log(`Provider: ${
|
|
5592
|
+
console.log(`Provider: ${import_chalk37.default.bold(cfg.provider)}`);
|
|
5192
5593
|
console.log(`Key: ${maskSecret(cfg.key)}`);
|
|
5193
5594
|
if (cfg.model) console.log(`Model: ${cfg.model}`);
|
|
5194
|
-
console.log(
|
|
5595
|
+
console.log(import_chalk37.default.gray(`Stored at ${LLM_PATH}`));
|
|
5195
5596
|
}
|
|
5196
5597
|
async function runLlmClearKey() {
|
|
5197
5598
|
try {
|
|
5198
5599
|
fs8.unlinkSync(LLM_PATH);
|
|
5199
|
-
console.log(`${
|
|
5600
|
+
console.log(`${import_chalk37.default.green("\u2713")} Removed local LLM key.`);
|
|
5200
5601
|
} catch {
|
|
5201
|
-
console.log(
|
|
5602
|
+
console.log(import_chalk37.default.gray("No LLM key was set."));
|
|
5202
5603
|
}
|
|
5203
5604
|
}
|
|
5204
5605
|
|
|
@@ -5206,9 +5607,9 @@ async function runLlmClearKey() {
|
|
|
5206
5607
|
init_trace();
|
|
5207
5608
|
init_types();
|
|
5208
5609
|
function fail4(message, hint) {
|
|
5209
|
-
console.error(
|
|
5610
|
+
console.error(import_chalk38.default.red(`
|
|
5210
5611
|
Error: ${message}`));
|
|
5211
|
-
if (hint) console.error(
|
|
5612
|
+
if (hint) console.error(import_chalk38.default.dim(hint));
|
|
5212
5613
|
process.exit(1);
|
|
5213
5614
|
}
|
|
5214
5615
|
function normalizeName2(raw) {
|
|
@@ -5356,7 +5757,7 @@ async function resolveTargetAuth(spec2, opts) {
|
|
|
5356
5757
|
"Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
|
|
5357
5758
|
);
|
|
5358
5759
|
}
|
|
5359
|
-
if (sawOAuth) console.log(
|
|
5760
|
+
if (sawOAuth) console.log(import_chalk38.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
|
|
5360
5761
|
return null;
|
|
5361
5762
|
}
|
|
5362
5763
|
if (candidates.length === 1 && !noneAllowed) return candidates[0];
|
|
@@ -5433,7 +5834,16 @@ async function provision(spec2, target, opts) {
|
|
|
5433
5834
|
if (!base && spec2.info && typeof spec2.info.title === "string") base = normalizeName2(spec2.info.title);
|
|
5434
5835
|
if (!base || base.length < 3) base = "apichat";
|
|
5435
5836
|
let name = opts.name ? base : `${base}${salt()}`;
|
|
5436
|
-
const
|
|
5837
|
+
const access = anon ? "open" : opts.access === "open" ? "open" : "invite";
|
|
5838
|
+
if (anon && opts.access === "invite") {
|
|
5839
|
+
console.log(import_chalk38.default.dim(" Note: --access invite needs an account to pre-approve people. Staying open for this anonymous proxy \u2014 run `apiblaze login`, then `apiblaze apichat --access invite`."));
|
|
5840
|
+
}
|
|
5841
|
+
const DUAL_AUTH = {
|
|
5842
|
+
mode: "authenticate",
|
|
5843
|
+
methods: ["api_key", "jwt"],
|
|
5844
|
+
...access === "invite" ? { preapproved_users_only: true } : {}
|
|
5845
|
+
};
|
|
5846
|
+
const spinner = (0, import_ora20.default)("Provisioning an api_key proxy...").start();
|
|
5437
5847
|
let result;
|
|
5438
5848
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
5439
5849
|
try {
|
|
@@ -5443,6 +5853,7 @@ async function provision(spec2, target, opts) {
|
|
|
5443
5853
|
name,
|
|
5444
5854
|
target_url: target,
|
|
5445
5855
|
auth_type: "api_key",
|
|
5856
|
+
requests_auth: DUAL_AUTH,
|
|
5446
5857
|
team_id: creds.teamId,
|
|
5447
5858
|
...opts.apiversion ? { api_version: opts.apiversion } : {}
|
|
5448
5859
|
});
|
|
@@ -5454,6 +5865,7 @@ async function provision(spec2, target, opts) {
|
|
|
5454
5865
|
target,
|
|
5455
5866
|
target_url: target,
|
|
5456
5867
|
auth_type: "api_key",
|
|
5868
|
+
requests_auth: DUAL_AUTH,
|
|
5457
5869
|
...opts.apiversion ? { api_version: opts.apiversion } : {}
|
|
5458
5870
|
};
|
|
5459
5871
|
if (cred) {
|
|
@@ -5510,10 +5922,21 @@ async function provision(spec2, target, opts) {
|
|
|
5510
5922
|
if (!dpKey) fail4("The proxy was created but no api_key was returned \u2014 cannot authenticate the chat.");
|
|
5511
5923
|
const mcpHost = result.mcpHost || `${projectId}.mcp.${anon ? "tryabz.run" : "abz.run"}`;
|
|
5512
5924
|
const proxyUrl = Array.isArray(result.endpoints) && (result.endpoints.find((e) => e.includes(`/${environment}`)) || result.endpoints[0]) || (anon ? `https://${projectId}.tryabz.run/${version2}/${environment}` : `https://${projectId}.abz.run/${version2}/${environment}`);
|
|
5513
|
-
|
|
5925
|
+
const tenant2 = typeof result.tenant === "string" ? result.tenant : void 0;
|
|
5926
|
+
if (access === "invite" && tenant2) {
|
|
5927
|
+
const email = loadCredentials()?.email;
|
|
5928
|
+
if (email) {
|
|
5929
|
+
try {
|
|
5930
|
+
await addPreapprovalRule(tenant2, email);
|
|
5931
|
+
} catch {
|
|
5932
|
+
console.log(import_chalk38.default.dim(` (Could not auto-approve your email for sign-in \u2014 add it later: apiblaze preapprove ${email})`));
|
|
5933
|
+
}
|
|
5934
|
+
}
|
|
5935
|
+
}
|
|
5936
|
+
return { projectId, version: version2, environment, dpKey, mcpHost, proxyUrl, anon, access, tenant: tenant2 };
|
|
5514
5937
|
}
|
|
5515
5938
|
async function writeTargetAuth(p, auth, secret) {
|
|
5516
|
-
const spinner = (0,
|
|
5939
|
+
const spinner = (0, import_ora20.default)("Storing target credentials (encrypted)...").start();
|
|
5517
5940
|
try {
|
|
5518
5941
|
await cpPost(
|
|
5519
5942
|
p.anon,
|
|
@@ -5535,7 +5958,7 @@ async function writeTargetAuth(p, auth, secret) {
|
|
|
5535
5958
|
}
|
|
5536
5959
|
}
|
|
5537
5960
|
async function uploadSpec(p, specText, opts) {
|
|
5538
|
-
const spinner = (0,
|
|
5961
|
+
const spinner = (0, import_ora20.default)("Uploading the spec...").start();
|
|
5539
5962
|
let out;
|
|
5540
5963
|
try {
|
|
5541
5964
|
out = await cpPost(
|
|
@@ -5550,7 +5973,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5550
5973
|
throw err;
|
|
5551
5974
|
}
|
|
5552
5975
|
if (out && out.reused === true) {
|
|
5553
|
-
console.log(
|
|
5976
|
+
console.log(import_chalk38.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
|
|
5554
5977
|
} else if (out && out.changed === true && out.previous_spec_hash) {
|
|
5555
5978
|
const interactive = !!process.stdin.isTTY && !opts.yes;
|
|
5556
5979
|
if (interactive) {
|
|
@@ -5558,12 +5981,12 @@ async function uploadSpec(p, specText, opts) {
|
|
|
5558
5981
|
const { go } = await inquirer3.prompt([
|
|
5559
5982
|
{ type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
|
|
5560
5983
|
]);
|
|
5561
|
-
if (!go) console.log(
|
|
5984
|
+
if (!go) console.log(import_chalk38.default.dim(" Keeping the existing MCP catalogue."));
|
|
5562
5985
|
}
|
|
5563
5986
|
}
|
|
5564
5987
|
}
|
|
5565
5988
|
async function publishMcp(p, spec2) {
|
|
5566
|
-
const spinner = (0,
|
|
5989
|
+
const spinner = (0, import_ora20.default)("Publishing the MCP catalogue...").start();
|
|
5567
5990
|
try {
|
|
5568
5991
|
const url = `https://${p.mcpHost}/${p.version}/${p.environment}/mcp/generate`;
|
|
5569
5992
|
const res = await fetch(url, {
|
|
@@ -5599,14 +6022,14 @@ var revealAuth = false;
|
|
|
5599
6022
|
function renderToolEvents(events, dpKey) {
|
|
5600
6023
|
for (const e of events ?? []) {
|
|
5601
6024
|
const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
|
|
5602
|
-
const mark = ok ?
|
|
5603
|
-
console.log(` ${mark} ${
|
|
6025
|
+
const mark = ok ? import_chalk38.default.green("\u2713") : import_chalk38.default.red("\u2717");
|
|
6026
|
+
console.log(` ${mark} ${import_chalk38.default.cyan(e.name)} ${import_chalk38.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
|
|
5604
6027
|
if (isVerbose() && e.method && e.url) {
|
|
5605
|
-
console.log(
|
|
6028
|
+
console.log(import_chalk38.default.dim(` curl -sS -X ${e.method} '${e.url}'${dpKey ? " \\" : ""}`));
|
|
5606
6029
|
if (dpKey) {
|
|
5607
6030
|
const keyLine = ` -H 'X-API-Key: ${revealAuth ? dpKey : maskKey(dpKey)}'`;
|
|
5608
|
-
const hint = revealAuth ? "" :
|
|
5609
|
-
console.log(
|
|
6031
|
+
const hint = revealAuth ? "" : import_chalk38.default.yellow(" \u2190 /showauth will reveal this");
|
|
6032
|
+
console.log(import_chalk38.default.dim(keyLine) + hint);
|
|
5610
6033
|
}
|
|
5611
6034
|
}
|
|
5612
6035
|
}
|
|
@@ -5615,9 +6038,9 @@ function billingLine(billing) {
|
|
|
5615
6038
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
5616
6039
|
if (typeof billing.free_turns_remaining === "number") return null;
|
|
5617
6040
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
5618
|
-
let line =
|
|
6041
|
+
let line = import_chalk38.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk38.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
5619
6042
|
if (typeof billing.credits_remaining === "number") {
|
|
5620
|
-
line +=
|
|
6043
|
+
line += import_chalk38.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
5621
6044
|
}
|
|
5622
6045
|
return line;
|
|
5623
6046
|
}
|
|
@@ -5625,21 +6048,21 @@ function freeBudgetWarning(billing, anon) {
|
|
|
5625
6048
|
if (!anon || !billing) return null;
|
|
5626
6049
|
if (typeof billing.free_turns_remaining === "number") {
|
|
5627
6050
|
const left2 = billing.free_turns_remaining;
|
|
5628
|
-
if (left2 <= 0) return
|
|
5629
|
-
return
|
|
6051
|
+
if (left2 <= 0) return import_chalk38.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
|
|
6052
|
+
return import_chalk38.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
|
|
5630
6053
|
}
|
|
5631
6054
|
if (typeof billing.free_remaining_cents !== "number") return null;
|
|
5632
6055
|
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
5633
6056
|
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
5634
6057
|
if (left > 8) return null;
|
|
5635
|
-
if (left <= 0) return
|
|
5636
|
-
return
|
|
6058
|
+
if (left <= 0) return import_chalk38.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
6059
|
+
return import_chalk38.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
5637
6060
|
}
|
|
5638
6061
|
function printAssistant(delta) {
|
|
5639
6062
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
5640
6063
|
const m = delta[i];
|
|
5641
6064
|
if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
|
|
5642
|
-
console.log("\n" +
|
|
6065
|
+
console.log("\n" + import_chalk38.default.green("assistant \u203A ") + m.content + "\n");
|
|
5643
6066
|
return;
|
|
5644
6067
|
}
|
|
5645
6068
|
}
|
|
@@ -5649,7 +6072,7 @@ async function replTurn(p, messages, userText) {
|
|
|
5649
6072
|
const llm2 = loadLlmConfig();
|
|
5650
6073
|
const turnId = crypto2.randomUUID();
|
|
5651
6074
|
for (let round = 0; round < CLIENT_ROUND_CAP; round++) {
|
|
5652
|
-
const spinner = (0,
|
|
6075
|
+
const spinner = (0, import_ora20.default)({ text: round === 0 ? "thinking..." : "working...", color: "magenta" }).start();
|
|
5653
6076
|
const body = {
|
|
5654
6077
|
turn_id: turnId,
|
|
5655
6078
|
messages,
|
|
@@ -5665,7 +6088,7 @@ async function replTurn(p, messages, userText) {
|
|
|
5665
6088
|
});
|
|
5666
6089
|
} catch (err) {
|
|
5667
6090
|
spinner.fail("Network error.");
|
|
5668
|
-
console.log(
|
|
6091
|
+
console.log(import_chalk38.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
5669
6092
|
return;
|
|
5670
6093
|
}
|
|
5671
6094
|
let data = null;
|
|
@@ -5683,12 +6106,12 @@ async function replTurn(p, messages, userText) {
|
|
|
5683
6106
|
return;
|
|
5684
6107
|
}
|
|
5685
6108
|
if (res.status === 401) {
|
|
5686
|
-
console.log(
|
|
6109
|
+
console.log(import_chalk38.default.red(" The proxy rejected the API key (401). The key may have been revoked; re-run `apiblaze apichat` to re-provision."));
|
|
5687
6110
|
return;
|
|
5688
6111
|
}
|
|
5689
6112
|
if (!res.ok || !data) {
|
|
5690
6113
|
const err = data && data.error || `HTTP ${res.status}`;
|
|
5691
|
-
console.log(
|
|
6114
|
+
console.log(import_chalk38.default.red(` Chat error: ${err}`));
|
|
5692
6115
|
return;
|
|
5693
6116
|
}
|
|
5694
6117
|
if (Array.isArray(data.delta)) {
|
|
@@ -5702,25 +6125,25 @@ async function replTurn(p, messages, userText) {
|
|
|
5702
6125
|
if (warn) console.log(warn);
|
|
5703
6126
|
if (!data.continue) return;
|
|
5704
6127
|
}
|
|
5705
|
-
console.log(
|
|
6128
|
+
console.log(import_chalk38.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
|
|
5706
6129
|
}
|
|
5707
6130
|
function renderUpsell(p, upsell) {
|
|
5708
6131
|
const loggedIn = !!loadCredentials();
|
|
5709
6132
|
if (upsell.reason === "CAPPED" && !loggedIn) {
|
|
5710
|
-
console.log("\n" +
|
|
5711
|
-
console.log(
|
|
6133
|
+
console.log("\n" + import_chalk38.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
|
|
6134
|
+
console.log(import_chalk38.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
|
|
5712
6135
|
console.log();
|
|
5713
6136
|
return;
|
|
5714
6137
|
}
|
|
5715
|
-
console.log("\n" +
|
|
6138
|
+
console.log("\n" + import_chalk38.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
5716
6139
|
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
|
|
5717
6140
|
if (!loggedIn) {
|
|
5718
|
-
console.log(
|
|
6141
|
+
console.log(import_chalk38.default.dim(" Options: `/login` for more free chats and requests, or `apiblaze llm set-key` to bring your own model key."));
|
|
5719
6142
|
} else {
|
|
5720
|
-
console.log(
|
|
6143
|
+
console.log(import_chalk38.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
|
|
5721
6144
|
}
|
|
5722
6145
|
} else if (upsell.reason === "INFLIGHT") {
|
|
5723
|
-
console.log(
|
|
6146
|
+
console.log(import_chalk38.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
5724
6147
|
}
|
|
5725
6148
|
console.log();
|
|
5726
6149
|
}
|
|
@@ -5789,9 +6212,9 @@ async function openServerProxy(project) {
|
|
|
5789
6212
|
const prior = loadApichats().find((a) => a.projectId === project.projectId && a.dpKey);
|
|
5790
6213
|
let dpKey = prior?.dpKey;
|
|
5791
6214
|
if (!dpKey) {
|
|
5792
|
-
console.log(
|
|
6215
|
+
console.log(import_chalk38.default.dim(` Minting an API key for tenant ${import_chalk38.default.bold(tenant2)} to query project ${import_chalk38.default.bold(project.projectName)}\u2026`));
|
|
5793
6216
|
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
5794
|
-
console.log(` ${
|
|
6217
|
+
console.log(` ${import_chalk38.default.green("\u2714")} API key: ${import_chalk38.default.dim(maskKey(dpKey))}`);
|
|
5795
6218
|
}
|
|
5796
6219
|
const p = {
|
|
5797
6220
|
projectId: project.projectId,
|
|
@@ -5800,9 +6223,11 @@ async function openServerProxy(project) {
|
|
|
5800
6223
|
dpKey,
|
|
5801
6224
|
mcpHost,
|
|
5802
6225
|
proxyUrl: `https://${project.projectId}.abz.run/${version2}/${environment}`,
|
|
5803
|
-
anon: false
|
|
6226
|
+
anon: false,
|
|
6227
|
+
// Reusing an owned proxy: logged-in apichat doors default to invite-only.
|
|
6228
|
+
access: "invite"
|
|
5804
6229
|
};
|
|
5805
|
-
const spinner = (0,
|
|
6230
|
+
const spinner = (0, import_ora20.default)("Preparing the chat\u2026").start();
|
|
5806
6231
|
try {
|
|
5807
6232
|
const raw = await admin({
|
|
5808
6233
|
method: "GET",
|
|
@@ -5814,7 +6239,7 @@ async function openServerProxy(project) {
|
|
|
5814
6239
|
if (spec2 && (spec2.paths || spec2.openapi)) {
|
|
5815
6240
|
await publishMcp(p, spec2);
|
|
5816
6241
|
} else {
|
|
5817
|
-
console.log(
|
|
6242
|
+
console.log(import_chalk38.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
|
|
5818
6243
|
}
|
|
5819
6244
|
} catch (err) {
|
|
5820
6245
|
spinner.fail("Could not open the proxy.");
|
|
@@ -5867,7 +6292,7 @@ async function noArgsMenu(opts) {
|
|
|
5867
6292
|
const me = loadCredentials()?.apiblazeUserId;
|
|
5868
6293
|
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
5869
6294
|
const choices = saved.map((a) => ({
|
|
5870
|
-
name: `Chat with ${
|
|
6295
|
+
name: `Chat with ${import_chalk38.default.bold(a.name)} ${import_chalk38.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
|
|
5871
6296
|
value: { type: "existing", a }
|
|
5872
6297
|
}));
|
|
5873
6298
|
const creds = loadCredentials();
|
|
@@ -5877,14 +6302,14 @@ async function noArgsMenu(opts) {
|
|
|
5877
6302
|
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
5878
6303
|
for (const pr of proxies) {
|
|
5879
6304
|
choices.push({
|
|
5880
|
-
name: `Chat with ${
|
|
6305
|
+
name: `Chat with ${import_chalk38.default.bold(pr.projectName)} ${import_chalk38.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
5881
6306
|
value: { type: "server", project: pr }
|
|
5882
6307
|
});
|
|
5883
6308
|
}
|
|
5884
6309
|
} catch {
|
|
5885
6310
|
}
|
|
5886
6311
|
}
|
|
5887
|
-
choices.push({ name:
|
|
6312
|
+
choices.push({ name: import_chalk38.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
5888
6313
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
5889
6314
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
5890
6315
|
]);
|
|
@@ -5912,7 +6337,9 @@ async function noArgsMenu(opts) {
|
|
|
5912
6337
|
dpKey: a.dpKey,
|
|
5913
6338
|
mcpHost: a.mcpHost,
|
|
5914
6339
|
proxyUrl: void 0,
|
|
5915
|
-
anon: a.anon
|
|
6340
|
+
anon: a.anon,
|
|
6341
|
+
// Saved sessions predate the access field: mirror the provisioning default.
|
|
6342
|
+
access: a.anon ? "open" : "invite"
|
|
5916
6343
|
};
|
|
5917
6344
|
return { p, messages };
|
|
5918
6345
|
}
|
|
@@ -5956,63 +6383,70 @@ async function noArgsMenu(opts) {
|
|
|
5956
6383
|
async function runRepl(p, initialMessages) {
|
|
5957
6384
|
const { default: inquirer3 } = await import("inquirer");
|
|
5958
6385
|
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
5959
|
-
console.log("\n" +
|
|
5960
|
-
if (messages.length) console.log(
|
|
6386
|
+
console.log("\n" + import_chalk38.default.cyan.bold("Chat with your API") + import_chalk38.default.dim(` \xB7 ${p.mcpHost}`));
|
|
6387
|
+
if (messages.length) console.log(import_chalk38.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
5961
6388
|
const llm2 = loadLlmConfig();
|
|
5962
6389
|
console.log(
|
|
5963
|
-
|
|
5964
|
-
llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for
|
|
6390
|
+
import_chalk38.default.dim(
|
|
6391
|
+
llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for more free chats \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
|
|
5965
6392
|
)
|
|
5966
6393
|
);
|
|
5967
6394
|
for (; ; ) {
|
|
5968
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
6395
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk38.default.green("you \u203A") }]);
|
|
5969
6396
|
const text = (input ?? "").trim();
|
|
5970
6397
|
if (!text) continue;
|
|
5971
6398
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
5972
6399
|
if (text === "/login") {
|
|
5973
6400
|
try {
|
|
5974
6401
|
await runLogin();
|
|
5975
|
-
console.log(
|
|
6402
|
+
console.log(import_chalk38.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
5976
6403
|
} catch (err) {
|
|
5977
|
-
console.log(
|
|
6404
|
+
console.log(import_chalk38.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5978
6405
|
}
|
|
5979
6406
|
continue;
|
|
5980
6407
|
}
|
|
5981
6408
|
if (text === "/claim") {
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
6409
|
+
const justLoggedIn = !loadCredentials();
|
|
6410
|
+
if (justLoggedIn) {
|
|
6411
|
+
console.log(import_chalk38.default.dim(" Logging in to claim your workspace\u2026"));
|
|
6412
|
+
try {
|
|
6413
|
+
await runLogin();
|
|
6414
|
+
} catch (err) {
|
|
6415
|
+
console.log(import_chalk38.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6416
|
+
continue;
|
|
6417
|
+
}
|
|
6418
|
+
if (!loadCredentials()) continue;
|
|
5985
6419
|
}
|
|
5986
6420
|
try {
|
|
5987
|
-
await runClaim(void 0, {});
|
|
6421
|
+
if (!justLoggedIn) await runClaim(void 0, { team: loadCredentials()?.teamId });
|
|
5988
6422
|
if (p.mcpHost.endsWith(".mcp.tryabz.run")) {
|
|
5989
6423
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
5990
6424
|
p.anon = false;
|
|
5991
6425
|
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
5992
|
-
console.log(
|
|
6426
|
+
console.log(import_chalk38.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
5993
6427
|
}
|
|
5994
6428
|
} catch (err) {
|
|
5995
|
-
console.log(
|
|
6429
|
+
console.log(import_chalk38.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
5996
6430
|
}
|
|
5997
6431
|
continue;
|
|
5998
6432
|
}
|
|
5999
6433
|
if (text === "/showauth") {
|
|
6000
6434
|
revealAuth = !revealAuth;
|
|
6001
|
-
console.log(
|
|
6435
|
+
console.log(import_chalk38.default.dim(revealAuth ? " The real API key will be shown in the next call's curl. /showauth again to re-mask." : " API key re-masked."));
|
|
6002
6436
|
continue;
|
|
6003
6437
|
}
|
|
6004
6438
|
if (text.startsWith("/")) {
|
|
6005
|
-
console.log(
|
|
6439
|
+
console.log(import_chalk38.default.dim(" Commands: /login /claim /showauth /exit"));
|
|
6006
6440
|
continue;
|
|
6007
6441
|
}
|
|
6008
6442
|
await replTurn(p, messages, text);
|
|
6009
6443
|
saveTranscript(p, messages);
|
|
6010
6444
|
}
|
|
6011
|
-
console.log(
|
|
6445
|
+
console.log(import_chalk38.default.dim("\nBye."));
|
|
6012
6446
|
}
|
|
6013
6447
|
async function runApichat(opts) {
|
|
6014
6448
|
setVerbose(opts.verbose !== false);
|
|
6015
|
-
console.log(
|
|
6449
|
+
console.log(import_chalk38.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
6016
6450
|
if (!opts.openapispec && !opts.target) {
|
|
6017
6451
|
if (!process.stdin.isTTY) {
|
|
6018
6452
|
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
@@ -6025,7 +6459,7 @@ async function runApichat(opts) {
|
|
|
6025
6459
|
}
|
|
6026
6460
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
6027
6461
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
6028
|
-
console.log(` ${
|
|
6462
|
+
console.log(` ${import_chalk38.default.dim("Target:")} ${import_chalk38.default.bold(target)}`);
|
|
6029
6463
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
6030
6464
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
6031
6465
|
fail4(
|
|
@@ -6034,7 +6468,7 @@ async function runApichat(opts) {
|
|
|
6034
6468
|
);
|
|
6035
6469
|
}
|
|
6036
6470
|
const p = await provision(spec2, target, opts);
|
|
6037
|
-
console.log(` ${
|
|
6471
|
+
console.log(` ${import_chalk38.default.dim("Proxy: ")} ${import_chalk38.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
6038
6472
|
upsertApichat({
|
|
6039
6473
|
name: p.projectId,
|
|
6040
6474
|
target,
|
|
@@ -6053,24 +6487,33 @@ async function runApichat(opts) {
|
|
|
6053
6487
|
const secret = await captureTargetSecret(auth, opts);
|
|
6054
6488
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
6055
6489
|
} else {
|
|
6056
|
-
console.log(
|
|
6490
|
+
console.log(import_chalk38.default.dim(" Target auth: none required."));
|
|
6057
6491
|
}
|
|
6058
6492
|
const specText = JSON.stringify(spec2);
|
|
6059
6493
|
await uploadSpec(p, specText, opts);
|
|
6060
6494
|
const mcpUrl = await publishMcp(p, spec2);
|
|
6061
6495
|
console.log();
|
|
6062
|
-
if (p.proxyUrl) console.log(` ${
|
|
6063
|
-
if (mcpUrl)
|
|
6496
|
+
if (p.proxyUrl) console.log(` ${import_chalk38.default.green("\u2713")} proxy ${import_chalk38.default.bold(p.proxyUrl)}`);
|
|
6497
|
+
if (mcpUrl) {
|
|
6498
|
+
console.log(` ${import_chalk38.default.green("\u2713")} mcp ${import_chalk38.default.bold(mcpUrl)}`);
|
|
6499
|
+
if (p.access === "invite") {
|
|
6500
|
+
console.log(import_chalk38.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: invite \u2014 only you + emails you pre-approve"));
|
|
6501
|
+
console.log(import_chalk38.default.dim(` Let others in: apiblaze preapprove someone@company.com${p.tenant ? ` --tenant ${p.tenant}` : ""} (or re-run with --access open)`));
|
|
6502
|
+
} else {
|
|
6503
|
+
console.log(import_chalk38.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: open \u2014 anyone who signs in can call this API"));
|
|
6504
|
+
}
|
|
6505
|
+
}
|
|
6064
6506
|
if (p.anon) {
|
|
6065
|
-
console.log(
|
|
6507
|
+
console.log(import_chalk38.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
|
|
6066
6508
|
}
|
|
6067
6509
|
await runRepl(p);
|
|
6068
6510
|
}
|
|
6069
6511
|
|
|
6070
6512
|
// src/commands/consumer.ts
|
|
6071
|
-
var
|
|
6072
|
-
var
|
|
6513
|
+
var import_chalk39 = __toESM(require("chalk"));
|
|
6514
|
+
var import_ora21 = __toESM(require("ora"));
|
|
6073
6515
|
init_admin();
|
|
6516
|
+
init_resolve();
|
|
6074
6517
|
var DEFAULT_SCOPE = "openid email profile offline_access";
|
|
6075
6518
|
var APIKEYS_BASE = process.env.APIBLAZE_APIKEYS_BASE || "https://apikeys.apiblaze.com";
|
|
6076
6519
|
async function consumerFetch(creds, suffix, init) {
|
|
@@ -6089,7 +6532,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
6089
6532
|
function requireConsumer() {
|
|
6090
6533
|
const c = loadConsumer();
|
|
6091
6534
|
if (!c) {
|
|
6092
|
-
console.error(
|
|
6535
|
+
console.error(import_chalk39.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
6093
6536
|
process.exit(1);
|
|
6094
6537
|
}
|
|
6095
6538
|
return c;
|
|
@@ -6100,7 +6543,7 @@ async function runConsumerLogin(opts) {
|
|
|
6100
6543
|
let clientId = opts.client;
|
|
6101
6544
|
if (clientId) {
|
|
6102
6545
|
if (!tenant2) {
|
|
6103
|
-
console.error(
|
|
6546
|
+
console.error(import_chalk39.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
6104
6547
|
process.exit(1);
|
|
6105
6548
|
}
|
|
6106
6549
|
} else {
|
|
@@ -6112,25 +6555,25 @@ async function runConsumerLogin(opts) {
|
|
|
6112
6555
|
if (!picked) process.exit(1);
|
|
6113
6556
|
tenant2 = picked;
|
|
6114
6557
|
}
|
|
6115
|
-
const s2 = (0,
|
|
6558
|
+
const s2 = (0, import_ora21.default)("Finding the login app...").start();
|
|
6116
6559
|
const clients = await admin({ method: "GET", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/app-clients`, summary: `List app clients for ${tenant2}` }).catch(() => []);
|
|
6117
6560
|
s2.stop();
|
|
6118
6561
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
6119
6562
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
6120
6563
|
if (!pick2) {
|
|
6121
|
-
console.error(
|
|
6564
|
+
console.error(import_chalk39.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
6122
6565
|
process.exit(1);
|
|
6123
6566
|
}
|
|
6124
6567
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
6125
6568
|
}
|
|
6126
6569
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
6127
|
-
console.log(`${
|
|
6570
|
+
console.log(`${import_chalk39.default.cyan("\u2192")} Logging in to ${import_chalk39.default.bold(tenant2)} as a consumer...`);
|
|
6128
6571
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
6129
6572
|
console.log(`
|
|
6130
|
-
Open: ${
|
|
6131
|
-
console.log(` Code: ${
|
|
6573
|
+
Open: ${import_chalk39.default.underline(verificationUri)}`);
|
|
6574
|
+
console.log(` Code: ${import_chalk39.default.bold(userCode)}
|
|
6132
6575
|
`);
|
|
6133
|
-
console.log(
|
|
6576
|
+
console.log(import_chalk39.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
6134
6577
|
}, portalResource);
|
|
6135
6578
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
6136
6579
|
const creds = {
|
|
@@ -6145,7 +6588,7 @@ async function runConsumerLogin(opts) {
|
|
|
6145
6588
|
obtainedAt: Date.now()
|
|
6146
6589
|
};
|
|
6147
6590
|
saveConsumer(creds);
|
|
6148
|
-
console.log(
|
|
6591
|
+
console.log(import_chalk39.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
6149
6592
|
}
|
|
6150
6593
|
async function runConsumerTokens(opts) {
|
|
6151
6594
|
const creds = requireConsumer();
|
|
@@ -6158,29 +6601,29 @@ async function runConsumerTokens(opts) {
|
|
|
6158
6601
|
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));
|
|
6159
6602
|
return;
|
|
6160
6603
|
}
|
|
6161
|
-
console.log(`${
|
|
6604
|
+
console.log(`${import_chalk39.default.cyan("Consumer")} ${import_chalk39.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk39.default.bold(fresh.tenant)}
|
|
6162
6605
|
`);
|
|
6163
|
-
console.log(`${
|
|
6606
|
+
console.log(`${import_chalk39.default.bold("access_token")} ${import_chalk39.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
6164
6607
|
${fresh.accessToken}
|
|
6165
6608
|
`);
|
|
6166
|
-
if (fresh.idToken) console.log(`${
|
|
6609
|
+
if (fresh.idToken) console.log(`${import_chalk39.default.bold("id_token")} ${import_chalk39.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
6167
6610
|
${fresh.idToken}
|
|
6168
6611
|
`);
|
|
6169
|
-
if (fresh.refreshToken) console.log(`${
|
|
6612
|
+
if (fresh.refreshToken) console.log(`${import_chalk39.default.bold("refresh_token")}
|
|
6170
6613
|
${fresh.refreshToken}
|
|
6171
6614
|
`);
|
|
6172
|
-
console.log(
|
|
6615
|
+
console.log(import_chalk39.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
6173
6616
|
}
|
|
6174
6617
|
async function runConsumerApikeys(opts) {
|
|
6175
6618
|
const creds = requireConsumer();
|
|
6176
6619
|
const { default: inquirer3 } = await import("inquirer");
|
|
6177
|
-
const spinner = (0,
|
|
6620
|
+
const spinner = (0, import_ora21.default)("Loading your API keys...").start();
|
|
6178
6621
|
const list = await consumerFetch(creds, "/apikeys");
|
|
6179
6622
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
6180
6623
|
spinner.stop();
|
|
6181
6624
|
if (list.status >= 400) {
|
|
6182
|
-
console.error(
|
|
6183
|
-
if (list.status === 401) console.error(
|
|
6625
|
+
console.error(import_chalk39.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
6626
|
+
if (list.status === 401) console.error(import_chalk39.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
6184
6627
|
process.exit(1);
|
|
6185
6628
|
}
|
|
6186
6629
|
const keys = list.data?.keys ?? [];
|
|
@@ -6188,16 +6631,16 @@ async function runConsumerApikeys(opts) {
|
|
|
6188
6631
|
if (opts.json) {
|
|
6189
6632
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
6190
6633
|
} else if (!keys.length) {
|
|
6191
|
-
console.log(
|
|
6634
|
+
console.log(import_chalk39.default.yellow("No API keys yet."));
|
|
6192
6635
|
} else {
|
|
6193
6636
|
for (const k of keys) {
|
|
6194
6637
|
const clear = revealMap[k.environment]?.key;
|
|
6195
|
-
const shown = clear ?
|
|
6196
|
-
const exp = k.expires_at ?
|
|
6197
|
-
console.log(` ${
|
|
6638
|
+
const shown = clear ? import_chalk39.default.green(clear) : import_chalk39.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
6639
|
+
const exp = k.expires_at ? import_chalk39.default.dim(`exp ${k.expires_at}`) : import_chalk39.default.dim("no expiry");
|
|
6640
|
+
console.log(` ${import_chalk39.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk39.default.dim(k.description ?? "")}`);
|
|
6198
6641
|
}
|
|
6199
6642
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
6200
|
-
console.log(
|
|
6643
|
+
console.log(import_chalk39.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
6201
6644
|
}
|
|
6202
6645
|
}
|
|
6203
6646
|
if (opts.json) return;
|
|
@@ -6211,7 +6654,7 @@ async function runConsumerApikeys(opts) {
|
|
|
6211
6654
|
const body = { environment: answers.environment };
|
|
6212
6655
|
if (answers.description) body.description = answers.description;
|
|
6213
6656
|
if (answers.expiresDays) body.expires_in_seconds = Number(answers.expiresDays) * 86400;
|
|
6214
|
-
const s2 = (0,
|
|
6657
|
+
const s2 = (0, import_ora21.default)("Creating key...").start();
|
|
6215
6658
|
const created = await consumerFetch(list.creds, "/apikeys", { method: "POST", body: JSON.stringify(body) });
|
|
6216
6659
|
if (created.status >= 400) {
|
|
6217
6660
|
s2.fail(`Create failed (${created.status}): ${created.data?.error ?? ""}`);
|
|
@@ -6219,16 +6662,17 @@ async function runConsumerApikeys(opts) {
|
|
|
6219
6662
|
}
|
|
6220
6663
|
s2.succeed("Key created.");
|
|
6221
6664
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
6222
|
-
if (key) console.log(` ${
|
|
6223
|
-
else console.log(
|
|
6665
|
+
if (key) console.log(` ${import_chalk39.default.green(key)} ${import_chalk39.default.dim("(shown once \u2014 store it now)")}`);
|
|
6666
|
+
else console.log(import_chalk39.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
6224
6667
|
}
|
|
6225
6668
|
|
|
6226
6669
|
// src/commands/sidecar.ts
|
|
6227
|
-
var
|
|
6228
|
-
var
|
|
6670
|
+
var import_chalk40 = __toESM(require("chalk"));
|
|
6671
|
+
var import_ora22 = __toESM(require("ora"));
|
|
6229
6672
|
var fs10 = __toESM(require("fs"));
|
|
6230
6673
|
var path7 = __toESM(require("path"));
|
|
6231
6674
|
init_admin();
|
|
6675
|
+
init_resolve();
|
|
6232
6676
|
init_auth();
|
|
6233
6677
|
function detectNextProject(root) {
|
|
6234
6678
|
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs10.existsSync(path7.join(root, f)));
|
|
@@ -6266,18 +6710,18 @@ function upsertEnvLocal(root, token) {
|
|
|
6266
6710
|
}
|
|
6267
6711
|
function installSidecarPackage(root) {
|
|
6268
6712
|
if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
6269
|
-
console.log(` ${
|
|
6713
|
+
console.log(` ${import_chalk40.default.green("\u2713")} apiblaze package already installed`);
|
|
6270
6714
|
return;
|
|
6271
6715
|
}
|
|
6272
6716
|
const has = (f) => fs10.existsSync(path7.join(root, f));
|
|
6273
6717
|
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" };
|
|
6274
|
-
const spinner = (0,
|
|
6718
|
+
const spinner = (0, import_ora22.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
6275
6719
|
try {
|
|
6276
6720
|
const { execSync } = require("child_process");
|
|
6277
6721
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
6278
6722
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
6279
6723
|
} catch {
|
|
6280
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
6724
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk40.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk40.default.cyan("npm run dev")}.`);
|
|
6281
6725
|
}
|
|
6282
6726
|
}
|
|
6283
6727
|
function readEnvKey(root) {
|
|
@@ -6416,7 +6860,7 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6416
6860
|
const { sidecarInitAnonymous: sidecarInitAnonymous2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
6417
6861
|
const { saveAnonCred: saveAnonCred2, clearAnonCred: clearAnonCred2 } = await Promise.resolve().then(() => (init_anon_cred(), anon_cred_exports));
|
|
6418
6862
|
if (opts.newSession) clearAnonCred2();
|
|
6419
|
-
const spinner = (0,
|
|
6863
|
+
const spinner = (0, import_ora22.default)("Setting up a sidecar (no login needed)...").start();
|
|
6420
6864
|
let out;
|
|
6421
6865
|
try {
|
|
6422
6866
|
out = await sidecarInitAnonymous2();
|
|
@@ -6428,29 +6872,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
6428
6872
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
6429
6873
|
const envState = upsertEnvLocal(root, out.token);
|
|
6430
6874
|
ensureGitignored(root);
|
|
6431
|
-
console.log(` ${
|
|
6432
|
-
console.log(` ${
|
|
6875
|
+
console.log(` ${import_chalk40.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6876
|
+
console.log(` ${import_chalk40.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
6433
6877
|
installSidecarPackage(root);
|
|
6434
6878
|
let inspectorPath = null;
|
|
6435
6879
|
if (!opts.noInspector) {
|
|
6436
6880
|
inspectorPath = generateInspector(root, router);
|
|
6437
|
-
if (inspectorPath) console.log(` ${
|
|
6881
|
+
if (inspectorPath) console.log(` ${import_chalk40.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6438
6882
|
}
|
|
6439
6883
|
console.log("");
|
|
6440
|
-
console.log(
|
|
6441
|
-
console.log(` 1. ${
|
|
6884
|
+
console.log(import_chalk40.default.bold("Done (no account needed). What happens next:"));
|
|
6885
|
+
console.log(` 1. ${import_chalk40.default.cyan("npm run dev")} and use your app.`);
|
|
6442
6886
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
6443
|
-
console.log(` ${
|
|
6887
|
+
console.log(` ${import_chalk40.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
6444
6888
|
console.log("");
|
|
6445
|
-
console.log(
|
|
6446
|
-
console.log(` ${
|
|
6447
|
-
console.log(
|
|
6889
|
+
console.log(import_chalk40.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
6890
|
+
console.log(` ${import_chalk40.default.cyan("apiblaze login")} then ${import_chalk40.default.cyan("apiblaze claim")} ${import_chalk40.default.dim("(no code needed here)")}`);
|
|
6891
|
+
console.log(import_chalk40.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
6448
6892
|
}
|
|
6449
6893
|
async function runSidecar(opts) {
|
|
6450
6894
|
const root = path7.resolve(opts.dir ?? process.cwd());
|
|
6451
6895
|
const detected = detectNextProject(root);
|
|
6452
6896
|
if (!detected.found) {
|
|
6453
|
-
console.log(
|
|
6897
|
+
console.log(import_chalk40.default.yellow(`No Next.js project detected in ${root}.`));
|
|
6454
6898
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
6455
6899
|
return;
|
|
6456
6900
|
}
|
|
@@ -6461,10 +6905,10 @@ async function runSidecar(opts) {
|
|
|
6461
6905
|
if (!loadCredentials()) {
|
|
6462
6906
|
upsertEnvLocal(root, readEnvKey(root));
|
|
6463
6907
|
ensureGitignored(root);
|
|
6464
|
-
console.log(` ${
|
|
6465
|
-
console.log(` ${
|
|
6908
|
+
console.log(` ${import_chalk40.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
6909
|
+
console.log(` ${import_chalk40.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
6466
6910
|
installSidecarPackage(root);
|
|
6467
|
-
console.log(
|
|
6911
|
+
console.log(import_chalk40.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
6468
6912
|
return;
|
|
6469
6913
|
}
|
|
6470
6914
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -6473,7 +6917,7 @@ async function runSidecar(opts) {
|
|
|
6473
6917
|
const mustMint = !existingKey || opts.rotate || switchingTeam;
|
|
6474
6918
|
let token = existingKey ?? "";
|
|
6475
6919
|
if (mustMint) {
|
|
6476
|
-
const spinner = (0,
|
|
6920
|
+
const spinner = (0, import_ora22.default)(existingKey ? "Re-establishing the sidecar (minting a fresh invoke key)..." : "Setting up the sidecar (tenant + non-expiring invoke key)...").start();
|
|
6477
6921
|
try {
|
|
6478
6922
|
const out = await admin({
|
|
6479
6923
|
method: "POST",
|
|
@@ -6487,40 +6931,41 @@ async function runSidecar(opts) {
|
|
|
6487
6931
|
throw err;
|
|
6488
6932
|
}
|
|
6489
6933
|
} else {
|
|
6490
|
-
console.log(
|
|
6934
|
+
console.log(import_chalk40.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
6491
6935
|
}
|
|
6492
6936
|
const envState = upsertEnvLocal(root, token);
|
|
6493
6937
|
ensureGitignored(root);
|
|
6494
|
-
console.log(` ${
|
|
6938
|
+
console.log(` ${import_chalk40.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
6495
6939
|
const wireState = wireInstrumentation(root);
|
|
6496
|
-
console.log(` ${
|
|
6940
|
+
console.log(` ${import_chalk40.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
6497
6941
|
installSidecarPackage(root);
|
|
6498
6942
|
let inspectorPath = null;
|
|
6499
6943
|
if (!opts.noInspector) {
|
|
6500
6944
|
inspectorPath = generateInspector(root, detected.router);
|
|
6501
|
-
if (inspectorPath) console.log(` ${
|
|
6945
|
+
if (inspectorPath) console.log(` ${import_chalk40.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
6502
6946
|
}
|
|
6503
6947
|
console.log("");
|
|
6504
|
-
console.log(
|
|
6505
|
-
console.log(` 1. ${
|
|
6506
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
6507
|
-
console.log(` 3. Approve the ones to route: ${
|
|
6948
|
+
console.log(import_chalk40.default.bold("Done. What happens next:"));
|
|
6949
|
+
console.log(` 1. ${import_chalk40.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
6950
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk40.default.bold("candidates")} \u2014 list them: ${import_chalk40.default.cyan("apiblaze sidecar")}`);
|
|
6951
|
+
console.log(` 3. Approve the ones to route: ${import_chalk40.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
6508
6952
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
6509
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
6510
|
-
if (switchingTeam) console.log(
|
|
6953
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk40.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
|
|
6954
|
+
if (switchingTeam) console.log(import_chalk40.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
6511
6955
|
console.log("");
|
|
6512
|
-
console.log(
|
|
6513
|
-
console.log(
|
|
6514
|
-
console.log(
|
|
6956
|
+
console.log(import_chalk40.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
6957
|
+
console.log(import_chalk40.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
6958
|
+
console.log(import_chalk40.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
6515
6959
|
console.log("");
|
|
6516
|
-
console.log(
|
|
6517
|
-
console.log(
|
|
6960
|
+
console.log(import_chalk40.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
6961
|
+
console.log(import_chalk40.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
6518
6962
|
}
|
|
6519
6963
|
|
|
6520
6964
|
// src/commands/origins.ts
|
|
6521
|
-
var
|
|
6522
|
-
var
|
|
6965
|
+
var import_chalk41 = __toESM(require("chalk"));
|
|
6966
|
+
var import_ora23 = __toESM(require("ora"));
|
|
6523
6967
|
init_admin();
|
|
6968
|
+
init_resolve();
|
|
6524
6969
|
init_auth();
|
|
6525
6970
|
init_anon_cred();
|
|
6526
6971
|
async function runOriginsList(opts) {
|
|
@@ -6528,7 +6973,7 @@ async function runOriginsList(opts) {
|
|
|
6528
6973
|
if (!loadCredentials()) {
|
|
6529
6974
|
const cred = loadAnonCred();
|
|
6530
6975
|
if (!cred) {
|
|
6531
|
-
console.log(
|
|
6976
|
+
console.log(import_chalk41.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
6532
6977
|
return;
|
|
6533
6978
|
}
|
|
6534
6979
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -6546,30 +6991,30 @@ async function runOriginsList(opts) {
|
|
|
6546
6991
|
}
|
|
6547
6992
|
const routed = out.routed ?? [];
|
|
6548
6993
|
const candidates = out.candidates ?? [];
|
|
6549
|
-
console.log(
|
|
6994
|
+
console.log(import_chalk41.default.bold(`
|
|
6550
6995
|
Routed through APIblaze (${routed.length})`));
|
|
6551
|
-
if (!routed.length) console.log(
|
|
6552
|
-
for (const r of routed) console.log(` ${
|
|
6553
|
-
console.log(
|
|
6996
|
+
if (!routed.length) console.log(import_chalk41.default.dim(" none yet"));
|
|
6997
|
+
for (const r of routed) console.log(` ${import_chalk41.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk41.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
6998
|
+
console.log(import_chalk41.default.bold(`
|
|
6554
6999
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
6555
|
-
if (!candidates.length) console.log(
|
|
7000
|
+
if (!candidates.length) console.log(import_chalk41.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
6556
7001
|
for (const c of candidates) {
|
|
6557
|
-
console.log(` ${
|
|
7002
|
+
console.log(` ${import_chalk41.default.yellow("\u25CB")} ${c.origin} ${import_chalk41.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
6558
7003
|
}
|
|
6559
7004
|
if (candidates.length) {
|
|
6560
|
-
console.log(
|
|
7005
|
+
console.log(import_chalk41.default.dim(`
|
|
6561
7006
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
6562
|
-
console.log(
|
|
7007
|
+
console.log(import_chalk41.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
6563
7008
|
}
|
|
6564
7009
|
}
|
|
6565
7010
|
async function runOriginsApprove(origin, opts) {
|
|
6566
7011
|
if (!loadCredentials()) {
|
|
6567
7012
|
const cred = loadAnonCred();
|
|
6568
7013
|
if (!cred) {
|
|
6569
|
-
console.error(
|
|
7014
|
+
console.error(import_chalk41.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
6570
7015
|
process.exit(1);
|
|
6571
7016
|
}
|
|
6572
|
-
const spinner2 = (0,
|
|
7017
|
+
const spinner2 = (0, import_ora23.default)(`Approving ${origin} (anonymous)...`).start();
|
|
6573
7018
|
try {
|
|
6574
7019
|
const out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/approve`, { method: "POST", body: JSON.stringify({ origin }) });
|
|
6575
7020
|
spinner2.succeed(`Approved ${origin} \u2192 proxy ${out.project_id}. Routing within ~5 min.`);
|
|
@@ -6580,7 +7025,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
6580
7025
|
return;
|
|
6581
7026
|
}
|
|
6582
7027
|
const { teamId } = await resolveTeam(opts.team);
|
|
6583
|
-
const spinner = (0,
|
|
7028
|
+
const spinner = (0, import_ora23.default)(`Approving ${origin}...`).start();
|
|
6584
7029
|
try {
|
|
6585
7030
|
const out = await admin({
|
|
6586
7031
|
method: "POST",
|
|
@@ -6597,7 +7042,7 @@ async function runOriginsApprove(origin, opts) {
|
|
|
6597
7042
|
}
|
|
6598
7043
|
async function runOriginsDeny(origin, opts) {
|
|
6599
7044
|
const { teamId } = await resolveTeam(opts.team);
|
|
6600
|
-
const spinner = (0,
|
|
7045
|
+
const spinner = (0, import_ora23.default)(`Dismissing ${origin}...`).start();
|
|
6601
7046
|
try {
|
|
6602
7047
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/dismiss`, body: { origin }, summary: `Dismiss sidecar origin ${origin}` });
|
|
6603
7048
|
spinner.succeed(`Dismissed ${origin}. It won't be suggested again.`);
|
|
@@ -6608,7 +7053,7 @@ async function runOriginsDeny(origin, opts) {
|
|
|
6608
7053
|
}
|
|
6609
7054
|
async function runOriginsRemove(origin, opts) {
|
|
6610
7055
|
const { teamId } = await resolveTeam(opts.team);
|
|
6611
|
-
const spinner = (0,
|
|
7056
|
+
const spinner = (0, import_ora23.default)(`Removing the proxy for ${origin}...`).start();
|
|
6612
7057
|
try {
|
|
6613
7058
|
await admin({ method: "POST", path: `/teams/${encodeURIComponent(teamId)}/sidecar/remove`, body: { origin }, summary: `Un-route sidecar origin ${origin}` });
|
|
6614
7059
|
spinner.succeed(`Removed ${origin}. Your app will stop routing it (goes direct) within ~5 min.`);
|
|
@@ -6619,19 +7064,19 @@ async function runOriginsRemove(origin, opts) {
|
|
|
6619
7064
|
}
|
|
6620
7065
|
|
|
6621
7066
|
// src/commands/op.ts
|
|
6622
|
-
var
|
|
7067
|
+
var import_chalk42 = __toESM(require("chalk"));
|
|
6623
7068
|
init_auth();
|
|
6624
7069
|
init_trace();
|
|
6625
7070
|
init_types();
|
|
6626
7071
|
var OPERATOR_EMAILS = /* @__PURE__ */ new Set(["julienpmjacquet@gmail.com", "chkev@umich.edu"]);
|
|
6627
|
-
var
|
|
7072
|
+
var DASHBOARD_BASE8 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
6628
7073
|
function isOperatorLogin() {
|
|
6629
7074
|
const email = loadCredentials()?.email?.toLowerCase();
|
|
6630
7075
|
return !!email && OPERATOR_EMAILS.has(email);
|
|
6631
7076
|
}
|
|
6632
7077
|
async function opCall(call) {
|
|
6633
7078
|
const token = getAccessToken();
|
|
6634
|
-
const res = await fetch(`${
|
|
7079
|
+
const res = await fetch(`${DASHBOARD_BASE8}/api/cli/op`, {
|
|
6635
7080
|
method: "POST",
|
|
6636
7081
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
6637
7082
|
body: JSON.stringify({ path: call.path, method: call.method, body: call.body })
|
|
@@ -6652,82 +7097,82 @@ function printResidue(report, applied) {
|
|
|
6652
7097
|
const up = report?.upstash ?? {};
|
|
6653
7098
|
const fga = report?.fga ?? {};
|
|
6654
7099
|
const ghosts = report?.ghosts ?? {};
|
|
6655
|
-
console.log(
|
|
6656
|
-
console.log(
|
|
7100
|
+
console.log(import_chalk42.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
7101
|
+
console.log(import_chalk42.default.bold("\n Upstash"));
|
|
6657
7102
|
const orphans = up.orphans ?? [];
|
|
6658
|
-
if (orphans.length === 0) console.log(
|
|
6659
|
-
for (const o of orphans) console.log(` ${
|
|
6660
|
-
console.log(
|
|
7103
|
+
if (orphans.length === 0) console.log(import_chalk42.default.green(" no orphaned keys"));
|
|
7104
|
+
for (const o of orphans) console.log(` ${import_chalk42.default.yellow(o.key)} ${import_chalk42.default.dim(`\u2014 ${o.reason}`)}`);
|
|
7105
|
+
console.log(import_chalk42.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
6661
7106
|
if (up.anon_wallet_detail) {
|
|
6662
7107
|
const d = up.anon_wallet_detail;
|
|
6663
|
-
console.log(
|
|
7108
|
+
console.log(import_chalk42.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)"}`));
|
|
6664
7109
|
}
|
|
6665
7110
|
if (up.keyspace_census) {
|
|
6666
7111
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
6667
|
-
console.log(
|
|
7112
|
+
console.log(import_chalk42.default.dim(` keyspace: ${census}`));
|
|
6668
7113
|
}
|
|
6669
|
-
if (up.unknown?.length) console.log(
|
|
6670
|
-
if (applied) console.log(` ${
|
|
6671
|
-
for (const e of up.errors ?? []) console.log(
|
|
6672
|
-
console.log(
|
|
7114
|
+
if (up.unknown?.length) console.log(import_chalk42.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
7115
|
+
if (applied) console.log(` ${import_chalk42.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
7116
|
+
for (const e of up.errors ?? []) console.log(import_chalk42.default.red(` error: ${e}`));
|
|
7117
|
+
console.log(import_chalk42.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
6673
7118
|
if (applied) {
|
|
6674
7119
|
const swept = fga?.swept ?? [];
|
|
6675
|
-
if (swept.length === 0) console.log(
|
|
7120
|
+
if (swept.length === 0) console.log(import_chalk42.default.green(" no orphaned stores"));
|
|
6676
7121
|
for (const s of swept) {
|
|
6677
7122
|
console.log(
|
|
6678
|
-
` ${
|
|
7123
|
+
` ${import_chalk42.default.yellow(s.store_id)} ${import_chalk42.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
6679
7124
|
);
|
|
6680
7125
|
}
|
|
6681
|
-
if (fga?.remaining) console.log(
|
|
7126
|
+
if (fga?.remaining) console.log(import_chalk42.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
6682
7127
|
const st = fga?.side_tables;
|
|
6683
|
-
if (st) console.log(
|
|
7128
|
+
if (st) console.log(import_chalk42.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})` : ""}`));
|
|
6684
7129
|
} else {
|
|
6685
7130
|
const fgaOrphans = fga?.orphans ?? [];
|
|
6686
|
-
if (fgaOrphans.length === 0) console.log(
|
|
7131
|
+
if (fgaOrphans.length === 0) console.log(import_chalk42.default.green(" no orphaned stores"));
|
|
6687
7132
|
for (const s of fgaOrphans) {
|
|
6688
7133
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
6689
|
-
console.log(` ${
|
|
7134
|
+
console.log(` ${import_chalk42.default.yellow(s.store_id)} ${import_chalk42.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
6690
7135
|
}
|
|
6691
|
-
console.log(
|
|
7136
|
+
console.log(import_chalk42.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
6692
7137
|
const st = fga?.side_tables;
|
|
6693
|
-
if (st) console.log(
|
|
7138
|
+
if (st) console.log(import_chalk42.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`));
|
|
6694
7139
|
}
|
|
6695
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
6696
|
-
console.log(
|
|
7140
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk42.default.red(` error: ${e}`));
|
|
7141
|
+
console.log(import_chalk42.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
6697
7142
|
if (applied) {
|
|
6698
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
6699
|
-
else console.log(` ${
|
|
7143
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk42.default.green(" no ghost tuples"));
|
|
7144
|
+
else console.log(` ${import_chalk42.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk42.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
6700
7145
|
} else {
|
|
6701
7146
|
const n = ghosts?.ghost_count ?? 0;
|
|
6702
|
-
if (n === 0) console.log(
|
|
7147
|
+
if (n === 0) console.log(import_chalk42.default.green(` no ghost tuples ${import_chalk42.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
6703
7148
|
else {
|
|
6704
|
-
console.log(
|
|
7149
|
+
console.log(import_chalk42.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
6705
7150
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
6706
|
-
console.log(
|
|
7151
|
+
console.log(import_chalk42.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
6707
7152
|
}
|
|
6708
|
-
if (n > 20) console.log(
|
|
7153
|
+
if (n > 20) console.log(import_chalk42.default.dim(` \u2026 and ${n - 20} more`));
|
|
6709
7154
|
}
|
|
6710
7155
|
}
|
|
6711
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
7156
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk42.default.red(` error: ${e}`));
|
|
6712
7157
|
console.log();
|
|
6713
7158
|
}
|
|
6714
7159
|
async function runOp(sub, opts = {}) {
|
|
6715
7160
|
if (!loadCredentials()) {
|
|
6716
|
-
console.log(
|
|
7161
|
+
console.log(import_chalk42.default.dim("Not logged in. Run `apiblaze login`."));
|
|
6717
7162
|
return;
|
|
6718
7163
|
}
|
|
6719
7164
|
if (!isOperatorLogin()) {
|
|
6720
|
-
console.log(
|
|
7165
|
+
console.log(import_chalk42.default.dim("`apiblaze op` is only available to platform operators."));
|
|
6721
7166
|
return;
|
|
6722
7167
|
}
|
|
6723
7168
|
switch (sub) {
|
|
6724
7169
|
case void 0:
|
|
6725
7170
|
case "menu": {
|
|
6726
|
-
console.log(
|
|
6727
|
-
console.log(` ${
|
|
6728
|
-
console.log(` ${
|
|
6729
|
-
console.log(` ${
|
|
6730
|
-
console.log(
|
|
7171
|
+
console.log(import_chalk42.default.bold("\nOperator menu"));
|
|
7172
|
+
console.log(` ${import_chalk42.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
7173
|
+
console.log(` ${import_chalk42.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk42.default.dim("-y to skip")})`);
|
|
7174
|
+
console.log(` ${import_chalk42.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
7175
|
+
console.log(import_chalk42.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
6731
7176
|
`));
|
|
6732
7177
|
return;
|
|
6733
7178
|
}
|
|
@@ -6746,17 +7191,17 @@ async function runOp(sub, opts = {}) {
|
|
|
6746
7191
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
6747
7192
|
printResidue(report, false);
|
|
6748
7193
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
6749
|
-
console.log(
|
|
7194
|
+
console.log(import_chalk42.default.green("Nothing to sweep."));
|
|
6750
7195
|
return;
|
|
6751
7196
|
}
|
|
6752
7197
|
if (!opts.yes) {
|
|
6753
7198
|
const readline2 = await import("readline/promises");
|
|
6754
7199
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
6755
7200
|
const answer = await rl.question(
|
|
6756
|
-
|
|
7201
|
+
import_chalk42.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: `)
|
|
6757
7202
|
);
|
|
6758
7203
|
rl.close();
|
|
6759
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
7204
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk42.default.dim("Aborted."));
|
|
6760
7205
|
}
|
|
6761
7206
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
6762
7207
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -6767,15 +7212,15 @@ async function runOp(sub, opts = {}) {
|
|
|
6767
7212
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
6768
7213
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
6769
7214
|
const accounts = data?.accounts ?? [];
|
|
6770
|
-
if (accounts.length === 0) return void console.log(
|
|
7215
|
+
if (accounts.length === 0) return void console.log(import_chalk42.default.dim("No credit wallets."));
|
|
6771
7216
|
for (const a of accounts) {
|
|
6772
7217
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
6773
|
-
console.log(` ${
|
|
7218
|
+
console.log(` ${import_chalk42.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk42.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk42.default.dim(" \u2014 anon") : ""}`);
|
|
6774
7219
|
}
|
|
6775
7220
|
return;
|
|
6776
7221
|
}
|
|
6777
7222
|
default:
|
|
6778
|
-
console.log(
|
|
7223
|
+
console.log(import_chalk42.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
6779
7224
|
}
|
|
6780
7225
|
}
|
|
6781
7226
|
|
|
@@ -6808,7 +7253,7 @@ program.command("login").description("Authenticate with APIblaze").action(async
|
|
|
6808
7253
|
process.exit(1);
|
|
6809
7254
|
}
|
|
6810
7255
|
});
|
|
6811
|
-
program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.abz.run)").option("--target <url>", "Target URL to forward requests to").option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("--apiversion <version>", "API version to create (e.g. 2.0.0). Creating a new version of a proxy you own adds a version to the existing project.").option("--product <slug>", "Product tag to group this project under in the portal (team-scoped; anonymous create). Defaults to your team's existing/placeholder tag.").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--new-session", "Start a fresh anonymous session (do not group with prior anonymous creates)").option("--json", "Output machine-readable JSON (non-interactive)").action(async (opts) => {
|
|
7256
|
+
program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.abz.run)").option("--target <url>", "Target URL to forward requests to").option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("--identified", "Require every call to identify its end user (X-End-User-Id or a login token); unattributed calls are rejected").option("--iam", "Turn IAM on for the proxy's tenant so users & groups apply to identified calls").option("--apiversion <version>", "API version to create (e.g. 2.0.0). Creating a new version of a proxy you own adds a version to the existing project.").option("--product <slug>", "Product tag to group this project under in the portal (team-scoped; anonymous create). Defaults to your team's existing/placeholder tag.").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--new-session", "Start a fresh anonymous session (do not group with prior anonymous creates)").option("--json", "Output machine-readable JSON (non-interactive)").action(async (opts) => {
|
|
6812
7257
|
try {
|
|
6813
7258
|
await runCreate(opts);
|
|
6814
7259
|
} catch (err) {
|
|
@@ -6821,7 +7266,7 @@ agent.command("authz").description("Chat to design and turn on access rules for
|
|
|
6821
7266
|
program.command("rule").description("Author an object-level access rule in plain English, in one shot (billed per turn)").argument("<rule>", 'The rule in plain English, e.g. "users see only their own rows"').argument("<project>", "Project name or id").option("--enforce", "Turn enforcement on immediately (default: shadow-publish only)").option("--apiversion <version>", "API version (defaults to the project's)").action(action((rule, project, opts) => runRule(rule, project, opts)));
|
|
6822
7267
|
agent.command("openapi").description("Chat to build your API spec from real traffic").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").action(action((project, apiVersion) => runOpenapi(project, apiVersion)));
|
|
6823
7268
|
agent.command("mcp").description("Chat to build an MCP server for an API").argument("<project>", "Project name or id").argument("[apiVersion]", "API version (defaults to the project's)").option("--environment <env>", "Environment to publish (default: prod)").action(action((project, apiVersion, opts) => runMcp(project, apiVersion, opts)));
|
|
6824
|
-
program.command("apichat").description("Turn any API into a chat: point at an OpenAPI spec and start asking questions (no login needed)").option("--
|
|
7269
|
+
program.command("apichat").description("Turn any API into a chat: point at an OpenAPI spec and start asking questions (no login needed)").option("--openapi <file|url>", "OpenAPI/Swagger spec to build from (JSON or YAML; file path or URL)").option("--openapispec <file|url>", "Alias for --openapi").option("--target <url>", "Upstream base URL (overrides servers[0].url; enables spec discovery when --openapi is omitted)").option("--name <name>", "Proxy name (defaults to the target host)").option("--apiversion <version>", "API version to create (e.g. 1.0.0)").option("--environment <env>", "Environment to chat against (default: prod anonymous / dev logged-in)").option("--access <mode>", 'Who can call this API once connected (e.g. via Claude): "open" = anyone who signs in, "invite" = only you + emails you pre-approve. Default: invite when logged in, open when anonymous.').option("--target-auth-env <ENV_VAR>", "Read the upstream credential from this env var (CI-safe; required when there is no TTY and the API needs auth)").option("--force", "Proceed even if the API uses oauth2/openIdConnect target auth (you configure target auth yourself later)").option("-y, --yes", "Skip confirmation prompts").option("--no-verbose", "Hide the per-turn proxy curl trace (shown by default for apichat)").action(action((opts) => runApichat({ ...opts, openapispec: opts.openapispec ?? opts.openapi })));
|
|
6825
7270
|
var llm = program.command("llm").description("Manage a local LLM provider key for chat (optional \u2014 lifts model quality, bills your key)");
|
|
6826
7271
|
llm.command("set-key").description("Store an LLM provider key locally (OpenRouter/Anthropic/DeepSeek/OpenAI)").argument("[key]", "The API key (omit to enter it hidden at a prompt)").option("--model <id>", "Model id to use with this key (e.g. anthropic/claude-haiku-4.5)").action(action((key, opts) => runLlmSetKey(key, opts)));
|
|
6827
7272
|
llm.command("show").description("Show the locally stored LLM key (masked)").action(action(() => runLlmShow()));
|
|
@@ -6838,7 +7283,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
6838
7283
|
try {
|
|
6839
7284
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
6840
7285
|
if (Number.isNaN(resolved)) {
|
|
6841
|
-
console.error(
|
|
7286
|
+
console.error(import_chalk43.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
6842
7287
|
process.exit(1);
|
|
6843
7288
|
}
|
|
6844
7289
|
await runDev({ port: resolved, captureFile: opts.captureFile });
|
|
@@ -6907,6 +7352,7 @@ group.command("remove-user").description("Remove a user from a group").argument(
|
|
|
6907
7352
|
group.command("add-group").description("Nest a group inside another (e.g. `group add-group reservationists admin`)").argument("<child>", "Child group name").argument("<parent>", "Parent group name").option("--tenant <slug>", "Tenant").action(action((child, parent, opts) => runGroupAddGroup(child, parent, opts)));
|
|
6908
7353
|
group.command("remove-group").description("Un-nest a subgroup from its parent").argument("<child>", "Child group name").argument("<parent>", "Parent group name").option("--tenant <slug>", "Tenant").action(action((child, parent, opts) => runGroupRemoveGroup(child, parent, opts)));
|
|
6909
7354
|
group.command("users").description("List the tenant user directory + identities seen in traffic").option("--tenant <slug>", "Tenant").option("--json", "Machine-readable output").action(action((opts) => runGroupUsers(opts)));
|
|
7355
|
+
program.command("preapprove").description("Allow an email or company domain to sign in to an access-restricted API (--list, --remove)").argument("[email-or-domain]", "Email (someone@acme.com) or whole company domain (acme.com)").option("--tenant <slug>", "Tenant (defaults to your team's only tenant, or prompts)").option("--list", "List the current pre-approval allow-list").option("--remove", "Remove the given email/domain from the allow-list").option("--group <names...>", "Group(s) to auto-assign the user to on first sign-in").option("--json", "Machine-readable output (with --list)").action(action((who, opts) => runPreapprove(who, opts)));
|
|
6910
7356
|
var tenant = program.command("tenant").description("Manage tenants \u2014 bare command opens the interactive picker (settings, app clients, providers)").action(action(() => runTenantManage(void 0, {})));
|
|
6911
7357
|
tenant.command("manage").description("Browse & edit one tenant: settings, login app clients, providers, issuers (search-first picker)").argument("[query]", "Search by tenant name/display name (omit to use your tenant scope or pick)").option("--tenant <slug>", "Exact tenant slug (skips the picker)").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantManage(query, opts)));
|
|
6912
7358
|
tenant.command("use").description("Set the sticky tenant scope for future commands (search-first; --clear to unset)").argument("[query]", "Search by tenant name/display name").option("--clear", "Clear the tenant scope").option("--team <id|name>", "Team (defaults to active team)").action(action((query, opts) => runTenantUse(query, opts)));
|
|
@@ -6915,6 +7361,8 @@ tenant.command("create").description("Create a tenant in your team (tenant names
|
|
|
6915
7361
|
tenant.command("attach").description("Attach a tenant to a proxy").argument("<project>", "Project name or id").requiredOption("--tenant <slug>", "Tenant slug to attach").option("--auth-config <id>", "Auth config id to bind").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTenantAttach(project, opts)));
|
|
6916
7362
|
tenant.command("delete").description("Delete a tenant (full cascade)").argument("<slug>", "Tenant slug to delete").option("--team <id|name>", "Team (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").action(action((slug, opts) => runTenantDelete(slug, opts)));
|
|
6917
7363
|
tenant.command("cors").description("Set the CORS allow-list for a tenant").requiredOption("--tenant <slug>", "Tenant slug").option("--origins <list>", 'Comma-separated origins (or "*"); empty clears').option("--team <id|name>", "Team (defaults to active team)").action(action((opts) => runTenantCors(opts)));
|
|
7364
|
+
program.command("iam").description("Turn users & groups on/off for a proxy's tenant (identified calls get their user's groups applied)").argument("<project>", "Project name or id").argument("<state>", "on | off").option("--tenant <name>", "Tenant to toggle (defaults to the project's tenant)").option("--team <id|name>", "Team (defaults to active team, or your anonymous workspace)").option("--apiversion <version>", "API version (defaults to the first)").option("--json", "Output machine-readable JSON").action(action((project, state, opts) => runIamToggle(project, state, opts)));
|
|
7365
|
+
program.command("identified").description("Require calls to identify their end user (X-End-User-Id or a login token) \u2014 or allow unattributed calls again").argument("<project>", "Project name or id").argument("<mode>", "require | allow-anon").option("--team <id|name>", "Team (defaults to active team, or your anonymous workspace)").option("--apiversion <version>", "API version (defaults to the first)").option("--json", "Output machine-readable JSON").action(action((project, mode, opts) => runIdentifiedToggle(project, mode, opts)));
|
|
6918
7366
|
var apikeys = new import_commander.Command("apikeys").description("Producer control-plane API keys (list, then offer to create one)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runApikeysMenu(opts)));
|
|
6919
7367
|
apikeys.command("list").description("List control-plane API keys in your team").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runKeyList(opts)));
|
|
6920
7368
|
apikeys.command("mint").description("Create a control-plane API key (secret shown once)").option("--desc <text>", "Description").option("--expires-days <n>", "Expiry in days (default 90 server-side)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts) => runKeyMint(opts)));
|
|
@@ -6925,10 +7373,12 @@ program.addCommand(op, { hidden: true });
|
|
|
6925
7373
|
var spec = program.command("spec").description("View or update a proxy's OpenAPI spec (or build one by chatting: apiblaze agent openapi)");
|
|
6926
7374
|
spec.command("get").description("Print the current OpenAPI document").argument("<project>", "Project name or id").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Compact JSON output").action(action((project, opts) => runSpecGet(project, opts)));
|
|
6927
7375
|
spec.command("set").description("Replace the stored OpenAPI spec from a local file").argument("<project>", "Project name or id").requiredOption("--file <path>", "OpenAPI JSON or YAML file to upload").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runSpecSet(project, opts)));
|
|
7376
|
+
spec.command("carry").description("Copy a route's saved rules to the path a spec rename moved traffic to").argument("<project>", "Project name or id").requiredOption("--method <method>", "HTTP method of the route (GET, POST, ...)").requiredOption("--from <path>", "Old route path, e.g. /owners/{ownerId}").requiredOption("--to <path>", "New route path, e.g. /customers/{customerId}").option("--delete-source", "Also delete the old rules after carrying").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runSpecCarry(project, opts)));
|
|
7377
|
+
spec.command("delete-rule").description("Delete the saved rules for a route (e.g. an orphan left by a spec change)").argument("<project>", "Project name or id").requiredOption("--method <method>", "HTTP method of the route").requiredOption("--route <path>", "Route path, e.g. /owners/{ownerId}").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runSpecDeleteRule(project, opts)));
|
|
6928
7378
|
var HELP_GROUPS = [
|
|
6929
7379
|
{ title: "Chat", commands: ["apichat", "agent"] },
|
|
6930
7380
|
{ title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
|
|
6931
|
-
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
7381
|
+
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
6932
7382
|
{ title: "Data plane commands", commands: [
|
|
6933
7383
|
{ parent: "consumer", sub: "login" },
|
|
6934
7384
|
{ parent: "consumer", sub: "apikeys" }
|
|
@@ -6947,7 +7397,7 @@ function groupedCommandHelp() {
|
|
|
6947
7397
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
6948
7398
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
6949
7399
|
}).filter(Boolean).join("\n");
|
|
6950
|
-
return `${
|
|
7400
|
+
return `${import_chalk43.default.bold(g.title)}
|
|
6951
7401
|
${rows}`;
|
|
6952
7402
|
}).join("\n\n");
|
|
6953
7403
|
}
|
|
@@ -6967,7 +7417,7 @@ Tips:
|
|
|
6967
7417
|
\u2022 Run \`apiblaze <command> --help\` (e.g. \`apiblaze consumer --help\`) for sub-commands.
|
|
6968
7418
|
|
|
6969
7419
|
Examples:
|
|
6970
|
-
$ npx apiblaze apichat --
|
|
7420
|
+
$ npx apiblaze apichat --openapi https://pokeapi.co/openapi.yaml # chat with any API
|
|
6971
7421
|
$ npx apiblaze agent # just chat
|
|
6972
7422
|
$ npx apiblaze create --target https://api.example.com # one-line API
|
|
6973
7423
|
$ npx apiblaze dev 3000 # localhost \u2192 public URL
|
|
@@ -6981,14 +7431,14 @@ async function recoverStaleTeam() {
|
|
|
6981
7431
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
6982
7432
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
6983
7433
|
if (!linked) {
|
|
6984
|
-
console.error(
|
|
7434
|
+
console.error(import_chalk43.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
6985
7435
|
return;
|
|
6986
7436
|
}
|
|
6987
7437
|
if (linked.teamId === creds.teamId) return;
|
|
6988
7438
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
6989
7439
|
delete next.activeTenant;
|
|
6990
7440
|
saveCredentials(next);
|
|
6991
|
-
console.error(
|
|
7441
|
+
console.error(import_chalk43.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk43.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
6992
7442
|
} catch {
|
|
6993
7443
|
}
|
|
6994
7444
|
}
|
|
@@ -6996,16 +7446,16 @@ async function printError(err) {
|
|
|
6996
7446
|
if (err instanceof ApiError) {
|
|
6997
7447
|
const data = err.body;
|
|
6998
7448
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
6999
|
-
console.error(
|
|
7449
|
+
console.error(import_chalk43.default.red(`
|
|
7000
7450
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
7001
7451
|
if (err.status === 403 || err.status === 404) {
|
|
7002
7452
|
await recoverStaleTeam();
|
|
7003
7453
|
}
|
|
7004
7454
|
} else if (err instanceof Error) {
|
|
7005
|
-
console.error(
|
|
7455
|
+
console.error(import_chalk43.default.red(`
|
|
7006
7456
|
Error: ${err.message}`));
|
|
7007
7457
|
} else {
|
|
7008
|
-
console.error(
|
|
7458
|
+
console.error(import_chalk43.default.red("\nUnknown error"));
|
|
7009
7459
|
}
|
|
7010
7460
|
}
|
|
7011
7461
|
program.parse(process.argv);
|