mirror of
https://mirror.skon.top/github.com/cft0808/edict
synced 2026-04-30 22:10:34 +08:00
- install.sh: clean invalid binding 'pattern' field (Closes #142) - server.py: make dispatch channel configurable via agent_config.json (Closes #139) - server.py: add POST /api/set-dispatch-channel endpoint - sync_agent_config.py: preserve dispatchChannel across syncs - ModelConfig.tsx/api.ts: add dispatch channel selector UI - install.ps1: add Windows PowerShell install script (Closes #136) - Verify gongbu/bingbu SOUL.md are correct and consistent (Closes #131) - Already fixed in prior commit: Closes #127, Closes #124, Closes #132, Closes #125
This commit is contained in:
File diff suppressed because one or more lines are too long
2
dashboard/dist/index.html
vendored
2
dashboard/dist/index.html
vendored
@@ -5,7 +5,7 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>三省六部 · Edict Dashboard</title>
|
||||
<script type="module" crossorigin src="/assets/index-J5u1Q_A5.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DQ-p_wPk.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-NQIHw-yB.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1952,8 +1952,11 @@ def dispatch_for_state(task_id, task, new_state, trigger='state-transition'):
|
||||
'lastDispatchTrigger': trigger,
|
||||
}))
|
||||
return
|
||||
# Fix #139: dispatch channel 可配置(默认 feishu,支持 telegram/wecom/signal 等)
|
||||
_agent_cfg = read_json(DATA / 'agent_config.json', {})
|
||||
_channel = (_agent_cfg.get('dispatchChannel') or 'feishu').strip()
|
||||
cmd = ['openclaw', 'agent', '--agent', agent_id, '-m', msg,
|
||||
'--deliver', '--channel', 'feishu', '--timeout', '300']
|
||||
'--deliver', '--channel', _channel, '--timeout', '300']
|
||||
max_retries = 2
|
||||
err = ''
|
||||
for attempt in range(1, max_retries + 1):
|
||||
@@ -2466,6 +2469,19 @@ class Handler(BaseHTTPRequestHandler):
|
||||
threading.Thread(target=apply_async, daemon=True).start()
|
||||
self.send_json({'ok': True, 'message': f'Queued: {agent_id} → {model}'})
|
||||
|
||||
# Fix #139: 设置派发渠道(feishu/telegram/wecom/signal/tui)
|
||||
elif p == '/api/set-dispatch-channel':
|
||||
channel = body.get('channel', '').strip()
|
||||
allowed = {'feishu', 'telegram', 'wecom', 'signal', 'tui', 'discord', 'slack'}
|
||||
if not channel or channel not in allowed:
|
||||
self.send_json({'ok': False, 'error': f'channel must be one of: {", ".join(sorted(allowed))}'}, 400)
|
||||
return
|
||||
def _set_channel(cfg):
|
||||
cfg['dispatchChannel'] = channel
|
||||
return cfg
|
||||
atomic_json_update(DATA / 'agent_config.json', _set_channel, {})
|
||||
self.send_json({'ok': True, 'message': f'派发渠道已切换为 {channel}'})
|
||||
|
||||
# ── 朝堂议政 POST ──
|
||||
elif p == '/api/court-discuss/start':
|
||||
topic = body.get('topic', '').strip()
|
||||
|
||||
@@ -49,6 +49,8 @@ export const api = {
|
||||
// 操作类
|
||||
setModel: (agentId: string, model: string) =>
|
||||
postJ<ActionResult>(`${API_BASE}/api/set-model`, { agentId, model }),
|
||||
setDispatchChannel: (channel: string) =>
|
||||
postJ<ActionResult>(`${API_BASE}/api/set-dispatch-channel`, { channel }),
|
||||
agentWake: (agentId: string) =>
|
||||
postJ<ActionResult>(`${API_BASE}/api/agent-wake`, { agentId }),
|
||||
taskAction: (taskId: string, action: string, reason: string) =>
|
||||
@@ -190,6 +192,7 @@ export interface KnownModel {
|
||||
export interface AgentConfig {
|
||||
agents: AgentInfo[];
|
||||
knownModels?: KnownModel[];
|
||||
dispatchChannel?: string;
|
||||
}
|
||||
|
||||
export interface ChangeLogEntry {
|
||||
|
||||
@@ -15,6 +15,16 @@ const FALLBACK_MODELS = [
|
||||
{ id: 'copilot/gemini-2.5-pro', l: 'Gemini 2.5 Pro', p: 'Copilot' },
|
||||
];
|
||||
|
||||
const CHANNELS = [
|
||||
{ id: 'feishu', label: '飞书 Feishu' },
|
||||
{ id: 'telegram', label: 'Telegram' },
|
||||
{ id: 'wecom', label: '企业微信 WeCom' },
|
||||
{ id: 'discord', label: 'Discord' },
|
||||
{ id: 'slack', label: 'Slack' },
|
||||
{ id: 'signal', label: 'Signal' },
|
||||
{ id: 'tui', label: 'TUI (终端)' },
|
||||
];
|
||||
|
||||
export default function ModelConfig() {
|
||||
const agentConfig = useStore((s) => s.agentConfig);
|
||||
const changeLog = useStore((s) => s.changeLog);
|
||||
@@ -23,6 +33,8 @@ export default function ModelConfig() {
|
||||
|
||||
const [selMap, setSelMap] = useState<Record<string, string>>({});
|
||||
const [statusMap, setStatusMap] = useState<Record<string, { cls: string; text: string }>>({});
|
||||
const [channelSel, setChannelSel] = useState('feishu');
|
||||
const [channelStatus, setChannelStatus] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
loadAgentConfig();
|
||||
@@ -36,6 +48,9 @@ export default function ModelConfig() {
|
||||
});
|
||||
setSelMap(m);
|
||||
}
|
||||
if (agentConfig?.dispatchChannel) {
|
||||
setChannelSel(agentConfig.dispatchChannel);
|
||||
}
|
||||
}, [agentConfig]);
|
||||
|
||||
if (!agentConfig?.agents) {
|
||||
@@ -116,6 +131,30 @@ export default function ModelConfig() {
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Dispatch Channel 配置 */}
|
||||
<div style={{ marginTop: 24, marginBottom: 8 }}>
|
||||
<div className="sec-title">派发渠道</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 0' }}>
|
||||
<select className="msel" value={channelSel} onChange={(e) => setChannelSel(e.target.value)}
|
||||
style={{ maxWidth: 220 }}>
|
||||
{CHANNELS.map((ch) => (
|
||||
<option key={ch.id} value={ch.id}>{ch.label}</option>
|
||||
))}
|
||||
</select>
|
||||
<button className="btn btn-p" disabled={channelSel === (agentConfig?.dispatchChannel || 'feishu')}
|
||||
onClick={async () => {
|
||||
try {
|
||||
const r = await api.setDispatchChannel(channelSel);
|
||||
if (r.ok) { setChannelStatus('✅ 已保存'); toast('派发渠道已切换', 'ok'); loadAgentConfig(); }
|
||||
else setChannelStatus('❌ ' + (r.error || '失败'));
|
||||
} catch { setChannelStatus('❌ 无法连接'); }
|
||||
setTimeout(() => setChannelStatus(''), 3000);
|
||||
}}>应用</button>
|
||||
{channelStatus && <span style={{ fontSize: 12, color: channelStatus.startsWith('✅') ? 'var(--success)' : 'var(--danger)' }}>{channelStatus}</span>}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: 'var(--muted)' }}>自动派发时使用的 OpenClaw 通知渠道(需已在 openclaw.json 中配置对应 channel)</div>
|
||||
</div>
|
||||
|
||||
{/* Change Log */}
|
||||
<div style={{ marginTop: 24 }}>
|
||||
<div className="sec-title">变更日志</div>
|
||||
|
||||
308
install.ps1
Normal file
308
install.ps1
Normal file
@@ -0,0 +1,308 @@
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
# 三省六部 · OpenClaw Multi-Agent System 一键安装脚本 (Windows)
|
||||
# PowerShell 版本 — 对应 install.sh
|
||||
# ══════════════════════════════════════════════════════════════
|
||||
#Requires -Version 5.1
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$REPO_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$OC_HOME = 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 python3 -ErrorAction SilentlyContinue
|
||||
if (-not $py) {
|
||||
$py = Get-Command python -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
|
||||
|
||||
cfg_path = pathlib.Path(os.environ['USERPROFILE']) / '.openclaw' / '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(pathlib.Path(os.environ['USERPROFILE']) / f'.openclaw/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. 启动数据刷新循环: Start-Process python3 -ArgumentList 'scripts/run_loop.sh'"
|
||||
Write-Host " 3. 启动看板服务器: python3 dashboard/server.py"
|
||||
Write-Host " 4. 打开看板: http://127.0.0.1:7891"
|
||||
Write-Host ""
|
||||
Warn "首次安装必须配置 API Key,否则 Agent 会报错"
|
||||
Info "文档: docs/getting-started.md"
|
||||
13
install.sh
13
install.sh
@@ -165,6 +165,19 @@ for ag in AGENTS:
|
||||
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
|
||||
|
||||
@@ -165,10 +165,20 @@ def main():
|
||||
'isDefaultModel': True,
|
||||
})
|
||||
|
||||
# 保留已有的 dispatchChannel 配置 (Fix #139)
|
||||
existing_cfg = {}
|
||||
cfg_path = DATA / 'agent_config.json'
|
||||
if cfg_path.exists():
|
||||
try:
|
||||
existing_cfg = json.loads(cfg_path.read_text())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
payload = {
|
||||
'generatedAt': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'defaultModel': default_model,
|
||||
'knownModels': merged_models,
|
||||
'dispatchChannel': existing_cfg.get('dispatchChannel', 'feishu'),
|
||||
'agents': result,
|
||||
}
|
||||
DATA.mkdir(exist_ok=True)
|
||||
|
||||
Reference in New Issue
Block a user