# PRD — Nexus AI 终端面板
**版本**: v1.0.0 **状态**: Complete **锚点**: `docs/NORTH-STAR.md` **最后更新**: 2026-07-15
---
## Problem Statement
开发者需要一个统一入口,让 AI Agent 能在任意项目目录中持续运行,并能在 PC、手机、IM 等任意渠道随时介入——现有工具(ttyd、SSH)在移动端控制字符输入残缺,且缺乏针对 AI Agent 生命周期的管理界面和异步交互能力。
---
## Target Users
**用户即开发者本人**(单用户/个人服务器)
- 同时跑多个 Claude Code Agent,需要随时从任意设备查看进度、发送指令
- 外出时通过手机/Telegram 给 AI 下任务,回家后在 PC 上接续
- 不想保持 SSH 连接,关掉浏览器后 Agent 继续运行
---
## Core Features
### Must(v1 Complete)
| ID | Feature | 验收标准 |
|---|---|---|
| F-01 | WebSocket tmux 桥接 | 浏览器关闭后 tmux 和 Agent 继续运行;重新打开可接续 |
| F-02 | JWT 单密码认证 | 密码 bcrypt hash 存 env,Token 30天有效 |
| F-03 | xterm.js 终端渲染 | 256色/TrueColor/Unicode;scrollback 10000行 |
| F-04 | 移动端控制字符工具栏 | 可发送 Esc/Tab/Ctrl+C/方向键等;触摸不弹软键盘 |
| F-05 | 移动端滚动与缩放 | 单指滑动浏览历史;双指捏合调字号(8–32px) |
| F-06 | Session 管理 API | `POST/GET/DELETE /api/sessions`;tmux 新建/切换/关闭 window |
| F-07 | 工具栏服务端持久化 | 配置存 `data/toolbar-config.json`(volume),跨设备共享 |
| F-08 | PWA 支持 | manifest.json + Service Worker,可添加主屏幕 |
| F-12 | claude -c 会话续接 | 自动检测 `.claude-data/.claude`,`claude -c` 续接历史会话 |
### Should(v1 Complete)
> 对应北极星「轴三:极致 Agent 管理体验」
| ID | Feature | 验收标准 |
|---|---|---|
| F-09 | Tab Bar UI | 顶部实时显示所有 tmux window,点击切换,活跃 tab 高亮 |
| F-10 | 移动端底导航 | 底部 Tab 快速切换 window,支持新建;覆盖顶部 Tab Bar |
| F-11 | 独立 window PTY | `ensurePty(windowId)` Map;`/ws?window=N` 多设备不互扰 |
| F-15 | Agent 状态卡片 | 每个 window 显示最后输出摘要(是否在跑/是否等待输入) |
### Could(v1 Complete)
> 对应北极星「轴二:零摩擦上下文同步」——不限于浏览器终端的交互渠道
*(F-13/F-14/F-16/F-17 已移除 — 非交互派发、上下文附件、Telegram Bot、多渠道路由功能已从代码库中移除,不再需要。)*
### Done(原 Nice/v4 — 已提前完成)
> 对应北极星「轴一:零配置启动」——消灭一切不必要的决策步骤
| ID | Feature | 验收标准 |
|---|---|---|
| **F-19** | **项目-窗口两级结构** | **项目 = 目录,窗口 = 同目录标签**。新建项目时选目录;新窗口自动继承当前目录;消灭「每次新建都要选目录」的重复操作 |
| **F-20** | **统一会话管理界面** | **借鉴 Slack Workspace/Channel 模式**:项目列表(下部)+ 窗口列表(上部),新建按钮分区放置,视觉层次清晰 |
---
## Feature Detail: 项目-窗口两级结构(F-19)
**问题**:当前每次新建 window 都要选目录,而用户心智模型是「项目=目录,窗口=同目录下的多个标签」。
**解法**:利用 tmux session 环境变量存储项目目录,实现「新建项目选目录,新建窗口自动继承」。
```
POST /api/windows
body: { rel_path?, shell_type?, profile? }
场景 1 - 新项目(提供 rel_path):
→ tmux set-environment NEXUS_CWD
→ tmux new-window -c "$dir"
场景 2 - 新窗口(不提供 rel_path):
→ cwd=$(tmux show-environment NEXUS_CWD | cut -d= -f2)
→ tmux new-window -c "$cwd"
```
**交互**:
- Sidebar 「+」按钮拆分为二级菜单:「📁 新项目」/「➕ 新窗口」
- 「新项目」→ 弹出 WorkspaceSelector 选目录
- 「新窗口」→ 直接创建,继承当前项目目录
**心智模型**:
- 项目 = 目录(首次需要指定)
- 窗口 = 同目录下的多个终端标签(自动继承目录)
---
## Feature Detail: 统一会话管理界面(F-20)
**核心映射**:直接使用 tmux 原生概念,前端映射更易理解
- **tmux session** → **Project**(工作目录,环境隔离)
- **tmux window** → **Channel**(终端标签,共享目录)
**设计灵感**:Slack 的 Workspace/Channel 两层导航结构
- Channel 列表(上部):当前 Project 下的多个终端窗口
- Project 列表(下部):不同的工作目录(每个对应一个 tmux session)
### 界面布局
```
┌─────────────────────────────┐
│ 会话管理 [×] │
├─────────────────────────────┤
│ │
│ 📂 nexus ~/work/nexus │ ← 标题:当前 Project 名+路径
│ ───────────────────────── │
│ #general ● │ ← Channel 列表(tmux windows)
│ #backend ○ │
│ #test ⏳ │
│ │
│ [+ 新 Channel] │ ← 在下方,靠近 Channel 列表
│ │
│ ═════════════════════════ │ ← 粗分隔线
│ │
│ 📁 Projects │
│ ● nexus (3) │ ← Project 列表(tmux sessions)
│ ○ my-app (1) │
│ ○ backend-api (2) │
│ │
│ [+ 新 Project] │ ← 在下方,靠近 Project 列表
│ │
└─────────────────────────────┘
```
### 关键设计决策
| 概念 | 对应 | 说明 |
|------|------|------|
| Project | tmux session | 每个 session 独立环境变量,有自己的 NEXUS_CWD |
| Channel | tmux window | 同 session 内的多个窗口,共享工作目录 |
| 激活态 | active session/window | 高亮显示当前所在的 project 和 channel |
### 状态指示
```
Channel 列表项(带 # 前缀):
#general ● ← 绿色点 = 运行中
#backend ○ ← 灰色点 = 空闲
#deploy ⏳ ← 黄色点 = 等待输入
#shell 💤 ← 灰色 = shell 状态
Project 列表项:
● nexus (3) ← 蓝色高亮 = 当前激活,(3)=3个channel
○ my-app (1) ← 未激活,有1个channel
○ backend-api (2) ← 未激活,有2个channel
○ legacy ← 无括号 = 该session没有窗口(异常)
```
### API 设计(简化版)
```
GET /api/projects → 列出所有 tmux sessions(Project 列表)
GET /api/projects/:name/channels → 列出指定 session 的所有 windows(Channel 列表)
POST /api/projects → 新建 Project
body: { name, path, shell_type, profile? }
→ tmux new-session -d -s -c
→ tmux set-environment NEXUS_CWD
POST /api/projects/:name/channels → 新建 Channel
body: { shell_type, profile? }
→ 在当前 session 内 tmux new-window -c "$NEXUS_CWD"
POST /api/projects/:name/activate → 切换到指定 Project
→ 切换 active tmux session(attach-client 或设置 target)
POST /api/channels/:index/attach → 切换到指定 Channel(已有接口)
DELETE /api/channels/:index → 关闭 Channel(已有接口)
```
### 交互流程
**1. 新建 Project**
```
点击「+ 新 Project」
→ 弹出 WorkspaceSelector 选择目录
→ 用户输入 Project 名称(默认目录名)
→ POST /api/projects { name: "my-app", path: "/home/libra/work/my-app" }
→ tmux new-session -d -s my-app -c /home/libra/work/my-app
→ tmux set-environment -t my-app NEXUS_CWD /home/libra/work/my-app
→ 在该 session 创建第一个 window(自动命名为 #general 或目录名)
→ 自动切换到新 Project(Project 列表更新,Channel 列表加载)
```
**2. 新建 Channel**
```
点击「+ 新 Channel」
→ 检查当前是否有激活 Project
→ POST /api/projects/:name/channels { shell_type, profile }
→ 在当前 session 内创建新 window
→ 自动继承该 session 的 NEXUS_CWD
→ Channel 列表更新,自动切换到新 Channel
```
**3. 切换 Project**
```
点击 Project 列表中的某项
→ 设置该 session 为 active
→ Channel 列表区域刷新:显示该 Project 的所有 Channel
→ 自动切换到该 Project 的 active window(或第一个 window)
→ 终端 WebSocket 重连到新的 session:window
```
**4. 切换 Channel**
```
点击 Channel 列表中的某项
→ POST /api/channels/:index/attach
→ 同现有行为:切换到该 window
```
### Channel 命名规则
```
第一个 Channel(创建 Project 时):
- 默认:目录名(如 nexus)
- 或:#general
后续 Channels:
- 默认:目录名-序号(如 nexus-1, nexus-2)
- 用户可重命名(重命名 tmux window)
```
### 空状态处理
**无任何 Project 时:**
```
Channel 列表区域:
「没有活跃的 Project」
Project 列表区域:
「暂无 Projects」
[+ 创建第一个 Project]
```
**当前 Project 无 Channel(异常情况):**
```
Channel 列表区域:
"📂 nexus ~/work/nexus"
「该 Project 没有 Channel」
[+ 创建第一个 Channel]
```
### 向后兼容
- 现有 tmux session 直接显示为 Projects(name 作为 project name)
- 现有 windows 显示为 Channels
- 首次打开界面时,为当前 session 尝试读取 NEXUS_CWD
- 如果未设置,提示用户「为当前 Project 设置工作目录」
- 或自动设置为 WORKSPACE_ROOT
### 视觉设计
```css
/* Channel 列表区域(上部)*/
- 标题栏:"📂 {project.name} {cwd路径}"(cwd 用灰色小字)
- Channel 项:"#{name}" 前缀(Slack 风格)
- 状态点:跟在名字后面
- +按钮:在区域底部,样式与列表项对齐
/* Project 列表区域(下部)*/
- 标题栏:"📁 Projects"
- 背景:var(--nexus-bg2) - 稍暗,与 Channel 区形成层次
- Project 项:简洁显示,名称 + 右侧 channel 计数
- +按钮:在区域底部
/* 分隔线 */
- Channel 区标题下:1px solid var(--nexus-border)
- 两区域之间:2px solid var(--nexus-border)
```
### 数据结构(前端状态)
```typescript
// 不再需要独立的 projects.json
// 直接从 tmux 读取
interface Project {
name: string; // tmux session name
path: string; // NEXUS_CWD (tmux show-environment)
active: boolean; // 是否是当前 active session
channelCount: number; // window 数量
}
interface Channel {
index: number; // tmux window index
name: string; // tmux window name
active: boolean; // 是否是该 session 的 active window
status: 'running' | 'idle' | 'waiting' | 'shell'; // 状态推断
}
```
---
## Success Metrics
| Metric | Target |
|---|---|
| 移动端 Esc/Ctrl+C 发送成功率 | 100% |
| 浏览器重连后终端恢复时间 | < 2s |
| 工具栏配置跨设备同步 | 重连后自动加载 |
| PWA 添加主屏并可用 | iOS Safari / Android Chrome |
---
## Out of Scope
- 多用户/团队功能、注册系统、权限管理
- 替换 tmux(持久化/scrollback 继续由 tmux 负责)
- 通用 Web SSH 工具(不针对 claude CLI 工作流的功能不做)
- Session 数据库(JSON 文件 + tmux 实时读取)
- Docker socket 暴露给前端
---
## Known Limitations(v1)
| 问题 | 影响 | 解法 |
|---|---|---|
| 多客户端 resize 冲突 | 多设备同时连接时 PTY 尺寸以最后收到的为准 | 直接使用当前客户端尺寸 |