@glwhappen/web-code 1.32.9 → 1.32.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.de.md +1 -1
  2. package/README.ko.md +1 -1
  3. package/README.md +1 -1
  4. package/README.ru.md +1 -1
  5. package/README.tr.md +1 -1
  6. package/README.zh-CN.md +1 -1
  7. package/dist/api-docs.html +6 -7
  8. package/dist/assets/{index-D_7CSvqO.js → index-CBo8yakG.js} +276 -261
  9. package/dist/assets/index-Dl5QP21C.css +32 -0
  10. package/dist/index.html +2 -2
  11. package/dist/modelConstants.js +841 -0
  12. package/dist-server/server/claude-sdk.js +57 -34
  13. package/dist-server/server/claude-sdk.js.map +1 -1
  14. package/dist-server/server/cursor-cli.js +6 -3
  15. package/dist-server/server/cursor-cli.js.map +1 -1
  16. package/dist-server/server/gemini-cli.js +3 -1
  17. package/dist-server/server/gemini-cli.js.map +1 -1
  18. package/dist-server/server/gemini-response-handler.js +34 -0
  19. package/dist-server/server/gemini-response-handler.js.map +1 -1
  20. package/dist-server/server/index.js +131 -19
  21. package/dist-server/server/index.js.map +1 -1
  22. package/dist-server/server/modules/database/index.js +1 -0
  23. package/dist-server/server/modules/database/index.js.map +1 -1
  24. package/dist-server/server/modules/projects/services/project-management.service.js +1 -0
  25. package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -1
  26. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +4 -0
  27. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
  28. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +143 -0
  29. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -0
  30. package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
  31. package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
  32. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +84 -0
  33. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -0
  34. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +7 -39
  35. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -1
  36. package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
  37. package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
  38. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +754 -0
  39. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -0
  40. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +2 -15
  41. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
  42. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +2 -0
  43. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
  44. package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js +27 -0
  45. package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js.map +1 -0
  46. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +3 -9
  47. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -1
  48. package/dist-server/server/modules/providers/list/gemini/gemini.provider.js +2 -0
  49. package/dist-server/server/modules/providers/list/gemini/gemini.provider.js.map +1 -1
  50. package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js +92 -0
  51. package/dist-server/server/modules/providers/list/opencode/opencode-auth.provider.js.map +1 -0
  52. package/dist-server/server/modules/providers/list/opencode/opencode-mcp.provider.js +181 -0
  53. package/dist-server/server/modules/providers/list/opencode/opencode-mcp.provider.js.map +1 -0
  54. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +267 -0
  55. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -0
  56. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +115 -0
  57. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -0
  58. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +410 -0
  59. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -0
  60. package/dist-server/server/modules/providers/list/opencode/opencode-skills.provider.js +62 -0
  61. package/dist-server/server/modules/providers/list/opencode/opencode-skills.provider.js.map +1 -0
  62. package/dist-server/server/modules/providers/list/opencode/opencode.provider.js +19 -0
  63. package/dist-server/server/modules/providers/list/opencode/opencode.provider.js.map +1 -0
  64. package/dist-server/server/modules/providers/provider.registry.js +2 -0
  65. package/dist-server/server/modules/providers/provider.registry.js.map +1 -1
  66. package/dist-server/server/modules/providers/provider.routes.js +42 -1
  67. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  68. package/dist-server/server/modules/providers/services/mcp.service.js +1 -9
  69. package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -1
  70. package/dist-server/server/modules/providers/services/provider-models.service.js +199 -0
  71. package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -0
  72. package/dist-server/server/modules/providers/services/session-synchronizer.service.js +1 -0
  73. package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
  74. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +7 -0
  75. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
  76. package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
  77. package/dist-server/server/modules/providers/tests/mcp.test.js +73 -6
  78. package/dist-server/server/modules/providers/tests/mcp.test.js.map +1 -1
  79. package/dist-server/server/modules/providers/tests/opencode-models.test.js +66 -0
  80. package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -0
  81. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +264 -0
  82. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -0
  83. package/dist-server/server/modules/providers/tests/provider-models.service.test.js +270 -0
  84. package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -0
  85. package/dist-server/server/modules/providers/tests/skills.test.js +33 -0
  86. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
  87. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +18 -1
  88. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  89. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +9 -1
  90. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  91. package/dist-server/server/openai-codex.js +32 -4
  92. package/dist-server/server/openai-codex.js.map +1 -1
  93. package/dist-server/server/opencode-cli.js +287 -0
  94. package/dist-server/server/opencode-cli.js.map +1 -0
  95. package/dist-server/server/opencode-cli.test.js +84 -0
  96. package/dist-server/server/opencode-cli.test.js.map +1 -0
  97. package/dist-server/server/routes/agent.js +21 -8
  98. package/dist-server/server/routes/agent.js.map +1 -1
  99. package/dist-server/server/routes/commands.js +202 -209
  100. package/dist-server/server/routes/commands.js.map +1 -1
  101. package/dist-server/server/routes/cursor.js +2 -2
  102. package/dist-server/server/routes/cursor.js.map +1 -1
  103. package/dist-server/server/routes/settings.js +0 -10
  104. package/dist-server/server/routes/settings.js.map +1 -1
  105. package/dist-server/server/routes/tests/commands.test.js +76 -0
  106. package/dist-server/server/routes/tests/commands.test.js.map +1 -0
  107. package/dist-server/server/shared/utils.js +286 -0
  108. package/dist-server/server/shared/utils.js.map +1 -1
  109. package/package.json +3 -1
  110. package/public/api-docs.html +878 -0
  111. package/public/modelConstants.js +841 -0
  112. package/server/claude-sdk.js +64 -35
  113. package/server/cursor-cli.js +6 -3
  114. package/server/gemini-cli.js +7 -1
  115. package/server/gemini-response-handler.js +38 -0
  116. package/server/index.js +150 -19
  117. package/server/modules/database/index.ts +1 -0
  118. package/server/modules/projects/services/project-management.service.ts +2 -0
  119. package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +7 -1
  120. package/server/modules/providers/README.md +11 -3
  121. package/server/modules/providers/list/claude/claude-models.provider.ts +193 -0
  122. package/server/modules/providers/list/claude/claude.provider.ts +3 -0
  123. package/server/modules/providers/list/codex/codex-models.provider.ts +125 -0
  124. package/server/modules/providers/list/codex/codex-skills.provider.ts +10 -50
  125. package/server/modules/providers/list/codex/codex.provider.ts +3 -0
  126. package/server/modules/providers/list/cursor/cursor-models.provider.ts +820 -0
  127. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +7 -20
  128. package/server/modules/providers/list/cursor/cursor.provider.ts +3 -0
  129. package/server/modules/providers/list/gemini/gemini-models.provider.ts +42 -0
  130. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +3 -10
  131. package/server/modules/providers/list/gemini/gemini.provider.ts +3 -0
  132. package/server/modules/providers/list/opencode/opencode-auth.provider.ts +111 -0
  133. package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +228 -0
  134. package/server/modules/providers/list/opencode/opencode-models.provider.ts +339 -0
  135. package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +158 -0
  136. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +506 -0
  137. package/server/modules/providers/list/opencode/opencode-skills.provider.ts +78 -0
  138. package/server/modules/providers/list/opencode/opencode.provider.ts +27 -0
  139. package/server/modules/providers/provider.registry.ts +2 -0
  140. package/server/modules/providers/provider.routes.ts +62 -2
  141. package/server/modules/providers/services/mcp.service.ts +1 -12
  142. package/server/modules/providers/services/provider-models.service.ts +325 -0
  143. package/server/modules/providers/services/session-synchronizer.service.ts +1 -0
  144. package/server/modules/providers/services/sessions-watcher.service.ts +8 -0
  145. package/server/modules/providers/shared/base/abstract.provider.ts +2 -0
  146. package/server/modules/providers/tests/mcp.test.ts +93 -6
  147. package/server/modules/providers/tests/opencode-models.test.ts +73 -0
  148. package/server/modules/providers/tests/opencode-sessions.test.ts +336 -0
  149. package/server/modules/providers/tests/provider-models.service.test.ts +318 -0
  150. package/server/modules/providers/tests/skills.test.ts +66 -0
  151. package/server/modules/websocket/services/chat-websocket.service.ts +21 -1
  152. package/server/modules/websocket/services/shell-websocket.service.ts +9 -0
  153. package/server/openai-codex.js +40 -4
  154. package/server/opencode-cli.js +336 -0
  155. package/server/opencode-cli.test.js +95 -0
  156. package/server/routes/agent.js +22 -8
  157. package/server/routes/commands.js +254 -233
  158. package/server/routes/cursor.js +2 -2
  159. package/server/routes/settings.js +1 -10
  160. package/server/routes/tests/commands.test.js +82 -0
  161. package/server/shared/interfaces.ts +45 -0
  162. package/server/shared/types.ts +88 -1
  163. package/server/shared/utils.ts +384 -0
  164. package/dist/assets/index-DdxLnCfK.css +0 -32
  165. package/dist-server/shared/modelConstants.js +0 -99
  166. package/dist-server/shared/modelConstants.js.map +0 -1
  167. package/shared/modelConstants.js +0 -107
