From 4f387b5ae77a5e53d0fa4854c1e49fefe650203a Mon Sep 17 00:00:00 2001 From: AskaEth Date: Wed, 12 Aug 2026 21:19:35 +0800 Subject: [PATCH] feat(daemon): add stealth injection orchestrator (Task 17) - Create stealth/index.ts: applyStealth() calls all 6 injection modules in correct order (initScript before nav, page.route after page) - Update browser-manager.ts: call applyStealth in createPage before goto - Constructor now accepts profileId (default 'desktop-chrome') or profile object - Update daemon src/index.ts: re-export stealth orchestrator, getProfile, humanClick, humanType, humanScroll, injectHeaderStealth --- packages/daemon/src/browser-manager.ts | 16 ++++++++-- packages/daemon/src/index.ts | 16 +++++++++- packages/daemon/src/stealth/index.ts | 43 ++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 packages/daemon/src/stealth/index.ts diff --git a/packages/daemon/src/browser-manager.ts b/packages/daemon/src/browser-manager.ts index ab51ab2..b07983a 100644 --- a/packages/daemon/src/browser-manager.ts +++ b/packages/daemon/src/browser-manager.ts @@ -4,6 +4,8 @@ import StealthPlugin from 'puppeteer-extra-plugin-stealth'; import type { Browser, BrowserContext, Page } from 'playwright'; import type { FingerprintProfile, PageInfo } from '@visionl/core'; import { PageRegistry } from './page-registry.js'; +import { getProfile } from './stealth/profiles/index.js'; +import { applyStealth } from './stealth/index.js'; // Apply stealth plugin once at module level / 模块级别一次性注入隐身插件 chromium.use(StealthPlugin()); @@ -18,8 +20,14 @@ export class BrowserManager { private registry = new PageRegistry(); private profile: FingerprintProfile; - constructor(profile: FingerprintProfile) { - this.profile = profile; + constructor(profileOrId: FingerprintProfile | string = 'desktop-chrome') { + if (typeof profileOrId === 'string') { + const resolved = getProfile(profileOrId); + if (!resolved) throw new Error(`Unknown profile: ${profileOrId}`); + this.profile = resolved; + } else { + this.profile = profileOrId; + } } async init(): Promise { @@ -61,6 +69,10 @@ export class BrowserManager { }); const page = await context.newPage(); + + // Apply stealth injections before navigation / 导航前注入隐身模块 + await applyStealth(context, page, this.profile); + await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 }); const title = await page.title(); diff --git a/packages/daemon/src/index.ts b/packages/daemon/src/index.ts index c5e7ecd..4780c8b 100644 --- a/packages/daemon/src/index.ts +++ b/packages/daemon/src/index.ts @@ -1,2 +1,16 @@ -// @visionl/daemon — placeholder, to be implemented in Task 3 +// @visionl/daemon — browser daemon entry point / 浏览器守护进程入口 export const DAEMON_VERSION = '0.1.0'; + +// Re-export stealth orchestrator and helpers / 重新导出隐身编排器和辅助函数 +export { + applyStealth, + getProfile, + listProfiles, + humanClick, + humanType, + humanScroll, + injectHeaderStealth, +} from './stealth/index.js'; + +export { BrowserManager } from './browser-manager.js'; +export { startServer } from './server.js'; diff --git a/packages/daemon/src/stealth/index.ts b/packages/daemon/src/stealth/index.ts new file mode 100644 index 0000000..c2843c6 --- /dev/null +++ b/packages/daemon/src/stealth/index.ts @@ -0,0 +1,43 @@ +// Stealth Injection Orchestrator — single entry point for all stealth modules / 隐身注入编排器 — 所有隐身模块的统一入口 +import type { BrowserContext, Page } from 'playwright'; +import type { FingerprintProfile } from '@visionl/core'; +import { injectNavigatorStealth } from './navigator.js'; +import { injectChromeRuntime } from './chrome-runtime.js'; +import { injectScreenStealth } from './screen.js'; +import { injectPermissionsStealth } from './permissions.js'; +import { injectCanvasNoise } from './canvas-noise.js'; +import { injectHeaderStealth } from './headers.js'; + +// Re-export for external use / 重新导出供外部使用 +export { getProfile, listProfiles } from './profiles/index.js'; +export { humanClick, humanType, humanScroll } from './human-input.js'; +export { injectHeaderStealth } from './headers.js'; + +/** + * Apply all stealth injections to a newly created page. + * Phase 1: addInitScript (must run before navigation). + * Phase 2: page.route interception (must run after page creation). + * + * @param context BrowserContext + * @param page Playwright Page + * @param profile Fingerprint profile to emulate + */ +export async function applyStealth( + context: BrowserContext, + page: Page, + profile: FingerprintProfile, +): Promise { + // Phase 1: initScript injections (must happen before page navigation) + // 阶段 1: initScript 注入(必须在页面导航前执行) + await injectNavigatorStealth(context, profile); + await injectChromeRuntime(context); + await injectScreenStealth(context, profile); + await injectPermissionsStealth(context, profile); + if (profile.canvasNoise.enabled) { + await injectCanvasNoise(context, profile.canvasNoise); + } + + // Phase 2: page.route interception (must happen after page creation) + // 阶段 2: page.route 拦截(必须在页面创建后执行) + await injectHeaderStealth(page, profile); +}