apiblaze 0.20.5 → 0.20.8
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 +393 -87
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -929,10 +929,10 @@ var init_tenant_pick = __esm({
|
|
|
929
929
|
|
|
930
930
|
// src/index.ts
|
|
931
931
|
var import_commander = require("commander");
|
|
932
|
-
var
|
|
932
|
+
var import_chalk52 = __toESM(require("chalk"));
|
|
933
933
|
|
|
934
934
|
// package.json
|
|
935
|
-
var version = "0.20.
|
|
935
|
+
var version = "0.20.8";
|
|
936
936
|
|
|
937
937
|
// src/index.ts
|
|
938
938
|
init_types();
|
|
@@ -9801,10 +9801,242 @@ async function runOriginsRemove(origin, opts) {
|
|
|
9801
9801
|
}
|
|
9802
9802
|
|
|
9803
9803
|
// src/commands/op.ts
|
|
9804
|
-
var
|
|
9804
|
+
var import_chalk51 = __toESM(require("chalk"));
|
|
9805
9805
|
init_auth();
|
|
9806
9806
|
init_trace();
|
|
9807
9807
|
init_types();
|
|
9808
|
+
|
|
9809
|
+
// src/commands/op-billing.ts
|
|
9810
|
+
var import_chalk50 = __toESM(require("chalk"));
|
|
9811
|
+
init_admin();
|
|
9812
|
+
var SANDBOX = {
|
|
9813
|
+
teamId: "team_1782844865835_zujrf",
|
|
9814
|
+
project: "portaltestproxypreapproved",
|
|
9815
|
+
tenant: "portalteststenant",
|
|
9816
|
+
version: "2.7.0",
|
|
9817
|
+
environment: "prod"
|
|
9818
|
+
};
|
|
9819
|
+
var dpHost = `${SANDBOX.project}.abz.run`;
|
|
9820
|
+
var ZONE_COUNT = 3;
|
|
9821
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
9822
|
+
async function readQuiet(read, tries = 8) {
|
|
9823
|
+
let prev = await read();
|
|
9824
|
+
for (let i = 0; i < tries; i++) {
|
|
9825
|
+
await sleep(1200);
|
|
9826
|
+
const now = await read();
|
|
9827
|
+
if (now.band_count === prev.band_count && now.balance_microcents === prev.balance_microcents) return now;
|
|
9828
|
+
prev = now;
|
|
9829
|
+
}
|
|
9830
|
+
return prev;
|
|
9831
|
+
}
|
|
9832
|
+
async function fire(url, n, headers = {}) {
|
|
9833
|
+
const out = [];
|
|
9834
|
+
for (let i = 0; i < n; i++) {
|
|
9835
|
+
try {
|
|
9836
|
+
const res = await fetch(url, { headers });
|
|
9837
|
+
const ray = (res.headers.get("cf-ray") || "").split("-")[0];
|
|
9838
|
+
out.push({ ray, status: res.status });
|
|
9839
|
+
await res.text().catch(() => "");
|
|
9840
|
+
} catch {
|
|
9841
|
+
out.push({ ray: "", status: 0 });
|
|
9842
|
+
}
|
|
9843
|
+
}
|
|
9844
|
+
return out;
|
|
9845
|
+
}
|
|
9846
|
+
async function rowsForRays(project, version2, tenant2, rays) {
|
|
9847
|
+
const want = new Set(rays.filter(Boolean));
|
|
9848
|
+
const found = /* @__PURE__ */ new Map();
|
|
9849
|
+
for (let attempt = 0; attempt < 6 && found.size < want.size; attempt++) {
|
|
9850
|
+
if (attempt) await sleep(2e3);
|
|
9851
|
+
const q = new URLSearchParams({ tenant: tenant2, limit: "200" });
|
|
9852
|
+
const res = await admin({
|
|
9853
|
+
path: `/projects/${project}/${version2}/logs/window?${q.toString()}`,
|
|
9854
|
+
method: "GET",
|
|
9855
|
+
summary: "read request log for billing assertion"
|
|
9856
|
+
}).catch(() => ({ rows: [] }));
|
|
9857
|
+
found.clear();
|
|
9858
|
+
for (const r of res.rows ?? []) {
|
|
9859
|
+
if (!want.has(r.request_id)) continue;
|
|
9860
|
+
const list = found.get(r.request_id) ?? [];
|
|
9861
|
+
list.push({ status: r.status, cost_cents: r.cost_cents });
|
|
9862
|
+
found.set(r.request_id, list);
|
|
9863
|
+
}
|
|
9864
|
+
}
|
|
9865
|
+
return found;
|
|
9866
|
+
}
|
|
9867
|
+
function printDoors(data) {
|
|
9868
|
+
const checks = [];
|
|
9869
|
+
console.log(import_chalk50.default.bold("\n Doors \u2014 is every way in metered?\n"));
|
|
9870
|
+
const doors = data?.doors ?? [];
|
|
9871
|
+
const metered = doors.filter((d) => d.verdict === "metered");
|
|
9872
|
+
const allowed = doors.filter((d) => d.verdict === "allowed-free");
|
|
9873
|
+
const known = doors.filter((d) => d.verdict === "known-open");
|
|
9874
|
+
const stray = data?.stray_routes ?? [];
|
|
9875
|
+
const errs = data?.errors ?? [];
|
|
9876
|
+
const routeAuditBroke = errs.some((e) => e.startsWith("zone "));
|
|
9877
|
+
const devAuditBroke = errs.some((e) => e.startsWith("workers.dev audit"));
|
|
9878
|
+
console.log(import_chalk50.default.dim(` ${metered.length} route(s) behind main-proxy (metered)`));
|
|
9879
|
+
for (const d of metered) console.log(import_chalk50.default.green(` \u2713 ${d.pattern}`));
|
|
9880
|
+
console.log(import_chalk50.default.dim(`
|
|
9881
|
+
${allowed.length} route(s) free ON PURPOSE`));
|
|
9882
|
+
for (const d of allowed) {
|
|
9883
|
+
console.log(import_chalk50.default.cyan(` \u2022 ${d.pattern}`) + import_chalk50.default.dim(` \u2192 ${d.script}`));
|
|
9884
|
+
console.log(import_chalk50.default.dim(` ${d.why}`));
|
|
9885
|
+
}
|
|
9886
|
+
if (known.length) {
|
|
9887
|
+
console.log(import_chalk50.default.yellow(`
|
|
9888
|
+
${known.length} route(s) KNOWN OPEN \u2014 unmetered, not yet closed`));
|
|
9889
|
+
for (const d of known) {
|
|
9890
|
+
console.log(import_chalk50.default.yellow(` ! ${d.pattern}`) + import_chalk50.default.dim(` \u2192 ${d.script}`));
|
|
9891
|
+
console.log(import_chalk50.default.dim(` ${d.why}`));
|
|
9892
|
+
}
|
|
9893
|
+
checks.push({ name: "no known-open doors", status: "KNOWN", detail: `${known.length} unmetered route(s) still open \u2014 see above` });
|
|
9894
|
+
}
|
|
9895
|
+
if (stray.length) {
|
|
9896
|
+
console.log(import_chalk50.default.red(`
|
|
9897
|
+
${stray.length} STRAY route(s) \u2014 not main-proxy, not on the allowlist`));
|
|
9898
|
+
for (const d of stray) {
|
|
9899
|
+
console.log(import_chalk50.default.red(` \u2717 ${d.pattern}`) + import_chalk50.default.dim(` \u2192 ${d.script}`));
|
|
9900
|
+
console.log(import_chalk50.default.dim(` ${d.why}`));
|
|
9901
|
+
}
|
|
9902
|
+
checks.push({ name: "no stray routes", status: "FAIL", detail: `${stray.length}: ${stray.map((s) => s.pattern).join(", ")}` });
|
|
9903
|
+
} else if (routeAuditBroke) {
|
|
9904
|
+
checks.push({ name: "no stray routes", status: "SKIP", detail: `route enumeration failed for ${errs.filter((e) => e.startsWith("zone ")).length} zone(s) \u2014 NOT a pass, the routes were never read. Needs a CF token with Workers Routes:Read.` });
|
|
9905
|
+
} else {
|
|
9906
|
+
checks.push({ name: "no stray routes", status: "PASS", detail: `all ${doors.length} live route(s) across ${ZONE_COUNT} zone(s) are main-proxy or allowlisted` });
|
|
9907
|
+
}
|
|
9908
|
+
const wd = data?.workers_dev ?? {};
|
|
9909
|
+
const open = wd.enabled ?? [];
|
|
9910
|
+
if (open.length) {
|
|
9911
|
+
console.log(import_chalk50.default.red(`
|
|
9912
|
+
${open.length} of ${wd.total} worker(s) reachable on *.workers.dev`));
|
|
9913
|
+
for (const s of open) console.log(import_chalk50.default.red(` \u2717 ${s.script}.workers.dev`) + import_chalk50.default.dim(` (enabled=${s.enabled} previews=${s.previews})`));
|
|
9914
|
+
console.log(import_chalk50.default.dim(" A workers.dev hostname bypasses every CF route, WAF rule and the credit gate."));
|
|
9915
|
+
checks.push({ name: "no workers.dev doors", status: "FAIL", detail: `${open.length} script(s) publicly reachable: ${open.map((s) => s.script).join(", ")}` });
|
|
9916
|
+
} else if (devAuditBroke || !wd.total) {
|
|
9917
|
+
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." });
|
|
9918
|
+
} else {
|
|
9919
|
+
console.log(import_chalk50.default.green(`
|
|
9920
|
+
\u2713 0 of ${wd.total} workers reachable on *.workers.dev`));
|
|
9921
|
+
checks.push({ name: "no workers.dev doors", status: "PASS", detail: `all ${wd.total} scripts have workers.dev + previews disabled` });
|
|
9922
|
+
}
|
|
9923
|
+
if (data?.how_to_fix) console.log(import_chalk50.default.yellow(`
|
|
9924
|
+
${data.reason}`) + import_chalk50.default.dim(`
|
|
9925
|
+
${data.how_to_fix}`));
|
|
9926
|
+
else for (const e of errs) console.log(import_chalk50.default.red(`
|
|
9927
|
+
audit error: ${e}`));
|
|
9928
|
+
if (errs.length) {
|
|
9929
|
+
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` });
|
|
9930
|
+
}
|
|
9931
|
+
return checks;
|
|
9932
|
+
}
|
|
9933
|
+
async function runMeter(readLedger, opts) {
|
|
9934
|
+
const checks = [];
|
|
9935
|
+
const N = Math.max(1, Math.min(10, opts.count ?? 3));
|
|
9936
|
+
console.log(import_chalk50.default.bold("\n Meter \u2014 is 1 request charged exactly 1 request?\n"));
|
|
9937
|
+
const snap = await readLedger();
|
|
9938
|
+
console.log(import_chalk50.default.dim(` wallet ${snap.billing_account_id} \xB7 band count ${snap.band_count} \xB7 next request ${snap.next_request_cents}\xA2
|
|
9939
|
+
`));
|
|
9940
|
+
const url = `https://${dpHost}/${SANDBOX.version}/${SANDBOX.environment}/`;
|
|
9941
|
+
const headers = opts.key ? { "X-API-Key": opts.key } : {};
|
|
9942
|
+
const sent = await fire(url, N, headers);
|
|
9943
|
+
const rays = sent.map((r) => r.ray).filter(Boolean);
|
|
9944
|
+
if (rays.length !== N) {
|
|
9945
|
+
checks.push({ name: "1 request = 1 charge", status: "SKIP", detail: `only ${rays.length}/${N} responses carried a cf-ray \u2014 cannot attribute charges without a request id.` });
|
|
9946
|
+
} else {
|
|
9947
|
+
const rows = await rowsForRays(SANDBOX.project, SANDBOX.version, SANDBOX.tenant, rays);
|
|
9948
|
+
const missing = rays.filter((r) => !rows.has(r));
|
|
9949
|
+
const doubled = rays.filter((r) => (rows.get(r)?.length ?? 0) > 1);
|
|
9950
|
+
if (missing.length === rays.length) {
|
|
9951
|
+
checks.push({ name: "1 request = 1 charge", status: "SKIP", detail: `none of the ${N} request(s) appeared in ${SANDBOX.project}'s log within ~12s (statuses ${sent.map((x) => x.status).join(",")}). Either request logging is off for this proxy or the requests never reached it \u2014 nothing was verified.` });
|
|
9952
|
+
} else if (doubled.length) {
|
|
9953
|
+
checks.push({ name: "1 request = 1 charge", status: "FAIL", detail: `DOUBLE CHARGE: ${doubled.length} of ${N} request(s) produced more than one billed log row (${doubled.map((r) => `${r}\xD7${rows.get(r).length}`).join(", ")}).` });
|
|
9954
|
+
} else if (missing.length) {
|
|
9955
|
+
checks.push({ name: "1 request = 1 charge", status: "FAIL", detail: `${rays.length - missing.length}/${N} request(s) logged exactly once, but ${missing.length} never appeared \u2014 those requests were served and not recorded.` });
|
|
9956
|
+
} else {
|
|
9957
|
+
const costs = rays.map((r) => rows.get(r)[0].cost_cents ?? 0);
|
|
9958
|
+
const statuses = sent.map((x) => x.status).join(",");
|
|
9959
|
+
checks.push({ name: "1 request = 1 charge", status: "PASS", detail: `${N} request(s) (${statuses}) \u2192 exactly ${N} billed row(s), one per request id, at ${costs.join("/")}\xA2 each` });
|
|
9960
|
+
const served = sent.every((x) => x.status > 0 && x.status < 400);
|
|
9961
|
+
const want = served ? snap.next_request_cents : 0;
|
|
9962
|
+
const label3 = served ? "served request charged the catalog rate" : "refused request is not billed";
|
|
9963
|
+
const wrong = want === null ? [] : costs.filter((cst) => Math.abs(cst - want) > 1e-9);
|
|
9964
|
+
if (want === null) {
|
|
9965
|
+
checks.push({ name: label3, status: "SKIP", detail: "no request_bands row \u2014 banded pricing is off, so there is no catalog rate to check against." });
|
|
9966
|
+
} else {
|
|
9967
|
+
checks.push(wrong.length ? { name: label3, status: "FAIL", detail: served ? `expected ${want}\xA2 per request (band ${snap.band_count + 1}, thresholds ${snap.bands?.b1_to}/${snap.bands?.b2_to}); saw ${costs.join(", ")}\xA2` : `our own rejection (status ${sent[0].status}) should cost the caller nothing; the log shows ${costs.join(", ")}\xA2 billed` } : { name: label3, status: "PASS", detail: served ? `every request charged ${want}\xA2, matching the live request_bands row` : `${N} request(s) refused at the gate (status ${sent[0].status}) \u2192 0\xA2 billed, one row each` });
|
|
9968
|
+
}
|
|
9969
|
+
}
|
|
9970
|
+
}
|
|
9971
|
+
const shared = snap.wallet_shared_with ?? [];
|
|
9972
|
+
if (shared.length) {
|
|
9973
|
+
checks.push({
|
|
9974
|
+
name: "pre-upstream reject is refunded",
|
|
9975
|
+
status: "SKIP",
|
|
9976
|
+
detail: `needs a wallet nothing else spends. ${snap.billing_account_id} also pays for ${shared.length} other team(s): ${shared.slice(0, 3).join(", ")}${shared.length > 3 ? " \u2026" : ""} \u2014 including this command's own op-lane calls. A refund is a wallet event with no per-request row, so it cannot be attributed here. NOT VERIFIED.`
|
|
9977
|
+
});
|
|
9978
|
+
} else {
|
|
9979
|
+
const before = await readQuiet(readLedger);
|
|
9980
|
+
const rejects = await fire(`https://${dpHost}/${SANDBOX.version}/${SANDBOX.environment}/__billing_probe`, N);
|
|
9981
|
+
const after = await readQuiet(readLedger);
|
|
9982
|
+
const dBand = after.band_count - before.band_count;
|
|
9983
|
+
const dMoney = (before.balance_microcents ?? 0) - (after.balance_microcents ?? 0);
|
|
9984
|
+
checks.push(dBand === 0 && dMoney === 0 ? { name: "pre-upstream reject is refunded", status: "PASS", detail: `${N} unauthenticated request(s) (${rejects.map((r) => r.status).join(",")}) \u2192 charged then fully refunded: band +0, money +0` } : { name: "pre-upstream reject is refunded", status: "FAIL", detail: `${N} rejected request(s) left band +${dBand} and ${dMoney}\xB5\xA2 charged; expected 0 and 0 \u2014 refund-on-reject is ${dMoney > 0 ? "not firing" : "over-refunding"}.` });
|
|
9985
|
+
}
|
|
9986
|
+
if (!opts.key) {
|
|
9987
|
+
checks.push({ name: "management planes bill correctly", status: "SKIP", detail: "needs --key <key> to call the iam/apikeys planes. NOT COVERED: whether a direct IAM call is billed, and whether the apikeys plane stayed free." });
|
|
9988
|
+
} else {
|
|
9989
|
+
for (const plane of [
|
|
9990
|
+
{ name: "iam plane bills 1", project: "iam", url: "https://iam.apiblaze.com/1.0.0/prod/groups", want: 1 },
|
|
9991
|
+
{ name: "apikeys plane is free", project: "apikeys", url: "https://apikeys.apiblaze.com/1.0.0/prod/apikeys", want: 0 }
|
|
9992
|
+
]) {
|
|
9993
|
+
const b = await readQuiet(readLedger);
|
|
9994
|
+
const st = await fire(plane.url, 1, { "X-API-Key": opts.key });
|
|
9995
|
+
const aft = await readQuiet(readLedger);
|
|
9996
|
+
const d = aft.band_count - b.band_count;
|
|
9997
|
+
if (shared.length) {
|
|
9998
|
+
checks.push({ name: plane.name, status: "SKIP", detail: `measured band +${d}, but the wallet is shared so that number is not attributable. NOT VERIFIED.` });
|
|
9999
|
+
} else {
|
|
10000
|
+
checks.push(d === plane.want ? { name: plane.name, status: "PASS", detail: `1 call (${st.map((x) => x.status).join(",")}) \u2192 band +${d}` } : { name: plane.name, status: "FAIL", detail: `1 call (${st.map((x) => x.status).join(",")}) \u2192 band +${d}, expected +${plane.want}. ${d > plane.want ? "Charged more than once for one call." : "Free lane \u2014 this plane is usable without paying."}` });
|
|
10001
|
+
}
|
|
10002
|
+
}
|
|
10003
|
+
}
|
|
10004
|
+
if (!opts.mcpHost || !opts.key) {
|
|
10005
|
+
checks.push({ name: "MCP tools/call = 1 charge", status: "SKIP", detail: "needs --mcp-host <host> --key <key> to send a SERVED tool call. Fixed in code and deployed, but NOT verified live: an unauthenticated probe bills 0\xA2 either way, so it would prove nothing." });
|
|
10006
|
+
} else {
|
|
10007
|
+
const sentMcp = await fire(`https://${opts.mcpHost}/${SANDBOX.version}/${SANDBOX.environment}`, 1, { "X-API-Key": opts.key });
|
|
10008
|
+
const ray = sentMcp[0]?.ray;
|
|
10009
|
+
if (!ray || sentMcp[0].status >= 400) {
|
|
10010
|
+
checks.push({ name: "MCP tools/call = 1 charge", status: "SKIP", detail: `the MCP call returned ${sentMcp[0]?.status} \u2014 only a SERVED tool call can tell one charge from two.` });
|
|
10011
|
+
} else {
|
|
10012
|
+
const rows = await rowsForRays(SANDBOX.project, SANDBOX.version, SANDBOX.tenant, [ray]);
|
|
10013
|
+
const n = rows.get(ray)?.length ?? 0;
|
|
10014
|
+
checks.push(n === 1 ? { name: "MCP tools/call = 1 charge", status: "PASS", detail: "1 tool call \u2192 1 billed row (was 2 before 2026-08-11)" } : { name: "MCP tools/call = 1 charge", status: "FAIL", detail: `1 tool call \u2192 ${n} billed row(s); expected 1. The edge hop is billing again.` });
|
|
10015
|
+
}
|
|
10016
|
+
}
|
|
10017
|
+
return checks;
|
|
10018
|
+
}
|
|
10019
|
+
function printChecks(checks) {
|
|
10020
|
+
console.log(import_chalk50.default.bold("\n Results\n"));
|
|
10021
|
+
const mark = { PASS: import_chalk50.default.green(" PASS"), FAIL: import_chalk50.default.red(" FAIL"), SKIP: import_chalk50.default.dim(" SKIP"), KNOWN: import_chalk50.default.yellow(" KNOWN") };
|
|
10022
|
+
for (const ch of checks) {
|
|
10023
|
+
console.log(` ${mark[ch.status]} ${import_chalk50.default.bold(ch.name)}`);
|
|
10024
|
+
console.log(import_chalk50.default.dim(` ${ch.detail}`));
|
|
10025
|
+
}
|
|
10026
|
+
const fails = checks.filter((c) => c.status === "FAIL").length;
|
|
10027
|
+
const skips = checks.filter((c) => c.status === "SKIP").length;
|
|
10028
|
+
const known = checks.filter((c) => c.status === "KNOWN").length;
|
|
10029
|
+
console.log("");
|
|
10030
|
+
const passes = checks.filter((c) => c.status === "PASS").length;
|
|
10031
|
+
if (fails) console.log(import_chalk50.default.red(` ${fails} check(s) FAILED.`));
|
|
10032
|
+
else if (passes) console.log(import_chalk50.default.green(` ${passes} check(s) passed, 0 failed.`));
|
|
10033
|
+
else console.log(import_chalk50.default.yellow(" NOTHING WAS VERIFIED \u2014 every check was skipped."));
|
|
10034
|
+
if (known) console.log(import_chalk50.default.yellow(` ${known} known-open issue(s) still outstanding.`));
|
|
10035
|
+
if (skips) console.log(import_chalk50.default.dim(` ${skips} check(s) NOT RUN (see SKIP above) \u2014 those invariants are unverified.`));
|
|
10036
|
+
console.log("");
|
|
10037
|
+
}
|
|
10038
|
+
|
|
10039
|
+
// src/commands/op.ts
|
|
9808
10040
|
var OPERATOR_EMAILS = /* @__PURE__ */ new Set(["julienpmjacquet@gmail.com", "chkev@umich.edu"]);
|
|
9809
10041
|
var DASHBOARD_BASE8 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
|
|
9810
10042
|
function isOperatorLogin() {
|
|
@@ -9834,102 +10066,106 @@ var OP_COMMANDS = [
|
|
|
9834
10066
|
{ cmd: "op latency", blurb: "which FEATURE is slow, and what inside it", extra: "--hours N --team T --project P" },
|
|
9835
10067
|
{ cmd: "op latency slow", blurb: "slowest requests, each with a request id you can look up", extra: "--min-ms N" },
|
|
9836
10068
|
{ cmd: "op latency llm", blurb: "chat timing by provider/model, tokens, credit-reserve cost" },
|
|
10069
|
+
{ cmd: "op latency grades", blurb: "daily %% excellent / okay / bad / terrible, and who to blame", extra: "--days N" },
|
|
9837
10070
|
{ cmd: "op mark", blurb: 'record "I changed something significant just now"', extra: 'apiblaze op mark "cached tenant count"' },
|
|
9838
10071
|
{ cmd: "op latency compare", blurb: "latency before vs after the last mark, equal windows", extra: "--hours N" },
|
|
9839
10072
|
{ cmd: "op credits", blurb: "list credit wallets across all teams" },
|
|
10073
|
+
{ cmd: "op billing", blurb: "billing conformance: every door metered, 1 request = 1 charge", extra: "--key K --count N" },
|
|
10074
|
+
{ cmd: "op billing doors", blurb: "every live route + workers.dev URL, and whether it passes the meter (free, read-only)" },
|
|
10075
|
+
{ cmd: "op billing meter", blurb: "send known traffic, assert the ledger moved by exactly that much", extra: "spends a few tenths of a cent" },
|
|
9840
10076
|
{ cmd: "op residue", blurb: "external-store residue report (Upstash + Neon/OpenFGA) \u2014 dry run, deletes nothing" },
|
|
9841
10077
|
{ cmd: "op sweep", blurb: "delete the orphans the residue report found", extra: "asks first; -y to skip" }
|
|
9842
10078
|
];
|
|
9843
10079
|
function renderOpCommands() {
|
|
9844
10080
|
const width = Math.max(...OP_COMMANDS.map((c) => c.cmd.length)) + 10;
|
|
9845
10081
|
const lines = OP_COMMANDS.map((c) => {
|
|
9846
|
-
const left = ` ${
|
|
10082
|
+
const left = ` ${import_chalk51.default.cyan(`apiblaze ${c.cmd}`)}`;
|
|
9847
10083
|
const pad = " ".repeat(Math.max(1, width - c.cmd.length));
|
|
9848
|
-
return `${left}${pad}${c.blurb}${c.extra ? " " +
|
|
10084
|
+
return `${left}${pad}${c.blurb}${c.extra ? " " + import_chalk51.default.dim(`(${c.extra})`) : ""}`;
|
|
9849
10085
|
});
|
|
9850
10086
|
return [
|
|
9851
|
-
|
|
10087
|
+
import_chalk51.default.bold("Operator commands"),
|
|
9852
10088
|
...lines,
|
|
9853
10089
|
"",
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
10090
|
+
import_chalk51.default.dim(" Operators only. The gate is server-side (dashboard /api/cli/op checks the"),
|
|
10091
|
+
import_chalk51.default.dim(" signed-in email, admin-api re-checks with operatorGate) \u2014 a patched CLI just"),
|
|
10092
|
+
import_chalk51.default.dim(" gets 403s. Every op call is read-only except `op sweep`."),
|
|
9857
10093
|
"",
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
10094
|
+
import_chalk51.default.dim(" Not a CLI command: to prune all non-CP data run scripts/nuke-but-cp.sh --apply --sweep"),
|
|
10095
|
+
import_chalk51.default.dim(" in the repo. Operator dashboards (dlq, thresholds, throttling, pricing, billing,"),
|
|
10096
|
+
import_chalk51.default.dim(" agent-spend, teams, tests, leak-detection, lifecycle) live at /operator/* in the app.")
|
|
9861
10097
|
].join("\n");
|
|
9862
10098
|
}
|
|
9863
10099
|
function printResidue(report, applied) {
|
|
9864
10100
|
const up = report?.upstash ?? {};
|
|
9865
10101
|
const fga = report?.fga ?? {};
|
|
9866
10102
|
const ghosts = report?.ghosts ?? {};
|
|
9867
|
-
console.log(
|
|
9868
|
-
console.log(
|
|
10103
|
+
console.log(import_chalk51.default.bold(applied ? "\nExternal-residue sweep" : "\nExternal residue (dry-run \u2014 nothing deleted)"));
|
|
10104
|
+
console.log(import_chalk51.default.bold("\n Upstash"));
|
|
9869
10105
|
const orphans = up.orphans ?? [];
|
|
9870
|
-
if (orphans.length === 0) console.log(
|
|
9871
|
-
for (const o of orphans) console.log(` ${
|
|
9872
|
-
console.log(
|
|
10106
|
+
if (orphans.length === 0) console.log(import_chalk51.default.green(" no orphaned keys"));
|
|
10107
|
+
for (const o of orphans) console.log(` ${import_chalk51.default.yellow(o.key)} ${import_chalk51.default.dim(`\u2014 ${o.reason}`)}`);
|
|
10108
|
+
console.log(import_chalk51.default.dim(` kept (live principals): ${up.kept ?? 0} \xB7 anon wallets (untouched): ${up.anon_wallets ?? 0}`));
|
|
9873
10109
|
if (up.anon_wallet_detail) {
|
|
9874
10110
|
const d = up.anon_wallet_detail;
|
|
9875
|
-
console.log(
|
|
10111
|
+
console.log(import_chalk51.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)"}`));
|
|
9876
10112
|
}
|
|
9877
10113
|
if (up.keyspace_census) {
|
|
9878
10114
|
const census = Object.entries(up.keyspace_census).map(([k, v]) => `${k}=${v}`).join(" \xB7 ");
|
|
9879
|
-
console.log(
|
|
10115
|
+
console.log(import_chalk51.default.dim(` keyspace: ${census}`));
|
|
9880
10116
|
}
|
|
9881
|
-
if (up.unknown?.length) console.log(
|
|
9882
|
-
if (applied) console.log(` ${
|
|
9883
|
-
for (const e of up.errors ?? []) console.log(
|
|
9884
|
-
console.log(
|
|
10117
|
+
if (up.unknown?.length) console.log(import_chalk51.default.dim(` unknown (never deleted): ${up.unknown.join(", ")}`));
|
|
10118
|
+
if (applied) console.log(` ${import_chalk51.default.bold(String(up.deleted ?? 0))} key(s) deleted`);
|
|
10119
|
+
for (const e of up.errors ?? []) console.log(import_chalk51.default.red(` error: ${e}`));
|
|
10120
|
+
console.log(import_chalk51.default.bold("\n OpenFGA / Neon \u2014 orphan stores"));
|
|
9885
10121
|
if (applied) {
|
|
9886
10122
|
const swept = fga?.swept ?? [];
|
|
9887
|
-
if (swept.length === 0) console.log(
|
|
10123
|
+
if (swept.length === 0) console.log(import_chalk51.default.green(" no orphaned stores"));
|
|
9888
10124
|
for (const s of swept) {
|
|
9889
10125
|
console.log(
|
|
9890
|
-
` ${
|
|
10126
|
+
` ${import_chalk51.default.yellow(s.store_id)} ${import_chalk51.default.dim(`\u2014 store ${s.openfga_deleted ? "deleted" : "DEFERRED"}, ${s.neon_deleted} Neon tuple(s) purged`)}`
|
|
9891
10127
|
);
|
|
9892
10128
|
}
|
|
9893
|
-
if (fga?.remaining) console.log(
|
|
10129
|
+
if (fga?.remaining) console.log(import_chalk51.default.yellow(` ${fga.remaining} more orphan store(s) \u2014 re-run to drain`));
|
|
9894
10130
|
const st = fga?.side_tables;
|
|
9895
|
-
if (st) console.log(
|
|
10131
|
+
if (st) console.log(import_chalk51.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})` : ""}`));
|
|
9896
10132
|
} else {
|
|
9897
10133
|
const fgaOrphans = fga?.orphans ?? [];
|
|
9898
|
-
if (fgaOrphans.length === 0) console.log(
|
|
10134
|
+
if (fgaOrphans.length === 0) console.log(import_chalk51.default.green(" no orphaned stores"));
|
|
9899
10135
|
for (const s of fgaOrphans) {
|
|
9900
10136
|
const src = s.in_openfga ? "live in OpenFGA" : "Neon tuples only";
|
|
9901
|
-
console.log(` ${
|
|
10137
|
+
console.log(` ${import_chalk51.default.yellow(s.store_id)} ${import_chalk51.default.dim(`\u2014 ${src}${s.name ? ` (${s.name})` : ""}, ${s.neon_tuples} Neon tuple(s)`)}`);
|
|
9902
10138
|
}
|
|
9903
|
-
console.log(
|
|
10139
|
+
console.log(import_chalk51.default.dim(` kept stores: ${(fga?.kept_store_ids ?? []).length}`));
|
|
9904
10140
|
const st = fga?.side_tables;
|
|
9905
|
-
if (st) console.log(
|
|
10141
|
+
if (st) console.log(import_chalk51.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`));
|
|
9906
10142
|
}
|
|
9907
|
-
for (const e of fga?.errors ?? []) console.log(
|
|
9908
|
-
console.log(
|
|
10143
|
+
for (const e of fga?.errors ?? []) console.log(import_chalk51.default.red(` error: ${e}`));
|
|
10144
|
+
console.log(import_chalk51.default.bold("\n OpenFGA \u2014 ghost tuples in surviving stores"));
|
|
9909
10145
|
if (applied) {
|
|
9910
|
-
if ((ghosts?.ghost_count ?? 0) === 0) console.log(
|
|
9911
|
-
else console.log(` ${
|
|
10146
|
+
if ((ghosts?.ghost_count ?? 0) === 0) console.log(import_chalk51.default.green(" no ghost tuples"));
|
|
10147
|
+
else console.log(` ${import_chalk51.default.bold(String(ghosts.deleted ?? 0))} ghost tuple(s) deleted ${import_chalk51.default.dim(`(of ${ghosts.ghost_count} found, ${ghosts.scanned_tuples} scanned across ${ghosts.live_stores} live stores)`)}`);
|
|
9912
10148
|
} else {
|
|
9913
10149
|
const n = ghosts?.ghost_count ?? 0;
|
|
9914
|
-
if (n === 0) console.log(
|
|
10150
|
+
if (n === 0) console.log(import_chalk51.default.green(` no ghost tuples ${import_chalk51.default.dim(`(${ghosts.scanned_tuples ?? 0} scanned across ${ghosts.live_stores ?? 0} live stores)`)}`));
|
|
9915
10151
|
else {
|
|
9916
|
-
console.log(
|
|
10152
|
+
console.log(import_chalk51.default.yellow(` ${n} ghost tuple(s) referencing entities absent from D1:`));
|
|
9917
10153
|
for (const g of (ghosts.ghosts ?? []).slice(0, 20)) {
|
|
9918
|
-
console.log(
|
|
10154
|
+
console.log(import_chalk51.default.dim(` ${g.object_type}:${g.object_id} ${g.relation} ${g._user}`));
|
|
9919
10155
|
}
|
|
9920
|
-
if (n > 20) console.log(
|
|
10156
|
+
if (n > 20) console.log(import_chalk51.default.dim(` \u2026 and ${n - 20} more`));
|
|
9921
10157
|
}
|
|
9922
10158
|
}
|
|
9923
|
-
for (const e of ghosts?.errors ?? []) console.log(
|
|
10159
|
+
for (const e of ghosts?.errors ?? []) console.log(import_chalk51.default.red(` error: ${e}`));
|
|
9924
10160
|
console.log();
|
|
9925
10161
|
}
|
|
9926
10162
|
async function runOp(sub, opts = {}, view) {
|
|
9927
10163
|
if (!loadCredentials()) {
|
|
9928
|
-
console.log(
|
|
10164
|
+
console.log(import_chalk51.default.dim("Not logged in. Run `apiblaze login`."));
|
|
9929
10165
|
return;
|
|
9930
10166
|
}
|
|
9931
10167
|
if (!isOperatorLogin()) {
|
|
9932
|
-
console.log(
|
|
10168
|
+
console.log(import_chalk51.default.dim("`apiblaze op` is only available to platform operators."));
|
|
9933
10169
|
return;
|
|
9934
10170
|
}
|
|
9935
10171
|
switch (sub) {
|
|
@@ -9955,17 +10191,17 @@ async function runOp(sub, opts = {}, view) {
|
|
|
9955
10191
|
const nSide = (st.soft_deleted_stores ?? 0) + (st.orphan_models ?? 0) + (st.orphan_changelog ?? 0);
|
|
9956
10192
|
printResidue(report, false);
|
|
9957
10193
|
if (nUp + nFga + nGhost + nSide === 0) {
|
|
9958
|
-
console.log(
|
|
10194
|
+
console.log(import_chalk51.default.green("Nothing to sweep."));
|
|
9959
10195
|
return;
|
|
9960
10196
|
}
|
|
9961
10197
|
if (!opts.yes) {
|
|
9962
10198
|
const readline3 = await import("readline/promises");
|
|
9963
10199
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
9964
10200
|
const answer = await rl.question(
|
|
9965
|
-
|
|
10201
|
+
import_chalk51.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: `)
|
|
9966
10202
|
);
|
|
9967
10203
|
rl.close();
|
|
9968
|
-
if (answer.trim() !== "sweep") return void console.log(
|
|
10204
|
+
if (answer.trim() !== "sweep") return void console.log(import_chalk51.default.dim("Aborted."));
|
|
9969
10205
|
}
|
|
9970
10206
|
const result = await opCall({ method: "POST", path: "/operator/external-residue/sweep", summary: "external residue sweep" });
|
|
9971
10207
|
if (opts.json) return void console.log(JSON.stringify(result, null, 2));
|
|
@@ -9975,26 +10211,61 @@ async function runOp(sub, opts = {}, view) {
|
|
|
9975
10211
|
case "mark": {
|
|
9976
10212
|
const label3 = (view ?? "").trim();
|
|
9977
10213
|
if (!label3) {
|
|
9978
|
-
console.log(
|
|
10214
|
+
console.log(import_chalk51.default.red("Give the change a name:") + import_chalk51.default.cyan(' apiblaze op mark "cached tenant count"'));
|
|
9979
10215
|
return;
|
|
9980
10216
|
}
|
|
9981
10217
|
const res = await opCall({ method: "POST", path: "/operator/latency/mark", body: { label: label3 }, summary: "record change marker" });
|
|
9982
10218
|
const ts = new Date(res?.marker?.ts ?? Date.now()).toISOString();
|
|
9983
|
-
console.log(
|
|
9984
|
-
Marked: `) +
|
|
9985
|
-
console.log(
|
|
9986
|
-
console.log(
|
|
10219
|
+
console.log(import_chalk51.default.green(`
|
|
10220
|
+
Marked: `) + import_chalk51.default.bold(label3));
|
|
10221
|
+
console.log(import_chalk51.default.dim(` ${ts}`));
|
|
10222
|
+
console.log(import_chalk51.default.dim(` Once traffic has run on both sides, compare with: `) + import_chalk51.default.cyan("apiblaze op latency compare") + "\n");
|
|
9987
10223
|
return;
|
|
9988
10224
|
}
|
|
9989
10225
|
case "credits": {
|
|
9990
10226
|
const data = await opCall({ method: "GET", path: "/operator/credits", summary: "list credit wallets" });
|
|
9991
10227
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
9992
10228
|
const accounts = data?.accounts ?? [];
|
|
9993
|
-
if (accounts.length === 0) return void console.log(
|
|
10229
|
+
if (accounts.length === 0) return void console.log(import_chalk51.default.dim("No credit wallets."));
|
|
9994
10230
|
for (const a of accounts) {
|
|
9995
10231
|
const bal = typeof a.balance_cents === "number" ? `$${(a.balance_cents / 100).toFixed(2)}` : "?";
|
|
9996
|
-
console.log(` ${
|
|
10232
|
+
console.log(` ${import_chalk51.default.bold(bal.padStart(9))} ${a.walletId}${a.owner_email ? import_chalk51.default.dim(` \u2014 ${a.owner_email}`) : a.anon ? import_chalk51.default.dim(" \u2014 anon") : ""}`);
|
|
10233
|
+
}
|
|
10234
|
+
return;
|
|
10235
|
+
}
|
|
10236
|
+
// ── BILLING (specs/billing/callable_outside_a_request.md) ─────────────
|
|
10237
|
+
// Two invariants: every door is metered (so nobody runs their RBAC/OpenFGA on
|
|
10238
|
+
// us for free), and one request is charged one request (so closing a free lane
|
|
10239
|
+
// by billing it can never turn into a double charge).
|
|
10240
|
+
case "billing": {
|
|
10241
|
+
const which = (view ?? "").trim().toLowerCase();
|
|
10242
|
+
if (which && which !== "doors" && which !== "meter") {
|
|
10243
|
+
return void console.log(import_chalk51.default.red(`Unknown: apiblaze op billing ${which}. Use 'doors', 'meter', or neither for both.`));
|
|
10244
|
+
}
|
|
10245
|
+
const checks = [];
|
|
10246
|
+
let doorsData = null;
|
|
10247
|
+
if (which !== "meter") {
|
|
10248
|
+
doorsData = await opCall({ method: "GET", path: "/operator/billing/doors", summary: "billing door audit" });
|
|
10249
|
+
if (!opts.json) checks.push(...printDoors(doorsData));
|
|
10250
|
+
}
|
|
10251
|
+
if (which !== "doors") {
|
|
10252
|
+
const teamId = opts.team || void 0;
|
|
10253
|
+
const readLedger = async () => {
|
|
10254
|
+
const q = teamId ? `?team_id=${encodeURIComponent(String(teamId))}` : "";
|
|
10255
|
+
return await opCall({
|
|
10256
|
+
method: "GET",
|
|
10257
|
+
path: `/operator/billing/ledger${q || "?team_id=team_1782844865835_zujrf"}`,
|
|
10258
|
+
summary: "billing ledger snapshot"
|
|
10259
|
+
});
|
|
10260
|
+
};
|
|
10261
|
+
checks.push(...await runMeter(readLedger, {
|
|
10262
|
+
key: opts.key,
|
|
10263
|
+
mcpHost: opts.mcpHost,
|
|
10264
|
+
count: opts.count ? Number(opts.count) : void 0
|
|
10265
|
+
}));
|
|
9997
10266
|
}
|
|
10267
|
+
if (opts.json) return void console.log(JSON.stringify({ doors: doorsData, checks }, null, 2));
|
|
10268
|
+
printChecks(checks);
|
|
9998
10269
|
return;
|
|
9999
10270
|
}
|
|
10000
10271
|
// ── LATENCY (specs/latency/ §7.4) ─────────────────────────────────────
|
|
@@ -10014,7 +10285,11 @@ async function runOp(sub, opts = {}, view) {
|
|
|
10014
10285
|
["route", "route"],
|
|
10015
10286
|
["environment", "environment"],
|
|
10016
10287
|
["statusClass", "status_class"],
|
|
10017
|
-
["minMs", "min_ms"]
|
|
10288
|
+
["minMs", "min_ms"],
|
|
10289
|
+
["days", "days"],
|
|
10290
|
+
["excellent", "excellent"],
|
|
10291
|
+
["okay", "okay"],
|
|
10292
|
+
["bad", "bad"]
|
|
10018
10293
|
];
|
|
10019
10294
|
for (const [optKey, queryKey] of FILTERS) {
|
|
10020
10295
|
const v = opts[optKey];
|
|
@@ -10022,28 +10297,59 @@ async function runOp(sub, opts = {}, view) {
|
|
|
10022
10297
|
}
|
|
10023
10298
|
const q = qs.toString() ? `?${qs}` : "";
|
|
10024
10299
|
const which = view;
|
|
10300
|
+
if (which === "grades") {
|
|
10301
|
+
const data = await opCall({ method: "GET", path: `/operator/latency/grades${q}`, summary: "latency grades" });
|
|
10302
|
+
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
10303
|
+
const t = data.thresholds_ms;
|
|
10304
|
+
console.log(import_chalk51.default.bold("\nHow good was apiblaze itself?") + import_chalk51.default.dim(" (our overhead only \u2014 a slow customer API never counts against us)"));
|
|
10305
|
+
console.log(import_chalk51.default.dim(` excellent <${t.excellent.replace("<", "")}ms \xB7 okay ${t.okay}ms \xB7 bad ${t.bad}ms \xB7 terrible ${t.terrible.replace(">=", "")}ms+
|
|
10306
|
+
`));
|
|
10307
|
+
console.log(import_chalk51.default.dim(" date reqs excellent okay bad terrible"));
|
|
10308
|
+
for (const d of data.days ?? []) {
|
|
10309
|
+
const p = d.pct;
|
|
10310
|
+
const cell = (v, colour) => v > 0 ? colour(`${String(v).padStart(5)}%`) : import_chalk51.default.dim(`${String(v).padStart(5)}%`);
|
|
10311
|
+
console.log(
|
|
10312
|
+
` ${d.date} ${String(d.total).padStart(5)} ${cell(p.excellent, import_chalk51.default.green)} ${cell(p.okay, import_chalk51.default.cyan)} ${cell(p.bad, import_chalk51.default.yellow)} ${cell(p.terrible, import_chalk51.default.red)}`
|
|
10313
|
+
);
|
|
10314
|
+
}
|
|
10315
|
+
const cul = data.culprits ?? [];
|
|
10316
|
+
if (cul.length) {
|
|
10317
|
+
console.log(import_chalk51.default.bold("\n Who caused the bad and terrible ones\n"));
|
|
10318
|
+
console.log(import_chalk51.default.dim(" bad terrible feature \u2192 dependency"));
|
|
10319
|
+
for (const r of cul.slice(0, 12)) {
|
|
10320
|
+
if (!r.bad && !r.terrible) continue;
|
|
10321
|
+
console.log(
|
|
10322
|
+
` ${String(r.bad).padStart(6)} ${import_chalk51.default.red(String(r.terrible).padStart(8))} ${import_chalk51.default.yellow(r.feature)} ${import_chalk51.default.dim("\u2192")} ${import_chalk51.default.cyan(r.dep)}`
|
|
10323
|
+
);
|
|
10324
|
+
}
|
|
10325
|
+
}
|
|
10326
|
+
if (data.caveat) console.log(import_chalk51.default.dim(`
|
|
10327
|
+
\u26A0 ${data.caveat}
|
|
10328
|
+
`));
|
|
10329
|
+
return;
|
|
10330
|
+
}
|
|
10025
10331
|
if (which === "compare") {
|
|
10026
10332
|
const data = await opCall({ method: "GET", path: `/operator/latency/compare${q}`, summary: "latency before/after" });
|
|
10027
10333
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
10028
10334
|
const b = data.before, a = data.after, d = data.delta;
|
|
10029
|
-
console.log(
|
|
10030
|
-
Before vs after: `) +
|
|
10031
|
-
console.log(
|
|
10335
|
+
console.log(import_chalk51.default.bold(`
|
|
10336
|
+
Before vs after: `) + import_chalk51.default.cyan(data.marker.label));
|
|
10337
|
+
console.log(import_chalk51.default.dim(` marked ${new Date(data.marker.ts).toISOString()} \xB7 ${data.window_hours}h either side
|
|
10032
10338
|
`));
|
|
10033
10339
|
const row = (name, before, after, delta) => {
|
|
10034
10340
|
const arrow = delta === 0 ? "=" : delta < 0 ? "\u2193" : "\u2191";
|
|
10035
10341
|
const txt = `${String(before).padStart(6)}ms \u2192${String(after).padStart(7)}ms ${arrow}${Math.abs(delta)}ms`;
|
|
10036
|
-
console.log(` ${name.padEnd(22)}${data.trustworthy ? delta <= 0 ?
|
|
10342
|
+
console.log(` ${name.padEnd(22)}${data.trustworthy ? delta <= 0 ? import_chalk51.default.green(txt) : import_chalk51.default.red(txt) : import_chalk51.default.dim(txt)}`);
|
|
10037
10343
|
};
|
|
10038
|
-
console.log(
|
|
10344
|
+
console.log(import_chalk51.default.dim(" metric before after change"));
|
|
10039
10345
|
row("total p50", b.total_p50, a.total_p50, d.total_p50);
|
|
10040
10346
|
row("total p95", b.total_p95, a.total_p95, d.total_p95);
|
|
10041
10347
|
row("apiblaze overhead p50", b.gw_p50, a.gw_p50, d.gw_p50);
|
|
10042
10348
|
row("apiblaze overhead p95", b.gw_p95, a.gw_p95, d.gw_p95);
|
|
10043
|
-
console.log(
|
|
10349
|
+
console.log(import_chalk51.default.dim(`
|
|
10044
10350
|
requests: ${b.requests} before \xB7 ${a.requests} after`));
|
|
10045
10351
|
for (const w of data.warnings ?? []) {
|
|
10046
|
-
console.log((data.trustworthy ?
|
|
10352
|
+
console.log((data.trustworthy ? import_chalk51.default.dim : import_chalk51.default.yellow)(` ${data.trustworthy ? "\xB7" : "\u26A0"} ${w}`));
|
|
10047
10353
|
}
|
|
10048
10354
|
console.log("");
|
|
10049
10355
|
return;
|
|
@@ -10052,19 +10358,19 @@ Before vs after: `) + import_chalk50.default.cyan(data.marker.label));
|
|
|
10052
10358
|
const data = await opCall({ method: "GET", path: `/operator/latency/slow${q}`, summary: "slowest requests" });
|
|
10053
10359
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
10054
10360
|
const rows2 = data?.rows ?? [];
|
|
10055
|
-
if (!rows2.length) return void console.log(
|
|
10056
|
-
console.log(
|
|
10361
|
+
if (!rows2.length) return void console.log(import_chalk51.default.dim("No requests over the threshold in that window."));
|
|
10362
|
+
console.log(import_chalk51.default.bold(`
|
|
10057
10363
|
Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
|
|
10058
10364
|
`));
|
|
10059
|
-
console.log(
|
|
10365
|
+
console.log(import_chalk51.default.dim(" total ours theirs blame request id"));
|
|
10060
10366
|
for (const r of rows2.slice(0, 30)) {
|
|
10061
10367
|
console.log(
|
|
10062
|
-
` ${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)} ${
|
|
10368
|
+
` ${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_chalk51.default.yellow(`${r.slow_gw_feature || "-"}\u2192${r.slow_dep || "-"}`.padEnd(20))} ${import_chalk51.default.dim(r.request_id || "")}`
|
|
10063
10369
|
);
|
|
10064
10370
|
}
|
|
10065
|
-
console.log(
|
|
10371
|
+
console.log(import_chalk51.default.dim(`
|
|
10066
10372
|
The last column is the request id (Cloudflare calls it a "cf-ray"). Look one up with`));
|
|
10067
|
-
console.log(
|
|
10373
|
+
console.log(import_chalk51.default.dim(` \`apiblaze logs\` for that request's exact per-feature breakdown \u2014 unsampled, unlike the table above.
|
|
10068
10374
|
`));
|
|
10069
10375
|
return;
|
|
10070
10376
|
}
|
|
@@ -10072,17 +10378,17 @@ Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
|
|
|
10072
10378
|
const data = await opCall({ method: "GET", path: `/operator/latency/llm${q}`, summary: "llm latency" });
|
|
10073
10379
|
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
10074
10380
|
const rows2 = data?.rows ?? [];
|
|
10075
|
-
if (!rows2.length) return void console.log(
|
|
10076
|
-
console.log(
|
|
10381
|
+
if (!rows2.length) return void console.log(import_chalk51.default.dim("No LLM traffic in that window."));
|
|
10382
|
+
console.log(import_chalk51.default.bold(`
|
|
10077
10383
|
LLM timing \u2014 last ${data.window_hours}h
|
|
10078
10384
|
`));
|
|
10079
|
-
console.log(
|
|
10385
|
+
console.log(import_chalk51.default.dim(" requests gen p95 reserve p95 in/out tokens p95 model"));
|
|
10080
10386
|
for (const r of rows2) {
|
|
10081
10387
|
console.log(
|
|
10082
10388
|
` ${String(Math.round(r.requests)).padStart(8)} ${String(Math.round(r.gen_p95_ms)).padStart(6)}ms ${String(Math.round(r.reserve_p95_ms)).padStart(9)}ms ${String(Math.round(r.input_tokens_p95)).padStart(6)}/${String(Math.round(r.output_tokens_p95)).padEnd(6)} ${r.model || "-"}`
|
|
10083
10389
|
);
|
|
10084
10390
|
}
|
|
10085
|
-
console.log(
|
|
10391
|
+
console.log(import_chalk51.default.dim(`
|
|
10086
10392
|
${data.note}
|
|
10087
10393
|
`));
|
|
10088
10394
|
return;
|
|
@@ -10093,26 +10399,26 @@ LLM timing \u2014 last ${data.window_hours}h
|
|
|
10093
10399
|
]);
|
|
10094
10400
|
if (opts.json) return void console.log(JSON.stringify({ blame, summary }, null, 2));
|
|
10095
10401
|
const ov = summary?.apiblaze_overhead_ms ?? {};
|
|
10096
|
-
console.log(
|
|
10402
|
+
console.log(import_chalk51.default.bold(`
|
|
10097
10403
|
Latency \u2014 last ${summary?.window_hours ?? "?"}h, ${Number(summary?.requests ?? 0).toLocaleString()} requests
|
|
10098
10404
|
`));
|
|
10099
|
-
console.log(` ${
|
|
10100
|
-
console.log(` ${
|
|
10101
|
-
console.log(
|
|
10405
|
+
console.log(` ${import_chalk51.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_chalk51.default.dim("\u2190 ours")}`);
|
|
10406
|
+
console.log(` ${import_chalk51.default.bold("upstream ttfb ")} ${" ".repeat(24)}p95 ${String(summary?.upstream_ttfb_p95_ms ?? 0).padStart(6)}ms ${import_chalk51.default.dim("\u2190 theirs")}`);
|
|
10407
|
+
console.log(import_chalk51.default.dim(` (per-request percentiles \u2014 never subtract one from the other)
|
|
10102
10408
|
`));
|
|
10103
10409
|
const rows = blame?.blame ?? [];
|
|
10104
|
-
if (!rows.length) return void console.log(
|
|
10105
|
-
console.log(
|
|
10106
|
-
console.log(
|
|
10410
|
+
if (!rows.length) return void console.log(import_chalk51.default.dim("No latency rows in that window."));
|
|
10411
|
+
console.log(import_chalk51.default.bold(" Which feature ate the time, and what inside it\n"));
|
|
10412
|
+
console.log(import_chalk51.default.dim(" share p95 feature \u2192 dependency"));
|
|
10107
10413
|
for (const r of rows.slice(0, 15)) {
|
|
10108
10414
|
const share = `${(r.share * 100).toFixed(1)}%`;
|
|
10109
|
-
console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${
|
|
10415
|
+
console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${import_chalk51.default.yellow(r.feature)} ${import_chalk51.default.dim("\u2192")} ${import_chalk51.default.cyan(r.dep)}`);
|
|
10110
10416
|
}
|
|
10111
10417
|
console.log("");
|
|
10112
10418
|
return;
|
|
10113
10419
|
}
|
|
10114
10420
|
default:
|
|
10115
|
-
console.log(
|
|
10421
|
+
console.log(import_chalk51.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
10116
10422
|
}
|
|
10117
10423
|
}
|
|
10118
10424
|
|
|
@@ -10175,7 +10481,7 @@ program.command("dev").description("Put your localhost behind a public URL (dev
|
|
|
10175
10481
|
try {
|
|
10176
10482
|
const resolved = parseInt(port ?? opts.port, 10);
|
|
10177
10483
|
if (Number.isNaN(resolved)) {
|
|
10178
|
-
console.error(
|
|
10484
|
+
console.error(import_chalk52.default.red(`Invalid port: ${port ?? opts.port}`));
|
|
10179
10485
|
process.exit(1);
|
|
10180
10486
|
}
|
|
10181
10487
|
await runDev({ port: resolved, project: opts.project, yes: opts.yes, captureFile: opts.captureFile, newSession: opts.newSession });
|
|
@@ -10274,7 +10580,7 @@ apikeys.command("list").description("List control-plane API keys in your team").
|
|
|
10274
10580
|
apikeys.command("mint").description("Create a control-plane API key (secret shown once)").option("--desc <text>", "Description").option("--expires-days <n>", "Expiry in days (default 90 server-side)").option("--team <id|name>", "Team (defaults to active team)").option("--json", "Output machine-readable JSON").action(action((opts, cmd) => runKeyMint({ ...cmd.parent?.opts(), ...opts })));
|
|
10275
10581
|
apikeys.command("revoke").description("Revoke a control-plane API key").argument("<keyId>", "Key id (see `apikeys list`)").option("--team <id|name>", "Team (defaults to active team)").action(action((keyId, opts, cmd) => runKeyRevoke(keyId, { ...cmd.parent?.opts(), ...opts })));
|
|
10276
10582
|
program.addCommand(apikeys, { hidden: true });
|
|
10277
|
-
var op = new import_commander.Command("op").description("Operator menu (platform operators only)").argument("[action]", "latency | mark | credits | residue | sweep (omit for the menu)").argument("[view]", "for `latency`: slow | llm | compare \xB7 for `mark`: the change name, quoted").option("-y, --yes", "Skip the sweep confirmation prompt").option("--json", "Output machine-readable JSON").option("--hours <n>", "Look-back window in hours (default 1)").option("--team <id>", "Filter to one team").option("--project <name>", "Filter to one proxy").option("--api-version <v>", "Filter to one API version").option("--tenant <name>", "Filter to one tenant").option("--route <path>", "Filter to one route").option("--environment <env>", "Filter to one environment").option("--status-class <c>", "Filter to a status class (2xx/4xx/5xx)").option("--min-ms <n>", "For `latency slow`: minimum duration (default 1000)").action(action((sub, view, opts) => runOp(sub, opts, view))).addHelpText("after", () => `
|
|
10583
|
+
var op = new import_commander.Command("op").description("Operator menu (platform operators only)").argument("[action]", "latency | billing | mark | credits | residue | sweep (omit for the menu)").argument("[view]", "for `latency`: slow | llm | compare | grades \xB7 for `billing`: doors | meter \xB7 for `mark`: the change name, quoted").option("-y, --yes", "Skip the sweep confirmation prompt").option("--json", "Output machine-readable JSON").option("--hours <n>", "Look-back window in hours (default 1)").option("--team <id>", "Filter to one team").option("--project <name>", "Filter to one proxy").option("--api-version <v>", "Filter to one API version").option("--tenant <name>", "Filter to one tenant").option("--route <path>", "Filter to one route").option("--environment <env>", "Filter to one environment").option("--status-class <c>", "Filter to a status class (2xx/4xx/5xx)").option("--min-ms <n>", "For `latency slow`: minimum duration (default 1000)").option("--days <n>", "For `latency grades`: how many days back (default 7)").option("--excellent <ms>", "Grade threshold: under this is excellent (default 250)").option("--okay <ms>", "Grade threshold: under this is okay (default 500)").option("--bad <ms>", "Grade threshold: under this is bad, at/over is terrible (default 1000)").option("--key <key>", "For `billing meter`: a data-plane API key for the sandbox proxy (without it the served-request checks are SKIPPED)").option("--count <n>", "For `billing meter`: requests per check (default 3, max 10)").option("--mcp-host <host>", "For `billing meter`: an MCP host to check tools/call is charged once").action(action((sub, view, opts) => runOp(sub, opts, view))).addHelpText("after", () => `
|
|
10278
10584
|
${renderOpCommands()}
|
|
10279
10585
|
`);
|
|
10280
10586
|
program.addCommand(op, { hidden: true });
|
|
@@ -10307,7 +10613,7 @@ function groupedCommandHelp() {
|
|
|
10307
10613
|
const sub = byName.get(e.parent)?.commands.find((s) => s.name() === e.sub);
|
|
10308
10614
|
return sub ? ` ${helpLabel(e).padEnd(width)}${sub.description()}` : "";
|
|
10309
10615
|
}).filter(Boolean).join("\n");
|
|
10310
|
-
return `${
|
|
10616
|
+
return `${import_chalk52.default.bold(g.title)}
|
|
10311
10617
|
${rows}`;
|
|
10312
10618
|
}).join("\n\n");
|
|
10313
10619
|
}
|
|
@@ -10345,14 +10651,14 @@ async function recoverStaleTeam() {
|
|
|
10345
10651
|
const { resolveLinkedTeam: resolveLinkedTeam2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
10346
10652
|
const linked = await resolveLinkedTeam2({ preferredId: creds.teamId, interactive: !!process.stdin.isTTY });
|
|
10347
10653
|
if (!linked) {
|
|
10348
|
-
console.error(
|
|
10654
|
+
console.error(import_chalk52.default.yellow("Your account has no teams anymore (deleted?). Run `apiblaze login` or `apiblaze create` to get a workspace."));
|
|
10349
10655
|
return;
|
|
10350
10656
|
}
|
|
10351
10657
|
if (linked.teamId === creds.teamId) return;
|
|
10352
10658
|
const next = { ...creds, teamId: linked.teamId, teamName: linked.teamName };
|
|
10353
10659
|
delete next.activeTenant;
|
|
10354
10660
|
saveCredentials(next);
|
|
10355
|
-
console.error(
|
|
10661
|
+
console.error(import_chalk52.default.yellow(`Your previous team no longer exists \u2014 relinked to ${import_chalk52.default.bold(linked.teamName ?? linked.teamId)}. Re-run your command.`));
|
|
10356
10662
|
} catch {
|
|
10357
10663
|
}
|
|
10358
10664
|
}
|
|
@@ -10360,16 +10666,16 @@ async function printError(err) {
|
|
|
10360
10666
|
if (err instanceof ApiError) {
|
|
10361
10667
|
const data = err.body;
|
|
10362
10668
|
const extra = [data?.body?.reason, data?.body?.details, data?.details, data?.body?.error].find((x) => typeof x === "string" && x && x !== err.message);
|
|
10363
|
-
console.error(
|
|
10669
|
+
console.error(import_chalk52.default.red(`
|
|
10364
10670
|
API error (${err.status}): ${err.message}${extra ? ` \u2014 ${extra}` : ""}`));
|
|
10365
10671
|
if (err.status === 403 || err.status === 404) {
|
|
10366
10672
|
await recoverStaleTeam();
|
|
10367
10673
|
}
|
|
10368
10674
|
} else if (err instanceof Error) {
|
|
10369
|
-
console.error(
|
|
10675
|
+
console.error(import_chalk52.default.red(`
|
|
10370
10676
|
Error: ${err.message}`));
|
|
10371
10677
|
} else {
|
|
10372
|
-
console.error(
|
|
10678
|
+
console.error(import_chalk52.default.red("\nUnknown error"));
|
|
10373
10679
|
}
|
|
10374
10680
|
}
|
|
10375
10681
|
program.parse(process.argv);
|