* Internal function to load and process logs from a specified path * @param path Directory containing logs * @returns Array of logs sorted by date * @private
(path: string)
| 229 | * @private |
| 230 | */ |
| 231 | async function loadLogList(path: string): Promise<LogOption[]> { |
| 232 | let files: Awaited<ReturnType<typeof readdir>> |
| 233 | try { |
| 234 | files = await readdir(path, { withFileTypes: true }) |
| 235 | } catch { |
| 236 | logError(new Error(`No logs found at ${path}`)) |
| 237 | return [] |
| 238 | } |
| 239 | const logData = await Promise.all( |
| 240 | files.map(async (file, i) => { |
| 241 | const fullPath = join(path, file.name) |
| 242 | const content = await readFile(fullPath, { encoding: 'utf8' }) |
| 243 | const messages = jsonParse(content) as SerializedMessage[] |
| 244 | const firstMessage = messages[0] |
| 245 | const lastMessage = messages[messages.length - 1] |
| 246 | const firstPrompt = |
| 247 | firstMessage?.type === 'user' && |
| 248 | typeof firstMessage?.message?.content === 'string' |
| 249 | ? firstMessage?.message?.content |
| 250 | : 'No prompt' |
| 251 | |
| 252 | // For new random filenames, we'll get stats from the file itself |
| 253 | const fileStats = await stat(fullPath) |
| 254 | |
| 255 | // Check if it's a sidechain by looking at filename |
| 256 | const isSidechain = fullPath.includes('sidechain') |
| 257 | |
| 258 | // For new files, use the file modified time as date |
| 259 | const date = dateToFilename(fileStats.mtime) |
| 260 | |
| 261 | return { |
| 262 | date, |
| 263 | fullPath, |
| 264 | messages, |
| 265 | value: i, // hack: overwritten after sorting, right below this |
| 266 | created: parseISOString(firstMessage?.timestamp || date), |
| 267 | modified: lastMessage?.timestamp |
| 268 | ? parseISOString(lastMessage.timestamp) |
| 269 | : parseISOString(date), |
| 270 | firstPrompt: |
| 271 | firstPrompt.split('\n')[0]?.slice(0, 50) + |
| 272 | (firstPrompt.length > 50 ? '…' : '') || 'No prompt', |
| 273 | messageCount: messages.length, |
| 274 | isSidechain, |
| 275 | } |
| 276 | }), |
| 277 | ) |
| 278 | |
| 279 | return sortLogs(logData.filter(_ => _ !== null)).map((_, i) => ({ |
| 280 | ..._, |
| 281 | value: i, |
| 282 | })) |
| 283 | } |
| 284 | |
| 285 | function parseISOString(s: string): Date { |
| 286 | const b = s.split(/\D+/) |
no test coverage detected