feat(desktop): implement workspace foundation + desktop-client skeleton
Plan 0 (workspaces): add workspaces + workspace_memberships schema, extend JWT/Actor/claims with primary_workspace_id, seed default workspace per tenant, thread workspace_id through tenant monitoring quota. Plan A (desktop skeleton): new Electron app (apps/desktop-client) with main/ preload/renderer, shared Vue component package (packages/ui-shared), and server surface — desktop client registration + token rotation + heartbeat, SSE task event stream, desktop accounts/tasks/content handlers, publish job endpoint, and supporting repositories, services, sqlc queries, and migrations. Hard cutover per plan: remove browser-extension monitoring callback endpoints, stub legacy media API in admin-web, and delete monitoring_callback_handler.go.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import type {
|
||||
ApiEnvelope,
|
||||
JsonValue,
|
||||
MonitoringHeartbeatPlatformPayload,
|
||||
MonitoringHeartbeatResponse,
|
||||
@@ -161,30 +160,9 @@ function toMonitoringPlatformPayload(platform: StoredMonitoringPlatformState): M
|
||||
}
|
||||
|
||||
async function postMonitoringApi<T>(path: string, payload: unknown): Promise<T> {
|
||||
const state = await getExtensionState();
|
||||
if (!state.api_base_url) {
|
||||
throw new Error("monitoring_api_base_url_missing");
|
||||
}
|
||||
if (!state.plugin_installation_id || !state.installation_token) {
|
||||
throw new Error("monitoring_installation_credentials_missing");
|
||||
}
|
||||
|
||||
const response = await fetch(new URL(path.replace(/^\//, ""), normalizeBaseUrl(state.api_base_url)).toString(), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Geo-Installation-Id": String(state.plugin_installation_id),
|
||||
"X-Geo-Installation-Token": state.installation_token,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
const body = (await response.json()) as ApiEnvelope<T> | { message?: string };
|
||||
if (!response.ok || !("data" in body)) {
|
||||
throw new Error(body.message || "monitoring_plugin_request_failed");
|
||||
}
|
||||
|
||||
return body.data;
|
||||
void path;
|
||||
void payload;
|
||||
throw new Error("browser_extension_monitoring_removed_use_desktop_client");
|
||||
}
|
||||
|
||||
async function detectAllMonitoringPlatforms(): Promise<StoredMonitoringPlatformState[]> {
|
||||
@@ -255,11 +233,12 @@ async function markMonitoringTaskSkipped(task: MonitoringLeaseTask, reason: stri
|
||||
error_message: normalizeText(errorMessage),
|
||||
completed_at: new Date().toISOString(),
|
||||
};
|
||||
return await postMonitoringApi<MonitoringSkipTaskResponse>(`/api/plugin/monitoring/tasks/${task.task_id}/skip`, payload);
|
||||
return await postMonitoringApi<MonitoringSkipTaskResponse>("skip_task", payload);
|
||||
}
|
||||
|
||||
async function submitMonitoringTaskResult(task: MonitoringLeaseTask, payload: MonitoringTaskResultPayload): Promise<MonitoringTaskResultResponse> {
|
||||
return await postMonitoringApi<MonitoringTaskResultResponse>(`/api/plugin/monitoring/tasks/${task.task_id}/result`, payload);
|
||||
void task;
|
||||
return await postMonitoringApi<MonitoringTaskResultResponse>("submit_result", payload);
|
||||
}
|
||||
|
||||
async function processMonitoringLeasedTask(
|
||||
@@ -290,7 +269,7 @@ async function resumeMonitoringTasks(aiPlatformIDs: string[]): Promise<Monitorin
|
||||
const payload: MonitoringResumeTasksPayload = {
|
||||
ai_platform_ids: aiPlatformIDs,
|
||||
};
|
||||
const resumed = await postMonitoringApi<MonitoringResumeTasksResponse>("/api/plugin/monitoring/tasks/resume", payload);
|
||||
const resumed = await postMonitoringApi<MonitoringResumeTasksResponse>("resume_tasks", payload);
|
||||
return resumed.tasks;
|
||||
}
|
||||
|
||||
@@ -299,7 +278,7 @@ async function leaseMonitoringTasks(aiPlatformIDs: string[]): Promise<Monitoring
|
||||
limit: monitoringCycleBatchLeaseLimit,
|
||||
ai_platform_ids: aiPlatformIDs,
|
||||
};
|
||||
return await postMonitoringApi<MonitoringLeaseTasksResponse>("/api/plugin/monitoring/tasks/lease", payload);
|
||||
return await postMonitoringApi<MonitoringLeaseTasksResponse>("lease_tasks", payload);
|
||||
}
|
||||
|
||||
function mergeMonitoringQueueTasks(
|
||||
@@ -580,7 +559,7 @@ async function handleMonitoringSyncHeartbeat(allowCachedRemote: boolean): Promis
|
||||
}
|
||||
|
||||
try {
|
||||
const heartbeat = await postMonitoringApi<MonitoringHeartbeatResponse>("/api/plugin/monitoring/heartbeat", {
|
||||
const heartbeat = await postMonitoringApi<MonitoringHeartbeatResponse>("heartbeat", {
|
||||
platforms: persistedPlatforms.map(toMonitoringPlatformPayload),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user