# Web UI API 文档（非 `/api/v1`）

> 本文档描述**网页端使用的 Session/CSRF 接口**，不包含 `/api/v1`。  
> 适合在**不启动前端**的情况下，用脚本/服务调用同样的网页端 API。

---

## 1. 基本信息

- 默认地址：`http://localhost:8091`
- 鉴权方式：**Session Cookie**
- CSRF：除 GET/HEAD/OPTIONS/TRACE 外，`/api/*` 需要 **`X-CSRF-Token` + Cookie**

> 说明：CSRF 由 `attach_security_hooks` 统一拦截；`/api/csrf-token` 会写入 Session 并返回 token。

---

## 2. CSRF 获取

### `GET /api/csrf-token`

**用途**：获取 CSRF Token（同时写入 Session Cookie）。

**响应示例**
```json
{ "success": true, "token": "xxx" }
```

**后续请求需要**：
```
X-CSRF-Token: <token>
Cookie: <session cookie>
```

---

## 3. 登录（Docker 模式）

### `POST /login`

**用途**：使用邮箱 + 密码登录（Docker 模式）。

**请求头**
```
Content-Type: application/json
X-CSRF-Token: <token>
```

**请求体**
```json
{
  "email": "jojo@local",
  "password": "******"
}
```

**成功响应**
```json
{ "success": true }
```

**失败响应（示例）**
```json
{ "success": false, "error": "账号或密码错误" }
```

---

## 4. 宿主机模式登录

### `GET /api/host-mode-enabled`

**用途**：确认是否允许宿主机模式。

**响应示例**
```json
{ "success": true, "enabled": true }
```

### `POST /host-login`

**用途**：宿主机一键登录（仅当 `TERMINAL_SANDBOX_MODE=host` 时可用）。

**请求头**
```
X-CSRF-Token: <token>
```

**响应示例**
```json
{ "success": true }
```

---

## 5. 对话管理

### 5.1 创建对话
`POST /api/conversations`

**请求体（可选）**
```json
{
  "preserve_mode": false,
  "mode": "fast",
  "thinking_mode": false
}
```

- `preserve_mode=true` 时才会使用 `mode/thinking_mode`；否则回到默认配置。

**响应示例**
```json
{
  "success": true,
  "conversation_id": "conv_20260315_162256_599",
  "message": "已创建新对话: conv_20260315_162256_599"
}
```

### 5.2 列表查询
`GET /api/conversations?limit=20&offset=0`

### 5.3 获取对话信息
`GET /api/conversations/<conversation_id>`

### 5.4 加载/切换对话
`PUT /api/conversations/<conversation_id>/load`

### 5.5 获取对话消息
`GET /api/conversations/<conversation_id>/messages?limit=50`

### 5.6 删除对话
`DELETE /api/conversations/<conversation_id>`

---

## 6. 模型与思考模式

### 6.1 切换模型
`POST /api/model`

**请求体**
```json
{ "model_key": "qwen3-vl-plus" }
```

**响应示例**
```json
{
  "success": true,
  "data": {
    "model_key": "qwen3-vl-plus",
    "run_mode": "fast",
    "thinking_mode": false
  }
}
```

### 6.2 切换思考模式
`POST /api/thinking-mode`

**请求体（两种写法）**
```json
{ "mode": "thinking" }
```
或
```json
{ "thinking_mode": true }
```

**响应示例**
```json
{
  "success": true,
  "data": { "thinking_mode": true, "mode": "thinking" }
}
```

---

## 7. 图片/视频上传（用于多模态消息）

### 7.1 上传文件
`POST /api/upload`

**用途**：上传图片/视频，返回相对路径（通常为 `user_upload/<filename>`），之后将该路径放到 `POST /api/tasks` 的 `images` 或 `videos` 里。

**请求类型**
```
Content-Type: multipart/form-data
```

**表单字段**
- `file`: 文件本体
- `filename`: 原始文件名（可选）

**响应示例**
```json
{
  "success": true,
  "path": "user_upload/1.jpg",
  "filename": "1.jpg",
  "folder": "user_upload",
  "scan": { "status": "skipped" },
  "sha256": "....",
  "size": 118897
}
```

**注意事项**
- 上传功能可能被管理员策略禁用（返回 403）。
- 文件过大时返回 413（单文件大小受 `MAX_UPLOAD_SIZE` 限制）。

### 7.2 多模态模型要求
当前网页端逻辑限制：  
**仅以下模型支持图片/视频**
- `qwen3-vl-plus`
- `kimi-k2.5`

在发送图片/视频前，请先 `POST /api/model` 切换到上述模型，否则会报错。

---

## 8. 发送消息（任务式 / 轮询）

### 8.1 创建任务（发送消息）
`POST /api/tasks`

**请求体**
```json
{
  "message": "今天英伟达的股价是多少？",
  "conversation_id": "conv_20260315_162256_599",
  "images": ["user_upload/1.jpg"],
  "videos": [],
  "model_key": null,
  "thinking_mode": null,
  "run_mode": null,
  "max_iterations": 100
}
```

**响应示例（202）**
```json
{
  "success": true,
  "data": {
    "task_id": "5aae14a2-cd12-4644-b5eb-c394b83f4045",
    "status": "running",
    "created_at": 1773562976.607287,
    "conversation_id": "conv_20260315_162256_599"
  }
}
```

### 8.2 轮询任务事件
`GET /api/tasks/<task_id>?from=0`

**响应要点**
```json
{
  "success": true,
  "data": {
    "status": "succeeded",
    "events": [
      { "type": "text_chunk", "data": { "content": "..." } },
      { "type": "text_end", "data": { "full_content": "..." } }
    ],
    "next_offset": 437
  }
}
```

> `text_end.data.full_content` 是最终整段回复；`next_offset` 用于增量轮询。

### 8.3 查询任务列表
`GET /api/tasks`

### 8.4 取消任务
`POST /api/tasks/<task_id>/cancel`

---

## 9. 可选：WebSocket Token（如果需要实时推送）

### `GET /api/socket-token`

返回 `{ success: true, token: "...", expires_at: ... }`  
Socket.IO 连接时将 token 放到 `auth` 字段中：

```js
io('/', { auth: { socket_token: '<token>' } })
```

---

## 10. 最小调用流程（示例）

1) `GET /api/csrf-token`  
2) `POST /login`（或 `POST /host-login`）  
3) `GET /api/csrf-token`（刷新 token）  
4) `POST /api/conversations`  
5) `POST /api/tasks`  
6) `GET /api/tasks/<task_id>?from=0` 轮询直到 `status=succeeded`

---

## 11. 常见错误码

- `401 Unauthorized`：未登录或 Session 失效  
- `403 CSRF validation failed`：缺少/错误的 `X-CSRF-Token`  
- `409`：同一用户同一工作区已有运行中任务（任务互斥）  
- `503`：资源繁忙 / 宿主机模式不可用
