feat(browser): complete camofox BrowserProvider with tabs, interaction, download capture and control lease

Contracts for browser API and browser.* agent capabilities; adapter implements
close/list/restore sessions, tabs + reconcile, history, click/type/select/scroll/press,
extract, waitForDownload (staged via DownloadSink, sha256), stale-ref guard and
automation/human actor check. testkit mock camofox gains an interactive site
(login redirect, captcha, click-driven page changes, downloads, PNG screenshots).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 20:17:46 +08:00
1 parent 62290d4477
commit c17d4b90bd
12 files changed
+1832 -101

No files matched your search

+53 -4
View File
@@ -35,12 +35,51 @@ export interface BrowserProvider {
screenshot(ref: TabRef, opts?: { maxBytes?: number }): Promise<BrowserScreenshot>;
click(ref: TabRef, target: ElementTarget): Promise<void>;
/** text 为明文或凭证引用;凭证引用由服务端解析后直接下发,不进入模型与日志 */
type(ref: TabRef, target: ElementTarget, text: TypeInput): Promise<void>;
type(ref: TabRef, target: ElementTarget | null, text: TypeInput): Promise<void>;
extract(ref: TabRef, schema: Record<string, unknown>): Promise<Record<string, unknown>>;
waitForDownload(ref: TabRef, opts: { timeoutMs: number; maxBytes: number }): Promise<DownloadArtifact>;
acquireControl(req: AcquireControlRequest): Promise<BrowserControlLease>;
releaseControl(leaseId: string, reason?: string): Promise<void>;
// ── C1-E:标签页、历史导航、其余交互与状态同步 ──
openTab(sessionId: string, opts: { url?: string; budget: NavigationBudget; as?: ActorKind }): Promise<BrowserTab>;
closeTab(ref: TabRef): Promise<void>;
/** 后退/前进/刷新(camofox /back /forward /refresh) */
history(ref: TabRef, action: "back" | "forward" | "reload", budget: NavigationBudget): Promise<NavigationResult>;
select(ref: TabRef, target: ElementTarget, value: string): Promise<void>;
scroll(ref: TabRef, opts: { direction: "up" | "down" | "left" | "right"; amount: number }): Promise<void>;
press(ref: TabRef, key: string): Promise<void>;
/** 服务层把控制权变更同步给适配器;任何变更都让旧元素引用失效 */
setController(sessionId: string, controller: BrowserSession["controller"]): void;
/** 服务重启后按数据库记录恢复内存状态(旧元素引用一律失效) */
restoreSession(state: RestoreSessionState): void;
/** 对账:上游已不存在的标签页标 lost */
reconcileTabs(sessionId: string): Promise<{ alive: string[]; lost: string[] }>;
/** 会话内存状态摘要(不含未脱敏地址) */
sessionInfo(sessionId: string): BrowserSessionInfo | null;
/** 等待进行中的操作收尾(接管前调用) */
drain(sessionId: string): Promise<void>;
}
/** 动作发起方:自动化(Agent/任务)或持有接管租约的人 */
export type ActorKind = "automation" | "human";
export interface RestoreSessionState {
session: BrowserSession;
tabs: BrowserTab[];
currentTabId: string | null;
budget: NavigationBudget;
}
export interface BrowserSessionInfo {
session: BrowserSession;
tabs: BrowserTab[];
currentTabId: string | null;
/** 最近一次快照之后没有导航/交接:eN 引用可用 */
refsValid: boolean;
/** 当前页面是登录页 */
loginPage: boolean;
}
export interface OpenSessionRequest {
@@ -64,6 +103,8 @@ export interface OpenSessionResult {
export interface TabRef {
sessionId: string;
tabId?: string;
/** 动作发起方,缺省 automation;与会话 controller 不符时报 control_conflict */
as?: ActorKind;
}
export interface NavigationResult {
@@ -95,11 +136,19 @@ export interface BrowserScreenshot {
capturedAt: string;
}
export type ElementTarget = { ref: string } | { selector: string };
/** 元素定位:快照引用 / CSS 选择器 / 视口像素坐标(原站视图点击;camofox 坐标点击待核验) */
export type ElementTarget = { ref: string } | { selector: string } | { x: number; y: number };
export type TypeInput =
| { kind: "text"; text: string }
| { kind: "secret"; siteId: string; accountId: string | null; field: "username" | "password" | "totp" };
| { kind: "text"; text: string; submit?: boolean; clear?: boolean }
| {
kind: "secret";
siteId: string;
accountId: string | null;
field: "username" | "password" | "totp";
submit?: boolean;
clear?: boolean;
};
export interface AcquireControlRequest {
sessionId: string;