+39
-23
run_bots.py
+39
-23
run_bots.py
···
1
1
#!/usr/bin/env python3
2
2
"""
3
-
Multi-bot launcher for running all 6 Void agents simultaneously with aggregated logs.
3
+
Multi-bot launcher for running all 5 Void agents simultaneously with aggregated logs.
4
4
Usage: python run_bots.py [bsky.py arguments...]
5
5
Example: python run_bots.py --synthesis-interval 0 --no-git
6
6
7
-
Bots: void, herald, archivist, grunk, ezra, blank
7
+
Bots: void, herald, archivist, grunk, ezra
8
8
"""
9
9
10
10
import subprocess
···
21
21
'archivist': '\033[92m', # Green
22
22
'grunk': '\033[93m', # Yellow
23
23
'ezra': '\033[95m', # Magenta
24
-
'blank': '\033[96m', # Cyan
25
24
}
26
25
RESET = '\033[0m'
27
26
BOLD = '\033[1m'
···
33
32
'archivist': 'configs/archivist.yaml',
34
33
'grunk': 'configs/grunk.yaml',
35
34
'ezra': 'configs/ezra.yaml',
36
-
'blank': 'configs/blank.yaml',
37
35
}
38
36
39
37
# Track all running processes
40
38
processes: List[subprocess.Popen] = []
41
39
shutdown_flag = threading.Event()
40
+
shutdown_in_progress = threading.Lock()
42
41
43
42
44
43
def stream_output(proc: subprocess.Popen, bot_name: str, stream_name: str):
···
67
66
68
67
def shutdown_handler(signum, frame):
69
68
"""Handle shutdown signals gracefully."""
70
-
print(f"\n{BOLD}๐ Shutting down all bots...{RESET}", flush=True)
71
-
shutdown_flag.set()
69
+
# Prevent re-entry if already shutting down
70
+
if not shutdown_in_progress.acquire(blocking=False):
71
+
# Already shutting down, force kill on second Ctrl+C
72
+
print(f"\n{BOLD}โก Force killing all processes...{RESET}", flush=True)
73
+
for proc in processes:
74
+
if proc.poll() is None:
75
+
try:
76
+
proc.kill()
77
+
except:
78
+
pass
79
+
sys.exit(1)
72
80
73
-
# Send SIGTERM to all processes
74
-
for proc in processes:
75
-
if proc.poll() is None: # Still running
76
-
try:
77
-
proc.terminate()
78
-
except Exception as e:
79
-
print(f"Error terminating process: {e}", flush=True)
81
+
try:
82
+
print(f"\n{BOLD}๐ Shutting down all bots...{RESET}", flush=True)
83
+
shutdown_flag.set()
80
84
81
-
# Wait for graceful shutdown (up to 10 seconds each)
82
-
for i, proc in enumerate(processes):
83
-
try:
84
-
proc.wait(timeout=10)
85
-
except subprocess.TimeoutExpired:
86
-
print(f"Process {i} didn't exit gracefully, force killing...", flush=True)
87
-
proc.kill()
88
-
proc.wait()
85
+
# Send SIGTERM to all processes
86
+
for proc in processes:
87
+
if proc.poll() is None: # Still running
88
+
try:
89
+
proc.terminate()
90
+
except Exception as e:
91
+
print(f"Error terminating process: {e}", flush=True)
92
+
93
+
# Wait for graceful shutdown (up to 3 seconds each)
94
+
for i, proc in enumerate(processes):
95
+
try:
96
+
proc.wait(timeout=3)
97
+
except subprocess.TimeoutExpired:
98
+
print(f"Process {i} didn't exit gracefully, force killing...", flush=True)
99
+
proc.kill()
100
+
try:
101
+
proc.wait(timeout=1)
102
+
except:
103
+
pass
89
104
90
-
print(f"{BOLD}โ
All bots stopped{RESET}", flush=True)
91
-
sys.exit(0)
105
+
print(f"{BOLD}โ
All bots stopped{RESET}", flush=True)
106
+
finally:
107
+
sys.exit(0)
92
108
93
109
94
110
def main():