@tokenzero/cli 0.1.4 → 0.1.6
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/bin/tokenzero.mjs +183 -23
- package/package.json +1 -1
package/bin/tokenzero.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
// cli/src/main.ts
|
|
5
5
|
import fs2 from "node:fs";
|
|
6
|
+
import os from "node:os";
|
|
6
7
|
import path2 from "node:path";
|
|
7
8
|
import crypto2 from "node:crypto";
|
|
8
9
|
import readline from "node:readline/promises";
|
|
@@ -69,7 +70,18 @@ var PRICES = {
|
|
|
69
70
|
"gemini-3-flash": P("gemini", "fast", 0.5, 3, 0.05),
|
|
70
71
|
"gemini-2.5-pro": P("gemini", "frontier", 1.25, 10, 0.125, void 0, { longCtxAt: 2e5, longCtx: { input: 2.5, output: 15, cachedInput: 0.25 } }),
|
|
71
72
|
"gemini-2.5-flash": P("gemini", "fast", 0.3, 2.5, 0.03),
|
|
72
|
-
"gemini-2.5-flash-lite": P("gemini", "fast", 0.1, 0.4, 0.01)
|
|
73
|
+
"gemini-2.5-flash-lite": P("gemini", "fast", 0.1, 0.4, 0.01),
|
|
74
|
+
// ---- xAI Grok ----
|
|
75
|
+
"grok-4": P("xai", "frontier", 3, 15, 0.75),
|
|
76
|
+
"grok-4-fast": P("xai", "fast", 0.2, 0.8, 0.05),
|
|
77
|
+
"grok-4-mini": P("xai", "fast", 0.2, 0.8, 0.05),
|
|
78
|
+
"grok-3": P("xai", "mid", 3, 15, 0.75),
|
|
79
|
+
"grok-3-mini": P("xai", "fast", 0.3, 0.5, 0.075),
|
|
80
|
+
// ---- DeepSeek ----
|
|
81
|
+
"deepseek-v3": P("deepseek", "fast", 0.27, 1.1, 0.07),
|
|
82
|
+
"deepseek-chat": P("deepseek", "fast", 0.27, 1.1, 0.07),
|
|
83
|
+
"deepseek-r1": P("deepseek", "mid", 0.55, 2.19, 0.14),
|
|
84
|
+
"deepseek-reasoner": P("deepseek", "mid", 0.55, 2.19, 0.14)
|
|
73
85
|
};
|
|
74
86
|
var CASCADE_TARGET = {
|
|
75
87
|
"gpt-6-astra": "gpt-6.1-sol",
|
|
@@ -100,13 +112,19 @@ var CASCADE_TARGET = {
|
|
|
100
112
|
"gemini-3.1-pro": "gemini-3.8-flash",
|
|
101
113
|
"gemini-3.8-flash": "gemini-3.5-flash-lite",
|
|
102
114
|
"gemini-2.5-pro": "gemini-2.5-flash",
|
|
103
|
-
"gemini-2.5-flash": "gemini-2.5-flash-lite"
|
|
115
|
+
"gemini-2.5-flash": "gemini-2.5-flash-lite",
|
|
116
|
+
"grok-4": "grok-4-fast",
|
|
117
|
+
"grok-3": "grok-3-mini",
|
|
118
|
+
"deepseek-r1": "deepseek-v3",
|
|
119
|
+
"deepseek-reasoner": "deepseek-chat"
|
|
104
120
|
};
|
|
105
121
|
var KEYS_BY_LENGTH = Object.keys(PRICES).sort((a, b) => b.length - a.length);
|
|
106
122
|
function normalizeModel(model) {
|
|
107
123
|
if (typeof model !== "string" || !model) return null;
|
|
108
124
|
let m = model.toLowerCase().trim();
|
|
109
|
-
m = m.replace(/^(openai|anthropic|google|models|vertex_ai)\//, "");
|
|
125
|
+
m = m.replace(/^(openai|anthropic|google|gemini|x-ai|xai|deepseek|bedrock|models|vertex_ai)\//, "");
|
|
126
|
+
m = m.replace(/^(?:us\.|eu\.|apac\.)?anthropic\./, "");
|
|
127
|
+
m = m.replace(/-v\d+:\d+$/, "");
|
|
110
128
|
m = m.replace(/@\d{8}$/, "");
|
|
111
129
|
m = m.replace(/-(\d{8}|\d{4}-\d{2}-\d{2})$/, "");
|
|
112
130
|
m = m.replace(/-(latest|preview|customtools|thinking)$/, "");
|
|
@@ -649,7 +667,7 @@ function canonical(obj) {
|
|
|
649
667
|
}
|
|
650
668
|
|
|
651
669
|
// cli/src/main.ts
|
|
652
|
-
var VERSION = "0.1.
|
|
670
|
+
var VERSION = "0.1.6";
|
|
653
671
|
var BAKED_URL = true ? "https://tokenzero.sh" : "__TOKENZERO_DEFAULT_URL__";
|
|
654
672
|
function parseArgs(argv) {
|
|
655
673
|
const a = { _: [], flags: {} };
|
|
@@ -856,41 +874,175 @@ ${bold("Your code")}`);
|
|
|
856
874
|
if (sn) out(` ${s.padEnd(10)} ${sn}`);
|
|
857
875
|
}
|
|
858
876
|
}
|
|
877
|
+
if (a.flags["github-action"]) await setupGithubAction(root, a);
|
|
859
878
|
if (a.flags.mcp) await setupMcp(root, base, a);
|
|
879
|
+
if (a.flags["agent-rules"]) await setupAgentRules(root, base, a);
|
|
880
|
+
if (key && wsId) saveCreds({ workspace_id: wsId, api_key: key, url: base });
|
|
860
881
|
out(`
|
|
861
882
|
${bold("Next")}`);
|
|
862
|
-
if (wsId) out(` dashboard ${base}/dashboard?workspace=${wsId}`);
|
|
883
|
+
if (wsId) out(` dashboard ${base}/dashboard?workspace=${wsId}${key ? "#key=" + redactKey(key, key) : ""}`);
|
|
863
884
|
out(` usage ${selfCmd(base)} usage`);
|
|
864
885
|
if (wsId) out(` badge turn on public stats in the dashboard, then: [](${base}/dashboard?workspace=${wsId})`);
|
|
886
|
+
if (!a.flags["github-action"]) out(` PR checks ${selfCmd(base)} init --github-action`);
|
|
865
887
|
if (!a.flags.mcp) out(` coding agents: ${selfCmd(base)} init --mcp`);
|
|
866
888
|
}
|
|
867
|
-
var selfCmd = (
|
|
889
|
+
var selfCmd = (_base) => "npx -y @tokenzero/cli";
|
|
868
890
|
var redactKey = (s, key) => key ? s.replace(key, key.slice(0, 12) + "\u2026" + key.slice(-4)) : s;
|
|
891
|
+
var GH_ACTION_YAML = `name: TokenZero LLM Cost Check
|
|
892
|
+
on: [pull_request]
|
|
893
|
+
permissions:
|
|
894
|
+
contents: read
|
|
895
|
+
pull-requests: write
|
|
896
|
+
jobs:
|
|
897
|
+
cost-audit:
|
|
898
|
+
runs-on: ubuntu-latest
|
|
899
|
+
steps:
|
|
900
|
+
- uses: actions/checkout@v4
|
|
901
|
+
with:
|
|
902
|
+
fetch-depth: 0
|
|
903
|
+
- uses: tokenzero-ai/tokenzero/action@v0
|
|
904
|
+
with:
|
|
905
|
+
github-token: \${{ secrets.GITHUB_TOKEN }}
|
|
906
|
+
`;
|
|
907
|
+
async function setupGithubAction(root, a) {
|
|
908
|
+
out(`
|
|
909
|
+
${bold("GitHub Action")} ${dim("(PR token-waste & model-arbitrage check)")}`);
|
|
910
|
+
const wfDir = path2.join(root, ".github", "workflows");
|
|
911
|
+
const file = path2.join(wfDir, "tokenzero.yml");
|
|
912
|
+
if (fs2.existsSync(file)) {
|
|
913
|
+
out(dim(` ${path2.relative(root, file)} already exists`));
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
if (await confirm(` Add ${path2.relative(root, file)} to audit every PR?`, a)) {
|
|
917
|
+
fs2.mkdirSync(wfDir, { recursive: true });
|
|
918
|
+
fs2.writeFileSync(file, GH_ACTION_YAML);
|
|
919
|
+
out(green(` \u2713 wrote ${path2.relative(root, file)}`));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
869
922
|
async function setupMcp(root, base, a) {
|
|
870
923
|
out(`
|
|
871
924
|
${bold("MCP")} ${dim("(audit_repo_token_waste, optimize_prompt_payload, calculate_model_arbitrage)")}`);
|
|
872
925
|
out(` Claude Code: claude mcp add --transport http tokenzero ${base}/mcp`);
|
|
873
|
-
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
926
|
+
out(` Local stdio: npx -y @tokenzero/mcp`);
|
|
927
|
+
for (const rel of [path2.join(".cursor", "mcp.json"), ".mcp.json"]) {
|
|
928
|
+
const file = path2.join(root, rel);
|
|
929
|
+
let cfg = {};
|
|
930
|
+
if (fs2.existsSync(file)) {
|
|
931
|
+
try {
|
|
932
|
+
cfg = JSON.parse(fs2.readFileSync(file, "utf8"));
|
|
933
|
+
} catch {
|
|
934
|
+
out(yellow(` ! ${file} is not valid JSON; not touching it`));
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
882
937
|
}
|
|
938
|
+
if (cfg?.mcpServers?.tokenzero?.url === `${base}/mcp`) {
|
|
939
|
+
out(dim(` ${rel}: already configured`));
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
if (await confirm(` Add TokenZero to ${rel}?`, a)) {
|
|
943
|
+
cfg.mcpServers = { ...cfg.mcpServers ?? {}, tokenzero: { url: `${base}/mcp` } };
|
|
944
|
+
fs2.mkdirSync(path2.dirname(file), { recursive: true });
|
|
945
|
+
fs2.writeFileSync(file, JSON.stringify(cfg, null, 2) + "\n");
|
|
946
|
+
out(green(` \u2713 wrote ${rel}`));
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
var AGENT_RULES_MARKER = "<!-- tokenzero-agent-rules -->";
|
|
951
|
+
function credsPath() {
|
|
952
|
+
return path2.join(process.env.TOKENZERO_CONFIG_DIR || path2.join(os.homedir(), ".config", "tokenzero"), "credentials.json");
|
|
953
|
+
}
|
|
954
|
+
function saveCreds(c2) {
|
|
955
|
+
try {
|
|
956
|
+
const p = credsPath();
|
|
957
|
+
fs2.mkdirSync(path2.dirname(p), { recursive: true, mode: 448 });
|
|
958
|
+
fs2.writeFileSync(p, JSON.stringify({ ...c2, updated_at: (/* @__PURE__ */ new Date()).toISOString() }, null, 2) + "\n", { mode: 384 });
|
|
959
|
+
} catch {
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
function loadCreds() {
|
|
963
|
+
try {
|
|
964
|
+
return JSON.parse(fs2.readFileSync(credsPath(), "utf8"));
|
|
965
|
+
} catch {
|
|
966
|
+
return null;
|
|
883
967
|
}
|
|
884
|
-
|
|
885
|
-
|
|
968
|
+
}
|
|
969
|
+
async function setupAgentRules(root, base, a) {
|
|
970
|
+
out(`
|
|
971
|
+
${bold("Agent Rules")} ${dim("(AGENTS.md FinOps & MCP routing rules)")}`);
|
|
972
|
+
const file = path2.join(root, "AGENTS.md");
|
|
973
|
+
const existing = fs2.existsSync(file) ? fs2.readFileSync(file, "utf8") : "";
|
|
974
|
+
if (existing.includes(AGENT_RULES_MARKER)) {
|
|
975
|
+
out(dim(" AGENTS.md: already includes TokenZero FinOps rules"));
|
|
886
976
|
return;
|
|
887
977
|
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
978
|
+
const block = [
|
|
979
|
+
AGENT_RULES_MARKER,
|
|
980
|
+
"## TokenZero LLM FinOps & MCP Gateway",
|
|
981
|
+
`- **1-Line BYOK Gateway:** Point \`OPENAI_BASE_URL=${base}/v1\` and \`ANTHROPIC_BASE_URL=${base}\` (with \`X-TokenZero-Key\` or \`/w/<key>/v1\`). Provider keys pass straight through and are never stored.`,
|
|
982
|
+
`- **MCP Tools:** Run \`npx -y @tokenzero/mcp\` or connect HTTP MCP at \`${base}/mcp\` for \`audit_repo_token_waste\`, \`calculate_model_arbitrage\`, \`optimize_prompt_payload\`, and \`provision_workspace\`.`,
|
|
983
|
+
`- **Local Audit:** Run \`npx -y @tokenzero/cli audit\` before opening PRs that add or modify LLM calls.`
|
|
984
|
+
].join("\n") + "\n";
|
|
985
|
+
if (await confirm(" Add TokenZero FinOps & MCP rules to AGENTS.md?", a)) {
|
|
986
|
+
const sep = existing && !existing.endsWith("\n") ? "\n\n" : existing ? "\n" : "";
|
|
987
|
+
fs2.writeFileSync(file, existing + sep + block);
|
|
988
|
+
out(green(" \u2713 wrote AGENTS.md"));
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
async function cmdSignup(a) {
|
|
992
|
+
const base = baseUrl(a);
|
|
993
|
+
if (!base) {
|
|
994
|
+
err(red("No gateway URL. Pass --url https://\u2026 or set TOKENZERO_URL."));
|
|
995
|
+
process.exitCode = 2;
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
const name = a.flags.name || path2.basename(process.cwd()) || "cli-signup";
|
|
999
|
+
const r = await api(base, "/v1/workspaces", { method: "POST", body: JSON.stringify({ name }) });
|
|
1000
|
+
if (r.status !== 201 && r.status !== 200) {
|
|
1001
|
+
err(red(`signup failed (${r.status}): ${JSON.stringify(r.body).slice(0, 200)}`));
|
|
1002
|
+
process.exitCode = 1;
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
const wsId = r.body.workspace?.id ?? r.body.workspace_id ?? r.body.id;
|
|
1006
|
+
const key = r.body.key ?? r.body.api_key;
|
|
1007
|
+
saveCreds({ workspace_id: wsId, api_key: key, url: base });
|
|
1008
|
+
const payload = {
|
|
1009
|
+
workspace_id: wsId,
|
|
1010
|
+
api_key: key,
|
|
1011
|
+
dashboard_url: `${base}/dashboard?workspace=${wsId}`,
|
|
1012
|
+
one_click_dashboard_url: `${base}/dashboard?workspace=${wsId}#key=${key}`,
|
|
1013
|
+
openai_base_url: `${base}/w/${key}/v1`,
|
|
1014
|
+
anthropic_base_url: `${base}/w/${key}`,
|
|
1015
|
+
mcp_url: `${base}/mcp`,
|
|
1016
|
+
badge_svg_url: `${base}/v1/badge/${wsId}.svg`
|
|
1017
|
+
};
|
|
1018
|
+
if (a.flags.json) {
|
|
1019
|
+
out(JSON.stringify(payload, null, 2));
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
out(`${bold("TokenZero Workspace Provisioned (0-Click / No Form)")}`);
|
|
1023
|
+
out(` workspace_id ${cyan(wsId)}`);
|
|
1024
|
+
out(` api_key ${green(key)}`);
|
|
1025
|
+
out(` 1-click dash ${cyan(payload.one_click_dashboard_url)}`);
|
|
1026
|
+
out(` OPENAI_BASE_URL ${payload.openai_base_url}`);
|
|
1027
|
+
out(` ANTHROPIC_BASE_URL ${payload.anthropic_base_url}`);
|
|
1028
|
+
out(dim(` Saved credentials to ${credsPath()}`));
|
|
1029
|
+
}
|
|
1030
|
+
async function cmdDashboard(a) {
|
|
1031
|
+
const saved = loadCreds();
|
|
1032
|
+
const base = baseUrl(a) || saved?.url || "https://tokenzero.sh";
|
|
1033
|
+
const key = a.flags.key || process.env.TOKENZERO_KEY || saved?.api_key || "";
|
|
1034
|
+
const wsId = saved?.workspace_id || "";
|
|
1035
|
+
if (!key && !wsId) {
|
|
1036
|
+
err(red("No saved workspace credentials. Run `npx -y @tokenzero/cli signup` or `tokenzero init` first."));
|
|
1037
|
+
process.exitCode = 2;
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
const url = `${base}/dashboard${wsId ? "?workspace=" + wsId : ""}${key ? "#key=" + key : ""}`;
|
|
1041
|
+
if (a.flags.json) {
|
|
1042
|
+
out(JSON.stringify({ workspace_id: wsId || null, dashboard_url: url }, null, 2));
|
|
1043
|
+
return;
|
|
893
1044
|
}
|
|
1045
|
+
out(url);
|
|
894
1046
|
}
|
|
895
1047
|
async function cmdAudit(a) {
|
|
896
1048
|
const root = path2.resolve(a._[1] ?? ".");
|
|
@@ -1007,7 +1159,11 @@ async function cmdVerify(a) {
|
|
|
1007
1159
|
function help() {
|
|
1008
1160
|
out(`${bold("tokenzero")} v${VERSION} \u2014 cut your LLM bill with a BYOK gateway
|
|
1009
1161
|
|
|
1010
|
-
${bold("tokenzero
|
|
1162
|
+
${bold("tokenzero signup")} [--name NAME] [--json]
|
|
1163
|
+
Instant 1-command workspace & API key provisioning (no form, saves to ~/.config/tokenzero).
|
|
1164
|
+
${bold("tokenzero dashboard")} [--json]
|
|
1165
|
+
Print your 1-click auto-unlock dashboard URL (#key= fragment, never sent in HTTP headers).
|
|
1166
|
+
${bold("tokenzero init")} [dir] [--url URL] [--name NAME] [--yes] [--no-env] [--mcp] [--github-action] [--agent-rules]
|
|
1011
1167
|
Audit locally, create a workspace, point OPENAI_BASE_URL / ANTHROPIC_BASE_URL at the gateway.
|
|
1012
1168
|
${bold("tokenzero audit")} [dir] [--json | --format markdown] [--diff file|-] [--fail-over PCT]
|
|
1013
1169
|
Static token-waste audit. Runs locally; nothing is uploaded. --fail-over makes it a CI gate.
|
|
@@ -1026,6 +1182,10 @@ async function main() {
|
|
|
1026
1182
|
const cmd = a._[0];
|
|
1027
1183
|
if (a.flags.version || cmd === "version") return out(VERSION);
|
|
1028
1184
|
switch (cmd) {
|
|
1185
|
+
case "signup":
|
|
1186
|
+
return cmdSignup(a);
|
|
1187
|
+
case "dashboard":
|
|
1188
|
+
return cmdDashboard(a);
|
|
1029
1189
|
case "init":
|
|
1030
1190
|
return cmdInit(a);
|
|
1031
1191
|
case "audit":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenzero/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "TokenZero CLI: connect a project to the TokenZero BYOK gateway, audit LLM token waste locally, check usage and verify signed savings receipts.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|