apiblaze 0.20.10 → 0.20.11
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 +547 -357
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -137,8 +137,8 @@ async function createProxyAnonymous(body) {
|
|
|
137
137
|
}
|
|
138
138
|
return res.json();
|
|
139
139
|
}
|
|
140
|
-
async function apiFetch(
|
|
141
|
-
const url = `${DASHBOARD_BASE}${
|
|
140
|
+
async function apiFetch(path9, options = {}, auth) {
|
|
141
|
+
const url = `${DASHBOARD_BASE}${path9}`;
|
|
142
142
|
const res = await fetch(url, {
|
|
143
143
|
...options,
|
|
144
144
|
headers: {
|
|
@@ -164,12 +164,12 @@ async function apiFetch(path8, options = {}, auth) {
|
|
|
164
164
|
}
|
|
165
165
|
return res.json();
|
|
166
166
|
}
|
|
167
|
-
async function agentCall(
|
|
167
|
+
async function agentCall(path9, method, body) {
|
|
168
168
|
const token = getAccessToken();
|
|
169
169
|
const res = await fetch(`${DASHBOARD_BASE}/api/cli/agents`, {
|
|
170
170
|
method: "POST",
|
|
171
171
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
172
|
-
body: JSON.stringify({ path:
|
|
172
|
+
body: JSON.stringify({ path: path9, method, body })
|
|
173
173
|
});
|
|
174
174
|
let data = null;
|
|
175
175
|
try {
|
|
@@ -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_chalk53 = __toESM(require("chalk"));
|
|
1025
1025
|
|
|
1026
1026
|
// package.json
|
|
1027
|
-
var version = "0.20.
|
|
1027
|
+
var version = "0.20.11";
|
|
1028
1028
|
|
|
1029
1029
|
// src/index.ts
|
|
1030
1030
|
init_types();
|
|
@@ -1290,11 +1290,11 @@ function decodeJwt(token) {
|
|
|
1290
1290
|
return null;
|
|
1291
1291
|
}
|
|
1292
1292
|
}
|
|
1293
|
-
function maskPath(
|
|
1294
|
-
const q =
|
|
1295
|
-
if (q < 0) return
|
|
1296
|
-
const base2 =
|
|
1297
|
-
const query =
|
|
1293
|
+
function maskPath(path9) {
|
|
1294
|
+
const q = path9.indexOf("?");
|
|
1295
|
+
if (q < 0) return path9;
|
|
1296
|
+
const base2 = path9.slice(0, q);
|
|
1297
|
+
const query = path9.slice(q + 1);
|
|
1298
1298
|
const masked = query.split("&").map((pair) => {
|
|
1299
1299
|
const eq = pair.indexOf("=");
|
|
1300
1300
|
if (eq < 0) return pair;
|
|
@@ -2328,7 +2328,7 @@ async function runCreate(opts = {}) {
|
|
|
2328
2328
|
}
|
|
2329
2329
|
let openapiContent = null;
|
|
2330
2330
|
if (opts.openapi !== void 0) {
|
|
2331
|
-
if (opts.target !== void 0) fail("Provide only one
|
|
2331
|
+
if (opts.target !== void 0) fail("Provide only one source (the smart --target covers servers, spec files and spec URLs).");
|
|
2332
2332
|
openapiContent = await loadOpenapiSource(opts.openapi);
|
|
2333
2333
|
}
|
|
2334
2334
|
let targetUrl = "";
|
|
@@ -2352,7 +2352,7 @@ async function runCreate(opts = {}) {
|
|
|
2352
2352
|
break;
|
|
2353
2353
|
}
|
|
2354
2354
|
} else {
|
|
2355
|
-
fail("--target
|
|
2355
|
+
fail("--target is required in non-interactive mode (a server base URL, a local OpenAPI file, or a remote OpenAPI URL).");
|
|
2356
2356
|
}
|
|
2357
2357
|
if (interactive && !opts.yes) {
|
|
2358
2358
|
const { default: inquirer3 } = await import("inquirer");
|
|
@@ -3529,8 +3529,8 @@ function resolveRecipeName(raw, githubHandle, proxyName) {
|
|
|
3529
3529
|
function normalizeName2(raw) {
|
|
3530
3530
|
return (raw || "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
3531
3531
|
}
|
|
3532
|
-
async function publicFetch(
|
|
3533
|
-
const res = await fetch(`${RECIPES_BASE}/api/recipes${
|
|
3532
|
+
async function publicFetch(path9) {
|
|
3533
|
+
const res = await fetch(`${RECIPES_BASE}/api/recipes${path9}`, {
|
|
3534
3534
|
headers: { accept: "application/json" }
|
|
3535
3535
|
}).catch((err) => {
|
|
3536
3536
|
throw new Error(`Could not reach the recipe registry at ${RECIPES_BASE} \u2014 ${err.message}`);
|
|
@@ -3583,11 +3583,11 @@ async function fetchRecipeFile(ref, caller) {
|
|
|
3583
3583
|
Private recipes are visible only to the publisher's team \u2014 run \`apiblaze login\` if you are on it.`
|
|
3584
3584
|
);
|
|
3585
3585
|
}
|
|
3586
|
-
const
|
|
3586
|
+
const path9 = revision !== void 0 ? `/recipes/${ref.handle}/${ref.slug}/${revision}` : `/recipes/${ref.handle}/${ref.slug}/file`;
|
|
3587
3587
|
try {
|
|
3588
3588
|
return await producer(caller, {
|
|
3589
3589
|
method: "GET",
|
|
3590
|
-
path:
|
|
3590
|
+
path: path9,
|
|
3591
3591
|
summary: `Read private recipe ${ref.name}${revision !== void 0 ? `@${revision}` : ""}`
|
|
3592
3592
|
});
|
|
3593
3593
|
} catch (err) {
|
|
@@ -4739,9 +4739,9 @@ function printEnvelopeViolations(violations) {
|
|
|
4739
4739
|
console.log("bring-your-own-key case. Change the rules above, then publish again.");
|
|
4740
4740
|
console.log();
|
|
4741
4741
|
}
|
|
4742
|
-
async function askPromotion(
|
|
4742
|
+
async function askPromotion(path9) {
|
|
4743
4743
|
const { default: inquirer3 } = await import("inquirer");
|
|
4744
|
-
const suggestedId =
|
|
4744
|
+
const suggestedId = path9.split(/[.[\]]/).filter(Boolean).slice(-2).join("_").toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "") || "value";
|
|
4745
4745
|
const answers = await inquirer3.prompt([
|
|
4746
4746
|
{ type: "input", name: "prompt", message: " prompt them with:", validate: (v) => v.trim() ? true : "Required." },
|
|
4747
4747
|
{ type: "input", name: "example", message: " example:" },
|
|
@@ -4752,7 +4752,7 @@ async function askPromotion(path8) {
|
|
|
4752
4752
|
const id = String(answers.id || suggestedId).toLowerCase().replace(/[^a-z0-9_]/g, "_");
|
|
4753
4753
|
console.log(import_chalk27.default.dim(` \u2192 replaced with {{${id}}}`));
|
|
4754
4754
|
return {
|
|
4755
|
-
path:
|
|
4755
|
+
path: path9,
|
|
4756
4756
|
id,
|
|
4757
4757
|
prompt: String(answers.prompt).trim(),
|
|
4758
4758
|
...answers.example ? { example: String(answers.example).trim() } : {},
|
|
@@ -7802,11 +7802,11 @@ async function resolveTransport(opts) {
|
|
|
7802
7802
|
const token = getAccessToken();
|
|
7803
7803
|
return {
|
|
7804
7804
|
tenant: tenant2,
|
|
7805
|
-
call: async (
|
|
7805
|
+
call: async (path9, method = "GET", body) => {
|
|
7806
7806
|
const res = await fetch(`${DASHBOARD_BASE6}/api/cli/iam`, {
|
|
7807
7807
|
method: "POST",
|
|
7808
7808
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
7809
|
-
body: JSON.stringify({ tenant: tenant2, path:
|
|
7809
|
+
body: JSON.stringify({ tenant: tenant2, path: path9, method, body })
|
|
7810
7810
|
});
|
|
7811
7811
|
const data = await res.json().catch(() => ({}));
|
|
7812
7812
|
if (!res.ok) throw new ApiError(res.status, data?.details ?? data?.error ?? res.statusText, data);
|
|
@@ -7824,8 +7824,8 @@ async function resolveTransport(opts) {
|
|
|
7824
7824
|
return {
|
|
7825
7825
|
tenant: fresh.tenant,
|
|
7826
7826
|
selfEmail: fresh.email,
|
|
7827
|
-
call: async (
|
|
7828
|
-
const res = await fetch(`${IAM_BASE}${
|
|
7827
|
+
call: async (path9, method = "GET", body) => {
|
|
7828
|
+
const res = await fetch(`${IAM_BASE}${path9}`, {
|
|
7829
7829
|
method,
|
|
7830
7830
|
headers: {
|
|
7831
7831
|
"Content-Type": "application/json",
|
|
@@ -8177,12 +8177,12 @@ init_admin();
|
|
|
8177
8177
|
init_resolve();
|
|
8178
8178
|
init_types();
|
|
8179
8179
|
var DASHBOARD_BASE7 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
8180
|
-
async function iamCall(tenant2,
|
|
8180
|
+
async function iamCall(tenant2, path9, method = "GET", body) {
|
|
8181
8181
|
const token = getAccessToken();
|
|
8182
8182
|
const res = await fetch(`${DASHBOARD_BASE7}/api/cli/iam`, {
|
|
8183
8183
|
method: "POST",
|
|
8184
8184
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
8185
|
-
body: JSON.stringify({ tenant: tenant2, path:
|
|
8185
|
+
body: JSON.stringify({ tenant: tenant2, path: path9, method, body })
|
|
8186
8186
|
});
|
|
8187
8187
|
const data = await res.json().catch(() => ({}));
|
|
8188
8188
|
if (!res.ok) throw new ApiError(res.status, data?.details ?? data?.error ?? res.statusText, data);
|
|
@@ -8269,10 +8269,10 @@ async function runPreapprove(who, opts) {
|
|
|
8269
8269
|
}
|
|
8270
8270
|
|
|
8271
8271
|
// src/commands/apichat.ts
|
|
8272
|
-
var
|
|
8273
|
-
var
|
|
8272
|
+
var fs11 = __toESM(require("fs"));
|
|
8273
|
+
var path7 = __toESM(require("path"));
|
|
8274
8274
|
var crypto2 = __toESM(require("crypto"));
|
|
8275
|
-
var
|
|
8275
|
+
var import_chalk47 = __toESM(require("chalk"));
|
|
8276
8276
|
var import_ora23 = __toESM(require("ora"));
|
|
8277
8277
|
var import_yaml3 = require("yaml");
|
|
8278
8278
|
init_auth();
|
|
@@ -8353,11 +8353,134 @@ async function runLlmClearKey() {
|
|
|
8353
8353
|
|
|
8354
8354
|
// src/commands/apichat.ts
|
|
8355
8355
|
init_trace();
|
|
8356
|
+
|
|
8357
|
+
// src/lib/external-mcp.ts
|
|
8358
|
+
var fs10 = __toESM(require("fs"));
|
|
8359
|
+
var os4 = __toESM(require("os"));
|
|
8360
|
+
var path6 = __toESM(require("path"));
|
|
8361
|
+
var import_child_process2 = require("child_process");
|
|
8362
|
+
var import_chalk46 = __toESM(require("chalk"));
|
|
8363
|
+
var run = (cmd, args, opts = {}) => (0, import_child_process2.spawnSync)(cmd, args, {
|
|
8364
|
+
encoding: "utf-8",
|
|
8365
|
+
stdio: opts.inherit ? ["ignore", "inherit", "inherit"] : ["ignore", "pipe", "pipe"],
|
|
8366
|
+
timeout: opts.inherit ? void 0 : 15e3,
|
|
8367
|
+
shell: process.platform === "win32"
|
|
8368
|
+
// .cmd shims on Windows
|
|
8369
|
+
});
|
|
8370
|
+
function detectExternalClis() {
|
|
8371
|
+
const found = [];
|
|
8372
|
+
for (const [kind, label3] of [["claude", "Claude CLI"], ["codex", "Codex CLI"]]) {
|
|
8373
|
+
try {
|
|
8374
|
+
const r = run(kind, ["--version"]);
|
|
8375
|
+
if (r.status === 0) found.push({ kind, label: label3, version: (r.stdout || "").trim().split("\n")[0] || void 0 });
|
|
8376
|
+
} catch {
|
|
8377
|
+
}
|
|
8378
|
+
}
|
|
8379
|
+
return found;
|
|
8380
|
+
}
|
|
8381
|
+
function claudeInstallArgs(spec2) {
|
|
8382
|
+
const args = ["mcp", "add", "--transport", "http", spec2.name, spec2.url];
|
|
8383
|
+
if (spec2.apiKey) args.push("--header", `X-API-Key: ${spec2.apiKey}`);
|
|
8384
|
+
return args;
|
|
8385
|
+
}
|
|
8386
|
+
function installIntoClaude(spec2) {
|
|
8387
|
+
run("claude", ["mcp", "remove", spec2.name]);
|
|
8388
|
+
const r = run("claude", claudeInstallArgs(spec2));
|
|
8389
|
+
if (r.status === 0) return { ok: true };
|
|
8390
|
+
return { ok: false, error: (r.stderr || r.stdout || `exit ${r.status}`).trim().slice(0, 400) };
|
|
8391
|
+
}
|
|
8392
|
+
function claudeOneShot(spec2, prompt) {
|
|
8393
|
+
const argv = ["claude", "-p", prompt, "--allowedTools", `mcp__${spec2.name}__*`];
|
|
8394
|
+
const r = run(argv[0], argv.slice(1), { inherit: true });
|
|
8395
|
+
return { argv, status: r.status };
|
|
8396
|
+
}
|
|
8397
|
+
function codexConfigPath() {
|
|
8398
|
+
return path6.join(process.env.CODEX_HOME || path6.join(os4.homedir(), ".codex"), "config.toml");
|
|
8399
|
+
}
|
|
8400
|
+
var tomlStr = (s) => `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
8401
|
+
function codexServerBlock(spec2) {
|
|
8402
|
+
const lines = [`[mcp_servers.${tomlStr(spec2.name)}]`, `url = ${tomlStr(spec2.url)}`];
|
|
8403
|
+
if (spec2.apiKey) lines.push(`http_headers = { "X-API-Key" = ${tomlStr(spec2.apiKey)} }`);
|
|
8404
|
+
return lines.join("\n") + "\n";
|
|
8405
|
+
}
|
|
8406
|
+
function installIntoCodex(spec2) {
|
|
8407
|
+
const file = codexConfigPath();
|
|
8408
|
+
try {
|
|
8409
|
+
fs10.mkdirSync(path6.dirname(file), { recursive: true });
|
|
8410
|
+
let text = "";
|
|
8411
|
+
try {
|
|
8412
|
+
text = fs10.readFileSync(file, "utf-8");
|
|
8413
|
+
} catch {
|
|
8414
|
+
}
|
|
8415
|
+
const esc = spec2.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
8416
|
+
const section = new RegExp(`(^|\\n)\\[mcp_servers\\.(?:"${esc}"|${esc})\\][^\\[]*`, "g");
|
|
8417
|
+
const cleaned = text.replace(section, "$1");
|
|
8418
|
+
const sep = cleaned.length && !cleaned.endsWith("\n\n") ? cleaned.endsWith("\n") ? "\n" : "\n\n" : "";
|
|
8419
|
+
fs10.writeFileSync(file, cleaned + sep + codexServerBlock(spec2), "utf-8");
|
|
8420
|
+
return { ok: true, path: file };
|
|
8421
|
+
} catch (err) {
|
|
8422
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err), path: file };
|
|
8423
|
+
}
|
|
8424
|
+
}
|
|
8425
|
+
function codexOneShot(_spec, prompt) {
|
|
8426
|
+
const argv = ["codex", "exec", prompt];
|
|
8427
|
+
const r = run(argv[0], argv.slice(1), { inherit: true });
|
|
8428
|
+
return { argv, status: r.status };
|
|
8429
|
+
}
|
|
8430
|
+
var shellQuote = (s) => `"${s.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
8431
|
+
function renderCommand(argv) {
|
|
8432
|
+
return argv.map((a, i) => i === 0 || /^[A-Za-z0-9_@%+=:,./-]+$/.test(a) ? a : shellQuote(a)).join(" ");
|
|
8433
|
+
}
|
|
8434
|
+
function installAndDemo(cli, spec2, question, log = console.log) {
|
|
8435
|
+
if (cli.kind === "claude") {
|
|
8436
|
+
log(`
|
|
8437
|
+
${import_chalk46.default.dim("$")} ${renderCommand(["claude", ...claudeInstallArgs(spec2)])}`);
|
|
8438
|
+
const r = installIntoClaude(spec2);
|
|
8439
|
+
if (!r.ok) {
|
|
8440
|
+
log(import_chalk46.default.red(` Install failed: ${r.error}`));
|
|
8441
|
+
return false;
|
|
8442
|
+
}
|
|
8443
|
+
log(` ${import_chalk46.default.green("\u2714")} MCP ${import_chalk46.default.bold(spec2.name)} added to Claude CLI (local scope \u2014 this directory).`);
|
|
8444
|
+
} else {
|
|
8445
|
+
const r = installIntoCodex(spec2);
|
|
8446
|
+
if (!r.ok) {
|
|
8447
|
+
log(import_chalk46.default.red(` Could not write ${r.path}: ${r.error}`));
|
|
8448
|
+
return false;
|
|
8449
|
+
}
|
|
8450
|
+
log(` ${import_chalk46.default.green("\u2714")} MCP ${import_chalk46.default.bold(spec2.name)} added to ${r.path}.`);
|
|
8451
|
+
}
|
|
8452
|
+
if (!spec2.apiKey) {
|
|
8453
|
+
log(import_chalk46.default.dim(` This proxy authenticates by login: the first call from ${cli.label} will open its sign-in.`));
|
|
8454
|
+
log(`
|
|
8455
|
+
${import_chalk46.default.bold(`Your ${cli.label} is now able to talk to the ${spec2.projectLabel} API.`)}`);
|
|
8456
|
+
return true;
|
|
8457
|
+
}
|
|
8458
|
+
const oneShot = cli.kind === "claude" ? claudeOneShot : codexOneShot;
|
|
8459
|
+
const abilities = `What are the tool abilities of the MCP server "${spec2.name}"? List them briefly.`;
|
|
8460
|
+
log(`
|
|
8461
|
+
${import_chalk46.default.dim("Checking what the MCP exposes\u2026")}`);
|
|
8462
|
+
log(` ${import_chalk46.default.dim("$")} ${renderCommand(cli.kind === "claude" ? ["claude", "-p", abilities, "--allowedTools", `mcp__${spec2.name}__*`] : ["codex", "exec", abilities])}
|
|
8463
|
+
`);
|
|
8464
|
+
oneShot(spec2, abilities);
|
|
8465
|
+
if (question) {
|
|
8466
|
+
log(`
|
|
8467
|
+
${import_chalk46.default.dim("Your question, through " + cli.label + ":")}`);
|
|
8468
|
+
const shown = cli.kind === "claude" ? ["claude", "-p", question, "--allowedTools", `mcp__${spec2.name}__*`] : ["codex", "exec", question];
|
|
8469
|
+
log(` ${import_chalk46.default.dim("$")} ${renderCommand(shown)}
|
|
8470
|
+
`);
|
|
8471
|
+
oneShot(spec2, question);
|
|
8472
|
+
}
|
|
8473
|
+
log(`
|
|
8474
|
+
${import_chalk46.default.bold(`Your ${cli.label} is now able to talk to the ${spec2.projectLabel} API.`)}`);
|
|
8475
|
+
return true;
|
|
8476
|
+
}
|
|
8477
|
+
|
|
8478
|
+
// src/commands/apichat.ts
|
|
8356
8479
|
init_types();
|
|
8357
8480
|
function fail4(message, hint) {
|
|
8358
|
-
console.error(
|
|
8481
|
+
console.error(import_chalk47.default.red(`
|
|
8359
8482
|
Error: ${message}`));
|
|
8360
|
-
if (hint) console.error(
|
|
8483
|
+
if (hint) console.error(import_chalk47.default.dim(hint));
|
|
8361
8484
|
process.exit(1);
|
|
8362
8485
|
}
|
|
8363
8486
|
function normalizeName3(raw) {
|
|
@@ -8388,7 +8511,7 @@ function parseSpec(text) {
|
|
|
8388
8511
|
}
|
|
8389
8512
|
return parsed;
|
|
8390
8513
|
}
|
|
8391
|
-
var GENERATOR_HINT = "
|
|
8514
|
+
var GENERATOR_HINT = "Pass --target <server-url | spec-file | spec-url>, or build a spec from real traffic:\n apiblaze create --target <url> then apiblaze agent openapi <project>";
|
|
8392
8515
|
async function fetchText(url) {
|
|
8393
8516
|
try {
|
|
8394
8517
|
const res = await fetch(url, { headers: { accept: "application/json, application/yaml, text/yaml, */*" } });
|
|
@@ -8421,7 +8544,7 @@ async function loadSpec(opts) {
|
|
|
8421
8544
|
}
|
|
8422
8545
|
let text;
|
|
8423
8546
|
try {
|
|
8424
|
-
text =
|
|
8547
|
+
text = fs11.readFileSync(opts.openapispec, "utf-8");
|
|
8425
8548
|
} catch {
|
|
8426
8549
|
fail4(`Cannot read spec file: ${opts.openapispec}`);
|
|
8427
8550
|
}
|
|
@@ -8435,7 +8558,7 @@ async function loadSpec(opts) {
|
|
|
8435
8558
|
}
|
|
8436
8559
|
return { spec: found.spec, sourceUrl: found.sourceUrl };
|
|
8437
8560
|
}
|
|
8438
|
-
fail4("No spec source. Pass --
|
|
8561
|
+
fail4("No spec source. Pass --target <server-url | openapi-file | openapi-url>.", GENERATOR_HINT);
|
|
8439
8562
|
}
|
|
8440
8563
|
function resolveTarget(spec2, opts, sourceUrl) {
|
|
8441
8564
|
if (opts.target) {
|
|
@@ -8505,7 +8628,7 @@ async function resolveTargetAuth(spec2, opts) {
|
|
|
8505
8628
|
"Re-run with --force to provision anyway (configure target auth later with `apiblaze config`),\nor use an api_key / bearer / basic scheme."
|
|
8506
8629
|
);
|
|
8507
8630
|
}
|
|
8508
|
-
if (sawOAuth) console.log(
|
|
8631
|
+
if (sawOAuth) console.log(import_chalk47.default.yellow(" --force: skipping OAuth target auth \u2014 configure it later with `apiblaze config`."));
|
|
8509
8632
|
return null;
|
|
8510
8633
|
}
|
|
8511
8634
|
if (candidates.length === 1 && !noneAllowed) return candidates[0];
|
|
@@ -8579,7 +8702,7 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
|
|
|
8579
8702
|
const fresh = await validConsumerToken(existing);
|
|
8580
8703
|
if (fresh) {
|
|
8581
8704
|
if (fresh.accessToken !== existing.accessToken) saveConsumer({ ...fresh, resource });
|
|
8582
|
-
console.log(
|
|
8705
|
+
console.log(import_chalk47.default.dim(` Using your consumer session on ${import_chalk47.default.bold(tenant2)}${fresh.email ? ` (${fresh.email})` : ""}.`));
|
|
8583
8706
|
return fresh;
|
|
8584
8707
|
}
|
|
8585
8708
|
}
|
|
@@ -8598,13 +8721,13 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
|
|
|
8598
8721
|
);
|
|
8599
8722
|
}
|
|
8600
8723
|
const clientId = pick2.client_id ?? pick2.clientId;
|
|
8601
|
-
console.log(`${
|
|
8724
|
+
console.log(`${import_chalk47.default.cyan("\u2192")} This proxy signs consumers in with OAuth \u2014 logging you in to ${import_chalk47.default.bold(tenant2)}...`);
|
|
8602
8725
|
const result = await deviceLogin(clientId, "openid email profile offline_access", ({ verificationUri, userCode }) => {
|
|
8603
8726
|
console.log(`
|
|
8604
|
-
Open: ${
|
|
8605
|
-
console.log(` Code: ${
|
|
8727
|
+
Open: ${import_chalk47.default.underline(verificationUri)}`);
|
|
8728
|
+
console.log(` Code: ${import_chalk47.default.bold(userCode)}
|
|
8606
8729
|
`);
|
|
8607
|
-
console.log(
|
|
8730
|
+
console.log(import_chalk47.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
8608
8731
|
}, resource);
|
|
8609
8732
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
8610
8733
|
const creds = {
|
|
@@ -8620,16 +8743,16 @@ async function ensureConsumerLogin(teamId, tenant2, version2) {
|
|
|
8620
8743
|
resource
|
|
8621
8744
|
};
|
|
8622
8745
|
saveConsumer(creds);
|
|
8623
|
-
console.log(` ${
|
|
8746
|
+
console.log(` ${import_chalk47.default.green("\u2714")} Signed in as${creds.email ? ` ${import_chalk47.default.bold(creds.email)}` : " a consumer"} on ${tenant2}.`);
|
|
8624
8747
|
return creds;
|
|
8625
8748
|
}
|
|
8626
|
-
async function cpPost(anon,
|
|
8749
|
+
async function cpPost(anon, path9, body, summary) {
|
|
8627
8750
|
if (anon) {
|
|
8628
8751
|
const cred = loadAnonCred();
|
|
8629
8752
|
if (!cred) throw new Error("Anonymous workspace credential missing.");
|
|
8630
|
-
return cpFetch(cred.cp_key,
|
|
8753
|
+
return cpFetch(cred.cp_key, path9, { method: "POST", body: JSON.stringify(body) });
|
|
8631
8754
|
}
|
|
8632
|
-
return admin({ method: "POST", path:
|
|
8755
|
+
return admin({ method: "POST", path: path9, body, summary });
|
|
8633
8756
|
}
|
|
8634
8757
|
async function provision(spec2, target, opts) {
|
|
8635
8758
|
const loggedIn = !!loadCredentials();
|
|
@@ -8649,7 +8772,7 @@ async function provision(spec2, target, opts) {
|
|
|
8649
8772
|
let name = opts.name ? base2 : `${base2}${salt()}`;
|
|
8650
8773
|
const access = anon ? "open" : opts.access === "open" ? "open" : "invite";
|
|
8651
8774
|
if (anon && opts.access === "invite") {
|
|
8652
|
-
console.log(
|
|
8775
|
+
console.log(import_chalk47.default.dim(" Note: --access invite needs an account to pre-approve people. Staying open for this anonymous proxy \u2014 run `apiblaze login`, then `apiblaze apichat --access invite`."));
|
|
8653
8776
|
}
|
|
8654
8777
|
const DUAL_AUTH = {
|
|
8655
8778
|
mode: "authenticate",
|
|
@@ -8748,7 +8871,7 @@ async function provision(spec2, target, opts) {
|
|
|
8748
8871
|
try {
|
|
8749
8872
|
await addPreapprovalRule(tenant2, email);
|
|
8750
8873
|
} catch {
|
|
8751
|
-
console.log(
|
|
8874
|
+
console.log(import_chalk47.default.dim(` (Could not auto-approve your email for sign-in \u2014 add it later: apiblaze preapprove ${email})`));
|
|
8752
8875
|
}
|
|
8753
8876
|
}
|
|
8754
8877
|
}
|
|
@@ -8792,7 +8915,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
8792
8915
|
throw err;
|
|
8793
8916
|
}
|
|
8794
8917
|
if (out && out.reused === true) {
|
|
8795
|
-
console.log(
|
|
8918
|
+
console.log(import_chalk47.default.dim(" Spec unchanged since the last provision \u2014 reusing the existing configuration."));
|
|
8796
8919
|
} else if (out && out.changed === true && out.previous_spec_hash) {
|
|
8797
8920
|
const interactive = !!process.stdin.isTTY && !opts.yes;
|
|
8798
8921
|
if (interactive) {
|
|
@@ -8800,7 +8923,7 @@ async function uploadSpec(p, specText, opts) {
|
|
|
8800
8923
|
const { go } = await inquirer3.prompt([
|
|
8801
8924
|
{ type: "confirm", name: "go", message: "The spec changed since the last provision \u2014 re-publish the MCP catalogue?", default: true }
|
|
8802
8925
|
]);
|
|
8803
|
-
if (!go) console.log(
|
|
8926
|
+
if (!go) console.log(import_chalk47.default.dim(" Keeping the existing MCP catalogue."));
|
|
8804
8927
|
}
|
|
8805
8928
|
}
|
|
8806
8929
|
}
|
|
@@ -8842,9 +8965,9 @@ function billingLine(info) {
|
|
|
8842
8965
|
if (typeof info.free_turns_remaining === "number") return null;
|
|
8843
8966
|
const cents = info.charged_cents;
|
|
8844
8967
|
const usd = (cents / 100).toFixed(Math.abs(cents - Math.round(cents)) < 1e-9 ? 2 : 4);
|
|
8845
|
-
let line =
|
|
8968
|
+
let line = import_chalk47.default.magenta(` \u{1F4B3} $${usd}`);
|
|
8846
8969
|
if (typeof info.credits_remaining === "number") {
|
|
8847
|
-
line +=
|
|
8970
|
+
line += import_chalk47.default.dim(` \xB7 balance $${(info.credits_remaining / 100).toFixed(2)}`);
|
|
8848
8971
|
}
|
|
8849
8972
|
return line;
|
|
8850
8973
|
}
|
|
@@ -8852,15 +8975,15 @@ function freeBudgetWarning(info, anon) {
|
|
|
8852
8975
|
if (!anon || !info) return null;
|
|
8853
8976
|
if (typeof info.free_turns_remaining === "number") {
|
|
8854
8977
|
const left2 = info.free_turns_remaining;
|
|
8855
|
-
if (left2 <= 0) return
|
|
8856
|
-
return
|
|
8978
|
+
if (left2 <= 0) return import_chalk47.default.yellow(" Free chats used up \u2014 `npx apiblaze login` (free) to keep going.");
|
|
8979
|
+
return import_chalk47.default.dim(` ${left2} free chat${left2 === 1 ? "" : "s"} left \xB7 /login to get more`);
|
|
8857
8980
|
}
|
|
8858
8981
|
if (typeof info.free_remaining_cents !== "number") return null;
|
|
8859
8982
|
const perTurn = Math.max(info.charged_cents || 0, 0.02);
|
|
8860
8983
|
const left = Math.floor(info.free_remaining_cents / perTurn);
|
|
8861
8984
|
if (left > 8) return null;
|
|
8862
|
-
if (left <= 0) return
|
|
8863
|
-
return
|
|
8985
|
+
if (left <= 0) return import_chalk47.default.yellow(" Free messages used up \u2014 `npx apiblaze login` (free) to keep chatting.");
|
|
8986
|
+
return import_chalk47.default.yellow(` \u26A0 About ${left} free message${left === 1 ? "" : "s"} left \u2014 \`npx apiblaze login\` (free) for more.`);
|
|
8864
8987
|
}
|
|
8865
8988
|
async function readSse(body, onEvent) {
|
|
8866
8989
|
const reader = body.getReader();
|
|
@@ -8905,7 +9028,7 @@ async function replTurn(p, messages, userText) {
|
|
|
8905
9028
|
});
|
|
8906
9029
|
} catch (err) {
|
|
8907
9030
|
spinner.fail("Network error.");
|
|
8908
|
-
console.log(
|
|
9031
|
+
console.log(import_chalk47.default.red(` Could not reach ${p.mcpHost}: ${err instanceof Error ? err.message : String(err)}`));
|
|
8909
9032
|
return;
|
|
8910
9033
|
}
|
|
8911
9034
|
if ((res.headers.get("content-type") ?? "").includes("text/event-stream") && res.ok && res.body) break;
|
|
@@ -8928,12 +9051,12 @@ async function replTurn(p, messages, userText) {
|
|
|
8928
9051
|
spinner.start("retrying on the " + (p.anon ? "trial" : "claimed") + " plane\u2026");
|
|
8929
9052
|
continue;
|
|
8930
9053
|
}
|
|
8931
|
-
console.log(
|
|
9054
|
+
console.log(import_chalk47.default.red(` No proxy named ${p.projectId} was found (tried both abz.run and tryabz.run).`));
|
|
8932
9055
|
return;
|
|
8933
9056
|
}
|
|
8934
9057
|
if (code === "identity_required" || /identif/i.test(msg) && !code) {
|
|
8935
9058
|
if (!p.endUserId && tty) {
|
|
8936
|
-
console.log(
|
|
9059
|
+
console.log(import_chalk47.default.yellow(" This API requires every call to say WHO is calling."));
|
|
8937
9060
|
const { default: inquirer3 } = await import("inquirer");
|
|
8938
9061
|
const { id } = await inquirer3.prompt([{ type: "input", name: "id", message: "Your end-user id (usually your email):" }]);
|
|
8939
9062
|
if (typeof id === "string" && id.trim()) {
|
|
@@ -8943,27 +9066,27 @@ async function replTurn(p, messages, userText) {
|
|
|
8943
9066
|
continue;
|
|
8944
9067
|
}
|
|
8945
9068
|
}
|
|
8946
|
-
console.log(
|
|
8947
|
-
console.log(
|
|
9069
|
+
console.log(import_chalk47.default.red(" This API requires an identified caller."));
|
|
9070
|
+
console.log(import_chalk47.default.dim(" Re-run with --xenduserid <your id> (usually your email)."));
|
|
8948
9071
|
return;
|
|
8949
9072
|
}
|
|
8950
9073
|
if (code === "user_not_preapproved") {
|
|
8951
|
-
console.log(
|
|
8952
|
-
if (p.endUserId) console.log(
|
|
9074
|
+
console.log(import_chalk47.default.yellow(` ${msg || "You are not pre-approved on this API."}`));
|
|
9075
|
+
if (p.endUserId) console.log(import_chalk47.default.dim(` Identity sent: ${p.endUserId}`));
|
|
8953
9076
|
const accessUrl = errObj && errObj.request_access_url;
|
|
8954
|
-
if (accessUrl) console.log(` Request access: ${
|
|
8955
|
-
console.log(
|
|
9077
|
+
if (accessUrl) console.log(` Request access: ${import_chalk47.default.bold(String(accessUrl))}`);
|
|
9078
|
+
console.log(import_chalk47.default.dim(" Or ask the producer to pre-approve you: `apiblaze preapprove <your-email> --tenant <tenant>`."));
|
|
8956
9079
|
return;
|
|
8957
9080
|
}
|
|
8958
9081
|
if (code === "user_frozen") {
|
|
8959
|
-
console.log(
|
|
9082
|
+
console.log(import_chalk47.default.red(` ${msg || "Your access to this API has been frozen by the producer."}`));
|
|
8960
9083
|
return;
|
|
8961
9084
|
}
|
|
8962
9085
|
const oauthWanted = /oauth token required|authorization: bearer/i.test(msg);
|
|
8963
9086
|
const keyWanted = /api key required|x-api-key/i.test(msg);
|
|
8964
9087
|
if (oauthWanted && !p.consumerAuth) {
|
|
8965
9088
|
if (p.teamId && p.tenant && loadCredentials()) {
|
|
8966
|
-
console.log(
|
|
9089
|
+
console.log(import_chalk47.default.dim(" This proxy signs consumers in with OAuth \u2014 starting the login\u2026"));
|
|
8967
9090
|
try {
|
|
8968
9091
|
await ensureConsumerLogin(p.teamId, p.tenant, p.version);
|
|
8969
9092
|
p.consumerAuth = true;
|
|
@@ -8972,17 +9095,17 @@ async function replTurn(p, messages, userText) {
|
|
|
8972
9095
|
spinner.start("retrying\u2026");
|
|
8973
9096
|
continue;
|
|
8974
9097
|
} catch (err) {
|
|
8975
|
-
console.log(
|
|
9098
|
+
console.log(import_chalk47.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
8976
9099
|
return;
|
|
8977
9100
|
}
|
|
8978
9101
|
}
|
|
8979
|
-
console.log(
|
|
8980
|
-
console.log(
|
|
9102
|
+
console.log(import_chalk47.default.red(" This proxy signs consumers in with OAuth (a login), not an API key."));
|
|
9103
|
+
console.log(import_chalk47.default.dim(" Sign in with: `apiblaze consumer login --tenant <tenant> --client <app-client-id>`, then re-run apichat."));
|
|
8981
9104
|
return;
|
|
8982
9105
|
}
|
|
8983
9106
|
if (keyWanted) {
|
|
8984
9107
|
if (tty) {
|
|
8985
|
-
console.log(
|
|
9108
|
+
console.log(import_chalk47.default.yellow(` ${msg || "This API requires an API key."}`));
|
|
8986
9109
|
const { default: inquirer3 } = await import("inquirer");
|
|
8987
9110
|
const { key } = await inquirer3.prompt([{ type: "password", name: "key", mask: "*", message: "API key for this proxy:" }]);
|
|
8988
9111
|
if (typeof key === "string" && key.trim()) {
|
|
@@ -8993,8 +9116,8 @@ async function replTurn(p, messages, userText) {
|
|
|
8993
9116
|
continue;
|
|
8994
9117
|
}
|
|
8995
9118
|
}
|
|
8996
|
-
console.log(
|
|
8997
|
-
console.log(
|
|
9119
|
+
console.log(import_chalk47.default.red(` ${msg || "This API requires an API key."}`));
|
|
9120
|
+
console.log(import_chalk47.default.dim(" Re-run with --apikey <key> (mint one from the producer's site or dev portal)."));
|
|
8998
9121
|
return;
|
|
8999
9122
|
}
|
|
9000
9123
|
if (res && (res.status === 402 || res.status === 403)) {
|
|
@@ -9002,16 +9125,16 @@ async function replTurn(p, messages, userText) {
|
|
|
9002
9125
|
return;
|
|
9003
9126
|
}
|
|
9004
9127
|
if (res && res.status === 401) {
|
|
9005
|
-
console.log(
|
|
9006
|
-
console.log(
|
|
9128
|
+
console.log(import_chalk47.default.red(` The proxy rejected the request (401)${msg ? `: ${msg}` : "."}`));
|
|
9129
|
+
console.log(import_chalk47.default.dim(p.consumerAuth ? " Sent your consumer OAuth token. Run `apiblaze consumer login` again." : " Sent an API key. Pass a different one with --apikey <key>."));
|
|
9007
9130
|
return;
|
|
9008
9131
|
}
|
|
9009
|
-
console.log(
|
|
9132
|
+
console.log(import_chalk47.default.red(` Chat error: ${msg || (res ? `HTTP ${res.status}` : "request failed")}`));
|
|
9010
9133
|
return;
|
|
9011
9134
|
}
|
|
9012
9135
|
if (!res || !res.body || !(res.headers.get("content-type") ?? "").includes("text/event-stream")) {
|
|
9013
9136
|
spinner.stop();
|
|
9014
|
-
console.log(
|
|
9137
|
+
console.log(import_chalk47.default.red(" Chat error: could not authenticate to this proxy after several attempts."));
|
|
9015
9138
|
return;
|
|
9016
9139
|
}
|
|
9017
9140
|
let spinnerLive = true;
|
|
@@ -9052,7 +9175,7 @@ async function replTurn(p, messages, userText) {
|
|
|
9052
9175
|
const name = String(ev.toolName ?? "tool");
|
|
9053
9176
|
parts.push({ type: `tool-${name}`, toolCallId: String(ev.toolCallId ?? ""), state: "input-streaming" });
|
|
9054
9177
|
toolMeta.set(String(ev.toolCallId ?? ""), { name, startedAt: Date.now(), partIdx: parts.length - 1 });
|
|
9055
|
-
console.log(` ${
|
|
9178
|
+
console.log(` ${import_chalk47.default.cyan("\u2699")} ${import_chalk47.default.cyan(name)}${import_chalk47.default.dim("\u2026")}`);
|
|
9056
9179
|
break;
|
|
9057
9180
|
}
|
|
9058
9181
|
case "tool-input-available": {
|
|
@@ -9064,9 +9187,9 @@ async function replTurn(p, messages, userText) {
|
|
|
9064
9187
|
Object.assign(parts[meta.partIdx], { state: "input-available", input });
|
|
9065
9188
|
}
|
|
9066
9189
|
if (isVerbose()) {
|
|
9067
|
-
console.log(
|
|
9190
|
+
console.log(import_chalk47.default.dim(` args ${JSON.stringify(input)}`));
|
|
9068
9191
|
const hint = credHint();
|
|
9069
|
-
if (hint) console.log(
|
|
9192
|
+
if (hint) console.log(import_chalk47.default.dim(` auth ${hint}`) + (revealAuth ? "" : import_chalk47.default.yellow(" \u2190 /showauth reveals")));
|
|
9070
9193
|
}
|
|
9071
9194
|
break;
|
|
9072
9195
|
}
|
|
@@ -9077,8 +9200,8 @@ async function replTurn(p, messages, userText) {
|
|
|
9077
9200
|
const meta = toolMeta.get(id);
|
|
9078
9201
|
const ok = ev.type === "tool-output-available";
|
|
9079
9202
|
const ms = meta ? Date.now() - meta.startedAt : void 0;
|
|
9080
|
-
const mark = ok ?
|
|
9081
|
-
console.log(` ${mark} ${
|
|
9203
|
+
const mark = ok ? import_chalk47.default.green("\u2713") : import_chalk47.default.red("\u2717");
|
|
9204
|
+
console.log(` ${mark} ${import_chalk47.default.cyan(meta?.name ?? "tool")} ${import_chalk47.default.dim(`(${ok ? "ok" : "error"}${ms != null ? `, ${ms}ms` : ""})`)}`);
|
|
9082
9205
|
const detail = ok ? String(ev.output ?? "") : String(ev.errorText ?? "Tool call failed.");
|
|
9083
9206
|
if (meta) {
|
|
9084
9207
|
Object.assign(parts[meta.partIdx], ok ? { state: "output-available", output: detail } : { state: "output-error", errorText: detail });
|
|
@@ -9093,9 +9216,9 @@ async function replTurn(p, messages, userText) {
|
|
|
9093
9216
|
})();
|
|
9094
9217
|
const lines = pretty.split("\n");
|
|
9095
9218
|
const cap = ok ? 12 : 24;
|
|
9096
|
-
console.log(
|
|
9097
|
-
for (const line of lines.slice(0, cap)) console.log(
|
|
9098
|
-
if (lines.length > cap) console.log(
|
|
9219
|
+
console.log(import_chalk47.default.dim(" response:"));
|
|
9220
|
+
for (const line of lines.slice(0, cap)) console.log(import_chalk47.default.dim(` ${line}`));
|
|
9221
|
+
if (lines.length > cap) console.log(import_chalk47.default.dim(` \u2026${lines.length - cap} more lines`));
|
|
9099
9222
|
}
|
|
9100
9223
|
break;
|
|
9101
9224
|
}
|
|
@@ -9104,7 +9227,7 @@ async function replTurn(p, messages, userText) {
|
|
|
9104
9227
|
parts.push({ type: "text", text: "" });
|
|
9105
9228
|
openTextIdx = parts.length - 1;
|
|
9106
9229
|
if (!assistantOpen) {
|
|
9107
|
-
process.stdout.write("\n" +
|
|
9230
|
+
process.stdout.write("\n" + import_chalk47.default.green("assistant \u203A "));
|
|
9108
9231
|
assistantOpen = true;
|
|
9109
9232
|
}
|
|
9110
9233
|
break;
|
|
@@ -9135,12 +9258,12 @@ async function replTurn(p, messages, userText) {
|
|
|
9135
9258
|
});
|
|
9136
9259
|
} catch (err) {
|
|
9137
9260
|
stopSpinner();
|
|
9138
|
-
console.log(
|
|
9261
|
+
console.log(import_chalk47.default.red(` Stream error: ${err instanceof Error ? err.message : String(err)}`));
|
|
9139
9262
|
}
|
|
9140
9263
|
stopSpinner();
|
|
9141
9264
|
if (assistantOpen) process.stdout.write("\n\n");
|
|
9142
9265
|
if (parts.length) messages.push({ id: messageId, role: "assistant", parts });
|
|
9143
|
-
if (errorText) console.log(
|
|
9266
|
+
if (errorText) console.log(import_chalk47.default.red(` ${errorText}`));
|
|
9144
9267
|
if (upsell) {
|
|
9145
9268
|
renderUpsell(p, upsell, { messageAlreadyShown: true });
|
|
9146
9269
|
}
|
|
@@ -9152,39 +9275,39 @@ async function replTurn(p, messages, userText) {
|
|
|
9152
9275
|
function renderUpsell(p, upsell, opts = {}) {
|
|
9153
9276
|
const loggedIn = !!loadCredentials();
|
|
9154
9277
|
if (upsell.reason === "CAPPED" && !loggedIn) {
|
|
9155
|
-
console.log("\n" +
|
|
9156
|
-
console.log(
|
|
9278
|
+
console.log("\n" + import_chalk47.default.yellow(" Type `npx apiblaze login` to claim the rest of your balance."));
|
|
9279
|
+
console.log(import_chalk47.default.dim(" (or `/login` right here \u2014 your chat is preserved \u2014 or `apiblaze llm set-key` for your own model key.)"));
|
|
9157
9280
|
console.log();
|
|
9158
9281
|
return;
|
|
9159
9282
|
}
|
|
9160
9283
|
if (!opts.messageAlreadyShown) {
|
|
9161
|
-
console.log("\n" +
|
|
9284
|
+
console.log("\n" + import_chalk47.default.yellow(` ${upsell.message || "This turn is not available right now."}`));
|
|
9162
9285
|
}
|
|
9163
9286
|
if (upsell.reason === "INSUFFICIENT" || upsell.reason === "BREAKER" || upsell.reason === "CAPPED" || upsell.reason === "QUOTA" || upsell.reason === "PROXY_CAP" || upsell.reason === "PAUSED" || upsell.reason === "BYO_REQUIRED") {
|
|
9164
9287
|
if (!loggedIn) {
|
|
9165
|
-
console.log(
|
|
9288
|
+
console.log(import_chalk47.default.dim(" Options: `/login` for more free chats and requests, or `apiblaze llm set-key` to bring your own model key."));
|
|
9166
9289
|
} else {
|
|
9167
|
-
console.log(
|
|
9290
|
+
console.log(import_chalk47.default.dim(" Options: top up your wallet, or `apiblaze llm set-key` to bring your own model key (bypasses platform limits)."));
|
|
9168
9291
|
}
|
|
9169
9292
|
} else if (upsell.reason === "INFLIGHT") {
|
|
9170
|
-
console.log(
|
|
9293
|
+
console.log(import_chalk47.default.dim(" Another turn is still in flight \u2014 wait a moment and try again."));
|
|
9171
9294
|
}
|
|
9172
9295
|
console.log();
|
|
9173
9296
|
}
|
|
9174
|
-
var apichatsPath = () =>
|
|
9297
|
+
var apichatsPath = () => path7.join(getApiblazeDir(), "apichats.json");
|
|
9175
9298
|
function loadApichats() {
|
|
9176
9299
|
try {
|
|
9177
|
-
const list = JSON.parse(
|
|
9300
|
+
const list = JSON.parse(fs11.readFileSync(apichatsPath(), "utf-8"));
|
|
9178
9301
|
return Array.isArray(list) ? list : [];
|
|
9179
9302
|
} catch {
|
|
9180
9303
|
return [];
|
|
9181
9304
|
}
|
|
9182
9305
|
}
|
|
9183
9306
|
function writeApichats(list) {
|
|
9184
|
-
|
|
9185
|
-
|
|
9307
|
+
fs11.mkdirSync(getApiblazeDir(), { recursive: true });
|
|
9308
|
+
fs11.writeFileSync(apichatsPath(), JSON.stringify(list, null, 2), "utf-8");
|
|
9186
9309
|
try {
|
|
9187
|
-
|
|
9310
|
+
fs11.chmodSync(apichatsPath(), 384);
|
|
9188
9311
|
} catch {
|
|
9189
9312
|
}
|
|
9190
9313
|
}
|
|
@@ -9290,8 +9413,8 @@ async function openDirectProject(projectId, opts) {
|
|
|
9290
9413
|
else p.consumerAuth = true;
|
|
9291
9414
|
}
|
|
9292
9415
|
}
|
|
9293
|
-
console.log(` ${
|
|
9294
|
-
if (p.endUserId) console.log(` ${
|
|
9416
|
+
console.log(` ${import_chalk47.default.dim("Proxy:")} ${import_chalk47.default.bold(p.proxyUrl)}`);
|
|
9417
|
+
if (p.endUserId) console.log(` ${import_chalk47.default.dim("Acting as:")} ${import_chalk47.default.bold(p.endUserId)}`);
|
|
9295
9418
|
upsertApichat({
|
|
9296
9419
|
name: projectId,
|
|
9297
9420
|
target: p.proxyUrl,
|
|
@@ -9321,9 +9444,9 @@ async function openServerProxy(project) {
|
|
|
9321
9444
|
let consumerAuth = false;
|
|
9322
9445
|
if (acceptsApiKey) {
|
|
9323
9446
|
if (!dpKey) {
|
|
9324
|
-
console.log(
|
|
9447
|
+
console.log(import_chalk47.default.dim(` Minting an API key for tenant ${import_chalk47.default.bold(tenant2)} to query project ${import_chalk47.default.bold(project.projectName)}\u2026`));
|
|
9325
9448
|
dpKey = await mintDurableProxyKey(project.teamId, tenant2);
|
|
9326
|
-
console.log(` ${
|
|
9449
|
+
console.log(` ${import_chalk47.default.green("\u2714")} API key: ${import_chalk47.default.dim(maskKey(dpKey))}`);
|
|
9327
9450
|
}
|
|
9328
9451
|
} else {
|
|
9329
9452
|
consumerAuth = true;
|
|
@@ -9352,9 +9475,9 @@ async function openServerProxy(project) {
|
|
|
9352
9475
|
const spec2 = raw && (raw.spec ?? raw);
|
|
9353
9476
|
spinner.stop();
|
|
9354
9477
|
if (!spec2 || !(spec2.paths || spec2.openapi)) {
|
|
9355
|
-
console.log(
|
|
9478
|
+
console.log(import_chalk47.default.yellow(" This proxy has no OpenAPI spec yet \u2014 chat will have no tools. Build one with `apiblaze agent openapi`."));
|
|
9356
9479
|
} else {
|
|
9357
|
-
console.log(
|
|
9480
|
+
console.log(import_chalk47.default.dim(" Using the proxy's existing MCP catalogue (`apiblaze mcp` to rebuild it)."));
|
|
9358
9481
|
}
|
|
9359
9482
|
} catch (err) {
|
|
9360
9483
|
spinner.fail("Could not open the proxy.");
|
|
@@ -9386,15 +9509,15 @@ function discoverLocalSpecs() {
|
|
|
9386
9509
|
const found = [];
|
|
9387
9510
|
for (const n of known) {
|
|
9388
9511
|
try {
|
|
9389
|
-
if (
|
|
9512
|
+
if (fs11.statSync(path7.join(cwd, n)).isFile()) found.push(n);
|
|
9390
9513
|
} catch {
|
|
9391
9514
|
}
|
|
9392
9515
|
}
|
|
9393
9516
|
try {
|
|
9394
|
-
const files =
|
|
9517
|
+
const files = fs11.readdirSync(cwd).filter((f) => /\.(ya?ml|json)$/i.test(f) && !found.includes(f));
|
|
9395
9518
|
for (const f of files.slice(0, 60)) {
|
|
9396
9519
|
try {
|
|
9397
|
-
const head =
|
|
9520
|
+
const head = fs11.readFileSync(path7.join(cwd, f), "utf-8").slice(0, 4e3);
|
|
9398
9521
|
if (/["']?openapi["']?\s*:/i.test(head) || /["']?swagger["']?\s*:/i.test(head) || /^\s*paths\s*:/im.test(head) || /"paths"\s*:/.test(head)) {
|
|
9399
9522
|
found.push(f);
|
|
9400
9523
|
}
|
|
@@ -9410,7 +9533,7 @@ async function noArgsMenu(opts) {
|
|
|
9410
9533
|
const me = loadCredentials()?.apiblazeUserId;
|
|
9411
9534
|
const saved = loadApichats().filter((a) => a.anon ? true : a.ownerUserId !== void 0 && a.ownerUserId === me);
|
|
9412
9535
|
const choices = saved.map((a) => ({
|
|
9413
|
-
name: `Chat with ${
|
|
9536
|
+
name: `Chat with ${import_chalk47.default.bold(a.name)} ${import_chalk47.default.dim(`(${a.target})${a.messages && a.messages.length ? ` \xB7 ${a.messages.length} msgs` : ""}`)}`,
|
|
9414
9537
|
value: { type: "existing", a }
|
|
9415
9538
|
}));
|
|
9416
9539
|
const creds = loadCredentials();
|
|
@@ -9420,14 +9543,14 @@ async function noArgsMenu(opts) {
|
|
|
9420
9543
|
const proxies = (await getProjects(creds.teamId)).filter((pr) => !savedIds.has(pr.projectId));
|
|
9421
9544
|
for (const pr of proxies) {
|
|
9422
9545
|
choices.push({
|
|
9423
|
-
name: `Chat with ${
|
|
9546
|
+
name: `Chat with ${import_chalk47.default.bold(pr.projectName)} ${import_chalk47.default.dim(`(v${pr.apiVersion}) \xB7 your proxy`)}`,
|
|
9424
9547
|
value: { type: "server", project: pr }
|
|
9425
9548
|
});
|
|
9426
9549
|
}
|
|
9427
9550
|
} catch {
|
|
9428
9551
|
}
|
|
9429
9552
|
}
|
|
9430
|
-
choices.push({ name:
|
|
9553
|
+
choices.push({ name: import_chalk47.default.green("\uFF0B Create a new apichat"), value: { type: "new" } });
|
|
9431
9554
|
const { pick: pick2 } = await inquirer3.prompt([
|
|
9432
9555
|
{ type: "list", name: "pick", message: "What would you like to do?", choices }
|
|
9433
9556
|
]);
|
|
@@ -9530,36 +9653,36 @@ async function noArgsMenu(opts) {
|
|
|
9530
9653
|
async function runRepl(p, initialMessages) {
|
|
9531
9654
|
const { default: inquirer3 } = await import("inquirer");
|
|
9532
9655
|
const messages = (initialMessages ?? []).filter((m) => Array.isArray(m.parts));
|
|
9533
|
-
console.log("\n" +
|
|
9534
|
-
if (messages.length) console.log(
|
|
9656
|
+
console.log("\n" + import_chalk47.default.cyan.bold("Chat with your API") + import_chalk47.default.dim(` \xB7 ${p.mcpHost}`));
|
|
9657
|
+
if (messages.length) console.log(import_chalk47.default.dim(` Resumed \u2014 ${messages.length} prior messages.`));
|
|
9535
9658
|
const llm2 = loadLlmConfig();
|
|
9536
9659
|
console.log(
|
|
9537
|
-
|
|
9660
|
+
import_chalk47.default.dim(
|
|
9538
9661
|
llm2 ? `Using your local ${llm2.provider} key for the model. Type a question, or /exit. /login /claim manage your workspace.` : "Ask a question in plain English. /exit to quit \xB7 /login for more free chats \xB7 /claim to keep this workspace \xB7 `apiblaze llm set-key` for BYO models."
|
|
9539
9662
|
)
|
|
9540
9663
|
);
|
|
9541
9664
|
for (; ; ) {
|
|
9542
|
-
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message:
|
|
9665
|
+
const { input } = await inquirer3.prompt([{ type: "input", name: "input", message: import_chalk47.default.green("you \u203A") }]);
|
|
9543
9666
|
const text = (input ?? "").trim();
|
|
9544
9667
|
if (!text) continue;
|
|
9545
9668
|
if (["/exit", "/quit", "exit", "quit", ":q"].includes(text.toLowerCase())) break;
|
|
9546
9669
|
if (text === "/login") {
|
|
9547
9670
|
try {
|
|
9548
9671
|
await runLogin();
|
|
9549
|
-
console.log(
|
|
9672
|
+
console.log(import_chalk47.default.dim(" Logged in \u2014 history preserved. Keep chatting."));
|
|
9550
9673
|
} catch (err) {
|
|
9551
|
-
console.log(
|
|
9674
|
+
console.log(import_chalk47.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
9552
9675
|
}
|
|
9553
9676
|
continue;
|
|
9554
9677
|
}
|
|
9555
9678
|
if (text === "/claim") {
|
|
9556
9679
|
const justLoggedIn = !loadCredentials();
|
|
9557
9680
|
if (justLoggedIn) {
|
|
9558
|
-
console.log(
|
|
9681
|
+
console.log(import_chalk47.default.dim(" Logging in to claim your workspace\u2026"));
|
|
9559
9682
|
try {
|
|
9560
9683
|
await runLogin();
|
|
9561
9684
|
} catch (err) {
|
|
9562
|
-
console.log(
|
|
9685
|
+
console.log(import_chalk47.default.red(` Login failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
9563
9686
|
continue;
|
|
9564
9687
|
}
|
|
9565
9688
|
if (!loadCredentials()) continue;
|
|
@@ -9570,57 +9693,123 @@ async function runRepl(p, initialMessages) {
|
|
|
9570
9693
|
p.mcpHost = p.mcpHost.replace(".mcp.tryabz.run", ".mcp.abz.run");
|
|
9571
9694
|
p.anon = false;
|
|
9572
9695
|
claimApichat(p, loadCredentials()?.apiblazeUserId);
|
|
9573
|
-
console.log(
|
|
9696
|
+
console.log(import_chalk47.default.dim(` Workspace claimed \u2014 chat now routes on ${p.mcpHost}. History preserved.`));
|
|
9574
9697
|
}
|
|
9575
9698
|
} catch (err) {
|
|
9576
|
-
console.log(
|
|
9699
|
+
console.log(import_chalk47.default.red(` Claim failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
9577
9700
|
}
|
|
9578
9701
|
continue;
|
|
9579
9702
|
}
|
|
9580
9703
|
if (text === "/showauth") {
|
|
9581
9704
|
revealAuth = !revealAuth;
|
|
9582
|
-
console.log(
|
|
9705
|
+
console.log(import_chalk47.default.dim(revealAuth ? " The real API key will be shown in the next call's curl. /showauth again to re-mask." : " API key re-masked."));
|
|
9583
9706
|
continue;
|
|
9584
9707
|
}
|
|
9585
9708
|
if (text.startsWith("/")) {
|
|
9586
|
-
console.log(
|
|
9709
|
+
console.log(import_chalk47.default.dim(" Commands: /login /claim /showauth /exit"));
|
|
9587
9710
|
continue;
|
|
9588
9711
|
}
|
|
9589
9712
|
await replTurn(p, messages, text);
|
|
9590
9713
|
saveTranscript(p, messages);
|
|
9591
9714
|
}
|
|
9592
|
-
console.log(
|
|
9715
|
+
console.log(import_chalk47.default.dim("\nBye."));
|
|
9716
|
+
}
|
|
9717
|
+
function rememberCliOffer(projectId, cli, state) {
|
|
9718
|
+
const list = loadApichats();
|
|
9719
|
+
const i = list.findIndex((a) => a.projectId === projectId);
|
|
9720
|
+
if (i < 0) return;
|
|
9721
|
+
list[i].cliOffer = { ...list[i].cliOffer ?? {}, [cli]: state };
|
|
9722
|
+
list[i].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9723
|
+
writeApichats(list);
|
|
9724
|
+
}
|
|
9725
|
+
async function maybeInstallExternalCli(p, opts) {
|
|
9726
|
+
const spec2 = {
|
|
9727
|
+
name: p.projectId,
|
|
9728
|
+
url: `https://${p.mcpHost}/${p.version}/${p.environment}`,
|
|
9729
|
+
// consumerAuth means the door is a login — install bare, the CLI signs in.
|
|
9730
|
+
apiKey: p.consumerAuth ? void 0 : p.dpKey,
|
|
9731
|
+
projectLabel: p.projectId
|
|
9732
|
+
};
|
|
9733
|
+
const clis = detectExternalClis();
|
|
9734
|
+
if (opts.installMcp) {
|
|
9735
|
+
const want = opts.installMcp.toLowerCase();
|
|
9736
|
+
if (want !== "claude" && want !== "codex") fail4(`--install-mcp takes "claude" or "codex", not "${opts.installMcp}".`);
|
|
9737
|
+
const cli = clis.find((c) => c.kind === want);
|
|
9738
|
+
if (!cli) fail4(
|
|
9739
|
+
`${want === "claude" ? "Claude" : "Codex"} CLI not found on this machine.`,
|
|
9740
|
+
want === "claude" ? "Install it: npm install -g @anthropic-ai/claude-code" : "Install it: npm install -g @openai/codex"
|
|
9741
|
+
);
|
|
9742
|
+
const question2 = opts.prompt ?? (process.stdin.isTTY ? await askApiQuestion() : void 0);
|
|
9743
|
+
const ran2 = installAndDemo(cli, spec2, question2);
|
|
9744
|
+
if (ran2) rememberCliOffer(p.projectId, cli.kind, "installed");
|
|
9745
|
+
return ran2;
|
|
9746
|
+
}
|
|
9747
|
+
if (!process.stdin.isTTY || clis.length === 0) return false;
|
|
9748
|
+
const offer = loadApichats().find((a) => a.projectId === p.projectId)?.cliOffer ?? {};
|
|
9749
|
+
const fresh = clis.filter((c) => !offer[c.kind]);
|
|
9750
|
+
if (fresh.length === 0) return false;
|
|
9751
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
9752
|
+
const names = fresh.map((c) => c.label).join(" and ");
|
|
9753
|
+
const { pick: pick2 } = await inquirer3.prompt([{
|
|
9754
|
+
type: "list",
|
|
9755
|
+
name: "pick",
|
|
9756
|
+
message: `I see ${names} ${fresh.length > 1 ? "are" : "is"} installed on this computer. Add the MCP for this proxy so you can chat with your API from there directly?`,
|
|
9757
|
+
choices: [
|
|
9758
|
+
...fresh.map((c) => ({ name: `Yes \u2014 add it to ${c.label}`, value: c })),
|
|
9759
|
+
{ name: "No \u2014 chat here instead", value: "no" }
|
|
9760
|
+
]
|
|
9761
|
+
}]);
|
|
9762
|
+
if (pick2 === "no") {
|
|
9763
|
+
for (const c of fresh) rememberCliOffer(p.projectId, c.kind, "declined");
|
|
9764
|
+
return false;
|
|
9765
|
+
}
|
|
9766
|
+
const question = opts.prompt ?? await askApiQuestion();
|
|
9767
|
+
const ran = installAndDemo(pick2, spec2, question);
|
|
9768
|
+
if (ran) rememberCliOffer(p.projectId, pick2.kind, "installed");
|
|
9769
|
+
return ran;
|
|
9770
|
+
}
|
|
9771
|
+
async function askApiQuestion() {
|
|
9772
|
+
const { default: inquirer3 } = await import("inquirer");
|
|
9773
|
+
const { q } = await inquirer3.prompt([{
|
|
9774
|
+
type: "input",
|
|
9775
|
+
name: "q",
|
|
9776
|
+
message: "What question do you have for this API?"
|
|
9777
|
+
}]);
|
|
9778
|
+
const t = (q ?? "").trim();
|
|
9779
|
+
return t || void 0;
|
|
9593
9780
|
}
|
|
9594
9781
|
async function runApichat(opts) {
|
|
9595
9782
|
setVerbose(opts.verbose !== false);
|
|
9596
|
-
console.log(
|
|
9783
|
+
console.log(import_chalk47.default.bold("\napichat \u2014 turn any API into a chat\n"));
|
|
9597
9784
|
if (opts.target && !opts.openapispec) {
|
|
9598
9785
|
const { classifyTargetInput: classifyTargetInput2 } = await Promise.resolve().then(() => (init_spec_or_target(), spec_or_target_exports));
|
|
9599
9786
|
const c = await classifyTargetInput2(opts.target, fail4);
|
|
9600
9787
|
if (c.kind === "spec") {
|
|
9601
|
-
console.log(
|
|
9788
|
+
console.log(import_chalk47.default.dim(` --target is an OpenAPI document (${c.source}) \u2014 using it as the spec.`));
|
|
9602
9789
|
opts.openapispec = opts.target;
|
|
9603
9790
|
opts.target = void 0;
|
|
9604
9791
|
}
|
|
9605
9792
|
}
|
|
9606
9793
|
if (opts.project) {
|
|
9607
9794
|
const opened = await openDirectProject(opts.project, opts);
|
|
9795
|
+
if (await maybeInstallExternalCli(opened.p, opts)) return;
|
|
9608
9796
|
await runRepl(opened.p, opened.messages);
|
|
9609
9797
|
return;
|
|
9610
9798
|
}
|
|
9611
9799
|
if (!opts.openapispec && !opts.target) {
|
|
9612
9800
|
if (!process.stdin.isTTY) {
|
|
9613
|
-
fail4("No spec source. Pass --
|
|
9801
|
+
fail4("No spec source. Pass --target <server-url | openapi-file | openapi-url>.", GENERATOR_HINT);
|
|
9614
9802
|
}
|
|
9615
9803
|
const resumed = await noArgsMenu(opts);
|
|
9616
9804
|
if (resumed) {
|
|
9805
|
+
if (await maybeInstallExternalCli(resumed.p, opts)) return;
|
|
9617
9806
|
await runRepl(resumed.p, resumed.messages);
|
|
9618
9807
|
return;
|
|
9619
9808
|
}
|
|
9620
9809
|
}
|
|
9621
9810
|
const { spec: spec2, sourceUrl } = await loadSpec(opts);
|
|
9622
9811
|
const target = resolveTarget(spec2, opts, sourceUrl);
|
|
9623
|
-
console.log(` ${
|
|
9812
|
+
console.log(` ${import_chalk47.default.dim("Target:")} ${import_chalk47.default.bold(target)}`);
|
|
9624
9813
|
const auth = await resolveTargetAuth(spec2, opts);
|
|
9625
9814
|
if (auth && !process.stdin.isTTY && !opts.targetAuthEnv) {
|
|
9626
9815
|
fail4(
|
|
@@ -9629,7 +9818,7 @@ async function runApichat(opts) {
|
|
|
9629
9818
|
);
|
|
9630
9819
|
}
|
|
9631
9820
|
const p = await provision(spec2, target, opts);
|
|
9632
|
-
console.log(` ${
|
|
9821
|
+
console.log(` ${import_chalk47.default.dim("Proxy: ")} ${import_chalk47.default.bold(p.proxyUrl || `${p.projectId} v${p.version}`)}`);
|
|
9633
9822
|
upsertApichat({
|
|
9634
9823
|
name: p.projectId,
|
|
9635
9824
|
target,
|
|
@@ -9649,30 +9838,31 @@ async function runApichat(opts) {
|
|
|
9649
9838
|
const secret = await captureTargetSecret(auth, opts);
|
|
9650
9839
|
if (secret) await writeTargetAuth(p, auth, secret);
|
|
9651
9840
|
} else {
|
|
9652
|
-
console.log(
|
|
9841
|
+
console.log(import_chalk47.default.dim(" Target auth: none required."));
|
|
9653
9842
|
}
|
|
9654
9843
|
const specText = JSON.stringify(spec2);
|
|
9655
9844
|
await uploadSpec(p, specText, opts);
|
|
9656
9845
|
const mcpUrl = await publishMcp(p, spec2);
|
|
9657
9846
|
console.log();
|
|
9658
|
-
if (p.proxyUrl) console.log(` ${
|
|
9847
|
+
if (p.proxyUrl) console.log(` ${import_chalk47.default.green("\u2713")} proxy ${import_chalk47.default.bold(p.proxyUrl)}`);
|
|
9659
9848
|
if (mcpUrl) {
|
|
9660
|
-
console.log(` ${
|
|
9849
|
+
console.log(` ${import_chalk47.default.green("\u2713")} mcp ${import_chalk47.default.bold(mcpUrl)}`);
|
|
9661
9850
|
if (p.access === "invite") {
|
|
9662
|
-
console.log(
|
|
9663
|
-
console.log(
|
|
9851
|
+
console.log(import_chalk47.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: invite \u2014 only you + emails you pre-approve"));
|
|
9852
|
+
console.log(import_chalk47.default.dim(` Let others in: apiblaze preapprove someone@company.com${p.tenant ? ` --tenant ${p.tenant}` : ""} (or re-run with --access open)`));
|
|
9664
9853
|
} else {
|
|
9665
|
-
console.log(
|
|
9854
|
+
console.log(import_chalk47.default.dim(" Claude/ChatGPT-connectable (GitHub sign-in) \xB7 access: open \u2014 anyone who signs in can call this API"));
|
|
9666
9855
|
}
|
|
9667
9856
|
}
|
|
9668
9857
|
if (p.anon) {
|
|
9669
|
-
console.log(
|
|
9858
|
+
console.log(import_chalk47.default.dim("\n Anonymous workspace \u2014 /claim inside the chat to log in and keep it beyond 30 days."));
|
|
9670
9859
|
}
|
|
9860
|
+
if (await maybeInstallExternalCli(p, opts)) return;
|
|
9671
9861
|
await runRepl(p);
|
|
9672
9862
|
}
|
|
9673
9863
|
|
|
9674
9864
|
// src/commands/consumer.ts
|
|
9675
|
-
var
|
|
9865
|
+
var import_chalk48 = __toESM(require("chalk"));
|
|
9676
9866
|
var import_ora24 = __toESM(require("ora"));
|
|
9677
9867
|
init_admin();
|
|
9678
9868
|
init_resolve();
|
|
@@ -9694,7 +9884,7 @@ async function consumerFetch(creds, suffix, init) {
|
|
|
9694
9884
|
function requireConsumer2() {
|
|
9695
9885
|
const c = loadConsumer();
|
|
9696
9886
|
if (!c) {
|
|
9697
|
-
console.error(
|
|
9887
|
+
console.error(import_chalk48.default.red("Not logged in as a consumer. Run `apiblaze consumer login` first."));
|
|
9698
9888
|
process.exit(1);
|
|
9699
9889
|
}
|
|
9700
9890
|
return c;
|
|
@@ -9705,7 +9895,7 @@ async function runConsumerLogin(opts) {
|
|
|
9705
9895
|
let clientId = opts.client;
|
|
9706
9896
|
if (clientId) {
|
|
9707
9897
|
if (!tenant2) {
|
|
9708
|
-
console.error(
|
|
9898
|
+
console.error(import_chalk48.default.red("When using --client, also pass --tenant <slug> (it sets which portal/keys host to use)."));
|
|
9709
9899
|
process.exit(1);
|
|
9710
9900
|
}
|
|
9711
9901
|
} else {
|
|
@@ -9723,18 +9913,18 @@ async function runConsumerLogin(opts) {
|
|
|
9723
9913
|
const usable = (Array.isArray(clients) ? clients : []).filter((c) => c && (c.client_id || c.clientId));
|
|
9724
9914
|
const pick2 = usable.find((c) => c.is_default || c.default) ?? usable.find((c) => c.verified !== false) ?? usable[0];
|
|
9725
9915
|
if (!pick2) {
|
|
9726
|
-
console.error(
|
|
9916
|
+
console.error(import_chalk48.default.red(`Tenant "${tenant2}" has no login app configured. Set one up in the dashboard (or \`apiblaze create\` with auth).`));
|
|
9727
9917
|
process.exit(1);
|
|
9728
9918
|
}
|
|
9729
9919
|
clientId = pick2.client_id ?? pick2.clientId;
|
|
9730
9920
|
}
|
|
9731
|
-
console.log(`${
|
|
9921
|
+
console.log(`${import_chalk48.default.cyan("\u2192")} Logging in to ${import_chalk48.default.bold(tenant2)} as a consumer...`);
|
|
9732
9922
|
const result = await deviceLogin(clientId, DEFAULT_SCOPE, ({ verificationUri, userCode }) => {
|
|
9733
9923
|
console.log(`
|
|
9734
|
-
Open: ${
|
|
9735
|
-
console.log(` Code: ${
|
|
9924
|
+
Open: ${import_chalk48.default.underline(verificationUri)}`);
|
|
9925
|
+
console.log(` Code: ${import_chalk48.default.bold(userCode)}
|
|
9736
9926
|
`);
|
|
9737
|
-
console.log(
|
|
9927
|
+
console.log(import_chalk48.default.dim(" (opening your browser\u2026 waiting for you to finish)"));
|
|
9738
9928
|
});
|
|
9739
9929
|
const claims = result.idToken && decodeJwt2(result.idToken) || (decodeJwt2(result.accessToken) ?? {});
|
|
9740
9930
|
const creds = {
|
|
@@ -9749,7 +9939,7 @@ async function runConsumerLogin(opts) {
|
|
|
9749
9939
|
obtainedAt: Date.now()
|
|
9750
9940
|
};
|
|
9751
9941
|
saveConsumer(creds);
|
|
9752
|
-
console.log(
|
|
9942
|
+
console.log(import_chalk48.default.green(`\u2714 Logged in as consumer${creds.email ? ` ${creds.email}` : ""} on ${tenant2}.`));
|
|
9753
9943
|
}
|
|
9754
9944
|
async function runConsumerTokens(opts) {
|
|
9755
9945
|
const creds = requireConsumer2();
|
|
@@ -9762,18 +9952,18 @@ async function runConsumerTokens(opts) {
|
|
|
9762
9952
|
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));
|
|
9763
9953
|
return;
|
|
9764
9954
|
}
|
|
9765
|
-
console.log(`${
|
|
9955
|
+
console.log(`${import_chalk48.default.cyan("Consumer")} ${import_chalk48.default.bold(fresh.email ?? fresh.tenant)} on ${import_chalk48.default.bold(fresh.tenant)}
|
|
9766
9956
|
`);
|
|
9767
|
-
console.log(`${
|
|
9957
|
+
console.log(`${import_chalk48.default.bold("access_token")} ${import_chalk48.default.dim("exp " + (exp(fresh.accessToken) ?? "?"))}
|
|
9768
9958
|
${fresh.accessToken}
|
|
9769
9959
|
`);
|
|
9770
|
-
if (fresh.idToken) console.log(`${
|
|
9960
|
+
if (fresh.idToken) console.log(`${import_chalk48.default.bold("id_token")} ${import_chalk48.default.dim("exp " + (exp(fresh.idToken) ?? "?"))}
|
|
9771
9961
|
${fresh.idToken}
|
|
9772
9962
|
`);
|
|
9773
|
-
if (fresh.refreshToken) console.log(`${
|
|
9963
|
+
if (fresh.refreshToken) console.log(`${import_chalk48.default.bold("refresh_token")}
|
|
9774
9964
|
${fresh.refreshToken}
|
|
9775
9965
|
`);
|
|
9776
|
-
console.log(
|
|
9966
|
+
console.log(import_chalk48.default.dim("These are your own tokens \u2014 keep them secret."));
|
|
9777
9967
|
}
|
|
9778
9968
|
async function runConsumerApikeys(opts) {
|
|
9779
9969
|
const creds = requireConsumer2();
|
|
@@ -9783,8 +9973,8 @@ async function runConsumerApikeys(opts) {
|
|
|
9783
9973
|
const revealed = await consumerFetch(list.creds, "/apikeys/reveal").catch(() => ({ status: 0, data: null, creds: list.creds }));
|
|
9784
9974
|
spinner.stop();
|
|
9785
9975
|
if (list.status >= 400) {
|
|
9786
|
-
console.error(
|
|
9787
|
-
if (list.status === 401) console.error(
|
|
9976
|
+
console.error(import_chalk48.default.red(`Failed to list keys (${list.status}): ${list.data?.error ?? ""}`));
|
|
9977
|
+
if (list.status === 401) console.error(import_chalk48.default.dim("Your consumer session may have expired \u2014 run `apiblaze consumer login` again."));
|
|
9788
9978
|
process.exit(1);
|
|
9789
9979
|
}
|
|
9790
9980
|
const keys = list.data?.keys ?? [];
|
|
@@ -9792,16 +9982,16 @@ async function runConsumerApikeys(opts) {
|
|
|
9792
9982
|
if (opts.json) {
|
|
9793
9983
|
console.log(JSON.stringify({ keys, revealed: revealMap }, null, 2));
|
|
9794
9984
|
} else if (!keys.length) {
|
|
9795
|
-
console.log(
|
|
9985
|
+
console.log(import_chalk48.default.yellow("No API keys yet."));
|
|
9796
9986
|
} else {
|
|
9797
9987
|
for (const k of keys) {
|
|
9798
9988
|
const clear = revealMap[k.environment]?.key;
|
|
9799
|
-
const shown = clear ?
|
|
9800
|
-
const exp = k.expires_at ?
|
|
9801
|
-
console.log(` ${
|
|
9989
|
+
const shown = clear ? import_chalk48.default.green(clear) : import_chalk48.default.dim(`${k.key_prefix ?? ""}\u2026${k.key_suffix ?? ""}`);
|
|
9990
|
+
const exp = k.expires_at ? import_chalk48.default.dim(`exp ${k.expires_at}`) : import_chalk48.default.dim("no expiry");
|
|
9991
|
+
console.log(` ${import_chalk48.default.bold(k.environment ?? "")} ${shown} ${exp} ${import_chalk48.default.dim(k.description ?? "")}`);
|
|
9802
9992
|
}
|
|
9803
9993
|
if (Object.keys(revealMap).length === 0 && keys.some((k) => !k.expires_at)) {
|
|
9804
|
-
console.log(
|
|
9994
|
+
console.log(import_chalk48.default.dim("\n(Only expiring keys can be shown in clear; non-expiring keys show a prefix only.)"));
|
|
9805
9995
|
}
|
|
9806
9996
|
}
|
|
9807
9997
|
if (opts.json) return;
|
|
@@ -9823,35 +10013,35 @@ async function runConsumerApikeys(opts) {
|
|
|
9823
10013
|
}
|
|
9824
10014
|
s2.succeed("Key created.");
|
|
9825
10015
|
const key = created.data?.key ?? created.data?.fullKey;
|
|
9826
|
-
if (key) console.log(` ${
|
|
9827
|
-
else console.log(
|
|
10016
|
+
if (key) console.log(` ${import_chalk48.default.green(key)} ${import_chalk48.default.dim("(shown once \u2014 store it now)")}`);
|
|
10017
|
+
else console.log(import_chalk48.default.dim(" Key created; run `apiblaze consumer apikeys` to reveal it if it expires."));
|
|
9828
10018
|
}
|
|
9829
10019
|
|
|
9830
10020
|
// src/commands/sidecar.ts
|
|
9831
|
-
var
|
|
10021
|
+
var import_chalk49 = __toESM(require("chalk"));
|
|
9832
10022
|
var import_ora25 = __toESM(require("ora"));
|
|
9833
|
-
var
|
|
9834
|
-
var
|
|
10023
|
+
var fs12 = __toESM(require("fs"));
|
|
10024
|
+
var path8 = __toESM(require("path"));
|
|
9835
10025
|
init_admin();
|
|
9836
10026
|
init_resolve();
|
|
9837
10027
|
init_auth();
|
|
9838
10028
|
function detectNextProject(root) {
|
|
9839
|
-
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) =>
|
|
10029
|
+
const hasConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].some((f) => fs12.existsSync(path8.join(root, f)));
|
|
9840
10030
|
let hasDep = false;
|
|
9841
10031
|
try {
|
|
9842
|
-
const pkg = JSON.parse(
|
|
10032
|
+
const pkg = JSON.parse(fs12.readFileSync(path8.join(root, "package.json"), "utf8"));
|
|
9843
10033
|
hasDep = !!(pkg.dependencies?.next || pkg.devDependencies?.next);
|
|
9844
10034
|
} catch {
|
|
9845
10035
|
}
|
|
9846
|
-
const appDir =
|
|
9847
|
-
const pagesDir =
|
|
10036
|
+
const appDir = fs12.existsSync(path8.join(root, "app")) || fs12.existsSync(path8.join(root, "src", "app"));
|
|
10037
|
+
const pagesDir = fs12.existsSync(path8.join(root, "pages")) || fs12.existsSync(path8.join(root, "src", "pages"));
|
|
9848
10038
|
return { found: hasConfig || hasDep || appDir || pagesDir, router: appDir ? "app" : pagesDir ? "pages" : null };
|
|
9849
10039
|
}
|
|
9850
10040
|
function upsertEnvLocal(root, token) {
|
|
9851
|
-
const p =
|
|
10041
|
+
const p = path8.join(root, ".env.local");
|
|
9852
10042
|
let existing = "";
|
|
9853
10043
|
try {
|
|
9854
|
-
existing =
|
|
10044
|
+
existing = fs12.readFileSync(p, "utf8");
|
|
9855
10045
|
} catch {
|
|
9856
10046
|
}
|
|
9857
10047
|
const had = /^APIBLAZE_API_KEY=/m.test(existing) || /^APIBLAZE_TOKEN=/m.test(existing);
|
|
@@ -9866,15 +10056,15 @@ function upsertEnvLocal(root, token) {
|
|
|
9866
10056
|
next = (next.endsWith("\n") ? next : next + "\n") + `APIBLAZE_SIDECAR_VERBOSE=true
|
|
9867
10057
|
`;
|
|
9868
10058
|
}
|
|
9869
|
-
|
|
10059
|
+
fs12.writeFileSync(p, next);
|
|
9870
10060
|
return had ? "rotated" : "created";
|
|
9871
10061
|
}
|
|
9872
10062
|
function installSidecarPackage(root) {
|
|
9873
|
-
if (
|
|
9874
|
-
console.log(` ${
|
|
10063
|
+
if (fs12.existsSync(path8.join(root, "node_modules", "apiblaze", "package.json"))) {
|
|
10064
|
+
console.log(` ${import_chalk49.default.green("\u2713")} apiblaze package already installed`);
|
|
9875
10065
|
return;
|
|
9876
10066
|
}
|
|
9877
|
-
const has = (f) =>
|
|
10067
|
+
const has = (f) => fs12.existsSync(path8.join(root, f));
|
|
9878
10068
|
const pm = has("bun.lockb") || has("bun.lock") ? { cmd: "bun", add: "add" } : has("pnpm-lock.yaml") ? { cmd: "pnpm", add: "add" } : has("yarn.lock") ? { cmd: "yarn", add: "add" } : { cmd: "npm", add: "install" };
|
|
9879
10069
|
const spinner = (0, import_ora25.default)(`Installing the apiblaze package (${pm.cmd})\u2026`).start();
|
|
9880
10070
|
try {
|
|
@@ -9882,12 +10072,12 @@ function installSidecarPackage(root) {
|
|
|
9882
10072
|
execSync(`${pm.cmd} ${pm.add} apiblaze`, { cwd: root, stdio: "ignore" });
|
|
9883
10073
|
spinner.succeed("Installed apiblaze (the sidecar runtime).");
|
|
9884
10074
|
} catch {
|
|
9885
|
-
spinner.warn(`Couldn't auto-install \u2014 run ${
|
|
10075
|
+
spinner.warn(`Couldn't auto-install \u2014 run ${import_chalk49.default.cyan(`${pm.cmd} ${pm.add} apiblaze`)} yourself before ${import_chalk49.default.cyan("npm run dev")}.`);
|
|
9886
10076
|
}
|
|
9887
10077
|
}
|
|
9888
10078
|
function readEnvKey(root) {
|
|
9889
10079
|
try {
|
|
9890
|
-
const s =
|
|
10080
|
+
const s = fs12.readFileSync(path8.join(root, ".env.local"), "utf8");
|
|
9891
10081
|
const m = s.match(/^APIBLAZE_API_KEY=(.+)$/m) ?? s.match(/^APIBLAZE_TOKEN=(.+)$/m);
|
|
9892
10082
|
return m ? m[1].trim() : null;
|
|
9893
10083
|
} catch {
|
|
@@ -9895,16 +10085,16 @@ function readEnvKey(root) {
|
|
|
9895
10085
|
}
|
|
9896
10086
|
}
|
|
9897
10087
|
function ensureGitignored(root) {
|
|
9898
|
-
const p =
|
|
10088
|
+
const p = path8.join(root, ".gitignore");
|
|
9899
10089
|
let c = "";
|
|
9900
10090
|
try {
|
|
9901
|
-
c =
|
|
10091
|
+
c = fs12.readFileSync(p, "utf8");
|
|
9902
10092
|
} catch {
|
|
9903
10093
|
}
|
|
9904
|
-
if (!/^\.env\.local$/m.test(c) && !/^\.env\*/m.test(c))
|
|
10094
|
+
if (!/^\.env\.local$/m.test(c) && !/^\.env\*/m.test(c)) fs12.writeFileSync(p, (c && !c.endsWith("\n") ? c + "\n" : c) + ".env.local\n");
|
|
9905
10095
|
}
|
|
9906
10096
|
function wireInstrumentation(root) {
|
|
9907
|
-
const existing = ["instrumentation.ts", "instrumentation.js",
|
|
10097
|
+
const existing = ["instrumentation.ts", "instrumentation.js", path8.join("src", "instrumentation.ts")].map((c) => path8.join(root, c)).find((f) => fs12.existsSync(f));
|
|
9908
10098
|
const body = `import { register as apiblaze } from "apiblaze/sidecar";
|
|
9909
10099
|
|
|
9910
10100
|
export function register() {
|
|
@@ -9912,18 +10102,18 @@ export function register() {
|
|
|
9912
10102
|
}
|
|
9913
10103
|
`;
|
|
9914
10104
|
if (!existing) {
|
|
9915
|
-
|
|
10105
|
+
fs12.writeFileSync(path8.join(root, "instrumentation.ts"), body);
|
|
9916
10106
|
return "created";
|
|
9917
10107
|
}
|
|
9918
|
-
const cur =
|
|
10108
|
+
const cur = fs12.readFileSync(existing, "utf8");
|
|
9919
10109
|
if (cur.includes("apiblaze/sidecar")) return "present";
|
|
9920
10110
|
if (/export\s+function\s+register\s*\(/.test(cur)) {
|
|
9921
|
-
|
|
10111
|
+
fs12.writeFileSync(existing, `import { register as apiblaze } from "apiblaze/sidecar";
|
|
9922
10112
|
` + cur.replace(/export\s+function\s+register\s*\(\s*\)\s*\{/, (m) => `${m}
|
|
9923
10113
|
apiblaze();`));
|
|
9924
10114
|
return "patched";
|
|
9925
10115
|
}
|
|
9926
|
-
|
|
10116
|
+
fs12.writeFileSync(existing, `import { register as apiblaze } from "apiblaze/sidecar";
|
|
9927
10117
|
${cur}
|
|
9928
10118
|
// call apiblaze() inside your register() export.
|
|
9929
10119
|
`);
|
|
@@ -10002,17 +10192,17 @@ export default async function Page() {
|
|
|
10002
10192
|
function generateInspector(root, router) {
|
|
10003
10193
|
try {
|
|
10004
10194
|
if (router === "pages") {
|
|
10005
|
-
const dir2 =
|
|
10006
|
-
const f2 =
|
|
10007
|
-
|
|
10008
|
-
return
|
|
10009
|
-
}
|
|
10010
|
-
const base2 =
|
|
10011
|
-
const dir =
|
|
10012
|
-
|
|
10013
|
-
const f =
|
|
10014
|
-
|
|
10015
|
-
return
|
|
10195
|
+
const dir2 = fs12.existsSync(path8.join(root, "src", "pages")) ? path8.join(root, "src", "pages") : path8.join(root, "pages");
|
|
10196
|
+
const f2 = path8.join(dir2, "abz-inspector.tsx");
|
|
10197
|
+
fs12.writeFileSync(f2, INSPECTOR_PAGE);
|
|
10198
|
+
return path8.relative(root, f2);
|
|
10199
|
+
}
|
|
10200
|
+
const base2 = fs12.existsSync(path8.join(root, "src", "app")) ? path8.join(root, "src", "app") : path8.join(root, "app");
|
|
10201
|
+
const dir = path8.join(base2, "abz-inspector");
|
|
10202
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
10203
|
+
const f = path8.join(dir, "page.tsx");
|
|
10204
|
+
fs12.writeFileSync(f, INSPECTOR_PAGE);
|
|
10205
|
+
return path8.relative(root, f);
|
|
10016
10206
|
} catch {
|
|
10017
10207
|
return null;
|
|
10018
10208
|
}
|
|
@@ -10033,29 +10223,29 @@ async function runAnonymousInit(root, router, opts) {
|
|
|
10033
10223
|
if (out.cp_key && out.team_id) saveAnonCred2(out.cp_key, out.team_id, out.claim_code);
|
|
10034
10224
|
const envState = upsertEnvLocal(root, out.token);
|
|
10035
10225
|
ensureGitignored(root);
|
|
10036
|
-
console.log(` ${
|
|
10037
|
-
console.log(` ${
|
|
10226
|
+
console.log(` ${import_chalk49.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
10227
|
+
console.log(` ${import_chalk49.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
10038
10228
|
installSidecarPackage(root);
|
|
10039
10229
|
let inspectorPath = null;
|
|
10040
10230
|
if (!opts.noInspector) {
|
|
10041
10231
|
inspectorPath = generateInspector(root, router);
|
|
10042
|
-
if (inspectorPath) console.log(` ${
|
|
10232
|
+
if (inspectorPath) console.log(` ${import_chalk49.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
10043
10233
|
}
|
|
10044
10234
|
console.log("");
|
|
10045
|
-
console.log(
|
|
10046
|
-
console.log(` 1. ${
|
|
10235
|
+
console.log(import_chalk49.default.bold("Done (no account needed). What happens next:"));
|
|
10236
|
+
console.log(` 1. ${import_chalk49.default.cyan("npm run dev")} and use your app.`);
|
|
10047
10237
|
console.log(` 2. Each external origin your app calls is logged in the console \u2014 approve one with:`);
|
|
10048
|
-
console.log(` ${
|
|
10238
|
+
console.log(` ${import_chalk49.default.cyan("apiblaze sidecar approve api.stripe.com")} (no login needed)`);
|
|
10049
10239
|
console.log("");
|
|
10050
|
-
console.log(
|
|
10051
|
-
console.log(` ${
|
|
10052
|
-
console.log(
|
|
10240
|
+
console.log(import_chalk49.default.bold(" \u{1F511} Keep your setup \u2014 claim it into an account:"));
|
|
10241
|
+
console.log(` ${import_chalk49.default.cyan("apiblaze login")} then ${import_chalk49.default.cyan("apiblaze claim")} ${import_chalk49.default.dim("(no code needed here)")}`);
|
|
10242
|
+
console.log(import_chalk49.default.dim(` From another machine: apiblaze claim ${out.claim_code} \xB7 expires in 30 days`));
|
|
10053
10243
|
}
|
|
10054
10244
|
async function runSidecar(opts) {
|
|
10055
|
-
const root =
|
|
10245
|
+
const root = path8.resolve(opts.dir ?? process.cwd());
|
|
10056
10246
|
const detected = detectNextProject(root);
|
|
10057
10247
|
if (!detected.found) {
|
|
10058
|
-
console.log(
|
|
10248
|
+
console.log(import_chalk49.default.yellow(`No Next.js project detected in ${root}.`));
|
|
10059
10249
|
console.log("Create one (e.g. `npx create-next-app`) and re-run `apiblaze init` inside it.");
|
|
10060
10250
|
return;
|
|
10061
10251
|
}
|
|
@@ -10066,10 +10256,10 @@ async function runSidecar(opts) {
|
|
|
10066
10256
|
if (!loadCredentials()) {
|
|
10067
10257
|
upsertEnvLocal(root, readEnvKey(root));
|
|
10068
10258
|
ensureGitignored(root);
|
|
10069
|
-
console.log(` ${
|
|
10070
|
-
console.log(` ${
|
|
10259
|
+
console.log(` ${import_chalk49.default.green("\u2713")} .env.local present (APIBLAZE_API_KEY) \u2014 reusing`);
|
|
10260
|
+
console.log(` ${import_chalk49.default.green("\u2713")} instrumentation.ts ${wireInstrumentation(root)}`);
|
|
10071
10261
|
installSidecarPackage(root);
|
|
10072
|
-
console.log(
|
|
10262
|
+
console.log(import_chalk49.default.dim(" Log in and run `apiblaze claim <code>` to keep this setup, or `apiblaze login` to manage it."));
|
|
10073
10263
|
return;
|
|
10074
10264
|
}
|
|
10075
10265
|
const { teamId, teamName } = await resolveTeam(opts.team);
|
|
@@ -10092,38 +10282,38 @@ async function runSidecar(opts) {
|
|
|
10092
10282
|
throw err;
|
|
10093
10283
|
}
|
|
10094
10284
|
} else {
|
|
10095
|
-
console.log(
|
|
10285
|
+
console.log(import_chalk49.default.dim(` Reusing the existing APIBLAZE_API_KEY (run with --rotate to mint a fresh one, or --team <name> to switch teams).`));
|
|
10096
10286
|
}
|
|
10097
10287
|
const envState = upsertEnvLocal(root, token);
|
|
10098
10288
|
ensureGitignored(root);
|
|
10099
|
-
console.log(` ${
|
|
10289
|
+
console.log(` ${import_chalk49.default.green("\u2713")} .env.local ${envState} (APIBLAZE_API_KEY) \u2014 gitignored`);
|
|
10100
10290
|
const wireState = wireInstrumentation(root);
|
|
10101
|
-
console.log(` ${
|
|
10291
|
+
console.log(` ${import_chalk49.default.green("\u2713")} instrumentation.ts ${wireState}`);
|
|
10102
10292
|
installSidecarPackage(root);
|
|
10103
10293
|
let inspectorPath = null;
|
|
10104
10294
|
if (!opts.noInspector) {
|
|
10105
10295
|
inspectorPath = generateInspector(root, detected.router);
|
|
10106
|
-
if (inspectorPath) console.log(` ${
|
|
10296
|
+
if (inspectorPath) console.log(` ${import_chalk49.default.green("\u2713")} inspector at ${inspectorPath}`);
|
|
10107
10297
|
}
|
|
10108
10298
|
console.log("");
|
|
10109
|
-
console.log(
|
|
10110
|
-
console.log(` 1. ${
|
|
10111
|
-
console.log(` 2. The origins your app calls appear as ${
|
|
10112
|
-
console.log(` 3. Approve the ones to route: ${
|
|
10299
|
+
console.log(import_chalk49.default.bold("Done. What happens next:"));
|
|
10300
|
+
console.log(` 1. ${import_chalk49.default.cyan("npm run dev")} and use your app \u2014 it works exactly as before (all calls go direct).`);
|
|
10301
|
+
console.log(` 2. The origins your app calls appear as ${import_chalk49.default.bold("candidates")} \u2014 list them: ${import_chalk49.default.cyan("apiblaze sidecar")}`);
|
|
10302
|
+
console.log(` 3. Approve the ones to route: ${import_chalk49.default.cyan("apiblaze sidecar approve api.stripe.com")} (or in the dashboard)`);
|
|
10113
10303
|
console.log(` \u2026within ~5 min your app starts routing that origin through APIblaze.`);
|
|
10114
|
-
if (inspectorPath) console.log(` \u2022 Try it now: open ${
|
|
10115
|
-
if (switchingTeam) console.log(
|
|
10304
|
+
if (inspectorPath) console.log(` \u2022 Try it now: open ${import_chalk49.default.underline("http://localhost:3000/abz-inspector")} (dev only; rm ${path8.dirname(inspectorPath)} before shipping)`);
|
|
10305
|
+
if (switchingTeam) console.log(import_chalk49.default.dim(` \u2022 Approved origins are per-team \u2014 re-approve them on ${teamName ?? teamId} with \`apiblaze sidecar approve <origin>\`.`));
|
|
10116
10306
|
console.log("");
|
|
10117
|
-
console.log(
|
|
10118
|
-
console.log(
|
|
10119
|
-
console.log(
|
|
10307
|
+
console.log(import_chalk49.default.dim(" Manage: apiblaze sidecar (list/approve/deny/remove)"));
|
|
10308
|
+
console.log(import_chalk49.default.dim(" Rotate: apiblaze init --rotate \xB7 Switch team: apiblaze init --team <name>"));
|
|
10309
|
+
console.log(import_chalk49.default.dim(" Turn off: set APIBLAZE_SIDECAR=off in .env.local (flip back to on anytime; key stays put)."));
|
|
10120
10310
|
console.log("");
|
|
10121
|
-
console.log(
|
|
10122
|
-
console.log(
|
|
10311
|
+
console.log(import_chalk49.default.yellow(" \u26A0 APIBLAZE_API_KEY is long-lived and lets a holder call your team's proxies. Never commit it."));
|
|
10312
|
+
console.log(import_chalk49.default.dim(" Your control-plane login stays in ~/.apiblaze \u2014 it never entered this project."));
|
|
10123
10313
|
}
|
|
10124
10314
|
|
|
10125
10315
|
// src/commands/origins.ts
|
|
10126
|
-
var
|
|
10316
|
+
var import_chalk50 = __toESM(require("chalk"));
|
|
10127
10317
|
var import_ora26 = __toESM(require("ora"));
|
|
10128
10318
|
init_admin();
|
|
10129
10319
|
init_resolve();
|
|
@@ -10134,7 +10324,7 @@ async function runOriginsList(opts) {
|
|
|
10134
10324
|
if (!loadCredentials()) {
|
|
10135
10325
|
const cred = loadAnonCred();
|
|
10136
10326
|
if (!cred) {
|
|
10137
|
-
console.log(
|
|
10327
|
+
console.log(import_chalk50.default.yellow("No anonymous workspace here. Run `apiblaze init` first."));
|
|
10138
10328
|
return;
|
|
10139
10329
|
}
|
|
10140
10330
|
out = await cpFetch(cred.cp_key, `/teams/${encodeURIComponent(cred.team_id)}/sidecar/candidates`, { method: "GET" });
|
|
@@ -10152,27 +10342,27 @@ async function runOriginsList(opts) {
|
|
|
10152
10342
|
}
|
|
10153
10343
|
const routed = out.routed ?? [];
|
|
10154
10344
|
const candidates = out.candidates ?? [];
|
|
10155
|
-
console.log(
|
|
10345
|
+
console.log(import_chalk50.default.bold(`
|
|
10156
10346
|
Routed through APIblaze (${routed.length})`));
|
|
10157
|
-
if (!routed.length) console.log(
|
|
10158
|
-
for (const r of routed) console.log(` ${
|
|
10159
|
-
console.log(
|
|
10347
|
+
if (!routed.length) console.log(import_chalk50.default.dim(" none yet"));
|
|
10348
|
+
for (const r of routed) console.log(` ${import_chalk50.default.green("\u25CF")} ${r.sidecar_origin} ${import_chalk50.default.dim(`\u2192 ${r.project_id}`)}`);
|
|
10349
|
+
console.log(import_chalk50.default.bold(`
|
|
10160
10350
|
Candidates \u2014 going direct, not yet approved (${candidates.length})`));
|
|
10161
|
-
if (!candidates.length) console.log(
|
|
10351
|
+
if (!candidates.length) console.log(import_chalk50.default.dim(" none \u2014 run your app to discover the origins it calls"));
|
|
10162
10352
|
for (const c of candidates) {
|
|
10163
|
-
console.log(` ${
|
|
10353
|
+
console.log(` ${import_chalk50.default.yellow("\u25CB")} ${c.origin} ${import_chalk50.default.dim(`seen ${c.request_count}\xD7, last ${c.last_seen}`)}`);
|
|
10164
10354
|
}
|
|
10165
10355
|
if (candidates.length) {
|
|
10166
|
-
console.log(
|
|
10356
|
+
console.log(import_chalk50.default.dim(`
|
|
10167
10357
|
Approve: apiblaze sidecar approve ${candidates[0].origin.replace("https://", "")}`));
|
|
10168
|
-
console.log(
|
|
10358
|
+
console.log(import_chalk50.default.dim(` Dismiss: apiblaze sidecar deny ${candidates[0].origin.replace("https://", "")}`));
|
|
10169
10359
|
}
|
|
10170
10360
|
}
|
|
10171
10361
|
async function runOriginsApprove(origin, opts) {
|
|
10172
10362
|
if (!loadCredentials()) {
|
|
10173
10363
|
const cred = loadAnonCred();
|
|
10174
10364
|
if (!cred) {
|
|
10175
|
-
console.error(
|
|
10365
|
+
console.error(import_chalk50.default.red("Not logged in and no anonymous workspace. Run `apiblaze init` first."));
|
|
10176
10366
|
process.exit(1);
|
|
10177
10367
|
}
|
|
10178
10368
|
const spinner2 = (0, import_ora26.default)(`Approving ${origin} (anonymous)...`).start();
|
|
@@ -10225,13 +10415,13 @@ async function runOriginsRemove(origin, opts) {
|
|
|
10225
10415
|
}
|
|
10226
10416
|
|
|
10227
10417
|
// src/commands/op.ts
|
|
10228
|
-
var
|
|
10418
|
+
var import_chalk52 = __toESM(require("chalk"));
|
|
10229
10419
|
init_auth();
|
|
10230
10420
|
init_trace();
|
|
10231
10421
|
init_types();
|
|
10232
10422
|
|
|
10233
10423
|
// src/commands/op-billing.ts
|
|
10234
|
-
var
|
|
10424
|
+
var import_chalk51 = __toESM(require("chalk"));
|
|
10235
10425
|
init_admin();
|
|
10236
10426
|
var SANDBOX = {
|
|
10237
10427
|
teamId: "team_1782844865835_zujrf",
|
|
@@ -10290,7 +10480,7 @@ async function rowsForRays(project, version2, tenant2, rays) {
|
|
|
10290
10480
|
}
|
|
10291
10481
|
function printDoors(data) {
|
|
10292
10482
|
const checks = [];
|
|
10293
|
-
console.log(
|
|
10483
|
+
console.log(import_chalk51.default.bold("\n Doors \u2014 is every way in metered?\n"));
|
|
10294
10484
|
const doors = data?.doors ?? [];
|
|
10295
10485
|
const metered = doors.filter((d) => d.verdict === "metered");
|
|
10296
10486
|
const allowed = doors.filter((d) => d.verdict === "allowed-free");
|
|
@@ -10299,29 +10489,29 @@ function printDoors(data) {
|
|
|
10299
10489
|
const errs = data?.errors ?? [];
|
|
10300
10490
|
const routeAuditBroke = errs.some((e) => e.startsWith("zone "));
|
|
10301
10491
|
const devAuditBroke = errs.some((e) => e.startsWith("workers.dev audit"));
|
|
10302
|
-
console.log(
|
|
10303
|
-
for (const d of metered) console.log(
|
|
10304
|
-
console.log(
|
|
10492
|
+
console.log(import_chalk51.default.dim(` ${metered.length} route(s) behind main-proxy (metered)`));
|
|
10493
|
+
for (const d of metered) console.log(import_chalk51.default.green(` \u2713 ${d.pattern}`));
|
|
10494
|
+
console.log(import_chalk51.default.dim(`
|
|
10305
10495
|
${allowed.length} route(s) free ON PURPOSE`));
|
|
10306
10496
|
for (const d of allowed) {
|
|
10307
|
-
console.log(
|
|
10308
|
-
console.log(
|
|
10497
|
+
console.log(import_chalk51.default.cyan(` \u2022 ${d.pattern}`) + import_chalk51.default.dim(` \u2192 ${d.script}`));
|
|
10498
|
+
console.log(import_chalk51.default.dim(` ${d.why}`));
|
|
10309
10499
|
}
|
|
10310
10500
|
if (known.length) {
|
|
10311
|
-
console.log(
|
|
10501
|
+
console.log(import_chalk51.default.yellow(`
|
|
10312
10502
|
${known.length} route(s) KNOWN OPEN \u2014 unmetered, not yet closed`));
|
|
10313
10503
|
for (const d of known) {
|
|
10314
|
-
console.log(
|
|
10315
|
-
console.log(
|
|
10504
|
+
console.log(import_chalk51.default.yellow(` ! ${d.pattern}`) + import_chalk51.default.dim(` \u2192 ${d.script}`));
|
|
10505
|
+
console.log(import_chalk51.default.dim(` ${d.why}`));
|
|
10316
10506
|
}
|
|
10317
10507
|
checks.push({ name: "no known-open doors", status: "KNOWN", detail: `${known.length} unmetered route(s) still open \u2014 see above` });
|
|
10318
10508
|
}
|
|
10319
10509
|
if (stray.length) {
|
|
10320
|
-
console.log(
|
|
10510
|
+
console.log(import_chalk51.default.red(`
|
|
10321
10511
|
${stray.length} STRAY route(s) \u2014 not main-proxy, not on the allowlist`));
|
|
10322
10512
|
for (const d of stray) {
|
|
10323
|
-
console.log(
|
|
10324
|
-
console.log(
|
|
10513
|
+
console.log(import_chalk51.default.red(` \u2717 ${d.pattern}`) + import_chalk51.default.dim(` \u2192 ${d.script}`));
|
|
10514
|
+
console.log(import_chalk51.default.dim(` ${d.why}`));
|
|
10325
10515
|
}
|
|
10326
10516
|
checks.push({ name: "no stray routes", status: "FAIL", detail: `${stray.length}: ${stray.map((s) => s.pattern).join(", ")}` });
|
|
10327
10517
|
} else if (routeAuditBroke) {
|
|
@@ -10332,22 +10522,22 @@ function printDoors(data) {
|
|
|
10332
10522
|
const wd = data?.workers_dev ?? {};
|
|
10333
10523
|
const open = wd.enabled ?? [];
|
|
10334
10524
|
if (open.length) {
|
|
10335
|
-
console.log(
|
|
10525
|
+
console.log(import_chalk51.default.red(`
|
|
10336
10526
|
${open.length} of ${wd.total} worker(s) reachable on *.workers.dev`));
|
|
10337
|
-
for (const s of open) console.log(
|
|
10338
|
-
console.log(
|
|
10527
|
+
for (const s of open) console.log(import_chalk51.default.red(` \u2717 ${s.script}.workers.dev`) + import_chalk51.default.dim(` (enabled=${s.enabled} previews=${s.previews})`));
|
|
10528
|
+
console.log(import_chalk51.default.dim(" A workers.dev hostname bypasses every CF route, WAF rule and the credit gate."));
|
|
10339
10529
|
checks.push({ name: "no workers.dev doors", status: "FAIL", detail: `${open.length} script(s) publicly reachable: ${open.map((s) => s.script).join(", ")}` });
|
|
10340
10530
|
} else if (devAuditBroke || !wd.total) {
|
|
10341
10531
|
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." });
|
|
10342
10532
|
} else {
|
|
10343
|
-
console.log(
|
|
10533
|
+
console.log(import_chalk51.default.green(`
|
|
10344
10534
|
\u2713 0 of ${wd.total} workers reachable on *.workers.dev`));
|
|
10345
10535
|
checks.push({ name: "no workers.dev doors", status: "PASS", detail: `all ${wd.total} scripts have workers.dev + previews disabled` });
|
|
10346
10536
|
}
|
|
10347
|
-
if (data?.how_to_fix) console.log(
|
|
10348
|
-
${data.reason}`) +
|
|
10537
|
+
if (data?.how_to_fix) console.log(import_chalk51.default.yellow(`
|
|
10538
|
+
${data.reason}`) + import_chalk51.default.dim(`
|
|
10349
10539
|
${data.how_to_fix}`));
|
|
10350
|
-
else for (const e of errs) console.log(
|
|
10540
|
+
else for (const e of errs) console.log(import_chalk51.default.red(`
|
|
10351
10541
|
audit error: ${e}`));
|
|
10352
10542
|
if (errs.length) {
|
|
10353
10543
|
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` });
|
|
@@ -10357,9 +10547,9 @@ function printDoors(data) {
|
|
|
10357
10547
|
async function runMeter(readLedger, opts) {
|
|
10358
10548
|
const checks = [];
|
|
10359
10549
|
const N = Math.max(1, Math.min(10, opts.count ?? 3));
|
|
10360
|
-
console.log(
|
|
10550
|
+
console.log(import_chalk51.default.bold("\n Meter \u2014 is 1 request charged exactly 1 request?\n"));
|
|
10361
10551
|
const snap = await readLedger();
|
|
10362
|
-
console.log(
|
|
10552
|
+
console.log(import_chalk51.default.dim(` wallet ${snap.billing_account_id} \xB7 band count ${snap.band_count} \xB7 next request ${snap.next_request_cents}\xA2
|
|
10363
10553
|
`));
|
|
10364
10554
|
const url = `https://${dpHost}/${SANDBOX.version}/${SANDBOX.environment}/`;
|
|
10365
10555
|
const headers = opts.key ? { "X-API-Key": opts.key } : {};
|
|
@@ -10441,22 +10631,22 @@ async function runMeter(readLedger, opts) {
|
|
|
10441
10631
|
return checks;
|
|
10442
10632
|
}
|
|
10443
10633
|
function printChecks(checks) {
|
|
10444
|
-
console.log(
|
|
10445
|
-
const mark = { PASS:
|
|
10634
|
+
console.log(import_chalk51.default.bold("\n Results\n"));
|
|
10635
|
+
const mark = { PASS: import_chalk51.default.green(" PASS"), FAIL: import_chalk51.default.red(" FAIL"), SKIP: import_chalk51.default.dim(" SKIP"), KNOWN: import_chalk51.default.yellow(" KNOWN") };
|
|
10446
10636
|
for (const ch of checks) {
|
|
10447
|
-
console.log(` ${mark[ch.status]} ${
|
|
10448
|
-
console.log(
|
|
10637
|
+
console.log(` ${mark[ch.status]} ${import_chalk51.default.bold(ch.name)}`);
|
|
10638
|
+
console.log(import_chalk51.default.dim(` ${ch.detail}`));
|
|
10449
10639
|
}
|
|
10450
10640
|
const fails = checks.filter((c) => c.status === "FAIL").length;
|
|
10451
10641
|
const skips = checks.filter((c) => c.status === "SKIP").length;
|
|
10452
10642
|
const known = checks.filter((c) => c.status === "KNOWN").length;
|
|
10453
10643
|
console.log("");
|
|
10454
10644
|
const passes = checks.filter((c) => c.status === "PASS").length;
|
|
10455
|
-
if (fails) console.log(
|
|
10456
|
-
else if (passes) console.log(
|
|
10457
|
-
else console.log(
|
|
10458
|
-
if (known) console.log(
|
|
10459
|
-
if (skips) console.log(
|
|
10645
|
+
if (fails) console.log(import_chalk51.default.red(` ${fails} check(s) FAILED.`));
|
|
10646
|
+
else if (passes) console.log(import_chalk51.default.green(` ${passes} check(s) passed, 0 failed.`));
|
|
10647
|
+
else console.log(import_chalk51.default.yellow(" NOTHING WAS VERIFIED \u2014 every check was skipped."));
|
|
10648
|
+
if (known) console.log(import_chalk51.default.yellow(` ${known} known-open issue(s) still outstanding.`));
|
|
10649
|
+
if (skips) console.log(import_chalk51.default.dim(` ${skips} check(s) NOT RUN (see SKIP above) \u2014 those invariants are unverified.`));
|
|
10460
10650
|
console.log("");
|
|
10461
10651
|
}
|
|
10462
10652
|
|
|
@@ -10503,93 +10693,93 @@ var OP_COMMANDS = [
|
|
|
10503
10693
|
function renderOpCommands() {
|
|
10504
10694
|
const width = Math.max(...OP_COMMANDS.map((c) => c.cmd.length)) + 10;
|
|
10505
10695
|
const lines = OP_COMMANDS.map((c) => {
|
|
10506
|
-
const left = ` ${
|
|
10696
|
+
const left = ` ${import_chalk52.default.cyan(`apiblaze ${c.cmd}`)}`;
|
|
10507
10697
|
const pad = " ".repeat(Math.max(1, width - c.cmd.length));
|
|
10508
|
-
return `${left}${pad}${c.blurb}${c.extra ? " " +
|
|
10698
|
+
return `${left}${pad}${c.blurb}${c.extra ? " " + import_chalk52.default.dim(`(${c.extra})`) : ""}`;
|
|
10509
10699
|
});
|
|
10510
10700
|
return [
|
|
10511
|
-
|
|
10701
|
+
import_chalk52.default.bold("Operator commands"),
|
|
10512
10702
|
...lines,
|
|
10513
10703
|
"",
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10704
|
+
import_chalk52.default.dim(" Operators only. The gate is server-side (dashboard /api/cli/op checks the"),
|
|
10705
|
+
import_chalk52.default.dim(" signed-in email, admin-api re-checks with operatorGate) \u2014 a patched CLI just"),
|
|
10706
|
+
import_chalk52.default.dim(" gets 403s. Every op call is read-only except `op sweep`."),
|
|
10517
10707
|
"",
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10708
|
+
import_chalk52.default.dim(" Not a CLI command: to prune all non-CP data run scripts/nuke-but-cp.sh --apply --sweep"),
|
|
10709
|
+
import_chalk52.default.dim(" in the repo. Operator dashboards (dlq, thresholds, throttling, pricing, billing,"),
|
|
10710
|
+
import_chalk52.default.dim(" agent-spend, teams, tests, leak-detection, lifecycle) live at /operator/* in the app.")
|
|
10521
10711
|
].join("\n");
|
|
10522
10712
|
}
|
|
10523
10713
|
function printResidue(report, applied) {
|
|
10524
10714
|
const up = report?.upstash ?? {};
|
|
10525
10715
|
const fga = report?.fga ?? {};
|
|
10526
10716
|
const ghosts = report?.ghosts ?? {};
|
|
10527
|
-
console.log(
|
|
10528
|
-
console.log(
|
|
10717
|
+
console.log(import_chalk52.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
10718
|
+
console.log(import_chalk52.default.bold("\n Upstash"));
|
|
10529
10719
|
const orphans = up.orphans ?? [];
|
|
10530
|
-
if (orphans.length === 0) console.log(
|
|
10531
|
-
for (const o of orphans) console.log(` ${
|
|
10532
|
-
console.log(
|
|
10720
|
+
if (orphans.length === 0) console.log(import_chalk52.default.green(" no orphaned keys"));
|
|
10721
|
+
for (const o of orphans) console.log(` ${import_chalk52.default.yellow(o.key)} ${import_chalk52.default.dim(`\u2014 ${o.reason}`)}`);
|
|
10722
|
+
console.log(import_chalk52.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
10533
10723
|
if (up.anon_wallet_detail) {
|
|
10534
10724
|
const d = up.anon_wallet_detail;
|
|
10535
|
-
console.log(
|
|
10725
|
+
console.log(import_chalk52.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)"}`));
|
|
10536
10726
|
}
|
|
10537
10727
|
if (up.keyspace_census) {
|
|
10538
10728
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
10539
|
-
console.log(
|
|
10729
|
+
console.log(import_chalk52.default.dim(` keyspace: ${census}`));
|
|
10540
10730
|
}
|
|
10541
|
-
if (up.unknown?.length) console.log(
|
|
10542
|
-
if (applied) console.log(` ${
|
|
10543
|
-
for (const e of up.errors ?? []) console.log(
|
|
10544
|
-
console.log(
|
|
10731
|
+
if (up.unknown?.length) console.log(import_chalk52.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
10732
|
+
if (applied) console.log(` ${import_chalk52.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
10733
|
+
for (const e of up.errors ?? []) console.log(import_chalk52.default.red(` error: ${e}`));
|
|
10734
|
+
console.log(import_chalk52.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
10545
10735
|
if (applied) {
|
|
10546
10736
|
const swept = fga?.swept ?? [];
|
|
10547
|
-
if (swept.length === 0) console.log(
|
|
10737
|
+
if (swept.length === 0) console.log(import_chalk52.default.green(" no orphaned stores"));
|
|
10548
10738
|
for (const s of swept) {
|
|
10549
10739
|
console.log(
|
|
10550
|
-
` ${
|
|
10740
|
+
` ${import_chalk52.default.yellow(s.store_id)} ${import_chalk52.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
10551
10741
|
);
|
|
10552
10742
|
}
|
|
10553
|
-
if (fga?.remaining) console.log(
|
|
10743
|
+
if (fga?.remaining) console.log(import_chalk52.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
10554
10744
|
const st = fga?.side_tables;
|
|
10555
|
-
if (st) console.log(
|
|
10745
|
+
if (st) console.log(import_chalk52.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})` : ""}`));
|
|
10556
10746
|
} else {
|
|
10557
10747
|
const fgaOrphans = fga?.orphans ?? [];
|
|
10558
|
-
if (fgaOrphans.length === 0) console.log(
|
|
10748
|
+
if (fgaOrphans.length === 0) console.log(import_chalk52.default.green(" no orphaned stores"));
|
|
10559
10749
|
for (const s of fgaOrphans) {
|
|
10560
10750
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
10561
|
-
console.log(` ${
|
|
10751
|
+
console.log(` ${import_chalk52.default.yellow(s.store_id)} ${import_chalk52.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
10562
10752
|
}
|
|
10563
|
-
console.log(
|
|
10753
|
+
console.log(import_chalk52.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
10564
10754
|
const st = fga?.side_tables;
|
|
10565
|
-
if (st) console.log(
|
|
10755
|
+
if (st) console.log(import_chalk52.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`));
|
|
10566
10756
|
}
|
|
10567
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
10568
|
-
console.log(
|
|
10757
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk52.default.red(` error: ${e}`));
|
|
10758
|
+
console.log(import_chalk52.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
10569
10759
|
if (applied) {
|
|
10570
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
10571
|
-
else console.log(` ${
|
|
10760
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk52.default.green(" no ghost tuples"));
|
|
10761
|
+
else console.log(` ${import_chalk52.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk52.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
10572
10762
|
} else {
|
|
10573
10763
|
const n = ghosts?.ghost_count ?? 0;
|
|
10574
|
-
if (n === 0) console.log(
|
|
10764
|
+
if (n === 0) console.log(import_chalk52.default.green(` no ghost tuples ${import_chalk52.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
10575
10765
|
else {
|
|
10576
|
-
console.log(
|
|
10766
|
+
console.log(import_chalk52.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
10577
10767
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
10578
|
-
console.log(
|
|
10768
|
+
console.log(import_chalk52.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
10579
10769
|
}
|
|
10580
|
-
if (n > 20) console.log(
|
|
10770
|
+
if (n > 20) console.log(import_chalk52.default.dim(` \u2026 and ${n - 20} more`));
|
|
10581
10771
|
}
|
|
10582
10772
|
}
|
|
10583
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
10773
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk52.default.red(` error: ${e}`));
|
|
10584
10774
|
console.log();
|
|
10585
10775
|
}
|
|
10586
10776
|
async function runOp(sub, opts = {}, view) {
|
|
10587
10777
|
if (!loadCredentials()) {
|
|
10588
|
-
console.log(
|
|
10778
|
+
console.log(import_chalk52.default.dim("Not logged in. Run `apiblaze login`."));
|
|
10589
10779
|
return;
|
|
10590
10780
|
}
|
|
10591
10781
|
if (!isOperatorLogin()) {
|
|
10592
|
-
console.log(
|
|
10782
|
+
console.log(import_chalk52.default.dim("`apiblaze op` is only available to platform operators."));
|
|
10593
10783
|
return;
|
|
10594
10784
|
}
|
|
10595
10785
|
switch (sub) {
|
|
@@ -10615,17 +10805,17 @@ async function runOp(sub, opts = {}, view) {
|
|
|
10615
10805
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
10616
10806
|
printResidue(report, false);
|
|
10617
10807
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
10618
|
-
console.log(
|
|
10808
|
+
console.log(import_chalk52.default.green("Nothing to sweep."));
|
|
10619
10809
|
return;
|
|
10620
10810
|
}
|
|
10621
10811
|
if (!opts.yes) {
|
|
10622
10812
|
const readline3 = await import("readline/promises");
|
|
10623
10813
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
10624
10814
|
const answer = await rl.question(
|
|
10625
|
-
|
|
10815
|
+
import_chalk52.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: `)
|
|
10626
10816
|
);
|
|
10627
10817
|
rl.close();
|
|
10628
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
10818
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk52.default.dim("Aborted."));
|
|
10629
10819
|
}
|
|
10630
10820
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
10631
10821
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -10635,25 +10825,25 @@ async function runOp(sub, opts = {}, view) {
|
|
|
10635
10825
|
case "mark": {
|
|
10636
10826
|
const label3 = (view ?? "").trim();
|
|
10637
10827
|
if (!label3) {
|
|
10638
|
-
console.log(
|
|
10828
|
+
console.log(import_chalk52.default.red("Give the change a name:") + import_chalk52.default.cyan(' apiblaze op mark "cached tenant count"'));
|
|
10639
10829
|
return;
|
|
10640
10830
|
}
|
|
10641
10831
|
const res = await opCall({ method: "POST", path: "/operator/latency/mark", body: { label: label3 }, summary: "record change marker" });
|
|
10642
10832
|
const ts = new Date(res?.marker?.ts ?? Date.now()).toISOString();
|
|
10643
|
-
console.log(
|
|
10644
|
-
Marked: `) +
|
|
10645
|
-
console.log(
|
|
10646
|
-
console.log(
|
|
10833
|
+
console.log(import_chalk52.default.green(`
|
|
10834
|
+
Marked: `) + import_chalk52.default.bold(label3));
|
|
10835
|
+
console.log(import_chalk52.default.dim(` ${ts}`));
|
|
10836
|
+
console.log(import_chalk52.default.dim(` Once traffic has run on both sides, compare with: `) + import_chalk52.default.cyan("apiblaze op latency compare") + "\n");
|
|
10647
10837
|
return;
|
|
10648
10838
|
}
|
|
10649
10839
|
case "credits": {
|
|
10650
10840
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
10651
10841
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
10652
10842
|
const accounts = data?.accounts ?? [];
|
|
10653
|
-
if (accounts.length === 0) return void console.log(
|
|
10843
|
+
if (accounts.length === 0) return void console.log(import_chalk52.default.dim("No credit wallets."));
|
|
10654
10844
|
for (const a of accounts) {
|
|
10655
10845
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
10656
|
-
console.log(` ${
|
|
10846
|
+
console.log(` ${import_chalk52.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk52.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk52.default.dim(" \u2014 anon") : ""}`);
|
|
10657
10847
|
}
|
|
10658
10848
|
return;
|
|
10659
10849
|
}
|
|
@@ -10664,7 +10854,7 @@ async function runOp(sub, opts = {}, view) {
|
|
|
10664
10854
|
case "billing": {
|
|
10665
10855
|
const which = (view ?? "").trim().toLowerCase();
|
|
10666
10856
|
if (which && which !== "doors" && which !== "meter") {
|
|
10667
|
-
return void console.log(
|
|
10857
|
+
return void console.log(import_chalk52.default.red(`Unknown: apiblaze op billing ${which}. Use 'doors', 'meter', or neither for both.`));
|
|
10668
10858
|
}
|
|
10669
10859
|
const checks = [];
|
|
10670
10860
|
let doorsData = null;
|
|
@@ -10725,29 +10915,29 @@ async function runOp(sub, opts = {}, view) {
|
|
|
10725
10915
|
const data = await opCall({ method: "GET", path: `/operator/latency/grades${q}`, summary: "latency grades" });
|
|
10726
10916
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
10727
10917
|
const t = data.thresholds_ms;
|
|
10728
|
-
console.log(
|
|
10729
|
-
console.log(
|
|
10918
|
+
console.log(import_chalk52.default.bold("\nHow good was apiblaze itself?") + import_chalk52.default.dim(" (our overhead only \u2014 a slow customer API never counts against us)"));
|
|
10919
|
+
console.log(import_chalk52.default.dim(` excellent <${t.excellent.replace("<", "")}ms \xB7 okay ${t.okay}ms \xB7 bad ${t.bad}ms \xB7 terrible ${t.terrible.replace(">=", "")}ms+
|
|
10730
10920
|
`));
|
|
10731
|
-
console.log(
|
|
10921
|
+
console.log(import_chalk52.default.dim(" date reqs excellent okay bad terrible"));
|
|
10732
10922
|
for (const d of data.days ?? []) {
|
|
10733
10923
|
const p = d.pct;
|
|
10734
|
-
const cell = (v, colour) => v > 0 ? colour(`${String(v).padStart(5)}%`) :
|
|
10924
|
+
const cell = (v, colour) => v > 0 ? colour(`${String(v).padStart(5)}%`) : import_chalk52.default.dim(`${String(v).padStart(5)}%`);
|
|
10735
10925
|
console.log(
|
|
10736
|
-
` ${d.date} ${String(d.total).padStart(5)} ${cell(p.excellent,
|
|
10926
|
+
` ${d.date} ${String(d.total).padStart(5)} ${cell(p.excellent, import_chalk52.default.green)} ${cell(p.okay, import_chalk52.default.cyan)} ${cell(p.bad, import_chalk52.default.yellow)} ${cell(p.terrible, import_chalk52.default.red)}`
|
|
10737
10927
|
);
|
|
10738
10928
|
}
|
|
10739
10929
|
const cul = data.culprits ?? [];
|
|
10740
10930
|
if (cul.length) {
|
|
10741
|
-
console.log(
|
|
10742
|
-
console.log(
|
|
10931
|
+
console.log(import_chalk52.default.bold("\n Who caused the bad and terrible ones\n"));
|
|
10932
|
+
console.log(import_chalk52.default.dim(" bad terrible feature \u2192 dependency"));
|
|
10743
10933
|
for (const r of cul.slice(0, 12)) {
|
|
10744
10934
|
if (!r.bad && !r.terrible) continue;
|
|
10745
10935
|
console.log(
|
|
10746
|
-
` ${String(r.bad).padStart(6)} ${
|
|
10936
|
+
` ${String(r.bad).padStart(6)} ${import_chalk52.default.red(String(r.terrible).padStart(8))} ${import_chalk52.default.yellow(r.feature)} ${import_chalk52.default.dim("\u2192")} ${import_chalk52.default.cyan(r.dep)}`
|
|
10747
10937
|
);
|
|
10748
10938
|
}
|
|
10749
10939
|
}
|
|
10750
|
-
if (data.caveat) console.log(
|
|
10940
|
+
if (data.caveat) console.log(import_chalk52.default.dim(`
|
|
10751
10941
|
\u26A0 ${data.caveat}
|
|
10752
10942
|
`));
|
|
10753
10943
|
return;
|
|
@@ -10756,24 +10946,24 @@ async function runOp(sub, opts = {}, view) {
|
|
|
10756
10946
|
const data = await opCall({ method: "GET", path: `/operator/latency/compare${q}`, summary: "latency before/after" });
|
|
10757
10947
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
10758
10948
|
const b = data.before, a = data.after, d = data.delta;
|
|
10759
|
-
console.log(
|
|
10760
|
-
Before vs after: `) +
|
|
10761
|
-
console.log(
|
|
10949
|
+
console.log(import_chalk52.default.bold(`
|
|
10950
|
+
Before vs after: `) + import_chalk52.default.cyan(data.marker.label));
|
|
10951
|
+
console.log(import_chalk52.default.dim(` marked ${new Date(data.marker.ts).toISOString()} \xB7 ${data.window_hours}h either side
|
|
10762
10952
|
`));
|
|
10763
10953
|
const row = (name, before, after, delta) => {
|
|
10764
10954
|
const arrow = delta === 0 ? "=" : delta < 0 ? "\u2193" : "\u2191";
|
|
10765
10955
|
const txt = `${String(before).padStart(6)}ms \u2192${String(after).padStart(7)}ms ${arrow}${Math.abs(delta)}ms`;
|
|
10766
|
-
console.log(` ${name.padEnd(22)}${data.trustworthy ? delta <= 0 ?
|
|
10956
|
+
console.log(` ${name.padEnd(22)}${data.trustworthy ? delta <= 0 ? import_chalk52.default.green(txt) : import_chalk52.default.red(txt) : import_chalk52.default.dim(txt)}`);
|
|
10767
10957
|
};
|
|
10768
|
-
console.log(
|
|
10958
|
+
console.log(import_chalk52.default.dim(" metric before after change"));
|
|
10769
10959
|
row("total p50", b.total_p50, a.total_p50, d.total_p50);
|
|
10770
10960
|
row("total p95", b.total_p95, a.total_p95, d.total_p95);
|
|
10771
10961
|
row("apiblaze overhead p50", b.gw_p50, a.gw_p50, d.gw_p50);
|
|
10772
10962
|
row("apiblaze overhead p95", b.gw_p95, a.gw_p95, d.gw_p95);
|
|
10773
|
-
console.log(
|
|
10963
|
+
console.log(import_chalk52.default.dim(`
|
|
10774
10964
|
requests: ${b.requests} before \xB7 ${a.requests} after`));
|
|
10775
10965
|
for (const w of data.warnings ?? []) {
|
|
10776
|
-
console.log((data.trustworthy ?
|
|
10966
|
+
console.log((data.trustworthy ? import_chalk52.default.dim : import_chalk52.default.yellow)(` ${data.trustworthy ? "\xB7" : "\u26A0"} ${w}`));
|
|
10777
10967
|
}
|
|
10778
10968
|
console.log("");
|
|
10779
10969
|
return;
|
|
@@ -10782,19 +10972,19 @@ Before vs after: `) + import_chalk51.default.cyan(data.marker.label));
|
|
|
10782
10972
|
const data = await opCall({ method: "GET", path: `/operator/latency/slow${q}`, summary: "slowest requests" });
|
|
10783
10973
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
10784
10974
|
const rows2 = data?.rows ?? [];
|
|
10785
|
-
if (!rows2.length) return void console.log(
|
|
10786
|
-
console.log(
|
|
10975
|
+
if (!rows2.length) return void console.log(import_chalk52.default.dim("No requests over the threshold in that window."));
|
|
10976
|
+
console.log(import_chalk52.default.bold(`
|
|
10787
10977
|
Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
|
|
10788
10978
|
`));
|
|
10789
|
-
console.log(
|
|
10979
|
+
console.log(import_chalk52.default.dim(" total ours theirs blame request id"));
|
|
10790
10980
|
for (const r of rows2.slice(0, 30)) {
|
|
10791
10981
|
console.log(
|
|
10792
|
-
` ${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)} ${
|
|
10982
|
+
` ${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_chalk52.default.yellow(`${r.slow_gw_feature || "-"}\u2192${r.slow_dep || "-"}`.padEnd(20))} ${import_chalk52.default.dim(r.request_id || "")}`
|
|
10793
10983
|
);
|
|
10794
10984
|
}
|
|
10795
|
-
console.log(
|
|
10985
|
+
console.log(import_chalk52.default.dim(`
|
|
10796
10986
|
The last column is the request id (Cloudflare calls it a "cf-ray"). Look one up with`));
|
|
10797
|
-
console.log(
|
|
10987
|
+
console.log(import_chalk52.default.dim(` \`apiblaze logs\` for that request's exact per-feature breakdown \u2014 unsampled, unlike the table above.
|
|
10798
10988
|
`));
|
|
10799
10989
|
return;
|
|
10800
10990
|
}
|
|
@@ -10802,18 +10992,18 @@ Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
|
|
|
10802
10992
|
const data = await opCall({ method: "GET", path: `/operator/latency/llm${q}`, summary: "llm latency" });
|
|
10803
10993
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
10804
10994
|
const rows2 = data?.rows ?? [];
|
|
10805
|
-
if (!rows2.length) return void console.log(
|
|
10806
|
-
console.log(
|
|
10995
|
+
if (!rows2.length) return void console.log(import_chalk52.default.dim("No LLM traffic in that window."));
|
|
10996
|
+
console.log(import_chalk52.default.bold(`
|
|
10807
10997
|
LLM timing \u2014 last ${data.window_hours}h
|
|
10808
10998
|
`));
|
|
10809
|
-
console.log(
|
|
10999
|
+
console.log(import_chalk52.default.dim(" turns turn p95 ttfc p95 gen p95 reserve p95 in/out tokens p95 model"));
|
|
10810
11000
|
for (const r of rows2) {
|
|
10811
11001
|
const n = r.turns ?? r.requests ?? 0;
|
|
10812
11002
|
console.log(
|
|
10813
11003
|
` ${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 || "-"}`
|
|
10814
11004
|
);
|
|
10815
11005
|
}
|
|
10816
|
-
console.log(
|
|
11006
|
+
console.log(import_chalk52.default.dim(`
|
|
10817
11007
|
${data.note}
|
|
10818
11008
|
`));
|
|
10819
11009
|
return;
|
|
@@ -10824,26 +11014,26 @@ LLM timing \u2014 last ${data.window_hours}h
|
|
|
10824
11014
|
]);
|
|
10825
11015
|
if (opts.json) return void console.log(JSON.stringify({ blame, summary }, null, 2));
|
|
10826
11016
|
const ov = summary?.apiblaze_overhead_ms ?? {};
|
|
10827
|
-
console.log(
|
|
11017
|
+
console.log(import_chalk52.default.bold(`
|
|
10828
11018
|
Latency \u2014 last ${summary?.window_hours ?? "?"}h, ${Number(summary?.requests ?? 0).toLocaleString()} requests
|
|
10829
11019
|
`));
|
|
10830
|
-
console.log(` ${
|
|
10831
|
-
console.log(` ${
|
|
10832
|
-
console.log(
|
|
11020
|
+
console.log(` ${import_chalk52.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_chalk52.default.dim("\u2190 ours")}`);
|
|
11021
|
+
console.log(` ${import_chalk52.default.bold("upstream ttfb ")} ${" ".repeat(24)}p95 ${String(summary?.upstream_ttfb_p95_ms ?? 0).padStart(6)}ms ${import_chalk52.default.dim("\u2190 theirs")}`);
|
|
11022
|
+
console.log(import_chalk52.default.dim(` (per-request percentiles \u2014 never subtract one from the other)
|
|
10833
11023
|
`));
|
|
10834
11024
|
const rows = blame?.blame ?? [];
|
|
10835
|
-
if (!rows.length) return void console.log(
|
|
10836
|
-
console.log(
|
|
10837
|
-
console.log(
|
|
11025
|
+
if (!rows.length) return void console.log(import_chalk52.default.dim("No latency rows in that window."));
|
|
11026
|
+
console.log(import_chalk52.default.bold(" Which feature ate the time, and what inside it\n"));
|
|
11027
|
+
console.log(import_chalk52.default.dim(" share p95 feature \u2192 dependency"));
|
|
10838
11028
|
for (const r of rows.slice(0, 15)) {
|
|
10839
11029
|
const share = `${(r.share * 100).toFixed(1)}%`;
|
|
10840
|
-
console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${
|
|
11030
|
+
console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${import_chalk52.default.yellow(r.feature)} ${import_chalk52.default.dim("\u2192")} ${import_chalk52.default.cyan(r.dep)}`);
|
|
10841
11031
|
}
|
|
10842
11032
|
console.log("");
|
|
10843
11033
|
return;
|
|
10844
11034
|
}
|
|
10845
11035
|
default:
|
|
10846
|
-
console.log(
|
|
11036
|
+
console.log(import_chalk52.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
10847
11037
|
}
|
|
10848
11038
|
}
|
|
10849
11039
|
|
|
@@ -10876,7 +11066,7 @@ program.command("login").description("Authenticate with APIblaze").option("--tea
|
|
|
10876
11066
|
process.exit(1);
|
|
10877
11067
|
}
|
|
10878
11068
|
});
|
|
10879
|
-
program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.abz.run)").option("--target <url>", "
|
|
11069
|
+
program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.abz.run)").option("--target <url|file>", "What to proxy \u2014 pass ANY of: a target server base URL (https://httpbin.org), a local OpenAPI file (./openapi.yaml), or a remote OpenAPI URL (https://acme.com/openapi.yaml). Spec inputs are detected automatically; routes, API version and environments then come from the spec").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("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("--apikey", "Protect with API keys and print the bootstrap keys (the default, made explicit). Consumers send X-API-Key.").option("--oauth [config]", `Login door. Bare = APIblaze-hosted GitHub sign-in. '{"iss","aud","jwks"}' = trust YOUR hosted login's JWTs. '{"provider","clientId","clientSecret"}' = APIblaze-hosted login page with YOUR OAuth app (github \xB7 google \xB7 microsoft \xB7 facebook \xB7 auth0).`).option("--identified", "Require every call to identify its end user (X-End-User-Id or a login token); unattributed calls are rejected").option("--iam", "Turn IAM on for the proxy's tenant so users & groups apply to identified calls").option("--apiversion <version>", "API version to create (e.g. 2.0.0). Creating a new version of a proxy you own adds a version to the existing project.").option("--tenant <slug>", "Tenant to attach the proxy to (created if new). Omitted \u2192 your team's most-recently-used tenant.").option("--product <slug>", "Product tag to group this project under in the portal (team-scoped; anonymous create). Defaults to your team's existing/placeholder tag.").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--new-session", "Start a fresh anonymous session (do not group with prior anonymous creates)").option("--json", "Output machine-readable JSON (non-interactive)").action(async (opts) => {
|
|
10880
11070
|
try {
|
|
10881
11071
|
await runCreate({ ...opts, openapi: opts.openapi ?? opts.openapispec });
|
|
10882
11072
|
} catch (err) {
|
|
@@ -10889,7 +11079,7 @@ agent.command("authz").description("Chat to design and turn on access rules for
|
|
|
10889
11079
|
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)));
|
|
10890
11080
|
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)));
|
|
10891
11081
|
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)));
|
|
10892
|
-
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("--
|
|
11082
|
+
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("--no-verbose", "Hide the per-turn proxy curl trace (shown by default for apichat)").option("-p, --prompt <question>", "One-shot question piped through the external agent CLI after the MCP install (used with --install-mcp or the install offer)").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 })));
|
|
10893
11083
|
var llm = program.command("llm").description("Manage a local LLM provider key for chat (optional \u2014 lifts model quality, bills your key)");
|
|
10894
11084
|
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)));
|
|
10895
11085
|
llm.command("show").description("Show the locally stored LLM key (masked)").action(action(() => runLlmShow()));
|
|
@@ -10906,7 +11096,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
10906
11096
|
try {
|
|
10907
11097
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
10908
11098
|
if (Number.isNaN(resolved)) {
|
|
10909
|
-
console.error(
|
|
11099
|
+
console.error(import_chalk53.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
10910
11100
|
process.exit(1);
|
|
10911
11101
|
}
|
|
10912
11102
|
await runDev({ port: resolved, project: opts.project, yes: opts.yes, captureFile: opts.captureFile, newSession: opts.newSession });
|
|
@@ -11038,7 +11228,7 @@ function groupedCommandHelp() {
|
|
|
11038
11228
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
11039
11229
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
11040
11230
|
}).filter(Boolean).join("\n");
|
|
11041
|
-
return `${
|
|
11231
|
+
return `${import_chalk53.default.bold(g.title)}
|
|
11042
11232
|
${rows}`;
|
|
11043
11233
|
}).join("\n\n");
|
|
11044
11234
|
}
|
|
@@ -11076,14 +11266,14 @@ async function recoverStaleTeam() {
|
|
|
11076
11266
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
11077
11267
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
11078
11268
|
if (!linked) {
|
|
11079
|
-
console.error(
|
|
11269
|
+
console.error(import_chalk53.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
11080
11270
|
return;
|
|
11081
11271
|
}
|
|
11082
11272
|
if (linked.teamId === creds.teamId) return;
|
|
11083
11273
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
11084
11274
|
delete next.activeTenant;
|
|
11085
11275
|
saveCredentials(next);
|
|
11086
|
-
console.error(
|
|
11276
|
+
console.error(import_chalk53.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk53.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
11087
11277
|
} catch {
|
|
11088
11278
|
}
|
|
11089
11279
|
}
|
|
@@ -11091,16 +11281,16 @@ async function printError(err) {
|
|
|
11091
11281
|
if (err instanceof ApiError) {
|
|
11092
11282
|
const data = err.body;
|
|
11093
11283
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
11094
|
-
console.error(
|
|
11284
|
+
console.error(import_chalk53.default.red(`
|
|
11095
11285
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
11096
11286
|
if (err.status === 403 || err.status === 404) {
|
|
11097
11287
|
await recoverStaleTeam();
|
|
11098
11288
|
}
|
|
11099
11289
|
} else if (err instanceof Error) {
|
|
11100
|
-
console.error(
|
|
11290
|
+
console.error(import_chalk53.default.red(`
|
|
11101
11291
|
Error: ${err.message}`));
|
|
11102
11292
|
} else {
|
|
11103
|
-
console.error(
|
|
11293
|
+
console.error(import_chalk53.default.red("\nUnknown error"));
|
|
11104
11294
|
}
|
|
11105
11295
|
}
|
|
11106
11296
|
program.parse(process.argv);
|