@xiaoso/dsh-tool-plus 0.1.16 → 0.1.17-beta.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/lib/{abort-DZ54CaKU.mjs → abort-BTblp0Rt.mjs} +2 -2
- package/lib/{alibaba-coding-plan-DODpkcqi.mjs → alibaba-coding-plan-t3R4YKCB.mjs} +3 -3
- package/lib/{alibaba-token-plan-C6lpKCda.mjs → alibaba-token-plan-CO4djG1y.mjs} +4 -4
- package/lib/{alibaba-token-plan-CHGyWOfX.mjs → alibaba-token-plan-CUjYJX0-.mjs} +1 -1
- package/lib/{anthropic-Dv_GGX6g.mjs → anthropic-nf7w6so1.mjs} +48 -21
- package/lib/{api-key-validation-MnMf_ihX.mjs → api-key-validation-C_tZ7Y9a.mjs} +2 -2
- package/lib/behavior-BwvTUtag.mjs +111 -0
- package/lib/{browser-render-BZXt548F.mjs → browser-render-BQaEVK_n.mjs} +4 -4
- package/lib/{reference-CtvM4zLU.mjs → bundled-references-CrQ4dQcT.mjs} +57 -4
- package/lib/cascade-BWjRTD1q.mjs +30265 -0
- package/lib/{cline-pass-BT9CBEOj.mjs → cline-pass-Bjm-O8GG.mjs} +2 -2
- package/lib/cloudflare-CKXRJ0l9.mjs +37 -0
- package/lib/{cloudflare-ai-gateway-DReN6ohJ.mjs → cloudflare-ai-gateway-BpnBSjwV.mjs} +8 -4
- package/lib/cloudflare-workers-ai-odj9XswL.mjs +32 -0
- package/lib/collapse-_nkb2dn7.mjs +1945 -0
- package/lib/{coreweave-C82HrLpb.mjs → coreweave-CGDZOD8-.mjs} +1 -1
- package/lib/{coreweave-C68UgRQO.mjs → coreweave-DwffcgW5.mjs} +4 -4
- package/lib/cursor-BgOuV7Qf.mjs +794 -0
- package/lib/{cursor-_gHHq-Dq.mjs → cursor-CF1iHHbv.mjs} +68 -19
- package/lib/{cursor-proto-Cr_WEMuv.mjs → cursor-proto-BuNTjX3L.mjs} +823 -23
- package/lib/{devin-QpfTSl9H.mjs → devin-7OEeEb80.mjs} +112 -31
- package/lib/{devin-proto-DYhp4Ty6.mjs → devin-proto-C-QreeAv.mjs} +71 -5
- package/lib/{dom-DG3sPESc.mjs → dom-BmbZv4v9.mjs} +598 -182
- package/lib/{error-O63pC8zg.mjs → error-8LVo7tug.mjs} +725 -104
- package/lib/factory-droid-ChhmsGWb.mjs +71 -0
- package/lib/{fs-promises-shim-B2YFWkcd.mjs → fs-promises-shim-CANAxFWh.mjs} +10 -1
- package/lib/{github-copilot-BMEcw8N1.mjs → github-copilot-QwevdT30.mjs} +4 -4
- package/lib/{github-copilot-headers-DnjGEbko.mjs → github-copilot-headers-CAUZ0rUD.mjs} +138 -26
- package/lib/{gitlab-duo-DnHqJjFY.mjs → gitlab-duo-DigV7k_o.mjs} +2 -2
- package/lib/{google-antigravity-B1mSckrK.mjs → google-antigravity-D-oe0r8A.mjs} +6 -7
- package/lib/{google-gemini-cli-DJwThCCn.mjs → google-gemini-cli-CQBHgGJT.mjs} +5 -6
- package/lib/{google-oauth-shared-BctLpVUN.mjs → google-oauth-shared-CvgVIUwJ.mjs} +2 -2
- package/lib/google-validation-CdvRj4ec.mjs +126 -0
- package/lib/id-EJwVLDHE.mjs +2548 -0
- package/lib/index.mjs +248 -258
- package/lib/{kilo-BEgEwMJQ.mjs → kilo-5r6OffCa.mjs} +2 -2
- package/lib/{kimi-T9NldEIJ.mjs → kimi-DOOtbXmm.mjs} +9 -10
- package/lib/{launch-BIWNyrBB.mjs → launch-DkAjib7h.mjs} +1 -1
- package/lib/{markit-Dvp2JPdT.mjs → markit-CCcyeAds.mjs} +359 -140
- package/lib/{models-DnXMYGcJ.mjs → models-DCpDT_im.mjs} +199203 -112275
- package/lib/{muse-code-CNvqqAp6.mjs → muse-code-CYf8zj_B.mjs} +5 -5
- package/lib/{openai-codex-D5-CyTxa.mjs → openai-codex-Cj-TvSFh.mjs} +13 -10
- package/lib/{perplexity-A2PEoGaC.mjs → perplexity-Dh_Qgsmx.mjs} +68 -25
- package/lib/{pkce-BYjmugmw.mjs → pkce-DO-jBJLV.mjs} +1 -1
- package/lib/pricing-DusBEhwm.mjs +593 -0
- package/lib/{readability-uGhqb4GC.mjs → readability-XHK0o3bv.mjs} +287 -27
- package/lib/{scrapers-C1ujjV8k.mjs → scrapers-B6PADOVm.mjs} +13 -13
- package/lib/slugify-ynYWBIA5.mjs +11 -0
- package/lib/snowflake-B_jap6lz.mjs +666 -0
- package/lib/snowflake-Cu1ZyOp6.mjs +151 -0
- package/lib/{src-AELUUb5U.mjs → src-BhinCSpi.mjs} +1172 -458
- package/lib/{stream-AVOoe5Hj.mjs → stream-D2ivg3GH.mjs} +3740 -5311
- package/lib/{turndown-D6fof1OX.mjs → turndown-Vya8SavL.mjs} +82 -30
- package/lib/types/tools/hashline/native/index.d.ts +2 -2
- package/lib/types/tools/hashline/native/policy.d.ts +19 -21
- package/lib/types/tools/omp/session/agent-storage.d.ts +3 -3
- package/lib/types/tools/shared/bun-named-shim.d.ts +5 -0
- package/lib/{types-BLrLohwK.mjs → types-tH1v9uRm.mjs} +2 -2
- package/lib/{utils-cZX3f_Ls.mjs → utils-C_Ncya1r.mjs} +1737 -509
- package/lib/utils-D2tK38xq.mjs +34006 -0
- package/lib/{utils-I4I8azHN.mjs → utils-DrDwmNtu.mjs} +8 -3
- package/lib/{xai-oauth-CbeldUSV.mjs → xai-oauth-DLLzX6my.mjs} +4 -4
- package/lib/{xiaomi-Dl3VSs04.mjs → xiaomi-CqK-KivK.mjs} +2 -2
- package/lib/{zai-FyjGSypr.mjs → zai-DoJ2xPIH.mjs} +2 -2
- package/lib/{zip-DRrycScC.mjs → zip-DG1mUV6X.mjs} +1 -1
- package/package.json +8 -8
- package/lib/amazon-bedrock-Dx0rr9dY.mjs +0 -996
- package/lib/anthropic-ClCw0PV_.mjs +0 -3624
- package/lib/anthropic-wire-lbGAFzYb.mjs +0 -927
- package/lib/azure-openai-responses-WE_bgFWp.mjs +0 -285
- package/lib/bundled-references-DY3EJxZV.mjs +0 -56
- package/lib/cloudflare-ai-gateway-CrMa97Ap.mjs +0 -44
- package/lib/collapse-CkMNRNr7.mjs +0 -2304
- package/lib/cursor-C6jaIJDg.mjs +0 -283
- package/lib/cursor-D-XZYDf_.mjs +0 -4544
- package/lib/deterministic-id-CUEIMSqR.mjs +0 -17
- package/lib/devin-ExGIghmx.mjs +0 -69
- package/lib/devin-u0ljw5P6.mjs +0 -628
- package/lib/effort-BdtQMN-_.mjs +0 -23
- package/lib/event-stream-BKEtb45Y.mjs +0 -167
- package/lib/gemini-headers-Co9cVvex.mjs +0 -54
- package/lib/google-auth-qAN-6DMW.mjs +0 -232
- package/lib/google-gemini-cli-B2Aw1S5w.mjs +0 -844
- package/lib/google-lTxuq28n.mjs +0 -38
- package/lib/google-shared-Y01j9xOr.mjs +0 -856
- package/lib/google-validation-BEkNs-Bx.mjs +0 -18
- package/lib/google-vertex-DcM-FRC5.mjs +0 -99
- package/lib/hosts-00rjRzUD.mjs +0 -203
- package/lib/id-AL5obQzR.mjs +0 -71
- package/lib/idle-iterator-D0ZXEcUq.mjs +0 -366
- package/lib/ollama-6R0oXZks.mjs +0 -574
- package/lib/openai-codex-responses-C8bnBWOe.mjs +0 -3232
- package/lib/openai-completions-DNr3mUsn.mjs +0 -1430
- package/lib/openai-configuration-update-xMYUjaK_.mjs +0 -150
- package/lib/openai-reasoning-fallback-Ju-5pSR8.mjs +0 -380
- package/lib/openai-responses-CjpafAEr.mjs +0 -861
- package/lib/protobuf-BMgDYu_U.mjs +0 -717
- package/lib/provider-response-SqKSkkUc.mjs +0 -22
- package/lib/proxy-DxvCQOCA.mjs +0 -230
- package/lib/request-debug-BwPOqN_j.mjs +0 -183
- package/lib/sanitize-text-BCf5KmCf.mjs +0 -33
- package/lib/sse-debug-B-tkLCPW.mjs +0 -11
- package/lib/stream-markup-healing-CN3RsqG9.mjs +0 -267
- package/lib/strict-tool-validation-CBTz1dAA.mjs +0 -105
- package/lib/taxonomy-DSIM99rS.mjs +0 -15392
- package/lib/thinking-ngMwf_1Y.mjs +0 -336
- package/lib/type-guards-CttAANEx.mjs +0 -11
- package/lib/utils-CE3DSebh.mjs +0 -11763
- package/lib/vision-guard-BCpXWUj3.mjs +0 -77
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
import { installBunShim as __installBunShim } from "./bun-shim.mjs";
|
|
2
|
+
__installBunShim();
|
|
3
|
+
import "./src-BhinCSpi.mjs";
|
|
4
|
+
import { c as compareRevision, u as parseRevision } from "./cascade-BWjRTD1q.mjs";
|
|
5
|
+
import { t as slugify } from "./slugify-ynYWBIA5.mjs";
|
|
6
|
+
import { L as THINKING_EFFORTS, o as resolveCatalogAxes, s as resolveModelPolicy } from "./id-EJwVLDHE.mjs";
|
|
7
|
+
import { i as datedCost, l as classifyModel, s as parseDatedCostCards } from "./pricing-DusBEhwm.mjs";
|
|
8
|
+
import { i as create, l as toBinary, r as reviewedVariantFamilyId, s as fromBinary, t as collapseVariants } from "./collapse-_nkb2dn7.mjs";
|
|
9
|
+
import { r as toModelSpec } from "./bundled-references-CrQ4dQcT.mjs";
|
|
10
|
+
import { r as getBundledModels } from "./models-DCpDT_im.mjs";
|
|
11
|
+
import { Ar as CURSOR_DEFAULT_BASE_URL, Dr as CURSOR_AVAILABLE_MODELS_PATH, Fr as cursorClientHeaders, Mr as CURSOR_GET_USABLE_MODELS_PATH, ct as GetUsableModelsRequestSchema, jr as CURSOR_GET_DEFAULT_MODEL_PATH, lt as GetUsableModelsResponseSchema, m as AvailableModelsResponseSchema, ot as GetDefaultModelForCliRequestSchema, p as AvailableModelsRequestSchema, st as GetDefaultModelForCliResponseSchema } from "./cursor-proto-BuNTjX3L.mjs";
|
|
12
|
+
import { type } from "@oh-my-pi/omptype";
|
|
13
|
+
import * as http2$1 from "node:http2";
|
|
14
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/discovery/cursor.ts
|
|
15
|
+
const DEFAULT_CONTEXT_WINDOW = 2e5;
|
|
16
|
+
const DEFAULT_MAX_TOKENS = 64e3;
|
|
17
|
+
/**
|
|
18
|
+
* `GetUsableModels` carries no context-window field, so the 1M ceiling is
|
|
19
|
+
* recovered from the signals Cursor does send:
|
|
20
|
+
* - display-name labels ("Opus 5 1M", "GPT-5.5 1M High") across families,
|
|
21
|
+
* - natively 1M families Cursor serves unlabeled (Kimi K3, GLM 5.2+),
|
|
22
|
+
* - the max-mode flag on Claude/Gemini ids, whose max-mode ceiling is 1M.
|
|
23
|
+
*/
|
|
24
|
+
const CURSOR_1M_CONTEXT_WINDOW = 1e6;
|
|
25
|
+
const CURSOR_1M_NAME_PATTERN = /\b1m\b/i;
|
|
26
|
+
const OptionalDisplayNameSchema = type("unknown").pipe((raw) => typeof raw === "string" ? raw : void 0);
|
|
27
|
+
const CursorAliasesSchema = type("unknown").pipe((raw) => {
|
|
28
|
+
if (Array.isArray(raw)) return raw.filter((alias) => typeof alias === "string");
|
|
29
|
+
return [];
|
|
30
|
+
});
|
|
31
|
+
const CursorModelDetailsSchema = type({
|
|
32
|
+
modelId: "string",
|
|
33
|
+
displayName: OptionalDisplayNameSchema.default(void 0),
|
|
34
|
+
displayNameShort: OptionalDisplayNameSchema.default(void 0),
|
|
35
|
+
displayModelId: OptionalDisplayNameSchema.default(void 0),
|
|
36
|
+
aliases: CursorAliasesSchema.default(() => []),
|
|
37
|
+
"thinkingDetails?": "unknown",
|
|
38
|
+
maxMode: "boolean = false"
|
|
39
|
+
});
|
|
40
|
+
const CursorModelsInnerSchema = type("unknown[]");
|
|
41
|
+
const ResilientCursorModelsSchema = type("unknown").pipe((raw) => {
|
|
42
|
+
const out = CursorModelsInnerSchema(raw);
|
|
43
|
+
return out instanceof type.errors ? [] : out;
|
|
44
|
+
});
|
|
45
|
+
const CursorDecodedResponseSchema = type({ models: ResilientCursorModelsSchema.default(() => []) });
|
|
46
|
+
/**
|
|
47
|
+
* Joins Cursor's account-scoped model RPCs:
|
|
48
|
+
* - `AvailableModels` supplies rich base models, parameter axes, variants,
|
|
49
|
+
* capabilities, aliases, and cost-multiplier metadata.
|
|
50
|
+
* - `GetUsableModels` supplies the exact legacy wire slugs the account can run.
|
|
51
|
+
* - `GetDefaultModelForCli` identifies the account's current default.
|
|
52
|
+
*
|
|
53
|
+
* Per-token rates come from the reviewed `providers/cursor.kdl` rate card and
|
|
54
|
+
* the bundled rows; discovery only scales them by a variant's declared multiplier.
|
|
55
|
+
*
|
|
56
|
+
* Returns `null` unless at least one model-list RPC (`GetUsableModels` or
|
|
57
|
+
* `AvailableModels`) decoded. A successful empty catalog returns `[]`,
|
|
58
|
+
* preserving model-manager retry and fallback semantics.
|
|
59
|
+
*/
|
|
60
|
+
async function fetchCursorUsableModels(options) {
|
|
61
|
+
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
62
|
+
const baseUrl = (options.baseUrl ?? "https://api2.cursor.sh").replace(/\/+$/, "");
|
|
63
|
+
const usableRequest = create(GetUsableModelsRequestSchema, { customModelIds: normalizeCustomModelIds(options.customModelIds) });
|
|
64
|
+
const availableRequest = create(AvailableModelsRequestSchema, {
|
|
65
|
+
includeLongContextModels: true,
|
|
66
|
+
useModelParameters: true,
|
|
67
|
+
doNotUseMarkdown: true,
|
|
68
|
+
useCloudAgentEffortModes: true
|
|
69
|
+
});
|
|
70
|
+
const defaultRequest = create(GetDefaultModelForCliRequestSchema, {});
|
|
71
|
+
const [usablePayload, availablePayload, defaultPayload] = await Promise.all([
|
|
72
|
+
fetchCursorUnary(baseUrl, CURSOR_GET_USABLE_MODELS_PATH, toBinary(GetUsableModelsRequestSchema, usableRequest), options, timeoutMs),
|
|
73
|
+
fetchCursorUnary(baseUrl, CURSOR_AVAILABLE_MODELS_PATH, toBinary(AvailableModelsRequestSchema, availableRequest), options, Math.min(timeoutMs, 2e3)),
|
|
74
|
+
fetchCursorUnary(baseUrl, CURSOR_GET_DEFAULT_MODEL_PATH, toBinary(GetDefaultModelForCliRequestSchema, defaultRequest), options, timeoutMs)
|
|
75
|
+
]);
|
|
76
|
+
const usable = decodeUnary(GetUsableModelsResponseSchema, usablePayload);
|
|
77
|
+
const available = decodeUnary(AvailableModelsResponseSchema, availablePayload);
|
|
78
|
+
if (usable === null && available === null) return null;
|
|
79
|
+
const defaultModel = decodeUnary(GetDefaultModelForCliResponseSchema, defaultPayload)?.model;
|
|
80
|
+
const parsedUsable = CursorDecodedResponseSchema(usable);
|
|
81
|
+
const references = createCursorReferenceMap();
|
|
82
|
+
const legacyModels = parsedUsable instanceof type.errors ? [] : normalizeCursorModels(parsedUsable.models, options.baseUrl, references);
|
|
83
|
+
const usableModelIds = usable === null ? void 0 : new Set(legacyModels.map((model) => model.id));
|
|
84
|
+
if (!available || available.models.length === 0) return legacyModels;
|
|
85
|
+
const richModels = normalizeRichCursorModels(available.models, usableModelIds, options.baseUrl, references, defaultModel?.modelId, defaultModel?.maxMode);
|
|
86
|
+
const richCatalogIds = collectRichCursorCatalogIds(available.models);
|
|
87
|
+
const representedIds = /* @__PURE__ */ new Set();
|
|
88
|
+
for (const model of richModels) {
|
|
89
|
+
representedIds.add(model.id);
|
|
90
|
+
representedIds.add(model.requestModelId ?? model.id);
|
|
91
|
+
for (const routeId of Object.keys(model.cursorModelRoutes ?? {})) representedIds.add(routeId);
|
|
92
|
+
}
|
|
93
|
+
for (const model of legacyModels) if (!representedIds.has(model.id) && !richCatalogIds.has(model.id)) richModels.push(model);
|
|
94
|
+
return richModels.sort((a, b) => a.id.localeCompare(b.id));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* One unary RPC under a single deadline. HTTP/1.1 fetch goes first; the
|
|
98
|
+
* HTTP/2 Connect retry runs only after a transport-level failure (h2-only
|
|
99
|
+
* endpoints) and only with the remaining budget. An HTTP response of any
|
|
100
|
+
* status, or a deadline abort, is final.
|
|
101
|
+
*/
|
|
102
|
+
async function fetchCursorUnary(baseUrl, path, body, options, timeoutMs) {
|
|
103
|
+
const deadline = Date.now() + timeoutMs;
|
|
104
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
105
|
+
let response;
|
|
106
|
+
try {
|
|
107
|
+
response = await fetch(new URL(path, baseUrl), {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: {
|
|
110
|
+
...cursorClientHeaders(options.apiKey, { clientVersion: options.clientVersion }),
|
|
111
|
+
"connect-protocol-version": "1",
|
|
112
|
+
"x-request-id": crypto.randomUUID()
|
|
113
|
+
},
|
|
114
|
+
body,
|
|
115
|
+
signal
|
|
116
|
+
});
|
|
117
|
+
} catch {
|
|
118
|
+
const remainingMs = deadline - Date.now();
|
|
119
|
+
if (signal.aborted || remainingMs <= 0) return null;
|
|
120
|
+
return fetchViaHttp2(baseUrl, path, body, options, remainingMs);
|
|
121
|
+
}
|
|
122
|
+
if (!response.ok) return null;
|
|
123
|
+
try {
|
|
124
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** HTTP/2 transport used by Cursor's unary protobuf RPCs. */
|
|
130
|
+
async function fetchViaHttp2(baseUrl, path, body, options, timeoutMs) {
|
|
131
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
132
|
+
const client = http2$1.connect(baseUrl);
|
|
133
|
+
let settled = false;
|
|
134
|
+
const finish = (value) => {
|
|
135
|
+
if (settled) return;
|
|
136
|
+
settled = true;
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
client.close();
|
|
139
|
+
resolve(value);
|
|
140
|
+
};
|
|
141
|
+
const timer = setTimeout(() => {
|
|
142
|
+
client.destroy();
|
|
143
|
+
finish(null);
|
|
144
|
+
}, timeoutMs);
|
|
145
|
+
client.on("error", () => finish(null));
|
|
146
|
+
const req = client.request({
|
|
147
|
+
":method": "POST",
|
|
148
|
+
":path": path,
|
|
149
|
+
te: "trailers",
|
|
150
|
+
...cursorClientHeaders(options.apiKey, { clientVersion: options.clientVersion }),
|
|
151
|
+
"connect-protocol-version": "1",
|
|
152
|
+
"x-request-id": crypto.randomUUID()
|
|
153
|
+
});
|
|
154
|
+
const chunks = [];
|
|
155
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
156
|
+
req.on("end", () => finish(new Uint8Array(Buffer.concat(chunks))));
|
|
157
|
+
req.on("error", () => finish(null));
|
|
158
|
+
req.on("response", (headers) => {
|
|
159
|
+
const status = Number(headers[":status"] ?? 0);
|
|
160
|
+
if (status < 200 || status >= 300) finish(null);
|
|
161
|
+
});
|
|
162
|
+
req.end(Buffer.from(body));
|
|
163
|
+
return promise;
|
|
164
|
+
}
|
|
165
|
+
function normalizeCustomModelIds(customModelIds) {
|
|
166
|
+
if (!customModelIds) return [];
|
|
167
|
+
const normalized = /* @__PURE__ */ new Set();
|
|
168
|
+
for (const value of customModelIds) {
|
|
169
|
+
if (typeof value !== "string") continue;
|
|
170
|
+
const trimmed = value.trim();
|
|
171
|
+
if (!trimmed) continue;
|
|
172
|
+
normalized.add(trimmed);
|
|
173
|
+
}
|
|
174
|
+
return [...normalized];
|
|
175
|
+
}
|
|
176
|
+
function createCursorReferenceMap() {
|
|
177
|
+
const references = /* @__PURE__ */ new Map();
|
|
178
|
+
for (const model of getBundledModels("cursor")) references.set(model.id, toModelSpec(model));
|
|
179
|
+
return references;
|
|
180
|
+
}
|
|
181
|
+
function decodeUnary(schema, payload) {
|
|
182
|
+
if (payload === null) return null;
|
|
183
|
+
const body = decodeConnectUnaryBody(payload) ?? payload;
|
|
184
|
+
try {
|
|
185
|
+
return fromBinary(schema, body);
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function decodeConnectUnaryBody(payload) {
|
|
191
|
+
if (payload.length < 5) return null;
|
|
192
|
+
let offset = 0;
|
|
193
|
+
while (offset + 5 <= payload.length) {
|
|
194
|
+
const flags = payload[offset];
|
|
195
|
+
const messageLength = new DataView(payload.buffer, payload.byteOffset + offset, payload.byteLength - offset).getUint32(1, false);
|
|
196
|
+
const frameEnd = offset + 5 + messageLength;
|
|
197
|
+
if (frameEnd > payload.length) return null;
|
|
198
|
+
if ((flags & 1) !== 0) return null;
|
|
199
|
+
if (!((flags & 2) !== 0)) return payload.subarray(offset + 5, frameEnd);
|
|
200
|
+
offset = frameEnd;
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
function isCursorKimiK3(id) {
|
|
205
|
+
const identity = classifyModel("cursor", id, { lenient: true });
|
|
206
|
+
return identity.class === "kimi" && identity.family === "k3";
|
|
207
|
+
}
|
|
208
|
+
function isCursorVersionedGrok(id) {
|
|
209
|
+
const identity = classifyModel("cursor", id, { lenient: true });
|
|
210
|
+
if (identity.class !== "xai" || identity.revision === void 0) return false;
|
|
211
|
+
const revision = parseRevision(identity.revision);
|
|
212
|
+
const floor = parseRevision("4");
|
|
213
|
+
return revision !== void 0 && floor !== void 0 && compareRevision(revision, floor) >= 0;
|
|
214
|
+
}
|
|
215
|
+
function isCursorGlm52CodingModel(id) {
|
|
216
|
+
const identity = classifyModel("cursor", id, { lenient: true });
|
|
217
|
+
if (identity.class !== "glm" || identity.revision === void 0) return false;
|
|
218
|
+
if (identity.family !== void 0 && identity.family !== "air" && identity.family !== "turbo") return false;
|
|
219
|
+
const revision = parseRevision(identity.revision);
|
|
220
|
+
const floor = parseRevision("5.2");
|
|
221
|
+
return revision !== void 0 && floor !== void 0 && compareRevision(revision, floor) >= 0;
|
|
222
|
+
}
|
|
223
|
+
function collectRichCursorCatalogIds(models) {
|
|
224
|
+
const ids = /* @__PURE__ */ new Set();
|
|
225
|
+
for (const details of models) {
|
|
226
|
+
const baseId = details.name.trim();
|
|
227
|
+
if (baseId) ids.add(baseId);
|
|
228
|
+
for (const id of [...details.legacySlugs, ...details.idAliases]) if (id.trim()) ids.add(id.trim());
|
|
229
|
+
for (const [index, variant] of details.variants.entries()) {
|
|
230
|
+
const parameterSuffix = variant.parameterValues.map((parameter) => ({
|
|
231
|
+
id: parameter.id.trim(),
|
|
232
|
+
value: parameter.value.trim()
|
|
233
|
+
})).filter((parameter) => parameter.id.length > 0).map((parameter) => `${parameter.id}=${parameter.value}`).join(",");
|
|
234
|
+
const id = variant.legacySlug?.trim() || variant.variantStringRepresentation?.trim() || (parameterSuffix ? `${baseId}@${parameterSuffix}` : `${baseId}@variant-${index + 1}`);
|
|
235
|
+
if (id) ids.add(id);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return ids;
|
|
239
|
+
}
|
|
240
|
+
const CURSOR_REASONING_PARAMETER_IDS = /* @__PURE__ */ new Set([
|
|
241
|
+
"effort",
|
|
242
|
+
"reasoning",
|
|
243
|
+
"reasoning_effort",
|
|
244
|
+
"thinking",
|
|
245
|
+
"thinking_effort"
|
|
246
|
+
]);
|
|
247
|
+
/** The declared definition value a variant parameter selects, when Cursor defines one. */
|
|
248
|
+
function cursorParameterValue(details, parameter) {
|
|
249
|
+
const definition = details.parameterDefinitions.find((candidate) => candidate.id === parameter.id);
|
|
250
|
+
if (!definition) return void 0;
|
|
251
|
+
const booleanValue = definition.parameterType?.booleanParameter?.values.find((candidate) => candidate.value === parameter.value);
|
|
252
|
+
const enumValue = definition.parameterType?.enumParameter?.values.find((candidate) => candidate.value === parameter.value);
|
|
253
|
+
const selected = booleanValue ?? enumValue;
|
|
254
|
+
if (!selected) return void 0;
|
|
255
|
+
return {
|
|
256
|
+
increasesModelCost: selected.increasesModelCost,
|
|
257
|
+
markdownTooltip: enumValue?.markdownTooltip,
|
|
258
|
+
definitionTooltip: definition.markdownTooltip
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function cursorVariantCostMultiplier(details, entry) {
|
|
262
|
+
let multiplier = 1;
|
|
263
|
+
for (const parameter of entry.parameters) {
|
|
264
|
+
const selected = cursorParameterValue(details, parameter);
|
|
265
|
+
if (selected?.increasesModelCost !== true) continue;
|
|
266
|
+
for (const description of [selected.markdownTooltip, selected.definitionTooltip]) {
|
|
267
|
+
const match = description ? /\b(\d+(?:\.\d+)?)\s*[x×](?![a-z])/i.exec(description) : void 0;
|
|
268
|
+
if (!match?.[1]) continue;
|
|
269
|
+
const parameterMultiplier = Number(match[1]);
|
|
270
|
+
if (Number.isFinite(parameterMultiplier) && parameterMultiplier > 0) {
|
|
271
|
+
multiplier *= parameterMultiplier;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return multiplier;
|
|
277
|
+
}
|
|
278
|
+
function scaleTokenCost(cost, multiplier) {
|
|
279
|
+
if (multiplier === 1) return cost;
|
|
280
|
+
const scale = (card) => ({
|
|
281
|
+
...card,
|
|
282
|
+
input: card.input * multiplier,
|
|
283
|
+
output: card.output * multiplier,
|
|
284
|
+
cacheRead: card.cacheRead * multiplier,
|
|
285
|
+
cacheWrite: card.cacheWrite * multiplier
|
|
286
|
+
});
|
|
287
|
+
const effectiveRates = cost.timeBased?.effectiveRates;
|
|
288
|
+
return {
|
|
289
|
+
...scale({
|
|
290
|
+
input: cost.input,
|
|
291
|
+
output: cost.output,
|
|
292
|
+
cacheRead: cost.cacheRead,
|
|
293
|
+
cacheWrite: cost.cacheWrite
|
|
294
|
+
}),
|
|
295
|
+
...cost.timeBased && { timeBased: {
|
|
296
|
+
...cost.timeBased,
|
|
297
|
+
...effectiveRates && { effectiveRates: effectiveRates.map(scale) }
|
|
298
|
+
} }
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Price a rich variant from the reviewed `providers/cursor.kdl` rate card,
|
|
303
|
+
* which keys on Cursor wire slugs (`claude-opus-4-8-high`, `gpt-5.4`). Cursor
|
|
304
|
+
* bills per price class — the values of the model's cost-bearing parameters
|
|
305
|
+
* (`fast`, `context`) — not per effort or thinking toggle, so every slug in
|
|
306
|
+
* the variant's own class shares its price. The lane id leads the own-class
|
|
307
|
+
* slugs: `buildModel` later resolves the same card by lane id, so a lane-keyed
|
|
308
|
+
* `cost-patch` must win here too or the two would disagree. Multiplier rule:
|
|
309
|
+
* - `cost-patch` on an own-class slug is that class's reviewed price and is
|
|
310
|
+
* final, even for fast classes (`claude-opus-4-7-high-fast` $30/$150);
|
|
311
|
+
* - `cost-fallback` rows, and any base-class slug or the model name, carry the
|
|
312
|
+
* base card, scaled by the declared multiplier (`cursorVariantCostMultiplier`).
|
|
313
|
+
*/
|
|
314
|
+
function resolveRichCursorCardCost(details, entry, laneId) {
|
|
315
|
+
const costAxes = details.parameterDefinitions.filter((definition) => [...definition.parameterType?.booleanParameter?.values ?? [], ...definition.parameterType?.enumParameter?.values ?? []].some((value) => value.increasesModelCost === true)).map((definition) => definition.id);
|
|
316
|
+
const priceClass = (parameters) => costAxes.map((id) => parameters.find((parameter) => parameter.id === id)?.value ?? "").join("\0");
|
|
317
|
+
const entryClass = priceClass(entry.parameters);
|
|
318
|
+
const ownSlugs = [laneId, entry.id];
|
|
319
|
+
const baseSlugs = [details.name.trim()];
|
|
320
|
+
for (const variant of details.variants) {
|
|
321
|
+
const slug = variant.legacySlug?.trim();
|
|
322
|
+
if (!slug) continue;
|
|
323
|
+
const parameters = variant.parameterValues.map((parameter) => ({
|
|
324
|
+
id: parameter.id.trim(),
|
|
325
|
+
value: parameter.value.trim()
|
|
326
|
+
}));
|
|
327
|
+
if (priceClass(parameters) === entryClass) ownSlugs.push(slug);
|
|
328
|
+
else if (!parameters.some((parameter) => cursorParameterValue(details, parameter)?.increasesModelCost === true)) baseSlugs.push(slug);
|
|
329
|
+
}
|
|
330
|
+
let base;
|
|
331
|
+
for (const slug of ownSlugs) {
|
|
332
|
+
const card = resolveCursorCardAxes(slug);
|
|
333
|
+
if (card.patch) return card.patch;
|
|
334
|
+
base ??= card.fallback;
|
|
335
|
+
}
|
|
336
|
+
for (const slug of baseSlugs) {
|
|
337
|
+
if (base) break;
|
|
338
|
+
const card = resolveCursorCardAxes(slug);
|
|
339
|
+
base = card.patch ?? card.fallback;
|
|
340
|
+
}
|
|
341
|
+
return base && scaleTokenCost(base, cursorVariantCostMultiplier(details, entry));
|
|
342
|
+
}
|
|
343
|
+
function resolveCursorCardAxes(slug) {
|
|
344
|
+
const axes = resolveCatalogAxes({
|
|
345
|
+
id: slug,
|
|
346
|
+
name: slug,
|
|
347
|
+
api: "cursor-agent",
|
|
348
|
+
provider: "cursor",
|
|
349
|
+
baseUrl: CURSOR_DEFAULT_BASE_URL,
|
|
350
|
+
reasoning: false,
|
|
351
|
+
input: ["text"],
|
|
352
|
+
cost: {
|
|
353
|
+
input: 0,
|
|
354
|
+
output: 0,
|
|
355
|
+
cacheRead: 0,
|
|
356
|
+
cacheWrite: 0
|
|
357
|
+
},
|
|
358
|
+
contextWindow: DEFAULT_CONTEXT_WINDOW,
|
|
359
|
+
maxTokens: DEFAULT_MAX_TOKENS
|
|
360
|
+
});
|
|
361
|
+
const card = (payload, axis) => {
|
|
362
|
+
if (typeof payload !== "object" || payload === null) return void 0;
|
|
363
|
+
const field = (key) => {
|
|
364
|
+
const value = Reflect.get(payload, key);
|
|
365
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
366
|
+
};
|
|
367
|
+
const input = field("input");
|
|
368
|
+
const output = field("output");
|
|
369
|
+
if (input === void 0 || output === void 0) return void 0;
|
|
370
|
+
return datedCost({
|
|
371
|
+
input,
|
|
372
|
+
output,
|
|
373
|
+
cacheRead: field("cacheRead") ?? 0,
|
|
374
|
+
cacheWrite: field("cacheWrite") ?? 0
|
|
375
|
+
}, parseDatedCostCards(payload, axis));
|
|
376
|
+
};
|
|
377
|
+
return {
|
|
378
|
+
patch: card(axes.costPatch, "cost-patch"),
|
|
379
|
+
fallback: card(axes.costFallback, "cost-fallback")
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function normalizeRichCursorModels(models, usableModelIds, baseUrlOverride, references, defaultModelId, defaultMaxMode) {
|
|
383
|
+
const normalized = [];
|
|
384
|
+
for (const details of models) {
|
|
385
|
+
const baseId = details.name.trim();
|
|
386
|
+
if (!baseId || details.supportsAgent === false || details.isChatOnly === true) continue;
|
|
387
|
+
if (details.requiresDataRetention === true) continue;
|
|
388
|
+
const variants = normalizeRichCursorVariants(details, baseId, usableModelIds);
|
|
389
|
+
if (variants.length === 0) continue;
|
|
390
|
+
const defaultRank = (entry) => entry.variant?.isDefaultNonMaxConfig === true ? 0 : entry.variant?.isDefaultMaxConfig === true ? 1 : 2;
|
|
391
|
+
const parameterDefaults = /* @__PURE__ */ new Map();
|
|
392
|
+
for (const entry of variants.toSorted((left, right) => defaultRank(left) - defaultRank(right))) for (const parameter of cursorLaneParameters(entry.parameters)) if (!parameterDefaults.has(parameter.id)) parameterDefaults.set(parameter.id, parameter.value);
|
|
393
|
+
const lanes = /* @__PURE__ */ new Map();
|
|
394
|
+
for (const entry of variants) {
|
|
395
|
+
const dimensions = cursorLaneParameters(entry.parameters);
|
|
396
|
+
const key = JSON.stringify([dimensions, entry.variant?.isMaxMode === true]);
|
|
397
|
+
const lane = lanes.get(key);
|
|
398
|
+
if (lane) lane.push(entry);
|
|
399
|
+
else lanes.set(key, [entry]);
|
|
400
|
+
}
|
|
401
|
+
const claimedLaneIds = /* @__PURE__ */ new Set();
|
|
402
|
+
for (const entries of lanes.values()) {
|
|
403
|
+
const first = entries[0];
|
|
404
|
+
if (!first) continue;
|
|
405
|
+
const richLaneId = cursorLaneId(baseId, cursorLaneParameters(first.parameters), parameterDefaults, first.variant?.isMaxMode === true, claimedLaneIds);
|
|
406
|
+
const laneName = cursorLaneName(details, richLaneId, baseId);
|
|
407
|
+
const laneId = reviewedCursorLaneId(entries, claimedLaneIds) ?? richLaneId;
|
|
408
|
+
normalized.push(...buildRichCursorLane(details, entries, laneId, laneName, baseUrlOverride, references, defaultModelId, defaultMaxMode));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return normalized.sort((a, b) => a.id.localeCompare(b.id));
|
|
412
|
+
}
|
|
413
|
+
function normalizeRichCursorVariants(details, baseId, usableModelIds) {
|
|
414
|
+
if (details.variants.length === 0) {
|
|
415
|
+
const candidates = [
|
|
416
|
+
baseId,
|
|
417
|
+
...details.legacySlugs,
|
|
418
|
+
...details.idAliases
|
|
419
|
+
];
|
|
420
|
+
if (usableModelIds !== void 0 && !candidates.some((id) => usableModelIds.has(id))) return [];
|
|
421
|
+
return [{
|
|
422
|
+
id: baseId,
|
|
423
|
+
parameters: [],
|
|
424
|
+
variant: void 0,
|
|
425
|
+
effort: void 0
|
|
426
|
+
}];
|
|
427
|
+
}
|
|
428
|
+
const variants = [];
|
|
429
|
+
const seenRoutes = /* @__PURE__ */ new Set();
|
|
430
|
+
for (const [index, variant] of details.variants.entries()) {
|
|
431
|
+
if (isRichVariantBlocked(details, variant)) continue;
|
|
432
|
+
const parameters = variant.parameterValues.map((parameter) => ({
|
|
433
|
+
id: parameter.id.trim(),
|
|
434
|
+
value: parameter.value.trim()
|
|
435
|
+
})).filter((parameter) => parameter.id.length > 0);
|
|
436
|
+
const parameterSuffix = parameters.map((parameter) => `${parameter.id}=${parameter.value}`).join(",");
|
|
437
|
+
const id = variant.legacySlug?.trim() || variant.variantStringRepresentation?.trim() || (parameterSuffix ? `${baseId}@${parameterSuffix}` : `${baseId}@variant-${index + 1}`);
|
|
438
|
+
const entitlementIds = [
|
|
439
|
+
variant.legacySlug?.trim(),
|
|
440
|
+
id,
|
|
441
|
+
baseId,
|
|
442
|
+
...details.idAliases
|
|
443
|
+
].filter((candidate) => Boolean(candidate));
|
|
444
|
+
if (usableModelIds !== void 0 && !entitlementIds.some((candidate) => usableModelIds.has(candidate))) continue;
|
|
445
|
+
const routeSignature = `${parameterSuffix}\u0000${variant.isMaxMode === true ? "max" : "standard"}`;
|
|
446
|
+
if (seenRoutes.has(routeSignature)) continue;
|
|
447
|
+
seenRoutes.add(routeSignature);
|
|
448
|
+
variants.push({
|
|
449
|
+
id,
|
|
450
|
+
parameters,
|
|
451
|
+
variant,
|
|
452
|
+
effort: cursorVariantEffort(parameters)
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
return variants;
|
|
456
|
+
}
|
|
457
|
+
function cursorVariantEffort(parameters) {
|
|
458
|
+
const thinking = parameters.find((parameter) => parameter.id === "thinking")?.value.toLowerCase();
|
|
459
|
+
if (thinking === "false" || thinking === "off" || thinking === "none") return "off";
|
|
460
|
+
const rawEffort = parameters.find((parameter) => [
|
|
461
|
+
"reasoning",
|
|
462
|
+
"reasoning_effort",
|
|
463
|
+
"thinking_effort",
|
|
464
|
+
"effort"
|
|
465
|
+
].includes(parameter.id))?.value;
|
|
466
|
+
if (rawEffort === void 0) return void 0;
|
|
467
|
+
const normalized = rawEffort.toLowerCase().replace(/[_\s]+/g, "-");
|
|
468
|
+
if (normalized === "none" || normalized === "off" || normalized === "disabled") return "off";
|
|
469
|
+
if (normalized === "minimal" || normalized === "low" || normalized === "medium" || normalized === "high" || normalized === "xhigh" || normalized === "max") return normalized;
|
|
470
|
+
if (normalized === "extra-high") return "xhigh";
|
|
471
|
+
}
|
|
472
|
+
function cursorLaneParameters(parameters) {
|
|
473
|
+
return parameters.filter((parameter) => !CURSOR_REASONING_PARAMETER_IDS.has(parameter.id));
|
|
474
|
+
}
|
|
475
|
+
function cursorLaneId(baseId, parameters, defaults, isMaxMode, claimed) {
|
|
476
|
+
const suffixes = [];
|
|
477
|
+
for (const parameter of parameters) {
|
|
478
|
+
const id = slugify(parameter.id);
|
|
479
|
+
const value = slugify(parameter.value);
|
|
480
|
+
if (!id || !value || parameter.value === "false") continue;
|
|
481
|
+
if (parameter.value === "true") suffixes.push(id);
|
|
482
|
+
else if (parameter.value === defaults.get(parameter.id)) continue;
|
|
483
|
+
else if (parameter.id === "context") suffixes.push(value);
|
|
484
|
+
else suffixes.push(`${id}-${value}`);
|
|
485
|
+
}
|
|
486
|
+
const root = [baseId, ...suffixes].join("-");
|
|
487
|
+
let candidate = root;
|
|
488
|
+
if (claimed.has(candidate)) candidate = `${root}-${isMaxMode ? "max-mode" : "standard"}`;
|
|
489
|
+
let duplicate = 2;
|
|
490
|
+
while (claimed.has(candidate)) {
|
|
491
|
+
candidate = `${root}-${duplicate}`;
|
|
492
|
+
duplicate += 1;
|
|
493
|
+
}
|
|
494
|
+
claimed.add(candidate);
|
|
495
|
+
return candidate;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* The reviewed collapse family owning a lane's legacy slugs, when exactly one
|
|
499
|
+
* family claims any of them (slugs no family lists, e.g. a newly added tier,
|
|
500
|
+
* stay in the lane). `AvailableModels` names Grok 4.5/4.6 `grok-4.6` while its
|
|
501
|
+
* slugs are `cursor-grok-4.6-*`; reusing the family id keeps the lane id equal
|
|
502
|
+
* to the `GetUsableModels`-only fallback and the bundled catalog, so a selector
|
|
503
|
+
* survives whichever RPC answered.
|
|
504
|
+
*/
|
|
505
|
+
function reviewedCursorLaneId(entries, claimed) {
|
|
506
|
+
let familyId;
|
|
507
|
+
for (const entry of entries) {
|
|
508
|
+
const slug = entry.variant?.legacySlug?.trim();
|
|
509
|
+
const owner = slug ? reviewedVariantFamilyId("cursor", slug) : void 0;
|
|
510
|
+
if (owner === void 0) continue;
|
|
511
|
+
if (familyId !== void 0 && owner !== familyId) return void 0;
|
|
512
|
+
familyId = owner;
|
|
513
|
+
}
|
|
514
|
+
if (familyId === void 0 || claimed.has(familyId)) return void 0;
|
|
515
|
+
claimed.add(familyId);
|
|
516
|
+
return familyId;
|
|
517
|
+
}
|
|
518
|
+
function cursorLaneName(details, laneId, baseId) {
|
|
519
|
+
const baseName = details.clientDisplayName?.trim() || details.serverModelName?.trim() || details.name.trim() || baseId;
|
|
520
|
+
const suffix = laneId.slice(baseId.length).replace(/^-/, "").split("-").filter(Boolean).map((part) => part === "1m" ? "1M" : part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
521
|
+
return suffix ? `${baseName} ${suffix}` : baseName;
|
|
522
|
+
}
|
|
523
|
+
function buildRichCursorLane(details, entries, laneId, laneName, baseUrlOverride, references, defaultModelId, defaultMaxMode) {
|
|
524
|
+
const isThinkingToggle = (entry) => entry.effort === void 0 && entry.parameters.some((parameter) => parameter.id === "thinking" && parameter.value.toLowerCase() === "true");
|
|
525
|
+
const selectedByEffort = /* @__PURE__ */ new Map();
|
|
526
|
+
for (const entry of entries) {
|
|
527
|
+
const slot = entry.effort ?? (isThinkingToggle(entry) ? "thinking" : "fixed");
|
|
528
|
+
const current = selectedByEffort.get(slot);
|
|
529
|
+
if (current === void 0 || cursorVariantPreference(details, entry, defaultModelId, defaultMaxMode) > cursorVariantPreference(details, current, defaultModelId, defaultMaxMode)) selectedByEffort.set(slot, entry);
|
|
530
|
+
}
|
|
531
|
+
const selected = [...selectedByEffort.values()];
|
|
532
|
+
if (selected.length === 0) return [];
|
|
533
|
+
const routeKeys = /* @__PURE__ */ new Map();
|
|
534
|
+
const usedRouteKeys = /* @__PURE__ */ new Set();
|
|
535
|
+
const routes = {};
|
|
536
|
+
for (const entry of selected) {
|
|
537
|
+
let routeKey = entry.id;
|
|
538
|
+
if (usedRouteKeys.has(routeKey)) routeKey = entry.variant?.variantStringRepresentation?.trim() || `${entry.id}@${entry.parameters.map((parameter) => `${parameter.id}=${parameter.value}`).join(",")}`;
|
|
539
|
+
let duplicate = 2;
|
|
540
|
+
const root = routeKey;
|
|
541
|
+
while (usedRouteKeys.has(routeKey)) {
|
|
542
|
+
routeKey = `${root}#${duplicate}`;
|
|
543
|
+
duplicate += 1;
|
|
544
|
+
}
|
|
545
|
+
usedRouteKeys.add(routeKey);
|
|
546
|
+
routeKeys.set(entry, routeKey);
|
|
547
|
+
routes[routeKey] = {
|
|
548
|
+
modelId: details.name.trim(),
|
|
549
|
+
parameters: entry.parameters,
|
|
550
|
+
...entry.variant?.isMaxMode === void 0 ? void 0 : { maxMode: entry.variant.isMaxMode }
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
const members = [];
|
|
554
|
+
const routing = {};
|
|
555
|
+
let defaultMember;
|
|
556
|
+
let thinkingToggleMember;
|
|
557
|
+
for (const entry of selected) {
|
|
558
|
+
const routeKey = routeKeys.get(entry);
|
|
559
|
+
if (!routeKey) continue;
|
|
560
|
+
const variant = entry.variant;
|
|
561
|
+
const reference = references.get(entry.id) ?? references.get(details.name.trim()) ?? details.legacySlugs.map((id) => references.get(id)).find((candidate) => candidate !== void 0);
|
|
562
|
+
const isMaxMode = variant?.isMaxMode ?? false;
|
|
563
|
+
const contextParameter = entry.parameters.find((parameter) => parameter.id === "context")?.value;
|
|
564
|
+
const discoveredContextWindow = parseCursorContextWindow(contextParameter) ?? (isMaxMode ? details.contextTokenLimitForMaxMode : details.contextTokenLimit) ?? details.autoContextExtendedMaxTokens ?? details.autoContextMaxTokens;
|
|
565
|
+
const fallbackContext = reference?.contextWindow ?? DEFAULT_CONTEXT_WINDOW;
|
|
566
|
+
const input = details.supportsImages === void 0 ? resolveCursorInput(entry.id, reference?.input) : details.supportsImages ? ["text", "image"] : ["text"];
|
|
567
|
+
const isProviderDefault = isCursorProviderDefault(details, entry, defaultModelId, defaultMaxMode);
|
|
568
|
+
const reasoning = entry.effort === "off" ? false : entry.effort !== void 0 || isThinkingToggle(entry) ? true : details.supportsThinking ?? reference?.reasoning ?? false;
|
|
569
|
+
const referenceCost = reference && (reference.cost.input !== 0 || reference.cost.output !== 0) ? reference === references.get(entry.id) ? reference.cost : scaleTokenCost(reference.cost, cursorVariantCostMultiplier(details, entry)) : void 0;
|
|
570
|
+
const cost = resolveRichCursorCardCost(details, entry, laneId) ?? referenceCost ?? {
|
|
571
|
+
input: 0,
|
|
572
|
+
output: 0,
|
|
573
|
+
cacheRead: 0,
|
|
574
|
+
cacheWrite: 0
|
|
575
|
+
};
|
|
576
|
+
members.push({
|
|
577
|
+
...reference ?? {
|
|
578
|
+
id: routeKey,
|
|
579
|
+
name: laneName,
|
|
580
|
+
api: "cursor-agent",
|
|
581
|
+
provider: "cursor",
|
|
582
|
+
baseUrl: baseUrlOverride ?? "https://api2.cursor.sh",
|
|
583
|
+
reasoning,
|
|
584
|
+
input,
|
|
585
|
+
cost: {
|
|
586
|
+
input: 0,
|
|
587
|
+
output: 0,
|
|
588
|
+
cacheRead: 0,
|
|
589
|
+
cacheWrite: 0
|
|
590
|
+
},
|
|
591
|
+
contextWindow: fallbackContext,
|
|
592
|
+
maxTokens: DEFAULT_MAX_TOKENS
|
|
593
|
+
},
|
|
594
|
+
id: routeKey,
|
|
595
|
+
name: laneName,
|
|
596
|
+
baseUrl: baseUrlOverride ?? reference?.baseUrl ?? "https://api2.cursor.sh",
|
|
597
|
+
reasoning,
|
|
598
|
+
input,
|
|
599
|
+
cost,
|
|
600
|
+
supportsTools: details.supportsAgent ?? reference?.supportsTools,
|
|
601
|
+
contextWindow: discoveredContextWindow ?? fallbackContext,
|
|
602
|
+
cursorMaxMode: isMaxMode,
|
|
603
|
+
cursorModelParameters: entry.parameters,
|
|
604
|
+
cursorModelRoutes: routes,
|
|
605
|
+
cursorPrice: details.price,
|
|
606
|
+
cursorRequiresDataRetention: details.requiresDataRetention,
|
|
607
|
+
cursorSupportsAgent: details.supportsAgent,
|
|
608
|
+
cursorSupportsSandboxing: details.supportsSandboxing,
|
|
609
|
+
isRecommended: isProviderDefault || details.defaultOn,
|
|
610
|
+
description: variant?.tagline ?? details.tagline ?? cursorTooltipBlurb(variant?.tooltipData?.markdownContent) ?? cursorTooltipBlurb(details.tooltipData?.markdownContent) ?? reference?.description
|
|
611
|
+
});
|
|
612
|
+
if (entry.effort !== void 0) routing[entry.effort] = routeKey;
|
|
613
|
+
else if (isThinkingToggle(entry)) thinkingToggleMember = members.at(-1);
|
|
614
|
+
if (isProviderDefault) defaultMember = routeKey;
|
|
615
|
+
}
|
|
616
|
+
if (members.length === 0) return [];
|
|
617
|
+
const toggleEfforts = [];
|
|
618
|
+
if (thinkingToggleMember) {
|
|
619
|
+
const { compat: _compat, ...policySpec } = thinkingToggleMember;
|
|
620
|
+
const surfaceEfforts = thinkingToggleMember.thinking?.efforts.length ? thinkingToggleMember.thinking.efforts : resolveModelPolicy({
|
|
621
|
+
...policySpec,
|
|
622
|
+
reasoning: true,
|
|
623
|
+
thinking: void 0
|
|
624
|
+
}).thinking?.efforts ?? [];
|
|
625
|
+
for (const effort of surfaceEfforts) {
|
|
626
|
+
if (routing[effort] !== void 0) continue;
|
|
627
|
+
routing[effort] = thinkingToggleMember.id;
|
|
628
|
+
toggleEfforts.push(effort);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
defaultMember ??= selected.filter((entry) => entry.variant?.isDefaultNonMaxConfig === true || entry.variant?.isDefaultMaxConfig === true).map((entry) => routeKeys.get(entry)).find((routeKey) => routeKey !== void 0);
|
|
632
|
+
defaultMember ??= routing.off ?? members[0]?.id;
|
|
633
|
+
const efforts = THINKING_EFFORTS.filter((effort) => routing[effort] !== void 0);
|
|
634
|
+
const family = {
|
|
635
|
+
id: laneId,
|
|
636
|
+
name: laneName,
|
|
637
|
+
members: members.map((member) => member.id),
|
|
638
|
+
routing,
|
|
639
|
+
...defaultMember === void 0 ? void 0 : { defaultMember },
|
|
640
|
+
...efforts.length === 0 ? void 0 : { thinking: {
|
|
641
|
+
mode: "effort",
|
|
642
|
+
efforts,
|
|
643
|
+
...routing.off === void 0 ? { requiresEffort: true } : {},
|
|
644
|
+
...defaultMember === void 0 || toggleEfforts.length > 0 ? void 0 : { defaultLevel: efforts.find((effort) => routing[effort] === defaultMember) }
|
|
645
|
+
} }
|
|
646
|
+
};
|
|
647
|
+
return collapseVariants(members, { table: { families: [family] } });
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* The one-line model blurb from Cursor's IDE tooltip. The tooltip is HTML for
|
|
651
|
+
* Cursor's editor (`<br />` separators, a bold title, context/effort lines,
|
|
652
|
+
* styled warning spans); the model's prose sentence is the second segment.
|
|
653
|
+
* Context and effort are already separate model fields, so the rest is dropped.
|
|
654
|
+
*/
|
|
655
|
+
function cursorTooltipBlurb(markdown) {
|
|
656
|
+
if (!markdown) return void 0;
|
|
657
|
+
const segments = markdown.split(/<br\s*\/?>/i).map((segment) => segment.replace(/<[^>]+>/g, "").replace(/[*_`]/g, "").trim()).filter((segment) => segment.length > 0);
|
|
658
|
+
return segments[1] ?? segments[0];
|
|
659
|
+
}
|
|
660
|
+
function cursorVariantPreference(details, entry, defaultModelId, defaultMaxMode) {
|
|
661
|
+
if (isCursorProviderDefault(details, entry, defaultModelId, defaultMaxMode)) return 3;
|
|
662
|
+
if (entry.variant?.isDefaultNonMaxConfig === true || entry.variant?.isDefaultMaxConfig === true) return 2;
|
|
663
|
+
return 1;
|
|
664
|
+
}
|
|
665
|
+
function isCursorProviderDefault(details, entry, defaultModelId, defaultMaxMode) {
|
|
666
|
+
const variant = entry.variant;
|
|
667
|
+
if (defaultModelId !== void 0) {
|
|
668
|
+
if (defaultModelId === entry.id) return defaultMaxMode === void 0 || defaultMaxMode === (variant?.isMaxMode ?? false);
|
|
669
|
+
if (defaultModelId !== details.name && !details.legacySlugs.includes(defaultModelId) && !details.idAliases.includes(defaultModelId)) return false;
|
|
670
|
+
if (variant === void 0) return true;
|
|
671
|
+
return defaultMaxMode === true ? variant.isDefaultMaxConfig === true : variant.isDefaultNonMaxConfig === true;
|
|
672
|
+
}
|
|
673
|
+
if (!details.defaultOn) return false;
|
|
674
|
+
return variant === void 0 || variant.isDefaultNonMaxConfig === true || variant.isDefaultMaxConfig === true;
|
|
675
|
+
}
|
|
676
|
+
function parseCursorContextWindow(value) {
|
|
677
|
+
if (!value) return void 0;
|
|
678
|
+
const match = /^(\d+(?:\.\d+)?)([km])?$/i.exec(value.trim());
|
|
679
|
+
if (!match?.[1]) return void 0;
|
|
680
|
+
const amount = Number(match[1]);
|
|
681
|
+
if (!Number.isFinite(amount) || amount <= 0) return void 0;
|
|
682
|
+
const unit = match[2]?.toLowerCase();
|
|
683
|
+
return Math.round(amount * (unit === "m" ? 1e6 : unit === "k" ? 1e3 : 1));
|
|
684
|
+
}
|
|
685
|
+
function isRichVariantBlocked(details, variant) {
|
|
686
|
+
for (const parameter of variant.parameterValues) {
|
|
687
|
+
const definition = details.parameterDefinitions.find((candidate) => candidate.id === parameter.id);
|
|
688
|
+
if ([...definition?.parameterType?.booleanParameter?.values ?? [], ...definition?.parameterType?.enumParameter?.values ?? []].find((candidate) => candidate.value === parameter.value)?.blockedByAdminAllowlist === true) return true;
|
|
689
|
+
}
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
function normalizeCursorModels(models, baseUrlOverride, references) {
|
|
693
|
+
if (!models || models.length === 0) return [];
|
|
694
|
+
const byId = /* @__PURE__ */ new Map();
|
|
695
|
+
for (const model of models) {
|
|
696
|
+
const normalized = normalizeCursorModel(model, baseUrlOverride, references);
|
|
697
|
+
if (!normalized) continue;
|
|
698
|
+
byId.set(normalized.id, normalized);
|
|
699
|
+
}
|
|
700
|
+
return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
701
|
+
}
|
|
702
|
+
function normalizeCursorModel(model, baseUrlOverride, references) {
|
|
703
|
+
const parsedModel = CursorModelDetailsSchema(model);
|
|
704
|
+
if (parsedModel instanceof type.errors) return null;
|
|
705
|
+
const details = parsedModel;
|
|
706
|
+
const id = details.modelId.trim();
|
|
707
|
+
if (!id) return null;
|
|
708
|
+
const name = pickModelDisplayName(details, id);
|
|
709
|
+
const reference = references.get(id);
|
|
710
|
+
const reasoning = isCursorKimiK3(id) || isCursorVersionedGrok(id) || Boolean(details.thinkingDetails) || reference?.reasoning === true;
|
|
711
|
+
if (reference) return {
|
|
712
|
+
...reference,
|
|
713
|
+
id,
|
|
714
|
+
name,
|
|
715
|
+
baseUrl: baseUrlOverride ?? reference.baseUrl,
|
|
716
|
+
reasoning,
|
|
717
|
+
input: resolveCursorInput(id, reference.input),
|
|
718
|
+
contextWindow: resolveCursorContextWindow(details, id, reference.contextWindow),
|
|
719
|
+
cursorMaxMode: details.maxMode
|
|
720
|
+
};
|
|
721
|
+
return {
|
|
722
|
+
id,
|
|
723
|
+
name,
|
|
724
|
+
api: "cursor-agent",
|
|
725
|
+
provider: "cursor",
|
|
726
|
+
baseUrl: baseUrlOverride ?? "https://api2.cursor.sh",
|
|
727
|
+
reasoning,
|
|
728
|
+
input: resolveCursorInput(id),
|
|
729
|
+
cost: {
|
|
730
|
+
input: 0,
|
|
731
|
+
output: 0,
|
|
732
|
+
cacheRead: 0,
|
|
733
|
+
cacheWrite: 0
|
|
734
|
+
},
|
|
735
|
+
contextWindow: resolveCursorContextWindow(details, id, DEFAULT_CONTEXT_WINDOW),
|
|
736
|
+
maxTokens: DEFAULT_MAX_TOKENS,
|
|
737
|
+
cursorMaxMode: details.maxMode
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Context window for a discovered Cursor model: the 1M ceiling when any 1M
|
|
742
|
+
* signal fires (never below a larger bundled reference), else the fallback.
|
|
743
|
+
*/
|
|
744
|
+
function resolveCursorContextWindow(model, id, fallback) {
|
|
745
|
+
const labeled1M = CURSOR_1M_NAME_PATTERN.test(id) || [
|
|
746
|
+
model.displayName,
|
|
747
|
+
model.displayNameShort,
|
|
748
|
+
model.displayModelId,
|
|
749
|
+
...model.aliases
|
|
750
|
+
].some((candidate) => typeof candidate === "string" && CURSOR_1M_NAME_PATTERN.test(candidate));
|
|
751
|
+
const identity = classifyModel("cursor", id, { lenient: true });
|
|
752
|
+
const maxMode1M = model.maxMode && (identity.class === "anthropic" || identity.class === "gemini");
|
|
753
|
+
if (labeled1M || isCursorNative1MModelId(id) || maxMode1M) return Math.max(fallback ?? 0, CURSOR_1M_CONTEXT_WINDOW);
|
|
754
|
+
return fallback;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Natively 1M-context families Cursor serves without a "1M" label: GLM 5.2+
|
|
758
|
+
* base/air/turbo coding SKUs (structured family and revision gates exclude
|
|
759
|
+
* vision and sub-1M variants). K3 — including Cursor's bare `k3` alias — is
|
|
760
|
+
* rule-owned via `context-window-floor` in `providers/cursor.kdl`.
|
|
761
|
+
*/
|
|
762
|
+
function isCursorNative1MModelId(id) {
|
|
763
|
+
return isCursorGlm52CodingModel(id);
|
|
764
|
+
}
|
|
765
|
+
function pickModelDisplayName(model, fallbackId) {
|
|
766
|
+
const candidates = [
|
|
767
|
+
model.displayName,
|
|
768
|
+
model.displayNameShort,
|
|
769
|
+
model.displayModelId,
|
|
770
|
+
...model.aliases,
|
|
771
|
+
fallbackId
|
|
772
|
+
];
|
|
773
|
+
for (const candidate of candidates) {
|
|
774
|
+
if (typeof candidate !== "string") continue;
|
|
775
|
+
const trimmed = candidate.trim();
|
|
776
|
+
if (trimmed) return trimmed;
|
|
777
|
+
}
|
|
778
|
+
return fallbackId;
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Resolves input modalities from a bundled reference when available. The
|
|
782
|
+
* Cursor-verified families (K3, grok-4, composer-2.5) are rule-owned via
|
|
783
|
+
* `input-modalities` in `providers/cursor.kdl` and corrected at build time.
|
|
784
|
+
* Without a reference, families whose native catalogs are multimodal
|
|
785
|
+
* (anthropic, gemini, openai) fall back to id classification.
|
|
786
|
+
*/
|
|
787
|
+
function resolveCursorInput(id, referenceInput) {
|
|
788
|
+
if (referenceInput) return referenceInput;
|
|
789
|
+
const identity = classifyModel("cursor", id, { lenient: true });
|
|
790
|
+
if (identity.class === "anthropic" || identity.class === "gemini" || identity.class === "openai") return ["text", "image"];
|
|
791
|
+
return ["text"];
|
|
792
|
+
}
|
|
793
|
+
//#endregion
|
|
794
|
+
export { fetchCursorUsableModels, resolveCursorInput };
|