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
This commit is contained in:
@@ -4,6 +4,8 @@ import StealthPlugin from 'puppeteer-extra-plugin-stealth';
|
|||||||
import type { Browser, BrowserContext, Page } from 'playwright';
|
import type { Browser, BrowserContext, Page } from 'playwright';
|
||||||
import type { FingerprintProfile, PageInfo } from '@visionl/core';
|
import type { FingerprintProfile, PageInfo } from '@visionl/core';
|
||||||
import { PageRegistry } from './page-registry.js';
|
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 / 模块级别一次性注入隐身插件
|
// Apply stealth plugin once at module level / 模块级别一次性注入隐身插件
|
||||||
chromium.use(StealthPlugin());
|
chromium.use(StealthPlugin());
|
||||||
@@ -18,8 +20,14 @@ export class BrowserManager {
|
|||||||
private registry = new PageRegistry();
|
private registry = new PageRegistry();
|
||||||
private profile: FingerprintProfile;
|
private profile: FingerprintProfile;
|
||||||
|
|
||||||
constructor(profile: FingerprintProfile) {
|
constructor(profileOrId: FingerprintProfile | string = 'desktop-chrome') {
|
||||||
this.profile = profile;
|
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<void> {
|
async init(): Promise<void> {
|
||||||
@@ -61,6 +69,10 @@ export class BrowserManager {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
|
|
||||||
|
// Apply stealth injections before navigation / 导航前注入隐身模块
|
||||||
|
await applyStealth(context, page, this.profile);
|
||||||
|
|
||||||
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
||||||
const title = await page.title();
|
const title = await page.title();
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
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';
|
||||||
|
|||||||
@@ -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<void> {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user