consolle 0.3.9 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.version +1 -1
- data/Gemfile.lock +1 -1
- data/lib/consolle/cli.rb +424 -118
- data/lib/consolle/history.rb +210 -0
- data/lib/consolle/session_registry.rb +327 -0
- data/rule.ko.md +74 -5
- data/rule.md +74 -5
- metadata +3 -1
data/lib/consolle/cli.rb
CHANGED
|
@@ -8,6 +8,8 @@ require 'timeout'
|
|
|
8
8
|
require 'securerandom'
|
|
9
9
|
require 'date'
|
|
10
10
|
require_relative 'constants'
|
|
11
|
+
require_relative 'session_registry'
|
|
12
|
+
require_relative 'history'
|
|
11
13
|
require_relative 'adapters/rails_console'
|
|
12
14
|
|
|
13
15
|
module Consolle
|
|
@@ -88,7 +90,10 @@ module Consolle
|
|
|
88
90
|
shell.say ' cone status # Show Rails console status'
|
|
89
91
|
shell.say ' cone exec CODE # Execute Ruby code in Rails console'
|
|
90
92
|
shell.say ' cone rails SUBCOMMAND # Rails convenience commands'
|
|
91
|
-
shell.say ' cone ls # List active
|
|
93
|
+
shell.say ' cone ls # List active sessions (use -a for all)'
|
|
94
|
+
shell.say ' cone history # Show command history'
|
|
95
|
+
shell.say ' cone rm SESSION # Remove session and its history'
|
|
96
|
+
shell.say ' cone prune # Remove all stopped sessions'
|
|
92
97
|
shell.say ' cone stop_all # Stop all Rails console sessions'
|
|
93
98
|
shell.say ' cone rule FILE # Write cone command guide to FILE'
|
|
94
99
|
shell.say ' cone version # Show version'
|
|
@@ -236,18 +241,25 @@ module Consolle
|
|
|
236
241
|
|
|
237
242
|
begin
|
|
238
243
|
adapter.start
|
|
239
|
-
|
|
244
|
+
|
|
245
|
+
# Register session in registry
|
|
246
|
+
session = session_registry.create_session(
|
|
247
|
+
target: options[:target],
|
|
248
|
+
socket_path: adapter.socket_path,
|
|
249
|
+
pid: adapter.process_pid,
|
|
250
|
+
rails_env: current_rails_env,
|
|
251
|
+
mode: options[:mode] || 'pty'
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
puts '✓ Rails console started'
|
|
255
|
+
puts " Session ID: #{session['id']} (#{session['short_id']})"
|
|
256
|
+
puts " Target: #{session['target']}"
|
|
257
|
+
puts " Environment: #{current_rails_env}"
|
|
240
258
|
puts " PID: #{adapter.process_pid}"
|
|
241
259
|
puts " Socket: #{adapter.socket_path}"
|
|
242
260
|
|
|
243
|
-
#
|
|
244
|
-
save_session_info(adapter)
|
|
245
|
-
|
|
246
|
-
# Log session start
|
|
247
|
-
log_session_event(adapter.process_pid, 'session_start', {
|
|
248
|
-
rails_env: current_rails_env,
|
|
249
|
-
socket_path: adapter.socket_path
|
|
250
|
-
})
|
|
261
|
+
# Also save to legacy sessions.json for backward compatibility
|
|
262
|
+
save_session_info(adapter, session['id'])
|
|
251
263
|
rescue StandardError => e
|
|
252
264
|
puts "✗ Failed to start Rails console: #{e.message}"
|
|
253
265
|
exit 1
|
|
@@ -259,9 +271,11 @@ module Consolle
|
|
|
259
271
|
ensure_rails_project!
|
|
260
272
|
validate_session_name!(options[:target])
|
|
261
273
|
|
|
274
|
+
# Try to find session in registry first
|
|
275
|
+
session = session_registry.find_running_session(target: options[:target])
|
|
262
276
|
session_info = load_session_info
|
|
263
277
|
|
|
264
|
-
if session_info.nil?
|
|
278
|
+
if session_info.nil? && session.nil?
|
|
265
279
|
puts 'No active Rails console session found'
|
|
266
280
|
return
|
|
267
281
|
end
|
|
@@ -278,85 +292,141 @@ module Consolle
|
|
|
278
292
|
if process_running
|
|
279
293
|
rails_env = server_status['rails_env'] || 'unknown'
|
|
280
294
|
console_pid = server_status['pid'] || 'unknown'
|
|
295
|
+
uptime = session_info&.dig(:started_at) ? format_uptime(Time.now - Time.at(session_info[:started_at])) : 'unknown'
|
|
296
|
+
command_count = session ? session['command_count'] : 0
|
|
281
297
|
|
|
282
298
|
puts '✓ Rails console is running'
|
|
283
|
-
|
|
299
|
+
if session
|
|
300
|
+
puts " Session ID: #{session['id']} (#{session['short_id']})"
|
|
301
|
+
end
|
|
302
|
+
puts " Target: #{options[:target]}"
|
|
284
303
|
puts " Environment: #{rails_env}"
|
|
285
|
-
puts "
|
|
286
|
-
puts
|
|
304
|
+
puts " PID: #{console_pid}"
|
|
305
|
+
puts " Uptime: #{uptime}"
|
|
306
|
+
puts " Commands: #{command_count}"
|
|
307
|
+
puts " Socket: #{session_info&.dig(:socket_path) || session&.dig('socket_path')}"
|
|
287
308
|
else
|
|
288
309
|
puts '✗ Rails console is not running'
|
|
310
|
+
# Mark session as stopped in registry
|
|
311
|
+
session_registry.stop_session(target: options[:target], reason: 'process_died') if session
|
|
289
312
|
clear_session_info
|
|
290
313
|
end
|
|
291
314
|
end
|
|
292
315
|
|
|
293
|
-
desc 'ls', 'List
|
|
316
|
+
desc 'ls', 'List Rails console sessions'
|
|
294
317
|
long_desc <<-LONGDESC
|
|
295
|
-
Lists
|
|
318
|
+
Lists Rails console sessions in the current project.
|
|
319
|
+
|
|
320
|
+
By default, shows only active (running) sessions.
|
|
321
|
+
Use -a/--all to include stopped sessions.
|
|
296
322
|
|
|
297
323
|
Shows information about each session including:
|
|
298
|
-
- Session
|
|
299
|
-
-
|
|
324
|
+
- Session ID (short)
|
|
325
|
+
- Target name
|
|
300
326
|
- Rails environment
|
|
301
327
|
- Status (running/stopped)
|
|
328
|
+
- Uptime or stop time
|
|
329
|
+
- Command count
|
|
302
330
|
|
|
303
331
|
Example output:
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
- worker [PID: 12347, ENV: development, STATUS: stopped]
|
|
332
|
+
ID TARGET ENV STATUS UPTIME COMMANDS
|
|
333
|
+
a1b2 cone development running 2h 15m 42
|
|
334
|
+
e5f6 api production running 1h 30m 15
|
|
308
335
|
LONGDESC
|
|
336
|
+
method_option :all, type: :boolean, aliases: '-a', desc: 'Include stopped sessions'
|
|
309
337
|
def ls
|
|
310
338
|
ensure_rails_project!
|
|
311
339
|
|
|
312
|
-
|
|
340
|
+
include_stopped = options[:all]
|
|
341
|
+
sessions = session_registry.list_sessions(include_stopped: include_stopped)
|
|
313
342
|
|
|
314
|
-
|
|
315
|
-
|
|
343
|
+
# Also check legacy sessions.json for backward compatibility
|
|
344
|
+
legacy_sessions = load_sessions
|
|
345
|
+
legacy_sessions.each do |name, info|
|
|
346
|
+
next if name == '_schema'
|
|
347
|
+
next unless info['process_pid'] && process_alive?(info['process_pid'])
|
|
348
|
+
|
|
349
|
+
# Check if already in registry
|
|
350
|
+
existing = sessions.find { |s| s['target'] == name && s['status'] == 'running' }
|
|
351
|
+
next if existing
|
|
352
|
+
|
|
353
|
+
# Add legacy session (will be migrated on next start)
|
|
354
|
+
sessions << {
|
|
355
|
+
'short_id' => '----',
|
|
356
|
+
'target' => name,
|
|
357
|
+
'rails_env' => 'development',
|
|
358
|
+
'status' => 'running',
|
|
359
|
+
'pid' => info['process_pid'],
|
|
360
|
+
'created_at' => info['started_at'] ? Time.at(info['started_at']).iso8601 : Time.now.iso8601,
|
|
361
|
+
'command_count' => 0,
|
|
362
|
+
'_legacy' => true
|
|
363
|
+
}
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
if sessions.empty?
|
|
367
|
+
if include_stopped
|
|
368
|
+
puts 'No sessions found'
|
|
369
|
+
else
|
|
370
|
+
puts 'No active sessions'
|
|
371
|
+
puts "Use 'cone ls -a' to see stopped sessions"
|
|
372
|
+
end
|
|
316
373
|
return
|
|
317
374
|
end
|
|
318
375
|
|
|
319
|
-
|
|
320
|
-
|
|
376
|
+
# Verify running sessions are actually running
|
|
377
|
+
sessions.each do |session|
|
|
378
|
+
next unless session['status'] == 'running'
|
|
379
|
+
next if session['_legacy']
|
|
380
|
+
|
|
381
|
+
unless session['pid'] && process_alive?(session['pid'])
|
|
382
|
+
session_registry.stop_session(session_id: session['id'], reason: 'process_died')
|
|
383
|
+
session['status'] = 'stopped'
|
|
384
|
+
end
|
|
385
|
+
end
|
|
321
386
|
|
|
322
|
-
|
|
323
|
-
|
|
387
|
+
# Re-filter if needed
|
|
388
|
+
sessions = sessions.select { |s| s['status'] == 'running' } unless include_stopped
|
|
324
389
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
adapter.get_status
|
|
331
|
-
rescue StandardError
|
|
332
|
-
nil
|
|
333
|
-
end
|
|
390
|
+
if sessions.empty?
|
|
391
|
+
puts 'No active sessions'
|
|
392
|
+
puts "Use 'cone ls -a' to see stopped sessions"
|
|
393
|
+
return
|
|
394
|
+
end
|
|
334
395
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
396
|
+
# Display header
|
|
397
|
+
if include_stopped
|
|
398
|
+
puts 'ALL SESSIONS:'
|
|
399
|
+
else
|
|
400
|
+
puts 'ACTIVE SESSIONS:'
|
|
401
|
+
end
|
|
402
|
+
puts
|
|
403
|
+
puts format(' %-8s %-12s %-12s %-9s %-10s %s', 'ID', 'TARGET', 'ENV', 'STATUS', 'UPTIME', 'COMMANDS')
|
|
404
|
+
|
|
405
|
+
sessions.each do |session|
|
|
406
|
+
short_id = session['short_id'] || session['id']&.[](0, 4) || '----'
|
|
407
|
+
target = session['target'] || 'unknown'
|
|
408
|
+
env = session['rails_env'] || 'dev'
|
|
409
|
+
status = session['status'] || 'unknown'
|
|
410
|
+
commands = session['command_count'] || 0
|
|
411
|
+
|
|
412
|
+
if session['status'] == 'running'
|
|
413
|
+
started = session['started_at'] || session['created_at']
|
|
414
|
+
uptime = started ? format_uptime(Time.now - Time.parse(started)) : '---'
|
|
342
415
|
else
|
|
343
|
-
|
|
416
|
+
stopped = session['stopped_at']
|
|
417
|
+
uptime = stopped ? format_time_ago(Time.now - Time.parse(stopped)) : '---'
|
|
344
418
|
end
|
|
345
|
-
end
|
|
346
419
|
|
|
347
|
-
|
|
348
|
-
if stale_sessions.any?
|
|
349
|
-
with_sessions_lock do
|
|
350
|
-
sessions = load_sessions
|
|
351
|
-
stale_sessions.each { |name| sessions.delete(name) }
|
|
352
|
-
save_sessions(sessions)
|
|
353
|
-
end
|
|
420
|
+
puts format(' %-8s %-12s %-12s %-9s %-10s %d', short_id, target, env, status, uptime, commands)
|
|
354
421
|
end
|
|
355
422
|
|
|
356
|
-
|
|
357
|
-
|
|
423
|
+
puts
|
|
424
|
+
if include_stopped
|
|
425
|
+
puts "Use 'cone history --session ID' to view session history"
|
|
426
|
+
puts "Use 'cone rm ID' to remove session and history"
|
|
358
427
|
else
|
|
359
|
-
|
|
428
|
+
puts 'Usage: cone exec -t TARGET CODE'
|
|
429
|
+
puts ' cone exec --session ID CODE'
|
|
360
430
|
end
|
|
361
431
|
end
|
|
362
432
|
|
|
@@ -373,18 +443,15 @@ module Consolle
|
|
|
373
443
|
if adapter.stop
|
|
374
444
|
puts '✓ Rails console stopped'
|
|
375
445
|
|
|
376
|
-
#
|
|
377
|
-
|
|
378
|
-
if session_info && session_info[:process_pid]
|
|
379
|
-
log_session_event(session_info[:process_pid], 'session_stop', {
|
|
380
|
-
reason: 'user_requested'
|
|
381
|
-
})
|
|
382
|
-
end
|
|
446
|
+
# Mark session as stopped in registry (preserves history)
|
|
447
|
+
session_registry.stop_session(target: options[:target], reason: 'user_requested')
|
|
383
448
|
else
|
|
384
449
|
puts '✗ Failed to stop Rails console'
|
|
385
450
|
end
|
|
386
451
|
else
|
|
387
452
|
puts 'Rails console is not running'
|
|
453
|
+
# Mark as stopped anyway in case registry is out of sync
|
|
454
|
+
session_registry.stop_session(target: options[:target], reason: 'not_running')
|
|
388
455
|
end
|
|
389
456
|
|
|
390
457
|
clear_session_info
|
|
@@ -395,27 +462,31 @@ module Consolle
|
|
|
395
462
|
def stop_all
|
|
396
463
|
ensure_rails_project!
|
|
397
464
|
|
|
398
|
-
sessions
|
|
399
|
-
|
|
465
|
+
# Get running sessions from registry
|
|
466
|
+
running_sessions = session_registry.list_sessions(include_stopped: false)
|
|
400
467
|
|
|
401
|
-
#
|
|
402
|
-
|
|
468
|
+
# Also check legacy sessions
|
|
469
|
+
legacy_sessions = load_sessions
|
|
470
|
+
legacy_sessions.each do |name, info|
|
|
403
471
|
next if name == '_schema'
|
|
472
|
+
next unless info['process_pid'] && process_alive?(info['process_pid'])
|
|
404
473
|
|
|
405
|
-
|
|
474
|
+
existing = running_sessions.find { |s| s['target'] == name }
|
|
475
|
+
next if existing
|
|
476
|
+
|
|
477
|
+
running_sessions << { 'target' => name, 'pid' => info['process_pid'], '_legacy' => true }
|
|
406
478
|
end
|
|
407
479
|
|
|
408
|
-
if
|
|
480
|
+
if running_sessions.empty?
|
|
409
481
|
puts 'No active sessions to stop'
|
|
410
482
|
return
|
|
411
483
|
end
|
|
412
484
|
|
|
413
|
-
puts "Found #{
|
|
485
|
+
puts "Found #{running_sessions.size} active session(s)"
|
|
414
486
|
|
|
415
487
|
# Stop each active session
|
|
416
|
-
|
|
417
|
-
name = session[
|
|
418
|
-
info = session[:info]
|
|
488
|
+
running_sessions.each do |session|
|
|
489
|
+
name = session['target']
|
|
419
490
|
|
|
420
491
|
puts "\nStopping session '#{name}'..."
|
|
421
492
|
|
|
@@ -424,14 +495,10 @@ module Consolle
|
|
|
424
495
|
if adapter.stop
|
|
425
496
|
puts "✓ Session '#{name}' stopped"
|
|
426
497
|
|
|
427
|
-
#
|
|
428
|
-
|
|
429
|
-
log_session_event(info['process_pid'], 'session_stop', {
|
|
430
|
-
reason: 'stop_all_requested'
|
|
431
|
-
})
|
|
432
|
-
end
|
|
498
|
+
# Mark session as stopped in registry
|
|
499
|
+
session_registry.stop_session(target: name, reason: 'stop_all_requested') unless session['_legacy']
|
|
433
500
|
|
|
434
|
-
# Clear
|
|
501
|
+
# Clear from legacy sessions.json
|
|
435
502
|
with_sessions_lock do
|
|
436
503
|
sessions = load_sessions
|
|
437
504
|
sessions.delete(name)
|
|
@@ -652,6 +719,211 @@ module Consolle
|
|
|
652
719
|
end
|
|
653
720
|
end
|
|
654
721
|
|
|
722
|
+
desc 'rm SESSION_ID', 'Remove session and its history'
|
|
723
|
+
long_desc <<-LONGDESC
|
|
724
|
+
Removes a stopped session and all its history.
|
|
725
|
+
|
|
726
|
+
The SESSION_ID can be:
|
|
727
|
+
- Full session ID (8 characters, e.g., a1b2c3d4)
|
|
728
|
+
- Short session ID (4 characters, e.g., a1b2)
|
|
729
|
+
- Target name (e.g., cone, api)
|
|
730
|
+
|
|
731
|
+
Running sessions cannot be removed. Stop them first with 'cone stop -t TARGET'.
|
|
732
|
+
|
|
733
|
+
Use -f/--force to skip confirmation prompt.
|
|
734
|
+
Use -f/--force with a running session to stop and remove it.
|
|
735
|
+
|
|
736
|
+
Examples:
|
|
737
|
+
cone rm a1b2 # Remove by short ID
|
|
738
|
+
cone rm a1b2c3d4 # Remove by full ID
|
|
739
|
+
cone rm -f a1b2 # Remove without confirmation
|
|
740
|
+
LONGDESC
|
|
741
|
+
method_option :force, type: :boolean, aliases: '-f', desc: 'Skip confirmation (or force stop running session)'
|
|
742
|
+
def rm(session_id)
|
|
743
|
+
ensure_rails_project!
|
|
744
|
+
|
|
745
|
+
# Try to find session
|
|
746
|
+
session = session_registry.find_session(session_id: session_id) ||
|
|
747
|
+
session_registry.find_session(target: session_id)
|
|
748
|
+
|
|
749
|
+
unless session
|
|
750
|
+
puts "✗ Session not found: #{session_id}"
|
|
751
|
+
puts "Use 'cone ls -a' to see all sessions"
|
|
752
|
+
exit 1
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
# Check if running
|
|
756
|
+
if session['status'] == 'running'
|
|
757
|
+
if options[:force]
|
|
758
|
+
# Force stop first
|
|
759
|
+
puts "Stopping running session '#{session['target']}'..."
|
|
760
|
+
adapter = create_rails_adapter('development', session['target'])
|
|
761
|
+
adapter.stop
|
|
762
|
+
session_registry.stop_session(session_id: session['id'], reason: 'force_remove')
|
|
763
|
+
clear_session_info if options[:target] == session['target']
|
|
764
|
+
else
|
|
765
|
+
puts "✗ Session #{session['short_id']} (#{session['target']}) is still running"
|
|
766
|
+
puts " Use 'cone stop -t #{session['target']}' first, or 'cone rm -f #{session_id}' to force"
|
|
767
|
+
exit 1
|
|
768
|
+
end
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
# Confirm deletion
|
|
772
|
+
unless options[:force]
|
|
773
|
+
command_count = session['command_count'] || 0
|
|
774
|
+
print "Remove session #{session['id']} (#{session['target']}, #{command_count} commands)?\n"
|
|
775
|
+
print 'This will permanently delete all history. [y/N]: '
|
|
776
|
+
response = $stdin.gets&.strip&.downcase
|
|
777
|
+
unless response == 'y' || response == 'yes'
|
|
778
|
+
puts 'Cancelled'
|
|
779
|
+
return
|
|
780
|
+
end
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
# Remove session
|
|
784
|
+
result = session_registry.remove_session(session_id: session['id'])
|
|
785
|
+
|
|
786
|
+
if result && !result.is_a?(Hash)
|
|
787
|
+
puts "✓ Session #{session['id']} removed"
|
|
788
|
+
else
|
|
789
|
+
puts "✗ Failed to remove session"
|
|
790
|
+
exit 1
|
|
791
|
+
end
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
desc 'prune', 'Remove all stopped sessions'
|
|
795
|
+
long_desc <<-LONGDESC
|
|
796
|
+
Removes all stopped sessions and their history.
|
|
797
|
+
|
|
798
|
+
By default, only removes sessions from the current project.
|
|
799
|
+
|
|
800
|
+
Use --yes to skip confirmation prompt.
|
|
801
|
+
|
|
802
|
+
Examples:
|
|
803
|
+
cone prune # Remove stopped sessions (with confirmation)
|
|
804
|
+
cone prune --yes # Remove without confirmation
|
|
805
|
+
LONGDESC
|
|
806
|
+
method_option :yes, type: :boolean, aliases: '-y', desc: 'Skip confirmation'
|
|
807
|
+
def prune
|
|
808
|
+
ensure_rails_project!
|
|
809
|
+
|
|
810
|
+
stopped = session_registry.list_stopped_sessions
|
|
811
|
+
|
|
812
|
+
if stopped.empty?
|
|
813
|
+
puts 'No stopped sessions to remove'
|
|
814
|
+
return
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
# Show what will be removed
|
|
818
|
+
total_commands = stopped.sum { |s| s['command_count'] || 0 }
|
|
819
|
+
|
|
820
|
+
puts "Found #{stopped.size} stopped session(s):"
|
|
821
|
+
stopped.each do |session|
|
|
822
|
+
stopped_at = session['stopped_at'] ? Time.parse(session['stopped_at']).strftime('%Y-%m-%d') : '---'
|
|
823
|
+
commands = session['command_count'] || 0
|
|
824
|
+
puts " #{session['short_id']} #{session['target'].ljust(12)} stopped #{stopped_at} #{commands} commands"
|
|
825
|
+
end
|
|
826
|
+
puts
|
|
827
|
+
|
|
828
|
+
# Confirm
|
|
829
|
+
unless options[:yes]
|
|
830
|
+
print "Remove all stopped sessions and their history? [y/N]: "
|
|
831
|
+
response = $stdin.gets&.strip&.downcase
|
|
832
|
+
unless response == 'y' || response == 'yes'
|
|
833
|
+
puts 'Cancelled'
|
|
834
|
+
return
|
|
835
|
+
end
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
# Remove all stopped sessions
|
|
839
|
+
removed = session_registry.prune_sessions
|
|
840
|
+
|
|
841
|
+
puts "✓ Removed #{removed.size} sessions (#{total_commands} commands)"
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
desc 'history', 'Show command history'
|
|
845
|
+
long_desc <<-LONGDESC
|
|
846
|
+
Shows command history for sessions.
|
|
847
|
+
|
|
848
|
+
By default, shows history from the current active session (target).
|
|
849
|
+
|
|
850
|
+
Options:
|
|
851
|
+
--session ID Show history for specific session (by ID or short ID)
|
|
852
|
+
-t, --target Show history for specific target name
|
|
853
|
+
-n, --limit Limit number of entries shown
|
|
854
|
+
--today Show only today's commands
|
|
855
|
+
--date DATE Show commands from specific date (YYYY-MM-DD)
|
|
856
|
+
--success Show only successful commands
|
|
857
|
+
--failed Show only failed commands
|
|
858
|
+
--grep PATTERN Filter by code or result matching pattern
|
|
859
|
+
--all Include history from stopped sessions with same target
|
|
860
|
+
-v, --verbose Show detailed output
|
|
861
|
+
--json Output as JSON
|
|
862
|
+
|
|
863
|
+
Examples:
|
|
864
|
+
cone history # Current session history
|
|
865
|
+
cone history -t api # History for 'api' target
|
|
866
|
+
cone history --session a1b2 # History for specific session
|
|
867
|
+
cone history -n 10 # Last 10 commands
|
|
868
|
+
cone history --today # Today's commands only
|
|
869
|
+
cone history --failed # Failed commands only
|
|
870
|
+
cone history --grep User # Filter by pattern
|
|
871
|
+
LONGDESC
|
|
872
|
+
method_option :session, type: :string, aliases: '-s', desc: 'Session ID or short ID'
|
|
873
|
+
method_option :limit, type: :numeric, aliases: '-n', desc: 'Limit number of entries'
|
|
874
|
+
method_option :today, type: :boolean, desc: 'Show only today'
|
|
875
|
+
method_option :date, type: :string, desc: 'Show specific date (YYYY-MM-DD)'
|
|
876
|
+
method_option :success, type: :boolean, desc: 'Show only successful commands'
|
|
877
|
+
method_option :failed, type: :boolean, desc: 'Show only failed commands'
|
|
878
|
+
method_option :grep, type: :string, aliases: '-g', desc: 'Filter by pattern'
|
|
879
|
+
method_option :all, type: :boolean, desc: 'Include stopped sessions'
|
|
880
|
+
method_option :json, type: :boolean, desc: 'Output as JSON'
|
|
881
|
+
def history
|
|
882
|
+
ensure_rails_project!
|
|
883
|
+
|
|
884
|
+
history_manager = Consolle::History.new
|
|
885
|
+
|
|
886
|
+
entries = history_manager.query(
|
|
887
|
+
session_id: options[:session],
|
|
888
|
+
target: options[:target],
|
|
889
|
+
limit: options[:limit],
|
|
890
|
+
today: options[:today],
|
|
891
|
+
date: options[:date],
|
|
892
|
+
success_only: options[:success],
|
|
893
|
+
failed_only: options[:failed],
|
|
894
|
+
grep: options[:grep],
|
|
895
|
+
all_sessions: options[:all]
|
|
896
|
+
)
|
|
897
|
+
|
|
898
|
+
if entries.empty?
|
|
899
|
+
puts 'No history found'
|
|
900
|
+
if options[:session] || options[:target]
|
|
901
|
+
puts "Try 'cone history' without filters to see all history"
|
|
902
|
+
else
|
|
903
|
+
puts "Execute some commands first with 'cone exec'"
|
|
904
|
+
end
|
|
905
|
+
return
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
if options[:json]
|
|
909
|
+
puts history_manager.format_json(entries)
|
|
910
|
+
elsif options[:verbose]
|
|
911
|
+
entries.each do |entry|
|
|
912
|
+
puts history_manager.format_entry_verbose(entry)
|
|
913
|
+
puts
|
|
914
|
+
end
|
|
915
|
+
else
|
|
916
|
+
entries.each do |entry|
|
|
917
|
+
puts history_manager.format_entry(entry)
|
|
918
|
+
puts
|
|
919
|
+
end
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
unless options[:json]
|
|
923
|
+
puts "Showing #{entries.size} entries"
|
|
924
|
+
end
|
|
925
|
+
end
|
|
926
|
+
|
|
655
927
|
private
|
|
656
928
|
|
|
657
929
|
def current_rails_env
|
|
@@ -788,7 +1060,7 @@ module Consolle
|
|
|
788
1060
|
)
|
|
789
1061
|
end
|
|
790
1062
|
|
|
791
|
-
def save_session_info(adapter)
|
|
1063
|
+
def save_session_info(adapter, session_id = nil)
|
|
792
1064
|
target = options[:target]
|
|
793
1065
|
|
|
794
1066
|
with_sessions_lock do
|
|
@@ -800,7 +1072,8 @@ module Consolle
|
|
|
800
1072
|
'pid_path' => project_pid_path(target),
|
|
801
1073
|
'log_path' => project_log_path(target),
|
|
802
1074
|
'started_at' => Time.now.to_f,
|
|
803
|
-
'rails_root' => Dir.pwd
|
|
1075
|
+
'rails_root' => Dir.pwd,
|
|
1076
|
+
'session_id' => session_id
|
|
804
1077
|
}
|
|
805
1078
|
|
|
806
1079
|
save_sessions(sessions)
|
|
@@ -821,7 +1094,8 @@ module Consolle
|
|
|
821
1094
|
socket_path: session['socket_path'],
|
|
822
1095
|
process_pid: session['process_pid'],
|
|
823
1096
|
started_at: session['started_at'],
|
|
824
|
-
rails_root: session['rails_root']
|
|
1097
|
+
rails_root: session['rails_root'],
|
|
1098
|
+
session_id: session['session_id']
|
|
825
1099
|
}
|
|
826
1100
|
end
|
|
827
1101
|
|
|
@@ -836,47 +1110,44 @@ module Consolle
|
|
|
836
1110
|
end
|
|
837
1111
|
|
|
838
1112
|
def log_session_activity(process_pid, code, result)
|
|
839
|
-
#
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
1113
|
+
# Try to use new History class if session_id is available
|
|
1114
|
+
session_info = load_session_info
|
|
1115
|
+
if session_info&.dig(:session_id)
|
|
1116
|
+
history_manager = Consolle::History.new
|
|
1117
|
+
history_manager.log_command(
|
|
1118
|
+
session_id: session_info[:session_id],
|
|
1119
|
+
target: options[:target],
|
|
1120
|
+
code: code,
|
|
1121
|
+
result: result
|
|
1122
|
+
)
|
|
1123
|
+
else
|
|
1124
|
+
# Fallback to legacy logging
|
|
1125
|
+
log_file = File.join(project_session_dir, "session_#{Date.today.strftime('%Y%m%d')}_pid#{process_pid}.log")
|
|
1126
|
+
|
|
1127
|
+
log_entry = {
|
|
1128
|
+
timestamp: Time.now.iso8601,
|
|
1129
|
+
target: options[:target],
|
|
1130
|
+
request_id: result['request_id'],
|
|
1131
|
+
code: code,
|
|
1132
|
+
success: result['success'],
|
|
1133
|
+
result: result['result'],
|
|
1134
|
+
error: result['error'],
|
|
1135
|
+
message: result['message'],
|
|
1136
|
+
execution_time: result['execution_time']
|
|
1137
|
+
}
|
|
853
1138
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1139
|
+
File.open(log_file, 'a') do |f|
|
|
1140
|
+
f.puts JSON.generate(log_entry)
|
|
1141
|
+
end
|
|
857
1142
|
end
|
|
858
1143
|
rescue StandardError => e
|
|
859
1144
|
# Log errors should not crash the command
|
|
860
1145
|
puts "Warning: Failed to log session activity: #{e.message}" if options[:verbose]
|
|
861
1146
|
end
|
|
862
1147
|
|
|
863
|
-
def log_session_event(
|
|
864
|
-
#
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
# Create log entry
|
|
868
|
-
log_entry = {
|
|
869
|
-
timestamp: Time.now.iso8601,
|
|
870
|
-
event: event_type
|
|
871
|
-
}.merge(details)
|
|
872
|
-
|
|
873
|
-
# Append to log file
|
|
874
|
-
File.open(log_file, 'a') do |f|
|
|
875
|
-
f.puts JSON.generate(log_entry)
|
|
876
|
-
end
|
|
877
|
-
rescue StandardError => e
|
|
878
|
-
# Log errors should not crash the command
|
|
879
|
-
puts "Warning: Failed to log session event: #{e.message}" if options[:verbose]
|
|
1148
|
+
def log_session_event(_process_pid, _event_type, _details = {})
|
|
1149
|
+
# Legacy method kept for backward compatibility with tests
|
|
1150
|
+
# Session events are now tracked in registry metadata
|
|
880
1151
|
end
|
|
881
1152
|
|
|
882
1153
|
def load_sessions
|
|
@@ -957,6 +1228,41 @@ module Consolle
|
|
|
957
1228
|
end
|
|
958
1229
|
end
|
|
959
1230
|
|
|
1231
|
+
def session_registry
|
|
1232
|
+
@session_registry ||= Consolle::SessionRegistry.new
|
|
1233
|
+
end
|
|
1234
|
+
|
|
1235
|
+
def format_uptime(seconds)
|
|
1236
|
+
seconds = seconds.to_i
|
|
1237
|
+
if seconds < 60
|
|
1238
|
+
"#{seconds}s"
|
|
1239
|
+
elsif seconds < 3600
|
|
1240
|
+
"#{seconds / 60}m #{seconds % 60}s"
|
|
1241
|
+
elsif seconds < 86400
|
|
1242
|
+
hours = seconds / 3600
|
|
1243
|
+
mins = (seconds % 3600) / 60
|
|
1244
|
+
"#{hours}h #{mins}m"
|
|
1245
|
+
else
|
|
1246
|
+
days = seconds / 86400
|
|
1247
|
+
hours = (seconds % 86400) / 3600
|
|
1248
|
+
"#{days}d #{hours}h"
|
|
1249
|
+
end
|
|
1250
|
+
end
|
|
1251
|
+
|
|
1252
|
+
def format_time_ago(seconds)
|
|
1253
|
+
seconds = seconds.to_i
|
|
1254
|
+
if seconds < 60
|
|
1255
|
+
'just now'
|
|
1256
|
+
elsif seconds < 3600
|
|
1257
|
+
"#{seconds / 60}m ago"
|
|
1258
|
+
elsif seconds < 86400
|
|
1259
|
+
"#{seconds / 3600}h ago"
|
|
1260
|
+
else
|
|
1261
|
+
days = seconds / 86400
|
|
1262
|
+
"#{days}d ago"
|
|
1263
|
+
end
|
|
1264
|
+
end
|
|
1265
|
+
|
|
960
1266
|
def process_alive?(pid)
|
|
961
1267
|
return false unless pid
|
|
962
1268
|
|