brainiac 0.0.6 → 0.0.8

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +2 -2
  3. data/README.md +136 -6
  4. data/bin/brainiac +472 -44
  5. data/bin/brainiac-completion.bash +1 -1
  6. data/lib/brainiac/agents.rb +27 -74
  7. data/lib/brainiac/brain.rb +6 -6
  8. data/lib/brainiac/config.rb +40 -76
  9. data/lib/brainiac/handlers/discord/api.rb +196 -0
  10. data/lib/brainiac/handlers/discord/config.rb +134 -0
  11. data/lib/brainiac/handlers/discord/delivery.rb +196 -0
  12. data/lib/brainiac/handlers/discord/gateway.rb +212 -0
  13. data/lib/brainiac/handlers/discord/message.rb +933 -0
  14. data/lib/brainiac/handlers/discord/reactions.rb +215 -0
  15. data/lib/brainiac/handlers/discord.rb +14 -1892
  16. data/lib/brainiac/handlers/github.rb +134 -317
  17. data/lib/brainiac/handlers/shared/git.rb +190 -0
  18. data/lib/brainiac/handlers/shared/inline_tags.rb +89 -0
  19. data/lib/brainiac/handlers/zoho.rb +103 -153
  20. data/lib/brainiac/helpers.rb +43 -455
  21. data/lib/brainiac/hooks.rb +86 -0
  22. data/lib/brainiac/plugins.rb +154 -0
  23. data/lib/brainiac/prompts.rb +34 -172
  24. data/lib/brainiac/restart.rb +112 -0
  25. data/lib/brainiac/routes/api.rb +411 -0
  26. data/lib/brainiac/users.rb +1 -7
  27. data/lib/brainiac/version.rb +1 -1
  28. data/lib/brainiac/zoho_mail_api.rb +2 -1
  29. data/lib/brainiac.rb +8 -1
  30. data/monitor/daemon.rb +4 -27
  31. data/monitor/shared.rb +247 -0
  32. data/monitor/{waybar-deploy-env.rb → waybar/deploy_env.rb} +17 -89
  33. data/monitor/waybar/setup.rb +232 -0
  34. data/monitor/waybar/status.rb +51 -0
  35. data/monitor/{view-logs-rofi.rb → waybar/view_logs.rb} +21 -88
  36. data/monitor/{deploy-env-macos.rb → xbar/deploy_env.rb} +3 -2
  37. data/monitor/xbar/plugin.rb +149 -0
  38. data/monitor/{setup-menubar.rb → xbar/setup.rb} +14 -30
  39. data/receiver.rb +44 -551
  40. data/templates/agents.json.example +1 -2
  41. data/templates/brainiac.json.example +8 -0
  42. data/templates/cli-providers/kiro.json.example +8 -2
  43. data/templates/plugins.json.example +3 -0
  44. data/templates/users.json.example +0 -3
  45. metadata +25 -23
  46. data/lib/brainiac/card_index.rb +0 -389
  47. data/lib/brainiac/deployments.rb +0 -258
  48. data/lib/brainiac/handlers/fizzy.rb +0 -1292
  49. data/lib/brainiac/planning.rb +0 -237
  50. data/lib/user_registry.rb +0 -159
  51. data/monitor/menubar.rb +0 -295
  52. data/monitor/setup-waybar-deploy-envs.rb +0 -121
  53. data/monitor/setup-waybar-deployments.rb +0 -96
  54. data/monitor/setup-waybar-module.rb +0 -113
  55. data/monitor/setup-xbar-plugin.rb +0 -35
  56. data/monitor/view-logs.rb +0 -119
  57. data/monitor/waybar-config-updater.rb +0 -56
  58. data/monitor/waybar-deployments.rb +0 -239
  59. data/monitor/waybar.rb +0 -146
  60. data/monitor/xbar.3s.rb +0 -179
  61. data/templates/fizzy.json.example +0 -24
  62. /data/monitor/{open-action.sh → xbar/open_action.sh} +0 -0
  63. /data/monitor/{view-logs-macos.rb → xbar/view_logs.rb} +0 -0
