vibego 0.2.30__py3-none-any.whl → 0.2.31__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.
Potentially problematic release.
This version of vibego might be problematic. Click here for more details.
- master.py +47 -3
- scripts/start.sh +137 -14
- {vibego-0.2.30.dist-info → vibego-0.2.31.dist-info}/METADATA +1 -1
- {vibego-0.2.30.dist-info → vibego-0.2.31.dist-info}/RECORD +8 -8
- vibego_cli/__init__.py +1 -1
- {vibego-0.2.30.dist-info → vibego-0.2.31.dist-info}/WHEEL +0 -0
- {vibego-0.2.30.dist-info → vibego-0.2.31.dist-info}/entry_points.txt +0 -0
- {vibego-0.2.30.dist-info → vibego-0.2.31.dist-info}/top_level.txt +0 -0
master.py
CHANGED
|
@@ -1700,9 +1700,14 @@ def _read_restart_signal() -> Optional[dict]:
|
|
|
1700
1700
|
|
|
1701
1701
|
|
|
1702
1702
|
async def _notify_restart_success(bot: Bot) -> None:
|
|
1703
|
-
"""在新 master 启动时读取 signal
|
|
1703
|
+
"""在新 master 启动时读取 signal 并通知触发者(改进版:支持超时检测和详细诊断)"""
|
|
1704
1704
|
restart_expected = os.environ.pop("MASTER_RESTART_EXPECTED", None)
|
|
1705
1705
|
payload = _read_restart_signal()
|
|
1706
|
+
|
|
1707
|
+
# 定义重启健康检查阈值(2 分钟)
|
|
1708
|
+
RESTART_HEALTHY_THRESHOLD = 120 # 秒
|
|
1709
|
+
RESTART_WARNING_THRESHOLD = 60 # 超过 1 分钟发出警告
|
|
1710
|
+
|
|
1706
1711
|
if not payload:
|
|
1707
1712
|
if restart_expected:
|
|
1708
1713
|
targets = _collect_admin_targets()
|
|
@@ -1710,6 +1715,18 @@ async def _notify_restart_success(bot: Bot) -> None:
|
|
|
1710
1715
|
"启动时未检测到重启信号文件,将向管理员发送兜底提醒", extra={"targets": targets}
|
|
1711
1716
|
)
|
|
1712
1717
|
if targets:
|
|
1718
|
+
# 检查启动日志是否有错误信息
|
|
1719
|
+
error_log_dir = ROOT_DIR / "logs"
|
|
1720
|
+
error_log_hint = ""
|
|
1721
|
+
try:
|
|
1722
|
+
error_logs = sorted(error_log_dir.glob("master_error_*.log"), key=lambda p: p.stat().st_mtime, reverse=True)
|
|
1723
|
+
if error_logs:
|
|
1724
|
+
latest_error_log = error_logs[0]
|
|
1725
|
+
if latest_error_log.stat().st_size > 0:
|
|
1726
|
+
error_log_hint = f"\n⚠️ 发现错误日志:{latest_error_log}"
|
|
1727
|
+
except Exception:
|
|
1728
|
+
pass
|
|
1729
|
+
|
|
1713
1730
|
text_lines = [
|
|
1714
1731
|
"⚠️ Master 已重新上线,但未找到重启触发者信息。",
|
|
1715
1732
|
"",
|
|
@@ -1717,12 +1734,16 @@ async def _notify_restart_success(bot: Bot) -> None:
|
|
|
1717
1734
|
"1. 重启信号文件写入失败",
|
|
1718
1735
|
"2. 信号文件已超时被清理(TTL=30分钟)",
|
|
1719
1736
|
"3. 文件系统权限问题",
|
|
1737
|
+
"4. start.sh 启动失败后被清理",
|
|
1720
1738
|
"",
|
|
1721
1739
|
"建议检查:",
|
|
1722
1740
|
f"- 启动日志: {ROOT_DIR}/logs/start.log",
|
|
1723
1741
|
f"- 运行日志: {ROOT_DIR}/vibe.log",
|
|
1724
1742
|
f"- 信号文件: {RESTART_SIGNAL_PATH}",
|
|
1725
1743
|
]
|
|
1744
|
+
if error_log_hint:
|
|
1745
|
+
text_lines.append(error_log_hint)
|
|
1746
|
+
|
|
1726
1747
|
text = "\n".join(text_lines)
|
|
1727
1748
|
for chat in targets:
|
|
1728
1749
|
try:
|
|
@@ -1746,6 +1767,9 @@ async def _notify_restart_success(bot: Bot) -> None:
|
|
|
1746
1767
|
user_id = payload.get("user_id")
|
|
1747
1768
|
timestamp = payload.get("timestamp")
|
|
1748
1769
|
timestamp_fmt: Optional[str] = None
|
|
1770
|
+
restart_duration: Optional[int] = None
|
|
1771
|
+
|
|
1772
|
+
# 计算重启耗时
|
|
1749
1773
|
if timestamp:
|
|
1750
1774
|
try:
|
|
1751
1775
|
ts = datetime.fromisoformat(timestamp)
|
|
@@ -1753,6 +1777,10 @@ async def _notify_restart_success(bot: Bot) -> None:
|
|
|
1753
1777
|
ts = ts.replace(tzinfo=LOCAL_TZ)
|
|
1754
1778
|
ts_local = ts.astimezone(LOCAL_TZ)
|
|
1755
1779
|
timestamp_fmt = ts_local.strftime("%Y-%m-%d %H:%M:%S %Z")
|
|
1780
|
+
|
|
1781
|
+
# 计算重启耗时(秒)
|
|
1782
|
+
now = datetime.now(LOCAL_TZ)
|
|
1783
|
+
restart_duration = int((now - ts_local).total_seconds())
|
|
1756
1784
|
except Exception as exc:
|
|
1757
1785
|
log.warning("解析重启时间失败: %s", exc)
|
|
1758
1786
|
|
|
@@ -1764,7 +1792,23 @@ async def _notify_restart_success(bot: Bot) -> None:
|
|
|
1764
1792
|
if timestamp_fmt:
|
|
1765
1793
|
details.append(f"请求时间:{timestamp_fmt}")
|
|
1766
1794
|
|
|
1767
|
-
|
|
1795
|
+
# 添加重启耗时信息和健康状态
|
|
1796
|
+
message_lines = []
|
|
1797
|
+
if restart_duration is not None:
|
|
1798
|
+
if restart_duration <= RESTART_WARNING_THRESHOLD:
|
|
1799
|
+
message_lines.append(f"master 已重新上线 ✅(耗时 {restart_duration}秒)")
|
|
1800
|
+
elif restart_duration <= RESTART_HEALTHY_THRESHOLD:
|
|
1801
|
+
message_lines.append(f"⚠️ master 已重新上线(耗时 {restart_duration}秒,略慢)")
|
|
1802
|
+
details.append("💡 建议:检查依赖安装是否触发了重新下载")
|
|
1803
|
+
else:
|
|
1804
|
+
message_lines.append(f"⚠️ master 已重新上线(耗时 {restart_duration}秒,异常缓慢)")
|
|
1805
|
+
details.append("⚠️ 重启耗时过长,建议检查:")
|
|
1806
|
+
details.append(" - 网络连接是否正常")
|
|
1807
|
+
details.append(" - 依赖安装是否卡住")
|
|
1808
|
+
details.append(f" - 启动日志: {ROOT_DIR}/logs/start.log")
|
|
1809
|
+
else:
|
|
1810
|
+
message_lines.append("master 已重新上线 ✅")
|
|
1811
|
+
|
|
1768
1812
|
if details:
|
|
1769
1813
|
message_lines.extend(details)
|
|
1770
1814
|
|
|
@@ -1776,7 +1820,7 @@ async def _notify_restart_success(bot: Bot) -> None:
|
|
|
1776
1820
|
log.error("发送重启成功通知失败: %s", exc, extra={"chat": chat_id})
|
|
1777
1821
|
else:
|
|
1778
1822
|
# 重启成功后不再附带项目列表,避免高频重启时产生额外噪音
|
|
1779
|
-
log.info("重启成功通知已发送", extra={"chat": chat_id})
|
|
1823
|
+
log.info("重启成功通知已发送", extra={"chat": chat_id, "duration": restart_duration})
|
|
1780
1824
|
finally:
|
|
1781
1825
|
_safe_remove(RESTART_SIGNAL_PATH)
|
|
1782
1826
|
|
scripts/start.sh
CHANGED
|
@@ -186,21 +186,105 @@ check_deps_installed() {
|
|
|
186
186
|
return 0
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
189
|
+
# 清理旧 master 进程的健壮函数(改进版:支持 PID 文件 + pgrep 双保险)
|
|
190
|
+
cleanup_old_master() {
|
|
191
|
+
local max_wait=10 # 最多等待10秒优雅退出
|
|
192
|
+
local waited=0
|
|
193
|
+
local old_pids=""
|
|
194
|
+
local master_pid_file="${MASTER_CONFIG_ROOT:-$HOME/.config/vibego}/state/master.pid"
|
|
195
|
+
|
|
196
|
+
# 方案1:优先从 PID 文件读取
|
|
197
|
+
if [[ -f "$master_pid_file" ]]; then
|
|
198
|
+
local pid_from_file
|
|
199
|
+
pid_from_file=$(cat "$master_pid_file" 2>/dev/null || true)
|
|
200
|
+
if [[ "$pid_from_file" =~ ^[0-9]+$ ]]; then
|
|
201
|
+
if kill -0 "$pid_from_file" 2>/dev/null; then
|
|
202
|
+
old_pids="$pid_from_file"
|
|
203
|
+
log_info "从 PID 文件检测到旧 master 实例(PID: $old_pids)"
|
|
204
|
+
else
|
|
205
|
+
log_info "PID 文件存在但进程已不在,清理过期 PID 文件"
|
|
206
|
+
rm -f "$master_pid_file"
|
|
207
|
+
fi
|
|
208
|
+
fi
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
# 方案2:使用 pgrep 查找(支持多种运行方式)
|
|
212
|
+
if [[ -z "$old_pids" ]]; then
|
|
213
|
+
# 匹配模式:支持源码运行和 pipx 安装的方式
|
|
214
|
+
# - python.*master.py(源码运行)
|
|
215
|
+
# - Python.*master.py(macOS 上的 Python.app)
|
|
216
|
+
# - bot.py(pipx 安装的 master 别名)
|
|
217
|
+
local pgrep_pids
|
|
218
|
+
pgrep_pids=$(pgrep -f "master\.py$" 2>/dev/null || true)
|
|
219
|
+
if [[ -n "$pgrep_pids" ]]; then
|
|
220
|
+
old_pids="$pgrep_pids"
|
|
221
|
+
log_info "通过 pgrep 检测到旧 master 实例(PID: $old_pids)"
|
|
222
|
+
fi
|
|
197
223
|
fi
|
|
198
|
-
|
|
199
|
-
|
|
224
|
+
|
|
225
|
+
# 如果两种方式都没找到,说明没有旧进程
|
|
226
|
+
if [[ -z "$old_pids" ]]; then
|
|
227
|
+
log_info "未检测到旧 master 实例"
|
|
228
|
+
return 0
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
# 开始清理旧进程
|
|
232
|
+
log_info "正在优雅终止旧 master 实例(PID: $old_pids)..."
|
|
233
|
+
|
|
234
|
+
# 发送 SIGTERM 信号优雅终止
|
|
235
|
+
for pid in $old_pids; do
|
|
236
|
+
kill -15 "$pid" 2>/dev/null || true
|
|
237
|
+
done
|
|
238
|
+
|
|
239
|
+
# 循环等待进程退出
|
|
240
|
+
while (( waited < max_wait )); do
|
|
241
|
+
sleep 1
|
|
242
|
+
((waited++))
|
|
243
|
+
|
|
244
|
+
# 检查所有 PID 是否都已退出
|
|
245
|
+
local all_exited=1
|
|
246
|
+
for pid in $old_pids; do
|
|
247
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
248
|
+
all_exited=0
|
|
249
|
+
break
|
|
250
|
+
fi
|
|
251
|
+
done
|
|
252
|
+
|
|
253
|
+
if (( all_exited )); then
|
|
254
|
+
log_info "✅ 旧 master 已优雅退出(耗时 ${waited}秒)"
|
|
255
|
+
rm -f "$master_pid_file"
|
|
256
|
+
return 0
|
|
257
|
+
fi
|
|
258
|
+
done
|
|
259
|
+
|
|
260
|
+
# 优雅终止超时,执行强制结束
|
|
261
|
+
log_info "优雅终止超时(${max_wait}秒),执行强制结束..."
|
|
262
|
+
for pid in $old_pids; do
|
|
263
|
+
kill -9 "$pid" 2>/dev/null || true
|
|
264
|
+
done
|
|
265
|
+
sleep 2
|
|
266
|
+
|
|
267
|
+
# 最后检查
|
|
268
|
+
local remaining_pids=""
|
|
269
|
+
for pid in $old_pids; do
|
|
270
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
271
|
+
remaining_pids="$remaining_pids $pid"
|
|
272
|
+
fi
|
|
273
|
+
done
|
|
274
|
+
|
|
275
|
+
if [[ -n "$remaining_pids" ]]; then
|
|
276
|
+
log_error "❌ 无法清理旧 master 进程(残留 PID:$remaining_pids)"
|
|
277
|
+
log_error "请手动执行: kill -9$remaining_pids"
|
|
200
278
|
exit 1
|
|
201
279
|
fi
|
|
202
|
-
|
|
203
|
-
|
|
280
|
+
|
|
281
|
+
log_info "✅ 旧 master 实例已强制清理"
|
|
282
|
+
rm -f "$master_pid_file"
|
|
283
|
+
return 0
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
# 调用清理函数
|
|
287
|
+
cleanup_old_master
|
|
204
288
|
|
|
205
289
|
# 智能依赖管理:仅在必要时安装
|
|
206
290
|
REQUIREMENTS_FILE="${VIBEGO_REQUIREMENTS_PATH:-$ROOT_DIR/scripts/requirements.txt}"
|
|
@@ -256,8 +340,34 @@ if [[ -n "${MASTER_RESTART_EXPECTED:-}" ]]; then
|
|
|
256
340
|
fi
|
|
257
341
|
|
|
258
342
|
log_info "准备启动 master 进程..."
|
|
343
|
+
|
|
344
|
+
# 清理旧的错误日志(保留最近 10 次)
|
|
345
|
+
cleanup_old_error_logs() {
|
|
346
|
+
local error_log_pattern="$LOG_DIR/master_error_*.log"
|
|
347
|
+
local error_logs
|
|
348
|
+
error_logs=$(ls -t $error_log_pattern 2>/dev/null || true)
|
|
349
|
+
if [[ -n "$error_logs" ]]; then
|
|
350
|
+
local count=0
|
|
351
|
+
while IFS= read -r logfile; do
|
|
352
|
+
((count++))
|
|
353
|
+
if (( count > 10 )); then
|
|
354
|
+
rm -f "$logfile"
|
|
355
|
+
log_info "已清理旧错误日志: $logfile"
|
|
356
|
+
fi
|
|
357
|
+
done <<< "$error_logs"
|
|
358
|
+
fi
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
cleanup_old_error_logs
|
|
362
|
+
|
|
363
|
+
# 创建带时间戳的错误日志文件
|
|
364
|
+
MASTER_ERROR_LOG="$LOG_DIR/master_error_$(date +%Y%m%d_%H%M%S).log"
|
|
365
|
+
MASTER_STDOUT_LOG="$LOG_DIR/master_stdout.log"
|
|
366
|
+
|
|
259
367
|
# 显式传递环境变量给 nohup 进程,确保重启信号文件路径正确
|
|
260
|
-
|
|
368
|
+
# 使用虚拟环境的 Python 解释器,避免版本不匹配导致依赖加载失败
|
|
369
|
+
# 重要:将 stderr 保存到日志文件,方便排查启动失败问题
|
|
370
|
+
MASTER_RESTART_SIGNAL_PATH="$MASTER_RESTART_SIGNAL_PATH" nohup "$ROOT_DIR/.venv/bin/python" master.py > "$MASTER_STDOUT_LOG" 2> "$MASTER_ERROR_LOG" &
|
|
261
371
|
MASTER_PID=$!
|
|
262
372
|
|
|
263
373
|
# 健壮性检查:确保进程成功启动
|
|
@@ -274,7 +384,20 @@ if ! kill -0 "$MASTER_PID" 2>/dev/null; then
|
|
|
274
384
|
log_error "请检查:"
|
|
275
385
|
log_error " - master.py 是否有语法错误: python master.py"
|
|
276
386
|
log_error " - 依赖是否完整: pip list | grep aiogram"
|
|
277
|
-
log_error " -
|
|
387
|
+
log_error " - 错误日志: $MASTER_ERROR_LOG"
|
|
388
|
+
|
|
389
|
+
# 输出错误日志的最后 20 行,帮助快速定位问题
|
|
390
|
+
if [[ -s "$MASTER_ERROR_LOG" ]]; then
|
|
391
|
+
log_error ""
|
|
392
|
+
log_error "=== 错误日志最后 20 行 ==="
|
|
393
|
+
tail -20 "$MASTER_ERROR_LOG" | while IFS= read -r line; do
|
|
394
|
+
log_error " $line"
|
|
395
|
+
done
|
|
396
|
+
log_error "=========================="
|
|
397
|
+
else
|
|
398
|
+
log_error "错误日志文件为空,可能是环境变量或路径问题"
|
|
399
|
+
fi
|
|
400
|
+
|
|
278
401
|
exit 1
|
|
279
402
|
fi
|
|
280
403
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
bot.py,sha256=uJcmMJmj5SlpwUXSgtnIEeNiUGkBg-lG5g6kM9BobP4,272046
|
|
2
2
|
logging_setup.py,sha256=gvxHi8mUwK3IhXJrsGNTDo-DR6ngkyav1X-tvlBF_IE,4613
|
|
3
|
-
master.py,sha256=
|
|
3
|
+
master.py,sha256=2GxQh0rL7A_cEaH0RzO8DSwtau9T2cSJzOkhZ8ezHfs,111498
|
|
4
4
|
project_repository.py,sha256=UcthtSGOJK0cTE5bQCneo3xkomRG-kyc1N1QVqxeHIs,17577
|
|
5
5
|
scripts/__init__.py,sha256=LVrXUkvWKoc6Sb47X5G0gbIxu5aJ2ARW-qJ14vwi5vM,65
|
|
6
6
|
scripts/bump_version.sh,sha256=a4uB8V8Y5LPsoqTCdzQKsEE8HhwpBmqRaQInG52LDig,4089
|
|
@@ -9,7 +9,7 @@ scripts/master_healthcheck.py,sha256=-X0VVsZ0AXaOb7izxTO_oyu23g_1jsirNdGIcP8nrSI
|
|
|
9
9
|
scripts/publish.sh,sha256=ehLfMedcXuGKJ87jpZy3kuiFszG9Cpavp3zXPfR4h-g,3511
|
|
10
10
|
scripts/requirements.txt,sha256=QSt30DSSSHtfucTFPpc7twk9kLS5rVLNTcvDiagxrZg,62
|
|
11
11
|
scripts/run_bot.sh,sha256=rN4K1nz041XBaUJmnBBKHS2cHmQf11vPNX8wf1hbVR4,4596
|
|
12
|
-
scripts/start.sh,sha256=
|
|
12
|
+
scripts/start.sh,sha256=w1Q35NB-9FRZAez5I5veqgYIJ8XnVukGt8TTE_ad248,13608
|
|
13
13
|
scripts/start_tmux_codex.sh,sha256=xyLv29p924q-ysxvZYAP3T6VrqLPBPMBWo9QP7cuL50,4438
|
|
14
14
|
scripts/stop_all.sh,sha256=FOz07gi2CI9sMHxBb8XkqHtxRYs3jt1RYgGrEi-htVg,4086
|
|
15
15
|
scripts/stop_bot.sh,sha256=ot6Sm0IYoXuRNslUVEflQmJKu5Agm-5xIUXXudJWyTM,5588
|
|
@@ -426,14 +426,14 @@ tasks/constants.py,sha256=tS1kZxBIUm3JJUMHm25XI-KHNUZl5NhbbuzjzL_rF-c,299
|
|
|
426
426
|
tasks/fsm.py,sha256=rKXXLEieQQU4r2z_CZUvn1_70FXiZXBBugF40gpe_tQ,1476
|
|
427
427
|
tasks/models.py,sha256=N_qqRBo9xMSV0vbn4k6bLBXT8C_dp_oTFUxvdx16ZQM,2459
|
|
428
428
|
tasks/service.py,sha256=w_S_aWiVqRXzXEpimLDsuCCCX2lB5uDkff9aKThBw9c,41916
|
|
429
|
-
vibego_cli/__init__.py,sha256=
|
|
429
|
+
vibego_cli/__init__.py,sha256=QzEfWJBe5TQPIDo6c_5ayKnmn-2c3RHN0kZoHHPwiCQ,311
|
|
430
430
|
vibego_cli/__main__.py,sha256=qqTrYmRRLe4361fMzbI3-CqpZ7AhTofIHmfp4ykrrBY,158
|
|
431
431
|
vibego_cli/config.py,sha256=VxkPJMq01tA3h3cOkH-z_tiP7pMgfSGGicRvUnCWkhI,3054
|
|
432
432
|
vibego_cli/deps.py,sha256=1nRXI7Dd-S1hYE8DligzK5fIluQWETRUj4_OKL0DikQ,1419
|
|
433
433
|
vibego_cli/main.py,sha256=X__NXwZnIDIFbdKSTbNyZgZHKcPlN0DQz9sqTI1aQ9E,12158
|
|
434
434
|
vibego_cli/data/worker_requirements.txt,sha256=QSt30DSSSHtfucTFPpc7twk9kLS5rVLNTcvDiagxrZg,62
|
|
435
|
-
vibego-0.2.
|
|
436
|
-
vibego-0.2.
|
|
437
|
-
vibego-0.2.
|
|
438
|
-
vibego-0.2.
|
|
439
|
-
vibego-0.2.
|
|
435
|
+
vibego-0.2.31.dist-info/METADATA,sha256=hDq9uQe4YmG7kgon0jsy4lnurmqMD6PIZdXW41FSh4Y,10475
|
|
436
|
+
vibego-0.2.31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
437
|
+
vibego-0.2.31.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
|
|
438
|
+
vibego-0.2.31.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
|
|
439
|
+
vibego-0.2.31.dist-info/RECORD,,
|
vibego_cli/__init__.py
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|