cursor-opencode-provider 0.6.8 → 0.7.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.
- package/README.md +50 -180
- package/dist/compaction-marker.d.ts +3 -2
- package/dist/compaction-marker.js +3 -2
- package/dist/host-agent-mode.d.ts +32 -0
- package/dist/host-agent-mode.js +92 -0
- package/dist/language-model.d.ts +6 -1
- package/dist/language-model.js +170 -34
- package/dist/models.d.ts +12 -1
- package/dist/models.js +16 -6
- package/dist/opencode2/catalog.d.ts +46 -11
- package/dist/opencode2/catalog.js +60 -45
- package/dist/opencode2/todo-tools.d.ts +64 -0
- package/dist/opencode2/todo-tools.js +165 -0
- package/dist/opencode2/types.d.ts +138 -83
- package/dist/opencode2/types.js +8 -10
- package/dist/plan-execution-kickoff.d.ts +2 -0
- package/dist/plan-execution-kickoff.js +25 -1
- package/dist/plugin-opencode2.d.ts +4 -1
- package/dist/plugin-opencode2.js +335 -154
- package/dist/plugin.js +5 -1
- package/dist/protocol/conversation-persistence.d.ts +2 -0
- package/dist/protocol/conversation-persistence.js +19 -1
- package/dist/protocol/conversation-state.d.ts +4 -0
- package/dist/protocol/conversation-state.js +4 -0
- package/dist/protocol/switch-mode.d.ts +5 -4
- package/dist/protocol/switch-mode.js +12 -4
- package/dist/protocol/tool-call-bridge.js +1 -1
- package/dist/protocol/tools.d.ts +30 -4
- package/dist/protocol/tools.js +185 -58
- package/dist/session.d.ts +7 -1
- package/dist/shared.d.ts +3 -1
- package/dist/shared.js +3 -1
- package/dist/shell-timeout.d.ts +13 -0
- package/dist/shell-timeout.js +38 -0
- package/dist/todo-store.d.ts +29 -0
- package/dist/todo-store.js +78 -0
- package/dist/web-tools.d.ts +13 -0
- package/dist/web-tools.js +83 -5
- package/package.json +6 -3
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-session todo list used when the host does not ship `todowrite` /
|
|
3
|
+
* `todoread` (OpenCode 2 dropped those builtins; OpenCode 1.x still has
|
|
4
|
+
* `todowrite`).
|
|
5
|
+
*
|
|
6
|
+
* In-memory only — same lifetime as the provider process. A restart clears
|
|
7
|
+
* the list until the next write/read, matching the mirrored Cursor-todo
|
|
8
|
+
* snapshot in `language-model.ts`.
|
|
9
|
+
*/
|
|
10
|
+
export const TODOWRITE_TOOL = "todowrite";
|
|
11
|
+
export const TODOREAD_TOOL = "todoread";
|
|
12
|
+
export const TODO_STATUSES = ["pending", "in_progress", "completed", "cancelled"];
|
|
13
|
+
export const TODO_PRIORITIES = ["high", "medium", "low"];
|
|
14
|
+
const lists = new Map();
|
|
15
|
+
const MAX_TRACKED_SESSIONS = 256;
|
|
16
|
+
function isStatus(value) {
|
|
17
|
+
return typeof value === "string" && TODO_STATUSES.includes(value);
|
|
18
|
+
}
|
|
19
|
+
function isPriority(value) {
|
|
20
|
+
return typeof value === "string" && TODO_PRIORITIES.includes(value);
|
|
21
|
+
}
|
|
22
|
+
/** Normalize one host/Cursor todo item. Drops entries with empty content. */
|
|
23
|
+
export function normalizeSessionTodo(value, index) {
|
|
24
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
25
|
+
return undefined;
|
|
26
|
+
const record = value;
|
|
27
|
+
const content = typeof record.content === "string" ? record.content : "";
|
|
28
|
+
if (!content.trim())
|
|
29
|
+
return undefined;
|
|
30
|
+
const id = typeof record.id === "string" && record.id.trim()
|
|
31
|
+
? record.id.trim()
|
|
32
|
+
: String(index + 1);
|
|
33
|
+
return {
|
|
34
|
+
id,
|
|
35
|
+
content,
|
|
36
|
+
status: isStatus(record.status) ? record.status : "pending",
|
|
37
|
+
priority: isPriority(record.priority) ? record.priority : "medium",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function normalizeSessionTodos(value) {
|
|
41
|
+
if (!Array.isArray(value))
|
|
42
|
+
return [];
|
|
43
|
+
const out = [];
|
|
44
|
+
const seen = new Set();
|
|
45
|
+
for (const item of value) {
|
|
46
|
+
const todo = normalizeSessionTodo(item, out.length);
|
|
47
|
+
if (!todo)
|
|
48
|
+
continue;
|
|
49
|
+
let id = todo.id;
|
|
50
|
+
if (seen.has(id))
|
|
51
|
+
id = `${id}-${out.length + 1}`;
|
|
52
|
+
seen.add(id);
|
|
53
|
+
out.push(id === todo.id ? todo : { ...todo, id });
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
export function getSessionTodos(sessionID) {
|
|
58
|
+
return lists.get(sessionID)?.map((todo) => ({ ...todo })) ?? [];
|
|
59
|
+
}
|
|
60
|
+
export function setSessionTodos(sessionID, todos) {
|
|
61
|
+
const normalized = normalizeSessionTodos(todos);
|
|
62
|
+
lists.delete(sessionID);
|
|
63
|
+
lists.set(sessionID, normalized.map((todo) => ({ ...todo })));
|
|
64
|
+
while (lists.size > MAX_TRACKED_SESSIONS) {
|
|
65
|
+
const oldest = lists.keys().next().value;
|
|
66
|
+
if (oldest === undefined)
|
|
67
|
+
break;
|
|
68
|
+
lists.delete(oldest);
|
|
69
|
+
}
|
|
70
|
+
return getSessionTodos(sessionID);
|
|
71
|
+
}
|
|
72
|
+
export function clearSessionTodos(sessionID) {
|
|
73
|
+
lists.delete(sessionID);
|
|
74
|
+
}
|
|
75
|
+
/** Test-only: drop every session list. */
|
|
76
|
+
export function clearAllSessionTodos() {
|
|
77
|
+
lists.clear();
|
|
78
|
+
}
|
package/dist/web-tools.d.ts
CHANGED
|
@@ -14,5 +14,18 @@ export declare function parseOpenCodeWebSearchResponse(raw: string): string | un
|
|
|
14
14
|
* OpenCode 2.0 plugin's tool registration share one implementation — 2.0's
|
|
15
15
|
* ToolContext has no `ask`, permissions being handled by the host instead.
|
|
16
16
|
*/
|
|
17
|
+
export type OpenCode2WebSearchResult = {
|
|
18
|
+
url: string;
|
|
19
|
+
title?: string;
|
|
20
|
+
content?: string;
|
|
21
|
+
time: {
|
|
22
|
+
published?: number;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Turn an Exa MCP text blob into OpenCode 2.0 `websearch` results
|
|
27
|
+
* (`{ url, title?, content?, time }`). Unknown shapes yield an empty list.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseExaWebSearchResults(raw: string): OpenCode2WebSearchResult[];
|
|
17
30
|
export declare function fetchOpenCodeWebSearchText(args: OpenCodeWebSearchArgs, signal: AbortSignal | undefined, fetchImpl?: typeof fetch): Promise<string>;
|
|
18
31
|
export declare function executeOpenCodeWebSearch(args: OpenCodeWebSearchArgs, context: ToolContext, fetchImpl?: typeof fetch): Promise<ToolResult>;
|
package/dist/web-tools.js
CHANGED
|
@@ -53,13 +53,91 @@ export function parseOpenCodeWebSearchResponse(raw) {
|
|
|
53
53
|
}
|
|
54
54
|
return undefined;
|
|
55
55
|
}
|
|
56
|
+
function tryParseJson(raw) {
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(raw);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function publishedMs(value) {
|
|
65
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
66
|
+
return value < 1e12 ? Math.floor(value * 1000) : Math.floor(value);
|
|
67
|
+
}
|
|
68
|
+
if (typeof value === "string" && value.trim()) {
|
|
69
|
+
const parsed = Date.parse(value);
|
|
70
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
function asWebSearchResult(row) {
|
|
75
|
+
if (!row || typeof row !== "object")
|
|
76
|
+
return undefined;
|
|
77
|
+
const record = row;
|
|
78
|
+
const url = typeof record.url === "string" ? record.url
|
|
79
|
+
: typeof record.href === "string" ? record.href
|
|
80
|
+
: undefined;
|
|
81
|
+
if (!url)
|
|
82
|
+
return undefined;
|
|
83
|
+
const title = typeof record.title === "string" ? record.title : undefined;
|
|
84
|
+
const content = typeof record.content === "string" ? record.content
|
|
85
|
+
: typeof record.text === "string" ? record.text
|
|
86
|
+
: typeof record.snippet === "string" ? record.snippet
|
|
87
|
+
: undefined;
|
|
88
|
+
const published = publishedMs(record.publishedDate ?? record.published ?? record.date);
|
|
89
|
+
return {
|
|
90
|
+
url,
|
|
91
|
+
...(title ? { title } : {}),
|
|
92
|
+
...(content ? { content } : {}),
|
|
93
|
+
time: published !== undefined ? { published } : {},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function parseExaTextResults(text) {
|
|
97
|
+
return text.split(/\n\n---\n\n/).flatMap((block) => {
|
|
98
|
+
const url = block.match(/^URL:\s*(.+)$/m)?.[1]?.trim();
|
|
99
|
+
if (!url)
|
|
100
|
+
return [];
|
|
101
|
+
const title = block.match(/^Title:\s*(.+)$/m)?.[1]?.trim();
|
|
102
|
+
const publishedText = block.match(/^Published:\s*(.+)$/m)?.[1]?.trim();
|
|
103
|
+
const published = publishedText && publishedText !== "N/A"
|
|
104
|
+
? publishedMs(publishedText)
|
|
105
|
+
: undefined;
|
|
106
|
+
const content = block.match(/^(?:Highlights|Text):\s*\n?([\s\S]*)$/m)?.[1]?.trim();
|
|
107
|
+
return [{
|
|
108
|
+
url,
|
|
109
|
+
...(title && title !== "N/A" ? { title } : {}),
|
|
110
|
+
...(content ? { content } : {}),
|
|
111
|
+
time: published === undefined ? {} : { published },
|
|
112
|
+
}];
|
|
113
|
+
});
|
|
114
|
+
}
|
|
56
115
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* Split out so both the classic plugin's `custom_websearch` tool and the
|
|
60
|
-
* OpenCode 2.0 plugin's tool registration share one implementation — 2.0's
|
|
61
|
-
* ToolContext has no `ask`, permissions being handled by the host instead.
|
|
116
|
+
* Turn an Exa MCP text blob into OpenCode 2.0 `websearch` results
|
|
117
|
+
* (`{ url, title?, content?, time }`). Unknown shapes yield an empty list.
|
|
62
118
|
*/
|
|
119
|
+
export function parseExaWebSearchResults(raw) {
|
|
120
|
+
const text = parseOpenCodeWebSearchResponse(raw) ?? raw.trim();
|
|
121
|
+
if (!text)
|
|
122
|
+
return [];
|
|
123
|
+
const parsed = tryParseJson(text);
|
|
124
|
+
const rows = Array.isArray(parsed)
|
|
125
|
+
? parsed
|
|
126
|
+
: parsed && typeof parsed === "object"
|
|
127
|
+
? Array.isArray(parsed.results)
|
|
128
|
+
? parsed.results
|
|
129
|
+
: Array.isArray(parsed.data)
|
|
130
|
+
? parsed.data
|
|
131
|
+
: []
|
|
132
|
+
: [];
|
|
133
|
+
if (rows.length > 0) {
|
|
134
|
+
return rows.flatMap((row) => {
|
|
135
|
+
const result = asWebSearchResult(row);
|
|
136
|
+
return result ? [result] : [];
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return parseExaTextResults(text);
|
|
140
|
+
}
|
|
63
141
|
export async function fetchOpenCodeWebSearchText(args, signal, fetchImpl = fetch) {
|
|
64
142
|
const controller = new AbortController();
|
|
65
143
|
const abort = () => controller.abort(signal?.reason);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursor-opencode-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Use Cursor subscription models from OpenCode via Cursor's Connect-RPC agent protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
"types": "./dist/plugin-opencode2.d.ts",
|
|
41
41
|
"import": "./dist/plugin-opencode2.js"
|
|
42
42
|
},
|
|
43
|
+
"./server": {
|
|
44
|
+
"types": "./dist/plugin-opencode2.d.ts",
|
|
45
|
+
"import": "./dist/plugin-opencode2.js"
|
|
46
|
+
},
|
|
43
47
|
"./errors": {
|
|
44
48
|
"types": "./dist/errors.d.ts",
|
|
45
49
|
"import": "./dist/errors.js"
|
|
@@ -70,7 +74,7 @@
|
|
|
70
74
|
],
|
|
71
75
|
"scripts": {
|
|
72
76
|
"build": "tsc",
|
|
73
|
-
"typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
|
|
77
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
|
|
74
78
|
"test": "bun test",
|
|
75
79
|
"test:node-http2": "bun run build && node test/node-http2-cleanup.mjs",
|
|
76
80
|
"test:watch": "bun test --watch",
|
|
@@ -93,7 +97,6 @@
|
|
|
93
97
|
},
|
|
94
98
|
"devDependencies": {
|
|
95
99
|
"@opencode-ai/plugin": "^1.18.16",
|
|
96
|
-
"@opencode-ai/plugin-next": "npm:@opencode-ai/plugin@0.0.0-next-17155",
|
|
97
100
|
"@tsconfig/node22": "^22.0.5",
|
|
98
101
|
"@types/bun": "^1.3.0",
|
|
99
102
|
"@types/node": "^22.20.1",
|