All checks were successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
439 lines
11 KiB
TypeScript
439 lines
11 KiB
TypeScript
/**
|
|
* Minimal gateway REST API client for the TUI and CLI commands.
|
|
*/
|
|
|
|
export interface ModelInfo {
|
|
id: string;
|
|
provider: string;
|
|
name: string;
|
|
}
|
|
|
|
export interface ProviderInfo {
|
|
id: string;
|
|
name: string;
|
|
available: boolean;
|
|
models: ModelInfo[];
|
|
}
|
|
|
|
export interface SessionInfo {
|
|
id: string;
|
|
provider: string;
|
|
modelId: string;
|
|
createdAt: string;
|
|
promptCount: number;
|
|
channels: string[];
|
|
durationMs: number;
|
|
}
|
|
|
|
export interface SessionListResult {
|
|
sessions: SessionInfo[];
|
|
total: number;
|
|
}
|
|
|
|
// ── Agent Config types ──
|
|
|
|
export interface AgentConfigInfo {
|
|
id: string;
|
|
name: string;
|
|
provider: string;
|
|
model: string;
|
|
status: string;
|
|
projectId: string | null;
|
|
ownerId: string | null;
|
|
systemPrompt: string | null;
|
|
allowedTools: string[] | null;
|
|
skills: string[] | null;
|
|
isSystem: boolean;
|
|
config: Record<string, unknown> | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
// ── Project types ──
|
|
|
|
export interface ProjectInfo {
|
|
id: string;
|
|
name: string;
|
|
description: string | null;
|
|
status: string;
|
|
ownerId: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
// ── Mission types ──
|
|
|
|
export interface MissionInfo {
|
|
id: string;
|
|
name: string;
|
|
description: string | null;
|
|
status: string;
|
|
projectId: string | null;
|
|
userId: string | null;
|
|
phase: string | null;
|
|
milestones: Record<string, unknown>[] | null;
|
|
config: Record<string, unknown> | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
// ── Mission Task types ──
|
|
|
|
export interface MissionTaskInfo {
|
|
id: string;
|
|
missionId: string;
|
|
taskId: string | null;
|
|
userId: string;
|
|
status: string;
|
|
description: string | null;
|
|
notes: string | null;
|
|
pr: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
// ── Helpers ──
|
|
|
|
function headers(sessionCookie: string, gatewayUrl: string) {
|
|
return { Cookie: sessionCookie, Origin: gatewayUrl };
|
|
}
|
|
|
|
function jsonHeaders(sessionCookie: string, gatewayUrl: string) {
|
|
return { ...headers(sessionCookie, gatewayUrl), 'Content-Type': 'application/json' };
|
|
}
|
|
|
|
async function handleResponse<T>(res: Response, errorPrefix: string): Promise<T> {
|
|
if (!res.ok) {
|
|
const body = await res.text().catch(() => '');
|
|
throw new Error(`${errorPrefix} (${res.status}): ${body}`);
|
|
}
|
|
return (await res.json()) as T;
|
|
}
|
|
|
|
// ── Conversation types ──
|
|
|
|
export interface ConversationInfo {
|
|
id: string;
|
|
title: string | null;
|
|
archived: boolean;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
// ── Conversation endpoints ──
|
|
|
|
export async function createConversation(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
data: { title?: string; projectId?: string } = {},
|
|
): Promise<ConversationInfo> {
|
|
const res = await fetch(`${gatewayUrl}/api/conversations`, {
|
|
method: 'POST',
|
|
headers: jsonHeaders(sessionCookie, gatewayUrl),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return handleResponse<ConversationInfo>(res, 'Failed to create conversation');
|
|
}
|
|
|
|
// ── Provider / Model endpoints ──
|
|
|
|
export async function fetchAvailableModels(
|
|
gatewayUrl: string,
|
|
sessionCookie?: string,
|
|
): Promise<ModelInfo[]> {
|
|
try {
|
|
const res = await fetch(`${gatewayUrl}/api/providers/models`, {
|
|
headers: {
|
|
...(sessionCookie ? { Cookie: sessionCookie } : {}),
|
|
Origin: gatewayUrl,
|
|
},
|
|
});
|
|
if (!res.ok) return [];
|
|
const data = (await res.json()) as ModelInfo[];
|
|
return Array.isArray(data) ? data : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
export async function fetchProviders(
|
|
gatewayUrl: string,
|
|
sessionCookie?: string,
|
|
): Promise<ProviderInfo[]> {
|
|
try {
|
|
const res = await fetch(`${gatewayUrl}/api/providers`, {
|
|
headers: {
|
|
...(sessionCookie ? { Cookie: sessionCookie } : {}),
|
|
Origin: gatewayUrl,
|
|
},
|
|
});
|
|
if (!res.ok) return [];
|
|
const data = (await res.json()) as ProviderInfo[];
|
|
return Array.isArray(data) ? data : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
// ── Session endpoints ──
|
|
|
|
export async function fetchSessions(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
): Promise<SessionListResult> {
|
|
const res = await fetch(`${gatewayUrl}/api/sessions`, {
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
});
|
|
return handleResponse<SessionListResult>(res, 'Failed to list sessions');
|
|
}
|
|
|
|
export async function deleteSession(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
sessionId: string,
|
|
): Promise<void> {
|
|
const res = await fetch(`${gatewayUrl}/api/sessions/${encodeURIComponent(sessionId)}`, {
|
|
method: 'DELETE',
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
});
|
|
if (!res.ok && res.status !== 204) {
|
|
const body = await res.text().catch(() => '');
|
|
throw new Error(`Failed to destroy session (${res.status}): ${body}`);
|
|
}
|
|
}
|
|
|
|
// ── Agent Config endpoints ──
|
|
|
|
export async function fetchAgentConfigs(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
): Promise<AgentConfigInfo[]> {
|
|
const res = await fetch(`${gatewayUrl}/api/agents`, {
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
});
|
|
return handleResponse<AgentConfigInfo[]>(res, 'Failed to list agents');
|
|
}
|
|
|
|
export async function fetchAgentConfig(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
id: string,
|
|
): Promise<AgentConfigInfo> {
|
|
const res = await fetch(`${gatewayUrl}/api/agents/${encodeURIComponent(id)}`, {
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
});
|
|
return handleResponse<AgentConfigInfo>(res, 'Failed to get agent');
|
|
}
|
|
|
|
export async function createAgentConfig(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
data: {
|
|
name: string;
|
|
provider: string;
|
|
model: string;
|
|
projectId?: string;
|
|
systemPrompt?: string;
|
|
allowedTools?: string[];
|
|
skills?: string[];
|
|
config?: Record<string, unknown>;
|
|
},
|
|
): Promise<AgentConfigInfo> {
|
|
const res = await fetch(`${gatewayUrl}/api/agents`, {
|
|
method: 'POST',
|
|
headers: jsonHeaders(sessionCookie, gatewayUrl),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return handleResponse<AgentConfigInfo>(res, 'Failed to create agent');
|
|
}
|
|
|
|
export async function updateAgentConfig(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
id: string,
|
|
data: Record<string, unknown>,
|
|
): Promise<AgentConfigInfo> {
|
|
const res = await fetch(`${gatewayUrl}/api/agents/${encodeURIComponent(id)}`, {
|
|
method: 'PATCH',
|
|
headers: jsonHeaders(sessionCookie, gatewayUrl),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return handleResponse<AgentConfigInfo>(res, 'Failed to update agent');
|
|
}
|
|
|
|
export async function deleteAgentConfig(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
id: string,
|
|
): Promise<void> {
|
|
const res = await fetch(`${gatewayUrl}/api/agents/${encodeURIComponent(id)}`, {
|
|
method: 'DELETE',
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
});
|
|
if (!res.ok && res.status !== 204) {
|
|
const body = await res.text().catch(() => '');
|
|
throw new Error(`Failed to delete agent (${res.status}): ${body}`);
|
|
}
|
|
}
|
|
|
|
// ── Project endpoints ──
|
|
|
|
export async function fetchProjects(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
): Promise<ProjectInfo[]> {
|
|
const res = await fetch(`${gatewayUrl}/api/projects`, {
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
});
|
|
return handleResponse<ProjectInfo[]>(res, 'Failed to list projects');
|
|
}
|
|
|
|
// ── Mission endpoints ──
|
|
|
|
export async function fetchMissions(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
): Promise<MissionInfo[]> {
|
|
const res = await fetch(`${gatewayUrl}/api/missions`, {
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
});
|
|
return handleResponse<MissionInfo[]>(res, 'Failed to list missions');
|
|
}
|
|
|
|
export async function fetchMission(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
id: string,
|
|
): Promise<MissionInfo> {
|
|
const res = await fetch(`${gatewayUrl}/api/missions/${encodeURIComponent(id)}`, {
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
});
|
|
return handleResponse<MissionInfo>(res, 'Failed to get mission');
|
|
}
|
|
|
|
export async function createMission(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
data: {
|
|
name: string;
|
|
description?: string;
|
|
projectId?: string;
|
|
status?: string;
|
|
phase?: string;
|
|
milestones?: Record<string, unknown>[];
|
|
config?: Record<string, unknown>;
|
|
},
|
|
): Promise<MissionInfo> {
|
|
const res = await fetch(`${gatewayUrl}/api/missions`, {
|
|
method: 'POST',
|
|
headers: jsonHeaders(sessionCookie, gatewayUrl),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return handleResponse<MissionInfo>(res, 'Failed to create mission');
|
|
}
|
|
|
|
export async function updateMission(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
id: string,
|
|
data: Record<string, unknown>,
|
|
): Promise<MissionInfo> {
|
|
const res = await fetch(`${gatewayUrl}/api/missions/${encodeURIComponent(id)}`, {
|
|
method: 'PATCH',
|
|
headers: jsonHeaders(sessionCookie, gatewayUrl),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return handleResponse<MissionInfo>(res, 'Failed to update mission');
|
|
}
|
|
|
|
export async function deleteMission(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
id: string,
|
|
): Promise<void> {
|
|
const res = await fetch(`${gatewayUrl}/api/missions/${encodeURIComponent(id)}`, {
|
|
method: 'DELETE',
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
});
|
|
if (!res.ok && res.status !== 204) {
|
|
const body = await res.text().catch(() => '');
|
|
throw new Error(`Failed to delete mission (${res.status}): ${body}`);
|
|
}
|
|
}
|
|
|
|
// ── Conversation Message types ──
|
|
|
|
export interface ConversationMessage {
|
|
id: string;
|
|
role: 'user' | 'assistant' | 'system' | 'tool';
|
|
content: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
// ── Conversation Message endpoints ──
|
|
|
|
export async function fetchConversationMessages(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
conversationId: string,
|
|
): Promise<ConversationMessage[]> {
|
|
const res = await fetch(
|
|
`${gatewayUrl}/api/conversations/${encodeURIComponent(conversationId)}/messages`,
|
|
{
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
},
|
|
);
|
|
return handleResponse<ConversationMessage[]>(res, 'Failed to fetch conversation messages');
|
|
}
|
|
|
|
// ── Mission Task endpoints ──
|
|
|
|
export async function fetchMissionTasks(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
missionId: string,
|
|
): Promise<MissionTaskInfo[]> {
|
|
const res = await fetch(`${gatewayUrl}/api/missions/${encodeURIComponent(missionId)}/tasks`, {
|
|
headers: headers(sessionCookie, gatewayUrl),
|
|
});
|
|
return handleResponse<MissionTaskInfo[]>(res, 'Failed to list mission tasks');
|
|
}
|
|
|
|
export async function createMissionTask(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
missionId: string,
|
|
data: {
|
|
description?: string;
|
|
status?: string;
|
|
notes?: string;
|
|
pr?: string;
|
|
taskId?: string;
|
|
},
|
|
): Promise<MissionTaskInfo> {
|
|
const res = await fetch(`${gatewayUrl}/api/missions/${encodeURIComponent(missionId)}/tasks`, {
|
|
method: 'POST',
|
|
headers: jsonHeaders(sessionCookie, gatewayUrl),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return handleResponse<MissionTaskInfo>(res, 'Failed to create mission task');
|
|
}
|
|
|
|
export async function updateMissionTask(
|
|
gatewayUrl: string,
|
|
sessionCookie: string,
|
|
missionId: string,
|
|
taskId: string,
|
|
data: Record<string, unknown>,
|
|
): Promise<MissionTaskInfo> {
|
|
const res = await fetch(
|
|
`${gatewayUrl}/api/missions/${encodeURIComponent(missionId)}/tasks/${encodeURIComponent(taskId)}`,
|
|
{
|
|
method: 'PATCH',
|
|
headers: jsonHeaders(sessionCookie, gatewayUrl),
|
|
body: JSON.stringify(data),
|
|
},
|
|
);
|
|
return handleResponse<MissionTaskInfo>(res, 'Failed to update mission task');
|
|
}
|