import type { MonitoringCycleKickoffResponse } from "@geo/shared-types"; import { browser } from "wxt/browser"; import { defineBackground } from "wxt/utils/define-background"; import { patchDoubaoMonitoringRuntimeState, readDoubaoMonitoringRuntimePatchFromRequestUrl, } from "../src/monitoring-adapters/doubao-state"; import { handleMonitoringAction, runMonitoringCycle, type MonitoringCycleMode } from "../src/monitoring-runtime"; import { handlePublisherAction } from "../src/runtime"; import { getExtensionState, type ExtensionStorageState } from "../src/storage"; type BackgroundSuccessResponse = { ok: true; data: unknown; }; type BackgroundErrorResponse = { ok: false; error: string; }; const PUBLISHER_HEARTBEAT_ALARM = "geo.publisher.background-heartbeat"; const MONITORING_HEARTBEAT_ALARM = "geo.monitoring.background-heartbeat"; const MONITORING_QUEUE_ALARM = "geo.monitoring.queue-heartbeat"; const MONITORING_WARMUP_INTERVAL_MS = 10 * 60 * 1000; const MONITORING_HEARTBEAT_INTERVAL_MINUTES = 15; const MONITORING_HEARTBEAT_START_JITTER_MS = 3 * 60 * 1000; const DOUBAO_COMPLETION_REQUEST_FILTER = { urls: ["https://www.doubao.com/chat/completion*", "https://doubao.com/chat/completion*"], }; let monitoringCycleInFlight: Promise | null = null; function randomMonitoringHeartbeatStartDelayMs(): number { return Math.floor(Math.random() * (MONITORING_HEARTBEAT_START_JITTER_MS + 1)); } async function ensureBackgroundAlarms(): Promise { browser.alarms.create(PUBLISHER_HEARTBEAT_ALARM, { periodInMinutes: 60, }); browser.alarms.create(MONITORING_HEARTBEAT_ALARM, { when: Date.now() + randomMonitoringHeartbeatStartDelayMs(), periodInMinutes: MONITORING_HEARTBEAT_INTERVAL_MINUTES, }); } function shouldWarmupMonitoring(state: ExtensionStorageState): boolean { if (!state.plugin_installation_id || !state.installation_token || !state.api_base_url) { return false; } if (!state.monitoring_last_heartbeat_at) { return true; } const lastHeartbeatAt = new Date(state.monitoring_last_heartbeat_at).getTime(); if (Number.isNaN(lastHeartbeatAt)) { return true; } return Date.now() - lastHeartbeatAt >= MONITORING_WARMUP_INTERVAL_MS; } function registerMonitoringRuntimeCapture(): void { if (!browser.webRequest?.onBeforeRequest) { return; } browser.webRequest.onBeforeRequest.addListener( (details) => { const patch = readDoubaoMonitoringRuntimePatchFromRequestUrl(details.url); if (!patch) { return undefined; } void patchDoubaoMonitoringRuntimeState(patch).catch((error) => { console.warn("[geo-monitoring] failed to persist doubao runtime patch", error); }); return undefined; }, DOUBAO_COMPLETION_REQUEST_FILTER, ); } async function warmupMonitoringRuntime(): Promise { const state = await getExtensionState(); if (!shouldWarmupMonitoring(state)) { return; } await runMonitoringCycleSingleFlight("automatic"); } async function syncMonitoringQueueAlarm(): Promise { const state = await getExtensionState(); const nextExecuteAt = state.monitoring_task_queue.reduce((earliest, task) => { const timestamp = new Date(task.next_execute_at).getTime(); if (Number.isNaN(timestamp)) { return earliest; } if (earliest == null) { return timestamp; } return Math.min(earliest, timestamp); }, null); await browser.alarms.clear(MONITORING_QUEUE_ALARM); if (nextExecuteAt == null) { return; } browser.alarms.create(MONITORING_QUEUE_ALARM, { when: Math.max(Date.now() + 1000, nextExecuteAt), }); } function runMonitoringCycleSingleFlight(mode: MonitoringCycleMode): Promise { if (monitoringCycleInFlight) { return monitoringCycleInFlight; } monitoringCycleInFlight = runMonitoringCycle(mode).finally(() => { monitoringCycleInFlight = null; void syncMonitoringQueueAlarm().catch((error) => { console.warn("[geo-monitoring] failed to sync queue alarm", error); }); }); return monitoringCycleInFlight; } function kickoffMonitoringCycle(): MonitoringCycleKickoffResponse { if (monitoringCycleInFlight) { return { accepted: true, status: "already_running", message: "monitoring cycle is already running in background", }; } void runMonitoringCycleSingleFlight("manual").catch((error) => { console.warn("[geo-monitoring] background cycle failed", error); }); return { accepted: true, status: "started", message: "monitoring cycle started in background", }; } export default defineBackground(() => { void getExtensionState(); void ensureBackgroundAlarms(); registerMonitoringRuntimeCapture(); void warmupMonitoringRuntime().catch((error) => { console.warn("[geo-monitoring] warmup cycle failed", error); }); void syncMonitoringQueueAlarm(); browser.runtime.onInstalled.addListener(() => { void getExtensionState(); void ensureBackgroundAlarms(); void warmupMonitoringRuntime().catch((error) => { console.warn("[geo-monitoring] install warmup cycle failed", error); }); void syncMonitoringQueueAlarm(); }); browser.runtime.onStartup.addListener(() => { void getExtensionState(); void ensureBackgroundAlarms(); void warmupMonitoringRuntime().catch((error) => { console.warn("[geo-monitoring] startup warmup cycle failed", error); }); void syncMonitoringQueueAlarm(); }); browser.alarms.onAlarm.addListener(async (alarm) => { if (alarm.name === PUBLISHER_HEARTBEAT_ALARM) { await getExtensionState(); return; } if (alarm.name === MONITORING_HEARTBEAT_ALARM) { try { await runMonitoringCycleSingleFlight("automatic"); } catch (error) { console.warn("[geo-monitoring] cycle failed", error); } return; } if (alarm.name === MONITORING_QUEUE_ALARM) { try { await runMonitoringCycleSingleFlight("automatic"); } catch (error) { console.warn("[geo-monitoring] queue cycle failed", error); } } }); chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { if (!message) { return undefined; } if (message.type === "geo.publisher.request") { void handlePublisherAction(message.action, message.payload) .then((data) => { sendResponse({ ok: true, data, } satisfies BackgroundSuccessResponse); }) .catch((error: unknown) => { sendResponse({ ok: false, error: error instanceof Error ? error.message : "publisher_plugin_unknown_error", } satisfies BackgroundErrorResponse); }); return true; } if (message.type === "geo.monitoring.request") { if (message.action === "kickoffCycle") { sendResponse({ ok: true, data: kickoffMonitoringCycle(), } satisfies BackgroundSuccessResponse); return true; } const action = message.action === "runCycle" ? runMonitoringCycleSingleFlight("manual") : handleMonitoringAction(message.action); void action .then((data) => { sendResponse({ ok: true, data, } satisfies BackgroundSuccessResponse); }) .catch((error: unknown) => { sendResponse({ ok: false, error: error instanceof Error ? error.message : "monitoring_plugin_unknown_error", } satisfies BackgroundErrorResponse); }); return true; } return undefined; }); });