@playwo/opencode-cursor-oauth 0.0.0-dev.67ecd4697583 → 0.0.0-dev.7fe465ca080f
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.
|
@@ -212,8 +212,22 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
|
|
|
212
212
|
stopKeepalive();
|
|
213
213
|
}
|
|
214
214
|
}, SSE_KEEPALIVE_INTERVAL_MS);
|
|
215
|
+
logPluginInfo("Opened Cursor streaming bridge", {
|
|
216
|
+
modelId,
|
|
217
|
+
bridgeKey,
|
|
218
|
+
convKey,
|
|
219
|
+
mcpToolCount: mcpTools.length,
|
|
220
|
+
});
|
|
215
221
|
bridge.onData(processChunk);
|
|
216
222
|
bridge.onClose((code) => {
|
|
223
|
+
logPluginInfo("Cursor streaming bridge closed", {
|
|
224
|
+
modelId,
|
|
225
|
+
bridgeKey,
|
|
226
|
+
convKey,
|
|
227
|
+
code,
|
|
228
|
+
mcpExecReceived,
|
|
229
|
+
hadEndStreamError: Boolean(endStreamError),
|
|
230
|
+
});
|
|
217
231
|
clearInterval(heartbeatTimer);
|
|
218
232
|
stopKeepalive();
|
|
219
233
|
syncStoredBlobStore(convKey, blobStore);
|
|
@@ -263,6 +277,12 @@ function createBridgeStreamResponse(bridge, heartbeatTimer, blobStore, mcpTools,
|
|
|
263
277
|
return new Response(stream, { headers: SSE_HEADERS });
|
|
264
278
|
}
|
|
265
279
|
export async function handleStreamingResponse(payload, accessToken, modelId, bridgeKey, convKey, metadata) {
|
|
280
|
+
logPluginInfo("Starting Cursor streaming response", {
|
|
281
|
+
modelId,
|
|
282
|
+
bridgeKey,
|
|
283
|
+
convKey,
|
|
284
|
+
mcpToolCount: payload.mcpTools.length,
|
|
285
|
+
});
|
|
266
286
|
const { bridge, heartbeatTimer } = await startBridge(accessToken, payload.requestBytes);
|
|
267
287
|
return createBridgeStreamResponse(bridge, heartbeatTimer, payload.blobStore, payload.mcpTools, modelId, bridgeKey, convKey, metadata);
|
|
268
288
|
}
|
|
@@ -10,6 +10,19 @@ export function handleChatCompletion(body, accessToken, context = {}) {
|
|
|
10
10
|
const { systemPrompt, userText, turns, toolResults, pendingAssistantSummary, completedTurnsFingerprint, } = parsed;
|
|
11
11
|
const modelId = body.model;
|
|
12
12
|
const normalizedAgentKey = normalizeAgentKey(context.agentKey);
|
|
13
|
+
logPluginInfo("Handling Cursor chat completion request", {
|
|
14
|
+
modelId,
|
|
15
|
+
stream: body.stream !== false,
|
|
16
|
+
messageCount: body.messages.length,
|
|
17
|
+
toolCount: body.tools?.length ?? 0,
|
|
18
|
+
toolChoice: body.tool_choice,
|
|
19
|
+
sessionId: context.sessionId,
|
|
20
|
+
agentKey: normalizedAgentKey,
|
|
21
|
+
parsedUserText: userText,
|
|
22
|
+
parsedToolResults: toolResults,
|
|
23
|
+
hasPendingAssistantSummary: pendingAssistantSummary.trim().length > 0,
|
|
24
|
+
turnCount: turns.length,
|
|
25
|
+
});
|
|
13
26
|
const titleDetection = detectTitleRequest(body);
|
|
14
27
|
const isTitleAgent = titleDetection.matched;
|
|
15
28
|
if (isTitleAgent) {
|
|
@@ -38,6 +51,14 @@ export function handleChatCompletion(body, accessToken, context = {}) {
|
|
|
38
51
|
const bridgeKey = deriveBridgeKey(modelId, body.messages, context.sessionId, context.agentKey);
|
|
39
52
|
const convKey = deriveConversationKey(body.messages, context.sessionId, context.agentKey);
|
|
40
53
|
const activeBridge = activeBridges.get(bridgeKey);
|
|
54
|
+
logPluginInfo("Resolved Cursor conversation keys", {
|
|
55
|
+
modelId,
|
|
56
|
+
bridgeKey,
|
|
57
|
+
convKey,
|
|
58
|
+
hasActiveBridge: Boolean(activeBridge),
|
|
59
|
+
sessionId: context.sessionId,
|
|
60
|
+
agentKey: normalizedAgentKey,
|
|
61
|
+
});
|
|
41
62
|
if (activeBridge && toolResults.length > 0) {
|
|
42
63
|
logPluginInfo("Matched OpenAI tool results to active Cursor bridge", {
|
|
43
64
|
bridgeKey,
|
|
@@ -101,6 +122,16 @@ export function handleChatCompletion(body, accessToken, context = {}) {
|
|
|
101
122
|
: userText;
|
|
102
123
|
const payload = buildCursorRequest(modelId, systemPrompt, effectiveUserText, replayTurns, stored.conversationId, stored.checkpoint, stored.blobStore);
|
|
103
124
|
payload.mcpTools = mcpTools;
|
|
125
|
+
logPluginInfo("Built Cursor run request payload", {
|
|
126
|
+
modelId,
|
|
127
|
+
bridgeKey,
|
|
128
|
+
convKey,
|
|
129
|
+
mcpToolCount: mcpTools.length,
|
|
130
|
+
conversationId: stored.conversationId,
|
|
131
|
+
hasCheckpoint: Boolean(stored.checkpoint),
|
|
132
|
+
replayTurnCount: replayTurns.length,
|
|
133
|
+
effectiveUserText,
|
|
134
|
+
});
|
|
104
135
|
if (body.stream === false) {
|
|
105
136
|
return handleNonStreamingResponse(payload, accessToken, modelId, convKey, {
|
|
106
137
|
systemPrompt,
|
|
@@ -207,6 +207,11 @@ function emitPendingExec(exec, state, onMcpExec) {
|
|
|
207
207
|
}
|
|
208
208
|
export function processServerMessage(msg, blobStore, mcpTools, sendFrame, state, onText, onMcpExec, onCheckpoint, onTurnEnded, onUnsupportedMessage, onUnhandledExec) {
|
|
209
209
|
const msgCase = msg.message.case;
|
|
210
|
+
if (msgCase !== "conversationCheckpointUpdate") {
|
|
211
|
+
logPluginInfo("Received Cursor server message", {
|
|
212
|
+
messageCase: msgCase ?? "undefined",
|
|
213
|
+
});
|
|
214
|
+
}
|
|
210
215
|
if (msgCase === "interactionUpdate") {
|
|
211
216
|
handleInteractionUpdate(msg.message.value, state, onText, onMcpExec, onTurnEnded, onUnsupportedMessage);
|
|
212
217
|
}
|
|
@@ -243,6 +248,16 @@ export function processServerMessage(msg, blobStore, mcpTools, sendFrame, state,
|
|
|
243
248
|
}
|
|
244
249
|
function handleInteractionUpdate(update, state, onText, onMcpExec, onTurnEnded, onUnsupportedMessage) {
|
|
245
250
|
const updateCase = update.message?.case;
|
|
251
|
+
if (updateCase !== "textDelta" &&
|
|
252
|
+
updateCase !== "thinkingDelta" &&
|
|
253
|
+
updateCase !== "tokenDelta") {
|
|
254
|
+
logPluginInfo("Received Cursor interaction update", {
|
|
255
|
+
updateCase: updateCase ?? "undefined",
|
|
256
|
+
callId: update.message?.value?.callId,
|
|
257
|
+
modelCallId: update.message?.value?.modelCallId,
|
|
258
|
+
toolCase: update.message?.value?.toolCall?.tool?.case,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
246
261
|
if (updateCase === "textDelta") {
|
|
247
262
|
const delta = update.message.value.text || "";
|
|
248
263
|
if (delta)
|
|
@@ -465,7 +480,17 @@ function handleKvMessage(kvMsg, blobStore, sendFrame) {
|
|
|
465
480
|
}
|
|
466
481
|
function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnhandledExec) {
|
|
467
482
|
const execCase = execMsg.message.case;
|
|
483
|
+
logPluginInfo("Received Cursor exec message", {
|
|
484
|
+
execCase: execCase ?? "undefined",
|
|
485
|
+
execId: execMsg.execId,
|
|
486
|
+
execMsgId: execMsg.id,
|
|
487
|
+
});
|
|
468
488
|
if (execCase === "requestContextArgs") {
|
|
489
|
+
logPluginInfo("Responding to Cursor requestContextArgs", {
|
|
490
|
+
execId: execMsg.execId,
|
|
491
|
+
execMsgId: execMsg.id,
|
|
492
|
+
mcpToolCount: mcpTools.length,
|
|
493
|
+
});
|
|
469
494
|
const requestContext = create(RequestContextSchema, {
|
|
470
495
|
rules: [],
|
|
471
496
|
repositoryInfo: [],
|
|
@@ -512,6 +537,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
512
537
|
// so it falls back to our MCP tools (registered via RequestContext).
|
|
513
538
|
const REJECT_REASON = "Tool not available in this environment. Use the MCP tools provided instead.";
|
|
514
539
|
if (execCase === "readArgs") {
|
|
540
|
+
logPluginInfo("Rejecting native Cursor read tool in favor of MCP", {
|
|
541
|
+
execId: execMsg.execId,
|
|
542
|
+
execMsgId: execMsg.id,
|
|
543
|
+
path: execMsg.message.value.path,
|
|
544
|
+
});
|
|
515
545
|
const args = execMsg.message.value;
|
|
516
546
|
const result = create(ReadResultSchema, {
|
|
517
547
|
result: {
|
|
@@ -526,6 +556,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
526
556
|
return;
|
|
527
557
|
}
|
|
528
558
|
if (execCase === "lsArgs") {
|
|
559
|
+
logPluginInfo("Rejecting native Cursor ls tool in favor of MCP", {
|
|
560
|
+
execId: execMsg.execId,
|
|
561
|
+
execMsgId: execMsg.id,
|
|
562
|
+
path: execMsg.message.value.path,
|
|
563
|
+
});
|
|
529
564
|
const args = execMsg.message.value;
|
|
530
565
|
const result = create(LsResultSchema, {
|
|
531
566
|
result: {
|
|
@@ -540,6 +575,10 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
540
575
|
return;
|
|
541
576
|
}
|
|
542
577
|
if (execCase === "grepArgs") {
|
|
578
|
+
logPluginInfo("Rejecting native Cursor grep tool in favor of MCP", {
|
|
579
|
+
execId: execMsg.execId,
|
|
580
|
+
execMsgId: execMsg.id,
|
|
581
|
+
});
|
|
543
582
|
const result = create(GrepResultSchema, {
|
|
544
583
|
result: {
|
|
545
584
|
case: "error",
|
|
@@ -550,6 +589,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
550
589
|
return;
|
|
551
590
|
}
|
|
552
591
|
if (execCase === "writeArgs") {
|
|
592
|
+
logPluginInfo("Rejecting native Cursor write tool in favor of MCP", {
|
|
593
|
+
execId: execMsg.execId,
|
|
594
|
+
execMsgId: execMsg.id,
|
|
595
|
+
path: execMsg.message.value.path,
|
|
596
|
+
});
|
|
553
597
|
const args = execMsg.message.value;
|
|
554
598
|
const result = create(WriteResultSchema, {
|
|
555
599
|
result: {
|
|
@@ -564,6 +608,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
564
608
|
return;
|
|
565
609
|
}
|
|
566
610
|
if (execCase === "deleteArgs") {
|
|
611
|
+
logPluginInfo("Rejecting native Cursor delete tool in favor of MCP", {
|
|
612
|
+
execId: execMsg.execId,
|
|
613
|
+
execMsgId: execMsg.id,
|
|
614
|
+
path: execMsg.message.value.path,
|
|
615
|
+
});
|
|
567
616
|
const args = execMsg.message.value;
|
|
568
617
|
const result = create(DeleteResultSchema, {
|
|
569
618
|
result: {
|
|
@@ -578,6 +627,13 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
578
627
|
return;
|
|
579
628
|
}
|
|
580
629
|
if (execCase === "shellArgs" || execCase === "shellStreamArgs") {
|
|
630
|
+
logPluginInfo("Rejecting native Cursor shell tool in favor of MCP", {
|
|
631
|
+
execId: execMsg.execId,
|
|
632
|
+
execMsgId: execMsg.id,
|
|
633
|
+
command: execMsg.message.value.command ?? "",
|
|
634
|
+
workingDirectory: execMsg.message.value.workingDirectory ?? "",
|
|
635
|
+
execCase,
|
|
636
|
+
});
|
|
581
637
|
const args = execMsg.message.value;
|
|
582
638
|
const result = create(ShellResultSchema, {
|
|
583
639
|
result: {
|
|
@@ -594,6 +650,12 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
594
650
|
return;
|
|
595
651
|
}
|
|
596
652
|
if (execCase === "backgroundShellSpawnArgs") {
|
|
653
|
+
logPluginInfo("Rejecting native Cursor background shell tool in favor of MCP", {
|
|
654
|
+
execId: execMsg.execId,
|
|
655
|
+
execMsgId: execMsg.id,
|
|
656
|
+
command: execMsg.message.value.command ?? "",
|
|
657
|
+
workingDirectory: execMsg.message.value.workingDirectory ?? "",
|
|
658
|
+
});
|
|
597
659
|
const args = execMsg.message.value;
|
|
598
660
|
const result = create(BackgroundShellSpawnResultSchema, {
|
|
599
661
|
result: {
|
|
@@ -610,6 +672,10 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
610
672
|
return;
|
|
611
673
|
}
|
|
612
674
|
if (execCase === "writeShellStdinArgs") {
|
|
675
|
+
logPluginInfo("Rejecting native Cursor shell stdin tool in favor of MCP", {
|
|
676
|
+
execId: execMsg.execId,
|
|
677
|
+
execMsgId: execMsg.id,
|
|
678
|
+
});
|
|
613
679
|
const result = create(WriteShellStdinResultSchema, {
|
|
614
680
|
result: {
|
|
615
681
|
case: "error",
|
|
@@ -620,6 +686,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
620
686
|
return;
|
|
621
687
|
}
|
|
622
688
|
if (execCase === "fetchArgs") {
|
|
689
|
+
logPluginInfo("Rejecting native Cursor fetch tool in favor of MCP", {
|
|
690
|
+
execId: execMsg.execId,
|
|
691
|
+
execMsgId: execMsg.id,
|
|
692
|
+
url: execMsg.message.value.url,
|
|
693
|
+
});
|
|
623
694
|
const args = execMsg.message.value;
|
|
624
695
|
const result = create(FetchResultSchema, {
|
|
625
696
|
result: {
|
|
@@ -634,6 +705,11 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
634
705
|
return;
|
|
635
706
|
}
|
|
636
707
|
if (execCase === "diagnosticsArgs") {
|
|
708
|
+
logPluginInfo("Rejecting native Cursor diagnostics tool in favor of MCP", {
|
|
709
|
+
execId: execMsg.execId,
|
|
710
|
+
execMsgId: execMsg.id,
|
|
711
|
+
path: execMsg.message.value.path,
|
|
712
|
+
});
|
|
637
713
|
const result = create(DiagnosticsResultSchema, {});
|
|
638
714
|
sendExecResult(execMsg, "diagnosticsResult", result, sendFrame);
|
|
639
715
|
return;
|
|
@@ -647,6 +723,12 @@ function handleExecMessage(execMsg, mcpTools, sendFrame, state, onMcpExec, onUnh
|
|
|
647
723
|
};
|
|
648
724
|
const resultCase = miscCaseMap[execCase];
|
|
649
725
|
if (resultCase) {
|
|
726
|
+
logPluginInfo("Responding to miscellaneous Cursor exec message", {
|
|
727
|
+
execCase,
|
|
728
|
+
execId: execMsg.execId,
|
|
729
|
+
execMsgId: execMsg.id,
|
|
730
|
+
resultCase,
|
|
731
|
+
});
|
|
650
732
|
sendExecResult(execMsg, resultCase, create(McpResultSchema, {}), sendFrame);
|
|
651
733
|
return;
|
|
652
734
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playwo/opencode-cursor-oauth",
|
|
3
|
-
"version": "0.0.0-dev.
|
|
3
|
+
"version": "0.0.0-dev.7fe465ca080f",
|
|
4
4
|
"description": "OpenCode plugin that connects Cursor's API to OpenCode via OAuth, model discovery, and a local OpenAI-compatible proxy.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsc -p tsconfig.json",
|
|
22
|
-
"test": "bun test/smoke.ts",
|
|
23
22
|
"prepublishOnly": "npm run build"
|
|
24
23
|
},
|
|
25
24
|
"repository": {
|