Releases: masknull/dsh-qoder-connect
Release list
v0.1.8
v0.1.8:修复图片输入必定 400 失败的问题
本版本修复了带图片请求时稳定返回 HTTP 400(ATTACHMENT 错误)的问题。
关键修复
- 图片输入必定失败(Issue #7) 感谢:@luguagndong :
- 根因分析:
DefaultQoderTransport在解析图片块时需要QoderImageAttachments(提供imageLimits与readImageRequest)来准备图片的二进制和规格;但在插件运行时组装阶段,src/index.ts构造createQoderTransport时遗漏了attachments参数,导致 transport 内部的attachments恒为undefined。任何带图片的请求一进入消息翻译(resolveImagePart)便直接抛出Qoder image input requires the DSH attachment service.异常(HTTP 400),从而触发 DSH 无限重试直至取消。 - 修复实现:在
src/index.ts组装层构造了统一且按需动态解析的attachmentFacade,同时包含imageLimits、readImageRequest与saveImage,一并注入给createQoderTransport与QoderUpstreamClient。服务解析保持惰性获取(ctx.get('attachments')),既避免插件加载时序问题,又打通了带图对话时 transport 读回 attachment 的链路。
- 根因分析:
质量与测试验证
- 双端 TypeScript 类型检查:
exactOptionalPropertyTypes0 错误; - Vitest 单元测试:29 个文件、420 项测试全部通过(0 失败);
- Node.js 原生模型通信测试:121 项测试全部通过;
- 全量产物重新构建:
tsdown构建通过,lib/产物已同步注入0.1.8版本号。
v0.1.7
v0.1.6
v0.1.6:修复 v0.1.5 自动签到无法工作的全部问题
v0.1.5 引入的「每日自动签到」在实际使用中存在多处缺陷,导致签到从未真正执行过。本版本逐项修复,并补上额度明细中缺失的一行。
🐛 关键修复
-
自动签到从未执行(v0.1.5 的开关实际无效):
- 配置未合并:设置分区新增的
autoCheckInCN/autoCheckInGlobal/checkInMinuteCN/checkInMinuteGlobal四个字段没有被搬运进插件读取的实时配置,插件读到的永远是undefined—— 卡片保存了、文件里也存着,但调度器始终把开关当作关闭。现已改为按字段清单驱动合并,并新增把「schema 字段」与「合并字段」逐个比对的测试:任何一侧漏字段都会立刻失败。 - 上游客户端标识错误:签到活动接口只对桌面端标识
cosy-clienttype: 10下发活动列表,此前沿用通用的5,接口返回 HTTP 200 但活动数组为空,于是每次都被判定为「今日无活动」。已改为显式发送桌面端标识。 - 定时器从未排期:定时排期依赖了当时还读不到的配置,结果一个定时器都没建立,配置就绪后也没有重排。现在每个变体总会排上定时器,「是否该执行」交由触发时刻判断。
- 同一动作被并发执行两次:
start()的启动补领与设置分区就绪后的补领同时进行,会连续领取两次(服务端幂等,但会写出两条重复日志)。已加在途闩锁。
- 配置未合并:设置分区新增的
-
额度明细少一行:
- 上游在
userQuota(计划额度)之外还会返回addOnQuota(计划外赠送额度,即每日签到领到的那 100),插件此前完全没有解析这个桶,于是网页显示两个包、卡片只显示一个。现已新增「赠送额度」一行。
- 上游在
✨ 改进
- 签到时刻可自定义:每个变体各有自己的签到时间(默认 10:00,按 UTC+8),修改后立即生效,无需重启 DSH。
- 时间可直接键入:原
type=time控件在中文输入法下只能点选/滚轮,已改为时、分两个数字输入框,失焦或回车提交,越界自动收敛。 - 可见性:
- 签到日志页显示「下次自动签到:{时间}」,可直接确认定时器排在哪一刻;
- 定时器每次触发都会留下一条日志(即使当天已领取),不再出现「静默跳过」与「彻底坏了」难以区分的情况。
- 不再把失败当作完成:「无活动」或出错的日子不再标记为「当天已完成」,当天后续仍可重试。
🧪 验证
- 双端 TypeScript 类型检查 0 错误;
- Vitest 29 文件 / 418 项测试全部通过(新增覆盖:配置字段一致性、桌面端标识、并发闩锁、定时排期与重排、自定义时刻、已领取日的可见记录、赠送额度成行等);
- Node 原生测试 121 项通过;
- 真实链路验证:对真实账号执行签到返回
claimed、+100 Credits。
v0.1.5
v0.1.5: 每日 10:00 自动签到引擎、常驻签到日志面板与快捷操作
本版本正式引入了 Qoder 每日自动签到引擎与完整的签到日志管理面板,解决了社区关心的每日 100 Credits 算力额度自动领取诉求(#2)。
✨ 新特性与改进
-
每日 10:00 (UTC+8) 自动签到与开机防漏补签:
- 精准整点唤醒:内置调度引擎,以北京时间 10:00 为基准精确计算时间差并在后台定时唤醒;
- 防漏补签机制:若开机或启动 DSH 时已过当日 10:00 且当天未签到,自动在后台异步补领,避免关机导致的漏签;
- 自动换票与自愈:复用 PAT 自动兑换网关 jobToken,如遇凭证过期自动触发刷新轮换;
- 额度即时生效:签到成功后自动触发额度查询,卡片与侧栏上的可用额度即时 +100。
-
清晰区分国内版与国际版开关:
- 设置面板提供清晰标注的产品开关:
- Qoder(国内版)每日自动签到:每天 10:00 (UTC+8) 自动签到领取 100 Credits 算力额度;
- Qoder Global(国际版)每日自动签到:每天 10:00 (UTC+8) 自动检测并领取可用的福利额度;
- 具备纵深防御:未绑定账号时开关强制置灰禁用,底层拦截非法配置写入。
- 设置面板提供清晰标注的产品开关:
-
常驻【签到日志】Tab 与三大快捷操作按钮:
- 卡片展开后,下方 Tab 栏常驻展示:[ 状态 ] [ 上下文窗口 ] [ 额度明细 ] [ 签到日志 ];
- 立即签到:点击立即调用签到服务领取当日福利并追加日志;
- 刷新:即刻拉取最新状态与签到记录;
- 清空日志:一键清空历史日志记录并重置为空状态;
- 本地持久化:签到日志存放在插件配置目录下(checkin-status.json),自动保留最近 30 条流水记录。
-
README 图文说明更新:
- 中英文双语文档同步补充了自动签到与日志功能的详细说明及操作截图(�ssets/7.png)。
🧪 质量与测试验证
- 双端 TypeScript 类型检查(exactOptionalPropertyTypes)0 错误;
- Vitest 单元测试:29 个文件、406 项测试全部通过(0 失败),全面覆盖自动调度计算、补签触发、Token 轮换、4 个 Tab 渲染与 3 个按钮动作;
- Node.js 原生模型通信测试:121 项测试全部通过;
- tsdown 全量重新打包构建。
v0.1.4
v0.1.4: 统一 Qoder 插件设置卡片、推理 Token 计量与侧栏额度未登录防误触
本版本主要整合了社区贡献的插件卡片统一化重构,补充了思考模型的推理 Token 统计,并针对账号未绑定时的侧栏额度交互构筑了完整的纵深防御。
✨ 新特性与界面优化
- 插件设置卡片三合一重构(感谢 @Lewis-0710 的贡献 #4,解决 #2):
- 将原本在 DSH “设置 -> 插件” 列表中分散占用的 3 个独立卡片(侧栏额度、国内版、国际版)整合为单个以 Qoder 命名的统一卡片;
- 卡片内提供 分段切换器(Segmented Tabs: 国内版 / 国际版),配备实时的绿/灰登录指示灯;
- 展开后统一管理侧栏额度轮询配置与各版本的账号用量、套餐明细及 PAT 凭据。
- 支持上游流式 reasoning_tokens 透传(#4):
- 在出站的 OpenAI 兼容 usage 帧中,将上游返回的
chunk.usage.reasoningTokens透传至completion_tokens_details.reasoning_tokens,DSH 前端可精确展示思考模型的思考过程消耗。
- 在出站的 OpenAI 兼容 usage 帧中,将上游返回的
🐛 缺陷修复与纵深防御
- 账号未绑定时禁止开启侧栏额度与面板(纵深防御):
- 四层防护实现:
- 状态裁决:重构优先级,探针返回
signed-out或未绑定时严格判定为false; - UI 阻断:
ToggleRow增加disabled事件防护,禁用状态下硬拦截点击; - 写入拦截:持久化
write函数拦截sidebarQuotaCN/sidebarQuotaGlobal的开启操作,未登录时直接拒绝写入; - 侧栏卡片防护:主侧栏底部的额度卡片按钮在未登录时添加
disabled属性并拦截open()调用,防止弹出无效面板。
- 状态裁决:重构优先级,探针返回
- 四层防护实现:
- 缓存命中与推理 Token 双重防回归:
🧪 验证与质量保证
- TypeScript 双端类型检查(
tsc -p tsconfig.json && tsc -p tsconfig.client.json)0 错误; - Vitest 单元测试:28 个文件、395 项测试全部通过(0 失败),新增统一卡片交互、登录响应式及未登录拦截等专用回归用例;
- Node 原生测试套件:121 项模型与通信测试全部通过;
- 全量重新打包构建
lib/产物。
v0.1.3
修复:缓存命中不再恒为 0%,上游拒绝原因可见,自愈失败不再静默
缓存命中恒为 0%(#3,感谢 @liuli-meng 的报告)
用 Qoder 模型跑会话时,DSH 统计里「缓存命中」永远是 0%,即使上游返回了缓存读数。
传输层本来已经从上游读到了 prompt_tokens_details.cached_tokens / cache_write_tokens,但 OpenAI 兼容层把 chunk 重新封装成 usage 帧时只透传了 prompt_tokens / completion_tokens / total_tokens 三个字段 —— prompt_tokens_details 在这里被丢掉。而宿主正是从 prompt_tokens_details.cached_tokens 取缓存读数的,于是按 0% 计算;未命中输入也一并算进了 prompt_tokens。
现在出站 usage 帧会把 details 带回去,且只在传输层确实报告过任一缓存计数时才输出 —— 没碰到缓存的请求,帧形状和以前完全一致。prompt_tokens 保持含缓存的 sum 不变(OpenAI 语义下它本来就包含缓存部分)。
上游拒绝原因不再不可见
Qoder 的拒绝是 200 SSE 里第一个 envelope 带 statusCodeValue,而那个 envelope 的 body 才是真正的原因(配额、区域权限、风控……)。旧代码在非 200 时直接抛出、把 body 丢掉,于是一个配额耗尽和一个真正的令牌吊销在用户眼里长得一模一样,都是干巴巴的「无效 API 密钥」。
现在 body 会拼进错误消息,错误详情里能直接看到上游原文。
自愈失败不再静默(并修掉一个会误导的时间戳)
两处一起说,因为它们是同一条链上的:
- 换令牌成功一直有提示行(
jobToken 已自动刷新(时间)),但两轮都失败时一条提示都没有 —— 用户只看到最终的鉴权失败,完全不知道插件已经奋力抢救过。现在会补一条失败提示,且同一场故障只提醒一次(59 次重试的风暴也只打一行)。 - 「成功提示」原来只在聊天被接受时清除,自愈耗尽时没人清。于是一场失败的自愈会留着一张"已恢复"的欠条,被后续某次无关的成功聊天刷出来 —— 实际观测到 16:56 的轮换在 17:47 的消息旁显示「已自动刷新(16:56:13)」,和同一场自愈已经打过的失败行自相矛盾。现在自愈未恢复即作废该提示,同时给顺延加上 5 分钟上限,过期轮换直接丢弃而不是迟发。
验证
- typecheck 0 错;
- vitest 387/387,node:test 121/121;
- 新增回归:缓存 details 透传、envelope body 透传、失败提示只报一次且恢复后可再报、自愈耗尽不留成功欠条、过期轮换丢弃、正常路径提示不受影响;
- 构建产物声明的版本与
package.json一致(由tests/version.spec.ts守护)。
v0.1.2
优化:请求体编码提速约 20 倍,后台任务不再可能拖垮宿主
编码提速(本次核心)
qoderEncodeBody 原先逐字符翻译请求体(out += char 配 alphabet.indexOf(char)),每 1 MB 请求体在主线程上要花约 500 ms,而每个模型请求都要跑一遍——几个大上下文会话并发时足以把 Harness 的事件循环压满,表现为界面卡住、HTTP 端口无响应。
现在改为 256 字节翻译表 + latin1 Buffer 单遍翻译:输出与旧实现逐字节一致,单函数 1 MB 约 25 ms。
同机实测(序列化 + 编码的端到端准备):
| 请求体 | 优化前 | 优化后 |
|---|---|---|
| 1 M 字符 | 458 ms | 112 ms |
| 4 M 字符 | 4765 ms | 377 ms |
| 10 M 字符 | 13100 ms | 865 ms |
tests/qoder/encoding.test.ts 新增用例:与旧实现的等价性对照(长度 0..40 全覆盖、中文与 emoji、Buffer 入参),以及 1 MB 的时间预算断言。
后台任务不再可能拖垮宿主
Node 遇到未处理的 Promise 拒绝会直接终止进程(桌面壳显示为后端"意外退出")。此前插件里若干"发射即忘"的调用——回环端点关闭、宿主心跳写入与清理、凭据轮询、变体启动——一旦失败就是未处理拒绝,一个心跳写失败就足以带走整个 Harness。
- 上述路径全部改为带失败处理,失败只记一条
warn,不再外泄; - 目录持久化失败改为记日志而非抛出(
fetchCatalog承诺对调用方永不拒绝); - 回环 shim 的顶层请求处理再兜一层
catch。
文档
- 中文 README 措辞微调(中国版 → 国内版),英文 README 保持同步。
验证
- typecheck 0 错;
- vitest 385/385,node:test 115/115;
- 构建产物声明的版本与
package.json一致(由tests/version.spec.ts守护)。
v0.1.1
修复:上游拒绝 jobToken 时的自愈、会话内提示,以及新增账号后的目录刷新
自愈(核心修复)
上游网关可能在本进程仍持有缓存 jobToken 时开始拒绝它,而 PAT 本身完全有效。此前这会表现为《API 密钥无效》,只能重启 DSH 才能恢复。
- 自动重换并重试:聊天端点返回 401/403 时,丢弃缓存的 jobToken,绕过共享 single-flight 重新换取(避免被并发的额度轮询/目录扫描取消),并以 2 秒节奏重试两轮;真正被吊销的 PAT 仍会如实失败,不会死循环。
- 在被接受的瞬间上报:刷新在重试请求被上游接受时即上报,而不是等流读完——消费者可能提前关闭流,写在
yield*之后的代码永远不会执行。 - 配额读取同样自愈:额度接口遇 401 时走同一条恢复路径。
- 归类收敛:新增共享判定
isQoderAuthRejection(code 或 401/403 状态码),网关文案invalid job token、token expired归入 auth。
会话内提示行
自愈发生时,向触发它的会话打印一行:
[图标] qoder · jobToken 已自动刷新(时间)— 旧令牌被上游拒绝,已自动重换并恢复
实现方式为直接追加宿主自带的 log-only command/run + command/done 事件。
新增账号后立即拿到真实模型列表
保存 PAT 时原先以「发射即忘」触发目录拉取,卡片紧接着读取的状态里仍是内置兜底名单(Cantus (Qoder)、Qoder Ultimate…),必须手点刷新或重开设置才会变成上游真实目录。现在保存应答会等待目录拉取完成再返回;fetchCatalog 自带错误处理(失败仅记为 catalog.error),因此上游慢或失败都不会把一次有效保存变成报错。
文档
- 以中文 README 为基准同步英文 README(补齐 PAT 直达链接)。
验证
- 385 个 vitest 用例 + 113 个 node:test 传输层用例全部通过;
typecheck通过。 - 自愈链路已在真实上游端到端验证:旧令牌被拒(403)→ 188ms 内自动重换 → 重试被接受 → 消息正常返回、会话出现提示行、无《API 密钥无效》。
- 新增用例覆盖「保存应答返回时目录已是 live」,并已确认该用例在未修复时会失败(防止回归)。
完整改动:788e74c...v0.1.1
v0.1.0
dsh-qoder-connect v0.1.0
将 Qoder(国内版 / 国际版)的模型以个人访问令牌(PAT)接入 DeepSeek Harness(DSH)。
功能
- PAT 即存即用:卡片粘贴 PAT 实时校验后落盘,模型分组立即出现,无需重启 DSH;支持更换 / 清除 PAT
- 双变体独立配置:
qoder(国内版)与qoder-global(国际版)各有凭证文件、路由与已保存目录,互不混用 - 模型目录三级降级:live → saved → fallback,卡片明示列表来源与最近一次失败原因,支持手动刷新
- 上下文窗口一键切换:两版卡片各自独立的「使用上游声明的最大上下文窗口」开关(默认勾选),如 Qwen3.8-Max 1M / 200K
- 侧栏额度展示 + 额度明细:侧栏底部额度小卡,点开按套餐逐行展示剩余 / 总量 / 进度 / 到期,两版可切换
- 倍率显示:模型名后缀
x<priceFactor>(来自上游 price_factor,仅展示) - CLI:
doctor/status/pat set|clear/logout/catalog refresh,支持--json
安装
dsh plugin --profile web add github:masknull/dsh-qoder-connect
# 或 npm(发布后)
dsh plugin --profile web add dsh-qoder-connect质量
- vitest 384 用例 + node:test 108 传输层用例全绿
pnpm run check一条门禁:typecheck + test + test:qoder + build
Full Changelog: 首个发布版本