移植自 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 带网关原话 / 正常返回)。
237 lines
8.5 KiB
Bash
237 lines
8.5 KiB
Bash
#!/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 ""
|