apteva 0.4.16 → 0.4.18
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/ActivityPage.yv28a2vj.js +3 -0
- package/dist/ApiDocsPage.4ccwjjbk.js +4 -0
- package/dist/App.155wke5v.js +4 -0
- package/dist/App.2e19nvn4.js +13 -0
- package/dist/App.2ye1b5n0.js +4 -0
- package/dist/App.4da4ycbe.js +4 -0
- package/dist/App.b6wtzd1j.js +4 -0
- package/dist/App.fjrh28tf.js +4 -0
- package/dist/App.htc36cy8.js +4 -0
- package/dist/App.me6reaa6.js +4 -0
- package/dist/App.n5q6p960.js +4 -0
- package/dist/App.nft7h9jt.js +4 -0
- package/dist/App.np463xvy.js +4 -0
- package/dist/App.nps62kvt.js +4 -0
- package/dist/App.q8ws33cc.js +181 -0
- package/dist/App.tb0y0jmt.js +40 -0
- package/dist/ConnectionsPage.52evzrp7.js +3 -0
- package/dist/McpPage.bjqrp0n2.js +3 -0
- package/dist/SettingsPage.es76hnj2.js +3 -0
- package/dist/SkillsPage.06h8yf0h.js +3 -0
- package/dist/TasksPage.99df66mk.js +3 -0
- package/dist/TelemetryPage.bmdnxhq7.js +3 -0
- package/dist/TestsPage.denxrg8c.js +3 -0
- package/dist/index.html +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/auth/middleware.ts +2 -0
- package/src/db.ts +162 -11
- package/src/mcp-platform.ts +41 -1
- package/src/routes/api/agent-utils.ts +38 -2
- package/src/routes/api/agents.ts +65 -2
- package/src/routes/api/projects.ts +19 -2
- package/src/routes/api/system.ts +26 -12
- package/src/routes/api/triggers.ts +458 -0
- package/src/routes/api/webhooks.ts +171 -0
- package/src/routes/api.ts +4 -0
- package/src/routes/static.ts +12 -3
- package/src/server.ts +6 -4
- package/src/triggers/agentdojo.ts +248 -0
- package/src/triggers/composio.ts +264 -0
- package/src/triggers/index.ts +71 -0
- package/src/web/App.tsx +20 -12
- package/src/web/components/agents/AgentCard.tsx +14 -7
- package/src/web/components/agents/AgentPanel.tsx +105 -115
- package/src/web/components/common/Icons.tsx +8 -0
- package/src/web/components/common/index.ts +1 -0
- package/src/web/components/connections/ConnectionsPage.tsx +54 -0
- package/src/web/components/connections/IntegrationsTab.tsx +144 -0
- package/src/web/components/connections/OverviewTab.tsx +183 -0
- package/src/web/components/connections/TriggersTab.tsx +690 -0
- package/src/web/components/index.ts +1 -0
- package/src/web/components/layout/Sidebar.tsx +7 -1
- package/src/web/components/mcp/IntegrationsPanel.tsx +19 -3
- package/src/web/components/mcp/McpPage.tsx +9 -3
- package/src/web/components/settings/SettingsPage.tsx +96 -2
- package/src/web/components/tasks/TasksPage.tsx +2 -2
- package/src/web/components/tests/TestsPage.tsx +1 -2
- package/src/web/context/TelemetryContext.tsx +14 -1
- package/src/web/context/index.ts +1 -1
- package/src/web/hooks/useAgents.ts +15 -11
- package/src/web/types.ts +1 -1
- package/dist/App.2194efgj.js +0 -228
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { DashboardIcon, ActivityIcon, AgentsIcon, TasksIcon, McpIcon, SkillsIcon, TestsIcon, TelemetryIcon, ApiIcon, SettingsIcon, CloseIcon } from "../common/Icons";
|
|
2
|
+
import { DashboardIcon, ActivityIcon, AgentsIcon, TasksIcon, ConnectionsIcon, McpIcon, SkillsIcon, TestsIcon, TelemetryIcon, ApiIcon, SettingsIcon, CloseIcon } from "../common/Icons";
|
|
3
3
|
import type { Route } from "../../types";
|
|
4
4
|
|
|
5
5
|
interface SidebarProps {
|
|
@@ -88,6 +88,12 @@ export function Sidebar({ route, agentCount, taskCount, onNavigate, isOpen, onCl
|
|
|
88
88
|
active={route === "skills"}
|
|
89
89
|
onClick={() => handleNavigate("skills")}
|
|
90
90
|
/>
|
|
91
|
+
<NavButton
|
|
92
|
+
icon={<ConnectionsIcon />}
|
|
93
|
+
label="Connections"
|
|
94
|
+
active={route === "connections"}
|
|
95
|
+
onClick={() => handleNavigate("connections")}
|
|
96
|
+
/>
|
|
91
97
|
<NavButton
|
|
92
98
|
icon={<TestsIcon />}
|
|
93
99
|
label="Tests"
|
|
@@ -46,10 +46,14 @@ export function IntegrationsPanel({
|
|
|
46
46
|
providerId = "composio",
|
|
47
47
|
projectId,
|
|
48
48
|
onConnectionComplete,
|
|
49
|
+
onBrowseTriggers,
|
|
50
|
+
hideMcpConfig,
|
|
49
51
|
}: {
|
|
50
52
|
providerId?: string;
|
|
51
53
|
projectId?: string | null;
|
|
52
54
|
onConnectionComplete?: () => void;
|
|
55
|
+
onBrowseTriggers?: (toolkitSlug: string) => void;
|
|
56
|
+
hideMcpConfig?: boolean;
|
|
53
57
|
}) {
|
|
54
58
|
const { authFetch } = useAuth();
|
|
55
59
|
const [apps, setApps] = useState<IntegrationApp[]>([]);
|
|
@@ -314,9 +318,10 @@ export function IntegrationsPanel({
|
|
|
314
318
|
);
|
|
315
319
|
};
|
|
316
320
|
|
|
317
|
-
// Get connection for app
|
|
321
|
+
// Get connection for app (prefer active account)
|
|
318
322
|
const getConnection = (appSlug: string) => {
|
|
319
|
-
return connectedAccounts.find((a) => a.appId === appSlug)
|
|
323
|
+
return connectedAccounts.find((a) => a.appId === appSlug && a.status === "active")
|
|
324
|
+
|| connectedAccounts.find((a) => a.appId === appSlug);
|
|
320
325
|
};
|
|
321
326
|
|
|
322
327
|
// Filter apps
|
|
@@ -590,7 +595,8 @@ export function IntegrationsPanel({
|
|
|
590
595
|
const conn = getConnection(app.slug);
|
|
591
596
|
if (conn) handleDisconnect(conn);
|
|
592
597
|
}}
|
|
593
|
-
onCreateMcpConfig={() => openMcpConfigModal(app)}
|
|
598
|
+
onCreateMcpConfig={hideMcpConfig ? undefined : () => openMcpConfigModal(app)}
|
|
599
|
+
onBrowseTriggers={onBrowseTriggers ? () => onBrowseTriggers(app.slug) : undefined}
|
|
594
600
|
connecting={connecting === app.slug}
|
|
595
601
|
/>
|
|
596
602
|
))}
|
|
@@ -636,6 +642,7 @@ function AppCard({
|
|
|
636
642
|
onConnect,
|
|
637
643
|
onDisconnect,
|
|
638
644
|
onCreateMcpConfig,
|
|
645
|
+
onBrowseTriggers,
|
|
639
646
|
connecting,
|
|
640
647
|
}: {
|
|
641
648
|
app: IntegrationApp;
|
|
@@ -643,6 +650,7 @@ function AppCard({
|
|
|
643
650
|
onConnect: () => void;
|
|
644
651
|
onDisconnect?: () => void;
|
|
645
652
|
onCreateMcpConfig?: () => void;
|
|
653
|
+
onBrowseTriggers?: () => void;
|
|
646
654
|
connecting: boolean;
|
|
647
655
|
}) {
|
|
648
656
|
const isConnected = connection?.status === "active";
|
|
@@ -723,6 +731,14 @@ function AppCard({
|
|
|
723
731
|
Create MCP Config
|
|
724
732
|
</button>
|
|
725
733
|
)}
|
|
734
|
+
{onBrowseTriggers && (
|
|
735
|
+
<button
|
|
736
|
+
onClick={onBrowseTriggers}
|
|
737
|
+
className="flex-1 text-xs bg-[#1a1a2a] hover:bg-[#1a1a3a] border border-blue-500/30 hover:border-blue-500/50 text-blue-400 px-3 py-1.5 rounded transition"
|
|
738
|
+
>
|
|
739
|
+
Browse Triggers
|
|
740
|
+
</button>
|
|
741
|
+
)}
|
|
726
742
|
{onDisconnect && (
|
|
727
743
|
<button
|
|
728
744
|
onClick={onDisconnect}
|
|
@@ -948,9 +948,12 @@ function HostedServices({ onServerAdded, projectId }: { onServerAdded?: () => vo
|
|
|
948
948
|
|
|
949
949
|
const fetchStatus = async () => {
|
|
950
950
|
try {
|
|
951
|
+
const serversUrl = projectId && projectId !== "unassigned"
|
|
952
|
+
? `/api/mcp/servers?project=${encodeURIComponent(projectId)}`
|
|
953
|
+
: "/api/mcp/servers";
|
|
951
954
|
const [providersRes, serversRes] = await Promise.all([
|
|
952
955
|
authFetch("/api/providers"),
|
|
953
|
-
authFetch(
|
|
956
|
+
authFetch(serversUrl),
|
|
954
957
|
]);
|
|
955
958
|
const providersData = await providersRes.json();
|
|
956
959
|
const serversData = await serversRes.json();
|
|
@@ -1037,7 +1040,7 @@ function HostedServices({ onServerAdded, projectId }: { onServerAdded?: () => vo
|
|
|
1037
1040
|
|
|
1038
1041
|
useEffect(() => {
|
|
1039
1042
|
fetchStatus();
|
|
1040
|
-
}, [authFetch]);
|
|
1043
|
+
}, [authFetch, projectId]);
|
|
1041
1044
|
|
|
1042
1045
|
if (loading) {
|
|
1043
1046
|
return <div className="text-center py-8 text-[#666]">Loading...</div>;
|
|
@@ -1360,9 +1363,12 @@ function AgentDojoContent({
|
|
|
1360
1363
|
setLoadingConfigs(true);
|
|
1361
1364
|
try {
|
|
1362
1365
|
const projectParam = projectId && projectId !== "unassigned" ? `?project_id=${projectId}` : "";
|
|
1366
|
+
const serversUrl = projectId && projectId !== "unassigned"
|
|
1367
|
+
? `/api/mcp/servers?project=${encodeURIComponent(projectId)}`
|
|
1368
|
+
: "/api/mcp/servers";
|
|
1363
1369
|
const [configsRes, serversRes] = await Promise.all([
|
|
1364
1370
|
authFetch(`/api/integrations/agentdojo/configs${projectParam}`),
|
|
1365
|
-
authFetch(
|
|
1371
|
+
authFetch(serversUrl),
|
|
1366
1372
|
]);
|
|
1367
1373
|
const configsData = await configsRes.json();
|
|
1368
1374
|
const serversData = await serversRes.json();
|
|
@@ -5,13 +5,14 @@ import { Select } from "../common/Select";
|
|
|
5
5
|
import { useProjects, useAuth, type Project } from "../../context";
|
|
6
6
|
import type { Provider } from "../../types";
|
|
7
7
|
|
|
8
|
-
type SettingsTab = "providers" | "projects" | "api-keys" | "account" | "updates" | "data";
|
|
8
|
+
type SettingsTab = "general" | "providers" | "projects" | "api-keys" | "account" | "updates" | "data";
|
|
9
9
|
|
|
10
10
|
export function SettingsPage() {
|
|
11
11
|
const { projectsEnabled } = useProjects();
|
|
12
|
-
const [activeTab, setActiveTab] = useState<SettingsTab>("
|
|
12
|
+
const [activeTab, setActiveTab] = useState<SettingsTab>("general");
|
|
13
13
|
|
|
14
14
|
const tabs: { key: SettingsTab; label: string }[] = [
|
|
15
|
+
{ key: "general", label: "General" },
|
|
15
16
|
{ key: "providers", label: "Providers" },
|
|
16
17
|
...(projectsEnabled ? [{ key: "projects" as SettingsTab, label: "Projects" }] : []),
|
|
17
18
|
{ key: "api-keys", label: "API Keys" },
|
|
@@ -58,6 +59,7 @@ export function SettingsPage() {
|
|
|
58
59
|
|
|
59
60
|
{/* Settings Content */}
|
|
60
61
|
<div className="flex-1 overflow-auto p-4 md:p-6">
|
|
62
|
+
{activeTab === "general" && <GeneralSettings />}
|
|
61
63
|
{activeTab === "providers" && <ProvidersSettings />}
|
|
62
64
|
{activeTab === "projects" && projectsEnabled && <ProjectsSettings />}
|
|
63
65
|
{activeTab === "api-keys" && <ApiKeysSettings />}
|
|
@@ -92,6 +94,98 @@ function SettingsNavItem({
|
|
|
92
94
|
);
|
|
93
95
|
}
|
|
94
96
|
|
|
97
|
+
function GeneralSettings() {
|
|
98
|
+
const { authFetch } = useAuth();
|
|
99
|
+
const [instanceUrl, setInstanceUrl] = useState("");
|
|
100
|
+
const [loading, setLoading] = useState(true);
|
|
101
|
+
const [saving, setSaving] = useState(false);
|
|
102
|
+
const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
const fetch = async () => {
|
|
106
|
+
try {
|
|
107
|
+
const res = await authFetch("/api/settings/instance-url");
|
|
108
|
+
const data = await res.json();
|
|
109
|
+
setInstanceUrl(data.instance_url || "");
|
|
110
|
+
} catch {
|
|
111
|
+
// ignore
|
|
112
|
+
}
|
|
113
|
+
setLoading(false);
|
|
114
|
+
};
|
|
115
|
+
fetch();
|
|
116
|
+
}, []);
|
|
117
|
+
|
|
118
|
+
const handleSave = async () => {
|
|
119
|
+
setSaving(true);
|
|
120
|
+
setMessage(null);
|
|
121
|
+
try {
|
|
122
|
+
const res = await authFetch("/api/settings/instance-url", {
|
|
123
|
+
method: "PUT",
|
|
124
|
+
headers: { "Content-Type": "application/json" },
|
|
125
|
+
body: JSON.stringify({ instance_url: instanceUrl }),
|
|
126
|
+
});
|
|
127
|
+
const data = await res.json();
|
|
128
|
+
if (res.ok) {
|
|
129
|
+
setInstanceUrl(data.instance_url || "");
|
|
130
|
+
setMessage({ type: "success", text: "Instance URL saved" });
|
|
131
|
+
} else {
|
|
132
|
+
setMessage({ type: "error", text: data.error || "Failed to save" });
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
setMessage({ type: "error", text: "Failed to save" });
|
|
136
|
+
}
|
|
137
|
+
setSaving(false);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<div className="max-w-4xl w-full">
|
|
142
|
+
<div className="mb-6">
|
|
143
|
+
<h1 className="text-2xl font-semibold mb-1">General</h1>
|
|
144
|
+
<p className="text-[#666]">Instance configuration.</p>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<div className="bg-[#111] border border-[#1a1a1a] rounded-lg p-4">
|
|
148
|
+
<h3 className="font-medium mb-2">Instance URL</h3>
|
|
149
|
+
<p className="text-sm text-[#666] mb-4">
|
|
150
|
+
The public HTTPS URL for this instance. Used for webhook callbacks from external services like Composio.
|
|
151
|
+
</p>
|
|
152
|
+
|
|
153
|
+
{loading ? (
|
|
154
|
+
<div className="text-[#666] text-sm">Loading...</div>
|
|
155
|
+
) : (
|
|
156
|
+
<div className="space-y-3 max-w-lg">
|
|
157
|
+
<input
|
|
158
|
+
type="text"
|
|
159
|
+
value={instanceUrl}
|
|
160
|
+
onChange={e => setInstanceUrl(e.target.value)}
|
|
161
|
+
placeholder="https://your-domain.com"
|
|
162
|
+
className="w-full bg-[#0a0a0a] border border-[#333] rounded px-3 py-2 focus:outline-none focus:border-[#f97316] font-mono text-sm"
|
|
163
|
+
/>
|
|
164
|
+
|
|
165
|
+
{message && (
|
|
166
|
+
<div className={`p-3 rounded text-sm ${
|
|
167
|
+
message.type === "success"
|
|
168
|
+
? "bg-green-500/10 text-green-400 border border-green-500/30"
|
|
169
|
+
: "bg-red-500/10 text-red-400 border border-red-500/30"
|
|
170
|
+
}`}>
|
|
171
|
+
{message.text}
|
|
172
|
+
</div>
|
|
173
|
+
)}
|
|
174
|
+
|
|
175
|
+
<button
|
|
176
|
+
onClick={handleSave}
|
|
177
|
+
disabled={saving}
|
|
178
|
+
className="px-4 py-2 bg-[#f97316] hover:bg-[#fb923c] disabled:opacity-50 text-black rounded text-sm font-medium transition"
|
|
179
|
+
>
|
|
180
|
+
{saving ? "Saving..." : "Save"}
|
|
181
|
+
</button>
|
|
182
|
+
</div>
|
|
183
|
+
)}
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
95
189
|
function ProvidersSettings() {
|
|
96
190
|
const { authFetch } = useAuth();
|
|
97
191
|
const { projects, projectsEnabled } = useProjects();
|
|
@@ -500,7 +500,7 @@ function TrajectoryView({ trajectory }: { trajectory: TaskTrajectoryStep[] }) {
|
|
|
500
500
|
|
|
501
501
|
const DAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
502
502
|
|
|
503
|
-
function formatCron(cron: string): string {
|
|
503
|
+
export function formatCron(cron: string): string {
|
|
504
504
|
try {
|
|
505
505
|
const parts = cron.trim().split(/\s+/);
|
|
506
506
|
if (parts.length !== 5) return cron;
|
|
@@ -558,7 +558,7 @@ function formatCron(cron: string): string {
|
|
|
558
558
|
}
|
|
559
559
|
}
|
|
560
560
|
|
|
561
|
-
function formatRelativeTime(dateStr: string): string {
|
|
561
|
+
export function formatRelativeTime(dateStr: string): string {
|
|
562
562
|
const date = new Date(dateStr);
|
|
563
563
|
const now = new Date();
|
|
564
564
|
const diffMs = date.getTime() - now.getTime();
|
|
@@ -20,6 +20,7 @@ interface TelemetryContextValue {
|
|
|
20
20
|
lastActivityByAgent: Record<string, { timestamp: string; category: string; type: string }>;
|
|
21
21
|
activeAgents: Record<string, { type: string; expiresAt: number }>;
|
|
22
22
|
statusChangeCounter: number;
|
|
23
|
+
taskChangeCounter: number;
|
|
23
24
|
clearEvents: () => void;
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -33,6 +34,7 @@ export function TelemetryProvider({ children }: { children: React.ReactNode }) {
|
|
|
33
34
|
const [lastActivityByAgent, setLastActivityByAgent] = useState<Record<string, { timestamp: string; category: string; type: string }>>({});
|
|
34
35
|
const [activeAgents, setActiveAgents] = useState<Record<string, { type: string; expiresAt: number }>>({});
|
|
35
36
|
const [statusChangeCounter, setStatusChangeCounter] = useState(0);
|
|
37
|
+
const [taskChangeCounter, setTaskChangeCounter] = useState(0);
|
|
36
38
|
const eventSourceRef = useRef<EventSource | null>(null);
|
|
37
39
|
const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
38
40
|
|
|
@@ -117,6 +119,11 @@ export function TelemetryProvider({ children }: { children: React.ReactNode }) {
|
|
|
117
119
|
if (data.some((e: TelemetryEvent) => e.category === "system" && (e.type === "agent_started" || e.type === "agent_stopped"))) {
|
|
118
120
|
setStatusChangeCounter(c => c + 1);
|
|
119
121
|
}
|
|
122
|
+
|
|
123
|
+
// Detect task change events
|
|
124
|
+
if (data.some((e: TelemetryEvent) => e.category === "TASK" && (e.type === "task_created" || e.type === "task_updated" || e.type === "task_deleted"))) {
|
|
125
|
+
setTaskChangeCounter(c => c + 1);
|
|
126
|
+
}
|
|
120
127
|
}
|
|
121
128
|
} catch {
|
|
122
129
|
// Ignore parse errors (likely keepalive or empty message)
|
|
@@ -162,7 +169,7 @@ export function TelemetryProvider({ children }: { children: React.ReactNode }) {
|
|
|
162
169
|
}, []);
|
|
163
170
|
|
|
164
171
|
return (
|
|
165
|
-
<TelemetryContext.Provider value={{ connected, events, lastActivityByAgent, activeAgents, statusChangeCounter, clearEvents }}>
|
|
172
|
+
<TelemetryContext.Provider value={{ connected, events, lastActivityByAgent, activeAgents, statusChangeCounter, taskChangeCounter, clearEvents }}>
|
|
166
173
|
{children}
|
|
167
174
|
</TelemetryContext.Provider>
|
|
168
175
|
);
|
|
@@ -235,3 +242,9 @@ export function useAgentStatusChange(): number {
|
|
|
235
242
|
const { statusChangeCounter } = useTelemetryContext();
|
|
236
243
|
return statusChangeCounter;
|
|
237
244
|
}
|
|
245
|
+
|
|
246
|
+
// Hook to trigger task count refetch on task mutations
|
|
247
|
+
export function useTaskChange(): number {
|
|
248
|
+
const { taskChangeCounter } = useTelemetryContext();
|
|
249
|
+
return taskChangeCounter;
|
|
250
|
+
}
|
package/src/web/context/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { TelemetryProvider, useTelemetryContext, useTelemetry, useAgentActivity, useAgentStatusChange } from "./TelemetryContext";
|
|
1
|
+
export { TelemetryProvider, useTelemetryContext, useTelemetry, useAgentActivity, useAgentStatusChange, useTaskChange } from "./TelemetryContext";
|
|
2
2
|
export type { TelemetryEvent } from "./TelemetryContext";
|
|
3
3
|
|
|
4
4
|
export { AuthProvider, useAuth, useAuthHeaders } from "./AuthContext";
|
|
@@ -23,19 +23,13 @@ export function useAgents(enabled: boolean) {
|
|
|
23
23
|
setLoading(false);
|
|
24
24
|
}, [getHeaders]);
|
|
25
25
|
|
|
26
|
-
//
|
|
26
|
+
// Fetch on mount + auto-refetch when agents start/stop/crash (via SSE telemetry)
|
|
27
27
|
const statusChangeCounter = useAgentStatusChange();
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
if (enabled && statusChangeCounter > 0) {
|
|
30
|
-
fetchAgents();
|
|
31
|
-
}
|
|
32
|
-
}, [enabled, statusChangeCounter, fetchAgents]);
|
|
33
|
-
|
|
34
28
|
useEffect(() => {
|
|
35
29
|
if (enabled) {
|
|
36
30
|
fetchAgents();
|
|
37
31
|
}
|
|
38
|
-
}, [enabled, fetchAgents]);
|
|
32
|
+
}, [enabled, statusChangeCounter, fetchAgents]);
|
|
39
33
|
|
|
40
34
|
const createAgent = async (agent: {
|
|
41
35
|
name: string;
|
|
@@ -83,10 +77,20 @@ export function useAgents(enabled: boolean) {
|
|
|
83
77
|
|
|
84
78
|
const toggleAgent = async (agent: Agent): Promise<{ error?: string }> => {
|
|
85
79
|
const action = agent.status === "running" ? "stop" : "start";
|
|
80
|
+
|
|
81
|
+
// Optimistic UI update — show transitioning state immediately
|
|
82
|
+
setAgents(prev => prev.map(a =>
|
|
83
|
+
a.id === agent.id ? { ...a, status: action === "start" ? "starting" as any : "stopping" as any } : a
|
|
84
|
+
));
|
|
85
|
+
|
|
86
|
+
// Fire API call — telemetry SSE will trigger a refetch with the real status
|
|
86
87
|
const res = await fetch(`/api/agents/${agent.id}/${action}`, { method: "POST", headers: getHeaders() });
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
const data = await res.json();
|
|
90
|
+
// Revert on error
|
|
91
|
+
setAgents(prev => prev.map(a =>
|
|
92
|
+
a.id === agent.id ? { ...a, status: agent.status } : a
|
|
93
|
+
));
|
|
90
94
|
return { error: data.error };
|
|
91
95
|
}
|
|
92
96
|
return {};
|
package/src/web/types.ts
CHANGED
|
@@ -143,7 +143,7 @@ export interface OnboardingStatus {
|
|
|
143
143
|
has_any_keys: boolean;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
export type Route = "dashboard" | "activity" | "agents" | "tasks" | "mcp" | "skills" | "tests" | "telemetry" | "settings" | "api";
|
|
146
|
+
export type Route = "dashboard" | "activity" | "agents" | "tasks" | "connections" | "mcp" | "skills" | "tests" | "telemetry" | "settings" | "api";
|
|
147
147
|
|
|
148
148
|
// Tool use content block in trajectory
|
|
149
149
|
export interface ToolUseBlock {
|