feat(browser): 浏览器配置与探测库(internal/capability/browsercfg,纯库零接线)

按 design/28 §2/§3 的「配置不进 .env」口径:读 $DATA_DIR/config/plugins/browser.json
(0600 + temp+rename 范式,**不读 env、不加新键**);有头/无头、CDP 端点、profile 目录、
视口/超时/并发(1)/截图上限全部类型化 + 常量默认值;**探测优先级**(本机 Chrome/Chromium
可执行文件 → 已在跑的容器)并返回可分类的「为什么不可用」(供上层如实降级,不假装可用);
profile 目录含凭据 ⇒ 0600 + 不进 git/不进上下文的注释与校验。

本片零既有文件改动(不注册工具、不碰 cmd/),接线留 B2。
This commit is contained in:
AskaEth committed 2026-10-06 09:03:34 +08:00
1 parent b7fb4dd1c7
commit 29181245ce
9 files changed
+1398

No files matched your search

+54
View File
@@ -0,0 +1,54 @@
package browsercfg
import "errors"
// 哨兵错误。AGENTS §10 只放行 `var ErrXxx` 作为包级变量,故原因码与文案用常量。
var (
// ErrInvalidConfig 表示 browser.json 解码失败、是空文件,或字段越界:
// 视口/截图字节超过硬上限、cdpEndpoint 不是 http(s) 绝对地址。
ErrInvalidConfig = errors.New("browsercfg: 配置非法")
// ErrProfileInGitTree 表示 profile 目录会落在 git 工作树内 —— profile 含凭据
// (cookie / 登录态),被 `git add` 收走就是把主人的登录态提交出去(design/28 §4)。
ErrProfileInGitTree = errors.New("browsercfg: profile 目录不得位于 git 工作树内")
)
// Reason 是可用性判定「为什么不可用」的**分类原因码**。上层据它如实降级与记日志,
// 不许把它含糊成「探测失败」四个字(design/28 §6 坑 5:失败要如实报)。
type Reason string
// 分类原因码。取值稳定、可断言;人类可读文案见 Message()。
const (
// ReasonUsable 表示探测到可用来源(Usable=true 时使用)。
ReasonUsable Reason = "usable"
// ReasonExecutableMissing 表示配置显式给出的浏览器可执行文件不存在或不可执行。
ReasonExecutableMissing Reason = "executable_missing"
// ReasonCDPUnreachable 表示 CDP 端点不可达 —— 配置里显式给定的那个,或兜底候选。
ReasonCDPUnreachable Reason = "cdp_unreachable"
// ReasonNoSource 表示本机既无 Chrome/Chromium 可执行文件、也没有在跑的 CDP 端点。
ReasonNoSource Reason = "no_source"
)
// 分类原因的中文文案:上层直接写进日志/面板,避免两处各写一份措辞
// (照 config.WebSearchReason* 的既有口径)。
const (
messageUsable = "可用"
messageExecutableMissing = "指定的浏览器可执行文件不存在或不可执行"
messageCDPUnreachable = "CDP 端点不可达"
messageNoSource = "本机未发现 Chrome/Chromium,也没有在跑的 CDP 端点"
)
// Message 返回原因码对应的中文文案;未知码返回空串(调用方回落到 Detail)。
func (r Reason) Message() string {
switch r {
case ReasonUsable:
return messageUsable
case ReasonExecutableMissing:
return messageExecutableMissing
case ReasonCDPUnreachable:
return messageCDPUnreachable
case ReasonNoSource:
return messageNoSource
default:
return ""
}
}