cursor-opencode-provider 0.1.4 → 0.1.5
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/README.md +4 -3
- package/dist/agent-url.js +2 -1
- package/dist/debug.d.ts +1 -0
- package/dist/debug.js +22 -0
- package/dist/language-model.js +5 -2
- package/dist/protocol/tools.d.ts +28 -1
- package/dist/protocol/tools.js +81 -5
- package/dist/session.d.ts +6 -1
- package/dist/session.js +3 -3
- package/dist/transport/connect.d.ts +0 -1
- package/dist/transport/connect.js +1 -21
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ OpenCode driving a Cursor-routed Grok model through this provider:
|
|
|
18
18
|
- **Authentication** — browser OAuth (PKCE), or API key from [cursor.com/settings](https://cursor.com/settings)
|
|
19
19
|
- **Model discovery** — fetches available models from Cursor's API and caches them locally
|
|
20
20
|
- **Streaming** — bidirectional Connect-RPC stream for agent runs
|
|
21
|
-
- **Tool calls** — maps Cursor exec-server messages to AI SDK tool-call parts
|
|
21
|
+
- **Tool calls** — maps Cursor exec-server messages to AI SDK tool-call parts; strips OpenCode's `read` XML envelope (`<path>`/`<content>` + `N:` prefixes) before returning content to Cursor so the model cannot echo the wrapper into writes
|
|
22
22
|
- **Thinking / reasoning** — surfaces extended-thinking deltas where the model supports it
|
|
23
23
|
|
|
24
24
|
## Requirements
|
|
@@ -47,7 +47,7 @@ Add the package name to OpenCode config. OpenCode installs npm plugins with Bun
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Pin a version if you want: `"cursor-opencode-provider@0.1.
|
|
50
|
+
Pin a version if you want: `"cursor-opencode-provider@0.1.5"`.
|
|
51
51
|
|
|
52
52
|
### From a local clone
|
|
53
53
|
|
|
@@ -181,6 +181,7 @@ OpenCode
|
|
|
181
181
|
| `src/index.ts` | `createCursor` factory; default export is `CursorPlugin` |
|
|
182
182
|
| `src/language-model.ts` | AI SDK `LanguageModelV3` adapter (`doStream`, `doGenerate`) |
|
|
183
183
|
| `src/session.ts` | Held-open agent Run session and pending exec correlation |
|
|
184
|
+
| `src/debug.ts` | Opt-in wire-level debug logging (`CURSOR_PROVIDER_DEBUG`) |
|
|
184
185
|
| `src/auth.ts` | PKCE OAuth, API key exchange, JWT refresh |
|
|
185
186
|
| `src/models.ts` | `AvailableModels` fetch and `cursor-models.json` cache |
|
|
186
187
|
| `src/agent-url.ts` | `GetServerConfig` fetch + in-process memo (region-specific Run host) |
|
|
@@ -218,4 +219,4 @@ OpenCode
|
|
|
218
219
|
|
|
219
220
|
## License
|
|
220
221
|
|
|
221
|
-
MIT
|
|
222
|
+
MIT
|
package/dist/agent-url.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { fetchAgentUrl
|
|
2
|
+
import { fetchAgentUrl } from "./transport/connect.js";
|
|
3
|
+
import { trace } from "./debug.js";
|
|
3
4
|
import { CURSOR_API_HOST } from "./shared.js";
|
|
4
5
|
const DEFAULT_API_BASE = `https://${CURSOR_API_HOST}`;
|
|
5
6
|
// In-process memo of the region-specific Run stream origin (agentnUrl). Resolved
|
package/dist/debug.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function trace(msg: string): void;
|
package/dist/debug.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
// Wire-level diagnostics. Opt in with CURSOR_PROVIDER_DEBUG=1 (or "true").
|
|
3
|
+
// Writes to CURSOR_PROVIDER_DEBUG_FILE (default /tmp/cursor-provider-debug.log).
|
|
4
|
+
// Truncated once per process. Tokens / checksums should be redacted by callers.
|
|
5
|
+
const DEBUG_ENABLED = process.env.CURSOR_PROVIDER_DEBUG === "1" ||
|
|
6
|
+
process.env.CURSOR_PROVIDER_DEBUG === "true";
|
|
7
|
+
const DEBUG_FILE = process.env.CURSOR_PROVIDER_DEBUG_FILE || "/tmp/cursor-provider-debug.log";
|
|
8
|
+
let _traceInitialized = false;
|
|
9
|
+
export function trace(msg) {
|
|
10
|
+
if (!DEBUG_ENABLED)
|
|
11
|
+
return;
|
|
12
|
+
try {
|
|
13
|
+
if (!_traceInitialized) {
|
|
14
|
+
_traceInitialized = true;
|
|
15
|
+
fs.writeFileSync(DEBUG_FILE, `--- cursor-provider debug (pid ${process.pid}) ${new Date().toISOString()} ---\n`);
|
|
16
|
+
}
|
|
17
|
+
fs.appendFileSync(DEBUG_FILE, `[${new Date().toISOString()}] ${msg}\n`);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
/* ignore */
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/language-model.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { bidiRunStream, normalizeAgentRunOrigin
|
|
3
|
+
import { bidiRunStream, normalizeAgentRunOrigin } from "./transport/connect.js";
|
|
4
|
+
import { trace } from "./debug.js";
|
|
4
5
|
import { buildRunRequest, buildHeartbeat } from "./protocol/request.js";
|
|
5
6
|
import { decodeFramePayload } from "./protocol/framing.js";
|
|
6
7
|
import { decodeMessage } from "./protocol/messages.js";
|
|
@@ -78,6 +79,7 @@ async function doStreamImpl(modelId, options, callOptions) {
|
|
|
78
79
|
resultField: pending.resultField,
|
|
79
80
|
output: r.output,
|
|
80
81
|
error: r.error,
|
|
82
|
+
toolName: pending.toolName ?? r.toolName,
|
|
81
83
|
})) {
|
|
82
84
|
session.stream.write(frame);
|
|
83
85
|
}
|
|
@@ -575,6 +577,7 @@ async function pump(session, controller, ids, abortSignal) {
|
|
|
575
577
|
resultField: parsed.resultField,
|
|
576
578
|
output: "",
|
|
577
579
|
error: reason,
|
|
580
|
+
toolName: parsed.toolName,
|
|
578
581
|
})) {
|
|
579
582
|
session.stream.write(frame);
|
|
580
583
|
}
|
|
@@ -586,7 +589,7 @@ async function pump(session, controller, ids, abortSignal) {
|
|
|
586
589
|
}
|
|
587
590
|
const tc = buildToolCallPart(parsed, session.sessionId);
|
|
588
591
|
// Keep the stream open; the result arrives on the next doStream call.
|
|
589
|
-
sessionManager.registerPending(parsed.id, session, parsed.resultField);
|
|
592
|
+
sessionManager.registerPending(parsed.id, session, parsed.resultField, parsed.toolName);
|
|
590
593
|
// tc.input is already a JSON string (LanguageModelV3ToolCall.input).
|
|
591
594
|
trace(`exec: EMITTED tool-call toolCallId=${tc.toolCallId} toolName=${tc.toolName} inputLen=${tc.input.length}`);
|
|
592
595
|
// Close open text/reasoning spans before tool-call (required by AI SDK V3).
|
package/dist/protocol/tools.d.ts
CHANGED
|
@@ -53,6 +53,12 @@ export type ToolResultInput = {
|
|
|
53
53
|
output: string;
|
|
54
54
|
error?: string;
|
|
55
55
|
executionTimeMs?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Resolved opencode tool name (read/write/grep/…). Gates read-envelope
|
|
58
|
+
* unwrapping on the mcp_result path so non-read MCP output is never
|
|
59
|
+
* rewritten. read_result is always a read, so it unwraps regardless.
|
|
60
|
+
*/
|
|
61
|
+
toolName?: string;
|
|
56
62
|
};
|
|
57
63
|
/**
|
|
58
64
|
* Build one or more ExecClientMessage frames for a tool result.
|
|
@@ -64,12 +70,33 @@ export type ToolResultInput = {
|
|
|
64
70
|
export declare function buildExecClientMessages(input: ToolResultInput): Uint8Array[];
|
|
65
71
|
/** ACM #5 exec_client_control_message { stream_close { id } }. */
|
|
66
72
|
export declare function buildExecStreamClose(execId: number): Uint8Array;
|
|
73
|
+
/**
|
|
74
|
+
* Strip opencode's `read` envelope, leaving raw file content.
|
|
75
|
+
*
|
|
76
|
+
* opencode's read tool (opencode `tool/read.ts`) wraps content in an XML-ish
|
|
77
|
+
* envelope its own models are trained on, but Cursor's are not:
|
|
78
|
+
* <path>{abs}</path>\n<type>file</type>\n<content>\n{N}: {line}\n…\n\n{footer}\n</content>
|
|
79
|
+
* Forwarding that envelope verbatim made Cursor's model treat the wrapper as
|
|
80
|
+
* literal file content and write `<path>`/`<content>` tags + `N:` line prefixes
|
|
81
|
+
* back into files (silent corruption — the write still reports success; seen
|
|
82
|
+
* across 8+ sessions, e.g. language-model.ts rewritten starting with
|
|
83
|
+
* `<path>…</path>\n<type>file</type>\n<content>\n1: import fs…`).
|
|
84
|
+
*
|
|
85
|
+
* Returns the raw file body (line numbers + footer + `<system-reminder>` dropped).
|
|
86
|
+
*
|
|
87
|
+
* Deliberately exception-safe: if the expected envelope is absent — non-read
|
|
88
|
+
* output, already-raw text, or a future opencode format change — it returns the
|
|
89
|
+
* input unchanged, so a result is never broken and we never throw mid-turn.
|
|
90
|
+
* Callers must still gate `mcp_result` on `toolName === "read"`; this helper
|
|
91
|
+
* alone is not a tool-identity check.
|
|
92
|
+
*/
|
|
93
|
+
export declare function unwrapReadOutput(output: string): string;
|
|
67
94
|
/**
|
|
68
95
|
* Map OpenCode tool text into the agent.v1 result oneof for each exec variant.
|
|
69
96
|
* OpenCode returns free-form text; we wrap it in the minimal success shape the
|
|
70
97
|
* server accepts (verified against agent.v1 wire captures).
|
|
71
98
|
*/
|
|
72
|
-
export declare function buildTypedExecResult(resultField: string, output: string, error?: string): Record<string, unknown>;
|
|
99
|
+
export declare function buildTypedExecResult(resultField: string, output: string, error?: string, toolName?: string): Record<string, unknown>;
|
|
73
100
|
export declare function buildToolCallPart(execMsg: ParsedExecRequest, sessionId: string): {
|
|
74
101
|
toolCallId: string;
|
|
75
102
|
toolName: string;
|
package/dist/protocol/tools.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { encodeMessage } from "./messages.js";
|
|
2
2
|
import { encodeJsonAsValue, decodeStructEntriesToJson, readAllFields } from "./struct.js";
|
|
3
|
+
import { trace } from "../debug.js";
|
|
3
4
|
// Exec variant field number whose reply is the server-initiated request_context
|
|
4
5
|
// probe (ExecServerMessage #10 → ExecClientMessage #10). request/result share a
|
|
5
6
|
// field number for every exec variant, so this is also the result field.
|
|
@@ -394,7 +395,7 @@ export function buildExecClientMessages(input) {
|
|
|
394
395
|
id: input.execId,
|
|
395
396
|
local_execution_time_ms: input.executionTimeMs ?? 0,
|
|
396
397
|
};
|
|
397
|
-
clientMsg[resultField] = buildTypedExecResult(resultField, input.output, input.error);
|
|
398
|
+
clientMsg[resultField] = buildTypedExecResult(resultField, input.output, input.error, input.toolName);
|
|
398
399
|
frames.push(encodeMessage("AgentClientMessage", {
|
|
399
400
|
exec_client_message: clientMsg,
|
|
400
401
|
}));
|
|
@@ -411,21 +412,90 @@ export function buildExecStreamClose(execId) {
|
|
|
411
412
|
},
|
|
412
413
|
});
|
|
413
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Strip opencode's `read` envelope, leaving raw file content.
|
|
417
|
+
*
|
|
418
|
+
* opencode's read tool (opencode `tool/read.ts`) wraps content in an XML-ish
|
|
419
|
+
* envelope its own models are trained on, but Cursor's are not:
|
|
420
|
+
* <path>{abs}</path>\n<type>file</type>\n<content>\n{N}: {line}\n…\n\n{footer}\n</content>
|
|
421
|
+
* Forwarding that envelope verbatim made Cursor's model treat the wrapper as
|
|
422
|
+
* literal file content and write `<path>`/`<content>` tags + `N:` line prefixes
|
|
423
|
+
* back into files (silent corruption — the write still reports success; seen
|
|
424
|
+
* across 8+ sessions, e.g. language-model.ts rewritten starting with
|
|
425
|
+
* `<path>…</path>\n<type>file</type>\n<content>\n1: import fs…`).
|
|
426
|
+
*
|
|
427
|
+
* Returns the raw file body (line numbers + footer + `<system-reminder>` dropped).
|
|
428
|
+
*
|
|
429
|
+
* Deliberately exception-safe: if the expected envelope is absent — non-read
|
|
430
|
+
* output, already-raw text, or a future opencode format change — it returns the
|
|
431
|
+
* input unchanged, so a result is never broken and we never throw mid-turn.
|
|
432
|
+
* Callers must still gate `mcp_result` on `toolName === "read"`; this helper
|
|
433
|
+
* alone is not a tool-identity check.
|
|
434
|
+
*/
|
|
435
|
+
export function unwrapReadOutput(output) {
|
|
436
|
+
if (typeof output !== "string" || output.length === 0)
|
|
437
|
+
return output;
|
|
438
|
+
// Require the full opencode read-envelope skeleton *before* `<content>`
|
|
439
|
+
// (read.ts opens with <path>…</path>, <type>file</type>, <content>) so a
|
|
440
|
+
// stray "<content>" later in tool chatter can't trigger unwrapping just
|
|
441
|
+
// because path/type tags appear elsewhere in the payload.
|
|
442
|
+
const contentHeaderIdx = output.indexOf("<content>");
|
|
443
|
+
if (contentHeaderIdx === -1)
|
|
444
|
+
return output;
|
|
445
|
+
const header = output.slice(0, contentHeaderIdx);
|
|
446
|
+
const hasSkeleton = header.indexOf("<path>") !== -1 &&
|
|
447
|
+
header.indexOf("<type>file</type>") !== -1;
|
|
448
|
+
if (!hasSkeleton) {
|
|
449
|
+
// Saw "<content>" but not the read skeleton ahead of it — almost certainly
|
|
450
|
+
// a non-read payload, or an opencode format drift. Surface it so drift
|
|
451
|
+
// can't silently resurrect the wrapper-corruption bug, but still fail
|
|
452
|
+
// safe (no mutate).
|
|
453
|
+
trace("unwrapReadOutput: <content> present without leading <path>/<type>file> skeleton — leaving output unchanged (possible non-read payload or opencode read format drift)");
|
|
454
|
+
return output;
|
|
455
|
+
}
|
|
456
|
+
// Body starts right after "<content>\n". opencode emits one numbered line per
|
|
457
|
+
// file line ("N: <line>"), then a blank, a "(…)" footer, and a standalone
|
|
458
|
+
// "</content>". The body is a *contiguous run* of /^N: / lines — so we stop at
|
|
459
|
+
// the first non-numbered line. Critically we do NOT search for a closing
|
|
460
|
+
// "</content>" substring: a file line that literally contains "</content>"
|
|
461
|
+
// is rendered as "N: </content>" (a body line), and a raw indexOf would
|
|
462
|
+
// truncate the read there.
|
|
463
|
+
let rest = output.slice(contentHeaderIdx + "<content>".length);
|
|
464
|
+
if (rest.startsWith("\n"))
|
|
465
|
+
rest = rest.slice(1);
|
|
466
|
+
const raw = [];
|
|
467
|
+
for (const line of rest.split("\n")) {
|
|
468
|
+
const m = /^(\d+):[ \t]?(.*)$/.exec(line);
|
|
469
|
+
if (!m)
|
|
470
|
+
break; // blank / "(footer)" / "</content>" → end of body run
|
|
471
|
+
// Strip only the leading "N: " prefix; a line that itself begins with
|
|
472
|
+
// digits+colon keeps its content (we remove just the first match). Blank
|
|
473
|
+
// file lines render as "N: " and are preserved (capture group is "").
|
|
474
|
+
raw.push(m[2]);
|
|
475
|
+
}
|
|
476
|
+
// Envelope confirmed but no numbered body → empty file. Return "" rather
|
|
477
|
+
// than the envelope (the envelope is exactly what Cursor echoes into writes).
|
|
478
|
+
return raw.join("\n");
|
|
479
|
+
}
|
|
414
480
|
/**
|
|
415
481
|
* Map OpenCode tool text into the agent.v1 result oneof for each exec variant.
|
|
416
482
|
* OpenCode returns free-form text; we wrap it in the minimal success shape the
|
|
417
483
|
* server accepts (verified against agent.v1 wire captures).
|
|
418
484
|
*/
|
|
419
|
-
export function buildTypedExecResult(resultField, output, error) {
|
|
485
|
+
export function buildTypedExecResult(resultField, output, error, toolName) {
|
|
420
486
|
switch (resultField) {
|
|
421
487
|
case "read_result":
|
|
422
488
|
if (error)
|
|
423
489
|
return { error: { path: "", error } };
|
|
490
|
+
// Strip opencode's <path>/<content> envelope so Cursor's model receives
|
|
491
|
+
// raw file content and can't echo the wrapper into subsequent writes.
|
|
492
|
+
// reads route here only for native read_args; most arrive via mcp_result.
|
|
493
|
+
const content = unwrapReadOutput(output);
|
|
424
494
|
return {
|
|
425
495
|
success: {
|
|
426
496
|
path: extractPathTag(output) ?? "",
|
|
427
|
-
content
|
|
428
|
-
total_lines: countLines(
|
|
497
|
+
content,
|
|
498
|
+
total_lines: countLines(content),
|
|
429
499
|
},
|
|
430
500
|
};
|
|
431
501
|
case "grep_result": {
|
|
@@ -492,9 +562,15 @@ export function buildTypedExecResult(resultField, output, error) {
|
|
|
492
562
|
case "mcp_result":
|
|
493
563
|
if (error)
|
|
494
564
|
return { error: { error } };
|
|
565
|
+
// opencode built-ins (read/write/grep/…) are advertised as MCP tools, so
|
|
566
|
+
// a read call returns through mcp_result. Scope the unwrap to toolName
|
|
567
|
+
// "read" so a non-read MCP tool whose output merely contains a
|
|
568
|
+
// "<content>"-like block is never rewritten.
|
|
495
569
|
return {
|
|
496
570
|
success: {
|
|
497
|
-
content: [
|
|
571
|
+
content: [
|
|
572
|
+
{ text: { text: toolName === "read" ? unwrapReadOutput(output) : output } },
|
|
573
|
+
],
|
|
498
574
|
is_error: false,
|
|
499
575
|
},
|
|
500
576
|
};
|
package/dist/session.d.ts
CHANGED
|
@@ -14,6 +14,11 @@ export type Frame = {
|
|
|
14
14
|
export type PendingExec = {
|
|
15
15
|
/** ExecClientMessage result field to reply with (matches the request variant). */
|
|
16
16
|
resultField: string;
|
|
17
|
+
/**
|
|
18
|
+
* Resolved opencode tool name (read/write/grep/…). Used on continuation so
|
|
19
|
+
* mcp_result can unwrap read envelopes even if the prompt omits toolName.
|
|
20
|
+
*/
|
|
21
|
+
toolName?: string;
|
|
17
22
|
};
|
|
18
23
|
export type CursorSession = {
|
|
19
24
|
/**
|
|
@@ -58,7 +63,7 @@ export declare class SessionManager {
|
|
|
58
63
|
constructor(idleTimeoutMs?: number);
|
|
59
64
|
touch(session: CursorSession): void;
|
|
60
65
|
/** Register that `session` is awaiting a result for `execId`. */
|
|
61
|
-
registerPending(execId: number, session: CursorSession, resultField: string): void;
|
|
66
|
+
registerPending(execId: number, session: CursorSession, resultField: string, toolName?: string): void;
|
|
62
67
|
/** The pending exec info for an id on a specific session, if still awaiting it. */
|
|
63
68
|
pendingFor(sessionId: string, execId: number): PendingExec | undefined;
|
|
64
69
|
/** Find the live session awaiting one of the given exec ids. */
|
package/dist/session.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { trace } from "./
|
|
1
|
+
import { trace } from "./debug.js";
|
|
2
2
|
export class SessionManager {
|
|
3
3
|
// Composite key `${sessionId}:${execId}` → owning session. Composite keying
|
|
4
4
|
// means two Run streams that both register an execId of 1 (Cursor resets
|
|
@@ -12,8 +12,8 @@ export class SessionManager {
|
|
|
12
12
|
session.expiresAt = Date.now() + this.idleTimeoutMs;
|
|
13
13
|
}
|
|
14
14
|
/** Register that `session` is awaiting a result for `execId`. */
|
|
15
|
-
registerPending(execId, session, resultField) {
|
|
16
|
-
session.pending.set(execId, { resultField });
|
|
15
|
+
registerPending(execId, session, resultField, toolName) {
|
|
16
|
+
session.pending.set(execId, { resultField, toolName });
|
|
17
17
|
this.byExecId.set(this.key(session.sessionId, execId), session);
|
|
18
18
|
this.touch(session);
|
|
19
19
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export declare function trace(msg: string): void;
|
|
2
1
|
export declare function buildBaseHeaders(token: string, clientVersion: string, extra?: Record<string, string>): Record<string, string>;
|
|
3
2
|
export declare function unaryAvailableModels(token: string, options?: {
|
|
4
3
|
apiBaseURL?: string;
|
|
@@ -3,32 +3,12 @@ import { encodeFrame, streamFrames } from "../protocol/framing.js";
|
|
|
3
3
|
import { createCursorChecksumHeader } from "../protocol/checksum.js";
|
|
4
4
|
import { getDeviceIds } from "../protocol/device-id.js";
|
|
5
5
|
import { resolveClientVersion } from "../protocol/client-version.js";
|
|
6
|
+
import { trace } from "../debug.js";
|
|
6
7
|
import http2 from "node:http2";
|
|
7
|
-
import fs from "node:fs";
|
|
8
8
|
const API_BASE = `https://${CURSOR_API_HOST}`;
|
|
9
9
|
function resolveApiBaseURL(options) {
|
|
10
10
|
return new URL(options.apiBaseURL ?? options.baseURL ?? API_BASE).origin;
|
|
11
11
|
}
|
|
12
|
-
// Wire-level diagnostics. Opt in with CURSOR_PROVIDER_DEBUG=1 (or "true").
|
|
13
|
-
// Writes to CURSOR_PROVIDER_DEBUG_FILE (default /tmp/cursor-provider-debug.log).
|
|
14
|
-
// Truncated once per process. Captures h2 response status, parsed frames, and
|
|
15
|
-
// stream errors. Tokens / checksums are redacted in header dumps.
|
|
16
|
-
const DEBUG_ENABLED = process.env.CURSOR_PROVIDER_DEBUG === "1" ||
|
|
17
|
-
process.env.CURSOR_PROVIDER_DEBUG === "true";
|
|
18
|
-
const DEBUG_FILE = process.env.CURSOR_PROVIDER_DEBUG_FILE || "/tmp/cursor-provider-debug.log";
|
|
19
|
-
let _traceInitialized = false;
|
|
20
|
-
export function trace(msg) {
|
|
21
|
-
if (!DEBUG_ENABLED)
|
|
22
|
-
return;
|
|
23
|
-
try {
|
|
24
|
-
if (!_traceInitialized) {
|
|
25
|
-
_traceInitialized = true;
|
|
26
|
-
fs.writeFileSync(DEBUG_FILE, `--- cursor-provider debug (pid ${process.pid}) ${new Date().toISOString()} ---\n`);
|
|
27
|
-
}
|
|
28
|
-
fs.appendFileSync(DEBUG_FILE, `[${new Date().toISOString()}] ${msg}\n`);
|
|
29
|
-
}
|
|
30
|
-
catch { /* ignore */ }
|
|
31
|
-
}
|
|
32
12
|
trace("connect.ts module loaded");
|
|
33
13
|
export function buildBaseHeaders(token, clientVersion, extra) {
|
|
34
14
|
const { machineId, macMachineId } = getDeviceIds();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-opencode-provider",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Use Cursor subscription models from OpenCode via Cursor's Connect-RPC agent protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
"protobufjs": "^7.4.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@opencode-ai/plugin": "
|
|
57
|
+
"@opencode-ai/plugin": "^1.17.13"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@opencode-ai/plugin": "
|
|
60
|
+
"@opencode-ai/plugin": "^1.17.13",
|
|
61
61
|
"@tsconfig/node22": "^22.0.1",
|
|
62
62
|
"@types/node": "^22.15.3",
|
|
63
63
|
"typescript": "^5.8.3"
|
|
64
64
|
}
|
|
65
|
-
}
|
|
65
|
+
}
|