import { apiFetch } from "@/core/api"; import { getBackendBaseURL } from "@/core/config"; import type { HtmlPageFavorite, PublishedScheduledTask, ScheduledTask, ScheduledTaskDeliveryProfile, ScheduledTaskExecutionContext, ScheduledTaskResultFileContent, ScheduledTaskRun, ScheduledTaskRunFileUpdateResponse, ScheduledTaskRunHtml, ScheduledTaskRunMessagesResponse, ScheduledTaskSubscription, SchedulerThread, } from "./types"; const baseUrl = () => `${getBackendBaseURL()}/api/scheduled-tasks`; async function parseResponse(response: Response): Promise { if (!response.ok) { const error = await response .json() .catch(() => ({ detail: "Request failed" })); throw new Error(error.detail ?? "Request failed"); } return response.json() as Promise; } export async function getSchedulerThread() { return parseResponse(await apiFetch(`${baseUrl()}/thread`)); } export interface ListScheduledTasksParams { search?: string; tagIds?: string[]; } export async function listScheduledTasks(params: ListScheduledTasksParams = {}) { const qs = new URLSearchParams(); if (params.search) qs.set("search", params.search); for (const id of params.tagIds ?? []) qs.append("tag_ids", id); const query = qs.toString(); return parseResponse( await apiFetch(`${baseUrl()}${query ? `?${query}` : ""}`), ); } export async function createScheduledTask(data: { name: string; description?: string | null; prompt: string; schedule: string; timezone?: string | null; execution_agent_name?: string | null; require_markdown?: boolean; execution_context?: ScheduledTaskExecutionContext | null; }) { return parseResponse( await apiFetch(baseUrl(), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), ); } export async function listScheduledTaskRuns(taskId: string) { return parseResponse( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/runs`), ); } export async function getScheduledTaskRun(runId: string) { return parseResponse( await apiFetch(`${baseUrl()}/runs/${encodeURIComponent(runId)}`), ); } export async function deleteScheduledTaskRun(runId: string) { return parseResponse<{ success: boolean }>( await apiFetch(`${baseUrl()}/runs/${encodeURIComponent(runId)}`, { method: "DELETE", }), ); } /** Agent messages for a run's underlying agent run — used to inspect a stuck/running run. */ export async function getScheduledTaskRunMessages(runId: string) { return parseResponse( await apiFetch(`${baseUrl()}/runs/${encodeURIComponent(runId)}/messages`), ); } export function scheduledTaskRunDownloadUrl(runId: string) { return `${baseUrl()}/runs/${encodeURIComponent(runId)}/download`; } export function scheduledTaskRunFileDownloadUrl(runId: string, fileIndex: number) { return `${baseUrl()}/runs/${encodeURIComponent(runId)}/files/${fileIndex}/download`; } export async function getScheduledTaskRunFileContent(runId: string, fileIndex: number) { return parseResponse( await apiFetch(`${baseUrl()}/runs/${encodeURIComponent(runId)}/files/${fileIndex}`), ); } export async function updateScheduledTaskRunFile( runId: string, fileIndex: number, content: string, ) { return parseResponse( await apiFetch(`${baseUrl()}/runs/${encodeURIComponent(runId)}/files/${fileIndex}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ content }), }), ); } function filenameFromContentDisposition(header: string | null): string | null { if (!header) return null; const star = /filename\*=(?:UTF-8'')?([^;]+)/i.exec(header); if (star) { const raw = star[1].replace(/["']/g, "").trim(); try { return decodeURIComponent(raw); } catch { return raw; } } const plain = /filename="?([^";\n]+)"?/i.exec(header); return plain ? plain[1].trim() : null; } async function downloadResponse(response: Response, fallbackFilename: string) { if (!response.ok) { const error = await response .json() .catch(() => ({ detail: "Download failed" })); const detail = error.detail; if (typeof detail === "string") throw new Error(detail); if ( detail && typeof detail === "object" && "message" in detail && typeof detail.message === "string" ) { throw new Error(detail.message); } throw new Error("Download failed"); } const blob = await response.blob(); const contentType = response.headers.get("Content-Type") ?? blob.type; const fallbackByType = contentType.includes("application/zip") || contentType.includes("application/x-zip-compressed") ? fallbackFilename.replace(/\.[^.]+$/, ".zip") : fallbackFilename; const filename = filenameFromContentDisposition(response.headers.get("Content-Disposition")) ?? fallbackByType; const url = URL.createObjectURL(blob); const anchor = document.createElement("a"); anchor.href = url; anchor.download = filename; anchor.rel = "noopener"; document.body.appendChild(anchor); anchor.click(); anchor.remove(); window.setTimeout(() => URL.revokeObjectURL(url), 30_000); } export async function downloadScheduledTaskRun(runId: string) { await downloadResponse( await apiFetch(scheduledTaskRunDownloadUrl(runId)), "scheduled-task-result.zip", ); } export async function downloadScheduledTaskRunFile(runId: string, fileIndex: number) { await downloadResponse( await apiFetch(scheduledTaskRunFileDownloadUrl(runId, fileIndex)), "scheduled-task-file", ); } export async function getScheduledTaskDeliveryProfile() { return parseResponse( await apiFetch(`${baseUrl()}/delivery-profile`), ); } export async function updateScheduledTaskDeliveryProfile(data: { element_user_id?: string | null; }) { return parseResponse( await apiFetch(`${baseUrl()}/delivery-profile`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), ); } export async function updateScheduledTask( taskId: string, data: Partial<{ name: string; description: string | null; prompt: string; schedule: string; timezone: string; enabled: boolean; execution_agent_name: string | null; require_markdown: boolean; execution_context: ScheduledTaskExecutionContext | null; }>, ) { return parseResponse( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), ); } export async function deleteScheduledTask(taskId: string) { return parseResponse<{ success: boolean }>( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}`, { method: "DELETE", }), ); } export async function pauseScheduledTask(taskId: string) { return parseResponse( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/pause`, { method: "POST", }), ); } export async function resumeScheduledTask(taskId: string) { return parseResponse( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/resume`, { method: "POST", }), ); } export async function triggerScheduledTask(taskId: string) { return parseResponse<{ success: boolean; task: ScheduledTask }>( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/trigger`, { method: "POST", }), ); } export async function listPublishedScheduledTasks(params: { limit?: number; offset?: number } = {}) { const query = new URLSearchParams(); if (params.limit != null) query.set("limit", String(params.limit)); if (params.offset != null) query.set("offset", String(params.offset)); const suffix = query.toString(); return parseResponse( await apiFetch(`${baseUrl()}/published${suffix ? `?${suffix}` : ""}`), ); } export async function publishScheduledTask(taskId: string) { return parseResponse( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/publish`, { method: "POST", }), ); } export async function unpublishScheduledTask(taskId: string) { return parseResponse( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/unpublish`, { method: "POST", }), ); } export async function subscribeScheduledTask(taskId: string, notifyElement: boolean) { return parseResponse( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/subscribe`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ notify_element: notifyElement }), }), ); } export async function updateScheduledTaskSubscription(taskId: string, notifyElement: boolean) { return parseResponse( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/subscribe`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ notify_element: notifyElement }), }), ); } export async function unsubscribeScheduledTask(taskId: string) { return parseResponse<{ success: boolean }>( await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/subscribe`, { method: "DELETE", }), ); } // --- Admin (cross-user management) --- /** List every scheduled task across all users. Admin-only endpoint. */ export async function listAllScheduledTasks(search?: string) { const qs = new URLSearchParams(); if (search) qs.set("search", search); const query = qs.toString(); return parseResponse( await apiFetch(`${baseUrl()}/admin/all${query ? `?${query}` : ""}`), ); } /** List runs for any task regardless of owner. Admin-only endpoint. */ export async function adminListScheduledTaskRuns(taskId: string) { return parseResponse( await apiFetch(`${baseUrl()}/admin/${encodeURIComponent(taskId)}/runs`), ); } /** Update any task regardless of owner. Admin-only endpoint. */ export async function adminUpdateScheduledTask( taskId: string, data: Partial<{ name: string; description: string | null; prompt: string; schedule: string; timezone: string; enabled: boolean; execution_agent_name: string | null; require_markdown: boolean; }>, ) { return parseResponse( await apiFetch(`${baseUrl()}/admin/${encodeURIComponent(taskId)}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), ); } /** Delete any task regardless of owner. Admin-only endpoint. */ export async function adminDeleteScheduledTask(taskId: string) { return parseResponse<{ success: boolean }>( await apiFetch(`${baseUrl()}/admin/${encodeURIComponent(taskId)}`, { method: "DELETE", }), ); } /** Trigger an immediate run of any task regardless of owner. Admin-only. */ export async function adminTriggerScheduledTask(taskId: string) { return parseResponse<{ success: boolean; task: ScheduledTask }>( await apiFetch(`${baseUrl()}/admin/${encodeURIComponent(taskId)}/trigger`, { method: "POST", }), ); } // --- HTML page tasks --- /** Fetch the generated HTML document for an ``html_page`` run. */ export async function getScheduledTaskRunHtml(runId: string) { return parseResponse( await apiFetch(`${baseUrl()}/runs/${encodeURIComponent(runId)}/html`), ); } const favoritesUrl = () => `${getBackendBaseURL()}/api/html-page-favorites`; export async function listHtmlPageFavorites() { return parseResponse(await apiFetch(favoritesUrl())); } export async function getHtmlPageFavorite(favoriteId: string) { return parseResponse( await apiFetch(`${favoritesUrl()}/${encodeURIComponent(favoriteId)}`), ); } export async function createHtmlPageFavorite(data: { page_id: string; title?: string | null; description?: string | null; tags?: string[]; }) { return parseResponse( await apiFetch(favoritesUrl(), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), ); } export async function deleteHtmlPageFavorite(favoriteId: string) { return parseResponse<{ success: boolean }>( await apiFetch(`${favoritesUrl()}/${encodeURIComponent(favoriteId)}`, { method: "DELETE", }), ); }