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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -3
  3. package/dist/backends/agent-argv.mjs +12 -0
  4. package/dist/backends/agent-cli.d.ts +10 -0
  5. package/dist/backends/agent-cli.js +117 -0
  6. package/dist/backends/agent-cli.js.map +1 -0
  7. package/dist/backends/auto-models.d.ts +26 -0
  8. package/dist/backends/auto-models.js +43 -0
  9. package/dist/backends/auto-models.js.map +1 -0
  10. package/dist/backends/checkpoint.d.ts +8 -0
  11. package/dist/backends/checkpoint.js +28 -0
  12. package/dist/backends/checkpoint.js.map +1 -0
  13. package/dist/backends/native/auth.d.ts +20 -0
  14. package/dist/backends/native/auth.js +145 -0
  15. package/dist/backends/native/auth.js.map +1 -0
  16. package/dist/backends/native/backend.d.ts +12 -0
  17. package/dist/backends/native/backend.js +105 -0
  18. package/dist/backends/native/backend.js.map +1 -0
  19. package/dist/backends/native/h2-bridge.mjs +55 -0
  20. package/dist/backends/native/proto.d.ts +31 -0
  21. package/dist/backends/native/proto.js +130 -0
  22. package/dist/backends/native/proto.js.map +1 -0
  23. package/dist/backends/sdk-runner.mjs +317 -0
  24. package/dist/backends/sdk.d.ts +30 -0
  25. package/dist/backends/sdk.js +304 -0
  26. package/dist/backends/sdk.js.map +1 -0
  27. package/dist/backends/select.d.ts +4 -0
  28. package/dist/backends/select.js +15 -0
  29. package/dist/backends/select.js.map +1 -0
  30. package/dist/backends/spawn.d.ts +15 -0
  31. package/dist/backends/spawn.js +64 -0
  32. package/dist/backends/spawn.js.map +1 -0
  33. package/dist/backends/stream-json.d.ts +7 -0
  34. package/dist/backends/stream-json.js +106 -0
  35. package/dist/backends/stream-json.js.map +1 -0
  36. package/dist/backends/types.d.ts +72 -0
  37. package/dist/backends/types.js +15 -0
  38. package/dist/backends/types.js.map +1 -0
  39. package/dist/index.d.ts +4 -0
  40. package/dist/index.js +118 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/plugin/auth.d.ts +9 -0
  43. package/dist/plugin/auth.js +126 -0
  44. package/dist/plugin/auth.js.map +1 -0
  45. package/dist/plugin/catalog.d.ts +41 -0
  46. package/dist/plugin/catalog.js +123 -0
  47. package/dist/plugin/catalog.js.map +1 -0
  48. package/dist/proxy/server.d.ts +17 -0
  49. package/dist/proxy/server.js +265 -0
  50. package/dist/proxy/server.js.map +1 -0
  51. package/dist/proxy/title.d.ts +4 -0
  52. package/dist/proxy/title.js +24 -0
  53. package/dist/proxy/title.js.map +1 -0
  54. package/dist/proxy/tools.d.ts +4 -0
  55. package/dist/proxy/tools.js +26 -0
  56. package/dist/proxy/tools.js.map +1 -0
  57. package/dist/proxy/translate.d.ts +42 -0
  58. package/dist/proxy/translate.js +77 -0
  59. package/dist/proxy/translate.js.map +1 -0
  60. package/dist/util/log.d.ts +1 -0
  61. package/dist/util/log.js +27 -0
  62. package/dist/util/log.js.map +1 -0
  63. package/dist/util/paths.d.ts +6 -0
  64. package/dist/util/paths.js +30 -0
  65. package/dist/util/paths.js.map +1 -0
  66. package/dist/util/windows.d.ts +28 -0
  67. package/dist/util/windows.js +123 -0
  68. package/dist/util/windows.js.map +1 -0
  69. package/package.json +53 -6
