anye-99/dsh-codearts-auth ↗★ 0

dsh-codearts-auth

执行华为云CodeArts登录并存储临时凭据 适合需要调用CodeArts后端模型或使用其积分功能的用户。

套件
dsh-codearts-auth
相容性
待驗證
Harness 依賴範圍
^0.1.2-rc.1 || ^0.1.7-rc.2 || ^0.2.0-rc.1
Cordis 依賴範圍
^4.0.2
版本
0.1.0
授權
MIT
最近更新
2026年10月13日

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:anye-99/dsh-codearts-auth

dsh-codearts-auth

deepseek-harness 插件:执行 CodeArts(华为云)登录流程,默认走新式 IAM OAuth (portal /authorize 授权 → 本地 /oauth/callback 回调 → STS token 端点换取含 refresh_token 的凭据),到期前静默续期,无需再次打开浏览器;旧 ticket 流程保留 为显式回退(flow: 'ticket')。插件还注册一个 codearts LLM provider 路由,使该 凭证可直接用于 CodeArts 后端模型调用。

此外插件内置另外十三个 provider 路由(完整清单见 LLM provider); 其中带积分能力的几个:

  • buddy(腾讯 CodeBuddy) — 见 buddy provider; 另支持「一键领取积分」(每日签到)与「锁定永久积分」。
  • workbuddy(腾讯 WorkBuddy 国际版) — 见 WorkBuddy provider; 支持「锁定永久积分」。
  • lobsterai(有道 LobsterAI / 龙虾) — 见 LobsterAI provider; 另支持「一键领取积分」(每日签到)。
  • qoder(阿里系 Qoder) — 见 Qoder provider; 支持积分余额与每日领取(每日 100 Credits,10:00(UTC+8)刷新); 走加密推理端点,模型池与客户端一致(含 Qwen3.8 系列)。
  • loomy(讯飞 Loomy 办公助手) — 见 Loomy provider; 唯一用短信验证码登录、唯一不能自动续期的 provider; 支持积分余额(两个池)、每日额度签到、新手任务一键领取 10000 积分, 以及「锁定永久积分」(CodeBuddy / WorkBuddy 也有同名按钮,但判据不同)。
  • minimax(MiniMax Code 中国版) — 见 MiniMax Code provider; 首个 Anthropic Messages 协议族的 provider; 支持积分余额与每日签到,推理已启用(Anthropic Messages, 4 个模型实测通过;⚠️ 图片输入未实现)。
  • teleagent(中国电信天翼 AI 星辰超级智能体) — 见 TeleAgent provider; 唯一一个登录产物需要用户手动粘贴回调地址的面板(它的回调 URL 是服务端常量、 且自定义协议插件注册不了,后端抓不到 token); OpenAI 兼容且无加密(推理直接复用 openai-compat.ts); 无续期端点(失效只能重新登录);支持积分余额与每日签到。
  • antigravity(Google Antigravity / 官方 agy CLI) — 见 Antigravity provider; 唯一一个不由本插件持有凭据的面板(凭据归官方 agy 二进制、 一个账号 = 一个隔离 HOME 目录);支持多账号池、family 配额双窗 (5 小时滚动 + 本周)与限流自动切号。

codearts 面板同样支持积分账户检测、积分余额与「一键领取积分」 (华为云「每日签到得积分」活动,走 SDK-HMAC-SHA256 签名)—— 见 CodeArts 积分。

十四个 provider 面板(含 antigravity)都提供「显示列表」按钮,可逐个开关模型以控制其 是否出现在对话框的模型选择里(黑名单制,默认全部显示)—— 见 模型列表开关。

安装

该包尚未发布到 npm registry。提供两种安装方式:git 仓库安装(推荐,自动拉取 并构建)和源码目录安装(本地开发联调)。

方式一:从 git 仓库安装(推荐)

add 以 git+https 方式安装,pnpm 会运行本包的 prepare 脚本自动构建 lib/, 无需手动 pnpm build。

