@kylindc/ccxray 1.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/CHANGELOG.md +39 -0
- package/LICENSE +21 -0
- package/README.ja.md +144 -0
- package/README.md +145 -0
- package/README.zh-TW.md +144 -0
- package/package.json +46 -0
- package/public/app.js +99 -0
- package/public/cost-budget-ui.js +305 -0
- package/public/entry-rendering.js +535 -0
- package/public/index.html +119 -0
- package/public/intercept-ui.js +335 -0
- package/public/keyboard-nav.js +208 -0
- package/public/messages.js +750 -0
- package/public/miller-columns.js +1686 -0
- package/public/quota-ticker.js +67 -0
- package/public/style.css +431 -0
- package/public/system-prompt-ui.js +327 -0
- package/server/auth.js +34 -0
- package/server/bedrock-credentials.js +141 -0
- package/server/config.js +190 -0
- package/server/cost-budget.js +220 -0
- package/server/cost-worker.js +110 -0
- package/server/eventstream.js +148 -0
- package/server/forward.js +683 -0
- package/server/helpers.js +393 -0
- package/server/hub.js +418 -0
- package/server/index.js +551 -0
- package/server/pricing.js +133 -0
- package/server/restore.js +141 -0
- package/server/routes/api.js +123 -0
- package/server/routes/costs.js +124 -0
- package/server/routes/intercept.js +89 -0
- package/server/routes/sse.js +44 -0
- package/server/sigv4.js +104 -0
- package/server/sse-broadcast.js +71 -0
- package/server/storage/index.js +36 -0
- package/server/storage/interface.js +26 -0
- package/server/storage/local.js +79 -0
- package/server/storage/s3.js +91 -0
- package/server/store.js +108 -0
- package/server/system-prompt.js +150 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Orphan hub detection**: When `hub.json` lockfile is missing but a hub is still running, clients now probe the default port and reconnect automatically instead of failing with EADDRINUSE
|
|
8
|
+
- **Browser auto-open**: First client connecting to a hub now opens the dashboard, regardless of whether the client forked the hub or discovered an existing one
|
|
9
|
+
- **ECONNRESET handling**: Upstream socket destruction mid-response no longer leaves the client hanging; added `proxyRes` error handler for both SSE and non-SSE paths
|
|
10
|
+
- **OOM on long-running hub**: In-memory entries capped at 5000 (configurable via `CCXRAY_MAX_ENTRIES`), oldest evicted first; disk logs unaffected
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `CCXRAY_MAX_ENTRIES` environment variable to configure in-memory entry limit (default: 5000)
|
|
15
|
+
- Hub status endpoint includes `app: 'ccxray'` marker for identity verification
|
|
16
|
+
- 57 new tests (98 → 155) covering proxy E2E, SSE streaming, intercept lifecycle, error paths, concurrency, and hub crash recovery
|
|
17
|
+
|
|
18
|
+
## 1.1.0
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Multi-project hub**: Multiple `ccxray claude` instances automatically share a single proxy server and dashboard. No configuration needed — the first instance starts a hub, subsequent ones connect to it.
|
|
23
|
+
- **`ccxray status`**: New subcommand showing hub info and connected clients.
|
|
24
|
+
- **Hub crash auto-recovery**: If the hub process dies, connected clients detect and restart it within ~5 seconds.
|
|
25
|
+
- **Version compatibility check**: Clients with different major versions are rejected with a clear error message.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- **Logs location**: Moved from `./logs/` (package-relative) to `~/.ccxray/logs/` (user home). Existing logs are automatically migrated on first run.
|
|
30
|
+
- **`--port` behavior**: Explicitly specifying `--port` now opts out of hub mode, running an independent server instead.
|
|
31
|
+
|
|
32
|
+
### Migration from 1.0.0
|
|
33
|
+
|
|
34
|
+
- Logs are automatically migrated from the old `logs/` directory to `~/.ccxray/logs/` on first startup. No manual action needed.
|
|
35
|
+
- If you use `AUTH_TOKEN`, hub discovery endpoints (`/_api/health`, `/_api/hub/*`) bypass authentication since they are local IPC.
|
|
36
|
+
|
|
37
|
+
## 1.0.0
|
|
38
|
+
|
|
39
|
+
Initial release.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Justin Lee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# ccxray
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [正體中文](README.zh-TW.md) | **日本語**
|
|
4
|
+
|
|
5
|
+
AIエージェントセッションのX線ビュー。ゼロ設定のHTTPプロキシで、Claude CodeとAnthropic API間のすべてのAPI呼び出しを記録し、エージェント内部で実際に何が起きているかを確認できるリアルタイムダッシュボードを提供します。
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## なぜ必要か
|
|
12
|
+
|
|
13
|
+
Claude Codeはブラックボックスです。以下が見えません:
|
|
14
|
+
- どんなシステムプロンプトを送信しているか(バージョン間の変更も含む)
|
|
15
|
+
- 各ツール呼び出しのコスト
|
|
16
|
+
- なぜ30秒も思考しているのか
|
|
17
|
+
- 200Kトークンのコンテキストウィンドウを何が消費しているのか
|
|
18
|
+
|
|
19
|
+
ccxrayはそれをガラス箱に変えます。
|
|
20
|
+
|
|
21
|
+
## クイックスタート
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @kylindc/ccxray claude
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
これだけです。プロキシが起動し、Claude Codeがプロキシ経由で接続し、ダッシュボードが自動的にブラウザで開きます。複数のターミナルで実行すると、自動的に一つのダッシュボードを共有します。
|
|
28
|
+
|
|
29
|
+
### その他の実行方法
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
ccxray # プロキシ + ダッシュボードのみ
|
|
33
|
+
ccxray claude --continue # すべてのclaude引数がそのまま渡される
|
|
34
|
+
ccxray --port 8080 claude # カスタムポート(独立モード、hub共有なし)
|
|
35
|
+
ccxray claude --no-browser # ブラウザの自動オープンをスキップ
|
|
36
|
+
ccxray status # hubの情報と接続中のクライアントを表示
|
|
37
|
+
ANTHROPIC_BASE_URL=http://localhost:5577 claude # 手動設定(既存セッション)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### マルチプロジェクト
|
|
41
|
+
|
|
42
|
+
複数のターミナルで `ccxray claude` を実行すると、自動的に単一のプロキシとダッシュボードを共有します。設定は不要です。
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Terminal 1
|
|
46
|
+
cd ~/project-a && ccxray claude # hubを起動 + claude
|
|
47
|
+
|
|
48
|
+
# Terminal 2
|
|
49
|
+
cd ~/project-b && ccxray claude # 既存のhubに接続
|
|
50
|
+
|
|
51
|
+
# 両プロジェクトが http://localhost:5577 のダッシュボードに表示
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Hubプロセスがクラッシュした場合、接続中のクライアントは数秒以内に自動的に復旧します。
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
$ ccxray status
|
|
58
|
+
Hub: http://localhost:5577 (pid 12345, uptime 3600s, v1.1.0)
|
|
59
|
+
Connected clients (2):
|
|
60
|
+
[1] pid 23456 — ~/dev/project-a
|
|
61
|
+
[2] pid 34567 — ~/dev/project-b
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`--port` を使用すると独立モードで実行できます。
|
|
65
|
+
|
|
66
|
+
## 機能
|
|
67
|
+
|
|
68
|
+
### タイムライン
|
|
69
|
+
|
|
70
|
+
エージェントの思考をリアルタイムで観察。各ターンを思考ブロック(所要時間付き)、ツール呼び出しのインラインプレビュー、アシスタント応答に分解します。
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
### 使用量とコスト
|
|
75
|
+
|
|
76
|
+
実際の支出を把握。セッションヒートマップ、消費レート、ROI計算 — トークンの行き先を正確に把握できます。
|
|
77
|
+
|
|
78
|
+

