EXTENSION for TreeOS
monitor
Activity monitoring for the land, accessible through both AI conversation and raw data endpoints. Registers the land:monitor mode, a conversational AI that acts like a talking dashboard. Instead of dumping raw data, the monitor tells a story: '12 AI conversations today, mostly on the Fitness tree. Your bench press had 3 sessions this week, progressing from 130 to 140.' The mode has access to land-status, land-ext-list, land-users, land-peers, land-system-nodes, land-config-read, get-contributions-by-user, and get-root-nodes for gathering data before summarizing. The POST /land/activity endpoint drives the conversational interface. It accepts a natural language query, runs it through runChat in the land:monitor mode, and returns the AI's narrative summary. The CLI command 'activity' maps to this endpoint, so admins can ask 'what happened today' or 'which trees are busiest' from the command line. The GET /land/activity endpoint provides structured data without AI involvement, designed for dashboards and health checks. It aggregates contribution counts (today and this week), chat session counts (today and this week), action type breakdowns (top 10 actions today), AI mode usage breakdowns (top 10 mode/zone combinations today), total user count, loaded extension count, and registered hooks. All date ranges are computed server-side (24 hours and 7 days from request time). Both endpoints are admin-only.
v1.0.2 by TreeOS Site 0 downloads 4 files 243 lines 8.5 KB published 38d ago
treeos ext install monitor
View changelog

Manifest

Provides

  • routes
  • 1 CLI commands

Requires

  • services: hooks
  • models: Node, User, Contribution

Optional

  • services: llm
SHA256: 1bc6745360a92a2312adc13404b72808ff8fb409b0dcb14a2e9a58448acd3405

Dependents

1 package depend on this

PackageTypeRelationship
treeos v1.0.1osstandalone

CLI Commands

CommandMethodDescription
activity [query...]POSTAsk about land activity. What happened today, which trees are busiest, AI usage stats.

Source Code

