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 +37 -8
- master.py +52 -12
- scripts/start.sh +34 -5
- {vibego-0.2.19.dist-info → vibego-0.2.21.dist-info}/METADATA +1 -1
- {vibego-0.2.19.dist-info → vibego-0.2.21.dist-info}/RECORD +9 -9
- vibego_cli/__init__.py +1 -1
- {vibego-0.2.19.dist-info → vibego-0.2.21.dist-info}/WHEEL +0 -0
- {vibego-0.2.19.dist-info → vibego-0.2.21.dist-info}/entry_points.txt +0 -0
- {vibego-0.2.19.dist-info → vibego-0.2.21.dist-info}/top_level.txt +0 -0
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"-
|
|
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", "
|
|
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
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
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
|
-
|
|
1588
|
-
|
|
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
|
-
|
|
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
|
-
|
|
234
|
+
MASTER_PID=$!
|
|
235
|
+
log_info "master 已后台启动,PID=$MASTER_PID,日志写入 vibe.log"
|
|
228
236
|
|
|
229
237
|
# 健康检查:等待 master 上线并验证关键 worker
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
bot.py,sha256=
|
|
1
|
+
bot.py,sha256=RpOsQyJLRhl5Nw_zlugcBLyrRSDu_z0Bu4UzZgUBm5A,270815
|
|
2
2
|
logging_setup.py,sha256=gvxHi8mUwK3IhXJrsGNTDo-DR6ngkyav1X-tvlBF_IE,4613
|
|
3
|
-
master.py,sha256=
|
|
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=
|
|
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=
|
|
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.
|
|
435
|
-
vibego-0.2.
|
|
436
|
-
vibego-0.2.
|
|
437
|
-
vibego-0.2.
|
|
438
|
-
vibego-0.2.
|
|
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
|
File without changes
|
|
File without changes
|
|
File without changes
|