Class: Consolle::SessionRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/consolle/session_registry.rb

Overview

Manages session registry with unique session IDs Provides persistent storage for session metadata even after sessions stop

Constant Summary collapse

SCHEMA_VERSION =
2
SESSION_ID_LENGTH =
8
SHORT_ID_LENGTH =
4

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project_path = Dir.pwd) ⇒ SessionRegistry

Returns a new instance of SessionRegistry.



17
18
19
# File 'lib/consolle/session_registry.rb', line 17

def initialize(project_path = Dir.pwd)
  @project_path = project_path
end

Instance Attribute Details

#project_pathObject (readonly)

Returns the value of attribute project_path.



15
16
17
# File 'lib/consolle/session_registry.rb', line 15

def project_path
  @project_path
end

Instance Method Details

#create_session(target:, socket_path:, pid:, rails_env:, mode:) ⇒ Object

Create a new session entry



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
# File 'lib/consolle/session_registry.rb', line 32

def create_session(target:, socket_path:, pid:, rails_env:, mode:)
  session_id = generate_session_id

  session_data = {
    'id' => session_id,
    'short_id' => short_id(session_id),
    'target' => target,
    'project' => project_path,
    'project_hash' => project_hash,
    'rails_env' => rails_env,
    'mode' => mode,
    'status' => 'running',
    'pid' => pid,
    'socket_path' => socket_path,
    'created_at' => Time.now.iso8601,
    'started_at' => Time.now.iso8601,
    'stopped_at' => nil,
    'last_activity_at' => Time.now.iso8601,
    'command_count' => 0
  }

  with_registry_lock do
    registry = load_registry
    registry['sessions'][session_id] = session_data
    save_registry(registry)
  end

  # Create session directory
  ensure_session_directory(session_id)

  # Save session metadata
  (session_id, session_data)

  session_data
end

#find_running_session(target:) ⇒ Object

Find running session by target



141
142
143
# File 'lib/consolle/session_registry.rb', line 141

def find_running_session(target:)
  find_session(target: target, status: 'running')
end

#find_session(session_id: nil, target: nil, status: nil) ⇒ Object

Find session by ID (full or short) or target name



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
# File 'lib/consolle/session_registry.rb', line 109

def find_session(session_id: nil, target: nil, status: nil)
  registry = load_registry

  if session_id
    # Try exact match first
    session = registry['sessions'][session_id]
    return session if session && (status.nil? || session['status'] == status)

    # Try short ID match
    matches = registry['sessions'].values.select do |s|
      s['short_id'] == session_id && s['project'] == project_path
    end
    matches = matches.select { |s| s['status'] == status } if status
    return matches.first if matches.size == 1

    # Ambiguous short ID
    return nil if matches.size > 1
  end

  if target
    # Find by target name in current project
    matches = registry['sessions'].values.select do |s|
      s['target'] == target && s['project'] == project_path
    end
    matches = matches.select { |s| s['status'] == status } if status

    # Return most recent running session, or most recently created
    matches.sort_by { |s| s['created_at'] }.last
  end
end

#generate_session_idObject

Generate a new unique session ID (8 hex characters)



22
23
24
# File 'lib/consolle/session_registry.rb', line 22

def generate_session_id
  SecureRandom.hex(SESSION_ID_LENGTH / 2)
end

#history_log_path(session_id) ⇒ Object

Get history log path for session



217
218
219
# File 'lib/consolle/session_registry.rb', line 217

def history_log_path(session_id)
  File.join(session_directory(session_id), 'history.log')
end

#list_sessions(include_stopped: false, all_projects: false) ⇒ Object

List sessions for current project



146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/consolle/session_registry.rb', line 146

def list_sessions(include_stopped: false, all_projects: false)
  registry = load_registry

  sessions = registry['sessions'].values

  # Filter by project unless all_projects
  sessions = sessions.select { |s| s['project'] == project_path } unless all_projects

  # Filter by status
  sessions = sessions.select { |s| s['status'] == 'running' } unless include_stopped

  # Sort by created_at descending
  sessions.sort_by { |s| s['created_at'] }.reverse
end

#list_stopped_sessions(all_projects: false) ⇒ Object

List only stopped sessions



