@agent-relay/dashboard 2.0.81 → 2.0.82
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/out/404.html +1 -1
- package/out/_next/static/chunks/{118-4c8241b0218335de.js → 118-ae2b650136a5a5fc.js} +1 -1
- package/out/_next/static/chunks/407-0c82986cf79c8ecb.js +1 -0
- package/out/_next/static/chunks/app/app/[[...slug]]/{page-1e81c047cff17212.js → page-f7eca1b66fb4249b.js} +1 -1
- package/out/_next/static/chunks/app/{page-6892fe2dd07fb48b.js → page-0ee604f7070d14c0.js} +1 -1
- package/out/_next/static/css/8968d98ed4c4d33f.css +1 -0
- package/out/about.html +2 -2
- 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 +2 -2
- 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 +2 -2
- package/out/blog/let-them-cook-multi-agent-orchestration.txt +2 -2
- package/out/blog.html +2 -2
- package/out/blog.txt +1 -1
- package/out/careers.html +2 -2
- package/out/careers.txt +1 -1
- package/out/changelog.html +2 -2
- package/out/changelog.txt +1 -1
- package/out/cloud/link.html +1 -1
- package/out/cloud/link.txt +2 -2
- package/out/complete-profile.html +2 -2
- 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 +2 -2
- package/out/contact.txt +1 -1
- package/out/docs.html +2 -2
- 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 +2 -2
- package/out/login.txt +1 -1
- package/out/metrics.html +1 -1
- package/out/metrics.txt +2 -2
- package/out/pricing.html +2 -2
- package/out/pricing.txt +1 -1
- package/out/privacy.html +2 -2
- 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 +2 -2
- package/out/security.txt +1 -1
- package/out/signup.html +2 -2
- package/out/signup.txt +1 -1
- package/out/terms.html +2 -2
- package/out/terms.txt +1 -1
- package/package.json +7 -1
- package/src/app/about/page.tsx +7 -0
- package/src/app/app/[[...slug]]/DashboardPageClient.tsx +853 -0
- package/src/app/app/[[...slug]]/page.tsx +23 -0
- package/src/app/app/onboarding/page.tsx +394 -0
- package/src/app/apple-icon.png +0 -0
- package/src/app/blog/go-to-bed-wake-up-to-a-finished-product/page.tsx +88 -0
- package/src/app/blog/let-them-cook-multi-agent-orchestration/page.tsx +93 -0
- package/src/app/blog/page.tsx +15 -0
- package/src/app/careers/page.tsx +7 -0
- package/src/app/changelog/page.tsx +7 -0
- package/src/app/cloud/link/page.tsx +464 -0
- package/src/app/complete-profile/page.tsx +204 -0
- package/src/app/connect-repos/page.tsx +410 -0
- package/src/app/contact/page.tsx +7 -0
- package/src/app/docs/page.tsx +7 -0
- package/src/app/favicon.png +0 -0
- package/src/app/globals.css +200 -0
- package/src/app/history/page.tsx +658 -0
- package/src/app/layout.tsx +25 -0
- package/src/app/login/page.tsx +424 -0
- package/src/app/metrics/page.tsx +781 -0
- package/src/app/page.tsx +59 -0
- package/src/app/pricing/page.tsx +7 -0
- package/src/app/privacy/page.tsx +7 -0
- package/src/app/providers/page.tsx +193 -0
- package/src/app/providers/setup/[provider]/ProviderSetupClient.tsx +197 -0
- package/src/app/providers/setup/[provider]/constants.ts +35 -0
- package/src/app/providers/setup/[provider]/page.tsx +42 -0
- package/src/app/security/page.tsx +7 -0
- package/src/app/signup/page.tsx +533 -0
- package/src/app/terms/page.tsx +7 -0
- package/src/components/ActivityFeed.tsx +216 -0
- package/src/components/AddWorkspaceModal.tsx +170 -0
- package/src/components/AgentCard.test.tsx +134 -0
- package/src/components/AgentCard.tsx +585 -0
- package/src/components/AgentList.test.tsx +147 -0
- package/src/components/AgentList.tsx +419 -0
- package/src/components/AgentLogPreview.tsx +173 -0
- package/src/components/AgentProfilePanel.tsx +569 -0
- package/src/components/App.tsx +3424 -0
- package/src/components/BillingPanel.tsx +922 -0
- package/src/components/BillingResult.tsx +447 -0
- package/src/components/BroadcastComposer.tsx +690 -0
- package/src/components/ChannelAdminPanel.tsx +773 -0
- package/src/components/ChannelBrowser.tsx +385 -0
- package/src/components/ChannelChat.tsx +261 -0
- package/src/components/ChannelSidebar.tsx +399 -0
- package/src/components/CloudSessionProvider.tsx +130 -0
- package/src/components/CommandPalette.tsx +815 -0
- package/src/components/ConfirmationDialog.tsx +133 -0
- package/src/components/ConversationHistory.tsx +518 -0
- package/src/components/CoordinatorPanel.tsx +956 -0
- package/src/components/DecisionQueue.tsx +717 -0
- package/src/components/DirectMessageView.tsx +164 -0
- package/src/components/FileAutocomplete.tsx +368 -0
- package/src/components/FleetOverview.tsx +278 -0
- package/src/components/LogViewer.tsx +310 -0
- package/src/components/LogViewerPanel.tsx +482 -0
- package/src/components/Logo.tsx +284 -0
- package/src/components/MentionAutocomplete.tsx +384 -0
- package/src/components/MessageComposer.tsx +473 -0
- package/src/components/MessageList.tsx +725 -0
- package/src/components/MessageSenderName.tsx +91 -0
- package/src/components/MessageStatusIndicator.tsx +142 -0
- package/src/components/NewConversationModal.tsx +400 -0
- package/src/components/NotificationToast.tsx +488 -0
- package/src/components/OnlineUsersIndicator.tsx +164 -0
- package/src/components/Pagination.tsx +124 -0
- package/src/components/PricingPlans.tsx +386 -0
- package/src/components/ProjectList.tsx +711 -0
- package/src/components/ProviderAuthFlow.tsx +343 -0
- package/src/components/ProviderConnectionList.tsx +375 -0
- package/src/components/ProvisioningProgress.tsx +730 -0
- package/src/components/ReactionChips.tsx +70 -0
- package/src/components/ReactionPicker.tsx +121 -0
- package/src/components/RepoAccessPanel.tsx +787 -0
- package/src/components/RepositoriesPanel.tsx +901 -0
- package/src/components/ServerCard.tsx +202 -0
- package/src/components/SessionExpiredModal.tsx +128 -0
- package/src/components/SpawnModal.test.tsx +190 -0
- package/src/components/SpawnModal.tsx +1001 -0
- package/src/components/TaskAssignmentUI.tsx +375 -0
- package/src/components/TerminalProviderSetup.tsx +517 -0
- package/src/components/ThemeProvider.tsx +159 -0
- package/src/components/ThinkingIndicator.tsx +231 -0
- package/src/components/ThreadList.tsx +198 -0
- package/src/components/ThreadPanel.tsx +405 -0
- package/src/components/TrajectoryViewer.tsx +698 -0
- package/src/components/TypingIndicator.tsx +69 -0
- package/src/components/UsageBanner.tsx +231 -0
- package/src/components/UserProfilePanel.tsx +233 -0
- package/src/components/WorkspaceContext.tsx +95 -0
- package/src/components/WorkspaceSelector.tsx +234 -0
- package/src/components/WorkspaceStatusIndicator.tsx +396 -0
- package/src/components/XTermInteractive.tsx +516 -0
- package/src/components/XTermLogViewer.tsx +719 -0
- package/src/components/channels/ChannelDialogs.tsx +1411 -0
- package/src/components/channels/ChannelHeader.tsx +317 -0
- package/src/components/channels/ChannelMessageList.tsx +463 -0
- package/src/components/channels/ChannelViewV1.tsx +146 -0
- package/src/components/channels/MessageInput.tsx +302 -0
- package/src/components/channels/SearchInput.tsx +172 -0
- package/src/components/channels/SearchResults.tsx +336 -0
- package/src/components/channels/api.test.ts +1527 -0
- package/src/components/channels/api.ts +703 -0
- package/src/components/channels/index.ts +76 -0
- package/src/components/channels/mockApi.ts +344 -0
- package/src/components/channels/types.ts +566 -0
- package/src/components/hooks/index.ts +58 -0
- package/src/components/hooks/useAgentLogs.ts +504 -0
- package/src/components/hooks/useAgents.ts +127 -0
- package/src/components/hooks/useBroadcastDedup.test.ts +371 -0
- package/src/components/hooks/useBroadcastDedup.ts +86 -0
- package/src/components/hooks/useChannelAdmin.ts +329 -0
- package/src/components/hooks/useChannelBrowser.ts +239 -0
- package/src/components/hooks/useChannelCommands.ts +138 -0
- package/src/components/hooks/useChannels.ts +367 -0
- package/src/components/hooks/useDebounce.ts +29 -0
- package/src/components/hooks/useDirectMessage.test.ts +952 -0
- package/src/components/hooks/useDirectMessage.ts +141 -0
- package/src/components/hooks/useMessages.ts +310 -0
- package/src/components/hooks/useOrchestrator.test.ts +165 -0
- package/src/components/hooks/useOrchestrator.ts +424 -0
- package/src/components/hooks/usePinnedAgents.test.ts +356 -0
- package/src/components/hooks/usePinnedAgents.ts +140 -0
- package/src/components/hooks/usePresence.test.ts +245 -0
- package/src/components/hooks/usePresence.ts +377 -0
- package/src/components/hooks/useRecentRepos.ts +130 -0
- package/src/components/hooks/useSession.ts +209 -0
- package/src/components/hooks/useThread.ts +138 -0
- package/src/components/hooks/useTrajectory.ts +265 -0
- package/src/components/hooks/useWebSocket.ts +290 -0
- package/src/components/hooks/useWorkspaceMembers.ts +132 -0
- package/src/components/hooks/useWorkspaceRepos.ts +73 -0
- package/src/components/hooks/useWorkspaceStatus.ts +237 -0
- package/src/components/index.ts +81 -0
- package/src/components/layout/Header.tsx +311 -0
- package/src/components/layout/RepoContextHeader.tsx +361 -0
- package/src/components/layout/Sidebar.archive.test.tsx +126 -0
- package/src/components/layout/Sidebar.test.tsx +691 -0
- package/src/components/layout/Sidebar.tsx +900 -0
- package/src/components/layout/index.ts +7 -0
- package/src/components/settings/BillingSettingsPanel.tsx +564 -0
- package/src/components/settings/SettingsPage.tsx +683 -0
- package/src/components/settings/TeamSettingsPanel.tsx +560 -0
- package/src/components/settings/WorkspaceSettingsPanel.tsx +1368 -0
- package/src/components/settings/index.ts +11 -0
- package/src/components/settings/types.ts +79 -0
- package/src/components/utils/messageFormatting.test.tsx +331 -0
- package/src/components/utils/messageFormatting.tsx +597 -0
- package/src/index.ts +63 -0
- package/src/landing/AboutPage.tsx +77 -0
- package/src/landing/BlogContent.tsx +187 -0
- package/src/landing/BlogPage.tsx +47 -0
- package/src/landing/CareersPage.tsx +53 -0
- package/src/landing/ChangelogPage.tsx +33 -0
- package/src/landing/ContactPage.tsx +41 -0
- package/src/landing/DocsPage.tsx +43 -0
- package/src/landing/LandingPage.tsx +702 -0
- package/src/landing/PricingPage.tsx +549 -0
- package/src/landing/PrivacyPage.tsx +117 -0
- package/src/landing/SecurityPage.tsx +42 -0
- package/src/landing/StaticPage.tsx +165 -0
- package/src/landing/TermsPage.tsx +125 -0
- package/src/landing/blogData.ts +312 -0
- package/src/landing/index.ts +18 -0
- package/src/landing/styles.css +3673 -0
- package/src/lib/agent-merge.test.ts +43 -0
- package/src/lib/agent-merge.ts +35 -0
- package/src/lib/api.ts +1294 -0
- package/src/lib/cloudApi.ts +893 -0
- package/src/lib/colors.test.ts +175 -0
- package/src/lib/colors.ts +218 -0
- package/src/lib/config.ts +109 -0
- package/src/lib/hierarchy.ts +242 -0
- package/src/lib/stuckDetection.ts +142 -0
- package/src/lib/useUrlRouting.ts +190 -0
- package/src/types/index.ts +317 -0
- package/src/types/threading.ts +7 -0
- package/out/_next/static/chunks/285-dc644487a8d6500d.js +0 -1
- package/out/_next/static/css/4c58d9cf493aa626.css +0 -1
- /package/out/_next/static/{dYlczDQI12PIQ3tqq3N4Y → IxfA6RZu4trcsEMYlkQra}/_buildManifest.js +0 -0
- /package/out/_next/static/{dYlczDQI12PIQ3tqq3N4Y → IxfA6RZu4trcsEMYlkQra}/_ssgManifest.js +0 -0
- /package/out/_next/static/chunks/{528-d375bc8b46912d2c.js → 528-f5f676996d613c25.js} +0 -0
- /package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/{page-a58308f43557b908.js → page-b194f207fbd91862.js} +0 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
parseAgentHierarchy,
|
|
4
|
+
getAgentPrefix,
|
|
5
|
+
getAgentColor,
|
|
6
|
+
getAgentInitials,
|
|
7
|
+
groupAgentsByPrefix,
|
|
8
|
+
sortAgentsByHierarchy,
|
|
9
|
+
STATUS_COLORS,
|
|
10
|
+
} from './colors.js';
|
|
11
|
+
|
|
12
|
+
describe('colors', () => {
|
|
13
|
+
describe('parseAgentHierarchy', () => {
|
|
14
|
+
it('parses single-level name', () => {
|
|
15
|
+
expect(parseAgentHierarchy('Lead')).toEqual(['lead']);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('parses two-level name', () => {
|
|
19
|
+
expect(parseAgentHierarchy('backend-api')).toEqual(['backend', 'api']);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('parses three-level name', () => {
|
|
23
|
+
expect(parseAgentHierarchy('frontend-ui-components')).toEqual([
|
|
24
|
+
'frontend',
|
|
25
|
+
'ui',
|
|
26
|
+
'components',
|
|
27
|
+
]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('handles uppercase names', () => {
|
|
31
|
+
expect(parseAgentHierarchy('Backend-API')).toEqual(['backend', 'api']);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('handles empty string', () => {
|
|
35
|
+
expect(parseAgentHierarchy('')).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('getAgentPrefix', () => {
|
|
40
|
+
it('returns first segment for hyphenated name', () => {
|
|
41
|
+
expect(getAgentPrefix('backend-api-auth')).toBe('backend');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('returns lowercase name for single segment', () => {
|
|
45
|
+
expect(getAgentPrefix('Lead')).toBe('lead');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('handles empty string', () => {
|
|
49
|
+
expect(getAgentPrefix('')).toBe('');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('getAgentColor', () => {
|
|
54
|
+
it('returns predefined color for known prefix', () => {
|
|
55
|
+
const color = getAgentColor('backend-api');
|
|
56
|
+
expect(color.primary).toBe('#1264a3'); // Blue for backend
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('returns predefined color for frontend prefix', () => {
|
|
60
|
+
const color = getAgentColor('frontend-ui');
|
|
61
|
+
expect(color.primary).toBe('#7c3aed'); // Purple for frontend
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('returns predefined color for lead prefix', () => {
|
|
65
|
+
const color = getAgentColor('lead-main');
|
|
66
|
+
expect(color.primary).toBe('#2bac76'); // Green for lead
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('returns consistent fallback color for unknown prefix', () => {
|
|
70
|
+
const color1 = getAgentColor('custom-agent');
|
|
71
|
+
const color2 = getAgentColor('custom-other');
|
|
72
|
+
// Same prefix should get same color
|
|
73
|
+
expect(color1).toEqual(color2);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('returns different colors for different unknown prefixes', () => {
|
|
77
|
+
const color1 = getAgentColor('alpha-agent');
|
|
78
|
+
const color2 = getAgentColor('beta-agent');
|
|
79
|
+
// Different prefixes should likely get different colors
|
|
80
|
+
// (not guaranteed due to hash collisions, but likely)
|
|
81
|
+
expect(color1.primary).not.toBe(color2.primary);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('includes all required color properties', () => {
|
|
85
|
+
const color = getAgentColor('any-agent');
|
|
86
|
+
expect(color).toHaveProperty('primary');
|
|
87
|
+
expect(color).toHaveProperty('light');
|
|
88
|
+
expect(color).toHaveProperty('dark');
|
|
89
|
+
expect(color).toHaveProperty('text');
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('getAgentInitials', () => {
|
|
94
|
+
it('returns first two letters for single segment', () => {
|
|
95
|
+
expect(getAgentInitials('Lead')).toBe('LE');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('returns first letter of each segment for two segments', () => {
|
|
99
|
+
expect(getAgentInitials('backend-api')).toBe('BA');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('returns first letter of first two segments for three segments', () => {
|
|
103
|
+
expect(getAgentInitials('frontend-ui-components')).toBe('FU');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('handles single character segments', () => {
|
|
107
|
+
expect(getAgentInitials('a-b-c')).toBe('AB');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('groupAgentsByPrefix', () => {
|
|
112
|
+
it('groups agents by their prefix', () => {
|
|
113
|
+
const agents = [
|
|
114
|
+
{ name: 'backend-api' },
|
|
115
|
+
{ name: 'backend-db' },
|
|
116
|
+
{ name: 'frontend-ui' },
|
|
117
|
+
{ name: 'Lead' },
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
const groups = groupAgentsByPrefix(agents);
|
|
121
|
+
|
|
122
|
+
expect(groups.get('backend')).toHaveLength(2);
|
|
123
|
+
expect(groups.get('frontend')).toHaveLength(1);
|
|
124
|
+
expect(groups.get('lead')).toHaveLength(1);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('handles empty array', () => {
|
|
128
|
+
const groups = groupAgentsByPrefix([]);
|
|
129
|
+
expect(groups.size).toBe(0);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('sortAgentsByHierarchy', () => {
|
|
134
|
+
it('sorts agents by prefix then by name', () => {
|
|
135
|
+
const agents = [
|
|
136
|
+
{ name: 'frontend-ui' },
|
|
137
|
+
{ name: 'backend-db' },
|
|
138
|
+
{ name: 'backend-api' },
|
|
139
|
+
{ name: 'Lead' },
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
const sorted = sortAgentsByHierarchy(agents);
|
|
143
|
+
|
|
144
|
+
expect(sorted.map((a) => a.name)).toEqual([
|
|
145
|
+
'backend-api',
|
|
146
|
+
'backend-db',
|
|
147
|
+
'frontend-ui',
|
|
148
|
+
'Lead',
|
|
149
|
+
]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('does not mutate original array', () => {
|
|
153
|
+
const agents = [{ name: 'z-agent' }, { name: 'a-agent' }];
|
|
154
|
+
const original = [...agents];
|
|
155
|
+
|
|
156
|
+
sortAgentsByHierarchy(agents);
|
|
157
|
+
|
|
158
|
+
expect(agents).toEqual(original);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('STATUS_COLORS', () => {
|
|
163
|
+
it('has all required status colors', () => {
|
|
164
|
+
expect(STATUS_COLORS.online).toBeDefined();
|
|
165
|
+
expect(STATUS_COLORS.offline).toBeDefined();
|
|
166
|
+
expect(STATUS_COLORS.busy).toBeDefined();
|
|
167
|
+
expect(STATUS_COLORS.error).toBeDefined();
|
|
168
|
+
expect(STATUS_COLORS.attention).toBeDefined();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('uses green for online status', () => {
|
|
172
|
+
expect(STATUS_COLORS.online).toBe('#22c55e');
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hierarchical Color Coding System
|
|
3
|
+
*
|
|
4
|
+
* Inspired by AI Maestro's naming convention:
|
|
5
|
+
* - Agent names use hyphens to denote hierarchy: project-category-agent
|
|
6
|
+
* - Each top-level prefix gets a consistent color
|
|
7
|
+
* - Colors are visually distinct and accessible
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface ColorScheme {
|
|
11
|
+
primary: string;
|
|
12
|
+
light: string;
|
|
13
|
+
dark: string;
|
|
14
|
+
text: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Predefined color schemes for common agent prefixes
|
|
18
|
+
const PREFIX_COLORS: Record<string, ColorScheme> = {
|
|
19
|
+
backend: {
|
|
20
|
+
primary: '#1264a3',
|
|
21
|
+
light: '#e8f4fd',
|
|
22
|
+
dark: '#0d4f82',
|
|
23
|
+
text: '#ffffff',
|
|
24
|
+
},
|
|
25
|
+
frontend: {
|
|
26
|
+
primary: '#7c3aed',
|
|
27
|
+
light: '#f3e8ff',
|
|
28
|
+
dark: '#5b21b6',
|
|
29
|
+
text: '#ffffff',
|
|
30
|
+
},
|
|
31
|
+
infra: {
|
|
32
|
+
primary: '#ea580c',
|
|
33
|
+
light: '#fff7ed',
|
|
34
|
+
dark: '#c2410c',
|
|
35
|
+
text: '#ffffff',
|
|
36
|
+
},
|
|
37
|
+
lead: {
|
|
38
|
+
primary: '#2bac76',
|
|
39
|
+
light: '#ecfdf5',
|
|
40
|
+
dark: '#059669',
|
|
41
|
+
text: '#ffffff',
|
|
42
|
+
},
|
|
43
|
+
test: {
|
|
44
|
+
primary: '#0d9488',
|
|
45
|
+
light: '#f0fdfa',
|
|
46
|
+
dark: '#0f766e',
|
|
47
|
+
text: '#ffffff',
|
|
48
|
+
},
|
|
49
|
+
data: {
|
|
50
|
+
primary: '#dc2626',
|
|
51
|
+
light: '#fef2f2',
|
|
52
|
+
dark: '#b91c1c',
|
|
53
|
+
text: '#ffffff',
|
|
54
|
+
},
|
|
55
|
+
api: {
|
|
56
|
+
primary: '#2563eb',
|
|
57
|
+
light: '#eff6ff',
|
|
58
|
+
dark: '#1d4ed8',
|
|
59
|
+
text: '#ffffff',
|
|
60
|
+
},
|
|
61
|
+
worker: {
|
|
62
|
+
primary: '#9333ea',
|
|
63
|
+
light: '#faf5ff',
|
|
64
|
+
dark: '#7e22ce',
|
|
65
|
+
text: '#ffffff',
|
|
66
|
+
},
|
|
67
|
+
monitor: {
|
|
68
|
+
primary: '#0891b2',
|
|
69
|
+
light: '#ecfeff',
|
|
70
|
+
dark: '#0e7490',
|
|
71
|
+
text: '#ffffff',
|
|
72
|
+
},
|
|
73
|
+
security: {
|
|
74
|
+
primary: '#be123c',
|
|
75
|
+
light: '#fff1f2',
|
|
76
|
+
dark: '#9f1239',
|
|
77
|
+
text: '#ffffff',
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Fallback colors for unknown prefixes (generated from name hash)
|
|
82
|
+
const FALLBACK_COLORS: ColorScheme[] = [
|
|
83
|
+
{ primary: '#6366f1', light: '#eef2ff', dark: '#4f46e5', text: '#ffffff' },
|
|
84
|
+
{ primary: '#ec4899', light: '#fdf2f8', dark: '#db2777', text: '#ffffff' },
|
|
85
|
+
{ primary: '#14b8a6', light: '#f0fdfa', dark: '#0d9488', text: '#ffffff' },
|
|
86
|
+
{ primary: '#f59e0b', light: '#fffbeb', dark: '#d97706', text: '#000000' },
|
|
87
|
+
{ primary: '#8b5cf6', light: '#f5f3ff', dark: '#7c3aed', text: '#ffffff' },
|
|
88
|
+
{ primary: '#06b6d4', light: '#ecfeff', dark: '#0891b2', text: '#ffffff' },
|
|
89
|
+
{ primary: '#f43f5e', light: '#fff1f2', dark: '#e11d48', text: '#ffffff' },
|
|
90
|
+
{ primary: '#84cc16', light: '#f7fee7', dark: '#65a30d', text: '#000000' },
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
// Status colors
|
|
94
|
+
export const STATUS_COLORS = {
|
|
95
|
+
online: '#22c55e', // Green
|
|
96
|
+
offline: '#6b7280', // Gray
|
|
97
|
+
busy: '#eab308', // Yellow
|
|
98
|
+
processing: '#6366f1', // Indigo (thinking/processing)
|
|
99
|
+
error: '#ef4444', // Red
|
|
100
|
+
attention: '#ef4444', // Red (for badge)
|
|
101
|
+
stuck: '#f97316', // Orange (received message but no response)
|
|
102
|
+
} as const;
|
|
103
|
+
|
|
104
|
+
export type AgentStatus = keyof typeof STATUS_COLORS;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Parse agent name into hierarchy levels
|
|
108
|
+
* e.g., "backend-api-auth" => ["backend", "api", "auth"]
|
|
109
|
+
*/
|
|
110
|
+
export function parseAgentHierarchy(name: string): string[] {
|
|
111
|
+
return name.toLowerCase().split('-').filter(Boolean);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get the top-level prefix from an agent name
|
|
116
|
+
* e.g., "backend-api-auth" => "backend"
|
|
117
|
+
*/
|
|
118
|
+
export function getAgentPrefix(name: string): string {
|
|
119
|
+
const parts = parseAgentHierarchy(name);
|
|
120
|
+
return parts[0] || name.toLowerCase();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Generate a hash code from a string for consistent color assignment
|
|
125
|
+
*/
|
|
126
|
+
function hashCode(str: string): number {
|
|
127
|
+
let hash = 0;
|
|
128
|
+
for (let i = 0; i < str.length; i++) {
|
|
129
|
+
const char = str.charCodeAt(i);
|
|
130
|
+
hash = ((hash << 5) - hash) + char;
|
|
131
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
132
|
+
}
|
|
133
|
+
return Math.abs(hash);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get color scheme for an agent based on its name
|
|
138
|
+
* Uses prefix matching first, falls back to hash-based color
|
|
139
|
+
*/
|
|
140
|
+
export function getAgentColor(name: string): ColorScheme {
|
|
141
|
+
const prefix = getAgentPrefix(name);
|
|
142
|
+
|
|
143
|
+
// Check for predefined prefix color
|
|
144
|
+
if (prefix in PREFIX_COLORS) {
|
|
145
|
+
return PREFIX_COLORS[prefix];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Fall back to hash-based color for consistency
|
|
149
|
+
const hash = hashCode(prefix);
|
|
150
|
+
return FALLBACK_COLORS[hash % FALLBACK_COLORS.length];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get CSS custom properties for an agent's color scheme
|
|
155
|
+
* Useful for inline styles or CSS variables
|
|
156
|
+
*/
|
|
157
|
+
export function getAgentColorVars(name: string): Record<string, string> {
|
|
158
|
+
const colors = getAgentColor(name);
|
|
159
|
+
return {
|
|
160
|
+
'--agent-primary': colors.primary,
|
|
161
|
+
'--agent-light': colors.light,
|
|
162
|
+
'--agent-dark': colors.dark,
|
|
163
|
+
'--agent-text': colors.text,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get initials from agent name (first 2 chars of first segment)
|
|
169
|
+
*/
|
|
170
|
+
export function getAgentInitials(name: string): string {
|
|
171
|
+
const parts = parseAgentHierarchy(name);
|
|
172
|
+
if (parts.length === 0) return name.substring(0, 2).toUpperCase();
|
|
173
|
+
|
|
174
|
+
// Use first letter of first two segments, or first two letters of first segment
|
|
175
|
+
if (parts.length >= 2) {
|
|
176
|
+
return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
177
|
+
}
|
|
178
|
+
return parts[0].substring(0, 2).toUpperCase();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Group agents by their top-level prefix
|
|
183
|
+
*/
|
|
184
|
+
export function groupAgentsByPrefix<T extends { name: string }>(
|
|
185
|
+
agents: T[]
|
|
186
|
+
): Map<string, T[]> {
|
|
187
|
+
const groups = new Map<string, T[]>();
|
|
188
|
+
|
|
189
|
+
for (const agent of agents) {
|
|
190
|
+
const prefix = getAgentPrefix(agent.name);
|
|
191
|
+
const group = groups.get(prefix) || [];
|
|
192
|
+
group.push(agent);
|
|
193
|
+
groups.set(prefix, group);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return groups;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Sort agents by hierarchy for display
|
|
201
|
+
* Groups by prefix, then sorts alphabetically within groups
|
|
202
|
+
*/
|
|
203
|
+
export function sortAgentsByHierarchy<T extends { name: string }>(
|
|
204
|
+
agents: T[]
|
|
205
|
+
): T[] {
|
|
206
|
+
return [...agents].sort((a, b) => {
|
|
207
|
+
const prefixA = getAgentPrefix(a.name);
|
|
208
|
+
const prefixB = getAgentPrefix(b.name);
|
|
209
|
+
|
|
210
|
+
// Sort by prefix first
|
|
211
|
+
if (prefixA !== prefixB) {
|
|
212
|
+
return prefixA.localeCompare(prefixB);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Then by full name
|
|
216
|
+
return a.name.localeCompare(b.name);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard Configuration
|
|
3
|
+
*
|
|
4
|
+
* Centralized configuration for API and WebSocket URLs.
|
|
5
|
+
* Works out-of-the-box with sensible defaults - no configuration required.
|
|
6
|
+
*
|
|
7
|
+
* Defaults (no env vars needed):
|
|
8
|
+
* - Development: WebSocket connects to localhost:3889 (dashboard server)
|
|
9
|
+
* - Production: WebSocket auto-detects from page URL (same host/port)
|
|
10
|
+
* - API: Uses relative URLs (works for same-origin requests)
|
|
11
|
+
*
|
|
12
|
+
* Optional overrides (for advanced deployments):
|
|
13
|
+
* - NEXT_PUBLIC_WS_URL: Override WebSocket URL entirely
|
|
14
|
+
* - NEXT_PUBLIC_API_URL: Override API base URL
|
|
15
|
+
* - NEXT_PUBLIC_DEV_SERVER_PORT: Change dev server port (default: 3889)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Default port for dashboard server in development
|
|
19
|
+
const DEFAULT_DEV_SERVER_PORT = '3889';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get the configured dev server port
|
|
23
|
+
*/
|
|
24
|
+
function getDevServerPort(): string {
|
|
25
|
+
return process.env.NEXT_PUBLIC_DEV_SERVER_PORT || DEFAULT_DEV_SERVER_PORT;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if we're in development mode
|
|
30
|
+
*/
|
|
31
|
+
function isDevelopment(): boolean {
|
|
32
|
+
return process.env.NODE_ENV === 'development';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if we're running in the browser
|
|
37
|
+
*/
|
|
38
|
+
function isBrowser(): boolean {
|
|
39
|
+
return typeof window !== 'undefined';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the WebSocket URL for the main dashboard connection
|
|
44
|
+
*
|
|
45
|
+
* Priority:
|
|
46
|
+
* 1. NEXT_PUBLIC_WS_URL environment variable
|
|
47
|
+
* 2. Development mode: connect to dev server port
|
|
48
|
+
* 3. Production: auto-detect from page URL
|
|
49
|
+
*/
|
|
50
|
+
export function getWebSocketUrl(path: string = '/ws'): string {
|
|
51
|
+
// Check for explicit environment variable
|
|
52
|
+
const envWsUrl = process.env.NEXT_PUBLIC_WS_URL;
|
|
53
|
+
if (envWsUrl) {
|
|
54
|
+
// Append path if base URL provided
|
|
55
|
+
return envWsUrl.endsWith('/') ? `${envWsUrl.slice(0, -1)}${path}` : `${envWsUrl}${path}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// SSR fallback - return localhost with dev port
|
|
59
|
+
if (!isBrowser()) {
|
|
60
|
+
const port = getDevServerPort();
|
|
61
|
+
return `ws://localhost:${port}${path}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const host = window.location.hostname || 'localhost';
|
|
65
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
66
|
+
|
|
67
|
+
// Development mode: Next.js on 3888, dashboard server on dev port (3889)
|
|
68
|
+
// Next.js rewrites don't support WebSocket upgrade, so connect directly
|
|
69
|
+
if (isDevelopment() && window.location.port === '3888') {
|
|
70
|
+
const devPort = getDevServerPort();
|
|
71
|
+
return `ws://${host}:${devPort}${path}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Production: use same host/port as the page
|
|
75
|
+
return `${protocol}//${window.location.host}${path}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get the API base URL
|
|
80
|
+
*
|
|
81
|
+
* Priority:
|
|
82
|
+
* 1. NEXT_PUBLIC_API_URL environment variable
|
|
83
|
+
* 2. Empty string (relative URLs) - works for same-origin
|
|
84
|
+
*/
|
|
85
|
+
export function getApiBaseUrl(): string {
|
|
86
|
+
return process.env.NEXT_PUBLIC_API_URL || '';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Configuration object for easy access
|
|
91
|
+
*/
|
|
92
|
+
export const config = {
|
|
93
|
+
/** Get WebSocket URL for a given path */
|
|
94
|
+
getWebSocketUrl,
|
|
95
|
+
|
|
96
|
+
/** Get API base URL */
|
|
97
|
+
getApiBaseUrl,
|
|
98
|
+
|
|
99
|
+
/** Check if in development mode */
|
|
100
|
+
isDevelopment,
|
|
101
|
+
|
|
102
|
+
/** Check if in browser */
|
|
103
|
+
isBrowser,
|
|
104
|
+
|
|
105
|
+
/** Dev server port */
|
|
106
|
+
devServerPort: getDevServerPort(),
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export default config;
|