vibego 0.2.42__py3-none-any.whl → 0.2.44__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
@@ -85,6 +85,11 @@ def _get_restart_signal_path() -> Path:
85
85
 
86
86
 
87
87
  RESTART_SIGNAL_PATH = _get_restart_signal_path()
88
+ LEGACY_RESTART_SIGNAL_PATHS: Tuple[Path, ...] = tuple(
89
+ path
90
+ for path in (ROOT_DIR / "state/restart_signal.json",)
91
+ if path != RESTART_SIGNAL_PATH
92
+ )
88
93
  RESTART_SIGNAL_TTL = int(os.environ.get("MASTER_RESTART_SIGNAL_TTL", "1800")) # 默认 30 分钟
89
94
  LOCAL_TZ = ZoneInfo(os.environ.get("MASTER_TIMEZONE", "Asia/Shanghai"))
90
95
  JUMP_BUTTON_TEXT_WIDTH = 40
@@ -1699,48 +1704,57 @@ def _write_restart_signal(message: Message, *, override_user: Optional[User] = N
1699
1704
  )
1700
1705
 
1701
1706
 
1702
- def _read_restart_signal() -> Optional[dict]:
1703
- """读取并验证重启 signal,超时会自动清理"""
1704
- if not RESTART_SIGNAL_PATH.exists():
1705
- return None
1706
- try:
1707
- raw = json.loads(RESTART_SIGNAL_PATH.read_text(encoding="utf-8"))
1708
- if not isinstance(raw, dict):
1709
- raise ValueError("signal payload 必须是对象")
1710
- except Exception as exc:
1711
- log.error("读取重启信号失败: %s", exc)
1712
- _safe_remove(RESTART_SIGNAL_PATH)
1713
- return None
1714
-
1715
- timestamp_raw = raw.get("timestamp")
1716
- if timestamp_raw:
1707
+ def _read_restart_signal() -> Tuple[Optional[dict], Optional[Path]]:
1708
+ """读取并验证重启 signal,兼容历史路径并处理异常/超时情况"""
1709
+ candidates: Tuple[Path, ...] = (RESTART_SIGNAL_PATH, *LEGACY_RESTART_SIGNAL_PATHS)
1710
+ for path in candidates:
1711
+ if not path.exists():
1712
+ continue
1717
1713
  try:
1718
- ts = datetime.fromisoformat(timestamp_raw)
1719
- if ts.tzinfo is None:
1720
- ts = ts.replace(tzinfo=LOCAL_TZ)
1721
- ts_utc = ts.astimezone(timezone.utc)
1722
- age_seconds = (datetime.now(timezone.utc) - ts_utc).total_seconds()
1723
- if age_seconds > RESTART_SIGNAL_TTL:
1724
- log.info(
1725
- "重启信号超时,忽略",
1726
- extra={
1727
- "path": str(RESTART_SIGNAL_PATH),
1728
- "age_seconds": age_seconds,
1729
- "ttl": RESTART_SIGNAL_TTL,
1730
- },
1731
- )
1732
- _safe_remove(RESTART_SIGNAL_PATH)
1733
- return None
1714
+ raw = json.loads(path.read_text(encoding="utf-8"))
1715
+ if not isinstance(raw, dict):
1716
+ raise ValueError("signal payload 必须是对象")
1734
1717
  except Exception as exc:
1735
- log.warning("解析重启信号时间戳失败: %s", exc)
1718
+ log.error("读取重启信号失败: %s", exc, extra={"path": str(path)})
1719
+ _safe_remove(path)
1720
+ continue
1736
1721
 
1737
- return raw
1722
+ timestamp_raw = raw.get("timestamp")
1723
+ if timestamp_raw:
1724
+ try:
1725
+ ts = datetime.fromisoformat(timestamp_raw)
1726
+ if ts.tzinfo is None:
1727
+ ts = ts.replace(tzinfo=LOCAL_TZ)
1728
+ ts_utc = ts.astimezone(timezone.utc)
1729
+ age_seconds = (datetime.now(timezone.utc) - ts_utc).total_seconds()
1730
+ if age_seconds > RESTART_SIGNAL_TTL:
1731
+ log.info(
1732
+ "重启信号超时,忽略",
1733
+ extra={
1734
+ "path": str(path),
1735
+ "age_seconds": age_seconds,
1736
+ "ttl": RESTART_SIGNAL_TTL,
1737
+ },
1738
+ )
1739
+ _safe_remove(path)
1740
+ continue
1741
+ except Exception as exc:
1742
+ log.warning("解析重启信号时间戳失败: %s", exc, extra={"path": str(path)})
1743
+
1744
+ if path != RESTART_SIGNAL_PATH:
1745
+ log.info(
1746
+ "从兼容路径读取重启信号",
1747
+ extra={"path": str(path), "primary": str(RESTART_SIGNAL_PATH)},
1748
+ )
1749
+ return raw, path
1750
+
1751
+ return None, None
1738
1752
 
