mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
release: v1.6.0
This commit is contained in:
@@ -2,6 +2,8 @@ const HEARTBEAT_INTERVAL_MS = 30000;
|
||||
const RECONNECT_MIN_DELAY_MS = 1000;
|
||||
const RECONNECT_MAX_DELAY_MS = 30000;
|
||||
const PAGE_CAPTURE_RETRY_LIMIT = 3;
|
||||
const TAB_COMMAND_RETRY_LIMIT = 3;
|
||||
const TAB_COMMAND_RETRY_DELAY_MS = 250;
|
||||
const KEEPALIVE_ALARM_NAME = "limeBridgeKeepAlive";
|
||||
const KEEPALIVE_PERIOD_MINUTES = 1;
|
||||
const DEBUGGER_PROTOCOL_VERSION = "1.3";
|
||||
@@ -84,6 +86,10 @@ function logWarn(message, payload) {
|
||||
}
|
||||
}
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function readSettings() {
|
||||
return new Promise((resolve) => {
|
||||
chrome.storage.local.get(Object.keys(DEFAULT_SETTINGS), (stored) => {
|
||||
@@ -1167,12 +1173,41 @@ async function resolveTargetTabId() {
|
||||
}
|
||||
|
||||
async function sendCommandToTab(tabId, payload) {
|
||||
try {
|
||||
return await chrome.tabs.sendMessage(tabId, payload);
|
||||
} catch (_) {
|
||||
await injectContentScript(tabId);
|
||||
return await chrome.tabs.sendMessage(tabId, payload);
|
||||
let lastError = null;
|
||||
|
||||
for (let attempt = 0; attempt <= TAB_COMMAND_RETRY_LIMIT; attempt += 1) {
|
||||
try {
|
||||
return await chrome.tabs.sendMessage(tabId, payload);
|
||||
} catch (sendError) {
|
||||
lastError = sendError;
|
||||
try {
|
||||
await injectContentScript(tabId);
|
||||
return await chrome.tabs.sendMessage(tabId, payload);
|
||||
} catch (injectError) {
|
||||
lastError = injectError;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
attempt >= TAB_COMMAND_RETRY_LIMIT ||
|
||||
!looksLikeTransientTabCommandError(lastError)
|
||||
) {
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
const delayMs = TAB_COMMAND_RETRY_DELAY_MS * (attempt + 1);
|
||||
logWarn("标签页命令执行命中瞬态错误,准备重试", {
|
||||
tabId,
|
||||
attempt: attempt + 1,
|
||||
delayMs,
|
||||
payloadType: payload?.type || null,
|
||||
command: payload?.data?.command || null,
|
||||
error: lastError?.message || String(lastError),
|
||||
});
|
||||
await delay(delayMs);
|
||||
}
|
||||
|
||||
throw lastError || new Error("标签页命令执行失败");
|
||||
}
|
||||
|
||||
async function injectContentScript(tabId) {
|
||||
@@ -1183,6 +1218,18 @@ async function injectContentScript(tabId) {
|
||||
});
|
||||
}
|
||||
|
||||
function looksLikeTransientTabCommandError(error) {
|
||||
const normalized = String(error?.message || error || "").toLowerCase();
|
||||
return (
|
||||
normalized.includes("frame with id") ||
|
||||
normalized.includes("frame was removed") ||
|
||||
normalized.includes("receiving end does not exist") ||
|
||||
normalized.includes("could not establish connection") ||
|
||||
normalized.includes("message port closed") ||
|
||||
normalized.includes("extension context invalidated")
|
||||
);
|
||||
}
|
||||
|
||||
async function triggerPageCapture(reason, retry = 0) {
|
||||
if (!monitoringEnabled && reason !== "manual") {
|
||||
return;
|
||||
|
||||
@@ -515,7 +515,8 @@
|
||||
const ARTICLE_CONTENT_SELECTOR =
|
||||
'[data-testid="longformRichTextComponent"] [data-contents="true"], [data-testid="longformRichTextComponent"]';
|
||||
const IMAGE_SELECTOR = '[data-testid="tweetPhoto"] img';
|
||||
const CODE_BLOCK_SELECTOR = '[data-testid="markdown-code-block"]';
|
||||
const CODE_BLOCK_SELECTOR =
|
||||
'[data-testid="markdown-code-block"], [data-testid="prism-code-block"], pre';
|
||||
|
||||
function normalizeText(value) {
|
||||
return String(value || "")
|
||||
@@ -581,6 +582,39 @@
|
||||
}
|
||||
}
|
||||
|
||||
function resolveSrcsetUrl(rawSrcset) {
|
||||
const entries = String(rawSrcset || "")
|
||||
.split(",")
|
||||
.map((entry) => entry.trim())
|
||||
.filter(Boolean);
|
||||
if (entries.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const lastEntry = entries[entries.length - 1] || "";
|
||||
const [url] = lastEntry.split(/\s+/, 1);
|
||||
return String(url || "").trim();
|
||||
}
|
||||
|
||||
function resolveImageSource(imageElement) {
|
||||
const picture = imageElement.closest("picture");
|
||||
const pictureSources = picture
|
||||
? Array.from(picture.querySelectorAll("source"))
|
||||
: [];
|
||||
const candidate = [
|
||||
imageElement.currentSrc,
|
||||
imageElement.getAttribute("src"),
|
||||
imageElement.getAttribute("data-src"),
|
||||
imageElement.getAttribute("data-image-url"),
|
||||
resolveSrcsetUrl(imageElement.getAttribute("srcset")),
|
||||
...pictureSources.map((source) =>
|
||||
resolveSrcsetUrl(source.getAttribute("srcset")),
|
||||
),
|
||||
].find((value) => normalizeText(value));
|
||||
|
||||
return String(candidate || "");
|
||||
}
|
||||
|
||||
function resolveSuggestedImageName(rawUrl, fallbackIndex) {
|
||||
try {
|
||||
const url = new URL(rawUrl, location.href);
|
||||
@@ -626,10 +660,7 @@
|
||||
}
|
||||
|
||||
function registerImage(state, imageElement) {
|
||||
const rawUrl =
|
||||
imageElement.getAttribute("src") ||
|
||||
imageElement.getAttribute("data-src") ||
|
||||
"";
|
||||
const rawUrl = resolveImageSource(imageElement);
|
||||
const normalizedUrl = normalizeImageUrl(rawUrl);
|
||||
if (!normalizedUrl) {
|
||||
return "";
|
||||
|
||||
Reference in New Issue
Block a user