# qunce-cursor
**Repository Path**: zhuangsq/qunce-cursor
## Basic Information
- **Project Name**: qunce-cursor
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-07-05
- **Last Updated**: 2026-07-23
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# QunceApi
QunceApi 是 Qunce 服务的桌面客户端。用户直接在 App 内按 NewAPI 当前开放的注册方式创建账号并登录;登录后 App 为当前设备取得独立的 `sk-...` Token 并保存到当前系统用户的私有凭据文件 `~/.qunce-cursor/credentials.json`。Cursor 请求经过本地 Worker 后进入统一域名,渠道、上游地址及密钥都不会暴露给用户。
## NewAPI 与 Qunce Extension
服务端按职责拆分:
- NewAPI 负责渠道、模型、定价、订阅、额度、`/v1/*` 转发和用量日志。
- [`platform/`](platform/) 是独立 Qunce Extension,负责商品、套餐码、账号设备设置和 Worker/APP 发布。
- 管理页面统一位于 NewAPI,Qunce Extension 不再提供独立后台、管理员账号、模型网关或计费账本。
生产统一域名把 `/v1/*` 转发到 NewAPI,把 `/api/qunce/*` 转发到 Qunce Extension。完整架构和升级边界见 [`platform/docs/NEWAPI_MIGRATION.md`](platform/docs/NEWAPI_MIGRATION.md),可直接运行的部署模板见 [`deploy/unified/`](deploy/unified/)。
本地启动:
```bash
npm run platform:db
npm run platform:dev
```
Extension 地址默认为 `http://127.0.0.1:8088`。首次运行前根据 [`platform/.env.example`](platform/.env.example) 创建被 Git 忽略的 `platform/.env`,并配置 NewAPI Root 服务凭据。完整说明见 [`platform/README.md`](platform/README.md)。
当前主线:
- 原 API Worker 数据面经本地补丁后监听 `127.0.0.1:28623`
- Qunce Go 控制面默认监听 `127.0.0.1:28624`
- 启动时检查 Worker,之后每 12 小时检查一次更新,并按安装 ID 错峰
- 下载后把 Worker 原账号控制面改为本机 `28624`
- App 复用 NewAPI 注册登录,通过 Qunce Extension 为每台设备签发独立 `sk-...` Token;Token 只进入当前用户的私有凭据文件,不写入普通配置
- App 的 API 页面使用同一个统一服务地址管理独立 API Key;一个 Key 可调用账号已开放的 OpenAI、Anthropic 和 Gemini 协议,退出 App 不会撤销。列表默认显示掩码,点击复制时才按当前账号归属读取完整 Key
- 本机控制面向 Worker 返回本地兼容通道和 Cursor 当前选择的模型;本地兼容层使用该 Token 访问统一域名。回退模型保存在 Qunce 客户端设置中,未单独设置时继承后台默认值;模型目录、可用性和实际转发由 NewAPI 负责
- 隔离 HOME 自动生成本机 `.auth`,让无原插件 `x-auth-token` 的 Cursor Agent 也能进入本地控制面
- Worker 模式只改写 Agent 后端地址,不设置全局 `http.proxy`,因此不会触发 Worker HTTPS MITM 证书安装
- 正式 App 不包含证书功能;仓库仅为 `worker.enabled=false` 的原生研究模式保留证书工具
- Cursor `settings.json` 和 `state.vscdb` 自动备份、写入、恢复
- Cursor 私有 RPC 抓包、wire dump、模型目录 replay、Agent Run 实验接管
- GPT/OpenAI-compatible 请求规范化:模型名、tools、`function_call`、`tool_calls`、Anthropic 风格 `tool_use/tool_result`
完整 Worker 链路、更新和回滚见 `docs/WORKER_MODE.md`;原生 Go 适配器仍作为研究/回退实现保留。
## 环境
普通用户必须:
- QunceApi 账号;需要套餐时使用后台生成的 `QC-PKG-...` 套餐码
- 已安装 Cursor
源码开发必须安装 Go 1.25+。Node.js 20+ 只用于仓库开发、打包检查和研究脚本;用户安装后的 macOS/Windows App 和代理本体不依赖 Go 或 Node.js。
## 配置
```bash
cp config.example.json config.local.json
```
关键配置:
```json
{
"listenPort": 28624,
"platform": {
"enabled": true,
"baseUrl": "http://127.0.0.1:8080",
"credentialFile": "~/.qunce-cursor/credentials.json"
},
"worker": {
"enabled": true,
"listenHost": "127.0.0.1",
"listenPort": 28623,
"stateDir": "~/.qunce-cursor/worker",
"updateIntervalSeconds": 600
},
"sub2api": {
"provider": "openai-compatible",
"baseUrl": "http://127.0.0.1:8080/v1",
"defaultModel": "gpt-5.4",
"forceModel": true,
"modelAliases": {
"claude-opus-4-8-thinking-high": "claude-opus-4-8",
"gpt-5.5-medium": "gpt-5.5",
"gpt-5.6-sol-medium": "gpt-5.6-sol",
"gpt-5.6-terra-medium": "gpt-5.6-terra"
}
},
"intercept": {
"openaiCompatiblePathPrefix": "/v1",
"cursorRPC": {
"enabled": true,
"observeOnly": true,
"adapter": {
"enabled": false,
"agentRun": {
"enabled": false
}
}
}
}
}
```
`sub2api` 是 Worker 兼容层沿用的内部配置名,不是用户需要配置的服务。Platform 模式下,程序强制从 `platform.baseUrl` 派生该地址、忽略旧明文密钥,并从当前用户的私有凭据文件读取设备 Token。
下面的原生 Go Agent adapter 配置只在 `worker.enabled=false` 的研究/回退模式中使用:
```json
{
"intercept": {
"cursorRPC": {
"observeOnly": false,
"adapter": {
"enabled": true,
"agentRun": {
"enabled": true
}
}
}
}
}
```
## 启动
桌面 App 和 Windows 控制窗口默认打开后不会自动启动 Worker。点击“启动 Worker”前会先跑启动检查:
- 本机是否存在有效设备授权,且 Qunce Platform 可以访问
- Cursor 可执行文件是否能检测到,或是否已手动配置
- Cursor 用户配置目录是否存在
- Worker 下载、补丁、Node 语法检查和 `/health` 是否通过
```bash
export QUNCE_CURSOR_PROXY_CONFIG="$PWD/config.local.json"
npm run start
```
`npm run start` 只是调用 Go:
```bash
go run ./cmd/qunce-cursor-proxy start
```
后台启动:
```bash
mkdir -p "$HOME/.qunce-cursor"
export QUNCE_CURSOR_PROXY_CONFIG=/absolute/path/to/config.local.json
bash scripts/start-daemon.sh
bash scripts/status-daemon.sh
bash scripts/stop-daemon.sh
```
正式打包必须同时注入统一公网入口和后台下载的 Worker 构建包;统一入口把 `/api/qunce/*` 分流到 Extension,其余请求进入 NewAPI。打包过程会自动执行产物检查:
```bash
QUNCE_PLATFORM_URL=https://platform.example.com \
QUNCE_BUNDLED_WORKER_DIR=/path/to/worker-build-kit \
npm run package:macos
QUNCE_PLATFORM_URL=https://platform.example.com \
QUNCE_BUNDLED_WORKER_DIR=/path/to/worker-build-kit \
npm run package:windows
```
`config.package.local.json` 仍可作为被 Git 忽略的包配置源。打包脚本会强制启用 Platform、由 Platform 地址派生内部网关地址,并删除任何遗留明文 Token。
正式打包还会先验证公开地址已将 `/api/qunce/*` 分流到 Qunce Extension,避免生成新用户无法登录的客户端。只有在已通过其他方式完成同等验证的离线构建环境中,才可显式设置 `QUNCE_SKIP_PLATFORM_PROBE=1` 跳过。
发布前可单独执行同一门禁:
```bash
QUNCE_PLATFORM_URL=https://napi.example.com npm run check:platform-endpoint
```
只用于本机调试、允许本地 HTTP 且不要求内置 Worker 的开发包:
```bash
QUNCE_RELEASE_BUILD=0 npm run package:macos
npm run check:macos-package
```
产物在 `dist/packages/qunce-cursor-macos-universal.zip`。解压后打开 `QunceApi.app`,在 App 内注册或登录账号。注册字段会跟随 NewAPI 当前配置;启用邮箱验证后会自动显示邮箱和验证码。确认 Cursor 路径后先点击一次“一次性准备 Cursor”,再点击“启动服务”。套餐码在额度区域兑换。准备动作与日常启动分离;准备成功后,启动和停止服务都不再申请管理员权限。Worker 模式不需要证书信任。
只有“一次性准备 Cursor”会尝试修改 `/Applications/Cursor.app` 并可能要求管理员授权;如果 Cursor 当时已打开,准备成功后只重启这一次以加载新 Hook,Cursor 原本没开则不会主动打开。普通“启动 Worker”和“停止 Worker”都通过 `~/.codex_cursor` 热切换,不重启 Cursor 进程;已打开的 Cursor 窗口会自动重载一次以切换运行态。Hook 未准备时会直接提示,不会循环弹密码。
默认 Worker 模式只写入协议选择和更新开关:
```json
{
"cursor.general.disableHttp2": true,
"cursor.general.disableHttp1SSE": false,
"update.mode": "none",
"update.enableWindowsBackgroundUpdates": false
}
```
Worker 模式按原插件数据面强制使用 HTTP/1.1:`cursor.general.disableHttp2=true`、`cursor.general.disableHttp1SSE=false`。HTTP/2 Hook 只会建立原代理隧道,不会把 Agent RPC 转给本地 Worker。如需临时覆盖配置文件中的值,仍可使用:
```bash
QUNCE_CURSOR_DISABLE_HTTP2=true bash scripts/start-daemon.sh
```
Worker 模式的“禁用更新”只写入 `update.mode=none` 和 `update.enableWindowsBackgroundUpdates=false`,不改 `product.json`、`package.json` 或 Cursor 展示版本号。原生 Go 回退模式仍保留旧的版本锁实现,但默认不使用。Worker 模式不写入 Cursor 的 OpenAI Key、OpenAI Base URL、通用 HTTP 代理或 `cursorAuth/stripeMembershipType`。Workbench 只在 `~/.codex_cursor` 有效时返回 Pro profile 并开启相关工具补丁;停止后自动重载窗口、恢复 Cursor 原生 profile 和工具门控。一次性多文件 Hook 仍保留,下次启动无需再授权。
默认不注册登录自启。macOS 需要登录时自动启动时使用 `QUNCE_CURSOR_LOGIN_ITEM=1 bash scripts/start-daemon.sh`;Windows 当前生产包默认由用户手动打开 `QunceApi.exe`。
## Windows 安装
Windows 发布包不要求 MSI,也不依赖 PowerShell。解压后双击包根目录的 `QunceApi.exe`,它会准备 `%USERPROFILE%\.qunce-cursor` 运行目录并打开 Wails 图形界面;默认不会自动启动 Worker,也不会注册登录自启计划任务。这个 exe 自带控制界面、Go 控制面和 Worker 管理能力,不再要求用户点击或安装独立后台程序。Windows UI 使用系统 WebView2,界面布局和 macOS App 保持一致。
在窗口里直接注册或登录 QunceApi 账号;注册字段会跟随 NewAPI 当前配置。确认 Cursor.exe 路径后,先点一次“一次性准备 Cursor”,再点“启动服务”。准备动作可能弹出 UAC 并在 Cursor 已打开时重启一次;以后普通启动/停止只通过 `%USERPROFILE%\.codex_cursor` 热切换,不重启 Cursor、不重复弹 UAC。正式界面不提供证书管理或旧本地控制页入口。
如果 Cursor 装在 `C:\Program Files`,Windows 可能需要管理员权限才能写入 Cursor Hook。控制窗口只会在“一次性准备 Cursor”遇到权限不足时自动弹出 UAC;授权失败时再右键 `QunceApi.exe` 选择“以管理员身份运行”。
构建 Windows 发布包:
```bash
npm run package:windows
npm run check:windows-package
```
产物在 `dist/packages/qunce-cursor-windows-amd64.zip`。解压后双击 `QunceApi.exe` 即可打开 UI。代理本身不依赖 Cursor 安装目录;要让 Cursor 真正接入,必须写到 Cursor 用户数据目录里的 `settings.json` 和 `state.vscdb`。Cursor 可执行文件路径会先自动检测系统 `PATH`、常见安装目录、注册表、开始菜单/桌面快捷方式、WinGet 和 Scoop 路径;如果你的 Cursor 在 `%LOCALAPPDATA%\Programs\cursor\_\cursor.exe`、`C:\Program Files\Cursor\Cursor.exe` 或其他非默认目录,优先在本地控制窗口保存。
## GPT 适配
代理会在 `/v1/*` 和 Cursor Agent adapter 调用上游前做 OpenAI-compatible 规范化:
- `model: "auto"` 或空模型改成 `sub2api.defaultModel`
- `forceModel: true` 时忽略 Cursor 请求携带的模型,所有请求统一使用用户在 QunceApi 选择的 `defaultModel`
- NewAPI 明确返回 `model_not_found`、模型未配置或没有可用渠道时,使用 `defaultModel` 跨模型回退一次;鉴权、余额、限流、普通超时及其他上游错误不会触发回退
- NewAPI 仍负责同一模型的多渠道容灾,Qunce 只在同模型已经无路可用后执行跨模型回退,且原模型与 `defaultModel` 相同时不会重试
- 研究原始 Cursor 模型路由时可以临时设为 `false`;正式 App 固定使用 `true`
- 原 Worker 会给部分 Cursor 模型附加推理档位(例如 `gpt-5.5-medium`、`claude-opus-4-8-thinking-high`);`modelAliases` 在 Qunce 本地兼容层中将它转换成 NewAPI 实际发布的模型 ID。Claude 的 `thinking-low/medium/high` 还会转换成对应的 `reasoning_effort`,因此模型名兼容不会丢失思考强度
- `/__admin/models` 会返回 Platform 已发布且适合 Cursor 文本/代码链路的模型,并排除图片、音频、embedding、moderation、review 等类型。普通用户只从 App 查看可用模型,不配置模型上游;是否能完整执行 Cursor 工具链以 Cursortest 或手动 Agent 流程为准。
- `/chat/completions` 上的 Responses API `input` 请求会转成标准 `messages`
- Responses API 专用的 `store`、`previous_response_id`、`truncation` 和 `max_output_tokens` 会在转换后清理或映射
- Responses API 风格的 `include`、`reasoning`、`text` 会为 Chat Completions 清理;其中 `reasoning.effort` 映射成 `reasoning_effort`,`text.verbosity` 映射成 `verbosity`
- 裸 `{name, description, input_schema}` tools 转成标准 OpenAI `tools[].type="function"`
- 老式 `functions/function_call` 转成 `tools/tool_choice`
- Anthropic 风格 `assistant.content[].type="tool_use"` 转成 OpenAI `assistant.tool_calls`
- Anthropic 风格 `user.content[].type="tool_result"` 转成 OpenAI `role:"tool"`
- `tool_calls` 自动补 `id`、`type:"function"` 和参数 JSON 字符串
- `reasoningContent` 规范化为 `reasoning_content`
- `file_path` 规范化为 `path`
代理也会在 `/v1/chat/completions` 返回给客户端前规范化 JSON 和 SSE 响应:
- 老式 `finish_reason: "function_call"` 改成 `tool_calls`
- 老式 `message.function_call` / `delta.function_call` 改成标准 `tool_calls`
- Responses API `output` / `output_text` 响应会转成标准 Chat Completions `choices`
- Responses API SSE 事件里的 `response.output_text.delta`、`response.output_item.done`、`response.function_call_arguments.delta/done`、`response.completed` 可被代理和 Agent adapter 解析
- Anthropic 风格响应块里的 `tool_use` 改成标准 `tool_calls`
- `...` 文本片段拆成 `reasoning_content`
- 响应里的 `file_path` 参数同样规范化为 `path`
Cursor Agent adapter 在 `RunSSE + BidiAppend` 会话里会给 GPT 提供一组已确认可走 Cursor 原生 exec 的工具 schema:`read_file`、`list_dir`、`grep_search`、`edit_file`、`delete_file`、`run_terminal_cmd`。直接 `AgentService/Run` 没有工具结果通道,因此不会暴露工具。
工具循环当前有两条结果通道:
- 兼容通道:`ToolCallEventService/SubmitToolCallEvents` 里的工具结果会回填到下一轮 OpenAI-compatible `role:"tool"` 消息。
- Cursor 原生通道:GPT 返回 function call 后,代理会向 Cursor 客户端发 `AgentServerMessage.exec_server_message`,把常见工具映射为 Cursor 原生 `ExecServerMessage`;客户端随后通过 `BidiAppend` 回传 `ExecClientMessage`,代理解析后继续下一轮模型调用。
当前原生映射范围:
- `read_file` -> `ReadArgs`
- `list_dir` -> `LsArgs`
- `grep_search` -> `GrepArgs`
- `file_search` -> 暂未暴露为原生 exec;Cursor 8.9.16 里只确认到 `GlobToolCall` 记录结构,未确认 `ExecServerMessage` oneof
- `run_terminal_cmd` -> `ShellArgs`,会补 `simple_commands` 和 `parsing_result`
- `delete_file` -> `DeleteArgs`
- `edit_file` -> 全量 `content/full_content` 走 `WriteArgs`;定点 `old_string/new_string` 暂未确认原生 exec oneof,因此不再主动暴露给 GPT
## Cursor RPC 研究
默认识别这些 Cursor 私有 RPC host:
```text
api.cursor.sh
api2.cursor.sh
api3.cursor.sh
agentn.global.api5.cursor.sh
cursor.sh
```
默认识别这些路径前缀:
```text
/aiserver.v1.
/agent.v1.
```
抓包目录:
```text
~/.qunce-cursor/cursor-rpc-captures
```
`manifest.jsonl` 记录 direction、host、path、service、method、adapter、状态码、长度、sha256 和 body 文件路径。本地 adapter 生成的 Agent Run / RunSSE / BidiAppend / ToolCallEvents 响应也会写入同一个 manifest;`adapter` 字段用于区分它们和 Cursor 上游原始响应。`.bin` 是原始私有 RPC body,可能包含提示词、文件路径或上下文摘要,不要提交到仓库。
查看最近捕获:
```bash
bash scripts/status-daemon.sh
curl http://127.0.0.1:/__admin/cursor-rpc-captures
```
解析 wire:
```bash
npm run rpc:dump -- --method BidiAppend --limit 5
npm run rpc:dump -- --direction response --method AvailableModels --limit 3
npm run rpc:dump -- --method Run --direction request --limit 1 --format agent-run --max-depth 8 --max-fields 300
npm run rpc:dump -- --method RunSSE --direction response --limit 1 --format agent-run --max-depth 10 --max-fields 800
npm run rpc:dump -- --method BidiAppend --direction request --limit 1 --format agent-run --max-depth 10 --max-fields 800
```
`--format agent-run` 会摘要解析 Agent Run、RunSSE、BidiAppend 里的常见消息,包括 `interaction_update`、`exec_server_message`、`exec_client_message`、`kv_server_message`、`kv_client_message`、`conversation_checkpoint_update` 和 exec 控制消息。真实 Cursor 工具联调时,可以用它确认代理是否向客户端发出了原生工具请求、客户端是否通过 BidiAppend 回传了执行结果,以及是否出现 KV blob/检查点重放。对本地 adapter 的 RunSSE 响应,capture 保存的是可解析的 Connect proto 逻辑帧;即使客户端实际通过 SSE 接收,仍可用同一条 `rpc:dump --format agent-run` 命令解码。
`rpc:dump` 的字符串预览默认会脱敏常见账号标识、token、key、cookie、secret 和高熵长字符串;原始 `.bin` 捕获文件仍可能包含敏感内容,不要提交或外传。
Live 探针:
```bash
npm run probe:agent-run
```
`probe:agent-run` 会按 `--port`、`QUNCE_CURSOR_PROXY_PORT`、当前配置、`~/.qunce-cursor/config.local.json`、旧版 `~/.qunce-cursor/app/config.local.json` 的顺序自动推断端口。需要强制指定端口时再用:
这个探针会真实打到当前配置的 OpenAI-compatible 上游,覆盖 `AgentService/Run`、`RunSSE + BidiAppend`、`SubmitToolCallEvents` 空 ack,以及一次 `read_file` 原生工具 loop。原生工具 loop 会用 `exec-1` 回传模拟 Cursor 客户端执行结果,用来验证工具结果能映射回上游 `tool_call_id` 并进入第二轮模型调用。
```bash
npm run probe:agent-run -- --port 28623
```
真实 Cursor UI 回归优先跑 `cursortest` 自动脚本:
```bash
npm run test:cursortest
```
默认只跑轻量 `read_file` 场景。它会在 `/Users/zsq/Desktop/cursortest/tmp` 写入唯一 marker 文件,自动把提示发送到 Cursor Agent,轮询 `diagnose:ui --assert-success --require-tool-loop`,断言匹配响应里出现预期 Cursor native tool,再等待一轮 storage settle 并断言 OpenAI-compatible/BYOK 缓存仍指向本地代理。
完整工具链回归可跑:
```bash
npm run test:cursortest -- --scenario all
```
`--scenario` 支持 `read`、`list`、`grep`、`shell`、`edit`、`delete`,也可以用逗号组合,例如 `--scenario read,grep,edit`。输出里的 `ui.nativeTools` 和 `ui.nativeToolCalls` 会列出本轮 RunSSE 响应实际发给 Cursor 客户端的原生工具请求,例如 `read`、`ls`、`grep`、`shell`、`write`、`delete`。需要手动发送提示时可用:
```bash
npm run test:cursortest -- --no-send
```
手动在 Cursor 里验证工具执行时,底部模式要选 `Agent`。`Plan` 模式可能只显示 “Planning next moves” 并停在规划,不一定进入 `RunSSE + BidiAppend` 工具执行链路。默认 `sub2api.forceModel=true`,Cursor UI 选择的模型不会改变实际路由;所有请求统一使用用户在 QunceApi 选择的模型,该模型下架时客户端会切换到后台默认模型。
自动发送时,脚本默认只在诊断明确为 `cursor-ui-did-not-enter-agent-flow` 时重发一次提示,避免 Cursor 焦点偶发没有落到 Agent 输入框。它不会重试 adapter、上游、工具映射或文件副作用失败;需要调整时可用:
```bash
npm run test:cursortest -- --send-retries 1 --no-flow-retry-ms 15000
```
发送快捷键默认保持已验证能进入 Agent flow 的 `cmd-shift-l`。如果要对比 Cursor 当前版本的 Chat/Agent 入口,可显式指定其他快捷键:
```bash
npm run test:cursortest -- --scenario read --send-shortcut cmd-l
```
支持值:`cmd-shift-l`、`cmd-l`、`cmd-i`、`cmd-shift-i`。是否适合做默认值必须以 `agentFlowEntriesAfterSince`、`nativeTools` 和文件副作用结果为准。
当前只把非默认值当作实验对照入口;如果诊断返回 `cursor-ui-did-not-enter-agent-flow`,说明提示没有真正提交到 Agent 链路。
手动真实 Cursor UI 联调前后也可以用诊断脚本确认请求是否进入本地 adapter:
```bash
wc -l < ~/.qunce-cursor/cursor-rpc-captures/manifest.jsonl
# 在 Cursor Agent UI 里发起一次请求后,把上一行行数填进 --since-line
npm run diagnose:ui -- --since-line
```
`diagnose:ui` 只输出守护进程、Cursor 代理设置、会员类型、manifest 最近 Agent RPC/adapter 响应摘要,以及 `proxy.log` 里的工具链计数和最近 session 摘要,不打印 token、原始 `.bin` 内容或历史对话标题/摘要。如果 UI 弹 usage limit 且 `agentFlowEntriesAfterSince` 为 `0`,说明请求没有进入真实 `Run/RunSSE/BidiAppend/ToolCallEvents` flow;如果 broad `agentRPCEntriesAfterSince` 有值但 `agentFlowEntriesAfterSince` 为 `0`,通常只是 AgentService 元数据请求,不代表提示已提交。工具链正常时应能看到工具结果批次、`agent-tool-loop`、第二轮上游调用和 `RunSSE` adapter 响应;等待卡住时重点看 `runSSEAdapterTimeouts`、`upstreamErrors`、`latestWaitDuration`、`latestUpstreamDuration` 和 `latestError`。
只想看短诊断报告时加 `--report-only`:
```bash
npm run diagnose:ui -- --since-line --assert-success --require-tool-loop --report-only
```
短报告会输出 `cause`、失败断言、Cursor storage 状态、flow 计数、最近 session、匹配到的 RunSSE capture 行号/sha 和 native tool 摘要;`test:cursortest` 超时时也会优先打印这份报告。
真实 UI 回归建议使用一个明确标记,先在 Cursor Agent UI 输入:
```text
Use tools to read package.json. Reply exactly one line: QUNCE_REAL_UI_OK name=. Do not modify files.
```
提交后执行断言模式:
```bash
npm run diagnose:ui -- --since-line --assert-success --require-tool-loop \
--expect-text QUNCE_REAL_UI_OK \
--expect-native-tool read
```
这条命令会在 JSON 里输出 `assertions`,并在不满足成功标准时返回非 0。成功标准包括:真实 UI 请求进入 `AgentService/BidiAppend`、本地 adapter 返回 `RunSSE`、上游无错误、无等待超时、Cursor 工具结果进入 `agent-tool-loop`、第二轮上游调用完成,能在 `since-line` 之后的 `RunSSE` adapter 响应里匹配到预期标记,并且匹配响应里出现期望的 Cursor native tool。它只输出匹配响应文本的字节数和 sha256,不直接打印完整模型文本;工具证据会以摘要形式放在 `assertions.runSSETextSearch.nativeTools` 和 `nativeToolCalls`。
工具断言支持两种形式:
```bash
# 必须出现 read 和 write
--expect-native-tool read --expect-native-tool write
# 删除类回归必须出现 delete 原生工具,不再接受 shell 兜底
--expect-native-tool delete
```
文件增删改类回归不要只看模型回复,建议同时断言本地副作用:
```bash
npm run diagnose:ui -- --since-line --assert-success --require-tool-loop \
--expect-text CURSORTEST_DELETE_DONE \
--expect-file-missing /absolute/path/to/tmp/delete-me.txt \
--expect-file-contains /absolute/path/to/tmp/result.txt::EXPECTED_MARKER
```
需要确认 Cursor 的 OpenAI-compatible/BYOK 缓存仍指向本地代理时,加:
```bash
npm run diagnose:ui -- --require-cursor-openai-storage
```
临时代理烟测:
```bash
npm run smoke:agent-run
```
## macOS App
调整 Wails 前端样式时启动热更新开发版:
```bash
npm run dev:macos-wails-ui
```
命令会构建并打开 `dist/QunceApi-Wails.app`,随后监听 React 前端源码。修改 `cmd/qunce-cursor-wails/frontend/src` 下的 TypeScript、TSX 或 Tailwind CSS 后,Vite 会重新生成嵌入资源,当前 App 窗口自动刷新;退出 App 后监听进程也会结束。桌面 UI 统一使用 React、TypeScript、Vite、Tailwind CSS、shadcn/ui 风格组件、Radix UI 和 Lucide React。
构建 macOS Universal Wails 客户端:
```bash
bash scripts/build-macos-app.sh
open "dist/QunceApi.app"
```
macOS 和 Windows 共用同一套 UI 和 Go 业务层。App 内置本地控制面、代理运行时和已签名保底 Worker,不再打包独立 Swift 主程序或 Shell 运行层。登录后可在 Cursor 和 ChatGPT 之间切换:概览、额度和用量是公共页面;Cursor 下管理回退模型、路径、准备与更新;ChatGPT 下可安全切换 QunceApi 和 OpenAI 官方配置。
注册或登录后,App 为当前设备签发独立 Token;同一设备再次登录会轮换 Key,不影响其他设备和用户自行创建的 API Key。用户可在额度区域兑换套餐码。“退出账号”会先撤销当前设备 Token,再停止服务并删除本机凭据。App 启动时及运行中每 24 小时检查自身更新;完整安装包通过 SHA-256、Ed25519 发布签名和包结构校验后,由临时更新进程替换当前版本并自动重启,启动确认失败时恢复旧版本。“导出脱敏诊断”不包含 Token、设备 ID、地址、路径或请求内容。
## 证书
查看 Root CA:
```bash
npm run ca:info
```
信任 Root CA:
```bash
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "$HOME/.qunce-cursor/certs/root.crt"
```
清理证书文件:
```bash
bash scripts/clear-ca-files.sh
```
证书功能只供 `worker.enabled=false` 的原生 Go 研究/回退模式使用。默认 Worker 模式不需要生成或安装证书。原生模式下,删除本地证书文件不等于移除系统信任,移除系统信任也不等于删除本地私钥。
## 测试
```bash
npm test
npm run smoke:openai
npm run smoke:worker
npm run smoke
```
`smoke:openai` 会启动一个本地 fake OpenAI-compatible 上游和临时代理,真实请求 `/v1/chat/completions`,验证请求侧的 model/tools/function_call/tool_result 规范化,以及响应侧的 `function_call`、Anthropic `tool_use`、`reasoningContent` 和 `file_path` 规范化。
`smoke:worker` 会下载当前公开 Worker,用临时端口和 fake OpenAI-compatible 服务验证 Worker 的内部兼容链路。它属于开发测试,不代表发布包会让用户配置上游。
Platform 客户端和激活恢复另有 Go 单元测试及 PostgreSQL 隔离集成测试;集成测试只在临时 Schema 中写入数据。
构建 Go 代理:
```bash
npm run go:build
```
当前构建目标:
```text
darwin/arm64
darwin/amd64
windows/amd64
```
## 当前边界
- Claude Code、Gemini 等后续 provider 先不接入;当前重点是 GPT/OpenAI-compatible 适配稳定。
- Worker 模式复用其现有 Cursor 私有 RPC 与工具链;`worker.enabled=false` 时才使用仍在研究中的原生 Go 工具兼容层。
- `cursor-rpc-captures` 是本机私有研究数据,可能包含敏感上下文,不要提交或分享原始文件。