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:
1 parent
62290d4477
commit
c17d4b90bd
12 files changed
+1832
-101
No files matched your search
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user