const getActiveTab = () => new Promise((resolve) => { chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => resolve(tabs && tabs[0] ? tabs[0] : null)); }); const storageGet = (keys) => new Promise((resolve) => { chrome.storage.local.get(keys, (items) => resolve(items || {})); }); const storageSet = (items) => new Promise((resolve) => { chrome.storage.local.set(items, () => resolve()); }); const sendToTab = (tabId, message) => new Promise((resolve, reject) => { chrome.tabs.sendMessage(tabId, message, (resp) => { const err = chrome.runtime.lastError; if (err) { reject(new Error(err.message)); return; } resolve(resp); }); }); const badge = async (text) => { try { await chrome.action.setBadgeBackgroundColor({ color: "#2D7D46" }); await chrome.action.setBadgeText({ text: text || "" }); } catch (e) {} }; const badgeError = async (text) => { try { await chrome.action.setBadgeBackgroundColor({ color: "#B42318" }); await chrome.action.setBadgeText({ text: text || "" }); } catch (e) {} }; const normalizeBaseUrl = (url) => { const u = String(url || "").trim(); if (!u) return ""; return u.endsWith("/") ? u.slice(0, -1) : u; }; const extractTaskId = (text) => { const t = String(text || "").trim(); if (!t) return ""; const m = t.match(/[0-9a-fA-F]{32}/); return m ? m[0] : t; }; const getOrPromptServiceUrl = async (tabId) => { const items = await storageGet(["service_url"]); const existing = normalizeBaseUrl(items.service_url); if (existing) return existing; const resp = await sendToTab(tabId, { type: "PROMPT", text: "Spider_XHS 服务地址(例如 http://localhost:8000)", defaultValue: "http://localhost:8000", }); const v = normalizeBaseUrl(resp && resp.value); if (v) await storageSet({ service_url: v }); return v; }; const getTaskId = async (tabId) => { let clip = ""; try { const resp = await sendToTab(tabId, { type: "READ_CLIPBOARD" }); clip = extractTaskId(resp && resp.text); } catch (e) {} if (clip) return clip; const resp = await sendToTab(tabId, { type: "PROMPT", text: "task_id", defaultValue: "" }); return extractTaskId(resp && resp.value); }; const collectPage = async (tabId) => { const resp = await sendToTab(tabId, { type: "COLLECT_PAGE" }); return resp || {}; }; chrome.action.onClicked.addListener(async () => { await badge("..."); try { const tab = await getActiveTab(); const tabId = tab && tab.id; if (!tabId) { await badgeError("NO"); return; } const serviceUrl = await getOrPromptServiceUrl(tabId); if (!serviceUrl) { await badgeError("URL"); return; } const taskId = await getTaskId(tabId); if (!taskId) { await badgeError("ID"); return; } const page = await collectPage(tabId); const url = String(page.url || "").trim(); const html = String(page.html || ""); const body = { task_id: taskId, raw: { url, html }, normalized: { url, kind: "page" }, meta: { source_engine: "extension_rpa", source_type: "page", source_ref: url, ingested_at: new Date().toISOString(), ok: true }, }; const endpoint = `${serviceUrl}/api/v1/import/extension`; const resp = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (resp.ok) { await badge("OK"); } else { await badgeError(String(resp.status || "ERR")); } } catch (e) { await badgeError("ERR"); } finally { setTimeout(() => badge(""), 2000); } });