@agent-relay/dashboard-server 2.0.44 → 2.0.46
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/server.js +110 -19
- package/dist/server.js.map +1 -1
- package/out/404.html +1 -1
- package/out/_next/static/chunks/873-604131545363afd2.js +1 -0
- 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.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/docs.html +1 -1
- package/out/docs.txt +1 -1
- package/out/history.html +1 -1
- package/out/history.txt +1 -1
- 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 +1 -1
- 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 +10 -10
- package/out/_next/static/chunks/873-ca999501cec1e494.js +0 -1
- /package/out/_next/static/{Dp_8BslvsUb8aodHOf3qb → kt1pBkOoE9ZJk69H9H3_X}/_buildManifest.js +0 -0
- /package/out/_next/static/{Dp_8BslvsUb8aodHOf3qb → kt1pBkOoE9ZJk69H9H3_X}/_ssgManifest.js +0 -0
package/dist/server.js
CHANGED
|
@@ -831,6 +831,8 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
831
831
|
cli: 'dashboard',
|
|
832
832
|
reconnect: true,
|
|
833
833
|
maxReconnectAttempts: 5,
|
|
834
|
+
// Dashboard is a reserved name, so we need to mark it as a system component
|
|
835
|
+
_isSystemComponent: senderName === 'Dashboard',
|
|
834
836
|
});
|
|
835
837
|
client.onError = (err) => {
|
|
836
838
|
console.error(`[dashboard] Relay client error for ${senderName}:`, err.message);
|
|
@@ -872,6 +874,52 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
872
874
|
timestamp: new Date().toISOString(),
|
|
873
875
|
});
|
|
874
876
|
};
|
|
877
|
+
// Set up direct message handler to forward messages to presence WebSocket
|
|
878
|
+
// This enables agents to send replies that appear in the dashboard UI
|
|
879
|
+
client.onMessage = (from, payload, messageId) => {
|
|
880
|
+
const body = typeof payload === 'object' && payload !== null && 'body' in payload
|
|
881
|
+
? payload.body
|
|
882
|
+
: String(payload);
|
|
883
|
+
console.log(`[dashboard] *** DIRECT MESSAGE RECEIVED *** for ${senderName}: ${from} -> ${senderName}: ${body.substring(0, 50)}...`);
|
|
884
|
+
// Look up sender's info from presence (if they're an online user)
|
|
885
|
+
const senderPresence = onlineUsers.get(from);
|
|
886
|
+
const fromAvatarUrl = senderPresence?.info.avatarUrl;
|
|
887
|
+
// Determine entity type: user if they have presence state, agent otherwise
|
|
888
|
+
const fromEntityType = senderPresence ? 'user' : 'agent';
|
|
889
|
+
const timestamp = new Date().toISOString();
|
|
890
|
+
// Persist the message to storage so it survives page refresh
|
|
891
|
+
if (storage) {
|
|
892
|
+
storage.saveMessage({
|
|
893
|
+
id: messageId || `dm-${crypto.randomUUID()}`,
|
|
894
|
+
ts: Date.now(),
|
|
895
|
+
from,
|
|
896
|
+
to: senderName,
|
|
897
|
+
topic: undefined,
|
|
898
|
+
kind: 'message',
|
|
899
|
+
body,
|
|
900
|
+
data: {
|
|
901
|
+
fromAvatarUrl,
|
|
902
|
+
fromEntityType,
|
|
903
|
+
},
|
|
904
|
+
status: 'unread',
|
|
905
|
+
is_urgent: false,
|
|
906
|
+
is_broadcast: false,
|
|
907
|
+
}).catch((err) => {
|
|
908
|
+
console.error('[dashboard] Failed to persist direct message', err);
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
// Broadcast to presence WebSocket clients so cloud/dashboard can display the message
|
|
912
|
+
broadcastDirectMessage({
|
|
913
|
+
type: 'direct_message',
|
|
914
|
+
targetUser: senderName,
|
|
915
|
+
from,
|
|
916
|
+
fromAvatarUrl,
|
|
917
|
+
fromEntityType,
|
|
918
|
+
body,
|
|
919
|
+
messageId,
|
|
920
|
+
timestamp,
|
|
921
|
+
});
|
|
922
|
+
};
|
|
875
923
|
try {
|
|
876
924
|
await client.connect();
|
|
877
925
|
relayClients.set(senderName, client);
|
|
@@ -892,8 +940,8 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
892
940
|
return connectionPromise;
|
|
893
941
|
};
|
|
894
942
|
// Start default relay client connection (non-blocking)
|
|
895
|
-
// Use '
|
|
896
|
-
getRelayClient('
|
|
943
|
+
// Use 'Dashboard' to avoid conflicts with agents named 'Dashboard'
|
|
944
|
+
getRelayClient('Dashboard').catch(() => { });
|
|
897
945
|
// User bridge for human-to-human and human-to-agent messaging
|
|
898
946
|
userBridge = new UserBridge({
|
|
899
947
|
socketPath,
|
|
@@ -1105,10 +1153,10 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
1105
1153
|
}
|
|
1106
1154
|
targets = [to];
|
|
1107
1155
|
}
|
|
1108
|
-
// Always use '
|
|
1156
|
+
// Always use 'Dashboard' client to avoid name conflicts with user agents
|
|
1109
1157
|
// (underscore prefix indicates system client, prevents collision if user names an agent "Dashboard")
|
|
1110
1158
|
// The sender name is preserved in message history/logs but not used for the relay connection
|
|
1111
|
-
const relayClient = await getRelayClient('
|
|
1159
|
+
const relayClient = await getRelayClient('Dashboard');
|
|
1112
1160
|
if (!relayClient || relayClient.state !== 'READY') {
|
|
1113
1161
|
return res.status(503).json({ error: 'Relay daemon not connected' });
|
|
1114
1162
|
}
|
|
@@ -1126,7 +1174,7 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
1126
1174
|
}
|
|
1127
1175
|
// Include attachments, channel context, and sender info in the message data field
|
|
1128
1176
|
// For broadcasts (to='*'), include channel: 'general' so replies can be routed back
|
|
1129
|
-
// For dashboard messages, include senderName so frontend can display actual user instead of '
|
|
1177
|
+
// For dashboard messages, include senderName so frontend can display actual user instead of 'Dashboard'
|
|
1130
1178
|
const isBroadcast = targets.length === 1 && targets[0] === '*';
|
|
1131
1179
|
const messageData = {};
|
|
1132
1180
|
if (attachments && attachments.length > 0) {
|
|
@@ -1135,7 +1183,7 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
1135
1183
|
if (isBroadcast) {
|
|
1136
1184
|
messageData.channel = 'general';
|
|
1137
1185
|
}
|
|
1138
|
-
// Include actual sender name for dashboard messages (relay client uses '
|
|
1186
|
+
// Include actual sender name for dashboard messages (relay client uses 'Dashboard' but
|
|
1139
1187
|
// we want the real user's name displayed in message history)
|
|
1140
1188
|
if (senderName) {
|
|
1141
1189
|
messageData.senderName = senderName;
|
|
@@ -1149,6 +1197,25 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
1149
1197
|
allSent = false;
|
|
1150
1198
|
console.error(`[dashboard] Failed to send message to ${target}`);
|
|
1151
1199
|
}
|
|
1200
|
+
else if (storage) {
|
|
1201
|
+
// Persist outgoing message to storage so it survives page refresh
|
|
1202
|
+
const messageId = `out-${crypto.randomUUID()}`;
|
|
1203
|
+
storage.saveMessage({
|
|
1204
|
+
id: messageId,
|
|
1205
|
+
ts: Date.now(),
|
|
1206
|
+
from: senderName || 'Dashboard',
|
|
1207
|
+
to: target,
|
|
1208
|
+
topic: thread,
|
|
1209
|
+
kind: 'message',
|
|
1210
|
+
body: message,
|
|
1211
|
+
data: hasMessageData ? messageData : undefined,
|
|
1212
|
+
status: 'read', // Outgoing messages are already "read" by sender
|
|
1213
|
+
is_urgent: false,
|
|
1214
|
+
is_broadcast: isBroadcast,
|
|
1215
|
+
}).catch((err) => {
|
|
1216
|
+
console.error('[dashboard] Failed to persist outgoing message', err);
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1152
1219
|
}
|
|
1153
1220
|
if (allSent) {
|
|
1154
1221
|
// Broadcast updated data to all connected clients so they see the sent message
|
|
@@ -1408,9 +1475,9 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
1408
1475
|
if ('channel' in row.data) {
|
|
1409
1476
|
channel = row.data.channel;
|
|
1410
1477
|
}
|
|
1411
|
-
// For dashboard messages sent via
|
|
1478
|
+
// For dashboard messages sent via Dashboard, use the actual sender name
|
|
1412
1479
|
// This provides proper attribution in message history
|
|
1413
|
-
if ('senderName' in row.data && row.from === '
|
|
1480
|
+
if ('senderName' in row.data && row.from === 'Dashboard') {
|
|
1414
1481
|
effectiveFrom = row.data.senderName;
|
|
1415
1482
|
}
|
|
1416
1483
|
}
|
|
@@ -1441,7 +1508,7 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
1441
1508
|
// Fallback to daemon via RelayClient (for cases without local storage)
|
|
1442
1509
|
// Uses queryMessages if available (SDK >= 2.0.26)
|
|
1443
1510
|
try {
|
|
1444
|
-
const client = await getRelayClient('
|
|
1511
|
+
const client = await getRelayClient('Dashboard');
|
|
1445
1512
|
const clientAny = client;
|
|
1446
1513
|
if (client && client.state === 'READY' && typeof clientAny.queryMessages === 'function') {
|
|
1447
1514
|
const messages = await clientAny.queryMessages({ limit: 100, order: 'desc' });
|
|
@@ -1710,8 +1777,8 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
1710
1777
|
// Exclude agents starting with __ (internal/system agents)
|
|
1711
1778
|
if (agent.name.startsWith('__'))
|
|
1712
1779
|
return false;
|
|
1713
|
-
// Exclude
|
|
1714
|
-
if (agent.name === '
|
|
1780
|
+
// Exclude Dashboard (system client for sending dashboard messages)
|
|
1781
|
+
if (agent.name === 'Dashboard')
|
|
1715
1782
|
return false;
|
|
1716
1783
|
// Exclude agents without a proper CLI (improperly registered or stale)
|
|
1717
1784
|
if (!agent.cli || agent.cli === 'Unknown')
|
|
@@ -2190,6 +2257,30 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
2190
2257
|
}
|
|
2191
2258
|
});
|
|
2192
2259
|
};
|
|
2260
|
+
// Helper to broadcast direct messages to all connected clients
|
|
2261
|
+
// This enables agent replies to appear in the dashboard UI
|
|
2262
|
+
// Broadcasts to both main wss (local mode) and wssPresence (cloud mode)
|
|
2263
|
+
const broadcastDirectMessage = (message) => {
|
|
2264
|
+
const payload = JSON.stringify(message);
|
|
2265
|
+
// Broadcast to main WebSocket clients (local mode)
|
|
2266
|
+
const mainClients = Array.from(wss.clients).filter(c => c.readyState === WebSocket.OPEN);
|
|
2267
|
+
console.log(`[dashboard] Broadcasting direct_message to ${mainClients.length} main clients`);
|
|
2268
|
+
wss.clients.forEach((client) => {
|
|
2269
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
2270
|
+
client.send(payload);
|
|
2271
|
+
}
|
|
2272
|
+
});
|
|
2273
|
+
// Also broadcast to presence WebSocket clients (cloud mode)
|
|
2274
|
+
const presenceClients = Array.from(wssPresence.clients).filter(c => c.readyState === WebSocket.OPEN);
|
|
2275
|
+
if (presenceClients.length > 0) {
|
|
2276
|
+
console.log(`[dashboard] Broadcasting direct_message to ${presenceClients.length} presence clients`);
|
|
2277
|
+
wssPresence.clients.forEach((client) => {
|
|
2278
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
2279
|
+
client.send(payload);
|
|
2280
|
+
}
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
};
|
|
2193
2284
|
// Helper to get online users list (without ws references)
|
|
2194
2285
|
const getOnlineUsersList = () => {
|
|
2195
2286
|
return Array.from(onlineUsers.values()).map((state) => state.info);
|
|
@@ -3245,8 +3336,8 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
3245
3336
|
const uptime = process.uptime();
|
|
3246
3337
|
const memUsage = process.memoryUsage();
|
|
3247
3338
|
const socketExists = fs.existsSync(socketPath);
|
|
3248
|
-
// Check relay client connectivity (check if default
|
|
3249
|
-
const defaultClient = relayClients.get('
|
|
3339
|
+
// Check relay client connectivity (check if default Dashboard client is connected)
|
|
3340
|
+
const defaultClient = relayClients.get('Dashboard');
|
|
3250
3341
|
const relayConnected = defaultClient?.state === 'READY';
|
|
3251
3342
|
// If socket doesn't exist, daemon may not be running properly
|
|
3252
3343
|
if (!socketExists) {
|
|
@@ -3272,7 +3363,7 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
|
|
|
3272
3363
|
const uptime = process.uptime();
|
|
3273
3364
|
const memUsage = process.memoryUsage();
|
|
3274
3365
|
const socketExists = fs.existsSync(socketPath);
|
|
3275
|
-
const defaultClient = relayClients.get('
|
|
3366
|
+
const defaultClient = relayClients.get('Dashboard');
|
|
3276
3367
|
const relayConnected = defaultClient?.state === 'READY';
|
|
3277
3368
|
if (!socketExists) {
|
|
3278
3369
|
return res.status(503).json({
|
|
@@ -4818,7 +4909,7 @@ Start by greeting the project leads and asking for status updates.`;
|
|
|
4818
4909
|
}
|
|
4819
4910
|
// Try to send message to agent
|
|
4820
4911
|
try {
|
|
4821
|
-
const client = await getRelayClient('
|
|
4912
|
+
const client = await getRelayClient('Dashboard');
|
|
4822
4913
|
if (client) {
|
|
4823
4914
|
await client.sendMessage(agentName, responseMessage, 'message');
|
|
4824
4915
|
}
|
|
@@ -4848,7 +4939,7 @@ Start by greeting the project leads and asking for status updates.`;
|
|
|
4848
4939
|
responseMessage += `\nReason: ${reason}`;
|
|
4849
4940
|
}
|
|
4850
4941
|
try {
|
|
4851
|
-
const client = await getRelayClient('
|
|
4942
|
+
const client = await getRelayClient('Dashboard');
|
|
4852
4943
|
if (client) {
|
|
4853
4944
|
await client.sendMessage(agentName, responseMessage, 'message');
|
|
4854
4945
|
}
|
|
@@ -5036,7 +5127,7 @@ Start by greeting the project leads and asking for status updates.`;
|
|
|
5036
5127
|
tasks.set(task.id, task);
|
|
5037
5128
|
// Send task to agent via relay
|
|
5038
5129
|
try {
|
|
5039
|
-
const client = await getRelayClient('
|
|
5130
|
+
const client = await getRelayClient('Dashboard');
|
|
5040
5131
|
if (client) {
|
|
5041
5132
|
const taskMessage = `TASK ASSIGNED [${priority.toUpperCase()}]: ${title}\n\n${description || 'No additional details.'}`;
|
|
5042
5133
|
await client.sendMessage(agentName, taskMessage, 'message');
|
|
@@ -5084,7 +5175,7 @@ Start by greeting the project leads and asking for status updates.`;
|
|
|
5084
5175
|
// Notify agent of cancellation if task is still active
|
|
5085
5176
|
if (task.status === 'pending' || task.status === 'assigned' || task.status === 'in_progress') {
|
|
5086
5177
|
try {
|
|
5087
|
-
const client = await getRelayClient('
|
|
5178
|
+
const client = await getRelayClient('Dashboard');
|
|
5088
5179
|
if (client) {
|
|
5089
5180
|
await client.sendMessage(task.agentName, `TASK CANCELLED: ${task.title}`, 'message');
|
|
5090
5181
|
}
|
|
@@ -5158,7 +5249,7 @@ Start by greeting the project leads and asking for status updates.`;
|
|
|
5158
5249
|
return res.status(400).json({ success: false, error: 'Message content is required' });
|
|
5159
5250
|
}
|
|
5160
5251
|
try {
|
|
5161
|
-
const client = await getRelayClient('
|
|
5252
|
+
const client = await getRelayClient('Dashboard');
|
|
5162
5253
|
if (!client) {
|
|
5163
5254
|
return res.status(503).json({
|
|
5164
5255
|
success: false,
|