vibego 0.2.8__py3-none-any.whl → 0.2.10__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
@@ -868,9 +868,9 @@ async def _dispatch_prompt_to_model(
868
868
  session_path,
869
869
  extra=_session_extra(path=session_path),
870
870
  )
871
- if session_path is None and _is_claudecode_model():
871
+ if _is_claudecode_model():
872
872
  fallback = _find_latest_claudecode_rollout(pointer_path)
873
- if fallback is not None:
873
+ if fallback is not None and fallback != session_path:
874
874
  _update_pointer(pointer_path, fallback)
875
875
  session_path = fallback
876
876
  worker_log.info(
@@ -3892,18 +3892,6 @@ async def _ensure_session_watcher(chat_id: int) -> Optional[Path]:
3892
3892
  session_path,
3893
3893
  extra=_session_extra(path=session_path),
3894
3894
  )
3895
- if session_path is None and pointer_path is not None and _is_claudecode_model():
3896
- fallback = _find_latest_claudecode_rollout(pointer_path)
3897
- if fallback is not None:
3898
- session_path = fallback
3899
- _update_pointer(pointer_path, session_path)
3900
- worker_log.info(
3901
- "[session-map] chat=%s resumed ClaudeCode session %s",
3902
- chat_id,
3903
- session_path,
3904
- extra=_session_extra(path=session_path),
3905
- )
3906
-
3907
3895
  if session_path is None and pointer_path is not None:
3908
3896
  latest = _find_latest_rollout_for_cwd(pointer_path, target_cwd)
3909
3897
  if latest is not None:
@@ -3916,10 +3904,20 @@ async def _ensure_session_watcher(chat_id: int) -> Optional[Path]:
3916
3904
  extra=_session_extra(path=session_path),
3917
3905
  )
3918
3906
 
3907
+ if pointer_path is not None and _is_claudecode_model():
3908
+ fallback = _find_latest_claudecode_rollout(pointer_path)
3909
+ if fallback is not None and fallback != session_path:
3910
+ session_path = fallback
3911
+ _update_pointer(pointer_path, session_path)
3912
+ worker_log.info(
3913
+ "[session-map] chat=%s resume ClaudeCode session %s",
3914
+ chat_id,
3915
+ session_path,
3916
+ extra=_session_extra(path=session_path),
3917
+ )
3918
+
3919
3919
  if session_path is None and pointer_path is not None:
3920
3920
  session_path = await _await_session_path(pointer_path, target_cwd)
3921
- if session_path is None and _is_claudecode_model():
3922
- session_path = _find_latest_claudecode_rollout(pointer_path)
3923
3921
  if session_path is not None:
3924
3922
  _update_pointer(pointer_path, session_path)
3925
3923
  worker_log.info(
@@ -3928,6 +3926,17 @@ async def _ensure_session_watcher(chat_id: int) -> Optional[Path]:
3928
3926
  session_path,
3929
3927
  extra=_session_extra(path=session_path),
3930
3928
  )
3929
+ if session_path is None and pointer_path is not None and _is_claudecode_model():
3930
+ fallback = _find_latest_claudecode_rollout(pointer_path)
3931
+ if fallback is not None:
3932
+ session_path = fallback
3933
+ _update_pointer(pointer_path, session_path)
3934
+ worker_log.info(
3935
+ "[session-map] chat=%s fallback bind ClaudeCode session %s",
3936
+ chat_id,
3937
+ session_path,
3938
+ extra=_session_extra(path=session_path),
3939
+ )
3931
3940
 
3932
3941
  if session_path is None:
