火山方舟 Coding Plan (Volcengine Ark) 的 DeepSeek Harness 提供商插件:多账户、内置 arkcli、官方套餐额度、12 模型实测容量
32.3/100
综合分
3
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/24 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add Akiyuki1130/dsh-ark-coding-provider该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · model
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 8 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 8 处,展示前 8 条)
- 发起网络请求index.js:859
response = await fetch(`${connection.apiBase}/chat/completions`, { - 发起网络请求index.js:1261
response = await fetch(`${baseURL}/chat/completions`, { - 写入剪贴板client.js:666
return Promise.resolve(navigator.clipboard.writeText(text)).then(() => true, () => false) - 读取本地文件arkcli.js:1178
const existing = createHash('sha256').update(readFileSync(destination)).digest('hex') - 写入本地文件arkcli.js:1199
writeFileSync(temporary, bytes) - 写入本地文件arkcli.js:1201
if (this.platform !== 'win32') chmodSync(temporary, 0o755) - 写入本地文件arkcli.js:1205
renameSync(temporary, destination) - 删除本地文件arkcli.js:1214
try { rmSync(temporary, { force: true }) } catch { }
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-credentials@deepseek-ai/dsh-home-paths@deepseek-ai/dsh-launch-environment@deepseek-ai/dsh-llm@deepseek-ai/dsh-typert-protocol@deepseek-ai/schemastery给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
README
由 DeepSeek 最新模型翻译生成dsh-ark-coding-provider
火山方舟 Coding Plan 的 DeepSeek Harness LLM 提供商插件(非官方社区集成)。
仿照 @mars-sea/dsh-commandcode-provider 的形态做的:注册一个提供商路由、在 Models 页挂一张属于自己的卡片、在设置里开一个同名分区,把模型目录、容量事实和密钥管理都放在那里。
这是社区集成,不是火山引擎官方产品。你需要自己的火山方舟账号与 Coding Plan 订阅,并遵守火山引擎的服务条款。
截图
| 设置页 | 侧边栏额度卡片 + 详情面板 |
| 设置页浅色 | 额度面板浅色 |
| 设置页深色 | 额度面板深色 |
它给你什么
- 一个提供商路由 ark-coding —— 直接对着 https://ark.cn-beijing.volces.com/api/coding/v3 的 OpenAI 兼容端点说话,/chat/completions + SSE 流式。
- 12 个模型,容量事实是实测出来的 —— 上下文窗口、单次输出上限、是否吃图、接受哪些 reasoning_effort 档位,全部逐项探过(见下表)。
- 设置页「设置 → 火山方舟」 —— 账户管理、登录向导、逐模型可见性、连接参数、上下文覆盖。
展开完整文档(README 剩余 1 节)
dsh-ark-coding-provider
火山方舟 Coding Plan 的 DeepSeek Harness LLM 提供商插件(非官方社区集成)。
仿照 @mars-sea/dsh-commandcode-provider 的形态做的:注册一个提供商路由、在 Models 页挂一张属于自己的卡片、在设置里开一个同名分区,把模型目录、容量事实和密钥管理都放在那里。
这是社区集成,不是火山引擎官方产品。你需要自己的火山方舟账号与 Coding Plan 订阅,并遵守火山引擎的服务条款。
截图
| 设置页 | 侧边栏额度卡片 + 详情面板 |
| 设置页浅色 | 额度面板浅色 |
| 设置页深色 | 额度面板深色 |
它给你什么
- 一个提供商路由 ark-coding —— 直接对着 https://ark.cn-beijing.volces.com/api/coding/v3 的 OpenAI 兼容端点说话,/chat/completions + SSE 流式。
- 12 个模型,容量事实是实测出来的 —— 上下文窗口、单次输出上限、是否吃图、接受哪些 reasoning_effort 档位,全部逐项探过(见下表)。
- 设置页「设置 → 火山方舟」 —— 账户管理、登录向导、逐模型可见性、连接参数、上下文覆盖。
- Models 页的提供商卡片 —— 只读的账户现状:正在用哪个账户、额度来源、没配好该去哪。插件贡献的这张卡片上没有密钥输入框、也没有「测试连接」按钮;密钥在设置页的账户行里填。官方 Models 页自带的密钥编辑器(宿主渲染,插件管不着)写的是顶层 ARK_CODING_API_KEY,一个账户都没有时它会被当成兜底账户读进来(见「配置密钥」)。
- 模型选择器注解 —— 上下文长度与图像能力直接来自适配器上报的事实;文案是语言无关的短形式(厂商 · 1024K ctx),因为这段文字出现在 harness 共享的模型选择器里,那里没有插件侧的语言席位。
- 宿主侧的模型发现 —— 插件注册了一个 discovery(index.js 的 createDiscovery()):被调用时用一次性密钥真实打一次 Coding Plan 端点,成功则回传这 12 个模型的容量事实;密钥只在这一次请求里存在,宿主不存储它,浏览器永远不接触方舟。
它由宿主 Models 卡片上的模型拉取动作(Fetch available models)触发,不是插件设置页里的按钮。
- 密钥不进配置文件 —— 走本机凭据服务写入,页面只看到"配没配"。
- 内置 arkcli(不需要 npm / node) —— 不用你自己装:设置页里一个按钮把官方 CLI @volcengine/ark-cli 的平台二进制直接下到插件自己的目录(官方 CDN 优先、GitHub Release 兜底,落盘前校验 sha256),然后用它拉官方模型目录和真实套餐额度。装的是钉住的 @volcengine/ark-cli@1.0.39(实测通过的版本),不是 latest:额度、目录、聊天取 key 三条链全都建立在它的输出形状上,装 latest 等于每次点安装都默默绑定当天的最新版。DSH 桌面端(Electron)的 PATH 上通常没有 npm,所以安装不再走 npm install:直接下载即可;PATH 上恰好有 npm(或显式指定了别的版本)时才退回 npm。
- 两种账户,可混用 —— 每个账户要么是「CLI 登录(SSO)」,要么是「API Key」;添加时可以给它起个名字(不填也行)。某个账户打满就自动轮到下一个。
- 真实额度,不掺本机数字 —— 已登录的 CLI 账户(或一对 AK/SK)能查到 5 小时 / 本周 / 本月的已用百分比与刷新时间。左下角卡片只放日常要盯的 5 小时与本周两档、再没有第三行(账户是谁不影响你要盯什么,账户名只在点开的详情面板和设置页里出现),点一下展开详情:每个账户的登录情况 + 三档额度。
卡片行距也按侧边栏的尺寸调紧了:展开态行间 6px、窗口内 4px;侧边栏折叠成窄轨道(约 56px,卡片只剩两行百分比)时压到行间 5px、窗口内 2px。
查不到真实额度时卡片不显示(一张只写着"未配置"的常驻卡片没有信息量),绑上 CLI 账户或 AK/SK 后它自己就出现 —— 不用谁再去手动打开一次。
没有本机 token 统计:那个数字不是套餐额度,曾经摆在旁边只会让人误读。
百分比在 10% 以下保留一位小数(0.4%)—— 这个账号日常就落在 0.1%~1%,一律取整会让卡片长期写着 0%,看着像没用量。重置时间照抄控制面给的那一刻,不按日历推算:实测这个账号的 monthly 重置在 15 日 23:59:59,不是 1 号。
模型目录(实测 2026-10-15)
单位是 token。模态一栏里 图像 表示该模型接受图片输入(视频/音频输入在 DSH 的模态词汇里没有对应值,因此不计入)。
| 模型 id | 上下文 | 输出上限 | 模态 | 思考档位 | 厂商 |
| doubao-seed-evolving | 1M | 256K | 文本 · 图像 | off/minimal/low/medium/high/max | Doubao |
| doubao-seed-2.1-pro | 1M | 256K | 文本 · 图像 | off/minimal/low/medium/high/max | Doubao |
| doubao-seed-2.1-lite | 1M | 256K | 文本 · 图像 | off/minimal/low/medium/high/max | Doubao |
| doubao-seed-2.0-mini | 256K | 128K | 文本 · 图像 | off/minimal/low/medium/high/max | Doubao |
| minimax-m3 | 1M | 128K | 文本 · 图像 | off/minimal/low/medium/high/max | MiniMax |
| glm-5.3 | 1M | 128K | 文本 | low/medium/high/max | Zhipu |
| glm-5.3-flash | 1M | 128K | 文本 · 图像 | off/minimal/low/medium/high/max | Zhipu |
| deepseek-v4.1-flash | 1M | 384K | 文本 · 图像 | off/minimal/low/medium/high/max | DeepSeek |
| deepseek-v4-flash | 1M | 384K | 文本 | off/minimal/low/medium/high/max | DeepSeek |
| deepseek-v4-pro | 1M | 384K | 文本 | off/minimal/low/medium/high/max | DeepSeek |
| kimi-k2.8-preview | 1M | 128K | 文本 · 图像 | off/minimal/low/medium/high/max | Moonshot |
| kimi-k3 | 1M | 128K | 文本 · 图像 | off/minimal/low/medium/high/max | Moonshot |
三个反直觉的地方(都是实测结论,不是猜的)
1. glm-5.3-flash 其实能吃图。Ark 自己的 /models 把它记成 in=[text],但给它一张四象限测试图,它准确答出 blue, yellow, green, magenta。所以这里按支持图像处理。
2. glm-5.3 不能吃图,也不能关思考。图片请求被明确拒绝(Model only support text input),thinking.type=disabled 与 reasoning_effort=none/minimal 同样被拒。它的可用档位是 low/medium/high/max(off/minimal 被拿掉,其余照收)。
3. minimax-m3 回传的 tool_calls[].type 是 functionfunction(服务端序列化缺陷),原样回灌第二轮工具调用会 400。适配器重建请求时强制写 type: "function",多轮工具调用因此可用。
deepseek-v4-flash 与 deepseek-v4-pro 按纯文本处理:前者带图三次都在 180 秒内无响应,后者收图后回答"看不到图片"。DSH 的模态声明宁可少给也不能错给 —— 少给只是在附图片前拒绝并点名模型,错给会让一个不可完成的请求在消息落盘之后才失败。
安装
打包
cd ark-coding-provider && npm pack # 产出 dsh-ark-coding-provider-<version>.tgz
装进 profile(把路径换成上面那个 tarball)
dsh plugin --profile web add /path/to/dsh-ark-coding-provider-<version>.tgz
本地路径要用 tarball,不要用目录。 用目录安装时 pnpm 会建符号链接,而 DSH 的运行时拦截只为「profile 树内」的包解析 harness 包(@deepseek-ai/dsh-llm 等),链接到 workspace 的包会以真实路径在 profile 树之外被导入,于是所有 harness 包 ERR_MODULE_NOT_FOUND,表现为 failed to import。
装完重启一次 dsh(替换已加载的包需要新的模块世代)。
Windows / macOS 桌面端
桌面版(Electron)跑的是 desktop profile,不是 web,装法有两种:
- 应用里打开 插件 → 添加插件,填入 tarball 的绝对路径(或 release 里 tgz 的直链)——插件管理器认本地路径与 tarball 链接;
- 或者用桌面自带的 dsh 命令(Windows 上是 dsh.cmd,在应用里 Manage dsh Command 注册):先完全退出桌面应用,再
dsh plugin --profile desktop add C:\path\to\dsh-ark-coding-provider-<version>.tgz
装完重新打开桌面应用。
桌面端不会给宿主进程设 DSH_HOME,这没关系:插件按 DSH 自己的规则解析状态目录($DSH_HOME → ~/.dsh),和宿主的 sessions / profile / 凭据落在同一个根下。
配置密钥
最省事的路径是设置页:设置 → 火山方舟 → 「账户」卡片,新建一个「API Key」类型的账户,在那一行里的密钥输入框粘贴后保存(没有名为「API 密钥」的独立卡片)。
密钥写进本机凭据服务,页面只显示"已配置"与来源,永不回传值。
一个账户都没有时,顶层字段会作为兜底账户被读。 官方 Models 页会给每个可配置的 provider 渲染一把密钥编辑器,它写的引用名是从 provider 名推出来的 ARK_CODING_API_KEY(ark-coding → ARK_CODING_API_KEY,插件无法控制)。只要 accounts 是空的,插件就把顶层 apiKeyEnv 当成一个 id 为 default、显示名「默认账户」的 API Key 账户,顶层 apiKey 则是它的字面量密钥。这条兜底专门用来接住上面那把编辑器:不接的话,用户粘了 key、卡片显示"已配置",聊天却报"一个可用账户都没有"。
- accounts 非空时这条兜底整个不存在,顶层字段不再被任何人读。
- 它只会是 API Key 账户(走不了 CLI 登录),也不写进 accounts —— 所以在设置页删掉「默认账户」那一行没有持久效果,只要 apiKeyEnv 还在,下一个请求它就会回来。
- 要真正关掉它:把 apiKeyEnv 显式设成空串(默认值是 ARK_CODING_API_KEY,不设就是开着)。写成 MY-KEY 这种不合法的凭据引用名同样不会产生兜底账户。
- 在设置页保存账户列表时,空的 accounts 会被整份写回(老配置的迁移路径)——兜底账户那时就变成 accounts 里的一个普通账户,不再随顶层字段出现或消失。
也可以走环境变量(默认引用名 ARK_CODING_API_KEY):
export ARK_CODING_API_KEY="ark-..."
密钥优先级(每个账户各自解析):账户里的字面量 apiKey > 凭据服务 > 启动环境 / .env。凭据引用名不合法(文法 ^[A-Za-z_][A-Za-z0-9_]$)时跳过凭据服务,直接落到环境变量;顶层 apiKey 只在上面的兜底账户里生效,不会补给 accounts 里的其它账户。
账户:两种登录方式
账户列表是账户的主要真源(配置里的 accounts)。每个账户有三样东西:类型、名字(可选)、以及它自己的凭据来源。
「默认账户」不是配置里的账户,是 accounts 为空时的兜底。 它由顶层 apiKeyEnv(凭据引用)+ apiKey(字面量,可选)派生:id default、显示名「默认账户」、类型 API Key。它没法走 CLI 登录,也不写进 accounts,所以删掉它之后只要 apiKeyEnv 还在就会自己冒回来,真正关掉它得把 apiKeyEnv 设成空串。accounts 非空时它完全不出现。保留这条路径是因为它是官方 Models 页密钥编辑器唯一能落地的地方(详见上面「配置密钥」),代价就是这个"删不掉的默认账户"。
CLI 登录(推荐)
一次 SSO 登录同时解决三件事:聊天用的 API Key、真实套餐额度、官方模型目录。登录一次之后插件会自己从 arkcli 的 profile 里取 key,不需要你去控制台手抄。
点「添加账户」→ 选「CLI 登录」→(可选)填个名字 → 「开始登录」,然后:
1. 页面给出一个授权 URL。在任意设备的浏览器里打开它,完成火山引擎登录。
2. 登录完成后浏览器会跳到一个 http://127.0.0.1:<端口>/… 地址,并且打不开 —— 这是正常的,那个端口开在跑 DSH 的服务器上,不在你的电脑上。
3. 把浏览器地址栏里那条完整的地址复制下来,粘回设置页的输入框,提交。
插件会在服务器上把这条请求转发给 arkcli 的回环监听,登录随即完成。
每一步都在设置页里有提示,不需要你自己敲命令。如果你有办法把那个端口打通(比如 SSH 隧道),登录也会自己完成 —— 向导在轮询,看到成功就会自动跳过去。
凭据是有时效的,到期要重新登录。 设置页会直接告诉你到哪天:插件读 arkcli 落盘的 profile expires_at(<账户 HOME>/.arkcli/config.yaml)换算成日期显示在账户行上,快到期的会提前提示、过期了会标成需要重新登录。
为什么不写死"N 天":这是服务端策略给的值,会随租户变。实测本机一个 2026-10-16 登录的 Coding Plan 账户给的是 2026-12-15 23:59:59(UTC+8),而不是常被提到的 14 天。与其把一个可能不对的天数印在界面上,不如把算出来的具体日期给他。auth status 里那个 sts_expires_at_ms 只有 15 分钟(arkcli 自己会刷新),不能拿来当"何时重登"用。
多账户怎么隔离的:arkcli 的状态目录写死在 $HOME/.arkcli,没有环境变量能改(它的官方文档明确说了这点)。所以插件给每个 CLI 账户一个独立的 HOME(<DSH_HOME>/ark-coding/accounts/<账户 id>),用 HOME= 起子进程。这样同一台机器上可以同时持有多个火山引擎账号,互不覆盖。
Windows 上 HOME 不起作用:Go 的 os.UserHomeDir() 只认 %USERPROFILE%。插件在 Windows 上会把 USERPROFILE 和 HOME 都指到该账户的目录;只设 HOME 会让所有账户都读写宿主的 %USERPROFILE%\.arkcli,表现成"登录一个、全部串台"。
账户也可以显式指定 home,但它必须位于 <DSH_HOME>/ark-coding 之下,越界时插件在用到它的地方直接抛错、那个账户不可用。原因是那条路径会成为 root 子进程的 HOME,arkcli 会在它下面写 .arkcli/ 凭据:accounts 是浏览器可写的易变字段,若允许任意路径,一次配置写入就能让 root 往任意目录写文件。日常根本用不到自定义 HOME —— 多账户隔离靠的就是"每个账户一个派生目录"。
API Key
从控制台复制一把方舟 API Key 粘进来。它能聊天,但查不到套餐额度、也拉不到官方模型目录 —— 这不是插件的限制,是火山接口本身的划分:额度与目录在控制面,只认 SSO 身份(或 AK/SK),API Key 只是数据面凭据。所以要看到用量,至少要有一个 CLI 登录的账户。
额度怎么看
- 有已登录的 CLI 账户 → 显示真实百分比(5 小时 / 本周 / 本月)+ 刷新时间。Coding Plan 的上游只给百分比、不给绝对值,所以这里不会编一个"还剩多少 token"出来。
- 一个都没有 → 卡片(左下角)按 sidebarQuota 的判据不显示,设置页里照实说"查不到",不编数字、也不退回任何本机记账(那套机制已删除,理由见上)。
- 另外还支持一对 AK/SK 作为备选来源(设置页「用量与显示」卡片里的 accessKeyId + Secret 引用),走 Volcengine V4 签名直接打控制面。仅在没有任何 CLI 账户可用时才会去用它。
官方模型目录(arkcli)
设置页的模型卡片会把 arkcli 拉到的官方目录并排显示:上下文、最大输入/输出、输入模态、思考支持、版本、生命周期状态。
它是对照,不是覆盖。 本插件内置那 12 行的容量与模态是直接打 Coding Plan 端点实测出来的,而 arkcli 给的是方舟通用目录的元数据,两者本来就可能不一致 ——已知的真实分歧就写在上面那张表的下面(glm-5.3-flash)。所以插件把官方值挂在旁边、把分歧点标出来,而不是偷偷用较不可靠的来源推翻实测值。
官方目录里如果出现了这 12 个之外的模型,卡片会提示「官方目录里还有 N 个模型不在本套餐列表中」,这是“官方又发新模型了”的信号。
配置项
| 字段 | 默认 | 说明 |
| apiKeyEnv | ARK_CODING_API_KEY | 只在 accounts 为空时被读取:作为兜底账户(id default)的凭据引用,也是官方 Models 页密钥编辑器写的那个引用名。显式设成空串即关闭这条兜底;引用名不合法(如 MY-KEY)同样不产生兜底账户 |
| apiKey | — | 只在 accounts 为空时被读取:作为兜底账户的字面量密钥(带 secret 角色,永不进设置描述)。账户自己的字面量密钥写在账户对象的 apiKey 上,不会补给其它账户 |
| apiBase | Coding Plan v3 端点 | 换端点/代理时改这里 |
| defaultMaxTokens | 65536 | 未指定输出上限时的请求值,会被逐模型上限夹住 |
| requestTimeoutMs | 300000 | 首字节超时 |
| streamIdleTimeoutMs | 300000 | 流空闲超时 |
| visibleModels | [] | 允许出现在选择器里的模型;空 = 全部 |
| modelVisibility | {} | 逐模型开关,优先于 visibleModels |
| contextWindowOverrides | {} | 逐模型覆盖上下文长度,上游改容量时不用等插件更新 |
| accounts | [] | 账户列表:{id, label, type, apiKeyEnv, apiKey, home}。type 是 apikey / cli;home 只有 CLI 账户用,且必须位于 <DSH_HOME>/ark-coding 之下,越界时插件在用到它的地方直接抛错、那个账户不可用。重复的 id 只保留第一个(同名账户会共用同一个 HOME 与同一组派生缓存)。空 = 回到顶层兜底账户(见上两行) |
| activeAccount | '' | 偏好的账户 id,优先用它 |
| accessKeyId | '' | AK/SK 备选来源的 AccessKey ID(不是秘密,明文配);留空 = 不用这条路 |
| secretAccessKeyEnv | ARK_ACCESS_KEY_SECRET | AK/SK 的 Secret 走这个凭据引用(role: credential-ref),值写凭据服务、不进配置文件 |
| region | cn-beijing | AK/SK 打控制面时用的区域;默认值即 usage.js 的 VOLC_DEFAULT_REGION |
| arkcliPath | '' | 显式指定 arkcli 可执行文件,优先级高于「插件自装目录 → PATH」。准确性说明:该字段此前读进配置后没有消费者(显式指定不生效),从宿主接线的那一版(v0.5.0)起才真正传给 arkcli 探测 |
| catalogRefreshSeconds | 3600 | 官方模型目录的刷新间隔(目录是控制面数据,不必每次轮询都拉) |
| usageRefreshSeconds | 60 | 用量轮询间隔 |
| sidebarQuota | auto | 左下角用量卡片:auto(有能查额度的绑定就显示)/ on / off。显式档位永远压过自动判据 |
accounts 为空时,能聊的账户就只剩顶层兜底那一个(apiKeyEnv 非空才有;见上)。
它不写进 accounts,所以设置页删掉它并不会让它消失 —— 只要 apiKeyEnv 还在,下一个请求它就会回来;要关掉就把 apiKeyEnv 设成空串。那时设置页才会显示 "没有账户"的空状态并引导你新建。
除顶层 apiKey(secret 角色,只随组合配置文件落地)外都是易变字段:设置页保存后加载器原地提交,下一个请求就生效,不需要重启。
工作区里的文件
| 文件 | 作用 |
| index.js | 宿主半边:Config、OpenAI 兼容适配器、模型目录投影、模型发现、Remote 端点、arkcli 编排 |
| models.js | 12 个模型的实测目录 + 官方目录对照(annotateModels) |
| accounts.js | 账户归一化(apikey / cli 两种类型、accounts 为空时的兜底账户、重复 id 去重)、账户 id 清洗的唯一实现(arkcli.js re-export 这一份)、轮换标记与排序、侧边栏卡片的三态判据 |
| usage.js | 套餐额度的备选来源:AK/SK 的 Volcengine V4 签名与 GetCodingPlanUsage 解析 |
| arkcli.js | arkcli 桥:探测 / 一键安装(钉住 1.0.39)/ 取额度 / 取目录 / 无浏览器登录编排 |
| client.js | 浏览器半边:设置页 + Models 页卡片 + 侧边栏用量,只用主题 token,不引入任何 Client 包 |
| cordis.patch.yml | bundle 层:插入 llm-ark-coding 条目 |
| icon.svg / locale/.json | 插件卡片显示元数据 |
请求是怎么走的
DSH 会话历史 ──► buildMessages() ──► OpenAI 兼容 messages │ · system/developer 并成 system │ · assistant 只回放可见文本 + tool_calls │ · tool_call 的 type 强制成 "function" ▼
POST {apiBase}/chat/completions (stream + stream_options.include_usage)
│
▼
SSE 帧 ──► StreamChunk(block-start / text-delta / reasoning-delta / tool-call-delta / block-end / usage / finish)
失败会归类成 DSH 的稳定失败码(AUTH / RATE_LIMIT / SERVER / TIMEOUT / TRANSPORT / CONTEXT_WINDOW_EXCEEDED / QUOTA / EMPTY_RESPONSE / PROVIDER_ERROR),由 dsh-llm-retry 按策略决定要不要重试。其中 PROVIDER_ERROR 承接的是流内报出的、不属于额度类的上游错误。
图片:上层标了 offloaded 的先降级成占位文本;剩下的读成请求版(长边 1568、编码 ≤1 MiB)并内联为 data URL。单次请求图片总量超过 20 MiB 时按 DSH 的契约抛 IMAGE_OFFLOAD_REQUIRED,让上层卸载最旧的几张再重试,而不是把一个注定超限的请求发出去。
验证状态
| 套件 | 结果 | 需要什么 |
| tests/usage.test.mjs | 42/42 | 不需要密钥;末尾一条要网络(真实控制面) |
| tests/arkcli.test.mjs | 174/174 | 不需要密钥、不需要网络(起本地假 arkcli / sleep 做进程实验) |
| tests/client.test.mjs | 167/167 | 不需要密钥、不需要网络(内存渲染 + 自建 CSS 解析器) |
| tests/host.test.mjs | 80/80 | 不需要密钥、不需要网络;需要 harness 包在场(跑 apply() 与适配器本体) |
| tests/stream.test.mjs | 9/9 | 喂假 SSE:不需要密钥、不需要网络;需要 harness 包在场 |
| tests/adapter.test.mjs | 需要真实 key 才跑(否则打印 SKIP 并 exit 0) | 一把真实 key + 网络(打真实端点) |
合计 472 项;上面五套在 npm test 里默认全跑(adapter 无密钥时明确跳过,不会假红)。
- 宿主侧(不需要密钥):V4 签名的规范化请求结构、额度窗口顺序与百分比夹取、账户归一化(accounts 为空时的兜底账户、重复 id 去重、CLI 账户、id 清洗),以及侧边栏卡片的三态判据(auto 有绑定才显示 / on / off / 无法识别的档位退回 auto)。
这一段本身不碰网络 —— 唯一例外是 usage.test.mjs 末尾那条真实打控制面的可达性断言:它用一对假 AK/SK 发真请求,期望被服务端按鉴权拒绝(以此证明签名头与请求结构被接受)。
因此这一套需要网络,而且网络故障时上游返回的错误文本可能不匹配 /AccessKey|Signature/,会把这条误报成失败(不是插件坏了)。
- 宿主适配器本体离线(host.test.mjs):12 个模型的目录事实与思考档位、请求体的 max_tokens 夹取与 resolveModel 的默认上限夹取、失败码映射、图片字节预算与 IMAGE_OFFLOAD_REQUIRED 的形状、options.system 是否进请求体、Retry-After 的边界(0、过去的 HTTP-date、畸形巨值)、脏 token 计数被整形为非负安全整数、SSE 的七条边界
(末帧无换行、没有终止标记的截断、[DONE] 之后的数据、字符串错误帧、裸断连被包成
TRANSPORT、单行上限、空闲超时里 cancel() 拒绝不留未处理 rejection)、账户兜底三态与重复 id 去重、显式 home 越界被拒、派生缓存键、目录/额度在途去重、arkcliPath 接线、
settings.configure({ auto: false }),以及宿主↔客户端八个端点描述符逐一对齐。
以前这些只存在于需要真实密钥的 adapter 套件里,默认 npm test 对 provider 本体等于零覆盖;现在不需要密钥也会跑。
- arkcli 桥离线:路径与 id 清洗(与 accounts.js 共用同一份实现:白名单 [A-Za-z0-9._-]、去前导点、截断 64 字符、空值回退 default)、JSON 信封解析(含"错误信封在 stderr 上")、子进程最小环境(不继承宿主的 ARK_API_KEY)、超时击杀、按进程组杀掉被 execFileSync 连带起来的孙子进程、凭据到期时间解析、 usage plan / models search / profile apikey get 归一化、登录编排全流程。
- 客户端离线:三个席位注册与渲染、账户增删改、登录向导各状态、 "没有账户"的空状态、类型选择说明、凭据到期提示(未来/7 天内/已过期/未知)、目录分歧标注、provider 卡片不再有 key 输入框,以及用量卡片的三条新行为:没绑定就完全不渲染、只画 5 小时与本周(月额度不出现)、点开后详情里有登录情况与三档额度;还有两条 CSS 回归:品牌底上不许出现写死的白色前景(深色主题的品牌色是近白色)、卡片行距保持紧凑(展开态 gap:6px / 窗口内 4px;窄轨道 5px / 2px)。
卡片里必须一行账户信息都没有:akc-sideMeta 在卡片上为 0 个节点。
假上下文忠实模拟 cordis 的注入守卫;假的 $mount 也照真的一样校验贡献(只收 strict codec)。
- 流式 chunk 契约:用 DSH 自己的 isJsonValue 校验每一块,覆盖文本 / 思考 / 工具调用三条路径 —— 这条就是「输出出来后立刻失败」那个 bug 的回归测试。
- 适配器直连真实端点:目录事实、SSE 解析、流式文本、思考增量、usage 不相交计数、工具调用往返、图片确实落在线路上、多账户轮换、八个 Remote 端点的描述符形状,以及真实流式请求的每一块都通过无损 JSON 校验(all streams clean)。
- 对比度(当时手工量的,未随包提供脚本):修复主题色期间,用 Playwright 在真实 web 服务上遍历插件自己的每个叶子文本节点,逐个算「文字色 vs 实际生效背景」的 WCAG 对比度,浅色与深色各一遍 —— 侧边栏卡片、详情面板、设置页三个位置都是 0 处低于 4.5;用同样的手工量法在修复前抓到过深色主题下的 1.05(白底白字)。
仓库里没有这个脚本(package.json 无 playwright 依赖,tests/ 只有 .test.mjs,其中的 CSS 断言只核对 token 名、从不计算对比度),所以这条不可由仓库复现。要复现:起一个真实 web 服务,用 Playwright 取每个叶子文本节点,读 getComputedStyle 的 color 与实际生效背景色,按 WCAG 相对亮度公式算比值。
- 真机上手工验过的(当时用 Playwright 驱动真实 web 服务,控制台零报错,同样没有留下脚本):侧边栏真实额度卡片(只有 5 小时与本周两行,没有第三行账户信息,行距按实拍截图调紧)、点开后的详情面板(登录情况 + 三档额度 + 来源)、「自动/始终显示/始终隐藏」三态在浅色与深色下的对比度、账户列表与凭据有效期、添加账户(类型 + 可选名字)、CLI 登录向导(授权 URL、复制/打开、粘回框、步骤指示)、移除账户、Models 页 provider 卡片(0 个输入框)。
并且用 GLM-5.3 真的聊了一轮:Completed in 4s · 1+1 等于 2. —— 不再中断。
- 12 个模型全部实测可对话;工具调用逐模型实测;图像能力用四象限配色题验证。
跑测试: sh
npm test # 四套不需要密钥的测试;其中 usage 末尾一条要网络
ARK_CODING_TEST_KEY=ark-... npm test # 再加上打真实端点的适配器测试
踩过的坑(注释里也写了)
1. 远端命名空间必须「谁声明、谁读取」。在 ctx.inject(['remote.settings'], cb) 的回调里读 cb.remote.credentials,cordis 会抛 cannot get property "credentials" without inject;这个异常发生在注入回调内部,外层的 try/catch 抓不到,页面就永远停在「凭据服务未挂载」。
三个命名空间各自注入、各自读取。(对应用户 2026-10-16 实测报的那条错。)
2. remote.credentials 的契约是数组 + 信封:describe(refs: string[]) 返回 RemoteResult<Record<ref, CredentialInfo>>,set/unset 返回 RemoteResult<void>。
传单个字符串、或不解 ok/value 信封,都会静默拿到错误结果。
3. Node 的 fetch 会丢掉 Host 头。Host 在 Fetch 规范里是 forbidden header name, Node 24 的 undici 直接静默忽略你设的值。而 arkcli 的回环回调监听会校验 Host,头不对就是 invalid host、登录永远完不成。所以转发回调用的是 node:http.request,它的头能原样落到 socket 上。(这个坑是写测试时对着真实二进制量出来的,不是推理出来的。)
4. 回环监听只认 Host: 127.0.0.1:<那个端口>。逐条试过:localhost:<port>、裸
127.0.0.1、localhost、[::1]:<port>、端口差一个数字、大小写不同,全部 invalid host。
代码以 127.0.0.1:<port> 为准,并在真收到 invalid host 时自动用 localhost 再试一次,免得将来某个版本改了校验就把功能锁死。
5. arkcli 的错误信封打在 stderr 上。它自己的约定是「stdout 只放结构化结果,错误与解释走 stderr」。只看 stdout 的话,未登录时拿到的「错误信息」会是 stderr 的最后一行,也就是一个孤零零的 }。宿主两个流都会解析。
6. arkcli 没有 ARKCLI_HOME,状态目录写死 $HOME/.arkcli。多账户隔离只能靠换 HOME 起子进程(每个 CLI 账户一个),这也是插件创建 <DSH_HOME>/ark-coding/accounts/<id> 的原因。
7. 1.0.39 的 --no-browser 不是两段式。它会在 127.0.0.1:<随机端口> 上起监听并一直等,既不读 stdin,也不写 .sso-pending.json,所以 --code 那条路在这个版本上不可用(包内文档描述的两段式流程与实际行为不一致 —— 以实测为准)。
8. 客户端只收 mode: 'strict' 的参数 codec。协议里还有个 { mode: 'src-json' }(宿主网关内部自己就用它,宿主侧也照收),但浏览器的贡献校验会当场拒掉,抛 field "input" has no strict codec —— 而它抛在整个 $mount 里,于是八个端点一起失效、侧边栏永远停在 Loading…。不碰网络的测试全绿、真机全挂,只有真开一次页面才看得见。
现在两半都用 strict codec(create() 返回一个恒真的宽松解析器),并且把假的 $mount 改成会做同样的校验。
9. node_modules/.bin/arkcli 是包装脚本,不是 CLI 本身。它用 execFileSync 阻塞地去跑真正的 Go 二进制,所以只杀包装进程会留下一个还被 init 收养、继续 LISTEN 回环端口的孙子进程 —— 用户以为取消了,端口其实还占着。登录会话因此以 detached: true 起独立进程组,取消时按进程组(负 pid)杀。
10. 流式 chunk 里一个显式 undefined 属性会让整轮对话在最后一块上失败。DSH 落地每一块前跑 snapshotJsonValue(@deepseek-ai/dsh-util-values),{ foo: undefined } 在 JSON 里表示不出来,于是一整轮直接抛 Assistant stream chunk must be losslessly JSON-serializable。用户看到的现象是「字都打出来了,然后突然没了」 —— 因为炸的是最后那块 finish(它以前带着 replayState: undefined)。这个 bug 在 0.2.2 里就存在,不是重构引入的。
修法是两层:拿掉那一处,并在流式内容的唯一出口(parseStream)统一摘掉值为 undefined 的键 —— { foo: undefined } 与 {} 在 JSON 语义上完全等价,摘掉是忠实的。
回归测试见 tests/stream.test.mjs:它不需要网络也不需要密钥(喂假的 SSE),并且用真的 isJsonValue 校验,而不是自己重写一遍规则。
11. z.boolean().default(true) 会把「用户没配」填成 true,于是「自动」这一档永远失效。
侧边栏卡片要三态(自动 / 始终显示 / 始终隐藏),而 schemastery 的默认值在
apply() 之前就补齐了 —— 写成布尔 + 默认值,代码里根本读不到 undefined。
所以档位改用 z.union(['auto','on','off']).default('auto'):显式、可回传、设置文档里存的就是它本身。默认值不是「没配」,是「配成了这个值」。
12. 深色主题里 --dsw-alias-brand-primary 是近白色(#f9fafb),#0f1115 那种深色才是浅色主题的品牌色。所以「品牌底 + 写死 #fff」在深色下就是白字白底:左下角卡片上那枚「套餐额度」小药丸、主按钮、登录向导的当前步骤条全部看不见(实测对比度 1.05:1)。正解是主题自带的 on-brand 前景 token --dsw-alias-label-primary-foreground(浅色 = #fff,深色 = #0f1115)—— Harness 自己的 Switch 滑块用的就是它。这个主题里不许出现字面量白/黑。
13. 状态色不能当正文色用。state-success-primary(#22c55e)和 state-warn-primary(#f59e0b)在两个主题里是同一个中间调,压在白色卡片上只有 2.28 / 2.2 对比度(AA 要 4.5)。状态类文字一律改成「浅色底(state--tertiary)+ 常态文字(label-primary)+ 彩色描边」。
14. popover 表面在深色主题里是中灰 #61666b(--dsw-alias-bg-overlay),次级文字色(label-secondary)压在上面只有 3.85。所以详情面板内部一律用 label-primary 承载文字,层级改由字号/字重/大写表达。
另外进度条轨道不能用 bg-layer-2:它在浅色主题里等于卡片底色(#fff),轨道完全看不见;用 state-idle-primary(浅 #d4d4d4 / 深 #545557)。
15. 详情面板必须用 position: fixed,不能用 absolute。侧边栏的 sidebarCol / frame 两层都是 overflow: hidden,绝对定位会被直接裁掉;而 fixed 只在祖先带 transform/filter/contain 时才降级 —— 真页面上量过,卡片到 <body> 的祖先链上这三样全是 none,所以 fixed 能安全地逃出裁剪。
面板位置在点开时按卡片的 getBoundingClientRect() 算一次,滚动/改窗口就关掉,不去追着改。
许可
MIT。非官方集成,与火山引擎无关。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
Akiyuki1130/dsh-ark-coding-provider
火山方舟 Coding Plan (Volcengine Ark) 的 DeepSeek Harness 提供商插件:多账户、内置 arkcli、官方套餐额度、12 模型实测容量
相关插件 · 模型接入
API 中继安全审计toby-bridges/api-relay-audit
面向 AI API 中转与 LLM 代理的本地安全审计。
★ 897已汉化DSH
69.4
免费搜索插件DDDMUC/dsh-free-search
DeepSeek Harness 免费搜索插件 —— 无需 API key,零成本,多引擎可切换。 一个给…
★ 761已汉化DSH
68.7
免费联网搜索liustack/modsearch
在官方 App 里模型能联网,切到 API 就不能了。ModSearch 把联网补回来:网页搜索、X…
★ 863已汉化DSH
68.5
OpenCode 免费模型FishBottle7/opencode2dsh
在 DSHDeepSeek Harness里原生使用 OpenCode Zen 的免费匿名模型。
★ 379已汉化DSH
67.8
AI 模型网关tingly-dev/tingly-box
Tingly Box Web UI 演示
★ 373已汉化DSH
67.4
WorkBuddy 账号池XDTrees/dsh-xdpool
把你能白嫖的模型额度接进 DSH 同一个池:WorkBuddy 多账号自动容错、十三家扫码登录、76…
★ 359已汉化DSH
67.1