UNPKG

naisys

Version:

NAISYS - Autonomous AI agent runner with built-in context management and cost tracking

181 lines 6.75 kB
import { keyBy, pushToArrayMap } from "@naisys/common"; import table from "text-table"; import { usersCmd } from "../command/commandDefs.js"; function buildHierarchy(users) { const nodeMap = new Map(); const roots = []; for (const user of users) { nodeMap.set(user.userId, { ...user, children: [] }); } for (const user of users) { const node = nodeMap.get(user.userId); if (user.leadUserId && nodeMap.has(user.leadUserId)) { nodeMap.get(user.leadUserId).children.push(node); } else { roots.push(node); } } return roots; } function flattenHierarchy(nodes, hiddenCounts, depth = 0) { const result = []; const sortedNodes = [...nodes].sort((a, b) => a.username.localeCompare(b.username)); for (const node of sortedNodes) { result.push({ type: "user", node, depth }); result.push(...flattenHierarchy(node.children, hiddenCounts, depth + 1)); const hidden = hiddenCounts.get(node.userId) ?? 0; if (hidden > 0) { result.push({ type: "hidden", count: hidden, depth: depth + 1 }); } } return result; } /** * Collect the set of user IDs relevant to the current agent: * - All root nodes (no lead) * - Superiors chain (walking up leadUserId) * - Peers (direct subordinates of each superior, i.e. siblings) * - All subordinates (recursively walking down) */ function getRelevantUserIds(allUsers, currentUserId) { const relevant = new Set(); const byId = keyBy(allUsers, (u) => u.userId); const childrenOf = new Map(); for (const u of allUsers) { if (u.leadUserId != null) { pushToArrayMap(childrenOf, u.leadUserId, u.userId); } } // Root nodes for (const u of allUsers) { if (!u.leadUserId) { relevant.add(u.userId); } } // Walk up the chain (superiors) and include peers at each level let cursor = currentUserId; while (true) { relevant.add(cursor); const user = byId.get(cursor); if (!user?.leadUserId) break; // Add peers (all direct subordinates of our superior) for (const peerId of childrenOf.get(user.leadUserId) ?? []) { relevant.add(peerId); } cursor = user.leadUserId; } // Walk down: all subordinates recursively const addDescendants = (parentId) => { for (const childId of childrenOf.get(parentId) ?? []) { relevant.add(childId); addDescendants(childId); } }; addDescendants(currentUserId); return relevant; } export function createUserDisplayService(userService, inputMode, localUserId) { function handleCommand(cmdArgs) { const targetUsername = cmdArgs.trim(); let perspectiveUserId = localUserId; if (targetUsername) { const targetUser = userService.getUserByName(targetUsername); if (!targetUser) { return `Error: user '${targetUsername}' not found`; } perspectiveUserId = targetUser.userId; } // Filter ephemerals to those owned by this perspective so foreign // ephemerals are not counted in the hidden-children rollup either. const allUsers = userService.getVisibleUsers(perspectiveUserId); if (allUsers.length === 0) { return "No users found."; } const userItems = allUsers.map((u) => ({ userId: u.userId, username: u.username, title: u.config.title, leadUserId: u.leadUserId, })); const relevantIds = getRelevantUserIds(userItems, perspectiveUserId); const filteredItems = userItems.filter((u) => relevantIds.has(u.userId)); // Build full children map for hidden-count computation const fullChildrenOf = new Map(); for (const u of userItems) { if (u.leadUserId != null) { pushToArrayMap(fullChildrenOf, u.leadUserId, u.userId); } } // Count all descendants of a user in the full tree const countAllDescendants = (userId) => { let count = 0; for (const childId of fullChildrenOf.get(userId) ?? []) { count += 1 + countAllDescendants(childId); } return count; }; // For each visible node, count hidden users in its direct hidden branches // (visible children handle their own hidden subtrees) const hiddenCounts = new Map(); for (const u of filteredItems) { let count = 0; for (const childId of fullChildrenOf.get(u.userId) ?? []) { if (!relevantIds.has(childId)) { count += 1 + countAllDescendants(childId); } } if (count > 0) { hiddenCounts.set(u.userId, count); } } const hierarchy = buildHierarchy(filteredItems); const flattened = flattenHierarchy(hierarchy, hiddenCounts); // Build userId → username lookup for lead display const userById = keyBy(userItems, (u) => u.userId); const isDebug = inputMode.isDebug(); const headers = ["Username", "Title", "Lead", "Status"]; if (isDebug) { headers.push("*Host"); } const rows = flattened.map((entry) => { if (entry.type === "hidden") { const indent = " ".repeat(entry.depth); const row = [`${indent}(+${entry.count} not shown)`, "", "", ""]; if (isDebug) row.push(""); return row; } const { node, depth } = entry; const indent = " ".repeat(depth); const displayName = `${indent}${node.username}`; const leadUsername = node.leadUserId ? userById.get(node.leadUserId)?.username || "(unknown)" : "(none)"; const row = [ displayName, node.title, leadUsername, userService.getUserStatus(node.userId), ]; if (isDebug) { row.push(userService.getUserHostDisplayNames(node.userId).join(", ") || ""); } return row; }); let output = table([headers, ...rows], { hsep: " | " }); if (isDebug) { output += "\n* Only visible in debug mode"; } return output; } const registrableCommand = { command: usersCmd, handleCommand, }; return { ...registrableCommand, }; } //# sourceMappingURL=userDisplayService.js.map