@agent-relay/dashboard-server 2.0.93 → 2.0.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/routes/models.d.ts.map +1 -1
- package/dist/routes/models.js +39 -3
- package/dist/routes/models.js.map +1 -1
- package/dist/services/index.d.ts +0 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +0 -1
- package/dist/services/index.js.map +1 -1
- package/dist/types/index.d.ts +0 -75
- package/dist/types/index.d.ts.map +1 -1
- package/dist/websocket/logs.d.ts +1 -25
- package/dist/websocket/logs.d.ts.map +1 -1
- package/dist/websocket/logs.js +0 -397
- package/dist/websocket/logs.js.map +1 -1
- package/out/404.html +1 -1
- package/out/_next/static/chunks/{3663-47290254b8f6f5dd.js → 3663-191a9aa9104061af.js} +1 -1
- package/out/_next/static/chunks/4201-d11188cf35739bff.js +1 -0
- package/out/_next/static/chunks/5787-f6fd7a3fbfe5eae6.js +73 -0
- package/out/_next/static/chunks/app/app/[[...slug]]/{page-c1376e695ba19e38.js → page-2b872f60e3d64014.js} +1 -1
- package/out/_next/static/chunks/app/{page-f2ebc7d0bc08e395.js → page-ca5511e5d65100a5.js} +1 -1
- package/out/about.html +1 -1
- package/out/about.txt +1 -1
- package/out/app/onboarding.html +1 -1
- package/out/app/onboarding.txt +1 -1
- package/out/app.html +1 -1
- package/out/app.txt +2 -2
- package/out/blog/go-to-bed-wake-up-to-a-finished-product.html +1 -1
- package/out/blog/go-to-bed-wake-up-to-a-finished-product.txt +1 -1
- package/out/blog/let-them-cook-multi-agent-orchestration.html +1 -1
- package/out/blog/let-them-cook-multi-agent-orchestration.txt +1 -1
- package/out/blog.html +1 -1
- package/out/blog.txt +1 -1
- package/out/careers.html +1 -1
- package/out/careers.txt +1 -1
- package/out/changelog.html +1 -1
- package/out/changelog.txt +1 -1
- package/out/cloud/link.html +1 -1
- package/out/cloud/link.txt +1 -1
- package/out/complete-profile.html +1 -1
- package/out/complete-profile.txt +1 -1
- package/out/connect-repos.html +1 -1
- package/out/connect-repos.txt +1 -1
- package/out/contact.html +1 -1
- package/out/contact.txt +1 -1
- package/out/dev/cli-tools.html +1 -1
- package/out/dev/cli-tools.txt +1 -1
- package/out/dev/log-viewer.html +1 -1
- package/out/dev/log-viewer.txt +1 -1
- package/out/docs.html +1 -1
- package/out/docs.txt +1 -1
- package/out/history.html +1 -1
- package/out/history.txt +2 -2
- package/out/index.html +1 -1
- package/out/index.txt +2 -2
- package/out/login.html +1 -1
- package/out/login.txt +1 -1
- package/out/metrics.html +1 -1
- package/out/metrics.txt +2 -2
- package/out/pricing.html +1 -1
- package/out/pricing.txt +1 -1
- package/out/privacy.html +1 -1
- package/out/privacy.txt +1 -1
- package/out/providers/setup/claude.html +1 -1
- package/out/providers/setup/claude.txt +1 -1
- package/out/providers/setup/codex.html +1 -1
- package/out/providers/setup/codex.txt +1 -1
- package/out/providers/setup/cursor.html +1 -1
- package/out/providers/setup/cursor.txt +1 -1
- package/out/providers.html +1 -1
- package/out/providers.txt +1 -1
- package/out/security.html +1 -1
- package/out/security.txt +1 -1
- package/out/signup.html +1 -1
- package/out/signup.txt +1 -1
- package/out/terms.html +1 -1
- package/out/terms.txt +1 -1
- package/package.json +6 -9
- package/dist/lib/attachment-storage.d.ts +0 -10
- package/dist/lib/attachment-storage.d.ts.map +0 -1
- package/dist/lib/attachment-storage.js +0 -53
- package/dist/lib/attachment-storage.js.map +0 -1
- package/dist/lib/broadcast.d.ts +0 -18
- package/dist/lib/broadcast.d.ts.map +0 -1
- package/dist/lib/broadcast.js +0 -118
- package/dist/lib/broadcast.js.map +0 -1
- package/dist/lib/channel-state.d.ts +0 -32
- package/dist/lib/channel-state.d.ts.map +0 -1
- package/dist/lib/channel-state.js +0 -146
- package/dist/lib/channel-state.js.map +0 -1
- package/dist/lib/cli-auth.d.ts +0 -40
- package/dist/lib/cli-auth.d.ts.map +0 -1
- package/dist/lib/cli-auth.js +0 -144
- package/dist/lib/cli-auth.js.map +0 -1
- package/dist/lib/data-assembly.d.ts +0 -136
- package/dist/lib/data-assembly.d.ts.map +0 -1
- package/dist/lib/data-assembly.js +0 -550
- package/dist/lib/data-assembly.js.map +0 -1
- package/dist/lib/server-state.d.ts +0 -115
- package/dist/lib/server-state.d.ts.map +0 -1
- package/dist/lib/server-state.js +0 -138
- package/dist/lib/server-state.js.map +0 -1
- package/dist/lib/websocket-runtime.d.ts +0 -17
- package/dist/lib/websocket-runtime.d.ts.map +0 -1
- package/dist/lib/websocket-runtime.js +0 -76
- package/dist/lib/websocket-runtime.js.map +0 -1
- package/dist/routes/auth.d.ts +0 -45
- package/dist/routes/auth.d.ts.map +0 -1
- package/dist/routes/auth.js +0 -261
- package/dist/routes/auth.js.map +0 -1
- package/dist/routes/channels-integrated.d.ts +0 -84
- package/dist/routes/channels-integrated.d.ts.map +0 -1
- package/dist/routes/channels-integrated.js +0 -644
- package/dist/routes/channels-integrated.js.map +0 -1
- package/dist/routes/decisions.d.ts +0 -31
- package/dist/routes/decisions.d.ts.map +0 -1
- package/dist/routes/decisions.js +0 -109
- package/dist/routes/decisions.js.map +0 -1
- package/dist/routes/fleet.d.ts +0 -24
- package/dist/routes/fleet.d.ts.map +0 -1
- package/dist/routes/fleet.js +0 -131
- package/dist/routes/fleet.js.map +0 -1
- package/dist/routes/history.d.ts +0 -13
- package/dist/routes/history.d.ts.map +0 -1
- package/dist/routes/history.js +0 -205
- package/dist/routes/history.js.map +0 -1
- package/dist/routes/messaging.d.ts +0 -34
- package/dist/routes/messaging.d.ts.map +0 -1
- package/dist/routes/messaging.js +0 -306
- package/dist/routes/messaging.js.map +0 -1
- package/dist/routes/settings.d.ts +0 -6
- package/dist/routes/settings.d.ts.map +0 -1
- package/dist/routes/settings.js +0 -119
- package/dist/routes/settings.js.map +0 -1
- package/dist/routes/spawn.d.ts +0 -75
- package/dist/routes/spawn.d.ts.map +0 -1
- package/dist/routes/spawn.js +0 -521
- package/dist/routes/spawn.js.map +0 -1
- package/dist/routes/system.d.ts +0 -21
- package/dist/routes/system.d.ts.map +0 -1
- package/dist/routes/system.js +0 -186
- package/dist/routes/system.js.map +0 -1
- package/dist/routes/tasks.d.ts +0 -27
- package/dist/routes/tasks.d.ts.map +0 -1
- package/dist/routes/tasks.js +0 -103
- package/dist/routes/tasks.js.map +0 -1
- package/dist/routes/ui.d.ts +0 -6
- package/dist/routes/ui.d.ts.map +0 -1
- package/dist/routes/ui.js +0 -114
- package/dist/routes/ui.js.map +0 -1
- package/dist/server.d.ts +0 -4
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -481
- package/dist/server.js.map +0 -1
- package/dist/services/broker-spawn-reader.d.ts +0 -40
- package/dist/services/broker-spawn-reader.d.ts.map +0 -1
- package/dist/services/broker-spawn-reader.js +0 -155
- package/dist/services/broker-spawn-reader.js.map +0 -1
- package/dist/services/user-bridge.d.ts +0 -155
- package/dist/services/user-bridge.d.ts.map +0 -1
- package/dist/services/user-bridge.js +0 -385
- package/dist/services/user-bridge.js.map +0 -1
- package/dist/websocket/bridge.d.ts +0 -12
- package/dist/websocket/bridge.d.ts.map +0 -1
- package/dist/websocket/bridge.js +0 -33
- package/dist/websocket/bridge.js.map +0 -1
- package/dist/websocket/main.d.ts +0 -15
- package/dist/websocket/main.d.ts.map +0 -1
- package/dist/websocket/main.js +0 -84
- package/dist/websocket/main.js.map +0 -1
- package/dist/websocket/presence.d.ts +0 -74
- package/dist/websocket/presence.d.ts.map +0 -1
- package/dist/websocket/presence.js +0 -330
- package/dist/websocket/presence.js.map +0 -1
- package/out/_next/static/chunks/270-8c0b8109123a0c5f.js +0 -73
- package/out/_next/static/chunks/5518-3b96a248632a79c0.js +0 -1
- /package/out/_next/static/{9CykW6n4dJn75_XoFVN_X → wgSCX8AQnjVl_Rnx1gUp5}/_buildManifest.js +0 -0
- /package/out/_next/static/{9CykW6n4dJn75_XoFVN_X → wgSCX8AQnjVl_Rnx1gUp5}/_ssgManifest.js +0 -0
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { type StorageAdapter, type StoredMessage } from '@agent-relay/storage/adapter';
|
|
2
|
-
import type { ThreadMetadata } from '../types/threading.js';
|
|
3
|
-
interface AgentStatus {
|
|
4
|
-
name: string;
|
|
5
|
-
role: string;
|
|
6
|
-
cli: string;
|
|
7
|
-
messageCount: number;
|
|
8
|
-
status?: string;
|
|
9
|
-
lastActive?: string;
|
|
10
|
-
lastSeen?: string;
|
|
11
|
-
needsAttention?: boolean;
|
|
12
|
-
isProcessing?: boolean;
|
|
13
|
-
processingStartedAt?: number;
|
|
14
|
-
isSpawned?: boolean;
|
|
15
|
-
team?: string;
|
|
16
|
-
avatarUrl?: string;
|
|
17
|
-
model?: string;
|
|
18
|
-
cwd?: string;
|
|
19
|
-
}
|
|
20
|
-
interface Attachment {
|
|
21
|
-
id: string;
|
|
22
|
-
filename: string;
|
|
23
|
-
mimeType: string;
|
|
24
|
-
size: number;
|
|
25
|
-
url: string;
|
|
26
|
-
filePath?: string;
|
|
27
|
-
width?: number;
|
|
28
|
-
height?: number;
|
|
29
|
-
data?: string;
|
|
30
|
-
}
|
|
31
|
-
interface Message {
|
|
32
|
-
from: string;
|
|
33
|
-
to: string;
|
|
34
|
-
content: string;
|
|
35
|
-
timestamp: string;
|
|
36
|
-
id: string;
|
|
37
|
-
thread?: string;
|
|
38
|
-
isBroadcast?: boolean;
|
|
39
|
-
status?: string;
|
|
40
|
-
attachments?: Attachment[];
|
|
41
|
-
channel?: string;
|
|
42
|
-
replyCount?: number;
|
|
43
|
-
threadSummary?: ThreadMetadata;
|
|
44
|
-
}
|
|
45
|
-
interface SessionInfo {
|
|
46
|
-
id: string;
|
|
47
|
-
agentName: string;
|
|
48
|
-
cli?: string;
|
|
49
|
-
startedAt: string;
|
|
50
|
-
endedAt?: string;
|
|
51
|
-
duration?: string;
|
|
52
|
-
messageCount: number;
|
|
53
|
-
summary?: string;
|
|
54
|
-
isActive: boolean;
|
|
55
|
-
closedBy?: 'agent' | 'disconnect' | 'error';
|
|
56
|
-
}
|
|
57
|
-
interface AgentSummary {
|
|
58
|
-
agentName: string;
|
|
59
|
-
lastUpdated: string;
|
|
60
|
-
currentTask?: string;
|
|
61
|
-
completedTasks?: string[];
|
|
62
|
-
context?: string;
|
|
63
|
-
}
|
|
64
|
-
interface PresenceInfo {
|
|
65
|
-
avatarUrl?: string;
|
|
66
|
-
lastSeen: string;
|
|
67
|
-
}
|
|
68
|
-
interface PresenceStateLike {
|
|
69
|
-
info: PresenceInfo;
|
|
70
|
-
}
|
|
71
|
-
interface ActiveWorkerLike {
|
|
72
|
-
name: string;
|
|
73
|
-
team?: string;
|
|
74
|
-
cli?: string;
|
|
75
|
-
cwd?: string;
|
|
76
|
-
}
|
|
77
|
-
interface SpawnReaderLike {
|
|
78
|
-
getActiveWorkers: () => ActiveWorkerLike[];
|
|
79
|
-
}
|
|
80
|
-
interface BridgeLead {
|
|
81
|
-
name: string;
|
|
82
|
-
connected?: boolean;
|
|
83
|
-
}
|
|
84
|
-
interface BridgeAgent {
|
|
85
|
-
name: string;
|
|
86
|
-
status: string;
|
|
87
|
-
cli?: string;
|
|
88
|
-
lastSeen?: string;
|
|
89
|
-
}
|
|
90
|
-
interface BridgeProject {
|
|
91
|
-
path?: string;
|
|
92
|
-
lead?: BridgeLead;
|
|
93
|
-
agents?: BridgeAgent[];
|
|
94
|
-
}
|
|
95
|
-
interface BridgeData {
|
|
96
|
-
projects: BridgeProject[];
|
|
97
|
-
messages: unknown[];
|
|
98
|
-
connected: boolean;
|
|
99
|
-
}
|
|
100
|
-
export interface DataAssemblyDeps {
|
|
101
|
-
dataDir: string;
|
|
102
|
-
teamDir: string;
|
|
103
|
-
projectRoot?: string;
|
|
104
|
-
defaultWorkspaceId?: string;
|
|
105
|
-
storage?: StorageAdapter;
|
|
106
|
-
spawnReader?: SpawnReaderLike;
|
|
107
|
-
onlineUsers: Map<string, PresenceStateLike>;
|
|
108
|
-
agentCwdMap: Map<string, string>;
|
|
109
|
-
debug?: (message: string) => void;
|
|
110
|
-
}
|
|
111
|
-
export interface DataAssembly {
|
|
112
|
-
getAllData: () => Promise<{
|
|
113
|
-
agents: Array<AgentStatus & {
|
|
114
|
-
isHuman: false;
|
|
115
|
-
}>;
|
|
116
|
-
users: Array<AgentStatus & {
|
|
117
|
-
isHuman: true;
|
|
118
|
-
}>;
|
|
119
|
-
messages: Message[];
|
|
120
|
-
activity: Message[];
|
|
121
|
-
sessions: SessionInfo[];
|
|
122
|
-
summaries: AgentSummary[];
|
|
123
|
-
} | null>;
|
|
124
|
-
getBridgeData: () => Promise<BridgeData>;
|
|
125
|
-
isInternalAgent: (name: string) => boolean;
|
|
126
|
-
remapAgentName: (name: string) => string;
|
|
127
|
-
buildThreadSummaryMap: (rows: StoredMessage[]) => Map<string, ThreadMetadata>;
|
|
128
|
-
formatDuration: (startMs: number, endMs?: number) => string;
|
|
129
|
-
}
|
|
130
|
-
export declare const isInternalAgent: (name: string) => boolean;
|
|
131
|
-
export declare const remapAgentName: (name: string) => string;
|
|
132
|
-
export declare const buildThreadSummaryMap: (rows: StoredMessage[]) => Map<string, ThreadMetadata>;
|
|
133
|
-
export declare const formatDuration: (startMs: number, endMs?: number) => string;
|
|
134
|
-
export declare function createDataAssembly(deps: DataAssemblyDeps): DataAssembly;
|
|
135
|
-
export {};
|
|
136
|
-
//# sourceMappingURL=data-assembly.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-assembly.d.ts","sourceRoot":"","sources":["../../src/lib/data-assembly.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,550 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import crypto from 'crypto';
|
|
4
|
-
import { loadTeamsConfig } from '@agent-relay/config';
|
|
5
|
-
import { fetchCloudNeedsAttention, parseNeedsAttentionAgents } from '../services/needs-attention.js';
|
|
6
|
-
// Helper to check if an agent name is internal/system (should be hidden from UI).
|
|
7
|
-
// Convention: agent names starting with __ are internal (e.g., __spawner__, __DashboardBridge__).
|
|
8
|
-
export const isInternalAgent = (name) => {
|
|
9
|
-
if (name === '__cli_sender__')
|
|
10
|
-
return false;
|
|
11
|
-
return name.startsWith('__');
|
|
12
|
-
};
|
|
13
|
-
// Display-name remapping for CLI sender (used across message and history endpoints).
|
|
14
|
-
export const remapAgentName = (name) => {
|
|
15
|
-
if (name === '__cli_sender__')
|
|
16
|
-
return 'CLI';
|
|
17
|
-
return name;
|
|
18
|
-
};
|
|
19
|
-
export const buildThreadSummaryMap = (rows) => {
|
|
20
|
-
const summaries = new Map();
|
|
21
|
-
for (const row of rows) {
|
|
22
|
-
if (!row.thread) {
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
const threadId = row.thread;
|
|
26
|
-
const existing = summaries.get(threadId);
|
|
27
|
-
const participants = existing ? new Set(existing.participants) : new Set();
|
|
28
|
-
participants.add(row.from);
|
|
29
|
-
const isNewer = !existing || row.ts >= existing.lastReplyAt;
|
|
30
|
-
summaries.set(threadId, {
|
|
31
|
-
threadId,
|
|
32
|
-
replyCount: existing ? existing.replyCount + 1 : 1,
|
|
33
|
-
participants: Array.from(participants),
|
|
34
|
-
lastReplyAt: isNewer ? row.ts : existing.lastReplyAt,
|
|
35
|
-
lastReplyPreview: isNewer ? row.body : existing.lastReplyPreview,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
return summaries;
|
|
39
|
-
};
|
|
40
|
-
const mapStoredMessages = (rows, threadSummaries) => rows
|
|
41
|
-
// Filter out messages from/to internal system agents (e.g., __spawner__).
|
|
42
|
-
.filter((row) => !isInternalAgent(row.from) && !isInternalAgent(row.to))
|
|
43
|
-
// Filter out channel messages - these are shown in the channels view, not the agent messages view.
|
|
44
|
-
.filter((row) => {
|
|
45
|
-
if (row.data && typeof row.data === 'object' && '_isChannelMessage' in row.data) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
return true;
|
|
49
|
-
})
|
|
50
|
-
.map((row) => {
|
|
51
|
-
const summaryFromReplies = threadSummaries?.get(row.id);
|
|
52
|
-
const fallbackSummary = (!summaryFromReplies && row.replyCount && row.replyCount > 0)
|
|
53
|
-
? {
|
|
54
|
-
threadId: row.id,
|
|
55
|
-
replyCount: row.replyCount,
|
|
56
|
-
participants: Array.from(new Set([row.from, row.to])),
|
|
57
|
-
lastReplyAt: row.ts,
|
|
58
|
-
}
|
|
59
|
-
: undefined;
|
|
60
|
-
const threadSummary = summaryFromReplies ?? fallbackSummary;
|
|
61
|
-
let attachments;
|
|
62
|
-
let channel;
|
|
63
|
-
let effectiveFrom = row.from;
|
|
64
|
-
let effectiveTo = row.to;
|
|
65
|
-
if (row.data && typeof row.data === 'object') {
|
|
66
|
-
if ('attachments' in row.data) {
|
|
67
|
-
attachments = row.data.attachments;
|
|
68
|
-
}
|
|
69
|
-
if ('channel' in row.data) {
|
|
70
|
-
channel = row.data.channel;
|
|
71
|
-
}
|
|
72
|
-
// For dashboard messages sent via Dashboard, use the actual sender name.
|
|
73
|
-
if ('senderName' in row.data && row.from === 'Dashboard') {
|
|
74
|
-
effectiveFrom = row.data.senderName;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
effectiveFrom = remapAgentName(effectiveFrom);
|
|
78
|
-
effectiveTo = remapAgentName(effectiveTo);
|
|
79
|
-
return {
|
|
80
|
-
from: effectiveFrom,
|
|
81
|
-
to: effectiveTo,
|
|
82
|
-
content: row.body,
|
|
83
|
-
timestamp: new Date(row.ts).toISOString(),
|
|
84
|
-
id: row.id,
|
|
85
|
-
thread: row.thread,
|
|
86
|
-
isBroadcast: row.is_broadcast,
|
|
87
|
-
replyCount: threadSummary?.replyCount ?? row.replyCount,
|
|
88
|
-
threadSummary,
|
|
89
|
-
status: row.status,
|
|
90
|
-
attachments,
|
|
91
|
-
channel,
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
export const formatDuration = (startMs, endMs) => {
|
|
95
|
-
const end = endMs ?? Date.now();
|
|
96
|
-
const durationMs = end - startMs;
|
|
97
|
-
const minutes = Math.floor(durationMs / 60000);
|
|
98
|
-
const hours = Math.floor(minutes / 60);
|
|
99
|
-
if (hours > 0) {
|
|
100
|
-
return `${hours}h ${minutes % 60}m`;
|
|
101
|
-
}
|
|
102
|
-
return `${minutes}m`;
|
|
103
|
-
};
|
|
104
|
-
export function createDataAssembly(deps) {
|
|
105
|
-
const { dataDir, teamDir, projectRoot, defaultWorkspaceId, storage, spawnReader, onlineUsers, agentCwdMap, debug, } = deps;
|
|
106
|
-
const getTeamData = () => {
|
|
107
|
-
// Try team.json first (file-based team mode).
|
|
108
|
-
const teamPath = path.join(teamDir, 'team.json');
|
|
109
|
-
if (fs.existsSync(teamPath)) {
|
|
110
|
-
try {
|
|
111
|
-
return JSON.parse(fs.readFileSync(teamPath, 'utf-8'));
|
|
112
|
-
}
|
|
113
|
-
catch (e) {
|
|
114
|
-
console.error('Failed to read team.json', e);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// Fall back to agents.json (daemon mode - live connected agents).
|
|
118
|
-
const agentsPath = path.join(teamDir, 'agents.json');
|
|
119
|
-
if (fs.existsSync(agentsPath)) {
|
|
120
|
-
try {
|
|
121
|
-
const data = JSON.parse(fs.readFileSync(agentsPath, 'utf-8'));
|
|
122
|
-
// Convert agents.json format to team.json format.
|
|
123
|
-
return {
|
|
124
|
-
agents: data.agents.map((a) => ({
|
|
125
|
-
name: a.name,
|
|
126
|
-
role: 'Agent',
|
|
127
|
-
cli: a.cli ?? 'Unknown',
|
|
128
|
-
lastSeen: a.lastSeen ?? a.connectedAt,
|
|
129
|
-
lastActive: a.lastSeen ?? a.connectedAt,
|
|
130
|
-
team: a.team,
|
|
131
|
-
})),
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
catch (e) {
|
|
135
|
-
console.error('Failed to read agents.json', e);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return null;
|
|
139
|
-
};
|
|
140
|
-
const parseInbox = (agentName) => {
|
|
141
|
-
const inboxPath = path.join(dataDir, agentName, 'inbox.md');
|
|
142
|
-
if (!fs.existsSync(inboxPath))
|
|
143
|
-
return [];
|
|
144
|
-
try {
|
|
145
|
-
const content = fs.readFileSync(inboxPath, 'utf-8');
|
|
146
|
-
const messages = [];
|
|
147
|
-
// Split by "## Message from ".
|
|
148
|
-
const parts = content.split('## Message from ');
|
|
149
|
-
parts.forEach((part, index) => {
|
|
150
|
-
if (!part.trim())
|
|
151
|
-
return;
|
|
152
|
-
const firstLineEnd = part.indexOf('\n');
|
|
153
|
-
if (firstLineEnd === -1)
|
|
154
|
-
return;
|
|
155
|
-
const header = part.substring(0, firstLineEnd).trim();
|
|
156
|
-
const body = part.substring(firstLineEnd).trim();
|
|
157
|
-
let sender = header;
|
|
158
|
-
let timestamp = new Date().toISOString();
|
|
159
|
-
if (header.includes('|')) {
|
|
160
|
-
const split = header.split('|');
|
|
161
|
-
sender = split[0].trim();
|
|
162
|
-
timestamp = split.slice(1).join('|').trim();
|
|
163
|
-
}
|
|
164
|
-
messages.push({
|
|
165
|
-
from: sender,
|
|
166
|
-
to: agentName,
|
|
167
|
-
content: body,
|
|
168
|
-
timestamp,
|
|
169
|
-
id: `${agentName}-${index}-${Date.now()}`,
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
return messages;
|
|
173
|
-
}
|
|
174
|
-
catch (e) {
|
|
175
|
-
console.error(`Failed to read inbox for ${agentName}`, e);
|
|
176
|
-
return [];
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
const getMessages = async (agents) => {
|
|
180
|
-
// For local mode: use storage (SQLite) first - faster and avoids daemon query timeouts.
|
|
181
|
-
if (storage) {
|
|
182
|
-
const rows = await storage.getMessages({ limit: 100, order: 'desc' });
|
|
183
|
-
const threadSummaries = buildThreadSummaryMap(rows);
|
|
184
|
-
// Dashboard expects oldest first.
|
|
185
|
-
return mapStoredMessages(rows, threadSummaries).reverse();
|
|
186
|
-
}
|
|
187
|
-
// Final fallback to file-based inbox parsing.
|
|
188
|
-
let allMessages = [];
|
|
189
|
-
agents.forEach((a) => {
|
|
190
|
-
const msgs = parseInbox(a.name);
|
|
191
|
-
allMessages = [...allMessages, ...msgs];
|
|
192
|
-
});
|
|
193
|
-
return allMessages;
|
|
194
|
-
};
|
|
195
|
-
const getRecentSessions = async () => {
|
|
196
|
-
if (storage && typeof storage.getRecentSessions === 'function') {
|
|
197
|
-
const sessions = await storage.getRecentSessions(20);
|
|
198
|
-
return sessions.map((s) => ({
|
|
199
|
-
id: s.id,
|
|
200
|
-
agentName: s.agentName,
|
|
201
|
-
cli: s.cli,
|
|
202
|
-
startedAt: new Date(s.startedAt).toISOString(),
|
|
203
|
-
endedAt: s.endedAt ? new Date(s.endedAt).toISOString() : undefined,
|
|
204
|
-
duration: formatDuration(s.startedAt, s.endedAt),
|
|
205
|
-
messageCount: s.messageCount,
|
|
206
|
-
summary: s.summary,
|
|
207
|
-
isActive: !s.endedAt,
|
|
208
|
-
closedBy: s.closedBy,
|
|
209
|
-
}));
|
|
210
|
-
}
|
|
211
|
-
return [];
|
|
212
|
-
};
|
|
213
|
-
const getAgentSummaries = async () => {
|
|
214
|
-
if (storage && typeof storage.getAllAgentSummaries === 'function') {
|
|
215
|
-
const summaries = await storage.getAllAgentSummaries();
|
|
216
|
-
return summaries.map((s) => ({
|
|
217
|
-
agentName: s.agentName,
|
|
218
|
-
lastUpdated: new Date(s.lastUpdated).toISOString(),
|
|
219
|
-
currentTask: s.currentTask,
|
|
220
|
-
completedTasks: s.completedTasks,
|
|
221
|
-
context: s.context,
|
|
222
|
-
}));
|
|
223
|
-
}
|
|
224
|
-
return [];
|
|
225
|
-
};
|
|
226
|
-
const getAllData = async () => {
|
|
227
|
-
const team = getTeamData();
|
|
228
|
-
if (!team)
|
|
229
|
-
return null;
|
|
230
|
-
const agentsMap = new Map();
|
|
231
|
-
const allMessages = await getMessages(team.agents);
|
|
232
|
-
// Initialize agents from config.
|
|
233
|
-
team.agents.forEach((a) => {
|
|
234
|
-
agentsMap.set(a.name, {
|
|
235
|
-
name: a.name,
|
|
236
|
-
role: a.role,
|
|
237
|
-
cli: a.cli ?? 'Unknown',
|
|
238
|
-
messageCount: 0,
|
|
239
|
-
status: 'Idle',
|
|
240
|
-
lastSeen: a.lastSeen,
|
|
241
|
-
lastActive: a.lastActive,
|
|
242
|
-
needsAttention: false,
|
|
243
|
-
team: a.team,
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
// Inject online human users (connected via dashboard WebSocket) into agentsMap.
|
|
247
|
-
for (const [username, state] of onlineUsers) {
|
|
248
|
-
const existing = agentsMap.get(username);
|
|
249
|
-
if (existing) {
|
|
250
|
-
existing.cli = 'dashboard';
|
|
251
|
-
existing.status = 'online';
|
|
252
|
-
existing.avatarUrl = state.info.avatarUrl || existing.avatarUrl;
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
agentsMap.set(username, {
|
|
256
|
-
name: username,
|
|
257
|
-
role: 'User',
|
|
258
|
-
cli: 'dashboard',
|
|
259
|
-
messageCount: 0,
|
|
260
|
-
status: 'online',
|
|
261
|
-
lastSeen: state.info.lastSeen,
|
|
262
|
-
lastActive: state.info.lastSeen,
|
|
263
|
-
needsAttention: false,
|
|
264
|
-
avatarUrl: state.info.avatarUrl,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
// Inject remote users (connected via cloud dashboard) into agentsMap.
|
|
269
|
-
const remoteUsersPath = path.join(teamDir, 'remote-users.json');
|
|
270
|
-
if (fs.existsSync(remoteUsersPath)) {
|
|
271
|
-
try {
|
|
272
|
-
const remoteData = JSON.parse(fs.readFileSync(remoteUsersPath, 'utf-8'));
|
|
273
|
-
// Only include if file is fresh (within 60 seconds).
|
|
274
|
-
if (remoteData.updatedAt && Date.now() - remoteData.updatedAt <= 60 * 1000) {
|
|
275
|
-
for (const user of remoteData.users || []) {
|
|
276
|
-
// Don't override local users.
|
|
277
|
-
if (onlineUsers.has(user.name))
|
|
278
|
-
continue;
|
|
279
|
-
const existing = agentsMap.get(user.name);
|
|
280
|
-
if (existing) {
|
|
281
|
-
existing.cli = 'dashboard';
|
|
282
|
-
existing.status = 'online';
|
|
283
|
-
if (user.avatarUrl)
|
|
284
|
-
existing.avatarUrl = user.avatarUrl;
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
// Use stable timestamps from the user/file data, not new Date().
|
|
288
|
-
const stableTimestamp = user.lastSeen || user.connectedAt || new Date(remoteData.updatedAt).toISOString();
|
|
289
|
-
agentsMap.set(user.name, {
|
|
290
|
-
name: user.name,
|
|
291
|
-
role: 'User',
|
|
292
|
-
cli: 'dashboard',
|
|
293
|
-
messageCount: 0,
|
|
294
|
-
status: 'online',
|
|
295
|
-
lastSeen: stableTimestamp,
|
|
296
|
-
lastActive: stableTimestamp,
|
|
297
|
-
needsAttention: false,
|
|
298
|
-
avatarUrl: user.avatarUrl,
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
catch {
|
|
305
|
-
// Ignore parse errors for remote users file.
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
// Update inbox counts if fallback mode; if storage, count messages addressed to agent.
|
|
309
|
-
if (storage) {
|
|
310
|
-
for (const msg of allMessages) {
|
|
311
|
-
const agent = agentsMap.get(msg.to);
|
|
312
|
-
if (agent) {
|
|
313
|
-
agent.messageCount = (agent.messageCount ?? 0) + 1;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
// Sort by timestamp.
|
|
319
|
-
allMessages.sort((a, b) => {
|
|
320
|
-
return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime();
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
// Derive status from messages sent BY agents.
|
|
324
|
-
allMessages.forEach((m) => {
|
|
325
|
-
const agent = agentsMap.get(m.from);
|
|
326
|
-
if (agent) {
|
|
327
|
-
agent.lastActive = m.timestamp;
|
|
328
|
-
// Don't overwrite lastSeen - it comes from registry (heartbeat/connection tracking).
|
|
329
|
-
if (m.content.startsWith('STATUS:')) {
|
|
330
|
-
agent.status = m.content.substring(7).trim();
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
// Needs-attention ownership moved to cloud; dashboard now consumes pass-through data.
|
|
335
|
-
try {
|
|
336
|
-
const response = await fetchCloudNeedsAttention({
|
|
337
|
-
request: { workspaceId: defaultWorkspaceId },
|
|
338
|
-
});
|
|
339
|
-
if (response.ok) {
|
|
340
|
-
const payload = await response.json();
|
|
341
|
-
const needsAttentionAgents = parseNeedsAttentionAgents(payload);
|
|
342
|
-
needsAttentionAgents.forEach((agentName) => {
|
|
343
|
-
const agent = agentsMap.get(agentName);
|
|
344
|
-
if (agent) {
|
|
345
|
-
agent.needsAttention = true;
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
catch (err) {
|
|
351
|
-
debug?.(`[dashboard] cloud needs-attention proxy failed: ${err.message}`);
|
|
352
|
-
}
|
|
353
|
-
// Read processing state from daemon.
|
|
354
|
-
const processingStatePath = path.join(teamDir, 'processing-state.json');
|
|
355
|
-
if (fs.existsSync(processingStatePath)) {
|
|
356
|
-
try {
|
|
357
|
-
const processingData = JSON.parse(fs.readFileSync(processingStatePath, 'utf-8'));
|
|
358
|
-
const processingAgents = processingData.processingAgents || {};
|
|
359
|
-
for (const [agentName, state] of Object.entries(processingAgents)) {
|
|
360
|
-
const agent = agentsMap.get(agentName);
|
|
361
|
-
if (agent && state && typeof state === 'object') {
|
|
362
|
-
agent.isProcessing = true;
|
|
363
|
-
agent.processingStartedAt = state.startedAt;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
catch {
|
|
368
|
-
// Ignore errors reading processing state - it's optional.
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
// Mark spawned agents with isSpawned flag, team, model, and cwd.
|
|
372
|
-
if (spawnReader) {
|
|
373
|
-
const activeWorkers = spawnReader.getActiveWorkers();
|
|
374
|
-
for (const worker of activeWorkers) {
|
|
375
|
-
const agent = agentsMap.get(worker.name);
|
|
376
|
-
if (agent) {
|
|
377
|
-
agent.isSpawned = true;
|
|
378
|
-
if (worker.team) {
|
|
379
|
-
agent.team = worker.team;
|
|
380
|
-
}
|
|
381
|
-
const workerCwd = agentCwdMap.get(worker.name) || worker.cwd;
|
|
382
|
-
if (workerCwd) {
|
|
383
|
-
agent.cwd = workerCwd;
|
|
384
|
-
}
|
|
385
|
-
if (worker.cli) {
|
|
386
|
-
const modelMatch = worker.cli.match(/--model[=\s]+(\S+)/);
|
|
387
|
-
if (modelMatch) {
|
|
388
|
-
agent.model = modelMatch[1];
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
// Inject cwd from agentCwdMap for agents not in spawner's active workers.
|
|
395
|
-
for (const [name, cwd] of agentCwdMap) {
|
|
396
|
-
const agent = agentsMap.get(name);
|
|
397
|
-
if (agent && !agent.cwd) {
|
|
398
|
-
agent.cwd = cwd;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
// Also check workers.json for externally-spawned workers.
|
|
402
|
-
const workersJsonPath = path.join(teamDir, 'workers.json');
|
|
403
|
-
if (fs.existsSync(workersJsonPath)) {
|
|
404
|
-
try {
|
|
405
|
-
const workersData = JSON.parse(fs.readFileSync(workersJsonPath, 'utf-8'));
|
|
406
|
-
for (const worker of workersData.workers || []) {
|
|
407
|
-
const agent = agentsMap.get(worker.name);
|
|
408
|
-
if (agent && !agent.isSpawned && worker.logFile && fs.existsSync(worker.logFile)) {
|
|
409
|
-
agent.isSpawned = true;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
catch {
|
|
414
|
-
// Ignore errors reading workers.json.
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
// Mark relay-protocol spawned agents by log-file presence.
|
|
418
|
-
if (spawnReader) {
|
|
419
|
-
for (const [name, agent] of agentsMap) {
|
|
420
|
-
if (agent.isSpawned)
|
|
421
|
-
continue;
|
|
422
|
-
if (onlineUsers.has(name) || name === 'Dashboard')
|
|
423
|
-
continue;
|
|
424
|
-
const logPath = path.join(teamDir, `${name}.log`);
|
|
425
|
-
if (fs.existsSync(logPath)) {
|
|
426
|
-
agent.isSpawned = true;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
// Set team from teams.json for agents that don't have a team yet.
|
|
431
|
-
const teamsConfig = loadTeamsConfig(projectRoot || dataDir);
|
|
432
|
-
if (teamsConfig) {
|
|
433
|
-
for (const teamAgent of teamsConfig.agents) {
|
|
434
|
-
const agent = agentsMap.get(teamAgent.name);
|
|
435
|
-
if (agent && !agent.team) {
|
|
436
|
-
agent.team = teamsConfig.team;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
// Fetch sessions and summaries in parallel.
|
|
441
|
-
const [sessions, summaries] = await Promise.all([
|
|
442
|
-
getRecentSessions(),
|
|
443
|
-
getAgentSummaries(),
|
|
444
|
-
]);
|
|
445
|
-
// Filter and separate agents from human users.
|
|
446
|
-
const now = Date.now();
|
|
447
|
-
const OFFLINE_THRESHOLD_MS = 30 * 1000;
|
|
448
|
-
const validEntries = Array.from(agentsMap.values())
|
|
449
|
-
.filter((agent) => {
|
|
450
|
-
if (agent.name === 'Dashboard')
|
|
451
|
-
return false;
|
|
452
|
-
if (agent.name.startsWith('__'))
|
|
453
|
-
return false;
|
|
454
|
-
if (agent.name === 'Dashboard')
|
|
455
|
-
return false;
|
|
456
|
-
if (!agent.cli || agent.cli === 'Unknown')
|
|
457
|
-
return false;
|
|
458
|
-
if (!agent.lastSeen)
|
|
459
|
-
return false;
|
|
460
|
-
const lastSeenTime = new Date(agent.lastSeen).getTime();
|
|
461
|
-
if (now - lastSeenTime > OFFLINE_THRESHOLD_MS)
|
|
462
|
-
return false;
|
|
463
|
-
return true;
|
|
464
|
-
});
|
|
465
|
-
const filteredAgents = validEntries
|
|
466
|
-
.filter((agent) => agent.cli !== 'dashboard')
|
|
467
|
-
.map((agent) => ({
|
|
468
|
-
...agent,
|
|
469
|
-
isHuman: false,
|
|
470
|
-
}))
|
|
471
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
472
|
-
const humanUsers = validEntries
|
|
473
|
-
.filter((agent) => agent.cli === 'dashboard')
|
|
474
|
-
.map((agent) => ({
|
|
475
|
-
...agent,
|
|
476
|
-
isHuman: true,
|
|
477
|
-
}))
|
|
478
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
479
|
-
return {
|
|
480
|
-
agents: filteredAgents,
|
|
481
|
-
users: humanUsers,
|
|
482
|
-
messages: allMessages,
|
|
483
|
-
activity: allMessages,
|
|
484
|
-
sessions,
|
|
485
|
-
summaries,
|
|
486
|
-
};
|
|
487
|
-
};
|
|
488
|
-
const getBridgeData = async () => {
|
|
489
|
-
const bridgeStatePath = path.join(dataDir, 'bridge-state.json');
|
|
490
|
-
if (fs.existsSync(bridgeStatePath)) {
|
|
491
|
-
try {
|
|
492
|
-
const bridgeState = JSON.parse(fs.readFileSync(bridgeStatePath, 'utf-8'));
|
|
493
|
-
// Enrich each project with actual agent data from their team directories.
|
|
494
|
-
if (bridgeState.projects && Array.isArray(bridgeState.projects)) {
|
|
495
|
-
for (const project of bridgeState.projects) {
|
|
496
|
-
if (project.path) {
|
|
497
|
-
const projectHash = crypto.createHash('sha256').update(project.path).digest('hex').slice(0, 12);
|
|
498
|
-
const projectDataDir = path.join(path.dirname(dataDir), projectHash);
|
|
499
|
-
const projectTeamDir = path.join(projectDataDir, 'team');
|
|
500
|
-
const agentsPath = path.join(projectTeamDir, 'agents.json');
|
|
501
|
-
if (fs.existsSync(agentsPath)) {
|
|
502
|
-
try {
|
|
503
|
-
const agentsData = JSON.parse(fs.readFileSync(agentsPath, 'utf-8'));
|
|
504
|
-
if (agentsData.agents && Array.isArray(agentsData.agents)) {
|
|
505
|
-
// Filter to only show online agents (seen within 30 seconds - aligns with heartbeat timeout).
|
|
506
|
-
const thirtySecondsAgo = Date.now() - 30 * 1000;
|
|
507
|
-
project.agents = agentsData.agents
|
|
508
|
-
.filter((a) => {
|
|
509
|
-
if (!a.lastSeen)
|
|
510
|
-
return false;
|
|
511
|
-
return new Date(a.lastSeen).getTime() > thirtySecondsAgo;
|
|
512
|
-
})
|
|
513
|
-
.map((a) => ({
|
|
514
|
-
name: a.name,
|
|
515
|
-
status: 'active',
|
|
516
|
-
cli: a.cli,
|
|
517
|
-
lastSeen: a.lastSeen,
|
|
518
|
-
}));
|
|
519
|
-
// Update lead status based on actual agents.
|
|
520
|
-
if (project.lead) {
|
|
521
|
-
const leadAgent = project.agents.find((a) => a.name.toLowerCase() === project.lead.name.toLowerCase());
|
|
522
|
-
project.lead.connected = !!leadAgent;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
catch (e) {
|
|
527
|
-
console.error(`Failed to read agents for ${project.path}:`, e);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
return bridgeState;
|
|
534
|
-
}
|
|
535
|
-
catch {
|
|
536
|
-
return { projects: [], messages: [], connected: false };
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
return { projects: [], messages: [], connected: false };
|
|
540
|
-
};
|
|
541
|
-
return {
|
|
542
|
-
getAllData,
|
|
543
|
-
getBridgeData,
|
|
544
|
-
isInternalAgent,
|
|
545
|
-
remapAgentName,
|
|
546
|
-
buildThreadSummaryMap,
|
|
547
|
-
formatDuration,
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
//# sourceMappingURL=data-assembly.js.map
|