跳过主要内容
L

dsh-claude-subscription

lornwolf/dsh-claude-subscription

通过 OAuth 登录接入 Claude Pro/Max 订阅模型,支持多账号管理、额度查询和设置面板。请求使用 Claude Code 客户端身份发送,额度查询走实验性接口。

安装

dsh plugin --profile web add github:lornwolf/dsh-claude-subscription

README

dsh-claude-subscription

在 DeepSeek Harness 里直接使用 Claude Pro / Max 订阅。

它做什么

能力状态说明
订阅 OAuth 登录✅Claude Code 同一条授权通道(PKCE,claude.ai/oauth/authorize),不读 ~/.claude
模型直接可选✅注册 anthropic 路由,模型目录来自 pi-ai 的 anthropic 目录(17 个 chat 模型)
多账号✅一个凭据记录存数组;刷新和切换/增删/清空共用 vault 事务队列,切换会等待刷新结束
客户端伪装✅UA / x-app / anthropic-beta / system[0] 身份句 / 去掉浏览器专属头
额度⚠️ 实验调 Claude Code /usage 用的 api/oauth/usage,字段以服务端返回为准
系统提示与工具✅ 修补pi-ai 1.1.0 的 anthropic 分支不读 context.systemPrompt/context.tools,本插件把两者物化进首条 system 消息,否则请求里既没 system 也没 tools
设置页✅左侧「Claude 订阅」分区:登录、粘贴授权码、真实账号名与计划、额度卡片、切账号、看模型与指纹

安装

dsh plugin --profile desktop add file:./vendor/dsh-claude-subscription-0.1.4.tgz

或在 profile 的 package.json 里加依赖与 bundle 后重启宿主。

Claude 5 及以上:精简系统提示

~/.dsh/prompt-inject.md 如果存在,宿主会把它拼进系统提示最前面。实测:

模型带注入段的请求
claude-fable-5-1 / claude-opus-5 / claude-sonnet-5 等 5+被 Anthropic 生成前拦截:violative cyber content
claude-sonnet-4-6 / claude-opus-4-8 等 4.x 及更早正常
DeepSeek / GPT 路线不受影响(不过那道分类器)

所以插件对 Claude 5+ 的请求改用精简系统提示:只发「Claude Code 身份句 + 一段中性工程助手设定 + ~/.dsh/AGENTS.md(截断 4KB)」,不再发送 DSH 的整段系统提示(其中可能夹着注入段)。工具表由 tools 字段单独发送,不受影响。

为什么不是"剔除注入段":实测 DSH 拼装时会重写注入的首行(<|im_start|>system:<project_instructions> 被拆成两行),注入内容也不是文件原文的逐字拷贝,且它与 harness 提示拼成同一段文本 —— 按原文做子串切分既容易失配、也可能误删整段系统提示。精简模式是确定性的。

  • 4.x 及更早的 Claude、DeepSeek、ChatGPT:完全不受影响,系统提示原样发送
  • 开关:config.modernClaudeMode("minimal" 默认 / "strip-inject" 只剔除注入段)、config.sanitizeModernClaude: false 关闭、config.injectFile 指定注入文件
  • 诊断:默认把每次 5+ 请求的 system 摘要写入 ~/.dsh/state/claude-subscription/last-request.json(可用 config.diagnostics: false 关闭)
  • 正文过滤:lib/text-filter.js 在流式文本里删掉模型手写的 <invoke …>…</invoke> 工具语法(config.filterNarration: false 可关闭),工具结果卡片不受影响;完整的 <invoke> 调用块会转成真正的工具调用执行,调用之后的正文整块丢弃(那是模型在拿到工具结果前凭空写的结论,例如「联网工具全都没返回,没法下结论」)
  • 空回复兜底:lib/empty-response.js。Claude 5+ 偶发返回「只有 thinking、没有正文和工具调用」的 end_turn,DSH 的判空只看 content.length === 0,会把它当成正常完成、界面停在白屏。这里先缓冲首轮事件,确认产出再放行;确实为空就原样重发一次,两次都空则抛出明确的 PI_AI_ERROR
  • thinking 签名:lib/fingerprint.js 的 stripReasoning 在出站前丢掉 assistant 历史里的 reasoning 块。签名绑定的是模型原始内容,而我们会剥掉 <invoke>、追加合成 tool_use,签名必然失效;实测服务端对失效前缀的回应就是那条空回复,而不带 thinking 块的请求被正常接受(服务端对 thinking.type.disabled 与 thinking.adaptive.block_binding 都回 400,前者不支持、后者是未知字段)

