Files
Cyrene_v2/internal/capability/browser/errors.go
T
AskaEth f1fbc249fd
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(browser): B1 接线 + B2 薄层工具面(V5)
- browsercfg 接进组合根:配置读 $DATA_DIR/config/plugins/browser.json(零 env)→ 探测(本机
  Chromium → 已在跑的 CDP)→ 不可用就不注册(原因码入日志)
- 自研最小出站 CDP 客户端(不 Enabled Runtime、隔离世界求值、不发 --enable-automation)+
  导航 + 截图 0600 落盘 + 指纹自检;工具 browser(open/screenshot/info,owner-only)
- 3 个变异自检(启动恢复 --enable-automation/无视可用性/空闲回收失效)
- 真机对照(两站各 1 次,**如实结论**):两侧都 pass ⇒ 「headless 被拦 / headful 通过」**未被证明**;
  但测到两处硬差异:无头 UA 带 HeadlessChrome、screen 800 vs 2632;两模式 webdriver 均 false
  (把 L6 的推断变成实测)。跑完 0 残留 Chrome、无对外监听端口
⚠️ 未做:MCP 自动接入(口径委派未落地)/自动注入上下文/面板页/搜索降级/docker 配方/Windows 用
  pipe(Go ExtraFiles 不支持);SSRF 落了第三份同策实现待收口;浏览器会话未挂进程关闭链
2026-10-06 20:25:16 +08:00

82 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package browser
import "errors"
// 本包对外哨兵错误,一律用 errors.Is 判定;文案由调用方补齐上下文(AGENTS §10:
// 错误一律 %w wrap,禁裸 return err)。**不设包级可变状态**(除这些 var ErrXxx)。
var (
// ErrCDP 表示 CDP 层失败:WebSocket 连不上 / 命令返回协议错误 / 连接被打断。
ErrCDP = errors.New("browser: CDP 调用失败")
// ErrLaunch 表示启动本机浏览器失败:可执行文件拉不起来、进程立刻退出、
// 或者等不到调试端口就绪。
ErrLaunch = errors.New("browser: 启动本机浏览器失败")
// ErrNavigate 表示导航失败(CDP 报 errorText,或等待页面就绪超时)。
ErrNavigate = errors.New("browser: 导航失败")
// ErrEvaluate 表示在页面里求值失败(表达式异常 / 拿不到执行上下文)。
ErrEvaluate = errors.New("browser: 页面求值失败")
// ErrBlockedURL 表示目标 URL 被 SSRF 守卫拒绝(回环/私网/链路本地等)。
ErrBlockedURL = errors.New("browser: URL 被安全守卫拒绝")
// ErrScreenshotTooLarge 表示截图在两次尝试(PNG → JPEG)之后仍超过配置的字节上限。
// 超限**不截断**:半个 PNG 是坏图,模型读到的会是噪声(design/28 §3「超限如实
// 截断/缩放并标注」的落地口径 —— 能降编码就降,降不下来就如实报错)。
ErrScreenshotTooLarge = errors.New("browser: 截图超过字节上限")
// ErrNoSession 表示管理器已被关闭,或当前没有可用会话(不可用时不假装可用)。
ErrNoSession = errors.New("browser: 没有可用的浏览器会话")
// ErrUnavailable 表示这台机器上没有任何可用来源(没装 Chrome、也没有在跑的
// CDP 端点)。它由组合根在探测阶段判定,此处只用于「手里没有 dialer」这一态。
ErrUnavailable = errors.New("browser: 本机没有可用的浏览器来源")
)
// Reason 是「这次为什么没成功」的**分类原因码**:上层据此如实降级与记日志,
// 不许把它含糊成「失败」两个字(design/28 §6 坑 5)。取值稳定、可断言;人类可读
// 文案见 Message()。
type Reason string
// 分类原因码。
const (
// ReasonLaunched 表示会话由本机 Chrome/Chromium 新起(默认有头、持久 profile)。
ReasonLaunched Reason = "launched-local-chrome"
// ReasonRemoteCDP 表示会话复用了配置里显式给出的 CDP 端点(容器/手动实例)。
ReasonRemoteCDP Reason = "remote-cdp"
// ReasonIdleRecycled 表示上一次会话因空闲超时被回收(下一个动作会重新起)。
ReasonIdleRecycled Reason = "idle-recycled"
// ReasonLaunchFailed 表示本机浏览器没拉起来(可执行文件 / 端口未就绪)。
ReasonLaunchFailed Reason = "launch-failed"
// ReasonNoPageTarget 表示连上了浏览器却找不到可用的 page target。
ReasonNoPageTarget Reason = "no-page-target"
// ReasonBlockedURL 表示 URL 没过 SSRF 守卫。
ReasonBlockedURL Reason = "blocked-url"
// ReasonNavigateFailed 表示导航失败或等待就绪超时。
ReasonNavigateFailed Reason = "navigate-failed"
// ReasonShotTooLarge 表示截图超过字节上限(如实报,不交坏图)。
ReasonShotTooLarge Reason = "screenshot-too-large"
// ReasonClosed 表示管理器已关闭。
ReasonClosed Reason = "closed"
)
// Message 返回原因码对应的中文文案;未知码返回空串(调用方回落到错误的 Error())。
func (r Reason) Message() string {
switch r {
case ReasonLaunched:
return "已启动本机浏览器(默认有头 + 持久 profile)"
case ReasonRemoteCDP:
return "已连接到配置指定的 CDP 端点"
case ReasonIdleRecycled:
return "空闲超时,上一次会话已回收"
case ReasonLaunchFailed:
return "本机浏览器未能启动"
case ReasonNoPageTarget:
return "连上了浏览器,但没有可用的 page target"
case ReasonBlockedURL:
return "目标 URL 未通过安全守卫"
case ReasonNavigateFailed:
return "导航失败或等待页面就绪超时"
case ReasonShotTooLarge:
return "截图超过配置的字节上限"
case ReasonClosed:
return "浏览器管理器已关闭"
default:
return ""
}
}