vibego 0.2.19__py3-none-any.whl → 0.2.21__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.

bot.py CHANGED
@@ -435,6 +435,8 @@ def _escape_markdown_v2(text: str) -> str:
435
435
  LEGACY_DOUBLE_BOLD = re.compile(r"\*\*(.+?)\*\*", re.DOTALL)
436
436
  LEGACY_DOUBLE_UNDERLINE = re.compile(r"__(.+?)__", re.DOTALL)
437
437
  CODE_SEGMENT_RE = re.compile(r"(```.*?```|`[^`]*`)", re.DOTALL)
438
+ # Markdown 标题模式(# - ####)
439
+ MARKDOWN_HEADING = re.compile(r"^(#{1,4})\s+(.+)$", re.MULTILINE)
438
440
 
439
441
 
440
442
  def _normalize_legacy_markdown(text: str) -> str:
@@ -466,6 +468,32 @@ def _normalize_legacy_markdown(text: str) -> str:
466
468
  return "".join(pieces)
467
469
 
468
470
 
471
+ def _normalize_to_telegram_markdown(text: str) -> str:
472
+ """将标准 Markdown 格式转换为 Telegram MarkdownV2 支持的格式
473
+
474
+ 转换规则:
475
+ - **text** → *text* (加粗)
476
+ - __text__ → _text_ (斜体)
477
+ - #### 标题 → *标题* (标题转为加粗)
478
+ """
479
+ # 先转换加粗和下划线语法
480
+ text = _normalize_legacy_markdown(text)
481
+
482
+ # 转换标题:移除 # 符号,将标题文本转为加粗
483
+ def _replace_heading(match: re.Match[str]) -> str:
484
+ # match.group(1) 是 # 符号
485
+ # match.group(2) 是标题文本
486
+ heading_text = match.group(2)
487
+ # 标题转为加粗文本(如果文本已经是加粗的,避免重复)
488
+ if heading_text.startswith("*") and heading_text.endswith("*"):
489
+ return heading_text # 已经是加粗,保持不变
490
+ return f"*{heading_text}*"
491
+
492
+ text = MARKDOWN_HEADING.sub(_replace_heading, text)
493
+
494
+ return text
495
+
496
+
469
497
  # MarkdownV2 转义字符模式(用于检测已转义文本)