设置页(左侧「Claude 订阅」)

lib/client.js 往设置面板的 settings.section 槽注册一个分区:登录按钮、授权链接与授权码输入、账号列表(切换/移除)、额度、模型清单、当前指纹。浏览器只拿到状态与授权链接,凭据与网络请求都留在宿主进程。

宿主侧由 lib/rpc.js 在 connection.fetch 上挂 /api/claude-subscription/* 八个端点(status / login / login/submit / login/cancel / logout / quota / accounts/select / accounts/remove),信封走 DSH 既有的 {rpcId, method, payload} → {type: "server-response", rpcId, result}。

需要 ui-settings 处于启用状态才有设置面板可挂;有的部署把它关掉了,那时用下面的工具同样能完成登录。

工具(会话里直接说人话即可)

工具用途
claude_subscription_status登录状态、账号列表、可用模型、当前指纹
claude_subscription_login取授权链接(copy_code 默认,或 browser 起 53692 回调)
claude_subscription_login_submit提交授权页给出的 code#state
claude_subscription_account_select / _remove多账号切换 / 移除
claude_subscription_logout清空本地账号
claude_subscription_quota查额度窗口与重置时间

伪装层怎么工作

lib/context.js + lib/fingerprint.js,全部在请求离开本机前完成,不依赖宿主文件补丁——即使宿主里的 pi-ai 被改写或升级,这里在运行期仍会重新补回。

  1. 请求头:user-agent: claude-cli/2.1.280 (external, cli)、x-app: cli、anthropic-beta 补齐 claude-code-20250219 + oauth-2025-04-20;删掉 anthropic-dangerous-direct-browser-access(浏览器 SDK 才会带,官方 CLI 不发)与残留 x-api-key(订阅只认 Bearer)。
  2. 请求体:system[0] 固定为 You are Claude Code, Anthropic's official CLI for Claude.,宿主自己的提示词接在后面;幂等,不会重复插。
  3. 工具表:关掉 supportsMidConvoSystemMessages / supportsMidConvoToolChanges 两条 compat,避免 pi-ai 在请求里塞 __pi_deferred_placeholder__ 占位工具,让每个请求只带一份固定工具表(与 Claude Code 一致)。

刻意没有做的指纹项:工具名按 Claude Code 的大小写重命名(要在流里反解回来,容易把工具调用搞坏)、metadata.user_id(格式拿不准,默认关闭,可在 config 里打开)。

配置

apply(ctx, config) 接受:

- id: claude-subscription
  name: 'dsh-claude-subscription'
  config:
    displayName: Claude 订阅
    identity:
      userAgent: claude-cli/2.1.280 (external, cli)
      appHeader: cli
      dropBrowserHeaders: true
      dropSdkHeaders: false      # 打开后连 x-stainless-* 也删
      metadataUserId: null       # 填字符串则带上 metadata.user_id

0.1.4 设置页额度 RPC 修复

  • 设置页 quota 端点原来调用了两个根本没导入的标识符(readResolvedSubscriptionUsage、summarizeUsage,只有 export … from 的再导出,不产生本地绑定),每次点击都抛 ReferenceError。改为走 readAccountSubscriptionUsage():带 accountId、窗口、额外用量与抓取时间,上游原始 payload 不回传浏览器。
  • RPC 失败信封补上 error.details: {}。宿主客户端 parseConnectionResponse 要求 details 是对象,缺了会抛 connection: invalid server-response failure,把真实错误吞掉——面板只剩一句看不懂的连接错误,这正是「点查询额度没反应」的观感来源。
  • 新增两条测试:宿主冒烟里跑一次真实 quota RPC(断言 accountId、两个窗口、无 raw),以及 RPC 失败信封形状测试。

0.1.3 设置面板:真实账号名、额度卡片、字体统一

  • 账号名来自 api/oauth/profile(displayName / email / plan / organization),由 status RPC 在缓存过期时刷新并写回 vault;默认占位名(Claude 1)会被真实名字替换,用户自己起的备注名不动。
  • 面板不再显示 vault 内部 id(claude-mv…);账号行只显示名字、邮箱与计划。
  • 额度改成卡片式:窗口名、剩余百分比、进度条、剩余与重置时间;面板打开时自动读一次额度(每个账号一次)。不再输出 pre 纯文本。
  • 面板所有文字(含客户端指纹区)统一沿用宿主默认字体;只有百分比数字用 font-variant-numeric: tabular-nums 保证对齐。
  • 账号资料刷新失败时显示缓存并给出一行提示,不会让面板空掉。
  • 新增/更新设置面板测试,覆盖自动读额度、账号切换丢弃旧额度、无内部 id 与字体断言。

0.1.2 修复宿主归一化后的工具与思考历史

  • 实际 DSH 会先调用 Models.normalizeContext,将工具表放进首条 system 的 toolsAdded。精简模式现仅替换该消息的文字,保留工具增删与其他元数据,避免出站 tools: []。
  • stripReasoning 同时移除 Harness 的 reasoning 和 pi-ai 的 thinking 历史块(包括携带签名或 redacted 标记的 thinking),而非只匹配一种字段名;不伪造或改写签名。
  • 新增真实 Models.streamSimple → normalizeContext → minimal → provider 整链路离线测试,覆盖 Fable 5 工具表、原生/XML 调用后的结果回放及思考签名清理。
  • 用真实订阅和隔离工具夹具跑过两轮:首轮调用 read,第二轮收到夹具结果并正常输出标记。

0.1.1 认证与可靠性修复

  • OAuth 使用插件自己的 lib/oauth.js,独立生成 state 与 PKCE verifier,保留 browser / copy_code 两种方式及现有凭据格式;结构化授权结果严格校验唯一 code/state。裸授权码保留兼容支持。
  • 账号刷新与切换/新增/删除/清空共用同一 vault 的事务队列;多个 store 共享同一 vault 时有效,不是跨进程锁。损坏的存储报错并保留原值。
  • 登录默认 10 分钟总期限;提交默认 120 秒,超时会取消后台任务;取消旧登录不会清掉新登录。设置页取消按钮现在实际调用宿主取消接口。
  • 额度请求先解析/刷新授权,30 秒网络超时并禁止重定向;RPC、工具错误不回显服务端正文或未知异常详情。
  • 订阅请求仅接受 OAuth token 和官方 API origin,最终 Authorization 由插件持有;出站日志不再记录 HTTP 错误正文。
  • 新增离线并发、OAuth、登录、RPC/额度和真实 SDK 请求契约测试。npm test 运行测试;真实 SDK 契约测试需能解析 pi-ai,或设置 PI_AI_PACKAGE_ROOT 为只读包根目录。指定包根却加载失败会使测试失败;没有可解析 SDK 时明确显示跳过。
  • 保留 anthropic 路由名、既有请求身份与正文过滤行为;此次不迁移已有会话、不扩展模型计费能力。

已知边界

  • 额度接口未在真实订阅上验证过;失败只影响额度工具,不影响对话。
  • 请求体结构与 Claude Code 不完全一致(工具定义、thinking 配置来自 DSH 与 pi-ai),这是第三方接入的固有差异;本插件只保证客户端身份这一层可辨别特征与官方一致。
  • 订阅走的是计划额度,不是 API 计费;账号能否用、能用多少由 Anthropic 侧决定。

相关插件