@greatapps/greatagents-ui 0.3.2 → 0.3.4
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/index.d.ts +204 -1
- package/dist/index.js +1797 -125
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +14 -0
- package/src/components/agents/agent-edit-form.tsx +4 -1
- package/src/components/agents/agent-form-dialog.tsx +5 -1
- package/src/components/agents/agent-objectives-list.tsx +15 -6
- package/src/components/agents/agent-prompt-editor.tsx +9 -5
- package/src/components/agents/agent-tabs.tsx +4 -4
- package/src/components/agents/agent-tools-list.tsx +12 -5
- package/src/components/agents/agents-table.tsx +7 -2
- package/src/components/capabilities/advanced-tab.tsx +82 -0
- package/src/components/capabilities/capabilities-tab.tsx +475 -0
- package/src/components/capabilities/integration-card.tsx +162 -0
- package/src/components/capabilities/integration-wizard.tsx +537 -0
- package/src/components/capabilities/integrations-tab.tsx +61 -0
- package/src/components/capabilities/types.ts +48 -0
- package/src/components/capabilities/wizard-steps/config-step.tsx +117 -0
- package/src/components/capabilities/wizard-steps/confirm-step.tsx +123 -0
- package/src/components/capabilities/wizard-steps/credentials-step.tsx +205 -0
- package/src/components/capabilities/wizard-steps/info-step.tsx +78 -0
- package/src/components/conversations/agent-conversations-table.tsx +13 -2
- package/src/components/conversations/conversation-view.tsx +2 -2
- package/src/components/tools/tool-credentials-form.tsx +34 -14
- package/src/components/tools/tool-form-dialog.tsx +9 -5
- package/src/components/tools/tools-table.tsx +8 -3
- package/src/data/integrations-registry.ts +23 -0
- package/src/hooks/index.ts +10 -0
- package/src/hooks/use-capabilities.ts +50 -0
- package/src/hooks/use-integrations.ts +114 -0
- package/src/index.ts +34 -0
- package/src/pages/agent-capabilities-page.tsx +159 -0
- package/src/pages/agent-detail-page.tsx +1 -0
- package/src/pages/index.ts +2 -0
- package/src/pages/integrations-management-page.tsx +166 -0
- package/src/types/capabilities.ts +32 -0
- package/src/types/index.ts +10 -0
|
@@ -130,6 +130,7 @@ function useColumns(
|
|
|
130
130
|
variant="ghost"
|
|
131
131
|
size="icon"
|
|
132
132
|
className="h-8 w-8"
|
|
133
|
+
aria-label="Vincular"
|
|
133
134
|
disabled
|
|
134
135
|
>
|
|
135
136
|
<Link className="h-4 w-4" />
|
|
@@ -144,6 +145,7 @@ function useColumns(
|
|
|
144
145
|
variant="ghost"
|
|
145
146
|
size="icon"
|
|
146
147
|
className="h-8 w-8"
|
|
148
|
+
aria-label="Editar"
|
|
147
149
|
onClick={() => onEdit(row.original)}
|
|
148
150
|
>
|
|
149
151
|
<Pencil className="h-4 w-4" />
|
|
@@ -157,6 +159,7 @@ function useColumns(
|
|
|
157
159
|
variant="ghost"
|
|
158
160
|
size="icon"
|
|
159
161
|
className="h-8 w-8 text-destructive hover:text-destructive"
|
|
162
|
+
aria-label="Excluir"
|
|
160
163
|
onClick={() => onRemove(row.original)}
|
|
161
164
|
>
|
|
162
165
|
<Trash2 className="h-4 w-4" />
|
|
@@ -328,9 +331,12 @@ export function ToolCredentialsForm({
|
|
|
328
331
|
<div className="space-y-4">
|
|
329
332
|
<div className="flex items-center gap-3">
|
|
330
333
|
<div className="relative flex-1 max-w-md">
|
|
331
|
-
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
334
|
+
<Search aria-hidden="true" className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
332
335
|
<Input
|
|
333
|
-
placeholder="Buscar credenciais
|
|
336
|
+
placeholder="Buscar credenciais\u2026"
|
|
337
|
+
aria-label="Buscar credenciais"
|
|
338
|
+
name="search"
|
|
339
|
+
autoComplete="off"
|
|
334
340
|
value={search}
|
|
335
341
|
onChange={(e) => setSearch(e.target.value)}
|
|
336
342
|
className="pl-9"
|
|
@@ -353,7 +359,7 @@ export function ToolCredentialsForm({
|
|
|
353
359
|
</DialogHeader>
|
|
354
360
|
<div className="space-y-4">
|
|
355
361
|
<div>
|
|
356
|
-
<label className="mb-1 block text-sm font-medium">
|
|
362
|
+
<label htmlFor="cred-tool" className="mb-1 block text-sm font-medium">
|
|
357
363
|
Ferramenta *
|
|
358
364
|
</label>
|
|
359
365
|
<Select
|
|
@@ -362,7 +368,7 @@ export function ToolCredentialsForm({
|
|
|
362
368
|
setCreateForm((f) => ({ ...f, id_tool: val }))
|
|
363
369
|
}
|
|
364
370
|
>
|
|
365
|
-
<SelectTrigger>
|
|
371
|
+
<SelectTrigger id="cred-tool">
|
|
366
372
|
<SelectValue placeholder="Selecione a ferramenta" />
|
|
367
373
|
</SelectTrigger>
|
|
368
374
|
<SelectContent>
|
|
@@ -375,10 +381,12 @@ export function ToolCredentialsForm({
|
|
|
375
381
|
</Select>
|
|
376
382
|
</div>
|
|
377
383
|
<div>
|
|
378
|
-
<label className="mb-1 block text-sm font-medium">
|
|
384
|
+
<label htmlFor="cred-label" className="mb-1 block text-sm font-medium">
|
|
379
385
|
Label *
|
|
380
386
|
</label>
|
|
381
387
|
<Input
|
|
388
|
+
id="cred-label"
|
|
389
|
+
name="label"
|
|
382
390
|
value={createForm.label}
|
|
383
391
|
onChange={(e) =>
|
|
384
392
|
setCreateForm((f) => ({ ...f, label: e.target.value }))
|
|
@@ -387,10 +395,13 @@ export function ToolCredentialsForm({
|
|
|
387
395
|
/>
|
|
388
396
|
</div>
|
|
389
397
|
<div>
|
|
390
|
-
<label className="mb-1 block text-sm font-medium">
|
|
398
|
+
<label htmlFor="cred-credential" className="mb-1 block text-sm font-medium">
|
|
391
399
|
Credencial *
|
|
392
400
|
</label>
|
|
393
401
|
<Input
|
|
402
|
+
id="cred-credential"
|
|
403
|
+
name="credential"
|
|
404
|
+
autoComplete="off"
|
|
394
405
|
type="password"
|
|
395
406
|
value={createForm.credentials_encrypted}
|
|
396
407
|
onChange={(e) =>
|
|
@@ -403,10 +414,12 @@ export function ToolCredentialsForm({
|
|
|
403
414
|
/>
|
|
404
415
|
</div>
|
|
405
416
|
<div>
|
|
406
|
-
<label className="mb-1 block text-sm font-medium">
|
|
417
|
+
<label htmlFor="cred-expires" className="mb-1 block text-sm font-medium">
|
|
407
418
|
Data de Expiração (opcional)
|
|
408
419
|
</label>
|
|
409
420
|
<Input
|
|
421
|
+
id="cred-expires"
|
|
422
|
+
name="expires"
|
|
410
423
|
type="date"
|
|
411
424
|
value={createForm.expires_at}
|
|
412
425
|
onChange={(e) =>
|
|
@@ -448,7 +461,7 @@ export function ToolCredentialsForm({
|
|
|
448
461
|
</DialogHeader>
|
|
449
462
|
<div className="space-y-4">
|
|
450
463
|
<div>
|
|
451
|
-
<label className="mb-1 block text-sm font-medium">
|
|
464
|
+
<label htmlFor="edit-cred-tool" className="mb-1 block text-sm font-medium">
|
|
452
465
|
Ferramenta *
|
|
453
466
|
</label>
|
|
454
467
|
<Select
|
|
@@ -457,7 +470,7 @@ export function ToolCredentialsForm({
|
|
|
457
470
|
setEditForm((f) => ({ ...f, id_tool: val }))
|
|
458
471
|
}
|
|
459
472
|
>
|
|
460
|
-
<SelectTrigger>
|
|
473
|
+
<SelectTrigger id="edit-cred-tool">
|
|
461
474
|
<SelectValue placeholder="Selecione a ferramenta" />
|
|
462
475
|
</SelectTrigger>
|
|
463
476
|
<SelectContent>
|
|
@@ -470,10 +483,12 @@ export function ToolCredentialsForm({
|
|
|
470
483
|
</Select>
|
|
471
484
|
</div>
|
|
472
485
|
<div>
|
|
473
|
-
<label className="mb-1 block text-sm font-medium">
|
|
486
|
+
<label htmlFor="edit-cred-label" className="mb-1 block text-sm font-medium">
|
|
474
487
|
Label
|
|
475
488
|
</label>
|
|
476
489
|
<Input
|
|
490
|
+
id="edit-cred-label"
|
|
491
|
+
name="label"
|
|
477
492
|
value={editForm.label}
|
|
478
493
|
onChange={(e) =>
|
|
479
494
|
setEditForm((f) => ({ ...f, label: e.target.value }))
|
|
@@ -482,10 +497,13 @@ export function ToolCredentialsForm({
|
|
|
482
497
|
/>
|
|
483
498
|
</div>
|
|
484
499
|
<div>
|
|
485
|
-
<label className="mb-1 block text-sm font-medium">
|
|
500
|
+
<label htmlFor="edit-cred-credential" className="mb-1 block text-sm font-medium">
|
|
486
501
|
Nova Credencial (vazio = manter atual)
|
|
487
502
|
</label>
|
|
488
503
|
<Input
|
|
504
|
+
id="edit-cred-credential"
|
|
505
|
+
name="credential"
|
|
506
|
+
autoComplete="off"
|
|
489
507
|
type="password"
|
|
490
508
|
value={editForm.credentials_encrypted}
|
|
491
509
|
onChange={(e) =>
|
|
@@ -498,10 +516,12 @@ export function ToolCredentialsForm({
|
|
|
498
516
|
/>
|
|
499
517
|
</div>
|
|
500
518
|
<div>
|
|
501
|
-
<label className="mb-1 block text-sm font-medium">
|
|
519
|
+
<label htmlFor="edit-cred-expires" className="mb-1 block text-sm font-medium">
|
|
502
520
|
Data de Expiração
|
|
503
521
|
</label>
|
|
504
522
|
<Input
|
|
523
|
+
id="edit-cred-expires"
|
|
524
|
+
name="expires"
|
|
505
525
|
type="date"
|
|
506
526
|
value={editForm.expires_at}
|
|
507
527
|
onChange={(e) =>
|
|
@@ -510,7 +530,7 @@ export function ToolCredentialsForm({
|
|
|
510
530
|
/>
|
|
511
531
|
</div>
|
|
512
532
|
<div>
|
|
513
|
-
<label className="mb-1 block text-sm font-medium">Status</label>
|
|
533
|
+
<label htmlFor="edit-cred-status" className="mb-1 block text-sm font-medium">Status</label>
|
|
514
534
|
<Select
|
|
515
535
|
value={editForm.status || undefined}
|
|
516
536
|
onValueChange={(val) =>
|
|
@@ -520,7 +540,7 @@ export function ToolCredentialsForm({
|
|
|
520
540
|
}))
|
|
521
541
|
}
|
|
522
542
|
>
|
|
523
|
-
<SelectTrigger>
|
|
543
|
+
<SelectTrigger id="edit-cred-status">
|
|
524
544
|
<SelectValue />
|
|
525
545
|
</SelectTrigger>
|
|
526
546
|
<SelectContent>
|
|
@@ -189,6 +189,7 @@ export function ToolFormDialog({
|
|
|
189
189
|
<Label htmlFor="tool-name">Nome *</Label>
|
|
190
190
|
<Input
|
|
191
191
|
id="tool-name"
|
|
192
|
+
name="name"
|
|
192
193
|
value={form.name}
|
|
193
194
|
onChange={(e) => {
|
|
194
195
|
const name = e.target.value;
|
|
@@ -213,6 +214,7 @@ export function ToolFormDialog({
|
|
|
213
214
|
<Label htmlFor="tool-slug">Slug (identificador único) *</Label>
|
|
214
215
|
<Input
|
|
215
216
|
id="tool-slug"
|
|
217
|
+
name="slug"
|
|
216
218
|
value={form.slug}
|
|
217
219
|
onChange={(e) => {
|
|
218
220
|
setSlugManuallyEdited(true);
|
|
@@ -269,11 +271,12 @@ export function ToolFormDialog({
|
|
|
269
271
|
<Label htmlFor="tool-description">Descrição</Label>
|
|
270
272
|
<Textarea
|
|
271
273
|
id="tool-description"
|
|
274
|
+
name="description"
|
|
272
275
|
value={form.description}
|
|
273
276
|
onChange={(e) =>
|
|
274
277
|
setForm((prev) => ({ ...prev, description: e.target.value }))
|
|
275
278
|
}
|
|
276
|
-
placeholder="
|
|
279
|
+
placeholder="Descri\u00e7\u00e3o da ferramenta\u2026"
|
|
277
280
|
rows={3}
|
|
278
281
|
disabled={isPending}
|
|
279
282
|
/>
|
|
@@ -285,6 +288,7 @@ export function ToolFormDialog({
|
|
|
285
288
|
</Label>
|
|
286
289
|
<Textarea
|
|
287
290
|
id="tool-function-defs"
|
|
291
|
+
name="functionDefs"
|
|
288
292
|
value={form.functionDefinitions}
|
|
289
293
|
onChange={(e) => {
|
|
290
294
|
setForm((prev) => ({
|
|
@@ -298,11 +302,11 @@ export function ToolFormDialog({
|
|
|
298
302
|
"type": "function",
|
|
299
303
|
"function": {
|
|
300
304
|
"name": "nome_da_funcao",
|
|
301
|
-
"description": "O que a
|
|
305
|
+
"description": "O que a fun\u00e7\u00e3o faz",
|
|
302
306
|
"parameters": {
|
|
303
307
|
"type": "object",
|
|
304
|
-
"properties": {
|
|
305
|
-
"required": [
|
|
308
|
+
"properties": { \u2026 },
|
|
309
|
+
"required": [\u2026]
|
|
306
310
|
}
|
|
307
311
|
}
|
|
308
312
|
}
|
|
@@ -330,7 +334,7 @@ export function ToolFormDialog({
|
|
|
330
334
|
</Button>
|
|
331
335
|
<Button type="submit" disabled={isPending}>
|
|
332
336
|
{isPending ? (
|
|
333
|
-
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
337
|
+
<Loader2 aria-hidden="true" className="mr-2 h-4 w-4 animate-spin" />
|
|
334
338
|
) : null}
|
|
335
339
|
{isEditing ? "Salvar" : "Criar"}
|
|
336
340
|
</Button>
|
|
@@ -70,7 +70,7 @@ function useColumns(
|
|
|
70
70
|
if (!desc) return <span className="text-muted-foreground text-sm">{"\u2014"}</span>;
|
|
71
71
|
return (
|
|
72
72
|
<span className="text-muted-foreground text-sm">
|
|
73
|
-
{desc.length > 50 ? `${desc.slice(0, 50)}
|
|
73
|
+
{desc.length > 50 ? `${desc.slice(0, 50)}\u2026` : desc}
|
|
74
74
|
</span>
|
|
75
75
|
);
|
|
76
76
|
},
|
|
@@ -98,6 +98,7 @@ function useColumns(
|
|
|
98
98
|
variant="ghost"
|
|
99
99
|
size="icon"
|
|
100
100
|
className="h-8 w-8"
|
|
101
|
+
aria-label="Editar"
|
|
101
102
|
onClick={() => onEdit(row.original)}
|
|
102
103
|
>
|
|
103
104
|
<Pencil className="h-4 w-4" />
|
|
@@ -111,6 +112,7 @@ function useColumns(
|
|
|
111
112
|
variant="ghost"
|
|
112
113
|
size="icon"
|
|
113
114
|
className="h-8 w-8 text-destructive hover:text-destructive"
|
|
115
|
+
aria-label="Excluir"
|
|
114
116
|
onClick={() => onDelete(row.original.id)}
|
|
115
117
|
>
|
|
116
118
|
<Trash2 className="h-4 w-4" />
|
|
@@ -171,9 +173,12 @@ export function ToolsTable({ onEdit, config }: ToolsTableProps) {
|
|
|
171
173
|
<>
|
|
172
174
|
<div className="flex items-center gap-3">
|
|
173
175
|
<div className="relative flex-1 max-w-md">
|
|
174
|
-
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
176
|
+
<Search aria-hidden="true" className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
175
177
|
<Input
|
|
176
|
-
placeholder="Buscar ferramentas
|
|
178
|
+
placeholder="Buscar ferramentas\u2026"
|
|
179
|
+
aria-label="Buscar ferramentas"
|
|
180
|
+
name="search"
|
|
181
|
+
autoComplete="off"
|
|
177
182
|
value={search}
|
|
178
183
|
onChange={(e) => handleSearchChange(e.target.value)}
|
|
179
184
|
className="pl-9"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type IntegrationAuthType = "oauth2" | "api_key" | "none";
|
|
2
|
+
export type IntegrationStatus = "available" | "coming_soon";
|
|
3
|
+
|
|
4
|
+
export interface IntegrationDefinition {
|
|
5
|
+
slug: string;
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
icon: string; // Lucide icon name
|
|
9
|
+
authType: IntegrationAuthType;
|
|
10
|
+
status: IntegrationStatus;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const INTEGRATIONS_REGISTRY: IntegrationDefinition[] = [
|
|
14
|
+
{
|
|
15
|
+
slug: "google_calendar",
|
|
16
|
+
name: "Google Agenda",
|
|
17
|
+
description: "Sincronize agendamentos com o Google Calendar",
|
|
18
|
+
icon: "CalendarSync",
|
|
19
|
+
authType: "oauth2",
|
|
20
|
+
status: "available",
|
|
21
|
+
},
|
|
22
|
+
// Future integrations — add entries here without code changes
|
|
23
|
+
];
|
package/src/hooks/index.ts
CHANGED
|
@@ -28,3 +28,13 @@ export {
|
|
|
28
28
|
|
|
29
29
|
// Contacts
|
|
30
30
|
export { useGagentsContacts } from "./use-contacts";
|
|
31
|
+
|
|
32
|
+
// Capabilities
|
|
33
|
+
export { useCapabilities, useAgentCapabilities, useUpdateAgentCapabilities } from "./use-capabilities";
|
|
34
|
+
|
|
35
|
+
// Integrations
|
|
36
|
+
export {
|
|
37
|
+
useIntegrationState,
|
|
38
|
+
type IntegrationCardState,
|
|
39
|
+
type IntegrationCardData,
|
|
40
|
+
} from "./use-integrations";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
2
|
+
import type { GagentsHookConfig } from "./types";
|
|
3
|
+
import { useGagentsClient } from "./types";
|
|
4
|
+
import type { CapabilitiesResponse, AgentCapability, AgentCapabilitiesPayload } from "../types/capabilities";
|
|
5
|
+
|
|
6
|
+
export function useCapabilities(config: GagentsHookConfig) {
|
|
7
|
+
const client = useGagentsClient(config);
|
|
8
|
+
|
|
9
|
+
return useQuery({
|
|
10
|
+
queryKey: ["greatagents", "capabilities", config.accountId],
|
|
11
|
+
queryFn: async (): Promise<CapabilitiesResponse> => {
|
|
12
|
+
const res = await client.getCapabilities(config.accountId);
|
|
13
|
+
return (res.data as unknown as CapabilitiesResponse) ?? { product: null, categories: [] };
|
|
14
|
+
},
|
|
15
|
+
enabled: !!config.token && !!config.accountId,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useAgentCapabilities(config: GagentsHookConfig, agentId: number | null) {
|
|
20
|
+
const client = useGagentsClient(config);
|
|
21
|
+
|
|
22
|
+
return useQuery({
|
|
23
|
+
queryKey: ["greatagents", "agent-capabilities", config.accountId, agentId],
|
|
24
|
+
queryFn: async (): Promise<AgentCapability[]> => {
|
|
25
|
+
const res = await client.getAgentCapabilities(config.accountId, agentId!);
|
|
26
|
+
const d = res.data;
|
|
27
|
+
return (Array.isArray(d) ? d : []) as AgentCapability[];
|
|
28
|
+
},
|
|
29
|
+
enabled: !!config.token && !!config.accountId && !!agentId,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function useUpdateAgentCapabilities(config: GagentsHookConfig) {
|
|
34
|
+
const client = useGagentsClient(config);
|
|
35
|
+
const queryClient = useQueryClient();
|
|
36
|
+
|
|
37
|
+
return useMutation({
|
|
38
|
+
mutationFn: ({ agentId, payload }: { agentId: number; payload: AgentCapabilitiesPayload }) =>
|
|
39
|
+
client.updateAgentCapabilities(config.accountId, agentId, payload),
|
|
40
|
+
onSuccess: (_data, variables) => {
|
|
41
|
+
queryClient.invalidateQueries({
|
|
42
|
+
queryKey: ["greatagents", "agent-capabilities", config.accountId, variables.agentId],
|
|
43
|
+
});
|
|
44
|
+
// Also invalidate agent-tools since capabilities map to tools
|
|
45
|
+
queryClient.invalidateQueries({
|
|
46
|
+
queryKey: ["greatagents", "agent-tools"],
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import type { GagentsHookConfig } from "./types";
|
|
3
|
+
import { useToolCredentials } from "./use-settings";
|
|
4
|
+
import { useAgentTools } from "./use-agent-tools";
|
|
5
|
+
import { useTools } from "./use-tools";
|
|
6
|
+
import {
|
|
7
|
+
INTEGRATIONS_REGISTRY,
|
|
8
|
+
type IntegrationDefinition,
|
|
9
|
+
} from "../data/integrations-registry";
|
|
10
|
+
import type { AgentTool, Tool, ToolCredential } from "../types";
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Public types
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export type IntegrationCardState =
|
|
17
|
+
| "available"
|
|
18
|
+
| "connected"
|
|
19
|
+
| "expired"
|
|
20
|
+
| "coming_soon";
|
|
21
|
+
|
|
22
|
+
export interface IntegrationCardData {
|
|
23
|
+
/** Static definition from registry */
|
|
24
|
+
definition: IntegrationDefinition;
|
|
25
|
+
/** Resolved visual state */
|
|
26
|
+
state: IntegrationCardState;
|
|
27
|
+
/** Matching credential if one exists */
|
|
28
|
+
credential: ToolCredential | null;
|
|
29
|
+
/** Matching tool record if one exists */
|
|
30
|
+
tool: Tool | null;
|
|
31
|
+
/** How many agents share this credential */
|
|
32
|
+
sharedByAgentsCount: number;
|
|
33
|
+
/** Whether this agent has a linked agent_tool for this integration */
|
|
34
|
+
linkedToAgent: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Hook
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Cross-references the static integrations registry with live data
|
|
43
|
+
* (tools, tool_credentials, agent_tools) to produce card state for each
|
|
44
|
+
* integration entry.
|
|
45
|
+
*/
|
|
46
|
+
export function useIntegrationState(
|
|
47
|
+
config: GagentsHookConfig,
|
|
48
|
+
agentId: number | null,
|
|
49
|
+
) {
|
|
50
|
+
const { data: credentialsData, isLoading: loadingCredentials } =
|
|
51
|
+
useToolCredentials(config);
|
|
52
|
+
const { data: toolsData, isLoading: loadingTools } = useTools(config);
|
|
53
|
+
const { data: agentToolsData, isLoading: loadingAgentTools } = useAgentTools(
|
|
54
|
+
config,
|
|
55
|
+
agentId ?? 0,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const isLoading = loadingCredentials || loadingTools || loadingAgentTools;
|
|
59
|
+
|
|
60
|
+
const cards: IntegrationCardData[] = useMemo(() => {
|
|
61
|
+
const credentials: ToolCredential[] = credentialsData?.data ?? [];
|
|
62
|
+
const tools: Tool[] = toolsData?.data ?? [];
|
|
63
|
+
const agentTools: AgentTool[] = agentToolsData?.data ?? [];
|
|
64
|
+
|
|
65
|
+
return INTEGRATIONS_REGISTRY.map((def) => {
|
|
66
|
+
// coming_soon short-circuit
|
|
67
|
+
if (def.status === "coming_soon") {
|
|
68
|
+
return {
|
|
69
|
+
definition: def,
|
|
70
|
+
state: "coming_soon" as const,
|
|
71
|
+
credential: null,
|
|
72
|
+
tool: null,
|
|
73
|
+
sharedByAgentsCount: 0,
|
|
74
|
+
linkedToAgent: false,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Find tool record matching registry slug
|
|
79
|
+
const matchedTool = tools.find((t) => t.slug === def.slug) ?? null;
|
|
80
|
+
|
|
81
|
+
// Find credential for that tool
|
|
82
|
+
const matchedCredential = matchedTool
|
|
83
|
+
? credentials.find((c) => c.id_tool === matchedTool.id) ?? null
|
|
84
|
+
: null;
|
|
85
|
+
|
|
86
|
+
// Check if this agent has a linked agent_tool for this tool
|
|
87
|
+
const linkedToAgent = matchedTool
|
|
88
|
+
? agentTools.some((at) => at.id_tool === matchedTool.id)
|
|
89
|
+
: false;
|
|
90
|
+
|
|
91
|
+
// Sharing indicator: credential exists at account level (available to all agents)
|
|
92
|
+
// When a credential is account-scoped, any agent can use it — show as "shared"
|
|
93
|
+
const sharedByAgentsCount = matchedCredential ? 1 : 0;
|
|
94
|
+
|
|
95
|
+
// Determine state
|
|
96
|
+
let state: IntegrationCardState = "available";
|
|
97
|
+
if (matchedCredential) {
|
|
98
|
+
state =
|
|
99
|
+
matchedCredential.status === "expired" ? "expired" : "connected";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
definition: def,
|
|
104
|
+
state,
|
|
105
|
+
credential: matchedCredential,
|
|
106
|
+
tool: matchedTool,
|
|
107
|
+
sharedByAgentsCount,
|
|
108
|
+
linkedToAgent,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
}, [credentialsData, toolsData, agentToolsData]);
|
|
112
|
+
|
|
113
|
+
return { cards, isLoading };
|
|
114
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,12 @@ export type {
|
|
|
13
13
|
PromptVersion,
|
|
14
14
|
Tool,
|
|
15
15
|
ToolCredential,
|
|
16
|
+
CapabilityOperation,
|
|
17
|
+
CapabilityModule,
|
|
18
|
+
CapabilityCategory,
|
|
19
|
+
CapabilitiesResponse,
|
|
20
|
+
AgentCapability,
|
|
21
|
+
AgentCapabilitiesPayload,
|
|
16
22
|
} from "./types";
|
|
17
23
|
|
|
18
24
|
// Client
|
|
@@ -41,5 +47,33 @@ export { AgentConversationsPanel } from "./components/conversations/agent-conver
|
|
|
41
47
|
export { AgentConversationsTable } from "./components/conversations/agent-conversations-table";
|
|
42
48
|
export { ConversationView } from "./components/conversations/conversation-view";
|
|
43
49
|
|
|
50
|
+
// Capabilities
|
|
51
|
+
export { IntegrationCard } from "./components/capabilities/integration-card";
|
|
52
|
+
export type { IntegrationCardProps } from "./components/capabilities/integration-card";
|
|
53
|
+
export { IntegrationsTab } from "./components/capabilities/integrations-tab";
|
|
54
|
+
export type { IntegrationsTabProps } from "./components/capabilities/integrations-tab";
|
|
55
|
+
export { CapabilitiesTab } from "./components/capabilities/capabilities-tab";
|
|
56
|
+
export type { CapabilitiesTabProps } from "./components/capabilities/capabilities-tab";
|
|
57
|
+
export { AdvancedTab } from "./components/capabilities/advanced-tab";
|
|
58
|
+
export type { AdvancedTabProps } from "./components/capabilities/advanced-tab";
|
|
59
|
+
export { IntegrationWizard } from "./components/capabilities/integration-wizard";
|
|
60
|
+
export type { IntegrationWizardProps } from "./components/capabilities/integration-wizard";
|
|
61
|
+
export type {
|
|
62
|
+
WizardIntegrationMeta,
|
|
63
|
+
IntegrationCapability,
|
|
64
|
+
WizardStep,
|
|
65
|
+
OAuthStatus,
|
|
66
|
+
OAuthResult,
|
|
67
|
+
} from "./components/capabilities/types";
|
|
68
|
+
export type { ConfigOption } from "./components/capabilities/wizard-steps/config-step";
|
|
69
|
+
|
|
70
|
+
// Data
|
|
71
|
+
export { INTEGRATIONS_REGISTRY } from "./data/integrations-registry";
|
|
72
|
+
export type {
|
|
73
|
+
IntegrationDefinition,
|
|
74
|
+
IntegrationAuthType,
|
|
75
|
+
IntegrationStatus,
|
|
76
|
+
} from "./data/integrations-registry";
|
|
77
|
+
|
|
44
78
|
// Page Compositions
|
|
45
79
|
export * from "./pages";
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from "react";
|
|
4
|
+
import {
|
|
5
|
+
Tabs,
|
|
6
|
+
TabsList,
|
|
7
|
+
TabsTrigger,
|
|
8
|
+
TabsContent,
|
|
9
|
+
} from "@greatapps/greatauth-ui/ui";
|
|
10
|
+
import { Blocks, Plug, Settings } from "lucide-react";
|
|
11
|
+
|
|
12
|
+
import type { GagentsHookConfig } from "../hooks/types";
|
|
13
|
+
import type { IntegrationCardData } from "../hooks/use-integrations";
|
|
14
|
+
import type { WizardIntegrationMeta } from "../components/capabilities/types";
|
|
15
|
+
import { CapabilitiesTab } from "../components/capabilities/capabilities-tab";
|
|
16
|
+
import { IntegrationsTab } from "../components/capabilities/integrations-tab";
|
|
17
|
+
import { IntegrationWizard } from "../components/capabilities/integration-wizard";
|
|
18
|
+
import { AdvancedTab } from "../components/capabilities/advanced-tab";
|
|
19
|
+
import type { ConfigOption } from "../components/capabilities/wizard-steps/config-step";
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Props
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
export interface AgentCapabilitiesPageProps {
|
|
26
|
+
config: GagentsHookConfig;
|
|
27
|
+
agentId: number;
|
|
28
|
+
gagentsApiUrl: string;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve wizard metadata for a given integration card.
|
|
31
|
+
* The consuming app provides this so the wizard gets correct
|
|
32
|
+
* capabilities, requirements, and config step flag.
|
|
33
|
+
*/
|
|
34
|
+
resolveWizardMeta?: (card: IntegrationCardData) => WizardIntegrationMeta;
|
|
35
|
+
/**
|
|
36
|
+
* Callback to load config options after OAuth completes
|
|
37
|
+
* (e.g. load Google Calendar list). Forwarded to IntegrationWizard.
|
|
38
|
+
*/
|
|
39
|
+
loadConfigOptions?: (credentialId: number) => Promise<ConfigOption[]>;
|
|
40
|
+
/** Called after wizard completes successfully. */
|
|
41
|
+
onWizardComplete?: () => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Default wizard meta resolver
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
function defaultResolveWizardMeta(card: IntegrationCardData): WizardIntegrationMeta {
|
|
49
|
+
return {
|
|
50
|
+
capabilities: [
|
|
51
|
+
{ label: card.definition.name, description: card.definition.description },
|
|
52
|
+
],
|
|
53
|
+
requirements: [],
|
|
54
|
+
hasConfigStep: false,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Component
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
export function AgentCapabilitiesPage({
|
|
63
|
+
config,
|
|
64
|
+
agentId,
|
|
65
|
+
gagentsApiUrl,
|
|
66
|
+
resolveWizardMeta = defaultResolveWizardMeta,
|
|
67
|
+
loadConfigOptions,
|
|
68
|
+
onWizardComplete,
|
|
69
|
+
}: AgentCapabilitiesPageProps) {
|
|
70
|
+
// Wizard dialog state
|
|
71
|
+
const [wizardOpen, setWizardOpen] = useState(false);
|
|
72
|
+
const [activeCard, setActiveCard] = useState<IntegrationCardData | null>(null);
|
|
73
|
+
|
|
74
|
+
const handleConnect = useCallback(
|
|
75
|
+
(card: IntegrationCardData) => {
|
|
76
|
+
setActiveCard(card);
|
|
77
|
+
setWizardOpen(true);
|
|
78
|
+
},
|
|
79
|
+
[],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const handleWizardComplete = useCallback(() => {
|
|
83
|
+
setWizardOpen(false);
|
|
84
|
+
setActiveCard(null);
|
|
85
|
+
onWizardComplete?.();
|
|
86
|
+
}, [onWizardComplete]);
|
|
87
|
+
|
|
88
|
+
const handleWizardOpenChange = useCallback((open: boolean) => {
|
|
89
|
+
setWizardOpen(open);
|
|
90
|
+
if (!open) setActiveCard(null);
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
// Derive wizard meta from active card
|
|
94
|
+
const wizardMeta = activeCard ? resolveWizardMeta(activeCard) : null;
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="space-y-4">
|
|
98
|
+
<div>
|
|
99
|
+
<h2 className="text-lg font-semibold">Capacidades e Integrações</h2>
|
|
100
|
+
<p className="text-sm text-muted-foreground">
|
|
101
|
+
Configure o que este agente pode fazer e quais serviços externos ele utiliza.
|
|
102
|
+
</p>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<Tabs defaultValue="capacidades">
|
|
106
|
+
<TabsList>
|
|
107
|
+
<TabsTrigger value="capacidades" className="flex items-center gap-1.5">
|
|
108
|
+
<Blocks aria-hidden="true" className="h-3.5 w-3.5" />
|
|
109
|
+
Capacidades
|
|
110
|
+
</TabsTrigger>
|
|
111
|
+
<TabsTrigger value="integracoes" className="flex items-center gap-1.5">
|
|
112
|
+
<Plug aria-hidden="true" className="h-3.5 w-3.5" />
|
|
113
|
+
Integrações
|
|
114
|
+
</TabsTrigger>
|
|
115
|
+
<TabsTrigger value="avancado" className="flex items-center gap-1.5">
|
|
116
|
+
<Settings aria-hidden="true" className="h-3.5 w-3.5" />
|
|
117
|
+
Avançado
|
|
118
|
+
</TabsTrigger>
|
|
119
|
+
</TabsList>
|
|
120
|
+
|
|
121
|
+
<TabsContent value="capacidades" className="mt-4">
|
|
122
|
+
<CapabilitiesTab config={config} agentId={agentId} />
|
|
123
|
+
</TabsContent>
|
|
124
|
+
|
|
125
|
+
<TabsContent value="integracoes" className="mt-4">
|
|
126
|
+
<IntegrationsTab
|
|
127
|
+
config={config}
|
|
128
|
+
agentId={agentId}
|
|
129
|
+
onConnect={handleConnect}
|
|
130
|
+
/>
|
|
131
|
+
</TabsContent>
|
|
132
|
+
|
|
133
|
+
<TabsContent value="avancado" className="mt-4">
|
|
134
|
+
<AdvancedTab
|
|
135
|
+
config={config}
|
|
136
|
+
agentId={agentId}
|
|
137
|
+
gagentsApiUrl={gagentsApiUrl}
|
|
138
|
+
/>
|
|
139
|
+
</TabsContent>
|
|
140
|
+
</Tabs>
|
|
141
|
+
|
|
142
|
+
{/* Integration Wizard Dialog */}
|
|
143
|
+
{activeCard && wizardMeta && (
|
|
144
|
+
<IntegrationWizard
|
|
145
|
+
open={wizardOpen}
|
|
146
|
+
onOpenChange={handleWizardOpenChange}
|
|
147
|
+
integration={activeCard.definition}
|
|
148
|
+
meta={wizardMeta}
|
|
149
|
+
agentId={agentId}
|
|
150
|
+
config={config}
|
|
151
|
+
onComplete={handleWizardComplete}
|
|
152
|
+
gagentsApiUrl={gagentsApiUrl}
|
|
153
|
+
existingCredentialId={activeCard.credential?.id}
|
|
154
|
+
loadConfigOptions={loadConfigOptions}
|
|
155
|
+
/>
|
|
156
|
+
)}
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
}
|