1739
1753
 
1740
1754
  async def _notify_restart_success(bot: Bot) -> None:
1741
1755
  """在新 master 启动时读取 signal 并通知触发者(改进版:支持超时检测和详细诊断)"""
1742
1756
  restart_expected = os.environ.pop("MASTER_RESTART_EXPECTED", None)
1743
- payload = _read_restart_signal()
1757
+ payload, signal_path = _read_restart_signal()
1744
1758
 
1745
1759
  # 定义重启健康检查阈值(2 分钟)
1746
1760
  RESTART_HEALTHY_THRESHOLD = 120 # 秒
@@ -1754,7 +1768,7 @@ async def _notify_restart_success(bot: Bot) -> None:
1754
1768
  )
1755
1769
  if targets:
1756
1770
  # 检查启动日志是否有错误信息
1757
- error_log_dir = ROOT_DIR / "logs"
1771
+ error_log_dir = LOG_ROOT_PATH
1758
1772
  error_log_hint = ""
1759
1773
  try:
1760
1774
  error_logs = sorted(error_log_dir.glob("master_error_*.log"), key=lambda p: p.stat().st_mtime, reverse=True)
@@ -1775,8 +1789,8 @@ async def _notify_restart_success(bot: Bot) -> None:
1775
1789
  "4. start.sh 启动失败后被清理",
1776
1790
  "",
1777
1791
  "建议检查:",
1778
- f"- 启动日志: {ROOT_DIR}/logs/start.log",
1779
- f"- 运行日志: {ROOT_DIR}/vibe.log",
1792
+ f"- 启动日志: {LOG_ROOT_PATH / 'start.log'}",
1793
+ f"- 运行日志: {LOG_ROOT_PATH / 'vibe.log'}",
1780
1794
  f"- 信号文件: {RESTART_SIGNAL_PATH}",
1781
1795
  ]
1782
1796
  if error_log_hint:
@@ -1798,7 +1812,11 @@ async def _notify_restart_success(bot: Bot) -> None:
1798
1812
  chat_id = int(chat_id_raw)
1799
1813
  except (TypeError, ValueError):
1800
1814
  log.error("重启信号 chat_id 非法: %s", chat_id_raw)
1801
- _safe_remove(RESTART_SIGNAL_PATH)
1815
+ targets = (signal_path, RESTART_SIGNAL_PATH, *LEGACY_RESTART_SIGNAL_PATHS)
1816
+ for candidate in targets:
1817
+ if candidate is None:
1818
+ continue
1819
+ _safe_remove(candidate)
1802
1820
  return
1803
1821
 
1804
1822
  username = payload.get("username")
@@ -1843,7 +1861,7 @@ async def _notify_restart_success(bot: Bot) -> None:
1843
1861
  details.append("⚠️ 重启耗时过长,建议检查:")
1844
1862
  details.append(" - 网络连接是否正常")
1845
1863
  details.append(" - 依赖安装是否卡住")
1846
- details.append(f" - 启动日志: {ROOT_DIR}/logs/start.log")
1864
+ details.append(f" - 启动日志: {LOG_ROOT_PATH / 'start.log'}")
1847
1865
  else:
1848
1866
  message_lines.append("master 已重新上线 ✅")
1849
1867
 
@@ -1860,7 +1878,11 @@ async def _notify_restart_success(bot: Bot) -> None:
1860
1878
  # 重启成功后不再附带项目列表,避免高频重启时产生额外噪音
1861
1879
  log.info("重启成功通知已发送", extra={"chat": chat_id, "duration": restart_duration})
1862
1880
  finally:
