opencodev2-cursor 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +161 -3
- package/dist/backends/agent-argv.mjs +12 -0
- package/dist/backends/agent-cli.d.ts +10 -0
- package/dist/backends/agent-cli.js +117 -0
- package/dist/backends/agent-cli.js.map +1 -0
- package/dist/backends/auto-models.d.ts +26 -0
- package/dist/backends/auto-models.js +43 -0
- package/dist/backends/auto-models.js.map +1 -0
- package/dist/backends/checkpoint.d.ts +8 -0
- package/dist/backends/checkpoint.js +28 -0
- package/dist/backends/checkpoint.js.map +1 -0
- package/dist/backends/native/auth.d.ts +20 -0
- package/dist/backends/native/auth.js +145 -0
- package/dist/backends/native/auth.js.map +1 -0
- package/dist/backends/native/backend.d.ts +12 -0
- package/dist/backends/native/backend.js +105 -0
- package/dist/backends/native/backend.js.map +1 -0
- package/dist/backends/native/h2-bridge.mjs +55 -0
- package/dist/backends/native/proto.d.ts +31 -0
- package/dist/backends/native/proto.js +130 -0
- package/dist/backends/native/proto.js.map +1 -0
- package/dist/backends/sdk-runner.mjs +317 -0
- package/dist/backends/sdk.d.ts +30 -0
- package/dist/backends/sdk.js +304 -0
- package/dist/backends/sdk.js.map +1 -0
- package/dist/backends/select.d.ts +4 -0
- package/dist/backends/select.js +15 -0
- package/dist/backends/select.js.map +1 -0
- package/dist/backends/spawn.d.ts +15 -0
- package/dist/backends/spawn.js +64 -0
- package/dist/backends/spawn.js.map +1 -0
- package/dist/backends/stream-json.d.ts +7 -0
- package/dist/backends/stream-json.js +106 -0
- package/dist/backends/stream-json.js.map +1 -0
- package/dist/backends/types.d.ts +72 -0
- package/dist/backends/types.js +15 -0
- package/dist/backends/types.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +118 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin/auth.d.ts +9 -0
- package/dist/plugin/auth.js +126 -0
- package/dist/plugin/auth.js.map +1 -0
- package/dist/plugin/catalog.d.ts +41 -0
- package/dist/plugin/catalog.js +123 -0
- package/dist/plugin/catalog.js.map +1 -0
- package/dist/proxy/server.d.ts +17 -0
- package/dist/proxy/server.js +265 -0
- package/dist/proxy/server.js.map +1 -0
- package/dist/proxy/title.d.ts +4 -0
- package/dist/proxy/title.js +24 -0
- package/dist/proxy/title.js.map +1 -0
- package/dist/proxy/tools.d.ts +4 -0
- package/dist/proxy/tools.js +26 -0
- package/dist/proxy/tools.js.map +1 -0
- package/dist/proxy/translate.d.ts +42 -0
- package/dist/proxy/translate.js +77 -0
- package/dist/proxy/translate.js.map +1 -0
- package/dist/util/log.d.ts +1 -0
- package/dist/util/log.js +27 -0
- package/dist/util/log.js.map +1 -0
- package/dist/util/paths.d.ts +6 -0
- package/dist/util/paths.js +30 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/util/windows.d.ts +28 -0
- package/dist/util/windows.js +123 -0
- package/dist/util/windows.js.map +1 -0
- package/package.json +53 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aenemo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,161 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
# opencodev2-cursor
|
|
2
|
+
|
|
3
|
+
OpenCode V2 plugin that lets OpenChamber (desktop, CLI/web, and VS Code) and OpenCode use a Cursor subscription on Windows, macOS, and Linux. OpenChamber does not have its own provider list: every surface runs OpenCode and reads `opencode.json` from the OpenCode config folder, so one install covers all of them.
|
|
4
|
+
|
|
5
|
+
Source and issues: [github.com/aenemo/opencodev2-cursor](https://github.com/aenemo/opencodev2-cursor)
|
|
6
|
+
|
|
7
|
+
## What you need
|
|
8
|
+
|
|
9
|
+
- OpenCode V2, or OpenChamber (desktop, CLI/web, or VS Code extension). The plugin targets `@opencode/plugin` 2.1.4, the version bundled with current OpenChamber desktop builds.
|
|
10
|
+
- **Node.js 22.13 or newer** on your `PATH`. The Cursor SDK worker runs under `node`. If your default `node` is older, point `CURSOR_ACP_NODE_BIN` at a newer one.
|
|
11
|
+
- A Cursor account, and one of these for sign-in:
|
|
12
|
+
- the Cursor CLI (`cursor-agent`) installed and logged in, or
|
|
13
|
+
- a Cursor API key from [cursor.com/settings](https://cursor.com/settings), or
|
|
14
|
+
- nothing extra, if you use **Cursor account (native OAuth)** in the browser.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
1. Install the plugin from npm:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
opencode plugin add opencodev2-cursor
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This adds one entry to your global OpenCode config and downloads the package into OpenCode's plugin cache.
|
|
25
|
+
|
|
26
|
+
If the `opencode` command is not on your `PATH` (for example, you only use OpenChamber desktop), add the entry by hand instead. Open the global config file, add the plugin to the `plugins` array, and save:
|
|
27
|
+
|
|
28
|
+
```jsonc
|
|
29
|
+
{
|
|
30
|
+
"$schema": "https://opencode.ai/config.json",
|
|
31
|
+
"plugins": ["opencodev2-cursor"]
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| | Global config file |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| Windows | `%USERPROFILE%\.config\opencode\opencode.json` |
|
|
38
|
+
| macOS and Linux | `~/.config/opencode/opencode.json` |
|
|
39
|
+
|
|
40
|
+
2. **Restart OpenChamber or OpenCode** so it loads the plugin.
|
|
41
|
+
3. Confirm it loaded: `opencode plugin list` should show `opencodev2-cursor` once.
|
|
42
|
+
4. Sign in. In OpenCode or OpenChamber, run `/connect` and choose **Cursor**, then pick one method:
|
|
43
|
+
- **Log in with cursor-agent (browser)**. Run `cursor-agent login` first if you have not.
|
|
44
|
+
- **API key**. Paste a key from [cursor.com/settings](https://cursor.com/settings), or set `CURSOR_API_KEY` in your environment.
|
|
45
|
+
- **Cursor account (native OAuth)**. Sign in through the browser. The plugin mints and caches a Cursor user API key for the SDK.
|
|
46
|
+
5. Pick a model from the **Cursor** provider. `Auto-Cursor-Balanced`, `Auto-Cursor-Cost`, and `Auto-Cursor-Intelligence` use Cursor's Auto Router. The list also includes your other Cursor models.
|
|
47
|
+
|
|
48
|
+
Install it **once**, in the global config. Do not also copy the plugin into the `plugins` folder under the OpenCode config folder, and do not list it in more than one config file. OpenCode loads that folder automatically, and a second copy fails activation.
|
|
49
|
+
|
|
50
|
+
## Updates
|
|
51
|
+
|
|
52
|
+
Installing from npm does **not** silently replace the version you have. What happens depends on how the entry is written:
|
|
53
|
+
|
|
54
|
+
- **Unpinned** (`"opencodev2-cursor"`, the default from `opencode plugin add opencodev2-cursor`): OpenCode checks npm for a newer version when it starts, but it keeps using the installed one until you update.
|
|
55
|
+
- **Pinned** (`"opencodev2-cursor@0.1.0"`): never changes until you edit the entry. Choose this if you want a fixed version, for example while a specific OpenChamber build is known to work with it.
|
|
56
|
+
|
|
57
|
+
To see and apply updates:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
opencode plugin check
|
|
61
|
+
opencode plugin update opencodev2-cursor
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Restart OpenChamber or OpenCode afterwards. Version bumps follow semver: a patch or minor release should be safe to take, and the changelog on GitHub says when a release needs a newer OpenChamber.
|
|
65
|
+
|
|
66
|
+
## Uninstall
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
opencode plugin remove opencodev2-cursor
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or delete the `opencodev2-cursor` entry from `opencode.json`. Restart afterwards. Optional cleanup: remove the plugin cache folder shown in the file locations table below.
|
|
73
|
+
|
|
74
|
+
## Troubleshooting
|
|
75
|
+
|
|
76
|
+
- **No Cursor provider or models.** Check `opencode plugin list`, restart, and read the log (`opencodev2-cursor.log` in the plugin cache folder).
|
|
77
|
+
- **"Plugin failed to activate" or a duplicate plugin.** Make sure there is only one entry, and nothing in the config folder's `plugins` directory.
|
|
78
|
+
- **"Cursor SDK needs Node.js 22.13 or newer".** Install a newer Node, or set `CURSOR_ACP_NODE_BIN` to its path.
|
|
79
|
+
- **"Agent … already has active run".** Send the message again. A new turn cancels the stuck run.
|
|
80
|
+
- **Sign-in with cursor-agent fails.** Run `cursor-agent login` in a terminal, then retry `/connect`.
|
|
81
|
+
|
|
82
|
+
## Where files live
|
|
83
|
+
|
|
84
|
+
| | Windows | macOS and Linux |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| OpenCode config | `%USERPROFILE%\.config\opencode\opencode.json` | `~/.config/opencode/opencode.json` |
|
|
87
|
+
| Plugin cache and log | `%USERPROFILE%\.cache\opencodev2-cursor\` | `~/.cache/opencodev2-cursor/` (`$XDG_CACHE_HOME/opencodev2-cursor/` when set) |
|
|
88
|
+
| SDK state | `%USERPROFILE%\.cursor\oc-sdk\<id>` | `~/.cursor/oc-sdk/<id>` |
|
|
89
|
+
| V8 compile cache | `%LOCALAPPDATA%\cursor-compile-cache` | `<plugin cache>/compile-cache` |
|
|
90
|
+
| `cursor-agent` launcher | `cursor-agent.ps1`, run through PowerShell | `cursor-agent` on `PATH`, usually a symlink into `~/.local/share/cursor-agent/versions/<ver>/` |
|
|
91
|
+
|
|
92
|
+
## Backends
|
|
93
|
+
|
|
94
|
+
`CURSOR_BACKEND` selects the backend. The default is `auto`.
|
|
95
|
+
|
|
96
|
+
| Value | When it runs |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `auto` | API key uses `@cursor/sdk`. Native OAuth uses the native backend. Otherwise `cursor-agent`. |
|
|
99
|
+
| `cli` | `cursor-agent --print --output-format stream-json` |
|
|
100
|
+
| `sdk` | `@cursor/sdk` with an API key. Needs Node.js 22.13+ (`CURSOR_ACP_NODE_BIN`). |
|
|
101
|
+
| `native` | Cursor account OAuth. Uses the SDK with a minted user API key, or Connect-RPC over a Node HTTP/2 bridge when `CURSOR_NATIVE_TRANSPORT=connect`. |
|
|
102
|
+
|
|
103
|
+
SDK state is stored under `~/.cursor/oc-sdk/<id>` so SQLite paths stay short on Windows. On Windows, login and `--list-models` start `cursor-agent` through its `.ps1` launcher (`powershell.exe -NoProfile -ExecutionPolicy Bypass -File`), not the `.cmd` shim. On macOS and Linux they run the `cursor-agent` launcher directly. A chat run spawns the versioned `node` (`node.exe` on Windows) and `index.js` directly, with the prompt in a file, because the Windows `.ps1` launcher forwards every argument through PowerShell and a long prompt exceeds the command-line limit, and Linux caps a single argument near 128 KB. On macOS and Linux the launcher symlink is resolved first so the versioned runtime beside it is found. If that runtime is not installed beside the launcher, the chat run falls back to the launcher with the prompt as an argument.
|
|
104
|
+
|
|
105
|
+
Tool calls that match tools OpenCode advertised (`read`, `shell`, `glob`, `grep`, `edit`, `write`, `subagent`) are returned to OpenCode on the SDK and native backends. Cursor names map onto those tools: `bash` → `shell`, `Task` → `subagent`, `strreplace` → `edit`, `find` and `ls` → `glob`. The CLI backend stops `cursor-agent` at the first matching tool call and hands that call to OpenCode. Headless runs pass `--trust` so a new chat folder does not stop on the workspace prompt. Set `CURSOR_ACP_TRUST_WORKSPACE=false` to omit that flag.
|
|
106
|
+
|
|
107
|
+
`CURSOR_NATIVE_TRANSPORT=connect` is a fallback. It sends the OAuth access token, not the minted `crsr_` SDK key, and the request includes the model, prompt, `optimize_for` params, advertised tools, and tool results. The HTTP/2 bridge emits each Connect frame as it arrives. The default native backend uses `@cursor/sdk`.
|
|
108
|
+
|
|
109
|
+
## Request path
|
|
110
|
+
|
|
111
|
+
OpenCode talks to one loopback OpenAI-compatible proxy per process (`GET /health`, `GET /v1/models`, `POST /v1/chat/completions`). OpenChamber starts this plugin once per project. Each project registers its own auth and backends on that shared proxy; a request uses the most recently attached project, and an earlier project takes over when that one unloads.
|
|
112
|
+
|
|
113
|
+
Session-title prompts are answered locally with a short title so they do not start a Cursor run. Logs are written to `opencodev2-cursor.log` in the plugin cache folder.
|
|
114
|
+
|
|
115
|
+
## Environment
|
|
116
|
+
|
|
117
|
+
| Variable | Role |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| `CURSOR_BACKEND` | `auto` (default), `cli`, `sdk`, or `native` |
|
|
120
|
+
| `CURSOR_API_KEY` | API key when no integration credential is connected |
|
|
121
|
+
| `CURSOR_AGENT_BIN` | Explicit `cursor-agent` launcher path |
|
|
122
|
+
| `CURSOR_ACP_NODE_BIN` | Node.js 22.13+ binary for the SDK worker |
|
|
123
|
+
| `CURSOR_ACP_TRUST_WORKSPACE` | Set to `false` to omit `--trust` |
|
|
124
|
+
| `CURSOR_ACP_SDK_STATE_ROOT` | SDK store root (default `~/.cursor/oc-sdk`) |
|
|
125
|
+
| `OPENCODEV2_CURSOR_CACHE` | Cache directory (default `~/.cache/opencodev2-cursor`; `OPENCHAMBER_CURSOR_CACHE` and `CURSOR_CHAMBER_CACHE` still work) |
|
|
126
|
+
| `CURSOR_RIPWALK_CACHE_TTL_MS` | Workspace scan cache (default 30 minutes) |
|
|
127
|
+
| `NODE_COMPILE_CACHE` | V8 compile cache for the SDK worker and cursor-agent |
|
|
128
|
+
| `CURSOR_NATIVE_TRANSPORT` | `connect` uses the HTTP/2 bridge; otherwise native uses the SDK |
|
|
129
|
+
| `CURSOR_SDK_HTTP1` | Set to `1` to force HTTP/1 for the SDK agent transport |
|
|
130
|
+
| `CURSOR_BACKEND_URL` | Connect bridge origin (default `https://api2.cursor.sh`) |
|
|
131
|
+
| `CURSOR_REFRESH_URL` | Native token refresh endpoint |
|
|
132
|
+
|
|
133
|
+
## Develop
|
|
134
|
+
|
|
135
|
+
Contributors only. End users should use the npm install above.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
git clone https://github.com/aenemo/opencodev2-cursor.git
|
|
139
|
+
cd opencodev2-cursor
|
|
140
|
+
npm install
|
|
141
|
+
npm test
|
|
142
|
+
npm run build
|
|
143
|
+
node scripts/install-dev.mjs
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The installer works on Windows, macOS, and Linux. It adds one `plugins` entry that points at your clone. Remove any npm-installed `opencodev2-cursor` entry first, so only one copy loads. It does not also copy the plugin into the `plugins` folder under the OpenCode config folder, because OpenCode loads that folder automatically and a second copy fails activation. Restart OpenCode or OpenChamber after every rebuild.
|
|
147
|
+
|
|
148
|
+
## Publish to npm
|
|
149
|
+
|
|
150
|
+
Maintainers only. The tarball ships only `dist/` (see `files` in `package.json`); `prepublishOnly` runs `npm run build` before upload, so `dist/` does not need to be committed to git.
|
|
151
|
+
|
|
152
|
+
1. Create an [npm account](https://www.npmjs.com/signup) and enable 2FA.
|
|
153
|
+
2. Check the name is free: `npm view opencodev2-cursor` (a 404 means it is available).
|
|
154
|
+
3. From the repo root: `npm install`, `npm test`, `npm run build`.
|
|
155
|
+
4. Dry-run the tarball: `npm pack --dry-run` (expect `dist/`, `LICENSE`, `README.md`, `package.json`).
|
|
156
|
+
5. Log in: `npm login`, then `npm whoami`.
|
|
157
|
+
6. Publish: `npm publish` (add `--access public` for a scoped name).
|
|
158
|
+
7. Tag GitHub to match: `git tag v0.1.0`, `git push origin v0.1.0`.
|
|
159
|
+
8. Verify: `npm view opencodev2-cursor version`, then `opencode plugin add opencodev2-cursor`.
|
|
160
|
+
|
|
161
|
+
Later releases: bump with `npm version patch` (updates `package.json`, creates a git tag), push tags, then `npm publish`. You cannot republish the same version.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import fs from "node:fs"
|
|
2
|
+
import { pathToFileURL } from "node:url"
|
|
3
|
+
|
|
4
|
+
const [promptFile, indexJs, ...args] = process.argv.slice(2)
|
|
5
|
+
if (!promptFile || !indexJs) {
|
|
6
|
+
process.stderr.write("usage: agent-argv.mjs <prompt-file> <index.js> [agent args...]\n")
|
|
7
|
+
process.exit(1)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const prompt = fs.readFileSync(promptFile, "utf8")
|
|
11
|
+
process.argv = [process.execPath, indexJs, ...args, prompt]
|
|
12
|
+
await import(pathToFileURL(indexJs).href)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CursorBackend, CursorModel, BackendRunRequest, BackendEvent } from "./types.js";
|
|
2
|
+
import { type Launch } from "../util/windows.js";
|
|
3
|
+
export declare class AgentCliBackend implements CursorBackend {
|
|
4
|
+
private readonly launch;
|
|
5
|
+
readonly id: "cli";
|
|
6
|
+
constructor(launch?: Launch | undefined);
|
|
7
|
+
private requireLaunch;
|
|
8
|
+
listModels(): Promise<CursorModel[]>;
|
|
9
|
+
run(request: BackendRunRequest): AsyncIterable<BackendEvent>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { FALLBACK_MODELS, promptWithToolResults } from "./types.js";
|
|
6
|
+
import { parseModelList, parseStreamJsonLine } from "./stream-json.js";
|
|
7
|
+
import { collectLaunch, spawnCommand, spawnLaunch } from "./spawn.js";
|
|
8
|
+
import { resolveAgentRuntime, resolveCursorAgentLaunch } from "../util/windows.js";
|
|
9
|
+
import { cacheDir, compileCacheDir } from "../util/paths.js";
|
|
10
|
+
import { log } from "../util/log.js";
|
|
11
|
+
import { mapCursorTool } from "../proxy/tools.js";
|
|
12
|
+
function argvScript() {
|
|
13
|
+
return fileURLToPath(new URL("./agent-argv.mjs", import.meta.url));
|
|
14
|
+
}
|
|
15
|
+
function writePrompt(prompt) {
|
|
16
|
+
const file = path.join(cacheDir(), "prompts", `${randomBytes(8).toString("hex")}.txt`);
|
|
17
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
18
|
+
fs.writeFileSync(file, prompt);
|
|
19
|
+
return file;
|
|
20
|
+
}
|
|
21
|
+
export class AgentCliBackend {
|
|
22
|
+
launch;
|
|
23
|
+
id = "cli";
|
|
24
|
+
constructor(launch = resolveCursorAgentLaunch(process.env.CURSOR_AGENT_BIN)) {
|
|
25
|
+
this.launch = launch;
|
|
26
|
+
}
|
|
27
|
+
requireLaunch() {
|
|
28
|
+
if (!this.launch) {
|
|
29
|
+
throw new Error("cursor-agent is not on PATH. Install the Cursor CLI and run `cursor-agent login`.");
|
|
30
|
+
}
|
|
31
|
+
return this.launch;
|
|
32
|
+
}
|
|
33
|
+
async listModels() {
|
|
34
|
+
const launch = this.launch;
|
|
35
|
+
if (!launch)
|
|
36
|
+
return FALLBACK_MODELS;
|
|
37
|
+
try {
|
|
38
|
+
const listed = await collectLaunch(launch, ["--list-models"], process.cwd());
|
|
39
|
+
const parsed = parseModelList(listed.output);
|
|
40
|
+
if (parsed.length === 0)
|
|
41
|
+
return FALLBACK_MODELS;
|
|
42
|
+
return parsed.map((model) => ({
|
|
43
|
+
id: model.id,
|
|
44
|
+
name: model.name,
|
|
45
|
+
contextWindow: 200000,
|
|
46
|
+
maxTokens: 32000,
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
log("warn", "cursor-agent model list failed", error instanceof Error ? error.message : error);
|
|
51
|
+
return FALLBACK_MODELS;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async *run(request) {
|
|
55
|
+
const launch = this.requireLaunch();
|
|
56
|
+
const args = ["--print", "--output-format", "stream-json", "--stream-partial-output", "--workspace", request.cwd];
|
|
57
|
+
if (process.env.CURSOR_ACP_TRUST_WORKSPACE !== "false" && process.env.CURSOR_ACP_TRUST_WORKSPACE !== "0")
|
|
58
|
+
args.push("--trust");
|
|
59
|
+
if (request.tools.length === 0)
|
|
60
|
+
args.push("--mode", "ask");
|
|
61
|
+
const cliModel = request.cliModel || request.model;
|
|
62
|
+
if (cliModel && cliModel !== "auto")
|
|
63
|
+
args.push("--model", cliModel);
|
|
64
|
+
const prompt = promptWithToolResults(request.prompt, request.toolResults);
|
|
65
|
+
log("info", "cursor-agent run", { model: cliModel, cwd: request.cwd, promptChars: prompt.length });
|
|
66
|
+
const runtime = resolveAgentRuntime(launch);
|
|
67
|
+
const promptFile = runtime ? writePrompt(prompt) : undefined;
|
|
68
|
+
const { child, lines, stderr } = runtime && promptFile
|
|
69
|
+
? spawnCommand(runtime.node, [argvScript(), promptFile, runtime.index, ...args], request.cwd, request.signal, {
|
|
70
|
+
...process.env,
|
|
71
|
+
CURSOR_INVOKED_AS: process.env.CURSOR_INVOKED_AS || "cursor-agent",
|
|
72
|
+
NODE_COMPILE_CACHE: compileCacheDir(),
|
|
73
|
+
})
|
|
74
|
+
: spawnLaunch(launch, [...args, prompt], request.cwd, request.signal);
|
|
75
|
+
if (promptFile)
|
|
76
|
+
child.once("close", () => fs.rmSync(promptFile, { force: true }));
|
|
77
|
+
const advertised = new Set(request.tools.map((tool) => tool.name));
|
|
78
|
+
let sawDone = false;
|
|
79
|
+
let streamed = "";
|
|
80
|
+
for await (const line of lines) {
|
|
81
|
+
for (const event of parseStreamJsonLine(line)) {
|
|
82
|
+
if (event.type === "text") {
|
|
83
|
+
if (streamed && event.text === streamed)
|
|
84
|
+
continue;
|
|
85
|
+
streamed += event.text;
|
|
86
|
+
}
|
|
87
|
+
if (event.type === "tool_call") {
|
|
88
|
+
const mapped = mapCursorTool(event.name, advertised);
|
|
89
|
+
if (mapped) {
|
|
90
|
+
yield { ...event, name: mapped };
|
|
91
|
+
child.kill();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (event.type === "done")
|
|
97
|
+
sawDone = true;
|
|
98
|
+
yield event;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const code = await new Promise((resolve) => {
|
|
102
|
+
if (child.exitCode !== null || child.signalCode)
|
|
103
|
+
resolve(child.exitCode);
|
|
104
|
+
else
|
|
105
|
+
child.once("close", resolve);
|
|
106
|
+
});
|
|
107
|
+
if (!sawDone) {
|
|
108
|
+
if (code && code !== 0) {
|
|
109
|
+
const detail = stderr().trim().replace(/\s+/g, " ").slice(0, 500);
|
|
110
|
+
yield { type: "error", message: detail ? `cursor-agent exited with code ${code}: ${detail}` : `cursor-agent exited with code ${code}` };
|
|
111
|
+
}
|
|
112
|
+
else
|
|
113
|
+
yield { type: "done" };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=agent-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-cli.js","sourceRoot":"","sources":["../../src/backends/agent-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACnE,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACrE,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAe,MAAM,oBAAoB,CAAA;AAC/F,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEjD,SAAS,UAAU;IACjB,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,kBAAkB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AACpE,CAAC;AAED,SAAS,WAAW,CAAC,MAAc;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACtF,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACrD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IAC9B,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,OAAO,eAAe;IAGG;IAFpB,EAAE,GAAG,KAAc,CAAA;IAE5B,YAA6B,SAA6B,wBAAwB,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;QAAnF,WAAM,GAAN,MAAM,CAA6E;IAAG,CAAC;IAE5G,aAAa;QACnB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,mFAAmF,CAAC,CAAA;QACtG,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QAC1B,IAAI,CAAC,MAAM;YAAE,OAAO,eAAe,CAAA;QACnC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;YAC5E,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC5C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,eAAe,CAAA;YAC/C,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC5B,EAAE,EAAE,KAAK,CAAC,EAAE;gBACZ,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,aAAa,EAAE,MAAM;gBACrB,SAAS,EAAE,KAAK;aACjB,CAAC,CAAC,CAAA;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,EAAE,gCAAgC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;YAC7F,OAAO,eAAe,CAAA;QACxB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,CAAC,GAAG,CAAC,OAA0B;QACnC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAA;QACnC,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,iBAAiB,EAAE,aAAa,EAAE,yBAAyB,EAAE,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;QACjH,IAAI,OAAO,CAAC,GAAG,CAAC,0BAA0B,KAAK,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,0BAA0B,KAAK,GAAG;YAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC9H,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;QAC1D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,KAAK,CAAA;QAClD,IAAI,QAAQ,IAAI,QAAQ,KAAK,MAAM;YAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;QACnE,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;QACzE,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;QAClG,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAA;QAC3C,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAC5D,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,IAAI,UAAU;YACpD,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,UAAU,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE;gBAC5G,GAAG,OAAO,CAAC,GAAG;gBACd,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,cAAc;gBAClE,kBAAkB,EAAE,eAAe,EAAE;aACtC,CAAC;YACF,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;QACvE,IAAI,UAAU;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QACjF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QAClE,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,IAAI,QAAQ,GAAG,EAAE,CAAA;QACjB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC/B,KAAK,MAAM,KAAK,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC1B,IAAI,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;wBAAE,SAAQ;oBACjD,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAA;gBACxB,CAAC;gBACD,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBAC/B,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;oBACpD,IAAI,MAAM,EAAE,CAAC;wBACX,MAAM,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;wBAChC,KAAK,CAAC,IAAI,EAAE,CAAA;wBACZ,OAAM;oBACR,CAAC;oBACD,SAAQ;gBACV,CAAC;gBACD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;oBAAE,OAAO,GAAG,IAAI,CAAA;gBACzC,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;YACxD,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU;gBAAE,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;;gBACnE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,IAAI,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;gBACjE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,iCAAiC,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,iCAAiC,IAAI,EAAE,EAAE,CAAA;YACzI,CAAC;;gBACI,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;QAC7B,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Cursor Router is one model id (`auto-smart`) with optimize_for = cost | balanced | intelligence. */
|
|
2
|
+
export type OptimizeFor = "cost" | "balanced" | "intelligence";
|
|
3
|
+
export interface AutoCursorModel {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
optimizeFor: OptimizeFor;
|
|
7
|
+
}
|
|
8
|
+
export declare const AUTO_CURSOR_MODELS: readonly AutoCursorModel[];
|
|
9
|
+
export interface ResolvedCursorModel {
|
|
10
|
+
/** Id shown / selected in OpenCode. */
|
|
11
|
+
pickerId: string;
|
|
12
|
+
/** Id sent to Cursor (`auto-smart` for all Auto-Cursor-* variants). */
|
|
13
|
+
cursorId: string;
|
|
14
|
+
params?: Array<{
|
|
15
|
+
id: string;
|
|
16
|
+
value: string;
|
|
17
|
+
}>;
|
|
18
|
+
/** cursor-agent --model value, including bracket params when needed. */
|
|
19
|
+
cliModel: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function isAutoCursorId(model: string): boolean;
|
|
22
|
+
export declare function resolveCursorModel(model: string): ResolvedCursorModel;
|
|
23
|
+
export declare function modelSelectionKey(model: string, params?: Array<{
|
|
24
|
+
id: string;
|
|
25
|
+
value: string;
|
|
26
|
+
}>): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const AUTO_CURSOR_MODELS = [
|
|
2
|
+
{ id: "auto-cursor-balanced", name: "Auto-Cursor-Balanced", optimizeFor: "balanced" },
|
|
3
|
+
{ id: "auto-cursor-cost", name: "Auto-Cursor-Cost", optimizeFor: "cost" },
|
|
4
|
+
{ id: "auto-cursor-intelligence", name: "Auto-Cursor-Intelligence", optimizeFor: "intelligence" },
|
|
5
|
+
];
|
|
6
|
+
export function isAutoCursorId(model) {
|
|
7
|
+
return model === "auto"
|
|
8
|
+
|| model === "auto-smart"
|
|
9
|
+
|| model === "default"
|
|
10
|
+
|| AUTO_CURSOR_MODELS.some((entry) => entry.id === model);
|
|
11
|
+
}
|
|
12
|
+
export function resolveCursorModel(model) {
|
|
13
|
+
const named = AUTO_CURSOR_MODELS.find((entry) => entry.id === model);
|
|
14
|
+
if (named) {
|
|
15
|
+
return {
|
|
16
|
+
pickerId: named.id,
|
|
17
|
+
cursorId: "auto-smart",
|
|
18
|
+
params: [{ id: "optimize_for", value: named.optimizeFor }],
|
|
19
|
+
cliModel: `auto-smart[optimize_for=${named.optimizeFor}]`,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
// OpenCode defaults and Cursor's raw router ids map to Balance (Cursor's default Auto mode).
|
|
23
|
+
if (isAutoCursorId(model)) {
|
|
24
|
+
return {
|
|
25
|
+
pickerId: model,
|
|
26
|
+
cursorId: "auto-smart",
|
|
27
|
+
params: [{ id: "optimize_for", value: "balanced" }],
|
|
28
|
+
cliModel: "auto-smart[optimize_for=balanced]",
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
pickerId: model,
|
|
33
|
+
cursorId: model,
|
|
34
|
+
cliModel: model,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function modelSelectionKey(model, params) {
|
|
38
|
+
const list = [...(params ?? [])]
|
|
39
|
+
.map((param) => ({ id: String(param.id), value: String(param.value) }))
|
|
40
|
+
.sort((left, right) => left.id.localeCompare(right.id));
|
|
41
|
+
return JSON.stringify({ id: model, params: list });
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=auto-models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-models.js","sourceRoot":"","sources":["../../src/backends/auto-models.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,kBAAkB,GAA+B;IAC5D,EAAE,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,sBAAsB,EAAE,WAAW,EAAE,UAAU,EAAE;IACrF,EAAE,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,EAAE;IACzE,EAAE,EAAE,EAAE,0BAA0B,EAAE,IAAI,EAAE,0BAA0B,EAAE,WAAW,EAAE,cAAc,EAAE;CACzF,CAAA;AAYV,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,KAAK,KAAK,MAAM;WAClB,KAAK,KAAK,YAAY;WACtB,KAAK,KAAK,SAAS;WACnB,kBAAkB,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,CAAA;AAC7D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,CAAA;IACpE,IAAI,KAAK,EAAE,CAAC;QACV,OAAO;YACL,QAAQ,EAAE,KAAK,CAAC,EAAE;YAClB,QAAQ,EAAE,YAAY;YACtB,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;YAC1D,QAAQ,EAAE,2BAA2B,KAAK,CAAC,WAAW,GAAG;SAC1D,CAAA;IACH,CAAC;IACD,6FAA6F;IAC7F,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,YAAY;YACtB,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;YACnD,QAAQ,EAAE,mCAAmC;SAC9C,CAAA;IACH,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,KAAK;QACf,QAAQ,EAAE,KAAK;QACf,QAAQ,EAAE,KAAK;KAChB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,MAA6C;IAC5F,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;SAC7B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;SACtE,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAA;IACzD,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;AACpD,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface Checkpoint {
|
|
2
|
+
version?: number;
|
|
3
|
+
agentId: string;
|
|
4
|
+
model: string;
|
|
5
|
+
updatedAt: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function readCheckpoint(sessionKey: string): Checkpoint | undefined;
|
|
8
|
+
export declare function writeCheckpoint(sessionKey: string, checkpoint: Omit<Checkpoint, "updatedAt">): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import zlib from "node:zlib";
|
|
4
|
+
import { conversationDir } from "../util/paths.js";
|
|
5
|
+
const CHECKPOINT_VERSION = 2;
|
|
6
|
+
function fileFor(sessionKey) {
|
|
7
|
+
const safe = sessionKey.replace(/[^a-z0-9_-]/gi, "_").slice(0, 120);
|
|
8
|
+
return path.join(conversationDir(), `${safe}.json.gz`);
|
|
9
|
+
}
|
|
10
|
+
export function readCheckpoint(sessionKey) {
|
|
11
|
+
try {
|
|
12
|
+
const raw = zlib.gunzipSync(fs.readFileSync(fileFor(sessionKey)));
|
|
13
|
+
const parsed = JSON.parse(raw.toString());
|
|
14
|
+
if (!parsed.agentId || parsed.version !== CHECKPOINT_VERSION)
|
|
15
|
+
return undefined;
|
|
16
|
+
return parsed;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function writeCheckpoint(sessionKey, checkpoint) {
|
|
23
|
+
const dir = conversationDir();
|
|
24
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
25
|
+
const body = zlib.gzipSync(JSON.stringify({ ...checkpoint, version: CHECKPOINT_VERSION, updatedAt: Date.now() }));
|
|
26
|
+
fs.writeFileSync(fileFor(sessionKey), body);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=checkpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../../src/backends/checkpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAElD,MAAM,kBAAkB,GAAG,CAAC,CAAA;AAS5B,SAAS,OAAO,CAAC,UAAkB;IACjC,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACnE,OAAO,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,GAAG,IAAI,UAAU,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAe,CAAA;QACvD,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,KAAK,kBAAkB;YAAE,OAAO,SAAS,CAAA;QAC9E,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,UAAkB,EAAE,UAAyC;IAC3F,MAAM,GAAG,GAAG,eAAe,EAAE,CAAA;IAC7B,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,UAAU,EAAE,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAA;IACjH,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,CAAA;AAC7C,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface NativeLogin {
|
|
2
|
+
verifier: string;
|
|
3
|
+
loginUrl: string;
|
|
4
|
+
uuid: string;
|
|
5
|
+
}
|
|
6
|
+
export interface NativeTokens {
|
|
7
|
+
access: string;
|
|
8
|
+
refresh: string;
|
|
9
|
+
expires: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function createNativeLogin(): NativeLogin;
|
|
12
|
+
export declare function tokenExpiry(token: string): number;
|
|
13
|
+
export declare function pollNativeLogin(uuid: string, verifier: string, signal?: AbortSignal): Promise<NativeTokens>;
|
|
14
|
+
export declare function refreshNativeToken(refresh: string): Promise<NativeTokens>;
|
|
15
|
+
export declare function mintUserApiKey(accessToken: string, fetchImpl?: typeof fetch): Promise<{
|
|
16
|
+
apiKey: string;
|
|
17
|
+
expires: number;
|
|
18
|
+
}>;
|
|
19
|
+
/** Turns a browser-login JWT into a reusable user API key (`crsr_...`) required by `@cursor/sdk`. */
|
|
20
|
+
export declare function ensureSdkApiKey(tokenOrKey: string, refreshToken?: string, fetchImpl?: typeof fetch): Promise<string>;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { cacheDir } from "../../util/paths.js";
|
|
6
|
+
const LOGIN_URL = "https://cursor.com/loginDeepControl";
|
|
7
|
+
const POLL_URL = "https://api2.cursor.sh/auth/poll";
|
|
8
|
+
const REFRESH_URL = process.env.CURSOR_REFRESH_URL ?? "https://api2.cursor.sh/auth/exchange_user_api_key";
|
|
9
|
+
const MINT_URL = "https://api2.cursor.sh/aiserver.v1.DashboardService/CreateUserApiKey";
|
|
10
|
+
const NINETY_DAYS_MS = 90 * 24 * 3600_000;
|
|
11
|
+
function base64url(bytes) {
|
|
12
|
+
return bytes.toString("base64url");
|
|
13
|
+
}
|
|
14
|
+
export function createNativeLogin() {
|
|
15
|
+
const verifier = base64url(randomBytes(32));
|
|
16
|
+
const challenge = base64url(createHash("sha256").update(verifier).digest());
|
|
17
|
+
const uuid = randomUUID();
|
|
18
|
+
const params = new URLSearchParams({ challenge, uuid, mode: "login", redirectTarget: "cli" });
|
|
19
|
+
return { verifier, uuid, loginUrl: `${LOGIN_URL}?${params.toString()}` };
|
|
20
|
+
}
|
|
21
|
+
export function tokenExpiry(token) {
|
|
22
|
+
try {
|
|
23
|
+
const payload = token.split(".")[1];
|
|
24
|
+
if (!payload)
|
|
25
|
+
return Date.now() + 3600_000;
|
|
26
|
+
const decoded = JSON.parse(Buffer.from(payload, "base64url").toString());
|
|
27
|
+
if (typeof decoded.exp === "number")
|
|
28
|
+
return decoded.exp * 1000 - 5 * 60_000;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Fall through to a one-hour lifetime.
|
|
32
|
+
}
|
|
33
|
+
return Date.now() + 3600_000;
|
|
34
|
+
}
|
|
35
|
+
function issuedTokens(access, refresh) {
|
|
36
|
+
return {
|
|
37
|
+
access,
|
|
38
|
+
refresh,
|
|
39
|
+
expires: tokenExpiry(access),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export async function pollNativeLogin(uuid, verifier, signal) {
|
|
43
|
+
let delay = 1000;
|
|
44
|
+
for (let attempt = 0; attempt < 150; attempt++) {
|
|
45
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
46
|
+
if (signal?.aborted)
|
|
47
|
+
throw new Error("Cursor login cancelled");
|
|
48
|
+
const response = await fetch(`${POLL_URL}?uuid=${encodeURIComponent(uuid)}&verifier=${encodeURIComponent(verifier)}`, { signal });
|
|
49
|
+
if (response.status === 404) {
|
|
50
|
+
delay = Math.min(delay * 1.2, 10_000);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (!response.ok)
|
|
54
|
+
throw new Error(`Cursor login poll failed (${response.status})`);
|
|
55
|
+
const data = (await response.json());
|
|
56
|
+
if (!data.accessToken)
|
|
57
|
+
throw new Error("Cursor login returned no access token");
|
|
58
|
+
return issuedTokens(data.accessToken, data.refreshToken || data.accessToken);
|
|
59
|
+
}
|
|
60
|
+
throw new Error("Cursor login timed out");
|
|
61
|
+
}
|
|
62
|
+
export async function refreshNativeToken(refresh) {
|
|
63
|
+
const response = await fetch(REFRESH_URL, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: { Authorization: `Bearer ${refresh}`, "Content-Type": "application/json" },
|
|
66
|
+
body: "{}",
|
|
67
|
+
});
|
|
68
|
+
if (!response.ok)
|
|
69
|
+
throw new Error(`Cursor token refresh failed (${response.status})`);
|
|
70
|
+
const data = (await response.json());
|
|
71
|
+
if (!data.accessToken)
|
|
72
|
+
throw new Error("Cursor token refresh returned no access token");
|
|
73
|
+
return issuedTokens(data.accessToken, data.refreshToken || refresh);
|
|
74
|
+
}
|
|
75
|
+
export async function mintUserApiKey(accessToken, fetchImpl = fetch) {
|
|
76
|
+
const expires = Date.now() + NINETY_DAYS_MS;
|
|
77
|
+
const response = await fetchImpl(MINT_URL, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: {
|
|
80
|
+
Authorization: `Bearer ${accessToken}`,
|
|
81
|
+
"Connect-Protocol-Version": "1",
|
|
82
|
+
"Content-Type": "application/json",
|
|
83
|
+
},
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
name: `opencodev2-cursor-${os.hostname()}`,
|
|
86
|
+
expiresAt: String(Math.floor(expires)),
|
|
87
|
+
}),
|
|
88
|
+
});
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
const detail = await response.text().catch(() => "");
|
|
91
|
+
throw new Error(`Creating Cursor SDK API key failed (${response.status}): ${detail.slice(0, 200)}`);
|
|
92
|
+
}
|
|
93
|
+
const data = (await response.json());
|
|
94
|
+
if (!data.apiKey)
|
|
95
|
+
throw new Error("Cursor returned an empty API key");
|
|
96
|
+
return { apiKey: data.apiKey, expires };
|
|
97
|
+
}
|
|
98
|
+
function mintedFile() {
|
|
99
|
+
return path.join(cacheDir(), "minted-key.json");
|
|
100
|
+
}
|
|
101
|
+
let cachedKey;
|
|
102
|
+
/** Turns a browser-login JWT into a reusable user API key (`crsr_...`) required by `@cursor/sdk`. */
|
|
103
|
+
export async function ensureSdkApiKey(tokenOrKey, refreshToken, fetchImpl = fetch) {
|
|
104
|
+
if (!tokenOrKey.startsWith("eyJ"))
|
|
105
|
+
return tokenOrKey;
|
|
106
|
+
const sourceHash = createHash("sha256").update(refreshToken || tokenOrKey).digest("hex").slice(0, 16);
|
|
107
|
+
if (cachedKey && cachedKey.sourceHash === sourceHash && cachedKey.expires > Date.now() + 60_000) {
|
|
108
|
+
return cachedKey.apiKey;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const onDisk = JSON.parse(fs.readFileSync(mintedFile(), "utf8"));
|
|
112
|
+
if (onDisk.sourceHash === sourceHash && typeof onDisk.apiKey === "string" && (onDisk.expires ?? 0) > Date.now() + 60_000) {
|
|
113
|
+
cachedKey = { sourceHash, apiKey: onDisk.apiKey, expires: onDisk.expires };
|
|
114
|
+
return onDisk.apiKey;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Mint a fresh key below.
|
|
119
|
+
}
|
|
120
|
+
let minted;
|
|
121
|
+
try {
|
|
122
|
+
minted = await mintUserApiKey(tokenOrKey, fetchImpl);
|
|
123
|
+
}
|
|
124
|
+
catch (firstError) {
|
|
125
|
+
if (!refreshToken)
|
|
126
|
+
throw firstError;
|
|
127
|
+
const refreshed = await refreshNativeToken(refreshToken);
|
|
128
|
+
if (!refreshed.access.startsWith("eyJ")) {
|
|
129
|
+
minted = { apiKey: refreshed.access, expires: refreshed.expires };
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
minted = await mintUserApiKey(refreshed.access, fetchImpl);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
cachedKey = { sourceHash, apiKey: minted.apiKey, expires: minted.expires };
|
|
136
|
+
try {
|
|
137
|
+
fs.mkdirSync(cacheDir(), { recursive: true });
|
|
138
|
+
fs.writeFileSync(mintedFile(), JSON.stringify(cachedKey));
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// In-memory cache still serves this process.
|
|
142
|
+
}
|
|
143
|
+
return minted.apiKey;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/backends/native/auth.ts"],"names":[],"mappings":"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"}
|