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列表中的完整路径
Autodefaultcursor/default
Cursor Grok 4.6cursor-grok-4.6-highcursor/cursor-grok-4.6-high
Composer 2.5composer-2.5cursor/composer-2.5
GPT-5.6 Solgpt-5.6-sol-mediumcursor/gpt-5.6-sol-medium
GPT-5.6 Terragpt-5.6-terra-mediumcursor/gpt-5.6-terra-medium
Claude Sonnet 5claude-sonnet-5-highcursor/claude-sonnet-5-high
Claude Opus 5claude-opus-5-highcursor/claude-opus-5-high
Claude Fable 5.1claude-fable-5-1-highcursor/claude-fable-5-1-high
Gemini 3.8 Flashgemini-3.8-flash-mediumcursor/gemini-3.8-flash-medium

同一系列通常还有一堆变体,命名规律是后缀:

  • 强度档位:low / medium / high / max / xhigh
  • 思考模式:thinking-*
  • 加速版:*-fast

举几个例子:

  • composer-2.5-fast
  • claude-sonnet-5-thinking-high
  • cursor-grok-4.6-high-fast
  • gemini-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-oauthopencode plugin ... -g
must default export an object with server()OpenCode 1.18 与旧插件不兼容同上,换 playwo 包
登录成功但模型列表没有 Cursormodels 为空,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

插件仓库与包:

十、最短操作清单

  1. opencode plugin @playwo/opencode-cursor-oauth -g -f
  2. 编辑 opencode.jsonc:写上 plugin + cursor provider + 静态 models
  3. opencode auth login --provider cursor 并完成浏览器授权
  4. opencode models cursor 确认列表
  5. 如需安静终端:改缓存里的 logger.js
  6. 重启 OpenCode,选 cursor/... 模型开始用

回过头看,三个坑其实都指向同一件事:OpenCode 的 provider 与插件加载机制,和直觉不一致。 模型列表不读动态目录、插件不从你安装的位置加载——理解了这两点,配置过程会顺畅很多。

祝玩得开心。用得顺手的话,也别忘了给插件作者点个 star。

上一篇
Jev 模型使用教程:把「决策」做成一次函数调用
下一篇
Rust构建的AI智能体编排框架 - thClaws
余白

评论与来信

已通过审核的评论共 0 条。
还没有公开评论

如果正文触发了新的想法,可以把第一封留言写在右侧;提交后会先进入审核。

留言

写下你的想法

提交后进入审核队列,通过后显示于左侧。