@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.
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +238 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/bench.d.ts +5 -0
- package/dist/commands/bench.js +191 -0
- package/dist/commands/bench.js.map +1 -0
- package/dist/commands/demo.d.ts +3 -0
- package/dist/commands/demo.js +99 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +119 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/estimate.d.ts +3 -0
- package/dist/commands/estimate.js +90 -0
- package/dist/commands/estimate.js.map +1 -0
- package/dist/commands/integrations.d.ts +14 -0
- package/dist/commands/integrations.js +138 -0
- package/dist/commands/integrations.js.map +1 -0
- package/dist/commands/logs.d.ts +6 -0
- package/dist/commands/logs.js +66 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/setup.d.ts +9 -0
- package/dist/commands/setup.js +159 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +102 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/stop.d.ts +1 -0
- package/dist/commands/stop.js +27 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/up.d.ts +15 -0
- package/dist/commands/up.js +145 -0
- package/dist/commands/up.js.map +1 -0
- package/dist/dashboard.d.ts +4 -0
- package/dist/dashboard.js +186 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/lib/daemon.d.ts +23 -0
- package/dist/lib/daemon.js +164 -0
- package/dist/lib/daemon.js.map +1 -0
- package/dist/lib/errors.d.ts +23 -0
- package/dist/lib/errors.js +131 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/integrations.d.ts +47 -0
- package/dist/lib/integrations.js +210 -0
- package/dist/lib/integrations.js.map +1 -0
- package/dist/lib/paths.d.ts +5 -0
- package/dist/lib/paths.js +18 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/preflight.d.ts +20 -0
- package/dist/lib/preflight.js +97 -0
- package/dist/lib/preflight.js.map +1 -0
- package/dist/lib/ui.d.ts +68 -0
- package/dist/lib/ui.js +221 -0
- package/dist/lib/ui.js.map +1 -0
- 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":"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"}
|
|
@@ -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,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,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
|