apiblaze 0.20.3 → 0.20.4
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 +93 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -932,7 +932,7 @@ var import_commander = require("commander");
|
|
|
932
932
|
var import_chalk51 = __toESM(require("chalk"));
|
|
933
933
|
|
|
934
934
|
// package.json
|
|
935
|
-
var version = "0.20.
|
|
935
|
+
var version = "0.20.4";
|
|
936
936
|
|
|
937
937
|
// src/index.ts
|
|
938
938
|
init_types();
|
|
@@ -9888,7 +9888,7 @@ function printResidue(report, applied) {
|
|
|
9888
9888
|
for (const e of ghosts?.errors ?? []) console.log(import_chalk50.default.red(` error: ${e}`));
|
|
9889
9889
|
console.log();
|
|
9890
9890
|
}
|
|
9891
|
-
async function runOp(sub, opts = {}) {
|
|
9891
|
+
async function runOp(sub, opts = {}, view) {
|
|
9892
9892
|
if (!loadCredentials()) {
|
|
9893
9893
|
console.log(import_chalk50.default.dim("Not logged in. Run `apiblaze login`."));
|
|
9894
9894
|
return;
|
|
@@ -9904,6 +9904,9 @@ async function runOp(sub, opts = {}) {
|
|
|
9904
9904
|
console.log(` ${import_chalk50.default.cyan("apiblaze op residue")} external-store residue report (Upstash + Neon/OpenFGA, dry-run)`);
|
|
9905
9905
|
console.log(` ${import_chalk50.default.cyan("apiblaze op sweep")} delete the orphans the report shows (asks first; ${import_chalk50.default.dim("-y to skip")})`);
|
|
9906
9906
|
console.log(` ${import_chalk50.default.cyan("apiblaze op credits")} list credit wallets`);
|
|
9907
|
+
console.log(` ${import_chalk50.default.cyan("apiblaze op latency")} which FEATURE is slow, and what inside it ${import_chalk50.default.dim("(--hours N --team T --project P)")}`);
|
|
9908
|
+
console.log(` ${import_chalk50.default.cyan("apiblaze op latency slow")} the slowest requests, with a cf-ray to drill into`);
|
|
9909
|
+
console.log(` ${import_chalk50.default.cyan("apiblaze op latency llm")} chat timing by provider/model, tokens, credit-reserve cost`);
|
|
9907
9910
|
console.log(import_chalk50.default.dim(` (to prune all non-CP data: run scripts/nuke-but-cp.sh --apply --sweep in the repo)
|
|
9908
9911
|
`));
|
|
9909
9912
|
return;
|
|
@@ -9951,6 +9954,93 @@ async function runOp(sub, opts = {}) {
|
|
|
9951
9954
|
}
|
|
9952
9955
|
return;
|
|
9953
9956
|
}
|
|
9957
|
+
// ── LATENCY (specs/latency/ §7.4) ─────────────────────────────────────
|
|
9958
|
+
// The blame table first: level 1 (which feature) and level 2 (what inside
|
|
9959
|
+
// it) in one row. `upstream` is excluded from the blame column on purpose —
|
|
9960
|
+
// it is the biggest feature on almost every request, so blaming it would
|
|
9961
|
+
// report "upstream, 94%" forever and tell an operator nothing. The
|
|
9962
|
+
// summary's "us or them" split is where upstream shows up.
|
|
9963
|
+
case "latency": {
|
|
9964
|
+
const qs = new URLSearchParams();
|
|
9965
|
+
const FILTERS = [
|
|
9966
|
+
["hours", "hours"],
|
|
9967
|
+
["team", "team"],
|
|
9968
|
+
["project", "project"],
|
|
9969
|
+
["apiVersion", "api_version"],
|
|
9970
|
+
["tenant", "tenant"],
|
|
9971
|
+
["route", "route"],
|
|
9972
|
+
["environment", "environment"],
|
|
9973
|
+
["statusClass", "status_class"],
|
|
9974
|
+
["minMs", "min_ms"]
|
|
9975
|
+
];
|
|
9976
|
+
for (const [optKey, queryKey] of FILTERS) {
|
|
9977
|
+
const v = opts[optKey];
|
|
9978
|
+
if (v !== void 0 && v !== null && v !== "") qs.set(queryKey, String(v));
|
|
9979
|
+
}
|
|
9980
|
+
const q = qs.toString() ? `?${qs}` : "";
|
|
9981
|
+
const which = view;
|
|
9982
|
+
if (which === "slow") {
|
|
9983
|
+
const data = await opCall({ method: "GET", path: `/operator/latency/slow${q}`, summary: "slowest requests" });
|
|
9984
|
+
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
9985
|
+
const rows2 = data?.rows ?? [];
|
|
9986
|
+
if (!rows2.length) return void console.log(import_chalk50.default.dim("No requests over the threshold in that window."));
|
|
9987
|
+
console.log(import_chalk50.default.bold(`
|
|
9988
|
+
Slowest requests \u2014 last ${data.window_hours}h, over ${data.min_ms}ms
|
|
9989
|
+
`));
|
|
9990
|
+
console.log(import_chalk50.default.dim(" total ours theirs blame request (cf-ray)"));
|
|
9991
|
+
for (const r of rows2.slice(0, 30)) {
|
|
9992
|
+
console.log(
|
|
9993
|
+
` ${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_chalk50.default.yellow(`${r.slow_gw_feature || "-"}\u2192${r.slow_dep || "-"}`.padEnd(20))} ${import_chalk50.default.dim(r.request_id || "")}`
|
|
9994
|
+
);
|
|
9995
|
+
}
|
|
9996
|
+
console.log(import_chalk50.default.dim(`
|
|
9997
|
+
Take a cf-ray to \`apiblaze logs\` for the exact per-feature breakdown \u2014 that row is unsampled.
|
|
9998
|
+
`));
|
|
9999
|
+
return;
|
|
10000
|
+
}
|
|
10001
|
+
if (which === "llm") {
|
|
10002
|
+
const data = await opCall({ method: "GET", path: `/operator/latency/llm${q}`, summary: "llm latency" });
|
|
10003
|
+
if (opts.json) return void console.log(JSON.stringify(data, null, 2));
|
|
10004
|
+
const rows2 = data?.rows ?? [];
|
|
10005
|
+
if (!rows2.length) return void console.log(import_chalk50.default.dim("No LLM traffic in that window."));
|
|
10006
|
+
console.log(import_chalk50.default.bold(`
|
|
10007
|
+
LLM timing \u2014 last ${data.window_hours}h
|
|
10008
|
+
`));
|
|
10009
|
+
console.log(import_chalk50.default.dim(" requests gen p95 reserve p95 in/out tokens p95 model"));
|
|
10010
|
+
for (const r of rows2) {
|
|
10011
|
+
console.log(
|
|
10012
|
+
` ${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 || "-"}`
|
|
10013
|
+
);
|
|
10014
|
+
}
|
|
10015
|
+
console.log(import_chalk50.default.dim(`
|
|
10016
|
+
${data.note}
|
|
10017
|
+
`));
|
|
10018
|
+
return;
|
|
10019
|
+
}
|
|
10020
|
+
const [blame, summary] = await Promise.all([
|
|
10021
|
+
opCall({ method: "GET", path: `/operator/latency${q}`, summary: "latency blame table" }),
|
|
10022
|
+
opCall({ method: "GET", path: `/operator/latency/summary${q}`, summary: "latency summary" })
|
|
10023
|
+
]);
|
|
10024
|
+
if (opts.json) return void console.log(JSON.stringify({ blame, summary }, null, 2));
|
|
10025
|
+
const ov = summary?.apiblaze_overhead_ms ?? {};
|
|
10026
|
+
console.log(import_chalk50.default.bold(`
|
|
10027
|
+
Latency \u2014 last ${summary?.window_hours ?? "?"}h, ${Number(summary?.requests ?? 0).toLocaleString()} requests
|
|
10028
|
+
`));
|
|
10029
|
+
console.log(` ${import_chalk50.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_chalk50.default.dim("\u2190 ours")}`);
|
|
10030
|
+
console.log(` ${import_chalk50.default.bold("upstream ttfb ")} ${" ".repeat(24)}p95 ${String(summary?.upstream_ttfb_p95_ms ?? 0).padStart(6)}ms ${import_chalk50.default.dim("\u2190 theirs")}`);
|
|
10031
|
+
console.log(import_chalk50.default.dim(` (per-request percentiles \u2014 never subtract one from the other)
|
|
10032
|
+
`));
|
|
10033
|
+
const rows = blame?.blame ?? [];
|
|
10034
|
+
if (!rows.length) return void console.log(import_chalk50.default.dim("No latency rows in that window."));
|
|
10035
|
+
console.log(import_chalk50.default.bold(" Which feature ate the time, and what inside it\n"));
|
|
10036
|
+
console.log(import_chalk50.default.dim(" share p95 feature \u2192 dependency"));
|
|
10037
|
+
for (const r of rows.slice(0, 15)) {
|
|
10038
|
+
const share = `${(r.share * 100).toFixed(1)}%`;
|
|
10039
|
+
console.log(` ${share.padStart(6)} ${String(r.p95_ms).padStart(6)}ms ${import_chalk50.default.yellow(r.feature)} ${import_chalk50.default.dim("\u2192")} ${import_chalk50.default.cyan(r.dep)}`);
|
|
10040
|
+
}
|
|
10041
|
+
console.log("");
|
|
10042
|
+
return;
|
|
10043
|
+
}
|
|
9954
10044
|
default:
|
|
9955
10045
|
console.log(import_chalk50.default.red(`Unknown op subcommand '${sub}'. Run \`apiblaze op\` for the menu.`));
|
|
9956
10046
|
}
|
|
@@ -10114,7 +10204,7 @@ apikeys.command("list").description("List control-plane API keys in your team").
|
|
|
10114
10204
|
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 })));
|
|
10115
10205
|
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 })));
|
|
10116
10206
|
program.addCommand(apikeys, { hidden: true });
|
|
10117
|
-
var op = new import_commander.Command("op").description("Operator menu (platform operators only)").argument("[action]", "residue | sweep | credits (omit for the menu)").option("-y, --yes", "Skip the sweep confirmation prompt").option("--json", "Output machine-readable JSON").action(action((sub, opts) => runOp(sub, opts)));
|
|
10207
|
+
var op = new import_commander.Command("op").description("Operator menu (platform operators only)").argument("[action]", "residue | sweep | credits | latency (omit for the menu)").argument("[view]", "for `latency`: slow | llm (omit for the blame table)").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)));
|
|
10118
10208
|
program.addCommand(op, { hidden: true });
|
|
10119
10209
|
var spec = program.command("spec").description("View or update a proxy's OpenAPI spec (or build one by chatting: apiblaze agent openapi)");
|
|
10120
10210
|
spec.command("get").description("Print the current OpenAPI document").argument("<project>", "Project name or id").option("--team <id|name>", "Team the project is in").option("--apiversion <version>", "API version").option("--json", "Compact JSON output").action(action((project, opts) => runSpecGet(project, opts)));
|