# deepseek-driver

**[English](./README.en.md)** | 中文

浏览器端 TypeScript SDK，封装 DeepSeek 网页端（chat.deepseek.com）完整 API，支持 ESM/UMD 双格式。

## 特性

- 完整会话管理（创建 / 列表 / 重命名 / 删除 / 置顶）
- 流式消息发送（SSE AsyncIterator，区分 thinking / response / search delta）
- 消息编辑、重新生成、续写、反馈
- 文件上传（自动轮询处理状态）
- PoW 挑战求解（复用官方 WASM，Web Worker 内运行，不阻塞主线程）
- hif 心跳令牌自动获取
- **Token 自动发现**：页面注入脚本 / 浏览器扩展 / 前端网站三种场景均支持
- DOM 操作辅助（适用于页面注入场景）
- 零运行时依赖

## 安装

```bash
npm install deepseek-driver
```

安装后需将 PoW WASM 文件复制到项目的静态资源目录（`public/`）：

```bash
cp node_modules/deepseek-driver/dist/sha3_wasm_bg.wasm public/
```

然后通过 `wasmUrl` 参数指向它（见下方示例）。

## Token 获取方式

SDK 支持两种使用场景，token 获取策略不同：

### 场景一：浏览器扩展（Chrome Extension）

扩展无法直接使用页面 Cookie，但可以通过以下方式自动获取 token：

**推荐方式：拦截请求头（最可靠）**

在 Background Service Worker 中监听 DeepSeek 的 API 请求，自动提取并缓存 token：

```js
// background.js
import { extractTokenFromRequestHeaders, cacheTokenToExtensionStorage } from 'deepseek-driver'

chrome.webRequest.onBeforeSendHeaders.addListener(
  (details) => {
    const token = extractTokenFromRequestHeaders(details.requestHeaders)
    if (token) cacheTokenToExtensionStorage(token)  // 缓存到 chrome.storage.local
  },
  { urls: ['https://chat.deepseek.com/api/*'] },
  ['requestHeaders']
)
```

之后在任何地方创建 driver，会自动从 `chrome.storage` 读取缓存的 token：

```ts
// popup.js 或 content_script.js
import { DeepSeekDriver } from 'deepseek-driver'

// skipApiDiscover: true —— 跳过直接 API 调用（Background 无法使用），
// 优先读取 chrome.storage / chrome.cookies
const driver = new DeepSeekDriver({
  wasmUrl: chrome.runtime.getURL('sha3_wasm_bg.wasm'),
  skipApiDiscover: true,
})

// token 会自动从 chrome.storage 中发现，无需手动传入
const session = await driver.createSession()
```

manifest.json 需要声明相应权限：

```json
{
  "permissions": ["webRequest", "storage", "cookies"],
  "host_permissions": ["https://chat.deepseek.com/*"]
}
```

### 场景二：页面注入脚本 / 用户脚本（同域）

在 `chat.deepseek.com` 域内运行的脚本（Content Script、油猴脚本等），fetch 自动携带页面 Cookie，SDK 会直接调用 `/api/v0/users/current` 获取 token，**无需任何配置**：

```ts
import { DeepSeekDriver } from 'deepseek-driver'

const driver = new DeepSeekDriver({
  wasmUrl: '/sha3_wasm_bg.wasm',
  // 不传 token，SDK 自动通过 API 获取（fetch 携带页面 Cookie）
})

// 第一次调用任意 API 时自动完成 token 发现
const session = await driver.createSession()

// 流式发送消息
for await (const event of driver.sendMessage({
  chatSessionId: session.id,
  prompt: '你好',
  searchEnabled: true,
})) {
  if (event.type === 'thinking') {
    process.stdout.write('[思考] ' + event.content)
  } else if (event.type === 'response') {
    process.stdout.write(event.content)
  } else if (event.type === 'done') {
    console.log('\n完成')
  }
}
```

### 场景三：手动传入 token（任意环境）

```ts
const driver = new DeepSeekDriver({
  token: 'your-bearer-token',
  wasmUrl: '/sha3_wasm_bg.wasm',
})
```

### 便捷方法：一次性获取完整回复

```ts
const { thinking, response } = await driver.chat(session.id, '介绍一下量子计算', {
  thinkingEnabled: true,
  searchEnabled: false,
})
console.log('思考过程:', thinking)
console.log('回答:', response)
```

## 配置项

```ts
interface DeepSeekDriverOptions {
  token?: string           // Bearer token，不传则自动发现
  wasmUrl?: string         // PoW WASM 文件 URL，默认 './sha3_wasm_bg.wasm'
  baseUrl?: string         // API 基础 URL，默认 'https://chat.deepseek.com'
  skipApiDiscover?: boolean // 跳过 API 发现策略，扩展 Background 应设为 true
}
```

## Token 自动发现 API

除通过 `DeepSeekDriver` 自动使用外，也可单独调用：

```ts
import { autoDiscover, isExtensionContext, isDeepSeekPage } from 'deepseek-driver'

// 检测环境
console.log('是否扩展上下文:', isExtensionContext())
console.log('是否 DeepSeek 页面:', isDeepSeekPage())

// 自动发现，按优先级：storage → cookies → API → document.cookie → window 全局变量
const creds = await autoDiscover()
if (creds) {
  console.log('发现 token，来源:', creds.source)
  console.log('token:', creds.token)
}
```

发现优先级（`source` 字段）：