3933
3942
  worker_log.warning(
@@ -4192,43 +4201,54 @@ def _find_latest_claudecode_rollout(pointer: Path) -> Optional[Path]:
4192
4201
  """ClaudeCode 专用:在缺少 cwd 元数据时按更新时间选择最新会话文件。"""
4193
4202
 
4194
4203
  pointer_target = _read_pointer_path(pointer)
4204
+ candidates: List[Path] = []
4195
4205
  if pointer_target is not None:
4196
- return pointer_target
4206
+ candidates.append(pointer_target)
4197
4207
 
4198
4208
  search_roots: List[Path] = []
4199
4209
  if MODEL_SESSION_ROOT:
4200
4210
  search_roots.append(resolve_path(MODEL_SESSION_ROOT))
4211
+ if pointer_target is not None:
4212
+ search_roots.append(pointer_target.parent)
4201
4213
  search_roots.append(pointer.parent)
4202
4214
  search_roots.append(pointer.parent / "sessions")
4203
4215
 
4204
- latest_path: Optional[Path] = None
4205
- latest_mtime = -1.0
4206
- seen: set[str] = set()
4216
+ seen_roots: set[str] = set()
4207
4217
  pattern = f"**/{MODEL_SESSION_GLOB}"
4208
-
4209
4218
  for root in search_roots:
4210
4219
  try:
4211
4220
  real_root = root.resolve()
4212
4221
  except OSError:
4213
4222
  real_root = root
4214
4223
  key = str(real_root)
4215
- if key in seen:
4224
+ if key in seen_roots:
4216
4225
  continue
4217
- seen.add(key)
4226
+ seen_roots.add(key)
4218
4227
  if not real_root.exists():
4219
4228
  continue
4220
-
4221
4229
  for rollout in real_root.glob(pattern):
4222
- if not rollout.is_file():
4223
- continue
4224
- try:
4225
- mtime = rollout.stat().st_mtime
4226
- except OSError:
4227
- continue
4228
- if mtime <= latest_mtime:
4229
- continue
4230
+ if rollout.is_file():
4231
+ candidates.append(rollout)
4232
+
4233
+ latest_path: Optional[Path] = None
4234
+ latest_mtime = -1.0
4235
+ seen_files: set[str] = set()
4236
+ for rollout in candidates:
4237
+ try:
4238
+ real_rollout = rollout.resolve()
4239
+ except OSError:
4240
+ real_rollout = rollout
4241
+ key = str(real_rollout)
4242
+ if key in seen_files:
4243
+ continue
4244
+ seen_files.add(key)
4245
+ try:
4246
+ mtime = real_rollout.stat().st_mtime
4247
+ except OSError:
4248
+ continue
4249
+ if mtime > latest_mtime:
4230
4250
  latest_mtime = mtime
4231
- latest_path = rollout
4251
+ latest_path = Path(real_rollout)
4232
4252
 
4233
4253
  return latest_path
4234
4254
 
@@ -4430,7 +4450,14 @@ def _extract_codex_payload(data: dict, *, event_timestamp: Optional[str]) -> Opt
4430
4450
  return None
4431
4451
 
4432
4452
 
4433
- def _extract_claudecode_payload(data: dict, *, event_timestamp: Optional[str]) -> Optional[Tuple[str, str, Optional[Dict[str, Any]]]]:
4453
+ def _extract_claudecode_payload(
4454
+ data: dict, *, event_timestamp: Optional[str]
4455
+ ) -> Optional[Tuple[str, str, Optional[Dict[str, Any]]]]:
4456
+ # Claude Code 在启动时会输出 isSidechain=true 的欢迎语,此类事件直接忽略
4457
+ sidechain_flag = data.get("isSidechain")
4458
+ if isinstance(sidechain_flag, bool) and sidechain_flag:
4459
+ return None
4460
+
4434
4461
  event_type = data.get("type")
4435
4462
 
4436
4463
  if event_type == "assistant":
@@ -4443,20 +4470,11 @@ def _extract_claudecode_payload(data: dict, *, event_timestamp: Optional[str]) -
4443
4470
  if not isinstance(item, dict):
4444
4471
  continue
4445
4472
  item_type = item.get("type")
4446
- if item_type in {"thinking", "tool_use"}:
4447
- continue
4448
- if item_type == "tool_result":
4449
- tool_output = item.get("output")
4450
- if isinstance(tool_output, str) and tool_output.strip():
4451
- fragments.append(tool_output.strip())
4452
- content_value = item.get("content")
4453
- if isinstance(content_value, str) and content_value.strip():
4454
- fragments.append(content_value.strip())
4473
+ if item_type != "text":
4455
4474
  continue
4456
- if item_type in {"text", "output_text", "markdown"}:
4457
- text_value = item.get("text") or item.get("markdown")
4458
- if isinstance(text_value, str) and text_value.strip():
4459
- fragments.append(text_value)
4475
+ text_value = item.get("text")
4476
+ if isinstance(text_value, str) and text_value.strip():
4477
+ fragments.append(text_value)
4460
4478
  if fragments:
4461
4479
  combined = "\n\n".join(fragments)
4462
4480
  metadata: Optional[Dict[str, Any]] = None
master.py CHANGED
@@ -1716,13 +1716,8 @@ async def _notify_restart_success(bot: Bot) -> None:
1716
1716
  except Exception as exc:
1717
1717
  log.error("发送重启成功通知失败: %s", exc, extra={"chat": chat_id})
1718
1718
  else:
1719
+ # 重启成功后不再附带项目列表,避免高频重启时产生额外噪音
1719
1720
  log.info("重启成功通知已发送", extra={"chat": chat_id})
1720
- try:
1721
- manager = await _ensure_manager()
1722
- except Exception as ensure_exc:
1723
- log.error("发送项目概览前获取 manager 失败: %s", ensure_exc)
1724
- else:
1725
- await _send_projects_overview_to_chat(bot, chat_id, manager)
1726
1721
  finally:
1727
1722
  _safe_remove(RESTART_SIGNAL_PATH)
1728
1723
 
@@ -1802,6 +1797,12 @@ async def cmd_start(message: Message) -> None:
1802
1797
  "使用 /projects 查看状态,/run 或 /stop 控制 worker。",
1803
1798
  reply_markup=_build_master_main_keyboard(),
1804
1799
  )
