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 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
- message_lines = ["master 已重新上线 ✅"]
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
- if pgrep -f "python.*master.py" >/dev/null 2>&1; then
190
- log_info "检测到历史 master 实例,正在终止..."
191
- pkill -f "python.*master.py" || true
192
- sleep 1
193
- if pgrep -f "python.*master.py" >/dev/null 2>&1; then
194
- log_info "残留 master 进程仍在,执行强制结束"
195
- pkill -9 -f "python.*master.py" || true
196
- sleep 1
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
- if pgrep -f "python.*master.py" >/dev/null 2>&1; then
199
- log_error "仍存在 master 进程,请手动检查后再启动"
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
- log_info "历史 master 实例已清理"
203
- fi
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
- MASTER_RESTART_SIGNAL_PATH="$MASTER_RESTART_SIGNAL_PATH" nohup python master.py >> /dev/null 2>&1 &
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 " - 最近的错误日志: tail -50 $ROOT_DIR/vibe.log"
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
  Metadata-Version: 2.4
2
2
  Name: vibego
3
- Version: 0.2.30
3
+ Version: 0.2.31
4
4
  Summary: vibego CLI:用于初始化与管理 Telegram Master Bot 的工具
5
5
  Author: Hypha
6
6
  License-Expression: LicenseRef-Proprietary
@@ -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=CHCsjOYAhr_gnNb1Yl7NaOsFQTbRL9CdhLLMIlrx3G0,109337
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=WxByzBXALfjcjhGPCrLHLkaD8hNYZ2WDQAZ0tpoQSeA,9996
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=cGLGVLdOGT2rC2HZZBaLnLPkoa2ofpzKfDuuMEhgNeY,311
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.30.dist-info/METADATA,sha256=Ksc_G1L5Gn8gIEDXPTVHhQYXereR6HczAjdfEcsAdRc,10475
436
- vibego-0.2.30.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
437
- vibego-0.2.30.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
438
- vibego-0.2.30.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
439
- vibego-0.2.30.dist-info/RECORD,,
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
@@ -7,6 +7,6 @@ from __future__ import annotations
7
7
 
8
8
  __all__ = ["main", "__version__"]
9
9
 
10
- __version__ = "0.2.30"
10
+ __version__ = "0.2.31"
11
11
 
12
12
  from .main import main # noqa: E402