@playwo/opencode-cursor-oauth 0.0.0-dev.c80ebcb27754 → 0.0.0-dev.e3644b4a140d
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 +19 -91
- package/dist/auth.js +26 -1
- package/dist/index.js +128 -51
- package/dist/logger.d.ts +6 -0
- package/dist/logger.js +142 -0
- package/dist/models.d.ts +3 -0
- package/dist/models.js +79 -31
- package/dist/proxy.d.ts +1 -0
- package/dist/proxy.js +575 -107
- package/package.json +1 -1
package/dist/models.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cursor model discovery via GetUsableModels.
|
|
3
|
-
* Uses the H2 bridge for transport. Falls back to a hardcoded list
|
|
4
|
-
* when discovery fails.
|
|
5
|
-
*/
|
|
6
1
|
import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
|
|
7
2
|
import { z } from "zod";
|
|
3
|
+
import { errorDetails, logPluginError, logPluginWarn } from "./logger";
|
|
8
4
|
import { callCursorUnaryRpc } from "./proxy";
|
|
9
5
|
import { GetUsableModelsRequestSchema, GetUsableModelsResponseSchema, } from "./proto/agent_pb";
|
|
10
6
|
const GET_USABLE_MODELS_PATH = "/agent.v1.AgentService/GetUsableModels";
|
|
7
|
+
const MODEL_DISCOVERY_TIMEOUT_MS = 5_000;
|
|
11
8
|
const DEFAULT_CONTEXT_WINDOW = 200_000;
|
|
12
9
|
const DEFAULT_MAX_TOKENS = 64_000;
|
|
13
10
|
const CursorModelDetailsSchema = z.object({
|
|
@@ -22,24 +19,12 @@ const CursorModelDetailsSchema = z.object({
|
|
|
22
19
|
.transform((aliases) => (aliases ?? []).filter((alias) => typeof alias === "string")),
|
|
23
20
|
thinkingDetails: z.unknown().optional(),
|
|
24
21
|
});
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{ id: "claude-4.6-sonnet-medium", name: "Claude 4.6 Sonnet", reasoning: true, contextWindow: 200_000, maxTokens: 64_000 },
|
|
32
|
-
{ id: "claude-4.5-sonnet", name: "Claude 4.5 Sonnet", reasoning: true, contextWindow: 200_000, maxTokens: 64_000 },
|
|
33
|
-
// GPT models
|
|
34
|
-
{ id: "gpt-5.4-medium", name: "GPT-5.4", reasoning: true, contextWindow: 272_000, maxTokens: 128_000 },
|
|
35
|
-
{ id: "gpt-5.2", name: "GPT-5.2", reasoning: true, contextWindow: 400_000, maxTokens: 128_000 },
|
|
36
|
-
{ id: "gpt-5.2-codex", name: "GPT-5.2 Codex", reasoning: true, contextWindow: 400_000, maxTokens: 128_000 },
|
|
37
|
-
{ id: "gpt-5.3-codex", name: "GPT-5.3 Codex", reasoning: true, contextWindow: 400_000, maxTokens: 128_000 },
|
|
38
|
-
{ id: "gpt-5.3-codex-spark-preview", name: "GPT-5.3 Codex Spark", reasoning: true, contextWindow: 128_000, maxTokens: 128_000 },
|
|
39
|
-
// Other models
|
|
40
|
-
{ id: "gemini-3.1-pro", name: "Gemini 3.1 Pro", reasoning: true, contextWindow: 1_000_000, maxTokens: 64_000 },
|
|
41
|
-
{ id: "grok-code-fast-1", name: "Grok Code Fast 1", reasoning: false, contextWindow: 128_000, maxTokens: 64_000 },
|
|
42
|
-
];
|
|
22
|
+
export class CursorModelDiscoveryError extends Error {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "CursorModelDiscoveryError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
43
28
|
async function fetchCursorUsableModels(apiKey) {
|
|
44
29
|
try {
|
|
45
30
|
const requestPayload = create(GetUsableModelsRequestSchema, {});
|
|
@@ -48,18 +33,51 @@ async function fetchCursorUsableModels(apiKey) {
|
|
|
48
33
|
accessToken: apiKey,
|
|
49
34
|
rpcPath: GET_USABLE_MODELS_PATH,
|
|
50
35
|
requestBody,
|
|
36
|
+
timeoutMs: MODEL_DISCOVERY_TIMEOUT_MS,
|
|
51
37
|
});
|
|
52
|
-
if (response.timedOut
|
|
53
|
-
|
|
38
|
+
if (response.timedOut) {
|
|
39
|
+
logPluginError("Cursor model discovery timed out", {
|
|
40
|
+
rpcPath: GET_USABLE_MODELS_PATH,
|
|
41
|
+
timeoutMs: MODEL_DISCOVERY_TIMEOUT_MS,
|
|
42
|
+
});
|
|
43
|
+
throw new CursorModelDiscoveryError(`Cursor model discovery timed out after ${MODEL_DISCOVERY_TIMEOUT_MS}ms.`);
|
|
44
|
+
}
|
|
45
|
+
if (response.exitCode !== 0) {
|
|
46
|
+
logPluginError("Cursor model discovery HTTP failure", {
|
|
47
|
+
rpcPath: GET_USABLE_MODELS_PATH,
|
|
48
|
+
exitCode: response.exitCode,
|
|
49
|
+
responseBody: response.body,
|
|
50
|
+
});
|
|
51
|
+
throw new CursorModelDiscoveryError(buildDiscoveryHttpError(response.exitCode, response.body));
|
|
52
|
+
}
|
|
53
|
+
if (response.body.length === 0) {
|
|
54
|
+
logPluginWarn("Cursor model discovery returned an empty response", {
|
|
55
|
+
rpcPath: GET_USABLE_MODELS_PATH,
|
|
56
|
+
});
|
|
57
|
+
throw new CursorModelDiscoveryError("Cursor model discovery returned an empty response.");
|
|
54
58
|
}
|
|
55
59
|
const decoded = decodeGetUsableModelsResponse(response.body);
|
|
56
|
-
if (!decoded)
|
|
57
|
-
|
|
60
|
+
if (!decoded) {
|
|
61
|
+
logPluginError("Cursor model discovery returned an unreadable response", {
|
|
62
|
+
rpcPath: GET_USABLE_MODELS_PATH,
|
|
63
|
+
responseBody: response.body,
|
|
64
|
+
});
|
|
65
|
+
throw new CursorModelDiscoveryError("Cursor model discovery returned an unreadable response.");
|
|
66
|
+
}
|
|
58
67
|
const models = normalizeCursorModels(decoded.models);
|
|
59
|
-
|
|
68
|
+
if (models.length === 0) {
|
|
69
|
+
throw new CursorModelDiscoveryError("Cursor model discovery returned no usable models.");
|
|
70
|
+
}
|
|
71
|
+
return models;
|
|
60
72
|
}
|
|
61
|
-
catch {
|
|
62
|
-
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error instanceof CursorModelDiscoveryError)
|
|
75
|
+
throw error;
|
|
76
|
+
logPluginError("Cursor model discovery crashed", {
|
|
77
|
+
rpcPath: GET_USABLE_MODELS_PATH,
|
|
78
|
+
...errorDetails(error),
|
|
79
|
+
});
|
|
80
|
+
throw new CursorModelDiscoveryError("Cursor model discovery failed.");
|
|
63
81
|
}
|
|
64
82
|
}
|
|
65
83
|
let cachedModels = null;
|
|
@@ -67,13 +85,43 @@ export async function getCursorModels(apiKey) {
|
|
|
67
85
|
if (cachedModels)
|
|
68
86
|
return cachedModels;
|
|
69
87
|
const discovered = await fetchCursorUsableModels(apiKey);
|
|
70
|
-
cachedModels = discovered
|
|
88
|
+
cachedModels = discovered;
|
|
71
89
|
return cachedModels;
|
|
72
90
|
}
|
|
73
91
|
/** @internal Test-only. */
|
|
74
92
|
export function clearModelCache() {
|
|
75
93
|
cachedModels = null;
|
|
76
94
|
}
|
|
95
|
+
function buildDiscoveryHttpError(exitCode, body) {
|
|
96
|
+
const detail = extractDiscoveryErrorDetail(body);
|
|
97
|
+
const protocolHint = exitCode === 464
|
|
98
|
+
? " Likely protocol mismatch: Cursor appears to expect an HTTP/2 Connect unary request."
|
|
99
|
+
: "";
|
|
100
|
+
if (!detail) {
|
|
101
|
+
return `Cursor model discovery failed with HTTP ${exitCode}.${protocolHint}`;
|
|
102
|
+
}
|
|
103
|
+
return `Cursor model discovery failed with HTTP ${exitCode}: ${detail}.${protocolHint}`;
|
|
104
|
+
}
|
|
105
|
+
function extractDiscoveryErrorDetail(body) {
|
|
106
|
+
if (body.length === 0)
|
|
107
|
+
return null;
|
|
108
|
+
const text = new TextDecoder().decode(body).trim();
|
|
109
|
+
if (!text)
|
|
110
|
+
return null;
|
|
111
|
+
try {
|
|
112
|
+
const parsed = JSON.parse(text);
|
|
113
|
+
const code = typeof parsed.code === "string" ? parsed.code : undefined;
|
|
114
|
+
const message = typeof parsed.message === "string" ? parsed.message : undefined;
|
|
115
|
+
if (message && code)
|
|
116
|
+
return `${message} (${code})`;
|
|
117
|
+
if (message)
|
|
118
|
+
return message;
|
|
119
|
+
if (code)
|
|
120
|
+
return code;
|
|
121
|
+
}
|
|
122
|
+
catch { }
|
|
123
|
+
return text.length > 200 ? `${text.slice(0, 197)}...` : text;
|
|
124
|
+
}
|
|
77
125
|
function decodeGetUsableModelsResponse(payload) {
|
|
78
126
|
try {
|
|
79
127
|
return fromBinary(GetUsableModelsResponseSchema, payload);
|
package/dist/proxy.d.ts
CHANGED