ab98f4f24ccf7c6a6b7f6030f7bc9a019616e257f9d82a19dcfb7d4f03afde9f1import log from "../../seed/log.js";
2import { DELETED, NODE_STATUS } from "../../seed/protocol.js";
3import { getChats } from "../../seed/llm/chatHistory.js";
4
5export async function init(core) {
6 const Node = core.models.Node;
7 const {
8 getSessionsForUser, getActiveNavigator,
9 } = core.session;
10
11 // ── Dashboard tree helper ──────────────────────────────────────────
12 async function loadTreeForDashboard(rootId) {
13 const root = await Node.findById(rootId).populate("children").exec();
14 if (!root) throw new Error("Tree not found");
15 const populateChildren = async (node) => {
16 if (node.children?.length > 0) {
17 node.children = await Node.populate(node.children, { path: "children" });
18 for (const c of node.children) await populateChildren(c);
19 }
20 };
21 await populateChildren(root);
22 const simplify = (n) => {
23 const obj = typeof n.toObject === "function" ? n.toObject() : n;
24 return {
25 id: String(obj._id),
26 name: obj.name,
27 status: obj.status || "active",
28 children: (obj.children || []).map((c) =>
29 simplify(
30 typeof c === "object" && c !== null
31 ? c
32 : { _id: c, name: "?", children: [], status: NODE_STATUS.ACTIVE },
33 ),
34 ),
35 };
36 };
37 return simplify(root);
38 }
39
40 // ── Register socket handlers ──────────────────────────────────────
41 core.websocket.registerSocketHandler("getDashboardSessions", ({ socket, userId }) => {
42 if (!userId) return;
43 const sessions = getSessionsForUser(userId);
44 const activeNav = getActiveNavigator(userId);
45 socket.emit("dashboardSessions", {
46 sessions,
47 activeNavigatorId: activeNav,
48 selfSessionId: socket._registrySessionId || null,
49 });
50 });
51
52 core.websocket.registerSocketHandler("getDashboardTree", async ({ socket, userId, data }) => {
53 const rootId = data?.rootId;
54 if (!userId || !rootId) return;
55 try {
56 const tree = await loadTreeForDashboard(rootId);
57 socket.emit("dashboardTreeData", { rootId, tree });
58 } catch (err) {
59 socket.emit("dashboardTreeData", { rootId, error: err.message });
60 }
61 });
62
63 core.websocket.registerSocketHandler("getDashboardRoots", async ({ socket, userId }) => {
64 if (!userId) return;
65 try {
66 const roots = await Node.find({
67 rootOwner: userId,
68 parent: { $ne: DELETED },
69 }).select("_id name children");
70 const simplified = roots.map((r) => ({
71 id: String(r._id),
72 name: r.name,
73 childCount: r.children ? r.children.length : 0,
74 }));
75 socket.emit("dashboardRoots", { roots: simplified });
76 } catch (err) {
77 socket.emit("dashboardRoots", { roots: [], error: err.message });
78 }
79 });
80
81 core.websocket.registerSocketHandler("getDashboardChats", async ({ socket, userId, data }) => {
82 const sessionId = data?.sessionId;
83 if (!userId || !sessionId) return;
84 try {
85 const { sessions } = await getChats({
86 userId,
87 sessionId,
88 sessionLimit: 1,
89 });
90 const chats = sessions.flatMap((s) => s.chats);
91 socket.emit("dashboardChats", { sessionId, chats });
92 } catch (err) {
93 socket.emit("dashboardChats", { sessionId, error: err.message });
94 }
95 });
96
97 // ── Subscribe to session changes for real-time dashboard push ─────
98 function pushDashboard({ userId }) {
99 const sessions = getSessionsForUser(userId);
100 const activeNav = getActiveNavigator(userId);
101 core.websocket.emitToUser(userId, "dashboardSessions", {
102 sessions,
103 activeNavigatorId: activeNav,
104 });
105 }
106 core.hooks.register("afterSessionCreate", pushDashboard, "dashboard");
107 core.hooks.register("afterSessionEnd", pushDashboard, "dashboard");
108
109 // Register tree quick link
110 try {
111 const { getExtension } = await import("../loader.js");
112 const treeos = getExtension("treeos-base");
113 treeos?.exports?.registerSlot?.("tree-quick-links", "dashboard", () =>
114 `<a href="/dashboard/flow" class="back-link">Flow</a>`,
115 { priority: 50 }
116 );
117 } catch {}
118
119 log.info("Dashboard", "Dashboard socket handlers registered");
120
121 return {};
122}
1231export default {
2 name: "dashboard",
3 version: "1.0.1",
4 builtFor: "TreeOS",
5 description:
6 "The real-time operations panel for TreeOS. Every active session, every tree, every " +
7 "conversation happening on the land is visible in one place. The dashboard does not " +
8 "use HTTP polling. It registers WebSocket event handlers that push state changes to " +
9 "connected clients the instant they happen. " +
10 "\n\n" +
11 "Four socket handlers power the dashboard. getDashboardSessions returns all sessions " +
12 "for the current user with the active navigator session highlighted. getDashboardRoots " +
13 "lists all trees owned by the user with child counts. getDashboardTree loads a full " +
14 "tree structure recursively, populating all children depth-first and simplifying each " +
15 "node to id, name, status, and children. getDashboardChats retrieves the conversation " +
16 "history for a specific session so the user can review what the AI said and did. " +
17 "\n\n" +
18 "Session changes are pushed automatically. The extension hooks into afterSessionCreate " +
19 "and afterSessionEnd. Whenever any session starts or ends, the dashboard emits an " +
20 "updated session list to all of that user's connected sockets. The user never has to " +
21 "refresh. New sessions appear, ended sessions disappear, and the active navigator " +
22 "indicator updates in real time. " +
23 "\n\n" +
24 "The dashboard is infrastructure for frontends. It provides the data layer and the " +
25 "real-time transport. The React app, the HTML rendering extension, or any custom " +
26 "client can connect via WebSocket and receive the same structured events. No routes. " +
27 "No REST endpoints. Pure socket communication.",
28
29 needs: {
30 services: ["websocket", "session"],
31 models: ["Node"],
32 },
33
34 optional: {
35 extensions: ["treeos-base"],
36 },
37
38 provides: {
39 routes: false,
40 tools: false,
41 modes: false,
42 jobs: false,
43 orchestrator: false,
44 energyActions: {},
45 sessionTypes: {},
46 cli: [],
47 },
48};
49
| Version | Published | Downloads |
|---|---|---|
| 1.0.1 | 38d ago | 0 |
| 1.0.0 | 48d ago | 0 |
treeos ext star dashboard
Post comments from the CLI: treeos ext comment dashboard "your comment"
Max 3 comments per extension. One star and one flag per user.
Loading comments...