cursor-for-pi0.1.0
Cursor provider for the Pi coding agent. One HTTP/2 Run per turn.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
cursor-for-pi
给 Pi Coding Agent 用的新 Cursor provider。包名和目录名都是 cursor-for-pi(~/code/cursor-for-pi)。请求模型照搬 magpie 的 Cursor 插件:每次对话新建一条 HTTP/2 连接,用完即拆。不要 fork @rahularya01/pi-cursor 再改它的长连接。
状态:0.1.0 可以发到 npm。另一台机器要有 Node.js 22 和 Pi。先确认没有启用 @rahularya01/pi-cursor(两个都注册 provider id cursor,同时加载会冲突),再装:
pi install npm:cursor-for-pi@0.1.0
然后 /login cursor,再 /model cursor/<id>。/cursor.doctor 只报告用的是哪一级凭证,不打印 token。只想试一次、不写进配置:pi -e npm:cursor-for-pi@0.1.0。
发布包里是 src/*.ts,pi.extensions 指向 ./src/index.ts,Pi 用 jiti 加载。不要和已经装的 pi-cursor 一起开。实现按下文做,不要重开「新插件还是 fork」这个决定。
背景
本机已装 @rahularya01/pi-cursor@1.4.39(~/.pi/agent/settings.json 里是 npm:@rahularya01/pi-cursor)。它和 magpie 的 @magpie-community/opencode-cursor-auth@0.2.2 走同一条协议:浏览器 PKCE 登录,进程内用 Node http2 打 Cursor 的 Connect 双向流 POST /agent.v1.AgentService/Run(content-type: application/connect+proto)。两边都不把 cursor-agent 当模型子进程。
体验不一样。pi-cursor 把 HTTP/2 会话跨回合停放(默认 1 小时),工具结果必须写回同一条 Run。连接在上游失效后,要等到 GOAWAY 或 3 分钟静默才发现,然后靠 checkpoint 恢复。界面上就是偶发断开,以及这两句:
Recovered tool output after upstream bridge lossCursor tool continuation was lost because the live upstream bridge is no longer available
magpie 每次 Run 都 http2.connect,5 秒一次空的 client_heartbeat(protobuf 字段 7),结束时 req.close(NGHTTP2_CANCEL) 再 session.close()。工具调用一凑齐就关掉这次 Run,下一次请求带完整历史重连,用稳定的 conversation_id 让 Cursor 把请求送到缓存了 prompt 的那台机器。这套体验更稳。
本机一次会话的证据(不要把 token 写进仓库):
- 生命周期日志
~/.cache/pi-cursor/pi-cursor-lifecycle-65896.jsonl:同一个bridgeKeyd37817a2、同一个convKey从req-12复用到req-16,模型grok-4.7-high。 - 同一份日志里有
wire_drift:conversationCheckpointUpdate字段 38/39,interactionUpdate字段 27。stranding都是 false,不是那次中断的直接原因,但说明 pi-cursor 生成的agent_pb.ts已经落后于线上。 - 断线补水靠
~/.cache/pi-cursor/run-journal/<convKey>.json(24 小时)。新插件不要再做这套 journal。
决定
新做插件,把 magpie 的请求实现搬过来。不 fork @rahularya01/pi-cursor。
原因:偶发断开就是 pi-cursor 的核心(src/stream/native-core.ts 约 2100 行、checkpoint、recovery、run journal、停放的 bridge)。改成 magpie 的行为等于拆掉这些,只留登录和模型列表。上游还在改长连接,之后合并会一直冲突。
Pi 的工具循环本来就是:模型给出工具调用,Pi 执行,下一回合再请求。不需要把连接挂住等工具结果。
目标
在 Pi 里选 cursor/<model> 就能用 Cursor 订阅里的模型聊天、调 Pi 已经注册的工具。连接按次建立、按次销毁,不再出现上面那两句恢复/丢续接错误。
做到这些就算第一版:
- Pi 扩展:
package.json的pi.extensions指向入口,注册 provider idcursor。命令仍是/login cursor、/model cursor/<id>。 - 每次模型请求:新建 HTTP/2 →
AgentService/Run→ 5 秒心跳 → 结束NGHTTP2_CANCEL并关掉 session。不要停放连接,不要persistent: true,不要跨回合openStream。 - 工具:只把调用方(Pi)的工具当 MCP 工具列给 Cursor。模型经
CallDynamicTool发起调用后,插件把它译成 Pi 的 tool call 并结束这次 Run。工具由 Pi 执行,结果进下一回合的完整历史,不写回当前流。Cursor 自带的 shell、读文件、改文件不执行。 - 稳定
conversation_id:有 Pi session 时,对"cursor conversation\0" + sessionId + "\0" + 第一条 user 消息做 SHA-256,取前 16 字节改成 UUID v4。没有 session 名就每次 Run 新 UUID。子 agent 不要和主会话共用同一个 id(加上第一条 user 消息就是为了这个)。 - 区域:先
aiserver.v1.ServerConfigService/GetServerConfig,用返回的agentUrl/agentnUrl;拿不到用https://agentn.global.api5.cursor.sh。错误文案含region时强制再问一次,新地址不同才重跑这一次 Run。不要写死agentn.us.api5.cursor.sh。 - 鉴权从 pi-cursor 借用,不要用 OpenCode 的 auth 存储:
CURSOR_ACCESS_TOKEN- Pi OAuth(
~/.pi/agent/auth.json,/login cursor) - macOS 钥匙串
cursor-access-token - Cursor IDE 的
state.vscdb浏览器登录仍是https://cursor.com/loginDeepControl+ 轮询https://api2.cursor.sh/auth/poll。
- 请求头对齐 magpie,不要对齐 pi-cursor 的自定义头:
content-type: application/connect+protoconnect-protocol-version: 1x-cursor-client-type: clix-cursor-client-version:安装的cursor-agent版本,否则回退2026.09.23-86fc751,前面加cli-x-ghost-mode: truex-cursor-agent-allowed-tools: mcp_tool_call,get_mcp_tools_tool_callx-request-id每次新 UUID
- 协议层用 magpie 那份手写 protobuf(
index.mjs里的pb()),不要生成或复制 pi-cursor 的agent_pb.ts。
不做
- 不做跨回合 HTTP/2 会话、checkpoint、run journal、「上游断了就接着写」的恢复提示。
- 不在同一条 Run 上回工具结果。
- 不执行 Cursor 原生 shell / 读写文件。没有合适的 Pi 工具就失败这次调用,不要本地兜底。
- 不把 magpie 的 OpenAI chat-completions
fetch适配层整份贴进 Pi。Pi 走streamSimple,事件是文本、思考、tool call。只搬请求和解码。 - 不做多账号切换、Responses/Messages API、用量仪表板。用量可以以后再做。
- 不写
.env、不把 access token 写进源码或日志样例。 - 不要求用户卸掉已经装的
@rahularya01/pi-cursor。新包名就是cursor-for-pi,不要用pi-cursor、pi-cursor-runonce或@rahularya01作为包名。两个都注册 provider idcursor时会冲突,默认假设只装这一个,并在 README 里写明。
从哪里拿
| 拿 | 从哪 | 不拿什么 |
|---|---|---|
Run 的组包、解码、心跳、关连接、conversation_id、区域重试、手写 protobuf |
magpie packages/cursor/index.mjs(约 2044 行)。关键函数:open 1513、runOnce 1542、buildRun 1439、conversationID 1186、agentURL 1488、decode 里工具步在凑齐后 close() |
OpenCode 的 auth hook、chat completions 响应格式、dashboard 用量补记(owed / STEP_WAIT)。用量第一版可以缺 |
/login cursor、四级凭证、模型目录怎么注册进 Pi、streamSimple 事件怎么推 |
本机 ~/.pi/agent/npm/node_modules/@rahularya01/pi-cursor,源码仓库 https://github.com/Rahularya01/pi-cursor 。参考 src/stream/stream-writer.ts、登录和 GetUsableModels / AvailableModels |
src/client/h2-session.ts、src/stream/bridge-session.ts、native-core.ts、recovery.ts、session-state.ts、run-journal.ts、生成的 agent_pb.ts |
两个仓库都是 MIT。搬过来的文件头部保留原作者和出处:
- https://github.com/magpie-community/plugins/tree/main/packages/cursor
- https://github.com/Rahularya01/pi-cursor
magpie 这份本身注释写明是从 magpie Go 实现移植的(internal/provider/cursor*.go)。
建议的文件
先保持少数文件,不要照搬 pi-cursor 的 src/stream/ 拆成三十个模块。
package.json name 是 cursor-for-pi;pi.extensions、MIT、node >= 22
src/index.ts 扩展入口:注册 provider、/login、/cursor.doctor
src/auth.ts 四级凭证 + PKCE。只读钥匙串和 state.vscdb
src/run.ts 从 magpie 搬的 open / runOnce / buildRun / decode / agentURL
src/codec.ts 手写 protobuf,从 magpie 的 pb() 搬
src/pi-stream.ts Run 事件 → Pi 的 text / thinking / toolcall
src/models.ts AvailableModels + GetUsableModels
test/ 用录制的帧测解码和「工具凑齐就 close」,不要打真实 Cursor
依赖尽量少。magpie 这份只用 Node 内置模块(node:http2、node:crypto、node:zlib)。不要加 @bufbuild/protobuf,除非后来证明手写编码砌不动某个必须字段。
验收
不用真实账号也能跑的:
- 解码测试:文本、thinking、
CallDynamicTool凑齐后迭代器结束,且调用了NGHTTP2_CANCEL。 - 一次 Run 里不出现工具结果帧。
conversation_id对同一个 session + 同一条首条 user 消息稳定,换首条消息就变。- 区域错误:URL 变了才第二次
runOnce,没变就不重试。 - 原生 shell/read/write 的 exec 不产生 Pi tool call,也不在本地执行。
有账号再手工看一次:/login cursor,/model cursor/<一个账号里有的模型>,普通回复、一次工具调用、工具结果后的下一回合。连续几轮不应再出现 bridge loss。
实现顺序
- 搬
codec.ts和run.ts,用假 HTTP/2 把解码和关连接测绿。这一步不要接 Pi。 - 接
pi-stream.ts:一次 Run 的事件变成 Pi 的 assistant stream。工具调用使 stop reason 为 tool use,然后流结束。 - 接登录和模型列表,能在 Pi 里选中模型。
- 手工跑一轮真实对话。不要为了对齐 pi-cursor 再加回 checkpoint。