1800
+ await _send_projects_overview_to_chat(
1801
+ message.bot,
1802
+ message.chat.id,
1803
+ manager,
1804
+ reply_to_message_id=message.message_id,
1805
+ )
1805
1806
 
1806
1807
 
1807
1808
  async def _perform_restart(message: Message, start_script: Path) -> None:
@@ -1901,6 +1902,30 @@ async def _send_projects_overview_to_chat(
1901
1902
  log.info("已发送项目概览,按钮=%s", "无" if markup is None else "有")
1902
1903
 
1903
1904
 
1905
+ async def _refresh_project_overview(
1906
+ message: Optional[Message],
1907
+ manager: MasterManager,
1908
+ ) -> None:
1909
+ """在原消息上刷新项目概览,无法编辑时发送新消息。"""
1910
+
1911
+ if message is None:
1912
+ return
1913
+ manager.refresh_state()
1914
+ try:
1915
+ text, markup = _projects_overview(manager)
1916
+ except Exception as exc:
1917
+ log.exception("刷新项目概览失败: %s", exc)
1918
+ return
1919
+ try:
1920
+ await message.edit_text(text, reply_markup=markup)
1921
+ except TelegramBadRequest as exc:
1922
+ log.warning("编辑项目概览失败,将发送新消息: %s", exc)
1923
+ try:
1924
+ await message.answer(text, reply_markup=markup)
1925
+ except Exception as send_exc:
1926
+ log.exception("发送项目概览失败: %s", send_exc)
1927
+
1928
+
1904
1929
  @router.message(Command("projects"))
1905
1930
  async def cmd_projects(message: Message) -> None:
1906
1931
  """处理 /projects 命令,返回最新项目概览。"""
@@ -2123,6 +2148,7 @@ async def on_project_action(callback: CallbackQuery, state: FSMContext) -> None:
2123
2148
  elif action == "start_all":
2124
2149
  await manager.run_all()
2125
2150
  log.info("按钮操作成功: user=%s 启动全部项目", user_id)
2151
+ await callback.answer("全部项目已启动,正在刷新列表…")
2126
2152
  elif action == "restart_master":
2127
2153
  if callback.message is None:
2128
2154
  log.error("重启按钮回调缺少 message 对象", extra={"user": user_id})
@@ -2143,6 +2169,7 @@ async def on_project_action(callback: CallbackQuery, state: FSMContext) -> None:
2143
2169
  chosen,
2144
2170
  extra={"project": cfg.project_slug, "model": chosen},
2145
2171
  )
2172
+ await callback.answer("项目已启动,正在刷新列表…")
2146
2173
  elif action == "stop":
2147
2174
  await manager.stop_worker(cfg)
2148
2175
  log.info(
@@ -2151,6 +2178,7 @@ async def on_project_action(callback: CallbackQuery, state: FSMContext) -> None:
2151
2178
  cfg.display_name,
2152
2179
  extra={"project": cfg.project_slug},
2153
2180
  )
2181
+ await callback.answer("项目已停止,正在刷新列表…")
2154
2182
  elif action == "switch_all_to":
2155
2183
  model_map = dict(SWITCHABLE_MODELS)
2156
2184
  if target_model not in model_map:
@@ -2251,14 +2279,7 @@ async def on_project_action(callback: CallbackQuery, state: FSMContext) -> None:
2251
2279
  await callback.answer("操作失败", show_alert=True)