@@ -0,0 +1,265 @@
1
+ import http from "node:http";
2
+ import { randomBytes } from "node:crypto";
3
+ import { chooseBackend, parseBackendChoice } from "../backends/select.js";
4
+ import { chunk, prepareTurn, sse } from "./translate.js";
5
+ import { isTitleRequest, localTitle } from "./title.js";
6
+ import { resolveCursorModel } from "../backends/auto-models.js";
7
+ import { log } from "../util/log.js";
8
+ /** OpenCode sends the Cursor integration credential as Authorization, not the proxy token. The proxy only listens on loopback, so a local caller is allowed. */
9
+ export function isLoopback(address) {
10
+ return address === "127.0.0.1" || address === "::1" || address === "::ffff:127.0.0.1";
11
+ }
12
+ function readBody(req) {
13
+ return new Promise((resolve, reject) => {
14
+ const chunks = [];
15
+ req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
16
+ req.on("end", () => resolve(Buffer.concat(chunks).toString()));
17
+ req.on("error", reject);
18
+ });
19
+ }
20
+ async function collect(events, signal) {
21
+ const collected = [];
22
+ for await (const event of events) {
23
+ if (signal.aborted)
24
+ break;
25
+ collected.push(event);
26
+ if (event.type === "tool_call" || event.type === "done" || event.type === "error")
27
+ break;
28
+ }
29
+ return collected;
30
+ }
31
+ function writeJson(res, model, id, events) {
32
+ const text = events.filter((event) => event.type === "text").map((event) => event.text).join("");
33
+ const toolCalls = events.filter((event) => event.type === "tool_call");
34
+ const error = events.find((event) => event.type === "error");
35
+ res.setHeader("content-type", "application/json");
36
+ if (error && !text && toolCalls.length === 0) {
37
+ res.writeHead(502);
38
+ res.end(JSON.stringify({ error: { message: error.message } }));
39
+ return;
40
+ }
41
+ res.end(JSON.stringify({
42
+ id,
43
+ object: "chat.completion",
44
+ model,
45
+ choices: [{
46
+ index: 0,
47
+ message: {
48
+ role: "assistant",
49
+ content: text || null,
50
+ tool_calls: toolCalls.map((event) => event.type === "tool_call" ? {
51
+ id: event.id,
52
+ type: "function",
53
+ function: { name: event.name, arguments: event.arguments },
54
+ } : undefined),
55
+ },
56
+ finish_reason: toolCalls.length ? "tool_calls" : "stop",
57
+ }],
58
+ }));
59
+ }
60
+ async function streamSse(res, model, id, events, signal, timing) {
61
+ res.writeHead(200, {
62
+ "content-type": "text/event-stream",
63
+ "cache-control": "no-cache",
64
+ connection: "keep-alive",
65
+ });
66
+ res.flushHeaders();
67
+ let started = false;
68
+ let toolIndex = 0;
69
+ let firstTokenAt;
70
+ for await (const event of events) {
71
+ if (signal.aborted)
72
+ break;
73
+ if (event.type === "text") {
74
+ firstTokenAt ??= Date.now();
75
+ res.write(sse(chunk(model, id, { role: started ? undefined : "assistant", content: event.text }, null)));
76
+ started = true;
77
+ }
78
+ else if (event.type === "reasoning") {
79
+ firstTokenAt ??= Date.now();
80
+ res.write(sse(chunk(model, id, { reasoning_content: event.text }, null)));
81
+ }
82
+ else if (event.type === "tool_call") {
83
+ firstTokenAt ??= Date.now();
84
+ res.write(sse(chunk(model, id, {
85
+ tool_calls: [{ index: toolIndex, id: event.id, type: "function", function: { name: event.name, arguments: event.arguments } }],
86
+ }, null)));
87
+ toolIndex += 1;
88
+ break;
89
+ }
90
+ else if (event.type === "error") {
91
+ res.write(sse({ error: { message: event.message } }));
92
+ break;
93
+ }
94
+ else if (event.type === "done") {
95
+ break;
96
+ }
97
+ }
98
+ if (timing) {
99
+ log("info", "proxy timing", {
100
+ label: timing.label,
101
+ model,
102
+ ttftMs: firstTokenAt ? firstTokenAt - timing.started : undefined,
103
+ totalMs: Date.now() - timing.started,
104
+ });
105
+ }
106
+ res.write(sse(chunk(model, id, {}, toolIndex > 0 ? "tool_calls" : "stop")));
107
+ res.write("data: [DONE]\n\n");
108
+ res.end();
109
+ }
110
+ async function* titleEvents(text) {
111
+ yield { type: "text", text };
112
+ yield { type: "done", text };
113
+ }
114
+ const attached = [];
115
+ let shared;
116
+ let users = 0;
117
+ function currentDeps() {
118
+ const deps = attached[attached.length - 1];
119
+ if (!deps)
120
+ throw new Error("Cursor proxy has no active plugin instance");
121
+ return deps;
122
+ }
123
+ /** One proxy per process. Each OpenCode project registers its own auth and backends; requests use the latest one still attached. */
124
+ export async function startSharedProxy(deps) {
125
+ attached.push(deps);
126
+ shared ??= startProxy(currentDeps);
127
+ const proxy = await shared;
128
+ users += 1;
129
+ let closed = false;
130
+ return {
131
+ port: proxy.port,
132
+ token: proxy.token,
133
+ close: async () => {
134
+ if (closed)
135
+ return;
136
+ closed = true;
137
+ const index = attached.lastIndexOf(deps);
138
+ if (index >= 0)
139
+ attached.splice(index, 1);
140
+ users -= 1;
141
+ if (users === 0) {
142
+ shared = undefined;
143
+ attached.length = 0;
144
+ await proxy.close();
145
+ }
146
+ },
147
+ };
148
+ }
149
+ export function startProxy(depsOrGet) {
150
+ const getDeps = typeof depsOrGet === "function" ? depsOrGet : () => depsOrGet;
151
+ const token = randomBytes(24).toString("base64url");
152
+ const server = http.createServer(async (req, res) => {
153
+ try {
154
+ const deps = getDeps();
155
+ const url = new URL(req.url || "/", "http://127.0.0.1");
156
+ if (!isLoopback(req.socket.remoteAddress) && (req.headers.authorization || "") !== `Bearer ${token}`) {
157
+ res.writeHead(401, { "content-type": "application/json" });
158
+ res.end(JSON.stringify({ error: { message: "unauthorized" } }));
159
+ return;
160
+ }
161
+ if (req.method === "GET" && (url.pathname === "/health" || url.pathname === "/v1/models")) {
162
+ if (url.pathname === "/v1/models") {
163
+ res.setHeader("content-type", "application/json");
164
+ res.end(JSON.stringify({
165
+ object: "list",
166
+ data: deps.models().map((model) => ({ id: model.id, object: "model", owned_by: "cursor" })),
167
+ }));
168
+ return;
169
+ }
170
+ res.end("ok");
171
+ return;
172
+ }
173
+ if (req.method !== "POST" || url.pathname !== "/v1/chat/completions") {
174
+ res.writeHead(404);
175
+ res.end();
176
+ return;
177
+ }
178
+ const requestStarted = Date.now();
179
+ const body = JSON.parse(await readBody(req));
180
+ const turn = prepareTurn(body);
181
+ const resolved = resolveCursorModel(turn.model);
182
+ const model = resolved.pickerId;
183
+ const id = `chatcmpl-${randomBytes(8).toString("hex")}`;
184
+ if (isTitleRequest(turn)) {
185
+ const title = localTitle(turn.lastUserPrompt);
186
+ log("info", "local session title", { title, ms: Date.now() - requestStarted });
187
+ if (body.stream === false)
188
+ writeJson(res, model, id, [
189
+ { type: "text", text: title },
190
+ { type: "done", text: title },
191
+ ]);
192
+ else
193
+ await streamSse(res, model, id, titleEvents(title), new AbortController().signal, {
194
+ started: requestStarted,
195
+ label: "title-local",
196
+ });
197
+ return;
198
+ }
199
+ const auth = await deps.getAuth();
200
+ const choice = chooseBackend(parseBackendChoice(process.env.CURSOR_BACKEND), auth);
201
+ const backend = deps.backends[choice];
202
+ const encodedDir = req.headers["x-opencode-directory"];
203
+ const cwd = typeof encodedDir === "string" && encodedDir ? decodeURIComponent(encodedDir) : deps.fallbackDirectory();
204
+ const sessionHeader = req.headers["x-opencodev2-cursor-session"];
205
+ const sessionKey = typeof sessionHeader === "string" && sessionHeader ? sessionHeader : "default";
206
+ const controller = new AbortController();
207
+ req.on("close", () => controller.abort());
208
+ const stream = backend.run({
209
+ model: resolved.cursorId,
210
+ modelParams: resolved.params,
211
+ cliModel: resolved.cliModel,
212
+ prompt: turn.prompt,
213
+ lastUserPrompt: turn.lastUserPrompt,
214
+ cwd,
215
+ tools: turn.tools,
216
+ toolResults: turn.toolResults,
217
+ continuation: turn.continuation,
218
+ sessionKey,
219
+ signal: controller.signal,
220
+ });
221
+ if (body.stream === false) {
222
+ const events = await collect(stream, controller.signal);
223
+ log("info", "proxy timing", {
224
+ label: "chat-json",
225
+ model: resolved.cliModel,
226
+ totalMs: Date.now() - requestStarted,
227
+ });
228
+ writeJson(res, model, id, events);
229
+ }
230
+ else {
231
+ await streamSse(res, model, id, stream, controller.signal, {
232
+ started: requestStarted,
233
+ label: "chat",
234
+ });
235
+ }
236
+ }
237
+ catch (error) {
238
+ const message = error instanceof Error ? error.message : "Cursor request failed";
239
+ log("error", "proxy request failed", message);
240
+ if (!res.headersSent) {
241
+ res.writeHead(502, { "content-type": "application/json" });
242
+ res.end(JSON.stringify({ error: { message } }));
243
+ }
244
+ else
245
+ res.end();
246
+ }
247
+ });
248
+ return new Promise((resolve, reject) => {
249
+ server.on("error", reject);
250
+ server.listen(0, "127.0.0.1", () => {
251
+ const address = server.address();
252
+ if (!address || typeof address === "string") {
253
+ reject(new Error("Proxy failed to bind"));
254
+ return;
255
+ }
256
+ log("info", "proxy listening", { port: address.port });
257
+ resolve({
258
+ port: address.port,
259
+ token,
260
+ close: () => new Promise((done) => server.close(() => done())),
261
+ });
262
+ });
263
+ });
264
+ }
265
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/proxy/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { PreparedTurn } from "./translate.js";
2
+ /** OpenChamber's session-title plugin asks for a short name with no tools. Answer locally so it never starts a Cursor agent. */
3
+ export declare function isTitleRequest(turn: PreparedTurn): boolean;
4
+ export declare function localTitle(userText: string): string;
@@ -0,0 +1,24 @@
1
+ const TITLE_HINT = /\b(session title|chat title|conversation title|generate (a )?(short )?(title|name)|short title|concise title|title for (this|the) (chat|conversation|session)|name this (chat|conversation|session)|1-?2 words|one or two words|one-word|title only)\b/i;
2
+ /** OpenChamber's session-title plugin asks for a short name with no tools. Answer locally so it never starts a Cursor agent. */
3
+ export function isTitleRequest(turn) {
4
+ if (turn.tools.length > 0 || turn.continuation)
5
+ return false;
6
+ return TITLE_HINT.test(turn.prompt);
7
+ }
8
+ export function localTitle(userText) {
9
+ const cleaned = userText.replace(/\s+/g, " ").trim();
10
+ if (!cleaned)
11
+ return "New chat";
12
+ const words = cleaned
13
+ .replace(/[^\p{L}\p{N}\s'-]/gu, " ")
14
+ .split(/\s+/)
15
+ .filter(Boolean)
16
+ .slice(0, 4);
17
+ if (words.length === 0)
18
+ return "New chat";
19
+ const title = words
20
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
21
+ .join(" ");
22
+ return title.length > 48 ? `${title.slice(0, 45).trimEnd()}...` : title;
23
+ }
24
+ //# sourceMappingURL=title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"title.js","sourceRoot":"","sources":["../../src/proxy/title.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GACd,yPAAyP,CAAA;AAE3P,gIAAgI;AAChI,MAAM,UAAU,cAAc,CAAC,IAAkB;IAC/C,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,YAAY;QAAE,OAAO,KAAK,CAAA;IAC5D,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;IACpD,IAAI,CAAC,OAAO;QAAE,OAAO,UAAU,CAAA;IAC/B,MAAM,KAAK,GAAG,OAAO;SAClB,OAAO,CAAC,qBAAqB,EAAE,GAAG,CAAC;SACnC,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,OAAO,CAAC;SACf,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACd,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,UAAU,CAAA;IACzC,MAAM,KAAK,GAAG,KAAK;SAChB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SACzE,IAAI,CAAC,GAAG,CAAC,CAAA;IACZ,OAAO,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAA;AACzE,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function mapCursorTool(name: string, advertised: ReadonlySet<string>): string | undefined;
2
+ export declare function advertisedNames(tools: ReadonlyArray<{
3
+ name: string;
4
+ }>): Set<string>;
@@ -0,0 +1,26 @@
1
+ /** Cursor tool names that OpenCode can run itself. */
2
+ const CURSOR_TO_OPENCODE = {
3
+ read: "read",
4
+ write: "write",
5
+ edit: "edit",
6
+ strreplace: "edit",
7
+ grep: "grep",
8
+ glob: "glob",
9
+ find: "glob",
10
+ ls: "glob",
11
+ shell: "shell",
12
+ bash: "shell",
13
+ task: "subagent",
14
+ subagent: "subagent",
15
+ };
16
+ export function mapCursorTool(name, advertised) {
17
+ const key = name.trim().toLowerCase().replace(/[\s_-]+/g, "");
18
+ const mapped = CURSOR_TO_OPENCODE[key];
19
+ if (!mapped || !advertised.has(mapped))
20
+ return undefined;
21
+ return mapped;
22
+ }
23
+ export function advertisedNames(tools) {
24
+ return new Set(tools.map((tool) => tool.name));
25
+ }
26
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/proxy/tools.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,MAAM,kBAAkB,GAA2B;IACjD,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,UAAU,EAAE,MAAM;IAClB,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,EAAE,EAAE,MAAM;IACV,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,OAAO;IACb,IAAI,EAAE,UAAU;IAChB,QAAQ,EAAE,UAAU;CACrB,CAAA;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,UAA+B;IACzE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IAC7D,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAA;IACtC,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAA;IACxD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAsC;IACpE,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;AAChD,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { ToolSpec } from "../backends/types.js";
2
+ export interface ChatMessage {
3
+ role?: string;
4
+ content?: unknown;
5
+ tool_calls?: Array<{
6
+ id?: string;
7
+ function?: {
8
+ name?: string;
9
+ arguments?: string;
10
+ };
11
+ }>;
12
+ tool_call_id?: string;
13
+ name?: string;
14
+ }
15
+ export interface ChatRequest {
16
+ model?: string;
17
+ messages?: ChatMessage[];
18
+ tools?: Array<{
19
+ type?: string;
20
+ function?: {
21
+ name?: string;
22
+ description?: string;
23
+ parameters?: unknown;
24
+ };
25
+ }>;
26
+ stream?: boolean;
27
+ }
28
+ export interface PreparedTurn {
29
+ model: string;
30
+ prompt: string;
31
+ lastUserPrompt: string;
32
+ tools: ToolSpec[];
33
+ toolResults: Array<{
34
+ id: string;
35
+ content: string;
36
+ }>;
37
+ /** True when this request is the tool output that continues a paused Cursor run. */
38
+ continuation: boolean;
39
+ }
40
+ export declare function prepareTurn(body: ChatRequest): PreparedTurn;
41
+ export declare function sse(data: unknown): string;
42
+ export declare function chunk(model: string, id: string, delta: Record<string, unknown>, finish: string | null): unknown;
@@ -0,0 +1,77 @@
1
+ function contentText(content) {
2
+ if (typeof content === "string")
3
+ return content;
4
+ if (!Array.isArray(content))
5
+ return "";
6
+ return content
7
+ .map((part) => {
8
+ if (typeof part === "string")
9
+ return part;
10
+ if (part && typeof part === "object" && "text" in part && typeof part.text === "string")
11
+ return part.text;
12
+ return "";
13
+ })
14
+ .filter(Boolean)
15
+ .join("\n");
16
+ }
17
+ export function prepareTurn(body) {
18
+ const messages = body.messages ?? [];
19
+ const toolResults = [];
20
+ const lines = [];
21
+ let lastUserPrompt = "";
22
+ for (const message of messages) {
23
+ if (message.role === "tool" || message.tool_call_id) {
24
+ toolResults.push({
25
+ id: message.tool_call_id || "tool",
26
+ content: contentText(message.content),
27
+ });
28
+ continue;
29
+ }
30
+ const text = contentText(message.content);
31
+ if (message.role === "system")
32
+ lines.push(`System:\n${text}`);
33
+ else if (message.role === "assistant") {
34
+ const calls = (message.tool_calls ?? [])
35
+ .map((call) => `${call.function?.name ?? "tool"}(${call.function?.arguments ?? ""})`)
36
+ .join("\n");
37
+ lines.push(`Assistant:\n${[text, calls].filter(Boolean).join("\n")}`);
38
+ }
39
+ else {
40
+ lastUserPrompt = text;
41
+ lines.push(`User:\n${text}`);
42
+ }
43
+ }
44
+ let trailing = messages.length;
45
+ while (trailing > 0 && (messages[trailing - 1]?.role === "tool" || messages[trailing - 1]?.tool_call_id))
46
+ trailing -= 1;
47
+ const continuation = trailing < messages.length;
48
+ const tools = (body.tools ?? [])
49
+ .map((tool) => tool.function)
50
+ .filter((fn) => Boolean(fn?.name))
51
+ .map((fn) => ({
52
+ name: fn.name,
53
+ description: fn.description,
54
+ parameters: fn.parameters,
55
+ }));
56
+ return {
57
+ model: body.model || "auto",
58
+ prompt: lines.join("\n\n"),
59
+ lastUserPrompt: lastUserPrompt || lines.join("\n\n"),
60
+ tools,
61
+ toolResults: continuation ? toolResults.slice(toolResults.length - (messages.length - trailing)) : [],
62
+ continuation,
63
+ };
64
+ }
65
+ export function sse(data) {
66
+ return `data: ${JSON.stringify(data)}\n\n`;
67
+ }
68
+ export function chunk(model, id, delta, finish) {
69
+ return {
70
+ id,
71
+ object: "chat.completion.chunk",
72
+ created: Math.floor(Date.now() / 1000),
73
+ model,
74
+ choices: [{ index: 0, delta, finish_reason: finish }],
75
+ };
76
+ }
77
+ //# sourceMappingURL=translate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/proxy/translate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function log(level: "info" | "warn" | "error", message: string, extra?: unknown): void;
@@ -0,0 +1,27 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { cacheDir } from "./paths.js";
4
+ let dirReady = false;
5
+ function logFile() {
6
+ const dir = cacheDir();
7
+ if (!dirReady) {
8
+ fs.mkdirSync(dir, { recursive: true });
9
+ dirReady = true;
10
+ }
11
+ return path.join(dir, "opencodev2-cursor.log");
12
+ }
13
+ export function log(level, message, extra) {
14
+ const line = JSON.stringify({
15
+ time: new Date().toISOString(),
16
+ level,
17
+ message,
18
+ extra: extra === undefined ? undefined : extra,
19
+ });
20
+ try {
21
+ fs.appendFileSync(logFile(), line + "\n");
22
+ }
23
+ catch {
24
+ // Logging must not break a turn.
25
+ }
26
+ }
27
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/util/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,IAAI,QAAQ,GAAG,KAAK,CAAA;AAEpB,SAAS,OAAO;IACd,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAA;IACtB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACtC,QAAQ,GAAG,IAAI,CAAA;IACjB,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,uBAAuB,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,KAAgC,EAAE,OAAe,EAAE,KAAe;IACpF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC9B,KAAK;QACL,OAAO;QACP,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK;KAC/C,CAAC,CAAA;IACF,IAAI,CAAC;QACH,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC,CAAA;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;IACnC,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export declare function cacheDir(): string;
2
+ /** V8 compile cache shared by the SDK worker and direct cursor-agent runs. */
3
+ export declare function compileCacheDir(): string;
4
+ export declare function conversationDir(): string;
5
+ /** Short SDK store path. SQLite fails when the workspace path itself is the store on Windows. */
6
+ export declare function sdkStateDir(workspace: string): string;
@@ -0,0 +1,30 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ export function cacheDir() {
5
+ const override = process.env.OPENCODEV2_CURSOR_CACHE || process.env.OPENCHAMBER_CURSOR_CACHE || process.env.CURSOR_CHAMBER_CACHE;
6
+ if (override)
7
+ return override;
8
+ // Windows keeps %USERPROFILE%\.cache. macOS and Linux follow XDG_CACHE_HOME when it is set.
9
+ const xdg = process.platform === "win32" ? undefined : process.env.XDG_CACHE_HOME;
10
+ return path.join(xdg || path.join(os.homedir(), ".cache"), "opencodev2-cursor");
11
+ }
12
+ /** V8 compile cache shared by the SDK worker and direct cursor-agent runs. */
13
+ export function compileCacheDir() {
14
+ if (process.env.NODE_COMPILE_CACHE)
15
+ return process.env.NODE_COMPILE_CACHE;
16
+ if (process.platform === "win32" && process.env.LOCALAPPDATA) {
17
+ return path.join(process.env.LOCALAPPDATA, "cursor-compile-cache");
18
+ }
19
+ return path.join(cacheDir(), "compile-cache");
20
+ }
21
+ export function conversationDir() {
22
+ return path.join(cacheDir(), "conversations");
23
+ }
24
+ /** Short SDK store path. SQLite fails when the workspace path itself is the store on Windows. */
25
+ export function sdkStateDir(workspace) {
26
+ const root = process.env.CURSOR_ACP_SDK_STATE_ROOT || path.join(os.homedir(), ".cursor", "oc-sdk");
27
+ const id = createHash("sha256").update(workspace).digest("hex").slice(0, 12);
28
+ return path.join(root, id);
29
+ }
30
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/util/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,UAAU,QAAQ;IACtB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAA;IAChI,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAC7B,4FAA4F;IAC5F,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;IACjF,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,EAAE,mBAAmB,CAAC,CAAA;AACjF,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,eAAe;IAC7B,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAA;IACzE,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,sBAAsB,CAAC,CAAA;IACpE,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,eAAe,CAAC,CAAA;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,eAAe,CAAC,CAAA;AAC/C,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,WAAW,CAAC,SAAiB;IAC3C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;IAClG,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAC5E,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;AAC5B,CAAC"}
@@ -0,0 +1,28 @@
1
+ export interface Launch {
2
+ command: string;
3
+ args: string[];
4
+ }
5
+ export interface LaunchLookup {
6
+ pathEnv: string;
7
+ /** Windows PATHEXT. Empty on macOS and Linux, where the launcher has no extension. */
8
+ pathExt: string;
9
+ /** PATH separator. Defaults to the host's `path.delimiter`. */
10
+ delimiter?: string;
11
+ exists(file: string): boolean;
12
+ sibling(file: string, name: string): string | undefined;
13
+ }
14
+ export declare function launchFromPath(bin: string, lookup: LaunchLookup): Launch | undefined;
15
+ export declare function launchFor(file: string, lookup: Pick<LaunchLookup, "sibling">): Launch;
16
+ /** Looks up launchers on the host. Despite the file name, this works on macOS and Linux too. */
17
+ export declare function windowsLookup(platform?: NodeJS.Platform): LaunchLookup;
18
+ export declare function resolveCursorAgentLaunch(explicit?: string): Launch | undefined;
19
+ export interface AgentRuntime {
20
+ node: string;
21
+ index: string;
22
+ }
23
+ /**
24
+ * cursor-agent.ps1 forwards every argument through powershell. A long prompt then exceeds the Windows command-line limit,
25
+ * and Linux caps one argument near 128 KB. Spawn the versioned node binary directly instead.
26
+ * On macOS and Linux `cursor-agent` is usually a symlink into `versions/<ver>/`, so resolve it first.
27
+ */
28
+ export declare function resolveAgentRuntime(launch: Launch, exists?: (file: string) => boolean, platform?: NodeJS.Platform, realpath?: (file: string) => string): AgentRuntime | undefined;