Class: Ask::CodingProviders::Codex::CodexDB

Inherits:
Object
  • Object
show all
Defined in:
lib/ask/coding_providers/codex/codex_db.rb

Overview

Queries Codex's SQLite database for thread/project data.

Codex stores sessions in ~/.codex/state_5.sqlite (threads table) and full message history in JSONL rollout files.

Constant Summary collapse

DEFAULT_DB =
File.expand_path("~/.codex/state_5.sqlite")

Instance Method Summary collapse

Constructor Details

#initialize(db_path = nil) ⇒ CodexDB

Returns a new instance of CodexDB.



15
16
17
# File 'lib/ask/coding_providers/codex/codex_db.rb', line 15

def initialize(db_path = nil)
  @db_path = db_path || DEFAULT_DB
end

Instance Method Details

#available?Boolean

Returns:

  • (Boolean)


19
20
21
# File 'lib/ask/coding_providers/codex/codex_db.rb', line 19

def available?
  File.exist?(@db_path)
end

#find_recent_sessionObject

Find the most recent session across all projects. Returns directory: or nil.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/ask/coding_providers/codex/codex_db.rb', line 81

def find_recent_session
  return nil unless available?

  db = open_db
  row = db.get_first_row(<<~SQL)
    SELECT id, cwd FROM threads
    WHERE archived = 0
    ORDER BY updated_at DESC LIMIT 1
  SQL
  row ? { session_id: row["id"], directory: row["cwd"] } : nil
rescue => e
  nil
ensure
  db&.close
end

#find_recent_tui_session(workspace_path) ⇒ Object

Find the most recent TUI session in a workspace.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/ask/coding_providers/codex/codex_db.rb', line 98

def find_recent_tui_session(workspace_path)
  return nil unless available?

  db = open_db
  row = db.get_first_row(<<~SQL, [workspace_path, workspace_path])
    SELECT id, title, cwd FROM threads
    WHERE (cwd = ? OR ? LIKE cwd || '/%')
      AND archived = 0
    ORDER BY updated_at DESC LIMIT 1
  SQL
  return nil unless row
  {
    session_id: row["id"],
    title: row["title"],
    directory: row["cwd"]
  }
rescue => e
  nil
ensure
  db&.close
end

#find_sessions(directory:, limit: 20) ⇒ Object

Find sessions in a given directory. Returns [title:, updated:] or [].



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/ask/coding_providers/codex/codex_db.rb', line 54

def find_sessions(directory:, limit: 20)
  return [] unless available?

  db = open_db
  rows = db.execute(<<~SQL, [directory, directory, limit])
    SELECT id, title, updated_at, preview
    FROM threads
    WHERE (cwd = ? OR ? LIKE cwd || '/%')
      AND archived = 0
    ORDER BY updated_at DESC
    LIMIT ?
  SQL
  rows.map do |r|
    {
      session_id: r["id"],
      title: r["title"].to_s.empty? ? (r["preview"] || "(untitled)") : r["title"],
      updated: r["updated_at"]
    }
  end
rescue => e
  []
ensure
  db&.close
end

#list_projectsObject

List all projects (directories with threads), ordered by most recent. Returns [directory:, session_count:] or [].



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
# File 'lib/ask/coding_providers/codex/codex_db.rb', line 25

def list_projects
  return [] unless available?

  db = open_db
  rows = db.execute(<<~SQL)
    SELECT cwd AS directory,
           COUNT(*) AS session_count,
           MAX(updated_at) AS last_active
    FROM threads
    WHERE archived = 0
    GROUP BY cwd
    ORDER BY last_active DESC
    LIMIT 20
  SQL
  rows.map do |r|
    {
      project_id: r["directory"],
      directory: r["directory"],
      session_count: r["session_count"].to_i
    }
  end
rescue => e
  []
ensure
  db&.close
end

#recent_sessionsObject

List recent sessions across all projects. Returns [title:, updated:, msg_count:] or [].



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/ask/coding_providers/codex/codex_db.rb', line 154

def recent_sessions
  return [] unless available?

  db = open_db
  rows = db.execute(<<~SQL)
    SELECT id, title, updated_at, preview
    FROM threads
    WHERE archived = 0
    ORDER BY updated_at DESC
    LIMIT 50
  SQL
  rows.map do |r|
    {
      session_id: r["id"],
      title: r["title"].to_s.empty? ? (r["preview"] || "(untitled)") : r["title"],
      updated: r["updated_at"],
      msg_count: 0
    }
  end
rescue => e
  []
ensure
  db&.close
end

#session_directory(session_id) ⇒ Object

Look up a session's workspace directory by ID.



121
122
123
124
125
126
127
128
129
130
131
# File 'lib/ask/coding_providers/codex/codex_db.rb', line 121

def session_directory(session_id)
  return nil unless available?

  db = open_db
  row = db.get_first_row("SELECT cwd FROM threads WHERE id = ?", [session_id])
  row&.dig("cwd")
rescue => e
  nil
ensure
  db&.close
end

#session_history(session_id, limit: 100) ⇒ Object

Get session message history from the rollout JSONL file. Returns [role:, origin:] or [].



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/ask/coding_providers/codex/codex_db.rb', line 135

def session_history(session_id, limit: 100)
  return [] unless available?

  db = open_db
  row = db.get_first_row("SELECT rollout_path, title, preview FROM threads WHERE id = ?", [session_id])
  return [] unless row

  rollout_path = row["rollout_path"]
  return [] unless rollout_path && File.exist?(rollout_path)

  parse_rollout(rollout_path, limit)
rescue => e
  []
ensure
  db&.close
end