feat: implement OpenClaw integration with new API endpoints, frontend page, client, and tests.

This commit is contained in:
2026-03-14 11:38:16 -04:00
parent 8c6ef5acb8
commit 3b82d69b22
4 changed files with 274 additions and 21 deletions
+72
View File
@@ -12,6 +12,7 @@ import os
import re
import shutil
import socket
import sqlite3
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Optional
@@ -473,6 +474,77 @@ async def list_openclaw_skills():
return {"skills": skills, "count": len(skills)}
# ═════════════════════════════════════════════════════════════════════════════════
# 4.a Agent Memory (QMD)
# ═════════════════════════════════════════════════════════════════════════════════
@router.get("/agents/{agent_name}/memory")
async def get_agent_memory(agent_name: str, limit: int = 100):
"""Attempt to read the agent's memory (QMD) SQLite database."""
ws_dir = _workspace() / agent_name
if not ws_dir.is_dir():
return {"has_db": False, "error": "Workspace not found"}
db_files = list(ws_dir.rglob("*.sqlite")) + list(ws_dir.rglob("*.db")) + list(ws_dir.rglob("*.qmd"))
if not db_files:
return {"has_db": False, "message": "No SQLite memory database found"}
db_path = db_files[0]
result = {
"has_db": True,
"db_path": str(db_path.relative_to(_home())),
"size": db_path.stat().st_size,
"timeline": [],
"stats": {"total_thoughts": 0}
}
def _read_db():
timeline = []
try:
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
# Check if common thought tables exist
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = [row["name"] for row in cursor.fetchall()]
result["tables"] = tables
# Very heuristic approach to grab 'thoughts' or 'history'
target_table = None
for t in ["thoughts", "messages", "history", "runs", "memory", "qmd_memory", "events"]:
if t in tables:
target_table = t
break
if target_table:
# Get column names
cursor.execute(f"PRAGMA table_info('{target_table}')")
columns = [c[1] for c in cursor.fetchall()]
query = f"SELECT * FROM {target_table} ORDER BY rowid DESC LIMIT ?"
if "timestamp" in columns or "created_at" in columns:
order_col = "timestamp" if "timestamp" in columns else "created_at"
query = f"SELECT * FROM {target_table} ORDER BY {order_col} DESC LIMIT ?"
cursor.execute(query, (limit,))
rows = cursor.fetchall()
timeline = [dict(r) for r in rows]
# Count total
cursor.execute(f"SELECT COUNT(*) FROM {target_table}")
result["stats"]["total_thoughts"] = cursor.fetchone()[0]
conn.close()
return timeline
except Exception as e:
log.error(f"Error reading QMD db: {e}")
return []
result["timeline"] = await asyncio.to_thread(_read_db)
return result
# ═════════════════════════════════════════════════════════════════════════════════
# 5. Logs
# ═════════════════════════════════════════════════════════════════════════════════
+47
View File
@@ -0,0 +1,47 @@
import os
from pathlib import Path
import sqlite3
def explore_qmd():
foxy_home_str = os.environ.get("FOXY_HOME", "/home/foxy/.openclaw")
# If running on windows local, this might resolve weirdly.
# We will try the raw string first.
home = Path(foxy_home_str)
# Alternatively try C:\home\foxy\.openclaw if windows
if not home.exists() and os.name == 'nt':
alt_home = Path("C:") / foxy_home_str
if alt_home.exists():
home = alt_home
workspace = home / "workspace"
print("Exploring workspace:", workspace)
print("Exists:", workspace.exists())
if not workspace.exists():
print("Cannot find workspace.")
return
sqlite_files = list(workspace.rglob("*.sqlite")) + list(workspace.rglob("*.db")) + list(workspace.rglob("*.qmd"))
print("Found sqlite files:", len(sqlite_files))
for f in sqlite_files:
print("\n--- SCHEMA FOR", f)
try:
conn = sqlite3.connect(f)
cursor = conn.cursor()
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = cursor.fetchall()
for t in tables:
table_name = t[0]
print(f"Table: {table_name}")
cursor.execute(f"PRAGMA table_info('{table_name}')")
columns = cursor.fetchall()
for c in columns:
print(f" - {c[1]} ({c[2]})")
conn.close()
except Exception as e:
print("Error reading db:", e)
if __name__ == "__main__":
explore_qmd()