@@ -0,0 +1,410 @@
1
+ import fsSync from 'node:fs';
2
+ import Database from 'better-sqlite3';
3
+ import { createNormalizedMessage, generateMessageId, getOpenCodeDatabasePath, normalizeProviderTimestamp, readObjectRecord, readJsonRecord, readOptionalString, } from '../../../../shared/utils.js';
4
+ const PROVIDER = 'opencode';
5
+ const openOpenCodeDatabase = () => {
6
+ const dbPath = getOpenCodeDatabasePath();
7
+ if (!fsSync.existsSync(dbPath)) {
8
+ return null;
9
+ }
10
+ return new Database(dbPath, { readonly: true, fileMustExist: true });
11
+ };
12
+ const formatToolContent = (value) => {
13
+ if (value === undefined || value === null) {
14
+ return '';
15
+ }
16
+ if (typeof value === 'string') {
17
+ return value;
18
+ }
19
+ try {
20
+ return JSON.stringify(value, null, 2);
21
+ }
22
+ catch {
23
+ return String(value);
24
+ }
25
+ };
26
+ /**
27
+ * OpenCode can persist the first prompt as a JSON string literal inside a text
28
+ * part, for example `"hello"` instead of `hello`. Decode only complete JSON
29
+ * string literals so normal assistant/user prose remains untouched.
30
+ */
31
+ const unwrapJsonStringLiteral = (value) => {
32
+ const trimmed = value.trim();
33
+ if (!trimmed.startsWith('"') || !trimmed.endsWith('"')) {
34
+ return value;
35
+ }
36
+ try {
37
+ const parsed = JSON.parse(trimmed);
38
+ return typeof parsed === 'string' ? parsed : value;
39
+ }
40
+ catch {
41
+ return value;
42
+ }
43
+ };
44
+ const extractText = (value) => {
45
+ if (typeof value === 'string') {
46
+ return unwrapJsonStringLiteral(value);
47
+ }
48
+ const record = readObjectRecord(value);
49
+ const text = readOptionalString(record?.text)
50
+ ?? readOptionalString(record?.content)
51
+ ?? '';
52
+ return unwrapJsonStringLiteral(text);
53
+ };
54
+ const hasUserRole = (value) => {
55
+ const record = readObjectRecord(value);
56
+ return readOptionalString(record?.role) === 'user';
57
+ };
58
+ const isUserTextEcho = (raw) => {
59
+ return readOptionalString(raw.role) === 'user'
60
+ || hasUserRole(raw.message)
61
+ || hasUserRole(raw.part);
62
+ };
63
+ const buildTokenUsage = (totals) => {
64
+ if (!totals) {
65
+ return undefined;
66
+ }
67
+ const inputTokens = totals.inputTokens;
68
+ const displayInputTokens = inputTokens + totals.cacheReadTokens;
69
+ const outputTokens = totals.outputTokens;
70
+ const used = inputTokens
71
+ + outputTokens
72
+ + totals.reasoningTokens
73
+ + totals.cacheReadTokens
74
+ + totals.cacheWriteTokens;
75
+ if (used <= 0) {
76
+ return undefined;
77
+ }
78
+ return {
79
+ used,
80
+ inputTokens: displayInputTokens,
81
+ outputTokens,
82
+ breakdown: {
83
+ input: displayInputTokens,
84
+ output: outputTokens,
85
+ },
86
+ };
87
+ };
88
+ const readOpenCodeSessionColumnTokenUsage = (db, sessionId) => {
89
+ const columns = db.prepare('PRAGMA table_info(session)').all();
90
+ const columnNames = new Set(columns.map((column) => column.name));
91
+ const requiredColumns = ['tokens_input', 'tokens_output', 'tokens_reasoning', 'tokens_cache_read', 'tokens_cache_write'];
92
+ if (!requiredColumns.every((column) => columnNames.has(column))) {
93
+ return undefined;
94
+ }
95
+ const row = db.prepare(`
96
+ SELECT
97
+ tokens_input AS inputTokens,
98
+ tokens_output AS outputTokens,
99
+ tokens_reasoning AS reasoningTokens,
100
+ tokens_cache_read AS cacheReadTokens,
101
+ tokens_cache_write AS cacheWriteTokens
102
+ FROM session
103
+ WHERE id = ?
104
+ `).get(sessionId);
105
+ if (!row) {
106
+ return undefined;
107
+ }
108
+ return buildTokenUsage({
109
+ inputTokens: Number(row.inputTokens ?? 0),
110
+ outputTokens: Number(row.outputTokens ?? 0),
111
+ reasoningTokens: Number(row.reasoningTokens ?? 0),
112
+ cacheReadTokens: Number(row.cacheReadTokens ?? 0),
113
+ cacheWriteTokens: Number(row.cacheWriteTokens ?? 0),
114
+ });
115
+ };
116
+ /**
117
+ * OpenCode stores per-message token counts on assistant `message.data` objects
118
+ * (see MessageV2.Assistant). Older DBs also had session-level counters; this
119
+ * matches current `opencode.db` layouts that only persist message JSON.
120
+ */
121
+ const aggregateOpenCodeSessionTokenUsage = (db, sessionId) => {
122
+ const sessionColumnUsage = readOpenCodeSessionColumnTokenUsage(db, sessionId);
123
+ if (sessionColumnUsage) {
124
+ return sessionColumnUsage;
125
+ }
126
+ const rows = db.prepare('SELECT data FROM message WHERE session_id = ?').all(sessionId);
127
+ let inputTokens = 0;
128
+ let outputTokens = 0;
129
+ let reasoningTokens = 0;
130
+ let cacheReadTokens = 0;
131
+ let cacheWriteTokens = 0;
132
+ for (const row of rows) {
133
+ const info = readJsonRecord(row.data);
134
+ if (readOptionalString(info?.role) !== 'assistant') {
135
+ continue;
136
+ }
137
+ const tokens = readObjectRecord(info?.tokens);
138
+ if (!tokens) {
139
+ continue;
140
+ }
141
+ inputTokens += Number(tokens.input ?? 0);
142
+ outputTokens += Number(tokens.output ?? 0);
143
+ reasoningTokens += Number(tokens.reasoning ?? 0);
144
+ const cache = readObjectRecord(tokens.cache);
145
+ cacheReadTokens += Number(cache?.read ?? 0);
146
+ cacheWriteTokens += Number(cache?.write ?? 0);
147
+ }
148
+ return buildTokenUsage({
149
+ inputTokens,
150
+ outputTokens,
151
+ reasoningTokens,
152
+ cacheReadTokens,
153
+ cacheWriteTokens,
154
+ });
155
+ };
156
+ export class OpenCodeSessionsProvider {
157
+ /**
158
+ * Normalizes live `opencode run --format json` events into frontend messages.
159
+ */
160
+ normalizeMessage(rawMessage, sessionId) {
161
+ const raw = readObjectRecord(rawMessage);
162
+ if (!raw) {
163
+ return [];
164
+ }
165
+ const type = readOptionalString(raw.type) ?? readOptionalString(raw.event);
166
+ const eventSessionId = readOptionalString(raw.sessionID) ?? readOptionalString(raw.sessionId) ?? sessionId;
167
+ const timestamp = normalizeProviderTimestamp(raw.time ?? raw.timestamp);
168
+ const baseId = readOptionalString(raw.id)
169
+ ?? readOptionalString(raw.messageID)
170
+ ?? generateMessageId('opencode');
171
+ if (type === 'text') {
172
+ // The client already renders an optimistic user bubble, so provider user
173
+ // echoes must not be streamed back as assistant text.
174
+ if (isUserTextEcho(raw)) {
175
+ return [];
176
+ }
177
+ const content = extractText(raw.text ?? raw.delta ?? raw.message);
178
+ if (!content.trim()) {
179
+ return [];
180
+ }
181
+ return [createNormalizedMessage({
182
+ id: baseId,
183
+ sessionId: eventSessionId,
184
+ timestamp,
185
+ provider: PROVIDER,
186
+ kind: 'stream_delta',
187
+ content,
188
+ })];
189
+ }
190
+ if (type === 'reasoning') {
191
+ const content = extractText(raw.text ?? raw.delta ?? raw.message);
192
+ if (!content.trim()) {
193
+ return [];
194
+ }
195
+ return [createNormalizedMessage({
196
+ id: baseId,
197
+ sessionId: eventSessionId,
198
+ timestamp,
199
+ provider: PROVIDER,
200
+ kind: 'thinking',
201
+ content,
202
+ })];
203
+ }
204
+ if (type === 'tool_use') {
205
+ const toolName = readOptionalString(raw.tool) ?? readOptionalString(raw.name) ?? 'Tool';
206
+ const toolId = readOptionalString(raw.callID) ?? readOptionalString(raw.toolCallId) ?? baseId;
207
+ const toolMessage = createNormalizedMessage({
208
+ id: baseId,
209
+ sessionId: eventSessionId,
210
+ timestamp,
211
+ provider: PROVIDER,
212
+ kind: 'tool_use',
213
+ toolName,
214
+ toolInput: raw.input ?? raw.arguments ?? {},
215
+ toolId,
216
+ });
217
+ if (raw.output !== undefined || raw.error !== undefined) {
218
+ toolMessage.toolResult = {
219
+ content: formatToolContent(raw.output ?? raw.error),
220
+ isError: raw.error !== undefined,
221
+ };
222
+ }
223
+ return [toolMessage];
224
+ }
225
+ if (type === 'error') {
226
+ return [createNormalizedMessage({
227
+ id: baseId,
228
+ sessionId: eventSessionId,
229
+ timestamp,
230
+ provider: PROVIDER,
231
+ kind: 'error',
232
+ content: readOptionalString(raw.error) ?? readOptionalString(raw.message) ?? 'Unknown OpenCode error',
233
+ })];
234
+ }
235
+ if (type === 'step_finish') {
236
+ return [createNormalizedMessage({
237
+ id: baseId,
238
+ sessionId: eventSessionId,
239
+ timestamp,
240
+ provider: PROVIDER,
241
+ kind: 'stream_end',
242
+ })];
243
+ }
244
+ return [];
245
+ }
246
+ /**
247
+ * Loads OpenCode history from the shared SQLite session database.
248
+ */
249
+ async fetchHistory(sessionId, options = {}) {
250
+ const { limit = null, offset = 0 } = options;
251
+ const db = openOpenCodeDatabase();
252
+ if (!db) {
253
+ return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
254
+ }
255
+ try {
256
+ const rows = db.prepare(`
257
+ SELECT
258
+ m.id AS message_id,
259
+ m.time_created AS message_time_created,
260
+ m.data AS message_data,
261
+ p.id AS part_id,
262
+ p.time_created AS part_time_created,
263
+ p.data AS part_data
264
+ FROM message m
265
+ LEFT JOIN part p
266
+ ON p.session_id = m.session_id
267
+ AND p.message_id = m.id
268
+ WHERE m.session_id = ?
269
+ ORDER BY
270
+ COALESCE(m.time_created, 0),
271
+ m.id,
272
+ COALESCE(p.time_created, 0),
273
+ p.id
274
+ `).all(sessionId);
275
+ const normalized = this.normalizeHistoryRows(rows, sessionId);
276
+ const tokenUsage = aggregateOpenCodeSessionTokenUsage(db, sessionId);
277
+ const normalizedOffset = Math.max(0, offset);
278
+ const normalizedLimit = limit === null ? null : Math.max(0, limit);
279
+ const total = normalized.length;
280
+ const messages = normalizedLimit === null
281
+ ? normalized
282
+ : normalized.slice(Math.max(0, total - normalizedOffset - normalizedLimit), Math.max(0, total - normalizedOffset));
283
+ return {
284
+ messages,
285
+ total,
286
+ hasMore: normalizedLimit === null
287
+ ? false
288
+ : Math.max(0, total - normalizedOffset - normalizedLimit) > 0,
289
+ offset: normalizedOffset,
290
+ limit: normalizedLimit,
291
+ tokenUsage,
292
+ };
293
+ }
294
+ catch (error) {
295
+ const message = error instanceof Error ? error.message : String(error);
296
+ console.warn(`[OpenCodeProvider] Failed to load session ${sessionId}:`, message);
297
+ return { messages: [], total: 0, hasMore: false, offset: 0, limit: null };
298
+ }
299
+ finally {
300
+ db.close();
301
+ }
302
+ }
303
+ normalizeHistoryRows(rows, sessionId) {
304
+ const normalized = [];
305
+ const emittedMessageErrors = new Set();
306
+ for (const row of rows) {
307
+ const timestamp = normalizeProviderTimestamp(row.part_time_created ?? row.message_time_created);
308
+ const baseId = `${row.message_id}_${row.part_id ?? normalized.length}`;
309
+ const messageInfo = readJsonRecord(row.message_data);
310
+ const messageRole = readOptionalString(messageInfo?.role);
311
+ if (messageInfo
312
+ && messageRole === 'assistant'
313
+ && messageInfo.error != null
314
+ && !emittedMessageErrors.has(row.message_id)) {
315
+ emittedMessageErrors.add(row.message_id);
316
+ normalized.push(createNormalizedMessage({
317
+ id: `${baseId}_error`,
318
+ sessionId,
319
+ timestamp,
320
+ provider: PROVIDER,
321
+ kind: 'error',
322
+ content: formatToolContent(messageInfo.error),
323
+ }));
324
+ }
325
+ if (!row.part_id) {
326
+ continue;
327
+ }
328
+ const partData = readJsonRecord(row.part_data) ?? {};
329
+ const partType = readOptionalString(partData.type);
330
+ if (!partType) {
331
+ continue;
332
+ }
333
+ if (partType === 'text') {
334
+ const content = extractText(partData);
335
+ if (content.trim()) {
336
+ normalized.push(createNormalizedMessage({
337
+ id: baseId,
338
+ sessionId,
339
+ timestamp,
340
+ provider: PROVIDER,
341
+ kind: 'text',
342
+ role: messageRole === 'user' ? 'user' : 'assistant',
343
+ content,
344
+ }));
345
+ }
346
+ continue;
347
+ }
348
+ if (partType === 'reasoning') {
349
+ const content = extractText(partData);
350
+ if (content.trim()) {
351
+ normalized.push(createNormalizedMessage({
352
+ id: baseId,
353
+ sessionId,
354
+ timestamp,
355
+ provider: PROVIDER,
356
+ kind: 'thinking',
357
+ content,
358
+ }));
359
+ }
360
+ continue;
361
+ }
362
+ if (partType === 'tool') {
363
+ const state = readObjectRecord(partData.state) ?? {};
364
+ const status = readOptionalString(state.status);
365
+ const toolMessage = createNormalizedMessage({
366
+ id: baseId,
367
+ sessionId,
368
+ timestamp,
369
+ provider: PROVIDER,
370
+ kind: 'tool_use',
371
+ toolName: readOptionalString(partData.tool) ?? 'Tool',
372
+ toolInput: state.input ?? partData.input ?? {},
373
+ toolId: readOptionalString(partData.callID) ?? row.part_id,
374
+ });
375
+ if (status === 'completed' || status === 'error') {
376
+ toolMessage.toolResult = {
377
+ content: formatToolContent(state.output ?? state.error),
378
+ isError: status === 'error',
379
+ };
380
+ }
381
+ normalized.push(toolMessage);
382
+ continue;
383
+ }
384
+ if (partType === 'step-finish') {
385
+ normalized.push(createNormalizedMessage({
386
+ id: baseId,
387
+ sessionId,
388
+ timestamp,
389
+ provider: PROVIDER,
390
+ kind: 'stream_end',
391
+ }));
392
+ continue;
393
+ }
394
+ if (partType === 'patch' || partType === 'agent') {
395
+ normalized.push(createNormalizedMessage({
396
+ id: baseId,
397
+ sessionId,
398
+ timestamp,
399
+ provider: PROVIDER,
400
+ kind: 'tool_use',
401
+ toolName: partType === 'patch' ? 'Patch' : 'Agent',
402
+ toolInput: partData,
403
+ toolId: row.part_id,
404
+ }));
405
+ }
406
+ }
407
+ return normalized;
408
+ }
409
+ }
410
+ //# sourceMappingURL=opencode-sessions.provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-sessions.provider.js","sourceRoot":"","sources":["../../../../../../server/modules/providers/list/opencode/opencode-sessions.provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ import { SkillsProvider } from '../../../../modules/providers/shared/skills/skills.provider.js';
4
+ import { addUniqueProviderSkillSource, findTopmostGitRoot, } from '../../../../shared/utils.js';
5
+ const OPENCODE_PROJECT_SKILL_DIRS = [
6
+ ['.opencode', 'skills'],
7
+ ['.claude', 'skills'],
8
+ ['.agents', 'skills'],
9
+ ];
10
+ const OPENCODE_USER_SKILL_DIRS = [
11
+ ['.config', 'opencode', 'skills'],
12
+ ['.claude', 'skills'],
13
+ ['.agents', 'skills'],
14
+ ];
15
+ export class OpenCodeSkillsProvider extends SkillsProvider {
16
+ constructor() {
17
+ super('opencode');
18
+ }
19
+ async getSkillSources(workspacePath) {
20
+ const sources = [];
21
+ const seenRootDirs = new Set();
22
+ const repoRoot = await findTopmostGitRoot(workspacePath);
23
+ for (const projectRoot of this.getProjectSearchRoots(workspacePath, repoRoot)) {
24
+ for (const skillDir of OPENCODE_PROJECT_SKILL_DIRS) {
25
+ // OpenCode intentionally reads Claude and Agents skill folders so users
26
+ // can reuse the same skill libraries across compatible coding agents.
27
+ addUniqueProviderSkillSource(sources, seenRootDirs, {
28
+ scope: 'project',
29
+ rootDir: path.join(projectRoot, ...skillDir),
30
+ commandPrefix: '/',
31
+ });
32
+ }
33
+ }
34
+ for (const skillDir of OPENCODE_USER_SKILL_DIRS) {
35
+ addUniqueProviderSkillSource(sources, seenRootDirs, {
36
+ scope: 'user',
37
+ rootDir: path.join(os.homedir(), ...skillDir),
38
+ commandPrefix: '/',
39
+ });
40
+ }
41
+ return sources;
42
+ }
43
+ getProjectSearchRoots(workspacePath, repoRoot) {
44
+ const roots = [];
45
+ const normalizedWorkspacePath = path.resolve(workspacePath);
46
+ const normalizedRepoRoot = repoRoot ? path.resolve(repoRoot) : null;
47
+ let currentPath = normalizedWorkspacePath;
48
+ while (true) {
49
+ roots.push(currentPath);
50
+ if (!normalizedRepoRoot || currentPath === normalizedRepoRoot) {
51
+ break;
52
+ }
53
+ const parentPath = path.dirname(currentPath);
54
+ if (parentPath === currentPath) {
55
+ break;
56
+ }
57
+ currentPath = parentPath;
58
+ }
59
+ return roots;
60
+ }
61
+ }
62
+ //# sourceMappingURL=opencode-skills.provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-skills.provider.js","sourceRoot":"","sources":["../../../../../../server/modules/providers/list/opencode/opencode-skills.provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,MAAM,sDAAsD,CAAC;AAEtF,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,2BAA2B,GAAG;IAClC,CAAC,WAAW,EAAE,QAAQ,CAAC;IACvB,CAAC,SAAS,EAAE,QAAQ,CAAC;IACrB,CAAC,SAAS,EAAE,QAAQ,CAAC;CACtB,CAAC;AAEF,MAAM,wBAAwB,GAAG;IAC/B,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC;IACjC,CAAC,SAAS,EAAE,QAAQ,CAAC;IACrB,CAAC,SAAS,EAAE,QAAQ,CAAC;CACtB,CAAC;AAEF,MAAM,OAAO,sBAAuB,SAAQ,cAAc;IACxD;QACE,KAAK,CAAC,UAAU,CAAC,CAAC;IACpB,CAAC;IAES,KAAK,CAAC,eAAe,CAAC,aAAqB;QACnD,MAAM,OAAO,GAA0B,EAAE,CAAC;QAC1C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;QACvC,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,aAAa,CAAC,CAAC;QAEzD,KAAK,MAAM,WAAW,IAAI,IAAI,CAAC,qBAAqB,CAAC,aAAa,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC9E,KAAK,MAAM,QAAQ,IAAI,2BAA2B,EAAE,CAAC;gBACnD,wEAAwE;gBACxE,sEAAsE;gBACtE,4BAA4B,CAAC,OAAO,EAAE,YAAY,EAAE;oBAClD,KAAK,EAAE,SAAS;oBAChB,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,QAAQ,CAAC;oBAC5C,aAAa,EAAE,GAAG;iBACnB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,KAAK,MAAM,QAAQ,IAAI,wBAAwB,EAAE,CAAC;YAChD,4BAA4B,CAAC,OAAO,EAAE,YAAY,EAAE;gBAClD,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,GAAG,QAAQ,CAAC;gBAC7C,aAAa,EAAE,GAAG;aACnB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,qBAAqB,CAAC,aAAqB,EAAE,QAAuB;QAC1E,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,uBAAuB,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC5D,MAAM,kBAAkB,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACpE,IAAI,WAAW,GAAG,uBAAuB,CAAC;QAE1C,OAAO,IAAI,EAAE,CAAC;YACZ,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACxB,IAAI,CAAC,kBAAkB,IAAI,WAAW,KAAK,kBAAkB,EAAE,CAAC;gBAC9D,MAAM;YACR,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAC7C,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;gBAC/B,MAAM;YACR,CAAC;YAED,WAAW,GAAG,UAAU,CAAC;QAC3B,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
@@ -0,0 +1,19 @@
1
+ import { OpenCodeProviderAuth } from '../../../../modules/providers/list/opencode/opencode-auth.provider.js';
2
+ import { OpenCodeProviderModels } from '../../../../modules/providers/list/opencode/opencode-models.provider.js';
3
+ import { OpenCodeMcpProvider } from '../../../../modules/providers/list/opencode/opencode-mcp.provider.js';
4
+ import { OpenCodeSessionSynchronizer } from '../../../../modules/providers/list/opencode/opencode-session-synchronizer.provider.js';
5
+ import { OpenCodeSessionsProvider } from '../../../../modules/providers/list/opencode/opencode-sessions.provider.js';
6
+ import { OpenCodeSkillsProvider } from '../../../../modules/providers/list/opencode/opencode-skills.provider.js';
7
+ import { AbstractProvider } from '../../../../modules/providers/shared/base/abstract.provider.js';
8
+ export class OpenCodeProvider extends AbstractProvider {
9
+ models = new OpenCodeProviderModels();
10
+ mcp = new OpenCodeMcpProvider();
11
+ auth = new OpenCodeProviderAuth();
12
+ skills = new OpenCodeSkillsProvider();
13
+ sessions = new OpenCodeSessionsProvider();
14
+ sessionSynchronizer = new OpenCodeSessionSynchronizer();
15
+ constructor() {
16
+ super('opencode');
17
+ }
18
+ }
19
+ //# sourceMappingURL=opencode.provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.provider.js","sourceRoot":"","sources":["../../../../../../server/modules/providers/list/opencode/opencode.provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6DAA6D,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,MAAM,+DAA+D,CAAC;AACvG,OAAO,EAAE,mBAAmB,EAAE,MAAM,4DAA4D,CAAC;AACjG,OAAO,EAAE,2BAA2B,EAAE,MAAM,6EAA6E,CAAC;AAC1H,OAAO,EAAE,wBAAwB,EAAE,MAAM,iEAAiE,CAAC;AAC3G,OAAO,EAAE,sBAAsB,EAAE,MAAM,+DAA+D,CAAC;AACvG,OAAO,EAAE,gBAAgB,EAAE,MAAM,sDAAsD,CAAC;AASxF,MAAM,OAAO,gBAAiB,SAAQ,gBAAgB;IAC3C,MAAM,GAAoB,IAAI,sBAAsB,EAAE,CAAC;IACvD,GAAG,GAAG,IAAI,mBAAmB,EAAE,CAAC;IAChC,IAAI,GAAkB,IAAI,oBAAoB,EAAE,CAAC;IACjD,MAAM,GAAoB,IAAI,sBAAsB,EAAE,CAAC;IACvD,QAAQ,GAAsB,IAAI,wBAAwB,EAAE,CAAC;IAC7D,mBAAmB,GAAiC,IAAI,2BAA2B,EAAE,CAAC;IAE/F;QACE,KAAK,CAAC,UAAU,CAAC,CAAC;IACpB,CAAC;CACF"}
@@ -2,12 +2,14 @@ import { ClaudeProvider } from '../../modules/providers/list/claude/claude.provi
2
2
  import { CodexProvider } from '../../modules/providers/list/codex/codex.provider.js';
