This commit is contained in:
2026-05-22 17:05:39 +07:00
parent 582960cc32
commit 9e6f57be35
12 changed files with 4084 additions and 60 deletions

View File

@@ -0,0 +1,204 @@
export const DEFAULT_PACKAGE_BASE_URL = normalizeUrl(
import.meta.env.VITE_PACKAGE_BASE_URL || window.location.origin
);
export const DEFAULT_AGENT_BASE_URL = normalizeUrl(
import.meta.env.VITE_AGENT_BASE_URL || 'http://127.0.0.1:5010'
);
export function normalizeUrl(value) {
const text = String(value || '').trim();
return text.replace(/\/+$/, '');
}
export function joinUrl(baseUrl, path) {
const normalizedBaseUrl = normalizeUrl(baseUrl);
const normalizedPath = String(path || '').startsWith('/') ? path : `/${path || ''}`;
return `${normalizedBaseUrl}${normalizedPath}`;
}
async function requestJson(baseUrl, path, options = {}) {
const {
timeoutMs = 8000,
body,
headers,
...fetchOptions
} = options;
const controller = new AbortController();
const timeout = window.setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(joinUrl(baseUrl, path), {
...fetchOptions,
headers: {
Accept: 'application/json',
...(body ? { 'Content-Type': 'application/json' } : {}),
...headers
},
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal
});
const text = await response.text();
let payload = null;
if (text) {
try {
payload = JSON.parse(text);
} catch {
payload = text;
}
}
if (!response.ok) {
const detail = payload?.detail || payload?.error || payload || response.statusText;
throw new Error(`${response.status} ${detail}`);
}
return payload;
} catch (error) {
if (error?.name === 'AbortError') {
throw new Error(`Request timeout: ${joinUrl(baseUrl, path)}`);
}
throw error;
} finally {
window.clearTimeout(timeout);
}
}
export async function fetchPackageApps(packageBaseUrl) {
const payload = await requestJson(packageBaseUrl, '/api/apps', { timeoutMs: 10000 });
return Array.isArray(payload?.apps) ? payload.apps.map(normalizePackageApp) : [];
}
export async function fetchApplicationDetail(packageBaseUrl, appId) {
return requestJson(packageBaseUrl, `/api/apps/${encodeURIComponent(appId)}`, { timeoutMs: 10000 });
}
export async function fetchApplicationManifest(packageBaseUrl, appId, version) {
return requestJson(
packageBaseUrl,
`/api/apps/${encodeURIComponent(appId)}/versions/${encodeURIComponent(version)}/manifest`,
{ timeoutMs: 10000 }
);
}
export async function fetchAgentHealth(agentBaseUrl) {
return requestJson(agentBaseUrl, '/health', { timeoutMs: 2800 });
}
export async function fetchAgentSystemInfo(agentBaseUrl) {
return requestJson(agentBaseUrl, '/system-info', { timeoutMs: 5000 });
}
export async function fetchInstalledApps(agentBaseUrl) {
const payload = await requestJson(agentBaseUrl, '/apps/installed', { timeoutMs: 7000 });
return Array.isArray(payload) ? payload.map(normalizeInstalledApp) : [];
}
export async function queueInstall(agentBaseUrl, app) {
return requestJson(agentBaseUrl, '/apps/install', {
method: 'POST',
timeoutMs: 10000,
body: {
appId: app.appId,
appName: app.appName,
version: app.version
}
});
}
export async function queueUpdate(agentBaseUrl, app, installedApp) {
return requestJson(agentBaseUrl, '/apps/update', {
method: 'POST',
timeoutMs: 10000,
body: {
appId: app.appId,
appName: app.appName,
currentVersion: installedApp?.version || '',
targetVersion: app.version
}
});
}
export async function queueRemove(agentBaseUrl, app) {
return requestJson(agentBaseUrl, '/apps/remove', {
method: 'POST',
timeoutMs: 10000,
body: {
appId: app.appId,
purge: false
}
});
}
export async function fetchTaskStatus(agentBaseUrl, taskId) {
return normalizeTask(await requestJson(agentBaseUrl, `/tasks/${encodeURIComponent(taskId)}`, { timeoutMs: 7000 }));
}
export async function fetchTaskLogs(agentBaseUrl, taskId) {
const payload = await requestJson(agentBaseUrl, `/tasks/${encodeURIComponent(taskId)}/logs`, { timeoutMs: 7000 });
return Array.isArray(payload?.logs) ? payload.logs.map(normalizeLog) : [];
}
export async function fetchTaskComponents(agentBaseUrl, taskId) {
const payload = await requestJson(agentBaseUrl, `/tasks/${encodeURIComponent(taskId)}/components`, { timeoutMs: 7000 });
return Array.isArray(payload?.components) ? payload.components.map(normalizeComponent) : [];
}
function normalizePackageApp(app) {
return {
appId: String(app.appId || app.app_id || '').trim(),
appName: String(app.appName || app.app_name || app.name || '').trim(),
version: String(app.version || '').trim(),
status: String(app.status || 'Released').trim(),
packageCount: Number(app.packageCount || app.package_count || 0)
};
}
function normalizeInstalledApp(app) {
return {
appId: String(app.appId || app.app_id || '').trim(),
appName: String(app.appName || app.app_name || '').trim(),
version: String(app.installedVersion || app.version || app.package_version || '').trim(),
status: String(app.status || 'installed').trim(),
installedAt: app.installedAt || app.installed_at || '',
updatedAt: app.updatedAt || app.updated_at || ''
};
}
function normalizeTask(task) {
return {
taskId: task.taskId || task.task_id,
type: task.type,
appId: task.appId || task.app_id,
appName: task.appName || task.app_name,
status: task.status,
progress: Number(task.progress || 0),
currentStep: task.currentStep || task.current_step,
currentComponentId: task.currentComponentId || task.current_component_id,
errorMessage: task.errorMessage || task.error_message,
createdAt: task.createdAt || task.created_at,
startedAt: task.startedAt || task.started_at,
finishedAt: task.finishedAt || task.finished_at
};
}
function normalizeLog(log) {
return {
time: log.time || log.timestamp || '',
level: log.level || 'info',
message: log.message || ''
};
}
function normalizeComponent(component) {
return {
componentId: component.componentId || component.component_id,
type: component.type,
status: component.status,
progress: Number(component.progress || 0),
currentStep: component.currentStep || component.current_step,
errorMessage: component.errorMessage || component.error_message,
startedAt: component.startedAt || component.started_at,
finishedAt: component.finishedAt || component.finished_at
};
}