apiblaze 0.20.31 → 0.20.39
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 +923 -690
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -806,7 +806,7 @@ async function createTenantRow(teamId) {
|
|
|
806
806
|
const { name } = await inquirer3.prompt([{
|
|
807
807
|
type: "input",
|
|
808
808
|
name: "name",
|
|
809
|
-
message: `Tenant name ${
|
|
809
|
+
message: `Tenant name ${import_chalk38.default.dim("(lowercase letters/numbers; globally unique \u2014 becomes {name}.portal.apiblaze.com)")}:`,
|
|
810
810
|
validate: (s) => /^[a-z0-9]+$/.test(s.trim()) ? true : "lowercase letters and numbers only"
|
|
811
811
|
}]);
|
|
812
812
|
const slug = name.trim();
|
|
@@ -821,7 +821,7 @@ async function createTenantRow(teamId) {
|
|
|
821
821
|
} catch (err) {
|
|
822
822
|
const msg = err instanceof Error ? err.message : String(err);
|
|
823
823
|
if (/taken|unique|exists|reserved|conflict/i.test(msg)) {
|
|
824
|
-
console.log(
|
|
824
|
+
console.log(import_chalk38.default.yellow(` "${slug}" is not available (tenant names are global): ${msg}`));
|
|
825
825
|
continue;
|
|
826
826
|
}
|
|
827
827
|
throw err;
|
|
@@ -838,11 +838,11 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
838
838
|
pageSize: 10,
|
|
839
839
|
choices: [
|
|
840
840
|
...PROVIDERS.map((p) => ({ name: p.label, value: p.id })),
|
|
841
|
-
...opts.allowSkip ? [new inquirer3.Separator(), { name:
|
|
841
|
+
...opts.allowSkip ? [new inquirer3.Separator(), { name: import_chalk38.default.dim("Skip for now \u2014 set up a login method later"), value: null }] : []
|
|
842
842
|
]
|
|
843
843
|
}]);
|
|
844
844
|
if (!provider) {
|
|
845
|
-
console.log(
|
|
845
|
+
console.log(import_chalk38.default.dim(" No login method yet \u2014 add one later under Login methods."));
|
|
846
846
|
return false;
|
|
847
847
|
}
|
|
848
848
|
const opt = PROVIDERS.find((p) => p.id === provider);
|
|
@@ -888,26 +888,26 @@ async function addApiblazeHostedLogin(teamId, tenant2, opts = {}) {
|
|
|
888
888
|
async function createTenantInteractive(teamId) {
|
|
889
889
|
const { default: inquirer3 } = await import("inquirer");
|
|
890
890
|
const tenant2 = await createTenantRow(teamId);
|
|
891
|
-
console.log(
|
|
891
|
+
console.log(import_chalk38.default.green(` Tenant ${import_chalk38.default.bold(tenant2)} created.`));
|
|
892
892
|
const { users } = await inquirer3.prompt([{
|
|
893
893
|
type: "confirm",
|
|
894
894
|
name: "users",
|
|
895
895
|
default: false,
|
|
896
|
-
message: `Enable Users & groups? ${
|
|
896
|
+
message: `Enable Users & groups? ${import_chalk38.default.dim(`(identity/key management at iam.apiblaze.com)`)}`
|
|
897
897
|
}]);
|
|
898
898
|
if (users) {
|
|
899
899
|
await admin({ method: "PATCH", path: `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}/iam`, body: { enabled: true }, summary: "Enable Users & groups" }).catch(() => {
|
|
900
900
|
});
|
|
901
|
-
console.log(
|
|
901
|
+
console.log(import_chalk38.default.green(" Users & groups enabled."));
|
|
902
902
|
}
|
|
903
903
|
await addApiblazeHostedLogin(teamId, tenant2, { allowSkip: true });
|
|
904
904
|
return tenant2;
|
|
905
905
|
}
|
|
906
|
-
var
|
|
906
|
+
var import_chalk38, import_ora14, PROVIDERS, DEFAULT_SCOPES;
|
|
907
907
|
var init_tenant_create = __esm({
|
|
908
908
|
"src/lib/tenant-create.ts"() {
|
|
909
909
|
"use strict";
|
|
910
|
-
|
|
910
|
+
import_chalk38 = __toESM(require("chalk"));
|
|
911
911
|
import_ora14 = __toESM(require("ora"));
|
|
912
912
|
init_admin();
|
|
913
913
|
PROVIDERS = [
|
|
@@ -946,10 +946,10 @@ async function fetchPage2(teamId, q) {
|
|
|
946
946
|
}
|
|
947
947
|
function label(t, defaultTenant, active) {
|
|
948
948
|
const tags = [
|
|
949
|
-
t.tenant_name === active ?
|
|
950
|
-
t.tenant_name === defaultTenant ?
|
|
949
|
+
t.tenant_name === active ? import_chalk39.default.cyan("active scope") : "",
|
|
950
|
+
t.tenant_name === defaultTenant ? import_chalk39.default.dim("team default") : ""
|
|
951
951
|
].filter(Boolean).join(", ");
|
|
952
|
-
const disp = t.display_name && t.display_name !== t.tenant_name ?
|
|
952
|
+
const disp = t.display_name && t.display_name !== t.tenant_name ? import_chalk39.default.dim(` ${t.display_name}`) : "";
|
|
953
953
|
return `${t.tenant_name}${disp}${tags ? ` (${tags})` : ""}`;
|
|
954
954
|
}
|
|
955
955
|
async function pickTenant(teamId, opts = {}) {
|
|
@@ -964,7 +964,7 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
964
964
|
const { make } = await inquirer3.prompt([{ type: "confirm", name: "make", message: "No tenants yet \u2014 create one?", default: true }]);
|
|
965
965
|
if (make) return await createTenantInline(teamId);
|
|
966
966
|
}
|
|
967
|
-
console.error(
|
|
967
|
+
console.error(import_chalk39.default.red("This team has no tenants. Create one with `apiblaze tenant create`."));
|
|
968
968
|
return null;
|
|
969
969
|
}
|
|
970
970
|
const truncated = page.total > page.rows.length;
|
|
@@ -973,7 +973,7 @@ async function pickTenant(teamId, opts = {}) {
|
|
|
973
973
|
value: t.tenant_name
|
|
974
974
|
}));
|
|
975
975
|
if (truncated || q) {
|
|
976
|
-
choices.push(new inquirer3.Separator(
|
|
976
|
+
choices.push(new inquirer3.Separator(import_chalk39.default.dim(
|
|
977
977
|
truncated ? `showing ${page.rows.length} of ${page.total}${q ? ` matching "${q}"` : ""} \u2014 search to narrow` : `matches for "${q}"`
|
|
978
978
|
)));
|
|
979
979
|
choices.push({ name: `\u{1F50D} Search${q ? " again" : ""}\u2026`, value: "\0search" });
|
|
@@ -1007,11 +1007,11 @@ async function createTenantInline(teamId) {
|
|
|
1007
1007
|
const { createTenantInteractive: createTenantInteractive2 } = await Promise.resolve().then(() => (init_tenant_create(), tenant_create_exports));
|
|
1008
1008
|
return createTenantInteractive2(teamId);
|
|
1009
1009
|
}
|
|
1010
|
-
var
|
|
1010
|
+
var import_chalk39, import_ora15, PAGE;
|
|
1011
1011
|
var init_tenant_pick = __esm({
|
|
1012
1012
|
"src/lib/tenant-pick.ts"() {
|
|
1013
1013
|
"use strict";
|
|
1014
|
-
|
|
1014
|
+
import_chalk39 = __toESM(require("chalk"));
|
|
1015
1015
|
import_ora15 = __toESM(require("ora"));
|
|
1016
1016
|
init_admin();
|
|
1017
1017
|
init_auth();
|
|
@@ -1021,10 +1021,10 @@ var init_tenant_pick = __esm({
|
|
|
1021
1021
|
|
|
1022
1022
|
// src/index.ts
|
|
1023
1023
|
var import_commander = require("commander");
|
|
1024
|
-
var
|
|
1024
|
+
var import_chalk55 = __toESM(require("chalk"));
|
|
1025
1025
|
|
|
1026
1026
|
// package.json
|
|
1027
|
-
var version = "0.20.
|
|
1027
|
+
var version = "0.20.39";
|
|
1028
1028
|
|
|
1029
1029
|
// src/index.ts
|
|
1030
1030
|
init_types();
|
|
@@ -2102,6 +2102,7 @@ function suggestTenantName(projectSlug, taken) {
|
|
|
2102
2102
|
var fs8 = __toESM(require("fs"));
|
|
2103
2103
|
var path6 = __toESM(require("path"));
|
|
2104
2104
|
var crypto2 = __toESM(require("crypto"));
|
|
2105
|
+
var import_child_process3 = require("child_process");
|
|
2105
2106
|
var import_chalk14 = __toESM(require("chalk"));
|
|
2106
2107
|
var import_ora4 = __toESM(require("ora"));
|
|
2107
2108
|
var import_yaml2 = require("yaml");
|
|
@@ -2573,10 +2574,74 @@ function codexInteractive(_spec, prompt) {
|
|
|
2573
2574
|
}
|
|
2574
2575
|
var shellQuote = (s) => `"${s.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
2575
2576
|
function directedPrompt(spec2, question) {
|
|
2576
|
-
|
|
2577
|
+
const ctx = spec2.contextNote ? ` ${spec2.contextNote}` : "";
|
|
2578
|
+
return `Use the "${spec2.name}" MCP server (it exposes this API's tools) to answer: ${question}${ctx}`;
|
|
2579
|
+
}
|
|
2580
|
+
function agentInstructionFile(cli, dir = process.cwd()) {
|
|
2581
|
+
return path4.join(dir, cli === "claude" ? "CLAUDE.md" : "AGENTS.md");
|
|
2582
|
+
}
|
|
2583
|
+
var marker = (name) => ({
|
|
2584
|
+
start: `<!-- apiblaze:mcp:${name} -->`,
|
|
2585
|
+
end: `<!-- /apiblaze:mcp:${name} -->`
|
|
2586
|
+
});
|
|
2587
|
+
function stripMarkedBlock(text, name) {
|
|
2588
|
+
const m = marker(name);
|
|
2589
|
+
for (; ; ) {
|
|
2590
|
+
const i = text.indexOf(m.start);
|
|
2591
|
+
if (i < 0) return text;
|
|
2592
|
+
const j = text.indexOf(m.end, i);
|
|
2593
|
+
if (j < 0) return text.slice(0, i);
|
|
2594
|
+
let k = j + m.end.length;
|
|
2595
|
+
if (text[k] === "\n") k++;
|
|
2596
|
+
text = text.slice(0, i) + text.slice(k);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
function writeAgentInstruction(cli, spec2, dir = process.cwd()) {
|
|
2600
|
+
try {
|
|
2601
|
+
const file = agentInstructionFile(cli, dir);
|
|
2602
|
+
const m = marker(spec2.name);
|
|
2603
|
+
const block = [
|
|
2604
|
+
m.start,
|
|
2605
|
+
`## ${spec2.projectLabel} API \u2014 use the "${spec2.name}" MCP server`,
|
|
2606
|
+
"",
|
|
2607
|
+
`This directory is connected to the \`${spec2.name}\` MCP server, which exposes the`,
|
|
2608
|
+
`${spec2.projectLabel} API as tools. For any question about this API \u2014 its data, its`,
|
|
2609
|
+
"records, what it can do \u2014 call those tools rather than answering from memory or",
|
|
2610
|
+
"asking for data the API can provide.",
|
|
2611
|
+
...spec2.contextNote ? ["", spec2.contextNote] : [],
|
|
2612
|
+
"",
|
|
2613
|
+
`_Added by \`apiblaze apichat\`. Remove this block, or run \`npx apiblaze apichat ${spec2.name} --remove-mcp ${cli}\`._`,
|
|
2614
|
+
m.end,
|
|
2615
|
+
""
|
|
2616
|
+
].join("\n");
|
|
2617
|
+
let text = "";
|
|
2618
|
+
try {
|
|
2619
|
+
text = fs5.readFileSync(file, "utf-8");
|
|
2620
|
+
} catch {
|
|
2621
|
+
}
|
|
2622
|
+
const cleaned = stripMarkedBlock(text, spec2.name);
|
|
2623
|
+
const sep = cleaned.length && !cleaned.endsWith("\n\n") ? cleaned.endsWith("\n") ? "\n" : "\n\n" : "";
|
|
2624
|
+
fs5.writeFileSync(file, cleaned + sep + block, "utf-8");
|
|
2625
|
+
return file;
|
|
2626
|
+
} catch {
|
|
2627
|
+
return null;
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
function removeAgentInstruction(cli, name, dir = process.cwd()) {
|
|
2631
|
+
try {
|
|
2632
|
+
const file = agentInstructionFile(cli, dir);
|
|
2633
|
+
const text = fs5.readFileSync(file, "utf-8");
|
|
2634
|
+
const cleaned = stripMarkedBlock(text, name);
|
|
2635
|
+
if (cleaned === text) return false;
|
|
2636
|
+
fs5.writeFileSync(file, cleaned.replace(/\n{3,}/g, "\n\n"), "utf-8");
|
|
2637
|
+
return true;
|
|
2638
|
+
} catch {
|
|
2639
|
+
return false;
|
|
2640
|
+
}
|
|
2577
2641
|
}
|
|
2578
2642
|
function claudeSystemHint(spec2) {
|
|
2579
|
-
|
|
2643
|
+
const ctx = spec2.contextNote ? ` ${spec2.contextNote}` : "";
|
|
2644
|
+
return `You are connected to the "${spec2.name}" MCP server, which exposes the ${spec2.projectLabel} API as tools (mcp__${spec2.name}__*). For any question about this API, call those tools rather than answering from memory or asking the user for data the API can provide.${ctx}`;
|
|
2580
2645
|
}
|
|
2581
2646
|
function renderCommand(argv) {
|
|
2582
2647
|
return argv.map((a, i) => i === 0 || /^[A-Za-z0-9_@%+=:,./-]+$/.test(a) ? a : shellQuote(a)).join(" ");
|
|
@@ -2658,8 +2723,20 @@ async function installAndDemo(cli, spec2, getQuestion, log = console.log) {
|
|
|
2658
2723
|
if (ans.status !== 0) log(import_chalk12.default.yellow(`
|
|
2659
2724
|
${cli.label} exited with ${ans.status ?? "no status"} \u2014 the MCP stays installed.`));
|
|
2660
2725
|
}
|
|
2661
|
-
|
|
2662
|
-
|
|
2726
|
+
const instructionFile = writeAgentInstruction(cli.kind, spec2);
|
|
2727
|
+
const off = `npx apiblaze apichat ${spec2.name} --remove-mcp ${cli.kind}`;
|
|
2728
|
+
const mdName = cli.kind === "claude" ? "CLAUDE.md" : "AGENTS.md";
|
|
2729
|
+
log("");
|
|
2730
|
+
log(import_chalk12.default.bgRed.white.bold(` ${cli.label} will launch with your MCP to ${spec2.projectLabel} enabled from now on. `));
|
|
2731
|
+
log(import_chalk12.default.red.bold(` Every ${cli.label} session in this directory can call your API's tools.`));
|
|
2732
|
+
if (instructionFile) {
|
|
2733
|
+
log(import_chalk12.default.red(` We added a "use this MCP" block to your ${import_chalk12.default.bold(mdName)} \u2014 that is what makes it stick:`));
|
|
2734
|
+
log(import_chalk12.default.red(` ${instructionFile}`));
|
|
2735
|
+
} else {
|
|
2736
|
+
log(import_chalk12.default.red(` (Could not write ${mdName} here, so this applies only to sessions apichat starts.)`));
|
|
2737
|
+
}
|
|
2738
|
+
log(import_chalk12.default.red(` Turn it off (removes the MCP server AND the ${mdName} block):`));
|
|
2739
|
+
log(import_chalk12.default.red(` ${import_chalk12.default.bold(off)}`));
|
|
2663
2740
|
return true;
|
|
2664
2741
|
}
|
|
2665
2742
|
|
|
@@ -4137,6 +4214,9 @@ function rememberCliOffer(p, cli, state) {
|
|
|
4137
4214
|
}
|
|
4138
4215
|
function buildInstallSpec(p) {
|
|
4139
4216
|
return {
|
|
4217
|
+
// Control-plane tools take a team_id the model cannot guess; without it the
|
|
4218
|
+
// authorization check runs against `team:` (empty) and every call 403s.
|
|
4219
|
+
contextNote: p.projectId === "externalauth" && p.teamId ? `Your team_id is "${p.teamId}" \u2014 pass it as the team_id argument to any tool that accepts one, and use it wherever a team is required.` : void 0,
|
|
4140
4220
|
name: p.projectId,
|
|
4141
4221
|
url: `https://${p.mcpHost}/${p.version}/${p.environment}`,
|
|
4142
4222
|
// consumerAuth means the door is a login — install bare, the CLI signs in.
|
|
@@ -4180,6 +4260,12 @@ async function maybeInstallExternalCli(p, opts) {
|
|
|
4180
4260
|
);
|
|
4181
4261
|
if (!await ensureInstallableDoor(p, opts)) return false;
|
|
4182
4262
|
if (!await verifyAndHealMcpHost(p)) {
|
|
4263
|
+
if (p.projectId === "externalauth" && p.anon) {
|
|
4264
|
+
fail3(
|
|
4265
|
+
"Chatting with APIblaze needs an account for now.",
|
|
4266
|
+
"Run `apiblaze login` (free, keeps the proxies you already made), then re-run this command."
|
|
4267
|
+
);
|
|
4268
|
+
}
|
|
4183
4269
|
fail3(`The MCP endpoint https://${p.mcpHost}/${p.version}/${p.environment} is not answering \u2014 not installing it into ${cli.label}.`);
|
|
4184
4270
|
}
|
|
4185
4271
|
await authorizeClientRegistration(p, cli.label);
|
|
@@ -4334,9 +4420,112 @@ async function startChat(p, messages, opts) {
|
|
|
4334
4420
|
}
|
|
4335
4421
|
await runRepl(p, messages);
|
|
4336
4422
|
}
|
|
4423
|
+
function isApiblazeControlPlaneTarget(target) {
|
|
4424
|
+
try {
|
|
4425
|
+
const h = new URL(target).hostname.toLowerCase();
|
|
4426
|
+
return h === "api.mcp.apiblaze.com" || /^externalauth(-[a-z0-9]+)?\.mcp\.apiblaze\.com$/.test(h);
|
|
4427
|
+
} catch {
|
|
4428
|
+
return false;
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4431
|
+
function opTenantForTeam(teamId) {
|
|
4432
|
+
return ("op" + (teamId || "")).toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
4433
|
+
}
|
|
4434
|
+
async function openControlPlaneChat() {
|
|
4435
|
+
const creds = loadCredentials();
|
|
4436
|
+
const anonCred = creds?.teamId ? null : loadAnonCred();
|
|
4437
|
+
if (!creds?.teamId && !anonCred?.team_id) {
|
|
4438
|
+
fail3(
|
|
4439
|
+
"Chatting with APIblaze needs a workspace \u2014 its tools act on YOUR proxies.",
|
|
4440
|
+
"Make one first (no signup): apiblaze create --target <your api or spec> --auto"
|
|
4441
|
+
);
|
|
4442
|
+
}
|
|
4443
|
+
const teamId = creds?.teamId ?? anonCred.team_id;
|
|
4444
|
+
const tenant2 = opTenantForTeam(teamId);
|
|
4445
|
+
const projectId = "externalauth";
|
|
4446
|
+
const version2 = "1.0.0";
|
|
4447
|
+
const environment = "prod";
|
|
4448
|
+
const saved = loadApichats().find((a) => a.projectId === projectId);
|
|
4449
|
+
let dpKey = saved?.dpKey ?? anonCred?.cp_key;
|
|
4450
|
+
if (!dpKey) {
|
|
4451
|
+
const spinner = (0, import_ora4.default)("Preparing your APIblaze control-plane key\u2026").start();
|
|
4452
|
+
try {
|
|
4453
|
+
const out = await admin({
|
|
4454
|
+
method: "POST",
|
|
4455
|
+
path: `/teams/${encodeURIComponent(teamId)}/developer-keys`,
|
|
4456
|
+
body: { name: "apichat (APIblaze control plane)" },
|
|
4457
|
+
summary: "Mint a control-plane key for chatting with APIblaze"
|
|
4458
|
+
});
|
|
4459
|
+
dpKey = out?.key;
|
|
4460
|
+
spinner.stop();
|
|
4461
|
+
} catch (err) {
|
|
4462
|
+
spinner.fail("Could not mint a control-plane key.");
|
|
4463
|
+
throw err;
|
|
4464
|
+
}
|
|
4465
|
+
if (!dpKey) fail3("The server returned no control-plane key.");
|
|
4466
|
+
}
|
|
4467
|
+
const p = {
|
|
4468
|
+
projectId,
|
|
4469
|
+
version: version2,
|
|
4470
|
+
environment,
|
|
4471
|
+
dpKey,
|
|
4472
|
+
consumerAuth: false,
|
|
4473
|
+
mcpHost: `${projectId}-${tenant2}.mcp.apiblaze.com`,
|
|
4474
|
+
proxyUrl: `https://api.apiblaze.com/${version2}/${environment}`,
|
|
4475
|
+
anon: !creds?.teamId,
|
|
4476
|
+
access: "invite",
|
|
4477
|
+
tenant: tenant2,
|
|
4478
|
+
teamId
|
|
4479
|
+
};
|
|
4480
|
+
const who = creds?.teamName ?? (anonCred ? "your anonymous workspace" : teamId);
|
|
4481
|
+
console.log(` ${import_chalk14.default.dim("Chatting with:")} ${import_chalk14.default.bold("APIblaze itself")} ${import_chalk14.default.dim(`(${who})`)}`);
|
|
4482
|
+
if (anonCred) {
|
|
4483
|
+
console.log(import_chalk14.default.dim(" Anonymous workspace \u2014 `apiblaze login` then `/claim` inside the chat to keep it."));
|
|
4484
|
+
}
|
|
4485
|
+
upsertApichat({
|
|
4486
|
+
name: projectId,
|
|
4487
|
+
target: "https://api.mcp.apiblaze.com",
|
|
4488
|
+
projectId,
|
|
4489
|
+
version: version2,
|
|
4490
|
+
environment,
|
|
4491
|
+
mcpHost: p.mcpHost,
|
|
4492
|
+
teamId,
|
|
4493
|
+
tenant: tenant2,
|
|
4494
|
+
dpKey,
|
|
4495
|
+
consumerAuth: false,
|
|
4496
|
+
anon: false,
|
|
4497
|
+
ownerUserId: creds?.apiblazeUserId,
|
|
4498
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4499
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4500
|
+
});
|
|
4501
|
+
return { p };
|
|
4502
|
+
}
|
|
4503
|
+
async function removeExternalMcp(projectId, cliKind) {
|
|
4504
|
+
const kind = cliKind.toLowerCase();
|
|
4505
|
+
if (kind !== "claude" && kind !== "codex") fail3(`--remove-mcp takes "claude" or "codex", not "${cliKind}".`);
|
|
4506
|
+
const label3 = kind === "claude" ? "Claude CLI" : "Codex CLI";
|
|
4507
|
+
let removed = false;
|
|
4508
|
+
if (kind === "claude") {
|
|
4509
|
+
const r = (0, import_child_process3.spawnSync)("claude", ["mcp", "remove", projectId], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 15e3 });
|
|
4510
|
+
removed = r.status === 0;
|
|
4511
|
+
} else {
|
|
4512
|
+
removed = removeCodexServer(projectId);
|
|
4513
|
+
}
|
|
4514
|
+
console.log(removed ? ` ${import_chalk14.default.green("\u2714")} Removed the ${import_chalk14.default.bold(projectId)} MCP server from ${label3}.` : import_chalk14.default.dim(` ${label3} had no ${projectId} MCP server configured.`));
|
|
4515
|
+
const hadNote = removeAgentInstruction(kind, projectId);
|
|
4516
|
+
console.log(hadNote ? ` ${import_chalk14.default.green("\u2714")} Removed the standing instruction from ${agentInstructionFile(kind)}.` : import_chalk14.default.dim(` No standing instruction found in ${agentInstructionFile(kind)}.`));
|
|
4517
|
+
console.log(import_chalk14.default.dim(`
|
|
4518
|
+
${label3} will no longer launch with your API's tools in this directory.`));
|
|
4519
|
+
}
|
|
4337
4520
|
async function runApichat(opts) {
|
|
4338
4521
|
setVerbose(opts.verbose === true);
|
|
4339
4522
|
console.log(import_chalk14.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
4523
|
+
if (opts.target && isApiblazeControlPlaneTarget(opts.target)) {
|
|
4524
|
+
const cp = await openControlPlaneChat();
|
|
4525
|
+
if (await maybeInstallExternalCli(cp.p, opts)) return;
|
|
4526
|
+
await startChat(cp.p, [], opts);
|
|
4527
|
+
return;
|
|
4528
|
+
}
|
|
4340
4529
|
if (opts.target && !opts.openapispec) {
|
|
4341
4530
|
const { classifyTargetInput: classifyTargetInput2 } = await Promise.resolve().then(() => (init_spec_or_target(), spec_or_target_exports));
|
|
4342
4531
|
const c = await classifyTargetInput2(opts.target, fail3);
|
|
@@ -4346,6 +4535,11 @@ async function runApichat(opts) {
|
|
|
4346
4535
|
opts.target = void 0;
|
|
4347
4536
|
}
|
|
4348
4537
|
}
|
|
4538
|
+
if (opts.removeMcp) {
|
|
4539
|
+
if (!opts.project) fail3("Name the proxy: apiblaze apichat <project> --remove-mcp claude|codex");
|
|
4540
|
+
await removeExternalMcp(opts.project, opts.removeMcp);
|
|
4541
|
+
return;
|
|
4542
|
+
}
|
|
4349
4543
|
if (opts.project) {
|
|
4350
4544
|
const opened = await openDirectProject(opts.project, opts);
|
|
4351
4545
|
if (await maybeInstallExternalCli(opened.p, opts)) return;
|
|
@@ -5232,44 +5426,80 @@ async function runTeam(arg, opts = {}) {
|
|
|
5232
5426
|
activate(creds, chosen);
|
|
5233
5427
|
}
|
|
5234
5428
|
|
|
5429
|
+
// src/commands/docs.ts
|
|
5430
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
5431
|
+
var CONTROL_PLANE_TARGET = "https://api.mcp.apiblaze.com/openapi.json";
|
|
5432
|
+
async function runDocs(opts) {
|
|
5433
|
+
console.log("");
|
|
5434
|
+
console.log(import_chalk21.default.bgYellow.black.bold(" This is not a docs search \u2014 the chatbot can CHANGE your account. "));
|
|
5435
|
+
console.log(import_chalk21.default.yellow(` It connects ${import_chalk21.default.bold("Claude Code")} or ${import_chalk21.default.bold("Codex")} to the real APIblaze API, acting as you:`));
|
|
5436
|
+
console.log(import_chalk21.default.yellow(" it can create proxies, add logins, invite teammates and point domains for real."));
|
|
5437
|
+
console.log(import_chalk21.default.yellow(" Destructive actions (deleting proxies, teams or products) are deliberately not exposed."));
|
|
5438
|
+
console.log(import_chalk21.default.dim(' Read-only questions ("what proxies do I have?") are of course fine.'));
|
|
5439
|
+
console.log("");
|
|
5440
|
+
if (!opts.yes && !opts.prompt && process.stdin.isTTY) {
|
|
5441
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
5442
|
+
const { ok } = await inquirer3.prompt([{
|
|
5443
|
+
type: "confirm",
|
|
5444
|
+
name: "ok",
|
|
5445
|
+
default: true,
|
|
5446
|
+
message: "Continue, knowing the chatbot can act on your account?"
|
|
5447
|
+
}]);
|
|
5448
|
+
if (!ok) {
|
|
5449
|
+
console.log(import_chalk21.default.dim("\n Nothing connected. The written docs are at https://www.apiblaze.com/docs"));
|
|
5450
|
+
return;
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
const forwarded = {
|
|
5454
|
+
target: CONTROL_PLANE_TARGET,
|
|
5455
|
+
prompt: opts.prompt,
|
|
5456
|
+
installMcp: opts.installMcp,
|
|
5457
|
+
removeMcp: opts.removeMcp,
|
|
5458
|
+
verbose: opts.verbose,
|
|
5459
|
+
// `--remove-mcp` needs the project name; the control-plane proxy is `externalauth`.
|
|
5460
|
+
...opts.removeMcp ? { project: "externalauth" } : {}
|
|
5461
|
+
};
|
|
5462
|
+
await runApichat(forwarded);
|
|
5463
|
+
}
|
|
5464
|
+
|
|
5235
5465
|
// src/commands/authz.ts
|
|
5236
|
-
var
|
|
5466
|
+
var import_chalk24 = __toESM(require("chalk"));
|
|
5237
5467
|
init_auth();
|
|
5238
5468
|
init_api();
|
|
5239
5469
|
|
|
5240
5470
|
// src/lib/agent-chat.ts
|
|
5241
5471
|
var import_readline = __toESM(require("readline"));
|
|
5242
|
-
var
|
|
5472
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
5243
5473
|
init_api();
|
|
5244
5474
|
init_admin();
|
|
5245
5475
|
async function runAgentChatRepl(opts) {
|
|
5246
5476
|
const { title, subtitle, endpoint, buildBody, seedPrompt, summarizeProposal, commands } = opts;
|
|
5247
|
-
console.log("\n" +
|
|
5248
|
-
if (subtitle) console.log(
|
|
5477
|
+
console.log("\n" + import_chalk22.default.cyan.bold(title));
|
|
5478
|
+
if (subtitle) console.log(import_chalk22.default.dim(subtitle));
|
|
5249
5479
|
const messages = [];
|
|
5250
5480
|
const ctx = { proposal: null, lastData: null, log: (s) => console.log(s) };
|
|
5251
5481
|
const cmdByName = new Map(commands.map((c) => [c.name, c]));
|
|
5252
5482
|
const footer = () => {
|
|
5253
5483
|
const parts = [
|
|
5254
|
-
|
|
5255
|
-
...commands.map((c) =>
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5484
|
+
import_chalk22.default.dim("type to chat/refine"),
|
|
5485
|
+
...commands.map((c) => import_chalk22.default.dim(`/${c.name} ${c.describe}`)),
|
|
5486
|
+
import_chalk22.default.dim("/show"),
|
|
5487
|
+
import_chalk22.default.dim("/exit when done"),
|
|
5488
|
+
import_chalk22.default.dim("/drop discard changes")
|
|
5259
5489
|
];
|
|
5260
|
-
return " " +
|
|
5490
|
+
return " " + import_chalk22.default.dim("[ ") + parts.join(import_chalk22.default.dim(" \xB7 ")) + import_chalk22.default.dim(" ]");
|
|
5261
5491
|
};
|
|
5262
5492
|
async function turn(content) {
|
|
5263
5493
|
messages.push({ role: "user", content });
|
|
5264
|
-
process.stdout.write(
|
|
5494
|
+
process.stdout.write(import_chalk22.default.dim(" \u2026thinking\n"));
|
|
5265
5495
|
const { status, data } = await agentCall(endpoint, "POST", { messages, ...buildBody(messages) });
|
|
5266
5496
|
if (status === 402) {
|
|
5267
|
-
console.log(
|
|
5497
|
+
console.log(import_chalk22.default.red(" Insufficient credits \u2014 top up to keep using the agents.\n"));
|
|
5268
5498
|
messages.pop();
|
|
5269
5499
|
return;
|
|
5270
5500
|
}
|
|
5271
5501
|
if (status >= 400 || !data) {
|
|
5272
|
-
console.log(
|
|
5502
|
+
console.log(import_chalk22.default.red(` Error (${status}): ${(data && data.error) ?? "request failed"}
|
|
5273
5503
|
`));
|
|
5274
5504
|
messages.pop();
|
|
5275
5505
|
return;
|
|
@@ -5277,24 +5507,24 @@ async function runAgentChatRepl(opts) {
|
|
|
5277
5507
|
ctx.lastData = data;
|
|
5278
5508
|
const reply = data.reply ?? data.message ?? "(no reply)";
|
|
5279
5509
|
messages.push({ role: "assistant", content: reply });
|
|
5280
|
-
console.log("\n" +
|
|
5510
|
+
console.log("\n" + import_chalk22.default.cyan("agent \u203A ") + reply + "\n");
|
|
5281
5511
|
if (data.proposal) {
|
|
5282
5512
|
ctx.proposal = data.proposal;
|
|
5283
5513
|
const summary = summarizeProposal?.(data);
|
|
5284
|
-
if (summary) console.log(
|
|
5514
|
+
if (summary) console.log(import_chalk22.default.yellow(" " + summary));
|
|
5285
5515
|
const applies = commands.filter((c) => c.needsProposal);
|
|
5286
5516
|
const primary = applies[applies.length - 1];
|
|
5287
5517
|
if (primary) {
|
|
5288
|
-
console.log(
|
|
5518
|
+
console.log(import_chalk22.default.bold.green(" \u279C Next: type ") + import_chalk22.default.bold.cyan(`/${primary.name}`) + import_chalk22.default.bold.green(" to apply this, then ") + import_chalk22.default.bold.cyan("/exit") + import_chalk22.default.bold.green(" to leave."));
|
|
5289
5519
|
const rest = applies.slice(0, -1).map((c) => `/${c.name} = ${c.describe}`).join(" \xB7 ");
|
|
5290
|
-
console.log(
|
|
5520
|
+
console.log(import_chalk22.default.dim(` (or keep chatting to refine${rest ? ` \xB7 ${rest}` : ""} \xB7 /show = inspect)`));
|
|
5291
5521
|
}
|
|
5292
5522
|
console.log();
|
|
5293
5523
|
}
|
|
5294
5524
|
const line = formatBilling(data.billing);
|
|
5295
5525
|
if (line) {
|
|
5296
5526
|
const llm2 = data.llm;
|
|
5297
|
-
console.log(
|
|
5527
|
+
console.log(import_chalk22.default.dim(` ${line}${llm2?.model ? ` \xB7 ${llm2.model}` : ""}`));
|
|
5298
5528
|
}
|
|
5299
5529
|
}
|
|
5300
5530
|
const rl = import_readline.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -5302,31 +5532,31 @@ async function runAgentChatRepl(opts) {
|
|
|
5302
5532
|
await turn(seedPrompt);
|
|
5303
5533
|
for (; ; ) {
|
|
5304
5534
|
console.log(footer());
|
|
5305
|
-
const line = (await ask(
|
|
5535
|
+
const line = (await ask(import_chalk22.default.green("you \u203A "))).trim();
|
|
5306
5536
|
if (!line) continue;
|
|
5307
5537
|
const lower = line.toLowerCase();
|
|
5308
5538
|
if (line === "/exit" || line === "/quit" || lower === "exit" || lower === "quit" || lower === "q") {
|
|
5309
|
-
console.log(
|
|
5539
|
+
console.log(import_chalk22.default.dim(" Bye \u2014 anything you enabled or published stays in place.\n"));
|
|
5310
5540
|
break;
|
|
5311
5541
|
}
|
|
5312
5542
|
if (line === "/drop") {
|
|
5313
|
-
console.log(
|
|
5543
|
+
console.log(import_chalk22.default.dim(" Dropped the pending proposal \u2014 rules you already enabled/published are untouched.\n"));
|
|
5314
5544
|
break;
|
|
5315
5545
|
}
|
|
5316
5546
|
if (line === "/show") {
|
|
5317
|
-
if (!ctx.proposal) console.log(
|
|
5547
|
+
if (!ctx.proposal) console.log(import_chalk22.default.dim(" No proposal yet \u2014 keep chatting until the agent proposes one.\n"));
|
|
5318
5548
|
else console.log("\n" + JSON.stringify(ctx.proposal, null, 2) + "\n");
|
|
5319
5549
|
continue;
|
|
5320
5550
|
}
|
|
5321
5551
|
if (line.startsWith("/")) {
|
|
5322
5552
|
const cmd = cmdByName.get(line.slice(1));
|
|
5323
5553
|
if (!cmd) {
|
|
5324
|
-
console.log(
|
|
5554
|
+
console.log(import_chalk22.default.red(` Unknown command "${line}". Try one of: ${commands.map((c) => "/" + c.name).join(", ")}, /show, /drop
|
|
5325
5555
|
`));
|
|
5326
5556
|
continue;
|
|
5327
5557
|
}
|
|
5328
5558
|
if (cmd.needsProposal && !ctx.proposal) {
|
|
5329
|
-
console.log(
|
|
5559
|
+
console.log(import_chalk22.default.yellow(" No proposal yet \u2014 keep chatting until the agent generates one, then try again.\n"));
|
|
5330
5560
|
continue;
|
|
5331
5561
|
}
|
|
5332
5562
|
await cmd.run(ctx);
|
|
@@ -5338,7 +5568,7 @@ async function runAgentChatRepl(opts) {
|
|
|
5338
5568
|
}
|
|
5339
5569
|
|
|
5340
5570
|
// src/lib/authz-apply.ts
|
|
5341
|
-
var
|
|
5571
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
5342
5572
|
init_api();
|
|
5343
5573
|
async function gatherSampleIds(projectId, apiVersion, cap = 50) {
|
|
5344
5574
|
try {
|
|
@@ -5363,10 +5593,10 @@ async function gatherSampleIds(projectId, apiVersion, cap = 50) {
|
|
|
5363
5593
|
}
|
|
5364
5594
|
function maybeWarnNoTraffic(total, projectArg) {
|
|
5365
5595
|
if (total >= 3) return;
|
|
5366
|
-
console.log(
|
|
5367
|
-
console.log(
|
|
5368
|
-
console.log(
|
|
5369
|
-
console.log(
|
|
5596
|
+
console.log(import_chalk23.default.yellow(total === 0 ? " \u26A0 No captured traffic for this API yet." : ` \u26A0 Only ${total} captured request(s) so far.`));
|
|
5597
|
+
console.log(import_chalk23.default.dim(" The authorization agent designs much better models & rules from REAL requests"));
|
|
5598
|
+
console.log(import_chalk23.default.dim(" (it sees who is allowed vs denied). Send some through your proxy first \u2014"));
|
|
5599
|
+
console.log(import_chalk23.default.dim(` curl a few endpoints, or `) + import_chalk23.default.cyan("npx apiblaze dev") + import_chalk23.default.dim(` \u2014 then re-run.
|
|
5370
5600
|
`));
|
|
5371
5601
|
}
|
|
5372
5602
|
async function applyProposal(opts) {
|
|
@@ -5375,11 +5605,11 @@ async function applyProposal(opts) {
|
|
|
5375
5605
|
{
|
|
5376
5606
|
const m = await agentCall(`/projects/${projectId}/${apiVersion}/policies/model?tenantId=${encodeURIComponent(tenant2)}`, "POST", proposal.model);
|
|
5377
5607
|
if (m.status === 404) {
|
|
5378
|
-
log(
|
|
5608
|
+
log(import_chalk23.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"));
|
|
5379
5609
|
return { ok: 0, fail: 0, enabled: false, storeMissing: true };
|
|
5380
5610
|
}
|
|
5381
5611
|
if (m.status >= 400) {
|
|
5382
|
-
log(
|
|
5612
|
+
log(import_chalk23.default.red(` Model save failed (${m.status}): ${m.data?.error ?? ""}
|
|
5383
5613
|
`));
|
|
5384
5614
|
return { ok: 0, fail: 0, enabled: false };
|
|
5385
5615
|
}
|
|
@@ -5408,7 +5638,7 @@ async function applyProposal(opts) {
|
|
|
5408
5638
|
if (enable) {
|
|
5409
5639
|
const e = await agentCall(`/projects/${projectId}/${apiVersion}`, "PATCH", { authorization: { enforce_authorization: true }, tenant: tenant2 });
|
|
5410
5640
|
if (e.status >= 400) {
|
|
5411
|
-
log(
|
|
5641
|
+
log(import_chalk23.default.red(` Enforced ${ok} route(s) but turning on the project-level switch failed (${e.status}). Toggle "Enforce Authorization" on in the dashboard.
|
|
5412
5642
|
`));
|
|
5413
5643
|
return { ok, fail: fail6, enabled: false };
|
|
5414
5644
|
}
|
|
@@ -5434,10 +5664,10 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
5434
5664
|
const r = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable, log: ctx.log });
|
|
5435
5665
|
if (r.storeMissing) return;
|
|
5436
5666
|
if (enable && r.enabled) {
|
|
5437
|
-
ctx.log(
|
|
5438
|
-
ctx.log(
|
|
5667
|
+
ctx.log(import_chalk24.default.green(` \u2713 Enforcement ON \u2014 ${r.ok} route(s) + project switch on${r.fail ? `, ${r.fail} failed` : ""}.`));
|
|
5668
|
+
ctx.log(import_chalk24.default.dim(" Done here? Type /exit to leave the chat (your rule stays on).\n"));
|
|
5439
5669
|
} else if (!enable) {
|
|
5440
|
-
ctx.log(
|
|
5670
|
+
ctx.log(import_chalk24.default.green(` \u2713 Published (shadow): model + ${r.ok} route(s)${r.fail ? `, ${r.fail} failed` : ""}. Review, then /enable.
|
|
5441
5671
|
`));
|
|
5442
5672
|
}
|
|
5443
5673
|
}
|
|
@@ -5464,7 +5694,7 @@ async function runAuthz(projectArg, apiVersionArg) {
|
|
|
5464
5694
|
}
|
|
5465
5695
|
|
|
5466
5696
|
// src/commands/rule.ts
|
|
5467
|
-
var
|
|
5697
|
+
var import_chalk25 = __toESM(require("chalk"));
|
|
5468
5698
|
var import_ora8 = __toESM(require("ora"));
|
|
5469
5699
|
init_auth();
|
|
5470
5700
|
init_api();
|
|
@@ -5501,27 +5731,27 @@ async function runRule(ruleText, projectArg, opts) {
|
|
|
5501
5731
|
const proposal = data?.proposal;
|
|
5502
5732
|
if (!proposal || !Array.isArray(proposal.routes) || proposal.routes.length === 0) {
|
|
5503
5733
|
spinner.fail("The agent couldn\u2019t turn that into a rule in one shot.");
|
|
5504
|
-
if (data?.reply) console.log(
|
|
5505
|
-
console.log(
|
|
5734
|
+
if (data?.reply) console.log(import_chalk25.default.dim("\n " + String(data.reply).slice(0, 500) + "\n"));
|
|
5735
|
+
console.log(import_chalk25.default.dim(" Try an interactive session for anything nuanced: ") + import_chalk25.default.cyan(`apiblaze agent authz ${projectArg}`));
|
|
5506
5736
|
return;
|
|
5507
5737
|
}
|
|
5508
5738
|
spinner.succeed(`Rule designed \u2014 ${proposal.routes.length} route(s) affected.`);
|
|
5509
|
-
if (data?.reply) console.log(
|
|
5739
|
+
if (data?.reply) console.log(import_chalk25.default.dim(" " + String(data.reply).slice(0, 240)));
|
|
5510
5740
|
const pub = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable: false });
|
|
5511
5741
|
if (pub.storeMissing) return;
|
|
5512
|
-
console.log(
|
|
5742
|
+
console.log(import_chalk25.default.green(` \u2713 Published in shadow mode \u2014 model + ${pub.ok} route(s)${pub.fail ? `, ${pub.fail} failed` : ""}.`));
|
|
5513
5743
|
if (opts.enforce) {
|
|
5514
5744
|
const enf = await applyProposal({ projectId, apiVersion, tenant: tenant2, proposal, enable: true });
|
|
5515
5745
|
if (enf.enabled) {
|
|
5516
|
-
console.log(
|
|
5746
|
+
console.log(import_chalk25.default.green(` \u2713 Enforcement ON \u2014 ${enf.ok} route(s) + project switch on${enf.fail ? `, ${enf.fail} failed` : ""}.`));
|
|
5517
5747
|
}
|
|
5518
5748
|
} else {
|
|
5519
|
-
console.log(
|
|
5749
|
+
console.log(import_chalk25.default.dim(` Review it (shadow logs), then enforce: `) + import_chalk25.default.cyan(`apiblaze rule "${rule}" ${projectArg} --enforce`));
|
|
5520
5750
|
}
|
|
5521
5751
|
}
|
|
5522
5752
|
|
|
5523
5753
|
// src/commands/openapi.ts
|
|
5524
|
-
var
|
|
5754
|
+
var import_chalk26 = __toESM(require("chalk"));
|
|
5525
5755
|
init_auth();
|
|
5526
5756
|
init_api();
|
|
5527
5757
|
async function runOpenapi(projectArg, apiVersionArg) {
|
|
@@ -5532,10 +5762,10 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
5532
5762
|
if (!match) throw new Error(`Project "${projectArg}" not found in your active team. Run \`apiblaze projects\`.`);
|
|
5533
5763
|
const projectId = match.projectId;
|
|
5534
5764
|
const apiVersion = apiVersionArg || match.apiVersion;
|
|
5535
|
-
console.log(
|
|
5765
|
+
console.log(import_chalk26.default.dim("Gathering captured traffic samples\u2026"));
|
|
5536
5766
|
const routesRes = await agentCall(`/projects/${projectId}/${apiVersion}/samples/routes`, "GET");
|
|
5537
5767
|
if (routesRes.status >= 400) {
|
|
5538
|
-
console.log(
|
|
5768
|
+
console.log(import_chalk26.default.red(`Could not list traffic (${routesRes.status}): ${routesRes.data?.error ?? ""}`));
|
|
5539
5769
|
return;
|
|
5540
5770
|
}
|
|
5541
5771
|
const routes = routesRes.data?.routes ?? (Array.isArray(routesRes.data) ? routesRes.data : []);
|
|
@@ -5548,25 +5778,25 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
5548
5778
|
}
|
|
5549
5779
|
const sampleIds = [...ids];
|
|
5550
5780
|
if (sampleIds.length === 0) {
|
|
5551
|
-
console.log(
|
|
5781
|
+
console.log(import_chalk26.default.yellow("No captured traffic samples found. Hit the dev environment of this proxy to capture some traces, then retry."));
|
|
5552
5782
|
return;
|
|
5553
5783
|
}
|
|
5554
5784
|
async function publish(ctx) {
|
|
5555
5785
|
const patch = ctx.proposal.patch;
|
|
5556
5786
|
if (!Array.isArray(patch) || patch.length === 0) {
|
|
5557
|
-
ctx.log(
|
|
5787
|
+
ctx.log(import_chalk26.default.green(" Nothing to publish \u2014 the spec already covers the observed traffic.\n"));
|
|
5558
5788
|
return;
|
|
5559
5789
|
}
|
|
5560
5790
|
const specSource = ctx.lastData?.spec_source ?? "unknown";
|
|
5561
5791
|
const endpoint = specSource === "github" ? "open-pr" : "publish-openapi";
|
|
5562
5792
|
const pub = await agentCall(`/projects/${projectId}/${apiVersion}/samples/${endpoint}`, "POST", { patch, sample_ids_used: sampleIds });
|
|
5563
5793
|
if (pub.status >= 400) {
|
|
5564
|
-
ctx.log(
|
|
5794
|
+
ctx.log(import_chalk26.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
5565
5795
|
`));
|
|
5566
5796
|
return;
|
|
5567
5797
|
}
|
|
5568
5798
|
const prUrl = pub.data?.pr_url;
|
|
5569
|
-
ctx.log(
|
|
5799
|
+
ctx.log(import_chalk26.default.green(prUrl ? ` \u2713 Pull request opened: ${prUrl}
|
|
5570
5800
|
` : " \u2713 Published updated OpenAPI spec.\n"));
|
|
5571
5801
|
}
|
|
5572
5802
|
await runAgentChatRepl({
|
|
@@ -5587,7 +5817,7 @@ async function runOpenapi(projectArg, apiVersionArg) {
|
|
|
5587
5817
|
}
|
|
5588
5818
|
|
|
5589
5819
|
// src/commands/mcp.ts
|
|
5590
|
-
var
|
|
5820
|
+
var import_chalk27 = __toESM(require("chalk"));
|
|
5591
5821
|
init_auth();
|
|
5592
5822
|
init_api();
|
|
5593
5823
|
async function runMcp(projectArg, apiVersionArg, opts) {
|
|
@@ -5604,11 +5834,11 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
5604
5834
|
const spec2 = ctx.proposal;
|
|
5605
5835
|
const pub = await agentCall(`/projects/${projectId}/${apiVersion}/mcp/spec`, "PUT", { environment, spec: spec2 });
|
|
5606
5836
|
if (pub.status >= 400) {
|
|
5607
|
-
ctx.log(
|
|
5837
|
+
ctx.log(import_chalk27.default.red(` Publish failed (${pub.status}): ${pub.data?.error ?? ""}
|
|
5608
5838
|
`));
|
|
5609
5839
|
return;
|
|
5610
5840
|
}
|
|
5611
|
-
ctx.log(
|
|
5841
|
+
ctx.log(import_chalk27.default.green(` \u2713 Published MCP server \u2014 ${mcpHost}.mcp.apiblaze.com/${apiVersion}/${environment}.
|
|
5612
5842
|
`));
|
|
5613
5843
|
}
|
|
5614
5844
|
await runAgentChatRepl({
|
|
@@ -5630,7 +5860,7 @@ async function runMcp(projectArg, apiVersionArg, opts) {
|
|
|
5630
5860
|
}
|
|
5631
5861
|
|
|
5632
5862
|
// src/commands/delete.ts
|
|
5633
|
-
var
|
|
5863
|
+
var import_chalk28 = __toESM(require("chalk"));
|
|
5634
5864
|
var import_ora9 = __toESM(require("ora"));
|
|
5635
5865
|
init_admin();
|
|
5636
5866
|
init_resolve();
|
|
@@ -5656,10 +5886,10 @@ async function runDelete(project, version2, opts) {
|
|
|
5656
5886
|
return;
|
|
5657
5887
|
}
|
|
5658
5888
|
} else {
|
|
5659
|
-
console.log(`${
|
|
5889
|
+
console.log(`${import_chalk28.default.red.bold("Delete")} ${import_chalk28.default.bold(proj2.projectName)} ${import_chalk28.default.dim("v" + proj2.apiVersion)} ${import_chalk28.default.dim("(" + proj2.projectId + ")")}`);
|
|
5660
5890
|
if (impact && typeof impact === "object") {
|
|
5661
5891
|
const counts = impact.counts ?? impact.impact ?? impact;
|
|
5662
|
-
console.log(
|
|
5892
|
+
console.log(import_chalk28.default.dim(" This removes (cascade): ") + import_chalk28.default.yellow(JSON.stringify(counts)));
|
|
5663
5893
|
}
|
|
5664
5894
|
if (!opts.yes) {
|
|
5665
5895
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -5667,7 +5897,7 @@ async function runDelete(project, version2, opts) {
|
|
|
5667
5897
|
{ type: "confirm", name: "confirm", message: `Permanently delete ${proj2.projectName} v${proj2.apiVersion}? This cannot be undone.`, default: false }
|
|
5668
5898
|
]);
|
|
5669
5899
|
if (!confirm2) {
|
|
5670
|
-
console.log(
|
|
5900
|
+
console.log(import_chalk28.default.dim("Aborted."));
|
|
5671
5901
|
return;
|
|
5672
5902
|
}
|
|
5673
5903
|
}
|
|
@@ -5688,7 +5918,7 @@ async function runDelete(project, version2, opts) {
|
|
|
5688
5918
|
}
|
|
5689
5919
|
|
|
5690
5920
|
// src/commands/export.ts
|
|
5691
|
-
var
|
|
5921
|
+
var import_chalk29 = __toESM(require("chalk"));
|
|
5692
5922
|
var import_fs3 = require("fs");
|
|
5693
5923
|
init_admin();
|
|
5694
5924
|
init_auth();
|
|
@@ -5707,7 +5937,7 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
5707
5937
|
if (opts.noConsumers) q.set("include_consumers", "0");
|
|
5708
5938
|
if (opts.keys) q.set("keys", opts.keys);
|
|
5709
5939
|
const qs = q.toString() ? `?${q.toString()}` : "";
|
|
5710
|
-
console.log(
|
|
5940
|
+
console.log(import_chalk29.default.dim(`Queuing ${target} export of ${projectId}@${version2}\u2026`));
|
|
5711
5941
|
const job = await admin({
|
|
5712
5942
|
path: `/projects/${projectId}/${version2}/export/${target}${qs}`,
|
|
5713
5943
|
method: "GET",
|
|
@@ -5717,11 +5947,11 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
5717
5947
|
const statusPath = `/projects/${projectId}/${version2}/export/jobs/${job.job_id}`;
|
|
5718
5948
|
let status = "queued";
|
|
5719
5949
|
const started = Date.now();
|
|
5720
|
-
process.stdout.write(
|
|
5950
|
+
process.stdout.write(import_chalk29.default.dim("Building bundle"));
|
|
5721
5951
|
while (status !== "ready" && status !== "failed") {
|
|
5722
5952
|
if (Date.now() - started > 10 * 6e4) throw new Error("Export timed out after 10 minutes.");
|
|
5723
5953
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
5724
|
-
process.stdout.write(
|
|
5954
|
+
process.stdout.write(import_chalk29.default.dim("."));
|
|
5725
5955
|
const st = await admin({ path: statusPath, method: "GET", summary: "poll export" });
|
|
5726
5956
|
status = st?.status ?? "queued";
|
|
5727
5957
|
if (status === "failed") {
|
|
@@ -5739,14 +5969,14 @@ async function runExport(projectArg, versionArg, opts) {
|
|
|
5739
5969
|
if (!res.ok) throw new Error(`Download failed (${res.status}): ${await res.text().catch(() => "")}`);
|
|
5740
5970
|
const buf = Buffer.from(await res.arrayBuffer());
|
|
5741
5971
|
(0, import_fs3.writeFileSync)(outFile, buf);
|
|
5742
|
-
console.log(
|
|
5743
|
-
if (job.decryption_key) console.log(
|
|
5744
|
-
console.log(
|
|
5745
|
-
if (target === "kong") console.log(
|
|
5972
|
+
console.log(import_chalk29.default.green(`\u2713 Saved ${outFile} (${(buf.length / 1024).toFixed(0)} KB)`));
|
|
5973
|
+
if (job.decryption_key) console.log(import_chalk29.default.yellow(`Decryption key (shown once): ${job.decryption_key}`));
|
|
5974
|
+
console.log(import_chalk29.default.dim("Read MIGRATION-REPORT.json inside the zip for the full fidelity ledger."));
|
|
5975
|
+
if (target === "kong") console.log(import_chalk29.default.dim("Kong: unzip, then `cd kong && docker compose up` (see kong/README.md)."));
|
|
5746
5976
|
}
|
|
5747
5977
|
|
|
5748
5978
|
// src/commands/recipe-search.ts
|
|
5749
|
-
var
|
|
5979
|
+
var import_chalk30 = __toESM(require("chalk"));
|
|
5750
5980
|
var import_ora10 = __toESM(require("ora"));
|
|
5751
5981
|
init_caller();
|
|
5752
5982
|
|
|
@@ -5947,20 +6177,20 @@ async function runRecipeSearch(query, opts) {
|
|
|
5947
6177
|
return;
|
|
5948
6178
|
}
|
|
5949
6179
|
if (!hits.length) {
|
|
5950
|
-
console.log(
|
|
5951
|
-
console.log(
|
|
6180
|
+
console.log(import_chalk30.default.yellow(query ? `No public recipes match "${query}".` : "No public recipes yet."));
|
|
6181
|
+
console.log(import_chalk30.default.dim(" Publish one with: apiblaze publish <proxy> --as @yourhandle/name"));
|
|
5952
6182
|
return;
|
|
5953
6183
|
}
|
|
5954
6184
|
console.log();
|
|
5955
6185
|
for (const h of hits) {
|
|
5956
|
-
const pin = h.latest_revision ?
|
|
5957
|
-
const installs = h.install_count ?
|
|
5958
|
-
console.log(` ${
|
|
5959
|
-
if (h.summary) console.log(` ${
|
|
6186
|
+
const pin = h.latest_revision ? import_chalk30.default.dim(`@${h.latest_revision}`) : "";
|
|
6187
|
+
const installs = h.install_count ? import_chalk30.default.dim(` ${h.install_count} install${h.install_count === 1 ? "" : "s"}`) : "";
|
|
6188
|
+
console.log(` ${import_chalk30.default.bold(h.name)}${pin}${h.display_name ? import_chalk30.default.dim(` \u2014 ${h.display_name}`) : ""}${installs}`);
|
|
6189
|
+
if (h.summary) console.log(` ${import_chalk30.default.dim(h.summary)}`);
|
|
5960
6190
|
}
|
|
5961
6191
|
console.log();
|
|
5962
|
-
console.log(
|
|
5963
|
-
console.log(
|
|
6192
|
+
console.log(import_chalk30.default.dim(` Install one with: ${import_chalk30.default.cyan(`npx apiblaze install ${hits[0].name}`)}`));
|
|
6193
|
+
console.log(import_chalk30.default.dim(` Read one first: ${import_chalk30.default.cyan(`npx apiblaze show ${hits[0].name}`)}`));
|
|
5964
6194
|
console.log();
|
|
5965
6195
|
}
|
|
5966
6196
|
async function runRecipeShow(nameArg, opts) {
|
|
@@ -5986,8 +6216,8 @@ async function runRecipeShow(nameArg, opts) {
|
|
|
5986
6216
|
return;
|
|
5987
6217
|
}
|
|
5988
6218
|
console.log();
|
|
5989
|
-
console.log(`${
|
|
5990
|
-
if (recipe.display_name) console.log(` ${
|
|
6219
|
+
console.log(`${import_chalk30.default.bold(`${recipe.name}@${recipe.revision}`)}${import_chalk30.default.dim(` \u2014 by ${ref.handle}`)}`);
|
|
6220
|
+
if (recipe.display_name) console.log(` ${import_chalk30.default.bold(recipe.display_name)}`);
|
|
5991
6221
|
if (recipe.summary) console.log(` ${recipe.summary}`);
|
|
5992
6222
|
const meta = [
|
|
5993
6223
|
head?.install_count !== void 0 ? `${head.install_count} install${head.install_count === 1 ? "" : "s"}` : null,
|
|
@@ -5995,7 +6225,7 @@ async function runRecipeShow(nameArg, opts) {
|
|
|
5995
6225
|
recipe.published_at ? `published ${recipe.published_at.slice(0, 10)}` : null,
|
|
5996
6226
|
`api version ${recipe.api_version}`
|
|
5997
6227
|
].filter(Boolean).join(" \xB7 ");
|
|
5998
|
-
console.log(
|
|
6228
|
+
console.log(import_chalk30.default.dim(` ${meta}`));
|
|
5999
6229
|
console.log();
|
|
6000
6230
|
if (wantsSection) {
|
|
6001
6231
|
if (opts.settings) printSection("settings", recipe.settings);
|
|
@@ -6009,34 +6239,34 @@ async function runRecipeShow(nameArg, opts) {
|
|
|
6009
6239
|
const rules = recipe.transformations?.rules?.length ?? 0;
|
|
6010
6240
|
const tables = recipe.transformations?.mapping_tables?.length ?? 0;
|
|
6011
6241
|
const keyTypes = recipe.auth?.key_types?.length ?? 0;
|
|
6012
|
-
if (hosts.length) console.log(` ${
|
|
6013
|
-
console.log(` ${
|
|
6014
|
-
console.log(` ${
|
|
6242
|
+
if (hosts.length) console.log(` ${import_chalk30.default.dim("sends requests to")} ${hosts.join(", ")}`);
|
|
6243
|
+
console.log(` ${import_chalk30.default.dim("creates ")} ${describeCreates(keyTypes, routes, !!recipe.settings.mcp_enabled)}`);
|
|
6244
|
+
console.log(` ${import_chalk30.default.dim("asks you for ")} ${recipe.questions.length} value${recipe.questions.length === 1 ? "" : "s"}`);
|
|
6015
6245
|
if (rules || tables) {
|
|
6016
|
-
console.log(` ${
|
|
6246
|
+
console.log(` ${import_chalk30.default.dim("transforms ")} ${rules} rule${rules === 1 ? "" : "s"}${tables ? `, ${tables} mapping table${tables === 1 ? "" : "s"}` : ""}`);
|
|
6017
6247
|
}
|
|
6018
6248
|
if (recipe.settings.publish_openapi === true) {
|
|
6019
|
-
console.log(` ${
|
|
6249
|
+
console.log(` ${import_chalk30.default.dim("makes public ")} this API's spec`);
|
|
6020
6250
|
}
|
|
6021
6251
|
if (recipe.questions.length) {
|
|
6022
6252
|
console.log();
|
|
6023
|
-
console.log(
|
|
6253
|
+
console.log(import_chalk30.default.bold(" It will ask you for"));
|
|
6024
6254
|
for (const q of recipe.questions) {
|
|
6025
|
-
console.log(` ${q.prompt}${q.secret ?
|
|
6026
|
-
if (q.example) console.log(
|
|
6255
|
+
console.log(` ${q.prompt}${q.secret ? import_chalk30.default.dim(" (hidden while you type)") : ""}`);
|
|
6256
|
+
if (q.example) console.log(import_chalk30.default.dim(` e.g. ${q.example}`));
|
|
6027
6257
|
}
|
|
6028
6258
|
}
|
|
6029
6259
|
if (recipe.requires_confirmation?.length) {
|
|
6030
6260
|
console.log();
|
|
6031
|
-
console.log(
|
|
6261
|
+
console.log(import_chalk30.default.yellow(" Needs your explicit consent at install time"));
|
|
6032
6262
|
for (const c of recipe.requires_confirmation) {
|
|
6033
|
-
console.log(` ${
|
|
6034
|
-
console.log(
|
|
6263
|
+
console.log(` ${import_chalk30.default.bold(c.kind)} ${c.value}`);
|
|
6264
|
+
console.log(import_chalk30.default.dim(` ${c.why}`));
|
|
6035
6265
|
}
|
|
6036
6266
|
}
|
|
6037
6267
|
console.log();
|
|
6038
|
-
console.log(
|
|
6039
|
-
console.log(
|
|
6268
|
+
console.log(import_chalk30.default.dim(` Install: ${import_chalk30.default.cyan(`npx apiblaze install ${recipe.name}`)}`));
|
|
6269
|
+
console.log(import_chalk30.default.dim(" Sections: --settings --auth --transforms --openapi"));
|
|
6040
6270
|
console.log();
|
|
6041
6271
|
}
|
|
6042
6272
|
function describeCreates(keyTypes, routes, mcp, includeProxy = true) {
|
|
@@ -6046,45 +6276,45 @@ function describeCreates(keyTypes, routes, mcp, includeProxy = true) {
|
|
|
6046
6276
|
return parts.length ? parts.join(", ") : "nothing else";
|
|
6047
6277
|
}
|
|
6048
6278
|
function printSection(title, value) {
|
|
6049
|
-
console.log(
|
|
6279
|
+
console.log(import_chalk30.default.bold(` ${title}`));
|
|
6050
6280
|
console.log(indent(JSON.stringify(value ?? null, null, 2), " "));
|
|
6051
6281
|
console.log();
|
|
6052
6282
|
}
|
|
6053
6283
|
function printAuth(recipe) {
|
|
6054
6284
|
const a = recipe.auth ?? { app_client: null, provider: null, key_types: [] };
|
|
6055
|
-
console.log(
|
|
6285
|
+
console.log(import_chalk30.default.bold(" auth"));
|
|
6056
6286
|
if (a.provider) {
|
|
6057
|
-
console.log(` ${
|
|
6058
|
-
if (a.provider.scopes) console.log(` ${
|
|
6059
|
-
if (a.provider.target_server_token) console.log(` ${
|
|
6060
|
-
console.log(
|
|
6287
|
+
console.log(` ${import_chalk30.default.dim("provider ")} ${a.provider.type ?? "(unknown)"}`);
|
|
6288
|
+
if (a.provider.scopes) console.log(` ${import_chalk30.default.dim("scopes ")} ${a.provider.scopes}`);
|
|
6289
|
+
if (a.provider.target_server_token) console.log(` ${import_chalk30.default.dim("sends to API")} ${a.provider.target_server_token}`);
|
|
6290
|
+
console.log(import_chalk30.default.dim(" (no credentials travel in a recipe \u2014 install asks you for your own)"));
|
|
6061
6291
|
} else {
|
|
6062
|
-
console.log(
|
|
6292
|
+
console.log(import_chalk30.default.dim(" no login provider \u2014 consumers call it with an API key"));
|
|
6063
6293
|
}
|
|
6064
6294
|
if (a.app_client) {
|
|
6065
|
-
console.log(` ${
|
|
6295
|
+
console.log(` ${import_chalk30.default.dim("login app ")} ${a.app_client.name ?? "(unnamed)"}`);
|
|
6066
6296
|
}
|
|
6067
6297
|
if (a.key_types?.length) {
|
|
6068
|
-
console.log(` ${
|
|
6298
|
+
console.log(` ${import_chalk30.default.dim("key types ")} ${a.key_types.map((k) => String(k.name)).join(", ")}`);
|
|
6069
6299
|
}
|
|
6070
6300
|
console.log();
|
|
6071
6301
|
}
|
|
6072
6302
|
function printTransforms(recipe) {
|
|
6073
6303
|
const rules = recipe.transformations?.rules ?? [];
|
|
6074
6304
|
const tables = recipe.transformations?.mapping_tables ?? [];
|
|
6075
|
-
console.log(
|
|
6305
|
+
console.log(import_chalk30.default.bold(` transforms (${rules.length})`));
|
|
6076
6306
|
for (const r of rules) {
|
|
6077
|
-
const off = r.enabled === false ?
|
|
6078
|
-
console.log(` ${
|
|
6307
|
+
const off = r.enabled === false ? import_chalk30.default.dim(" (disabled)") : "";
|
|
6308
|
+
console.log(` ${import_chalk30.default.bold(String(r.name ?? "(unnamed)"))} ${import_chalk30.default.dim(String(r.phase ?? "request"))}${off}`);
|
|
6079
6309
|
console.log(indent(JSON.stringify(r.action ?? null, null, 2), " "));
|
|
6080
6310
|
if (r.condition) console.log(indent(`when ${JSON.stringify(r.condition)}`, " "));
|
|
6081
6311
|
}
|
|
6082
6312
|
if (tables.length) {
|
|
6083
6313
|
console.log();
|
|
6084
|
-
console.log(
|
|
6314
|
+
console.log(import_chalk30.default.bold(` mapping tables (${tables.length})`));
|
|
6085
6315
|
for (const t of tables) {
|
|
6086
6316
|
const entries2 = Array.isArray(t.entries) ? t.entries.length : 0;
|
|
6087
|
-
console.log(` ${
|
|
6317
|
+
console.log(` ${import_chalk30.default.bold(String(t.name ?? "(unnamed)"))} ${import_chalk30.default.dim(`${entries2} entr${entries2 === 1 ? "y" : "ies"}`)}`);
|
|
6088
6318
|
}
|
|
6089
6319
|
}
|
|
6090
6320
|
console.log();
|
|
@@ -6094,7 +6324,7 @@ function indent(text, pad) {
|
|
|
6094
6324
|
}
|
|
6095
6325
|
|
|
6096
6326
|
// src/commands/recipe-install.ts
|
|
6097
|
-
var
|
|
6327
|
+
var import_chalk31 = __toESM(require("chalk"));
|
|
6098
6328
|
init_types();
|
|
6099
6329
|
init_caller();
|
|
6100
6330
|
init_api();
|
|
@@ -6111,7 +6341,7 @@ async function runRecipeInstall(nameArg, opts) {
|
|
|
6111
6341
|
const chosenTeamId = picked.chosen ? picked.teamId : void 0;
|
|
6112
6342
|
const teamName = picked.teamName;
|
|
6113
6343
|
console.log();
|
|
6114
|
-
console.log(`${
|
|
6344
|
+
console.log(`${import_chalk31.default.bold(pinned)}${import_chalk31.default.dim(` \u2014 by ${ref.handle}`)}`);
|
|
6115
6345
|
if (recipe.summary) console.log(` ${recipe.summary}`);
|
|
6116
6346
|
console.log();
|
|
6117
6347
|
let project;
|
|
@@ -6135,7 +6365,7 @@ async function runRecipeInstall(nameArg, opts) {
|
|
|
6135
6365
|
reuseTenant = chosenTenant.existing;
|
|
6136
6366
|
printSummary(recipe, project, tenant2, reuseTenant);
|
|
6137
6367
|
if (!assumeYes && !await confirmYesNo(" continue?", interactive)) {
|
|
6138
|
-
console.log(
|
|
6368
|
+
console.log(import_chalk31.default.yellow(" Cancelled \u2014 nothing was created."));
|
|
6139
6369
|
return;
|
|
6140
6370
|
}
|
|
6141
6371
|
}
|
|
@@ -6154,18 +6384,18 @@ async function runRecipeInstall(nameArg, opts) {
|
|
|
6154
6384
|
process.stdout.write(` ${step.label.padEnd(33)}`);
|
|
6155
6385
|
try {
|
|
6156
6386
|
const note = await step.run();
|
|
6157
|
-
console.log(`${
|
|
6387
|
+
console.log(`${import_chalk31.default.green("\u2713")}${note ? ` ${import_chalk31.default.dim(note)}` : ""}`);
|
|
6158
6388
|
} catch (err) {
|
|
6159
|
-
console.log(`${
|
|
6389
|
+
console.log(`${import_chalk31.default.red("\u2717")} ${import_chalk31.default.red(describeError(err))}`);
|
|
6160
6390
|
console.log();
|
|
6161
6391
|
if (step.n === 1) {
|
|
6162
|
-
console.log(
|
|
6392
|
+
console.log(import_chalk31.default.red(`stopped at step 1 of ${steps.length}. Nothing was created.`));
|
|
6163
6393
|
console.log("run the same command again once the problem above is fixed:");
|
|
6164
|
-
console.log(
|
|
6394
|
+
console.log(import_chalk31.default.cyan(` npx apiblaze install ${nameArg}`));
|
|
6165
6395
|
} else {
|
|
6166
|
-
console.log(
|
|
6396
|
+
console.log(import_chalk31.default.red(`stopped at step ${step.n} of ${steps.length}. ${project} exists but is not finished.`));
|
|
6167
6397
|
console.log("continue with:");
|
|
6168
|
-
console.log(
|
|
6398
|
+
console.log(import_chalk31.default.cyan(` npx apiblaze install ${nameArg} --into ${project}`));
|
|
6169
6399
|
}
|
|
6170
6400
|
console.log();
|
|
6171
6401
|
process.exit(1);
|
|
@@ -6175,9 +6405,9 @@ async function runRecipeInstall(nameArg, opts) {
|
|
|
6175
6405
|
const urls = installUrls(ctx);
|
|
6176
6406
|
console.log();
|
|
6177
6407
|
console.log(`done in ${secs}s.`);
|
|
6178
|
-
console.log(` ${
|
|
6179
|
-
if (recipe.settings.mcp_enabled === true) console.log(` ${
|
|
6180
|
-
if (recipe.settings.enable_portal === true) console.log(` ${
|
|
6408
|
+
console.log(` ${import_chalk31.default.dim("proxy ")} ${import_chalk31.default.bold(urls.proxy_url)}`);
|
|
6409
|
+
if (recipe.settings.mcp_enabled === true) console.log(` ${import_chalk31.default.dim("mcp ")} ${import_chalk31.default.bold(urls.mcp_url)}`);
|
|
6410
|
+
if (recipe.settings.enable_portal === true) console.log(` ${import_chalk31.default.dim("portal")} ${import_chalk31.default.bold(urls.portal_url)}`);
|
|
6181
6411
|
console.log();
|
|
6182
6412
|
}
|
|
6183
6413
|
function buildSteps(inp) {
|
|
@@ -6493,7 +6723,7 @@ async function nameIsFree(name, teamId) {
|
|
|
6493
6723
|
async function chooseProxyName(slug, asOpt, teamId, interactive) {
|
|
6494
6724
|
if (asOpt !== void 0) {
|
|
6495
6725
|
const name = normalizeName3(asOpt);
|
|
6496
|
-
if (name !== asOpt) console.log(
|
|
6726
|
+
if (name !== asOpt) console.log(import_chalk31.default.yellow(` Using "${name}" \u2014 names are lowercase letters and digits only.`));
|
|
6497
6727
|
if (name.length < 3) throw new Error("A proxy name must be at least 3 characters (lowercase letters and digits only).");
|
|
6498
6728
|
const free = await nameIsFree(name, teamId);
|
|
6499
6729
|
if (free === false) throw new Error(`"${name}" is already taken. Pick another with --as.`);
|
|
@@ -6510,7 +6740,7 @@ async function chooseProxyName(slug, asOpt, teamId, interactive) {
|
|
|
6510
6740
|
}
|
|
6511
6741
|
if (!interactive) return suggested;
|
|
6512
6742
|
const { default: inquirer3 } = await import("inquirer");
|
|
6513
|
-
console.log(` ${
|
|
6743
|
+
console.log(` ${import_chalk31.default.dim("proxy")} ${slug} \u2192 ${import_chalk31.default.bold(suggested)} ${import_chalk31.default.dim("[enter to accept, or type a name]")}`);
|
|
6514
6744
|
for (; ; ) {
|
|
6515
6745
|
const { raw } = await inquirer3.prompt([{
|
|
6516
6746
|
type: "input",
|
|
@@ -6521,20 +6751,20 @@ async function chooseProxyName(slug, asOpt, teamId, interactive) {
|
|
|
6521
6751
|
}]);
|
|
6522
6752
|
const name = normalizeName3(raw);
|
|
6523
6753
|
if (name !== String(raw).trim()) {
|
|
6524
|
-
console.log(
|
|
6754
|
+
console.log(import_chalk31.default.yellow(` \u2192 using ${import_chalk31.default.bold(name)} (lowercase letters and digits only)`));
|
|
6525
6755
|
}
|
|
6526
6756
|
if (name.length < 3) {
|
|
6527
|
-
console.log(
|
|
6757
|
+
console.log(import_chalk31.default.yellow(" Name must be at least 3 characters.\n"));
|
|
6528
6758
|
continue;
|
|
6529
6759
|
}
|
|
6530
6760
|
const free = await nameIsFree(name, teamId);
|
|
6531
6761
|
if (free === false) {
|
|
6532
|
-
console.log(
|
|
6762
|
+
console.log(import_chalk31.default.yellow(` "${name}" is taken. Try another.
|
|
6533
6763
|
`));
|
|
6534
6764
|
continue;
|
|
6535
6765
|
}
|
|
6536
|
-
if (free === null) console.log(
|
|
6537
|
-
console.log(` ${
|
|
6766
|
+
if (free === null) console.log(import_chalk31.default.dim(" (could not verify availability; continuing)"));
|
|
6767
|
+
console.log(` ${import_chalk31.default.cyan("\u2192")} ${import_chalk31.default.bold(`https://${name}.abz.run`)}`);
|
|
6538
6768
|
return name;
|
|
6539
6769
|
}
|
|
6540
6770
|
}
|
|
@@ -6552,20 +6782,20 @@ function printSummary(recipe, project, tenant2, reuseTenant = false) {
|
|
|
6552
6782
|
const keyTypes = recipe.auth?.key_types?.length ?? 0;
|
|
6553
6783
|
const questions = reuseTenant ? 0 : recipe.questions?.length ?? 0;
|
|
6554
6784
|
const row = (label3, value, note) => {
|
|
6555
|
-
console.log(` ${
|
|
6785
|
+
console.log(` ${import_chalk31.default.dim(label3.padEnd(21))}${value}${note ? import_chalk31.default.dim(` ${note}`) : ""}`);
|
|
6556
6786
|
};
|
|
6557
6787
|
console.log();
|
|
6558
|
-
console.log(
|
|
6788
|
+
console.log(import_chalk31.default.bold(" This will:"));
|
|
6559
6789
|
console.log();
|
|
6560
|
-
row("create a proxy",
|
|
6561
|
-
row("for its users",
|
|
6790
|
+
row("create a proxy", import_chalk31.default.bold(project), `at https://${project}.abz.run`);
|
|
6791
|
+
row("for its users", import_chalk31.default.bold(tenant2), reuseTenant ? "(one you already have)" : "(a new tenant)");
|
|
6562
6792
|
if (hosts.length) row("sending requests to", hosts.join(", "));
|
|
6563
6793
|
row("and set up", describeCreates(keyTypes, routes, recipe.settings.mcp_enabled === true, false));
|
|
6564
6794
|
row("asking you for", questions ? `${questions} value${questions === 1 ? "" : "s"}` : "nothing");
|
|
6565
6795
|
if (reuseTenant) {
|
|
6566
6796
|
console.log();
|
|
6567
|
-
console.log(
|
|
6568
|
-
console.log(
|
|
6797
|
+
console.log(import_chalk31.default.dim(` ${tenant2} is used exactly as it is \u2014 its existing login stays, and this`));
|
|
6798
|
+
console.log(import_chalk31.default.dim(" recipe's own login setup is not applied."));
|
|
6569
6799
|
}
|
|
6570
6800
|
console.log();
|
|
6571
6801
|
}
|
|
@@ -6574,11 +6804,11 @@ function declineAll(items) {
|
|
|
6574
6804
|
const upstreams = items.filter((i) => i.kind === "upstream");
|
|
6575
6805
|
console.log();
|
|
6576
6806
|
for (const u of upstreams) {
|
|
6577
|
-
console.log(
|
|
6807
|
+
console.log(import_chalk31.default.yellow(` --yes: this proxy sends every request to ${import_chalk31.default.bold(u.value)}.`));
|
|
6578
6808
|
}
|
|
6579
6809
|
if (skippable.length) {
|
|
6580
|
-
console.log(
|
|
6581
|
-
for (const i of skippable) console.log(
|
|
6810
|
+
console.log(import_chalk31.default.yellow(` --yes: installing without ${skippable.length} setting${skippable.length === 1 ? "" : "s"} that need consent.`));
|
|
6811
|
+
for (const i of skippable) console.log(import_chalk31.default.dim(` skipped ${i.kind} ${i.value}`));
|
|
6582
6812
|
}
|
|
6583
6813
|
return new Set(skippable.map((i) => i.kind));
|
|
6584
6814
|
}
|
|
@@ -6596,14 +6826,14 @@ async function askConsent(items, interactive) {
|
|
|
6596
6826
|
const count = items.length;
|
|
6597
6827
|
const word = ["", "it", "both", "all three", "all four", "all five", "all six"][count] ?? `all ${count}`;
|
|
6598
6828
|
console.log();
|
|
6599
|
-
console.log(
|
|
6829
|
+
console.log(import_chalk31.default.yellow("\u26A0 This recipe changes who can reach your proxy, what it trusts, and what it can cost."));
|
|
6600
6830
|
console.log();
|
|
6601
6831
|
for (const item of items) {
|
|
6602
|
-
console.log(` ${
|
|
6603
|
-
for (const line of wrapText(item.why, 62)) console.log(` ${" ".repeat(16)} ${
|
|
6832
|
+
console.log(` ${import_chalk31.default.bold(item.kind.padEnd(16))} ${item.value}`);
|
|
6833
|
+
for (const line of wrapText(item.why, 62)) console.log(` ${" ".repeat(16)} ${import_chalk31.default.dim(line)}`);
|
|
6604
6834
|
}
|
|
6605
6835
|
console.log();
|
|
6606
|
-
console.log(`Type "${
|
|
6836
|
+
console.log(`Type "${import_chalk31.default.bold("yes")}" to accept ${word}.`);
|
|
6607
6837
|
console.log("Press enter to install the recipe without them.");
|
|
6608
6838
|
let typed = "";
|
|
6609
6839
|
if (interactive) {
|
|
@@ -6615,7 +6845,7 @@ async function askConsent(items, interactive) {
|
|
|
6615
6845
|
for (const item of items) {
|
|
6616
6846
|
declined.add(item.kind === "transform" ? `transform:${item.value}` : `kind:${item.kind}`);
|
|
6617
6847
|
}
|
|
6618
|
-
console.log(
|
|
6848
|
+
console.log(import_chalk31.default.dim(` Installing without ${count} setting${count === 1 ? "" : "s"} \u2014 everything else is applied.`));
|
|
6619
6849
|
return declined;
|
|
6620
6850
|
}
|
|
6621
6851
|
async function askQuestions(questions, ctx, interactive) {
|
|
@@ -6628,13 +6858,13 @@ async function askQuestions(questions, ctx, interactive) {
|
|
|
6628
6858
|
const BOX = 64;
|
|
6629
6859
|
const title = "\u2500\u2500 from the publisher ";
|
|
6630
6860
|
console.log();
|
|
6631
|
-
console.log(` ${
|
|
6632
|
-
for (const line of fillContext(q.before, ctx).split("\n")) console.log(` ${
|
|
6633
|
-
console.log(` ${
|
|
6861
|
+
console.log(` ${import_chalk31.default.dim(title + "\u2500".repeat(BOX - title.length))}`);
|
|
6862
|
+
for (const line of fillContext(q.before, ctx).split("\n")) console.log(` ${import_chalk31.default.dim("\u2502")} ${line}`);
|
|
6863
|
+
console.log(` ${import_chalk31.default.dim("\u2500".repeat(BOX))}`);
|
|
6634
6864
|
}
|
|
6635
6865
|
if (fromEnv !== void 0 && fromEnv !== "") {
|
|
6636
6866
|
answers[q.id] = fromEnv;
|
|
6637
|
-
console.log(` ${q.prompt}: ${
|
|
6867
|
+
console.log(` ${q.prompt}: ${import_chalk31.default.dim(`(from ${envName})`)}`);
|
|
6638
6868
|
continue;
|
|
6639
6869
|
}
|
|
6640
6870
|
if (!interactive) {
|
|
@@ -6644,7 +6874,7 @@ async function askQuestions(questions, ctx, interactive) {
|
|
|
6644
6874
|
);
|
|
6645
6875
|
}
|
|
6646
6876
|
const { default: inquirer3 } = await import("inquirer");
|
|
6647
|
-
if (q.example && !q.secret) console.log(
|
|
6877
|
+
if (q.example && !q.secret) console.log(import_chalk31.default.dim(` e.g. ${q.example}`));
|
|
6648
6878
|
const { value } = await inquirer3.prompt([{
|
|
6649
6879
|
// `secret: true` masks the input — the Google client secret is typed once,
|
|
6650
6880
|
// goes to provider creation, and is never echoed or stored.
|
|
@@ -6678,30 +6908,30 @@ async function confirmReinstall(recipe, project, tenant2, interactive) {
|
|
|
6678
6908
|
const tables = recipe.transformations?.mapping_tables?.length ?? 0;
|
|
6679
6909
|
const routes = countRoutes(recipe.openapi);
|
|
6680
6910
|
const keyTypes = recipe.auth?.key_types?.length ?? 0;
|
|
6681
|
-
console.log(`This installs ${
|
|
6911
|
+
console.log(`This installs ${import_chalk31.default.bold(`${recipe.name}@${recipe.revision}`)} over ${import_chalk31.default.bold(project)}.`);
|
|
6682
6912
|
console.log();
|
|
6683
|
-
console.log(` ${
|
|
6913
|
+
console.log(` ${import_chalk31.default.dim("settings ".padEnd(16))}replaced`);
|
|
6684
6914
|
if (rules || tables) {
|
|
6685
|
-
console.log(` ${
|
|
6915
|
+
console.log(` ${import_chalk31.default.dim("transforms".padEnd(16))}${rules} rule${rules === 1 ? "" : "s"}${tables ? ` and ${tables} mapping table${tables === 1 ? "" : "s"}` : ""} replaced \u2014 your edits to them are lost`);
|
|
6686
6916
|
}
|
|
6687
6917
|
if (routes) {
|
|
6688
|
-
console.log(` ${
|
|
6918
|
+
console.log(` ${import_chalk31.default.dim("api spec ".padEnd(16))}replaced, ${routes} routes regenerated${recipe.settings.mcp_enabled === true ? ", MCP rebuilt" : ""}`);
|
|
6689
6919
|
}
|
|
6690
6920
|
if (keyTypes) {
|
|
6691
|
-
console.log(` ${
|
|
6921
|
+
console.log(` ${import_chalk31.default.dim("key types ".padEnd(16))}${keyTypes} added if missing; existing types left alone`);
|
|
6692
6922
|
}
|
|
6693
6923
|
console.log();
|
|
6694
|
-
console.log(` ${
|
|
6924
|
+
console.log(` ${import_chalk31.default.dim("unchanged ".padEnd(16))}your API keys, your proxy URL, your users,`);
|
|
6695
6925
|
console.log(` ${" ".repeat(16)}and any tenant other than ${tenant2}`);
|
|
6696
6926
|
console.log();
|
|
6697
|
-
console.log(
|
|
6927
|
+
console.log(import_chalk31.default.yellow("This cannot be undone."));
|
|
6698
6928
|
if (!interactive) {
|
|
6699
6929
|
throw new Error("Reinstall needs a typed confirmation and there is no terminal to ask on.");
|
|
6700
6930
|
}
|
|
6701
6931
|
const { default: inquirer3 } = await import("inquirer");
|
|
6702
6932
|
const { typed } = await inquirer3.prompt([{ type: "input", name: "typed", message: `Type the proxy name (${project}) to continue:` }]);
|
|
6703
6933
|
if (String(typed ?? "").trim() !== project) {
|
|
6704
|
-
console.log(
|
|
6934
|
+
console.log(import_chalk31.default.yellow("Cancelled \u2014 nothing was changed."));
|
|
6705
6935
|
process.exit(0);
|
|
6706
6936
|
}
|
|
6707
6937
|
}
|
|
@@ -6709,7 +6939,7 @@ function requireCallerForInstall() {
|
|
|
6709
6939
|
try {
|
|
6710
6940
|
return requireCaller();
|
|
6711
6941
|
} catch {
|
|
6712
|
-
console.error(
|
|
6942
|
+
console.error(import_chalk31.default.red(
|
|
6713
6943
|
"Installing writes a proxy into an account, so it needs a credential.\n Run `apiblaze login`, or `apiblaze create` first (an anonymous workspace works too)."
|
|
6714
6944
|
));
|
|
6715
6945
|
process.exit(1);
|
|
@@ -6745,7 +6975,7 @@ async function chooseTeam(caller, optTeam, interactive) {
|
|
|
6745
6975
|
message: " which team should own this proxy?",
|
|
6746
6976
|
default: active.teamId,
|
|
6747
6977
|
choices: teams.map((t) => ({
|
|
6748
|
-
name: t.teamId === active.teamId ? `${t.name} ${
|
|
6978
|
+
name: t.teamId === active.teamId ? `${t.name} ${import_chalk31.default.dim("(active)")}` : t.name,
|
|
6749
6979
|
value: t.teamId
|
|
6750
6980
|
}))
|
|
6751
6981
|
}]);
|
|
@@ -6773,10 +7003,10 @@ async function chooseTenant(caller, project, teamId, optTenant, interactive) {
|
|
|
6773
7003
|
message: " where should this proxy's users live?",
|
|
6774
7004
|
default: "__new__",
|
|
6775
7005
|
choices: [
|
|
6776
|
-
{ name: `create ${fresh} ${
|
|
6777
|
-
new inquirer3.Separator(
|
|
7006
|
+
{ name: `create ${fresh} ${import_chalk31.default.dim("(a fresh tenant, set up by the recipe)")}`, value: "__new__" },
|
|
7007
|
+
new inquirer3.Separator(import_chalk31.default.dim(" \u2014 or reuse one you already have \u2014")),
|
|
6778
7008
|
...owned.map((t2) => ({
|
|
6779
|
-
name: `${t2} ${
|
|
7009
|
+
name: `${t2} ${import_chalk31.default.dim("(existing \u2014 its own login is kept)")}`,
|
|
6780
7010
|
value: t2
|
|
6781
7011
|
}))
|
|
6782
7012
|
]
|
|
@@ -6803,7 +7033,7 @@ async function teamTenants(caller, teamId) {
|
|
|
6803
7033
|
}
|
|
6804
7034
|
|
|
6805
7035
|
// src/commands/recipe-publish.ts
|
|
6806
|
-
var
|
|
7036
|
+
var import_chalk32 = __toESM(require("chalk"));
|
|
6807
7037
|
var import_ora11 = __toESM(require("ora"));
|
|
6808
7038
|
init_caller();
|
|
6809
7039
|
init_auth();
|
|
@@ -6813,15 +7043,15 @@ async function runRecipePublish(projectArg, opts) {
|
|
|
6813
7043
|
try {
|
|
6814
7044
|
resolved = resolveRecipeName(opts.as, creds?.githubHandle, normalizeName3(projectArg));
|
|
6815
7045
|
} catch (err) {
|
|
6816
|
-
console.error(
|
|
7046
|
+
console.error(import_chalk32.default.red(err instanceof Error ? err.message : String(err)));
|
|
6817
7047
|
process.exitCode = 1;
|
|
6818
7048
|
return;
|
|
6819
7049
|
}
|
|
6820
7050
|
const ref = resolved.ref;
|
|
6821
7051
|
if (resolved.derivedHandle) {
|
|
6822
|
-
console.log(
|
|
7052
|
+
console.log(import_chalk32.default.dim(` publishing as ${import_chalk32.default.bold(ref.name)}`));
|
|
6823
7053
|
if (resolved.normalizedFrom) {
|
|
6824
|
-
console.log(
|
|
7054
|
+
console.log(import_chalk32.default.dim(` (your GitHub login is ${resolved.normalizedFrom}; apiblaze names are letters and digits only)`));
|
|
6825
7055
|
}
|
|
6826
7056
|
}
|
|
6827
7057
|
const interactive = !!process.stdin.isTTY && !opts.json;
|
|
@@ -6840,7 +7070,7 @@ async function runRecipePublish(projectArg, opts) {
|
|
|
6840
7070
|
let tenant2 = opts.tenant;
|
|
6841
7071
|
if (!prep.tenant_used && prep.tenants.length > 1) {
|
|
6842
7072
|
if (!interactive) {
|
|
6843
|
-
console.error(
|
|
7073
|
+
console.error(import_chalk32.default.red(
|
|
6844
7074
|
`${proj2.projectId} has ${prep.tenants.length} tenants (${prep.tenants.join(", ")}). Pass --tenant <slug> to say which auth setup the recipe carries.`
|
|
6845
7075
|
));
|
|
6846
7076
|
process.exit(1);
|
|
@@ -6856,7 +7086,7 @@ async function runRecipePublish(projectArg, opts) {
|
|
|
6856
7086
|
prep = await prepare(caller, proj2.projectId, proj2.apiVersion, ref.name, tenant2);
|
|
6857
7087
|
}
|
|
6858
7088
|
if (!prep.name_available) {
|
|
6859
|
-
console.error(
|
|
7089
|
+
console.error(import_chalk32.default.red(`${ref.name} belongs to another team, so you cannot publish a revision of it.`));
|
|
6860
7090
|
process.exit(1);
|
|
6861
7091
|
}
|
|
6862
7092
|
if (prep.envelope_violations.length) {
|
|
@@ -6866,13 +7096,13 @@ async function runRecipePublish(projectArg, opts) {
|
|
|
6866
7096
|
if (prep.dropped.length) {
|
|
6867
7097
|
console.log();
|
|
6868
7098
|
console.log(`${prep.dropped.length} setting${prep.dropped.length === 1 ? " was" : "s were"} not included, because they are not on the publish list:`);
|
|
6869
|
-
for (const d of prep.dropped) console.log(
|
|
7099
|
+
for (const d of prep.dropped) console.log(import_chalk32.default.dim(` ${d}`));
|
|
6870
7100
|
if (prep.hidden_tables_excluded) {
|
|
6871
|
-
console.log(
|
|
7101
|
+
console.log(import_chalk32.default.dim(` (${prep.hidden_tables_excluded} hidden mapping table${prep.hidden_tables_excluded === 1 ? "" : "s"} excluded \u2014 those values belong to your consumers.)`));
|
|
6872
7102
|
}
|
|
6873
7103
|
console.log();
|
|
6874
7104
|
if (!opts.yes && !await confirm(" continue without them?", interactive)) {
|
|
6875
|
-
console.log(
|
|
7105
|
+
console.log(import_chalk32.default.yellow("Cancelled."));
|
|
6876
7106
|
return;
|
|
6877
7107
|
}
|
|
6878
7108
|
}
|
|
@@ -6885,12 +7115,12 @@ async function runRecipePublish(projectArg, opts) {
|
|
|
6885
7115
|
console.log();
|
|
6886
7116
|
for (const f of prep.findings) printFinding(f);
|
|
6887
7117
|
console.log();
|
|
6888
|
-
console.error(
|
|
7118
|
+
console.error(import_chalk32.default.red("--yes cannot accept scanner findings. Fix the value in the proxy, or publish interactively and decide."));
|
|
6889
7119
|
process.exit(1);
|
|
6890
7120
|
}
|
|
6891
7121
|
if (!interactive) {
|
|
6892
7122
|
for (const f of prep.findings) printFinding(f);
|
|
6893
|
-
console.error(
|
|
7123
|
+
console.error(import_chalk32.default.red("\nThese need a decision and there is no terminal to ask on."));
|
|
6894
7124
|
process.exit(1);
|
|
6895
7125
|
}
|
|
6896
7126
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -6911,10 +7141,10 @@ async function runRecipePublish(projectArg, opts) {
|
|
|
6911
7141
|
if (verdict === "ask") promotions.push(await askPromotion(f.path));
|
|
6912
7142
|
}
|
|
6913
7143
|
}
|
|
6914
|
-
console.log(
|
|
7144
|
+
console.log(import_chalk32.default.dim(`we cannot judge the other ${prep.unjudged_count} values. run \`apiblaze show ${ref.name}\` after publishing to read the whole recipe.`));
|
|
6915
7145
|
const totalQuestions = prep.question_ids.length + promotions.length;
|
|
6916
7146
|
if (totalQuestions > 5) {
|
|
6917
|
-
console.log(
|
|
7147
|
+
console.log(import_chalk32.default.yellow(`
|
|
6918
7148
|
\u26A0 installers will have to supply ${totalQuestions} values before this works.`));
|
|
6919
7149
|
}
|
|
6920
7150
|
const visibility = opts.private ? "private" : "public";
|
|
@@ -6922,11 +7152,11 @@ async function runRecipePublish(projectArg, opts) {
|
|
|
6922
7152
|
if (visibility === "public" && prep.spec_will_become_public) {
|
|
6923
7153
|
console.log();
|
|
6924
7154
|
console.log("Publishing publicly also makes this API's spec readable at");
|
|
6925
|
-
console.log(` ${
|
|
7155
|
+
console.log(` ${import_chalk32.default.bold(`https://${proj2.projectId}.abz.run/${proj2.apiVersion}/openapi.json`)}`);
|
|
6926
7156
|
console.log("It is currently private.");
|
|
6927
7157
|
confirmSpecPublic = opts.yes ? true : await confirm("Continue?", interactive);
|
|
6928
7158
|
if (!confirmSpecPublic) {
|
|
6929
|
-
console.log(
|
|
7159
|
+
console.log(import_chalk32.default.yellow("Cancelled \u2014 nothing was published."));
|
|
6930
7160
|
return;
|
|
6931
7161
|
}
|
|
6932
7162
|
}
|
|
@@ -6952,7 +7182,7 @@ async function runRecipePublish(projectArg, opts) {
|
|
|
6952
7182
|
},
|
|
6953
7183
|
summary: `Publish ${proj2.projectId} as ${ref.name} (${visibility})`
|
|
6954
7184
|
});
|
|
6955
|
-
spin2?.succeed(`Published ${
|
|
7185
|
+
spin2?.succeed(`Published ${import_chalk32.default.bold(`${out.name}@${out.revision}`)} (${out.visibility}).`);
|
|
6956
7186
|
} catch (err) {
|
|
6957
7187
|
spin2?.fail("Publish failed.");
|
|
6958
7188
|
throw err;
|
|
@@ -6961,15 +7191,15 @@ async function runRecipePublish(projectArg, opts) {
|
|
|
6961
7191
|
console.log(JSON.stringify(out));
|
|
6962
7192
|
return;
|
|
6963
7193
|
}
|
|
6964
|
-
console.log(` ${
|
|
6965
|
-
console.log(` ${
|
|
7194
|
+
console.log(` ${import_chalk32.default.dim("size ")} ${(out.size_bytes / 1024).toFixed(1)} KB`);
|
|
7195
|
+
console.log(` ${import_chalk32.default.dim("checksum")} ${out.sha256.slice(0, 16)}\u2026`);
|
|
6966
7196
|
console.log();
|
|
6967
7197
|
if (out.visibility === "public") {
|
|
6968
|
-
console.log(
|
|
7198
|
+
console.log(import_chalk32.default.dim(` Anyone can now run: ${import_chalk32.default.cyan(`npx apiblaze install ${out.name}`)}`));
|
|
6969
7199
|
} else {
|
|
6970
|
-
console.log(
|
|
7200
|
+
console.log(import_chalk32.default.dim(" Private \u2014 only members of this team can see or install it."));
|
|
6971
7201
|
}
|
|
6972
|
-
console.log(
|
|
7202
|
+
console.log(import_chalk32.default.dim(` Pull it back with: ${import_chalk32.default.cyan(`npx apiblaze withdraw ${out.name}@${out.revision}`)}`));
|
|
6973
7203
|
console.log();
|
|
6974
7204
|
}
|
|
6975
7205
|
async function prepare(caller, projectId, apiVersion, name, tenant2) {
|
|
@@ -6982,23 +7212,23 @@ async function prepare(caller, projectId, apiVersion, name, tenant2) {
|
|
|
6982
7212
|
}
|
|
6983
7213
|
function printFinding(f) {
|
|
6984
7214
|
const preview = (f.preview ?? "").replace(/\s+/g, " ").trim();
|
|
6985
|
-
console.log(` ${
|
|
6986
|
-
console.log(` ${
|
|
7215
|
+
console.log(` ${import_chalk32.default.bold(f.path)}`);
|
|
7216
|
+
console.log(` ${import_chalk32.default.yellow(preview.padEnd(38))}${import_chalk32.default.dim(f.why)}`);
|
|
6987
7217
|
}
|
|
6988
7218
|
function printEnvelopeViolations(violations) {
|
|
6989
7219
|
console.log();
|
|
6990
|
-
console.error(
|
|
7220
|
+
console.error(import_chalk32.default.red("This proxy has transform rules a recipe cannot carry."));
|
|
6991
7221
|
console.log();
|
|
6992
7222
|
for (const v of violations) {
|
|
6993
|
-
console.log(` ${
|
|
7223
|
+
console.log(` ${import_chalk32.default.bold(v.rule)} ${import_chalk32.default.dim(`(${v.kind}s ${v.field})`)}`);
|
|
6994
7224
|
console.log(` ${v.why}`);
|
|
6995
7225
|
}
|
|
6996
7226
|
console.log();
|
|
6997
7227
|
console.log("A recipe runs in a stranger's account, after apiblaze has attached their end user's");
|
|
6998
7228
|
console.log("real third-party token, so two rules are absolute:");
|
|
6999
|
-
console.log(` ${
|
|
7229
|
+
console.log(` ${import_chalk32.default.dim("\u2022")} a rule may never READ Authorization, Cookie, or the third-party token headers \u2014`);
|
|
7000
7230
|
console.log(" those hold a live credential that works at the provider without apiblaze;");
|
|
7001
|
-
console.log(` ${
|
|
7231
|
+
console.log(` ${import_chalk32.default.dim("\u2022")} a rule may never WRITE an identity header (x-end-user-id and friends) \u2014`);
|
|
7002
7232
|
console.log(" those tell the installer's own backend who is calling.");
|
|
7003
7233
|
console.log();
|
|
7004
7234
|
console.log("Writing Authorization from a question or a fixed value is fine \u2014 that is the");
|
|
@@ -7016,7 +7246,7 @@ async function askPromotion(path9) {
|
|
|
7016
7246
|
{ type: "input", name: "id", message: " id:", default: suggestedId }
|
|
7017
7247
|
]);
|
|
7018
7248
|
const id = String(answers.id || suggestedId).toLowerCase().replace(/[^a-z0-9_]/g, "_");
|
|
7019
|
-
console.log(
|
|
7249
|
+
console.log(import_chalk32.default.dim(` \u2192 replaced with {{${id}}}`));
|
|
7020
7250
|
return {
|
|
7021
7251
|
path: path9,
|
|
7022
7252
|
id,
|
|
@@ -7035,7 +7265,7 @@ async function confirm(message, interactive) {
|
|
|
7035
7265
|
async function runRecipeWithdraw(nameArg, opts) {
|
|
7036
7266
|
const ref = parseRecipeRef(nameArg);
|
|
7037
7267
|
if (ref.revision === void 0) {
|
|
7038
|
-
console.error(
|
|
7268
|
+
console.error(import_chalk32.default.red(`Name the revision to withdraw, e.g. ${nameArg}@3. Revisions are withdrawn one at a time.`));
|
|
7039
7269
|
process.exit(1);
|
|
7040
7270
|
}
|
|
7041
7271
|
const interactive = !!process.stdin.isTTY && !opts.json;
|
|
@@ -7055,11 +7285,11 @@ async function runRecipeWithdraw(nameArg, opts) {
|
|
|
7055
7285
|
const { default: inquirer3 } = await import("inquirer");
|
|
7056
7286
|
const { typed } = await inquirer3.prompt([{ type: "input", name: "typed", message: `type the recipe name to confirm (${ref.name}):` }]);
|
|
7057
7287
|
if (String(typed ?? "").trim() !== ref.name) {
|
|
7058
|
-
console.log(
|
|
7288
|
+
console.log(import_chalk32.default.yellow("Cancelled \u2014 nothing was withdrawn."));
|
|
7059
7289
|
return;
|
|
7060
7290
|
}
|
|
7061
7291
|
} else {
|
|
7062
|
-
console.error(
|
|
7292
|
+
console.error(import_chalk32.default.red("Withdrawing needs a typed confirmation and there is no terminal to ask on."));
|
|
7063
7293
|
process.exit(1);
|
|
7064
7294
|
}
|
|
7065
7295
|
const spinner = !opts.json ? (0, import_ora11.default)("Withdrawing...").start() : null;
|
|
@@ -7088,33 +7318,33 @@ async function runRecipeWithdraw(nameArg, opts) {
|
|
|
7088
7318
|
}
|
|
7089
7319
|
|
|
7090
7320
|
// src/commands/logs.ts
|
|
7091
|
-
var
|
|
7321
|
+
var import_chalk34 = __toESM(require("chalk"));
|
|
7092
7322
|
init_admin();
|
|
7093
7323
|
init_auth();
|
|
7094
7324
|
init_resolve();
|
|
7095
7325
|
|
|
7096
7326
|
// src/lib/log-view.ts
|
|
7097
|
-
var
|
|
7327
|
+
var import_chalk33 = __toESM(require("chalk"));
|
|
7098
7328
|
var readline2 = __toESM(require("readline"));
|
|
7099
7329
|
function statusColor(status) {
|
|
7100
|
-
if (status < 300) return
|
|
7101
|
-
if (status < 400) return
|
|
7102
|
-
if (status < 500) return
|
|
7103
|
-
return
|
|
7330
|
+
if (status < 300) return import_chalk33.default.green;
|
|
7331
|
+
if (status < 400) return import_chalk33.default.blue;
|
|
7332
|
+
if (status < 500) return import_chalk33.default.yellow;
|
|
7333
|
+
return import_chalk33.default.red;
|
|
7104
7334
|
}
|
|
7105
7335
|
function methodColor(method) {
|
|
7106
7336
|
switch ((method || "").toUpperCase()) {
|
|
7107
7337
|
case "GET":
|
|
7108
|
-
return
|
|
7338
|
+
return import_chalk33.default.green;
|
|
7109
7339
|
case "POST":
|
|
7110
|
-
return
|
|
7340
|
+
return import_chalk33.default.blue;
|
|
7111
7341
|
case "PUT":
|
|
7112
7342
|
case "PATCH":
|
|
7113
|
-
return
|
|
7343
|
+
return import_chalk33.default.yellow;
|
|
7114
7344
|
case "DELETE":
|
|
7115
|
-
return
|
|
7345
|
+
return import_chalk33.default.red;
|
|
7116
7346
|
default:
|
|
7117
|
-
return
|
|
7347
|
+
return import_chalk33.default.white;
|
|
7118
7348
|
}
|
|
7119
7349
|
}
|
|
7120
7350
|
function clock(ts) {
|
|
@@ -7126,12 +7356,12 @@ function routeCell(route) {
|
|
|
7126
7356
|
return (r.length > ROUTE_W ? `\u2026${r.slice(-(ROUTE_W - 1))}` : r).padEnd(ROUTE_W);
|
|
7127
7357
|
}
|
|
7128
7358
|
function formatRow(r) {
|
|
7129
|
-
const time =
|
|
7359
|
+
const time = import_chalk33.default.dim(clock(r.ts));
|
|
7130
7360
|
const method = methodColor(r.method)(String(r.method || "\u2014").padEnd(6));
|
|
7131
7361
|
const route = routeCell(String(r.route || "\u2014"));
|
|
7132
7362
|
const status = statusColor(r.status)(String(r.status ?? "\u2014").padStart(3));
|
|
7133
|
-
const ms =
|
|
7134
|
-
const step = r.error_step ?
|
|
7363
|
+
const ms = import_chalk33.default.dim(`${r.duration_ms}ms`.padStart(8));
|
|
7364
|
+
const step = r.error_step ? import_chalk33.default.dim(` ${r.error_step}`) : "";
|
|
7135
7365
|
return `${time} ${method} ${route} ${status} ${ms}${step}`;
|
|
7136
7366
|
}
|
|
7137
7367
|
function printRow(r, json) {
|
|
@@ -7146,15 +7376,15 @@ var WindowNotices = class {
|
|
|
7146
7376
|
note(res) {
|
|
7147
7377
|
if (res.history_unavailable && !this.saidUnavailable) {
|
|
7148
7378
|
this.saidUnavailable = true;
|
|
7149
|
-
console.error(
|
|
7379
|
+
console.error(import_chalk33.default.yellow("\u26A0 log history is unreachable right now \u2014 showing the live buffer only, older requests exist but cannot be read."));
|
|
7150
7380
|
}
|
|
7151
7381
|
if ((res.history_markers?.length ?? 0) > 0 && !this.saidGap) {
|
|
7152
7382
|
this.saidGap = true;
|
|
7153
|
-
console.error(
|
|
7383
|
+
console.error(import_chalk33.default.yellow("\u26A0 this range includes a volume-ceiling gap \u2014 some rows were dropped at intake and are gone; counts are incomplete."));
|
|
7154
7384
|
}
|
|
7155
7385
|
if ((res.dropped_this_hour ?? 0) > 0 && !this.saidDropped) {
|
|
7156
7386
|
this.saidDropped = true;
|
|
7157
|
-
console.error(
|
|
7387
|
+
console.error(import_chalk33.default.yellow(`\u26A0 volume ceiling \u2014 ${res.dropped_this_hour} rows dropped this hour; errors keep logging.`));
|
|
7158
7388
|
}
|
|
7159
7389
|
}
|
|
7160
7390
|
};
|
|
@@ -7213,13 +7443,13 @@ async function runTailSession(opts) {
|
|
|
7213
7443
|
if (!held.length && !heldRowsDropped && !heldNotesDropped) return;
|
|
7214
7444
|
const rows = held.filter((h) => h.kind === "row").length;
|
|
7215
7445
|
const total = rows + heldRowsDropped;
|
|
7216
|
-
if (total) console.log(
|
|
7446
|
+
if (total) console.log(import_chalk33.default.dim(`\u2500\u2500\u2500 ${total} request${total === 1 ? "" : "s"} arrived while paused \u2500\u2500\u2500`));
|
|
7217
7447
|
for (const h of held) {
|
|
7218
7448
|
if (h.kind === "row") printRow(h.row, opts.json);
|
|
7219
7449
|
else console.error(h.msg);
|
|
7220
7450
|
}
|
|
7221
7451
|
if (heldRowsDropped || heldNotesDropped) {
|
|
7222
|
-
console.log(
|
|
7452
|
+
console.log(import_chalk33.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`));
|
|
7223
7453
|
}
|
|
7224
7454
|
held.length = 0;
|
|
7225
7455
|
heldRowsDropped = 0;
|
|
@@ -7233,7 +7463,7 @@ async function runTailSession(opts) {
|
|
|
7233
7463
|
activeSocket?.close();
|
|
7234
7464
|
} catch {
|
|
7235
7465
|
}
|
|
7236
|
-
if (!opts.json) console.log(
|
|
7466
|
+
if (!opts.json) console.log(import_chalk33.default.dim("\nStopped."));
|
|
7237
7467
|
process.exit(0);
|
|
7238
7468
|
}
|
|
7239
7469
|
function onKeypress(_s, key) {
|
|
@@ -7261,17 +7491,17 @@ async function runTailSession(opts) {
|
|
|
7261
7491
|
async function openInspector() {
|
|
7262
7492
|
if (paused) return;
|
|
7263
7493
|
if (recent.length === 0) {
|
|
7264
|
-
console.log(
|
|
7494
|
+
console.log(import_chalk33.default.dim(" nothing captured yet \u2014 waiting for a request\u2026"));
|
|
7265
7495
|
return;
|
|
7266
7496
|
}
|
|
7267
7497
|
paused = true;
|
|
7268
7498
|
unbindKeys();
|
|
7269
7499
|
try {
|
|
7270
7500
|
const { default: inquirer3 } = await import("inquirer");
|
|
7271
|
-
console.log(
|
|
7501
|
+
console.log(import_chalk33.default.dim("\n\u2500\u2500\u2500 paused \xB7 still capturing in the background \u2500\u2500\u2500"));
|
|
7272
7502
|
for (; ; ) {
|
|
7273
7503
|
const choices = recent.slice(-TAIL_INSPECT_CHOICES).reverse().map((r) => ({ name: formatRow(r), value: r.request_id }));
|
|
7274
|
-
choices.push({ name:
|
|
7504
|
+
choices.push({ name: import_chalk33.default.dim("\u2039resume tail\u203A"), value: "__resume__" });
|
|
7275
7505
|
const { picked } = await inquirer3.prompt([{
|
|
7276
7506
|
type: "list",
|
|
7277
7507
|
name: "picked",
|
|
@@ -7282,12 +7512,12 @@ async function runTailSession(opts) {
|
|
|
7282
7512
|
if (picked === "__resume__") break;
|
|
7283
7513
|
const row = recent.find((r) => r.request_id === picked);
|
|
7284
7514
|
if (row) printDetail(row, opts.detail);
|
|
7285
|
-
else console.log(
|
|
7515
|
+
else console.log(import_chalk33.default.dim(" that request has scrolled out of the tail buffer \u2014 find it with `logs list`.\n"));
|
|
7286
7516
|
}
|
|
7287
7517
|
} finally {
|
|
7288
7518
|
paused = false;
|
|
7289
7519
|
flushHeld();
|
|
7290
|
-
console.log(
|
|
7520
|
+
console.log(import_chalk33.default.dim("\u2500\u2500\u2500 resumed \u2500\u2500\u2500"));
|
|
7291
7521
|
bindKeys();
|
|
7292
7522
|
}
|
|
7293
7523
|
}
|
|
@@ -7297,12 +7527,12 @@ async function runTailSession(opts) {
|
|
|
7297
7527
|
if (reconnects >= MAX_RECONNECTS) {
|
|
7298
7528
|
paused = false;
|
|
7299
7529
|
flushHeld();
|
|
7300
|
-
console.error(
|
|
7530
|
+
console.error(import_chalk33.default.red(`Connection lost \u2014 giving up after repeated reconnects${lastFailure ? ` (last error: ${lastFailure})` : ""}.`));
|
|
7301
7531
|
process.exit(1);
|
|
7302
7532
|
}
|
|
7303
7533
|
const delay = Math.min(1e3 * 2 ** reconnects, 15e3);
|
|
7304
7534
|
reconnects++;
|
|
7305
|
-
diag(
|
|
7535
|
+
diag(import_chalk33.default.dim(`disconnected${lastFailure ? ` (${lastFailure})` : ""} \u2014 reconnecting in ${Math.round(delay / 1e3)}s\u2026`));
|
|
7306
7536
|
setTimeout(() => {
|
|
7307
7537
|
if (!closed) void connect();
|
|
7308
7538
|
}, delay);
|
|
@@ -7313,7 +7543,7 @@ async function runTailSession(opts) {
|
|
|
7313
7543
|
url = await opts.mintTicket();
|
|
7314
7544
|
} catch (err) {
|
|
7315
7545
|
if (!everConnected) {
|
|
7316
|
-
console.error(
|
|
7546
|
+
console.error(import_chalk33.default.red(`Could not start tail: ${err.message}`));
|
|
7317
7547
|
process.exit(1);
|
|
7318
7548
|
}
|
|
7319
7549
|
lastFailure = `ticket mint failed: ${err.message}`;
|
|
@@ -7326,7 +7556,7 @@ async function runTailSession(opts) {
|
|
|
7326
7556
|
} catch (err) {
|
|
7327
7557
|
lastFailure = `bad tail URL: ${err.message}`;
|
|
7328
7558
|
if (!everConnected) {
|
|
7329
|
-
console.error(
|
|
7559
|
+
console.error(import_chalk33.default.red(`Could not start tail: ${lastFailure}`));
|
|
7330
7560
|
process.exit(1);
|
|
7331
7561
|
}
|
|
7332
7562
|
scheduleReconnect();
|
|
@@ -7341,9 +7571,9 @@ async function runTailSession(opts) {
|
|
|
7341
7571
|
lastInbound = Date.now();
|
|
7342
7572
|
lastFailure = "";
|
|
7343
7573
|
if (isReconnect) {
|
|
7344
|
-
diag(
|
|
7574
|
+
diag(import_chalk33.default.dim("reconnected \u2014 requests during the gap were not replayed (browse them with `logs list`)."));
|
|
7345
7575
|
} else {
|
|
7346
|
-
diag(
|
|
7576
|
+
diag(import_chalk33.default.dim(interactive ? "[Enter] inspect a request \xB7 [Ctrl+C] stop\n" : "Ctrl+C to stop."));
|
|
7347
7577
|
}
|
|
7348
7578
|
bindKeys();
|
|
7349
7579
|
pingTimer = setInterval(() => {
|
|
@@ -7370,7 +7600,7 @@ async function runTailSession(opts) {
|
|
|
7370
7600
|
return;
|
|
7371
7601
|
}
|
|
7372
7602
|
if (frame?.type === "ceiling") {
|
|
7373
|
-
diag(
|
|
7603
|
+
diag(import_chalk33.default.yellow(
|
|
7374
7604
|
`\u26A0 volume ceiling \u2014 ${Number(frame.dropped_this_hour) || 0} rows dropped this hour; errors keep streaming.`
|
|
7375
7605
|
));
|
|
7376
7606
|
return;
|
|
@@ -7450,10 +7680,10 @@ async function fetchPage(fetchWindow3, params, notices) {
|
|
|
7450
7680
|
return { rows, nextCursor: res.next_cursor ?? void 0 };
|
|
7451
7681
|
}
|
|
7452
7682
|
if (hop >= MAX_EMPTY_HOPS) {
|
|
7453
|
-
console.error(
|
|
7683
|
+
console.error(import_chalk33.default.yellow("\u26A0 stopped after scanning a long empty span \u2014 older rows may still exist."));
|
|
7454
7684
|
return { rows: [], nextCursor: res.next_cursor, fuseTripped: true };
|
|
7455
7685
|
}
|
|
7456
|
-
if (hop === 0) console.error(
|
|
7686
|
+
if (hop === 0) console.error(import_chalk33.default.dim("searching older history\u2026"));
|
|
7457
7687
|
cursor = res.next_cursor;
|
|
7458
7688
|
}
|
|
7459
7689
|
}
|
|
@@ -7493,18 +7723,18 @@ async function runPlainList(opts, notices) {
|
|
|
7493
7723
|
}
|
|
7494
7724
|
}
|
|
7495
7725
|
if (rows.length === 0) {
|
|
7496
|
-
console.log(
|
|
7726
|
+
console.log(import_chalk33.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."));
|
|
7497
7727
|
return;
|
|
7498
7728
|
}
|
|
7499
7729
|
const now = Date.now();
|
|
7500
7730
|
for (const d of groupIntoDays(rows)) {
|
|
7501
|
-
console.log(
|
|
7731
|
+
console.log(import_chalk33.default.bold(dayLabel(d.dayMs, now)));
|
|
7502
7732
|
for (const r of d.rows) console.log(` ${formatRow(r)}`);
|
|
7503
7733
|
console.log("");
|
|
7504
7734
|
}
|
|
7505
7735
|
const errs = rows.filter((r) => r.status >= 400).length;
|
|
7506
7736
|
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" : "";
|
|
7507
|
-
console.log(
|
|
7737
|
+
console.log(import_chalk33.default.dim(
|
|
7508
7738
|
`${rows.length} request${rows.length === 1 ? "" : "s"} \xB7 ${errs} error${errs === 1 ? "" : "s"}${tail}`
|
|
7509
7739
|
));
|
|
7510
7740
|
}
|
|
@@ -7523,7 +7753,7 @@ async function runHistoryBrowser(opts) {
|
|
|
7523
7753
|
for (const r of page.rows) console.log(JSON.stringify(r));
|
|
7524
7754
|
any = any || page.rows.length > 0;
|
|
7525
7755
|
if (page.fuseTripped || !page.nextCursor) {
|
|
7526
|
-
if (!any && !page.fuseTripped) console.error(
|
|
7756
|
+
if (!any && !page.fuseTripped) console.error(import_chalk33.default.dim(opts.errorsOnly ? "No errors logged yet." : "No requests logged yet."));
|
|
7527
7757
|
return;
|
|
7528
7758
|
}
|
|
7529
7759
|
cursor2 = page.nextCursor;
|
|
@@ -7544,7 +7774,7 @@ async function runHistoryBrowser(opts) {
|
|
|
7544
7774
|
scanRows = first.rows;
|
|
7545
7775
|
scanCursor = first.nextCursor;
|
|
7546
7776
|
if (scanRows.length === 0) {
|
|
7547
|
-
console.log(
|
|
7777
|
+
console.log(import_chalk33.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."));
|
|
7548
7778
|
return;
|
|
7549
7779
|
}
|
|
7550
7780
|
}
|
|
@@ -7555,10 +7785,10 @@ async function runHistoryBrowser(opts) {
|
|
|
7555
7785
|
const now2 = Date.now();
|
|
7556
7786
|
const oldestKey = days[days.length - 1]?.key;
|
|
7557
7787
|
const choices = days.map((d) => ({
|
|
7558
|
-
name: `${dayLabel(d.dayMs, now2).padEnd(12)} ${String(d.rows.length).padStart(4)}${(scanCursor !== void 0 || scanFloorUnknown) && d.key === oldestKey ? "+" : ""} requests${d.errors ?
|
|
7788
|
+
name: `${dayLabel(d.dayMs, now2).padEnd(12)} ${String(d.rows.length).padStart(4)}${(scanCursor !== void 0 || scanFloorUnknown) && d.key === oldestKey ? "+" : ""} requests${d.errors ? import_chalk33.default.red(` ${d.errors} errors`) : ""}`,
|
|
7559
7789
|
value: d.key
|
|
7560
7790
|
}));
|
|
7561
|
-
if (scanCursor) choices.push({ name:
|
|
7791
|
+
if (scanCursor) choices.push({ name: import_chalk33.default.dim("\u2026 older days"), value: "__older__" });
|
|
7562
7792
|
const { chosenDay } = await inquirer3.prompt([{
|
|
7563
7793
|
type: "list",
|
|
7564
7794
|
name: "chosenDay",
|
|
@@ -7578,7 +7808,7 @@ async function runHistoryBrowser(opts) {
|
|
|
7578
7808
|
scanCursor = void 0;
|
|
7579
7809
|
} else {
|
|
7580
7810
|
scanCursor = more.nextCursor;
|
|
7581
|
-
if (more.rows.length === 0 && !scanCursor) console.log(
|
|
7811
|
+
if (more.rows.length === 0 && !scanCursor) console.log(import_chalk33.default.dim("That was everything \u2014 no older requests."));
|
|
7582
7812
|
}
|
|
7583
7813
|
continue;
|
|
7584
7814
|
}
|
|
@@ -7600,11 +7830,11 @@ async function runHistoryBrowser(opts) {
|
|
|
7600
7830
|
cursor
|
|
7601
7831
|
}, notices);
|
|
7602
7832
|
if (page.rows.length === 0 && pageRows.length === 0) {
|
|
7603
|
-
console.log(
|
|
7833
|
+
console.log(import_chalk33.default.dim(page.fuseTripped ? "Stopped scanning this day (long empty span) \u2014 older rows in it may exist." : "No requests for this day."));
|
|
7604
7834
|
return;
|
|
7605
7835
|
}
|
|
7606
7836
|
if (page.rows.length === 0) {
|
|
7607
|
-
console.log(
|
|
7837
|
+
console.log(import_chalk33.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."));
|
|
7608
7838
|
moreCursor = void 0;
|
|
7609
7839
|
} else {
|
|
7610
7840
|
pageRows = page.rows;
|
|
@@ -7615,8 +7845,8 @@ async function runHistoryBrowser(opts) {
|
|
|
7615
7845
|
name: formatRow(r),
|
|
7616
7846
|
value: r.request_id
|
|
7617
7847
|
}));
|
|
7618
|
-
if (moreCursor) choices.push({ name:
|
|
7619
|
-
choices.push({ name:
|
|
7848
|
+
if (moreCursor) choices.push({ name: import_chalk33.default.dim("\u2026 load more"), value: "__more__" });
|
|
7849
|
+
choices.push({ name: import_chalk33.default.dim("quit"), value: "__quit__" });
|
|
7620
7850
|
for (; ; ) {
|
|
7621
7851
|
const { picked } = await inquirer3.prompt([{
|
|
7622
7852
|
type: "list",
|
|
@@ -7666,19 +7896,19 @@ function wrapCells(items, width) {
|
|
|
7666
7896
|
${PANE_INDENT}`);
|
|
7667
7897
|
}
|
|
7668
7898
|
function renderHeaders(h) {
|
|
7669
|
-
if (!h) return
|
|
7899
|
+
if (!h) return import_chalk33.default.dim("\u2014");
|
|
7670
7900
|
const width = process.stdout.columns || 100;
|
|
7671
7901
|
if (h.values) {
|
|
7672
7902
|
const e = Object.entries(h.values);
|
|
7673
|
-
if (!e.length) return
|
|
7903
|
+
if (!e.length) return import_chalk33.default.dim("none");
|
|
7674
7904
|
const rank = (k) => {
|
|
7675
7905
|
const i = HEADER_PRIORITY.indexOf(k.toLowerCase());
|
|
7676
7906
|
return i === -1 ? NOISE_HEADER.test(k) ? 999 : 500 : i;
|
|
7677
7907
|
};
|
|
7678
|
-
return e.sort((a, b) => rank(a[0]) - rank(b[0])).map(([k, v]) => `${
|
|
7908
|
+
return e.sort((a, b) => rank(a[0]) - rank(b[0])).map(([k, v]) => `${import_chalk33.default.dim(k + ":")} ${v}`).join(`
|
|
7679
7909
|
${PANE_INDENT}`);
|
|
7680
7910
|
}
|
|
7681
|
-
if (!h.names?.length) return
|
|
7911
|
+
if (!h.names?.length) return import_chalk33.default.dim("none");
|
|
7682
7912
|
const names = [...h.names];
|
|
7683
7913
|
const noise = names.filter((n) => NOISE_HEADER.test(n));
|
|
7684
7914
|
const shown = names.filter((n) => !NOISE_HEADER.test(n)).sort((a, b) => {
|
|
@@ -7686,70 +7916,70 @@ ${PANE_INDENT}`);
|
|
|
7686
7916
|
const ib = HEADER_PRIORITY.indexOf(b.toLowerCase());
|
|
7687
7917
|
return (ia === -1 ? 500 : ia) - (ib === -1 ? 500 : ib) || a.localeCompare(b);
|
|
7688
7918
|
});
|
|
7689
|
-
const body = shown.length ? wrapCells(shown, width) :
|
|
7919
|
+
const body = shown.length ? wrapCells(shown, width) : import_chalk33.default.dim("(all forwarding headers)");
|
|
7690
7920
|
const tail = noise.length ? `
|
|
7691
|
-
${PANE_INDENT}${
|
|
7692
|
-
return `${body}${tail} ${
|
|
7921
|
+
${PANE_INDENT}${import_chalk33.default.dim(`+ ${noise.length} forwarding/client-hint header${noise.length === 1 ? "" : "s"} (cf-*, sec-*)`)}` : "";
|
|
7922
|
+
return `${body}${tail} ${import_chalk33.default.dim("(names only)")}`;
|
|
7693
7923
|
}
|
|
7694
7924
|
function renderBody(b) {
|
|
7695
|
-
if (!b) return
|
|
7925
|
+
if (!b) return import_chalk33.default.dim("\u2014");
|
|
7696
7926
|
const meta = [b.content_type, b.size != null ? `${b.size}B` : null, b.truncated ? "truncated" : null].filter(Boolean).join(" \xB7 ");
|
|
7697
|
-
if (b.not_captured) return
|
|
7927
|
+
if (b.not_captured) return import_chalk33.default.dim(`not captured (${b.not_captured})${meta ? " \xB7 " + meta : ""}`);
|
|
7698
7928
|
const isShape = b.value === void 0 && b.shape !== void 0;
|
|
7699
7929
|
const content = b.value !== void 0 ? b.value : b.shape;
|
|
7700
7930
|
if (content === null || content === void 0 || content === "" || content === "empty") {
|
|
7701
|
-
return
|
|
7931
|
+
return import_chalk33.default.dim(`empty${meta ? " \xB7 " + meta : ""}`);
|
|
7702
7932
|
}
|
|
7703
7933
|
const text = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
7704
|
-
const head = isShape ?
|
|
7934
|
+
const head = isShape ? import_chalk33.default.dim("shape only (values not captured)\n ") : "";
|
|
7705
7935
|
return head + text.split("\n").join("\n ");
|
|
7706
7936
|
}
|
|
7707
7937
|
function pane(title, tone, view, empty) {
|
|
7708
7938
|
console.log(` ${tone(title)}`);
|
|
7709
7939
|
if (empty !== void 0) {
|
|
7710
|
-
console.log(` ${
|
|
7940
|
+
console.log(` ${import_chalk33.default.dim(empty)}`);
|
|
7711
7941
|
return;
|
|
7712
7942
|
}
|
|
7713
7943
|
const v = view ?? {};
|
|
7714
|
-
if (v.url) console.log(` ${
|
|
7944
|
+
if (v.url) console.log(` ${import_chalk33.default.dim("target")} ${v.url}`);
|
|
7715
7945
|
if (v.status != null) {
|
|
7716
|
-
console.log(` ${
|
|
7946
|
+
console.log(` ${import_chalk33.default.dim("status")} ${statusColor(v.status)(String(v.status))}${v.status_text ? import_chalk33.default.dim(" " + v.status_text) : ""}`);
|
|
7717
7947
|
}
|
|
7718
|
-
if (v.raw_path) console.log(` ${
|
|
7948
|
+
if (v.raw_path) console.log(` ${import_chalk33.default.dim("path")} ${v.raw_path}`);
|
|
7719
7949
|
if (v.query && Object.keys(v.query).length) {
|
|
7720
|
-
console.log(` ${
|
|
7950
|
+
console.log(` ${import_chalk33.default.dim("query")} ${Object.entries(v.query).map(([k, q]) => `${k}=${q}`).join(" ")}`);
|
|
7721
7951
|
}
|
|
7722
|
-
if (v.jwt !== void 0) console.log(` ${
|
|
7723
|
-
console.log(` ${
|
|
7724
|
-
console.log(` ${
|
|
7952
|
+
if (v.jwt !== void 0) console.log(` ${import_chalk33.default.dim("jwt")} ${JSON.stringify(v.jwt)}`);
|
|
7953
|
+
console.log(` ${import_chalk33.default.dim("headers")} ${renderHeaders(v.headers)}`);
|
|
7954
|
+
console.log(` ${import_chalk33.default.dim("body")} ${renderBody(v.body)}`);
|
|
7725
7955
|
}
|
|
7726
7956
|
function printDetail(r, mode = "producer") {
|
|
7727
7957
|
const v = r.views;
|
|
7728
7958
|
console.log("");
|
|
7729
|
-
console.log(
|
|
7959
|
+
console.log(import_chalk33.default.bold(`${r.method} ${r.route}`) + " " + statusColor(r.status)(String(r.status)) + import_chalk33.default.dim(` ${r.duration_ms}ms`));
|
|
7730
7960
|
const cost = typeof r.cost_cents === "number" && r.cost_cents > 0 ? ` \xB7 ${r.cost_cents}\xA2` : "";
|
|
7731
|
-
console.log(
|
|
7961
|
+
console.log(import_chalk33.default.dim(` request_id ${r.request_id}${r.environment ? ` \xB7 env ${r.environment}` : ""}${r.consumer_id ? ` \xB7 consumer ${r.consumer_id}` : ""}${cost}`));
|
|
7732
7962
|
console.log("");
|
|
7733
7963
|
if (!v) {
|
|
7734
|
-
console.log(
|
|
7964
|
+
console.log(import_chalk33.default.dim(" No view detail captured for this request.\n"));
|
|
7735
7965
|
return;
|
|
7736
7966
|
}
|
|
7737
|
-
pane("Request (before) \u2014 client \u2192 APIblaze",
|
|
7967
|
+
pane("Request (before) \u2014 client \u2192 APIblaze", import_chalk33.default.cyan, v.request);
|
|
7738
7968
|
if (mode === "producer") {
|
|
7739
7969
|
pane(
|
|
7740
7970
|
"Request (after) \u2014 APIblaze \u2192 upstream",
|
|
7741
|
-
|
|
7971
|
+
import_chalk33.default.cyan,
|
|
7742
7972
|
v.forwarded_request,
|
|
7743
7973
|
v.forwarded_request ? void 0 : "Request did not reach an upstream."
|
|
7744
7974
|
);
|
|
7745
7975
|
pane(
|
|
7746
7976
|
"Response (before) \u2014 upstream \u2192 APIblaze",
|
|
7747
|
-
|
|
7977
|
+
import_chalk33.default.magenta,
|
|
7748
7978
|
v.upstream_response,
|
|
7749
7979
|
v.upstream_response ? void 0 : "No upstream response."
|
|
7750
7980
|
);
|
|
7751
7981
|
}
|
|
7752
|
-
pane("Response (after) \u2014 APIblaze \u2192 client",
|
|
7982
|
+
pane("Response (after) \u2014 APIblaze \u2192 client", import_chalk33.default.magenta, v.response);
|
|
7753
7983
|
console.log("");
|
|
7754
7984
|
}
|
|
7755
7985
|
|
|
@@ -7798,7 +8028,7 @@ async function resolve(projectArg, versionArg, opts) {
|
|
|
7798
8028
|
return { projectId, projectName: match.projectName, version: version2, tenant: tenant2 };
|
|
7799
8029
|
}
|
|
7800
8030
|
function otherPlaneHint() {
|
|
7801
|
-
if (loadConsumer()) console.log(
|
|
8031
|
+
if (loadConsumer()) console.log(import_chalk34.default.dim(" \u2039consumer view: apiblaze consumer logs\u203A"));
|
|
7802
8032
|
}
|
|
7803
8033
|
function fetchWindow(projectId, version2, tenant2) {
|
|
7804
8034
|
return (params) => {
|
|
@@ -7817,8 +8047,8 @@ function fetchWindow(projectId, version2, tenant2) {
|
|
|
7817
8047
|
async function runLogsTail(projectArg, versionArg, opts) {
|
|
7818
8048
|
const { projectId, projectName, version: version2, tenant: tenant2 } = await resolve(projectArg, versionArg, opts);
|
|
7819
8049
|
if (!opts.json) {
|
|
7820
|
-
console.log(
|
|
7821
|
-
console.log(
|
|
8050
|
+
console.log(import_chalk34.default.bold("API Producer logs") + import_chalk34.default.dim(` \xB7 ${projectName}@${version2} \xB7 tenant ${tenant2}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
8051
|
+
console.log(import_chalk34.default.dim("every caller's requests, with cost and upstream detail"));
|
|
7822
8052
|
otherPlaneHint();
|
|
7823
8053
|
}
|
|
7824
8054
|
await runTailSession({
|
|
@@ -7839,8 +8069,8 @@ async function runLogsTail(projectArg, versionArg, opts) {
|
|
|
7839
8069
|
async function runLogsList(projectArg, versionArg, opts) {
|
|
7840
8070
|
const { projectId, projectName, version: version2, tenant: tenant2 } = await resolve(projectArg, versionArg, opts);
|
|
7841
8071
|
if (!opts.json) {
|
|
7842
|
-
console.log(
|
|
7843
|
-
console.log(
|
|
8072
|
+
console.log(import_chalk34.default.bold("API Producer logs") + import_chalk34.default.dim(` \xB7 ${projectName}@${version2} \xB7 tenant ${tenant2}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
8073
|
+
console.log(import_chalk34.default.dim("every caller's requests, with cost and upstream detail"));
|
|
7844
8074
|
otherPlaneHint();
|
|
7845
8075
|
console.log("");
|
|
7846
8076
|
}
|
|
@@ -7854,15 +8084,15 @@ async function runLogsList(projectArg, versionArg, opts) {
|
|
|
7854
8084
|
}
|
|
7855
8085
|
|
|
7856
8086
|
// src/commands/consumer-logs.ts
|
|
7857
|
-
var
|
|
8087
|
+
var import_chalk35 = __toESM(require("chalk"));
|
|
7858
8088
|
init_auth();
|
|
7859
8089
|
var CONSUMER_LOGS_BASE = process.env.APIBLAZE_CONSUMER_LOGS_BASE || "https://logs.apiblaze.com/consumer";
|
|
7860
8090
|
function requireConsumer() {
|
|
7861
8091
|
const c = loadConsumer();
|
|
7862
8092
|
if (!c) {
|
|
7863
|
-
console.error(
|
|
8093
|
+
console.error(import_chalk35.default.red("Not logged in as an API Consumer. Run `apiblaze consumer login` first."));
|
|
7864
8094
|
if (loadCredentials()) {
|
|
7865
|
-
console.error(
|
|
8095
|
+
console.error(import_chalk35.default.dim("You're signed in as an API Producer \u2014 for a proxy's full logs run `apiblaze logs <project>`."));
|
|
7866
8096
|
}
|
|
7867
8097
|
process.exit(1);
|
|
7868
8098
|
}
|
|
@@ -7893,7 +8123,7 @@ async function consumerLogsFetch(session, suffix, init) {
|
|
|
7893
8123
|
return data;
|
|
7894
8124
|
}
|
|
7895
8125
|
function otherPlaneHint2() {
|
|
7896
|
-
if (loadCredentials()) console.log(
|
|
8126
|
+
if (loadCredentials()) console.log(import_chalk35.default.dim(" \u2039producer view: apiblaze logs <project>\u203A"));
|
|
7897
8127
|
}
|
|
7898
8128
|
function fetchWindow2(session, project) {
|
|
7899
8129
|
return (params) => {
|
|
@@ -7909,8 +8139,8 @@ function fetchWindow2(session, project) {
|
|
|
7909
8139
|
async function runConsumerLogsTail(opts) {
|
|
7910
8140
|
const session = requireConsumer();
|
|
7911
8141
|
if (!opts.json) {
|
|
7912
|
-
console.log(
|
|
7913
|
-
console.log(
|
|
8142
|
+
console.log(import_chalk35.default.bold("API Consumer logs") + import_chalk35.default.dim(` \xB7 your own requests on ${session.creds.tenant}${opts.project ? ` \xB7 ${opts.project}` : ""}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
8143
|
+
console.log(import_chalk35.default.dim("cost and upstream details are producer-only and are not sent"));
|
|
7914
8144
|
otherPlaneHint2();
|
|
7915
8145
|
}
|
|
7916
8146
|
await runTailSession({
|
|
@@ -7928,8 +8158,8 @@ async function runConsumerLogsTail(opts) {
|
|
|
7928
8158
|
async function runConsumerLogsList(opts) {
|
|
7929
8159
|
const session = requireConsumer();
|
|
7930
8160
|
if (!opts.json) {
|
|
7931
|
-
console.log(
|
|
7932
|
-
console.log(
|
|
8161
|
+
console.log(import_chalk35.default.bold("API Consumer logs") + import_chalk35.default.dim(` \xB7 your own requests on ${session.creds.tenant}${opts.project ? ` \xB7 ${opts.project}` : ""}${opts.errorsOnly ? " \xB7 errors only" : ""}`));
|
|
8162
|
+
console.log(import_chalk35.default.dim("cost and upstream details are producer-only and are not sent"));
|
|
7933
8163
|
otherPlaneHint2();
|
|
7934
8164
|
console.log("");
|
|
7935
8165
|
}
|
|
@@ -7943,7 +8173,7 @@ async function runConsumerLogsList(opts) {
|
|
|
7943
8173
|
}
|
|
7944
8174
|
|
|
7945
8175
|
// src/commands/config.ts
|
|
7946
|
-
var
|
|
8176
|
+
var import_chalk36 = __toESM(require("chalk"));
|
|
7947
8177
|
var import_ora12 = __toESM(require("ora"));
|
|
7948
8178
|
init_admin();
|
|
7949
8179
|
init_resolve();
|
|
@@ -7967,7 +8197,7 @@ async function patchConfig(project, opts, body, summary) {
|
|
|
7967
8197
|
}
|
|
7968
8198
|
async function runTargetSet(project, opts) {
|
|
7969
8199
|
if (!opts.url) {
|
|
7970
|
-
console.error(
|
|
8200
|
+
console.error(import_chalk36.default.red("--url is required."));
|
|
7971
8201
|
process.exit(1);
|
|
7972
8202
|
}
|
|
7973
8203
|
const body = opts.env ? { environments: { [opts.env]: { target: opts.url } } } : { target_url: opts.url };
|
|
@@ -7980,40 +8210,40 @@ async function runThrottleSet(project, opts) {
|
|
|
7980
8210
|
if (opts.quota !== void 0) throttling.proxyQuota = Number(opts.quota);
|
|
7981
8211
|
if (opts.period !== void 0) {
|
|
7982
8212
|
if (!["daily", "weekly", "monthly"].includes(opts.period)) {
|
|
7983
|
-
console.error(
|
|
8213
|
+
console.error(import_chalk36.default.red("--period must be daily, weekly, or monthly."));
|
|
7984
8214
|
process.exit(1);
|
|
7985
8215
|
}
|
|
7986
8216
|
throttling.quotaPeriod = opts.period;
|
|
7987
8217
|
}
|
|
7988
8218
|
if (Object.keys(throttling).length === 0) {
|
|
7989
|
-
console.error(
|
|
8219
|
+
console.error(import_chalk36.default.red("Nothing to set. Pass at least one of --rate, --end-user-rate, --quota, --period."));
|
|
7990
8220
|
process.exit(1);
|
|
7991
8221
|
}
|
|
7992
8222
|
await patchConfig(project, opts, { throttling }, `Update throttling ${JSON.stringify(throttling)}`);
|
|
7993
8223
|
}
|
|
7994
8224
|
async function runRename(project, opts) {
|
|
7995
8225
|
if (!opts.displayName) {
|
|
7996
|
-
console.error(
|
|
8226
|
+
console.error(import_chalk36.default.red("--display-name is required."));
|
|
7997
8227
|
process.exit(1);
|
|
7998
8228
|
}
|
|
7999
8229
|
await patchConfig(project, opts, { display_name: opts.displayName }, `Rename \u2192 "${opts.displayName}"`);
|
|
8000
8230
|
}
|
|
8001
8231
|
|
|
8002
8232
|
// src/commands/config-browse.ts
|
|
8003
|
-
var
|
|
8233
|
+
var import_chalk45 = __toESM(require("chalk"));
|
|
8004
8234
|
var import_ora20 = __toESM(require("ora"));
|
|
8005
8235
|
init_admin();
|
|
8006
8236
|
init_auth();
|
|
8007
8237
|
init_resolve();
|
|
8008
8238
|
|
|
8009
8239
|
// src/commands/domain.ts
|
|
8010
|
-
var
|
|
8240
|
+
var import_chalk37 = __toESM(require("chalk"));
|
|
8011
8241
|
var import_ora13 = __toESM(require("ora"));
|
|
8012
8242
|
init_admin();
|
|
8013
8243
|
init_resolve();
|
|
8014
8244
|
async function runDomainAdd(project, opts) {
|
|
8015
8245
|
if (!opts.domain) {
|
|
8016
|
-
console.error(
|
|
8246
|
+
console.error(import_chalk37.default.red("--domain is required."));
|
|
8017
8247
|
process.exit(1);
|
|
8018
8248
|
}
|
|
8019
8249
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -8032,9 +8262,9 @@ async function runDomainAdd(project, opts) {
|
|
|
8032
8262
|
console.log(JSON.stringify(out));
|
|
8033
8263
|
return;
|
|
8034
8264
|
}
|
|
8035
|
-
if (out?.cnameRecord) console.log(` ${
|
|
8036
|
-
if (out?.dcv) console.log(` ${
|
|
8037
|
-
if (out?.id) console.log(
|
|
8265
|
+
if (out?.cnameRecord) console.log(` ${import_chalk37.default.cyan("CNAME")} ${out.cnameRecord.name ?? opts.domain} \u2192 ${out.cnameRecord.value ?? out.cnameRecord.target}`);
|
|
8266
|
+
if (out?.dcv) console.log(` ${import_chalk37.default.cyan("TXT")} ${out.dcv.name} = ${out.dcv.value}`);
|
|
8267
|
+
if (out?.id) console.log(import_chalk37.default.dim(` domain id: ${out.id}`));
|
|
8038
8268
|
} catch (err) {
|
|
8039
8269
|
spinner.fail("Domain registration failed.");
|
|
8040
8270
|
throw err;
|
|
@@ -8054,14 +8284,14 @@ async function runDomainList(project, opts) {
|
|
|
8054
8284
|
return;
|
|
8055
8285
|
}
|
|
8056
8286
|
if (!domains.length) {
|
|
8057
|
-
console.log(
|
|
8287
|
+
console.log(import_chalk37.default.yellow("No custom domains."));
|
|
8058
8288
|
return;
|
|
8059
8289
|
}
|
|
8060
|
-
for (const d of domains) console.log(` ${
|
|
8290
|
+
for (const d of domains) console.log(` ${import_chalk37.default.bold(d.hostname)} ${import_chalk37.default.dim(d.status ?? "")} ${import_chalk37.default.dim(d.id ?? "")}`);
|
|
8061
8291
|
}
|
|
8062
8292
|
async function runDomainStatus(project, opts) {
|
|
8063
8293
|
if (!opts.id) {
|
|
8064
|
-
console.error(
|
|
8294
|
+
console.error(import_chalk37.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
8065
8295
|
process.exit(1);
|
|
8066
8296
|
}
|
|
8067
8297
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -8071,11 +8301,11 @@ async function runDomainStatus(project, opts) {
|
|
|
8071
8301
|
path: `/projects/${proj2.projectId}/domains/${encodeURIComponent(opts.id)}/status`,
|
|
8072
8302
|
summary: `Check custom-domain status`
|
|
8073
8303
|
});
|
|
8074
|
-
console.log(opts.json ? JSON.stringify(out) : ` ${
|
|
8304
|
+
console.log(opts.json ? JSON.stringify(out) : ` ${import_chalk37.default.bold(out?.hostname ?? opts.id)}: ${import_chalk37.default.cyan(out?.status ?? "unknown")}`);
|
|
8075
8305
|
}
|
|
8076
8306
|
async function runDomainRemove(project, opts) {
|
|
8077
8307
|
if (!opts.id) {
|
|
8078
|
-
console.error(
|
|
8308
|
+
console.error(import_chalk37.default.red("--id <domainId> is required (see `apiblaze domain list`)."));
|
|
8079
8309
|
process.exit(1);
|
|
8080
8310
|
}
|
|
8081
8311
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -8113,7 +8343,7 @@ async function runDomainSetBase(project, opts) {
|
|
|
8113
8343
|
}
|
|
8114
8344
|
|
|
8115
8345
|
// src/commands/tenant.ts
|
|
8116
|
-
var
|
|
8346
|
+
var import_chalk40 = __toESM(require("chalk"));
|
|
8117
8347
|
var import_ora16 = __toESM(require("ora"));
|
|
8118
8348
|
init_admin();
|
|
8119
8349
|
init_resolve();
|
|
@@ -8122,20 +8352,20 @@ init_tenant_pick();
|
|
|
8122
8352
|
async function runTenantUse(query, opts) {
|
|
8123
8353
|
const creds = loadCredentials();
|
|
8124
8354
|
if (!creds) {
|
|
8125
|
-
console.error(
|
|
8355
|
+
console.error(import_chalk40.default.red("Not logged in. Run `apiblaze login` first."));
|
|
8126
8356
|
process.exit(1);
|
|
8127
8357
|
}
|
|
8128
8358
|
if (opts.clear) {
|
|
8129
8359
|
delete creds.activeTenant;
|
|
8130
8360
|
saveCredentials(creds);
|
|
8131
|
-
console.log(
|
|
8361
|
+
console.log(import_chalk40.default.green("Tenant scope cleared."));
|
|
8132
8362
|
return;
|
|
8133
8363
|
}
|
|
8134
8364
|
const { teamId } = await resolveTeam(opts.team);
|
|
8135
8365
|
const slug = await pickTenant(teamId, { message: "Scope future commands to which tenant?", initialQuery: query });
|
|
8136
8366
|
if (!slug) process.exit(1);
|
|
8137
8367
|
saveCredentials({ ...creds, activeTenant: slug });
|
|
8138
|
-
console.log(
|
|
8368
|
+
console.log(import_chalk40.default.green(`Tenant scope set to ${import_chalk40.default.bold(slug)}.`) + import_chalk40.default.dim(" (clear with `apiblaze tenant use --clear`)"));
|
|
8139
8369
|
}
|
|
8140
8370
|
async function runTenantList(opts) {
|
|
8141
8371
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -8152,22 +8382,22 @@ async function runTenantList(opts) {
|
|
|
8152
8382
|
return;
|
|
8153
8383
|
}
|
|
8154
8384
|
if (!tenants.length) {
|
|
8155
|
-
console.log(
|
|
8385
|
+
console.log(import_chalk40.default.yellow(opts.q ? `No tenants matching "${opts.q}".` : "No tenants."));
|
|
8156
8386
|
return;
|
|
8157
8387
|
}
|
|
8158
8388
|
for (const t of tenants) {
|
|
8159
8389
|
const name = typeof t === "string" ? t : t.tenant_name;
|
|
8160
|
-
const display = typeof t === "string" ? "" :
|
|
8161
|
-
console.log(` ${
|
|
8390
|
+
const display = typeof t === "string" ? "" : import_chalk40.default.dim(` ${t.display_name ?? ""}`);
|
|
8391
|
+
console.log(` ${import_chalk40.default.bold(name)}${display}`);
|
|
8162
8392
|
}
|
|
8163
8393
|
const total = out?.total ?? tenants.length;
|
|
8164
8394
|
if (total > tenants.length) {
|
|
8165
|
-
console.log(
|
|
8395
|
+
console.log(import_chalk40.default.dim(` \u2026 showing ${tenants.length} of ${total} \u2014 narrow with --q <search>`));
|
|
8166
8396
|
}
|
|
8167
8397
|
}
|
|
8168
8398
|
async function runTenantCreate(opts) {
|
|
8169
8399
|
if (!opts.name) {
|
|
8170
|
-
console.error(
|
|
8400
|
+
console.error(import_chalk40.default.red("--name (display name) is required."));
|
|
8171
8401
|
process.exit(1);
|
|
8172
8402
|
}
|
|
8173
8403
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -8179,7 +8409,7 @@ async function runTenantCreate(opts) {
|
|
|
8179
8409
|
body: { display_name: opts.name, ...opts.slug ? { tenant_name: opts.slug } : {} },
|
|
8180
8410
|
summary: `Create tenant "${opts.name}"`
|
|
8181
8411
|
});
|
|
8182
|
-
spinner.succeed(`Created tenant ${
|
|
8412
|
+
spinner.succeed(`Created tenant ${import_chalk40.default.bold(out?.tenant_name ?? opts.name)}.`);
|
|
8183
8413
|
if (opts.json) console.log(JSON.stringify(out));
|
|
8184
8414
|
} catch (err) {
|
|
8185
8415
|
spinner.fail("Tenant create failed.");
|
|
@@ -8188,7 +8418,7 @@ async function runTenantCreate(opts) {
|
|
|
8188
8418
|
}
|
|
8189
8419
|
async function runTenantAttach(project, opts) {
|
|
8190
8420
|
if (!opts.tenant) {
|
|
8191
|
-
console.error(
|
|
8421
|
+
console.error(import_chalk40.default.red("--tenant <slug> is required."));
|
|
8192
8422
|
process.exit(1);
|
|
8193
8423
|
}
|
|
8194
8424
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -8216,7 +8446,7 @@ async function runTenantDelete(slug, opts) {
|
|
|
8216
8446
|
{ type: "confirm", name: "confirm", message: `Permanently delete tenant "${slug}" and everything under it? This cannot be undone.`, default: false }
|
|
8217
8447
|
]);
|
|
8218
8448
|
if (!confirm2) {
|
|
8219
|
-
console.log(
|
|
8449
|
+
console.log(import_chalk40.default.dim("Aborted."));
|
|
8220
8450
|
return;
|
|
8221
8451
|
}
|
|
8222
8452
|
}
|
|
@@ -8235,7 +8465,7 @@ async function runTenantDelete(slug, opts) {
|
|
|
8235
8465
|
}
|
|
8236
8466
|
async function runTenantCors(opts) {
|
|
8237
8467
|
if (!opts.tenant) {
|
|
8238
|
-
console.error(
|
|
8468
|
+
console.error(import_chalk40.default.red("--tenant <slug> is required."));
|
|
8239
8469
|
process.exit(1);
|
|
8240
8470
|
}
|
|
8241
8471
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -8257,7 +8487,7 @@ async function runTenantCors(opts) {
|
|
|
8257
8487
|
}
|
|
8258
8488
|
|
|
8259
8489
|
// src/commands/tenant-drill.ts
|
|
8260
|
-
var
|
|
8490
|
+
var import_chalk41 = __toESM(require("chalk"));
|
|
8261
8491
|
var import_ora17 = __toESM(require("ora"));
|
|
8262
8492
|
var import_crypto = require("crypto");
|
|
8263
8493
|
init_admin();
|
|
@@ -8288,15 +8518,15 @@ async function validScopedTenant(teamId, query) {
|
|
|
8288
8518
|
const next = { ...creds };
|
|
8289
8519
|
delete next.activeTenant;
|
|
8290
8520
|
saveCredentials2(next);
|
|
8291
|
-
console.log(
|
|
8521
|
+
console.log(import_chalk41.default.yellow(`Tenant scope "${scoped}" no longer exists in this team \u2014 cleared.`));
|
|
8292
8522
|
return void 0;
|
|
8293
8523
|
}
|
|
8294
8524
|
async function tenantHome(teamId, tenant2) {
|
|
8295
8525
|
const { default: inquirer3 } = await import("inquirer");
|
|
8296
8526
|
const base2 = `/teams/${encodeURIComponent(teamId)}/tenants/${encodeURIComponent(tenant2)}`;
|
|
8297
|
-
console.log(
|
|
8527
|
+
console.log(import_chalk41.default.bold(`
|
|
8298
8528
|
Tenant ${tenant2}`));
|
|
8299
|
-
console.log(
|
|
8529
|
+
console.log(import_chalk41.default.dim("Tenant auth/settings are SHARED: changes apply to every proxy this tenant serves.\n"));
|
|
8300
8530
|
for (; ; ) {
|
|
8301
8531
|
const spinner = (0, import_ora17.default)("Reading tenant state...").start();
|
|
8302
8532
|
const [iam, cors, emails, issuers, opaque, clients] = await Promise.all([
|
|
@@ -8312,18 +8542,18 @@ Tenant ${tenant2}`));
|
|
|
8312
8542
|
const nClients = Array.isArray(clients) ? clients.length : 0;
|
|
8313
8543
|
const nOpaque = opaque?.opaque?.endpoint ? 1 : 0;
|
|
8314
8544
|
const nLogin = nClients + nIssuers + nOpaque;
|
|
8315
|
-
const onOff = (b) => b ?
|
|
8545
|
+
const onOff = (b) => b ? import_chalk41.default.green("on") : import_chalk41.default.dim("off");
|
|
8316
8546
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
8317
8547
|
type: "list",
|
|
8318
8548
|
name: "pick",
|
|
8319
8549
|
message: `Tenant ${tenant2}:`,
|
|
8320
8550
|
pageSize: 12,
|
|
8321
8551
|
choices: [
|
|
8322
|
-
{ name: `Login methods (${nLogin}) ${
|
|
8323
|
-
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${
|
|
8324
|
-
{ name: `Portal admins (${nEmails}) ${
|
|
8325
|
-
new inquirer3.Separator(
|
|
8326
|
-
{ name: `CORS: ${cors?.cors ?
|
|
8552
|
+
{ name: `Login methods (${nLogin}) ${import_chalk41.default.dim("how your consumers sign in")}`, value: "login" },
|
|
8553
|
+
{ name: `Users & groups: ${onOff(iam?.iam_enabled)} ${import_chalk41.default.dim(`identity & key management (iam.apiblaze.com)`)}`, value: "iam" },
|
|
8554
|
+
{ name: `Portal admins (${nEmails}) ${import_chalk41.default.dim("emails allowed to administer this tenant's portal")}`, value: "emails" },
|
|
8555
|
+
new inquirer3.Separator(import_chalk41.default.dim(" Settings")),
|
|
8556
|
+
{ name: `CORS: ${cors?.cors ? import_chalk41.default.cyan(JSON.stringify(cors.cors)) : import_chalk41.default.dim("(unset)")} ${import_chalk41.default.dim("browser origins allowed to call this tenant")}`, value: "cors" },
|
|
8327
8557
|
{ name: "\u2190 Back", value: "back" }
|
|
8328
8558
|
]
|
|
8329
8559
|
}]);
|
|
@@ -8336,7 +8566,7 @@ Tenant ${tenant2}`));
|
|
|
8336
8566
|
case "iam": {
|
|
8337
8567
|
const { v } = await inquirer3.prompt([{ type: "confirm", name: "v", message: "Enable Users & groups?", default: !!iam?.iam_enabled }]);
|
|
8338
8568
|
await admin({ method: "PATCH", path: `${base2}/iam`, body: { enabled: v }, summary: `IAM enforcement \u2192 ${v ? "on" : "off"}` });
|
|
8339
|
-
console.log(
|
|
8569
|
+
console.log(import_chalk41.default.green(` Users & groups ${v ? "enabled" : "disabled"}.`));
|
|
8340
8570
|
break;
|
|
8341
8571
|
}
|
|
8342
8572
|
case "cors": {
|
|
@@ -8349,11 +8579,11 @@ Tenant ${tenant2}`));
|
|
|
8349
8579
|
if (v === "") break;
|
|
8350
8580
|
const parsed = v === "null" ? null : safeJson(v);
|
|
8351
8581
|
if (parsed === void 0) {
|
|
8352
|
-
console.log(
|
|
8582
|
+
console.log(import_chalk41.default.yellow(" Not valid JSON \u2014 unchanged."));
|
|
8353
8583
|
break;
|
|
8354
8584
|
}
|
|
8355
8585
|
await admin({ method: "PUT", path: `${base2}/cors`, body: { cors: parsed }, summary: "Set tenant CORS" });
|
|
8356
|
-
console.log(
|
|
8586
|
+
console.log(import_chalk41.default.green(" CORS updated."));
|
|
8357
8587
|
break;
|
|
8358
8588
|
}
|
|
8359
8589
|
case "emails":
|
|
@@ -8377,15 +8607,15 @@ async function loginMethodsMenu(teamId, tenant2, base2) {
|
|
|
8377
8607
|
const choices = [];
|
|
8378
8608
|
for (const c of appClients) {
|
|
8379
8609
|
const nP = (c.providers ?? []).length || c.providers_count || 0;
|
|
8380
|
-
choices.push({ name: `${
|
|
8610
|
+
choices.push({ name: `${import_chalk41.default.cyan("APIblaze-hosted")} ${import_chalk41.default.bold(c.name ?? c.clientId)} ${import_chalk41.default.dim(`${c.clientId}${nP ? ` \xB7 ${nP} provider${nP === 1 ? "" : "s"}` : import_chalk41.default.yellow(" \xB7 needs a provider")}`)}`, value: { kind: "client", item: c } });
|
|
8381
8611
|
}
|
|
8382
8612
|
for (const i of issuers) {
|
|
8383
|
-
choices.push({ name: `${
|
|
8613
|
+
choices.push({ name: `${import_chalk41.default.cyan("Your own \u2014 JWT ")} ${import_chalk41.default.bold(i.iss)} ${import_chalk41.default.dim(`aud=${i.aud}`)}`, value: { kind: "issuer", item: i } });
|
|
8384
8614
|
}
|
|
8385
8615
|
if (opaque?.endpoint) {
|
|
8386
|
-
choices.push({ name: `${
|
|
8616
|
+
choices.push({ name: `${import_chalk41.default.cyan("Your own \u2014 opaque")} ${import_chalk41.default.bold(opaque.endpoint)}`, value: { kind: "opaque", item: opaque } });
|
|
8387
8617
|
}
|
|
8388
|
-
if (!choices.length) console.log(
|
|
8618
|
+
if (!choices.length) console.log(import_chalk41.default.dim("\n No login methods yet \u2014 consumers cannot sign in until you add one."));
|
|
8389
8619
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
8390
8620
|
type: "list",
|
|
8391
8621
|
name: "pick",
|
|
@@ -8425,9 +8655,9 @@ async function addLoginMethod(teamId, tenant2, base2) {
|
|
|
8425
8655
|
message: "How do people log in?",
|
|
8426
8656
|
pageSize: 8,
|
|
8427
8657
|
choices: [
|
|
8428
|
-
{ name: `APIblaze hosted login page ${
|
|
8429
|
-
{ name: `Your own hosted login \u2014 JWT ${
|
|
8430
|
-
{ name: `Your own login \u2014 opaque token ${
|
|
8658
|
+
{ name: `APIblaze hosted login page ${import_chalk41.default.dim("we host login; pick a provider (GitHub/Google/\u2026) \u2014 easiest")}`, value: "apiblaze" },
|
|
8659
|
+
{ name: `Your own hosted login \u2014 JWT ${import_chalk41.default.dim("your identity provider issues JWTs; we trust them")}`, value: "jwt" },
|
|
8660
|
+
{ name: `Your own login \u2014 opaque token ${import_chalk41.default.dim("we validate your opaque tokens via an introspection endpoint")}`, value: "opaque" },
|
|
8431
8661
|
{ name: "\u2190 Back", value: null }
|
|
8432
8662
|
]
|
|
8433
8663
|
}]);
|
|
@@ -8451,8 +8681,8 @@ function safeJson(s) {
|
|
|
8451
8681
|
async function emailsMenu(base2, emails) {
|
|
8452
8682
|
const { default: inquirer3 } = await import("inquirer");
|
|
8453
8683
|
console.log();
|
|
8454
|
-
if (!emails.length) console.log(
|
|
8455
|
-
for (const e of emails) console.log(` ${
|
|
8684
|
+
if (!emails.length) console.log(import_chalk41.default.dim(" No consumer-admin emails."));
|
|
8685
|
+
for (const e of emails) console.log(` ${import_chalk41.default.bold(e.email ?? e)} ${import_chalk41.default.dim(e.status ?? "")}`);
|
|
8456
8686
|
const { act } = await inquirer3.prompt([{
|
|
8457
8687
|
type: "list",
|
|
8458
8688
|
name: "act",
|
|
@@ -8467,7 +8697,7 @@ async function emailsMenu(base2, emails) {
|
|
|
8467
8697
|
if (act === "add") {
|
|
8468
8698
|
const { email } = await inquirer3.prompt([{ type: "input", name: "email", message: "Email:", validate: (s) => /.+@.+\..+/.test(s) || "not an email" }]);
|
|
8469
8699
|
await admin({ method: "POST", path: `${base2}/admin-emails`, body: { email }, summary: `Add consumer-admin ${email}` });
|
|
8470
|
-
console.log(
|
|
8700
|
+
console.log(import_chalk41.default.green(` ${email} added.`));
|
|
8471
8701
|
} else {
|
|
8472
8702
|
const { e } = await inquirer3.prompt([{
|
|
8473
8703
|
type: "list",
|
|
@@ -8477,7 +8707,7 @@ async function emailsMenu(base2, emails) {
|
|
|
8477
8707
|
}]);
|
|
8478
8708
|
if (!e) return;
|
|
8479
8709
|
await admin({ method: "DELETE", path: `${base2}/admin-emails/${encodeURIComponent(e)}`, summary: `Remove consumer-admin ${e}` });
|
|
8480
|
-
console.log(
|
|
8710
|
+
console.log(import_chalk41.default.green(` ${e} removed.`));
|
|
8481
8711
|
}
|
|
8482
8712
|
}
|
|
8483
8713
|
async function addIssuer(base2) {
|
|
@@ -8498,19 +8728,19 @@ async function addIssuer(base2) {
|
|
|
8498
8728
|
body: { iss: a.iss.trim(), aud: a.aud.trim(), jwks_url: a.jwks.trim() || null, sub_semantics: a.sem, ...claim ? { claim_name: claim } : {} },
|
|
8499
8729
|
summary: `Add external issuer ${a.iss.trim()}`
|
|
8500
8730
|
});
|
|
8501
|
-
console.log(
|
|
8731
|
+
console.log(import_chalk41.default.green(" JWT login method saved."));
|
|
8502
8732
|
}
|
|
8503
8733
|
async function issuerHome(base2, issuer) {
|
|
8504
8734
|
const { default: inquirer3 } = await import("inquirer");
|
|
8505
8735
|
console.log(`
|
|
8506
|
-
${
|
|
8736
|
+
${import_chalk41.default.bold(issuer.iss)} ${import_chalk41.default.dim(`aud=${issuer.aud} \xB7 ${issuer.sub_semantics ?? ""}`)}`);
|
|
8507
8737
|
const { act } = await inquirer3.prompt([{
|
|
8508
8738
|
type: "list",
|
|
8509
8739
|
name: "act",
|
|
8510
8740
|
message: "This JWT login method:",
|
|
8511
8741
|
choices: [
|
|
8512
8742
|
{ name: "Replace it (re-enter details)", value: "edit" },
|
|
8513
|
-
{ name:
|
|
8743
|
+
{ name: import_chalk41.default.red("Delete it"), value: "rm" },
|
|
8514
8744
|
{ name: "\u2190 Back", value: "back" }
|
|
8515
8745
|
]
|
|
8516
8746
|
}]);
|
|
@@ -8526,7 +8756,7 @@ async function issuerHome(base2, issuer) {
|
|
|
8526
8756
|
path: `${base2}/external-issuers?iss=${encodeURIComponent(issuer.iss)}&aud=${encodeURIComponent(issuer.aud)}`,
|
|
8527
8757
|
summary: `Delete issuer ${issuer.iss}`
|
|
8528
8758
|
});
|
|
8529
|
-
console.log(
|
|
8759
|
+
console.log(import_chalk41.default.green(" Deleted."));
|
|
8530
8760
|
}
|
|
8531
8761
|
async function setOpaque(base2, cur) {
|
|
8532
8762
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -8535,19 +8765,19 @@ async function setOpaque(base2, cur) {
|
|
|
8535
8765
|
{ type: "list", name: "method", message: "HTTP method:", choices: ["GET", "POST"], default: cur?.method ?? "GET" }
|
|
8536
8766
|
]);
|
|
8537
8767
|
await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: { endpoint: a.endpoint, method: a.method } }, summary: "Set opaque validator" });
|
|
8538
|
-
console.log(
|
|
8768
|
+
console.log(import_chalk41.default.green(" Opaque login method set."));
|
|
8539
8769
|
}
|
|
8540
8770
|
async function opaqueHome(base2, cur) {
|
|
8541
8771
|
const { default: inquirer3 } = await import("inquirer");
|
|
8542
8772
|
console.log(`
|
|
8543
|
-
${
|
|
8773
|
+
${import_chalk41.default.bold(cur.endpoint)} ${import_chalk41.default.dim(cur.method ?? "GET")}`);
|
|
8544
8774
|
const { act } = await inquirer3.prompt([{
|
|
8545
8775
|
type: "list",
|
|
8546
8776
|
name: "act",
|
|
8547
8777
|
message: "This opaque login method:",
|
|
8548
8778
|
choices: [
|
|
8549
8779
|
{ name: "Replace it", value: "edit" },
|
|
8550
|
-
{ name:
|
|
8780
|
+
{ name: import_chalk41.default.red("Delete it"), value: "rm" },
|
|
8551
8781
|
{ name: "\u2190 Back", value: "back" }
|
|
8552
8782
|
]
|
|
8553
8783
|
}]);
|
|
@@ -8557,7 +8787,7 @@ async function opaqueHome(base2, cur) {
|
|
|
8557
8787
|
return;
|
|
8558
8788
|
}
|
|
8559
8789
|
await admin({ method: "PUT", path: `${base2}/opaque`, body: { opaque: null }, summary: "Clear opaque validator" });
|
|
8560
|
-
console.log(
|
|
8790
|
+
console.log(import_chalk41.default.green(" Deleted."));
|
|
8561
8791
|
}
|
|
8562
8792
|
async function clientHome(base2, summary) {
|
|
8563
8793
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -8576,13 +8806,13 @@ async function clientHome(base2, summary) {
|
|
|
8576
8806
|
message: `${c.name ?? id}:`,
|
|
8577
8807
|
pageSize: 12,
|
|
8578
8808
|
choices: [
|
|
8579
|
-
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${
|
|
8580
|
-
{ name: `Callback URLs: ${cb.length ?
|
|
8581
|
-
{ name: `Scopes: ${scopes.length ?
|
|
8809
|
+
{ name: `Login providers${nProviders ? ` (${nProviders})` : ""} ${import_chalk41.default.dim("google/github/microsoft/\u2026 \u2014 how consumers sign in")}`, value: "providers" },
|
|
8810
|
+
{ name: `Callback URLs: ${cb.length ? import_chalk41.default.cyan(cb.join(", ")) : import_chalk41.default.dim("(none)")}`, value: "callbacks" },
|
|
8811
|
+
{ name: `Scopes: ${scopes.length ? import_chalk41.default.cyan(scopes.join(" ")) : import_chalk41.default.dim("(defaults)")}`, value: "scopes" },
|
|
8582
8812
|
{ name: `Token expiries: access ${c.accessTokenExpiry ?? 3600}s \xB7 id ${c.idTokenExpiry ?? 3600}s \xB7 refresh ${c.refreshTokenExpiry ?? 2592e3}s`, value: "expiries" },
|
|
8583
8813
|
{ name: "Reveal client secret", value: "secret" },
|
|
8584
8814
|
{ name: "Rotate client secret", value: "rotate" },
|
|
8585
|
-
{ name:
|
|
8815
|
+
{ name: import_chalk41.default.red("Delete this app client"), value: "delete" },
|
|
8586
8816
|
{ name: "\u2190 Back", value: "back" }
|
|
8587
8817
|
]
|
|
8588
8818
|
}]);
|
|
@@ -8595,13 +8825,13 @@ async function clientHome(base2, summary) {
|
|
|
8595
8825
|
case "callbacks": {
|
|
8596
8826
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Callback URLs (comma-separated):", default: cb.join(", ") }]);
|
|
8597
8827
|
await admin({ method: "PATCH", path: cBase, body: { authorizedCallbackUrls: parseList(v) }, summary: "Update callback URLs" });
|
|
8598
|
-
console.log(
|
|
8828
|
+
console.log(import_chalk41.default.green(" Callbacks updated."));
|
|
8599
8829
|
break;
|
|
8600
8830
|
}
|
|
8601
8831
|
case "scopes": {
|
|
8602
8832
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: "Scopes (space/comma-separated):", default: scopes.join(" ") }]);
|
|
8603
8833
|
await admin({ method: "PATCH", path: cBase, body: { scopes: v.split(/[\s,]+/).filter(Boolean) }, summary: "Update scopes" });
|
|
8604
|
-
console.log(
|
|
8834
|
+
console.log(import_chalk41.default.green(" Scopes updated."));
|
|
8605
8835
|
break;
|
|
8606
8836
|
}
|
|
8607
8837
|
case "expiries": {
|
|
@@ -8616,14 +8846,14 @@ async function clientHome(base2, summary) {
|
|
|
8616
8846
|
body: { accessTokenExpiry: Number(a.access), idTokenExpiry: Number(a.id), refreshTokenExpiry: Number(a.refresh) },
|
|
8617
8847
|
summary: "Update token expiries"
|
|
8618
8848
|
});
|
|
8619
|
-
console.log(
|
|
8849
|
+
console.log(import_chalk41.default.green(" Expiries updated."));
|
|
8620
8850
|
break;
|
|
8621
8851
|
}
|
|
8622
8852
|
case "secret": {
|
|
8623
8853
|
const { sure } = await inquirer3.prompt([{ type: "confirm", name: "sure", message: "Print the client secret to this terminal?", default: false }]);
|
|
8624
8854
|
if (!sure) break;
|
|
8625
8855
|
const s = await admin({ method: "GET", path: `${cBase}/secret`, summary: "Reveal client secret" });
|
|
8626
|
-
console.log(` ${
|
|
8856
|
+
console.log(` ${import_chalk41.default.bold("client_secret")}: ${import_chalk41.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
8627
8857
|
break;
|
|
8628
8858
|
}
|
|
8629
8859
|
case "rotate": {
|
|
@@ -8631,14 +8861,14 @@ async function clientHome(base2, summary) {
|
|
|
8631
8861
|
if (!sure) break;
|
|
8632
8862
|
const fresh = randomSecret();
|
|
8633
8863
|
await admin({ method: "PATCH", path: cBase, body: { clientSecret: fresh }, summary: "Rotate client secret" });
|
|
8634
|
-
console.log(` New ${
|
|
8864
|
+
console.log(` New ${import_chalk41.default.bold("client_secret")}: ${import_chalk41.default.green(fresh)} ${import_chalk41.default.dim("(store it now)")}`);
|
|
8635
8865
|
break;
|
|
8636
8866
|
}
|
|
8637
8867
|
case "delete": {
|
|
8638
8868
|
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 }]);
|
|
8639
8869
|
if (!sure) break;
|
|
8640
8870
|
await admin({ method: "DELETE", path: cBase, summary: `Delete app client ${id}` });
|
|
8641
|
-
console.log(
|
|
8871
|
+
console.log(import_chalk41.default.green(" App client deleted."));
|
|
8642
8872
|
return;
|
|
8643
8873
|
}
|
|
8644
8874
|
}
|
|
@@ -8665,9 +8895,9 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
8665
8895
|
const providers = Array.isArray(raw) ? raw : [];
|
|
8666
8896
|
console.log();
|
|
8667
8897
|
for (const p of providers) {
|
|
8668
|
-
console.log(` ${
|
|
8898
|
+
console.log(` ${import_chalk41.default.bold(p.type)} ${import_chalk41.default.dim(`${p.clientId || "(managed)"} \xB7 identity=${p.tokenType ?? "apiblaze"} \xB7 to-upstream=${p.targetServerToken ?? "apiblaze"}${p.isApiblazeDefault ? " \xB7 apiblaze-managed" : ""}`)}`);
|
|
8669
8899
|
}
|
|
8670
|
-
if (!providers.length) console.log(
|
|
8900
|
+
if (!providers.length) console.log(import_chalk41.default.dim(" No login providers \u2014 consumers cannot sign in to this client yet."));
|
|
8671
8901
|
const { act } = await inquirer3.prompt([{
|
|
8672
8902
|
type: "list",
|
|
8673
8903
|
name: "act",
|
|
@@ -8725,23 +8955,23 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
8725
8955
|
body.targetServerToken = routing;
|
|
8726
8956
|
}
|
|
8727
8957
|
await admin({ method: "POST", path: `${cBase}/providers`, body, summary: `Add ${type} login provider` });
|
|
8728
|
-
console.log(
|
|
8958
|
+
console.log(import_chalk41.default.green(` ${type} provider added.`));
|
|
8729
8959
|
} else {
|
|
8730
8960
|
const { p } = await inquirer3.prompt([{
|
|
8731
8961
|
type: "list",
|
|
8732
8962
|
name: "p",
|
|
8733
8963
|
message: act === "rm" ? "Remove which provider?" : "Reveal which secret?",
|
|
8734
|
-
choices: [...providers.map((x) => ({ name: `${x.type} ${
|
|
8964
|
+
choices: [...providers.map((x) => ({ name: `${x.type} ${import_chalk41.default.dim(x.clientId || "(managed)")}`, value: x })), { name: "\u2190 Back", value: null }]
|
|
8735
8965
|
}]);
|
|
8736
8966
|
if (!p) continue;
|
|
8737
8967
|
if (act === "rm") {
|
|
8738
8968
|
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 }]);
|
|
8739
8969
|
if (!sure) continue;
|
|
8740
8970
|
await admin({ method: "DELETE", path: `${cBase}/providers/${encodeURIComponent(p.id)}`, summary: `Remove ${p.type} provider` });
|
|
8741
|
-
console.log(
|
|
8971
|
+
console.log(import_chalk41.default.green(` ${p.type} removed.`));
|
|
8742
8972
|
} else {
|
|
8743
8973
|
const s = await admin({ method: "GET", path: `${cBase}/providers/${encodeURIComponent(p.id)}/secret`, summary: `Reveal ${p.type} provider secret` });
|
|
8744
|
-
console.log(` ${
|
|
8974
|
+
console.log(` ${import_chalk41.default.bold("client_secret")}: ${import_chalk41.default.green(s?.clientSecret ?? s?.client_secret ?? JSON.stringify(s))}`);
|
|
8745
8975
|
}
|
|
8746
8976
|
}
|
|
8747
8977
|
}
|
|
@@ -8749,7 +8979,7 @@ async function providersMenu(cBase, clientLabel) {
|
|
|
8749
8979
|
|
|
8750
8980
|
// src/commands/spec.ts
|
|
8751
8981
|
var fs11 = __toESM(require("fs"));
|
|
8752
|
-
var
|
|
8982
|
+
var import_chalk42 = __toESM(require("chalk"));
|
|
8753
8983
|
var import_ora18 = __toESM(require("ora"));
|
|
8754
8984
|
init_admin();
|
|
8755
8985
|
init_api();
|
|
@@ -8766,14 +8996,14 @@ async function runSpecGet(project, opts) {
|
|
|
8766
8996
|
}
|
|
8767
8997
|
async function runSpecSet(project, opts) {
|
|
8768
8998
|
if (!opts.file) {
|
|
8769
|
-
console.error(
|
|
8999
|
+
console.error(import_chalk42.default.red("--file <path> is required (OpenAPI JSON or YAML)."));
|
|
8770
9000
|
process.exit(1);
|
|
8771
9001
|
}
|
|
8772
9002
|
let specContent;
|
|
8773
9003
|
try {
|
|
8774
9004
|
specContent = fs11.readFileSync(opts.file, "utf-8");
|
|
8775
9005
|
} catch {
|
|
8776
|
-
console.error(
|
|
9006
|
+
console.error(import_chalk42.default.red(`Cannot read file: ${opts.file}`));
|
|
8777
9007
|
process.exit(1);
|
|
8778
9008
|
}
|
|
8779
9009
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -8802,23 +9032,23 @@ async function runSpecSet(project, opts) {
|
|
|
8802
9032
|
function printSpecChangeReport(report, projectName) {
|
|
8803
9033
|
const r = report.routes;
|
|
8804
9034
|
if (r) {
|
|
8805
|
-
for (const a of r.added) console.log(
|
|
8806
|
-
for (const d of r.removed) console.log(
|
|
8807
|
-
for (const rn of r.renamed ?? []) console.log(
|
|
8808
|
-
for (const m of r.modified ?? []) console.log(
|
|
9035
|
+
for (const a of r.added) console.log(import_chalk42.default.green(` + ${a}`));
|
|
9036
|
+
for (const d of r.removed) console.log(import_chalk42.default.red(` \u2212 ${d}`));
|
|
9037
|
+
for (const rn of r.renamed ?? []) console.log(import_chalk42.default.yellow(` ~ ${rn.from} \u2192 ${rn.to}`));
|
|
9038
|
+
for (const m of r.modified ?? []) console.log(import_chalk42.default.dim(` \xB1 ${m} (params/schema changed)`));
|
|
8809
9039
|
}
|
|
8810
|
-
if (report.applied?.mcp_regenerated) console.log(
|
|
8811
|
-
if (report.applied?.candidates_cleared) console.log(
|
|
9040
|
+
if (report.applied?.mcp_regenerated) console.log(import_chalk42.default.dim(" MCP catalogue regenerated."));
|
|
9041
|
+
if (report.applied?.candidates_cleared) console.log(import_chalk42.default.dim(` ${report.applied.candidates_cleared} route candidate(s) now documented \u2014 cleared.`));
|
|
8812
9042
|
for (const item of report.attention ?? []) {
|
|
8813
|
-
console.log(
|
|
9043
|
+
console.log(import_chalk42.default.yellow(` \u26A0 ${item.detail}`));
|
|
8814
9044
|
if (item.suggestion?.carry_from && item.suggestion.carry_to) {
|
|
8815
|
-
console.log(
|
|
9045
|
+
console.log(import_chalk42.default.dim(` Fix: apiblaze spec carry ${projectName} --method ${item.suggestion.method} --from "${item.suggestion.carry_from}" --to "${item.suggestion.carry_to}"`));
|
|
8816
9046
|
}
|
|
8817
9047
|
}
|
|
8818
9048
|
}
|
|
8819
9049
|
async function runSpecCarry(project, opts) {
|
|
8820
9050
|
if (!opts.method || !opts.from || !opts.to) {
|
|
8821
|
-
console.error(
|
|
9051
|
+
console.error(import_chalk42.default.red("--method, --from and --to are required."));
|
|
8822
9052
|
process.exit(1);
|
|
8823
9053
|
}
|
|
8824
9054
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -8840,7 +9070,7 @@ async function runSpecCarry(project, opts) {
|
|
|
8840
9070
|
}
|
|
8841
9071
|
async function runSpecDeleteRule(project, opts) {
|
|
8842
9072
|
if (!opts.method || !opts.route) {
|
|
8843
|
-
console.error(
|
|
9073
|
+
console.error(import_chalk42.default.red("--method and --route are required."));
|
|
8844
9074
|
process.exit(1);
|
|
8845
9075
|
}
|
|
8846
9076
|
const { teamId } = await resolveTeam(opts.team);
|
|
@@ -8860,14 +9090,14 @@ async function runSpecDeleteRule(project, opts) {
|
|
|
8860
9090
|
}
|
|
8861
9091
|
|
|
8862
9092
|
// src/commands/agent.ts
|
|
8863
|
-
var
|
|
9093
|
+
var import_chalk44 = __toESM(require("chalk"));
|
|
8864
9094
|
var import_ora19 = __toESM(require("ora"));
|
|
8865
9095
|
init_auth();
|
|
8866
9096
|
init_admin();
|
|
8867
9097
|
init_resolve();
|
|
8868
9098
|
|
|
8869
9099
|
// src/lib/tools.ts
|
|
8870
|
-
var
|
|
9100
|
+
var import_chalk43 = __toESM(require("chalk"));
|
|
8871
9101
|
init_admin();
|
|
8872
9102
|
init_api();
|
|
8873
9103
|
init_resolve();
|
|
@@ -8887,15 +9117,15 @@ var TOOLS = [
|
|
|
8887
9117
|
const key = keys.dev ?? Object.values(keys)[0];
|
|
8888
9118
|
const url = `https://${a.name}.abz.run/${version2}/dev`;
|
|
8889
9119
|
const tryIt = buildTryItCurl(url, auth, key);
|
|
8890
|
-
const lines = [` ${
|
|
8891
|
-
if (res.devPortal) lines.push(` ${
|
|
9120
|
+
const lines = [` ${import_chalk43.default.dim("Proxy URL:")} ${import_chalk43.default.bold(url)}`];
|
|
9121
|
+
if (res.devPortal) lines.push(` ${import_chalk43.default.dim("Dev portal:")} ${res.devPortal}`);
|
|
8892
9122
|
const envs = Object.keys(keys);
|
|
8893
9123
|
if (envs.length) {
|
|
8894
|
-
lines.push("", ` ${
|
|
9124
|
+
lines.push("", ` ${import_chalk43.default.bold("API keys")} ${import_chalk43.default.dim("(bootstrapped \u2014 send as the X-API-Key header; shown once):")}`);
|
|
8895
9125
|
const w = Math.max(...envs.map((e) => e.length));
|
|
8896
|
-
for (const env of envs) lines.push(` ${
|
|
9126
|
+
for (const env of envs) lines.push(` ${import_chalk43.default.cyan(env.padEnd(w))} ${import_chalk43.default.green(keys[env])}`);
|
|
8897
9127
|
}
|
|
8898
|
-
if (tryIt) lines.push("", ` ${
|
|
9128
|
+
if (tryIt) lines.push("", ` ${import_chalk43.default.dim("Try it:")}`, ` ${import_chalk43.default.cyan(tryIt)}`);
|
|
8899
9129
|
return { ...res, proxy_url: url, keys, ...tryIt ? { try_it: tryIt } : {}, display: lines.join("\n") };
|
|
8900
9130
|
}
|
|
8901
9131
|
},
|
|
@@ -9051,17 +9281,17 @@ function truncate(value, max = 1500) {
|
|
|
9051
9281
|
}
|
|
9052
9282
|
function printCost(resp) {
|
|
9053
9283
|
const line = formatBilling(resp.billing);
|
|
9054
|
-
if (line) console.log(
|
|
9284
|
+
if (line) console.log(import_chalk44.default.magenta(` ${line}`) + import_chalk44.default.dim(` (${resp.llm.model}, ${resp.llm.total_tokens} tok)`));
|
|
9055
9285
|
}
|
|
9056
9286
|
async function runAgent(opts) {
|
|
9057
9287
|
requireAuth();
|
|
9058
9288
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
9059
9289
|
const { default: inquirer3 } = await import("inquirer");
|
|
9060
|
-
console.log(
|
|
9061
|
-
console.log(
|
|
9290
|
+
console.log(import_chalk44.default.bold("APIblaze agent") + import_chalk44.default.dim(` \xB7 team ${teamName ?? teamId}`));
|
|
9291
|
+
console.log(import_chalk44.default.dim('Ask me to create/delete/configure proxies, tenants, keys, domains, specs. Type "exit" to quit.\n'));
|
|
9062
9292
|
const history = [];
|
|
9063
9293
|
while (true) {
|
|
9064
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
9294
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk44.default.cyan("you") + " \u203A" }]);
|
|
9065
9295
|
const text = (input ?? "").trim();
|
|
9066
9296
|
if (!text) continue;
|
|
9067
9297
|
if (["exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
@@ -9075,14 +9305,14 @@ async function runAgent(opts) {
|
|
|
9075
9305
|
} catch (err) {
|
|
9076
9306
|
spinner.stop();
|
|
9077
9307
|
if (err instanceof ApiError && err.status === 402) {
|
|
9078
|
-
console.log(
|
|
9308
|
+
console.log(import_chalk44.default.yellow(" Insufficient credits \u2014 top up to keep using the agent."));
|
|
9079
9309
|
break;
|
|
9080
9310
|
}
|
|
9081
9311
|
throw err;
|
|
9082
9312
|
}
|
|
9083
9313
|
history.push({ role: "assistant", content: resp.raw });
|
|
9084
9314
|
printCost(resp);
|
|
9085
|
-
if (resp.reply) console.log(
|
|
9315
|
+
if (resp.reply) console.log(import_chalk44.default.green("agent") + " \u203A " + resp.reply);
|
|
9086
9316
|
if (!resp.action) break;
|
|
9087
9317
|
const tool = findTool(resp.action.tool);
|
|
9088
9318
|
if (!tool) {
|
|
@@ -9107,11 +9337,11 @@ async function runAgent(opts) {
|
|
|
9107
9337
|
}
|
|
9108
9338
|
renderTrace();
|
|
9109
9339
|
if (step === MAX_TOOL_STEPS - 1) {
|
|
9110
|
-
console.log(
|
|
9340
|
+
console.log(import_chalk44.default.dim(" (paused after several steps \u2014 tell me how to continue)"));
|
|
9111
9341
|
}
|
|
9112
9342
|
}
|
|
9113
9343
|
}
|
|
9114
|
-
console.log(
|
|
9344
|
+
console.log(import_chalk44.default.dim("\nBye."));
|
|
9115
9345
|
}
|
|
9116
9346
|
|
|
9117
9347
|
// src/commands/config-browse.ts
|
|
@@ -9406,11 +9636,11 @@ function dig(blob, dotted) {
|
|
|
9406
9636
|
}
|
|
9407
9637
|
var readSetting = (s, cfg) => s.read ? s.read(cfg) : dig(cfg, s.key);
|
|
9408
9638
|
function show(v) {
|
|
9409
|
-
if (v === void 0) return
|
|
9410
|
-
if (v === null) return
|
|
9411
|
-
if (typeof v === "object") return
|
|
9412
|
-
if (typeof v === "boolean") return v ?
|
|
9413
|
-
return
|
|
9639
|
+
if (v === void 0) return import_chalk45.default.dim("(unset)");
|
|
9640
|
+
if (v === null) return import_chalk45.default.dim("null");
|
|
9641
|
+
if (typeof v === "object") return import_chalk45.default.cyan(JSON.stringify(v));
|
|
9642
|
+
if (typeof v === "boolean") return v ? import_chalk45.default.green("on") : import_chalk45.default.red("off");
|
|
9643
|
+
return import_chalk45.default.cyan(String(v));
|
|
9414
9644
|
}
|
|
9415
9645
|
function parseValue(raw) {
|
|
9416
9646
|
if (raw === "true") return true;
|
|
@@ -9452,10 +9682,10 @@ async function patchSetting(proj2, s, value, cfg) {
|
|
|
9452
9682
|
}
|
|
9453
9683
|
}
|
|
9454
9684
|
var loginFirst = (what) => {
|
|
9455
|
-
console.log(
|
|
9685
|
+
console.log(import_chalk45.default.yellow(`
|
|
9456
9686
|
Log in first to ${what}.`));
|
|
9457
|
-
console.log(
|
|
9458
|
-
console.log(
|
|
9687
|
+
console.log(import_chalk45.default.dim(" Run `npx apiblaze login` \u2014 or `npx apiblaze claim` if you created this proxy"));
|
|
9688
|
+
console.log(import_chalk45.default.dim(" anonymously and want to bring it into your account.\n"));
|
|
9459
9689
|
};
|
|
9460
9690
|
async function runConfig(project, key, value, opts) {
|
|
9461
9691
|
const creds = loadCredentials();
|
|
@@ -9472,9 +9702,9 @@ async function runConfig(project, key, value, opts) {
|
|
|
9472
9702
|
}
|
|
9473
9703
|
const setting = SETTINGS.find((s) => s.key === key);
|
|
9474
9704
|
if (!setting) {
|
|
9475
|
-
console.error(
|
|
9476
|
-
console.error(
|
|
9477
|
-
console.error(
|
|
9705
|
+
console.error(import_chalk45.default.red(`Unknown setting "${key}".`));
|
|
9706
|
+
console.error(import_chalk45.default.dim(" Known: " + SETTINGS.map((s) => s.key).join(", ")));
|
|
9707
|
+
console.error(import_chalk45.default.dim(" (Features like transforms/domains/tenants live in the menu: `apiblaze config <project>`.)"));
|
|
9478
9708
|
process.exit(1);
|
|
9479
9709
|
}
|
|
9480
9710
|
if (value === void 0) {
|
|
@@ -9489,7 +9719,7 @@ async function pickProject(teamId) {
|
|
|
9489
9719
|
const { getProjects: getProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
9490
9720
|
const projects = await getProjects2(teamId).catch(() => []);
|
|
9491
9721
|
if (!projects.length) {
|
|
9492
|
-
console.error(
|
|
9722
|
+
console.error(import_chalk45.default.red("No projects in this team. Create one: `npx apiblaze create`."));
|
|
9493
9723
|
process.exit(1);
|
|
9494
9724
|
}
|
|
9495
9725
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -9497,7 +9727,7 @@ async function pickProject(teamId) {
|
|
|
9497
9727
|
type: "list",
|
|
9498
9728
|
name: "picked",
|
|
9499
9729
|
message: "Which project?",
|
|
9500
|
-
choices: projects.map((p) => ({ name: `${p.projectName} ${
|
|
9730
|
+
choices: projects.map((p) => ({ name: `${p.projectName} ${import_chalk45.default.dim("v" + p.apiVersion)}`, value: p }))
|
|
9501
9731
|
}]);
|
|
9502
9732
|
return { projectId: picked.projectId, projectName: picked.projectName, apiVersion: picked.apiVersion, teamId, tenant: picked.tenant };
|
|
9503
9733
|
}
|
|
@@ -9508,25 +9738,25 @@ function printAll(proj2, cfg, json) {
|
|
|
9508
9738
|
console.log(JSON.stringify(out, null, 2));
|
|
9509
9739
|
return;
|
|
9510
9740
|
}
|
|
9511
|
-
console.log(
|
|
9741
|
+
console.log(import_chalk45.default.bold(`
|
|
9512
9742
|
${proj2.projectName} v${proj2.apiVersion} \u2014 settings
|
|
9513
9743
|
`));
|
|
9514
9744
|
for (const group2 of SETTING_GROUPS) {
|
|
9515
|
-
console.log(
|
|
9745
|
+
console.log(import_chalk45.default.bold(group2));
|
|
9516
9746
|
for (const s of SETTINGS.filter((x) => x.group === group2)) {
|
|
9517
|
-
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${
|
|
9747
|
+
console.log(` ${s.key.padEnd(32)} ${show(readSetting(s, cfg))} ${import_chalk45.default.dim(s.desc)}`);
|
|
9518
9748
|
}
|
|
9519
9749
|
console.log();
|
|
9520
9750
|
}
|
|
9521
|
-
console.log(
|
|
9751
|
+
console.log(import_chalk45.default.dim("Change one: apiblaze config " + proj2.projectName + " <key> <value> (add --verbose for the API call)"));
|
|
9522
9752
|
}
|
|
9523
9753
|
async function discoveryMenu(project) {
|
|
9524
9754
|
const { default: inquirer3 } = await import("inquirer");
|
|
9525
|
-
console.log(
|
|
9755
|
+
console.log(import_chalk45.default.bold(`
|
|
9526
9756
|
APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
9527
9757
|
`));
|
|
9528
|
-
console.log(
|
|
9529
|
-
console.log(
|
|
9758
|
+
console.log(import_chalk45.default.dim("You are not logged in \u2014 browsing what's configurable. Everything below works"));
|
|
9759
|
+
console.log(import_chalk45.default.dim("from this menu once you log in (`npx apiblaze login`).\n"));
|
|
9530
9760
|
for (; ; ) {
|
|
9531
9761
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
9532
9762
|
type: "list",
|
|
@@ -9534,13 +9764,13 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
9534
9764
|
message: "Explore:",
|
|
9535
9765
|
pageSize: 20,
|
|
9536
9766
|
choices: [
|
|
9537
|
-
new inquirer3.Separator(
|
|
9767
|
+
new inquirer3.Separator(import_chalk45.default.bold("\u2014 Settings \u2014")),
|
|
9538
9768
|
...SETTING_GROUPS.map((g) => ({
|
|
9539
|
-
name: `${g} ${
|
|
9769
|
+
name: `${g} ${import_chalk45.default.dim(SETTINGS.filter((s) => s.group === g).map((s) => s.label).join(", "))}`,
|
|
9540
9770
|
value: { kind: "settings", g }
|
|
9541
9771
|
})),
|
|
9542
|
-
new inquirer3.Separator(
|
|
9543
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
9772
|
+
new inquirer3.Separator(import_chalk45.default.bold("\u2014 Features \u2014")),
|
|
9773
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk45.default.dim(f.desc)}`, value: { kind: "feature", f } })),
|
|
9544
9774
|
new inquirer3.Separator(),
|
|
9545
9775
|
{ name: "Exit", value: { kind: "exit" } }
|
|
9546
9776
|
]
|
|
@@ -9549,24 +9779,24 @@ APIblaze proxy configuration${project ? ` \u2014 ${project}` : ""}
|
|
|
9549
9779
|
if (pick2.kind === "settings") {
|
|
9550
9780
|
console.log();
|
|
9551
9781
|
for (const s of SETTINGS.filter((x) => x.group === pick2.g)) {
|
|
9552
|
-
console.log(` ${
|
|
9553
|
-
console.log(` ${
|
|
9782
|
+
console.log(` ${import_chalk45.default.bold(s.label.padEnd(28))} ${import_chalk45.default.dim(s.desc)}`);
|
|
9783
|
+
console.log(` ${import_chalk45.default.dim(" key: " + s.key)}`);
|
|
9554
9784
|
}
|
|
9555
9785
|
loginFirst("view or change these settings");
|
|
9556
9786
|
} else {
|
|
9557
9787
|
const f = pick2.f;
|
|
9558
9788
|
console.log(`
|
|
9559
|
-
${
|
|
9789
|
+
${import_chalk45.default.bold(f.label)} \u2014 ${f.desc}`);
|
|
9560
9790
|
loginFirst(`use ${f.label.toLowerCase()}`);
|
|
9561
9791
|
}
|
|
9562
9792
|
}
|
|
9563
9793
|
}
|
|
9564
9794
|
async function navigator(proj2, cfg, opts) {
|
|
9565
9795
|
const { default: inquirer3 } = await import("inquirer");
|
|
9566
|
-
console.log(
|
|
9796
|
+
console.log(import_chalk45.default.bold(`
|
|
9567
9797
|
${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
9568
9798
|
`));
|
|
9569
|
-
console.log(
|
|
9799
|
+
console.log(import_chalk45.default.dim("Tip: every change is one API call \u2014 add --verbose to see the curl equivalent.\n"));
|
|
9570
9800
|
let blob = cfg;
|
|
9571
9801
|
for (; ; ) {
|
|
9572
9802
|
const { pick: pick2 } = await inquirer3.prompt([{
|
|
@@ -9575,10 +9805,10 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
9575
9805
|
message: "Where to?",
|
|
9576
9806
|
pageSize: 20,
|
|
9577
9807
|
choices: [
|
|
9578
|
-
new inquirer3.Separator(
|
|
9808
|
+
new inquirer3.Separator(import_chalk45.default.bold("\u2014 Settings \u2014")),
|
|
9579
9809
|
...SETTING_GROUPS.map((g) => ({ name: g, value: { kind: "settings", g } })),
|
|
9580
|
-
new inquirer3.Separator(
|
|
9581
|
-
...FEATURES.map((f) => ({ name: `${f.label} ${
|
|
9810
|
+
new inquirer3.Separator(import_chalk45.default.bold("\u2014 Features \u2014")),
|
|
9811
|
+
...FEATURES.map((f) => ({ name: `${f.label} ${import_chalk45.default.dim(f.desc)}`, value: { kind: f.go } })),
|
|
9582
9812
|
new inquirer3.Separator(),
|
|
9583
9813
|
{ name: "Show all settings", value: { kind: "list" } },
|
|
9584
9814
|
{ name: "Exit", value: { kind: "exit" } }
|
|
@@ -9618,7 +9848,7 @@ ${proj2.projectName} v${proj2.apiVersion} \u2014 configuration
|
|
|
9618
9848
|
}
|
|
9619
9849
|
}
|
|
9620
9850
|
} catch (err) {
|
|
9621
|
-
console.error(
|
|
9851
|
+
console.error(import_chalk45.default.red(` ${err instanceof Error ? err.message : String(err)}`));
|
|
9622
9852
|
}
|
|
9623
9853
|
}
|
|
9624
9854
|
}
|
|
@@ -9632,7 +9862,7 @@ async function settingsGroup(proj2, cfg, group2) {
|
|
|
9632
9862
|
message: group2 + ":",
|
|
9633
9863
|
pageSize: 16,
|
|
9634
9864
|
choices: [
|
|
9635
|
-
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${
|
|
9865
|
+
...items.map((s2) => ({ name: `${s2.label.padEnd(30)} ${show(readSetting(s2, cfg))} ${import_chalk45.default.dim(s2.desc)}`, value: s2 })),
|
|
9636
9866
|
new inquirer3.Separator(),
|
|
9637
9867
|
{ name: "\u2190 Back", value: null }
|
|
9638
9868
|
]
|
|
@@ -9650,7 +9880,7 @@ async function settingsGroup(proj2, cfg, group2) {
|
|
|
9650
9880
|
} else if (s.type === "number") {
|
|
9651
9881
|
const { v } = await inquirer3.prompt([{ type: "input", name: "v", message: `${s.label} (number):`, default: readSetting(s, cfg) }]);
|
|
9652
9882
|
if (v === "" || Number.isNaN(Number(v))) {
|
|
9653
|
-
console.log(
|
|
9883
|
+
console.log(import_chalk45.default.yellow(" Not a number \u2014 unchanged."));
|
|
9654
9884
|
continue;
|
|
9655
9885
|
}
|
|
9656
9886
|
value = Number(v);
|
|
@@ -9731,7 +9961,7 @@ async function buildCondition(phase) {
|
|
|
9731
9961
|
const items = [];
|
|
9732
9962
|
for (; ; ) {
|
|
9733
9963
|
const a = await inquirer3.prompt([
|
|
9734
|
-
{ type: "input", name: "source", message: `Condition field ${
|
|
9964
|
+
{ type: "input", name: "source", message: `Condition field ${import_chalk45.default.dim(srcHint)}:`, validate: (s) => !!s || "required" },
|
|
9735
9965
|
{ type: "list", name: "operator", message: "Operator:", choices: [
|
|
9736
9966
|
"eq",
|
|
9737
9967
|
"neq",
|
|
@@ -9762,7 +9992,7 @@ async function buildCondition(phase) {
|
|
|
9762
9992
|
function showCondition(cond) {
|
|
9763
9993
|
if (!Array.isArray(cond) || !cond.length) return "";
|
|
9764
9994
|
const s = cond.map((c) => `${c.source} ${c.operator}${c.value !== void 0 ? ` "${c.value}"` : ""}${c.logicOp ? ` ${c.logicOp}` : ""}`).join(" ");
|
|
9765
|
-
return
|
|
9995
|
+
return import_chalk45.default.dim(` when ${s}`);
|
|
9766
9996
|
}
|
|
9767
9997
|
async function transformsMenu(proj2) {
|
|
9768
9998
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -9771,12 +10001,12 @@ async function transformsMenu(proj2) {
|
|
|
9771
10001
|
const out = await admin({ method: "GET", path: base2, summary: "List transform rules" });
|
|
9772
10002
|
const rules = out?.rules ?? [];
|
|
9773
10003
|
console.log();
|
|
9774
|
-
if (!rules.length) console.log(
|
|
10004
|
+
if (!rules.length) console.log(import_chalk45.default.dim(" No transform rules yet."));
|
|
9775
10005
|
for (const r of rules) {
|
|
9776
10006
|
const a = r.action ?? {};
|
|
9777
10007
|
const fns = [...a.source_fns ?? [], ...a.dest_fns ?? []].map((f) => f.fn);
|
|
9778
|
-
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 ?
|
|
9779
|
-
console.log(` ${r.enabled ?
|
|
10008
|
+
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_chalk45.default.dim(` via ${fns.join("\u2192")}`) : ""}`;
|
|
10009
|
+
console.log(` ${r.enabled ? import_chalk45.default.green("\u25CF") : import_chalk45.default.dim("\u25CB")} ${import_chalk45.default.bold(r.name)} ${import_chalk45.default.dim(`[${r.phase ?? "request"}]`)} ${what}${showCondition(r.condition)}`);
|
|
9780
10010
|
}
|
|
9781
10011
|
const { act } = await inquirer3.prompt([{
|
|
9782
10012
|
type: "list",
|
|
@@ -9788,7 +10018,7 @@ async function transformsMenu(proj2) {
|
|
|
9788
10018
|
{ name: "Enable/disable a rule", value: "toggle" },
|
|
9789
10019
|
{ name: "Delete a rule", value: "delete" }
|
|
9790
10020
|
] : [],
|
|
9791
|
-
{ name:
|
|
10021
|
+
{ name: import_chalk45.default.dim("Add from raw JSON (grouped conditions, lookup tables, \u2026)"), value: "raw" },
|
|
9792
10022
|
{ name: "\u2190 Back", value: "back" }
|
|
9793
10023
|
]
|
|
9794
10024
|
}]);
|
|
@@ -9801,11 +10031,11 @@ async function transformsMenu(proj2) {
|
|
|
9801
10031
|
}]);
|
|
9802
10032
|
const body = parseValue(raw);
|
|
9803
10033
|
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
9804
|
-
console.log(
|
|
10034
|
+
console.log(import_chalk45.default.yellow(" Not a JSON object \u2014 skipped."));
|
|
9805
10035
|
continue;
|
|
9806
10036
|
}
|
|
9807
10037
|
await admin({ method: "POST", path: base2, body, summary: "Create transform rule (raw JSON)" });
|
|
9808
|
-
console.log(
|
|
10038
|
+
console.log(import_chalk45.default.green(" Rule created."));
|
|
9809
10039
|
continue;
|
|
9810
10040
|
}
|
|
9811
10041
|
if (act === "add") {
|
|
@@ -9821,7 +10051,7 @@ async function transformsMenu(proj2) {
|
|
|
9821
10051
|
{ name: "Remove a field", value: "remove" }
|
|
9822
10052
|
] }
|
|
9823
10053
|
]);
|
|
9824
|
-
const fieldHint =
|
|
10054
|
+
const fieldHint = import_chalk45.default.dim("(e.g. header:x-api-version, param:limit, bodyvar:user.id)");
|
|
9825
10055
|
let action2;
|
|
9826
10056
|
if (ans.kind === "hardcode") {
|
|
9827
10057
|
const a = await inquirer3.prompt([
|
|
@@ -9868,16 +10098,16 @@ async function transformsMenu(proj2) {
|
|
|
9868
10098
|
type: "list",
|
|
9869
10099
|
name: "rule",
|
|
9870
10100
|
message: act === "toggle" ? "Which rule?" : "Delete which rule?",
|
|
9871
|
-
choices: [...rules.map((r) => ({ name: `${r.name} ${
|
|
10101
|
+
choices: [...rules.map((r) => ({ name: `${r.name} ${import_chalk45.default.dim(`[${r.phase ?? "request"}]`)}`, value: r })), { name: "\u2190 Back", value: null }]
|
|
9872
10102
|
}]);
|
|
9873
10103
|
if (!rule) continue;
|
|
9874
10104
|
if (act === "toggle") {
|
|
9875
10105
|
const flipped = { ...rule, enabled: rule.enabled === false };
|
|
9876
10106
|
await admin({ method: "PUT", path: `${base2}/${rule.id}`, body: flipped, summary: `${flipped.enabled ? "Enable" : "Disable"} transform "${rule.name}"` });
|
|
9877
|
-
console.log(
|
|
10107
|
+
console.log(import_chalk45.default.green(` ${rule.name} \u2192 ${flipped.enabled ? "enabled" : "disabled"}`));
|
|
9878
10108
|
} else {
|
|
9879
10109
|
await admin({ method: "DELETE", path: `${base2}/${rule.id}`, summary: `Delete transform "${rule.name}"` });
|
|
9880
|
-
console.log(
|
|
10110
|
+
console.log(import_chalk45.default.green(` ${rule.name} deleted.`));
|
|
9881
10111
|
}
|
|
9882
10112
|
}
|
|
9883
10113
|
}
|
|
@@ -9889,9 +10119,9 @@ async function mappingsMenu(proj2) {
|
|
|
9889
10119
|
const out = await admin({ method: "GET", path: base2, summary: "List mapping tables" });
|
|
9890
10120
|
const tables = out?.mappings ?? out?.tables ?? [];
|
|
9891
10121
|
console.log();
|
|
9892
|
-
if (!tables.length) console.log(
|
|
10122
|
+
if (!tables.length) console.log(import_chalk45.default.dim(" No mapping tables yet."));
|
|
9893
10123
|
for (const t of tables) {
|
|
9894
|
-
console.log(` ${
|
|
10124
|
+
console.log(` ${import_chalk45.default.bold(t.name)} ${import_chalk45.default.dim(`${t.entries?.length ?? "?"} entries${t.hide_map_values ? ", hidden" : ""}${t.encrypt_values ? ", encrypted" : ""}`)}`);
|
|
9895
10125
|
}
|
|
9896
10126
|
const { act } = await inquirer3.prompt([{
|
|
9897
10127
|
type: "list",
|
|
@@ -9911,11 +10141,11 @@ async function mappingsMenu(proj2) {
|
|
|
9911
10141
|
]);
|
|
9912
10142
|
const entries2 = parseValue(a.entries);
|
|
9913
10143
|
if (!Array.isArray(entries2)) {
|
|
9914
|
-
console.log(
|
|
10144
|
+
console.log(import_chalk45.default.yellow(" Entries must be a JSON array \u2014 not created."));
|
|
9915
10145
|
continue;
|
|
9916
10146
|
}
|
|
9917
10147
|
await admin({ method: "POST", path: base2, body: { name: a.name, entries: entries2 }, summary: `Create mapping table "${a.name}"` });
|
|
9918
|
-
console.log(
|
|
10148
|
+
console.log(import_chalk45.default.green(` Table "${a.name}" created.`));
|
|
9919
10149
|
} else {
|
|
9920
10150
|
const { table } = await inquirer3.prompt([{
|
|
9921
10151
|
type: "list",
|
|
@@ -9925,7 +10155,7 @@ async function mappingsMenu(proj2) {
|
|
|
9925
10155
|
}]);
|
|
9926
10156
|
if (!table) continue;
|
|
9927
10157
|
await admin({ method: "DELETE", path: `${base2}/${table.id}`, summary: `Delete mapping table "${table.name}"` });
|
|
9928
|
-
console.log(
|
|
10158
|
+
console.log(import_chalk45.default.green(` ${table.name} deleted.`));
|
|
9929
10159
|
}
|
|
9930
10160
|
}
|
|
9931
10161
|
}
|
|
@@ -9936,14 +10166,14 @@ async function tenantsMenu(proj2, opts) {
|
|
|
9936
10166
|
const out = await admin({ method: "GET", path: base2, summary: "List attached tenants" });
|
|
9937
10167
|
const tenants = out?.tenants ?? [];
|
|
9938
10168
|
console.log();
|
|
9939
|
-
if (!tenants.length) console.log(
|
|
9940
|
-
for (const t of tenants) console.log(` ${
|
|
10169
|
+
if (!tenants.length) console.log(import_chalk45.default.dim(" No tenants attached (consumers use the default tenant)."));
|
|
10170
|
+
for (const t of tenants) console.log(` ${import_chalk45.default.bold(t.tenant_name ?? t.name)} ${import_chalk45.default.dim(t.display_name ?? "")}`);
|
|
9941
10171
|
const { act } = await inquirer3.prompt([{
|
|
9942
10172
|
type: "list",
|
|
9943
10173
|
name: "act",
|
|
9944
10174
|
message: "Tenants:",
|
|
9945
10175
|
choices: [
|
|
9946
|
-
{ name: `Manage a tenant\u2026 ${
|
|
10176
|
+
{ name: `Manage a tenant\u2026 ${import_chalk45.default.dim("settings, login app clients, providers, issuers \u2014 affects EVERY proxy the tenant serves")}`, value: "manage" },
|
|
9947
10177
|
{ name: "Attach a tenant to this project", value: "attach" },
|
|
9948
10178
|
...tenants.length ? [{ name: "Detach a tenant from this project", value: "detach" }] : [],
|
|
9949
10179
|
{ name: "\u2190 Back", value: "back" }
|
|
@@ -9966,7 +10196,7 @@ async function tenantsMenu(proj2, opts) {
|
|
|
9966
10196
|
}]);
|
|
9967
10197
|
if (!t) continue;
|
|
9968
10198
|
await admin({ method: "DELETE", path: `${base2}/${encodeURIComponent(t.tenant_name ?? t.name)}`, summary: `Detach tenant ${t.tenant_name ?? t.name}` });
|
|
9969
|
-
console.log(
|
|
10199
|
+
console.log(import_chalk45.default.green(` Detached ${t.tenant_name ?? t.name}.`));
|
|
9970
10200
|
}
|
|
9971
10201
|
}
|
|
9972
10202
|
}
|
|
@@ -10009,7 +10239,7 @@ async function specMenu(proj2, opts) {
|
|
|
10009
10239
|
choices: [
|
|
10010
10240
|
{ name: "Print the stored spec", value: "get" },
|
|
10011
10241
|
{ name: "Refresh the spec from its source", value: "refresh" },
|
|
10012
|
-
{ name:
|
|
10242
|
+
{ name: import_chalk45.default.dim("Build the spec by chatting over real traffic \u2192 agent"), value: "agent" },
|
|
10013
10243
|
{ name: "\u2190 Back", value: "back" }
|
|
10014
10244
|
]
|
|
10015
10245
|
}]);
|
|
@@ -10017,7 +10247,7 @@ async function specMenu(proj2, opts) {
|
|
|
10017
10247
|
if (act === "get") await runSpecGet(proj2.projectName, { team: opts.team, apiversion: proj2.apiVersion });
|
|
10018
10248
|
else if (act === "refresh") {
|
|
10019
10249
|
await admin({ method: "POST", path: `/projects/${proj2.projectId}/${proj2.apiVersion}/refresh-spec`, summary: "Refresh spec from source" });
|
|
10020
|
-
console.log(
|
|
10250
|
+
console.log(import_chalk45.default.green(" Spec refresh triggered."));
|
|
10021
10251
|
} else await runOpenapi(proj2.projectName, proj2.apiVersion);
|
|
10022
10252
|
}
|
|
10023
10253
|
async function agentsMenu(proj2, opts) {
|
|
@@ -10042,7 +10272,7 @@ async function agentsMenu(proj2, opts) {
|
|
|
10042
10272
|
}
|
|
10043
10273
|
|
|
10044
10274
|
// src/commands/group.ts
|
|
10045
|
-
var
|
|
10275
|
+
var import_chalk46 = __toESM(require("chalk"));
|
|
10046
10276
|
var import_ora21 = __toESM(require("ora"));
|
|
10047
10277
|
init_auth();
|
|
10048
10278
|
init_admin();
|
|
@@ -10136,7 +10366,7 @@ async function userByRefOrCreate(t, ref) {
|
|
|
10136
10366
|
if (!ref.includes("@")) throw e;
|
|
10137
10367
|
const created = await t.call("/v1/admin/users", "POST", { email: ref.trim().toLowerCase() });
|
|
10138
10368
|
if (!created?.abz_sub) throw e;
|
|
10139
|
-
console.log(
|
|
10369
|
+
console.log(import_chalk46.default.dim(` ${ref} wasn't a user yet \u2014 created.`));
|
|
10140
10370
|
return { abz_sub: created.abz_sub, email: created.email ?? ref, display_name: null };
|
|
10141
10371
|
}
|
|
10142
10372
|
}
|
|
@@ -10144,11 +10374,11 @@ async function runGroupList(opts) {
|
|
|
10144
10374
|
const t = await resolveTransport(opts);
|
|
10145
10375
|
const groups = await listGroups(t);
|
|
10146
10376
|
if (opts.json) return void console.log(JSON.stringify(groups));
|
|
10147
|
-
if (!groups.length) return void console.log(
|
|
10148
|
-
console.log(
|
|
10377
|
+
if (!groups.length) return void console.log(import_chalk46.default.yellow(`No groups in ${t.tenant}. Create one: apiblaze group create <name>`));
|
|
10378
|
+
console.log(import_chalk46.default.dim(`Tenant ${t.tenant}:`));
|
|
10149
10379
|
for (const g of groups) {
|
|
10150
|
-
const sealed = g.name === "apiblaze_admins" ?
|
|
10151
|
-
console.log(` ${
|
|
10380
|
+
const sealed = g.name === "apiblaze_admins" ? import_chalk46.default.dim(" (admins \u2014 managed by allowlist)") : "";
|
|
10381
|
+
console.log(` ${import_chalk46.default.bold(g.name)} ${import_chalk46.default.dim(`${g.member_count ?? 0} member(s)`)}${sealed}`);
|
|
10152
10382
|
}
|
|
10153
10383
|
}
|
|
10154
10384
|
async function runGroupCreate(name, opts) {
|
|
@@ -10176,7 +10406,7 @@ async function runGroupCreate(name, opts) {
|
|
|
10176
10406
|
const spinner = (0, import_ora21.default)(`Creating group ${name}...`).start();
|
|
10177
10407
|
try {
|
|
10178
10408
|
await t.call("/v1/admin/groups", "POST", { name, admins: [adminSub] });
|
|
10179
|
-
spinner.succeed(`Group ${
|
|
10409
|
+
spinner.succeed(`Group ${import_chalk46.default.bold(name)} created in ${t.tenant}.`);
|
|
10180
10410
|
} catch (e) {
|
|
10181
10411
|
spinner.fail("Create failed.");
|
|
10182
10412
|
throw e;
|
|
@@ -10188,7 +10418,7 @@ async function runGroupDelete(name, opts) {
|
|
|
10188
10418
|
const spinner = (0, import_ora21.default)(`Deleting group ${name}...`).start();
|
|
10189
10419
|
try {
|
|
10190
10420
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}`, "DELETE");
|
|
10191
|
-
spinner.succeed(`Group ${
|
|
10421
|
+
spinner.succeed(`Group ${import_chalk46.default.bold(name)} deleted.`);
|
|
10192
10422
|
} catch (e) {
|
|
10193
10423
|
spinner.fail("Delete failed.");
|
|
10194
10424
|
throw e;
|
|
@@ -10201,11 +10431,11 @@ async function runGroupMembers(name, opts) {
|
|
|
10201
10431
|
const subs = await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/subgroups`);
|
|
10202
10432
|
if (opts.json) return void console.log(JSON.stringify({ ...detail, subgroups: subs?.subgroups ?? [] }));
|
|
10203
10433
|
const members = detail?.members ?? [];
|
|
10204
|
-
console.log(
|
|
10205
|
-
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ?
|
|
10206
|
-
if (!members.length) console.log(
|
|
10434
|
+
console.log(import_chalk46.default.bold(`${g.name}`) + import_chalk46.default.dim(` (${t.tenant})`));
|
|
10435
|
+
for (const m of members) console.log(` ${label2(m)} ${m.role === "admin" ? import_chalk46.default.cyan("group-admin") : import_chalk46.default.dim("member")}`);
|
|
10436
|
+
if (!members.length) console.log(import_chalk46.default.dim(" no members"));
|
|
10207
10437
|
const sg = subs?.subgroups ?? [];
|
|
10208
|
-
if (sg.length) console.log(
|
|
10438
|
+
if (sg.length) console.log(import_chalk46.default.dim(` subgroups: ${sg.map((s) => s.name).join(", ")}`));
|
|
10209
10439
|
}
|
|
10210
10440
|
async function runGroupAddUser(userRef, groupName, opts) {
|
|
10211
10441
|
const t = await resolveTransport(opts);
|
|
@@ -10215,7 +10445,7 @@ async function runGroupAddUser(userRef, groupName, opts) {
|
|
|
10215
10445
|
const spinner = (0, import_ora21.default)(`Adding ${label2(u)} to ${g.name}...`).start();
|
|
10216
10446
|
try {
|
|
10217
10447
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members`, "POST", { abz_sub: u.abz_sub, role });
|
|
10218
|
-
spinner.succeed(`${
|
|
10448
|
+
spinner.succeed(`${import_chalk46.default.bold(label2(u))} added to ${import_chalk46.default.bold(g.name)} as ${role}.`);
|
|
10219
10449
|
} catch (e) {
|
|
10220
10450
|
spinner.fail("Add failed.");
|
|
10221
10451
|
throw e;
|
|
@@ -10227,7 +10457,7 @@ async function runGroupRemoveUser(userRef, groupName, opts) {
|
|
|
10227
10457
|
const spinner = (0, import_ora21.default)(`Removing ${label2(u)} from ${g.name}...`).start();
|
|
10228
10458
|
try {
|
|
10229
10459
|
await t.call(`/v1/admin/groups/${encodeURIComponent(g.id)}/members/_?abz_sub=${encodeURIComponent(u.abz_sub)}`, "DELETE");
|
|
10230
|
-
spinner.succeed(`${
|
|
10460
|
+
spinner.succeed(`${import_chalk46.default.bold(label2(u))} removed from ${import_chalk46.default.bold(g.name)}.`);
|
|
10231
10461
|
} catch (e) {
|
|
10232
10462
|
spinner.fail("Remove failed.");
|
|
10233
10463
|
throw e;
|
|
@@ -10239,7 +10469,7 @@ async function runGroupAddGroup(childName, parentName, opts) {
|
|
|
10239
10469
|
const spinner = (0, import_ora21.default)(`Nesting ${child.name} under ${parent.name}...`).start();
|
|
10240
10470
|
try {
|
|
10241
10471
|
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups`, "POST", { child_group_id: child.id });
|
|
10242
|
-
spinner.succeed(`${
|
|
10472
|
+
spinner.succeed(`${import_chalk46.default.bold(child.name)} is now a subgroup of ${import_chalk46.default.bold(parent.name)}.`);
|
|
10243
10473
|
} catch (e) {
|
|
10244
10474
|
spinner.fail("Nest failed.");
|
|
10245
10475
|
throw e;
|
|
@@ -10251,7 +10481,7 @@ async function runGroupRemoveGroup(childName, parentName, opts) {
|
|
|
10251
10481
|
const spinner = (0, import_ora21.default)(`Un-nesting ${child.name} from ${parent.name}...`).start();
|
|
10252
10482
|
try {
|
|
10253
10483
|
await t.call(`/v1/admin/groups/${encodeURIComponent(parent.id)}/subgroups/${encodeURIComponent(child.id)}`, "DELETE");
|
|
10254
|
-
spinner.succeed(`${
|
|
10484
|
+
spinner.succeed(`${import_chalk46.default.bold(child.name)} un-nested from ${import_chalk46.default.bold(parent.name)}.`);
|
|
10255
10485
|
} catch (e) {
|
|
10256
10486
|
spinner.fail("Un-nest failed.");
|
|
10257
10487
|
throw e;
|
|
@@ -10262,18 +10492,18 @@ async function runGroupUsers(opts) {
|
|
|
10262
10492
|
const users = await listUsers(t);
|
|
10263
10493
|
const observed = await t.call("/v1/admin/observed-identities").catch(() => ({ identities: [] }));
|
|
10264
10494
|
if (opts.json) return void console.log(JSON.stringify({ users, observed: observed?.identities ?? [] }));
|
|
10265
|
-
console.log(
|
|
10266
|
-
for (const u of users) console.log(` ${label2(u)} ${
|
|
10267
|
-
if (!users.length) console.log(
|
|
10495
|
+
console.log(import_chalk46.default.dim(`Tenant ${t.tenant} \u2014 user directory:`));
|
|
10496
|
+
for (const u of users) console.log(` ${label2(u)} ${import_chalk46.default.dim(u.abz_sub)}`);
|
|
10497
|
+
if (!users.length) console.log(import_chalk46.default.dim(" no users yet"));
|
|
10268
10498
|
const fresh = (observed?.identities ?? []).filter((o) => !o.provisioned);
|
|
10269
10499
|
if (fresh.length) {
|
|
10270
|
-
console.log(
|
|
10271
|
-
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${
|
|
10500
|
+
console.log(import_chalk46.default.dim("Seen in traffic (not yet users):"));
|
|
10501
|
+
for (const o of fresh) console.log(` ${o.email || o.abz_sub} ${import_chalk46.default.dim(o.source_label ?? "")}`);
|
|
10272
10502
|
}
|
|
10273
10503
|
}
|
|
10274
10504
|
|
|
10275
10505
|
// src/commands/key.ts
|
|
10276
|
-
var
|
|
10506
|
+
var import_chalk47 = __toESM(require("chalk"));
|
|
10277
10507
|
var import_ora22 = __toESM(require("ora"));
|
|
10278
10508
|
init_caller();
|
|
10279
10509
|
async function runApikeysMenu(opts) {
|
|
@@ -10303,11 +10533,11 @@ async function runKeyList(opts) {
|
|
|
10303
10533
|
return;
|
|
10304
10534
|
}
|
|
10305
10535
|
if (!keys.length) {
|
|
10306
|
-
console.log(
|
|
10536
|
+
console.log(import_chalk47.default.yellow("No developer keys."));
|
|
10307
10537
|
return;
|
|
10308
10538
|
}
|
|
10309
10539
|
for (const k of keys) {
|
|
10310
|
-
console.log(` ${
|
|
10540
|
+
console.log(` ${import_chalk47.default.bold(k.key_id ?? k.id)} ${import_chalk47.default.dim(k.description ?? "")} ${import_chalk47.default.dim(k.expires_at ?? "no expiry")}`);
|
|
10311
10541
|
}
|
|
10312
10542
|
}
|
|
10313
10543
|
async function runKeyMint(opts) {
|
|
@@ -10329,9 +10559,9 @@ async function runKeyMint(opts) {
|
|
|
10329
10559
|
console.log(JSON.stringify(out));
|
|
10330
10560
|
return;
|
|
10331
10561
|
}
|
|
10332
|
-
console.log(` ${
|
|
10333
|
-
console.log(` ${
|
|
10334
|
-
if (out?.expires_at) console.log(` ${
|
|
10562
|
+
console.log(` ${import_chalk47.default.bold("key_id")}: ${out?.key_id}`);
|
|
10563
|
+
console.log(` ${import_chalk47.default.bold("key")}: ${import_chalk47.default.green(out?.key)} ${import_chalk47.default.dim("(shown once \u2014 store it now)")}`);
|
|
10564
|
+
if (out?.expires_at) console.log(` ${import_chalk47.default.dim("expires:")} ${out.expires_at}`);
|
|
10335
10565
|
} catch (err) {
|
|
10336
10566
|
spinner.fail("Mint failed.");
|
|
10337
10567
|
throw err;
|
|
@@ -10358,7 +10588,7 @@ async function runKeyRevoke(keyId, opts) {
|
|
|
10358
10588
|
init_iam();
|
|
10359
10589
|
|
|
10360
10590
|
// src/commands/admins.ts
|
|
10361
|
-
var
|
|
10591
|
+
var import_chalk48 = __toESM(require("chalk"));
|
|
10362
10592
|
var import_ora23 = __toESM(require("ora"));
|
|
10363
10593
|
init_caller();
|
|
10364
10594
|
function requireTenant(opts) {
|
|
@@ -10386,13 +10616,13 @@ async function runAdminsList(opts) {
|
|
|
10386
10616
|
return;
|
|
10387
10617
|
}
|
|
10388
10618
|
if (!admins2.length) {
|
|
10389
|
-
console.log(
|
|
10619
|
+
console.log(import_chalk48.default.yellow(`No tenant admins for ${tenant2} yet.`));
|
|
10390
10620
|
return;
|
|
10391
10621
|
}
|
|
10392
10622
|
for (const a of admins2) {
|
|
10393
10623
|
const email = typeof a === "string" ? a : a.email;
|
|
10394
10624
|
const flags = [a.active === false ? "pending" : null, a.pinned ? "pinned" : null].filter(Boolean).join(", ");
|
|
10395
|
-
console.log(` ${
|
|
10625
|
+
console.log(` ${import_chalk48.default.bold(email)}${flags ? import_chalk48.default.dim(` (${flags})`) : ""}`);
|
|
10396
10626
|
}
|
|
10397
10627
|
}
|
|
10398
10628
|
async function runAdminsAdd(email, opts) {
|
|
@@ -10407,9 +10637,9 @@ async function runAdminsAdd(email, opts) {
|
|
|
10407
10637
|
body: { email },
|
|
10408
10638
|
summary: `Add ${email} to tenant ${tenant2} admins`
|
|
10409
10639
|
});
|
|
10410
|
-
spinner?.succeed(`${
|
|
10640
|
+
spinner?.succeed(`${import_chalk48.default.bold(email)} is now a tenant admin of ${import_chalk48.default.bold(tenant2)}.`);
|
|
10411
10641
|
if (opts.json) console.log(JSON.stringify(out ?? { ok: true }));
|
|
10412
|
-
else console.log(
|
|
10642
|
+
else console.log(import_chalk48.default.dim(" Reload the Users & Groups widget \u2014 access flips from \u201Cpending\u201D to ready."));
|
|
10413
10643
|
} catch (err) {
|
|
10414
10644
|
spinner?.fail("Add failed.");
|
|
10415
10645
|
throw err;
|
|
@@ -10426,7 +10656,7 @@ async function runAdminsRemove(email, opts) {
|
|
|
10426
10656
|
path: `${base(teamId, tenant2)}/${encodeURIComponent(email)}`,
|
|
10427
10657
|
summary: `Remove ${email} from tenant ${tenant2} admins`
|
|
10428
10658
|
});
|
|
10429
|
-
spinner?.succeed(`Removed ${
|
|
10659
|
+
spinner?.succeed(`Removed ${import_chalk48.default.bold(email)} from the tenant admins of ${import_chalk48.default.bold(tenant2)}.`);
|
|
10430
10660
|
} catch (err) {
|
|
10431
10661
|
spinner?.fail("Remove failed.");
|
|
10432
10662
|
throw err;
|
|
@@ -10434,7 +10664,7 @@ async function runAdminsRemove(email, opts) {
|
|
|
10434
10664
|
}
|
|
10435
10665
|
|
|
10436
10666
|
// src/commands/preapprove.ts
|
|
10437
|
-
var
|
|
10667
|
+
var import_chalk49 = __toESM(require("chalk"));
|
|
10438
10668
|
var import_ora24 = __toESM(require("ora"));
|
|
10439
10669
|
async function runPreapprove(who, opts) {
|
|
10440
10670
|
const tenant2 = await resolveProducerTenant(opts.tenant);
|
|
@@ -10442,14 +10672,14 @@ async function runPreapprove(who, opts) {
|
|
|
10442
10672
|
const rules = await listPreapprovalRules(tenant2);
|
|
10443
10673
|
if (opts.json) return void console.log(JSON.stringify(rules, null, 2));
|
|
10444
10674
|
if (!rules.length) {
|
|
10445
|
-
console.log(
|
|
10446
|
-
console.log(
|
|
10675
|
+
console.log(import_chalk49.default.dim(`No pre-approval rules in ${tenant2}. Anyone who signs in can access (unless access is restricted).`));
|
|
10676
|
+
console.log(import_chalk49.default.dim(`Add one: apiblaze preapprove someone@acme.com`));
|
|
10447
10677
|
return;
|
|
10448
10678
|
}
|
|
10449
|
-
console.log(
|
|
10679
|
+
console.log(import_chalk49.default.dim(`Pre-approved for ${import_chalk49.default.bold(tenant2)}:`));
|
|
10450
10680
|
for (const r of rules) {
|
|
10451
|
-
const tag = r.kind === "domain" ?
|
|
10452
|
-
const grp = r.groups?.length ?
|
|
10681
|
+
const tag = r.kind === "domain" ? import_chalk49.default.cyan("@" + r.value + " (whole domain)") : r.value;
|
|
10682
|
+
const grp = r.groups?.length ? import_chalk49.default.dim(` \u2192 groups: ${r.groups.join(", ")}`) : "";
|
|
10453
10683
|
console.log(` ${tag}${grp}`);
|
|
10454
10684
|
}
|
|
10455
10685
|
return;
|
|
@@ -10460,17 +10690,17 @@ async function runPreapprove(who, opts) {
|
|
|
10460
10690
|
if (opts.remove) {
|
|
10461
10691
|
const spinner2 = (0, import_ora24.default)(`Removing ${who} from ${tenant2}\u2026`).start();
|
|
10462
10692
|
const { removed, value } = await removePreapprovalRule(tenant2, who);
|
|
10463
|
-
if (removed) spinner2.succeed(`${
|
|
10464
|
-
else spinner2.warn(`No pre-approval rule for ${
|
|
10693
|
+
if (removed) spinner2.succeed(`${import_chalk49.default.bold(value)} is no longer pre-approved for ${tenant2}.`);
|
|
10694
|
+
else spinner2.warn(`No pre-approval rule for ${import_chalk49.default.bold(value)} in ${tenant2} \u2014 nothing to remove.`);
|
|
10465
10695
|
return;
|
|
10466
10696
|
}
|
|
10467
10697
|
const spinner = (0, import_ora24.default)(`Pre-approving ${who} for ${tenant2}\u2026`).start();
|
|
10468
10698
|
try {
|
|
10469
10699
|
const { kind, value } = await addPreapprovalRule(tenant2, who, opts.group);
|
|
10470
10700
|
const what = kind === "domain" ? `Anyone @${value}` : value;
|
|
10471
|
-
spinner.succeed(`${
|
|
10472
|
-
if (opts.group?.length) console.log(
|
|
10473
|
-
console.log(
|
|
10701
|
+
spinner.succeed(`${import_chalk49.default.bold(what)} can now sign in to ${import_chalk49.default.bold(tenant2)}.`);
|
|
10702
|
+
if (opts.group?.length) console.log(import_chalk49.default.dim(` They'll join group(s) ${opts.group.join(", ")} on first sign-in.`));
|
|
10703
|
+
console.log(import_chalk49.default.dim(` See the full list: apiblaze preapprove --list`));
|
|
10474
10704
|
} catch (err) {
|
|
10475
10705
|
spinner.fail("Could not add the rule.");
|
|
10476
10706
|
throw err;
|
|
@@ -10478,7 +10708,7 @@ async function runPreapprove(who, opts) {
|
|
|
10478
10708
|
}
|
|
10479
10709
|
|
|
10480
10710
|
// src/commands/consumer.ts
|
|
10481
|
-
var
|
|
10711
|
+
var import_chalk50 = __toESM(require("chalk"));
|
|
10482
10712
|
var import_ora25 = __toESM(require("ora"));
|
|
10483
10713
|
init_admin();
|
|
10484
10714
|
init_resolve();
|
|
@@ -10500,7 +10730,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
10500
10730
|
function requireConsumer2() {
|
|
10501
10731
|
const c = loadConsumer();
|
|
10502
10732
|
if (!c) {
|
|
10503
|
-
console.error(
|
|
10733
|
+
console.error(import_chalk50.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
10504
10734
|
process.exit(1);
|
|
10505
10735
|
}
|
|
10506
10736
|
return c;
|
|
@@ -10511,7 +10741,7 @@ async function runConsumerLogin(opts) {
|
|
|
10511
10741
|
let clientId = opts.client;
|
|
10512
10742
|
if (clientId) {
|
|
10513
10743
|
if (!tenant2) {
|
|
10514
|
-
console.error(
|
|
10744
|
+
console.error(import_chalk50.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
10515
10745
|
process.exit(1);
|
|
10516
10746
|
}
|
|
10517
10747
|
} else {
|
|
@@ -10529,18 +10759,18 @@ async function runConsumerLogin(opts) {
|
|
|
10529
10759
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
10530
10760
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
10531
10761
|
if (!pick2) {
|
|
10532
|
-
console.error(
|
|
10762
|
+
console.error(import_chalk50.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
10533
10763
|
process.exit(1);
|
|
10534
10764
|
}
|
|
10535
10765
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
10536
10766
|
}
|
|
10537
|
-
console.log(`${
|
|
10767
|
+
console.log(`${import_chalk50.default.cyan("\u2192")} Logging in to ${import_chalk50.default.bold(tenant2)} as a consumer...`);
|
|
10538
10768
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
10539
10769
|
console.log(`
|
|
10540
|
-
Open: ${
|
|
10541
|
-
console.log(` Code: ${
|
|
10770
|
+
Open: ${import_chalk50.default.underline(verificationUri)}`);
|
|
10771
|
+
console.log(` Code: ${import_chalk50.default.bold(userCode)}
|
|
10542
10772
|
`);
|
|
10543
|
-
console.log(
|
|
10773
|
+
console.log(import_chalk50.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
10544
10774
|
});
|
|
10545
10775
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
10546
10776
|
const creds = {
|
|
@@ -10555,7 +10785,7 @@ async function runConsumerLogin(opts) {
|
|
|
10555
10785
|
obtainedAt: Date.now()
|
|
10556
10786
|
};
|
|
10557
10787
|
saveConsumer(creds);
|
|
10558
|
-
console.log(
|
|
10788
|
+
console.log(import_chalk50.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
10559
10789
|
}
|
|
10560
10790
|
async function runConsumerTokens(opts) {
|
|
10561
10791
|
const creds = requireConsumer2();
|
|
@@ -10568,18 +10798,18 @@ async function runConsumerTokens(opts) {
|
|
|
10568
10798
|
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));
|
|
10569
10799
|
return;
|
|
10570
10800
|
}
|
|
10571
|
-
console.log(`${
|
|
10801
|
+
console.log(`${import_chalk50.default.cyan("Consumer")} ${import_chalk50.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk50.default.bold(fresh.tenant)}
|
|
10572
10802
|
`);
|
|
10573
|
-
console.log(`${
|
|
10803
|
+
console.log(`${import_chalk50.default.bold("access_token")} ${import_chalk50.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
10574
10804
|
${fresh.accessToken}
|
|
10575
10805
|
`);
|
|
10576
|
-
if (fresh.idToken) console.log(`${
|
|
10806
|
+
if (fresh.idToken) console.log(`${import_chalk50.default.bold("id_token")} ${import_chalk50.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
10577
10807
|
${fresh.idToken}
|
|
10578
10808
|
`);
|
|
10579
|
-
if (fresh.refreshToken) console.log(`${
|
|
10809
|
+
if (fresh.refreshToken) console.log(`${import_chalk50.default.bold("refresh_token")}
|
|
10580
10810
|
${fresh.refreshToken}
|
|
10581
10811
|
`);
|
|
10582
|
-
console.log(
|
|
10812
|
+
console.log(import_chalk50.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
10583
10813
|
}
|
|
10584
10814
|
async function runConsumerApikeys(opts) {
|
|
10585
10815
|
const creds = requireConsumer2();
|
|
@@ -10589,8 +10819,8 @@ async function runConsumerApikeys(opts) {
|
|
|
10589
10819
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
10590
10820
|
spinner.stop();
|
|
10591
10821
|
if (list.status >= 400) {
|
|
10592
|
-
console.error(
|
|
10593
|
-
if (list.status === 401) console.error(
|
|
10822
|
+
console.error(import_chalk50.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
10823
|
+
if (list.status === 401) console.error(import_chalk50.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
10594
10824
|
process.exit(1);
|
|
10595
10825
|
}
|
|
10596
10826
|
const keys = list.data?.keys ?? [];
|
|
@@ -10598,16 +10828,16 @@ async function runConsumerApikeys(opts) {
|
|
|
10598
10828
|
if (opts.json) {
|
|
10599
10829
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
10600
10830
|
} else if (!keys.length) {
|
|
10601
|
-
console.log(
|
|
10831
|
+
console.log(import_chalk50.default.yellow("No API keys yet."));
|
|
10602
10832
|
} else {
|
|
10603
10833
|
for (const k of keys) {
|
|
10604
10834
|
const clear = revealMap[k.environment]?.key;
|
|
10605
|
-
const shown = clear ?
|
|
10606
|
-
const exp = k.expires_at ?
|
|
10607
|
-
console.log(` ${
|
|
10835
|
+
const shown = clear ? import_chalk50.default.green(clear) : import_chalk50.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
10836
|
+
const exp = k.expires_at ? import_chalk50.default.dim(`exp ${k.expires_at}`) : import_chalk50.default.dim("no expiry");
|
|
10837
|
+
console.log(` ${import_chalk50.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk50.default.dim(k.description ?? "")}`);
|
|
10608
10838
|
}
|
|
10609
10839
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
10610
|
-
console.log(
|
|
10840
|
+
console.log(import_chalk50.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
10611
10841
|
}
|
|
10612
10842
|
}
|
|
10613
10843
|
if (opts.json) return;
|
|
@@ -10629,12 +10859,12 @@ async function runConsumerApikeys(opts) {
|
|
|
10629
10859
|
}
|
|
10630
10860
|
s2.succeed("Key created.");
|
|
10631
10861
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
10632
|
-
if (key) console.log(` ${
|
|
10633
|
-
else console.log(
|
|
10862
|
+
if (key) console.log(` ${import_chalk50.default.green(key)} ${import_chalk50.default.dim("(shown once \u2014 store it now)")}`);
|
|
10863
|
+
else console.log(import_chalk50.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
10634
10864
|
}
|
|
10635
10865
|
|
|
10636
10866
|
// src/commands/sidecar.ts
|
|
10637
|
-
var
|
|
10867
|
+
var import_chalk51 = __toESM(require("chalk"));
|
|
10638
10868
|
var import_ora26 = __toESM(require("ora"));
|
|
10639
10869
|
var fs12 = __toESM(require("fs"));
|
|
10640
10870
|
var path8 = __toESM(require("path"));
|
|
@@ -10677,7 +10907,7 @@ function upsertEnvLocal(root, token) {
|
|
|
10677
10907
|
}
|
|
10678
10908
|
function installSidecarPackage(root) {
|
|
10679
10909
|
if (fs12.existsSync(path8.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
10680
|
-
console.log(` ${
|
|
10910
|
+
console.log(` ${import_chalk51.default.green("\u2713")} apiblaze package already installed`);
|
|
10681
10911
|
return;
|
|
10682
10912
|
}
|
|
10683
10913
|
const has = (f) => fs12.existsSync(path8.join(root, f));
|
|
@@ -10688,7 +10918,7 @@ function installSidecarPackage(root) {
|
|
|
10688
10918
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
10689
10919
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
10690
10920
|
} catch {
|
|
10691
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
10921
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk51.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk51.default.cyan("npm run dev")}.`);
|
|
10692
10922
|
}
|
|
10693
10923
|
}
|
|
10694
10924
|
function readEnvKey(root) {
|
|
@@ -10839,29 +11069,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
10839
11069
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
10840
11070
|
const envState = upsertEnvLocal(root, out.token);
|
|
10841
11071
|
ensureGitignored(root);
|
|
10842
|
-
console.log(` ${
|
|
10843
|
-
console.log(` ${
|
|
11072
|
+
console.log(` ${import_chalk51.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
11073
|
+
console.log(` ${import_chalk51.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
10844
11074
|
installSidecarPackage(root);
|
|
10845
11075
|
let inspectorPath = null;
|
|
10846
11076
|
if (!opts.noInspector) {
|
|
10847
11077
|
inspectorPath = generateInspector(root, router);
|
|
10848
|
-
if (inspectorPath) console.log(` ${
|
|
11078
|
+
if (inspectorPath) console.log(` ${import_chalk51.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
10849
11079
|
}
|
|
10850
11080
|
console.log("");
|
|
10851
|
-
console.log(
|
|
10852
|
-
console.log(` 1. ${
|
|
11081
|
+
console.log(import_chalk51.default.bold("Done (no account needed). What happens next:"));
|
|
11082
|
+
console.log(` 1. ${import_chalk51.default.cyan("npm run dev")} and use your app.`);
|
|
10853
11083
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
10854
|
-
console.log(` ${
|
|
11084
|
+
console.log(` ${import_chalk51.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
10855
11085
|
console.log("");
|
|
10856
|
-
console.log(
|
|
10857
|
-
console.log(` ${
|
|
10858
|
-
console.log(
|
|
11086
|
+
console.log(import_chalk51.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
11087
|
+
console.log(` ${import_chalk51.default.cyan("apiblaze login")} then ${import_chalk51.default.cyan("apiblaze claim")} ${import_chalk51.default.dim("(no code needed here)")}`);
|
|
11088
|
+
console.log(import_chalk51.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
10859
11089
|
}
|
|
10860
11090
|
async function runSidecar(opts) {
|
|
10861
11091
|
const root = path8.resolve(opts.dir ?? process.cwd());
|
|
10862
11092
|
const detected2 = detectNextProject(root);
|
|
10863
11093
|
if (!detected2.found) {
|
|
10864
|
-
console.log(
|
|
11094
|
+
console.log(import_chalk51.default.yellow(`No Next.js project detected in ${root}.`));
|
|
10865
11095
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
10866
11096
|
return;
|
|
10867
11097
|
}
|
|
@@ -10872,10 +11102,10 @@ async function runSidecar(opts) {
|
|
|
10872
11102
|
if (!loadCredentials()) {
|
|
10873
11103
|
upsertEnvLocal(root, readEnvKey(root));
|
|
10874
11104
|
ensureGitignored(root);
|
|
10875
|
-
console.log(` ${
|
|
10876
|
-
console.log(` ${
|
|
11105
|
+
console.log(` ${import_chalk51.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
11106
|
+
console.log(` ${import_chalk51.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
10877
11107
|
installSidecarPackage(root);
|
|
10878
|
-
console.log(
|
|
11108
|
+
console.log(import_chalk51.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
10879
11109
|
return;
|
|
10880
11110
|
}
|
|
10881
11111
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -10898,38 +11128,38 @@ async function runSidecar(opts) {
|
|
|
10898
11128
|
throw err;
|
|
10899
11129
|
}
|
|
10900
11130
|
} else {
|
|
10901
|
-
console.log(
|
|
11131
|
+
console.log(import_chalk51.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
10902
11132
|
}
|
|
10903
11133
|
const envState = upsertEnvLocal(root, token);
|
|
10904
11134
|
ensureGitignored(root);
|
|
10905
|
-
console.log(` ${
|
|
11135
|
+
console.log(` ${import_chalk51.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
10906
11136
|
const wireState = wireInstrumentation(root);
|
|
10907
|
-
console.log(` ${
|
|
11137
|
+
console.log(` ${import_chalk51.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
10908
11138
|
installSidecarPackage(root);
|
|
10909
11139
|
let inspectorPath = null;
|
|
10910
11140
|
if (!opts.noInspector) {
|
|
10911
11141
|
inspectorPath = generateInspector(root, detected2.router);
|
|
10912
|
-
if (inspectorPath) console.log(` ${
|
|
11142
|
+
if (inspectorPath) console.log(` ${import_chalk51.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
10913
11143
|
}
|
|
10914
11144
|
console.log("");
|
|
10915
|
-
console.log(
|
|
10916
|
-
console.log(` 1. ${
|
|
10917
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
10918
|
-
console.log(` 3. Approve the ones to route: ${
|
|
11145
|
+
console.log(import_chalk51.default.bold("Done. What happens next:"));
|
|
11146
|
+
console.log(` 1. ${import_chalk51.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
11147
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk51.default.bold("candidates")} \u2014 list them: ${import_chalk51.default.cyan("apiblaze sidecar")}`);
|
|
11148
|
+
console.log(` 3. Approve the ones to route: ${import_chalk51.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
10919
11149
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
10920
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
10921
|
-
if (switchingTeam) console.log(
|
|
11150
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk51.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path8.dirname(inspectorPath)} before shipping)`);
|
|
11151
|
+
if (switchingTeam) console.log(import_chalk51.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
10922
11152
|
console.log("");
|
|
10923
|
-
console.log(
|
|
10924
|
-
console.log(
|
|
10925
|
-
console.log(
|
|
11153
|
+
console.log(import_chalk51.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
11154
|
+
console.log(import_chalk51.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
11155
|
+
console.log(import_chalk51.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
10926
11156
|
console.log("");
|
|
10927
|
-
console.log(
|
|
10928
|
-
console.log(
|
|
11157
|
+
console.log(import_chalk51.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
11158
|
+
console.log(import_chalk51.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
10929
11159
|
}
|
|
10930
11160
|
|
|
10931
11161
|
// src/commands/origins.ts
|
|
10932
|
-
var
|
|
11162
|
+
var import_chalk52 = __toESM(require("chalk"));
|
|
10933
11163
|
var import_ora27 = __toESM(require("ora"));
|
|
10934
11164
|
init_admin();
|
|
10935
11165
|
init_resolve();
|
|
@@ -10940,7 +11170,7 @@ async function runOriginsList(opts) {
|
|
|
10940
11170
|
if (!loadCredentials()) {
|
|
10941
11171
|
const cred = loadAnonCred();
|
|
10942
11172
|
if (!cred) {
|
|
10943
|
-
console.log(
|
|
11173
|
+
console.log(import_chalk52.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
10944
11174
|
return;
|
|
10945
11175
|
}
|
|
10946
11176
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -10958,27 +11188,27 @@ async function runOriginsList(opts) {
|
|
|
10958
11188
|
}
|
|
10959
11189
|
const routed = out.routed ?? [];
|
|
10960
11190
|
const candidates = out.candidates ?? [];
|
|
10961
|
-
console.log(
|
|
11191
|
+
console.log(import_chalk52.default.bold(`
|
|
10962
11192
|
Routed through APIblaze (${routed.length})`));
|
|
10963
|
-
if (!routed.length) console.log(
|
|
10964
|
-
for (const r of routed) console.log(` ${
|
|
10965
|
-
console.log(
|
|
11193
|
+
if (!routed.length) console.log(import_chalk52.default.dim(" none yet"));
|
|
11194
|
+
for (const r of routed) console.log(` ${import_chalk52.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk52.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
11195
|
+
console.log(import_chalk52.default.bold(`
|
|
10966
11196
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
10967
|
-
if (!candidates.length) console.log(
|
|
11197
|
+
if (!candidates.length) console.log(import_chalk52.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
10968
11198
|
for (const c of candidates) {
|
|
10969
|
-
console.log(` ${
|
|
11199
|
+
console.log(` ${import_chalk52.default.yellow("\u25CB")} ${c.origin} ${import_chalk52.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
10970
11200
|
}
|
|
10971
11201
|
if (candidates.length) {
|
|
10972
|
-
console.log(
|
|
11202
|
+
console.log(import_chalk52.default.dim(`
|
|
10973
11203
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
10974
|
-
console.log(
|
|
11204
|
+
console.log(import_chalk52.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
10975
11205
|
}
|
|
10976
11206
|
}
|
|
10977
11207
|
async function runOriginsApprove(origin, opts) {
|
|
10978
11208
|
if (!loadCredentials()) {
|
|
10979
11209
|
const cred = loadAnonCred();
|
|
10980
11210
|
if (!cred) {
|
|
10981
|
-
console.error(
|
|
11211
|
+
console.error(import_chalk52.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
10982
11212
|
process.exit(1);
|
|
10983
11213
|
}
|
|
10984
11214
|
const spinner2 = (0, import_ora27.default)(`Approving ${origin} (anonymous)...`).start();
|
|
@@ -11031,13 +11261,13 @@ async function runOriginsRemove(origin, opts) {
|
|
|
11031
11261
|
}
|
|
11032
11262
|
|
|
11033
11263
|
// src/commands/op.ts
|
|
11034
|
-
var
|
|
11264
|
+
var import_chalk54 = __toESM(require("chalk"));
|
|
11035
11265
|
init_auth();
|
|
11036
11266
|
init_trace();
|
|
11037
11267
|
init_types();
|
|
11038
11268
|
|
|
11039
11269
|
// src/commands/op-billing.ts
|
|
11040
|
-
var
|
|
11270
|
+
var import_chalk53 = __toESM(require("chalk"));
|
|
11041
11271
|
init_admin();
|
|
11042
11272
|
var SANDBOX = {
|
|
11043
11273
|
teamId: "team_1782844865835_zujrf",
|
|
@@ -11096,7 +11326,7 @@ async function rowsForRays(project, version2, tenant2, rays) {
|
|
|
11096
11326
|
}
|
|
11097
11327
|
function printDoors(data) {
|
|
11098
11328
|
const checks = [];
|
|
11099
|
-
console.log(
|
|
11329
|
+
console.log(import_chalk53.default.bold("\n Doors \u2014 is every way in metered?\n"));
|
|
11100
11330
|
const doors = data?.doors ?? [];
|
|
11101
11331
|
const metered = doors.filter((d) => d.verdict === "metered");
|
|
11102
11332
|
const allowed = doors.filter((d) => d.verdict === "allowed-free");
|
|
@@ -11105,29 +11335,29 @@ function printDoors(data) {
|
|
|
11105
11335
|
const errs = data?.errors ?? [];
|
|
11106
11336
|
const routeAuditBroke = errs.some((e) => e.startsWith("zone "));
|
|
11107
11337
|
const devAuditBroke = errs.some((e) => e.startsWith("workers.dev audit"));
|
|
11108
|
-
console.log(
|
|
11109
|
-
for (const d of metered) console.log(
|
|
11110
|
-
console.log(
|
|
11338
|
+
console.log(import_chalk53.default.dim(` ${metered.length} route(s) behind main-proxy (metered)`));
|
|
11339
|
+
for (const d of metered) console.log(import_chalk53.default.green(` \u2713 ${d.pattern}`));
|
|
11340
|
+
console.log(import_chalk53.default.dim(`
|
|
11111
11341
|
${allowed.length} route(s) free ON PURPOSE`));
|
|
11112
11342
|
for (const d of allowed) {
|
|
11113
|
-
console.log(
|
|
11114
|
-
console.log(
|
|
11343
|
+
console.log(import_chalk53.default.cyan(` \u2022 ${d.pattern}`) + import_chalk53.default.dim(` \u2192 ${d.script}`));
|
|
11344
|
+
console.log(import_chalk53.default.dim(` ${d.why}`));
|
|
11115
11345
|
}
|
|
11116
11346
|
if (known.length) {
|
|
11117
|
-
console.log(
|
|
11347
|
+
console.log(import_chalk53.default.yellow(`
|
|
11118
11348
|
${known.length} route(s) KNOWN OPEN \u2014 unmetered, not yet closed`));
|
|
11119
11349
|
for (const d of known) {
|
|
11120
|
-
console.log(
|
|
11121
|
-
console.log(
|
|
11350
|
+
console.log(import_chalk53.default.yellow(` ! ${d.pattern}`) + import_chalk53.default.dim(` \u2192 ${d.script}`));
|
|
11351
|
+
console.log(import_chalk53.default.dim(` ${d.why}`));
|
|
11122
11352
|
}
|
|
11123
11353
|
checks.push({ name: "no known-open doors", status: "KNOWN", detail: `${known.length} unmetered route(s) still open \u2014 see above` });
|
|
11124
11354
|
}
|
|
11125
11355
|
if (stray.length) {
|
|
11126
|
-
console.log(
|
|
11356
|
+
console.log(import_chalk53.default.red(`
|
|
11127
11357
|
${stray.length} STRAY route(s) \u2014 not main-proxy, not on the allowlist`));
|
|
11128
11358
|
for (const d of stray) {
|
|
11129
|
-
console.log(
|
|
11130
|
-
console.log(
|
|
11359
|
+
console.log(import_chalk53.default.red(` \u2717 ${d.pattern}`) + import_chalk53.default.dim(` \u2192 ${d.script}`));
|
|
11360
|
+
console.log(import_chalk53.default.dim(` ${d.why}`));
|
|
11131
11361
|
}
|
|
11132
11362
|
checks.push({ name: "no stray routes", status: "FAIL", detail: `${stray.length}: ${stray.map((s) => s.pattern).join(", ")}` });
|
|
11133
11363
|
} else if (routeAuditBroke) {
|
|
@@ -11138,22 +11368,22 @@ function printDoors(data) {
|
|
|
11138
11368
|
const wd = data?.workers_dev ?? {};
|
|
11139
11369
|
const open = wd.enabled ?? [];
|
|
11140
11370
|
if (open.length) {
|
|
11141
|
-
console.log(
|
|
11371
|
+
console.log(import_chalk53.default.red(`
|
|
11142
11372
|
${open.length} of ${wd.total} worker(s) reachable on *.workers.dev`));
|
|
11143
|
-
for (const s of open) console.log(
|
|
11144
|
-
console.log(
|
|
11373
|
+
for (const s of open) console.log(import_chalk53.default.red(` \u2717 ${s.script}.workers.dev`) + import_chalk53.default.dim(` (enabled=${s.enabled} previews=${s.previews})`));
|
|
11374
|
+
console.log(import_chalk53.default.dim(" A workers.dev hostname bypasses every CF route, WAF rule and the credit gate."));
|
|
11145
11375
|
checks.push({ name: "no workers.dev doors", status: "FAIL", detail: `${open.length} script(s) publicly reachable: ${open.map((s) => s.script).join(", ")}` });
|
|
11146
11376
|
} else if (devAuditBroke || !wd.total) {
|
|
11147
11377
|
checks.push({ name: "no workers.dev doors", status: "SKIP", detail: "script enumeration failed \u2014 NOT a pass, no subdomain was ever read. Needs a CF token with Workers Scripts:Read." });
|
|
11148
11378
|
} else {
|
|
11149
|
-
console.log(
|
|
11379
|
+
console.log(import_chalk53.default.green(`
|
|
11150
11380
|
\u2713 0 of ${wd.total} workers reachable on *.workers.dev`));
|
|
11151
11381
|
checks.push({ name: "no workers.dev doors", status: "PASS", detail: `all ${wd.total} scripts have workers.dev + previews disabled` });
|
|
11152
11382
|
}
|
|
11153
|
-
if (data?.how_to_fix) console.log(
|
|
11154
|
-
${data.reason}`) +
|
|
11383
|
+
if (data?.how_to_fix) console.log(import_chalk53.default.yellow(`
|
|
11384
|
+
${data.reason}`) + import_chalk53.default.dim(`
|
|
11155
11385
|
${data.how_to_fix}`));
|
|
11156
|
-
else for (const e of errs) console.log(
|
|
11386
|
+
else for (const e of errs) console.log(import_chalk53.default.red(`
|
|
11157
11387
|
audit error: ${e}`));
|
|
11158
11388
|
if (errs.length) {
|
|
11159
11389
|
checks.push({ name: "audit completeness", status: "FAIL", detail: `${errs.length} part(s) of the audit could not run \u2014 coverage is INCOMPLETE, and the checks they would have covered are SKIP above` });
|
|
@@ -11163,9 +11393,9 @@ function printDoors(data) {
|
|
|
11163
11393
|
async function runMeter(readLedger, opts) {
|
|
11164
11394
|
const checks = [];
|
|
11165
11395
|
const N = Math.max(1, Math.min(10, opts.count ?? 3));
|
|
11166
|
-
console.log(
|
|
11396
|
+
console.log(import_chalk53.default.bold("\n Meter \u2014 is 1 request charged exactly 1 request?\n"));
|
|
11167
11397
|
const snap = await readLedger();
|
|
11168
|
-
console.log(
|
|
11398
|
+
console.log(import_chalk53.default.dim(` wallet ${snap.billing_account_id} \xB7 band count ${snap.band_count} \xB7 next request ${snap.next_request_cents}\xA2
|
|
11169
11399
|
`));
|
|
11170
11400
|
const url = `https://${dpHost}/${SANDBOX.version}/${SANDBOX.environment}/`;
|
|
11171
11401
|
const headers = opts.key ? { "X-API-Key": opts.key } : {};
|
|
@@ -11247,22 +11477,22 @@ async function runMeter(readLedger, opts) {
|
|
|
11247
11477
|
return checks;
|
|
11248
11478
|
}
|
|
11249
11479
|
function printChecks(checks) {
|
|
11250
|
-
console.log(
|
|
11251
|
-
const mark = { PASS:
|
|
11480
|
+
console.log(import_chalk53.default.bold("\n Results\n"));
|
|
11481
|
+
const mark = { PASS: import_chalk53.default.green(" PASS"), FAIL: import_chalk53.default.red(" FAIL"), SKIP: import_chalk53.default.dim(" SKIP"), KNOWN: import_chalk53.default.yellow(" KNOWN") };
|
|
11252
11482
|
for (const ch of checks) {
|
|
11253
|
-
console.log(` ${mark[ch.status]} ${
|
|
11254
|
-
console.log(
|
|
11483
|
+
console.log(` ${mark[ch.status]} ${import_chalk53.default.bold(ch.name)}`);
|
|
11484
|
+
console.log(import_chalk53.default.dim(` ${ch.detail}`));
|
|
11255
11485
|
}
|
|
11256
11486
|
const fails = checks.filter((c) => c.status === "FAIL").length;
|
|
11257
11487
|
const skips = checks.filter((c) => c.status === "SKIP").length;
|
|
11258
11488
|
const known = checks.filter((c) => c.status === "KNOWN").length;
|
|
11259
11489
|
console.log("");
|
|
11260
11490
|
const passes = checks.filter((c) => c.status === "PASS").length;
|
|
11261
|
-
if (fails) console.log(
|
|
11262
|
-
else if (passes) console.log(
|
|
11263
|
-
else console.log(
|
|
11264
|
-
if (known) console.log(
|
|
11265
|
-
if (skips) console.log(
|
|
11491
|
+
if (fails) console.log(import_chalk53.default.red(` ${fails} check(s) FAILED.`));
|
|
11492
|
+
else if (passes) console.log(import_chalk53.default.green(` ${passes} check(s) passed, 0 failed.`));
|
|
11493
|
+
else console.log(import_chalk53.default.yellow(" NOTHING WAS VERIFIED \u2014 every check was skipped."));
|
|
11494
|
+
if (known) console.log(import_chalk53.default.yellow(` ${known} known-open issue(s) still outstanding.`));
|
|
11495
|
+
if (skips) console.log(import_chalk53.default.dim(` ${skips} check(s) NOT RUN (see SKIP above) \u2014 those invariants are unverified.`));
|
|
11266
11496
|
console.log("");
|
|
11267
11497
|
}
|
|
11268
11498
|
|
|
@@ -11309,93 +11539,93 @@ var OP_COMMANDS = [
|
|
|
11309
11539
|
function renderOpCommands() {
|
|
11310
11540
|
const width = Math.max(...OP_COMMANDS.map((c) => c.cmd.length)) + 10;
|
|
11311
11541
|
const lines = OP_COMMANDS.map((c) => {
|
|
11312
|
-
const left = ` ${
|
|
11542
|
+
const left = ` ${import_chalk54.default.cyan(`apiblaze ${c.cmd}`)}`;
|
|
11313
11543
|
const pad = " ".repeat(Math.max(1, width - c.cmd.length));
|
|
11314
|
-
return `${left}${pad}${c.blurb}${c.extra ? " " +
|
|
11544
|
+
return `${left}${pad}${c.blurb}${c.extra ? " " + import_chalk54.default.dim(`(${c.extra})`) : ""}`;
|
|
11315
11545
|
});
|
|
11316
11546
|
return [
|
|
11317
|
-
|
|
11547
|
+
import_chalk54.default.bold("Operator commands"),
|
|
11318
11548
|
...lines,
|
|
11319
11549
|
"",
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11550
|
+
import_chalk54.default.dim(" Operators only. The gate is server-side (dashboard /api/cli/op checks the"),
|
|
11551
|
+
import_chalk54.default.dim(" signed-in email, admin-api re-checks with operatorGate) \u2014 a patched CLI just"),
|
|
11552
|
+
import_chalk54.default.dim(" gets 403s. Every op call is read-only except `op sweep`."),
|
|
11323
11553
|
"",
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11554
|
+
import_chalk54.default.dim(" Not a CLI command: to prune all non-CP data run scripts/nuke-but-cp.sh --apply --sweep"),
|
|
11555
|
+
import_chalk54.default.dim(" in the repo. Operator dashboards (dlq, thresholds, throttling, pricing, billing,"),
|
|
11556
|
+
import_chalk54.default.dim(" agent-spend, teams, tests, leak-detection, lifecycle) live at /operator/* in the app.")
|
|
11327
11557
|
].join("\n");
|
|
11328
11558
|
}
|
|
11329
11559
|
function printResidue(report, applied) {
|
|
11330
11560
|
const up = report?.upstash ?? {};
|
|
11331
11561
|
const fga = report?.fga ?? {};
|
|
11332
11562
|
const ghosts = report?.ghosts ?? {};
|
|
11333
|
-
console.log(
|
|
11334
|
-
console.log(
|
|
11563
|
+
console.log(import_chalk54.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
11564
|
+
console.log(import_chalk54.default.bold("\n Upstash"));
|
|
11335
11565
|
const orphans = up.orphans ?? [];
|
|
11336
|
-
if (orphans.length === 0) console.log(
|
|
11337
|
-
for (const o of orphans) console.log(` ${
|
|
11338
|
-
console.log(
|
|
11566
|
+
if (orphans.length === 0) console.log(import_chalk54.default.green(" no orphaned keys"));
|
|
11567
|
+
for (const o of orphans) console.log(` ${import_chalk54.default.yellow(o.key)} ${import_chalk54.default.dim(`\u2014 ${o.reason}`)}`);
|
|
11568
|
+
console.log(import_chalk54.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
11339
11569
|
if (up.anon_wallet_detail) {
|
|
11340
11570
|
const d = up.anon_wallet_detail;
|
|
11341
|
-
console.log(
|
|
11571
|
+
console.log(import_chalk54.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)"}`));
|
|
11342
11572
|
}
|
|
11343
11573
|
if (up.keyspace_census) {
|
|
11344
11574
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
11345
|
-
console.log(
|
|
11575
|
+
console.log(import_chalk54.default.dim(` keyspace: ${census}`));
|
|
11346
11576
|
}
|
|
11347
|
-
if (up.unknown?.length) console.log(
|
|
11348
|
-
if (applied) console.log(` ${
|
|
11349
|
-
for (const e of up.errors ?? []) console.log(
|
|
11350
|
-
console.log(
|
|
11577
|
+
if (up.unknown?.length) console.log(import_chalk54.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
11578
|
+
if (applied) console.log(` ${import_chalk54.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
11579
|
+
for (const e of up.errors ?? []) console.log(import_chalk54.default.red(` error: ${e}`));
|
|
11580
|
+
console.log(import_chalk54.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
11351
11581
|
if (applied) {
|
|
11352
11582
|
const swept = fga?.swept ?? [];
|
|
11353
|
-
if (swept.length === 0) console.log(
|
|
11583
|
+
if (swept.length === 0) console.log(import_chalk54.default.green(" no orphaned stores"));
|
|
11354
11584
|
for (const s of swept) {
|
|
11355
11585
|
console.log(
|
|
11356
|
-
` ${
|
|
11586
|
+
` ${import_chalk54.default.yellow(s.store_id)} ${import_chalk54.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
11357
11587
|
);
|
|
11358
11588
|
}
|
|
11359
|
-
if (fga?.remaining) console.log(
|
|
11589
|
+
if (fga?.remaining) console.log(import_chalk54.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
11360
11590
|
const st = fga?.side_tables;
|
|
11361
|
-
if (st) console.log(
|
|
11591
|
+
if (st) console.log(import_chalk54.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})` : ""}`));
|
|
11362
11592
|
} else {
|
|
11363
11593
|
const fgaOrphans = fga?.orphans ?? [];
|
|
11364
|
-
if (fgaOrphans.length === 0) console.log(
|
|
11594
|
+
if (fgaOrphans.length === 0) console.log(import_chalk54.default.green(" no orphaned stores"));
|
|
11365
11595
|
for (const s of fgaOrphans) {
|
|
11366
11596
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
11367
|
-
console.log(` ${
|
|
11597
|
+
console.log(` ${import_chalk54.default.yellow(s.store_id)} ${import_chalk54.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
11368
11598
|
}
|
|
11369
|
-
console.log(
|
|
11599
|
+
console.log(import_chalk54.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
11370
11600
|
const st = fga?.side_tables;
|
|
11371
|
-
if (st) console.log(
|
|
11601
|
+
if (st) console.log(import_chalk54.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`));
|
|
11372
11602
|
}
|
|
11373
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
11374
|
-
console.log(
|
|
11603
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk54.default.red(` error: ${e}`));
|
|
11604
|
+
console.log(import_chalk54.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
11375
11605
|
if (applied) {
|
|
11376
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
11377
|
-
else console.log(` ${
|
|
11606
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk54.default.green(" no ghost tuples"));
|
|
11607
|
+
else console.log(` ${import_chalk54.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk54.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
11378
11608
|
} else {
|
|
11379
11609
|
const n = ghosts?.ghost_count ?? 0;
|
|
11380
|
-
if (n === 0) console.log(
|
|
11610
|
+
if (n === 0) console.log(import_chalk54.default.green(` no ghost tuples ${import_chalk54.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
11381
11611
|
else {
|
|
11382
|
-
console.log(
|
|
11612
|
+
console.log(import_chalk54.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
11383
11613
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
11384
|
-
console.log(
|
|
11614
|
+
console.log(import_chalk54.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
11385
11615
|
}
|
|
11386
|
-
if (n > 20) console.log(
|
|
11616
|
+
if (n > 20) console.log(import_chalk54.default.dim(` \u2026 and ${n - 20} more`));
|
|
11387
11617
|
}
|
|
11388
11618
|
}
|
|
11389
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
11619
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk54.default.red(` error: ${e}`));
|
|
11390
11620
|
console.log();
|
|
11391
11621
|
}
|
|
11392
11622
|
async function runOp(sub, opts = {}, view) {
|
|
11393
11623
|
if (!loadCredentials()) {
|
|
11394
|
-
console.log(
|
|
11624
|
+
console.log(import_chalk54.default.dim("Not logged in. Run `apiblaze login`."));
|
|
11395
11625
|
return;
|
|
11396
11626
|
}
|
|
11397
11627
|
if (!isOperatorLogin()) {
|
|
11398
|
-
console.log(
|
|
11628
|
+
console.log(import_chalk54.default.dim("`apiblaze op` is only available to platform operators."));
|
|
11399
11629
|
return;
|
|
11400
11630
|
}
|
|
11401
11631
|
switch (sub) {
|
|
@@ -11421,17 +11651,17 @@ async function runOp(sub, opts = {}, view) {
|
|
|
11421
11651
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
11422
11652
|
printResidue(report, false);
|
|
11423
11653
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
11424
|
-
console.log(
|
|
11654
|
+
console.log(import_chalk54.default.green("Nothing to sweep."));
|
|
11425
11655
|
return;
|
|
11426
11656
|
}
|
|
11427
11657
|
if (!opts.yes) {
|
|
11428
11658
|
const readline3 = await import("readline/promises");
|
|
11429
11659
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
11430
11660
|
const answer = await rl.question(
|
|
11431
|
-
|
|
11661
|
+
import_chalk54.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: `)
|
|
11432
11662
|
);
|
|
11433
11663
|
rl.close();
|
|
11434
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
11664
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk54.default.dim("Aborted."));
|
|
11435
11665
|
}
|
|
11436
11666
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
11437
11667
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -11441,25 +11671,25 @@ async function runOp(sub, opts = {}, view) {
|
|
|
11441
11671
|
case "mark": {
|
|
11442
11672
|
const label3 = (view ?? "").trim();
|
|
11443
11673
|
if (!label3) {
|
|
11444
|
-
console.log(
|
|
11674
|
+
console.log(import_chalk54.default.red("Give the change a name:") + import_chalk54.default.cyan(' apiblaze op mark "cached tenant count"'));
|
|
11445
11675
|
return;
|
|
11446
11676
|
}
|
|
11447
11677
|
const res = await opCall({ method: "POST", path: "/operator/latency/mark", body: { label: label3 }, summary: "record change marker" });
|
|
11448
11678
|
const ts = new Date(res?.marker?.ts ?? Date.now()).toISOString();
|
|
11449
|
-
console.log(
|
|
11450
|
-
Marked: `) +
|
|
11451
|
-
console.log(
|
|
11452
|
-
console.log(
|
|
11679
|
+
console.log(import_chalk54.default.green(`
|
|
11680
|
+
Marked: `) + import_chalk54.default.bold(label3));
|
|
11681
|
+
console.log(import_chalk54.default.dim(` ${ts}`));
|
|
11682
|
+
console.log(import_chalk54.default.dim(` Once traffic has run on both sides, compare with: `) + import_chalk54.default.cyan("apiblaze op latency compare") + "\n");
|
|
11453
11683
|
return;
|
|
11454
11684
|
}
|
|
11455
11685
|
case "credits": {
|
|
11456
11686
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
11457
11687
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
11458
11688
|
const accounts = data?.accounts ?? [];
|
|
11459
|
-
if (accounts.length === 0) return void console.log(
|
|
11689
|
+
if (accounts.length === 0) return void console.log(import_chalk54.default.dim("No credit wallets."));
|
|
11460
11690
|
for (const a of accounts) {
|
|
11461
11691
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
11462
|
-
console.log(` ${
|
|
11692
|
+
console.log(` ${import_chalk54.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk54.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk54.default.dim(" \u2014 anon") : ""}`);
|
|
11463
11693
|
}
|
|
11464
11694
|
return;
|
|
11465
11695
|
}
|
|
@@ -11470,7 +11700,7 @@ async function runOp(sub, opts = {}, view) {
|
|
|
11470
11700
|
case "billing": {
|
|
11471
11701
|
const which = (view ?? "").trim().toLowerCase();
|
|
11472
11702
|
if (which && which !== "doors" && which !== "meter") {
|
|
11473
|
-
return void console.log(
|
|
11703
|
+
return void console.log(import_chalk54.default.red(`Unknown: apiblaze op billing ${which}. Use 'doors', 'meter', or neither for both.`));
|
|
11474
11704
|
}
|
|
11475
11705
|
const checks = [];
|
|
11476
11706
|
let doorsData = null;
|
|
@@ -11531,29 +11761,29 @@ async function runOp(sub, opts = {}, view) {
|
|
|
11531
11761
|
const data = await opCall({ method: "GET", path: `/operator/latency/grades${q}`, summary: "latency grades" });
|
|
11532
11762
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
11533
11763
|
const t = data.thresholds_ms;
|
|
11534
|
-
console.log(
|
|
11535
|
-
console.log(
|
|
11764
|
+
console.log(import_chalk54.default.bold("\nHow good was apiblaze itself?") + import_chalk54.default.dim(" (our overhead only \u2014 a slow customer API never counts against us)"));
|
|
11765
|
+
console.log(import_chalk54.default.dim(` excellent <${t.excellent.replace("<", "")}ms \xB7 okay ${t.okay}ms \xB7 bad ${t.bad}ms \xB7 terrible ${t.terrible.replace(">=", "")}ms+
|
|
11536
11766
|
`));
|
|
11537
|
-
console.log(
|
|
11767
|
+
console.log(import_chalk54.default.dim(" date reqs excellent okay bad terrible"));
|
|
11538
11768
|
for (const d of data.days ?? []) {
|
|
11539
11769
|
const p = d.pct;
|
|
11540
|
-
const cell = (v, colour) => v > 0 ? colour(`${String(v).padStart(5)}%`) :
|
|
11770
|
+
const cell = (v, colour) => v > 0 ? colour(`${String(v).padStart(5)}%`) : import_chalk54.default.dim(`${String(v).padStart(5)}%`);
|
|
11541
11771
|
console.log(
|
|
11542
|
-
` ${d.date} ${String(d.total).padStart(5)} ${cell(p.excellent,
|
|
11772
|
+
` ${d.date} ${String(d.total).padStart(5)} ${cell(p.excellent, import_chalk54.default.green)} ${cell(p.okay, import_chalk54.default.cyan)} ${cell(p.bad, import_chalk54.default.yellow)} ${cell(p.terrible, import_chalk54.default.red)}`
|
|
11543
11773
|
);
|
|
11544
11774
|
}
|
|
11545
11775
|
const cul = data.culprits ?? [];
|
|
11546
11776
|
if (cul.length) {
|
|
11547
|
-
console.log(
|
|
11548
|
-
console.log(
|
|
11777
|
+
console.log(import_chalk54.default.bold("\n Who caused the bad and terrible ones\n"));
|
|
11778
|
+
console.log(import_chalk54.default.dim(" bad terrible feature \u2192 dependency"));
|
|
11549
11779
|
for (const r of cul.slice(0, 12)) {
|
|
11550
11780
|
if (!r.bad && !r.terrible) continue;
|
|
11551
11781
|
console.log(
|
|
11552
|
-
` ${String(r.bad).padStart(6)} ${
|
|
11782
|
+
` ${String(r.bad).padStart(6)} ${import_chalk54.default.red(String(r.terrible).padStart(8))} ${import_chalk54.default.yellow(r.feature)} ${import_chalk54.default.dim("\u2192")} ${import_chalk54.default.cyan(r.dep)}`
|
|
11553
11783
|
);
|
|
11554
11784
|
}
|
|
11555
11785
|
}
|
|
11556
|
-
if (data.caveat) console.log(
|
|
11786
|
+
if (data.caveat) console.log(import_chalk54.default.dim(`
|
|
11557
11787
|
\u26A0 ${data.caveat}
|
|
11558
11788
|
`));
|
|
11559
11789
|
return;
|
|
@@ -11562,24 +11792,24 @@ async function runOp(sub, opts = {}, view) {
|
|
|
11562
11792
|
const data = await opCall({ method: "GET", path: `/operator/latency/compare${q}`, summary: "latency before/after" });
|
|
11563
11793
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
11564
11794
|
const b = data.before, a = data.after, d = data.delta;
|
|
11565
|
-
console.log(
|
|
11566
|
-
Before vs after: `) +
|
|
11567
|
-
console.log(
|
|
11795
|
+
console.log(import_chalk54.default.bold(`
|
|
11796
|
+
Before vs after: `) + import_chalk54.default.cyan(data.marker.label));
|
|
11797
|
+
console.log(import_chalk54.default.dim(` marked ${new Date(data.marker.ts).toISOString()} \xB7 ${data.window_hours}h either side
|
|
11568
11798
|
`));
|
|
11569
11799
|
const row = (name, before, after, delta) => {
|
|
11570
11800
|
const arrow = delta === 0 ? "=" : delta < 0 ? "\u2193" : "\u2191";
|
|
11571
11801
|
const txt = `${String(before).padStart(6)}ms \u2192${String(after).padStart(7)}ms ${arrow}${Math.abs(delta)}ms`;
|
|
11572
|
-
console.log(` ${name.padEnd(22)}${data.trustworthy ? delta <= 0 ?
|
|
11802
|
+
console.log(` ${name.padEnd(22)}${data.trustworthy ? delta <= 0 ? import_chalk54.default.green(txt) : import_chalk54.default.red(txt) : import_chalk54.default.dim(txt)}`);
|
|
11573
11803
|
};
|
|
11574
|
-
console.log(
|
|
11804
|
+
console.log(import_chalk54.default.dim(" metric before after change"));
|
|
11575
11805
|
row("total p50", b.total_p50, a.total_p50, d.total_p50);
|
|
11576
11806
|
row("total p95", b.total_p95, a.total_p95, d.total_p95);
|
|
11577
11807
|
row("apiblaze overhead p50", b.gw_p50, a.gw_p50, d.gw_p50);
|
|
11578
11808
|
row("apiblaze overhead p95", b.gw_p95, a.gw_p95, d.gw_p95);
|
|
11579
|
-
console.log(
|
|
11809
|
+
console.log(import_chalk54.default.dim(`
|
|
11580
11810
|
requests: ${b.requests} before \xB7 ${a.requests} after`));
|
|
11581
11811
|
for (const w of data.warnings ?? []) {
|
|
11582
|
-
console.log((data.trustworthy ?
|
|
11812
|
+
console.log((data.trustworthy ? import_chalk54.default.dim : import_chalk54.default.yellow)(` ${data.trustworthy ? "\xB7" : "\u26A0"} ${w}`));
|
|
11583
11813
|
}
|
|
11584
11814
|
console.log("");
|
|
11585
11815
|
return;
|
|
@@ -11588,19 +11818,19 @@ Before vs after: `) + import_chalk53.default.cyan(data.marker.label));
|
|
|
11588
11818
|
const data = await opCall({ method: "GET", path: `/operator/latency/slow${q}`, summary: "slowest requests" });
|
|
11589
11819
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
11590
11820
|
const rows2 = data?.rows ?? [];
|
|
11591
|
-
if (!rows2.length) return void console.log(
|
|
11592
|
-
console.log(
|
|
11821
|
+
if (!rows2.length) return void console.log(import_chalk54.default.dim("No requests over the threshold in that window."));
|
|
11822
|
+
console.log(import_chalk54.default.bold(`
|
|
11593
11823
|
Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
|
|
11594
11824
|
`));
|
|
11595
|
-
console.log(
|
|
11825
|
+
console.log(import_chalk54.default.dim(" total ours theirs blame request id"));
|
|
11596
11826
|
for (const r of rows2.slice(0, 30)) {
|
|
11597
11827
|
console.log(
|
|
11598
|
-
` ${String(Math.round(r.duration_ms)).padStart(6)} ${String(Math.round(r.gateway_ms)).padStart(5)} ${String(Math.round(r.upstream_ttfb_ms)).padStart(6)} ${
|
|
11828
|
+
` ${String(Math.round(r.duration_ms)).padStart(6)} ${String(Math.round(r.gateway_ms)).padStart(5)} ${String(Math.round(r.upstream_ttfb_ms)).padStart(6)} ${import_chalk54.default.yellow(`${r.slow_gw_feature || "-"}\u2192${r.slow_dep || "-"}`.padEnd(20))} ${import_chalk54.default.dim(r.request_id || "")}`
|
|
11599
11829
|
);
|
|
11600
11830
|
}
|
|
11601
|
-
console.log(
|
|
11831
|
+
console.log(import_chalk54.default.dim(`
|
|
11602
11832
|
The last column is the request id (Cloudflare calls it a "cf-ray"). Look one up with`));
|
|
11603
|
-
console.log(
|
|
11833
|
+
console.log(import_chalk54.default.dim(` \`apiblaze logs\` for that request's exact per-feature breakdown \u2014 unsampled, unlike the table above.
|
|
11604
11834
|
`));
|
|
11605
11835
|
return;
|
|
11606
11836
|
}
|
|
@@ -11608,18 +11838,18 @@ Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
|
|
|
11608
11838
|
const data = await opCall({ method: "GET", path: `/operator/latency/llm${q}`, summary: "llm latency" });
|
|
11609
11839
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
11610
11840
|
const rows2 = data?.rows ?? [];
|
|
11611
|
-
if (!rows2.length) return void console.log(
|
|
11612
|
-
console.log(
|
|
11841
|
+
if (!rows2.length) return void console.log(import_chalk54.default.dim("No LLM traffic in that window."));
|
|
11842
|
+
console.log(import_chalk54.default.bold(`
|
|
11613
11843
|
LLM timing \u2014 last ${data.window_hours}h
|
|
11614
11844
|
`));
|
|
11615
|
-
console.log(
|
|
11845
|
+
console.log(import_chalk54.default.dim(" turns turn p95 ttfc p95 gen p95 reserve p95 in/out tokens p95 model"));
|
|
11616
11846
|
for (const r of rows2) {
|
|
11617
11847
|
const n = r.turns ?? r.requests ?? 0;
|
|
11618
11848
|
console.log(
|
|
11619
11849
|
` ${String(Math.round(n)).padStart(8)} ${String(Math.round(r.turn_p95_ms ?? 0)).padStart(6)}ms ${String(Math.round(r.ttfc_p95_ms ?? 0)).padStart(6)}ms ${String(Math.round(r.gen_p95_ms ?? 0)).padStart(6)}ms ${String(Math.round(r.reserve_p95_ms ?? 0)).padStart(9)}ms ${String(Math.round(r.input_tokens_p95 ?? 0)).padStart(6)}/${String(Math.round(r.output_tokens_p95 ?? 0)).padEnd(6)} ${r.model || "-"}`
|
|
11620
11850
|
);
|
|
11621
11851
|
}
|
|
11622
|
-
console.log(
|
|
11852
|
+
console.log(import_chalk54.default.dim(`
|
|
11623
11853
|
${data.note}
|
|
11624
11854
|
`));
|
|
11625
11855
|
return;
|
|
@@ -11630,26 +11860,26 @@ LLM timing \u2014 last ${data.window_hours}h
|
|
|
11630
11860
|
]);
|
|
11631
11861
|
if (opts.json) return void console.log(JSON.stringify({ blame, summary }, null, 2));
|
|
11632
11862
|
const ov = summary?.apiblaze_overhead_ms ?? {};
|
|
11633
|
-
console.log(
|
|
11863
|
+
console.log(import_chalk54.default.bold(`
|
|
11634
11864
|
Latency \u2014 last ${summary?.window_hours ?? "?"}h, ${Number(summary?.requests ?? 0).toLocaleString()} requests
|
|
11635
11865
|
`));
|
|
11636
|
-
console.log(` ${
|
|
11637
|
-
console.log(` ${
|
|
11638
|
-
console.log(
|
|
11866
|
+
console.log(` ${import_chalk54.default.bold("apiblaze overhead")} p50 ${String(ov.p50 ?? 0).padStart(5)}ms p95 ${String(ov.p95 ?? 0).padStart(6)}ms p99 ${String(ov.p99 ?? 0).padStart(6)}ms ${import_chalk54.default.dim("\u2190 ours")}`);
|
|
11867
|
+
console.log(` ${import_chalk54.default.bold("upstream ttfb ")} ${" ".repeat(24)}p95 ${String(summary?.upstream_ttfb_p95_ms ?? 0).padStart(6)}ms ${import_chalk54.default.dim("\u2190 theirs")}`);
|
|
11868
|
+
console.log(import_chalk54.default.dim(` (per-request percentiles \u2014 never subtract one from the other)
|
|
11639
11869
|
`));
|
|
11640
11870
|
const rows = blame?.blame ?? [];
|
|
11641
|
-
if (!rows.length) return void console.log(
|
|
11642
|
-
console.log(
|
|
11643
|
-
console.log(
|
|
11871
|
+
if (!rows.length) return void console.log(import_chalk54.default.dim("No latency rows in that window."));
|
|
11872
|
+
console.log(import_chalk54.default.bold(" Which feature ate the time, and what inside it\n"));
|
|
11873
|
+
console.log(import_chalk54.default.dim(" share p95 feature \u2192 dependency"));
|
|
11644
11874
|
for (const r of rows.slice(0, 15)) {
|
|
11645
11875
|
const share = `${(r.share * 100).toFixed(1)}%`;
|
|
11646
|
-
console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${
|
|
11876
|
+
console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${import_chalk54.default.yellow(r.feature)} ${import_chalk54.default.dim("\u2192")} ${import_chalk54.default.cyan(r.dep)}`);
|
|
11647
11877
|
}
|
|
11648
11878
|
console.log("");
|
|
11649
11879
|
return;
|
|
11650
11880
|
}
|
|
11651
11881
|
default:
|
|
11652
|
-
console.log(
|
|
11882
|
+
console.log(import_chalk54.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
11653
11883
|
}
|
|
11654
11884
|
}
|
|
11655
11885
|
|
|
@@ -11695,7 +11925,7 @@ agent.command("authz").description("Chat to design and turn on access rules for
|
|
|
11695
11925
|
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)));
|
|
11696
11926
|
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)));
|
|
11697
11927
|
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)));
|
|
11698
|
-
program.command("apichat [project]").description("Turn any API into a chat: point at an OpenAPI spec \u2014 or chat an EXISTING proxy by name (no login needed)").option("--target <url|file>", "What to chat with \u2014 pass ANY of: a target server base URL (spec auto-discovered at /openapi.json etc.), a local OpenAPI file (./openapi.yaml), or a remote OpenAPI URL (https://acme.com/openapi.yaml)").addOption(new import_commander.Option("--openapi <file|url>", "Deprecated alias \u2014 --target now detects spec files/URLs itself").hideHelp()).addOption(new import_commander.Option("--openapispec <file|url>", "Deprecated alias for --openapi").hideHelp()).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("--apikey <key>", "Use this API key for the proxy's door (api_key proxies). Without it, apichat detects the door and asks \u2014 or runs the consumer login for OAuth doors.").option("--xenduserid <id>", "Assert this end-user id (X-End-User-Id) \u2014 required by proxies with identified/pre-approved enforcement; you are asked for one when the proxy demands it.").option("--verbose", "Show the per-turn proxy curl trace (hidden by default)").option("-p, --prompt <question>", "One-shot question: answered through the external agent CLI after an MCP install, or by apichat itself (exits after answering when there is no TTY)").option("--install-mcp <cli>", "Install this proxy's MCP into an external agent CLI without asking: claude | codex. Also re-offers after an earlier decline.").action(action((project, opts) => runApichat({ ...opts, project, openapispec: opts.openapispec ?? opts.openapi })));
|
|
11928
|
+
program.command("apichat [project]").description("Turn any API into a chat: point at an OpenAPI spec \u2014 or chat an EXISTING proxy by name (no login needed)").option("--target <url|file>", "What to chat with \u2014 pass ANY of: a target server base URL (spec auto-discovered at /openapi.json etc.), a local OpenAPI file (./openapi.yaml), or a remote OpenAPI URL (https://acme.com/openapi.yaml)").addOption(new import_commander.Option("--openapi <file|url>", "Deprecated alias \u2014 --target now detects spec files/URLs itself").hideHelp()).addOption(new import_commander.Option("--openapispec <file|url>", "Deprecated alias for --openapi").hideHelp()).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("--apikey <key>", "Use this API key for the proxy's door (api_key proxies). Without it, apichat detects the door and asks \u2014 or runs the consumer login for OAuth doors.").option("--xenduserid <id>", "Assert this end-user id (X-End-User-Id) \u2014 required by proxies with identified/pre-approved enforcement; you are asked for one when the proxy demands it.").option("--verbose", "Show the per-turn proxy curl trace (hidden by default)").option("-p, --prompt <question>", "One-shot question: answered through the external agent CLI after an MCP install, or by apichat itself (exits after answering when there is no TTY)").option("--install-mcp <cli>", "Install this proxy's MCP into an external agent CLI without asking: claude | codex. Also re-offers after an earlier decline.").option("--remove-mcp <cli>", "Disconnect this proxy from an external agent CLI (claude | codex): removes the MCP server and the standing instruction").action(action((project, opts) => runApichat({ ...opts, project, openapispec: opts.openapispec ?? opts.openapi })));
|
|
11699
11929
|
var llm = program.command("llm").description("Manage a local LLM provider key for chat (optional \u2014 lifts model quality, bills your key)");
|
|
11700
11930
|
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)));
|
|
11701
11931
|
llm.command("show").description("Show the locally stored LLM key (masked)").action(action(() => runLlmShow()));
|
|
@@ -11712,7 +11942,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
11712
11942
|
try {
|
|
11713
11943
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
11714
11944
|
if (Number.isNaN(resolved)) {
|
|
11715
|
-
console.error(
|
|
11945
|
+
console.error(import_chalk55.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
11716
11946
|
process.exit(1);
|
|
11717
11947
|
}
|
|
11718
11948
|
await runDev({ port: resolved, project: opts.project, yes: opts.yes, captureFile: opts.captureFile, newSession: opts.newSession });
|
|
@@ -11745,6 +11975,7 @@ program.command("claim").description("Claim your anonymous workspace into your a
|
|
|
11745
11975
|
process.exit(1);
|
|
11746
11976
|
}
|
|
11747
11977
|
});
|
|
11978
|
+
program.command("docs").description("Ask an AI chatbot about APIblaze \u2014 and let it act on your account (it's the real API, not a docs search)").option("-p, --prompt <question>", "Ask one question and exit").option("--install-mcp <cli>", "Wire it straight into an external agent CLI: claude | codex").option("--remove-mcp <cli>", "Disconnect APIblaze from an external agent CLI (claude | codex)").option("--yes", 'Skip the "this can change things" confirmation').option("--verbose", "Show the per-turn proxy curl trace").action(action((opts) => runDocs(opts)));
|
|
11748
11979
|
program.command("allowoauthregistration").description("Approve the next OAuth client that registers against your tenant (Claude Code, Codex, any MCP client) \u2014 single use").requiredOption("--tenant <slug>", "Tenant the client will register against (see `apiblaze projects`)").option("--team <id|name>", "Team owning the tenant (defaults to your active team)").option("--minutes <n>", "Expire the unused authorization after this many minutes (default 10, max 60)").option("--cancel", "Cancel an unused authorization instead of opening one").action(action((opts) => runAllowOauthRegistration(opts)));
|
|
11749
11980
|
program.command("team").description("Switch the active team, or create a new one").argument("[team]", "Team name or id to switch to (omit to choose interactively)").option("--new [name]", "Create a new team and make it active (omit the name to be asked)").action(async (team, opts) => {
|
|
11750
11981
|
try {
|
|
@@ -11822,7 +12053,7 @@ spec.command("set").description("Replace the stored OpenAPI spec from a local fi
|
|
|
11822
12053
|
spec.command("carry").description("Copy a route's saved rules to the path a spec rename moved traffic to").argument("<project>", "Project name or id").requiredOption("--method <method>", "HTTP method of the route (GET, POST, ...)").requiredOption("--from <path>", "Old route path, e.g. /owners/{ownerId}").requiredOption("--to <path>", "New route path, e.g. /customers/{customerId}").option("--delete-source", "Also delete the old rules after carrying").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Output machine-readable JSON").action(action((project, opts) => runSpecCarry(project, opts)));
|
|
11823
12054
|
spec.command("delete-rule").description("Delete the saved rules for a route (e.g. an orphan left by a spec change)").argument("<project>", "Project name or id").requiredOption("--method <method>", "HTTP method of the route").requiredOption("--route <path>", "Route path, e.g. /owners/{ownerId}").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").action(action((project, opts) => runSpecDeleteRule(project, opts)));
|
|
11824
12055
|
var HELP_GROUPS = [
|
|
11825
|
-
{ title: "Chat", commands: ["apichat", "agent"] },
|
|
12056
|
+
{ title: "Chat", commands: ["docs", "apichat", "agent"] },
|
|
11826
12057
|
{ title: "Setup", commands: ["login", "create", "init", "sidecar", "dev", "claim", "team", "whoami", "logout"] },
|
|
11827
12058
|
{ title: "Recipes \u2014 install a working setup, or publish yours", commands: ["search", "show", "install", "publish", "withdraw"] },
|
|
11828
12059
|
{ title: "Control plane commands", commands: ["config", "projects", "logs", "tenant", "group", "admins", "iam", "identified", "preapprove", "rule", "domain", "delete", "target", "throttle", "rename", "spec", "export"] },
|
|
@@ -11845,7 +12076,7 @@ function groupedCommandHelp() {
|
|
|
11845
12076
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
11846
12077
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
11847
12078
|
}).filter(Boolean).join("\n");
|
|
11848
|
-
return `${
|
|
12079
|
+
return `${import_chalk55.default.bold(g.title)}
|
|
11849
12080
|
${rows}`;
|
|
11850
12081
|
}).join("\n\n");
|
|
11851
12082
|
}
|
|
@@ -11856,6 +12087,8 @@ program.configureHelp({
|
|
|
11856
12087
|
}
|
|
11857
12088
|
});
|
|
11858
12089
|
program.addHelpText("after", () => `
|
|
12090
|
+
${import_chalk55.default.bold("Don't like reading docs?")} Run ${import_chalk55.default.bgCyan.black.bold(" npx apiblaze docs ")} ${import_chalk55.default.cyan("\u2014 ask an AI chatbot about APIblaze, and let it act on your account.")}
|
|
12091
|
+
|
|
11859
12092
|
${groupedCommandHelp()}
|
|
11860
12093
|
|
|
11861
12094
|
Tips:
|
|
@@ -11885,14 +12118,14 @@ async function recoverStaleTeam() {
|
|
|
11885
12118
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
11886
12119
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
11887
12120
|
if (!linked) {
|
|
11888
|
-
console.error(
|
|
12121
|
+
console.error(import_chalk55.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
11889
12122
|
return;
|
|
11890
12123
|
}
|
|
11891
12124
|
if (linked.teamId === creds.teamId) return;
|
|
11892
12125
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
11893
12126
|
delete next.activeTenant;
|
|
11894
12127
|
saveCredentials(next);
|
|
11895
|
-
console.error(
|
|
12128
|
+
console.error(import_chalk55.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk55.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
11896
12129
|
} catch {
|
|
11897
12130
|
}
|
|
11898
12131
|
}
|
|
@@ -11900,16 +12133,16 @@ async function printError(err) {
|
|
|
11900
12133
|
if (err instanceof ApiError) {
|
|
11901
12134
|
const data = err.body;
|
|
11902
12135
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
11903
|
-
console.error(
|
|
12136
|
+
console.error(import_chalk55.default.red(`
|
|
11904
12137
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
11905
12138
|
if (err.status === 403 || err.status === 404) {
|
|
11906
12139
|
await recoverStaleTeam();
|
|
11907
12140
|
}
|
|
11908
12141
|
} else if (err instanceof Error) {
|
|
11909
|
-
console.error(
|
|
12142
|
+
console.error(import_chalk55.default.red(`
|
|
11910
12143
|
Error: ${err.message}`));
|
|
11911
12144
|
} else {
|
|
11912
|
-
console.error(
|
|
12145
|
+
console.error(import_chalk55.default.red("\nUnknown error"));
|
|
11913
12146
|
}
|
|
11914
12147
|
}
|
|
11915
12148
|
program.parse(process.argv);
|