@axhub/genie 0.1.3 → 0.1.4
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/assets/index-C_OkRKiC.js +1249 -0
- package/dist/assets/index-CtRxrKDm.css +32 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/database/auth.db +0 -0
- package/server/index.js +1 -17
- package/server/projects.js +3 -72
- package/server/routes/agent.js +9 -54
- package/shared/modelConstants.js +0 -14
- package/dist/assets/index-BYKlB9hp.css +0 -32
- package/dist/assets/index-YzZ559FA.js +0 -1249
- package/dist/icons/opencode-white.svg +0 -4
- package/dist/icons/opencode.svg +0 -10
- package/server/opencode-manager.js +0 -605
- package/server/opencode-sdk.js +0 -474
- package/server/routes/opencode.js +0 -99
package/server/opencode-sdk.js
DELETED
|
@@ -1,474 +0,0 @@
|
|
|
1
|
-
import { getOpencodeClient, resolveOpencodeModel } from './opencode-manager.js';
|
|
2
|
-
|
|
3
|
-
const activeOpencodeSessions = new Map();
|
|
4
|
-
|
|
5
|
-
function getEventSessionId(event) {
|
|
6
|
-
if (!event || typeof event !== 'object') return null;
|
|
7
|
-
|
|
8
|
-
const direct = event.sessionID || event.sessionId;
|
|
9
|
-
if (typeof direct === 'string' && direct.trim()) {
|
|
10
|
-
return direct.trim();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const props = event.properties || {};
|
|
14
|
-
const propId = props.sessionID || props.sessionId;
|
|
15
|
-
if (typeof propId === 'string' && propId.trim()) {
|
|
16
|
-
return propId.trim();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (props.info && typeof props.info === 'object') {
|
|
20
|
-
const infoId = props.info.sessionID || props.info.sessionId;
|
|
21
|
-
if (typeof infoId === 'string' && infoId.trim()) {
|
|
22
|
-
return infoId.trim();
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (props.part && typeof props.part === 'object') {
|
|
27
|
-
const partId = props.part.sessionID || props.part.sessionId;
|
|
28
|
-
if (typeof partId === 'string' && partId.trim()) {
|
|
29
|
-
return partId.trim();
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function toIso(value, fallback = null) {
|
|
37
|
-
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
38
|
-
return new Date(value).toISOString();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (typeof value === 'string' && value.trim()) {
|
|
42
|
-
const parsed = Date.parse(value);
|
|
43
|
-
if (!Number.isNaN(parsed)) {
|
|
44
|
-
return new Date(parsed).toISOString();
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return fallback;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function parsePermissionMode(permissionMode) {
|
|
52
|
-
switch (permissionMode) {
|
|
53
|
-
case 'acceptEdits':
|
|
54
|
-
return {
|
|
55
|
-
edit: 'allow',
|
|
56
|
-
bash: 'allow',
|
|
57
|
-
webfetch: 'allow'
|
|
58
|
-
};
|
|
59
|
-
case 'bypassPermissions':
|
|
60
|
-
return {
|
|
61
|
-
edit: 'allow',
|
|
62
|
-
bash: 'allow',
|
|
63
|
-
webfetch: 'allow',
|
|
64
|
-
external_directory: 'allow'
|
|
65
|
-
};
|
|
66
|
-
case 'default':
|
|
67
|
-
default:
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function normalizeToolName(toolName = 'tool') {
|
|
73
|
-
if (toolName === 'bash') return 'Bash';
|
|
74
|
-
return toolName;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function mapPartToDelta(part, context) {
|
|
78
|
-
if (!part || typeof part !== 'object') return null;
|
|
79
|
-
|
|
80
|
-
const partId = part.id;
|
|
81
|
-
const messageId = part.messageID;
|
|
82
|
-
|
|
83
|
-
if (part.type === 'text') {
|
|
84
|
-
const currentText = typeof part.text === 'string' ? part.text : '';
|
|
85
|
-
const previousText = context.textPartCache.get(partId) || '';
|
|
86
|
-
let delta = currentText;
|
|
87
|
-
|
|
88
|
-
if (currentText.startsWith(previousText)) {
|
|
89
|
-
delta = currentText.slice(previousText.length);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
context.textPartCache.set(partId, currentText);
|
|
93
|
-
|
|
94
|
-
if (!delta) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
type: 'text_delta',
|
|
100
|
-
messageId,
|
|
101
|
-
partId,
|
|
102
|
-
delta,
|
|
103
|
-
timestamp: toIso(part.time?.start, new Date().toISOString())
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (part.type === 'reasoning') {
|
|
108
|
-
const currentText = typeof part.text === 'string' ? part.text : '';
|
|
109
|
-
const previousText = context.reasoningPartCache.get(partId) || '';
|
|
110
|
-
let delta = currentText;
|
|
111
|
-
|
|
112
|
-
if (currentText.startsWith(previousText)) {
|
|
113
|
-
delta = currentText.slice(previousText.length);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
context.reasoningPartCache.set(partId, currentText);
|
|
117
|
-
|
|
118
|
-
if (!delta) {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
type: 'reasoning_delta',
|
|
124
|
-
messageId,
|
|
125
|
-
partId,
|
|
126
|
-
delta,
|
|
127
|
-
timestamp: toIso(part.time?.start, new Date().toISOString())
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (part.type === 'tool') {
|
|
132
|
-
const callId = part.callID || part.id;
|
|
133
|
-
const toolName = normalizeToolName(part.tool);
|
|
134
|
-
const state = part.state || {};
|
|
135
|
-
|
|
136
|
-
let toolInput = '';
|
|
137
|
-
if (state.input && typeof state.input === 'object') {
|
|
138
|
-
if (typeof state.input.command === 'string') {
|
|
139
|
-
toolInput = state.input.command;
|
|
140
|
-
} else {
|
|
141
|
-
try {
|
|
142
|
-
toolInput = JSON.stringify(state.input);
|
|
143
|
-
} catch {
|
|
144
|
-
toolInput = String(state.input);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
let toolOutput = null;
|
|
150
|
-
if (state.output !== undefined && state.output !== null) {
|
|
151
|
-
toolOutput = typeof state.output === 'string' ? state.output : JSON.stringify(state.output);
|
|
152
|
-
} else if (state.metadata?.output !== undefined && state.metadata?.output !== null) {
|
|
153
|
-
toolOutput = typeof state.metadata.output === 'string'
|
|
154
|
-
? state.metadata.output
|
|
155
|
-
: JSON.stringify(state.metadata.output);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
type: 'tool_update',
|
|
160
|
-
messageId,
|
|
161
|
-
partId,
|
|
162
|
-
toolCallId: callId,
|
|
163
|
-
toolName,
|
|
164
|
-
status: state.status || 'pending',
|
|
165
|
-
toolInput,
|
|
166
|
-
toolOutput,
|
|
167
|
-
exitCode: typeof state.metadata?.exit === 'number' ? state.metadata.exit : undefined,
|
|
168
|
-
timestamp: toIso(state.time?.start || part.time?.start, new Date().toISOString())
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (part.type === 'step-finish' && part.tokens) {
|
|
173
|
-
const input = Number(part.tokens.input || 0);
|
|
174
|
-
const output = Number(part.tokens.output || 0);
|
|
175
|
-
const reasoning = Number(part.tokens.reasoning || 0);
|
|
176
|
-
const cacheRead = Number(part.tokens.cache?.read || 0);
|
|
177
|
-
const cacheWrite = Number(part.tokens.cache?.write || 0);
|
|
178
|
-
|
|
179
|
-
return {
|
|
180
|
-
type: 'token_usage',
|
|
181
|
-
usage: {
|
|
182
|
-
used: input + output + reasoning + cacheRead + cacheWrite,
|
|
183
|
-
total: 0,
|
|
184
|
-
percentage: null,
|
|
185
|
-
unsupported: true,
|
|
186
|
-
message: 'OpenCode token total is unavailable from current event payload',
|
|
187
|
-
breakdown: {
|
|
188
|
-
input,
|
|
189
|
-
output,
|
|
190
|
-
reasoning,
|
|
191
|
-
cacheRead,
|
|
192
|
-
cacheCreation: cacheWrite
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function sendMessage(ws, data) {
|
|
202
|
-
try {
|
|
203
|
-
if (ws.isSSEStreamWriter || ws.isWebSocketWriter) {
|
|
204
|
-
ws.send(data);
|
|
205
|
-
} else if (typeof ws.send === 'function') {
|
|
206
|
-
ws.send(JSON.stringify(data));
|
|
207
|
-
}
|
|
208
|
-
} catch (error) {
|
|
209
|
-
console.error('[OpenCode] Error sending message:', error);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function updateSessionId(session, ws, nextSessionId, broadcastUpdate = false) {
|
|
214
|
-
if (!nextSessionId || nextSessionId === session.sessionId) return;
|
|
215
|
-
|
|
216
|
-
const previousSessionId = session.sessionId;
|
|
217
|
-
session.sessionId = nextSessionId;
|
|
218
|
-
|
|
219
|
-
if (previousSessionId && activeOpencodeSessions.has(previousSessionId)) {
|
|
220
|
-
activeOpencodeSessions.delete(previousSessionId);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
activeOpencodeSessions.set(nextSessionId, session);
|
|
224
|
-
|
|
225
|
-
if (ws?.setSessionId && typeof ws.setSessionId === 'function') {
|
|
226
|
-
ws.setSessionId(nextSessionId);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (broadcastUpdate) {
|
|
230
|
-
sendMessage(ws, {
|
|
231
|
-
type: 'session-created',
|
|
232
|
-
sessionId: nextSessionId,
|
|
233
|
-
provider: 'opencode'
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export async function queryOpencode(command, options = {}, ws) {
|
|
239
|
-
const {
|
|
240
|
-
sessionId,
|
|
241
|
-
cwd,
|
|
242
|
-
projectPath,
|
|
243
|
-
model,
|
|
244
|
-
permissionMode = 'default'
|
|
245
|
-
} = options;
|
|
246
|
-
|
|
247
|
-
const workingDirectory = cwd || projectPath || process.cwd();
|
|
248
|
-
|
|
249
|
-
let client;
|
|
250
|
-
let currentSessionId = sessionId;
|
|
251
|
-
|
|
252
|
-
const sessionState = {
|
|
253
|
-
sessionId: currentSessionId || null,
|
|
254
|
-
status: 'running',
|
|
255
|
-
startedAt: new Date().toISOString(),
|
|
256
|
-
abortController: new AbortController(),
|
|
257
|
-
textPartCache: new Map(),
|
|
258
|
-
reasoningPartCache: new Map()
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
try {
|
|
262
|
-
client = await getOpencodeClient({ directory: workingDirectory });
|
|
263
|
-
|
|
264
|
-
if (!currentSessionId) {
|
|
265
|
-
const created = await client.session.create({
|
|
266
|
-
query: { directory: workingDirectory },
|
|
267
|
-
body: {
|
|
268
|
-
title: 'OpenCode Session'
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
currentSessionId = created?.data?.id;
|
|
273
|
-
if (!currentSessionId) {
|
|
274
|
-
throw new Error('Failed to create OpenCode session');
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
sessionState.sessionId = currentSessionId;
|
|
279
|
-
activeOpencodeSessions.set(currentSessionId, sessionState);
|
|
280
|
-
|
|
281
|
-
if (ws?.setSessionId && typeof ws.setSessionId === 'function') {
|
|
282
|
-
ws.setSessionId(currentSessionId);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
sendMessage(ws, {
|
|
286
|
-
type: 'session-created',
|
|
287
|
-
sessionId: currentSessionId,
|
|
288
|
-
provider: 'opencode'
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
const modelSelection = resolveOpencodeModel(model);
|
|
292
|
-
const permission = parsePermissionMode(permissionMode);
|
|
293
|
-
|
|
294
|
-
const events = await client.event.subscribe({
|
|
295
|
-
query: { directory: workingDirectory },
|
|
296
|
-
signal: sessionState.abortController.signal
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
await client.session.promptAsync({
|
|
300
|
-
path: { id: currentSessionId },
|
|
301
|
-
query: { directory: workingDirectory },
|
|
302
|
-
body: {
|
|
303
|
-
model: modelSelection,
|
|
304
|
-
permission: permission || undefined,
|
|
305
|
-
parts: [{
|
|
306
|
-
type: 'text',
|
|
307
|
-
text: command
|
|
308
|
-
}]
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
for await (const event of events.stream) {
|
|
313
|
-
if (!event || sessionState.status !== 'running') {
|
|
314
|
-
break;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const eventSessionId = getEventSessionId(event);
|
|
318
|
-
if (eventSessionId) {
|
|
319
|
-
updateSessionId(sessionState, ws, eventSessionId, true);
|
|
320
|
-
currentSessionId = sessionState.sessionId;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (eventSessionId && currentSessionId && eventSessionId !== currentSessionId) {
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (event.type === 'server.connected') {
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (event.type === 'message.part.updated') {
|
|
332
|
-
const part = event.properties?.part;
|
|
333
|
-
const deltaEvent = mapPartToDelta(part, sessionState);
|
|
334
|
-
if (deltaEvent) {
|
|
335
|
-
sendMessage(ws, {
|
|
336
|
-
type: 'opencode-response',
|
|
337
|
-
data: deltaEvent,
|
|
338
|
-
sessionId: currentSessionId
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
if (deltaEvent.type === 'token_usage') {
|
|
342
|
-
sendMessage(ws, {
|
|
343
|
-
type: 'token-budget',
|
|
344
|
-
data: deltaEvent.usage,
|
|
345
|
-
sessionId: currentSessionId
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
if (event.type === 'message.updated') {
|
|
354
|
-
const info = event.properties?.info;
|
|
355
|
-
if (info?.error?.data?.message) {
|
|
356
|
-
sendMessage(ws, {
|
|
357
|
-
type: 'opencode-response',
|
|
358
|
-
data: {
|
|
359
|
-
type: 'error',
|
|
360
|
-
message: info.error.data.message
|
|
361
|
-
},
|
|
362
|
-
sessionId: currentSessionId
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (event.type === 'session.status') {
|
|
369
|
-
sendMessage(ws, {
|
|
370
|
-
type: 'opencode-response',
|
|
371
|
-
data: {
|
|
372
|
-
type: 'session_status',
|
|
373
|
-
status: event.properties?.status?.type || 'unknown'
|
|
374
|
-
},
|
|
375
|
-
sessionId: currentSessionId
|
|
376
|
-
});
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (event.type === 'session.error') {
|
|
381
|
-
sendMessage(ws, {
|
|
382
|
-
type: 'opencode-response',
|
|
383
|
-
data: {
|
|
384
|
-
type: 'error',
|
|
385
|
-
message: event.properties?.error?.data?.message || 'OpenCode session failed'
|
|
386
|
-
},
|
|
387
|
-
sessionId: currentSessionId
|
|
388
|
-
});
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
if (event.type === 'session.idle') {
|
|
393
|
-
break;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
if (sessionState.status === 'running') {
|
|
398
|
-
sendMessage(ws, {
|
|
399
|
-
type: 'opencode-complete',
|
|
400
|
-
sessionId: currentSessionId,
|
|
401
|
-
actualSessionId: currentSessionId
|
|
402
|
-
});
|
|
403
|
-
sessionState.status = 'completed';
|
|
404
|
-
}
|
|
405
|
-
} catch (error) {
|
|
406
|
-
if (sessionState.status === 'aborted') {
|
|
407
|
-
sendMessage(ws, {
|
|
408
|
-
type: 'opencode-complete',
|
|
409
|
-
sessionId: currentSessionId,
|
|
410
|
-
actualSessionId: currentSessionId,
|
|
411
|
-
aborted: true
|
|
412
|
-
});
|
|
413
|
-
} else {
|
|
414
|
-
sendMessage(ws, {
|
|
415
|
-
type: 'opencode-error',
|
|
416
|
-
error: error.message,
|
|
417
|
-
sessionId: currentSessionId
|
|
418
|
-
});
|
|
419
|
-
sessionState.status = 'failed';
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
export function abortOpencodeSession(sessionId) {
|
|
425
|
-
const session = activeOpencodeSessions.get(sessionId);
|
|
426
|
-
if (!session) {
|
|
427
|
-
return false;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
session.status = 'aborted';
|
|
431
|
-
try {
|
|
432
|
-
session.abortController.abort();
|
|
433
|
-
} catch {
|
|
434
|
-
// no-op
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
return true;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
export function isOpencodeSessionActive(sessionId) {
|
|
441
|
-
const session = activeOpencodeSessions.get(sessionId);
|
|
442
|
-
return session?.status === 'running';
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
export function getActiveOpencodeSessions() {
|
|
446
|
-
const sessions = [];
|
|
447
|
-
|
|
448
|
-
for (const [id, session] of activeOpencodeSessions.entries()) {
|
|
449
|
-
if (session.status === 'running') {
|
|
450
|
-
sessions.push({
|
|
451
|
-
id,
|
|
452
|
-
status: session.status,
|
|
453
|
-
startedAt: session.startedAt
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
return sessions;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
setInterval(() => {
|
|
462
|
-
const now = Date.now();
|
|
463
|
-
const maxAge = 30 * 60 * 1000;
|
|
464
|
-
|
|
465
|
-
for (const [id, session] of activeOpencodeSessions.entries()) {
|
|
466
|
-
if (session.status === 'running') continue;
|
|
467
|
-
|
|
468
|
-
const startedAt = new Date(session.startedAt).getTime();
|
|
469
|
-
if (now - startedAt > maxAge) {
|
|
470
|
-
activeOpencodeSessions.delete(id);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}, 5 * 60 * 1000);
|
|
474
|
-
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import {
|
|
3
|
-
listOpencodeSessions,
|
|
4
|
-
getOpencodeSessionMessages,
|
|
5
|
-
deleteOpencodeSession,
|
|
6
|
-
listOpencodeModels,
|
|
7
|
-
getOpencodeStatus
|
|
8
|
-
} from '../opencode-manager.js';
|
|
9
|
-
|
|
10
|
-
const router = express.Router();
|
|
11
|
-
|
|
12
|
-
router.get('/sessions', async (req, res) => {
|
|
13
|
-
try {
|
|
14
|
-
const { projectPath } = req.query;
|
|
15
|
-
|
|
16
|
-
if (!projectPath || typeof projectPath !== 'string') {
|
|
17
|
-
return res.status(400).json({
|
|
18
|
-
success: false,
|
|
19
|
-
error: 'projectPath query parameter required'
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const sessions = await listOpencodeSessions(projectPath);
|
|
24
|
-
res.json({ success: true, sessions });
|
|
25
|
-
} catch (error) {
|
|
26
|
-
console.error('Error fetching OpenCode sessions:', error);
|
|
27
|
-
res.status(500).json({ success: false, error: error.message });
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
router.get('/sessions/:sessionId/messages', async (req, res) => {
|
|
32
|
-
try {
|
|
33
|
-
const { sessionId } = req.params;
|
|
34
|
-
const { limit, offset, projectPath } = req.query;
|
|
35
|
-
|
|
36
|
-
const result = await getOpencodeSessionMessages(sessionId, {
|
|
37
|
-
directory: typeof projectPath === 'string' ? projectPath : null,
|
|
38
|
-
limit: limit ? parseInt(limit, 10) : null,
|
|
39
|
-
offset: offset ? parseInt(offset, 10) : 0
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
res.json({ success: true, ...result });
|
|
43
|
-
} catch (error) {
|
|
44
|
-
console.error('Error fetching OpenCode session messages:', error);
|
|
45
|
-
res.status(500).json({ success: false, error: error.message });
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
router.delete('/sessions/:sessionId', async (req, res) => {
|
|
50
|
-
try {
|
|
51
|
-
const { sessionId } = req.params;
|
|
52
|
-
const { projectPath } = req.query;
|
|
53
|
-
|
|
54
|
-
const deleted = await deleteOpencodeSession(sessionId, {
|
|
55
|
-
directory: typeof projectPath === 'string' ? projectPath : null
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
res.json({ success: deleted });
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error(`Error deleting OpenCode session ${req.params.sessionId}:`, error);
|
|
61
|
-
res.status(500).json({ success: false, error: error.message });
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
router.get('/models', async (req, res) => {
|
|
66
|
-
try {
|
|
67
|
-
const { projectPath } = req.query;
|
|
68
|
-
|
|
69
|
-
const models = await listOpencodeModels({
|
|
70
|
-
directory: typeof projectPath === 'string' ? projectPath : null
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
res.json({
|
|
74
|
-
success: true,
|
|
75
|
-
models: models.options,
|
|
76
|
-
defaultModel: models.defaultModel
|
|
77
|
-
});
|
|
78
|
-
} catch (error) {
|
|
79
|
-
console.error('Error loading OpenCode models:', error);
|
|
80
|
-
res.status(500).json({ success: false, error: error.message });
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
router.get('/status', async (req, res) => {
|
|
85
|
-
try {
|
|
86
|
-
const status = await getOpencodeStatus();
|
|
87
|
-
res.json(status);
|
|
88
|
-
} catch (error) {
|
|
89
|
-
console.error('Error checking OpenCode status:', error);
|
|
90
|
-
res.status(500).json({
|
|
91
|
-
authenticated: false,
|
|
92
|
-
email: null,
|
|
93
|
-
error: error.message
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
export default router;
|
|
99
|
-
|