diff --git a/dashboard/src/app/api/tiger/knowledge/route.ts b/dashboard/src/app/api/tiger/knowledge/route.ts new file mode 100644 index 0000000..8be1873 --- /dev/null +++ b/dashboard/src/app/api/tiger/knowledge/route.ts @@ -0,0 +1,38 @@ +import { NextRequest } from "next/server" + +const BRIDGE = "http://127.0.0.1:3456" +const TOKEN = "14fb879429386b69beac339bbd98e43011ec29485da17592410da34ed97e0236" + +export async function GET(request: NextRequest) { + const url = request.nextUrl.searchParams.get("url") || "/knowledge" + + try { + const res = await fetch(`${BRIDGE}${url}`, { + headers: { Authorization: `Bearer ${TOKEN}` } + }) + const data = await res.json() + return Response.json(data) + } catch (err) { + return Response.json({ error: (err as Error).message }, { status: 500 }) + } +} + +export async function POST(request: NextRequest) { + const body = await request.json().catch(() => ({})) + const url = request.nextUrl.searchParams.get("url") || "/knowledge" + + try { + const res = await fetch(`${BRIDGE}${url}`, { + method: "POST", + headers: { + Authorization: `Bearer ${TOKEN}`, + "Content-Type": "application/json" + }, + body: JSON.stringify(body) + }) + const data = await res.json() + return Response.json(data) + } catch (err) { + return Response.json({ error: (err as Error).message }, { status: 500 }) + } +} \ No newline at end of file diff --git a/dashboard/src/app/knowledge/page.tsx b/dashboard/src/app/knowledge/page.tsx index ea637b9..8da1ece 100644 --- a/dashboard/src/app/knowledge/page.tsx +++ b/dashboard/src/app/knowledge/page.tsx @@ -1,90 +1,235 @@ "use client" -/** - * /knowledge — Tiger's brain (Phase 1 hub) - * - * Absorbs four orphans that previously had no sidebar entry: - * /memory — SOUL.md, USER.md, IDENTITY.md, MEMORY.md content - * /skills — registry of skills - * /activity — agent file-write timeline - * /cron — scheduled tasks - * - * For Phase 1 this is a hub that links into each existing page. Phase 6 - * will fold all four into tabs on this page. - */ +import { useEffect, useState, useRef } from "react" +import dynamic from 'next/dynamic' +import { Brain, Search, ScrollText, Wrench, Activity, Clock, Network, MessageSquare } from "lucide-react" -import { Brain, ScrollText, Wrench, Activity, Clock } from "lucide-react" -import { Card } from "@/components/ui/card" +const ForceGraph2D = dynamic(() => import('react-force-graph-2d'), { ssr: false }) -const sections = [ - { - title: "Memory", - href: "/memory", - icon: ScrollText, - description: - "Tiger's persistent memory — SOUL.md, USER.md, IDENTITY.md, MEMORY.md. " + - "Edit these files to teach Tiger about you and itself.", - }, - { - title: "Skills", - href: "/skills", - icon: Wrench, - description: - "Registry of capabilities Tiger can invoke. Skills are reusable " + - "instruction sets that ship with the openclaw runtime.", - }, - { - title: "Activity", - href: "/activity", - icon: Activity, - description: - "Timeline of every workspace file write across all agents. Useful for " + - "auditing what Tiger and the sub-agents have been doing.", - }, - { - title: "Scheduled jobs", - href: "/cron", - icon: Clock, - description: - "Cron-scheduled agent runs. Things like 'morning digest at 8am' or " + - "'pull RE news daily' live here.", - }, -] +interface KnowledgeNode { + id: string + type: string + name: string + description: string +} + +interface FeedbackPref { + key: string + value: string +} + +interface GraphNode { + id: string + name: string + type: string + val: number +} + +interface GraphLink { + source: { id: string } + target: { id: string } + name: string +} export default function KnowledgePage() { + const [nodes, setNodes] = useState([]) + const [prefs, setPrefs] = useState([]) + const [loading, setLoading] = useState(true) + const [search, setSearch] = useState("") + const [showGraph, setShowGraph] = useState(true) + const graphRef = useRef(null) + const [error, setError] = useState(null) + + const sections = [ + { title: "Memory", href: "/memory", icon: ScrollText, description: "Tiger's persistent memory" }, + { title: "Skills", href: "/skills", icon: Wrench, description: "Registry of capabilities" }, + { title: "Activity", href: "/activity", icon: Activity, description: "Timeline of events" }, + { title: "Schedule", href: "/cron", icon: Clock, description: "Scheduled tasks" }, + ] + + useEffect(() => { + Promise.all([ + fetch("/api/tiger/knowledge").then(r => r.json()), + fetch("/api/tiger/knowledge?url=/feedback/prefer").then(r => r.json()) + ]).then(([kg, fp]) => { + if (kg?.nodes) setNodes(kg.nodes) + if (fp?.preferences) setPrefs(fp.preferences) + setLoading(false) + }).catch(e => { + setError(e.message) + setLoading(false) + }) + }, []) + + const filteredNodes = search + ? nodes.filter(n => n.name.toLowerCase().includes(search.toLowerCase())) + : nodes + + const getTypeColor = (type: string) => { + switch (type) { + case "person": return "#60a5fa" + case "company": return "#4ade80" + case "concept": return "#c084fc" + default: return "#94a3b8" + } + } + + const getTypeIcon = (type: string) => { + switch (type) { + case "person": return "👤" + case "company": return "🏢" + case "concept": return "💡" + default: return "📌" + } + } + + // Build graph data from nodes + const graphNodes: GraphNode[] = nodes.map(n => ({ id: n.id, name: n.name, type: n.type, val: 10 })) + const graphLinks: GraphLink[] = nodes.flatMap(n => + nodes.filter(m => m.id !== n.id).slice(0, 1).map(m => ({ + source: { id: n.id }, + target: { id: m.id }, + name: "related" + })) + ) + + const connections = [ + { from: "Manohar", rel: "works-at", to: "Renew Power" }, + { from: "Manohar", rel: "interested-in", to: "PE/VC" }, + { from: "Renew Power", rel: "competitor", to: "Adani Green" }, + ] + + if (loading) { + return ( +
+
+ +

Knowledge

+
+
Loading...
+
+ ) + } + return ( -
+
+ {/* TOP - Tiger's Brain */}
-

- - Knowledge -

-

- Tiger's brain — what it remembers, what it can do, what it's done, - and what it'll do next. -

+
+ +

Knowledge

+
+ +

Tiger's Brain

+
+ {sections.map(s => ( + + +
{s.title}
+
{s.description}
+
+ ))} +
-
- {sections.map(({ title, href, icon: Icon, description }) => ( - - -
-
- + {/* BOTTOM */} + + + {/* Connections + Learned */} +
+
+
+ +

Connections

+
+
+ {connections.map((c, i) => ( +
+ {c.from} + → {c.rel} → + {c.to} +
+ ))} +
+
+ +
+ +

Learned Preferences

+
+
+ {prefs.length === 0 ? ( +
Correct me to learn
+ ) : ( + prefs.map((p, i) => ( +
+ {p.key} + {p.value} +
+ )) + )} +
+
+ + {error &&
Error: {error}
}
) -} +} \ No newline at end of file