feat(browser): fixed Windows identity and per-site egress in umt-cookies plugin (BR-4)
- browser:launching hook generates a Windows fingerprint once (camoufox-js, os=windows), persists it to /root/.camofox/umt-identity.json and reuses it on every launch; Firefox/Camoufox upgrades only bump UA/version fields - Windows fontconfig with absolute font dir (upstream path was broken) - GET /umt/identity (public UA/OS/version) and PUT /umt/sessions/:id/proxy (per-context proxy; Firefox no longer uses container HTTP_PROXY env) - adapter identity()/setEgress(), splitBrowserOnlyCookies for cf_clearance/__cf_bm Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
6663afce7e
commit
ebd07c9230
8 files changed
+746
-5
No files matched your search
@@ -19,7 +19,9 @@ import type {
|
||||
BrowserCapabilities,
|
||||
BrowserControlLease,
|
||||
BrowserCookie,
|
||||
BrowserEgressProxy,
|
||||
BrowserHealth,
|
||||
BrowserIdentity,
|
||||
BrowserSession,
|
||||
BrowserSnapshot,
|
||||
BrowserTab,
|
||||
@@ -175,7 +177,7 @@ export class CamofoxBrowserProvider implements BrowserProvider {
|
||||
}
|
||||
|
||||
private async request(
|
||||
method: "GET" | "POST" | "DELETE",
|
||||
method: "GET" | "POST" | "PUT" | "DELETE",
|
||||
path: string,
|
||||
opts: { query?: Record<string, string | undefined>; body?: Json; timeoutMs?: number; auth?: boolean } = {},
|
||||
): Promise<Response> {
|
||||
@@ -386,6 +388,54 @@ export class CamofoxBrowserProvider implements BrowserProvider {
|
||||
return typeof v === "string" || typeof v === "number" ? String(v) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 固定浏览器身份(BR-4):umt 插件 `GET /umt/identity`(UA、OS、版本、生成时间,不含指纹细节);
|
||||
* 插件不在或身份关闭(404)返回 null。
|
||||
*/
|
||||
async identity(): Promise<BrowserIdentity | null> {
|
||||
let body: Json;
|
||||
try {
|
||||
body = await this.json(await this.request("GET", "/umt/identity", { timeoutMs: 10_000 }), "/umt/identity");
|
||||
} catch (e) {
|
||||
if (e instanceof BrowserError && e.httpStatus === 404) return null;
|
||||
throw e;
|
||||
}
|
||||
const userAgent = str(body.userAgent);
|
||||
if (!userAgent) return null;
|
||||
return {
|
||||
userAgent,
|
||||
os: str(body.os) ?? null,
|
||||
platform: str(body.platform) ?? null,
|
||||
firefoxVersion: str(body.firefoxVersion) ?? null,
|
||||
camoufoxVersion: str(body.camoufoxVersion) ?? null,
|
||||
fingerprintId: str(body.fingerprintId) ?? null,
|
||||
revision: int(body.revision) ?? 1,
|
||||
createdAt: str(body.createdAt) ?? null,
|
||||
updatedAt: str(body.updatedAt) ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 该 profile 的出口(BR-4 出口 IP 按站点统一):umt 插件 `PUT /umt/sessions/{userId}/proxy`。
|
||||
* proxy = null 直连;活动上下文出口不同时插件会先关掉它(落盘登录状态)。插件不在报 not_implemented。
|
||||
*/
|
||||
async setEgress(profileKey: string, proxy: BrowserEgressProxy | null): Promise<{ restarted: boolean }> {
|
||||
try {
|
||||
const body = await this.json(
|
||||
await this.request("PUT", `/umt/sessions/${encodeURIComponent(profileKey)}/proxy`, {
|
||||
body: { proxy } as unknown as Json,
|
||||
timeoutMs: 15_000,
|
||||
}),
|
||||
"/umt/sessions/{userId}/proxy",
|
||||
);
|
||||
return { restarted: body.restarted === true };
|
||||
} catch (e) {
|
||||
if (e instanceof BrowserError && e.httpStatus === 404)
|
||||
throw new BrowserError("not_implemented", "浏览器服务不支持按站点设出口(镜像未带新版 umt-cookies 插件)");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重建会话的上游上下文(BR-3):上游重启 / 回收后旧标签页都不在了。先(可选)注入 Cookie,再用同一 userId / sessionKey
|
||||
* 开新标签页并导航到 url;旧的打开中标签页标 lost,旧元素引用作废。不检查控制者:接管中的会话重连后仍归原控制者。
|
||||
|
||||
Reference in new issue
Block a user