162
163
164
165
166
167
168
169
170
171
172
# File 'lib/consolle/session_registry.rb', line 162

def list_stopped_sessions(all_projects: false)
  registry = load_registry

  sessions = registry['sessions'].values.select { |s| s['status'] == 'stopped' }

  # Filter by project unless all_projects
  sessions = sessions.select { |s| s['project'] == project_path } unless all_projects

  # Sort by stopped_at descending
  sessions.sort_by { |s| s['stopped_at'] || s['created_at'] }.reverse
end

#migrate_from_legacyObject

Migration from old sessions.json format



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/consolle/session_registry.rb', line 222

def migrate_from_legacy
  legacy_sessions_file = File.join(project_path, 'tmp', 'cone', 'sessions.json')
  return unless File.exist?(legacy_sessions_file)

  legacy_data = JSON.parse(File.read(legacy_sessions_file))
  return if legacy_data.empty?

  legacy_data.each do |target, info|
    next if target == '_schema'

    # Create new session entry for running legacy sessions
    if info['process_pid'] && process_alive?(info['process_pid'])
      create_session(
        target: target,
        socket_path: info['socket_path'],
        pid: info['process_pid'],
        rails_env: 'development',
        mode: 'pty'
      )
    end
  end
end

#prune_sessions(all_projects: false) ⇒ Object

Remove all stopped sessions (prune)



198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/consolle/session_registry.rb', line 198

def prune_sessions(all_projects: false)
  stopped = list_stopped_sessions(all_projects: all_projects)
  removed = []

  stopped.each do |session|
    result = remove_session(session_id: session['id'])
    # Check if result is session data (success) vs error hash
    removed << result if result && !result.key?(:error)
  end

  removed
end

#record_command(session_id) ⇒ Object

Record command execution



96
97
98
99
100
101
102
103
104
105
106
# File 'lib/consolle/session_registry.rb', line 96

def record_command(session_id)
  with_registry_lock do
    registry = load_registry
    if registry['sessions'][session_id]
      registry['sessions'][session_id]['command_count'] ||= 0
      registry['sessions'][session_id]['command_count'] += 1
      registry['sessions'][session_id]['last_activity_at'] = Time.now.iso8601
      save_registry(registry)
    end
  end
end

#remove_session(session_id: nil, target: nil) ⇒ Object

Remove session and its history



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/consolle/session_registry.rb', line 175

def remove_session(session_id: nil, target: nil)
  session = find_session(session_id: session_id, target: target)
  return nil unless session

  # Check if running
  if session['status'] == 'running'
    return { error: 'running', session: session }
  end

  with_registry_lock do
    registry = load_registry
    registry['sessions'].delete(session['id'])
    save_registry(registry)
  end

  # Remove session directory
  session_dir = session_directory(session['id'])
  FileUtils.rm_rf(session_dir) if Dir.exist?(session_dir)

  session
end

#session_directory(session_id) ⇒ Object

Get session directory path



212
213
214
# File 'lib/consolle/session_registry.rb', line 212

def session_directory(session_id)
  File.join(sessions_base_dir, session_id)
end

#short_id(session_id) ⇒ Object

Get short ID (first 4 characters)



27
28
29
# File 'lib/consolle/session_registry.rb', line 27

def short_id(session_id)
  session_id[0, SHORT_ID_LENGTH]
end

#stop_session(session_id: nil, target: nil, reason: 'user_requested') ⇒ Object

Update session status to stopped



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
# File 'lib/consolle/session_registry.rb', line 69

def stop_session(session_id: nil, target: nil, reason: 'user_requested')
  session = find_session(session_id: session_id, target: target)
  return nil unless session

  with_registry_lock do
    registry = load_registry
    if registry['sessions'][session['id']]
      registry['sessions'][session['id']]['status'] = 'stopped'
      registry['sessions'][session['id']]['stopped_at'] = Time.now.iso8601
      registry['sessions'][session['id']]['stop_reason'] = reason
      save_registry(registry)
    end
  end

  # Update metadata file
   = (session['id'])
  if 
    ['status'] = 'stopped'
    ['stopped_at'] = Time.now.iso8601
    ['stop_reason'] = reason
    (session['id'], )
  end

  session
end