Files
XCDesktop/api/modules/opencode/routes.ts

223 lines
7.1 KiB
TypeScript
Raw Normal View History

import { Router } from 'express'
import { createOpencodeClient } from '@opencode-ai/sdk'
const OPENCODE_URL = 'http://localhost:4096'
async function getClient() {
console.log('[OpenCode] Creating client for URL:', OPENCODE_URL)
return createOpencodeClient({
baseUrl: OPENCODE_URL,
})
}
export function createOpencodeRoutes(): Router {
const router = Router()
router.get('/', async (req, res) => {
const action = req.query.action as string | undefined
try {
const client = await getClient()
if (action === 'list-sessions') {
const response = await client.session.list()
const sessions = (response.data || []).map((session: { id: string; title?: string; createdAt?: string }) => ({
id: session.id,
title: session.title || 'New Chat',
createdAt: session.createdAt ? new Date(session.createdAt).getTime() : Date.now(),
}))
res.json({ sessions })
return
}
res.status(400).json({ error: 'Unknown action' })
} catch (error: unknown) {
console.error('API error:', error)
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
res.status(500).json({ error: errorMessage })
}
})
router.post('/', async (req, res) => {
try {
const body = req.body
const { action, sessionId, title, text, attachments } = body
console.log('[OpenCode] Received action:', action, 'sessionId:', sessionId)
const client = await getClient()
console.log('[OpenCode] Client created successfully')
if (action === 'create-session') {
try {
const response = await client.session.create({
body: { title: title || 'New Chat' },
})
if (response.error) {
res.status(500).json({ error: 'OpenCode error', details: response.error })
return
}
const sessionData = response.data as { id: string; title?: string; createdAt?: string } | undefined
if (sessionData) {
res.json({
session: {
id: sessionData.id,
title: sessionData.title,
createdAt: sessionData.createdAt ? new Date(sessionData.createdAt).getTime() : Date.now(),
},
})
return
}
res.status(500).json({ error: 'Failed to create session - no data' })
return
} catch (e: unknown) {
console.error('Create session exception:', e)
const errorMessage = e instanceof Error ? e.message : 'Unknown error'
res.status(500).json({ error: errorMessage, exception: true })
return
}
}
if (action === 'delete-session') {
const response = await client.session.delete({
path: { id: sessionId },
})
res.json({ success: response.data })
return
}
if (action === 'get-session') {
const response = await client.session.get({
path: { id: sessionId },
})
if (response.data) {
res.json({
title: response.data.title || 'New Chat',
})
return
}
res.status(404).json({ error: 'Session not found' })
return
}
if (action === 'get-messages') {
const limit = body.limit || 20
const response = await client.session.messages({
path: { id: sessionId },
query: { limit }
})
const messages = (response.data || []).map((item: { info?: { id?: string; role?: string; content?: string; createdAt?: string }; content?: string; parts?: unknown[]; id?: string; role?: string }) => {
let content = item.info?.content || item.content || ''
const parts = item.parts as Array<{ type: string; text?: string }> | undefined
if (parts && parts.length > 0) {
const textParts = parts
.filter((p) => p.type === 'text')
.map((p) => p.text)
.join('')
if (textParts) {
content = textParts
}
}
return {
id: item.info?.id || item.id,
role: item.info?.role || item.role,
content: content,
parts: item.parts || [],
createdAt: item.info?.createdAt,
}
})
const hasMore = messages.length >= limit
res.json({ messages, hasMore })
return
}
if (action === 'prompt') {
const parts: Array<{ type: 'text'; text: string } | { type: 'file'; url: string; name: string; mime: string }> = []
if (attachments && attachments.length > 0) {
for (const att of attachments as Array<{ url: string; name: string; mediaType: string }>) {
parts.push({
type: 'file',
url: att.url,
name: att.name,
mime: att.mediaType,
})
}
}
parts.push({ type: 'text', text })
try {
const response = await client.session.prompt({
path: { id: sessionId },
body: { parts },
})
if (response.data) {
res.json({
message: {
id: (response.data.info as { id?: string })?.id || crypto.randomUUID(),
role: (response.data.info as { role?: string })?.role || 'assistant',
content: '',
parts: response.data.parts || [],
createdAt: new Date().toISOString(),
},
})
return
}
res.status(500).json({ error: 'Failed to get response', details: response.error })
return
} catch (e: unknown) {
console.error('Prompt exception:', e)
const errorMessage = e instanceof Error ? e.message : 'Unknown error'
res.status(500).json({ error: errorMessage })
return
}
}
if (action === 'summarize-session') {
try {
await client.session.summarize({
path: { id: sessionId },
})
const response = await client.session.get({
path: { id: sessionId },
})
if (response.data) {
res.json({
title: response.data.title || 'New Chat',
})
return
}
res.status(500).json({ error: 'Failed to get session' })
return
} catch (e: unknown) {
console.error('Summarize error:', e)
const errorMessage = e instanceof Error ? e.message : 'Unknown error'
res.status(500).json({ error: errorMessage })
return
}
}
res.status(400).json({ error: 'Unknown action' })
} catch (error: unknown) {
console.error('API error:', error)
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
res.status(500).json({ error: errorMessage })
}
})
return router
}