cursor-opencode-provider 0.3.1 → 0.3.2
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 +3 -2
- package/dist/model-config.js +5 -3
- package/dist/opencode2/catalog.js +2 -1
- package/dist/opencode2/types.d.ts +13 -1
- package/dist/pricing-data.d.ts +194 -0
- package/dist/pricing-data.js +194 -0
- package/dist/pricing.d.ts +62 -0
- package/dist/pricing.js +127 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Add the package to OpenCode config. OpenCode installs npm plugins with Bun at st
|
|
|
47
47
|
}
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Pin a version if you want: `"cursor-opencode-provider@0.3.
|
|
50
|
+
Pin a version if you want: `"cursor-opencode-provider@0.3.2"`.
|
|
51
51
|
|
|
52
52
|
You can also install it yourself first:
|
|
53
53
|
|
|
@@ -117,7 +117,7 @@ browser login or an API key. `CURSOR_API_KEY` is also picked up automatically.
|
|
|
117
117
|
}
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
Pin a version if you want: `"cursor-opencode-provider@0.3.
|
|
120
|
+
Pin a version if you want: `"cursor-opencode-provider@0.3.2/plugin/opencode2"`.
|
|
121
121
|
|
|
122
122
|
OpenCode 2.0 installs the published package into its host cache and loads the AI SDK
|
|
123
123
|
entry from there (`aisdk:cursor-opencode-provider`). No extra env vars are required.
|
|
@@ -337,6 +337,7 @@ OpenCode
|
|
|
337
337
|
| `src/opencode2/` | 2.0-only catalog mapping, integration/auth, and local API types |
|
|
338
338
|
| `src/plugin-core.ts` | Host-neutral SDK factory, package matching, API base/telemetry resolution |
|
|
339
339
|
| `src/model-config.ts` | Cursor model → OpenCode model mapping shared by every plugin surface |
|
|
340
|
+
| `src/pricing.ts` / `src/pricing-data.ts` | Cursor docs token rates → classic `cost` and OpenCode 2.0 cost tiers |
|
|
340
341
|
| `src/index.ts` | `createCursor` factory; default export is `CursorPlugin` |
|
|
341
342
|
| `src/language-model.ts` | AI SDK `LanguageModelV3` adapter (`doStream`, `doGenerate`) |
|
|
342
343
|
| `src/session.ts` | Held-open agent Run session and pending exec correlation |
|
package/dist/model-config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CURSOR_VARIANT_PARAMETERS_KEY, CURSOR_WIRE_MODEL_ID_KEY, parseCursorContextLimit, } from "./models.js";
|
|
2
|
+
import { applyCursorModelCost } from "./pricing.js";
|
|
2
3
|
/**
|
|
3
4
|
* Cursor `ModelInfo` → OpenCode model-config mapping.
|
|
4
5
|
*
|
|
@@ -187,12 +188,13 @@ export function modelsToConfig(models) {
|
|
|
187
188
|
const baseVariants = variantsForTier(m, "base");
|
|
188
189
|
const longVariants = variantsForTier(m, "long");
|
|
189
190
|
if (baseVariants.length > 0 || longVariants.length === 0) {
|
|
190
|
-
out[m.id] = modelInfoToConfig(m, { thinkingSuffix, contextTier: "base" });
|
|
191
|
+
out[m.id] = applyCursorModelCost(m.id, modelInfoToConfig(m, { thinkingSuffix, contextTier: "base" }));
|
|
191
192
|
}
|
|
192
193
|
if (longVariants.length === 0)
|
|
193
194
|
continue;
|
|
194
195
|
if (baseVariants.length === 0) {
|
|
195
|
-
|
|
196
|
+
// No separate base tier — the wire id itself is the long-context entry.
|
|
197
|
+
out[m.id] = applyCursorModelCost(`${m.id}-1m`, modelInfoToConfig(m, { thinkingSuffix, contextTier: "long" }));
|
|
196
198
|
continue;
|
|
197
199
|
}
|
|
198
200
|
let longId = `${m.id}-1m`;
|
|
@@ -200,7 +202,7 @@ export function modelsToConfig(models) {
|
|
|
200
202
|
while (usedIds.has(longId))
|
|
201
203
|
longId = `${m.id}-1m-${suffix++}`;
|
|
202
204
|
usedIds.add(longId);
|
|
203
|
-
out[longId] = modelInfoToConfig(m, { thinkingSuffix, contextTier: "long" });
|
|
205
|
+
out[longId] = applyCursorModelCost(longId, modelInfoToConfig(m, { thinkingSuffix, contextTier: "long" }));
|
|
204
206
|
}
|
|
205
207
|
return out;
|
|
206
208
|
}
|
|
@@ -2,6 +2,7 @@ import { pathToFileURL } from "node:url";
|
|
|
2
2
|
import { CURSOR_PROVIDER_ID } from "../shared.js";
|
|
3
3
|
import { CURSOR_WIRE_MODEL_ID_KEY } from "../models.js";
|
|
4
4
|
import { modelsToConfig } from "../model-config.js";
|
|
5
|
+
import { toOpenCode2Costs } from "../pricing.js";
|
|
5
6
|
/**
|
|
6
7
|
* Catalog registration for the OpenCode 2.0 plugin — the replacement for the
|
|
7
8
|
* classic plugin's `config` hook.
|
|
@@ -69,7 +70,7 @@ function applyModelEntry(draft, id, entry) {
|
|
|
69
70
|
model.status = "active";
|
|
70
71
|
model.enabled = true;
|
|
71
72
|
model.time = { released: 0 };
|
|
72
|
-
model.cost =
|
|
73
|
+
model.cost = toOpenCode2Costs(entry.cost);
|
|
73
74
|
// Carries the default variant parameters (and the wire id) through to
|
|
74
75
|
// doStream as provider options, matching the classic plugin.
|
|
75
76
|
if (options)
|
|
@@ -53,7 +53,19 @@ export type ModelInfo2 = {
|
|
|
53
53
|
time: {
|
|
54
54
|
released: number;
|
|
55
55
|
};
|
|
56
|
-
|
|
56
|
+
/** OpenCode 2.0 tiered $/1M rates; empty when Cursor does not publish a price. */
|
|
57
|
+
cost: readonly {
|
|
58
|
+
tier?: {
|
|
59
|
+
type: "context";
|
|
60
|
+
size: number;
|
|
61
|
+
};
|
|
62
|
+
input: number;
|
|
63
|
+
output: number;
|
|
64
|
+
cache: {
|
|
65
|
+
read: number;
|
|
66
|
+
write: number;
|
|
67
|
+
};
|
|
68
|
+
}[];
|
|
57
69
|
status: "alpha" | "beta" | "deprecated" | "active";
|
|
58
70
|
enabled: boolean;
|
|
59
71
|
limit: {
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUTO-GENERATED by scripts/generate-cursor-pricing.ts — do not edit by hand.
|
|
3
|
+
* Source: https://cursor.com/docs/models-and-pricing.md
|
|
4
|
+
* Regenerate: bun run generate:pricing
|
|
5
|
+
*/
|
|
6
|
+
export declare const CURSOR_PRICING_SOURCE: "https://cursor.com/docs/models-and-pricing.md";
|
|
7
|
+
export declare const CURSOR_MODEL_COSTS: {
|
|
8
|
+
readonly "claude-fable-5": {
|
|
9
|
+
readonly input: 10;
|
|
10
|
+
readonly output: 50;
|
|
11
|
+
readonly cache_read: 1;
|
|
12
|
+
readonly cache_write: 12.5;
|
|
13
|
+
};
|
|
14
|
+
readonly "claude-haiku-4-5": {
|
|
15
|
+
readonly input: 1;
|
|
16
|
+
readonly output: 5;
|
|
17
|
+
readonly cache_read: 0.1;
|
|
18
|
+
readonly cache_write: 1.25;
|
|
19
|
+
};
|
|
20
|
+
readonly "claude-opus-4-5": {
|
|
21
|
+
readonly input: 5;
|
|
22
|
+
readonly output: 25;
|
|
23
|
+
readonly cache_read: 0.5;
|
|
24
|
+
readonly cache_write: 6.25;
|
|
25
|
+
};
|
|
26
|
+
readonly "claude-opus-4-6": {
|
|
27
|
+
readonly input: 5;
|
|
28
|
+
readonly output: 25;
|
|
29
|
+
readonly cache_read: 0.5;
|
|
30
|
+
readonly cache_write: 6.25;
|
|
31
|
+
};
|
|
32
|
+
readonly "claude-opus-4-7": {
|
|
33
|
+
readonly input: 5;
|
|
34
|
+
readonly output: 25;
|
|
35
|
+
readonly cache_read: 0.5;
|
|
36
|
+
readonly cache_write: 6.25;
|
|
37
|
+
};
|
|
38
|
+
readonly "claude-opus-4-8": {
|
|
39
|
+
readonly input: 5;
|
|
40
|
+
readonly output: 25;
|
|
41
|
+
readonly cache_read: 0.5;
|
|
42
|
+
readonly cache_write: 6.25;
|
|
43
|
+
};
|
|
44
|
+
readonly "claude-opus-5": {
|
|
45
|
+
readonly input: 5;
|
|
46
|
+
readonly output: 25;
|
|
47
|
+
readonly cache_read: 0.5;
|
|
48
|
+
readonly cache_write: 6.25;
|
|
49
|
+
};
|
|
50
|
+
readonly "claude-sonnet-4": {
|
|
51
|
+
readonly input: 3;
|
|
52
|
+
readonly output: 15;
|
|
53
|
+
readonly cache_read: 0.3;
|
|
54
|
+
readonly cache_write: 3.75;
|
|
55
|
+
readonly context_over_200k: {
|
|
56
|
+
readonly input: 6;
|
|
57
|
+
readonly output: 22.5;
|
|
58
|
+
readonly cache_read: 0.6;
|
|
59
|
+
readonly cache_write: 7.5;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
readonly "claude-sonnet-4-5": {
|
|
63
|
+
readonly input: 3;
|
|
64
|
+
readonly output: 15;
|
|
65
|
+
readonly cache_read: 0.3;
|
|
66
|
+
readonly cache_write: 3.75;
|
|
67
|
+
};
|
|
68
|
+
readonly "claude-sonnet-4-6": {
|
|
69
|
+
readonly input: 3;
|
|
70
|
+
readonly output: 15;
|
|
71
|
+
readonly cache_read: 0.3;
|
|
72
|
+
readonly cache_write: 3.75;
|
|
73
|
+
};
|
|
74
|
+
readonly "claude-sonnet-5": {
|
|
75
|
+
readonly input: 3;
|
|
76
|
+
readonly output: 15;
|
|
77
|
+
readonly cache_read: 0.3;
|
|
78
|
+
readonly cache_write: 3.75;
|
|
79
|
+
};
|
|
80
|
+
readonly "gemini-2.5-flash": {
|
|
81
|
+
readonly input: 0.3;
|
|
82
|
+
readonly output: 2.5;
|
|
83
|
+
readonly cache_read: 0.03;
|
|
84
|
+
};
|
|
85
|
+
readonly "gemini-3-flash": {
|
|
86
|
+
readonly input: 0.5;
|
|
87
|
+
readonly output: 3;
|
|
88
|
+
readonly cache_read: 0.05;
|
|
89
|
+
};
|
|
90
|
+
readonly "gemini-3.1-pro": {
|
|
91
|
+
readonly input: 2;
|
|
92
|
+
readonly output: 12;
|
|
93
|
+
readonly cache_read: 0.2;
|
|
94
|
+
};
|
|
95
|
+
readonly "gemini-3.5-flash": {
|
|
96
|
+
readonly input: 1.5;
|
|
97
|
+
readonly output: 9;
|
|
98
|
+
readonly cache_read: 0.15;
|
|
99
|
+
};
|
|
100
|
+
readonly "gemini-3.6-flash": {
|
|
101
|
+
readonly input: 1.5;
|
|
102
|
+
readonly output: 7.5;
|
|
103
|
+
readonly cache_read: 0.15;
|
|
104
|
+
};
|
|
105
|
+
readonly "glm-5.2": {
|
|
106
|
+
readonly input: 1.4;
|
|
107
|
+
readonly output: 4.4;
|
|
108
|
+
readonly cache_read: 0.26;
|
|
109
|
+
};
|
|
110
|
+
readonly "gpt-5-mini": {
|
|
111
|
+
readonly input: 0.25;
|
|
112
|
+
readonly output: 2;
|
|
113
|
+
readonly cache_read: 0.025;
|
|
114
|
+
};
|
|
115
|
+
readonly "gpt-5.1": {
|
|
116
|
+
readonly input: 1.25;
|
|
117
|
+
readonly output: 10;
|
|
118
|
+
readonly cache_read: 0.125;
|
|
119
|
+
};
|
|
120
|
+
readonly "gpt-5.2": {
|
|
121
|
+
readonly input: 1.75;
|
|
122
|
+
readonly output: 14;
|
|
123
|
+
readonly cache_read: 0.175;
|
|
124
|
+
};
|
|
125
|
+
readonly "gpt-5.3-codex": {
|
|
126
|
+
readonly input: 1.75;
|
|
127
|
+
readonly output: 14;
|
|
128
|
+
readonly cache_read: 0.175;
|
|
129
|
+
};
|
|
130
|
+
readonly "gpt-5.4": {
|
|
131
|
+
readonly input: 2.5;
|
|
132
|
+
readonly output: 15;
|
|
133
|
+
readonly cache_read: 0.25;
|
|
134
|
+
readonly context_over_200k: {
|
|
135
|
+
readonly input: 5;
|
|
136
|
+
readonly output: 15;
|
|
137
|
+
readonly cache_read: 0.5;
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
readonly "gpt-5.4-mini": {
|
|
141
|
+
readonly input: 0.75;
|
|
142
|
+
readonly output: 4.5;
|
|
143
|
+
readonly cache_read: 0.075;
|
|
144
|
+
};
|
|
145
|
+
readonly "gpt-5.4-nano": {
|
|
146
|
+
readonly input: 0.2;
|
|
147
|
+
readonly output: 1.25;
|
|
148
|
+
readonly cache_read: 0.02;
|
|
149
|
+
};
|
|
150
|
+
readonly "gpt-5.5": {
|
|
151
|
+
readonly input: 5;
|
|
152
|
+
readonly output: 30;
|
|
153
|
+
readonly cache_read: 0.5;
|
|
154
|
+
readonly context_over_200k: {
|
|
155
|
+
readonly input: 10;
|
|
156
|
+
readonly output: 30;
|
|
157
|
+
readonly cache_read: 1;
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
readonly "gpt-5.6-luna": {
|
|
161
|
+
readonly input: 0.2;
|
|
162
|
+
readonly output: 1.2;
|
|
163
|
+
readonly cache_read: 0.02;
|
|
164
|
+
readonly cache_write: 0.25;
|
|
165
|
+
};
|
|
166
|
+
readonly "gpt-5.6-sol": {
|
|
167
|
+
readonly input: 5;
|
|
168
|
+
readonly output: 30;
|
|
169
|
+
readonly cache_read: 0.5;
|
|
170
|
+
readonly cache_write: 6.25;
|
|
171
|
+
readonly context_over_200k: {
|
|
172
|
+
readonly input: 10;
|
|
173
|
+
readonly output: 30;
|
|
174
|
+
readonly cache_read: 1;
|
|
175
|
+
readonly cache_write: 12.5;
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
readonly "gpt-5.6-terra": {
|
|
179
|
+
readonly input: 2;
|
|
180
|
+
readonly output: 12;
|
|
181
|
+
readonly cache_read: 0.2;
|
|
182
|
+
readonly cache_write: 2.5;
|
|
183
|
+
};
|
|
184
|
+
readonly "kimi-k2.7-code": {
|
|
185
|
+
readonly input: 0.95;
|
|
186
|
+
readonly output: 4;
|
|
187
|
+
readonly cache_read: 0.19;
|
|
188
|
+
};
|
|
189
|
+
readonly "kimi-k3": {
|
|
190
|
+
readonly input: 3;
|
|
191
|
+
readonly output: 15;
|
|
192
|
+
readonly cache_read: 0.3;
|
|
193
|
+
};
|
|
194
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUTO-GENERATED by scripts/generate-cursor-pricing.ts — do not edit by hand.
|
|
3
|
+
* Source: https://cursor.com/docs/models-and-pricing.md
|
|
4
|
+
* Regenerate: bun run generate:pricing
|
|
5
|
+
*/
|
|
6
|
+
export const CURSOR_PRICING_SOURCE = "https://cursor.com/docs/models-and-pricing.md";
|
|
7
|
+
export const CURSOR_MODEL_COSTS = {
|
|
8
|
+
"claude-fable-5": {
|
|
9
|
+
"input": 10,
|
|
10
|
+
"output": 50,
|
|
11
|
+
"cache_read": 1,
|
|
12
|
+
"cache_write": 12.5
|
|
13
|
+
},
|
|
14
|
+
"claude-haiku-4-5": {
|
|
15
|
+
"input": 1,
|
|
16
|
+
"output": 5,
|
|
17
|
+
"cache_read": 0.1,
|
|
18
|
+
"cache_write": 1.25
|
|
19
|
+
},
|
|
20
|
+
"claude-opus-4-5": {
|
|
21
|
+
"input": 5,
|
|
22
|
+
"output": 25,
|
|
23
|
+
"cache_read": 0.5,
|
|
24
|
+
"cache_write": 6.25
|
|
25
|
+
},
|
|
26
|
+
"claude-opus-4-6": {
|
|
27
|
+
"input": 5,
|
|
28
|
+
"output": 25,
|
|
29
|
+
"cache_read": 0.5,
|
|
30
|
+
"cache_write": 6.25
|
|
31
|
+
},
|
|
32
|
+
"claude-opus-4-7": {
|
|
33
|
+
"input": 5,
|
|
34
|
+
"output": 25,
|
|
35
|
+
"cache_read": 0.5,
|
|
36
|
+
"cache_write": 6.25
|
|
37
|
+
},
|
|
38
|
+
"claude-opus-4-8": {
|
|
39
|
+
"input": 5,
|
|
40
|
+
"output": 25,
|
|
41
|
+
"cache_read": 0.5,
|
|
42
|
+
"cache_write": 6.25
|
|
43
|
+
},
|
|
44
|
+
"claude-opus-5": {
|
|
45
|
+
"input": 5,
|
|
46
|
+
"output": 25,
|
|
47
|
+
"cache_read": 0.5,
|
|
48
|
+
"cache_write": 6.25
|
|
49
|
+
},
|
|
50
|
+
"claude-sonnet-4": {
|
|
51
|
+
"input": 3,
|
|
52
|
+
"output": 15,
|
|
53
|
+
"cache_read": 0.3,
|
|
54
|
+
"cache_write": 3.75,
|
|
55
|
+
"context_over_200k": {
|
|
56
|
+
"input": 6,
|
|
57
|
+
"output": 22.5,
|
|
58
|
+
"cache_read": 0.6,
|
|
59
|
+
"cache_write": 7.5
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"claude-sonnet-4-5": {
|
|
63
|
+
"input": 3,
|
|
64
|
+
"output": 15,
|
|
65
|
+
"cache_read": 0.3,
|
|
66
|
+
"cache_write": 3.75
|
|
67
|
+
},
|
|
68
|
+
"claude-sonnet-4-6": {
|
|
69
|
+
"input": 3,
|
|
70
|
+
"output": 15,
|
|
71
|
+
"cache_read": 0.3,
|
|
72
|
+
"cache_write": 3.75
|
|
73
|
+
},
|
|
74
|
+
"claude-sonnet-5": {
|
|
75
|
+
"input": 3,
|
|
76
|
+
"output": 15,
|
|
77
|
+
"cache_read": 0.3,
|
|
78
|
+
"cache_write": 3.75
|
|
79
|
+
},
|
|
80
|
+
"gemini-2.5-flash": {
|
|
81
|
+
"input": 0.3,
|
|
82
|
+
"output": 2.5,
|
|
83
|
+
"cache_read": 0.03
|
|
84
|
+
},
|
|
85
|
+
"gemini-3-flash": {
|
|
86
|
+
"input": 0.5,
|
|
87
|
+
"output": 3,
|
|
88
|
+
"cache_read": 0.05
|
|
89
|
+
},
|
|
90
|
+
"gemini-3.1-pro": {
|
|
91
|
+
"input": 2,
|
|
92
|
+
"output": 12,
|
|
93
|
+
"cache_read": 0.2
|
|
94
|
+
},
|
|
95
|
+
"gemini-3.5-flash": {
|
|
96
|
+
"input": 1.5,
|
|
97
|
+
"output": 9,
|
|
98
|
+
"cache_read": 0.15
|
|
99
|
+
},
|
|
100
|
+
"gemini-3.6-flash": {
|
|
101
|
+
"input": 1.5,
|
|
102
|
+
"output": 7.5,
|
|
103
|
+
"cache_read": 0.15
|
|
104
|
+
},
|
|
105
|
+
"glm-5.2": {
|
|
106
|
+
"input": 1.4,
|
|
107
|
+
"output": 4.4,
|
|
108
|
+
"cache_read": 0.26
|
|
109
|
+
},
|
|
110
|
+
"gpt-5-mini": {
|
|
111
|
+
"input": 0.25,
|
|
112
|
+
"output": 2,
|
|
113
|
+
"cache_read": 0.025
|
|
114
|
+
},
|
|
115
|
+
"gpt-5.1": {
|
|
116
|
+
"input": 1.25,
|
|
117
|
+
"output": 10,
|
|
118
|
+
"cache_read": 0.125
|
|
119
|
+
},
|
|
120
|
+
"gpt-5.2": {
|
|
121
|
+
"input": 1.75,
|
|
122
|
+
"output": 14,
|
|
123
|
+
"cache_read": 0.175
|
|
124
|
+
},
|
|
125
|
+
"gpt-5.3-codex": {
|
|
126
|
+
"input": 1.75,
|
|
127
|
+
"output": 14,
|
|
128
|
+
"cache_read": 0.175
|
|
129
|
+
},
|
|
130
|
+
"gpt-5.4": {
|
|
131
|
+
"input": 2.5,
|
|
132
|
+
"output": 15,
|
|
133
|
+
"cache_read": 0.25,
|
|
134
|
+
"context_over_200k": {
|
|
135
|
+
"input": 5,
|
|
136
|
+
"output": 15,
|
|
137
|
+
"cache_read": 0.5
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"gpt-5.4-mini": {
|
|
141
|
+
"input": 0.75,
|
|
142
|
+
"output": 4.5,
|
|
143
|
+
"cache_read": 0.075
|
|
144
|
+
},
|
|
145
|
+
"gpt-5.4-nano": {
|
|
146
|
+
"input": 0.2,
|
|
147
|
+
"output": 1.25,
|
|
148
|
+
"cache_read": 0.02
|
|
149
|
+
},
|
|
150
|
+
"gpt-5.5": {
|
|
151
|
+
"input": 5,
|
|
152
|
+
"output": 30,
|
|
153
|
+
"cache_read": 0.5,
|
|
154
|
+
"context_over_200k": {
|
|
155
|
+
"input": 10,
|
|
156
|
+
"output": 30,
|
|
157
|
+
"cache_read": 1
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"gpt-5.6-luna": {
|
|
161
|
+
"input": 0.2,
|
|
162
|
+
"output": 1.2,
|
|
163
|
+
"cache_read": 0.02,
|
|
164
|
+
"cache_write": 0.25
|
|
165
|
+
},
|
|
166
|
+
"gpt-5.6-sol": {
|
|
167
|
+
"input": 5,
|
|
168
|
+
"output": 30,
|
|
169
|
+
"cache_read": 0.5,
|
|
170
|
+
"cache_write": 6.25,
|
|
171
|
+
"context_over_200k": {
|
|
172
|
+
"input": 10,
|
|
173
|
+
"output": 30,
|
|
174
|
+
"cache_read": 1,
|
|
175
|
+
"cache_write": 12.5
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"gpt-5.6-terra": {
|
|
179
|
+
"input": 2,
|
|
180
|
+
"output": 12,
|
|
181
|
+
"cache_read": 0.2,
|
|
182
|
+
"cache_write": 2.5
|
|
183
|
+
},
|
|
184
|
+
"kimi-k2.7-code": {
|
|
185
|
+
"input": 0.95,
|
|
186
|
+
"output": 4,
|
|
187
|
+
"cache_read": 0.19
|
|
188
|
+
},
|
|
189
|
+
"kimi-k3": {
|
|
190
|
+
"input": 3,
|
|
191
|
+
"output": 15,
|
|
192
|
+
"cache_read": 0.3
|
|
193
|
+
}
|
|
194
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor model token pricing for OpenCode cost reporting.
|
|
3
|
+
*
|
|
4
|
+
* Rates come from Cursor's public docs (Other Models pool = provider API price).
|
|
5
|
+
* Generated data lives in `pricing-data.ts`; regenerate with
|
|
6
|
+
* `bun run generate:pricing`. Unknown / unpublished models get no `cost`.
|
|
7
|
+
*/
|
|
8
|
+
/** Classic OpenCode / models.dev cost shape used in provider config. */
|
|
9
|
+
export type OpenCodeModelCost = {
|
|
10
|
+
input: number;
|
|
11
|
+
output: number;
|
|
12
|
+
cache_read?: number;
|
|
13
|
+
cache_write?: number;
|
|
14
|
+
context_over_200k?: OpenCodeModelCost;
|
|
15
|
+
};
|
|
16
|
+
/** OpenCode 2.0 `Model.Info.cost` entry (array of tiered rates). */
|
|
17
|
+
export type OpenCode2ModelCost = {
|
|
18
|
+
tier?: {
|
|
19
|
+
type: "context";
|
|
20
|
+
size: number;
|
|
21
|
+
};
|
|
22
|
+
input: number;
|
|
23
|
+
output: number;
|
|
24
|
+
cache: {
|
|
25
|
+
read: number;
|
|
26
|
+
write: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export type CursorPricingCoverage = {
|
|
30
|
+
priced: string[];
|
|
31
|
+
missing: string[];
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Models we expose that Cursor does not currently publish numeric rates for
|
|
35
|
+
* on the Other Models table (Auto / first-party Cursor Models pool).
|
|
36
|
+
*/
|
|
37
|
+
export declare const CURSOR_UNPRICED_MODEL_IDS: readonly ["default", "composer-2.5", "grok-4.5"];
|
|
38
|
+
export declare function validateOpenCodeModelCost(value: unknown, path?: string): {
|
|
39
|
+
valid: boolean;
|
|
40
|
+
errors: string[];
|
|
41
|
+
};
|
|
42
|
+
export declare function isOpenCodeModelCost(value: unknown): value is OpenCodeModelCost;
|
|
43
|
+
/** Strip synthetic OpenCode long-context suffix (`…-1m`, `…-1m-2`, …). */
|
|
44
|
+
export declare function wireModelIdForPricing(modelId: string): {
|
|
45
|
+
baseId: string;
|
|
46
|
+
longContextEntry: boolean;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Look up classic OpenCode cost for a catalog / wire model id.
|
|
50
|
+
* Synthetic `-1m` entries retain both base and documented long-context rates.
|
|
51
|
+
*/
|
|
52
|
+
export declare function getCursorModelCost(modelId: string): OpenCodeModelCost | undefined;
|
|
53
|
+
export declare function applyCursorModelCost<T extends Record<string, unknown>>(modelId: string, entry: T): T & {
|
|
54
|
+
cost?: OpenCodeModelCost;
|
|
55
|
+
};
|
|
56
|
+
/** Convert classic cost → OpenCode 2.0 `Model.Info.cost` array. */
|
|
57
|
+
export declare function toOpenCode2Costs(cost: OpenCodeModelCost | undefined): OpenCode2ModelCost[];
|
|
58
|
+
/**
|
|
59
|
+
* Coverage helper for CI / maintainer checks.
|
|
60
|
+
* Intentionally unpriced first-party ids are counted as priced (covered).
|
|
61
|
+
*/
|
|
62
|
+
export declare function checkCursorPricingCoverage(modelIds: string[]): CursorPricingCoverage;
|
package/dist/pricing.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor model token pricing for OpenCode cost reporting.
|
|
3
|
+
*
|
|
4
|
+
* Rates come from Cursor's public docs (Other Models pool = provider API price).
|
|
5
|
+
* Generated data lives in `pricing-data.ts`; regenerate with
|
|
6
|
+
* `bun run generate:pricing`. Unknown / unpublished models get no `cost`.
|
|
7
|
+
*/
|
|
8
|
+
import { CURSOR_MODEL_COSTS } from "./pricing-data.js";
|
|
9
|
+
/**
|
|
10
|
+
* Models we expose that Cursor does not currently publish numeric rates for
|
|
11
|
+
* on the Other Models table (Auto / first-party Cursor Models pool).
|
|
12
|
+
*/
|
|
13
|
+
export const CURSOR_UNPRICED_MODEL_IDS = ["default", "composer-2.5", "grok-4.5"];
|
|
14
|
+
const UNPRICED = new Set(CURSOR_UNPRICED_MODEL_IDS);
|
|
15
|
+
function isRecord(value) {
|
|
16
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17
|
+
}
|
|
18
|
+
function isNonNegativeFiniteNumber(value) {
|
|
19
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
20
|
+
}
|
|
21
|
+
function collectCostValidationErrors(value, path, errors) {
|
|
22
|
+
if (!isRecord(value)) {
|
|
23
|
+
errors.push(`${path} must be an object`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (!isNonNegativeFiniteNumber(value.input)) {
|
|
27
|
+
errors.push(`${path}.input must be a non-negative finite number`);
|
|
28
|
+
}
|
|
29
|
+
if (!isNonNegativeFiniteNumber(value.output)) {
|
|
30
|
+
errors.push(`${path}.output must be a non-negative finite number`);
|
|
31
|
+
}
|
|
32
|
+
if (value.cache_read !== undefined && !isNonNegativeFiniteNumber(value.cache_read)) {
|
|
33
|
+
errors.push(`${path}.cache_read must be a non-negative finite number`);
|
|
34
|
+
}
|
|
35
|
+
if (value.cache_write !== undefined && !isNonNegativeFiniteNumber(value.cache_write)) {
|
|
36
|
+
errors.push(`${path}.cache_write must be a non-negative finite number`);
|
|
37
|
+
}
|
|
38
|
+
if (value.context_over_200k !== undefined) {
|
|
39
|
+
collectCostValidationErrors(value.context_over_200k, `${path}.context_over_200k`, errors);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function validateOpenCodeModelCost(value, path = "cost") {
|
|
43
|
+
const errors = [];
|
|
44
|
+
collectCostValidationErrors(value, path, errors);
|
|
45
|
+
return { valid: errors.length === 0, errors };
|
|
46
|
+
}
|
|
47
|
+
export function isOpenCodeModelCost(value) {
|
|
48
|
+
return validateOpenCodeModelCost(value).valid;
|
|
49
|
+
}
|
|
50
|
+
/** Strip synthetic OpenCode long-context suffix (`…-1m`, `…-1m-2`, …). */
|
|
51
|
+
export function wireModelIdForPricing(modelId) {
|
|
52
|
+
const match = /^(.*)-1m(?:-(\d+))?$/.exec(modelId);
|
|
53
|
+
if (!match)
|
|
54
|
+
return { baseId: modelId, longContextEntry: false };
|
|
55
|
+
return { baseId: match[1], longContextEntry: true };
|
|
56
|
+
}
|
|
57
|
+
function asMutableCost(value) {
|
|
58
|
+
const copy = { input: value.input, output: value.output };
|
|
59
|
+
if (value.cache_read !== undefined)
|
|
60
|
+
copy.cache_read = value.cache_read;
|
|
61
|
+
if (value.cache_write !== undefined)
|
|
62
|
+
copy.cache_write = value.cache_write;
|
|
63
|
+
if (value.context_over_200k) {
|
|
64
|
+
copy.context_over_200k = asMutableCost(value.context_over_200k);
|
|
65
|
+
}
|
|
66
|
+
return copy;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Look up classic OpenCode cost for a catalog / wire model id.
|
|
70
|
+
* Synthetic `-1m` entries retain both base and documented long-context rates.
|
|
71
|
+
*/
|
|
72
|
+
export function getCursorModelCost(modelId) {
|
|
73
|
+
const { baseId } = wireModelIdForPricing(modelId);
|
|
74
|
+
const found = CURSOR_MODEL_COSTS[baseId];
|
|
75
|
+
if (!found)
|
|
76
|
+
return undefined;
|
|
77
|
+
return asMutableCost(found);
|
|
78
|
+
}
|
|
79
|
+
export function applyCursorModelCost(modelId, entry) {
|
|
80
|
+
const modelCost = getCursorModelCost(modelId);
|
|
81
|
+
if (!modelCost)
|
|
82
|
+
return entry;
|
|
83
|
+
return { ...entry, cost: modelCost };
|
|
84
|
+
}
|
|
85
|
+
/** Convert classic cost → OpenCode 2.0 `Model.Info.cost` array. */
|
|
86
|
+
export function toOpenCode2Costs(cost) {
|
|
87
|
+
if (!cost)
|
|
88
|
+
return [];
|
|
89
|
+
const out = [
|
|
90
|
+
{
|
|
91
|
+
input: cost.input,
|
|
92
|
+
output: cost.output,
|
|
93
|
+
cache: {
|
|
94
|
+
read: cost.cache_read ?? 0,
|
|
95
|
+
write: cost.cache_write ?? 0,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
if (cost.context_over_200k) {
|
|
100
|
+
out.push({
|
|
101
|
+
tier: { type: "context", size: 200_000 },
|
|
102
|
+
input: cost.context_over_200k.input,
|
|
103
|
+
output: cost.context_over_200k.output,
|
|
104
|
+
cache: {
|
|
105
|
+
read: cost.context_over_200k.cache_read ?? 0,
|
|
106
|
+
write: cost.context_over_200k.cache_write ?? 0,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Coverage helper for CI / maintainer checks.
|
|
114
|
+
* Intentionally unpriced first-party ids are counted as priced (covered).
|
|
115
|
+
*/
|
|
116
|
+
export function checkCursorPricingCoverage(modelIds) {
|
|
117
|
+
const priced = [];
|
|
118
|
+
const missing = [];
|
|
119
|
+
for (const modelId of modelIds) {
|
|
120
|
+
const { baseId } = wireModelIdForPricing(modelId);
|
|
121
|
+
if (UNPRICED.has(baseId) || getCursorModelCost(modelId))
|
|
122
|
+
priced.push(modelId);
|
|
123
|
+
else
|
|
124
|
+
missing.push(modelId);
|
|
125
|
+
}
|
|
126
|
+
return { priced, missing };
|
|
127
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-opencode-provider",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Use Cursor subscription models from OpenCode via Cursor's Connect-RPC agent protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,7 +58,10 @@
|
|
|
58
58
|
"test": "bun test",
|
|
59
59
|
"test:node-http2": "bun run build && node test/node-http2-cleanup.mjs",
|
|
60
60
|
"test:watch": "bun test --watch",
|
|
61
|
-
"
|
|
61
|
+
"generate:pricing": "bun run scripts/generate-cursor-pricing.ts",
|
|
62
|
+
"generate:pricing:check": "bun run scripts/generate-cursor-pricing.ts -- --check",
|
|
63
|
+
"check:pricing": "bun run scripts/check-cursor-pricing-coverage.ts",
|
|
64
|
+
"prepublishOnly": "bun run generate:pricing && bun run build"
|
|
62
65
|
},
|
|
63
66
|
"dependencies": {
|
|
64
67
|
"@ai-sdk/provider": "3.0.14",
|