EXTENSION for TreeOS
dashboard
The real-time operations panel for TreeOS. Every active session, every tree, every conversation happening on the land is visible in one place. The dashboard does not use HTTP polling. It registers WebSocket event handlers that push state changes to connected clients the instant they happen. Four socket handlers power the dashboard. getDashboardSessions returns all sessions for the current user with the active navigator session highlighted. getDashboardRoots lists all trees owned by the user with child counts. getDashboardTree loads a full tree structure recursively, populating all children depth-first and simplifying each node to id, name, status, and children. getDashboardChats retrieves the conversation history for a specific session so the user can review what the AI said and did. Session changes are pushed automatically. The extension hooks into afterSessionCreate and afterSessionEnd. Whenever any session starts or ends, the dashboard emits an updated session list to all of that user's connected sockets. The user never has to refresh. New sessions appear, ended sessions disappear, and the active navigator indicator updates in real time. The dashboard is infrastructure for frontends. It provides the data layer and the real-time transport. The React app, the HTML rendering extension, or any custom client can connect via WebSocket and receive the same structured events. No routes. No REST endpoints. Pure socket communication.
v1.0.1 by TreeOS Site 0 downloads 2 files 172 lines 6.1 KB published 38d ago
treeos ext install dashboard
View changelog

Manifest

Requires

  • services: websocket, session
  • models: Node

Optional

  • extensions: treeos-base
SHA256: ab98f4f24ccf7c6a6b7f6030f7bc9a019616e257f9d82a19dcfb7d4f03afde9f

Dependents

1 package depend on this

PackageTypeRelationship
treeos v1.0.1osstandalone

Source Code

1import 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}
123
1export 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

Versions

Version Published Downloads
1.0.1 38d ago 0
1.0.0 48d ago 0
0 stars
0 flags
React from the CLI: treeos ext star dashboard

Comments

Loading comments...

Post comments from the CLI: treeos ext comment dashboard "your comment"
Max 3 comments per extension. One star and one flag per user.