opencode-cursor-provider 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +55 -0
  2. package/README.md +112 -0
  3. package/dist/auth/credential.d.ts +44 -0
  4. package/dist/auth/credential.js +85 -0
  5. package/dist/auth/link.d.ts +14 -0
  6. package/dist/auth/link.js +120 -0
  7. package/dist/auth/state.d.ts +54 -0
  8. package/dist/auth/state.js +54 -0
  9. package/dist/bridge/agent.d.ts +17 -0
  10. package/dist/bridge/agent.js +39 -0
  11. package/dist/bridge/binding.d.ts +35 -0
  12. package/dist/bridge/binding.js +59 -0
  13. package/dist/bridge/bridge.d.ts +16 -0
  14. package/dist/bridge/bridge.js +21 -0
  15. package/dist/bridge/conversation.d.ts +64 -0
  16. package/dist/bridge/conversation.js +86 -0
  17. package/dist/bridge/correlation.d.ts +10 -0
  18. package/dist/bridge/correlation.js +52 -0
  19. package/dist/bridge/lock.d.ts +4 -0
  20. package/dist/bridge/lock.js +24 -0
  21. package/dist/bridge/response-journal.d.ts +7 -0
  22. package/dist/bridge/response-journal.js +49 -0
  23. package/dist/bridge/translate.d.ts +60 -0
  24. package/dist/bridge/translate.js +188 -0
  25. package/dist/bridge/turn.d.ts +29 -0
  26. package/dist/bridge/turn.js +219 -0
  27. package/dist/catalog/catalog.d.ts +20 -0
  28. package/dist/catalog/catalog.js +192 -0
  29. package/dist/catalog/source.d.ts +8 -0
  30. package/dist/catalog/source.js +30 -0
  31. package/dist/errors.d.ts +35 -0
  32. package/dist/errors.js +36 -0
  33. package/dist/ids.d.ts +33 -0
  34. package/dist/ids.js +52 -0
  35. package/dist/index.d.ts +4 -0
  36. package/dist/index.js +3 -0
  37. package/dist/model/language-model.d.ts +10 -0
  38. package/dist/model/language-model.js +539 -0
  39. package/dist/model/provider-options.d.ts +19 -0
  40. package/dist/model/provider-options.js +84 -0
  41. package/dist/plugin.d.ts +2 -0
  42. package/dist/plugin.js +127 -0
  43. package/dist/runtime.d.ts +8 -0
  44. package/dist/runtime.js +25 -0
  45. package/package.json +33 -0
