Cursor 的订阅里躺着一堆模型——Composer、Claude、GPT、Gemini、Grok——但它们被锁在编辑器里,出不来。想在 OpenCode 里直接调用,官方并没有提供通用外部 API。
社区插件 @playwo/opencode-cursor-oauth 提供了一条曲线救国的路径。本文是在 macOS + OpenCode 1.18.31 上实测跑通后的完整记录。
风险提示:这是非官方方案,随时可能随 Cursor 或 OpenCode 升级而失效。请自行评估账号风险与用量消耗。
一、前置条件
确认本机环境:
opencode --version # 本文实测 1.18.31
bun --version # 插件运行需要
node --version # >= 18
配置文件通常位于:
~/.config/opencode/opencode.jsonc
如果你只有 opencode.jsonc 而没有 opencode.json,这是正常的——OpenCode 同时认这两种格式,.jsonc 只是允许多写注释。
二、装对插件(第一个坑)
2.1 旧包名会直接报错
网上流传的旧教程大多写的是:
"plugin": ["opencode-cursor-oauth"]
这个包名在 OpenCode 1.18 上会失败,报错信息通常长这样:
Plugin opencode-cursor-oauth must default export an object with server()
或者:
Unknown provider "cursor"
原因很简单:OpenCode 1.18 改了插件的加载约定,旧包没有跟上。
2.2 正确的包名与安装方式
opencode plugin @playwo/opencode-cursor-oauth -g -f
然后在 ~/.config/opencode/opencode.jsonc 中声明:
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
"@playwo/opencode-cursor-oauth"
],
"provider": {
"cursor": {
"name": "Cursor",
"npm": "@ai-sdk/openai-compatible",
"api": "http://127.0.0.1:65535/v1",
"models": {
// 必须静态写入,见第四节
}
}
}
}
api 里的 65535 是个占位地址。真正发起对话时,插件会拉起本地代理并接管请求,所以这个端口号写什么其实无所谓——但字段不能省。
三、OAuth 登录
opencode auth login --provider cursor
成功时会打印一个浏览器登录链接:
Go to: https://cursor.com/loginDeepControl?...
Complete login in your browser.
在浏览器完成授权后,终端会显示 Login successful。
验证一下:
opencode auth list
应该能看到:
cursor oauth
凭证默认保存在 ~/.local/share/opencode/auth.json。
四、模型列表为什么是空的(第二个坑)
这是最让人困惑的一步:明明登录成功了,模型列表里却完全没有 Cursor。
原因是 OpenCode 的一条硬规则——如果 provider.cursor.models 为空,OpenCode 会把这个 provider 整个丢掉。于是:
opencode models cursor
# Error: Provider not found: cursor
模型选择器里自然也是空的。
有意思的是,插件本身其实能从 Cursor 拉到完整目录(账号侧可能有 200 多个模型),但 OpenCode 的模型列表只依赖配置里的静态条目,不会去读插件拉回来的动态结果。
所以解决办法只有一个:把要用的模型手写进 provider.cursor.models。
五、显示名 ↔ 真实模型 ID 对照
Cursor 界面里看到的名字,和 OpenCode 需要的 ID 不是一回事。常用对照如下:
| Cursor 界面显示名 | OpenCode 模型 ID | 列表中的完整路径 |
|---|---|---|
| Auto | default | cursor/default |
| Cursor Grok 4.6 | cursor-grok-4.6-high | cursor/cursor-grok-4.6-high |
| Composer 2.5 | composer-2.5 | cursor/composer-2.5 |
| GPT-5.6 Sol | gpt-5.6-sol-medium | cursor/gpt-5.6-sol-medium |
| GPT-5.6 Terra | gpt-5.6-terra-medium | cursor/gpt-5.6-terra-medium |
| Claude Sonnet 5 | claude-sonnet-5-high | cursor/claude-sonnet-5-high |
| Claude Opus 5 | claude-opus-5-high | cursor/claude-opus-5-high |
| Claude Fable 5.1 | claude-fable-5-1-high | cursor/claude-fable-5-1-high |
| Gemini 3.8 Flash | gemini-3.8-flash-medium | cursor/gemini-3.8-flash-medium |
同一系列通常还有一堆变体,命名规律是后缀:
- 强度档位:
low/medium/high/max/xhigh - 思考模式:
thinking-* - 加速版:
*-fast
举几个例子:
composer-2.5-fastclaude-sonnet-5-thinking-highcursor-grok-4.6-high-fastgemini-3.8-flash-high
六、推荐配置(可直接抄)
把下面整段合进你的 opencode.jsonc,注意保留原有的其它 provider:
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
"@playwo/opencode-cursor-oauth"
],
"provider": {
"cursor": {
"name": "Cursor",
"npm": "@ai-sdk/openai-compatible",
"api": "http://127.0.0.1:65535/v1",
"models": {
"default": { "name": "Auto", "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"cursor-grok-4.6-high": { "name": "Cursor Grok 4.6", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"cursor-grok-4.6-high-fast": { "name": "Cursor Grok 4.6 Fast", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"cursor-grok-4.6-medium": { "name": "Cursor Grok 4.6 Medium", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"cursor-grok-4.6-low": { "name": "Cursor Grok 4.6 Low", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"cursor-grok-4.6-xhigh": { "name": "Cursor Grok 4.6 Extra High", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"composer-2.5": { "name": "Composer 2.5", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"composer-2.5-fast": { "name": "Composer 2.5 Fast", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-sol-medium": { "name": "GPT-5.6 Sol", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-sol-high": { "name": "GPT-5.6 Sol High", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-sol-low": { "name": "GPT-5.6 Sol Low", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-sol-max": { "name": "GPT-5.6 Sol Max", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-sol-xhigh": { "name": "GPT-5.6 Sol Extra High", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-sol-medium-fast": { "name": "GPT-5.6 Sol Fast", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-terra-medium": { "name": "GPT-5.6 Terra", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-terra-high": { "name": "GPT-5.6 Terra High", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-terra-low": { "name": "GPT-5.6 Terra Low", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-terra-max": { "name": "GPT-5.6 Terra Max", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-terra-xhigh": { "name": "GPT-5.6 Terra Extra High", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gpt-5.6-terra-medium-fast": { "name": "GPT-5.6 Terra Fast", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-sonnet-5-high": { "name": "Claude Sonnet 5", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-sonnet-5-medium": { "name": "Claude Sonnet 5 Medium", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-sonnet-5-low": { "name": "Claude Sonnet 5 Low", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-sonnet-5-max": { "name": "Claude Sonnet 5 Max", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-sonnet-5-thinking-high": { "name": "Claude Sonnet 5 Thinking", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-sonnet-5-thinking-medium": { "name": "Claude Sonnet 5 Medium Thinking", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-sonnet-5-thinking-max": { "name": "Claude Sonnet 5 Max Thinking", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-opus-5-high": { "name": "Claude Opus 5", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-opus-5-medium": { "name": "Claude Opus 5 Medium", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-opus-5-low": { "name": "Claude Opus 5 Low", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-opus-5-high-fast": { "name": "Claude Opus 5 Fast", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-opus-5-thinking-high": { "name": "Claude Opus 5 Thinking", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-opus-5-thinking-medium": { "name": "Claude Opus 5 Medium Thinking", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-opus-5-thinking-max": { "name": "Claude Opus 5 Max Thinking", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-fable-5-1-high": { "name": "Claude Fable 5.1", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-fable-5-1-medium": { "name": "Claude Fable 5.1 Medium", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-fable-5-1-low": { "name": "Claude Fable 5.1 Low", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-fable-5-1-max": { "name": "Claude Fable 5.1 Max", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-fable-5-1-thinking-high": { "name": "Claude Fable 5.1 Thinking", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-fable-5-1-thinking-medium": { "name": "Claude Fable 5.1 Medium Thinking", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"claude-fable-5-1-thinking-max": { "name": "Claude Fable 5.1 Max Thinking", "reasoning": true, "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gemini-3.8-flash-medium": { "name": "Gemini 3.8 Flash", "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gemini-3.8-flash-high": { "name": "Gemini 3.8 Flash High", "tool_call": true, "limit": { "context": 200000, "output": 64000 } },
"gemini-3.8-flash-low": { "name": "Gemini 3.8 Flash Low", "tool_call": true, "limit": { "context": 200000, "output": 64000 } }
}
}
}
}
验证配置是否生效:
opencode models cursor
# 应能看到 cursor/composer-2.5、cursor/cursor-grok-4.6-high 等
然后完全退出并重启 OpenCode,在模型列表里选择对应模型即可。
七、关掉刷屏日志(第三个坑)
调通之后,你会立刻遇到第三个问题:每调用一次 Cursor 模型,终端就被日志刷屏。
[opencode-cursor-oauth] Received Cursor server signal ...
[opencode-cursor-oauth] Handling Cursor chat completion request ...
想改掉它,必须先理解一个关键点:
OpenCode 真正加载的插件,是缓存目录里的那一份,而不是你手动
npm install下来的那份。
真正的路径在这里:
~/.cache/opencode/packages/@playwo/opencode-cursor-oauth/
~/.cache/opencode/packages/@playwo/opencode-cursor-oauth@latest/
只改 ~/.config/opencode/node_modules/... 下的文件不会生效——这是最容易白费功夫的地方。
修改方法
编辑缓存中的 logger.js:
~/.cache/opencode/packages/@playwo/opencode-cursor-oauth/node_modules/@playwo/opencode-cursor-oauth/dist/logger.js
~/.cache/opencode/packages/@playwo/opencode-cursor-oauth@latest/node_modules/@playwo/opencode-cursor-oauth/dist/logger.js
把 writeConsoleLog 改成默认只输出 error:
function writeConsoleLog(level, message, extra) {
// Default: only errors. Set OPENCODE_CURSOR_OAUTH_LOG=info|warn|error to raise verbosity.
const wanted = (process.env.OPENCODE_CURSOR_OAUTH_LOG || "error").toLowerCase();
const rank = { error: 0, warn: 1, info: 2 };
const wantRank = rank[wanted] ?? 0;
const levelRank = rank[level] ?? 2;
if (levelRank > wantRank) return;
const prefix = `[${PLUGIN_LOG_SERVICE}] ${message}`;
const suffix = Object.keys(extra).length > 0 ? ` ${JSON.stringify(extra)}` : "";
if (level === "error") {
console.error(`${prefix}${suffix}`);
return;
}
console.warn(`${prefix}${suffix}`);
}
改完同样需要完全退出并重启 OpenCode。
需要临时恢复详细日志时:
export OPENCODE_CURSOR_OAUTH_LOG=info
注意:
opencode plugin ...重装或缓存刷新后,这个改动可能被覆盖,需要再补一次。
八、常见问题速查
| 现象 | 原因 | 处理 |
|---|---|---|
Unknown provider "cursor" | 插件没加载,或用了旧包 | 换 @playwo/opencode-cursor-oauth 并 opencode plugin ... -g |
must default export an object with server() | OpenCode 1.18 与旧插件不兼容 | 同上,换 playwo 包 |
| 登录成功但模型列表没有 Cursor | models 为空,provider 被丢掉 | 静态写入 provider.cursor.models |
| 有模型但一调用就刷屏日志 | 插件 info 日志 | 改缓存目录里的 logger.js |
| 改了 node_modules 仍刷屏 | 改错路径 | 改 ~/.cache/opencode/packages/... |
只有 .jsonc 没有 .json | 正常 | 继续用 opencode.jsonc |
九、相关路径汇总
配置: ~/.config/opencode/opencode.jsonc
凭证: ~/.local/share/opencode/auth.json
插件缓存: ~/.cache/opencode/packages/@playwo/opencode-cursor-oauth*/
日志: ~/.local/share/opencode/log/opencode.log
插件仓库与包:
- GitHub:https://github.com/PoolPirate/opencode-cursor
- npm:
@playwo/opencode-cursor-oauth
十、最短操作清单
opencode plugin @playwo/opencode-cursor-oauth -g -f- 编辑
opencode.jsonc:写上 plugin + cursor provider + 静态 models opencode auth login --provider cursor并完成浏览器授权opencode models cursor确认列表- 如需安静终端:改缓存里的
logger.js - 重启 OpenCode,选
cursor/...模型开始用
回过头看,三个坑其实都指向同一件事:OpenCode 的 provider 与插件加载机制,和直觉不一致。 模型列表不读动态目录、插件不从你安装的位置加载——理解了这两点,配置过程会顺畅很多。
祝玩得开心。用得顺手的话,也别忘了给插件作者点个 star。
评论与来信
如果正文触发了新的想法,可以把第一封留言写在右侧;提交后会先进入审核。
写下你的想法
提交后进入审核队列,通过后显示于左侧。