Files
edict/install.sh
狼哥 f92c54f371 fix: support OPENCLAW_HOME env var (#275)
fix: support OPENCLAW_HOME for non-standard OpenClaw paths\n\nAdds get_openclaw_home() helper in scripts/utils.py and updates all\ninstall/runtime scripts to resolve OpenClaw home from OPENCLAW_HOME\nenvironment variable with fallback to ~/.openclaw.\n\nCloses #271
2026-04-10 00:55:27 +08:00

440 lines
16 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ══════════════════════════════════════════════════════════════
# 三省六部 · OpenClaw Multi-Agent System 一键安装脚本
# ══════════════════════════════════════════════════════════════
set -e
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OC_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}"
OC_CFG="$OC_HOME/openclaw.json"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
banner() {
echo ""
echo -e "${BLUE}╔══════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ 🏛️ 三省六部 · OpenClaw Multi-Agent ║${NC}"
echo -e "${BLUE}║ 安装向导 ║${NC}"
echo -e "${BLUE}╚══════════════════════════════════════════╝${NC}"
echo ""
}
log() { echo -e "${GREEN}$1${NC}"; }
warn() { echo -e "${YELLOW}⚠️ $1${NC}"; }
error() { echo -e "${RED}$1${NC}"; }
info() { echo -e "${BLUE} $1${NC}"; }
# ── Step 0: 依赖检查 ──────────────────────────────────────────
check_deps() {
info "检查依赖..."
if ! command -v openclaw &>/dev/null; then
error "未找到 openclaw CLI。请先安装 OpenClaw: https://openclaw.ai"
exit 1
fi
log "OpenClaw CLI: $(openclaw --version 2>/dev/null || echo 'OK')"
if ! command -v python3 &>/dev/null; then
error "未找到 python3"
exit 1
fi
log "Python3: $(python3 --version)"
if [ ! -f "$OC_CFG" ]; then
error "未找到 openclaw.json。请先运行 openclaw 完成初始化。"
exit 1
fi
log "openclaw.json: $OC_CFG"
}
# ── Step 0.5: 备份已有 Agent 数据 ──────────────────────────────
backup_existing() {
AGENTS_DIR="$OC_HOME"
BACKUP_DIR="$OC_HOME/backups/pre-install-$(date +%Y%m%d-%H%M%S)"
HAS_EXISTING=false
# 检查是否有已存在的 workspace
for d in "$AGENTS_DIR"/workspace-*/; do
if [ -d "$d" ]; then
HAS_EXISTING=true
break
fi
done
if $HAS_EXISTING; then
info "检测到已有 Agent Workspace自动备份中..."
mkdir -p "$BACKUP_DIR"
# 备份所有 workspace 目录
for d in "$AGENTS_DIR"/workspace-*/; do
if [ -d "$d" ]; then
ws_name=$(basename "$d")
cp -R "$d" "$BACKUP_DIR/$ws_name"
fi
done
# 备份 openclaw.json
if [ -f "$OC_CFG" ]; then
cp "$OC_CFG" "$BACKUP_DIR/openclaw.json"
fi
# 备份 agents 目录agent 注册信息)
if [ -d "$AGENTS_DIR/agents" ]; then
cp -R "$AGENTS_DIR/agents" "$BACKUP_DIR/agents"
fi
log "已备份到: $BACKUP_DIR"
info "如需恢复,运行: cp -R $BACKUP_DIR/workspace-* $AGENTS_DIR/"
fi
}
# ── Step 1: 创建 Workspace ──────────────────────────────────
create_workspaces() {
info "创建 Agent Workspace..."
AGENTS=(taizi zhongshu menxia shangshu hubu libu bingbu xingbu gongbu libu_hr zaochao)
for agent in "${AGENTS[@]}"; do
ws="$OC_HOME/workspace-$agent"
mkdir -p "$ws/skills"
if [ -f "$REPO_DIR/agents/$agent/SOUL.md" ]; then
if [ -f "$ws/SOUL.md" ]; then
# 已存在的 SOUL.md先备份再覆盖
cp "$ws/SOUL.md" "$ws/SOUL.md.bak.$(date +%Y%m%d-%H%M%S)"
warn "已备份旧 SOUL.md → $ws/SOUL.md.bak.*"
fi
sed "s|__REPO_DIR__|$REPO_DIR|g" "$REPO_DIR/agents/$agent/SOUL.md" > "$ws/SOUL.md"
fi
log "Workspace 已创建: $ws"
done
# 通用 AGENTS.md工作协议
for agent in "${AGENTS[@]}"; do
cat > "$OC_HOME/workspace-$agent/AGENTS.md" << 'AGENTS_EOF'
# AGENTS.md · 工作协议
1. 接到任务先回复"已接旨"。
2. 输出必须包含任务ID、结果、证据/文件路径、阻塞项。
3. 需要协作时,回复尚书省请求转派,不跨部直连。
4. 涉及删除/外发动作必须明确标注并等待批准。
AGENTS_EOF
done
}
# ── Step 2: 注册 Agents ─────────────────────────────────────
register_agents() {
info "注册三省六部 Agents..."
# 备份配置
cp "$OC_CFG" "$OC_CFG.bak.sansheng-$(date +%Y%m%d-%H%M%S)"
log "已备份配置: $OC_CFG.bak.*"
python3 << 'PYEOF'
import json, os as _os, pathlib, sys
oc_home = pathlib.Path(_os.environ.get('OPENCLAW_HOME', str(pathlib.Path.home() / '.openclaw'))).expanduser()
cfg_path = oc_home / 'openclaw.json'
cfg = json.loads(cfg_path.read_text())
AGENTS = [
{"id": "taizi", "subagents": {"allowAgents": ["zhongshu"]}},
{"id": "zhongshu", "subagents": {"allowAgents": ["menxia", "shangshu"]}},
{"id": "menxia", "subagents": {"allowAgents": ["shangshu", "zhongshu"]}},
{"id": "shangshu", "subagents": {"allowAgents": ["zhongshu", "menxia", "hubu", "libu", "bingbu", "xingbu", "gongbu", "libu_hr"]}},
{"id": "hubu", "subagents": {"allowAgents": ["shangshu"]}},
{"id": "libu", "subagents": {"allowAgents": ["shangshu"]}},
{"id": "bingbu", "subagents": {"allowAgents": ["shangshu"]}},
{"id": "xingbu", "subagents": {"allowAgents": ["shangshu"]}},
{"id": "gongbu", "subagents": {"allowAgents": ["shangshu"]}},
{"id": "libu_hr", "subagents": {"allowAgents": ["shangshu"]}},
{"id": "zaochao", "subagents": {"allowAgents": []}},
]
agents_cfg = cfg.setdefault('agents', {})
agents_list = agents_cfg.get('list', [])
existing_ids = {a['id'] for a in agents_list}
added = 0
for ag in AGENTS:
ag_id = ag['id']
ws = str(oc_home / f'workspace-{ag_id}')
if ag_id not in existing_ids:
entry = {'id': ag_id, 'workspace': ws, **{k:v for k,v in ag.items() if k!='id'}}
agents_list.append(entry)
added += 1
print(f' + added: {ag_id}')
else:
print(f' ~ exists: {ag_id} (skipped)')
agents_cfg['list'] = agents_list
# Fix #142: 清理 bindings 中的非法字段pattern 不被 gateway 支持)
bindings = cfg.get('bindings', [])
cleaned = 0
for b in bindings:
match = b.get('match', {})
if isinstance(match, dict) and 'pattern' in match:
del match['pattern']
cleaned += 1
print(f' 🧹 cleaned invalid "pattern" from binding: {b.get("agentId", "?")}')
if cleaned:
print(f'Cleaned {cleaned} invalid binding field(s)')
cfg_path.write_text(json.dumps(cfg, ensure_ascii=False, indent=2))
print(f'Done: {added} agents added')
PYEOF
log "Agents 注册完成"
}
# ── Step 3: 初始化 Data ─────────────────────────────────────
init_data() {
info "初始化数据目录..."
mkdir -p "$REPO_DIR/data"
# 初始化空文件
for f in live_status.json agent_config.json model_change_log.json; do
if [ ! -f "$REPO_DIR/data/$f" ]; then
echo '{}' > "$REPO_DIR/data/$f"
fi
done
echo '[]' > "$REPO_DIR/data/pending_model_changes.json"
# 初始任务文件
if [ ! -f "$REPO_DIR/data/tasks_source.json" ]; then
python3 << 'PYEOF'
import json, pathlib
tasks = [
{
"id": "JJC-DEMO-001",
"title": "🎉 系统初始化完成",
"official": "工部尚书",
"org": "工部",
"state": "Done",
"now": "三省六部系统已就绪",
"eta": "-",
"block": "无",
"output": "",
"ac": "系统正常运行",
"flow_log": [
{"at": "2024-01-01T00:00:00Z", "from": "皇上", "to": "中书省", "remark": "下旨初始化三省六部系统"},
{"at": "2024-01-01T00:01:00Z", "from": "中书省", "to": "门下省", "remark": "规划方案提交审核"},
{"at": "2024-01-01T00:02:00Z", "from": "门下省", "to": "尚书省", "remark": "✅ 准奏"},
{"at": "2024-01-01T00:03:00Z", "from": "尚书省", "to": "工部", "remark": "派发:系统初始化"},
{"at": "2024-01-01T00:04:00Z", "from": "工部", "to": "尚书省", "remark": "✅ 完成"},
]
}
]
import os
data_dir = pathlib.Path(os.environ.get('REPO_DIR', '.')) / 'data'
data_dir.mkdir(exist_ok=True)
(data_dir / 'tasks_source.json').write_text(json.dumps(tasks, ensure_ascii=False, indent=2))
print('tasks_source.json 已初始化')
PYEOF
fi
log "数据目录初始化完成: $REPO_DIR/data"
}
# ── Step 3.3: 创建 data 软链接确保数据一致 (Fix #88) ─────────
link_resources() {
info "创建 data/scripts 软链接以确保 Agent 数据一致..."
AGENTS=(taizi zhongshu menxia shangshu hubu libu bingbu xingbu gongbu libu_hr zaochao)
LINKED=0
for agent in "${AGENTS[@]}"; do
ws="$OC_HOME/workspace-$agent"
mkdir -p "$ws"
# 软链接 data 目录:确保各 agent 读写同一份 tasks_source.json
ws_data="$ws/data"
if [ -L "$ws_data" ]; then
: # 已是软链接,跳过
elif [ -d "$ws_data" ]; then
# 已有 data 目录(非符号链接),备份后替换
mv "$ws_data" "${ws_data}.bak.$(date +%Y%m%d-%H%M%S)"
ln -s "$REPO_DIR/data" "$ws_data"
LINKED=$((LINKED + 1))
else
ln -s "$REPO_DIR/data" "$ws_data"
LINKED=$((LINKED + 1))
fi
# 软链接 scripts 目录
ws_scripts="$ws/scripts"
if [ -L "$ws_scripts" ]; then
: # 已是软链接
elif [ -d "$ws_scripts" ]; then
mv "$ws_scripts" "${ws_scripts}.bak.$(date +%Y%m%d-%H%M%S)"
ln -s "$REPO_DIR/scripts" "$ws_scripts"
LINKED=$((LINKED + 1))
else
ln -s "$REPO_DIR/scripts" "$ws_scripts"
LINKED=$((LINKED + 1))
fi
done
# Legacy: workspace-main
ws_main="$OC_HOME/workspace-main"
if [ -d "$ws_main" ]; then
for target in data scripts; do
link_path="$ws_main/$target"
if [ ! -L "$link_path" ]; then
[ -d "$link_path" ] && mv "$link_path" "${link_path}.bak.$(date +%Y%m%d-%H%M%S)"
ln -s "$REPO_DIR/$target" "$link_path"
LINKED=$((LINKED + 1))
fi
done
fi
log "已创建 $LINKED 个软链接data/scripts → 项目目录)"
}
# ── Step 3.5: 设置 Agent 间通信可见性 (Fix #83) ──────────────
setup_visibility() {
info "配置 Agent 间消息可见性..."
if openclaw config set tools.sessions.visibility all 2>/dev/null; then
log "已设置 tools.sessions.visibility=allAgent 间可互相通信)"
else
warn "设置 visibility 失败(可能 openclaw 版本不支持),请手动执行:"
echo " openclaw config set tools.sessions.visibility all"
fi
}
# ── Step 3.5b: 同步 API Key 到所有 Agent ──────────────────────────
sync_auth() {
info "同步 API Key 到所有 Agent..."
# OpenClaw ≥ 3.13 stores credentials in models.json; older versions use
# auth-profiles.json. Try the new name first, then fall back to the old one.
MAIN_AUTH=""
AUTH_FILENAME=""
AGENT_BASE="$OC_HOME/agents/main/agent"
for candidate in models.json auth-profiles.json; do
if [ -f "$AGENT_BASE/$candidate" ]; then
MAIN_AUTH="$AGENT_BASE/$candidate"
AUTH_FILENAME="$candidate"
break
fi
done
# Fallback: search across all agents for either filename
if [ -z "$MAIN_AUTH" ]; then
for candidate in models.json auth-profiles.json; do
MAIN_AUTH=$(find "$OC_HOME/agents" -name "$candidate" -maxdepth 3 2>/dev/null | head -1)
if [ -n "$MAIN_AUTH" ] && [ -f "$MAIN_AUTH" ]; then
AUTH_FILENAME="$candidate"
break
fi
MAIN_AUTH=""
done
fi
if [ -z "$MAIN_AUTH" ] || [ ! -f "$MAIN_AUTH" ]; then
warn "未找到已有的 models.json 或 auth-profiles.json"
warn "请先为任意 Agent 配置 API Key:"
echo " openclaw agents add taizi"
echo " 然后重新运行 install.sh或手动执行:"
echo " bash install.sh --sync-auth"
return
fi
# 检查文件内容是否有效(非空 JSON
if ! python3 -c "import json; d=json.load(open('$MAIN_AUTH')); assert d" 2>/dev/null; then
warn "$AUTH_FILENAME 为空或无效,请先配置 API Key:"
echo " openclaw agents add taizi"
return
fi
AGENTS=(taizi zhongshu menxia shangshu hubu libu bingbu xingbu gongbu libu_hr zaochao)
SYNCED=0
for agent in "${AGENTS[@]}"; do
AGENT_DIR="$OC_HOME/agents/$agent/agent"
if [ -d "$AGENT_DIR" ] || mkdir -p "$AGENT_DIR" 2>/dev/null; then
cp "$MAIN_AUTH" "$AGENT_DIR/$AUTH_FILENAME"
SYNCED=$((SYNCED + 1))
fi
done
log "API Key 已同步到 $SYNCED 个 Agent"
info "来源: $MAIN_AUTH"
}
# ── Step 4: 构建前端 ──────────────────────────────────────────
build_frontend() {
info "构建 React 前端..."
if ! command -v node &>/dev/null; then
warn "未找到 node跳过前端构建。看板将使用预构建版本如果存在"
warn "请安装 Node.js 18+ 后运行: cd edict/frontend && npm install && npm run build"
return
fi
if [ -f "$REPO_DIR/edict/frontend/package.json" ]; then
cd "$REPO_DIR/edict/frontend"
npm install --silent 2>/dev/null || npm install
npm run build 2>/dev/null
cd "$REPO_DIR"
if [ -f "$REPO_DIR/dashboard/dist/index.html" ]; then
log "前端构建完成: dashboard/dist/"
else
warn "前端构建可能失败,请手动检查"
fi
else
warn "未找到 edict/frontend/package.json跳过前端构建"
fi
}
# ── Step 5: 首次数据同步 ────────────────────────────────────
first_sync() {
info "执行首次数据同步..."
cd "$REPO_DIR"
REPO_DIR="$REPO_DIR" python3 scripts/sync_agent_config.py || warn "sync_agent_config 有警告"
python3 scripts/sync_officials_stats.py || warn "sync_officials_stats 有警告"
python3 scripts/refresh_live_data.py || warn "refresh_live_data 有警告"
log "首次同步完成"
}
# ── Step 6: 重启 Gateway ────────────────────────────────────
restart_gateway() {
info "重启 OpenClaw Gateway..."
if openclaw gateway restart 2>/dev/null; then
log "Gateway 重启成功"
else
warn "Gateway 重启失败请手动重启openclaw gateway restart"
fi
}
# ── Main ────────────────────────────────────────────────────
banner
check_deps
backup_existing
create_workspaces
register_agents
init_data
link_resources
setup_visibility
sync_auth
build_frontend
first_sync
restart_gateway
echo ""
echo -e "${GREEN}╔══════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ 🎉 三省六部安装完成! ║${NC}"
echo -e "${GREEN}╚══════════════════════════════════════════════════╝${NC}"
echo ""
echo "下一步:"
echo " 1. 配置 API Key如尚未配置:"
echo " openclaw agents add taizi # 按提示输入 Anthropic API Key"
echo " ./install.sh # 重新运行以同步到所有 Agent"
echo " 2. 启动数据刷新循环: bash scripts/run_loop.sh &"
echo " 3. 启动看板服务器: python3 \"\$REPO_DIR/dashboard/server.py\""
echo " 4. 打开看板: http://127.0.0.1:7891"
echo ""
warn "首次安装必须配置 API Key否则 Agent 会报错"
info "文档: docs/getting-started.md"