DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Ark Coding Provider — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins
A

dsh-ark-coding-provider

Ark Coding Provider

DeepSeek Harness provider plugin for Volcengine Ark Coding Plan: registers the ark-coding route, provides a catalog of 12 models, uses the built-in arkcli to retrieve the official model catalog and actual plan quotas, supports multiple accounts (CLI SSO login / API Key), and includes a login wizard

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:Akiyuki1130/dsh-ark-coding-provider#a412af3f085afe7989d1386efb426e3e4a1c3d8a
READMECompatibilityVersions
设置页浅色设置页深色

Description

DeepSeek Harness provider plugin for Volcengine Ark Coding Plan: registers the ark-coding route, provides a catalog of 12 models, uses the built-in arkcli to retrieve the official model catalog and actual plan quotas, supports multiple accounts (CLI SSO login / API Key), and includes a login wizard in the settings page and a usage card in the sidebar (click to expand login status and three-tier quotas).

Compatibility and provenance

Ark Coding Provider is published as dsh-ark-coding-provider and currently resolves to version 0.5.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
github
Registry updated
10/16/2026

Versions

0.5.0stable
10/16/2026

Related plugins

Loading related plugins…

Latest
0.5.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
web
License
MIT
Source
github
GitHub
★ 0
Weekly downloads
0
Last push
10/16/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in models-usage.

Whale Widgetdsh-whale-widgetDeepSeek balance whale widget in the bottom-right corner of the DSH Web interface: balance/today’s usage/peak-off-peak pricing, customizable bubble click sequence (text/balance/today/peak-off-peak/image/random phrases and parallel weighted selection), per-line styles and fonts, floating quick editinUsage@linxin666/dsh-usageUsage statistics plugin for the dsh web GUI: per-provider balance and coding-plan quota detection plus a live token usage ledger, with a compact price glance card above the sidebar's Settings rowClient Ui Model Capabilities@linxin666/dsh-client-ui-model-capabilitiesPer-model capability declarations (image input and reasoning efforts) for custom pi-ai providers, edited in place on the Models settings cards; writes the official llm-pi-ai settings namespace, no DSH source changes.Codex Subscriptiondsh-codex-subscriptionUse ChatGPT and Codex subscriptions in DeepSeek Harness with OAuth, quota, safe resets, web search, images, and Fast mode

README

dsh-ark-coding-provider

火山方舟 Coding Plan 的 DeepSeek Harness LLM 提供商插件(非官方社区集成)。

仿照 @mars-sea/dsh-commandcode-provider 的形态做的:注册一个提供商路由、在 Models 页挂一张属于自己的卡片、在设置里开一个 同名分区,把模型目录、容量事实和密钥管理都放在那里。

这是社区集成,不是火山引擎官方产品。你需要自己的火山方舟账号与 Coding Plan 订阅, 并遵守火山引擎的服务条款。

截图

设置页侧边栏额度卡片 + 详情面板
设置页浅色额度面板浅色
设置页深色额度面板深色

它给你什么

  • 一个提供商路由 ark-coding —— 直接对着 <span>https://ark.cn-beijing.volces.com/api/coding/v3</span> 的 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 —— 不用你自己装:设置页里一个按钮把官方 CLI @volcengine/ark-cli 装进插件自己的目录, 然后用它拉官方模型目录和真实套餐额度。装的是钉住的 @volcengine/ark-cli@1.0.39(实测通过的版本),不是 latest:额度、目录、 聊天取 key 三条链全都建立在它的输出形状上,装 latest 等于每次点安装都默默绑定 当天的最新版。
  • 两种账户,可混用 —— 每个账户要么是「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-evolving1M256K文本 · 图像off/minimal/low/medium/high/maxDoubao
