尝试修复浏览器网关断开问题
This commit is contained in:
22
SKILL.md
22
SKILL.md
@@ -31,7 +31,7 @@ Gemini 页面的操作按钮(`.send-button-container` 内)通过 `aria-label
|
||||
| 发送 / Send | `ready` | 输入框有内容,可发送 |
|
||||
| 停止 / Stop | `loading` | 已发送,正在生成回答 |
|
||||
|
||||
可通过 `GeminiOps.getStatus()` 获取当前状态,通过 `GeminiOps.waitForComplete()` 轮询等待生成完毕。
|
||||
可通过 `GeminiOps.getStatus()` 获取当前状态,通过 `GeminiOps.pollStatus()` 分段轮询等待生成完毕。
|
||||
|
||||
### A. 文本问答
|
||||
1. 打开 `https://gemini.google.com`。
|
||||
@@ -39,7 +39,7 @@ Gemini 页面的操作按钮(`.send-button-container` 内)通过 `aria-label
|
||||
3. 新建会话:`click('newChatBtn')`,确保干净上下文。
|
||||
4. 选择最强可用模型(优先 Gemini 3.1 Pro)。
|
||||
5. 将用户问题原样输入并发送。
|
||||
6. 调用 `waitForComplete()` 等待状态从 `loading` 变回 `idle`。
|
||||
6. **分段轮询等待**(见下方"CDP 保活轮询策略")。
|
||||
7. 等待完整输出,提炼后回传(必要时附原文要点)。
|
||||
|
||||
### B. 生图流程
|
||||
@@ -48,12 +48,28 @@ Gemini 页面的操作按钮(`.send-button-container` 内)通过 `aria-label
|
||||
3. 选择最强可用模型(优先 Gemini 3.1 Pro)。
|
||||
4. 将用户提示词原样输入。
|
||||
5. 发送后立即通知用户:正在绘图中。
|
||||
6. 调用 `waitForComplete()` 等待生成完毕(生图默认超时 120s)。
|
||||
6. **分段轮询等待**(见下方"CDP 保活轮询策略",生图超时上限 120s)。
|
||||
7. 结果出现后:
|
||||
- 优先用"下载原图"按钮获取原图。
|
||||
- 若无下载按钮或失败,可对图片右键另存(通常是标清图)。
|
||||
8. 把图片返回用户;若有多张,按顺序全部回传。
|
||||
|
||||
## CDP 保活轮询策略
|
||||
|
||||
> **核心原则**:绝不在页面内做长时间 Promise 等待。每次 `evaluate` 必须毫秒级返回,由调用端控制循环。
|
||||
|
||||
生图/问答发送后,按以下方式等待结果:
|
||||
|
||||
1. 每隔 **8~10 秒**调用一次 `GeminiOps.pollStatus()`。
|
||||
2. 该函数立即返回 `{status, label, pageVisible, ts}`。
|
||||
3. 调用端根据 `status` 判断:
|
||||
- `loading` → 继续等待,累计已耗时。
|
||||
- `idle` → 生成完毕,进入结果获取阶段。
|
||||
- `unknown` → 页面可能异常,做一次 snapshot 兜底排查。
|
||||
4. 累计耗时超过上限(文本 60s / 生图 120s)→ 走超时回退逻辑。
|
||||
|
||||
**为什么这样做**:OpenClaw 通过 CDP(Chrome DevTools Protocol)WebSocket 控制浏览器。若长时间(>30s)无消息往来,网关/代理可能判定连接空闲并断开。分段短轮询保证 CDP 通道始终有心跳流量。
|
||||
|
||||
## 失败回退
|
||||
|
||||
1. 元素定位失败:刷新页面后重试一次。
|
||||
|
||||
@@ -26,7 +26,14 @@
|
||||
|
||||
使用方式:
|
||||
- `GeminiOps.getStatus()` → 返回 `{status: 'idle'|'ready'|'loading', label, disabled}`
|
||||
- `GeminiOps.waitForComplete(timeout, interval)` → 返回 Promise,状态脱离 `loading` 后 resolve
|
||||
- `GeminiOps.pollStatus()` → 返回 `{status, label, pageVisible, ts}`,**毫秒级返回**,供调用端分段轮询
|
||||
|
||||
### CDP 保活轮询(重要)
|
||||
|
||||
**禁止**在页面内做长 Promise 等待(旧版 `waitForComplete` 已移除)。
|
||||
|
||||
正确做法:调用端每 8~10 秒 evaluate 一次 `GeminiOps.pollStatus()`,自行累计耗时并判断超时。
|
||||
这确保 CDP WebSocket 通道持续有消息流量,避免被网关判定空闲而断开连接。
|
||||
|
||||
## 4) 生图结果获取
|
||||
|
||||
|
||||
@@ -81,25 +81,15 @@
|
||||
return {status:'idle',label,disabled};
|
||||
}
|
||||
|
||||
function waitForComplete(timeout,interval){
|
||||
timeout=timeout||120000;
|
||||
interval=interval||2000;
|
||||
return new Promise(function(resolve){
|
||||
var elapsed=0;
|
||||
var timer=setInterval(function(){
|
||||
elapsed+=interval;
|
||||
var s=getStatus();
|
||||
if(s.status!=='loading'){
|
||||
clearInterval(timer);
|
||||
resolve({ok:true,status:s.status,elapsed});
|
||||
return;
|
||||
}
|
||||
if(elapsed>=timeout){
|
||||
clearInterval(timer);
|
||||
resolve({ok:false,status:'timeout',elapsed});
|
||||
}
|
||||
},interval);
|
||||
});
|
||||
/* ── 保活式轮询 ──
|
||||
* 不在页面内做长 Promise 等待(会导致 CDP 连接因长时间无消息被网关判定空闲断开)。
|
||||
* 改为:调用端每 8-10s evaluate 一次 GeminiOps.pollStatus(),立即拿到结果。
|
||||
* 调用端自行累计耗时并判断超时。
|
||||
*/
|
||||
function pollStatus(){
|
||||
var s=getStatus();
|
||||
// 顺便返回页面可见性,帮助调用端判断 tab 是否还活着
|
||||
return {status:s.status, label:s.label, pageVisible:!document.hidden, ts:Date.now()};
|
||||
}
|
||||
|
||||
function probe(){
|
||||
@@ -113,5 +103,5 @@
|
||||
};
|
||||
}
|
||||
|
||||
window.GeminiOps = {probe, click, fillPrompt, getStatus, waitForComplete, selectors:S, version:'0.3.0'};
|
||||
window.GeminiOps = {probe, click, fillPrompt, getStatus, pollStatus, selectors:S, version:'0.4.0'};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user