1863
- _safe_remove(RESTART_SIGNAL_PATH)
1881
+ candidates = (signal_path, RESTART_SIGNAL_PATH, *LEGACY_RESTART_SIGNAL_PATHS)
1882
+ for candidate in candidates:
1883
+ if candidate is None:
1884
+ continue
1885
+ _safe_remove(candidate)
1864
1886
 
1865
1887
 
1866
1888
  async def _ensure_manager() -> MasterManager:
@@ -25,11 +25,15 @@ from urllib.error import URLError, HTTPError
25
25
  from urllib.request import Request, urlopen
26
26
 
27
27
  # 导入 master 中的配置与工具,复用项目解析逻辑
28
+ ROOT_DIR = Path(__file__).resolve().parent.parent
29
+ ROOT_DIR_STR = str(ROOT_DIR)
30
+ if ROOT_DIR_STR not in sys.path:
31
+ # 确保可以从仓库根目录导入 master 模块
32
+ sys.path.insert(0, ROOT_DIR_STR)
33
+
28
34
  import master # type: ignore
29
35
  from project_repository import ProjectRepository
30
-
31
- ROOT_DIR = Path(__file__).resolve().parent.parent
32
- DEFAULT_MASTER_LOG = ROOT_DIR / "vibe.log"
36
+ DEFAULT_MASTER_LOG = master.LOG_ROOT_PATH / "vibe.log"
33
37
  DEFAULT_TIMEOUT_MASTER = 60.0
34
38
  DEFAULT_TIMEOUT_PROBE = 15.0
35
39
  PROBE_TEXT = "hello"
scripts/start.sh CHANGED
@@ -2,16 +2,21 @@
2
2
  set -eo pipefail
3
3
 
4
4
  ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
- LOCK_FILE="$ROOT_DIR/state/master_restart.lock"
6
- LOG_DIR="$ROOT_DIR/logs"
5
+ MASTER_CONFIG_ROOT="${MASTER_CONFIG_ROOT:-$HOME/.config/vibego}"
6
+ STATE_DIR="$MASTER_CONFIG_ROOT/state"
7
+ LOG_DIR="$MASTER_CONFIG_ROOT/logs"
8
+ LOCK_FILE="$STATE_DIR/master_restart.lock"
7
9
  START_LOG="$LOG_DIR/start.log"
8
- CODEX_STAMP_FILE="$ROOT_DIR/state/npm_codex_install.stamp"
10
+ CODEX_STAMP_FILE="$STATE_DIR/npm_codex_install.stamp"
9
11
  CODEX_INSTALL_TTL="${CODEX_INSTALL_TTL:-86400}"
10
12
 
11
13
  # 统一重启信号文件路径:使用配置目录而非代码目录
12
14
  # 这样 pipx 安装的 master 和源码运行的 master 可以共享同一个信号文件
13
- MASTER_CONFIG_ROOT="${MASTER_CONFIG_ROOT:-$HOME/.config/vibego}"
14
- export MASTER_RESTART_SIGNAL_PATH="$MASTER_CONFIG_ROOT/state/restart_signal.json"
15
+ export MASTER_RESTART_SIGNAL_PATH="$STATE_DIR/restart_signal.json"
16
+ export LOG_ROOT="${LOG_ROOT:-$LOG_DIR}"
17
+ if [[ -z "${LOG_FILE:-}" ]]; then
18
+ export LOG_FILE="$LOG_DIR/vibe.log"
19
+ fi
15
20
 
