feat(browser): ship camofox v1.17.0 in compose, verified on a real instance
- compose: camofox service on by default (internal only, volume umt-camofox, healthcheck, CAMOFOX_REPLICAS=0 to skip); app depends on it with service_started/required=false and auto-registers camofox-env from BROWSER_PROVIDER_URL / BROWSER_ACCESS_KEY / BROWSER_ENABLED - docker/camofox/Dockerfile: builds upstream tag v1.17.0 (389c996); fixes the upstream Dockerfile (missing postinstall.js before npm ci, sh -c not forwarding SIGTERM so logins were never persisted on restart) - adapter fixes from the real container: coordinate click via evaluate + selector, focus typing with mode=keyboard, password values redacted from ARIA snapshots/extract, downloads field names, 410 after browser restart treated as lost tab; navigate reopens a tab in the same session - verified-capability table keyed by upstream version (1.17.0 all ten); other versions stay unverified; probe reports auth failures - settings: browser providers block (create/edit/delete/probe, capability table); API POST/PATCH/DELETE /browser/providers, POST .../probe - scripts/browser-verify: umtbr stack + mock NexusPHP run of REQ 4.7.4 items - docs: runbook browser §1/§7 (real-instance record), deploy §2.1 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
b4c9b1ead2
commit
40dd0e69e4
29 files changed
+1661
-73
No files matched your search
@@ -10,7 +10,10 @@ import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
BrowserError,
|
||||
type BrowserLogger,
|
||||
CAMOFOX_ENDPOINTS,
|
||||
CAMOFOX_VERIFIED_RELEASES,
|
||||
type CamofoxConfig,
|
||||
camofoxCapabilitiesFromOpenApi,
|
||||
camofoxStaticCapabilities,
|
||||
classifyHttpStatus,
|
||||
createCamofoxBrowserProvider,
|
||||
@@ -89,7 +92,7 @@ describe("能力声明", () => {
|
||||
expect(c.navigate).toMatchObject({ supported: true, verified: false, source: "static" });
|
||||
expect(c.snapshot).toMatchObject({ supported: true, verified: true });
|
||||
expect(c.interact).toMatchObject({ supported: true, verified: true });
|
||||
expect(c.interact.note).toContain("待核验");
|
||||
expect(c.interact.note).toContain("evaluate");
|
||||
expect(c.takeover).toMatchObject({ supported: true, verified: false });
|
||||
expect(c.upstreamVersion).toBeNull();
|
||||
});
|
||||
@@ -97,13 +100,31 @@ describe("能力声明", () => {
|
||||
it("探测 /openapi.json:读取上游版本,缺端点的能力降级", async () => {
|
||||
const { provider } = await setup({ omitPaths: ["get /tabs/{tabId}/snapshot"] });
|
||||
const c = await provider.capabilities({ probe: true });
|
||||
expect(c.upstreamVersion).toBe("1.17.0");
|
||||
expect(c.upstreamVersion).toBe("1.17.0-mock");
|
||||
expect(c.probedAt).not.toBeNull();
|
||||
expect(c.navigate).toMatchObject({ supported: true, source: "probed", verified: false });
|
||||
expect(c.snapshot).toMatchObject({ supported: false, source: "probed" });
|
||||
expect(c.snapshot.note).toContain("/tabs/{tabId}/snapshot");
|
||||
});
|
||||
|
||||
it("真机核验表:探测到已核验的上游版本才置 verified;配置可逐项撤销;其它版本不继承", () => {
|
||||
const paths: Record<string, Record<string, unknown>> = {};
|
||||
for (const e of Object.values(CAMOFOX_ENDPOINTS).flat()) {
|
||||
const [m, p] = e.split(" ");
|
||||
paths[p!] = { ...paths[p!], [m!]: {} };
|
||||
}
|
||||
const doc = (version: string) => ({ info: { version }, paths });
|
||||
const ok = camofoxCapabilitiesFromOpenApi(doc("1.17.0"));
|
||||
expect(Object.keys(CAMOFOX_VERIFIED_RELEASES)).toContain("1.17.0");
|
||||
for (const k of CAMOFOX_VERIFIED_RELEASES["1.17.0"]!.keys)
|
||||
expect(ok[k]).toMatchObject({ supported: true, verified: true });
|
||||
expect(camofoxCapabilitiesFromOpenApi(doc("1.17.0"), { takeover: false }).takeover.verified).toBe(false);
|
||||
const other = camofoxCapabilitiesFromOpenApi(doc("1.18.0"));
|
||||
expect(other.snapshot.verified).toBe(false);
|
||||
// 静态声明不知道上游版本:一律未核验
|
||||
expect(camofoxStaticCapabilities().snapshot.verified).toBe(false);
|
||||
});
|
||||
|
||||
it("none 实现:能力全关,动作报 unavailable", async () => {
|
||||
const none = createNoneBrowserProvider();
|
||||
const c = await none.capabilities();
|
||||
@@ -300,6 +321,7 @@ describe("错误分类", () => {
|
||||
expect(classifyHttpStatus(401)).toBe("auth");
|
||||
expect(classifyHttpStatus(403)).toBe("auth");
|
||||
expect(classifyHttpStatus(404)).toBe("not_found");
|
||||
expect(classifyHttpStatus(410)).toBe("not_found");
|
||||
expect(classifyHttpStatus(409)).toBe("bad_request");
|
||||
expect(classifyHttpStatus(429)).toBe("rate_limited");
|
||||
expect(classifyHttpStatus(500)).toBe("upstream");
|
||||
|
||||
Reference in new issue
Block a user