|
|
79
|
+
|
|
80
|
+
### システムプロンプト追跡
|
|
81
|
+
|
|
82
|
+
バージョンの自動検出とdiffビューア。Claude Codeのアップデートで何が変わったかを正確に把握 — プロンプトの変更を見逃しません。
|
|
83
|
+
|
|
84
|
+

|
|
85
|
+
|
|
86
|
+
### その他
|
|
87
|
+
|
|
88
|
+
- **セッション検出** — Claude Codeセッションごとに自動グループ化。プロジェクト/作業ディレクトリの抽出付き
|
|
89
|
+
- **トークン会計** — ターンごとの内訳:input/output/cache-read/cache-createトークン、USD単位のコスト、コンテキストウィンドウ使用率バー
|
|
90
|
+
|
|
91
|
+
## 仕組み
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Claude Code ──► ccxray (:5577) ──► api.anthropic.com
|
|
95
|
+
│
|
|
96
|
+
▼
|
|
97
|
+
~/.ccxray/logs/ (JSON)
|
|
98
|
+
│
|
|
99
|
+
▼
|
|
100
|
+
ダッシュボード(同じポート)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
ccxrayは透過型HTTPプロキシです。リクエストをそのままAnthropicに転送し、リクエストとレスポンスの両方をJSONファイルとして記録し、同じポートでWebダッシュボードを提供します。APIキーは不要です — Claude Codeが送信する内容をそのまま通過させます。
|
|
104
|
+
|
|
105
|
+
## 設定
|
|
106
|
+
|
|
107
|
+
### CLIフラグ
|
|
108
|
+
|
|
109
|
+
| フラグ | 説明 |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `--port <number>` | プロキシ + ダッシュボードのポート(デフォルト: 5577)。hub共有を無効化 |
|
|
112
|
+
| `--no-browser` | ダッシュボードをブラウザで自動オープンしない |
|
|
113
|
+
|
|
114
|
+
### 環境変数
|
|
115
|
+
|
|
116
|
+
| 変数 | デフォルト | 説明 |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `PROXY_PORT` | `5577` | プロキシ + ダッシュボードのポート(`--port`で上書き) |
|
|
119
|
+
| `BROWSER` | — | `none`に設定すると自動オープンを無効化 |
|
|
120
|
+
| `AUTH_TOKEN` | _(なし)_ | アクセス制御用APIキー(未設定時は無効) |
|
|
121
|
+
| `CCXRAY_HOME` | `~/.ccxray` | hubロックファイル、ログ、hub.logの基本ディレクトリ |
|
|
122
|
+
|
|
123
|
+
ログは`~/.ccxray/logs/`に`{timestamp}_req.json`と`{timestamp}_res.json`として保存されます。v1.0からアップグレードする場合、`./logs/`のログは初回起動時に自動的に移行されます。
|
|
124
|
+
|
|
125
|
+
## Docker
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
docker build -t ccxray .
|
|
129
|
+
docker run -p 5577:5577 ccxray
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## 要件
|
|
133
|
+
|
|
134
|
+
- Node.js 18+
|
|
135
|
+
|
|
136
|
+
## 作者の他のプロジェクト
|
|
137
|
+
|
|
138
|
+
- [SourceAtlas](https://sourceatlas.io/) — あらゆるコードベースへのマップ
|
|
139
|
+
- [AskRoundtable](https://github.com/AskRoundtable/expert-skills) — AIをMunger、Feynman、Paul Grahamのように思考させる
|
|
140
|
+
- Xで [@lis186](https://x.com/lis186) をフォローして最新情報をチェック
|
|
141
|
+
|
|
142
|
+
## ライセンス
|
|
143
|
+
|
|
144
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# ccxray
|
|
2
|
+
|
|
3
|
+
**English** | [正體中文](README.zh-TW.md) | [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
X-ray vision for AI agent sessions. A zero-config HTTP proxy that records every API call between Claude Code and Anthropic, with a real-time dashboard to inspect what's actually happening inside your agent.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
Claude Code is a black box. You can't see:
|
|
14
|
+
- What system prompts it sends (and how they change between versions)
|
|
15
|
+
- How much each tool call costs
|
|
16
|
+
- Why it's thinking for 30 seconds
|
|
17
|
+
- What context is eating your 200K token window
|
|
18
|
+
|
|
19
|
+
ccxray makes it a glass box.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @kylindc/ccxray claude
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
That's it. Proxy starts, Claude Code launches through it, and the dashboard opens automatically in your browser. Run it in multiple terminals — they automatically share one dashboard.
|
|
28
|
+
|
|
29
|
+
### Other ways to run
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
ccxray # Proxy + dashboard only
|
|
33
|
+
ccxray claude --continue # All claude args pass through
|
|
34
|
+
ccxray --port 8080 claude # Custom port (independent, no hub sharing)
|
|
35
|
+
ccxray claude --no-browser # Skip auto-open browser
|
|
36
|
+
ccxray status # Show hub info and connected clients
|
|
37
|
+
ANTHROPIC_BASE_URL=http://localhost:5577 claude # Manual setup (existing sessions)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Multi-project
|
|
41
|
+
|
|
42
|
+
Running `ccxray claude` in multiple terminals automatically shares a single proxy and dashboard — no configuration needed.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Terminal 1
|
|
46
|
+
cd ~/project-a && ccxray claude # Starts hub + claude
|
|
47
|
+
|
|
48
|
+
# Terminal 2
|
|
49
|
+
cd ~/project-b && ccxray claude # Connects to existing hub
|
|
50
|
+
|
|
51
|
+
# Both projects visible in one dashboard at http://localhost:5577
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If the hub process crashes, connected clients automatically recover within seconds.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
$ ccxray status
|
|
58
|
+
Hub: http://localhost:5577 (pid 12345, uptime 3600s, v1.1.0)
|
|
59
|
+
Connected clients (2):
|
|
60
|
+
[1] pid 23456 — ~/dev/project-a
|
|
61
|
+
[2] pid 34567 — ~/dev/project-b
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use `--port` to opt out and run an independent server instead.
|
|
65
|
+
|
|
66
|
+
## Features
|
|
67
|
+
|
|
68
|
+
### Timeline
|
|
69
|
+
|
|
70
|
+
Watch your agent think in real-time. Every turn broken down into thinking blocks (with duration), tool calls with inline previews, and assistant responses.
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
### Usage & Cost
|
|
75
|
+
|
|
76
|
+
Track your real spending. Session heatmap, burn rate, ROI calculator — know exactly where your tokens go.
|
|
77
|
+
|
|
78
|
+

|
|
79
|
+
|
|
80
|
+
### System Prompt Tracking
|
|
81
|
+
|
|
82
|
+
Automatic version detection with diff viewer. See exactly what changed between Claude Code updates — never miss a prompt change again.
|
|
83
|
+
|
|
84
|
+

|
|
85
|
+
|
|
86
|
+
### More
|
|
87
|
+
|
|
88
|
+
- **Session Detection** — Automatically groups turns by Claude Code session, with project/cwd extraction
|
|
89
|
+
- **Token Accounting** — Per-turn breakdown: input/output/cache-read/cache-create tokens, cost in USD, context window usage bar
|
|
90
|
+
|
|
91
|
+
## How It Works
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Claude Code ──► ccxray (:5577) ──► api.anthropic.com
|
|
95
|
+
│
|
|
96
|
+
▼
|
|
97
|
+
~/.ccxray/logs/ (JSON)
|
|
98
|
+
│
|
|
99
|
+
▼
|
|
100
|
+
Dashboard (same port)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
ccxray is a transparent HTTP proxy. It forwards requests to Anthropic unchanged, records both request and response as JSON files, and serves a web dashboard on the same port. No API key needed — it passes through whatever Claude Code sends.
|
|
104
|
+
|
|
105
|
+
## Configuration
|
|
106
|
+
|
|
107
|
+
### CLI flags
|
|
108
|
+
|
|
109
|
+
| Flag | Description |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `--port <number>` | Port for proxy + dashboard (default: 5577). Opts out of hub sharing. |
|
|
112
|
+
| `--no-browser` | Don't auto-open the dashboard in your browser |
|
|
113
|
+
|
|
114
|
+
### Environment variables
|
|
115
|
+
|
|
116
|
+
| Variable | Default | Description |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `PROXY_PORT` | `5577` | Port for proxy + dashboard (overridden by `--port`) |
|
|
119
|
+
| `BROWSER` | — | Set to `none` to disable auto-open |
|
|
120
|
+
| `AUTH_TOKEN` | _(none)_ | API key for access control (disabled when unset) |
|
|
121
|
+
| `CCXRAY_HOME` | `~/.ccxray` | Base directory for hub lockfile, logs, and hub.log |
|
|
122
|
+
| `CCXRAY_MAX_ENTRIES` | `5000` | Max in-memory entries (oldest evicted; disk logs unaffected) |
|
|
123
|
+
|
|
124
|
+
Logs are stored in `~/.ccxray/logs/` as `{timestamp}_req.json` and `{timestamp}_res.json`. Upgrading from v1.0? Logs previously in `./logs/` are automatically migrated on first run.
|
|
125
|
+
|
|
126
|
+
## Docker
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
docker build -t ccxray .
|
|
130
|
+
docker run -p 5577:5577 ccxray
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Requirements
|
|
134
|
+
|
|
135
|
+
- Node.js 18+
|
|
136
|
+
|
|
137
|
+
## Also by the author
|
|
138
|
+
|
|
139
|
+
- [SourceAtlas](https://sourceatlas.io/) — Your map to any codebase
|
|
140
|
+
- [AskRoundtable](https://github.com/AskRoundtable/expert-skills) — Make your AI think like Munger, Feynman, or Paul Graham
|
|
141
|
+
- Follow [@lis186](https://x.com/lis186) on X for updates
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
package/README.zh-TW.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# ccxray
|
|
2
|
+
|
|
3
|
+
[English](README.md) | **正體中文** | [日本語](README.ja.md)
|
|
4
|
+
|
|
5
|
+
AI 代理工作階段的透視鏡。零設定的 HTTP 代理,記錄 Claude Code 與 Anthropic API 之間的每一次呼叫,搭配即時儀表板,讓你看清代理內部到底在做什麼。
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## 為什麼需要
|
|
12
|
+
|
|
13
|
+
Claude Code 是個黑盒子。你看不到:
|
|
14
|
+
- 它送出了什麼 system prompt(以及版本間的變化)
|
|
15
|
+
- 每次 tool call 花了多少錢
|
|
16
|
+
- 為什麼它思考了 30 秒
|
|
17
|
+
- 什麼東西吃掉了你的 200K token 上下文窗口
|
|
18
|
+
|
|
19
|
+
ccxray 讓它變成透明的。
|
|
20
|
+
|
|
21
|
+
## 快速開始
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @kylindc/ccxray claude
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
就這樣。代理啟動、Claude Code 透過代理連線、儀表板自動在瀏覽器中開啟。在多個終端機執行時會自動共用同一個 dashboard。
|
|
28
|
+
|
|
29
|
+
### 其他執行方式
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
ccxray # 只啟動代理 + 儀表板
|
|
33
|
+
ccxray claude --continue # 所有 claude 參數直接穿透
|
|
34
|
+
ccxray --port 8080 claude # 自訂 port(獨立模式,不共用 hub)
|
|
35
|
+
ccxray claude --no-browser # 不自動開啟瀏覽器
|
|
36
|
+
ccxray status # 顯示 hub 資訊及已連線的 client
|
|
37
|
+
ANTHROPIC_BASE_URL=http://localhost:5577 claude # 手動設定(現有工作階段)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 多專案
|
|
41
|
+
|
|
42
|
+
在多個終端機執行 `ccxray claude` 會自動共用同一個 proxy 和 dashboard — 無需任何設定。
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Terminal 1
|
|
46
|
+
cd ~/project-a && ccxray claude # 啟動 hub + claude
|
|
47
|
+
|
|
48
|
+
# Terminal 2
|
|
49
|
+
cd ~/project-b && ccxray claude # 連線至現有 hub
|
|
50
|
+
|
|
51
|
+
# 兩個專案都顯示在 http://localhost:5577 的 dashboard
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
如果 hub 意外終止,已連線的 client 會在數秒內自動恢復。
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
$ ccxray status
|
|
58
|
+
Hub: http://localhost:5577 (pid 12345, uptime 3600s, v1.1.0)
|
|
59
|
+
Connected clients (2):
|
|
60
|
+
[1] pid 23456 — ~/dev/project-a
|
|
61
|
+
[2] pid 34567 — ~/dev/project-b
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
使用 `--port` 可改為獨立模式。
|
|
65
|
+
|
|
66
|
+
## 功能
|
|
67
|
+
|
|
68
|
+
### 時間軸
|
|
69
|
+
|
|
70
|
+
即時觀看代理的思考過程。每個回合拆解為思考區塊(含時長)、tool call 內聯預覽、助手回應。
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
### 用量與成本
|
|
75
|
+
|
|
76
|
+
追蹤你的實際花費。工作階段熱力圖、消耗速率、ROI 計算 — 精確掌握 token 流向。
|
|
77
|
+
|
|
78
|
+

|
|
79
|
+
|
|
80
|
+
### System Prompt 追蹤
|
|
81
|
+
|
|
82
|
+
自動偵測版本變更,內建 diff 檢視器。精確掌握 Claude Code 更新時改了什麼 — 不再遺漏任何 prompt 變動。
|
|
83
|
+
|
|
84
|
+

|
|
85
|
+
|
|
86
|
+
### 其他功能
|
|
87
|
+
|
|
88
|
+
- **工作階段偵測** — 自動依 Claude Code session 分組,含專案/工作目錄擷取
|
|
89
|
+
- **Token 記帳** — 每回合明細:input/output/cache-read/cache-create tokens、美元成本、上下文窗口使用率
|
|
90
|
+
|
|
91
|
+
## 運作原理
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Claude Code ──► ccxray (:5577) ──► api.anthropic.com
|
|
95
|
+
│
|
|
96
|
+
▼
|
|
97
|
+
~/.ccxray/logs/ (JSON)
|
|
98
|
+
│
|
|
99
|
+
▼
|
|
100
|
+
儀表板(同一連接埠)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
ccxray 是透明的 HTTP 代理。它將請求原封不動地轉發到 Anthropic,將請求與回應記錄為 JSON 檔案,並在同一連接埠提供網頁儀表板。不需要 API 金鑰 — 它直接傳遞 Claude Code 送出的內容。
|
|
104
|
+
|
|
105
|
+
## 設定
|
|
106
|
+
|
|
107
|
+
### CLI 參數
|
|
108
|
+
|
|
109
|
+
| 參數 | 說明 |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `--port <number>` | 代理 + 儀表板的連接埠(預設:5577)。使用後不共用 hub。 |
|
|
112
|
+
| `--no-browser` | 不自動在瀏覽器中開啟儀表板 |
|
|
113
|
+
|
|
114
|
+
### 環境變數
|
|
115
|
+
|
|
116
|
+
| 變數 | 預設值 | 說明 |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `PROXY_PORT` | `5577` | 代理 + 儀表板的連接埠(`--port` 會覆蓋此值) |
|
|
119
|
+
| `BROWSER` | — | 設為 `none` 可停用自動開啟 |
|
|
120
|
+
| `AUTH_TOKEN` | _(無)_ | 存取控制用 API 金鑰(未設定時停用) |
|
|
121
|
+
| `CCXRAY_HOME` | `~/.ccxray` | 基底目錄,存放 hub lockfile、logs、hub.log |
|
|
122
|
+
|
|
123
|
+
日誌儲存在 `~/.ccxray/logs/`,格式為 `{timestamp}_req.json` 和 `{timestamp}_res.json`。從 v1.0 升級?`./logs/` 中的日誌會在首次啟動時自動遷移。
|
|
124
|
+
|
|
125
|
+
## Docker
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
docker build -t ccxray .
|
|
129
|
+
docker run -p 5577:5577 ccxray
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## 系統需求
|
|
133
|
+
|
|
134
|
+
- Node.js 18+
|
|
135
|
+
|
|
136
|
+
## 作者的其他作品
|
|
137
|
+
|
|
138
|
+
- [SourceAtlas](https://sourceatlas.io/) — 任何 codebase 的導航地圖
|
|
139
|
+
- [AskRoundtable](https://github.com/AskRoundtable/expert-skills) — 讓你的 AI 像 Munger、Feynman、Paul Graham 一樣思考
|
|
140
|
+
- 在 X 上追蹤 [@lis186](https://x.com/lis186) 獲取最新動態
|
|
141
|
+
|
|
142
|
+
## 授權
|
|
143
|
+
|
|
144
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kylindc/ccxray",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "X-ray vision for AI agent sessions — a transparent HTTP proxy and dashboard for Claude Code",
|
|
5
|
+
"main": "server/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ccxray": "server/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node server/index.js",
|
|
11
|
+
"dev": "node --watch-path=server --watch-path=public server/index.js",
|
|
12
|
+
"test": "node --test test/*.test.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"claude",
|
|
16
|
+
"claude-code",
|
|
17
|
+
"proxy",
|
|
18
|
+
"dashboard",
|
|
19
|
+
"anthropic",
|
|
20
|
+
"ai-agent",
|
|
21
|
+
"debugging",
|
|
22
|
+
"observability"
|
|
23
|
+
],
|
|
24
|
+
"author": "KylinDC",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/KylinDC/ccxray.git"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/KylinDC/ccxray",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/KylinDC/ccxray/issues"
|
|
33
|
+
},
|
|
34
|
+
"type": "commonjs",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@anthropic-ai/tokenizer": "^0.0.4",
|
|
40
|
+
"ws": "^8.19.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@fission-ai/openspec": "^1.2.0",
|
|
44
|
+
"puppeteer": "^24.39.1"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/public/app.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// ── Global Config ────────────────────────────────────────────────────
|
|
2
|
+
const DEFAULT_MAX_CTX = window.__PROXY_CONFIG__?.DEFAULT_CONTEXT || 200000;
|
|
3
|
+
|
|
4
|
+
// ── Active Tab State ─────────────────────────────────────────────────
|
|
5
|
+
let activeTab = 'dashboard';
|
|
6
|
+
|
|
7
|
+
function switchTab(tab, forceDiff) {
|
|
8
|
+
if (activeTab === tab) return;
|
|
9
|
+
activeTab = tab;
|
|
10
|
+
|
|
11
|
+
// Update tab buttons
|
|
12
|
+
document.querySelectorAll('.topbar-tab').forEach(btn => {
|
|
13
|
+
btn.classList.toggle('active', btn.dataset.tab === tab);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Show/hide content areas
|
|
17
|
+
document.getElementById('columns').style.display = tab === 'dashboard' ? '' : 'none';
|
|
18
|
+
const costPage = document.getElementById('cost-page');
|
|
19
|
+
const diffOverlay = document.getElementById('diff-overlay');
|
|
20
|
+
if (tab === 'usage') {
|
|
21
|
+
costPage.classList.add('open');
|
|
22
|
+
diffOverlay.classList.remove('open');
|
|
23
|
+
loadCostPage();
|
|
24
|
+
} else {
|
|
25
|
+
costPage.classList.remove('open');
|
|
26
|
+
}
|
|
27
|
+
if (tab === 'sysprompt') {
|
|
28
|
+
diffOverlay.classList.add('open');
|
|
29
|
+
costPage.classList.remove('open');
|
|
30
|
+
openSystemPromptPanel(forceDiff);
|
|
31
|
+
} else if (tab !== 'sysprompt') {
|
|
32
|
+
diffOverlay.classList.remove('open');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Update Row 2 contextual content
|
|
36
|
+
document.getElementById('row2-dashboard').style.display = tab === 'dashboard' ? '' : 'none';
|
|
37
|
+
document.getElementById('row2-usage').style.display = tab === 'usage' ? '' : 'none';
|
|
38
|
+
document.getElementById('row2-sysprompt').style.display = tab === 'sysprompt' ? '' : 'none';
|
|
39
|
+
|
|
40
|
+
// Sync URL
|
|
41
|
+
syncViewParam();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function syncViewParam() {
|
|
45
|
+
// Use syncUrlFromState if available (miller-columns.js loaded), otherwise fallback
|
|
46
|
+
if (typeof syncUrlFromState === 'function') {
|
|
47
|
+
syncUrlFromState();
|
|
48
|
+
} else {
|
|
49
|
+
const params = new URLSearchParams(window.location.search);
|
|
50
|
+
if (activeTab === 'dashboard') params.delete('view');
|
|
51
|
+
else params.set('view', activeTab);
|
|
52
|
+
const qs = params.toString();
|
|
53
|
+
history.replaceState(null, '', window.location.pathname + (qs ? '?' + qs : ''));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Capture view param early before deep-link resolution rewrites URL
|
|
58
|
+
const _savedViewParam = new URLSearchParams(window.location.search).get('view');
|
|
59
|
+
|
|
60
|
+
function restoreTabFromUrl() {
|
|
61
|
+
const view = _savedViewParam;
|
|
62
|
+
if (view === 'usage' || view === 'sysprompt') {
|
|
63
|
+
switchTab(view);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── Theme Toggle ─────────────────────────────────────────────────────
|
|
68
|
+
function toggleTheme() {
|
|
69
|
+
const isLight = document.documentElement.getAttribute('data-theme') === 'light';
|
|
70
|
+
const next = isLight ? 'dark' : 'light';
|
|
71
|
+
document.documentElement.setAttribute('data-theme', next);
|
|
72
|
+
localStorage.setItem('theme', next);
|
|
73
|
+
updateThemeIcon();
|
|
74
|
+
}
|
|
75
|
+
function updateThemeIcon() {
|
|
76
|
+
const btn = document.getElementById('theme-toggle');
|
|
77
|
+
if (!btn) return;
|
|
78
|
+
btn.textContent = document.documentElement.getAttribute('data-theme') === 'light' ? '🌙' : '☀️';
|
|
79
|
+
}
|
|
80
|
+
updateThemeIcon();
|
|
81
|
+
|
|
82
|
+
// ── Unified Escape + tab switching handler ──────────────────────────
|
|
83
|
+
document.addEventListener('keydown', (e) => {
|
|
84
|
+
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) return;
|
|
85
|
+
// Don't intercept when miller-columns focused mode is active
|
|
86
|
+
if (typeof isFocusedMode !== 'undefined' && isFocusedMode) return;
|
|
87
|
+
|
|
88
|
+
// Escape → switch to dashboard
|
|
89
|
+
if (e.key === 'Escape' && activeTab !== 'dashboard') {
|
|
90
|
+
switchTab('dashboard');
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Tab switching: 1/2/3
|
|
96
|
+
if (e.key === '1') { switchTab('dashboard'); e.preventDefault(); return; }
|
|
97
|
+
if (e.key === '2') { switchTab('usage'); e.preventDefault(); return; }
|
|
98
|
+
if (e.key === '3') { switchTab('sysprompt'); e.preventDefault(); return; }
|
|
99
|
+
});
|