doubao-seed-2.1-pro1M256K文本 · 图像off/minimal/low/medium/high/maxDoubao
doubao-seed-2.1-lite1M256K文本 · 图像off/minimal/low/medium/high/maxDoubao
doubao-seed-2.0-mini256K128K文本 · 图像off/minimal/low/medium/high/maxDoubao
minimax-m31M128K文本 · 图像off/minimal/low/medium/high/maxMiniMax
glm-5.31M128K文本low/medium/high/maxZhipu
glm-5.3-flash1M128K文本 · 图像off/minimal/low/medium/high/maxZhipu
deepseek-v4.1-flash1M384K文本 · 图像off/minimal/low/medium/high/maxDeepSeek
deepseek-v4-flash1M384K文本off/minimal/low/medium/high/maxDeepSeek
deepseek-v4-pro1M384K文本off/minimal/low/medium/high/maxDeepSeek
kimi-k2.8-preview1M128K文本 · 图像off/minimal/low/medium/high/maxMoonshot
kimi-k31M128K文本 · 图像off/minimal/low/medium/high/maxMoonshot

三个反直觉的地方(都是实测结论,不是猜的)

  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(替换已加载的包需要新的模块世代)。

配置密钥

最省事的路径是设置页:设置 → 火山方舟 → 「账户」卡片,新建一个「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= 起子进程。这样同一台机器上 可以同时持有多个火山引擎账号,互不覆盖。

账户也可以显式指定 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 个模型不在本套餐列表中」, 这是"官方又发新模型了"的信号。

配置项

字段默认说明
apiKeyEnvARK_CODING_API_KEY只在 accounts 为空时被读取:作为兜底账户(id default)的凭据引用,也是官方 Models 页密钥编辑器写的那个引用名。显式设成空串即关闭这条兜底;引用名不合法(如 MY-KEY)同样不产生兜底账户
apiKey—只在 accounts 为空时被读取:作为兜底账户的字面量密钥(带 secret 角色,永不进设置描述)。账户自己的字面量密钥写在账户对象的 apiKey 上,不会补给其它账户
apiBaseCoding Plan v3 端点换端点/代理时改这里
defaultMaxTokens65536未指定输出上限时的请求值,会被逐模型上限夹住
requestTimeoutMs300000首字节超时
streamIdleTimeoutMs300000流空闲超时
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(不是秘密,明文配);留空 = 不用这条路
secretAccessKeyEnvARK_ACCESS_KEY_SECRETAK/SK 的 Secret 走这个凭据引用(role: credential-ref),值写凭据服务、不进配置文件
regioncn-beijing

accounts 为空时,能聊的账户就只剩顶层兜底那一个(apiKeyEnv 非空才有;见上)。 它不写进 accounts,所以设置页删掉它并不会让它消失 —— 只要 apiKeyEnv 还在, 下一个请求它就会回来;要关掉就把 apiKeyEnv 设成空串。那时设置页才会显示 "没有账户"的空状态并引导你新建。

除顶层 apiKey(secret 角色,只随组合配置文件落地)外都是易变字段:设置页保存后 加载器原地提交,下一个请求就生效,不需要重启。

工作区里的文件

文件作用
index.js宿主半边:Config、OpenAI 兼容适配器、模型目录投影、模型发现、Remote 端点、arkcli 编排
models.js12 个模型的实测目录 + 官方目录对照(annotateModels)
accounts.js账户归一化(apikey / cli 两种类型、accounts 为空时的兜底账户、重复 id 去重)、账户 id 清洗的唯一实现(arkcli.js re-export 这一份)、轮换标记与排序、侧边栏卡片的三态判据
usage.js套餐额度的备选来源:AK/SK 的 Volcengine V4 签名与 GetCodingPlanUsage 解析
arkcli.jsarkcli 桥:探测 / 一键安装(钉住 1.0.39)/ 取额度 / 取目录 / 无浏览器登录编排
client.js浏览器半边:设置页 + Models 页卡片 + 侧边栏用量,只用主题 token,不引入任何 Client 包
cordis.patch.ymlbundle 层:插入 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.mjs42/42不需要密钥;末尾一条要网络(真实控制面)
tests/arkcli.test.mjs174/174不需要密钥、不需要网络(起本地假 arkcli / sleep 做进程实验)
tests/client.test.mjs167/167不需要密钥、不需要网络(内存渲染 + 自建 CSS 解析器)
tests/host.test.mjs80/80不需要密钥、不需要网络;需要 harness 包在场(跑 apply() 与适配器本体)
tests/stream.test.mjs9/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 的回归测试。

