Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| beb4fb6a76 | |||
| a371eada4a |
@@ -64,13 +64,36 @@ class OpenClawClient:
|
||||
raise Exception(f"CLI error: {result.stderr}")
|
||||
|
||||
# 解析 JSON 输出(跳过警告)
|
||||
lines = result.stdout.strip().split("\n")
|
||||
json_lines = [l for l in lines if l.startswith("{") or l.startswith("[")]
|
||||
# OpenClaw 输出格式化的多行 JSON,需要提取完整的 JSON 对象
|
||||
output = result.stdout.strip()
|
||||
|
||||
if json_lines:
|
||||
return json.loads(json_lines[-1])
|
||||
# 找到 JSON 开始位置
|
||||
json_start = -1
|
||||
for i, char in enumerate(output):
|
||||
if char == '{' or char == '[':
|
||||
json_start = i
|
||||
break
|
||||
|
||||
return {}
|
||||
if json_start == -1:
|
||||
return {}
|
||||
|
||||
# 提取 JSON 部分
|
||||
json_str = output[json_start:]
|
||||
|
||||
try:
|
||||
return json.loads(json_str)
|
||||
except json.JSONDecodeError:
|
||||
# 如果失败,尝试其他方法
|
||||
lines = output.split("\n")
|
||||
json_lines = [l for l in lines if l.strip().startswith("{") or l.strip().startswith("[")]
|
||||
if json_lines:
|
||||
# 尝试解析每一行
|
||||
for line in json_lines:
|
||||
try:
|
||||
return json.loads(line)
|
||||
except:
|
||||
continue
|
||||
return {}
|
||||
except subprocess.TimeoutExpired:
|
||||
raise Exception(f"Timeout running: {cmd}")
|
||||
except json.JSONDecodeError as e:
|
||||
|
||||
@@ -46,6 +46,74 @@ def get_client() -> OpenClawClient:
|
||||
|
||||
# ============== API 路由 ==============
|
||||
|
||||
@app.get("/api/config")
|
||||
async def api_config():
|
||||
"""获取 OpenClaw 配置信息"""
|
||||
try:
|
||||
client = get_client()
|
||||
config = client.get_config()
|
||||
|
||||
# 提取关键信息
|
||||
agents_config = config.get("agents", {})
|
||||
defaults = agents_config.get("defaults", {})
|
||||
|
||||
# 查找所有智能体目录
|
||||
agents_list = agents_config.get("list", [])
|
||||
agent_dirs = []
|
||||
for agent in agents_list:
|
||||
if agent.get("agentDir"):
|
||||
agent_dirs.append(agent.get("agentDir"))
|
||||
|
||||
return {
|
||||
"config_path": config.get("_path", "/home/openclaw/.openclaw/openclaw.json"),
|
||||
"workspace_root": defaults.get("workspace", "/home/openclaw/.openclaw/workspace"),
|
||||
"agents_dir": "/home/openclaw/.openclaw", # OpenClaw 根目录
|
||||
"agents_count": len(agents_list),
|
||||
"agent_dirs": agent_dirs,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/rescan")
|
||||
async def api_rescan():
|
||||
"""重新扫描智能体目录"""
|
||||
try:
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
# 调用 openclaw gateway 命令重新加载配置
|
||||
result = subprocess.run(
|
||||
["openclaw", "gateway", "call", "agents.list", "--json"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise Exception(f"扫描失败: {result.stderr}")
|
||||
|
||||
# 解析结果
|
||||
import json
|
||||
output_lines = result.stdout.strip().split("\n")
|
||||
json_line = [l for l in output_lines if l.startswith("{") or l.startswith("[")]
|
||||
|
||||
if json_line:
|
||||
data = json.loads(json_line[-1])
|
||||
agents = data.get("agents", [])
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"agents_count": len(agents),
|
||||
"message": f"扫描完成,找到 {len(agents)} 个智能体",
|
||||
}
|
||||
|
||||
|
||||
return {"success": True, "agents_count": 0, "message": "扫描完成"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/health")
|
||||
async def api_health():
|
||||
"""健康检查"""
|
||||
|
||||
@@ -18,8 +18,15 @@
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-white">🤖 OpenClaw Extra</h1>
|
||||
<p class="text-gray-400 mt-1">智能体管理面板</p>
|
||||
<div x-show="config" class="mt-2 text-sm">
|
||||
<span class="text-gray-500">根目录:</span>
|
||||
<span class="text-blue-400 font-mono" x-text="config?.agents_dir || '-'"></span>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="flex gap-4">
|
||||
<button @click="rescan()" class="px-4 py-2 bg-green-600 hover:bg-green-700 rounded-lg transition">
|
||||
🔄 重新扫描
|
||||
</button>
|
||||
<a href="/models" class="px-4 py-2 bg-gray-800 hover:bg-gray-700 rounded-lg transition">📊 模型列表</a>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -31,6 +38,13 @@
|
||||
<span x-text="error"></span>
|
||||
</div>
|
||||
|
||||
<!-- Rescan Message -->
|
||||
<div x-show="rescanMessage" class="mb-4 p-4 bg-green-900/50 border border-green-700 rounded-lg">
|
||||
<span class="text-green-300">✓ </span>
|
||||
<span x-text="rescanMessage"></span>
|
||||
<button @click="rescanMessage=''" class="ml-2 text-gray-400 hover:text-white">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||
<div class="bg-gray-800 rounded-xl p-6">
|
||||
@@ -104,14 +118,19 @@
|
||||
loading: true,
|
||||
error: null,
|
||||
gatewayOk: false,
|
||||
config: null,
|
||||
rescanMessage: '',
|
||||
|
||||
get totalSessions() {
|
||||
return this.agents.reduce((sum, a) => sum + a.sessions_count, 0)
|
||||
},
|
||||
|
||||
async init() {
|
||||
await this.loadAgents()
|
||||
await this.checkHealth()
|
||||
await Promise.all([
|
||||
this.loadAgents(),
|
||||
this.loadConfig(),
|
||||
this.checkHealth()
|
||||
])
|
||||
},
|
||||
|
||||
async loadAgents() {
|
||||
@@ -126,6 +145,33 @@
|
||||
}
|
||||
},
|
||||
|
||||
async loadConfig() {
|
||||
try {
|
||||
const res = await fetch('/api/config')
|
||||
this.config = await res.json()
|
||||
} catch (e) {
|
||||
console.error('Load config error:', e)
|
||||
}
|
||||
},
|
||||
|
||||
async rescan() {
|
||||
try {
|
||||
this.rescanMessage = '正在扫描...'
|
||||
const res = await fetch('/api/rescan', { method: 'POST' })
|
||||
const data = await res.json()
|
||||
|
||||
if (data.success) {
|
||||
this.rescanMessage = data.message
|
||||
// 重新加载智能体列表
|
||||
await this.loadAgents()
|
||||
} else {
|
||||
this.error = data.message || '扫描失败'
|
||||
}
|
||||
} catch (e) {
|
||||
this.error = '扫描失败: ' + e.message
|
||||
}
|
||||
},
|
||||
|
||||
async checkHealth() {
|
||||
try {
|
||||
const res = await fetch('/api/health')
|
||||
|
||||
Reference in New Issue
Block a user