2 Commits
v1.0.0 ... main

Author SHA1 Message Date
beb4fb6a76 修复 JSON 解析问题,支持多行格式化JSON输出 2026-07-06 23:12:02 +08:00
a371eada4a 添加根目录显示和重新扫描功能
- 新增 /api/config 接口显示配置信息
- 新增 /api/rescan 接口重新扫描智能体
- 首页显示 OpenClaw 根目录
- 首页添加重新扫描按钮
2026-07-06 22:28:41 +08:00
3 changed files with 144 additions and 7 deletions

View File

@@ -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:

View File

@@ -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():
"""健康检查"""

View File

@@ -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')