coding-tool-x 3.4.12 → 3.5.1
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/README.md +158 -171
- package/dist/web/assets/{Analytics-Q_QFMM2p.js → Analytics-B653rHbb.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-Ca8C7VtV.js → ConfigTemplates-uvPIB9bY.js} +1 -1
- package/dist/web/assets/{Home-ChIIT4Ew.js → Home-C3w31EDB.js} +1 -1
- package/dist/web/assets/{PluginManager-C3w7p-sj.js → PluginManager-CfvgUebQ.js} +1 -1
- package/dist/web/assets/{ProjectList-BfJTDXDw.js → ProjectList-C16vMDcU.js} +1 -1
- package/dist/web/assets/SessionList-B8dXVXfi.css +1 -0
- package/dist/web/assets/SessionList-DWuhaeMb.js +1 -0
- package/dist/web/assets/{SkillManager-CohW5iqS.js → SkillManager-CRMUhw4v.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-DxaKsaJK.js → WorkspaceManager-BOX_nqej.js} +1 -1
- package/dist/web/assets/index-B1ujw2sM.js +2 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/src/server/api/codex-sessions.js +54 -0
- package/src/server/api/gemini-sessions.js +54 -0
- package/src/server/api/opencode-sessions.js +54 -0
- package/src/server/api/sessions.js +49 -0
- package/src/server/index.js +1 -0
- package/src/server/services/notification-hooks.js +94 -8
- package/dist/web/assets/SessionList-C55tjV7i.css +0 -1
- package/dist/web/assets/SessionList-a3EoL0hZ.js +0 -1
- package/dist/web/assets/index-EMrm1wk-.js +0 -2
package/README.md
CHANGED
|
@@ -1,101 +1,107 @@
|
|
|
1
|
-
#
|
|
1
|
+
# coding-tool-x
|
|
2
2
|
|
|
3
3
|
> 面向 Claude Code、Codex CLI、Gemini CLI、OpenCode 的统一增强控制台
|
|
4
|
-
> Web UI + CLI +
|
|
4
|
+
> Web UI + CLI + 多平台代理 + 配置托管 + 工作区编排 + 分析面板
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
7
9
|
|
|
8
10
|

