apiblaze 0.19.26 → 0.19.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1597 -608
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -237,7 +237,7 @@ var init_api = __esm({
|
|
|
237
237
|
"use strict";
|
|
238
238
|
init_auth();
|
|
239
239
|
init_types();
|
|
240
|
-
DASHBOARD_BASE = "https://dashboard.apiblaze.com";
|
|
240
|
+
DASHBOARD_BASE = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
241
241
|
PUBLIC_API_BASE = "https://api.apiblaze.com";
|
|
242
242
|
}
|
|
243
243
|
});
|
|
@@ -272,14 +272,14 @@ async function resolveLinkedTeam(opts) {
|
|
|
272
272
|
return { teamId: picked2.teamId, teamName: picked2.name };
|
|
273
273
|
}
|
|
274
274
|
if (opts.preferredId) {
|
|
275
|
-
console.
|
|
275
|
+
console.error(import_chalk.default.yellow("\nYour previously linked team is no longer available."));
|
|
276
276
|
}
|
|
277
277
|
if (teams.length === 1) {
|
|
278
|
-
console.
|
|
278
|
+
console.error(`${import_chalk.default.cyan("\u2192")} Linking to your team ${import_chalk.default.bold(teams[0].name)}.`);
|
|
279
279
|
return { teamId: teams[0].teamId, teamName: teams[0].name };
|
|
280
280
|
}
|
|
281
281
|
if (!opts.interactive) {
|
|
282
|
-
console.
|
|
282
|
+
console.error(import_chalk.default.yellow(`Linking to "${teams[0].name}" \u2014 pass --team to choose another.`));
|
|
283
283
|
return { teamId: teams[0].teamId, teamName: teams[0].name };
|
|
284
284
|
}
|
|
285
285
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -392,24 +392,24 @@ function maskBody(body) {
|
|
|
392
392
|
}
|
|
393
393
|
function renderTrace() {
|
|
394
394
|
if (!verbose || entries.length === 0) return;
|
|
395
|
-
console.
|
|
396
|
-
console.
|
|
397
|
-
console.
|
|
395
|
+
console.error(import_chalk6.default.dim("\n" + "\u2500".repeat(64)));
|
|
396
|
+
console.error(import_chalk6.default.bold(`--verbose: ${entries.length} API call${entries.length === 1 ? "" : "s"} this command made`));
|
|
397
|
+
console.error(
|
|
398
398
|
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")
|
|
399
399
|
);
|
|
400
400
|
entries.forEach((e, i) => {
|
|
401
401
|
const n = entries.length > 1 ? import_chalk6.default.bold(`${i + 1}. `) : "";
|
|
402
|
-
if (e.summary) console.
|
|
402
|
+
if (e.summary) console.error(`${n}${import_chalk6.default.cyan(e.summary)}${e.status ? import_chalk6.default.dim(` (HTTP ${e.status})`) : ""}`);
|
|
403
403
|
const url = `https://api.apiblaze.com/${CONTROL_API_VERSION}/prod${e.path}`;
|
|
404
404
|
const masked = maskBody(e.body);
|
|
405
405
|
const hasBody = e.method !== "GET" && masked !== void 0;
|
|
406
|
-
console.
|
|
407
|
-
console.
|
|
406
|
+
console.error(import_chalk6.default.green(` curl -sS -X ${e.method} ${url}` + (hasBody ? " \\" : "")));
|
|
407
|
+
console.error(import_chalk6.default.green(' -H "X-API-Key: $APIBLAZE_CONTROLPLANE_APIKEY"' + (hasBody ? " \\" : "")));
|
|
408
408
|
if (hasBody) {
|
|
409
|
-
console.
|
|
410
|
-
console.
|
|
409
|
+
console.error(import_chalk6.default.green(" -H 'Content-Type: application/json' \\"));
|
|
410
|
+
console.error(import_chalk6.default.green(` -d '${masked}'`));
|
|
411
411
|
}
|
|
412
|
-
if (i < entries.length - 1) console.
|
|
412
|
+
if (i < entries.length - 1) console.error();
|
|
413
413
|
});
|
|
414
414
|
entries.length = 0;
|
|
415
415
|
}
|
|
@@ -458,7 +458,7 @@ function formatBilling(billing) {
|
|
|
458
458
|
}
|
|
459
459
|
function maybePrintBilling(data) {
|
|
460
460
|
const line = formatBilling(data?.billing);
|
|
461
|
-
if (line) console.
|
|
461
|
+
if (line) console.error(import_chalk7.default.magenta(` ${line}`));
|
|
462
462
|
}
|
|
463
463
|
var import_chalk7, DASHBOARD_BASE3;
|
|
464
464
|
var init_admin = __esm({
|
|
@@ -476,7 +476,7 @@ var init_admin = __esm({
|
|
|
476
476
|
function requireAuth() {
|
|
477
477
|
const creds = loadCredentials();
|
|
478
478
|
if (!creds) {
|
|
479
|
-
console.error(
|
|
479
|
+
console.error(import_chalk9.default.red("Not logged in. Run `apiblaze login` first."));
|
|
480
480
|
process.exit(1);
|
|
481
481
|
}
|
|
482
482
|
return creds;
|
|
@@ -485,7 +485,7 @@ async function resolveTeam(opt) {
|
|
|
485
485
|
const creds = requireAuth();
|
|
486
486
|
if (!opt) {
|
|
487
487
|
if (!creds.teamId) {
|
|
488
|
-
console.error(
|
|
488
|
+
console.error(import_chalk9.default.red("No active team. Run `apiblaze login` or pass --team."));
|
|
489
489
|
process.exit(1);
|
|
490
490
|
}
|
|
491
491
|
return { teamId: creds.teamId, teamName: creds.teamName };
|
|
@@ -494,7 +494,7 @@ async function resolveTeam(opt) {
|
|
|
494
494
|
const teams = await getTeams().catch(() => []);
|
|
495
495
|
const match = teams.find((t) => t.name === opt || t.teamId === opt);
|
|
496
496
|
if (!match) {
|
|
497
|
-
console.error(
|
|
497
|
+
console.error(import_chalk9.default.red(`Team "${opt}" not found.${teams.length ? " Available: " + teams.map((t) => t.name).join(", ") : ""}`));
|
|
498
498
|
process.exit(1);
|
|
499
499
|
}
|
|
500
500
|
return { teamId: match.teamId, teamName: match.name };
|
|
@@ -505,13 +505,13 @@ async function resolveProject(teamId, nameOrId, version2) {
|
|
|
505
505
|
(p) => p.projectId === nameOrId || p.projectName === nameOrId
|
|
506
506
|
);
|
|
507
507
|
if (candidates.length === 0) {
|
|
508
|
-
console.error(
|
|
509
|
-
if (projects.length) console.error(
|
|
508
|
+
console.error(import_chalk9.default.red(`Project "${nameOrId}" not found in this team.`));
|
|
509
|
+
if (projects.length) console.error(import_chalk9.default.dim(" Known: " + projects.map((p) => p.projectName).join(", ")));
|
|
510
510
|
process.exit(1);
|
|
511
511
|
}
|
|
512
512
|
const chosen = version2 ? candidates.find((p) => p.apiVersion === version2) : candidates[0];
|
|
513
513
|
if (!chosen) {
|
|
514
|
-
console.error(
|
|
514
|
+
console.error(import_chalk9.default.red(`Project "${nameOrId}" has no version ${version2}. Versions: ${candidates.map((p) => p.apiVersion).join(", ")}`));
|
|
515
515
|
process.exit(1);
|
|
516
516
|
}
|
|
517
517
|
return {
|
|
@@ -522,11 +522,11 @@ async function resolveProject(teamId, nameOrId, version2) {
|
|
|
522
522
|
tenant: chosen.tenant
|
|
523
523
|
};
|
|
524
524
|
}
|
|
525
|
-
var
|
|
525
|
+
var import_chalk9;
|
|
526
526
|
var init_resolve = __esm({
|
|
527
527
|
"src/lib/resolve.ts"() {
|
|
528
528
|
"use strict";
|
|
529
|
-
|
|
529
|
+
import_chalk9 = __toESM(require("chalk"));
|
|
530
530
|
init_auth();
|
|
531
531
|
init_api();
|
|
532
532
|
}
|
|
@@ -635,12 +635,12 @@ async function runIamToggle(project, state, opts) {
|
|
|
635
635
|
summary: `IAM ${state} for tenant ${tenant2}`
|
|
636
636
|
});
|
|
637
637
|
if (opts.quiet) return;
|
|
638
|
-
spinner?.succeed(`IAM is ${
|
|
638
|
+
spinner?.succeed(`IAM is ${import_chalk10.default.bold(state)} for tenant ${tenant2}.`);
|
|
639
639
|
if (opts.json) console.log(JSON.stringify(out ?? { iam_enabled: enabled }));
|
|
640
640
|
else if (enabled) {
|
|
641
|
-
console.log(
|
|
642
|
-
console.log(
|
|
643
|
-
console.log(
|
|
641
|
+
console.log(import_chalk10.default.dim(" Groups now apply to identified calls. Identify callers with"));
|
|
642
|
+
console.log(import_chalk10.default.dim(" X-End-User-Id, a login token, or per-user keys \u2014 and consider"));
|
|
643
|
+
console.log(import_chalk10.default.dim(` \`apiblaze identified ${project} require\` to reject unattributed traffic.`));
|
|
644
644
|
}
|
|
645
645
|
} catch (err) {
|
|
646
646
|
spinner?.fail(`IAM ${state} failed.`);
|
|
@@ -692,11 +692,11 @@ async function runIdentifiedToggle(project, mode, opts) {
|
|
|
692
692
|
throw err;
|
|
693
693
|
}
|
|
694
694
|
}
|
|
695
|
-
var
|
|
695
|
+
var import_chalk10, import_ora4;
|
|
696
696
|
var init_iam = __esm({
|
|
697
697
|
"src/commands/iam.ts"() {
|
|
698
698
|
"use strict";
|
|
699
|
-
|
|
699
|
+
import_chalk10 = __toESM(require("chalk"));
|
|
700
700
|
import_ora4 = __toESM(require("ora"));
|
|
701
701
|
init_caller();
|
|
702
702
|
}
|
|
@@ -714,7 +714,7 @@ async function createTenantRow(teamId) {
|
|
|
714
714
|
const { name } = await inquirer3.prompt([{
|
|
715
715
|
type: "input",
|
|
716
716
|
name: "name",
|
|
717
|
-
message: `Tenant name ${
|
|
717
|
+
message: `Tenant name ${import_chalk30.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
|
|
718
718
|
validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
|
|
719
719
|
}]);
|
|
720
720
|
const slug = name.trim();
|
|
@@ -729,7 +729,7 @@ async function createTenantRow(teamId) {
|
|
|
729
729
|
} catch (err) {
|
|
730
730
|
const msg = err instanceof Error ? err.message : String(err);
|
|
731
731
|
if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
|
|
732
|
-
console.log(
|
|
732
|
+
console.log(import_chalk30.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
|
|
733
733
|
continue;
|
|
734
734
|
}
|
|
735
735
|
throw err;
|
|
@@ -746,11 +746,11 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
746
746
|
pageSize: 10,
|
|
747
747
|
choices: [
|
|
748
748
|
...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
|
|
749
|
-
...opts.allowSkip ? [new inquirer3.Separator(), { name:
|
|
749
|
+
...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk30.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
|
|
750
750
|
]
|
|
751
751
|
}]);
|
|
752
752
|
if (!provider) {
|
|
753
|
-
console.log(
|
|
753
|
+
console.log(import_chalk30.default.dim(" No login method yet \u2014 add one later under Login methods."));
|
|
754
754
|
return false;
|
|
755
755
|
}
|
|
756
756
|
const opt = PROVIDERS.find((p) => p.id === provider);
|
|
@@ -796,26 +796,26 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
796
796
|
async function createTenantInteractive(teamId) {
|
|
797
797
|
const { default: inquirer3 } = await import("inquirer");
|
|
798
798
|
const tenant2 = await createTenantRow(teamId);
|
|
799
|
-
console.log(
|
|
799
|
+
console.log(import_chalk30.default.green(` Tenant ${import_chalk30.default.bold(tenant2)} created.`));
|
|
800
800
|
const { users } = await inquirer3.prompt([{
|
|
801
801
|
type: "confirm",
|
|
802
802
|
name: "users",
|
|
803
803
|
default: false,
|
|
804
|
-
message: `Enable Users & groups? ${
|
|
804
|
+
message: `Enable Users & groups? ${import_chalk30.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
|
|
805
805
|
}]);
|
|
806
806
|
if (users) {
|
|
807
807
|
await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
|
|
808
808
|
});
|
|
809
|
-
console.log(
|
|
809
|
+
console.log(import_chalk30.default.green(" Users & groups enabled."));
|
|
810
810
|
}
|
|
811
811
|
await addApiblazeHostedLogin(teamId, tenant2, { allowSkip: true });
|
|
812
812
|
return tenant2;
|
|
813
813
|
}
|
|
814
|
-
var
|
|
814
|
+
var import_chalk30, import_ora10, PROVIDERS, DEFAULT_SCOPES;
|
|
815
815
|
var init_tenant_create = __esm({
|
|
816
816
|
"src/lib/tenant-create.ts"() {
|
|
817
817
|
"use strict";
|
|
818
|
-
|
|
818
|
+
import_chalk30 = __toESM(require("chalk"));
|
|
819
819
|
import_ora10 = __toESM(require("ora"));
|
|
820
820
|
init_admin();
|
|
821
821
|
PROVIDERS = [
|
|
@@ -841,7 +841,7 @@ var tenant_pick_exports = {};
|
|
|
841
841
|
__export(tenant_pick_exports, {
|
|
842
842
|
pickTenant: () => pickTenant
|
|
843
843
|
});
|
|
844
|
-
async function
|
|
844
|
+
async function fetchPage2(teamId, q) {
|
|
845
845
|
const out = await admin({
|
|
846
846
|
method: "GET",
|
|
847
847
|
path: `/teams/${encodeURIComponent(teamId)}/tenants?detail=1&limit=${PAGE}${q ? `&q=${encodeURIComponent(q)}` : ""}`,
|
|
@@ -854,10 +854,10 @@ async function fetchPage(teamId, q) {
|
|
|
854
854
|
}
|
|
855
855
|
function label(t, defaultTenant, active) {
|
|
856
856
|
const tags = [
|
|
857
|
-
t.tenant_name === active ?
|
|
858
|
-
t.tenant_name === defaultTenant ?
|
|
857
|
+
t.tenant_name === active ? import_chalk31.default.cyan("active scope") : "",
|
|
858
|
+
t.tenant_name === defaultTenant ? import_chalk31.default.dim("team default") : ""
|
|
859
859
|
].filter(Boolean).join(", ");
|
|
860
|
-
const disp = t.display_name && t.display_name !== t.tenant_name ?
|
|
860
|
+
const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk31.default.dim(` ${t.display_name}`) : "";
|
|
861
861
|
return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
|
|
862
862
|
}
|
|
863
863
|
async function pickTenant(teamId, opts = {}) {
|
|
@@ -866,13 +866,13 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
866
866
|
let q = opts.initialQuery ?? "";
|
|
867
867
|
for (; ; ) {
|
|
868
868
|
const spinner = (0, import_ora11.default)(q ? `Searching tenants for "${q}"...` : "Loading tenants...").start();
|
|
869
|
-
const page = await
|
|
869
|
+
const page = await fetchPage2(teamId, q).finally(() => spinner.stop());
|
|
870
870
|
if (!page.total && !q) {
|
|
871
871
|
if (opts.allowCreate) {
|
|
872
872
|
const { make } = await inquirer3.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
|
|
873
873
|
if (make) return await createTenantInline(teamId);
|
|
874
874
|
}
|
|
875
|
-
console.error(
|
|
875
|
+
console.error(import_chalk31.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
876
876
|
return null;
|
|
877
877
|
}
|
|
878
878
|
const truncated = page.total > page.rows.length;
|
|
@@ -881,7 +881,7 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
881
881
|
value: t.tenant_name
|
|
882
882
|
}));
|
|
883
883
|
if (truncated || q) {
|
|
884
|
-
choices.push(new inquirer3.Separator(
|
|
884
|
+
choices.push(new inquirer3.Separator(import_chalk31.default.dim(
|
|
885
885
|
truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
|
|
886
886
|
)));
|
|
887
887
|
choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
|
|
@@ -915,11 +915,11 @@ async function createTenantInline(teamId) {
|
|
|
915
915
|
const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
|
|
916
916
|
return createTenantInteractive2(teamId);
|
|
917
917
|
}
|
|
918
|
-
var
|
|
918
|
+
var import_chalk31, import_ora11, PAGE;
|
|
919
919
|
var init_tenant_pick = __esm({
|
|
920
920
|
"src/lib/tenant-pick.ts"() {
|
|
921
921
|
"use strict";
|
|
922
|
-
|
|
922
|
+
import_chalk31 = __toESM(require("chalk"));
|
|
923
923
|
import_ora11 = __toESM(require("ora"));
|
|
924
924
|
init_admin();
|
|
925
925
|
init_auth();
|
|
@@ -929,10 +929,10 @@ var init_tenant_pick = __esm({
|
|
|
929
929
|
|
|
930
930
|
// src/index.ts
|
|
931
931
|
var import_commander = require("commander");
|
|
932
|
-
var
|
|
932
|
+
var import_chalk48 = __toESM(require("chalk"));
|
|
933
933
|
|
|
934
934
|
// package.json
|
|
935
|
-
var version = "0.19.
|
|
935
|
+
var version = "0.19.29";
|
|
936
936
|
|
|
937
937
|
// src/index.ts
|
|
938
938
|
init_types();
|
|
@@ -942,7 +942,7 @@ var import_chalk2 = __toESM(require("chalk"));
|
|
|
942
942
|
var import_ora = __toESM(require("ora"));
|
|
943
943
|
init_auth();
|
|
944
944
|
init_team();
|
|
945
|
-
var DASHBOARD_BASE2 = "https://dashboard.apiblaze.com";
|
|
945
|
+
var DASHBOARD_BASE2 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
946
946
|
function openBrowser(url) {
|
|
947
947
|
const { exec } = require("child_process");
|
|
948
948
|
const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
@@ -1936,8 +1936,79 @@ ${projects.length} project${projects.length === 1 ? "" : "s"}`));
|
|
|
1936
1936
|
|
|
1937
1937
|
// src/commands/create.ts
|
|
1938
1938
|
var import_fs2 = __toESM(require("fs"));
|
|
1939
|
+
|
|
1940
|
+
// src/lib/tenant-choice.ts
|
|
1941
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
1942
|
+
init_admin();
|
|
1943
|
+
function decideTenant(input) {
|
|
1944
|
+
if (input.tenant) {
|
|
1945
|
+
const slug = input.tenant.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1946
|
+
return slug ? { kind: "use", tenant: slug } : { kind: "server-default" };
|
|
1947
|
+
}
|
|
1948
|
+
if (input.existing.length === 0) return { kind: "server-default" };
|
|
1949
|
+
const suggested = suggestTenantName(input.projectSlug, input.existing);
|
|
1950
|
+
if (input.existing.length === 1) return { kind: "use", tenant: input.existing[0] };
|
|
1951
|
+
if (!input.interactive) return { kind: "use", tenant: suggested };
|
|
1952
|
+
return { kind: "ask", existing: input.existing, suggested };
|
|
1953
|
+
}
|
|
1954
|
+
async function chooseTenantForCreate(teamId, projectSlug, opts) {
|
|
1955
|
+
if (opts.tenant) return opts.tenant.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1956
|
+
if (!teamId) return void 0;
|
|
1957
|
+
let existing = [];
|
|
1958
|
+
try {
|
|
1959
|
+
const rows = await admin({
|
|
1960
|
+
method: "GET",
|
|
1961
|
+
path: `/teams/${encodeURIComponent(teamId)}/tenants`,
|
|
1962
|
+
summary: "List tenants to place the new proxy"
|
|
1963
|
+
});
|
|
1964
|
+
const list = Array.isArray(rows) ? rows : rows?.tenants ?? [];
|
|
1965
|
+
existing = list.map((t) => t?.tenant_name ?? t?.name).filter((n) => typeof n === "string" && n.length > 0);
|
|
1966
|
+
} catch {
|
|
1967
|
+
return void 0;
|
|
1968
|
+
}
|
|
1969
|
+
const decision = decideTenant({
|
|
1970
|
+
existing,
|
|
1971
|
+
projectSlug,
|
|
1972
|
+
tenant: opts.tenant,
|
|
1973
|
+
yes: opts.yes,
|
|
1974
|
+
json: opts.json,
|
|
1975
|
+
interactive: !opts.yes && !opts.json && !!process.stdin.isTTY
|
|
1976
|
+
});
|
|
1977
|
+
if (decision.kind === "server-default") return void 0;
|
|
1978
|
+
if (decision.kind === "use") {
|
|
1979
|
+
if (!opts.json && !existing.includes(decision.tenant)) {
|
|
1980
|
+
console.log(import_chalk8.default.dim(` Placing this proxy in a new tenant "${decision.tenant}" (pass --tenant to choose another).`));
|
|
1981
|
+
}
|
|
1982
|
+
return decision.tenant;
|
|
1983
|
+
}
|
|
1984
|
+
const { existing: choices, suggested } = decision;
|
|
1985
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
1986
|
+
const { pick: pick2 } = await inquirer3.prompt([
|
|
1987
|
+
{
|
|
1988
|
+
type: "list",
|
|
1989
|
+
name: "pick",
|
|
1990
|
+
message: "Which tenant should this proxy's consumers live in?",
|
|
1991
|
+
choices: [
|
|
1992
|
+
{ name: `${import_chalk8.default.green("\uFF0B")} New tenant "${suggested}" ${import_chalk8.default.dim("(its own portal, login and users)")}`, value: "\0new" },
|
|
1993
|
+
...choices.map((t) => ({ name: `${t} ${import_chalk8.default.dim("(share its portal, login app and users)")}`, value: t }))
|
|
1994
|
+
]
|
|
1995
|
+
}
|
|
1996
|
+
]);
|
|
1997
|
+
return pick2 === "\0new" ? suggested : pick2;
|
|
1998
|
+
}
|
|
1999
|
+
function suggestTenantName(projectSlug, taken) {
|
|
2000
|
+
const base2 = (projectSlug || "tenant").toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 24) || "tenant";
|
|
2001
|
+
if (!taken.includes(base2)) return base2;
|
|
2002
|
+
for (let i = 2; i < 100; i++) {
|
|
2003
|
+
const candidate = `${base2}${i}`;
|
|
2004
|
+
if (!taken.includes(candidate)) return candidate;
|
|
2005
|
+
}
|
|
2006
|
+
return `${base2}${Math.random().toString(36).slice(2, 7)}`;
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
// src/commands/create.ts
|
|
1939
2010
|
var import_yaml = require("yaml");
|
|
1940
|
-
var
|
|
2011
|
+
var import_chalk11 = __toESM(require("chalk"));
|
|
1941
2012
|
var import_ora5 = __toESM(require("ora"));
|
|
1942
2013
|
init_auth();
|
|
1943
2014
|
init_api();
|
|
@@ -2000,7 +2071,7 @@ function stripTenantFromPortal(devPortal) {
|
|
|
2000
2071
|
}
|
|
2001
2072
|
}
|
|
2002
2073
|
function fail(message) {
|
|
2003
|
-
console.error(
|
|
2074
|
+
console.error(import_chalk11.default.red(`Error: ${message}`));
|
|
2004
2075
|
process.exit(1);
|
|
2005
2076
|
}
|
|
2006
2077
|
function buildTryItCurl(url, authType, apiKey) {
|
|
@@ -2015,11 +2086,11 @@ function printCurlExample(url, authType, apiKey, devPortal) {
|
|
|
2015
2086
|
const curl = buildTryItCurl(url, authType, apiKey);
|
|
2016
2087
|
console.log();
|
|
2017
2088
|
if (curl) {
|
|
2018
|
-
console.log(` ${
|
|
2019
|
-
console.log(` ${
|
|
2089
|
+
console.log(` ${import_chalk11.default.dim("Try it \u2014 copy/paste:")}`);
|
|
2090
|
+
console.log(` ${import_chalk11.default.cyan(curl)}`);
|
|
2020
2091
|
} else if (authType === "oauth") {
|
|
2021
|
-
console.log(` ${
|
|
2022
|
-
console.log(` ${
|
|
2092
|
+
console.log(` ${import_chalk11.default.dim("Try it:")} this proxy uses OAuth \u2014 sign in at ${import_chalk11.default.bold(devPortal ?? "the dev portal")} to get a token,`);
|
|
2093
|
+
console.log(` ${import_chalk11.default.dim(`then call ${url} with`)} ${import_chalk11.default.cyan('-H "Authorization: Bearer <token>"')}`);
|
|
2023
2094
|
}
|
|
2024
2095
|
}
|
|
2025
2096
|
var VALID_AUTH = ["api_key", "none", "oauth"];
|
|
@@ -2049,7 +2120,7 @@ async function runCreate(opts = {}) {
|
|
|
2049
2120
|
teamId = match.teamId;
|
|
2050
2121
|
}
|
|
2051
2122
|
}
|
|
2052
|
-
if (!opts.json) console.log(
|
|
2123
|
+
if (!opts.json) console.log(import_chalk11.default.bold("\nCreate an API proxy\n"));
|
|
2053
2124
|
let name = "";
|
|
2054
2125
|
if (opts.name !== void 0) {
|
|
2055
2126
|
name = normalizeName(opts.name);
|
|
@@ -2069,7 +2140,7 @@ async function runCreate(opts = {}) {
|
|
|
2069
2140
|
}]);
|
|
2070
2141
|
name = normalizeName(rawName);
|
|
2071
2142
|
if (name.length < 3) {
|
|
2072
|
-
console.log(
|
|
2143
|
+
console.log(import_chalk11.default.yellow(" Name must be at least 3 characters (letters and digits only).\n"));
|
|
2073
2144
|
continue;
|
|
2074
2145
|
}
|
|
2075
2146
|
const spinner2 = (0, import_ora5.default)("Checking availability...").start();
|
|
@@ -2077,15 +2148,15 @@ async function runCreate(opts = {}) {
|
|
|
2077
2148
|
const check = await checkProxyName(name, teamId, opts.apiversion);
|
|
2078
2149
|
spinner2.stop();
|
|
2079
2150
|
if (!check.canUseProjectName || !check.canUseApiVersion) {
|
|
2080
|
-
console.log(
|
|
2151
|
+
console.log(import_chalk11.default.yellow(` "${name}" is not available${check.message ? ` \u2014 ${check.message}` : ""}. Try another.
|
|
2081
2152
|
`));
|
|
2082
2153
|
continue;
|
|
2083
2154
|
}
|
|
2084
2155
|
} catch {
|
|
2085
2156
|
spinner2.stop();
|
|
2086
|
-
console.log(
|
|
2157
|
+
console.log(import_chalk11.default.dim(" (could not verify availability; continuing)"));
|
|
2087
2158
|
}
|
|
2088
|
-
console.log(`${
|
|
2159
|
+
console.log(`${import_chalk11.default.cyan("\u2192")} Your API will live at ${import_chalk11.default.bold(`https://${name}.abz.run`)}
|
|
2089
2160
|
`);
|
|
2090
2161
|
break;
|
|
2091
2162
|
}
|
|
@@ -2111,7 +2182,7 @@ async function runCreate(opts = {}) {
|
|
|
2111
2182
|
message: "Target URL to forward requests to (e.g. https://httpbin.org):"
|
|
2112
2183
|
}]);
|
|
2113
2184
|
if (!isHttpUrl(url)) {
|
|
2114
|
-
console.log(
|
|
2185
|
+
console.log(import_chalk11.default.yellow(" Enter a valid http(s) URL.\n"));
|
|
2115
2186
|
continue;
|
|
2116
2187
|
}
|
|
2117
2188
|
targetUrl = url.trim();
|
|
@@ -2122,7 +2193,7 @@ async function runCreate(opts = {}) {
|
|
|
2122
2193
|
}
|
|
2123
2194
|
if (interactive && !opts.yes) {
|
|
2124
2195
|
const { default: inquirer3 } = await import("inquirer");
|
|
2125
|
-
console.log(`${
|
|
2196
|
+
console.log(`${import_chalk11.default.cyan("\u2192")} Auth: ${import_chalk11.default.bold(auth)}${auth === "api_key" ? " \u2014 consumers send an X-API-Key header" : ""}`);
|
|
2126
2197
|
const { ok } = await inquirer3.prompt([{
|
|
2127
2198
|
type: "confirm",
|
|
2128
2199
|
name: "ok",
|
|
@@ -2130,10 +2201,15 @@ async function runCreate(opts = {}) {
|
|
|
2130
2201
|
default: true
|
|
2131
2202
|
}]);
|
|
2132
2203
|
if (!ok) {
|
|
2133
|
-
console.log(
|
|
2204
|
+
console.log(import_chalk11.default.yellow("Cancelled."));
|
|
2134
2205
|
return;
|
|
2135
2206
|
}
|
|
2136
2207
|
}
|
|
2208
|
+
const chosenTenant = await chooseTenantForCreate(teamId, name, {
|
|
2209
|
+
tenant: opts.tenant,
|
|
2210
|
+
yes: opts.yes,
|
|
2211
|
+
json: opts.json
|
|
2212
|
+
});
|
|
2137
2213
|
const spinner = !opts.json ? (0, import_ora5.default)("Creating proxy (tenant, keys, dev portal)...").start() : null;
|
|
2138
2214
|
let result;
|
|
2139
2215
|
try {
|
|
@@ -2142,10 +2218,10 @@ async function runCreate(opts = {}) {
|
|
|
2142
2218
|
...openapiContent ? { openapi: openapiContent } : { target_url: targetUrl },
|
|
2143
2219
|
auth_type: auth,
|
|
2144
2220
|
team_id: teamId,
|
|
2145
|
-
...
|
|
2221
|
+
...chosenTenant ? { tenant: chosenTenant } : {},
|
|
2146
2222
|
...opts.apiversion ? { api_version: opts.apiversion } : {}
|
|
2147
2223
|
});
|
|
2148
|
-
spinner?.succeed(
|
|
2224
|
+
spinner?.succeed(import_chalk11.default.green("Proxy created!"));
|
|
2149
2225
|
} catch (err) {
|
|
2150
2226
|
spinner?.fail("Failed to create proxy.");
|
|
2151
2227
|
throw err;
|
|
@@ -2170,16 +2246,16 @@ async function runCreate(opts = {}) {
|
|
|
2170
2246
|
return;
|
|
2171
2247
|
}
|
|
2172
2248
|
console.log();
|
|
2173
|
-
console.log(` ${
|
|
2174
|
-
if (devPortal) console.log(` ${
|
|
2249
|
+
console.log(` ${import_chalk11.default.dim("Proxy URL: ")} ${import_chalk11.default.bold(proxyUrl)}`);
|
|
2250
|
+
if (devPortal) console.log(` ${import_chalk11.default.dim("Dev portal:")} ${import_chalk11.default.bold(devPortal)}`);
|
|
2175
2251
|
if (adminKey) {
|
|
2176
2252
|
console.log();
|
|
2177
|
-
console.log(` ${
|
|
2178
|
-
console.log(` ${
|
|
2179
|
-
console.log(
|
|
2253
|
+
console.log(` ${import_chalk11.default.dim("Consumer admin API key (dev):")}`);
|
|
2254
|
+
console.log(` ${import_chalk11.default.bold.green(adminKey)}`);
|
|
2255
|
+
console.log(import_chalk11.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
|
|
2180
2256
|
const otherEnvs = Object.keys(keys).filter((e) => e !== "dev");
|
|
2181
2257
|
if (otherEnvs.length) {
|
|
2182
|
-
console.log(
|
|
2258
|
+
console.log(import_chalk11.default.dim(` (Separate keys were also created for: ${otherEnvs.join(", ")}.)`));
|
|
2183
2259
|
}
|
|
2184
2260
|
}
|
|
2185
2261
|
printCurlExample(proxyUrl, auth, adminKey, devPortal);
|
|
@@ -2195,8 +2271,8 @@ async function applyCreateToggles(name, opts) {
|
|
|
2195
2271
|
async function runAnonymousCreate(opts) {
|
|
2196
2272
|
const interactive = !!process.stdin.isTTY && !opts.json;
|
|
2197
2273
|
if (!opts.json) {
|
|
2198
|
-
console.log(
|
|
2199
|
-
console.log(
|
|
2274
|
+
console.log(import_chalk11.default.bold("\nCreate an API proxy"));
|
|
2275
|
+
console.log(import_chalk11.default.dim("Not logged in \u2014 creating an anonymous proxy. You can claim it to your account within 30 days.\n"));
|
|
2200
2276
|
}
|
|
2201
2277
|
let body = {};
|
|
2202
2278
|
if (opts.config) {
|
|
@@ -2245,7 +2321,7 @@ async function runAnonymousCreate(opts) {
|
|
|
2245
2321
|
message: "Target URL to forward requests to (e.g. https://httpbin.org):"
|
|
2246
2322
|
}]);
|
|
2247
2323
|
if (!isHttpUrl(url)) {
|
|
2248
|
-
console.log(
|
|
2324
|
+
console.log(import_chalk11.default.yellow(" Enter a valid http(s) URL.\n"));
|
|
2249
2325
|
continue;
|
|
2250
2326
|
}
|
|
2251
2327
|
target = url.trim();
|
|
@@ -2286,7 +2362,7 @@ async function runAnonymousCreate(opts) {
|
|
|
2286
2362
|
saveAnonCred2(result.cp_key, result.team_id, result.claim_code);
|
|
2287
2363
|
}
|
|
2288
2364
|
}
|
|
2289
|
-
spinner?.succeed(
|
|
2365
|
+
spinner?.succeed(import_chalk11.default.green("Proxy created!"));
|
|
2290
2366
|
} catch (err) {
|
|
2291
2367
|
spinner?.fail("Failed to create proxy.");
|
|
2292
2368
|
throw err;
|
|
@@ -2309,36 +2385,36 @@ async function runAnonymousCreate(opts) {
|
|
|
2309
2385
|
return;
|
|
2310
2386
|
}
|
|
2311
2387
|
console.log();
|
|
2312
|
-
if (prodEndpoint) console.log(` ${
|
|
2313
|
-
if (result.portal) console.log(` ${
|
|
2388
|
+
if (prodEndpoint) console.log(` ${import_chalk11.default.dim("Proxy URL: ")} ${import_chalk11.default.bold(prodEndpoint)}`);
|
|
2389
|
+
if (result.portal) console.log(` ${import_chalk11.default.dim("Dev portal:")} ${import_chalk11.default.bold(result.portal)}`);
|
|
2314
2390
|
if (apiKey) {
|
|
2315
2391
|
console.log();
|
|
2316
|
-
console.log(` ${
|
|
2317
|
-
console.log(` ${
|
|
2318
|
-
console.log(
|
|
2392
|
+
console.log(` ${import_chalk11.default.dim("API key:")}`);
|
|
2393
|
+
console.log(` ${import_chalk11.default.bold.green(apiKey)}`);
|
|
2394
|
+
console.log(import_chalk11.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
|
|
2319
2395
|
}
|
|
2320
2396
|
if (prodEndpoint) printCurlExample(prodEndpoint, opts.auth || "api_key", apiKey, result.portal);
|
|
2321
2397
|
const claimCode = result.claim_code || cred?.claim_code;
|
|
2322
2398
|
if (claimCode) {
|
|
2323
2399
|
console.log();
|
|
2324
|
-
console.log(` ${
|
|
2325
|
-
console.log(` ${
|
|
2400
|
+
console.log(` ${import_chalk11.default.yellow("\u26A0 Anonymous \u2014 claim within 30 days or it expires.")} To claim these proxies:`);
|
|
2401
|
+
console.log(` ${import_chalk11.default.cyan(`npx apiblaze login && npx apiblaze claim ${claimCode}`)}`);
|
|
2326
2402
|
} else if (result.claim_url) {
|
|
2327
2403
|
console.log();
|
|
2328
|
-
console.log(` ${
|
|
2329
|
-
console.log(` ${
|
|
2404
|
+
console.log(` ${import_chalk11.default.yellow("\u26A0 Anonymous proxy \u2014 claim it to your account within 30 days or it expires:")}`);
|
|
2405
|
+
console.log(` ${import_chalk11.default.bold(result.claim_url)}`);
|
|
2330
2406
|
}
|
|
2331
2407
|
console.log();
|
|
2332
2408
|
}
|
|
2333
2409
|
|
|
2334
2410
|
// src/commands/claim.ts
|
|
2335
|
-
var
|
|
2411
|
+
var import_chalk12 = __toESM(require("chalk"));
|
|
2336
2412
|
init_auth();
|
|
2337
2413
|
init_anon_cred();
|
|
2338
2414
|
init_api();
|
|
2339
2415
|
init_types();
|
|
2340
2416
|
function fail2(message) {
|
|
2341
|
-
console.error(
|
|
2417
|
+
console.error(import_chalk12.default.red(`Error: ${message}`));
|
|
2342
2418
|
process.exit(1);
|
|
2343
2419
|
}
|
|
2344
2420
|
async function runClaim(claimCodeArg, opts) {
|
|
@@ -2374,9 +2450,9 @@ async function runClaim(claimCodeArg, opts) {
|
|
|
2374
2450
|
const c = typeof err.body === "object" && err.body ? err.body.error : void 0;
|
|
2375
2451
|
if (c === "already_claimed" || c === "not_claimable") {
|
|
2376
2452
|
if (fromLocal) clearAnonCred();
|
|
2377
|
-
console.log(
|
|
2378
|
-
if (fromLocal) console.log(
|
|
2379
|
-
console.log(
|
|
2453
|
+
console.log(import_chalk12.default.yellow("\nThis workspace was already claimed (e.g. on dashboard.apiblaze.com)."));
|
|
2454
|
+
if (fromLocal) console.log(import_chalk12.default.dim(" Cleared the local anonymous key."));
|
|
2455
|
+
console.log(import_chalk12.default.dim(" Your proxies are safe under your account \u2014 manage them with `apiblaze login`."));
|
|
2380
2456
|
return;
|
|
2381
2457
|
}
|
|
2382
2458
|
if (c === "expired") {
|
|
@@ -2394,20 +2470,20 @@ async function runClaim(claimCodeArg, opts) {
|
|
|
2394
2470
|
console.log(JSON.stringify(res, null, 2));
|
|
2395
2471
|
return;
|
|
2396
2472
|
}
|
|
2397
|
-
console.log(
|
|
2473
|
+
console.log(import_chalk12.default.green("\n\u2714 Claimed your anonymous workspace into your account."));
|
|
2398
2474
|
if (res.merged) {
|
|
2399
|
-
console.log(` Merged ${
|
|
2475
|
+
console.log(` Merged ${import_chalk12.default.bold(String(res.proxy_count ?? ""))} prox${res.proxy_count === 1 ? "y" : "ies"} into team ${import_chalk12.default.bold(res.team_id)}.`);
|
|
2400
2476
|
} else {
|
|
2401
2477
|
const label3 = res.team_name ? `\u201C${res.team_name}\u201D` : res.team_id;
|
|
2402
|
-
console.log(` It's now your team ${
|
|
2403
|
-
console.log(
|
|
2478
|
+
console.log(` It's now your team ${import_chalk12.default.bold(label3)}. Switch to it with ${import_chalk12.default.cyan(`apiblaze team ${res.team_id}`)}.`);
|
|
2479
|
+
console.log(import_chalk12.default.dim(` (Tip: name it with --name, or --team <name> to merge into an existing team instead.)`));
|
|
2404
2480
|
}
|
|
2405
2481
|
console.log(`
|
|
2406
|
-
Manage it at ${
|
|
2482
|
+
Manage it at ${import_chalk12.default.cyan("https://dashboard.apiblaze.com/dashboard")}`);
|
|
2407
2483
|
}
|
|
2408
2484
|
|
|
2409
2485
|
// src/commands/logout.ts
|
|
2410
|
-
var
|
|
2486
|
+
var import_chalk13 = __toESM(require("chalk"));
|
|
2411
2487
|
init_auth();
|
|
2412
2488
|
|
|
2413
2489
|
// src/lib/consumer-auth.ts
|
|
@@ -2539,7 +2615,7 @@ async function runLogout(opts = {}) {
|
|
|
2539
2615
|
const creds = loadCredentials();
|
|
2540
2616
|
const consumer2 = loadConsumer();
|
|
2541
2617
|
if (!creds && !consumer2) {
|
|
2542
|
-
console.log(
|
|
2618
|
+
console.log(import_chalk13.default.yellow("You were not logged in."));
|
|
2543
2619
|
return;
|
|
2544
2620
|
}
|
|
2545
2621
|
let target;
|
|
@@ -2566,16 +2642,16 @@ async function runLogout(opts = {}) {
|
|
|
2566
2642
|
}
|
|
2567
2643
|
if (target === "producer" || target === "all") {
|
|
2568
2644
|
clearCredentials();
|
|
2569
|
-
console.log(
|
|
2645
|
+
console.log(import_chalk13.default.green(`\u2714 Logged out of API Producer${creds?.githubHandle ? ` (${creds.githubHandle})` : ""}.`));
|
|
2570
2646
|
}
|
|
2571
2647
|
if (target === "consumer" || target === "all") {
|
|
2572
2648
|
clearConsumer();
|
|
2573
|
-
console.log(
|
|
2649
|
+
console.log(import_chalk13.default.green(`\u2714 Logged out of API Consumer${consumer2?.tenant ? ` (${consumer2.tenant})` : ""}.`));
|
|
2574
2650
|
}
|
|
2575
2651
|
}
|
|
2576
2652
|
|
|
2577
2653
|
// src/commands/flush.ts
|
|
2578
|
-
var
|
|
2654
|
+
var import_chalk14 = __toESM(require("chalk"));
|
|
2579
2655
|
var fs6 = __toESM(require("fs"));
|
|
2580
2656
|
var path4 = __toESM(require("path"));
|
|
2581
2657
|
init_auth();
|
|
@@ -2583,22 +2659,22 @@ async function runFlush(opts) {
|
|
|
2583
2659
|
const dir = getApiblazeDir();
|
|
2584
2660
|
const entries2 = fs6.existsSync(dir) ? fs6.readdirSync(dir) : [];
|
|
2585
2661
|
if (!entries2.length) {
|
|
2586
|
-
console.log(
|
|
2662
|
+
console.log(import_chalk14.default.dim("Nothing to flush \u2014 no local apiblaze config found."));
|
|
2587
2663
|
return;
|
|
2588
2664
|
}
|
|
2589
|
-
console.log(
|
|
2590
|
-
console.log(
|
|
2591
|
-
console.log(
|
|
2592
|
-
console.log(
|
|
2593
|
-
console.log(
|
|
2594
|
-
console.log(
|
|
2665
|
+
console.log(import_chalk14.default.yellow("This removes ALL local apiblaze state from this machine:"));
|
|
2666
|
+
console.log(import_chalk14.default.dim(" \u2022 login credentials & anonymous-workspace keys"));
|
|
2667
|
+
console.log(import_chalk14.default.dim(" \u2022 saved apichats \u2014 their DP API keys and transcripts"));
|
|
2668
|
+
console.log(import_chalk14.default.dim(" \u2022 your BYO LLM key and any cached secrets"));
|
|
2669
|
+
console.log(import_chalk14.default.dim(` (${entries2.length} item(s) in ${dir})`));
|
|
2670
|
+
console.log(import_chalk14.default.dim(" It does NOT delete server-side proxies/keys \u2014 those GC or are revoked separately."));
|
|
2595
2671
|
if (!opts.yes) {
|
|
2596
2672
|
const { default: inquirer3 } = await import("inquirer");
|
|
2597
2673
|
const { ok } = await inquirer3.prompt([
|
|
2598
2674
|
{ type: "confirm", name: "ok", message: "Delete all local state?", default: false }
|
|
2599
2675
|
]);
|
|
2600
2676
|
if (!ok) {
|
|
2601
|
-
console.log(
|
|
2677
|
+
console.log(import_chalk14.default.dim("Cancelled \u2014 nothing removed."));
|
|
2602
2678
|
return;
|
|
2603
2679
|
}
|
|
2604
2680
|
}
|
|
@@ -2608,14 +2684,14 @@ async function runFlush(opts) {
|
|
|
2608
2684
|
fs6.rmSync(path4.join(dir, e), { recursive: true, force: true });
|
|
2609
2685
|
removed++;
|
|
2610
2686
|
} catch (err) {
|
|
2611
|
-
console.log(
|
|
2687
|
+
console.log(import_chalk14.default.red(` \u2717 could not remove ${e}: ${err instanceof Error ? err.message : "error"}`));
|
|
2612
2688
|
}
|
|
2613
2689
|
}
|
|
2614
|
-
console.log(`${
|
|
2690
|
+
console.log(`${import_chalk14.default.green("\u2714")} Flushed \u2014 removed ${removed} item(s). You're fully logged out and clean.`);
|
|
2615
2691
|
}
|
|
2616
2692
|
|
|
2617
2693
|
// src/commands/whoami.ts
|
|
2618
|
-
var
|
|
2694
|
+
var import_chalk15 = __toESM(require("chalk"));
|
|
2619
2695
|
init_auth();
|
|
2620
2696
|
init_anon_cred();
|
|
2621
2697
|
async function runWhoami(opts = {}) {
|
|
@@ -2638,43 +2714,43 @@ async function runWhoami(opts = {}) {
|
|
|
2638
2714
|
}, null, 2));
|
|
2639
2715
|
return;
|
|
2640
2716
|
}
|
|
2641
|
-
console.log(
|
|
2717
|
+
console.log(import_chalk15.default.bold("API Producer"));
|
|
2642
2718
|
if (!creds) {
|
|
2643
|
-
console.log(
|
|
2719
|
+
console.log(import_chalk15.default.dim(" Not logged in. Run `apiblaze login`."));
|
|
2644
2720
|
} else {
|
|
2645
2721
|
const who = creds.githubHandle ?? creds.email ?? creds.apiblazeUserId ?? "unknown";
|
|
2646
|
-
console.log(` ${
|
|
2722
|
+
console.log(` ${import_chalk15.default.cyan("Signed in as")} ${import_chalk15.default.bold(who)}`);
|
|
2647
2723
|
if (creds.email && creds.email !== who) console.log(` Email: ${creds.email}`);
|
|
2648
|
-
if (creds.teamName || creds.teamId) console.log(` Team: ${
|
|
2649
|
-
if (creds.activeTenant) console.log(` Tenant scope: ${
|
|
2650
|
-
if (Date.now() >= creds.expiresAt) console.log(
|
|
2724
|
+
if (creds.teamName || creds.teamId) console.log(` Team: ${import_chalk15.default.bold(creds.teamName ?? creds.teamId)}`);
|
|
2725
|
+
if (creds.activeTenant) console.log(` Tenant scope: ${import_chalk15.default.bold(creds.activeTenant)} ${import_chalk15.default.dim("(clear: `apiblaze tenant use --clear`)")}`);
|
|
2726
|
+
if (Date.now() >= creds.expiresAt) console.log(import_chalk15.default.yellow(" \u26A0 Session expired \u2014 run `apiblaze login`."));
|
|
2651
2727
|
}
|
|
2652
2728
|
if (anon) {
|
|
2653
2729
|
const masked = `${anon.cp_key.slice(0, 11)}\u2026${anon.cp_key.slice(-4)}`;
|
|
2654
|
-
console.log(
|
|
2655
|
-
console.log(` Team: ${
|
|
2656
|
-
console.log(` CP key: ${
|
|
2730
|
+
console.log(import_chalk15.default.bold("\nAnonymous workspace") + import_chalk15.default.dim(" (created without an account \u2014 unclaimed)"));
|
|
2731
|
+
console.log(` Team: ${import_chalk15.default.bold(anon.team_id)}`);
|
|
2732
|
+
console.log(` CP key: ${import_chalk15.default.bold(masked)} ${import_chalk15.default.dim("controls the proxies you created anonymously")}`);
|
|
2657
2733
|
console.log(
|
|
2658
|
-
|
|
2734
|
+
import_chalk15.default.dim(` Keep it: ${import_chalk15.default.cyan("apiblaze login")} then ${import_chalk15.default.cyan("apiblaze claim")}`) + (anon.claim_code ? import_chalk15.default.dim(` \xB7 from another machine: ${import_chalk15.default.cyan(`apiblaze claim ${anon.claim_code}`)}`) : "")
|
|
2659
2735
|
);
|
|
2660
|
-
if (creds) console.log(
|
|
2736
|
+
if (creds) console.log(import_chalk15.default.dim(" (you're logged in \u2014 `apiblaze claim` folds this workspace into your account)"));
|
|
2661
2737
|
}
|
|
2662
|
-
console.log(
|
|
2738
|
+
console.log(import_chalk15.default.bold("\nAPI Consumer"));
|
|
2663
2739
|
if (!consumer2) {
|
|
2664
|
-
console.log(
|
|
2740
|
+
console.log(import_chalk15.default.dim(" Not logged in. Run `apiblaze consumer login`."));
|
|
2665
2741
|
} else {
|
|
2666
|
-
console.log(` ${
|
|
2667
|
-
console.log(` Tenant: ${
|
|
2668
|
-
if (Date.now() >= consumer2.expiresAt) console.log(
|
|
2742
|
+
console.log(` ${import_chalk15.default.cyan("Signed in as")} ${import_chalk15.default.bold(consumer2.email ?? consumer2.tenant)}`);
|
|
2743
|
+
console.log(` Tenant: ${import_chalk15.default.bold(consumer2.tenant)}`);
|
|
2744
|
+
if (Date.now() >= consumer2.expiresAt) console.log(import_chalk15.default.yellow(" \u26A0 Token expired \u2014 it will refresh on next use, or re-run `apiblaze consumer login`."));
|
|
2669
2745
|
}
|
|
2670
2746
|
}
|
|
2671
2747
|
|
|
2672
2748
|
// src/commands/team.ts
|
|
2673
|
-
var
|
|
2749
|
+
var import_chalk16 = __toESM(require("chalk"));
|
|
2674
2750
|
init_auth();
|
|
2675
2751
|
init_api();
|
|
2676
2752
|
function fail3(message) {
|
|
2677
|
-
console.error(
|
|
2753
|
+
console.error(import_chalk16.default.red(`Error: ${message}`));
|
|
2678
2754
|
process.exit(1);
|
|
2679
2755
|
}
|
|
2680
2756
|
async function runTeam(arg) {
|
|
@@ -2696,7 +2772,7 @@ async function runTeam(arg) {
|
|
|
2696
2772
|
}
|
|
2697
2773
|
} else if (teams.length === 1) {
|
|
2698
2774
|
chosen = teams[0];
|
|
2699
|
-
console.log(`${
|
|
2775
|
+
console.log(`${import_chalk16.default.cyan("\u2192")} You only have one team: ${import_chalk16.default.bold(chosen.name)}`);
|
|
2700
2776
|
} else if (process.stdin.isTTY) {
|
|
2701
2777
|
const { default: inquirer3 } = await import("inquirer");
|
|
2702
2778
|
const { picked } = await inquirer3.prompt([{
|
|
@@ -2711,48 +2787,48 @@ async function runTeam(arg) {
|
|
|
2711
2787
|
fail3(`Specify a team: \`apiblaze team <name|id>\`. Available: ${teams.map((t) => t.name).join(", ")}.`);
|
|
2712
2788
|
}
|
|
2713
2789
|
saveCredentials({ ...creds, teamId: chosen.teamId, teamName: chosen.name });
|
|
2714
|
-
console.log(
|
|
2715
|
-
\u2714 Active team: ${
|
|
2790
|
+
console.log(import_chalk16.default.green(`
|
|
2791
|
+
\u2714 Active team: ${import_chalk16.default.bold(chosen.name)}`));
|
|
2716
2792
|
}
|
|
2717
2793
|
|
|
2718
2794
|
// src/commands/authz.ts
|
|
2719
|
-
var
|
|
2795
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
2720
2796
|
init_auth();
|
|
2721
2797
|
init_api();
|
|
2722
2798
|
|
|
2723
2799
|
// src/lib/agent-chat.ts
|
|
2724
2800
|
var import_readline = __toESM(require("readline"));
|
|
2725
|
-
var
|
|
2801
|
+
var import_chalk17 = __toESM(require("chalk"));
|
|
2726
2802
|
init_api();
|
|
2727
2803
|
init_admin();
|
|
2728
2804
|
async function runAgentChatRepl(opts) {
|
|
2729
2805
|
const { title, subtitle, endpoint, buildBody, seedPrompt, summarizeProposal, commands } = opts;
|
|
2730
|
-
console.log("\n" +
|
|
2731
|
-
if (subtitle) console.log(
|
|
2806
|
+
console.log("\n" + import_chalk17.default.cyan.bold(title));
|
|
2807
|
+
if (subtitle) console.log(import_chalk17.default.dim(subtitle));
|
|
2732
2808
|
const messages = [];
|
|
2733
2809
|
const ctx = { proposal: null, lastData: null, log: (s) => console.log(s) };
|
|
2734
2810
|
const cmdByName = new Map(commands.map((c) => [c.name, c]));
|
|
2735
2811
|
const footer = () => {
|
|
2736
2812
|
const parts = [
|
|
2737
|
-
|
|
2738
|
-
...commands.map((c) =>
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2813
|
+
import_chalk17.default.dim("type to chat/refine"),
|
|
2814
|
+
...commands.map((c) => import_chalk17.default.dim(`/${c.name} ${c.describe}`)),
|
|
2815
|
+
import_chalk17.default.dim("/show"),
|
|
2816
|
+
import_chalk17.default.dim("/exit when done"),
|
|
2817
|
+
import_chalk17.default.dim("/drop discard changes")
|
|
2742
2818
|
];
|
|
2743
|
-
return " " +
|
|
2819
|
+
return " " + import_chalk17.default.dim("[ ") + parts.join(import_chalk17.default.dim(" \xB7 ")) + import_chalk17.default.dim(" ]");
|
|
2744
2820
|
};
|
|
2745
2821
|
async function turn(content) {
|
|
2746
2822
|
messages.push({ role: "user", content });
|
|
2747
|
-
process.stdout.write(
|
|
2823
|
+
process.stdout.write(import_chalk17.default.dim(" \u2026thinking\n"));
|
|
2748
2824
|
const { status, data } = await agentCall(endpoint, "POST", { messages, ...buildBody(messages) });
|
|
2749
2825
|
if (status === 402) {
|
|
2750
|
-
console.log(
|
|
2826
|
+
console.log(import_chalk17.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
|
|
2751
2827
|
messages.pop();
|
|
2752
2828
|
return;
|
|
2753
2829
|
}
|
|
2754
2830
|
if (status >= 400 || !data) {
|
|
2755
|
-
console.log(
|
|
2831
|
+
console.log(import_chalk17.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
|
|
2756
2832
|
`));
|
|
2757
2833
|
messages.pop();
|
|
2758
2834
|
return;
|
|
@@ -2760,24 +2836,24 @@ async function runAgentChatRepl(opts) {
|
|
|
2760
2836
|
ctx.lastData = data;
|
|
2761
2837
|
const reply = data.reply ?? data.message ?? "(no reply)";
|
|
2762
2838
|
messages.push({ role: "assistant", content: reply });
|
|
2763
|
-
console.log("\n" +
|
|
2839
|
+
console.log("\n" + import_chalk17.default.cyan("agent \u203A ") + reply + "\n");
|
|
2764
2840
|
if (data.proposal) {
|
|
2765
2841
|
ctx.proposal = data.proposal;
|
|
2766
2842
|
const summary = summarizeProposal?.(data);
|
|
2767
|
-
if (summary) console.log(
|
|
2843
|
+
if (summary) console.log(import_chalk17.default.yellow(" " + summary));
|
|
2768
2844
|
const applies = commands.filter((c) => c.needsProposal);
|
|
2769
2845
|
const primary = applies[applies.length - 1];
|
|
2770
2846
|
if (primary) {
|
|
2771
|
-
console.log(
|
|
2847
|
+
console.log(import_chalk17.default.bold.green(" \u279C Next: type ") + import_chalk17.default.bold.cyan(`/${primary.name}`) + import_chalk17.default.bold.green(" to apply this, then ") + import_chalk17.default.bold.cyan("/exit") + import_chalk17.default.bold.green(" to leave."));
|
|
2772
2848
|
const rest = applies.slice(0, -1).map((c) => `/${c.name} = ${c.describe}`).join(" \xB7 ");
|
|
2773
|
-
console.log(
|
|
2849
|
+
console.log(import_chalk17.default.dim(` (or keep chatting to refine${rest ? ` \xB7 ${rest}` : ""} \xB7 /show = inspect)`));
|
|
2774
2850
|
}
|
|
2775
2851
|
console.log();
|
|
2776
2852
|
}
|
|
2777
2853
|
const line = formatBilling(data.billing);
|
|
2778
2854
|
if (line) {
|
|
2779
2855
|
const llm2 = data.llm;
|
|
2780
|
-
console.log(
|
|
2856
|
+
console.log(import_chalk17.default.dim(` ${line}${llm2?.model ? ` \xB7 ${llm2.model}` : ""}`));
|
|
2781
2857
|
}
|
|
2782
2858
|
}
|
|
2783
2859
|
const rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -2785,31 +2861,31 @@ async function runAgentChatRepl(opts) {
|
|
|
2785
2861
|
await turn(seedPrompt);
|
|
2786
2862
|
for (; ; ) {
|
|
2787
2863
|
console.log(footer());
|
|
2788
|
-
const line = (await ask(
|
|
2864
|
+
const line = (await ask(import_chalk17.default.green("you \u203A "))).trim();
|
|
2789
2865
|
if (!line) continue;
|
|
2790
2866
|
const lower = line.toLowerCase();
|
|
2791
2867
|
if (line === "/exit" || line === "/quit" || lower === "exit" || lower === "quit" || lower === "q") {
|
|
2792
|
-
console.log(
|
|
2868
|
+
console.log(import_chalk17.default.dim(" Bye \u2014 anything you enabled or published stays in place.\n"));
|
|
2793
2869
|
break;
|
|
2794
2870
|
}
|
|
2795
2871
|
if (line === "/drop") {
|
|
2796
|
-
console.log(
|
|
2872
|
+
console.log(import_chalk17.default.dim(" Dropped the pending proposal \u2014 rules you already enabled/published are untouched.\n"));
|
|
2797
2873
|
break;
|
|
2798
2874
|
}
|
|
2799
2875
|
if (line === "/show") {
|
|
2800
|
-
if (!ctx.proposal) console.log(
|
|
2876
|
+
if (!ctx.proposal) console.log(import_chalk17.default.dim(" No proposal yet \u2014 keep chatting until the agent proposes one.\n"));
|
|
2801
2877
|
else console.log("\n" + JSON.stringify(ctx.proposal, null, 2) + "\n");
|
|
2802
2878
|
continue;
|
|
2803
2879
|
}
|
|
2804
2880
|
if (line.startsWith("/")) {
|
|
2805
2881
|
const cmd = cmdByName.get(line.slice(1));
|
|
2806
2882
|
if (!cmd) {
|
|
2807
|
-
console.log(
|
|
2883
|
+
console.log(import_chalk17.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
|
|
2808
2884
|
`));
|
|
2809
2885
|
continue;
|
|
2810
2886
|
}
|
|
2811
2887
|
if (cmd.needsProposal && !ctx.proposal) {
|
|
2812
|
-
console.log(
|
|
2888
|
+
console.log(import_chalk17.default.yellow(" No proposal yet \u2014 keep chatting until the agent generates one, then try again.\n"));
|
|
2813
2889
|
continue;
|
|
2814
2890
|
}
|
|
2815
2891
|
await cmd.run(ctx);
|
|
@@ -2821,7 +2897,7 @@ async function runAgentChatRepl(opts) {
|
|
|
2821
2897
|
}
|
|
2822
2898
|
|
|
2823
2899
|
// src/lib/authz-apply.ts
|
|
2824
|
-
var
|
|
2900
|
+
var import_chalk18 = __toESM(require("chalk"));
|
|
2825
2901
|
init_api();
|
|
2826
2902
|
async function gatherSampleIds(projectId, apiVersion, cap = 50) {
|
|
2827
2903
|
try {
|
|
@@ -2846,10 +2922,10 @@ async function gatherSampleIds(projectId, apiVersion, cap = 50) {
|
|
|
2846
2922
|
}
|
|
2847
2923
|
function maybeWarnNoTraffic(total, projectArg) {
|
|
2848
2924
|
if (total >= 3) return;
|
|
2849
|
-
console.log(
|
|
2850
|
-
console.log(
|
|
2851
|
-
console.log(
|
|
2852
|
-
console.log(
|
|
2925
|
+
console.log(import_chalk18.default.yellow(total === 0 ? " \u26A0 No captured traffic for this API yet." : ` \u26A0 Only ${total} captured request(s) so far.`));
|
|
2926
|
+
console.log(import_chalk18.default.dim(" The authorization agent designs much better models & rules from REAL requests"));
|
|
2927
|
+
console.log(import_chalk18.default.dim(" (it sees who is allowed vs denied). Send some through your proxy first \u2014"));
|
|
2928
|
+
console.log(import_chalk18.default.dim(` curl a few endpoints, or `) + import_chalk18.default.cyan("npx apiblaze dev") + import_chalk18.default.dim(` \u2014 then re-run.
|
|
2853
2929
|
`));
|
|
2854
2930
|
}
|
|
2855
2931
|
async function applyProposal(opts) {
|
|
@@ -2858,11 +2934,11 @@ async function applyProposal(opts) {
|
|
|
2858
2934
|
{
|
|
2859
2935
|
const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant2)}`, "POST", proposal.model);
|
|
2860
2936
|
if (m.status === 404) {
|
|
2861
|
-
log(
|
|
2937
|
+
log(import_chalk18.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"));
|
|
2862
2938
|
return { ok: 0, fail: 0, enabled: false, storeMissing: true };
|
|
2863
2939
|
}
|
|
2864
2940
|
if (m.status >= 400) {
|
|
2865
|
-
log(
|
|
2941
|
+
log(import_chalk18.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
|
|
2866
2942
|
`));
|
|
2867
2943
|
return { ok: 0, fail: 0, enabled: false };
|
|
2868
2944
|
}
|
|
@@ -2891,7 +2967,7 @@ async function applyProposal(opts) {
|
|
|
2891
2967
|
if (enable) {
|
|
2892
2968
|
const e = await agentCall(`/projects/${projectId}/${apiVersion}`, "PATCH", { authorization: { enforce_authorization: true }, tenant: tenant2 });
|
|
2893
2969
|
if (e.status >= 400) {
|
|
2894
|
-
log(
|
|
2970
|
+
log(import_chalk18.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
|
|
2895
2971
|
`));
|
|
2896
2972
|
return { ok, fail: fail5, enabled: false };
|
|
2897
2973
|
}
|
|
@@ -2917,10 +2993,10 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
2917
2993
|
const r = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable, log: ctx.log });
|
|
2918
2994
|
if (r.storeMissing) return;
|
|
2919
2995
|
if (enable && r.enabled) {
|
|
2920
|
-
ctx.log(
|
|
2921
|
-
ctx.log(
|
|
2996
|
+
ctx.log(import_chalk19.default.green(` \u2713 Enforcement ON \u2014 ${r.ok} route(s) + project switch on${r.fail ? `, ${r.fail} failed` : ""}.`));
|
|
2997
|
+
ctx.log(import_chalk19.default.dim(" Done here? Type /exit to leave the chat (your rule stays on).\n"));
|
|
2922
2998
|
} else if (!enable) {
|
|
2923
|
-
ctx.log(
|
|
2999
|
+
ctx.log(import_chalk19.default.green(` \u2713 Published (shadow): model + ${r.ok} route(s)${r.fail ? `, ${r.fail} failed` : ""}. Review, then /enable.
|
|
2924
3000
|
`));
|
|
2925
3001
|
}
|
|
2926
3002
|
}
|
|
@@ -2947,7 +3023,7 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
2947
3023
|
}
|
|
2948
3024
|
|
|
2949
3025
|
// src/commands/rule.ts
|
|
2950
|
-
var
|
|
3026
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
2951
3027
|
var import_ora6 = __toESM(require("ora"));
|
|
2952
3028
|
init_auth();
|
|
2953
3029
|
init_api();
|
|
@@ -2984,27 +3060,27 @@ async function runRule(ruleText, projectArg, opts) {
|
|
|
2984
3060
|
const proposal = data?.proposal;
|
|
2985
3061
|
if (!proposal || !Array.isArray(proposal.routes) || proposal.routes.length === 0) {
|
|
2986
3062
|
spinner.fail("The agent couldn\u2019t turn that into a rule in one shot.");
|
|
2987
|
-
if (data?.reply) console.log(
|
|
2988
|
-
console.log(
|
|
3063
|
+
if (data?.reply) console.log(import_chalk20.default.dim("\n " + String(data.reply).slice(0, 500) + "\n"));
|
|
3064
|
+
console.log(import_chalk20.default.dim(" Try an interactive session for anything nuanced: ") + import_chalk20.default.cyan(`apiblaze agent authz ${projectArg}`));
|
|
2989
3065
|
return;
|
|
2990
3066
|
}
|
|
2991
3067
|
spinner.succeed(`Rule designed \u2014 ${proposal.routes.length} route(s) affected.`);
|
|
2992
|
-
if (data?.reply) console.log(
|
|
3068
|
+
if (data?.reply) console.log(import_chalk20.default.dim(" " + String(data.reply).slice(0, 240)));
|
|
2993
3069
|
const pub = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable: false });
|
|
2994
3070
|
if (pub.storeMissing) return;
|
|
2995
|
-
console.log(
|
|
3071
|
+
console.log(import_chalk20.default.green(` \u2713 Published in shadow mode \u2014 model + ${pub.ok} route(s)${pub.fail ? `, ${pub.fail} failed` : ""}.`));
|
|
2996
3072
|
if (opts.enforce) {
|
|
2997
3073
|
const enf = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable: true });
|
|
2998
3074
|
if (enf.enabled) {
|
|
2999
|
-
console.log(
|
|
3075
|
+
console.log(import_chalk20.default.green(` \u2713 Enforcement ON \u2014 ${enf.ok} route(s) + project switch on${enf.fail ? `, ${enf.fail} failed` : ""}.`));
|
|
3000
3076
|
}
|
|
3001
3077
|
} else {
|
|
3002
|
-
console.log(
|
|
3078
|
+
console.log(import_chalk20.default.dim(` Review it (shadow logs), then enforce: `) + import_chalk20.default.cyan(`apiblaze rule "${rule}" ${projectArg} --enforce`));
|
|
3003
3079
|
}
|
|
3004
3080
|
}
|
|
3005
3081
|
|
|
3006
3082
|
// src/commands/openapi.ts
|
|
3007
|
-
var
|
|
3083
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
3008
3084
|
init_auth();
|
|
3009
3085
|
init_api();
|
|
3010
3086
|
async function runOpenapi(projectArg, apiVersionArg) {
|
|
@@ -3015,10 +3091,10 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
3015
3091
|
if (!match) throw new Error(`Project "${projectArg}" not found in your active team. Run \`apiblaze projects\`.`);
|
|
3016
3092
|
const projectId = match.projectId;
|
|
3017
3093
|
const apiVersion = apiVersionArg || match.apiVersion;
|
|
3018
|
-
console.log(
|
|
3094
|
+
console.log(import_chalk21.default.dim("Gathering captured traffic samples\u2026"));
|
|
3019
3095
|
const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
|
|
3020
3096
|
if (routesRes.status >= 400) {
|
|
3021
|
-
console.log(
|
|
3097
|
+
console.log(import_chalk21.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
|
|
3022
3098
|
return;
|
|
3023
3099
|
}
|
|
3024
3100
|
const routes = routesRes.data?.routes ?? (Array.isArray(routesRes.data) ? routesRes.data : []);
|
|
@@ -3031,25 +3107,25 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
3031
3107
|
}
|
|
3032
3108
|
const sampleIds = [...ids];
|
|
3033
3109
|
if (sampleIds.length === 0) {
|
|
3034
|
-
console.log(
|
|
3110
|
+
console.log(import_chalk21.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
|
|
3035
3111
|
return;
|
|
3036
3112
|
}
|
|
3037
3113
|
async function publish(ctx) {
|
|
3038
3114
|
const patch = ctx.proposal.patch;
|
|
3039
3115
|
if (!Array.isArray(patch) || patch.length === 0) {
|
|
3040
|
-
ctx.log(
|
|
3116
|
+
ctx.log(import_chalk21.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
|
|
3041
3117
|
return;
|
|
3042
3118
|
}
|
|
3043
3119
|
const specSource = ctx.lastData?.spec_source ?? "unknown";
|
|
3044
3120
|
const endpoint = specSource === "github" ? "open-pr" : "publish-openapi";
|
|
3045
3121
|
const pub = await agentCall(`/projects/${projectId}/${apiVersion}/samples/${endpoint}`, "POST", { patch, sample_ids_used: sampleIds });
|
|
3046
3122
|
if (pub.status >= 400) {
|
|
3047
|
-
ctx.log(
|
|
3123
|
+
ctx.log(import_chalk21.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
3048
3124
|
`));
|
|
3049
3125
|
return;
|
|
3050
3126
|
}
|
|
3051
3127
|
const prUrl = pub.data?.pr_url;
|
|
3052
|
-
ctx.log(
|
|
3128
|
+
ctx.log(import_chalk21.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
|
|
3053
3129
|
` : " \u2713 Published updated OpenAPI spec.\n"));
|
|
3054
3130
|
}
|
|
3055
3131
|
await runAgentChatRepl({
|
|
@@ -3070,7 +3146,7 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
3070
3146
|
}
|
|
3071
3147
|
|
|
3072
3148
|
// src/commands/mcp.ts
|
|
3073
|
-
var
|
|
3149
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
3074
3150
|
init_auth();
|
|
3075
3151
|
init_api();
|
|
3076
3152
|
async function runMcp(projectArg, apiVersionArg, opts) {
|
|
@@ -3087,11 +3163,11 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
3087
3163
|
const spec2 = ctx.proposal;
|
|
3088
3164
|
const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec: spec2 });
|
|
3089
3165
|
if (pub.status >= 400) {
|
|
3090
|
-
ctx.log(
|
|
3166
|
+
ctx.log(import_chalk22.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
3091
3167
|
`));
|
|
3092
3168
|
return;
|
|
3093
3169
|
}
|
|
3094
|
-
ctx.log(
|
|
3170
|
+
ctx.log(import_chalk22.default.green(` \u2713 Published MCP server \u2014 ${mcpHost}.mcp.apiblaze.com/${apiVersion}/${environment}.
|
|
3095
3171
|
`));
|
|
3096
3172
|
}
|
|
3097
3173
|
await runAgentChatRepl({
|
|
@@ -3113,7 +3189,7 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
3113
3189
|
}
|
|
3114
3190
|
|
|
3115
3191
|
// src/commands/delete.ts
|
|
3116
|
-
var
|
|
3192
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
3117
3193
|
var import_ora7 = __toESM(require("ora"));
|
|
3118
3194
|
init_admin();
|
|
3119
3195
|
init_resolve();
|
|
@@ -3139,10 +3215,10 @@ async function runDelete(project, version2, opts) {
|
|
|
3139
3215
|
return;
|
|
3140
3216
|
}
|
|
3141
3217
|
} else {
|
|
3142
|
-
console.log(`${
|
|
3218
|
+
console.log(`${import_chalk23.default.red.bold("Delete")} ${import_chalk23.default.bold(proj2.projectName)} ${import_chalk23.default.dim("v" + proj2.apiVersion)} ${import_chalk23.default.dim("(" + proj2.projectId + ")")}`);
|
|
3143
3219
|
if (impact && typeof impact === "object") {
|
|
3144
3220
|
const counts = impact.counts ?? impact.impact ?? impact;
|
|
3145
|
-
console.log(
|
|
3221
|
+
console.log(import_chalk23.default.dim(" This removes (cascade): ") + import_chalk23.default.yellow(JSON.stringify(counts)));
|
|
3146
3222
|
}
|
|
3147
3223
|
if (!opts.yes) {
|
|
3148
3224
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -3150,7 +3226,7 @@ async function runDelete(project, version2, opts) {
|
|
|
3150
3226
|
{ type: "confirm", name: "confirm", message: `Permanently delete ${proj2.projectName} v${proj2.apiVersion}? This cannot be undone.`, default: false }
|
|
3151
3227
|
]);
|
|
3152
3228
|
if (!confirm) {
|
|
3153
|
-
console.log(
|
|
3229
|
+
console.log(import_chalk23.default.dim("Aborted."));
|
|
3154
3230
|
return;
|
|
3155
3231
|
}
|
|
3156
3232
|
}
|
|
@@ -3171,7 +3247,7 @@ async function runDelete(project, version2, opts) {
|
|
|
3171
3247
|
}
|
|
3172
3248
|
|
|
3173
3249
|
// src/commands/export.ts
|
|
3174
|
-
var
|
|
3250
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
3175
3251
|
var import_fs3 = require("fs");
|
|
3176
3252
|
init_admin();
|
|
3177
3253
|
init_auth();
|
|
@@ -3190,7 +3266,7 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
3190
3266
|
if (opts.noConsumers) q.set("include_consumers", "0");
|
|
3191
3267
|
if (opts.keys) q.set("keys", opts.keys);
|
|
3192
3268
|
const qs = q.toString() ? `?${q.toString()}` : "";
|
|
3193
|
-
console.log(
|
|
3269
|
+
console.log(import_chalk24.default.dim(`Queuing ${target} export of ${projectId}@${version2}\u2026`));
|
|
3194
3270
|
const job = await admin({
|
|
3195
3271
|
path: `/projects/${projectId}/${version2}/export/${target}${qs}`,
|
|
3196
3272
|
method: "GET",
|
|
@@ -3200,11 +3276,11 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
3200
3276
|
const statusPath = `/projects/${projectId}/${version2}/export/jobs/${job.job_id}`;
|
|
3201
3277
|
let status = "queued";
|
|
3202
3278
|
const started = Date.now();
|
|
3203
|
-
process.stdout.write(
|
|
3279
|
+
process.stdout.write(import_chalk24.default.dim("Building bundle"));
|
|
3204
3280
|
while (status !== "ready" && status !== "failed") {
|
|
3205
3281
|
if (Date.now() - started > 10 * 6e4) throw new Error("Export timed out after 10 minutes.");
|
|
3206
3282
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
3207
|
-
process.stdout.write(
|
|
3283
|
+
process.stdout.write(import_chalk24.default.dim("."));
|
|
3208
3284
|
const st = await admin({ path: statusPath, method: "GET", summary: "poll export" });
|
|
3209
3285
|
status = st?.status ?? "queued";
|
|
3210
3286
|
if (status === "failed") {
|
|
@@ -3222,14 +3298,869 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
3222
3298
|
if (!res.ok) throw new Error(`Download failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
3223
3299
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
3224
3300
|
(0, import_fs3.writeFileSync)(outFile, buf);
|
|
3225
|
-
console.log(
|
|
3226
|
-
if (job.decryption_key) console.log(
|
|
3227
|
-
console.log(
|
|
3228
|
-
if (target === "kong") console.log(
|
|
3301
|
+
console.log(import_chalk24.default.green(`\u2713 Saved ${outFile} (${(buf.length / 1024).toFixed(0)} KB)`));
|
|
3302
|
+
if (job.decryption_key) console.log(import_chalk24.default.yellow(`Decryption key (shown once): ${job.decryption_key}`));
|
|
3303
|
+
console.log(import_chalk24.default.dim("Read MIGRATION-REPORT.json inside the zip for the full fidelity ledger."));
|
|
3304
|
+
if (target === "kong") console.log(import_chalk24.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3307
|
+
// src/commands/logs.ts
|
|
3308
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
3309
|
+
init_admin();
|
|
3310
|
+
init_auth();
|
|
3311
|
+
init_resolve();
|
|
3312
|
+
|
|
3313
|
+
// src/lib/log-view.ts
|
|
3314
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
3315
|
+
var readline2 = __toESM(require("readline"));
|
|
3316
|
+
function statusColor(status) {
|
|
3317
|
+
if (status < 300) return import_chalk25.default.green;
|
|
3318
|
+
if (status < 400) return import_chalk25.default.blue;
|
|
3319
|
+
if (status < 500) return import_chalk25.default.yellow;
|
|
3320
|
+
return import_chalk25.default.red;
|
|
3321
|
+
}
|
|
3322
|
+
function methodColor(method) {
|
|
3323
|
+
switch ((method || "").toUpperCase()) {
|
|
3324
|
+
case "GET":
|
|
3325
|
+
return import_chalk25.default.green;
|
|
3326
|
+
case "POST":
|
|
3327
|
+
return import_chalk25.default.blue;
|
|
3328
|
+
case "PUT":
|
|
3329
|
+
case "PATCH":
|
|
3330
|
+
return import_chalk25.default.yellow;
|
|
3331
|
+
case "DELETE":
|
|
3332
|
+
return import_chalk25.default.red;
|
|
3333
|
+
default:
|
|
3334
|
+
return import_chalk25.default.white;
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
function clock(ts) {
|
|
3338
|
+
return new Date(ts).toLocaleTimeString(void 0, { hour12: false });
|
|
3339
|
+
}
|
|
3340
|
+
var ROUTE_W = 34;
|
|
3341
|
+
function routeCell(route) {
|
|
3342
|
+
const r = route || "\u2014";
|
|
3343
|
+
return (r.length > ROUTE_W ? `\u2026${r.slice(-(ROUTE_W - 1))}` : r).padEnd(ROUTE_W);
|
|
3344
|
+
}
|
|
3345
|
+
function formatRow(r) {
|
|
3346
|
+
const time = import_chalk25.default.dim(clock(r.ts));
|
|
3347
|
+
const method = methodColor(r.method)(String(r.method || "\u2014").padEnd(6));
|
|
3348
|
+
const route = routeCell(String(r.route || "\u2014"));
|
|
3349
|
+
const status = statusColor(r.status)(String(r.status ?? "\u2014").padStart(3));
|
|
3350
|
+
const ms = import_chalk25.default.dim(`${r.duration_ms}ms`.padStart(8));
|
|
3351
|
+
const step = r.error_step ? import_chalk25.default.dim(` ${r.error_step}`) : "";
|
|
3352
|
+
return `${time} ${method} ${route} ${status} ${ms}${step}`;
|
|
3353
|
+
}
|
|
3354
|
+
function printRow(r, json) {
|
|
3355
|
+
console.log(json ? JSON.stringify(r) : formatRow(r));
|
|
3356
|
+
}
|
|
3357
|
+
var WindowNotices = class {
|
|
3358
|
+
constructor() {
|
|
3359
|
+
this.saidUnavailable = false;
|
|
3360
|
+
this.saidGap = false;
|
|
3361
|
+
this.saidDropped = false;
|
|
3362
|
+
}
|
|
3363
|
+
note(res) {
|
|
3364
|
+
if (res.history_unavailable && !this.saidUnavailable) {
|
|
3365
|
+
this.saidUnavailable = true;
|
|
3366
|
+
console.error(import_chalk25.default.yellow("\u26A0 log history is unreachable right now \u2014 showing the live buffer only, older requests exist but cannot be read."));
|
|
3367
|
+
}
|
|
3368
|
+
if ((res.history_markers?.length ?? 0) > 0 && !this.saidGap) {
|
|
3369
|
+
this.saidGap = true;
|
|
3370
|
+
console.error(import_chalk25.default.yellow("\u26A0 this range includes a volume-ceiling gap \u2014 some rows were dropped at intake and are gone; counts are incomplete."));
|
|
3371
|
+
}
|
|
3372
|
+
if ((res.dropped_this_hour ?? 0) > 0 && !this.saidDropped) {
|
|
3373
|
+
this.saidDropped = true;
|
|
3374
|
+
console.error(import_chalk25.default.yellow(`\u26A0 volume ceiling \u2014 ${res.dropped_this_hour} rows dropped this hour; errors keep logging.`));
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
};
|
|
3378
|
+
var MAX_RECONNECTS = 5;
|
|
3379
|
+
var PING_MS = Number(process.env.APIBLAZE_TAIL_PING_MS) || 3e4;
|
|
3380
|
+
var LIVENESS_MS = PING_MS * 2.5;
|
|
3381
|
+
var STABLE_MS = 3e4;
|
|
3382
|
+
var SEEN_CAP = 1e4;
|
|
3383
|
+
var TAIL_INSPECT_BUFFER = 500;
|
|
3384
|
+
var TAIL_INSPECT_CHOICES = 40;
|
|
3385
|
+
async function runTailSession(opts) {
|
|
3386
|
+
const { default: WebSocket2 } = await import("ws");
|
|
3387
|
+
guardStdoutPipe();
|
|
3388
|
+
let closed = false;
|
|
3389
|
+
let everConnected = false;
|
|
3390
|
+
let reconnects = 0;
|
|
3391
|
+
let openedAt = 0;
|
|
3392
|
+
let lastFailure = "";
|
|
3393
|
+
let pingTimer;
|
|
3394
|
+
let activeSocket;
|
|
3395
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3396
|
+
const seenOrder = [];
|
|
3397
|
+
function remember(id) {
|
|
3398
|
+
seen.add(id);
|
|
3399
|
+
seenOrder.push(id);
|
|
3400
|
+
if (seenOrder.length > SEEN_CAP) seen.delete(seenOrder.shift());
|
|
3401
|
+
}
|
|
3402
|
+
const interactive = !opts.json && !!process.stdin.isTTY && !!process.stdout.isTTY;
|
|
3403
|
+
const recent = [];
|
|
3404
|
+
const held = [];
|
|
3405
|
+
let heldRowsDropped = 0;
|
|
3406
|
+
let heldNotesDropped = 0;
|
|
3407
|
+
let paused = false;
|
|
3408
|
+
let keysBound = false;
|
|
3409
|
+
function pushHeld(item) {
|
|
3410
|
+
if (held.length >= TAIL_INSPECT_BUFFER) {
|
|
3411
|
+
const victimIdx = held.findIndex((h) => h.kind === "row");
|
|
3412
|
+
if (victimIdx >= 0) {
|
|
3413
|
+
held.splice(victimIdx, 1);
|
|
3414
|
+
heldRowsDropped++;
|
|
3415
|
+
} else {
|
|
3416
|
+
held.shift();
|
|
3417
|
+
heldNotesDropped++;
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
held.push(item);
|
|
3421
|
+
}
|
|
3422
|
+
function diag(msg) {
|
|
3423
|
+
if (paused) {
|
|
3424
|
+
pushHeld({ kind: "note", msg });
|
|
3425
|
+
return;
|
|
3426
|
+
}
|
|
3427
|
+
console.error(msg);
|
|
3428
|
+
}
|
|
3429
|
+
function flushHeld() {
|
|
3430
|
+
if (!held.length && !heldRowsDropped && !heldNotesDropped) return;
|
|
3431
|
+
const rows = held.filter((h) => h.kind === "row").length;
|
|
3432
|
+
const total = rows + heldRowsDropped;
|
|
3433
|
+
if (total) console.log(import_chalk25.default.dim(`\u2500\u2500\u2500 ${total} request${total === 1 ? "" : "s"} arrived while paused \u2500\u2500\u2500`));
|
|
3434
|
+
for (const h of held) {
|
|
3435
|
+
if (h.kind === "row") printRow(h.row, opts.json);
|
|
3436
|
+
else console.error(h.msg);
|
|
3437
|
+
}
|
|
3438
|
+
if (heldRowsDropped || heldNotesDropped) {
|
|
3439
|
+
console.log(import_chalk25.default.dim(`\u2500\u2500\u2500 ${heldRowsDropped} older one${heldRowsDropped === 1 ? "" : "s"} not shown (pause buffer full) \u2014 browse them with \`logs list\` \u2500\u2500\u2500`));
|
|
3440
|
+
}
|
|
3441
|
+
held.length = 0;
|
|
3442
|
+
heldRowsDropped = 0;
|
|
3443
|
+
heldNotesDropped = 0;
|
|
3444
|
+
}
|
|
3445
|
+
function shutdown() {
|
|
3446
|
+
closed = true;
|
|
3447
|
+
if (pingTimer) clearInterval(pingTimer);
|
|
3448
|
+
unbindKeys();
|
|
3449
|
+
try {
|
|
3450
|
+
activeSocket?.close();
|
|
3451
|
+
} catch {
|
|
3452
|
+
}
|
|
3453
|
+
if (!opts.json) console.log(import_chalk25.default.dim("\nStopped."));
|
|
3454
|
+
process.exit(0);
|
|
3455
|
+
}
|
|
3456
|
+
function onKeypress(_s, key) {
|
|
3457
|
+
if (key?.ctrl && key.name === "c") {
|
|
3458
|
+
shutdown();
|
|
3459
|
+
return;
|
|
3460
|
+
}
|
|
3461
|
+
if (key?.name === "return" || key?.name === "enter") void openInspector();
|
|
3462
|
+
}
|
|
3463
|
+
function bindKeys() {
|
|
3464
|
+
if (!interactive || keysBound || paused) return;
|
|
3465
|
+
keysBound = true;
|
|
3466
|
+
readline2.emitKeypressEvents(process.stdin);
|
|
3467
|
+
process.stdin.setRawMode(true);
|
|
3468
|
+
process.stdin.on("keypress", onKeypress);
|
|
3469
|
+
process.stdin.resume();
|
|
3470
|
+
}
|
|
3471
|
+
function unbindKeys() {
|
|
3472
|
+
if (!keysBound) return;
|
|
3473
|
+
keysBound = false;
|
|
3474
|
+
process.stdin.off("keypress", onKeypress);
|
|
3475
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
3476
|
+
process.stdin.pause();
|
|
3477
|
+
}
|
|
3478
|
+
async function openInspector() {
|
|
3479
|
+
if (paused) return;
|
|
3480
|
+
if (recent.length === 0) {
|
|
3481
|
+
console.log(import_chalk25.default.dim(" nothing captured yet \u2014 waiting for a request\u2026"));
|
|
3482
|
+
return;
|
|
3483
|
+
}
|
|
3484
|
+
paused = true;
|
|
3485
|
+
unbindKeys();
|
|
3486
|
+
try {
|
|
3487
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
3488
|
+
console.log(import_chalk25.default.dim("\n\u2500\u2500\u2500 paused \xB7 still capturing in the background \u2500\u2500\u2500"));
|
|
3489
|
+
for (; ; ) {
|
|
3490
|
+
const choices = recent.slice(-TAIL_INSPECT_CHOICES).reverse().map((r) => ({ name: formatRow(r), value: r.request_id }));
|
|
3491
|
+
choices.push({ name: import_chalk25.default.dim("\u2039resume tail\u203A"), value: "__resume__" });
|
|
3492
|
+
const { picked } = await inquirer3.prompt([{
|
|
3493
|
+
type: "list",
|
|
3494
|
+
name: "picked",
|
|
3495
|
+
message: "Inspect which request?",
|
|
3496
|
+
choices,
|
|
3497
|
+
pageSize: 15
|
|
3498
|
+
}]);
|
|
3499
|
+
if (picked === "__resume__") break;
|
|
3500
|
+
const row = recent.find((r) => r.request_id === picked);
|
|
3501
|
+
if (row) printDetail(row, opts.detail);
|
|
3502
|
+
else console.log(import_chalk25.default.dim(" that request has scrolled out of the tail buffer \u2014 find it with `logs list`.\n"));
|
|
3503
|
+
}
|
|
3504
|
+
} finally {
|
|
3505
|
+
paused = false;
|
|
3506
|
+
flushHeld();
|
|
3507
|
+
console.log(import_chalk25.default.dim("\u2500\u2500\u2500 resumed \u2500\u2500\u2500"));
|
|
3508
|
+
bindKeys();
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
process.on("SIGINT", shutdown);
|
|
3512
|
+
function scheduleReconnect() {
|
|
3513
|
+
if (closed) return;
|
|
3514
|
+
if (reconnects >= MAX_RECONNECTS) {
|
|
3515
|
+
paused = false;
|
|
3516
|
+
flushHeld();
|
|
3517
|
+
console.error(import_chalk25.default.red(`Connection lost \u2014 giving up after repeated reconnects${lastFailure ? ` (last error: ${lastFailure})` : ""}.`));
|
|
3518
|
+
process.exit(1);
|
|
3519
|
+
}
|
|
3520
|
+
const delay = Math.min(1e3 * 2 ** reconnects, 15e3);
|
|
3521
|
+
reconnects++;
|
|
3522
|
+
diag(import_chalk25.default.dim(`disconnected${lastFailure ? ` (${lastFailure})` : ""} \u2014 reconnecting in ${Math.round(delay / 1e3)}s\u2026`));
|
|
3523
|
+
setTimeout(() => {
|
|
3524
|
+
if (!closed) void connect();
|
|
3525
|
+
}, delay);
|
|
3526
|
+
}
|
|
3527
|
+
async function connect() {
|
|
3528
|
+
let url;
|
|
3529
|
+
try {
|
|
3530
|
+
url = await opts.mintTicket();
|
|
3531
|
+
} catch (err) {
|
|
3532
|
+
if (!everConnected) {
|
|
3533
|
+
console.error(import_chalk25.default.red(`Could not start tail: ${err.message}`));
|
|
3534
|
+
process.exit(1);
|
|
3535
|
+
}
|
|
3536
|
+
lastFailure = `ticket mint failed: ${err.message}`;
|
|
3537
|
+
scheduleReconnect();
|
|
3538
|
+
return;
|
|
3539
|
+
}
|
|
3540
|
+
let socket;
|
|
3541
|
+
try {
|
|
3542
|
+
socket = new WebSocket2(url);
|
|
3543
|
+
} catch (err) {
|
|
3544
|
+
lastFailure = `bad tail URL: ${err.message}`;
|
|
3545
|
+
if (!everConnected) {
|
|
3546
|
+
console.error(import_chalk25.default.red(`Could not start tail: ${lastFailure}`));
|
|
3547
|
+
process.exit(1);
|
|
3548
|
+
}
|
|
3549
|
+
scheduleReconnect();
|
|
3550
|
+
return;
|
|
3551
|
+
}
|
|
3552
|
+
activeSocket = socket;
|
|
3553
|
+
let lastInbound = Date.now();
|
|
3554
|
+
socket.on("open", () => {
|
|
3555
|
+
const isReconnect = everConnected;
|
|
3556
|
+
everConnected = true;
|
|
3557
|
+
openedAt = Date.now();
|
|
3558
|
+
lastInbound = Date.now();
|
|
3559
|
+
lastFailure = "";
|
|
3560
|
+
if (isReconnect) {
|
|
3561
|
+
diag(import_chalk25.default.dim("reconnected \u2014 requests during the gap were not replayed (browse them with `logs list`)."));
|
|
3562
|
+
} else {
|
|
3563
|
+
diag(import_chalk25.default.dim(interactive ? "[Enter] inspect a request \xB7 [Ctrl+C] stop\n" : "Ctrl+C to stop."));
|
|
3564
|
+
}
|
|
3565
|
+
bindKeys();
|
|
3566
|
+
pingTimer = setInterval(() => {
|
|
3567
|
+
if (Date.now() - lastInbound > LIVENESS_MS) {
|
|
3568
|
+
lastFailure = `no pong for ${Math.round(LIVENESS_MS / 1e3)}s \u2014 connection presumed dead`;
|
|
3569
|
+
try {
|
|
3570
|
+
socket.terminate();
|
|
3571
|
+
} catch {
|
|
3572
|
+
}
|
|
3573
|
+
return;
|
|
3574
|
+
}
|
|
3575
|
+
try {
|
|
3576
|
+
socket.send("ping");
|
|
3577
|
+
} catch {
|
|
3578
|
+
}
|
|
3579
|
+
}, PING_MS);
|
|
3580
|
+
});
|
|
3581
|
+
socket.on("message", (data) => {
|
|
3582
|
+
lastInbound = Date.now();
|
|
3583
|
+
let frame;
|
|
3584
|
+
try {
|
|
3585
|
+
frame = JSON.parse(String(data));
|
|
3586
|
+
} catch {
|
|
3587
|
+
return;
|
|
3588
|
+
}
|
|
3589
|
+
if (frame?.type === "ceiling") {
|
|
3590
|
+
diag(import_chalk25.default.yellow(
|
|
3591
|
+
`\u26A0 volume ceiling \u2014 ${Number(frame.dropped_this_hour) || 0} rows dropped this hour; errors keep streaming.`
|
|
3592
|
+
));
|
|
3593
|
+
return;
|
|
3594
|
+
}
|
|
3595
|
+
if (!frame?.request_id || seen.has(frame.request_id)) return;
|
|
3596
|
+
remember(frame.request_id);
|
|
3597
|
+
if (opts.errorsOnly && Number(frame.status) < 400) return;
|
|
3598
|
+
recent.push(frame);
|
|
3599
|
+
if (recent.length > TAIL_INSPECT_BUFFER) recent.shift();
|
|
3600
|
+
if (paused) {
|
|
3601
|
+
pushHeld({ kind: "row", row: frame });
|
|
3602
|
+
return;
|
|
3603
|
+
}
|
|
3604
|
+
printRow(frame, opts.json);
|
|
3605
|
+
});
|
|
3606
|
+
socket.on("error", (err) => {
|
|
3607
|
+
lastFailure = err?.message || String(err);
|
|
3608
|
+
});
|
|
3609
|
+
socket.on("close", (code, reason) => {
|
|
3610
|
+
if (pingTimer) clearInterval(pingTimer);
|
|
3611
|
+
if (openedAt && Date.now() - openedAt >= STABLE_MS) reconnects = 0;
|
|
3612
|
+
openedAt = 0;
|
|
3613
|
+
const why = String(reason ?? "").trim();
|
|
3614
|
+
if (why) lastFailure = `socket closed: ${why} (${code})`;
|
|
3615
|
+
scheduleReconnect();
|
|
3616
|
+
});
|
|
3617
|
+
}
|
|
3618
|
+
await connect();
|
|
3619
|
+
}
|
|
3620
|
+
function localDayMs(ts) {
|
|
3621
|
+
const d = new Date(ts);
|
|
3622
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
3623
|
+
}
|
|
3624
|
+
function nextLocalDayMs(dayMs) {
|
|
3625
|
+
const d = new Date(dayMs);
|
|
3626
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate() + 1).getTime();
|
|
3627
|
+
}
|
|
3628
|
+
function dayKey(ts) {
|
|
3629
|
+
const d = new Date(ts);
|
|
3630
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
3631
|
+
}
|
|
3632
|
+
function dayLabel(dayMs, now) {
|
|
3633
|
+
const today = localDayMs(now);
|
|
3634
|
+
const n = new Date(now);
|
|
3635
|
+
const yesterday = new Date(n.getFullYear(), n.getMonth(), n.getDate() - 1).getTime();
|
|
3636
|
+
if (dayMs === today) return "Today";
|
|
3637
|
+
if (dayMs === yesterday) return "Yesterday";
|
|
3638
|
+
return new Date(dayMs).toLocaleDateString(void 0, { weekday: "short", month: "short", day: "numeric" });
|
|
3639
|
+
}
|
|
3640
|
+
function groupIntoDays(rows) {
|
|
3641
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
3642
|
+
const order = [];
|
|
3643
|
+
for (const r of [...rows].sort((a, b) => b.ts - a.ts)) {
|
|
3644
|
+
const k = dayKey(r.ts);
|
|
3645
|
+
let b = byKey.get(k);
|
|
3646
|
+
if (!b) {
|
|
3647
|
+
b = { key: k, dayMs: localDayMs(r.ts), rows: [], errors: 0 };
|
|
3648
|
+
byKey.set(k, b);
|
|
3649
|
+
order.push(b);
|
|
3650
|
+
}
|
|
3651
|
+
b.rows.push(r);
|
|
3652
|
+
if (r.status >= 400) b.errors++;
|
|
3653
|
+
}
|
|
3654
|
+
return order;
|
|
3655
|
+
}
|
|
3656
|
+
var SCAN_LIMIT = 200;
|
|
3657
|
+
var PAGE_LIMIT = 50;
|
|
3658
|
+
var JSON_PAGE_LIMIT = 500;
|
|
3659
|
+
var MAX_EMPTY_HOPS = 100;
|
|
3660
|
+
async function fetchPage(fetchWindow3, params, notices) {
|
|
3661
|
+
let cursor = params.cursor;
|
|
3662
|
+
for (let hop = 0; ; hop++) {
|
|
3663
|
+
const res = await fetchWindow3({ ...params, cursor });
|
|
3664
|
+
notices.note(res);
|
|
3665
|
+
const rows = res.rows ?? [];
|
|
3666
|
+
if (rows.length > 0 || !res.next_cursor) {
|
|
3667
|
+
return { rows, nextCursor: res.next_cursor ?? void 0 };
|
|
3668
|
+
}
|
|
3669
|
+
if (hop >= MAX_EMPTY_HOPS) {
|
|
3670
|
+
console.error(import_chalk25.default.yellow("\u26A0 stopped after scanning a long empty span \u2014 older rows may still exist."));
|
|
3671
|
+
return { rows: [], nextCursor: res.next_cursor, fuseTripped: true };
|
|
3672
|
+
}
|
|
3673
|
+
if (hop === 0) console.error(import_chalk25.default.dim("searching older history\u2026"));
|
|
3674
|
+
cursor = res.next_cursor;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
var epipeGuarded = false;
|
|
3678
|
+
function guardStdoutPipe() {
|
|
3679
|
+
if (epipeGuarded) return;
|
|
3680
|
+
epipeGuarded = true;
|
|
3681
|
+
process.stdout.on("error", (err) => {
|
|
3682
|
+
if (err?.code === "EPIPE") process.exit(0);
|
|
3683
|
+
throw err;
|
|
3684
|
+
});
|
|
3685
|
+
}
|
|
3686
|
+
var PLAIN_MAX_ROWS = 200;
|
|
3687
|
+
async function runPlainList(opts, notices) {
|
|
3688
|
+
guardStdoutPipe();
|
|
3689
|
+
const rows = [];
|
|
3690
|
+
let cursor;
|
|
3691
|
+
let more = false;
|
|
3692
|
+
let fuseTripped = false;
|
|
3693
|
+
while (rows.length < PLAIN_MAX_ROWS) {
|
|
3694
|
+
const page = await fetchPage(opts.fetchWindow, {
|
|
3695
|
+
limit: Math.min(PAGE_LIMIT, PLAIN_MAX_ROWS - rows.length),
|
|
3696
|
+
errorsOnly: opts.errorsOnly,
|
|
3697
|
+
cursor
|
|
3698
|
+
}, notices);
|
|
3699
|
+
rows.push(...page.rows);
|
|
3700
|
+
if (page.fuseTripped) {
|
|
3701
|
+
more = true;
|
|
3702
|
+
fuseTripped = true;
|
|
3703
|
+
break;
|
|
3704
|
+
}
|
|
3705
|
+
if (!page.nextCursor) break;
|
|
3706
|
+
cursor = page.nextCursor;
|
|
3707
|
+
if (rows.length >= PLAIN_MAX_ROWS) {
|
|
3708
|
+
more = true;
|
|
3709
|
+
break;
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
if (rows.length === 0) {
|
|
3713
|
+
console.log(import_chalk25.default.dim(fuseTripped ? "Nothing found in the scanned span \u2014 older requests may still exist." : opts.errorsOnly ? "No errors logged yet." : "No requests logged yet."));
|
|
3714
|
+
return;
|
|
3715
|
+
}
|
|
3716
|
+
const now = Date.now();
|
|
3717
|
+
for (const d of groupIntoDays(rows)) {
|
|
3718
|
+
console.log(import_chalk25.default.bold(dayLabel(d.dayMs, now)));
|
|
3719
|
+
for (const r of d.rows) console.log(` ${formatRow(r)}`);
|
|
3720
|
+
console.log("");
|
|
3721
|
+
}
|
|
3722
|
+
const errs = rows.filter((r) => r.status >= 400).length;
|
|
3723
|
+
const tail = fuseTripped ? " \xB7 stopped early (long empty span) \u2014 older requests may still exist" : more ? " \xB7 showing the most recent \u2014 `--json` streams the rest" : "";
|
|
3724
|
+
console.log(import_chalk25.default.dim(
|
|
3725
|
+
`${rows.length} request${rows.length === 1 ? "" : "s"} \xB7 ${errs} error${errs === 1 ? "" : "s"}${tail}`
|
|
3726
|
+
));
|
|
3727
|
+
}
|
|
3728
|
+
async function runHistoryBrowser(opts) {
|
|
3729
|
+
const notices = new WindowNotices();
|
|
3730
|
+
if (opts.json) {
|
|
3731
|
+
guardStdoutPipe();
|
|
3732
|
+
let cursor2;
|
|
3733
|
+
let any = false;
|
|
3734
|
+
for (; ; ) {
|
|
3735
|
+
const page = await fetchPage(opts.fetchWindow, {
|
|
3736
|
+
limit: JSON_PAGE_LIMIT,
|
|
3737
|
+
errorsOnly: opts.errorsOnly,
|
|
3738
|
+
cursor: cursor2
|
|
3739
|
+
}, notices);
|
|
3740
|
+
for (const r of page.rows) console.log(JSON.stringify(r));
|
|
3741
|
+
any = any || page.rows.length > 0;
|
|
3742
|
+
if (page.fuseTripped || !page.nextCursor) {
|
|
3743
|
+
if (!any && !page.fuseTripped) console.error(import_chalk25.default.dim(opts.errorsOnly ? "No errors logged yet." : "No requests logged yet."));
|
|
3744
|
+
return;
|
|
3745
|
+
}
|
|
3746
|
+
cursor2 = page.nextCursor;
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
if (opts.plain || !process.stdout.isTTY || !process.stdin.isTTY) {
|
|
3750
|
+
await runPlainList(opts, notices);
|
|
3751
|
+
return;
|
|
3752
|
+
}
|
|
3753
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
3754
|
+
let scanRows = [];
|
|
3755
|
+
let scanCursor;
|
|
3756
|
+
{
|
|
3757
|
+
const first = await fetchPage(opts.fetchWindow, {
|
|
3758
|
+
limit: SCAN_LIMIT,
|
|
3759
|
+
errorsOnly: opts.errorsOnly
|
|
3760
|
+
}, notices);
|
|
3761
|
+
scanRows = first.rows;
|
|
3762
|
+
scanCursor = first.nextCursor;
|
|
3763
|
+
if (scanRows.length === 0) {
|
|
3764
|
+
console.log(import_chalk25.default.dim(first.fuseTripped ? "Nothing found in the scanned span \u2014 older rows may exist." : opts.errorsOnly ? "No errors logged yet." : "No requests logged yet."));
|
|
3765
|
+
return;
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
let day;
|
|
3769
|
+
let scanFloorUnknown = false;
|
|
3770
|
+
for (; ; ) {
|
|
3771
|
+
const days = groupIntoDays(scanRows);
|
|
3772
|
+
const now2 = Date.now();
|
|
3773
|
+
const oldestKey = days[days.length - 1]?.key;
|
|
3774
|
+
const choices = days.map((d) => ({
|
|
3775
|
+
name: `${dayLabel(d.dayMs, now2).padEnd(12)} ${String(d.rows.length).padStart(4)}${(scanCursor !== void 0 || scanFloorUnknown) && d.key === oldestKey ? "+" : ""} requests${d.errors ? import_chalk25.default.red(` ${d.errors} errors`) : ""}`,
|
|
3776
|
+
value: d.key
|
|
3777
|
+
}));
|
|
3778
|
+
if (scanCursor) choices.push({ name: import_chalk25.default.dim("\u2026 older days"), value: "__older__" });
|
|
3779
|
+
const { chosenDay } = await inquirer3.prompt([{
|
|
3780
|
+
type: "list",
|
|
3781
|
+
name: "chosenDay",
|
|
3782
|
+
message: "Which day?",
|
|
3783
|
+
choices,
|
|
3784
|
+
pageSize: 20
|
|
3785
|
+
}]);
|
|
3786
|
+
if (chosenDay === "__older__") {
|
|
3787
|
+
const more = await fetchPage(opts.fetchWindow, {
|
|
3788
|
+
limit: SCAN_LIMIT,
|
|
3789
|
+
errorsOnly: opts.errorsOnly,
|
|
3790
|
+
cursor: scanCursor
|
|
3791
|
+
}, notices);
|
|
3792
|
+
scanRows = scanRows.concat(more.rows);
|
|
3793
|
+
if (more.fuseTripped) {
|
|
3794
|
+
scanFloorUnknown = true;
|
|
3795
|
+
scanCursor = void 0;
|
|
3796
|
+
} else {
|
|
3797
|
+
scanCursor = more.nextCursor;
|
|
3798
|
+
if (more.rows.length === 0 && !scanCursor) console.log(import_chalk25.default.dim("That was everything \u2014 no older requests."));
|
|
3799
|
+
}
|
|
3800
|
+
continue;
|
|
3801
|
+
}
|
|
3802
|
+
day = days.find((d) => d.key === chosenDay);
|
|
3803
|
+
break;
|
|
3804
|
+
}
|
|
3805
|
+
const since = day.dayMs;
|
|
3806
|
+
const until = nextLocalDayMs(day.dayMs) - 1;
|
|
3807
|
+
const now = Date.now();
|
|
3808
|
+
let cursor;
|
|
3809
|
+
let pageRows = [];
|
|
3810
|
+
let moreCursor;
|
|
3811
|
+
for (; ; ) {
|
|
3812
|
+
const page = await fetchPage(opts.fetchWindow, {
|
|
3813
|
+
limit: PAGE_LIMIT,
|
|
3814
|
+
since,
|
|
3815
|
+
until,
|
|
3816
|
+
errorsOnly: opts.errorsOnly,
|
|
3817
|
+
cursor
|
|
3818
|
+
}, notices);
|
|
3819
|
+
if (page.rows.length === 0 && pageRows.length === 0) {
|
|
3820
|
+
console.log(import_chalk25.default.dim(page.fuseTripped ? "Stopped scanning this day (long empty span) \u2014 older rows in it may exist." : "No requests for this day."));
|
|
3821
|
+
return;
|
|
3822
|
+
}
|
|
3823
|
+
if (page.rows.length === 0) {
|
|
3824
|
+
console.log(import_chalk25.default.dim(page.fuseTripped ? "Stopped scanning further into this day (long empty span) \u2014 older rows in it may exist." : "That was everything for this day."));
|
|
3825
|
+
moreCursor = void 0;
|
|
3826
|
+
} else {
|
|
3827
|
+
pageRows = page.rows;
|
|
3828
|
+
moreCursor = page.fuseTripped ? void 0 : page.nextCursor;
|
|
3829
|
+
}
|
|
3830
|
+
console.log("");
|
|
3831
|
+
const choices = pageRows.map((r) => ({
|
|
3832
|
+
name: formatRow(r),
|
|
3833
|
+
value: r.request_id
|
|
3834
|
+
}));
|
|
3835
|
+
if (moreCursor) choices.push({ name: import_chalk25.default.dim("\u2026 load more"), value: "__more__" });
|
|
3836
|
+
choices.push({ name: import_chalk25.default.dim("quit"), value: "__quit__" });
|
|
3837
|
+
for (; ; ) {
|
|
3838
|
+
const { picked } = await inquirer3.prompt([{
|
|
3839
|
+
type: "list",
|
|
3840
|
+
name: "picked",
|
|
3841
|
+
message: `${dayLabel(day.dayMs, now)} \u2014 select a request for detail`,
|
|
3842
|
+
choices,
|
|
3843
|
+
pageSize: 20
|
|
3844
|
+
}]);
|
|
3845
|
+
if (picked === "__quit__") return;
|
|
3846
|
+
if (picked === "__more__") {
|
|
3847
|
+
cursor = moreCursor;
|
|
3848
|
+
break;
|
|
3849
|
+
}
|
|
3850
|
+
const row = pageRows.find((r) => r.request_id === picked);
|
|
3851
|
+
if (row) printDetail(row, opts.detail);
|
|
3852
|
+
}
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
var NOISE_HEADER = /^(cf-|sec-ch-|sec-fetch-|x-forwarded-|x-real-ip$)/i;
|
|
3856
|
+
var HEADER_PRIORITY = [
|
|
3857
|
+
"authorization",
|
|
3858
|
+
"x-api-key",
|
|
3859
|
+
"content-type",
|
|
3860
|
+
"accept",
|
|
3861
|
+
"x-end-user-id",
|
|
3862
|
+
"user-agent",
|
|
3863
|
+
"host",
|
|
3864
|
+
"origin",
|
|
3865
|
+
"referer",
|
|
3866
|
+
"content-length",
|
|
3867
|
+
"cookie"
|
|
3868
|
+
];
|
|
3869
|
+
var PANE_INDENT = " ".repeat(13);
|
|
3870
|
+
function wrapCells(items, width) {
|
|
3871
|
+
const cols = Math.max(40, width - PANE_INDENT.length);
|
|
3872
|
+
const lines = [];
|
|
3873
|
+
let line = "";
|
|
3874
|
+
for (const it of items) {
|
|
3875
|
+
if (line && line.length + 2 + it.length > cols) {
|
|
3876
|
+
lines.push(line);
|
|
3877
|
+
line = "";
|
|
3878
|
+
}
|
|
3879
|
+
line = line ? `${line} ${it}` : it;
|
|
3880
|
+
}
|
|
3881
|
+
if (line) lines.push(line);
|
|
3882
|
+
return lines.join(`
|
|
3883
|
+
${PANE_INDENT}`);
|
|
3884
|
+
}
|
|
3885
|
+
function renderHeaders(h) {
|
|
3886
|
+
if (!h) return import_chalk25.default.dim("\u2014");
|
|
3887
|
+
const width = process.stdout.columns || 100;
|
|
3888
|
+
if (h.values) {
|
|
3889
|
+
const e = Object.entries(h.values);
|
|
3890
|
+
if (!e.length) return import_chalk25.default.dim("none");
|
|
3891
|
+
const rank = (k) => {
|
|
3892
|
+
const i = HEADER_PRIORITY.indexOf(k.toLowerCase());
|
|
3893
|
+
return i === -1 ? NOISE_HEADER.test(k) ? 999 : 500 : i;
|
|
3894
|
+
};
|
|
3895
|
+
return e.sort((a, b) => rank(a[0]) - rank(b[0])).map(([k, v]) => `${import_chalk25.default.dim(k + ":")} ${v}`).join(`
|
|
3896
|
+
${PANE_INDENT}`);
|
|
3897
|
+
}
|
|
3898
|
+
if (!h.names?.length) return import_chalk25.default.dim("none");
|
|
3899
|
+
const names = [...h.names];
|
|
3900
|
+
const noise = names.filter((n) => NOISE_HEADER.test(n));
|
|
3901
|
+
const shown = names.filter((n) => !NOISE_HEADER.test(n)).sort((a, b) => {
|
|
3902
|
+
const ia = HEADER_PRIORITY.indexOf(a.toLowerCase());
|
|
3903
|
+
const ib = HEADER_PRIORITY.indexOf(b.toLowerCase());
|
|
3904
|
+
return (ia === -1 ? 500 : ia) - (ib === -1 ? 500 : ib) || a.localeCompare(b);
|
|
3905
|
+
});
|
|
3906
|
+
const body = shown.length ? wrapCells(shown, width) : import_chalk25.default.dim("(all forwarding headers)");
|
|
3907
|
+
const tail = noise.length ? `
|
|
3908
|
+
${PANE_INDENT}${import_chalk25.default.dim(`+ ${noise.length} forwarding/client-hint header${noise.length === 1 ? "" : "s"} (cf-*, sec-*)`)}` : "";
|
|
3909
|
+
return `${body}${tail} ${import_chalk25.default.dim("(names only)")}`;
|
|
3910
|
+
}
|
|
3911
|
+
function renderBody(b) {
|
|
3912
|
+
if (!b) return import_chalk25.default.dim("\u2014");
|
|
3913
|
+
const meta = [b.content_type, b.size != null ? `${b.size}B` : null, b.truncated ? "truncated" : null].filter(Boolean).join(" \xB7 ");
|
|
3914
|
+
if (b.not_captured) return import_chalk25.default.dim(`not captured (${b.not_captured})${meta ? " \xB7 " + meta : ""}`);
|
|
3915
|
+
const isShape = b.value === void 0 && b.shape !== void 0;
|
|
3916
|
+
const content = b.value !== void 0 ? b.value : b.shape;
|
|
3917
|
+
if (content === null || content === void 0 || content === "" || content === "empty") {
|
|
3918
|
+
return import_chalk25.default.dim(`empty${meta ? " \xB7 " + meta : ""}`);
|
|
3919
|
+
}
|
|
3920
|
+
const text = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
3921
|
+
const head = isShape ? import_chalk25.default.dim("shape only (values not captured)\n ") : "";
|
|
3922
|
+
return head + text.split("\n").join("\n ");
|
|
3923
|
+
}
|
|
3924
|
+
function pane(title, tone, view, empty) {
|
|
3925
|
+
console.log(` ${tone(title)}`);
|
|
3926
|
+
if (empty !== void 0) {
|
|
3927
|
+
console.log(` ${import_chalk25.default.dim(empty)}`);
|
|
3928
|
+
return;
|
|
3929
|
+
}
|
|
3930
|
+
const v = view ?? {};
|
|
3931
|
+
if (v.url) console.log(` ${import_chalk25.default.dim("target")} ${v.url}`);
|
|
3932
|
+
if (v.status != null) {
|
|
3933
|
+
console.log(` ${import_chalk25.default.dim("status")} ${statusColor(v.status)(String(v.status))}${v.status_text ? import_chalk25.default.dim(" " + v.status_text) : ""}`);
|
|
3934
|
+
}
|
|
3935
|
+
if (v.raw_path) console.log(` ${import_chalk25.default.dim("path")} ${v.raw_path}`);
|
|
3936
|
+
if (v.query && Object.keys(v.query).length) {
|
|
3937
|
+
console.log(` ${import_chalk25.default.dim("query")} ${Object.entries(v.query).map(([k, q]) => `${k}=${q}`).join(" ")}`);
|
|
3938
|
+
}
|
|
3939
|
+
if (v.jwt !== void 0) console.log(` ${import_chalk25.default.dim("jwt")} ${JSON.stringify(v.jwt)}`);
|
|
3940
|
+
console.log(` ${import_chalk25.default.dim("headers")} ${renderHeaders(v.headers)}`);
|
|
3941
|
+
console.log(` ${import_chalk25.default.dim("body")} ${renderBody(v.body)}`);
|
|
3942
|
+
}
|
|
3943
|
+
function printDetail(r, mode = "producer") {
|
|
3944
|
+
const v = r.views;
|
|
3945
|
+
console.log("");
|
|
3946
|
+
console.log(import_chalk25.default.bold(`${r.method} ${r.route}`) + " " + statusColor(r.status)(String(r.status)) + import_chalk25.default.dim(` ${r.duration_ms}ms`));
|
|
3947
|
+
const cost = typeof r.cost_cents === "number" && r.cost_cents > 0 ? ` \xB7 ${r.cost_cents}\xA2` : "";
|
|
3948
|
+
console.log(import_chalk25.default.dim(` request_id ${r.request_id}${r.environment ? ` \xB7 env ${r.environment}` : ""}${r.consumer_id ? ` \xB7 consumer ${r.consumer_id}` : ""}${cost}`));
|
|
3949
|
+
console.log("");
|
|
3950
|
+
if (!v) {
|
|
3951
|
+
console.log(import_chalk25.default.dim(" No view detail captured for this request.\n"));
|
|
3952
|
+
return;
|
|
3953
|
+
}
|
|
3954
|
+
pane("Request (before) \u2014 client \u2192 APIblaze", import_chalk25.default.cyan, v.request);
|
|
3955
|
+
if (mode === "producer") {
|
|
3956
|
+
pane(
|
|
3957
|
+
"Request (after) \u2014 APIblaze \u2192 upstream",
|
|
3958
|
+
import_chalk25.default.cyan,
|
|
3959
|
+
v.forwarded_request,
|
|
3960
|
+
v.forwarded_request ? void 0 : "Request did not reach an upstream."
|
|
3961
|
+
);
|
|
3962
|
+
pane(
|
|
3963
|
+
"Response (before) \u2014 upstream \u2192 APIblaze",
|
|
3964
|
+
import_chalk25.default.magenta,
|
|
3965
|
+
v.upstream_response,
|
|
3966
|
+
v.upstream_response ? void 0 : "No upstream response."
|
|
3967
|
+
);
|
|
3968
|
+
}
|
|
3969
|
+
pane("Response (after) \u2014 APIblaze \u2192 client", import_chalk25.default.magenta, v.response);
|
|
3970
|
+
console.log("");
|
|
3971
|
+
}
|
|
3972
|
+
|
|
3973
|
+
// src/commands/logs.ts
|
|
3974
|
+
async function resolve(projectArg, versionArg, opts) {
|
|
3975
|
+
try {
|
|
3976
|
+
getAccessToken();
|
|
3977
|
+
} catch (err) {
|
|
3978
|
+
const consumerOnly = !loadCredentials() && !!loadConsumer();
|
|
3979
|
+
throw new Error(
|
|
3980
|
+
err.message + (consumerOnly ? "\nYou're signed in as an API Consumer. A proxy's full logs need a Producer login; for your own requests run `apiblaze consumer logs`." : "")
|
|
3981
|
+
);
|
|
3982
|
+
}
|
|
3983
|
+
const { teamId } = await resolveTeam(opts.team);
|
|
3984
|
+
const match = await resolveProject(teamId, projectArg, versionArg);
|
|
3985
|
+
const projectId = match.projectId;
|
|
3986
|
+
const version2 = versionArg || match.apiVersion;
|
|
3987
|
+
let tenant2 = opts.tenant;
|
|
3988
|
+
if (!tenant2) {
|
|
3989
|
+
const res = await admin({
|
|
3990
|
+
path: `/projects/${projectId}/${version2}/tenants`,
|
|
3991
|
+
method: "GET",
|
|
3992
|
+
summary: "list project tenants"
|
|
3993
|
+
});
|
|
3994
|
+
const tenants = res?.tenants ?? [];
|
|
3995
|
+
if (tenants.length === 0) {
|
|
3996
|
+
throw new Error(`No tenants attached to ${match.projectName}@${version2} \u2014 nothing to read logs for.`);
|
|
3997
|
+
} else if (tenants.length === 1) {
|
|
3998
|
+
tenant2 = tenants[0].tenant_name;
|
|
3999
|
+
} else if (opts.json || !process.stdin.isTTY) {
|
|
4000
|
+
throw new Error(`Project has ${tenants.length} tenants \u2014 pass --tenant <slug>. Attached: ${tenants.map((t) => t.tenant_name).join(", ")}`);
|
|
4001
|
+
} else {
|
|
4002
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
4003
|
+
const ans = await inquirer3.prompt([{
|
|
4004
|
+
type: "list",
|
|
4005
|
+
name: "tenant",
|
|
4006
|
+
message: "Which tenant?",
|
|
4007
|
+
choices: tenants.map((t) => ({
|
|
4008
|
+
name: t.display_name ? `${t.display_name} (${t.tenant_name})` : t.tenant_name,
|
|
4009
|
+
value: t.tenant_name
|
|
4010
|
+
}))
|
|
4011
|
+
}]);
|
|
4012
|
+
tenant2 = ans.tenant;
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
return { projectId, projectName: match.projectName, version: version2, tenant: tenant2 };
|
|
4016
|
+
}
|
|
4017
|
+
function otherPlaneHint() {
|
|
4018
|
+
if (loadConsumer()) console.log(import_chalk26.default.dim(" \u2039consumer view: apiblaze consumer logs\u203A"));
|
|
4019
|
+
}
|
|
4020
|
+
function fetchWindow(projectId, version2, tenant2) {
|
|
4021
|
+
return (params) => {
|
|
4022
|
+
const q = new URLSearchParams({ tenant: tenant2, limit: String(params.limit) });
|
|
4023
|
+
if (params.since !== void 0) q.set("since", String(params.since));
|
|
4024
|
+
if (params.until !== void 0) q.set("until", String(params.until));
|
|
4025
|
+
if (params.errorsOnly) q.set("errors_only", "true");
|
|
4026
|
+
if (params.cursor) q.set("cursor", params.cursor);
|
|
4027
|
+
return admin({
|
|
4028
|
+
path: `/projects/${projectId}/${version2}/logs/window?${q.toString()}`,
|
|
4029
|
+
method: "GET",
|
|
4030
|
+
summary: "read log window"
|
|
4031
|
+
});
|
|
4032
|
+
};
|
|
4033
|
+
}
|
|
4034
|
+
async function runLogsTail(projectArg, versionArg, opts) {
|
|
4035
|
+
const { projectId, projectName, version: version2, tenant: tenant2 } = await resolve(projectArg, versionArg, opts);
|
|
4036
|
+
if (!opts.json) {
|
|
4037
|
+
console.log(import_chalk26.default.bold("API Producer logs") + import_chalk26.default.dim(` \xB7 ${projectName}@${version2} \xB7 tenant ${tenant2}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
4038
|
+
console.log(import_chalk26.default.dim("every caller's requests, with cost and upstream detail"));
|
|
4039
|
+
otherPlaneHint();
|
|
4040
|
+
}
|
|
4041
|
+
await runTailSession({
|
|
4042
|
+
mintTicket: async () => {
|
|
4043
|
+
const res = await admin({
|
|
4044
|
+
path: `/projects/${projectId}/${version2}/logs/tail-ticket?tenant=${encodeURIComponent(tenant2)}`,
|
|
4045
|
+
method: "POST",
|
|
4046
|
+
summary: "mint log tail ticket"
|
|
4047
|
+
});
|
|
4048
|
+
if (!res?.ws_url) throw new Error("No ws_url returned by tail-ticket");
|
|
4049
|
+
return res.ws_url;
|
|
4050
|
+
},
|
|
4051
|
+
errorsOnly: opts.errorsOnly,
|
|
4052
|
+
json: opts.json,
|
|
4053
|
+
detail: "producer"
|
|
4054
|
+
});
|
|
4055
|
+
}
|
|
4056
|
+
async function runLogsList(projectArg, versionArg, opts) {
|
|
4057
|
+
const { projectId, projectName, version: version2, tenant: tenant2 } = await resolve(projectArg, versionArg, opts);
|
|
4058
|
+
if (!opts.json) {
|
|
4059
|
+
console.log(import_chalk26.default.bold("API Producer logs") + import_chalk26.default.dim(` \xB7 ${projectName}@${version2} \xB7 tenant ${tenant2}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
4060
|
+
console.log(import_chalk26.default.dim("every caller's requests, with cost and upstream detail"));
|
|
4061
|
+
otherPlaneHint();
|
|
4062
|
+
console.log("");
|
|
4063
|
+
}
|
|
4064
|
+
await runHistoryBrowser({
|
|
4065
|
+
fetchWindow: fetchWindow(projectId, version2, tenant2),
|
|
4066
|
+
errorsOnly: opts.errorsOnly,
|
|
4067
|
+
json: opts.json,
|
|
4068
|
+
plain: opts.plain,
|
|
4069
|
+
detail: "producer"
|
|
4070
|
+
});
|
|
4071
|
+
}
|
|
4072
|
+
|
|
4073
|
+
// src/commands/consumer-logs.ts
|
|
4074
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
4075
|
+
init_auth();
|
|
4076
|
+
var CONSUMER_LOGS_BASE = process.env.APIBLAZE_CONSUMER_LOGS_BASE || "https://logs.apiblaze.com/consumer";
|
|
4077
|
+
function requireConsumer() {
|
|
4078
|
+
const c = loadConsumer();
|
|
4079
|
+
if (!c) {
|
|
4080
|
+
console.error(import_chalk27.default.red("Not logged in as an API Consumer. Run `apiblaze consumer login` first."));
|
|
4081
|
+
if (loadCredentials()) {
|
|
4082
|
+
console.error(import_chalk27.default.dim("You're signed in as an API Producer \u2014 for a proxy's full logs run `apiblaze logs <project>`."));
|
|
4083
|
+
}
|
|
4084
|
+
process.exit(1);
|
|
4085
|
+
}
|
|
4086
|
+
return { creds: c };
|
|
4087
|
+
}
|
|
4088
|
+
async function consumerLogsFetch(session, suffix, init) {
|
|
4089
|
+
const fresh = await validConsumerToken(session.creds) ?? session.creds;
|
|
4090
|
+
session.creds = fresh;
|
|
4091
|
+
const res = await fetch(`${CONSUMER_LOGS_BASE}${suffix}`, {
|
|
4092
|
+
...init,
|
|
4093
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${fresh.accessToken}`, ...init?.headers ?? {} }
|
|
4094
|
+
});
|
|
4095
|
+
let data = null;
|
|
4096
|
+
try {
|
|
4097
|
+
data = await res.json();
|
|
4098
|
+
} catch {
|
|
4099
|
+
}
|
|
4100
|
+
if (res.status === 404 || res.status === 405 || res.status === 501) {
|
|
4101
|
+
throw new Error("Consumer log access is not available on this tenant yet.");
|
|
4102
|
+
}
|
|
4103
|
+
if (res.status === 401) {
|
|
4104
|
+
throw new Error("Your consumer session was rejected \u2014 run `apiblaze consumer login` again.");
|
|
4105
|
+
}
|
|
4106
|
+
if (!res.ok) {
|
|
4107
|
+
const msg = data?.error ?? res.statusText;
|
|
4108
|
+
throw new Error(typeof msg === "string" ? msg : JSON.stringify(msg));
|
|
4109
|
+
}
|
|
4110
|
+
return data;
|
|
4111
|
+
}
|
|
4112
|
+
function otherPlaneHint2() {
|
|
4113
|
+
if (loadCredentials()) console.log(import_chalk27.default.dim(" \u2039producer view: apiblaze logs <project>\u203A"));
|
|
4114
|
+
}
|
|
4115
|
+
function fetchWindow2(session, project) {
|
|
4116
|
+
return (params) => {
|
|
4117
|
+
const q = new URLSearchParams({ limit: String(params.limit) });
|
|
4118
|
+
if (project) q.set("project", project);
|
|
4119
|
+
if (params.since !== void 0) q.set("since", String(params.since));
|
|
4120
|
+
if (params.until !== void 0) q.set("until", String(params.until));
|
|
4121
|
+
if (params.errorsOnly) q.set("errors_only", "true");
|
|
4122
|
+
if (params.cursor) q.set("cursor", params.cursor);
|
|
4123
|
+
return consumerLogsFetch(session, `/logs/window?${q.toString()}`);
|
|
4124
|
+
};
|
|
4125
|
+
}
|
|
4126
|
+
async function runConsumerLogsTail(opts) {
|
|
4127
|
+
const session = requireConsumer();
|
|
4128
|
+
if (!opts.json) {
|
|
4129
|
+
console.log(import_chalk27.default.bold("API Consumer logs") + import_chalk27.default.dim(` \xB7 your own requests on ${session.creds.tenant}${opts.project ? ` \xB7 ${opts.project}` : ""}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
4130
|
+
console.log(import_chalk27.default.dim("cost and upstream details are producer-only and are not sent"));
|
|
4131
|
+
otherPlaneHint2();
|
|
4132
|
+
}
|
|
4133
|
+
await runTailSession({
|
|
4134
|
+
mintTicket: async () => {
|
|
4135
|
+
const q = opts.project ? `?project=${encodeURIComponent(opts.project)}` : "";
|
|
4136
|
+
const res = await consumerLogsFetch(session, `/logs/tail-ticket${q}`, { method: "POST" });
|
|
4137
|
+
if (!res?.ws_url) throw new Error("No ws_url returned by tail-ticket");
|
|
4138
|
+
return res.ws_url;
|
|
4139
|
+
},
|
|
4140
|
+
errorsOnly: opts.errorsOnly,
|
|
4141
|
+
json: opts.json,
|
|
4142
|
+
detail: "consumer"
|
|
4143
|
+
});
|
|
4144
|
+
}
|
|
4145
|
+
async function runConsumerLogsList(opts) {
|
|
4146
|
+
const session = requireConsumer();
|
|
4147
|
+
if (!opts.json) {
|
|
4148
|
+
console.log(import_chalk27.default.bold("API Consumer logs") + import_chalk27.default.dim(` \xB7 your own requests on ${session.creds.tenant}${opts.project ? ` \xB7 ${opts.project}` : ""}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
4149
|
+
console.log(import_chalk27.default.dim("cost and upstream details are producer-only and are not sent"));
|
|
4150
|
+
otherPlaneHint2();
|
|
4151
|
+
console.log("");
|
|
4152
|
+
}
|
|
4153
|
+
await runHistoryBrowser({
|
|
4154
|
+
fetchWindow: fetchWindow2(session, opts.project),
|
|
4155
|
+
errorsOnly: opts.errorsOnly,
|
|
4156
|
+
json: opts.json,
|
|
4157
|
+
plain: opts.plain,
|
|
4158
|
+
detail: "consumer"
|
|
4159
|
+
});
|
|
3229
4160
|
}
|
|
3230
4161
|
|
|
3231
4162
|
// src/commands/config.ts
|
|
3232
|
-
var
|
|
4163
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
3233
4164
|
var import_ora8 = __toESM(require("ora"));
|
|
3234
4165
|
init_admin();
|
|
3235
4166
|
init_resolve();
|
|
@@ -3253,7 +4184,7 @@ async function patchConfig(project, opts, body, summary) {
|
|
|
3253
4184
|
}
|
|
3254
4185
|
async function runTargetSet(project, opts) {
|
|
3255
4186
|
if (!opts.url) {
|
|
3256
|
-
console.error(
|
|
4187
|
+
console.error(import_chalk28.default.red("--url is required."));
|
|
3257
4188
|
process.exit(1);
|
|
3258
4189
|
}
|
|
3259
4190
|
const body = opts.env ? { environments: { [opts.env]: { target: opts.url } } } : { target_url: opts.url };
|
|
@@ -3266,40 +4197,40 @@ async function runThrottleSet(project, opts) {
|
|
|
3266
4197
|
if (opts.quota !== void 0) throttling.proxyQuota = Number(opts.quota);
|
|
3267
4198
|
if (opts.period !== void 0) {
|
|
3268
4199
|
if (!["daily", "weekly", "monthly"].includes(opts.period)) {
|
|
3269
|
-
console.error(
|
|
4200
|
+
console.error(import_chalk28.default.red("--period must be daily, weekly, or monthly."));
|
|
3270
4201
|
process.exit(1);
|
|
3271
4202
|
}
|
|
3272
4203
|
throttling.quotaPeriod = opts.period;
|
|
3273
4204
|
}
|
|
3274
4205
|
if (Object.keys(throttling).length === 0) {
|
|
3275
|
-
console.error(
|
|
4206
|
+
console.error(import_chalk28.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
|
|
3276
4207
|
process.exit(1);
|
|
3277
4208
|
}
|
|
3278
4209
|
await patchConfig(project, opts, { throttling }, `Update throttling ${JSON.stringify(throttling)}`);
|
|
3279
4210
|
}
|
|
3280
4211
|
async function runRename(project, opts) {
|
|
3281
4212
|
if (!opts.displayName) {
|
|
3282
|
-
console.error(
|
|
4213
|
+
console.error(import_chalk28.default.red("--display-name is required."));
|
|
3283
4214
|
process.exit(1);
|
|
3284
4215
|
}
|
|
3285
4216
|
await patchConfig(project, opts, { display_name: opts.displayName }, `Rename \u2192 "${opts.displayName}"`);
|
|
3286
4217
|
}
|
|
3287
4218
|
|
|
3288
4219
|
// src/commands/config-browse.ts
|
|
3289
|
-
var
|
|
4220
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
3290
4221
|
var import_ora16 = __toESM(require("ora"));
|
|
3291
4222
|
init_admin();
|
|
3292
4223
|
init_auth();
|
|
3293
4224
|
init_resolve();
|
|
3294
4225
|
|
|
3295
4226
|
// src/commands/domain.ts
|
|
3296
|
-
var
|
|
4227
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
3297
4228
|
var import_ora9 = __toESM(require("ora"));
|
|
3298
4229
|
init_admin();
|
|
3299
4230
|
init_resolve();
|
|
3300
4231
|
async function runDomainAdd(project, opts) {
|
|
3301
4232
|
if (!opts.domain) {
|
|
3302
|
-
console.error(
|
|
4233
|
+
console.error(import_chalk29.default.red("--domain is required."));
|
|
3303
4234
|
process.exit(1);
|
|
3304
4235
|
}
|
|
3305
4236
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3318,9 +4249,9 @@ async function runDomainAdd(project, opts) {
|
|
|
3318
4249
|
console.log(JSON.stringify(out));
|
|
3319
4250
|
return;
|
|
3320
4251
|
}
|
|
3321
|
-
if (out?.cnameRecord) console.log(` ${
|
|
3322
|
-
if (out?.dcv) console.log(` ${
|
|
3323
|
-
if (out?.id) console.log(
|
|
4252
|
+
if (out?.cnameRecord) console.log(` ${import_chalk29.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
|
|
4253
|
+
if (out?.dcv) console.log(` ${import_chalk29.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
|
|
4254
|
+
if (out?.id) console.log(import_chalk29.default.dim(` domain id: ${out.id}`));
|
|
3324
4255
|
} catch (err) {
|
|
3325
4256
|
spinner.fail("Domain registration failed.");
|
|
3326
4257
|
throw err;
|
|
@@ -3340,14 +4271,14 @@ async function runDomainList(project, opts) {
|
|
|
3340
4271
|
return;
|
|
3341
4272
|
}
|
|
3342
4273
|
if (!domains.length) {
|
|
3343
|
-
console.log(
|
|
4274
|
+
console.log(import_chalk29.default.yellow("No custom domains."));
|
|
3344
4275
|
return;
|
|
3345
4276
|
}
|
|
3346
|
-
for (const d of domains) console.log(` ${
|
|
4277
|
+
for (const d of domains) console.log(` ${import_chalk29.default.bold(d.hostname)} ${import_chalk29.default.dim(d.status ?? "")} ${import_chalk29.default.dim(d.id ?? "")}`);
|
|
3347
4278
|
}
|
|
3348
4279
|
async function runDomainStatus(project, opts) {
|
|
3349
4280
|
if (!opts.id) {
|
|
3350
|
-
console.error(
|
|
4281
|
+
console.error(import_chalk29.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
3351
4282
|
process.exit(1);
|
|
3352
4283
|
}
|
|
3353
4284
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3357,11 +4288,11 @@ async function runDomainStatus(project, opts) {
|
|
|
3357
4288
|
path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}/status`,
|
|
3358
4289
|
summary: `Check custom-domain status`
|
|
3359
4290
|
});
|
|
3360
|
-
console.log(opts.json ? JSON.stringify(out) : ` ${
|
|
4291
|
+
console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk29.default.bold(out?.hostname ?? opts.id)}: ${import_chalk29.default.cyan(out?.status ?? "unknown")}`);
|
|
3361
4292
|
}
|
|
3362
4293
|
async function runDomainRemove(project, opts) {
|
|
3363
4294
|
if (!opts.id) {
|
|
3364
|
-
console.error(
|
|
4295
|
+
console.error(import_chalk29.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
3365
4296
|
process.exit(1);
|
|
3366
4297
|
}
|
|
3367
4298
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3399,7 +4330,7 @@ async function runDomainSetBase(project, opts) {
|
|
|
3399
4330
|
}
|
|
3400
4331
|
|
|
3401
4332
|
// src/commands/tenant.ts
|
|
3402
|
-
var
|
|
4333
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
3403
4334
|
var import_ora12 = __toESM(require("ora"));
|
|
3404
4335
|
init_admin();
|
|
3405
4336
|
init_resolve();
|
|
@@ -3408,20 +4339,20 @@ init_tenant_pick();
|
|
|
3408
4339
|
async function runTenantUse(query, opts) {
|
|
3409
4340
|
const creds = loadCredentials();
|
|
3410
4341
|
if (!creds) {
|
|
3411
|
-
console.error(
|
|
4342
|
+
console.error(import_chalk32.default.red("Not logged in. Run `apiblaze login` first."));
|
|
3412
4343
|
process.exit(1);
|
|
3413
4344
|
}
|
|
3414
4345
|
if (opts.clear) {
|
|
3415
4346
|
delete creds.activeTenant;
|
|
3416
4347
|
saveCredentials(creds);
|
|
3417
|
-
console.log(
|
|
4348
|
+
console.log(import_chalk32.default.green("Tenant scope cleared."));
|
|
3418
4349
|
return;
|
|
3419
4350
|
}
|
|
3420
4351
|
const { teamId } = await resolveTeam(opts.team);
|
|
3421
4352
|
const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
|
|
3422
4353
|
if (!slug) process.exit(1);
|
|
3423
4354
|
saveCredentials({ ...creds, activeTenant: slug });
|
|
3424
|
-
console.log(
|
|
4355
|
+
console.log(import_chalk32.default.green(`Tenant scope set to ${import_chalk32.default.bold(slug)}.`) + import_chalk32.default.dim(" (clear with `apiblaze tenant use --clear`)"));
|
|
3425
4356
|
}
|
|
3426
4357
|
async function runTenantList(opts) {
|
|
3427
4358
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -3438,22 +4369,22 @@ async function runTenantList(opts) {
|
|
|
3438
4369
|
return;
|
|
3439
4370
|
}
|
|
3440
4371
|
if (!tenants.length) {
|
|
3441
|
-
console.log(
|
|
4372
|
+
console.log(import_chalk32.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
|
|
3442
4373
|
return;
|
|
3443
4374
|
}
|
|
3444
4375
|
for (const t of tenants) {
|
|
3445
4376
|
const name = typeof t === "string" ? t : t.tenant_name;
|
|
3446
|
-
const display = typeof t === "string" ? "" :
|
|
3447
|
-
console.log(` ${
|
|
4377
|
+
const display = typeof t === "string" ? "" : import_chalk32.default.dim(` ${t.display_name ?? ""}`);
|
|
4378
|
+
console.log(` ${import_chalk32.default.bold(name)}${display}`);
|
|
3448
4379
|
}
|
|
3449
4380
|
const total = out?.total ?? tenants.length;
|
|
3450
4381
|
if (total > tenants.length) {
|
|
3451
|
-
console.log(
|
|
4382
|
+
console.log(import_chalk32.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
|
|
3452
4383
|
}
|
|
3453
4384
|
}
|
|
3454
4385
|
async function runTenantCreate(opts) {
|
|
3455
4386
|
if (!opts.name) {
|
|
3456
|
-
console.error(
|
|
4387
|
+
console.error(import_chalk32.default.red("--name (display name) is required."));
|
|
3457
4388
|
process.exit(1);
|
|
3458
4389
|
}
|
|
3459
4390
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3465,7 +4396,7 @@ async function runTenantCreate(opts) {
|
|
|
3465
4396
|
body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
|
|
3466
4397
|
summary: `Create tenant "${opts.name}"`
|
|
3467
4398
|
});
|
|
3468
|
-
spinner.succeed(`Created tenant ${
|
|
4399
|
+
spinner.succeed(`Created tenant ${import_chalk32.default.bold(out?.tenant_name ?? opts.name)}.`);
|
|
3469
4400
|
if (opts.json) console.log(JSON.stringify(out));
|
|
3470
4401
|
} catch (err) {
|
|
3471
4402
|
spinner.fail("Tenant create failed.");
|
|
@@ -3474,7 +4405,7 @@ async function runTenantCreate(opts) {
|
|
|
3474
4405
|
}
|
|
3475
4406
|
async function runTenantAttach(project, opts) {
|
|
3476
4407
|
if (!opts.tenant) {
|
|
3477
|
-
console.error(
|
|
4408
|
+
console.error(import_chalk32.default.red("--tenant <slug> is required."));
|
|
3478
4409
|
process.exit(1);
|
|
3479
4410
|
}
|
|
3480
4411
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3502,7 +4433,7 @@ async function runTenantDelete(slug, opts) {
|
|
|
3502
4433
|
{ type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
|
|
3503
4434
|
]);
|
|
3504
4435
|
if (!confirm) {
|
|
3505
|
-
console.log(
|
|
4436
|
+
console.log(import_chalk32.default.dim("Aborted."));
|
|
3506
4437
|
return;
|
|
3507
4438
|
}
|
|
3508
4439
|
}
|
|
@@ -3521,7 +4452,7 @@ async function runTenantDelete(slug, opts) {
|
|
|
3521
4452
|
}
|
|
3522
4453
|
async function runTenantCors(opts) {
|
|
3523
4454
|
if (!opts.tenant) {
|
|
3524
|
-
console.error(
|
|
4455
|
+
console.error(import_chalk32.default.red("--tenant <slug> is required."));
|
|
3525
4456
|
process.exit(1);
|
|
3526
4457
|
}
|
|
3527
4458
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -3543,7 +4474,7 @@ async function runTenantCors(opts) {
|
|
|
3543
4474
|
}
|
|
3544
4475
|
|
|
3545
4476
|
// src/commands/tenant-drill.ts
|
|
3546
|
-
var
|
|
4477
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
3547
4478
|
var import_ora13 = __toESM(require("ora"));
|
|
3548
4479
|
var import_crypto = require("crypto");
|
|
3549
4480
|
init_admin();
|
|
@@ -3574,15 +4505,15 @@ async function validScopedTenant(teamId, query) {
|
|
|
3574
4505
|
const next = { ...creds };
|
|
3575
4506
|
delete next.activeTenant;
|
|
3576
4507
|
saveCredentials2(next);
|
|
3577
|
-
console.log(
|
|
4508
|
+
console.log(import_chalk33.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
|
|
3578
4509
|
return void 0;
|
|
3579
4510
|
}
|
|
3580
4511
|
async function tenantHome(teamId, tenant2) {
|
|
3581
4512
|
const { default: inquirer3 } = await import("inquirer");
|
|
3582
4513
|
const base2 = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
3583
|
-
console.log(
|
|
4514
|
+
console.log(import_chalk33.default.bold(`
|
|
3584
4515
|
Tenant ${tenant2}`));
|
|
3585
|
-
console.log(
|
|
4516
|
+
console.log(import_chalk33.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
3586
4517
|
for (; ; ) {
|
|
3587
4518
|
const spinner = (0, import_ora13.default)("Reading tenant state...").start();
|
|
3588
4519
|
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
@@ -3598,18 +4529,18 @@ Tenant ${tenant2}`));
|
|
|
3598
4529
|
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
3599
4530
|
const nOpaque = opaque?.opaque?.endpoint ? 1 : 0;
|
|
3600
4531
|
const nLogin = nClients + nIssuers + nOpaque;
|
|
3601
|
-
const onOff = (b) => b ?
|
|
4532
|
+
const onOff = (b) => b ? import_chalk33.default.green("on") : import_chalk33.default.dim("off");
|
|
3602
4533
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
3603
4534
|
type: "list",
|
|
3604
4535
|
name: "pick",
|
|
3605
4536
|
message: `Tenant ${tenant2}:`,
|
|
3606
4537
|
pageSize: 12,
|
|
3607
4538
|
choices: [
|
|
3608
|
-
{ name: `Login methods (${nLogin}) ${
|
|
3609
|
-
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${
|
|
3610
|
-
{ name: `Portal admins (${nEmails}) ${
|
|
3611
|
-
new inquirer3.Separator(
|
|
3612
|
-
{ name: `CORS: ${cors?.cors ?
|
|
4539
|
+
{ name: `Login methods (${nLogin}) ${import_chalk33.default.dim("how your consumers sign in")}`, value: "login" },
|
|
4540
|
+
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk33.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
|
|
4541
|
+
{ name: `Portal admins (${nEmails}) ${import_chalk33.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
|
|
4542
|
+
new inquirer3.Separator(import_chalk33.default.dim(" Settings")),
|
|
4543
|
+
{ name: `CORS: ${cors?.cors ? import_chalk33.default.cyan(JSON.stringify(cors.cors)) : import_chalk33.default.dim("(unset)")} ${import_chalk33.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
|
|
3613
4544
|
{ name: "\u2190 Back", value: "back" }
|
|
3614
4545
|
]
|
|
3615
4546
|
}]);
|
|
@@ -3622,7 +4553,7 @@ Tenant ${tenant2}`));
|
|
|
3622
4553
|
case "iam": {
|
|
3623
4554
|
const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
|
|
3624
4555
|
await admin({ method: "PATCH", path: `${base2}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
3625
|
-
console.log(
|
|
4556
|
+
console.log(import_chalk33.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
|
|
3626
4557
|
break;
|
|
3627
4558
|
}
|
|
3628
4559
|
case "cors": {
|
|
@@ -3635,11 +4566,11 @@ Tenant ${tenant2}`));
|
|
|
3635
4566
|
if (v === "") break;
|
|
3636
4567
|
const parsed = v === "null" ? null : safeJson(v);
|
|
3637
4568
|
if (parsed === void 0) {
|
|
3638
|
-
console.log(
|
|
4569
|
+
console.log(import_chalk33.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
3639
4570
|
break;
|
|
3640
4571
|
}
|
|
3641
4572
|
await admin({ method: "PUT", path: `${base2}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
3642
|
-
console.log(
|
|
4573
|
+
console.log(import_chalk33.default.green(" CORS updated."));
|
|
3643
4574
|
break;
|
|
3644
4575
|
}
|
|
3645
4576
|
case "emails":
|
|
@@ -3663,15 +4594,15 @@ async function loginMethodsMenu(teamId, tenant2, base2) {
|
|
|
3663
4594
|
const choices = [];
|
|
3664
4595
|
for (const c of appClients) {
|
|
3665
4596
|
const nP = (c.providers ?? []).length || c.providers_count || 0;
|
|
3666
|
-
choices.push({ name: `${
|
|
4597
|
+
choices.push({ name: `${import_chalk33.default.cyan("APIblaze-hosted")} ${import_chalk33.default.bold(c.name ?? c.clientId)} ${import_chalk33.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk33.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
|
|
3667
4598
|
}
|
|
3668
4599
|
for (const i of issuers) {
|
|
3669
|
-
choices.push({ name: `${
|
|
4600
|
+
choices.push({ name: `${import_chalk33.default.cyan("Your own \u2014 JWT ")} ${import_chalk33.default.bold(i.iss)} ${import_chalk33.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
|
|
3670
4601
|
}
|
|
3671
4602
|
if (opaque?.endpoint) {
|
|
3672
|
-
choices.push({ name: `${
|
|
4603
|
+
choices.push({ name: `${import_chalk33.default.cyan("Your own \u2014 opaque")} ${import_chalk33.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
|
|
3673
4604
|
}
|
|
3674
|
-
if (!choices.length) console.log(
|
|
4605
|
+
if (!choices.length) console.log(import_chalk33.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
|
|
3675
4606
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
3676
4607
|
type: "list",
|
|
3677
4608
|
name: "pick",
|
|
@@ -3711,9 +4642,9 @@ async function addLoginMethod(teamId, tenant2, base2) {
|
|
|
3711
4642
|
message: "How do people log in?",
|
|
3712
4643
|
pageSize: 8,
|
|
3713
4644
|
choices: [
|
|
3714
|
-
{ name: `APIblaze hosted login page ${
|
|
3715
|
-
{ name: `Your own hosted login \u2014 JWT ${
|
|
3716
|
-
{ name: `Your own login \u2014 opaque token ${
|
|
4645
|
+
{ name: `APIblaze hosted login page ${import_chalk33.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
|
|
4646
|
+
{ name: `Your own hosted login \u2014 JWT ${import_chalk33.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
|
|
4647
|
+
{ name: `Your own login \u2014 opaque token ${import_chalk33.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
|
|
3717
4648
|
{ name: "\u2190 Back", value: null }
|
|
3718
4649
|
]
|
|
3719
4650
|
}]);
|
|
@@ -3737,8 +4668,8 @@ function safeJson(s) {
|
|
|
3737
4668
|
async function emailsMenu(base2, emails) {
|
|
3738
4669
|
const { default: inquirer3 } = await import("inquirer");
|
|
3739
4670
|
console.log();
|
|
3740
|
-
if (!emails.length) console.log(
|
|
3741
|
-
for (const e of emails) console.log(` ${
|
|
4671
|
+
if (!emails.length) console.log(import_chalk33.default.dim(" No consumer-admin emails."));
|
|
4672
|
+
for (const e of emails) console.log(` ${import_chalk33.default.bold(e.email ?? e)} ${import_chalk33.default.dim(e.status ?? "")}`);
|
|
3742
4673
|
const { act } = await inquirer3.prompt([{
|
|
3743
4674
|
type: "list",
|
|
3744
4675
|
name: "act",
|
|
@@ -3753,7 +4684,7 @@ async function emailsMenu(base2, emails) {
|
|
|
3753
4684
|
if (act === "add") {
|
|
3754
4685
|
const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
3755
4686
|
await admin({ method: "POST", path: `${base2}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
3756
|
-
console.log(
|
|
4687
|
+
console.log(import_chalk33.default.green(` ${email} added.`));
|
|
3757
4688
|
} else {
|
|
3758
4689
|
const { e } = await inquirer3.prompt([{
|
|
3759
4690
|
type: "list",
|
|
@@ -3763,7 +4694,7 @@ async function emailsMenu(base2, emails) {
|
|
|
3763
4694
|
}]);
|
|
3764
4695
|
if (!e) return;
|
|
3765
4696
|
await admin({ method: "DELETE", path: `${base2}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
3766
|
-
console.log(
|
|
4697
|
+
console.log(import_chalk33.default.green(` ${e} removed.`));
|
|
3767
4698
|
}
|
|
3768
4699
|
}
|
|
3769
4700
|
async function addIssuer(base2) {
|
|
@@ -3784,19 +4715,19 @@ async function addIssuer(base2) {
|
|
|
3784
4715
|
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
3785
4716
|
summary: `Add external issuer ${a.iss.trim()}`
|
|
3786
4717
|
});
|
|
3787
|
-
console.log(
|
|
4718
|
+
console.log(import_chalk33.default.green(" JWT login method saved."));
|
|
3788
4719
|
}
|
|
3789
4720
|
async function issuerHome(base2, issuer) {
|
|
3790
4721
|
const { default: inquirer3 } = await import("inquirer");
|
|
3791
4722
|
console.log(`
|
|
3792
|
-
${
|
|
4723
|
+
${import_chalk33.default.bold(issuer.iss)} ${import_chalk33.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
|
|
3793
4724
|
const { act } = await inquirer3.prompt([{
|
|
3794
4725
|
type: "list",
|
|
3795
4726
|
name: "act",
|
|
3796
4727
|
message: "This JWT login method:",
|
|
3797
4728
|
choices: [
|
|
3798
4729
|
{ name: "Replace it (re-enter details)", value: "edit" },
|
|
3799
|
-
{ name:
|
|
4730
|
+
{ name: import_chalk33.default.red("Delete it"), value: "rm" },
|
|
3800
4731
|
{ name: "\u2190 Back", value: "back" }
|
|
3801
4732
|
]
|
|
3802
4733
|
}]);
|
|
@@ -3812,7 +4743,7 @@ async function issuerHome(base2, issuer) {
|
|
|
3812
4743
|
path: `${base2}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
|
|
3813
4744
|
summary: `Delete issuer ${issuer.iss}`
|
|
3814
4745
|
});
|
|
3815
|
-
console.log(
|
|
4746
|
+
console.log(import_chalk33.default.green(" Deleted."));
|
|
3816
4747
|
}
|
|
3817
4748
|
async function setOpaque(base2, cur) {
|
|
3818
4749
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -3821,19 +4752,19 @@ async function setOpaque(base2, cur) {
|
|
|
3821
4752
|
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
3822
4753
|
]);
|
|
3823
4754
|
await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
3824
|
-
console.log(
|
|
4755
|
+
console.log(import_chalk33.default.green(" Opaque login method set."));
|
|
3825
4756
|
}
|
|
3826
4757
|
async function opaqueHome(base2, cur) {
|
|
3827
4758
|
const { default: inquirer3 } = await import("inquirer");
|
|
3828
4759
|
console.log(`
|
|
3829
|
-
${
|
|
4760
|
+
${import_chalk33.default.bold(cur.endpoint)} ${import_chalk33.default.dim(cur.method ?? "GET")}`);
|
|
3830
4761
|
const { act } = await inquirer3.prompt([{
|
|
3831
4762
|
type: "list",
|
|
3832
4763
|
name: "act",
|
|
3833
4764
|
message: "This opaque login method:",
|
|
3834
4765
|
choices: [
|
|
3835
4766
|
{ name: "Replace it", value: "edit" },
|
|
3836
|
-
{ name:
|
|
4767
|
+
{ name: import_chalk33.default.red("Delete it"), value: "rm" },
|
|
3837
4768
|
{ name: "\u2190 Back", value: "back" }
|
|
3838
4769
|
]
|
|
3839
4770
|
}]);
|
|
@@ -3843,7 +4774,7 @@ async function opaqueHome(base2, cur) {
|
|
|
3843
4774
|
return;
|
|
3844
4775
|
}
|
|
3845
4776
|
await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
3846
|
-
console.log(
|
|
4777
|
+
console.log(import_chalk33.default.green(" Deleted."));
|
|
3847
4778
|
}
|
|
3848
4779
|
async function clientHome(base2, summary) {
|
|
3849
4780
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -3862,13 +4793,13 @@ async function clientHome(base2, summary) {
|
|
|
3862
4793
|
message: `${c.name ?? id}:`,
|
|
3863
4794
|
pageSize: 12,
|
|
3864
4795
|
choices: [
|
|
3865
|
-
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${
|
|
3866
|
-
{ name: `Callback URLs: ${cb.length ?
|
|
3867
|
-
{ name: `Scopes: ${scopes.length ?
|
|
4796
|
+
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk33.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
|
|
4797
|
+
{ name: `Callback URLs: ${cb.length ? import_chalk33.default.cyan(cb.join(", ")) : import_chalk33.default.dim("(none)")}`, value: "callbacks" },
|
|
4798
|
+
{ name: `Scopes: ${scopes.length ? import_chalk33.default.cyan(scopes.join(" ")) : import_chalk33.default.dim("(defaults)")}`, value: "scopes" },
|
|
3868
4799
|
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
3869
4800
|
{ name: "Reveal client secret", value: "secret" },
|
|
3870
4801
|
{ name: "Rotate client secret", value: "rotate" },
|
|
3871
|
-
{ name:
|
|
4802
|
+
{ name: import_chalk33.default.red("Delete this app client"), value: "delete" },
|
|
3872
4803
|
{ name: "\u2190 Back", value: "back" }
|
|
3873
4804
|
]
|
|
3874
4805
|
}]);
|
|
@@ -3881,13 +4812,13 @@ async function clientHome(base2, summary) {
|
|
|
3881
4812
|
case "callbacks": {
|
|
3882
4813
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
3883
4814
|
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
3884
|
-
console.log(
|
|
4815
|
+
console.log(import_chalk33.default.green(" Callbacks updated."));
|
|
3885
4816
|
break;
|
|
3886
4817
|
}
|
|
3887
4818
|
case "scopes": {
|
|
3888
4819
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
3889
4820
|
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
3890
|
-
console.log(
|
|
4821
|
+
console.log(import_chalk33.default.green(" Scopes updated."));
|
|
3891
4822
|
break;
|
|
3892
4823
|
}
|
|
3893
4824
|
case "expiries": {
|
|
@@ -3902,14 +4833,14 @@ async function clientHome(base2, summary) {
|
|
|
3902
4833
|
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
3903
4834
|
summary: "Update token expiries"
|
|
3904
4835
|
});
|
|
3905
|
-
console.log(
|
|
4836
|
+
console.log(import_chalk33.default.green(" Expiries updated."));
|
|
3906
4837
|
break;
|
|
3907
4838
|
}
|
|
3908
4839
|
case "secret": {
|
|
3909
4840
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
3910
4841
|
if (!sure) break;
|
|
3911
4842
|
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
3912
|
-
console.log(` ${
|
|
4843
|
+
console.log(` ${import_chalk33.default.bold("client_secret")}: ${import_chalk33.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
3913
4844
|
break;
|
|
3914
4845
|
}
|
|
3915
4846
|
case "rotate": {
|
|
@@ -3917,14 +4848,14 @@ async function clientHome(base2, summary) {
|
|
|
3917
4848
|
if (!sure) break;
|
|
3918
4849
|
const fresh = randomSecret();
|
|
3919
4850
|
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
3920
|
-
console.log(` New ${
|
|
4851
|
+
console.log(` New ${import_chalk33.default.bold("client_secret")}: ${import_chalk33.default.green(fresh)} ${import_chalk33.default.dim("(store it now)")}`);
|
|
3921
4852
|
break;
|
|
3922
4853
|
}
|
|
3923
4854
|
case "delete": {
|
|
3924
4855
|
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 }]);
|
|
3925
4856
|
if (!sure) break;
|
|
3926
4857
|
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
3927
|
-
console.log(
|
|
4858
|
+
console.log(import_chalk33.default.green(" App client deleted."));
|
|
3928
4859
|
return;
|
|
3929
4860
|
}
|
|
3930
4861
|
}
|
|
@@ -3951,9 +4882,9 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
3951
4882
|
const providers = Array.isArray(raw) ? raw : [];
|
|
3952
4883
|
console.log();
|
|
3953
4884
|
for (const p of providers) {
|
|
3954
|
-
console.log(` ${
|
|
4885
|
+
console.log(` ${import_chalk33.default.bold(p.type)} ${import_chalk33.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
|
|
3955
4886
|
}
|
|
3956
|
-
if (!providers.length) console.log(
|
|
4887
|
+
if (!providers.length) console.log(import_chalk33.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
|
|
3957
4888
|
const { act } = await inquirer3.prompt([{
|
|
3958
4889
|
type: "list",
|
|
3959
4890
|
name: "act",
|
|
@@ -4011,23 +4942,23 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
4011
4942
|
body.targetServerToken = routing;
|
|
4012
4943
|
}
|
|
4013
4944
|
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
4014
|
-
console.log(
|
|
4945
|
+
console.log(import_chalk33.default.green(` ${type} provider added.`));
|
|
4015
4946
|
} else {
|
|
4016
4947
|
const { p } = await inquirer3.prompt([{
|
|
4017
4948
|
type: "list",
|
|
4018
4949
|
name: "p",
|
|
4019
4950
|
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
4020
|
-
choices: [...providers.map((x) => ({ name: `${x.type} ${
|
|
4951
|
+
choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk33.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
|
|
4021
4952
|
}]);
|
|
4022
4953
|
if (!p) continue;
|
|
4023
4954
|
if (act === "rm") {
|
|
4024
4955
|
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 }]);
|
|
4025
4956
|
if (!sure) continue;
|
|
4026
4957
|
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
4027
|
-
console.log(
|
|
4958
|
+
console.log(import_chalk33.default.green(` ${p.type} removed.`));
|
|
4028
4959
|
} else {
|
|
4029
4960
|
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
4030
|
-
console.log(` ${
|
|
4961
|
+
console.log(` ${import_chalk33.default.bold("client_secret")}: ${import_chalk33.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
4031
4962
|
}
|
|
4032
4963
|
}
|
|
4033
4964
|
}
|
|
@@ -4035,7 +4966,7 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
4035
4966
|
|
|
4036
4967
|
// src/commands/spec.ts
|
|
4037
4968
|
var fs7 = __toESM(require("fs"));
|
|
4038
|
-
var
|
|
4969
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
4039
4970
|
var import_ora14 = __toESM(require("ora"));
|
|
4040
4971
|
init_admin();
|
|
4041
4972
|
init_api();
|
|
@@ -4052,14 +4983,14 @@ async function runSpecGet(project, opts) {
|
|
|
4052
4983
|
}
|
|
4053
4984
|
async function runSpecSet(project, opts) {
|
|
4054
4985
|
if (!opts.file) {
|
|
4055
|
-
console.error(
|
|
4986
|
+
console.error(import_chalk34.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
4056
4987
|
process.exit(1);
|
|
4057
4988
|
}
|
|
4058
4989
|
let specContent;
|
|
4059
4990
|
try {
|
|
4060
4991
|
specContent = fs7.readFileSync(opts.file, "utf-8");
|
|
4061
4992
|
} catch {
|
|
4062
|
-
console.error(
|
|
4993
|
+
console.error(import_chalk34.default.red(`Cannot read file: ${opts.file}`));
|
|
4063
4994
|
process.exit(1);
|
|
4064
4995
|
}
|
|
4065
4996
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -4088,23 +5019,23 @@ async function runSpecSet(project, opts) {
|
|
|
4088
5019
|
function printSpecChangeReport(report, projectName) {
|
|
4089
5020
|
const r = report.routes;
|
|
4090
5021
|
if (r) {
|
|
4091
|
-
for (const a of r.added) console.log(
|
|
4092
|
-
for (const d of r.removed) console.log(
|
|
4093
|
-
for (const rn of r.renamed ?? []) console.log(
|
|
4094
|
-
for (const m of r.modified ?? []) console.log(
|
|
5022
|
+
for (const a of r.added) console.log(import_chalk34.default.green(` + ${a}`));
|
|
5023
|
+
for (const d of r.removed) console.log(import_chalk34.default.red(` \u2212 ${d}`));
|
|
5024
|
+
for (const rn of r.renamed ?? []) console.log(import_chalk34.default.yellow(` ~ ${rn.from} \u2192 ${rn.to}`));
|
|
5025
|
+
for (const m of r.modified ?? []) console.log(import_chalk34.default.dim(` \xB1 ${m} (params/schema changed)`));
|
|
4095
5026
|
}
|
|
4096
|
-
if (report.applied?.mcp_regenerated) console.log(
|
|
4097
|
-
if (report.applied?.candidates_cleared) console.log(
|
|
5027
|
+
if (report.applied?.mcp_regenerated) console.log(import_chalk34.default.dim(" MCP catalogue regenerated."));
|
|
5028
|
+
if (report.applied?.candidates_cleared) console.log(import_chalk34.default.dim(` ${report.applied.candidates_cleared} route candidate(s) now documented \u2014 cleared.`));
|
|
4098
5029
|
for (const item of report.attention ?? []) {
|
|
4099
|
-
console.log(
|
|
5030
|
+
console.log(import_chalk34.default.yellow(` \u26A0 ${item.detail}`));
|
|
4100
5031
|
if (item.suggestion?.carry_from && item.suggestion.carry_to) {
|
|
4101
|
-
console.log(
|
|
5032
|
+
console.log(import_chalk34.default.dim(` Fix: apiblaze spec carry ${projectName} --method ${item.suggestion.method} --from "${item.suggestion.carry_from}" --to "${item.suggestion.carry_to}"`));
|
|
4102
5033
|
}
|
|
4103
5034
|
}
|
|
4104
5035
|
}
|
|
4105
5036
|
async function runSpecCarry(project, opts) {
|
|
4106
5037
|
if (!opts.method || !opts.from || !opts.to) {
|
|
4107
|
-
console.error(
|
|
5038
|
+
console.error(import_chalk34.default.red("--method, --from and --to are required."));
|
|
4108
5039
|
process.exit(1);
|
|
4109
5040
|
}
|
|
4110
5041
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -4126,7 +5057,7 @@ async function runSpecCarry(project, opts) {
|
|
|
4126
5057
|
}
|
|
4127
5058
|
async function runSpecDeleteRule(project, opts) {
|
|
4128
5059
|
if (!opts.method || !opts.route) {
|
|
4129
|
-
console.error(
|
|
5060
|
+
console.error(import_chalk34.default.red("--method and --route are required."));
|
|
4130
5061
|
process.exit(1);
|
|
4131
5062
|
}
|
|
4132
5063
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -4146,14 +5077,14 @@ async function runSpecDeleteRule(project, opts) {
|
|
|
4146
5077
|
}
|
|
4147
5078
|
|
|
4148
5079
|
// src/commands/agent.ts
|
|
4149
|
-
var
|
|
5080
|
+
var import_chalk36 = __toESM(require("chalk"));
|
|
4150
5081
|
var import_ora15 = __toESM(require("ora"));
|
|
4151
5082
|
init_auth();
|
|
4152
5083
|
init_admin();
|
|
4153
5084
|
init_resolve();
|
|
4154
5085
|
|
|
4155
5086
|
// src/lib/tools.ts
|
|
4156
|
-
var
|
|
5087
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
4157
5088
|
init_admin();
|
|
4158
5089
|
init_api();
|
|
4159
5090
|
init_resolve();
|
|
@@ -4173,15 +5104,15 @@ var TOOLS = [
|
|
|
4173
5104
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
4174
5105
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
4175
5106
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
4176
|
-
const lines = [` ${
|
|
4177
|
-
if (res.devPortal) lines.push(` ${
|
|
5107
|
+
const lines = [` ${import_chalk35.default.dim("Proxy URL:")} ${import_chalk35.default.bold(url)}`];
|
|
5108
|
+
if (res.devPortal) lines.push(` ${import_chalk35.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
4178
5109
|
const envs = Object.keys(keys);
|
|
4179
5110
|
if (envs.length) {
|
|
4180
|
-
lines.push("", ` ${
|
|
5111
|
+
lines.push("", ` ${import_chalk35.default.bold("API keys")} ${import_chalk35.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
4181
5112
|
const w = Math.max(...envs.map((e) => e.length));
|
|
4182
|
-
for (const env of envs) lines.push(` ${
|
|
5113
|
+
for (const env of envs) lines.push(` ${import_chalk35.default.cyan(env.padEnd(w))} ${import_chalk35.default.green(keys[env])}`);
|
|
4183
5114
|
}
|
|
4184
|
-
if (tryIt) lines.push("", ` ${
|
|
5115
|
+
if (tryIt) lines.push("", ` ${import_chalk35.default.dim("Try it:")}`, ` ${import_chalk35.default.cyan(tryIt)}`);
|
|
4185
5116
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
4186
5117
|
}
|
|
4187
5118
|
},
|
|
@@ -4337,17 +5268,17 @@ function truncate(value, max = 1500) {
|
|
|
4337
5268
|
}
|
|
4338
5269
|
function printCost(resp) {
|
|
4339
5270
|
const line = formatBilling(resp.billing);
|
|
4340
|
-
if (line) console.log(
|
|
5271
|
+
if (line) console.log(import_chalk36.default.magenta(` ${line}`) + import_chalk36.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
|
|
4341
5272
|
}
|
|
4342
5273
|
async function runAgent(opts) {
|
|
4343
5274
|
requireAuth();
|
|
4344
5275
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
4345
5276
|
const { default: inquirer3 } = await import("inquirer");
|
|
4346
|
-
console.log(
|
|
4347
|
-
console.log(
|
|
5277
|
+
console.log(import_chalk36.default.bold("APIblaze agent") + import_chalk36.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
5278
|
+
console.log(import_chalk36.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
4348
5279
|
const history = [];
|
|
4349
5280
|
while (true) {
|
|
4350
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
5281
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk36.default.cyan("you") + " \u203A" }]);
|
|
4351
5282
|
const text = (input ?? "").trim();
|
|
4352
5283
|
if (!text) continue;
|
|
4353
5284
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
@@ -4361,14 +5292,14 @@ async function runAgent(opts) {
|
|
|
4361
5292
|
} catch (err) {
|
|
4362
5293
|
spinner.stop();
|
|
4363
5294
|
if (err instanceof ApiError && err.status === 402) {
|
|
4364
|
-
console.log(
|
|
5295
|
+
console.log(import_chalk36.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
4365
5296
|
break;
|
|
4366
5297
|
}
|
|
4367
5298
|
throw err;
|
|
4368
5299
|
}
|
|
4369
5300
|
history.push({ role: "assistant", content: resp.raw });
|
|
4370
5301
|
printCost(resp);
|
|
4371
|
-
if (resp.reply) console.log(
|
|
5302
|
+
if (resp.reply) console.log(import_chalk36.default.green("agent") + " \u203A " + resp.reply);
|
|
4372
5303
|
if (!resp.action) break;
|
|
4373
5304
|
const tool = findTool(resp.action.tool);
|
|
4374
5305
|
if (!tool) {
|
|
@@ -4393,11 +5324,11 @@ async function runAgent(opts) {
|
|
|
4393
5324
|
}
|
|
4394
5325
|
renderTrace();
|
|
4395
5326
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
4396
|
-
console.log(
|
|
5327
|
+
console.log(import_chalk36.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
4397
5328
|
}
|
|
4398
5329
|
}
|
|
4399
5330
|
}
|
|
4400
|
-
console.log(
|
|
5331
|
+
console.log(import_chalk36.default.dim("\nBye."));
|
|
4401
5332
|
}
|
|
4402
5333
|
|
|
4403
5334
|
// src/commands/config-browse.ts
|
|
@@ -4539,6 +5470,53 @@ var SETTINGS = [
|
|
|
4539
5470
|
desc: "Team-scoped tag grouping projects in the consumer portal",
|
|
4540
5471
|
toPatch: (v) => ({ product_slug: v })
|
|
4541
5472
|
},
|
|
5473
|
+
// Who funds AI chat here (specs/chat_funding.MD). A partial PATCH merges, so
|
|
5474
|
+
// setting one key never resurrects a switch the producer turned off.
|
|
5475
|
+
{
|
|
5476
|
+
key: "chat_sponsorship.enabled",
|
|
5477
|
+
label: "Pay for your users' AI chats",
|
|
5478
|
+
group: "Portal & MCP",
|
|
5479
|
+
type: "boolean",
|
|
5480
|
+
desc: "Every chat message costs money and your users can't pay for it here yet, so you cover it \u2014 for people who sign in, and for API keys you gave the use-premium permission. Off = everyone brings their own AI key, your own tools included",
|
|
5481
|
+
read: (cfg) => cfg.chat_sponsorship?.enabled !== false,
|
|
5482
|
+
toPatch: (v) => ({ chat_sponsorship: { enabled: v } })
|
|
5483
|
+
},
|
|
5484
|
+
{
|
|
5485
|
+
key: "chat_sponsorship.quota_enabled",
|
|
5486
|
+
label: "Limit AI chat per person per day",
|
|
5487
|
+
group: "Portal & MCP",
|
|
5488
|
+
type: "boolean",
|
|
5489
|
+
desc: "Give each person their own daily allowance, reset at midnight UTC. Off = your wallet running out is the only thing that stops it",
|
|
5490
|
+
read: (cfg) => cfg.chat_sponsorship?.quota_enabled !== false,
|
|
5491
|
+
toPatch: (v) => ({ chat_sponsorship: { quota_enabled: v } })
|
|
5492
|
+
},
|
|
5493
|
+
{
|
|
5494
|
+
key: "chat_sponsorship.cap_cents_per_day",
|
|
5495
|
+
label: "AI chat: cents per person per day",
|
|
5496
|
+
group: "Portal & MCP",
|
|
5497
|
+
type: "number",
|
|
5498
|
+
desc: "Most you'll spend on one person per day, in cents you're actually charged (not raw model price). 25 is roughly 25 short chats. Max 5000; 0 blocks everyone",
|
|
5499
|
+
read: (cfg) => cfg.chat_sponsorship?.cap_cents_per_day ?? 25,
|
|
5500
|
+
toPatch: (v) => ({ chat_sponsorship: { cap_cents_per_day: v } })
|
|
5501
|
+
},
|
|
5502
|
+
{
|
|
5503
|
+
key: "chat_sponsorship.max_chats_per_day",
|
|
5504
|
+
label: "AI chat: chats per person per day",
|
|
5505
|
+
group: "Portal & MCP",
|
|
5506
|
+
type: "number",
|
|
5507
|
+
desc: "Chats one person can have per day \u2014 counted even when a chat is cheap, so nobody runs thousands of tiny ones. Max 1000; 0 blocks everyone",
|
|
5508
|
+
read: (cfg) => cfg.chat_sponsorship?.max_chats_per_day ?? 25,
|
|
5509
|
+
toPatch: (v) => ({ chat_sponsorship: { max_chats_per_day: v } })
|
|
5510
|
+
},
|
|
5511
|
+
{
|
|
5512
|
+
key: "chat_sponsorship.max_chars_per_message",
|
|
5513
|
+
label: "AI chat: longest single message",
|
|
5514
|
+
group: "Portal & MCP",
|
|
5515
|
+
type: "number",
|
|
5516
|
+
desc: "2000 characters is about 300 words. Has its own limit because one very long message can cost more than a whole day of short ones. Max 64000",
|
|
5517
|
+
read: (cfg) => cfg.chat_sponsorship?.max_chars_per_message ?? 2e3,
|
|
5518
|
+
toPatch: (v) => ({ chat_sponsorship: { max_chars_per_message: v } })
|
|
5519
|
+
},
|
|
4542
5520
|
// ── Advanced (specs/missing.md): all optional, default-off; null clears ────
|
|
4543
5521
|
{
|
|
4544
5522
|
key: "ip_access.mode",
|
|
@@ -4636,11 +5614,11 @@ function dig(blob, dotted) {
|
|
|
4636
5614
|
}
|
|
4637
5615
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
4638
5616
|
function show(v) {
|
|
4639
|
-
if (v === void 0) return
|
|
4640
|
-
if (v === null) return
|
|
4641
|
-
if (typeof v === "object") return
|
|
4642
|
-
if (typeof v === "boolean") return v ?
|
|
4643
|
-
return
|
|
5617
|
+
if (v === void 0) return import_chalk37.default.dim("(unset)");
|
|
5618
|
+
if (v === null) return import_chalk37.default.dim("null");
|
|
5619
|
+
if (typeof v === "object") return import_chalk37.default.cyan(JSON.stringify(v));
|
|
5620
|
+
if (typeof v === "boolean") return v ? import_chalk37.default.green("on") : import_chalk37.default.red("off");
|
|
5621
|
+
return import_chalk37.default.cyan(String(v));
|
|
4644
5622
|
}
|
|
4645
5623
|
function parseValue(raw) {
|
|
4646
5624
|
if (raw === "true") return true;
|
|
@@ -4682,10 +5660,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
4682
5660
|
}
|
|
4683
5661
|
}
|
|
4684
5662
|
var loginFirst = (what) => {
|
|
4685
|
-
console.log(
|
|
5663
|
+
console.log(import_chalk37.default.yellow(`
|
|
4686
5664
|
Log in first to ${what}.`));
|
|
4687
|
-
console.log(
|
|
4688
|
-
console.log(
|
|
5665
|
+
console.log(import_chalk37.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
|
|
5666
|
+
console.log(import_chalk37.default.dim(" anonymously and want to bring it into your account.\n"));
|
|
4689
5667
|
};
|
|
4690
5668
|
async function runConfig(project, key, value, opts) {
|
|
4691
5669
|
const creds = loadCredentials();
|
|
@@ -4702,9 +5680,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
4702
5680
|
}
|
|
4703
5681
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
4704
5682
|
if (!setting) {
|
|
4705
|
-
console.error(
|
|
4706
|
-
console.error(
|
|
4707
|
-
console.error(
|
|
5683
|
+
console.error(import_chalk37.default.red(`Unknown setting "${key}".`));
|
|
5684
|
+
console.error(import_chalk37.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
|
|
5685
|
+
console.error(import_chalk37.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
|
|
4708
5686
|
process.exit(1);
|
|
4709
5687
|
}
|
|
4710
5688
|
if (value === void 0) {
|
|
@@ -4719,7 +5697,7 @@ async function pickProject(teamId) {
|
|
|
4719
5697
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
4720
5698
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
4721
5699
|
if (!projects.length) {
|
|
4722
|
-
console.error(
|
|
5700
|
+
console.error(import_chalk37.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
4723
5701
|
process.exit(1);
|
|
4724
5702
|
}
|
|
4725
5703
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -4727,7 +5705,7 @@ async function pickProject(teamId) {
|
|
|
4727
5705
|
type: "list",
|
|
4728
5706
|
name: "picked",
|
|
4729
5707
|
message: "Which project?",
|
|
4730
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
5708
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk37.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
4731
5709
|
}]);
|
|
4732
5710
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
4733
5711
|
}
|
|
@@ -4738,25 +5716,25 @@ function printAll(proj2, cfg, json) {
|
|
|
4738
5716
|
console.log(JSON.stringify(out, null, 2));
|
|
4739
5717
|
return;
|
|
4740
5718
|
}
|
|
4741
|
-
console.log(
|
|
5719
|
+
console.log(import_chalk37.default.bold(`
|
|
4742
5720
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
4743
5721
|
`));
|
|
4744
5722
|
for (const group2 of SETTING_GROUPS) {
|
|
4745
|
-
console.log(
|
|
5723
|
+
console.log(import_chalk37.default.bold(group2));
|
|
4746
5724
|
for (const s of SETTINGS.filter((x) => x.group === group2)) {
|
|
4747
|
-
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${
|
|
5725
|
+
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk37.default.dim(s.desc)}`);
|
|
4748
5726
|
}
|
|
4749
5727
|
console.log();
|
|
4750
5728
|
}
|
|
4751
|
-
console.log(
|
|
5729
|
+
console.log(import_chalk37.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
4752
5730
|
}
|
|
4753
5731
|
async function discoveryMenu(project) {
|
|
4754
5732
|
const { default: inquirer3 } = await import("inquirer");
|
|
4755
|
-
console.log(
|
|
5733
|
+
console.log(import_chalk37.default.bold(`
|
|
4756
5734
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
4757
5735
|
`));
|
|
4758
|
-
console.log(
|
|
4759
|
-
console.log(
|
|
5736
|
+
console.log(import_chalk37.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
|
|
5737
|
+
console.log(import_chalk37.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
|
|
4760
5738
|
for (; ; ) {
|
|
4761
5739
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
4762
5740
|
type: "list",
|
|
@@ -4764,13 +5742,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
4764
5742
|
message: "Explore:",
|
|
4765
5743
|
pageSize: 20,
|
|
4766
5744
|
choices: [
|
|
4767
|
-
new inquirer3.Separator(
|
|
5745
|
+
new inquirer3.Separator(import_chalk37.default.bold("\u2014 Settings \u2014")),
|
|
4768
5746
|
...SETTING_GROUPS.map((g) => ({
|
|
4769
|
-
name: `${g} ${
|
|
5747
|
+
name: `${g} ${import_chalk37.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
4770
5748
|
value: { kind: "settings", g }
|
|
4771
5749
|
})),
|
|
4772
|
-
new inquirer3.Separator(
|
|
4773
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
5750
|
+
new inquirer3.Separator(import_chalk37.default.bold("\u2014 Features \u2014")),
|
|
5751
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk37.default.dim(f.desc)}`, value: { kind: "feature", f } })),
|
|
4774
5752
|
new inquirer3.Separator(),
|
|
4775
5753
|
{ name: "Exit", value: { kind: "exit" } }
|
|
4776
5754
|
]
|
|
@@ -4779,24 +5757,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
4779
5757
|
if (pick2.kind === "settings") {
|
|
4780
5758
|
console.log();
|
|
4781
5759
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
4782
|
-
console.log(` ${
|
|
4783
|
-
console.log(` ${
|
|
5760
|
+
console.log(` ${import_chalk37.default.bold(s.label.padEnd(28))} ${import_chalk37.default.dim(s.desc)}`);
|
|
5761
|
+
console.log(` ${import_chalk37.default.dim(" key: " + s.key)}`);
|
|
4784
5762
|
}
|
|
4785
5763
|
loginFirst("view or change these settings");
|
|
4786
5764
|
} else {
|
|
4787
5765
|
const f = pick2.f;
|
|
4788
5766
|
console.log(`
|
|
4789
|
-
${
|
|
5767
|
+
${import_chalk37.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
4790
5768
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
4791
5769
|
}
|
|
4792
5770
|
}
|
|
4793
5771
|
}
|
|
4794
5772
|
async function navigator(proj2, cfg, opts) {
|
|
4795
5773
|
const { default: inquirer3 } = await import("inquirer");
|
|
4796
|
-
console.log(
|
|
5774
|
+
console.log(import_chalk37.default.bold(`
|
|
4797
5775
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
4798
5776
|
`));
|
|
4799
|
-
console.log(
|
|
5777
|
+
console.log(import_chalk37.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
|
|
4800
5778
|
let blob = cfg;
|
|
4801
5779
|
for (; ; ) {
|
|
4802
5780
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
@@ -4805,10 +5783,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4805
5783
|
message: "Where to?",
|
|
4806
5784
|
pageSize: 20,
|
|
4807
5785
|
choices: [
|
|
4808
|
-
new inquirer3.Separator(
|
|
5786
|
+
new inquirer3.Separator(import_chalk37.default.bold("\u2014 Settings \u2014")),
|
|
4809
5787
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
4810
|
-
new inquirer3.Separator(
|
|
4811
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
5788
|
+
new inquirer3.Separator(import_chalk37.default.bold("\u2014 Features \u2014")),
|
|
5789
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk37.default.dim(f.desc)}`, value: { kind: f.go } })),
|
|
4812
5790
|
new inquirer3.Separator(),
|
|
4813
5791
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
4814
5792
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -4848,7 +5826,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
4848
5826
|
}
|
|
4849
5827
|
}
|
|
4850
5828
|
} catch (err) {
|
|
4851
|
-
console.error(
|
|
5829
|
+
console.error(import_chalk37.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
4852
5830
|
}
|
|
4853
5831
|
}
|
|
4854
5832
|
}
|
|
@@ -4862,7 +5840,7 @@ async function settingsGroup(proj2, cfg, group2) {
|
|
|
4862
5840
|
message: group2 + ":",
|
|
4863
5841
|
pageSize: 16,
|
|
4864
5842
|
choices: [
|
|
4865
|
-
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${
|
|
5843
|
+
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk37.default.dim(s2.desc)}`, value: s2 })),
|
|
4866
5844
|
new inquirer3.Separator(),
|
|
4867
5845
|
{ name: "\u2190 Back", value: null }
|
|
4868
5846
|
]
|
|
@@ -4880,7 +5858,7 @@ async function settingsGroup(proj2, cfg, group2) {
|
|
|
4880
5858
|
} else if (s.type === "number") {
|
|
4881
5859
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
4882
5860
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
4883
|
-
console.log(
|
|
5861
|
+
console.log(import_chalk37.default.yellow(" Not a number \u2014 unchanged."));
|
|
4884
5862
|
continue;
|
|
4885
5863
|
}
|
|
4886
5864
|
value = Number(v);
|
|
@@ -4961,7 +5939,7 @@ async function buildCondition(phase) {
|
|
|
4961
5939
|
const items = [];
|
|
4962
5940
|
for (; ; ) {
|
|
4963
5941
|
const a = await inquirer3.prompt([
|
|
4964
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
5942
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk37.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
4965
5943
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
4966
5944
|
"eq",
|
|
4967
5945
|
"neq",
|
|
@@ -4992,7 +5970,7 @@ async function buildCondition(phase) {
|
|
|
4992
5970
|
function showCondition(cond) {
|
|
4993
5971
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
4994
5972
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
4995
|
-
return
|
|
5973
|
+
return import_chalk37.default.dim(` when ${s}`);
|
|
4996
5974
|
}
|
|
4997
5975
|
async function transformsMenu(proj2) {
|
|
4998
5976
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -5001,12 +5979,12 @@ async function transformsMenu(proj2) {
|
|
|
5001
5979
|
const out = await admin({ method: "GET", path: base2, summary: "List transform rules" });
|
|
5002
5980
|
const rules = out?.rules ?? [];
|
|
5003
5981
|
console.log();
|
|
5004
|
-
if (!rules.length) console.log(
|
|
5982
|
+
if (!rules.length) console.log(import_chalk37.default.dim(" No transform rules yet."));
|
|
5005
5983
|
for (const r of rules) {
|
|
5006
5984
|
const a = r.action ?? {};
|
|
5007
5985
|
const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
|
|
5008
|
-
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 ?
|
|
5009
|
-
console.log(` ${r.enabled ?
|
|
5986
|
+
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_chalk37.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
|
|
5987
|
+
console.log(` ${r.enabled ? import_chalk37.default.green("\u25CF") : import_chalk37.default.dim("\u25CB")} ${import_chalk37.default.bold(r.name)} ${import_chalk37.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
|
|
5010
5988
|
}
|
|
5011
5989
|
const { act } = await inquirer3.prompt([{
|
|
5012
5990
|
type: "list",
|
|
@@ -5018,7 +5996,7 @@ async function transformsMenu(proj2) {
|
|
|
5018
5996
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
5019
5997
|
{ name: "Delete a rule", value: "delete" }
|
|
5020
5998
|
] : [],
|
|
5021
|
-
{ name:
|
|
5999
|
+
{ name: import_chalk37.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
5022
6000
|
{ name: "\u2190 Back", value: "back" }
|
|
5023
6001
|
]
|
|
5024
6002
|
}]);
|
|
@@ -5031,11 +6009,11 @@ async function transformsMenu(proj2) {
|
|
|
5031
6009
|
}]);
|
|
5032
6010
|
const body = parseValue(raw);
|
|
5033
6011
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
5034
|
-
console.log(
|
|
6012
|
+
console.log(import_chalk37.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
5035
6013
|
continue;
|
|
5036
6014
|
}
|
|
5037
6015
|
await admin({ method: "POST", path: base2, body, summary: "Create transform rule (raw JSON)" });
|
|
5038
|
-
console.log(
|
|
6016
|
+
console.log(import_chalk37.default.green(" Rule created."));
|
|
5039
6017
|
continue;
|
|
5040
6018
|
}
|
|
5041
6019
|
if (act === "add") {
|
|
@@ -5051,7 +6029,7 @@ async function transformsMenu(proj2) {
|
|
|
5051
6029
|
{ name: "Remove a field", value: "remove" }
|
|
5052
6030
|
] }
|
|
5053
6031
|
]);
|
|
5054
|
-
const fieldHint =
|
|
6032
|
+
const fieldHint = import_chalk37.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
5055
6033
|
let action2;
|
|
5056
6034
|
if (ans.kind === "hardcode") {
|
|
5057
6035
|
const a = await inquirer3.prompt([
|
|
@@ -5098,16 +6076,16 @@ async function transformsMenu(proj2) {
|
|
|
5098
6076
|
type: "list",
|
|
5099
6077
|
name: "rule",
|
|
5100
6078
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
5101
|
-
choices: [...rules.map((r) => ({ name: `${r.name} ${
|
|
6079
|
+
choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk37.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
|
|
5102
6080
|
}]);
|
|
5103
6081
|
if (!rule) continue;
|
|
5104
6082
|
if (act === "toggle") {
|
|
5105
6083
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
5106
6084
|
await admin({ method: "PUT", path: `${base2}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
5107
|
-
console.log(
|
|
6085
|
+
console.log(import_chalk37.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
5108
6086
|
} else {
|
|
5109
6087
|
await admin({ method: "DELETE", path: `${base2}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
5110
|
-
console.log(
|
|
6088
|
+
console.log(import_chalk37.default.green(` ${rule.name} deleted.`));
|
|
5111
6089
|
}
|
|
5112
6090
|
}
|
|
5113
6091
|
}
|
|
@@ -5119,9 +6097,9 @@ async function mappingsMenu(proj2) {
|
|
|
5119
6097
|
const out = await admin({ method: "GET", path: base2, summary: "List mapping tables" });
|
|
5120
6098
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
5121
6099
|
console.log();
|
|
5122
|
-
if (!tables.length) console.log(
|
|
6100
|
+
if (!tables.length) console.log(import_chalk37.default.dim(" No mapping tables yet."));
|
|
5123
6101
|
for (const t of tables) {
|
|
5124
|
-
console.log(` ${
|
|
6102
|
+
console.log(` ${import_chalk37.default.bold(t.name)} ${import_chalk37.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
|
|
5125
6103
|
}
|
|
5126
6104
|
const { act } = await inquirer3.prompt([{
|
|
5127
6105
|
type: "list",
|
|
@@ -5141,11 +6119,11 @@ async function mappingsMenu(proj2) {
|
|
|
5141
6119
|
]);
|
|
5142
6120
|
const entries2 = parseValue(a.entries);
|
|
5143
6121
|
if (!Array.isArray(entries2)) {
|
|
5144
|
-
console.log(
|
|
6122
|
+
console.log(import_chalk37.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
5145
6123
|
continue;
|
|
5146
6124
|
}
|
|
5147
6125
|
await admin({ method: "POST", path: base2, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
5148
|
-
console.log(
|
|
6126
|
+
console.log(import_chalk37.default.green(` Table "${a.name}" created.`));
|
|
5149
6127
|
} else {
|
|
5150
6128
|
const { table } = await inquirer3.prompt([{
|
|
5151
6129
|
type: "list",
|
|
@@ -5155,7 +6133,7 @@ async function mappingsMenu(proj2) {
|
|
|
5155
6133
|
}]);
|
|
5156
6134
|
if (!table) continue;
|
|
5157
6135
|
await admin({ method: "DELETE", path: `${base2}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
5158
|
-
console.log(
|
|
6136
|
+
console.log(import_chalk37.default.green(` ${table.name} deleted.`));
|
|
5159
6137
|
}
|
|
5160
6138
|
}
|
|
5161
6139
|
}
|
|
@@ -5166,14 +6144,14 @@ async function tenantsMenu(proj2, opts) {
|
|
|
5166
6144
|
const out = await admin({ method: "GET", path: base2, summary: "List attached tenants" });
|
|
5167
6145
|
const tenants = out?.tenants ?? [];
|
|
5168
6146
|
console.log();
|
|
5169
|
-
if (!tenants.length) console.log(
|
|
5170
|
-
for (const t of tenants) console.log(` ${
|
|
6147
|
+
if (!tenants.length) console.log(import_chalk37.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
6148
|
+
for (const t of tenants) console.log(` ${import_chalk37.default.bold(t.tenant_name ?? t.name)} ${import_chalk37.default.dim(t.display_name ?? "")}`);
|
|
5171
6149
|
const { act } = await inquirer3.prompt([{
|
|
5172
6150
|
type: "list",
|
|
5173
6151
|
name: "act",
|
|
5174
6152
|
message: "Tenants:",
|
|
5175
6153
|
choices: [
|
|
5176
|
-
{ name: `Manage a tenant\u2026 ${
|
|
6154
|
+
{ name: `Manage a tenant\u2026 ${import_chalk37.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
|
|
5177
6155
|
{ name: "Attach a tenant to this project", value: "attach" },
|
|
5178
6156
|
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
5179
6157
|
{ name: "\u2190 Back", value: "back" }
|
|
@@ -5196,7 +6174,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
5196
6174
|
}]);
|
|
5197
6175
|
if (!t) continue;
|
|
5198
6176
|
await admin({ method: "DELETE", path: `${base2}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
5199
|
-
console.log(
|
|
6177
|
+
console.log(import_chalk37.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
5200
6178
|
}
|
|
5201
6179
|
}
|
|
5202
6180
|
}
|
|
@@ -5239,7 +6217,7 @@ async function specMenu(proj2, opts) {
|
|
|
5239
6217
|
choices: [
|
|
5240
6218
|
{ name: "Print the stored spec", value: "get" },
|
|
5241
6219
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
5242
|
-
{ name:
|
|
6220
|
+
{ name: import_chalk37.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
5243
6221
|
{ name: "\u2190 Back", value: "back" }
|
|
5244
6222
|
]
|
|
5245
6223
|
}]);
|
|
@@ -5247,7 +6225,7 @@ async function specMenu(proj2, opts) {
|
|
|
5247
6225
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
5248
6226
|
else if (act === "refresh") {
|
|
5249
6227
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
5250
|
-
console.log(
|
|
6228
|
+
console.log(import_chalk37.default.green(" Spec refresh triggered."));
|
|
5251
6229
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
5252
6230
|
}
|
|
5253
6231
|
async function agentsMenu(proj2, opts) {
|
|
@@ -5272,7 +6250,7 @@ async function agentsMenu(proj2, opts) {
|
|
|
5272
6250
|
}
|
|
5273
6251
|
|
|
5274
6252
|
// src/commands/group.ts
|
|
5275
|
-
var
|
|
6253
|
+
var import_chalk38 = __toESM(require("chalk"));
|
|
5276
6254
|
var import_ora17 = __toESM(require("ora"));
|
|
5277
6255
|
init_auth();
|
|
5278
6256
|
init_admin();
|
|
@@ -5366,7 +6344,7 @@ async function userByRefOrCreate(t, ref) {
|
|
|
5366
6344
|
if (!ref.includes("@")) throw e;
|
|
5367
6345
|
const created = await t.call("/v1/admin/users", "POST", { email: ref.trim().toLowerCase() });
|
|
5368
6346
|
if (!created?.abz_sub) throw e;
|
|
5369
|
-
console.log(
|
|
6347
|
+
console.log(import_chalk38.default.dim(` ${ref} wasn't a user yet \u2014 created.`));
|
|
5370
6348
|
return { abz_sub: created.abz_sub, email: created.email ?? ref, display_name: null };
|
|
5371
6349
|
}
|
|
5372
6350
|
}
|
|
@@ -5374,11 +6352,11 @@ async function runGroupList(opts) {
|
|
|
5374
6352
|
const t = await resolveTransport(opts);
|
|
5375
6353
|
const groups = await listGroups(t);
|
|
5376
6354
|
if (opts.json) return void console.log(JSON.stringify(groups));
|
|
5377
|
-
if (!groups.length) return void console.log(
|
|
5378
|
-
console.log(
|
|
6355
|
+
if (!groups.length) return void console.log(import_chalk38.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
|
|
6356
|
+
console.log(import_chalk38.default.dim(`Tenant ${t.tenant}:`));
|
|
5379
6357
|
for (const g of groups) {
|
|
5380
|
-
const sealed = g.name === "apiblaze_admins" ?
|
|
5381
|
-
console.log(` ${
|
|
6358
|
+
const sealed = g.name === "apiblaze_admins" ? import_chalk38.default.dim(" (admins \u2014 managed by allowlist)") : "";
|
|
6359
|
+
console.log(` ${import_chalk38.default.bold(g.name)} ${import_chalk38.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
|
|
5382
6360
|
}
|
|
5383
6361
|
}
|
|
5384
6362
|
async function runGroupCreate(name, opts) {
|
|
@@ -5406,7 +6384,7 @@ async function runGroupCreate(name, opts) {
|
|
|
5406
6384
|
const spinner = (0, import_ora17.default)(`Creating group ${name}...`).start();
|
|
5407
6385
|
try {
|
|
5408
6386
|
await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
|
|
5409
|
-
spinner.succeed(`Group ${
|
|
6387
|
+
spinner.succeed(`Group ${import_chalk38.default.bold(name)} created in ${t.tenant}.`);
|
|
5410
6388
|
} catch (e) {
|
|
5411
6389
|
spinner.fail("Create failed.");
|
|
5412
6390
|
throw e;
|
|
@@ -5418,7 +6396,7 @@ async function runGroupDelete(name, opts) {
|
|
|
5418
6396
|
const spinner = (0, import_ora17.default)(`Deleting group ${name}...`).start();
|
|
5419
6397
|
try {
|
|
5420
6398
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
|
|
5421
|
-
spinner.succeed(`Group ${
|
|
6399
|
+
spinner.succeed(`Group ${import_chalk38.default.bold(name)} deleted.`);
|
|
5422
6400
|
} catch (e) {
|
|
5423
6401
|
spinner.fail("Delete failed.");
|
|
5424
6402
|
throw e;
|
|
@@ -5431,11 +6409,11 @@ async function runGroupMembers(name, opts) {
|
|
|
5431
6409
|
const subs = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/subgroups`);
|
|
5432
6410
|
if (opts.json) return void console.log(JSON.stringify({ ...detail, subgroups: subs?.subgroups ?? [] }));
|
|
5433
6411
|
const members = detail?.members ?? [];
|
|
5434
|
-
console.log(
|
|
5435
|
-
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ?
|
|
5436
|
-
if (!members.length) console.log(
|
|
6412
|
+
console.log(import_chalk38.default.bold(`${g.name}`) + import_chalk38.default.dim(` (${t.tenant})`));
|
|
6413
|
+
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk38.default.cyan("group-admin") : import_chalk38.default.dim("member")}`);
|
|
6414
|
+
if (!members.length) console.log(import_chalk38.default.dim(" no members"));
|
|
5437
6415
|
const sg = subs?.subgroups ?? [];
|
|
5438
|
-
if (sg.length) console.log(
|
|
6416
|
+
if (sg.length) console.log(import_chalk38.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
|
|
5439
6417
|
}
|
|
5440
6418
|
async function runGroupAddUser(userRef, groupName, opts) {
|
|
5441
6419
|
const t = await resolveTransport(opts);
|
|
@@ -5445,7 +6423,7 @@ async function runGroupAddUser(userRef, groupName, opts) {
|
|
|
5445
6423
|
const spinner = (0, import_ora17.default)(`Adding ${label2(u)} to ${g.name}...`).start();
|
|
5446
6424
|
try {
|
|
5447
6425
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
|
|
5448
|
-
spinner.succeed(`${
|
|
6426
|
+
spinner.succeed(`${import_chalk38.default.bold(label2(u))} added to ${import_chalk38.default.bold(g.name)} as ${role}.`);
|
|
5449
6427
|
} catch (e) {
|
|
5450
6428
|
spinner.fail("Add failed.");
|
|
5451
6429
|
throw e;
|
|
@@ -5457,7 +6435,7 @@ async function runGroupRemoveUser(userRef, groupName, opts) {
|
|
|
5457
6435
|
const spinner = (0, import_ora17.default)(`Removing ${label2(u)} from ${g.name}...`).start();
|
|
5458
6436
|
try {
|
|
5459
6437
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
|
|
5460
|
-
spinner.succeed(`${
|
|
6438
|
+
spinner.succeed(`${import_chalk38.default.bold(label2(u))} removed from ${import_chalk38.default.bold(g.name)}.`);
|
|
5461
6439
|
} catch (e) {
|
|
5462
6440
|
spinner.fail("Remove failed.");
|
|
5463
6441
|
throw e;
|
|
@@ -5469,7 +6447,7 @@ async function runGroupAddGroup(childName, parentName, opts) {
|
|
|
5469
6447
|
const spinner = (0, import_ora17.default)(`Nesting ${child.name} under ${parent.name}...`).start();
|
|
5470
6448
|
try {
|
|
5471
6449
|
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
|
|
5472
|
-
spinner.succeed(`${
|
|
6450
|
+
spinner.succeed(`${import_chalk38.default.bold(child.name)} is now a subgroup of ${import_chalk38.default.bold(parent.name)}.`);
|
|
5473
6451
|
} catch (e) {
|
|
5474
6452
|
spinner.fail("Nest failed.");
|
|
5475
6453
|
throw e;
|
|
@@ -5481,7 +6459,7 @@ async function runGroupRemoveGroup(childName, parentName, opts) {
|
|
|
5481
6459
|
const spinner = (0, import_ora17.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
|
|
5482
6460
|
try {
|
|
5483
6461
|
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
|
|
5484
|
-
spinner.succeed(`${
|
|
6462
|
+
spinner.succeed(`${import_chalk38.default.bold(child.name)} un-nested from ${import_chalk38.default.bold(parent.name)}.`);
|
|
5485
6463
|
} catch (e) {
|
|
5486
6464
|
spinner.fail("Un-nest failed.");
|
|
5487
6465
|
throw e;
|
|
@@ -5492,18 +6470,18 @@ async function runGroupUsers(opts) {
|
|
|
5492
6470
|
const users = await listUsers(t);
|
|
5493
6471
|
const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
|
|
5494
6472
|
if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
|
|
5495
|
-
console.log(
|
|
5496
|
-
for (const u of users) console.log(` ${label2(u)} ${
|
|
5497
|
-
if (!users.length) console.log(
|
|
6473
|
+
console.log(import_chalk38.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
|
|
6474
|
+
for (const u of users) console.log(` ${label2(u)} ${import_chalk38.default.dim(u.abz_sub)}`);
|
|
6475
|
+
if (!users.length) console.log(import_chalk38.default.dim(" no users yet"));
|
|
5498
6476
|
const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
|
|
5499
6477
|
if (fresh.length) {
|
|
5500
|
-
console.log(
|
|
5501
|
-
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${
|
|
6478
|
+
console.log(import_chalk38.default.dim("Seen in traffic (not yet users):"));
|
|
6479
|
+
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk38.default.dim(o.source_label ?? "")}`);
|
|
5502
6480
|
}
|
|
5503
6481
|
}
|
|
5504
6482
|
|
|
5505
6483
|
// src/commands/key.ts
|
|
5506
|
-
var
|
|
6484
|
+
var import_chalk39 = __toESM(require("chalk"));
|
|
5507
6485
|
var import_ora18 = __toESM(require("ora"));
|
|
5508
6486
|
init_caller();
|
|
5509
6487
|
async function runApikeysMenu(opts) {
|
|
@@ -5533,11 +6511,11 @@ async function runKeyList(opts) {
|
|
|
5533
6511
|
return;
|
|
5534
6512
|
}
|
|
5535
6513
|
if (!keys.length) {
|
|
5536
|
-
console.log(
|
|
6514
|
+
console.log(import_chalk39.default.yellow("No developer keys."));
|
|
5537
6515
|
return;
|
|
5538
6516
|
}
|
|
5539
6517
|
for (const k of keys) {
|
|
5540
|
-
console.log(` ${
|
|
6518
|
+
console.log(` ${import_chalk39.default.bold(k.key_id ?? k.id)} ${import_chalk39.default.dim(k.description ?? "")} ${import_chalk39.default.dim(k.expires_at ?? "no expiry")}`);
|
|
5541
6519
|
}
|
|
5542
6520
|
}
|
|
5543
6521
|
async function runKeyMint(opts) {
|
|
@@ -5559,9 +6537,9 @@ async function runKeyMint(opts) {
|
|
|
5559
6537
|
console.log(JSON.stringify(out));
|
|
5560
6538
|
return;
|
|
5561
6539
|
}
|
|
5562
|
-
console.log(` ${
|
|
5563
|
-
console.log(` ${
|
|
5564
|
-
if (out?.expires_at) console.log(` ${
|
|
6540
|
+
console.log(` ${import_chalk39.default.bold("key_id")}: ${out?.key_id}`);
|
|
6541
|
+
console.log(` ${import_chalk39.default.bold("key")}: ${import_chalk39.default.green(out?.key)} ${import_chalk39.default.dim("(shown once \u2014 store it now)")}`);
|
|
6542
|
+
if (out?.expires_at) console.log(` ${import_chalk39.default.dim("expires:")} ${out.expires_at}`);
|
|
5565
6543
|
} catch (err) {
|
|
5566
6544
|
spinner.fail("Mint failed.");
|
|
5567
6545
|
throw err;
|
|
@@ -5588,7 +6566,7 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
5588
6566
|
init_iam();
|
|
5589
6567
|
|
|
5590
6568
|
// src/commands/admins.ts
|
|
5591
|
-
var
|
|
6569
|
+
var import_chalk40 = __toESM(require("chalk"));
|
|
5592
6570
|
var import_ora19 = __toESM(require("ora"));
|
|
5593
6571
|
init_caller();
|
|
5594
6572
|
function requireTenant(opts) {
|
|
@@ -5616,13 +6594,13 @@ async function runAdminsList(opts) {
|
|
|
5616
6594
|
return;
|
|
5617
6595
|
}
|
|
5618
6596
|
if (!admins2.length) {
|
|
5619
|
-
console.log(
|
|
6597
|
+
console.log(import_chalk40.default.yellow(`No tenant admins for ${tenant2} yet.`));
|
|
5620
6598
|
return;
|
|
5621
6599
|
}
|
|
5622
6600
|
for (const a of admins2) {
|
|
5623
6601
|
const email = typeof a === "string" ? a : a.email;
|
|
5624
6602
|
const flags = [a.active === false ? "pending" : null, a.pinned ? "pinned" : null].filter(Boolean).join(", ");
|
|
5625
|
-
console.log(` ${
|
|
6603
|
+
console.log(` ${import_chalk40.default.bold(email)}${flags ? import_chalk40.default.dim(` (${flags})`) : ""}`);
|
|
5626
6604
|
}
|
|
5627
6605
|
}
|
|
5628
6606
|
async function runAdminsAdd(email, opts) {
|
|
@@ -5637,9 +6615,9 @@ async function runAdminsAdd(email, opts) {
|
|
|
5637
6615
|
body: { email },
|
|
5638
6616
|
summary: `Add ${email} to tenant ${tenant2} admins`
|
|
5639
6617
|
});
|
|
5640
|
-
spinner?.succeed(`${
|
|
6618
|
+
spinner?.succeed(`${import_chalk40.default.bold(email)} is now a tenant admin of ${import_chalk40.default.bold(tenant2)}.`);
|
|
5641
6619
|
if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
|
|
5642
|
-
else console.log(
|
|
6620
|
+
else console.log(import_chalk40.default.dim(" Reload the Users & Groups widget \u2014 access flips from \u201Cpending\u201D to ready."));
|
|
5643
6621
|
} catch (err) {
|
|
5644
6622
|
spinner?.fail("Add failed.");
|
|
5645
6623
|
throw err;
|
|
@@ -5656,7 +6634,7 @@ async function runAdminsRemove(email, opts) {
|
|
|
5656
6634
|
path: `${base(teamId, tenant2)}/${encodeURIComponent(email)}`,
|
|
5657
6635
|
summary: `Remove ${email} from tenant ${tenant2} admins`
|
|
5658
6636
|
});
|
|
5659
|
-
spinner?.succeed(`Removed ${
|
|
6637
|
+
spinner?.succeed(`Removed ${import_chalk40.default.bold(email)} from the tenant admins of ${import_chalk40.default.bold(tenant2)}.`);
|
|
5660
6638
|
} catch (err) {
|
|
5661
6639
|
spinner?.fail("Remove failed.");
|
|
5662
6640
|
throw err;
|
|
@@ -5664,7 +6642,7 @@ async function runAdminsRemove(email, opts) {
|
|
|
5664
6642
|
}
|
|
5665
6643
|
|
|
5666
6644
|
// src/commands/preapprove.ts
|
|
5667
|
-
var
|
|
6645
|
+
var import_chalk41 = __toESM(require("chalk"));
|
|
5668
6646
|
var import_ora20 = __toESM(require("ora"));
|
|
5669
6647
|
|
|
5670
6648
|
// src/lib/preapproval.ts
|
|
@@ -5729,14 +6707,14 @@ async function runPreapprove(who, opts) {
|
|
|
5729
6707
|
const rules = await listPreapprovalRules(tenant2);
|
|
5730
6708
|
if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
|
|
5731
6709
|
if (!rules.length) {
|
|
5732
|
-
console.log(
|
|
5733
|
-
console.log(
|
|
6710
|
+
console.log(import_chalk41.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
|
|
6711
|
+
console.log(import_chalk41.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
|
|
5734
6712
|
return;
|
|
5735
6713
|
}
|
|
5736
|
-
console.log(
|
|
6714
|
+
console.log(import_chalk41.default.dim(`Pre-approved for ${import_chalk41.default.bold(tenant2)}:`));
|
|
5737
6715
|
for (const r of rules) {
|
|
5738
|
-
const tag = r.kind === "domain" ?
|
|
5739
|
-
const grp = r.groups?.length ?
|
|
6716
|
+
const tag = r.kind === "domain" ? import_chalk41.default.cyan("@" + r.value + " (whole domain)") : r.value;
|
|
6717
|
+
const grp = r.groups?.length ? import_chalk41.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
|
|
5740
6718
|
console.log(` ${tag}${grp}`);
|
|
5741
6719
|
}
|
|
5742
6720
|
return;
|
|
@@ -5747,17 +6725,17 @@ async function runPreapprove(who, opts) {
|
|
|
5747
6725
|
if (opts.remove) {
|
|
5748
6726
|
const spinner2 = (0, import_ora20.default)(`Removing ${who} from ${tenant2}\u2026`).start();
|
|
5749
6727
|
const { removed, value } = await removePreapprovalRule(tenant2, who);
|
|
5750
|
-
if (removed) spinner2.succeed(`${
|
|
5751
|
-
else spinner2.warn(`No pre-approval rule for ${
|
|
6728
|
+
if (removed) spinner2.succeed(`${import_chalk41.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
|
|
6729
|
+
else spinner2.warn(`No pre-approval rule for ${import_chalk41.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
|
|
5752
6730
|
return;
|
|
5753
6731
|
}
|
|
5754
6732
|
const spinner = (0, import_ora20.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
|
|
5755
6733
|
try {
|
|
5756
6734
|
const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
|
|
5757
6735
|
const what = kind === "domain" ? `Anyone @${value}` : value;
|
|
5758
|
-
spinner.succeed(`${
|
|
5759
|
-
if (opts.group?.length) console.log(
|
|
5760
|
-
console.log(
|
|
6736
|
+
spinner.succeed(`${import_chalk41.default.bold(what)} can now sign in to ${import_chalk41.default.bold(tenant2)}.`);
|
|
6737
|
+
if (opts.group?.length) console.log(import_chalk41.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
|
|
6738
|
+
console.log(import_chalk41.default.dim(` See the full list: apiblaze preapprove --list`));
|
|
5761
6739
|
} catch (err) {
|
|
5762
6740
|
spinner.fail("Could not add the rule.");
|
|
5763
6741
|
throw err;
|
|
@@ -5768,7 +6746,7 @@ async function runPreapprove(who, opts) {
|
|
|
5768
6746
|
var fs9 = __toESM(require("fs"));
|
|
5769
6747
|
var path6 = __toESM(require("path"));
|
|
5770
6748
|
var crypto2 = __toESM(require("crypto"));
|
|
5771
|
-
var
|
|
6749
|
+
var import_chalk43 = __toESM(require("chalk"));
|
|
5772
6750
|
var import_ora21 = __toESM(require("ora"));
|
|
5773
6751
|
var import_yaml2 = require("yaml");
|
|
5774
6752
|
init_auth();
|
|
@@ -5779,7 +6757,7 @@ init_admin();
|
|
|
5779
6757
|
// src/commands/llm.ts
|
|
5780
6758
|
var fs8 = __toESM(require("fs"));
|
|
5781
6759
|
var path5 = __toESM(require("path"));
|
|
5782
|
-
var
|
|
6760
|
+
var import_chalk42 = __toESM(require("chalk"));
|
|
5783
6761
|
var import_inquirer2 = __toESM(require("inquirer"));
|
|
5784
6762
|
init_auth();
|
|
5785
6763
|
var LLM_PATH = path5.join(getApiblazeDir(), "llm.json");
|
|
@@ -5823,27 +6801,27 @@ async function runLlmSetKey(keyArg, opts) {
|
|
|
5823
6801
|
}
|
|
5824
6802
|
const existing = loadLlmConfig();
|
|
5825
6803
|
saveLlmConfig({ key, provider, model: opts.model ?? existing?.model });
|
|
5826
|
-
console.log(`${
|
|
5827
|
-
console.log(
|
|
5828
|
-
if (opts.model) console.log(
|
|
6804
|
+
console.log(`${import_chalk42.default.green("\u2713")} Saved ${import_chalk42.default.bold(provider)} key ${maskSecret(key)}`);
|
|
6805
|
+
console.log(import_chalk42.default.gray(` Stored locally at ${LLM_PATH} (0600) \u2014 never sent to APIblaze except per chat turn.`));
|
|
6806
|
+
if (opts.model) console.log(import_chalk42.default.gray(` Model: ${opts.model}`));
|
|
5829
6807
|
}
|
|
5830
6808
|
async function runLlmShow() {
|
|
5831
6809
|
const cfg = loadLlmConfig();
|
|
5832
6810
|
if (!cfg) {
|
|
5833
|
-
console.log(
|
|
6811
|
+
console.log(import_chalk42.default.gray("No LLM key set. `apiblaze llm set-key sk-...` to add one (optional \u2014 chat works without it)."));
|
|
5834
6812
|
return;
|
|
5835
6813
|
}
|
|
5836
|
-
console.log(`Provider: ${
|
|
6814
|
+
console.log(`Provider: ${import_chalk42.default.bold(cfg.provider)}`);
|
|
5837
6815
|
console.log(`Key: ${maskSecret(cfg.key)}`);
|
|
5838
6816
|
if (cfg.model) console.log(`Model: ${cfg.model}`);
|
|
5839
|
-
console.log(
|
|
6817
|
+
console.log(import_chalk42.default.gray(`Stored at ${LLM_PATH}`));
|
|
5840
6818
|
}
|
|
5841
6819
|
async function runLlmClearKey() {
|
|
5842
6820
|
try {
|
|
5843
6821
|
fs8.unlinkSync(LLM_PATH);
|
|
5844
|
-
console.log(`${
|
|
6822
|
+
console.log(`${import_chalk42.default.green("\u2713")} Removed local LLM key.`);
|
|
5845
6823
|
} catch {
|
|
5846
|
-
console.log(
|
|
6824
|
+
console.log(import_chalk42.default.gray("No LLM key was set."));
|
|
5847
6825
|
}
|
|
5848
6826
|
}
|
|
5849
6827
|
|
|
@@ -5851,9 +6829,9 @@ async function runLlmClearKey() {
|
|
|
5851
6829
|
init_trace();
|
|
5852
6830
|
init_types();
|
|
5853
6831
|
function fail4(message, hint) {
|
|
5854
|
-
console.error(
|
|
6832
|
+
console.error(import_chalk43.default.red(`
|
|
5855
6833
|
Error: ${message}`));
|
|
5856
|
-
if (hint) console.error(
|
|
6834
|
+
if (hint) console.error(import_chalk43.default.dim(hint));
|
|
5857
6835
|
process.exit(1);
|
|
5858
6836
|
}
|
|
5859
6837
|
function normalizeName2(raw) {
|
|
@@ -6001,7 +6979,7 @@ async function resolveTargetAuth(spec2, opts) {
|
|
|
6001
6979
|
"Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
|
|
6002
6980
|
);
|
|
6003
6981
|
}
|
|
6004
|
-
if (sawOAuth) console.log(
|
|
6982
|
+
if (sawOAuth) console.log(import_chalk43.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
|
|
6005
6983
|
return null;
|
|
6006
6984
|
}
|
|
6007
6985
|
if (candidates.length === 1 && !noneAllowed) return candidates[0];
|
|
@@ -6074,7 +7052,7 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
|
|
|
6074
7052
|
const fresh = await validConsumerToken(existing);
|
|
6075
7053
|
if (fresh) {
|
|
6076
7054
|
if (fresh.accessToken !== existing.accessToken) saveConsumer({ ...fresh, resource });
|
|
6077
|
-
console.log(
|
|
7055
|
+
console.log(import_chalk43.default.dim(` Using your consumer session on ${import_chalk43.default.bold(tenant2)}${fresh.email ? ` (${fresh.email})` : ""}.`));
|
|
6078
7056
|
return fresh;
|
|
6079
7057
|
}
|
|
6080
7058
|
}
|
|
@@ -6093,13 +7071,13 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
|
|
|
6093
7071
|
);
|
|
6094
7072
|
}
|
|
6095
7073
|
const clientId = pick2.client_id ?? pick2.clientId;
|
|
6096
|
-
console.log(`${
|
|
7074
|
+
console.log(`${import_chalk43.default.cyan("\u2192")} This proxy signs consumers in with OAuth \u2014 logging you in to ${import_chalk43.default.bold(tenant2)}...`);
|
|
6097
7075
|
const result = await deviceLogin(clientId, "openid email profile offline_access", ({ verificationUri, userCode }) => {
|
|
6098
7076
|
console.log(`
|
|
6099
|
-
Open: ${
|
|
6100
|
-
console.log(` Code: ${
|
|
7077
|
+
Open: ${import_chalk43.default.underline(verificationUri)}`);
|
|
7078
|
+
console.log(` Code: ${import_chalk43.default.bold(userCode)}
|
|
6101
7079
|
`);
|
|
6102
|
-
console.log(
|
|
7080
|
+
console.log(import_chalk43.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
6103
7081
|
}, resource);
|
|
6104
7082
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
6105
7083
|
const creds = {
|
|
@@ -6115,7 +7093,7 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
|
|
|
6115
7093
|
resource
|
|
6116
7094
|
};
|
|
6117
7095
|
saveConsumer(creds);
|
|
6118
|
-
console.log(` ${
|
|
7096
|
+
console.log(` ${import_chalk43.default.green("\u2714")} Signed in as${creds.email ? ` ${import_chalk43.default.bold(creds.email)}` : " a consumer"} on ${tenant2}.`);
|
|
6119
7097
|
return creds;
|
|
6120
7098
|
}
|
|
6121
7099
|
async function cpPost(anon, path8, body, summary) {
|
|
@@ -6144,13 +7122,18 @@ async function provision(spec2, target, opts) {
|
|
|
6144
7122
|
let name = opts.name ? base2 : `${base2}${salt()}`;
|
|
6145
7123
|
const access = anon ? "open" : opts.access === "open" ? "open" : "invite";
|
|
6146
7124
|
if (anon && opts.access === "invite") {
|
|
6147
|
-
console.log(
|
|
7125
|
+
console.log(import_chalk43.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`."));
|
|
6148
7126
|
}
|
|
6149
7127
|
const DUAL_AUTH = {
|
|
6150
7128
|
mode: "authenticate",
|
|
6151
7129
|
methods: ["api_key", "jwt"],
|
|
6152
7130
|
...access === "invite" ? { preapproved_users_only: true } : {}
|
|
6153
7131
|
};
|
|
7132
|
+
const chosenTenant = loggedIn ? await chooseTenantForCreate(loadCredentials()?.teamId, name, {
|
|
7133
|
+
tenant: opts.tenant,
|
|
7134
|
+
yes: opts.yes,
|
|
7135
|
+
json: opts.json
|
|
7136
|
+
}) : void 0;
|
|
6154
7137
|
const spinner = (0, import_ora21.default)("Provisioning an api_key proxy...").start();
|
|
6155
7138
|
let result;
|
|
6156
7139
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
@@ -6163,6 +7146,7 @@ async function provision(spec2, target, opts) {
|
|
|
6163
7146
|
auth_type: "api_key",
|
|
6164
7147
|
requests_auth: DUAL_AUTH,
|
|
6165
7148
|
team_id: creds.teamId,
|
|
7149
|
+
...chosenTenant ? { tenant: chosenTenant } : {},
|
|
6166
7150
|
...opts.apiversion ? { api_version: opts.apiversion } : {}
|
|
6167
7151
|
});
|
|
6168
7152
|
} else {
|
|
@@ -6237,7 +7221,7 @@ async function provision(spec2, target, opts) {
|
|
|
6237
7221
|
try {
|
|
6238
7222
|
await addPreapprovalRule(tenant2, email);
|
|
6239
7223
|
} catch {
|
|
6240
|
-
console.log(
|
|
7224
|
+
console.log(import_chalk43.default.dim(` (Could not auto-approve your email for sign-in \u2014 add it later: apiblaze preapprove ${email})`));
|
|
6241
7225
|
}
|
|
6242
7226
|
}
|
|
6243
7227
|
}
|
|
@@ -6281,7 +7265,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
6281
7265
|
throw err;
|
|
6282
7266
|
}
|
|
6283
7267
|
if (out && out.reused === true) {
|
|
6284
|
-
console.log(
|
|
7268
|
+
console.log(import_chalk43.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
|
|
6285
7269
|
} else if (out && out.changed === true && out.previous_spec_hash) {
|
|
6286
7270
|
const interactive = !!process.stdin.isTTY && !opts.yes;
|
|
6287
7271
|
if (interactive) {
|
|
@@ -6289,7 +7273,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
6289
7273
|
const { go } = await inquirer3.prompt([
|
|
6290
7274
|
{ type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
|
|
6291
7275
|
]);
|
|
6292
|
-
if (!go) console.log(
|
|
7276
|
+
if (!go) console.log(import_chalk43.default.dim(" Keeping the existing MCP catalogue."));
|
|
6293
7277
|
}
|
|
6294
7278
|
}
|
|
6295
7279
|
}
|
|
@@ -6330,15 +7314,15 @@ var revealAuth = false;
|
|
|
6330
7314
|
function renderToolEvents(events, cred) {
|
|
6331
7315
|
for (const e of events ?? []) {
|
|
6332
7316
|
const ok = typeof e.status === "number" ? e.status < 400 : String(e.status).toLowerCase() === "ok";
|
|
6333
|
-
const mark = ok ?
|
|
6334
|
-
console.log(` ${mark} ${
|
|
7317
|
+
const mark = ok ? import_chalk43.default.green("\u2713") : import_chalk43.default.red("\u2717");
|
|
7318
|
+
console.log(` ${mark} ${import_chalk43.default.cyan(e.name)} ${import_chalk43.default.dim(`(${e.status}, ${e.ms}ms)`)}`);
|
|
6335
7319
|
if (isVerbose() && e.method && e.url) {
|
|
6336
|
-
console.log(
|
|
7320
|
+
console.log(import_chalk43.default.dim(` curl -sS -X ${e.method} '${e.url}'${cred ? " \\" : ""}`));
|
|
6337
7321
|
if (cred) {
|
|
6338
7322
|
const shown = revealAuth ? cred.value : maskKey(cred.value);
|
|
6339
7323
|
const keyLine = ` -H '${cred.header}: ${shown}'`;
|
|
6340
|
-
const hint = revealAuth ? "" :
|
|
6341
|
-
console.log(
|
|
7324
|
+
const hint = revealAuth ? "" : import_chalk43.default.yellow(" \u2190 /showauth will reveal this");
|
|
7325
|
+
console.log(import_chalk43.default.dim(keyLine) + hint);
|
|
6342
7326
|
}
|
|
6343
7327
|
}
|
|
6344
7328
|
}
|
|
@@ -6359,20 +7343,20 @@ function renderToolResults(delta, events) {
|
|
|
6359
7343
|
}
|
|
6360
7344
|
})();
|
|
6361
7345
|
const lines = pretty.split("\n");
|
|
6362
|
-
console.log(
|
|
7346
|
+
console.log(import_chalk43.default.dim(" response:"));
|
|
6363
7347
|
for (const line of lines.slice(0, cap)) {
|
|
6364
|
-
console.log(
|
|
7348
|
+
console.log(import_chalk43.default.dim(` ${line}`));
|
|
6365
7349
|
}
|
|
6366
|
-
if (lines.length > cap) console.log(
|
|
7350
|
+
if (lines.length > cap) console.log(import_chalk43.default.dim(` \u2026${lines.length - cap} more lines`));
|
|
6367
7351
|
}
|
|
6368
7352
|
}
|
|
6369
7353
|
function billingLine(billing) {
|
|
6370
7354
|
if (!billing || typeof billing.cents !== "number") return null;
|
|
6371
7355
|
if (typeof billing.free_turns_remaining === "number") return null;
|
|
6372
7356
|
const usd = (billing.cents / 100).toFixed(Math.abs(billing.cents - Math.round(billing.cents)) < 1e-9 ? 2 : 4);
|
|
6373
|
-
let line =
|
|
7357
|
+
let line = import_chalk43.default.magenta(` \u{1F4B3} $${usd}`) + import_chalk43.default.dim(billing.model ? ` \xB7 ${billing.model}` : "");
|
|
6374
7358
|
if (typeof billing.credits_remaining === "number") {
|
|
6375
|
-
line +=
|
|
7359
|
+
line += import_chalk43.default.dim(` \xB7 balance $${(billing.credits_remaining / 100).toFixed(2)}`);
|
|
6376
7360
|
}
|
|
6377
7361
|
return line;
|
|
6378
7362
|
}
|
|
@@ -6380,21 +7364,21 @@ function freeBudgetWarning(billing, anon) {
|
|
|
6380
7364
|
if (!anon || !billing) return null;
|
|
6381
7365
|
if (typeof billing.free_turns_remaining === "number") {
|
|
6382
7366
|
const left2 = billing.free_turns_remaining;
|
|
6383
|
-
if (left2 <= 0) return
|
|
6384
|
-
return
|
|
7367
|
+
if (left2 <= 0) return import_chalk43.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
|
|
7368
|
+
return import_chalk43.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
|
|
6385
7369
|
}
|
|
6386
7370
|
if (typeof billing.free_remaining_cents !== "number") return null;
|
|
6387
7371
|
const perTurn = Math.max(billing.cents || 0, 0.02);
|
|
6388
7372
|
const left = Math.floor(billing.free_remaining_cents / perTurn);
|
|
6389
7373
|
if (left > 8) return null;
|
|
6390
|
-
if (left <= 0) return
|
|
6391
|
-
return
|
|
7374
|
+
if (left <= 0) return import_chalk43.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
7375
|
+
return import_chalk43.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
6392
7376
|
}
|
|
6393
7377
|
function printAssistant(delta) {
|
|
6394
7378
|
for (let i = delta.length - 1; i >= 0; i--) {
|
|
6395
7379
|
const m = delta[i];
|
|
6396
7380
|
if (m && m.role === "assistant" && typeof m.content === "string" && m.content.trim()) {
|
|
6397
|
-
console.log("\n" +
|
|
7381
|
+
console.log("\n" + import_chalk43.default.green("assistant \u203A ") + m.content + "\n");
|
|
6398
7382
|
return;
|
|
6399
7383
|
}
|
|
6400
7384
|
}
|
|
@@ -6420,7 +7404,7 @@ async function replTurn(p, messages, userText) {
|
|
|
6420
7404
|
});
|
|
6421
7405
|
} catch (err) {
|
|
6422
7406
|
spinner.fail("Network error.");
|
|
6423
|
-
console.log(
|
|
7407
|
+
console.log(import_chalk43.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
6424
7408
|
return;
|
|
6425
7409
|
}
|
|
6426
7410
|
let data = null;
|
|
@@ -6440,9 +7424,9 @@ async function replTurn(p, messages, userText) {
|
|
|
6440
7424
|
if (res.status === 401) {
|
|
6441
7425
|
const reason = data && (data.error || data.message) || null;
|
|
6442
7426
|
const usingKey = !p.consumerAuth;
|
|
6443
|
-
console.log(
|
|
7427
|
+
console.log(import_chalk43.default.red(` The proxy rejected the request (401)${reason ? `: ${reason}` : "."}`));
|
|
6444
7428
|
console.log(
|
|
6445
|
-
|
|
7429
|
+
import_chalk43.default.dim(
|
|
6446
7430
|
usingKey ? " Sent an API key. If the key was revoked, re-run `apiblaze apichat` to re-provision." : " Sent your consumer OAuth token. Re-run `apiblaze apichat` to sign in again."
|
|
6447
7431
|
)
|
|
6448
7432
|
);
|
|
@@ -6450,7 +7434,7 @@ async function replTurn(p, messages, userText) {
|
|
|
6450
7434
|
}
|
|
6451
7435
|
if (!res.ok || !data) {
|
|
6452
7436
|
const err = data && data.error || `HTTP ${res.status}`;
|
|
6453
|
-
console.log(
|
|
7437
|
+
console.log(import_chalk43.default.red(` Chat error: ${err}`));
|
|
6454
7438
|
return;
|
|
6455
7439
|
}
|
|
6456
7440
|
if (Array.isArray(data.delta)) {
|
|
@@ -6471,25 +7455,25 @@ async function replTurn(p, messages, userText) {
|
|
|
6471
7455
|
if (warn) console.log(warn);
|
|
6472
7456
|
if (!data.continue) return;
|
|
6473
7457
|
}
|
|
6474
|
-
console.log(
|
|
7458
|
+
console.log(import_chalk43.default.dim(" (stopped after several tool rounds \u2014 ask again to continue)"));
|
|
6475
7459
|
}
|
|
6476
7460
|
function renderUpsell(p, upsell) {
|
|
6477
7461
|
const loggedIn = !!loadCredentials();
|
|
6478
7462
|
if (upsell.reason === "CAPPED" && !loggedIn) {
|
|
6479
|
-
console.log("\n" +
|
|
6480
|
-
console.log(
|
|
7463
|
+
console.log("\n" + import_chalk43.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
|
|
7464
|
+
console.log(import_chalk43.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
|
|
6481
7465
|
console.log();
|
|
6482
7466
|
return;
|
|
6483
7467
|
}
|
|
6484
|
-
console.log("\n" +
|
|
6485
|
-
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "PAUSED") {
|
|
7468
|
+
console.log("\n" + import_chalk43.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
7469
|
+
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "QUOTA" || upsell.reason === "PROXY_CAP" || upsell.reason === "PAUSED" || upsell.reason === "BYO_REQUIRED") {
|
|
6486
7470
|
if (!loggedIn) {
|
|
6487
|
-
console.log(
|
|
7471
|
+
console.log(import_chalk43.default.dim(" Options: `/login` for more free chats and requests, or `apiblaze llm set-key` to bring your own model key."));
|
|
6488
7472
|
} else {
|
|
6489
|
-
console.log(
|
|
7473
|
+
console.log(import_chalk43.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
|
|
6490
7474
|
}
|
|
6491
7475
|
} else if (upsell.reason === "INFLIGHT") {
|
|
6492
|
-
console.log(
|
|
7476
|
+
console.log(import_chalk43.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
6493
7477
|
}
|
|
6494
7478
|
console.log();
|
|
6495
7479
|
}
|
|
@@ -6582,9 +7566,9 @@ async function openServerProxy(project) {
|
|
|
6582
7566
|
let consumerAuth = false;
|
|
6583
7567
|
if (acceptsApiKey) {
|
|
6584
7568
|
if (!dpKey) {
|
|
6585
|
-
console.log(
|
|
7569
|
+
console.log(import_chalk43.default.dim(` Minting an API key for tenant ${import_chalk43.default.bold(tenant2)} to query project ${import_chalk43.default.bold(project.projectName)}\u2026`));
|
|
6586
7570
|
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
6587
|
-
console.log(` ${
|
|
7571
|
+
console.log(` ${import_chalk43.default.green("\u2714")} API key: ${import_chalk43.default.dim(maskKey(dpKey))}`);
|
|
6588
7572
|
}
|
|
6589
7573
|
} else {
|
|
6590
7574
|
consumerAuth = true;
|
|
@@ -6613,9 +7597,9 @@ async function openServerProxy(project) {
|
|
|
6613
7597
|
const spec2 = raw && (raw.spec ?? raw);
|
|
6614
7598
|
spinner.stop();
|
|
6615
7599
|
if (!spec2 || !(spec2.paths || spec2.openapi)) {
|
|
6616
|
-
console.log(
|
|
7600
|
+
console.log(import_chalk43.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
|
|
6617
7601
|
} else {
|
|
6618
|
-
console.log(
|
|
7602
|
+
console.log(import_chalk43.default.dim(" Using the proxy's existing MCP catalogue (`apiblaze mcp` to rebuild it)."));
|
|
6619
7603
|
}
|
|
6620
7604
|
} catch (err) {
|
|
6621
7605
|
spinner.fail("Could not open the proxy.");
|
|
@@ -6671,7 +7655,7 @@ async function noArgsMenu(opts) {
|
|
|
6671
7655
|
const me = loadCredentials()?.apiblazeUserId;
|
|
6672
7656
|
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
6673
7657
|
const choices = saved.map((a) => ({
|
|
6674
|
-
name: `Chat with ${
|
|
7658
|
+
name: `Chat with ${import_chalk43.default.bold(a.name)} ${import_chalk43.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
|
|
6675
7659
|
value: { type: "existing", a }
|
|
6676
7660
|
}));
|
|
6677
7661
|
const creds = loadCredentials();
|
|
@@ -6681,14 +7665,14 @@ async function noArgsMenu(opts) {
|
|
|
6681
7665
|
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
6682
7666
|
for (const pr of proxies) {
|
|
6683
7667
|
choices.push({
|
|
6684
|
-
name: `Chat with ${
|
|
7668
|
+
name: `Chat with ${import_chalk43.default.bold(pr.projectName)} ${import_chalk43.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
6685
7669
|
value: { type: "server", project: pr }
|
|
6686
7670
|
});
|
|
6687
7671
|
}
|
|
6688
7672
|
} catch {
|
|
6689
7673
|
}
|
|
6690
7674
|
}
|
|
6691
|
-
choices.push({ name:
|
|
7675
|
+
choices.push({ name: import_chalk43.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
6692
7676
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
6693
7677
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
6694
7678
|
]);
|
|
@@ -6791,36 +7775,36 @@ async function noArgsMenu(opts) {
|
|
|
6791
7775
|
async function runRepl(p, initialMessages) {
|
|
6792
7776
|
const { default: inquirer3 } = await import("inquirer");
|
|
6793
7777
|
const messages = initialMessages && initialMessages.length ? initialMessages.slice() : [];
|
|
6794
|
-
console.log("\n" +
|
|
6795
|
-
if (messages.length) console.log(
|
|
7778
|
+
console.log("\n" + import_chalk43.default.cyan.bold("Chat with your API") + import_chalk43.default.dim(` \xB7 ${p.mcpHost}`));
|
|
7779
|
+
if (messages.length) console.log(import_chalk43.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
6796
7780
|
const llm2 = loadLlmConfig();
|
|
6797
7781
|
console.log(
|
|
6798
|
-
|
|
7782
|
+
import_chalk43.default.dim(
|
|
6799
7783
|
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."
|
|
6800
7784
|
)
|
|
6801
7785
|
);
|
|
6802
7786
|
for (; ; ) {
|
|
6803
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
7787
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk43.default.green("you \u203A") }]);
|
|
6804
7788
|
const text = (input ?? "").trim();
|
|
6805
7789
|
if (!text) continue;
|
|
6806
7790
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
6807
7791
|
if (text === "/login") {
|
|
6808
7792
|
try {
|
|
6809
7793
|
await runLogin();
|
|
6810
|
-
console.log(
|
|
7794
|
+
console.log(import_chalk43.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
6811
7795
|
} catch (err) {
|
|
6812
|
-
console.log(
|
|
7796
|
+
console.log(import_chalk43.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6813
7797
|
}
|
|
6814
7798
|
continue;
|
|
6815
7799
|
}
|
|
6816
7800
|
if (text === "/claim") {
|
|
6817
7801
|
const justLoggedIn = !loadCredentials();
|
|
6818
7802
|
if (justLoggedIn) {
|
|
6819
|
-
console.log(
|
|
7803
|
+
console.log(import_chalk43.default.dim(" Logging in to claim your workspace\u2026"));
|
|
6820
7804
|
try {
|
|
6821
7805
|
await runLogin();
|
|
6822
7806
|
} catch (err) {
|
|
6823
|
-
console.log(
|
|
7807
|
+
console.log(import_chalk43.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6824
7808
|
continue;
|
|
6825
7809
|
}
|
|
6826
7810
|
if (!loadCredentials()) continue;
|
|
@@ -6831,30 +7815,30 @@ async function runRepl(p, initialMessages) {
|
|
|
6831
7815
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
6832
7816
|
p.anon = false;
|
|
6833
7817
|
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
6834
|
-
console.log(
|
|
7818
|
+
console.log(import_chalk43.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
6835
7819
|
}
|
|
6836
7820
|
} catch (err) {
|
|
6837
|
-
console.log(
|
|
7821
|
+
console.log(import_chalk43.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
6838
7822
|
}
|
|
6839
7823
|
continue;
|
|
6840
7824
|
}
|
|
6841
7825
|
if (text === "/showauth") {
|
|
6842
7826
|
revealAuth = !revealAuth;
|
|
6843
|
-
console.log(
|
|
7827
|
+
console.log(import_chalk43.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."));
|
|
6844
7828
|
continue;
|
|
6845
7829
|
}
|
|
6846
7830
|
if (text.startsWith("/")) {
|
|
6847
|
-
console.log(
|
|
7831
|
+
console.log(import_chalk43.default.dim(" Commands: /login /claim /showauth /exit"));
|
|
6848
7832
|
continue;
|
|
6849
7833
|
}
|
|
6850
7834
|
await replTurn(p, messages, text);
|
|
6851
7835
|
saveTranscript(p, messages);
|
|
6852
7836
|
}
|
|
6853
|
-
console.log(
|
|
7837
|
+
console.log(import_chalk43.default.dim("\nBye."));
|
|
6854
7838
|
}
|
|
6855
7839
|
async function runApichat(opts) {
|
|
6856
7840
|
setVerbose(opts.verbose !== false);
|
|
6857
|
-
console.log(
|
|
7841
|
+
console.log(import_chalk43.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
6858
7842
|
if (!opts.openapispec && !opts.target) {
|
|
6859
7843
|
if (!process.stdin.isTTY) {
|
|
6860
7844
|
fail4("No spec source. Pass --openapispec <file|url> or --target <url>.", GENERATOR_HINT);
|
|
@@ -6867,7 +7851,7 @@ async function runApichat(opts) {
|
|
|
6867
7851
|
}
|
|
6868
7852
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
6869
7853
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
6870
|
-
console.log(` ${
|
|
7854
|
+
console.log(` ${import_chalk43.default.dim("Target:")} ${import_chalk43.default.bold(target)}`);
|
|
6871
7855
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
6872
7856
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
6873
7857
|
fail4(
|
|
@@ -6876,7 +7860,7 @@ async function runApichat(opts) {
|
|
|
6876
7860
|
);
|
|
6877
7861
|
}
|
|
6878
7862
|
const p = await provision(spec2, target, opts);
|
|
6879
|
-
console.log(` ${
|
|
7863
|
+
console.log(` ${import_chalk43.default.dim("Proxy: ")} ${import_chalk43.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
6880
7864
|
upsertApichat({
|
|
6881
7865
|
name: p.projectId,
|
|
6882
7866
|
target,
|
|
@@ -6896,30 +7880,30 @@ async function runApichat(opts) {
|
|
|
6896
7880
|
const secret = await captureTargetSecret(auth, opts);
|
|
6897
7881
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
6898
7882
|
} else {
|
|
6899
|
-
console.log(
|
|
7883
|
+
console.log(import_chalk43.default.dim(" Target auth: none required."));
|
|
6900
7884
|
}
|
|
6901
7885
|
const specText = JSON.stringify(spec2);
|
|
6902
7886
|
await uploadSpec(p, specText, opts);
|
|
6903
7887
|
const mcpUrl = await publishMcp(p, spec2);
|
|
6904
7888
|
console.log();
|
|
6905
|
-
if (p.proxyUrl) console.log(` ${
|
|
7889
|
+
if (p.proxyUrl) console.log(` ${import_chalk43.default.green("\u2713")} proxy ${import_chalk43.default.bold(p.proxyUrl)}`);
|
|
6906
7890
|
if (mcpUrl) {
|
|
6907
|
-
console.log(` ${
|
|
7891
|
+
console.log(` ${import_chalk43.default.green("\u2713")} mcp ${import_chalk43.default.bold(mcpUrl)}`);
|
|
6908
7892
|
if (p.access === "invite") {
|
|
6909
|
-
console.log(
|
|
6910
|
-
console.log(
|
|
7893
|
+
console.log(import_chalk43.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: invite \u2014 only you + emails you pre-approve"));
|
|
7894
|
+
console.log(import_chalk43.default.dim(` Let others in: apiblaze preapprove someone@company.com${p.tenant ? ` --tenant ${p.tenant}` : ""} (or re-run with --access open)`));
|
|
6911
7895
|
} else {
|
|
6912
|
-
console.log(
|
|
7896
|
+
console.log(import_chalk43.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: open \u2014 anyone who signs in can call this API"));
|
|
6913
7897
|
}
|
|
6914
7898
|
}
|
|
6915
7899
|
if (p.anon) {
|
|
6916
|
-
console.log(
|
|
7900
|
+
console.log(import_chalk43.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
|
|
6917
7901
|
}
|
|
6918
7902
|
await runRepl(p);
|
|
6919
7903
|
}
|
|
6920
7904
|
|
|
6921
7905
|
// src/commands/consumer.ts
|
|
6922
|
-
var
|
|
7906
|
+
var import_chalk44 = __toESM(require("chalk"));
|
|
6923
7907
|
var import_ora22 = __toESM(require("ora"));
|
|
6924
7908
|
init_admin();
|
|
6925
7909
|
init_resolve();
|
|
@@ -6938,10 +7922,10 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
6938
7922
|
}
|
|
6939
7923
|
return { status: res.status, data, creds: fresh };
|
|
6940
7924
|
}
|
|
6941
|
-
function
|
|
7925
|
+
function requireConsumer2() {
|
|
6942
7926
|
const c = loadConsumer();
|
|
6943
7927
|
if (!c) {
|
|
6944
|
-
console.error(
|
|
7928
|
+
console.error(import_chalk44.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
6945
7929
|
process.exit(1);
|
|
6946
7930
|
}
|
|
6947
7931
|
return c;
|
|
@@ -6952,7 +7936,7 @@ async function runConsumerLogin(opts) {
|
|
|
6952
7936
|
let clientId = opts.client;
|
|
6953
7937
|
if (clientId) {
|
|
6954
7938
|
if (!tenant2) {
|
|
6955
|
-
console.error(
|
|
7939
|
+
console.error(import_chalk44.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
6956
7940
|
process.exit(1);
|
|
6957
7941
|
}
|
|
6958
7942
|
} else {
|
|
@@ -6970,19 +7954,19 @@ async function runConsumerLogin(opts) {
|
|
|
6970
7954
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
6971
7955
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
6972
7956
|
if (!pick2) {
|
|
6973
|
-
console.error(
|
|
7957
|
+
console.error(import_chalk44.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
6974
7958
|
process.exit(1);
|
|
6975
7959
|
}
|
|
6976
7960
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
6977
7961
|
}
|
|
6978
7962
|
const portalResource = `https://${tenant2}.portal.apiblaze.com/1.0.0`;
|
|
6979
|
-
console.log(`${
|
|
7963
|
+
console.log(`${import_chalk44.default.cyan("\u2192")} Logging in to ${import_chalk44.default.bold(tenant2)} as a consumer...`);
|
|
6980
7964
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
6981
7965
|
console.log(`
|
|
6982
|
-
Open: ${
|
|
6983
|
-
console.log(` Code: ${
|
|
7966
|
+
Open: ${import_chalk44.default.underline(verificationUri)}`);
|
|
7967
|
+
console.log(` Code: ${import_chalk44.default.bold(userCode)}
|
|
6984
7968
|
`);
|
|
6985
|
-
console.log(
|
|
7969
|
+
console.log(import_chalk44.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
6986
7970
|
}, portalResource);
|
|
6987
7971
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
6988
7972
|
const creds = {
|
|
@@ -6997,10 +7981,10 @@ async function runConsumerLogin(opts) {
|
|
|
6997
7981
|
obtainedAt: Date.now()
|
|
6998
7982
|
};
|
|
6999
7983
|
saveConsumer(creds);
|
|
7000
|
-
console.log(
|
|
7984
|
+
console.log(import_chalk44.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
7001
7985
|
}
|
|
7002
7986
|
async function runConsumerTokens(opts) {
|
|
7003
|
-
const creds =
|
|
7987
|
+
const creds = requireConsumer2();
|
|
7004
7988
|
const fresh = await validConsumerToken(creds) ?? creds;
|
|
7005
7989
|
const exp = (t) => {
|
|
7006
7990
|
const p = t ? decodeJwt2(t) : null;
|
|
@@ -7010,29 +7994,29 @@ async function runConsumerTokens(opts) {
|
|
|
7010
7994
|
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));
|
|
7011
7995
|
return;
|
|
7012
7996
|
}
|
|
7013
|
-
console.log(`${
|
|
7997
|
+
console.log(`${import_chalk44.default.cyan("Consumer")} ${import_chalk44.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk44.default.bold(fresh.tenant)}
|
|
7014
7998
|
`);
|
|
7015
|
-
console.log(`${
|
|
7999
|
+
console.log(`${import_chalk44.default.bold("access_token")} ${import_chalk44.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
7016
8000
|
${fresh.accessToken}
|
|
7017
8001
|
`);
|
|
7018
|
-
if (fresh.idToken) console.log(`${
|
|
8002
|
+
if (fresh.idToken) console.log(`${import_chalk44.default.bold("id_token")} ${import_chalk44.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
7019
8003
|
${fresh.idToken}
|
|
7020
8004
|
`);
|
|
7021
|
-
if (fresh.refreshToken) console.log(`${
|
|
8005
|
+
if (fresh.refreshToken) console.log(`${import_chalk44.default.bold("refresh_token")}
|
|
7022
8006
|
${fresh.refreshToken}
|
|
7023
8007
|
`);
|
|
7024
|
-
console.log(
|
|
8008
|
+
console.log(import_chalk44.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
7025
8009
|
}
|
|
7026
8010
|
async function runConsumerApikeys(opts) {
|
|
7027
|
-
const creds =
|
|
8011
|
+
const creds = requireConsumer2();
|
|
7028
8012
|
const { default: inquirer3 } = await import("inquirer");
|
|
7029
8013
|
const spinner = (0, import_ora22.default)("Loading your API keys...").start();
|
|
7030
8014
|
const list = await consumerFetch(creds, "/apikeys");
|
|
7031
8015
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
7032
8016
|
spinner.stop();
|
|
7033
8017
|
if (list.status >= 400) {
|
|
7034
|
-
console.error(
|
|
7035
|
-
if (list.status === 401) console.error(
|
|
8018
|
+
console.error(import_chalk44.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
8019
|
+
if (list.status === 401) console.error(import_chalk44.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
7036
8020
|
process.exit(1);
|
|
7037
8021
|
}
|
|
7038
8022
|
const keys = list.data?.keys ?? [];
|
|
@@ -7040,16 +8024,16 @@ async function runConsumerApikeys(opts) {
|
|
|
7040
8024
|
if (opts.json) {
|
|
7041
8025
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
7042
8026
|
} else if (!keys.length) {
|
|
7043
|
-
console.log(
|
|
8027
|
+
console.log(import_chalk44.default.yellow("No API keys yet."));
|
|
7044
8028
|
} else {
|
|
7045
8029
|
for (const k of keys) {
|
|
7046
8030
|
const clear = revealMap[k.environment]?.key;
|
|
7047
|
-
const shown = clear ?
|
|
7048
|
-
const exp = k.expires_at ?
|
|
7049
|
-
console.log(` ${
|
|
8031
|
+
const shown = clear ? import_chalk44.default.green(clear) : import_chalk44.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
8032
|
+
const exp = k.expires_at ? import_chalk44.default.dim(`exp ${k.expires_at}`) : import_chalk44.default.dim("no expiry");
|
|
8033
|
+
console.log(` ${import_chalk44.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk44.default.dim(k.description ?? "")}`);
|
|
7050
8034
|
}
|
|
7051
8035
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
7052
|
-
console.log(
|
|
8036
|
+
console.log(import_chalk44.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
7053
8037
|
}
|
|
7054
8038
|
}
|
|
7055
8039
|
if (opts.json) return;
|
|
@@ -7071,12 +8055,12 @@ async function runConsumerApikeys(opts) {
|
|
|
7071
8055
|
}
|
|
7072
8056
|
s2.succeed("Key created.");
|
|
7073
8057
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
7074
|
-
if (key) console.log(` ${
|
|
7075
|
-
else console.log(
|
|
8058
|
+
if (key) console.log(` ${import_chalk44.default.green(key)} ${import_chalk44.default.dim("(shown once \u2014 store it now)")}`);
|
|
8059
|
+
else console.log(import_chalk44.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
7076
8060
|
}
|
|
7077
8061
|
|
|
7078
8062
|
// src/commands/sidecar.ts
|
|
7079
|
-
var
|
|
8063
|
+
var import_chalk45 = __toESM(require("chalk"));
|
|
7080
8064
|
var import_ora23 = __toESM(require("ora"));
|
|
7081
8065
|
var fs10 = __toESM(require("fs"));
|
|
7082
8066
|
var path7 = __toESM(require("path"));
|
|
@@ -7119,7 +8103,7 @@ function upsertEnvLocal(root, token) {
|
|
|
7119
8103
|
}
|
|
7120
8104
|
function installSidecarPackage(root) {
|
|
7121
8105
|
if (fs10.existsSync(path7.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
7122
|
-
console.log(` ${
|
|
8106
|
+
console.log(` ${import_chalk45.default.green("\u2713")} apiblaze package already installed`);
|
|
7123
8107
|
return;
|
|
7124
8108
|
}
|
|
7125
8109
|
const has = (f) => fs10.existsSync(path7.join(root, f));
|
|
@@ -7130,7 +8114,7 @@ function installSidecarPackage(root) {
|
|
|
7130
8114
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
7131
8115
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
7132
8116
|
} catch {
|
|
7133
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
8117
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk45.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk45.default.cyan("npm run dev")}.`);
|
|
7134
8118
|
}
|
|
7135
8119
|
}
|
|
7136
8120
|
function readEnvKey(root) {
|
|
@@ -7281,29 +8265,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
7281
8265
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
7282
8266
|
const envState = upsertEnvLocal(root, out.token);
|
|
7283
8267
|
ensureGitignored(root);
|
|
7284
|
-
console.log(` ${
|
|
7285
|
-
console.log(` ${
|
|
8268
|
+
console.log(` ${import_chalk45.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
8269
|
+
console.log(` ${import_chalk45.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
7286
8270
|
installSidecarPackage(root);
|
|
7287
8271
|
let inspectorPath = null;
|
|
7288
8272
|
if (!opts.noInspector) {
|
|
7289
8273
|
inspectorPath = generateInspector(root, router);
|
|
7290
|
-
if (inspectorPath) console.log(` ${
|
|
8274
|
+
if (inspectorPath) console.log(` ${import_chalk45.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
7291
8275
|
}
|
|
7292
8276
|
console.log("");
|
|
7293
|
-
console.log(
|
|
7294
|
-
console.log(` 1. ${
|
|
8277
|
+
console.log(import_chalk45.default.bold("Done (no account needed). What happens next:"));
|
|
8278
|
+
console.log(` 1. ${import_chalk45.default.cyan("npm run dev")} and use your app.`);
|
|
7295
8279
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
7296
|
-
console.log(` ${
|
|
8280
|
+
console.log(` ${import_chalk45.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
7297
8281
|
console.log("");
|
|
7298
|
-
console.log(
|
|
7299
|
-
console.log(` ${
|
|
7300
|
-
console.log(
|
|
8282
|
+
console.log(import_chalk45.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
8283
|
+
console.log(` ${import_chalk45.default.cyan("apiblaze login")} then ${import_chalk45.default.cyan("apiblaze claim")} ${import_chalk45.default.dim("(no code needed here)")}`);
|
|
8284
|
+
console.log(import_chalk45.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
7301
8285
|
}
|
|
7302
8286
|
async function runSidecar(opts) {
|
|
7303
8287
|
const root = path7.resolve(opts.dir ?? process.cwd());
|
|
7304
8288
|
const detected = detectNextProject(root);
|
|
7305
8289
|
if (!detected.found) {
|
|
7306
|
-
console.log(
|
|
8290
|
+
console.log(import_chalk45.default.yellow(`No Next.js project detected in ${root}.`));
|
|
7307
8291
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
7308
8292
|
return;
|
|
7309
8293
|
}
|
|
@@ -7314,10 +8298,10 @@ async function runSidecar(opts) {
|
|
|
7314
8298
|
if (!loadCredentials()) {
|
|
7315
8299
|
upsertEnvLocal(root, readEnvKey(root));
|
|
7316
8300
|
ensureGitignored(root);
|
|
7317
|
-
console.log(` ${
|
|
7318
|
-
console.log(` ${
|
|
8301
|
+
console.log(` ${import_chalk45.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
8302
|
+
console.log(` ${import_chalk45.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
7319
8303
|
installSidecarPackage(root);
|
|
7320
|
-
console.log(
|
|
8304
|
+
console.log(import_chalk45.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
7321
8305
|
return;
|
|
7322
8306
|
}
|
|
7323
8307
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -7340,38 +8324,38 @@ async function runSidecar(opts) {
|
|
|
7340
8324
|
throw err;
|
|
7341
8325
|
}
|
|
7342
8326
|
} else {
|
|
7343
|
-
console.log(
|
|
8327
|
+
console.log(import_chalk45.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
7344
8328
|
}
|
|
7345
8329
|
const envState = upsertEnvLocal(root, token);
|
|
7346
8330
|
ensureGitignored(root);
|
|
7347
|
-
console.log(` ${
|
|
8331
|
+
console.log(` ${import_chalk45.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
7348
8332
|
const wireState = wireInstrumentation(root);
|
|
7349
|
-
console.log(` ${
|
|
8333
|
+
console.log(` ${import_chalk45.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
7350
8334
|
installSidecarPackage(root);
|
|
7351
8335
|
let inspectorPath = null;
|
|
7352
8336
|
if (!opts.noInspector) {
|
|
7353
8337
|
inspectorPath = generateInspector(root, detected.router);
|
|
7354
|
-
if (inspectorPath) console.log(` ${
|
|
8338
|
+
if (inspectorPath) console.log(` ${import_chalk45.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
7355
8339
|
}
|
|
7356
8340
|
console.log("");
|
|
7357
|
-
console.log(
|
|
7358
|
-
console.log(` 1. ${
|
|
7359
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
7360
|
-
console.log(` 3. Approve the ones to route: ${
|
|
8341
|
+
console.log(import_chalk45.default.bold("Done. What happens next:"));
|
|
8342
|
+
console.log(` 1. ${import_chalk45.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
8343
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk45.default.bold("candidates")} \u2014 list them: ${import_chalk45.default.cyan("apiblaze sidecar")}`);
|
|
8344
|
+
console.log(` 3. Approve the ones to route: ${import_chalk45.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
7361
8345
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
7362
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
7363
|
-
if (switchingTeam) console.log(
|
|
8346
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk45.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path7.dirname(inspectorPath)} before shipping)`);
|
|
8347
|
+
if (switchingTeam) console.log(import_chalk45.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
7364
8348
|
console.log("");
|
|
7365
|
-
console.log(
|
|
7366
|
-
console.log(
|
|
7367
|
-
console.log(
|
|
8349
|
+
console.log(import_chalk45.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
8350
|
+
console.log(import_chalk45.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
8351
|
+
console.log(import_chalk45.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
7368
8352
|
console.log("");
|
|
7369
|
-
console.log(
|
|
7370
|
-
console.log(
|
|
8353
|
+
console.log(import_chalk45.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
8354
|
+
console.log(import_chalk45.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
7371
8355
|
}
|
|
7372
8356
|
|
|
7373
8357
|
// src/commands/origins.ts
|
|
7374
|
-
var
|
|
8358
|
+
var import_chalk46 = __toESM(require("chalk"));
|
|
7375
8359
|
var import_ora24 = __toESM(require("ora"));
|
|
7376
8360
|
init_admin();
|
|
7377
8361
|
init_resolve();
|
|
@@ -7382,7 +8366,7 @@ async function runOriginsList(opts) {
|
|
|
7382
8366
|
if (!loadCredentials()) {
|
|
7383
8367
|
const cred = loadAnonCred();
|
|
7384
8368
|
if (!cred) {
|
|
7385
|
-
console.log(
|
|
8369
|
+
console.log(import_chalk46.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
7386
8370
|
return;
|
|
7387
8371
|
}
|
|
7388
8372
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -7400,27 +8384,27 @@ async function runOriginsList(opts) {
|
|
|
7400
8384
|
}
|
|
7401
8385
|
const routed = out.routed ?? [];
|
|
7402
8386
|
const candidates = out.candidates ?? [];
|
|
7403
|
-
console.log(
|
|
8387
|
+
console.log(import_chalk46.default.bold(`
|
|
7404
8388
|
Routed through APIblaze (${routed.length})`));
|
|
7405
|
-
if (!routed.length) console.log(
|
|
7406
|
-
for (const r of routed) console.log(` ${
|
|
7407
|
-
console.log(
|
|
8389
|
+
if (!routed.length) console.log(import_chalk46.default.dim(" none yet"));
|
|
8390
|
+
for (const r of routed) console.log(` ${import_chalk46.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk46.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
8391
|
+
console.log(import_chalk46.default.bold(`
|
|
7408
8392
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
7409
|
-
if (!candidates.length) console.log(
|
|
8393
|
+
if (!candidates.length) console.log(import_chalk46.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
7410
8394
|
for (const c of candidates) {
|
|
7411
|
-
console.log(` ${
|
|
8395
|
+
console.log(` ${import_chalk46.default.yellow("\u25CB")} ${c.origin} ${import_chalk46.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
7412
8396
|
}
|
|
7413
8397
|
if (candidates.length) {
|
|
7414
|
-
console.log(
|
|
8398
|
+
console.log(import_chalk46.default.dim(`
|
|
7415
8399
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
7416
|
-
console.log(
|
|
8400
|
+
console.log(import_chalk46.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
7417
8401
|
}
|
|
7418
8402
|
}
|
|
7419
8403
|
async function runOriginsApprove(origin, opts) {
|
|
7420
8404
|
if (!loadCredentials()) {
|
|
7421
8405
|
const cred = loadAnonCred();
|
|
7422
8406
|
if (!cred) {
|
|
7423
|
-
console.error(
|
|
8407
|
+
console.error(import_chalk46.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
7424
8408
|
process.exit(1);
|
|
7425
8409
|
}
|
|
7426
8410
|
const spinner2 = (0, import_ora24.default)(`Approving ${origin} (anonymous)...`).start();
|
|
@@ -7473,7 +8457,7 @@ async function runOriginsRemove(origin, opts) {
|
|
|
7473
8457
|
}
|
|
7474
8458
|
|
|
7475
8459
|
// src/commands/op.ts
|
|
7476
|
-
var
|
|
8460
|
+
var import_chalk47 = __toESM(require("chalk"));
|
|
7477
8461
|
init_auth();
|
|
7478
8462
|
init_trace();
|
|
7479
8463
|
init_types();
|
|
@@ -7506,82 +8490,82 @@ function printResidue(report, applied) {
|
|
|
7506
8490
|
const up = report?.upstash ?? {};
|
|
7507
8491
|
const fga = report?.fga ?? {};
|
|
7508
8492
|
const ghosts = report?.ghosts ?? {};
|
|
7509
|
-
console.log(
|
|
7510
|
-
console.log(
|
|
8493
|
+
console.log(import_chalk47.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
8494
|
+
console.log(import_chalk47.default.bold("\n Upstash"));
|
|
7511
8495
|
const orphans = up.orphans ?? [];
|
|
7512
|
-
if (orphans.length === 0) console.log(
|
|
7513
|
-
for (const o of orphans) console.log(` ${
|
|
7514
|
-
console.log(
|
|
8496
|
+
if (orphans.length === 0) console.log(import_chalk47.default.green(" no orphaned keys"));
|
|
8497
|
+
for (const o of orphans) console.log(` ${import_chalk47.default.yellow(o.key)} ${import_chalk47.default.dim(`\u2014 ${o.reason}`)}`);
|
|
8498
|
+
console.log(import_chalk47.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
7515
8499
|
if (up.anon_wallet_detail) {
|
|
7516
8500
|
const d = up.anon_wallet_detail;
|
|
7517
|
-
console.log(
|
|
8501
|
+
console.log(import_chalk47.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)"}`));
|
|
7518
8502
|
}
|
|
7519
8503
|
if (up.keyspace_census) {
|
|
7520
8504
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
7521
|
-
console.log(
|
|
8505
|
+
console.log(import_chalk47.default.dim(` keyspace: ${census}`));
|
|
7522
8506
|
}
|
|
7523
|
-
if (up.unknown?.length) console.log(
|
|
7524
|
-
if (applied) console.log(` ${
|
|
7525
|
-
for (const e of up.errors ?? []) console.log(
|
|
7526
|
-
console.log(
|
|
8507
|
+
if (up.unknown?.length) console.log(import_chalk47.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
8508
|
+
if (applied) console.log(` ${import_chalk47.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
8509
|
+
for (const e of up.errors ?? []) console.log(import_chalk47.default.red(` error: ${e}`));
|
|
8510
|
+
console.log(import_chalk47.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
7527
8511
|
if (applied) {
|
|
7528
8512
|
const swept = fga?.swept ?? [];
|
|
7529
|
-
if (swept.length === 0) console.log(
|
|
8513
|
+
if (swept.length === 0) console.log(import_chalk47.default.green(" no orphaned stores"));
|
|
7530
8514
|
for (const s of swept) {
|
|
7531
8515
|
console.log(
|
|
7532
|
-
` ${
|
|
8516
|
+
` ${import_chalk47.default.yellow(s.store_id)} ${import_chalk47.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
7533
8517
|
);
|
|
7534
8518
|
}
|
|
7535
|
-
if (fga?.remaining) console.log(
|
|
8519
|
+
if (fga?.remaining) console.log(import_chalk47.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
7536
8520
|
const st = fga?.side_tables;
|
|
7537
|
-
if (st) console.log(
|
|
8521
|
+
if (st) console.log(import_chalk47.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})` : ""}`));
|
|
7538
8522
|
} else {
|
|
7539
8523
|
const fgaOrphans = fga?.orphans ?? [];
|
|
7540
|
-
if (fgaOrphans.length === 0) console.log(
|
|
8524
|
+
if (fgaOrphans.length === 0) console.log(import_chalk47.default.green(" no orphaned stores"));
|
|
7541
8525
|
for (const s of fgaOrphans) {
|
|
7542
8526
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
7543
|
-
console.log(` ${
|
|
8527
|
+
console.log(` ${import_chalk47.default.yellow(s.store_id)} ${import_chalk47.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
7544
8528
|
}
|
|
7545
|
-
console.log(
|
|
8529
|
+
console.log(import_chalk47.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
7546
8530
|
const st = fga?.side_tables;
|
|
7547
|
-
if (st) console.log(
|
|
8531
|
+
if (st) console.log(import_chalk47.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`));
|
|
7548
8532
|
}
|
|
7549
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
7550
|
-
console.log(
|
|
8533
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk47.default.red(` error: ${e}`));
|
|
8534
|
+
console.log(import_chalk47.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
7551
8535
|
if (applied) {
|
|
7552
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
7553
|
-
else console.log(` ${
|
|
8536
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk47.default.green(" no ghost tuples"));
|
|
8537
|
+
else console.log(` ${import_chalk47.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk47.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
7554
8538
|
} else {
|
|
7555
8539
|
const n = ghosts?.ghost_count ?? 0;
|
|
7556
|
-
if (n === 0) console.log(
|
|
8540
|
+
if (n === 0) console.log(import_chalk47.default.green(` no ghost tuples ${import_chalk47.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
7557
8541
|
else {
|
|
7558
|
-
console.log(
|
|
8542
|
+
console.log(import_chalk47.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
7559
8543
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
7560
|
-
console.log(
|
|
8544
|
+
console.log(import_chalk47.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
7561
8545
|
}
|
|
7562
|
-
if (n > 20) console.log(
|
|
8546
|
+
if (n > 20) console.log(import_chalk47.default.dim(` \u2026 and ${n - 20} more`));
|
|
7563
8547
|
}
|
|
7564
8548
|
}
|
|
7565
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
8549
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk47.default.red(` error: ${e}`));
|
|
7566
8550
|
console.log();
|
|
7567
8551
|
}
|
|
7568
8552
|
async function runOp(sub, opts = {}) {
|
|
7569
8553
|
if (!loadCredentials()) {
|
|
7570
|
-
console.log(
|
|
8554
|
+
console.log(import_chalk47.default.dim("Not logged in. Run `apiblaze login`."));
|
|
7571
8555
|
return;
|
|
7572
8556
|
}
|
|
7573
8557
|
if (!isOperatorLogin()) {
|
|
7574
|
-
console.log(
|
|
8558
|
+
console.log(import_chalk47.default.dim("`apiblaze op` is only available to platform operators."));
|
|
7575
8559
|
return;
|
|
7576
8560
|
}
|
|
7577
8561
|
switch (sub) {
|
|
7578
8562
|
case void 0:
|
|
7579
8563
|
case "menu": {
|
|
7580
|
-
console.log(
|
|
7581
|
-
console.log(` ${
|
|
7582
|
-
console.log(` ${
|
|
7583
|
-
console.log(` ${
|
|
7584
|
-
console.log(
|
|
8564
|
+
console.log(import_chalk47.default.bold("\nOperator menu"));
|
|
8565
|
+
console.log(` ${import_chalk47.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
8566
|
+
console.log(` ${import_chalk47.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk47.default.dim("-y to skip")})`);
|
|
8567
|
+
console.log(` ${import_chalk47.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
8568
|
+
console.log(import_chalk47.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
7585
8569
|
`));
|
|
7586
8570
|
return;
|
|
7587
8571
|
}
|
|
@@ -7600,17 +8584,17 @@ async function runOp(sub, opts = {}) {
|
|
|
7600
8584
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
7601
8585
|
printResidue(report, false);
|
|
7602
8586
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
7603
|
-
console.log(
|
|
8587
|
+
console.log(import_chalk47.default.green("Nothing to sweep."));
|
|
7604
8588
|
return;
|
|
7605
8589
|
}
|
|
7606
8590
|
if (!opts.yes) {
|
|
7607
|
-
const
|
|
7608
|
-
const rl =
|
|
8591
|
+
const readline3 = await import("readline/promises");
|
|
8592
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
7609
8593
|
const answer = await rl.question(
|
|
7610
|
-
|
|
8594
|
+
import_chalk47.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: `)
|
|
7611
8595
|
);
|
|
7612
8596
|
rl.close();
|
|
7613
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
8597
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk47.default.dim("Aborted."));
|
|
7614
8598
|
}
|
|
7615
8599
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
7616
8600
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -7621,15 +8605,15 @@ async function runOp(sub, opts = {}) {
|
|
|
7621
8605
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
7622
8606
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
7623
8607
|
const accounts = data?.accounts ?? [];
|
|
7624
|
-
if (accounts.length === 0) return void console.log(
|
|
8608
|
+
if (accounts.length === 0) return void console.log(import_chalk47.default.dim("No credit wallets."));
|
|
7625
8609
|
for (const a of accounts) {
|
|
7626
8610
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
7627
|
-
console.log(` ${
|
|
8611
|
+
console.log(` ${import_chalk47.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk47.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk47.default.dim(" \u2014 anon") : ""}`);
|
|
7628
8612
|
}
|
|
7629
8613
|
return;
|
|
7630
8614
|
}
|
|
7631
8615
|
default:
|
|
7632
|
-
console.log(
|
|
8616
|
+
console.log(import_chalk47.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
7633
8617
|
}
|
|
7634
8618
|
}
|
|
7635
8619
|
|
|
@@ -7675,7 +8659,7 @@ agent.command("authz").description("Chat to design and turn on access rules for
|
|
|
7675
8659
|
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)));
|
|
7676
8660
|
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)));
|
|
7677
8661
|
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)));
|
|
7678
|
-
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 })));
|
|
8662
|
+
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("--tenant <slug>", "Tenant (consumer namespace: portal, login, users) for the new proxy; omit to be asked").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 })));
|
|
7679
8663
|
var llm = program.command("llm").description("Manage a local LLM provider key for chat (optional \u2014 lifts model quality, bills your key)");
|
|
7680
8664
|
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)));
|
|
7681
8665
|
llm.command("show").description("Show the locally stored LLM key (masked)").action(action(() => runLlmShow()));
|
|
@@ -7684,15 +8668,15 @@ var runSetup = (opts) => runSidecar({ team: opts.team, dir: opts.dir, yes: opts.
|
|
|
7684
8668
|
var withSetupOptions = (cmd) => cmd.option("--team <id|name>", "Team to set up under (defaults to your active team)").option("--dir <path>", "Project directory (defaults to cwd)").option("--no-inspector", "Skip generating the dev-only /abz-inspector page").option("--rotate", "Mint a fresh invoke key even if one already exists").option("--new-session", "Start a fresh anonymous session (logged-out only)").option("-y, --yes", "Skip prompts");
|
|
7685
8669
|
withSetupOptions(program.command("init").description("Set up the APIblaze sidecar in a Next.js app (shortcut for `apiblaze sidecar setup`)")).action(action((opts) => runSetup(opts)));
|
|
7686
8670
|
var sidecar = program.command("sidecar").description("The APIblaze sidecar \u2014 set it up, then approve which origins route through APIblaze").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Machine-readable output").action(action((opts) => runOriginsList(opts)));
|
|
7687
|
-
withSetupOptions(sidecar.command("setup").description("Wire a Next.js app to route its external fetches through APIblaze")).action(action((opts) => runSetup(opts)));
|
|
7688
|
-
sidecar.command("approve").description("Route an origin through APIblaze (creates its proxy)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Machine-readable output").action(action((origin, opts) => runOriginsApprove(origin, opts)));
|
|
7689
|
-
sidecar.command("deny").description("Dismiss a candidate origin so it stops being suggested").argument("<origin>", "Origin, e.g. sentry.io").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts) => runOriginsDeny(origin, opts)));
|
|
7690
|
-
sidecar.command("remove").description("Un-route an approved origin (deletes its proxy; the app goes direct again)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts) => runOriginsRemove(origin, opts)));
|
|
8671
|
+
withSetupOptions(sidecar.command("setup").description("Wire a Next.js app to route its external fetches through APIblaze")).action(action((opts, cmd) => runSetup({ ...cmd.parent?.opts(), ...opts })));
|
|
8672
|
+
sidecar.command("approve").description("Route an origin through APIblaze (creates its proxy)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Machine-readable output").action(action((origin, opts, cmd) => runOriginsApprove(origin, { ...cmd.parent?.opts(), ...opts })));
|
|
8673
|
+
sidecar.command("deny").description("Dismiss a candidate origin so it stops being suggested").argument("<origin>", "Origin, e.g. sentry.io").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts, cmd) => runOriginsDeny(origin, { ...cmd.parent?.opts(), ...opts })));
|
|
8674
|
+
sidecar.command("remove").description("Un-route an approved origin (deletes its proxy; the app goes direct again)").argument("<origin>", "Origin, e.g. api.stripe.com").option("--team <id|name>", "Team (defaults to active team)").action(action((origin, opts, cmd) => runOriginsRemove(origin, { ...cmd.parent?.opts(), ...opts })));
|
|
7691
8675
|
program.command("dev").description("Put your localhost behind a public URL (dev tunnel)").argument("[port]", "Local port to tunnel (positional; overrides --port)").option("-p, --port <number>", "Local port to tunnel", "3000").option("--project <nameOrId>", "Tunnel this specific project (skips the picker \u2014 for scripts)").option("-y, --yes", "Skip confirmation prompts (non-interactive)").option("-o, --capture-file <path>", "Stream full request/response traffic to a file (JSON lines)").option("--new-session", "Logged-out only: start a fresh anonymous workspace instead of reusing this machine's (each run = a throwaway proxy)").action(async (port, opts) => {
|
|
7692
8676
|
try {
|
|
7693
8677
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
7694
8678
|
if (Number.isNaN(resolved)) {
|
|
7695
|
-
console.error(
|
|
8679
|
+
console.error(import_chalk48.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
7696
8680
|
process.exit(1);
|
|
7697
8681
|
}
|
|
7698
8682
|
await runDev({ port: resolved, project: opts.project, yes: opts.yes, captureFile: opts.captureFile, newSession: opts.newSession });
|
|
@@ -7705,6 +8689,8 @@ var consumer = program.command("consumer").description("Act as a consumer of you
|
|
|
7705
8689
|
consumer.command("login").description("Log in to a tenant's portal as a consumer (device flow)").option("--team <id|name>", "Team whose tenants to choose from (producer mode)").option("--tenant <slug>", "Tenant to log in to (skips the picker)").option("--client <clientId>", "App client id \u2014 for standalone login without a producer session").action(action((opts) => runConsumerLogin(opts)));
|
|
7706
8690
|
consumer.command("tokens").description("Show the logged-in consumer access / refresh / id tokens").option("--json", "Output machine-readable JSON").action(action((opts) => runConsumerTokens(opts)));
|
|
7707
8691
|
consumer.command("apikeys").description("List your consumer API keys (reveals expiring ones), then offer to create one").option("--json", "Output machine-readable JSON").action(action((opts) => runConsumerApikeys(opts)));
|
|
8692
|
+
var consumerLogs = consumer.command("logs").description("API Consumer view \u2014 stream YOUR OWN requests on this tenant live (see `consumer logs list` for past days)").option("--project <name>", "Only this project (when the tenant serves several)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit raw NDJSON rows (pipe into jq)").action(action((opts) => runConsumerLogsTail(opts)));
|
|
8693
|
+
consumerLogs.command("list").description("API Consumer view \u2014 browse YOUR OWN past requests by day, then open one for detail").option("--project <name>", "Only this project (when the tenant serves several)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit ALL matching rows as NDJSON, newest first (non-interactive)").option("--plain", "Print day-grouped rows instead of prompting (automatic when piped)").action(action((_o, cmd) => runConsumerLogsList(cmd.optsWithGlobals())));
|
|
7708
8694
|
program.command("flush").description("Wipe ALL local state (login, workspace keys, apichats, DP keys, secrets)").option("-y, --yes", "Skip the confirmation prompt").action(runFlush);
|
|
7709
8695
|
program.command("logout").description("Sign out (asks whether to drop the Producer or Consumer login)").option("--producer", "Log out of the API Producer identity").option("--consumer", "Log out of the API Consumer identity").option("--all", "Log out of both").action(action((opts) => runLogout(opts)));
|
|
7710
8696
|
program.command("whoami").description("Show who you are \u2014 both API Producer and API Consumer").option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
@@ -7741,6 +8727,8 @@ program.command("projects").description("List the projects in your team").action
|
|
|
7741
8727
|
});
|
|
7742
8728
|
program.command("delete").description("Delete a proxy and everything under it (asks first)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--team <id|name>", "Team the project is in (defaults to active team)").option("-y, --yes", "Skip the confirmation prompt").option("--json", "Output machine-readable JSON").action(action((project, version2, opts) => runDelete(project, version2, opts)));
|
|
7743
8729
|
program.command("export").description("Export config and data for migration out of APIblaze (Kong, ...)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--kong", "Produce a runnable Kong OSS bundle (decK config + plugins + docker-compose)").option("--data", "Plain data export (default)").option("--secrets", "Include decrypted producer-supplied secrets (member role; audit-logged)").option("--keys <mode>", "API-key export: hashes (default, consumers keep keys) | mint | none").option("--no-consumers", "Skip the end-user lane (users, groups, keys)").option("-o, --out <file>", "Output zip path").option("--team <id|name>", "Team the project is in (defaults to active team)").action(action((project, version2, opts) => runExport(project, version2, { ...opts, noConsumers: opts.consumers === false })));
|
|
8730
|
+
var logs = program.command("logs").description("API Producer view \u2014 stream this proxy's requests from EVERY caller live (see `apiblaze logs list` to browse past days)").argument("<project>", "Project name or id (see `apiblaze projects`)").argument("[version]", "API version (defaults to the first match)").option("--tenant <slug>", "Tenant to tail (auto when one is attached; prompted otherwise)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit raw NDJSON rows (pipe into jq)").option("--team <id|name>", "Team the project is in (defaults to active team)").action(action((project, version2, opts) => runLogsTail(project, version2, opts)));
|
|
8731
|
+
logs.command("list").description("API Producer view \u2014 browse EVERY caller's past requests by day, then open one to see its 4 views").argument("<project>", "Project name or id").argument("[version]", "API version (defaults to the first match)").option("--tenant <slug>", "Tenant to read (auto when one is attached; prompted otherwise)").option("--errors-only", "Only show 4xx/5xx responses").option("--json", "Emit ALL matching rows as NDJSON, newest first (non-interactive)").option("--plain", "Print day-grouped rows instead of prompting (automatic when piped)").option("--team <id|name>", "Team the project is in (defaults to active team)").action(action((project, version2, _o, cmd) => runLogsList(project, version2, cmd.optsWithGlobals())));
|
|
7744
8732
|
program.command("config").description("Browse and change every proxy setting & feature (interactive; git-config-style get/set)").argument("[project]", "Project name or id (omit to pick interactively)").argument("[key]", "Setting key, e.g. throttling.proxyQuota (omit for the menu)").argument("[value]", "New value (omit to read the current value)").option("--list", "Print every setting and its current value").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version (defaults to the first match)").option("--json", "Machine-readable output (with --list or a key read)").action(action((project, key, value, opts) => runConfig(project, key, value, opts)));
|
|
7745
8733
|
program.command("target").description("Change where a proxy forwards requests").argument("<project>", "Project name or id").requiredOption("--url <url>", "Target URL to forward to").option("--env <env>", "Environment to scope the target to (e.g. prod, dev)").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version (defaults to the first match)").option("--json", "Output machine-readable JSON").action(action((project, opts) => runTargetSet(project, opts)));
|
|
7746
8734
|
program.command("throttle").description("Set rate limits and quotas for a proxy").argument("<project>", "Project name or id").option("--rate <n>", "User rate limit (requests/sec)").option("--end-user-rate <n>", "Per-end-user rate limit (requests/sec)").option("--quota <n>", "Proxy quota (requests/period)").option("--period <p>", "Quota period: daily | weekly | monthly").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runThrottleSet(project, opts)));
|
|
@@ -7792,10 +8780,11 @@ spec.command("delete-rule").description("Delete the saved rules for a route (e.g
|
|
|
7792
8780
|
var HELP_GROUPS = [
|
|
7793
8781
|
{ title: "Chat", commands: ["apichat", "agent"] },
|
|
7794
8782
|
{ title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
|
|
7795
|
-
{ title: "Control plane commands", commands: ["config", "projects", "tenant", "group", "admins", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
8783
|
+
{ title: "Control plane commands", commands: ["config", "projects", "logs", "tenant", "group", "admins", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
7796
8784
|
{ title: "Data plane commands", commands: [
|
|
7797
8785
|
{ parent: "consumer", sub: "login" },
|
|
7798
|
-
{ parent: "consumer", sub: "apikeys" }
|
|
8786
|
+
{ parent: "consumer", sub: "apikeys" },
|
|
8787
|
+
{ parent: "consumer", sub: "logs" }
|
|
7799
8788
|
] }
|
|
7800
8789
|
];
|
|
7801
8790
|
var helpLabel = (e) => typeof e === "string" ? e : `${e.parent} ${e.sub}`;
|
|
@@ -7811,7 +8800,7 @@ function groupedCommandHelp() {
|
|
|
7811
8800
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
7812
8801
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
7813
8802
|
}).filter(Boolean).join("\n");
|
|
7814
|
-
return `${
|
|
8803
|
+
return `${import_chalk48.default.bold(g.title)}
|
|
7815
8804
|
${rows}`;
|
|
7816
8805
|
}).join("\n\n");
|
|
7817
8806
|
}
|
|
@@ -7846,14 +8835,14 @@ async function recoverStaleTeam() {
|
|
|
7846
8835
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
7847
8836
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
7848
8837
|
if (!linked) {
|
|
7849
|
-
console.error(
|
|
8838
|
+
console.error(import_chalk48.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
7850
8839
|
return;
|
|
7851
8840
|
}
|
|
7852
8841
|
if (linked.teamId === creds.teamId) return;
|
|
7853
8842
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
7854
8843
|
delete next.activeTenant;
|
|
7855
8844
|
saveCredentials(next);
|
|
7856
|
-
console.error(
|
|
8845
|
+
console.error(import_chalk48.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk48.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
7857
8846
|
} catch {
|
|
7858
8847
|
}
|
|
7859
8848
|
}
|
|
@@ -7861,16 +8850,16 @@ async function printError(err) {
|
|
|
7861
8850
|
if (err instanceof ApiError) {
|
|
7862
8851
|
const data = err.body;
|
|
7863
8852
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
7864
|
-
console.error(
|
|
8853
|
+
console.error(import_chalk48.default.red(`
|
|
7865
8854
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
7866
8855
|
if (err.status === 403 || err.status === 404) {
|
|
7867
8856
|
await recoverStaleTeam();
|
|
7868
8857
|
}
|
|
7869
8858
|
} else if (err instanceof Error) {
|
|
7870
|
-
console.error(
|
|
8859
|
+
console.error(import_chalk48.default.red(`
|
|
7871
8860
|
Error: ${err.message}`));
|
|
7872
8861
|
} else {
|
|
7873
|
-
console.error(
|
|
8862
|
+
console.error(import_chalk48.default.red("\nUnknown error"));
|
|
7874
8863
|
}
|
|
7875
8864
|
}
|
|
7876
8865
|
program.parse(process.argv);
|