# 任务:用 DeepSeek 账号登录 DSH,触发 6 元体验赠金发放 你是一名执行型编码智能体。请按本文档实现并跑通一个**单文件脚本**,帮助**当前这台机器的用户本人**,用他自己的 DeepSeek 账号完成一次 DeepSeek Harness(DSH)登录授权,并读取赠金结果。 本任务是**一次性、单账号、用户本人授权**的操作。不要做任何形式的批量、多账号或自动化重复执行。 --- ## 一、背景事实(省去你重复调研,可直接采信) - **DSH = DeepSeek Harness**,DeepSeek 开源的 Agent 运行时(`github.com/deepseek-ai/deepseek-harness`)。 - 活动:用 DeepSeek 账号登录 DSH,服务端发放 **6 元「DSH 体验赠金」**(内部 campaign 标识 `dsh_login_bonus`),进账号的 `bonus_wallets`,与充值余额 `normal_wallets` 分列。 - **关键认知:这个活动没有「领取」按钮。** 客户端只负责「读取未通知赠金 → 展示卡片 → 回执已读」,赠金**完全由服务端在登录授权成功那一刻发放**。所以完成任务 = 完成一次账号登录授权,没有任何 claim / redeem 接口可调。 - **不需要下载桌面端。** DSH 的 `web` profile 服务端本身就挂载了账号控制器(`account-controller`),只是它的客户端 UI 被 `'dshDesktop' in globalThis` 门控(仅桌面渲染进程可见)。直接走平台官方的浏览器授权流即可,实测**诚实上报 `x-client-platform: web` 也能拿到赠金**。 - **官方资格条款**(务必先告知用户): - 适用于「活动开始前已注册」的 DeepSeek 用户,或「活动开始后用中国大陆手机号(+86,不含虚拟号段)注册」的用户。 - 活动开始前已在 DSH 里登录过的用户,需**先登出再重新登录**才能领。 - 赠金**限量发放、赠完即止**,**到账后 7 天内有效**。 - 若不符合资格,服务端不发——这种情况要如实告知用户,不要试图绕过。 ### 协议来源(可自行核对,也可验证本文档) 这是还原自官方客户端 `@deepseek-ai/dsh-deepseek-account-platform` 的真实实现,不是猜的。相关源码位置: `packages/credentials/deepseek-account-platform/src/index.ts` 与 `src/protocol.ts`; 账号服务定义在 `packages/credentials/deepseek-account/src/index.ts`。 --- ## 二、硬性约束(违反即任务失败) 1. **网络范围**:只允许请求 `https://platform.deepseek.com`。不要访问任何第三方站点。 2. **禁止伪造身份**:`x-client-platform` 必须保持 `web`。**绝对不要**改成 `desktop-win` / `desktop-mac` 去骗服务端。 3. **禁止代填凭据**:不要代理用户输入手机号、密码、短信验证码。登录与授权**必须由用户本人在浏览器里亲自完成**。 4. **禁止调用 `ack_bonus_notified`**:那是「已读回执」,调了会把用户后续在 DSH 里本该看到的「赠金已到账」卡片标记成已读。 5. **禁止滥用**:只为当前用户自己的一个账号执行一次。不要写循环、不要遍历账号、不要做自动化重复领取。 6. **凭据纪律**:拿到的 `token` 是账号级凭据—— - 只写入本机 `0600` 权限的文件; - 终端输出必须脱敏(例如只显示前 6 位 + 长度); - **绝不允许写进任何 git 仓库工作区**; - 任务结束后默认删除,或明确交给用户决定去留。 7. **失败要诚实**:若因资格不符或客户端类型限制而未到账,如实报告「没领到」以及观察到的事实。不要伪造成成功,也不要采取任何绕过手段。 --- ## 三、协议规格(已核实,按此实现) 平台 origin 固定为 `https://platform.deepseek.com`。 ### 3.1 通用请求头(每个请求都带) ``` x-client-bundle-id: '' # 有意留空 x-client-platform: 'web' # 诚实身份,不得更改 x-client-version: '<任意合理版本串>' # 例如 '0.2.0-rc.2' x-client-locale: 'zh_CN' # 或 en_US,按用户界面语言 x-client-timezone-offset: '<秒>' # 本地时间减 UTC 的秒数;UTC+8 => 28800 user-agent: '<一个普通浏览器 UA>' # 必须带,否则可能被 WAF 拦 accept: application/json ``` 带 body 的请求再加 `content-type: application/json`。 账号读取类请求额外带 `x-dsh-auth-token: `。 ### 3.2 响应信封 ```json {"code": 0, "data": {"biz_code": 0, "biz_data": }} ``` 判定规则: - `status === 401` 或顶层 `code === 40003` → **鉴权失效**; - 非 2xx → 协议/网络失败; - 顶层 `code !== 0` → 信封无效; - `data.biz_code !== 0` → 业务失败。 失败时只输出「阶段名 + HTTP 状态 + 业务 code + 路径」,**不要打印响应正文中的敏感内容**。 > 注意:`auth_init` 若字段缺失,平台会返回 FastAPI 风格的 `{"detail":[{"loc":"body.xxx"}]}`(HTTP 422),而不是上面那个信封。可把它当成「参数不合法」处理。 ### 3.3 步骤 A:初始化 PKCE: ```js verifier = crypto.randomBytes(32).toString('base64url') state = crypto.randomBytes(32).toString('base64url') challenge = sha256(verifier).digest('base64url') // 注意是 verifier 的 sha256 ``` `POST /auth-api/v0/dsh/auth_init` ```json { "code_challenge": "", "code_challenge_method": "S256", "state": "", "redirect_uri": "http://127.0.0.1:/oauth/callback", "locale": "zh_CN", "login_source": "web" } ``` 成功 `biz_data` 形如:`{ "authorize_url": "https://platform.deepseek.com/dsh/authorize?...", "authorize_id": "...", "expires_in": 600 }` **端口必须先确定**:先 `server.listen(0, '127.0.0.1')` 拿到系统分配的端口,再用它拼 `redirect_uri`,然后才发 `auth_init`。 `authorize_url` 必须校验:同源 `https://platform.deepseek.com`、路径**正好**是 `/dsh/authorize`、无用户名密码、无 fragment。不满足则视为协议异常。 ### 3.4 步骤 B:本地回调 起 `http://127.0.0.1:`,**精确**处理 `GET /oauth/callback`: - 校验 `state`(先比长度再 `crypto.timingSafeEqual`)、`code` 存在、`state` 与 `code` **各只出现一次**; - 任一不满足 → `400`; - 若本次已有待处理回调 → `410`; - 通过后**先只记下 code,不要立刻响应**;等步骤 C 兑换成功,再 `302` 跳到 `?login_source=web`(`authorized_url` 来自 C 的响应,需同样校验同源 + 路径正好 `/dsh/authorized`)。 - 所有响应加 `cache-control: no-store`。 这期间把 `authorize_url` 打印出来,并**打开浏览器让用户自己登录**。 ### 3.5 步骤 C:兑换 `POST /auth-api/v0/dsh/auth_exchange` ```json { "code": "<回调收到的 code>", "code_verifier": "", "redirect_uri": "<与 auth_init 完全相同的字符串>", "device_id": "<一个持久化的随机 UUID>", "device_model": "-", "os_version": " " } ``` 成功 `biz_data`:`{ "token": "...", "authorized_url": "...", "user": {...} }`。 `device_id` 语义是「设备标识」:首次生成随机 UUID 并持久化到本机文件,之后复用(模拟官方客户端行为)。 ### 3.6 步骤 D:验证(这才是「拿到结果」) 带 `x-dsh-auth-token` 依次读取: | 用途 | 请求 | |---|---| | 账号资料 | `GET /auth-api/v0/users/current` | | 钱包(重点) | `GET /api/v0/users/get_user_summary` → 看 `normal_wallets`(充值)与 **`bonus_wallets`(赠金)**,分开打印 | | 未通知赠金 | `GET /api/v0/users/get_unnotified_bonuses` → 含 `campaign` / `amount` / `msg` / `expires_at` | **成功判据**:`bonus_wallets` 中出现 `6.00 CNY`(或其一部分),且 `get_unnotified_bonuses` 里出现 `campaign: "dsh_login_bonus"`。 ### 3.7 超时与取消 - 等回调的总窗口取 `min(用户可配的超时, expires_in * 1000)`,默认 15 分钟。 - 超时或用户 Ctrl-C 时,调一次 `POST /auth-api/v0/dsh/auth_cancel`,body: `{ "authorize_id": "", "code_verifier": "" }`,失败忽略即可。 --- ## 四、实现要求 - **单文件脚本**,Node ≥ 22(用内置全局 `fetch`、`AbortSignal.timeout`)。 - **不引入任何第三方依赖**(只用 `node:http` / `node:crypto` / `node:os` / `node:fs` / `node:path`)。 - 提供一个 `verify` 子命令:跳过登录,直接用已存 token 重跑步骤 D。 - 退出码:成功 `0`;超时 `1`;协议/网络/鉴权失败 `2`;SIGINT `130`。 - 代码简洁、扁平、错误处理明确,不要深层嵌套。 - 所有对外错误信息**不得包含** token、code、code_verifier 等机密。 ### 参考流程(伪代码) ``` 1. listen(0,'127.0.0.1') → port;redirect_uri = http://127.0.0.1:port/oauth/callback 2. 生成 verifier / state / challenge 3. auth_init → authorize_url(校验)→ 打印 + 通知用户去浏览器登录 4. 等待回调 → 校验 state/code → 得到 code 5. auth_exchange → token、authorized_url(校验) 6. 用 authorized_url + login_source=web 回 302 给浏览器 7. 用 token 读 current / get_user_summary / get_unnotified_bonuses 8. 分开打印「充值余额」「赠金」,写入 0600 文件,token 脱敏 9. 提示用户:赠金已到账、7 天内有效;并清理或经用户同意后保留凭据文件 ``` --- ## 五、验收标准(必须实际执行,不能只写代码) 1. `node --check