vibego 0.2.58__py3-none-any.whl → 1.0.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.
@@ -1,147 +1,75 @@
1
1
  #!/usr/bin/env python3
2
- """Master 启动后的健康检查脚本。
2
+ """Master post-startup health check.
3
3
 
4
- 流程:
5
- 1. 等待 master 日志出现启动标记。
6
- 2. 调用 MasterManager 启动指定 worker(默认 hyphavibebotbackend)。
7
- 3. 自动发现该 worker 的 chat_id(优先 state 文件,其次读取最新日志)。
8
- 4. 通过 Telegram Bot API 向该 chat 发送探针消息,确认发送成功。
9
- 5. 任何步骤失败则抛出异常,并尝试通知管理员。
10
-
11
- 注意:本脚本不会自动重试重启,仅返回非零退出码供外层脚本处理。
4
+ This lightweight variant only verifies that the master process has emitted the
5
+ readiness marker in its log file. Worker bootstrap and Telegram probes are no
6
+ longer executed because master restart confirmation is now limited to the
7
+ controller process itself.
12
8
  """
13
9
  from __future__ import annotations
14
10
 
15
11
  import argparse
16
- import asyncio
17
12
  import json
18
13
  import os
19
- import re
20
14
  import sys
21
15
  import time
22
16
  from pathlib import Path
23
- from typing import Optional
24
- from urllib.error import URLError, HTTPError
25
17
  from urllib.request import Request, urlopen
26
18
 
27
- # 导入 master 中的配置与工具,复用项目解析逻辑
19
+ # Make sure the master module can be imported from the repository root
28
20
  ROOT_DIR = Path(__file__).resolve().parent.parent
29
21
  ROOT_DIR_STR = str(ROOT_DIR)
30
22
  if ROOT_DIR_STR not in sys.path:
31
- # 确保可以从仓库根目录导入 master 模块
32
23
  sys.path.insert(0, ROOT_DIR_STR)
33
24
 
34
25
  import master # type: ignore
35
- from project_repository import ProjectRepository
26
+
36
27
  DEFAULT_MASTER_LOG = master.LOG_ROOT_PATH / "vibe.log"
37
28
  DEFAULT_TIMEOUT_MASTER = 60.0
38
- DEFAULT_TIMEOUT_PROBE = 15.0
39
- PROBE_TEXT = "hello"
40
- REPOSITORY = ProjectRepository(master.CONFIG_DB_PATH, master.CONFIG_PATH)
41
-
42
-
43
- def _load_project(project_id: str) -> master.ProjectConfig:
44
- """根据 slug 或 bot 名获取项目配置,失败时列出可选项。"""
45
-
46
- record = REPOSITORY.get_by_slug(project_id)
47
- if record is None:
48
- record = REPOSITORY.get_by_bot_name(project_id)
49
- if record is None:
50
- available = [r.project_slug for r in REPOSITORY.list_projects()]
51
- raise RuntimeError(f"未找到项目 {project_id},可选项目: {available}")
52
- return master.ProjectConfig.from_dict(record.to_dict())
29
+ MASTER_READY_MARKER = "Master Started, listening for administrator commands."
53
30
 
54
31
 
55
32
  def _wait_for_log_flag(path: Path, pattern: str, timeout: float) -> None:
56
- """在超时时间内等待日志出现特定标记。"""
33
+ """Poll the master log until the readiness marker is detected or timeout."""
57
34
 
58
35
  deadline = time.monotonic() + timeout
59
- position = 0
36
+ if path.exists():
37
+ position = path.stat().st_size
38
+ initialized = True
39
+ else:
40
+ position = 0
41
+ initialized = False
60
42
  while time.monotonic() < deadline:
61
43
  if path.exists():
62
- if position == 0:
63
- position = path.stat().st_size
44
+ if not initialized:
45
+ position = 0
46
+ initialized = True
64
47
  with path.open("r", encoding="utf-8", errors="ignore") as fh:
65
48
  fh.seek(position)
66
49
  while time.monotonic() < deadline:
67
50
  line = fh.readline()
68
51
  if not line:
69
52
  time.sleep(0.5)
70
- continue
53
+ break
71
54
  position = fh.tell()
72
55
  if pattern in line:
73
56
  return
74
57
  time.sleep(0.5)
75
- raise TimeoutError(f" {timeout:.0f} 秒内未检测到日志标记: {pattern}")
76
-
77
-
78
- def _extract_chat_id_from_logs(log_path: Path) -> Optional[int]:
79
- """从日志文件倒序查找最近的 chat_id。"""
80
-
81
- if not log_path.exists():
82
- return None
83
- pattern = re.compile(r"chat=(-?\d+)")
84
- try:
85
- lines = log_path.read_text(encoding="utf-8", errors="ignore").splitlines()
86
- except Exception:
87
- return None
88
- for line in reversed(lines[-200:]): # 反向查找最近的记录
89
- match = pattern.search(line)
90
- if match:
91
- try:
92
- return int(match.group(1))
93
- except ValueError:
94
- continue
95
- return None
96
-
97
-
98
- def _ensure_chat_id(cfg: master.ProjectConfig, manager: master.MasterManager) -> int:
99
- """确保任务分配有 chat_id,必要时从日志回填并写回 state。"""
100
-
101
- state = manager.state_store.data.get(cfg.project_slug)
102
- if state and state.chat_id:
103
- return int(state.chat_id)
104
- # 回落到日志查找
105
- log_dir = master.LOG_ROOT_PATH / (cfg.default_model.lower()) / cfg.project_slug
106
- chat_id = _extract_chat_id_from_logs(log_dir / "run_bot.log")
107
- if chat_id is None:
108
- raise RuntimeError(
109
- "无法自动获取 chat_id,请先手动与该 bot 发生一次对话以写入 state/log"
110
- )
111
- # 将发现的 chat_id 写回 state,便于下次复用
112
- manager.state_store.update(cfg.project_slug, chat_id=chat_id)
113
- return chat_id
114
-
115
-
116
- def _send_probe(bot_token: str, chat_id: int, text: str, timeout: float) -> None:
117
- """向指定 chat 发送探针消息,验证 Telegram API 可用。"""
118
-
119
- url = f"https://api.telegram.org/bot{bot_token}/sendMessage"
120
- payload = json.dumps({"chat_id": chat_id, "text": text, "disable_notification": True}).encode("utf-8")
121
- request = Request(url, data=payload, headers={"Content-Type": "application/json"}, method="POST")
122
- try:
123
- with urlopen(request, timeout=timeout) as resp:
124
- data = json.loads(resp.read().decode("utf-8"))
125
- except HTTPError as exc: # pragma: no cover - 网络异常时抛出
126
- raise RuntimeError(f"发送探针消息失败,HTTP {exc.code}: {exc.reason}") from exc
127
- except URLError as exc: # pragma: no cover - 网络异常时抛出
128
- raise RuntimeError(f"发送探针消息失败: {exc}") from exc
129
- if not data.get("ok"):
130
- raise RuntimeError(f"发送探针消息失败: {data}")
58
+ raise TimeoutError(f"No log markers detected within {timeout:.0f}s: {pattern}")
131
59
 
132
60
 
133
61
  def _format_admin_notice(reason: str) -> str:
134
- """生成通知管理员的告警文本。"""
62
+ """Compose the notification text for administrator alerts."""
135
63
 
136
64
  return (
137
- "Master 重启健康检查失败\n"
138
- f"原因:{reason}\n"
139
- "请尽快登录服务器排查(start.log / vibe.log)。"
65
+ "Master Restart health check failed\n"
66
+ f"reason:{reason}\n"
67
+ "Please log in to the server as soon as possible to troubleshoot (start.log / vibe.log)."
140
68
  )
141
69
 
142
70
 
143
71
  def _notify_admins(reason: str) -> None:
144
- """如果 master token 可用,则向管理员列表广播失败原因。"""
72
+ """Broadcast the failure reason to administrators if the master token exists."""
145
73
 
146
74
  master_token = os.environ.get("MASTER_BOT_TOKEN")
147
75
  if not master_token:
@@ -163,58 +91,30 @@ def _notify_admins(reason: str) -> None:
163
91
  continue
164
92
 
165
93
 
166
- def _ensure_worker(cfg: master.ProjectConfig) -> master.MasterManager:
167
- """启动指定项目 worker,并返回临时构造的 MasterManager。"""
168
-
169
- records = REPOSITORY.list_projects()
170
- configs = [master.ProjectConfig.from_dict(record.to_dict()) for record in records]
171
- state_store = master.StateStore(
172
- master.STATE_PATH, {item.project_slug: item for item in configs}
173
- )
174
- manager = master.MasterManager(configs, state_store=state_store)
175
-
176
- async def _run() -> None:
177
- """协程执行实际的 stop/start 流程。"""
178
- # 确保先停止旧实例(若存在)
179
- try:
180
- await manager.stop_worker(cfg)
181
- except Exception:
182
- pass
183
- await manager.run_worker(cfg)
184
-
185
- asyncio.run(_run())
186
- return manager
187
-
188
-
189
94
  def main() -> int:
190
- """命令行入口,执行 master 健康检查并返回退出码。"""
191
-
192
- parser = argparse.ArgumentParser(description="Master 启动后的健康检查")
193
- parser.add_argument("--project", default="hyphavibebotbackend", help="项目 slug 或 bot 名称")
194
- parser.add_argument("--master-log", default=str(DEFAULT_MASTER_LOG), help="master 日志路径")
195
- parser.add_argument("--master-timeout", type=float, default=DEFAULT_TIMEOUT_MASTER, help="master 日志等待超时时间 (秒)")
196
- parser.add_argument("--probe-timeout", type=float, default=DEFAULT_TIMEOUT_PROBE, help="Telegram 探针超时时间 (秒)")
95
+ """Command line entry point, only validates master readiness."""
96
+
97
+ parser = argparse.ArgumentParser(description="Master post-launch health check (master only)")
98
+ parser.add_argument("--master-log", default=str(DEFAULT_MASTER_LOG), help="Master log path")
99
+ parser.add_argument(
100
+ "--master-timeout",
101
+ type=float,
102
+ default=DEFAULT_TIMEOUT_MASTER,
103
+ help="Master log wait timeout (seconds)",
104
+ )
197
105
  args = parser.parse_args()
198
106
 
199
- project_id = master._sanitize_slug(args.project)
200
107
  master_log = Path(args.master_log)
201
108
 
202
109
  try:
203
- _wait_for_log_flag(master_log, "Master 已启动,监听管理员指令。", args.master_timeout)
204
- cfg = _load_project(project_id)
205
- manager = _ensure_worker(cfg)
206
- chat_id = _ensure_chat_id(cfg, manager)
207
- _send_probe(cfg.bot_token, chat_id, PROBE_TEXT, args.probe_timeout)
110
+ _wait_for_log_flag(master_log, MASTER_READY_MARKER, args.master_timeout)
208
111
  except Exception as exc:
209
112
  reason = str(exc)
210
113
  _notify_admins(reason)
211
- print(f"[healthcheck] 失败: {reason}", file=sys.stderr)
114
+ print(f"[healthcheck] fail: {reason}", file=sys.stderr)
212
115
  return 1
213
116
  else:
214
- print(
215
- "[healthcheck] 成功: master 已就绪,"
216
- f"worker={cfg.display_name} 启动完成,chat_id={chat_id},已发送探针消息"
217
- )
117
+ print("[healthcheck] success: master ready, worker checks skipped by configuration")
218
118
  return 0
219
119
 
220
120
 
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env bash
2
- # ClaudeCode 模型配置
2
+ # ClaudeCode Model configuration
3
3
 
4
- # 将工作目录转换为 Claude CLI 默认的 project key
5
- # Claude 官方实现是简单地把绝对路径中的斜杠替换成连字符,并保留大小写。
4
+ # Convert the working directory to Claude CLI default project key.
5
+ # Claude The official implementation simply replaces slashes in absolute paths with hyphens, and preserve case.
6
6
  claude_project_key_from_workdir() {
7
7
  local path="$1"
8
8
  if [[ -z "$path" ]]; then
@@ -20,7 +20,7 @@ claude_project_key_from_workdir() {
20
20
  model_configure() {
21
21
  MODEL_NAME="ClaudeCode"
22
22
  MODEL_WORKDIR="${CLAUDE_WORKDIR:-${MODEL_WORKDIR:-$ROOT_DIR}}"
23
- # 默认关闭文件快照,避免孤儿 CLI 持续写入 jsonl
23
+ # File snapshot is turned off by default, avoid orphans CLI Continuous writing jsonl
24
24
  CLAUDE_DISABLE_FILE_CHECKPOINTING="${CLAUDE_DISABLE_FILE_CHECKPOINTING:-1}"
25
25
  CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING="${CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING:-$CLAUDE_DISABLE_FILE_CHECKPOINTING}"
26
26
  export CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING
scripts/models/codex.sh CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # Codex 模型配置
2
+ # Codex Model configuration
3
3
 
4
4
  model_configure() {
5
5
  MODEL_NAME="codex"
scripts/models/common.sh CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
- # 公共工具:模型脚本/运行脚本/停止脚本共享
2
+ # Public Tools: Model Scripts/run script/Stop script sharing
3
3
 
4
- # 避免重复定义时覆盖
4
+ # Avoid overwriting when repeating definitions
5
5
  if [[ -n "${_MODEL_COMMON_LOADED:-}" ]]; then
6
6
  return
7
7
  fi
@@ -9,10 +9,29 @@ _MODEL_COMMON_LOADED=1
9
9
 
10
10
  COMMON_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
11
  ROOT_DIR="${ROOT_DIR:-$(cd "$COMMON_DIR/.." && pwd)}"
12
- LOG_ROOT="${LOG_ROOT:-$ROOT_DIR/logs}"
12
+ resolve_config_root() {
13
+ local raw=""
14
+ if [[ -n "${MASTER_CONFIG_ROOT:-}" ]]; then
15
+ raw="$MASTER_CONFIG_ROOT"
16
+ elif [[ -n "${VIBEGO_CONFIG_DIR:-}" ]]; then
17
+ raw="$VIBEGO_CONFIG_DIR"
18
+ elif [[ -n "${XDG_CONFIG_HOME:-}" ]]; then
19
+ raw="${XDG_CONFIG_HOME%/}/vibego"
20
+ else
21
+ raw="$HOME/.config/vibego"
22
+ fi
23
+ if [[ "$raw" == ~* ]]; then
24
+ printf '%s' "${raw/#\~/$HOME}"
25
+ else
26
+ printf '%s' "$raw"
27
+ fi
28
+ }
29
+
30
+ CONFIG_ROOT="${CONFIG_ROOT:-$(resolve_config_root)}"
31
+ LOG_ROOT="${LOG_ROOT:-$CONFIG_ROOT/logs}"
13
32
  TMUX_SESSION_PREFIX="${TMUX_SESSION_PREFIX:-vibe}"
14
33
 
15
- # 将任意路径/名称转换为 tmux/session 等安全的 slug
34
+ # Change any path/Name converted to tmux/session Wait for a safe slug
16
35
  sanitize_slug() {
17
36
  local input="$1"
18
37
  if [[ -z "$input" ]]; then
@@ -34,7 +53,7 @@ project_slug_from_workdir() {
34
53
  printf 'project'
35
54
  return
36
55
  fi
37
- # 将绝对路径改写为与 Claude 类似的 -Users-... 形式
56
+ # Rewrite the absolute path to something similar to Claude -Users-... form
38
57
  local replaced
39
58
  replaced=$(printf '%s' "$path" | sed 's#/#-#g')
40
59
  replaced="${replaced#-}"
@@ -87,4 +106,3 @@ find_latest_with_pattern() {
87
106
  done < <(find "$root" -type f -name "$pattern" -print0 2>/dev/null)
88
107
  [[ -n "$latest" ]] && printf '%s\n' "$latest"
89
108
  }
90
-
scripts/models/gemini.sh CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env bash
2
- # Gemini 模型占位配置(待实现)
2
+ # Gemini Model placeholder configuration (to be implemented)
3
3
 
4
4
  model_configure() {
5
5
  MODEL_NAME="gemini"
6
6
  MODEL_WORKDIR="${GEMINI_WORKDIR:-${MODEL_WORKDIR:-$ROOT_DIR}}"
7
- MODEL_CMD="${GEMINI_CMD:-echo 'gemini CLI 尚未配置'; sleep 1}"
7
+ MODEL_CMD="${GEMINI_CMD:-echo 'gemini CLI Not configured yet'; sleep 1}"
8
8
  MODEL_SESSION_ROOT="${GEMINI_SESSION_ROOT:-$LOG_ROOT/gemini-placeholder}"
9
9
  MODEL_SESSION_GLOB="${GEMINI_SESSION_GLOB:-*.jsonl}"
10
10
  MODEL_POINTER_BASENAME="${MODEL_POINTER_BASENAME:-current_session.txt}"
scripts/publish.sh CHANGED
@@ -1,33 +1,33 @@
1
1
  #!/usr/bin/env bash
2
- # VibeGo 完整发布脚本
3
- # 使用 keyring 进行 PyPI 认证,无需手动输入 token
2
+ # VibeGo Full release script
3
+ # Use keyring for PyPI authentication without manually entering tokens
4
4
  #
5
- # 前置条件:
6
- # 1. 已安装 keyring: pip install keyring
7
- # 2. 已存储 PyPI token keyring:
5
+ # Prerequisites:
6
+ # 1. keyring installed: pip install keyring
7
+ # 2. PyPI token stored in keyring:
8
8
  # python3.11 -c "import keyring; keyring.set_password('https://upload.pypi.org/legacy/', '__token__', 'your-token')"
9
9
  #
10
- # 使用方式:
11
- # ./scripts/publish.sh # 发布 patch 版本(默认)
12
- # ./scripts/publish.sh minor # 发布 minor 版本
13
- # ./scripts/publish.sh major # 发布 major 版本
10
+ # How to use:
11
+ # ./scripts/publish.sh # Publish patch version (default)
12
+ # ./scripts/publish.sh minor # Release minor version
13
+ # ./scripts/publish.sh major # Release major version
14
14
 
15
15
  set -e
16
16
 
17
- # 颜色定义
17
+ # color definition
18
18
  RED='\033[0;31m'
19
19
  GREEN='\033[0;32m'
20
20
  YELLOW='\033[1;33m'
21
21
  BLUE='\033[0;34m'
22
22
  NC='\033[0m' # No Color
23
23
 
24
- # 打印带颜色的消息
24
+ # Print colored messages
25
25
  print_info() {
26
26
  echo -e "${BLUE}ℹ️ $1${NC}"
27
27
  }
28
28
 
29
29
  print_success() {
30
- echo -e "${GREEN} $1${NC}"
30
+ echo -e "${GREEN}OK: $1${NC}"
31
31
  }
32
32
 
33
33
  print_warning() {
@@ -35,92 +35,92 @@ print_warning() {
35
35
  }
36
36
 
37
37
  print_error() {
38
- echo -e "${RED} $1${NC}"
38
+ echo -e "${RED}ERROR: $1${NC}"
39
39
  }
40
40
 
41
- # 项目根目录
41
+ # Project root directory
42
42
  PROJECT_ROOT="/Users/david/hypha/tools/vibeBot"
43
43
  cd "$PROJECT_ROOT"
44
44
 
45
- print_info "开始 VibeGo 发布流程..."
45
+ print_info "Start the VibeGo publishing process..."
46
46
  echo ""
47
47
 
48
- # 步骤 1: 检查 keyring 中是否存储了 PyPI token
49
- print_info "检查 keyring 配置..."
48
+ # Step 1: Check if PyPI token is stored in keyring
49
+ print_info "Check keyring configuration..."
50
50
  if ! python3.11 -c "import keyring; token = keyring.get_password('https://upload.pypi.org/legacy/', '__token__'); exit(0 if token else 1)" 2>/dev/null; then
51
- print_error "未在 keyring 中找到 PyPI token"
51
+ print_error "PyPI token not found in keyring"
52
52
  echo ""
53
- echo "请先执行以下命令存储 token"
53
+ echo "Please execute the following command to store the token first:"
54
54
  echo " python3.11 -c \"import keyring; keyring.set_password('https://upload.pypi.org/legacy/', '__token__', 'your-pypi-token')\""
55
55
  echo ""
56
56
  exit 1
57
57
  fi
58
- print_success "Keyring 配置正确"
58
+ print_success "Keyring Configured correctly"
59
59
  echo ""
60
60
 
61
- # 步骤 2: 创建/激活虚拟环境
62
- print_info "创建构建虚拟环境..."
61
+ # Step 2: create/Activate virtual environment
62
+ print_info "createBuild a virtual environment..."
63
63
  python3.11 -m venv ~/.venvs/vibego-build
64
64
  source ~/.venvs/vibego-build/bin/activate
65
- print_success "虚拟环境已激活"
65
+ print_success "Virtual environment is activated"
66
66
  echo ""
67
67
 
68
- # 步骤 3: 升级 pip 和安装构建工具
69
- print_info "安装构建依赖..."
68
+ # Step 3: Upgrade pip and install build tools
69
+ print_info "Install build dependencies..."
70
70
  pip install --upgrade pip build twine keyring > /dev/null 2>&1
71
- print_success "构建依赖已安装"
71
+ print_success "Build dependencies installed"
72
72
  echo ""
73
73
 
74
- # 步骤 4: 清理旧的构建产物
75
- print_info "清理旧的构建产物..."
74
+ # Step 4: Clean up old build artifacts
75
+ print_info "Clean up old build artifacts..."
76
76
  rm -rf "$PROJECT_ROOT/dist"
77
- print_success "构建产物已清理"
77
+ print_success "Build artifacts cleaned"
78
78
  echo ""
79
79
 
80
- # 步骤 5: 递增版本号
81
- VERSION_TYPE="${1:-patch}" # 默认为 patch
82
- print_info "递增版本号(类型:$VERSION_TYPE)..."
80
+ # Step 5: Increment version number
81
+ VERSION_TYPE="${1:-patch}" # Default is patch
82
+ print_info "Increment version number (type:$VERSION_TYPE)..."
83
83
  ./scripts/bump_version.sh "$VERSION_TYPE"
84
84
  echo ""
85
85
 
86
- # 步骤 6: 构建分发包
87
- print_info "构建 Python 分发包..."
86
+ # Step 6: Build distribution package
87
+ print_info "Build the Python distribution..."
88
88
  python3.11 -m build
89
- print_success "分发包构建完成"
89
+ print_success "The distribution package is built"
90
90
  echo ""
91
91
 
92
- # 步骤 7: 上传到 PyPI(使用 keyring 自动认证)
93
- print_info "上传到 PyPI(使用 keyring 认证)..."
92
+ # Step 7: Upload to PyPI (automatic authentication using keyring)
93
+ print_info "Upload to PyPI (using keyring authentication)..."
94
94
  twine upload dist/*
95
- print_success "已成功上传到 PyPI"
95
+ print_success "Successfully uploaded to PyPI"
96
96
  echo ""
97
97
 
98
- # 步骤 8: 清理并重装 pipx 中的 vibego
99
- print_info "更新本地 pipx 安装..."
98
+ # Step 8: Clean and reinstall vibego in pipx
99
+ print_info "Update local pipx installation..."
100
100
  rm -rf ~/.cache/pipx
101
101
  rm -rf ~/.local/pipx/venvs/vibego
102
102
  pipx install --python python3.11 vibego
103
103
  pipx upgrade vibego
104
- print_success "本地 vibego 已更新"
104
+ print_success "local vibego updated"
105
105
  echo ""
106
106
 
107
- # 步骤 9: 重启 vibego 服务
108
- print_info "重启 vibego 服务..."
109
- vibego stop || true # 忽略停止失败的错误
107
+ # Step 9: Restart vibego service
108
+ print_info "Restart vibego service..."
109
+ vibego stop || true # Ignore stop failure errors
110
110
  sleep 2
111
111
  vibego start
112
- print_success "vibego 服务已重启"
112
+ print_success "vibego Service has been restarted"
113
113
  echo ""
114
114
 
115
- # 完成
115
+ # Complete
116
116
  print_success "========================================="
117
- print_success "🎉 发布流程完成!"
117
+ print_success "🎉 The publishing process is complete!"
118
118
  print_success "========================================="
119
119
  echo ""
120
- print_info "后续步骤:"
121
- echo " 1. 推送 git 提交和标签:"
120
+ print_info "Next steps:"
121
+ echo " 1. Push git commits and tags:"
122
122
  echo " git push && git push --tags"
123
123
  echo ""
124
- echo " 2. 验证 PyPI 页面:"
124
+ echo " 2. Verify PyPI page:"
125
125
  echo " https://pypi.org/project/vibego/"
126
126
  echo ""
scripts/requirements.txt CHANGED
@@ -2,3 +2,4 @@ aiogram>=3.0.0,<4.0.0
2
2
  aiohttp-socks>=0.10.0
3
3
  aiosqlite>=0.19.0
4
4
  markdown-it-py>=3.0.0,<4.0.0
5
+ watchdog>=4.0.0
scripts/run_bot.sh CHANGED
@@ -1,11 +1,32 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
+ # shellcheck disable=SC2155
4
5
  SOURCE_ROOT="${VIBEGO_PACKAGE_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
5
- RUNTIME_ROOT="${VIBEGO_RUNTIME_ROOT:-$SOURCE_ROOT}"
6
+
7
+ resolve_config_root() {
8
+ local raw=""
9
+ if [[ -n "${MASTER_CONFIG_ROOT:-}" ]]; then
10
+ raw="$MASTER_CONFIG_ROOT"
11
+ elif [[ -n "${VIBEGO_CONFIG_DIR:-}" ]]; then
12
+ raw="$VIBEGO_CONFIG_DIR"
13
+ elif [[ -n "${XDG_CONFIG_HOME:-}" ]]; then
14
+ raw="${XDG_CONFIG_HOME%/}/vibego"
15
+ else
16
+ raw="$HOME/.config/vibego"
17
+ fi
18
+ if [[ "$raw" == ~* ]]; then
19
+ printf '%s' "${raw/#\~/$HOME}"
20
+ else
21
+ printf '%s' "$raw"
22
+ fi
23
+ }
24
+
25
+ CONFIG_ROOT="$(resolve_config_root)"
26
+ RUNTIME_ROOT="${VIBEGO_RUNTIME_ROOT:-$CONFIG_ROOT/runtime}"
6
27
  VENV_DIR="$RUNTIME_ROOT/.venv"
7
28
  MODELS_DIR="$SOURCE_ROOT/scripts/models"
8
- LOG_ROOT="${LOG_ROOT:-$RUNTIME_ROOT/logs}"
29
+ LOG_ROOT="${LOG_ROOT:-$CONFIG_ROOT/logs}"
9
30
  DEFAULT_LOG_FILE="$LOG_ROOT/run_bot.log"
10
31
  LOG_FILE="${LOG_FILE:-$DEFAULT_LOG_FILE}"
11
32
  MODEL_DEFAULT="${MODEL_DEFAULT:-codex}"
@@ -13,11 +34,11 @@ PROJECT_DEFAULT="${PROJECT_NAME:-}"
13
34
 
14
35
  usage() {
15
36
  cat <<USAGE
16
- 用法:${0##*/} [--model 名称] [--project 名称] [--foreground] [--no-stop]
17
- --model 启动指定模型 (codex|claudecode|gemini),默认: $MODEL_DEFAULT
18
- --project 指定项目别名,用于日志/会话区分
19
- --foreground 在前台运行(调试用),默认后台
20
- --no-stop 启动前不执行 stop_bot.sh(默认会先停旧实例)
37
+ usage:${0##*/} [--model name] [--project name] [--foreground] [--no-stop]
38
+ --model Start the specified model (codex|claudecode|gemini), default: $MODEL_DEFAULT
39
+ --project Specify project alias for logging/Conversation distinction
40
+ --foreground Run in foreground(for debugging), defaultBackstage
41
+ --no-stop Not executed before startup stop_bot.sh(defaultOld instances will be stopped first)
21
42
  USAGE
22
43
  }
23
44
 
@@ -39,7 +60,7 @@ while [[ $# -gt 0 ]]; do
39
60
  -h|--help)
40
61
  usage; exit 0 ;;
41
62
  *)
42
- echo "未知参数: $1" >&2
63
+ echo "unknown parameters: $1" >&2
43
64
  usage
44
65
  exit 1 ;;
45
66
  esac
@@ -47,14 +68,14 @@ done
47
68
 
48
69
  MODEL_SCRIPT="$MODELS_DIR/$MODEL.sh"
49
70
  if [[ ! -f "$MODEL_SCRIPT" ]]; then
50
- echo "[run-bot] 不支持的模型: $MODEL" >&2
71
+ echo "[run-bot] Unsupported model: $MODEL" >&2
51
72
  exit 1
52
73
  fi
53
74
 
54
- # 加载公共函数 + 模型配置
75
+ # Load public functions + Model configuration
55
76
  # shellcheck disable=SC1090
56
77
  source "$MODELS_DIR/common.sh"
57
- # 允许模型脚本重用环境变量
78
+ # Allow model scripts to reuse environment variables
58
79
  MODEL_WORKDIR="${MODEL_WORKDIR:-}"
59
80
  # shellcheck disable=SC1090
60
81
  source "$MODEL_SCRIPT"
@@ -69,6 +90,7 @@ LOG_DIR="$(log_dir_for "$MODEL" "$PROJECT_NAME")"
69
90
  MODEL_LOG="$LOG_DIR/model.log"
70
91
  RUN_LOG="$LOG_DIR/run_bot.log"
71
92
  POINTER_FILE="$LOG_DIR/${MODEL_POINTER_BASENAME:-current_session.txt}"
93
+ LOCK_FILE="${SESSION_LOCK_FILE:-$LOG_DIR/${MODEL_POINTER_LOCK_BASENAME:-session_lock.json}}"
72
94
  TMUX_SESSION="$(tmux_session_for "$PROJECT_NAME")"
73
95
 
74
96
  expand_model_workdir() {
@@ -83,24 +105,24 @@ expand_model_workdir() {
83
105
  MODEL_WORKDIR="$(expand_model_workdir "$MODEL_WORKDIR")"
84
106
 
85
107
  if [[ -z "$MODEL_WORKDIR" ]]; then
86
- echo "[run-bot] 配置缺少 MODEL_WORKDIR,请检查 config/projects.json" >&2
108
+ echo "[run-bot] Configuration is missing MODEL_WORKDIR, Please check config/projects.json" >&2
87
109
  exit 1
88
110
  fi
89
111
 
90
112
  if [[ ! -d "$MODEL_WORKDIR" ]]; then
91
- echo "[run-bot] 工作目录不存在: $MODEL_WORKDIR" >&2
113
+ echo "[run-bot] Working directory does not exist: $MODEL_WORKDIR" >&2
92
114
  exit 1
93
115
  fi
94
116
 
95
117
  if ! command -v tmux >/dev/null 2>&1; then
96
- echo "[run-bot] 未检测到 tmux,可通过 'brew install tmux' 安装" >&2
118
+ echo "[run-bot] tmux not detected, pass 'brew install tmux' Install" >&2
97
119
  exit 1
98
120
  fi
99
121
 
100
122
  if [[ -n "$MODEL_CMD" ]]; then
101
123
  IFS=' ' read -r MODEL_CMD_BIN _ <<<"$MODEL_CMD"
102
124
  if [[ -n "$MODEL_CMD_BIN" ]] && ! command -v "$MODEL_CMD_BIN" >/dev/null 2>&1; then
103
- echo "[run-bot] 未检测到模型命令: $MODEL_CMD_BIN" >&2
125
+ echo "[run-bot] Model command not detected: $MODEL_CMD_BIN" >&2
104
126
  exit 1
105
127
  fi
106
128
  fi
@@ -112,7 +134,7 @@ if (( FOREGROUND == 0 )); then
112
134
  CMD=("$0" --model "$MODEL" --project "$PROJECT_NAME" --foreground)
113
135
  (( NO_STOP )) && CMD+=(--no-stop)
114
136
  nohup "${CMD[@]}" >>"$RUN_LOG" 2>&1 &
115
- echo "[run-bot] 后台启动 (model=$MODEL project=$PROJECT_NAME) 日志: $RUN_LOG"
137
+ echo "[run-bot] Start in background (model=$MODEL project=$PROJECT_NAME) log: $RUN_LOG"
116
138
  exit 0
117
139
  fi
118
140
 
@@ -129,7 +151,7 @@ fi
129
151
  source "$VENV_DIR/bin/activate"
130
152
  REQUIREMENTS_PATH="${VIBEGO_REQUIREMENTS_PATH:-$SOURCE_ROOT/scripts/requirements.txt}"
131
153
  if [[ ! -f "$REQUIREMENTS_PATH" ]]; then
132
- echo "[run-bot] 未找到依赖清单: $REQUIREMENTS_PATH" >&2
154
+ echo "[run-bot] Dependency list not found: $REQUIREMENTS_PATH" >&2
133
155
  exit 1
134
156
  fi
135
157
  if [[ ! -f "$VENV_DIR/.requirements.installed" ]] || [[ "$REQUIREMENTS_PATH" -nt "$VENV_DIR/.requirements.installed" ]]; then
@@ -146,6 +168,8 @@ export MODEL_SESSION_ROOT
146
168
  export MODEL_SESSION_GLOB
147
169
  export SESSION_POINTER_FILE="$POINTER_FILE"
148
170
  export CODEX_SESSION_FILE_PATH="$POINTER_FILE"
171
+ export SESSION_LOCK_FILE="$LOCK_FILE"
172
+ export SESSION_LOCK_FILE_PATH="$LOCK_FILE"
149
173
  export TMUX_SESSION="$TMUX_SESSION"
150
174
  export TMUX_LOG="$MODEL_LOG"
151
175
  export PROJECT_NAME="$PROJECT_NAME"