| 516 | } |
| 517 | |
| 518 | async function readCodexThreadRows( |
| 519 | dbPath: string, |
| 520 | query: ExternalSessionQuery, |
| 521 | exactId?: string, |
| 522 | ): Promise<CodexThreadRow[] | undefined> { |
| 523 | try { |
| 524 | const sqlite = await import('node:sqlite'); |
| 525 | const db = new sqlite.DatabaseSync(dbPath, { readOnly: true }); |
| 526 | try { |
| 527 | const columns = new Set( |
| 528 | (db.prepare('PRAGMA table_info(threads)').all() as { name?: unknown }[]) |
| 529 | .map((column) => (typeof column.name === 'string' ? column.name : '')) |
| 530 | .filter(Boolean), |
| 531 | ); |
| 532 | if (!columns.has('id') || !columns.has('rollout_path')) return undefined; |
| 533 | const wanted = [ |
| 534 | 'id', |
| 535 | 'rollout_path', |
| 536 | 'cwd', |
| 537 | 'name', |
| 538 | 'title', |
| 539 | 'preview', |
| 540 | 'first_user_message', |
| 541 | 'created_at_ms', |
| 542 | 'created_at', |
| 543 | 'updated_at_ms', |
| 544 | 'updated_at', |
| 545 | 'archived', |
| 546 | 'source', |
| 547 | ].filter((column) => columns.has(column)); |
| 548 | const where: string[] = []; |
| 549 | const params: Array<string | number> = []; |
| 550 | if (exactId !== undefined) { |
| 551 | where.push('id = ?'); |
| 552 | params.push(exactId); |
| 553 | } |
| 554 | if (!query.includeArchived && columns.has('archived')) { |
| 555 | where.push('(archived IS NULL OR archived = 0)'); |
| 556 | } |
| 557 | if (query.cwd !== undefined && columns.has('cwd')) { |
| 558 | const variants = cwdSqlVariants(query.cwd); |
| 559 | where.push(`cwd IN (${variants.map(() => '?').join(', ')})`); |
| 560 | params.push(...variants); |
| 561 | } |
| 562 | const orderColumn = columns.has('updated_at_ms') |
| 563 | ? 'updated_at_ms' |
| 564 | : columns.has('updated_at') |
| 565 | ? 'updated_at' |
| 566 | : 'id'; |
| 567 | const sql = |
| 568 | `SELECT ${wanted.join(', ')} FROM threads` + |
| 569 | (where.length > 0 ? ` WHERE ${where.join(' AND ')}` : '') + |
| 570 | ` ORDER BY ${orderColumn} DESC`; |
| 571 | return db.prepare(sql).all(...params) as CodexThreadRow[]; |
| 572 | } finally { |
| 573 | db.close(); |
| 574 | } |
| 575 | } catch { |