mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-17 19:47:10 +08:00
88 lines
4.8 KiB
Markdown
88 lines
4.8 KiB
Markdown
# cursor-byok
|
||
|
||
`cursor-byok` 是一个基于真实 Cursor Agent 流量与 protobuf 实现的自托管服务端,用于把 Cursor 客户端接入用户指定的 LLM Provider。
|
||
|
||
当前 Rust 服务 `cursor-server` 已实现:
|
||
|
||
- Cursor `RunSSE + BidiAppend` 双向协议与 Connect envelope。
|
||
- OpenAI Chat、OpenAI Responses、Anthropic 三种无状态流式端点。
|
||
- `LLM → 客户端工具 → 结果提交 → 下一轮 LLM` 的通用 Loop。
|
||
- append-only canonical messages、不可变 revision 分支和同对话 Run 抢占。
|
||
- Cursor typed tool UI、Exec/Interaction、多阶段编辑、MCP 与子代理。
|
||
- Blob CAS、KV GET/SET ACK、两阶段 checkpoint 和 pending ToolRound 恢复。
|
||
- 未匹配 Cursor backend 路由原样流式转发到上游。
|
||
- React + Vite + TypeScript + Tailwind 管理台,支持 Provider 配置、模型发现和调用明细。
|
||
- 每次 Provider 调用的时间、模型快照、状态和 authoritative usage;详细模式保存脱敏请求与原始流响应。
|
||
|
||
启动方式见 [cursor-server/README.md](./cursor-server/README.md)。协议证据见 [Cursor上下文与状态同步抓包分析.md](./Cursor上下文与状态同步抓包分析.md),当前实现约束见 [一次性重构计划计划.md](./docs/一次性重构计划计划.md)。
|
||
|
||
## 核心数据流
|
||
|
||
```text
|
||
HTTP / Connect
|
||
↓
|
||
Cursor adapter
|
||
↓ ClientCommand / ClientEvent
|
||
RunEngine
|
||
↓
|
||
canonical messages + selected revision
|
||
↓
|
||
typed ModelRequest
|
||
↓
|
||
Provider adapter → HTTP/SSE → ModelEvent
|
||
```
|
||
|
||
Loop 不依赖 Cursor protobuf、Blob、checkpoint、数字 wire id 或具体 Provider JSON。Cursor adapter 和 Provider adapter 只在各自边界做协议投射。
|
||
|
||
## 状态与 checkpoint
|
||
|
||
- SQLite 中的 immutable messages 与 revision 父链是对话事实源;回滚只选择旧 revision 并建立新分支。
|
||
- ToolRound 保存完整 assistant、原始 call 顺序和真实 result 完成顺序;结果未齐时不会把悬空 tool call 投给下一轮模型。
|
||
- BlobID 是 `SHA-256(data)`;Blob 类型来自引用字段,不编码在 ID 中。
|
||
- checkpoint 引用的新 Blob 必须先收到对应 KV SET ACK。协议中不存在 checkpoint ACK,也不保存跨流 outbox。
|
||
- staged checkpoint 内联完整 pending assistant;ToolRound 全部结果提交后才折叠进 stable roots。抓包没有单 ToolResult checkpoint,因此实现也不制造该状态。
|
||
- settled checkpoint 必须先于下一轮 LLM 调用。最终文本轮严格发送 `turn_ended → staged → settled → settled 重发 → EndStream`。
|
||
- Cursor.app 只把 `turn_ended` 前的 checkpoint 作为自动恢复候选;恢复 pending assistant 时先继续工具,不重复调用 LLM。
|
||
|
||
## 目录边界
|
||
|
||
```text
|
||
cursor-server/src/
|
||
├── control/ # 客户端无关的 Provider、模型、调用观测 HTTP API
|
||
├── client/ # 所有客户端共用的最小 command/event port
|
||
├── model/ # canonical message、revision、ModelSpec、typed history
|
||
├── run/ # 协议无关 Loop、ModelCycle、ToolRound 和 RunRegistry
|
||
├── provider/ # 各 LLM 端点请求与流事件适配
|
||
├── cursor/
|
||
│ ├── request/ # RunRequest → PreparedRun/CursorRunContext
|
||
│ ├── prompting/ # Cursor prompt、工具 catalog 和 mode manifest
|
||
│ ├── projection/ # Cursor AI-SDK stable/pending JSON 编解码
|
||
│ ├── interaction/ # UI 更新、InteractionQuery、typed ToolCall 渲染
|
||
│ ├── tools/ # Cursor 工具 transport、runtime、dispatch 和 result
|
||
│ └── checkpoint/ # root/Turn/derived/recovery 与串行 worker
|
||
└── store/ # SQLite messages、revision、ToolRound、Run 和 Blob CAS
|
||
|
||
console/ # React 管理台;只依赖 control API,不依赖 Cursor protobuf
|
||
```
|
||
|
||
详细到文件的目标目录和验收项只在重构计划中维护,README 不复制第二份易漂移的完整文件清单。
|
||
|
||
## 工程原则
|
||
|
||
- 不保留旧路径、兼容层或失败后的隐式 fallback。
|
||
- 同一状态只有一个所有者;协议层不做 Loop 决策。
|
||
- PromptSpec、ModelSpec 和 selected revision 决定可重放的 ModelRequest;request id、时间和 model call id 不进入模型输入。
|
||
- 前缀稳定限定在相同 PromptSpec/ModelSpec/Provider route;新 Run 切换模型或模式时只替换 Cursor system root,其他历史 message roots 继续复用。
|
||
- Provider replay state 只回传给产生它的端点;可展示 thinking 不是跨端点 reasoning 字段。
|
||
- Provider usage 只采用端点报告的单轮最终值,不自行估算。
|
||
- 同一个取消信号覆盖等待 HTTP 响应头和读取 SSE 两段。
|
||
|
||
## 验证
|
||
|
||
```bash
|
||
cd cursor-server
|
||
cargo fmt --check
|
||
cargo clippy --all-targets -- -D warnings
|
||
cargo test --all-targets
|
||
```
|