⚠️ pnpm 10 起会拦截依赖的构建脚本,必须先在 profile 的 pnpm-workspace.yaml (路径形如 ~/.dsh/profiles//pnpm-workspace.yaml)里放行;而放行键的写法 在 pnpm 10 与 pnpm 11 之间互不兼容,写错就装不上(两种报错见本节末)。

1. 先跑一次安装(这一次必然失败,为的是让 pnpm 打印它期望的键):

dsh plugin --profile  add "https://gitee.com/iJetLi/deepseek-harness-codearts.git"

2. 按第 1 步的报错选一种写法,写进 profile 的 pnpm-workspace.yaml。

报 ERR_PNPM_INVALID_VERSION_UNION 的(pnpm 10.x)—— 只认纯包名键:

allowBuilds:
  dsh-codearts-auth: true

报 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 的(pnpm 11.x,如本机实测的 一版 DSH Desktop 内置 pnpm 11.7.0)—— git 托管包不认纯包名键,必须用第 1 步 pnpm 打印的完整键(含 #):

allowBuilds:
  dsh-codearts-auth@git+https://gitee.com/iJetLi/deepseek-harness-codearts.git#: true

两代通用(省事,但放宽了权限):

dangerouslyAllowAllBuilds: true

它会放行该 profile 里所有依赖的构建脚本(不止本插件)。

3. 重跑第 1 步的命令:这次会拉取、构建并安装成功。之后每次升级重新 add 即可。

⚠️ 不要写 dsh-codearts-auth@git+https://gitee.com/…(不带 #)—— 这个"看起来最自然"的键在两代 pnpm 上都不工作:

  • pnpm 10.x:解析该键即抛 ERR_PNPM_INVALID_VERSION_UNION("Use exact versions only."),安装当场失败 —— issue IKJCOC 报的就是它;
  • pnpm 11.x:不报错,但该键缺 commit,永远匹配不上真实依赖,表现为反复 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED。

⚠️ pnpm 11.x 的精确键里带 commit,因此升级(重新 add 拉到新 commit)后该键 失效,按新报错里的键替换即可;用 dangerouslyAllowAllBuilds 则不必改。

实测矩阵、判据与本仓库自身 pnpm-workspace.yaml 为什么不能出现同类键, 见 AGENTS.md 的「安装(git 插件)的 allowBuilds 键在 pnpm 10 / 11 语义互不兼容」。

方式二:从源码目录安装(本地开发)

先在本仓库中构建 lib/,再用 dsh plugin install 将本地检出安装为 pnpm link: 依赖(指向本目录):

pnpm build:all
dsh plugin --profile  install 

dsh plugin install 以 link: 方式安装,pnpm 不会为 link: 依赖运行 prepare 脚本,因此必须先手动执行 pnpm build:all 生成 lib/,否则 dsh 启动时 报 ERR_MODULE_NOT_FOUND: ... dsh-codearts-auth/lib/index.js。 注意必须用 build:all 而非 build:后者只编译宿主侧,不产出 lib/client/jet-hub.js。

每次修改 src/ 或 plugin-src/ 后都需要重新执行 pnpm build:all——dsh 启动时 不会自动重建。

通用说明

该包声明了 dsh.bundle 补丁(cordis.patch.yml),因此 profile 的 layer 栈会 自动拾取 codearts-auth 行。插件注入由 dsh base 提供的 credentials、 commands 和 llm 服务。

用法

登录入口:Jet Hub 设置页的 CodeArts 面板(与其余五个 provider 一致)。

⚠️ 不注册任何斜杠命令。早期的 /codearts-login、/codearts-status、 /codearts-refresh 三个命令已移除 —— 登录、状态查看与续期统一在 Jet Hub 完成。 编程式调用仍可用:ctx.codeartsAuth.login() / startLogin() / refreshAccountCredential(refName) / refreshAll(pool)。

LLM provider

插件在 ctx.llm 上注册了一个 codearts provider 路由(OpenAI 兼容端点 https://snap-access.cn-north-4.myhuaweicloud.com/api/v2)。每个模型请求都使用 存储的 AK/SK/SecurityToken 按华为 SDK-HMAC-SHA256 方案签名,并附带 Chat-Id/Session-Id 请求头。

模型列表以远端两个端点的合并结果为准(opengw gateway/config 的 result.models ∪ snap-access /v1/model/builtin 的 builtinModels)。 2026-10-09 实测远端共下发 7 条:deepseek-v4-flash / deepseek-v4-pro / deepseek-v4.1-flash(每日 1000 万免费 Tokens 福利)、GLM-5.2、 glm-5.2-sft-harmony(展示名 GLM-5.2-ArkTS-SPARK)、 openpangu-2.0-flash (92B) / openpangu-2.0-pro (505B)。 登录后在 dsh Models 页面选择该 provider 即可。

注 1:CodeArts Agent IDE 模型列表显示的 flash ID 为 deepseek-v4-flash-0731 (带日期后缀),但后端实际注册的可用 ID 是 deepseek-v4-flash(无后缀)。 用 deepseek-v4-flash-0731 调用会返回 InferHub.002002009.404 The model is not registered,因此本插件只注册无后缀的 deepseek-v4-flash。

注 2:maas_type: benefit 的判据已改为自适应(2026-10-09 第二次推翻)。 早期结论是「benefit 模型必须带该头,否则 404 not registered」,该结论 已失效:实测当前服务端对所有模型带上该头都回 InferHub.4004.200 benefit not found,而默认不带时 GLM-5.2 / deepseek-v4-flash / kimi-k2.6-vl 等全部可用。故适配器现在默认不带, 只有当服务端以 not registered 明确拒绝时才带上重试一次,并把结果学习进缓存 —— 不再依赖任何静态或远端清单。详见 src/models.ts 的 isCodeArtsBenefitModel(含三套判据的历史与失效方式)。

注 3:glm-5.3-flash 已退役(服务端不再注册,带/不带该头都失败), 故 src/models.ts 的 RETIRED_MODEL_IDS 会把它从远端目录里过滤掉。 GLM-5.1 与 GLM-5 在 src/llm-adapter.ts 的 DEFAULT_MODELS 里保留, 但远端两个端点都不下发它们 —— 它们只在远端拉取失败时出现在列表里; 按「某次实测没看到 ≠ 不存在」的红线未删除(详见该常量注释)。

注 4:视觉(VL)多模态模型不参与对话模型列表(它们上下文小、不支持工具 调用,只通过 analyzeImage 等工具间接调用)。⚠️ 该过滤必须大小写不敏感 —— 真实缺陷(2026-10-09 实测):原判据是 id.includes('-VL-') || id.endsWith('-VL'),而远端下发的 id 大小写并不统一: Qwen3-VL-235B 是大写(会被过滤),而 kimi-k2.6-vl 是全小写, 原判据不命中,于是它混进了模型选择器。 远端自己对两者的标注完全同类(都是 display_enabled: false + model_category: "图片理解"),可见「不该展示」是服务端的明确意图。 复核脚本 scripts/probe-codearts-vl.mjs。

凭据来自默认的新式 IAM OAuth 流程(含 refresh_token)。请求发起时会解析最新 凭据,若已过期则先静默续期,再用新 AK/SK/SecurityToken 签名,无需重新打开浏览器。

除 codearts 外,插件另注册十四个独立的 provider 路由:buddy(见 buddy provider)、workbuddy(见 WorkBuddy provider)、lobsterai(见 LobsterAI provider)、qoder(见 Qoder provider)、qodercn(见 Qoder CN provider)、trae(见 TRAE provider)、cline(见 Cline provider)、loomy(见 Loomy provider)、raccoon(见 Raccoon Work provider)、minimax(见 MiniMax Code provider)、zcode(见 ZCode provider)、stepcode(见 Step Code provider)、antigravity(见 Antigravity provider)。十四者互不覆盖,可同时使用。

历史:2026-10-01 曾接入 vibex(RunningHub 的 vibe-coding 平台)作为一个 非 LLM 面板(OAuth 账号 + Remote MCP 工具挂载,无补全端点);2026-10-02 应要求整块移除。相关设计要点保留在 AGENTS.md 的「VibeX 维护要点」里, 仅作将来重新评估时的参考,代码与面板均已不存在。 2026-10-04 曾接入 doubao(字节豆包桌面客户端的 CDP 调试通道桥接,非凭据型 LLM 路由);2026-10-05 应要求整块移除 —— 实现文件、Jet Hub 面板、RPC 分支 与本文档章节均已不存在。

凭证

  • ⚠️ 只支持账号池:每个账号的凭据存储在 CODEARTS_ACCOUNT_XXX(Ref 为 POSIX 标识符格式的凭证 ref),由 Jet Hub 设置页管理。
  • 单凭据模式已移除:早期那条「登录写固定 ref CODEARTS_ACCESS_TOKEN、 适配器在账号池取不到时回退读它」的路径已删除。固定的 CODEARTS_ACCESS_TOKEN 不再被写入或读取 —— 若你此前只用它登录过, 模型列表会变空,请在 Jet Hub 的 CodeArts 面板重新登录一次。
  • 值:JSON 字符串 { access_key_id, secret_access_key, security_token, expires_at, domain_id?, user_id?, user_name? } — AK/SK 对用于给每个 CodeArts 后端 API 请求签名。

续期(refresh)

  • 默认登录流程为新式 IAM OAuth(PKCE + DPoP):portal /authorize 授权 → 本地 /oauth/callback 回调收取 code → sts.cn-north-4.myhuaweicloud.com/v1/oauth2/tokens 换取含 refresh_token 的凭据。
  • 凭据在过期前 1 小时静默续期(getFirstRefreshTime 语义:距过期 ≤1h 立即刷, 否则 now+1h 叠加随机秒偏移),全程无浏览器、无人工操作。
  • 刷新失败后 10 分钟重试(异常网络 1 分钟);refresh_token 失效后停止续期并提示 重新登录。
  • 旧 ticket 流程保留为显式回退:编程式调用 ctx.codeartsAuth.login({ flow: 'ticket' })。ticket 凭据没有 refresh_token, 其续期仍意味着重新运行浏览器登录流程。
  • 续期按账号:refreshAccountCredential(refName)(账号卡片的「刷新」按钮) 与 refreshAll(pool)(定时调度器)。早期的 refresh() / logout() / status() / scheduleRefresh() / scheduleModelRefresh() 只服务于单凭据路径, 已随该模式一并移除。
  • 运行时依赖新增 jose(用于 DPoP JWS 签发,与 CodeArts Agent 插件实现一致)。

⚠️ 停用的账号同样会被续期

refreshAll() 与续期调度器只按 refreshable 过滤,不看 enabled。

停用只应影响「账号池的自动选号」,与「凭据是否需要保持新鲜」无关 —— 停用账号仍然出现在 Jet Hub 里,也仍然参与积分领取。

真实缺陷:两个曾停用的 CodeBuddy 账号显示「凭证过期」,点「一键领取 积分」报 Unexpected token ' 根因是两处都按 enabled` 过滤:

  • refreshAll() 里的 if (!entry.enabled || !entry.refreshable) continue → 停用期间 refresh_token 一路放到失效;
  • src/index.ts 的 accounts.some(a => a.refreshable && a.enabled) → 所有账号都停用时,续期定时器根本不启动。

用户重新启用后拿到的是死凭据,只能重新登录。四个 provider 的 refreshAll 与调度器都必须保持只看 refreshable。

凭据失效时不再抛 `Unexpected token ' 流式工具调用 id 稳定性:CodeBuddy 仅首个工具调用分片携带真实 id

(chatcmpl-tool-xxx),后续参数分片只有 index。适配器按 index 缓存并沿用 真实 id(缺失时回退 call_{index}),保证同一工具的所有分片 id 一致——否则 跨轮次(每轮都从 call_0 重新编号)会把 tool/result 配对到错误的历史条目。

安全策略拦截(11140):会自动换号,被拦账号冷却 30 分钟

腾讯侧业务码 11140(request illegal)的文案写的是「内容未通过安全审核,请调整后重试」, 但实测是按账号生效:同一份请求体(哪怕只有「你好」两个字)发往池里 7 个账号, 结果是「2 个正常、4 个被拦、1 个限流」,换一个会话照样被拦。所以它的出路是换号, 不是让用户改内容。

适配器对它的处理(CodeBuddy 与 WorkBuddy 共用同一实现):

  • 三条通道都会继续换号:首发 401/403 的认证换号、首发限流的换号、以及 HTTP 200 + SSE 流内的错误帧 —— 坏账号排在池里任何位置都不会中断这一轮。

  • 被拦的账号会在「该模型」上冷却 30 分钟,避免每一轮都重撞它 (池的候选顺序由你在 Jet Hub 拖拽决定,排在前面的坏账号否则每次都会先吃一发)。 ⚠️ 账号卡片因此会亮「限额重置 · · 30 分钟后」徽章 —— 这个徽章现在同时承载两种原因(服务端限流 / 策略拦截冷却), 因为账号池里只有「模型 → 解禁时刻」这一个「暂时不可用」的载体,没有单独的字段。 若确认该账号是好的,点「重测」(它会真发一条最小消息,正常返回就清掉标记) 或「重置」即可立即解禁。

  • 报错不会显示「API 密钥无效」:这类错误归 PERMISSION_DENIED 而不是 AUTH。 DSH 客户端对 AUTH 一律替换成「API 密钥无效」并丢掉原文,会把用户引向重新登录 —— 而实测这些账号的 token 全部有效。现在看到的是原文,例如:

    workbuddy: 全部账号均被服务端安全策略拦截(HTTP 403,已逐个换号重试)。
    该拦截按账号生效(同一请求在其他账号可正常返回),请在 Jet Hub 停用或更换被拦账号:
    内容未通过安全审核,请调整后重试。 request illegal
    

单次输出上限(max_tokens)

远端下发 maxOutputTokens,适配器必须消费它并写进请求体。 这是「回答被 截断、UI 报『已达到输出 token 上限』」的唯一根因修复。

  • 请求体 max_tokens 取值优先级: options.maxTokens(DSH 注入)→ 远端 data.models[].maxOutputTokens → 产品兜底表。 三者皆无则不发该字段,交回网关默认值(不编造数值)。
  • resolveModel() 同时把该值声明为 defaultMaxTokens。DSH 只在调用方未显式 给值时用声明的默认值兜底;适配器不声明就等于把上限永久交给网关默认。
  • ⚠️ 远端非法值必须过滤:DSH 对 defaultMaxTokens 有硬校验(非安全整数 或 ≤0 直接抛 INVALID_MODEL_MAX_TOKENS,整轮对话起不来),故远端是外部 输入,0 / 负数 / NaN 一律视为未声明(见 positiveMaxTokens)。

实测(2026-09-19,node scripts/dump-max-output.mjs):

模型中国版 scoped 端点中国版 /v3/config国际版 /v3/config
deepseek-v4.1-flash128000131072128000
deepseek-v4-pro128000131072—
deepseek-v4-flash5000050000—
hy4-preview640006400064000

⚠️ 网关默认值恰好是 32000(远端 auto / glm-4.6 / kimi-k2.6 等模型 声明的就是 32000),这正是未下发 max_tokens 时 deepseek-v4.1-flash 在 32000 处被截断的原因 —— 不是「网关固定上限」,而是上游声明的额度被适配器丢了。

网关确实接受 max_tokens 且精确生效(node scripts/verify-max-tokens.mjs, 国际版 deepseek-v4.1-flash,当时处于官方限免期 credit: 0):

请求结果
不带 max_tokensHTTP 200,finish_reason=stop
max_tokens: 128000HTTP 200,接受
max_tokens: 64HTTP 200,finish_reason=length、completion_tokens=64

第三行是关键证据:输出被精确截断在 64,证明该字段被服务端真实消费,而不是 被静默忽略。

单次请求 ≠ 单轮:每个 step 都是独立请求、各有各的预算。因此 「拆多步写文件」仍是超出单次额度时最有效的手段;max_tokens 只是把单次 额度提升到上游声明的真实值。

思考档位与输出预算共享同一额度:reasoning_tokens 计入 completion_tokens(实测 thinking 内容与正文同池),故思考开到 max 时正文更早撞上上限。

模型计费倍率与同名模型

模型切换列表里每个模型后面会显示它的计费倍率:

Deepseek-V4.1-Flash · x0.03
GLM-5.3 · x0.79→x0.50          ← 有促销活动时显示 原价→促销价

倍率拼在模型名(name)后面,而不是说明(description)里 —— composer 的模型切换菜单只渲染 name,description 仅用于 /model 弹窗。 name 纯属展示,DSH 的选择与持久化只用 id,所以附加价格不会影响会话。

各 provider 的倍率字段形态互不相同,实现里是分开解析的:

provider远端字段真实形态
buddy / workbuddydata.models[].credits字符串 "x0.29"(可为空串)
buddy / workbuddymodelPromotions[].discount.discountedCredits字符串 "0.50x"(x 在后);factor: 0 = 免费。⚠️ 只由 /v3/config 下发,企业模型端点没有;且须按 schedule 判时段
lobsteraidata[].costMultiplier裸数字 0.05
qoder目录 chat[].price_factor裸数字,0 = 免费
trae`display_con