Files
2026-07-13 13:32:32 +08:00

312 lines
12 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters
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.
# ══════════════════════════════════════════════════════════════
# 三省六部 · OpenClaw Multi-Agent System 一键安装脚本 (Windows)
# PowerShell 版本 — 对应 install.sh
# ══════════════════════════════════════════════════════════════
#Requires -Version 5.1
$ErrorActionPreference = "Stop"
$REPO_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
$OC_HOME = if ($env:OPENCLAW_HOME) { $env:OPENCLAW_HOME } else { Join-Path $env:USERPROFILE ".openclaw" }
$OC_CFG = Join-Path $OC_HOME "openclaw.json"
function Write-Banner {
Write-Host ""
Write-Host "╔══════════════════════════════════════════╗" -ForegroundColor Blue
Write-Host "║ 🏛️ 三省六部 · OpenClaw Multi-Agent ║" -ForegroundColor Blue
Write-Host "║ 安装向导 (Windows) ║" -ForegroundColor Blue
Write-Host "╚══════════════════════════════════════════╝" -ForegroundColor Blue
Write-Host ""
}
function Log { param($msg) Write-Host "✅ $msg" -ForegroundColor Green }
function Warn { param($msg) Write-Host "⚠️ $msg" -ForegroundColor Yellow }
function Error { param($msg) Write-Host "❌ $msg" -ForegroundColor Red }
function Info { param($msg) Write-Host "$msg" -ForegroundColor Blue }
# ── Step 0: 依赖检查 ──
function Check-Deps {
Info "检查依赖..."
$oc = Get-Command openclaw -ErrorAction SilentlyContinue
if (-not $oc) {
Error "未找到 openclaw CLI。请先安装 OpenClaw: https://openclaw.ai"
exit 1
}
Log "OpenClaw CLI: OK"
$py = Get-Command python -ErrorAction SilentlyContinue
if (-not $py) {
$py = Get-Command python3 -ErrorAction SilentlyContinue
}
if (-not $py) {
Error "未找到 python3 或 python"
exit 1
}
$global:PYTHON = $py.Source
Log "Python: $($global:PYTHON)"
if (-not (Test-Path $OC_CFG)) {
Error "未找到 openclaw.json。请先运行 openclaw 完成初始化。"
exit 1
}
Log "openclaw.json: $OC_CFG"
}
# ── Step 0.5: 备份已有 Agent 数据 ──
function Backup-Existing {
$hasExisting = Get-ChildItem -Path $OC_HOME -Directory -Filter "workspace-*" -ErrorAction SilentlyContinue
if ($hasExisting) {
Info "检测到已有 Agent Workspace,自动备份中..."
$ts = Get-Date -Format "yyyyMMdd-HHmmss"
$backupDir = Join-Path $OC_HOME "backups\pre-install-$ts"
New-Item -ItemType Directory -Path $backupDir -Force | Out-Null
Get-ChildItem -Path $OC_HOME -Directory -Filter "workspace-*" | ForEach-Object {
Copy-Item -Path $_.FullName -Destination (Join-Path $backupDir $_.Name) -Recurse
}
if (Test-Path $OC_CFG) {
Copy-Item $OC_CFG (Join-Path $backupDir "openclaw.json")
}
Log "已备份到: $backupDir"
}
}
# ── Step 1: 创建 Workspace ──
function Create-Workspaces {
Info "创建 Agent Workspace..."
$agents = @("taizi","zhongshu","menxia","shangshu","hubu","libu","bingbu","xingbu","gongbu","libu_hr","zaochao")
foreach ($agent in $agents) {
$ws = Join-Path $OC_HOME "workspace-$agent"
New-Item -ItemType Directory -Path (Join-Path $ws "skills") -Force | Out-Null
$soulSrc = Join-Path $REPO_DIR "agents\$agent\SOUL.md"
$soulDst = Join-Path $ws "SOUL.md"
if (Test-Path $soulSrc) {
if (Test-Path $soulDst) {
$ts = Get-Date -Format "yyyyMMdd-HHmmss"
Copy-Item $soulDst "$soulDst.bak.$ts"
Warn "已备份旧 SOUL.md → $soulDst.bak.$ts"
}
$content = (Get-Content $soulSrc -Raw) -replace "__REPO_DIR__", $REPO_DIR
Set-Content -Path $soulDst -Value $content -Encoding UTF8
}
Log "Workspace 已创建: $ws"
# AGENTS.md
$agentsMd = @"
# AGENTS.md · 工作协议
1. 接到任务先回复"已接旨"。
2. 输出必须包含:任务ID、结果、证据/文件路径、阻塞项。
3. 需要协作时,回复尚书省请求转派,不跨部直连。
4. 涉及删除/外发动作必须明确标注并等待批准。
"@
Set-Content -Path (Join-Path $ws "AGENTS.md") -Value $agentsMd -Encoding UTF8
}
}
# ── Step 2: 注册 Agents ──
function Register-Agents {
Info "注册三省六部 Agents..."
$ts = Get-Date -Format "yyyyMMdd-HHmmss"
Copy-Item $OC_CFG "$OC_CFG.bak.sansheng-$ts"
Log "已备份配置: $OC_CFG.bak.*"
$pyScript = @"
import json, pathlib, sys, os
oc_home = pathlib.Path(
os.environ.get('OPENCLAW_HOME', str(pathlib.Path(os.environ['USERPROFILE']) / '.openclaw'))
).expanduser()
cfg_path = oc_home / 'openclaw.json'
cfg = json.loads(cfg_path.read_text(encoding='utf-8'))
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: clean invalid binding pattern
bindings = cfg.get('bindings', [])
for b in bindings:
match = b.get('match', {})
if isinstance(match, dict) and 'pattern' in match:
del match['pattern']
print(f' cleaned invalid pattern from binding: {b.get("agentId", "?")}')
cfg_path.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), encoding='utf-8')
print(f'Done: {added} agents added')
"@
& $global:PYTHON -c $pyScript
Log "Agents 注册完成"
}
# ── Step 3: 初始化 Data ──
function Init-Data {
Info "初始化数据目录..."
$dataDir = Join-Path $REPO_DIR "data"
New-Item -ItemType Directory -Path $dataDir -Force | Out-Null
foreach ($f in @("live_status.json","agent_config.json","model_change_log.json")) {
$fp = Join-Path $dataDir $f
if (-not (Test-Path $fp)) { Set-Content $fp "{}" -Encoding UTF8 }
}
Set-Content (Join-Path $dataDir "pending_model_changes.json") "[]" -Encoding UTF8
Log "数据目录初始化完成"
}
# ── Step 3.3: 创建 data/scripts 目录连接 (Junction) ──
function Link-Resources {
Info "创建 data/scripts 目录连接..."
$linked = 0
$agents = @("taizi","zhongshu","menxia","shangshu","hubu","libu","bingbu","xingbu","gongbu","libu_hr","zaochao")
foreach ($agent in $agents) {
$ws = Join-Path $OC_HOME "workspace-$agent"
New-Item -ItemType Directory -Path $ws -Force | Out-Null
# data 目录
$wsData = Join-Path $ws "data"
$srcData = Join-Path $REPO_DIR "data"
if (-not (Test-Path $wsData)) {
cmd /c mklink /J "$wsData" "$srcData" | Out-Null
$linked++
} elseif (-not ((Get-Item $wsData).Attributes -band [IO.FileAttributes]::ReparsePoint)) {
$ts = Get-Date -Format "yyyyMMdd-HHmmss"
Rename-Item $wsData "$wsData.bak.$ts"
cmd /c mklink /J "$wsData" "$srcData" | Out-Null
$linked++
}
# scripts 目录
$wsScripts = Join-Path $ws "scripts"
$srcScripts = Join-Path $REPO_DIR "scripts"
if (-not (Test-Path $wsScripts)) {
cmd /c mklink /J "$wsScripts" "$srcScripts" | Out-Null
$linked++
} elseif (-not ((Get-Item $wsScripts).Attributes -band [IO.FileAttributes]::ReparsePoint)) {
$ts = Get-Date -Format "yyyyMMdd-HHmmss"
Rename-Item $wsScripts "$wsScripts.bak.$ts"
cmd /c mklink /J "$wsScripts" "$srcScripts" | Out-Null
$linked++
}
}
Log "已创建 $linked 个目录连接 (data/scripts → 项目目录)"
}
# ── Step 3.5: 设置 Agent 间通信可见性 ──
function Setup-Visibility {
Info "配置 Agent 间消息可见性..."
try {
openclaw config set tools.sessions.visibility all 2>$null
Log "已设置 tools.sessions.visibility=all"
} catch {
Warn "设置 visibility 失败,请手动执行: openclaw config set tools.sessions.visibility all"
}
}
# ── Step 4: 构建前端 ──
function Build-Frontend {
Info "构建 React 前端..."
$node = Get-Command node -ErrorAction SilentlyContinue
if (-not $node) {
Warn "未找到 node,跳过前端构建。"
Warn "请安装 Node.js 18+ 后运行: cd edict\frontend && npm install && npm run build"
return
}
$pkgJson = Join-Path $REPO_DIR "edict\frontend\package.json"
if (Test-Path $pkgJson) {
Push-Location (Join-Path $REPO_DIR "edict\frontend")
npm install --silent 2>$null
npm run build 2>$null
Pop-Location
$indexHtml = Join-Path $REPO_DIR "dashboard\dist\index.html"
if (Test-Path $indexHtml) {
Log "前端构建完成: dashboard\dist\"
} else {
Warn "前端构建可能失败,请手动检查"
}
}
}
# ── Step 5: 首次数据同步 ──
function First-Sync {
Info "执行首次数据同步..."
Push-Location $REPO_DIR
$env:REPO_DIR = $REPO_DIR
try { & $global:PYTHON scripts/sync_agent_config.py } catch { Warn "sync_agent_config 有警告" }
try { & $global:PYTHON scripts/sync_officials_stats.py } catch { Warn "sync_officials_stats 有警告" }
try { & $global:PYTHON scripts/refresh_live_data.py } catch { Warn "refresh_live_data 有警告" }
Pop-Location
Log "首次同步完成"
}
# ── Step 6: 重启 Gateway ──
function Restart-Gateway {
Info "重启 OpenClaw Gateway..."
try {
openclaw gateway restart 2>$null
Log "Gateway 重启成功"
} catch {
Warn "Gateway 重启失败,请手动重启: openclaw gateway restart"
}
}
# ── Main ──
Write-Banner
Check-Deps
Backup-Existing
Create-Workspaces
Register-Agents
Init-Data
Link-Resources
Setup-Visibility
Build-Frontend
First-Sync
Restart-Gateway
Write-Host ""
Write-Host "╔══════════════════════════════════════════════════╗" -ForegroundColor Green
Write-Host "║ 🎉 三省六部安装完成! ║" -ForegroundColor Green
Write-Host "╚══════════════════════════════════════════════════╝" -ForegroundColor Green
Write-Host ""
Write-Host "下一步:"
Write-Host " 1. 配置 API Key(如尚未配置):"
Write-Host " openclaw agents add taizi # 按提示输入 Anthropic API Key"
Write-Host " .\install.ps1 # 重新运行以同步到所有 Agent"
Write-Host " 2. 启动数据刷新循环: bash scripts/run_loop.sh"
Write-Host " 3. 启动看板服务器: python dashboard/server.py"
Write-Host " 4. 打开看板: http://127.0.0.1:7891"
Write-Host ""
Warn "首次安装必须配置 API Key,否则 Agent 会报错"
Info "文档: docs/getting-started.md"