feat(browser): W4-C BrowserProvider contract and camofox-browser adapter skeleton

- 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]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-27 21:39:29 +08:00
1 parent 269a2269a1
commit effaa32b70
23 files changed
+2290

No files matched your search

+83
View File
@@ -73,6 +73,12 @@ export type AssetOperationStatus = "planned" | "started" | "succeeded" | "failed
export type AssetUnitCoverageCoverage = "full" | "partial" | "segment" | "unknown";
export type AssetUnitCoverageSegment = "full" | "upper" | "middle" | "lower" | "unknown";
export type AutomationGrantStatus = "active" | "revoked" | "expired";
export type BrowserControlLeaseHolderKind = "automation" | "human";
export type BrowserControlLeaseStatus = "active" | "released" | "expired" | "revoked";
export type BrowserProviderKind = "camofox" | "none";
export type BrowserProviderHealthStatus = "unknown" | "healthy" | "degraded" | "offline";
export type BrowserSessionController = "automation" | "human" | "waiting" | "closed";
export type BrowserTabStatus = "open" | "closed" | "lost";
export type CapabilityInvocationStatus = "running" | "succeeded" | "failed";
export type ContentUnitKind = "season" | "episode" | "special" | "part";
export type ContentUnitContentType =
@@ -460,6 +466,67 @@ export interface AutomationGrantTable {
updated_at: GeneratedTimestamptz;
}
export interface BrowserControlLeaseTable {
id: Generated<string>;
session_id: string;
holder_kind: BrowserControlLeaseHolderKind;
holder: string;
reason: string;
status: Generated<BrowserControlLeaseStatus>;
acquired_at: GeneratedTimestamptz;
expires_at: Timestamptz;
released_at: Timestamptz | null;
release_reason: string | null;
created_at: GeneratedTimestamptz;
updated_at: GeneratedTimestamptz;
}
export interface BrowserProviderTable {
id: Generated<string>;
name: string;
kind: BrowserProviderKind;
base_url: string | null;
secret_ref: string | null;
enabled: Generated<boolean>;
capabilities: Generated<Json>;
verified_at: Timestamptz | null;
health_status: Generated<BrowserProviderHealthStatus>;
last_health_at: Timestamptz | null;
config: Generated<Json>;
created_at: GeneratedTimestamptz;
updated_at: GeneratedTimestamptz;
}
export interface BrowserSessionTable {
id: Generated<string>;
provider_id: string;
site_id: string;
account_id: string | null;
profile_key: string;
controller: Generated<BrowserSessionController>;
current_url: string | null;
last_activity_at: GeneratedTimestamptz;
upstream_ref: Generated<Json>;
recovery_state: Generated<Json>;
closed_at: Timestamptz | null;
close_reason: string | null;
created_at: GeneratedTimestamptz;
updated_at: GeneratedTimestamptz;
}
export interface BrowserTabTable {
id: Generated<string>;
session_id: string;
upstream_tab_id: string;
url: string | null;
title: string | null;
status: Generated<BrowserTabStatus>;
last_snapshot_at: Timestamptz | null;
closed_at: Timestamptz | null;
created_at: GeneratedTimestamptz;
updated_at: GeneratedTimestamptz;
}
export interface CapabilityInvocationTable {
id: Generated<string>;
idempotency_key: string;
@@ -1225,6 +1292,10 @@ export interface DB {
asset_unit_coverage: AssetUnitCoverageTable;
audit_event: AuditEventTable;
automation_grant: AutomationGrantTable;
browser_control_lease: BrowserControlLeaseTable;
browser_provider: BrowserProviderTable;
browser_session: BrowserSessionTable;
browser_tab: BrowserTabTable;
capability_invocation: CapabilityInvocationTable;
content_unit: ContentUnitTable;
decision: DecisionTable;
@@ -1314,6 +1385,18 @@ export type AuditEventUpdate = Updateable<AuditEventTable>;
export type AutomationGrantRow = Selectable<AutomationGrantTable>;
export type NewAutomationGrant = Insertable<AutomationGrantTable>;
export type AutomationGrantUpdate = Updateable<AutomationGrantTable>;
export type BrowserControlLeaseRow = Selectable<BrowserControlLeaseTable>;
export type NewBrowserControlLease = Insertable<BrowserControlLeaseTable>;
export type BrowserControlLeaseUpdate = Updateable<BrowserControlLeaseTable>;
export type BrowserProviderRow = Selectable<BrowserProviderTable>;
export type NewBrowserProvider = Insertable<BrowserProviderTable>;
export type BrowserProviderUpdate = Updateable<BrowserProviderTable>;
export type BrowserSessionRow = Selectable<BrowserSessionTable>;
export type NewBrowserSession = Insertable<BrowserSessionTable>;
export type BrowserSessionUpdate = Updateable<BrowserSessionTable>;
export type BrowserTabRow = Selectable<BrowserTabTable>;
export type NewBrowserTab = Insertable<BrowserTabTable>;
export type BrowserTabUpdate = Updateable<BrowserTabTable>;
export type CapabilityInvocationRow = Selectable<CapabilityInvocationTable>;
export type NewCapabilityInvocation = Insertable<CapabilityInvocationTable>;
export type CapabilityInvocationUpdate = Updateable<CapabilityInvocationTable>;