阶段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被拒, 环境阻塞)
这个提交包含在:
1 父节点
4139400dbd
当前提交
caa8e19332
111 个文件已更改
+581
-337
没有与搜索条件相匹配的文件
@@ -0,0 +1,50 @@
|
||||
import { Buffer } from "node:buffer";
|
||||
import { register } from "../engine/registry";
|
||||
import { RuntimeError, type CapabilityHandler } from "../engine/types";
|
||||
|
||||
const SM4_KEY = "INPUT_PARAM_KEY4";
|
||||
const SM4_KEY_HEX = Buffer.from(SM4_KEY, "utf8").toString("hex");
|
||||
|
||||
function looksLikeBase64(value: string): boolean {
|
||||
const text = value.trim().replace(/\s+/g, "");
|
||||
if (!text || text.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(text)) return false;
|
||||
return Buffer.from(text, "base64").toString("base64").replace(/=+$/, "") === text.replace(/=+$/, "");
|
||||
}
|
||||
|
||||
function readableUtf8(value: string): boolean {
|
||||
if (!value) return false;
|
||||
let controls = 0;
|
||||
for (const char of value) {
|
||||
const code = char.codePointAt(0)!;
|
||||
if (code < 32 && !["\n", "\r", "\t"].includes(char)) controls += 1;
|
||||
if (code === 0xfffd) controls += 10;
|
||||
}
|
||||
return controls === 0;
|
||||
}
|
||||
|
||||
async function sm4Decrypt(value: string): Promise<string> {
|
||||
const { sm4 } = await import("sm-crypto");
|
||||
const errors: string[] = [];
|
||||
for (const padding of ["pkcs#7", "none"] as const) {
|
||||
try {
|
||||
const output = sm4.decrypt(value.toLowerCase(), SM4_KEY_HEX, { padding });
|
||||
if (output && readableUtf8(output)) return output;
|
||||
errors.push(`${padding}: empty or non-text output`);
|
||||
} catch (error) {
|
||||
errors.push(`${padding}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
throw new RuntimeError("decryption_failed", "SM4 密文解密失败", { detail: errors.join("; ") });
|
||||
}
|
||||
|
||||
const dodecryption: CapabilityHandler = async (_ctx, node, args) => {
|
||||
const words = String(args.words ?? args.value ?? args.text ?? "");
|
||||
if (!words.trim()) throw new RuntimeError("validation_error", "密文内容为空", { nodeId: node.id, componentId: node.componentId });
|
||||
if (looksLikeBase64(words)) {
|
||||
const decoded = Buffer.from(words, "base64").toString("utf8");
|
||||
if (readableUtf8(decoded)) return decoded;
|
||||
}
|
||||
return sm4Decrypt(words.trim());
|
||||
};
|
||||
|
||||
register("Basic_Dodecryption", dodecryption);
|
||||
在新工单中引用
屏蔽一个用户