Files
rootandClaude Opus 5.5 40dd0e69e4 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]>
2026-09-29 00:05:21 +08:00

36 lines
1.7 KiB
TypeScript

/**
* 真机核验用的 mock NexusPHP 站(testkit mock-nexusphp,观众风格),在 compose 网络里以 mocknexus:8080 提供给 camofox。
* testkit 的 mock 只监听 127.0.0.1 随机端口,这里在 0.0.0.0:8080 做一层 TCP 转发。
* 控制端点(只供核验脚本):POST /__mock/login?v=0|1 切换登录态;GET /__mock/infohash?id= 取种子 infohash;
* GET /__mock/stats 取 takelogin 次数。
*/
import { createServer } from "node:http";
import { connect, createServer as createTcp } from "node:net";
import { createMockNexusPhpSite } from "../../packages/testkit/src/sites/mock-nexusphp.ts";
const site = await createMockNexusPhpSite({ siteName: "观众(mock)", loginMode: "redirect" });
const upstream = new URL(site.baseUrl);
const control = createServer((req, res) => {
const u = new URL(req.url ?? "/", "http://x");
res.setHeader("content-type", "application/json");
if (u.pathname === "/__mock/login") {
site.setLoggedIn(u.searchParams.get("v") !== "0");
return res.end(JSON.stringify({ ok: true }));
}
if (u.pathname === "/__mock/infohash")
return res.end(JSON.stringify({ infohash: site.infoHashOf(u.searchParams.get("id") ?? "") }));
if (u.pathname === "/__mock/stats")
return res.end(JSON.stringify({ loginAttempts: site.loginAttempts, releases: site.releases.length }));
res.statusCode = 404;
res.end("{}");
});
control.listen(8081, "0.0.0.0");
createTcp((client) => {
const up = connect(Number(upstream.port), upstream.hostname);
client.pipe(up).pipe(client);
client.on("error", () => up.destroy());
up.on("error", () => client.destroy());
}).listen(8080, "0.0.0.0", () => console.log(`mock nexus ${site.baseUrl} → 0.0.0.0:8080(控制 8081)`));