@@ -0,0 +1,411 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Admin and data API routes.
4
+ #
5
+ # These are read-only (mostly) endpoints for inspecting system state:
6
+ # projects, agents, users, brain, skills, sessions, logs, etc.
7
+
8
+ # --- Session Helpers ---
9
+
10
+ def reap_dead_sessions
11
+ ACTIVE_SESSIONS.delete_if do |card_key, info|
12
+ Process.kill(0, info[:pid])
13
+ false
14
+ rescue Errno::ESRCH, Errno::EPERM
15
+ archive_session(card_key, info)
16
+ true
17
+ end
18
+ end
19
+
20
+ def format_active_sessions
21
+ ACTIVE_SESSIONS.map do |card_key, info|
22
+ agent_name = resolve_session_agent_name(card_key, info)
23
+ {
24
+ card_key: card_key, agent: agent_name, pid: info[:pid],
25
+ started_at: info[:started_at].iso8601,
26
+ elapsed_seconds: (Time.now - info[:started_at]).to_i,
27
+ log_file: info[:log_file], alive: true,
28
+ children: child_processes_for(info[:pid])
29
+ }
30
+ end
31
+ end
32
+
33
+ def resolve_session_agent_name(card_key, info)
34
+ return info[:agent_name] if info[:agent_name]
35
+
36
+ parts = card_key.split("-")
37
+ agent_key = if parts[0] == "discord" && parts.size >= 4
38
+ parts[1]
39
+ else
40
+ "Unknown"
41
+ end
42
+ agent_display_name(agent_key)
43
+ end
44
+
45
+ def format_recent_sessions
46
+ RECENT_SESSIONS.map do |s|
47
+ {
48
+ card_key: s[:card_key], agent: s[:agent_name] || "Unknown",
49
+ log_file: s[:log_file], started_at: s[:started_at]&.iso8601,
50
+ finished_at: s[:finished_at]&.iso8601
51
+ }
52
+ end
53
+ end
54
+
55
+ def kill_child_process(target_pid)
56
+ Process.kill("TERM", target_pid)
57
+ Thread.new do
58
+ sleep 3
59
+ begin
60
+ Process.kill(0, target_pid)
61
+ Process.kill("KILL", target_pid)
62
+ rescue Errno::ESRCH, Errno::EPERM # rubocop:disable Lint/SuppressedException
63
+ end
64
+ end
65
+ LOG.info "Killed child process #{target_pid} (SIGTERM)"
66
+ { killed: target_pid }.to_json
67
+ rescue Errno::ESRCH
68
+ halt 404, { error: "process not found" }.to_json
69
+ rescue Errno::EPERM
70
+ halt 403, { error: "permission denied" }.to_json
71
+ end
72
+
73
+ def search_giphy(query, api_key)
74
+ uri = URI("https://api.giphy.com/v1/gifs/search")
75
+ uri.query = URI.encode_www_form(api_key: api_key, q: query, limit: 5, rating: "pg-13")
76
+ response = Net::HTTP.get_response(uri)
77
+
78
+ if response.code.to_i == 200
79
+ results = JSON.parse(response.body)["data"] || []
80
+ results.map { |g| { url: g.dig("images", "original", "url") || g["url"], title: g["title"] } }
81
+ else
82
+ LOG.warn "[GIF] Giphy API returned #{response.code}: #{response.body[0..200]}"
83
+ nil
84
+ end
85
+ end
86
+
87
+ # --- Projects ---
88
+
89
+ get "/api/projects" do
90
+ content_type :json
91
+ reload_projects!
92
+ { projects: PROJECTS }.to_json
93
+ end
94
+
95
+ get "/api/projects/:key" do
96
+ content_type :json
97
+ reload_projects!
98
+ project_key = params["key"]
99
+ if PROJECTS.key?(project_key)
100
+ { project: PROJECTS[project_key] }.to_json
101
+ else
102
+ halt 404, { error: "Project not found" }.to_json
103
+ end
104
+ end
105
+
106
+ post "/api/reload" do
107
+ content_type :json
108
+ reload_projects!(force: true)
109
+ reload_agent_registry!(force: true)
110
+ reload_user_registry!(force: true)
111
+ reload_github_config!(force: true)
112
+ ReloadHooks.run_all!
113
+ { status: "reloaded", projects: PROJECTS.keys, agents: all_agent_names.to_a, registry: AGENT_REGISTRY.keys,
114
+ users: USER_REGISTRY["users"].size }.to_json
115
+ end
116
+
117
+ # --- Agents & Roles ---
118
+
119
+ get "/api/agents" do
120
+ content_type :json
121
+ { default: AI_AGENT_NAME, agents: discover_kiro_agents, all_known: all_agent_names.to_a, roster: agent_roster }.to_json
122
+ end
123
+
124
+ get "/api/roles" do
125
+ content_type :json
126
+ roles = []
127
+ if Dir.exist?(ROLES_DIR)
128
+ Dir.glob(File.join(ROLES_DIR, "*.md")).each do |f|
129
+ name = File.basename(f, ".md")
130
+ agents = AGENT_REGISTRY.select { |_, e| e.is_a?(Hash) && Array(e["role"]).include?(name) }.map { |k, _e| agent_display_name(k) }
131
+ roles << { name: name, agents: agents }
132
+ end
133
+ end
134
+ { roles: roles, dir: ROLES_DIR }.to_json
135
+ end
136
+
137
+ # --- Users ---
138
+
139
+ get "/api/users" do
140
+ content_type :json
141
+ reload_user_registry!
142
+
143
+ filter = params["filter"]
144
+ users = case filter
145
+ when "humans" then human_users
146
+ when "agents" then ai_agents
147
+ else USER_REGISTRY["users"]
148
+ end
149
+
150
+ { users: users, total: USER_REGISTRY["users"].size, schema_version: USER_REGISTRY["schema_version"] }.to_json
151
+ end
152
+
153
+ get "/api/users/:identifier" do
154
+ content_type :json
155
+ reload_user_registry!
156
+
157
+ identifier = params["identifier"]
158
+ user = find_user(identifier)
159
+
160
+ if user
161
+ { user: user }.to_json
162
+ else
163
+ halt 404, { error: "User not found", identifier: identifier }.to_json
164
+ end
165
+ end
166
+
167
+ # --- Brain ---
168
+
169
+ get "/api/brain" do
170
+ content_type :json
171
+ agent = params["agent"] || AI_AGENT_NAME
172
+ persona_dir = persona_dir_for(agent)
173
+ persona_col = persona_collection_for(agent)
174
+
175
+ knowledge_files = File.directory?(KNOWLEDGE_DIR) ? Dir.glob(File.join(KNOWLEDGE_DIR, "**", "*.md")).map { |f| f.sub("#{KNOWLEDGE_DIR}/", "") } : []
176
+ persona_files = File.directory?(persona_dir) ? Dir.glob(File.join(persona_dir, "**", "*.md")).map { |f| f.sub("#{persona_dir}/", "") } : []
177
+
178
+ {
179
+ agent: agent,
180
+ knowledge: { dir: KNOWLEDGE_DIR, collection: KNOWLEDGE_COLLECTION, files: knowledge_files },
181
+ persona: { dir: persona_dir, collection: persona_col, files: persona_files }
182
+ }.to_json
183
+ end
184
+
185
+ get "/api/brain/search" do
186
+ content_type :json
187
+ query = params["q"]
188
+ halt 400, { error: "Missing query parameter ?q=" }.to_json unless query && !query.empty?
189
+
190
+ agent = params["agent"] || AI_AGENT_NAME
191
+ scope = (params["scope"] || "knowledge").to_sym
192
+ scope = :knowledge unless %i[knowledge persona].include?(scope)
193
+ results = query_brain(query, agent_name: agent, scope: scope, max_results: (params["n"] || 5).to_i)
194
+
195
+ { agent: agent, scope: scope, query: query, results: results }.to_json
196
+ end
197
+
198
+ # --- Skills ---
199
+
200
+ get "/api/skills" do
201
+ content_type :json
202
+ skills = build_skill_index
203
+ { total: skills.size, skills: skills }.to_json
204
+ end
205
+
206
+ post "/api/skills/curate" do
207
+ content_type :json
208
+ result = curate_skills
209
+ result.to_json
210
+ end
211
+
212
+ # --- Dispatch Depth ---
213
+
214
+ get "/api/dispatch-depth" do
215
+ content_type :json
216
+ {
217
+ max_depth: AGENT_DISPATCH_MAX_DEPTH,
218
+ window_seconds: AGENT_DISPATCH_WINDOW,
219
+ cards: AGENT_DISPATCH_DEPTH.transform_values do |v|
220
+ { count: v[:count], last_human_at: v[:last_human_at]&.iso8601, blocked: v[:count] >= AGENT_DISPATCH_MAX_DEPTH }
221
+ end
222
+ }.to_json
223
+ end
224
+
225
+ # --- Sessions ---
226
+
227
+ get "/api/status" do
228
+ content_type :json
229
+ ACTIVE_SESSIONS_MUTEX.synchronize do
230
+ reap_dead_sessions
231
+
232
+ sessions = format_active_sessions
233
+ recent = format_recent_sessions
234
+
235
+ { sessions: sessions, count: sessions.size, recent: recent, version: BRAINIAC_VERSION,
236
+ server_root: File.expand_path("../../..", __dir__) }.to_json
237
+ end
238
+ end
239
+
240
+ post "/api/sessions/kill/:card_key" do
241
+ content_type :json
242
+ card_key = params[:card_key]
243
+ halt 400, { error: "missing card_key" }.to_json if card_key.to_s.empty?
244
+
245
+ killed = kill_session(card_key)
246
+ halt 404, { error: "session not found" }.to_json unless killed
247
+
248
+ LOG.info "Killed agent session #{card_key} via API"
249
+ { killed: card_key }.to_json
250
+ end
251
+
252
+ post "/api/sessions/kill-process/:pid" do
253
+ content_type :json
254
+ target_pid = params[:pid].to_i
255
+ halt 400, { error: "invalid pid" }.to_json if target_pid <= 0
256
+
257
+ valid = ACTIVE_SESSIONS_MUTEX.synchronize do
258
+ ACTIVE_SESSIONS.any? do |_, info|
259
+ child_processes_for(info[:pid]).any? { |c| c[:pid] == target_pid }
260
+ end
261
+ end
262
+ halt 403, { error: "pid is not a child of any active agent session" }.to_json unless valid
263
+
264
+ kill_child_process(target_pid)
265
+ end
266
+
267
+ # --- Logs ---
268
+
269
+ get "/api/logs" do
270
+ content_type "text/plain"
271
+ log_file = params["file"]
272
+ lines = (params["lines"] || 200).to_i
273
+
274
+ halt 400, "Missing ?file= parameter" unless log_file && !log_file.empty?
275
+ halt 400, "Invalid path" if log_file.include?("..") || !log_file.start_with?("/")
276
+ halt 404, "File not found" unless File.exist?(log_file)
277
+
278
+ allowed = PROJECTS.values.map { |p| File.join(p["repo_path"], "tmp") }
279
+ allowed << File.join(BRAINIAC_DIR, "tmp")
280
+ halt 403, "Forbidden" unless allowed.any? { |dir| log_file.start_with?(dir) }
281
+
282
+ all_lines = File.readlines(log_file).last(lines)
283
+ all_lines.join.gsub(/\e\[[\d;]*[a-zA-Z]/, "").gsub(/\e\[\?[\d;]*[a-zA-Z]/, "")
284
+ end
285
+
286
+ # --- GIF ---
287
+
288
+ get "/api/gif" do
289
+ content_type :json
290
+ query = params[:q].to_s.strip
291
+ halt 400, { error: "Missing ?q= parameter" }.to_json if query.empty?
292
+
293
+ api_key = DISCORD_CONFIG["giphy_api_key"]
294
+ halt 503, { error: "No giphy_api_key configured in discord.json" }.to_json unless api_key
295
+
296
+ gifs = search_giphy(query, api_key)
297
+ halt 502, { error: "Giphy API error" }.to_json unless gifs
298
+
299
+ { query: query, results: gifs }.to_json
300
+ rescue StandardError => e
301
+ LOG.error "[GIF] Search failed: #{e.message}"
302
+ halt 500, { error: e.message }.to_json
303
+ end
304
+
305
+ # --- Cron ---
306
+
307
+ get "/api/cron/script" do
308
+ content_type "text/plain"
309
+ path = params["path"]
310
+ halt 400, "Missing ?path= parameter" unless path && !path.empty?
311
+ halt 400, "Invalid path" if path.include?("..")
312
+
313
+ valid = CRON_JOBS.values.any? do |j|
314
+ j[:script] == path || j["script"] == path ||
315
+ (j[:prompt] || j["prompt"] || "").include?(path)
316
+ end
317
+ halt 403, "Not a registered cron script" unless valid
318
+ halt 404, "File not found" unless File.exist?(path)
319
+
320
+ File.read(path)
321
+ end
322
+
323
+ get "/api/cron" do
324
+ content_type :json
325
+ reload_cron_jobs!
326
+ {
327
+ enabled: true,
328
+ jobs: CRON_JOBS,
329
+ thread_alive: CRON_THREAD[:ref]&.alive? || false
330
+ }.to_json
331
+ end
332
+
333
+ post "/api/cron/add" do
334
+ content_type :json
335
+ request.body.rewind
336
+ payload = JSON.parse(request.body.read)
337
+
338
+ result = add_cron_job(
339
+ id: payload["id"],
340
+ schedule: payload["schedule"],
341
+ agent: payload["agent"],
342
+ project: payload["project"],
343
+ prompt: payload["prompt"],
344
+ script: payload["script"],
345
+ model: payload["model"],
346
+ effort: payload["effort"],
347
+ discord_channel_id: payload["discord_channel_id"],
348
+ forum_title: payload["forum_title"],
349
+ forum_reply_to_latest: payload["forum_reply_to_latest"] || false,
350
+ repeat_count: payload["repeat_count"]
351
+ )
352
+
353
+ result.to_json
354
+ end
355
+
356
+ post "/api/cron/remove" do
357
+ content_type :json
358
+ request.body.rewind
359
+ payload = JSON.parse(request.body.read)
360
+
361
+ result = remove_cron_job(payload["id"])
362
+ result.to_json
363
+ end
364
+
365
+ post "/api/cron/toggle" do
366
+ content_type :json
367
+ request.body.rewind
368
+ payload = JSON.parse(request.body.read)
369
+
370
+ result = toggle_cron_job(payload["id"], payload["enabled"])
371
+ result.to_json
372
+ end
373
+
374
+ post "/api/cron/update" do
375
+ content_type :json
376
+ request.body.rewind
377
+ payload = JSON.parse(request.body.read)
378
+
379
+ result = update_cron_job(
380
+ payload["id"],
381
+ schedule: payload["schedule"],
382
+ discord_channel_id: payload["discord_channel_id"],
383
+ forum_title: payload["forum_title"],
384
+ forum_reply_to_latest: payload["forum_reply_to_latest"]
385
+ )
386
+ result.to_json
387
+ end
388
+
389
+ post "/api/cron/reload" do
390
+ content_type :json
391
+ reload_cron_jobs!
392
+ { status: "reloaded", jobs: CRON_JOBS.size }.to_json
393
+ end
394
+
395
+ get "/api/cron/logs" do
396
+ content_type :json
397
+ job_id = params["id"]
398
+ halt 400, { error: "Missing ?id= parameter" }.to_json unless job_id && !job_id.empty?
399
+
400
+ logs = []
401
+ PROJECTS.each_value do |proj|
402
+ tmp_dir = File.join(proj["repo_path"], "tmp")
403
+ next unless Dir.exist?(tmp_dir)
404
+
405
+ Dir.glob(File.join(tmp_dir, "{agent-cron,cron-script}-#{job_id}-*.log")).each do |f|
406
+ logs << { file: f, size: File.size(f), modified: File.mtime(f).iso8601 }
407
+ end
408
+ end
409
+ logs.sort_by! { |l| l[:modified] }.reverse!
410
+ logs.first(20).to_json
411
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # User identity registry - resolves identities across platforms
4
- # (Discord, GitHub, Fizzy)
4
+ # (Discord, GitHub)
5
5
 
6
6
  USERS_FILE = File.join(BRAINIAC_DIR, "users.json")
7
7
 
@@ -40,11 +40,6 @@ def find_user_by_github_username(username)
40
40
  USER_REGISTRY["users"].find { |u| u.dig("identities", "github", "username") == username.to_s }
41
41
  end
42
42
 
43
- # Find user by Fizzy username
44
- def find_user_by_fizzy_username(username)
45
- USER_REGISTRY["users"].find { |u| u.dig("identities", "fizzy", "username") == username.to_s }
46
- end
47
-
48
43
  # Find user by canonical name
49
44
  def find_user_by_canonical_name(name)
50
45
  USER_REGISTRY["users"].find { |u| u["canonical_name"].downcase == name.downcase }
@@ -55,7 +50,6 @@ def find_user(identifier)
55
50
  find_user_by_discord_id(identifier) ||
56
51
  find_user_by_discord_username(identifier) ||
57
52
  find_user_by_github_username(identifier) ||
58
- find_user_by_fizzy_username(identifier) ||
59
53
  find_user_by_canonical_name(identifier)
60
54
  end
61
55
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Brainiac
4
4
  # @return [String] the current gem version
5
- VERSION = "0.0.6"
5
+ VERSION = "0.0.8"
6
6
  end
@@ -94,7 +94,8 @@ end
94
94
  def extract_text_from_mime(mime)
95
95
  # Try to find text/plain part
96
96
  if mime =~ %r{Content-Type: text/plain[^\r\n]*\r?\n(?:Content-Transfer-Encoding:[^\r\n]*\r?\n)?(?:\r?\n)(.*?)(?:\r?\n------=_Part|\z)}mi
97
- return Regexp.last_match(1).gsub("\r\n", "\n").strip
97
+ return Regexp.last_match(1).gsub("\r\n",
98
+ "\n").strip
98
99
  end
99
100
 
100
101
  # Fallback: extract text/html part and strip tags
data/lib/brainiac.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require_relative "brainiac/hooks"
1
2
  require_relative "brainiac/config"
2
3
  require_relative "brainiac/users"
3
4
  require_relative "brainiac/agents"
@@ -5,6 +6,12 @@ require_relative "brainiac/brain"
5
6
  require_relative "brainiac/skills"
6
7
  require_relative "brainiac/sessions"
7
8
  require_relative "brainiac/prompts"
8
- require_relative "brainiac/planning"
9
9
  require_relative "brainiac/helpers"
10
10
  require_relative "brainiac/cron"
11
+ require_relative "brainiac/plugins"
12
+
13
+ # Namespace for gem-based plugins (brainiac-whatsapp, brainiac-slack, etc.)
14
+ module Brainiac
15
+ module Plugins
16
+ end
17
+ end
data/monitor/daemon.rb CHANGED
@@ -2,32 +2,13 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # Brainiac Monitor Daemon
5
- # Polls /api/status and exposes agent state via Unix socket for waybar
6
- # No longer triggers config updates - waybar module polls this socket directly
5
+ # Polls /api/status and exposes agent state via Unix socket for waybar/xbar.
6
+ # Modules (waybar/status.rb, xbar/plugin.rb) read from this socket.
7
7
 
8
- require "json"
9
- require "net/http"
10
- require "socket"
8
+ require "fileutils"
9
+ require_relative "shared"
11
10
 
12
- SOCKET_PATH = "/tmp/brainiac-monitor.sock"
13
- API_URL = "http://localhost:4567/api/status"
14
11
  POLL_INTERVAL = 2 # seconds
15
- CONFIG_PATH = File.expand_path("~/.brainiac/waybar.json")
16
-
17
- # Load agent configuration from JSON
18
- def load_agent_config
19
- config = JSON.parse(File.read(CONFIG_PATH))
20
- agents = {}
21
- config["agents"].each do |agent|
22
- agents[agent["name"]] = { color: agent["color"], emoji: agent["emoji"] }
23
- end
24
- agents
25
- rescue StandardError => e
26
- warn "Failed to load waybar.json: #{e.message}"
27
- {}
28
- end
29
-
30
- AGENTS = load_agent_config.freeze
31
12
 
32
13
  @state = { sessions: [], count: 0, recent: [], last_update: nil }
33
14
 
@@ -67,12 +48,10 @@ def start_server
67
48
  server = UNIXServer.new(SOCKET_PATH)
68
49
  File.chmod(0o666, SOCKET_PATH)
69
50
 
70
- # Write PID file
71
51
  File.write("/tmp/brainiac-daemon.pid", Process.pid)
72
52
 
73
53
  puts "Monitor daemon started, socket: #{SOCKET_PATH}"
74
54
 
75
- # Start polling thread
76
55
  poller = Thread.new do
77
56
  loop do
78
57
  update_state
@@ -80,10 +59,8 @@ def start_server
80
59
  end
81
60
  end
82
61
 
83
- # Initial state fetch
84
62
  update_state
85
63
 
86
- # Accept client connections
87
64
  loop do
88
65
  client = server.accept
89
66
  Thread.new { handle_client(client) }