3
3
  import { CursorProvider } from '../../modules/providers/list/cursor/cursor.provider.js';
4
4
  import { GeminiProvider } from '../../modules/providers/list/gemini/gemini.provider.js';
5
+ import { OpenCodeProvider } from '../../modules/providers/list/opencode/opencode.provider.js';
5
6
  import { AppError } from '../../shared/utils.js';
6
7
  const providers = {
7
8
  claude: new ClaudeProvider(),
8
9
  codex: new CodexProvider(),
9
10
  cursor: new CursorProvider(),
10
11
  gemini: new GeminiProvider(),
12
+ opencode: new OpenCodeProvider(),
11
13
  };
12
14
  /**
13
15
  * Central registry for resolving concrete provider implementations by id.
@@ -1 +1 @@
1
- {"version":3,"file":"provider.registry.js","sourceRoot":"","sources":["../../../../server/modules/providers/provider.registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,kDAAkD,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AAGpF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,MAAM,SAAS,GAAmC;IAChD,MAAM,EAAE,IAAI,cAAc,EAAE;IAC5B,KAAK,EAAE,IAAI,aAAa,EAAE;IAC1B,MAAM,EAAE,IAAI,cAAc,EAAE;IAC5B,MAAM,EAAE,IAAI,cAAc,EAAE;CAC7B,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,aAAa;QACX,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,eAAe,CAAC,QAAgB;QAC9B,MAAM,GAAG,GAAG,QAAuB,CAAC;QACpC,MAAM,gBAAgB,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,MAAM,IAAI,QAAQ,CAAC,yBAAyB,QAAQ,IAAI,EAAE;gBACxD,IAAI,EAAE,sBAAsB;gBAC5B,UAAU,EAAE,GAAG;aAChB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,gBAAgB,CAAC;IAC1B,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"provider.registry.js","sourceRoot":"","sources":["../../../../server/modules/providers/provider.registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,kDAAkD,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wDAAwD,CAAC;AAG1F,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C,MAAM,SAAS,GAAmC;IAChD,MAAM,EAAE,IAAI,cAAc,EAAE;IAC5B,KAAK,EAAE,IAAI,aAAa,EAAE;IAC1B,MAAM,EAAE,IAAI,cAAc,EAAE;IAC5B,MAAM,EAAE,IAAI,cAAc,EAAE;IAC5B,QAAQ,EAAE,IAAI,gBAAgB,EAAE;CACjC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,aAAa;QACX,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED,eAAe,CAAC,QAAgB;QAC9B,MAAM,GAAG,GAAG,QAAuB,CAAC;QACpC,MAAM,gBAAgB,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,MAAM,IAAI,QAAQ,CAAC,yBAAyB,QAAQ,IAAI,EAAE;gBACxD,IAAI,EAAE,sBAAsB;gBAC5B,UAAU,EAAE,GAAG;aAChB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,gBAAgB,CAAC;IAC1B,CAAC;CACF,CAAC"}
@@ -2,6 +2,7 @@ import express from 'express';
2
2
  import { queuedMessagesDb } from '../../modules/database/index.js';
3
3
  import { providerAuthService } from '../../modules/providers/services/provider-auth.service.js';
4
4
  import { providerMcpService } from '../../modules/providers/services/mcp.service.js';
5
+ import { providerModelsService } from '../../modules/providers/services/provider-models.service.js';
5
6
  import { providerSkillsService } from '../../modules/providers/services/skills.service.js';
6
7
  import { sessionConversationsSearchService } from '../../modules/providers/services/session-conversations-search.service.js';
7
8
  import { sessionsService } from '../../modules/providers/services/sessions.service.js';
@@ -163,7 +164,11 @@ const parseMcpUpsertPayload = (payload) => {
163
164
  };
164
165
  const parseProvider = (value) => {
165
166
  const normalized = normalizeProviderParam(value);
166
- if (normalized === 'claude' || normalized === 'codex' || normalized === 'cursor' || normalized === 'gemini') {
167
+ if (normalized === 'claude'
168
+ || normalized === 'codex'
169
+ || normalized === 'cursor'
170
+ || normalized === 'gemini'
171
+ || normalized === 'opencode') {
167
172
  return normalized;
168
173
  }
169
174
  throw new AppError(`Unsupported provider "${normalized}".`, {
@@ -271,11 +276,47 @@ const parseSessionSearchLimit = (value) => {
271
276
  }
272
277
  return Math.max(1, Math.min(parsed, 100));
273
278
  };
279
+ const parseChangeActiveModelPayload = (payload) => {
280
+ if (!payload || typeof payload !== 'object') {
281
+ throw new AppError('Request body must be an object.', {
282
+ code: 'INVALID_REQUEST_BODY',
283
+ statusCode: 400,
284
+ });
285
+ }
286
+ const body = payload;
287
+ const model = readOptionalQueryString(body.model);
288
+ if (!model) {
289
+ throw new AppError('model is required.', {
290
+ code: 'MODEL_REQUIRED',
291
+ statusCode: 400,
292
+ });
293
+ }
294
+ return {
295
+ sessionId: '',
296
+ model,
297
+ };
298
+ };
274
299
  router.get('/:provider/auth/status', asyncHandler(async (req, res) => {
275
300
  const provider = parseProvider(req.params.provider);
276
301
  const status = await providerAuthService.getProviderAuthStatus(provider);
277
302
  res.json(createApiSuccessResponse(status));
278
303
  }));
304
+ router.get('/:provider/models', asyncHandler(async (req, res) => {
305
+ const provider = parseProvider(req.params.provider);
306
+ const bypassCache = parseOptionalBooleanQuery(req.query.bypassCache, 'bypassCache') ?? false;
307
+ const result = await providerModelsService.getProviderModels(provider, { bypassCache });
308
+ res.json(createApiSuccessResponse({ provider, models: result.models, cache: result.cache }));
309
+ }));
310
+ router.post('/:provider/sessions/:sessionId/active-model', asyncHandler(async (req, res) => {
311
+ const provider = parseProvider(req.params.provider);
312
+ const sessionId = parseSessionId(req.params.sessionId);
313
+ const payload = parseChangeActiveModelPayload(req.body);
314
+ const result = await providerModelsService.changeActiveModel(provider, {
315
+ ...payload,
316
+ sessionId,
317
+ });
318
+ res.json(createApiSuccessResponse(result));
319
+ }));
279
320
  // ----------------- Skills routes -----------------
280
321
  router.get('/:provider/skills', asyncHandler(async (req, res) => {
281
322
  const provider = parseProvider(req.params.provider);