@@ -0,0 +1,539 @@
1
+ import { extractScope } from "../bridge/correlation.js";
2
+ import { canonicalJson, } from "../bridge/conversation.js";
3
+ import { CursorPluginFailure } from "../errors.js";
4
+ import { parseCursorOptions } from "./provider-options.js";
5
+ export function toLanguageModel(input) {
6
+ const stream = (options) => {
7
+ const coupled = coupleAbort(options.abortSignal);
8
+ return toStreamParts(input.bridge.turn(parseCall({ ...options, abortSignal: coupled.signal }, input.modelID, input.params)), coupled.abort, warningsOf(options));
9
+ };
10
+ return {
11
+ specificationVersion: "v3",
12
+ provider: "cursor",
13
+ modelId: input.wireID,
14
+ supportedUrls: {},
15
+ doGenerate: async (options) => collectGenerate(stream(options)),
16
+ doStream: async (options) => ({ stream: stream(options) }),
17
+ };
18
+ }
19
+ function refuse(error) {
20
+ throw new CursorPluginFailure(error);
21
+ }
22
+ async function collectGenerate(stream) {
23
+ const content = [];
24
+ let finishReason = { unified: "other", raw: undefined };
25
+ let usage = emptyUsage();
26
+ let warnings = [];
27
+ let response;
28
+ let providerMetadata;
29
+ const reader = stream.getReader();
30
+ try {
31
+ while (true) {
32
+ const next = await reader.read();
33
+ if (next.done)
34
+ break;
35
+ const part = next.value;
36
+ switch (part.type) {
37
+ case "stream-start":
38
+ warnings = part.warnings;
39
+ break;
40
+ case "text-delta":
41
+ appendText(content, part.delta);
42
+ break;
43
+ case "reasoning-delta":
44
+ appendReasoning(content, part.delta);
45
+ break;
46
+ case "tool-call":
47
+ case "tool-result":
48
+ content.push(part);
49
+ break;
50
+ case "finish":
51
+ finishReason = part.finishReason;
52
+ usage = part.usage;
53
+ providerMetadata = part.providerMetadata;
54
+ break;
55
+ case "response-metadata":
56
+ response = {
57
+ ...(part.id === undefined ? {} : { id: part.id }),
58
+ ...(part.timestamp === undefined ? {} : { timestamp: part.timestamp }),
59
+ ...(part.modelId === undefined ? {} : { modelId: part.modelId }),
60
+ };
61
+ break;
62
+ case "error":
63
+ throw part.error;
64
+ default:
65
+ break;
66
+ }
67
+ }
68
+ return {
69
+ content,
70
+ finishReason,
71
+ usage,
72
+ warnings,
73
+ ...(response === undefined ? {} : { response }),
74
+ ...(providerMetadata === undefined ? {} : { providerMetadata }),
75
+ };
76
+ }
77
+ finally {
78
+ await reader.cancel();
79
+ }
80
+ }
81
+ function appendText(content, delta) {
82
+ const previous = content.at(-1);
83
+ if (previous?.type === "text") {
84
+ previous.text += delta;
85
+ return;
86
+ }
87
+ content.push({ type: "text", text: delta });
88
+ }
89
+ function appendReasoning(content, delta) {
90
+ const previous = content.at(-1);
91
+ if (previous?.type === "reasoning") {
92
+ previous.text += delta;
93
+ return;
94
+ }
95
+ content.push({ type: "reasoning", text: delta });
96
+ }
97
+ function parseCall(options, modelID, params) {
98
+ if (options.tools !== undefined && options.tools.length > 0) {
99
+ refuse({ kind: "unsupported-request", reason: "tools-requested" });
100
+ }
101
+ if (options.toolChoice !== undefined && options.toolChoice.type !== "auto") {
102
+ refuse({ kind: "unsupported-request", reason: "tool-choice" });
103
+ }
104
+ if (options.responseFormat?.type === "json") {
105
+ refuse({ kind: "unsupported-request", reason: "structured-output" });
106
+ }
107
+ const system = [];
108
+ const turns = [];
109
+ for (const message of options.prompt) {
110
+ if (message.role === "system") {
111
+ system.push(message.content);
112
+ continue;
113
+ }
114
+ if (message.role === "user") {
115
+ const parts = userParts(message.content);
116
+ if (parts.length > 0)
117
+ turns.push({ role: "user", parts });
118
+ continue;
119
+ }
120
+ if (message.role === "assistant") {
121
+ const parts = assistantParts(message.content);
122
+ if (parts.length > 0)
123
+ turns.push({ role: "assistant", parts });
124
+ continue;
125
+ }
126
+ if (message.role === "tool") {
127
+ const parts = toolParts(message.content);
128
+ if (parts.length > 0)
129
+ turns.push({ role: "tool", parts });
130
+ }
131
+ }
132
+ const extracted = extractScope(system);
133
+ const conversation = { system: extracted.system, turns };
134
+ const cursor = parseCursorOptions(options.providerOptions?.cursor);
135
+ return {
136
+ modelID,
137
+ scope: extracted.scope,
138
+ conversation,
139
+ ...cursor,
140
+ ...(options.includeRawChunks === true ? { includeRawChunks: true } : {}),
141
+ ...(params === undefined ? {} : { params }),
142
+ ...(options.abortSignal === undefined ? {} : { signal: options.abortSignal }),
143
+ };
144
+ }
145
+ function assistantParts(content) {
146
+ const parts = [];
147
+ for (const part of content) {
148
+ switch (part.type) {
149
+ case "text":
150
+ parts.push({ type: "text", text: part.text });
151
+ break;
152
+ case "reasoning":
153
+ parts.push({ type: "reasoning", text: part.text });
154
+ break;
155
+ case "tool-call":
156
+ parts.push({
157
+ type: "tool-call",
158
+ id: part.toolCallId,
159
+ name: part.toolName,
160
+ input: canonicalJson(part.input),
161
+ });
162
+ break;
163
+ case "tool-result": {
164
+ const result = historyResult(part.output);
165
+ parts.push({
166
+ type: "tool-result",
167
+ id: part.toolCallId,
168
+ name: part.toolName,
169
+ output: result.output,
170
+ isError: result.isError,
171
+ });
172
+ break;
173
+ }
174
+ case "file":
175
+ refuse({ kind: "unsupported-request", reason: "file-input" });
176
+ default: {
177
+ const _exhaustive = part;
178
+ return _exhaustive;
179
+ }
180
+ }
181
+ }
182
+ return parts;
183
+ }
184
+ function toolParts(content) {
185
+ return content.map((part) => {
186
+ if (part.type === "tool-result") {
187
+ const result = historyResult(part.output);
188
+ return {
189
+ type: "tool-result",
190
+ id: part.toolCallId,
191
+ name: part.toolName,
192
+ output: result.output,
193
+ isError: result.isError,
194
+ };
195
+ }
196
+ return {
197
+ type: "tool-approval",
198
+ id: part.approvalId,
199
+ approved: part.approved,
200
+ ...(part.reason === undefined ? {} : { reason: part.reason }),
201
+ };
202
+ });
203
+ }
204
+ function historyResult(output) {
205
+ switch (output.type) {
206
+ case "text":
207
+ return { output: [{ type: "text", text: output.value }], isError: false };
208
+ case "json":
209
+ return { output: [{ type: "text", text: canonicalJson(output.value) }], isError: false };
210
+ case "error-text":
211
+ return { output: [{ type: "text", text: output.value }], isError: true };
212
+ case "error-json":
213
+ return { output: [{ type: "text", text: canonicalJson(output.value) }], isError: true };
214
+ case "execution-denied":
215
+ return { output: [{ type: "text", text: output.reason ?? "Execution denied" }], isError: true };
216
+ case "content":
217
+ return { output: toolOutputParts(output.value), isError: false };
218
+ default: {
219
+ const _exhaustive = output;
220
+ return _exhaustive;
221
+ }
222
+ }
223
+ }
224
+ function toolOutputParts(content) {
225
+ const parts = [];
226
+ for (const part of content) {
227
+ switch (part.type) {
228
+ case "text":
229
+ parts.push({ type: "text", text: part.text });
230
+ break;
231
+ case "file-data":
232
+ case "image-data":
233
+ if (!part.mediaType.startsWith("image/") || part.mediaType === "image/*") {
234
+ refuse({ kind: "unsupported-request", reason: "file-input" });
235
+ }
236
+ parts.push({ type: "image", image: { data: part.data, mimeType: part.mediaType } });
237
+ break;
238
+ case "file-url":
239
+ case "file-id":
240
+ case "image-url":
241
+ case "image-file-id":
242
+ refuse({ kind: "unsupported-request", reason: "file-input" });
243
+ case "custom":
244
+ refuse({ kind: "unsupported-request", reason: "tool-result-content" });
245
+ default: {
246
+ const _exhaustive = part;
247
+ return _exhaustive;
248
+ }
249
+ }
250
+ }
251
+ return parts;
252
+ }
253
+ function userParts(content) {
254
+ const parts = [];
255
+ for (const part of content) {
256
+ if (part.type === "text") {
257
+ parts.push({ type: "text", text: part.text });
258
+ continue;
259
+ }
260
+ if (!part.mediaType.startsWith("image/") || part.mediaType === "image/*") {
261
+ refuse({ kind: "unsupported-request", reason: "file-input" });
262
+ }
263
+ if (part.data instanceof URL) {
264
+ refuse({ kind: "unsupported-request", reason: "file-input" });
265
+ }
266
+ const image = {
267
+ data: typeof part.data === "string" ? part.data : Buffer.from(part.data).toString("base64"),
268
+ mimeType: part.mediaType,
269
+ };
270
+ parts.push({ type: "image", image });
271
+ }
272
+ return parts;
273
+ }
274
+ function toStreamParts(events, abort, warnings) {
275
+ return new ReadableStream({
276
+ async start(controller) {
277
+ controller.enqueue({ type: "stream-start", warnings });
278
+ let textId;
279
+ let reasoningId;
280
+ let textSequence = 0;
281
+ let reasoningSequence = 0;
282
+ let usage = emptyUsage();
283
+ let finishReason = { unified: "other", raw: undefined };
284
+ let doneMetadata;
285
+ const tools = new Map();
286
+ const closeOpenParts = () => {
287
+ if (reasoningId) {
288
+ controller.enqueue({ type: "reasoning-end", id: reasoningId });
289
+ reasoningId = undefined;
290
+ }
291
+ if (textId) {
292
+ controller.enqueue({ type: "text-end", id: textId });
293
+ textId = undefined;
294
+ }
295
+ };
296
+ try {
297
+ for await (const event of events) {
298
+ switch (event.type) {
299
+ case "text":
300
+ if (reasoningId) {
301
+ controller.enqueue({ type: "reasoning-end", id: reasoningId });
302
+ reasoningId = undefined;
303
+ }
304
+ if (!textId) {
305
+ textSequence += 1;
306
+ textId = `text-${textSequence}`;
307
+ controller.enqueue({ type: "text-start", id: textId });
308
+ }
309
+ controller.enqueue({ type: "text-delta", id: textId, delta: event.delta });
310
+ break;
311
+ case "raw":
312
+ controller.enqueue({ type: "raw", rawValue: event.value });
313
+ break;
314
+ case "response-metadata":
315
+ controller.enqueue({
316
+ type: "response-metadata",
317
+ id: event.id,
318
+ ...(event.timestamp === undefined ? {} : { timestamp: new Date(event.timestamp) }),
319
+ ...(event.modelId === undefined ? {} : { modelId: event.modelId }),
320
+ });
321
+ break;
322
+ case "reasoning":
323
+ if (textId) {
324
+ controller.enqueue({ type: "text-end", id: textId });
325
+ textId = undefined;
326
+ }
327
+ if (!reasoningId) {
328
+ reasoningSequence += 1;
329
+ reasoningId = `reasoning-${reasoningSequence}`;
330
+ controller.enqueue({ type: "reasoning-start", id: reasoningId });
331
+ }
332
+ controller.enqueue({ type: "reasoning-delta", id: reasoningId, delta: event.delta });
333
+ break;
334
+ case "tool-call":
335
+ if (tools.has(event.id))
336
+ break;
337
+ if (reasoningId) {
338
+ controller.enqueue({ type: "reasoning-end", id: reasoningId });
339
+ reasoningId = undefined;
340
+ }
341
+ if (textId) {
342
+ controller.enqueue({ type: "text-end", id: textId });
343
+ textId = undefined;
344
+ }
345
+ controller.enqueue({
346
+ type: "tool-call",
347
+ toolCallId: event.id,
348
+ toolName: event.name,
349
+ input: JSON.stringify(event.input),
350
+ providerExecuted: true,
351
+ dynamic: true,
352
+ });
353
+ tools.set(event.id, "called");
354
+ break;
355
+ case "tool-result":
356
+ if (tools.get(event.id) === "completed")
357
+ break;
358
+ controller.enqueue({
359
+ type: "tool-result",
360
+ toolCallId: event.id,
361
+ toolName: event.name,
362
+ result: event.result,
363
+ isError: event.isError,
364
+ dynamic: true,
365
+ });
366
+ tools.set(event.id, "completed");
367
+ break;
368
+ case "usage":
369
+ usage = usageFrom(event);
370
+ break;
371
+ case "done":
372
+ finishReason = reasonFrom(event.reason);
373
+ doneMetadata = event.metadata;
374
+ break;
375
+ case "failed":
376
+ closeOpenParts();
377
+ controller.enqueue({ type: "error", error: new CursorPluginFailure(event.error) });
378
+ controller.close();
379
+ return;
380
+ default: {
381
+ const _exhaustive = event;
382
+ void _exhaustive;
383
+ }
384
+ }
385
+ }
386
+ closeOpenParts();
387
+ const providerMetadata = finishMetadata(doneMetadata);
388
+ controller.enqueue({
389
+ type: "finish",
390
+ finishReason,
391
+ usage,
392
+ ...(providerMetadata === undefined ? {} : { providerMetadata }),
393
+ });
394
+ controller.close();
395
+ }
396
+ catch (error) {
397
+ closeOpenParts();
398
+ controller.enqueue({ type: "error", error });
399
+ controller.close();
400
+ }
401
+ },
402
+ cancel() {
403
+ abort.abort();
404
+ },
405
+ });
406
+ }
407
+ function finishMetadata(metadata) {
408
+ if (metadata === undefined)
409
+ return undefined;
410
+ return {
411
+ cursor: {
412
+ runId: metadata.runId,
413
+ ...(metadata.requestId === undefined ? {} : { requestId: metadata.requestId }),
414
+ ...(metadata.durationMs === undefined ? {} : { durationMs: metadata.durationMs }),
415
+ ...(metadata.modelId === undefined ? {} : { modelId: metadata.modelId }),
416
+ ...(metadata.git === undefined ? {} : { git: metadata.git.map((branch) => ({ ...branch })) }),
417
+ },
418
+ };
419
+ }
420
+ function coupleAbort(signal) {
421
+ const abort = new AbortController();
422
+ if (signal === undefined)
423
+ return { signal: abort.signal, abort };
424
+ if (signal.aborted) {
425
+ abort.abort();
426
+ return { signal, abort };
427
+ }
428
+ return { signal: AbortSignal.any([signal, abort.signal]), abort };
429
+ }
430
+ function warningsOf(options) {
431
+ const warnings = [];
432
+ if (options.temperature !== undefined)
433
+ warnings.push({ type: "unsupported", feature: "temperature" });
434
+ if (options.topP !== undefined)
435
+ warnings.push({ type: "unsupported", feature: "topP" });
436
+ if (options.topK !== undefined)
437
+ warnings.push({ type: "unsupported", feature: "topK" });
438
+ if (options.maxOutputTokens !== undefined)
439
+ warnings.push({ type: "unsupported", feature: "maxOutputTokens" });
440
+ if (options.stopSequences !== undefined && options.stopSequences.length > 0) {
441
+ warnings.push({ type: "unsupported", feature: "stopSequences" });
442
+ }
443
+ if (options.presencePenalty !== undefined)
444
+ warnings.push({ type: "unsupported", feature: "presencePenalty" });
445
+ if (options.frequencyPenalty !== undefined)
446
+ warnings.push({ type: "unsupported", feature: "frequencyPenalty" });
447
+ if (options.seed !== undefined)
448
+ warnings.push({ type: "unsupported", feature: "seed" });
449
+ if (options.headers !== undefined && Object.values(options.headers).some((value) => value !== undefined)) {
450
+ warnings.push({ type: "unsupported", feature: "headers" });
451
+ }
452
+ for (const [provider, value] of Object.entries(options.providerOptions ?? {})) {
453
+ if (Object.keys(value).length === 0)
454
+ continue;
455
+ if (provider !== "cursor") {
456
+ warnings.push({ type: "unsupported", feature: `providerOptions.${provider}` });
457
+ continue;
458
+ }
459
+ for (const option of Object.keys(value)) {
460
+ if (!CURSOR_OPTIONS.has(option)) {
461
+ warnings.push({ type: "unsupported", feature: `providerOptions.cursor.${option}` });
462
+ }
463
+ }
464
+ }
465
+ for (const message of options.prompt) {
466
+ if (hasOptions(message.providerOptions))
467
+ pushUnsupported(warnings, "message.providerOptions");
468
+ if (message.role === "system")
469
+ continue;
470
+ for (const part of message.content) {
471
+ if (hasOptions(part.providerOptions))
472
+ pushUnsupported(warnings, "content.providerOptions");
473
+ if (part.type !== "tool-result")
474
+ continue;
475
+ if ("providerOptions" in part.output && hasOptions(part.output.providerOptions)) {
476
+ pushUnsupported(warnings, "content.providerOptions");
477
+ }
478
+ if (part.output.type !== "content")
479
+ continue;
480
+ for (const output of part.output.value) {
481
+ if (hasOptions(output.providerOptions))
482
+ pushUnsupported(warnings, "content.providerOptions");
483
+ }
484
+ }
485
+ }
486
+ return warnings;
487
+ }
488
+ const CURSOR_OPTIONS = new Set([
489
+ "mode",
490
+ "tools",
491
+ "disallowedTools",
492
+ "sandboxOptions",
493
+ "autoReview",
494
+ "settingSources",
495
+ ]);
496
+ function hasOptions(options) {
497
+ return options !== undefined && Object.keys(options).length > 0;
498
+ }
499
+ function pushUnsupported(warnings, feature) {
500
+ if (warnings.some((warning) => warning.type === "unsupported" && warning.feature === feature))
501
+ return;
502
+ warnings.push({ type: "unsupported", feature });
503
+ }
504
+ function emptyUsage() {
505
+ return {
506
+ inputTokens: { total: undefined, noCache: undefined, cacheRead: undefined, cacheWrite: undefined },
507
+ outputTokens: { total: undefined, text: undefined, reasoning: undefined },
508
+ };
509
+ }
510
+ function usageFrom(event) {
511
+ return {
512
+ inputTokens: {
513
+ total: event.input,
514
+ noCache: Math.max(0, event.input - event.cacheRead - event.cacheWrite),
515
+ cacheRead: event.cacheRead,
516
+ cacheWrite: event.cacheWrite,
517
+ },
518
+ outputTokens: {
519
+ total: event.output,
520
+ text: Math.max(0, event.output - event.reasoning),
521
+ reasoning: event.reasoning,
522
+ },
523
+ raw: {
524
+ inputTokens: event.input,
525
+ outputTokens: event.output,
526
+ cacheReadTokens: event.cacheRead,
527
+ cacheWriteTokens: event.cacheWrite,
528
+ reasoningTokens: event.reasoning,
529
+ totalTokens: event.total,
530
+ },
531
+ };
532
+ }
533
+ function reasonFrom(reason) {
534
+ if (reason === "stop")
535
+ return { unified: "stop", raw: reason };
536
+ if (reason === "length")
537
+ return { unified: "length", raw: reason };
538
+ return { unified: "other", raw: reason };
539
+ }
@@ -0,0 +1,19 @@
1
+ import type { JSONObject } from "@ai-sdk/provider";
2
+ import type { AgentModeOption, SettingSource, ToolName } from "@cursor/sdk";
3
+ export interface CursorAgentOptions {
4
+ readonly tools?: readonly ToolName[];
5
+ readonly disallowedTools?: readonly ToolName[];
6
+ readonly sandboxOptions?: {
7
+ readonly enabled: boolean;
8
+ };
9
+ readonly autoReview?: boolean;
10
+ readonly settingSources?: readonly SettingSource[];
11
+ }
12
+ export interface CursorProviderOptions extends CursorAgentOptions {
13
+ readonly mode?: AgentModeOption;
14
+ }
15
+ export interface ParsedCursorOptions {
16
+ readonly mode?: AgentModeOption;
17
+ readonly agentOptions?: CursorAgentOptions;
18
+ }
19
+ export declare function parseCursorOptions(cursor: JSONObject | undefined): ParsedCursorOptions;
@@ -0,0 +1,84 @@
1
+ import { CursorPluginFailure } from "../errors.js";
2
+ export function parseCursorOptions(cursor) {
3
+ if (cursor === undefined)
4
+ return {};
5
+ const mode = parseMode(cursor.mode);
6
+ const tools = stringList(cursor.tools);
7
+ const disallowedTools = stringList(cursor.disallowedTools);
8
+ const sandboxOptions = parseSandbox(cursor.sandboxOptions);
9
+ const autoReview = optionalBoolean(cursor.autoReview);
10
+ const settingSources = parseSettingSources(cursor.settingSources);
11
+ const agentOptions = compactAgentOptions({
12
+ ...(tools === undefined ? {} : { tools }),
13
+ ...(disallowedTools === undefined ? {} : { disallowedTools }),
14
+ ...(sandboxOptions === undefined ? {} : { sandboxOptions }),
15
+ ...(autoReview === undefined ? {} : { autoReview }),
16
+ ...(settingSources === undefined ? {} : { settingSources }),
17
+ });
18
+ return {
19
+ ...(mode === undefined ? {} : { mode }),
20
+ ...(agentOptions === undefined ? {} : { agentOptions }),
21
+ };
22
+ }
23
+ function compactAgentOptions(input) {
24
+ return Object.keys(input).length === 0 ? undefined : input;
25
+ }
26
+ function parseMode(value) {
27
+ if (value === undefined)
28
+ return undefined;
29
+ if (value === "agent" || value === "plan")
30
+ return value;
31
+ invalidOption();
32
+ }
33
+ function stringList(value) {
34
+ if (value === undefined)
35
+ return undefined;
36
+ if (!Array.isArray(value) || !value.every((item) => typeof item === "string"))
37
+ invalidOption();
38
+ return [...value];
39
+ }
40
+ function parseSandbox(value) {
41
+ if (value === undefined)
42
+ return undefined;
43
+ if (!isObject(value) || Object.keys(value).length !== 1 || typeof value.enabled !== "boolean")
44
+ invalidOption();
45
+ return { enabled: value.enabled };
46
+ }
47
+ function optionalBoolean(value) {
48
+ if (value === undefined)
49
+ return undefined;
50
+ if (typeof value !== "boolean")
51
+ invalidOption();
52
+ return value;
53
+ }
54
+ function parseSettingSources(value) {
55
+ const sources = stringList(value);
56
+ if (sources === undefined)
57
+ return undefined;
58
+ const parsed = [];
59
+ for (const source of sources) {
60
+ if (!isSettingSource(source))
61
+ invalidOption();
62
+ parsed.push(source);
63
+ }
64
+ return parsed;
65
+ }
66
+ function isSettingSource(value) {
67
+ switch (value) {
68
+ case "project":
69
+ case "user":
70
+ case "team":
71
+ case "mdm":
72
+ case "plugins":
73
+ case "all":
74
+ return true;
75
+ default:
76
+ return false;
77
+ }
78
+ }
79
+ function isObject(value) {
80
+ return typeof value === "object" && value !== null && !Array.isArray(value);
81
+ }
82
+ function invalidOption() {
83
+ throw new CursorPluginFailure({ kind: "unsupported-request", reason: "provider-option" });
84
+ }
@@ -0,0 +1,2 @@
1
+ import { Plugin } from "@opencode-ai/plugin";
2
+ export declare const plugin: Plugin.Plugin;