1import router from "./routes.js";
2import monitorMode from "./modes/monitor.js";
3
4export async function init(core) {
5  core.modes.registerMode("land:monitor", monitorMode, "monitor");
6
7  if (core.llm?.registerModeAssignment) {
8    core.llm.registerModeAssignment("land:monitor", "monitor");
9  }
10
11  return { router };
12}
13
1export default {
2  name: "monitor",
3  version: "1.0.2",
4  builtFor: "TreeOS",
5  description:
6    "Activity monitoring for the land, accessible through both AI conversation and raw data " +
7    "endpoints. Registers the land:monitor mode, a conversational AI that acts like a talking " +
8    "dashboard. Instead of dumping raw data, the monitor tells a story: '12 AI conversations " +
9    "today, mostly on the Fitness tree. Your bench press had 3 sessions this week, progressing " +
10    "from 130 to 140.' The mode has access to land-status, land-ext-list, land-users, " +
11    "land-peers, land-system-nodes, land-config-read, get-contributions-by-user, and " +
12    "get-root-nodes for gathering data before summarizing.\n\n" +
13    "The POST /land/activity endpoint drives the conversational interface. It accepts a " +
14    "natural language query, runs it through runChat in the land:monitor mode, and returns " +
15    "the AI's narrative summary. The CLI command 'activity' maps to this endpoint, so admins " +
16    "can ask 'what happened today' or 'which trees are busiest' from the command line.\n\n" +
17    "The GET /land/activity endpoint provides structured data without AI involvement, designed " +
18    "for dashboards and health checks. It aggregates contribution counts (today and this week), " +
19    "chat session counts (today and this week), action type breakdowns (top 10 actions today), " +
20    "AI mode usage breakdowns (top 10 mode/zone combinations today), total user count, loaded " +
21    "extension count, and registered hooks. All date ranges are computed server-side (24 hours " +
22    "and 7 days from request time). Both endpoints are admin-only.",
23
24  needs: {
25    services: ["hooks"],
26    models: ["Node", "User", "Contribution"],
27  },
28
29  optional: {
30    services: ["llm"],
31  },
32
33  provides: {
34    models: {},
35    routes: "./routes.js",
36    tools: false,
37    jobs: false,
38    orchestrator: false,
39    energyActions: {},
40    sessionTypes: {},
41
42    hooks: {
43      fires: [],
44      listens: [],
45    },
46
47    cli: [
48      {
49        command: "activity [query...]", scope: ["home","land"],
50        description: "Ask about land activity. What happened today, which trees are busiest, AI usage stats.",
51        method: "POST",
52        endpoint: "/land/activity",
53        body: ["query"],
54      },
55    ],
56  },
57};
58
1export default {
2  emoji: "📊",
3  label: "Monitor",
4  bigMode: "land",
5
6  toolNames: [
7    "land-status",
8    "land-ext-list",
9    "land-users",
10    "land-peers",
11    "land-system-nodes",
12    "land-config-read",
13    "get-contributions-by-user",
14    "get-root-nodes",
15  ],
16
17  buildSystemPrompt({ username }) {
18    return `You are the activity monitor for this TreeOS land. ${username} is asking about what's happening.
19
20YOUR ROLE
21You summarize land activity. You don't dump raw data. You tell a story.
22"12 AI conversations today, mostly on the Fitness tree. Prestige fired 8 times.
23Your bench press had 3 sessions this week, progressing from 130 to 140."
24
25WHAT YOU CAN SEE
26Use your tools to gather data, then summarize concisely:
27- land-status: overview of the land (users, trees, extensions, peers)
28- land-ext-list: which extensions are loaded with versions
29- land-users: user list with profile types
30- land-peers: federation peer status
31- land-system-nodes: system node details
32- land-config-read: read any config value
33- get-contributions-by-user: audit trail for a specific user
34- get-root-nodes: list of trees
35
36HOW TO ANSWER
371. Gather relevant data with tools (usually 1-2 calls)
382. Aggregate mentally: counts, trends, patterns
393. Present as a short narrative, not a table dump
404. Highlight anything unusual: spikes, errors, new activity, quiet periods
41
42EXAMPLES OF GOOD ANSWERS
43"Quiet day. 4 AI chats, all on your Life tree. No new users. All 2 peers alive."
44
45"Busy week. 89 contributions across 3 trees. Fitness tree is the most active with 34 contributions (mostly value edits from workout logging). 2 new users registered. Understanding ran twice on the Life tree."
46
47"Right now: 3 active sessions, 25 extensions loaded, 2 peers connected. No circuit breakers tripped. Last heartbeat was 4 minutes ago."
48
49WHAT NOT TO DO
50- Don't list every contribution individually
51- Don't show raw JSON
52- Don't say "I queried the database and found..."
53- Don't overwhelm with numbers. Pick the 3-5 most interesting facts.
54- If asked about a specific thing, focus on that. Don't give a full report when they asked about one tree.
55
56TONE
57Clear. Concise. Like a dashboard that talks. Not a sysadmin report.`;
58  },
59};
60
1import express from "express";
2import authenticate from "../../seed/middleware/authenticate.js";
3import { sendOk, sendError, ERR } from "../../seed/protocol.js";
4import User from "../../seed/models/user.js";
5import Contribution from "../../seed/models/contribution.js";
6import log from "../../seed/log.js";
7
8const router = express.Router();
9
10// POST /land/activity - ask about land activity
11router.post("/land/activity", authenticate, async (req, res) => {
12  try {
13    const user = await User.findById(req.userId).select("isAdmin username").lean();
14    if (!user || !user.isAdmin) {
15      return sendError(res, 403, ERR.FORBIDDEN, "Requires admin.");
16    }
17
18    const rawQuery = req.body.query;
19    const query = Array.isArray(rawQuery) ? rawQuery.join(" ") : rawQuery;
20    if (!query) return sendError(res, 400, ERR.INVALID_INPUT, "query required");
21
22    const { runChat } = await import("../../seed/llm/conversation.js");
23
24    const { answer, chatId } = await runChat({
25      userId: req.userId,
26      username: user.username,
27      message: query,
28      mode: "land:monitor",
29      res,
30    });
31
32    sendOk(res, { answer, chatId });
33  } catch (err) {
34    log.error("Monitor", "Activity query error:", err.message);
35    sendError(res, 500, ERR.INTERNAL, err.message);
36  }
37});
38
39// GET /land/activity - quick stats without AI (for dashboards, health checks)
40router.get("/land/activity", authenticate, async (req, res) => {
41  try {
42    const user = await User.findById(req.userId).select("isAdmin").lean();
43    if (!user || !user.isAdmin) {
44      return sendError(res, 403, ERR.FORBIDDEN, "Requires god-tier.");
45    }
46
47    const now = new Date();
48    const oneDayAgo = new Date(now - 24 * 60 * 60 * 1000);
49    const oneWeekAgo = new Date(now - 7 * 24 * 60 * 60 * 1000);
50
51    const Chat = (await import("../../seed/models/chat.js")).default;
52    const { getSessionsForUser } = await import("../../seed/ws/sessionRegistry.js");
53    const { hooks } = await import("../../seed/hooks.js");
54    const { getLoadedExtensionNames } = await import("../../extensions/loader.js");
55
56    // Aggregate stats
57    const [
58      contributionsToday,
59      contributionsWeek,
60      chatsToday,
61      chatsWeek,
62      totalUsers,
63    ] = await Promise.all([
64      Contribution.countDocuments({ date: { $gte: oneDayAgo } }),
65      Contribution.countDocuments({ date: { $gte: oneWeekAgo } }),
66      Chat.countDocuments({ "startMessage.time": { $gte: oneDayAgo } }),
67      Chat.countDocuments({ "startMessage.time": { $gte: oneWeekAgo } }),
68      User.countDocuments({}),
69    ]);
70
71    // Action breakdown today
72    const actionBreakdown = await Contribution.aggregate([
73      { $match: { date: { $gte: oneDayAgo } } },
74      { $group: { _id: "$action", count: { $sum: 1 } } },
75      { $sort: { count: -1 } },
76      { $limit: 10 },
77    ]);
78
79    // AI mode breakdown today
80    const modeBreakdown = await Chat.aggregate([
81      { $match: { "startMessage.time": { $gte: oneDayAgo } } },
82      { $group: { _id: { zone: "$aiContext.zone", mode: "$aiContext.mode" }, count: { $sum: 1 } } },
83      { $sort: { count: -1 } },
84      { $limit: 10 },
85    ]);
86
87    sendOk(res, {
88      period: { today: oneDayAgo, week: oneWeekAgo },
89      today: {
90        contributions: contributionsToday,
91        chats: chatsToday,
92        actions: actionBreakdown.map(a => ({ action: a._id, count: a.count })),
93        modes: modeBreakdown.map(m => ({ mode: m._id, count: m.count })),
94      },
95      week: {
96        contributions: contributionsWeek,
97        chats: chatsWeek,
98      },
99      system: {
100        users: totalUsers,
101        extensions: getLoadedExtensionNames().length,
102        hooks: hooks.list(),
103      },
104    });
105  } catch (err) {
106    log.error("Monitor", "Stats error:", err.message);
107    sendError(res, 500, ERR.INTERNAL, err.message);
108  }
109});
110
111export default router;
112

Versions

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

Comments

Loading comments...

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