import type { FlowDefinition } from "../types"; import { RuntimeError } from "./types"; /** * 宿主桥(任务 2.1 正式化)。 * * 包内能力需要的宿主能力(子流程仓库 / Electron 窗口 / DPI 换算 / 应用配置) * 通过本接口注入,包本身禁止 import "electron" 或宿主模块(见包内依赖检查脚本)。 * 宿主启动时调用 setSubflowSource() / setHostBridge()。 */ export interface SubflowSource { listFlows(): Promise>; getFlow(id: string): Promise; } let subflowSource: SubflowSource | undefined; export function setSubflowSource(source: SubflowSource): void { subflowSource = source; } export function getSubflowSource(): SubflowSource | undefined { return subflowSource; } /** 人机交互表单窗口的最小面(能力只用到这些成员,宿主用 BrowserWindow 适配) */ export interface HostDialogWindow { isDestroyed(): boolean; destroy(): void; loadURL(url: string): Promise; on(event: "closed", listener: () => void): void; /** 在窗口页面里执行 JS 并取回结果(宿主侧以 userGesture=true 执行) */ executeJavaScript(script: string): Promise; } export interface HostPoint { x: number; y: number; } /** AI 服务配置(宿主 settings.aiConfig) */ export interface HostAiConfig { baseUrl: string; apiKey: string; model: string; visionModel: string; } /** 显示器信息(视觉能力的屏幕枚举用;id 为宿主侧稳定标识字符串) */ export interface HostDisplay { id: string; bounds: { x: number; y: number; width: number; height: number }; scaleFactor: number; } /** * 屏幕服务(迁移阶段1-A9):视觉能力原本经动态 import("electron") 拿 * desktopCapturer/screen,现改为宿主注入,Core 回归零 Electron 依赖。 */ export interface HostScreenService { /** 全部显示器 */ listDisplays(): HostDisplay[]; /** 主显示器 */ primaryDisplay(): HostDisplay; /** DIP 屏幕坐标 → 物理像素 */ dipToScreenPoint(point: HostPoint): HostPoint; /** 与该 DIP 矩形相交面积最大的显示器 */ displayMatching(rect: { x: number; y: number; width: number; height: number }): HostDisplay; /** * 抓取指定显示器当前画面,像素尺寸尽量接近 want 值,返回 PNG 字节。 * 无画面/抓取失败时抛错(调用方决定回退策略)。 */ captureDisplay(displayId: string, wantWidth: number, wantHeight: number): Promise; } /** 宿主能力桥最小面(决策点 4):dialog 窗口工厂 / DPI 坐标换算 / 配置读取 / 屏幕服务 */ export interface HostBridge { /** 创建人机交互表单窗口(human-dialog 用) */ createFormWindow(options: { title: string }): HostDialogWindow; /** DIP 逻辑坐标 → 物理像素(window-extended 的鼠标/拖拽坐标换算) */ dipToScreenPoint(point: HostPoint): HostPoint; /** 物理像素 → DIP 逻辑坐标 */ screenToDipPoint(point: HostPoint): HostPoint; /** 读取 AI 服务配置(ai-service 用) */ loadAiConfig(): HostAiConfig; /** 屏幕服务(vision/cv-vision 截屏与多屏几何);未注入时视觉能力报 host_unavailable */ screen?: HostScreenService; } let hostBridge: HostBridge | undefined; export function setHostBridge(bridge: HostBridge): void { hostBridge = bridge; } export function getHostBridge(): HostBridge | undefined { return hostBridge; } /** 视觉能力取屏幕服务的统一入口:未注入宿主桥/屏幕服务时抛稳定错误码 */ export function requireScreenService(): HostScreenService { const bridge = getHostBridge(); if (!bridge?.screen) { throw new RuntimeError("host_unavailable", "宿主未注入屏幕服务(HostBridge.screen),截屏/视觉能力不可用", { recommendation: "宿主启动时需调用 setHostBridge 并提供 screen 服务(参考 demo 的 src/main/index.ts)" }); } return bridge.screen; }