-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathopencode.ts
More file actions
151 lines (133 loc) · 4.72 KB
/
Copy pathopencode.ts
File metadata and controls
151 lines (133 loc) · 4.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import Database from 'better-sqlite3'
import { UnifiedSession, SessionMessagePreview } from '../../shared/types'
import { homedir } from 'os'
import { join } from 'path'
import { existsSync } from 'fs'
function extractProjectName(path: string | null): string | null {
if (!path) return null
if (path.startsWith('/private/var/folders/') || path.startsWith('/var/folders/')) return null
const parts = path.replace(/\/$/, '').split('/')
return parts[parts.length - 1] || null
}
export async function scanOpencode(): Promise<UnifiedSession[]> {
const dbPath = join(homedir(), '.local', 'share', 'opencode', 'opencode.db')
if (!existsSync(dbPath)) return []
const openCodeDb = new Database(dbPath, { readonly: true })
const sessions: UnifiedSession[] = []
try {
const rows = openCodeDb.prepare(`
SELECT
s.id, s.title, s.directory, s.model, s.cost,
s.tokens_input, s.tokens_output, s.tokens_reasoning,
s.time_created, s.time_updated,
(SELECT COUNT(*) FROM message m WHERE m.session_id = s.id) as msg_count
FROM session s
ORDER BY s.time_updated DESC
`).all() as Array<{
id: string; title: string | null; directory: string | null;
model: string | null; cost: number | null;
tokens_input: number | null; tokens_output: number | null;
tokens_reasoning: number | null;
time_created: number | null; time_updated: number | null;
msg_count: number
}>
for (const row of rows) {
let modelStr: string | null = null
if (row.model) {
try {
const parsed = JSON.parse(row.model)
modelStr = parsed.id || parsed.name || row.model
} catch {
modelStr = row.model
}
}
const tokens = (row.tokens_input || 0) + (row.tokens_output || 0) + (row.tokens_reasoning || 0)
sessions.push({
id: `opencode:${row.id}`,
tool: 'opencode',
originalId: row.id,
projectPath: row.directory,
projectName: extractProjectName(row.directory),
title: row.title,
summary: null,
model: modelStr,
messageCount: row.msg_count,
tokensTotal: tokens,
cost: row.cost || 0,
gitBranch: null,
createdAt: row.time_created ? Math.floor(row.time_created / 1000) : null,
updatedAt: row.time_updated ? Math.floor(row.time_updated / 1000) : null,
isActive: 0,
starred: 0,
tags: null,
archived: 0
})
}
} finally {
openCodeDb.close()
}
return sessions
}
export interface ChatMessage {
role: 'user' | 'assistant'
type: 'text' | 'tool'
content: string
toolName?: string
timestamp: number | null
}
export function getOpencodeMessages(sessionId: string): ChatMessage[] {
const dbPath = join(homedir(), '.local', 'share', 'opencode', 'opencode.db')
if (!existsSync(dbPath)) return []
const openCodeDb = new Database(dbPath, { readonly: true })
try {
const stmt = openCodeDb.prepare(`
SELECT m.id, m.data as msg_data
FROM message m
WHERE m.session_id = ?
ORDER BY m.time_created
`)
const msgRows = stmt.all(sessionId) as Array<{ id: string; msg_data: string }>
const partStmt = openCodeDb.prepare(
'SELECT data FROM part WHERE message_id = ? AND session_id = ? ORDER BY id'
)
const messages: ChatMessage[] = []
for (const row of msgRows) {
try {
const msg = JSON.parse(row.msg_data)
const role = msg.role
if (!role) continue
const partRows = partStmt.all(row.id, sessionId) as Array<{ data: string }>
const ts = msg.time?.created ? Math.floor(msg.time.created / 1000) : null
for (const pr of partRows) {
try {
const p = JSON.parse(pr.data)
if (p.type === 'text' && p.text) {
const content = p.text.trim()
if (content) {
messages.push({ role, type: 'text', content, timestamp: ts })
}
} else if (p.type === 'tool' && p.tool) {
const toolName = p.tool
let content = ''
if (p.state?.input) {
const input = p.state.input
if (typeof input === 'string') content = input
else content = JSON.stringify(input, null, 2)
}
content = content.trim()
if (content) {
messages.push({ role: 'assistant', type: 'tool', content, toolName, timestamp: ts })
}
}
} catch {}
}
} catch {}
}
return messages
} finally {
openCodeDb.close()
}
}
export function getOpencodeResumeCmd(originalId: string, projectPath: string): string {
return `cd "${projectPath}" && opencode resume ${originalId}`
}