2252
2280
  return
2253
2281
 
2254
- manager.refresh_state()
2255
- text, markup = _projects_overview(manager)
2256
- if callback.message:
2257
- try:
2258
- await callback.message.edit_text(text, reply_markup=_ensure_numbered_markup(markup))
2259
- except TelegramBadRequest as exc:
2260
- if "message is not modified" not in str(exc).lower():
2261
- raise
2282
+ await _refresh_project_overview(callback.message, manager)
2262
2283
 
2263
2284
 
2264
2285
  @router.message(Command("run"))
scripts/start.sh CHANGED
@@ -102,6 +102,57 @@ ensure_codex_installed() {
102
102
 
103
103
  ensure_codex_installed
104
104
 
105
+ select_python_binary() {
106
+ # 选择满足 CPython <=3.13 的解释器,避免 PyO3 依赖构建失败
107
+ local candidates=()
108
+ local chosen=""
109
+ local name
110
+ if [[ -n "${VIBEGO_PYTHON:-}" ]]; then
111
+ candidates+=("$VIBEGO_PYTHON")
112
+ fi
113
+ for name in python3.13 python3.12 python3.11 python3.10 python3.9 python3; do
114
+ if [[ "${VIBEGO_PYTHON:-}" == "$name" ]]; then
115
+ continue
116
+ fi
117
+ candidates+=("$name")
118
+ done
119
+
120
+ for name in "${candidates[@]}"; do
121
+ if [[ -z "$name" ]]; then
122
+ continue
123
+ fi
124
+ if ! command -v "$name" >/dev/null 2>&1; then
125
+ continue
126
+ fi
127
+ local version_raw
128
+ version_raw=$("$name" -c 'import sys; print(f"{sys.version_info[0]}.{sys.version_info[1]}")' 2>/dev/null) || continue
129
+ local major="${version_raw%%.*}"
130
+ local minor="${version_raw#*.}"
131
+ if [[ "$major" != "3" ]]; then
132
+ log_info "跳过 ${name} (版本 ${version_raw}):非 CPython 3.x"
133
+ continue
134
+ fi
135
+ if [[ "$minor" =~ ^[0-9]+$ ]] && (( minor > 13 )); then
136
+ log_info "跳过 ${name} (版本 ${version_raw}):高于 3.13"
137
+ continue
138
+ fi
139
+ if [[ "$minor" =~ ^[0-9]+$ ]] && (( minor < 9 )); then
140
+ log_info "跳过 ${name} (版本 ${version_raw}):低于 3.9,可能缺少官方轮子"
141
+ continue
142
+ fi
143
+ chosen="$name"
144
+ log_info "使用 Python 解释器:${chosen} (版本 ${version_raw})"
145
+ break
146
+ done
147
+
148
+ if [[ -z "$chosen" ]]; then
149
+ log_error "未找到满足 <=3.13 的 Python 解释器,可通过设置 VIBEGO_PYTHON 指定路径"
150
+ exit 1
151
+ fi
152
+
153
+ printf '%s' "$chosen"
154
+ }
155
+
105
156
  if pgrep -f "python.*master.py" >/dev/null 2>&1; then
106
157
  log_info "检测到历史 master 实例,正在终止..."
107
158
  pkill -f "python.*master.py" || true
@@ -119,7 +170,8 @@ if pgrep -f "python.*master.py" >/dev/null 2>&1; then
119
170
  fi
120
171
 
121
172
  # 创建并启用虚拟环境
122
- python3 -m venv .venv
173
+ PYTHON_BIN="$(select_python_binary)"
174
+ "$PYTHON_BIN" -m venv .venv
123
175
  source .venv/bin/activate
124
176
 
125
177
  # 安装依赖
@@ -131,11 +183,11 @@ fi
131
183
  pip install -r "$REQUIREMENTS_FILE"
132
184
 
133
185
  # 后台启动 master,日志落在 vibe.log
134
- nohup python3 master.py >> /dev/null 2>&1 &
186
+ nohup python master.py >> /dev/null 2>&1 &
135
187
  log_info "master 已后台启动,日志写入 vibe.log"
136
188
 
137
189
  # 健康检查:等待 master 上线并验证关键 worker
138
- if ! python3 scripts/master_healthcheck.py --project hyphavibebotbackend; then
190
+ if ! python scripts/master_healthcheck.py --project hyphavibebotbackend; then
139
191
  log_error "master 健康检查失败,请查看 logs/start.log / vibe.log"
140
192
  exit 1
141
193
  fi
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibego
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: vibego CLI:用于初始化与管理 Telegram Master Bot 的工具
5
5
  Author: Hypha
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,13 +1,13 @@
1
- bot.py,sha256=QkDvk3QNkgVPRjEU4hvrrb54vTVZHclbiPOgGzUgNsg,249422
1
+ bot.py,sha256=q_aRgSBen4XThKdExfa-6NeX9R_Mgs0UjXYx32rsGzo,249827
2
2
  logging_setup.py,sha256=gvxHi8mUwK3IhXJrsGNTDo-DR6ngkyav1X-tvlBF_IE,4613
3
- master.py,sha256=Dz3yOew0b04k3eyp3wSfiPMtYujIRemxGXQBxDmtErk,105771
3
+ master.py,sha256=Qz2NTapUexVvpQz8Y_pVhKd-uXkqp3M6oclzfAzIuGs,106497
4
4
  project_repository.py,sha256=UcthtSGOJK0cTE5bQCneo3xkomRG-kyc1N1QVqxeHIs,17577
5
5
  scripts/__init__.py,sha256=LVrXUkvWKoc6Sb47X5G0gbIxu5aJ2ARW-qJ14vwi5vM,65
6
6
  scripts/log_writer.py,sha256=8euoMlRo7cbtHApbcEoJnwzLABxti-ovJWFLRN1oDQw,3843
7
7
  scripts/master_healthcheck.py,sha256=-X0VVsZ0AXaOb7izxTO_oyu23g_1jsirNdGIcP8nrSI,8321
8
8
  scripts/requirements.txt,sha256=QSt30DSSSHtfucTFPpc7twk9kLS5rVLNTcvDiagxrZg,62
9
9
  scripts/run_bot.sh,sha256=rM2Op_l4mP9asT3VoTwGQuHocwaxiGyQp2l1PLegsY4,4481
10
- scripts/start.sh,sha256=lYCzQcgN_TC1rAhaboKrtnLJQEjBDb3PFQLfaF_j8TA,4047
10
+ scripts/start.sh,sha256=tb_h90-nU4po3asUc3D7YBwvDXmxeWGgPwxTEBpI8Ow,5641
11
11
  scripts/start_tmux_codex.sh,sha256=4ko72SK7EDJmzXVuLBOz1_S1lLEmgIpLAVuD7mZhpdk,4018
12
12
  scripts/stop_all.sh,sha256=FOz07gi2CI9sMHxBb8XkqHtxRYs3jt1RYgGrEi-htVg,4086
13
13
  scripts/stop_bot.sh,sha256=b9vvrke5jeyO18SEBFZxQD09Gf65BgGzeKtpM8sa60Y,3677
@@ -429,8 +429,8 @@ vibego_cli/config.py,sha256=33WSORCfUIxrDtgASPEbVqVLBVNHh-RSFLpNy7tfc0s,2992
429
429
  vibego_cli/deps.py,sha256=1nRXI7Dd-S1hYE8DligzK5fIluQWETRUj4_OKL0DikQ,1419
430
430
  vibego_cli/main.py,sha256=e2W5Pb9U9rfmF-jNX9uIA3222lhM0GgcvSdFTDBZd2s,12086
431
431
  vibego_cli/data/worker_requirements.txt,sha256=QSt30DSSSHtfucTFPpc7twk9kLS5rVLNTcvDiagxrZg,62
432
- vibego-0.2.8.dist-info/METADATA,sha256=d4knXRIW7t5Wdi0aa_L-WZgasZbQBehmJGQENoCzDP8,10474
433
- vibego-0.2.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
434
- vibego-0.2.8.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
435
- vibego-0.2.8.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
436
- vibego-0.2.8.dist-info/RECORD,,
432
+ vibego-0.2.10.dist-info/METADATA,sha256=CzxFnVhczBzeTQ1aK8jjhhoYJT85g6QtQy_rU4aDoaQ,10475
433
+ vibego-0.2.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
434
+ vibego-0.2.10.dist-info/entry_points.txt,sha256=Lsy_zm-dlyxt8-9DL9blBReIwU2k22c8-kifr46ND1M,48
435
+ vibego-0.2.10.dist-info/top_level.txt,sha256=R56CT3nW5H5v3ce0l3QDN4-C4qxTrNWzRTwrxnkDX4U,69
436
+ vibego-0.2.10.dist-info/RECORD,,