Skip to content

Commit c8bc5ab

Browse files
authored
Create AgentManagement.tsx
1 parent 3312df1 commit c8bc5ab

File tree

1 file changed

+295
-0
lines changed

1 file changed

+295
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
// app/components/workbench/mobile/AgentManagement.tsx
2+
3+
import { memo, useCallback, useState, useEffect } from 'react';
4+
import { motion, AnimatePresence } from 'framer-motion';
5+
import { IconButton } from '~/components/ui/IconButton';
6+
import { createTermuxBridge } from '~/lib/runtime/android-termux-bridge';
7+
import type { AgentConfig, RAGConfig } from '~/lib/runtime/android-termux-bridge';
8+
9+
interface AgentNode {
10+
id: string;
11+
type: 'agent' | 'ragbot';
12+
status: 'active' | 'learning' | 'idle' | 'syncing';
13+
config: AgentConfig | RAGConfig;
14+
connections: string[];
15+
metrics: {
16+
discoveries: number;
17+
syncScore: number;
18+
lastUpdate: Date;
19+
resourceUsage: number;
20+
};
21+
}
22+
23+
interface AgentNetwork {
24+
id: string;
25+
name: string;
26+
description: string;
27+
nodes: AgentNode[];
28+
topology: 'mesh' | 'star' | 'hierarchical' | 'custom';
29+
}
30+
31+
export const AgentManagement = memo(() => {
32+
const [activeNetworks, setActiveNetworks] = useState<AgentNetwork[]>([]);
33+
const [selectedNetwork, setSelectedNetwork] = useState<string | null>(null);
34+
const [selectedNode, setSelectedNode] = useState<string | null>(null);
35+
const [viewMode, setViewMode] = useState<'list' | 'graph'>('list');
36+
37+
// Topology visualization settings
38+
const [networkScale, setNetworkScale] = useState(1);
39+
const [autoLayout, setAutoLayout] = useState(true);
40+
41+
const deployNewAgent = useCallback(async (config: AgentConfig) => {
42+
const termuxBridge = createTermuxBridge(window.webcontainer);
43+
const agentId = await termuxBridge.deployAgent(config);
44+
45+
// Update network with new agent
46+
setActiveNetworks(networks => networks.map(network => {
47+
if (network.id === selectedNetwork) {
48+
return {
49+
...network,
50+
nodes: [...network.nodes, {
51+
id: agentId,
52+
type: 'agent',
53+
status: 'active',
54+
config,
55+
connections: [],
56+
metrics: {
57+
discoveries: 0,
58+
syncScore: 1,
59+
lastUpdate: new Date(),
60+
resourceUsage: 0
61+
}
62+
}]
63+
};
64+
}
65+
return network;
66+
}));
67+
}, [selectedNetwork]);
68+
69+
const deployRAGBot = useCallback(async (config: RAGConfig) => {
70+
const termuxBridge = createTermuxBridge(window.webcontainer);
71+
const botId = await termuxBridge.createRAGBot(config);
72+
73+
setActiveNetworks(networks => networks.map(network => {
74+
if (network.id === selectedNetwork) {
75+
return {
76+
...network,
77+
nodes: [...network.nodes, {
78+
id: botId,
79+
type: 'ragbot',
80+
status: 'learning',
81+
config,
82+
connections: [],
83+
metrics: {
84+
discoveries: 0,
85+
syncScore: 1,
86+
lastUpdate: new Date(),
87+
resourceUsage: 0
88+
}
89+
}]
90+
};
91+
}
92+
return network;
93+
}));
94+
}, [selectedNetwork]);
95+
96+
return (
97+
<div className="flex h-full">
98+
{/* Network List Sidebar */}
99+
<div className="w-64 border-r border-bolt-elements-borderColor bg-bolt-elements-background-depth-2">
100+
<div className="p-4 border-b border-bolt-elements-borderColor">
101+
<button
102+
className="w-full px-3 py-2 text-sm bg-bolt-elements-item-backgroundAccent text-bolt-elements-item-contentAccent rounded-md hover:bg-bolt-elements-item-backgroundAccentHover"
103+
onClick={() => {
104+
// Create new network dialog
105+
}}
106+
>
107+
<div className="flex items-center gap-2">
108+
<div className="i-ph:network" />
109+
New Agent Network
110+
</div>
111+
</button>
112+
</div>
113+
114+
<div className="p-2">
115+
{activeNetworks.map(network => (
116+
<button
117+
key={network.id}
118+
className={`w-full px-3 py-2 text-sm rounded-md text-left ${
119+
selectedNetwork === network.id
120+
? 'bg-bolt-elements-item-backgroundActive'
121+
: 'hover:bg-bolt-elements-item-backgroundHover'
122+
}`}
123+
onClick={() => setSelectedNetwork(network.id)}
124+
>
125+
<div className="font-medium">{network.name}</div>
126+
<div className="text-xs text-bolt-elements-textTertiary">
127+
{network.nodes.length} nodes • {network.topology}
128+
</div>
129+
</button>
130+
))}
131+
</div>
132+
</div>
133+
134+
{/* Main Content */}
135+
<div className="flex-1 flex flex-col">
136+
{/* Toolbar */}
137+
<div className="p-2 border-b border-bolt-elements-borderColor bg-bolt-elements-background-depth-1">
138+
<div className="flex items-center justify-between">
139+
<div className="flex items-center gap-2">
140+
<IconButton
141+
icon="i-ph:list"
142+
title="List View"
143+
onClick={() => setViewMode('list')}
144+
className={viewMode === 'list' ? 'bg-bolt-elements-item-backgroundActive' : ''}
145+
/>
146+
<IconButton
147+
icon="i-ph:graph"
148+
title="Network Graph"
149+
onClick={() => setViewMode('graph')}
150+
className={viewMode === 'graph' ? 'bg-bolt-elements-item-backgroundActive' : ''}
151+
/>
152+
{viewMode === 'graph' && (
153+
<>
154+
<div className="h-4 w-px bg-bolt-elements-borderColor mx-2" />
155+
<IconButton
156+
icon="i-ph:arrows-in"
157+
title="Zoom Out"
158+
onClick={() => setNetworkScale(s => Math.max(0.5, s - 0.1))}
159+
/>
160+
<IconButton
161+
icon="i-ph:arrows-out"
162+
title="Zoom In"
163+
onClick={() => setNetworkScale(s => Math.min(2, s + 0.1))}
164+
/>
165+
<IconButton
166+
icon="i-ph:magic-wand"
167+
title="Auto Layout"
168+
onClick={() => setAutoLayout(a => !a)}
169+
className={autoLayout ? 'bg-bolt-elements-item-backgroundActive' : ''}
170+
/>
171+
</>
172+
)}
173+
</div>
174+
175+
<div className="flex items-center gap-2">
176+
<button
177+
className="px-3 py-1.5 text-sm bg-bolt-elements-background-depth-2 text-bolt-elements-textPrimary rounded-md hover:bg-bolt-elements-item-backgroundHover"
178+
onClick={() => deployNewAgent({
179+
id: `agent-${Date.now()}`,
180+
role: 'researcher',
181+
capabilities: ['search', 'analyze', 'summarize'],
182+
dependencies: []
183+
})}
184+
>
185+
<div className="flex items-center gap-2">
186+
<div className="i-ph:robot" />
187+
New Agent
188+
</div>
189+
</button>
190+
<button
191+
className="px-3 py-1.5 text-sm bg-bolt-elements-background-depth-2 text-bolt-elements-textPrimary rounded-md hover:bg-bolt-elements-item-backgroundHover"
192+
onClick={() => deployRAGBot({
193+
id: `rag-${Date.now()}`,
194+
storage: {
195+
type: 'team',
196+
path: '/knowledge'
197+
},
198+
indexType: 'hierarchical',
199+
updateFrequency: 'realtime'
200+
})}
201+
>
202+
<div className="flex items-center gap-2">
203+
<div className="i-ph:brain" />
204+
New RAGBot
205+
</div>
206+
</button>
207+
</div>
208+
</div>
209+
</div>
210+
211+
{/* Network Content */}
212+
<div className="flex-1 overflow-auto p-4">
213+
{viewMode === 'list' ? (
214+
<div className="grid grid-cols-2 gap-4">
215+
{selectedNetwork && activeNetworks
216+
.find(n => n.id === selectedNetwork)
217+
?.nodes.map(node => (
218+
<div
219+
key={node.id}
220+
className="p-4 bg-bolt-elements-background-depth-2 rounded-lg border border-bolt-elements-borderColor"
221+
>
222+
<div className="flex items-start justify-between">
223+
<div>
224+
<div className="flex items-center gap-2">
225+
<div className={`i-ph:${node.type === 'agent' ? 'robot' : 'brain'} text-xl`} />
226+
<div className="font-medium">{node.config.id}</div>
227+
</div>
228+
<div className="text-sm text-bolt-elements-textTertiary mt-1">
229+
{node.type === 'agent'
230+
? (node.config as AgentConfig).role
231+
: (node.config as RAGConfig).storage.type}
232+
</div>
233+
</div>
234+
<div className={`px-2 py-1 text-xs rounded-full ${
235+
node.status === 'active' ? 'bg-green-100 text-green-800' :
236+
node.status === 'learning' ? 'bg-blue-100 text-blue-800' :
237+
node.status === 'syncing' ? 'bg-purple-100 text-purple-800' :
238+
'bg-gray-100 text-gray-800'
239+
}`}>
240+
{node.status}
241+
</div>
242+
</div>
243+
244+
<div className="mt-4 space-y-2">
245+
<div className="flex items-center justify-between text-sm">
246+
<span className="text-bolt-elements-textSecondary">Discoveries</span>
247+
<span>{node.metrics.discoveries}</span>
248+
</div>
249+
<div className="flex items-center justify-between text-sm">
250+
<span className="text-bolt-elements-textSecondary">Sync Score</span>
251+
<span>{node.metrics.syncScore.toFixed(2)}</span>
252+
</div>
253+
<div className="flex items-center justify-between text-sm">
254+
<span className="text-bolt-elements-textSecondary">Last Update</span>
255+
<span>{node.metrics.lastUpdate.toLocaleTimeString()}</span>
256+
</div>
257+
<div className="flex items-center justify-between text-sm">
258+
<span className="text-bolt-elements-textSecondary">Resource Usage</span>
259+
<span>{node.metrics.resourceUsage}%</span>
260+
</div>
261+
</div>
262+
263+
<div className="mt-4 flex items-center gap-2">
264+
<IconButton
265+
icon="i-ph:gear"
266+
title="Configure"
267+
onClick={() => setSelectedNode(node.id)}
268+
/>
269+
<IconButton
270+
icon="i-ph:arrows-out"
271+
title="View Details"
272+
onClick={() => {/* Open details panel */}}
273+
/>
274+
<IconButton
275+
icon={node.status === 'active' ? 'i-ph:pause' : 'i-ph:play'}
276+
title={node.status === 'active' ? 'Pause' : 'Resume'}
277+
onClick={() => {/* Toggle status */}}
278+
/>
279+
</div>
280+
</div>
281+
))}
282+
</div>
283+
) : (
284+
<div className="h-full">
285+
{/* Network Graph Visualization */}
286+
{/* TODO: Implement D3 or similar for network visualization */}
287+
</div>
288+
)}
289+
</div>
290+
</div>
291+
</div>
292+
);
293+
});
294+
295+
export default AgentManagement;

0 commit comments

Comments
 (0)