Skip to content

Commit 76a323d

Browse files
authored
Merge pull request #52 from AgentWorkforce/relaycast-integration
feat: reactions, thread improvements, REST fallback for relaycast integration
2 parents 414370c + 886d43d commit 76a323d

13 files changed

Lines changed: 844 additions & 34 deletions

File tree

packages/dashboard-server/src/mocks/fixtures.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export const mockMessages: Message[] = [
5656
to: 'claude-1',
5757
content: 'Please implement user authentication with JWT tokens',
5858
timestamp: new Date(Date.now() - 300000).toISOString(),
59+
reactions: [
60+
{ emoji: '👍', count: 2, agents: ['claude-1', 'architect'] },
61+
{ emoji: '🚀', count: 1, agents: ['reviewer'] },
62+
],
5963
},
6064
{
6165
id: 'msg-002',
@@ -64,13 +68,17 @@ export const mockMessages: Message[] = [
6468
content: 'I\'ll implement JWT authentication. Let me start by creating the auth middleware.',
6569
timestamp: new Date(Date.now() - 295000).toISOString(),
6670
thread: 'msg-001',
71+
reactions: [
72+
{ emoji: '✅', count: 1, agents: ['user'] },
73+
],
6774
},
6875
{
6976
id: 'msg-003',
7077
from: 'claude-1',
7178
to: 'architect',
7279
content: 'What\'s the preferred token expiration time for the JWT implementation?',
7380
timestamp: new Date(Date.now() - 290000).toISOString(),
81+
replyCount: 2,
7482
},
7583
{
7684
id: 'msg-004',
@@ -80,13 +88,28 @@ export const mockMessages: Message[] = [
8088
timestamp: new Date(Date.now() - 280000).toISOString(),
8189
thread: 'msg-003',
8290
},
91+
{
92+
id: 'msg-004b',
93+
from: 'claude-1',
94+
to: 'architect',
95+
content: 'Got it, implementing with those values. Will add refresh token rotation too.',
96+
timestamp: new Date(Date.now() - 270000).toISOString(),
97+
thread: 'msg-003',
98+
reactions: [
99+
{ emoji: '👍', count: 1, agents: ['architect'] },
100+
],
101+
},
83102
{
84103
id: 'msg-005',
85104
from: 'reviewer',
86105
to: '*',
87106
content: 'PR #42 has been reviewed. Ready for merge.',
88107
timestamp: new Date(Date.now() - 200000).toISOString(),
89108
isBroadcast: true,
109+
reactions: [
110+
{ emoji: '🎉', count: 3, agents: ['user', 'claude-1', 'architect'] },
111+
{ emoji: '👏', count: 1, agents: ['user'] },
112+
],
90113
},
91114
];
92115

packages/dashboard-server/src/mocks/routes.ts

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import type { Express, Request, Response } from 'express';
9+
import type { Message } from './types.js';
910
import {
1011
mockAgents,
1112
mockMessages,
@@ -649,6 +650,133 @@ export function registerMockRoutes(app: Express, verbose: boolean): void {
649650
});
650651
});
651652