470
498
  _ESCAPED_MARKDOWN_PATTERN = re.compile(
471
499
  r"\\[_*\[\]()~`>#+=|{}.!:-]" # 添加了冒号
@@ -593,7 +621,10 @@ def _unescape_if_already_escaped(text: str) -> str:
593
621
 
594
622
  def _prepare_model_payload(text: str) -> str:
595
623
  if _IS_MARKDOWN_V2:
596
- return _escape_markdown_v2(text)
624
+ # 先转换标准 Markdown 格式为 Telegram 支持的格式
625
+ normalized = _normalize_to_telegram_markdown(text)
626
+ # 再进行 MarkdownV2 转义
627
+ return _escape_markdown_v2(normalized)
597
628
  if _IS_MARKDOWN:
598
629
  return _normalize_legacy_markdown(text)
599
630
  return text
@@ -663,10 +694,7 @@ async def _send_with_markdown_guard(
663
694
  sanitized: Optional[str]
664
695
  if _IS_MARKDOWN_V2:
665
696
  sanitized = _escape_markdown_v2(text)
666
- if "**" in text:
667
- sanitized = sanitized.replace(r"\*\*", "**")
668
- if "__" in text:
669
- sanitized = sanitized.replace(r"\_\_", "__")
697
+ # 保留代码块标记不转义(它们本身就是 Markdown 语法)
670
698
  if "```" in text:
671
699
  sanitized = sanitized.replace(r"\`\`\`", "```")
672
700
  if "`" in text:
@@ -2448,8 +2476,9 @@ def _format_history_line(item: TaskHistoryRecord) -> str:
2448
2476
  for line in description.splitlines()
2449
2477
  if line.strip()
2450
2478
  ]
2451
- # Markdown 列表使用“- ”起始,后续详情以缩进列表呈现,便于聊天端渲染。
2452
- formatted = [f"- **{summary}** · {timestamp}"]
2479
+ # Markdown 列表使用"- "起始,后续详情以缩进列表呈现,便于聊天端渲染。
2480
+ # MarkdownV2 使用单星号 * 表示加粗
2481
+ formatted = [f"- *{summary}* · {timestamp}"]
2453
2482
  for detail in detail_lines:
2454
2483
  formatted.append(f" - {detail}")
2455
2484
  formatted.append("") # 追加空行分隔历史记录
@@ -3076,7 +3105,7 @@ def _build_truncated_history_entry(item: TaskHistoryRecord) -> str:
3076
3105
  summary = _format_history_summary(item)
3077
3106
  return "\n".join(
3078
3107
  [
3079
- f"- **{summary}** · {timestamp}",
3108
+ f"- *{summary}* · {timestamp}",
3080
3109
  " - ⚠️ 该记录内容较长,仅展示摘要概要。",
3081
3110
  ]
3082
3111
  )
master.py CHANGED
@@ -65,7 +65,7 @@ STOP_SCRIPT = ROOT_DIR / "scripts/stop_bot.sh"
65
65
  RESTART_SIGNAL_PATH = Path(
66
66
  os.environ.get("MASTER_RESTART_SIGNAL_PATH", ROOT_DIR / "state/restart_signal.json")
67
67
  )
68
- RESTART_SIGNAL_TTL = int(os.environ.get("MASTER_RESTART_SIGNAL_TTL", "600")) # 默认 10 分钟
68
+ RESTART_SIGNAL_TTL = int(os.environ.get("MASTER_RESTART_SIGNAL_TTL", "1800")) # 默认 30 分钟
69
69
  LOCAL_TZ = ZoneInfo(os.environ.get("MASTER_TIMEZONE", "Asia/Shanghai"))
70
70
  JUMP_BUTTON_TEXT_WIDTH = 40
71
71
 
@@ -1578,14 +1578,42 @@ def _log_update(message: Message, *, override_user: Optional[User] = None) -> No
1578
1578
  _update_master_env(chat_id, user_id)
1579
1579
 
1580
1580
 
1581
- def _safe_remove(path: Path) -> None:
1582
- """安全移除文件,忽略不存在的情况"""
1583
- try:
1584
- path.unlink()
1585
- except FileNotFoundError:
1581
+ def _safe_remove(path: Path, *, retries: int = 3) -> None:
1582
+ """安全移除文件,支持重试机制
1583
+
1584
+ Args:
1585
+ path: 要删除的文件路径
1586
+ retries: 最大重试次数(默认 3 次)
1587
+ """
1588
+ if not path.exists():
1589
+ log.debug("文件不存在,无需删除", extra={"path": str(path)})
1586
1590
  return
1587
- except Exception as exc:
1588
- log.warning("删除文件失败: %s", exc, extra={"path": str(path)})
1591
+
1592
+ for attempt in range(retries):
1593
+ try:
1594
+ path.unlink()
1595
+ log.info("重启信号文件已删除", extra={"path": str(path), "attempt": attempt + 1})
1596
+ return
1597
+ except FileNotFoundError:
1598
+ log.debug("文件已被其他进程删除", extra={"path": str(path)})
1599
+ return
1600
+ except Exception as exc:
1601
+ if attempt < retries - 1:
1602
+ log.warning(
1603
+ "删除文件失败,将重试 (attempt %d/%d): %s",
1604
+ attempt + 1,
1605
+ retries,
1606
+ exc,
1607
+ extra={"path": str(path)}
1608
+ )
1609
+ import time
1610
+ time.sleep(0.1) # 等待 100ms 后重试
1611
+ else:
1612
+ log.error(
1613
+ "删除文件失败,已达最大重试次数: %s",
1614
+ exc,
1615
+ extra={"path": str(path), "retries": retries}
1616
+ )
1589
1617
 
1590
1618
 
1591
1619
  def _write_restart_signal(message: Message, *, override_user: Optional[User] = None) -> None:
@@ -1663,13 +1691,24 @@ async def _notify_restart_success(bot: Bot) -> None:
1663
1691
  "启动时未检测到重启信号文件,将向管理员发送兜底提醒", extra={"targets": targets}
1664
1692
  )
1665
1693
  if targets:
1666
- text = (
1667
- "Master 已重新上线,但未找到重启触发者信息。"
1668
- "可能是重启信号写入失败,请确认服务状态。"
1669
- )
1694
+ text_lines = [
1695
+ "⚠️ Master 已重新上线,但未找到重启触发者信息。",
1696
+ "",
1697
+ "可能原因:",
1698
+ "1. 重启信号文件写入失败",
1699
+ "2. 信号文件已超时被清理(TTL=30分钟)",
1700
+ "3. 文件系统权限问题",
1701
+ "",
1702
+ "建议检查:",
1703
+ f"- 启动日志: {ROOT_DIR}/logs/start.log",
1704
+ f"- 运行日志: {ROOT_DIR}/vibe.log",
1705
+ f"- 信号文件: {RESTART_SIGNAL_PATH}",
1706
+ ]
1707
+ text = "\n".join(text_lines)
1670
1708
  for chat in targets:
