Ordo commited on
Commit ·
6425ec7
0
Parent(s):
Initial public release
Browse files- .env.example +3 -0
- .gitignore +14 -0
- Dockerfile +16 -0
- LICENSE +21 -0
- README.md +37 -0
- SECURITY.md +3 -0
- app.py +1084 -0
- docker-compose.yml +32 -0
- react-dashboard/.dockerignore +6 -0
- react-dashboard/.gitignore +4 -0
- react-dashboard/Dockerfile +19 -0
- react-dashboard/README.md +20 -0
- react-dashboard/index.html +12 -0
- react-dashboard/nginx.conf +17 -0
- react-dashboard/package-lock.json +1673 -0
- react-dashboard/package.json +1 -0
- react-dashboard/src/App.jsx +310 -0
- react-dashboard/src/main.jsx +10 -0
- react-dashboard/src/styles.css +331 -0
- react-dashboard/vite.config.js +18 -0
- requirements.txt +2 -0
.env.example
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
OPENCLAW_AGENTS_ROOT=~/.openclaw/agents
|
| 2 |
+
SESSION_AMPLIFIER_BASE_URL=http://localhost:8477
|
| 3 |
+
VITE_SESSION_AMPLIFIER_BASE_URL=/session-amplifier
|
.gitignore
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.env
|
| 2 |
+
.env.*
|
| 3 |
+
!.env.example
|
| 4 |
+
__pycache__/
|
| 5 |
+
*.py[cod]
|
| 6 |
+
.pytest_cache/
|
| 7 |
+
.venv/
|
| 8 |
+
venv/
|
| 9 |
+
node_modules/
|
| 10 |
+
react-dashboard/dist/
|
| 11 |
+
*.db
|
| 12 |
+
*.sqlite
|
| 13 |
+
*.log
|
| 14 |
+
*.bak*
|
Dockerfile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
COPY requirements.txt /app/requirements.txt
|
| 6 |
+
RUN pip install --no-cache-dir -r /app/requirements.txt
|
| 7 |
+
|
| 8 |
+
COPY . /app
|
| 9 |
+
|
| 10 |
+
ENV STREAMLIT_SERVER_HEADLESS=true \
|
| 11 |
+
STREAMLIT_SERVER_PORT=8501 \
|
| 12 |
+
STREAMLIT_SERVER_ADDRESS=0.0.0.0
|
| 13 |
+
|
| 14 |
+
EXPOSE 8501
|
| 15 |
+
|
| 16 |
+
CMD ["streamlit", "run", "app.py"]
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 Patrick
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OpenClaw Ops Dashboard
|
| 2 |
+
|
| 3 |
+
Developer-facing dashboard experiments for OpenClaw operations.
|
| 4 |
+
|
| 5 |
+
This repo includes two dashboard surfaces:
|
| 6 |
+
|
| 7 |
+
- `react-dashboard/` — the current Vite/React dashboard for Session Amplifier data.
|
| 8 |
+
- `app.py` — the older Streamlit dashboard that reads local agent session JSON files.
|
| 9 |
+
|
| 10 |
+
## React Dashboard
|
| 11 |
+
|
| 12 |
+
```bash
|
| 13 |
+
cd react-dashboard
|
| 14 |
+
npm install
|
| 15 |
+
npm run build
|
| 16 |
+
npm run dev -- --port 5177
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
The Vite dev server proxies `/session-amplifier/*` to `SESSION_AMPLIFIER_BASE_URL` or `http://localhost:8477`.
|
| 20 |
+
|
| 21 |
+
## Streamlit Dashboard
|
| 22 |
+
|
| 23 |
+
The Streamlit app reads agent session JSON files from `~/.openclaw/agents/*/sessions` and displays:
|
| 24 |
+
|
| 25 |
+
- Agent list with last activity timestamps (best-effort)
|
| 26 |
+
- A simple timeline of recent cron runs (files with "cron" in the name under agents)
|
| 27 |
+
|
| 28 |
+
```bash
|
| 29 |
+
python3 -m venv .venv
|
| 30 |
+
. .venv/bin/activate
|
| 31 |
+
pip install -r requirements.txt
|
| 32 |
+
streamlit run app.py
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
Notes:
|
| 36 |
+
- The app is intentionally minimal and tolerant of malformed session files.
|
| 37 |
+
- Timestamps are parsed best-effort from common keys (last_activity, updated_at, timestamp).
|
SECURITY.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Security
|
| 2 |
+
|
| 3 |
+
Do not commit session transcripts, generated dashboards containing private operational data, `.env` files, or screenshots showing private conversations or credentials.
|
app.py
ADDED
|
@@ -0,0 +1,1084 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import datetime
|
| 2 |
+
import json
|
| 3 |
+
import os
|
| 4 |
+
import re
|
| 5 |
+
import time
|
| 6 |
+
from collections import Counter, deque
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
from zoneinfo import ZoneInfo
|
| 9 |
+
|
| 10 |
+
import streamlit as st
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
if os.name == "nt" or Path("C:/openclaw-biz/state/openclaw").exists():
|
| 14 |
+
OPENCLAW_ROOT = Path("C:/openclaw-biz/state/openclaw")
|
| 15 |
+
else:
|
| 16 |
+
OPENCLAW_ROOT = Path("/home/node/.openclaw")
|
| 17 |
+
|
| 18 |
+
AGENTS_DIR = OPENCLAW_ROOT / "agents"
|
| 19 |
+
CRON_DIR = OPENCLAW_ROOT / "cron"
|
| 20 |
+
STATE_DIR = Path("/home/node/.openclaw/workspace/ops/state")
|
| 21 |
+
SESSION_AMPLIFIER_BASE = os.environ.get("SESSION_AMPLIFIER_BASE_URL", "http://session-amplifier:8477")
|
| 22 |
+
LOCAL_TZ = ZoneInfo("America/New_York")
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def parse_ts_any(ts):
|
| 26 |
+
if ts is None:
|
| 27 |
+
return None
|
| 28 |
+
if isinstance(ts, datetime.datetime):
|
| 29 |
+
return ts if ts.tzinfo else ts.replace(tzinfo=datetime.timezone.utc)
|
| 30 |
+
if isinstance(ts, (int, float)):
|
| 31 |
+
try:
|
| 32 |
+
value = float(ts)
|
| 33 |
+
if value > 1e11:
|
| 34 |
+
value /= 1000.0
|
| 35 |
+
return datetime.datetime.fromtimestamp(value, tz=datetime.timezone.utc)
|
| 36 |
+
except Exception:
|
| 37 |
+
return None
|
| 38 |
+
if isinstance(ts, str):
|
| 39 |
+
text = ts.strip()
|
| 40 |
+
if not text:
|
| 41 |
+
return None
|
| 42 |
+
if text.endswith("Z"):
|
| 43 |
+
text = text[:-1] + "+00:00"
|
| 44 |
+
try:
|
| 45 |
+
dt = datetime.datetime.fromisoformat(text)
|
| 46 |
+
return dt if dt.tzinfo else dt.replace(tzinfo=datetime.timezone.utc)
|
| 47 |
+
except Exception:
|
| 48 |
+
return None
|
| 49 |
+
return None
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def fmt_local(ts):
|
| 53 |
+
dt = parse_ts_any(ts)
|
| 54 |
+
if dt is None:
|
| 55 |
+
return ""
|
| 56 |
+
try:
|
| 57 |
+
return dt.astimezone(LOCAL_TZ).strftime("%Y-%m-%d %I:%M:%S %p %Z")
|
| 58 |
+
except Exception:
|
| 59 |
+
return dt.isoformat()
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def fmt_duration(seconds):
|
| 63 |
+
"""Format seconds into human-readable duration."""
|
| 64 |
+
if seconds < 60:
|
| 65 |
+
return f"{int(seconds)}s"
|
| 66 |
+
elif seconds < 3600:
|
| 67 |
+
return f"{int(seconds/60)}m"
|
| 68 |
+
elif seconds < 86400:
|
| 69 |
+
return f"{int(seconds/3600)}h"
|
| 70 |
+
else:
|
| 71 |
+
return f"{int(seconds/86400)}d"
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def read_json(path: Path):
|
| 75 |
+
try:
|
| 76 |
+
return json.loads(path.read_text(encoding="utf-8"))
|
| 77 |
+
except Exception:
|
| 78 |
+
return None
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def read_tail_lines(path: Path, count: int = 80):
|
| 82 |
+
try:
|
| 83 |
+
with path.open("r", encoding="utf-8", errors="ignore") as fh:
|
| 84 |
+
return list(deque(fh, maxlen=count))
|
| 85 |
+
except Exception:
|
| 86 |
+
return []
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def clean_session_label(value: str, *, fallback: str = "") -> str:
|
| 90 |
+
text = (value or "").strip()
|
| 91 |
+
if not text:
|
| 92 |
+
return fallback
|
| 93 |
+
text = re.sub(r"^Discord thread\s+#?[^›]+›\s*", "", text)
|
| 94 |
+
text = re.sub(r"\s+channel id:\d+\s*$", "", text, flags=re.IGNORECASE)
|
| 95 |
+
text = text.strip(" #")
|
| 96 |
+
text = text.replace("-", " ")
|
| 97 |
+
text = re.sub(r"\s+", " ", text)
|
| 98 |
+
return text[:96] or fallback
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def session_display_label(row: dict) -> str:
|
| 102 |
+
label = clean_session_label(row.get("label") or "")
|
| 103 |
+
if label:
|
| 104 |
+
return label
|
| 105 |
+
key = row.get("sessionKey") or row.get("sessionId") or "session"
|
| 106 |
+
kind = row.get("kind") or "session"
|
| 107 |
+
return f"{kind}: {str(key)[:32]}"
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def normalize_exec_tool_label(content_block):
|
| 111 |
+
try:
|
| 112 |
+
name = content_block.get("name") or "unknown_tool"
|
| 113 |
+
if name != "exec":
|
| 114 |
+
return name
|
| 115 |
+
args = content_block.get("arguments")
|
| 116 |
+
cmd = None
|
| 117 |
+
if isinstance(args, dict):
|
| 118 |
+
cmd = args.get("command") or args.get("cmd")
|
| 119 |
+
elif isinstance(args, str):
|
| 120 |
+
cmd = args
|
| 121 |
+
if not isinstance(cmd, str):
|
| 122 |
+
return "exec"
|
| 123 |
+
if "mcporter" not in cmd.strip().lower():
|
| 124 |
+
return "exec"
|
| 125 |
+
import re
|
| 126 |
+
|
| 127 |
+
match = re.search(r"(?:^|\s)mcporter\s+call\s+([A-Za-z0-9_\-\.]+)", cmd)
|
| 128 |
+
if match:
|
| 129 |
+
return f"exec mcporter {match.group(1)}"
|
| 130 |
+
return "exec mcporter"
|
| 131 |
+
except Exception:
|
| 132 |
+
return content_block.get("name") or "unknown_tool"
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def session_index_rows():
|
| 136 |
+
rows = []
|
| 137 |
+
session_key_by_agent_and_id = {}
|
| 138 |
+
if not AGENTS_DIR.exists():
|
| 139 |
+
return rows, session_key_by_agent_and_id
|
| 140 |
+
|
| 141 |
+
for agent_dir in sorted(AGENTS_DIR.iterdir()):
|
| 142 |
+
if not agent_dir.is_dir() or agent_dir.name.startswith("."):
|
| 143 |
+
continue
|
| 144 |
+
sessions_file = agent_dir / "sessions" / "sessions.json"
|
| 145 |
+
data = read_json(sessions_file)
|
| 146 |
+
if not isinstance(data, dict):
|
| 147 |
+
continue
|
| 148 |
+
for session_key, meta in data.items():
|
| 149 |
+
if not isinstance(meta, dict):
|
| 150 |
+
continue
|
| 151 |
+
updated_raw = meta.get("updatedAt") or meta.get("updatedAtMs")
|
| 152 |
+
session_id = meta.get("sessionId") or ""
|
| 153 |
+
delivery = meta.get("deliveryContext") if isinstance(meta.get("deliveryContext"), dict) else {}
|
| 154 |
+
kind = "session"
|
| 155 |
+
if ":subagent:" in session_key:
|
| 156 |
+
kind = "subagent"
|
| 157 |
+
elif ":cron:" in session_key:
|
| 158 |
+
kind = "cron"
|
| 159 |
+
elif ":discord:" in session_key:
|
| 160 |
+
kind = "discord"
|
| 161 |
+
origin = meta.get("origin") if isinstance(meta.get("origin"), dict) else {}
|
| 162 |
+
label = (
|
| 163 |
+
meta.get("displayName")
|
| 164 |
+
or meta.get("derivedTitle")
|
| 165 |
+
or meta.get("title")
|
| 166 |
+
or origin.get("label")
|
| 167 |
+
or meta.get("groupChannel")
|
| 168 |
+
or ""
|
| 169 |
+
)
|
| 170 |
+
row = {
|
| 171 |
+
"agent": agent_dir.name,
|
| 172 |
+
"sessionKey": session_key,
|
| 173 |
+
"sessionId": session_id,
|
| 174 |
+
"label": clean_session_label(str(label), fallback=session_key),
|
| 175 |
+
"updatedAt": parse_ts_any(updated_raw),
|
| 176 |
+
"updated": fmt_local(updated_raw),
|
| 177 |
+
"deliveryTo": delivery.get("to") or "",
|
| 178 |
+
"kind": kind,
|
| 179 |
+
}
|
| 180 |
+
rows.append(row)
|
| 181 |
+
if session_id:
|
| 182 |
+
session_key_by_agent_and_id[(agent_dir.name, session_id)] = row
|
| 183 |
+
|
| 184 |
+
rows.sort(
|
| 185 |
+
key=lambda row: row["updatedAt"] or datetime.datetime.min.replace(tzinfo=datetime.timezone.utc),
|
| 186 |
+
reverse=True,
|
| 187 |
+
)
|
| 188 |
+
return rows, session_key_by_agent_and_id
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def transcript_activity_rows(session_lookup, max_files=80):
|
| 192 |
+
rows = []
|
| 193 |
+
if not AGENTS_DIR.exists():
|
| 194 |
+
return rows
|
| 195 |
+
|
| 196 |
+
jsonl_files = sorted(
|
| 197 |
+
AGENTS_DIR.rglob("sessions/*.jsonl"),
|
| 198 |
+
key=lambda path: path.stat().st_mtime if path.exists() else 0,
|
| 199 |
+
reverse=True,
|
| 200 |
+
)[:max_files]
|
| 201 |
+
|
| 202 |
+
for path in jsonl_files:
|
| 203 |
+
agent = path.parts[-3] if len(path.parts) >= 3 else "unknown"
|
| 204 |
+
session_id = path.stem
|
| 205 |
+
session_meta = session_lookup.get((agent, session_id))
|
| 206 |
+
session_key = session_meta.get("sessionKey") if isinstance(session_meta, dict) else session_meta
|
| 207 |
+
session_key = session_key or session_id
|
| 208 |
+
label = session_meta.get("label") if isinstance(session_meta, dict) else ""
|
| 209 |
+
last_ts = None
|
| 210 |
+
summary = ""
|
| 211 |
+
role = ""
|
| 212 |
+
for line in reversed(read_tail_lines(path, 120)):
|
| 213 |
+
line = line.strip()
|
| 214 |
+
if not line:
|
| 215 |
+
continue
|
| 216 |
+
try:
|
| 217 |
+
row = json.loads(line)
|
| 218 |
+
except Exception:
|
| 219 |
+
continue
|
| 220 |
+
msg = row.get("message") if isinstance(row.get("message"), dict) else {}
|
| 221 |
+
ts = parse_ts_any(row.get("timestamp") or row.get("time") or msg.get("timestamp") or msg.get("time"))
|
| 222 |
+
if ts and last_ts is None:
|
| 223 |
+
last_ts = ts
|
| 224 |
+
if not summary and isinstance(msg, dict):
|
| 225 |
+
role = msg.get("role") or row.get("type") or ""
|
| 226 |
+
content = msg.get("content") if isinstance(msg.get("content"), list) else []
|
| 227 |
+
for block in content:
|
| 228 |
+
if not isinstance(block, dict):
|
| 229 |
+
continue
|
| 230 |
+
if block.get("type") == "toolCall":
|
| 231 |
+
summary = f"tool: {normalize_exec_tool_label(block)}"
|
| 232 |
+
break
|
| 233 |
+
if block.get("type") == "text":
|
| 234 |
+
text = (block.get("text") or "").strip().replace("\n", " ")
|
| 235 |
+
if text:
|
| 236 |
+
summary = text[:160]
|
| 237 |
+
break
|
| 238 |
+
if not summary and msg.get("errorMessage"):
|
| 239 |
+
summary = str(msg.get("errorMessage"))[:160]
|
| 240 |
+
if last_ts and summary:
|
| 241 |
+
break
|
| 242 |
+
rows.append(
|
| 243 |
+
{
|
| 244 |
+
"agent": agent,
|
| 245 |
+
"sessionKey": session_key,
|
| 246 |
+
"sessionId": session_id,
|
| 247 |
+
"label": label or clean_session_label(session_key, fallback=session_id),
|
| 248 |
+
"updatedAt": last_ts,
|
| 249 |
+
"updated": fmt_local(last_ts),
|
| 250 |
+
"role": role,
|
| 251 |
+
"summary": summary or "(no recent summary)",
|
| 252 |
+
"file": str(path),
|
| 253 |
+
}
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
rows.sort(
|
| 257 |
+
key=lambda row: row["updatedAt"] or datetime.datetime.min.replace(tzinfo=datetime.timezone.utc),
|
| 258 |
+
reverse=True,
|
| 259 |
+
)
|
| 260 |
+
return rows
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def list_cron_jobs(limit=100):
|
| 264 |
+
"""Enhanced cron job listing with health status and staleness detection."""
|
| 265 |
+
jobs_file = CRON_DIR / "jobs.json"
|
| 266 |
+
data = read_json(jobs_file)
|
| 267 |
+
jobs = data.get("jobs", []) if isinstance(data, dict) else []
|
| 268 |
+
rows = []
|
| 269 |
+
now = datetime.datetime.now(datetime.timezone.utc)
|
| 270 |
+
now_ms = time.time() * 1000
|
| 271 |
+
|
| 272 |
+
for job in jobs[:limit]:
|
| 273 |
+
if not isinstance(job, dict):
|
| 274 |
+
continue
|
| 275 |
+
state = job.get("state") if isinstance(job.get("state"), dict) else {}
|
| 276 |
+
schedule = job.get("schedule") if isinstance(job.get("schedule"), dict) else {}
|
| 277 |
+
payload = job.get("payload") if isinstance(job.get("payload"), dict) else {}
|
| 278 |
+
|
| 279 |
+
last_run_ms = state.get("lastRunAtMs")
|
| 280 |
+
next_run_ms = state.get("nextRunAtMs")
|
| 281 |
+
consecutive_errors = state.get("consecutiveErrors", 0)
|
| 282 |
+
last_status = state.get("lastStatus") or state.get("lastRunStatus") or ""
|
| 283 |
+
|
| 284 |
+
# Calculate staleness
|
| 285 |
+
staleness = None
|
| 286 |
+
if last_run_ms and next_run_ms:
|
| 287 |
+
# If next run was scheduled but hasn't happened yet, check if we're past due
|
| 288 |
+
if now_ms > next_run_ms + 300000: # 5 min grace period
|
| 289 |
+
staleness = fmt_duration((now_ms - next_run_ms) / 1000)
|
| 290 |
+
elif last_run_ms and schedule.get("expr"):
|
| 291 |
+
# Estimate staleness based on schedule (rough: check if > 2x expected interval)
|
| 292 |
+
pass # Skip for now - would need cron parsing
|
| 293 |
+
|
| 294 |
+
# Health status
|
| 295 |
+
health = "healthy"
|
| 296 |
+
health_reason = ""
|
| 297 |
+
if not job.get("enabled", True):
|
| 298 |
+
health = "disabled"
|
| 299 |
+
elif consecutive_errors >= 3:
|
| 300 |
+
health = "critical"
|
| 301 |
+
health_reason = f"{consecutive_errors} consecutive errors"
|
| 302 |
+
elif consecutive_errors > 0:
|
| 303 |
+
health = "warning"
|
| 304 |
+
health_reason = f"{consecutive_errors} error(s)"
|
| 305 |
+
elif last_status == "error":
|
| 306 |
+
health = "error"
|
| 307 |
+
elif staleness:
|
| 308 |
+
health = "stale"
|
| 309 |
+
health_reason = f"overdue by {staleness}"
|
| 310 |
+
elif state.get("runningAtMs"):
|
| 311 |
+
health = "running"
|
| 312 |
+
|
| 313 |
+
rows.append(
|
| 314 |
+
{
|
| 315 |
+
"name": job.get("name") or job.get("id") or "unnamed",
|
| 316 |
+
"jobId": job.get("id") or "",
|
| 317 |
+
"enabled": bool(job.get("enabled", True)),
|
| 318 |
+
"kind": payload.get("kind") or "",
|
| 319 |
+
"model": payload.get("model") or "",
|
| 320 |
+
"schedule": schedule.get("expr") or schedule.get("kind") or "",
|
| 321 |
+
"tz": schedule.get("tz") or "",
|
| 322 |
+
"nextRunAt": parse_ts_any(state.get("nextRunAtMs") or state.get("nextRunAt")),
|
| 323 |
+
"nextRun": fmt_local(state.get("nextRunAtMs") or state.get("nextRunAt")),
|
| 324 |
+
"runningAt": parse_ts_any(state.get("runningAtMs") or state.get("runningAt")),
|
| 325 |
+
"running": fmt_local(state.get("runningAtMs") or state.get("runningAt")),
|
| 326 |
+
"lastRunAt": parse_ts_any(state.get("lastRunAtMs") or state.get("lastRunAt")),
|
| 327 |
+
"lastRun": fmt_local(state.get("lastRunAtMs") or state.get("lastRunAt")),
|
| 328 |
+
"lastStatus": last_status,
|
| 329 |
+
"sessionKey": job.get("sessionKey") or "",
|
| 330 |
+
"consecutiveErrors": consecutive_errors,
|
| 331 |
+
"health": health,
|
| 332 |
+
"healthReason": health_reason,
|
| 333 |
+
"staleness": staleness,
|
| 334 |
+
}
|
| 335 |
+
)
|
| 336 |
+
rows.sort(
|
| 337 |
+
key=lambda row: (
|
| 338 |
+
{"critical": 0, "error": 1, "warning": 2, "stale": 3, "running": 4, "healthy": 5, "disabled": 6}.get(row["health"], 7),
|
| 339 |
+
row["runningAt"] or row["nextRunAt"] or row["lastRunAt"] or datetime.datetime.min.replace(tzinfo=datetime.timezone.utc)
|
| 340 |
+
),
|
| 341 |
+
reverse=False,
|
| 342 |
+
)
|
| 343 |
+
return rows
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
def collect_metrics_last_24h(max_files=500):
|
| 347 |
+
now = datetime.datetime.now(datetime.timezone.utc)
|
| 348 |
+
cutoff = now - datetime.timedelta(hours=24)
|
| 349 |
+
jsonl_files = sorted(
|
| 350 |
+
AGENTS_DIR.rglob("sessions/*.jsonl"),
|
| 351 |
+
key=lambda path: path.stat().st_mtime if path.exists() else 0,
|
| 352 |
+
reverse=True,
|
| 353 |
+
)[:max_files]
|
| 354 |
+
|
| 355 |
+
tool_counts = Counter()
|
| 356 |
+
error_count = 0
|
| 357 |
+
total_events = 0
|
| 358 |
+
token_total = 0
|
| 359 |
+
input_tokens = 0
|
| 360 |
+
output_tokens = 0
|
| 361 |
+
cost_total = 0.0
|
| 362 |
+
cache_hits = 0
|
| 363 |
+
cache_samples = 0
|
| 364 |
+
latest_per_agent = {}
|
| 365 |
+
|
| 366 |
+
for path in jsonl_files:
|
| 367 |
+
agent = path.parts[-3] if len(path.parts) >= 3 else "unknown"
|
| 368 |
+
try:
|
| 369 |
+
with path.open("r", encoding="utf-8", errors="ignore") as fh:
|
| 370 |
+
for line in fh:
|
| 371 |
+
line = line.strip()
|
| 372 |
+
if not line:
|
| 373 |
+
continue
|
| 374 |
+
try:
|
| 375 |
+
row = json.loads(line)
|
| 376 |
+
except Exception:
|
| 377 |
+
continue
|
| 378 |
+
msg = row.get("message") if isinstance(row.get("message"), dict) else {}
|
| 379 |
+
ts = parse_ts_any(row.get("timestamp") or msg.get("timestamp") or row.get("time") or msg.get("time"))
|
| 380 |
+
if ts is None or ts < cutoff:
|
| 381 |
+
continue
|
| 382 |
+
total_events += 1
|
| 383 |
+
if msg.get("errorMessage") or msg.get("stopReason") == "error":
|
| 384 |
+
error_count += 1
|
| 385 |
+
summary = None
|
| 386 |
+
content = msg.get("content") if isinstance(msg.get("content"), list) else []
|
| 387 |
+
for block in content:
|
| 388 |
+
if not isinstance(block, dict):
|
| 389 |
+
continue
|
| 390 |
+
if block.get("type") == "toolCall":
|
| 391 |
+
label = normalize_exec_tool_label(block)
|
| 392 |
+
tool_counts[label] += 1
|
| 393 |
+
summary = summary or f"tool: {label}"
|
| 394 |
+
elif block.get("type") == "text" and msg.get("role") == "assistant":
|
| 395 |
+
text = (block.get("text") or "").strip().replace("\n", " ")
|
| 396 |
+
if text and summary is None:
|
| 397 |
+
summary = text[:120]
|
| 398 |
+
usage = msg.get("usage") if isinstance(msg.get("usage"), dict) else {}
|
| 399 |
+
if usage:
|
| 400 |
+
# Token extraction - prefer explicit totals
|
| 401 |
+
total_tok = usage.get("total_tokens") or usage.get("totalTokens")
|
| 402 |
+
input_tok = usage.get("input_tokens") or usage.get("inputTokens") or usage.get("input")
|
| 403 |
+
output_tok = usage.get("output_tokens") or usage.get("outputTokens") or usage.get("output")
|
| 404 |
+
|
| 405 |
+
if total_tok:
|
| 406 |
+
token_total += int(total_tok)
|
| 407 |
+
elif input_tok or output_tok:
|
| 408 |
+
token_total += int(input_tok or 0) + int(output_tok or 0)
|
| 409 |
+
|
| 410 |
+
if input_tok:
|
| 411 |
+
input_tokens += int(input_tok)
|
| 412 |
+
if output_tok:
|
| 413 |
+
output_tokens += int(output_tok)
|
| 414 |
+
|
| 415 |
+
for key in ("cost", "total_cost", "usd", "costUsd"):
|
| 416 |
+
value = usage.get(key)
|
| 417 |
+
if isinstance(value, (int, float)):
|
| 418 |
+
cost_total += float(value)
|
| 419 |
+
cached = usage.get("cache_hit") or usage.get("cacheHit")
|
| 420 |
+
cached_tokens = usage.get("cached_tokens") or usage.get("cachedTokens") or usage.get("cache_read_tokens") or usage.get("cacheReadTokens")
|
| 421 |
+
if isinstance(cached, bool):
|
| 422 |
+
cache_samples += 1
|
| 423 |
+
if cached:
|
| 424 |
+
cache_hits += 1
|
| 425 |
+
elif isinstance(cached_tokens, (int, float)):
|
| 426 |
+
cache_samples += 1
|
| 427 |
+
if cached_tokens > 0:
|
| 428 |
+
cache_hits += 1
|
| 429 |
+
latest = latest_per_agent.get(agent)
|
| 430 |
+
if latest is None or ts > latest["ts"]:
|
| 431 |
+
latest_per_agent[agent] = {"ts": ts, "summary": summary or "No recent assistant/tool summary"}
|
| 432 |
+
except Exception:
|
| 433 |
+
continue
|
| 434 |
+
|
| 435 |
+
active_cutoff = now - datetime.timedelta(minutes=60)
|
| 436 |
+
active_agents = [
|
| 437 |
+
{"agent": agent, "ts": info["ts"], "updated": fmt_local(info["ts"]), "summary": info["summary"]}
|
| 438 |
+
for agent, info in latest_per_agent.items()
|
| 439 |
+
if info["ts"] >= active_cutoff
|
| 440 |
+
]
|
| 441 |
+
active_agents.sort(key=lambda row: row["ts"], reverse=True)
|
| 442 |
+
return {
|
| 443 |
+
"tool_counts": tool_counts,
|
| 444 |
+
"error_rate": (error_count / total_events * 100.0) if total_events else 0.0,
|
| 445 |
+
"error_count": error_count,
|
| 446 |
+
"total_events": total_events,
|
| 447 |
+
"token_total": token_total,
|
| 448 |
+
"input_tokens": input_tokens,
|
| 449 |
+
"output_tokens": output_tokens,
|
| 450 |
+
"cost_total": cost_total,
|
| 451 |
+
"cache_hit_proxy": (cache_hits / cache_samples * 100.0) if cache_samples else None,
|
| 452 |
+
"cache_samples": cache_samples,
|
| 453 |
+
"active_summaries": active_agents,
|
| 454 |
+
}
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
def get_live_session_activity(session_key, agent_name, max_lines=100):
|
| 458 |
+
"""Extract detailed recent activity from a specific session's JSONL."""
|
| 459 |
+
if not AGENTS_DIR.exists():
|
| 460 |
+
return []
|
| 461 |
+
|
| 462 |
+
# Find the JSONL file for this session
|
| 463 |
+
agent_dir = AGENTS_DIR / agent_name
|
| 464 |
+
if not agent_dir.exists():
|
| 465 |
+
return []
|
| 466 |
+
|
| 467 |
+
# session_key format: agent:name:uuid or just uuid
|
| 468 |
+
session_id = session_key.split(":")[-1] if ":" in session_key else session_key
|
| 469 |
+
jsonl_path = agent_dir / "sessions" / f"{session_id}.jsonl"
|
| 470 |
+
|
| 471 |
+
if not jsonl_path.exists():
|
| 472 |
+
return []
|
| 473 |
+
|
| 474 |
+
activities = []
|
| 475 |
+
lines = read_tail_lines(jsonl_path, max_lines)
|
| 476 |
+
|
| 477 |
+
for line in lines:
|
| 478 |
+
line = line.strip()
|
| 479 |
+
if not line:
|
| 480 |
+
continue
|
| 481 |
+
try:
|
| 482 |
+
row = json.loads(line)
|
| 483 |
+
except Exception:
|
| 484 |
+
continue
|
| 485 |
+
|
| 486 |
+
msg = row.get("message") if isinstance(row.get("message"), dict) else {}
|
| 487 |
+
ts = parse_ts_any(row.get("timestamp") or msg.get("timestamp") or row.get("time") or msg.get("time"))
|
| 488 |
+
|
| 489 |
+
entry = {
|
| 490 |
+
"timestamp": ts,
|
| 491 |
+
"time": fmt_local(ts),
|
| 492 |
+
"role": msg.get("role", ""),
|
| 493 |
+
"type": "",
|
| 494 |
+
"summary": "",
|
| 495 |
+
"details": "",
|
| 496 |
+
}
|
| 497 |
+
|
| 498 |
+
content = msg.get("content") if isinstance(msg.get("content"), list) else []
|
| 499 |
+
for block in content:
|
| 500 |
+
if not isinstance(block, dict):
|
| 501 |
+
continue
|
| 502 |
+
|
| 503 |
+
if block.get("type") == "thinking":
|
| 504 |
+
entry["type"] = "thinking"
|
| 505 |
+
thinking = block.get("thinking") or ""
|
| 506 |
+
entry["summary"] = "Thinking..." if thinking else "(empty thinking)"
|
| 507 |
+
entry["details"] = thinking[:500] if thinking else ""
|
| 508 |
+
|
| 509 |
+
elif block.get("type") == "toolCall":
|
| 510 |
+
entry["type"] = "tool"
|
| 511 |
+
label = normalize_exec_tool_label(block)
|
| 512 |
+
entry["summary"] = f"→ {label}"
|
| 513 |
+
args = block.get("arguments", {})
|
| 514 |
+
if isinstance(args, dict):
|
| 515 |
+
# Summarize key arguments
|
| 516 |
+
arg_summary = []
|
| 517 |
+
for k, v in list(args.items())[:3]:
|
| 518 |
+
v_str = str(v)[:50]
|
| 519 |
+
arg_summary.append(f"{k}={v_str}")
|
| 520 |
+
entry["details"] = ", ".join(arg_summary)
|
| 521 |
+
elif isinstance(args, str):
|
| 522 |
+
entry["details"] = args[:100]
|
| 523 |
+
|
| 524 |
+
elif block.get("type") == "toolResult":
|
| 525 |
+
entry["type"] = "result"
|
| 526 |
+
content_data = block.get("content")
|
| 527 |
+
is_error = block.get("isError") or block.get("error")
|
| 528 |
+
entry["summary"] = "✗ Error" if is_error else "✓ Result"
|
| 529 |
+
if isinstance(content_data, str):
|
| 530 |
+
entry["details"] = content_data[:200]
|
| 531 |
+
elif isinstance(content_data, list) and content_data:
|
| 532 |
+
entry["details"] = str(content_data[0])[:200]
|
| 533 |
+
|
| 534 |
+
elif block.get("type") == "text":
|
| 535 |
+
entry["type"] = "text"
|
| 536 |
+
text = (block.get("text") or "").strip()
|
| 537 |
+
entry["summary"] = text[:80].replace("\n", " ")
|
| 538 |
+
entry["details"] = text[:300]
|
| 539 |
+
|
| 540 |
+
if entry["type"] or entry["summary"]:
|
| 541 |
+
activities.append(entry)
|
| 542 |
+
|
| 543 |
+
return activities
|
| 544 |
+
|
| 545 |
+
|
| 546 |
+
def _load_sidecar_artifact(name: str):
|
| 547 |
+
"""Load a Session Amplifier artifact from the state directory. Returns {} if unavailable."""
|
| 548 |
+
path = STATE_DIR / "session_amplifier" / name
|
| 549 |
+
if not path.exists():
|
| 550 |
+
return None
|
| 551 |
+
try:
|
| 552 |
+
return json.loads(path.read_text(encoding="utf-8"))
|
| 553 |
+
except Exception:
|
| 554 |
+
return None
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
def _sidecar_reachable() -> bool:
|
| 558 |
+
try:
|
| 559 |
+
import urllib.request
|
| 560 |
+
req = urllib.request.Request(f"{SESSION_AMPLIFIER_BASE}/health", method="GET")
|
| 561 |
+
urllib.request.urlopen(req, timeout=2)
|
| 562 |
+
return True
|
| 563 |
+
except Exception:
|
| 564 |
+
return False
|
| 565 |
+
|
| 566 |
+
|
| 567 |
+
def _fetch_sidecar_json(path: str, default=None):
|
| 568 |
+
"""GET a path from the sidecar API. Returns parsed JSON or default on failure."""
|
| 569 |
+
try:
|
| 570 |
+
import urllib.request
|
| 571 |
+
req = urllib.request.Request(f"{SESSION_AMPLIFIER_BASE}{path}", method="GET")
|
| 572 |
+
with urllib.request.urlopen(req, timeout=5) as resp:
|
| 573 |
+
return json.loads(resp.read().decode())
|
| 574 |
+
except Exception:
|
| 575 |
+
return default
|
| 576 |
+
|
| 577 |
+
|
| 578 |
+
def _load_ops_json(name: str, default=None):
|
| 579 |
+
path = STATE_DIR / name
|
| 580 |
+
try:
|
| 581 |
+
return json.loads(path.read_text(encoding="utf-8"))
|
| 582 |
+
except Exception:
|
| 583 |
+
return default
|
| 584 |
+
|
| 585 |
+
|
| 586 |
+
def render_model_ops_tab(session_rows):
|
| 587 |
+
snapshot = _load_ops_json("model-ops-snapshot-latest.json", {}) or {}
|
| 588 |
+
merged = _load_ops_json("model-benchmarks-merged-latest.json", {}) or {}
|
| 589 |
+
cost_rows = _load_ops_json("session-cost-top50-latest.json", []) or []
|
| 590 |
+
|
| 591 |
+
st.subheader("Model Ops")
|
| 592 |
+
c1, c2, c3, c4 = st.columns(4)
|
| 593 |
+
c1.metric("OpenRouter models", snapshot.get("market_model_count", 0))
|
| 594 |
+
c2.metric("Merged benchmark models", len(merged.get("models", [])))
|
| 595 |
+
c3.metric("HF models", len((_load_ops_json("hf-benchmarks-latest.json", {}) or {}).get("models", [])))
|
| 596 |
+
c4.metric("Top cost rows", len(cost_rows))
|
| 597 |
+
|
| 598 |
+
st.markdown("### Benchmark sources")
|
| 599 |
+
src_rows = []
|
| 600 |
+
for src in snapshot.get("benchmark_source_status", []):
|
| 601 |
+
if isinstance(src, dict):
|
| 602 |
+
src_rows.append({"source": src.get("name"), "status": src.get("status")})
|
| 603 |
+
if src_rows:
|
| 604 |
+
st.dataframe(src_rows, use_container_width=True, hide_index=True)
|
| 605 |
+
else:
|
| 606 |
+
st.caption("No benchmark source metadata available")
|
| 607 |
+
|
| 608 |
+
st.markdown("### Cost coverage quality")
|
| 609 |
+
coverage = Counter((row.get("cost_source") or "unknown") for row in cost_rows)
|
| 610 |
+
cc1, cc2, cc3, cc4 = st.columns(4)
|
| 611 |
+
cc1.metric("Observed", coverage.get("observed", 0))
|
| 612 |
+
cc2.metric("Estimated", coverage.get("estimated", 0))
|
| 613 |
+
cc3.metric("Unknown", coverage.get("unknown", 0) + coverage.get("", 0))
|
| 614 |
+
cc4.metric("Trust score", f"{(snapshot.get('cost_trust_score', 0.0) * 100):.1f}%")
|
| 615 |
+
|
| 616 |
+
st.markdown("### Highest current observed session cost")
|
| 617 |
+
if cost_rows:
|
| 618 |
+
agent_filter = st.selectbox("Cost rows filter by agent", ["all"] + sorted({r.get('agent_id') or '' for r in cost_rows if r.get('agent_id')}), index=0)
|
| 619 |
+
filtered_cost = [r for r in cost_rows if agent_filter == 'all' or r.get('agent_id') == agent_filter]
|
| 620 |
+
st.dataframe(filtered_cost[:15], use_container_width=True, hide_index=True)
|
| 621 |
+
|
| 622 |
+
st.markdown("#### Inspect costly session")
|
| 623 |
+
lookup = {f"{r.get('agent_id')}/{r.get('session_id')} — {r.get('primary_model')} — ${r.get('total_estimated_usd')}": r for r in filtered_cost[:25]}
|
| 624 |
+
if lookup:
|
| 625 |
+
selected = st.selectbox("Jump to session details", list(lookup.keys()), index=0)
|
| 626 |
+
chosen = lookup[selected]
|
| 627 |
+
agent = chosen.get('agent_id')
|
| 628 |
+
session_id = chosen.get('session_id')
|
| 629 |
+
matching = [r for r in session_rows if r.get('agent') == agent and (r.get('sessionId') == session_id or session_id in (r.get('sessionKey') or ''))]
|
| 630 |
+
if matching:
|
| 631 |
+
match = matching[0]
|
| 632 |
+
st.caption(f"Session key: {match.get('sessionKey')}")
|
| 633 |
+
st.caption(f"Last updated: {match.get('updated')}")
|
| 634 |
+
st.caption(f"Delivery: {match.get('deliveryTo')}")
|
| 635 |
+
recent = get_live_session_activity(match.get('sessionKey'), agent, max_lines=40)
|
| 636 |
+
if recent:
|
| 637 |
+
st.dataframe([
|
| 638 |
+
{
|
| 639 |
+
'time': a.get('time'),
|
| 640 |
+
'type': a.get('type'),
|
| 641 |
+
'summary': a.get('summary'),
|
| 642 |
+
'details': (a.get('details') or '')[:120],
|
| 643 |
+
} for a in recent[-15:]
|
| 644 |
+
], use_container_width=True, hide_index=True)
|
| 645 |
+
else:
|
| 646 |
+
st.caption('No recent activity extracted from transcript.')
|
| 647 |
+
else:
|
| 648 |
+
st.caption('No matching session found in current session index.')
|
| 649 |
+
else:
|
| 650 |
+
st.caption("No session cost artifacts found")
|
| 651 |
+
|
| 652 |
+
show_legacy = st.checkbox("Show legacy / non-orchestration benchmark leaders", value=False)
|
| 653 |
+
st.markdown("### Top merged Arena Elo")
|
| 654 |
+
arena_elo = snapshot.get("top_arena_elo", [])
|
| 655 |
+
if show_legacy:
|
| 656 |
+
arena_elo = sorted([m for m in merged.get('models', []) if (m.get('arena') or {}).get('elo') is not None], key=lambda m: (m.get('arena') or {}).get('elo') or 0, reverse=True)[:10]
|
| 657 |
+
if arena_elo:
|
| 658 |
+
st.dataframe([
|
| 659 |
+
{"model": r.get("model"), "elo": (r.get("arena") or {}).get("elo"), "mapping": (r.get("mapping") or {}).get("confidence")}
|
| 660 |
+
for r in arena_elo
|
| 661 |
+
], use_container_width=True, hide_index=True)
|
| 662 |
+
else:
|
| 663 |
+
st.caption("No merged arena elo rows")
|
| 664 |
+
|
| 665 |
+
st.markdown("### Top merged Arena winrate")
|
| 666 |
+
arena_wr = snapshot.get("top_arena_winrate", [])
|
| 667 |
+
if show_legacy:
|
| 668 |
+
arena_wr = sorted([m for m in merged.get('models', []) if (m.get('arena') or {}).get('winrate') is not None], key=lambda m: (m.get('arena') or {}).get('winrate') or 0, reverse=True)[:10]
|
| 669 |
+
if arena_wr:
|
| 670 |
+
st.dataframe([
|
| 671 |
+
{
|
| 672 |
+
"model": r.get("model"),
|
| 673 |
+
"winrate": (r.get("arena") or {}).get("winrate"),
|
| 674 |
+
"appearances": (r.get("arena") or {}).get("appearances"),
|
| 675 |
+
"mapping": (r.get("mapping") or {}).get("confidence"),
|
| 676 |
+
}
|
| 677 |
+
for r in arena_wr
|
| 678 |
+
], use_container_width=True, hide_index=True)
|
| 679 |
+
else:
|
| 680 |
+
st.caption("No merged arena winrate rows")
|
| 681 |
+
|
| 682 |
+
st.markdown("### Highest heuristic efficiency")
|
| 683 |
+
eff = snapshot.get("top_efficiency", [])
|
| 684 |
+
if eff:
|
| 685 |
+
st.dataframe([
|
| 686 |
+
{
|
| 687 |
+
"model": r.get("id"),
|
| 688 |
+
"efficiency": r.get("efficiency_score"),
|
| 689 |
+
"input_per_1m": (r.get("pricing_per_1m") or {}).get("input"),
|
| 690 |
+
"context": r.get("context_length"),
|
| 691 |
+
}
|
| 692 |
+
for r in eff
|
| 693 |
+
], use_container_width=True, hide_index=True)
|
| 694 |
+
|
| 695 |
+
st.markdown("### Mapping confidence breakdown")
|
| 696 |
+
counts = Counter(((m.get("mapping") or {}).get("confidence") or "unknown") for m in merged.get("models", []))
|
| 697 |
+
st.json(dict(counts))
|
| 698 |
+
|
| 699 |
+
|
| 700 |
+
def main():
|
| 701 |
+
st.set_page_config(page_title="OpenClaw Ops Dashboard", layout="wide")
|
| 702 |
+
st.title("OpenClaw Ops Dashboard")
|
| 703 |
+
st.caption(f"Root: {OPENCLAW_ROOT}")
|
| 704 |
+
|
| 705 |
+
# Load Session Amplifier artifacts if available
|
| 706 |
+
sidecar_status = "unavailable"
|
| 707 |
+
sidecar_report = _load_sidecar_artifact("review-latest.json")
|
| 708 |
+
sidecar_skills = _load_sidecar_artifact("skills-latest.json")
|
| 709 |
+
if sidecar_report or sidecar_skills:
|
| 710 |
+
sidecar_status = "ok"
|
| 711 |
+
elif _sidecar_reachable():
|
| 712 |
+
sidecar_status = "reachable"
|
| 713 |
+
|
| 714 |
+
session_rows, session_lookup = session_index_rows()
|
| 715 |
+
activity_rows = transcript_activity_rows(session_lookup)
|
| 716 |
+
cron_rows = list_cron_jobs()
|
| 717 |
+
metrics = collect_metrics_last_24h()
|
| 718 |
+
|
| 719 |
+
running_cron = [row for row in cron_rows if row["runningAt"]]
|
| 720 |
+
subagent_rows = [row for row in session_rows if row["kind"] == "subagent"]
|
| 721 |
+
|
| 722 |
+
# Health summary
|
| 723 |
+
critical_jobs = [r for r in cron_rows if r["health"] == "critical"]
|
| 724 |
+
warning_jobs = [r for r in cron_rows if r["health"] == "warning"]
|
| 725 |
+
stale_jobs = [r for r in cron_rows if r["health"] == "stale"]
|
| 726 |
+
|
| 727 |
+
c1, c2, c3, c4, c5 = st.columns(5)
|
| 728 |
+
c1.metric("Agents", len({row["agent"] for row in session_rows}))
|
| 729 |
+
c2.metric("Indexed sessions", len(session_rows))
|
| 730 |
+
c3.metric("Running cron", len(running_cron))
|
| 731 |
+
c4.metric("Sidecar", sidecar_status.title(), help="Session Amplifier sidecar availability")
|
| 732 |
+
health_display = "✓" if not (critical_jobs or warning_jobs or stale_jobs) else f"⚠ {len(critical_jobs)}/{len(warning_jobs)}/{len(stale_jobs)}"
|
| 733 |
+
c5.metric("Health", health_display, help="Critical/Warning/Stale cron jobs")
|
| 734 |
+
|
| 735 |
+
tab_overview, tab_sessions, tab_cron, tab_activity, tab_live, tab_model_ops = st.tabs(
|
| 736 |
+
["Overview", "Sessions", "Cron", "Activity", "Live Session", "Model Ops"]
|
| 737 |
+
)
|
| 738 |
+
|
| 739 |
+
with tab_overview:
|
| 740 |
+
left, right = st.columns([1, 1])
|
| 741 |
+
with left:
|
| 742 |
+
st.subheader("Active agent summaries (last 60m)")
|
| 743 |
+
if metrics["active_summaries"]:
|
| 744 |
+
st.dataframe(metrics["active_summaries"], use_container_width=True, hide_index=True)
|
| 745 |
+
else:
|
| 746 |
+
st.info("No active agent summaries in the last 60 minutes.")
|
| 747 |
+
|
| 748 |
+
st.subheader("Recent sessions")
|
| 749 |
+
if session_rows:
|
| 750 |
+
st.dataframe(
|
| 751 |
+
[
|
| 752 |
+
{
|
| 753 |
+
"agent": row["agent"],
|
| 754 |
+
"kind": row["kind"],
|
| 755 |
+
"updated": row["updated"],
|
| 756 |
+
"sessionKey": row["sessionKey"],
|
| 757 |
+
"deliveryTo": row["deliveryTo"],
|
| 758 |
+
}
|
| 759 |
+
for row in session_rows[:20]
|
| 760 |
+
],
|
| 761 |
+
use_container_width=True,
|
| 762 |
+
hide_index=True,
|
| 763 |
+
)
|
| 764 |
+
else:
|
| 765 |
+
st.info("No indexed sessions found.")
|
| 766 |
+
|
| 767 |
+
with right:
|
| 768 |
+
st.subheader("Last 24h metrics")
|
| 769 |
+
m1, m2, m3, m4 = st.columns(4)
|
| 770 |
+
m1.metric("Error rate", f"{metrics['error_rate']:.2f}%")
|
| 771 |
+
m2.metric("Errors / events", f"{metrics['error_count']} / {metrics['total_events']}")
|
| 772 |
+
|
| 773 |
+
# Show tokens instead of cost (more reliable)
|
| 774 |
+
token_display = f"{metrics['token_total']:,}" if metrics['token_total'] else "0"
|
| 775 |
+
m3.metric("Tokens", token_display)
|
| 776 |
+
m4.metric("Cost", f"${metrics['cost_total']:.4f}")
|
| 777 |
+
|
| 778 |
+
st.caption(f"Input: {metrics['input_tokens']:,} | Output: {metrics['output_tokens']:,}")
|
| 779 |
+
|
| 780 |
+
if metrics["cache_hit_proxy"] is None:
|
| 781 |
+
st.caption("Cache-hit proxy unavailable in recent usage fields.")
|
| 782 |
+
else:
|
| 783 |
+
st.caption(f"Cache-hit proxy: {metrics['cache_hit_proxy']:.2f}% across {metrics['cache_samples']} samples.")
|
| 784 |
+
|
| 785 |
+
st.subheader("Top tools (last 24h)")
|
| 786 |
+
top_tools = [{"tool": tool, "count": count} for tool, count in metrics["tool_counts"].most_common(20)]
|
| 787 |
+
if top_tools:
|
| 788 |
+
st.dataframe(top_tools, use_container_width=True, hide_index=True)
|
| 789 |
+
else:
|
| 790 |
+
st.info("No tool invocation events found in the last 24 hours.")
|
| 791 |
+
|
| 792 |
+
# Session Amplifier findings
|
| 793 |
+
st.subheader("Session Amplifier")
|
| 794 |
+
if sidecar_report:
|
| 795 |
+
patterns = sidecar_report.get("failure_patterns", [])
|
| 796 |
+
if patterns:
|
| 797 |
+
st.warning(f"{len(patterns)} failure pattern(s) detected — see Activity tab for details")
|
| 798 |
+
for p in patterns[:5]:
|
| 799 |
+
st.caption(f"• {p.get('description', p.get('pattern', '?'))} ({p.get('count', 0)})")
|
| 800 |
+
else:
|
| 801 |
+
st.success("No failure patterns detected")
|
| 802 |
+
if sidecar_report.get("sessions_reviewed"):
|
| 803 |
+
st.caption(f"Reviewed {sidecar_report['sessions_reviewed']} sessions")
|
| 804 |
+
elif sidecar_status == "reachable":
|
| 805 |
+
st.info("Sidecar reachable but not yet warmed up — run spool to populate")
|
| 806 |
+
else:
|
| 807 |
+
st.caption("Session Amplifier unavailable — see docs to deploy sidecar/session-amplifier/")
|
| 808 |
+
|
| 809 |
+
if sidecar_skills:
|
| 810 |
+
missing = sidecar_skills.get("mcps_missing_skill_surface", [])
|
| 811 |
+
if missing:
|
| 812 |
+
st.warning(f"{len(missing)} MCP(s) without skill surface: {', '.join(missing[:5])}")
|
| 813 |
+
else:
|
| 814 |
+
st.caption("All registered MCPs have skill surfaces ✓")
|
| 815 |
+
|
| 816 |
+
with tab_sessions:
|
| 817 |
+
# Prefer sidecar session list when available
|
| 818 |
+
sidecar_sessions = _fetch_sidecar_json("/sessions/recent?limit=50")
|
| 819 |
+
if sidecar_sessions and sidecar_sessions.get("sessions"):
|
| 820 |
+
st.subheader("Recent sessions (Session Amplifier)")
|
| 821 |
+
st.caption(f"Fetched at {fmt_local(datetime.datetime.now(datetime.timezone.utc).isoformat())}")
|
| 822 |
+
sess_rows = []
|
| 823 |
+
for sr in sidecar_sessions["sessions"]:
|
| 824 |
+
health = sr.get("health", "ok")
|
| 825 |
+
hints = sr.get("hints", [])
|
| 826 |
+
hint_str = "; ".join(hints) if hints else ""
|
| 827 |
+
sess_rows.append({
|
| 828 |
+
"agent": sr.get("agent_id", "?"),
|
| 829 |
+
"session_id": sr.get("session_id", "?")[:32],
|
| 830 |
+
"events": sr.get("event_count", 0),
|
| 831 |
+
"tools": sr.get("tool_result_count", 0),
|
| 832 |
+
"errors": sr.get("error_count", 0),
|
| 833 |
+
"health": health.upper(),
|
| 834 |
+
"hints": hint_str,
|
| 835 |
+
"last_event": fmt_local(sr.get("last_event_at")),
|
| 836 |
+
})
|
| 837 |
+
st.dataframe(sess_rows, use_container_width=True, hide_index=True)
|
| 838 |
+
else:
|
| 839 |
+
st.subheader("Session index")
|
| 840 |
+
agent_names = ["all"] + sorted({row["agent"] for row in session_rows})
|
| 841 |
+
selected_agent = st.selectbox("Filter agent", agent_names, index=0)
|
| 842 |
+
selected_kind = st.selectbox("Filter kind", ["all", "session", "discord", "cron", "subagent"], index=0)
|
| 843 |
+
filtered_rows = []
|
| 844 |
+
for row in session_rows:
|
| 845 |
+
if selected_agent != "all" and row["agent"] != selected_agent:
|
| 846 |
+
continue
|
| 847 |
+
if selected_kind != "all" and row["kind"] != selected_kind:
|
| 848 |
+
continue
|
| 849 |
+
filtered_rows.append(
|
| 850 |
+
{
|
| 851 |
+
"agent": row["agent"],
|
| 852 |
+
"kind": row["kind"],
|
| 853 |
+
"updated": row["updated"],
|
| 854 |
+
"sessionKey": row["sessionKey"],
|
| 855 |
+
"sessionId": row["sessionId"],
|
| 856 |
+
"deliveryTo": row["deliveryTo"],
|
| 857 |
+
}
|
| 858 |
+
)
|
| 859 |
+
st.dataframe(filtered_rows, use_container_width=True, hide_index=True)
|
| 860 |
+
|
| 861 |
+
st.subheader("Recent child/subagent sessions")
|
| 862 |
+
st.dataframe(
|
| 863 |
+
[
|
| 864 |
+
{
|
| 865 |
+
"agent": row["agent"],
|
| 866 |
+
"updated": row["updated"],
|
| 867 |
+
"sessionKey": row["sessionKey"],
|
| 868 |
+
"sessionId": row["sessionId"],
|
| 869 |
+
}
|
| 870 |
+
for row in subagent_rows[:50]
|
| 871 |
+
],
|
| 872 |
+
use_container_width=True,
|
| 873 |
+
hide_index=True,
|
| 874 |
+
)
|
| 875 |
+
|
| 876 |
+
with tab_cron:
|
| 877 |
+
# Health filter
|
| 878 |
+
health_filter = st.selectbox(
|
| 879 |
+
"Filter by health",
|
| 880 |
+
["all", "critical", "error", "warning", "stale", "running", "healthy", "disabled"],
|
| 881 |
+
index=0
|
| 882 |
+
)
|
| 883 |
+
|
| 884 |
+
filtered_cron = cron_rows
|
| 885 |
+
if health_filter != "all":
|
| 886 |
+
filtered_cron = [r for r in cron_rows if r["health"] == health_filter]
|
| 887 |
+
|
| 888 |
+
st.subheader(f"Cron jobs ({len(filtered_cron)} shown)")
|
| 889 |
+
|
| 890 |
+
# Color-coded health display
|
| 891 |
+
def health_color(health):
|
| 892 |
+
return {
|
| 893 |
+
"critical": "🔴",
|
| 894 |
+
"error": "🟠",
|
| 895 |
+
"warning": "🟡",
|
| 896 |
+
"stale": "⚪",
|
| 897 |
+
"running": "🟢",
|
| 898 |
+
"healthy": "✓",
|
| 899 |
+
"disabled": "⊘",
|
| 900 |
+
}.get(health, "?")
|
| 901 |
+
|
| 902 |
+
display_rows = []
|
| 903 |
+
for row in filtered_cron:
|
| 904 |
+
display_rows.append({
|
| 905 |
+
"health": f"{health_color(row['health'])} {row['health']}",
|
| 906 |
+
"name": row["name"],
|
| 907 |
+
"enabled": "✓" if row["enabled"] else "✗",
|
| 908 |
+
"schedule": row["schedule"],
|
| 909 |
+
"lastRun": row["lastRun"],
|
| 910 |
+
"lastStatus": row["lastStatus"],
|
| 911 |
+
"nextRun": row["nextRun"],
|
| 912 |
+
"errors": row["consecutiveErrors"] if row["consecutiveErrors"] > 0 else "",
|
| 913 |
+
"reason": row["healthReason"],
|
| 914 |
+
})
|
| 915 |
+
|
| 916 |
+
st.dataframe(display_rows, use_container_width=True, hide_index=True)
|
| 917 |
+
|
| 918 |
+
# Quick stats
|
| 919 |
+
if critical_jobs or warning_jobs or stale_jobs:
|
| 920 |
+
st.error(f"**{len(critical_jobs)}** critical, **{len(warning_jobs)}** warning, **{len(stale_jobs)}** stale jobs need attention")
|
| 921 |
+
else:
|
| 922 |
+
st.success("All monitored cron jobs are healthy")
|
| 923 |
+
|
| 924 |
+
st.subheader("Running now")
|
| 925 |
+
if running_cron:
|
| 926 |
+
st.dataframe(running_cron, use_container_width=True, hide_index=True)
|
| 927 |
+
else:
|
| 928 |
+
st.info("No cron jobs currently marked running.")
|
| 929 |
+
|
| 930 |
+
with tab_activity:
|
| 931 |
+
st.subheader("Recent transcript activity")
|
| 932 |
+
st.dataframe(
|
| 933 |
+
[
|
| 934 |
+
{
|
| 935 |
+
"agent": row["agent"],
|
| 936 |
+
"updated": row["updated"],
|
| 937 |
+
"role": row["role"],
|
| 938 |
+
"sessionKey": row["sessionKey"],
|
| 939 |
+
"summary": row["summary"],
|
| 940 |
+
"file": row["file"],
|
| 941 |
+
}
|
| 942 |
+
for row in activity_rows[:50]
|
| 943 |
+
],
|
| 944 |
+
use_container_width=True,
|
| 945 |
+
hide_index=True,
|
| 946 |
+
)
|
| 947 |
+
|
| 948 |
+
with tab_live:
|
| 949 |
+
st.subheader("Live Session Activity Stream")
|
| 950 |
+
|
| 951 |
+
# Prefer sidecar-based session list for selection when available
|
| 952 |
+
sidecar_sessions = _fetch_sidecar_json("/sessions/recent?limit=30") or {}
|
| 953 |
+
sidecar_session_list = sidecar_sessions.get("sessions") or []
|
| 954 |
+
|
| 955 |
+
# Show spooler health / readiness
|
| 956 |
+
if sidecar_status == "reachable":
|
| 957 |
+
st.info("Session Amplifier reachable but not yet warmed up — spool may be empty")
|
| 958 |
+
elif sidecar_status == "unavailable":
|
| 959 |
+
st.warning("Session Amplifier unavailable. Deploy: cd sidecar/session-amplifier && docker compose up -d")
|
| 960 |
+
|
| 961 |
+
st.caption("Normalized activity feed from Session Amplifier. "
|
| 962 |
+
"For continuous streaming, use: python scripts/session_amplifier_live_monitor.py")
|
| 963 |
+
|
| 964 |
+
# Build unified session options: sidecar sessions first, then fallback to dashboard sessions
|
| 965 |
+
if sidecar_session_list:
|
| 966 |
+
sidecar_opts = [
|
| 967 |
+
f"[SA] {s.get('agent_id','?')}: {clean_session_label(s.get('display_title') or '', fallback=s.get('session_id','?')[:32])}"
|
| 968 |
+
for s in sidecar_session_list[:20]
|
| 969 |
+
]
|
| 970 |
+
sidecar_map = {opt: s for opt, s in zip(sidecar_opts, sidecar_session_list[:20])}
|
| 971 |
+
use_sidecar = True
|
| 972 |
+
else:
|
| 973 |
+
recent = [r for r in session_rows if r["updatedAt"] and r["updatedAt"] > datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=24)]
|
| 974 |
+
sidecar_opts = []
|
| 975 |
+
sidecar_map = {}
|
| 976 |
+
use_sidecar = False
|
| 977 |
+
|
| 978 |
+
# Fallback: dashboard-based session list
|
| 979 |
+
recent_sessions = [r for r in session_rows if r["updatedAt"] and r["updatedAt"] > datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=24)]
|
| 980 |
+
dash_opts = [f"{r['agent']}: {session_display_label(r)}" for r in recent_sessions[:20]]
|
| 981 |
+
dash_map = {opt: r for opt, r in zip(dash_opts, recent_sessions[:20])}
|
| 982 |
+
|
| 983 |
+
# Combine: sidecar sessions first, then dashboard sessions
|
| 984 |
+
all_opts = sidecar_opts + dash_opts
|
| 985 |
+
all_map = {**sidecar_map, **dash_map}
|
| 986 |
+
|
| 987 |
+
if all_opts:
|
| 988 |
+
selected = st.selectbox("Select session to monitor", all_opts, index=0)
|
| 989 |
+
selected_item = all_map[selected]
|
| 990 |
+
|
| 991 |
+
if use_sidecar and selected in sidecar_map:
|
| 992 |
+
sel_session_id = selected_item["session_id"]
|
| 993 |
+
sel_agent = selected_item["agent_id"]
|
| 994 |
+
sel_title = clean_session_label(selected_item.get("display_title") or "", fallback=sel_session_id)
|
| 995 |
+
sel_updated = fmt_local(selected_item.get("last_event_at"))
|
| 996 |
+
sel_health = selected_item.get("health", "ok").upper()
|
| 997 |
+
sel_hints = "; ".join(selected_item.get("hints", []) or [])
|
| 998 |
+
st.caption(f"Health: {sel_health} | Events: {selected_item.get('event_count',0)} | Tools: {selected_item.get('tool_result_count',0)} | Errors: {selected_item.get('error_count',0)}")
|
| 999 |
+
if sel_hints:
|
| 1000 |
+
st.caption(f"Hints: {sel_hints}")
|
| 1001 |
+
|
| 1002 |
+
# Fetch activity from sidecar
|
| 1003 |
+
activity_data = _fetch_sidecar_json(f"/session/{sel_session_id}/activity?limit=200")
|
| 1004 |
+
activities = activity_data.get("activity", []) if activity_data else []
|
| 1005 |
+
else:
|
| 1006 |
+
if isinstance(selected_item, dict) and "sessionKey" in selected_item:
|
| 1007 |
+
sel_session_id = selected_item["sessionKey"]
|
| 1008 |
+
sel_agent = selected_item["agent"]
|
| 1009 |
+
sel_title = session_display_label(selected_item)
|
| 1010 |
+
sel_updated = selected_item["updated"]
|
| 1011 |
+
else:
|
| 1012 |
+
sel_session_id = "?"
|
| 1013 |
+
sel_agent = "?"
|
| 1014 |
+
sel_title = "?"
|
| 1015 |
+
sel_updated = "?"
|
| 1016 |
+
st.caption(f"Dashboard session — may not appear in Session Amplifier spool")
|
| 1017 |
+
activities = get_live_session_activity(sel_session_id, sel_agent, max_lines=200)
|
| 1018 |
+
|
| 1019 |
+
col1, col2, col3 = st.columns([1, 1, 1])
|
| 1020 |
+
with col1:
|
| 1021 |
+
st.write(f"**Agent:** {sel_agent}")
|
| 1022 |
+
with col2:
|
| 1023 |
+
st.write(f"**Session:** {sel_title}")
|
| 1024 |
+
with col3:
|
| 1025 |
+
st.write(f"**Last event:** {sel_updated}")
|
| 1026 |
+
|
| 1027 |
+
if activities:
|
| 1028 |
+
current_bucket = None
|
| 1029 |
+
for act in reversed(activities[-50:]):
|
| 1030 |
+
ts = act.get("timestamp")
|
| 1031 |
+
if ts:
|
| 1032 |
+
try:
|
| 1033 |
+
bucket = ts.replace(minute=(ts.minute // 5) * 5, second=0, microsecond=0)
|
| 1034 |
+
if bucket != current_bucket:
|
| 1035 |
+
current_bucket = bucket
|
| 1036 |
+
st.divider()
|
| 1037 |
+
st.caption(f"📍 {fmt_local(bucket)}")
|
| 1038 |
+
except Exception:
|
| 1039 |
+
pass
|
| 1040 |
+
|
| 1041 |
+
evtype = act.get("type") or act.get("event_type", "")
|
| 1042 |
+
ts_str = act.get("time") or ""
|
| 1043 |
+
|
| 1044 |
+
if evtype == "thinking":
|
| 1045 |
+
with st.expander(f"🧠 {ts_str} — Thinking", expanded=False):
|
| 1046 |
+
st.text(act.get("details", "")[:1000] or "(no details)")
|
| 1047 |
+
elif evtype == "tool":
|
| 1048 |
+
st.markdown(f"**🔧 {ts_str}** — {act.get('summary', '')}")
|
| 1049 |
+
if act.get("details"):
|
| 1050 |
+
st.code(act["details"][:500], language="bash")
|
| 1051 |
+
elif evtype == "tool_call":
|
| 1052 |
+
st.markdown(f"**⚙ {ts_str}** → {act.get('tool_name', '')} — {act.get('summary', '')}")
|
| 1053 |
+
elif evtype == "tool_result" or evtype == "result":
|
| 1054 |
+
icon = "❌" if act.get("is_error") else "✅"
|
| 1055 |
+
st.markdown(f"**{icon} {ts_str}** — {act.get('summary', '')}")
|
| 1056 |
+
if act.get("details"):
|
| 1057 |
+
st.text(act["details"][:300])
|
| 1058 |
+
elif evtype == "tool_error":
|
| 1059 |
+
st.error(f"⚠ {ts_str} — {act.get('summary', 'tool error')}")
|
| 1060 |
+
if act.get("details"):
|
| 1061 |
+
st.text(act["details"][:300])
|
| 1062 |
+
elif evtype == "assistant_meta":
|
| 1063 |
+
st.markdown(f"**💡 {ts_str}** — {act.get('summary', '')[:120]}")
|
| 1064 |
+
elif evtype == "assistant_thinking":
|
| 1065 |
+
with st.expander(f"🧠 {ts_str} — Thinking", expanded=False):
|
| 1066 |
+
st.text(act.get("details", "")[:500] or "(no details)")
|
| 1067 |
+
elif evtype == "assistant_text":
|
| 1068 |
+
st.markdown(f"**💬 {ts_str}** — {act.get('summary', '')[:120]}")
|
| 1069 |
+
elif evtype == "user_message":
|
| 1070 |
+
with st.expander(f"👤 {ts_str} — User message", expanded=False):
|
| 1071 |
+
st.text(act.get("details", "")[:300])
|
| 1072 |
+
else:
|
| 1073 |
+
st.caption(f"[{evtype or '?'}] {ts_str} — {act.get('summary', '')[:80]}")
|
| 1074 |
+
else:
|
| 1075 |
+
st.info("No activity found for this session (may be archived or not yet written)")
|
| 1076 |
+
else:
|
| 1077 |
+
st.info("No recent sessions found in the last 24 hours")
|
| 1078 |
+
|
| 1079 |
+
with tab_model_ops:
|
| 1080 |
+
render_model_ops_tab(session_rows)
|
| 1081 |
+
|
| 1082 |
+
|
| 1083 |
+
if __name__ == "__main__":
|
| 1084 |
+
main()
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: '3.8'
|
| 2 |
+
|
| 3 |
+
services:
|
| 4 |
+
ops-dashboard:
|
| 5 |
+
image: ops-dashboard:latest
|
| 6 |
+
container_name: ops-dashboard
|
| 7 |
+
restart: unless-stopped
|
| 8 |
+
working_dir: /app
|
| 9 |
+
command: [ "streamlit", "run", "app.py" ]
|
| 10 |
+
volumes:
|
| 11 |
+
- ./app.py:/app/app.py
|
| 12 |
+
- ${OPENCLAW_HOME:-~/.openclaw}:/openclaw:ro
|
| 13 |
+
environment:
|
| 14 |
+
OPENCLAW_AGENTS_ROOT: /openclaw/agents
|
| 15 |
+
networks:
|
| 16 |
+
- librechat_default
|
| 17 |
+
|
| 18 |
+
ops-dashboard-react:
|
| 19 |
+
build:
|
| 20 |
+
context: ./react-dashboard
|
| 21 |
+
args:
|
| 22 |
+
VITE_BASE_PATH: /react/
|
| 23 |
+
image: ops-dashboard-react:latest
|
| 24 |
+
container_name: ops-dashboard-react
|
| 25 |
+
restart: unless-stopped
|
| 26 |
+
networks:
|
| 27 |
+
- librechat_default
|
| 28 |
+
|
| 29 |
+
networks:
|
| 30 |
+
librechat_default:
|
| 31 |
+
external: true
|
| 32 |
+
name: librechat_default
|
react-dashboard/.dockerignore
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules
|
| 2 |
+
dist
|
| 3 |
+
.git
|
| 4 |
+
.cache
|
| 5 |
+
npm-debug.log*
|
| 6 |
+
|
react-dashboard/.gitignore
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules/
|
| 2 |
+
dist/
|
| 3 |
+
.env
|
| 4 |
+
.env.*
|
react-dashboard/Dockerfile
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM node:22-alpine AS build
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
COPY package*.json ./
|
| 6 |
+
RUN npm ci
|
| 7 |
+
|
| 8 |
+
COPY . .
|
| 9 |
+
ARG VITE_BASE_PATH=/react/
|
| 10 |
+
ENV VITE_BASE_PATH=$VITE_BASE_PATH
|
| 11 |
+
RUN npm run build
|
| 12 |
+
|
| 13 |
+
FROM nginx:1.27-alpine
|
| 14 |
+
|
| 15 |
+
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
| 16 |
+
COPY --from=build /app/dist /usr/share/nginx/html
|
| 17 |
+
|
| 18 |
+
EXPOSE 8080
|
| 19 |
+
|
react-dashboard/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OpenClaw Ops React Dashboard
|
| 2 |
+
|
| 3 |
+
Paperclip-style action dashboard prototype for Session Amplifier data.
|
| 4 |
+
|
| 5 |
+
## Run locally
|
| 6 |
+
|
| 7 |
+
npm install
|
| 8 |
+
npm run dev -- --port 5177
|
| 9 |
+
|
| 10 |
+
The Vite dev server proxies /session-amplifier/* to SESSION_AMPLIFIER_BASE_URL or http://localhost:8477.
|
| 11 |
+
|
| 12 |
+
For browser access from another host, set the API URL directly:
|
| 13 |
+
|
| 14 |
+
VITE_SESSION_AMPLIFIER_BASE_URL=http://session-amplifier:8477 npm run dev -- --port 5177
|
| 15 |
+
|
| 16 |
+
## Status
|
| 17 |
+
|
| 18 |
+
- Additive prototype beside the existing Streamlit dashboard.
|
| 19 |
+
- Uses /health, /sessions/active-bulk, and /review/skills.
|
| 20 |
+
- Run/pause/open controls are intentionally disabled until first-class action endpoints exist.
|
react-dashboard/index.html
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>OpenClaw Ops</title>
|
| 7 |
+
</head>
|
| 8 |
+
<body>
|
| 9 |
+
<div id="root"></div>
|
| 10 |
+
<script type="module" src="/src/main.jsx"></script>
|
| 11 |
+
</body>
|
| 12 |
+
</html>
|
react-dashboard/nginx.conf
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
server {
|
| 2 |
+
listen 8080;
|
| 3 |
+
server_name _;
|
| 4 |
+
root /usr/share/nginx/html;
|
| 5 |
+
index index.html;
|
| 6 |
+
|
| 7 |
+
location /healthz {
|
| 8 |
+
access_log off;
|
| 9 |
+
add_header Content-Type text/plain;
|
| 10 |
+
return 200 "ok\n";
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
location / {
|
| 14 |
+
try_files $uri $uri/ /index.html;
|
| 15 |
+
}
|
| 16 |
+
}
|
| 17 |
+
|
react-dashboard/package-lock.json
ADDED
|
@@ -0,0 +1,1673 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "openclaw-ops-react-dashboard",
|
| 3 |
+
"version": "0.1.0",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "openclaw-ops-react-dashboard",
|
| 9 |
+
"version": "0.1.0",
|
| 10 |
+
"dependencies": {
|
| 11 |
+
"@vitejs/plugin-react": "^5.0.4",
|
| 12 |
+
"lucide-react": "^0.468.0",
|
| 13 |
+
"react": "^19.2.0",
|
| 14 |
+
"react-dom": "^19.2.0",
|
| 15 |
+
"vite": "^7.2.4"
|
| 16 |
+
}
|
| 17 |
+
},
|
| 18 |
+
"node_modules/@babel/code-frame": {
|
| 19 |
+
"version": "7.29.0",
|
| 20 |
+
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
|
| 21 |
+
"integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
|
| 22 |
+
"license": "MIT",
|
| 23 |
+
"dependencies": {
|
| 24 |
+
"@babel/helper-validator-identifier": "^7.28.5",
|
| 25 |
+
"js-tokens": "^4.0.0",
|
| 26 |
+
"picocolors": "^1.1.1"
|
| 27 |
+
},
|
| 28 |
+
"engines": {
|
| 29 |
+
"node": ">=6.9.0"
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
"node_modules/@babel/compat-data": {
|
| 33 |
+
"version": "7.29.3",
|
| 34 |
+
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz",
|
| 35 |
+
"integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==",
|
| 36 |
+
"license": "MIT",
|
| 37 |
+
"engines": {
|
| 38 |
+
"node": ">=6.9.0"
|
| 39 |
+
}
|
| 40 |
+
},
|
| 41 |
+
"node_modules/@babel/core": {
|
| 42 |
+
"version": "7.29.0",
|
| 43 |
+
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
|
| 44 |
+
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
| 45 |
+
"license": "MIT",
|
| 46 |
+
"dependencies": {
|
| 47 |
+
"@babel/code-frame": "^7.29.0",
|
| 48 |
+
"@babel/generator": "^7.29.0",
|
| 49 |
+
"@babel/helper-compilation-targets": "^7.28.6",
|
| 50 |
+
"@babel/helper-module-transforms": "^7.28.6",
|
| 51 |
+
"@babel/helpers": "^7.28.6",
|
| 52 |
+
"@babel/parser": "^7.29.0",
|
| 53 |
+
"@babel/template": "^7.28.6",
|
| 54 |
+
"@babel/traverse": "^7.29.0",
|
| 55 |
+
"@babel/types": "^7.29.0",
|
| 56 |
+
"@jridgewell/remapping": "^2.3.5",
|
| 57 |
+
"convert-source-map": "^2.0.0",
|
| 58 |
+
"debug": "^4.1.0",
|
| 59 |
+
"gensync": "^1.0.0-beta.2",
|
| 60 |
+
"json5": "^2.2.3",
|
| 61 |
+
"semver": "^6.3.1"
|
| 62 |
+
},
|
| 63 |
+
"engines": {
|
| 64 |
+
"node": ">=6.9.0"
|
| 65 |
+
},
|
| 66 |
+
"funding": {
|
| 67 |
+
"type": "opencollective",
|
| 68 |
+
"url": "https://opencollective.com/babel"
|
| 69 |
+
}
|
| 70 |
+
},
|
| 71 |
+
"node_modules/@babel/generator": {
|
| 72 |
+
"version": "7.29.1",
|
| 73 |
+
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
|
| 74 |
+
"integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
|
| 75 |
+
"license": "MIT",
|
| 76 |
+
"dependencies": {
|
| 77 |
+
"@babel/parser": "^7.29.0",
|
| 78 |
+
"@babel/types": "^7.29.0",
|
| 79 |
+
"@jridgewell/gen-mapping": "^0.3.12",
|
| 80 |
+
"@jridgewell/trace-mapping": "^0.3.28",
|
| 81 |
+
"jsesc": "^3.0.2"
|
| 82 |
+
},
|
| 83 |
+
"engines": {
|
| 84 |
+
"node": ">=6.9.0"
|
| 85 |
+
}
|
| 86 |
+
},
|
| 87 |
+
"node_modules/@babel/helper-compilation-targets": {
|
| 88 |
+
"version": "7.28.6",
|
| 89 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
|
| 90 |
+
"integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
|
| 91 |
+
"license": "MIT",
|
| 92 |
+
"dependencies": {
|
| 93 |
+
"@babel/compat-data": "^7.28.6",
|
| 94 |
+
"@babel/helper-validator-option": "^7.27.1",
|
| 95 |
+
"browserslist": "^4.24.0",
|
| 96 |
+
"lru-cache": "^5.1.1",
|
| 97 |
+
"semver": "^6.3.1"
|
| 98 |
+
},
|
| 99 |
+
"engines": {
|
| 100 |
+
"node": ">=6.9.0"
|
| 101 |
+
}
|
| 102 |
+
},
|
| 103 |
+
"node_modules/@babel/helper-globals": {
|
| 104 |
+
"version": "7.28.0",
|
| 105 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
|
| 106 |
+
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
|
| 107 |
+
"license": "MIT",
|
| 108 |
+
"engines": {
|
| 109 |
+
"node": ">=6.9.0"
|
| 110 |
+
}
|
| 111 |
+
},
|
| 112 |
+
"node_modules/@babel/helper-module-imports": {
|
| 113 |
+
"version": "7.28.6",
|
| 114 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
|
| 115 |
+
"integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
|
| 116 |
+
"license": "MIT",
|
| 117 |
+
"dependencies": {
|
| 118 |
+
"@babel/traverse": "^7.28.6",
|
| 119 |
+
"@babel/types": "^7.28.6"
|
| 120 |
+
},
|
| 121 |
+
"engines": {
|
| 122 |
+
"node": ">=6.9.0"
|
| 123 |
+
}
|
| 124 |
+
},
|
| 125 |
+
"node_modules/@babel/helper-module-transforms": {
|
| 126 |
+
"version": "7.28.6",
|
| 127 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
|
| 128 |
+
"integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
|
| 129 |
+
"license": "MIT",
|
| 130 |
+
"dependencies": {
|
| 131 |
+
"@babel/helper-module-imports": "^7.28.6",
|
| 132 |
+
"@babel/helper-validator-identifier": "^7.28.5",
|
| 133 |
+
"@babel/traverse": "^7.28.6"
|
| 134 |
+
},
|
| 135 |
+
"engines": {
|
| 136 |
+
"node": ">=6.9.0"
|
| 137 |
+
},
|
| 138 |
+
"peerDependencies": {
|
| 139 |
+
"@babel/core": "^7.0.0"
|
| 140 |
+
}
|
| 141 |
+
},
|
| 142 |
+
"node_modules/@babel/helper-plugin-utils": {
|
| 143 |
+
"version": "7.28.6",
|
| 144 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
|
| 145 |
+
"integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
|
| 146 |
+
"license": "MIT",
|
| 147 |
+
"engines": {
|
| 148 |
+
"node": ">=6.9.0"
|
| 149 |
+
}
|
| 150 |
+
},
|
| 151 |
+
"node_modules/@babel/helper-string-parser": {
|
| 152 |
+
"version": "7.27.1",
|
| 153 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
| 154 |
+
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
| 155 |
+
"license": "MIT",
|
| 156 |
+
"engines": {
|
| 157 |
+
"node": ">=6.9.0"
|
| 158 |
+
}
|
| 159 |
+
},
|
| 160 |
+
"node_modules/@babel/helper-validator-identifier": {
|
| 161 |
+
"version": "7.28.5",
|
| 162 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
| 163 |
+
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
| 164 |
+
"license": "MIT",
|
| 165 |
+
"engines": {
|
| 166 |
+
"node": ">=6.9.0"
|
| 167 |
+
}
|
| 168 |
+
},
|
| 169 |
+
"node_modules/@babel/helper-validator-option": {
|
| 170 |
+
"version": "7.27.1",
|
| 171 |
+
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
|
| 172 |
+
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
|
| 173 |
+
"license": "MIT",
|
| 174 |
+
"engines": {
|
| 175 |
+
"node": ">=6.9.0"
|
| 176 |
+
}
|
| 177 |
+
},
|
| 178 |
+
"node_modules/@babel/helpers": {
|
| 179 |
+
"version": "7.29.2",
|
| 180 |
+
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
|
| 181 |
+
"integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
|
| 182 |
+
"license": "MIT",
|
| 183 |
+
"dependencies": {
|
| 184 |
+
"@babel/template": "^7.28.6",
|
| 185 |
+
"@babel/types": "^7.29.0"
|
| 186 |
+
},
|
| 187 |
+
"engines": {
|
| 188 |
+
"node": ">=6.9.0"
|
| 189 |
+
}
|
| 190 |
+
},
|
| 191 |
+
"node_modules/@babel/parser": {
|
| 192 |
+
"version": "7.29.3",
|
| 193 |
+
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz",
|
| 194 |
+
"integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
|
| 195 |
+
"license": "MIT",
|
| 196 |
+
"dependencies": {
|
| 197 |
+
"@babel/types": "^7.29.0"
|
| 198 |
+
},
|
| 199 |
+
"bin": {
|
| 200 |
+
"parser": "bin/babel-parser.js"
|
| 201 |
+
},
|
| 202 |
+
"engines": {
|
| 203 |
+
"node": ">=6.0.0"
|
| 204 |
+
}
|
| 205 |
+
},
|
| 206 |
+
"node_modules/@babel/plugin-transform-react-jsx-self": {
|
| 207 |
+
"version": "7.27.1",
|
| 208 |
+
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
|
| 209 |
+
"integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
|
| 210 |
+
"license": "MIT",
|
| 211 |
+
"dependencies": {
|
| 212 |
+
"@babel/helper-plugin-utils": "^7.27.1"
|
| 213 |
+
},
|
| 214 |
+
"engines": {
|
| 215 |
+
"node": ">=6.9.0"
|
| 216 |
+
},
|
| 217 |
+
"peerDependencies": {
|
| 218 |
+
"@babel/core": "^7.0.0-0"
|
| 219 |
+
}
|
| 220 |
+
},
|
| 221 |
+
"node_modules/@babel/plugin-transform-react-jsx-source": {
|
| 222 |
+
"version": "7.27.1",
|
| 223 |
+
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
|
| 224 |
+
"integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
|
| 225 |
+
"license": "MIT",
|
| 226 |
+
"dependencies": {
|
| 227 |
+
"@babel/helper-plugin-utils": "^7.27.1"
|
| 228 |
+
},
|
| 229 |
+
"engines": {
|
| 230 |
+
"node": ">=6.9.0"
|
| 231 |
+
},
|
| 232 |
+
"peerDependencies": {
|
| 233 |
+
"@babel/core": "^7.0.0-0"
|
| 234 |
+
}
|
| 235 |
+
},
|
| 236 |
+
"node_modules/@babel/template": {
|
| 237 |
+
"version": "7.28.6",
|
| 238 |
+
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
| 239 |
+
"integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
|
| 240 |
+
"license": "MIT",
|
| 241 |
+
"dependencies": {
|
| 242 |
+
"@babel/code-frame": "^7.28.6",
|
| 243 |
+
"@babel/parser": "^7.28.6",
|
| 244 |
+
"@babel/types": "^7.28.6"
|
| 245 |
+
},
|
| 246 |
+
"engines": {
|
| 247 |
+
"node": ">=6.9.0"
|
| 248 |
+
}
|
| 249 |
+
},
|
| 250 |
+
"node_modules/@babel/traverse": {
|
| 251 |
+
"version": "7.29.0",
|
| 252 |
+
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
|
| 253 |
+
"integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
|
| 254 |
+
"license": "MIT",
|
| 255 |
+
"dependencies": {
|
| 256 |
+
"@babel/code-frame": "^7.29.0",
|
| 257 |
+
"@babel/generator": "^7.29.0",
|
| 258 |
+
"@babel/helper-globals": "^7.28.0",
|
| 259 |
+
"@babel/parser": "^7.29.0",
|
| 260 |
+
"@babel/template": "^7.28.6",
|
| 261 |
+
"@babel/types": "^7.29.0",
|
| 262 |
+
"debug": "^4.3.1"
|
| 263 |
+
},
|
| 264 |
+
"engines": {
|
| 265 |
+
"node": ">=6.9.0"
|
| 266 |
+
}
|
| 267 |
+
},
|
| 268 |
+
"node_modules/@babel/types": {
|
| 269 |
+
"version": "7.29.0",
|
| 270 |
+
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
| 271 |
+
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
| 272 |
+
"license": "MIT",
|
| 273 |
+
"dependencies": {
|
| 274 |
+
"@babel/helper-string-parser": "^7.27.1",
|
| 275 |
+
"@babel/helper-validator-identifier": "^7.28.5"
|
| 276 |
+
},
|
| 277 |
+
"engines": {
|
| 278 |
+
"node": ">=6.9.0"
|
| 279 |
+
}
|
| 280 |
+
},
|
| 281 |
+
"node_modules/@esbuild/aix-ppc64": {
|
| 282 |
+
"version": "0.27.7",
|
| 283 |
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
|
| 284 |
+
"integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
|
| 285 |
+
"cpu": [
|
| 286 |
+
"ppc64"
|
| 287 |
+
],
|
| 288 |
+
"license": "MIT",
|
| 289 |
+
"optional": true,
|
| 290 |
+
"os": [
|
| 291 |
+
"aix"
|
| 292 |
+
],
|
| 293 |
+
"engines": {
|
| 294 |
+
"node": ">=18"
|
| 295 |
+
}
|
| 296 |
+
},
|
| 297 |
+
"node_modules/@esbuild/android-arm": {
|
| 298 |
+
"version": "0.27.7",
|
| 299 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
|
| 300 |
+
"integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
|
| 301 |
+
"cpu": [
|
| 302 |
+
"arm"
|
| 303 |
+
],
|
| 304 |
+
"license": "MIT",
|
| 305 |
+
"optional": true,
|
| 306 |
+
"os": [
|
| 307 |
+
"android"
|
| 308 |
+
],
|
| 309 |
+
"engines": {
|
| 310 |
+
"node": ">=18"
|
| 311 |
+
}
|
| 312 |
+
},
|
| 313 |
+
"node_modules/@esbuild/android-arm64": {
|
| 314 |
+
"version": "0.27.7",
|
| 315 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
|
| 316 |
+
"integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
|
| 317 |
+
"cpu": [
|
| 318 |
+
"arm64"
|
| 319 |
+
],
|
| 320 |
+
"license": "MIT",
|
| 321 |
+
"optional": true,
|
| 322 |
+
"os": [
|
| 323 |
+
"android"
|
| 324 |
+
],
|
| 325 |
+
"engines": {
|
| 326 |
+
"node": ">=18"
|
| 327 |
+
}
|
| 328 |
+
},
|
| 329 |
+
"node_modules/@esbuild/android-x64": {
|
| 330 |
+
"version": "0.27.7",
|
| 331 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
|
| 332 |
+
"integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
|
| 333 |
+
"cpu": [
|
| 334 |
+
"x64"
|
| 335 |
+
],
|
| 336 |
+
"license": "MIT",
|
| 337 |
+
"optional": true,
|
| 338 |
+
"os": [
|
| 339 |
+
"android"
|
| 340 |
+
],
|
| 341 |
+
"engines": {
|
| 342 |
+
"node": ">=18"
|
| 343 |
+
}
|
| 344 |
+
},
|
| 345 |
+
"node_modules/@esbuild/darwin-arm64": {
|
| 346 |
+
"version": "0.27.7",
|
| 347 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
|
| 348 |
+
"integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
|
| 349 |
+
"cpu": [
|
| 350 |
+
"arm64"
|
| 351 |
+
],
|
| 352 |
+
"license": "MIT",
|
| 353 |
+
"optional": true,
|
| 354 |
+
"os": [
|
| 355 |
+
"darwin"
|
| 356 |
+
],
|
| 357 |
+
"engines": {
|
| 358 |
+
"node": ">=18"
|
| 359 |
+
}
|
| 360 |
+
},
|
| 361 |
+
"node_modules/@esbuild/darwin-x64": {
|
| 362 |
+
"version": "0.27.7",
|
| 363 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
|
| 364 |
+
"integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
|
| 365 |
+
"cpu": [
|
| 366 |
+
"x64"
|
| 367 |
+
],
|
| 368 |
+
"license": "MIT",
|
| 369 |
+
"optional": true,
|
| 370 |
+
"os": [
|
| 371 |
+
"darwin"
|
| 372 |
+
],
|
| 373 |
+
"engines": {
|
| 374 |
+
"node": ">=18"
|
| 375 |
+
}
|
| 376 |
+
},
|
| 377 |
+
"node_modules/@esbuild/freebsd-arm64": {
|
| 378 |
+
"version": "0.27.7",
|
| 379 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
|
| 380 |
+
"integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
|
| 381 |
+
"cpu": [
|
| 382 |
+
"arm64"
|
| 383 |
+
],
|
| 384 |
+
"license": "MIT",
|
| 385 |
+
"optional": true,
|
| 386 |
+
"os": [
|
| 387 |
+
"freebsd"
|
| 388 |
+
],
|
| 389 |
+
"engines": {
|
| 390 |
+
"node": ">=18"
|
| 391 |
+
}
|
| 392 |
+
},
|
| 393 |
+
"node_modules/@esbuild/freebsd-x64": {
|
| 394 |
+
"version": "0.27.7",
|
| 395 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
|
| 396 |
+
"integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
|
| 397 |
+
"cpu": [
|
| 398 |
+
"x64"
|
| 399 |
+
],
|
| 400 |
+
"license": "MIT",
|
| 401 |
+
"optional": true,
|
| 402 |
+
"os": [
|
| 403 |
+
"freebsd"
|
| 404 |
+
],
|
| 405 |
+
"engines": {
|
| 406 |
+
"node": ">=18"
|
| 407 |
+
}
|
| 408 |
+
},
|
| 409 |
+
"node_modules/@esbuild/linux-arm": {
|
| 410 |
+
"version": "0.27.7",
|
| 411 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
|
| 412 |
+
"integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
|
| 413 |
+
"cpu": [
|
| 414 |
+
"arm"
|
| 415 |
+
],
|
| 416 |
+
"license": "MIT",
|
| 417 |
+
"optional": true,
|
| 418 |
+
"os": [
|
| 419 |
+
"linux"
|
| 420 |
+
],
|
| 421 |
+
"engines": {
|
| 422 |
+
"node": ">=18"
|
| 423 |
+
}
|
| 424 |
+
},
|
| 425 |
+
"node_modules/@esbuild/linux-arm64": {
|
| 426 |
+
"version": "0.27.7",
|
| 427 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
|
| 428 |
+
"integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
|
| 429 |
+
"cpu": [
|
| 430 |
+
"arm64"
|
| 431 |
+
],
|
| 432 |
+
"license": "MIT",
|
| 433 |
+
"optional": true,
|
| 434 |
+
"os": [
|
| 435 |
+
"linux"
|
| 436 |
+
],
|
| 437 |
+
"engines": {
|
| 438 |
+
"node": ">=18"
|
| 439 |
+
}
|
| 440 |
+
},
|
| 441 |
+
"node_modules/@esbuild/linux-ia32": {
|
| 442 |
+
"version": "0.27.7",
|
| 443 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
|
| 444 |
+
"integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
|
| 445 |
+
"cpu": [
|
| 446 |
+
"ia32"
|
| 447 |
+
],
|
| 448 |
+
"license": "MIT",
|
| 449 |
+
"optional": true,
|
| 450 |
+
"os": [
|
| 451 |
+
"linux"
|
| 452 |
+
],
|
| 453 |
+
"engines": {
|
| 454 |
+
"node": ">=18"
|
| 455 |
+
}
|
| 456 |
+
},
|
| 457 |
+
"node_modules/@esbuild/linux-loong64": {
|
| 458 |
+
"version": "0.27.7",
|
| 459 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
|
| 460 |
+
"integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
|
| 461 |
+
"cpu": [
|
| 462 |
+
"loong64"
|
| 463 |
+
],
|
| 464 |
+
"license": "MIT",
|
| 465 |
+
"optional": true,
|
| 466 |
+
"os": [
|
| 467 |
+
"linux"
|
| 468 |
+
],
|
| 469 |
+
"engines": {
|
| 470 |
+
"node": ">=18"
|
| 471 |
+
}
|
| 472 |
+
},
|
| 473 |
+
"node_modules/@esbuild/linux-mips64el": {
|
| 474 |
+
"version": "0.27.7",
|
| 475 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
|
| 476 |
+
"integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
|
| 477 |
+
"cpu": [
|
| 478 |
+
"mips64el"
|
| 479 |
+
],
|
| 480 |
+
"license": "MIT",
|
| 481 |
+
"optional": true,
|
| 482 |
+
"os": [
|
| 483 |
+
"linux"
|
| 484 |
+
],
|
| 485 |
+
"engines": {
|
| 486 |
+
"node": ">=18"
|
| 487 |
+
}
|
| 488 |
+
},
|
| 489 |
+
"node_modules/@esbuild/linux-ppc64": {
|
| 490 |
+
"version": "0.27.7",
|
| 491 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
|
| 492 |
+
"integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
|
| 493 |
+
"cpu": [
|
| 494 |
+
"ppc64"
|
| 495 |
+
],
|
| 496 |
+
"license": "MIT",
|
| 497 |
+
"optional": true,
|
| 498 |
+
"os": [
|
| 499 |
+
"linux"
|
| 500 |
+
],
|
| 501 |
+
"engines": {
|
| 502 |
+
"node": ">=18"
|
| 503 |
+
}
|
| 504 |
+
},
|
| 505 |
+
"node_modules/@esbuild/linux-riscv64": {
|
| 506 |
+
"version": "0.27.7",
|
| 507 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
|
| 508 |
+
"integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
|
| 509 |
+
"cpu": [
|
| 510 |
+
"riscv64"
|
| 511 |
+
],
|
| 512 |
+
"license": "MIT",
|
| 513 |
+
"optional": true,
|
| 514 |
+
"os": [
|
| 515 |
+
"linux"
|
| 516 |
+
],
|
| 517 |
+
"engines": {
|
| 518 |
+
"node": ">=18"
|
| 519 |
+
}
|
| 520 |
+
},
|
| 521 |
+
"node_modules/@esbuild/linux-s390x": {
|
| 522 |
+
"version": "0.27.7",
|
| 523 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
|
| 524 |
+
"integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
|
| 525 |
+
"cpu": [
|
| 526 |
+
"s390x"
|
| 527 |
+
],
|
| 528 |
+
"license": "MIT",
|
| 529 |
+
"optional": true,
|
| 530 |
+
"os": [
|
| 531 |
+
"linux"
|
| 532 |
+
],
|
| 533 |
+
"engines": {
|
| 534 |
+
"node": ">=18"
|
| 535 |
+
}
|
| 536 |
+
},
|
| 537 |
+
"node_modules/@esbuild/linux-x64": {
|
| 538 |
+
"version": "0.27.7",
|
| 539 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
|
| 540 |
+
"integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
|
| 541 |
+
"cpu": [
|
| 542 |
+
"x64"
|
| 543 |
+
],
|
| 544 |
+
"license": "MIT",
|
| 545 |
+
"optional": true,
|
| 546 |
+
"os": [
|
| 547 |
+
"linux"
|
| 548 |
+
],
|
| 549 |
+
"engines": {
|
| 550 |
+
"node": ">=18"
|
| 551 |
+
}
|
| 552 |
+
},
|
| 553 |
+
"node_modules/@esbuild/netbsd-arm64": {
|
| 554 |
+
"version": "0.27.7",
|
| 555 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
|
| 556 |
+
"integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
|
| 557 |
+
"cpu": [
|
| 558 |
+
"arm64"
|
| 559 |
+
],
|
| 560 |
+
"license": "MIT",
|
| 561 |
+
"optional": true,
|
| 562 |
+
"os": [
|
| 563 |
+
"netbsd"
|
| 564 |
+
],
|
| 565 |
+
"engines": {
|
| 566 |
+
"node": ">=18"
|
| 567 |
+
}
|
| 568 |
+
},
|
| 569 |
+
"node_modules/@esbuild/netbsd-x64": {
|
| 570 |
+
"version": "0.27.7",
|
| 571 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
|
| 572 |
+
"integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
|
| 573 |
+
"cpu": [
|
| 574 |
+
"x64"
|
| 575 |
+
],
|
| 576 |
+
"license": "MIT",
|
| 577 |
+
"optional": true,
|
| 578 |
+
"os": [
|
| 579 |
+
"netbsd"
|
| 580 |
+
],
|
| 581 |
+
"engines": {
|
| 582 |
+
"node": ">=18"
|
| 583 |
+
}
|
| 584 |
+
},
|
| 585 |
+
"node_modules/@esbuild/openbsd-arm64": {
|
| 586 |
+
"version": "0.27.7",
|
| 587 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
|
| 588 |
+
"integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
|
| 589 |
+
"cpu": [
|
| 590 |
+
"arm64"
|
| 591 |
+
],
|
| 592 |
+
"license": "MIT",
|
| 593 |
+
"optional": true,
|
| 594 |
+
"os": [
|
| 595 |
+
"openbsd"
|
| 596 |
+
],
|
| 597 |
+
"engines": {
|
| 598 |
+
"node": ">=18"
|
| 599 |
+
}
|
| 600 |
+
},
|
| 601 |
+
"node_modules/@esbuild/openbsd-x64": {
|
| 602 |
+
"version": "0.27.7",
|
| 603 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
|
| 604 |
+
"integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
|
| 605 |
+
"cpu": [
|
| 606 |
+
"x64"
|
| 607 |
+
],
|
| 608 |
+
"license": "MIT",
|
| 609 |
+
"optional": true,
|
| 610 |
+
"os": [
|
| 611 |
+
"openbsd"
|
| 612 |
+
],
|
| 613 |
+
"engines": {
|
| 614 |
+
"node": ">=18"
|
| 615 |
+
}
|
| 616 |
+
},
|
| 617 |
+
"node_modules/@esbuild/openharmony-arm64": {
|
| 618 |
+
"version": "0.27.7",
|
| 619 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
|
| 620 |
+
"integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
|
| 621 |
+
"cpu": [
|
| 622 |
+
"arm64"
|
| 623 |
+
],
|
| 624 |
+
"license": "MIT",
|
| 625 |
+
"optional": true,
|
| 626 |
+
"os": [
|
| 627 |
+
"openharmony"
|
| 628 |
+
],
|
| 629 |
+
"engines": {
|
| 630 |
+
"node": ">=18"
|
| 631 |
+
}
|
| 632 |
+
},
|
| 633 |
+
"node_modules/@esbuild/sunos-x64": {
|
| 634 |
+
"version": "0.27.7",
|
| 635 |
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
|
| 636 |
+
"integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
|
| 637 |
+
"cpu": [
|
| 638 |
+
"x64"
|
| 639 |
+
],
|
| 640 |
+
"license": "MIT",
|
| 641 |
+
"optional": true,
|
| 642 |
+
"os": [
|
| 643 |
+
"sunos"
|
| 644 |
+
],
|
| 645 |
+
"engines": {
|
| 646 |
+
"node": ">=18"
|
| 647 |
+
}
|
| 648 |
+
},
|
| 649 |
+
"node_modules/@esbuild/win32-arm64": {
|
| 650 |
+
"version": "0.27.7",
|
| 651 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
|
| 652 |
+
"integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
|
| 653 |
+
"cpu": [
|
| 654 |
+
"arm64"
|
| 655 |
+
],
|
| 656 |
+
"license": "MIT",
|
| 657 |
+
"optional": true,
|
| 658 |
+
"os": [
|
| 659 |
+
"win32"
|
| 660 |
+
],
|
| 661 |
+
"engines": {
|
| 662 |
+
"node": ">=18"
|
| 663 |
+
}
|
| 664 |
+
},
|
| 665 |
+
"node_modules/@esbuild/win32-ia32": {
|
| 666 |
+
"version": "0.27.7",
|
| 667 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
|
| 668 |
+
"integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
|
| 669 |
+
"cpu": [
|
| 670 |
+
"ia32"
|
| 671 |
+
],
|
| 672 |
+
"license": "MIT",
|
| 673 |
+
"optional": true,
|
| 674 |
+
"os": [
|
| 675 |
+
"win32"
|
| 676 |
+
],
|
| 677 |
+
"engines": {
|
| 678 |
+
"node": ">=18"
|
| 679 |
+
}
|
| 680 |
+
},
|
| 681 |
+
"node_modules/@esbuild/win32-x64": {
|
| 682 |
+
"version": "0.27.7",
|
| 683 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
|
| 684 |
+
"integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
|
| 685 |
+
"cpu": [
|
| 686 |
+
"x64"
|
| 687 |
+
],
|
| 688 |
+
"license": "MIT",
|
| 689 |
+
"optional": true,
|
| 690 |
+
"os": [
|
| 691 |
+
"win32"
|
| 692 |
+
],
|
| 693 |
+
"engines": {
|
| 694 |
+
"node": ">=18"
|
| 695 |
+
}
|
| 696 |
+
},
|
| 697 |
+
"node_modules/@jridgewell/gen-mapping": {
|
| 698 |
+
"version": "0.3.13",
|
| 699 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
| 700 |
+
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
| 701 |
+
"license": "MIT",
|
| 702 |
+
"dependencies": {
|
| 703 |
+
"@jridgewell/sourcemap-codec": "^1.5.0",
|
| 704 |
+
"@jridgewell/trace-mapping": "^0.3.24"
|
| 705 |
+
}
|
| 706 |
+
},
|
| 707 |
+
"node_modules/@jridgewell/remapping": {
|
| 708 |
+
"version": "2.3.5",
|
| 709 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
| 710 |
+
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
| 711 |
+
"license": "MIT",
|
| 712 |
+
"dependencies": {
|
| 713 |
+
"@jridgewell/gen-mapping": "^0.3.5",
|
| 714 |
+
"@jridgewell/trace-mapping": "^0.3.24"
|
| 715 |
+
}
|
| 716 |
+
},
|
| 717 |
+
"node_modules/@jridgewell/resolve-uri": {
|
| 718 |
+
"version": "3.1.2",
|
| 719 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
| 720 |
+
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
| 721 |
+
"license": "MIT",
|
| 722 |
+
"engines": {
|
| 723 |
+
"node": ">=6.0.0"
|
| 724 |
+
}
|
| 725 |
+
},
|
| 726 |
+
"node_modules/@jridgewell/sourcemap-codec": {
|
| 727 |
+
"version": "1.5.5",
|
| 728 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
| 729 |
+
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
| 730 |
+
"license": "MIT"
|
| 731 |
+
},
|
| 732 |
+
"node_modules/@jridgewell/trace-mapping": {
|
| 733 |
+
"version": "0.3.31",
|
| 734 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
| 735 |
+
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
| 736 |
+
"license": "MIT",
|
| 737 |
+
"dependencies": {
|
| 738 |
+
"@jridgewell/resolve-uri": "^3.1.0",
|
| 739 |
+
"@jridgewell/sourcemap-codec": "^1.4.14"
|
| 740 |
+
}
|
| 741 |
+
},
|
| 742 |
+
"node_modules/@rolldown/pluginutils": {
|
| 743 |
+
"version": "1.0.0-rc.3",
|
| 744 |
+
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz",
|
| 745 |
+
"integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==",
|
| 746 |
+
"license": "MIT"
|
| 747 |
+
},
|
| 748 |
+
"node_modules/@rollup/rollup-android-arm-eabi": {
|
| 749 |
+
"version": "4.60.4",
|
| 750 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz",
|
| 751 |
+
"integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==",
|
| 752 |
+
"cpu": [
|
| 753 |
+
"arm"
|
| 754 |
+
],
|
| 755 |
+
"license": "MIT",
|
| 756 |
+
"optional": true,
|
| 757 |
+
"os": [
|
| 758 |
+
"android"
|
| 759 |
+
]
|
| 760 |
+
},
|
| 761 |
+
"node_modules/@rollup/rollup-android-arm64": {
|
| 762 |
+
"version": "4.60.4",
|
| 763 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz",
|
| 764 |
+
"integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==",
|
| 765 |
+
"cpu": [
|
| 766 |
+
"arm64"
|
| 767 |
+
],
|
| 768 |
+
"license": "MIT",
|
| 769 |
+
"optional": true,
|
| 770 |
+
"os": [
|
| 771 |
+
"android"
|
| 772 |
+
]
|
| 773 |
+
},
|
| 774 |
+
"node_modules/@rollup/rollup-darwin-arm64": {
|
| 775 |
+
"version": "4.60.4",
|
| 776 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz",
|
| 777 |
+
"integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==",
|
| 778 |
+
"cpu": [
|
| 779 |
+
"arm64"
|
| 780 |
+
],
|
| 781 |
+
"license": "MIT",
|
| 782 |
+
"optional": true,
|
| 783 |
+
"os": [
|
| 784 |
+
"darwin"
|
| 785 |
+
]
|
| 786 |
+
},
|
| 787 |
+
"node_modules/@rollup/rollup-darwin-x64": {
|
| 788 |
+
"version": "4.60.4",
|
| 789 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz",
|
| 790 |
+
"integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==",
|
| 791 |
+
"cpu": [
|
| 792 |
+
"x64"
|
| 793 |
+
],
|
| 794 |
+
"license": "MIT",
|
| 795 |
+
"optional": true,
|
| 796 |
+
"os": [
|
| 797 |
+
"darwin"
|
| 798 |
+
]
|
| 799 |
+
},
|
| 800 |
+
"node_modules/@rollup/rollup-freebsd-arm64": {
|
| 801 |
+
"version": "4.60.4",
|
| 802 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz",
|
| 803 |
+
"integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==",
|
| 804 |
+
"cpu": [
|
| 805 |
+
"arm64"
|
| 806 |
+
],
|
| 807 |
+
"license": "MIT",
|
| 808 |
+
"optional": true,
|
| 809 |
+
"os": [
|
| 810 |
+
"freebsd"
|
| 811 |
+
]
|
| 812 |
+
},
|
| 813 |
+
"node_modules/@rollup/rollup-freebsd-x64": {
|
| 814 |
+
"version": "4.60.4",
|
| 815 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz",
|
| 816 |
+
"integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==",
|
| 817 |
+
"cpu": [
|
| 818 |
+
"x64"
|
| 819 |
+
],
|
| 820 |
+
"license": "MIT",
|
| 821 |
+
"optional": true,
|
| 822 |
+
"os": [
|
| 823 |
+
"freebsd"
|
| 824 |
+
]
|
| 825 |
+
},
|
| 826 |
+
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
| 827 |
+
"version": "4.60.4",
|
| 828 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz",
|
| 829 |
+
"integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==",
|
| 830 |
+
"cpu": [
|
| 831 |
+
"arm"
|
| 832 |
+
],
|
| 833 |
+
"license": "MIT",
|
| 834 |
+
"optional": true,
|
| 835 |
+
"os": [
|
| 836 |
+
"linux"
|
| 837 |
+
]
|
| 838 |
+
},
|
| 839 |
+
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
| 840 |
+
"version": "4.60.4",
|
| 841 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz",
|
| 842 |
+
"integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==",
|
| 843 |
+
"cpu": [
|
| 844 |
+
"arm"
|
| 845 |
+
],
|
| 846 |
+
"license": "MIT",
|
| 847 |
+
"optional": true,
|
| 848 |
+
"os": [
|
| 849 |
+
"linux"
|
| 850 |
+
]
|
| 851 |
+
},
|
| 852 |
+
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
| 853 |
+
"version": "4.60.4",
|
| 854 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz",
|
| 855 |
+
"integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==",
|
| 856 |
+
"cpu": [
|
| 857 |
+
"arm64"
|
| 858 |
+
],
|
| 859 |
+
"license": "MIT",
|
| 860 |
+
"optional": true,
|
| 861 |
+
"os": [
|
| 862 |
+
"linux"
|
| 863 |
+
]
|
| 864 |
+
},
|
| 865 |
+
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
| 866 |
+
"version": "4.60.4",
|
| 867 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz",
|
| 868 |
+
"integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==",
|
| 869 |
+
"cpu": [
|
| 870 |
+
"arm64"
|
| 871 |
+
],
|
| 872 |
+
"license": "MIT",
|
| 873 |
+
"optional": true,
|
| 874 |
+
"os": [
|
| 875 |
+
"linux"
|
| 876 |
+
]
|
| 877 |
+
},
|
| 878 |
+
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
| 879 |
+
"version": "4.60.4",
|
| 880 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz",
|
| 881 |
+
"integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==",
|
| 882 |
+
"cpu": [
|
| 883 |
+
"loong64"
|
| 884 |
+
],
|
| 885 |
+
"license": "MIT",
|
| 886 |
+
"optional": true,
|
| 887 |
+
"os": [
|
| 888 |
+
"linux"
|
| 889 |
+
]
|
| 890 |
+
},
|
| 891 |
+
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
| 892 |
+
"version": "4.60.4",
|
| 893 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz",
|
| 894 |
+
"integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==",
|
| 895 |
+
"cpu": [
|
| 896 |
+
"loong64"
|
| 897 |
+
],
|
| 898 |
+
"license": "MIT",
|
| 899 |
+
"optional": true,
|
| 900 |
+
"os": [
|
| 901 |
+
"linux"
|
| 902 |
+
]
|
| 903 |
+
},
|
| 904 |
+
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
| 905 |
+
"version": "4.60.4",
|
| 906 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz",
|
| 907 |
+
"integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==",
|
| 908 |
+
"cpu": [
|
| 909 |
+
"ppc64"
|
| 910 |
+
],
|
| 911 |
+
"license": "MIT",
|
| 912 |
+
"optional": true,
|
| 913 |
+
"os": [
|
| 914 |
+
"linux"
|
| 915 |
+
]
|
| 916 |
+
},
|
| 917 |
+
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
| 918 |
+
"version": "4.60.4",
|
| 919 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz",
|
| 920 |
+
"integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==",
|
| 921 |
+
"cpu": [
|
| 922 |
+
"ppc64"
|
| 923 |
+
],
|
| 924 |
+
"license": "MIT",
|
| 925 |
+
"optional": true,
|
| 926 |
+
"os": [
|
| 927 |
+
"linux"
|
| 928 |
+
]
|
| 929 |
+
},
|
| 930 |
+
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
| 931 |
+
"version": "4.60.4",
|
| 932 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz",
|
| 933 |
+
"integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==",
|
| 934 |
+
"cpu": [
|
| 935 |
+
"riscv64"
|
| 936 |
+
],
|
| 937 |
+
"license": "MIT",
|
| 938 |
+
"optional": true,
|
| 939 |
+
"os": [
|
| 940 |
+
"linux"
|
| 941 |
+
]
|
| 942 |
+
},
|
| 943 |
+
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
| 944 |
+
"version": "4.60.4",
|
| 945 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz",
|
| 946 |
+
"integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==",
|
| 947 |
+
"cpu": [
|
| 948 |
+
"riscv64"
|
| 949 |
+
],
|
| 950 |
+
"license": "MIT",
|
| 951 |
+
"optional": true,
|
| 952 |
+
"os": [
|
| 953 |
+
"linux"
|
| 954 |
+
]
|
| 955 |
+
},
|
| 956 |
+
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
| 957 |
+
"version": "4.60.4",
|
| 958 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz",
|
| 959 |
+
"integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==",
|
| 960 |
+
"cpu": [
|
| 961 |
+
"s390x"
|
| 962 |
+
],
|
| 963 |
+
"license": "MIT",
|
| 964 |
+
"optional": true,
|
| 965 |
+
"os": [
|
| 966 |
+
"linux"
|
| 967 |
+
]
|
| 968 |
+
},
|
| 969 |
+
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
| 970 |
+
"version": "4.60.4",
|
| 971 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz",
|
| 972 |
+
"integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==",
|
| 973 |
+
"cpu": [
|
| 974 |
+
"x64"
|
| 975 |
+
],
|
| 976 |
+
"license": "MIT",
|
| 977 |
+
"optional": true,
|
| 978 |
+
"os": [
|
| 979 |
+
"linux"
|
| 980 |
+
]
|
| 981 |
+
},
|
| 982 |
+
"node_modules/@rollup/rollup-linux-x64-musl": {
|
| 983 |
+
"version": "4.60.4",
|
| 984 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz",
|
| 985 |
+
"integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==",
|
| 986 |
+
"cpu": [
|
| 987 |
+
"x64"
|
| 988 |
+
],
|
| 989 |
+
"license": "MIT",
|
| 990 |
+
"optional": true,
|
| 991 |
+
"os": [
|
| 992 |
+
"linux"
|
| 993 |
+
]
|
| 994 |
+
},
|
| 995 |
+
"node_modules/@rollup/rollup-openbsd-x64": {
|
| 996 |
+
"version": "4.60.4",
|
| 997 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz",
|
| 998 |
+
"integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==",
|
| 999 |
+
"cpu": [
|
| 1000 |
+
"x64"
|
| 1001 |
+
],
|
| 1002 |
+
"license": "MIT",
|
| 1003 |
+
"optional": true,
|
| 1004 |
+
"os": [
|
| 1005 |
+
"openbsd"
|
| 1006 |
+
]
|
| 1007 |
+
},
|
| 1008 |
+
"node_modules/@rollup/rollup-openharmony-arm64": {
|
| 1009 |
+
"version": "4.60.4",
|
| 1010 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz",
|
| 1011 |
+
"integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==",
|
| 1012 |
+
"cpu": [
|
| 1013 |
+
"arm64"
|
| 1014 |
+
],
|
| 1015 |
+
"license": "MIT",
|
| 1016 |
+
"optional": true,
|
| 1017 |
+
"os": [
|
| 1018 |
+
"openharmony"
|
| 1019 |
+
]
|
| 1020 |
+
},
|
| 1021 |
+
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
| 1022 |
+
"version": "4.60.4",
|
| 1023 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz",
|
| 1024 |
+
"integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==",
|
| 1025 |
+
"cpu": [
|
| 1026 |
+
"arm64"
|
| 1027 |
+
],
|
| 1028 |
+
"license": "MIT",
|
| 1029 |
+
"optional": true,
|
| 1030 |
+
"os": [
|
| 1031 |
+
"win32"
|
| 1032 |
+
]
|
| 1033 |
+
},
|
| 1034 |
+
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
| 1035 |
+
"version": "4.60.4",
|
| 1036 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz",
|
| 1037 |
+
"integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==",
|
| 1038 |
+
"cpu": [
|
| 1039 |
+
"ia32"
|
| 1040 |
+
],
|
| 1041 |
+
"license": "MIT",
|
| 1042 |
+
"optional": true,
|
| 1043 |
+
"os": [
|
| 1044 |
+
"win32"
|
| 1045 |
+
]
|
| 1046 |
+
},
|
| 1047 |
+
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
| 1048 |
+
"version": "4.60.4",
|
| 1049 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz",
|
| 1050 |
+
"integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==",
|
| 1051 |
+
"cpu": [
|
| 1052 |
+
"x64"
|
| 1053 |
+
],
|
| 1054 |
+
"license": "MIT",
|
| 1055 |
+
"optional": true,
|
| 1056 |
+
"os": [
|
| 1057 |
+
"win32"
|
| 1058 |
+
]
|
| 1059 |
+
},
|
| 1060 |
+
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
| 1061 |
+
"version": "4.60.4",
|
| 1062 |
+
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz",
|
| 1063 |
+
"integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==",
|
| 1064 |
+
"cpu": [
|
| 1065 |
+
"x64"
|
| 1066 |
+
],
|
| 1067 |
+
"license": "MIT",
|
| 1068 |
+
"optional": true,
|
| 1069 |
+
"os": [
|
| 1070 |
+
"win32"
|
| 1071 |
+
]
|
| 1072 |
+
},
|
| 1073 |
+
"node_modules/@types/babel__core": {
|
| 1074 |
+
"version": "7.20.5",
|
| 1075 |
+
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
| 1076 |
+
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
|
| 1077 |
+
"license": "MIT",
|
| 1078 |
+
"dependencies": {
|
| 1079 |
+
"@babel/parser": "^7.20.7",
|
| 1080 |
+
"@babel/types": "^7.20.7",
|
| 1081 |
+
"@types/babel__generator": "*",
|
| 1082 |
+
"@types/babel__template": "*",
|
| 1083 |
+
"@types/babel__traverse": "*"
|
| 1084 |
+
}
|
| 1085 |
+
},
|
| 1086 |
+
"node_modules/@types/babel__generator": {
|
| 1087 |
+
"version": "7.27.0",
|
| 1088 |
+
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
|
| 1089 |
+
"integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
|
| 1090 |
+
"license": "MIT",
|
| 1091 |
+
"dependencies": {
|
| 1092 |
+
"@babel/types": "^7.0.0"
|
| 1093 |
+
}
|
| 1094 |
+
},
|
| 1095 |
+
"node_modules/@types/babel__template": {
|
| 1096 |
+
"version": "7.4.4",
|
| 1097 |
+
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
|
| 1098 |
+
"integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
|
| 1099 |
+
"license": "MIT",
|
| 1100 |
+
"dependencies": {
|
| 1101 |
+
"@babel/parser": "^7.1.0",
|
| 1102 |
+
"@babel/types": "^7.0.0"
|
| 1103 |
+
}
|
| 1104 |
+
},
|
| 1105 |
+
"node_modules/@types/babel__traverse": {
|
| 1106 |
+
"version": "7.28.0",
|
| 1107 |
+
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
|
| 1108 |
+
"integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
|
| 1109 |
+
"license": "MIT",
|
| 1110 |
+
"dependencies": {
|
| 1111 |
+
"@babel/types": "^7.28.2"
|
| 1112 |
+
}
|
| 1113 |
+
},
|
| 1114 |
+
"node_modules/@types/estree": {
|
| 1115 |
+
"version": "1.0.8",
|
| 1116 |
+
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
| 1117 |
+
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
| 1118 |
+
"license": "MIT"
|
| 1119 |
+
},
|
| 1120 |
+
"node_modules/@vitejs/plugin-react": {
|
| 1121 |
+
"version": "5.2.0",
|
| 1122 |
+
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz",
|
| 1123 |
+
"integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==",
|
| 1124 |
+
"license": "MIT",
|
| 1125 |
+
"dependencies": {
|
| 1126 |
+
"@babel/core": "^7.29.0",
|
| 1127 |
+
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
| 1128 |
+
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
|
| 1129 |
+
"@rolldown/pluginutils": "1.0.0-rc.3",
|
| 1130 |
+
"@types/babel__core": "^7.20.5",
|
| 1131 |
+
"react-refresh": "^0.18.0"
|
| 1132 |
+
},
|
| 1133 |
+
"engines": {
|
| 1134 |
+
"node": "^20.19.0 || >=22.12.0"
|
| 1135 |
+
},
|
| 1136 |
+
"peerDependencies": {
|
| 1137 |
+
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
| 1138 |
+
}
|
| 1139 |
+
},
|
| 1140 |
+
"node_modules/baseline-browser-mapping": {
|
| 1141 |
+
"version": "2.10.31",
|
| 1142 |
+
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.31.tgz",
|
| 1143 |
+
"integrity": "sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==",
|
| 1144 |
+
"license": "Apache-2.0",
|
| 1145 |
+
"bin": {
|
| 1146 |
+
"baseline-browser-mapping": "dist/cli.cjs"
|
| 1147 |
+
},
|
| 1148 |
+
"engines": {
|
| 1149 |
+
"node": ">=6.0.0"
|
| 1150 |
+
}
|
| 1151 |
+
},
|
| 1152 |
+
"node_modules/browserslist": {
|
| 1153 |
+
"version": "4.28.2",
|
| 1154 |
+
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz",
|
| 1155 |
+
"integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
|
| 1156 |
+
"funding": [
|
| 1157 |
+
{
|
| 1158 |
+
"type": "opencollective",
|
| 1159 |
+
"url": "https://opencollective.com/browserslist"
|
| 1160 |
+
},
|
| 1161 |
+
{
|
| 1162 |
+
"type": "tidelift",
|
| 1163 |
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
| 1164 |
+
},
|
| 1165 |
+
{
|
| 1166 |
+
"type": "github",
|
| 1167 |
+
"url": "https://github.com/sponsors/ai"
|
| 1168 |
+
}
|
| 1169 |
+
],
|
| 1170 |
+
"license": "MIT",
|
| 1171 |
+
"dependencies": {
|
| 1172 |
+
"baseline-browser-mapping": "^2.10.12",
|
| 1173 |
+
"caniuse-lite": "^1.0.30001782",
|
| 1174 |
+
"electron-to-chromium": "^1.5.328",
|
| 1175 |
+
"node-releases": "^2.0.36",
|
| 1176 |
+
"update-browserslist-db": "^1.2.3"
|
| 1177 |
+
},
|
| 1178 |
+
"bin": {
|
| 1179 |
+
"browserslist": "cli.js"
|
| 1180 |
+
},
|
| 1181 |
+
"engines": {
|
| 1182 |
+
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
| 1183 |
+
}
|
| 1184 |
+
},
|
| 1185 |
+
"node_modules/caniuse-lite": {
|
| 1186 |
+
"version": "1.0.30001793",
|
| 1187 |
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz",
|
| 1188 |
+
"integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==",
|
| 1189 |
+
"funding": [
|
| 1190 |
+
{
|
| 1191 |
+
"type": "opencollective",
|
| 1192 |
+
"url": "https://opencollective.com/browserslist"
|
| 1193 |
+
},
|
| 1194 |
+
{
|
| 1195 |
+
"type": "tidelift",
|
| 1196 |
+
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
| 1197 |
+
},
|
| 1198 |
+
{
|
| 1199 |
+
"type": "github",
|
| 1200 |
+
"url": "https://github.com/sponsors/ai"
|
| 1201 |
+
}
|
| 1202 |
+
],
|
| 1203 |
+
"license": "CC-BY-4.0"
|
| 1204 |
+
},
|
| 1205 |
+
"node_modules/convert-source-map": {
|
| 1206 |
+
"version": "2.0.0",
|
| 1207 |
+
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
| 1208 |
+
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
| 1209 |
+
"license": "MIT"
|
| 1210 |
+
},
|
| 1211 |
+
"node_modules/debug": {
|
| 1212 |
+
"version": "4.4.3",
|
| 1213 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
| 1214 |
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
| 1215 |
+
"license": "MIT",
|
| 1216 |
+
"dependencies": {
|
| 1217 |
+
"ms": "^2.1.3"
|
| 1218 |
+
},
|
| 1219 |
+
"engines": {
|
| 1220 |
+
"node": ">=6.0"
|
| 1221 |
+
},
|
| 1222 |
+
"peerDependenciesMeta": {
|
| 1223 |
+
"supports-color": {
|
| 1224 |
+
"optional": true
|
| 1225 |
+
}
|
| 1226 |
+
}
|
| 1227 |
+
},
|
| 1228 |
+
"node_modules/electron-to-chromium": {
|
| 1229 |
+
"version": "1.5.359",
|
| 1230 |
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.359.tgz",
|
| 1231 |
+
"integrity": "sha512-8lPELWuYZIWk7NDvCNthtmMw/7Q5Wu25NpM4djFMHBmk8DubPAtL4YTOp7ou0e7HyJtwkVlWv8XMLURnrtgJQw==",
|
| 1232 |
+
"license": "ISC"
|
| 1233 |
+
},
|
| 1234 |
+
"node_modules/esbuild": {
|
| 1235 |
+
"version": "0.27.7",
|
| 1236 |
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
|
| 1237 |
+
"integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
|
| 1238 |
+
"hasInstallScript": true,
|
| 1239 |
+
"license": "MIT",
|
| 1240 |
+
"bin": {
|
| 1241 |
+
"esbuild": "bin/esbuild"
|
| 1242 |
+
},
|
| 1243 |
+
"engines": {
|
| 1244 |
+
"node": ">=18"
|
| 1245 |
+
},
|
| 1246 |
+
"optionalDependencies": {
|
| 1247 |
+
"@esbuild/aix-ppc64": "0.27.7",
|
| 1248 |
+
"@esbuild/android-arm": "0.27.7",
|
| 1249 |
+
"@esbuild/android-arm64": "0.27.7",
|
| 1250 |
+
"@esbuild/android-x64": "0.27.7",
|
| 1251 |
+
"@esbuild/darwin-arm64": "0.27.7",
|
| 1252 |
+
"@esbuild/darwin-x64": "0.27.7",
|
| 1253 |
+
"@esbuild/freebsd-arm64": "0.27.7",
|
| 1254 |
+
"@esbuild/freebsd-x64": "0.27.7",
|
| 1255 |
+
"@esbuild/linux-arm": "0.27.7",
|
| 1256 |
+
"@esbuild/linux-arm64": "0.27.7",
|
| 1257 |
+
"@esbuild/linux-ia32": "0.27.7",
|
| 1258 |
+
"@esbuild/linux-loong64": "0.27.7",
|
| 1259 |
+
"@esbuild/linux-mips64el": "0.27.7",
|
| 1260 |
+
"@esbuild/linux-ppc64": "0.27.7",
|
| 1261 |
+
"@esbuild/linux-riscv64": "0.27.7",
|
| 1262 |
+
"@esbuild/linux-s390x": "0.27.7",
|
| 1263 |
+
"@esbuild/linux-x64": "0.27.7",
|
| 1264 |
+
"@esbuild/netbsd-arm64": "0.27.7",
|
| 1265 |
+
"@esbuild/netbsd-x64": "0.27.7",
|
| 1266 |
+
"@esbuild/openbsd-arm64": "0.27.7",
|
| 1267 |
+
"@esbuild/openbsd-x64": "0.27.7",
|
| 1268 |
+
"@esbuild/openharmony-arm64": "0.27.7",
|
| 1269 |
+
"@esbuild/sunos-x64": "0.27.7",
|
| 1270 |
+
"@esbuild/win32-arm64": "0.27.7",
|
| 1271 |
+
"@esbuild/win32-ia32": "0.27.7",
|
| 1272 |
+
"@esbuild/win32-x64": "0.27.7"
|
| 1273 |
+
}
|
| 1274 |
+
},
|
| 1275 |
+
"node_modules/escalade": {
|
| 1276 |
+
"version": "3.2.0",
|
| 1277 |
+
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
| 1278 |
+
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
| 1279 |
+
"license": "MIT",
|
| 1280 |
+
"engines": {
|
| 1281 |
+
"node": ">=6"
|
| 1282 |
+
}
|
| 1283 |
+
},
|
| 1284 |
+
"node_modules/fdir": {
|
| 1285 |
+
"version": "6.5.0",
|
| 1286 |
+
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
| 1287 |
+
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
| 1288 |
+
"license": "MIT",
|
| 1289 |
+
"engines": {
|
| 1290 |
+
"node": ">=12.0.0"
|
| 1291 |
+
},
|
| 1292 |
+
"peerDependencies": {
|
| 1293 |
+
"picomatch": "^3 || ^4"
|
| 1294 |
+
},
|
| 1295 |
+
"peerDependenciesMeta": {
|
| 1296 |
+
"picomatch": {
|
| 1297 |
+
"optional": true
|
| 1298 |
+
}
|
| 1299 |
+
}
|
| 1300 |
+
},
|
| 1301 |
+
"node_modules/fsevents": {
|
| 1302 |
+
"version": "2.3.3",
|
| 1303 |
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
| 1304 |
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
| 1305 |
+
"hasInstallScript": true,
|
| 1306 |
+
"license": "MIT",
|
| 1307 |
+
"optional": true,
|
| 1308 |
+
"os": [
|
| 1309 |
+
"darwin"
|
| 1310 |
+
],
|
| 1311 |
+
"engines": {
|
| 1312 |
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 1313 |
+
}
|
| 1314 |
+
},
|
| 1315 |
+
"node_modules/gensync": {
|
| 1316 |
+
"version": "1.0.0-beta.2",
|
| 1317 |
+
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
| 1318 |
+
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
|
| 1319 |
+
"license": "MIT",
|
| 1320 |
+
"engines": {
|
| 1321 |
+
"node": ">=6.9.0"
|
| 1322 |
+
}
|
| 1323 |
+
},
|
| 1324 |
+
"node_modules/js-tokens": {
|
| 1325 |
+
"version": "4.0.0",
|
| 1326 |
+
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
| 1327 |
+
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
| 1328 |
+
"license": "MIT"
|
| 1329 |
+
},
|
| 1330 |
+
"node_modules/jsesc": {
|
| 1331 |
+
"version": "3.1.0",
|
| 1332 |
+
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
| 1333 |
+
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
| 1334 |
+
"license": "MIT",
|
| 1335 |
+
"bin": {
|
| 1336 |
+
"jsesc": "bin/jsesc"
|
| 1337 |
+
},
|
| 1338 |
+
"engines": {
|
| 1339 |
+
"node": ">=6"
|
| 1340 |
+
}
|
| 1341 |
+
},
|
| 1342 |
+
"node_modules/json5": {
|
| 1343 |
+
"version": "2.2.3",
|
| 1344 |
+
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
| 1345 |
+
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
| 1346 |
+
"license": "MIT",
|
| 1347 |
+
"bin": {
|
| 1348 |
+
"json5": "lib/cli.js"
|
| 1349 |
+
},
|
| 1350 |
+
"engines": {
|
| 1351 |
+
"node": ">=6"
|
| 1352 |
+
}
|
| 1353 |
+
},
|
| 1354 |
+
"node_modules/lru-cache": {
|
| 1355 |
+
"version": "5.1.1",
|
| 1356 |
+
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
| 1357 |
+
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
|
| 1358 |
+
"license": "ISC",
|
| 1359 |
+
"dependencies": {
|
| 1360 |
+
"yallist": "^3.0.2"
|
| 1361 |
+
}
|
| 1362 |
+
},
|
| 1363 |
+
"node_modules/lucide-react": {
|
| 1364 |
+
"version": "0.468.0",
|
| 1365 |
+
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz",
|
| 1366 |
+
"integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==",
|
| 1367 |
+
"license": "ISC",
|
| 1368 |
+
"peerDependencies": {
|
| 1369 |
+
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
|
| 1370 |
+
}
|
| 1371 |
+
},
|
| 1372 |
+
"node_modules/ms": {
|
| 1373 |
+
"version": "2.1.3",
|
| 1374 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 1375 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 1376 |
+
"license": "MIT"
|
| 1377 |
+
},
|
| 1378 |
+
"node_modules/nanoid": {
|
| 1379 |
+
"version": "3.3.12",
|
| 1380 |
+
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
| 1381 |
+
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
| 1382 |
+
"funding": [
|
| 1383 |
+
{
|
| 1384 |
+
"type": "github",
|
| 1385 |
+
"url": "https://github.com/sponsors/ai"
|
| 1386 |
+
}
|
| 1387 |
+
],
|
| 1388 |
+
"license": "MIT",
|
| 1389 |
+
"bin": {
|
| 1390 |
+
"nanoid": "bin/nanoid.cjs"
|
| 1391 |
+
},
|
| 1392 |
+
"engines": {
|
| 1393 |
+
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
| 1394 |
+
}
|
| 1395 |
+
},
|
| 1396 |
+
"node_modules/node-releases": {
|
| 1397 |
+
"version": "2.0.44",
|
| 1398 |
+
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz",
|
| 1399 |
+
"integrity": "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==",
|
| 1400 |
+
"license": "MIT"
|
| 1401 |
+
},
|
| 1402 |
+
"node_modules/picocolors": {
|
| 1403 |
+
"version": "1.1.1",
|
| 1404 |
+
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
| 1405 |
+
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
| 1406 |
+
"license": "ISC"
|
| 1407 |
+
},
|
| 1408 |
+
"node_modules/picomatch": {
|
| 1409 |
+
"version": "4.0.4",
|
| 1410 |
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
| 1411 |
+
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
| 1412 |
+
"license": "MIT",
|
| 1413 |
+
"engines": {
|
| 1414 |
+
"node": ">=12"
|
| 1415 |
+
},
|
| 1416 |
+
"funding": {
|
| 1417 |
+
"url": "https://github.com/sponsors/jonschlinkert"
|
| 1418 |
+
}
|
| 1419 |
+
},
|
| 1420 |
+
"node_modules/postcss": {
|
| 1421 |
+
"version": "8.5.15",
|
| 1422 |
+
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
| 1423 |
+
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
| 1424 |
+
"funding": [
|
| 1425 |
+
{
|
| 1426 |
+
"type": "opencollective",
|
| 1427 |
+
"url": "https://opencollective.com/postcss/"
|
| 1428 |
+
},
|
| 1429 |
+
{
|
| 1430 |
+
"type": "tidelift",
|
| 1431 |
+
"url": "https://tidelift.com/funding/github/npm/postcss"
|
| 1432 |
+
},
|
| 1433 |
+
{
|
| 1434 |
+
"type": "github",
|
| 1435 |
+
"url": "https://github.com/sponsors/ai"
|
| 1436 |
+
}
|
| 1437 |
+
],
|
| 1438 |
+
"license": "MIT",
|
| 1439 |
+
"dependencies": {
|
| 1440 |
+
"nanoid": "^3.3.12",
|
| 1441 |
+
"picocolors": "^1.1.1",
|
| 1442 |
+
"source-map-js": "^1.2.1"
|
| 1443 |
+
},
|
| 1444 |
+
"engines": {
|
| 1445 |
+
"node": "^10 || ^12 || >=14"
|
| 1446 |
+
}
|
| 1447 |
+
},
|
| 1448 |
+
"node_modules/react": {
|
| 1449 |
+
"version": "19.2.6",
|
| 1450 |
+
"resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz",
|
| 1451 |
+
"integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==",
|
| 1452 |
+
"license": "MIT",
|
| 1453 |
+
"engines": {
|
| 1454 |
+
"node": ">=0.10.0"
|
| 1455 |
+
}
|
| 1456 |
+
},
|
| 1457 |
+
"node_modules/react-dom": {
|
| 1458 |
+
"version": "19.2.6",
|
| 1459 |
+
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz",
|
| 1460 |
+
"integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==",
|
| 1461 |
+
"license": "MIT",
|
| 1462 |
+
"dependencies": {
|
| 1463 |
+
"scheduler": "^0.27.0"
|
| 1464 |
+
},
|
| 1465 |
+
"peerDependencies": {
|
| 1466 |
+
"react": "^19.2.6"
|
| 1467 |
+
}
|
| 1468 |
+
},
|
| 1469 |
+
"node_modules/react-refresh": {
|
| 1470 |
+
"version": "0.18.0",
|
| 1471 |
+
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
|
| 1472 |
+
"integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
|
| 1473 |
+
"license": "MIT",
|
| 1474 |
+
"engines": {
|
| 1475 |
+
"node": ">=0.10.0"
|
| 1476 |
+
}
|
| 1477 |
+
},
|
| 1478 |
+
"node_modules/rollup": {
|
| 1479 |
+
"version": "4.60.4",
|
| 1480 |
+
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz",
|
| 1481 |
+
"integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==",
|
| 1482 |
+
"license": "MIT",
|
| 1483 |
+
"dependencies": {
|
| 1484 |
+
"@types/estree": "1.0.8"
|
| 1485 |
+
},
|
| 1486 |
+
"bin": {
|
| 1487 |
+
"rollup": "dist/bin/rollup"
|
| 1488 |
+
},
|
| 1489 |
+
"engines": {
|
| 1490 |
+
"node": ">=18.0.0",
|
| 1491 |
+
"npm": ">=8.0.0"
|
| 1492 |
+
},
|
| 1493 |
+
"optionalDependencies": {
|
| 1494 |
+
"@rollup/rollup-android-arm-eabi": "4.60.4",
|
| 1495 |
+
"@rollup/rollup-android-arm64": "4.60.4",
|
| 1496 |
+
"@rollup/rollup-darwin-arm64": "4.60.4",
|
| 1497 |
+
"@rollup/rollup-darwin-x64": "4.60.4",
|
| 1498 |
+
"@rollup/rollup-freebsd-arm64": "4.60.4",
|
| 1499 |
+
"@rollup/rollup-freebsd-x64": "4.60.4",
|
| 1500 |
+
"@rollup/rollup-linux-arm-gnueabihf": "4.60.4",
|
| 1501 |
+
"@rollup/rollup-linux-arm-musleabihf": "4.60.4",
|
| 1502 |
+
"@rollup/rollup-linux-arm64-gnu": "4.60.4",
|
| 1503 |
+
"@rollup/rollup-linux-arm64-musl": "4.60.4",
|
| 1504 |
+
"@rollup/rollup-linux-loong64-gnu": "4.60.4",
|
| 1505 |
+
"@rollup/rollup-linux-loong64-musl": "4.60.4",
|
| 1506 |
+
"@rollup/rollup-linux-ppc64-gnu": "4.60.4",
|
| 1507 |
+
"@rollup/rollup-linux-ppc64-musl": "4.60.4",
|
| 1508 |
+
"@rollup/rollup-linux-riscv64-gnu": "4.60.4",
|
| 1509 |
+
"@rollup/rollup-linux-riscv64-musl": "4.60.4",
|
| 1510 |
+
"@rollup/rollup-linux-s390x-gnu": "4.60.4",
|
| 1511 |
+
"@rollup/rollup-linux-x64-gnu": "4.60.4",
|
| 1512 |
+
"@rollup/rollup-linux-x64-musl": "4.60.4",
|
| 1513 |
+
"@rollup/rollup-openbsd-x64": "4.60.4",
|
| 1514 |
+
"@rollup/rollup-openharmony-arm64": "4.60.4",
|
| 1515 |
+
"@rollup/rollup-win32-arm64-msvc": "4.60.4",
|
| 1516 |
+
"@rollup/rollup-win32-ia32-msvc": "4.60.4",
|
| 1517 |
+
"@rollup/rollup-win32-x64-gnu": "4.60.4",
|
| 1518 |
+
"@rollup/rollup-win32-x64-msvc": "4.60.4",
|
| 1519 |
+
"fsevents": "~2.3.2"
|
| 1520 |
+
}
|
| 1521 |
+
},
|
| 1522 |
+
"node_modules/scheduler": {
|
| 1523 |
+
"version": "0.27.0",
|
| 1524 |
+
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
| 1525 |
+
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
| 1526 |
+
"license": "MIT"
|
| 1527 |
+
},
|
| 1528 |
+
"node_modules/semver": {
|
| 1529 |
+
"version": "6.3.1",
|
| 1530 |
+
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
| 1531 |
+
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
| 1532 |
+
"license": "ISC",
|
| 1533 |
+
"bin": {
|
| 1534 |
+
"semver": "bin/semver.js"
|
| 1535 |
+
}
|
| 1536 |
+
},
|
| 1537 |
+
"node_modules/source-map-js": {
|
| 1538 |
+
"version": "1.2.1",
|
| 1539 |
+
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
| 1540 |
+
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
| 1541 |
+
"license": "BSD-3-Clause",
|
| 1542 |
+
"engines": {
|
| 1543 |
+
"node": ">=0.10.0"
|
| 1544 |
+
}
|
| 1545 |
+
},
|
| 1546 |
+
"node_modules/tinyglobby": {
|
| 1547 |
+
"version": "0.2.16",
|
| 1548 |
+
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
| 1549 |
+
"integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
|
| 1550 |
+
"license": "MIT",
|
| 1551 |
+
"dependencies": {
|
| 1552 |
+
"fdir": "^6.5.0",
|
| 1553 |
+
"picomatch": "^4.0.4"
|
| 1554 |
+
},
|
| 1555 |
+
"engines": {
|
| 1556 |
+
"node": ">=12.0.0"
|
| 1557 |
+
},
|
| 1558 |
+
"funding": {
|
| 1559 |
+
"url": "https://github.com/sponsors/SuperchupuDev"
|
| 1560 |
+
}
|
| 1561 |
+
},
|
| 1562 |
+
"node_modules/update-browserslist-db": {
|
| 1563 |
+
"version": "1.2.3",
|
| 1564 |
+
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
| 1565 |
+
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
|
| 1566 |
+
"funding": [
|
| 1567 |
+
{
|
| 1568 |
+
"type": "opencollective",
|
| 1569 |
+
"url": "https://opencollective.com/browserslist"
|
| 1570 |
+
},
|
| 1571 |
+
{
|
| 1572 |
+
"type": "tidelift",
|
| 1573 |
+
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
| 1574 |
+
},
|
| 1575 |
+
{
|
| 1576 |
+
"type": "github",
|
| 1577 |
+
"url": "https://github.com/sponsors/ai"
|
| 1578 |
+
}
|
| 1579 |
+
],
|
| 1580 |
+
"license": "MIT",
|
| 1581 |
+
"dependencies": {
|
| 1582 |
+
"escalade": "^3.2.0",
|
| 1583 |
+
"picocolors": "^1.1.1"
|
| 1584 |
+
},
|
| 1585 |
+
"bin": {
|
| 1586 |
+
"update-browserslist-db": "cli.js"
|
| 1587 |
+
},
|
| 1588 |
+
"peerDependencies": {
|
| 1589 |
+
"browserslist": ">= 4.21.0"
|
| 1590 |
+
}
|
| 1591 |
+
},
|
| 1592 |
+
"node_modules/vite": {
|
| 1593 |
+
"version": "7.3.3",
|
| 1594 |
+
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz",
|
| 1595 |
+
"integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==",
|
| 1596 |
+
"license": "MIT",
|
| 1597 |
+
"dependencies": {
|
| 1598 |
+
"esbuild": "^0.27.0",
|
| 1599 |
+
"fdir": "^6.5.0",
|
| 1600 |
+
"picomatch": "^4.0.3",
|
| 1601 |
+
"postcss": "^8.5.6",
|
| 1602 |
+
"rollup": "^4.43.0",
|
| 1603 |
+
"tinyglobby": "^0.2.15"
|
| 1604 |
+
},
|
| 1605 |
+
"bin": {
|
| 1606 |
+
"vite": "bin/vite.js"
|
| 1607 |
+
},
|
| 1608 |
+
"engines": {
|
| 1609 |
+
"node": "^20.19.0 || >=22.12.0"
|
| 1610 |
+
},
|
| 1611 |
+
"funding": {
|
| 1612 |
+
"url": "https://github.com/vitejs/vite?sponsor=1"
|
| 1613 |
+
},
|
| 1614 |
+
"optionalDependencies": {
|
| 1615 |
+
"fsevents": "~2.3.3"
|
| 1616 |
+
},
|
| 1617 |
+
"peerDependencies": {
|
| 1618 |
+
"@types/node": "^20.19.0 || >=22.12.0",
|
| 1619 |
+
"jiti": ">=1.21.0",
|
| 1620 |
+
"less": "^4.0.0",
|
| 1621 |
+
"lightningcss": "^1.21.0",
|
| 1622 |
+
"sass": "^1.70.0",
|
| 1623 |
+
"sass-embedded": "^1.70.0",
|
| 1624 |
+
"stylus": ">=0.54.8",
|
| 1625 |
+
"sugarss": "^5.0.0",
|
| 1626 |
+
"terser": "^5.16.0",
|
| 1627 |
+
"tsx": "^4.8.1",
|
| 1628 |
+
"yaml": "^2.4.2"
|
| 1629 |
+
},
|
| 1630 |
+
"peerDependenciesMeta": {
|
| 1631 |
+
"@types/node": {
|
| 1632 |
+
"optional": true
|
| 1633 |
+
},
|
| 1634 |
+
"jiti": {
|
| 1635 |
+
"optional": true
|
| 1636 |
+
},
|
| 1637 |
+
"less": {
|
| 1638 |
+
"optional": true
|
| 1639 |
+
},
|
| 1640 |
+
"lightningcss": {
|
| 1641 |
+
"optional": true
|
| 1642 |
+
},
|
| 1643 |
+
"sass": {
|
| 1644 |
+
"optional": true
|
| 1645 |
+
},
|
| 1646 |
+
"sass-embedded": {
|
| 1647 |
+
"optional": true
|
| 1648 |
+
},
|
| 1649 |
+
"stylus": {
|
| 1650 |
+
"optional": true
|
| 1651 |
+
},
|
| 1652 |
+
"sugarss": {
|
| 1653 |
+
"optional": true
|
| 1654 |
+
},
|
| 1655 |
+
"terser": {
|
| 1656 |
+
"optional": true
|
| 1657 |
+
},
|
| 1658 |
+
"tsx": {
|
| 1659 |
+
"optional": true
|
| 1660 |
+
},
|
| 1661 |
+
"yaml": {
|
| 1662 |
+
"optional": true
|
| 1663 |
+
}
|
| 1664 |
+
}
|
| 1665 |
+
},
|
| 1666 |
+
"node_modules/yallist": {
|
| 1667 |
+
"version": "3.1.1",
|
| 1668 |
+
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
| 1669 |
+
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
| 1670 |
+
"license": "ISC"
|
| 1671 |
+
}
|
| 1672 |
+
}
|
| 1673 |
+
}
|
react-dashboard/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"name":"openclaw-ops-react-dashboard","version":"0.1.0","private":false,"type":"module","scripts":{"dev":"vite --host 0.0.0.0","build":"vite build","preview":"vite preview --host 0.0.0.0","test":"npm run build"},"dependencies":{"@vitejs/plugin-react":"^5.0.4","vite":"^7.2.4","react":"^19.2.0","react-dom":"^19.2.0","lucide-react":"^0.468.0"}}
|
react-dashboard/src/App.jsx
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useEffect, useMemo, useState } from 'react';
|
| 2 |
+
import {
|
| 3 |
+
Activity,
|
| 4 |
+
AlertTriangle,
|
| 5 |
+
Bot,
|
| 6 |
+
CheckCircle2,
|
| 7 |
+
CirclePause,
|
| 8 |
+
CirclePlay,
|
| 9 |
+
ClipboardCheck,
|
| 10 |
+
ExternalLink,
|
| 11 |
+
RefreshCw,
|
| 12 |
+
ShieldCheck,
|
| 13 |
+
Sparkles,
|
| 14 |
+
TerminalSquare,
|
| 15 |
+
} from 'lucide-react';
|
| 16 |
+
|
| 17 |
+
const API_BASE = import.meta.env.VITE_SESSION_AMPLIFIER_BASE_URL || '/session-amplifier';
|
| 18 |
+
const POLL_MS = 15000;
|
| 19 |
+
|
| 20 |
+
async function fetchJson(path) {
|
| 21 |
+
const response = await fetch(API_BASE + path);
|
| 22 |
+
if (!response.ok) {
|
| 23 |
+
throw new Error(response.status + ' ' + response.statusText);
|
| 24 |
+
}
|
| 25 |
+
return response.json();
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
function formatAge(value) {
|
| 29 |
+
if (!value) return 'unknown';
|
| 30 |
+
const at = new Date(value);
|
| 31 |
+
if (Number.isNaN(at.getTime())) return 'unknown';
|
| 32 |
+
const seconds = Math.max(0, Math.floor((Date.now() - at.getTime()) / 1000));
|
| 33 |
+
if (seconds < 60) return seconds + 's';
|
| 34 |
+
const minutes = Math.floor(seconds / 60);
|
| 35 |
+
if (minutes < 60) return minutes + 'm';
|
| 36 |
+
const hours = Math.floor(minutes / 60);
|
| 37 |
+
if (hours < 36) return hours + 'h';
|
| 38 |
+
return Math.floor(hours / 24) + 'd';
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
function compactText(value, limit = 110) {
|
| 42 |
+
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
| 43 |
+
if (!text) return '';
|
| 44 |
+
return text.length > limit ? text.slice(0, limit - 1).trimEnd() + '…' : text;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
function looksLikeOpaqueId(value) {
|
| 48 |
+
return /^[0-9a-f]{8}-[0-9a-f-]{27,}$/i.test(String(value || '').trim());
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
function firstMeaningfulEvent(events = []) {
|
| 52 |
+
return events.find((event) => {
|
| 53 |
+
const text = event?.details || event?.summary || event?.clean_text || event?.preview || '';
|
| 54 |
+
return text && text !== 'assistant' && (event.role === 'user' || event.event_type === 'user_message');
|
| 55 |
+
}) || events.find((event) => {
|
| 56 |
+
const text = event?.details || event?.summary || event?.clean_text || event?.preview || '';
|
| 57 |
+
return text && text !== 'assistant';
|
| 58 |
+
});
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
function sessionTextLabel(event) {
|
| 62 |
+
if (!event) return '';
|
| 63 |
+
const raw = event.details || event.summary || event.clean_text || event.preview || '';
|
| 64 |
+
const firstLine = String(raw).split('\n').find((line) => line.trim()) || '';
|
| 65 |
+
const cronMatch = firstLine.match(/^\[cron:[^\s\]]+\s+([^\]]+)\]\s*(.*)$/);
|
| 66 |
+
if (cronMatch) {
|
| 67 |
+
return compactText(cronMatch[1] + ': ' + cronMatch[2], 96);
|
| 68 |
+
}
|
| 69 |
+
return compactText(firstLine.replace(/^#+\s*/, ''), 96);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
function quotedHint(event) {
|
| 73 |
+
if (!event) return '';
|
| 74 |
+
const raw = event.details || event.summary || event.clean_text || event.preview || '';
|
| 75 |
+
const quote = String(raw).match(/["“']([^"”']{8,130})["”']/);
|
| 76 |
+
if (quote?.[1]) return compactText('"' + quote[1] + '"', 120);
|
| 77 |
+
const label = sessionTextLabel(event);
|
| 78 |
+
return label ? compactText('"' + label + '"', 120) : '';
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
function shortSessionId(session) {
|
| 82 |
+
return String(session.session_id || '').slice(0, 8) || 'unknown';
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
function pickTitle(session, events = []) {
|
| 86 |
+
const configuredTitle = (
|
| 87 |
+
session.display_title ||
|
| 88 |
+
session.displayTitle ||
|
| 89 |
+
session.display_name ||
|
| 90 |
+
session.displayName ||
|
| 91 |
+
session.origin_label
|
| 92 |
+
);
|
| 93 |
+
if (configuredTitle && !looksLikeOpaqueId(configuredTitle)) {
|
| 94 |
+
return { title: configuredTitle, hint: quotedHint(firstMeaningfulEvent(events)) };
|
| 95 |
+
}
|
| 96 |
+
const firstEvent = firstMeaningfulEvent(events);
|
| 97 |
+
const derivedTitle = sessionTextLabel(firstEvent);
|
| 98 |
+
return {
|
| 99 |
+
title: derivedTitle || 'Session ' + shortSessionId(session),
|
| 100 |
+
hint: quotedHint(firstEvent),
|
| 101 |
+
};
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
function deriveState(session, events = []) {
|
| 105 |
+
if (session.derived_state) return session.derived_state;
|
| 106 |
+
if (session.health === 'error') return 'error';
|
| 107 |
+
const latest = [...events].reverse().find(Boolean);
|
| 108 |
+
if (!latest) return 'idle';
|
| 109 |
+
if (latest.is_error) return 'error';
|
| 110 |
+
if (latest.tool_name) return 'active';
|
| 111 |
+
if ((latest.preview || latest.clean_text || '').toLowerCase().includes('permission')) return 'waiting';
|
| 112 |
+
return latest.role === 'assistant' || latest.role === 'user' ? 'active' : 'idle';
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
function stateMeta(state, needsPermission) {
|
| 116 |
+
if (needsPermission || state === 'waiting') {
|
| 117 |
+
return { label: 'Waiting', tone: 'waiting', Icon: CirclePause };
|
| 118 |
+
}
|
| 119 |
+
if (state === 'active') return { label: 'Active', tone: 'active', Icon: Activity };
|
| 120 |
+
if (state === 'error') return { label: 'Error', tone: 'error', Icon: AlertTriangle };
|
| 121 |
+
return { label: 'Idle', tone: 'idle', Icon: CheckCircle2 };
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
function MetricCard({ icon: Icon, label, value, sublabel, tone = 'neutral' }) {
|
| 125 |
+
return (
|
| 126 |
+
<section className={'metric metric--' + tone}>
|
| 127 |
+
<div className="metric__icon"><Icon size={18} /></div>
|
| 128 |
+
<div>
|
| 129 |
+
<div className="metric__value">{value}</div>
|
| 130 |
+
<div className="metric__label">{label}</div>
|
| 131 |
+
{sublabel ? <div className="metric__sublabel">{sublabel}</div> : null}
|
| 132 |
+
</div>
|
| 133 |
+
</section>
|
| 134 |
+
);
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
function SessionCard({ session, events }) {
|
| 138 |
+
const state = deriveState(session, events);
|
| 139 |
+
const meta = stateMeta(state, session.needs_permission);
|
| 140 |
+
const latest = [...events].reverse().find(Boolean);
|
| 141 |
+
const currentTool = session.current_tool_name || latest?.tool_name || 'none';
|
| 142 |
+
const { title, hint } = pickTitle(session, events);
|
| 143 |
+
|
| 144 |
+
return (
|
| 145 |
+
<article className={'session-card session-card--' + meta.tone}>
|
| 146 |
+
<header className="session-card__head">
|
| 147 |
+
<div className="session-card__identity">
|
| 148 |
+
<span className={'status-pill status-pill--' + meta.tone}>
|
| 149 |
+
<meta.Icon size={14} />
|
| 150 |
+
{meta.label}
|
| 151 |
+
</span>
|
| 152 |
+
<h2 title={title}>{title}</h2>
|
| 153 |
+
{hint ? <p className="session-card__quote">{hint}</p> : null}
|
| 154 |
+
</div>
|
| 155 |
+
<span className="session-card__age">{formatAge(session.last_activity_at || session.updated_at || latest?.timestamp)}</span>
|
| 156 |
+
</header>
|
| 157 |
+
|
| 158 |
+
<div className="session-card__meta">
|
| 159 |
+
<span><Bot size={14} />{session.agent_id || 'unknown'}</span>
|
| 160 |
+
<span><TerminalSquare size={14} />{currentTool}</span>
|
| 161 |
+
<span><Activity size={14} />{shortSessionId(session)} · {events.length} events</span>
|
| 162 |
+
</div>
|
| 163 |
+
|
| 164 |
+
<p className="session-card__preview">
|
| 165 |
+
{latest?.preview || latest?.clean_text || session.active_reason || 'No recent transcript preview'}
|
| 166 |
+
</p>
|
| 167 |
+
|
| 168 |
+
<footer className="session-card__actions">
|
| 169 |
+
<button type="button" title="Open transcript when a route is wired" disabled>
|
| 170 |
+
<ExternalLink size={15} />
|
| 171 |
+
Open
|
| 172 |
+
</button>
|
| 173 |
+
<button type="button" title="Run control requires an action endpoint" disabled>
|
| 174 |
+
<CirclePlay size={15} />
|
| 175 |
+
Run
|
| 176 |
+
</button>
|
| 177 |
+
<button type="button" title="Pause/resume requires an action endpoint" disabled>
|
| 178 |
+
<CirclePause size={15} />
|
| 179 |
+
Pause
|
| 180 |
+
</button>
|
| 181 |
+
</footer>
|
| 182 |
+
</article>
|
| 183 |
+
);
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
function App() {
|
| 187 |
+
const [health, setHealth] = useState(null);
|
| 188 |
+
const [bulk, setBulk] = useState({ sessions: [], activity: {} });
|
| 189 |
+
const [skills, setSkills] = useState(null);
|
| 190 |
+
const [error, setError] = useState('');
|
| 191 |
+
const [loading, setLoading] = useState(true);
|
| 192 |
+
|
| 193 |
+
async function refresh() {
|
| 194 |
+
setError('');
|
| 195 |
+
try {
|
| 196 |
+
const results = await Promise.allSettled([
|
| 197 |
+
fetchJson('/health'),
|
| 198 |
+
fetchJson('/sessions/active-bulk?limit=30&activity_limit=80'),
|
| 199 |
+
fetchJson('/review/skills'),
|
| 200 |
+
]);
|
| 201 |
+
const [healthResult, bulkResult, skillsResult] = results;
|
| 202 |
+
if (healthResult.status === 'fulfilled') setHealth(healthResult.value);
|
| 203 |
+
if (bulkResult.status === 'fulfilled') setBulk(bulkResult.value);
|
| 204 |
+
if (skillsResult.status === 'fulfilled') setSkills(skillsResult.value);
|
| 205 |
+
const rejected = [healthResult, bulkResult].find((result) => result.status === 'rejected');
|
| 206 |
+
if (rejected) throw rejected.reason;
|
| 207 |
+
} catch (err) {
|
| 208 |
+
setError(err.message || String(err));
|
| 209 |
+
} finally {
|
| 210 |
+
setLoading(false);
|
| 211 |
+
}
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
useEffect(() => {
|
| 215 |
+
refresh();
|
| 216 |
+
const timer = window.setInterval(refresh, POLL_MS);
|
| 217 |
+
return () => window.clearInterval(timer);
|
| 218 |
+
}, []);
|
| 219 |
+
|
| 220 |
+
const sessions = bulk.sessions || [];
|
| 221 |
+
const activity = bulk.activity || {};
|
| 222 |
+
const metrics = useMemo(() => {
|
| 223 |
+
const states = sessions.map((session) => deriveState(session, activity[session.session_id] || []));
|
| 224 |
+
const active = states.filter((state) => state === 'active').length;
|
| 225 |
+
const waiting = sessions.filter((session, index) => session.needs_permission || states[index] === 'waiting').length;
|
| 226 |
+
const errors = states.filter((state) => state === 'error').length;
|
| 227 |
+
const toolHeavy = sessions.filter((session) => (activity[session.session_id] || []).filter((row) => row.tool_name).length >= 5).length;
|
| 228 |
+
const candidateCount = Array.isArray(skills?.candidates) ? skills.candidates.length : Array.isArray(skills?.recommendations) ? skills.recommendations.length : 0;
|
| 229 |
+
return { active, waiting, errors, toolHeavy, candidateCount };
|
| 230 |
+
}, [sessions, activity, skills]);
|
| 231 |
+
|
| 232 |
+
return (
|
| 233 |
+
<main className="app-shell">
|
| 234 |
+
<header className="topbar">
|
| 235 |
+
<div>
|
| 236 |
+
<p className="eyebrow">OpenClaw Ops</p>
|
| 237 |
+
<h1>Action Dashboard</h1>
|
| 238 |
+
</div>
|
| 239 |
+
<button className="refresh-button" type="button" onClick={refresh}>
|
| 240 |
+
<RefreshCw size={16} className={loading ? 'spin' : ''} />
|
| 241 |
+
Refresh
|
| 242 |
+
</button>
|
| 243 |
+
</header>
|
| 244 |
+
|
| 245 |
+
{error ? (
|
| 246 |
+
<section className="alert">
|
| 247 |
+
<AlertTriangle size={16} />
|
| 248 |
+
<span>{error}</span>
|
| 249 |
+
</section>
|
| 250 |
+
) : null}
|
| 251 |
+
|
| 252 |
+
<section className="metrics-grid">
|
| 253 |
+
<MetricCard icon={ShieldCheck} label="Amplifier" value={health?.status || 'unknown'} sublabel={(health?.sessions ?? 0) + ' indexed sessions'} tone={health?.status === 'ok' ? 'good' : 'warn'} />
|
| 254 |
+
<MetricCard icon={Activity} label="Active" value={metrics.active} sublabel={sessions.length + ' recent sessions'} tone="info" />
|
| 255 |
+
<MetricCard icon={CirclePause} label="Waiting" value={metrics.waiting} sublabel="permission or stalled state" tone={metrics.waiting ? 'warn' : 'neutral'} />
|
| 256 |
+
<MetricCard icon={AlertTriangle} label="Errors" value={metrics.errors} sublabel="recent session state" tone={metrics.errors ? 'bad' : 'neutral'} />
|
| 257 |
+
<MetricCard icon={TerminalSquare} label="Tool Heavy" value={metrics.toolHeavy} sublabel="5+ tool events" tone="neutral" />
|
| 258 |
+
<MetricCard icon={ClipboardCheck} label="Skill Signals" value={metrics.candidateCount || 'review'} sublabel="from skill review API" tone="neutral" />
|
| 259 |
+
</section>
|
| 260 |
+
|
| 261 |
+
<section className="workspace-grid">
|
| 262 |
+
<section className="panel panel--wide">
|
| 263 |
+
<div className="panel__head">
|
| 264 |
+
<h2>Live Sessions</h2>
|
| 265 |
+
<span>{bulk.generated_at ? 'Updated ' + formatAge(bulk.generated_at) + ' ago' : 'Not loaded'}</span>
|
| 266 |
+
</div>
|
| 267 |
+
<div className="sessions-grid">
|
| 268 |
+
{sessions.length ? (
|
| 269 |
+
sessions.map((session) => (
|
| 270 |
+
<SessionCard
|
| 271 |
+
key={session.session_id}
|
| 272 |
+
session={session}
|
| 273 |
+
events={activity[session.session_id] || []}
|
| 274 |
+
/>
|
| 275 |
+
))
|
| 276 |
+
) : (
|
| 277 |
+
<div className="empty-state">
|
| 278 |
+
<Sparkles size={18} />
|
| 279 |
+
<span>No recent sessions returned.</span>
|
| 280 |
+
</div>
|
| 281 |
+
)}
|
| 282 |
+
</div>
|
| 283 |
+
</section>
|
| 284 |
+
|
| 285 |
+
<aside className="panel">
|
| 286 |
+
<div className="panel__head">
|
| 287 |
+
<h2>Promotion Lane</h2>
|
| 288 |
+
<span>advisory</span>
|
| 289 |
+
</div>
|
| 290 |
+
<div className="lane-list">
|
| 291 |
+
<div className="lane-row">
|
| 292 |
+
<ShieldCheck size={16} />
|
| 293 |
+
<span>Installed skills may be used automatically when the match is safe.</span>
|
| 294 |
+
</div>
|
| 295 |
+
<div className="lane-row">
|
| 296 |
+
<ClipboardCheck size={16} />
|
| 297 |
+
<span>New installs, agent allowlists, and config changes stay approval-gated.</span>
|
| 298 |
+
</div>
|
| 299 |
+
<div className="lane-row">
|
| 300 |
+
<ExternalLink size={16} />
|
| 301 |
+
<span>Approval packets are summarized by scripts/openclaw_skill_approval_status.py.</span>
|
| 302 |
+
</div>
|
| 303 |
+
</div>
|
| 304 |
+
</aside>
|
| 305 |
+
</section>
|
| 306 |
+
</main>
|
| 307 |
+
);
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
export default App;
|
react-dashboard/src/main.jsx
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react';
|
| 2 |
+
import { createRoot } from 'react-dom/client';
|
| 3 |
+
import App from './App.jsx';
|
| 4 |
+
import './styles.css';
|
| 5 |
+
|
| 6 |
+
createRoot(document.getElementById('root')).render(
|
| 7 |
+
<React.StrictMode>
|
| 8 |
+
<App />
|
| 9 |
+
</React.StrictMode>,
|
| 10 |
+
);
|
react-dashboard/src/styles.css
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root {
|
| 2 |
+
color-scheme: dark;
|
| 3 |
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
| 4 |
+
background: #111318;
|
| 5 |
+
color: #edf1f7;
|
| 6 |
+
font-synthesis: none;
|
| 7 |
+
text-rendering: optimizeLegibility;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
* { box-sizing: border-box; }
|
| 11 |
+
|
| 12 |
+
body {
|
| 13 |
+
margin: 0;
|
| 14 |
+
min-width: 320px;
|
| 15 |
+
background: #111318;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
button { font: inherit; }
|
| 19 |
+
|
| 20 |
+
.app-shell {
|
| 21 |
+
width: min(1480px, calc(100vw - 32px));
|
| 22 |
+
margin: 0 auto;
|
| 23 |
+
padding: 24px 0 36px;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
.topbar {
|
| 27 |
+
display: flex;
|
| 28 |
+
align-items: center;
|
| 29 |
+
justify-content: space-between;
|
| 30 |
+
gap: 16px;
|
| 31 |
+
margin-bottom: 20px;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
.eyebrow {
|
| 35 |
+
margin: 0 0 4px;
|
| 36 |
+
color: #9aa8bb;
|
| 37 |
+
font-size: 0.78rem;
|
| 38 |
+
font-weight: 700;
|
| 39 |
+
letter-spacing: 0;
|
| 40 |
+
text-transform: uppercase;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
h1, h2, p { margin: 0; }
|
| 44 |
+
|
| 45 |
+
h1 {
|
| 46 |
+
font-size: clamp(1.7rem, 3vw, 2.35rem);
|
| 47 |
+
line-height: 1.05;
|
| 48 |
+
letter-spacing: 0;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.refresh-button,
|
| 52 |
+
.session-card__actions button {
|
| 53 |
+
display: inline-flex;
|
| 54 |
+
align-items: center;
|
| 55 |
+
justify-content: center;
|
| 56 |
+
gap: 7px;
|
| 57 |
+
min-height: 36px;
|
| 58 |
+
border: 1px solid #303847;
|
| 59 |
+
border-radius: 7px;
|
| 60 |
+
background: #191d25;
|
| 61 |
+
color: #edf1f7;
|
| 62 |
+
cursor: pointer;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
.refresh-button { padding: 0 14px; }
|
| 66 |
+
|
| 67 |
+
.session-card__actions button {
|
| 68 |
+
width: 100%;
|
| 69 |
+
padding: 0 10px;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.session-card__actions button:disabled {
|
| 73 |
+
cursor: not-allowed;
|
| 74 |
+
color: #7f8ca0;
|
| 75 |
+
background: #151922;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.alert {
|
| 79 |
+
display: flex;
|
| 80 |
+
align-items: center;
|
| 81 |
+
gap: 10px;
|
| 82 |
+
min-height: 42px;
|
| 83 |
+
margin-bottom: 16px;
|
| 84 |
+
padding: 0 12px;
|
| 85 |
+
border: 1px solid #8a5630;
|
| 86 |
+
border-radius: 7px;
|
| 87 |
+
background: #231a14;
|
| 88 |
+
color: #ffbf80;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
.metrics-grid {
|
| 92 |
+
display: grid;
|
| 93 |
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
| 94 |
+
gap: 12px;
|
| 95 |
+
margin-bottom: 16px;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
.metric,
|
| 99 |
+
.panel,
|
| 100 |
+
.session-card {
|
| 101 |
+
border: 1px solid #2a313f;
|
| 102 |
+
border-radius: 8px;
|
| 103 |
+
background: #171b23;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
.metric {
|
| 107 |
+
display: grid;
|
| 108 |
+
grid-template-columns: 32px minmax(0, 1fr);
|
| 109 |
+
gap: 10px;
|
| 110 |
+
min-height: 96px;
|
| 111 |
+
padding: 14px;
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
.metric__icon {
|
| 115 |
+
display: grid;
|
| 116 |
+
width: 32px;
|
| 117 |
+
height: 32px;
|
| 118 |
+
place-items: center;
|
| 119 |
+
border-radius: 7px;
|
| 120 |
+
background: #202733;
|
| 121 |
+
color: #9fb9ff;
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
.metric__value {
|
| 125 |
+
font-size: 1.45rem;
|
| 126 |
+
font-weight: 800;
|
| 127 |
+
line-height: 1.05;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
.metric__label {
|
| 131 |
+
margin-top: 4px;
|
| 132 |
+
color: #d6deeb;
|
| 133 |
+
font-weight: 700;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
.metric__sublabel {
|
| 137 |
+
margin-top: 5px;
|
| 138 |
+
color: #8e9caf;
|
| 139 |
+
font-size: 0.82rem;
|
| 140 |
+
line-height: 1.25;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
.metric--good .metric__icon,
|
| 144 |
+
.status-pill--idle { color: #6ee7a8; }
|
| 145 |
+
|
| 146 |
+
.metric--warn .metric__icon,
|
| 147 |
+
.status-pill--waiting { color: #ffd166; }
|
| 148 |
+
|
| 149 |
+
.metric--bad .metric__icon,
|
| 150 |
+
.status-pill--error { color: #ff8f8f; }
|
| 151 |
+
|
| 152 |
+
.metric--info .metric__icon,
|
| 153 |
+
.status-pill--active { color: #8ec7ff; }
|
| 154 |
+
|
| 155 |
+
.workspace-grid {
|
| 156 |
+
display: grid;
|
| 157 |
+
grid-template-columns: minmax(0, 1fr) 360px;
|
| 158 |
+
gap: 16px;
|
| 159 |
+
align-items: start;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
.panel { padding: 16px; }
|
| 163 |
+
|
| 164 |
+
.panel__head {
|
| 165 |
+
display: flex;
|
| 166 |
+
align-items: center;
|
| 167 |
+
justify-content: space-between;
|
| 168 |
+
gap: 12px;
|
| 169 |
+
margin-bottom: 14px;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
.panel__head h2,
|
| 173 |
+
.session-card h2 {
|
| 174 |
+
font-size: 1rem;
|
| 175 |
+
letter-spacing: 0;
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
.panel__head span,
|
| 179 |
+
.session-card__age {
|
| 180 |
+
color: #8794a8;
|
| 181 |
+
font-size: 0.82rem;
|
| 182 |
+
white-space: nowrap;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
.sessions-grid {
|
| 186 |
+
display: grid;
|
| 187 |
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
| 188 |
+
gap: 12px;
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
.session-card {
|
| 192 |
+
display: flex;
|
| 193 |
+
min-height: 214px;
|
| 194 |
+
flex-direction: column;
|
| 195 |
+
padding: 14px;
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
.session-card--active { border-color: #315c88; }
|
| 199 |
+
.session-card--waiting { border-color: #816632; }
|
| 200 |
+
.session-card--error { border-color: #8a3f3f; }
|
| 201 |
+
|
| 202 |
+
.session-card__head {
|
| 203 |
+
display: flex;
|
| 204 |
+
justify-content: space-between;
|
| 205 |
+
gap: 12px;
|
| 206 |
+
margin-bottom: 12px;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
.session-card__identity { min-width: 0; }
|
| 210 |
+
|
| 211 |
+
.session-card h2 {
|
| 212 |
+
margin-top: 8px;
|
| 213 |
+
overflow: hidden;
|
| 214 |
+
text-overflow: ellipsis;
|
| 215 |
+
white-space: nowrap;
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
.session-card__quote {
|
| 219 |
+
margin-top: 5px;
|
| 220 |
+
overflow: hidden;
|
| 221 |
+
color: #9faabd;
|
| 222 |
+
font-size: 0.82rem;
|
| 223 |
+
line-height: 1.3;
|
| 224 |
+
text-overflow: ellipsis;
|
| 225 |
+
white-space: nowrap;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
.status-pill {
|
| 229 |
+
display: inline-flex;
|
| 230 |
+
align-items: center;
|
| 231 |
+
gap: 6px;
|
| 232 |
+
min-height: 24px;
|
| 233 |
+
padding: 0 8px;
|
| 234 |
+
border-radius: 999px;
|
| 235 |
+
background: #202733;
|
| 236 |
+
font-size: 0.78rem;
|
| 237 |
+
font-weight: 800;
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
.session-card__meta {
|
| 241 |
+
display: grid;
|
| 242 |
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
| 243 |
+
gap: 8px;
|
| 244 |
+
margin-bottom: 12px;
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
.session-card__meta span {
|
| 248 |
+
display: inline-flex;
|
| 249 |
+
min-width: 0;
|
| 250 |
+
align-items: center;
|
| 251 |
+
gap: 6px;
|
| 252 |
+
overflow: hidden;
|
| 253 |
+
color: #a9b5c7;
|
| 254 |
+
font-size: 0.82rem;
|
| 255 |
+
text-overflow: ellipsis;
|
| 256 |
+
white-space: nowrap;
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
.session-card__preview {
|
| 260 |
+
display: -webkit-box;
|
| 261 |
+
flex: 1;
|
| 262 |
+
overflow: hidden;
|
| 263 |
+
color: #c4ccda;
|
| 264 |
+
font-size: 0.9rem;
|
| 265 |
+
line-height: 1.42;
|
| 266 |
+
-webkit-box-orient: vertical;
|
| 267 |
+
-webkit-line-clamp: 3;
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
.session-card__actions {
|
| 271 |
+
display: grid;
|
| 272 |
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
| 273 |
+
gap: 8px;
|
| 274 |
+
margin-top: 14px;
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
.lane-list { display: grid; gap: 10px; }
|
| 278 |
+
|
| 279 |
+
.lane-row,
|
| 280 |
+
.empty-state {
|
| 281 |
+
display: flex;
|
| 282 |
+
align-items: center;
|
| 283 |
+
gap: 10px;
|
| 284 |
+
color: #c4ccda;
|
| 285 |
+
line-height: 1.35;
|
| 286 |
+
}
|
| 287 |
+
|
| 288 |
+
.lane-row {
|
| 289 |
+
min-height: 46px;
|
| 290 |
+
padding: 10px;
|
| 291 |
+
border: 1px solid #2a313f;
|
| 292 |
+
border-radius: 7px;
|
| 293 |
+
background: #141820;
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
.empty-state {
|
| 297 |
+
min-height: 130px;
|
| 298 |
+
justify-content: center;
|
| 299 |
+
border: 1px dashed #364154;
|
| 300 |
+
border-radius: 8px;
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
.spin { animation: spin 0.9s linear infinite; }
|
| 304 |
+
|
| 305 |
+
@keyframes spin {
|
| 306 |
+
from { transform: rotate(0deg); }
|
| 307 |
+
to { transform: rotate(360deg); }
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
@media (max-width: 1120px) {
|
| 311 |
+
.metrics-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
| 312 |
+
.workspace-grid { grid-template-columns: 1fr; }
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
@media (max-width: 760px) {
|
| 316 |
+
.app-shell {
|
| 317 |
+
width: min(100vw - 20px, 720px);
|
| 318 |
+
padding-top: 16px;
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
.topbar {
|
| 322 |
+
align-items: flex-start;
|
| 323 |
+
flex-direction: column;
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
.metrics-grid,
|
| 327 |
+
.sessions-grid { grid-template-columns: 1fr; }
|
| 328 |
+
|
| 329 |
+
.session-card__meta,
|
| 330 |
+
.session-card__actions { grid-template-columns: 1fr; }
|
| 331 |
+
}
|
react-dashboard/vite.config.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defineConfig } from 'vite';
|
| 2 |
+
import react from '@vitejs/plugin-react';
|
| 3 |
+
|
| 4 |
+
export default defineConfig({
|
| 5 |
+
plugins: [react()],
|
| 6 |
+
base: process.env.VITE_BASE_PATH || '/',
|
| 7 |
+
server: {
|
| 8 |
+
port: 5177,
|
| 9 |
+
strictPort: false,
|
| 10 |
+
proxy: {
|
| 11 |
+
'/session-amplifier': {
|
| 12 |
+
target: process.env.SESSION_AMPLIFIER_BASE_URL || 'http://localhost:8477',
|
| 13 |
+
changeOrigin: true,
|
| 14 |
+
rewrite: (path) => path.replace(/^\/session-amplifier/, ''),
|
| 15 |
+
},
|
| 16 |
+
},
|
| 17 |
+
},
|
| 18 |
+
});
|
requirements.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit==1.26.0
|
| 2 |
+
|