- contracts/browser.ts: provider kind, per-capability {supported, verified} flags,
session/tab/control-lease rows, NavigationBudget, redacted BrowserSnapshot,
DownloadArtifact with blob/secret refs, BrowserErrorKind
- adapters/browser: BrowserProvider interface, camofox client (health, capabilities
static + /openapi.json probe, openSession/navigate/snapshot/screenshot; rest
not_implemented for M2), none provider, redactSnapshot, profile serial lock,
domain allowlist budget; Bearer access key via BrowserSecretResolver
- testkit/browser: mock camofox server (auth, fixtures with secrets, faults)
- db 0023: browser_provider/session/tab/control_lease with one-active-lease and
one-live-session-per-profile partial unique indexes; types regenerated
- docs/design/browser-provider.md: contract, camofox OpenAPI 1.17.0 mapping,
unverified items and M2 prototype checklist; Playwright is not a product browser
Co-Authored-By: Claude Opus 5.5 <[email protected]>
37 lines
1.6 KiB
TypeScript
37 lines
1.6 KiB
TypeScript
import type { BrowserErrorKind } from "@umt/contracts";
|
|
|
|
/**
|
|
* 浏览器适配层统一错误。message 只含方法、路径与状态,不含访问凭据、Cookie、页面正文。
|
|
* retryable:同一预算内是否值得重试(超时/网络/上游 5xx/不可用);认证、预算、控制权冲突一律不重试。
|
|
*/
|
|
export class BrowserError extends Error {
|
|
readonly kind: BrowserErrorKind;
|
|
readonly httpStatus?: number;
|
|
constructor(kind: BrowserErrorKind, message?: string, opts: { httpStatus?: number; cause?: unknown } = {}) {
|
|
super(message ?? kind, opts.cause === undefined ? undefined : { cause: opts.cause });
|
|
this.name = "BrowserError";
|
|
this.kind = kind;
|
|
if (opts.httpStatus !== undefined) this.httpStatus = opts.httpStatus;
|
|
}
|
|
get retryable(): boolean {
|
|
return (
|
|
this.kind === "timeout" || this.kind === "network" || this.kind === "upstream" || this.kind === "unavailable"
|
|
);
|
|
}
|
|
}
|
|
|
|
export const notImplemented = (method: string): BrowserError =>
|
|
new BrowserError("not_implemented", `${method} 未实现(M2 原型验证后启用)`);
|
|
|
|
/** HTTP 状态 → 错误种类(camofox OpenAPI 1.17.0 的响应码:400/401/403/404/409/422/429/500/503) */
|
|
export function classifyHttpStatus(status: number): BrowserErrorKind {
|
|
if (status === 401 || status === 403) return "auth";
|
|
if (status === 404) return "not_found";
|
|
if (status === 408) return "timeout";
|
|
if (status === 429) return "rate_limited";
|
|
if (status === 503) return "unavailable";
|
|
if (status >= 500) return "upstream";
|
|
if (status >= 400) return "bad_request";
|
|
return "protocol";
|
|
}
|