312 lines
12 KiB
PowerShell
312 lines
12 KiB
PowerShell
# ══════════════════════════════════════════════════════════════
|
||
# 三省六部 · 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"
|