ccgauge 1.0.2 → 1.0.4
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 +36 -36
- 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_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/blocks/route.js +1 -1
- package/.next/standalone/.next/server/app/api/blocks/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/blocks/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/export/usage/route.js +1 -1
- package/.next/standalone/.next/server/app/api/export/usage/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/export/usage/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/pricing/route_client-reference-manifest.js +1 -1
- 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/projects/route_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/api/scan/route.js +1 -1
- package/.next/standalone/.next/server/app/api/scan/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scan/route_client-reference-manifest.js +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/sessions/route_client-reference-manifest.js +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/api/usage/route_client-reference-manifest.js +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/models/page_client-reference-manifest.js +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/page_client-reference-manifest.js +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/[id]/page_client-reference-manifest.js +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/projects/page_client-reference-manifest.js +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/[id]/page_client-reference-manifest.js +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/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page.js +1 -1
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.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/usage/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +9 -9
- package/.next/standalone/.next/server/chunks/517.js +1 -1
- package/.next/standalone/.next/server/chunks/567.js +2 -2
- package/.next/standalone/.next/server/chunks/971.js +1 -1
- package/.next/standalone/.next/server/chunks/98.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/.next/static/chunks/148-6c2eaf5508bfe739.js +1 -0
- package/.next/standalone/.next/static/chunks/930-ca5c6f8b5cb6ac3d.js +1 -0
- package/.next/standalone/.next/static/chunks/app/layout-4f3538437c5e8366.js +1 -0
- package/.next/standalone/.next/static/chunks/app/page-3cda7f70ecf5017a.js +1 -0
- package/.next/standalone/.next/static/chunks/app/settings/page-1ba7c4a4c0fae2f8.js +1 -0
- package/.next/standalone/.next/static/css/{406e067663b8b429.css → fbd2c395e5bf32cb.css} +1 -1
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-darwin-arm64/LICENSE +191 -0
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-darwin-arm64/lib/sharp-darwin-arm64.node +0 -0
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-darwin-arm64/package.json +40 -0
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64/lib/index.js +1 -0
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64/lib/libvips-cpp.8.17.3.dylib +0 -0
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64/package.json +36 -0
- package/.next/standalone/node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64/versions.json +30 -0
- package/.next/standalone/node_modules/next/node_modules/postcss/package.json +0 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/channel.js +177 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/colour.js +195 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/composite.js +212 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/constructor.js +499 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/index.js +16 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/input.js +809 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/is.js +143 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/libvips.js +207 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/operation.js +1016 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/output.js +1666 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/resize.js +595 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/sharp.js +121 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/lib/utility.js +291 -0
- package/.next/standalone/node_modules/next/node_modules/sharp/package.json +202 -0
- package/.next/standalone/node_modules/semver/classes/comparator.js +143 -0
- package/.next/standalone/node_modules/semver/classes/range.js +557 -0
- package/.next/standalone/node_modules/semver/classes/semver.js +333 -0
- package/.next/standalone/node_modules/semver/functions/cmp.js +54 -0
- package/.next/standalone/node_modules/semver/functions/coerce.js +62 -0
- package/.next/standalone/node_modules/semver/functions/compare.js +7 -0
- package/.next/standalone/node_modules/semver/functions/eq.js +5 -0
- package/.next/standalone/node_modules/semver/functions/gt.js +5 -0
- package/.next/standalone/node_modules/semver/functions/gte.js +5 -0
- package/.next/standalone/node_modules/semver/functions/lt.js +5 -0
- package/.next/standalone/node_modules/semver/functions/lte.js +5 -0
- package/.next/standalone/node_modules/semver/functions/neq.js +5 -0
- package/.next/standalone/node_modules/semver/functions/parse.js +18 -0
- package/.next/standalone/node_modules/semver/functions/satisfies.js +12 -0
- package/.next/standalone/node_modules/semver/internal/constants.js +37 -0
- package/.next/standalone/node_modules/semver/internal/debug.js +11 -0
- package/.next/standalone/node_modules/semver/internal/identifiers.js +29 -0
- package/.next/standalone/node_modules/semver/internal/lrucache.js +42 -0
- package/.next/standalone/node_modules/semver/internal/parse-options.js +17 -0
- package/.next/standalone/node_modules/semver/internal/re.js +223 -0
- package/.next/standalone/node_modules/semver/package.json +78 -0
- package/.next/standalone/package.json +13 -2
- package/.next/standalone/public/favicon.svg +19 -5
- package/CHANGELOG.md +212 -0
- package/README.md +21 -6
- package/README.zh-CN.md +24 -8
- package/bin/cli.mjs +47 -18
- package/dist/mcp/server.mjs +40 -23699
- package/dist/report/index.mjs +49 -18
- package/package.json +29 -16
- package/.next/standalone/.next/server/chunks/155.js +0 -1
- package/.next/standalone/.next/static/chunks/148-0a1e1b0207b89e3f.js +0 -1
- package/.next/standalone/.next/static/chunks/930-3035d0b294080d0b.js +0 -1
- package/.next/standalone/.next/static/chunks/app/layout-2512ccdfb13aeb17.js +0 -1
- package/.next/standalone/.next/static/chunks/app/page-19d3e77d4aa35a63.js +0 -1
- package/.next/standalone/.next/static/chunks/app/settings/page-cfeb089549c94f88.js +0 -1
- /package/.next/standalone/.next/static/{4YjiQrRI-CsVEPC1UOUEJ → ir1LZCnQKkiNUVXLprtzh}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{4YjiQrRI-CsVEPC1UOUEJ → ir1LZCnQKkiNUVXLprtzh}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -38,8 +38,9 @@ Everything runs locally as a Next.js app. Your conversation transcripts never le
|
|
|
38
38
|
## Highlights
|
|
39
39
|
|
|
40
40
|
### Cross-provider analytics
|
|
41
|
-
- One dashboard for both **Claude Code** and **OpenAI Codex CLI**
|
|
42
|
-
- Toggle data source from the nav bar; URL persists via `?source=`, last choice cached in cookie
|
|
41
|
+
- One dashboard for both **Claude Code** and **OpenAI Codex CLI**, plus an **All view** that merges the two
|
|
42
|
+
- Toggle data source from the nav bar (Claude · Codex · All), each button rendered with the real provider logo; URL persists via `?source=`, last choice cached in cookie
|
|
43
|
+
- **Worktree-aware Projects** — all worktrees of the same repo collapse into a single project row
|
|
43
44
|
- Built-in **provider adapter layer** (`lib/providers/`) — adding a third CLI (Gemini CLI, Cursor, Aider, …) is one new file plus a single registry line
|
|
44
45
|
|
|
45
46
|
### At-a-glance KPIs
|
|
@@ -51,7 +52,7 @@ Everything runs locally as a Next.js app. Your conversation transcripts never le
|
|
|
51
52
|
- **Sessions** — per-conversation list with model / tokens / cost / duration, plus a message-level timeline
|
|
52
53
|
- **Projects** — per-`cwd` aggregation cards with sparkline and spend share
|
|
53
54
|
- **Models** — side-by-side comparison: cost share, tokens share, cache hit, USD pricing
|
|
54
|
-
- **Usage** — turn-grouped table with expandable tool-call breakdown, CSV export
|
|
55
|
+
- **Usage** — turn-grouped table with expandable tool-call breakdown, CSV export. **Tokens / Conversations** toggle on the trend chart so you can count rows the way the usage table counts them
|
|
55
56
|
|
|
56
57
|
### Cost transparency
|
|
57
58
|
- **Cache savings** is its own KPI — quantifies how much Anthropic prompt caching saved you vs. paying full input price
|
|
@@ -63,6 +64,17 @@ Everything runs locally as a Next.js app. Your conversation transcripts never le
|
|
|
63
64
|
- **English / 中文** (cookie + localStorage)
|
|
64
65
|
- Filters: time range (today / 7d / 30d / 90d / all), granularity (hour / day / week / month), model and project multi-select
|
|
65
66
|
|
|
67
|
+
### CLI report (no server)
|
|
68
|
+
- `ccgauge report` prints a colored, aligned terminal usage report in ~0.2 s from the same JSONL the dashboard reads
|
|
69
|
+
- `--range / --source / --by / --since / --until / --model / --project` filters
|
|
70
|
+
- `--json` for machine-readable output; `--no-color` auto-applied when piped — drops cleanly into shell scripts and CI
|
|
71
|
+
|
|
72
|
+
### MCP server (for LLMs)
|
|
73
|
+
- `ccgauge mcp` runs a stdio JSON-RPC server so **Claude Desktop / Cursor / Cline** can query your local usage directly
|
|
74
|
+
- Nine MCP tools: `usage_summary`, `usage_by_time`, `usage_by_model`, `usage_by_project`, `usage_by_session`, `daily_summary`, `weekly_summary`, `recent_activity`, `cost_estimator`
|
|
75
|
+
- Reasoning-token breakdown surfaced for the models that emit one
|
|
76
|
+
- Separate named cache (`index-mcp-v2.json`) so MCP runs don't contend with the dashboard
|
|
77
|
+
|
|
66
78
|
### Privacy by design
|
|
67
79
|
- 100 % local: read-only access to existing JSONL files, zero outbound network calls
|
|
68
80
|
- Open source, MIT-licensed
|
|
@@ -217,6 +229,7 @@ English with real numbers from your machine.
|
|
|
217
229
|
| `daily_summary` | "What did I do today / yesterday / Monday / on YYYY-MM-DD?" Sessions grouped by project + models + top tool calls. |
|
|
218
230
|
| `weekly_summary` | 7-day roll-up: per-day cost trend, top sessions, top projects, models. `week_offset=-1` for last week. |
|
|
219
231
|
| `recent_activity` | The N most recently active sessions across providers. |
|
|
232
|
+
| `cost_estimator` | Compute the USD cost of a hypothetical request (`{ source, model, input_tokens, output_tokens, cache_* }`). Uses built-in per-1M-token pricing; does NOT consult usage history. |
|
|
220
233
|
|
|
221
234
|
| Resource URI | Content |
|
|
222
235
|
| --- | --- |
|
|
@@ -334,8 +347,8 @@ debug "why did it answer X".
|
|
|
334
347
|
→ `daily_summary({ date: "yesterday" })`
|
|
335
348
|
- *"Generate a Monday stand-up bullet list of what I shipped last week."*
|
|
336
349
|
→ `weekly_summary({ week_offset: -1 })`
|
|
337
|
-
- *"Which 3 projects have I touched most in the last
|
|
338
|
-
→ `usage_by_project({
|
|
350
|
+
- *"Which 3 projects have I touched most in the last two weeks?"*
|
|
351
|
+
→ `usage_by_project({ from: "2026-05-01", to: "2026-05-15", limit: 3 })` — pass explicit `from`/`to` for any window not covered by the named ranges (`7d` / `30d` / `90d` / `this_week` / `last_week` / …).
|
|
339
352
|
- *"What was my last coding session about?"*
|
|
340
353
|
→ `recent_activity({ limit: 1 })`
|
|
341
354
|
|
|
@@ -351,7 +364,7 @@ debug "why did it answer X".
|
|
|
351
364
|
- *"At my current burn rate, how much will I spend this month?"*
|
|
352
365
|
→ `usage_summary({ range: "this_month" })` + `usage_by_time({ range: "this_month", granularity: "day" })` — LLM extrapolates.
|
|
353
366
|
- *"If I run another 200K input + 50K output on Opus 4.7 today, what does that add to my month-to-date cost?"*
|
|
354
|
-
→ `usage_summary({ range: "this_month" })`
|
|
367
|
+
→ `cost_estimator({ source: "claude", model: "claude-opus-4-7", input_tokens: 200000, output_tokens: 50000 })` + `usage_summary({ range: "this_month" })` — the estimator returns the dollar cost for the hypothetical request without touching your usage history.
|
|
355
368
|
|
|
356
369
|
#### Cross-source comparisons
|
|
357
370
|
|
|
@@ -448,6 +461,8 @@ pnpm test # codex parser smoke test (Node 22+)
|
|
|
448
461
|
pnpm build # next build + copy static into .next/standalone
|
|
449
462
|
pnpm start # run bin/cli.mjs against the standalone build
|
|
450
463
|
pnpm screenshots # regenerate docs/screenshots/*.png
|
|
464
|
+
pnpm site:dev # marketing site dev server, http://localhost:4321
|
|
465
|
+
pnpm site:build # build only the site/ marketing site
|
|
451
466
|
pnpm clean # rm -rf .next node_modules
|
|
452
467
|
```
|
|
453
468
|
|
package/README.zh-CN.md
CHANGED
|
@@ -38,8 +38,9 @@ npx ccgauge
|
|
|
38
38
|
## 亮点
|
|
39
39
|
|
|
40
40
|
### 多 CLI 数据源
|
|
41
|
-
- 一份看板覆盖 **Claude Code** 与 **OpenAI Codex CLI**
|
|
42
|
-
-
|
|
41
|
+
- 一份看板覆盖 **Claude Code** 与 **OpenAI Codex CLI**,并提供 **All 视图**把两者合并查看
|
|
42
|
+
- 顶部三档切换(Claude · Codex · All),每个按钮都带真品牌 logo;URL 用 `?source=` 持久化,cookie 记忆上次选择
|
|
43
|
+
- **Worktree 感知的 Projects 合并** —— 同一个 repo 的所有 worktree 自动并到同一个项目行
|
|
43
44
|
- 内置 **Provider 适配层**(`lib/providers/`)—— 增加第三个 CLI(Gemini CLI / Cursor / Aider …)只需一个新文件加注册表一行
|
|
44
45
|
|
|
45
46
|
### KPI 一眼看完
|
|
@@ -51,7 +52,7 @@ npx ccgauge
|
|
|
51
52
|
- **会话页** —— 每场对话单独成行(模型 / token / 花费 / 时长),点进去看消息级时间线
|
|
52
53
|
- **项目页** —— 按 `cwd` 聚合成卡片网格,含趋势条与花费占比
|
|
53
54
|
- **模型页** —— 各模型并排对比:成本占比、token 占比、缓存命中、官方单价
|
|
54
|
-
- **用量页** —— 按对话轮次分组的明细表,可展开看每次工具调用,支持 CSV
|
|
55
|
+
- **用量页** —— 按对话轮次分组的明细表,可展开看每次工具调用,支持 CSV 导出。趋势图支持 **Token / 对话数** 切换,让条形图行数和用量表 1:1 对齐
|
|
55
56
|
|
|
56
57
|
### 成本透明
|
|
57
58
|
- **缓存节省** 单独成卡 —— 量化 Anthropic prompt caching 实际帮你节省了多少美元
|
|
@@ -63,6 +64,17 @@ npx ccgauge
|
|
|
63
64
|
- **English / 中文** 双语,cookie + localStorage 双向同步
|
|
64
65
|
- 完整筛选:时间区间(今天 / 7 天 / 30 天 / 90 天 / 全部)、粒度(小时 / 天 / 周 / 月)、模型 / 项目 multi-select
|
|
65
66
|
|
|
67
|
+
### 命令行报告(无 server)
|
|
68
|
+
- `ccgauge report` 读取同一份 JSONL,在 ~0.2 秒内打出彩色对齐的终端报告
|
|
69
|
+
- `--range / --source / --by / --since / --until / --model / --project` 滤波参数
|
|
70
|
+
- `--json` 输出给脚本;`--no-color` 走管道时自动开启 —— 可以直接塞进 shell 和 CI
|
|
71
|
+
|
|
72
|
+
### MCP 服务(给 LLM 用)
|
|
73
|
+
- `ccgauge mcp` 起一个 stdio JSON-RPC 服务,让 **Claude Desktop / Cursor / Cline** 等 MCP 客户端直接查你本地的 ccgauge 历史
|
|
74
|
+
- 9 个 MCP tool:`usage_summary`、`usage_by_time`、`usage_by_model`、`usage_by_project`、`usage_by_session`、`daily_summary`、`weekly_summary`、`recent_activity`、`cost_estimator`
|
|
75
|
+
- 支持模型有 reasoning token 时单独折算
|
|
76
|
+
- 独立命名缓存(`index-mcp-v2.json`),MCP 进程不会和仪表盘抢同一份磁盘索引
|
|
77
|
+
|
|
66
78
|
### 隐私优先
|
|
67
79
|
- 100 % 本地:只读访问已有 JSONL 文件,零外网调用
|
|
68
80
|
- 开源,MIT 协议
|
|
@@ -213,7 +225,8 @@ LLM 会自动选合适的 tool、本地调用、用大白话给你带真实数
|
|
|
213
225
|
| `usage_by_session` | 会话列表,含标题(首条用户消息)/ 模型 / 时长 / 花费。可按 recent / cost / tokens / duration 排序。 |
|
|
214
226
|
| `daily_summary` | "今天 / 昨天 / 周一 / YYYY-MM-DD 我都干了啥?" 按项目分组的会话 + 模型 + top 工具调用。 |
|
|
215
227
|
| `weekly_summary` | 7 天 roll-up:每日花费趋势 + top 会话 + top 项目 + 模型分布。`week_offset=-1` 看上周。 |
|
|
216
|
-
| `recent_activity` | 最近 N
|
|
228
|
+
| `recent_activity` | 最近 N 条活跃会话(默认最近 30 天,可显式给 `from`/`to`)。 |
|
|
229
|
+
| `cost_estimator` | 计算"假设我用 X 模型发 N 个 token 要花多少钱"。直接读内置 per-1M-token 单价表,不查历史。常用于额度规划 / what-if。 |
|
|
217
230
|
|
|
218
231
|
| Resource URI | 内容 |
|
|
219
232
|
| --- | --- |
|
|
@@ -329,7 +342,7 @@ LLM 大概率会调的工具——方便你"为什么会这样回答"反查。
|
|
|
329
342
|
- *"给我一份周一 standup 用的 bullet list,列我上周完成的事。"*
|
|
330
343
|
→ `weekly_summary({ week_offset: -1 })`
|
|
331
344
|
- *"过去两周我接触最多的 3 个项目是什么?"*
|
|
332
|
-
→ `usage_by_project({
|
|
345
|
+
→ `usage_by_project({ from: "2026-05-01", to: "2026-05-15", limit: 3 })`——非 `7d` / `30d` / `90d` / `this_week` / `last_week` 等命名窗口时,传显式 `from` / `to`。
|
|
333
346
|
- *"我最近一次的编码会话是关于什么的?"*
|
|
334
347
|
→ `recent_activity({ limit: 1 })`
|
|
335
348
|
|
|
@@ -345,7 +358,7 @@ LLM 大概率会调的工具——方便你"为什么会这样回答"反查。
|
|
|
345
358
|
- *"按当前消耗速度,本月预计花多少?"*
|
|
346
359
|
→ `usage_summary({ range: "this_month" })` + `usage_by_time({ range: "this_month", granularity: "day" })`——LLM 自己外推。
|
|
347
360
|
- *"如果我今天再在 Opus 4.7 上跑 200K input + 50K output,本月累计要多少?"*
|
|
348
|
-
→ `
|
|
361
|
+
→ `cost_estimator({ source: "claude", model: "claude-opus-4-7", input_tokens: 200000, output_tokens: 50000 })` + `usage_summary({ range: "this_month" })`——estimator 直接按内置单价表返回这笔假设请求的美元成本,不查历史。
|
|
349
362
|
|
|
350
363
|
#### 跨数据源对比
|
|
351
364
|
|
|
@@ -441,6 +454,8 @@ pnpm test # codex parser 烟测(Node 22+)
|
|
|
441
454
|
pnpm build # next build + 把 static 拷进 .next/standalone
|
|
442
455
|
pnpm start # 用 bin/cli.mjs 跑 standalone 产物
|
|
443
456
|
pnpm screenshots # 重新生成 docs/screenshots/*.png
|
|
457
|
+
pnpm site:dev # 产品官网开发服务,http://localhost:4321
|
|
458
|
+
pnpm site:build # 只构建 site/ 产品官网
|
|
444
459
|
pnpm clean # rm -rf .next node_modules
|
|
445
460
|
```
|
|
446
461
|
|
|
@@ -495,10 +510,11 @@ pnpm publish --access public # 会自动先跑 pnpm build(prepublishOnly)
|
|
|
495
510
|
## 产品官网
|
|
496
511
|
|
|
497
512
|
产品官网(Astro + Tailwind 自建、中英双语、暗 / 亮主题、独立部署)放在
|
|
498
|
-
[`site/`](./site/) 目录。它跟着主仓库一起在 git 里,但**不会**进 npm
|
|
513
|
+
[`site/`](./site/) 目录。它跟着主仓库一起在 git 里,但**不会**进 npm 包;
|
|
514
|
+
命令和依赖统一由根目录 `package.json` 管理。
|
|
499
515
|
|
|
500
516
|
```bash
|
|
501
|
-
|
|
517
|
+
pnpm site:dev # http://localhost:4321
|
|
502
518
|
```
|
|
503
519
|
|
|
504
520
|
构建 / 部署细节见 [`site/README.md`](./site/README.md)。
|
package/bin/cli.mjs
CHANGED
|
@@ -142,8 +142,9 @@ program
|
|
|
142
142
|
program
|
|
143
143
|
.command('mcp')
|
|
144
144
|
.description('start the MCP server (stdio) so LLMs can query usage data')
|
|
145
|
-
.
|
|
146
|
-
|
|
145
|
+
.option('--check', 'verify the bundle + indexer; print one line per provider and exit')
|
|
146
|
+
.action(async (opts) => {
|
|
147
|
+
await startMcp(opts);
|
|
147
148
|
});
|
|
148
149
|
|
|
149
150
|
function addReportOptions(cmd) {
|
|
@@ -438,7 +439,7 @@ or run the full build with
|
|
|
438
439
|
}
|
|
439
440
|
}
|
|
440
441
|
|
|
441
|
-
async function startMcp() {
|
|
442
|
+
async function startMcp(opts = {}) {
|
|
442
443
|
const bundle = join(packageRoot, 'dist', 'mcp', 'server.mjs');
|
|
443
444
|
if (!existsSync(bundle)) {
|
|
444
445
|
console.error(`
|
|
@@ -455,20 +456,39 @@ or run the full build with
|
|
|
455
456
|
`);
|
|
456
457
|
process.exit(1);
|
|
457
458
|
}
|
|
458
|
-
|
|
459
|
-
//
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
459
|
+
|
|
460
|
+
// --check: don't actually run the JSON-RPC server — load the bundle,
|
|
461
|
+
// boot the indexer, print one line per provider, and exit. Lets users
|
|
462
|
+
// verify their install without wiring up an MCP client.
|
|
463
|
+
if (opts.check) {
|
|
464
|
+
const mod = await import(pathToFileURL(bundle).href);
|
|
465
|
+
if (typeof mod.printCheck !== 'function') {
|
|
466
|
+
console.error('[ccgauge-mcp] this bundle was built without --check support');
|
|
467
|
+
process.exit(1);
|
|
468
|
+
}
|
|
469
|
+
const code = await mod.printCheck();
|
|
470
|
+
process.exit(typeof code === 'number' ? code : 0);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Run the bundled MCP server **in this process** — the bundle exposes a
|
|
474
|
+
// top-level `runStdioServer()` so we just import + invoke it. Spawning a
|
|
475
|
+
// second Node process here is wasted memory/latency (LLM clients already
|
|
476
|
+
// spawn `ccgauge mcp` per conversation), and forwarding signals across
|
|
477
|
+
// processes is brittle (e.g. SIGHUP isn't covered by the old shim).
|
|
478
|
+
try {
|
|
479
|
+
const mod = await import(pathToFileURL(bundle).href);
|
|
480
|
+
if (typeof mod.runStdioServer !== 'function') {
|
|
481
|
+
console.error('[ccgauge-mcp] bundle missing runStdioServer export');
|
|
482
|
+
process.exit(1);
|
|
483
|
+
}
|
|
484
|
+
await mod.runStdioServer();
|
|
485
|
+
// runStdioServer keeps the loop alive via the stdio transport; if it
|
|
486
|
+
// ever returns it means the transport closed cleanly.
|
|
487
|
+
process.exit(0);
|
|
488
|
+
} catch (err) {
|
|
489
|
+
console.error('[ccgauge-mcp] failed to start:', err?.message ?? err);
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
472
492
|
}
|
|
473
493
|
|
|
474
494
|
async function resolvePort(opts) {
|
|
@@ -567,9 +587,18 @@ async function readState() {
|
|
|
567
587
|
try {
|
|
568
588
|
const raw = await readFile(STATE_FILE, 'utf8');
|
|
569
589
|
const parsed = JSON.parse(raw);
|
|
570
|
-
if (!parsed || typeof parsed !== 'object') return null;
|
|
590
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return null;
|
|
571
591
|
// Treat unknown / future versions as stale (auto-clean on next stop/start).
|
|
572
592
|
if (parsed.version !== STATE_VERSION) return null;
|
|
593
|
+
// Type-guard the fields callers actually use — `stop`, `status`, and
|
|
594
|
+
// `restart` all assume these have the right shape. A hand-edited
|
|
595
|
+
// state.json with the right `version` but garbage in `pid` could
|
|
596
|
+
// otherwise crash `safeKill()` or `isProcessRunning()`.
|
|
597
|
+
if (typeof parsed.pid !== 'number' || !Number.isInteger(parsed.pid) || parsed.pid <= 0) {
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
if (typeof parsed.url !== 'string' || !parsed.url) return null;
|
|
601
|
+
if (typeof parsed.logFile !== 'string' || !parsed.logFile) return null;
|
|
573
602
|
return parsed;
|
|
574
603
|
} catch {
|
|
575
604
|
return null;
|