Files
deerflow-code/frontend-web/src/core/scheduled-tasks/api.ts
T
吉鑫升andClaude Opus 4.7 10e35055e0 新增智能体收藏、定时任务必须生成MD校验、问答时间显示及账号绑定的常用提示词
- 智能体支持从广场收藏到「我的」(新增 agent_favorites 表与收藏/取消收藏接口)
- 定时任务新增「必须生成Markdown文件」开关:注入提示词并校验生成的 .md 文件且正文超过100字,否则记为失败(存于 execution_context,无需迁移)
- 聊天问答展示时间(年月日 时分):问题在右下角、回答在左下角
- 常用提示词改为与账号绑定(新增 user_custom_prompts 表与 /api/user-prompts 接口),仅在首页输入框上方以居中标签 + 轮播展示,齿轮置于最右侧管理

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 17:16:15 +08:00

371 lines
11 KiB
TypeScript

import { apiFetch } from "@/core/api";
import { getBackendBaseURL } from "@/core/config";
import type {
PublishedScheduledTask,
ScheduledTask,
ScheduledTaskDeliveryProfile,
ScheduledTaskResultFileContent,
ScheduledTaskRun,
ScheduledTaskRunFileUpdateResponse,
ScheduledTaskRunMessagesResponse,
ScheduledTaskSubscription,
SchedulerThread,
} from "./types";
const baseUrl = () => `${getBackendBaseURL()}/api/scheduled-tasks`;
async function parseResponse<T>(response: Response): Promise<T> {
if (!response.ok) {
const error = await response
.json()
.catch(() => ({ detail: "Request failed" }));
throw new Error(error.detail ?? "Request failed");
}
return response.json() as Promise<T>;
}
export async function getSchedulerThread() {
return parseResponse<SchedulerThread>(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<ScheduledTask[]>(
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;
}) {
return parseResponse<ScheduledTask>(
await apiFetch(baseUrl(), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
}),
);
}
export async function listScheduledTaskRuns(taskId: string) {
return parseResponse<ScheduledTaskRun[]>(
await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/runs`),
);
}
export async function getScheduledTaskRun(runId: string) {
return parseResponse<ScheduledTaskRun>(
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<ScheduledTaskRunMessagesResponse>(
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<ScheduledTaskResultFileContent>(
await apiFetch(`${baseUrl()}/runs/${encodeURIComponent(runId)}/files/${fileIndex}`),
);
}
export async function updateScheduledTaskRunFile(
runId: string,
fileIndex: number,
content: string,
) {
return parseResponse<ScheduledTaskRunFileUpdateResponse>(
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<ScheduledTaskDeliveryProfile>(
await apiFetch(`${baseUrl()}/delivery-profile`),
);
}
export async function updateScheduledTaskDeliveryProfile(data: {
element_user_id?: string | null;
}) {
return parseResponse<ScheduledTaskDeliveryProfile>(
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;
}>,
) {
return parseResponse<ScheduledTask>(
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<ScheduledTask>(
await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/pause`, {
method: "POST",
}),
);
}
export async function resumeScheduledTask(taskId: string) {
return parseResponse<ScheduledTask>(
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<PublishedScheduledTask[]>(
await apiFetch(`${baseUrl()}/published${suffix ? `?${suffix}` : ""}`),
);
}
export async function publishScheduledTask(taskId: string) {
return parseResponse<ScheduledTask>(
await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/publish`, {
method: "POST",
}),
);
}
export async function unpublishScheduledTask(taskId: string) {
return parseResponse<ScheduledTask>(
await apiFetch(`${baseUrl()}/${encodeURIComponent(taskId)}/unpublish`, {
method: "POST",
}),
);
}
export async function subscribeScheduledTask(taskId: string, notifyElement: boolean) {
return parseResponse<ScheduledTaskSubscription>(
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<ScheduledTaskSubscription>(
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<ScheduledTask[]>(
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<ScheduledTaskRun[]>(
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<ScheduledTask>(
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",
}),
);
}