@liguoshuai/pi-web-chat 2.11.2 → 2.12.10
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 +9 -6
- package/docs/ARCHITECTURE.md +7 -4
- package/docs/CHANGELOG.md +153 -0
- package/package.json +6 -6
- package/public/app.js +23 -24
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
(`pi --mode rpc`) 与 pi 子进程通信,前端走 WebSocket 流式渲染。
|
|
10
10
|
|
|
11
11
|
> 📦 **NPM 软件包**:[`@liguoshuai/pi-web-chat`](https://www.npmjs.com/package/@liguoshuai/pi-web-chat)
|
|
12
|
-
> 🔗 **项目主页**:https://github.com/liguoshuai-1990/pi-
|
|
12
|
+
> 🔗 **项目主页**:https://github.com/liguoshuai-1990/pi-chat
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -132,8 +132,8 @@ npx @liguoshuai/pi-web-chat --port 8080 --cwd /path/to/your/project
|
|
|
132
132
|
#### 方式 C:通过 Git 源码克隆与启动(适合二次开发)
|
|
133
133
|
|
|
134
134
|
```bash
|
|
135
|
-
git clone https://github.com/liguoshuai-1990/pi-
|
|
136
|
-
cd pi-
|
|
135
|
+
git clone https://github.com/liguoshuai-1990/pi-chat.git
|
|
136
|
+
cd pi-chat
|
|
137
137
|
npm install
|
|
138
138
|
npm start
|
|
139
139
|
```
|
|
@@ -181,11 +181,14 @@ Web 服务启动后,在浏览器中访问:
|
|
|
181
181
|
| `PORT` | `3000` | Web 服务监听端口 |
|
|
182
182
|
| `PI_BIN` | 自动探测(`~/.npm-global/bin/pi`、`/usr/local/bin/pi` 或 `PATH`) | 显式指定 pi 可执行文件绝对路径 |
|
|
183
183
|
| `PI_SESSIONS_DIR` | `~/.pi/agent/sessions` | pi 的 session 存储目录 |
|
|
184
|
-
| `IDLE_TIMEOUT_MS` | `300000` (5分钟) |
|
|
185
|
-
| `MAX_AGENT_LIFETIME_MS` | `
|
|
186
|
-
| `EVENT_BUFFER_SIZE` | `
|
|
184
|
+
| `IDLE_TIMEOUT_MS` | `300000` (5分钟) | 真正空闲(无连接+非流式)后的进程回收超时(`0` 为禁用回收) |
|
|
185
|
+
| `MAX_AGENT_LIFETIME_MS` | `0` (无上限) | 单个 Agent 进程后台生存硬上限(`0` 为禁用) |
|
|
186
|
+
| `EVENT_BUFFER_SIZE` | `5000` | 离线环形 Buffer 允许缓存的最大事件条数 |
|
|
187
187
|
| `MAX_CONCURRENT_AGENTS` | `0` (无限制) | 进程池最大并发 Agent 进程数量 |
|
|
188
188
|
| `IDLE_DROP_HEAP` | `false` | 进入空闲时是否给 V8 引擎 GC 提示(需 `--expose-gc`) |
|
|
189
|
+
| `HOST` | `0.0.0.0` | 服务监听地址 |
|
|
190
|
+
| `AUTH_TOKEN` / `PI_AUTH_TOKEN` | 空 | 全局访问令牌,客户端需以 Bearer Token 鉴权(空为不鉴权) |
|
|
191
|
+
| `ALLOWED_ORIGINS` | 空 | 允许的 CORS/WebSocket 源白名单(逗号分隔,空为仅允许 localhost/同源) |
|
|
189
192
|
|
|
190
193
|
---
|
|
191
194
|
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 架构设计文档
|
|
2
2
|
|
|
3
|
-
> pi-web-chat 的技术架构、数据流、关键设计决策与扩展点说明(对应 v2.
|
|
3
|
+
> pi-web-chat 的技术架构、数据流、关键设计决策与扩展点说明(对应 v2.12.7 版本)。
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -147,11 +147,14 @@ Browser server.js pi RPC 子进程
|
|
|
147
147
|
| `PORT` | `3000` | HTTP 与 WebSocket 监听端口 |
|
|
148
148
|
| `PI_BIN` | 自动探测 | pi 可执行文件绝对路径 |
|
|
149
149
|
| `PI_SESSIONS_DIR` | `~/.pi/agent/sessions` | 会话 JSONL 文件存取目录 |
|
|
150
|
-
| `IDLE_TIMEOUT_MS` | `300000` (5分钟) |
|
|
151
|
-
| `MAX_AGENT_LIFETIME_MS` | `
|
|
152
|
-
| `EVENT_BUFFER_SIZE` | `
|
|
150
|
+
| `IDLE_TIMEOUT_MS` | `300000` (5分钟) | 真正空闲(无连接+非流式)后的回收超时(`0` 为禁用回收) |
|
|
151
|
+
| `MAX_AGENT_LIFETIME_MS` | `0` (无上限) | 单个 Agent 进程后台生存硬上限(`0` 为禁用) |
|
|
152
|
+
| `EVENT_BUFFER_SIZE` | `5000` | 离线环形 Buffer 允许缓存的最大事件条数 |
|
|
153
153
|
| `MAX_CONCURRENT_AGENTS` | `0` (无限制) | 进程池最大并发 Agent 进程数量 |
|
|
154
154
|
| `IDLE_DROP_HEAP` | `false` | 进入空闲时是否给 V8 引擎 GC 提示(需 `--expose-gc`) |
|
|
155
|
+
| `HOST` | `0.0.0.0` | 服务监听地址 |
|
|
156
|
+
| `AUTH_TOKEN` / `PI_AUTH_TOKEN` | 空 | 全局访问令牌,客户端需以 Bearer Token 鉴权(空为不鉴权) |
|
|
157
|
+
| `ALLOWED_ORIGINS` | 空 | 允许的 CORS/WebSocket 源白名单(逗号分隔,空为仅允许 localhost/同源) |
|
|
155
158
|
|
|
156
159
|
---
|
|
157
160
|
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -5,8 +5,161 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
|
|
9
|
+
## [2.12.10] - 2026-09-04
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **修复 GitHub Actions CI 的 Android 构建失败 (`clients/android/app/build.gradle.kts`)**:Kotlin DSL 脚本中 `java.util.Properties()` 因 `java` 标识符被 Java 插件扩展遮蔽而解析失败,导致 `./gradlew assembleDebug` 编译报错 `Unresolved reference: util`。改为脚本顶部显式 `import java.util.Properties` 并引用 `Properties()`,恢复 Android Debug APK 产物构建。
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- 全端版本号统一递增至 2.12.10(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## [2.12.9] - 2026-09-04
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- **用户旅途文档修正与补充 (`docs/USER_JOURNEY.md`)**:基于 5 个代码探索子代理的完整分析结果,修正并补充文档内容:
|
|
24
|
+
- 修正痛点 #7:会话搜索功能仅 HarmonyOS 缺失(Web 和 Android 已有),原误标为"全端"问题。
|
|
25
|
+
- 新增痛点 #9-#13:HarmonyOS 端无 Token 输入 UI、错误信息不可见、无删除会话、无模型选择 UI;Android/HarmonyOS 端错误提示 UI 薄弱。
|
|
26
|
+
- 功能对齐矩阵补充 7 行:空状态引导芯片、键盘快捷键、消息发送失败重试、离开页面警告、图片 Lightbox、代码块复制按钮、错误提示方式。
|
|
27
|
+
- 三端交互差异对比补充 8 行:空状态引导、键盘快捷键、离开警告、发送失败重试、图片大图查看、明文 HTTP 限制、流式看门狗超时。
|
|
28
|
+
- 改进建议从 8 条扩展至 9 条,新增移动端错误提示增强建议。
|
|
29
|
+
- 文档末尾补充分析方法说明与子代理交叉验证过程。
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- 全端版本号统一递增至 2.12.9(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## [2.12.8] - 2026-09-04
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- **用户旅途全景分析文档 (`docs/USER_JOURNEY.md`)**:新增面向终端使用者的 Customer Journey Map 文档,深度分析代码库后从用户视角梳理完整用户旅途,覆盖六大阶段(发现获取 → 配置连接 → 核心对话 → 会话管理 → 高级功能 → 异常恢复),包含产品定位与用户画像、系统拓扑与触点、三端功能对齐矩阵、交互模式差异对比、用户痛点与改进建议、协议消息全景附录。
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- 全端版本号统一递增至 2.12.8(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## [2.12.7] - 2026-09-04
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
- **全仓文档版本号漂移清理 (`README.md`, `clients/harmony/README.md`, `clients/android/README.md`)**:修复多处残留的旧版本号(`version-2.10.0` 徽章、HarmonyOS / Android 页首标题与工程目录注释中的 `v2.11.3`、`2.10.0`),统一对齐至当前版本 2.12.7。
|
|
52
|
+
- **Web 客户端仓库地址更新 (`clients/web/package.json`, `clients/web/README.md`)**:`repository` / `bugs` / `homepage` 及克隆地址由已废弃的 `pi-web-chat` 仓库更正为 Monorepo 主仓 `pi-chat`。
|
|
53
|
+
- **环境变量文档补齐与默认值修正 (`.env.example`, `server/.env.example`, `clients/web/README.md`, `clients/web/docs/ARCHITECTURE.md`)**:补充缺失的 `PI_AUTH_TOKEN`、`MAX_AGENT_LIFETIME_MS`、`EVENT_BUFFER_SIZE`、`IDLE_DROP_HEAP`、`HOST`、`ALLOWED_ORIGINS` 说明;修正 `MAX_AGENT_LIFETIME_MS`(默认 `0` 禁用)与 `EVENT_BUFFER_SIZE`(默认 `5000`)两处过时默认值。
|
|
54
|
+
- **Android 构建产物命名文档同步 (`README.md`, `clients/android/README.md`, `docs/ARCHITECTURE.md`)**:将过时的 `app-debug.apk` 引用更正为构建脚本实际产出的 `pi-chat-v{version}-debug.apk` 命名。
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
- 全端版本号统一递增至 2.12.7(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
58
|
+
|
|
8
59
|
---
|
|
9
60
|
|
|
61
|
+
## [2.12.6] - 2026-09-04
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
- **服务端代码深度扫描修复(`server/src/*`)**:
|
|
65
|
+
- `auth.js`:Token 校验改用 `crypto.timingSafeEqual` 恒定时间比较,消除计时侧信道攻击风险,并对非字符串输入做安全归一化。
|
|
66
|
+
- `ws.js`:修复 WebSocket 鉴权失败/容量拒绝时错误消息的 `id` 被错误放入 `details` 字段的问题,改为顶层 `id`,保证客户端可按请求 id 正确关联错误响应。
|
|
67
|
+
- `ws.js`:移除 `verifyClient` 中残留的空操作死代码(仅注释无逻辑的判断块)。
|
|
68
|
+
- `agent.js`:修复 `replayBufferedSse` 在非流式状态下回放后未清空事件缓冲区的问题(与 `replayBufferedWs` 行为对齐),避免旧事件向新连接的 SSE 客户端重复回放。
|
|
69
|
+
- `routes.js`:`/api/log-error` 端点新增字段长度截断(每字段上限 2048 字符),防止恶意/异常客户端写入超大内容刷屏服务端日志。
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## [2.12.5] - 2026-09-04
|
|
74
|
+
|
|
75
|
+
### Improved
|
|
76
|
+
- **CI NPM 发布支持动态 OTP 验证与 Automation Token 配置 (`.github/workflows/ci.yml`)**:
|
|
77
|
+
- 在 `workflow_dispatch` 触发器中新增可选的 `npm_otp` 参数,允许在缺少 Automation Token 时通过手动输入 2FA 一次性验证码完成 NPM 发布;
|
|
78
|
+
- 完善发布环境的 Token 传递与错误提示。
|
|
79
|
+
|
|
80
|
+
### Changed
|
|
81
|
+
- 全端版本号统一递增至 2.12.5(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
82
|
+
|
|
83
|
+
## [2.12.4] - 2026-09-04
|
|
84
|
+
|
|
85
|
+
### Added
|
|
86
|
+
- **GitHub Actions CI 自动发布至 NPM Registry (`.github/workflows/ci.yml`)**:
|
|
87
|
+
- 新增 `publish-npm` 任务,集成 GitHub 仓库配置的 `NPM_TOKEN` Secret 与 `actions/setup-node` 注册表认证;
|
|
88
|
+
- 支持在推送版本 Tag(如 `v2.12.4`)或手动触发 `workflow_dispatch`(勾选 `publish_npm: true`)时,自动将全套 Monorepo 包(`@liguoshuai/pi-chat-protocol`、`@liguoshuai/pi-chat-server`、`@liguoshuai/pi-web-chat`)安全发布至 NPM Registry。
|
|
89
|
+
|
|
90
|
+
### Changed
|
|
91
|
+
- 全端版本号统一递增至 2.12.4(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
92
|
+
|
|
93
|
+
## [2.12.3] - 2026-09-04
|
|
94
|
+
|
|
95
|
+
### Fixed
|
|
96
|
+
- **Android 端 Compose `Modifier.padding` 参数签名编译错误 (`clients/android/app/src/main/java/com/pichat/android/ui/screen/ChatScreen.kt`)**:修复输入栏容器 `Column` 中非法混用 `horizontal` 与 `top` / `bottom` 命名参数导致 Kotlin 编译报错(`None of the following candidates is applicable`)的问题,规范化为四向命名参数(`start`, `top`, `end`, `bottom`)。
|
|
97
|
+
- **GitHub Actions CI 升级 (`.github/workflows/ci.yml`)**:将 `actions/setup-java` 从 v4 升级至官方推荐的 v5。
|
|
98
|
+
|
|
99
|
+
### Changed
|
|
100
|
+
- 全端版本号统一递增至 2.12.3(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
101
|
+
|
|
102
|
+
## [2.12.2] - 2026-09-04
|
|
103
|
+
|
|
104
|
+
### Fixed
|
|
105
|
+
- **Web 端发送消息后输入框锁死并误显“正在中止…” (`clients/web/public/app.js`)**:`submitPrompt` 发送成功后调用 `setComposerAborting(true)`(现已更名为 `setComposerStreaming`),其内部误将 `state.aborting` 置为 `true`,导致 `updateComposerUI` 进入“中止中”分支:输入框占位符显示“正在中止当前任务…”,发送按钮变成 `⏳` 并置为 `disabled`,UI 锁死直至任务结束。同时 `agent_start` / `backfill_end` / `updateState` 三处进入流式状态的路径也存在同样的误用。现将语义统一为 `setComposerStreaming`:进入流式状态时始终将 `state.aborting` 清理为 `false`,仅刷新 `updateComposerUI` 与 `renderModelPill`;而“中止”仅由用户点击中止时通过 `abortGeneration` 单独置位,彻底解决发送消息后对话框锁死、误显“中止…”的问题。
|
|
106
|
+
|
|
107
|
+
### Changed
|
|
108
|
+
- 全端版本号统一递增至 2.12.2(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
109
|
+
|
|
110
|
+
## [2.12.1] - 2026-09-04
|
|
111
|
+
|
|
112
|
+
### Improved
|
|
113
|
+
- **耗时时长格式化进位与边界修复 (`packages/protocol`, `clients/web/public/app.js`)**:
|
|
114
|
+
- 重构 `formatDuration` 算法,避免毫秒数在接近整分边界(例如 `59950ms`、`119950ms`)时因浮点数取模四舍五入导致输出 `"0m 60s"` 或 `"1m 60s"` 异常格式,统一进位并补齐单元测试。
|
|
115
|
+
- **会话列表排序时间戳健壮性增强 (`server/src/routes.js`)**:
|
|
116
|
+
- 在 `/api/sessions` 排序中加入严格的 `Number.isFinite` 与 `Date.parse` 解析校验,防止无效或非标准日期字符串导致 `NaN` 比较破坏 JavaScript 引擎排序稳定性。
|
|
117
|
+
- **流式事件回放与请求 ID 边界保护 (`server/src/agent.js`)**:
|
|
118
|
+
- 优化 WebSocket / SSE 缓冲区回放遍历逻辑,精准依据缓冲区环形溢出状态重放事件,过滤潜在空槽位;
|
|
119
|
+
- 改进 `PiAgent.send` 在子进程异常终止时对于数字 `0` 作为请求 ID 的正确保留与响应返回。
|
|
120
|
+
- **Android 客户端协程生命周期防御 (`clients/android/.../ChatRepository.kt`)**:
|
|
121
|
+
- 仓库层使用 `SupervisorJob()` 配合 `Dispatchers.IO`,避免子协程失败导致整个仓库层作用域被级联取消。
|
|
122
|
+
|
|
123
|
+
### Changed
|
|
124
|
+
- 全端版本号统一递增至 2.12.1(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
125
|
+
|
|
126
|
+
## [2.12.0] - 2026-09-04
|
|
127
|
+
|
|
128
|
+
### Added
|
|
129
|
+
- **Android 构建产物自定义重命名与 Release 加固配置 (`clients/android/app/build.gradle.kts`, `proguard-rules.pro`)**:
|
|
130
|
+
- 构建产物根据版本号和构建类型重命名为语义化规范格式:`pi-chat-v{versionName}-debug.apk` 与 `pi-chat-v{versionName}-release.apk`。
|
|
131
|
+
- 启用 Release 版本的混淆加固与代码资源缩减(`isMinifyEnabled = true`, `isShrinkResources = true`),配置完整的 ProGuard / R8 保护规则(数据模型、Kotlinx Serialization、OkHttp、Compose 与协程)。
|
|
132
|
+
- 配置默认签名回退支持,使 Release APK 可开箱即用构建。
|
|
133
|
+
- **Android 会话删除功能 (`clients/android/.../ApiService.kt`, `ChatRepository.kt`, `ChatViewModel.kt`, `ChatScreen.kt`)**:
|
|
134
|
+
- 新增 `DELETE /api/session?file=...` 网络请求与仓库层方法,并在侧边栏每个会话项右侧提供删除按钮及二次确认弹窗。
|
|
135
|
+
|
|
136
|
+
### Improved
|
|
137
|
+
- **Android 侧边栏抽屉界面优化与对齐 Web 体验 (`clients/android/.../ChatScreen.kt`)**:
|
|
138
|
+
- 抽屉宽度适配调整为 `280dp`,改善手机端视觉比例;
|
|
139
|
+
- 会话项展示信息全面对齐 Web:新增格式化时间戳(如 `9月4日 15:30`)、消息条数展示(`· N 条`)与流式任务运行中微动徽标(`运行中`);
|
|
140
|
+
- 选中会话样式重构:高亮背景(`BgHover`)+ 左侧强调色指示条(`Accent`),会话行间距与内边距紧凑精致化;
|
|
141
|
+
- 搜索框高度重构为紧凑式 `34dp`,包含搜索图标与一键清空按钮;
|
|
142
|
+
- 侧边栏底部补齐 `pi.dev` 与 `pi-web-chat` 外部链接跳转,统一展示版本信息。
|
|
143
|
+
- **Android 顶栏排版与信息层级重构 (`clients/android/.../ChatScreen.kt`)**:
|
|
144
|
+
- 顶部导航栏高度规整至 `56dp`,居中展示当前对话主标题,并在下方紧凑排列工作目录(CWD)、模型名称(含 `★ 默认` 徽标)和思考推理等级胶囊;
|
|
145
|
+
- 左右两侧合理布局抽屉按钮、分享导出按钮及设置按钮,排版清晰美观不拥挤,彻底解决信息遮挡与丢失问题。
|
|
146
|
+
- **Android 底部输入框体验与样式重构 (`clients/android/.../ChatScreen.kt`)**:
|
|
147
|
+
- 输入框整体采用 Web 端 ChatGPT 风格圆角胶囊卡片(`RoundedCornerShape(22.dp)`),内边距与高度严格贴合文本行高;
|
|
148
|
+
- 采用 `BasicTextField` 配合优雅的占位符提示,支持多行平滑自适应扩展;
|
|
149
|
+
- 发送按钮、指导指令按钮及中止按钮尺寸规整(32dp 圆形/胶囊),并对齐底部安全区。
|
|
150
|
+
|
|
151
|
+
### Changed
|
|
152
|
+
- 全端版本号统一递增至 2.12.0(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
153
|
+
|
|
154
|
+
## [2.11.3] - 2026-09-03
|
|
155
|
+
|
|
156
|
+
### Fixed
|
|
157
|
+
- **HarmonyOS 端配置持久化与初始加载缺失 (`clients/harmony/...`)**:`PreferencesHelper` 虽然实现了服务端 URL 的存储与获取,但未在 App 启动/初始化流程中实际调用。现已在 `Index.ets` 的 `aboutToAppear()` 中调用 `PreferencesHelper.getServerUrl()` 动态读取已保存的 URL 进行初始化,并在“保存并重连”时调用 `PreferencesHelper.setServerUrl()` 写入持久化存储,解决应用重启后配置丢失问题。
|
|
158
|
+
- **HarmonyOS 端 UI 界面版本号硬编码滞后 (`clients/harmony/.../Index.ets`)**:侧边栏抽屉底部硬编码展示旧版本号 `v2.10.1`,现已更新为当前全仓最新的 `v2.11.3`,与 Monorepo 版本数严格对齐。
|
|
159
|
+
|
|
160
|
+
### Changed
|
|
161
|
+
- 全端版本号统一递增至 2.11.3(Monorepo Lockstep:Root / Protocol / Server / Web / Android / HarmonyOS)。
|
|
162
|
+
|
|
10
163
|
## [2.11.2] - 2026-09-03
|
|
11
164
|
|
|
12
165
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liguoshuai/pi-web-chat",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.10",
|
|
4
4
|
"description": "A ChatGPT/Gemini-style web UI for the pi coding agent, powered by pi's RPC mode.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server.js",
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
],
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
|
-
"url": "git+https://github.com/liguoshuai-1990/pi-
|
|
29
|
+
"url": "git+https://github.com/liguoshuai-1990/pi-chat.git"
|
|
30
30
|
},
|
|
31
31
|
"bugs": {
|
|
32
|
-
"url": "https://github.com/liguoshuai-1990/pi-
|
|
32
|
+
"url": "https://github.com/liguoshuai-1990/pi-chat/issues"
|
|
33
33
|
},
|
|
34
|
-
"homepage": "https://github.com/liguoshuai-1990/pi-
|
|
34
|
+
"homepage": "https://github.com/liguoshuai-1990/pi-chat#readme",
|
|
35
35
|
"license": "MIT",
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"express": "^4.21.2",
|
|
44
44
|
"ws": "^8.18.0",
|
|
45
|
-
"@liguoshuai/pi-chat-
|
|
46
|
-
"@liguoshuai/pi-chat-
|
|
45
|
+
"@liguoshuai/pi-chat-protocol": "2.12.10",
|
|
46
|
+
"@liguoshuai/pi-chat-server": "2.12.10"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "node --check server.js && node --check bin/pi-web-chat.js && node --check public/app.js",
|
package/public/app.js
CHANGED
|
@@ -560,14 +560,12 @@ function formatMessageTime(ts) {
|
|
|
560
560
|
// ---- Duration Formatter ----
|
|
561
561
|
function formatDuration(ms) {
|
|
562
562
|
if (ms == null || isNaN(ms) || ms < 0) return "";
|
|
563
|
-
|
|
563
|
+
const totalSecs = Math.round(ms / 1000);
|
|
564
|
+
if (totalSecs < 60) {
|
|
564
565
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
565
566
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
}
|
|
569
|
-
const mins = Math.floor(ms / 60000);
|
|
570
|
-
const secs = Math.round((ms % 60000) / 1000);
|
|
567
|
+
const mins = Math.floor(totalSecs / 60);
|
|
568
|
+
const secs = totalSecs % 60;
|
|
571
569
|
return `${mins}m ${secs}s`;
|
|
572
570
|
}
|
|
573
571
|
|
|
@@ -741,7 +739,7 @@ function startNewSession() {
|
|
|
741
739
|
state.streamingItems = [];
|
|
742
740
|
state.streamingMsg = null;
|
|
743
741
|
state.activeToolCalls.clear();
|
|
744
|
-
|
|
742
|
+
setComposerStreaming(false);
|
|
745
743
|
try {
|
|
746
744
|
window.history.replaceState({}, "", window.location.pathname);
|
|
747
745
|
} catch {}
|
|
@@ -861,7 +859,7 @@ async function loadSession(file) {
|
|
|
861
859
|
state.streamingItems = [];
|
|
862
860
|
state.streamingMsg = null;
|
|
863
861
|
state.activeToolCalls.clear();
|
|
864
|
-
|
|
862
|
+
setComposerStreaming(false);
|
|
865
863
|
try {
|
|
866
864
|
const newUrl = window.location.pathname + "?session=" + encodeURIComponent(file);
|
|
867
865
|
window.history.replaceState({ session: file }, "", newUrl);
|
|
@@ -1981,7 +1979,7 @@ function handlePiMessage(obj) {
|
|
|
1981
1979
|
// After replay, sync the composer / streaming state to what the server thinks.
|
|
1982
1980
|
if (obj.streaming) {
|
|
1983
1981
|
state.streaming = true;
|
|
1984
|
-
|
|
1982
|
+
setComposerStreaming(true);
|
|
1985
1983
|
ensureStreamingMsg();
|
|
1986
1984
|
refreshStreamingContent();
|
|
1987
1985
|
// If ring buffer overflowed while client was away, sync full session history in background
|
|
@@ -1992,7 +1990,7 @@ function handlePiMessage(obj) {
|
|
|
1992
1990
|
finalizeStreamingMsg();
|
|
1993
1991
|
state.streaming = false;
|
|
1994
1992
|
state.aborting = false;
|
|
1995
|
-
|
|
1993
|
+
setComposerStreaming(false);
|
|
1996
1994
|
if (state.currentSessionFile) {
|
|
1997
1995
|
syncSessionHistory(state.currentSessionFile, true);
|
|
1998
1996
|
}
|
|
@@ -2040,7 +2038,7 @@ function handlePiMessage(obj) {
|
|
|
2040
2038
|
}
|
|
2041
2039
|
} else if (obj.command === "abort") {
|
|
2042
2040
|
state.aborting = false;
|
|
2043
|
-
|
|
2041
|
+
setComposerStreaming(false);
|
|
2044
2042
|
}
|
|
2045
2043
|
else if (obj.command === "switch_session" && obj.success) {
|
|
2046
2044
|
// ask pi for current state so we can get session id, name
|
|
@@ -2076,7 +2074,7 @@ function handlePiMessage(obj) {
|
|
|
2076
2074
|
case "agent_start":
|
|
2077
2075
|
state.streaming = true;
|
|
2078
2076
|
state.aborting = false;
|
|
2079
|
-
|
|
2077
|
+
setComposerStreaming(true);
|
|
2080
2078
|
ensureStreamingMsg();
|
|
2081
2079
|
refreshStreamingContent();
|
|
2082
2080
|
refreshSessions();
|
|
@@ -2088,7 +2086,7 @@ function handlePiMessage(obj) {
|
|
|
2088
2086
|
finalizeStreamingMsg();
|
|
2089
2087
|
state.streaming = false;
|
|
2090
2088
|
state.aborting = false;
|
|
2091
|
-
|
|
2089
|
+
setComposerStreaming(false);
|
|
2092
2090
|
sendWs({ type: "get_state" });
|
|
2093
2091
|
refreshSessions(); // titles may have changed
|
|
2094
2092
|
break;
|
|
@@ -2278,14 +2276,14 @@ function handlePiMessage(obj) {
|
|
|
2278
2276
|
finalizeStreamingMsg();
|
|
2279
2277
|
state.streaming = false;
|
|
2280
2278
|
state.aborting = false;
|
|
2281
|
-
|
|
2279
|
+
setComposerStreaming(false);
|
|
2282
2280
|
showToast(`⚠️ ${obj.message || obj.code || "发生错误"}`);
|
|
2283
2281
|
break;
|
|
2284
2282
|
case "pi_exit":
|
|
2285
2283
|
finalizeStreamingMsg();
|
|
2286
2284
|
state.streaming = false;
|
|
2287
2285
|
state.aborting = false;
|
|
2288
|
-
|
|
2286
|
+
setComposerStreaming(false);
|
|
2289
2287
|
$("#connDot").style.color = "var(--danger)";
|
|
2290
2288
|
break;
|
|
2291
2289
|
default:
|
|
@@ -2374,14 +2372,14 @@ function updateState(d) {
|
|
|
2374
2372
|
if (d.isStreaming) {
|
|
2375
2373
|
if (!state.streaming) {
|
|
2376
2374
|
state.streaming = true;
|
|
2377
|
-
|
|
2375
|
+
setComposerStreaming(true);
|
|
2378
2376
|
ensureStreamingMsg();
|
|
2379
2377
|
}
|
|
2380
2378
|
} else if (state.streaming && !state.isBackfilling) {
|
|
2381
2379
|
finalizeStreamingMsg();
|
|
2382
2380
|
state.streaming = false;
|
|
2383
2381
|
state.aborting = false;
|
|
2384
|
-
|
|
2382
|
+
setComposerStreaming(false);
|
|
2385
2383
|
refreshSessions();
|
|
2386
2384
|
}
|
|
2387
2385
|
}
|
|
@@ -2941,11 +2939,12 @@ function updateComposerUI() {
|
|
|
2941
2939
|
}
|
|
2942
2940
|
}
|
|
2943
2941
|
|
|
2944
|
-
function
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2942
|
+
function setComposerStreaming(yes) {
|
|
2943
|
+
// Entering/exiting the streaming state must never turn on the "aborting"
|
|
2944
|
+
// flag — otherwise the composer would lock and show "正在中止…" while the
|
|
2945
|
+
// model is actually just running. Abort is a separate transient state.
|
|
2946
|
+
state.aborting = false;
|
|
2947
|
+
if (!yes) {
|
|
2949
2948
|
const inner = $("#composerInner");
|
|
2950
2949
|
if (inner) inner.classList.remove("aborting");
|
|
2951
2950
|
const hint = $(".composer-hint");
|
|
@@ -3050,7 +3049,7 @@ function submitPrompt() {
|
|
|
3050
3049
|
|
|
3051
3050
|
state.streaming = true;
|
|
3052
3051
|
state.aborting = false;
|
|
3053
|
-
|
|
3052
|
+
setComposerStreaming(true);
|
|
3054
3053
|
ensureStreamingMsg(now);
|
|
3055
3054
|
refreshStreamingContent();
|
|
3056
3055
|
|
|
@@ -3083,7 +3082,7 @@ function submitPrompt() {
|
|
|
3083
3082
|
if (!sendWs({ type: "prompt", message: text, images: imagesToSend })) {
|
|
3084
3083
|
state.streaming = false;
|
|
3085
3084
|
state.aborting = false;
|
|
3086
|
-
|
|
3085
|
+
setComposerStreaming(false);
|
|
3087
3086
|
stopStreamingTimer();
|
|
3088
3087
|
state.turnStartedAt = null;
|
|
3089
3088
|
state.streamingMsgDurationEl = null;
|