2026-04-20 09:52:48 +08:00
|
|
|
import { ApiClientError, createApiClient, type ApiClient } from "@geo/http-client";
|
2026-04-19 14:18:20 +08:00
|
|
|
import type {
|
2026-04-20 09:52:48 +08:00
|
|
|
ApiEnvelope,
|
|
|
|
|
ApiErrorEnvelope,
|
2026-04-22 00:24:21 +08:00
|
|
|
CancelDesktopTaskRequest,
|
2026-04-19 14:18:20 +08:00
|
|
|
CompleteDesktopTaskRequest,
|
2026-04-20 09:52:48 +08:00
|
|
|
CreatePublishJobResponse,
|
2026-04-19 14:18:20 +08:00
|
|
|
DesktopArticleContent,
|
|
|
|
|
DesktopAccountInfo,
|
|
|
|
|
DesktopClientHeartbeatRequest,
|
|
|
|
|
DesktopClientHeartbeatResponse,
|
2026-04-20 09:52:48 +08:00
|
|
|
DesktopPublishTaskListResponse,
|
2026-04-19 14:18:20 +08:00
|
|
|
DesktopTaskInfo,
|
|
|
|
|
DesktopRuntimeSessionSyncRequest,
|
|
|
|
|
ExtendDesktopTaskRequest,
|
|
|
|
|
LeaseDesktopTaskRequest,
|
|
|
|
|
LeaseDesktopTaskResponse,
|
2026-04-20 09:52:48 +08:00
|
|
|
ListDesktopPublishTasksParams,
|
2026-04-22 00:24:21 +08:00
|
|
|
MonitoringLeaseTasksPayload,
|
|
|
|
|
MonitoringLeaseTasksResponse,
|
|
|
|
|
MonitoringResumeTasksPayload,
|
|
|
|
|
MonitoringResumeTasksResponse,
|
|
|
|
|
MonitoringSkipTaskPayload,
|
|
|
|
|
MonitoringSkipTaskResponse,
|
|
|
|
|
MonitoringTaskResultPayload,
|
|
|
|
|
MonitoringTaskResultResponse,
|
2026-04-19 14:18:20 +08:00
|
|
|
UpsertDesktopAccountRequest,
|
|
|
|
|
} from "@geo/shared-types";
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
import { canUseRendererDevtoolsProxy, rendererDevtoolsFetch } from "../renderer-devtools-proxy";
|
2026-04-22 00:24:21 +08:00
|
|
|
import {
|
|
|
|
|
failObservedRequest,
|
|
|
|
|
resolveObservedRequest,
|
|
|
|
|
startObservedRequest,
|
|
|
|
|
} from "../network-observer";
|
2026-04-20 09:52:48 +08:00
|
|
|
|
2026-04-19 14:18:20 +08:00
|
|
|
type TransportAuthState = "pending" | "registered" | "expired";
|
2026-04-22 00:24:21 +08:00
|
|
|
type TransportDispatchState = "idle" | "connecting" | "streaming";
|
|
|
|
|
|
|
|
|
|
type ObservedAxiosConfig = {
|
|
|
|
|
method?: string;
|
|
|
|
|
url?: string;
|
|
|
|
|
baseURL?: string;
|
|
|
|
|
headers?: Record<string, unknown>;
|
|
|
|
|
__observedRequestId?: string;
|
|
|
|
|
};
|
2026-04-19 14:18:20 +08:00
|
|
|
|
|
|
|
|
interface DesktopTransportSession {
|
|
|
|
|
baseURL: string;
|
|
|
|
|
clientToken: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 00:24:21 +08:00
|
|
|
interface MonitoringCancelTaskPayload {
|
|
|
|
|
lease_token: string;
|
|
|
|
|
reason?: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface MonitoringCancelTaskResponse {
|
|
|
|
|
task_id: number;
|
|
|
|
|
task_status: string;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 14:18:20 +08:00
|
|
|
let desktopApiClient: ApiClient | null = null;
|
|
|
|
|
let transportSession: DesktopTransportSession = {
|
|
|
|
|
baseURL: process.env.DESKTOP_API_BASE_URL ?? "http://localhost:8080",
|
|
|
|
|
clientToken: null,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const transportState = {
|
|
|
|
|
baseURL: transportSession.baseURL,
|
|
|
|
|
initializedAt: 0,
|
|
|
|
|
mode: "rabbitmq-first" as const,
|
2026-04-20 09:52:48 +08:00
|
|
|
requestDebugMode: "main-process" as "main-process" | "renderer-devtools",
|
2026-04-19 14:18:20 +08:00
|
|
|
auth: "pending" as TransportAuthState,
|
2026-04-22 00:24:21 +08:00
|
|
|
dispatchState: "idle" as TransportDispatchState,
|
2026-04-19 14:18:20 +08:00
|
|
|
lastHeartbeatAt: 0,
|
|
|
|
|
lastHeartbeatStatus: "idle" as "idle" | "success" | "failed",
|
|
|
|
|
lastPullAt: 0,
|
|
|
|
|
lastPullStatus: "idle" as "idle" | "success" | "failed",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function createDesktopClient(baseURL: string): ApiClient {
|
|
|
|
|
const client = createApiClient({
|
|
|
|
|
baseURL,
|
|
|
|
|
timeoutMs: 15000,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
client.raw.interceptors.request.use((config) => {
|
|
|
|
|
if (transportSession.clientToken) {
|
|
|
|
|
config.headers = config.headers ?? {};
|
|
|
|
|
(config.headers as Record<string, string>).Authorization = `Bearer ${transportSession.clientToken}`;
|
|
|
|
|
}
|
2026-04-22 00:24:21 +08:00
|
|
|
|
|
|
|
|
const observedConfig = config as ObservedAxiosConfig;
|
|
|
|
|
observedConfig.__observedRequestId = startObservedRequest({
|
|
|
|
|
source: "transport",
|
|
|
|
|
label: "desktop.api",
|
|
|
|
|
method: config.method,
|
|
|
|
|
url: resolveObservedRequestURL(config.url, config.baseURL ?? baseURL),
|
|
|
|
|
});
|
2026-04-19 14:18:20 +08:00
|
|
|
return config;
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-22 00:24:21 +08:00
|
|
|
client.raw.interceptors.response.use(
|
|
|
|
|
(response) => {
|
|
|
|
|
const observedConfig = response.config as ObservedAxiosConfig;
|
|
|
|
|
if (observedConfig.__observedRequestId) {
|
|
|
|
|
resolveObservedRequest(observedConfig.__observedRequestId, {
|
|
|
|
|
phase: "response",
|
|
|
|
|
status: response.status,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return response;
|
|
|
|
|
},
|
|
|
|
|
(error: unknown) => {
|
|
|
|
|
const responseError = error as {
|
|
|
|
|
config?: ObservedAxiosConfig;
|
|
|
|
|
response?: { status?: number };
|
|
|
|
|
};
|
|
|
|
|
const requestId = responseError.config?.__observedRequestId;
|
|
|
|
|
if (requestId) {
|
|
|
|
|
if (typeof responseError.response?.status === "number") {
|
|
|
|
|
resolveObservedRequest(requestId, {
|
|
|
|
|
phase: "response",
|
|
|
|
|
status: responseError.response.status,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
failObservedRequest(requestId, error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Promise.reject(error);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
2026-04-19 14:18:20 +08:00
|
|
|
return client;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
function currentRequestDebugMode(): "main-process" | "renderer-devtools" {
|
|
|
|
|
return canUseRendererDevtoolsProxy() ? "renderer-devtools" : "main-process";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resolveDesktopURL(path: string): string {
|
|
|
|
|
return new URL(path, transportSession.baseURL).toString();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-26 19:47:35 +08:00
|
|
|
export function resolveDesktopApiURL(path: string): string {
|
|
|
|
|
return resolveDesktopURL(path);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 00:24:21 +08:00
|
|
|
function resolveObservedRequestURL(path: string | undefined, baseURL: string): string {
|
|
|
|
|
if (!path) {
|
|
|
|
|
return baseURL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return new URL(path, baseURL).toString();
|
|
|
|
|
} catch {
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function shouldFallbackToMainProcess(error: unknown): boolean {
|
|
|
|
|
if (error instanceof ApiClientError) {
|
|
|
|
|
if (typeof error.status === "number" && error.status > 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return /Failed to fetch|renderer_devtools_proxy_/i.test(error.message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (error instanceof Error) {
|
|
|
|
|
return /Failed to fetch|renderer_devtools_proxy_/i.test(error.message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
async function proxyDesktopRequest<T, B = unknown>(
|
|
|
|
|
method: "GET" | "POST" | "DELETE",
|
|
|
|
|
path: string,
|
|
|
|
|
body?: B,
|
|
|
|
|
): Promise<T> {
|
|
|
|
|
const headers: Record<string, string> = {
|
|
|
|
|
Accept: "application/json",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let serializedBody: string | undefined;
|
|
|
|
|
if (body !== undefined) {
|
|
|
|
|
headers["Content-Type"] = "application/json";
|
|
|
|
|
serializedBody = JSON.stringify(body);
|
|
|
|
|
}
|
|
|
|
|
if (transportSession.clientToken) {
|
|
|
|
|
headers.Authorization = `Bearer ${transportSession.clientToken}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const response = await rendererDevtoolsFetch({
|
|
|
|
|
url: resolveDesktopURL(path),
|
|
|
|
|
method,
|
|
|
|
|
headers,
|
|
|
|
|
body: serializedBody,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.error) {
|
|
|
|
|
throw new ApiClientError({ message: response.error });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let payload: ApiEnvelope<T> | Partial<ApiErrorEnvelope> | null = null;
|
|
|
|
|
if (response.bodyText) {
|
|
|
|
|
try {
|
|
|
|
|
payload = JSON.parse(response.bodyText) as ApiEnvelope<T> | Partial<ApiErrorEnvelope>;
|
|
|
|
|
} catch {
|
|
|
|
|
payload = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
const errorPayload = payload as Partial<ApiErrorEnvelope> | null;
|
|
|
|
|
throw new ApiClientError({
|
|
|
|
|
message: errorPayload?.message ?? `desktop_http_${response.status}`,
|
|
|
|
|
code: errorPayload?.code ?? 50000,
|
|
|
|
|
status: response.status,
|
|
|
|
|
detail: errorPayload?.detail,
|
|
|
|
|
requestId: errorPayload?.request_id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!payload || typeof payload !== "object" || !("data" in payload)) {
|
|
|
|
|
throw new ApiClientError({
|
|
|
|
|
message: "desktop_invalid_api_envelope",
|
|
|
|
|
status: response.status,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return payload.data as T;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function desktopGet<T>(path: string): Promise<T> {
|
|
|
|
|
transportState.requestDebugMode = currentRequestDebugMode();
|
|
|
|
|
if (transportState.requestDebugMode === "renderer-devtools") {
|
2026-04-22 00:24:21 +08:00
|
|
|
try {
|
|
|
|
|
return await proxyDesktopRequest<T>("GET", path);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if (!shouldFallbackToMainProcess(error)) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
transportState.requestDebugMode = "main-process";
|
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
}
|
|
|
|
|
return getDesktopApiClient().get<T>(path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function desktopPost<T, B = unknown>(path: string, body?: B): Promise<T> {
|
|
|
|
|
transportState.requestDebugMode = currentRequestDebugMode();
|
|
|
|
|
if (transportState.requestDebugMode === "renderer-devtools") {
|
2026-04-22 00:24:21 +08:00
|
|
|
try {
|
|
|
|
|
return await proxyDesktopRequest<T, B>("POST", path, body);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if (!shouldFallbackToMainProcess(error)) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
transportState.requestDebugMode = "main-process";
|
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
}
|
|
|
|
|
return getDesktopApiClient().post<T, B>(path, body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function desktopDelete<T>(path: string): Promise<T> {
|
|
|
|
|
transportState.requestDebugMode = currentRequestDebugMode();
|
|
|
|
|
if (transportState.requestDebugMode === "renderer-devtools") {
|
2026-04-22 00:24:21 +08:00
|
|
|
try {
|
|
|
|
|
return await proxyDesktopRequest<T>("DELETE", path);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if (!shouldFallbackToMainProcess(error)) {
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
transportState.requestDebugMode = "main-process";
|
|
|
|
|
}
|
2026-04-20 09:52:48 +08:00
|
|
|
}
|
|
|
|
|
return getDesktopApiClient().remove<T>(path);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 14:18:20 +08:00
|
|
|
export function initTransport(): ApiClient {
|
|
|
|
|
if (desktopApiClient) {
|
|
|
|
|
return desktopApiClient;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transportState.initializedAt = transportState.initializedAt || Date.now();
|
|
|
|
|
transportState.baseURL = transportSession.baseURL;
|
2026-04-20 09:52:48 +08:00
|
|
|
transportState.requestDebugMode = currentRequestDebugMode();
|
2026-04-19 14:18:20 +08:00
|
|
|
desktopApiClient = createDesktopClient(transportSession.baseURL);
|
|
|
|
|
return desktopApiClient;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function configureTransport(session: DesktopRuntimeSessionSyncRequest | null): ApiClient | null {
|
|
|
|
|
if (!session) {
|
|
|
|
|
transportSession = {
|
|
|
|
|
baseURL: process.env.DESKTOP_API_BASE_URL ?? "http://localhost:8080",
|
|
|
|
|
clientToken: null,
|
|
|
|
|
};
|
|
|
|
|
transportState.baseURL = transportSession.baseURL;
|
2026-04-20 09:52:48 +08:00
|
|
|
transportState.requestDebugMode = currentRequestDebugMode();
|
2026-04-19 14:18:20 +08:00
|
|
|
transportState.auth = "pending";
|
2026-04-22 00:24:21 +08:00
|
|
|
transportState.dispatchState = "idle";
|
2026-04-19 14:18:20 +08:00
|
|
|
desktopApiClient = null;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transportSession = {
|
|
|
|
|
baseURL: session.api_base_url.trim() || "http://localhost:8080",
|
|
|
|
|
clientToken: session.client_token,
|
|
|
|
|
};
|
|
|
|
|
transportState.baseURL = transportSession.baseURL;
|
2026-04-20 09:52:48 +08:00
|
|
|
transportState.requestDebugMode = currentRequestDebugMode();
|
2026-04-19 14:18:20 +08:00
|
|
|
transportState.auth = session.client_token ? "registered" : "pending";
|
2026-04-22 00:24:21 +08:00
|
|
|
transportState.dispatchState = session.client_token ? "connecting" : "idle";
|
2026-04-19 14:18:20 +08:00
|
|
|
transportState.initializedAt = Date.now();
|
|
|
|
|
desktopApiClient = createDesktopClient(transportSession.baseURL);
|
|
|
|
|
return desktopApiClient;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function setTransportAuthState(next: TransportAuthState): void {
|
|
|
|
|
transportState.auth = next;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 00:24:21 +08:00
|
|
|
export function setTransportDispatchState(next: TransportDispatchState): void {
|
|
|
|
|
transportState.dispatchState = next;
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function noteTransportHeartbeat(success: boolean): void {
|
|
|
|
|
transportState.lastHeartbeatAt = Date.now();
|
|
|
|
|
transportState.lastHeartbeatStatus = success ? "success" : "failed";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function noteTransportPull(success: boolean): void {
|
|
|
|
|
transportState.lastPullAt = Date.now();
|
|
|
|
|
transportState.lastPullStatus = success ? "success" : "failed";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getDesktopApiClient(): ApiClient {
|
|
|
|
|
return desktopApiClient ?? initTransport();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getTransportSnapshot() {
|
|
|
|
|
return {
|
|
|
|
|
...transportState,
|
|
|
|
|
hasClient: desktopApiClient !== null,
|
|
|
|
|
hasClientToken: Boolean(transportSession.clientToken),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function heartbeatDesktopClient(
|
|
|
|
|
payload: DesktopClientHeartbeatRequest,
|
|
|
|
|
): Promise<DesktopClientHeartbeatResponse> {
|
2026-04-20 09:52:48 +08:00
|
|
|
return desktopPost<DesktopClientHeartbeatResponse, DesktopClientHeartbeatRequest>(
|
2026-04-19 14:18:20 +08:00
|
|
|
"/api/desktop/clients/heartbeat",
|
|
|
|
|
payload,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
export async function offlineDesktopClient(): Promise<void> {
|
|
|
|
|
await desktopPost<{ server_time: string }, Record<string, never>>(
|
|
|
|
|
"/api/desktop/clients/offline",
|
|
|
|
|
{},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function revokeDesktopClient(): Promise<void> {
|
|
|
|
|
await desktopPost<{ id: string }, Record<string, never>>(
|
|
|
|
|
"/api/desktop/clients/revoke",
|
|
|
|
|
{},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 14:18:20 +08:00
|
|
|
export async function listDesktopAccounts(): Promise<DesktopAccountInfo[]> {
|
2026-04-20 09:52:48 +08:00
|
|
|
return desktopGet<DesktopAccountInfo[]>("/api/desktop/accounts");
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function getDesktopArticleContent(articleId: number): Promise<DesktopArticleContent> {
|
2026-04-20 09:52:48 +08:00
|
|
|
return desktopGet<DesktopArticleContent>(`/api/desktop/content/articles/${articleId}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function listDesktopPublishTasks(
|
|
|
|
|
params: ListDesktopPublishTasksParams = {},
|
|
|
|
|
): Promise<DesktopPublishTaskListResponse> {
|
|
|
|
|
const search = new URLSearchParams();
|
|
|
|
|
if (params.page) {
|
|
|
|
|
search.set("page", String(params.page));
|
|
|
|
|
}
|
|
|
|
|
if (params.page_size) {
|
|
|
|
|
search.set("page_size", String(params.page_size));
|
|
|
|
|
}
|
|
|
|
|
if (params.title?.trim()) {
|
|
|
|
|
search.set("title", params.title.trim());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const query = search.toString();
|
|
|
|
|
const suffix = query ? `?${query}` : "";
|
|
|
|
|
return desktopGet<DesktopPublishTaskListResponse>(`/api/desktop/publish-tasks${suffix}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function retryDesktopPublishTask(taskId: string): Promise<CreatePublishJobResponse> {
|
|
|
|
|
return desktopPost<CreatePublishJobResponse, Record<string, never>>(
|
|
|
|
|
`/api/desktop/publish-tasks/${encodeURIComponent(taskId)}/retry`,
|
|
|
|
|
{},
|
|
|
|
|
);
|
2026-04-19 14:18:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function upsertDesktopAccount(
|
|
|
|
|
payload: UpsertDesktopAccountRequest,
|
|
|
|
|
): Promise<DesktopAccountInfo> {
|
2026-04-20 09:52:48 +08:00
|
|
|
return desktopPost<DesktopAccountInfo, UpsertDesktopAccountRequest>(
|
2026-04-19 14:18:20 +08:00
|
|
|
"/api/desktop/accounts",
|
|
|
|
|
payload,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 09:52:48 +08:00
|
|
|
export async function deleteDesktopAccount(
|
|
|
|
|
desktopId: string,
|
|
|
|
|
ifSyncVersion: number,
|
|
|
|
|
): Promise<DesktopAccountInfo> {
|
|
|
|
|
const params = new URLSearchParams({ if_sync_version: String(ifSyncVersion) });
|
|
|
|
|
return desktopDelete<DesktopAccountInfo>(
|
|
|
|
|
`/api/desktop/accounts/${encodeURIComponent(desktopId)}?${params.toString()}`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-19 14:18:20 +08:00
|
|
|
export async function leaseDesktopTask(
|
|
|
|
|
request: LeaseDesktopTaskRequest,
|
|
|
|
|
): Promise<LeaseDesktopTaskResponse> {
|
|
|
|
|
const taskId = request.task_id?.trim();
|
|
|
|
|
const path = taskId ? `/api/desktop/tasks/${taskId}/lease` : "/api/desktop/tasks/lease";
|
2026-04-20 09:52:48 +08:00
|
|
|
return desktopPost<LeaseDesktopTaskResponse, LeaseDesktopTaskRequest>(
|
|
|
|
|
path,
|
2026-04-19 14:18:20 +08:00
|
|
|
taskId ? { kind: request.kind } : request,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function extendDesktopTask(
|
|
|
|
|
taskId: string,
|
|
|
|
|
payload: ExtendDesktopTaskRequest,
|
|
|
|
|
): Promise<DesktopTaskInfo> {
|
2026-04-20 09:52:48 +08:00
|
|
|
return desktopPost<DesktopTaskInfo, ExtendDesktopTaskRequest>(
|
2026-04-19 14:18:20 +08:00
|
|
|
`/api/desktop/tasks/${taskId}/extend`,
|
|
|
|
|
payload,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function completeDesktopTask(
|
|
|
|
|
taskId: string,
|
|
|
|
|
payload: CompleteDesktopTaskRequest,
|
|
|
|
|
): Promise<DesktopTaskInfo> {
|
2026-04-20 09:52:48 +08:00
|
|
|
return desktopPost<DesktopTaskInfo, CompleteDesktopTaskRequest>(
|
2026-04-19 14:18:20 +08:00
|
|
|
`/api/desktop/tasks/${taskId}/result`,
|
|
|
|
|
payload,
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-04-22 00:24:21 +08:00
|
|
|
|
|
|
|
|
export async function cancelDesktopTask(
|
|
|
|
|
taskId: string,
|
|
|
|
|
payload: CancelDesktopTaskRequest,
|
|
|
|
|
): Promise<DesktopTaskInfo> {
|
|
|
|
|
return desktopPost<DesktopTaskInfo, CancelDesktopTaskRequest>(
|
|
|
|
|
`/api/desktop/tasks/${taskId}/cancel`,
|
|
|
|
|
payload,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function leaseMonitoringTasks(
|
|
|
|
|
payload: MonitoringLeaseTasksPayload = {},
|
|
|
|
|
): Promise<MonitoringLeaseTasksResponse> {
|
|
|
|
|
return desktopPost<MonitoringLeaseTasksResponse, MonitoringLeaseTasksPayload>(
|
|
|
|
|
"/api/desktop/monitoring/tasks/lease",
|
|
|
|
|
payload,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function resumeMonitoringTasks(
|
|
|
|
|
payload: MonitoringResumeTasksPayload = {},
|
|
|
|
|
): Promise<MonitoringResumeTasksResponse> {
|
|
|
|
|
return desktopPost<MonitoringResumeTasksResponse, MonitoringResumeTasksPayload>(
|
|
|
|
|
"/api/desktop/monitoring/tasks/resume",
|
|
|
|
|
payload,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function submitMonitoringTaskResult(
|
|
|
|
|
taskId: number,
|
|
|
|
|
payload: MonitoringTaskResultPayload,
|
|
|
|
|
): Promise<MonitoringTaskResultResponse> {
|
|
|
|
|
return desktopPost<MonitoringTaskResultResponse, MonitoringTaskResultPayload>(
|
|
|
|
|
`/api/desktop/monitoring/tasks/${taskId}/result`,
|
|
|
|
|
payload,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function skipMonitoringTask(
|
|
|
|
|
taskId: number,
|
|
|
|
|
payload: MonitoringSkipTaskPayload,
|
|
|
|
|
): Promise<MonitoringSkipTaskResponse> {
|
|
|
|
|
return desktopPost<MonitoringSkipTaskResponse, MonitoringSkipTaskPayload>(
|
|
|
|
|
`/api/desktop/monitoring/tasks/${taskId}/skip`,
|
|
|
|
|
payload,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function cancelMonitoringTask(
|
|
|
|
|
taskId: number,
|
|
|
|
|
payload: MonitoringCancelTaskPayload,
|
|
|
|
|
): Promise<MonitoringCancelTaskResponse> {
|
|
|
|
|
return desktopPost<MonitoringCancelTaskResponse, MonitoringCancelTaskPayload>(
|
|
|
|
|
`/api/desktop/monitoring/tasks/${taskId}/cancel`,
|
|
|
|
|
payload,
|
|
|
|
|
);
|
|
|
|
|
}
|