LazyBoy2/web/src/api.ts

132 lines
4.5 KiB
TypeScript
Raw Normal View History

2026-09-15 05:20:44 +00:00
export type HumanQuestion = {
question?: string; reason?: string; options?: unknown[]; kind?: string;
task_id?: string; question_id?: string; site_url?: string; surface?: string;
handover?: boolean; submitted?: boolean;
};
2026-09-15 03:20:42 +00:00
export type AgentActivity = {
state: "idle" | "queued" | "running" | "waiting_input";
running: boolean;
queued: boolean;
observed_at_ms: number;
active_task_ids: string[];
2026-09-15 05:20:44 +00:00
question?: HumanQuestion | null;
2026-09-15 03:20:42 +00:00
};
2026-09-14 09:08:20 +00:00
export type AgentRow = {
id: string;
name: string;
expertise?: string;
2026-09-15 09:07:16 +00:00
title?: string;
description?: string;
tags?: string[];
avatar_color?: string;
avatar_shape?: string;
has_avatar?: boolean;
avatar_url?: string | null;
2026-09-14 09:08:20 +00:00
preview?: string;
2026-09-15 09:07:16 +00:00
last_at?: string | null;
2026-09-14 09:08:20 +00:00
running?: boolean;
2026-09-15 03:20:42 +00:00
activity?: AgentActivity;
2026-09-14 09:08:20 +00:00
};
2026-09-15 09:07:16 +00:00
export type AgentProfile = {
name: string;
title: string;
description: string;
tags: string[];
avatar_color: string;
avatar_shape: string;
};
2026-09-15 03:20:42 +00:00
export type TranscriptItem = { role: "user" | "assistant"; content: string; at?: string };
2026-09-14 09:08:20 +00:00
2026-09-15 09:07:16 +00:00
export type AgentDetail = AgentRow & {
2026-09-14 09:08:20 +00:00
expertise: string;
preview: string;
transcript: TranscriptItem[];
running: boolean;
2026-09-15 03:20:42 +00:00
activity: AgentActivity;
2026-09-14 09:08:20 +00:00
};
export type TeamEvent = {
id: number;
kind: string;
task_id?: string | null;
payload: Record<string, unknown>;
};
async function request<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(path, {
...init,
2026-09-16 06:47:32 +00:00
signal: init?.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(15000)]) : (!init?.method || init.method === "GET" ? AbortSignal.timeout(15000) : undefined),
2026-09-14 09:08:20 +00:00
headers: { "content-type": "application/json", ...(init?.headers || {}) },
});
const raw = await res.text();
let data: unknown = null;
try {
data = raw ? JSON.parse(raw) : null;
} catch {
data = raw;
}
if (!res.ok) {
const err = data as { error?: string } | null;
throw new Error(err?.error || raw || String(res.status));
}
return data as T;
}
export const api = {
health: () => request<{ ok: boolean; service: boolean; model: string }>("/api/health"),
agents: async () => {
const data = await request<AgentRow[] | { agents?: AgentRow[] }>("/api/agents");
const agents = Array.isArray(data) ? data : data.agents || [];
return { agents };
},
2026-09-15 09:07:16 +00:00
createAgent: (profile: AgentProfile) =>
request<AgentRow>("/api/agents", { method: "POST", body: JSON.stringify(profile) }),
updateAgent: (id: string, profile: AgentProfile) =>
request<AgentRow>(`/api/agents/${id}`, { method: "PATCH", body: JSON.stringify(profile) }),
setAvatar: async (id: string, png: Blob) => {
const res = await fetch(`/api/agents/${encodeURIComponent(id)}/avatar`, {
method: "PUT",
headers: { "content-type": "image/png" },
body: png,
});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error((data as { error?: string }).error || String(res.status));
return data as AgentRow;
},
clearAvatar: (id: string) =>
request<AgentRow>(`/api/agents/${encodeURIComponent(id)}/avatar`, { method: "DELETE" }),
2026-09-15 03:20:42 +00:00
deleteAgent: (id: string) => request<{ ok: boolean; id: string; name: string }>(`/api/agents/${id}`, { method: "DELETE" }),
2026-09-16 06:47:32 +00:00
agent: (id: string, signal?: AbortSignal) => request<AgentDetail>(`/api/agents/${id}`, { signal }),
2026-09-15 03:20:42 +00:00
activity: (id: string) => request<AgentActivity>(`/api/agents/${id}/activity`, { signal: AbortSignal.timeout(8000) }),
2026-09-14 09:08:20 +00:00
send: (id: string, text: string) =>
request<{ queued?: string }>(`/api/agents/${id}/messages`, { method: "POST", body: JSON.stringify({ text }) }),
2026-09-15 05:20:44 +00:00
handoverDone: (id: string, taskId: string, questionId: string, action: "done" | "cancel" = "done") => request(`/api/agents/${id}/handover`, { method: "POST", body: JSON.stringify({ task_id: taskId, question_id: questionId, action }), signal: AbortSignal.timeout(8000) }),
2026-09-14 09:08:20 +00:00
stop: (id: string) => request(`/api/agents/${id}/stop`, { method: "POST", body: "{}" }),
2026-09-16 06:40:00 +00:00
computerStatus: (id: string) =>
2026-09-15 03:20:42 +00:00
request<{
ready: boolean;
2026-09-16 06:40:00 +00:00
state?: string;
viewer_url: string;
error?: string;
revision?: string;
crowded?: boolean;
}>(`/api/agents/${encodeURIComponent(id)}/computer`),
computer: (id: string, action: "start" | "restart" | "update" = "start") =>
request<{
ready: boolean;
state?: string;
2026-09-15 03:20:42 +00:00
viewer_url: string;
error?: string;
updated?: boolean;
revision?: string;
2026-09-16 06:40:00 +00:00
crowded?: boolean;
}>(`/api/agents/${encodeURIComponent(id)}/computer`, {
2026-09-14 09:08:20 +00:00
method: "POST",
2026-09-15 03:20:42 +00:00
body: JSON.stringify({ action }),
2026-09-14 09:08:20 +00:00
}),
};