openclaw-agent-dashboard 1.0.46 → 1.0.47
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/dashboard/api/agents.py +29 -29
- package/dashboard/api/metrics.py +24 -0
- package/dashboard/api/websocket.py +445 -194
- package/dashboard/core/agent_state_ingestor.py +474 -0
- package/dashboard/core/checkpoint_manager.py +383 -0
- package/dashboard/core/config_fortify.py +33 -0
- package/dashboard/core/event_bus.py +128 -0
- package/dashboard/core/event_types.py +169 -0
- package/dashboard/core/file_change_classifier.py +80 -0
- package/dashboard/core/metrics_collector.py +106 -0
- package/dashboard/core/state_store.py +112 -0
- package/dashboard/data/session_reader.py +94 -0
- package/dashboard/ingest/collaboration_ingestor.py +176 -0
- package/dashboard/ingest/performance_ingestor.py +134 -0
- package/dashboard/ingest/task_ingestor.py +164 -0
- package/dashboard/main.py +54 -2
- package/dashboard/status/change_tracker.py +1 -1
- package/dashboard/status/status_calculator.py +129 -53
- package/dashboard/tests/ecs/__init__.py +0 -0
- package/dashboard/tests/ecs/conftest.py +150 -0
- package/dashboard/tests/ecs/test_agent_state_ingestor.py +430 -0
- package/dashboard/tests/ecs/test_c0_acceptance.py +342 -0
- package/dashboard/tests/ecs/test_c1_integration.py +804 -0
- package/dashboard/tests/ecs/test_c2_integration.py +611 -0
- package/dashboard/tests/ecs/test_checkpoint_manager.py +495 -0
- package/dashboard/tests/ecs/test_event_bus.py +236 -0
- package/dashboard/tests/ecs/test_file_change_classifier.py +244 -0
- package/dashboard/tests/ecs/test_file_watcher_c0.py +173 -0
- package/dashboard/tests/ecs/test_integration.py +767 -0
- package/dashboard/tests/ecs/test_main_lifespan.py +49 -0
- package/dashboard/tests/ecs/test_metrics_collector.py +202 -0
- package/dashboard/tests/ecs/test_state_store.py +295 -0
- package/dashboard/tests/ecs/test_websocket_c0.py +301 -0
- package/dashboard/tests/test_bench_fortify.py +21 -5
- package/dashboard/tests/test_event_bus.py +195 -0
- package/dashboard/tests/test_file_change_classifier.py +94 -0
- package/dashboard/tests/test_metrics_collector.py +130 -0
- package/dashboard/tests/test_session_reader_tail.py +48 -0
- package/dashboard/tests/test_state_store.py +176 -0
- package/dashboard/watchers/file_watcher.py +32 -17
- package/frontend-dist/assets/{index-CKnEqyAW.css → index-B5BS7sok.css} +1 -1
- package/frontend-dist/assets/index-qkRGxSvS.js +24 -0
- package/frontend-dist/index.html +2 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/frontend-dist/assets/index-B9FVSu__.js +0 -24
package/dashboard/api/agents.py
CHANGED
|
@@ -13,11 +13,6 @@ from pydantic import BaseModel
|
|
|
13
13
|
|
|
14
14
|
from api.input_safety import require_safe_agent_id
|
|
15
15
|
|
|
16
|
-
from status.status_calculator import (
|
|
17
|
-
get_agents_with_status,
|
|
18
|
-
format_last_active
|
|
19
|
-
)
|
|
20
|
-
|
|
21
16
|
router = APIRouter()
|
|
22
17
|
|
|
23
18
|
|
|
@@ -32,26 +27,40 @@ class AgentStatus(BaseModel):
|
|
|
32
27
|
error: Optional[dict] = None
|
|
33
28
|
|
|
34
29
|
|
|
30
|
+
async def _load_agents_with_retry(max_retry: int = 2) -> list:
|
|
31
|
+
"""C1: Load agents with retry. get_agents_with_status is now truly async."""
|
|
32
|
+
from core.error_handler import ErrorHandler
|
|
33
|
+
from status.status_calculator import get_agents_with_status
|
|
34
|
+
|
|
35
|
+
for attempt in range(max_retry + 1):
|
|
36
|
+
try:
|
|
37
|
+
return await get_agents_with_status()
|
|
38
|
+
except OSError as e:
|
|
39
|
+
from core.error_handler import classify_exception, record_error
|
|
40
|
+
cat = classify_exception(e)
|
|
41
|
+
record_error(cat, str(e), f"get_agents_with_status:attempt={attempt}", exc=e)
|
|
42
|
+
if attempt < max_retry:
|
|
43
|
+
import time
|
|
44
|
+
time.sleep(0.5 * (attempt + 1))
|
|
45
|
+
else:
|
|
46
|
+
from core.fallback_manager import run_fallback
|
|
47
|
+
fb = run_fallback(cat)
|
|
48
|
+
return fb if fb else []
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
|
|
35
52
|
@router.get("/agents", response_model=List[AgentStatus])
|
|
36
53
|
async def get_agents():
|
|
37
54
|
"""获取所有 Agent 列表及状态"""
|
|
38
|
-
from
|
|
55
|
+
from status.status_calculator import format_last_active
|
|
39
56
|
|
|
40
|
-
|
|
41
|
-
h = ErrorHandler(max_retry=2, base_delay=0.5)
|
|
42
|
-
return h.run_with_retry(
|
|
43
|
-
lambda: get_agents_with_status(),
|
|
44
|
-
operation="get_agents_with_status",
|
|
45
|
-
error_type="io-error",
|
|
46
|
-
)
|
|
57
|
+
agents = await _load_agents_with_retry()
|
|
47
58
|
|
|
48
|
-
agents = await asyncio.to_thread(_load)
|
|
49
|
-
|
|
50
59
|
# 格式化最后活跃时间
|
|
51
60
|
for agent in agents:
|
|
52
61
|
if agent.get('lastActiveAt'):
|
|
53
62
|
agent['lastActiveFormatted'] = format_last_active(agent['lastActiveAt'])
|
|
54
|
-
|
|
63
|
+
|
|
55
64
|
return agents
|
|
56
65
|
|
|
57
66
|
|
|
@@ -59,26 +68,17 @@ async def get_agents():
|
|
|
59
68
|
async def get_agent(agent_id: str):
|
|
60
69
|
"""获取单个 Agent 详情"""
|
|
61
70
|
require_safe_agent_id(agent_id)
|
|
62
|
-
from
|
|
63
|
-
|
|
64
|
-
def _load():
|
|
65
|
-
h = ErrorHandler(max_retry=2, base_delay=0.5)
|
|
66
|
-
return h.run_with_retry(
|
|
67
|
-
lambda: get_agents_with_status(),
|
|
68
|
-
operation="get_agents_with_status",
|
|
69
|
-
error_type="io-error",
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
agents = await asyncio.to_thread(_load)
|
|
73
|
-
|
|
71
|
+
from status.status_calculator import format_last_active
|
|
74
72
|
from data.config_reader import agent_ids_equal
|
|
75
73
|
|
|
74
|
+
agents = await _load_agents_with_retry()
|
|
75
|
+
|
|
76
76
|
for agent in agents:
|
|
77
77
|
if agent_ids_equal(agent['id'], agent_id):
|
|
78
78
|
if agent.get('lastActiveAt'):
|
|
79
79
|
agent['lastActiveFormatted'] = format_last_active(agent['lastActiveAt'])
|
|
80
80
|
return agent
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")
|
|
83
83
|
|
|
84
84
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ECS Metrics API — Prometheus-style metrics endpoint for C0 observability.
|
|
3
|
+
|
|
4
|
+
Exposes /api/metrics returning MetricsCollector snapshot.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from fastapi import APIRouter
|
|
9
|
+
from typing import Dict, Any
|
|
10
|
+
|
|
11
|
+
router = APIRouter()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@router.get("/metrics")
|
|
15
|
+
async def get_metrics():
|
|
16
|
+
"""Return C0 runtime metrics (JSON format).
|
|
17
|
+
|
|
18
|
+
Prometheus text format is deferred to C1. C0 uses JSON for simplicity.
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
from core.metrics_collector import get_metrics
|
|
22
|
+
return get_metrics().get_snapshot()
|
|
23
|
+
except Exception as e:
|
|
24
|
+
return {"error": str(e)}
|