import type { Client } from "basic-ftp"; import type { Workbook as ExcelJSWorkbook } from "exceljs"; import { WindowsDriver } from "../driver/windows/windows-driver"; import type { AutomationDriver } from "../driver/types"; import type { CatalogComponent, FlowNode } from "../types"; import { getHandler } from "./registry"; import { RuntimeError, type RunContext } from "./types"; /** 人为拟真操作的开关与随机参数(desktop 能力的进程级设置)。 */ export interface ManualOperationSettings { enabled: boolean; randomMove: boolean; randomClick: boolean; randomSleep: boolean; minMs: number; maxMs: number; } /** * 引擎实例状态容器(任务 1.3,R4:模块级全局状态收拢)。 * * 折中方案(决策记录 2026-11-04):能力文件的 register() 调用与全局注册表保持不变, * 但 Excel 缓存、数据库连接、FTP 客户端等进程级可变状态统一挂到引擎实例上。 * 包内各能力模块通过 defaultEngine 访问;宿主/CLI 可用 createEngine() 产生隔离实例, * 或拿到 defaultEngine 做测试间重置。 */ export class RpaEngine { /** excel-store:路径 → ExcelJS 工作簿共享缓存 */ readonly excelCache = new Map(); /** office.ts:Excel 句柄 id → 工作簿 */ readonly excelHandles = new Map(); /** remaining-core:连接 id → 数据库连接(mysql2/pg/mssql) */ readonly dbConnections = new Map(); /** ftp.ts:当前 FTP 连接 */ ftpClient?: Client; /** ocr-caps.ts:懒加载的 OCR 实例 */ ocrInstance?: unknown; /** desktop.ts:人为拟真操作设置 */ manualOperation: ManualOperationSettings = { enabled: false, randomMove: true, randomClick: true, randomSleep: true, minMs: 1000, maxMs: 3000 }; /** 桌面自动化驱动:按系统自动选(迁移指南五件小事 #2)。win32 用 PowerShell 版 WindowsDriver; * 其他平台尚未实现,访问时抛 platform_unsupported 明确报错(惰性创建,避免 import 即炸) */ private _driver?: AutomationDriver; get driver(): AutomationDriver { if (!this._driver) { if (process.platform === "win32") { this._driver = new WindowsDriver(); } else { throw new RuntimeError( "platform_unsupported", `该平台暂不支持桌面自动化驱动(当前:${process.platform})`, { recommendation: "桌面/键鼠/窗口能力目前仅支持 Windows;macOS/Linux 驱动在迁移指南三端计划中规划" } ); } } return this._driver; } /** 注入驱动实现(测试替身或宿主适配器);注入后不再走平台自动选择 */ setDriver(driver: AutomationDriver): void { this._driver = driver; } /** * 单能力直接调用(ADR-R1 语义真实化):CLI/宿主脚本不再裸摸全局 getHandler, * 而是经引擎入口调用。注意 handler 仍解析自进程注册表(ADR-R1 短期形态)。 */ async execute( componentId: string, args: Record, ctx: RunContext, node?: FlowNode, component?: CatalogComponent ): Promise { const handler = getHandler(componentId); if (!handler) { throw new RuntimeError("capability_not_implemented", `能力 ${componentId} 未注册`, { componentId }); } const resolvedNode: FlowNode = node ?? { id: "direct", componentId, position: { x: 0, y: 0 }, values: {}, options: {} }; return handler(ctx, resolvedNode, args, component as CatalogComponent); } } export interface EngineOptions { /** 注入桌面自动化驱动(测试替身或宿主适配器);缺省按 process.platform 自动选择 */ driver?: AutomationDriver; } export function createEngine(options?: EngineOptions): RpaEngine { const engine = new RpaEngine(); if (options?.driver) engine.setDriver(options.driver); return engine; } /** 能力模块内部使用的默认引擎实例(与全局注册表配套的进程级单例)。 */ export const defaultEngine = createEngine();