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.
- bot.py +1346 -1136
- logging_setup.py +25 -18
- master.py +812 -506
- project_repository.py +42 -40
- scripts/__init__.py +1 -2
- scripts/bump_version.sh +57 -55
- scripts/log_writer.py +19 -16
- scripts/master_healthcheck.py +38 -138
- scripts/models/claudecode.sh +4 -4
- scripts/models/codex.sh +1 -1
- scripts/models/common.sh +24 -6
- scripts/models/gemini.sh +2 -2
- scripts/publish.sh +50 -50
- scripts/requirements.txt +1 -0
- scripts/run_bot.sh +41 -17
- scripts/session_pointer_watch.py +265 -0
- scripts/start.sh +147 -120
- scripts/start_tmux_codex.sh +33 -8
- scripts/stop_all.sh +21 -21
- scripts/stop_bot.sh +31 -10
- scripts/test_deps_check.sh +32 -28
- tasks/__init__.py +1 -1
- tasks/commands.py +4 -4
- tasks/constants.py +1 -1
- tasks/fsm.py +9 -9
- tasks/models.py +7 -7
- tasks/service.py +56 -101
- vibego-1.0.10.dist-info/METADATA +226 -0
- {vibego-0.2.58.dist-info → vibego-1.0.10.dist-info}/RECORD +38 -36
- vibego-1.0.10.dist-info/licenses/LICENSE +201 -0
- vibego_cli/__init__.py +5 -4
- vibego_cli/__main__.py +1 -2
- vibego_cli/config.py +9 -9
- vibego_cli/deps.py +8 -9
- vibego_cli/main.py +63 -63
- vibego-0.2.58.dist-info/METADATA +0 -197
- {vibego-0.2.58.dist-info → vibego-1.0.10.dist-info}/WHEEL +0 -0
- {vibego-0.2.58.dist-info → vibego-1.0.10.dist-info}/entry_points.txt +0 -0
- {vibego-0.2.58.dist-info → vibego-1.0.10.dist-info}/top_level.txt +0 -0
scripts/master_healthcheck.py
CHANGED
|
@@ -1,147 +1,75 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Master
|
|
2
|
+
"""Master post-startup health check.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
26
|
+
|
|
36
27
|
DEFAULT_MASTER_LOG = master.LOG_ROOT_PATH / "vibe.log"
|
|
37
28
|
DEFAULT_TIMEOUT_MASTER = 60.0
|
|
38
|
-
|
|
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
|
-
|
|
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
|
|
63
|
-
position =
|
|
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
|
-
|
|
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"
|
|
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
|
|
138
|
-
f"
|
|
139
|
-
"
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
191
|
-
|
|
192
|
-
parser = argparse.ArgumentParser(description="Master
|
|
193
|
-
parser.add_argument("--
|
|
194
|
-
parser.add_argument(
|
|
195
|
-
|
|
196
|
-
|
|
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,
|
|
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]
|
|
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
|
|
scripts/models/claudecode.sh
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# ClaudeCode
|
|
2
|
+
# ClaudeCode Model configuration
|
|
3
3
|
|
|
4
|
-
#
|
|
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
|
-
#
|
|
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
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
|
-
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
|
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
|
-
#
|
|
2
|
+
# VibeGo Full release script
|
|
3
|
+
# Use keyring for PyPI authentication without manually entering tokens
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
# 1.
|
|
7
|
-
# 2.
|
|
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 #
|
|
12
|
-
# ./scripts/publish.sh minor #
|
|
13
|
-
# ./scripts/publish.sh 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}
|
|
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}
|
|
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 "
|
|
45
|
+
print_info "Start the VibeGo publishing process..."
|
|
46
46
|
echo ""
|
|
47
47
|
|
|
48
|
-
#
|
|
49
|
-
print_info "
|
|
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 "
|
|
51
|
+
print_error "PyPI token not found in keyring"
|
|
52
52
|
echo ""
|
|
53
|
-
echo "
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
81
|
-
VERSION_TYPE="${1:-patch}" #
|
|
82
|
-
print_info "
|
|
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
|
-
#
|
|
87
|
-
print_info "
|
|
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
|
-
#
|
|
93
|
-
print_info "
|
|
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 "
|
|
95
|
+
print_success "Successfully uploaded to PyPI"
|
|
96
96
|
echo ""
|
|
97
97
|
|
|
98
|
-
#
|
|
99
|
-
print_info "
|
|
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 "
|
|
104
|
+
print_success "local vibego updated"
|
|
105
105
|
echo ""
|
|
106
106
|
|
|
107
|
-
#
|
|
108
|
-
print_info "
|
|
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.
|
|
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.
|
|
124
|
+
echo " 2. Verify PyPI page:"
|
|
125
125
|
echo " https://pypi.org/project/vibego/"
|
|
126
126
|
echo ""
|
scripts/requirements.txt
CHANGED
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
|
-
|
|
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:-$
|
|
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
|
-
|
|
17
|
-
--model
|
|
18
|
-
--project
|
|
19
|
-
--foreground
|
|
20
|
-
--no-stop
|
|
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 "
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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"
|