@wipal/agent-team 1.2.0 → 1.2.2
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/package.json +1 -1
- package/src/ui/agents.html +187 -54
- package/src/ui/css/styles.css +71 -0
- package/src/ui/index.html +110 -40
package/package.json
CHANGED
package/src/ui/agents.html
CHANGED
|
@@ -26,15 +26,39 @@
|
|
|
26
26
|
|
|
27
27
|
<!-- Add Agent Button -->
|
|
28
28
|
<div class="actions-bar">
|
|
29
|
-
<button class="btn btn-primary" @click="
|
|
29
|
+
<button class="btn btn-primary" @click="openAddModal()">
|
|
30
30
|
➕ Add New Agent
|
|
31
31
|
</button>
|
|
32
32
|
</div>
|
|
33
33
|
|
|
34
34
|
<!-- Agents List -->
|
|
35
35
|
<section class="section">
|
|
36
|
-
<div id="agents-grid"
|
|
37
|
-
<div class="loading">Loading agents...</div>
|
|
36
|
+
<div id="agents-grid">
|
|
37
|
+
<div x-show="loadingAgents" class="loading">Loading agents...</div>
|
|
38
|
+
<div x-show="!loadingAgents && agents.length === 0" class="empty-state">
|
|
39
|
+
<p>No agents yet. <a href="#" @click.prevent="openAddModal()">Add your first agent</a></p>
|
|
40
|
+
</div>
|
|
41
|
+
<template x-for="agent in agents" :key="agent.name">
|
|
42
|
+
<div class="agent-card">
|
|
43
|
+
<div class="agent-header">
|
|
44
|
+
<span class="agent-name" x-text="agent.name"></span>
|
|
45
|
+
<span class="agent-role" x-text="agent.variants?.base_role || 'unknown'"></span>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="agent-variants">
|
|
48
|
+
<template x-for="(value, key) in agent.variants" :key="key">
|
|
49
|
+
<span class="variant-tag" x-text="key + ': ' + value"></span>
|
|
50
|
+
</template>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="agent-skills" x-show="agent.skills?.length">
|
|
53
|
+
<template x-for="skill in agent.skills" :key="skill">
|
|
54
|
+
<span class="skill-tag" x-text="skill"></span>
|
|
55
|
+
</template>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="agent-actions">
|
|
58
|
+
<button class="btn btn-danger" @click="deleteAgent(agent.name)">Delete</button>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</template>
|
|
38
62
|
</div>
|
|
39
63
|
</section>
|
|
40
64
|
|
|
@@ -49,42 +73,55 @@
|
|
|
49
73
|
</div>
|
|
50
74
|
|
|
51
75
|
<div class="modal-body">
|
|
52
|
-
<!-- Step 1:
|
|
76
|
+
<!-- Step 1: Agent Name -->
|
|
53
77
|
<div class="form-group">
|
|
54
|
-
<label>Agent Name
|
|
78
|
+
<label>Agent Name *</label>
|
|
55
79
|
<input type="text" x-model="newAgent.name" placeholder="my-agent"
|
|
56
80
|
class="input" pattern="[a-z0-9-]+">
|
|
57
81
|
<small class="hint">Use lowercase letters, numbers, and hyphens</small>
|
|
58
82
|
</div>
|
|
59
83
|
|
|
60
|
-
<!-- Step 2: Select Role -->
|
|
84
|
+
<!-- Step 2: Select Role (Dropdown) -->
|
|
61
85
|
<div class="form-group">
|
|
62
|
-
<label>Role
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
86
|
+
<label>Role *</label>
|
|
87
|
+
<select x-model="newAgent.role" class="input" @change="onRoleChange()" :disabled="loadingRoles">
|
|
88
|
+
<option value="">-- Select Role --</option>
|
|
89
|
+
<template x-for="role in roles" :key="role.name">
|
|
90
|
+
<option :value="role.name" x-text="role.name"></option>
|
|
91
|
+
</template>
|
|
92
|
+
</select>
|
|
93
|
+
<small class="hint" x-show="selectedRoleDescription" x-text="selectedRoleDescription"></small>
|
|
66
94
|
</div>
|
|
67
95
|
|
|
68
|
-
<!-- Step 3: Select Variants (
|
|
69
|
-
<div x-show="newAgent.role" x-cloak class="variants-section">
|
|
70
|
-
<h3>Select Variants</h3>
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
96
|
+
<!-- Step 3: Select Variants (Dropdowns) -->
|
|
97
|
+
<div x-show="newAgent.role && Object.keys(variantCategories).length > 0" x-cloak class="variants-section">
|
|
98
|
+
<h3>Select Variants (Optional)</h3>
|
|
99
|
+
<template x-for="(category, key) in variantCategories" :key="key">
|
|
100
|
+
<div class="form-group">
|
|
101
|
+
<label x-text="category.label"></label>
|
|
102
|
+
<select class="input" @change="selectVariant(key, $event.target.value)">
|
|
103
|
+
<option value="">-- Select --</option>
|
|
104
|
+
<template x-for="option in category.options" :key="option.value">
|
|
105
|
+
<option :value="option.value"
|
|
106
|
+
:selected="newAgent.variants[key] === option.value"
|
|
107
|
+
x-text="option.label"></option>
|
|
108
|
+
</template>
|
|
109
|
+
</select>
|
|
110
|
+
</div>
|
|
111
|
+
</template>
|
|
77
112
|
</div>
|
|
78
113
|
|
|
79
114
|
<!-- Step 4: Preview Skills -->
|
|
80
115
|
<div x-show="newAgent.role" x-cloak class="skills-preview">
|
|
81
116
|
<h3>Skills Preview</h3>
|
|
82
|
-
<div
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
117
|
+
<div x-show="loadingSkills" class="loading">Calculating skills...</div>
|
|
118
|
+
<div x-show="!loadingSkills && previewSkills.length > 0" class="skills-list">
|
|
119
|
+
<template x-for="skill in previewSkills" :key="skill">
|
|
120
|
+
<span class="skill-tag" x-text="skill"></span>
|
|
121
|
+
</template>
|
|
122
|
+
</div>
|
|
123
|
+
<div x-show="!loadingSkills && previewSkills.length === 0" class="hint">
|
|
124
|
+
Select variants to see included skills
|
|
88
125
|
</div>
|
|
89
126
|
</div>
|
|
90
127
|
</div>
|
|
@@ -92,8 +129,9 @@
|
|
|
92
129
|
<div class="modal-footer">
|
|
93
130
|
<button class="btn btn-secondary" @click="showAddModal = false">Cancel</button>
|
|
94
131
|
<button class="btn btn-primary" @click="createAgent()"
|
|
95
|
-
:disabled="!newAgent.name || !newAgent.role">
|
|
96
|
-
Create Agent
|
|
132
|
+
:disabled="!newAgent.name || !newAgent.role || creating">
|
|
133
|
+
<span x-show="!creating">Create Agent</span>
|
|
134
|
+
<span x-show="creating">Creating...</span>
|
|
97
135
|
</button>
|
|
98
136
|
</div>
|
|
99
137
|
</div>
|
|
@@ -104,27 +142,136 @@
|
|
|
104
142
|
function agentApp() {
|
|
105
143
|
return {
|
|
106
144
|
showAddModal: false,
|
|
145
|
+
loadingAgents: true,
|
|
146
|
+
loadingRoles: false,
|
|
147
|
+
loadingSkills: false,
|
|
148
|
+
creating: false,
|
|
149
|
+
agents: [],
|
|
150
|
+
roles: [],
|
|
151
|
+
variantCategories: {},
|
|
152
|
+
previewSkills: [],
|
|
107
153
|
newAgent: {
|
|
108
154
|
name: '',
|
|
109
155
|
role: '',
|
|
110
156
|
variants: {}
|
|
111
157
|
},
|
|
112
158
|
|
|
113
|
-
|
|
114
|
-
this.
|
|
115
|
-
// Trigger variant loading
|
|
116
|
-
htmx.trigger(document.body, 'roleSelected', { role });
|
|
159
|
+
async init() {
|
|
160
|
+
await this.loadAgents();
|
|
117
161
|
},
|
|
118
162
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
163
|
+
async loadAgents() {
|
|
164
|
+
this.loadingAgents = true;
|
|
165
|
+
try {
|
|
166
|
+
const response = await fetch('/api/agents');
|
|
167
|
+
const data = await response.json();
|
|
168
|
+
this.agents = data.agents || [];
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error('Failed to load agents:', error);
|
|
171
|
+
} finally {
|
|
172
|
+
this.loadingAgents = false;
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
async deleteAgent(name) {
|
|
177
|
+
if (!confirm(`Delete agent "${name}"?`)) return;
|
|
178
|
+
try {
|
|
179
|
+
const response = await fetch(`/api/agents/${name}`, { method: 'DELETE' });
|
|
180
|
+
if (response.ok) {
|
|
181
|
+
await this.loadAgents();
|
|
182
|
+
} else {
|
|
183
|
+
const error = await response.json();
|
|
184
|
+
alert('Failed to delete: ' + error.error);
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
alert('Failed to delete agent: ' + error.message);
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
get selectedRoleDescription() {
|
|
192
|
+
const role = this.roles.find(r => r.name === this.newAgent.role);
|
|
193
|
+
return role ? role.description : '';
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
async openAddModal() {
|
|
197
|
+
this.showAddModal = true;
|
|
198
|
+
await this.loadRoles();
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
async loadRoles() {
|
|
202
|
+
if (this.roles.length > 0) return; // Already loaded
|
|
203
|
+
|
|
204
|
+
this.loadingRoles = true;
|
|
205
|
+
try {
|
|
206
|
+
const response = await fetch('/api/agents/meta/roles');
|
|
207
|
+
this.roles = await response.json();
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error('Failed to load roles:', error);
|
|
210
|
+
} finally {
|
|
211
|
+
this.loadingRoles = false;
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
async onRoleChange() {
|
|
216
|
+
// Reset variants and skills
|
|
217
|
+
this.newAgent.variants = {};
|
|
218
|
+
this.previewSkills = [];
|
|
219
|
+
this.variantCategories = {};
|
|
220
|
+
|
|
221
|
+
if (!this.newAgent.role) return;
|
|
222
|
+
|
|
223
|
+
// Load variants for selected role
|
|
224
|
+
await this.loadVariants(this.newAgent.role);
|
|
225
|
+
// Load skills preview
|
|
226
|
+
await this.loadSkillsPreview();
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
async loadVariants(role) {
|
|
230
|
+
try {
|
|
231
|
+
const response = await fetch(`/api/agents/meta/variants/${role}`);
|
|
232
|
+
const data = await response.json();
|
|
233
|
+
this.variantCategories = data.categories || data;
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.error('Failed to load variants:', error);
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
async selectVariant(category, value) {
|
|
240
|
+
if (value) {
|
|
123
241
|
this.newAgent.variants[category] = value;
|
|
242
|
+
} else {
|
|
243
|
+
delete this.newAgent.variants[category];
|
|
244
|
+
}
|
|
245
|
+
// Update skills preview
|
|
246
|
+
await this.loadSkillsPreview();
|
|
247
|
+
},
|
|
248
|
+
|
|
249
|
+
async loadSkillsPreview() {
|
|
250
|
+
if (!this.newAgent.role) return;
|
|
251
|
+
|
|
252
|
+
this.loadingSkills = true;
|
|
253
|
+
try {
|
|
254
|
+
const response = await fetch('/api/agents/preview-skills', {
|
|
255
|
+
method: 'POST',
|
|
256
|
+
headers: { 'Content-Type': 'application/json' },
|
|
257
|
+
body: JSON.stringify({
|
|
258
|
+
role: this.newAgent.role,
|
|
259
|
+
variants: this.newAgent.variants
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
const data = await response.json();
|
|
263
|
+
this.previewSkills = data.skills || data.core?.concat(data.role || []) || [];
|
|
264
|
+
} catch (error) {
|
|
265
|
+
console.error('Failed to load skills preview:', error);
|
|
266
|
+
} finally {
|
|
267
|
+
this.loadingSkills = false;
|
|
124
268
|
}
|
|
125
269
|
},
|
|
126
270
|
|
|
127
271
|
async createAgent() {
|
|
272
|
+
if (!this.newAgent.name || !this.newAgent.role) return;
|
|
273
|
+
|
|
274
|
+
this.creating = true;
|
|
128
275
|
try {
|
|
129
276
|
const response = await fetch('/api/agents', {
|
|
130
277
|
method: 'POST',
|
|
@@ -135,7 +282,7 @@
|
|
|
135
282
|
if (response.ok) {
|
|
136
283
|
this.showAddModal = false;
|
|
137
284
|
this.resetForm();
|
|
138
|
-
|
|
285
|
+
await this.loadAgents();
|
|
139
286
|
alert('Agent created successfully!');
|
|
140
287
|
} else {
|
|
141
288
|
const error = await response.json();
|
|
@@ -143,32 +290,18 @@
|
|
|
143
290
|
}
|
|
144
291
|
} catch (error) {
|
|
145
292
|
alert('Error creating agent: ' + error.message);
|
|
293
|
+
} finally {
|
|
294
|
+
this.creating = false;
|
|
146
295
|
}
|
|
147
296
|
},
|
|
148
297
|
|
|
149
298
|
resetForm() {
|
|
150
299
|
this.newAgent = { name: '', role: '', variants: {} };
|
|
300
|
+
this.variantCategories = {};
|
|
301
|
+
this.previewSkills = [];
|
|
151
302
|
}
|
|
152
303
|
}
|
|
153
304
|
}
|
|
154
|
-
|
|
155
|
-
// Transform role list to radio buttons
|
|
156
|
-
document.body.addEventListener('htmx:beforeSwap', function(evt) {
|
|
157
|
-
if (evt.detail.pathInfo.requestPath.includes('/meta/roles')) {
|
|
158
|
-
try {
|
|
159
|
-
const roles = JSON.parse(evt.detail.xhr.response);
|
|
160
|
-
evt.detail.xhr.response = roles.map(r => `
|
|
161
|
-
<label class="role-option" :class="{'selected': newAgent.role === '${r.name}'}">
|
|
162
|
-
<input type="radio" name="role" value="${r.name}" @click="selectRole('${r.name}')">
|
|
163
|
-
<div class="role-info">
|
|
164
|
-
<strong>${r.name}</strong>
|
|
165
|
-
<p>${r.description}</p>
|
|
166
|
-
</div>
|
|
167
|
-
</label>
|
|
168
|
-
`).join('');
|
|
169
|
-
} catch (e) {}
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
305
|
</script>
|
|
173
306
|
</body>
|
|
174
307
|
</html>
|
package/src/ui/css/styles.css
CHANGED
|
@@ -497,6 +497,77 @@ body {
|
|
|
497
497
|
/* Utility */
|
|
498
498
|
[x-cloak] { display: none !important; }
|
|
499
499
|
|
|
500
|
+
/* Empty State */
|
|
501
|
+
.empty-state {
|
|
502
|
+
text-align: center;
|
|
503
|
+
padding: 2rem;
|
|
504
|
+
color: var(--text-muted);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.empty-state a {
|
|
508
|
+
color: var(--primary);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/* Compact agent card for dashboard */
|
|
512
|
+
.agent-card-compact {
|
|
513
|
+
display: flex;
|
|
514
|
+
justify-content: space-between;
|
|
515
|
+
align-items: center;
|
|
516
|
+
padding: 0.75rem 1rem;
|
|
517
|
+
background: var(--bg);
|
|
518
|
+
border-radius: 0.375rem;
|
|
519
|
+
margin-bottom: 0.5rem;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.agent-card-compact .agent-name {
|
|
523
|
+
font-weight: 500;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.agent-card-compact .agent-role {
|
|
527
|
+
font-size: 0.75rem;
|
|
528
|
+
color: var(--text-muted);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/* Roles preview grid */
|
|
532
|
+
.roles-grid-preview {
|
|
533
|
+
display: grid;
|
|
534
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
535
|
+
gap: 0.75rem;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.role-chip {
|
|
539
|
+
padding: 0.75rem 1rem;
|
|
540
|
+
background: var(--bg);
|
|
541
|
+
border-radius: 0.375rem;
|
|
542
|
+
border: 1px solid var(--border);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.role-chip strong {
|
|
546
|
+
display: block;
|
|
547
|
+
margin-bottom: 0.25rem;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.role-chip span {
|
|
551
|
+
font-size: 0.75rem;
|
|
552
|
+
color: var(--text-muted);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/* Agent variants in card */
|
|
556
|
+
.agent-variants {
|
|
557
|
+
display: flex;
|
|
558
|
+
flex-wrap: wrap;
|
|
559
|
+
gap: 0.25rem;
|
|
560
|
+
margin: 0.5rem 0;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.variant-tag {
|
|
564
|
+
background: var(--bg);
|
|
565
|
+
padding: 0.125rem 0.5rem;
|
|
566
|
+
border-radius: 0.25rem;
|
|
567
|
+
font-size: 0.7rem;
|
|
568
|
+
color: var(--text-muted);
|
|
569
|
+
}
|
|
570
|
+
|
|
500
571
|
/* Responsive */
|
|
501
572
|
@media (max-width: 768px) {
|
|
502
573
|
.container {
|
package/src/ui/index.html
CHANGED
|
@@ -41,9 +41,18 @@
|
|
|
41
41
|
</nav>
|
|
42
42
|
|
|
43
43
|
<!-- Stats Cards -->
|
|
44
|
-
<div class="stats-grid"
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
<div class="stats-grid" x-data="statsCards()">
|
|
45
|
+
<template x-if="loading">
|
|
46
|
+
<div class="stat-card"><div class="loading">Loading...</div></div>
|
|
47
|
+
</template>
|
|
48
|
+
<template x-if="!loading">
|
|
49
|
+
<template x-for="stat in stats" :key="stat.label">
|
|
50
|
+
<div class="stat-card">
|
|
51
|
+
<div class="stat-value" x-text="stat.value"></div>
|
|
52
|
+
<div class="stat-label" x-text="stat.label"></div>
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
55
|
+
</template>
|
|
47
56
|
</div>
|
|
48
57
|
|
|
49
58
|
<!-- Quick Actions -->
|
|
@@ -65,16 +74,36 @@
|
|
|
65
74
|
<!-- Recent Agents -->
|
|
66
75
|
<section class="section">
|
|
67
76
|
<h2>Your Agents</h2>
|
|
68
|
-
<div id="agents-list"
|
|
69
|
-
<div class="loading">Loading agents...</div>
|
|
77
|
+
<div id="agents-list" x-data="dashboardAgents()">
|
|
78
|
+
<div x-show="loading" class="loading">Loading agents...</div>
|
|
79
|
+
<div x-show="!loading && agents.length === 0" class="empty-state">
|
|
80
|
+
<p>No agents configured yet. <a href="/ui/agents.html?action=add">Add your first agent</a></p>
|
|
81
|
+
</div>
|
|
82
|
+
<template x-for="agent in agents" :key="agent.name">
|
|
83
|
+
<div class="agent-card-compact">
|
|
84
|
+
<span class="agent-name" x-text="agent.name"></span>
|
|
85
|
+
<span class="agent-role" x-text="agent.variants?.base_role || 'unknown'"></span>
|
|
86
|
+
</div>
|
|
87
|
+
</template>
|
|
88
|
+
<div x-show="!loading && agents.length > 0" style="margin-top: 1rem;">
|
|
89
|
+
<a href="/ui/agents.html" class="btn btn-secondary">Manage Agents</a>
|
|
90
|
+
</div>
|
|
70
91
|
</div>
|
|
71
92
|
</section>
|
|
72
93
|
|
|
73
94
|
<!-- Available Roles -->
|
|
74
95
|
<section class="section">
|
|
75
96
|
<h2>Available Roles</h2>
|
|
76
|
-
<div id="roles-list"
|
|
77
|
-
<div class="loading">Loading roles...</div>
|
|
97
|
+
<div id="roles-list" x-data="rolesPreview()">
|
|
98
|
+
<div x-show="loading" class="loading">Loading roles...</div>
|
|
99
|
+
<div class="roles-grid-preview">
|
|
100
|
+
<template x-for="role in roles" :key="role.name">
|
|
101
|
+
<div class="role-chip">
|
|
102
|
+
<strong x-text="role.name"></strong>
|
|
103
|
+
<span x-text="role.description"></span>
|
|
104
|
+
</div>
|
|
105
|
+
</template>
|
|
106
|
+
</div>
|
|
78
107
|
</div>
|
|
79
108
|
</section>
|
|
80
109
|
</div>
|
|
@@ -141,44 +170,85 @@
|
|
|
141
170
|
}
|
|
142
171
|
}
|
|
143
172
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
173
|
+
// Dashboard Agents Alpine Component
|
|
174
|
+
function dashboardAgents() {
|
|
175
|
+
return {
|
|
176
|
+
agents: [],
|
|
177
|
+
loading: true,
|
|
178
|
+
|
|
179
|
+
async init() {
|
|
180
|
+
await this.loadAgents();
|
|
181
|
+
// Listen for project changes
|
|
182
|
+
document.body.addEventListener('projectChanged', () => this.loadAgents());
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
async loadAgents() {
|
|
186
|
+
this.loading = true;
|
|
149
187
|
try {
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
188
|
+
const response = await fetch('/api/agents');
|
|
189
|
+
const data = await response.json();
|
|
190
|
+
this.agents = data.agents || [];
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error('Failed to load agents:', error);
|
|
193
|
+
} finally {
|
|
194
|
+
this.loading = false;
|
|
195
|
+
}
|
|
156
196
|
}
|
|
157
197
|
}
|
|
158
|
-
}
|
|
198
|
+
}
|
|
159
199
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
200
|
+
// Roles Preview Alpine Component
|
|
201
|
+
function rolesPreview() {
|
|
202
|
+
return {
|
|
203
|
+
roles: [],
|
|
204
|
+
loading: true,
|
|
163
205
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
206
|
+
async init() {
|
|
207
|
+
try {
|
|
208
|
+
const response = await fetch('/api/agents/meta/roles');
|
|
209
|
+
this.roles = await response.json();
|
|
210
|
+
} catch (error) {
|
|
211
|
+
console.error('Failed to load roles:', error);
|
|
212
|
+
} finally {
|
|
213
|
+
this.loading = false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Stats Cards Alpine Component
|
|
220
|
+
function statsCards() {
|
|
221
|
+
return {
|
|
222
|
+
stats: [],
|
|
223
|
+
loading: true,
|
|
224
|
+
|
|
225
|
+
async init() {
|
|
226
|
+
await this.loadStats();
|
|
227
|
+
// Listen for project changes
|
|
228
|
+
document.body.addEventListener('projectChanged', () => this.loadStats());
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
async loadStats() {
|
|
232
|
+
this.loading = true;
|
|
233
|
+
try {
|
|
234
|
+
const response = await fetch('/api/agents');
|
|
235
|
+
const data = await response.json();
|
|
236
|
+
const agents = data.agents || [];
|
|
237
|
+
const roles = [...new Set(agents.map(a => a.variants?.base_role).filter(Boolean))];
|
|
238
|
+
|
|
239
|
+
this.stats = [
|
|
240
|
+
{ value: agents.length, label: 'Total Agents' },
|
|
241
|
+
{ value: roles.length, label: 'Roles Used' },
|
|
242
|
+
{ value: 50, label: 'Skills Available' },
|
|
243
|
+
{ value: 9, label: 'Roles Available' }
|
|
244
|
+
];
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.error('Failed to load stats:', error);
|
|
247
|
+
} finally {
|
|
248
|
+
this.loading = false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
182
252
|
}
|
|
183
253
|
</script>
|
|
184
254
|
</body>
|