ccgauge 1.0.4 → 1.0.5
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-build-manifest.json +33 -33
- package/.next/standalone/.next/app-path-routes-manifest.json +9 -9
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
- package/.next/standalone/.next/server/app/api/projects/route.js +1 -1
- package/.next/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/route.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/usage/route.js +1 -1
- package/.next/standalone/.next/server/app/api/usage/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/models/page.js +2 -2
- package/.next/standalone/.next/server/app/models/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/[id]/page.js +2 -2
- package/.next/standalone/.next/server/app/projects/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/sessions/[id]/page.js +2 -2
- package/.next/standalone/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/sessions/page.js +1 -1
- package/.next/standalone/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page.js +1 -1
- package/.next/standalone/.next/server/app/usage/page.js +3 -3
- package/.next/standalone/.next/server/app/usage/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +9 -9
- package/.next/standalone/.next/server/chunks/287.js +1 -0
- package/.next/standalone/.next/server/chunks/567.js +2 -2
- package/.next/standalone/.next/server/chunks/730.js +1 -0
- package/.next/standalone/.next/server/functions-config-manifest.json +2 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/node_modules/next/node_modules/postcss/package.json +0 -0
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/public/codex-logo.png +0 -0
- package/CHANGELOG.md +223 -0
- package/README.md +6 -2
- package/README.zh-CN.md +6 -2
- package/bin/cli.mjs +394 -91
- package/dist/mcp/server.mjs +16 -16
- package/dist/report/index.mjs +183 -28
- package/package.json +22 -24
- package/.next/standalone/.next/server/chunks/971.js +0 -1
- /package/.next/standalone/.next/static/{ir1LZCnQKkiNUVXLprtzh → kdpS1dOlXPsnKYuNBuMt9}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{ir1LZCnQKkiNUVXLprtzh → kdpS1dOlXPsnKYuNBuMt9}/_ssgManifest.js +0 -0
package/dist/report/index.mjs
CHANGED
|
@@ -1528,6 +1528,69 @@ function projectNameFromCwd(cwd) {
|
|
|
1528
1528
|
return parts[parts.length - 1] || cwd;
|
|
1529
1529
|
}
|
|
1530
1530
|
|
|
1531
|
+
// lib/project-label.ts
|
|
1532
|
+
import { readFileSync, statSync } from "node:fs";
|
|
1533
|
+
import { basename } from "node:path";
|
|
1534
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1535
|
+
var GITDIR_PATTERN = /^(.+?)[/\\]\.git[/\\]worktrees[/\\]([^/\\]+)[/\\]?$/;
|
|
1536
|
+
var CWD_WORKTREE_PATTERN = /^(.+?)[/\\](?:\.git|\.claude)[/\\]worktrees[/\\]([^/\\]+)(?:[/\\].*)?$/;
|
|
1537
|
+
function resolveRaw(cwd) {
|
|
1538
|
+
const fallbackName = projectNameFromCwd(cwd);
|
|
1539
|
+
if (!cwd) {
|
|
1540
|
+
return { label: fallbackName, isWorktree: false, mainName: fallbackName, worktreeName: "", canonicalCwd: cwd };
|
|
1541
|
+
}
|
|
1542
|
+
const pathMatch = CWD_WORKTREE_PATTERN.exec(cwd);
|
|
1543
|
+
if (pathMatch) {
|
|
1544
|
+
const mainRepoPath = pathMatch[1];
|
|
1545
|
+
const worktreeName = pathMatch[2];
|
|
1546
|
+
const mainName = basename(mainRepoPath) || mainRepoPath;
|
|
1547
|
+
return {
|
|
1548
|
+
label: `${mainName} (${worktreeName})`,
|
|
1549
|
+
isWorktree: true,
|
|
1550
|
+
mainName,
|
|
1551
|
+
worktreeName,
|
|
1552
|
+
canonicalCwd: mainRepoPath
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
try {
|
|
1556
|
+
const gitPath = `${cwd}/.git`;
|
|
1557
|
+
const s = statSync(gitPath);
|
|
1558
|
+
if (!s.isFile()) {
|
|
1559
|
+
return { label: fallbackName, isWorktree: false, mainName: fallbackName, worktreeName: "", canonicalCwd: cwd };
|
|
1560
|
+
}
|
|
1561
|
+
const text = readFileSync(gitPath, "utf8").trim();
|
|
1562
|
+
const firstLine = text.split(/\r?\n/, 1)[0] ?? "";
|
|
1563
|
+
const m = /^gitdir:\s*(.+)$/.exec(firstLine);
|
|
1564
|
+
if (!m) {
|
|
1565
|
+
return { label: fallbackName, isWorktree: false, mainName: fallbackName, worktreeName: "", canonicalCwd: cwd };
|
|
1566
|
+
}
|
|
1567
|
+
const gitdir = m[1].trim();
|
|
1568
|
+
const wt = GITDIR_PATTERN.exec(gitdir);
|
|
1569
|
+
if (!wt) {
|
|
1570
|
+
return { label: fallbackName, isWorktree: false, mainName: fallbackName, worktreeName: "", canonicalCwd: cwd };
|
|
1571
|
+
}
|
|
1572
|
+
const mainRepoPath = wt[1];
|
|
1573
|
+
const worktreeName = wt[2];
|
|
1574
|
+
const mainName = basename(mainRepoPath) || mainRepoPath;
|
|
1575
|
+
return {
|
|
1576
|
+
label: `${mainName} (${worktreeName})`,
|
|
1577
|
+
isWorktree: true,
|
|
1578
|
+
mainName,
|
|
1579
|
+
worktreeName,
|
|
1580
|
+
canonicalCwd: mainRepoPath
|
|
1581
|
+
};
|
|
1582
|
+
} catch {
|
|
1583
|
+
return { label: fallbackName, isWorktree: false, mainName: fallbackName, worktreeName: "", canonicalCwd: cwd };
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
function resolveProjectLabel(cwd) {
|
|
1587
|
+
const cached = cache.get(cwd);
|
|
1588
|
+
if (cached) return cached.label;
|
|
1589
|
+
const r = resolveRaw(cwd);
|
|
1590
|
+
cache.set(cwd, r);
|
|
1591
|
+
return r.label;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1531
1594
|
// lib/aggregator/index.ts
|
|
1532
1595
|
var GRANULARITIES = ["hour", "day", "week", "month"];
|
|
1533
1596
|
function isGranularity(v) {
|
|
@@ -1719,6 +1782,12 @@ function aggregateBySession(records, userRecords, opts) {
|
|
|
1719
1782
|
source: rec.source,
|
|
1720
1783
|
cwd: rec.cwd,
|
|
1721
1784
|
projectName: projectNameFromCwd(rec.cwd),
|
|
1785
|
+
// Worktree-aware label so the sessions table shows the same
|
|
1786
|
+
// identifier the usage table does for the same record (e.g.
|
|
1787
|
+
// `ai-self-web (playwright)` instead of just `playwright`).
|
|
1788
|
+
// `resolveProjectLabel` caches per-cwd, so this is one fs.stat
|
|
1789
|
+
// per unique cwd across the whole aggregation.
|
|
1790
|
+
projectLabel: resolveProjectLabel(rec.cwd),
|
|
1722
1791
|
startTime: rec.timestamp,
|
|
1723
1792
|
endTime: rec.timestamp,
|
|
1724
1793
|
durationMs: 0,
|
|
@@ -1825,6 +1894,66 @@ function rangeToDates(range) {
|
|
|
1825
1894
|
return { from };
|
|
1826
1895
|
}
|
|
1827
1896
|
|
|
1897
|
+
// lib/turns.ts
|
|
1898
|
+
var MAX_PARENT_WALK = 5e3;
|
|
1899
|
+
function buildTurnIndex(assistants, users, parentMap) {
|
|
1900
|
+
const userTextMap = /* @__PURE__ */ new Map();
|
|
1901
|
+
for (const u of users) {
|
|
1902
|
+
if (u.isSynthetic) continue;
|
|
1903
|
+
if (u.textPreview && u.textPreview.trim()) userTextMap.set(u.uuid, u.textPreview);
|
|
1904
|
+
}
|
|
1905
|
+
const result = /* @__PURE__ */ new Map();
|
|
1906
|
+
const memo = /* @__PURE__ */ new Map();
|
|
1907
|
+
function resolve(startUuid) {
|
|
1908
|
+
const path5 = [];
|
|
1909
|
+
let cur = startUuid;
|
|
1910
|
+
let answer = null;
|
|
1911
|
+
let steps = 0;
|
|
1912
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1913
|
+
while (cur && steps++ < MAX_PARENT_WALK) {
|
|
1914
|
+
if (seen.has(cur)) break;
|
|
1915
|
+
seen.add(cur);
|
|
1916
|
+
const m = memo.get(cur);
|
|
1917
|
+
if (m) {
|
|
1918
|
+
answer = m;
|
|
1919
|
+
break;
|
|
1920
|
+
}
|
|
1921
|
+
path5.push(cur);
|
|
1922
|
+
if (userTextMap.has(cur)) {
|
|
1923
|
+
answer = cur;
|
|
1924
|
+
break;
|
|
1925
|
+
}
|
|
1926
|
+
cur = parentMap[cur] ?? null;
|
|
1927
|
+
}
|
|
1928
|
+
if (!answer) answer = startUuid;
|
|
1929
|
+
for (const id of path5) memo.set(id, answer);
|
|
1930
|
+
return answer;
|
|
1931
|
+
}
|
|
1932
|
+
for (const a of assistants) {
|
|
1933
|
+
result.set(a.uuid, resolve(a.uuid));
|
|
1934
|
+
}
|
|
1935
|
+
return result;
|
|
1936
|
+
}
|
|
1937
|
+
function summarizeTurns(records, users, parentMap) {
|
|
1938
|
+
const turnIndex = buildTurnIndex(records, users, parentMap);
|
|
1939
|
+
const out = /* @__PURE__ */ new Map();
|
|
1940
|
+
for (const r of records) {
|
|
1941
|
+
const turnId = turnIndex.get(r.uuid) ?? r.uuid;
|
|
1942
|
+
const existing = out.get(turnId);
|
|
1943
|
+
if (!existing || r.timestamp < existing.firstTimestamp) {
|
|
1944
|
+
out.set(turnId, {
|
|
1945
|
+
turnId,
|
|
1946
|
+
firstTimestamp: r.timestamp,
|
|
1947
|
+
firstModel: r.model,
|
|
1948
|
+
cwd: r.cwd,
|
|
1949
|
+
sessionId: r.sessionId,
|
|
1950
|
+
source: r.source
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
return out;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1828
1957
|
// lib/cli-report/index.ts
|
|
1829
1958
|
var REPORT_RANGES = ["today", "1d", "7d", "30d", "90d", "all"];
|
|
1830
1959
|
var DIMS = ["model", "project", "session"];
|
|
@@ -1844,7 +1973,7 @@ async function runReport(opts) {
|
|
|
1844
1973
|
const filled = normalizeReportOptions(opts);
|
|
1845
1974
|
const scan = await getCachedScan();
|
|
1846
1975
|
const sources = filled.source === "all" ? ALL_PROVIDER_IDS : [filled.source];
|
|
1847
|
-
const data = computeReportData(scan
|
|
1976
|
+
const data = computeReportData(scan, sources, filled);
|
|
1848
1977
|
if (filled.json) return JSON.stringify(data, null, 2);
|
|
1849
1978
|
return renderText(data, filled);
|
|
1850
1979
|
}
|
|
@@ -1879,14 +2008,15 @@ function isDim(v) {
|
|
|
1879
2008
|
function invalidOptionMessage(name, value, expected) {
|
|
1880
2009
|
return `invalid ${name}: ${JSON.stringify(value)}. Expected one of: ${expected.join(", ")}`;
|
|
1881
2010
|
}
|
|
1882
|
-
function computeReportData(
|
|
2011
|
+
function computeReportData(scan, sources, o) {
|
|
2012
|
+
const allRecords = scan.records;
|
|
1883
2013
|
const dates = resolveRange(o);
|
|
1884
2014
|
const baseOpts = {
|
|
1885
2015
|
from: dates.from ?? void 0,
|
|
1886
|
-
to: dates.until ?? void 0
|
|
1887
|
-
|
|
1888
|
-
//
|
|
1889
|
-
|
|
2016
|
+
to: dates.until ?? void 0
|
|
2017
|
+
// `o.model` / `o.project` are substring patterns, not exact matches —
|
|
2018
|
+
// they're applied per-record by `withinSrcAndFilters` below, so we
|
|
2019
|
+
// deliberately leave the aggregator's exact-match filters unset.
|
|
1890
2020
|
};
|
|
1891
2021
|
const totals = {
|
|
1892
2022
|
input: 0,
|
|
@@ -1897,7 +2027,8 @@ function computeReportData(allRecords, sources, o) {
|
|
|
1897
2027
|
total: 0,
|
|
1898
2028
|
cost: 0,
|
|
1899
2029
|
saved: 0,
|
|
1900
|
-
requests: 0
|
|
2030
|
+
requests: 0,
|
|
2031
|
+
turns: 0
|
|
1901
2032
|
};
|
|
1902
2033
|
const trendBuckets = /* @__PURE__ */ new Map();
|
|
1903
2034
|
for (const source of sources) {
|
|
@@ -1913,19 +2044,28 @@ function computeReportData(allRecords, sources, o) {
|
|
|
1913
2044
|
totals.saved += t.saved;
|
|
1914
2045
|
totals.requests += t.requests;
|
|
1915
2046
|
for (const r of sourceRecs) totals.reasoning += r.usage.reasoning_tokens ?? 0;
|
|
2047
|
+
const turnSummaries = summarizeTurns(sourceRecs, scan.userRecords, scan.parentMap);
|
|
2048
|
+
totals.turns += turnSummaries.size;
|
|
2049
|
+
const turnsByKey = /* @__PURE__ */ new Map();
|
|
2050
|
+
for (const tn of turnSummaries.values()) {
|
|
2051
|
+
const { key } = bucketKey(tn.firstTimestamp, o.gran);
|
|
2052
|
+
turnsByKey.set(key, (turnsByKey.get(key) ?? 0) + 1);
|
|
2053
|
+
}
|
|
1916
2054
|
const buckets = aggregateByTime(sourceRecs, o.gran, opts);
|
|
1917
2055
|
for (const b of buckets) {
|
|
1918
2056
|
const ex = trendBuckets.get(b.key);
|
|
2057
|
+
const tnCount = turnsByKey.get(b.key) ?? 0;
|
|
1919
2058
|
if (ex) {
|
|
1920
2059
|
ex.cost += b.cost;
|
|
1921
2060
|
ex.tokens += b.totalTokens;
|
|
2061
|
+
ex.turns += tnCount;
|
|
1922
2062
|
} else {
|
|
1923
|
-
trendBuckets.set(b.key, { label: b.label, cost: b.cost, tokens: b.totalTokens });
|
|
2063
|
+
trendBuckets.set(b.key, { label: b.label, cost: b.cost, tokens: b.totalTokens, turns: tnCount });
|
|
1924
2064
|
}
|
|
1925
2065
|
}
|
|
1926
2066
|
}
|
|
1927
2067
|
const trend = Array.from(trendBuckets.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v);
|
|
1928
|
-
const breakdown = buildBreakdown(
|
|
2068
|
+
const breakdown = buildBreakdown(scan, sources, baseOpts, o);
|
|
1929
2069
|
return {
|
|
1930
2070
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1931
2071
|
range: o.range,
|
|
@@ -1939,7 +2079,8 @@ function computeReportData(allRecords, sources, o) {
|
|
|
1939
2079
|
breakdown
|
|
1940
2080
|
};
|
|
1941
2081
|
}
|
|
1942
|
-
function buildBreakdown(
|
|
2082
|
+
function buildBreakdown(scan, sources, base, o) {
|
|
2083
|
+
const allRecords = scan.records;
|
|
1943
2084
|
if (o.by === "model") {
|
|
1944
2085
|
const rows2 = [];
|
|
1945
2086
|
for (const source of sources) {
|
|
@@ -1947,11 +2088,16 @@ function buildBreakdown(allRecords, sources, base, o) {
|
|
|
1947
2088
|
const filtered = allRecords.filter((r) => withinSrcAndFilters(r, opts, o));
|
|
1948
2089
|
const models = aggregateByModel(filtered, opts);
|
|
1949
2090
|
const provider = getProvider(source);
|
|
2091
|
+
const turnsByModel = /* @__PURE__ */ new Map();
|
|
2092
|
+
for (const tn of summarizeTurns(filtered, scan.userRecords, scan.parentMap).values()) {
|
|
2093
|
+
turnsByModel.set(tn.firstModel, (turnsByModel.get(tn.firstModel) ?? 0) + 1);
|
|
2094
|
+
}
|
|
1950
2095
|
for (const m of models) {
|
|
1951
2096
|
rows2.push({
|
|
1952
2097
|
key: `${source}::${m.model}`,
|
|
1953
2098
|
label: provider.shortenModel(m.model),
|
|
1954
2099
|
requests: m.requests,
|
|
2100
|
+
turns: turnsByModel.get(m.model) ?? 0,
|
|
1955
2101
|
tokens: m.totalTokens,
|
|
1956
2102
|
cost: m.cost,
|
|
1957
2103
|
share: 0,
|
|
@@ -1968,11 +2114,17 @@ function buildBreakdown(allRecords, sources, base, o) {
|
|
|
1968
2114
|
const opts = { ...base, source };
|
|
1969
2115
|
const filtered = allRecords.filter((r) => withinSrcAndFilters(r, opts, o));
|
|
1970
2116
|
const projects = aggregateByProject(filtered, opts);
|
|
2117
|
+
const turnsByCwd = /* @__PURE__ */ new Map();
|
|
2118
|
+
for (const tn of summarizeTurns(filtered, scan.userRecords, scan.parentMap).values()) {
|
|
2119
|
+
const key = tn.cwd || "(unknown)";
|
|
2120
|
+
turnsByCwd.set(key, (turnsByCwd.get(key) ?? 0) + 1);
|
|
2121
|
+
}
|
|
1971
2122
|
for (const p of projects) {
|
|
1972
2123
|
rows2.push({
|
|
1973
2124
|
key: `${source}::${p.cwd}`,
|
|
1974
2125
|
label: p.projectName,
|
|
1975
2126
|
requests: p.requests,
|
|
2127
|
+
turns: turnsByCwd.get(p.cwd) ?? 0,
|
|
1976
2128
|
tokens: p.totalTokens,
|
|
1977
2129
|
cost: p.cost,
|
|
1978
2130
|
share: 0,
|
|
@@ -1987,15 +2139,23 @@ function buildBreakdown(allRecords, sources, base, o) {
|
|
|
1987
2139
|
const opts = { ...base, source };
|
|
1988
2140
|
const filtered = allRecords.filter((r) => withinSrcAndFilters(r, opts, o));
|
|
1989
2141
|
const sessions = aggregateBySession(filtered, [], opts);
|
|
2142
|
+
const turnsBySession = /* @__PURE__ */ new Map();
|
|
2143
|
+
for (const tn of summarizeTurns(filtered, scan.userRecords, scan.parentMap).values()) {
|
|
2144
|
+
turnsBySession.set(tn.sessionId, (turnsBySession.get(tn.sessionId) ?? 0) + 1);
|
|
2145
|
+
}
|
|
1990
2146
|
for (const s of sessions) {
|
|
1991
2147
|
rows.push({
|
|
1992
2148
|
key: `${source}::${s.sessionId}`,
|
|
1993
2149
|
label: s.title ?? s.sessionId.slice(0, 8),
|
|
1994
2150
|
requests: s.requests,
|
|
2151
|
+
turns: turnsBySession.get(s.sessionId) ?? 0,
|
|
1995
2152
|
tokens: s.totalTokens,
|
|
1996
2153
|
cost: s.cost,
|
|
1997
2154
|
share: 0,
|
|
1998
|
-
|
|
2155
|
+
// Worktree-aware so the breakdown reads
|
|
2156
|
+
// `ai-self-web (playwright)` instead of just `playwright`,
|
|
2157
|
+
// matching the dashboard's usage / sessions tables.
|
|
2158
|
+
sub: s.projectLabel
|
|
1999
2159
|
});
|
|
2000
2160
|
}
|
|
2001
2161
|
}
|
|
@@ -2097,21 +2257,15 @@ function renderText(d, o) {
|
|
|
2097
2257
|
]
|
|
2098
2258
|
];
|
|
2099
2259
|
if (t.reasoning > 0) {
|
|
2100
|
-
tokenRows.push([
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
"Total",
|
|
2110
|
-
c.bold(formatTokensCompact(t.total)),
|
|
2111
|
-
"Requests",
|
|
2112
|
-
t.requests.toLocaleString()
|
|
2113
|
-
]);
|
|
2114
|
-
}
|
|
2260
|
+
tokenRows.push(["Reasoning", c.dim(formatTokensCompact(t.reasoning)), "", ""]);
|
|
2261
|
+
}
|
|
2262
|
+
tokenRows.push(["Total", c.bold(formatTokensCompact(t.total)), "", ""]);
|
|
2263
|
+
tokenRows.push([
|
|
2264
|
+
"Convos",
|
|
2265
|
+
c.bold(t.turns.toLocaleString()),
|
|
2266
|
+
"Requests",
|
|
2267
|
+
t.requests.toLocaleString()
|
|
2268
|
+
]);
|
|
2115
2269
|
lines.push(renderPairedKv(tokenRows, c));
|
|
2116
2270
|
lines.push("");
|
|
2117
2271
|
lines.push(c.brand("\u25B8") + " " + c.bold("Cost"));
|
|
@@ -2147,16 +2301,17 @@ function renderText(d, o) {
|
|
|
2147
2301
|
lines.push(
|
|
2148
2302
|
c.brand("\u25B8") + " " + c.bold(`Top ${d.breakdown.length} ${dimLabel}s`) + " " + c.dim("(by cost)")
|
|
2149
2303
|
);
|
|
2150
|
-
const headers = ["#", dimLabel, "Reqs", "Tokens", "Cost", "Share"];
|
|
2304
|
+
const headers = ["#", dimLabel, "Convos", "Reqs", "Tokens", "Cost", "Share"];
|
|
2151
2305
|
const rows = d.breakdown.map((r, i) => [
|
|
2152
2306
|
String(i + 1),
|
|
2153
2307
|
truncate(r.label, 28),
|
|
2308
|
+
r.turns.toLocaleString(),
|
|
2154
2309
|
r.requests.toLocaleString(),
|
|
2155
2310
|
formatTokensCompact(r.tokens),
|
|
2156
2311
|
formatUSD(r.cost),
|
|
2157
2312
|
formatPct(r.share, 1)
|
|
2158
2313
|
]);
|
|
2159
|
-
lines.push(renderTable(headers, rows, c, [false, false, true, true, true, true]));
|
|
2314
|
+
lines.push(renderTable(headers, rows, c, [false, false, true, true, true, true, true]));
|
|
2160
2315
|
lines.push("");
|
|
2161
2316
|
}
|
|
2162
2317
|
return lines.join("\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccgauge",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Local web dashboard for Claude Code and OpenAI Codex CLI token usage and cost",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -41,7 +41,6 @@
|
|
|
41
41
|
"x64",
|
|
42
42
|
"arm64"
|
|
43
43
|
],
|
|
44
|
-
"packageManager": "pnpm@10.30.3",
|
|
45
44
|
"bin": {
|
|
46
45
|
"ccgauge": "bin/cli.mjs"
|
|
47
46
|
},
|
|
@@ -55,27 +54,6 @@
|
|
|
55
54
|
"CHANGELOG.md",
|
|
56
55
|
"LICENSE"
|
|
57
56
|
],
|
|
58
|
-
"scripts": {
|
|
59
|
-
"dev": "next dev -p 3738",
|
|
60
|
-
"build": "next build && node scripts/build-mcp.mjs && node scripts/build-report.mjs && node scripts/postbuild.mjs",
|
|
61
|
-
"build:mcp": "node scripts/build-mcp.mjs",
|
|
62
|
-
"build:report": "node scripts/build-report.mjs",
|
|
63
|
-
"start": "node bin/cli.mjs",
|
|
64
|
-
"start:next": "next start -p 3737",
|
|
65
|
-
"lint": "eslint .",
|
|
66
|
-
"typecheck": "tsc --noEmit",
|
|
67
|
-
"test": "node --experimental-strip-types --no-warnings scripts/test-codex-parser.mjs && node --experimental-strip-types --no-warnings scripts/test-turns.mjs && node --experimental-strip-types --no-warnings scripts/test-source-merge.mjs && node --experimental-strip-types --no-warnings scripts/test-cost-from-usage.mjs && node --experimental-strip-types --no-warnings scripts/test-range.mjs && node scripts/check-parser-versions.mjs && node scripts/check-readme-images.mjs",
|
|
68
|
-
"test:mcp": "node scripts/test-mcp-server.mjs",
|
|
69
|
-
"clean": "node -e \"for (const p of ['.next','node_modules','tsconfig.tsbuildinfo']) require('node:fs').rmSync(p,{recursive:true,force:true})\"",
|
|
70
|
-
"screenshots": "node scripts/screenshots.mjs",
|
|
71
|
-
"site:dev": "cd site && astro dev --port 4321",
|
|
72
|
-
"site:build": "cd site && astro build",
|
|
73
|
-
"site:preview": "cd site && astro preview --port 4322",
|
|
74
|
-
"site:check": "cd site && astro check",
|
|
75
|
-
"site:gen:placeholders": "node site/scripts/gen-placeholders.mjs",
|
|
76
|
-
"site:clean": "node -e \"for (const p of ['site/dist','site/.astro','site/node_modules']) require('node:fs').rmSync(p,{recursive:true,force:true})\"",
|
|
77
|
-
"prepack": "pnpm build"
|
|
78
|
-
},
|
|
79
57
|
"dependencies": {
|
|
80
58
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
81
59
|
"commander": "^13.1.0",
|
|
@@ -108,5 +86,25 @@
|
|
|
108
86
|
"tailwindcss": "^3.4.17",
|
|
109
87
|
"typescript": "^5.7.0",
|
|
110
88
|
"zod": "^3.24.1"
|
|
89
|
+
},
|
|
90
|
+
"scripts": {
|
|
91
|
+
"dev": "next dev -p 3738",
|
|
92
|
+
"build": "next build && node scripts/build-mcp.mjs && node scripts/build-report.mjs && node scripts/postbuild.mjs",
|
|
93
|
+
"build:mcp": "node scripts/build-mcp.mjs",
|
|
94
|
+
"build:report": "node scripts/build-report.mjs",
|
|
95
|
+
"start": "node bin/cli.mjs",
|
|
96
|
+
"start:next": "next start -p 3737",
|
|
97
|
+
"lint": "eslint .",
|
|
98
|
+
"typecheck": "tsc --noEmit",
|
|
99
|
+
"test": "node --experimental-strip-types --no-warnings scripts/test-codex-parser.mjs && node --experimental-strip-types --no-warnings scripts/test-turns.mjs && node --experimental-strip-types --no-warnings scripts/test-source-merge.mjs && node --experimental-strip-types --no-warnings scripts/test-cost-from-usage.mjs && node --experimental-strip-types --no-warnings scripts/test-range.mjs && node scripts/check-parser-versions.mjs && node scripts/check-readme-images.mjs",
|
|
100
|
+
"test:mcp": "node scripts/test-mcp-server.mjs",
|
|
101
|
+
"clean": "node -e \"for (const p of ['.next','node_modules','tsconfig.tsbuildinfo']) require('node:fs').rmSync(p,{recursive:true,force:true})\"",
|
|
102
|
+
"screenshots": "node scripts/screenshots.mjs",
|
|
103
|
+
"site:dev": "cd site && astro dev --port 4321",
|
|
104
|
+
"site:build": "cd site && astro build",
|
|
105
|
+
"site:preview": "cd site && astro preview --port 4322",
|
|
106
|
+
"site:check": "cd site && astro check",
|
|
107
|
+
"site:gen:placeholders": "node site/scripts/gen-placeholders.mjs",
|
|
108
|
+
"site:clean": "node -e \"for (const p of ['site/dist','site/.astro','site/node_modules']) require('node:fs').rmSync(p,{recursive:true,force:true})\""
|
|
111
109
|
}
|
|
112
|
-
}
|
|
110
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";exports.id=971,exports.ids=[971],exports.modules={24919:(a,b,c)=>{c.d(b,{TokenStackChart:()=>o});var d=c(21124),e=c(6077),f=c(72400),g=c(57495),h=c(59296),i=c(16803),j=c(11767),k=c(18311),l=c(15514),m=c(15519);let n={input:"rgb(var(--chart-input))",output:"rgb(var(--chart-output))",cacheRead:"rgb(var(--chart-cache-read))",cacheCreation:"rgb(var(--chart-cache-create))"};function o({data:a,height:b="h-72"}){let c=(0,m.kj)(),{locale:o}=(0,m.s9)();return a.length?(0,d.jsxs)("div",{className:`${b} w-full`,children:[(0,d.jsx)(e.u,{width:"100%",height:"100%",children:(0,d.jsxs)(f.E,{data:a,margin:{top:12,right:8,bottom:4,left:8},barCategoryGap:"22%",children:[(0,d.jsx)(g.d,{stroke:"rgb(var(--chart-grid))",strokeOpacity:.6,strokeDasharray:"3 3",vertical:!1}),(0,d.jsx)(h.W,{dataKey:"label",tick:{fill:"rgb(var(--chart-axis))",fontSize:11},tickLine:!1,axisLine:!1,interval:"preserveStartEnd",minTickGap:32,tickMargin:8}),(0,d.jsx)(i.h,{tickFormatter:a=>(0,l.jh)(Number(a),o),tick:{fill:"rgb(var(--chart-axis))",fontSize:11},tickLine:!1,axisLine:!1,width:56,tickMargin:4}),(0,d.jsx)(j.m,{content:(0,d.jsx)(q,{}),cursor:{fill:"rgb(var(--text-primary) / 0.05)",radius:4}}),(0,d.jsx)(k.y,{dataKey:"input",stackId:"a",fill:n.input,isAnimationActive:!1}),(0,d.jsx)(k.y,{dataKey:"cacheCreation",stackId:"a",fill:n.cacheCreation,isAnimationActive:!1}),(0,d.jsx)(k.y,{dataKey:"cacheRead",stackId:"a",fill:n.cacheRead,isAnimationActive:!1}),(0,d.jsx)(k.y,{dataKey:"output",stackId:"a",fill:n.output,radius:[4,4,0,0],isAnimationActive:!1})]})}),(0,d.jsxs)("div",{className:"flex items-center flex-wrap justify-center gap-4 text-xs text-text-secondary mt-2",children:[(0,d.jsx)(p,{color:n.input,label:c("chart.legend.input")}),(0,d.jsx)(p,{color:n.cacheCreation,label:c("chart.legend.cacheWrite")}),(0,d.jsx)(p,{color:n.cacheRead,label:c("chart.legend.cacheRead")}),(0,d.jsx)(p,{color:n.output,label:c("chart.legend.output")})]})]}):(0,d.jsx)("div",{className:`${b} flex items-center justify-center text-text-tertiary text-sm`,children:c("chart.empty")})}function p({color:a,label:b}){return(0,d.jsxs)("span",{className:"inline-flex items-center gap-1.5",children:[(0,d.jsx)("span",{className:"w-2.5 h-2.5 rounded-sm",style:{background:a}}),(0,d.jsx)("span",{children:b})]})}function q(a){let b=(0,m.kj)(),{locale:c}=(0,m.s9)();if(!a.active||!a.payload||!a.payload.length)return null;let e=a.payload[0].payload,f=e.input+e.output+e.cacheRead+e.cacheCreation;return(0,d.jsxs)("div",{className:"card-elevated border border-border-hi rounded-card p-3 text-xs min-w-[200px]",children:[(0,d.jsx)("div",{className:"font-medium text-text-primary mb-2",children:a.label}),(0,d.jsxs)("div",{className:"space-y-1",children:[(0,d.jsx)(r,{color:n.input,label:b("chart.legend.input"),value:e.input,locale:c}),(0,d.jsx)(r,{color:n.cacheCreation,label:b("chart.legend.cacheWrite"),value:e.cacheCreation,locale:c}),(0,d.jsx)(r,{color:n.cacheRead,label:b("chart.legend.cacheRead"),value:e.cacheRead,locale:c}),(0,d.jsx)(r,{color:n.output,label:b("chart.legend.output"),value:e.output,locale:c})]}),(0,d.jsxs)("div",{className:"mt-2 pt-2 border-t border-border flex items-center justify-between",children:[(0,d.jsx)("span",{className:"text-text-secondary",children:b("chart.tooltip.total")}),(0,d.jsx)("span",{className:"num-mono text-text-primary",children:(0,l.jh)(f,c)})]}),(0,d.jsxs)("div",{className:"flex items-center justify-between mt-1",children:[(0,d.jsx)("span",{className:"text-text-secondary",children:b("chart.tooltip.cost")}),(0,d.jsx)("span",{className:"num-mono text-brand",children:(0,l.az)(e.cost)})]}),(0,d.jsxs)("div",{className:"flex items-center justify-between mt-1",children:[(0,d.jsx)("span",{className:"text-text-secondary",children:b("chart.tooltip.requests")}),(0,d.jsx)("span",{className:"num-mono text-text-primary",children:e.requests})]})]})}function r({color:a,label:b,value:c,locale:e}){return(0,d.jsxs)("div",{className:"flex items-center justify-between gap-3",children:[(0,d.jsxs)("span",{className:"inline-flex items-center gap-1.5 text-text-secondary",children:[(0,d.jsx)("span",{className:"w-2 h-2 rounded-sm",style:{background:a}}),b]}),(0,d.jsx)("span",{className:"num-mono text-text-primary",children:(0,l.jh)(c,e)})]})}},37583:(a,b,c)=>{c.d(b,{pp:()=>h,qK:()=>g,wn:()=>f});var d=c(75338),e=c(95012);function f({title:a,desc:b,right:c,children:f,className:g,inlineDesc:h,fillBody:i}){return(0,d.jsxs)("section",{className:(0,e.cn)("card overflow-hidden",i&&"flex flex-col",g),children:[(a||c)&&(0,d.jsxs)("header",{className:(0,e.cn)("section-header","flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3 sm:gap-4","px-5 sm:px-6 pt-4 sm:pt-5 pb-3 border-b border-border"),children:[(0,d.jsxs)("div",{className:(0,e.cn)("min-w-0",h&&"sm:flex sm:items-baseline sm:gap-2.5"),children:[a&&(0,d.jsx)("h2",{className:"text-[15px] font-semibold text-text-primary tracking-tight leading-tight",children:a}),b&&(0,d.jsx)("p",{className:(0,e.cn)("text-xs text-text-secondary leading-relaxed",h?"mt-1 sm:mt-0":"mt-1.5"),children:b})]}),c&&(0,d.jsx)("div",{className:"flex items-center gap-2 flex-wrap",children:c})]}),(0,d.jsx)("div",{className:(0,e.cn)("p-5 sm:p-6",i&&"flex-1 flex flex-col"),children:f})]})}function g({title:a,desc:b,right:c,children:e}){return(0,d.jsxs)("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 py-6 sm:py-8 space-y-5 sm:space-y-6",children:[(0,d.jsxs)("div",{className:"flex flex-col sm:flex-row sm:items-end sm:justify-between gap-3 sm:gap-4",children:[(0,d.jsxs)("div",{className:"min-w-0",children:[(0,d.jsx)("h1",{className:"text-2xl sm:text-[1.75rem] font-semibold tracking-tight leading-tight truncate",children:a}),b&&(0,d.jsx)("p",{className:"text-sm text-text-secondary mt-1.5 leading-relaxed",children:b})]}),c&&(0,d.jsx)("div",{className:"flex items-center gap-2 flex-wrap",children:c})]}),e]})}function h({title:a,desc:b,icon:c,action:e}){return(0,d.jsxs)("div",{className:"card text-center py-14 sm:py-16 px-6 flex flex-col items-center gap-3",children:[(0,d.jsx)("div",{className:"w-12 h-12 rounded-full bg-bg-surface-hi border border-border flex items-center justify-center text-text-tertiary",children:c??(0,d.jsx)(i,{})}),(0,d.jsx)("div",{className:"text-base font-medium text-text-primary",children:a}),b&&(0,d.jsx)("div",{className:"text-sm text-text-tertiary max-w-md leading-relaxed",children:b}),e&&(0,d.jsx)("div",{className:"mt-2",children:e})]})}function i(){return(0,d.jsxs)("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[(0,d.jsx)("circle",{cx:"12",cy:"12",r:"9"}),(0,d.jsx)("path",{d:"M9 9h.01M15 9h.01M9 15c1 1 2 1.5 3 1.5s2-.5 3-1.5"})]})}},50160:(a,b,c)=>{c.d(b,{SI:()=>e});var d=c(60704);function e(a){let b=(0,d.sO)(a.source),{pricing:c}=b.resolvePricing(a.model);return b.costFromUsage(a.usage,c)}},67803:(a,b,c)=>{c.d(b,{$M:()=>p,OJ:()=>n,a3:()=>i,a6:()=>l,a9:()=>m,bo:()=>h,pb:()=>o});var d=c(50160),e=c(60704),f=c(95012);let g=["hour","day","week","month"];function h(a){return"string"==typeof a&&g.includes(a)}function i(a,b){let c=new Date(a),d=c.getFullYear(),e=String(c.getMonth()+1).padStart(2,"0"),f=String(c.getDate()).padStart(2,"0"),g=String(c.getHours()).padStart(2,"0");if("hour"===b)return{key:`${d}-${e}-${f}T${g}`,label:`${e}/${f} ${g}:00`};if("day"===b)return{key:`${d}-${e}-${f}`,label:`${e}/${f}`};if("week"===b){let a=new Date(c),b=a.getDay()||7;a.setDate(a.getDate()-b+1);let d=String(a.getMonth()+1).padStart(2,"0"),e=String(a.getDate()).padStart(2,"0");return{key:`${a.getFullYear()}-W${d}${e}`,label:`Wk ${d}/${e}`}}return{key:`${d}-${e}`,label:`${d}-${e}`}}function j(a){return{source:a.source,fromIso:a.from?.toISOString(),toIso:a.to?.toISOString(),models:a.models&&a.models.length?new Set(a.models):void 0,projects:a.projects&&a.projects.length?new Set(a.projects):void 0}}function k(a,b){return a.source===b.source&&(!b.fromIso||!(a.timestamp<b.fromIso))&&(!b.toIso||!(a.timestamp>b.toIso))&&(!b.models||!!b.models.has(a.model))&&(!b.projects||!!b.projects.has(a.cwd))}function l(a,b,c){let e=new Map,f=j(c);for(let c of a){if(!k(c,f))continue;let{key:a,label:g}=i(c.timestamp,b),h=e.get(a);h||(h={key:a,label:g,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,totalTokens:0,cost:0,saved:0,requests:0,models:{}},e.set(a,h)),function(a,b){let c=(0,d.SI)(b);a.inputTokens+=b.usage.input_tokens,a.outputTokens+=b.usage.output_tokens,a.cacheReadTokens+=b.usage.cache_read_input_tokens,a.cacheCreationTokens+=b.usage.cache_creation_input_tokens,a.totalTokens=a.inputTokens+a.outputTokens+a.cacheReadTokens+a.cacheCreationTokens,a.cost+=c.total,a.saved+=c.saved,a.requests+=1;let e=a.models[b.model]??{tokens:0,cost:0,requests:0};e.tokens+=b.usage.input_tokens+b.usage.output_tokens+b.usage.cache_read_input_tokens+b.usage.cache_creation_input_tokens,e.cost+=c.total,e.requests+=1,a.models[b.model]=e}(h,c)}return Array.from(e.values()).sort((a,b)=>a.key.localeCompare(b.key))}function m(a,b){let c=new Map,f=j(b);for(let b of a){if(!k(b,f))continue;let a=c.get(b.model);if(!a){let{pricing:d,matchType:f}=(0,e.sO)(b.source).resolvePricing(b.model);a={model:b.model,source:b.source,requests:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,totalTokens:0,cost:0,saved:0,pricing:d,pricingResolved:"exact"===f||"date-stripped"===f||"prefix-stripped"===f},c.set(b.model,a)}let g=(0,d.SI)(b);a.requests+=1,a.inputTokens+=b.usage.input_tokens,a.outputTokens+=b.usage.output_tokens,a.cacheReadTokens+=b.usage.cache_read_input_tokens,a.cacheCreationTokens+=b.usage.cache_creation_input_tokens,a.totalTokens=a.inputTokens+a.outputTokens+a.cacheReadTokens+a.cacheCreationTokens,a.cost+=g.total,a.saved+=g.saved}return Array.from(c.values()).sort((a,b)=>b.cost-a.cost)}function n(a,b){let c=new Map,e=new Map,g=j(b);for(let h of a){if(!k(h,g))continue;let a=h.cwd||"(unknown)",i=c.get(a);i||(i={source:b.source,cwd:a,projectName:(0,f.PJ)(a),sessions:0,requests:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,totalTokens:0,cost:0,saved:0,firstActivity:h.timestamp,lastActivity:h.timestamp,models:[]},c.set(a,i),e.set(a,new Set)),e.get(a).add(h.sessionId);let j=(0,d.SI)(h);i.requests+=1,i.inputTokens+=h.usage.input_tokens,i.outputTokens+=h.usage.output_tokens,i.cacheReadTokens+=h.usage.cache_read_input_tokens,i.cacheCreationTokens+=h.usage.cache_creation_input_tokens,i.totalTokens=i.inputTokens+i.outputTokens+i.cacheReadTokens+i.cacheCreationTokens,i.cost+=j.total,i.saved+=j.saved,h.timestamp<i.firstActivity&&(i.firstActivity=h.timestamp),h.timestamp>i.lastActivity&&(i.lastActivity=h.timestamp),i.models.includes(h.model)||i.models.push(h.model)}for(let[a,b]of e)c.get(a).sessions=b.size;return Array.from(c.values()).sort((a,b)=>b.cost-a.cost)}function o(a,b,c){let e=new Map,g=j(c);for(let b of a){if(!k(b,g))continue;let a=b.sessionId||b.uuid,c=e.get(a);c||(c={sessionId:a,source:b.source,cwd:b.cwd,projectName:(0,f.PJ)(b.cwd),startTime:b.timestamp,endTime:b.timestamp,durationMs:0,requests:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,totalTokens:0,cost:0,saved:0,models:[],modelBreakdown:{}},e.set(a,c));let h=(0,d.SI)(b);c.requests+=1,c.inputTokens+=b.usage.input_tokens,c.outputTokens+=b.usage.output_tokens,c.cacheReadTokens+=b.usage.cache_read_input_tokens,c.cacheCreationTokens+=b.usage.cache_creation_input_tokens,c.totalTokens=c.inputTokens+c.outputTokens+c.cacheReadTokens+c.cacheCreationTokens,c.cost+=h.total,c.saved+=h.saved,b.timestamp<c.startTime&&(c.startTime=b.timestamp),b.timestamp>c.endTime&&(c.endTime=b.timestamp),c.models.includes(b.model)||c.models.push(b.model);let i=c.modelBreakdown[b.model]??{tokens:0,cost:0,requests:0};i.tokens+=b.usage.input_tokens+b.usage.output_tokens+b.usage.cache_read_input_tokens+b.usage.cache_creation_input_tokens,i.cost+=h.total,i.requests+=1,c.modelBreakdown[b.model]=i}let h=new Map;for(let a of b){let b=h.get(a.sessionId);(!b||a.timestamp<b.timestamp)&&h.set(a.sessionId,a)}for(let a of e.values()){a.durationMs=Math.max(0,new Date(a.endTime).getTime()-new Date(a.startTime).getTime());let b=h.get(a.sessionId);b&&b.textPreview&&(a.firstUserMessage=b.textPreview,a.title=b.textPreview.slice(0,80))}return Array.from(e.values()).sort((a,b)=>b.endTime.localeCompare(a.endTime))}function p(a,b){let c=0,e=0,f=0,g=0,h=0,i=0,l=0,m=j(b);for(let b of a){if(!k(b,m))continue;let a=(0,d.SI)(b);c+=b.usage.input_tokens,e+=b.usage.output_tokens,f+=b.usage.cache_read_input_tokens,g+=b.usage.cache_creation_input_tokens,h+=a.total,i+=a.saved,l+=1}return{inputTokens:c,outputTokens:e,cacheReadTokens:f,cacheCreationTokens:g,totalTokens:c+e+f+g,cost:h,saved:i,requests:l}}}};
|
|
File without changes
|
|
File without changes
|