@curatedmcp/tokenshield 0.2.0

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.
Files changed (55) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.js +238 -0
  3. package/dist/cli.js.map +1 -0
  4. package/dist/commands/bench.d.ts +5 -0
  5. package/dist/commands/bench.js +191 -0
  6. package/dist/commands/bench.js.map +1 -0
  7. package/dist/commands/demo.d.ts +3 -0
  8. package/dist/commands/demo.js +99 -0
  9. package/dist/commands/demo.js.map +1 -0
  10. package/dist/commands/doctor.d.ts +1 -0
  11. package/dist/commands/doctor.js +119 -0
  12. package/dist/commands/doctor.js.map +1 -0
  13. package/dist/commands/estimate.d.ts +3 -0
  14. package/dist/commands/estimate.js +90 -0
  15. package/dist/commands/estimate.js.map +1 -0
  16. package/dist/commands/integrations.d.ts +14 -0
  17. package/dist/commands/integrations.js +138 -0
  18. package/dist/commands/integrations.js.map +1 -0
  19. package/dist/commands/logs.d.ts +6 -0
  20. package/dist/commands/logs.js +66 -0
  21. package/dist/commands/logs.js.map +1 -0
  22. package/dist/commands/setup.d.ts +9 -0
  23. package/dist/commands/setup.js +159 -0
  24. package/dist/commands/setup.js.map +1 -0
  25. package/dist/commands/status.d.ts +1 -0
  26. package/dist/commands/status.js +102 -0
  27. package/dist/commands/status.js.map +1 -0
  28. package/dist/commands/stop.d.ts +1 -0
  29. package/dist/commands/stop.js +27 -0
  30. package/dist/commands/stop.js.map +1 -0
  31. package/dist/commands/up.d.ts +15 -0
  32. package/dist/commands/up.js +145 -0
  33. package/dist/commands/up.js.map +1 -0
  34. package/dist/dashboard.d.ts +4 -0
  35. package/dist/dashboard.js +186 -0
  36. package/dist/dashboard.js.map +1 -0
  37. package/dist/lib/daemon.d.ts +23 -0
  38. package/dist/lib/daemon.js +164 -0
  39. package/dist/lib/daemon.js.map +1 -0
  40. package/dist/lib/errors.d.ts +23 -0
  41. package/dist/lib/errors.js +131 -0
  42. package/dist/lib/errors.js.map +1 -0
  43. package/dist/lib/integrations.d.ts +47 -0
  44. package/dist/lib/integrations.js +210 -0
  45. package/dist/lib/integrations.js.map +1 -0
  46. package/dist/lib/paths.d.ts +5 -0
  47. package/dist/lib/paths.js +18 -0
  48. package/dist/lib/paths.js.map +1 -0
  49. package/dist/lib/preflight.d.ts +20 -0
  50. package/dist/lib/preflight.js +97 -0
  51. package/dist/lib/preflight.js.map +1 -0
  52. package/dist/lib/ui.d.ts +68 -0
  53. package/dist/lib/ui.js +221 -0
  54. package/dist/lib/ui.js.map +1 -0
  55. package/package.json +44 -0
