@stablekernel/opencode-cursor 0.1.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,502 @@
1
+ // src/api-key.ts
2
+ import { createHash } from "crypto";
3
+ var CURSOR_API_KEY_ENV_VAR = "CURSOR_API_KEY";
4
+ var PLACEHOLDERS = /* @__PURE__ */ new Set([
5
+ CURSOR_API_KEY_ENV_VAR,
6
+ `$${CURSOR_API_KEY_ENV_VAR}`,
7
+ `\${${CURSOR_API_KEY_ENV_VAR}}`
8
+ ]);
9
+ function resolveCursorApiKey(candidate) {
10
+ const trimmed = candidate?.trim();
11
+ if (trimmed && !PLACEHOLDERS.has(trimmed)) return trimmed;
12
+ const fromEnv = process.env[CURSOR_API_KEY_ENV_VAR]?.trim();
13
+ return fromEnv ? fromEnv : void 0;
14
+ }
15
+ function fingerprintApiKey(apiKey) {
16
+ return createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
17
+ }
18
+
19
+ // src/provider/agent-events.ts
20
+ function toolDisplayName(toolCall) {
21
+ if (!toolCall) return "tool";
22
+ if (toolCall.type === "mcp") {
23
+ const name = toolCall.args?.toolName;
24
+ const server = toolCall.args?.providerIdentifier;
25
+ if (name) return server ? `${server}/${name}` : String(name);
26
+ return "mcp";
27
+ }
28
+ return toolCall.type ?? "tool";
29
+ }
30
+ async function* streamAgentTurn(agent, message, options) {
31
+ const queue = [];
32
+ let wake;
33
+ let finished = false;
34
+ let failure;
35
+ const debug = process.env.OPENCODE_CURSOR_DEBUG === "1";
36
+ const counts = {};
37
+ const push = (event) => {
38
+ queue.push(event);
39
+ wake?.();
40
+ wake = void 0;
41
+ };
42
+ const onDelta = ({ update }) => {
43
+ if (debug) counts[update.type] = (counts[update.type] ?? 0) + 1;
44
+ switch (update.type) {
45
+ case "text-delta":
46
+ push({ type: "text-delta", text: update.text });
47
+ break;
48
+ case "thinking-delta":
49
+ push({ type: "reasoning-delta", text: update.text });
50
+ break;
51
+ case "tool-call-started":
52
+ push({
53
+ type: "tool-call",
54
+ id: String(update.callId),
55
+ name: toolDisplayName(update.toolCall),
56
+ input: update.toolCall?.args ?? {}
57
+ });
58
+ break;
59
+ case "tool-call-completed": {
60
+ const tool = update.toolCall ?? {};
61
+ const result = tool.result;
62
+ const mcpError = tool.type === "mcp" && result?.value?.isError === true;
63
+ push({
64
+ type: "tool-result",
65
+ id: String(update.callId),
66
+ name: toolDisplayName(tool),
67
+ result: result ?? null,
68
+ isError: result?.status === "error" || mcpError
69
+ });
70
+ break;
71
+ }
72
+ case "turn-ended":
73
+ if (update.usage) push({ type: "usage", usage: update.usage });
74
+ break;
75
+ }
76
+ };
77
+ const runHolder = {};
78
+ const onAbort = () => {
79
+ void Promise.resolve(runHolder.run?.cancel()).catch(() => {
80
+ });
81
+ };
82
+ options.abortSignal?.addEventListener("abort", onAbort);
83
+ const sendTurn = async () => {
84
+ try {
85
+ return await agent.send(message, { mode: options.mode, onDelta });
86
+ } catch (err) {
87
+ if (err instanceof Error && err.name === "AgentBusyError") {
88
+ if (debug) console.error("[cursor:debug] agent busy; retrying send with local.force");
89
+ return agent.send(message, { mode: options.mode, onDelta, local: { force: true } });
90
+ }
91
+ throw err;
92
+ }
93
+ };
94
+ void sendTurn().then(async (run) => {
95
+ runHolder.run = run;
96
+ const result = await run.wait();
97
+ if (debug) {
98
+ console.error(
99
+ `[cursor:debug] updates=${JSON.stringify(counts)} status=${result.status} resultLen=${(result.result ?? "").length}`
100
+ );
101
+ }
102
+ if (result.status === "error") {
103
+ throw new Error(
104
+ `Cursor run ended with status "error"${result.result ? `: ${result.result}` : ""}`
105
+ );
106
+ }
107
+ push({ type: "finish", ...result.status === "cancelled" ? {} : { text: result.result } });
108
+ }).catch((err) => {
109
+ failure = err;
110
+ if (debug) console.error(`[cursor:debug] send failed: ${err instanceof Error ? err.message : String(err)}`);
111
+ }).finally(() => {
112
+ finished = true;
113
+ wake?.();
114
+ wake = void 0;
115
+ });
116
+ try {
117
+ while (true) {
118
+ if (queue.length > 0) {
119
+ yield queue.shift();
120
+ continue;
121
+ }
122
+ if (finished) break;
123
+ await new Promise((resolve) => {
124
+ wake = resolve;
125
+ });
126
+ }
127
+ while (queue.length > 0) yield queue.shift();
128
+ if (failure) throw failure;
129
+ } finally {
130
+ options.abortSignal?.removeEventListener("abort", onAbort);
131
+ }
132
+ }
133
+
134
+ // src/provider/controls.ts
135
+ function buildModelSelection(modelId, params) {
136
+ const paramList = Object.entries(params ?? {}).map(([id, value]) => ({ id, value }));
137
+ return paramList.length > 0 ? { id: modelId, params: paramList } : { id: modelId };
138
+ }
139
+ function isRecord(value) {
140
+ return typeof value === "object" && value !== null && !Array.isArray(value);
141
+ }
142
+ function isMode(value) {
143
+ return value === "agent" || value === "plan";
144
+ }
145
+ function resolveControls(modelId, staticControls, providerOptions) {
146
+ const po = providerOptions ?? {};
147
+ const mode = isMode(po["mode"]) ? po["mode"] : staticControls.mode;
148
+ const params = { ...staticControls.params ?? {} };
149
+ if (isRecord(po["params"])) {
150
+ for (const [key, value] of Object.entries(po["params"])) {
151
+ if (value != null) params[key] = String(value);
152
+ }
153
+ }
154
+ if (typeof po["thinking"] === "string" && params["thinking"] === void 0) {
155
+ params["thinking"] = po["thinking"];
156
+ }
157
+ return { mode, modelSelection: buildModelSelection(modelId, params) };
158
+ }
159
+
160
+ // src/cursor-runtime.ts
161
+ var cached;
162
+ async function loadCursorSdk() {
163
+ if (!cached) {
164
+ cached = import("@cursor/sdk").catch((err) => {
165
+ cached = void 0;
166
+ const detail = err instanceof Error ? err.message : String(err);
167
+ throw new Error(
168
+ `[opencode-cursor] Failed to load "@cursor/sdk". Make sure it is installed (\`npm install @cursor/sdk\`). Original error: ${detail}`
169
+ );
170
+ });
171
+ }
172
+ return cached;
173
+ }
174
+
175
+ // src/provider/agent-backend.ts
176
+ import { execSync } from "child_process";
177
+ import { existsSync } from "fs";
178
+ import { fileURLToPath } from "url";
179
+
180
+ // src/provider/sidecar-client.ts
181
+ import { spawn } from "child_process";
182
+ import { createInterface } from "readline";
183
+ function reviveError(error) {
184
+ const e = error ?? {};
185
+ const err = new Error(e.message ?? "sidecar error");
186
+ if (e.name) err.name = e.name;
187
+ return err;
188
+ }
189
+ var SidecarClient = class {
190
+ options;
191
+ child;
192
+ reader;
193
+ pending = /* @__PURE__ */ new Map();
194
+ nextId = 1;
195
+ disposed = false;
196
+ constructor(options) {
197
+ this.options = options;
198
+ }
199
+ /** Spawn (or reuse) the child process. */
200
+ ensureChild() {
201
+ if (this.disposed) throw new Error("cursor sidecar client disposed");
202
+ if (this.child) return this.child;
203
+ const child = spawn(this.options.nodePath ?? "node", [this.options.scriptPath], {
204
+ stdio: ["pipe", "pipe", "pipe"],
205
+ env: { ...process.env, ...this.options.env }
206
+ });
207
+ this.child = child;
208
+ this.reader = createInterface({ input: child.stdout });
209
+ this.reader.on("line", (line) => this.handleLine(line));
210
+ child.stderr.on("data", (chunk) => {
211
+ if (this.options.debug || process.env["OPENCODE_CURSOR_DEBUG"]) {
212
+ process.stderr.write(`[cursor:sidecar] ${chunk}`);
213
+ }
214
+ });
215
+ child.on("exit", (code) => {
216
+ this.failAll(new Error(`cursor sidecar exited (code ${code ?? "unknown"})`));
217
+ this.child = void 0;
218
+ this.reader?.close();
219
+ this.reader = void 0;
220
+ });
221
+ child.on("error", (err) => {
222
+ this.failAll(new Error(`cursor sidecar failed to start: ${err.message}`));
223
+ this.child = void 0;
224
+ });
225
+ this.updateRefs();
226
+ return child;
227
+ }
228
+ /**
229
+ * Keep the child (and its pipes) from holding the parent's event loop open
230
+ * while idle, but ref it whenever a reply is outstanding so the loop can't
231
+ * exit mid-request. Without this, any process that uses the provider and
232
+ * never dispose()s — scripts, tests, opencode itself on shutdown — hangs.
233
+ */
234
+ updateRefs() {
235
+ const child = this.child;
236
+ if (!child) return;
237
+ const refable = [child, child.stdin, child.stdout, child.stderr];
238
+ if (this.pending.size > 0) {
239
+ for (const target of refable) target.ref?.();
240
+ } else {
241
+ for (const target of refable) target.unref?.();
242
+ }
243
+ }
244
+ failAll(err) {
245
+ for (const pending of this.pending.values()) {
246
+ pending.onStreamError?.(err);
247
+ pending.reject(err);
248
+ }
249
+ this.pending.clear();
250
+ this.updateRefs();
251
+ }
252
+ handleLine(line) {
253
+ if (!line.trim()) return;
254
+ let msg;
255
+ try {
256
+ msg = JSON.parse(line);
257
+ } catch {
258
+ return;
259
+ }
260
+ const id = msg["id"];
261
+ if (typeof id !== "number") return;
262
+ const pending = this.pending.get(id);
263
+ if (!pending) return;
264
+ const ev = msg["ev"];
265
+ if (ev === "update") {
266
+ pending.onUpdate?.(msg["update"]);
267
+ return;
268
+ }
269
+ if (ev === "result") {
270
+ this.pending.delete(id);
271
+ this.updateRefs();
272
+ pending.onResult?.(msg["result"]);
273
+ return;
274
+ }
275
+ if (ev === "error") {
276
+ this.pending.delete(id);
277
+ this.updateRefs();
278
+ pending.onStreamError?.(reviveError(msg["error"]));
279
+ return;
280
+ }
281
+ if (msg["ok"] === true) {
282
+ if (!pending.onResult) {
283
+ this.pending.delete(id);
284
+ this.updateRefs();
285
+ }
286
+ pending.resolve(msg);
287
+ } else {
288
+ this.pending.delete(id);
289
+ this.updateRefs();
290
+ pending.reject(reviveError(msg["error"]));
291
+ }
292
+ }
293
+ request(payload, hooks) {
294
+ const child = this.ensureChild();
295
+ const id = this.nextId++;
296
+ return new Promise((resolve, reject) => {
297
+ this.pending.set(id, { resolve, reject, ...hooks });
298
+ this.updateRefs();
299
+ child.stdin.write(`${JSON.stringify({ id, ...payload })}
300
+ `, (err) => {
301
+ if (err) {
302
+ this.pending.delete(id);
303
+ this.updateRefs();
304
+ reject(err);
305
+ }
306
+ });
307
+ });
308
+ }
309
+ async createAgent(options) {
310
+ const res = await this.request({ op: "create", options });
311
+ return this.wrapAgent(String(res["agentId"]));
312
+ }
313
+ async resumeAgent(agentId, options) {
314
+ const res = await this.request({ op: "resume", agentId, options });
315
+ return this.wrapAgent(String(res["agentId"]));
316
+ }
317
+ wrapAgent(agentId) {
318
+ return {
319
+ agentId,
320
+ send: (message, options) => this.sendTurn(agentId, message, options),
321
+ close: () => {
322
+ void this.request({ op: "close", agentId }).catch(() => {
323
+ });
324
+ }
325
+ };
326
+ }
327
+ async sendTurn(agentId, message, options) {
328
+ let settle;
329
+ const waited = new Promise((resolve, reject) => {
330
+ settle = { resolve, reject };
331
+ });
332
+ waited.catch(() => {
333
+ });
334
+ let sendId;
335
+ const ack = this.request(
336
+ {
337
+ op: "send",
338
+ agentId,
339
+ message,
340
+ ...options?.mode ? { mode: options.mode } : {},
341
+ ...options?.local?.force ? { force: true } : {}
342
+ },
343
+ {
344
+ onUpdate: (update) => options?.onDelta?.({ update }),
345
+ onResult: (result) => settle.resolve(result),
346
+ onStreamError: (err) => settle.reject(err)
347
+ }
348
+ );
349
+ sendId = this.nextId - 1;
350
+ await ack;
351
+ return {
352
+ wait: () => waited,
353
+ cancel: async () => {
354
+ if (sendId === void 0) return;
355
+ await this.request({ op: "cancel", sendId }).catch(() => {
356
+ });
357
+ }
358
+ };
359
+ }
360
+ /** Kill the child and reject anything in flight. */
361
+ dispose() {
362
+ this.disposed = true;
363
+ this.failAll(new Error("cursor sidecar client disposed"));
364
+ this.reader?.close();
365
+ this.reader = void 0;
366
+ this.child?.kill();
367
+ this.child = void 0;
368
+ }
369
+ };
370
+
371
+ // src/provider/agent-backend.ts
372
+ function resolveBackendKind(env) {
373
+ const override = process.env["OPENCODE_CURSOR_SIDECAR"];
374
+ if (override === "0" || override === "false") return "in-process";
375
+ if (override === "1" || override === "true") return env.nodePath ? "sidecar" : "in-process";
376
+ return env.isBun && env.nodePath ? "sidecar" : "in-process";
377
+ }
378
+ function detectNode() {
379
+ try {
380
+ const out = execSync(process.platform === "win32" ? "where node" : "command -v node", {
381
+ encoding: "utf8",
382
+ stdio: ["ignore", "pipe", "ignore"]
383
+ }).trim();
384
+ return out.split("\n")[0] || void 0;
385
+ } catch {
386
+ return void 0;
387
+ }
388
+ }
389
+ function detectEnvironment() {
390
+ const isBun = typeof globalThis.Bun !== "undefined";
391
+ const needsNode = isBun || process.env["OPENCODE_CURSOR_SIDECAR"] === "1";
392
+ return { isBun, nodePath: needsNode ? detectNode() : process.execPath };
393
+ }
394
+ function inProcessBackend() {
395
+ return {
396
+ kind: "in-process",
397
+ createAgent: async (options) => {
398
+ const { Agent } = await loadCursorSdk();
399
+ return await Agent.create(options);
400
+ },
401
+ resumeAgent: async (agentId, options) => {
402
+ const { Agent } = await loadCursorSdk();
403
+ return await Agent.resume(agentId, options);
404
+ }
405
+ };
406
+ }
407
+ function resolveSidecarScript() {
408
+ const candidates = [
409
+ "./sidecar/agent-host.js",
410
+ // importer is a chunk at dist root
411
+ "../sidecar/agent-host.js",
412
+ // importer is dist/provider/index.js
413
+ "../sidecar/agent-host.mjs"
414
+ // importer is src/provider/*.ts (dev/tests)
415
+ ];
416
+ for (const candidate of candidates) {
417
+ const path = fileURLToPath(new URL(candidate, import.meta.url));
418
+ if (existsSync(path)) return path;
419
+ }
420
+ return void 0;
421
+ }
422
+ function sidecarBackend(nodePath, scriptPath) {
423
+ const client = new SidecarClient({ scriptPath, nodePath });
424
+ return {
425
+ kind: "sidecar",
426
+ createAgent: (options) => client.createAgent(options),
427
+ resumeAgent: (agentId, options) => client.resumeAgent(agentId, options)
428
+ };
429
+ }
430
+ var cached2;
431
+ function loadAgentBackend() {
432
+ if (!cached2) {
433
+ const env = detectEnvironment();
434
+ const kind = resolveBackendKind(env);
435
+ const scriptPath = kind === "sidecar" ? resolveSidecarScript() : void 0;
436
+ const override = process.env["OPENCODE_CURSOR_SIDECAR"];
437
+ const optedOut = override === "0" || override === "false";
438
+ if (env.isBun && !optedOut && (kind === "in-process" || !scriptPath)) {
439
+ console.error(
440
+ `[opencode-cursor] Running under Bun without a usable Node sidecar (node: ${env.nodePath ?? "not found"}, script: ${scriptPath ?? "not found"}): Cursor native tool calls may fail (Bun node:http2 incompatibility). Install Node.js to enable the sidecar, or set OPENCODE_CURSOR_SIDECAR=0 to silence this warning.`
441
+ );
442
+ }
443
+ cached2 = kind === "sidecar" && env.nodePath && scriptPath ? sidecarBackend(env.nodePath, scriptPath) : inProcessBackend();
444
+ }
445
+ return cached2;
446
+ }
447
+
448
+ // src/provider/session-pool.ts
449
+ var pool = /* @__PURE__ */ new Map();
450
+ async function acquireAgent(params) {
451
+ const backend = loadAgentBackend();
452
+ const createOptions = {
453
+ apiKey: params.apiKey,
454
+ model: params.modelSelection,
455
+ mode: params.mode,
456
+ local: {
457
+ cwd: params.cwd,
458
+ ...params.settingSources ? { settingSources: params.settingSources } : {},
459
+ ...params.sandbox !== void 0 ? { sandboxOptions: { enabled: params.sandbox } } : {}
460
+ },
461
+ ...params.mcpServers ? { mcpServers: params.mcpServers } : {},
462
+ ...params.agents ? { agents: params.agents } : {},
463
+ ...params.name ? { name: params.name } : {}
464
+ };
465
+ const pooling = params.session && Boolean(params.sessionID);
466
+ const pooledId = pooling ? pool.get(params.sessionID) : void 0;
467
+ const resumeId = params.agentId ?? pooledId;
468
+ let agent;
469
+ let resumed = false;
470
+ if (resumeId) {
471
+ try {
472
+ agent = await backend.resumeAgent(resumeId, createOptions);
473
+ resumed = true;
474
+ } catch {
475
+ if (pooledId && resumeId === pooledId) pool.delete(params.sessionID);
476
+ }
477
+ }
478
+ if (!agent) {
479
+ agent = await backend.createAgent(createOptions);
480
+ }
481
+ if (pooling) pool.set(params.sessionID, agent.agentId);
482
+ const release = () => {
483
+ if (!pooling) {
484
+ try {
485
+ agent.close();
486
+ } catch {
487
+ }
488
+ }
489
+ };
490
+ return { agent, resumed, release };
491
+ }
492
+
493
+ export {
494
+ resolveCursorApiKey,
495
+ fingerprintApiKey,
496
+ streamAgentTurn,
497
+ buildModelSelection,
498
+ resolveControls,
499
+ loadCursorSdk,
500
+ acquireAgent
501
+ };
502
+ //# sourceMappingURL=chunk-YYO6O43T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api-key.ts","../src/provider/agent-events.ts","../src/provider/controls.ts","../src/cursor-runtime.ts","../src/provider/agent-backend.ts","../src/provider/sidecar-client.ts","../src/provider/session-pool.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\n/** Environment variable the Cursor SDK itself reads as a fallback. */\nexport const CURSOR_API_KEY_ENV_VAR = \"CURSOR_API_KEY\";\n\n/**\n * Values that are *not* real keys but rather instructions to read the key from\n * the environment. opencode config commonly stores literal `{env:...}` style\n * placeholders, and users sometimes paste the variable name itself.\n */\nconst PLACEHOLDERS = new Set<string>([\n CURSOR_API_KEY_ENV_VAR,\n `$${CURSOR_API_KEY_ENV_VAR}`,\n `\\${${CURSOR_API_KEY_ENV_VAR}}`,\n]);\n\n/**\n * Resolve a usable Cursor API key.\n *\n * Resolution order: an explicit, non-placeholder candidate (e.g. from opencode\n * auth storage or provider options) wins; otherwise fall back to the\n * `CURSOR_API_KEY` environment variable. Returns `undefined` when no key is\n * available so callers can present a clear \"needs auth\" path.\n *\n * The key is never logged or persisted by this module.\n */\nexport function resolveCursorApiKey(candidate?: string | null): string | undefined {\n const trimmed = candidate?.trim();\n if (trimmed && !PLACEHOLDERS.has(trimmed)) return trimmed;\n const fromEnv = process.env[CURSOR_API_KEY_ENV_VAR]?.trim();\n return fromEnv ? fromEnv : undefined;\n}\n\n/**\n * Produce a short, non-reversible fingerprint of an API key. Used purely to key\n * the on-disk model cache so the cache invalidates when the key changes. The\n * raw key is never written to disk.\n */\nexport function fingerprintApiKey(apiKey: string): string {\n return createHash(\"sha256\").update(apiKey).digest(\"hex\").slice(0, 16);\n}\n","import type { AgentModeOption, SDKUserMessage } from \"@cursor/sdk\";\nimport type { AgentLike, AgentRunLike } from \"./agent-backend.js\";\n\n/** Token usage as reported by Cursor's `turn-ended` update. */\nexport interface CursorUsage {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n}\n\n/** Normalized events bridged from the Cursor SDK's push callbacks. */\nexport type CursorEvent =\n | { type: \"text-delta\"; text: string }\n | { type: \"reasoning-delta\"; text: string }\n | { type: \"tool-call\"; id: string; name: string; input: unknown }\n | { type: \"tool-result\"; id: string; name: string; result: unknown; isError: boolean }\n | { type: \"usage\"; usage: CursorUsage }\n | { type: \"finish\"; text?: string };\n\nexport interface StreamAgentTurnOptions {\n mode: AgentModeOption;\n abortSignal?: AbortSignal;\n}\n\n/**\n * Human-readable name for a Cursor tool call. Most Cursor tools carry their\n * name in `toolCall.type` (shell/read/edit/…), but an MCP tool call has\n * `type: \"mcp\"` with the real tool in `args.toolName` (and server in\n * `args.providerIdentifier`) — surface that instead of the literal \"mcp\".\n */\nfunction toolDisplayName(toolCall: ({ type?: string } & Record<string, any>) | undefined): string {\n if (!toolCall) return \"tool\";\n if (toolCall.type === \"mcp\") {\n const name = toolCall.args?.toolName;\n const server = toolCall.args?.providerIdentifier;\n if (name) return server ? `${server}/${name}` : String(name);\n return \"mcp\";\n }\n return toolCall.type ?? \"tool\";\n}\n\n/**\n * Stream a single turn on an already-acquired Cursor agent and yield normalized\n * events. The agent's lifecycle (create/resume/close) is owned by the caller\n * (see session-pool.ts) so it can be reused across turns. The SDK streams via\n * `onDelta` callbacks; we bridge those into a pull-based async generator so both\n * `doStream` and `doGenerate` can consume them.\n */\nexport async function* streamAgentTurn(\n agent: AgentLike,\n message: SDKUserMessage,\n options: StreamAgentTurnOptions,\n): AsyncGenerator<CursorEvent> {\n const queue: CursorEvent[] = [];\n let wake: (() => void) | undefined;\n let finished = false;\n let failure: unknown;\n\n // Opt-in stderr tracing of what the live agent emits (set OPENCODE_CURSOR_DEBUG=1).\n const debug = process.env.OPENCODE_CURSOR_DEBUG === \"1\";\n const counts: Record<string, number> = {};\n\n const push = (event: CursorEvent) => {\n queue.push(event);\n wake?.();\n wake = undefined;\n };\n\n const onDelta = ({ update }: { update: { type: string } & Record<string, any> }) => {\n if (debug) counts[update.type] = (counts[update.type] ?? 0) + 1;\n switch (update.type) {\n case \"text-delta\":\n push({ type: \"text-delta\", text: update.text });\n break;\n case \"thinking-delta\":\n push({ type: \"reasoning-delta\", text: update.text });\n break;\n case \"tool-call-started\":\n push({\n type: \"tool-call\",\n id: String(update.callId),\n name: toolDisplayName(update.toolCall),\n input: update.toolCall?.args ?? {},\n });\n break;\n case \"tool-call-completed\": {\n const tool = update.toolCall ?? {};\n const result = tool.result;\n // MCP failures often arrive as {status:\"success\", value:{isError:true}}\n // (the MCP-protocol error flag), not as a top-level status error.\n const mcpError = tool.type === \"mcp\" && result?.value?.isError === true;\n push({\n type: \"tool-result\",\n id: String(update.callId),\n name: toolDisplayName(tool),\n result: result ?? null,\n isError: result?.status === \"error\" || mcpError,\n });\n break;\n }\n case \"turn-ended\":\n if (update.usage) push({ type: \"usage\", usage: update.usage as CursorUsage });\n break;\n }\n };\n\n const runHolder: { run?: AgentRunLike } = {};\n const onAbort = () => {\n void Promise.resolve(runHolder.run?.cancel()).catch(() => {});\n };\n options.abortSignal?.addEventListener(\"abort\", onAbort);\n\n // A previous opencode/CLI crash (or a second instance racing on the same\n // agent store) can leave a persisted run wedged; the SDK then rejects new\n // sends with AgentBusyError. Retry once with the SDK's documented recovery\n // path (local.force expires the wedged run) instead of failing the turn.\n const sendTurn = async (): Promise<AgentRunLike> => {\n try {\n return await agent.send(message, { mode: options.mode, onDelta });\n } catch (err) {\n if (err instanceof Error && err.name === \"AgentBusyError\") {\n if (debug) console.error(\"[cursor:debug] agent busy; retrying send with local.force\");\n return agent.send(message, { mode: options.mode, onDelta, local: { force: true } });\n }\n throw err;\n }\n };\n\n // Kick off the turn. Resolve text from run.wait() for models that don't emit\n // incremental text deltas.\n void sendTurn()\n .then(async (run) => {\n runHolder.run = run;\n const result = await run.wait();\n if (debug) {\n console.error(\n `[cursor:debug] updates=${JSON.stringify(counts)} status=${result.status} resultLen=${(result.result ?? \"\").length}`,\n );\n }\n if (result.status === \"error\") {\n // Surface the failure instead of finishing silently — a silent stop\n // leaves opencode showing dangling tool calls with no explanation.\n throw new Error(\n `Cursor run ended with status \"error\"${result.result ? `: ${result.result}` : \"\"}`,\n );\n }\n // A cancelled run finishes without fabricating final text.\n push({ type: \"finish\", ...(result.status === \"cancelled\" ? {} : { text: result.result }) });\n })\n .catch((err) => {\n failure = err;\n if (debug) console.error(`[cursor:debug] send failed: ${err instanceof Error ? err.message : String(err)}`);\n })\n .finally(() => {\n finished = true;\n wake?.();\n wake = undefined;\n });\n\n try {\n while (true) {\n if (queue.length > 0) {\n yield queue.shift()!;\n continue;\n }\n if (finished) break;\n await new Promise<void>((resolve) => {\n wake = resolve;\n });\n }\n // Drain anything queued right before completion.\n while (queue.length > 0) yield queue.shift()!;\n if (failure) throw failure;\n } finally {\n options.abortSignal?.removeEventListener(\"abort\", onAbort);\n }\n}\n","import type { AgentModeOption, ModelSelection } from \"@cursor/sdk\";\n\n/** Per-model static control defaults (from provider/model config options). */\nexport interface StaticControls {\n mode: AgentModeOption;\n /** Default Cursor model params (id -> value), e.g. { thinking: \"high\" }. */\n params?: Record<string, string>;\n}\n\nexport interface ResolvedControls {\n mode: AgentModeOption;\n modelSelection: ModelSelection;\n}\n\n/**\n * Build a Cursor `ModelSelection` from a model id and an optional map of model\n * params (e.g. `{ thinking: \"high\" }`). Shared by the provider control\n * resolution and the cloud/delegate tools so param handling stays consistent.\n */\nexport function buildModelSelection(\n modelId: string,\n params?: Record<string, string>,\n): ModelSelection {\n const paramList = Object.entries(params ?? {}).map(([id, value]) => ({ id, value }));\n return paramList.length > 0 ? { id: modelId, params: paramList } : { id: modelId };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isMode(value: unknown): value is AgentModeOption {\n return value === \"agent\" || value === \"plan\";\n}\n\n/**\n * Resolve the per-turn Cursor controls from static config plus opencode's\n * per-request `providerOptions.cursor` (which carries merged model `options` and\n * the selected model `variant`). Per-request values win over static defaults.\n *\n * Recognized keys in `providerOptions.cursor`:\n * - `mode`: \"agent\" | \"plan\"\n * - `params`: Record<string,string> of Cursor model params (e.g. { thinking: \"high\" })\n * - `thinking`: string convenience, mapped to the `thinking` param if not already set\n */\nexport function resolveControls(\n modelId: string,\n staticControls: StaticControls,\n providerOptions: Record<string, unknown> | undefined,\n): ResolvedControls {\n const po = providerOptions ?? {};\n\n const mode: AgentModeOption = isMode(po[\"mode\"]) ? po[\"mode\"] : staticControls.mode;\n\n const params: Record<string, string> = { ...(staticControls.params ?? {}) };\n if (isRecord(po[\"params\"])) {\n for (const [key, value] of Object.entries(po[\"params\"])) {\n if (value != null) params[key] = String(value);\n }\n }\n if (typeof po[\"thinking\"] === \"string\" && params[\"thinking\"] === undefined) {\n params[\"thinking\"] = po[\"thinking\"];\n }\n\n return { mode, modelSelection: buildModelSelection(modelId, params) };\n}\n","/**\n * Lazy loader for the official Cursor SDK (`@cursor/sdk`).\n *\n * The SDK is heavy and only needed once a Cursor model is actually used or\n * models are discovered, so it is imported on demand. A failed import (e.g. the\n * dependency is missing) degrades gracefully into a clear error instead of\n * crashing opencode at startup.\n */\nexport type CursorSdkModule = typeof import(\"@cursor/sdk\");\n\nlet cached: Promise<CursorSdkModule> | undefined;\n\nexport async function loadCursorSdk(): Promise<CursorSdkModule> {\n if (!cached) {\n cached = import(\"@cursor/sdk\").catch((err: unknown) => {\n // Allow a later retry if the failure was transient.\n cached = undefined;\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(\n `[opencode-cursor] Failed to load \"@cursor/sdk\". Make sure it is installed ` +\n `(\\`npm install @cursor/sdk\\`). Original error: ${detail}`,\n );\n });\n }\n return cached;\n}\n","/**\n * Selects where Cursor agents run:\n *\n * - \"in-process\": straight through `@cursor/sdk` in this process (Node — the\n * normal path for tests, scripts, and any non-Bun host).\n * - \"sidecar\": a spawned Node child hosting the SDK (Bun — opencode's runtime —\n * has a `node:http2` bug that kills Cursor's streaming RPC with\n * NGHTTP2_FRAME_SIZE_ERROR, losing tool-completion updates; see\n * src/sidecar/agent-host.mjs).\n *\n * Override with OPENCODE_CURSOR_SIDECAR=1/0 (force on/off).\n */\nimport { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { loadCursorSdk } from \"../cursor-runtime.js\";\nimport { SidecarClient, type AgentLike } from \"./sidecar-client.js\";\n\nexport type { AgentLike, AgentRunLike, AgentSendOptions } from \"./sidecar-client.js\";\n\nexport type BackendKind = \"in-process\" | \"sidecar\";\n\nexport interface AgentBackend {\n kind: BackendKind;\n createAgent(options: unknown): Promise<AgentLike>;\n resumeAgent(agentId: string, options: unknown): Promise<AgentLike>;\n}\n\nexport interface BackendEnvironment {\n isBun: boolean;\n /** Resolved node executable, or undefined when not on PATH. */\n nodePath: string | undefined;\n}\n\n/** Pure selection logic (unit-testable without spawning anything). */\nexport function resolveBackendKind(env: BackendEnvironment): BackendKind {\n const override = process.env[\"OPENCODE_CURSOR_SIDECAR\"];\n if (override === \"0\" || override === \"false\") return \"in-process\";\n if (override === \"1\" || override === \"true\") return env.nodePath ? \"sidecar\" : \"in-process\";\n return env.isBun && env.nodePath ? \"sidecar\" : \"in-process\";\n}\n\nfunction detectNode(): string | undefined {\n try {\n const out = execSync(process.platform === \"win32\" ? \"where node\" : \"command -v node\", {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n return out.split(\"\\n\")[0] || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction detectEnvironment(): BackendEnvironment {\n const isBun = typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\";\n // Only pay the PATH lookup when the answer can matter.\n const needsNode = isBun || process.env[\"OPENCODE_CURSOR_SIDECAR\"] === \"1\";\n return { isBun, nodePath: needsNode ? detectNode() : process.execPath };\n}\n\nfunction inProcessBackend(): AgentBackend {\n return {\n kind: \"in-process\",\n createAgent: async (options) => {\n const { Agent } = await loadCursorSdk();\n return (await Agent.create(options as never)) as unknown as AgentLike;\n },\n resumeAgent: async (agentId, options) => {\n const { Agent } = await loadCursorSdk();\n return (await Agent.resume(agentId, options as never)) as unknown as AgentLike;\n },\n };\n}\n\n/**\n * Locate the sidecar script across layouts: tsup may place this module in\n * dist/provider/index.js or hoist it into a root-level dist/chunk-*.js, and in\n * dev/tests it runs straight from src/. Try each known relative position.\n */\nexport function resolveSidecarScript(): string | undefined {\n const candidates = [\n \"./sidecar/agent-host.js\", // importer is a chunk at dist root\n \"../sidecar/agent-host.js\", // importer is dist/provider/index.js\n \"../sidecar/agent-host.mjs\", // importer is src/provider/*.ts (dev/tests)\n ];\n for (const candidate of candidates) {\n const path = fileURLToPath(new URL(candidate, import.meta.url));\n if (existsSync(path)) return path;\n }\n return undefined;\n}\n\nfunction sidecarBackend(nodePath: string, scriptPath: string): AgentBackend {\n const client = new SidecarClient({ scriptPath, nodePath });\n return {\n kind: \"sidecar\",\n createAgent: (options) => client.createAgent(options),\n resumeAgent: (agentId, options) => client.resumeAgent(agentId, options),\n };\n}\n\nlet cached: AgentBackend | undefined;\n\n/** Resolve (and cache) the agent backend for this process. */\nexport function loadAgentBackend(): AgentBackend {\n if (!cached) {\n const env = detectEnvironment();\n const kind = resolveBackendKind(env);\n const scriptPath = kind === \"sidecar\" ? resolveSidecarScript() : undefined;\n // A user who explicitly opted out (OPENCODE_CURSOR_SIDECAR=0/false) has\n // accepted the in-process behavior and should not be warned.\n const override = process.env[\"OPENCODE_CURSOR_SIDECAR\"];\n const optedOut = override === \"0\" || override === \"false\";\n if (env.isBun && !optedOut && (kind === \"in-process\" || !scriptPath)) {\n console.error(\n \"[opencode-cursor] Running under Bun without a usable Node sidecar \" +\n `(node: ${env.nodePath ?? \"not found\"}, script: ${scriptPath ?? \"not found\"}): ` +\n \"Cursor native tool calls may fail (Bun node:http2 incompatibility). \" +\n \"Install Node.js to enable the sidecar, or set OPENCODE_CURSOR_SIDECAR=0 \" +\n \"to silence this warning.\",\n );\n }\n cached =\n kind === \"sidecar\" && env.nodePath && scriptPath\n ? sidecarBackend(env.nodePath, scriptPath)\n : inProcessBackend();\n }\n return cached;\n}\n\n/** Test hook. */\nexport function resetAgentBackend(): void {\n cached = undefined;\n}\n","/**\n * Client half of the Node sidecar (see src/sidecar/agent-host.mjs for the\n * protocol and the why). Spawns one Node child per client and multiplexes\n * agent create/resume/send/cancel/close requests over JSON-lines stdio,\n * exposing agents through the same minimal surface the provider already\n * consumes ({@link AgentLike}), so session-pool/agent-events need no\n * sidecar-specific logic.\n */\nimport { spawn, type ChildProcessByStdio } from \"node:child_process\";\nimport { createInterface, type Interface } from \"node:readline\";\nimport type { Readable, Writable } from \"node:stream\";\n\n/** Minimal run surface the provider consumes (subset of the SDK's Run). */\nexport interface AgentRunLike {\n wait(): Promise<{ status: string; result?: string }>;\n cancel(): void | Promise<void>;\n}\n\nexport interface AgentSendOptions {\n mode?: string;\n onDelta?: (input: { update: Record<string, unknown> & { type: string } }) => void;\n local?: { force?: boolean };\n}\n\n/** Minimal agent surface the provider consumes (subset of the SDK's SDKAgent). */\nexport interface AgentLike {\n agentId: string;\n send(message: unknown, options?: AgentSendOptions): Promise<AgentRunLike>;\n close(): void;\n}\n\nexport interface SidecarClientOptions {\n /** Path to the agent-host script. */\n scriptPath: string;\n /** Node executable; default \"node\" from PATH. */\n nodePath?: string;\n /** Extra environment for the child (merged over process.env). */\n env?: Record<string, string>;\n /** Mirror child stderr to this process (debug aid). */\n debug?: boolean;\n}\n\ninterface Pending {\n resolve: (msg: Record<string, unknown>) => void;\n reject: (err: Error) => void;\n /** Streaming hooks for \"send\" requests. */\n onUpdate?: (update: Record<string, unknown> & { type: string }) => void;\n onResult?: (result: { status: string; result?: string }) => void;\n onStreamError?: (err: Error) => void;\n}\n\nfunction reviveError(error: unknown): Error {\n const e = (error ?? {}) as { name?: string; message?: string };\n const err = new Error(e.message ?? \"sidecar error\");\n if (e.name) err.name = e.name;\n return err;\n}\n\nexport class SidecarClient {\n private readonly options: SidecarClientOptions;\n private child: ChildProcessByStdio<Writable, Readable, Readable> | undefined;\n private reader: Interface | undefined;\n private readonly pending = new Map<number, Pending>();\n private nextId = 1;\n private disposed = false;\n\n constructor(options: SidecarClientOptions) {\n this.options = options;\n }\n\n /** Spawn (or reuse) the child process. */\n private ensureChild(): ChildProcessByStdio<Writable, Readable, Readable> {\n if (this.disposed) throw new Error(\"cursor sidecar client disposed\");\n if (this.child) return this.child;\n\n const child = spawn(this.options.nodePath ?? \"node\", [this.options.scriptPath], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...this.options.env },\n });\n this.child = child;\n\n this.reader = createInterface({ input: child.stdout });\n this.reader.on(\"line\", (line) => this.handleLine(line));\n child.stderr.on(\"data\", (chunk: Buffer) => {\n if (this.options.debug || process.env[\"OPENCODE_CURSOR_DEBUG\"]) {\n process.stderr.write(`[cursor:sidecar] ${chunk}`);\n }\n });\n child.on(\"exit\", (code) => {\n this.failAll(new Error(`cursor sidecar exited (code ${code ?? \"unknown\"})`));\n this.child = undefined;\n this.reader?.close();\n this.reader = undefined;\n });\n child.on(\"error\", (err) => {\n this.failAll(new Error(`cursor sidecar failed to start: ${err.message}`));\n this.child = undefined;\n });\n this.updateRefs();\n return child;\n }\n\n /**\n * Keep the child (and its pipes) from holding the parent's event loop open\n * while idle, but ref it whenever a reply is outstanding so the loop can't\n * exit mid-request. Without this, any process that uses the provider and\n * never dispose()s — scripts, tests, opencode itself on shutdown — hangs.\n */\n private updateRefs(): void {\n const child = this.child;\n if (!child) return;\n const refable = [child, child.stdin, child.stdout, child.stderr] as Array<{\n ref?: () => void;\n unref?: () => void;\n }>;\n if (this.pending.size > 0) {\n for (const target of refable) target.ref?.();\n } else {\n for (const target of refable) target.unref?.();\n }\n }\n\n private failAll(err: Error): void {\n for (const pending of this.pending.values()) {\n pending.onStreamError?.(err);\n pending.reject(err);\n }\n this.pending.clear();\n this.updateRefs();\n }\n\n private handleLine(line: string): void {\n if (!line.trim()) return;\n let msg: Record<string, unknown>;\n try {\n msg = JSON.parse(line) as Record<string, unknown>;\n } catch {\n return; // ignore non-protocol noise on stdout\n }\n const id = msg[\"id\"];\n if (typeof id !== \"number\") return;\n const pending = this.pending.get(id);\n if (!pending) return;\n\n const ev = msg[\"ev\"];\n if (ev === \"update\") {\n pending.onUpdate?.(msg[\"update\"] as Record<string, unknown> & { type: string });\n return;\n }\n if (ev === \"result\") {\n this.pending.delete(id);\n this.updateRefs();\n pending.onResult?.(msg[\"result\"] as { status: string; result?: string });\n return;\n }\n if (ev === \"error\") {\n this.pending.delete(id);\n this.updateRefs();\n pending.onStreamError?.(reviveError(msg[\"error\"]));\n return;\n }\n\n if (msg[\"ok\"] === true) {\n // \"send\" acks stay pending for their streaming terminal event.\n if (!pending.onResult) {\n this.pending.delete(id);\n this.updateRefs();\n }\n pending.resolve(msg);\n } else {\n this.pending.delete(id);\n this.updateRefs();\n pending.reject(reviveError(msg[\"error\"]));\n }\n }\n\n private request(\n payload: Record<string, unknown>,\n hooks?: Pick<Pending, \"onUpdate\" | \"onResult\" | \"onStreamError\">,\n ): Promise<Record<string, unknown>> {\n const child = this.ensureChild();\n const id = this.nextId++;\n return new Promise<Record<string, unknown>>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, ...hooks });\n this.updateRefs();\n child.stdin.write(`${JSON.stringify({ id, ...payload })}\\n`, (err) => {\n if (err) {\n this.pending.delete(id);\n this.updateRefs();\n reject(err);\n }\n });\n });\n }\n\n async createAgent(options: unknown): Promise<AgentLike> {\n const res = await this.request({ op: \"create\", options });\n return this.wrapAgent(String(res[\"agentId\"]));\n }\n\n async resumeAgent(agentId: string, options: unknown): Promise<AgentLike> {\n const res = await this.request({ op: \"resume\", agentId, options });\n return this.wrapAgent(String(res[\"agentId\"]));\n }\n\n private wrapAgent(agentId: string): AgentLike {\n return {\n agentId,\n send: (message, options) => this.sendTurn(agentId, message, options),\n close: () => {\n void this.request({ op: \"close\", agentId }).catch(() => {\n // best effort, mirrors SDKAgent.close()\n });\n },\n };\n }\n\n private async sendTurn(\n agentId: string,\n message: unknown,\n options?: AgentSendOptions,\n ): Promise<AgentRunLike> {\n let settle!: {\n resolve: (r: { status: string; result?: string }) => void;\n reject: (e: Error) => void;\n };\n const waited = new Promise<{ status: string; result?: string }>((resolve, reject) => {\n settle = { resolve, reject };\n });\n // Avoid unhandled-rejection noise when the consumer never calls wait().\n waited.catch(() => {});\n\n let sendId: number | undefined;\n const ack = this.request(\n {\n op: \"send\",\n agentId,\n message,\n ...(options?.mode ? { mode: options.mode } : {}),\n ...(options?.local?.force ? { force: true } : {}),\n },\n {\n onUpdate: (update) => options?.onDelta?.({ update }),\n onResult: (result) => settle.resolve(result),\n onStreamError: (err) => settle.reject(err),\n },\n );\n // The request id is allocated synchronously inside request(); capture it\n // for cancel by reading the id we just used.\n sendId = this.nextId - 1;\n\n await ack;\n return {\n wait: () => waited,\n cancel: async () => {\n if (sendId === undefined) return;\n await this.request({ op: \"cancel\", sendId }).catch(() => {});\n },\n };\n }\n\n /** Kill the child and reject anything in flight. */\n dispose(): void {\n this.disposed = true;\n this.failAll(new Error(\"cursor sidecar client disposed\"));\n this.reader?.close();\n this.reader = undefined;\n this.child?.kill();\n this.child = undefined;\n }\n}\n","import type {\n AgentDefinition,\n AgentModeOption,\n McpServerConfig,\n ModelSelection,\n SettingSource,\n} from \"@cursor/sdk\";\nimport { loadAgentBackend, type AgentLike } from \"./agent-backend.js\";\n\n/** sessionID -> Cursor agentId, so a session reuses one Cursor agent across turns. */\nconst pool = new Map<string, string>();\n\n/** Test/diagnostic helpers. */\nexport function getPooledAgentId(sessionID: string): string | undefined {\n return pool.get(sessionID);\n}\nexport function clearAgentPool(): void {\n pool.clear();\n}\n\nexport interface AcquireAgentParams {\n apiKey: string;\n modelSelection: ModelSelection;\n mode: AgentModeOption;\n cwd: string;\n settingSources?: SettingSource[];\n sandbox?: boolean;\n mcpServers?: Record<string, McpServerConfig>;\n agents?: Record<string, AgentDefinition>;\n name?: string;\n /** opencode session id; required for pooling. */\n sessionID?: string;\n /** When true (and sessionID present) reuse/resume one agent per session. */\n session: boolean;\n /**\n * Resume a specific Cursor agent by id. Takes precedence over session\n * pooling; lets power users continue an explicit agent (e.g. one returned by\n * a prior tool call) rather than the session's auto-managed one.\n */\n agentId?: string;\n}\n\nexport interface AcquiredAgent {\n agent: AgentLike;\n /** True when an existing pooled agent was resumed (send only the new turn). */\n resumed: boolean;\n /** Close the agent unless it's pooled (pooled agents persist for the next turn). */\n release: () => void;\n}\n\n/**\n * Get an agent to run a turn: resume the session's pooled agent when possible,\n * otherwise create a fresh one. Resume failures fall back to creation, so a\n * stale/expired pool entry degrades to a correct fresh turn rather than an error.\n */\nexport async function acquireAgent(params: AcquireAgentParams): Promise<AcquiredAgent> {\n const backend = loadAgentBackend();\n\n const createOptions = {\n apiKey: params.apiKey,\n model: params.modelSelection,\n mode: params.mode,\n local: {\n cwd: params.cwd,\n ...(params.settingSources ? { settingSources: params.settingSources } : {}),\n ...(params.sandbox !== undefined ? { sandboxOptions: { enabled: params.sandbox } } : {}),\n },\n ...(params.mcpServers ? { mcpServers: params.mcpServers } : {}),\n ...(params.agents ? { agents: params.agents } : {}),\n ...(params.name ? { name: params.name } : {}),\n };\n\n const pooling = params.session && Boolean(params.sessionID);\n const pooledId = pooling ? pool.get(params.sessionID!) : undefined;\n // An explicit agentId wins over the session's pooled agent.\n const resumeId = params.agentId ?? pooledId;\n\n let agent: AgentLike | undefined;\n let resumed = false;\n if (resumeId) {\n try {\n agent = await backend.resumeAgent(resumeId, createOptions);\n resumed = true;\n } catch {\n // A stale/expired id degrades to a fresh agent; drop a matching pool entry.\n if (pooledId && resumeId === pooledId) pool.delete(params.sessionID!);\n }\n }\n if (!agent) {\n agent = await backend.createAgent(createOptions);\n }\n\n if (pooling) pool.set(params.sessionID!, agent.agentId);\n\n const release = () => {\n if (!pooling) {\n try {\n agent!.close();\n } catch {\n // best effort\n }\n }\n };\n\n return { agent, resumed, release };\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAGpB,IAAM,yBAAyB;AAOtC,IAAM,eAAe,oBAAI,IAAY;AAAA,EACnC;AAAA,EACA,IAAI,sBAAsB;AAAA,EAC1B,MAAM,sBAAsB;AAC9B,CAAC;AAYM,SAAS,oBAAoB,WAA+C;AACjF,QAAM,UAAU,WAAW,KAAK;AAChC,MAAI,WAAW,CAAC,aAAa,IAAI,OAAO,EAAG,QAAO;AAClD,QAAM,UAAU,QAAQ,IAAI,sBAAsB,GAAG,KAAK;AAC1D,SAAO,UAAU,UAAU;AAC7B;AAOO,SAAS,kBAAkB,QAAwB;AACxD,SAAO,WAAW,QAAQ,EAAE,OAAO,MAAM,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AACtE;;;ACTA,SAAS,gBAAgB,UAAyE;AAChG,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,SAAS,SAAS,OAAO;AAC3B,UAAM,OAAO,SAAS,MAAM;AAC5B,UAAM,SAAS,SAAS,MAAM;AAC9B,QAAI,KAAM,QAAO,SAAS,GAAG,MAAM,IAAI,IAAI,KAAK,OAAO,IAAI;AAC3D,WAAO;AAAA,EACT;AACA,SAAO,SAAS,QAAQ;AAC1B;AASA,gBAAuB,gBACrB,OACA,SACA,SAC6B;AAC7B,QAAM,QAAuB,CAAC;AAC9B,MAAI;AACJ,MAAI,WAAW;AACf,MAAI;AAGJ,QAAM,QAAQ,QAAQ,IAAI,0BAA0B;AACpD,QAAM,SAAiC,CAAC;AAExC,QAAM,OAAO,CAAC,UAAuB;AACnC,UAAM,KAAK,KAAK;AAChB,WAAO;AACP,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,CAAC,EAAE,OAAO,MAA0D;AAClF,QAAI,MAAO,QAAO,OAAO,IAAI,KAAK,OAAO,OAAO,IAAI,KAAK,KAAK;AAC9D,YAAQ,OAAO,MAAM;AAAA,MACnB,KAAK;AACH,aAAK,EAAE,MAAM,cAAc,MAAM,OAAO,KAAK,CAAC;AAC9C;AAAA,MACF,KAAK;AACH,aAAK,EAAE,MAAM,mBAAmB,MAAM,OAAO,KAAK,CAAC;AACnD;AAAA,MACF,KAAK;AACH,aAAK;AAAA,UACH,MAAM;AAAA,UACN,IAAI,OAAO,OAAO,MAAM;AAAA,UACxB,MAAM,gBAAgB,OAAO,QAAQ;AAAA,UACrC,OAAO,OAAO,UAAU,QAAQ,CAAC;AAAA,QACnC,CAAC;AACD;AAAA,MACF,KAAK,uBAAuB;AAC1B,cAAM,OAAO,OAAO,YAAY,CAAC;AACjC,cAAM,SAAS,KAAK;AAGpB,cAAM,WAAW,KAAK,SAAS,SAAS,QAAQ,OAAO,YAAY;AACnE,aAAK;AAAA,UACH,MAAM;AAAA,UACN,IAAI,OAAO,OAAO,MAAM;AAAA,UACxB,MAAM,gBAAgB,IAAI;AAAA,UAC1B,QAAQ,UAAU;AAAA,UAClB,SAAS,QAAQ,WAAW,WAAW;AAAA,QACzC,CAAC;AACD;AAAA,MACF;AAAA,MACA,KAAK;AACH,YAAI,OAAO,MAAO,MAAK,EAAE,MAAM,SAAS,OAAO,OAAO,MAAqB,CAAC;AAC5E;AAAA,IACJ;AAAA,EACF;AAEA,QAAM,YAAoC,CAAC;AAC3C,QAAM,UAAU,MAAM;AACpB,SAAK,QAAQ,QAAQ,UAAU,KAAK,OAAO,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAC9D;AACA,UAAQ,aAAa,iBAAiB,SAAS,OAAO;AAMtD,QAAM,WAAW,YAAmC;AAClD,QAAI;AACF,aAAO,MAAM,MAAM,KAAK,SAAS,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,IAClE,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,SAAS,kBAAkB;AACzD,YAAI,MAAO,SAAQ,MAAM,2DAA2D;AACpF,eAAO,MAAM,KAAK,SAAS,EAAE,MAAM,QAAQ,MAAM,SAAS,OAAO,EAAE,OAAO,KAAK,EAAE,CAAC;AAAA,MACpF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAIA,OAAK,SAAS,EACX,KAAK,OAAO,QAAQ;AACnB,cAAU,MAAM;AAChB,UAAM,SAAS,MAAM,IAAI,KAAK;AAC9B,QAAI,OAAO;AACT,cAAQ;AAAA,QACN,0BAA0B,KAAK,UAAU,MAAM,CAAC,WAAW,OAAO,MAAM,eAAe,OAAO,UAAU,IAAI,MAAM;AAAA,MACpH;AAAA,IACF;AACA,QAAI,OAAO,WAAW,SAAS;AAG7B,YAAM,IAAI;AAAA,QACR,uCAAuC,OAAO,SAAS,KAAK,OAAO,MAAM,KAAK,EAAE;AAAA,MAClF;AAAA,IACF;AAEA,SAAK,EAAE,MAAM,UAAU,GAAI,OAAO,WAAW,cAAc,CAAC,IAAI,EAAE,MAAM,OAAO,OAAO,EAAG,CAAC;AAAA,EAC5F,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,cAAU;AACV,QAAI,MAAO,SAAQ,MAAM,+BAA+B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,EAC5G,CAAC,EACA,QAAQ,MAAM;AACb,eAAW;AACX,WAAO;AACP,WAAO;AAAA,EACT,CAAC;AAEH,MAAI;AACF,WAAO,MAAM;AACX,UAAI,MAAM,SAAS,GAAG;AACpB,cAAM,MAAM,MAAM;AAClB;AAAA,MACF;AACA,UAAI,SAAU;AACd,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAEA,WAAO,MAAM,SAAS,EAAG,OAAM,MAAM,MAAM;AAC3C,QAAI,QAAS,OAAM;AAAA,EACrB,UAAE;AACA,YAAQ,aAAa,oBAAoB,SAAS,OAAO;AAAA,EAC3D;AACF;;;AC9JO,SAAS,oBACd,SACA,QACgB;AAChB,QAAM,YAAY,OAAO,QAAQ,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,IAAI,MAAM,EAAE;AACnF,SAAO,UAAU,SAAS,IAAI,EAAE,IAAI,SAAS,QAAQ,UAAU,IAAI,EAAE,IAAI,QAAQ;AACnF;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,OAAO,OAA0C;AACxD,SAAO,UAAU,WAAW,UAAU;AACxC;AAYO,SAAS,gBACd,SACA,gBACA,iBACkB;AAClB,QAAM,KAAK,mBAAmB,CAAC;AAE/B,QAAM,OAAwB,OAAO,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,IAAI,eAAe;AAE/E,QAAM,SAAiC,EAAE,GAAI,eAAe,UAAU,CAAC,EAAG;AAC1E,MAAI,SAAS,GAAG,QAAQ,CAAC,GAAG;AAC1B,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,QAAQ,CAAC,GAAG;AACvD,UAAI,SAAS,KAAM,QAAO,GAAG,IAAI,OAAO,KAAK;AAAA,IAC/C;AAAA,EACF;AACA,MAAI,OAAO,GAAG,UAAU,MAAM,YAAY,OAAO,UAAU,MAAM,QAAW;AAC1E,WAAO,UAAU,IAAI,GAAG,UAAU;AAAA,EACpC;AAEA,SAAO,EAAE,MAAM,gBAAgB,oBAAoB,SAAS,MAAM,EAAE;AACtE;;;ACvDA,IAAI;AAEJ,eAAsB,gBAA0C;AAC9D,MAAI,CAAC,QAAQ;AACX,aAAS,OAAO,aAAa,EAAE,MAAM,CAAC,QAAiB;AAErD,eAAS;AACT,YAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,YAAM,IAAI;AAAA,QACR,4HACoD,MAAM;AAAA,MAC5D;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;;;ACbA,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;;;ACN9B,SAAS,aAAuC;AAChD,SAAS,uBAAuC;AA0ChD,SAAS,YAAY,OAAuB;AAC1C,QAAM,IAAK,SAAS,CAAC;AACrB,QAAM,MAAM,IAAI,MAAM,EAAE,WAAW,eAAe;AAClD,MAAI,EAAE,KAAM,KAAI,OAAO,EAAE;AACzB,SAAO;AACT;AAEO,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EACT;AAAA,EACA;AAAA,EACS,UAAU,oBAAI,IAAqB;AAAA,EAC5C,SAAS;AAAA,EACT,WAAW;AAAA,EAEnB,YAAY,SAA+B;AACzC,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGQ,cAAiE;AACvE,QAAI,KAAK,SAAU,OAAM,IAAI,MAAM,gCAAgC;AACnE,QAAI,KAAK,MAAO,QAAO,KAAK;AAE5B,UAAM,QAAQ,MAAM,KAAK,QAAQ,YAAY,QAAQ,CAAC,KAAK,QAAQ,UAAU,GAAG;AAAA,MAC9E,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,MAC9B,KAAK,EAAE,GAAG,QAAQ,KAAK,GAAG,KAAK,QAAQ,IAAI;AAAA,IAC7C,CAAC;AACD,SAAK,QAAQ;AAEb,SAAK,SAAS,gBAAgB,EAAE,OAAO,MAAM,OAAO,CAAC;AACrD,SAAK,OAAO,GAAG,QAAQ,CAAC,SAAS,KAAK,WAAW,IAAI,CAAC;AACtD,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACzC,UAAI,KAAK,QAAQ,SAAS,QAAQ,IAAI,uBAAuB,GAAG;AAC9D,gBAAQ,OAAO,MAAM,oBAAoB,KAAK,EAAE;AAAA,MAClD;AAAA,IACF,CAAC;AACD,UAAM,GAAG,QAAQ,CAAC,SAAS;AACzB,WAAK,QAAQ,IAAI,MAAM,+BAA+B,QAAQ,SAAS,GAAG,CAAC;AAC3E,WAAK,QAAQ;AACb,WAAK,QAAQ,MAAM;AACnB,WAAK,SAAS;AAAA,IAChB,CAAC;AACD,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,WAAK,QAAQ,IAAI,MAAM,mCAAmC,IAAI,OAAO,EAAE,CAAC;AACxE,WAAK,QAAQ;AAAA,IACf,CAAC;AACD,SAAK,WAAW;AAChB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,aAAmB;AACzB,UAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,MAAO;AACZ,UAAM,UAAU,CAAC,OAAO,MAAM,OAAO,MAAM,QAAQ,MAAM,MAAM;AAI/D,QAAI,KAAK,QAAQ,OAAO,GAAG;AACzB,iBAAW,UAAU,QAAS,QAAO,MAAM;AAAA,IAC7C,OAAO;AACL,iBAAW,UAAU,QAAS,QAAO,QAAQ;AAAA,IAC/C;AAAA,EACF;AAAA,EAEQ,QAAQ,KAAkB;AAChC,eAAW,WAAW,KAAK,QAAQ,OAAO,GAAG;AAC3C,cAAQ,gBAAgB,GAAG;AAC3B,cAAQ,OAAO,GAAG;AAAA,IACpB;AACA,SAAK,QAAQ,MAAM;AACnB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEQ,WAAW,MAAoB;AACrC,QAAI,CAAC,KAAK,KAAK,EAAG;AAClB,QAAI;AACJ,QAAI;AACF,YAAM,KAAK,MAAM,IAAI;AAAA,IACvB,QAAQ;AACN;AAAA,IACF;AACA,UAAM,KAAK,IAAI,IAAI;AACnB,QAAI,OAAO,OAAO,SAAU;AAC5B,UAAM,UAAU,KAAK,QAAQ,IAAI,EAAE;AACnC,QAAI,CAAC,QAAS;AAEd,UAAM,KAAK,IAAI,IAAI;AACnB,QAAI,OAAO,UAAU;AACnB,cAAQ,WAAW,IAAI,QAAQ,CAA+C;AAC9E;AAAA,IACF;AACA,QAAI,OAAO,UAAU;AACnB,WAAK,QAAQ,OAAO,EAAE;AACtB,WAAK,WAAW;AAChB,cAAQ,WAAW,IAAI,QAAQ,CAAwC;AACvE;AAAA,IACF;AACA,QAAI,OAAO,SAAS;AAClB,WAAK,QAAQ,OAAO,EAAE;AACtB,WAAK,WAAW;AAChB,cAAQ,gBAAgB,YAAY,IAAI,OAAO,CAAC,CAAC;AACjD;AAAA,IACF;AAEA,QAAI,IAAI,IAAI,MAAM,MAAM;AAEtB,UAAI,CAAC,QAAQ,UAAU;AACrB,aAAK,QAAQ,OAAO,EAAE;AACtB,aAAK,WAAW;AAAA,MAClB;AACA,cAAQ,QAAQ,GAAG;AAAA,IACrB,OAAO;AACL,WAAK,QAAQ,OAAO,EAAE;AACtB,WAAK,WAAW;AAChB,cAAQ,OAAO,YAAY,IAAI,OAAO,CAAC,CAAC;AAAA,IAC1C;AAAA,EACF;AAAA,EAEQ,QACN,SACA,OACkC;AAClC,UAAM,QAAQ,KAAK,YAAY;AAC/B,UAAM,KAAK,KAAK;AAChB,WAAO,IAAI,QAAiC,CAAC,SAAS,WAAW;AAC/D,WAAK,QAAQ,IAAI,IAAI,EAAE,SAAS,QAAQ,GAAG,MAAM,CAAC;AAClD,WAAK,WAAW;AAChB,YAAM,MAAM,MAAM,GAAG,KAAK,UAAU,EAAE,IAAI,GAAG,QAAQ,CAAC,CAAC;AAAA,GAAM,CAAC,QAAQ;AACpE,YAAI,KAAK;AACP,eAAK,QAAQ,OAAO,EAAE;AACtB,eAAK,WAAW;AAChB,iBAAO,GAAG;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAY,SAAsC;AACtD,UAAM,MAAM,MAAM,KAAK,QAAQ,EAAE,IAAI,UAAU,QAAQ,CAAC;AACxD,WAAO,KAAK,UAAU,OAAO,IAAI,SAAS,CAAC,CAAC;AAAA,EAC9C;AAAA,EAEA,MAAM,YAAY,SAAiB,SAAsC;AACvE,UAAM,MAAM,MAAM,KAAK,QAAQ,EAAE,IAAI,UAAU,SAAS,QAAQ,CAAC;AACjE,WAAO,KAAK,UAAU,OAAO,IAAI,SAAS,CAAC,CAAC;AAAA,EAC9C;AAAA,EAEQ,UAAU,SAA4B;AAC5C,WAAO;AAAA,MACL;AAAA,MACA,MAAM,CAAC,SAAS,YAAY,KAAK,SAAS,SAAS,SAAS,OAAO;AAAA,MACnE,OAAO,MAAM;AACX,aAAK,KAAK,QAAQ,EAAE,IAAI,SAAS,QAAQ,CAAC,EAAE,MAAM,MAAM;AAAA,QAExD,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,SACZ,SACA,SACA,SACuB;AACvB,QAAI;AAIJ,UAAM,SAAS,IAAI,QAA6C,CAAC,SAAS,WAAW;AACnF,eAAS,EAAE,SAAS,OAAO;AAAA,IAC7B,CAAC;AAED,WAAO,MAAM,MAAM;AAAA,IAAC,CAAC;AAErB,QAAI;AACJ,UAAM,MAAM,KAAK;AAAA,MACf;AAAA,QACE,IAAI;AAAA,QACJ;AAAA,QACA;AAAA,QACA,GAAI,SAAS,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,QAC9C,GAAI,SAAS,OAAO,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,MACjD;AAAA,MACA;AAAA,QACE,UAAU,CAAC,WAAW,SAAS,UAAU,EAAE,OAAO,CAAC;AAAA,QACnD,UAAU,CAAC,WAAW,OAAO,QAAQ,MAAM;AAAA,QAC3C,eAAe,CAAC,QAAQ,OAAO,OAAO,GAAG;AAAA,MAC3C;AAAA,IACF;AAGA,aAAS,KAAK,SAAS;AAEvB,UAAM;AACN,WAAO;AAAA,MACL,MAAM,MAAM;AAAA,MACZ,QAAQ,YAAY;AAClB,YAAI,WAAW,OAAW;AAC1B,cAAM,KAAK,QAAQ,EAAE,IAAI,UAAU,OAAO,CAAC,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC7D;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,WAAW;AAChB,SAAK,QAAQ,IAAI,MAAM,gCAAgC,CAAC;AACxD,SAAK,QAAQ,MAAM;AACnB,SAAK,SAAS;AACd,SAAK,OAAO,KAAK;AACjB,SAAK,QAAQ;AAAA,EACf;AACF;;;AD3OO,SAAS,mBAAmB,KAAsC;AACvE,QAAM,WAAW,QAAQ,IAAI,yBAAyB;AACtD,MAAI,aAAa,OAAO,aAAa,QAAS,QAAO;AACrD,MAAI,aAAa,OAAO,aAAa,OAAQ,QAAO,IAAI,WAAW,YAAY;AAC/E,SAAO,IAAI,SAAS,IAAI,WAAW,YAAY;AACjD;AAEA,SAAS,aAAiC;AACxC,MAAI;AACF,UAAM,MAAM,SAAS,QAAQ,aAAa,UAAU,eAAe,mBAAmB;AAAA,MACpF,UAAU;AAAA,MACV,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,IACpC,CAAC,EAAE,KAAK;AACR,WAAO,IAAI,MAAM,IAAI,EAAE,CAAC,KAAK;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oBAAwC;AAC/C,QAAM,QAAQ,OAAQ,WAAiC,QAAQ;AAE/D,QAAM,YAAY,SAAS,QAAQ,IAAI,yBAAyB,MAAM;AACtE,SAAO,EAAE,OAAO,UAAU,YAAY,WAAW,IAAI,QAAQ,SAAS;AACxE;AAEA,SAAS,mBAAiC;AACxC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa,OAAO,YAAY;AAC9B,YAAM,EAAE,MAAM,IAAI,MAAM,cAAc;AACtC,aAAQ,MAAM,MAAM,OAAO,OAAgB;AAAA,IAC7C;AAAA,IACA,aAAa,OAAO,SAAS,YAAY;AACvC,YAAM,EAAE,MAAM,IAAI,MAAM,cAAc;AACtC,aAAQ,MAAM,MAAM,OAAO,SAAS,OAAgB;AAAA,IACtD;AAAA,EACF;AACF;AAOO,SAAS,uBAA2C;AACzD,QAAM,aAAa;AAAA,IACjB;AAAA;AAAA,IACA;AAAA;AAAA,IACA;AAAA;AAAA,EACF;AACA,aAAW,aAAa,YAAY;AAClC,UAAM,OAAO,cAAc,IAAI,IAAI,WAAW,YAAY,GAAG,CAAC;AAC9D,QAAI,WAAW,IAAI,EAAG,QAAO;AAAA,EAC/B;AACA,SAAO;AACT;AAEA,SAAS,eAAe,UAAkB,YAAkC;AAC1E,QAAM,SAAS,IAAI,cAAc,EAAE,YAAY,SAAS,CAAC;AACzD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa,CAAC,YAAY,OAAO,YAAY,OAAO;AAAA,IACpD,aAAa,CAAC,SAAS,YAAY,OAAO,YAAY,SAAS,OAAO;AAAA,EACxE;AACF;AAEA,IAAIA;AAGG,SAAS,mBAAiC;AAC/C,MAAI,CAACA,SAAQ;AACX,UAAM,MAAM,kBAAkB;AAC9B,UAAM,OAAO,mBAAmB,GAAG;AACnC,UAAM,aAAa,SAAS,YAAY,qBAAqB,IAAI;AAGjE,UAAM,WAAW,QAAQ,IAAI,yBAAyB;AACtD,UAAM,WAAW,aAAa,OAAO,aAAa;AAClD,QAAI,IAAI,SAAS,CAAC,aAAa,SAAS,gBAAgB,CAAC,aAAa;AACpE,cAAQ;AAAA,QACN,4EACY,IAAI,YAAY,WAAW,aAAa,cAAc,WAAW;AAAA,MAI/E;AAAA,IACF;AACA,IAAAA,UACE,SAAS,aAAa,IAAI,YAAY,aAClC,eAAe,IAAI,UAAU,UAAU,IACvC,iBAAiB;AAAA,EACzB;AACA,SAAOA;AACT;;;AEvHA,IAAM,OAAO,oBAAI,IAAoB;AA6CrC,eAAsB,aAAa,QAAoD;AACrF,QAAM,UAAU,iBAAiB;AAEjC,QAAM,gBAAgB;AAAA,IACpB,QAAQ,OAAO;AAAA,IACf,OAAO,OAAO;AAAA,IACd,MAAM,OAAO;AAAA,IACb,OAAO;AAAA,MACL,KAAK,OAAO;AAAA,MACZ,GAAI,OAAO,iBAAiB,EAAE,gBAAgB,OAAO,eAAe,IAAI,CAAC;AAAA,MACzE,GAAI,OAAO,YAAY,SAAY,EAAE,gBAAgB,EAAE,SAAS,OAAO,QAAQ,EAAE,IAAI,CAAC;AAAA,IACxF;AAAA,IACA,GAAI,OAAO,aAAa,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,IAC7D,GAAI,OAAO,SAAS,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;AAAA,IACjD,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;AAAA,EAC7C;AAEA,QAAM,UAAU,OAAO,WAAW,QAAQ,OAAO,SAAS;AAC1D,QAAM,WAAW,UAAU,KAAK,IAAI,OAAO,SAAU,IAAI;AAEzD,QAAM,WAAW,OAAO,WAAW;AAEnC,MAAI;AACJ,MAAI,UAAU;AACd,MAAI,UAAU;AACZ,QAAI;AACF,cAAQ,MAAM,QAAQ,YAAY,UAAU,aAAa;AACzD,gBAAU;AAAA,IACZ,QAAQ;AAEN,UAAI,YAAY,aAAa,SAAU,MAAK,OAAO,OAAO,SAAU;AAAA,IACtE;AAAA,EACF;AACA,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,QAAQ,YAAY,aAAa;AAAA,EACjD;AAEA,MAAI,QAAS,MAAK,IAAI,OAAO,WAAY,MAAM,OAAO;AAEtD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,SAAS;AACZ,UAAI;AACF,cAAO,MAAM;AAAA,MACf,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,SAAS,QAAQ;AACnC;","names":["cached"]}
@@ -0,0 +1,17 @@
1
+ import { Plugin } from '@opencode-ai/plugin';
2
+
3
+ /**
4
+ * opencode plugin that adds a "Cursor" provider backed by the official Cursor
5
+ * SDK (`@cursor/sdk`).
6
+ *
7
+ * - `auth`: registers an API-key login for Cursor and a `loader` that feeds the
8
+ * key into the AI-SDK provider factory. The key is validated on first use
9
+ * (model discovery / first call), not at login — see the note on `methods`.
10
+ * - `config`: registers the provider (npm package + discovered/fallback models)
11
+ * so it shows up in opencode immediately.
12
+ * - `provider.models`: auth-aware live model discovery via `Cursor.models.list`.
13
+ * - `tool.cursor_refresh_models`: force-refresh the model catalog.
14
+ */
15
+ declare const CursorPlugin: Plugin;
16
+
17
+ export { CursorPlugin, CursorPlugin as default };