653+
// ===== Reactions =====
654+
655+
app.post('/api/messages/:id/reactions', (req: Request, res: Response) => {
656+
const { id } = req.params;
657+
const { emoji } = req.body || {};
658+
log(`POST /api/messages/${id}/reactions - ${emoji}`);
659+
660+
const message = mockMessages.find(m => m.id === id);
661+
if (!message) {
662+
res.status(404).json({ ok: false, error: { code: 'message_not_found', message: 'Message not found' } });
663+
return;
664+
}
665+
666+
const agentName = req.body.from || mockUser.displayName;
667+
if (!message.reactions) message.reactions = [];
668+
const existing = message.reactions.find(r => r.emoji === emoji);
669+
if (existing) {
670+
if (!existing.agents.includes(agentName)) {
671+
existing.agents.push(agentName);
672+
existing.count++;
673+
}
674+
} else {
675+
message.reactions.push({ emoji, count: 1, agents: [agentName] });
676+
}
677+
678+
res.status(201).json({
679+
ok: true,
680+
data: { id: `reaction-${Date.now()}`, message_id: id, emoji, agent_name: agentName, created_at: new Date().toISOString() },
681+
});
682+
});
683+
684+
app.delete('/api/messages/:id/reactions/:emoji', (req: Request, res: Response) => {
685+
const { id, emoji } = req.params;
686+
log(`DELETE /api/messages/${id}/reactions/${emoji}`);
687+
688+
const message = mockMessages.find(m => m.id === id);
689+
if (!message) {
690+
res.status(404).json({ ok: false, error: { code: 'message_not_found', message: 'Message not found' } });
691+
return;
692+
}
693+
694+
if (message.reactions) {
695+
const existing = message.reactions.find(r => r.emoji === emoji);
696+
if (existing) {
697+
const agentToRemove = (req.body && req.body.from) || mockUser.displayName;
698+
existing.agents = existing.agents.filter(a => a !== agentToRemove);
699+
existing.count = existing.agents.length;
700+
if (existing.count === 0) {
701+
message.reactions = message.reactions.filter(r => r.emoji !== emoji);
702+
}
703+
}
704+
}
705+
706+
res.status(204).end();
707+
});
708+
709+
app.get('/api/messages/:id/reactions', (req: Request, res: Response) => {
710+
const { id } = req.params;
711+
log(`GET /api/messages/${id}/reactions`);
712+
713+
const message = mockMessages.find(m => m.id === id);
714+
if (!message) {
715+
res.status(404).json({ ok: false, error: { code: 'message_not_found', message: 'Message not found' } });
716+
return;
717+
}
718+
719+
res.json({ ok: true, data: message.reactions || [] });
720+
});
721+
722+
// ===== Thread Replies =====
723+
724+
app.get('/api/messages/:id/replies', (req: Request, res: Response) => {
725+
const { id } = req.params;
726+
log(`GET /api/messages/${id}/replies`);
727+
728+
const parent = mockMessages.find(m => m.id === id);
729+
if (!parent) {
730+
res.status(404).json({ ok: false, error: { code: 'message_not_found', message: 'Message not found' } });
731+
return;
732+
}
733+
734+
const replies = mockMessages
735+
.filter(m => m.thread === id)
736+
.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
737+
738+
res.json({
739+
ok: true,
740+
data: {
741+
parent: { ...parent, reply_count: replies.length },
742+
replies,
743+
},
744+
});
745+
});
746+
747+
app.post('/api/messages/:id/replies', (req: Request, res: Response) => {
748+
const { id } = req.params;
749+
const { text } = req.body || {};
750+
log(`POST /api/messages/${id}/replies`);
751+
752+
const parent = mockMessages.find(m => m.id === id);
753+
if (!parent) {
754+
res.status(404).json({ ok: false, error: { code: 'message_not_found', message: 'Message not found' } });
755+
return;
756+
}
757+
758+
const replyFrom = req.body.from || mockUser.displayName;
759+
const reply: Message = {
760+
id: `msg-reply-${Date.now()}`,
761+
from: replyFrom,
762+
to: parent.from === replyFrom ? parent.to : parent.from,
763+
content: text,
764+
timestamp: new Date().toISOString(),
765+
thread: id as string,
766+
};
767+
768+
mockMessages.push(reply);
769+
770+
// Update parent reply count
771+
if (parent.replyCount !== undefined) {
772+
parent.replyCount++;
773+
} else {
774+
parent.replyCount = mockMessages.filter(m => m.thread === id).length;
775+
}
776+
777+
res.status(201).json({ ok: true, data: reply });
778+
});
779+
652780
// ===== Relay Message =====
653781

654782
app.post('/api/relay/send', (req: Request, res: Response) => {
@@ -1178,5 +1306,24 @@ export function registerMockRoutes(app: Express, verbose: boolean): void {
11781306
});
11791307
});
11801308

1309+
// ===== Relaycast compatibility (v1 API) =====
1310+
1311+
app.get('/v1/workspace', (req: Request, res: Response) => {
1312+
const auth = req.headers.authorization;
1313+
log(`GET /v1/workspace - auth: ${auth ? 'present' : 'missing'}`);
1314+
if (!auth?.startsWith('Bearer rk_live_')) {
1315+
res.status(401).json({ ok: false, error: { code: 'unauthorized', message: 'Invalid API key' } });
1316+
return;
1317+
}
1318+
res.json({
1319+
ok: true,
1320+
data: {
1321+
id: mockWorkspaces[0].id,
1322+
name: mockWorkspaces[0].name,
1323+
status: 'active',
1324+
},
1325+
});
1326+
});
1327+
11811328
console.log('[mock] Mock API routes registered');
11821329
}

packages/dashboard-server/src/mocks/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ export interface Agent {
1515
projectPath?: string;
1616
}
1717

18+
export interface Reaction {
19+
emoji: string;
20+
count: number;
21+
agents: string[];
22+
}
23+
1824
export interface Message {
1925
id: string;
2026
from: string;
@@ -25,13 +31,15 @@ export interface Message {
2531
isBroadcast?: boolean;
2632
isUrgent?: boolean;
2733
status?: string;
34+
replyCount?: number;
2835
data?: Record<string, unknown>;
2936
attachments?: Array<{
3037
id: string;
3138
filename: string;
3239
mimeType: string;
3340
size: number;
3441
}>;
42+
reactions?: Reaction[];
3543
}
3644

3745
export interface Session {

packages/dashboard/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@
1313
"types": "./dist/index.d.ts",
1414
"import": "./dist/index.js"
1515
},
16+
"./components/*": "./src/components/*.tsx",
17+
"./hooks/*": "./src/components/hooks/*.ts",
18+
"./lib/*": "./src/lib/*.ts",
19+
"./types": "./src/types/index.ts",
20+
"./src/app/*.css": "./src/app/*.css",
1621
"./out/*": "./out/*"
1722
},
1823
"files": [
1924
"out",
2025
"dist",
26+
"src",
2127
"README.md"
2228
],
2329
"scripts": {

0 commit comments

Comments
 (0)