Class: Aidp::Daemon::ProcessManager

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/daemon/process_manager.rb

Overview

Manages daemon process lifecycle: start, stop, status, attach Handles PID file management and process communication

Constant Summary collapse

DAEMON_DIR =
".aidp/daemon"
PID_FILE =
"#{DAEMON_DIR}/aidp.pid"
SOCKET_FILE =
"#{DAEMON_DIR}/aidp.sock"
LOG_FILE =
"#{DAEMON_DIR}/daemon.log"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project_dir = Dir.pwd) ⇒ ProcessManager

Returns a new instance of ProcessManager.



15
16
17
18
19
20
21
# File 'lib/aidp/daemon/process_manager.rb', line 15

def initialize(project_dir = Dir.pwd)
  @project_dir = project_dir
  @pid_file_path = File.join(@project_dir, PID_FILE)
  @socket_path = File.join(@project_dir, SOCKET_FILE)
  @log_path = File.join(@project_dir, LOG_FILE)
  ensure_daemon_dir
end

Instance Attribute Details

#socket_pathObject (readonly)

Get socket path for IPC



104
105
106
# File 'lib/aidp/daemon/process_manager.rb', line 104

def socket_path
  @socket_path
end

Instance Method Details

#log_file_pathObject

Get log file path



117
118
119
# File 'lib/aidp/daemon/process_manager.rb', line 117

def log_file_path
  @log_path
end

#pidObject

Get daemon PID



42
43
44
45
# File 'lib/aidp/daemon/process_manager.rb', line 42

def pid
  return nil unless running?
  read_pid
end

#remove_pidObject

Remove PID file



72
73
74
# File 'lib/aidp/daemon/process_manager.rb', line 72

def remove_pid
  File.delete(@pid_file_path) if File.exist?(@pid_file_path)
end

#remove_socketObject

Remove socket file



112
113
114
# File 'lib/aidp/daemon/process_manager.rb', line 112

def remove_socket
  File.delete(@socket_path) if File.exist?(@socket_path)
end

#running?Boolean

Check if daemon is running

Returns:

  • (Boolean)


24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/aidp/daemon/process_manager.rb', line 24

def running?
  return false unless File.exist?(@pid_file_path)

  pid = read_pid
  return false unless pid

  Process.kill(0, pid)
  true
rescue Errno::ESRCH
  # Process doesn't exist
  cleanup_stale_files
  false
rescue Errno::EPERM
  # Process exists but we don't have permission
  true
end

#socket_exists?Boolean

Check if socket exists

Returns:

  • (Boolean)


107
108
109
# File 'lib/aidp/daemon/process_manager.rb', line 107

def socket_exists?
  File.exist?(@socket_path)
end

#statusObject

Get daemon status summary



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/aidp/daemon/process_manager.rb', line 48

def status
  if running?
    {
      running: true,
      pid: pid,
      socket: File.exist?(@socket_path),
      log_file: @log_path
    }
  else
    {
      running: false,
      pid: nil,
      socket: false,
      log_file: @log_path
    }
  end
end

#stop(timeout: 30) ⇒ Object

Stop daemon gracefully



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/aidp/daemon/process_manager.rb', line 77

def stop(timeout: 30)
  return {success: false, message: "Daemon not running"} unless running?

  daemon_pid = pid
  Process.kill("TERM", daemon_pid)

  # Wait for process to exit
  timeout.times do
    sleep 1
    unless process_exists?(daemon_pid)
      cleanup_stale_files
      return {success: true, message: "Daemon stopped gracefully"}
    end
  end

  # Force kill if still running
  Process.kill("KILL", daemon_pid)
  cleanup_stale_files
  {success: true, message: "Daemon force-killed after timeout"}
rescue Errno::ESRCH
  cleanup_stale_files
  {success: true, message: "Daemon already stopped"}
rescue => e
  {success: false, message: "Error stopping daemon: #{e.message}"}
end

#write_pid(daemon_pid = Process.pid) ⇒ Object

Write PID file for daemon



67
68
69
# File 'lib/aidp/daemon/process_manager.rb', line 67

def write_pid(daemon_pid = Process.pid)
  File.write(@pid_file_path, daemon_pid.to_s)
end