| source | 说明 | 适用场景 |
|--------|------|---------|
| `extension-storage` | chrome.storage 缓存 | 扩展（最快，无请求） |
| `extension-cookie` | chrome.cookies API | 扩展 Background |
| `api` | /api/v0/users/current | 同域页面 / Content Script |
| `cookie` | document.cookie 解析 | 同域页面 |
| `page-global` | window 全局变量 | 特殊注入环境 |
| `manual` | 手动传入 | 任意环境 |

## PoW WASM 文件

PoW 求解需要 DeepSeek 官方 WASM 文件（`sha3_wasm_bg.wasm`）。
项目根目录已附带该文件，需将其托管到与页面同域的路径下，通过 `wasmUrl` 配置指定。

若 WASM 加载失败，SDK 会自动回退到纯 JS 实现（速度稍慢）。

## API 参考

### 认证

| 方法 | 说明 |
|------|------|
| `getCurrentUser()` | 获取当前登录用户，自动缓存 token |
| `setToken(token)` | 手动设置 token |
| `getToken()` | 获取当前缓存的 token |

### 会话管理

| 方法 | 说明 |
|------|------|
| `createSession()` | 创建新会话 |
| `listSessions(cursor?, count?)` | 获取会话列表（分页） |
| `listAllSessions()` | 获取全部会话（自动翻页） |
| `renameSession(id, title)` | 重命名会话 |
| `deleteSession(id)` | 删除会话 |
| `deleteAllSessions()` | 删除全部会话 |
| `pinSession(id, pinned)` | 置顶/取消置顶 |
| `getHistoryMessages(id)` | 获取历史消息 |

### 消息收发

| 方法 | 说明 |
|------|------|
| `sendMessage(params)` | 发送消息，返回 `AsyncGenerator<StreamEvent>` |
| `editMessage(params)` | 编辑消息，返回 `AsyncGenerator<StreamEvent>` |
| `regenerate(params)` | 重新生成，返回 `AsyncGenerator<StreamEvent>` |
| `continueMessage(params)` | 续写 |
| `messageFeedback(sessionId, messageId, type)` | 消息反馈（`'GOOD'`/`'BAD'`/`null`） |
| `chat(sessionId, prompt, opts?)` | 便捷方法，返回完整 `{ thinking, response }` |

#### SendMessageParams

```ts
{
  chatSessionId: string
  prompt: string
  parentMessageId?: number | null    // 续写时传入上一条消息 ID
  modelType?: 'default' | 'expert' | 'vision' | null
  thinkingEnabled?: boolean          // 深度思考，默认 false
  searchEnabled?: boolean            // 智能搜索，默认 true
  refFileIds?: string[]              // 文件附件 ID 列表
  preempt?: boolean
}
```

#### StreamEvent 类型

| type | 说明 | 字段 |
|------|------|------|
| `ready` | 流就绪 | `requestMessageId`, `responseMessageId`, `modelType` |
| `thinking` | 思考过程 delta | `content` |
| `response` | 正文回复 delta | `content` |
| `search` | 搜索信息 | `queries?`, `results?` |
| `status` | 状态变更 | `status` |
| `title` | 会话标题生成 | `title` |
| `done` | 流结束 | `autoResume` |

### 文件上传

```ts
const file = new File(['内容'], 'test.txt', { type: 'text/plain' })
const uploaded = await driver.uploadFile(file)
// uploaded.id 即可用于 refFileIds

const result = await driver.chat(session.id, '分析这个文件', {
  refFileIds: [uploaded.id],
})
```

### DOM 辅助（注入脚本场景）

```ts
import {
  clickNewChat,
  toggleThinking,
  toggleSearch,
  setInputText,
  clickSend,
  getMessages,
  getSessionList,
  getCurrentSessionId,
} from 'deepseek-driver'

clickNewChat()                  // 点击「开启新对话」
toggleThinking(true)            // 开启深度思考
setInputText('你好')            // 设置输入框文字
clickSend()                     // 点击发送

const msgs = getMessages()      // 读取页面消息列表
const sessions = getSessionList() // 读取侧边栏会话列表
```

## 测试页面

项目包含本地交互式测试页面，用于验证 SDK 各项 API 功能。

### 配置

```bash
cp test/.env.example test/.env
```

编辑 `test/.env`，填入你的 DeepSeek Bearer Token：

```
VITE_DEEPSEEK_TOKEN=your_bearer_token_here
```

### 运行

```bash
npm run test:serve
```

浏览器自动打开 http://localhost:5174

详细说明参见 [test/README.md](test/README.md)

## 更新记录

### 0.1.0

- 初始版本
- 完整会话 / 消息 / 文件 API
- PoW WASM + Web Worker 求解
- hif 心跳令牌自动获取
- SSE 流解析（SET/APPEND/BATCH 补丁）
- DOM 操作辅助模块

## 致谢与声明

本项目基于对 [DeepSeek](https://www.deepseek.com/) 网页端（chat.deepseek.com）的逆向分析与研究实现。

> **⚠️ 免责声明**
>
> - 本项目为**非官方**的社区维护项目，与 DeepSeek 官方无任何关联。
> - 本项目仅供学习、研究和个人使用，请勿用于任何违反 DeepSeek 服务条款的场景。
> - DeepSeek 随时可能更新其网页端实现，本项目不保证持续可用。
> - 使用本项目产生的任何后果由使用者自行承担。

感谢 DeepSeek 团队构建了优秀的 AI 产品。

## License

MIT
