@curatedmcp/tokenshield 1.0.1 → 1.0.2
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/dashboard.js +146 -34
- package/dist/dashboard.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/dashboard.js
CHANGED
|
@@ -16,9 +16,15 @@ export function dashboardHtml(opts) {
|
|
|
16
16
|
--text: #e7e9ee;
|
|
17
17
|
--muted: #8b94a3;
|
|
18
18
|
--accent: #6dd3a8;
|
|
19
|
-
--accent-dim: rgba(109,211,168,0.15);
|
|
20
19
|
--warn: #f0b35e;
|
|
20
|
+
--warn-bg: rgba(240,179,94,0.08);
|
|
21
|
+
--warn-border: rgba(240,179,94,0.35);
|
|
21
22
|
--bad: #ef6868;
|
|
23
|
+
--bad-bg: rgba(239,104,104,0.08);
|
|
24
|
+
--bad-border: rgba(239,104,104,0.35);
|
|
25
|
+
--info: #7eb8ff;
|
|
26
|
+
--info-bg: rgba(126,184,255,0.08);
|
|
27
|
+
--info-border: rgba(126,184,255,0.35);
|
|
22
28
|
--link: #7eb8ff;
|
|
23
29
|
}
|
|
24
30
|
* { box-sizing: border-box }
|
|
@@ -26,35 +32,54 @@ html, body { margin: 0; background: var(--bg); color: var(--text); font: 14px/1.
|
|
|
26
32
|
.wrap { max-width: 1100px; margin: 0 auto; padding: 24px; }
|
|
27
33
|
|
|
28
34
|
/* ── header ── */
|
|
29
|
-
header { display: flex; align-items: center; justify-content: space-between; margin-bottom:
|
|
30
|
-
.brand { display: flex; align-items: baseline; gap: 10px; }
|
|
35
|
+
header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; gap: 12px; flex-wrap: wrap; }
|
|
36
|
+
.brand { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
|
|
31
37
|
.brand-name { font-size: 18px; font-weight: 700; letter-spacing: 0.02em; color: var(--text); text-decoration: none; }
|
|
32
38
|
.brand-name:hover { color: var(--accent); }
|
|
33
39
|
.brand-by { font-size: 11px; color: var(--muted); font-weight: 400; letter-spacing: 0.02em; }
|
|
34
40
|
.brand-by a { color: var(--accent); text-decoration: none; font-weight: 500; }
|
|
35
41
|
.brand-by a:hover { text-decoration: underline; }
|
|
36
42
|
.brand-ver { font-size: 12px; color: var(--muted); font-weight: 400; }
|
|
43
|
+
.mode-pill { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; padding: 2px 7px; border-radius: 999px; background: var(--info-bg); border: 1px solid var(--info-border); color: var(--info); font-weight: 600; cursor: help; }
|
|
37
44
|
.status { display: flex; align-items: center; gap: 14px; }
|
|
38
45
|
.status-pill { font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 6px; }
|
|
39
46
|
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 999px; background: var(--accent); flex-shrink: 0; }
|
|
40
47
|
.dot.pulse { animation: pulse 2s ease-in-out infinite; }
|
|
41
48
|
@keyframes pulse { 0%,100% { opacity:1 } 50% { opacity:.45 } }
|
|
42
49
|
|
|
50
|
+
/* ── diagnostic banner ── */
|
|
51
|
+
.diag { display: none; margin-bottom: 18px; border-radius: 10px; padding: 14px 16px; font-size: 13px; line-height: 1.55; border: 1px solid; }
|
|
52
|
+
.diag.show { display: block; }
|
|
53
|
+
.diag.info { background: var(--info-bg); border-color: var(--info-border); color: var(--text); }
|
|
54
|
+
.diag.warn { background: var(--warn-bg); border-color: var(--warn-border); color: var(--text); }
|
|
55
|
+
.diag.bad { background: var(--bad-bg); border-color: var(--bad-border); color: var(--text); }
|
|
56
|
+
.diag-title { font-weight: 600; margin-bottom: 6px; display: flex; align-items: center; gap: 8px; }
|
|
57
|
+
.diag-title .icon { display: inline-flex; width: 18px; height: 18px; align-items: center; justify-content: center; border-radius: 999px; font-size: 11px; font-weight: 700; }
|
|
58
|
+
.diag.info .diag-title { color: var(--info); }
|
|
59
|
+
.diag.info .icon { background: var(--info); color: #0b0d10; }
|
|
60
|
+
.diag.warn .diag-title { color: var(--warn); }
|
|
61
|
+
.diag.warn .icon { background: var(--warn); color: #0b0d10; }
|
|
62
|
+
.diag.bad .diag-title { color: var(--bad); }
|
|
63
|
+
.diag.bad .icon { background: var(--bad); color: #0b0d10; }
|
|
64
|
+
.diag code { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px; background: var(--panel-2); padding: 1px 6px; border-radius: 4px; border: 1px solid var(--border); }
|
|
65
|
+
.diag a { color: var(--link); }
|
|
66
|
+
|
|
43
67
|
/* ── metric cards ── */
|
|
44
68
|
.grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
|
|
45
69
|
.card { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 14px; }
|
|
46
70
|
.card .label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
|
|
47
71
|
.card .value { font-size: 22px; font-weight: 600; margin-top: 6px; font-variant-numeric: tabular-nums; }
|
|
48
72
|
.card .sub { font-size: 11px; color: var(--muted); margin-top: 4px; }
|
|
49
|
-
|
|
50
|
-
/* ── savings highlight card ── */
|
|
51
73
|
.card.savings { border-color: rgba(109,211,168,0.25); background: linear-gradient(135deg, var(--panel) 0%, rgba(109,211,168,0.06) 100%); }
|
|
52
74
|
.card.savings .value { color: var(--accent); }
|
|
53
75
|
.card.savings .label { color: rgba(109,211,168,0.7); }
|
|
54
76
|
|
|
55
77
|
/* ── tables ── */
|
|
56
78
|
.section { margin-top: 24px; }
|
|
57
|
-
.section
|
|
79
|
+
.section-head { display: flex; justify-content: space-between; align-items: center; margin: 0 0 10px; }
|
|
80
|
+
.section h2 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); margin: 0; }
|
|
81
|
+
.toggle-link { font-size: 11px; color: var(--muted); cursor: pointer; user-select: none; background: none; border: none; padding: 0; font-family: inherit; }
|
|
82
|
+
.toggle-link:hover { color: var(--accent); }
|
|
58
83
|
table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
|
|
59
84
|
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); font-size: 13px; }
|
|
60
85
|
th { color: var(--muted); font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; }
|
|
@@ -63,18 +88,21 @@ tr:last-child td { border-bottom: 0; }
|
|
|
63
88
|
.num { text-align: right; }
|
|
64
89
|
.pill { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11px; border: 1px solid var(--border); }
|
|
65
90
|
.pill.ok { color: var(--accent); border-color: rgba(109,211,168,0.35); }
|
|
66
|
-
.pill.
|
|
91
|
+
.pill.warn { color: var(--warn); border-color: var(--warn-border); }
|
|
92
|
+
.pill.err { color: var(--bad); border-color: var(--bad-border); }
|
|
93
|
+
.row-faded td { opacity: 0.55; }
|
|
67
94
|
|
|
68
95
|
/* ── misc ── */
|
|
69
|
-
.cmd { background: var(--panel-2); border: 1px solid var(--border); border-radius: 6px; padding: 10px
|
|
96
|
+
.cmd { background: var(--panel-2); border: 1px solid var(--border); border-radius: 6px; padding: 6px 10px; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12px; display: inline-block; }
|
|
70
97
|
.kbd { font-family: ui-monospace, monospace; background: var(--panel-2); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; font-size: 11px; }
|
|
71
98
|
a { color: var(--link); text-decoration: none; }
|
|
72
99
|
a:hover { text-decoration: underline; }
|
|
73
100
|
|
|
74
101
|
/* ── footer ── */
|
|
75
|
-
.footer { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--border); display: flex; justify-content: space-between; align-items: flex-start; gap:
|
|
76
|
-
.footer-privacy { color: var(--muted); font-size: 12px; flex: 1 1
|
|
77
|
-
.footer-
|
|
102
|
+
.footer { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--border); display: flex; justify-content: space-between; align-items: flex-start; gap: 20px; flex-wrap: wrap; }
|
|
103
|
+
.footer-privacy { color: var(--muted); font-size: 12px; flex: 1 1 380px; line-height: 1.7; }
|
|
104
|
+
.footer-privacy .cmd { margin: 4px 0; }
|
|
105
|
+
.footer-links { display: flex; align-items: center; gap: 12px; flex-shrink: 0; flex-wrap: wrap; }
|
|
78
106
|
.footer-links a { font-size: 12px; color: var(--muted); text-decoration: none; white-space: nowrap; }
|
|
79
107
|
.footer-links a:hover { color: var(--accent); text-decoration: none; }
|
|
80
108
|
.footer-links .sep { color: var(--border); user-select: none; }
|
|
@@ -89,20 +117,23 @@ a:hover { text-decoration: underline; }
|
|
|
89
117
|
<a class="brand-name" href="https://www.curatedmcp.com/tokenshield" target="_blank" rel="noopener">TokenShield</a>
|
|
90
118
|
<span class="brand-ver">v${opts.version}</span>
|
|
91
119
|
<span class="brand-by">by <a href="https://www.curatedmcp.com" target="_blank" rel="noopener">CuratedMCP</a></span>
|
|
120
|
+
<span class="mode-pill" title="Estimate mode measures your spend. Active compression processors (dedup, cache, diff) ship in upcoming releases — track at curatedmcp.com/tokenshield.">Estimate mode</span>
|
|
92
121
|
</div>
|
|
93
122
|
<div class="status">
|
|
94
123
|
<div class="status-pill">
|
|
95
|
-
<span class="dot pulse"></span>
|
|
124
|
+
<span class="dot pulse" id="status-dot"></span>
|
|
96
125
|
<span id="status-text">proxy live · ${opts.bind}:${opts.proxyPort}</span>
|
|
97
126
|
</div>
|
|
98
127
|
</div>
|
|
99
128
|
</header>
|
|
100
129
|
|
|
130
|
+
<div id="diag" class="diag"></div>
|
|
131
|
+
|
|
101
132
|
<div class="grid">
|
|
102
133
|
<div class="card savings">
|
|
103
134
|
<div class="label">Spent (24h)</div>
|
|
104
135
|
<div class="value" id="dollars-spent">$0.00</div>
|
|
105
|
-
<div class="sub" id="request-count">0 requests</div>
|
|
136
|
+
<div class="sub" id="request-count">0 requests · 0 successful</div>
|
|
106
137
|
</div>
|
|
107
138
|
<div class="card">
|
|
108
139
|
<div class="label">Input tokens (24h)</div>
|
|
@@ -138,7 +169,10 @@ a:hover { text-decoration: underline; }
|
|
|
138
169
|
</div>
|
|
139
170
|
|
|
140
171
|
<div class="section">
|
|
141
|
-
<
|
|
172
|
+
<div class="section-head">
|
|
173
|
+
<h2>Recent requests</h2>
|
|
174
|
+
<button class="toggle-link" id="toggle-noise" onclick="toggleNoise()">Show probes</button>
|
|
175
|
+
</div>
|
|
142
176
|
<table id="recent">
|
|
143
177
|
<thead>
|
|
144
178
|
<tr>
|
|
@@ -158,7 +192,7 @@ a:hover { text-decoration: underline; }
|
|
|
158
192
|
|
|
159
193
|
<div class="footer">
|
|
160
194
|
<div class="footer-privacy">
|
|
161
|
-
Privacy: TokenShield never stores prompt content. Your Anthropic API key stays in process memory
|
|
195
|
+
Privacy: TokenShield never stores prompt content. Your Anthropic API key stays in process memory.<br>
|
|
162
196
|
Set <span class="cmd">export ANTHROPIC_BASE_URL=${proxyBase}</span> in the shell you run Claude Code from.
|
|
163
197
|
</div>
|
|
164
198
|
<div class="footer-links">
|
|
@@ -178,6 +212,88 @@ const fmtDollars = (d) => '$' + Number(d || 0).toFixed(Math.abs(d) < 1 ? 4 : 2);
|
|
|
178
212
|
const fmtTime = (ms) => new Date(ms).toLocaleTimeString();
|
|
179
213
|
const fmtMs = (ms) => ms < 1000 ? ms + 'ms' : (ms / 1000).toFixed(1) + 's';
|
|
180
214
|
|
|
215
|
+
// Treat /v1/* requests as real Anthropic traffic; everything else (e.g. GET /, probes) is noise.
|
|
216
|
+
const isApiCall = (r) => typeof r.endpoint === 'string' && r.endpoint.startsWith('/v1/');
|
|
217
|
+
|
|
218
|
+
let showNoise = false;
|
|
219
|
+
function toggleNoise() {
|
|
220
|
+
showNoise = !showNoise;
|
|
221
|
+
document.getElementById('toggle-noise').textContent = showNoise ? 'Hide probes' : 'Show probes';
|
|
222
|
+
// Re-render on next tick from cached state
|
|
223
|
+
if (window.__lastRec) renderRecent(window.__lastRec);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function renderDiagnostic(rec) {
|
|
227
|
+
const diag = document.getElementById('diag');
|
|
228
|
+
const apiCalls = rec.filter(isApiCall);
|
|
229
|
+
const failed = apiCalls.filter((r) => r.upstreamStatus === 401 || r.upstreamStatus === 403);
|
|
230
|
+
const fiveXx = apiCalls.filter((r) => r.upstreamStatus >= 500);
|
|
231
|
+
const ok = apiCalls.filter((r) => r.upstreamStatus >= 200 && r.upstreamStatus < 300);
|
|
232
|
+
|
|
233
|
+
let html = '';
|
|
234
|
+
let cls = '';
|
|
235
|
+
|
|
236
|
+
if (apiCalls.length === 0) {
|
|
237
|
+
cls = 'info';
|
|
238
|
+
html = '<div class="diag-title"><span class="icon">i</span>Waiting for traffic</div>' +
|
|
239
|
+
'No Anthropic API calls yet. In the shell that runs Claude Code, set:<br>' +
|
|
240
|
+
'<code>export ANTHROPIC_API_KEY=sk-ant-…</code> <code>export ANTHROPIC_BASE_URL=${proxyBase}</code><br>' +
|
|
241
|
+
'Then run <code>claude</code> in that same shell.';
|
|
242
|
+
} else if (failed.length >= apiCalls.length * 0.5 && failed.length > 0) {
|
|
243
|
+
cls = 'bad';
|
|
244
|
+
const pct = Math.round((failed.length / apiCalls.length) * 100);
|
|
245
|
+
html = '<div class="diag-title"><span class="icon">!</span>' + pct + '% of API calls failing with 401/403 — your API key isn\\'t reaching Anthropic</div>' +
|
|
246
|
+
'The proxy is forwarding requests fine, but Anthropic is rejecting them. In the <strong>shell that runs Claude Code</strong> (not this one), check:<br>' +
|
|
247
|
+
'<code>echo $ANTHROPIC_API_KEY</code> — should start with <code>sk-ant-</code><br>' +
|
|
248
|
+
'<code>echo $ANTHROPIC_BASE_URL</code> — should be <code>${proxyBase}</code><br>' +
|
|
249
|
+
'Then restart Claude Code in that shell.';
|
|
250
|
+
} else if (fiveXx.length >= apiCalls.length * 0.3 && fiveXx.length > 0) {
|
|
251
|
+
cls = 'warn';
|
|
252
|
+
html = '<div class="diag-title"><span class="icon">!</span>Upstream errors from Anthropic</div>' +
|
|
253
|
+
fiveXx.length + ' of ' + apiCalls.length + ' recent requests returned 5xx. Check <a href="https://status.anthropic.com" target="_blank" rel="noopener">status.anthropic.com</a>.';
|
|
254
|
+
} else if (ok.length > 0) {
|
|
255
|
+
// Healthy — hide the banner entirely
|
|
256
|
+
diag.className = 'diag';
|
|
257
|
+
diag.innerHTML = '';
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
diag.className = 'diag show ' + cls;
|
|
262
|
+
diag.innerHTML = html;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function renderRecent(rec) {
|
|
266
|
+
const recBody = document.querySelector('#recent tbody');
|
|
267
|
+
const filtered = showNoise ? rec : rec.filter(isApiCall);
|
|
268
|
+
|
|
269
|
+
if (filtered.length === 0) {
|
|
270
|
+
const msg = rec.length === 0
|
|
271
|
+
? 'No requests recorded yet.'
|
|
272
|
+
: 'No Anthropic API calls yet (' + rec.length + ' non-API probe' + (rec.length === 1 ? '' : 's') + ' hidden — click "Show probes" to view).';
|
|
273
|
+
recBody.innerHTML = '<tr><td colspan="8" class="muted">' + msg + '</td></tr>';
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
recBody.innerHTML = filtered.map((r) => {
|
|
278
|
+
const status = Number(r.upstreamStatus) || 0;
|
|
279
|
+
const isOk = status >= 200 && status < 300;
|
|
280
|
+
const isAuthFail = status === 401 || status === 403;
|
|
281
|
+
const pillCls = isOk ? 'ok' : (isAuthFail ? 'warn' : 'err');
|
|
282
|
+
const isProbe = !isApiCall(r);
|
|
283
|
+
const modelDisplay = (r.model === 'unknown' && isProbe) ? '<span class="muted">—</span>' : r.model;
|
|
284
|
+
return '<tr class="' + (isProbe ? 'row-faded' : '') + '">' +
|
|
285
|
+
'<td class="muted">' + fmtTime(r.timestamp) + '</td>' +
|
|
286
|
+
'<td>' + modelDisplay + '</td>' +
|
|
287
|
+
'<td class="muted">' + r.endpoint + '</td>' +
|
|
288
|
+
'<td class="num">' + fmtNum(r.usageRaw.inputTokens) + '</td>' +
|
|
289
|
+
'<td class="num">' + fmtNum(r.usageRaw.outputTokens) + '</td>' +
|
|
290
|
+
'<td class="num muted">' + fmtMs(r.durationMs) + '</td>' +
|
|
291
|
+
'<td class="num">' + fmtDollars(r.dollarsRaw) + '</td>' +
|
|
292
|
+
'<td><span class="pill ' + pillCls + '">' + status + '</span></td>' +
|
|
293
|
+
'</tr>';
|
|
294
|
+
}).join('');
|
|
295
|
+
}
|
|
296
|
+
|
|
181
297
|
async function refresh() {
|
|
182
298
|
try {
|
|
183
299
|
const [sumRes, recRes] = await Promise.all([
|
|
@@ -189,12 +305,19 @@ async function refresh() {
|
|
|
189
305
|
}
|
|
190
306
|
const sum = await sumRes.json();
|
|
191
307
|
const rec = await recRes.json();
|
|
308
|
+
window.__lastRec = rec;
|
|
309
|
+
|
|
192
310
|
// Successful refresh — clear any stale error and restore the live indicator
|
|
193
311
|
const statusEl = document.getElementById('status-text');
|
|
194
312
|
if (statusEl) statusEl.textContent = 'live · ${opts.bind}:${opts.proxyPort}';
|
|
195
313
|
|
|
314
|
+
// Count successful API calls for the spent card
|
|
315
|
+
const apiCalls = rec.filter(isApiCall);
|
|
316
|
+
const okCount = apiCalls.filter((r) => r.upstreamStatus >= 200 && r.upstreamStatus < 300).length;
|
|
317
|
+
|
|
196
318
|
document.getElementById('dollars-spent').textContent = fmtDollars(sum.dollarsRaw);
|
|
197
|
-
document.getElementById('request-count').textContent =
|
|
319
|
+
document.getElementById('request-count').textContent =
|
|
320
|
+
fmtNum(sum.requestCount) + ' total · ' + okCount + ' successful';
|
|
198
321
|
document.getElementById('input-tokens').textContent = fmtNum(sum.totalInputTokensRaw);
|
|
199
322
|
document.getElementById('output-tokens').textContent = fmtNum(sum.totalOutputTokensRaw);
|
|
200
323
|
|
|
@@ -203,32 +326,21 @@ async function refresh() {
|
|
|
203
326
|
document.getElementById('weekly-projected').textContent = fmtDollars(weeklyProjection);
|
|
204
327
|
|
|
205
328
|
const modelBody = document.querySelector('#by-model tbody');
|
|
206
|
-
|
|
207
|
-
|
|
329
|
+
const realModels = (sum.byModel || []).filter((m) => m.model && m.model !== 'unknown');
|
|
330
|
+
if (realModels.length > 0) {
|
|
331
|
+
modelBody.innerHTML = realModels.map((m) =>
|
|
208
332
|
'<tr><td>' + m.model + '</td>' +
|
|
209
333
|
'<td class="num">' + fmtNum(m.requests) + '</td>' +
|
|
210
334
|
'<td class="num">' + fmtNum(m.inputTokens) + '</td>' +
|
|
211
335
|
'<td class="num">' + fmtNum(m.outputTokens) + '</td>' +
|
|
212
336
|
'<td class="num">' + fmtDollars(m.dollars) + '</td></tr>'
|
|
213
337
|
).join('');
|
|
338
|
+
} else {
|
|
339
|
+
modelBody.innerHTML = '<tr><td colspan="5" class="muted">No model traffic yet. Run Claude Code with <span class="kbd">ANTHROPIC_BASE_URL=${proxyBase}</span>.</td></tr>';
|
|
214
340
|
}
|
|
215
341
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
recBody.innerHTML = rec.map((r) => {
|
|
219
|
-
const ok = r.upstreamStatus >= 200 && r.upstreamStatus < 300;
|
|
220
|
-
return '<tr>' +
|
|
221
|
-
'<td class="muted">' + fmtTime(r.timestamp) + '</td>' +
|
|
222
|
-
'<td>' + r.model + '</td>' +
|
|
223
|
-
'<td class="muted">' + r.endpoint + '</td>' +
|
|
224
|
-
'<td class="num">' + fmtNum(r.usageRaw.inputTokens) + '</td>' +
|
|
225
|
-
'<td class="num">' + fmtNum(r.usageRaw.outputTokens) + '</td>' +
|
|
226
|
-
'<td class="num muted">' + fmtMs(r.durationMs) + '</td>' +
|
|
227
|
-
'<td class="num">' + fmtDollars(r.dollarsRaw) + '</td>' +
|
|
228
|
-
'<td><span class="pill ' + (ok ? 'ok' : 'err') + '">' + r.upstreamStatus + '</span></td>' +
|
|
229
|
-
'</tr>';
|
|
230
|
-
}).join('');
|
|
231
|
-
}
|
|
342
|
+
renderRecent(rec);
|
|
343
|
+
renderDiagnostic(rec);
|
|
232
344
|
} catch (e) {
|
|
233
345
|
document.getElementById('status-text').textContent = 'error: ' + e.message;
|
|
234
346
|
}
|
package/dist/dashboard.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,IAA0D;IACtF,MAAM,SAAS,GAAG,UAAU,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;IAC1D,OAAO;;;;;qBAKY,IAAI,CAAC,OAAO
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa,CAAC,IAA0D;IACtF,MAAM,SAAS,GAAG,UAAU,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;IAC1D,OAAO;;;;;qBAKY,IAAI,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iCA8GA,IAAI,CAAC,OAAO;;;;;;;8CAOC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2HA0CkD,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wDA6B5E,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+FA4C8B,SAAS;;;;;;;;iEAQvC,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mDAgEvB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;iJA2BmE,SAAS;;;;;;;;;;;;;;QAclJ,CAAC;AACT,CAAC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.0.
|
|
1
|
+
export declare const VERSION = "1.0.2";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@curatedmcp/tokenshield",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "TokenShield CLI — cut your Claude Code bill 40–70%. Local proxy. Your API key never leaves your machine.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@curatedmcp/tokenshield-core": "^1.0.
|
|
35
|
+
"@curatedmcp/tokenshield-core": "^1.0.2",
|
|
36
36
|
"commander": "^12.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|