16
21
  log_line() {
17
22
  local ts
@@ -109,7 +114,8 @@ ensure_codex_installed() {
109
114
  ensure_codex_installed
110
115
 
111
116
  select_python_binary() {
112
- # 选择满足 CPython <=3.13 的解释器,避免 PyO3 依赖构建失败
117
+ # 选择满足 CPython <=3.12 的解释器,默认禁用 3.13(pydantic-core pipx 基础环境下无兼容轮子)
118
+ local allow_py313="${VIBEGO_ALLOW_PY313:-}"
113
119
  local candidates=()
114
120
  local chosen=""
115
121
  local name
@@ -138,6 +144,14 @@ select_python_binary() {
138
144
  log_line "跳过 ${name} (版本 ${version_raw}):非 CPython 3.x" >&2
139
145
  continue
140
146
  fi
147
+ local explicit_override=0
148
+ if [[ -n "${VIBEGO_PYTHON:-}" && "$name" == "$VIBEGO_PYTHON" ]]; then
149
+ explicit_override=1
150
+ fi
151
+ if [[ "$minor" =~ ^[0-9]+$ ]] && (( minor == 13 )) && [[ "$allow_py313" != "1" ]] && (( explicit_override == 0 )); then
152
+ log_line "跳过 ${name} (版本 ${version_raw}):默认禁用 Python 3.13,可设置 VIBEGO_ALLOW_PY313=1 覆盖" >&2
153
+ continue
154
+ fi
141
155
  if [[ "$minor" =~ ^[0-9]+$ ]] && (( minor > 13 )); then
142
156
  log_line "跳过 ${name} (版本 ${version_raw}):高于 3.13" >&2
143
157
  continue
@@ -191,7 +205,7 @@ cleanup_old_master() {
191
205
  local max_wait=10 # 最多等待10秒优雅退出
192
206
  local waited=0
193
207
  local old_pids=""
194
- local master_pid_file="${MASTER_CONFIG_ROOT:-$HOME/.config/vibego}/state/master.pid"
208
+ local master_pid_file="$STATE_DIR/master.pid"
195
209
 
196
210
  # 方案1:优先从 PID 文件读取
197
211
  if [[ -f "$master_pid_file" ]]; then
@@ -401,7 +415,7 @@ if ! kill -0 "$MASTER_PID" 2>/dev/null; then
401
415
  exit 1
402
416
  fi
403
417
 
404
- log_info "master 已后台启动,PID=$MASTER_PID,日志写入 vibe.log"
418
+ log_info "master 已后台启动,PID=$MASTER_PID,日志写入 ${LOG_FILE}"
405
419
 
406
420
  # 健康检查:等待 master 上线并验证关键 worker
407
421
  log_info "开始执行健康检查..."
@@ -417,8 +431,8 @@ else
417
431
  log_error "⚠️ master 健康检查失败,耗时 ${HEALTHCHECK_DURATION}s"
418
432
  log_error "建议检查:"
419
433
  log_error " - 进程状态: ps aux | grep 'python.*master.py'"
420
- log_error " - 启动日志: tail -100 $ROOT_DIR/logs/start.log"
421
- log_error " - 运行日志: tail -100 $ROOT_DIR/vibe.log"
434
+ log_error " - 启动日志: tail -100 $LOG_DIR/start.log"
435
+ log_error " - 运行日志: tail -100 $LOG_FILE"
422
436
  log_error " - 进程 PID: $MASTER_PID"
423
437
 
424
438
  # 检查进程是否仍在运行
scripts/stop_all.sh CHANGED
@@ -2,9 +2,13 @@
2
2
  set -euo pipefail
3
3
 
4
4
  ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
- STATE_DIR="$ROOT_DIR/state"
5
+ MASTER_CONFIG_ROOT="${MASTER_CONFIG_ROOT:-$HOME/.config/vibego}"
6
+ STATE_DIR="$MASTER_CONFIG_ROOT/state"
6
7
  LOCK_FILE="$STATE_DIR/master_restart.lock"
7
8
  RESTART_SIGNAL="$STATE_DIR/restart_signal.json"
9
+ LEGACY_STATE_DIR="$ROOT_DIR/state"
10
+ LEGACY_LOCK_FILE="$LEGACY_STATE_DIR/master_restart.lock"
11
+ LEGACY_RESTART_SIGNAL="$LEGACY_STATE_DIR/restart_signal.json"
8
12
  DEFAULT_TMUX_PREFIX="${TMUX_SESSION_PREFIX:-vibe}"
9
13
  STOP_BOT_SCRIPT="$ROOT_DIR/scripts/stop_bot.sh"
10
14
 
@@ -138,7 +142,7 @@ stop_tmux_sessions() {
138
142
 
139
143
  cleanup_state_files() {
140
144
  local removed=0
141
- for file in "$LOCK_FILE" "$RESTART_SIGNAL"; do
145
+ for file in "$LOCK_FILE" "$RESTART_SIGNAL" "$LEGACY_LOCK_FILE" "$LEGACY_RESTART_SIGNAL"; do
142
146
  if [[ -e "$file" ]]; then
143
147
  if (( DRY_RUN )); then
144
148
  log_info "[dry-run] rm -f $file"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibego
3
- Version: 0.2.42
3
+ Version: 0.2.44
4
4
  Summary: vibego CLI:用于初始化与管理 Telegram Master Bot 的工具
5
5
  Author: Hypha
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,17 +1,17 @@
1
1
  bot.py,sha256=TthqY_Rvndzd3pgLH6lvPcsDXQbwkuMfB4W9qt_Li8I,273832
2
2
  logging_setup.py,sha256=gvxHi8mUwK3IhXJrsGNTDo-DR6ngkyav1X-tvlBF_IE,4613
3
- master.py,sha256=ZW4A3Gh0MUKFnfZX-VJ7OCNnBzlEcOWkPKYH92OfyKA,112967
3
+ master.py,sha256=uFRhROoRvd93CTRzS6DdlvxMYok7q1jRWZwe8QrTqIY,113997
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
7
7
  scripts/log_writer.py,sha256=8euoMlRo7cbtHApbcEoJnwzLABxti-ovJWFLRN1oDQw,3843
8
- scripts/master_healthcheck.py,sha256=-X0VVsZ0AXaOb7izxTO_oyu23g_1jsirNdGIcP8nrSI,8321
8
+ scripts/master_healthcheck.py,sha256=kvE6owdqyLmfQ-UYY_jglg86xFzOc87sINxBe5_qWzk,8489
9
9
  scripts/publish.sh,sha256=ehLfMedcXuGKJ87jpZy3kuiFszG9Cpavp3zXPfR4h-g,3511
10
10
  scripts/requirements.txt,sha256=ukJbFLJyzqnQYMz6j07O-IOrG87IwXg0oikmn1nfJ9M,91
11
11
  scripts/run_bot.sh,sha256=rN4K1nz041XBaUJmnBBKHS2cHmQf11vPNX8wf1hbVR4,4596
12
- scripts/start.sh,sha256=w1Q35NB-9FRZAez5I5veqgYIJ8XnVukGt8TTE_ad248,13608
12
+ scripts/start.sh,sha256=Sg6EUkQlRzex-B4fhHXSCNQtLsls_0Yh8nejLY3uEUU,14197
13
13
  scripts/start_tmux_codex.sh,sha256=xyLv29p924q-ysxvZYAP3T6VrqLPBPMBWo9QP7cuL50,4438
14
- scripts/stop_all.sh,sha256=FOz07gi2CI9sMHxBb8XkqHtxRYs3jt1RYgGrEi-htVg,4086
14
+ scripts/stop_all.sh,sha256=VbNX4h8g1RcnGmLyutPcSySTHb84eIMbo3Ve9Vh-CDg,4360
15
15
  scripts/stop_bot.sh,sha256=ot6Sm0IYoXuRNslUVEflQmJKu5Agm-5xIUXXudJWyTM,5588
16
16
  scripts/test_deps_check.sh,sha256=AeSTucbNuNdOPSGvqVp0m31TVFDb0DmU8gSKg-Y5z2I,1696
17
17
  scripts/.venv/lib/python3.14/site-packages/pip/__init__.py,sha256=_lgs5Mfp0t7AGtI7sTVwxKWquz_vahWWH9kKO1cJusA,353
@@ -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=dM5R5cK5kft5hzz0-VxwpKzzUMt5ZpQqfPLQaDyAILg,311
429
+ vibego_cli/__init__.py,sha256=sMLnwzYulAP_Q8uYOcW_39L0BHzfzHQtdJ3iGiivoQQ,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.42.dist-info/METADATA,sha256=Nw9HJPv7fH0iQmxjRx7wQ_mDoODZ_1hcwmVwJC5TvTI,10519
436
- vibego-0.2.42.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
437
- vibego-0.2.42.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
438
- vibego-0.2.42.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
439
- vibego-0.2.42.dist-info/RECORD,,
435
+ vibego-0.2.44.dist-info/METADATA,sha256=MUqjLTXp0J9uHaYEa1ihXzLOh5bv6OPyVvxwAGXTTmM,10519
436
+ vibego-0.2.44.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
437
+ vibego-0.2.44.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
438
+ vibego-0.2.44.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
439
+ vibego-0.2.44.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.42"
10
+ __version__ = "0.2.44"
11
11
 
12
12
  from .main import main # noqa: E402