openclaw-agent-dashboard 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +56 -0
- package/README.md +302 -0
- package/docs/CHANGELOG_AGENT_MODIFICATIONS.md +132 -0
- package/docs/RELEASE-LATEST.md +189 -0
- package/docs/RELEASE-MODEL-CONFIG.md +95 -0
- package/docs/release-guide.md +259 -0
- package/docs/release-operations-manual.md +167 -0
- package/docs/specs/tr3-install-system.md +580 -0
- package/docs/windows-collaboration-model-paths-troubleshooting.md +0 -0
- package/frontend/index.html +12 -0
- package/frontend/package-lock.json +1240 -0
- package/frontend/package.json +19 -0
- package/frontend/src/App.vue +331 -0
- package/frontend/src/components/AgentCard.vue +796 -0
- package/frontend/src/components/AgentConfigPanel.vue +539 -0
- package/frontend/src/components/AgentDetailPanel.vue +738 -0
- package/frontend/src/components/ErrorAnalysisView.vue +546 -0
- package/frontend/src/components/ErrorCenterPanel.vue +844 -0
- package/frontend/src/components/PerformanceMonitor.vue +515 -0
- package/frontend/src/components/SettingsPanel.vue +236 -0
- package/frontend/src/components/TokenAnalysisPanel.vue +683 -0
- package/frontend/src/components/chain/ChainEdge.vue +85 -0
- package/frontend/src/components/chain/ChainNode.vue +166 -0
- package/frontend/src/components/chain/TaskChainView.vue +425 -0
- package/frontend/src/components/chain/index.ts +3 -0
- package/frontend/src/components/chain/types.ts +70 -0
- package/frontend/src/components/collaboration/CollaborationFlowSection.vue +1032 -0
- package/frontend/src/components/collaboration/CollaborationFlowWrapper.vue +113 -0
- package/frontend/src/components/performance/PerformancePanel.vue +119 -0
- package/frontend/src/components/performance/PerformanceSection.vue +1137 -0
- package/frontend/src/components/tasks/TaskStatusSection.vue +973 -0
- package/frontend/src/components/timeline/TimelineConnector.vue +31 -0
- package/frontend/src/components/timeline/TimelineRound.vue +135 -0
- package/frontend/src/components/timeline/TimelineStep.vue +691 -0
- package/frontend/src/components/timeline/TimelineToolLink.vue +109 -0
- package/frontend/src/components/timeline/TimelineView.vue +540 -0
- package/frontend/src/components/timeline/index.ts +5 -0
- package/frontend/src/components/timeline/types.ts +120 -0
- package/frontend/src/composables/index.ts +7 -0
- package/frontend/src/composables/useDebounce.ts +48 -0
- package/frontend/src/composables/useRealtime.ts +52 -0
- package/frontend/src/composables/useState.ts +52 -0
- package/frontend/src/composables/useThrottle.ts +46 -0
- package/frontend/src/composables/useVirtualScroll.ts +106 -0
- package/frontend/src/main.ts +4 -0
- package/frontend/src/managers/EventDispatcher.ts +127 -0
- package/frontend/src/managers/RealtimeDataManager.ts +293 -0
- package/frontend/src/managers/StateManager.ts +128 -0
- package/frontend/src/managers/index.ts +5 -0
- package/frontend/src/types/collaboration.ts +135 -0
- package/frontend/src/types/index.ts +20 -0
- package/frontend/src/types/performance.ts +105 -0
- package/frontend/src/types/task.ts +38 -0
- package/frontend/vite.config.ts +18 -0
- package/package.json +22 -0
- package/plugin/README.md +99 -0
- package/plugin/config.json.example +1 -0
- package/plugin/index.js +250 -0
- package/plugin/openclaw.plugin.json +17 -0
- package/plugin/package.json +21 -0
- package/scripts/build-plugin.js +67 -0
- package/scripts/bundle.sh +62 -0
- package/scripts/install-plugin.sh +162 -0
- package/scripts/install-python-deps.js +346 -0
- package/scripts/install-python-deps.sh +226 -0
- package/scripts/install.js +512 -0
- package/scripts/install.sh +367 -0
- package/scripts/lib/common.js +490 -0
- package/scripts/lib/common.sh +137 -0
- package/scripts/release-pack.sh +110 -0
- package/scripts/start.js +50 -0
- package/scripts/test_available_models.py +284 -0
- package/scripts/test_websocket_ping.py +44 -0
- package/src/backend/agents.py +73 -0
- package/src/backend/api/__init__.py +1 -0
- package/src/backend/api/agent_config_api.py +90 -0
- package/src/backend/api/agents.py +73 -0
- package/src/backend/api/agents_config.py +75 -0
- package/src/backend/api/chains.py +126 -0
- package/src/backend/api/collaboration.py +902 -0
- package/src/backend/api/debug_paths.py +39 -0
- package/src/backend/api/error_analysis.py +146 -0
- package/src/backend/api/errors.py +281 -0
- package/src/backend/api/performance.py +784 -0
- package/src/backend/api/subagents.py +770 -0
- package/src/backend/api/timeline.py +144 -0
- package/src/backend/api/websocket.py +251 -0
- package/src/backend/collaboration.py +405 -0
- package/src/backend/data/__init__.py +1 -0
- package/src/backend/data/agent_config_manager.py +270 -0
- package/src/backend/data/chain_reader.py +299 -0
- package/src/backend/data/config_reader.py +153 -0
- package/src/backend/data/error_analyzer.py +430 -0
- package/src/backend/data/session_reader.py +445 -0
- package/src/backend/data/subagent_reader.py +244 -0
- package/src/backend/data/task_history.py +118 -0
- package/src/backend/data/timeline_reader.py +981 -0
- package/src/backend/errors.py +63 -0
- package/src/backend/main.py +89 -0
- package/src/backend/mechanism_reader.py +131 -0
- package/src/backend/mechanisms.py +32 -0
- package/src/backend/performance.py +474 -0
- package/src/backend/requirements.txt +5 -0
- package/src/backend/session_reader.py +238 -0
- package/src/backend/status/__init__.py +1 -0
- package/src/backend/status/error_detector.py +122 -0
- package/src/backend/status/status_calculator.py +301 -0
- package/src/backend/status_calculator.py +121 -0
- package/src/backend/subagent_reader.py +229 -0
- package/src/backend/watchers/__init__.py +4 -0
- package/src/backend/watchers/file_watcher.py +159 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent API 路由
|
|
3
|
+
"""
|
|
4
|
+
from fastapi import APIRouter
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
sys.path.append(str(Path(__file__).parent.parent))
|
|
10
|
+
|
|
11
|
+
from status.status_calculator import (
|
|
12
|
+
get_agents_with_status,
|
|
13
|
+
format_last_active
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
router = APIRouter()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AgentStatus(BaseModel):
|
|
20
|
+
id: str
|
|
21
|
+
name: str
|
|
22
|
+
role: str
|
|
23
|
+
status: str # idle/working/down
|
|
24
|
+
currentTask: Optional[str] = None
|
|
25
|
+
lastActiveAt: Optional[int] = None
|
|
26
|
+
lastActiveFormatted: Optional[str] = None
|
|
27
|
+
error: Optional[dict] = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@router.get("/agents", response_model=List[AgentStatus])
|
|
31
|
+
async def get_agents():
|
|
32
|
+
"""获取所有 Agent 列表及状态"""
|
|
33
|
+
agents = get_agents_with_status()
|
|
34
|
+
|
|
35
|
+
# 格式化最后活跃时间
|
|
36
|
+
for agent in agents:
|
|
37
|
+
if agent.get('lastActiveAt'):
|
|
38
|
+
agent['lastActiveFormatted'] = format_last_active(agent['lastActiveAt'])
|
|
39
|
+
|
|
40
|
+
return agents
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@router.get("/agents/{agent_id}", response_model=AgentStatus)
|
|
44
|
+
async def get_agent(agent_id: str):
|
|
45
|
+
"""获取单个 Agent 详情"""
|
|
46
|
+
agents = get_agents_with_status()
|
|
47
|
+
|
|
48
|
+
for agent in agents:
|
|
49
|
+
if agent['id'] == agent_id:
|
|
50
|
+
if agent.get('lastActiveAt'):
|
|
51
|
+
agent['lastActiveFormatted'] = format_last_active(agent['lastActiveAt'])
|
|
52
|
+
return agent
|
|
53
|
+
|
|
54
|
+
from fastapi import HTTPException
|
|
55
|
+
raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@router.get("/agents/{agent_id}/output")
|
|
59
|
+
async def get_agent_output(agent_id: str, limit: int = 50):
|
|
60
|
+
"""
|
|
61
|
+
获取 Agent 最近会话详情:每轮 user/assistant/toolResult 及 usage
|
|
62
|
+
用于调试视图展示
|
|
63
|
+
"""
|
|
64
|
+
from data.session_reader import get_session_turns
|
|
65
|
+
from data.config_reader import get_agents_list
|
|
66
|
+
|
|
67
|
+
agents = get_agents_list()
|
|
68
|
+
if not any(a.get('id') == agent_id for a in agents):
|
|
69
|
+
from fastapi import HTTPException
|
|
70
|
+
raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")
|
|
71
|
+
|
|
72
|
+
turns = get_session_turns(agent_id, limit=limit)
|
|
73
|
+
return {"agentId": agent_id, "turns": turns}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent 配置 API - 直接从 openclaw.json 读取
|
|
3
|
+
仅依赖 config_reader,无其他模块依赖,作为协作流程的兜底数据源
|
|
4
|
+
"""
|
|
5
|
+
from fastapi import APIRouter
|
|
6
|
+
from typing import List, Dict, Any
|
|
7
|
+
|
|
8
|
+
router = APIRouter()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@router.get("/agents-config")
|
|
12
|
+
async def get_agents_config():
|
|
13
|
+
"""
|
|
14
|
+
直接从 ~/.openclaw/openclaw.json 读取 agents.list
|
|
15
|
+
返回格式与协作流程兼容,用于 API 失败时的兜底展示
|
|
16
|
+
"""
|
|
17
|
+
try:
|
|
18
|
+
from data.config_reader import (
|
|
19
|
+
get_agents_list,
|
|
20
|
+
get_main_agent_id,
|
|
21
|
+
get_agent_models,
|
|
22
|
+
)
|
|
23
|
+
agents_list = get_agents_list()
|
|
24
|
+
main_id = get_main_agent_id()
|
|
25
|
+
agent_models: Dict[str, Dict[str, Any]] = {}
|
|
26
|
+
for a in agents_list:
|
|
27
|
+
aid = a.get('id', '')
|
|
28
|
+
if aid:
|
|
29
|
+
agent_models[aid] = get_agent_models(aid)
|
|
30
|
+
|
|
31
|
+
nodes = []
|
|
32
|
+
edges = []
|
|
33
|
+
for agent in agents_list:
|
|
34
|
+
aid = agent.get('id', '')
|
|
35
|
+
if not aid:
|
|
36
|
+
continue
|
|
37
|
+
name = agent.get('name', aid)
|
|
38
|
+
nodes.append({
|
|
39
|
+
'id': aid,
|
|
40
|
+
'type': 'agent',
|
|
41
|
+
'name': name,
|
|
42
|
+
'status': 'idle',
|
|
43
|
+
'metadata': agent_models.get(aid),
|
|
44
|
+
})
|
|
45
|
+
if aid != main_id:
|
|
46
|
+
edges.append({
|
|
47
|
+
'id': f'edge-{main_id}-{aid}',
|
|
48
|
+
'source': main_id,
|
|
49
|
+
'target': aid,
|
|
50
|
+
'type': 'delegates',
|
|
51
|
+
'label': '委托',
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
'nodes': nodes,
|
|
56
|
+
'edges': edges,
|
|
57
|
+
'activePath': [],
|
|
58
|
+
'mainAgentId': main_id,
|
|
59
|
+
'agentModels': agent_models,
|
|
60
|
+
'models': [],
|
|
61
|
+
'recentCalls': [],
|
|
62
|
+
'lastUpdate': int(__import__('time').time() * 1000),
|
|
63
|
+
}
|
|
64
|
+
except Exception as e:
|
|
65
|
+
return {
|
|
66
|
+
'nodes': [],
|
|
67
|
+
'edges': [],
|
|
68
|
+
'activePath': [],
|
|
69
|
+
'mainAgentId': 'main',
|
|
70
|
+
'agentModels': {},
|
|
71
|
+
'models': [],
|
|
72
|
+
'recentCalls': [],
|
|
73
|
+
'lastUpdate': 0,
|
|
74
|
+
'_error': str(e),
|
|
75
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
任务链路 API 路由
|
|
3
|
+
"""
|
|
4
|
+
from fastapi import APIRouter, Query, HTTPException
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
from typing import Optional, List, Dict, Any
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
sys.path.append(str(Path(__file__).parent.parent))
|
|
10
|
+
|
|
11
|
+
from data.chain_reader import (
|
|
12
|
+
build_task_chains,
|
|
13
|
+
get_task_chain,
|
|
14
|
+
get_active_chain,
|
|
15
|
+
get_chains_summary
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
router = APIRouter()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ChainNode(BaseModel):
|
|
22
|
+
agentId: str
|
|
23
|
+
agentName: str
|
|
24
|
+
role: str
|
|
25
|
+
status: str
|
|
26
|
+
startedAt: Optional[int] = None
|
|
27
|
+
endedAt: Optional[int] = None
|
|
28
|
+
duration: Optional[int] = None
|
|
29
|
+
task: Optional[str] = None
|
|
30
|
+
runId: Optional[str] = None
|
|
31
|
+
input: Optional[str] = None
|
|
32
|
+
output: Optional[str] = None
|
|
33
|
+
artifacts: List[str] = []
|
|
34
|
+
toolCallCount: int = 0
|
|
35
|
+
tokenUsage: Dict[str, int] = {"input": 0, "output": 0}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ChainEdge(BaseModel):
|
|
39
|
+
from_: str
|
|
40
|
+
to: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TaskChain(BaseModel):
|
|
44
|
+
chainId: str
|
|
45
|
+
projectId: Optional[str] = None
|
|
46
|
+
rootTask: str
|
|
47
|
+
startedAt: Optional[int] = None
|
|
48
|
+
status: str
|
|
49
|
+
nodes: List[ChainNode]
|
|
50
|
+
edges: List[Dict[str, str]]
|
|
51
|
+
progress: float
|
|
52
|
+
completedNodes: int
|
|
53
|
+
totalNodes: int
|
|
54
|
+
totalDuration: int
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TaskChainListResponse(BaseModel):
|
|
58
|
+
chains: List[TaskChain]
|
|
59
|
+
activeChain: Optional[TaskChain] = None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ChainSummaryResponse(BaseModel):
|
|
63
|
+
total: int
|
|
64
|
+
running: int
|
|
65
|
+
completed: int
|
|
66
|
+
error: int
|
|
67
|
+
chains: List[Dict[str, Any]]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@router.get("/chains", response_model=TaskChainListResponse)
|
|
71
|
+
async def list_chains(
|
|
72
|
+
limit: int = Query(20, ge=1, le=100, description="返回链路数量")
|
|
73
|
+
):
|
|
74
|
+
"""
|
|
75
|
+
获取所有任务链路列表
|
|
76
|
+
|
|
77
|
+
返回 Agent 间的任务派发链路,包括:
|
|
78
|
+
- 链路中的所有节点(Agent)
|
|
79
|
+
- 节点间的派发关系
|
|
80
|
+
- 各节点的状态和进度
|
|
81
|
+
"""
|
|
82
|
+
chains = build_task_chains(limit=limit)
|
|
83
|
+
active = get_active_chain()
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
"chains": chains,
|
|
87
|
+
"activeChain": active
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@router.get("/chains/summary", response_model=ChainSummaryResponse)
|
|
92
|
+
async def get_summary():
|
|
93
|
+
"""
|
|
94
|
+
获取任务链路摘要统计
|
|
95
|
+
|
|
96
|
+
快速查看所有链路的状态分布
|
|
97
|
+
"""
|
|
98
|
+
return get_chains_summary()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@router.get("/chains/active")
|
|
102
|
+
async def get_active():
|
|
103
|
+
"""
|
|
104
|
+
获取当前活跃的任务链路
|
|
105
|
+
|
|
106
|
+
返回正在执行的任务链
|
|
107
|
+
"""
|
|
108
|
+
chain = get_active_chain()
|
|
109
|
+
if not chain:
|
|
110
|
+
return {"activeChain": None, "message": "当前没有正在执行的任务链"}
|
|
111
|
+
|
|
112
|
+
return {"activeChain": chain}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@router.get("/chains/{chain_id}")
|
|
116
|
+
async def get_chain(chain_id: str):
|
|
117
|
+
"""
|
|
118
|
+
获取单个任务链的详情
|
|
119
|
+
|
|
120
|
+
返回完整的链路信息,包括所有节点和边
|
|
121
|
+
"""
|
|
122
|
+
chain = get_task_chain(chain_id)
|
|
123
|
+
if not chain:
|
|
124
|
+
raise HTTPException(status_code=404, detail=f"Chain {chain_id} not found")
|
|
125
|
+
|
|
126
|
+
return chain
|