dsh-plugin-llm-codebuddy
Verifieddsh-plugin-llm-codebuddy · v1.2.1 · MIT · Web UI
Tencent CodeBuddy LLM adapter for DeepSeek Harness
Install
dsh plugin add dsh-plugin-llm-codebuddy Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Readme
dsh-plugin-llm-codebuddy
腾讯 CodeBuddy 的 DeepSeek Harness(dsh)适配插件。
它把 CodeBuddy 的模型目录与对话端点接入 harness,注册为名为 codebuddy 的
LLM 路由,让你的 CodeBuddy 账号额度直接用于 dsh 里的对话。插件不会自行创建
或删除凭据:它读取凭据与目录、流式对话,并在令牌临近过期时刷新后写回同一条
记录(刷新失败不删除凭据)。
装上之后你会多得到这些:
- 模型选择器上直接看价格与促销——模型名后面挂促销徽章,最右边是积分倍率。
- 账号额度可视化——侧栏进度条、输入框旁的胶囊、插件详情页的同心圆环,三处同源。
- 用 CodeBuddy 账号接管网页搜索与抓取——一个开关,不抢别人的位置。
- 限流与偶发失败有人兜底——429 会告诉你重置时刻,瞬时失败自动退避重试。
安装
dsh plugin 会把参数转交给该 profile 目录下的包管理器,因此安装就是一次普通的包安装:
dsh plugin --profile web add dsh-plugin-llm-codebuddy
装好后重启 dsh web(或让 profile 重新加载)生效。
登录
CodeBuddy 使用浏览器签发的令牌认证。安装后在 设置 → 插件 → CodeBuddy 里点「登录」,浏览器完成授权即可;令牌写入 harness 的凭据存储,不作为配置项,也不会被本插件另存一份。
- 未登录时模型列表为空,实际发起请求会以「缺少凭据」失败。
- 访问令牌临近过期会自动刷新;刷新失败不会删除凭据,只有刷新令牌本身也过期 才会提示重新登录。
配置
| 配置项 | 默认值 | 说明 |
|---|---|---|
baseURL |
https://copilot.tencent.com/v2 |
对话接口地址。 |
defaultContextWindow |
128000 |
目录未标注容量的模型所用上下文窗口。 |
defaultMaxTokens |
8192 |
目录未标注容量的模型所用最大输出。 |
streamIdleTimeoutMs |
300000 |
对话流的空闲超时。 |
模型目录
模型来自 CodeBuddy 的目录接口,会随服务端编辑自动更新,无需重启。只在目录里 标注了容量(上下文与最大输出均为正)的模型才会出现在选择器中;未标注容量的 条目不出现在列表里,但仍可用模型 id 直接指定,此时按上面的默认值保守处理。
已知问题:部分模型的自动压缩不会触发
CodeBuddy 目录里标注的「最大输出」有时比模型本身的上下文窗口还大:
| 模型 | 上下文窗口 | 目录标注的最大输出 |
|---|---|---|
minimax-m3 |
512,000 | 524,288 |
kimi-k3-1 |
1,000,000 | 1,048,576 |
harness 判断「什么时候该自动压缩会话」时,会先把这段输出预留从窗口里减掉。这两个 模型减出来是负数,于是自动压缩永远不触发,上下文一路增长,最后由网关报上下文 超限。
已经验证过:这不是网关的限制(发同样大的 max_tokens 网关照收,而且输出会精确
停在那个数),是客户端按目录声明值算出来的结果。把输出预留压到窗口以内,这两个
模型的判断就恢复正常,其余模型不受影响。
本插件目前没有修这个(仍然直接使用目录声明的值)。如果你常用上面这两个模型、 又遇到了上下文超限,先换一个模型试试。
模型选择器上的价格与促销
CodeBuddy 的模型目录里带着促销与订阅信息,插件把它们直接摆到模型选择器里,省得 每次去官方 IDE 里对一遍:
Hy3 [限时免费] x0
- 徽章:模型名后面是当前生效的促销(限时免费、夜间折扣、错峰使用……)与订阅 权益(订阅优先)。徽章颜色由 CodeBuddy 服务端给出。
- 倍率:最右边的
x0.29是这个模型的积分倍率,倍率越低越便宜(x0就是 免费)。促销生效时显示的是折后的倍率——限时免费期间是x0。 - 悬停看详情:基础倍率 → 当前生效倍率、促销说明、促销的时间窗、模型简介。
促销按 CodeBuddy 给的时间窗自动出现和消失(比如「每晚 23:00–次日 8:00」那种夜间 折扣,白天就没有)。插件每 5 分钟向 CodeBuddy 对一次目录,所以时间窗翻转后最多五 分钟内生效。
徽章只属于 CodeBuddy 提供的模型:用别的 provider 时,模型名照常显示,不会有徽章 或倍率。
用量额度
插件提供三个入口查看账号额度,数据同源,因此任何两处显示的数字都不会互相矛盾。
侧栏指示器(默认关闭)
侧栏底部用两条进度条回答「还剩多少、还能自由花多少」:
- 第一条「本月剩余」:灰轨是总剩余,高亮段是本月内到期的那部分,条长就是 本月剩余占总剩余的比例——一眼看出「总剩看着不少,本月能自由花的只是一小段」。 右侧数字是本月消耗率,消耗率越高颜色在同一灰蓝色系内越深。
- 第二条「最近剩余」:把未来到期的额度按 ≤1 天 / 1–2 天 / 2–7 天分成三段, 每段宽度按该档额度分配,段内实心部分是该档的已用比例。没有任何短期到期额度时 整条隐藏。
悬停可看到本月已用/额度、本月剩余、总剩余,以及每个到期段的精确数值。
默认关闭是因为侧栏底部是常驻区域,加进度条会改变默认界面。
输入框额度胶囊(默认开启)
输入框工具行、模型选择器左侧显示 CodeBuddy · 本月 xx%,点击打开完整明细。
只在当前选中 CodeBuddy 模型时出现——额度是 CodeBuddy 账号的属性,用别的
provider 时它既无意义也不该占位。
同心圆环
插件详情页与胶囊弹窗展示同一套同心圆环,中心与三环由内向外分别是:
| 位置 | 内容 |
|---|---|
| 中心 | 总剩余积分 |
| 内环 | 已用积分(占总额度的比例) |
| 中环 | 本月到期(占本月额度的比例) |
| 外环 | 未来 7 天,按天分段 |
三个环是同一种读法:空轨表示未使用,彩色弧表示已使用,弧长就是已用的比例。 分段环也不例外——每一天未用的部分与另外两环同一个底色,只有已用的部分才是 当天自己的颜色,所以七个环段彼此可分辨,一眼能看出「哪几天有额度、各用了多少」。
按天分段的宽度按当天发放的额度分配,段与段之间留有空隙。当天没有发放额度
是特殊情况:它不画彩色弧,只留一小段很淡的占位,既让七个位置保持稳定(便于记住
「第几天」),又不会让人误以为那天有额度可花;图例里也会显式写「—」而不是 0 / 0。
打开或数据刷新时,三个环会从零顺时针生长出来,并逐环错开;如果你在系统里设置了 「减少动态效果」,动画会被跳过,静态图形照常显示。
每日签到
圆心是签到按钮:点它就给 CodeBuddy 账号签到,领当日积分(Buddy 加油站)。
- 未签到时圆心是干净的,鼠标移上去会浮起一圈淡底,提示「这里可点」。
- 签到成功后圆心铺上一层灰绿,中心数字换成深墨色——铺色的过程与三环的生长 动画同一个节奏(同一条曲线、同一时长),读起来是一次动作。
- 已签到时圆心再点不动:按钮是真的
disabled,键盘也到不了。 - 签到失败才会弹一条提示(成功不打扰,因为「颜色变了」本身就是结果);提示里 会带上服务端给的原因,例如「活动未开启」。
签到成功会顺带刷新一次额度读数——签到加了积分,数字得跟着变。
签到只在你点的时候发生,插件不会定时替你签。
读数时效
额度读数由宿主向 CodeBuddy 拉取,每 60 秒刷新一次。
同账号的重复读取由宿主缓存吸收:多个入口、多次重开面板或刷新页面都不会各自再打 一次 CodeBuddy,只有缓存过期后才在后台刷新一次。
用 CodeBuddy 接管网页搜索与抓取
开启后,harness 内置的 web_search 与 web_fetch 会改走 CodeBuddy 网关,
复用同一个账号,不需要额外凭据。
开关在 设置 → 插件 → CodeBuddy → 「使用 CodeBuddy 搜索」,且只在已登录时 出现(未登录时插件根本不注册相关后端,开关无从生效)。
| 行为 | 说明 |
|---|---|
| 默认值 | 关闭。安装插件不会静默抢走搜索位。 |
| 生效时机 | 即时,无需重启。 |
| 范围 | 一个开关同时控制搜索与抓取。 |
| 退出登录 | 自动释放,不影响其他搜索提供方。 |
| 重新登录 | 开关仍为开时自动恢复接管。 |
开关旁有一枚生效态徽标,用来区分「开关想怎样」和「实际怎样」:
| 徽标 | 含义 |
|---|---|
| CodeBuddy 搜索生效中(绿) | 搜索确实走 CodeBuddy 网关。 |
| 未接管搜索(灰) | 你主动关掉了,或未登录。 |
| 接管未生效(琥珀) | 开关已开且已登录,但接管没成立(宿主未提供相关能力等)。 |
没有这枚徽标时,「开关开着却没生效」与「开关是关的」看起来一模一样。接管通过 运行时选择实现,关闭时会还原成原来那个提供方,因此可以和其他搜索插件共存。
遇到频率限制(429)时的行为
当账号额度被限流,CodeBuddy 返回 429,并在提示里给出重置时刻(例如
「将在 2026-09-27 17:30:18 UTC+8 重置」)。插件会读出这个时刻,接下来怎么走
由一个开关决定:
| 「遇到 429 等待重置」开关 | 行为 |
|---|---|
| 关(默认) | 429 按普通失败处理:快速重试几次(约 5 次、15 秒内)后抛出错误,报文里的重置时刻随错误消息原样保留,可据此切换模型继续。 |
| 开 | 等到服务给出的重置时刻再自动重试;但最多只等 5 小时。 |
- 5 小时是硬上限,与开关无关:重置时刻更远时,插件也不会挂上这个等待——一个 回合为一个可能长到隔夜的重置默默挂住,不如立刻失败让人接手。
- 等待路径(开关开且 ≤5 小时)在对话里有一条可展开的倒计时提示,说明第几次 重试与失败原因,随时可中断。
- 报文里读不出重置时刻时(服务抽风,少见):与开关无关,按普通失败重试几次后 失败;宿主日志会留一份报文原文供核对。
- 提示里的重置时间会原样保留,不会因为错误分类而丢失(分类特意不用
QUOTA—— 界面会把它换成一句通用文案,把那句话吃掉)。
开关在 设置 → 插件 → CodeBuddy →「遇到 429 等待重置」,即时生效,无需重启。
与 CodeBuddy 网关的两处兼容
CodeBuddy 网关对请求的写法有自己的规矩,harness 默认发出来的样子过不去。插件在 请求出门前做了两处调整——不调整的话,每一轮对话都会被网关当场拒收。
改写系统提示开头的身份声明
harness 给每轮对话加的系统提示,开头固定有一句「You are an AI agent powered by DeepSeek Harness…」。CodeBuddy 网关按渠道校验调用方,以这句原文开头的请求一律 拒收:
HTTP 400 · code=11128 · Illegal API invocation from an unapproved channel
界面上表现为一句「Request blocked. Please send it again」。这看着像登录出了问题, 其实不是:同一份凭据、同一组请求头,只要把这句改掉就立刻正常返回。
网关只认开头那一句(原句出现在别处不拦),也不区分大小写。所以插件只在开头
那句里补一个逗号——You are an AI agent powered by DeepSeek Harness. 变成
You are an AI agent, powered by DeepSeek Harness.,句子仍然成立、模型读到的身份
说明不变,但上游的整句比对失效。系统提示其余位置的产品名换成中性代称
(the DeepSeek Harness Web GUI → this harness Web GUI)。
改写 @ 路径说明那句
系统提示中段还有一句由 @deepseek-ai/dsh-file-reference 贡献的说明:
Tokens prefixed with @ are paths the user explicitly referenced. Relative paths resolve from the workspace root; …
它与产品名无关,所以上面「补逗号 + 产品名代称」两条规则都盖不到它。2026-10-29 用
真实 system 正文二分定位确认:把这句原样放进请求就 400 · code=11128,把
prefixed with 改成 starting with 立刻 200(只改一个词 explicitly →
directly 也不再命中,说明上游比对的是长字面量,不是语义)。插件因此把这两个词
换成等义说法,其余文字逐个保留。
把你这一轮说的话放在最后
harness 在你的消息后面还会附上运行时上下文(AGENTS.md、运行时快照、技能清单、 当前时间),而 CodeBuddy 把一轮里最后一条用户消息当成你这次的提问。不重排的话, 网关读到的是那些附带内容,不是你问的话。
所以插件把每一轮里你的话挪到该轮用户消息的末尾。每一轮都这么排(包括已经结束的 轮次),这样一轮对话无论出现在哪都长得一样,请求前缀保持稳定,网关那边的缓存照旧 命中。
渠道自检与 CHANNEL_BLOCKED
网关按渠道校验调用方时,用的是这一句:
HTTP 400 · code=11128 · Illegal API invocation from an unapproved channel
以前它被归进 INVALID_REQUEST,读起来像「请求参数写错了」。这两件事的处置正好相反:
参数错了改请求就有救;渠道校验拒收是确定性的——同一份请求重发多少次都是同一个结果,
只能等上游放开或更新插件。所以插件把它单独归成 CHANNEL_BLOCKED,并且默认策略下不重试
(mode: 'always' 的策略会重试一切):重发只是把一次失败拖成几十秒。
每次真实请求撞上它,插件都把现场追加到 $DSH_HOME/codebuddy-channel-health.json
(最多留 20 条),卡片上的「上次真实拦截」读的就是这份。文件里只有诊断——时间、
endpoint、请求 id、当时发出的 X-User-Id、系统提示的长度、一条关于指纹形状的猜测——
不含凭据,也不含提示词正文;文件是插件自己写的 UTF-8(不带 BOM),读取时容忍开头
的 BOM(Windows 编辑器加的),不会因此把整份记录当成不存在;UTF-16 不自动识别,手改成
UTF-16 会按「无记录」处理。文件权限 0600;目录权限 0700 只在本插件创建它时生效,
已存在的 $DSH_HOME 保持原有权限;这两项在 Windows 上只映射到只读位,不对应 POSIX 语义。
删掉它只是丢掉读数,不影响任何请求。
自检按钮
设置 → 插件 → CodeBuddy →「渠道自检」→「运行自检」。
三发最小请求(max_tokens: 1),依次量:不带系统提示的裸请求能不能过(这一发只考请求头
与客户端身份)、原样带上已知指纹的请求会不会被拦、当前中和过的那份会不会被拦。三发的
差别只在系统提示,所以结论能把「请求头不合规」与「指纹被拦」分开——这正是 11128 的
报文本身分不出来的。
| 结论 | 含义 |
|---|---|
| 通道正常 | 已知指纹规则仍在,当前中和有效。 |
| 上游已不再按已知指纹拦截 | 三发全过;中和规则可以简化了。 |
| 中和失效 | 带上指纹的那发与中和过的那发都被拦:上游改了匹配方式,需要更新插件。 |
| 结果自相矛盾 | 未中和的放行、中和过的被拦。按上游灰度对待,隔一会儿再跑一次。 |
| 请求头/客户端身份不合规 | 裸请求就被拒,与提示词无关。 |
| 凭据失效 / 频率受限 / 额度用尽 / 连不上网关 | 与渠道校验无关的前置问题,先解决它。 |
会消耗一点点额度:三发都是最小请求,但确实是三次真实调用。因此自检只在你点按钮 时发生——插件没有定时检查,打开设置页时「渠道自检」这一块只读盘、零外呼;连点会被 并成同一轮,不会重复扣。 自检那一行只在已登录、且对话地址是官方网关时出现:自建网关没有渠道校验,三发会恒报 「上游已不再拦截」,那是误导,不如不显示。
自检自己撞上的 11128 不写进「上次真实拦截」:那是主动探测的结果,混进去会把一次
自检说成你的请求被拒。
已知盲区:探针只认识仓库里有证据的那条指纹
自检的 B、C 两发,用的都是仓库里确证过的那一条指纹(系统提示开头那句身份声明)。 它回答的是「这条规则还在不在、当前的中和是否还有效」,不是「上游以后还认不认我们」。
上游若新增一条与这句话无关的规则,探针完全可能三发全过、结论写着「通道正常」,而真实
对话照样被拒:真实请求带着完整系统提示、工具定义与多轮历史,比三发 ping 多得多,新的
判据就藏在探针根本不发的东西里。
这条盲区已经兑现过一次。 2026-10-29 的拦截就属于这一类:真实请求全被拦,自检三发
却报「通道正常」。事后拿拦截会话里 system/message 的 14,342 字正文做二分,才定位到
@ 路径说明那句(见上文「改写 @ 路径说明那句」)。探针至今仍只测身份声明那一条,
所以它现在覆盖的是两条已知规则里的第一条——报「通道正常」依然不等于「真实请求
能过」。
这种规则只能由被动记录发现——下一次真实请求撞上 11128,「上次真实拦截」那一行就会
更新,留下时间与指纹猜测、以及拦截会话的 system/message(会话日志里就有完整正文,
报障时 session-<id> 就是二分的原料)。所以两处读数是互补的:探针回答已知规则的
状态,拦截记录回答有没有出现未知规则。任何一处单独亮着,都不能当作「一切正常」。
尚未支持
- 命令行登录命令。
- 目录与用户自定义模型条目的合并。
许可证
MIT,见 LICENSE。