import { featureManifest } from "@umt/contracts"; import { sourcesIdentityModule } from "../sources-identity/index.ts"; import type { ServerContext, ServerModule } from "../types.ts"; import { registerBrowserCapabilities } from "./capabilities.ts"; import { registerBrowserRoutes } from "./routes.ts"; import { type BrowserModuleOptions, BrowserService } from "./service.ts"; /** 进程内:ServerContext → 本模块为它建的 BrowserService(agent 模块的视觉子流程要与能力共用同一实例) */ const byCtx = new WeakMap(); /** 取浏览器模块为这个上下文建的服务;浏览器模块没装配时返回 null */ export function browserServiceFor(ctx: ServerContext): BrowserService | null { return byCtx.get(ctx) ?? null; } /** * C1-E 浏览器:BrowserProvider(camofox)之上的站点浏览器会话、原站视图与人工接管、Agent 浏览器能力。 * 会话/标签/租约以数据库(0023 四张表)为准;站点限流与 sources-identity 共享;下载产物只进后端暂存核验, * 提交走 POST /acquisition-plans 同一流程。运维与真实实例核验见 docs/runbook/browser.md。 */ export function createBrowserModule(opts: BrowserModuleOptions = {}): ServerModule & { service(ctx: ServerContext): BrowserService; } { const services = new WeakMap(); const options: BrowserModuleOptions = { runtime: (ctx) => sourcesIdentityModule.runtime(ctx), ...opts, }; const service = (ctx: ServerContext) => { let s = services.get(ctx); if (!s) { s = new BrowserService(ctx, options); services.set(ctx, s); byCtx.set(ctx, s); } return s; }; return { name: "browser", manifest: featureManifest("browser"), service, registerRoutes(app, ctx) { registerBrowserRoutes(app, () => service(ctx)); }, registerTasks(_registry, ctx) { // worker 里的 refresh-login 也要能从浏览器 profile 读回 Cookie(BR-2):建服务即注册读回钩子 service(ctx); }, registerCapabilities(registry, ctx) { service(ctx); registerBrowserCapabilities(registry, () => service(ctx)); }, }; } export const browserModule = createBrowserModule();