Files
qiming/qimingclaw/docs/sessions-tab-and-process-cleanup-plan.md

178 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Plan: 新增「会话」Tab内嵌 webview+ 进程清理改进
## Context
当前「开始会话」会打开一个新的浏览器窗口体验割裂。需要重构为在主窗口内新增「会话」Tab内嵌 webview 展示会话页面,同时展示活跃会话列表,支持打开/停止。此外需要修复停止 agent 服务时僵尸进程的问题。
---
## 1. 新增 `DetailedSession` 类型
**新建:** `src/shared/types/sessions.ts`
```typescript
export interface DetailedSession {
id: string;
title?: string;
engineType: 'claude-code' | 'qimingcode';
projectId?: string;
status: 'idle' | 'pending' | 'active' | 'terminating';
createdAt: number;
lastActivity?: number;
}
```
---
## 2. AcpEngine + UnifiedAgentService 新增方法
**文件:** `src/main/services/engines/acp/acpEngine.ts`
- 新增 `listSessionsDetailed()` — 遍历 `this.sessions` Map 返回 DetailedSession[](含 status/projectId/lastActivity + engineType from `this.config.engine`
**文件:** `src/main/services/engines/unifiedAgent.ts`
- 新增 `listAllSessionsDetailed()` — 遍历 `this.engines` Map汇总所有 engine 的 sessions
---
## 3. 新增 IPC handlers + Preload
**文件:** `src/main/ipc/agentHandlers.ts`
- `agent:listSessionsDetailed` — 调用 `agentService.listAllSessionsDetailed()`
- `agent:stopSession` — 根据 sessionId 定位 engine先 abort 再 destroy 该 engine
**文件:** `src/preload/index.ts`
- `agent.listSessionsDetailed`
- `agent.stopSession`
---
## 4. 新增 SessionsPage 组件(核心变更)
**新建:** `src/renderer/components/pages/SessionsPage.tsx`
### 布局设计
SessionsPage 内部有两个视图,通过 state 切换:
#### 视图 A: 会话列表(默认)
- **顶部操作栏**: 「新建会话」按钮 + 「刷新」按钮
- **活跃会话列表**Ant Design Table/List:
- 每行: 会话标题/ID、引擎类型 Tag、状态 Tag、创建时间
- 操作: 「打开」按钮(切换到视图 B 展示该会话 webview、「停止」按钮kill 进程)
- 每 3s 轮询 `agent:listSessionsDetailed` 刷新
- 空状态提示
#### 视图 B: 内嵌 webview
- 复用现有 `EmbeddedWebview` 组件(`src/renderer/components/EmbeddedWebview.tsx`
- 工具栏「返回」按钮 → 切回视图 A
- URL 构造复用 ClientPage 中的 redirect URL 逻辑:`${domain}/api/sandbox/config/redirect/${userId}`
- Cookie 设置复用 ClientPage 中的 setCookie 逻辑
### 「新建会话」流程
点击「新建会话」→ 执行 cookie sync → 构造 redirect URL → 切换到视图 B内嵌 webview 加载 URL
### 「打开」已有会话流程
点击列表中「打开」→ 同样切换到视图 BURL 可附带 session 参数(如果后端支持)或直接用 redirect URL
### Props
SessionsPage 需要从 App.tsx 获取 auth 信息来构造 URL。两种方案
- **方案 A推荐**: 组件内部通过 `window.electronAPI.settings.get()` 读取 auth 信息domain, userId, token自包含
- 方案 B: 从 App.tsx 传 props
---
## 5. 重构 ClientPage「开始会话」
**文件:** `src/renderer/components/pages/ClientPage.tsx`
- `handleStartSession` 改为调用 `onNavigate('sessions')`(已有此 prop切换到会话 Tab
- 删除 `webview.openWindow` 相关逻辑
- 保留 QR 码功能不变
---
## 6. App.tsx 集成
**文件:** `src/renderer/App.tsx`
1. TabKey 类型新增 `'sessions'`
2. `menuItems``'client'` 之后插入 `{ key: 'sessions', icon: <TeamOutlined />, label: '会话' }`
3. 渲染区域新增 `{activeTab === 'sessions' && <SessionsPage />}`
4. import `SessionsPage``TeamOutlined`
---
## 7. 进程清理改进(僵尸进程修复)
### 7a. 进程树 kill 工具
**新建:** `src/main/services/utils/processTree.ts`
```typescript
export async function killProcessTree(pid: number, signal?: NodeJS.Signals): Promise<void>
```
- macOS/Linux: spawn 时 `detached: true` + `process.kill(-pid, signal)` 杀进程组
- Windows: `taskkill /T /F /PID`
- 兜底: SIGTERM → 等 3s → SIGKILL
### 7b. AcpEngine.destroy() — SIGKILL 升级 + 进程树
**文件:** `src/main/services/engines/acp/acpEngine.ts`
改当前 `this.acpProcess.kill()` 为:
1.`killProcessTree(pid, 'SIGTERM')` 杀进程树
2. 等待最多 5s监听 `exit` 事件)
3. 超时后 `killProcessTree(pid, 'SIGKILL')` 强杀
### 7c. AcpClient spawn — Unix 下 detached: true
**文件:** `src/main/services/engines/acp/acpClient.ts`
`createAcpConnection` spawn 时非 Windows 加 `detached: true`,支持进程组杀法。
### 7d. UnifiedAgentService.destroy() — 每个 engine 加超时
**文件:** `src/main/services/engines/unifiedAgent.ts`
每个 `engine.destroy()` 外包 `Promise.race` + 10s 超时。
### 7e. ManagedProcess.kill() — SIGKILL 升级
**文件:** `src/main/processManager.ts`
SIGTERM 后设 3s 定时器,进程未退出则 SIGKILL。
---
## 实施顺序
1. `src/shared/types/sessions.ts` — 新建 DetailedSession 类型
2. `src/main/services/utils/processTree.ts` — 新建进程树 kill 工具
3. `src/main/services/engines/acp/acpClient.ts` — spawn detached: trueUnix
4. `src/main/services/engines/acp/acpEngine.ts` — listSessionsDetailed() + destroy() 改进
5. `src/main/services/engines/unifiedAgent.ts` — listAllSessionsDetailed() + destroy 超时
6. `src/main/processManager.ts` — kill() SIGKILL 升级
7. `src/main/ipc/agentHandlers.ts` — 新增 IPC handlers
8. `src/preload/index.ts` — 暴露新 IPC
9. `src/renderer/components/pages/SessionsPage.tsx` — 新建会话页面(列表 + 内嵌 webview
10. `src/renderer/App.tsx` — 新增 sessions tab
11. `src/renderer/components/pages/ClientPage.tsx` — 重构「开始会话」为导航到 sessions tab
---
## 验证
1. `npm run electron:dev` 启动应用
2. 登录后点击「开始会话」→ 确认跳转到「会话」tab 并在内嵌 webview 中打开(不再弹出新窗口)
3. 「会话」tab 列表中可见活跃会话,状态正确
4. 点击列表中「打开」→ 切到 webview 展示
5. 点击「停止」→ 会话进程被终止,列表刷新
6. 停止 agent 服务 → 所有引擎进程及子进程被清理,`ps aux | grep acp` 无僵尸
7. webview 工具栏「返回」→ 回到列表视图