跑测试:

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 改成会做同样的校验。

许可

MIT。非官方集成,与火山引擎无关。

AK/SK 打控制面时用的区域;默认值即 usage.js 的 VOLC_DEFAULT_REGION
arkcliPath''显式指定 arkcli 可执行文件,优先级高于「插件自装目录 → PATH」。准确性说明:该字段此前读进配置后没有消费者(显式指定不生效),从宿主接线的那一版(v0.5.0)起才真正传给 arkcli 探测
catalogRefreshSeconds3600官方模型目录的刷新间隔(目录是控制面数据,不必每次轮询都拉)
usageRefreshSeconds60用量轮询间隔
sidebarQuotaauto左下角用量卡片:auto(有能查额度的绑定就显示)/ on / off。显式档位永远压过自动判据
  • 适配器直连真实端点:目录事实、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 个模型全部实测可对话;工具调用逐模型实测;图像能力用四象限配色题验证。
  • node_modules/.bin/arkcli 是包装脚本,不是 CLI 本身。它用 execFileSync 阻塞地去跑 真正的 Go 二进制,所以只杀包装进程会留下一个还被 init 收养、继续 LISTEN 回环端口的 孙子进程 —— 用户以为取消了,端口其实还占着。登录会话因此以 detached: true 起独立进程组, 取消时按进程组(负 pid)杀。

  • 流式 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 校验,而不是自己重写一遍规则。

  • z.boolean().default(true) 会把"用户没配"填成 true,于是"自动"这一档永远失效。 侧边栏卡片要三态(自动 / 始终显示 / 始终隐藏),而 schemastery 的默认值在 apply() 之前就补齐了 —— 写成布尔 + 默认值,代码里根本读不到 undefined。 所以档位改用 z.union(['auto','on','off']).default('auto'):显式、可回传、 设置文档里存的就是它本身。默认值不是"没配",是"配成了这个值"。

  • 深色主题里 --dsw-alias-brand-primary 是近白色(#f9fafb),#0f1115 那种 深色才是浅色主题的品牌色。所以"品牌底 + 写死 #fff"在深色下就是白字白底: 左下角卡片上那枚「套餐额度」小药丸、主按钮、登录向导的当前步骤条全部看不见 (实测对比度 1.05:1)。正解是主题自带的 on-brand 前景 token --dsw-alias-label-primary-foreground(浅色 = #fff,深色 = #0f1115)—— Harness 自己的 Switch 滑块用的就是它。这个主题里不许出现字面量白/黑。

  • 状态色不能当正文色用。state-success-primary(#22c55e)和 state-warn-primary(#f59e0b)在两个主题里是同一个中间调,压在白色卡片上只有 2.28 / 2.2 对比度(AA 要 4.5)。状态类文字一律改成"浅色底 (state-*-tertiary)+ 常态文字(label-primary)+ 彩色描边"。

  • popover 表面在深色主题里是中灰 #61666b(--dsw-alias-bg-overlay), 次级文字色(label-secondary)压在上面只有 3.85。所以详情面板内部 一律用 label-primary 承载文字,层级改由字号/字重/大写表达。 另外进度条轨道不能用 bg-layer-2:它在浅色主题里等于卡片底色(#fff), 轨道完全看不见;用 state-idle-primary(浅 #d4d4d4 / 深 #545557)。

  • 详情面板必须用 position: fixed,不能用 absolute。侧边栏的 sidebarCol / frame 两层都是 overflow: hidden,绝对定位会被直接裁掉; 而 fixed 只在祖先带 transform/filter/contain 时才降级 —— 真页面上量过, 卡片到 <body> 的祖先链上这三样全是 none,所以 fixed 能安全地逃出裁剪。 面板位置在点开时按卡片的 getBoundingClientRect() 算一次,滚动/改窗口就关掉, 不去追着改。