vibego 0.2.58__py3-none-any.whl → 1.0.10__py3-none-any.whl
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.
- bot.py +1346 -1136
- logging_setup.py +25 -18
- master.py +812 -506
- project_repository.py +42 -40
- scripts/__init__.py +1 -2
- scripts/bump_version.sh +57 -55
- scripts/log_writer.py +19 -16
- scripts/master_healthcheck.py +38 -138
- scripts/models/claudecode.sh +4 -4
- scripts/models/codex.sh +1 -1
- scripts/models/common.sh +24 -6
- scripts/models/gemini.sh +2 -2
- scripts/publish.sh +50 -50
- scripts/requirements.txt +1 -0
- scripts/run_bot.sh +41 -17
- scripts/session_pointer_watch.py +265 -0
- scripts/start.sh +147 -120
- scripts/start_tmux_codex.sh +33 -8
- scripts/stop_all.sh +21 -21
- scripts/stop_bot.sh +31 -10
- scripts/test_deps_check.sh +32 -28
- tasks/__init__.py +1 -1
- tasks/commands.py +4 -4
- tasks/constants.py +1 -1
- tasks/fsm.py +9 -9
- tasks/models.py +7 -7
- tasks/service.py +56 -101
- vibego-1.0.10.dist-info/METADATA +226 -0
- {vibego-0.2.58.dist-info → vibego-1.0.10.dist-info}/RECORD +38 -36
- vibego-1.0.10.dist-info/licenses/LICENSE +201 -0
- vibego_cli/__init__.py +5 -4
- vibego_cli/__main__.py +1 -2
- vibego_cli/config.py +9 -9
- vibego_cli/deps.py +8 -9
- vibego_cli/main.py +63 -63
- vibego-0.2.58.dist-info/METADATA +0 -197
- {vibego-0.2.58.dist-info → vibego-1.0.10.dist-info}/WHEEL +0 -0
- {vibego-0.2.58.dist-info → vibego-1.0.10.dist-info}/entry_points.txt +0 -0
- {vibego-0.2.58.dist-info → vibego-1.0.10.dist-info}/top_level.txt +0 -0
scripts/start.sh
CHANGED
|
@@ -3,6 +3,9 @@ set -eo pipefail
|
|
|
3
3
|
|
|
4
4
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
5
|
MASTER_CONFIG_ROOT="${MASTER_CONFIG_ROOT:-$HOME/.config/vibego}"
|
|
6
|
+
RUNTIME_DIR="${VIBEGO_RUNTIME_ROOT:-$MASTER_CONFIG_ROOT/runtime}"
|
|
7
|
+
VENV_DIR="$RUNTIME_DIR/.venv"
|
|
8
|
+
LEGACY_VENV_DIR="$ROOT_DIR/.venv"
|
|
6
9
|
STATE_DIR="$MASTER_CONFIG_ROOT/state"
|
|
7
10
|
LOG_DIR="$MASTER_CONFIG_ROOT/logs"
|
|
8
11
|
LOCK_FILE="$STATE_DIR/master_restart.lock"
|
|
@@ -10,8 +13,8 @@ START_LOG="$LOG_DIR/start.log"
|
|
|
10
13
|
CODEX_STAMP_FILE="$STATE_DIR/npm_codex_install.stamp"
|
|
11
14
|
CODEX_INSTALL_TTL="${CODEX_INSTALL_TTL:-86400}"
|
|
12
15
|
|
|
13
|
-
#
|
|
14
|
-
#
|
|
16
|
+
# Unified restart signal file path: use the configuration directory instead of the code directory
|
|
17
|
+
# In this way, the master installed by pipx and the master run from the source code can share the same signal file.
|
|
15
18
|
export MASTER_RESTART_SIGNAL_PATH="$STATE_DIR/restart_signal.json"
|
|
16
19
|
export LOG_ROOT="${LOG_ROOT:-$LOG_DIR}"
|
|
17
20
|
if [[ -z "${LOG_FILE:-}" ]]; then
|
|
@@ -42,34 +45,35 @@ cd "$ROOT_DIR"
|
|
|
42
45
|
|
|
43
46
|
mkdir -p "$(dirname "$LOCK_FILE")"
|
|
44
47
|
mkdir -p "$(dirname "$START_LOG")"
|
|
48
|
+
mkdir -p "$RUNTIME_DIR"
|
|
45
49
|
touch "$START_LOG"
|
|
46
50
|
exec >>"$START_LOG"
|
|
47
51
|
exec 2>&1
|
|
48
52
|
|
|
49
|
-
log_info "start.sh
|
|
53
|
+
log_info "start.sh start, pid=$$"
|
|
50
54
|
|
|
51
55
|
if [[ -f "$LOCK_FILE" ]]; then
|
|
52
|
-
log_error "
|
|
56
|
+
log_error "Already have start.sh During execution, skip this startup."
|
|
53
57
|
exit 1
|
|
54
58
|
fi
|
|
55
59
|
|
|
56
60
|
printf '%d\n' $$ > "$LOCK_FILE"
|
|
57
61
|
|
|
58
|
-
log_info "
|
|
62
|
+
log_info "Lock file created:$LOCK_FILE"
|
|
59
63
|
|
|
60
64
|
ensure_codex_installed() {
|
|
61
65
|
local need_install=1
|
|
62
66
|
local now
|
|
63
67
|
local codex_bin
|
|
64
68
|
if ! command -v npm >/dev/null 2>&1; then
|
|
65
|
-
log_error "
|
|
69
|
+
log_error "npm not detected, executable missing, skipped @openai/codex Global installation"
|
|
66
70
|
return
|
|
67
71
|
fi
|
|
68
72
|
|
|
69
|
-
log_info "
|
|
73
|
+
log_info "npm version detected:$(npm --version)"
|
|
70
74
|
|
|
71
75
|
if [[ ! "$CODEX_INSTALL_TTL" =~ ^[0-9]+$ ]]; then
|
|
72
|
-
log_error "CODEX_INSTALL_TTL
|
|
76
|
+
log_error "CODEX_INSTALL_TTL Illegal value:$CODEX_INSTALL_TTL, Fallback to 86400 seconds"
|
|
73
77
|
CODEX_INSTALL_TTL=86400
|
|
74
78
|
fi
|
|
75
79
|
|
|
@@ -99,11 +103,11 @@ ensure_codex_installed() {
|
|
|
99
103
|
fi
|
|
100
104
|
|
|
101
105
|
if (( need_install )); then
|
|
102
|
-
log_info "
|
|
106
|
+
log_info "Start executing npm install -g @openai/codex@latest"
|
|
103
107
|
if npm install -g @openai/codex@latest; then
|
|
104
108
|
now=$(date +%s)
|
|
105
109
|
printf '%s\n' "$now" > "$CODEX_STAMP_FILE"
|
|
106
|
-
log_info "npm install -g @openai/codex@latest
|
|
110
|
+
log_info "npm install -g @openai/codex@latest success"
|
|
107
111
|
else
|
|
108
112
|
local status=$?
|
|
109
113
|
log_error "npm install -g @openai/codex@latest failed (exit code ${status}); continuing startup"
|
|
@@ -114,8 +118,9 @@ ensure_codex_installed() {
|
|
|
114
118
|
ensure_codex_installed
|
|
115
119
|
|
|
116
120
|
select_python_binary() {
|
|
117
|
-
#
|
|
121
|
+
# Select a compatible CPython version; defaults accept 3.9-3.14 and can be overridden via env variables.
|
|
118
122
|
local allow_py313="${VIBEGO_ALLOW_PY313:-}"
|
|
123
|
+
local supported_max_minor="${VIBEGO_MAX_MINOR:-14}"
|
|
119
124
|
local candidates=()
|
|
120
125
|
local chosen=""
|
|
121
126
|
local name
|
|
@@ -141,121 +146,127 @@ select_python_binary() {
|
|
|
141
146
|
local major="${version_raw%%.*}"
|
|
142
147
|
local minor="${version_raw#*.}"
|
|
143
148
|
if [[ "$major" != "3" ]]; then
|
|
144
|
-
log_line "
|
|
149
|
+
log_line "jump over ${name} (Version ${version_raw}): Non-CPython 3.x" >&2
|
|
145
150
|
continue
|
|
146
151
|
fi
|
|
147
152
|
local explicit_override=0
|
|
148
153
|
if [[ -n "${VIBEGO_PYTHON:-}" && "$name" == "$VIBEGO_PYTHON" ]]; then
|
|
149
154
|
explicit_override=1
|
|
150
155
|
fi
|
|
151
|
-
if [[ "$minor" =~ ^[0-9]+$ ]] && (( minor == 13 )) &&
|
|
152
|
-
|
|
153
|
-
|
|
156
|
+
if [[ "$minor" =~ ^[0-9]+$ ]] && (( minor == 13 )) && (( explicit_override == 0 )); then
|
|
157
|
+
if [[ "$allow_py313" == "0" ]]; then
|
|
158
|
+
log_line "Skip ${name} (version ${version_raw}): disabled explicitly by VIBEGO_ALLOW_PY313=0" >&2
|
|
159
|
+
continue
|
|
160
|
+
fi
|
|
161
|
+
log_line "Detected ${name} (version ${version_raw}): Python 3.13 accepted by default; use VIBEGO_ALLOW_PY313=1 to prefer or 0 to disable" >&2
|
|
154
162
|
fi
|
|
155
|
-
if [[ "$minor" =~ ^[0-9]+$ ]] && (( minor >
|
|
156
|
-
log_line "
|
|
163
|
+
if [[ "$minor" =~ ^[0-9]+$ ]] && (( minor > supported_max_minor )) && (( explicit_override == 0 )); then
|
|
164
|
+
log_line "Skip ${name} (version ${version_raw}): above supported ceiling 3.${supported_max_minor}; override with VIBEGO_MAX_MINOR if needed" >&2
|
|
157
165
|
continue
|
|
158
166
|
fi
|
|
159
167
|
if [[ "$minor" =~ ^[0-9]+$ ]] && (( minor < 9 )); then
|
|
160
|
-
log_line "
|
|
168
|
+
log_line "jump over ${name} (Version ${version_raw}): less than 3.9, May be missing official wheels" >&2
|
|
161
169
|
continue
|
|
162
170
|
fi
|
|
171
|
+
if [[ "$minor" =~ ^[0-9]+$ ]] && (( minor >= 14 )); then
|
|
172
|
+
log_line "Detected ${name} (version ${version_raw}): ensure dependencies support this Python version" >&2
|
|
173
|
+
fi
|
|
163
174
|
chosen="$name"
|
|
164
|
-
log_line "
|
|
175
|
+
log_line "Using the Python interpreter:${chosen} (Version ${version_raw})" >&2
|
|
165
176
|
break
|
|
166
177
|
done
|
|
167
178
|
|
|
168
179
|
if [[ -z "$chosen" ]]; then
|
|
169
|
-
log_error "
|
|
180
|
+
log_error "no satisfaction found <=3.13 The Python interpreter can be set by VIBEGO_PYTHON Specify path"
|
|
170
181
|
exit 1
|
|
171
182
|
fi
|
|
172
183
|
|
|
173
184
|
printf '%s' "$chosen"
|
|
174
185
|
}
|
|
175
186
|
|
|
176
|
-
#
|
|
187
|
+
# Check whether Python dependencies are installed completely
|
|
177
188
|
check_deps_installed() {
|
|
178
|
-
#
|
|
179
|
-
if [[ ! -d "$
|
|
180
|
-
log_info "
|
|
189
|
+
# Check if the virtual environment exists
|
|
190
|
+
if [[ ! -d "$VENV_DIR" ]]; then
|
|
191
|
+
log_info "The virtual environment does not exist and needs to be initialized."
|
|
181
192
|
return 1
|
|
182
193
|
fi
|
|
183
194
|
|
|
184
|
-
#
|
|
185
|
-
if [[ ! -x "$
|
|
186
|
-
log_info "
|
|
195
|
+
# Check the virtual environment's Python interpreter
|
|
196
|
+
if [[ ! -x "$VENV_DIR/bin/python" ]]; then
|
|
197
|
+
log_info "Virtual environment Python interpreter missing"
|
|
187
198
|
return 1
|
|
188
199
|
fi
|
|
189
200
|
|
|
190
|
-
#
|
|
191
|
-
# aiogram: Telegram
|
|
192
|
-
# aiohttp:
|
|
193
|
-
# aiosqlite:
|
|
194
|
-
if ! "$
|
|
195
|
-
log_info "
|
|
201
|
+
# Activate the virtual environment and check key dependency packages
|
|
202
|
+
# aiogram: Telegram Botframe
|
|
203
|
+
# aiohttp: Asynchronous HTTP client
|
|
204
|
+
# aiosqlite: Asynchronous SQLite database
|
|
205
|
+
if ! "$VENV_DIR/bin/python" -c "import aiogram, aiohttp, aiosqlite" 2>/dev/null; then
|
|
206
|
+
log_info "Key dependency packages are missing or damaged"
|
|
196
207
|
return 1
|
|
197
208
|
fi
|
|
198
209
|
|
|
199
|
-
log_info "
|
|
210
|
+
log_info "The dependency check passed and the virtual environment is complete"
|
|
200
211
|
return 0
|
|
201
212
|
}
|
|
202
213
|
|
|
203
|
-
#
|
|
214
|
+
# Robust function to clean up old master processes (improved version: supports PID files + pgrep Double insurance)
|
|
204
215
|
cleanup_old_master() {
|
|
205
|
-
local max_wait=10 #
|
|
216
|
+
local max_wait=10 # Wait up to 10 seconds to exit gracefully
|
|
206
217
|
local waited=0
|
|
207
218
|
local old_pids=""
|
|
208
219
|
local master_pid_file="$STATE_DIR/master.pid"
|
|
209
220
|
|
|
210
|
-
#
|
|
221
|
+
# Option 1: Read from the PID file first
|
|
211
222
|
if [[ -f "$master_pid_file" ]]; then
|
|
212
223
|
local pid_from_file
|
|
213
224
|
pid_from_file=$(cat "$master_pid_file" 2>/dev/null || true)
|
|
214
225
|
if [[ "$pid_from_file" =~ ^[0-9]+$ ]]; then
|
|
215
226
|
if kill -0 "$pid_from_file" 2>/dev/null; then
|
|
216
227
|
old_pids="$pid_from_file"
|
|
217
|
-
log_info "
|
|
228
|
+
log_info "Old master instance detected from PID file (PID: $old_pids)"
|
|
218
229
|
else
|
|
219
|
-
log_info "PID
|
|
230
|
+
log_info "PID The file exists but the process is no longer there, clean up expired PID files"
|
|
220
231
|
rm -f "$master_pid_file"
|
|
221
232
|
fi
|
|
222
233
|
fi
|
|
223
234
|
fi
|
|
224
235
|
|
|
225
|
-
#
|
|
236
|
+
# Option 2: Use pgrep to find (supports multiple running modes)
|
|
226
237
|
if [[ -z "$old_pids" ]]; then
|
|
227
|
-
#
|
|
228
|
-
# - python.*master.py
|
|
229
|
-
# - Python.*master.py
|
|
230
|
-
# - bot.py
|
|
238
|
+
# Matching mode: supports source code running and pipx installation methods
|
|
239
|
+
# - python.*master.py(Source code running)
|
|
240
|
+
# - Python.*master.py(macOS Python on.app)
|
|
241
|
+
# - bot.py(pipx installed master alias)
|
|
231
242
|
local pgrep_pids
|
|
232
243
|
pgrep_pids=$(pgrep -f "master\.py$" 2>/dev/null || true)
|
|
233
244
|
if [[ -n "$pgrep_pids" ]]; then
|
|
234
245
|
old_pids="$pgrep_pids"
|
|
235
|
-
log_info "
|
|
246
|
+
log_info "Old master instance detected via pgrep (PID: $old_pids)"
|
|
236
247
|
fi
|
|
237
248
|
fi
|
|
238
249
|
|
|
239
|
-
#
|
|
250
|
+
# If neither method is found, it means there is no old process.
|
|
240
251
|
if [[ -z "$old_pids" ]]; then
|
|
241
|
-
log_info "
|
|
252
|
+
log_info "Old master instance not detected"
|
|
242
253
|
return 0
|
|
243
254
|
fi
|
|
244
255
|
|
|
245
|
-
#
|
|
246
|
-
log_info "
|
|
256
|
+
# Start cleaning up old processes
|
|
257
|
+
log_info "Gracefully terminating old master instance (PID: $old_pids)..."
|
|
247
258
|
|
|
248
|
-
#
|
|
259
|
+
# Send SIGTERM signal to terminate gracefully
|
|
249
260
|
for pid in $old_pids; do
|
|
250
261
|
kill -15 "$pid" 2>/dev/null || true
|
|
251
262
|
done
|
|
252
263
|
|
|
253
|
-
#
|
|
264
|
+
# Loop waiting for process to exit
|
|
254
265
|
while (( waited < max_wait )); do
|
|
255
266
|
sleep 1
|
|
256
267
|
((waited++))
|
|
257
268
|
|
|
258
|
-
#
|
|
269
|
+
# Check if all PIDs have exited
|
|
259
270
|
local all_exited=1
|
|
260
271
|
for pid in $old_pids; do
|
|
261
272
|
if kill -0 "$pid" 2>/dev/null; then
|
|
@@ -265,20 +276,20 @@ cleanup_old_master() {
|
|
|
265
276
|
done
|
|
266
277
|
|
|
267
278
|
if (( all_exited )); then
|
|
268
|
-
log_info "
|
|
279
|
+
log_info "OK: The old master exited gracefully (elapsed ${waited}s)"
|
|
269
280
|
rm -f "$master_pid_file"
|
|
270
281
|
return 0
|
|
271
282
|
fi
|
|
272
283
|
done
|
|
273
284
|
|
|
274
|
-
#
|
|
275
|
-
log_info "
|
|
285
|
+
# Graceful termination timeout, execution forced end
|
|
286
|
+
log_info "graceful termination timeout (${max_wait}Second), Execute forced end..."
|
|
276
287
|
for pid in $old_pids; do
|
|
277
288
|
kill -9 "$pid" 2>/dev/null || true
|
|
278
289
|
done
|
|
279
290
|
sleep 2
|
|
280
291
|
|
|
281
|
-
#
|
|
292
|
+
# final check
|
|
282
293
|
local remaining_pids=""
|
|
283
294
|
for pid in $old_pids; do
|
|
284
295
|
if kill -0 "$pid" 2>/dev/null; then
|
|
@@ -287,75 +298,91 @@ cleanup_old_master() {
|
|
|
287
298
|
done
|
|
288
299
|
|
|
289
300
|
if [[ -n "$remaining_pids" ]]; then
|
|
290
|
-
log_error "
|
|
291
|
-
log_error "
|
|
301
|
+
log_error "ERROR: Unable to clean up old master process (residual PID:$remaining_pids)"
|
|
302
|
+
log_error "Please execute manually: kill -9$remaining_pids"
|
|
292
303
|
exit 1
|
|
293
304
|
fi
|
|
294
305
|
|
|
295
|
-
log_info "
|
|
306
|
+
log_info "OK: The old master instance has been forcefully cleaned"
|
|
296
307
|
rm -f "$master_pid_file"
|
|
297
308
|
return 0
|
|
298
309
|
}
|
|
299
310
|
|
|
300
|
-
#
|
|
311
|
+
# Call the cleanup function
|
|
301
312
|
cleanup_old_master
|
|
302
313
|
|
|
303
|
-
#
|
|
314
|
+
# Smart dependency management: install only when necessary
|
|
304
315
|
REQUIREMENTS_FILE="${VIBEGO_REQUIREMENTS_PATH:-$ROOT_DIR/scripts/requirements.txt}"
|
|
305
316
|
if [[ ! -f "$REQUIREMENTS_FILE" ]]; then
|
|
306
|
-
log_error "
|
|
317
|
+
log_error "Dependency files are missing: $REQUIREMENTS_FILE"
|
|
307
318
|
exit 1
|
|
308
319
|
fi
|
|
309
320
|
|
|
310
321
|
PYTHON_BIN="$(select_python_binary)"
|
|
311
322
|
|
|
312
|
-
#
|
|
323
|
+
# Compatible with oldVersion: If it is detected in the warehouse .venv, then migrate to the runtime directory
|
|
324
|
+
migrate_legacy_venv() {
|
|
325
|
+
if [[ -d "$LEGACY_VENV_DIR" && ! -e "$VENV_DIR" ]]; then
|
|
326
|
+
log_info "Old virtual environment directory detected:$LEGACY_VENV_DIR, Prepare to migrate to $VENV_DIR"
|
|
327
|
+
if mv "$LEGACY_VENV_DIR" "$VENV_DIR"; then
|
|
328
|
+
log_info "The virtual environment has been migrated to:$VENV_DIR"
|
|
329
|
+
else
|
|
330
|
+
log_error "Migration of the old virtual environment failed. Please check manually and try again."
|
|
331
|
+
fi
|
|
332
|
+
fi
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
migrate_legacy_venv
|
|
336
|
+
|
|
337
|
+
# Check if dependencies need to be installed
|
|
313
338
|
if check_deps_installed; then
|
|
314
|
-
log_info "
|
|
315
|
-
|
|
339
|
+
log_info "Dependencies are installed and complete, jump overpip install(accelerated restart)"
|
|
340
|
+
# shellcheck disable=SC1091
|
|
341
|
+
source "$VENV_DIR/bin/activate"
|
|
316
342
|
else
|
|
317
|
-
log_info "
|
|
343
|
+
log_info "First startup or missing dependencies, installing dependencies..."
|
|
318
344
|
|
|
319
|
-
#
|
|
320
|
-
"$PYTHON_BIN" -m venv
|
|
321
|
-
|
|
345
|
+
# Create or rebuild a virtual environment
|
|
346
|
+
"$PYTHON_BIN" -m venv "$VENV_DIR"
|
|
347
|
+
# shellcheck disable=SC1091
|
|
348
|
+
source "$VENV_DIR/bin/activate"
|
|
322
349
|
|
|
323
|
-
#
|
|
324
|
-
#
|
|
325
|
-
log_info "
|
|
350
|
+
# Install dependencies
|
|
351
|
+
# Redirect pip output to log file to avoid BrokenPipe errors
|
|
352
|
+
log_info "Start executing pip install -r $REQUIREMENTS_FILE"
|
|
326
353
|
PIP_LOG_FILE="$LOG_DIR/pip_install_$(date +%Y%m%d_%H%M%S).log"
|
|
327
354
|
if pip install -r "$REQUIREMENTS_FILE" > "$PIP_LOG_FILE" 2>&1; then
|
|
328
|
-
log_info "
|
|
355
|
+
log_info "Dependency installation completed"
|
|
329
356
|
else
|
|
330
357
|
PIP_EXIT_CODE=$?
|
|
331
|
-
log_error "pip install
|
|
332
|
-
#
|
|
358
|
+
log_error "pip install Failure, exit code=$PIP_EXIT_CODE, See details $PIP_LOG_FILE"
|
|
359
|
+
# If it is BrokenPipe (Exit code 141), Verify that dependencies are actually installed
|
|
333
360
|
if [[ $PIP_EXIT_CODE -eq 141 ]]; then
|
|
334
|
-
log_info "
|
|
335
|
-
#
|
|
336
|
-
if "$
|
|
337
|
-
log_info "
|
|
361
|
+
log_info "BrokenPipe error detected, dependency integrity verified..."
|
|
362
|
+
# Verify whether key dependency packages can be imported
|
|
363
|
+
if "$VENV_DIR/bin/python" -c "import aiogram, aiohttp, aiosqlite" 2>/dev/null; then
|
|
364
|
+
log_info "Dependency verification passed, BrokenPipe can be ignored and execution continues."
|
|
338
365
|
else
|
|
339
|
-
log_error "
|
|
366
|
+
log_error "Dependency verification failed. Although it is BrokenPipe, the dependency is not completely installed."
|
|
340
367
|
exit 1
|
|
341
368
|
fi
|
|
342
369
|
else
|
|
343
|
-
#
|
|
370
|
+
# If there are other errors, exit directly.
|
|
344
371
|
exit $PIP_EXIT_CODE
|
|
345
372
|
fi
|
|
346
373
|
fi
|
|
347
374
|
fi
|
|
348
375
|
|
|
349
|
-
#
|
|
350
|
-
#
|
|
376
|
+
# Start the master in the background, and the log falls in vibe.log
|
|
377
|
+
# Explicitly pass the restart flag environment variable if present
|
|
351
378
|
if [[ -n "${MASTER_RESTART_EXPECTED:-}" ]]; then
|
|
352
|
-
log_info "
|
|
379
|
+
log_info "Restart flag environment variable MASTER detected_RESTART_EXPECTED=$MASTER_RESTART_EXPECTED"
|
|
353
380
|
export MASTER_RESTART_EXPECTED
|
|
354
381
|
fi
|
|
355
382
|
|
|
356
|
-
log_info "
|
|
383
|
+
log_info "Prepare to start the master process..."
|
|
357
384
|
|
|
358
|
-
#
|
|
385
|
+
# Clean old error logs (keep the last 10)
|
|
359
386
|
cleanup_old_error_logs() {
|
|
360
387
|
local error_log_pattern="$LOG_DIR/master_error_*.log"
|
|
361
388
|
local error_logs
|
|
@@ -366,7 +393,7 @@ cleanup_old_error_logs() {
|
|
|
366
393
|
((count++))
|
|
367
394
|
if (( count > 10 )); then
|
|
368
395
|
rm -f "$logfile"
|
|
369
|
-
log_info "
|
|
396
|
+
log_info "Cleaned old error logs: $logfile"
|
|
370
397
|
fi
|
|
371
398
|
done <<< "$error_logs"
|
|
372
399
|
fi
|
|
@@ -374,73 +401,73 @@ cleanup_old_error_logs() {
|
|
|
374
401
|
|
|
375
402
|
cleanup_old_error_logs
|
|
376
403
|
|
|
377
|
-
#
|
|
404
|
+
# Create a timestamped error log file
|
|
378
405
|
MASTER_ERROR_LOG="$LOG_DIR/master_error_$(date +%Y%m%d_%H%M%S).log"
|
|
379
406
|
MASTER_STDOUT_LOG="$LOG_DIR/master_stdout.log"
|
|
380
407
|
|
|
381
|
-
#
|
|
382
|
-
#
|
|
383
|
-
#
|
|
384
|
-
MASTER_RESTART_SIGNAL_PATH="$MASTER_RESTART_SIGNAL_PATH" nohup "$
|
|
408
|
+
# Explicitly pass environment variables to the nohup process to ensure that the restart signal file path is correct
|
|
409
|
+
# Using virtual environment Python interpreter, avoidVersionMismatch causes dependency loading to fail
|
|
410
|
+
# Important: Save stderr to a log file to facilitate troubleshooting startup failures.
|
|
411
|
+
MASTER_RESTART_SIGNAL_PATH="$MASTER_RESTART_SIGNAL_PATH" nohup "$VENV_DIR/bin/python" master.py > "$MASTER_STDOUT_LOG" 2> "$MASTER_ERROR_LOG" &
|
|
385
412
|
MASTER_PID=$!
|
|
386
413
|
|
|
387
|
-
#
|
|
414
|
+
# Robustness check: ensure processsuccessstart up
|
|
388
415
|
if [[ -z "${MASTER_PID:-}" ]]; then
|
|
389
|
-
log_error "
|
|
390
|
-
log_error "
|
|
416
|
+
log_error "ERROR: Unable to obtain master process PID; startup failed"
|
|
417
|
+
log_error "Possible reasons: python command is not available or master.py There are grammatical errors"
|
|
391
418
|
exit 1
|
|
392
419
|
fi
|
|
393
420
|
|
|
394
|
-
#
|
|
421
|
+
# Check if the process is still running after a short wait
|
|
395
422
|
sleep 0.5
|
|
396
423
|
if ! kill -0 "$MASTER_PID" 2>/dev/null; then
|
|
397
|
-
log_error "
|
|
398
|
-
log_error "
|
|
399
|
-
log_error " - master.py
|
|
400
|
-
log_error " -
|
|
401
|
-
log_error " -
|
|
424
|
+
log_error "ERROR: Master process exited immediately after starting (PID=$MASTER_PID)"
|
|
425
|
+
log_error "Check, please:"
|
|
426
|
+
log_error " - master.py whetherThere are grammatical errors: python master.py"
|
|
427
|
+
log_error " - Are dependencies complete?: pip list | grep aiogram"
|
|
428
|
+
log_error " - error log: $MASTER_ERROR_LOG"
|
|
402
429
|
|
|
403
|
-
#
|
|
430
|
+
# outputerror logthe end 20 OK, Help locate problems quickly
|
|
404
431
|
if [[ -s "$MASTER_ERROR_LOG" ]]; then
|
|
405
432
|
log_error ""
|
|
406
|
-
log_error "===
|
|
433
|
+
log_error "=== error logat last 20 OK ==="
|
|
407
434
|
tail -20 "$MASTER_ERROR_LOG" | while IFS= read -r line; do
|
|
408
435
|
log_error " $line"
|
|
409
436
|
done
|
|
410
437
|
log_error "=========================="
|
|
411
438
|
else
|
|
412
|
-
log_error "
|
|
439
|
+
log_error "error logFile is empty, It may be an environment variable or path problem"
|
|
413
440
|
fi
|
|
414
441
|
|
|
415
442
|
exit 1
|
|
416
443
|
fi
|
|
417
444
|
|
|
418
|
-
log_info "master
|
|
445
|
+
log_info "master Started in background, PID=$MASTER_PID, Log writing ${LOG_FILE}"
|
|
419
446
|
|
|
420
|
-
#
|
|
421
|
-
log_info "
|
|
447
|
+
# Health check: wait for master to come online and verify key workers
|
|
448
|
+
log_info "Start master readiness check..."
|
|
422
449
|
HEALTHCHECK_START=$(date +%s)
|
|
423
450
|
|
|
424
|
-
if python scripts/master_healthcheck.py --
|
|
451
|
+
if python scripts/master_healthcheck.py --master-log "$LOG_FILE"; then
|
|
425
452
|
HEALTHCHECK_END=$(date +%s)
|
|
426
453
|
HEALTHCHECK_DURATION=$((HEALTHCHECK_END - HEALTHCHECK_START))
|
|
427
|
-
log_info "
|
|
454
|
+
log_info "OK: Master readiness confirmed (elapsed ${HEALTHCHECK_DURATION}s)"
|
|
428
455
|
else
|
|
429
456
|
HEALTHCHECK_END=$(date +%s)
|
|
430
457
|
HEALTHCHECK_DURATION=$((HEALTHCHECK_END - HEALTHCHECK_START))
|
|
431
|
-
log_error "⚠️ master
|
|
432
|
-
log_error "
|
|
433
|
-
log_error " -
|
|
434
|
-
log_error " -
|
|
435
|
-
log_error " -
|
|
436
|
-
log_error " -
|
|
437
|
-
|
|
438
|
-
#
|
|
458
|
+
log_error "⚠️ master Health check failed, time consuming ${HEALTHCHECK_DURATION}s"
|
|
459
|
+
log_error "It is recommended to check:"
|
|
460
|
+
log_error " - process status: ps aux | grep 'python.*master.py'"
|
|
461
|
+
log_error " - Startup log: tail -100 $LOG_DIR/start.log"
|
|
462
|
+
log_error " - Run log: tail -100 $LOG_FILE"
|
|
463
|
+
log_error " - Process PID: $MASTER_PID"
|
|
464
|
+
|
|
465
|
+
# Check if the process is still running
|
|
439
466
|
if kill -0 "$MASTER_PID" 2>/dev/null; then
|
|
440
|
-
log_info "master
|
|
441
|
-
log_info "⚠️
|
|
467
|
+
log_info "master The process is still running (PID=$MASTER_PID), Allow startup to continue"
|
|
468
|
+
log_info "⚠️ Please manually verify that the service is working properly"
|
|
442
469
|
else
|
|
443
|
-
log_error "
|
|
470
|
+
log_error "ERROR: Master process exited and failed to start"
|
|
444
471
|
exit 1
|
|
445
472
|
fi
|
|
446
473
|
fi
|
scripts/start_tmux_codex.sh
CHANGED
|
@@ -18,8 +18,13 @@ MODEL_WORKDIR="${MODEL_WORKDIR:-$ROOT_DIR}"
|
|
|
18
18
|
MODEL_SESSION_ROOT="${MODEL_SESSION_ROOT:-${CODEX_SESSION_ROOT:-$HOME/.codex/sessions}}"
|
|
19
19
|
MODEL_SESSION_GLOB="${MODEL_SESSION_GLOB:-rollout-*.jsonl}"
|
|
20
20
|
SESSION_POINTER_FILE="${SESSION_POINTER_FILE:-$LOG_ROOT/${MODEL_NAME:-codex}/${PROJECT_NAME:-project}/current_session.txt}"
|
|
21
|
+
SESSION_LOCK_FILE="${SESSION_LOCK_FILE:-${SESSION_POINTER_FILE%.txt}.lock.json}"
|
|
22
|
+
SESSION_CAPTURE_TIMEOUT="${SESSION_CAPTURE_TIMEOUT:-180}"
|
|
23
|
+
SESSION_CAPTURE_POLL_INTERVAL="${SESSION_CAPTURE_POLL_INTERVAL:-0.5}"
|
|
24
|
+
# Ensure optional CODEX session root is always defined even under `set -u`
|
|
25
|
+
: "${CODEX_SESSIONS_ROOT:=}"
|
|
21
26
|
|
|
22
|
-
#
|
|
27
|
+
# Avoid oh-my-zsh popping up update prompts in non-interactive environments
|
|
23
28
|
export DISABLE_UPDATE_PROMPT="${DISABLE_UPDATE_PROMPT:-true}"
|
|
24
29
|
|
|
25
30
|
expand_path() {
|
|
@@ -40,7 +45,7 @@ KILL_SESSION=0
|
|
|
40
45
|
|
|
41
46
|
usage() {
|
|
42
47
|
cat <<USAGE
|
|
43
|
-
|
|
48
|
+
usage:${0##*/} [--dry-run] [--force] [--restart] [--kill]
|
|
44
49
|
USAGE
|
|
45
50
|
}
|
|
46
51
|
|
|
@@ -51,13 +56,13 @@ while [[ $# -gt 0 ]]; do
|
|
|
51
56
|
--restart) RESTART=1; FORCE_START=1 ;;
|
|
52
57
|
--kill) KILL_SESSION=1 ;;
|
|
53
58
|
-h|--help) usage; exit 0 ;;
|
|
54
|
-
*) echo "
|
|
59
|
+
*) echo "unknown parameters: $1" >&2; usage; exit 1 ;;
|
|
55
60
|
esac
|
|
56
61
|
shift
|
|
57
62
|
done
|
|
58
63
|
|
|
59
64
|
if ! command -v tmux >/dev/null 2>&1; then
|
|
60
|
-
echo "tmux
|
|
65
|
+
echo "tmux Not installed" >&2
|
|
61
66
|
exit 1
|
|
62
67
|
fi
|
|
63
68
|
|
|
@@ -67,6 +72,7 @@ MODEL_SESSION_ROOT=$(expand_path "$MODEL_SESSION_ROOT")
|
|
|
67
72
|
SESSION_POINTER_FILE=$(expand_path "$SESSION_POINTER_FILE")
|
|
68
73
|
ensure_dir "$(dirname "$LOG_PATH")"
|
|
69
74
|
ensure_dir "$(dirname "$SESSION_POINTER_FILE")"
|
|
75
|
+
ensure_dir "$(dirname "$SESSION_LOCK_FILE")"
|
|
70
76
|
|
|
71
77
|
run_tmux() {
|
|
72
78
|
if (( DRY_RUN )); then
|
|
@@ -98,12 +104,12 @@ else
|
|
|
98
104
|
fi
|
|
99
105
|
fi
|
|
100
106
|
|
|
101
|
-
#
|
|
107
|
+
# Clean up before starting to avoid old logs exceeding the limit.
|
|
102
108
|
if ! env \
|
|
103
109
|
MODEL_LOG_MAX_BYTES="$MODEL_LOG_MAX_BYTES" \
|
|
104
110
|
MODEL_LOG_RETENTION_SECONDS="$MODEL_LOG_RETENTION_SECONDS" \
|
|
105
111
|
"$PYTHON_EXEC" "$LOG_WRITER" "$LOG_PATH" </dev/null; then
|
|
106
|
-
echo "
|
|
112
|
+
echo "Preprocessing log file failed" >&2
|
|
107
113
|
exit 1
|
|
108
114
|
fi
|
|
109
115
|
|
|
@@ -115,7 +121,7 @@ printf -v PIPE_CMD 'env MODEL_LOG_MAX_BYTES=%q MODEL_LOG_RETENTION_SECONDS=%q %q
|
|
|
115
121
|
"$LOG_PATH"
|
|
116
122
|
run_tmux pipe-pane -o -t "$SESSION_NAME" "$PIPE_CMD"
|
|
117
123
|
|
|
118
|
-
#
|
|
124
|
+
# Synchronize environment variables to the tmux server to avoid losing settings when reusing old sessions
|
|
119
125
|
run_tmux set-environment -t "$SESSION_NAME" DISABLE_UPDATE_PROMPT "${DISABLE_UPDATE_PROMPT:-true}"
|
|
120
126
|
if [[ -n "${CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING:-}" ]]; then
|
|
121
127
|
run_tmux set-environment -t "$SESSION_NAME" CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING "${CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING}"
|
|
@@ -138,10 +144,29 @@ fi
|
|
|
138
144
|
|
|
139
145
|
|
|
140
146
|
if (( DRY_RUN )); then
|
|
141
|
-
printf '[dry-run]
|
|
147
|
+
printf '[dry-run] Session log path: %s\n' "$SESSION_POINTER_FILE"
|
|
142
148
|
exit 0
|
|
143
149
|
fi
|
|
144
150
|
|
|
145
151
|
: > "$SESSION_POINTER_FILE"
|
|
152
|
+
rm -f "$SESSION_LOCK_FILE"
|
|
153
|
+
|
|
154
|
+
if (( ! DRY_RUN )); then
|
|
155
|
+
WATCH_ARGS=("$PYTHON_EXEC" "$ROOT_DIR/scripts/session_pointer_watch.py" "--pointer" "$SESSION_POINTER_FILE" "--lock" "$SESSION_LOCK_FILE" "--glob" "$MODEL_SESSION_GLOB" "--workdir" "$MODEL_WORKDIR" "--tmux-session" "$SESSION_NAME" "--project" "${PROJECT_NAME:-}")
|
|
156
|
+
if [[ -n "$MODEL_SESSION_ROOT" ]]; then
|
|
157
|
+
WATCH_ARGS+=("--session-root" "$MODEL_SESSION_ROOT")
|
|
158
|
+
fi
|
|
159
|
+
if [[ -n "$CODEX_SESSIONS_ROOT" ]]; then
|
|
160
|
+
WATCH_ARGS+=("--additional-root" "$CODEX_SESSIONS_ROOT")
|
|
161
|
+
fi
|
|
162
|
+
WATCH_ARGS+=("--additional-root" "$(dirname "$SESSION_POINTER_FILE")")
|
|
163
|
+
WATCH_ARGS+=("--additional-root" "$(dirname "$SESSION_POINTER_FILE")/sessions")
|
|
164
|
+
WATCH_ARGS+=("--timeout" "$SESSION_CAPTURE_TIMEOUT")
|
|
165
|
+
WATCH_ARGS+=("--poll" "$SESSION_CAPTURE_POLL_INTERVAL")
|
|
166
|
+
if ! "${WATCH_ARGS[@]}"; then
|
|
167
|
+
echo "Failed to capture Codex/Claude session log for tmux session '$SESSION_NAME'. Please check CLI startup output." >&2
|
|
168
|
+
exit 1
|
|
169
|
+
fi
|
|
170
|
+
fi
|
|
146
171
|
|
|
147
172
|
exit 0
|