@meetopenbot/cursor 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/format.js +39 -10
- package/dist/index.d.ts +4 -0
- package/dist/index.js +95 -12
- package/dist/stream.js +9 -6
- package/package.json +15 -9
package/dist/format.js
CHANGED
|
@@ -93,7 +93,19 @@ const formatCloudStatus = (status, message, config) => {
|
|
|
93
93
|
return message ? truncate(message) : undefined;
|
|
94
94
|
}
|
|
95
95
|
};
|
|
96
|
-
|
|
96
|
+
export const formatToolResult = (result) => {
|
|
97
|
+
if (result == null)
|
|
98
|
+
return '';
|
|
99
|
+
if (typeof result === 'string')
|
|
100
|
+
return result;
|
|
101
|
+
try {
|
|
102
|
+
return JSON.stringify(result, null, 2);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return String(result);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
/** Map Cursor SDK stream events to structured output chunks. */
|
|
97
109
|
export const formatCursorEvent = (event, state, config) => {
|
|
98
110
|
switch (event.type) {
|
|
99
111
|
case 'assistant': {
|
|
@@ -107,27 +119,44 @@ export const formatCursorEvent = (event, state, config) => {
|
|
|
107
119
|
}
|
|
108
120
|
case 'tool_call': {
|
|
109
121
|
if (event.status === 'running') {
|
|
110
|
-
return
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
122
|
+
return {
|
|
123
|
+
type: 'tool_start',
|
|
124
|
+
toolCallId: event.call_id,
|
|
125
|
+
toolName: event.name,
|
|
126
|
+
args: event.args,
|
|
127
|
+
statusLine: formatToolStart(event.name, event.args),
|
|
128
|
+
};
|
|
115
129
|
}
|
|
116
|
-
|
|
130
|
+
const isError = event.status === 'error';
|
|
131
|
+
return {
|
|
132
|
+
type: 'tool_end',
|
|
133
|
+
toolCallId: event.call_id,
|
|
134
|
+
toolName: event.name,
|
|
135
|
+
args: event.args,
|
|
136
|
+
result: event.result,
|
|
137
|
+
isError,
|
|
138
|
+
statusLine: isError
|
|
139
|
+
? `Tool **${event.name}** failed.`
|
|
140
|
+
: formatToolStart(event.name, event.args).replace(/…$/, ''),
|
|
141
|
+
};
|
|
117
142
|
}
|
|
118
143
|
case 'status': {
|
|
119
144
|
if (event.status === 'ERROR') {
|
|
120
145
|
const details = JSON.stringify(event);
|
|
121
146
|
const isCloud = config?.runtime === 'cloud';
|
|
122
147
|
const prefix = isCloud ? 'Cloud run failed' : 'Local run failed';
|
|
123
|
-
return
|
|
148
|
+
return {
|
|
149
|
+
type: 'text',
|
|
150
|
+
content: `**Cursor error:** ${prefix}. Event details: \`${details}\``,
|
|
151
|
+
};
|
|
124
152
|
}
|
|
125
|
-
|
|
153
|
+
const statusLine = formatCloudStatus(event.status, event.message, config);
|
|
154
|
+
return statusLine ? { type: 'status', statusLine } : undefined;
|
|
126
155
|
}
|
|
127
156
|
case 'task': {
|
|
128
157
|
if (!event.text?.trim())
|
|
129
158
|
return undefined;
|
|
130
|
-
return truncate(event.text);
|
|
159
|
+
return { type: 'status', statusLine: truncate(event.text) };
|
|
131
160
|
}
|
|
132
161
|
default:
|
|
133
162
|
return undefined;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { agentOutput, definePlugin, shouldHandleInvoke, } from '@meetopenbot/plugin-sdk';
|
|
1
|
+
import { agentOutput, buildDiffWidget, definePlugin, diffFileFromMutationTool, resolveRunDiffFiles, shouldHandleInvoke, snapshotWorkspace, toolTraceWidget, uiWidget, } from '@meetopenbot/plugin-sdk';
|
|
2
2
|
import { CURSOR_API_KEY_ENV_VAR, resolveConfig, } from './config.js';
|
|
3
|
-
import { formatCursorError } from './format.js';
|
|
3
|
+
import { formatCursorError, formatToolResult } from './format.js';
|
|
4
4
|
import { getOrCreateCursorAgent, StaleAgentSessionError } from './session.js';
|
|
5
5
|
import { streamCursorPrompt } from './stream.js';
|
|
6
6
|
export default definePlugin({
|
|
@@ -112,14 +112,103 @@ export default definePlugin({
|
|
|
112
112
|
state: handlerCtx.state,
|
|
113
113
|
storage: context.storage,
|
|
114
114
|
});
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
const snapshot = snapshotWorkspace(config.cwd);
|
|
116
|
+
const runTurn = async function* (wasResumed) {
|
|
117
|
+
const toolInfoMap = new Map();
|
|
118
|
+
const changedFiles = new Map();
|
|
119
|
+
let fullTextContent = '';
|
|
120
|
+
for await (const chunk of streamCursorPrompt(agentHandle.agent, prompt, config, wasResumed)) {
|
|
121
|
+
switch (chunk.type) {
|
|
122
|
+
case 'tool_start':
|
|
123
|
+
toolInfoMap.set(chunk.toolCallId, {
|
|
124
|
+
statusLine: chunk.statusLine,
|
|
125
|
+
args: chunk.args,
|
|
126
|
+
});
|
|
127
|
+
yield uiWidget({
|
|
128
|
+
agentId: context.agentId,
|
|
129
|
+
threadId,
|
|
130
|
+
widget: toolTraceWidget({
|
|
131
|
+
widgetId: chunk.toolCallId,
|
|
132
|
+
groupId: 'cursor:tools',
|
|
133
|
+
title: chunk.statusLine,
|
|
134
|
+
body: `**Input**\n\`\`\`json\n${JSON.stringify(chunk.args ?? {}, null, 2)}\n\`\`\``,
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
break;
|
|
138
|
+
case 'tool_end': {
|
|
139
|
+
const info = toolInfoMap.get(chunk.toolCallId);
|
|
140
|
+
const args = chunk.args ?? info?.args;
|
|
141
|
+
const inputMd = args
|
|
142
|
+
? `**Input**\n\`\`\`json\n${JSON.stringify(args, null, 2)}\n\`\`\`\n\n`
|
|
143
|
+
: '';
|
|
144
|
+
const outputMd = chunk.result != null
|
|
145
|
+
? `**Output**\n${formatToolResult(chunk.result)}`
|
|
146
|
+
: '';
|
|
147
|
+
yield uiWidget({
|
|
148
|
+
agentId: context.agentId,
|
|
149
|
+
threadId,
|
|
150
|
+
widget: toolTraceWidget({
|
|
151
|
+
widgetId: chunk.toolCallId,
|
|
152
|
+
groupId: 'cursor:tools',
|
|
153
|
+
title: chunk.statusLine || info?.statusLine || `Tool ${chunk.toolName} finished`,
|
|
154
|
+
body: inputMd + outputMd,
|
|
155
|
+
state: chunk.isError ? 'error' : 'submitted',
|
|
156
|
+
}),
|
|
157
|
+
});
|
|
158
|
+
if (!chunk.isError) {
|
|
159
|
+
const file = diffFileFromMutationTool({
|
|
160
|
+
toolName: chunk.toolName,
|
|
161
|
+
input: args,
|
|
162
|
+
result: chunk.result,
|
|
163
|
+
});
|
|
164
|
+
if (file)
|
|
165
|
+
changedFiles.set(file.path, file);
|
|
166
|
+
}
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
case 'status':
|
|
170
|
+
yield uiWidget({
|
|
171
|
+
agentId: context.agentId,
|
|
172
|
+
threadId,
|
|
173
|
+
widget: toolTraceWidget({
|
|
174
|
+
widgetId: `status-${Date.now()}`,
|
|
175
|
+
groupId: 'cursor:tools',
|
|
176
|
+
title: 'Status',
|
|
177
|
+
body: chunk.statusLine,
|
|
178
|
+
}),
|
|
179
|
+
});
|
|
180
|
+
break;
|
|
181
|
+
case 'text':
|
|
182
|
+
if (fullTextContent)
|
|
183
|
+
fullTextContent += '\n\n';
|
|
184
|
+
fullTextContent += chunk.content;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (fullTextContent.trim()) {
|
|
117
189
|
yield agentOutput({
|
|
118
190
|
agentId: context.agentId,
|
|
119
|
-
content:
|
|
191
|
+
content: fullTextContent.trim(),
|
|
120
192
|
threadId,
|
|
121
193
|
});
|
|
122
194
|
}
|
|
195
|
+
const diff = buildDiffWidget({
|
|
196
|
+
widgetId: `cursor-diff:${threadId ?? 'run'}:${Date.now()}`,
|
|
197
|
+
files: resolveRunDiffFiles({
|
|
198
|
+
snapshot,
|
|
199
|
+
fallback: changedFiles.values(),
|
|
200
|
+
}),
|
|
201
|
+
});
|
|
202
|
+
if (diff) {
|
|
203
|
+
yield uiWidget({
|
|
204
|
+
agentId: context.agentId,
|
|
205
|
+
threadId,
|
|
206
|
+
widget: diff,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
try {
|
|
211
|
+
yield* runTurn(agentHandle.wasResumed);
|
|
123
212
|
}
|
|
124
213
|
catch (streamErr) {
|
|
125
214
|
if (streamErr instanceof StaleAgentSessionError) {
|
|
@@ -130,13 +219,7 @@ export default definePlugin({
|
|
|
130
219
|
storage: context.storage,
|
|
131
220
|
forceNew: true,
|
|
132
221
|
});
|
|
133
|
-
|
|
134
|
-
yield agentOutput({
|
|
135
|
-
agentId: context.agentId,
|
|
136
|
-
content: chunk,
|
|
137
|
-
threadId,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
222
|
+
yield* runTurn(agentHandle.wasResumed);
|
|
140
223
|
}
|
|
141
224
|
else {
|
|
142
225
|
throw streamErr;
|
package/dist/stream.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { buildModelSelection } from './config.js';
|
|
2
|
-
import { createStreamState, formatCursorEvent } from './format.js';
|
|
2
|
+
import { createStreamState, formatCursorEvent, } from './format.js';
|
|
3
3
|
import { StaleAgentSessionError } from './session.js';
|
|
4
|
-
/** Bridge Cursor SDK run events into an async generator of
|
|
4
|
+
/** Bridge Cursor SDK run events into an async generator of structured chunks. */
|
|
5
5
|
export async function* streamCursorPrompt(agent, prompt, config, wasResumed) {
|
|
6
6
|
const state = createStreamState();
|
|
7
7
|
const pending = [];
|
|
@@ -28,17 +28,20 @@ export async function* streamCursorPrompt(agent, prompt, config, wasResumed) {
|
|
|
28
28
|
const result = await run.wait();
|
|
29
29
|
const finalText = (result.result ?? state.assistantText).trim();
|
|
30
30
|
if (finalText) {
|
|
31
|
-
pending.push(finalText);
|
|
31
|
+
pending.push({ type: 'text', content: finalText });
|
|
32
32
|
}
|
|
33
33
|
if (result.status === 'error') {
|
|
34
34
|
if (wasResumed && (result.durationMs ?? 0) < 2000) {
|
|
35
35
|
throw new StaleAgentSessionError('Resumed Cursor agent session is stale or expired.');
|
|
36
36
|
}
|
|
37
37
|
const details = JSON.stringify(result);
|
|
38
|
-
pending.push(
|
|
38
|
+
pending.push({
|
|
39
|
+
type: 'text',
|
|
40
|
+
content: `**Cursor error:** Run finished with error status. Details: \`${details}\``,
|
|
41
|
+
});
|
|
39
42
|
}
|
|
40
43
|
else if (result.status === 'cancelled') {
|
|
41
|
-
pending.push('**Cursor:** Run cancelled.');
|
|
44
|
+
pending.push({ type: 'text', content: '**Cursor:** Run cancelled.' });
|
|
42
45
|
}
|
|
43
46
|
if (result.git?.branches?.length) {
|
|
44
47
|
const branches = result.git.branches
|
|
@@ -48,7 +51,7 @@ export async function* streamCursorPrompt(agent, prompt, config, wasResumed) {
|
|
|
48
51
|
})
|
|
49
52
|
.filter(Boolean);
|
|
50
53
|
if (branches.length) {
|
|
51
|
-
pending.push(`Git: ${branches.join('; ')}`);
|
|
54
|
+
pending.push({ type: 'text', content: `Git: ${branches.join('; ')}` });
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
}
|
package/package.json
CHANGED
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetopenbot/cursor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "OpenBot agent plugin powered by the Cursor SDK.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
10
10
|
"exports": {
|
|
11
|
-
".":
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
12
16
|
},
|
|
13
17
|
"files": [
|
|
14
18
|
"dist"
|
|
15
19
|
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "tsc -p tsconfig.json",
|
|
18
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
19
|
-
},
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@cursor/sdk": "^1.0.18",
|
|
23
|
-
"@meetopenbot/plugin-sdk": "^0.
|
|
23
|
+
"@meetopenbot/plugin-sdk": "^0.2.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "^25.9.2",
|
|
27
27
|
"typescript": "^6.0.3"
|
|
28
|
+
},
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -p tsconfig.json && node ../../scripts/write-plugin-declaration.mjs",
|
|
32
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
33
|
+
"dev": "tsc -p tsconfig.json --watch --preserveWatchOutput"
|
|
28
34
|
}
|
|
29
|
-
}
|
|
35
|
+
}
|