-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathagents.sh
More file actions
executable file
·163 lines (144 loc) · 8.14 KB
/
Copy pathagents.sh
File metadata and controls
executable file
·163 lines (144 loc) · 8.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
67
68
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#!/usr/bin/env bash
# Рубильник агентской машинерии. Всё поднимается и тушится отсюда.
#
# Сервер живёт в tmux-сессии, потому что cron и systemd в devcontainer недоступны,
# а tmux заодно даёт возможность подключиться и посмотреть живьём.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SESSION="agents"
SERVER_WINDOW="server"
RUNS="$ROOT/.agents-runs"
LOG="$RUNS/daemon.log"
STOP="$RUNS/STOP"
cd "$ROOT"
mkdir -p "$RUNS"
config_port() {
(cd agents && node -e "const{parse}=require('jsonc-parser');const fs=require('fs');process.stdout.write(String(parse(fs.readFileSync('config.jsonc','utf8')).ports?.$1??$2))") 2>/dev/null || echo "$2"
}
dashboard_port() { config_port dashboard 7777; }
usage() {
cat <<'EOF'
Использование: ./agents.sh <команда>
start поднять сервер в tmux-сессии "agents" (идемпотентно)
start --paused поднять сервер, но не запускать роли по расписанию
stop мягко: поставить STOP — по расписанию ничего не запускается
stop --now жёстко: остановить сервер и всех агентов (разговоры сохраняются)
restart перезапустить только сервер — агенты продолжают работать
status состояние сервера и агентов
logs tail -f лога сервера
attach подключиться к серверу (tmux)
watch <ключ> подключиться к живому агенту и договорить с ним руками
run <роль> [аргумент] форграунд, живой диалог — отладка скилла руками
spawn <роль> [аргумент] фоном, как по расписанию
wake <роль> <аргумент> синоним spawn: тот же ключ → продолжение той же сессии
list живые агенты
stop-agent <ключ> остановить одного агента
tick запустить роль orchestrate прямо сейчас
Повторный запуск с тем же аргументом НЕ создаёт нового агента, а продолжает
разговор с прежним — так его и зовут обратно после ревью.
EOF
}
# Наши агенты — и только они: окна нашей tmux-сессии, кроме окна сервера.
# Разговоры человека живут в других сессиях и сюда не попадают по построению.
agent_windows() {
tmux list-windows -t "$SESSION" -F '#{window_name}' 2>/dev/null | grep -vx "$SERVER_WINDOW" || true
}
SERVER_CMD="cd '$ROOT/agents' && npx tsx src/server.ts 2>&1 | tee -a '$LOG'"
server_running() {
tmux list-windows -t "$SESSION" -F '#{window_name}' 2>/dev/null | grep -qx "$SERVER_WINDOW"
}
# --paused: поднять серверы (MCP + витрину), но не запускать роли по расписанию.
# Нужно, когда запуски хочется делать руками и видеть их целиком.
apply_pause() {
if [ "${1:-}" = "--paused" ]; then
touch "$STOP"
else
rm -f "$STOP"
fi
}
cmd_start() {
[ -d agents/node_modules ] || (cd agents && npm install)
apply_pause "${1:-}"
if server_running; then
echo "Сервер уже поднят. Два планировщика = двойные запуски, так что второй не поднимаю."
[ -f "$STOP" ] && echo "режим: STOP" || echo "режим: работает по расписанию"
return 0
fi
# Сервер — окно "server"; агенты живут соседними окнами той же сессии,
# и tmux сам оказывается реестром: имя окна = ключ агента.
if tmux has-session -t "$SESSION" 2>/dev/null; then
tmux new-window -d -t "$SESSION" -n "$SERVER_WINDOW" -c "$ROOT" "$SERVER_CMD"
else
tmux new-session -d -s "$SESSION" -n "$SERVER_WINDOW" -c "$ROOT" "$SERVER_CMD"
fi
echo "Сервер поднят. Витрина: http://localhost:$(dashboard_port)"
}
# Рестарт трогает ТОЛЬКО окно сервера. Агенты — соседние окна, и терять их незачем:
# сервер не владеет их состоянием, он выводит его из мира заново (окна tmux, claude
# agents --json, git). Поэтому его перезапуск для агентов вообще незаметен.
cmd_restart() {
apply_pause "${1:-}"
if ! server_running; then
cmd_start "${1:-}"
return 0
fi
tmux respawn-window -k -c "$ROOT" -t "$SESSION:$SERVER_WINDOW" "$SERVER_CMD"
local kept
kept="$(agent_windows | wc -l)"
echo "Сервер перезапущен. Агентов не тронуто: $kept"
}
cmd_stop() {
if [ "${1:-}" = "--now" ]; then
for name in $(agent_windows); do
echo "останавливаю агента $name"
tmux kill-window -t "$SESSION:$name" || true
done
tmux kill-session -t "$SESSION" 2>/dev/null || true
echo "Сервер и агенты остановлены."
return 0
fi
touch "$STOP"
echo "STOP поставлен: по расписанию ничего не запускается."
echo "Снять — ./agents.sh start, или кнопкой на витрине."
}
cmd_status() {
if tmux has-session -t "$SESSION" 2>/dev/null; then
echo "сервер: работает (tmux: $SESSION)"
else
echo "сервер: не запущен"
fi
[ -f "$STOP" ] && echo "режим: STOP" || echo "режим: работает по расписанию"
echo
curl -s "http://127.0.0.1:$(dashboard_port)/api/state" | node -e '
let raw = ""; process.stdin.on("data", c => raw += c).on("end", () => {
if (!raw) { console.log("витрина не отвечает"); return; }
const s = JSON.parse(raw);
console.log("роли: " + s.roles.map(r => r.name + (r.everyMin ? ` (раз в ${r.everyMin}м)` : "")).join(", "));
if (s.running.length) console.log("сейчас выполняются: " + s.running.join(", "));
if (!s.agents.length) { console.log("агентов нет"); return; }
for (const a of s.agents)
console.log(` ${a.key} ${a.status}${a.state ? "/" + a.state : ""} idle ${a.idleMin ?? "—"}м ${a.alive ? "" : "МЁРТВ"}`);
});
' || true
}
# Запуск роли немедленно — через витрину, чтобы он прошёл тем же путём, что и по расписанию
# (и попал в журнал с trigger: dashboard). Сервер не поднят — говорим прямо.
cmd_tick() {
local role="${1:-orchestrate}"
tmux has-session -t "$SESSION" 2>/dev/null || { echo "Сервер не поднят: ./agents.sh start --paused"; exit 1; }
curl -sS -X POST "http://127.0.0.1:$(dashboard_port)/api/run?role=$role" && echo
}
case "${1:-}" in
start) shift; cmd_start "$@" ;;
stop) shift; cmd_stop "$@" ;;
restart) shift; cmd_restart "$@" ;;
status) cmd_status ;;
logs) touch "$LOG"; tail -f "$LOG" ;;
attach) tmux attach -t "$SESSION:$SERVER_WINDOW" ;;
watch) shift; [ -n "${1:-}" ] || { echo "Использование: ./agents.sh watch <ключ агента>"; exit 2; }
tmux attach -t "$SESSION:$1" ;;
tick) shift; cmd_tick "$@" ;;
run|spawn|wake|list|stop-agent) (cd agents && npx tsx src/cli.ts "$@") ;;
""|-h|--help) usage ;;
*) echo "Неизвестная команда: $1"; echo; usage; exit 2 ;;
esac