feat(monitor): 抖音 Web 接口客户端 —— 绕开爬虫子进程,直接发 HTTP
移植自 mac-agent-os 的 mediacrawler_adapter:不起子进程、不开页面,用浏览器里那份
登录态直接调抖音 Web 接口。产物键名照抄 store/douyin,所以 ingest 那条链路一个字不用改。
**目前能用的(真环境实测,非推断)**:
profile/other : 200, 7075 字节 —— 博主主页指标(粉丝/获赞/作品数/昵称)
aweme/detail : 200, 45425 字节 —— 单条作品详情(含点赞/评论/收藏/分享)
**目前不能用的:作品列表 `aweme/post`。** 两个互相独立的原因:
1. 这个接口被抖音单独升级成了真校验:不带 x-tt-argus 回 403「Uifid Not Found」,
带上 dummy 值回 200 + **空 body**。也就是说「头在不在」骗得过,「真校验」过不了。
同一套头打 profile/other 和 aweme/detail 都是通的 —— 抖音是挑着接口加保护的,
挑中的恰好是「批量拉作品列表」这个最敏感的动作。
2. 改走页面截获也不行:CDP 浏览器打开博主主页会落到「验证码中间页」(当天大量探测的
代价,过几小时要重测)。
所以现在的边界是:**已知作品的指标刷新能做,自动发现新作品做不了**。
**排查中控住变量后得到的两条事实**(都写进注释了):
· `Accept` / `Accept-Language` / `Referer` 才是主页接口能返回真数据的原因 —— 只有
UA+client hints+Cookie 时是 200 但仅 121 字节的空壳,补上这三个头变 7074 字节。
(我先前猜的 sec-ch-ua 不是关键。)
· 因此 UA 与 client hints 必须**成套地取自同一个浏览器**,所以 BrowserIdentity 一次
从 CDP 取齐 cookie + UA + hints,而不是各自写死。
「200 + 空 body 必须当场报错」也是刻意写死的:放过去它会在下游变成「这个博主没作品」,
把一次失败伪装成一条正常结果 —— 爬虫那条路正是这么栽的,还被翻译成「账号被封」。
测试 +11:cookie 解析、请求头成套性(含 uifid 缺失/回退)、产物键名与 store 对齐、
以及 _get 的三条失败路径(空 body / 403 带网关原话 / 正常返回)。
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
# AgentOS 新机器部署指南
|
||||
|
||||
> 版本: v2.0 | 最后更新: 2026-06-15
|
||||
> 本文件供 AI 智能体读取后自动执行部署。
|
||||
|
||||
---
|
||||
|
||||
## 架构说明
|
||||
|
||||
### 与旧版部署的关系
|
||||
|
||||
| 旧版 (v1.0) | 新版 (v2.0) | 关系 |
|
||||
|:------------|:------------|:------|
|
||||
| `init.sh` | 保留,增加代码签名检测 | 扩展 |
|
||||
| `deploy.sh` | 保留,增加步骤 | 扩展 |
|
||||
| `setup_env.sh` | 保留 | 不变 |
|
||||
| — | `requirements.txt` | **新增**:版本锁定 |
|
||||
| — | Python 重签步骤 | **新增**:解决代码签名问题 |
|
||||
|
||||
**旧版部署方案仍然可用**,v2.0 是在其基础上增加了 2 个新步骤:
|
||||
1. 检测 WorkBuddy 管理的 Python 是否有代码签名问题 → 自动修复
|
||||
2. 从 `requirements.txt` 安装锁定版本的核心依赖
|
||||
|
||||
### 联邦系统 CLI
|
||||
|
||||
底层 CLI 是 `mc`(`05_tools/07_matrix/mc`),它统一管理养号、采集、远程执行等功能。未来 AgentOS 系统级的 CLI 可以在此基础上扩展,但目前 `mc` 已经覆盖了所有核心操作。
|
||||
|
||||
---
|
||||
|
||||
## 一、前置条件
|
||||
|
||||
1. ✅ 本机已安装 **WorkBuddy**(从官网下载)
|
||||
2. ✅ 已拥有 Gitee 仓库的 SSH 访问权限
|
||||
3. ✅ macOS 版本 ≥ 14.0
|
||||
4. ✅ **Tailscale** 已安装并登录(跨局域网通信用)
|
||||
5. ✅ **远程登录**已开启:系统设置 → 通用 → 共享 → 远程登录
|
||||
|
||||
---
|
||||
|
||||
## 二、部署步骤(AI 请按顺序执行)
|
||||
|
||||
### 步骤 1:克隆仓库
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
git clone [email protected]:babycalf/mac-agent-os.git workbuddy-agent-os/agent-sync
|
||||
```
|
||||
|
||||
### 步骤 2:运行部署脚本
|
||||
|
||||
```bash
|
||||
cd ~/workbuddy-agent-os/agent-sync
|
||||
bash 00_bootstrap/deploy.sh
|
||||
```
|
||||
|
||||
`deploy.sh` 会自动:
|
||||
- 设置环境变量 `AGENT_SYNC` / `AGENT_LOCAL` 并写入 `.zshrc`
|
||||
- 创建 `agent-local/` 目录结构
|
||||
- 创建 `agent-local/config.yaml` 默认配置
|
||||
- 检测 Tailscale 状态
|
||||
|
||||
### 步骤 3:修复 Python 代码签名(新增)
|
||||
|
||||
WorkBuddy 管理的 Python 二进制被腾讯签名,带有 Team ID,导致 orjson 等 `.so` 文件无法加载。需要移除签名:
|
||||
|
||||
```bash
|
||||
PYTHON_BIN="$HOME/.workbuddy/binaries/python/versions/3.13.12/bin/python3"
|
||||
codesign -f -s - "$PYTHON_BIN"
|
||||
```
|
||||
|
||||
验证修复:
|
||||
```bash
|
||||
"$PYTHON_BIN" -c "import orjson; print(orjson.__version__)"
|
||||
```
|
||||
|
||||
预期输出:`3.11.9`
|
||||
|
||||
### 步骤 4:安装核心依赖(新增)
|
||||
|
||||
```bash
|
||||
cd ~/workbuddy-agent-os/agent-sync
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 步骤 5:验证环境
|
||||
|
||||
```bash
|
||||
bash 00_bootstrap/fleet_reconcile.sh
|
||||
```
|
||||
|
||||
所有检查项应为绿色 ✅。如果红色 ❌,请根据提示修复。
|
||||
|
||||
### 步骤 6:启动 Dashboard
|
||||
|
||||
```bash
|
||||
cd ~/workbuddy-agent-os/agent-sync/05_tools/10_dashboard
|
||||
nohup python3 run.py 9988 > /dev/null 2>&1 &
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、新机器加入联邦集群
|
||||
|
||||
上述步骤完成后,通知主控机器(chengzigedeAir)的管理员,将新机器信息加入 `ORACLE.yaml`:
|
||||
|
||||
```yaml
|
||||
machines:
|
||||
新机器名:
|
||||
hostname: 新机器名
|
||||
tailscale_host: 新机器tailscale名
|
||||
tailscale_ip: 100.x.x.x
|
||||
ssh_user: 用户名
|
||||
uid_prefix: 新机器的 uid 前8位
|
||||
role: worker
|
||||
os: macOS arm64
|
||||
```
|
||||
|
||||
然后在本机执行同步:
|
||||
```bash
|
||||
bash 00_bootstrap/fleet_sync.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、已知问题与处理
|
||||
|
||||
### 1. 登录进程卡死
|
||||
|
||||
`mc account login` 命令在 Cookie 过期或需要扫码时,会打开浏览器等待用户操作,不会自动超时退出。如果调度器连续 spawn 多个登录命令且无人操作,会积累大量僵尸进程。
|
||||
|
||||
**预防**: 启动养号前先验证账号登录状态:
|
||||
```bash
|
||||
python3 -m mc status all | grep -i "login"
|
||||
```
|
||||
|
||||
**处理**: 如果已累积僵尸进程,手动 kill:
|
||||
```bash
|
||||
pkill -f "mc account login"
|
||||
```
|
||||
|
||||
### 2. Python 代码签名问题复发
|
||||
|
||||
如果 WorkBuddy 升级后重新安装了 Python,代码签名可能会恢复。重新执行步骤 3 即可。
|
||||
|
||||
### 3. 包版本不一致
|
||||
|
||||
三台机器从 `requirements.txt` 安装即可对齐:
|
||||
```bash
|
||||
pip install -r requirements.txt --force-reinstall
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、完整部署流程(速查)
|
||||
|
||||
```bash
|
||||
# 新机器部署(6步,约10分钟)
|
||||
git clone [email protected]:babycalf/mac-agent-os.git ~/workbuddy-agent-os/agent-sync
|
||||
cd ~/workbuddy-agent-os/agent-sync
|
||||
bash 00_bootstrap/deploy.sh
|
||||
source ~/.zshrc
|
||||
codesign -f -s - $HOME/.workbuddy/binaries/python/versions/3.13.12/bin/python3
|
||||
pip install -r requirements.txt
|
||||
bash 00_bootstrap/fleet_reconcile.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、联邦常用操作
|
||||
|
||||
```bash
|
||||
# 一键同步所有机器
|
||||
bash 00_bootstrap/fleet_sync.sh
|
||||
|
||||
# 单机对账检查
|
||||
bash 00_bootstrap/fleet_reconcile.sh
|
||||
|
||||
# 远程执行命令
|
||||
python3 -m mc remote exec 机器名 "命令" --via ssh
|
||||
|
||||
# 查看所有机器状态
|
||||
python3 -m mc remote status
|
||||
```
|
||||
@@ -0,0 +1,160 @@
|
||||
#!/bin/bash
|
||||
# ============================================================
|
||||
# AgentOS 核心配置部署脚本 v2.0
|
||||
# 用途:将 01_core/ 下的配置文件安全部署到 ~/.workbuddy/
|
||||
# 带版本检查 + 多机角色预设
|
||||
# 使用:cd ~/workbuddy-agent-os/agent-sync/00_bootstrap && bash apply-config.sh
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---------- 颜色定义 ----------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
err() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
|
||||
header(){ echo -e "${CYAN}━━━ $1 ━━━${NC}"; }
|
||||
|
||||
# ---------- 定位路径 ----------
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
AGENT_OS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
CORE_DIR="$AGENT_OS_ROOT/01_core"
|
||||
WORKBUDDY_DIR="$HOME/.workbuddy"
|
||||
|
||||
# ---------- 版本追踪 ----------
|
||||
VERSION_FILE="$WORKBUDDY_DIR/.config-version.json"
|
||||
CURRENT_VERSION=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# 多机角色预设(当前统一版本,后续可按角色分配)
|
||||
# 角色: unified | main-node | submit-node | media-node
|
||||
HOST_ROLE="${HOST_ROLE:-unified}"
|
||||
|
||||
info "核心配置目录: $CORE_DIR"
|
||||
info "WorkBuddy 目录: $WORKBUDDY_DIR"
|
||||
info "本机角色: $HOST_ROLE"
|
||||
|
||||
# ---------- 备份旧配置 ----------
|
||||
BACKUP_DIR="$WORKBUDDY_DIR/backup_$(date +%Y%m%d_%H%M%S)"
|
||||
CONFIG_FILES=("SOUL.md" "IDENTITY.md" "USER.md" "mcp.json")
|
||||
|
||||
NEED_BACKUP=false
|
||||
for f in "${CONFIG_FILES[@]}"; do
|
||||
if [ -f "$WORKBUDDY_DIR/$f" ]; then
|
||||
NEED_BACKUP=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$NEED_BACKUP" = true ]; then
|
||||
info "检测到已有配置文件,创建备份..."
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
for f in "${CONFIG_FILES[@]}"; do
|
||||
if [ -f "$WORKBUDDY_DIR/$f" ]; then
|
||||
cp "$WORKBUDDY_DIR/$f" "$BACKUP_DIR/"
|
||||
info "已备份: $f → $BACKUP_DIR/$f"
|
||||
fi
|
||||
done
|
||||
ok "旧配置已备份到: $BACKUP_DIR"
|
||||
fi
|
||||
|
||||
# ---------- 确保 .workbuddy 目录存在 ----------
|
||||
mkdir -p "$WORKBUDDY_DIR"
|
||||
|
||||
# ---------- 部署配置文件 ----------
|
||||
info "部署配置文件到 ~/.workbuddy/ ..."
|
||||
|
||||
# SOUL.md
|
||||
if [ -f "$CORE_DIR/SOUL.md" ]; then
|
||||
cp "$CORE_DIR/SOUL.md" "$WORKBUDDY_DIR/SOUL.md"
|
||||
ok "SOUL.md 已部署"
|
||||
fi
|
||||
|
||||
# IDENTITY.md
|
||||
if [ -f "$CORE_DIR/IDENTITY.md" ]; then
|
||||
cp "$CORE_DIR/IDENTITY.md" "$WORKBUDDY_DIR/IDENTITY.md"
|
||||
ok "IDENTITY.md 已部署"
|
||||
fi
|
||||
|
||||
# CONSTITUTION.md(架构宪法,按需加载)
|
||||
if [ -f "$AGENT_OS_ROOT/CONSTITUTION.md" ]; then
|
||||
cp "$AGENT_OS_ROOT/CONSTITUTION.md" "$WORKBUDDY_DIR/CONSTITUTION.md"
|
||||
ok "CONSTITUTION.md 已部署(架构宪法,WorkBuddy AI 按需加载)"
|
||||
fi
|
||||
|
||||
# USER.md
|
||||
if [ -f "$CORE_DIR/USER.md" ]; then
|
||||
cp "$CORE_DIR/USER.md" "$WORKBUDDY_DIR/USER.md"
|
||||
ok "USER.md 已部署"
|
||||
fi
|
||||
|
||||
# mcp.json(合并而非覆盖)
|
||||
if [ -f "$CORE_DIR/mcp.json" ]; then
|
||||
if [ -f "$WORKBUDDY_DIR/mcp.json" ]; then
|
||||
# 已存在 mcp.json,提示用户手动合并
|
||||
warn "$WORKBUDDY_DIR/mcp.json 已存在"
|
||||
warn "请手动合并 $CORE_DIR/mcp.json 中的配置到现有文件"
|
||||
info "参考命令: diff $CORE_DIR/mcp.json $WORKBUDDY_DIR/mcp.json"
|
||||
else
|
||||
cp "$CORE_DIR/mcp.json" "$WORKBUDDY_DIR/mcp.json"
|
||||
ok "mcp.json 已部署"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------- 版本追踪 ----------
|
||||
header "版本信息"
|
||||
|
||||
# 写入部署版本记录
|
||||
cat > "$VERSION_FILE" << VERSIONEOF
|
||||
{
|
||||
"deployed_at": "$CURRENT_VERSION",
|
||||
"host_role": "$HOST_ROLE",
|
||||
"files": {
|
||||
"SOUL.md": "$(md5 -q "$WORKBUDDY_DIR/SOUL.md" 2>/dev/null || echo 'unknown')",
|
||||
"IDENTITY.md": "$(md5 -q "$WORKBUDDY_DIR/IDENTITY.md" 2>/dev/null || echo 'unknown')",
|
||||
"USER.md": "$(md5 -q "$WORKBUDDY_DIR/USER.md" 2>/dev/null || echo 'unknown')"
|
||||
}
|
||||
}
|
||||
VERSIONEOF
|
||||
ok "版本记录已保存: $VERSION_FILE"
|
||||
|
||||
# 多机角色提示
|
||||
if [ "$HOST_ROLE" != "unified" ]; then
|
||||
info "本机角色: $HOST_ROLE"
|
||||
info "后续可根据角色加载不同的 SOUL.md/IDENTITY.md 变体"
|
||||
fi
|
||||
|
||||
# ---------- 安装 Git pre-push 钩子(防止 --force) ----------
|
||||
HOOK_SRC="$AGENT_OS_ROOT/00_bootstrap/hooks/pre-push"
|
||||
HOOK_DST="$AGENT_OS_ROOT/.git/hooks/pre-push"
|
||||
if [ -f "$HOOK_SRC" ] && [ -d "$(dirname "$HOOK_DST")" ]; then
|
||||
cp "$HOOK_SRC" "$HOOK_DST"
|
||||
chmod +x "$HOOK_DST"
|
||||
ok "Git pre-push 钩子已安装(禁止 --force)"
|
||||
fi
|
||||
|
||||
# ---------- 自动注册到集群 ----------
|
||||
info "自动注册本机到集群..."
|
||||
if python3 "$AGENT_OS_ROOT/05_tools/01_system/cluster_registry.py" register 2>/dev/null; then
|
||||
ok "集群注册成功"
|
||||
info "查看集群状态: agentos cluster-status"
|
||||
else
|
||||
warn "集群注册失败(不影响核心配置)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
ok "核心配置部署完成(v2.0)!"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
info "配置文件位置: $WORKBUDDY_DIR/"
|
||||
info "备份位置: ${BACKUP_DIR:-无旧配置,无需备份}"
|
||||
info "本机角色: $HOST_ROLE"
|
||||
echo ""
|
||||
warn "请重启 WorkBuddy 以使配置生效"
|
||||
@@ -0,0 +1,232 @@
|
||||
#!/bin/bash
|
||||
# deploy.sh — AgentOS 一键部署脚本
|
||||
# 其他机器在 git pull 后运行此脚本,即可同步本机所有变更
|
||||
# 用法: bash 00_bootstrap/deploy.sh
|
||||
# 目标: 在其他机器上运行一次,环境变量 → 本地配置 → 依赖检查 → 全部就绪
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
AGENT_SYNC="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
info() { echo -e "\033[36m[INFO]\033[0m $1"; }
|
||||
ok() { echo -e "\033[32m[OK]\033[0m $1"; }
|
||||
warn() { echo -e "\033[33m[WARN]\033[0m $1"; }
|
||||
err() { echo -e "\033[31m[ERROR]\033[0m $1"; }
|
||||
|
||||
echo "============================================"
|
||||
echo " AgentOS 一键部署脚本"
|
||||
echo " 仓库: $AGENT_SYNC"
|
||||
echo " 时间: $(date '+%Y-%m-%d %H:%M')"
|
||||
echo "============================================"
|
||||
|
||||
# ── 步骤1: 设置环境变量 AGENT_SYNC / AGENT_LOCAL ──
|
||||
echo ""
|
||||
info "步骤1/5: 设置环境变量..."
|
||||
|
||||
if [ -z "$AGENT_SYNC" ]; then
|
||||
export AGENT_SYNC="$AGENT_SYNC"
|
||||
fi
|
||||
if [ -z "$AGENT_LOCAL" ]; then
|
||||
export AGENT_LOCAL="$HOME/workbuddy-agent-os/agent-local"
|
||||
fi
|
||||
|
||||
# 写入 .zshrc
|
||||
for RC_FILE in "$HOME/.zshrc" "$HOME/.bash_profile"; do
|
||||
if [ -f "$RC_FILE" ]; then
|
||||
if ! grep -q "export AGENT_SYNC" "$RC_FILE" 2>/dev/null; then
|
||||
echo "" >> "$RC_FILE"
|
||||
echo "# AgentOS 路径变量(由 deploy.sh 自动添加)" >> "$RC_FILE"
|
||||
echo "export AGENT_SYNC=\"$AGENT_SYNC\"" >> "$RC_FILE"
|
||||
echo "export AGENT_LOCAL=\"$AGENT_LOCAL\"" >> "$RC_FILE"
|
||||
ok "已写入 $RC_FILE"
|
||||
else
|
||||
ok "$RC_FILE 已有 AGENT_SYNC 设置"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# ── 步骤2: 创建 agent-local 目录结构 ──
|
||||
echo ""
|
||||
info "步骤2/5: 创建本机目录结构..."
|
||||
|
||||
mkdir -p "$AGENT_LOCAL/identity"
|
||||
mkdir -p "$AGENT_LOCAL/memory/raw"
|
||||
mkdir -p "$AGENT_LOCAL/memory/vector_db"
|
||||
mkdir -p "$AGENT_LOCAL/memory/daily"
|
||||
mkdir -p "$AGENT_LOCAL/runtime/cache"
|
||||
mkdir -p "$AGENT_LOCAL/tools/matrix/config"
|
||||
mkdir -p "$AGENT_LOCAL/tools/matrix/identities"
|
||||
mkdir -p "$AGENT_LOCAL/tools/matrix/data"
|
||||
ok "目录结构已创建"
|
||||
|
||||
# ── 步骤3: 创建本地配置(如不存在)──
|
||||
echo ""
|
||||
info "步骤3/5: 配置本地参数..."
|
||||
|
||||
LOCAL_CONFIG="$AGENT_LOCAL/config.yaml"
|
||||
if [ ! -f "$LOCAL_CONFIG" ]; then
|
||||
HOST="$(hostname)"
|
||||
cat > "$LOCAL_CONFIG" << CONFEOF
|
||||
# agent-local/config.yaml — 本机唯一配置入口
|
||||
# 此文件每台机器独立,不同步到 Git。覆盖 ORACLE.yaml 中的默认值。
|
||||
|
||||
hostname: "$HOST"
|
||||
machine_uid: ""
|
||||
|
||||
# 代理设置(各机器不同)
|
||||
proxy:
|
||||
socks5: "socks5://127.0.0.1:10800"
|
||||
|
||||
# 端口分配
|
||||
ports:
|
||||
dashboard: 9988
|
||||
camouflage_base: 9200
|
||||
CONFEOF
|
||||
ok "已创建: $LOCAL_CONFIG"
|
||||
else
|
||||
ok "本地配置已存在: $LOCAL_CONFIG"
|
||||
fi
|
||||
|
||||
# ── 步骤4: 检查 Python 和依赖 ──
|
||||
echo ""
|
||||
info "步骤4/5: 检查运行环境..."
|
||||
|
||||
PYTHON_OK=false
|
||||
for py in "$AGENT_LOCAL/../.workbuddy/binaries/python/envs/agent-os/bin/python3" \
|
||||
"$HOME/.workbuddy/binaries/python/envs/agent-os/bin/python3" \
|
||||
"/Users/chengzige/.workbuddy/binaries/python/envs/agent-os/bin/python3" \
|
||||
"/usr/local/bin/python3" "/usr/bin/python3"; do
|
||||
if [ -x "$py" ]; then
|
||||
VER=$("$py" --version 2>&1)
|
||||
info "Python: $VER ($py)"
|
||||
PYTHON_OK=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$PYTHON_OK" = false ]; then
|
||||
warn "未找到可用的 Python 3。请先安装 WorkBuddy 或 Python 3.13+"
|
||||
fi
|
||||
|
||||
# 检查关键依赖
|
||||
if [ "$PYTHON_OK" = true ]; then
|
||||
# 找可用的 pip
|
||||
for py in "$HOME/.workbuddy/binaries/python/envs/agent-os/bin/pip3" \
|
||||
"$HOME/.workbuddy/binaries/python/versions/3.13.12/bin/pip3" \
|
||||
"pip3"; do
|
||||
if command -v "$py" &>/dev/null; then
|
||||
PIP="$py"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# ── 步骤5: 检查 Tailscale(远程连接)──
|
||||
echo ""
|
||||
info "步骤5/6: 检查远程连接..."
|
||||
|
||||
if command -v tailscale &>/dev/null; then
|
||||
TS_STATUS=$(tailscale status 2>/dev/null | head -3)
|
||||
if [ -n "$TS_STATUS" ]; then
|
||||
ok "Tailscale 已连接"
|
||||
echo "$TS_STATUS" | while read line; do echo " $line"; done
|
||||
else
|
||||
warn "Tailscale 已安装但未登录,请运行: tailscale up"
|
||||
fi
|
||||
else
|
||||
warn "Tailscale 未安装。远程连接需要 Tailscale 实现跨局域网通信。"
|
||||
warn "安装方法: brew install --cask tailscale 或从 https://tailscale.com/download 下载"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ── 步骤6: 检查 Python 代码签名(新增:解决 macOS 代码签名问题)──
|
||||
echo ""
|
||||
info "步骤6/7: 检查 Python 代码签名..."
|
||||
|
||||
# 找到 WorkBuddy 管理的 Python
|
||||
for PY_BIN in "$HOME/.workbuddy/binaries/python/versions/3.13.12/bin/python3" \
|
||||
"$HOME/.workbuddy/binaries/python/versions/"*/bin/python3; do
|
||||
if [ -x "$PY_BIN" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -x "$PY_BIN" ]; then
|
||||
# 检查是否有 Team ID(腾讯签名会导致 orjson 无法加载)
|
||||
TEAM_ID=$(codesign -dvv "$PY_BIN" 2>&1 | grep "TeamIdentifier" | awk '{print $2}')
|
||||
if [ -n "$TEAM_ID" ] && [ "$TEAM_ID" != "not set" ]; then
|
||||
warn "Python 带有 Team ID 签名: $TEAM_ID → 可能导致 orjson 加载失败"
|
||||
info "正在修复: codesign -f -s - ..."
|
||||
codesign -f -s - "$PY_BIN" 2>&1 && ok "Python 代码签名已修复" || warn "修复失败,需手动执行"
|
||||
else
|
||||
ok "Python 代码签名正常(adhoc)"
|
||||
fi
|
||||
|
||||
# 验证 orjson
|
||||
OK=$("$PY_BIN" -c "import orjson; print(orjson.__version__)" 2>/dev/null)
|
||||
if [ -n "$OK" ]; then
|
||||
ok "orjson $OK ✅"
|
||||
else
|
||||
warn "orjson 不可用,尝试安装依赖..."
|
||||
"$PY_BIN" -m pip install -r "$SCRIPT_DIR/requirements.txt" 2>&1 | tail -2
|
||||
fi
|
||||
|
||||
# 验证 Camoufox
|
||||
C_OK=$("$PY_BIN" -c "from camoufox import Camoufox; print(1)" 2>/dev/null)
|
||||
if [ "$C_OK" = "1" ]; then
|
||||
ok "Camoufox ✅"
|
||||
else
|
||||
warn "Camoufox 不可用,安装中..."
|
||||
"$PY_BIN" -m pip install camoufox 2>&1 | tail -2
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── 步骤7: 同步 ORACLE 和授权 ──
|
||||
echo ""
|
||||
info "步骤7/7: 同步配置..."
|
||||
|
||||
# 确保 ORACLE.yaml 可读
|
||||
if [ -f "$AGENT_SYNC/ORACLE.yaml" ]; then
|
||||
ok "ORACLE.yaml 已就绪"
|
||||
else
|
||||
warn "ORACLE.yaml 不存在(可能尚未创建)"
|
||||
fi
|
||||
|
||||
# ── 安装防泄露 pre-commit hook ──
|
||||
echo ""
|
||||
info "安装防泄露 hook..."
|
||||
|
||||
HOOK_SRC="$AGENT_SYNC/00_bootstrap/hooks/pre-commit"
|
||||
HOOK_DST="$AGENT_SYNC/.git/hooks/pre-commit"
|
||||
if [ -f "$HOOK_SRC" ]; then
|
||||
cp "$HOOK_SRC" "$HOOK_DST" && chmod +x "$HOOK_DST"
|
||||
ok "pre-commit hook 已安装(提交前自动拦截 API Key)"
|
||||
else
|
||||
warn "未找到 pre-commit hook 源文件,跳过"
|
||||
fi
|
||||
|
||||
# 提醒用户
|
||||
echo ""
|
||||
echo "============================================"
|
||||
ok "部署完成!"
|
||||
echo ""
|
||||
echo " ✅ 环境变量: AGENT_SYNC/AGENT_LOCAL 已写入 shell 配置文件"
|
||||
echo " ✅ 目录结构: $AGENT_LOCAL 已创建"
|
||||
echo " ✅ 本地配置: $AGENT_LOCAL/config.yaml"
|
||||
echo " ✅ ORACLE 宪法: $AGENT_SYNC/ORACLE.yaml"
|
||||
echo " ✅ 防泄露 hook: 已安装(API Key 提交将被拦截)"
|
||||
echo ""
|
||||
echo " 后续手动步骤:"
|
||||
echo " 1. 使环境变量生效: source ~/.zshrc"
|
||||
echo " 2. 配置你自己的 API 密钥(重要!):"
|
||||
echo " 打开 Dashboard → 服务 → 🔑 API 配置"
|
||||
echo " 或用环境变量: export DEEPSEEK_API_KEY=sk-xxx / DASHSCOPE_API_KEY=sk-xxx"
|
||||
echo " (密钥只存本机 agent-local,不会提交到仓库)"
|
||||
echo " 3. (如未安装 Tailscale)安装并登录:"
|
||||
echo " brew install --cask tailscale"
|
||||
echo " 或从 https://tailscale.com/download 下载"
|
||||
echo " 4. 开启远程登录(每台机器一次):"
|
||||
echo " sudo systemsetup -setremotelogin on"
|
||||
echo "============================================"
|
||||
@@ -0,0 +1,72 @@
|
||||
#!/bin/bash
|
||||
# disable_worker_dashboard.sh — 停用 worker 机器上的 Dashboard(只保留 guardd)
|
||||
#
|
||||
# 背景:Dashboard 是联邦的统一控制平面,**只有 master(chengzigedeAir)运行**;
|
||||
# worker 只跑 guardd(:9090 节点代理)。
|
||||
# worker 误装 Dashboard 会导致「数据视图分裂」:账号标签/备注等本机数据
|
||||
# 不跨机同步,同一账号在不同看板显示不同值 → 出现"改了看不到"的困惑。
|
||||
#
|
||||
# 用法(在 worker 机器上执行):
|
||||
# bash 00_bootstrap/disable_worker_dashboard.sh
|
||||
#
|
||||
# 恢复(如需在 worker 上重新启用):
|
||||
# mv ~/Library/LaunchAgents/com.agentos.dashboard.plist{.disabled,}
|
||||
# launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.agentos.dashboard.plist
|
||||
|
||||
echo "============================================"
|
||||
echo " 停用本机 Dashboard(保留 guardd)"
|
||||
echo " 主机: $(hostname) 时间: $(date '+%Y-%m-%d %H:%M')"
|
||||
echo "============================================"
|
||||
echo
|
||||
|
||||
# ── 安全检查:master 不应执行本脚本(多来源识别,避免 hostname 返回 IP 时误判)──
|
||||
IDENT="$(hostname) $(scutil --get ComputerName 2>/dev/null) $(scutil --get LocalHostName 2>/dev/null)"
|
||||
HOSTID_MD="$HOME/workbuddy-agent-os/agent-local/identity/HOST_ID.md"
|
||||
[ -f "$HOSTID_MD" ] && IDENT="$IDENT $(head -20 "$HOSTID_MD" | tr '\n' ' ')"
|
||||
|
||||
IS_MASTER=false
|
||||
echo "$IDENT" | grep -qiE "chengzige|橙子" && IS_MASTER=true
|
||||
|
||||
if [ "$IS_MASTER" = "true" ] && [ "$1" != "--force" ]; then
|
||||
echo "⛔ 检测到本机疑似 master(识别串: ${IDENT:0:80}…)"
|
||||
echo " master 必须运行 Dashboard(联邦统一控制平面),已中止。"
|
||||
echo
|
||||
echo " 若确认要在本机停用(例如本机降级为 worker),请加 --force:"
|
||||
echo " bash $0 --force"
|
||||
exit 1
|
||||
fi
|
||||
[ "$IS_MASTER" = "true" ] && echo "⚠️ master 身份 + --force,继续执行(用户明确要求)" && echo
|
||||
|
||||
# ── ① 卸载 launchd 服务 ──
|
||||
echo "① 卸载 launchd 服务..."
|
||||
launchctl bootout "gui/$(id -u)/com.agentos.dashboard" 2>&1 | head -2 || echo " (服务未运行或已卸载)"
|
||||
|
||||
# ── ② 禁用 plist(改名保留,不删除)──
|
||||
echo "② 禁用 plist(改名保留,符合「不删除文件」纪律)..."
|
||||
PLIST="$HOME/Library/LaunchAgents/com.agentos.dashboard.plist"
|
||||
if [ -f "$PLIST" ]; then
|
||||
mv "$PLIST" "$PLIST.disabled" && echo " ✅ 已改名为 com.agentos.dashboard.plist.disabled"
|
||||
else
|
||||
echo " (plist 不存在或已禁用)"
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
|
||||
# ── ③ 验证 ──
|
||||
echo "③ 验证..."
|
||||
DASH_N=$(ps aux | grep "uvicorn app:app" | grep -v grep | wc -l | tr -d ' ')
|
||||
GUARDD_N=$(ps aux | grep -c "[g]uardd" | tr -d ' ')
|
||||
echo " Dashboard 进程数: $DASH_N (应为 0)"
|
||||
echo " guardd 进程数: $GUARDD_N (应 > 0)"
|
||||
if lsof -i :9988 >/dev/null 2>&1; then
|
||||
echo " ⚠️ 9988 端口仍在监听"
|
||||
else
|
||||
echo " ✅ 9988 端口已释放"
|
||||
fi
|
||||
echo
|
||||
if [ "$DASH_N" = "0" ] && [ "$GUARDD_N" != "0" ]; then
|
||||
echo "✅ 完成:Dashboard 已停用,guardd 正常运行"
|
||||
echo " 请统一访问中心看板:http://100.111.43.6:9988"
|
||||
else
|
||||
echo "⚠️ 请检查上述结果(Dashboard 应为 0,guardd 应 > 0)"
|
||||
fi
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
# ============================================================
|
||||
# AgentOS 技能导出脚本
|
||||
# 用途:将指定技能导出为可分发的压缩包
|
||||
# 使用:cd ~/workbuddy-agent-os/agent-sync/00_bootstrap && bash export_skills.sh <skill_name>
|
||||
# 示例:bash export_skills.sh memory_manager
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---------- 颜色定义 ----------
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
|
||||
# ---------- 定位路径 ----------
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
AGENT_OS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
SKILLS_DIR="$AGENT_OS_ROOT/02_skills"
|
||||
OUTPUT_DIR="$AGENT_OS_ROOT/07_migration/exports"
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# ---------- 参数检查 ----------
|
||||
SKILL_NAME="${1:-}"
|
||||
if [ -z "$SKILL_NAME" ]; then
|
||||
echo "用法: bash export_skills.sh <skill_name>"
|
||||
echo "可导出的技能:"
|
||||
ls -1 "$SKILLS_DIR" | grep -v "_template"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SKILL_PATH="$SKILLS_DIR/$SKILL_NAME"
|
||||
if [ ! -d "$SKILL_PATH" ]; then
|
||||
echo "错误: 技能不存在: $SKILL_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---------- 读取版本号 ----------
|
||||
VERSION="1.0.0"
|
||||
if [ -f "$SKILL_PATH/version.json" ]; then
|
||||
VERSION="$(python3 -c "import json; print(json.load(open('$SKILL_PATH/version.json')).get('version', '1.0.0'))" 2>/dev/null || echo "1.0.0")"
|
||||
fi
|
||||
|
||||
# ---------- 打包 ----------
|
||||
ARCHIVE_NAME="${SKILL_NAME}_v${VERSION}.zip"
|
||||
ARCHIVE_PATH="$OUTPUT_DIR/$ARCHIVE_NAME"
|
||||
|
||||
cd "$SKILLS_DIR"
|
||||
zip -r "$ARCHIVE_PATH" "$SKILL_NAME/" -x "*.pyc" "__pycache__/*" ".DS_Store"
|
||||
cd - > /dev/null
|
||||
|
||||
ok "已导出: $ARCHIVE_PATH"
|
||||
info "其他人可通过以下命令导入:"
|
||||
info " cd ~/workbuddy-agent-os/agent-sync/00_bootstrap && bash import_skills.sh $ARCHIVE_PATH"
|
||||
@@ -0,0 +1,202 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# fleet_reconcile.sh — AgentOS 联邦对账引擎 (v1.0)
|
||||
# 功能: 本机读取 ORACLE → 检查自己该执行的任务 → 报告不一致
|
||||
# 用法: bash 00_bootstrap/fleet_reconcile.sh
|
||||
#
|
||||
# 自动对账流程:
|
||||
# 1. 读 ORACLE.yaml → 找到本机定义
|
||||
# 2. 检查本机环境变量、目录、文件是否就绪
|
||||
# 3. 检查 ORACLE 中本机该执行的任务是否在运行
|
||||
# 4. 输出对账报告
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ORACLE="$SCRIPT_DIR/ORACLE.yaml"
|
||||
LOCAL_HOSTNAME=$(hostname)
|
||||
|
||||
# 查找可用 Python(优先 agent-os 虚拟环境)
|
||||
if [ -f "$SCRIPT_DIR/.venv/bin/python3" ]; then
|
||||
PYTHON="$SCRIPT_DIR/.venv/bin/python3"
|
||||
elif [ -f "$HOME/.workbuddy/binaries/python/envs/agent-os/bin/python3" ]; then
|
||||
PYTHON="$HOME/.workbuddy/binaries/python/envs/agent-os/bin/python3"
|
||||
else
|
||||
PYTHON="python3"
|
||||
fi
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
||||
ok() { echo -e " ${GREEN}✅${NC} $1"; }
|
||||
warn() { echo -e " ${YELLOW}⚠️${NC} $1"; }
|
||||
fail() { echo -e " ${RED}❌${NC} $1"; }
|
||||
info() { echo -e " ${CYAN}ℹ️${NC} $1"; }
|
||||
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════"
|
||||
echo " 📋 AgentOS 联邦对账"
|
||||
echo "═══════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
# ── 1. 检查 ORACLE ──
|
||||
if [ ! -f "$ORACLE" ]; then
|
||||
fail "ORACLE.yaml 不存在"
|
||||
exit 1
|
||||
fi
|
||||
ok "ORACLE.yaml 已读取"
|
||||
|
||||
# ── 2. 本机身份 ──
|
||||
# 在 ORACLE 中找到本机
|
||||
MACHINE_INFO=$($PYTHON -c "
|
||||
import yaml
|
||||
d = yaml.safe_load(open('$ORACLE'))
|
||||
hostname = '$LOCAL_HOSTNAME'
|
||||
for name, info in d.get('machines', {}).items():
|
||||
if info.get('hostname') == hostname or name == hostname:
|
||||
print(yaml.dump({name: info}, default_flow_style=False))
|
||||
break
|
||||
else:
|
||||
print('NOT_FOUND')
|
||||
")
|
||||
|
||||
if [ "$MACHINE_INFO" = "NOT_FOUND" ]; then
|
||||
warn "本机 ($LOCAL_HOSTNAME) 未在 ORACLE 中找到,可能是一台新机器"
|
||||
info "如需加入联邦,请在 ORACLE.yaml 中添加本机定义"
|
||||
else
|
||||
ok "本机身份: $LOCAL_HOSTNAME"
|
||||
echo "$MACHINE_INFO" | head -6 | sed 's/^/ /'
|
||||
fi
|
||||
|
||||
# ── 3. 环境变量对账 ──
|
||||
echo ""
|
||||
echo "━━━ 3. 运行环境 ━━━"
|
||||
|
||||
# AGENT_SYNC
|
||||
if [ -n "$AGENT_SYNC" ]; then
|
||||
ok "AGENT_SYNC=$AGENT_SYNC"
|
||||
elif grep -q "AGENT_SYNC" ~/.zshrc 2>/dev/null; then
|
||||
warn "AGENT_SYNC 已配置但未生效,请执行: source ~/.zshrc"
|
||||
info "设置临时变量: export AGENT_SYNC=\"\$HOME/workbuddy-agent-os/agent-sync\""
|
||||
export AGENT_SYNC="$HOME/workbuddy-agent-os/agent-sync"
|
||||
else
|
||||
warn "AGENT_SYNC 未配置"
|
||||
info "写入 .zshrc: bash $SCRIPT_DIR/00_bootstrap/setup_env.sh && source ~/.zshrc"
|
||||
fi
|
||||
|
||||
# AGENT_LOCAL
|
||||
if [ -z "$AGENT_LOCAL" ]; then
|
||||
export AGENT_LOCAL="$HOME/workbuddy-agent-os/agent-local"
|
||||
fi
|
||||
ok "AGENT_LOCAL=${AGENT_LOCAL:-$HOME/workbuddy-agent-os/agent-local}"
|
||||
|
||||
# ── 4. 目录结构 ──
|
||||
echo ""
|
||||
echo "━━━ 4. 目录结构 ━━━"
|
||||
|
||||
SYNC_DIR="${AGENT_SYNC:-$SCRIPT_DIR}"
|
||||
LOCAL_DIR="${AGENT_LOCAL:-$HOME/workbuddy-agent-os/agent-local}"
|
||||
|
||||
test -d "$SYNC_DIR" && ok "agent-sync: ✅" || fail "agent-sync: ❌ 不存在"
|
||||
test -d "$LOCAL_DIR" && ok "agent-local: ✅" || {
|
||||
fail "agent-local: ❌ 不存在"
|
||||
mkdir -p "$LOCAL_DIR" && ok " → 已自动创建"
|
||||
}
|
||||
|
||||
# ── 5. 关键文件 ──
|
||||
echo ""
|
||||
echo "━━━ 5. 关键文件 ━━━"
|
||||
|
||||
test -f "$SYNC_DIR/ORACLE.yaml" && ok "ORACLE.yaml: ✅" || fail "ORACLE.yaml: ❌"
|
||||
test -f "$LOCAL_DIR/config.yaml" && ok "config.yaml: ✅" || {
|
||||
warn "config.yaml: ❌ 不存在"
|
||||
info "正在创建默认配置..."
|
||||
$PYTHON -c "
|
||||
import yaml
|
||||
config = {
|
||||
'hostname': '$LOCAL_HOSTNAME',
|
||||
'proxy': {'socks5': 'socks5://127.0.0.1:10800', 'http': ''},
|
||||
'ports': {'dashboard': 9988, 'camouflage_base': 9200},
|
||||
}
|
||||
with open('$LOCAL_DIR/config.yaml', 'w') as f:
|
||||
yaml.dump(config, f, default_flow_style=False)
|
||||
print(' -> 已创建默认 config.yaml')
|
||||
"
|
||||
}
|
||||
|
||||
# ── 6. Git 状态 ──
|
||||
echo ""
|
||||
echo "━━━ 6. Git 状态 ━━━"
|
||||
|
||||
if cd "$SYNC_DIR" 2>/dev/null; then
|
||||
GIT_LOG=$(git log --oneline -1 2>/dev/null)
|
||||
GIT_STATUS=$(git status --short 2>/dev/null | head -3)
|
||||
ok "仓库: $(basename $(git rev-parse --show-toplevel 2>/dev/null))"
|
||||
info "最新提交: $GIT_LOG"
|
||||
if [ -n "$GIT_STATUS" ]; then
|
||||
warn "有未提交的更改:"
|
||||
echo "$GIT_STATUS" | while read line; do echo " $line"; done
|
||||
else
|
||||
ok "工作区干净"
|
||||
fi
|
||||
else
|
||||
fail "Git 仓库不可用"
|
||||
fi
|
||||
|
||||
# ── 7. Dashboard ──
|
||||
echo ""
|
||||
echo "━━━ 7. 服务状态 ━━━"
|
||||
|
||||
DASH=$(curl -s --connect-timeout 3 http://localhost:9988/api/identity 2>/dev/null)
|
||||
if [ -n "$DASH" ]; then
|
||||
HOST=$(echo "$DASH" | $PYTHON -c "import sys,json;print(json.load(sys.stdin).get('hostname','?'))" 2>/dev/null)
|
||||
ok "Dashboard: ✅ 运行中 ($HOST:9988)"
|
||||
else
|
||||
warn "Dashboard: ❌ 未运行"
|
||||
info "启动: cd \$SYNC_DIR/05_tools/10_dashboard && \$PYTHON run.py 9988 &"
|
||||
fi
|
||||
|
||||
# ── 8. ORACLE 中本机的任务检查 ──
|
||||
echo ""
|
||||
echo "━━━ 8. 任务对账 ━━━"
|
||||
|
||||
$PYTHON -c "
|
||||
import yaml, json, os, subprocess
|
||||
d = yaml.safe_load(open('$ORACLE'))
|
||||
hostname = '$LOCAL_HOSTNAME'
|
||||
|
||||
# 找本机的机器名
|
||||
my_name = None
|
||||
for name, info in d.get('machines', {}).items():
|
||||
if info.get('hostname') == hostname or name == hostname:
|
||||
my_name = name
|
||||
break
|
||||
|
||||
if not my_name:
|
||||
print(' ⚠️ 本机未在 ORACLE 中找到任务定义')
|
||||
else:
|
||||
# 找本机该执行的任务
|
||||
my_tasks = []
|
||||
for t in d.get('tasks', []):
|
||||
machines = t.get('on_machines', [])
|
||||
if any(my_name in m or hostname in m for m in machines):
|
||||
my_tasks.append(t)
|
||||
|
||||
if my_tasks:
|
||||
print(f' 本机 ({my_name}) 应执行 {len(my_tasks)} 个任务:')
|
||||
for t in my_tasks:
|
||||
print(f' - {t[\"name\"]}: {t.get(\"schedule\",\"?\")} → {t.get(\"blueprint\",\"?\")}')
|
||||
else:
|
||||
print(f' 本机 ({my_name}) 无定时任务')
|
||||
print(' (本机可能只负责运行 Dashboard 或按需执行)')
|
||||
" 2>&1
|
||||
|
||||
# ── 汇总 ──
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════"
|
||||
echo " 对账完成"
|
||||
echo "═══════════════════════════════════════════════"
|
||||
echo ""
|
||||
echo " 如果全部 ✅,本机已达标。"
|
||||
echo " 如果有 ⚠️ 或 ❌,请按提示修复。"
|
||||
echo " 同步所有机器: bash $SCRIPT_DIR/00_bootstrap/fleet_sync.sh"
|
||||
echo ""
|
||||
@@ -0,0 +1,236 @@
|
||||
#!/bin/bash
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# fleet_sync.sh — AgentOS 联邦一键同步脚本 (v1.0)
|
||||
# 功能: 一条命令让所有机器 git pull + 环境检查 + 状态报告
|
||||
# 用法: bash 00_bootstrap/fleet_sync.sh
|
||||
#
|
||||
# 依赖:
|
||||
# - Tailscale 网络 (所有机器在同一网络)
|
||||
# - SSH 密钥已加入各机器 authorized_keys
|
||||
# - ORACLE.yaml 定义了所有机器信息
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
# ── 配置 ──
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ORACLE="$SCRIPT_DIR/ORACLE.yaml"
|
||||
PARALLEL=true
|
||||
|
||||
# 颜色
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
ok() { echo -e " ${GREEN}✅${NC} $1"; }
|
||||
warn() { echo -e " ${YELLOW}⚠️${NC} $1"; }
|
||||
fail() { echo -e " ${RED}❌${NC} $1"; }
|
||||
info() { echo -e " ${CYAN}ℹ️${NC} $1"; }
|
||||
|
||||
# ── 读取 ORACLE 获取机器信息 ──
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════"
|
||||
echo " 🚀 AgentOS 联邦同步"
|
||||
echo "═══════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
if [ ! -f "$ORACLE" ]; then
|
||||
fail "ORACLE.yaml 不存在: $ORACLE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 用 Python 从 ORACLE 解析机器信息
|
||||
MACHINES=$(python3 -c "
|
||||
import yaml, json
|
||||
d = yaml.safe_load(open('$ORACLE'))
|
||||
machines = []
|
||||
for name, info in d.get('machines', {}).items():
|
||||
machines.append({
|
||||
'name': name,
|
||||
'hostname': info.get('hostname', name),
|
||||
'tailscale_ip': info.get('tailscale_ip', ''),
|
||||
'user': info.get('ssh_user', ''),
|
||||
})
|
||||
print(json.dumps(machines))
|
||||
")
|
||||
|
||||
# 解析本机名
|
||||
LOCAL_HOSTNAME=$(hostname)
|
||||
|
||||
# 同步每个远程机器
|
||||
sync_machine() {
|
||||
local name="$1"
|
||||
local ip="$2"
|
||||
local user="$3"
|
||||
|
||||
echo " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " 📡 $name ($ip)"
|
||||
echo " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# SSH 执行同步
|
||||
local result
|
||||
result=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 \
|
||||
"$user@$ip" '
|
||||
export AGENT_SYNC="$HOME/workbuddy-agent-os/agent-sync"
|
||||
export AGENT_LOCAL="$HOME/workbuddy-agent-os/agent-local"
|
||||
|
||||
echo "SYNC_START"
|
||||
|
||||
# 1. 环境变量检查
|
||||
if [ -f ~/.zshrc ] && grep -q "AGENT_SYNC" ~/.zshrc 2>/dev/null; then
|
||||
echo "ENV_OK"
|
||||
else
|
||||
echo "ENV_MISSING"
|
||||
fi
|
||||
|
||||
# 2. git pull
|
||||
cd "$AGENT_SYNC" 2>/dev/null && {
|
||||
git pull origin main --rebase 2>&1 | tail -1
|
||||
echo "GIT_VERSION:$(git log --oneline -1 2>/dev/null)"
|
||||
} || echo "GIT_FAIL"
|
||||
|
||||
# 3. 目录检查
|
||||
for d in "$AGENT_SYNC" "$AGENT_LOCAL"; do
|
||||
if [ -d "$d" ]; then echo "DIR_OK:$d"; else echo "DIR_FAIL:$d"; fi
|
||||
done
|
||||
|
||||
# 4. 关键文件检查
|
||||
for f in "$AGENT_SYNC/ORACLE.yaml" "$AGENT_LOCAL/config.yaml"; do
|
||||
if [ -f "$f" ]; then echo "FILE_OK:$f"; else echo "FILE_FAIL:$f"; fi
|
||||
done
|
||||
|
||||
# 5. Dashboard 状态
|
||||
local dash
|
||||
dash=$(curl -s --connect-timeout 3 http://localhost:9988/api/identity 2>/dev/null | python3 -c "import sys,json;print(json.load(sys.stdin).get('hostname',''))" 2>/dev/null)
|
||||
if [ -n "$dash" ]; then
|
||||
echo "DASH_OK:$dash"
|
||||
else
|
||||
echo "DASH_FAIL"
|
||||
fi
|
||||
|
||||
echo "SYNC_END"
|
||||
' 2>&1)
|
||||
|
||||
local exit_code=$?
|
||||
|
||||
if [ $exit_code -ne 0 ]; then
|
||||
fail "$name — SSH 连接失败 (exit=$exit_code)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 解析结果
|
||||
local env_ok=false dir_ok=true file_ok=true dash_ok=false git_ver=""
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
ENV_OK) env_ok=true ;;
|
||||
ENV_MISSING) warn "$name — 环境变量 AGENT_SYNC 未在 .zshrc 中配置" ;;
|
||||
GIT_VERSION:*) git_ver="${line#GIT_VERSION:}" ;;
|
||||
GIT_FAIL) fail "$name — Git 仓库不存在" ;;
|
||||
DIR_OK:*) ;;
|
||||
DIR_FAIL:*) fail "$name — 目录缺失: ${line#DIR_FAIL:}"; dir_ok=false ;;
|
||||
FILE_OK:*) ;;
|
||||
FILE_FAIL:*) fail "$name — 文件缺失: ${line#FILE_FAIL:}"; file_ok=false ;;
|
||||
DASH_OK:*) dash_ok=true ;;
|
||||
DASH_FAIL) warn "$name — Dashboard 未运行" ;;
|
||||
Already|Updating|Successfully|From*) ;;
|
||||
SYNC_START|SYNC_END) ;;
|
||||
esac
|
||||
done <<< "$result"
|
||||
|
||||
# 总结
|
||||
if $env_ok && $dir_ok && $file_ok; then
|
||||
ok "$name — 同步完成"
|
||||
[ -n "$git_ver" ] && echo " Git: $git_ver"
|
||||
$dash_ok && echo " Dashboard: ✅ 运行中" || echo " Dashboard: ⚠️ 未运行"
|
||||
else
|
||||
warn "$name — 部分异常,请检查"
|
||||
fi
|
||||
}
|
||||
|
||||
# 同步本机
|
||||
sync_local() {
|
||||
echo " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " 🖥️ 本机 ($LOCAL_HOSTNAME)"
|
||||
echo " ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# 1. 环境变量
|
||||
if [ -n "$AGENT_SYNC" ]; then
|
||||
ok "环境变量 AGENT_SYNC=$AGENT_SYNC"
|
||||
elif [ -f ~/.zshrc ] && grep -q "AGENT_SYNC" ~/.zshrc 2>/dev/null; then
|
||||
warn "环境变量已配置但未生效,请执行: source ~/.zshrc"
|
||||
else
|
||||
warn "环境变量未配置"
|
||||
fi
|
||||
|
||||
# 2. git pull
|
||||
cd "$SCRIPT_DIR" 2>/dev/null
|
||||
if git pull origin main --rebase 2>&1 | grep -q "Already up to date"; then
|
||||
ok "Git 已是最新: $(git log --oneline -1)"
|
||||
else
|
||||
ok "Git 已更新: $(git log --oneline -1)"
|
||||
fi
|
||||
|
||||
# 3. 目录检查
|
||||
local sync_dir="$SCRIPT_DIR"
|
||||
local local_dir="${AGENT_LOCAL:-$HOME/workbuddy-agent-os/agent-local}"
|
||||
test -d "$sync_dir" && ok "agent-sync: ✅" || fail "agent-sync: ❌"
|
||||
test -d "$local_dir" && ok "agent-local: ✅" || fail "agent-local: ❌"
|
||||
|
||||
# 4. 文件检查
|
||||
test -f "$sync_dir/ORACLE.yaml" && ok "ORACLE.yaml: ✅" || fail "ORACLE.yaml: ❌"
|
||||
test -f "$local_dir/config.yaml" && ok "config.yaml: ✅" || fail "config.yaml: ❌"
|
||||
|
||||
# 5. Dashboard
|
||||
local dash
|
||||
dash=$(curl -s --connect-timeout 2 http://localhost:9988/api/identity 2>/dev/null | python3 -c "import sys,json;print(json.load(sys.stdin).get('hostname',''))" 2>/dev/null)
|
||||
if [ -n "$dash" ]; then
|
||||
ok "Dashboard: ✅ ($dash)"
|
||||
else
|
||||
warn "Dashboard: 未运行"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── 主流程 ──
|
||||
sync_local
|
||||
|
||||
echo ""
|
||||
echo " ─────────────────────────────────────────"
|
||||
echo ""
|
||||
|
||||
# 解析机器列表并逐个同步
|
||||
REMOTE_COUNT=0
|
||||
while IFS= read -r line; do
|
||||
name=$(echo "$line" | python3 -c "import sys,json;print(json.load(sys.stdin).get('name',''))")
|
||||
ip=$(echo "$line" | python3 -c "import sys,json;print(json.load(sys.stdin).get('tailscale_ip',''))")
|
||||
user=$(echo "$line" | python3 -c "import sys,json;print(json.load(sys.stdin).get('user',''))")
|
||||
|
||||
# 跳过本机
|
||||
if [ "$(echo "$line" | python3 -c "import sys,json;print(json.load(sys.stdin).get('hostname',''))")" = "$LOCAL_HOSTNAME" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -n "$ip" ] && [ -n "$user" ]; then
|
||||
sync_machine "$name" "$ip" "$user"
|
||||
REMOTE_COUNT=$((REMOTE_COUNT + 1))
|
||||
echo ""
|
||||
fi
|
||||
done < <(echo "$MACHINES" | python3 -c "
|
||||
import sys, json
|
||||
machines = json.load(sys.stdin)
|
||||
for m in machines:
|
||||
print(json.dumps(m))
|
||||
")
|
||||
|
||||
# ── 汇总 ──
|
||||
echo "═══════════════════════════════════════════════"
|
||||
echo " 同步完成"
|
||||
echo "═══════════════════════════════════════════════"
|
||||
echo ""
|
||||
ok "本机: $LOCAL_HOSTNAME"
|
||||
ok "远程机器: $REMOTE_COUNT 台已同步"
|
||||
echo ""
|
||||
echo " 如果一切正常,所有机器的代码和配置现在是一致的。"
|
||||
echo " 如果某台机器报 ❌,请检查 SSH 连接或手动登录处理。"
|
||||
echo ""
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
# AgentOS pre-commit 防泄露钩子
|
||||
#
|
||||
# 作用:提交前扫描暂存区内容,命中疑似 API Key 模式则阻止提交。
|
||||
# 安装:随机组部署自动安装(见 00_bootstrap/deploy.sh),或手动:
|
||||
# cp 00_bootstrap/hooks/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
|
||||
#
|
||||
# 背景:公开仓库一旦提交密钥即永久泄露(git 历史无法简单抹除),
|
||||
# 故用钩子在任何提交前拦截。密钥应放 agent-local/(不被 git 跟踪)或环境变量。
|
||||
|
||||
PATTERNS="sk-[A-Za-z0-9]{20,}|sk-ws-[A-Za-z0-9._-]{20,}|api-key-kling-[A-Za-z0-9_-]{10,}|AKLT[A-Za-z0-9]{12,}|LTAI[A-Za-z0-9]{12,}|sk-ant-[A-Za-z0-9_-]{20,}|ark-[a-f0-9]{8}-[a-f0-9-]{20,}"
|
||||
|
||||
hits=""
|
||||
for f in $(git diff --cached --name-only --diff-filter=ACM); do
|
||||
case "$f" in
|
||||
*.png|*.jpg|*.jpeg|*.gif|*.webp|*.db|*.sqlite|*.sqlite3|*.zip|*.gz|*.mp4|*.mov|*.woff*|*.ttf) continue ;;
|
||||
esac
|
||||
m=$(git show ":$f" 2>/dev/null | grep -E -o "$PATTERNS" | sort -u | head -3)
|
||||
if [ -n "$m" ]; then
|
||||
hits="${hits}\n ${f}:\n$(echo "$m" | sed -E 's/([A-Za-z0-9_-]{6})[A-Za-z0-9_-]{8,}/\1…(已脱敏)/g' | sed 's/^/ /')"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$hits" ]; then
|
||||
echo "❌ 检测到疑似 API Key,提交已阻止:"
|
||||
echo -e "$hits"
|
||||
echo ""
|
||||
echo "👉 密钥请放 agent-local/(不被 git 跟踪)或使用环境变量;"
|
||||
echo " 配置界面:Dashboard → 服务 → 🔑 API 配置"
|
||||
echo " 如确认是误报(示例/占位文本),可用:git commit --no-verify"
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# AgentOS pre-push 钩子
|
||||
# 防止意外使用 git push --force 覆盖他人提交
|
||||
# 在所有机器上通过 git pull 自动同步
|
||||
|
||||
# 检查是否使用了 --force
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "--force" ] || [ "$arg" = "-f" ]; then
|
||||
echo ""
|
||||
echo "❌ 禁止使用 git push --force"
|
||||
echo " 这会覆盖其他机器的提交记录"
|
||||
echo ""
|
||||
echo "✅ 正确做法:"
|
||||
echo " git pull # 先拉取别人更新"
|
||||
echo " git push # 再推自己的"
|
||||
echo ""
|
||||
echo "⚠️ 如需强制推送(如修复敏感信息),改用安全的:"
|
||||
echo " git push --force-with-lease"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查是否落后于远程
|
||||
if git rev-parse --verify HEAD >/dev/null 2>&1; then
|
||||
REMOTE_HASH=$(git rev-parse origin/main 2>/dev/null)
|
||||
LOCAL_HASH=$(git rev-parse HEAD)
|
||||
if [ "$REMOTE_HASH" != "" ] && [ "$REMOTE_HASH" != "$LOCAL_HASH" ]; then
|
||||
# 检查本地是否直接基于远程
|
||||
MERGE_BASE=$(git merge-base HEAD origin/main)
|
||||
if [ "$MERGE_BASE" != "$REMOTE_HASH" ]; then
|
||||
echo ""
|
||||
echo "⚠️ 本地落后于远程,建议先 git pull 再推送"
|
||||
echo " git pull"
|
||||
echo " git push"
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
# ============================================================
|
||||
# AgentOS 技能导入脚本
|
||||
# 用途:将 02_skills/ 下的自定义技能注册到 WorkBuddy
|
||||
# 使用:cd ~/workbuddy-agent-os/agent-sync/00_bootstrap && bash import_skills.sh [skill_name]
|
||||
# 参数:skill_name - 可选,指定导入单个技能;不传则导入全部
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---------- 颜色定义 ----------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
err() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
|
||||
|
||||
# ---------- 定位路径 ----------
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
AGENT_OS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
SKILLS_DIR="$AGENT_OS_ROOT/02_skills"
|
||||
USER_SKILLS_DIR="$HOME/.workbuddy/skills"
|
||||
PROJECT_SKILLS_DIR=""
|
||||
|
||||
# 尝试找到当前工作区的 .workbuddy/skills 目录
|
||||
for ws in "$HOME/WorkBuddy"/*; do
|
||||
if [ -d "$ws/.workbuddy/skills" ]; then
|
||||
PROJECT_SKILLS_DIR="$ws/.workbuddy/skills"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
info "技能源目录: $SKILLS_DIR"
|
||||
info "用户级技能目录: $USER_SKILLS_DIR"
|
||||
|
||||
# ---------- 创建目标目录 ----------
|
||||
mkdir -p "$USER_SKILLS_DIR"
|
||||
|
||||
# ---------- 导入技能 ----------
|
||||
TARGET_SKILL="${1:-}"
|
||||
|
||||
import_skill() {
|
||||
local skill_dir="$1"
|
||||
local skill_name="$(basename "$skill_dir")"
|
||||
|
||||
# 跳过模板目录
|
||||
if [ "$skill_name" = "_template" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# 检查 SKILL.md 是否存在
|
||||
if [ ! -f "$skill_dir/SKILL.md" ]; then
|
||||
warn "跳过 $skill_name:缺少 SKILL.md"
|
||||
return
|
||||
fi
|
||||
|
||||
# 复制技能到用户级目录
|
||||
local dest="$USER_SKILLS_DIR/$skill_name"
|
||||
if [ -d "$dest" ]; then
|
||||
warn "$skill_name 已存在,更新..."
|
||||
rm -rf "$dest"
|
||||
fi
|
||||
|
||||
cp -r "$skill_dir" "$dest"
|
||||
ok "已导入: $skill_name → $dest"
|
||||
}
|
||||
|
||||
if [ -n "$TARGET_SKILL" ]; then
|
||||
# 导入指定技能
|
||||
if [ -d "$SKILLS_DIR/$TARGET_SKILL" ]; then
|
||||
import_skill "$SKILLS_DIR/$TARGET_SKILL"
|
||||
else
|
||||
err "技能不存在: $TARGET_SKILL"
|
||||
fi
|
||||
else
|
||||
# 导入全部技能
|
||||
info "扫描技能目录..."
|
||||
for skill_dir in "$SKILLS_DIR"/*/; do
|
||||
if [ -d "$skill_dir" ]; then
|
||||
import_skill "$skill_dir"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
ok "技能导入完成!"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
info "已导入的技能目录: $USER_SKILLS_DIR/"
|
||||
info "重启 WorkBuddy 后新技能将生效"
|
||||
@@ -0,0 +1,463 @@
|
||||
#!/bin/bash
|
||||
# ============================================================
|
||||
# AgentOS 初始化脚本
|
||||
# 用途:首次部署或换机还原时运行,创建目录结构、安装依赖、
|
||||
# 填充设备信息、部署核心配置
|
||||
# 使用:cd ~/workbuddy-agent-os/agent-sync/00_bootstrap && bash init.sh
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---------- 颜色定义 ----------
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
err() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
|
||||
|
||||
# ---------- 定位 agent-os 根目录 ----------
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
AGENT_OS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
info "AgentOS 根目录: $AGENT_OS_ROOT"
|
||||
|
||||
# ---------- 检测操作系统 ----------
|
||||
OS_TYPE="$(uname -s)"
|
||||
case "$OS_TYPE" in
|
||||
Darwin) OS_NAME="macOS"; OS_INFO="$(sw_vers -productName) $(sw_vers -productVersion)" ;;
|
||||
Linux) OS_NAME="Linux"; OS_INFO="$(lsb_release -ds 2>/dev/null || cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2)" ;;
|
||||
*) OS_NAME="$OS_TYPE"; OS_INFO="$OS_TYPE" ;;
|
||||
esac
|
||||
HOSTNAME="$(hostname)"
|
||||
info "操作系统: $OS_INFO ($OS_NAME)"
|
||||
info "主机名: $HOSTNAME"
|
||||
|
||||
# ---------- 检测 Python ----------
|
||||
PYTHON_CMD=""
|
||||
CURRENT_USER="$(whoami)"
|
||||
MANAGED_PYTHON="$HOME/.workbuddy/binaries/python/versions/3.13.12/bin/python3"
|
||||
AGENTOS_PYTHON="$HOME/.workbuddy/binaries/python/envs/agent-os/bin/python3"
|
||||
AGENTOS_VENV="$HOME/.workbuddy/binaries/python/envs/agent-os"
|
||||
|
||||
if [ -x "$AGENTOS_PYTHON" ]; then
|
||||
PYTHON_CMD="$AGENTOS_PYTHON"
|
||||
info "使用 agent-os 虚拟环境: $AGENTOS_VENV"
|
||||
elif [ -x "$MANAGED_PYTHON" ]; then
|
||||
PYTHON_CMD="$MANAGED_PYTHON"
|
||||
warn "agent-os venv 不存在,使用裸 managed Python"
|
||||
elif command -v python3 &>/dev/null && python3 --version &>/dev/null; then
|
||||
PYTHON_CMD="python3"
|
||||
warn "managed Python 不可用,使用系统 python3"
|
||||
elif command -v python &>/dev/null && python --version &>/dev/null; then
|
||||
PYTHON_CMD="python"
|
||||
fi
|
||||
|
||||
if [ -n "$PYTHON_CMD" ]; then
|
||||
PYTHON_PATH="$(command -v $PYTHON_CMD)"
|
||||
PYTHON_VER="$($PYTHON_CMD --version 2>&1)"
|
||||
info "Python: $PYTHON_VER ($PYTHON_PATH)"
|
||||
else
|
||||
warn "未检测到 Python,部分技能将不可用"
|
||||
fi
|
||||
|
||||
# ---------- 检测环境变量 AGENT_SYNC / AGENT_LOCAL ----------
|
||||
if [ -z "$AGENT_SYNC" ]; then
|
||||
export AGENT_SYNC="$HOME/workbuddy-agent-os/agent-sync"
|
||||
warn "AGENT_SYNC 未设置,默认: $AGENT_SYNC"
|
||||
# 写入 .zshrc(如果存在)
|
||||
if [ -f "$HOME/.zshrc" ]; then
|
||||
if ! grep -q "export AGENT_SYNC" "$HOME/.zshrc" 2>/dev/null; then
|
||||
echo "" >> "$HOME/.zshrc"
|
||||
echo "# AgentOS 路径变量" >> "$HOME/.zshrc"
|
||||
echo "export AGENT_SYNC=\"\$HOME/workbuddy-agent-os/agent-sync\"" >> "$HOME/.zshrc"
|
||||
echo "export AGENT_LOCAL=\"\$HOME/workbuddy-agent-os/agent-local\"" >> "$HOME/.zshrc"
|
||||
ok "已写入 .zshrc: AGENT_SYNC / AGENT_LOCAL"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
ok "AGENT_SYNC=$AGENT_SYNC"
|
||||
fi
|
||||
if [ -z "$AGENT_LOCAL" ]; then
|
||||
export AGENT_LOCAL="$HOME/workbuddy-agent-os/agent-local"
|
||||
warn "AGENT_LOCAL 未设置,默认: $AGENT_LOCAL"
|
||||
fi
|
||||
|
||||
# ---------- 检测或创建 agent-local/config.yaml ----------
|
||||
LOCAL_CONFIG="$AGENT_LOCAL/config.yaml"
|
||||
if [ ! -f "$LOCAL_CONFIG" ]; then
|
||||
mkdir -p "$AGENT_LOCAL" 2>/dev/null
|
||||
cat > "$LOCAL_CONFIG" << 'CONFEOF'
|
||||
# agent-local/config.yaml — 本机唯一配置入口
|
||||
# 此文件每台机器独立,不同步到 Git。覆盖 ORACLE.yaml 中的默认值。
|
||||
|
||||
hostname: "$(hostname)"
|
||||
machine_uid: ""
|
||||
|
||||
# 代理设置(各机器不同)
|
||||
proxy:
|
||||
socks5: "socks5://127.0.0.1:10800"
|
||||
http: ""
|
||||
|
||||
# 浏览器窗口偏移(多账号同屏时避免重叠)
|
||||
screen_offsets:
|
||||
default: {x: 0, y: 0, width: 702, height: 783}
|
||||
|
||||
# 端口分配(避免多机器端口冲突)
|
||||
ports:
|
||||
dashboard: 9988
|
||||
camouflage_base: 9200
|
||||
CONFEOF
|
||||
ok "已创建: $LOCAL_CONFIG"
|
||||
else
|
||||
ok "本地配置已存在: $LOCAL_CONFIG"
|
||||
fi
|
||||
|
||||
# ---------- 检测 Node.js ----------
|
||||
NODE_CMD=""
|
||||
if command -v node &>/dev/null; then
|
||||
NODE_CMD="node"
|
||||
NODE_PATH="$(command -v node)"
|
||||
NODE_VER="$(node --version 2>&1)"
|
||||
info "Node.js: $NODE_VER ($NODE_PATH)"
|
||||
else
|
||||
warn "未检测到 Node.js,WorkBuddy 可能无法运行"
|
||||
fi
|
||||
|
||||
# ---------- 创建目录结构 ----------
|
||||
info "创建目录结构..."
|
||||
|
||||
# ---------- 创建 ~/workbuddy-agent-os/agent-local/ 本机专属目录 ----------
|
||||
info "创建本机专属目录 ~/workbuddy-agent-os/agent-local/..."
|
||||
|
||||
LOCAL_DIRS=(
|
||||
"$HOME/workbuddy-agent-os/agent-local/identity"
|
||||
"$HOME/workbuddy-agent-os/agent-local/memory/raw"
|
||||
"$HOME/workbuddy-agent-os/agent-local/memory/vector_db"
|
||||
"$HOME/workbuddy-agent-os/agent-local/memory/daily"
|
||||
"$HOME/workbuddy-agent-os/agent-local/runtime/cache"
|
||||
"$HOME/workbuddy-agent-os/agent-local/materials/web"
|
||||
"$HOME/workbuddy-agent-os/agent-local/materials/video"
|
||||
"$HOME/workbuddy-agent-os/agent-local/materials/audio"
|
||||
"$HOME/workbuddy-agent-os/agent-local/materials/screenshots"
|
||||
"$HOME/workbuddy-agent-os/agent-local/materials/refined_for_inbox"
|
||||
"$HOME/workbuddy-agent-os/agent-local/submissions/inbox"
|
||||
"$HOME/workbuddy-agent-os/agent-local/submissions/memory_export"
|
||||
)
|
||||
|
||||
for dir in "${LOCAL_DIRS[@]}"; do
|
||||
mkdir -p "$dir"
|
||||
done
|
||||
ok "本机专属目录创建完成"
|
||||
|
||||
# ---------- 重建软链接(agent-os-local ↔ agent-os) ----------
|
||||
info "重建软链接..."
|
||||
|
||||
SYMLINKS=(
|
||||
"$AGENT_OS_ROOT/04_memory/long_term/raw:$HOME/workbuddy-agent-os/agent-local/memory/raw"
|
||||
"$AGENT_OS_ROOT/04_memory/vector_db:$HOME/workbuddy-agent-os/agent-local/memory/vector_db"
|
||||
"$AGENT_OS_ROOT/06_runtime/cache:$HOME/workbuddy-agent-os/agent-local/runtime/cache"
|
||||
)
|
||||
|
||||
for entry in "${SYMLINKS[@]}"; do
|
||||
LINK_PATH="${entry%%:*}"
|
||||
TARGET_PATH="${entry##*:}"
|
||||
|
||||
if [ -L "$LINK_PATH" ]; then
|
||||
# 已是软链接,检查目标是否存在
|
||||
if [ -e "$LINK_PATH" ]; then
|
||||
ok "软链接已存在: $(basename $LINK_PATH) → $TARGET_PATH"
|
||||
else
|
||||
warn "软链接目标不存在,重建: $LINK_PATH"
|
||||
rm -f "$LINK_PATH"
|
||||
ln -s "$TARGET_PATH" "$LINK_PATH"
|
||||
fi
|
||||
elif [ -d "$LINK_PATH" ]; then
|
||||
# 是真实目录(首次迁移后的老机器),保留不动
|
||||
ok "真实目录已存在: $LINK_PATH(未软链接)"
|
||||
else
|
||||
# 不存在,创建软链接
|
||||
mkdir -p "$(dirname "$LINK_PATH")"
|
||||
ln -s "$TARGET_PATH" "$LINK_PATH"
|
||||
ok "软链接已创建: $(basename $LINK_PATH) → $TARGET_PATH"
|
||||
fi
|
||||
done
|
||||
|
||||
ok "软链接处理完成"
|
||||
|
||||
# ---------- 创建 agent-os 内部目录结构 ----------
|
||||
info "创建目录结构..."
|
||||
|
||||
DIRS=(
|
||||
"$AGENT_OS_ROOT/03_knowledge/00_inbox"
|
||||
"$AGENT_OS_ROOT/03_knowledge/01_daily"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/cs"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/ai"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/finance"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/law"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/medicine"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/physics"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/math"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/psychology"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/philosophy"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/history"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/engineering"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/design"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/business"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/personal-management"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/personal-insight"
|
||||
"$AGENT_OS_ROOT/03_knowledge/10_concepts/other"
|
||||
"$AGENT_OS_ROOT/03_knowledge/20_methods"
|
||||
"$AGENT_OS_ROOT/03_knowledge/30_facts"
|
||||
"$AGENT_OS_ROOT/03_knowledge/40_references/papers"
|
||||
"$AGENT_OS_ROOT/03_knowledge/40_references/docs"
|
||||
"$AGENT_OS_ROOT/03_knowledge/50_resources"
|
||||
"$AGENT_OS_ROOT/03_knowledge/60_opinions"
|
||||
"$AGENT_OS_ROOT/03_knowledge/90_archive/deprecated"
|
||||
"$AGENT_OS_ROOT/03_knowledge/99_system/templates"
|
||||
"$AGENT_OS_ROOT/03_knowledge/99_system/prompts"
|
||||
"$AGENT_OS_ROOT/03_knowledge/99_system/taxonomies"
|
||||
"$AGENT_OS_ROOT/03_knowledge/99_system/timelines"
|
||||
"$AGENT_OS_ROOT/04_memory/logs"
|
||||
"$AGENT_OS_ROOT/04_memory/daily_summaries"
|
||||
"$AGENT_OS_ROOT/04_memory/memory_backup"
|
||||
"$AGENT_OS_ROOT/05_tools/00_setup"
|
||||
"$AGENT_OS_ROOT/05_tools/01_system"
|
||||
"$AGENT_OS_ROOT/05_tools/01_system/reports"
|
||||
"$AGENT_OS_ROOT/05_tools/02_browser"
|
||||
"$AGENT_OS_ROOT/05_tools/03_ocr"
|
||||
"$AGENT_OS_ROOT/05_tools/04_media"
|
||||
"$AGENT_OS_ROOT/05_tools/05_crawl"
|
||||
"$AGENT_OS_ROOT/05_tools/06_mobile"
|
||||
"$AGENT_OS_ROOT/06_runtime/tasks"
|
||||
"$AGENT_OS_ROOT/07_migration"
|
||||
)
|
||||
|
||||
for dir in "${DIRS[@]}"; do
|
||||
# 跳过已是软链接的路径
|
||||
if [ -L "$dir" ]; then
|
||||
continue
|
||||
fi
|
||||
mkdir -p "$dir"
|
||||
done
|
||||
|
||||
# 空目录添加 .gitkeep(确保坚果云能同步)
|
||||
for dir in "${DIRS[@]}"; do
|
||||
if [ -L "$dir" ]; then
|
||||
continue
|
||||
fi
|
||||
if [ -z "$(ls -A "$dir" 2>/dev/null)" ]; then
|
||||
touch "$dir/.gitkeep"
|
||||
fi
|
||||
done
|
||||
|
||||
ok "目录结构创建完成"
|
||||
|
||||
# ---------- 安装 Python 依赖 ----------
|
||||
if [ -n "$PYTHON_CMD" ]; then
|
||||
info "安装 Python 依赖..."
|
||||
|
||||
# 如果 agent-os venv 不存在,自动创建
|
||||
if [ ! -d "$AGENTOS_VENV" ]; then
|
||||
info "创建 agent-os 虚拟环境..."
|
||||
if [ -x "$MANAGED_PYTHON" ]; then
|
||||
$MANAGED_PYTHON -m venv "$AGENTOS_VENV"
|
||||
else
|
||||
python3 -m venv "$AGENTOS_VENV" 2>/dev/null || warn "无法创建 venv,跳过依赖安装"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 使用 requirements.txt 安装
|
||||
REQ_FILE="$AGENT_OS_ROOT/requirements.txt"
|
||||
if [ -f "$REQ_FILE" ] && [ -x "$AGENTOS_PYTHON" ]; then
|
||||
$AGENTOS_PYTHON -m pip install -r "$REQ_FILE" --quiet 2>/dev/null \
|
||||
&& ok "Python 依赖安装完成(来自 requirements.txt)" \
|
||||
|| warn "部分依赖安装失败,请检查 requirements.txt"
|
||||
else
|
||||
warn "requirements.txt 或 venv 不存在,跳过依赖安装"
|
||||
warn "手动安装: $AGENTOS_PYTHON -m pip install -r $REQ_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ---------- 生成本机身份文件(从模板) ----------
|
||||
info "生成本机身份文件..."
|
||||
TEMPLATE_DIR="$AGENT_OS_ROOT/01_core"
|
||||
LOCAL_IDENTITY_DIR="$HOME/workbuddy-agent-os/agent-local/identity"
|
||||
mkdir -p "$LOCAL_IDENTITY_DIR"
|
||||
|
||||
INIT_TIME="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
|
||||
# 从模板生成 IDENTITY.md(如果模板存在)
|
||||
TEMPLATE_FILE="$TEMPLATE_DIR/IDENTITY.tpl.md"
|
||||
LOCAL_IDENTITY="$LOCAL_IDENTITY_DIR/IDENTITY.md"
|
||||
if [ -f "$TEMPLATE_FILE" ]; then
|
||||
cp "$TEMPLATE_FILE" "$LOCAL_IDENTITY"
|
||||
sed -i.bak \
|
||||
-e "s|__HOSTNAME__|$HOSTNAME|g" \
|
||||
-e "s|__OS_INFO__|$OS_INFO|g" \
|
||||
-e "s|__AGENT_OS_PATH__|$AGENT_OS_ROOT|g" \
|
||||
-e "s|__PYTHON_PATH__|${PYTHON_PATH:-未安装}|g" \
|
||||
-e "s|__NODE_PATH__|${NODE_PATH:-未安装}|g" \
|
||||
-e "s|__INIT_TIME__|$INIT_TIME|g" \
|
||||
"$LOCAL_IDENTITY"
|
||||
rm -f "$LOCAL_IDENTITY.bak"
|
||||
ok "IDENTITY.md 已从模板生成: $LOCAL_IDENTITY"
|
||||
elif [ -f "$TEMPLATE_DIR/IDENTITY.md" ]; then
|
||||
# 兼容: 直接复制现有 IDENTITY.md
|
||||
cp "$TEMPLATE_DIR/IDENTITY.md" "$LOCAL_IDENTITY"
|
||||
ok "IDENTITY.md 已从共享配置复制"
|
||||
fi
|
||||
|
||||
# 生成 HOST_ID.md(含角色选择)
|
||||
HOST_ID_FILE="$LOCAL_IDENTITY_DIR/HOST_ID.md"
|
||||
if [ ! -f "$HOST_ID_FILE" ]; then
|
||||
cat > "$HOST_ID_FILE" << HOSTEOF
|
||||
# HOST_ID.md — 本机标识与角色
|
||||
|
||||
## 主机信息
|
||||
- 主机名: $HOSTNAME
|
||||
- 系统: $OS_INFO
|
||||
- 角色: node
|
||||
- 创建时间: $INIT_TIME
|
||||
|
||||
## 角色说明
|
||||
| 角色 | 权限 | 执行任务 |
|
||||
|------|------|---------|
|
||||
| master | 读写全部协同目录 | 知识提纯/记忆汇总/核心维护 |
|
||||
| maintainer | 写入提交箱 | 内容采集/本地记忆/提交有价值内容 |
|
||||
| node | 只提交 | 信息采集/素材上传 |
|
||||
|
||||
## 能力开关
|
||||
- memory_digestion: true
|
||||
- knowledge_refinement: true
|
||||
- content_collection: true
|
||||
- knowledge_publish: true
|
||||
HOSTEOF
|
||||
ok "HOST_ID.md 已创建(默认角色: node(最低权限),可手动提升为 maintainer/master)"
|
||||
warn "请检查 $HOST_ID_FILE 中的角色设置是否正确"
|
||||
fi
|
||||
|
||||
ok "本机身份文件初始化完成"
|
||||
|
||||
# ---------- 初始化记忆体文件 ----------
|
||||
info "初始化记忆体文件..."
|
||||
|
||||
# L1 关键词索引(存在 agent-local 的 vector_db 中)
|
||||
VECTOR_DB_DIR="$HOME/workbuddy-agent-os/agent-local/memory/vector_db"
|
||||
mkdir -p "$VECTOR_DB_DIR"
|
||||
KEYWORD_INDEX="$VECTOR_DB_DIR/keyword_index.json"
|
||||
if [ ! -f "$KEYWORD_INDEX" ]; then
|
||||
cat > "$KEYWORD_INDEX" << 'EOF'
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"last_updated": "",
|
||||
"entries": []
|
||||
}
|
||||
EOF
|
||||
ok "L1 关键词索引已创建"
|
||||
fi
|
||||
|
||||
# L2 facts.db
|
||||
FACTS_DB="$AGENT_OS_ROOT/04_memory/long_term/facts.db"
|
||||
if [ ! -f "$FACTS_DB" ]; then
|
||||
if [ -n "$PYTHON_CMD" ]; then
|
||||
$PYTHON_CMD -c "
|
||||
import sqlite3
|
||||
conn = sqlite3.connect('$FACTS_DB')
|
||||
c = conn.cursor()
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS facts (
|
||||
id TEXT PRIMARY KEY,
|
||||
subject TEXT NOT NULL,
|
||||
predicate TEXT NOT NULL,
|
||||
object TEXT NOT NULL,
|
||||
confidence REAL DEFAULT 0.7,
|
||||
nature TEXT DEFAULT 'fact',
|
||||
domain TEXT,
|
||||
source TEXT,
|
||||
date_created TEXT,
|
||||
date_modified TEXT,
|
||||
previous_version TEXT,
|
||||
superseded_by TEXT,
|
||||
version INTEGER DEFAULT 1
|
||||
)''')
|
||||
c.execute('''CREATE INDEX IF NOT EXISTS idx_facts_subject ON facts(subject)''')
|
||||
c.execute('''CREATE INDEX IF NOT EXISTS idx_facts_domain ON facts(domain)''')
|
||||
c.execute('''CREATE INDEX IF NOT EXISTS idx_facts_confidence ON facts(confidence)''')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print('L2 facts.db 已创建')
|
||||
"
|
||||
else
|
||||
warn "无法创建 facts.db(需要 Python),请稍后手动运行"
|
||||
fi
|
||||
fi
|
||||
|
||||
ok "记忆体初始化完成"
|
||||
|
||||
# ---------- 检测坚果云同步路径 ----------
|
||||
info "检测坚果云同步..."
|
||||
NUTSTORE_PATHS=(
|
||||
"$HOME/Nutstore Files"
|
||||
"$HOME/坚果云"
|
||||
"$HOME/Nutstore"
|
||||
)
|
||||
NUTSTORE_FOUND=""
|
||||
for p in "${NUTSTORE_PATHS[@]}"; do
|
||||
if [ -d "$p" ]; then
|
||||
NUTSTORE_FOUND="$p"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$NUTSTORE_FOUND" ]; then
|
||||
ok "坚果云目录已找到: $NUTSTORE_FOUND"
|
||||
info "建议将 agent-os 目录放在坚果云同步文件夹内以实现跨机同步"
|
||||
else
|
||||
warn "未检测到坚果云目录,如需跨机同步请手动配置"
|
||||
fi
|
||||
|
||||
# ---------- 检测 oMLX ----------
|
||||
info "检测本地 LLM 引擎..."
|
||||
OMLX_MODELS=""
|
||||
if command -v curl &>/dev/null; then
|
||||
OMLX_RESP=$(curl -s --max-time 3 http://localhost:8000/v1/models -H "Authorization: Bearer omlx" 2>/dev/null || echo "")
|
||||
if echo "$OMLX_RESP" | grep -q '"data"'; then
|
||||
OMLX_MODELS=$(echo "$OMLX_RESP" | python3 -c "import sys,json; d=json.load(sys.stdin); [print(m['id']) for m in d.get('data',[])]" 2>/dev/null || echo "")
|
||||
ok "oMLX 已运行,可用模型:"
|
||||
echo "$OMLX_MODELS" | sed 's/^/ /'
|
||||
else
|
||||
warn "oMLX 未检测到,AI 分析功能将不可用"
|
||||
warn "请手动安装 oMLX: https://omlx.ai"
|
||||
fi
|
||||
else
|
||||
warn "curl 不可用,跳过 oMLX 检测"
|
||||
fi
|
||||
|
||||
# ---------- 完成提示 ----------
|
||||
echo ""
|
||||
echo "========================================="
|
||||
ok "AgentOS 初始化完成!"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
info "换机还原检查清单:"
|
||||
echo " [ ] 1. 检查本机角色: cat $HOME/workbuddy-agent-os/agent-local/identity/HOST_ID.md"
|
||||
echo " [ ] 2. 配置 GitHub/Gitee SSH 密钥(如需同步)"
|
||||
echo " [ ] 3. 安装 oMLX(本地 LLM 引擎)"
|
||||
echo " [ ] 4. 在 WorkBuddy 中配置自动化任务"
|
||||
echo " [ ] 5. 重建向量数据库: agentos rebuild-vector"
|
||||
echo ""
|
||||
info "目录边界说明:"
|
||||
echo " ~/workbuddy-agent-os/agent-sync/ ← Git全量跟踪(知识库/技能/配置/模板)"
|
||||
echo " ~/workbuddy-agent-os/agent-local/ ← 本机专属,不同步"
|
||||
echo " ├── identity/ ← 本机身份(从模板生成)"
|
||||
echo " ├── memory/raw/ ← L3对话原文"
|
||||
echo " ├── memory/vector_db/← 向量数据库(升级后重建)"
|
||||
echo " ├── memory/daily/ ← 本机每日记忆摘要"
|
||||
echo " ├── materials/ ← 原始素材"
|
||||
echo " ├── submissions/ ← 待提交内容(inbox / memory_export)"
|
||||
echo " └── runtime/cache/ ← 临时缓存"
|
||||
echo ""
|
||||
info "架构文档: $AGENT_OS_ROOT/CORE-ARCHITECTURE.md"
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
# setup_env.sh — 一键配置 AgentOS 环境变量
|
||||
# 在任何机器上运行一次即可,后续 init.sh 会自动维护
|
||||
# 用法: bash 00_bootstrap/setup_env.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
info() { echo -e "\033[36m[INFO]\033[0m $1"; }
|
||||
ok() { echo -e "\033[32m[OK]\033[0m $1"; }
|
||||
warn() { echo -e "\033[33m[WARN]\033[0m $1"; }
|
||||
|
||||
# 检测并设置 AGENT_SYNC
|
||||
if [ -z "$AGENT_SYNC" ]; then
|
||||
# 自动检测: 找脚本所在目录的父目录(agent-sync)
|
||||
CANDIDATE="$SCRIPT_DIR/.."
|
||||
if [ -f "$CANDIDATE/ORACLE.yaml" ] || [ -d "$CANDIDATE/01_core" ]; then
|
||||
export AGENT_SYNC="$CANDIDATE"
|
||||
else
|
||||
export AGENT_SYNC="$HOME/workbuddy-agent-os/agent-sync"
|
||||
fi
|
||||
warn "AGENT_SYNC 未设置,自动检测: $AGENT_SYNC"
|
||||
else
|
||||
ok "AGENT_SYNC=$AGENT_SYNC"
|
||||
fi
|
||||
|
||||
# 检测并设置 AGENT_LOCAL
|
||||
if [ -z "$AGENT_LOCAL" ]; then
|
||||
CANDIDATE="${AGENT_SYNC}/agent-local"
|
||||
if [ -d "$CANDIDATE/identity" ]; then
|
||||
export AGENT_LOCAL="$CANDIDATE"
|
||||
else
|
||||
export AGENT_LOCAL="$HOME/workbuddy-agent-os/agent-local"
|
||||
fi
|
||||
warn "AGENT_LOCAL 未设置,自动检测: $AGENT_LOCAL"
|
||||
else
|
||||
ok "AGENT_LOCAL=$AGENT_LOCAL"
|
||||
fi
|
||||
|
||||
# 写入 shell 配置文件
|
||||
for RC_FILE in "$HOME/.zshrc" "$HOME/.bash_profile" "$HOME/.bashrc"; do
|
||||
if [ -f "$RC_FILE" ]; then
|
||||
if ! grep -q "export AGENT_SYNC" "$RC_FILE" 2>/dev/null; then
|
||||
echo "" >> "$RC_FILE"
|
||||
echo "# AgentOS 路径变量(由 setup_env.sh 自动添加)" >> "$RC_FILE"
|
||||
echo "export AGENT_SYNC=\"$AGENT_SYNC\"" >> "$RC_FILE"
|
||||
echo "export AGENT_LOCAL=\"$AGENT_LOCAL\"" >> "$RC_FILE"
|
||||
ok "已写入 $RC_FILE"
|
||||
else
|
||||
ok "$RC_FILE 已有 AGENT_SYNC 设置"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
info "环境变量配置完成。请执行: source ~/.zshrc"
|
||||
echo ""
|
||||
echo " AGENT_SYNC = $AGENT_SYNC"
|
||||
echo " AGENT_LOCAL = $AGENT_LOCAL"
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# 2026-05-14 系统升级脚本:Peekaboo v3 + CloakBrowser
|
||||
# 本脚本放在 git 仓库中,所有机器 git pull 后执行即可
|
||||
set -e
|
||||
|
||||
echo "=== 2026-05-14 系统升级 ==="
|
||||
echo ""
|
||||
|
||||
# 0. 同步代码
|
||||
echo "[0/4] 同步代码..."
|
||||
cd "$(dirname "$0")/../../../"
|
||||
git pull 2>&1 | tail -2
|
||||
bash 00_bootstrap/apply-config.sh 2>&1 | tail -1
|
||||
|
||||
# 1. 安装 Peekaboo
|
||||
echo "[1/4] 安装 Peekaboo..."
|
||||
npm install -g @steipete/peekaboo 2>&1 | tail -1
|
||||
peekaboo permissions status 2>&1 | grep -q "Granted" && echo " ✅ 权限已开启" || echo " ⚠️ 请手动开启权限:系统设置 → 隐私 → 屏幕录制 + 辅助功能"
|
||||
|
||||
# 2. 安装 CloakBrowser
|
||||
echo "[2/4] 安装 CloakBrowser..."
|
||||
pip install cloakbrowser 2>&1 | tail -1
|
||||
echo " ⏳ 首次启动自动下载 ~350MB Chromium..."
|
||||
/Users/chengzige/.workbuddy/binaries/python/envs/agent-os/bin/python3 -c "
|
||||
from cloakbrowser import launch
|
||||
b = launch(humanize=True)
|
||||
print(f' ✅ CloakBrowser 就绪 (Chromium: {b.version})')
|
||||
b.close()
|
||||
" 2>&1
|
||||
|
||||
# 3. Peekaboo token 优化配置
|
||||
echo "[3/4] Peekaboo token 优化..."
|
||||
mkdir -p ~/.peekaboo
|
||||
if [ ! -f ~/.peekaboo/config.json ]; then
|
||||
cat > ~/.peekaboo/config.json << 'CONF'
|
||||
{
|
||||
// Peekaboo 配置 — token 优化版
|
||||
"log": { "level": "error" }, // 只输出错误
|
||||
"output": { "format": "json" } // JSON 输出节省 token
|
||||
}
|
||||
CONF
|
||||
echo " ✅ 已创建 token 优化配置"
|
||||
else
|
||||
echo " ⏭ 配置已存在,跳过"
|
||||
fi
|
||||
|
||||
# 4. 验证
|
||||
echo "[4/4] 验证..."
|
||||
peekaboo --version 2>&1
|
||||
echo " ✅ 升级完成"
|
||||
|
||||
echo ""
|
||||
echo "=== 完成 ==="
|
||||
echo "详细信息见: 99_system/upgrade_notes/2026-05-14_peekaboo_cloakbrowser.md"
|
||||
Reference in New Issue
Block a user