opencodev2-cursor 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +161 -3
- package/dist/backends/agent-argv.mjs +12 -0
- package/dist/backends/agent-cli.d.ts +10 -0
- package/dist/backends/agent-cli.js +117 -0
- package/dist/backends/agent-cli.js.map +1 -0
- package/dist/backends/auto-models.d.ts +26 -0
- package/dist/backends/auto-models.js +43 -0
- package/dist/backends/auto-models.js.map +1 -0
- package/dist/backends/checkpoint.d.ts +8 -0
- package/dist/backends/checkpoint.js +28 -0
- package/dist/backends/checkpoint.js.map +1 -0
- package/dist/backends/native/auth.d.ts +20 -0
- package/dist/backends/native/auth.js +145 -0
- package/dist/backends/native/auth.js.map +1 -0
- package/dist/backends/native/backend.d.ts +12 -0
- package/dist/backends/native/backend.js +105 -0
- package/dist/backends/native/backend.js.map +1 -0
- package/dist/backends/native/h2-bridge.mjs +55 -0
- package/dist/backends/native/proto.d.ts +31 -0
- package/dist/backends/native/proto.js +130 -0
- package/dist/backends/native/proto.js.map +1 -0
- package/dist/backends/sdk-runner.mjs +317 -0
- package/dist/backends/sdk.d.ts +30 -0
- package/dist/backends/sdk.js +304 -0
- package/dist/backends/sdk.js.map +1 -0
- package/dist/backends/select.d.ts +4 -0
- package/dist/backends/select.js +15 -0
- package/dist/backends/select.js.map +1 -0
- package/dist/backends/spawn.d.ts +15 -0
- package/dist/backends/spawn.js +64 -0
- package/dist/backends/spawn.js.map +1 -0
- package/dist/backends/stream-json.d.ts +7 -0
- package/dist/backends/stream-json.js +106 -0
- package/dist/backends/stream-json.js.map +1 -0
- package/dist/backends/types.d.ts +72 -0
- package/dist/backends/types.js +15 -0
- package/dist/backends/types.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +118 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin/auth.d.ts +9 -0
- package/dist/plugin/auth.js +126 -0
- package/dist/plugin/auth.js.map +1 -0
- package/dist/plugin/catalog.d.ts +41 -0
- package/dist/plugin/catalog.js +120 -0
- package/dist/plugin/catalog.js.map +1 -0
- package/dist/proxy/server.d.ts +17 -0
- package/dist/proxy/server.js +265 -0
- package/dist/proxy/server.js.map +1 -0
- package/dist/proxy/title.d.ts +4 -0
- package/dist/proxy/title.js +24 -0
- package/dist/proxy/title.js.map +1 -0
- package/dist/proxy/tools.d.ts +4 -0
- package/dist/proxy/tools.js +26 -0
- package/dist/proxy/tools.js.map +1 -0
- package/dist/proxy/translate.d.ts +42 -0
- package/dist/proxy/translate.js +77 -0
- package/dist/proxy/translate.js.map +1 -0
- package/dist/util/log.d.ts +1 -0
- package/dist/util/log.js +27 -0
- package/dist/util/log.js.map +1 -0
- package/dist/util/paths.d.ts +6 -0
- package/dist/util/paths.js +30 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/util/windows.d.ts +28 -0
- package/dist/util/windows.js +123 -0
- package/dist/util/windows.js.map +1 -0
- package/package.json +53 -6
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export interface CursorModel {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
contextWindow: number;
|
|
5
|
+
maxTokens: number;
|
|
6
|
+
}
|
|
7
|
+
export type BackendEvent = {
|
|
8
|
+
type: "text";
|
|
9
|
+
text: string;
|
|
10
|
+
} | {
|
|
11
|
+
type: "reasoning";
|
|
12
|
+
text: string;
|
|
13
|
+
} | {
|
|
14
|
+
type: "tool_call";
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
arguments: string;
|
|
18
|
+
} | {
|
|
19
|
+
type: "done";
|
|
20
|
+
text?: string;
|
|
21
|
+
} | {
|
|
22
|
+
type: "error";
|
|
23
|
+
message: string;
|
|
24
|
+
};
|
|
25
|
+
export interface ToolSpec {
|
|
26
|
+
name: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
parameters?: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface BackendRunRequest {
|
|
31
|
+
model: string;
|
|
32
|
+
/** Cursor SDK model params such as optimize_for for Auto Router. */
|
|
33
|
+
modelParams?: Array<{
|
|
34
|
+
id: string;
|
|
35
|
+
value: string;
|
|
36
|
+
}>;
|
|
37
|
+
/** cursor-agent --model string, including bracket params when needed. */
|
|
38
|
+
cliModel?: string;
|
|
39
|
+
prompt: string;
|
|
40
|
+
/** Latest user message only, used when continuing an existing stateful SDK agent. */
|
|
41
|
+
lastUserPrompt?: string;
|
|
42
|
+
cwd: string;
|
|
43
|
+
tools: ToolSpec[];
|
|
44
|
+
toolResults?: Array<{
|
|
45
|
+
id: string;
|
|
46
|
+
content: string;
|
|
47
|
+
}>;
|
|
48
|
+
continuation?: boolean;
|
|
49
|
+
sessionKey: string;
|
|
50
|
+
signal: AbortSignal;
|
|
51
|
+
}
|
|
52
|
+
export interface CursorBackend {
|
|
53
|
+
readonly id: "cli" | "sdk" | "native";
|
|
54
|
+
listModels(): Promise<CursorModel[]>;
|
|
55
|
+
run(request: BackendRunRequest): AsyncIterable<BackendEvent>;
|
|
56
|
+
/** Start the long-lived worker and load the SDK so the first chat turn is not cold. */
|
|
57
|
+
/** Prefetch models and prewarm the local workspace executor. Returns the model list when available. */
|
|
58
|
+
warmUp?(cwd: string): Promise<CursorModel[]>;
|
|
59
|
+
dispose?(): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
/** Appends tool results the way a stateless backend must, because it has no live tool channel. */
|
|
62
|
+
export declare function promptWithToolResults(prompt: string, toolResults?: ReadonlyArray<{
|
|
63
|
+
id: string;
|
|
64
|
+
content: string;
|
|
65
|
+
}>): string;
|
|
66
|
+
export interface AuthMaterial {
|
|
67
|
+
kind: "cli" | "api-key" | "native";
|
|
68
|
+
apiKey?: string;
|
|
69
|
+
accessToken?: string;
|
|
70
|
+
refreshToken?: string;
|
|
71
|
+
}
|
|
72
|
+
export declare const FALLBACK_MODELS: CursorModel[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Appends tool results the way a stateless backend must, because it has no live tool channel. */
|
|
2
|
+
export function promptWithToolResults(prompt, toolResults) {
|
|
3
|
+
if (!toolResults?.length)
|
|
4
|
+
return prompt;
|
|
5
|
+
return `${prompt}\n\nTool results:\n${toolResults.map((result) => `${result.id}: ${result.content}`).join("\n")}`;
|
|
6
|
+
}
|
|
7
|
+
export const FALLBACK_MODELS = [
|
|
8
|
+
{ id: "auto-cursor-balanced", name: "Auto-Cursor-Balanced", contextWindow: 200000, maxTokens: 32000 },
|
|
9
|
+
{ id: "auto-cursor-cost", name: "Auto-Cursor-Cost", contextWindow: 200000, maxTokens: 32000 },
|
|
10
|
+
{ id: "auto-cursor-intelligence", name: "Auto-Cursor-Intelligence", contextWindow: 200000, maxTokens: 32000 },
|
|
11
|
+
{ id: "auto", name: "Auto-Cursor-Balanced", contextWindow: 200000, maxTokens: 32000 },
|
|
12
|
+
{ id: "composer-2.5", name: "Composer 2.5", contextWindow: 200000, maxTokens: 32000 },
|
|
13
|
+
{ id: "composer-2", name: "Composer 2", contextWindow: 200000, maxTokens: 32000 },
|
|
14
|
+
];
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":"AA+CA,kGAAkG;AAClG,MAAM,UAAU,qBAAqB,CACnC,MAAc,EACd,WAA4D;IAE5D,IAAI,CAAC,WAAW,EAAE,MAAM;QAAE,OAAO,MAAM,CAAA;IACvC,OAAO,GAAG,MAAM,sBAAsB,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAA;AACnH,CAAC;AASD,MAAM,CAAC,MAAM,eAAe,GAAkB;IAC5C,EAAE,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE;IACrG,EAAE,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE;IAC7F,EAAE,EAAE,EAAE,0BAA0B,EAAE,IAAI,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE;IAC7G,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE;IACrF,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE;IACrF,EAAE,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE;CAClF,CAAA"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Plugin } from "@opencode/plugin";
|
|
2
|
+
import { AgentCliBackend } from "./backends/agent-cli.js";
|
|
3
|
+
import { SdkBackend } from "./backends/sdk.js";
|
|
4
|
+
import { NativeBackend } from "./backends/native/backend.js";
|
|
5
|
+
import { ensureSdkApiKey } from "./backends/native/auth.js";
|
|
6
|
+
import { registerIntegrations, resolveAuth } from "./plugin/auth.js";
|
|
7
|
+
import { modelsForPublish, noteDiscovered, publishProvider } from "./plugin/catalog.js";
|
|
8
|
+
import { startSharedProxy } from "./proxy/server.js";
|
|
9
|
+
import { log } from "./util/log.js";
|
|
10
|
+
const OpenCodeV2Cursor = Plugin.define({
|
|
11
|
+
id: "opencodev2-cursor",
|
|
12
|
+
async setup(ctx) {
|
|
13
|
+
await registerIntegrations(ctx);
|
|
14
|
+
const state = {
|
|
15
|
+
port: 0,
|
|
16
|
+
token: "",
|
|
17
|
+
};
|
|
18
|
+
const cli = new AgentCliBackend();
|
|
19
|
+
const sdk = new SdkBackend(async () => {
|
|
20
|
+
const auth = await resolveAuth(ctx);
|
|
21
|
+
const key = auth?.apiKey || auth?.accessToken;
|
|
22
|
+
if (!key)
|
|
23
|
+
throw new Error("Connect Cursor with an API key, or set CURSOR_API_KEY, before using the SDK backend.");
|
|
24
|
+
return ensureSdkApiKey(key, auth?.refreshToken);
|
|
25
|
+
});
|
|
26
|
+
const native = new NativeBackend(async () => {
|
|
27
|
+
const auth = await resolveAuth(ctx);
|
|
28
|
+
if (!auth?.accessToken)
|
|
29
|
+
throw new Error("Use Cursor account (native OAuth) before the native backend.");
|
|
30
|
+
// Connect-RPC wants the OAuth access token. The SDK wants a minted crsr_ user API key.
|
|
31
|
+
if (process.env.CURSOR_NATIVE_TRANSPORT === "connect")
|
|
32
|
+
return auth.accessToken;
|
|
33
|
+
return ensureSdkApiKey(auth.accessToken, auth.refreshToken);
|
|
34
|
+
});
|
|
35
|
+
const proxy = await startSharedProxy({
|
|
36
|
+
getAuth: () => resolveAuth(ctx),
|
|
37
|
+
backends: { cli, sdk, native },
|
|
38
|
+
fallbackDirectory: () => ctx.location.directory,
|
|
39
|
+
models: () => modelsForPublish(),
|
|
40
|
+
});
|
|
41
|
+
state.port = proxy.port;
|
|
42
|
+
state.token = proxy.token;
|
|
43
|
+
await publishProvider(ctx, state);
|
|
44
|
+
const warm = async () => {
|
|
45
|
+
const auth = await resolveAuth(ctx);
|
|
46
|
+
if (!auth || auth.kind === "cli")
|
|
47
|
+
return;
|
|
48
|
+
const started = Date.now();
|
|
49
|
+
try {
|
|
50
|
+
if (auth.kind === "native")
|
|
51
|
+
await native.warmUp(ctx.location.directory);
|
|
52
|
+
else
|
|
53
|
+
await sdk.warmUp(ctx.location.directory);
|
|
54
|
+
log("info", "cursor backend warm-up complete", { kind: auth.kind, ms: Date.now() - started });
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
log("warn", "cursor backend warm-up failed", error instanceof Error ? error.message : error);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const refresh = async () => {
|
|
61
|
+
const auth = await resolveAuth(ctx);
|
|
62
|
+
try {
|
|
63
|
+
// SDK/native warmUp lists models and prewarms the workspace executor in one worker round-trip.
|
|
64
|
+
const next = !auth || auth.kind === "cli"
|
|
65
|
+
? await cli.listModels()
|
|
66
|
+
: auth.kind === "native"
|
|
67
|
+
? await native.warmUp(ctx.location.directory)
|
|
68
|
+
: await sdk.warmUp(ctx.location.directory);
|
|
69
|
+
if (noteDiscovered(next))
|
|
70
|
+
log("info", "cursor models updated", modelsForPublish().map((model) => model.id));
|
|
71
|
+
else
|
|
72
|
+
log("info", "cursor model discovery kept the current catalog", modelsForPublish().map((model) => model.id));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
log("warn", "model discovery failed", error instanceof Error ? error.message : error);
|
|
76
|
+
void warm();
|
|
77
|
+
}
|
|
78
|
+
await ctx.provider.reload();
|
|
79
|
+
};
|
|
80
|
+
void refresh();
|
|
81
|
+
await ctx.session.hook("model.request", async (event) => {
|
|
82
|
+
let directory = ctx.location.directory;
|
|
83
|
+
try {
|
|
84
|
+
const session = await ctx.session.get({ sessionID: event.sessionID });
|
|
85
|
+
const located = session;
|
|
86
|
+
directory = located.location?.directory || located.directory || directory;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
log("warn", "session directory lookup failed", error instanceof Error ? error.message : error);
|
|
90
|
+
}
|
|
91
|
+
event.headers["x-opencode-directory"] = encodeURIComponent(directory);
|
|
92
|
+
event.headers["x-opencodev2-cursor-session"] = String(event.sessionID);
|
|
93
|
+
event.headers.authorization = `Bearer ${state.token}`;
|
|
94
|
+
event.baseURL = `http://127.0.0.1:${state.port}/v1`;
|
|
95
|
+
}, { providerID: "cursor" });
|
|
96
|
+
const controller = new AbortController();
|
|
97
|
+
void (async () => {
|
|
98
|
+
try {
|
|
99
|
+
for await (const event of ctx.event.subscribe({ signal: controller.signal })) {
|
|
100
|
+
const type = event.type;
|
|
101
|
+
if (type.includes("connection") || type.includes("credential"))
|
|
102
|
+
void refresh();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Unsubscribing ends the loop.
|
|
107
|
+
}
|
|
108
|
+
})();
|
|
109
|
+
return async () => {
|
|
110
|
+
controller.abort();
|
|
111
|
+
await sdk.dispose();
|
|
112
|
+
await native.dispose();
|
|
113
|
+
await proxy.close();
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
export default OpenCodeV2Cursor;
|
|
118
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Plugin } from "@opencode/plugin";
|
|
2
|
+
import type { AuthMaterial } from "../backends/types.js";
|
|
3
|
+
import { type Launch } from "../util/windows.js";
|
|
4
|
+
export declare const CURSOR_ID = "cursor";
|
|
5
|
+
export declare const AGENT_METHOD = "cursor-agent";
|
|
6
|
+
export declare const NATIVE_METHOD = "cursor-native";
|
|
7
|
+
export declare function cursorAgentLoggedIn(launch: Launch): Promise<boolean>;
|
|
8
|
+
export declare function registerIntegrations(ctx: Plugin.Context): Promise<void>;
|
|
9
|
+
export declare function resolveAuth(ctx: Plugin.Context): Promise<AuthMaterial | undefined>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Credential, Integration } from "@opencode/plugin";
|
|
2
|
+
import { createNativeLogin, pollNativeLogin, refreshNativeToken } from "../backends/native/auth.js";
|
|
3
|
+
import { resolveCursorAgentLaunch } from "../util/windows.js";
|
|
4
|
+
import { collectLaunch, spawnLaunch } from "../backends/spawn.js";
|
|
5
|
+
export const CURSOR_ID = "cursor";
|
|
6
|
+
export const AGENT_METHOD = "cursor-agent";
|
|
7
|
+
export const NATIVE_METHOD = "cursor-native";
|
|
8
|
+
const AGENT_MARKER = "cursor-agent";
|
|
9
|
+
function oauth(methodID, tokens) {
|
|
10
|
+
return Credential.OAuth.make({
|
|
11
|
+
type: "oauth",
|
|
12
|
+
methodID: Integration.MethodID.make(methodID),
|
|
13
|
+
refresh: tokens.refresh,
|
|
14
|
+
access: tokens.access,
|
|
15
|
+
expires: Math.floor(tokens.expires),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export async function cursorAgentLoggedIn(launch) {
|
|
19
|
+
const { code, output } = await collectLaunch(launch, ["status", "--format", "json"], process.cwd());
|
|
20
|
+
if (/not logged in|login required/i.test(output))
|
|
21
|
+
return false;
|
|
22
|
+
return code === 0;
|
|
23
|
+
}
|
|
24
|
+
async function waitForAgentLogin(launch) {
|
|
25
|
+
for (let attempt = 0; attempt < 90; attempt++) {
|
|
26
|
+
if (await cursorAgentLoggedIn(launch))
|
|
27
|
+
return;
|
|
28
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
29
|
+
}
|
|
30
|
+
throw new Error("cursor-agent login timed out. Run `cursor-agent login` and try again.");
|
|
31
|
+
}
|
|
32
|
+
function loginUrlFrom(output) {
|
|
33
|
+
const match = output.match(/https:\/\/\S+/);
|
|
34
|
+
return match?.[0];
|
|
35
|
+
}
|
|
36
|
+
export async function registerIntegrations(ctx) {
|
|
37
|
+
const integrationID = Integration.ID.make(CURSOR_ID);
|
|
38
|
+
await ctx.integration.transform((editor) => {
|
|
39
|
+
editor.update(CURSOR_ID, (integration) => {
|
|
40
|
+
integration.name = "Cursor";
|
|
41
|
+
});
|
|
42
|
+
editor.method.update({
|
|
43
|
+
integrationID,
|
|
44
|
+
method: { id: Integration.MethodID.make(AGENT_METHOD), type: "oauth", label: "Log in with cursor-agent (browser)" },
|
|
45
|
+
async authorize() {
|
|
46
|
+
const launch = resolveCursorAgentLaunch(process.env.CURSOR_AGENT_BIN);
|
|
47
|
+
if (!launch)
|
|
48
|
+
throw new Error("cursor-agent is not installed.");
|
|
49
|
+
const { child, lines } = spawnLaunch(launch, ["login"], process.cwd(), new AbortController().signal);
|
|
50
|
+
let buffer = "";
|
|
51
|
+
const url = await Promise.race([
|
|
52
|
+
(async () => {
|
|
53
|
+
for await (const line of lines) {
|
|
54
|
+
buffer += `${line}\n`;
|
|
55
|
+
const found = loginUrlFrom(buffer);
|
|
56
|
+
if (found)
|
|
57
|
+
return found;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
})(),
|
|
61
|
+
new Promise((resolve) => setTimeout(() => resolve(undefined), 4000)),
|
|
62
|
+
]);
|
|
63
|
+
return {
|
|
64
|
+
mode: "auto",
|
|
65
|
+
url: url || "https://cursor.com/login",
|
|
66
|
+
instructions: "Finish login in the browser. cursor-agent keeps the session.",
|
|
67
|
+
callback: waitForAgentLogin(launch).then(() => {
|
|
68
|
+
child.kill();
|
|
69
|
+
return oauth(AGENT_METHOD, { access: AGENT_MARKER, refresh: AGENT_MARKER, expires: Date.now() + 365 * 24 * 3600_000 });
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
async refresh(credential) {
|
|
74
|
+
return oauth(AGENT_METHOD, {
|
|
75
|
+
access: credential.access,
|
|
76
|
+
refresh: credential.refresh,
|
|
77
|
+
expires: Date.now() + 365 * 24 * 3600_000,
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
editor.method.update({
|
|
82
|
+
integrationID,
|
|
83
|
+
method: { id: Integration.MethodID.make(NATIVE_METHOD), type: "oauth", label: "Cursor account (native OAuth)" },
|
|
84
|
+
async authorize() {
|
|
85
|
+
const login = createNativeLogin();
|
|
86
|
+
return {
|
|
87
|
+
mode: "auto",
|
|
88
|
+
url: login.loginUrl,
|
|
89
|
+
instructions: "Sign in to Cursor. This stores an access token for the native backend.",
|
|
90
|
+
callback: pollNativeLogin(login.uuid, login.verifier).then((tokens) => oauth(NATIVE_METHOD, tokens)),
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
async refresh(credential) {
|
|
94
|
+
const tokens = await refreshNativeToken(credential.refresh);
|
|
95
|
+
return oauth(NATIVE_METHOD, tokens);
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
editor.method.update({
|
|
99
|
+
integrationID,
|
|
100
|
+
method: { type: "key", label: "API key" },
|
|
101
|
+
});
|
|
102
|
+
editor.method.update({
|
|
103
|
+
integrationID,
|
|
104
|
+
method: { type: "env", names: ["CURSOR_API_KEY"] },
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
export async function resolveAuth(ctx) {
|
|
109
|
+
const envKey = process.env.CURSOR_API_KEY;
|
|
110
|
+
const connection = await ctx.integration.connection.active(CURSOR_ID);
|
|
111
|
+
if (!connection)
|
|
112
|
+
return envKey ? { kind: "api-key", apiKey: envKey } : undefined;
|
|
113
|
+
const credential = await ctx.integration.connection.resolve(connection);
|
|
114
|
+
if (!credential)
|
|
115
|
+
return envKey ? { kind: "api-key", apiKey: envKey } : undefined;
|
|
116
|
+
if (credential.type === "key")
|
|
117
|
+
return { kind: "api-key", apiKey: credential.key };
|
|
118
|
+
if (credential.type === "oauth") {
|
|
119
|
+
if (String(credential.methodID) === NATIVE_METHOD) {
|
|
120
|
+
return { kind: "native", accessToken: credential.access, refreshToken: credential.refresh };
|
|
121
|
+
}
|
|
122
|
+
return { kind: "cli" };
|
|
123
|
+
}
|
|
124
|
+
return envKey ? { kind: "api-key", apiKey: envKey } : { kind: "cli" };
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/plugin/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Plugin } from "@opencode/plugin";
|
|
2
|
+
import { type CursorModel } from "../backends/types.js";
|
|
3
|
+
export declare const PROVIDER_ID = "cursor";
|
|
4
|
+
export declare const INTEGRATION_ID = "cursor";
|
|
5
|
+
export declare function modelInfo(model: CursorModel): {
|
|
6
|
+
name: string;
|
|
7
|
+
limit: {
|
|
8
|
+
context: number;
|
|
9
|
+
output: number;
|
|
10
|
+
};
|
|
11
|
+
time: {
|
|
12
|
+
released: number;
|
|
13
|
+
};
|
|
14
|
+
id: string & import("effect/Brand").Brand<"Model.ID">;
|
|
15
|
+
modelID: string & import("effect/Brand").Brand<"Model.ID">;
|
|
16
|
+
providerID: string & import("effect/Brand").Brand<"Provider.ID">;
|
|
17
|
+
capabilities: {
|
|
18
|
+
tools: true;
|
|
19
|
+
input: string[];
|
|
20
|
+
output: string[];
|
|
21
|
+
};
|
|
22
|
+
variants: never[];
|
|
23
|
+
cost: never[];
|
|
24
|
+
status: "active";
|
|
25
|
+
enabled: true;
|
|
26
|
+
};
|
|
27
|
+
export declare function resetCatalogForTests(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Cursor Router (`auto-smart`) has three optimize_for modes. Publish them as distinct picker models
|
|
30
|
+
* and keep `auto` as an OpenCode-compatible alias of Auto-Cursor-Balanced.
|
|
31
|
+
*/
|
|
32
|
+
export declare function ensureRoutableAliases(models: CursorModel[]): CursorModel[];
|
|
33
|
+
/** @deprecated Prefer resolveCursorModel; kept for callers that only need the Cursor id string. */
|
|
34
|
+
export declare function resolveCursorModelId(model: string): string;
|
|
35
|
+
export declare function modelsForPublish(): CursorModel[];
|
|
36
|
+
/** Keep a real Cursor model list. A failed discovery returns the shared fallback array and must not wipe it. */
|
|
37
|
+
export declare function noteDiscovered(next: CursorModel[]): boolean;
|
|
38
|
+
export declare function publishProvider(ctx: Plugin.Context, state: {
|
|
39
|
+
port: number;
|
|
40
|
+
token: string;
|
|
41
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Integration, Model, Provider } from "@opencode/plugin";
|
|
4
|
+
import { AUTO_CURSOR_MODELS, resolveCursorModel } from "../backends/auto-models.js";
|
|
5
|
+
import { FALLBACK_MODELS } from "../backends/types.js";
|
|
6
|
+
import { cacheDir } from "../util/paths.js";
|
|
7
|
+
export const PROVIDER_ID = "cursor";
|
|
8
|
+
export const INTEGRATION_ID = "cursor";
|
|
9
|
+
export function modelInfo(model) {
|
|
10
|
+
const providerID = Provider.ID.make(PROVIDER_ID);
|
|
11
|
+
const id = Model.ID.make(model.id);
|
|
12
|
+
return {
|
|
13
|
+
...Model.Info.default(providerID, id),
|
|
14
|
+
name: model.name,
|
|
15
|
+
limit: { context: model.contextWindow, output: model.maxTokens },
|
|
16
|
+
time: { released: 0 },
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function modelsFile() {
|
|
20
|
+
return path.join(cacheDir(), "models.json");
|
|
21
|
+
}
|
|
22
|
+
function isModel(value) {
|
|
23
|
+
if (!value || typeof value !== "object")
|
|
24
|
+
return false;
|
|
25
|
+
const model = value;
|
|
26
|
+
return typeof model.id === "string" && model.id.length > 0 && typeof model.name === "string"
|
|
27
|
+
&& typeof model.contextWindow === "number" && typeof model.maxTokens === "number";
|
|
28
|
+
}
|
|
29
|
+
function readPersisted() {
|
|
30
|
+
try {
|
|
31
|
+
const parsed = JSON.parse(fs.readFileSync(modelsFile(), "utf8"));
|
|
32
|
+
if (!Array.isArray(parsed))
|
|
33
|
+
return [];
|
|
34
|
+
return parsed.filter(isModel);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
let current;
|
|
41
|
+
export function resetCatalogForTests() {
|
|
42
|
+
current = undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Cursor Router (`auto-smart`) has three optimize_for modes. Publish them as distinct picker models
|
|
46
|
+
* and keep `auto` as an OpenCode-compatible alias of Auto-Cursor-Balanced.
|
|
47
|
+
*/
|
|
48
|
+
export function ensureRoutableAliases(models) {
|
|
49
|
+
const byId = new Map(models.map((model) => [model.id, model]));
|
|
50
|
+
const source = byId.get("auto-smart")
|
|
51
|
+
|| byId.get("default")
|
|
52
|
+
|| byId.get("auto-cursor-balanced")
|
|
53
|
+
|| byId.get("auto")
|
|
54
|
+
|| FALLBACK_MODELS[0];
|
|
55
|
+
byId.delete("auto-smart");
|
|
56
|
+
byId.delete("default");
|
|
57
|
+
// `auto` routes to Balanced for compatibility, but publishing it makes a second
|
|
58
|
+
// picker row with the same display name as Auto-Cursor-Balanced.
|
|
59
|
+
byId.delete("auto");
|
|
60
|
+
for (const auto of AUTO_CURSOR_MODELS) {
|
|
61
|
+
byId.set(auto.id, {
|
|
62
|
+
id: auto.id,
|
|
63
|
+
name: auto.name,
|
|
64
|
+
contextWindow: source.contextWindow,
|
|
65
|
+
maxTokens: source.maxTokens,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const autos = ["auto-cursor-balanced", "auto-cursor-cost", "auto-cursor-intelligence"];
|
|
69
|
+
const rest = [...byId.values()].filter((model) => !autos.includes(model.id));
|
|
70
|
+
return [
|
|
71
|
+
...autos.map((id) => byId.get(id)),
|
|
72
|
+
...rest,
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
/** @deprecated Prefer resolveCursorModel; kept for callers that only need the Cursor id string. */
|
|
76
|
+
export function resolveCursorModelId(model) {
|
|
77
|
+
return resolveCursorModel(model).cursorId;
|
|
78
|
+
}
|
|
79
|
+
export function modelsForPublish() {
|
|
80
|
+
if (!current) {
|
|
81
|
+
const persisted = readPersisted();
|
|
82
|
+
current = ensureRoutableAliases(persisted.length > 0 ? persisted : [...FALLBACK_MODELS]);
|
|
83
|
+
}
|
|
84
|
+
return current;
|
|
85
|
+
}
|
|
86
|
+
/** Keep a real Cursor model list. A failed discovery returns the shared fallback array and must not wipe it. */
|
|
87
|
+
export function noteDiscovered(next) {
|
|
88
|
+
if (next === FALLBACK_MODELS || next.length === 0)
|
|
89
|
+
return false;
|
|
90
|
+
current = ensureRoutableAliases(next.map((model) => ({ ...model })));
|
|
91
|
+
try {
|
|
92
|
+
fs.mkdirSync(cacheDir(), { recursive: true });
|
|
93
|
+
fs.writeFileSync(modelsFile(), JSON.stringify(current));
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// The in-memory catalog still routes this process.
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
export async function publishProvider(ctx, state) {
|
|
101
|
+
const providerID = Provider.ID.make(PROVIDER_ID);
|
|
102
|
+
await ctx.provider.transform((editor) => {
|
|
103
|
+
editor.add({
|
|
104
|
+
info: {
|
|
105
|
+
...Provider.Info.empty(providerID),
|
|
106
|
+
name: "Cursor",
|
|
107
|
+
activation: "enabled",
|
|
108
|
+
integrationID: Integration.ID.make("cursor"),
|
|
109
|
+
package: "@opencode/ai/providers/openai-compatible",
|
|
110
|
+
settings: {
|
|
111
|
+
baseURL: `http://127.0.0.1:${state.port}/v1`,
|
|
112
|
+
apiKey: state.token,
|
|
113
|
+
},
|
|
114
|
+
headers: { Authorization: `Bearer ${state.token}` },
|
|
115
|
+
},
|
|
116
|
+
models: modelsForPublish().map((model) => modelInfo(model)),
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/plugin/catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AuthMaterial, CursorBackend, CursorModel } from "../backends/types.js";
|
|
2
|
+
export interface ProxyDeps {
|
|
3
|
+
getAuth: () => Promise<AuthMaterial | undefined>;
|
|
4
|
+
backends: Record<"cli" | "sdk" | "native", CursorBackend>;
|
|
5
|
+
fallbackDirectory: () => string;
|
|
6
|
+
models: () => CursorModel[];
|
|
7
|
+
}
|
|
8
|
+
export interface RunningProxy {
|
|
9
|
+
port: number;
|
|
10
|
+
token: string;
|
|
11
|
+
close: () => Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
/** OpenCode sends the Cursor integration credential as Authorization, not the proxy token. The proxy only listens on loopback, so a local caller is allowed. */
|
|
14
|
+
export declare function isLoopback(address: string | undefined): boolean;
|
|
15
|
+
/** One proxy per process. Each OpenCode project registers its own auth and backends; requests use the latest one still attached. */
|
|
16
|
+
export declare function startSharedProxy(deps: ProxyDeps): Promise<RunningProxy>;
|
|
17
|
+
export declare function startProxy(depsOrGet: ProxyDeps | (() => ProxyDeps)): Promise<RunningProxy>;
|