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.
- package/'1' +0 -0
- package/.env.example +46 -0
- package/CHANGELOG.md +87 -0
- package/CLAUDE.md +287 -0
- package/LICENSE +21 -0
- package/README.md +316 -0
- package/Webhooks) +0 -0
- package/docs/plans/2026-03-11-github-webhooks.md +957 -0
- package/docs/screenshot-swarm-monitor.png +0 -0
- package/frontend +0 -0
- package/index.html +13 -0
- package/package.json +56 -0
- package/public/vite.svg +4 -0
- package/src/backend/__tests__/webhook-github.test.ts +934 -0
- package/src/backend/jsonl-monitor.ts +430 -0
- package/src/backend/server.ts +2972 -0
- package/src/backend/telegram-bot.ts +511 -0
- package/src/backend/webhook-github.ts +350 -0
- package/src/frontend/App.tsx +461 -0
- package/src/frontend/api.ts +281 -0
- package/src/frontend/components/ErrorBoundary.tsx +98 -0
- package/src/frontend/components/Layout.tsx +431 -0
- package/src/frontend/components/ui/Button.tsx +111 -0
- package/src/frontend/components/ui/Card.tsx +51 -0
- package/src/frontend/components/ui/StatusBadge.tsx +60 -0
- package/src/frontend/main.tsx +63 -0
- package/src/frontend/pages/AgentVizPanel.tsx +428 -0
- package/src/frontend/pages/AgentsPanel.tsx +445 -0
- package/src/frontend/pages/ConfigPanel.tsx +661 -0
- package/src/frontend/pages/Dashboard.tsx +482 -0
- package/src/frontend/pages/HiveMindPanel.tsx +355 -0
- package/src/frontend/pages/HooksPanel.tsx +240 -0
- package/src/frontend/pages/LogsPanel.tsx +261 -0
- package/src/frontend/pages/MemoryPanel.tsx +444 -0
- package/src/frontend/pages/NeuralPanel.tsx +301 -0
- package/src/frontend/pages/PerformancePanel.tsx +198 -0
- package/src/frontend/pages/SessionsPanel.tsx +428 -0
- package/src/frontend/pages/SetupWizard.tsx +181 -0
- package/src/frontend/pages/SwarmMonitorPanel.tsx +634 -0
- package/src/frontend/pages/SwarmPanel.tsx +322 -0
- package/src/frontend/pages/TasksPanel.tsx +535 -0
- package/src/frontend/pages/WebhooksPanel.tsx +335 -0
- package/src/frontend/pages/WorkflowsPanel.tsx +448 -0
- package/src/frontend/store.ts +185 -0
- package/src/frontend/styles/global.css +113 -0
- package/src/frontend/test-setup.ts +1 -0
- package/src/frontend/tour/TourContext.tsx +161 -0
- package/src/frontend/tour/tourSteps.ts +181 -0
- package/src/frontend/tour/tourStyles.css +116 -0
- package/src/frontend/types.ts +239 -0
- package/src/frontend/utils/formatTime.test.ts +83 -0
- package/src/frontend/utils/formatTime.ts +23 -0
- package/tsconfig.json +23 -0
- package/vite.config.ts +26 -0
- package/vitest.config.ts +17 -0
- 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
|
+
})
|
package/vitest.config.ts
ADDED
|
@@ -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
|