@stablekernel/opencode-cursor 0.1.0-rc.1 → 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/CHANGELOG.md +23 -11
- package/README.md +60 -19
- package/dist/{chunk-YYO6O43T.js → chunk-D4YQ7ZEM.js} +135 -18
- package/dist/chunk-D4YQ7ZEM.js.map +1 -0
- package/dist/plugin/index.js +34 -17
- package/dist/plugin/index.js.map +1 -1
- package/dist/provider/index.d.ts +8 -7
- package/dist/provider/index.js +202 -47
- package/dist/provider/index.js.map +1 -1
- package/package.json +5 -1
- package/dist/chunk-YYO6O43T.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,10 +4,10 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
npm `next` dist-tag for validation ahead of the stable `0.1.0`.
|
|
7
|
+
## [0.1.0] — 2026-06-10
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
> Pre-releases: `0.1.0-rc.1` and `0.1.0-rc.2` were published to the npm `next`
|
|
10
|
+
> dist-tag for validation ahead of this stable release.
|
|
11
11
|
|
|
12
12
|
Initial public release. A complete opencode integration for Cursor built on the
|
|
13
13
|
official `@cursor/sdk`: a streaming chat provider, an auth/config/model plugin,
|
|
@@ -21,17 +21,24 @@ and a permission-gated delegation tool surface.
|
|
|
21
21
|
tool activity, usage). Implements both `doStream()` and `doGenerate()`.
|
|
22
22
|
- **Per-request controls** via `providerOptions.cursor` — `mode` (agent/plan),
|
|
23
23
|
`params`, and `thinking` level; works with opencode's model variant picker.
|
|
24
|
-
- **Model variants** auto-generated from `Cursor.models.list` parameters:
|
|
25
|
-
|
|
24
|
+
- **Model variants** auto-generated from `Cursor.models.list` parameters: one
|
|
25
|
+
per reasoning/effort level a model advertises (boolean params collapse to a
|
|
26
|
+
single on-variant). opencode's plan agent maps to Cursor plan mode.
|
|
26
27
|
- **Session reuse** (`session: true`) — keeps one Cursor agent per opencode
|
|
27
28
|
session via `Agent.resume()` across turns, with automatic fallback to a fresh
|
|
28
29
|
agent. A run wedged by a crashed/duplicate process is recovered by retrying
|
|
29
30
|
the send once with the SDK's `local.force` escape hatch.
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
- **Native diff viewer for Cursor edits (blocks mode).** A Cursor `edit` tool
|
|
32
|
+
call is now surfaced under opencode's registered `edit` tool with its real
|
|
33
|
+
unified diff in `metadata.diff`, so opencode renders its built-in diff viewer
|
|
34
|
+
instead of a generic block. The required `oldString`/`newString` (which Cursor
|
|
35
|
+
does not expose) are reconstructed from the diff; the call is provider-executed
|
|
36
|
+
so they are never applied to disk. Any edit without a usable diff (errors,
|
|
37
|
+
unexpected shapes, or a host without a registered `edit` tool) falls back to a
|
|
38
|
+
safe `cursor_edit` block. Other Cursor tools (shell/read/mcp/…) remain
|
|
39
|
+
prefixed `cursor_*` blocks.
|
|
40
|
+
- **`toolDisplay` provider option** (`"blocks"` default | `"reasoning"`):
|
|
41
|
+
- `"blocks"` (default) emits structured, provider-executed **dynamic** `tool-call` /
|
|
35
42
|
`tool-result` parts so opencode renders native tool blocks. Names are
|
|
36
43
|
`cursor_`-prefixed and sanitized (`shell` → `cursor_shell`,
|
|
37
44
|
`serena/find_symbol` → `cursor_serena_find_symbol`) so they can't collide
|
|
@@ -42,6 +49,10 @@ and a permission-gated delegation tool surface.
|
|
|
42
49
|
errored/cancelled mid-tool) is closed with a synthetic error result so the
|
|
43
50
|
block never dangles as "Tool execution aborted", and a run that ends with
|
|
44
51
|
status `error` surfaces the failure instead of finishing silently.
|
|
52
|
+
- `"reasoning"` renders Cursor's internal tool activity (including the real
|
|
53
|
+
MCP tool name) as concise `[tool] …` reasoning lines. Always safe — tool
|
|
54
|
+
calls never cross opencode's tool-execution boundary; the fallback for
|
|
55
|
+
older/non-V3 hosts (`provider.cursor.options.toolDisplay: "reasoning"`).
|
|
45
56
|
|
|
46
57
|
### Node sidecar (Bun compatibility)
|
|
47
58
|
|
|
@@ -56,7 +67,8 @@ and a permission-gated delegation tool surface.
|
|
|
56
67
|
|
|
57
68
|
### Plugin
|
|
58
69
|
|
|
59
|
-
- **opencode plugin** (`@stablekernel/opencode-cursor
|
|
70
|
+
- **opencode plugin** (`@stablekernel/opencode-cursor`, resolved via the package's
|
|
71
|
+
`./server` export): auth hook (API-key login;
|
|
60
72
|
the key is validated on first use rather than at login), config hook
|
|
61
73
|
(auto-injects `provider.cursor`),
|
|
62
74
|
`provider.models()` (live catalog via `Cursor.models.list`), and the
|
package/README.md
CHANGED
|
@@ -12,7 +12,8 @@ It uses your Cursor API key to:
|
|
|
12
12
|
- register a `cursor` provider in opencode,
|
|
13
13
|
- **list the models available to your account** (live, via `Cursor.models.list()`), and
|
|
14
14
|
- run chats through Cursor's local agent runtime (`Agent.create` / `agent.send`), streaming
|
|
15
|
-
text and reasoning back into opencode (Cursor's own tool activity is surfaced as
|
|
15
|
+
text and reasoning back into opencode (Cursor's own tool activity is surfaced as structured tool
|
|
16
|
+
blocks by default; see [Tool display](#tool-display)).
|
|
16
17
|
|
|
17
18
|
This plugin registers Cursor as a **native opencode provider**: its models appear in
|
|
18
19
|
`opencode models` and the model picker, and you talk to a Cursor model *directly* — with live model
|
|
@@ -52,7 +53,7 @@ opencode loads two things from this one package:
|
|
|
52
53
|
|
|
53
54
|
| opencode concept | What it loads | Export |
|
|
54
55
|
| --- | --- | --- |
|
|
55
|
-
| Plugin (`plugin` config) | auth + provider registration + dynamic model listing + a refresh tool | `@stablekernel/opencode-cursor
|
|
56
|
+
| Plugin (`plugin` config) | auth + provider registration + dynamic model listing + a refresh tool | `@stablekernel/opencode-cursor` (resolved via the package's `./server` export) |
|
|
56
57
|
| Provider (`provider.cursor.npm`) | a Vercel AI SDK `LanguageModelV3` that drives a local Cursor agent | `@stablekernel/opencode-cursor` (`createCursor`) |
|
|
57
58
|
|
|
58
59
|
The plugin's `config` hook registers `provider.cursor` (pointing `npm` at this package) and seeds
|
|
@@ -62,6 +63,25 @@ login. The `provider.models()` hook refreshes the catalog live once you're authe
|
|
|
62
63
|
|
|
63
64
|
## Install
|
|
64
65
|
|
|
66
|
+
### Quick install (one line)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
curl -fsSL https://raw.githubusercontent.com/stablekernel/opencode-cursor/main/install.sh | bash
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The script registers the plugin in your **global** `opencode.json`
|
|
73
|
+
(`~/.config/opencode/opencode.json`), checks for Node.js 22+ on your `PATH`, and offers to set
|
|
74
|
+
`CURSOR_API_KEY`. Flags:
|
|
75
|
+
|
|
76
|
+
- `--project` — write `./opencode.json` in the current directory instead of the global config.
|
|
77
|
+
- `--yes` / `-y` — non-interactive; skip all prompts.
|
|
78
|
+
|
|
79
|
+
It backs up an existing config before editing, is safe to re-run (idempotent), and uses `jq` when
|
|
80
|
+
available (falling back to Node). You can review it first:
|
|
81
|
+
[`install.sh`](./install.sh).
|
|
82
|
+
|
|
83
|
+
### Manual install
|
|
84
|
+
|
|
65
85
|
```bash
|
|
66
86
|
npm install @stablekernel/opencode-cursor
|
|
67
87
|
```
|
|
@@ -71,7 +91,7 @@ Add the plugin to your `opencode.json` (project or global):
|
|
|
71
91
|
```json
|
|
72
92
|
{
|
|
73
93
|
"$schema": "https://opencode.ai/config.json",
|
|
74
|
-
"plugin": ["@stablekernel/opencode-cursor
|
|
94
|
+
"plugin": ["@stablekernel/opencode-cursor"]
|
|
75
95
|
}
|
|
76
96
|
```
|
|
77
97
|
|
|
@@ -135,7 +155,7 @@ This plugin also registers two **delegation tools** that complement the provider
|
|
|
135
155
|
| `session` | `false` | Reuse one Cursor agent per opencode session (resume across turns; see below) |
|
|
136
156
|
| `forwardMcp` | `true` | Forward opencode's configured MCP servers to the Cursor agent |
|
|
137
157
|
| `mcpServers` | — | Extra MCP servers (Cursor `McpServerConfig` shape); merged with forwarded ones |
|
|
138
|
-
| `toolDisplay` | `"
|
|
158
|
+
| `toolDisplay` | `"blocks"` | How Cursor's internal tool activity is shown: `"blocks"` (structured provider-executed tool blocks; default, requires opencode 1.16+) or `"reasoning"` (compact lines, the fallback for older/non-V3 hosts). See [Tool display](#tool-display) |
|
|
139
159
|
|
|
140
160
|
### Session reuse (`session`)
|
|
141
161
|
|
|
@@ -156,10 +176,19 @@ opencode delivers per-request, provider-specific settings to the model under
|
|
|
156
176
|
- `params` → `{ <paramId>: value }` mapped to Cursor `ModelSelection.params`
|
|
157
177
|
- `thinking` → convenience, mapped to the `thinking` param
|
|
158
178
|
|
|
159
|
-
These are most naturally driven by opencode's **model variant picker**: the plugin auto-generates
|
|
160
|
-
|
|
161
|
-
(
|
|
162
|
-
|
|
179
|
+
These are most naturally driven by opencode's **model variant picker**: the plugin auto-generates
|
|
180
|
+
one variant per reasoning/effort level a model advertises (`Cursor.models.list()` parameters). A
|
|
181
|
+
boolean parameter (e.g. `thinking: ["false","true"]`) collapses to a single variant named after the
|
|
182
|
+
parameter that switches it on (the off state is the default — no variant selected); enum parameters
|
|
183
|
+
(e.g. `effort`, `reasoning`) produce one variant per value. Selecting a variant sends its settings
|
|
184
|
+
through `providerOptions.cursor`.
|
|
185
|
+
|
|
186
|
+
> **Plan mode is not a variant.** opencode's **plan agent** (toggled with `Tab`) is mapped to
|
|
187
|
+
> Cursor's plan mode automatically by the plugin's `chat.params` hook, so switching opencode into
|
|
188
|
+
> plan mode puts the Cursor agent into plan mode too. An explicit `mode` from a selected variant or
|
|
189
|
+
> model option still wins.
|
|
190
|
+
|
|
191
|
+
You can also set controls statically per model:
|
|
163
192
|
|
|
164
193
|
```json
|
|
165
194
|
{ "provider": { "cursor": { "models": {
|
|
@@ -302,31 +331,32 @@ no sidecar is spawned.
|
|
|
302
331
|
Cursor runs its own agent loop and executes its own tools. The `toolDisplay` option controls how
|
|
303
332
|
that activity appears in opencode:
|
|
304
333
|
|
|
305
|
-
- **`"
|
|
306
|
-
(`[tool] write {"path":…}`; failures as `[tool] x failed`). Robust on every host: no tool-call
|
|
307
|
-
parts cross into opencode, so there's no dependency on how the host treats provider-executed
|
|
308
|
-
tools.
|
|
309
|
-
- **`"blocks"` (opt-in)** — tool activity is emitted as structured, **provider-executed**
|
|
334
|
+
- **`"blocks"` (default)** — tool activity is emitted as structured, **provider-executed**
|
|
310
335
|
`tool-call`/`tool-result` parts so opencode renders proper, collapsible tool blocks with inputs
|
|
311
336
|
and outputs. opencode skips execution for provider-executed calls (they're display-only), so
|
|
312
|
-
Cursor's tools (`shell`, `mcp`, …) don't trigger an "unavailable tool" error.
|
|
337
|
+
Cursor's tools (`shell`, `mcp`, …) don't trigger an "unavailable tool" error. Requires a
|
|
313
338
|
V3-native opencode host (1.16+).
|
|
339
|
+
- **`"reasoning"` (fallback)** — each tool call is shown as a compact reasoning line
|
|
340
|
+
(`[tool] write {"path":…}`; failures as `[tool] x failed`). Robust on every host: no tool-call
|
|
341
|
+
parts cross into opencode, so there's no dependency on how the host treats provider-executed
|
|
342
|
+
tools. Use this on older/non-V3 opencode hosts.
|
|
314
343
|
|
|
315
|
-
|
|
344
|
+
The default needs no configuration. To force the reasoning fallback (e.g. on a pre-1.16 host):
|
|
316
345
|
|
|
317
346
|
```jsonc
|
|
318
347
|
{
|
|
319
348
|
"provider": {
|
|
320
349
|
"cursor": {
|
|
321
|
-
"options": { "toolDisplay": "
|
|
350
|
+
"options": { "toolDisplay": "reasoning" }
|
|
322
351
|
}
|
|
323
352
|
}
|
|
324
353
|
}
|
|
325
354
|
```
|
|
326
355
|
|
|
327
|
-
> Why
|
|
328
|
-
>
|
|
329
|
-
>
|
|
356
|
+
> Why blocks by default: structured tool blocks are the nicer experience and have been verified
|
|
357
|
+
> against opencode 1.16+. `"blocks"` depends on V3-native, provider-executed dynamic tool parts; if
|
|
358
|
+
> your host predates that (or renders them poorly), set `"toolDisplay": "reasoning"` — it requires
|
|
359
|
+
> nothing from the host and works everywhere.
|
|
330
360
|
|
|
331
361
|
## Troubleshooting
|
|
332
362
|
|
|
@@ -336,6 +366,17 @@ Enable blocks mode in your opencode config:
|
|
|
336
366
|
with `OPENCODE_CURSOR_SIDECAR=1`.
|
|
337
367
|
- **"Running under Bun without a usable Node sidecar" warning.** Install Node.js 22+, or set
|
|
338
368
|
`OPENCODE_CURSOR_SIDECAR=0` to accept in-process behavior and silence the warning.
|
|
369
|
+
- **"Could not locate the bindings file" / `node_sqlite3.node` not found.** `@cursor/sdk` depends on
|
|
370
|
+
the native `sqlite3` addon, and opencode installs plugins with Bun, which skips sqlite3's install
|
|
371
|
+
script — so the prebuilt binary may be missing. The plugin detects this and self-heals on first SDK
|
|
372
|
+
load by running sqlite3's own `prebuild-install -r napi` under your system Node (requires Node on
|
|
373
|
+
`PATH`). If it can't (no Node, offline), it logs a one-line manual fix: `cd` into the printed
|
|
374
|
+
sqlite3 directory and run `npx prebuild-install -r napi` (or `npm rebuild sqlite3`). Set
|
|
375
|
+
`OPENCODE_CURSOR_DEBUG=1` to see the repair output.
|
|
376
|
+
- **Plugin looks enabled but no `cursor` provider/models appear.** opencode caches a plugin by its
|
|
377
|
+
install spec under `~/.cache/opencode/packages/`; a stale cache from an older version can persist.
|
|
378
|
+
Pin an exact version (`@stablekernel/opencode-cursor@<version>`) or delete the cached dir and
|
|
379
|
+
restart so opencode reinstalls.
|
|
339
380
|
- **Only the four fallback models appear in the picker.** The live catalog loads after the first
|
|
340
381
|
authenticated use — restart opencode once after logging in, or run `cursor_refresh_models` to
|
|
341
382
|
force a refresh.
|
|
@@ -157,28 +157,139 @@ function resolveControls(modelId, staticControls, providerOptions) {
|
|
|
157
157
|
return { mode, modelSelection: buildModelSelection(modelId, params) };
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
// src/
|
|
160
|
+
// src/native-binding.ts
|
|
161
|
+
import { execSync, spawn } from "child_process";
|
|
162
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
163
|
+
import { createRequire } from "module";
|
|
164
|
+
import { dirname, join } from "path";
|
|
165
|
+
var BINDING_ROOTS = ["build", "lib/binding", "compiled"];
|
|
166
|
+
function hasNodeFile(dir, depth) {
|
|
167
|
+
if (depth < 0) return false;
|
|
168
|
+
let entries;
|
|
169
|
+
try {
|
|
170
|
+
entries = readdirSync(dir);
|
|
171
|
+
} catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
for (const entry of entries) {
|
|
175
|
+
const path = join(dir, entry);
|
|
176
|
+
if (entry.endsWith(".node")) {
|
|
177
|
+
try {
|
|
178
|
+
if (statSync(path).isFile()) return true;
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
if (statSync(path).isDirectory() && hasNodeFile(path, depth - 1)) return true;
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
function hasSqliteBinding(sqliteDir) {
|
|
191
|
+
return BINDING_ROOTS.some((root) => hasNodeFile(join(sqliteDir, root), 3));
|
|
192
|
+
}
|
|
193
|
+
function resolveSqliteDir() {
|
|
194
|
+
const req = createRequire(import.meta.url);
|
|
195
|
+
try {
|
|
196
|
+
const sdkPkg = req.resolve("@cursor/sdk/package.json");
|
|
197
|
+
return dirname(createRequire(sdkPkg).resolve("sqlite3/package.json"));
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
return dirname(req.resolve("sqlite3/package.json"));
|
|
202
|
+
} catch {
|
|
203
|
+
return void 0;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function detectNodeExecutable() {
|
|
207
|
+
const isBun = typeof globalThis.Bun !== "undefined";
|
|
208
|
+
if (!isBun) return process.execPath;
|
|
209
|
+
try {
|
|
210
|
+
const out = execSync(process.platform === "win32" ? "where node" : "command -v node", {
|
|
211
|
+
encoding: "utf8",
|
|
212
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
213
|
+
}).trim();
|
|
214
|
+
return out.split("\n")[0] || process.execPath;
|
|
215
|
+
} catch {
|
|
216
|
+
return process.execPath;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function runPrebuildInstall(sqliteDir) {
|
|
220
|
+
let bin;
|
|
221
|
+
try {
|
|
222
|
+
const req = createRequire(join(sqliteDir, "package.json"));
|
|
223
|
+
const pkgPath = req.resolve("prebuild-install/package.json");
|
|
224
|
+
const pkg = await import(pkgPath, { with: { type: "json" } });
|
|
225
|
+
const binField = pkg.default.bin;
|
|
226
|
+
const rel = typeof binField === "string" ? binField : binField?.["prebuild-install"];
|
|
227
|
+
if (!rel) return false;
|
|
228
|
+
bin = join(dirname(pkgPath), rel);
|
|
229
|
+
} catch {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
if (!existsSync(bin)) return false;
|
|
233
|
+
return new Promise((resolve) => {
|
|
234
|
+
const child = spawn(detectNodeExecutable(), [bin, "-r", "napi"], {
|
|
235
|
+
cwd: sqliteDir,
|
|
236
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
237
|
+
});
|
|
238
|
+
let stderr = "";
|
|
239
|
+
child.stderr?.on("data", (chunk) => {
|
|
240
|
+
stderr += chunk.toString();
|
|
241
|
+
});
|
|
242
|
+
child.on("error", () => resolve(false));
|
|
243
|
+
child.on("exit", (code) => {
|
|
244
|
+
if (code !== 0 && stderr && process.env["OPENCODE_CURSOR_DEBUG"]) {
|
|
245
|
+
console.error(`[opencode-cursor] prebuild-install stderr: ${stderr.trim()}`);
|
|
246
|
+
}
|
|
247
|
+
resolve(code === 0);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
}
|
|
161
251
|
var cached;
|
|
252
|
+
function ensureSqliteBinding(options = {}) {
|
|
253
|
+
cached ??= (async () => {
|
|
254
|
+
const log = options.log ?? ((message) => console.error(message));
|
|
255
|
+
const sqliteDir = options.sqliteDir ?? resolveSqliteDir();
|
|
256
|
+
if (!sqliteDir || !existsSync(join(sqliteDir, "package.json"))) {
|
|
257
|
+
return "not-found";
|
|
258
|
+
}
|
|
259
|
+
if (hasSqliteBinding(sqliteDir)) return "present";
|
|
260
|
+
const run = options.run ?? runPrebuildInstall;
|
|
261
|
+
const ok = await run(sqliteDir).catch(() => false);
|
|
262
|
+
if (ok && hasSqliteBinding(sqliteDir)) return "repaired";
|
|
263
|
+
log(
|
|
264
|
+
`[opencode-cursor] sqlite3 native binding is missing in ${sqliteDir} and automatic repair failed. @cursor/sdk will not load. Fix manually with: cd ${sqliteDir} && npx prebuild-install -r napi (or: npm rebuild sqlite3)`
|
|
265
|
+
);
|
|
266
|
+
return "failed";
|
|
267
|
+
})();
|
|
268
|
+
return cached;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// src/cursor-runtime.ts
|
|
272
|
+
var cached2;
|
|
162
273
|
async function loadCursorSdk() {
|
|
163
|
-
if (!
|
|
164
|
-
|
|
165
|
-
|
|
274
|
+
if (!cached2) {
|
|
275
|
+
cached2 = ensureSqliteBinding().then(() => import("@cursor/sdk")).catch((err) => {
|
|
276
|
+
cached2 = void 0;
|
|
166
277
|
const detail = err instanceof Error ? err.message : String(err);
|
|
167
278
|
throw new Error(
|
|
168
279
|
`[opencode-cursor] Failed to load "@cursor/sdk". Make sure it is installed (\`npm install @cursor/sdk\`). Original error: ${detail}`
|
|
169
280
|
);
|
|
170
281
|
});
|
|
171
282
|
}
|
|
172
|
-
return
|
|
283
|
+
return cached2;
|
|
173
284
|
}
|
|
174
285
|
|
|
175
286
|
// src/provider/agent-backend.ts
|
|
176
|
-
import { execSync } from "child_process";
|
|
177
|
-
import { existsSync } from "fs";
|
|
287
|
+
import { execSync as execSync2 } from "child_process";
|
|
288
|
+
import { existsSync as existsSync2 } from "fs";
|
|
178
289
|
import { fileURLToPath } from "url";
|
|
179
290
|
|
|
180
291
|
// src/provider/sidecar-client.ts
|
|
181
|
-
import { spawn } from "child_process";
|
|
292
|
+
import { spawn as spawn2 } from "child_process";
|
|
182
293
|
import { createInterface } from "readline";
|
|
183
294
|
function reviveError(error) {
|
|
184
295
|
const e = error ?? {};
|
|
@@ -200,7 +311,7 @@ var SidecarClient = class {
|
|
|
200
311
|
ensureChild() {
|
|
201
312
|
if (this.disposed) throw new Error("cursor sidecar client disposed");
|
|
202
313
|
if (this.child) return this.child;
|
|
203
|
-
const child =
|
|
314
|
+
const child = spawn2(this.options.nodePath ?? "node", [this.options.scriptPath], {
|
|
204
315
|
stdio: ["pipe", "pipe", "pipe"],
|
|
205
316
|
env: { ...process.env, ...this.options.env }
|
|
206
317
|
});
|
|
@@ -377,7 +488,7 @@ function resolveBackendKind(env) {
|
|
|
377
488
|
}
|
|
378
489
|
function detectNode() {
|
|
379
490
|
try {
|
|
380
|
-
const out =
|
|
491
|
+
const out = execSync2(process.platform === "win32" ? "where node" : "command -v node", {
|
|
381
492
|
encoding: "utf8",
|
|
382
493
|
stdio: ["ignore", "pipe", "ignore"]
|
|
383
494
|
}).trim();
|
|
@@ -415,7 +526,7 @@ function resolveSidecarScript() {
|
|
|
415
526
|
];
|
|
416
527
|
for (const candidate of candidates) {
|
|
417
528
|
const path = fileURLToPath(new URL(candidate, import.meta.url));
|
|
418
|
-
if (
|
|
529
|
+
if (existsSync2(path)) return path;
|
|
419
530
|
}
|
|
420
531
|
return void 0;
|
|
421
532
|
}
|
|
@@ -423,13 +534,19 @@ function sidecarBackend(nodePath, scriptPath) {
|
|
|
423
534
|
const client = new SidecarClient({ scriptPath, nodePath });
|
|
424
535
|
return {
|
|
425
536
|
kind: "sidecar",
|
|
426
|
-
createAgent: (options) =>
|
|
427
|
-
|
|
537
|
+
createAgent: async (options) => {
|
|
538
|
+
await ensureSqliteBinding();
|
|
539
|
+
return client.createAgent(options);
|
|
540
|
+
},
|
|
541
|
+
resumeAgent: async (agentId, options) => {
|
|
542
|
+
await ensureSqliteBinding();
|
|
543
|
+
return client.resumeAgent(agentId, options);
|
|
544
|
+
}
|
|
428
545
|
};
|
|
429
546
|
}
|
|
430
|
-
var
|
|
547
|
+
var cached3;
|
|
431
548
|
function loadAgentBackend() {
|
|
432
|
-
if (!
|
|
549
|
+
if (!cached3) {
|
|
433
550
|
const env = detectEnvironment();
|
|
434
551
|
const kind = resolveBackendKind(env);
|
|
435
552
|
const scriptPath = kind === "sidecar" ? resolveSidecarScript() : void 0;
|
|
@@ -440,9 +557,9 @@ function loadAgentBackend() {
|
|
|
440
557
|
`[opencode-cursor] Running under Bun without a usable Node sidecar (node: ${env.nodePath ?? "not found"}, script: ${scriptPath ?? "not found"}): Cursor native tool calls may fail (Bun node:http2 incompatibility). Install Node.js to enable the sidecar, or set OPENCODE_CURSOR_SIDECAR=0 to silence this warning.`
|
|
441
558
|
);
|
|
442
559
|
}
|
|
443
|
-
|
|
560
|
+
cached3 = kind === "sidecar" && env.nodePath && scriptPath ? sidecarBackend(env.nodePath, scriptPath) : inProcessBackend();
|
|
444
561
|
}
|
|
445
|
-
return
|
|
562
|
+
return cached3;
|
|
446
563
|
}
|
|
447
564
|
|
|
448
565
|
// src/provider/session-pool.ts
|
|
@@ -499,4 +616,4 @@ export {
|
|
|
499
616
|
loadCursorSdk,
|
|
500
617
|
acquireAgent
|
|
501
618
|
};
|
|
502
|
-
//# sourceMappingURL=chunk-
|
|
619
|
+
//# sourceMappingURL=chunk-D4YQ7ZEM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-key.ts","../src/provider/agent-events.ts","../src/provider/controls.ts","../src/native-binding.ts","../src/cursor-runtime.ts","../src/provider/agent-backend.ts","../src/provider/sidecar-client.ts","../src/provider/session-pool.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\n/** Environment variable the Cursor SDK itself reads as a fallback. */\nexport const CURSOR_API_KEY_ENV_VAR = \"CURSOR_API_KEY\";\n\n/**\n * Values that are *not* real keys but rather instructions to read the key from\n * the environment. opencode config commonly stores literal `{env:...}` style\n * placeholders, and users sometimes paste the variable name itself.\n */\nconst PLACEHOLDERS = new Set<string>([\n CURSOR_API_KEY_ENV_VAR,\n `$${CURSOR_API_KEY_ENV_VAR}`,\n `\\${${CURSOR_API_KEY_ENV_VAR}}`,\n]);\n\n/**\n * Resolve a usable Cursor API key.\n *\n * Resolution order: an explicit, non-placeholder candidate (e.g. from opencode\n * auth storage or provider options) wins; otherwise fall back to the\n * `CURSOR_API_KEY` environment variable. Returns `undefined` when no key is\n * available so callers can present a clear \"needs auth\" path.\n *\n * The key is never logged or persisted by this module.\n */\nexport function resolveCursorApiKey(candidate?: string | null): string | undefined {\n const trimmed = candidate?.trim();\n if (trimmed && !PLACEHOLDERS.has(trimmed)) return trimmed;\n const fromEnv = process.env[CURSOR_API_KEY_ENV_VAR]?.trim();\n return fromEnv ? fromEnv : undefined;\n}\n\n/**\n * Produce a short, non-reversible fingerprint of an API key. Used purely to key\n * the on-disk model cache so the cache invalidates when the key changes. The\n * raw key is never written to disk.\n */\nexport function fingerprintApiKey(apiKey: string): string {\n return createHash(\"sha256\").update(apiKey).digest(\"hex\").slice(0, 16);\n}\n","import type { AgentModeOption, SDKUserMessage } from \"@cursor/sdk\";\nimport type { AgentLike, AgentRunLike } from \"./agent-backend.js\";\n\n/** Token usage as reported by Cursor's `turn-ended` update. */\nexport interface CursorUsage {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n}\n\n/** Normalized events bridged from the Cursor SDK's push callbacks. */\nexport type CursorEvent =\n | { type: \"text-delta\"; text: string }\n | { type: \"reasoning-delta\"; text: string }\n | { type: \"tool-call\"; id: string; name: string; input: unknown }\n | { type: \"tool-result\"; id: string; name: string; result: unknown; isError: boolean }\n | { type: \"usage\"; usage: CursorUsage }\n | { type: \"finish\"; text?: string };\n\nexport interface StreamAgentTurnOptions {\n mode: AgentModeOption;\n abortSignal?: AbortSignal;\n}\n\n/**\n * Human-readable name for a Cursor tool call. Most Cursor tools carry their\n * name in `toolCall.type` (shell/read/edit/…), but an MCP tool call has\n * `type: \"mcp\"` with the real tool in `args.toolName` (and server in\n * `args.providerIdentifier`) — surface that instead of the literal \"mcp\".\n */\nfunction toolDisplayName(toolCall: ({ type?: string } & Record<string, any>) | undefined): string {\n if (!toolCall) return \"tool\";\n if (toolCall.type === \"mcp\") {\n const name = toolCall.args?.toolName;\n const server = toolCall.args?.providerIdentifier;\n if (name) return server ? `${server}/${name}` : String(name);\n return \"mcp\";\n }\n return toolCall.type ?? \"tool\";\n}\n\n/**\n * Stream a single turn on an already-acquired Cursor agent and yield normalized\n * events. The agent's lifecycle (create/resume/close) is owned by the caller\n * (see session-pool.ts) so it can be reused across turns. The SDK streams via\n * `onDelta` callbacks; we bridge those into a pull-based async generator so both\n * `doStream` and `doGenerate` can consume them.\n */\nexport async function* streamAgentTurn(\n agent: AgentLike,\n message: SDKUserMessage,\n options: StreamAgentTurnOptions,\n): AsyncGenerator<CursorEvent> {\n const queue: CursorEvent[] = [];\n let wake: (() => void) | undefined;\n let finished = false;\n let failure: unknown;\n\n // Opt-in stderr tracing of what the live agent emits (set OPENCODE_CURSOR_DEBUG=1).\n const debug = process.env.OPENCODE_CURSOR_DEBUG === \"1\";\n const counts: Record<string, number> = {};\n\n const push = (event: CursorEvent) => {\n queue.push(event);\n wake?.();\n wake = undefined;\n };\n\n const onDelta = ({ update }: { update: { type: string } & Record<string, any> }) => {\n if (debug) counts[update.type] = (counts[update.type] ?? 0) + 1;\n switch (update.type) {\n case \"text-delta\":\n push({ type: \"text-delta\", text: update.text });\n break;\n case \"thinking-delta\":\n push({ type: \"reasoning-delta\", text: update.text });\n break;\n case \"tool-call-started\":\n push({\n type: \"tool-call\",\n id: String(update.callId),\n name: toolDisplayName(update.toolCall),\n input: update.toolCall?.args ?? {},\n });\n break;\n case \"tool-call-completed\": {\n const tool = update.toolCall ?? {};\n const result = tool.result;\n // MCP failures often arrive as {status:\"success\", value:{isError:true}}\n // (the MCP-protocol error flag), not as a top-level status error.\n const mcpError = tool.type === \"mcp\" && result?.value?.isError === true;\n push({\n type: \"tool-result\",\n id: String(update.callId),\n name: toolDisplayName(tool),\n result: result ?? null,\n isError: result?.status === \"error\" || mcpError,\n });\n break;\n }\n case \"turn-ended\":\n if (update.usage) push({ type: \"usage\", usage: update.usage as CursorUsage });\n break;\n }\n };\n\n const runHolder: { run?: AgentRunLike } = {};\n const onAbort = () => {\n void Promise.resolve(runHolder.run?.cancel()).catch(() => {});\n };\n options.abortSignal?.addEventListener(\"abort\", onAbort);\n\n // A previous opencode/CLI crash (or a second instance racing on the same\n // agent store) can leave a persisted run wedged; the SDK then rejects new\n // sends with AgentBusyError. Retry once with the SDK's documented recovery\n // path (local.force expires the wedged run) instead of failing the turn.\n const sendTurn = async (): Promise<AgentRunLike> => {\n try {\n return await agent.send(message, { mode: options.mode, onDelta });\n } catch (err) {\n if (err instanceof Error && err.name === \"AgentBusyError\") {\n if (debug) console.error(\"[cursor:debug] agent busy; retrying send with local.force\");\n return agent.send(message, { mode: options.mode, onDelta, local: { force: true } });\n }\n throw err;\n }\n };\n\n // Kick off the turn. Resolve text from run.wait() for models that don't emit\n // incremental text deltas.\n void sendTurn()\n .then(async (run) => {\n runHolder.run = run;\n const result = await run.wait();\n if (debug) {\n console.error(\n `[cursor:debug] updates=${JSON.stringify(counts)} status=${result.status} resultLen=${(result.result ?? \"\").length}`,\n );\n }\n if (result.status === \"error\") {\n // Surface the failure instead of finishing silently — a silent stop\n // leaves opencode showing dangling tool calls with no explanation.\n throw new Error(\n `Cursor run ended with status \"error\"${result.result ? `: ${result.result}` : \"\"}`,\n );\n }\n // A cancelled run finishes without fabricating final text.\n push({ type: \"finish\", ...(result.status === \"cancelled\" ? {} : { text: result.result }) });\n })\n .catch((err) => {\n failure = err;\n if (debug) console.error(`[cursor:debug] send failed: ${err instanceof Error ? err.message : String(err)}`);\n })\n .finally(() => {\n finished = true;\n wake?.();\n wake = undefined;\n });\n\n try {\n while (true) {\n if (queue.length > 0) {\n yield queue.shift()!;\n continue;\n }\n if (finished) break;\n await new Promise<void>((resolve) => {\n wake = resolve;\n });\n }\n // Drain anything queued right before completion.\n while (queue.length > 0) yield queue.shift()!;\n if (failure) throw failure;\n } finally {\n options.abortSignal?.removeEventListener(\"abort\", onAbort);\n }\n}\n","import type { AgentModeOption, ModelSelection } from \"@cursor/sdk\";\n\n/** Per-model static control defaults (from provider/model config options). */\nexport interface StaticControls {\n mode: AgentModeOption;\n /** Default Cursor model params (id -> value), e.g. { thinking: \"high\" }. */\n params?: Record<string, string>;\n}\n\nexport interface ResolvedControls {\n mode: AgentModeOption;\n modelSelection: ModelSelection;\n}\n\n/**\n * Build a Cursor `ModelSelection` from a model id and an optional map of model\n * params (e.g. `{ thinking: \"high\" }`). Shared by the provider control\n * resolution and the cloud/delegate tools so param handling stays consistent.\n */\nexport function buildModelSelection(\n modelId: string,\n params?: Record<string, string>,\n): ModelSelection {\n const paramList = Object.entries(params ?? {}).map(([id, value]) => ({ id, value }));\n return paramList.length > 0 ? { id: modelId, params: paramList } : { id: modelId };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isMode(value: unknown): value is AgentModeOption {\n return value === \"agent\" || value === \"plan\";\n}\n\n/**\n * Resolve the per-turn Cursor controls from static config plus opencode's\n * per-request `providerOptions.cursor` (which carries merged model `options` and\n * the selected model `variant`). Per-request values win over static defaults.\n *\n * Recognized keys in `providerOptions.cursor`:\n * - `mode`: \"agent\" | \"plan\"\n * - `params`: Record<string,string> of Cursor model params (e.g. { thinking: \"high\" })\n * - `thinking`: string convenience, mapped to the `thinking` param if not already set\n */\nexport function resolveControls(\n modelId: string,\n staticControls: StaticControls,\n providerOptions: Record<string, unknown> | undefined,\n): ResolvedControls {\n const po = providerOptions ?? {};\n\n const mode: AgentModeOption = isMode(po[\"mode\"]) ? po[\"mode\"] : staticControls.mode;\n\n const params: Record<string, string> = { ...(staticControls.params ?? {}) };\n if (isRecord(po[\"params\"])) {\n for (const [key, value] of Object.entries(po[\"params\"])) {\n if (value != null) params[key] = String(value);\n }\n }\n if (typeof po[\"thinking\"] === \"string\" && params[\"thinking\"] === undefined) {\n params[\"thinking\"] = po[\"thinking\"];\n }\n\n return { mode, modelSelection: buildModelSelection(modelId, params) };\n}\n","/**\n * Self-heal for sqlite3's native binding.\n *\n * `@cursor/sdk` depends on `sqlite3` (a native addon). opencode installs\n * plugin packages with Bun, which does not run sqlite3's `install` lifecycle\n * script (`prebuild-install -r napi || node-gyp rebuild`), so the installed\n * tree has **no** `node_sqlite3.node` binary and the SDK crashes at import\n * with \"Could not locate the bindings file\".\n *\n * Before loading the SDK (in-process or via the Node sidecar) we check for a\n * binding and, when it is missing, run sqlite3's own `prebuild-install -r napi`\n * to fetch the prebuilt NAPI binary (ABI-portable across Node versions, also\n * loadable by Bun). Failures degrade to a clear warning; the SDK import then\n * surfaces its own error.\n */\nimport { execSync, spawn } from \"node:child_process\";\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\n\nexport type EnsureResult = \"present\" | \"repaired\" | \"failed\" | \"not-found\";\n\nexport interface EnsureOptions {\n /** Override the sqlite3 package directory (tests). */\n sqliteDir?: string;\n /** Override the repair runner (tests). Returns true when the command succeeded. */\n run?: (sqliteDir: string) => Promise<boolean>;\n /** Override the warning sink (tests). */\n log?: (message: string) => void;\n}\n\n/** Directories (relative to the sqlite3 package root) that may hold the binding. */\nconst BINDING_ROOTS = [\"build\", \"lib/binding\", \"compiled\"];\n\nfunction hasNodeFile(dir: string, depth: number): boolean {\n if (depth < 0) return false;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return false;\n }\n for (const entry of entries) {\n const path = join(dir, entry);\n if (entry.endsWith(\".node\")) {\n try {\n if (statSync(path).isFile()) return true;\n } catch {\n // ignore unreadable entries\n }\n continue;\n }\n try {\n if (statSync(path).isDirectory() && hasNodeFile(path, depth - 1)) return true;\n } catch {\n // ignore unreadable entries\n }\n }\n return false;\n}\n\n/** True when the sqlite3 package dir contains a compiled `.node` binding. */\nexport function hasSqliteBinding(sqliteDir: string): boolean {\n return BINDING_ROOTS.some((root) => hasNodeFile(join(sqliteDir, root), 3));\n}\n\n/**\n * Locate the sqlite3 package directory that `@cursor/sdk` will load, walking\n * the same resolution chain (our module -> @cursor/sdk -> sqlite3).\n */\nexport function resolveSqliteDir(): string | undefined {\n const req = createRequire(import.meta.url);\n try {\n const sdkPkg = req.resolve(\"@cursor/sdk/package.json\");\n return dirname(createRequire(sdkPkg).resolve(\"sqlite3/package.json\"));\n } catch {\n // fall through: try resolving sqlite3 directly (hoisted installs)\n }\n try {\n return dirname(req.resolve(\"sqlite3/package.json\"));\n } catch {\n return undefined;\n }\n}\n\nfunction detectNodeExecutable(): string {\n const isBun = typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\";\n if (!isBun) return process.execPath;\n // Under Bun prefer a real Node (matches the sidecar runtime); prebuild-install\n // itself is plain JS, so Bun works as a last resort.\n try {\n const out = execSync(process.platform === \"win32\" ? \"where node\" : \"command -v node\", {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n return out.split(\"\\n\")[0] || process.execPath;\n } catch {\n return process.execPath;\n }\n}\n\n/** Default repair: run sqlite3's own `prebuild-install -r napi` in its package dir. */\nasync function runPrebuildInstall(sqliteDir: string): Promise<boolean> {\n let bin: string;\n try {\n const req = createRequire(join(sqliteDir, \"package.json\"));\n const pkgPath = req.resolve(\"prebuild-install/package.json\");\n const pkg = (await import(pkgPath, { with: { type: \"json\" } })) as {\n default: { bin?: string | Record<string, string> };\n };\n const binField = pkg.default.bin;\n const rel = typeof binField === \"string\" ? binField : binField?.[\"prebuild-install\"];\n if (!rel) return false;\n bin = join(dirname(pkgPath), rel);\n } catch {\n return false;\n }\n if (!existsSync(bin)) return false;\n\n return new Promise<boolean>((resolve) => {\n const child = spawn(detectNodeExecutable(), [bin, \"-r\", \"napi\"], {\n cwd: sqliteDir,\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n });\n let stderr = \"\";\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => {\n if (code !== 0 && stderr && process.env[\"OPENCODE_CURSOR_DEBUG\"]) {\n console.error(`[opencode-cursor] prebuild-install stderr: ${stderr.trim()}`);\n }\n resolve(code === 0);\n });\n });\n}\n\nlet cached: Promise<EnsureResult> | undefined;\n\n/**\n * Ensure the sqlite3 native binding exists, repairing it once per process if\n * needed. Never throws; \"failed\"/\"not-found\" outcomes warn and let the SDK\n * import surface its own error.\n */\nexport function ensureSqliteBinding(options: EnsureOptions = {}): Promise<EnsureResult> {\n cached ??= (async () => {\n const log = options.log ?? ((message: string) => console.error(message));\n const sqliteDir = options.sqliteDir ?? resolveSqliteDir();\n if (!sqliteDir || !existsSync(join(sqliteDir, \"package.json\"))) {\n return \"not-found\";\n }\n if (hasSqliteBinding(sqliteDir)) return \"present\";\n\n const run = options.run ?? runPrebuildInstall;\n const ok = await run(sqliteDir).catch(() => false);\n if (ok && hasSqliteBinding(sqliteDir)) return \"repaired\";\n\n log(\n `[opencode-cursor] sqlite3 native binding is missing in ${sqliteDir} and automatic ` +\n `repair failed. @cursor/sdk will not load. Fix manually with: ` +\n `cd ${sqliteDir} && npx prebuild-install -r napi (or: npm rebuild sqlite3)`,\n );\n return \"failed\";\n })();\n return cached;\n}\n\n/** Test hook. */\nexport function resetNativeBinding(): void {\n cached = undefined;\n}\n","/**\n * Lazy loader for the official Cursor SDK (`@cursor/sdk`).\n *\n * The SDK is heavy and only needed once a Cursor model is actually used or\n * models are discovered, so it is imported on demand. A failed import (e.g. the\n * dependency is missing) degrades gracefully into a clear error instead of\n * crashing opencode at startup.\n */\nimport { ensureSqliteBinding } from \"./native-binding.js\";\n\nexport type CursorSdkModule = typeof import(\"@cursor/sdk\");\n\nlet cached: Promise<CursorSdkModule> | undefined;\n\nexport async function loadCursorSdk(): Promise<CursorSdkModule> {\n if (!cached) {\n // @cursor/sdk eagerly requires sqlite3 (native addon); opencode's Bun\n // install skips its build script, so repair the binding first if missing.\n cached = ensureSqliteBinding()\n .then(() => import(\"@cursor/sdk\"))\n .catch((err: unknown) => {\n // Allow a later retry if the failure was transient.\n cached = undefined;\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(\n `[opencode-cursor] Failed to load \"@cursor/sdk\". Make sure it is installed ` +\n `(\\`npm install @cursor/sdk\\`). Original error: ${detail}`,\n );\n });\n }\n return cached;\n}\n","/**\n * Selects where Cursor agents run:\n *\n * - \"in-process\": straight through `@cursor/sdk` in this process (Node — the\n * normal path for tests, scripts, and any non-Bun host).\n * - \"sidecar\": a spawned Node child hosting the SDK (Bun — opencode's runtime —\n * has a `node:http2` bug that kills Cursor's streaming RPC with\n * NGHTTP2_FRAME_SIZE_ERROR, losing tool-completion updates; see\n * src/sidecar/agent-host.mjs).\n *\n * Override with OPENCODE_CURSOR_SIDECAR=1/0 (force on/off).\n */\nimport { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { loadCursorSdk } from \"../cursor-runtime.js\";\nimport { ensureSqliteBinding } from \"../native-binding.js\";\nimport { SidecarClient, type AgentLike } from \"./sidecar-client.js\";\n\nexport type { AgentLike, AgentRunLike, AgentSendOptions } from \"./sidecar-client.js\";\n\nexport type BackendKind = \"in-process\" | \"sidecar\";\n\nexport interface AgentBackend {\n kind: BackendKind;\n createAgent(options: unknown): Promise<AgentLike>;\n resumeAgent(agentId: string, options: unknown): Promise<AgentLike>;\n}\n\nexport interface BackendEnvironment {\n isBun: boolean;\n /** Resolved node executable, or undefined when not on PATH. */\n nodePath: string | undefined;\n}\n\n/** Pure selection logic (unit-testable without spawning anything). */\nexport function resolveBackendKind(env: BackendEnvironment): BackendKind {\n const override = process.env[\"OPENCODE_CURSOR_SIDECAR\"];\n if (override === \"0\" || override === \"false\") return \"in-process\";\n if (override === \"1\" || override === \"true\") return env.nodePath ? \"sidecar\" : \"in-process\";\n return env.isBun && env.nodePath ? \"sidecar\" : \"in-process\";\n}\n\nfunction detectNode(): string | undefined {\n try {\n const out = execSync(process.platform === \"win32\" ? \"where node\" : \"command -v node\", {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n return out.split(\"\\n\")[0] || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction detectEnvironment(): BackendEnvironment {\n const isBun = typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\";\n // Only pay the PATH lookup when the answer can matter.\n const needsNode = isBun || process.env[\"OPENCODE_CURSOR_SIDECAR\"] === \"1\";\n return { isBun, nodePath: needsNode ? detectNode() : process.execPath };\n}\n\nfunction inProcessBackend(): AgentBackend {\n return {\n kind: \"in-process\",\n createAgent: async (options) => {\n const { Agent } = await loadCursorSdk();\n return (await Agent.create(options as never)) as unknown as AgentLike;\n },\n resumeAgent: async (agentId, options) => {\n const { Agent } = await loadCursorSdk();\n return (await Agent.resume(agentId, options as never)) as unknown as AgentLike;\n },\n };\n}\n\n/**\n * Locate the sidecar script across layouts: tsup may place this module in\n * dist/provider/index.js or hoist it into a root-level dist/chunk-*.js, and in\n * dev/tests it runs straight from src/. Try each known relative position.\n */\nexport function resolveSidecarScript(): string | undefined {\n const candidates = [\n \"./sidecar/agent-host.js\", // importer is a chunk at dist root\n \"../sidecar/agent-host.js\", // importer is dist/provider/index.js\n \"../sidecar/agent-host.mjs\", // importer is src/provider/*.ts (dev/tests)\n ];\n for (const candidate of candidates) {\n const path = fileURLToPath(new URL(candidate, import.meta.url));\n if (existsSync(path)) return path;\n }\n return undefined;\n}\n\nfunction sidecarBackend(nodePath: string, scriptPath: string): AgentBackend {\n const client = new SidecarClient({ scriptPath, nodePath });\n // The sidecar imports @cursor/sdk (which eagerly requires sqlite3's native\n // binding) in the child process; repair the binding before first use.\n return {\n kind: \"sidecar\",\n createAgent: async (options) => {\n await ensureSqliteBinding();\n return client.createAgent(options);\n },\n resumeAgent: async (agentId, options) => {\n await ensureSqliteBinding();\n return client.resumeAgent(agentId, options);\n },\n };\n}\n\nlet cached: AgentBackend | undefined;\n\n/** Resolve (and cache) the agent backend for this process. */\nexport function loadAgentBackend(): AgentBackend {\n if (!cached) {\n const env = detectEnvironment();\n const kind = resolveBackendKind(env);\n const scriptPath = kind === \"sidecar\" ? resolveSidecarScript() : undefined;\n // A user who explicitly opted out (OPENCODE_CURSOR_SIDECAR=0/false) has\n // accepted the in-process behavior and should not be warned.\n const override = process.env[\"OPENCODE_CURSOR_SIDECAR\"];\n const optedOut = override === \"0\" || override === \"false\";\n if (env.isBun && !optedOut && (kind === \"in-process\" || !scriptPath)) {\n console.error(\n \"[opencode-cursor] Running under Bun without a usable Node sidecar \" +\n `(node: ${env.nodePath ?? \"not found\"}, script: ${scriptPath ?? \"not found\"}): ` +\n \"Cursor native tool calls may fail (Bun node:http2 incompatibility). \" +\n \"Install Node.js to enable the sidecar, or set OPENCODE_CURSOR_SIDECAR=0 \" +\n \"to silence this warning.\",\n );\n }\n cached =\n kind === \"sidecar\" && env.nodePath && scriptPath\n ? sidecarBackend(env.nodePath, scriptPath)\n : inProcessBackend();\n }\n return cached;\n}\n\n/** Test hook. */\nexport function resetAgentBackend(): void {\n cached = undefined;\n}\n","/**\n * Client half of the Node sidecar (see src/sidecar/agent-host.mjs for the\n * protocol and the why). Spawns one Node child per client and multiplexes\n * agent create/resume/send/cancel/close requests over JSON-lines stdio,\n * exposing agents through the same minimal surface the provider already\n * consumes ({@link AgentLike}), so session-pool/agent-events need no\n * sidecar-specific logic.\n */\nimport { spawn, type ChildProcessByStdio } from \"node:child_process\";\nimport { createInterface, type Interface } from \"node:readline\";\nimport type { Readable, Writable } from \"node:stream\";\n\n/** Minimal run surface the provider consumes (subset of the SDK's Run). */\nexport interface AgentRunLike {\n wait(): Promise<{ status: string; result?: string }>;\n cancel(): void | Promise<void>;\n}\n\nexport interface AgentSendOptions {\n mode?: string;\n onDelta?: (input: { update: Record<string, unknown> & { type: string } }) => void;\n local?: { force?: boolean };\n}\n\n/** Minimal agent surface the provider consumes (subset of the SDK's SDKAgent). */\nexport interface AgentLike {\n agentId: string;\n send(message: unknown, options?: AgentSendOptions): Promise<AgentRunLike>;\n close(): void;\n}\n\nexport interface SidecarClientOptions {\n /** Path to the agent-host script. */\n scriptPath: string;\n /** Node executable; default \"node\" from PATH. */\n nodePath?: string;\n /** Extra environment for the child (merged over process.env). */\n env?: Record<string, string>;\n /** Mirror child stderr to this process (debug aid). */\n debug?: boolean;\n}\n\ninterface Pending {\n resolve: (msg: Record<string, unknown>) => void;\n reject: (err: Error) => void;\n /** Streaming hooks for \"send\" requests. */\n onUpdate?: (update: Record<string, unknown> & { type: string }) => void;\n onResult?: (result: { status: string; result?: string }) => void;\n onStreamError?: (err: Error) => void;\n}\n\nfunction reviveError(error: unknown): Error {\n const e = (error ?? {}) as { name?: string; message?: string };\n const err = new Error(e.message ?? \"sidecar error\");\n if (e.name) err.name = e.name;\n return err;\n}\n\nexport class SidecarClient {\n private readonly options: SidecarClientOptions;\n private child: ChildProcessByStdio<Writable, Readable, Readable> | undefined;\n private reader: Interface | undefined;\n private readonly pending = new Map<number, Pending>();\n private nextId = 1;\n private disposed = false;\n\n constructor(options: SidecarClientOptions) {\n this.options = options;\n }\n\n /** Spawn (or reuse) the child process. */\n private ensureChild(): ChildProcessByStdio<Writable, Readable, Readable> {\n if (this.disposed) throw new Error(\"cursor sidecar client disposed\");\n if (this.child) return this.child;\n\n const child = spawn(this.options.nodePath ?? \"node\", [this.options.scriptPath], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...this.options.env },\n });\n this.child = child;\n\n this.reader = createInterface({ input: child.stdout });\n this.reader.on(\"line\", (line) => this.handleLine(line));\n child.stderr.on(\"data\", (chunk: Buffer) => {\n if (this.options.debug || process.env[\"OPENCODE_CURSOR_DEBUG\"]) {\n process.stderr.write(`[cursor:sidecar] ${chunk}`);\n }\n });\n child.on(\"exit\", (code) => {\n this.failAll(new Error(`cursor sidecar exited (code ${code ?? \"unknown\"})`));\n this.child = undefined;\n this.reader?.close();\n this.reader = undefined;\n });\n child.on(\"error\", (err) => {\n this.failAll(new Error(`cursor sidecar failed to start: ${err.message}`));\n this.child = undefined;\n });\n this.updateRefs();\n return child;\n }\n\n /**\n * Keep the child (and its pipes) from holding the parent's event loop open\n * while idle, but ref it whenever a reply is outstanding so the loop can't\n * exit mid-request. Without this, any process that uses the provider and\n * never dispose()s — scripts, tests, opencode itself on shutdown — hangs.\n */\n private updateRefs(): void {\n const child = this.child;\n if (!child) return;\n const refable = [child, child.stdin, child.stdout, child.stderr] as Array<{\n ref?: () => void;\n unref?: () => void;\n }>;\n if (this.pending.size > 0) {\n for (const target of refable) target.ref?.();\n } else {\n for (const target of refable) target.unref?.();\n }\n }\n\n private failAll(err: Error): void {\n for (const pending of this.pending.values()) {\n pending.onStreamError?.(err);\n pending.reject(err);\n }\n this.pending.clear();\n this.updateRefs();\n }\n\n private handleLine(line: string): void {\n if (!line.trim()) return;\n let msg: Record<string, unknown>;\n try {\n msg = JSON.parse(line) as Record<string, unknown>;\n } catch {\n return; // ignore non-protocol noise on stdout\n }\n const id = msg[\"id\"];\n if (typeof id !== \"number\") return;\n const pending = this.pending.get(id);\n if (!pending) return;\n\n const ev = msg[\"ev\"];\n if (ev === \"update\") {\n pending.onUpdate?.(msg[\"update\"] as Record<string, unknown> & { type: string });\n return;\n }\n if (ev === \"result\") {\n this.pending.delete(id);\n this.updateRefs();\n pending.onResult?.(msg[\"result\"] as { status: string; result?: string });\n return;\n }\n if (ev === \"error\") {\n this.pending.delete(id);\n this.updateRefs();\n pending.onStreamError?.(reviveError(msg[\"error\"]));\n return;\n }\n\n if (msg[\"ok\"] === true) {\n // \"send\" acks stay pending for their streaming terminal event.\n if (!pending.onResult) {\n this.pending.delete(id);\n this.updateRefs();\n }\n pending.resolve(msg);\n } else {\n this.pending.delete(id);\n this.updateRefs();\n pending.reject(reviveError(msg[\"error\"]));\n }\n }\n\n private request(\n payload: Record<string, unknown>,\n hooks?: Pick<Pending, \"onUpdate\" | \"onResult\" | \"onStreamError\">,\n ): Promise<Record<string, unknown>> {\n const child = this.ensureChild();\n const id = this.nextId++;\n return new Promise<Record<string, unknown>>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, ...hooks });\n this.updateRefs();\n child.stdin.write(`${JSON.stringify({ id, ...payload })}\\n`, (err) => {\n if (err) {\n this.pending.delete(id);\n this.updateRefs();\n reject(err);\n }\n });\n });\n }\n\n async createAgent(options: unknown): Promise<AgentLike> {\n const res = await this.request({ op: \"create\", options });\n return this.wrapAgent(String(res[\"agentId\"]));\n }\n\n async resumeAgent(agentId: string, options: unknown): Promise<AgentLike> {\n const res = await this.request({ op: \"resume\", agentId, options });\n return this.wrapAgent(String(res[\"agentId\"]));\n }\n\n private wrapAgent(agentId: string): AgentLike {\n return {\n agentId,\n send: (message, options) => this.sendTurn(agentId, message, options),\n close: () => {\n void this.request({ op: \"close\", agentId }).catch(() => {\n // best effort, mirrors SDKAgent.close()\n });\n },\n };\n }\n\n private async sendTurn(\n agentId: string,\n message: unknown,\n options?: AgentSendOptions,\n ): Promise<AgentRunLike> {\n let settle!: {\n resolve: (r: { status: string; result?: string }) => void;\n reject: (e: Error) => void;\n };\n const waited = new Promise<{ status: string; result?: string }>((resolve, reject) => {\n settle = { resolve, reject };\n });\n // Avoid unhandled-rejection noise when the consumer never calls wait().\n waited.catch(() => {});\n\n let sendId: number | undefined;\n const ack = this.request(\n {\n op: \"send\",\n agentId,\n message,\n ...(options?.mode ? { mode: options.mode } : {}),\n ...(options?.local?.force ? { force: true } : {}),\n },\n {\n onUpdate: (update) => options?.onDelta?.({ update }),\n onResult: (result) => settle.resolve(result),\n onStreamError: (err) => settle.reject(err),\n },\n );\n // The request id is allocated synchronously inside request(); capture it\n // for cancel by reading the id we just used.\n sendId = this.nextId - 1;\n\n await ack;\n return {\n wait: () => waited,\n cancel: async () => {\n if (sendId === undefined) return;\n await this.request({ op: \"cancel\", sendId }).catch(() => {});\n },\n };\n }\n\n /** Kill the child and reject anything in flight. */\n dispose(): void {\n this.disposed = true;\n this.failAll(new Error(\"cursor sidecar client disposed\"));\n this.reader?.close();\n this.reader = undefined;\n this.child?.kill();\n this.child = undefined;\n }\n}\n","import type {\n AgentDefinition,\n AgentModeOption,\n McpServerConfig,\n ModelSelection,\n SettingSource,\n} from \"@cursor/sdk\";\nimport { loadAgentBackend, type AgentLike } from \"./agent-backend.js\";\n\n/** sessionID -> Cursor agentId, so a session reuses one Cursor agent across turns. */\nconst pool = new Map<string, string>();\n\n/** Test/diagnostic helpers. */\nexport function getPooledAgentId(sessionID: string): string | undefined {\n return pool.get(sessionID);\n}\nexport function clearAgentPool(): void {\n pool.clear();\n}\n\nexport interface AcquireAgentParams {\n apiKey: string;\n modelSelection: ModelSelection;\n mode: AgentModeOption;\n cwd: string;\n settingSources?: SettingSource[];\n sandbox?: boolean;\n mcpServers?: Record<string, McpServerConfig>;\n agents?: Record<string, AgentDefinition>;\n name?: string;\n /** opencode session id; required for pooling. */\n sessionID?: string;\n /** When true (and sessionID present) reuse/resume one agent per session. */\n session: boolean;\n /**\n * Resume a specific Cursor agent by id. Takes precedence over session\n * pooling; lets power users continue an explicit agent (e.g. one returned by\n * a prior tool call) rather than the session's auto-managed one.\n */\n agentId?: string;\n}\n\nexport interface AcquiredAgent {\n agent: AgentLike;\n /** True when an existing pooled agent was resumed (send only the new turn). */\n resumed: boolean;\n /** Close the agent unless it's pooled (pooled agents persist for the next turn). */\n release: () => void;\n}\n\n/**\n * Get an agent to run a turn: resume the session's pooled agent when possible,\n * otherwise create a fresh one. Resume failures fall back to creation, so a\n * stale/expired pool entry degrades to a correct fresh turn rather than an error.\n */\nexport async function acquireAgent(params: AcquireAgentParams): Promise<AcquiredAgent> {\n const backend = loadAgentBackend();\n\n const createOptions = {\n apiKey: params.apiKey,\n model: params.modelSelection,\n mode: params.mode,\n local: {\n cwd: params.cwd,\n ...(params.settingSources ? { settingSources: params.settingSources } : {}),\n ...(params.sandbox !== undefined ? { sandboxOptions: { enabled: params.sandbox } } : {}),\n },\n ...(params.mcpServers ? { mcpServers: params.mcpServers } : {}),\n ...(params.agents ? { agents: params.agents } : {}),\n ...(params.name ? { name: params.name } : {}),\n };\n\n const pooling = params.session && Boolean(params.sessionID);\n const pooledId = pooling ? pool.get(params.sessionID!) : undefined;\n // An explicit agentId wins over the session's pooled agent.\n const resumeId = params.agentId ?? pooledId;\n\n let agent: AgentLike | undefined;\n let resumed = false;\n if (resumeId) {\n try {\n agent = await backend.resumeAgent(resumeId, createOptions);\n resumed = true;\n } catch {\n // A stale/expired id degrades to a fresh agent; drop a matching pool entry.\n if (pooledId && resumeId === pooledId) pool.delete(params.sessionID!);\n }\n }\n if (!agent) {\n agent = await backend.createAgent(createOptions);\n }\n\n if (pooling) pool.set(params.sessionID!, agent.agentId);\n\n const release = () => {\n if (!pooling) {\n try {\n agent!.close();\n } catch {\n // best effort\n }\n }\n };\n\n return { agent, resumed, release };\n}\n"],"mappings":"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","names":["cached","execSync","existsSync","spawn","execSync","existsSync","cached"]}
|
package/dist/plugin/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
resolveControls,
|
|
7
7
|
resolveCursorApiKey,
|
|
8
8
|
streamAgentTurn
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-D4YQ7ZEM.js";
|
|
10
10
|
|
|
11
11
|
// src/model-cache.ts
|
|
12
12
|
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -73,6 +73,29 @@ var FALLBACK_MODELS = [
|
|
|
73
73
|
{ id: "gpt-5.5", displayName: "GPT-5.5 (via Cursor)" }
|
|
74
74
|
];
|
|
75
75
|
|
|
76
|
+
// src/model-variants.ts
|
|
77
|
+
var REASONING_PARAM = /think|reason|effort/i;
|
|
78
|
+
var BOOLEAN_VALUES = /* @__PURE__ */ new Set(["true", "false"]);
|
|
79
|
+
function buildModelVariants(item) {
|
|
80
|
+
const out = {};
|
|
81
|
+
for (const param of item.parameters ?? []) {
|
|
82
|
+
if (!REASONING_PARAM.test(param.id)) continue;
|
|
83
|
+
const values = (param.values ?? []).map((v) => v.value);
|
|
84
|
+
if (values.length === 0) continue;
|
|
85
|
+
if (values.every((v) => BOOLEAN_VALUES.has(v))) {
|
|
86
|
+
if (values.includes("true")) {
|
|
87
|
+
out[param.id.toLowerCase()] = { params: { [param.id]: "true" } };
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
for (const value of values) {
|
|
92
|
+
const key = out[value] === void 0 ? value : `${param.id}-${value}`;
|
|
93
|
+
out[key] = { params: { [param.id]: value } };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
76
99
|
// src/model-discovery.ts
|
|
77
100
|
async function discoverModels(options = {}) {
|
|
78
101
|
const apiKey = resolveCursorApiKey(options.apiKey);
|
|
@@ -129,27 +152,13 @@ function toOpencodeModels(items) {
|
|
|
129
152
|
attachment: true,
|
|
130
153
|
reasoning: modelSupportsReasoning(item),
|
|
131
154
|
temperature: false,
|
|
132
|
-
tool_call: true
|
|
155
|
+
tool_call: true,
|
|
156
|
+
variants: buildModelVariants(item)
|
|
133
157
|
};
|
|
134
158
|
}
|
|
135
159
|
return out;
|
|
136
160
|
}
|
|
137
161
|
|
|
138
|
-
// src/model-variants.ts
|
|
139
|
-
var REASONING_PARAM = /think|reason|effort/i;
|
|
140
|
-
function buildModelVariants(item) {
|
|
141
|
-
const out = {};
|
|
142
|
-
for (const param of item.parameters ?? []) {
|
|
143
|
-
if (!REASONING_PARAM.test(param.id)) continue;
|
|
144
|
-
for (const { value } of param.values ?? []) {
|
|
145
|
-
const key = param.id.toLowerCase() === "thinking" ? value : `${param.id}-${value}`;
|
|
146
|
-
out[key] = { params: { [param.id]: value } };
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
out["plan"] = { mode: "plan" };
|
|
150
|
-
return out;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
162
|
// src/plugin/model-v2.ts
|
|
154
163
|
var PROVIDER_ID = "cursor";
|
|
155
164
|
var NPM_PACKAGE = "@stablekernel/opencode-cursor";
|
|
@@ -535,9 +544,17 @@ var CursorPlugin = async (input) => {
|
|
|
535
544
|
// Bridge opencode's session id to the provider: it lands in
|
|
536
545
|
// providerOptions.cursor.sessionID, which the provider reads to pool/resume a
|
|
537
546
|
// Cursor agent per session (when the `session` option is enabled).
|
|
547
|
+
//
|
|
548
|
+
// Also map opencode's plan AGENT to Cursor's plan mode. This hook fires
|
|
549
|
+
// after opencode merges the selected variant into `output.options`, so an
|
|
550
|
+
// explicit mode from the `plan` variant (or model options) wins — the
|
|
551
|
+
// agent-based default only applies when no mode was set.
|
|
538
552
|
"chat.params": async (input2, output) => {
|
|
539
553
|
if (input2.model?.providerID !== PROVIDER_ID) return;
|
|
540
554
|
output.options = { ...output.options ?? {}, sessionID: input2.sessionID };
|
|
555
|
+
if (input2.agent === "plan" && output.options["mode"] === void 0) {
|
|
556
|
+
output.options["mode"] = "plan";
|
|
557
|
+
}
|
|
541
558
|
},
|
|
542
559
|
tool: {
|
|
543
560
|
cursor_refresh_models: {
|