|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
`coding-tool-x` 把多个 Coding CLI 的常用管理能力收拢到一套界面里: 会话查看、渠道代理、配置同步、工作区组织、MCP / Skills / Commands / Agents / Plugins 管理、OAuth 凭证托管、通知设置、统计分析和配置导入导出。
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
如果你同时在用 Claude Code、Codex CLI、Gemini CLI、OpenCode,这个项目的目标就是把这些分散在不同目录、不同配置文件、不同命令里的日常操作,尽量放回一个统一入口。
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
- 为各平台配置多渠道、代理、模型重定向、测速与健康检查
|
|
16
|
-
- 管理并同步 Prompts、Skills、Commands、Agents、MCP、插件配置
|
|
17
|
-
- 提供工作区、Git worktree、配置模板、导入导出与诊断能力
|
|
18
|
-
- 通过 Web 面板查看实时日志、请求统计、Token/费用趋势
|
|
16
|
+
## 适合做什么
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
- 统一查看四个平台的项目和会话
|
|
19
|
+
- 管理多渠道代理、测速、模型探测和健康状态
|
|
20
|
+
- 集中托管 Prompts、Skills、Agents、Commands、MCP、Plugins 等常用配置项
|
|
21
|
+
- 为多项目创建工作区,必要时自动创建 Git worktree
|
|
22
|
+
- 统一查看请求量、Token、费用趋势与实时日志
|
|
23
|
+
- 导入导出整套配置,包含原生配置快照
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
## 功能概览
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
- 支持会话别名、最近会话、全局搜索、会话 Fork、跨平台会话转换
|
|
26
|
-
- 每个平台都有独立代理入口与独立端口
|
|
27
|
-
- 支持多渠道启用/停用、测速、健康状态、优先级与模型列表探测
|
|
28
|
-
- 支持 Claude / Codex / Gemini 的模型重定向与默认测速模型配置
|
|
29
|
-
- OpenCode 支持网关适配与会话格式转换/导出
|
|
27
|
+
### 会话与项目
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
- 支持 Claude、Codex、Gemini、OpenCode 四个平台的项目与会话列表
|
|
30
|
+
- 支持项目排序、项目搜索、会话排序、会话搜索
|
|
31
|
+
- 支持最近会话、收藏、别名、聊天记录查看
|
|
32
|
+
- 支持新建会话、删除会话、复制启动命令
|
|
33
|
+
- Claude / Codex / Gemini 会话支持格式转换
|
|
34
|
+
- 支持将 Claude / Codex / Gemini 请求转换为 OpenCode 网关请求
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
- 自动读写平台原生配置目录,而不是替换你的平台使用方式
|
|
35
|
-
- 支持 Prompts 预设创建、激活、停用与平台状态查看
|
|
36
|
-
- 支持配置模板、工作区模板与导入配置时的预览
|
|
37
|
-
- 支持 ZIP / JSON 配置导入导出
|
|
38
|
-
- 支持 Claude / Codex / Gemini / OpenCode 的配置同步
|
|
39
|
-
|
|
40
|
-
### 3. 扩展能力
|
|
41
|
-
|
|
42
|
-
- MCP 服务器管理、平台启用开关、连通性测试、预设与导入
|
|
43
|
-
- Skills 管理:远程仓库安装、本地托管、详情查看、按平台安装
|
|
44
|
-
- Commands 管理:当前支持 Claude / OpenCode
|
|
45
|
-
- Agents 管理:当前支持 Claude / Codex / OpenCode
|
|
46
|
-
- 插件系统:支持 Git 安装、启用/禁用、升级、配置
|
|
47
|
-
- OAuth 凭证管理:集中管理 Claude / Codex / Gemini / OpenCode OAuth 数据
|
|
48
|
-
|
|
49
|
-
### 4. 运维与观测
|
|
50
|
-
|
|
51
|
-
- 首页 Dashboard 按平台展示状态卡、代理状态、实时日志和快捷操作
|
|
52
|
-
- Analytics 页面查看多平台请求数、Token、费用趋势并导出 CSV/JSON
|
|
53
|
-
- `ctx logs` 查看 UI/代理日志,支持 `--follow` / `--lines` / `--clear`
|
|
54
|
-
- `ctx stats` 查看总体和分平台统计
|
|
55
|
-
- `ctx doctor` 做环境、端口、配置、日志、磁盘与进程诊断
|
|
56
|
-
- Web UI 支持访问密码;`--host` 暴露 LAN 时默认禁止远程写操作
|
|
36
|
+
### 多渠道代理
|
|
57
37
|
|
|
38
|
+
- 四个平台均支持独立代理端口和独立渠道配置
|
|
39
|
+
- 支持渠道增删改查、启用 / 停用、排序、权重、并发限制
|
|
40
|
+
- 支持速度测试、模型可用性探测、健康检查与故障冻结
|
|
41
|
+
- 支持模型重定向和默认测速模型配置
|
|
42
|
+
- Web UI 与 CLI 都可查看代理状态和日志
|
|
58
43
|
|
|
59
|
-
|
|
44
|
+
### 配置与托管
|
|
60
45
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- Express API:`src/server/index.js`
|
|
70
|
-
- WebSocket:用于 Dashboard / 日志 / 状态推送
|
|
71
|
-
- API 范围覆盖:
|
|
72
|
-
- 项目 / 会话
|
|
73
|
-
- 渠道 / 代理
|
|
74
|
-
- Skills / Commands / Agents / Plugins
|
|
75
|
-
- Prompts / MCP / OAuth / Config Export / Config Sync
|
|
76
|
-
- Dashboard / Analytics / Security / Workspace
|
|
46
|
+
- 集中存储在 `~/.cc-tool`
|
|
47
|
+
- 保留并同步各平台原生配置目录,而不是替代原生用法
|
|
48
|
+
- 支持 Prompts 预设管理,并同步到各平台对应提示文件
|
|
49
|
+
- 支持 Skills、Agents、Commands、Plugins 的中心托管与按支持的平台启停
|
|
50
|
+
- 支持 MCP 服务器配置、预设、连通性测试和多平台写入
|
|
51
|
+
- 支持 OAuth 凭证池管理与回写原生配置
|
|
52
|
+
- 支持 ZIP / JSON 配置导入导出
|
|
77
53
|
|
|
78
|
-
###
|
|
54
|
+
### 工作区与运维
|
|
79
55
|
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
56
|
+
- 支持多项目工作区
|
|
57
|
+
- 支持为 Git 仓库创建 worktree
|
|
58
|
+
- 支持配置模板,将提示词、技能、命令、代理、MCP、插件组合成一套模板
|
|
59
|
+
- 支持 Dashboard、Analytics、日志、统计导出、环境诊断
|
|
60
|
+
- 支持面板访问密码
|
|
61
|
+
- LAN 模式默认禁止远程写操作,可按需开启
|
|
62
|
+
|
|
63
|
+
### 通知
|
|
64
|
+
|
|
65
|
+
- 支持 Claude、Codex、Gemini、OpenCode 的任务完成通知托管
|
|
66
|
+
- 支持系统通知和弹窗模式
|
|
67
|
+
- 支持飞书机器人 Webhook 通知
|
|
68
|
+
|
|
69
|
+
## 能力矩阵
|
|
70
|
+
|
|
71
|
+
| 能力 | Claude | Codex | Gemini | OpenCode |
|
|
72
|
+
| --- | --- | --- | --- | --- |
|
|
73
|
+
| 项目 / 会话查看 | 支持 | 支持 | 支持 | 支持 |
|
|
74
|
+
| 渠道 / 代理管理 | 支持 | 支持 | 支持 | 支持 |
|
|
75
|
+
| Prompts 预设同步 | 支持 | 支持 | 支持 | 支持 |
|
|
76
|
+
| Skills 管理 | 支持 | 支持 | 支持 | 支持 |
|
|
77
|
+
| Commands 管理 | 支持 | - | - | 支持 |
|
|
78
|
+
| Agents 管理 | 支持 | 支持 | - | 支持 |
|
|
79
|
+
| Plugins 管理 | 支持 | - | - | 支持 |
|
|
80
|
+
| OAuth 凭证托管 | 支持 | 支持 | 支持 | 支持 |
|
|
81
|
+
| 通知托管 | 支持 | 支持 | 支持 | 支持 |
|
|
82
|
+
| 请求 / 会话统计 | 支持 | 支持 | 支持 | 支持 |
|
|
83
|
+
|
|
84
|
+
补充说明:
|
|
85
|
+
|
|
86
|
+
- Codex Agents 目前仅支持用户级代理
|
|
87
|
+
- OpenCode 会话读取依赖本机 `sqlite3`
|
|
88
|
+
- Commands 的直接 CRUD 当前面向 Claude / OpenCode
|
|
89
89
|
|
|
90
90
|
## 安装
|
|
91
91
|
|
|
92
|
-
###
|
|
92
|
+
### 全局安装
|
|
93
93
|
|
|
94
94
|
```bash
|
|
95
95
|
npm install -g coding-tool-x
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
###
|
|
98
|
+
### 国内镜像
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm install -g coding-tool-x --registry=https://registry.npmmirror.com
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 从源码运行
|
|
99
105
|
|
|
100
106
|
```bash
|
|
101
107
|
git clone https://github.com/ZeaoZhang/coding-tool.git
|
|
@@ -105,54 +111,48 @@ npm run build:web
|
|
|
105
111
|
npm link
|
|
106
112
|
```
|
|
107
113
|
|
|
108
|
-
|
|
114
|
+
## 环境要求
|
|
109
115
|
|
|
110
|
-
- Node.js `>= 14.0.0
|
|
111
|
-
-
|
|
112
|
-
|
|
113
|
-
### 验证安装
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
ctx --version
|
|
117
|
-
ctx --help
|
|
118
|
-
```
|
|
116
|
+
- Node.js `>= 14.0.0`
|
|
117
|
+
- 建议至少运行过一次目标 CLI,以便生成原生配置目录
|
|
118
|
+
- 如需读取 OpenCode 会话,请确保系统里有可用的 `sqlite3`
|
|
119
119
|
|
|
120
120
|
## 快速开始
|
|
121
121
|
|
|
122
|
-
###
|
|
122
|
+
### 推荐方式
|
|
123
123
|
|
|
124
124
|
```bash
|
|
125
125
|
ctx start
|
|
126
126
|
ctx status
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
启动后默认访问:
|
|
130
130
|
|
|
131
131
|
- Web UI: `http://localhost:19999`
|
|
132
132
|
|
|
133
|
-
###
|
|
133
|
+
### 前台运行
|
|
134
134
|
|
|
135
135
|
```bash
|
|
136
136
|
ctx ui
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
###
|
|
139
|
+
### 开启局域网访问
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
142
|
ctx ui --host
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
LAN 模式说明:
|
|
146
146
|
|
|
147
|
-
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
147
|
+
- 服务会监听 `0.0.0.0`
|
|
148
|
+
- 默认只允许本机执行写操作
|
|
149
|
+
- 如确需允许远程写操作,可显式设置:
|
|
150
150
|
|
|
151
151
|
```bash
|
|
152
152
|
CC_TOOL_ALLOW_REMOTE_WRITE=true ctx ui --host
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
###
|
|
155
|
+
### 单独控制平台代理
|
|
156
156
|
|
|
157
157
|
```bash
|
|
158
158
|
ctx claude start
|
|
@@ -163,98 +163,96 @@ ctx opencode start
|
|
|
163
163
|
|
|
164
164
|
## 常用命令
|
|
165
165
|
|
|
166
|
-
###
|
|
166
|
+
### 服务
|
|
167
167
|
|
|
168
168
|
| 命令 | 说明 |
|
|
169
169
|
| --- | --- |
|
|
170
|
-
| `ctx start` |
|
|
170
|
+
| `ctx start` | 后台启动整套服务 |
|
|
171
171
|
| `ctx stop` | 停止后台服务 |
|
|
172
172
|
| `ctx restart` | 重启后台服务 |
|
|
173
173
|
| `ctx status` | 查看后台服务状态 |
|
|
174
174
|
| `ctx ui` | 前台启动 Web UI |
|
|
175
|
-
| `ctx ui
|
|
175
|
+
| `ctx ui start` | 后台启动 Web UI |
|
|
176
|
+
| `ctx ui stop` | 停止后台 Web UI |
|
|
177
|
+
| `ctx ui restart` | 重启后台 Web UI |
|
|
176
178
|
|
|
177
|
-
###
|
|
179
|
+
### 平台代理
|
|
178
180
|
|
|
179
181
|
| 命令 | 说明 |
|
|
180
182
|
| --- | --- |
|
|
181
|
-
| `ctx claude start
|
|
182
|
-
| `ctx codex start
|
|
183
|
-
| `ctx gemini start
|
|
184
|
-
| `ctx opencode start
|
|
183
|
+
| `ctx claude start\|stop\|restart\|status` | Claude 代理管理 |
|
|
184
|
+
| `ctx codex start\|stop\|restart\|status` | Codex 代理管理 |
|
|
185
|
+
| `ctx gemini start\|stop\|restart\|status` | Gemini 代理管理 |
|
|
186
|
+
| `ctx opencode start\|stop\|restart\|status` | OpenCode 代理管理 |
|
|
185
187
|
|
|
186
|
-
###
|
|
188
|
+
### 日志与统计
|
|
187
189
|
|
|
188
190
|
| 命令 | 说明 |
|
|
189
191
|
| --- | --- |
|
|
190
|
-
| `ctx logs` |
|
|
192
|
+
| `ctx logs` | 查看所有日志 |
|
|
191
193
|
| `ctx logs ui` | 查看 UI 日志 |
|
|
192
194
|
| `ctx logs claude` | 查看 Claude 代理日志 |
|
|
193
195
|
| `ctx logs --follow` | 实时追踪日志 |
|
|
194
196
|
| `ctx logs --lines 100` | 查看最近 100 行 |
|
|
195
197
|
| `ctx logs --clear` | 清空日志 |
|
|
196
198
|
| `ctx stats` | 查看总体统计 |
|
|
197
|
-
| `ctx stats claude` |
|
|
199
|
+
| `ctx stats claude` | 查看单个平台统计 |
|
|
198
200
|
| `ctx stats export` | 导出统计数据 |
|
|
199
|
-
| `ctx doctor` |
|
|
201
|
+
| `ctx doctor` | 运行环境诊断 |
|
|
200
202
|
|
|
201
|
-
###
|
|
203
|
+
### 其他
|
|
202
204
|
|
|
203
205
|
| 命令 | 说明 |
|
|
204
206
|
| --- | --- |
|
|
205
207
|
| `ctx update` | 检查并更新版本 |
|
|
206
|
-
| `ctx port` |
|
|
207
|
-
| `ctx reset` | 重置 cc-tool 配置 |
|
|
208
|
-
| `ctx security reset` |
|
|
208
|
+
| `ctx port` | 修改默认端口 |
|
|
209
|
+
| `ctx reset` | 重置 `~/.cc-tool` 配置 |
|
|
210
|
+
| `ctx security reset` | 关闭面板访问密码 |
|
|
209
211
|
| `ctx plugin list` | 查看已安装插件 |
|
|
210
212
|
| `ctx plugin install <git-url>` | 从 Git 安装插件 |
|
|
211
213
|
|
|
212
|
-
|
|
214
|
+
兼容说明:
|
|
213
215
|
|
|
214
|
-
`ctx proxy start|stop|status`
|
|
216
|
+
- `ctx proxy start|stop|status` 仍保留为旧入口
|
|
217
|
+
- 新用法更推荐 `ctx claude ...`、`ctx codex ...`、`ctx gemini ...`、`ctx opencode ...`
|
|
215
218
|
|
|
216
219
|
## Web UI 主要模块
|
|
217
220
|
|
|
218
|
-
###
|
|
221
|
+
### Home / Dashboard
|
|
219
222
|
|
|
220
|
-
-
|
|
221
|
-
-
|
|
222
|
-
-
|
|
223
|
-
- 实时日志
|
|
224
|
-
- 快捷操作
|
|
223
|
+
- 四个平台并列状态卡
|
|
224
|
+
- 支持拖拽调整平台顺序
|
|
225
|
+
- 展示代理状态、今日请求、Token、费用、项目数、会话数
|
|
225
226
|
|
|
226
|
-
###
|
|
227
|
+
### 项目与会话
|
|
227
228
|
|
|
228
|
-
-
|
|
229
|
-
-
|
|
230
|
-
-
|
|
231
|
-
-
|
|
232
|
-
- 会话 Fork
|
|
233
|
-
- 会话详情查看
|
|
229
|
+
- 项目列表、会话列表
|
|
230
|
+
- 全局搜索和项目内搜索
|
|
231
|
+
- 聊天历史查看
|
|
232
|
+
- 收藏、别名、删除、复制启动命令,部分平台支持 Fork
|
|
234
233
|
|
|
235
|
-
###
|
|
234
|
+
### 配置管理
|
|
236
235
|
|
|
237
|
-
- 聚合多个项目为一个工作区
|
|
238
|
-
- 支持为 Git 仓库创建 worktree
|
|
239
|
-
- 支持使用工作区内配置模版
|
|
240
|
-
|
|
241
|
-
### 配置与扩展
|
|
242
|
-
|
|
243
|
-
- Config Templates
|
|
244
236
|
- Prompts
|
|
237
|
+
- MCP
|
|
245
238
|
- Skills
|
|
246
239
|
- Commands
|
|
247
240
|
- Agents
|
|
248
|
-
- MCP
|
|
249
241
|
- Plugins
|
|
250
242
|
- OAuth Credentials
|
|
251
243
|
- Config Export / Import
|
|
252
244
|
|
|
245
|
+
### 工作区与模板
|
|
246
|
+
|
|
247
|
+
- Workspaces
|
|
248
|
+
- Config Templates
|
|
249
|
+
- Git worktree 组织
|
|
250
|
+
|
|
253
251
|
### Analytics
|
|
254
252
|
|
|
255
|
-
-
|
|
253
|
+
- 多平台统计汇总
|
|
256
254
|
- 模型 / 渠道 / 工具维度分析
|
|
257
|
-
-
|
|
255
|
+
- 时间范围筛选
|
|
258
256
|
- CSV / JSON 导出
|
|
259
257
|
|
|
260
258
|
## 默认端口
|
|
@@ -269,40 +267,34 @@ ctx opencode start
|
|
|
269
267
|
|
|
270
268
|
可通过 `ctx port` 修改。
|
|
271
269
|
|
|
272
|
-
##
|
|
270
|
+
## 数据目录
|
|
273
271
|
|
|
274
|
-
###
|
|
272
|
+
### 中央目录
|
|
275
273
|
|
|
276
|
-
|
|
274
|
+
默认位于:
|
|
277
275
|
|
|
278
276
|
```text
|
|
279
277
|
~/.cc-tool
|
|
280
278
|
```
|
|
281
279
|
|
|
282
|
-
|
|
280
|
+
常见内容:
|
|
283
281
|
|
|
284
|
-
- `config
|
|
285
|
-
- `
|
|
286
|
-
- `
|
|
287
|
-
- `
|
|
288
|
-
- `plugins
|
|
282
|
+
- `config/`: 主配置、Prompts、MCP、OAuth、工作区、模板等
|
|
283
|
+
- `configs/`: 托管的 skills / commands / agents / plugins
|
|
284
|
+
- `storage/`: 渠道、缓存、统计、内部运行数据
|
|
285
|
+
- `logs/`: 服务与代理日志
|
|
286
|
+
- `plugins/`: 插件安装与插件配置
|
|
289
287
|
|
|
290
|
-
###
|
|
288
|
+
### 原生配置目录
|
|
291
289
|
|
|
292
|
-
|
|
290
|
+
项目会继续读写各平台原生配置:
|
|
293
291
|
|
|
294
|
-
- Claude
|
|
295
|
-
- Codex
|
|
296
|
-
- Gemini
|
|
297
|
-
- OpenCode
|
|
298
|
-
-
|
|
299
|
-
-
|
|
300
|
-
|
|
301
|
-
说明:
|
|
302
|
-
|
|
303
|
-
- Coding-Tool-X 负责集中管理与同步
|
|
304
|
-
- 平台自身仍然可以直接使用原生目录
|
|
305
|
-
- 仓库内实现对旧的 `~/.claude/cc-tool` 数据做自动迁移
|
|
292
|
+
- Claude: `~/.claude`
|
|
293
|
+
- Codex: `${CODEX_HOME:-~/.codex}`
|
|
294
|
+
- Gemini: `~/.gemini`
|
|
295
|
+
- OpenCode:
|
|
296
|
+
- 配置: `~/.config/opencode`
|
|
297
|
+
- 数据: `~/.local/share/opencode`
|
|
306
298
|
|
|
307
299
|
## 开发
|
|
308
300
|
|
|
@@ -336,24 +328,13 @@ npm run build:web
|
|
|
336
328
|
npm test
|
|
337
329
|
```
|
|
338
330
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
## 与上游仓库的关系
|
|
342
|
-
|
|
343
|
-
本仓库不是对上游 README 的原样搬运,而是在 [CooperJiang/coding-tool](https://github.com/CooperJiang/coding-tool) 的基础上继续演进。当前代码相较原始项目,重点扩展在:
|
|
344
|
-
|
|
345
|
-
- 四平台统一支持:Claude / Codex / Gemini / OpenCode
|
|
346
|
-
- 分平台代理与统计体系
|
|
347
|
-
- OpenCode 网关适配、会话转换与导出
|
|
348
|
-
- Skills / Commands / Agents / Prompts / MCP 的集中管理
|
|
349
|
-
- OAuth 凭证管理与原生配置同步
|
|
350
|
-
- 工作区、Git worktree、配置模板、配置导入导出
|
|
351
|
-
- 更完整的 Dashboard 与 Analytics 面板
|
|
331
|
+
当前仓库内置了基础命令、API 一致性、Codex Agents、Skills Provider、插件市场缓存等相关回归测试。
|
|
352
332
|
|
|
353
333
|
## 已知说明
|
|
354
334
|
|
|
355
|
-
- `ctx ui --host` 开启 LAN
|
|
356
|
-
-
|
|
335
|
+
- `ctx ui --host` 开启 LAN 访问后,默认不会允许远程写操作,这是安全保护行为
|
|
336
|
+
- OpenCode 部分能力依赖本机可访问的 OpenCode 配置目录和 `sqlite3`
|
|
337
|
+
- 配置导出包可能包含 API Key、Webhook、OAuth 等敏感信息,请妥善保管
|
|
357
338
|
|
|
358
339
|
## 相关文档
|
|
359
340
|
|
|
@@ -361,6 +342,12 @@ npm test
|
|
|
361
342
|
- [docs/multi-channel-load-balancing.md](docs/multi-channel-load-balancing.md)
|
|
362
343
|
- [src/web/README.md](src/web/README.md)
|
|
363
344
|
|
|
345
|
+
## 致谢
|
|
346
|
+
|
|
347
|
+
特别感谢 [CooperJiang/coding-tool](https://github.com/CooperJiang/coding-tool) 提供的项目基础。`coding-tool-x` 在原有能力之上持续扩展,补齐了多平台支持、配置同步、扩展管理、工作区编排与分析面板等增强能力;没有上游项目打下的基础,这个增强型分支也很难持续演进。
|
|
348
|
+
|
|
349
|
+
也感谢所有在使用、反馈、测试和持续完善这个分支过程中提供帮助的贡献者与用户。正是这些真实场景下的需求、问题和建议,让这个项目逐步从单一工具发展成更完整的 Coding CLI 工作台。
|
|
350
|
+
|
|
364
351
|
## License
|
|
365
352
|
|
|
366
353
|
[MIT](LICENSE)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{d as t,t as e,A as n,l as i,Q as r,f as o,g as a,w as s,B as l,h as u,i as h,v as c,aa as p,N as d,r as f,c as g,o as v,a as y,W as m,X as _,F as x,$ as w,a0 as b,Y as S,p as M,_ as T,L as C}from"./vue-vendor-3bf-fPGP.js";import{_ as I,
|
|
1
|
+
import{d as t,t as e,A as n,l as i,Q as r,f as o,g as a,w as s,B as l,h as u,i as h,v as c,aa as p,N as d,r as f,c as g,o as v,a as y,W as m,X as _,F as x,$ as w,a0 as b,Y as S,p as M,_ as T,L as C}from"./vue-vendor-3bf-fPGP.js";import{_ as I,x as D,y as k,z as A}from"./index-B1ujw2sM.js";import{L as P,$ as L,k as O,B as R,N as z,F as E,g as N}from"./naive-ui-Bdxp09n2.js";import{a3 as B,aA as F,aB as V}from"./icons-B5Pl4lrD.js";import"./markdown-DyTJGI4N.js";import"./vendors-CKPV1OAU.js";
|
|
2
2
|
/*! *****************************************************************************
|
|
3
3
|
Copyright (c) Microsoft Corporation.
|
|
4
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as s,
|
|
1
|
+
import{_ as s,w as a}from"./index-B1ujw2sM.js";import{c as e,o,X as r,Y as t,N as i}from"./vue-vendor-3bf-fPGP.js";import{K as d}from"./naive-ui-Bdxp09n2.js";import"./vendors-CKPV1OAU.js";import"./markdown-DyTJGI4N.js";import"./icons-B5Pl4lrD.js";const m={class:"config-templates-page"},p=s({__name:"ConfigTemplates",setup:s=>(s,p)=>(o(),e("div",m,[r(i(d),{title:"配置模版管理",bordered:!1},{default:t(()=>[r(a,{"in-drawer":!1,"hide-back":!0})]),_:1})]))},[["__scopeId","data-v-a6c39fe5"]]);export{p as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{d as e}from"./vendors-CKPV1OAU.js";import{a8 as a,f as l,r as n,w as s,h as c,K as t,a7 as o,c as i,o as u,a as d,W as r,X as v,Y as p,N as h,a0 as y,a3 as g,_ as k,p as m,L as f,F as T,$ as b,Z as x,v as C}from"./vue-vendor-3bf-fPGP.js";import{_ as w,u as $,a as _,R as q,g as z,b as S,c as L,d as I,e as j,f as O,h as N,i as M,j as A,k as F,l as D,s as P}from"./index-EMrm1wk-.js";import{E as U,o as E,n as V,m as J,ar as K,as as G,at as R,au as Z,av as B,a9 as H,i as W,A as X,aw as Y,ax as Q,T as ee,a as ae,b as le}from"./icons-B5Pl4lrD.js";import{L as ne,N as se,B as ce,s as te,b as oe,c as ie,a as ue,Z as de}from"./naive-ui-Bdxp09n2.js";import"./markdown-DyTJGI4N.js";const re={class:"channel-column"},ve={class:"drag-handle",title:"拖拽排序"},pe={class:"header-icon"},he={class:"channel-title"},ye={key:0,class:"claude-extra-area"},ge={key:0,class:"channel-content"},ke={class:"card"},me={class:"card-header"},fe={key:0,class:"runtime-badge"},Te={class:"card-body",style:{padding:"6px 10px"}},be={class:"proxy-info-row"},xe={class:"proxy-status"},Ce={class:"proxy-port"},we={class:"channel-name"},$e={class:"channel-quick-panel"},_e={class:"panel-title"},qe={key:0,class:"no-channels"},ze={key:1,class:"channel-quick-list"},Se={class:"channel-quick-info"},Le={class:"channel-quick-name"},Ie={key:0,class:"channel-switching-tip"},je={class:"channel-metrics"},Oe={class:"metric-item"},Ne={class:"metric-value"},Me={class:"metric-item"},Ae={class:"metric-value"},Fe={class:"metric-item"},De={class:"metric-value"},Pe={class:"metric-item"},Ue={class:"card"},Ee={class:"card-header compact"},Ve={class:"card-body",style:{padding:"8px 10px"}},Je={class:"quick-access-list"},Ke={class:"access-icon"},Ge={class:"access-content"},Re={class:"access-value"},Ze={class:"access-icon"},Be={class:"access-content"},He={class:"access-value"},We={class:"access-icon"},Xe={class:"access-content"},Ye={class:"access-goto"},Qe={class:"card-header compact"},ea={class:"card-body",style:{padding:"8px 10px"}},aa={class:"stats-inline stats-3col"},la={class:"stat-inline-item stat-requests"},na={class:"stat-info"},sa={class:"stat-inline-item stat-input"},ca={class:"stat-info"},ta={class:"stat-inline-item stat-output"},oa={class:"stat-info"},ia={key:1,class:"card logs-card"},ua={class:"card-header compact"},da={class:"card-body logs-card-body"},ra={class:"logs-table-wrapper"},va={key:0,class:"empty-logs"},pa={key:0,class:"action-content"},ha={class:"action-msg"},ya={class:"action-time"},ga={key:1,class:"action-content"},ka={class:"action-msg error-msg"},ma={class:"action-time error-time"},fa={class:"locked-content"},Ta={class:"lock-icon"},ba="channelLocks",xa=w({__name:"ChannelColumn",props:{channelType:{type:String,required:!0,validator:e=>["claude","codex","gemini","opencode"].includes(e)}},setup(e){const w=e,D=a(),P=ne(),{claudeProxy:xa,codexProxy:Ca,geminiProxy:wa,opencodeProxy:$a,claudeChannels:_a,codexChannels:qa,geminiChannels:za,opencodeChannels:Sa,schedulerState:La,getProxyState:Ia,startProxy:ja,stopProxy:Oa,getLogs:Na,clearLogsForSource:Ma,loadChannels:Aa,logLimit:Fa,statsInterval:Da}=$(),{dashboardData:Pa,loadDashboard:Ua}=_(),Ea={claude:{title:"ClaudeCode",subtitle:"智能编程助手",icon:J},codex:{title:"Codex-CLI",subtitle:"高效代码生成",icon:V},gemini:{title:"Gemini-CLI",subtitle:"多模态AI助手",icon:E},opencode:{title:"OpenCode",subtitle:"AI 代码助手",icon:U}},Va=l(()=>Ea[w.channelType].title);l(()=>Ea[w.channelType].subtitle);const Ja=l(()=>Ea[w.channelType].icon),Ka=l(()=>"claude"===w.channelType?xa.value:"codex"===w.channelType?Ca.value:"gemini"===w.channelType?wa.value:"opencode"===w.channelType?$a.value:{}),Ga=l(()=>"claude"===w.channelType?"Claude":"codex"===w.channelType?"Codex":"gemini"===w.channelType?"Gemini":"opencode"===w.channelType?"OpenCode":""),Ra=n({projects:0,sessions:0}),Za=n({requests:0,tokens:0,cost:0}),Ba=n({requests:0,tokens:0,cost:0}),Ha={requests:0,tokens:0,cost:3};let Wa={requests:null,tokens:null,cost:null};const Xa=n({requests:!1,tokens:!1,cost:!1}),Ya=l(()=>{var e,a,l;const n="claude"===w.channelType?"claude":"codex"===w.channelType?"codex":"opencode"===w.channelType?"opencode":"gemini";return(null==(l=null==(a=null==(e=Pa.value)?void 0:e.todayStats)?void 0:a[n])?void 0:l.byModel)||{}});function Qa(e,a,l,n=600){Wa[e]&&cancelAnimationFrame(Wa[e]),Xa.value[e]=!0;const s=Date.now(),c=()=>{const t=Date.now()-s,o=Math.min(t/n,1),i=1-Math.pow(1-o,2),u=a+(l-a)*i,d=Ha[e]??0,r=Math.pow(10,d),v=d>0?Math.round(u*r)/r:Math.round(u);Ba.value[e]=v,o<1?Wa[e]=requestAnimationFrame(c):Xa.value[e]=!1};Wa[e]=requestAnimationFrame(c)}const el=n(!1);function al(e){try{const a=localStorage.getItem(ba),l=a?JSON.parse(a):{};l[w.channelType]=e,localStorage.setItem(ba,JSON.stringify(l))}catch(a){}}const ll=n(function(){try{const e=localStorage.getItem(ba);if(e){return JSON.parse(e)[w.channelType]||!1}}catch(e){}return!1}());async function nl(){ll.value=!ll.value,al(ll.value);try{if(await F("channelLocks",w.channelType,ll.value),Pa.value){const e=Pa.value.uiConfig||{},a={...e.channelLocks||{}};a[w.channelType]=ll.value,Pa.value={...Pa.value,uiConfig:{...e,channelLocks:a}}}}catch(e){}}const sl=n(!0);function cl(e){e.detail&&void 0!==e.detail.showLogs&&(sl.value=e.detail.showLogs)}const tl=n(Date.now()),ol=l(()=>{if(!Ka.value.running)return"";const e=Ka.value.startTime;if(!e)return"";const a=tl.value-e;if(a<=0)return"";const l=Math.floor(a/1e3),n=Math.floor(l/3600),s=Math.floor(l%3600/60),c=l%60;let t="已运行 ";return n>0&&(t+=`${n}小时`),s>0&&(t+=`${s}分`),(c>0||0===n&&0===s)&&(t+=`${c}秒`),t}),il=n(null),ul=l(()=>Fa.value),dl={claude:Na("claude"),codex:Na("codex"),gemini:Na("gemini"),opencode:Na("opencode")},rl=l(()=>((dl[w.channelType]||dl.claude).value||[]).slice(0,ul.value));let vl=null,pl=null,hl=null,yl=!1,gl=null;const kl=l(()=>{let e=[];"claude"===w.channelType?e=_a.value||[]:"codex"===w.channelType?e=qa.value||[]:"gemini"===w.channelType?e=za.value||[]:"opencode"===w.channelType&&(e=Sa.value||[]);return[...e.filter(e=>!1!==e.enabled),...e.filter(e=>!1===e.enabled)]});function ml(e){const a=La[w.channelType];if(!a||!a.channels)return 0;const l=a.channels.find(a=>a.id===e);return l?l.inflight:0}const fl=l(()=>{var e;const a=kl.value.filter(e=>!1!==e.enabled).length;return(null==(e=Ka.value.proxy)?void 0:e.running)?`${a}个渠道调度中`:kl.value.length>0?`${a}个渠道已启用`:"无渠道"}),Tl=n({});function bl(e){return Tl.value[e]||{requests:0,tokens:0,cost:0}}async function xl(){var e;try{let a;if("claude"===w.channelType?a=await z():"codex"===w.channelType?a=await S():"gemini"===w.channelType?a=await L():"opencode"===w.channelType&&(a=await I()),a&&a.byChannel){const l={};for(const[n,s]of Object.entries(a.byChannel))l[n]={requests:s.requests||0,tokens:(null==(e=s.tokens)?void 0:e.total)||0,cost:s.cost||0};Tl.value=l}}catch(a){}}async function Cl(e){var a,l;const n=Ia(w.channelType);n.value.loading=!0;try{let a;a=e?await ja(w.channelType):await Oa(w.channelType,{refreshChannelsDrawer:!0}),!1!==a.success?P.success(e?`${Va.value} 代理已启动`:`${Va.value} 代理已停止`):(P.error(a.error||"操作失败"),n.value.running=!e)}catch(s){P.error((null==(l=null==(a=s.response)?void 0:a.data)?void 0:l.error)||s.message||"操作失败"),n.value.running=!e}finally{n.value.loading=!1}}function wl(){D.push({name:`${w.channelType}-projects`})}function $l(){D.push({name:`${w.channelType}-projects`})}s(rl,e=>{var a;const l=(null==(a=e[0])?void 0:a.id)||null;if(!l||l===vl)return void(vl=l);vl=l,gl&&clearTimeout(gl),gl=setTimeout(()=>{xl()},5e3);(!il.value||il.value.scrollTop<20)&&C(()=>{il.value&&(il.value.scrollTop=0)})}),s(()=>Za.value.requests,e=>{Qa("requests",Ba.value.requests,e,600)}),s(()=>Za.value.tokens,e=>{Qa("tokens",Ba.value.tokens,e,600)}),s(()=>Za.value.cost,e=>{Qa("cost",Ba.value.cost,e,600)}),s(()=>w.channelType,()=>{var e;vl=(null==(e=rl.value[0])?void 0:e.id)||null}),s(()=>{var e,a;return null==(a=null==(e=Pa.value)?void 0:e.counts)?void 0:a[w.channelType]},()=>{Sl()}),s(Da,()=>{yl&&function(){pl&&(clearInterval(pl),pl=null);const e=Da.value||30,a=Math.max(1e3*e,1e4);pl=setInterval(()=>{Ll(),xl()},a)}()});const _l=n({});function ql(e){return!!_l.value[e]}function zl(e,a){e&&(_l.value={..._l.value,[e]:!!a})}function Sl(){var e,a;const l=null==(a=null==(e=Pa.value)?void 0:e.counts)?void 0:a[w.channelType];Ra.value.projects=(null==l?void 0:l.projectCount)||0,Ra.value.sessions=(null==l?void 0:l.sessionCount)||0}async function Ll(){if(Pa.value&&Pa.value.todayStats){const e=Pa.value.todayStats[w.channelType];e&&(Za.value.requests=e.requests||0,Za.value.tokens=e.tokens||0,Za.value.cost=e.cost||0)}Sl()}function Il(){Ma(w.channelType)}function jl(){window.dispatchEvent(new CustomEvent("open-skills-drawer",{detail:{platform:w.channelType}}))}return c(async()=>{await Promise.all([Ua().then(()=>Ll()),xl()]),async function(){var e,a;try{if(Pa.value&&Pa.value.uiConfig)sl.value=!1!==(null==(e=Pa.value.uiConfig.panelVisibility)?void 0:e.showLogs);else{const e=await j();e.success&&e.config&&(sl.value=!1!==(null==(a=e.config.panelVisibility)?void 0:a.showLogs))}}catch(l){}}(),async function(){var e,a;try{let l=!1;if(Pa.value&&Pa.value.uiConfig)l=(null==(e=Pa.value.uiConfig.channelLocks)?void 0:e[w.channelType])||!1;else{const e=await j();e.success&&e.config&&(l=(null==(a=e.config.channelLocks)?void 0:a[w.channelType])||!1)}ll.value=l,al(l)}catch(l){}}(),window.addEventListener("panel-visibility-change",cl),Ba.value={...Za.value},yl=!0,hl=setInterval(()=>{tl.value=Date.now()},1e3)}),t(()=>{yl=!1,pl&&clearInterval(pl),hl&&clearInterval(hl),gl&&clearTimeout(gl),window.removeEventListener("panel-visibility-change",cl),Object.values(Wa).forEach(e=>{e&&cancelAnimationFrame(e)})}),(a,l)=>{const n=o("n-collapse");return u(),i("div",re,[d("div",{class:f(["channel-header",e.channelType])},[d("div",ve,[v(h(se),{size:16},{default:p(()=>[v(h(K))]),_:1})]),d("div",pe,[v(h(se),{size:20},{default:p(()=>[(u(),y(g(Ja.value)))]),_:1})]),d("h2",he,k(Va.value),1),["claude","codex","gemini","opencode"].includes(e.channelType)?(u(),i("div",ye,[v(h(te),{trigger:"hover"},{trigger:p(()=>[v(h(ce),{text:"",class:"skills-button",onClick:jl,title:"Skills 技能管理"},{icon:p(()=>[v(h(se),{size:18},{default:p(()=>[v(h(U))]),_:1})]),_:1})]),default:p(()=>[l[3]||(l[3]=m(" Skills 技能管理 ",-1))]),_:1})])):r("",!0),v(h(ce),{text:"",class:"lock-button",onClick:nl,title:ll.value?"解锁此列":"锁定此列"},{icon:p(()=>[v(h(se),{size:18},{default:p(()=>[ll.value?(u(),y(h(G),{key:0})):(u(),y(h(R),{key:1}))]),_:1})]),_:1},8,["title"])],2),ll.value?r("",!0):(u(),i("div",ge,[d("div",ke,[d("div",me,[v(h(se),{size:16},{default:p(()=>[v(h(Z))]),_:1}),l[4]||(l[4]=d("h3",{class:"card-title"},"代理控制",-1)),Ka.value.running&&ol.value?(u(),i("span",fe,k(ol.value),1)):r("",!0),v(h(oe),{value:Ka.value.running,"onUpdate:value":[l[0]||(l[0]=e=>Ka.value.running=e),Cl],loading:Ka.value.loading,size:"small",style:{"margin-left":"auto"}},null,8,["value","loading"])]),d("div",Te,[d("div",be,[d("div",xe,[d("div",{class:f(["status-dot",{active:Ka.value.running}])},null,2),v(h(ie),{type:Ka.value.running?"success":"default",style:{"font-size":"12px"}},{default:p(()=>[m(k(Ka.value.running?"运行中":"已停止"),1)]),_:1},8,["type"]),d("span",Ce,"端口: "+k(Ka.value.port),1)]),v(h(de),{trigger:"click",placement:"bottom",width:320,class:"channel-popover"},{trigger:p(()=>[v(h(ce),{text:"",size:"tiny",class:"channel-status"},{default:p(()=>[d("span",we,k(fl.value),1)]),_:1})]),default:p(()=>[d("div",$e,[d("div",_e,[l[6]||(l[6]=d("span",null,"渠道快捷管理",-1)),v(h(ie),{depth:"3",style:{"font-size":"11px"}},{default:p(()=>[...l[5]||(l[5]=[m("点击开关切换状态",-1)])]),_:1})]),0===kl.value.length?(u(),i("div",qe,[v(h(ie),{depth:"3"},{default:p(()=>[...l[7]||(l[7]=[m("暂无配置渠道",-1)])]),_:1})])):(u(),i("div",ze,[(u(!0),i(T,null,b(kl.value,e=>{var a,n;return u(),i("div",{key:e.id,class:f(["channel-quick-item",{disabled:!1===e.enabled}])},[d("div",Se,[d("span",Le,k(e.name),1),"frozen"===(null==(a=e.health)?void 0:a.status)?(u(),y(h(ue),{key:0,size:"tiny",type:"error",bordered:!1},{default:p(()=>[...l[8]||(l[8]=[m(" 冻结 ",-1)])]),_:1})):r("",!0),v(h(oe),{size:"small",value:!1!==e.enabled,"onUpdate:value":a=>async function(e,a){if(e&&!ql(e.id)){zl(e.id,!0);try{let l;"claude"===w.channelType?l=O:"codex"===w.channelType?l=N:"gemini"===w.channelType?l=M:"opencode"===w.channelType&&(l=A),l&&(await l(e.id,{enabled:a}),P.success(a?`渠道「${e.name}」已启用`:`渠道「${e.name}」已停用`),await Aa())}catch(l){P.error("操作失败: "+l.message)}finally{zl(e.id,!1)}}}(e,a),loading:ql(e.id),disabled:ql(e.id),style:{"margin-left":"auto"}},null,8,["value","onUpdate:value","loading","disabled"])]),ql(e.id)?(u(),i("div",Ie," 正在切换渠道状态... ")):r("",!0),d("div",je,[d("span",Oe,[l[9]||(l[9]=d("span",{class:"metric-label"},"请求",-1)),d("span",Ne,k(bl(e.id).requests),1)]),d("span",Me,[l[10]||(l[10]=d("span",{class:"metric-label"},"Tokens",-1)),d("span",Ae,k((n=bl(e.id).tokens,n>=1e6?(n/1e6).toFixed(1)+"M":n>=1e3?(n/1e3).toFixed(1)+"K":n.toString())),1)]),d("span",Fe,[l[11]||(l[11]=d("span",{class:"metric-label"},"权重",-1)),d("span",De,k(e.weight||1),1)]),d("span",Pe,[l[12]||(l[12]=d("span",{class:"metric-label"},"并发",-1)),d("span",{class:f(["metric-value",{active:ml(e.id)>0}])},k(ml(e.id))+k(e.maxConcurrency?`/${e.maxConcurrency}`:""),3)])])],2)}),128))]))])]),_:1})])])]),d("div",Ue,[d("div",Ee,[v(h(se),{size:14},{default:p(()=>[v(h(B))]),_:1}),l[13]||(l[13]=d("h3",{class:"card-title"},"快速访问",-1))]),d("div",Ve,[d("div",Je,[d("div",{class:"access-card access-card-projects clickable",onClick:wl},[d("div",Ke,[v(h(se),{size:16},{default:p(()=>[v(h(H))]),_:1})]),d("div",Ge,[l[14]||(l[14]=d("span",{class:"access-label"},"项目",-1)),d("span",Re,k(Ra.value.projects),1)])]),d("div",{class:"access-card access-card-sessions clickable",onClick:l[1]||(l[1]=e=>el.value=!0)},[d("div",Ze,[v(h(se),{size:16},{default:p(()=>[v(h(W))]),_:1})]),d("div",Be,[l[15]||(l[15]=d("span",{class:"access-label"},"最新对话",-1)),d("span",He,k(Ra.value.sessions),1)])]),d("div",{class:"access-card access-card-goto clickable",onClick:$l},[d("div",We,[v(h(se),{size:16},{default:p(()=>[v(h(X))]),_:1})]),d("div",Xe,[l[16]||(l[16]=d("span",{class:"access-label"},"前往",-1)),d("span",Ye,k(Ga.value),1)])])])])]),d("div",{class:f(["card stats-card",`stats-card-${e.channelType}`])},[d("div",Qe,[v(h(se),{size:14},{default:p(()=>[v(h(Y))]),_:1}),l[17]||(l[17]=d("h3",{class:"card-title"},"今日数据",-1))]),d("div",ea,[d("div",aa,[d("div",la,[l[19]||(l[19]=d("div",{class:"stat-icon-dot requests"},null,-1)),d("div",na,[l[18]||(l[18]=d("span",{class:"stat-label"},"请求",-1)),d("span",{class:f(["stat-value",{animating:Xa.value.requests}])},k(Ba.value.requests),3)])]),d("div",sa,[l[21]||(l[21]=d("div",{class:"stat-icon-dot tokens"},null,-1)),d("div",ca,[l[20]||(l[20]=d("span",{class:"stat-label"},"总 Tokens",-1)),d("span",{class:f(["stat-value",{animating:Xa.value.tokens}])},k((c=Ba.value.tokens,c>=1e6?(c/1e6).toFixed(1)+"M":c>=1e3?(c/1e3).toFixed(1)+"K":c.toString())),3)])]),d("div",ta,[l[23]||(l[23]=d("div",{class:"stat-icon-dot cost"},null,-1)),d("div",oa,[l[22]||(l[22]=d("span",{class:"stat-label"},"成本 / USD",-1)),d("span",{class:f(["stat-value",{animating:Xa.value.cost}])},k((s=Ba.value.cost,!s||Number.isNaN(s)?"$0":"$"+Number(s).toFixed(3))),3)])])])])],2),Object.keys(Ya.value).length>0?(u(),i("div",{key:0,class:f(["card chart-card",`chart-card-${e.channelType}`])},[v(n,{"default-expanded-names":[],accordion:""})],2)):r("",!0),sl.value?(u(),i("div",ia,[d("div",ua,[v(h(se),{size:14},{default:p(()=>[v(h(Q))]),_:1}),l[24]||(l[24]=d("h3",{class:"card-title"},"实时日志",-1)),v(h(ce),{text:"",size:"tiny",onClick:x(Il,["stop"]),style:{"margin-left":"auto"},title:"清空日志"},{icon:p(()=>[v(h(se),{size:14},{default:p(()=>[v(h(ee))]),_:1})]),_:1})]),d("div",da,[d("div",ra,[d("div",{class:f(["logs-table-header",`logs-header-${e.channelType}`])},[d("div",{class:f(["log-col col-channel",`col-channel-${e.channelType}`])},"渠道",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"请求",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"回复",2),"claude"===e.channelType?(u(),i(T,{key:0},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"写入",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"命中",2)],64)):"codex"===e.channelType?(u(),i(T,{key:1},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"推理",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):"gemini"===e.channelType?(u(),i(T,{key:2},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):"opencode"===e.channelType?(u(),i(T,{key:3},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"推理",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):r("",!0),d("div",{class:f(["log-col col-time",`col-time-${e.channelType}`])},"时间",2)],2),d("div",{class:"logs-container",ref_key:"logsContainer",ref:il},[0===rl.value.length?(u(),i("div",va,[v(h(se),{size:32,depth:"3",style:{"margin-bottom":"8px"}},{default:p(()=>[v(h(Q))]),_:1}),v(h(ie),{depth:"3",style:{"font-size":"12px","font-weight":"500"}},{default:p(()=>[...l[25]||(l[25]=[m("暂无实时日志",-1)])]),_:1}),v(h(ie),{depth:"3",style:{"font-size":"11px","margin-top":"4px"}},{default:p(()=>[...l[26]||(l[26]=[m("开启代理后将显示请求记录",-1)])]),_:1})])):r("",!0),(u(!0),i(T,null,b(rl.value,a=>{var l,n,s,c,t,o,y,g,b,x,C,w;return u(),i("div",{key:a.id,class:f(["log-row",{"action-row":"action"===a.type,"error-row":"error"===a.status,"new-log":a.isNew}])},["action"===a.type?(u(),i("div",pa,[v(h(se),{size:12,color:"#18a058"},{default:p(()=>[v(h(ae))]),_:1}),d("span",ha,k(a.message),1),d("span",ya,k(a.time),1)])):"error"===a.status?(u(),i("div",ga,[v(h(se),{size:12,color:"#d03050"},{default:p(()=>[v(h(le))]),_:1}),d("span",ka,k(a.channel)+": "+k(a.error||a.message||"请求失败"),1),d("span",ma,k(a.time),1)])):(u(),i(T,{key:2},[d("div",{class:f(["log-col col-channel",`col-channel-${e.channelType}`])},[v(h(ue),{size:"tiny",type:"success"},{default:p(()=>[m(k(a.channel),1)]),_:2},1024)],2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(l=a.tokens)?void 0:l.input)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(n=a.tokens)?void 0:n.output)||0),3),"claude"===e.channelType?(u(),i(T,{key:0},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(s=a.tokens)?void 0:s.cacheCreation)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(c=a.tokens)?void 0:c.cacheRead)||0),3)],64)):"codex"===e.channelType?(u(),i(T,{key:1},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(t=a.tokens)?void 0:t.reasoning)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(o=a.tokens)?void 0:o.cached)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(y=a.tokens)?void 0:y.total)||0),3)],64)):"gemini"===e.channelType?(u(),i(T,{key:2},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(g=a.tokens)?void 0:g.cached)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(b=a.tokens)?void 0:b.total)||0),3)],64)):"opencode"===e.channelType?(u(),i(T,{key:3},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(x=a.tokens)?void 0:x.reasoning)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(C=a.tokens)?void 0:C.cached)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(w=a.tokens)?void 0:w.total)||0),3)],64)):r("",!0),d("div",{class:f(["log-col col-time",`col-time-${e.channelType}`])},k(a.time),3)],64))],2)}),128))],512)])])])):r("",!0)])),ll.value?(u(),i("div",{key:1,class:f(["locked-overlay",`locked-${e.channelType}`])},[d("div",fa,[d("div",Ta,[v(h(se),{size:48},{default:p(()=>[v(h(G))]),_:1})]),l[28]||(l[28]=d("h3",{class:"locked-title"},"该渠道已锁定",-1)),v(h(ie),{depth:"3",class:"locked-hint"},{default:p(()=>[...l[27]||(l[27]=[m(" 点击上方按钮解锁以查看内容 ",-1)])]),_:1})])],2)):r("",!0),v(q,{visible:el.value,"onUpdate:visible":l[2]||(l[2]=e=>el.value=e),channel:e.channelType},null,8,["visible","channel"])]);var s,c}}},[["__scopeId","data-v-0bad694b"]]),Ca=n({theme:"light",panelVisibility:{showChannels:!0,showLogs:!0},channelLocks:{claude:!1,codex:!1,gemini:!1},channelCollapse:{claude:[],codex:[],gemini:[]},channelOrder:{claude:[],codex:[],gemini:[]}});let wa=!1,$a=null;async function _a(){return wa?Ca.value:$a||($a=(async()=>{try{const e=await j();e.success&&e.config&&(Ca.value=e.config,wa=!0)}catch(e){}finally{$a=null}return Ca.value})(),$a)}const qa={class:"dashboard-container"},za="dashboardChannelOrder",Sa=w({__name:"Home",setup(a){const l=["claude","codex","gemini","opencode"],{uiConfig:s,updateConfig:t,loadUIConfig:o}=(wa||_a(),{uiConfig:Ca,loadUIConfig:_a,saveConfig:async function(e){try{const a=await P(e);return!!a.success&&(Ca.value=a.config,!0)}catch(a){return!1}},updateConfig:async function(e,a){try{const l=await D(e,a);return!!l.success&&(Ca.value=l.config,!0)}catch(l){return!1}},updateNestedConfig:async function(e,a,l){try{const n=await F(e,a,l);return!!n.success&&(Ca.value=n.config,!0)}catch(n){return!1}}});function d(e){try{localStorage.setItem(za,JSON.stringify(e))}catch(a){}}const r=n(function(){try{const e=localStorage.getItem(za);if(e){const a=JSON.parse(e);if(Array.isArray(a)&&4===a.length)return a}}catch(e){}return l}().map(e=>({type:e})));async function g(){const e=r.value.map(e=>e.type);d(e),await t("dashboardChannelOrder",e)}return c(async()=>{if(await o(),s.value.dashboardChannelOrder&&Array.isArray(s.value.dashboardChannelOrder)&&4===s.value.dashboardChannelOrder.length){const e=s.value.dashboardChannelOrder;r.value=e.map(e=>({type:e})),d(e)}}),(a,l)=>(u(),i("div",qa,[v(h(e),{modelValue:r.value,"onUpdate:modelValue":l[0]||(l[0]=e=>r.value=e),class:"dashboard-grid","item-key":"type",animation:200,handle:".drag-handle",onEnd:g},{item:p(({element:e})=>[(u(),y(xa,{"channel-type":e.type,key:e.type},null,8,["channel-type"]))]),_:1},8,["modelValue"])]))}},[["__scopeId","data-v-06100f0f"]]);export{Sa as default};
|
|
1
|
+
import{d as e}from"./vendors-CKPV1OAU.js";import{a8 as a,f as l,r as n,w as s,h as c,K as t,a7 as o,c as i,o as u,a as d,W as r,X as v,Y as p,N as h,a0 as y,a3 as g,_ as k,p as m,L as f,F as T,$ as b,Z as x,v as C}from"./vue-vendor-3bf-fPGP.js";import{_ as w,u as $,a as _,R as q,g as z,b as S,c as L,d as I,e as j,f as O,h as N,i as M,j as A,k as F,l as D,s as P}from"./index-B1ujw2sM.js";import{E as U,o as E,n as V,m as J,ar as K,as as G,at as R,au as Z,av as B,a9 as H,i as W,A as X,aw as Y,ax as Q,T as ee,a as ae,b as le}from"./icons-B5Pl4lrD.js";import{L as ne,N as se,B as ce,s as te,b as oe,c as ie,a as ue,Z as de}from"./naive-ui-Bdxp09n2.js";import"./markdown-DyTJGI4N.js";const re={class:"channel-column"},ve={class:"drag-handle",title:"拖拽排序"},pe={class:"header-icon"},he={class:"channel-title"},ye={key:0,class:"claude-extra-area"},ge={key:0,class:"channel-content"},ke={class:"card"},me={class:"card-header"},fe={key:0,class:"runtime-badge"},Te={class:"card-body",style:{padding:"6px 10px"}},be={class:"proxy-info-row"},xe={class:"proxy-status"},Ce={class:"proxy-port"},we={class:"channel-name"},$e={class:"channel-quick-panel"},_e={class:"panel-title"},qe={key:0,class:"no-channels"},ze={key:1,class:"channel-quick-list"},Se={class:"channel-quick-info"},Le={class:"channel-quick-name"},Ie={key:0,class:"channel-switching-tip"},je={class:"channel-metrics"},Oe={class:"metric-item"},Ne={class:"metric-value"},Me={class:"metric-item"},Ae={class:"metric-value"},Fe={class:"metric-item"},De={class:"metric-value"},Pe={class:"metric-item"},Ue={class:"card"},Ee={class:"card-header compact"},Ve={class:"card-body",style:{padding:"8px 10px"}},Je={class:"quick-access-list"},Ke={class:"access-icon"},Ge={class:"access-content"},Re={class:"access-value"},Ze={class:"access-icon"},Be={class:"access-content"},He={class:"access-value"},We={class:"access-icon"},Xe={class:"access-content"},Ye={class:"access-goto"},Qe={class:"card-header compact"},ea={class:"card-body",style:{padding:"8px 10px"}},aa={class:"stats-inline stats-3col"},la={class:"stat-inline-item stat-requests"},na={class:"stat-info"},sa={class:"stat-inline-item stat-input"},ca={class:"stat-info"},ta={class:"stat-inline-item stat-output"},oa={class:"stat-info"},ia={key:1,class:"card logs-card"},ua={class:"card-header compact"},da={class:"card-body logs-card-body"},ra={class:"logs-table-wrapper"},va={key:0,class:"empty-logs"},pa={key:0,class:"action-content"},ha={class:"action-msg"},ya={class:"action-time"},ga={key:1,class:"action-content"},ka={class:"action-msg error-msg"},ma={class:"action-time error-time"},fa={class:"locked-content"},Ta={class:"lock-icon"},ba="channelLocks",xa=w({__name:"ChannelColumn",props:{channelType:{type:String,required:!0,validator:e=>["claude","codex","gemini","opencode"].includes(e)}},setup(e){const w=e,D=a(),P=ne(),{claudeProxy:xa,codexProxy:Ca,geminiProxy:wa,opencodeProxy:$a,claudeChannels:_a,codexChannels:qa,geminiChannels:za,opencodeChannels:Sa,schedulerState:La,getProxyState:Ia,startProxy:ja,stopProxy:Oa,getLogs:Na,clearLogsForSource:Ma,loadChannels:Aa,logLimit:Fa,statsInterval:Da}=$(),{dashboardData:Pa,loadDashboard:Ua}=_(),Ea={claude:{title:"ClaudeCode",subtitle:"智能编程助手",icon:J},codex:{title:"Codex-CLI",subtitle:"高效代码生成",icon:V},gemini:{title:"Gemini-CLI",subtitle:"多模态AI助手",icon:E},opencode:{title:"OpenCode",subtitle:"AI 代码助手",icon:U}},Va=l(()=>Ea[w.channelType].title);l(()=>Ea[w.channelType].subtitle);const Ja=l(()=>Ea[w.channelType].icon),Ka=l(()=>"claude"===w.channelType?xa.value:"codex"===w.channelType?Ca.value:"gemini"===w.channelType?wa.value:"opencode"===w.channelType?$a.value:{}),Ga=l(()=>"claude"===w.channelType?"Claude":"codex"===w.channelType?"Codex":"gemini"===w.channelType?"Gemini":"opencode"===w.channelType?"OpenCode":""),Ra=n({projects:0,sessions:0}),Za=n({requests:0,tokens:0,cost:0}),Ba=n({requests:0,tokens:0,cost:0}),Ha={requests:0,tokens:0,cost:3};let Wa={requests:null,tokens:null,cost:null};const Xa=n({requests:!1,tokens:!1,cost:!1}),Ya=l(()=>{var e,a,l;const n="claude"===w.channelType?"claude":"codex"===w.channelType?"codex":"opencode"===w.channelType?"opencode":"gemini";return(null==(l=null==(a=null==(e=Pa.value)?void 0:e.todayStats)?void 0:a[n])?void 0:l.byModel)||{}});function Qa(e,a,l,n=600){Wa[e]&&cancelAnimationFrame(Wa[e]),Xa.value[e]=!0;const s=Date.now(),c=()=>{const t=Date.now()-s,o=Math.min(t/n,1),i=1-Math.pow(1-o,2),u=a+(l-a)*i,d=Ha[e]??0,r=Math.pow(10,d),v=d>0?Math.round(u*r)/r:Math.round(u);Ba.value[e]=v,o<1?Wa[e]=requestAnimationFrame(c):Xa.value[e]=!1};Wa[e]=requestAnimationFrame(c)}const el=n(!1);function al(e){try{const a=localStorage.getItem(ba),l=a?JSON.parse(a):{};l[w.channelType]=e,localStorage.setItem(ba,JSON.stringify(l))}catch(a){}}const ll=n(function(){try{const e=localStorage.getItem(ba);if(e){return JSON.parse(e)[w.channelType]||!1}}catch(e){}return!1}());async function nl(){ll.value=!ll.value,al(ll.value);try{if(await F("channelLocks",w.channelType,ll.value),Pa.value){const e=Pa.value.uiConfig||{},a={...e.channelLocks||{}};a[w.channelType]=ll.value,Pa.value={...Pa.value,uiConfig:{...e,channelLocks:a}}}}catch(e){}}const sl=n(!0);function cl(e){e.detail&&void 0!==e.detail.showLogs&&(sl.value=e.detail.showLogs)}const tl=n(Date.now()),ol=l(()=>{if(!Ka.value.running)return"";const e=Ka.value.startTime;if(!e)return"";const a=tl.value-e;if(a<=0)return"";const l=Math.floor(a/1e3),n=Math.floor(l/3600),s=Math.floor(l%3600/60),c=l%60;let t="已运行 ";return n>0&&(t+=`${n}小时`),s>0&&(t+=`${s}分`),(c>0||0===n&&0===s)&&(t+=`${c}秒`),t}),il=n(null),ul=l(()=>Fa.value),dl={claude:Na("claude"),codex:Na("codex"),gemini:Na("gemini"),opencode:Na("opencode")},rl=l(()=>((dl[w.channelType]||dl.claude).value||[]).slice(0,ul.value));let vl=null,pl=null,hl=null,yl=!1,gl=null;const kl=l(()=>{let e=[];"claude"===w.channelType?e=_a.value||[]:"codex"===w.channelType?e=qa.value||[]:"gemini"===w.channelType?e=za.value||[]:"opencode"===w.channelType&&(e=Sa.value||[]);return[...e.filter(e=>!1!==e.enabled),...e.filter(e=>!1===e.enabled)]});function ml(e){const a=La[w.channelType];if(!a||!a.channels)return 0;const l=a.channels.find(a=>a.id===e);return l?l.inflight:0}const fl=l(()=>{var e;const a=kl.value.filter(e=>!1!==e.enabled).length;return(null==(e=Ka.value.proxy)?void 0:e.running)?`${a}个渠道调度中`:kl.value.length>0?`${a}个渠道已启用`:"无渠道"}),Tl=n({});function bl(e){return Tl.value[e]||{requests:0,tokens:0,cost:0}}async function xl(){var e;try{let a;if("claude"===w.channelType?a=await z():"codex"===w.channelType?a=await S():"gemini"===w.channelType?a=await L():"opencode"===w.channelType&&(a=await I()),a&&a.byChannel){const l={};for(const[n,s]of Object.entries(a.byChannel))l[n]={requests:s.requests||0,tokens:(null==(e=s.tokens)?void 0:e.total)||0,cost:s.cost||0};Tl.value=l}}catch(a){}}async function Cl(e){var a,l;const n=Ia(w.channelType);n.value.loading=!0;try{let a;a=e?await ja(w.channelType):await Oa(w.channelType,{refreshChannelsDrawer:!0}),!1!==a.success?P.success(e?`${Va.value} 代理已启动`:`${Va.value} 代理已停止`):(P.error(a.error||"操作失败"),n.value.running=!e)}catch(s){P.error((null==(l=null==(a=s.response)?void 0:a.data)?void 0:l.error)||s.message||"操作失败"),n.value.running=!e}finally{n.value.loading=!1}}function wl(){D.push({name:`${w.channelType}-projects`})}function $l(){D.push({name:`${w.channelType}-projects`})}s(rl,e=>{var a;const l=(null==(a=e[0])?void 0:a.id)||null;if(!l||l===vl)return void(vl=l);vl=l,gl&&clearTimeout(gl),gl=setTimeout(()=>{xl()},5e3);(!il.value||il.value.scrollTop<20)&&C(()=>{il.value&&(il.value.scrollTop=0)})}),s(()=>Za.value.requests,e=>{Qa("requests",Ba.value.requests,e,600)}),s(()=>Za.value.tokens,e=>{Qa("tokens",Ba.value.tokens,e,600)}),s(()=>Za.value.cost,e=>{Qa("cost",Ba.value.cost,e,600)}),s(()=>w.channelType,()=>{var e;vl=(null==(e=rl.value[0])?void 0:e.id)||null}),s(()=>{var e,a;return null==(a=null==(e=Pa.value)?void 0:e.counts)?void 0:a[w.channelType]},()=>{Sl()}),s(Da,()=>{yl&&function(){pl&&(clearInterval(pl),pl=null);const e=Da.value||30,a=Math.max(1e3*e,1e4);pl=setInterval(()=>{Ll(),xl()},a)}()});const _l=n({});function ql(e){return!!_l.value[e]}function zl(e,a){e&&(_l.value={..._l.value,[e]:!!a})}function Sl(){var e,a;const l=null==(a=null==(e=Pa.value)?void 0:e.counts)?void 0:a[w.channelType];Ra.value.projects=(null==l?void 0:l.projectCount)||0,Ra.value.sessions=(null==l?void 0:l.sessionCount)||0}async function Ll(){if(Pa.value&&Pa.value.todayStats){const e=Pa.value.todayStats[w.channelType];e&&(Za.value.requests=e.requests||0,Za.value.tokens=e.tokens||0,Za.value.cost=e.cost||0)}Sl()}function Il(){Ma(w.channelType)}function jl(){window.dispatchEvent(new CustomEvent("open-skills-drawer",{detail:{platform:w.channelType}}))}return c(async()=>{await Promise.all([Ua().then(()=>Ll()),xl()]),async function(){var e,a;try{if(Pa.value&&Pa.value.uiConfig)sl.value=!1!==(null==(e=Pa.value.uiConfig.panelVisibility)?void 0:e.showLogs);else{const e=await j();e.success&&e.config&&(sl.value=!1!==(null==(a=e.config.panelVisibility)?void 0:a.showLogs))}}catch(l){}}(),async function(){var e,a;try{let l=!1;if(Pa.value&&Pa.value.uiConfig)l=(null==(e=Pa.value.uiConfig.channelLocks)?void 0:e[w.channelType])||!1;else{const e=await j();e.success&&e.config&&(l=(null==(a=e.config.channelLocks)?void 0:a[w.channelType])||!1)}ll.value=l,al(l)}catch(l){}}(),window.addEventListener("panel-visibility-change",cl),Ba.value={...Za.value},yl=!0,hl=setInterval(()=>{tl.value=Date.now()},1e3)}),t(()=>{yl=!1,pl&&clearInterval(pl),hl&&clearInterval(hl),gl&&clearTimeout(gl),window.removeEventListener("panel-visibility-change",cl),Object.values(Wa).forEach(e=>{e&&cancelAnimationFrame(e)})}),(a,l)=>{const n=o("n-collapse");return u(),i("div",re,[d("div",{class:f(["channel-header",e.channelType])},[d("div",ve,[v(h(se),{size:16},{default:p(()=>[v(h(K))]),_:1})]),d("div",pe,[v(h(se),{size:20},{default:p(()=>[(u(),y(g(Ja.value)))]),_:1})]),d("h2",he,k(Va.value),1),["claude","codex","gemini","opencode"].includes(e.channelType)?(u(),i("div",ye,[v(h(te),{trigger:"hover"},{trigger:p(()=>[v(h(ce),{text:"",class:"skills-button",onClick:jl,title:"Skills 技能管理"},{icon:p(()=>[v(h(se),{size:18},{default:p(()=>[v(h(U))]),_:1})]),_:1})]),default:p(()=>[l[3]||(l[3]=m(" Skills 技能管理 ",-1))]),_:1})])):r("",!0),v(h(ce),{text:"",class:"lock-button",onClick:nl,title:ll.value?"解锁此列":"锁定此列"},{icon:p(()=>[v(h(se),{size:18},{default:p(()=>[ll.value?(u(),y(h(G),{key:0})):(u(),y(h(R),{key:1}))]),_:1})]),_:1},8,["title"])],2),ll.value?r("",!0):(u(),i("div",ge,[d("div",ke,[d("div",me,[v(h(se),{size:16},{default:p(()=>[v(h(Z))]),_:1}),l[4]||(l[4]=d("h3",{class:"card-title"},"代理控制",-1)),Ka.value.running&&ol.value?(u(),i("span",fe,k(ol.value),1)):r("",!0),v(h(oe),{value:Ka.value.running,"onUpdate:value":[l[0]||(l[0]=e=>Ka.value.running=e),Cl],loading:Ka.value.loading,size:"small",style:{"margin-left":"auto"}},null,8,["value","loading"])]),d("div",Te,[d("div",be,[d("div",xe,[d("div",{class:f(["status-dot",{active:Ka.value.running}])},null,2),v(h(ie),{type:Ka.value.running?"success":"default",style:{"font-size":"12px"}},{default:p(()=>[m(k(Ka.value.running?"运行中":"已停止"),1)]),_:1},8,["type"]),d("span",Ce,"端口: "+k(Ka.value.port),1)]),v(h(de),{trigger:"click",placement:"bottom",width:320,class:"channel-popover"},{trigger:p(()=>[v(h(ce),{text:"",size:"tiny",class:"channel-status"},{default:p(()=>[d("span",we,k(fl.value),1)]),_:1})]),default:p(()=>[d("div",$e,[d("div",_e,[l[6]||(l[6]=d("span",null,"渠道快捷管理",-1)),v(h(ie),{depth:"3",style:{"font-size":"11px"}},{default:p(()=>[...l[5]||(l[5]=[m("点击开关切换状态",-1)])]),_:1})]),0===kl.value.length?(u(),i("div",qe,[v(h(ie),{depth:"3"},{default:p(()=>[...l[7]||(l[7]=[m("暂无配置渠道",-1)])]),_:1})])):(u(),i("div",ze,[(u(!0),i(T,null,b(kl.value,e=>{var a,n;return u(),i("div",{key:e.id,class:f(["channel-quick-item",{disabled:!1===e.enabled}])},[d("div",Se,[d("span",Le,k(e.name),1),"frozen"===(null==(a=e.health)?void 0:a.status)?(u(),y(h(ue),{key:0,size:"tiny",type:"error",bordered:!1},{default:p(()=>[...l[8]||(l[8]=[m(" 冻结 ",-1)])]),_:1})):r("",!0),v(h(oe),{size:"small",value:!1!==e.enabled,"onUpdate:value":a=>async function(e,a){if(e&&!ql(e.id)){zl(e.id,!0);try{let l;"claude"===w.channelType?l=O:"codex"===w.channelType?l=N:"gemini"===w.channelType?l=M:"opencode"===w.channelType&&(l=A),l&&(await l(e.id,{enabled:a}),P.success(a?`渠道「${e.name}」已启用`:`渠道「${e.name}」已停用`),await Aa())}catch(l){P.error("操作失败: "+l.message)}finally{zl(e.id,!1)}}}(e,a),loading:ql(e.id),disabled:ql(e.id),style:{"margin-left":"auto"}},null,8,["value","onUpdate:value","loading","disabled"])]),ql(e.id)?(u(),i("div",Ie," 正在切换渠道状态... ")):r("",!0),d("div",je,[d("span",Oe,[l[9]||(l[9]=d("span",{class:"metric-label"},"请求",-1)),d("span",Ne,k(bl(e.id).requests),1)]),d("span",Me,[l[10]||(l[10]=d("span",{class:"metric-label"},"Tokens",-1)),d("span",Ae,k((n=bl(e.id).tokens,n>=1e6?(n/1e6).toFixed(1)+"M":n>=1e3?(n/1e3).toFixed(1)+"K":n.toString())),1)]),d("span",Fe,[l[11]||(l[11]=d("span",{class:"metric-label"},"权重",-1)),d("span",De,k(e.weight||1),1)]),d("span",Pe,[l[12]||(l[12]=d("span",{class:"metric-label"},"并发",-1)),d("span",{class:f(["metric-value",{active:ml(e.id)>0}])},k(ml(e.id))+k(e.maxConcurrency?`/${e.maxConcurrency}`:""),3)])])],2)}),128))]))])]),_:1})])])]),d("div",Ue,[d("div",Ee,[v(h(se),{size:14},{default:p(()=>[v(h(B))]),_:1}),l[13]||(l[13]=d("h3",{class:"card-title"},"快速访问",-1))]),d("div",Ve,[d("div",Je,[d("div",{class:"access-card access-card-projects clickable",onClick:wl},[d("div",Ke,[v(h(se),{size:16},{default:p(()=>[v(h(H))]),_:1})]),d("div",Ge,[l[14]||(l[14]=d("span",{class:"access-label"},"项目",-1)),d("span",Re,k(Ra.value.projects),1)])]),d("div",{class:"access-card access-card-sessions clickable",onClick:l[1]||(l[1]=e=>el.value=!0)},[d("div",Ze,[v(h(se),{size:16},{default:p(()=>[v(h(W))]),_:1})]),d("div",Be,[l[15]||(l[15]=d("span",{class:"access-label"},"最新对话",-1)),d("span",He,k(Ra.value.sessions),1)])]),d("div",{class:"access-card access-card-goto clickable",onClick:$l},[d("div",We,[v(h(se),{size:16},{default:p(()=>[v(h(X))]),_:1})]),d("div",Xe,[l[16]||(l[16]=d("span",{class:"access-label"},"前往",-1)),d("span",Ye,k(Ga.value),1)])])])])]),d("div",{class:f(["card stats-card",`stats-card-${e.channelType}`])},[d("div",Qe,[v(h(se),{size:14},{default:p(()=>[v(h(Y))]),_:1}),l[17]||(l[17]=d("h3",{class:"card-title"},"今日数据",-1))]),d("div",ea,[d("div",aa,[d("div",la,[l[19]||(l[19]=d("div",{class:"stat-icon-dot requests"},null,-1)),d("div",na,[l[18]||(l[18]=d("span",{class:"stat-label"},"请求",-1)),d("span",{class:f(["stat-value",{animating:Xa.value.requests}])},k(Ba.value.requests),3)])]),d("div",sa,[l[21]||(l[21]=d("div",{class:"stat-icon-dot tokens"},null,-1)),d("div",ca,[l[20]||(l[20]=d("span",{class:"stat-label"},"总 Tokens",-1)),d("span",{class:f(["stat-value",{animating:Xa.value.tokens}])},k((c=Ba.value.tokens,c>=1e6?(c/1e6).toFixed(1)+"M":c>=1e3?(c/1e3).toFixed(1)+"K":c.toString())),3)])]),d("div",ta,[l[23]||(l[23]=d("div",{class:"stat-icon-dot cost"},null,-1)),d("div",oa,[l[22]||(l[22]=d("span",{class:"stat-label"},"成本 / USD",-1)),d("span",{class:f(["stat-value",{animating:Xa.value.cost}])},k((s=Ba.value.cost,!s||Number.isNaN(s)?"$0":"$"+Number(s).toFixed(3))),3)])])])])],2),Object.keys(Ya.value).length>0?(u(),i("div",{key:0,class:f(["card chart-card",`chart-card-${e.channelType}`])},[v(n,{"default-expanded-names":[],accordion:""})],2)):r("",!0),sl.value?(u(),i("div",ia,[d("div",ua,[v(h(se),{size:14},{default:p(()=>[v(h(Q))]),_:1}),l[24]||(l[24]=d("h3",{class:"card-title"},"实时日志",-1)),v(h(ce),{text:"",size:"tiny",onClick:x(Il,["stop"]),style:{"margin-left":"auto"},title:"清空日志"},{icon:p(()=>[v(h(se),{size:14},{default:p(()=>[v(h(ee))]),_:1})]),_:1})]),d("div",da,[d("div",ra,[d("div",{class:f(["logs-table-header",`logs-header-${e.channelType}`])},[d("div",{class:f(["log-col col-channel",`col-channel-${e.channelType}`])},"渠道",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"请求",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"回复",2),"claude"===e.channelType?(u(),i(T,{key:0},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"写入",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"命中",2)],64)):"codex"===e.channelType?(u(),i(T,{key:1},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"推理",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):"gemini"===e.channelType?(u(),i(T,{key:2},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):"opencode"===e.channelType?(u(),i(T,{key:3},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"推理",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):r("",!0),d("div",{class:f(["log-col col-time",`col-time-${e.channelType}`])},"时间",2)],2),d("div",{class:"logs-container",ref_key:"logsContainer",ref:il},[0===rl.value.length?(u(),i("div",va,[v(h(se),{size:32,depth:"3",style:{"margin-bottom":"8px"}},{default:p(()=>[v(h(Q))]),_:1}),v(h(ie),{depth:"3",style:{"font-size":"12px","font-weight":"500"}},{default:p(()=>[...l[25]||(l[25]=[m("暂无实时日志",-1)])]),_:1}),v(h(ie),{depth:"3",style:{"font-size":"11px","margin-top":"4px"}},{default:p(()=>[...l[26]||(l[26]=[m("开启代理后将显示请求记录",-1)])]),_:1})])):r("",!0),(u(!0),i(T,null,b(rl.value,a=>{var l,n,s,c,t,o,y,g,b,x,C,w;return u(),i("div",{key:a.id,class:f(["log-row",{"action-row":"action"===a.type,"error-row":"error"===a.status,"new-log":a.isNew}])},["action"===a.type?(u(),i("div",pa,[v(h(se),{size:12,color:"#18a058"},{default:p(()=>[v(h(ae))]),_:1}),d("span",ha,k(a.message),1),d("span",ya,k(a.time),1)])):"error"===a.status?(u(),i("div",ga,[v(h(se),{size:12,color:"#d03050"},{default:p(()=>[v(h(le))]),_:1}),d("span",ka,k(a.channel)+": "+k(a.error||a.message||"请求失败"),1),d("span",ma,k(a.time),1)])):(u(),i(T,{key:2},[d("div",{class:f(["log-col col-channel",`col-channel-${e.channelType}`])},[v(h(ue),{size:"tiny",type:"success"},{default:p(()=>[m(k(a.channel),1)]),_:2},1024)],2),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(l=a.tokens)?void 0:l.input)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(n=a.tokens)?void 0:n.output)||0),3),"claude"===e.channelType?(u(),i(T,{key:0},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(s=a.tokens)?void 0:s.cacheCreation)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(c=a.tokens)?void 0:c.cacheRead)||0),3)],64)):"codex"===e.channelType?(u(),i(T,{key:1},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(t=a.tokens)?void 0:t.reasoning)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(o=a.tokens)?void 0:o.cached)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(y=a.tokens)?void 0:y.total)||0),3)],64)):"gemini"===e.channelType?(u(),i(T,{key:2},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(g=a.tokens)?void 0:g.cached)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(b=a.tokens)?void 0:b.total)||0),3)],64)):"opencode"===e.channelType?(u(),i(T,{key:3},[d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(x=a.tokens)?void 0:x.reasoning)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(C=a.tokens)?void 0:C.cached)||0),3),d("div",{class:f(["log-col col-token",`col-token-${e.channelType}`])},k((null==(w=a.tokens)?void 0:w.total)||0),3)],64)):r("",!0),d("div",{class:f(["log-col col-time",`col-time-${e.channelType}`])},k(a.time),3)],64))],2)}),128))],512)])])])):r("",!0)])),ll.value?(u(),i("div",{key:1,class:f(["locked-overlay",`locked-${e.channelType}`])},[d("div",fa,[d("div",Ta,[v(h(se),{size:48},{default:p(()=>[v(h(G))]),_:1})]),l[28]||(l[28]=d("h3",{class:"locked-title"},"该渠道已锁定",-1)),v(h(ie),{depth:"3",class:"locked-hint"},{default:p(()=>[...l[27]||(l[27]=[m(" 点击上方按钮解锁以查看内容 ",-1)])]),_:1})])],2)):r("",!0),v(q,{visible:el.value,"onUpdate:visible":l[2]||(l[2]=e=>el.value=e),channel:e.channelType},null,8,["visible","channel"])]);var s,c}}},[["__scopeId","data-v-0bad694b"]]),Ca=n({theme:"light",panelVisibility:{showChannels:!0,showLogs:!0},channelLocks:{claude:!1,codex:!1,gemini:!1},channelCollapse:{claude:[],codex:[],gemini:[]},channelOrder:{claude:[],codex:[],gemini:[]}});let wa=!1,$a=null;async function _a(){return wa?Ca.value:$a||($a=(async()=>{try{const e=await j();e.success&&e.config&&(Ca.value=e.config,wa=!0)}catch(e){}finally{$a=null}return Ca.value})(),$a)}const qa={class:"dashboard-container"},za="dashboardChannelOrder",Sa=w({__name:"Home",setup(a){const l=["claude","codex","gemini","opencode"],{uiConfig:s,updateConfig:t,loadUIConfig:o}=(wa||_a(),{uiConfig:Ca,loadUIConfig:_a,saveConfig:async function(e){try{const a=await P(e);return!!a.success&&(Ca.value=a.config,!0)}catch(a){return!1}},updateConfig:async function(e,a){try{const l=await D(e,a);return!!l.success&&(Ca.value=l.config,!0)}catch(l){return!1}},updateNestedConfig:async function(e,a,l){try{const n=await F(e,a,l);return!!n.success&&(Ca.value=n.config,!0)}catch(n){return!1}}});function d(e){try{localStorage.setItem(za,JSON.stringify(e))}catch(a){}}const r=n(function(){try{const e=localStorage.getItem(za);if(e){const a=JSON.parse(e);if(Array.isArray(a)&&4===a.length)return a}}catch(e){}return l}().map(e=>({type:e})));async function g(){const e=r.value.map(e=>e.type);d(e),await t("dashboardChannelOrder",e)}return c(async()=>{if(await o(),s.value.dashboardChannelOrder&&Array.isArray(s.value.dashboardChannelOrder)&&4===s.value.dashboardChannelOrder.length){const e=s.value.dashboardChannelOrder;r.value=e.map(e=>({type:e})),d(e)}}),(a,l)=>(u(),i("div",qa,[v(h(e),{modelValue:r.value,"onUpdate:modelValue":l[0]||(l[0]=e=>r.value=e),class:"dashboard-grid","item-key":"type",animation:200,handle:".drag-handle",onEnd:g},{item:p(({element:e})=>[(u(),y(xa,{"channel-type":e.type,key:e.type},null,8,["channel-type"]))]),_:1},8,["modelValue"])]))}},[["__scopeId","data-v-06100f0f"]]);export{Sa as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as a,P as s}from"./index-
|
|
1
|
+
import{_ as a,P as s}from"./index-B1ujw2sM.js";import{c as r,o as e,X as o,Y as i,N as d}from"./vue-vendor-3bf-fPGP.js";import{K as n}from"./naive-ui-Bdxp09n2.js";import"./vendors-CKPV1OAU.js";import"./markdown-DyTJGI4N.js";import"./icons-B5Pl4lrD.js";const t={class:"plugin-manager"},m=a({__name:"PluginManager",setup:a=>(a,m)=>(e(),r("div",t,[o(d(n),{title:"Plugins 插件管理",bordered:!1},{default:i(()=>[o(s,{"in-drawer":!1,"hide-back":!0})]),_:1})]))},[["__scopeId","data-v-6d92033b"]]);export{m as default};
|