rufloui 0.3.1

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.
Files changed (56) hide show
  1. package/'1' +0 -0
  2. package/.env.example +46 -0
  3. package/CHANGELOG.md +87 -0
  4. package/CLAUDE.md +287 -0
  5. package/LICENSE +21 -0
  6. package/README.md +316 -0
  7. package/Webhooks) +0 -0
  8. package/docs/plans/2026-03-11-github-webhooks.md +957 -0
  9. package/docs/screenshot-swarm-monitor.png +0 -0
  10. package/frontend +0 -0
  11. package/index.html +13 -0
  12. package/package.json +56 -0
  13. package/public/vite.svg +4 -0
  14. package/src/backend/__tests__/webhook-github.test.ts +934 -0
  15. package/src/backend/jsonl-monitor.ts +430 -0
  16. package/src/backend/server.ts +2972 -0
  17. package/src/backend/telegram-bot.ts +511 -0
  18. package/src/backend/webhook-github.ts +350 -0
  19. package/src/frontend/App.tsx +461 -0
  20. package/src/frontend/api.ts +281 -0
  21. package/src/frontend/components/ErrorBoundary.tsx +98 -0
  22. package/src/frontend/components/Layout.tsx +431 -0
  23. package/src/frontend/components/ui/Button.tsx +111 -0
  24. package/src/frontend/components/ui/Card.tsx +51 -0
  25. package/src/frontend/components/ui/StatusBadge.tsx +60 -0
  26. package/src/frontend/main.tsx +63 -0
  27. package/src/frontend/pages/AgentVizPanel.tsx +428 -0
  28. package/src/frontend/pages/AgentsPanel.tsx +445 -0
  29. package/src/frontend/pages/ConfigPanel.tsx +661 -0
  30. package/src/frontend/pages/Dashboard.tsx +482 -0
  31. package/src/frontend/pages/HiveMindPanel.tsx +355 -0
  32. package/src/frontend/pages/HooksPanel.tsx +240 -0
  33. package/src/frontend/pages/LogsPanel.tsx +261 -0
  34. package/src/frontend/pages/MemoryPanel.tsx +444 -0
  35. package/src/frontend/pages/NeuralPanel.tsx +301 -0
  36. package/src/frontend/pages/PerformancePanel.tsx +198 -0
  37. package/src/frontend/pages/SessionsPanel.tsx +428 -0
  38. package/src/frontend/pages/SetupWizard.tsx +181 -0
  39. package/src/frontend/pages/SwarmMonitorPanel.tsx +634 -0
  40. package/src/frontend/pages/SwarmPanel.tsx +322 -0
  41. package/src/frontend/pages/TasksPanel.tsx +535 -0
  42. package/src/frontend/pages/WebhooksPanel.tsx +335 -0
  43. package/src/frontend/pages/WorkflowsPanel.tsx +448 -0
  44. package/src/frontend/store.ts +185 -0
  45. package/src/frontend/styles/global.css +113 -0
  46. package/src/frontend/test-setup.ts +1 -0
  47. package/src/frontend/tour/TourContext.tsx +161 -0
  48. package/src/frontend/tour/tourSteps.ts +181 -0
  49. package/src/frontend/tour/tourStyles.css +116 -0
  50. package/src/frontend/types.ts +239 -0
  51. package/src/frontend/utils/formatTime.test.ts +83 -0
  52. package/src/frontend/utils/formatTime.ts +23 -0
  53. package/tsconfig.json +23 -0
  54. package/vite.config.ts +26 -0
  55. package/vitest.config.ts +17 -0
  56. package/{,+ +0 -0
@@ -0,0 +1,239 @@
1
+ export interface Agent {
2
+ id: string
3
+ name: string
4
+ type: string
5
+ status: 'idle' | 'running' | 'completed' | 'error' | 'terminated'
6
+ createdAt: string
7
+ lastActivity?: string
8
+ taskId?: string
9
+ currentTask?: string
10
+ currentAction?: string
11
+ metrics?: {
12
+ tasksCompleted: number
13
+ errorRate: number
14
+ avgResponseTime: number
15
+ }
16
+ }
17
+
18
+ export interface SwarmState {
19
+ id: string
20
+ topology: 'hierarchical' | 'mesh' | 'star' | 'ring' | 'hierarchical-mesh'
21
+ strategy: string
22
+ status: 'initializing' | 'active' | 'paused' | 'shutdown' | 'inactive'
23
+ maxAgents: number
24
+ activeAgents: number
25
+ agents: Agent[]
26
+ createdAt: string
27
+ }
28
+
29
+ export interface Task {
30
+ id: string
31
+ title: string
32
+ description: string
33
+ status: 'pending' | 'in_progress' | 'completed' | 'cancelled' | 'failed'
34
+ assignedTo?: string
35
+ priority: 'low' | 'normal' | 'high' | 'critical'
36
+ createdAt: string
37
+ completedAt?: string
38
+ result?: string
39
+ /** Working directory for agent execution */
40
+ cwd?: string
41
+ }
42
+
43
+ export interface MemoryEntry {
44
+ key: string
45
+ value: string
46
+ namespace: string
47
+ tags: string[]
48
+ ttl?: number
49
+ createdAt: string
50
+ updatedAt: string
51
+ }
52
+
53
+ export interface MemoryStats {
54
+ totalEntries: number
55
+ namespaces: string[]
56
+ storageSize: string
57
+ hnswEnabled: boolean
58
+ indexedVectors: number
59
+ }
60
+
61
+ export interface Session {
62
+ id: string
63
+ name: string
64
+ status: 'active' | 'saved' | 'restored'
65
+ createdAt: string
66
+ agentCount: number
67
+ taskCount: number
68
+ }
69
+
70
+ export interface HiveMindState {
71
+ status: 'inactive' | 'active' | 'consensus'
72
+ members: string[]
73
+ consensusProtocol: string
74
+ lastConsensus?: {
75
+ topic: string
76
+ result: string
77
+ timestamp: string
78
+ votes: Record<string, string>
79
+ }
80
+ }
81
+
82
+ export interface HealthCheck {
83
+ name: string
84
+ status: 'pass' | 'warn' | 'fail'
85
+ detail: string
86
+ }
87
+
88
+ export interface SystemHealth {
89
+ status: 'healthy' | 'degraded' | 'unhealthy'
90
+ uptime: string
91
+ cpu: number
92
+ memory: number
93
+ activeConnections: number
94
+ mcpStatus: string
95
+ checks?: HealthCheck[]
96
+ passed?: number
97
+ warnings?: number
98
+ }
99
+
100
+ export interface NeuralStatus {
101
+ enabled: boolean
102
+ models: Array<{
103
+ name: string
104
+ status: string
105
+ accuracy: number
106
+ lastTrained?: string
107
+ }>
108
+ trainingQueue: number
109
+ }
110
+
111
+ export interface PerformanceMetrics {
112
+ latency: { avg: number; p95: number; p99: number }
113
+ throughput: number
114
+ errorRate: number
115
+ activeRequests: number
116
+ history: Array<{
117
+ timestamp: string
118
+ latency: number
119
+ throughput: number
120
+ }>
121
+ }
122
+
123
+ export interface HookConfig {
124
+ name: string
125
+ type: string
126
+ enabled: boolean
127
+ trigger: string
128
+ command?: string
129
+ timeout?: number
130
+ lastRun?: string
131
+ runCount: number
132
+ }
133
+
134
+ export interface WorkflowDef {
135
+ id: string
136
+ name: string
137
+ status: 'draft' | 'running' | 'completed' | 'paused' | 'cancelled'
138
+ steps: Array<{
139
+ id: string
140
+ name: string
141
+ status: string
142
+ agent?: string
143
+ detail?: string
144
+ }>
145
+ createdAt: string
146
+ }
147
+
148
+ export interface CoordinationMetrics {
149
+ topology: string
150
+ nodes: number
151
+ syncLatency: number
152
+ consensusRounds: number
153
+ loadDistribution: Record<string, number>
154
+ }
155
+
156
+ export interface VizNode {
157
+ id: string
158
+ sessionId: string
159
+ agentId?: string
160
+ slug?: string
161
+ agentType?: string
162
+ status: 'active' | 'idle' | 'done' | 'error'
163
+ currentTool?: string
164
+ currentFile?: string
165
+ lastActivity?: string
166
+ taskId?: string
167
+ children: VizNode[]
168
+ }
169
+
170
+ export interface VizSession {
171
+ sessionId: string
172
+ taskId: string
173
+ tree: VizNode
174
+ startedAt: string
175
+ }
176
+
177
+ export interface SwarmAgent {
178
+ id: string
179
+ type: string
180
+ status: 'idle' | 'active' | 'working' | 'error' | 'healthy'
181
+ health: number
182
+ taskCount: number
183
+ createdAt: string
184
+ uptime?: number
185
+ memory?: { used: number; limit: number }
186
+ cpu?: number
187
+ tasks?: { active: number; queued: number; completed: number; failed: number }
188
+ latency?: { avg: number; p99: number }
189
+ errors?: { count: number }
190
+ currentTask?: string
191
+ currentAction?: string
192
+ }
193
+
194
+ export interface SwarmMonitorState {
195
+ swarmId: string
196
+ status: string
197
+ topology: string
198
+ objective: string
199
+ strategy: string
200
+ progress: number
201
+ agents: SwarmAgent[]
202
+ agentSummary: { total: number; active: number; idle: number; completed: number }
203
+ taskSummary: { total: number; completed: number; inProgress: number; pending: number }
204
+ metrics: { tokensUsed: number; avgResponseTime: string; successRate: string; elapsedTime: string }
205
+ coordination: { consensusRounds: number; messagesSent: number; conflictsResolved: number }
206
+ }
207
+
208
+ export interface WSMessage {
209
+ type: string
210
+ payload: unknown
211
+ timestamp: string
212
+ }
213
+
214
+ export interface WebhookEvent {
215
+ id: string
216
+ provider: 'github'
217
+ repo: string
218
+ event: string
219
+ title: string
220
+ body: string
221
+ url: string
222
+ number: number
223
+ author: string
224
+ labels: string[]
225
+ receivedAt: string
226
+ taskId?: string
227
+ status: 'received' | 'processing' | 'completed' | 'failed' | 'ignored'
228
+ }
229
+
230
+ export interface GitHubWebhookStatus {
231
+ enabled: boolean
232
+ hasToken: boolean
233
+ tokenPreview: string
234
+ webhookSecret: string
235
+ hasSecret: boolean
236
+ repos: string[]
237
+ autoAssign: boolean
238
+ taskTemplate: string
239
+ }
@@ -0,0 +1,83 @@
1
+ import { describe, it, expect, vi, afterEach } from 'vitest';
2
+ import { formatRelativeTime } from './formatTime';
3
+
4
+ describe('formatRelativeTime', () => {
5
+ afterEach(() => {
6
+ vi.restoreAllMocks();
7
+ });
8
+
9
+ it('returns "just now" for a timestamp less than 60 seconds ago', () => {
10
+ const now = Date.now();
11
+ vi.spyOn(Date, 'now').mockReturnValue(now);
12
+ const stamp = new Date(now - 30 * 1000).toISOString();
13
+ expect(formatRelativeTime(stamp)).toBe('just now');
14
+ });
15
+
16
+ it('returns "2 minutes ago" for 120 seconds ago', () => {
17
+ const now = Date.now();
18
+ vi.spyOn(Date, 'now').mockReturnValue(now);
19
+ const stamp = new Date(now - 120 * 1000).toISOString();
20
+ expect(formatRelativeTime(stamp)).toBe('2 minutes ago');
21
+ });
22
+
23
+ it('returns "1 hour ago" for 3600 seconds ago', () => {
24
+ const now = Date.now();
25
+ vi.spyOn(Date, 'now').mockReturnValue(now);
26
+ const stamp = new Date(now - 3600 * 1000).toISOString();
27
+ expect(formatRelativeTime(stamp)).toBe('1 hour ago');
28
+ });
29
+
30
+ it('returns "3 days ago" for 3*86400 seconds ago', () => {
31
+ const now = Date.now();
32
+ vi.spyOn(Date, 'now').mockReturnValue(now);
33
+ const stamp = new Date(now - 3 * 86400 * 1000).toISOString();
34
+ expect(formatRelativeTime(stamp)).toBe('3 days ago');
35
+ });
36
+
37
+ it('returns "just now" for invalid input', () => {
38
+ expect(formatRelativeTime('')).toBe('just now');
39
+ expect(formatRelativeTime('not-a-date')).toBe('just now');
40
+ });
41
+
42
+ it('returns singular "1 minute ago"', () => {
43
+ const now = Date.now();
44
+ vi.spyOn(Date, 'now').mockReturnValue(now);
45
+ const stamp = new Date(now - 60 * 1000).toISOString();
46
+ expect(formatRelativeTime(stamp)).toBe('1 minute ago');
47
+ });
48
+
49
+ it('returns "just now" for a timestamp 0 seconds ago', () => {
50
+ const now = Date.now();
51
+ vi.spyOn(Date, 'now').mockReturnValue(now);
52
+ const stamp = new Date(now).toISOString();
53
+ expect(formatRelativeTime(stamp)).toBe('just now');
54
+ });
55
+
56
+ it('returns "1 day ago" for exactly 86400 seconds ago', () => {
57
+ const now = Date.now();
58
+ vi.spyOn(Date, 'now').mockReturnValue(now);
59
+ const stamp = new Date(now - 86400 * 1000).toISOString();
60
+ expect(formatRelativeTime(stamp)).toBe('1 day ago');
61
+ });
62
+
63
+ it('returns "1 month ago" for 45 days ago', () => {
64
+ const now = Date.now();
65
+ vi.spyOn(Date, 'now').mockReturnValue(now);
66
+ const stamp = new Date(now - 45 * 86400 * 1000).toISOString();
67
+ expect(formatRelativeTime(stamp)).toBe('1 month ago');
68
+ });
69
+
70
+ it('returns "1 year ago" for 400 days ago', () => {
71
+ const now = Date.now();
72
+ vi.spyOn(Date, 'now').mockReturnValue(now);
73
+ const stamp = new Date(now - 400 * 86400 * 1000).toISOString();
74
+ expect(formatRelativeTime(stamp)).toBe('1 year ago');
75
+ });
76
+
77
+ it('returns "just now" for future timestamps', () => {
78
+ const now = Date.now();
79
+ vi.spyOn(Date, 'now').mockReturnValue(now);
80
+ const stamp = new Date(now + 60000).toISOString();
81
+ expect(formatRelativeTime(stamp)).toBe('just now');
82
+ });
83
+ });
@@ -0,0 +1,23 @@
1
+ export function formatRelativeTime(isoString: string): string {
2
+ const now = Date.now();
3
+ const then = new Date(isoString).getTime();
4
+ if (isNaN(then)) return 'just now';
5
+ const seconds = Math.floor((now - then) / 1000);
6
+
7
+ if (seconds < 60) return 'just now';
8
+
9
+ const minutes = Math.floor(seconds / 60);
10
+ if (minutes < 60) return `${minutes} ${minutes === 1 ? 'minute' : 'minutes'} ago`;
11
+
12
+ const hours = Math.floor(minutes / 60);
13
+ if (hours < 24) return `${hours} ${hours === 1 ? 'hour' : 'hours'} ago`;
14
+
15
+ const days = Math.floor(hours / 24);
16
+ if (days < 30) return `${days} ${days === 1 ? 'day' : 'days'} ago`;
17
+
18
+ const months = Math.floor(days / 30);
19
+ if (months < 12) return `${months} ${months === 1 ? 'month' : 'months'} ago`;
20
+
21
+ const years = Math.floor(months / 12);
22
+ return `${years} ${years === 1 ? 'year' : 'years'} ago`;
23
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2023", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "skipLibCheck": true,
7
+ "moduleResolution": "bundler",
8
+ "allowImportingTsExtensions": true,
9
+ "isolatedModules": true,
10
+ "moduleDetection": "force",
11
+ "noEmit": true,
12
+ "jsx": "react-jsx",
13
+ "esModuleInterop": true,
14
+ "strict": true,
15
+ "noUnusedLocals": false,
16
+ "noUnusedParameters": false,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "paths": {
19
+ "@/*": ["./src/frontend/*"]
20
+ }
21
+ },
22
+ "include": ["src"]
23
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import path from 'path'
4
+
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ root: '.',
8
+ resolve: {
9
+ alias: {
10
+ '@': path.resolve(__dirname, 'src/frontend'),
11
+ },
12
+ },
13
+ server: {
14
+ port: 5173,
15
+ proxy: {
16
+ '/api': 'http://localhost:3001',
17
+ '/ws': {
18
+ target: 'ws://localhost:3001',
19
+ ws: true,
20
+ },
21
+ },
22
+ },
23
+ build: {
24
+ outDir: 'dist',
25
+ },
26
+ })
@@ -0,0 +1,17 @@
1
+ import { defineConfig } from 'vitest/config'
2
+ import react from '@vitejs/plugin-react'
3
+ import path from 'path'
4
+
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ resolve: {
8
+ alias: {
9
+ '@': path.resolve(__dirname, 'src/frontend'),
10
+ },
11
+ },
12
+ test: {
13
+ environment: 'jsdom',
14
+ globals: true,
15
+ setupFiles: ['./src/frontend/test-setup.ts'],
16
+ },
17
+ })
package/{,+ ADDED
File without changes