@synergenius/flow-weaver-pack-weaver 0.9.62 → 0.9.78
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/dist/ai-chat-provider.d.ts +12 -0
- package/dist/ai-chat-provider.d.ts.map +1 -1
- package/dist/ai-chat-provider.js +173 -19
- package/dist/ai-chat-provider.js.map +1 -1
- package/dist/bot/agent-loop.d.ts +20 -0
- package/dist/bot/agent-loop.d.ts.map +1 -0
- package/dist/bot/agent-loop.js +331 -0
- package/dist/bot/agent-loop.js.map +1 -0
- package/dist/bot/ai-router.d.ts +19 -0
- package/dist/bot/ai-router.d.ts.map +1 -0
- package/dist/bot/ai-router.js +104 -0
- package/dist/bot/ai-router.js.map +1 -0
- package/dist/bot/bot-registry.js +2 -2
- package/dist/bot/bot-registry.js.map +1 -1
- package/dist/bot/conversation-store.d.ts +1 -0
- package/dist/bot/conversation-store.d.ts.map +1 -1
- package/dist/bot/conversation-store.js.map +1 -1
- package/dist/bot/improve-loop.js.map +1 -1
- package/dist/bot/instance-manager.d.ts +31 -0
- package/dist/bot/instance-manager.d.ts.map +1 -0
- package/dist/bot/instance-manager.js +115 -0
- package/dist/bot/instance-manager.js.map +1 -0
- package/dist/bot/orchestrator.d.ts +36 -0
- package/dist/bot/orchestrator.d.ts.map +1 -0
- package/dist/bot/orchestrator.js +176 -0
- package/dist/bot/orchestrator.js.map +1 -0
- package/dist/bot/profile-store.d.ts +36 -0
- package/dist/bot/profile-store.d.ts.map +1 -0
- package/dist/bot/profile-store.js +208 -0
- package/dist/bot/profile-store.js.map +1 -0
- package/dist/bot/profile-types.d.ts +126 -0
- package/dist/bot/profile-types.d.ts.map +1 -0
- package/dist/bot/profile-types.js +7 -0
- package/dist/bot/profile-types.js.map +1 -0
- package/dist/bot/session-state.d.ts +25 -0
- package/dist/bot/session-state.d.ts.map +1 -0
- package/dist/bot/session-state.js +110 -0
- package/dist/bot/session-state.js.map +1 -0
- package/dist/bot/swarm-controller.d.ts +37 -21
- package/dist/bot/swarm-controller.d.ts.map +1 -1
- package/dist/bot/swarm-controller.js +344 -163
- package/dist/bot/swarm-controller.js.map +1 -1
- package/dist/bot/task-prompt-builder.d.ts +2 -1
- package/dist/bot/task-prompt-builder.d.ts.map +1 -1
- package/dist/bot/task-prompt-builder.js +33 -10
- package/dist/bot/task-prompt-builder.js.map +1 -1
- package/dist/bot/task-queue.d.ts +46 -0
- package/dist/bot/task-queue.d.ts.map +1 -0
- package/dist/bot/task-queue.js +237 -0
- package/dist/bot/task-queue.js.map +1 -0
- package/dist/bot/task-store.d.ts +1 -6
- package/dist/bot/task-store.d.ts.map +1 -1
- package/dist/bot/task-store.js +27 -78
- package/dist/bot/task-store.js.map +1 -1
- package/dist/bot/task-types.d.ts +8 -4
- package/dist/bot/task-types.d.ts.map +1 -1
- package/dist/cli-handlers.d.ts.map +1 -1
- package/dist/cli-handlers.js +2 -3
- package/dist/cli-handlers.js.map +1 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +749 -0
- package/dist/cli.js.map +1 -0
- package/dist/docs/docs/weaver-bot-usage.md +35 -18
- package/dist/docs/docs/weaver-config.md +20 -0
- package/dist/docs/docs/weaver-task-queue.md +31 -19
- package/dist/docs/weaver-config.md +15 -9
- package/dist/mcp-tools.d.ts +17 -0
- package/dist/mcp-tools.d.ts.map +1 -1
- package/dist/mcp-tools.js +98 -232
- package/dist/mcp-tools.js.map +1 -1
- package/dist/node-types/orchestrator-dispatch.d.ts +17 -0
- package/dist/node-types/orchestrator-dispatch.d.ts.map +1 -0
- package/dist/node-types/orchestrator-dispatch.js +63 -0
- package/dist/node-types/orchestrator-dispatch.js.map +1 -0
- package/dist/node-types/orchestrator-load-state.d.ts +16 -0
- package/dist/node-types/orchestrator-load-state.d.ts.map +1 -0
- package/dist/node-types/orchestrator-load-state.js +60 -0
- package/dist/node-types/orchestrator-load-state.js.map +1 -0
- package/dist/node-types/orchestrator-route.d.ts +16 -0
- package/dist/node-types/orchestrator-route.d.ts.map +1 -0
- package/dist/node-types/orchestrator-route.js +28 -0
- package/dist/node-types/orchestrator-route.js.map +1 -0
- package/dist/node-types/receive-task.d.ts +2 -3
- package/dist/node-types/receive-task.d.ts.map +1 -1
- package/dist/node-types/receive-task.js +3 -28
- package/dist/node-types/receive-task.js.map +1 -1
- package/dist/templates/weaver-template.d.ts +11 -0
- package/dist/templates/weaver-template.d.ts.map +1 -0
- package/dist/templates/weaver-template.js +53 -0
- package/dist/templates/weaver-template.js.map +1 -0
- package/dist/ui/bot-constants.d.ts +14 -0
- package/dist/ui/bot-constants.d.ts.map +1 -0
- package/dist/ui/bot-constants.js +189 -0
- package/dist/ui/bot-constants.js.map +1 -0
- package/dist/ui/bot-panel.js +51 -90
- package/dist/ui/bot-slot-card.js +87 -122
- package/dist/ui/budget-bar.js +5 -3
- package/dist/ui/chat-task-result.js +4 -7
- package/dist/ui/decision-log.js +136 -0
- package/dist/ui/profile-card.js +158 -0
- package/dist/ui/profile-editor.js +597 -0
- package/dist/ui/swarm-controls.js +36 -27
- package/dist/ui/swarm-dashboard.js +2034 -736
- package/dist/ui/task-create-form.js +39 -116
- package/dist/ui/task-detail-view.js +490 -239
- package/dist/ui/task-pool-list.js +69 -94
- package/dist/workflows/orchestrator.d.ts +21 -0
- package/dist/workflows/orchestrator.d.ts.map +1 -0
- package/dist/workflows/orchestrator.js +281 -0
- package/dist/workflows/orchestrator.js.map +1 -0
- package/dist/workflows/weaver-bot-session.d.ts +65 -0
- package/dist/workflows/weaver-bot-session.d.ts.map +1 -0
- package/dist/workflows/weaver-bot-session.js +68 -0
- package/dist/workflows/weaver-bot-session.js.map +1 -0
- package/dist/workflows/weaver.d.ts +24 -0
- package/dist/workflows/weaver.d.ts.map +1 -0
- package/dist/workflows/weaver.js +28 -0
- package/dist/workflows/weaver.js.map +1 -0
- package/flowweaver.manifest.json +253 -66
- package/package.json +1 -1
- package/src/ai-chat-provider.ts +184 -18
- package/src/bot/ai-router.ts +132 -0
- package/src/bot/bot-registry.ts +2 -2
- package/src/bot/conversation-store.ts +2 -1
- package/src/bot/improve-loop.ts +6 -6
- package/src/bot/instance-manager.ts +128 -0
- package/src/bot/orchestrator.ts +244 -0
- package/src/bot/profile-store.ts +225 -0
- package/src/bot/profile-types.ts +141 -0
- package/src/bot/swarm-controller.ts +385 -186
- package/src/bot/task-prompt-builder.ts +37 -6
- package/src/bot/task-store.ts +28 -89
- package/src/bot/task-types.ts +10 -4
- package/src/cli-handlers.ts +2 -3
- package/src/docs/weaver-bot-usage.md +35 -18
- package/src/docs/weaver-config.md +20 -0
- package/src/docs/weaver-task-queue.md +31 -19
- package/src/mcp-tools.ts +129 -320
- package/src/node-types/orchestrator-dispatch.ts +71 -0
- package/src/node-types/orchestrator-load-state.ts +66 -0
- package/src/node-types/orchestrator-route.ts +33 -0
- package/src/node-types/receive-task.ts +3 -26
- package/src/ui/bot-constants.ts +192 -0
- package/src/ui/bot-panel.tsx +55 -79
- package/src/ui/bot-slot-card.tsx +69 -117
- package/src/ui/budget-bar.tsx +5 -3
- package/src/ui/chat-task-result.tsx +6 -9
- package/src/ui/decision-log.tsx +148 -0
- package/src/ui/profile-card.tsx +157 -0
- package/src/ui/profile-editor.tsx +384 -0
- package/src/ui/swarm-controls.tsx +35 -31
- package/src/ui/swarm-dashboard.tsx +409 -80
- package/src/ui/task-create-form.tsx +29 -119
- package/src/ui/task-detail-view.tsx +461 -215
- package/src/ui/task-pool-list.tsx +74 -95
- package/src/workflows/orchestrator.ts +302 -0
- package/dist/docs/weaver-bot-usage.md +0 -34
- package/dist/docs/weaver-genesis.md +0 -32
- package/dist/docs/weaver-task-queue.md +0 -34
- package/src/bot/error-guide.ts +0 -4
- package/src/bot/retry-utils.ts +0 -4
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ProfileEditor — unified component for creating and editing bot profiles.
|
|
3
|
+
*
|
|
4
|
+
* Handles both modes:
|
|
5
|
+
* - Create: all fields start empty/default, saves via fw_weaver_profile_create.
|
|
6
|
+
* - Edit: loads profile by ID on mount, saves via fw_weaver_profile_update.
|
|
7
|
+
*
|
|
8
|
+
* Calls tool functions internally via usePackWorkspace.
|
|
9
|
+
*
|
|
10
|
+
* Pattern: CommonJS require for platform deps, ESM import for local files,
|
|
11
|
+
* React.createElement throughout, module.exports at end.
|
|
12
|
+
*/
|
|
13
|
+
const React = require('react');
|
|
14
|
+
const { useState, useEffect, useCallback } = React;
|
|
15
|
+
const {
|
|
16
|
+
Flex, Typography, Input, Button, IconButton, Icon, SectionTitle, Checkbox, Field,
|
|
17
|
+
IconPicker, ColorPicker, toast, usePackWorkspace,
|
|
18
|
+
} = require('@fw/plugin-ui-kit');
|
|
19
|
+
|
|
20
|
+
import { ICON_CATALOG, BOT_COLORS } from './bot-constants';
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Types
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
interface ProfileEditorProps {
|
|
27
|
+
mode: 'create' | 'edit';
|
|
28
|
+
profileId?: string;
|
|
29
|
+
bots: Array<{ id: string; name: string; icon?: string; color?: string }>;
|
|
30
|
+
onSave: () => void;
|
|
31
|
+
onCancel: () => void;
|
|
32
|
+
onDelete?: () => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Component
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }: ProfileEditorProps) {
|
|
40
|
+
const ctx = usePackWorkspace();
|
|
41
|
+
const { callTool } = ctx;
|
|
42
|
+
|
|
43
|
+
// --- Field state ---
|
|
44
|
+
const [name, setName] = useState('');
|
|
45
|
+
const [description, setDescription] = useState('');
|
|
46
|
+
const [botId, setBotId] = useState(mode === 'create' && bots.length > 0 ? bots[0].id : '');
|
|
47
|
+
const [icon, setIcon] = useState('smartToy');
|
|
48
|
+
const [color, setColor] = useState('color-node-blue-icon');
|
|
49
|
+
const [costStrategy, setCostStrategy] = useState<'frugal' | 'balanced' | 'performance'>('balanced');
|
|
50
|
+
const [capabilities, setCapabilities] = useState<Array<{ name: string; description: string }>>([]);
|
|
51
|
+
const [capName, setCapName] = useState('');
|
|
52
|
+
const [capDescription, setCapDescription] = useState('');
|
|
53
|
+
const [instructions, setInstructions] = useState('');
|
|
54
|
+
const [requireApproval, setRequireApproval] = useState(false);
|
|
55
|
+
const [loading, setLoading] = useState(mode === 'edit');
|
|
56
|
+
|
|
57
|
+
// --- Load profile for edit mode ---
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (mode !== 'edit' || !profileId) return;
|
|
60
|
+
let cancelled = false;
|
|
61
|
+
|
|
62
|
+
(async () => {
|
|
63
|
+
try {
|
|
64
|
+
const raw = await callTool('fw_weaver_profile_list', {});
|
|
65
|
+
if (cancelled) return;
|
|
66
|
+
const data = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
67
|
+
const list = Array.isArray(data) ? data : [];
|
|
68
|
+
const profile = list.find((p: Record<string, unknown>) => p.id === profileId);
|
|
69
|
+
if (!profile) {
|
|
70
|
+
toast('Profile not found', { type: 'error' });
|
|
71
|
+
onCancel();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
setName((profile.name as string) || '');
|
|
75
|
+
setDescription((profile.description as string) || '');
|
|
76
|
+
setBotId((profile.botId as string) || '');
|
|
77
|
+
setIcon((profile.icon as string) || 'smartToy');
|
|
78
|
+
setColor((profile.color as string) || 'color-node-blue-icon');
|
|
79
|
+
const prefs = (profile.preferences || {}) as Record<string, unknown>;
|
|
80
|
+
setCostStrategy((prefs.costStrategy as 'frugal' | 'balanced' | 'performance') || 'balanced');
|
|
81
|
+
setCapabilities(
|
|
82
|
+
Array.isArray(profile.capabilities)
|
|
83
|
+
? (profile.capabilities as Array<{ name: string; description: string }>)
|
|
84
|
+
: [],
|
|
85
|
+
);
|
|
86
|
+
setInstructions((prefs.instructions as string) || '');
|
|
87
|
+
setRequireApproval(!!(prefs.requireApproval));
|
|
88
|
+
} catch {
|
|
89
|
+
toast('Failed to load profile', { type: 'error' });
|
|
90
|
+
onCancel();
|
|
91
|
+
} finally {
|
|
92
|
+
if (!cancelled) setLoading(false);
|
|
93
|
+
}
|
|
94
|
+
})();
|
|
95
|
+
|
|
96
|
+
return () => { cancelled = true; };
|
|
97
|
+
}, [mode, profileId, callTool, onCancel]);
|
|
98
|
+
|
|
99
|
+
// --- Capabilities ---
|
|
100
|
+
const handleAddCapability = useCallback(() => {
|
|
101
|
+
const trimName = capName.trim();
|
|
102
|
+
const trimDesc = capDescription.trim();
|
|
103
|
+
if (!trimName || !trimDesc) return;
|
|
104
|
+
setCapabilities((prev: Array<{ name: string; description: string }>) => [...prev, { name: trimName, description: trimDesc }]);
|
|
105
|
+
setCapName('');
|
|
106
|
+
setCapDescription('');
|
|
107
|
+
}, [capName, capDescription]);
|
|
108
|
+
|
|
109
|
+
const handleRemoveCapability = useCallback((index: number) => {
|
|
110
|
+
setCapabilities((prev: Array<{ name: string; description: string }>) => prev.filter((_: unknown, i: number) => i !== index));
|
|
111
|
+
}, []);
|
|
112
|
+
|
|
113
|
+
// --- Save ---
|
|
114
|
+
const handleSave = useCallback(async () => {
|
|
115
|
+
if (!name.trim()) {
|
|
116
|
+
toast('Profile name is required', { type: 'error' });
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (!botId) {
|
|
120
|
+
toast('Select a bot workflow', { type: 'error' });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (capabilities.length === 0) {
|
|
124
|
+
toast('Add at least one capability', { type: 'error' });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
if (mode === 'create') {
|
|
130
|
+
await callTool('fw_weaver_profile_create', {
|
|
131
|
+
name: name.trim(),
|
|
132
|
+
description: description.trim(),
|
|
133
|
+
botId,
|
|
134
|
+
icon,
|
|
135
|
+
color,
|
|
136
|
+
capabilities,
|
|
137
|
+
costStrategy,
|
|
138
|
+
instructions: instructions.trim() || undefined,
|
|
139
|
+
requireApproval,
|
|
140
|
+
});
|
|
141
|
+
toast('Profile created', { type: 'success' });
|
|
142
|
+
} else {
|
|
143
|
+
await callTool('fw_weaver_profile_update', {
|
|
144
|
+
id: profileId,
|
|
145
|
+
name: name.trim(),
|
|
146
|
+
description: description.trim(),
|
|
147
|
+
icon,
|
|
148
|
+
color,
|
|
149
|
+
capabilities,
|
|
150
|
+
costStrategy,
|
|
151
|
+
instructions: instructions.trim() || undefined,
|
|
152
|
+
requireApproval,
|
|
153
|
+
});
|
|
154
|
+
toast('Profile updated', { type: 'success' });
|
|
155
|
+
}
|
|
156
|
+
onSave();
|
|
157
|
+
} catch (err: unknown) {
|
|
158
|
+
toast(err instanceof Error ? err.message : `Failed to ${mode} profile`, { type: 'error' });
|
|
159
|
+
}
|
|
160
|
+
}, [mode, profileId, name, description, botId, icon, color, costStrategy, capabilities, instructions, requireApproval, callTool, onSave]);
|
|
161
|
+
|
|
162
|
+
// --- Delete ---
|
|
163
|
+
const handleDelete = useCallback(async () => {
|
|
164
|
+
if (!profileId) return;
|
|
165
|
+
const ok = await ctx.confirm('Are you sure you want to delete this profile?', {
|
|
166
|
+
title: 'Delete Profile',
|
|
167
|
+
confirmLabel: 'Delete',
|
|
168
|
+
state: 'danger',
|
|
169
|
+
});
|
|
170
|
+
if (!ok) return;
|
|
171
|
+
try {
|
|
172
|
+
await callTool('fw_weaver_profile_delete', { id: profileId });
|
|
173
|
+
toast('Profile deleted', { type: 'success' });
|
|
174
|
+
if (onDelete) onDelete();
|
|
175
|
+
} catch (err: unknown) {
|
|
176
|
+
toast(err instanceof Error ? err.message : 'Failed to delete profile', { type: 'error' });
|
|
177
|
+
}
|
|
178
|
+
}, [profileId, callTool, onDelete, ctx]);
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
// --- Loading state ---
|
|
183
|
+
if (loading) {
|
|
184
|
+
return React.createElement(Flex, {
|
|
185
|
+
variant: 'column-center-center-nowrap-12',
|
|
186
|
+
style: { padding: '24px 16px' },
|
|
187
|
+
},
|
|
188
|
+
React.createElement(Typography, { variant: 'caption-regular', color: 'color-text-subtle' }, 'Loading profile...'),
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// --- Render ---
|
|
193
|
+
return React.createElement(Flex, {
|
|
194
|
+
variant: 'column-stretch-start-nowrap-0',
|
|
195
|
+
style: { width: '100%', height: '100%', overflow: 'hidden' },
|
|
196
|
+
},
|
|
197
|
+
// ── Header bar ──
|
|
198
|
+
React.createElement(Flex, {
|
|
199
|
+
variant: 'row-center-space-between-nowrap-8',
|
|
200
|
+
style: { padding: '8px 16px', flexShrink: 0, borderBottom: '1px solid var(--color-border-default)' },
|
|
201
|
+
},
|
|
202
|
+
React.createElement(Flex, { variant: 'row-center-start-nowrap-8' },
|
|
203
|
+
React.createElement(IconButton, {
|
|
204
|
+
icon: 'back', size: 'xs', variant: 'clear',
|
|
205
|
+
onClick: onCancel,
|
|
206
|
+
title: 'Back',
|
|
207
|
+
}),
|
|
208
|
+
React.createElement(Typography, { variant: 'caption-thick', color: 'color-text-high' }, mode === 'create' ? 'Create Profile' : 'Edit Profile'),
|
|
209
|
+
),
|
|
210
|
+
mode === 'edit' && onDelete && React.createElement(IconButton, {
|
|
211
|
+
icon: 'outlinedDelete', size: 'sm', variant: 'clear', color: 'danger',
|
|
212
|
+
onClick: handleDelete,
|
|
213
|
+
title: 'Delete profile',
|
|
214
|
+
}),
|
|
215
|
+
),
|
|
216
|
+
|
|
217
|
+
// ── Scrollable form body ──
|
|
218
|
+
React.createElement(Flex, {
|
|
219
|
+
variant: 'column-stretch-start-nowrap-10',
|
|
220
|
+
style: { flex: 1, minHeight: 0, overflow: 'auto', padding: '12px 16px' },
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
// ── Name ──
|
|
224
|
+
React.createElement(Field, { label: 'Name' },
|
|
225
|
+
React.createElement(Input, {
|
|
226
|
+
type: 'text', size: 'small',
|
|
227
|
+
placeholder: 'Profile name',
|
|
228
|
+
value: name,
|
|
229
|
+
onChange: (v: string) => setName(v),
|
|
230
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
231
|
+
inputBoxStyle: { maxWidth: 'none' },
|
|
232
|
+
}),
|
|
233
|
+
),
|
|
234
|
+
|
|
235
|
+
// ── Description ──
|
|
236
|
+
React.createElement(Field, { label: 'Description' },
|
|
237
|
+
React.createElement(Input, {
|
|
238
|
+
type: 'text', size: 'small',
|
|
239
|
+
placeholder: 'What does this profile do?',
|
|
240
|
+
value: description,
|
|
241
|
+
onChange: (v: string) => setDescription(v),
|
|
242
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
243
|
+
inputBoxStyle: { maxWidth: 'none' },
|
|
244
|
+
}),
|
|
245
|
+
),
|
|
246
|
+
|
|
247
|
+
// ── Bot ──
|
|
248
|
+
React.createElement(Field, { label: 'Bot' },
|
|
249
|
+
React.createElement(Input, {
|
|
250
|
+
type: 'select', size: 'small',
|
|
251
|
+
options: bots.map((b: { id: string; name: string; icon?: string }) => ({ id: b.id, label: b.name, icon: b.icon || 'smartToy' })),
|
|
252
|
+
optionId: botId,
|
|
253
|
+
onChange: (id: string) => setBotId(id),
|
|
254
|
+
disabled: mode === 'edit',
|
|
255
|
+
placeholder: 'Select bot',
|
|
256
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
257
|
+
}),
|
|
258
|
+
),
|
|
259
|
+
|
|
260
|
+
// ── Icon ──
|
|
261
|
+
React.createElement(Field, { label: 'Icon', align: 'start' },
|
|
262
|
+
React.createElement(IconPicker, {
|
|
263
|
+
catalog: ICON_CATALOG,
|
|
264
|
+
value: icon,
|
|
265
|
+
onChange: (v: string) => setIcon(v),
|
|
266
|
+
accentColor: color,
|
|
267
|
+
variant: 'inline',
|
|
268
|
+
}),
|
|
269
|
+
),
|
|
270
|
+
|
|
271
|
+
// ── Color ──
|
|
272
|
+
React.createElement(Field, { label: 'Color', align: 'start' },
|
|
273
|
+
React.createElement(ColorPicker, {
|
|
274
|
+
colors: BOT_COLORS,
|
|
275
|
+
value: color,
|
|
276
|
+
onChange: (v: string) => setColor(v),
|
|
277
|
+
variant: 'inline',
|
|
278
|
+
}),
|
|
279
|
+
),
|
|
280
|
+
|
|
281
|
+
// ── Cost Strategy ──
|
|
282
|
+
React.createElement(Field, { label: 'Cost Strategy' },
|
|
283
|
+
React.createElement(Input, {
|
|
284
|
+
type: 'select', size: 'small',
|
|
285
|
+
options: [
|
|
286
|
+
{ id: 'frugal', label: 'Frugal', icon: 'timer' },
|
|
287
|
+
{ id: 'balanced', label: 'Balanced', icon: 'syncAlt' },
|
|
288
|
+
{ id: 'performance', label: 'Performance', icon: 'rocketLaunch' },
|
|
289
|
+
],
|
|
290
|
+
optionId: costStrategy,
|
|
291
|
+
onChange: (id: string) => setCostStrategy(id as 'frugal' | 'balanced' | 'performance'),
|
|
292
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
293
|
+
}),
|
|
294
|
+
),
|
|
295
|
+
|
|
296
|
+
// ── Capabilities ──
|
|
297
|
+
React.createElement(Field, { label: 'Capabilities', align: 'start' },
|
|
298
|
+
React.createElement(Flex, { variant: 'column-stretch-start-nowrap-6' },
|
|
299
|
+
// Add capability row
|
|
300
|
+
React.createElement(Flex, { variant: 'row-center-start-nowrap-4', style: { overflow: 'hidden' } },
|
|
301
|
+
React.createElement(Input, {
|
|
302
|
+
type: 'text', size: 'small',
|
|
303
|
+
placeholder: 'Name',
|
|
304
|
+
value: capName,
|
|
305
|
+
onChange: (v: string) => setCapName(v),
|
|
306
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
307
|
+
inputBoxStyle: { maxWidth: 'none' },
|
|
308
|
+
}),
|
|
309
|
+
React.createElement(Input, {
|
|
310
|
+
type: 'text', size: 'small',
|
|
311
|
+
placeholder: 'Description',
|
|
312
|
+
value: capDescription,
|
|
313
|
+
onChange: (v: string) => setCapDescription(v),
|
|
314
|
+
onEnter: handleAddCapability,
|
|
315
|
+
defaultBoxStyle: { flex: 2, minWidth: 0 },
|
|
316
|
+
inputBoxStyle: { maxWidth: 'none' },
|
|
317
|
+
}),
|
|
318
|
+
React.createElement(IconButton, {
|
|
319
|
+
icon: 'add', size: 'sm', variant: 'outlined', color: 'primary',
|
|
320
|
+
onClick: handleAddCapability,
|
|
321
|
+
disabled: !capName.trim() || !capDescription.trim(),
|
|
322
|
+
}),
|
|
323
|
+
),
|
|
324
|
+
// Capability list
|
|
325
|
+
...capabilities.map((cap: { name: string; description: string }, idx: number) =>
|
|
326
|
+
React.createElement(Flex, {
|
|
327
|
+
key: `${cap.name}-${idx}`,
|
|
328
|
+
variant: 'row-center-start-nowrap-6',
|
|
329
|
+
style: { paddingLeft: '4px' },
|
|
330
|
+
},
|
|
331
|
+
React.createElement(Typography, {
|
|
332
|
+
variant: 'smallCaption-regular', color: 'color-text-high',
|
|
333
|
+
style: { flexShrink: 0 },
|
|
334
|
+
}, `\u2022 ${cap.name}:`),
|
|
335
|
+
React.createElement(Typography, {
|
|
336
|
+
variant: 'smallCaption-regular', color: 'color-text-medium',
|
|
337
|
+
style: { flex: 1, minWidth: 0 },
|
|
338
|
+
}, cap.description),
|
|
339
|
+
React.createElement(IconButton, {
|
|
340
|
+
icon: 'close', size: 'xs', variant: 'clear', color: 'danger',
|
|
341
|
+
onClick: () => handleRemoveCapability(idx),
|
|
342
|
+
}),
|
|
343
|
+
),
|
|
344
|
+
),
|
|
345
|
+
),
|
|
346
|
+
),
|
|
347
|
+
|
|
348
|
+
// ── Instructions ──
|
|
349
|
+
React.createElement(Field, { label: 'Instructions' },
|
|
350
|
+
React.createElement(Input, {
|
|
351
|
+
type: 'text', size: 'small',
|
|
352
|
+
placeholder: 'Optional instructions for the bot',
|
|
353
|
+
value: instructions,
|
|
354
|
+
onChange: (v: string) => setInstructions(v),
|
|
355
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
356
|
+
inputBoxStyle: { maxWidth: 'none' },
|
|
357
|
+
}),
|
|
358
|
+
),
|
|
359
|
+
|
|
360
|
+
// ── Require Approval ──
|
|
361
|
+
React.createElement(Field, { label: '' },
|
|
362
|
+
React.createElement(Checkbox, {
|
|
363
|
+
checked: requireApproval,
|
|
364
|
+
onChange: (v: boolean) => setRequireApproval(v),
|
|
365
|
+
label: 'Require approval',
|
|
366
|
+
size: 'sm',
|
|
367
|
+
}),
|
|
368
|
+
),
|
|
369
|
+
|
|
370
|
+
// ── Save button ──
|
|
371
|
+
React.createElement(Flex, { variant: 'row-center-end-nowrap-8' },
|
|
372
|
+
React.createElement(Button, {
|
|
373
|
+
size: 'xs', variant: 'fill', color: 'primary',
|
|
374
|
+
onClick: handleSave,
|
|
375
|
+
disabled: !name.trim() || !botId || capabilities.length === 0,
|
|
376
|
+
}, mode === 'create' ? 'Create' : 'Save'),
|
|
377
|
+
),
|
|
378
|
+
),
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export { ProfileEditor };
|
|
383
|
+
export default ProfileEditor;
|
|
384
|
+
module.exports = ProfileEditor;
|
|
@@ -12,16 +12,16 @@ const {
|
|
|
12
12
|
// Types
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
|
|
15
|
-
interface
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
interface InstanceInfo {
|
|
16
|
+
instanceId: string;
|
|
17
|
+
profileId: string;
|
|
18
18
|
status: 'idle' | 'executing' | 'paused' | 'stopped';
|
|
19
19
|
currentTaskId?: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
interface SwarmStatus {
|
|
23
23
|
status: 'idle' | 'running' | 'paused' | 'stopping';
|
|
24
|
-
|
|
24
|
+
instances: Record<string, InstanceInfo>;
|
|
25
25
|
maxConcurrent: number;
|
|
26
26
|
tasksCompleted: number;
|
|
27
27
|
tasksFailed: number;
|
|
@@ -39,15 +39,13 @@ interface SwarmControlsProps {
|
|
|
39
39
|
// Helpers
|
|
40
40
|
// ---------------------------------------------------------------------------
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
function statusToVariant(status: string): StatusVariant {
|
|
42
|
+
function swarmStatusToIconStatus(status: string): 'pending' | 'running' | 'completed' | 'failed' {
|
|
45
43
|
switch (status) {
|
|
46
|
-
case 'running': return '
|
|
47
|
-
case 'paused': return '
|
|
48
|
-
case 'stopping': return '
|
|
44
|
+
case 'running': return 'running';
|
|
45
|
+
case 'paused': return 'pending';
|
|
46
|
+
case 'stopping': return 'failed';
|
|
49
47
|
case 'idle':
|
|
50
|
-
default: return '
|
|
48
|
+
default: return 'completed';
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
51
|
|
|
@@ -61,9 +59,9 @@ function statusLabel(status: string): string {
|
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
function
|
|
65
|
-
const entries = Object.values(
|
|
66
|
-
const active = entries.filter(
|
|
62
|
+
function countActiveInstances(instances: Record<string, InstanceInfo>): { active: number; total: number } {
|
|
63
|
+
const entries = Object.values(instances);
|
|
64
|
+
const active = entries.filter(i => i.status === 'executing').length;
|
|
67
65
|
return { active, total: entries.length };
|
|
68
66
|
}
|
|
69
67
|
|
|
@@ -78,8 +76,8 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
|
|
|
78
76
|
const [stopping, setStopping] = useState(false);
|
|
79
77
|
|
|
80
78
|
const status = swarmStatus?.status ?? 'idle';
|
|
81
|
-
const
|
|
82
|
-
const { active, total } =
|
|
79
|
+
const instances = swarmStatus?.instances ?? {};
|
|
80
|
+
const { active, total } = countActiveInstances(instances);
|
|
83
81
|
|
|
84
82
|
const isIdle = status === 'idle';
|
|
85
83
|
const isRunning = status === 'running';
|
|
@@ -144,6 +142,12 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
|
|
|
144
142
|
|
|
145
143
|
const handleStop = useCallback(async () => {
|
|
146
144
|
setStopping(true);
|
|
145
|
+
const ok = await ctx.confirm('This will stop all running instances and abort active tasks.', {
|
|
146
|
+
title: 'Stop Swarm',
|
|
147
|
+
confirmLabel: 'Stop',
|
|
148
|
+
state: 'danger',
|
|
149
|
+
});
|
|
150
|
+
if (!ok) { setStopping(false); return; }
|
|
147
151
|
try {
|
|
148
152
|
const result = await ctx.callTool('fw_weaver_swarm_stop', {});
|
|
149
153
|
const data = result as Record<string, unknown> | null;
|
|
@@ -167,9 +171,9 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
|
|
|
167
171
|
return React.createElement(Flex, {
|
|
168
172
|
variant: 'row-center-space-between-nowrap-10',
|
|
169
173
|
style: {
|
|
170
|
-
flexShrink: 0,
|
|
171
174
|
padding: '8px 16px',
|
|
172
175
|
borderBottom: '1px solid var(--color-border-default)',
|
|
176
|
+
flexShrink: 0,
|
|
173
177
|
backgroundColor: isRunning
|
|
174
178
|
? 'var(--color-brand-main-alpha-10)'
|
|
175
179
|
: isPaused
|
|
@@ -180,19 +184,18 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
|
|
|
180
184
|
// Left: status icon + label
|
|
181
185
|
React.createElement(Flex, { variant: 'row-center-start-nowrap-8', style: { flexShrink: 0 } },
|
|
182
186
|
React.createElement(StatusIcon, {
|
|
183
|
-
|
|
184
|
-
size:
|
|
187
|
+
status: swarmStatusToIconStatus(status),
|
|
188
|
+
size: 'sm',
|
|
185
189
|
}),
|
|
186
190
|
React.createElement(Typography, {
|
|
187
191
|
variant: 'caption-bold',
|
|
188
|
-
style: { textTransform: 'capitalize' },
|
|
189
192
|
}, statusLabel(status)),
|
|
190
193
|
),
|
|
191
194
|
|
|
192
195
|
// Center: bot count + stats
|
|
193
196
|
React.createElement(Flex, {
|
|
194
197
|
variant: 'row-center-start-nowrap-12',
|
|
195
|
-
style: { flex: 1
|
|
198
|
+
style: { flex: 1 },
|
|
196
199
|
},
|
|
197
200
|
total > 0 && React.createElement(Typography, {
|
|
198
201
|
variant: 'caption-regular',
|
|
@@ -209,11 +212,12 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
|
|
|
209
212
|
),
|
|
210
213
|
|
|
211
214
|
// Right: action buttons
|
|
212
|
-
React.createElement(Flex, { variant: 'row-center-start-nowrap-
|
|
215
|
+
React.createElement(Flex, { variant: 'row-center-start-nowrap-4', style: { flexShrink: 0 } },
|
|
213
216
|
// Start / Resume button
|
|
214
217
|
(isIdle || isPaused) && React.createElement(Button, {
|
|
215
|
-
size: '
|
|
216
|
-
variant: '
|
|
218
|
+
size: 'xs',
|
|
219
|
+
variant: 'outlined',
|
|
220
|
+
color: 'primary',
|
|
217
221
|
onClick: isPaused ? handleResume : handleStart,
|
|
218
222
|
loading: starting,
|
|
219
223
|
disabled: anyLoading,
|
|
@@ -221,8 +225,9 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
|
|
|
221
225
|
|
|
222
226
|
// Pause button (only when running)
|
|
223
227
|
isRunning && React.createElement(Button, {
|
|
224
|
-
size: '
|
|
225
|
-
variant: '
|
|
228
|
+
size: 'xs',
|
|
229
|
+
variant: 'outlined',
|
|
230
|
+
color: 'warning',
|
|
226
231
|
onClick: handlePause,
|
|
227
232
|
loading: pausing,
|
|
228
233
|
disabled: anyLoading,
|
|
@@ -230,8 +235,8 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
|
|
|
230
235
|
|
|
231
236
|
// Stop button (when running or paused)
|
|
232
237
|
(isRunning || isPaused) && React.createElement(Button, {
|
|
233
|
-
size: '
|
|
234
|
-
variant: '
|
|
238
|
+
size: 'xs',
|
|
239
|
+
variant: 'outlined',
|
|
235
240
|
color: 'danger',
|
|
236
241
|
onClick: handleStop,
|
|
237
242
|
loading: stopping,
|
|
@@ -240,12 +245,11 @@ function SwarmControls({ swarmStatus, onRefresh }: SwarmControlsProps) {
|
|
|
240
245
|
|
|
241
246
|
// Refresh button (always available)
|
|
242
247
|
React.createElement(IconButton, {
|
|
243
|
-
icon: '
|
|
248
|
+
icon: 'reset',
|
|
244
249
|
size: 'xs',
|
|
245
|
-
variant: '
|
|
250
|
+
variant: 'outlined',
|
|
246
251
|
onClick: onRefresh,
|
|
247
252
|
disabled: anyLoading,
|
|
248
|
-
title: 'Refresh swarm status',
|
|
249
253
|
}),
|
|
250
254
|
),
|
|
251
255
|
);
|