Skip to content

Commit 548a2f9

Browse files
echobtfactorydroid
andauthored
feat(cortex-gui): implement frontend dashboard components for cortex-cli features (#210)
Implements AGENT 9's mission from ORCHESTRATE plan: ## Session Sharing - SharedSession page (/share/:token) - SharedSessionHeader with copy link & report - SharedMessageList for read-only message view - ShareFooter with view count - PasswordPrompt for protected sessions - API functions (share.ts) ## Admin Sessions Management - AdminSessions page (/admin/sessions) - SessionFilters with search, date range, status - SessionsTable with bulk selection - BulkActions (delete, archive, export) - Pagination component - API functions (admin.ts) ## Agent Management - AgentCard with tools badges - AgentGrid for displaying agents - AgentForm for create/edit - ToolsSelector by category - useAgents hook - API functions (agents.ts) ## Mode & Spec - ModeIndicator (build/plan/spec modes) - SpecPlanDialog for reviewing implementation plans ## Task Progress - TaskProgress for real-time task tracking - useTaskSubscription hook with WebSocket support All components follow SolidJS patterns and existing design system. Co-authored-by: Droid Agent <droid@factory.ai>
1 parent d55dc1e commit 548a2f9

31 files changed

Lines changed: 4388 additions & 13 deletions

cortex-gui/src/api/admin.ts

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/**
2+
* API functions for admin panel
3+
*/
4+
5+
import type {
6+
AdminSession,
7+
AdminSessionsResponse,
8+
SessionFilters,
9+
SessionStats,
10+
BulkAction,
11+
} from "@/types/admin";
12+
13+
const API_BASE = "/api/v1/admin";
14+
15+
/**
16+
* Fetch paginated sessions for admin view
17+
*/
18+
export async function fetchAdminSessions(
19+
filters: SessionFilters
20+
): Promise<AdminSessionsResponse> {
21+
const params = new URLSearchParams();
22+
23+
if (filters.search) params.set("search", filters.search);
24+
if (filters.dateRange !== "all") params.set("dateRange", filters.dateRange);
25+
if (filters.status !== "all") params.set("status", filters.status);
26+
if (filters.startDate) params.set("startDate", filters.startDate);
27+
if (filters.endDate) params.set("endDate", filters.endDate);
28+
params.set("page", filters.page.toString());
29+
params.set("pageSize", filters.pageSize.toString());
30+
params.set("sortBy", filters.sortBy);
31+
params.set("sortOrder", filters.sortOrder);
32+
33+
const response = await fetch(`${API_BASE}/sessions?${params.toString()}`);
34+
35+
if (!response.ok) {
36+
throw new Error("Failed to fetch sessions");
37+
}
38+
39+
return response.json();
40+
}
41+
42+
/**
43+
* Get session statistics
44+
*/
45+
export async function fetchSessionStats(): Promise<SessionStats> {
46+
const response = await fetch(`${API_BASE}/sessions/stats`);
47+
48+
if (!response.ok) {
49+
throw new Error("Failed to fetch statistics");
50+
}
51+
52+
return response.json();
53+
}
54+
55+
/**
56+
* Delete a single session
57+
*/
58+
export async function deleteSession(sessionId: string): Promise<void> {
59+
const response = await fetch(`${API_BASE}/sessions/${sessionId}`, {
60+
method: "DELETE",
61+
});
62+
63+
if (!response.ok) {
64+
throw new Error("Failed to delete session");
65+
}
66+
}
67+
68+
/**
69+
* Archive a single session
70+
*/
71+
export async function archiveSession(sessionId: string): Promise<void> {
72+
const response = await fetch(`${API_BASE}/sessions/${sessionId}/archive`, {
73+
method: "POST",
74+
});
75+
76+
if (!response.ok) {
77+
throw new Error("Failed to archive session");
78+
}
79+
}
80+
81+
/**
82+
* Restore a single session from archive
83+
*/
84+
export async function restoreSession(sessionId: string): Promise<void> {
85+
const response = await fetch(`${API_BASE}/sessions/${sessionId}/restore`, {
86+
method: "POST",
87+
});
88+
89+
if (!response.ok) {
90+
throw new Error("Failed to restore session");
91+
}
92+
}
93+
94+
/**
95+
* Perform bulk action on sessions
96+
*/
97+
export async function bulkAction(
98+
sessionIds: string[],
99+
action: BulkAction
100+
): Promise<{ success: number; failed: number }> {
101+
const response = await fetch(`${API_BASE}/sessions/bulk`, {
102+
method: "POST",
103+
headers: { "Content-Type": "application/json" },
104+
body: JSON.stringify({ sessionIds, action }),
105+
});
106+
107+
if (!response.ok) {
108+
throw new Error(`Failed to ${action} sessions`);
109+
}
110+
111+
return response.json();
112+
}
113+
114+
/**
115+
* Export sessions to CSV
116+
*/
117+
export async function exportSessions(
118+
sessionIds: string[]
119+
): Promise<Blob> {
120+
const response = await fetch(`${API_BASE}/sessions/export`, {
121+
method: "POST",
122+
headers: { "Content-Type": "application/json" },
123+
body: JSON.stringify({ sessionIds }),
124+
});
125+
126+
if (!response.ok) {
127+
throw new Error("Failed to export sessions");
128+
}
129+
130+
return response.blob();
131+
}
132+
133+
/**
134+
* Get session details for admin view
135+
*/
136+
export async function fetchSessionDetails(
137+
sessionId: string
138+
): Promise<AdminSession & { messages: unknown[] }> {
139+
const response = await fetch(`${API_BASE}/sessions/${sessionId}`);
140+
141+
if (!response.ok) {
142+
throw new Error("Failed to fetch session details");
143+
}
144+
145+
return response.json();
146+
}

cortex-gui/src/api/agents.ts

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/**
2+
* API functions for agent management
3+
*/
4+
5+
import type { Agent, AgentFormData, AvailableTool, AgentStats } from "@/types/agents";
6+
7+
const API_BASE = "/api/v1";
8+
9+
/**
10+
* Fetch all user agents
11+
*/
12+
export async function fetchUserAgents(): Promise<Agent[]> {
13+
const response = await fetch(`${API_BASE}/agents`);
14+
15+
if (!response.ok) {
16+
throw new Error("Failed to fetch agents");
17+
}
18+
19+
return response.json();
20+
}
21+
22+
/**
23+
* Fetch a single agent by ID
24+
*/
25+
export async function fetchAgent(agentId: string): Promise<Agent> {
26+
const response = await fetch(`${API_BASE}/agents/${agentId}`);
27+
28+
if (!response.ok) {
29+
throw new Error("Failed to fetch agent");
30+
}
31+
32+
return response.json();
33+
}
34+
35+
/**
36+
* Create a new agent
37+
*/
38+
export async function createAgent(data: AgentFormData): Promise<Agent> {
39+
const response = await fetch(`${API_BASE}/agents`, {
40+
method: "POST",
41+
headers: { "Content-Type": "application/json" },
42+
body: JSON.stringify(data),
43+
});
44+
45+
if (!response.ok) {
46+
throw new Error("Failed to create agent");
47+
}
48+
49+
return response.json();
50+
}
51+
52+
/**
53+
* Update an existing agent
54+
*/
55+
export async function updateAgent(
56+
agentId: string,
57+
data: AgentFormData
58+
): Promise<Agent> {
59+
const response = await fetch(`${API_BASE}/agents/${agentId}`, {
60+
method: "PUT",
61+
headers: { "Content-Type": "application/json" },
62+
body: JSON.stringify(data),
63+
});
64+
65+
if (!response.ok) {
66+
throw new Error("Failed to update agent");
67+
}
68+
69+
return response.json();
70+
}
71+
72+
/**
73+
* Delete an agent
74+
*/
75+
export async function deleteAgent(agentId: string): Promise<void> {
76+
const response = await fetch(`${API_BASE}/agents/${agentId}`, {
77+
method: "DELETE",
78+
});
79+
80+
if (!response.ok) {
81+
throw new Error("Failed to delete agent");
82+
}
83+
}
84+
85+
/**
86+
* Fetch available tools that can be assigned to agents
87+
*/
88+
export async function fetchAvailableTools(): Promise<AvailableTool[]> {
89+
const response = await fetch(`${API_BASE}/agents/tools`);
90+
91+
if (!response.ok) {
92+
throw new Error("Failed to fetch available tools");
93+
}
94+
95+
return response.json();
96+
}
97+
98+
/**
99+
* Fetch agent statistics
100+
*/
101+
export async function fetchAgentStats(agentId: string): Promise<AgentStats> {
102+
const response = await fetch(`${API_BASE}/agents/${agentId}/stats`);
103+
104+
if (!response.ok) {
105+
throw new Error("Failed to fetch agent statistics");
106+
}
107+
108+
return response.json();
109+
}
110+
111+
/**
112+
* Fetch built-in agents
113+
*/
114+
export async function fetchBuiltinAgents(): Promise<Agent[]> {
115+
const response = await fetch(`${API_BASE}/agents/builtin`);
116+
117+
if (!response.ok) {
118+
throw new Error("Failed to fetch built-in agents");
119+
}
120+
121+
return response.json();
122+
}
123+
124+
/**
125+
* Generate agent prompt using AI
126+
*/
127+
export async function generateAgentPrompt(
128+
description: string,
129+
tools?: string[],
130+
name?: string
131+
): Promise<AgentFormData> {
132+
const response = await fetch(`${API_BASE}/agents/generate-prompt`, {
133+
method: "POST",
134+
headers: { "Content-Type": "application/json" },
135+
body: JSON.stringify({ description, tools, name }),
136+
});
137+
138+
if (!response.ok) {
139+
throw new Error("Failed to generate agent prompt");
140+
}
141+
142+
return response.json();
143+
}

cortex-gui/src/api/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from "./share";
2+
export * from "./admin";
3+
export * from "./agents";

cortex-gui/src/api/share.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* API functions for session sharing
3+
*/
4+
5+
import type { SharedSession, ShareSettings, ShareResponse } from "@/types/share";
6+
7+
const API_BASE = "/api/v1";
8+
9+
/**
10+
* Fetch a shared session by token
11+
*/
12+
export async function fetchSharedSession(token: string): Promise<SharedSession> {
13+
const response = await fetch(`${API_BASE}/share/${token}`);
14+
15+
if (!response.ok) {
16+
if (response.status === 404) {
17+
throw new Error("Session not found or expired");
18+
}
19+
if (response.status === 401) {
20+
throw new Error("Password required");
21+
}
22+
throw new Error("Failed to fetch shared session");
23+
}
24+
25+
return response.json();
26+
}
27+
28+
/**
29+
* Fetch a password-protected shared session
30+
*/
31+
export async function fetchProtectedSession(
32+
token: string,
33+
password: string
34+
): Promise<SharedSession> {
35+
const response = await fetch(`${API_BASE}/share/${token}`, {
36+
method: "POST",
37+
headers: { "Content-Type": "application/json" },
38+
body: JSON.stringify({ password }),
39+
});
40+
41+
if (!response.ok) {
42+
if (response.status === 401) {
43+
throw new Error("Invalid password");
44+
}
45+
throw new Error("Failed to fetch shared session");
46+
}
47+
48+
return response.json();
49+
}
50+
51+
/**
52+
* Create a new share for a session
53+
*/
54+
export async function createShare(
55+
sessionId: string,
56+
settings: ShareSettings
57+
): Promise<ShareResponse> {
58+
const response = await fetch(`${API_BASE}/sessions/${sessionId}/share`, {
59+
method: "POST",
60+
headers: { "Content-Type": "application/json" },
61+
body: JSON.stringify(settings),
62+
});
63+
64+
if (!response.ok) {
65+
throw new Error("Failed to create share");
66+
}
67+
68+
return response.json();
69+
}
70+
71+
/**
72+
* Revoke a share
73+
*/
74+
export async function revokeShare(token: string): Promise<void> {
75+
const response = await fetch(`${API_BASE}/share/${token}`, {
76+
method: "DELETE",
77+
});
78+
79+
if (!response.ok) {
80+
throw new Error("Failed to revoke share");
81+
}
82+
}
83+
84+
/**
85+
* Report a shared session
86+
*/
87+
export async function reportShare(
88+
token: string,
89+
reason: string
90+
): Promise<void> {
91+
const response = await fetch(`${API_BASE}/share/${token}/report`, {
92+
method: "POST",
93+
headers: { "Content-Type": "application/json" },
94+
body: JSON.stringify({ reason }),
95+
});
96+
97+
if (!response.ok) {
98+
throw new Error("Failed to report share");
99+
}
100+
}

0 commit comments

Comments
 (0)