Files
UHD_Media_Tool/packages/adapters/src/browser/none.ts
T
rootandClaude Opus 5.5 c437c6bff6 feat(browser): vision automation loop with screenshots, coordinate actions and bounded challenge solving
- browser.screenshot returns the page as an image content block (≤1280px wide, JPEG quality
  configurable) for vision-capable upstreams; non-vision upstreams get a redacted text snapshot.
  Login pages / password fields are marked sensitive and withheld unless a
  browser.sensitive_screenshot grant exists and allowSensitive is passed.
- browser.click_at / type_at / scroll / wait act in screenshot pixels; the server converts via
  viewport/DPR, requires automation control and an allowlisted host, and writes a thumbnail
  evidence file (DATA_DIR/tmp/evidence) referenced from agent_run_event.
- browser.solve_challenge runs a bounded child agent_run (vision caps only, maxSteps ≤ 12,
  budget), judges success from page facts, stops immediately on 2FA / SMS / device confirmation,
  and otherwise moves the session to waiting with evidence and a takeover link.
- PiAgentRuntime passes modelImage outputs as pi-ai image content when model.input has image;
  the ledger never stores image data; coordinate actions use per-invocation idempotency keys.
- Tests: image codec, runtime image content, E14 e2e (login solved, 2FA stop, step limit,
  sensitive withheld). Real camofox 1.17.0 check on example.com recorded in the runbook.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 01:27:48 +08:00

63 lines
2.1 KiB
TypeScript

import type { BrowserCapabilities, BrowserCapabilityFlag, BrowserHealth } from "@umt/contracts";
import { BROWSER_CAPABILITY_KEYS } from "@umt/contracts";
import { BrowserError } from "./errors.ts";
import type { BrowserProvider } from "./provider.ts";
const off: BrowserCapabilityFlag = { supported: false, verified: false, source: "static", note: "未配置浏览器服务" };
export function noneCapabilities(): BrowserCapabilities {
const flags = Object.fromEntries(BROWSER_CAPABILITY_KEYS.map((k) => [k, { ...off }])) as Record<
(typeof BROWSER_CAPABILITY_KEYS)[number],
BrowserCapabilityFlag
>;
return { ...flags, upstreamVersion: null, probedAt: null };
}
const unavailable = () => Promise.reject(new BrowserError("unavailable", "未配置浏览器服务"));
/**
* 默认实现:未配置浏览器服务。所有动作返回 unavailable,能力全部 false;
* 业务侧据此在界面标注"浏览器不可用",不回退到其他浏览器。
*/
export function createNoneBrowserProvider(): BrowserProvider {
return {
kind: "none",
async health(): Promise<BrowserHealth> {
return { status: "disabled", kind: "none", detail: {}, checkedAt: new Date().toISOString() };
},
async capabilities() {
return noneCapabilities();
},
openSession: unavailable,
closeSession: unavailable,
async listSessions() {
return [];
},
navigate: unavailable,
snapshot: unavailable,
screenshot: unavailable,
click: unavailable,
type: unavailable,
extract: unavailable,
waitForDownload: unavailable,
acquireControl: unavailable,
releaseControl: unavailable,
openTab: unavailable,
closeTab: unavailable,
history: unavailable,
select: unavailable,
scroll: unavailable,
press: unavailable,
setController() {
throw new BrowserError("unavailable", "未配置浏览器服务");
},
restoreSession() {
throw new BrowserError("unavailable", "未配置浏览器服务");
},
reconcileTabs: unavailable,
sessionInfo: () => null,
drain: async () => {},
pageMetrics: async () => null,
};
}