cli-link 0.0.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 +271 -0
- package/bin/agentpilot.js +239 -0
- package/dist/client/assets/History-DR_K6WbO.js +3 -0
- package/dist/client/assets/MarkdownRenderer-D9IwexPM.js +1 -0
- package/dist/client/assets/PageTopBar-SnTIrSb5.js +1 -0
- package/dist/client/assets/Session-EFYFIC_X.js +11 -0
- package/dist/client/assets/Settings-DgmHC_Hw.js +1 -0
- package/dist/client/assets/Workspace-CJvVQVzU.js +8 -0
- package/dist/client/assets/WorkspaceLinkedText-D6hNg0T9.js +2 -0
- package/dist/client/assets/code-highlight-CEcsuMpw.js +1 -0
- package/dist/client/assets/index-Bk4_acsd.css +1 -0
- package/dist/client/assets/index-C89UCwGk.js +2 -0
- package/dist/client/assets/vendor-icons-S_ObYVVf.js +331 -0
- package/dist/client/assets/vendor-markdown-BDwu-Ux6.js +35 -0
- package/dist/client/assets/vendor-motion-n6Lx6G4a.js +9 -0
- package/dist/client/assets/vendor-react-DSV5aFEg.js +67 -0
- package/dist/client/assets/vendor-virtual-CcftJrIC.js +4 -0
- package/dist/client/favicon.svg +18 -0
- package/dist/client/icons/apple-touch-icon.png +0 -0
- package/dist/client/icons/icon-192.png +0 -0
- package/dist/client/icons/icon-512.png +0 -0
- package/dist/client/index.html +34 -0
- package/dist/client/manifest.webmanifest +59 -0
- package/dist/client/sw.js +143 -0
- package/dist/client//344/273/243/347/240/201/351/241/265/351/235/242.png +0 -0
- package/dist/client//345/216/206/345/217/262/350/256/260/345/275/225.png +0 -0
- package/dist/client//345/257/271/350/257/235/351/241/265/351/235/242.png +0 -0
- package/dist/client//350/256/276/347/275/256/351/241/265/351/235/242.png +0 -0
- package/dist/server/cli-manager.js +1532 -0
- package/dist/server/codex-history.js +280 -0
- package/dist/server/index.js +2097 -0
- package/dist/server/store.js +594 -0
- package/dist/server/terminal-qr.js +317 -0
- package/package.json +71 -0
package/README.md
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# AgentPilot - AI Agent 遥控器
|
|
2
|
+
|
|
3
|
+
AgentPilot 是一个移动端优先的 AI 编程 Agent 控制台。它把运行在个人电脑/开发机上的 Claude Code CLI / Codex CLI 变成一个可以在手机浏览器里遥控的工作台:发任务、看输出、审确认、回答问题、切换工作目录、回看历史记录。
|
|
4
|
+
|
|
5
|
+
## 典型场景
|
|
6
|
+
|
|
7
|
+
你让 Agent 在电脑上跑一个长任务,然后离开工位。AgentPilot 可以在手机上继续显示执行进度、工具调用、命令结果和最终回复;如果 Agent 等你确认或补充信息,也可以直接在手机上处理。
|
|
8
|
+
|
|
9
|
+
它也适合这些场景:
|
|
10
|
+
|
|
11
|
+
- 在会议、通勤或离开电脑时继续盯 Agent 任务。
|
|
12
|
+
- 远程批准或拒绝 CLI 的确认请求。
|
|
13
|
+
- 手机上继续发下一条指令,或者中断当前任务。
|
|
14
|
+
- 在多个仓库之间切换工作目录。
|
|
15
|
+
- 任务结束后从历史记录里复盘 Agent 做过什么。
|
|
16
|
+
|
|
17
|
+
## 截图
|
|
18
|
+
|
|
19
|
+
| 对话页面 | 代码页面 |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| <img src="public/对话页面.png" alt="对话页面" width="280"> | <img src="public/代码页面.png" alt="代码页面" width="280"> |
|
|
22
|
+
|
|
23
|
+
| 历史记录 | 设置页面 |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| <img src="public/历史记录.png" alt="历史记录" width="280"> | <img src="public/设置页面.png" alt="设置页面" width="280"> |
|
|
26
|
+
|
|
27
|
+
## 代码页面
|
|
28
|
+
|
|
29
|
+
代码页面用于在手机上查看当前工作目录的文件和 Git 变更,适合在离开电脑时快速确认 Agent 修改了什么、是否需要继续追问或直接提交。
|
|
30
|
+
|
|
31
|
+
- 代码浏览:按当前设置的工作目录展示文件夹和文件,支持逐级进入目录、返回上级目录,并在底部弹层中预览文件内容。
|
|
32
|
+
- 文件预览:文本和代码文件会按扩展名做语法高亮;图片文件可直接预览;过大的文件和二进制文件会给出提示,避免在手机端加载异常内容。
|
|
33
|
+
- 变更查看:在 Git 仓库中显示当前分支、变更文件列表和文件状态,点开单个文件后可以查看结构化 diff,包括新增、删除和上下文行。
|
|
34
|
+
- 暂存控制:支持对单个文件暂存/取消暂存,也支持全部暂存/全部取消,方便在手机上整理本次提交范围。
|
|
35
|
+
- 代码提交:暂存文件后可以打开提交面板,手动填写 Commit 信息,或让底层 CLI 基于已暂存变更和最近提交生成提交信息,再直接执行 `git commit`。
|
|
36
|
+
|
|
37
|
+
## 启动
|
|
38
|
+
|
|
39
|
+
### 业务方 NPX 启动
|
|
40
|
+
|
|
41
|
+
前置条件:
|
|
42
|
+
|
|
43
|
+
- Node.js 20 或更高版本
|
|
44
|
+
- 已安装并登录 Claude Code CLI 或 Codex CLI
|
|
45
|
+
|
|
46
|
+
在要遥控的业务仓库根目录执行:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx cli-link
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
首次运行时,`npx` 可能会提示确认安装远程包;如果用于脚本或自动化场景,可以使用 `npx -y cli-link` 跳过确认。
|
|
53
|
+
|
|
54
|
+
启动后终端会打印本机和手机访问地址:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
AgentPilot READY
|
|
58
|
+
Mobile-first remote console for your local agent
|
|
59
|
+
|
|
60
|
+
OPEN ON MOBILE
|
|
61
|
+
Mobile http://192.168.1.23:5101/?token=...
|
|
62
|
+
Scan use the QR code below
|
|
63
|
+
|
|
64
|
+
QR CODE
|
|
65
|
+
[terminal QR code]
|
|
66
|
+
|
|
67
|
+
LOCAL ACCESS
|
|
68
|
+
Local http://localhost:5101/?token=...
|
|
69
|
+
Token enabled, required for browser/API/WebSocket access
|
|
70
|
+
|
|
71
|
+
RUNTIME
|
|
72
|
+
Workdir /path/to/your-repo
|
|
73
|
+
CLI codex (codex)
|
|
74
|
+
|
|
75
|
+
MACOS LONG TASKS
|
|
76
|
+
手机长时间遥控任务前,建议先开启 Mac 防休眠,避免息屏后任务中断。
|
|
77
|
+
System Settings > Battery > Options >
|
|
78
|
+
开启“使用电源适配器供电且显示器关闭时,防止自动进入睡眠”。
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
默认行为:
|
|
82
|
+
|
|
83
|
+
- 监听 `0.0.0.0:5101`,电脑访问 `http://localhost:5101`,手机访问终端打印的 `Mobile` 地址。
|
|
84
|
+
- 终端会为第一个 `Mobile` 地址打印二维码,手机扫码即可打开。
|
|
85
|
+
- NPX 启动默认生成一次性访问 token;缺少 token 或校验 cookie 时,前端页面、后端 API 和 WebSocket 都会返回 401。
|
|
86
|
+
- 使用执行 `npx` 时的当前目录作为工作目录。
|
|
87
|
+
- 同一个 server 进程内,如果在前端切换了工作目录,刷新或重新打开前端会保持最近一次切换后的目录;重启 server 后会重新以启动命令所在目录作为默认工作目录。
|
|
88
|
+
- 优先自动识别 `codex`,其次识别 `claude`;未识别到时,可以在设置页手动配置 CLI 类型和命令。
|
|
89
|
+
- 前端、后端 API 和 WebSocket 共用同一个访问端口。
|
|
90
|
+
|
|
91
|
+
常用参数:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npx cli-link --port 5101
|
|
95
|
+
npx cli-link --host 0.0.0.0
|
|
96
|
+
npx cli-link --token your-fixed-token
|
|
97
|
+
npx cli-link --no-token
|
|
98
|
+
npx cli-link --workdir /path/to/project
|
|
99
|
+
npx cli-link --cli codex
|
|
100
|
+
npx cli-link --cli claude
|
|
101
|
+
npx cli-link --cli-command /path/to/codex
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`--token` 适合需要固定访问地址的受控环境;`--no-token` 只建议在本机或完全可信网络中临时使用。
|
|
105
|
+
|
|
106
|
+
当前最低 Node 版本是 20,主要由运行时数据库依赖 `better-sqlite3@12.9.0` 决定;Node 18 不在该依赖的支持范围内。
|
|
107
|
+
|
|
108
|
+
如需固定版本:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npx cli-link@0.1.0
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 发布到公开 npm
|
|
115
|
+
|
|
116
|
+
发布前确认包名、版本和打包内容:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pnpm publish:dry-run
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
发布到公开 npm registry:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pnpm publish:public
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
发布命令会使用 `https://registry.npmjs.org`,不受本机默认 registry 配置影响。发布前需要先完成 npm 登录,并确认当前版本号还没有发布过。
|
|
129
|
+
|
|
130
|
+
### 本地开发调试
|
|
131
|
+
|
|
132
|
+
前置条件:
|
|
133
|
+
|
|
134
|
+
- Node.js 20 或更高版本
|
|
135
|
+
- pnpm 10.32.1
|
|
136
|
+
- 已安装并登录 Claude Code CLI 或 Codex CLI
|
|
137
|
+
|
|
138
|
+
当前项目开发调试使用 pnpm 10.32.1;业务方通过 NPX 启动时不需要安装 pnpm。
|
|
139
|
+
本地开发调试默认不启用 token 校验,保持前端 `5101`、后端 `3101` 的现有联调方式;如需临时验证 token 逻辑,可以给后端设置 `AGENTPILOT_AUTH_TOKEN` 后再用带 `?token=...` 的前端地址打开。
|
|
140
|
+
|
|
141
|
+
安装依赖:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pnpm install
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
启动前端和后端:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
pnpm dev:all
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
访问地址:
|
|
154
|
+
|
|
155
|
+
- 电脑本机:`http://localhost:5101`
|
|
156
|
+
- 手机访问:先挂上公司 VPN,再打开 `http://<电脑局域网 IP>:5101`
|
|
157
|
+
|
|
158
|
+
电脑和手机需要在同一个局域网或公司 VPN 网络内。例如电脑 IP 是 `192.168.1.23`,手机挂上公司 VPN 后在浏览器打开:
|
|
159
|
+
|
|
160
|
+
```text
|
|
161
|
+
http://192.168.1.23:5101
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
如果启动后终端打印了多个 `Network` 地址,选择手机挂公司 VPN 后能访问的那个 IP 即可;其它地址通常是不同网卡、VPN 或虚拟网卡,可以忽略。
|
|
165
|
+
|
|
166
|
+
进入设置页后,选择 CLI 类型、CLI 命令和工作目录,点击启动 CLI,然后回到会话页发送任务。
|
|
167
|
+
|
|
168
|
+
### HTTPS/WSS 与 PWA
|
|
169
|
+
|
|
170
|
+
如果需要在手机上安装为 PWA,或启用 Service Worker 缓存,需要使用 HTTPS 访问前端,并让后端 WebSocket 走 WSS。NPX 启动可以直接传入证书:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
npx cli-link --https-key /path/to/key.pem --https-cert /path/to/cert.pem
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
本地开发调试时,AgentPilot 也支持通过环境变量给前端 Vite 服务和后端服务同时启用同一套证书:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
AGENTPILOT_HTTPS_KEY=/path/to/key.pem AGENTPILOT_HTTPS_CERT=/path/to/cert.pem pnpm dev:all
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
启用后访问地址会变为:
|
|
183
|
+
|
|
184
|
+
- 电脑本机:`https://localhost:5101`
|
|
185
|
+
- 手机访问:`https://<电脑局域网 IP>:5101`
|
|
186
|
+
|
|
187
|
+
证书需要被手机信任,并且证书的 SAN 需要包含实际访问的主机名或 IP。NPX 启动时前端、后端 API 和 WebSocket 共用同一个 HTTPS/WSS 端口;本地开发调试时前端仍在 `5101`,后端 API 和 WebSocket 仍在 `3101`。
|
|
188
|
+
|
|
189
|
+
PWA manifest 会随页面加载。Service Worker 默认只在生产构建中注册;如需在开发服务中验证 PWA 行为,可以额外加上:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
VITE_ENABLE_PWA_DEV=true AGENTPILOT_HTTPS_KEY=/path/to/key.pem AGENTPILOT_HTTPS_CERT=/path/to/cert.pem pnpm dev:all
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
当前 Service Worker 只缓存应用壳层和静态资源,并显式避开 `/api/*`。会话、历史、文件内容、Git diff、确认/提交等动态数据不会被离线缓存或自动重放。
|
|
196
|
+
|
|
197
|
+
### 防止息屏后休眠
|
|
198
|
+
|
|
199
|
+
如果要离开电脑、从手机继续遥控长任务,建议先打开 macOS 的防休眠选项:
|
|
200
|
+
|
|
201
|
+
1. 打开 `系统设置` -> `电池` -> `选项...`。
|
|
202
|
+
2. 开启“使用电源适配器供电且显示器关闭时,防止自动进入睡眠”。
|
|
203
|
+
3. 点击“完成”。
|
|
204
|
+
|
|
205
|
+
这样电脑接入电源且显示器关闭后,AgentPilot 后端和底层 CLI 仍可以继续运行。
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
## 安全风险与使用边界
|
|
209
|
+
|
|
210
|
+
AgentPilot 的设计目标是个人开发机上的 Agent 遥控器,不是多用户协作平台,也不是可直接暴露到公网的服务。NPX 启动默认开启轻量 token 校验:启动链接会携带一次性 token,服务端校验通过后写入 HttpOnly cookie;缺少有效 token 或 cookie 的客户端不能访问前端页面、后端 API 或 WebSocket。这个机制可以降低 IP/端口泄漏带来的误接入风险,但不等价于完整登录系统,也不提供 Origin 校验、CSRF 防护或 TLS 终止。
|
|
211
|
+
|
|
212
|
+
使用时建议遵守这些边界:
|
|
213
|
+
|
|
214
|
+
- 只在可信的本机、内网或受控 VPN 中使用;不要把 NPX 启动的 `5101` 单端口,或本地开发调试的 `5101` 前端端口和 `3101` 后端 HTTP/WebSocket 端口直接暴露到公网。
|
|
215
|
+
- 如果只需要本机访问,优先把前端和后端改为监听 `127.0.0.1`,或者用系统防火墙限制可访问来源。
|
|
216
|
+
- 如果必须跨网络访问,建议放在带身份认证和 HTTPS/WSS 的反向代理或安全隧道后面,不要直接转发裸 HTTP/WebSocket 端口。
|
|
217
|
+
- 公司 VPN 只解决“网络可达”问题,不等于应用层鉴权;同一 VPN 或同一网段内的非预期客户端仍然可能访问。
|
|
218
|
+
- 项目内置的 HTTPS/WSS 只解决传输加密和 PWA 安全上下文;安装为 PWA 后仍应只在可信网络中使用。
|
|
219
|
+
|
|
220
|
+
### CLI 权限风险
|
|
221
|
+
|
|
222
|
+
项目默认使用高权限 CLI 参数运行底层 Agent:
|
|
223
|
+
|
|
224
|
+
- Claude Code:`--dangerously-skip-permissions`
|
|
225
|
+
- Codex CLI:`--dangerously-bypass-approvals-and-sandbox`
|
|
226
|
+
|
|
227
|
+
这意味着底层 CLI 可能拥有完整的本地文件读写和命令执行能力,且很多操作不会再经过原 CLI 的逐项权限确认。AgentPilot 的“按键确认/自动确认”只是遥控流程的一部分,不能当作安全沙箱或权限边界。恶意提示词、错误操作、被他人接入的浏览器客户端,或模型误判都可能导致代码被改写、文件被删除、命令被执行、凭据被读取,或向外部服务发送敏感内容。
|
|
228
|
+
|
|
229
|
+
建议:
|
|
230
|
+
|
|
231
|
+
- 只在可信仓库和可接受回滚的工作区中运行;重要操作前保持干净的 Git 状态。
|
|
232
|
+
- 不要把工作目录设为整个主目录、包含生产密钥的目录,或包含大量无关敏感文件的目录。
|
|
233
|
+
- 高风险任务优先使用专用低权限系统用户、容器、虚拟机或临时开发环境运行。
|
|
234
|
+
- 不要在不可信提示词、网页内容、日志、Issue、PR 描述或第三方文件上下文中启用自动确认。
|
|
235
|
+
- 如果要把项目改造成长期服务,优先移除这些危险参数,并恢复底层 CLI 的沙箱和人工审批机制。
|
|
236
|
+
|
|
237
|
+
### 数据落盘风险
|
|
238
|
+
|
|
239
|
+
会话消息、用户输入、AI 输出、工具调用参数、命令输出、权限请求和历史任务会写入本机 SQLite 数据库:
|
|
240
|
+
|
|
241
|
+
```text
|
|
242
|
+
~/.agentpilot/data.db
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
这些内容可能包含源码片段、文件路径、命令输出、错误栈、密钥片段或业务信息。后端 HTTP/WebSocket 可达的客户端可以读取当前会话和历史任务;README 中的“清空历史”也不应被视为安全擦除,因为底层消息记录可能仍保留在 SQLite 数据库中。
|
|
246
|
+
|
|
247
|
+
建议:
|
|
248
|
+
|
|
249
|
+
- 不要在提示词、命令输出或工具调用中粘贴长期有效的密钥、Token、Cookie、私钥或生产数据。
|
|
250
|
+
- 定期清理或加密备份 `~/.agentpilot/data.db`;需要彻底清除敏感会话时,停止服务后手动处理该数据库文件。
|
|
251
|
+
- 不要把 `~/.agentpilot` 目录加入同步盘、共享目录或 Git 仓库。
|
|
252
|
+
|
|
253
|
+
### 目录枚举与文件路径泄露
|
|
254
|
+
|
|
255
|
+
目录选择器和 `@` 文件选择器会通过后端 HTTP API 读取服务端机器上的目录和文件名。当前实现会隐藏点号开头的条目,但连接者仍可向上级目录导航,并看到可读目录结构、文件名和绝对路径。这些信息本身可能暴露项目名称、用户目录、客户名称或内部代号。
|
|
256
|
+
|
|
257
|
+
建议:
|
|
258
|
+
|
|
259
|
+
- 只让可信设备连接 AgentPilot。
|
|
260
|
+
- 把工作目录限制在当前项目目录或临时工作区。
|
|
261
|
+
- 不要依赖前端选择器来隔离文件系统权限;真正的隔离应通过操作系统账号、容器或沙箱实现。
|
|
262
|
+
|
|
263
|
+
### 模型与供应链风险
|
|
264
|
+
|
|
265
|
+
底层 Claude Code / Codex CLI 可能会把提示词、选中的文件内容、命令输出和上下文发送到对应模型服务,具体取决于 CLI 配置、登录账号和工具调用行为。使用前应确认团队对源码、日志、客户数据和密钥材料进入外部模型服务的合规要求。
|
|
266
|
+
|
|
267
|
+
依赖方面,已在 2026-05-12 执行过一次 `pnpm audit --prod`,结果为 `No known vulnerabilities found`。漏洞库会持续变化,后续升级依赖或长期使用前仍应重新执行:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
pnpm audit --prod
|
|
271
|
+
```
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { accessSync, constants, existsSync, readFileSync, statSync } from 'node:fs';
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
import { dirname, delimiter, isAbsolute, join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const packageRoot = resolve(__dirname, '..');
|
|
9
|
+
const packageJsonPath = join(packageRoot, 'package.json');
|
|
10
|
+
|
|
11
|
+
function readPackageJson() {
|
|
12
|
+
return JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function printHelp() {
|
|
16
|
+
const pkg = readPackageJson();
|
|
17
|
+
console.log(`AgentPilot ${pkg.version}
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
npx ${pkg.name} [options]
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--host <host> Host to bind. Default: 0.0.0.0
|
|
24
|
+
--port <port> Port to listen on. Default: 5101
|
|
25
|
+
--token <token> Fixed access token. Default: random per startup
|
|
26
|
+
--no-token Disable access token checks
|
|
27
|
+
--workdir <path> Project directory. Default: current directory
|
|
28
|
+
--cli <codex|claude> CLI preset. Auto-detected when possible
|
|
29
|
+
--cli-command <command> CLI executable path or command name
|
|
30
|
+
--https-key <path> HTTPS private key
|
|
31
|
+
--https-cert <path> HTTPS certificate
|
|
32
|
+
-h, --help Show help
|
|
33
|
+
-v, --version Show version
|
|
34
|
+
`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readOptionValue(args, index, name) {
|
|
38
|
+
const value = args[index + 1];
|
|
39
|
+
if (!value || value.startsWith('-')) {
|
|
40
|
+
throw new Error(`${name} requires a value`);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseArgs(argv) {
|
|
46
|
+
const options = {};
|
|
47
|
+
const aliases = new Map([
|
|
48
|
+
['-h', '--help'],
|
|
49
|
+
['-v', '--version'],
|
|
50
|
+
['-p', '--port'],
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
54
|
+
if (argv[i] === '--') continue;
|
|
55
|
+
|
|
56
|
+
const raw = aliases.get(argv[i]) || argv[i];
|
|
57
|
+
const eqIndex = raw.indexOf('=');
|
|
58
|
+
const name = eqIndex > 0 ? raw.slice(0, eqIndex) : raw;
|
|
59
|
+
const inlineValue = eqIndex > 0 ? raw.slice(eqIndex + 1) : undefined;
|
|
60
|
+
|
|
61
|
+
switch (name) {
|
|
62
|
+
case '--help':
|
|
63
|
+
options.help = true;
|
|
64
|
+
break;
|
|
65
|
+
case '--version':
|
|
66
|
+
options.version = true;
|
|
67
|
+
break;
|
|
68
|
+
case '--no-token':
|
|
69
|
+
options.noToken = true;
|
|
70
|
+
break;
|
|
71
|
+
case '--host':
|
|
72
|
+
case '--port':
|
|
73
|
+
case '--token':
|
|
74
|
+
case '--workdir':
|
|
75
|
+
case '--cli':
|
|
76
|
+
case '--cli-command':
|
|
77
|
+
case '--https-key':
|
|
78
|
+
case '--https-cert': {
|
|
79
|
+
const key = name.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
80
|
+
options[key] = inlineValue ?? readOptionValue(argv, i, name);
|
|
81
|
+
if (inlineValue === undefined) i += 1;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
default:
|
|
85
|
+
throw new Error(`Unknown option: ${argv[i]}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return options;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function validatePort(value) {
|
|
93
|
+
const port = Number(value);
|
|
94
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
95
|
+
throw new Error(`Invalid port: ${value}`);
|
|
96
|
+
}
|
|
97
|
+
return String(port);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function generateToken() {
|
|
101
|
+
return randomBytes(24)
|
|
102
|
+
.toString('base64')
|
|
103
|
+
.replace(/\+/g, '-')
|
|
104
|
+
.replace(/\//g, '_')
|
|
105
|
+
.replace(/=+$/g, '');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function resolveWorkDir(value) {
|
|
109
|
+
const target = resolve(value || process.cwd());
|
|
110
|
+
try {
|
|
111
|
+
if (!statSync(target).isDirectory()) {
|
|
112
|
+
throw new Error(`Workdir is not a directory: ${target}`);
|
|
113
|
+
}
|
|
114
|
+
} catch (err) {
|
|
115
|
+
if (err?.code === 'ENOENT') {
|
|
116
|
+
throw new Error(`Workdir does not exist: ${target}`);
|
|
117
|
+
}
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
120
|
+
return target;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function isExecutable(filePath) {
|
|
124
|
+
try {
|
|
125
|
+
accessSync(filePath, constants.X_OK);
|
|
126
|
+
return true;
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function commandExists(command) {
|
|
133
|
+
if (!command) return false;
|
|
134
|
+
if (command.includes('/') || command.includes('\\')) {
|
|
135
|
+
const target = isAbsolute(command) ? command : resolve(process.cwd(), command);
|
|
136
|
+
return isExecutable(target);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const pathEnv = process.env.PATH || '';
|
|
140
|
+
const extensions = process.platform === 'win32'
|
|
141
|
+
? (process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM').split(';')
|
|
142
|
+
: [''];
|
|
143
|
+
|
|
144
|
+
for (const dir of pathEnv.split(delimiter)) {
|
|
145
|
+
if (!dir) continue;
|
|
146
|
+
for (const ext of extensions) {
|
|
147
|
+
const target = join(dir, command + ext.toLowerCase());
|
|
148
|
+
if (isExecutable(target)) return true;
|
|
149
|
+
if (process.platform === 'win32' && isExecutable(join(dir, command + ext))) return true;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function inferCliType(command) {
|
|
156
|
+
const name = command.split(/[\\/]/).pop()?.toLowerCase() || command.toLowerCase();
|
|
157
|
+
return name.includes('claude') ? 'claude' : 'codex';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function detectCli() {
|
|
161
|
+
if (commandExists('codex')) return { type: 'codex', command: 'codex' };
|
|
162
|
+
if (commandExists('claude')) return { type: 'claude', command: 'claude' };
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function main() {
|
|
167
|
+
const options = parseArgs(process.argv.slice(2));
|
|
168
|
+
const pkg = readPackageJson();
|
|
169
|
+
|
|
170
|
+
if (options.help) {
|
|
171
|
+
printHelp();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (options.version) {
|
|
176
|
+
console.log(pkg.version);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const port = validatePort(options.port || process.env.PORT || '5101');
|
|
181
|
+
const host = options.host || process.env.HOST || '0.0.0.0';
|
|
182
|
+
const workDir = resolveWorkDir(options.workdir);
|
|
183
|
+
const serverEntry = join(packageRoot, 'dist', 'server', 'index.js');
|
|
184
|
+
const staticDir = join(packageRoot, 'dist', 'client');
|
|
185
|
+
|
|
186
|
+
if (!existsSync(serverEntry)) {
|
|
187
|
+
throw new Error('AgentPilot server build is missing. Run `pnpm build` before using the NPX entry.');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let cliType = options.cli;
|
|
191
|
+
let cliCommand = options.cliCommand;
|
|
192
|
+
if (cliType && cliType !== 'codex' && cliType !== 'claude') {
|
|
193
|
+
throw new Error('--cli must be either "codex" or "claude"');
|
|
194
|
+
}
|
|
195
|
+
if (!cliType && cliCommand) {
|
|
196
|
+
cliType = inferCliType(cliCommand);
|
|
197
|
+
}
|
|
198
|
+
if (!cliType && !cliCommand) {
|
|
199
|
+
const detected = detectCli();
|
|
200
|
+
if (detected) {
|
|
201
|
+
cliType = detected.type;
|
|
202
|
+
cliCommand = detected.command;
|
|
203
|
+
} else {
|
|
204
|
+
console.warn('[AgentPilot] codex/claude was not found in PATH. Configure the CLI in Settings after launch.');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (cliType && !cliCommand) {
|
|
208
|
+
cliCommand = cliType;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
process.env.PORT = port;
|
|
212
|
+
process.env.HOST = host;
|
|
213
|
+
process.env.AGENTPILOT_NPX = '1';
|
|
214
|
+
if (options.noToken && options.token) {
|
|
215
|
+
throw new Error('--token cannot be used with --no-token');
|
|
216
|
+
}
|
|
217
|
+
if (options.noToken) {
|
|
218
|
+
delete process.env.AGENTPILOT_AUTH_TOKEN;
|
|
219
|
+
} else {
|
|
220
|
+
const authToken = String(options.token || process.env.AGENTPILOT_AUTH_TOKEN || generateToken()).trim();
|
|
221
|
+
if (!authToken) {
|
|
222
|
+
throw new Error('--token cannot be empty');
|
|
223
|
+
}
|
|
224
|
+
process.env.AGENTPILOT_AUTH_TOKEN = authToken;
|
|
225
|
+
}
|
|
226
|
+
process.env.AGENTPILOT_WORKDIR = workDir;
|
|
227
|
+
process.env.AGENTPILOT_STATIC_DIR = staticDir;
|
|
228
|
+
if (cliType) process.env.AGENTPILOT_CLI_TYPE = cliType;
|
|
229
|
+
if (cliCommand) process.env.AGENTPILOT_CLI_COMMAND = cliCommand;
|
|
230
|
+
if (options.httpsKey) process.env.AGENTPILOT_HTTPS_KEY = resolve(options.httpsKey);
|
|
231
|
+
if (options.httpsCert) process.env.AGENTPILOT_HTTPS_CERT = resolve(options.httpsCert);
|
|
232
|
+
|
|
233
|
+
await import(pathToFileURL(serverEntry).href);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
main().catch((err) => {
|
|
237
|
+
console.error(`[AgentPilot] ${err?.message || err}`);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{u as oe,h as xe,r as m,j as e}from"./vendor-react-DSV5aFEg.js";import{u as ce,s as N,g as me,c as g}from"./index-C89UCwGk.js";import{g as ue,M as C,W as ge}from"./WorkspaceLinkedText-D6hNg0T9.js";import{P as pe}from"./PageTopBar-SnTIrSb5.js";import{C as ye,X as K,L as O,j as he,g as L,q as H,r as R,s as fe,B as be,m as _,n as D,c as F,o as je,p as ke}from"./vendor-icons-S_ObYVVf.js";import{A as Ne,m as q}from"./vendor-motion-n6Lx6G4a.js";function G(r){if(/^```/.test(r.trim()))return r;try{const s=JSON.parse(r);return"```json\n"+JSON.stringify(s,null,2)+"\n```"}catch{}return r.split(`
|
|
2
|
+
`).map(s=>{const n=s.trim();if(!n)return s;try{const i=JSON.parse(n);return"```json\n"+JSON.stringify(i,null,2)+"\n```"}catch{return s}}).join(`
|
|
3
|
+
`)}function V(r){const l=Date.now()-r,s=Math.floor(l/6e4);if(s<1)return"刚刚";if(s<60)return`${s}分钟前`;const n=Math.floor(s/60);if(n<24)return`${n}小时前`;const i=Math.floor(n/24);return i===1?"昨天":i<7?`${i}天前`:new Date(r).toLocaleDateString("zh-CN",{month:"short",day:"numeric"})}function we(r){return r.split(/[\\/]/).filter(Boolean).pop()||r}const ze=()=>{var A;const r=oe(),{cliConfig:t}=ce(),[l,s]=xe(),n=l.get("filter")||"all",i=l.get("detail"),[c,p]=m.useState([]),[d,u]=m.useState(!0),[b,h]=m.useState(""),[j,$]=m.useState(null),[I,k]=m.useState(""),[re,B]=m.useState(null),[P,w]=m.useState(""),v=m.useRef(null),T=m.useCallback(async()=>{try{const a=await N.fetchHistory();p(x=>a.map(y=>{const f=x.find(de=>de.id===y.id);return f!=null&&f.messagesLoaded?{...y,messagesLoaded:!0,messages:f.messages}:y}))}catch(a){console.error("[History] Failed to fetch:",a)}finally{u(!1)}},[]);m.useEffect(()=>{T()},[T]),m.useEffect(()=>{h(""),k(""),$(null),w(""),v.current=null},[i]),m.useEffect(()=>{const x=me().onMessage(y=>{y.type==="history_changed"&&T()});return()=>{x()}},[T]);const U=c.filter(a=>n==="all"?!0:n==="running"?a.status==="running"||a.status==="confirm":a.status===n),o=c.find(a=>a.id===i),W=o!=null&&o.messagesLoaded?ue(o.messages):[],M=(t==null?void 0:t.workDir)||((A=c.find(a=>a.workDir))==null?void 0:A.workDir)||"",te=M?we(M):"未选择工作目录";m.useEffect(()=>{if(!i||!o||o.messagesLoaded||v.current===i)return;let a=!1;return v.current=i,B(i),w(""),N.fetchHistoryTask(i).then(x=>{if(!a){if(!x){w("会话记录不存在或已被删除");return}p(y=>y.map(f=>f.id===x.id?x:f))}}).catch(x=>{a||(console.error("[History] Failed to fetch detail:",x),w(x instanceof Error?x.message:"加载会话记录失败"))}).finally(()=>{a||(B(null),v.current=null)}),()=>{a=!0}},[i,o==null?void 0:o.id,o==null?void 0:o.messagesLoaded]);const ae=a=>{s(x=>(x.set("filter",a),x.delete("detail"),x))},se=a=>{s(x=>(x.set("detail",a),x))},S=()=>{s(a=>(a.delete("detail"),a))},ne=async(a,x)=>{x.stopPropagation();try{await N.deleteHistoryTask(a),p(y=>y.filter(f=>f.id!==a)),i===a&&S()}catch(y){console.error("[History] Failed to delete:",y)}},le=async()=>{try{await N.clearHistory(),p([])}catch(a){console.error("[History] Failed to clear:",a)}},z=async()=>{if(!o||j)return;const a=b.trim();if(a){if(!o.canResume){k("该会话记录缺少可恢复的 CLI 会话 ID,无法继续追问");return}$(o.id),k("");try{await N.resumeHistoryTask(o.id,a),h(""),r("/session")}catch(x){k(x instanceof Error?x.message:"恢复会话记录失败")}finally{$(null)}}},ie=[{id:"all",label:"全部",count:c.length},{id:"running",label:"进行中",count:c.filter(a=>a.status==="running"||a.status==="confirm").length},{id:"completed",label:"已完成",count:c.filter(a=>a.status==="completed").length},{id:"failed",label:"失败",count:c.filter(a=>a.status==="failed").length}];return e.jsxs("div",{className:"flex flex-col h-full bg-gray-50 dark:bg-gray-950 relative",children:[e.jsx(pe,{title:"会话记录",meta:e.jsx("span",{className:"text-[10px] text-gray-500 dark:text-gray-400 truncate max-w-[120px] flex-shrink-0",title:M,children:te}),actions:c.length>0?e.jsx("button",{onClick:le,className:"text-xs text-gray-400 hover:text-red-500 transition-colors flex-shrink-0",children:"清空"}):void 0,bottom:e.jsx("div",{className:"flex px-2 overflow-x-auto hide-scrollbar",children:ie.map(a=>e.jsxs("button",{onClick:()=>ae(a.id),className:g("flex items-center gap-1.5 px-4 py-2.5 border-b-2 text-sm font-medium whitespace-nowrap transition-colors",n===a.id?"border-primary-600 text-primary-600 dark:text-primary-400":"border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"),children:[a.label,e.jsx("span",{className:g("text-[10px] px-1.5 py-0.5 rounded-full",n===a.id?"bg-primary-100 text-primary-700 dark:bg-primary-900 dark:text-primary-300":"bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400"),children:a.count})]},a.id))})}),e.jsx("div",{className:"flex-1 overflow-y-auto p-4 space-y-3",children:U.length>0?U.map(a=>e.jsx(Re,{task:a,onClick:()=>se(a.id),onDelete:x=>ne(a.id,x)},a.id)):e.jsxs("div",{className:"flex flex-col items-center justify-center h-full text-gray-400 dark:text-gray-600 gap-3",children:[e.jsx(ye,{className:"w-12 h-12 opacity-20"}),e.jsx("p",{className:"text-sm",children:"暂无相关会话"})]})}),e.jsx(Ne,{children:i&&o&&e.jsxs(e.Fragment,{children:[e.jsx(q.div,{initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},onClick:S,className:"absolute inset-0 bg-black/40 z-40"}),e.jsxs(q.div,{initial:{y:"100%"},animate:{y:0},exit:{y:"100%"},transition:{type:"spring",damping:25,stiffness:200},className:"absolute bottom-0 left-0 right-0 h-[85%] bg-white dark:bg-gray-900 rounded-t-2xl z-50 flex flex-col shadow-xl",children:[e.jsx("div",{className:"flex justify-center pt-3 pb-2 flex-shrink-0",onClick:S,children:e.jsx("div",{className:"w-12 h-1.5 bg-gray-300 dark:bg-gray-600 rounded-full"})}),e.jsxs("div",{className:"px-5 pb-4 border-b border-gray-100 dark:border-gray-800 flex-shrink-0",children:[e.jsxs("div",{className:"flex justify-between items-start gap-4 mb-3",children:[e.jsx("h2",{className:"text-base font-medium text-gray-900 dark:text-gray-100 leading-snug",children:o.title}),e.jsx("button",{onClick:S,className:"p-1 text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-full flex-shrink-0",children:e.jsx(K,{className:"w-5 h-5"})})]}),e.jsxs("div",{className:"flex items-center gap-3 text-sm",children:[e.jsx(ee,{status:o.status}),o.duration&&e.jsxs("span",{className:"text-gray-500 dark:text-gray-400",children:["耗时 ",o.duration]}),e.jsx("span",{className:"text-gray-400 dark:text-gray-500",children:V(o.startTime)})]})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-5 bg-gray-50 dark:bg-gray-950",children:[e.jsx("div",{className:"text-center text-xs text-gray-400 dark:text-gray-600 mb-4",children:"完整消息流"}),re===o.id?e.jsxs("div",{className:"flex flex-col items-center justify-center mt-10 gap-2 text-gray-400 dark:text-gray-600",children:[e.jsx(O,{className:"w-5 h-5 animate-spin"}),e.jsx("div",{className:"text-sm",children:"正在加载会话详情"})]}):P?e.jsx("div",{className:"text-center text-sm text-red-500 dark:text-red-400 mt-8",children:P}):W.length>0?e.jsx("div",{className:"space-y-4",children:W.map(a=>e.jsx(ve,{message:a},a.id))}):o.messagesLoaded?e.jsx("div",{className:"text-center text-sm text-gray-400 dark:text-gray-600 mt-8",children:"无消息记录"}):e.jsx("div",{className:"text-center text-sm text-gray-400 dark:text-gray-600 mt-8",children:"点击后加载消息记录"})]}),e.jsx("div",{className:"border-t border-gray-100 dark:border-gray-800 bg-white dark:bg-gray-900 p-3 pb-safe flex-shrink-0",children:o.canResume?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-end gap-2",children:[e.jsx("textarea",{value:b,onChange:a=>{h(a.target.value),k(""),a.currentTarget.style.height="auto",a.currentTarget.style.height=`${Math.min(a.currentTarget.scrollHeight,96)}px`},onKeyDown:a=>{a.key==="Enter"&&(a.metaKey||a.ctrlKey)&&(a.preventDefault(),z())},placeholder:"基于这段会话继续追问…",rows:1,disabled:j===o.id,className:"flex-1 min-h-[42px] max-h-24 resize-none rounded-2xl border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 px-4 py-2.5 text-sm text-gray-800 dark:text-gray-200 placeholder:text-gray-400 dark:placeholder:text-gray-600 outline-none focus:border-primary-400 focus:bg-white dark:focus:bg-gray-800 focus:ring-[3px] focus:ring-primary-100 dark:focus:ring-primary-900 hide-scrollbar"}),e.jsx("button",{type:"button",onClick:z,disabled:!b.trim()||j===o.id,className:g("p-2.5 rounded-full flex-shrink-0 transition-all duration-200 shadow-sm active:scale-95",b.trim()&&j!==o.id?"bg-primary-600 text-white hover:bg-primary-700":"bg-gray-200 dark:bg-gray-700 text-gray-400 dark:text-gray-500 shadow-none"),title:"继续追问",children:j===o.id?e.jsx(O,{className:"w-4 h-4 animate-spin"}):e.jsx(he,{className:"w-4 h-4"})})]}),I&&e.jsx("div",{className:"mt-2 text-xs text-red-500 dark:text-red-400 px-1",children:I})]}):e.jsx("div",{className:"rounded-xl border border-gray-200 dark:border-gray-800 bg-gray-50 dark:bg-gray-950 px-3 py-2 text-xs text-gray-500 dark:text-gray-400",children:"该会话记录缺少底层 CLI 会话 ID,无法继续追问。"})})]})]})})]})},ve=({message:r})=>{switch(r.type){case"system_group":return e.jsx(Ee,{group:r});case"tool_group":return e.jsx(Me,{group:r});case"user":return e.jsx("div",{className:"flex justify-end",children:e.jsx("div",{className:"bg-primary-600 text-white rounded-2xl rounded-tr-sm px-4 py-2.5 max-w-[85%] shadow-sm",children:e.jsx("p",{className:"text-[15px] leading-relaxed whitespace-pre-wrap",children:e.jsx(ge,{linkClassName:"text-white decoration-white/70",children:r.content})})})});case"ai":return e.jsx("div",{className:"flex justify-start",children:e.jsx("div",{className:"bg-white dark:bg-gray-800 border border-gray-100 dark:border-gray-700 text-gray-800 dark:text-gray-200 rounded-2xl rounded-tl-md px-3.5 py-3 max-w-[88%] shadow-sm markdown-body overflow-hidden min-w-0",children:e.jsx(C,{children:r.content})})});case"tool":return e.jsx($e,{message:r});case"confirm":return e.jsx("div",{className:"flex justify-start",children:e.jsxs("div",{className:g("border rounded-xl px-4 py-2.5 max-w-[85%] shadow-sm",r.status==="approved"?"bg-green-50 dark:bg-green-950 border-green-200 dark:border-green-800":"bg-red-50 dark:bg-red-950 border-red-200 dark:border-red-800"),children:[e.jsxs("div",{className:"flex items-center gap-1.5 mb-1",children:[r.status==="approved"?e.jsx(R,{className:"w-3.5 h-3.5 text-green-600"}):e.jsx(H,{className:"w-3.5 h-3.5 text-red-600"}),e.jsx("span",{className:g("text-xs font-medium",r.status==="approved"?"text-green-700 dark:text-green-400":"text-red-700 dark:text-red-400"),children:r.status==="approved"?"已批准":"已拒绝"})]}),e.jsx("p",{className:"text-sm text-gray-700 dark:text-gray-300",children:r.content})]})});case"system":{const t=r.details;if(t&&Object.keys(t).length>0){const l=t.subtype==="result",s=[];if(t.model&&s.push({label:"模型",value:t.model}),t.costUsd!=null&&s.push({label:"费用",value:`$${Number(t.costUsd).toFixed(4)}`}),t.durationMs!=null){const n=Math.round(Number(t.durationMs)/1e3);s.push({label:"耗时",value:n>=60?`${Math.floor(n/60)}m${n%60}s`:`${n}s`})}return t.numTurns!=null&&s.push({label:"轮次",value:String(t.numTurns)}),e.jsx("div",{className:"flex justify-start",children:e.jsxs("div",{className:`px-3 py-2 rounded-xl border max-w-[85%] ${l?"bg-green-50/80 dark:bg-green-950/40 border-green-200/60 dark:border-green-800/40":"bg-gray-50 dark:bg-gray-800/80 border-gray-200 dark:border-gray-700"}`,children:[e.jsxs("div",{className:"flex items-center gap-1.5 mb-1",children:[e.jsx("span",{className:`text-xs font-medium ${l?"text-green-700 dark:text-green-300":"text-gray-500 dark:text-gray-400"}`,children:r.content}),e.jsx("span",{className:"text-[10px] text-gray-400 dark:text-gray-500",children:r.time})]}),s.length>0&&e.jsx("div",{className:"flex flex-wrap gap-1.5",children:s.map((n,i)=>e.jsxs("span",{className:"inline-flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded-full font-medium bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300",children:[e.jsx("span",{className:"opacity-60",children:n.label}),e.jsx("span",{className:"font-semibold",children:n.value})]},i))})]})})}return e.jsx("div",{className:"flex justify-start",children:e.jsx("span",{className:"text-xs text-gray-400 dark:text-gray-600 bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded-full",children:r.content})})}case"thinking":return e.jsx(Le,{message:r});case"error":return e.jsx("div",{className:"flex justify-start",children:e.jsxs("div",{className:"bg-red-50 dark:bg-red-950 border border-red-100 dark:border-red-800 text-red-800 dark:text-red-300 rounded-xl px-4 py-2.5 max-w-[85%] shadow-sm flex items-start gap-2",children:[e.jsx(H,{className:"w-4 h-4 mt-0.5 flex-shrink-0"}),e.jsx("p",{className:"text-sm leading-relaxed",children:r.content})]})});default:return null}};function Te(r){const t=(r.toolName||r.content||"").toLowerCase();return["readfile","writefile","read","write","edit","multiedit"].includes(t)}function E(r){return r.toolName&&r.toolName!=="result"?r.toolName:r.content||"tool"}function Se(r){return X(r).join("、")||"工具调用"}function X(r){return Array.from(new Set(r.map(E).filter(Boolean)))}function Ce(r){if(r.some(t=>t.status==="failed"))return"failed";if(r.some(t=>t.status==="running"))return"running";if(r.some(t=>t.status==="success"))return"success"}function He(r){const t=r.filter(i=>i.status==="running").length,l=r.filter(i=>i.status==="failed").length,s=r.filter(i=>i.status==="success").length,n=[`${r.length} 个`];return t&&n.push(`${t} 运行中`),l&&n.push(`${l} 失败`),s&&!t&&!l&&n.push("已完成"),n.join(" · ")}function Q(r,t="w-4 h-4 text-primary-600 dark:text-primary-400"){return Te(r)?e.jsx(je,{className:t}):e.jsx(F,{className:t})}function Y({status:r,className:t="w-3.5 h-3.5"}){return r==="running"?e.jsx(ke,{className:g(t,"text-primary-500 animate-pulse")}):r==="success"?e.jsx(R,{className:g(t,"text-green-600")}):r==="failed"?e.jsx(H,{className:g(t,"text-red-600")}):null}function De({status:r}){return e.jsx("span",{className:g("w-1.5 h-1.5 rounded-full flex-shrink-0",r==="running"?"bg-primary-500 animate-pulse":r==="failed"?"bg-red-500":r==="success"?"bg-green-500":"bg-gray-300 dark:bg-gray-600")})}const Z=({message:r,compact:t=!1})=>{const l="max-h-32";return e.jsxs(e.Fragment,{children:[r.toolDetails&&e.jsxs("div",{children:[e.jsx("div",{className:"text-[10px] font-medium text-primary-600 dark:text-primary-400 uppercase tracking-wider mb-0.5",children:"输入"}),e.jsx("div",{className:g("text-xs text-gray-600 dark:text-gray-400 bg-white/50 dark:bg-gray-900/50 p-2 rounded-lg border border-primary-50 dark:border-gray-700 overflow-y-auto",l),children:e.jsx(C,{children:G(r.toolDetails)})})]}),r.toolResult&&e.jsxs("div",{children:[e.jsx("div",{className:"text-[10px] font-medium text-primary-600 dark:text-primary-400 uppercase tracking-wider mb-0.5",children:"输出"}),e.jsx("div",{className:g("text-xs p-2 rounded-lg overflow-y-auto",l,r.status==="failed"?"text-red-700 dark:text-red-300 bg-red-50/50 dark:bg-red-950/50 border border-red-100 dark:border-red-900":"text-gray-600 dark:text-gray-400 bg-white/50 dark:bg-gray-900/50 border border-primary-50 dark:border-gray-700"),children:e.jsx(C,{children:G(r.toolResult)})})]})]})},$e=({message:r})=>e.jsx("div",{className:"flex justify-start",children:e.jsxs("div",{className:"bg-primary-50 dark:bg-primary-950 border border-primary-100 dark:border-primary-900 rounded-xl px-3 py-2 max-w-[85%] flex flex-col gap-1.5",children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[Q(r),e.jsx("span",{className:"text-sm text-primary-800 dark:text-primary-300 break-words min-w-0",children:r.content}),e.jsx("span",{className:"ml-auto flex-shrink-0",children:e.jsx(Y,{status:r.status})})]}),e.jsx(Z,{message:r})]})}),Me=({group:r})=>{const[t,l]=m.useState(!1),[s,n]=m.useState({}),i=Ce(r.messages),c=Se(r.messages),p=m.useCallback(d=>{n(u=>({...u,[d]:!u[d]}))},[]);return e.jsx("div",{className:"flex justify-start",children:e.jsxs("div",{className:"max-w-[85%] rounded-xl border border-primary-100 dark:border-gray-700 bg-primary-50/90 dark:bg-gray-800 overflow-hidden",children:[e.jsxs("button",{type:"button",onClick:()=>l(!t),className:"w-full px-3 py-2 flex items-center gap-2 text-left min-w-0 hover:bg-primary-100/60 dark:hover:bg-gray-700 transition-colors",children:[t?e.jsx(D,{className:"w-3.5 h-3.5 text-primary-500 flex-shrink-0"}):e.jsx(L,{className:"w-3.5 h-3.5 text-primary-500 flex-shrink-0"}),e.jsx(F,{className:"w-3.5 h-3.5 text-primary-600 dark:text-primary-400 flex-shrink-0"}),e.jsx("span",{className:"text-xs font-semibold text-primary-800 dark:text-primary-300 flex-shrink-0",children:"工具调用"}),e.jsx("span",{className:g("text-[10px] px-1.5 py-0.5 rounded-full flex-shrink-0",i==="failed"?"bg-red-100 text-red-700 dark:bg-red-950 dark:text-red-300":i==="running"?"bg-primary-100 text-primary-700 dark:bg-primary-950 dark:text-primary-300":"bg-green-100 text-green-700 dark:bg-green-950 dark:text-green-300"),children:He(r.messages)}),e.jsx("span",{className:"text-[10px] text-gray-400 dark:text-gray-500 flex-shrink-0",children:r.time}),e.jsx("span",{className:"min-w-0 truncate text-xs text-gray-600 dark:text-gray-400",title:c,children:c})]}),!t&&e.jsx("div",{className:"px-3 pb-2 flex flex-wrap gap-1.5",children:X(r.messages).map(d=>{const u=r.messages.find(b=>E(b)===d);return e.jsxs("span",{className:"inline-flex max-w-full min-w-0 items-center gap-1.5 text-[10px] px-1.5 py-0.5 rounded-full bg-white/70 dark:bg-gray-900/50 text-gray-600 dark:text-gray-300 border border-primary-100/60 dark:border-gray-700",title:d,children:[e.jsx(De,{status:u==null?void 0:u.status}),e.jsx("span",{className:"truncate text-left",children:d})]},d)})}),t&&e.jsx("div",{className:"border-t border-primary-100/80 dark:border-gray-700 divide-y divide-primary-100/70 dark:divide-gray-700",children:r.messages.map(d=>{const u=E(d),b=d.content&&d.content!==u,h=!!(d.toolDetails||d.toolResult),j=!!s[d.id];return e.jsxs("div",{className:"px-3 py-2 flex flex-col gap-1.5 min-w-0",children:[e.jsxs("button",{type:"button",onClick:()=>h&&p(d.id),className:g("w-full flex flex-col gap-1 min-w-0 text-left",h&&"cursor-pointer hover:text-primary-900 dark:hover:text-primary-200"),children:[e.jsxs("div",{className:"w-full flex items-start gap-2 min-w-0",children:[Q(d,"w-3.5 h-3.5 text-primary-600 dark:text-primary-400 flex-shrink-0 mt-0.5"),e.jsx("span",{className:"min-w-0 flex-1 truncate text-xs font-semibold text-primary-800 dark:text-primary-300",title:u,children:u}),e.jsxs("span",{className:"ml-auto flex items-center gap-1.5 flex-shrink-0",children:[e.jsx("span",{className:"text-[10px] text-gray-400 dark:text-gray-500 flex-shrink-0",children:d.time}),h&&e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded-full bg-white/70 dark:bg-gray-900/50 text-gray-500 dark:text-gray-400 border border-primary-100/60 dark:border-gray-700",children:d.toolDetails&&d.toolResult?"输入/输出":d.toolDetails?"输入":"输出"}),e.jsx(Y,{status:d.status,className:"w-3 h-3"}),h&&(j?e.jsx(_,{className:"w-3 h-3 text-primary-500"}):e.jsx(D,{className:"w-3 h-3 text-primary-500"}))]})]}),b&&e.jsx("span",{className:"w-full pl-5 pr-1 text-xs text-gray-500 dark:text-gray-400 truncate min-w-0",children:d.content})]}),h&&j&&e.jsx(Z,{message:d,compact:!0})]},d.id)})})]})})};function J(r){const t=r.details;if(!t)return[];const l=[];if(t.model&&l.push({label:"模型",value:t.model}),t.costUsd!=null&&l.push({label:"费用",value:`$${Number(t.costUsd).toFixed(4)}`}),t.durationMs!=null){const s=Math.round(Number(t.durationMs)/1e3);l.push({label:"耗时",value:s>=60?`${Math.floor(s/60)}m${s%60}s`:`${s}s`})}if(t.numTurns!=null&&l.push({label:"轮次",value:String(t.numTurns)}),t.inputTokens!=null||t.outputTokens!=null){const s=t.inputTokens?Number(t.inputTokens).toLocaleString():"0",n=t.outputTokens?Number(t.outputTokens).toLocaleString():"0";l.push({label:"Token",value:`${s} → ${n}`})}return l}const Ee=({group:r})=>{const[t,l]=m.useState(!1),s=r.messages.length>1,n=r.messages[r.messages.length-1],i=J(n);return e.jsx("div",{className:"flex justify-start",children:e.jsxs("div",{className:"max-w-[85%] rounded-xl border border-gray-200 dark:border-gray-800 bg-gray-50/90 dark:bg-gray-900/80 overflow-hidden",children:[e.jsxs("button",{type:"button",onClick:()=>s&&l(!t),className:g("w-full px-3 py-2 flex items-center gap-2 text-left min-w-0",s&&"hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"),children:[s?t?e.jsx(D,{className:"w-3.5 h-3.5 text-gray-400 flex-shrink-0"}):e.jsx(L,{className:"w-3.5 h-3.5 text-gray-400 flex-shrink-0"}):e.jsx(F,{className:"w-3.5 h-3.5 text-gray-400 flex-shrink-0"}),e.jsx("span",{className:"text-xs font-semibold text-gray-600 dark:text-gray-300 flex-shrink-0",children:"系统消息"}),s&&e.jsxs("span",{className:"text-[10px] px-1.5 py-0.5 rounded-full bg-gray-200/70 dark:bg-gray-800 text-gray-500 dark:text-gray-400 flex-shrink-0",children:[r.messages.length," 条"]}),e.jsx("span",{className:"text-[10px] text-gray-400 dark:text-gray-500 flex-shrink-0",children:r.time}),e.jsx("span",{className:"text-xs text-gray-500 dark:text-gray-400 truncate min-w-0",children:r.content})]}),!t&&i.length>0&&e.jsx("div",{className:"px-3 pb-2 flex flex-wrap gap-1.5",children:i.map((c,p)=>e.jsxs("span",{className:"inline-flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded-full font-medium bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 max-w-full",children:[e.jsx("span",{className:"opacity-60 flex-shrink-0",children:c.label}),e.jsx("span",{className:"font-semibold truncate",children:c.value})]},`${c.label}-${p}`))}),t&&s&&e.jsx("div",{className:"border-t border-gray-200/80 dark:border-gray-800 divide-y divide-gray-200/70 dark:divide-gray-800",children:r.messages.map(c=>{const p=J(c);return e.jsxs("div",{className:"px-3 py-2 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx("span",{className:"text-xs text-gray-600 dark:text-gray-300 truncate min-w-0",children:c.content}),e.jsx("span",{className:"text-[10px] text-gray-400 dark:text-gray-500 flex-shrink-0",children:c.time})]}),p.length>0&&e.jsx("div",{className:"mt-1.5 flex flex-wrap gap-1.5",children:p.map((d,u)=>e.jsxs("span",{className:"inline-flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded-full font-medium bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 max-w-full",children:[e.jsx("span",{className:"opacity-60 flex-shrink-0",children:d.label}),e.jsx("span",{className:"font-semibold truncate",children:d.value})]},`${d.label}-${u}`))})]},c.id)})})]})})},Le=({message:r})=>{const[t,l]=m.useState(!1);return e.jsx("div",{className:"flex justify-start",children:e.jsxs("div",{className:"bg-indigo-50/80 dark:bg-indigo-950/40 border border-indigo-200/60 dark:border-indigo-800/40 rounded-xl px-3 py-2 max-w-[85%] cursor-pointer hover:bg-indigo-100/60 dark:hover:bg-indigo-950/60 transition-colors",onClick:()=>l(!t),children:[e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx(be,{className:"w-3.5 h-3.5 text-indigo-500 dark:text-indigo-400"}),e.jsx("span",{className:"text-xs font-medium text-indigo-600 dark:text-indigo-400",children:t?"思考过程":"思考中..."}),e.jsxs("span",{className:"text-[10px] text-indigo-400 dark:text-indigo-500",children:["(",r.content.length," 字)"]}),t?e.jsx(_,{className:"w-3 h-3 text-indigo-400"}):e.jsx(D,{className:"w-3 h-3 text-indigo-400"})]}),t&&e.jsx("div",{className:"mt-2 text-sm text-indigo-800/80 dark:text-indigo-200/80 markdown-body-sm prose-sm",children:e.jsx(C,{children:r.content})})]})})},Re=({task:r,onClick:t,onDelete:l})=>e.jsxs("div",{onClick:t,className:"bg-white dark:bg-gray-900 p-4 rounded-xl border border-gray-100 dark:border-gray-800 shadow-sm active:scale-[0.98] transition-transform cursor-pointer group",children:[e.jsxs("div",{className:"flex justify-between items-start mb-2",children:[e.jsx(ee,{status:r.status}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs text-gray-400 dark:text-gray-500",children:V(r.startTime)}),e.jsx("button",{onClick:l,className:"p-0.5 text-gray-300 dark:text-gray-600 hover:text-red-400 opacity-0 group-hover:opacity-100 transition-all",children:e.jsx(K,{className:"w-3.5 h-3.5"})})]})]}),e.jsx("p",{className:"text-sm text-gray-800 dark:text-gray-200 font-medium line-clamp-2 mb-3 leading-relaxed",children:r.title}),e.jsxs("div",{className:"flex items-center justify-between text-xs text-gray-500 dark:text-gray-400",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{children:[r.confirmCount," 次确认"]}),e.jsx("span",{children:"·"}),e.jsxs("span",{children:[r.toolCount," 个工具调用"]}),r.duration&&e.jsxs(e.Fragment,{children:[e.jsx("span",{children:"·"}),e.jsx("span",{children:r.duration})]})]}),e.jsx(L,{className:"w-4 h-4 text-gray-300 dark:text-gray-600"})]})]}),ee=({status:r})=>{switch(r){case"running":return e.jsxs("div",{className:"flex items-center gap-1.5 text-primary-600 dark:text-primary-400",children:[e.jsx("div",{className:"w-2 h-2 rounded-full bg-primary-600 dark:bg-primary-400 animate-pulse"}),e.jsx("span",{className:"text-xs font-medium",children:"进行中"})]});case"confirm":return e.jsxs("div",{className:"flex items-center gap-1.5 text-orange-600 dark:text-orange-400",children:[e.jsx(fe,{className:"w-3.5 h-3.5"}),e.jsx("span",{className:"text-xs font-medium",children:"等待确认"})]});case"completed":return e.jsxs("div",{className:"flex items-center gap-1.5 text-green-600",children:[e.jsx(R,{className:"w-3.5 h-3.5"}),e.jsx("span",{className:"text-xs font-medium",children:"已完成"})]});case"failed":return e.jsxs("div",{className:"flex items-center gap-1.5 text-red-600",children:[e.jsx(H,{className:"w-3.5 h-3.5"}),e.jsx("span",{className:"text-xs font-medium",children:"失败"})]})}};export{ze as History};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{u as x,r as f,j as m}from"./vendor-react-DSV5aFEg.js";import{u as k}from"./index-C89UCwGk.js";import{c as H}from"./code-highlight-CEcsuMpw.js";import{p as d,s as M}from"./WorkspaceLinkedText-D6hNg0T9.js";import{r as j,M as w,a as K}from"./vendor-markdown-BDwu-Ux6.js";import"./vendor-icons-S_ObYVVf.js";const P=[K];function R(t){return function(){return function(n){h(n,t)}}}function W(t){return(t==null?void 0:t.type)==="element"&&["a","code","pre"].includes(t.tagName)}function h(t,o){if(!(t!=null&&t.children)||W(t))return;const r=[];let n=!1;for(const e of t.children){if((e==null?void 0:e.type)==="text"&&typeof e.value=="string"){const c=M(e.value,o);if(c.length===1&&c[0].type==="text"){r.push(e);continue}n=!0;for(const i of c)i.type==="text"?r.push({type:"text",value:i.text}):r.push({type:"element",tagName:"a",properties:{href:i.target.href,title:`打开 ${i.target.path}:${i.target.line}`},children:[{type:"text",value:i.text}]});continue}h(e,o),r.push(e)}n&&(t.children=r)}function $(t){return t.startsWith("/workspace?")}function S({children:t,enableWorkspaceLinks:o=!0}){const r=x(),{cliConfig:n}=k(),e=n==null?void 0:n.workDir,c=f.useMemo(()=>{const s=[[j,H]];return o&&s.push(R(e)),s},[o,e]),i=f.useMemo(()=>({a({href:s,children:g,node:N,...l}){const a=typeof s=="string"?d(s,e):null,p=(a==null?void 0:a.href)||(typeof s=="string"&&$(s)?s:""),y=p||s;return m.jsx("a",{...l,href:y,title:a?`打开 ${a.path}:${a.line}`:l.title,onClick:u=>{!p||u.button!==0||u.metaKey||u.ctrlKey||u.shiftKey||u.altKey||(u.preventDefault(),r(p))},children:g})}}),[r,e]);return m.jsx(w,{remarkPlugins:P,rehypePlugins:c,components:i,children:t})}export{S as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{j as e}from"./vendor-react-DSV5aFEg.js";import{c}from"./index-C89UCwGk.js";const x=({title:a,meta:s,leading:i,actions:r,bottom:t,className:l})=>e.jsxs("div",{className:c("bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800 flex-shrink-0 pt-safe z-10",l),children:[e.jsxs("div",{className:"min-h-14 px-4 py-2.5 flex items-center justify-between gap-3",children:[e.jsxs("div",{className:"flex items-center gap-3 min-w-0",children:[i,e.jsxs("div",{className:"flex flex-col min-w-0",children:[e.jsx("div",{className:"text-sm font-semibold text-gray-900 dark:text-gray-100 truncate",children:a}),s&&e.jsx("div",{className:"mt-0.5 flex items-center gap-2 min-w-0",children:s})]})]}),r&&e.jsx("div",{className:"flex items-center gap-1 flex-shrink-0",children:r})]}),t]});export{x as P};
|