opencode-models-discovery 最初解决的是一个很具体的问题:OpenCode 接入 LM Studio、Ollama、LiteLLM、OpenRouter 或各种 OpenAI-compatible provider 时,模型列表变化太快,手写 `models` 配置很容易过期。
所以插件做了一件简单但高频的事:启动 OpenCode 时,从 provider 的模型接口读取模型列表,再把发现到的模型注入到 OpenCode 的 provider 配置里。
但走到 v0.12,这个插件遇到的问题已经不只是“如何发现模型”。真正摆在面前的是:当插件自己的配置机制变复杂后,它能不能帮助用户理解和维护这些配置?
这就是 v0.12 的转折点。
v0.12 原本只是兼容提醒版本
v0.12 的原始定位并不激进。
它原本只是 v1.0 之前的兼容提醒版本。
一路走来,插件增加了很多能力:provider-level 控制、正则过滤、自定义 endpoint、LiteLLM 元数据、models.dev 补全、/connect 凭据支持。
能力变多之后,配置模型也不可避免地积累了一些历史包袱。
其中最典型的,就是 plugin-level 全局 discovery 配置。
早期全局配置是合理的。那时插件还更像一个统一的“发现开关”:启用 discovery、过滤 provider、过滤模型。用一个全局配置控制所有 Provider,简单直接。
但当 Provider 数量变多之后,这个边界开始变得不自然。
不同 Provider 可能有不同的 models endpoint,不同的模型过滤规则,不同的 metadata 来源,不同的认证方式。此时 discovery 规则更应该贴近 Provider 自身,而不是散落在插件全局配置里。
所以 v1.0 会淘汰 plugin-level 全局配置,把配置边界收敛到:
provider.<name>.options.modelsDiscovery
对应地,v0.12 需要承担两个职责:
- 提醒用户旧配置即将废弃。
- 帮助用户把旧配置迁移到新的 provider-level 配置模型。
原计划:写一个程序自动迁移
一开始,我设想的是写一个单纯的程序来完成配置迁移。
它大概会这样工作:
检测旧的 plugin-level 配置。
找到 `opencode.json`。
生成 JSON patch。
把 对应的配置 等字段移动到Provider 的 option 配置。
这看起来很直接。
但开发越深入,这个方案的问题越明显。
OpenCode 配置并不总是只有一个文件。它可能来自:
项目根目录的 opencode.json 或 opencode.jsonc
也可能来自 .opencode/opencode.json
用户全局配置
OPENCODE_CONFIG环境变量
甚至还有 managed 或 organization-controlled config。
更复杂的是,迁移不只是字段搬运,还涉及语义判断:
旧的 providers.include 应该如何等价迁移?
已有 provider-level option 能不能覆盖?
用户的注释和 JSONC 格式要不要保留?
某些配置文件是不是根本不应该动?
如果 Provider 不在当前可编辑配置里,应该猜测还是停止?
如果把这些情况全部硬编码进插件,迁移工具会越来越像一个脆弱的配置编辑器。
这并不是一个好方向。
后来我意识到:这是一个非常适合自举的场景
随着对 LLM 应用理解得更深入,我发现配置迁移其实并不一定要由插件自己硬编码完成。
插件可以自描述。
它可以告诉 OpenCode 和 LLM:
我的旧配置字段有哪些。
我的新配置边界是什么。
哪些配置文件应该检查。
哪些 managed config 不应该直接修改。
迁移时哪些字段可以移动,哪些已有字段不能覆盖。
如果不能安全迁移,应该向用户解释阻塞点,而不是强行修改。
然后,由 OpenCode 中的 LLM assistant 在真实项目上下文里完成配置迁移。
换句话说,插件不再只是运行一段固定逻辑,而是把“如何配置自己”描述给 LLM,让 OpenCode 与 LLM 相互驱动来完成本插件的配置。
这就是“LLM 的自举配置”。
v0.12 做了什么
v0.12 最终没有内置一个强行改文件的迁移器,而是引入了两个 helper commands:
/models-discovery:config
/models-discovery:migrate
/models-discovery:config 会在插件加载时注入,用于日常配置。
当用户想设置 provider-level discovery、开启 metadata enrichment、为某个 provider 禁用 discovery,或者检查当前配置是否合理时,可以使用这个命令。
它会引导LLM assistant 使用 OpenCode 的 customize-opencode skill,检查项目配置、用户全局配置和 OPENCODE_CONFIG环境变量,再根据用户意图修改配置。
/models-discovery:migrate 只在检测到存在plugin-level 全局配置选项时注入。
它的目标是迁移旧配置,而不是简单字符串替换。它会引导 assistant 理解当前 OpenCode 的配置上下文,理解 opencode-models-discovery 插件的配置规则,如果还有plugin-level 的配置,则与用户交互,确认用户的意图,执行相关迁移。
同时,v0.12 仍然保持兼容:旧的 plugin-level 全局配置 在 0.12.x 中继续工作,只是会收到 warning、toast 和迁移入口。
这很重要。过渡版本不应该突然破坏用户配置。
这不是简单的“加两个命令”
表面看,v0.12 只是多了两个 command。
但背后的理念变化更大。
过去插件的能力是:我帮你发现模型。
现在插件开始具备另一种能力:
你需要配置插件,那我来帮你;
我发现你正在使用即将废弃的配置方式,我会告诉你风险,并给你一个由 assistant 执行的迁移入口。
这和传统 CLI 工具的 migration command 不一样。
传统 migration command 往往假设配置文件格式固定、位置固定、语义固定,一经上线,逻辑固定。
我们现在放弃这种形式,让 opencode 背后的 LLM 协助最终用户,按用户的意愿以及插件的运行规则,来实现配置。
所以 v0.12. 的选择是:
插件负责检测 deprecated config。
插件负责发出 warning 和 toast。
插件负责注入 migration/config helper command。
LLM assistant 负责在用户授权和上下文中完成实际编辑。
这是一个更符合 OpenCode 这种 LLM 生态工具的迁移方式。
自举不只用于迁移
迁移只是第一个场景。
一旦插件可以用自描述的方式告诉 LLM “我应该如何被配置”,日常配置也可以进入同一个流程。
比如:
给 LM Studio 开启 discovery。
给 DeepSeek 指定非标准 `/models` endpoint。
给 LiteLLM 配置 modelInfoEndpoint和modelInfoFormat。
给 CLI Proxy 开启 modelInfoFormat: "models.dev"。
给某个 provider 禁用 models 发现。
检查旧配置是否还存在。
解释为什么配置改完后需要重启 OpenCode。
这意味着配置不再只是用户读文档、手动复制 JSON。
插件可以把自己的配置知识带进 OpenCode,让 LLM 在上下文中协助用户完成配置。
写给 v1.0 之前
v0.12 是过渡版本,不是终点。
在 0.12.x 中,旧的 plugin-level discovery config 仍然继续工作。
但 v1.0 会确立新的配置边界:
plugin-level global discovery config 将被移除。
discovery 默认仍然保持启用,除非某个 provider 显式禁用。
单个 provider 使用modelsDiscovery.enabled = false 禁用 discovery。
后续计划支持用环境变量配置,让未显式配置的 provider 默认不启用 discovery。
v1.0 的目标不是让配置更复杂,而是让配置边界更自然。
OpenCode Provider 决定 provider 是什么,modelsDiscovery 决定这个 provider 如何发现模型。
而我们通过 v0.12实现配置自举的意义在于,它没有把迁移压力全部丢给用户,也没有让插件变成一个脆弱的配置重写器。
它选择了第三条路:让配置从“用户独自读文档”,变成“插件、OpenCode 和 LLM 一起协作”。
这也是我认为 v0.12 是走向 v1.0 前最重要版本的原因。它把下一阶段的方向提前暴露出来:
未来的软件,不只是运行代码,也会帮助用户理解和维护自己、改进自己。
AI LLM 时代的软件,理应具备自我表达能力。

点击“阅读原文”, 查看Release Note
夜雨聆风