102 lines
4.0 KiB
TypeScript
102 lines
4.0 KiB
TypeScript
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<string, ExcelJSWorkbook>();
|
||
/** office.ts:Excel 句柄 id → 工作簿 */
|
||
readonly excelHandles = new Map<string, ExcelJSWorkbook>();
|
||
/** remaining-core:连接 id → 数据库连接(mysql2/pg/mssql) */
|
||
readonly dbConnections = new Map<string, unknown>();
|
||
/** 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<string, unknown>,
|
||
ctx: RunContext,
|
||
node?: FlowNode,
|
||
component?: CatalogComponent
|
||
): Promise<unknown> {
|
||
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();
|