@@ -0,0 +1,119 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { defaultConfig } from "@curatedmcp/tokenshield-core";
3
+ import { classifyApiKey, probeUpstream, checkPort } from "../lib/preflight.js";
4
+ import { readDaemon } from "../lib/daemon.js";
5
+ import { c, sym, dim, emit, emitJson, isJson, say, heading } from "../lib/ui.js";
6
+ function row(c0) {
7
+ const symbol = c0.status === "ok" ? sym.check : c0.status === "warn" ? sym.warn : sym.cross;
8
+ const name = c0.name.padEnd(24);
9
+ return ` ${symbol} ${name} ${c0.detail}`;
10
+ }
11
+ export async function runDoctor() {
12
+ const cfg = defaultConfig();
13
+ const checks = [];
14
+ // Node version
15
+ const major = Number(process.versions.node.split(".")[0]);
16
+ checks.push(major >= 22
17
+ ? { name: "Node version", status: "ok", detail: `${process.version} (>= 22)` }
18
+ : { name: "Node version", status: "fail", detail: `${process.version} — requires Node 22+`, hint: "Upgrade: https://nodejs.org/" });
19
+ // API key
20
+ const ks = classifyApiKey(process.env["ANTHROPIC_API_KEY"]);
21
+ if (ks.state === "ok") {
22
+ checks.push({ name: "ANTHROPIC_API_KEY", status: "ok", detail: `set, ends in …${(process.env["ANTHROPIC_API_KEY"] ?? "").slice(-4)}` });
23
+ }
24
+ else if (ks.state === "wrong_prefix") {
25
+ checks.push({ name: "ANTHROPIC_API_KEY", status: "warn", detail: "set but wrong prefix", hint: ks.hint });
26
+ }
27
+ else {
28
+ checks.push({ name: "ANTHROPIC_API_KEY", status: "warn", detail: "not set in this shell", hint: ks.hint });
29
+ }
30
+ // Base URL
31
+ const baseUrl = process.env["ANTHROPIC_BASE_URL"];
32
+ if (baseUrl) {
33
+ const local = baseUrl.includes("127.0.0.1") || baseUrl.includes("localhost");
34
+ checks.push(local
35
+ ? { name: "ANTHROPIC_BASE_URL", status: "ok", detail: `points at local proxy: ${baseUrl}` }
36
+ : { name: "ANTHROPIC_BASE_URL", status: "warn", detail: `set to ${baseUrl} (not local)` });
37
+ }
38
+ else {
39
+ checks.push({
40
+ name: "ANTHROPIC_BASE_URL",
41
+ status: "warn",
42
+ detail: "not set in this shell",
43
+ hint: `export ANTHROPIC_BASE_URL=http://${cfg.bind}:${cfg.port}`,
44
+ });
45
+ }
46
+ // Daemon
47
+ const daemon = readDaemon();
48
+ if (daemon !== null) {
49
+ checks.push({ name: "Daemon", status: "ok", detail: `running, pid ${daemon.pid}, port ${daemon.port}` });
50
+ }
51
+ else {
52
+ checks.push({ name: "Daemon", status: "warn", detail: "not running (foreground or unstarted)" });
53
+ }
54
+ // Ports
55
+ const proxyPort = await checkPort(cfg.port);
56
+ const dashPort = await checkPort(cfg.dashboardPort);
57
+ // If daemon is running, ports being "in use" is the EXPECTED state.
58
+ const expectInUse = daemon !== null;
59
+ if (expectInUse) {
60
+ checks.push({
61
+ name: `Port ${cfg.port}`,
62
+ status: !proxyPort.available ? "ok" : "warn",
63
+ detail: !proxyPort.available ? "in use by TokenShield (expected)" : "free but daemon claims to be running",
64
+ });
65
+ checks.push({
66
+ name: `Port ${cfg.dashboardPort}`,
67
+ status: !dashPort.available ? "ok" : "warn",
68
+ detail: !dashPort.available ? "in use by TokenShield (expected)" : "free but daemon claims to be running",
69
+ });
70
+ }
71
+ else {
72
+ checks.push({
73
+ name: `Port ${cfg.port}`,
74
+ status: proxyPort.available ? "ok" : "fail",
75
+ detail: proxyPort.available ? "free" : `in use (${proxyPort.detail ?? "unknown"})`,
76
+ hint: proxyPort.available ? undefined : "tokenshield up --port <other>",
77
+ });
78
+ checks.push({
79
+ name: `Port ${cfg.dashboardPort}`,
80
+ status: dashPort.available ? "ok" : "fail",
81
+ detail: dashPort.available ? "free" : `in use (${dashPort.detail ?? "unknown"})`,
82
+ });
83
+ }
84
+ // Ledger
85
+ if (existsSync(cfg.ledgerPath)) {
86
+ const s = statSync(cfg.ledgerPath);
87
+ checks.push({ name: "Ledger file", status: "ok", detail: `${cfg.ledgerPath} (${s.size} bytes)` });
88
+ }
89
+ else {
90
+ checks.push({ name: "Ledger file", status: "ok", detail: `will be created at ${cfg.ledgerPath}` });
91
+ }
92
+ // Upstream
93
+ const probe = await probeUpstream(cfg.upstreamBaseUrl);
94
+ checks.push(probe.reachable
95
+ ? { name: "Anthropic reachable", status: "ok", detail: `${cfg.upstreamBaseUrl} (${probe.latencyMs}ms, status ${probe.status})` }
96
+ : { name: "Anthropic reachable", status: "fail", detail: `${cfg.upstreamBaseUrl}: ${probe.detail ?? "unknown"}` });
97
+ if (isJson()) {
98
+ emitJson({ ok: checks.every((c) => c.status !== "fail"), checks });
99
+ process.exit(checks.some((c) => c.status === "fail") ? 1 : 0);
100
+ }
101
+ heading("TokenShield doctor");
102
+ emit("");
103
+ for (const ch of checks) {
104
+ emit(row(ch));
105
+ if (ch.hint)
106
+ emit(` ${dim(ch.hint)}`);
107
+ }
108
+ emit("");
109
+ const anyFail = checks.some((ch) => ch.status === "fail");
110
+ if (anyFail) {
111
+ emit(`${sym.cross} ${c.bold("Some checks failed.")} Fix the items above, then re-run ${c.cyan("tokenshield doctor")}.`);
112
+ }
113
+ else {
114
+ emit(`${sym.check} ${c.bold("TokenShield looks healthy.")}`);
115
+ }
116
+ say("");
117
+ process.exit(anyFail ? 1 : 0);
118
+ }
119
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AASjF,SAAS,GAAG,CAAC,EAAS;IACpB,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;IAC5F,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChC,OAAO,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAY,EAAE,CAAC;IAE3B,eAAe;IACf,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,CAAC,IAAI,CACT,KAAK,IAAI,EAAE;QACT,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,UAAU,EAAE;QAC9E,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,sBAAsB,EAAE,IAAI,EAAE,8BAA8B,EAAE,CACrI,CAAC;IAEF,UAAU;IACV,MAAM,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC5D,IAAI,EAAE,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QACtB,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1I,CAAC;SAAM,IAAI,EAAE,CAAC,KAAK,KAAK,cAAc,EAAE,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5G,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,uBAAuB,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7G,CAAC;IAED,WAAW;IACX,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAClD,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC7E,MAAM,CAAC,IAAI,CACT,KAAK;YACH,CAAC,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,0BAA0B,OAAO,EAAE,EAAE;YAC3F,CAAC,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,OAAO,cAAc,EAAE,CAC5F,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,oBAAoB;YAC1B,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,uBAAuB;YAC/B,IAAI,EAAE,oCAAoC,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE;SACjE,CAAC,CAAC;IACL,CAAC;IAED,SAAS;IACT,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,MAAM,CAAC,GAAG,UAAU,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAC3G,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,uCAAuC,EAAE,CAAC,CAAC;IACnG,CAAC;IAED,QAAQ;IACR,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACpD,oEAAoE;IACpE,MAAM,WAAW,GAAG,MAAM,KAAK,IAAI,CAAC;IACpC,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,QAAQ,GAAG,CAAC,IAAI,EAAE;YACxB,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAC5C,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,sCAAsC;SAC3G,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,QAAQ,GAAG,CAAC,aAAa,EAAE;YACjC,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAC3C,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC,sCAAsC;SAC1G,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,QAAQ,GAAG,CAAC,IAAI,EAAE;YACxB,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAC3C,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,SAAS,CAAC,MAAM,IAAI,SAAS,GAAG;YAClF,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,+BAA+B;SACxE,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,QAAQ,GAAG,CAAC,aAAa,EAAE;YACjC,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAC1C,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,QAAQ,CAAC,MAAM,IAAI,SAAS,GAAG;SACjF,CAAC,CAAC;IACL,CAAC;IAED,SAAS;IACT,IAAI,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,UAAU,KAAK,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC;IACpG,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,sBAAsB,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACrG,CAAC;IAED,WAAW;IACX,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACvD,MAAM,CAAC,IAAI,CACT,KAAK,CAAC,SAAS;QACb,CAAC,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,eAAe,KAAK,KAAK,CAAC,SAAS,cAAc,KAAK,CAAC,MAAM,GAAG,EAAE;QAChI,CAAC,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,eAAe,KAAK,KAAK,CAAC,MAAM,IAAI,SAAS,EAAE,EAAE,CACpH,CAAC;IAEF,IAAI,MAAM,EAAE,EAAE,CAAC;QACb,QAAQ,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACnE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC9B,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACd,IAAI,EAAE,CAAC,IAAI;YAAE,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IAC1D,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,qCAAqC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC1H,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,GAAG,CAAC,EAAE,CAAC,CAAC;IACR,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function runEstimate(opts: {
2
+ hours: number;
3
+ }): Promise<void>;
@@ -0,0 +1,90 @@
1
+ import { Ledger, defaultConfig } from "@curatedmcp/tokenshield-core";
2
+ import { c, sym, dim, emit, emitJson, isJson, heading } from "../lib/ui.js";
3
+ function dollars(n) {
4
+ return "$" + n.toFixed(n < 1 ? 4 : 2);
5
+ }
6
+ function fmt(n) {
7
+ return n.toLocaleString();
8
+ }
9
+ export async function runEstimate(opts) {
10
+ const hours = opts.hours;
11
+ const cfg = defaultConfig();
12
+ const ledger = new Ledger(cfg.ledgerPath);
13
+ try {
14
+ const since = Date.now() - hours * 60 * 60 * 1000;
15
+ const summary = ledger.summary(since);
16
+ if (summary.requestCount === 0) {
17
+ if (isJson()) {
18
+ emitJson({ ok: true, requestCount: 0, message: "no traffic in window" });
19
+ return;
20
+ }
21
+ emit("");
22
+ emit(`${sym.info} No traffic recorded in the last ${hours}h.`);
23
+ emit(dim(" Run Claude Code through the proxy first:"));
24
+ emit(` ${c.cyan("tokenshield up")}`);
25
+ emit(` ${c.cyan(`export ANTHROPIC_BASE_URL=http://${cfg.bind}:${cfg.port}`)}`);
26
+ emit("");
27
+ return;
28
+ }
29
+ const windowMs = Math.max(1, summary.windowEnd - summary.windowStart);
30
+ const weekly = (summary.dollarsRaw / windowMs) * 7 * 24 * 60 * 60 * 1000;
31
+ const monthly = (summary.dollarsRaw / windowMs) * 30 * 24 * 60 * 60 * 1000;
32
+ const targets = [
33
+ { name: "Conversation dedup", pct: 0.30 },
34
+ { name: "Result cache", pct: 0.07 },
35
+ { name: "Diff-based file reads", pct: 0.12 },
36
+ { name: "Streaming early-stop", pct: 0.18 },
37
+ { name: "Context auto-summarize", pct: 0.20 },
38
+ ];
39
+ let surviving = 1;
40
+ const breakdown = [];
41
+ for (const t of targets) {
42
+ const saves = surviving * t.pct;
43
+ breakdown.push({ name: t.name, saves });
44
+ surviving -= saves;
45
+ }
46
+ const totalSavingsPct = 1 - surviving;
47
+ const projectedMonthlySavings = monthly * totalSavingsPct;
48
+ if (isJson()) {
49
+ emitJson({
50
+ ok: true,
51
+ windowHours: hours,
52
+ requests: summary.requestCount,
53
+ measuredDollars: summary.dollarsRaw,
54
+ projectedWeeklyDollars: weekly,
55
+ projectedMonthlyDollars: monthly,
56
+ projectedMonthlySavingsDollars: projectedMonthlySavings,
57
+ projectedSavingsPercent: totalSavingsPct,
58
+ byModel: summary.byModel,
59
+ });
60
+ return;
61
+ }
62
+ heading(`Estimate (last ${hours}h)`);
63
+ emit("");
64
+ emit(` ${sym.bullet} Requests: ${fmt(summary.requestCount)}`);
65
+ emit(` ${sym.bullet} Measured spend: ${dollars(summary.dollarsRaw)}`);
66
+ emit(` ${sym.bullet} Input tokens: ${fmt(summary.totalInputTokensRaw)}`);
67
+ emit(` ${sym.bullet} Output tokens: ${fmt(summary.totalOutputTokensRaw)}`);
68
+ emit("");
69
+ emit(` ${sym.bullet} Projected weekly: ${dollars(weekly)}`);
70
+ emit(` ${sym.bullet} Projected monthly: ${dollars(monthly)}`);
71
+ emit("");
72
+ emit(c.bold(" Forward-looking savings at TokenShield v1.0:"));
73
+ for (const b of breakdown) {
74
+ emit(` ${sym.bullet} ${b.name.padEnd(30)} ${c.green("saves " + dollars(monthly * b.saves) + "/mo")}`);
75
+ }
76
+ emit("");
77
+ emit(` ${c.bold("Total projected savings:")} ${c.green(dollars(projectedMonthlySavings) + "/mo")} ${dim(`(${(totalSavingsPct * 100).toFixed(1)}%)`)}`);
78
+ emit(` ${dim("TokenShield Individual at $19/mo:")} ROI = ${c.green((projectedMonthlySavings / 19).toFixed(1) + "×")}`);
79
+ emit("");
80
+ emit(c.bold(" Spend by model:"));
81
+ for (const m of summary.byModel) {
82
+ emit(` ${m.model.padEnd(28)} ${String(m.requests).padStart(6)} req ${dollars(m.dollars).padStart(8)}`);
83
+ }
84
+ emit("");
85
+ }
86
+ finally {
87
+ ledger.close();
88
+ }
89
+ }
90
+ //# sourceMappingURL=estimate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate.js","sourceRoot":"","sources":["../../src/commands/estimate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type IntegrationId } from "../lib/integrations.js";
2
+ export interface IntegrationsListOptions {
3
+ baseUrl: string;
4
+ }
5
+ export declare function runIntegrationsList(opts: IntegrationsListOptions): void;
6
+ export interface IntegrationsEnableOptions {
7
+ id: IntegrationId;
8
+ baseUrl: string;
9
+ }
10
+ export declare function runIntegrationsEnable(opts: IntegrationsEnableOptions): void;
11
+ export declare function runIntegrationsShow(opts: {
12
+ baseUrl: string;
13
+ }): void;
14
+ export declare function runIntegrationsDisable(target: "shell" | IntegrationId): void;
@@ -0,0 +1,138 @@
1
+ import { detectAll, manualSnippet, writeShellRc, removeShellRc, } from "../lib/integrations.js";
2
+ import { c, sym, dim, emit, emitJson, isJson, table, heading, box } from "../lib/ui.js";
3
+ import { TokenShieldError } from "../lib/errors.js";
4
+ const KNOWN_IDS = ["claude-code", "cursor", "windsurf", "zed", "aider"];
5
+ function statusLabel(s) {
6
+ if (s === "detected")
7
+ return c.green("detected");
8
+ if (s === "not-found")
9
+ return c.gray("not found");
10
+ return c.yellow("unknown");
11
+ }
12
+ export function runIntegrationsList(opts) {
13
+ const all = detectAll();
14
+ if (isJson()) {
15
+ emitJson({ ok: true, baseUrl: opts.baseUrl, integrations: all });
16
+ return;
17
+ }
18
+ heading("AI tools we can route through TokenShield");
19
+ emit("");
20
+ emit(table(all.map((i) => [
21
+ " " + c.bold(i.name),
22
+ statusLabel(i.status),
23
+ i.configureMethod === "shell-rc" ? c.cyan("env via shell rc") : c.cyan("paste snippet"),
24
+ dim(i.detail),
25
+ ]), { header: [" Tool", "Status", "Setup", "Detail"] }));
26
+ emit("");
27
+ emit(dim(` Configure one: tokenshield integrations enable <tool>`));
28
+ emit(dim(` Show all manual: tokenshield integrations show`));
29
+ emit(dim(` Remove env: tokenshield integrations disable shell`));
30
+ emit("");
31
+ }
32
+ export function runIntegrationsEnable(opts) {
33
+ if (!KNOWN_IDS.includes(opts.id)) {
34
+ throw new TokenShieldError({
35
+ code: "INVALID_ARGUMENT",
36
+ message: `Unknown integration: ${opts.id}`,
37
+ hint: `Pick one of: ${KNOWN_IDS.join(", ")}`,
38
+ nextSteps: ["tokenshield integrations list"],
39
+ });
40
+ }
41
+ const all = detectAll();
42
+ const target = all.find((i) => i.id === opts.id);
43
+ if (target === undefined) {
44
+ throw new TokenShieldError({ code: "INTERNAL", message: "integration definition not found" });
45
+ }
46
+ if (target.configureMethod === "shell-rc") {
47
+ let result;
48
+ try {
49
+ result = writeShellRc({ baseUrl: opts.baseUrl });
50
+ }
51
+ catch (err) {
52
+ throw new TokenShieldError({
53
+ code: "PERMISSION_DENIED",
54
+ message: `Failed to write to your shell rc: ${err.message}`,
55
+ nextSteps: [`echo 'export ANTHROPIC_BASE_URL=${opts.baseUrl}' >> ~/.zshrc`],
56
+ });
57
+ }
58
+ if (isJson()) {
59
+ emitJson({ ok: true, integration: target.id, result });
60
+ return;
61
+ }
62
+ emit("");
63
+ emit(`${sym.check} ${c.bold(target.name)} configured.`);
64
+ emit("");
65
+ emit(dim(` Wrote managed block to: ${result.rcPath}`));
66
+ emit(dim(` Shell detected: ${result.shell}`));
67
+ emit(dim(` Action: ${result.action}`));
68
+ emit("");
69
+ emit(`${sym.warn} Reload your shell so the variable is picked up:`);
70
+ emit(` ${c.cyan(`source ${result.rcPath}`)}`);
71
+ emit(dim(" …or just open a new terminal."));
72
+ emit("");
73
+ return;
74
+ }
75
+ // manual-snippet path
76
+ const snippet = manualSnippet(opts.id, opts.baseUrl);
77
+ if (isJson()) {
78
+ emitJson({ ok: true, integration: target.id, method: "manual-snippet", snippet });
79
+ return;
80
+ }
81
+ emit("");
82
+ emit(`${sym.info} ${c.bold(target.name)} — manual config required`);
83
+ emit("");
84
+ emit(dim(" " + target.instructions));
85
+ emit("");
86
+ emit(box(snippet, { title: `${target.name} config`, color: c.gray, padding: 1 }));
87
+ emit("");
88
+ }
89
+ export function runIntegrationsShow(opts) {
90
+ const all = detectAll();
91
+ if (isJson()) {
92
+ emitJson({
93
+ ok: true,
94
+ baseUrl: opts.baseUrl,
95
+ snippets: all.map((i) => ({
96
+ id: i.id,
97
+ method: i.configureMethod,
98
+ instructions: i.instructions,
99
+ snippet: i.configureMethod === "shell-rc"
100
+ ? `export ANTHROPIC_BASE_URL=${opts.baseUrl}`
101
+ : manualSnippet(i.id, opts.baseUrl),
102
+ })),
103
+ });
104
+ return;
105
+ }
106
+ for (const i of all) {
107
+ emit("");
108
+ emit(c.bold(`■ ${i.name}`) + " " + statusLabel(i.status));
109
+ emit(dim(" " + i.instructions));
110
+ emit("");
111
+ const snippet = i.configureMethod === "shell-rc"
112
+ ? `export ANTHROPIC_BASE_URL=${opts.baseUrl}`
113
+ : manualSnippet(i.id, opts.baseUrl);
114
+ emit(box(snippet, { color: c.gray, padding: 1 }));
115
+ }
116
+ emit("");
117
+ }
118
+ export function runIntegrationsDisable(target) {
119
+ if (target !== "shell") {
120
+ throw new TokenShieldError({
121
+ code: "INVALID_ARGUMENT",
122
+ message: `Only 'shell' can be disabled automatically (manual-snippet tools can't be auto-reverted).`,
123
+ hint: "For Cursor / Windsurf, remove the base URL in their settings UI.",
124
+ });
125
+ }
126
+ const result = removeShellRc();
127
+ if (isJson()) {
128
+ emitJson({ ok: true, result });
129
+ return;
130
+ }
131
+ if (!result.removed) {
132
+ emit(`${sym.info} No managed TokenShield block found in ${result.rcPath ?? "your shell rc"}.`);
133
+ return;
134
+ }
135
+ emit(`${sym.check} Removed managed block from ${dim(result.rcPath ?? "")}.`);
136
+ emit(dim(" Reload your shell or open a new terminal."));
137
+ }
138
+ //# sourceMappingURL=integrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../src/commands/integrations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface LogsOptions {
2
+ limit: number;
3
+ modelFilter?: string;
4
+ errorsOnly: boolean;
5
+ }
6
+ export declare function runLogs(opts: LogsOptions): Promise<void>;
@@ -0,0 +1,66 @@
1
+ import { Ledger, defaultConfig } from "@curatedmcp/tokenshield-core";
2
+ import { c, sym, dim, emit, emitJson, isJson, table } from "../lib/ui.js";
3
+ function fmtTime(ms) {
4
+ return new Date(ms).toLocaleTimeString();
5
+ }
6
+ function fmtMs(ms) {
7
+ return ms < 1000 ? `${ms}ms` : `${(ms / 1000).toFixed(1)}s`;
8
+ }
9
+ function fmtDollars(n) {
10
+ if (n === 0)
11
+ return "$0.00";
12
+ return "$" + n.toFixed(n < 0.01 ? 5 : n < 1 ? 4 : 2);
13
+ }
14
+ function fmtN(n) {
15
+ return n.toLocaleString();
16
+ }
17
+ export async function runLogs(opts) {
18
+ const cfg = defaultConfig();
19
+ const ledger = new Ledger(cfg.ledgerPath);
20
+ try {
21
+ const recent = ledger.recent(Math.min(Math.max(opts.limit, 1), 500));
22
+ let filtered = recent;
23
+ if (opts.modelFilter) {
24
+ const needle = opts.modelFilter.toLowerCase();
25
+ filtered = filtered.filter((r) => r.model.toLowerCase().includes(needle));
26
+ }
27
+ if (opts.errorsOnly) {
28
+ filtered = filtered.filter((r) => r.upstreamStatus < 200 || r.upstreamStatus >= 300 || r.upstreamError !== null);
29
+ }
30
+ if (isJson()) {
31
+ emitJson({ ok: true, count: filtered.length, records: filtered });
32
+ return;
33
+ }
34
+ if (filtered.length === 0) {
35
+ emit(dim(" No requests match. Run Claude Code through the proxy to populate the ledger."));
36
+ return;
37
+ }
38
+ emit("");
39
+ emit(table(filtered.map((r) => {
40
+ const okStatus = r.upstreamStatus >= 200 && r.upstreamStatus < 300;
41
+ const statusStr = r.upstreamError
42
+ ? c.red("ERR")
43
+ : okStatus
44
+ ? c.green(String(r.upstreamStatus))
45
+ : c.yellow(String(r.upstreamStatus));
46
+ return [
47
+ dim(fmtTime(r.timestamp)),
48
+ r.model,
49
+ dim(r.endpoint),
50
+ fmtN(r.usageRaw.inputTokens),
51
+ fmtN(r.usageRaw.outputTokens),
52
+ dim(fmtMs(r.durationMs)),
53
+ fmtDollars(r.dollarsRaw),
54
+ statusStr,
55
+ ];
56
+ }), { header: ["Time", "Model", "Endpoint", "Input", "Output", "Dur", "$", "Status"] }));
57
+ emit("");
58
+ emit(dim(` Showing ${filtered.length} of ${recent.length} recent requests.`));
59
+ // suppress unused warning
60
+ void sym;
61
+ }
62
+ finally {
63
+ ledger.close();
64
+ }
65
+ }
66
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface SetupOptions {
2
+ port: number;
3
+ dashboardPort: number;
4
+ bind: string;
5
+ upstream: string;
6
+ retentionDays: number;
7
+ yes: boolean;
8
+ }
9
+ export declare function runSetup(opts: SetupOptions): Promise<void>;
@@ -0,0 +1,159 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stdin as input, stdout as output } from "node:process";
3
+ import { detectAll, writeShellRc, manualSnippet } from "../lib/integrations.js";
4
+ import { spawnDaemon, readDaemon } from "../lib/daemon.js";
5
+ import { requirePortFree, classifyApiKey, probeUpstream } from "../lib/preflight.js";
6
+ import { defaultConfig } from "@curatedmcp/tokenshield-core";
7
+ import { c, sym, dim, emit, say, heading, spinner, box } from "../lib/ui.js";
8
+ async function ask(rl, prompt, def) {
9
+ const hint = def === "y" ? "(Y/n)" : "(y/N)";
10
+ const ans = (await rl.question(`${prompt} ${dim(hint)} `)).trim().toLowerCase();
11
+ if (ans === "")
12
+ return def === "y";
13
+ return ans === "y" || ans === "yes";
14
+ }
15
+ function recommendedTool(all) {
16
+ // Prefer Claude Code if it's installed; otherwise the first detected tool
17
+ const cc = all.find((i) => i.id === "claude-code" && i.status === "detected");
18
+ if (cc)
19
+ return cc;
20
+ return all.find((i) => i.status === "detected") ?? null;
21
+ }
22
+ export async function runSetup(opts) {
23
+ const rl = createInterface({ input, output });
24
+ const baseUrl = `http://${opts.bind}:${opts.port}`;
25
+ try {
26
+ say("");
27
+ say(c.bold("TokenShield setup") + dim(" — about 60 seconds, no signup, no telemetry"));
28
+ say("");
29
+ // Step 1: detect tools
30
+ heading("1) Looking for AI tools to route through TokenShield");
31
+ const all = detectAll();
32
+ for (const i of all) {
33
+ const status = i.status === "detected" ? c.green("found") : c.gray("not found");
34
+ emit(` ${i.status === "detected" ? sym.check : sym.bullet} ${i.name.padEnd(14)} ${status} ${dim(i.detail)}`);
35
+ }
36
+ const target = recommendedTool(all);
37
+ say("");
38
+ if (target === null) {
39
+ emit(`${sym.warn} We didn't find a known AI client on this machine.`);
40
+ emit(dim(" Setup will continue, but you'll need to configure your tool manually."));
41
+ }
42
+ else {
43
+ emit(`${sym.check} Recommended target: ${c.bold(target.name)}`);
44
+ }
45
+ // Step 2: preflight
46
+ say("");
47
+ heading("2) Checking the network");
48
+ const portSpin = spinner(`Checking ports ${opts.port} and ${opts.dashboardPort}…`);
49
+ try {
50
+ await requirePortFree(opts.port, "proxy");
51
+ await requirePortFree(opts.dashboardPort, "dashboard");
52
+ portSpin.succeed(`Ports ${opts.port} and ${opts.dashboardPort} are free.`);
53
+ }
54
+ catch (err) {
55
+ portSpin.fail("Port conflict.");
56
+ throw err;
57
+ }
58
+ const upSpin = spinner(`Reaching ${opts.upstream}…`);
59
+ const probe = await probeUpstream(opts.upstream);
60
+ if (probe.reachable) {
61
+ upSpin.succeed(`Anthropic reachable ${dim(`(${probe.latencyMs}ms, status ${probe.status})`)}`);
62
+ }
63
+ else {
64
+ upSpin.fail(`Anthropic unreachable: ${probe.detail ?? "unknown"}`);
65
+ emit(dim(" Setup will continue. You can re-run after fixing your network."));
66
+ }
67
+ // Step 3: API key sanity
68
+ const keyState = classifyApiKey(process.env["ANTHROPIC_API_KEY"]);
69
+ say("");
70
+ heading("3) Checking your Anthropic API key");
71
+ if (keyState.state === "ok") {
72
+ emit(`${sym.check} ANTHROPIC_API_KEY is set in this shell. ${dim("(That's fine; you may also set it in the shell that runs Claude Code.)")}`);
73
+ }
74
+ else if (keyState.state === "wrong_prefix") {
75
+ emit(`${sym.warn} ${keyState.hint}`);
76
+ }
77
+ else {
78
+ emit(`${sym.info} ANTHROPIC_API_KEY isn't set in this shell.`);
79
+ emit(dim(" That's expected — set it in the shell where you run Claude Code, not here."));
80
+ }
81
+ // Step 4: configure tool
82
+ say("");
83
+ heading("4) Configure your AI tool");
84
+ let configuredVia = null;
85
+ if (target !== null && target.configureMethod === "shell-rc") {
86
+ const ok = opts.yes ? true : await ask(rl, `Add ANTHROPIC_BASE_URL=${baseUrl} to your shell rc so ${target.name} uses TokenShield?`, "y");
87
+ if (ok) {
88
+ try {
89
+ const result = writeShellRc({ baseUrl });
90
+ emit(`${sym.check} ${target.name} configured via ${result.rcPath} ${dim(`(${result.action})`)}`);
91
+ emit(dim(` Reload: source ${result.rcPath} (or open a new terminal)`));
92
+ configuredVia = "shell-rc";
93
+ }
94
+ catch (err) {
95
+ emit(`${sym.warn} Couldn't write shell rc: ${err.message}`);
96
+ emit(dim(` Add manually: export ANTHROPIC_BASE_URL=${baseUrl}`));
97
+ }
98
+ }
99
+ }
100
+ else if (target !== null && target.configureMethod === "manual-snippet") {
101
+ emit(dim(" " + target.instructions));
102
+ emit("");
103
+ emit(box(manualSnippet(target.id, baseUrl), { title: `${target.name} config`, color: c.gray, padding: 1 }));
104
+ configuredVia = "manual-snippet";
105
+ }
106
+ else {
107
+ emit(dim(` Set ANTHROPIC_BASE_URL=${baseUrl} in your AI tool's settings or shell.`));
108
+ }
109
+ // Step 5: start the proxy as a daemon
110
+ say("");
111
+ heading("5) Starting the proxy");
112
+ const existing = readDaemon();
113
+ if (existing !== null) {
114
+ emit(`${sym.info} TokenShield is already running ${dim(`(pid ${existing.pid})`)}.`);
115
+ }
116
+ else {
117
+ const proceed = opts.yes ? true : await ask(rl, "Start the proxy in the background now?", "y");
118
+ if (proceed) {
119
+ const spin = spinner("Starting daemon…");
120
+ try {
121
+ const cfg = defaultConfig();
122
+ const info = await spawnDaemon({
123
+ port: opts.port,
124
+ dashboardPort: opts.dashboardPort,
125
+ bind: opts.bind,
126
+ upstream: opts.upstream,
127
+ ledger: cfg.ledgerPath,
128
+ retentionDays: opts.retentionDays,
129
+ });
130
+ spin.succeed(`Proxy live on http://${info.bind}:${info.port}`);
131
+ }
132
+ catch (err) {
133
+ spin.fail(err.message);
134
+ }
135
+ }
136
+ }
137
+ // Done
138
+ say("");
139
+ say(c.bold(`${sym.check} Setup complete.`));
140
+ say("");
141
+ say(" Dashboard:");
142
+ say(` ${c.cyan(`http://${opts.bind}:${opts.dashboardPort}`)}`);
143
+ say("");
144
+ say(" Useful commands:");
145
+ say(" tokenshield status # live spend + uptime");
146
+ say(" tokenshield logs --limit 20");
147
+ say(" tokenshield integrations list");
148
+ say(" tokenshield stop # stop the daemon");
149
+ if (configuredVia === "shell-rc") {
150
+ say("");
151
+ say(dim(" Don't forget: open a new terminal (or `source` your rc) so ANTHROPIC_BASE_URL takes effect."));
152
+ }
153
+ say("");
154
+ }
155
+ finally {
156
+ rl.close();
157
+ }
158
+ }
159
+ //# sourceMappingURL=setup.js.map