1671
1709
  try:
1672
1710
  await bot.send_message(chat_id=chat, text=text)
1711
+ log.info("兜底重启通知已发送", extra={"chat": chat})
1673
1712
  except Exception as exc:
1674
1713
  log.error("发送兜底重启通知失败: %s", exc, extra={"chat": chat})
1675
1714
  else:
@@ -2161,6 +2200,7 @@ async def on_project_action(callback: CallbackQuery, state: FSMContext) -> None:
2161
2200
  manager=manager,
2162
2201
  )
2163
2202
  log.info("按钮操作成功: user=%s 重启 master", user_id)
2203
+ return # 重启后不刷新项目列表,避免产生额外噪音
2164
2204
  elif action == "run":
2165
2205
  chosen = await manager.run_worker(cfg)
2166
2206
  log.info(
scripts/start.sh CHANGED
@@ -223,12 +223,41 @@ else
223
223
  fi
224
224
 
225
225
  # 后台启动 master,日志落在 vibe.log
226
+ # 显式传递重启标记环境变量(如果存在)
227
+ if [[ -n "${MASTER_RESTART_EXPECTED:-}" ]]; then
228
+ log_info "检测到重启标记环境变量 MASTER_RESTART_EXPECTED=$MASTER_RESTART_EXPECTED"
229
+ export MASTER_RESTART_EXPECTED
230
+ fi
231
+
232
+ log_info "准备启动 master 进程..."
226
233
  nohup python master.py >> /dev/null 2>&1 &
227
- log_info "master 已后台启动,日志写入 vibe.log"
234
+ MASTER_PID=$!
235
+ log_info "master 已后台启动,PID=$MASTER_PID,日志写入 vibe.log"
228
236
 
229
237
  # 健康检查:等待 master 上线并验证关键 worker
230
- if ! python scripts/master_healthcheck.py --project hyphavibebotbackend; then
231
- log_error "master 健康检查失败,请查看 logs/start.log / vibe.log"
232
- exit 1
238
+ log_info "开始执行健康检查..."
239
+ HEALTHCHECK_START=$(date +%s)
240
+
241
+ if python scripts/master_healthcheck.py --project hyphavibebotbackend; then
242
+ HEALTHCHECK_END=$(date +%s)
243
+ HEALTHCHECK_DURATION=$((HEALTHCHECK_END - HEALTHCHECK_START))
244
+ log_info "✅ master 健康检查通过,耗时 ${HEALTHCHECK_DURATION}s"
245
+ else
246
+ HEALTHCHECK_END=$(date +%s)
247
+ HEALTHCHECK_DURATION=$((HEALTHCHECK_END - HEALTHCHECK_START))
248
+ log_error "⚠️ master 健康检查失败,耗时 ${HEALTHCHECK_DURATION}s"
249
+ log_error "建议检查:"
250
+ log_error " - 进程状态: ps aux | grep 'python.*master.py'"
251
+ log_error " - 启动日志: tail -100 $ROOT_DIR/logs/start.log"
252
+ log_error " - 运行日志: tail -100 $ROOT_DIR/vibe.log"
253
+ log_error " - 进程 PID: $MASTER_PID"
254
+
255
+ # 检查进程是否仍在运行
256
+ if kill -0 "$MASTER_PID" 2>/dev/null; then
257
+ log_info "master 进程仍在运行(PID=$MASTER_PID),允许继续启动"
258
+ log_info "⚠️ 请手动验证服务是否正常工作"
259
+ else
260
+ log_error "❌ master 进程已退出,启动失败"
261
+ exit 1
262
+ fi
233
263
  fi
234
- log_info "master 健康检查通过"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibego
3
- Version: 0.2.19
3
+ Version: 0.2.21
4
4
  Summary: vibego CLI:用于初始化与管理 Telegram Master Bot 的工具
5
5
  Author: Hypha
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
- bot.py,sha256=aK68QaITXzOoUStLqejg8L6Bd3LuAqNLOw6R3-7SfYw,269638
1
+ bot.py,sha256=RpOsQyJLRhl5Nw_zlugcBLyrRSDu_z0Bu4UzZgUBm5A,270815
2
2
  logging_setup.py,sha256=gvxHi8mUwK3IhXJrsGNTDo-DR6ngkyav1X-tvlBF_IE,4613
3
- master.py,sha256=qw6ZxItpSr6C5YzjVDOGm4jZpnGYLZndsJ-lmJrzGcM,106553
3
+ master.py,sha256=mA8eXtTGHrtqZ3MB4u56unHnq8NEMm3HZ2GikxRFeYY,108225
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
@@ -8,7 +8,7 @@ scripts/log_writer.py,sha256=8euoMlRo7cbtHApbcEoJnwzLABxti-ovJWFLRN1oDQw,3843
8
8
  scripts/master_healthcheck.py,sha256=-X0VVsZ0AXaOb7izxTO_oyu23g_1jsirNdGIcP8nrSI,8321
9
9
  scripts/requirements.txt,sha256=QSt30DSSSHtfucTFPpc7twk9kLS5rVLNTcvDiagxrZg,62
10
10
  scripts/run_bot.sh,sha256=rN4K1nz041XBaUJmnBBKHS2cHmQf11vPNX8wf1hbVR4,4596
11
- scripts/start.sh,sha256=IsNjFWXX3qmFxx_7iiVEidTj9VHBUTkNNbsCPoNSL0E,6782
11
+ scripts/start.sh,sha256=uy_KlLaxt-UKQLeWO3UxIgh38noqw9yRsQgdd-y0HBc,7962
12
12
  scripts/start_tmux_codex.sh,sha256=xyLv29p924q-ysxvZYAP3T6VrqLPBPMBWo9QP7cuL50,4438
13
13
  scripts/stop_all.sh,sha256=FOz07gi2CI9sMHxBb8XkqHtxRYs3jt1RYgGrEi-htVg,4086
14
14
  scripts/stop_bot.sh,sha256=ot6Sm0IYoXuRNslUVEflQmJKu5Agm-5xIUXXudJWyTM,5588
@@ -425,14 +425,14 @@ tasks/constants.py,sha256=tS1kZxBIUm3JJUMHm25XI-KHNUZl5NhbbuzjzL_rF-c,299
425
425
  tasks/fsm.py,sha256=rKXXLEieQQU4r2z_CZUvn1_70FXiZXBBugF40gpe_tQ,1476
426
426
  tasks/models.py,sha256=N_qqRBo9xMSV0vbn4k6bLBXT8C_dp_oTFUxvdx16ZQM,2459
427
427
  tasks/service.py,sha256=w_S_aWiVqRXzXEpimLDsuCCCX2lB5uDkff9aKThBw9c,41916
428
- vibego_cli/__init__.py,sha256=PbHZ3j0Ku7MxszOczRhZnNfiggYU_AB2P91m7bMQqCw,311
428
+ vibego_cli/__init__.py,sha256=TUL2ldIDgEsLVjy5LM958jPJFGU_dia4rwPFBO6kMeU,311
429
429
  vibego_cli/__main__.py,sha256=qqTrYmRRLe4361fMzbI3-CqpZ7AhTofIHmfp4ykrrBY,158
430
430
  vibego_cli/config.py,sha256=33WSORCfUIxrDtgASPEbVqVLBVNHh-RSFLpNy7tfc0s,2992
431
431
  vibego_cli/deps.py,sha256=1nRXI7Dd-S1hYE8DligzK5fIluQWETRUj4_OKL0DikQ,1419
432
432
  vibego_cli/main.py,sha256=e2W5Pb9U9rfmF-jNX9uIA3222lhM0GgcvSdFTDBZd2s,12086
433
433
  vibego_cli/data/worker_requirements.txt,sha256=QSt30DSSSHtfucTFPpc7twk9kLS5rVLNTcvDiagxrZg,62
434
- vibego-0.2.19.dist-info/METADATA,sha256=EcNsx_kVnc0gZiqMdbpIHRDYcIFu09gERPQubTVW-mY,10475
435
- vibego-0.2.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
436
- vibego-0.2.19.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
437
- vibego-0.2.19.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
438
- vibego-0.2.19.dist-info/RECORD,,
434
+ vibego-0.2.21.dist-info/METADATA,sha256=vbbhaHjt4q3lxVsRnWOSGkSRa1_ZuiFizOYGcU90A3c,10475
435
+ vibego-0.2.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
436
+ vibego-0.2.21.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
437
+ vibego-0.2.21.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
438
+ vibego-0.2.21.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.19"
10
+ __version__ = "0.2.21"
11
11
 
12
12
  from .main import main # noqa: E402