阶段1-rpa-core建包: engine+31个纯Node能力迁入packages/rpa-core, 领域类型单一源化, createEngine工厂收拢模块级状态
- engine/(registry/types/runtime/expression)物理迁入包, 解耦shared/types(域类型归包, 宿主type-only re-export) - 31个纯Node能力+27个测试迁入包; 宿主留守Electron/浏览器/AI依赖能力, index.ts经@rpa/core/capabilities拉起 - 新增createEngine()/defaultEngine: excel缓存/Excel句柄/db连接/FTP客户端/OCR实例/拟真设置挂引擎实例 - remaining-core子流程依赖改为host-bridge注入, 包内不再import宿主flows - 宿主main/index.ts启动时注入子流程源; electron-vite/vitest/tsconfig配@rpa/core源码别名 - 验证: typecheck绿, 快照909个id diff为零, 332测试过(2个桌面冒烟因会话无交互SetForegroundWindow被拒, 环境阻塞)
This commit is contained in:
parent
4139400dbd
commit
caa8e19332
@ -6,9 +6,12 @@ export default defineConfig({
|
|||||||
main: {
|
main: {
|
||||||
plugins: [externalizeDepsPlugin()],
|
plugins: [externalizeDepsPlugin()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: [
|
||||||
"@shared": resolve("src/shared")
|
{ find: "@shared", replacement: resolve("src/shared") },
|
||||||
}
|
// rpa-core 包以源码形式打进主进程产物(不作为外部依赖运行时 require)
|
||||||
|
{ find: /^@rpa\/core$/, replacement: resolve("packages/rpa-core/src/index.ts") },
|
||||||
|
{ find: /^@rpa\/core\/capabilities$/, replacement: resolve("packages/rpa-core/src/capabilities/index.ts") }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"description": "Canvas-based RPA desktop application",
|
"description": "Canvas-based RPA desktop application",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
|
"workspaces": [
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "electron-vite dev",
|
"dev": "electron-vite dev",
|
||||||
"dev:local": "node scripts/dev-safe.mjs",
|
"dev:local": "node scripts/dev-safe.mjs",
|
||||||
|
|||||||
40
packages/rpa-core/package.json
Normal file
40
packages/rpa-core/package.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "@rpa/core",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "RPA 能力层:零 Electron 依赖的流程引擎、能力注册表与纯 Node 能力",
|
||||||
|
"main": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts",
|
||||||
|
"./capabilities": "./src/capabilities/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "vitest run",
|
||||||
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@gutenye/ocr-node": "^1.4.9",
|
||||||
|
"adm-zip": "^0.5.16",
|
||||||
|
"basic-ftp": "^6.2.3",
|
||||||
|
"exceljs": "^4.4.0",
|
||||||
|
"imapflow": "^2.10.0",
|
||||||
|
"mailparser": "^3.14.1",
|
||||||
|
"mssql": "^12.7.13",
|
||||||
|
"mysql2": "^3.24.6",
|
||||||
|
"nodemailer": "^6.9.16",
|
||||||
|
"pdf-lib": "^1.17.1",
|
||||||
|
"pg": "^8.23.1",
|
||||||
|
"pngjs": "^7.0.0",
|
||||||
|
"sm-crypto": "^0.5.8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/adm-zip": "^0.5.7",
|
||||||
|
"@types/mssql": "^12.3.0",
|
||||||
|
"@types/nodemailer": "^6.4.17",
|
||||||
|
"@types/pg": "^8.23.1",
|
||||||
|
"@types/pngjs": "^6.0.5",
|
||||||
|
"typescript": "^5.7.2",
|
||||||
|
"vitest": "^2.1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import "./data";
|
import "./data";
|
||||||
import "./desktop";
|
import "./desktop";
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import "./desktop";
|
import "./desktop";
|
||||||
import "./basic-encryption";
|
import "./basic-encryption";
|
||||||
@ -7,7 +7,7 @@ import { PDFDocument } from "pdf-lib";
|
|||||||
import { PNG } from "pngjs";
|
import { PNG } from "pngjs";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import "./desktop";
|
import "./desktop";
|
||||||
import "./office";
|
import "./office";
|
||||||
import "./pdf";
|
import "./pdf";
|
||||||
@ -2,7 +2,7 @@ import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import type { Catalog, CatalogComponent, FlowNode } from "../../shared/types";
|
import type { Catalog, CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import "./data-extended";
|
import "./data-extended";
|
||||||
@ -4,7 +4,7 @@ import path from "node:path";
|
|||||||
import { afterAll, describe, expect, it } from "vitest";
|
import { afterAll, describe, expect, it } from "vitest";
|
||||||
import { capabilityIds, getHandler } from "../engine/registry";
|
import { capabilityIds, getHandler } from "../engine/registry";
|
||||||
import { RuntimeError } from "../engine/types";
|
import { RuntimeError } from "../engine/types";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import "./data";
|
import "./data";
|
||||||
import "./data-extended";
|
import "./data-extended";
|
||||||
@ -2,7 +2,7 @@ import { mkdtemp, rm } from "node:fs/promises";
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import "./data-table-common";
|
import "./data-table-common";
|
||||||
@ -2,21 +2,12 @@ import { execFile } from "node:child_process";
|
|||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "../engine/registry";
|
||||||
import { RuntimeError, type CapabilityHandler } from "../engine/types";
|
import { RuntimeError, type CapabilityHandler } from "../engine/types";
|
||||||
|
import { type ManualOperationSettings, defaultEngine } from "../engine/engine";
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile);
|
const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
interface ManualOperationSettings {
|
// 人为拟真设置本体挂在引擎实例上(任务 1.3),此处委托 defaultEngine。
|
||||||
enabled: boolean;
|
const manualOperation: ManualOperationSettings = defaultEngine.manualOperation;
|
||||||
randomMove: boolean;
|
|
||||||
randomClick: boolean;
|
|
||||||
randomSleep: boolean;
|
|
||||||
minMs: number;
|
|
||||||
maxMs: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const manualOperation: ManualOperationSettings = {
|
|
||||||
enabled: false, randomMove: true, randomClick: true, randomSleep: true, minMs: 1000, maxMs: 3000
|
|
||||||
};
|
|
||||||
|
|
||||||
function jitter(value: number, amount = 3): number {
|
function jitter(value: number, amount = 3): number {
|
||||||
return Math.max(0, Math.round(value + (Math.random() * amount * 2 - amount)));
|
return Math.max(0, Math.round(value + (Math.random() * amount * 2 - amount)));
|
||||||
@ -4,7 +4,7 @@ import path from "node:path";
|
|||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import ExcelJS from "exceljs";
|
import ExcelJS from "exceljs";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import "./excel-com";
|
import "./excel-com";
|
||||||
|
|
||||||
@ -3,7 +3,7 @@ import os from "node:os";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import ExcelJS from "exceljs";
|
import ExcelJS from "exceljs";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { Catalog, CatalogComponent, FlowNode } from "../../shared/types";
|
import type { Catalog, CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import "./index";
|
import "./index";
|
||||||
@ -3,7 +3,7 @@ import os from "node:os";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import ExcelJS from "exceljs";
|
import ExcelJS from "exceljs";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import "./office";
|
import "./office";
|
||||||
@ -1,10 +1,11 @@
|
|||||||
import ExcelJS from "exceljs";
|
import ExcelJS from "exceljs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
import { defaultEngine } from "../engine/engine";
|
||||||
|
|
||||||
// Excel 工作簿共享缓存:同一文件路径在整条流程中复用同一个 ExcelJS 实例。
|
// Excel 工作簿共享缓存:同一文件路径在整条流程中复用同一个 ExcelJS 实例。
|
||||||
// 此前 office.ts / office-file-extended.ts / excel-common.ts / extended.ts 各自持有
|
// 此前 office.ts / office-file-extended.ts / excel-common.ts / extended.ts 各自持有
|
||||||
// 工作簿实例并整簿写回磁盘,后保存者会把先保存者的修改整体覆盖掉。
|
// 工作簿实例并整簿写回磁盘,后保存者会把先保存者的修改整体覆盖掉。
|
||||||
const cache = new Map<string, ExcelJS.Workbook>();
|
// 缓存本体挂在引擎实例上(任务 1.3),此处委托 defaultEngine。
|
||||||
|
|
||||||
function keyOf(filePath: string): string {
|
function keyOf(filePath: string): string {
|
||||||
return path.resolve(filePath);
|
return path.resolve(filePath);
|
||||||
@ -13,22 +14,22 @@ function keyOf(filePath: string): string {
|
|||||||
/** 按路径取工作簿:命中缓存直接复用,未命中从磁盘读取后登记。 */
|
/** 按路径取工作簿:命中缓存直接复用,未命中从磁盘读取后登记。 */
|
||||||
export async function acquireWorkbook(filePath: string): Promise<ExcelJS.Workbook> {
|
export async function acquireWorkbook(filePath: string): Promise<ExcelJS.Workbook> {
|
||||||
const key = keyOf(filePath);
|
const key = keyOf(filePath);
|
||||||
let workbook = cache.get(key);
|
let workbook = defaultEngine.excelCache.get(key);
|
||||||
if (!workbook) {
|
if (!workbook) {
|
||||||
workbook = new ExcelJS.Workbook();
|
workbook = new ExcelJS.Workbook();
|
||||||
await workbook.xlsx.readFile(key);
|
await workbook.xlsx.readFile(key);
|
||||||
cache.set(key, workbook);
|
defaultEngine.excelCache.set(key, workbook);
|
||||||
}
|
}
|
||||||
return workbook;
|
return workbook;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 把一个新建/另建的工作簿实例登记为该路径的共享实例。 */
|
/** 把一个新建/另建的工作簿实例登记为该路径的共享实例。 */
|
||||||
export function publishWorkbook(filePath: string, workbook: ExcelJS.Workbook): ExcelJS.Workbook {
|
export function publishWorkbook(filePath: string, workbook: ExcelJS.Workbook): ExcelJS.Workbook {
|
||||||
cache.set(keyOf(filePath), workbook);
|
defaultEngine.excelCache.set(keyOf(filePath), workbook);
|
||||||
return workbook;
|
return workbook;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 释放路径对应的共享实例(关闭工作簿时调用)。 */
|
/** 释放路径对应的共享实例(关闭工作簿时调用)。 */
|
||||||
export function releaseWorkbook(filePath: string): void {
|
export function releaseWorkbook(filePath: string): void {
|
||||||
cache.delete(keyOf(filePath));
|
defaultEngine.excelCache.delete(keyOf(filePath));
|
||||||
}
|
}
|
||||||
@ -3,7 +3,7 @@ import os from "node:os";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import ExcelJS from "exceljs";
|
import ExcelJS from "exceljs";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import "./extended";
|
import "./extended";
|
||||||
@ -3,7 +3,7 @@ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import "./files";
|
import "./files";
|
||||||
@ -3,11 +3,10 @@ import path from "node:path";
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "../engine/registry";
|
||||||
import { RuntimeError } from "../engine/types";
|
import { RuntimeError } from "../engine/types";
|
||||||
|
import { defaultEngine } from "../engine/engine";
|
||||||
const flags = globalThis as { __rpaFtpClient?: Client };
|
|
||||||
|
|
||||||
async function requireClient(nodeId: string): Promise<Client> {
|
async function requireClient(nodeId: string): Promise<Client> {
|
||||||
if (flags.__rpaFtpClient) return flags.__rpaFtpClient;
|
if (defaultEngine.ftpClient) return defaultEngine.ftpClient;
|
||||||
throw new RuntimeError("validation_error", "尚未连接 FTP,请先使用 FTP_Connect", { nodeId, componentId: nodeId, recommendation: "先运行 FTP_Connect 建立连接" });
|
throw new RuntimeError("validation_error", "尚未连接 FTP,请先使用 FTP_Connect", { nodeId, componentId: nodeId, recommendation: "先运行 FTP_Connect 建立连接" });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,14 +26,14 @@ register("FTP_Connect", async (ctx, node, args) => {
|
|||||||
const secure = String(args.ftp_type ?? "").toLowerCase().includes("ftps");
|
const secure = String(args.ftp_type ?? "").toLowerCase().includes("ftps");
|
||||||
const client = new Client(10_000);
|
const client = new Client(10_000);
|
||||||
await client.access({ host, port, secure, user, password });
|
await client.access({ host, port, secure, user, password });
|
||||||
flags.__rpaFtpClient = client;
|
defaultEngine.ftpClient = client;
|
||||||
ctx.log("已连接 FTP:" + host + ":" + port, "info", node.id);
|
ctx.log("已连接 FTP:" + host + ":" + port, "info", node.id);
|
||||||
return { connected: true, host, port };
|
return { connected: true, host, port };
|
||||||
});
|
});
|
||||||
|
|
||||||
register("FTP_Disconnect", async (ctx, node) => {
|
register("FTP_Disconnect", async (ctx, node) => {
|
||||||
flags.__rpaFtpClient?.close();
|
defaultEngine.ftpClient?.close();
|
||||||
flags.__rpaFtpClient = undefined;
|
defaultEngine.ftpClient = undefined;
|
||||||
ctx.log("已断开 FTP 连接", "info", node.id);
|
ctx.log("已断开 FTP 连接", "info", node.id);
|
||||||
return { disconnected: true };
|
return { disconnected: true };
|
||||||
});
|
});
|
||||||
39
packages/rpa-core/src/capabilities/index.ts
Normal file
39
packages/rpa-core/src/capabilities/index.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* rpa-core 纯 Node 能力注册入口。
|
||||||
|
*
|
||||||
|
* import 本模块即完成全部纯 Node 能力的副作用注册(与改造前
|
||||||
|
* src/main/capabilities/index.ts 的加载顺序保持一致,别名/重复 id 的
|
||||||
|
* last-wins 语义不变)。依赖 Electron 或宿主模块的能力(human-dialog /
|
||||||
|
* window-extended / ai-service / ai-caps / browser* / web-batch2 / web-crawl)
|
||||||
|
* 留在宿主 src/main/capabilities,阶段 2 通过 HostBridge 迁入。
|
||||||
|
*/
|
||||||
|
import "./control";
|
||||||
|
import "./files";
|
||||||
|
import "./http";
|
||||||
|
import "./data";
|
||||||
|
import "./office";
|
||||||
|
import "./desktop";
|
||||||
|
import "./desktop-uia";
|
||||||
|
import "./time-caps";
|
||||||
|
import "./windows-system";
|
||||||
|
import "./ftp";
|
||||||
|
import "./ocr-caps";
|
||||||
|
import "./pdf";
|
||||||
|
import "./word";
|
||||||
|
import "./word-extended";
|
||||||
|
import "./message";
|
||||||
|
import "./vision";
|
||||||
|
import "./cv-vision";
|
||||||
|
import "./extended";
|
||||||
|
import "./system-extended";
|
||||||
|
import "./office-file-extended";
|
||||||
|
import "./office-aliases";
|
||||||
|
import "./excel-common";
|
||||||
|
import "./excel-com";
|
||||||
|
import "./data-extended";
|
||||||
|
import "./utility-extended";
|
||||||
|
import "./data-table-common";
|
||||||
|
import "./basic-encryption";
|
||||||
|
import "./mail-remaining";
|
||||||
|
import "./remaining-core";
|
||||||
|
import "./common-final";
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import "./desktop";
|
import "./desktop";
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import net from "node:net";
|
import net from "node:net";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler, hasCapability } from "../engine/registry";
|
import { getHandler, hasCapability } from "../engine/registry";
|
||||||
import "./message";
|
import "./message";
|
||||||
@ -2,17 +2,16 @@ import path from "node:path";
|
|||||||
import { promises as fs } from "node:fs";
|
import { promises as fs } from "node:fs";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "../engine/registry";
|
||||||
import { RuntimeError } from "../engine/types";
|
import { RuntimeError } from "../engine/types";
|
||||||
|
import { defaultEngine } from "../engine/engine";
|
||||||
|
|
||||||
interface OcrWord { text: string; x: number; y: number; width: number; height: number; confidence: number }
|
interface OcrWord { text: string; x: number; y: number; width: number; height: number; confidence: number }
|
||||||
interface RapidOcrLine { text: string; mean: number; box?: number[][] }
|
interface RapidOcrLine { text: string; mean: number; box?: number[][] }
|
||||||
|
|
||||||
let ocrInstance: unknown = null;
|
|
||||||
|
|
||||||
async function getOcr(): Promise<{ detect: (image: string) => Promise<{ texts: RapidOcrLine[] }> }> {
|
async function getOcr(): Promise<{ detect: (image: string) => Promise<{ texts: RapidOcrLine[] }> }> {
|
||||||
if (ocrInstance) return ocrInstance as { detect: (image: string) => Promise<{ texts: RapidOcrLine[] }> };
|
if (defaultEngine.ocrInstance) return defaultEngine.ocrInstance as { detect: (image: string) => Promise<{ texts: RapidOcrLine[] }> };
|
||||||
const mod = await import("@gutenye/ocr-node");
|
const mod = await import("@gutenye/ocr-node");
|
||||||
const instance = await mod.default.create();
|
const instance = await mod.default.create();
|
||||||
ocrInstance = instance;
|
defaultEngine.ocrInstance = instance;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import "./ocr-caps";
|
import "./ocr-caps";
|
||||||
import "./remaining-core";
|
import "./remaining-core";
|
||||||
@ -2,9 +2,8 @@ import { readFile, mkdtemp, rm } from "node:fs/promises";
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import ExcelJS from "exceljs";
|
import ExcelJS from "exceljs";
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { Catalog, CatalogComponent, FlowNode } from "../../shared/types";
|
import type { Catalog, CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import "./office";
|
import "./office";
|
||||||
@ -21,7 +20,8 @@ afterEach(async () => Promise.all(dirs.splice(0).map((dir) => rm(dir, { recursiv
|
|||||||
|
|
||||||
describe("common Excel aliases", () => {
|
describe("common Excel aliases", () => {
|
||||||
it("registers only real catalog aliases", async () => {
|
it("registers only real catalog aliases", async () => {
|
||||||
const catalog = JSON.parse(await readFile(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../resources/catalog.json"), "utf8")) as Catalog;
|
// vitest 工作目录为仓库根,包内测试统一以 cwd 定位仓库资源
|
||||||
|
const catalog = JSON.parse(await readFile(path.resolve(process.cwd(), "resources/catalog.json"), "utf8")) as Catalog;
|
||||||
const ids = new Set(catalog.components.map((item) => item.id));
|
const ids = new Set(catalog.components.map((item) => item.id));
|
||||||
expect(EXCEL_COMMON_ALIASES.length).toBeGreaterThanOrEqual(25);
|
expect(EXCEL_COMMON_ALIASES.length).toBeGreaterThanOrEqual(25);
|
||||||
for (const id of EXCEL_COMMON_ALIASES) { expect(ids.has(id), id).toBe(true); expect(getHandler(id), id).toBeTruthy(); }
|
for (const id of EXCEL_COMMON_ALIASES) { expect(ids.has(id), id).toBe(true); expect(getHandler(id), id).toBeTruthy(); }
|
||||||
@ -4,7 +4,7 @@ import os from "node:os";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { PDFDocument } from "pdf-lib";
|
import { PDFDocument } from "pdf-lib";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import "./office-file-extended";
|
import "./office-file-extended";
|
||||||
@ -3,6 +3,7 @@ import { promises as fs } from "node:fs";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "../engine/registry";
|
||||||
import { RuntimeError, type CapabilityHandler } from "../engine/types";
|
import { RuntimeError, type CapabilityHandler } from "../engine/types";
|
||||||
|
import { defaultEngine } from "../engine/engine";
|
||||||
import { acquireWorkbook, publishWorkbook, releaseWorkbook } from "./excel-store";
|
import { acquireWorkbook, publishWorkbook, releaseWorkbook } from "./excel-store";
|
||||||
|
|
||||||
interface ExcelHandle {
|
interface ExcelHandle {
|
||||||
@ -10,7 +11,8 @@ interface ExcelHandle {
|
|||||||
path: string;
|
path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const workbooks = new Map<string, ExcelJS.Workbook>();
|
// 句柄 id → 工作簿;本体挂在引擎实例上(任务 1.3),此处委托 defaultEngine。
|
||||||
|
const workbooks = defaultEngine.excelHandles;
|
||||||
|
|
||||||
function value(args: Record<string, unknown>, keys: string[], fallback = ""): unknown {
|
function value(args: Record<string, unknown>, keys: string[], fallback = ""): unknown {
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
@ -5,7 +5,7 @@ import { afterEach, describe, expect, it } from "vitest";
|
|||||||
import { PDFDocument, StandardFonts } from "pdf-lib";
|
import { PDFDocument, StandardFonts } from "pdf-lib";
|
||||||
import { PNG } from "pngjs";
|
import { PNG } from "pngjs";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import "./pdf";
|
import "./pdf";
|
||||||
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import "./vision";
|
import "./vision";
|
||||||
import "./remaining-core";
|
import "./remaining-core";
|
||||||
@ -3,6 +3,8 @@ import path from "node:path";
|
|||||||
import { getHandler, register } from "../engine/registry";
|
import { getHandler, register } from "../engine/registry";
|
||||||
import { runFlow } from "../engine/runtime";
|
import { runFlow } from "../engine/runtime";
|
||||||
import { RuntimeError, type CapabilityHandler, type RunContext } from "../engine/types";
|
import { RuntimeError, type CapabilityHandler, type RunContext } from "../engine/types";
|
||||||
|
import { defaultEngine } from "../engine/engine";
|
||||||
|
import { getSubflowSource } from "../engine/host-bridge";
|
||||||
import { powershell, psString } from "./desktop";
|
import { powershell, psString } from "./desktop";
|
||||||
import { findImage } from "./vision";
|
import { findImage } from "./vision";
|
||||||
|
|
||||||
@ -24,7 +26,8 @@ function required(value: string, label: string, node: { id: string; componentId:
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbConnections = new Map<string, any>();
|
// 连接 id → 数据库连接;本体挂在引擎实例上(任务 1.3)。
|
||||||
|
const dbConnections = defaultEngine.dbConnections as Map<string, any>;
|
||||||
function dbConfig(args: Record<string, unknown>): Record<string, unknown> {
|
function dbConfig(args: Record<string, unknown>): Record<string, unknown> {
|
||||||
const inline = record(args.DatabaseConfig || args.config || args.connectionID);
|
const inline = record(args.DatabaseConfig || args.config || args.connectionID);
|
||||||
const merged = { ...inline, ...args };
|
const merged = { ...inline, ...args };
|
||||||
@ -67,12 +70,14 @@ register("Database_CloseDBConnect", async (_ctx, _node, args) => {
|
|||||||
dbConnections.delete(key); return { closed: true };
|
dbConnections.delete(key); return { closed: true };
|
||||||
});
|
});
|
||||||
async function callSubflow(ctx: RunContext, node: { id: string; componentId: string }, args: Record<string, unknown>) {
|
async function callSubflow(ctx: RunContext, node: { id: string; componentId: string }, args: Record<string, unknown>) {
|
||||||
const { listFlows, getFlow } = await import("../flows");
|
// 子流程仓库属宿主职责:经 host-bridge 注入(阶段 2 正式化),包内不 import "../flows"
|
||||||
|
const source = getSubflowSource();
|
||||||
|
if (!source) throw new RuntimeError("validation_error", "子流程源未注入", { nodeId: node.id, componentId: node.componentId, recommendation: "宿主需在启动时调用 setSubflowSource()" });
|
||||||
const wanted = text(args, ["module_name", "module", "flow"]);
|
const wanted = text(args, ["module_name", "module", "flow"]);
|
||||||
const rows = await listFlows(); const found = rows.find((row) => row.id === wanted || row.name === wanted);
|
const rows = await source.listFlows(); const found = rows.find((row) => row.id === wanted || row.name === wanted);
|
||||||
if (!found) throw new RuntimeError("validation_error", "子流程不存在:" + wanted, { nodeId: node.id, componentId: node.componentId });
|
if (!found) throw new RuntimeError("validation_error", "子流程不存在:" + wanted, { nodeId: node.id, componentId: node.componentId });
|
||||||
const { randomUUID } = await import("node:crypto");
|
const { randomUUID } = await import("node:crypto");
|
||||||
const child = await getFlow(found.id); const inputs = record(args.args || args.input);
|
const child = await source.getFlow(found.id); const inputs = record(args.args || args.input);
|
||||||
return await runFlow(child, { runId: randomUUID(), evidenceDir: ctx.evidenceDir, signal: ctx.signal, emit: ctx.emit, log: ctx.log, componentOf: ctx.componentOf, browser: ctx.browser, variables: { ...ctx.vars, ...inputs } });
|
return await runFlow(child, { runId: randomUUID(), evidenceDir: ctx.evidenceDir, signal: ctx.signal, emit: ctx.emit, log: ctx.log, componentOf: ctx.componentOf, browser: ctx.browser, variables: { ...ctx.vars, ...inputs } });
|
||||||
}
|
}
|
||||||
register("child_modules_v1", async (ctx, node, args) => callSubflow(ctx, node, args));
|
register("child_modules_v1", async (ctx, node, args) => callSubflow(ctx, node, args));
|
||||||
@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import "./system-extended";
|
import "./system-extended";
|
||||||
@ -2,7 +2,7 @@ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { Catalog, CatalogComponent, FlowDefinition, FlowNode } from "../../shared/types";
|
import type { Catalog, CatalogComponent, FlowDefinition, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import "./utility-extended";
|
import "./utility-extended";
|
||||||
@ -2,7 +2,7 @@ import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "../engine/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "../engine/registry";
|
||||||
import "./word";
|
import "./word";
|
||||||
49
packages/rpa-core/src/engine/engine.ts
Normal file
49
packages/rpa-core/src/engine/engine.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import type { Client } from "basic-ftp";
|
||||||
|
import type { Workbook as ExcelJSWorkbook } from "exceljs";
|
||||||
|
|
||||||
|
/** 人为拟真操作的开关与随机参数(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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEngine(): RpaEngine {
|
||||||
|
return new RpaEngine();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 能力模块内部使用的默认引擎实例(与全局注册表配套的进程级单例)。 */
|
||||||
|
export const defaultEngine = createEngine();
|
||||||
22
packages/rpa-core/src/engine/host-bridge.ts
Normal file
22
packages/rpa-core/src/engine/host-bridge.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import type { FlowDefinition } from "../types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 宿主桥(HostBridge 候选,阶段 2 正式化接口)。
|
||||||
|
*
|
||||||
|
* 包内个别能力需要宿主数据(目前仅子流程仓库),通过显式 setter 注入,
|
||||||
|
* 包本身不得 import 宿主模块。宿主启动时调用 setSubflowSource()。
|
||||||
|
*/
|
||||||
|
export interface SubflowSource {
|
||||||
|
listFlows(): Promise<Array<{ id: string; name: string }>>;
|
||||||
|
getFlow(id: string): Promise<FlowDefinition>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let subflowSource: SubflowSource | undefined;
|
||||||
|
|
||||||
|
export function setSubflowSource(source: SubflowSource): void {
|
||||||
|
subflowSource = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubflowSource(): SubflowSource | undefined {
|
||||||
|
return subflowSource;
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowDefinition } from "../../shared/types";
|
import type { CatalogComponent, FlowDefinition } from "../types";
|
||||||
import { register } from "./registry";
|
import { register } from "./registry";
|
||||||
import { runFlow } from "./runtime";
|
import { runFlow } from "./runtime";
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ import type {
|
|||||||
FlowEdge,
|
FlowEdge,
|
||||||
FlowNode,
|
FlowNode,
|
||||||
RunEvent
|
RunEvent
|
||||||
} from "../../shared/types";
|
} from "../types";
|
||||||
import "../capabilities";
|
import "../capabilities";
|
||||||
import { register } from "./registry";
|
import { register } from "./registry";
|
||||||
import { runFlow } from "./runtime";
|
import { runFlow } from "./runtime";
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import type { FlowDefinition, FlowNode, RunError } from "../../shared/types";
|
import type { FlowDefinition, FlowNode, RunError } from "../types";
|
||||||
import { evaluate, evaluateCondition, interpolate, toNumber } from "./expression";
|
import { evaluate, evaluateCondition, interpolate, toNumber } from "./expression";
|
||||||
import { getHandler } from "./registry";
|
import { getHandler } from "./registry";
|
||||||
import {
|
import {
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import type { CatalogComponent, FlowDefinition, FlowNode, RunError, RunEvent } from "../../shared/types";
|
import type { CatalogComponent, FlowDefinition, FlowNode, RunError, RunEvent } from "../types";
|
||||||
|
|
||||||
export interface BrowserLike {
|
export interface BrowserLike {
|
||||||
navigate(url: string): Promise<{ url: string; title: string }>;
|
navigate(url: string): Promise<{ url: string; title: string }>;
|
||||||
43
packages/rpa-core/src/index.ts
Normal file
43
packages/rpa-core/src/index.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* @rpa/core 包入口。
|
||||||
|
*
|
||||||
|
* 只导出引擎 API 与类型,不做能力注册副作用;
|
||||||
|
* 纯 Node 能力通过 `@rpa/core/capabilities` 子路径按需拉起(保持与 demo 宿主
|
||||||
|
* src/main/capabilities/index.ts 相同的副作用注册模型,见任务 4.2 再显式化)。
|
||||||
|
*/
|
||||||
|
export { createEngine, defaultEngine, RpaEngine, type ManualOperationSettings } from "./engine/engine";
|
||||||
|
export { register, getHandler, hasCapability, capabilityIds, capabilityCount } from "./engine/registry";
|
||||||
|
export { runFlow } from "./engine/runtime";
|
||||||
|
export { RuntimeError, BreakSignal, ContinueSignal, StopSignal, asRuntimeError } from "./engine/types";
|
||||||
|
export { setSubflowSource, getSubflowSource, type SubflowSource } from "./engine/host-bridge";
|
||||||
|
// PowerShell 助手(阶段 3 将移入 driver/windows/):宿主留守能力(window-extended 等)经此引用
|
||||||
|
export { powershell, psString } from "./capabilities/desktop";
|
||||||
|
export type {
|
||||||
|
BrowserLike,
|
||||||
|
BrowserTabInfo,
|
||||||
|
BrowserElementReference,
|
||||||
|
BrowserElementInfo,
|
||||||
|
BrowserSelectOption,
|
||||||
|
BrowserSelectState,
|
||||||
|
BrowserSimilarElementOptions,
|
||||||
|
BrowserElementRegion,
|
||||||
|
RunContext,
|
||||||
|
CapabilityHandler,
|
||||||
|
NodeState,
|
||||||
|
RunResult
|
||||||
|
} from "./engine/types";
|
||||||
|
export type {
|
||||||
|
CatalogWidget,
|
||||||
|
CatalogField,
|
||||||
|
CatalogOutput,
|
||||||
|
CatalogComponent,
|
||||||
|
CatalogGroup,
|
||||||
|
Catalog,
|
||||||
|
FlowVariable,
|
||||||
|
FlowNodeOptions,
|
||||||
|
FlowNode,
|
||||||
|
FlowEdge,
|
||||||
|
FlowDefinition,
|
||||||
|
RunError,
|
||||||
|
RunEvent
|
||||||
|
} from "./types";
|
||||||
187
packages/rpa-core/src/types.ts
Normal file
187
packages/rpa-core/src/types.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/**
|
||||||
|
* rpa-core 领域类型(任务 1.2,类型单一源)
|
||||||
|
*
|
||||||
|
* 从 src/shared/types 拆出:与流程/目录/运行相关、引擎必须依赖的模型归包所有。
|
||||||
|
* 宿主专属类型(AppInfo / AppSettings / FlowSummary / VisionLab* 等)留在 src/shared/types,
|
||||||
|
* 该文件通过 type-only re-export 引用本文件,保证全仓不出现两份定义。
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type CatalogWidget =
|
||||||
|
| "text"
|
||||||
|
| "textarea"
|
||||||
|
| "number"
|
||||||
|
| "select"
|
||||||
|
| "radio"
|
||||||
|
| "boolean"
|
||||||
|
| "file"
|
||||||
|
| "folder"
|
||||||
|
| "element"
|
||||||
|
| "code"
|
||||||
|
| "secret"
|
||||||
|
| "region"
|
||||||
|
| "hidden";
|
||||||
|
|
||||||
|
export interface CatalogField {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
widget: CatalogWidget;
|
||||||
|
type: "string" | "number" | "boolean" | "object";
|
||||||
|
displayType: string;
|
||||||
|
required: boolean;
|
||||||
|
defaultValue: string;
|
||||||
|
placeholder: string;
|
||||||
|
description: string;
|
||||||
|
advanced: boolean;
|
||||||
|
valueTypes: string[];
|
||||||
|
/** 下拉/单选选项:字符串(显示=存储值)或 { label, value }(显示与存储分离) */
|
||||||
|
options?: Array<string | { label: string; value: string }>;
|
||||||
|
unit?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CatalogOutput {
|
||||||
|
label: string;
|
||||||
|
key: string;
|
||||||
|
suggestedName: string;
|
||||||
|
valueTypes: string[];
|
||||||
|
fields: Array<{ key: string; label: string; defaultValue: string; valueTypes: string[] }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CatalogComponent {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
kind: "vendor" | "native" | "trigger";
|
||||||
|
group: string;
|
||||||
|
categories: string[];
|
||||||
|
categorySignature: string;
|
||||||
|
description: string;
|
||||||
|
messageTemplate: string;
|
||||||
|
baseType: string;
|
||||||
|
functionName: string;
|
||||||
|
functionCalls: Array<{ function: string; args: string[]; assignTo: string }>;
|
||||||
|
version: string;
|
||||||
|
tags: string[];
|
||||||
|
platformTag: string | null;
|
||||||
|
icon: string;
|
||||||
|
risk: "low" | "high";
|
||||||
|
fields: CatalogField[];
|
||||||
|
output: CatalogOutput;
|
||||||
|
slots: string[];
|
||||||
|
nodeKind?: string;
|
||||||
|
/** 同名旧版本 / 重复别名指向的保留能力 ID。 */
|
||||||
|
duplicateOf?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CatalogGroup {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
icon: string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Catalog {
|
||||||
|
schemaVersion: number;
|
||||||
|
generatedAt: string;
|
||||||
|
counts: {
|
||||||
|
total: number;
|
||||||
|
vendor: number;
|
||||||
|
native: number;
|
||||||
|
triggers: number;
|
||||||
|
fields: number;
|
||||||
|
};
|
||||||
|
groups: CatalogGroup[];
|
||||||
|
components: CatalogComponent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlowVariable {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
defaultValue?: string;
|
||||||
|
description?: string;
|
||||||
|
scope?: "flow" | "local";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlowNodeOptions {
|
||||||
|
outputVar?: string;
|
||||||
|
delayBeforeMs?: number;
|
||||||
|
delayAfterMs?: number;
|
||||||
|
timeoutMs?: number;
|
||||||
|
retryCount?: number;
|
||||||
|
retryIntervalMs?: number;
|
||||||
|
skipOnError?: boolean;
|
||||||
|
logException?: boolean;
|
||||||
|
errorRouteEnabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlowNode {
|
||||||
|
id: string;
|
||||||
|
componentId: string;
|
||||||
|
label?: string;
|
||||||
|
position: { x: number; y: number };
|
||||||
|
values: Record<string, unknown>;
|
||||||
|
options: FlowNodeOptions;
|
||||||
|
parentId?: string | null;
|
||||||
|
slot?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlowEdge {
|
||||||
|
id: string;
|
||||||
|
source: string;
|
||||||
|
target: string;
|
||||||
|
sourceHandle?: string | null;
|
||||||
|
targetHandle?: string | null;
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlowDefinition {
|
||||||
|
schemaVersion: 1;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
trigger?: {
|
||||||
|
type: "manual" | "schedule" | "file";
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
variables: FlowVariable[];
|
||||||
|
nodes: FlowNode[];
|
||||||
|
edges: FlowEdge[];
|
||||||
|
meta: {
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
appVersion: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RunError {
|
||||||
|
code: string;
|
||||||
|
reason: string;
|
||||||
|
recommendation?: string;
|
||||||
|
location?: { flowId?: string; nodeId?: string; componentId?: string; arg?: string };
|
||||||
|
detail?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RunEvent {
|
||||||
|
type:
|
||||||
|
| "run.started"
|
||||||
|
| "node.started"
|
||||||
|
| "node.log"
|
||||||
|
| "node.finished"
|
||||||
|
| "run.finished"
|
||||||
|
| "run.total";
|
||||||
|
runId: string;
|
||||||
|
nodeId?: string;
|
||||||
|
level?: "info" | "warn" | "error";
|
||||||
|
message?: string;
|
||||||
|
status?: string;
|
||||||
|
output?: unknown;
|
||||||
|
error?: RunError;
|
||||||
|
durationMs?: number;
|
||||||
|
evidence?: string[];
|
||||||
|
attempt?: number;
|
||||||
|
startedAt?: string;
|
||||||
|
nodeLabel?: string;
|
||||||
|
total?: number;
|
||||||
|
}
|
||||||
16
packages/rpa-core/tsconfig.json
Normal file
16
packages/rpa-core/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts"]
|
||||||
|
}
|
||||||
8
packages/rpa-core/vitest.config.ts
Normal file
8
packages/rpa-core/vitest.config.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
// 与根配置一致:桌面自动化冒烟测试共用同一物理桌面,串行执行防焦点竞争。
|
||||||
|
fileParallelism: false
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"generatedAt": "2026-11-04T03:53:23.689Z",
|
"generatedAt": "2026-11-04T04:34:06.782Z",
|
||||||
"count": 909,
|
"count": 909,
|
||||||
"ids": [
|
"ids": [
|
||||||
"App_GetPid",
|
"App_GetPid",
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import { afterEach, describe, expect, it } from "vitest";
|
|||||||
import { buildSuite, bundleRoot } from "./app-system-test-flows.mjs";
|
import { buildSuite, bundleRoot } from "./app-system-test-flows.mjs";
|
||||||
import { installBundle, upgradeBundle, makeReport, caseResults } from "./app-system-tests.mjs";
|
import { installBundle, upgradeBundle, makeReport, caseResults } from "./app-system-tests.mjs";
|
||||||
import { applyCatalogOverrides } from "./catalog-overrides.mjs";
|
import { applyCatalogOverrides } from "./catalog-overrides.mjs";
|
||||||
import { hasCapability } from "../src/main/capabilities/index.ts";
|
import { hasCapability, runFlow } from "@rpa/core";
|
||||||
import { runFlow } from "../src/main/engine/runtime.ts";
|
import "../src/main/capabilities/index.ts";
|
||||||
import { validateFlow } from "../src/renderer/src/validation/flowValidation.ts";
|
import { validateFlow } from "../src/renderer/src/validation/flowValidation.ts";
|
||||||
|
|
||||||
const containers = new Set(["logic_control_try_catch", "logic_control_if_else"]);
|
const containers = new Set(["logic_control_try_catch", "logic_control_if_else"]);
|
||||||
@ -58,7 +58,7 @@ describe("应用与系统测试流程包", () => {
|
|||||||
const options = component.fields.find(field => field.key === "into_type").options;
|
const options = component.fields.find(field => field.key === "into_type").options;
|
||||||
expect(options.length).toBeGreaterThanOrEqual(4);
|
expect(options.length).toBeGreaterThanOrEqual(4);
|
||||||
for (const option of options) {
|
for (const option of options) {
|
||||||
const output = await import("../src/main/engine/registry.ts").then(({ getHandler }) => getHandler(id)({}, { id: "type-test", componentId: id }, { val: 42, into_type: option.value }, component));
|
const output = await import("@rpa/core").then(({ getHandler }) => getHandler(id)({}, { id: "type-test", componentId: id }, { val: 42, into_type: option.value }, component));
|
||||||
if (id === "Basic_JudgeDataType") expect(typeof output).toBe("boolean");
|
if (id === "Basic_JudgeDataType") expect(typeof output).toBe("boolean");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { execFile, spawn, type ChildProcess } from "node:child_process";
|
|||||||
import { promises as fs } from "node:fs";
|
import { promises as fs } from "node:fs";
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { RuntimeError } from "../engine/types";
|
import { RuntimeError } from "@rpa/core";
|
||||||
import { ExternalTransport } from "./transport";
|
import { ExternalTransport } from "./transport";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import {
|
|||||||
type BrowserSelectState,
|
type BrowserSelectState,
|
||||||
type BrowserSimilarElementOptions,
|
type BrowserSimilarElementOptions,
|
||||||
type BrowserTabInfo
|
type BrowserTabInfo
|
||||||
} from "../engine/types";
|
} from "@rpa/core";
|
||||||
|
|
||||||
function assertUrl(raw: string): URL {
|
function assertUrl(raw: string): URL {
|
||||||
let url: URL;
|
let url: URL;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { BrowserWindow } from "electron";
|
import type { BrowserWindow } from "electron";
|
||||||
import WebSocket from "ws";
|
import WebSocket from "ws";
|
||||||
import { RuntimeError } from "../engine/types";
|
import { RuntimeError } from "@rpa/core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CDP 传输层抽象。
|
* CDP 传输层抽象。
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { register } from "../engine/registry";
|
import { register } from "@rpa/core";
|
||||||
import { RuntimeError } from "../engine/types";
|
import { RuntimeError } from "@rpa/core";
|
||||||
import { chatCompletion, requireAiConfig } from "./ai-service";
|
import { chatCompletion, requireAiConfig } from "./ai-service";
|
||||||
|
|
||||||
function inputText(args: Record<string, unknown>, nodeId: string): string {
|
function inputText(args: Record<string, unknown>, nodeId: string): string {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { loadSettings } from "../settings";
|
import { loadSettings } from "../settings";
|
||||||
import { RuntimeError } from "../engine/types";
|
import { RuntimeError } from "@rpa/core";
|
||||||
|
|
||||||
export type ChatContentPart =
|
export type ChatContentPart =
|
||||||
| { type: "text"; text: string }
|
| { type: "text"; text: string }
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { readFileSync } from "node:fs";
|
|||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "@rpa/core";
|
||||||
import {
|
import {
|
||||||
RuntimeError,
|
RuntimeError,
|
||||||
type BrowserElementInfo,
|
type BrowserElementInfo,
|
||||||
@ -10,7 +10,7 @@ import {
|
|||||||
type BrowserLike,
|
type BrowserLike,
|
||||||
type BrowserSelectState,
|
type BrowserSelectState,
|
||||||
type RunContext
|
type RunContext
|
||||||
} from "../engine/types";
|
} from "@rpa/core";
|
||||||
import { BROWSER_COMMON_IDS } from "./browser-common";
|
import { BROWSER_COMMON_IDS } from "./browser-common";
|
||||||
|
|
||||||
const component = {} as CatalogComponent;
|
const component = {} as CatalogComponent;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { browserSession } from "../browser/session";
|
import { browserSession } from "../browser/session";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "@rpa/core";
|
||||||
import { RuntimeError, type BrowserElementInfo, type BrowserLike, type RunContext } from "../engine/types";
|
import { RuntimeError, type BrowserElementInfo, type BrowserLike, type RunContext } from "@rpa/core";
|
||||||
|
|
||||||
const GET_ELEMENT_IDS = ["Element_GetElement", "Element_GetElementV1", "Element_GetElementV2", "Element_GetElementNew"];
|
const GET_ELEMENT_IDS = ["Element_GetElement", "Element_GetElementV1", "Element_GetElementV2", "Element_GetElementNew"];
|
||||||
const GET_INFO_IDS = ["Element_GetElementInfo", "Element_GetElementInfo_V1"];
|
const GET_INFO_IDS = ["Element_GetElementInfo", "Element_GetElementInfo_V1"];
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { readFileSync } from "node:fs";
|
|||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
||||||
import type { BrowserLike, RunContext } from "../engine/types";
|
import type { BrowserLike, RunContext } from "@rpa/core";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "@rpa/core";
|
||||||
import { BROWSER_CONTROL_IDS } from "./browser-control";
|
import { BROWSER_CONTROL_IDS } from "./browser-control";
|
||||||
|
|
||||||
const component = {} as CatalogComponent; const node = (id: string): FlowNode => ({ id, componentId: id, position: { x: 0, y: 0 }, values: {}, options: {} });
|
const component = {} as CatalogComponent; const node = (id: string): FlowNode => ({ id, componentId: id, position: { x: 0, y: 0 }, values: {}, options: {} });
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { promises as fs } from "node:fs";
|
import { promises as fs } from "node:fs";
|
||||||
import { browserSession } from "../browser/session";
|
import { browserSession } from "../browser/session";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "@rpa/core";
|
||||||
import { RuntimeError, type BrowserLike, type RunContext } from "../engine/types";
|
import { RuntimeError, type BrowserLike, type RunContext } from "@rpa/core";
|
||||||
|
|
||||||
async function session(ctx: RunContext): Promise<BrowserLike> { if (!ctx.browser) ctx.browser = await browserSession(); return ctx.browser; }
|
async function session(ctx: RunContext): Promise<BrowserLike> { if (!ctx.browser) ctx.browser = await browserSession(); return ctx.browser; }
|
||||||
function selector(args: Record<string, unknown>): unknown { const value = args.uiSelector ?? args.download_selector ?? args.selector; if (value === undefined || value === null || value === "") throw new RuntimeError("validation_error", "元素选择器为空"); return value; }
|
function selector(args: Record<string, unknown>): unknown { const value = args.uiSelector ?? args.download_selector ?? args.selector; if (value === undefined || value === null || value === "") throw new RuntimeError("validation_error", "元素选择器为空"); return value; }
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { readFileSync } from "node:fs";
|
|||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "@rpa/core";
|
||||||
import { RuntimeError, type BrowserElementRegion, type BrowserLike, type RunContext } from "../engine/types";
|
import { RuntimeError, type BrowserElementRegion, type BrowserLike, type RunContext } from "@rpa/core";
|
||||||
import { BROWSER_DOM_IDS } from "./browser-dom";
|
import { BROWSER_DOM_IDS } from "./browser-dom";
|
||||||
|
|
||||||
const component = {} as CatalogComponent;
|
const component = {} as CatalogComponent;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { browserSession } from "../browser/session";
|
import { browserSession } from "../browser/session";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "@rpa/core";
|
||||||
import { RuntimeError, type BrowserLike, type RunContext } from "../engine/types";
|
import { RuntimeError, type BrowserLike, type RunContext } from "@rpa/core";
|
||||||
|
|
||||||
export const BROWSER_DOM_IDS = [
|
export const BROWSER_DOM_IDS = [
|
||||||
"Element_GetAttribute", "Element_GetAttribute_v1", "Element_GetAttribute_v2", "Element_GetAttribute_v3",
|
"Element_GetAttribute", "Element_GetAttribute_v1", "Element_GetAttribute_v2", "Element_GetAttribute_v3",
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { readFileSync } from "node:fs";
|
|||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "@rpa/core";
|
||||||
import { RuntimeError, type BrowserLike, type RunContext } from "../engine/types";
|
import { RuntimeError, type BrowserLike, type RunContext } from "@rpa/core";
|
||||||
import "./browser-extended";
|
import "./browser-extended";
|
||||||
|
|
||||||
const EXTENDED_IDS = [
|
const EXTENDED_IDS = [
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { browserSession } from "../browser/session";
|
import { browserSession } from "../browser/session";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "@rpa/core";
|
||||||
import { RuntimeError, type BrowserLike, type RunContext } from "../engine/types";
|
import { RuntimeError, type BrowserLike, type RunContext } from "@rpa/core";
|
||||||
|
|
||||||
const CLICK_IDS = ["Element_Click_V1", "Element_Click_V2", "Element_Click_V4", "Element_Click_V5"];
|
const CLICK_IDS = ["Element_Click_V1", "Element_Click_V2", "Element_Click_V4", "Element_Click_V5"];
|
||||||
const TEXT_INPUT_IDS = ["Element_SetValue_V1", "Element_SetValue_V2", "Element_SetValue_V3", "Element_SetValue_V5"];
|
const TEXT_INPUT_IDS = ["Element_SetValue_V1", "Element_SetValue_V2", "Element_SetValue_V3", "Element_SetValue_V5"];
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "@rpa/core";
|
||||||
import { RuntimeError, type BrowserLike, type RunContext } from "../engine/types";
|
import { RuntimeError, type BrowserLike, type RunContext } from "@rpa/core";
|
||||||
import "./browser";
|
import "./browser";
|
||||||
|
|
||||||
const component = {} as CatalogComponent;
|
const component = {} as CatalogComponent;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { register } from "../engine/registry";
|
import { register } from "@rpa/core";
|
||||||
import { RuntimeError } from "../engine/types";
|
import { RuntimeError } from "@rpa/core";
|
||||||
import { browserSession, externalBrowserSession } from "../browser/session";
|
import { browserSession, externalBrowserSession } from "../browser/session";
|
||||||
import type { BrowserLike, RunContext } from "../engine/types";
|
import type { BrowserLike, RunContext } from "@rpa/core";
|
||||||
|
|
||||||
async function session(ctx: RunContext): Promise<BrowserLike> {
|
async function session(ctx: RunContext): Promise<BrowserLike> {
|
||||||
if (!ctx.browser) ctx.browser = await browserSession();
|
if (!ctx.browser) ctx.browser = await browserSession();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { readFile } from "node:fs/promises";
|
import { readFile } from "node:fs/promises";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import type { Catalog } from "../../shared/types";
|
import type { Catalog } from "../../shared/types";
|
||||||
import { hasCapability } from "../engine/registry";
|
import { hasCapability } from "@rpa/core";
|
||||||
import "./index";
|
import "./index";
|
||||||
|
|
||||||
const planned = [
|
const planned = [
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { Catalog, CatalogComponent, CatalogField } from "../../shared/types";
|
import type { Catalog, CatalogComponent, CatalogField } from "../../shared/types";
|
||||||
import { readFile } from "node:fs/promises";
|
import { readFile } from "node:fs/promises";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { hasCapability } from "../engine/registry";
|
import { hasCapability } from "@rpa/core";
|
||||||
import "./index";
|
import "./index";
|
||||||
|
|
||||||
const widgets = new Set(["text","textarea","number","select","radio","boolean","file","folder","element","code","secret","region","hidden"]);
|
const widgets = new Set(["text","textarea","number","select","radio","boolean","file","folder","element","code","secret","region","hidden"]);
|
||||||
|
|||||||
@ -5,9 +5,9 @@ import path from "node:path";
|
|||||||
import { PDFDocument } from "pdf-lib";
|
import { PDFDocument } from "pdf-lib";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "@rpa/core";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "@rpa/core";
|
||||||
import "./desktop"; import "./pdf"; import "./system-extended"; import "./window-extended"; import "./common-final";
|
import "@rpa/core/capabilities"; import "./window-extended";
|
||||||
|
|
||||||
const dirs: string[] = []; const context = { vars: {} } as RunContext; const component = {} as CatalogComponent;
|
const dirs: string[] = []; const context = { vars: {} } as RunContext; const component = {} as CatalogComponent;
|
||||||
const node = (id: string): FlowNode => ({ id, componentId: id, position: { x: 0, y: 0 }, values: {}, options: {} });
|
const node = (id: string): FlowNode => ({ id, componentId: id, position: { x: 0, y: 0 }, values: {}, options: {} });
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { BrowserWindow } from "electron";
|
import { BrowserWindow } from "electron";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "@rpa/core";
|
||||||
import { RuntimeError } from "../engine/types";
|
import { RuntimeError } from "@rpa/core";
|
||||||
|
|
||||||
interface FormFieldSpec { key?: string; name?: string; label?: string; type?: string; value?: string; options?: unknown[] }
|
interface FormFieldSpec { key?: string; name?: string; label?: string; type?: string; value?: string; options?: unknown[] }
|
||||||
|
|
||||||
|
|||||||
@ -1,44 +1,21 @@
|
|||||||
import "./control";
|
/**
|
||||||
import "./files";
|
* 宿主能力注册入口。
|
||||||
import "./http";
|
*
|
||||||
import "./data";
|
* 纯 Node 能力已迁入 @rpa/core(packages/rpa-core),第一行 import 拉起包内全部注册;
|
||||||
|
* 此处只保留依赖 Electron / 宿主模块的能力,阶段 2 通过 HostBridge 逐步迁入。
|
||||||
|
* 导出顺序整体保持改造前 index.ts 的注册顺序(别名/重复 id last-wins 语义不变)。
|
||||||
|
*/
|
||||||
|
import "@rpa/core/capabilities";
|
||||||
import "./browser";
|
import "./browser";
|
||||||
import "./browser-extended";
|
import "./browser-extended";
|
||||||
import "./browser-dom";
|
import "./browser-dom";
|
||||||
import "./browser-common";
|
import "./browser-common";
|
||||||
import "./browser-control";
|
import "./browser-control";
|
||||||
import "./office";
|
|
||||||
import "./desktop";
|
|
||||||
import "./desktop-uia";
|
|
||||||
import "./web-batch2";
|
import "./web-batch2";
|
||||||
import "./web-crawl";
|
import "./web-crawl";
|
||||||
import "./human-dialog";
|
import "./human-dialog";
|
||||||
import "./time-caps";
|
|
||||||
import "./windows-system";
|
|
||||||
import "./ftp";
|
|
||||||
import "./ai-service";
|
import "./ai-service";
|
||||||
import "./ai-caps";
|
import "./ai-caps";
|
||||||
import "./ocr-caps";
|
|
||||||
import "./pdf";
|
|
||||||
import "./word";
|
|
||||||
import "./word-extended";
|
|
||||||
import "./message";
|
|
||||||
import "./vision";
|
|
||||||
import "./cv-vision";
|
|
||||||
import "./extended";
|
|
||||||
import "./system-extended";
|
|
||||||
import "./office-file-extended";
|
|
||||||
import "./office-aliases";
|
|
||||||
import "./excel-common";
|
|
||||||
import "./excel-com";
|
|
||||||
import "./window-extended";
|
import "./window-extended";
|
||||||
import "./data-extended";
|
|
||||||
import "./utility-extended";
|
|
||||||
import "./data-table-common";
|
|
||||||
import "./basic-encryption";
|
|
||||||
import "./mail-remaining";
|
|
||||||
import "./remaining-core";
|
|
||||||
import "./common-final";
|
|
||||||
|
|
||||||
export { capabilityCount, capabilityIds, getHandler, hasCapability, register } from "../engine/registry";
|
|
||||||
|
|
||||||
|
export { capabilityCount, capabilityIds, getHandler, hasCapability, register } from "@rpa/core";
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { execFile } from "node:child_process";
|
|||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "@rpa/core";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "@rpa/core";
|
||||||
import "./window-extended";
|
import "./window-extended";
|
||||||
|
|
||||||
const run = promisify(execFile);
|
const run = promisify(execFile);
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
import { createServer, type IncomingMessage, type ServerResponse, type Server } from "node:http";
|
import { createServer, type IncomingMessage, type ServerResponse, type Server } from "node:http";
|
||||||
import net from "node:net";
|
import net from "node:net";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getHandler } from "../engine/registry";
|
import { getHandler } from "@rpa/core";
|
||||||
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
import type { CatalogComponent, FlowNode } from "../../shared/types";
|
||||||
import type { RunContext } from "../engine/types";
|
import type { RunContext } from "@rpa/core";
|
||||||
import "./http";
|
import "@rpa/core/capabilities";
|
||||||
import "./message";
|
|
||||||
import "./common-final";
|
|
||||||
import "./ai-caps";
|
import "./ai-caps";
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { readFile } from "node:fs/promises";
|
import { readFile } from "node:fs/promises";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { capabilityCount, hasCapability } from "../engine/registry";
|
import { capabilityCount, hasCapability } from "@rpa/core";
|
||||||
import "./index";
|
import "./index";
|
||||||
|
|
||||||
const selected = [
|
const selected = [
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { browserSession } from "../browser/session";
|
import { browserSession } from "../browser/session";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "@rpa/core";
|
||||||
import { RuntimeError, type BrowserLike, type BrowserTabInfo, type RunContext } from "../engine/types";
|
import { RuntimeError, type BrowserLike, type BrowserTabInfo, type RunContext } from "@rpa/core";
|
||||||
|
|
||||||
/** 优先使用流程中「打开网页」已建立的浏览器会话(含外部 Edge/Chrome),否则退回内置 */
|
/** 优先使用流程中「打开网页」已建立的浏览器会话(含外部 Edge/Chrome),否则退回内置 */
|
||||||
async function currentSession(ctx: RunContext): Promise<BrowserLike> {
|
async function currentSession(ctx: RunContext): Promise<BrowserLike> {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { browserSession, externalBrowserSession } from "../browser/session";
|
import { browserSession, externalBrowserSession } from "../browser/session";
|
||||||
import { register } from "../engine/registry";
|
import { register } from "@rpa/core";
|
||||||
import { RuntimeError, type BrowserLike, type RunContext } from "../engine/types";
|
import { RuntimeError, type BrowserLike, type RunContext } from "@rpa/core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 采集/监听组件的浏览器会话解析:
|
* 采集/监听组件的浏览器会话解析:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user