feat: 平台标记单一源 PLATFORM_GROUPS + catalog 重生成 platformTag 生效

迁移前置项(指南五件小事 #1 + 第 0 步作业 #2):
- register-all.ts 新增 PLATFORM_GROUPS 单一权威表(33 域全标 win32,darwin/linux 仅类型占位)
- registerAll 逐域加载时把平台默认值落进 meta.platforms(能力显式声明优先);
  新增 buildDomainPlatformMap 供构建脚本与运行时共用同一张表
- build-catalog.mjs 删除 WIN32_GROUPS 硬编码,platformTag 从包内分组表派生(改 tsx 运行)
- 守卫测试 platform-groups.test.ts:域无缺行 / 不偷填非 win32 数据 / meta 派生一致
- catalog.json 重生成:687 个组件获得 platformTag=win32(此前从未生效)
- 源数据恢复:对面重构删了 shizai-catalog-data.js,从其历史 commit 7bc627c8 恢复到
  temp/catalog-source/(不入库)并作为回退路径
- catalog-overrides 增强 addFields 支持;把此前直接改 catalog.json 的 96 处假必填修正、
  label 修正、Screenlock 字段清理、12 个实现读取但源数据缺失的字段全部沉淀进 overrides,
  重生成后参数审计 A/B 类保持 0/0
This commit is contained in:
yu 2026-11-05 17:53:07 +08:00
parent a7709aefbb
commit 8414c4f355
9 changed files with 1002 additions and 125520 deletions

View File

@ -16,7 +16,7 @@
"typecheck": "tsc --noEmit -p tsconfig.json",
"check:deps": "node packages/rpa-core/scripts/check-deps.mjs",
"rpa-cli": "tsx scripts/rpa-cli.ts",
"catalog": "node scripts/build-catalog.mjs",
"catalog": "tsx scripts/build-catalog.mjs",
"snapshot": "vitest run scripts/capability-snapshot.test.ts",
"package": "node scripts/package.mjs",
"test": "vitest run",

View File

@ -0,0 +1,37 @@
import { describe, expect, it } from "vitest";
import { capabilityCount, getCapabilityMeta } from "../engine/registry";
import {
PLATFORM_GROUPS,
buildDomainPlatformMap,
capabilityDomains
} from "./register-all";
describe("PLATFORM_GROUPS 平台单一源(迁移指南五件小事 #1)", () => {
it("每个能力域都有平台行,不允许缺行", () => {
for (const domain of capabilityDomains) {
expect(PLATFORM_GROUPS[domain], `域 ${domain} 缺少 PLATFORM_GROUPS 行`).toBeDefined();
}
expect(Object.keys(PLATFORM_GROUPS).sort()).toEqual([...capabilityDomains].sort());
});
it("现阶段数据只允许 win32,darwin/linux 仅类型占位不填数据", () => {
for (const [domain, platforms] of Object.entries(PLATFORM_GROUPS)) {
expect(
platforms.every((p) => p === "win32"),
`域 ${domain} 填了 win32 以外的平台数据`
).toBe(true);
}
});
it("registerAll 后所有能力 meta.platforms 与分组表一致(运行时派生)", async () => {
// buildDomainPlatformMap 逐域加载(动态 import 有缓存,必须在 registerAll 语义内一次完成)
const map = await buildDomainPlatformMap();
expect(capabilityCount()).toBeGreaterThan(700);
expect(map.size).toBe(capabilityCount());
for (const id of map.keys()) {
const meta = getCapabilityMeta(id);
expect(meta, `能力 ${id} 无 meta`).toBeTruthy();
expect(meta!.platforms, `能力 ${id} 的 meta.platforms 未从分组表派生`).toEqual(["win32"]);
}
});
});

View File

@ -1,4 +1,53 @@
import type { RpaEngine } from "../engine/engine";
import { applyPlatformDefaults, capabilityIds, type CapabilityPlatform } from "../engine/registry";
export type { CapabilityPlatform } from "../engine/registry";
/**
* 平台归属单一权威表(迁移指南「五件小事 #1」,dev-conventions 规则 4)。
*
* - 运行时 `meta.platforms` 与构建期 catalog `platformTag` 都从这张表派生,
* 任何代码不得自行声明第二份平台数据(守卫测试 platform-groups.test.ts 把关)。
* - 现阶段所有能力只在 Windows 交互会话验证过,全部标 `["win32"]`;
* `darwin`/`linux` 仅类型占位,不填数据。将来支持新平台 = 对应行加平台,仅此而已。
* - 后续若某域内出现平台混装(如 remaining-core 里混着 PS 专用能力),
* 先把域拆细,再在能力 meta.platforms 里显式声明更窄限制。
*/
export const PLATFORM_GROUPS: Record<string, CapabilityPlatform[]> = {
control: ["win32"],
files: ["win32"],
http: ["win32"],
data: ["win32"],
office: ["win32"],
desktop: ["win32"],
desktopUia: ["win32"],
time: ["win32"],
windowsSystem: ["win32"],
ftp: ["win32"],
ocr: ["win32"],
pdf: ["win32"],
word: ["win32"],
wordExtended: ["win32"],
message: ["win32"],
vision: ["win32"],
cvVision: ["win32"],
extended: ["win32"],
systemExtended: ["win32"],
officeFileExtended: ["win32"],
officeAliases: ["win32"],
excelCommon: ["win32"],
excelCom: ["win32"],
dataExtended: ["win32"],
utilityExtended: ["win32"],
dataTableCommon: ["win32"],
basicEncryption: ["win32"],
mailRemaining: ["win32"],
remainingCore: ["win32"],
commonFinal: ["win32"],
humanDialog: ["win32"],
aiService: ["win32"],
windowExtended: ["win32"]
};
/**
* 显式注册入口(任务 4.2,决策点 9:双入口过渡)。
@ -46,14 +95,35 @@ const DOMAINS: Record<string, () => Promise<unknown>> = {
windowExtended: () => import("./window-extended")
};
async function loadDomainWithPlatformDefaults(domain: string): Promise<Set<string>> {
const load = DOMAINS[domain];
if (!load) throw new Error(`未知能力域:${domain}(可选:${Object.keys(DOMAINS).join(", ")})`);
const before = new Set(capabilityIds());
await load();
const fresh = [...capabilityIds()].filter((id) => !before.has(id));
// 平台默认值从 PLATFORM_GROUPS 派生(单一源);能力显式声明的 platforms 优先
applyPlatformDefaults(fresh, PLATFORM_GROUPS[domain] ?? []);
return new Set(fresh);
}
export async function registerAll(engine: RpaEngine, groups?: string[]): Promise<RpaEngine> {
const wanted = groups && groups.length ? groups : Object.keys(DOMAINS);
for (const group of wanted) {
const load = DOMAINS[group];
if (!load) throw new Error(`未知能力域:${group}(可选:${Object.keys(DOMAINS).join(", ")})`);
await load();
}
for (const group of wanted) await loadDomainWithPlatformDefaults(group);
return engine;
}
/**
* 逐域加载并返回「能力 id → 平台数组」映射(副作用:完成全量注册)。
* 供构建脚本(build-catalog)派生 catalog platformTag,与运行时同一张表。
*/
export async function buildDomainPlatformMap(): Promise<Map<string, CapabilityPlatform[]>> {
const map = new Map<string, CapabilityPlatform[]>();
for (const domain of Object.keys(DOMAINS)) {
const fresh = await loadDomainWithPlatformDefaults(domain);
const platforms = PLATFORM_GROUPS[domain] ?? [];
for (const id of fresh) map.set(id, platforms);
}
return map;
}
export const capabilityDomains = Object.keys(DOMAINS);

View File

@ -13,6 +13,9 @@ export interface CapabilityParamMeta {
description?: string;
}
/** 平台类型;现阶段只有 win32 有实现,darwin/linux 仅类型占位(dev-conventions 规则 4) */
export type CapabilityPlatform = "win32" | "darwin" | "linux";
export interface CapabilityMeta {
/** 一句话能力摘要(中文) */
summary?: string;
@ -27,7 +30,7 @@ export interface CapabilityMeta {
/** 执行中是否需要回传截图(Agent 侧视觉判断用) */
needsScreenshot?: boolean;
/** 平台限制;省略表示跨平台 */
platforms?: Array<"win32">;
platforms?: CapabilityPlatform[];
}
const handlers = new Map<string, CapabilityHandler>();
@ -60,3 +63,21 @@ export function capabilityIds(): string[] {
export function capabilityCount(): number {
return handlers.size;
}
/**
* 给一批能力落平台默认值(来自 register-all.ts 的 PLATFORM_GROUPS 单一权威表)。
* - 空数组 = 平台无限制,不落 meta;
* - 已有 meta 且已声明 platforms 的不覆盖(能力可显式声明更窄的平台限制);
* - 无 meta 的补一个只含 platforms 的 meta。
*/
export function applyPlatformDefaults(ids: string[], platforms: CapabilityPlatform[]): void {
if (!platforms.length) return;
for (const id of ids) {
const meta = metas.get(id);
if (meta) {
if (!meta.platforms) metas.set(id, { ...meta, platforms: [...platforms] });
} else {
metas.set(id, { platforms: [...platforms] });
}
}
}

View File

@ -1,5 +1,5 @@
{
"generatedAt": "2026-11-04T09:12:00.449Z",
"generatedAt": "2026-11-05T09:52:08.862Z",
"count": 909,
"ids": [
"App_GetPid",

View File

@ -176,7 +176,25 @@
"cvSelector": {
"description": "屏幕上可见的目标文本,运行时截屏后通过 OCR 定位(填按钮或界面上的文字,如「登录」)"
}
}
},
"removeFields": [
"searchRegion"
],
"addFields": [
{
"key": "searchRegion",
"label": "搜索区域",
"widget": "region",
"type": "object",
"displayType": "region",
"required": false,
"defaultValue": "",
"placeholder": "",
"description": "可选。点「屏幕框选」框住目标所在区域,运行时只在该区域内查找目标文本,屏幕上有相同文字时可避免点错;留空则全屏查找",
"advanced": false,
"valueTypes": []
}
]
},
"CV_SetText": {
"fields": {
@ -187,7 +205,25 @@
"description": "填写参数值,支持 ${变量} 引用上游输出",
"placeholder": "输入内容或 ${变量}"
}
}
},
"removeFields": [
"searchRegion"
],
"addFields": [
{
"key": "searchRegion",
"label": "搜索区域",
"widget": "region",
"type": "object",
"displayType": "region",
"required": false,
"defaultValue": "",
"placeholder": "",
"description": "可选。点「屏幕框选」框住目标所在区域,运行时只在该区域内查找目标文本,屏幕上有相同文字时可避免点错;留空则全屏查找",
"advanced": false,
"valueTypes": []
}
]
},
"CV_MouseHover": {
"fields": {
@ -200,7 +236,25 @@
"offsetY": {
"description": "相对目标中心的垂直偏移像素,正数向下、负数向上,0 为正中"
}
}
},
"removeFields": [
"searchRegion"
],
"addFields": [
{
"key": "searchRegion",
"label": "搜索区域",
"widget": "region",
"type": "object",
"displayType": "region",
"required": false,
"defaultValue": "",
"placeholder": "",
"description": "可选。点「屏幕框选」框住目标所在区域,运行时只在该区域内查找目标文本,屏幕上有相同文字时可避免点错;留空则全屏查找",
"advanced": false,
"valueTypes": []
}
]
},
"CV_MouseClick": {
"fields": {
@ -216,7 +270,25 @@
"offsetY": {
"description": "相对目标中心的垂直偏移像素,正数向下、负数向上,0 为正中"
}
}
},
"removeFields": [
"searchRegion"
],
"addFields": [
{
"key": "searchRegion",
"label": "搜索区域",
"widget": "region",
"type": "object",
"displayType": "region",
"required": false,
"defaultValue": "",
"placeholder": "",
"description": "可选。点「屏幕框选」框住目标所在区域,运行时只在该区域内查找目标文本,屏幕上有相同文字时可避免点错;留空则全屏查找",
"advanced": false,
"valueTypes": []
}
]
},
"Image_Click": {
"fields": {
@ -225,6 +297,9 @@
},
"show_track": {
"description": "勾选后运行时显示鼠标移动轨迹动画,便于观察执行过程"
},
"accuracy2": {
"required": false
}
}
},
@ -298,15 +373,14 @@
"Image_Screenshot": {
"fields": {
"image_path": {
"placeholder": "如 D:\\files\\demo.png"
"label": "截图保存路径"
},
"action": {
"description": "区域坐标 JSON;点右侧「屏幕框选」在屏幕上框选生成",
"placeholder": "{\"x\":0,\"y\":0,\"width\":100,\"height\":50}"
},
"position": {
"description": "区域坐标 JSON;点右侧「屏幕框选」在屏幕上框选生成",
"placeholder": "{\"x\":0,\"y\":0,\"width\":100,\"height\":50}"
"label": "截图区域"
},
"position_img": {
"description": "区域坐标 JSON;点右侧「屏幕框选」在屏幕上框选生成",
@ -332,7 +406,68 @@
"path": {
"description": "编码方向的源图片路径,支持 ${变量} 引用上游输出(如上游截图的保存路径)"
}
}
},
"removeFields": [
"mode",
"output",
"val"
],
"addFields": [
{
"key": "mode",
"label": "转换方向",
"widget": "radio",
"type": "string",
"displayType": "buttonGroup",
"required": false,
"defaultValue": "encode",
"placeholder": "",
"description": "编码:图片文件转为 base64 字符串;解码:base64 字符串还原保存为图片文件",
"advanced": false,
"valueTypes": [],
"options": [
{
"label": "图片 → Base64",
"value": "encode"
},
{
"label": "Base64 → 图片",
"value": "decode"
}
]
},
{
"key": "output",
"label": "图片保存路径",
"widget": "file",
"type": "object",
"displayType": "selectFile",
"required": false,
"defaultValue": "",
"placeholder": "如 D:\\files\\restored.png",
"description": "解码方向的保存位置(含文件名);留空自动生成临时图片文件",
"advanced": false,
"valueTypes": [
"rpa_str_file",
"rpa_str"
]
},
{
"key": "val",
"label": "Base64编码",
"widget": "text",
"type": "string",
"displayType": "text-expression",
"required": true,
"defaultValue": "",
"placeholder": "如 ${图片编码值}",
"description": "解码方向的 base64 字符串,支持 ${变量} 引用上游输出;自动忽略 data:image/...;base64, 前缀",
"advanced": false,
"valueTypes": [
"rpa_str"
]
}
]
},
"Image_Wait_V1": {
"fields": {
@ -413,7 +548,7 @@
"Image_Find_V1": {
"fields": {
"window_obj": {
"description": "目标窗口标题,留空则作用于当前活动窗口;点「选窗口」从可见窗口列表中选择"
"required": false
},
"action": {
"description": "区域坐标 JSON;点右侧「屏幕框选」在屏幕上框选生成",
@ -425,23 +560,75 @@
},
"image": {
"description": "模板图片文件的完整路径,用于匹配识别;点右侧「屏幕截取」可框选屏幕上的目标图案"
},
"accuracy": {
"label": "模板相似度"
}
}
},
"Image_Exists_V1": {
"fields": {
"image": {
"description": "模板图片文件的完整路径,用于匹配识别;点右侧「屏幕截取」可框选屏幕上的目标图案"
"required": true,
"label": "目标图像"
},
"accuracy": {
"description": "相似度阈值(0~1,越大越严格):0.9 表示屏幕上至少 90% 像素与模板一致才算找到。建议 0.8~0.9。"
"required": true,
"label": "模板相似度"
}
}
},
"removeFields": [
"find_range",
"timeout",
"window_obj"
],
"addFields": [
{
"key": "find_range",
"label": "图像查找范围",
"widget": "select",
"displayType": "dropDownSelect",
"defaultValue": "1",
"required": false,
"options": [
{
"label": "整个屏幕",
"value": "1"
},
{
"label": "指定窗口",
"value": "2"
}
],
"description": "在整个虚拟桌面(含副屏)中查找,或仅在指定窗口范围内查找",
"type": "string"
},
{
"key": "timeout",
"label": "等待超时(秒)",
"widget": "number",
"displayType": "text-expression",
"defaultValue": "0",
"required": false,
"description": "大于 0 时在该时间内每隔 0.3 秒重试查找,超时仍未找到才返回 false",
"type": "string"
},
{
"key": "window_obj",
"label": "窗口对象",
"widget": "text",
"displayType": "text-expression",
"defaultValue": "",
"required": false,
"description": "目标窗口标题,留空则作用于当前活动窗口;点「选窗口」从可见窗口列表中选择",
"type": "string"
}
]
},
"Image_CropImg": {
"fields": {
"file_path": {
"description": "要被裁剪的源图片完整路径——本组件从已有图片文件中裁出区域,不是截屏;截屏请用「截图图像」组件"
"label": "源图片地址"
},
"imagePath": {
"description": "文件夹的完整路径;点右侧按钮浏览选择,支持 ${变量} 引用"
@ -467,13 +654,16 @@
},
"show_track": {
"description": "勾选后运行时显示鼠标移动轨迹动画,便于观察执行过程"
},
"accuracy2": {
"required": false
}
}
},
"Image_Click_V3": {
"fields": {
"window_obj": {
"description": "目标窗口标题,留空则作用于当前活动窗口;点「选窗口」从可见窗口列表中选择"
"required": false
},
"action": {
"description": "区域坐标 JSON;点右侧「屏幕框选」在屏幕上框选生成",
@ -500,10 +690,10 @@
"Image_RelativePosition": {
"fields": {
"source": {
"description": "被搜索的较大图片路径(如整屏截图),支持 ${变量} 引用上游输出"
"label": "被搜索图像"
},
"template": {
"description": "要寻找的小图案完整路径(按钮/图标/文字块),可点右侧按钮选择文件"
"label": "查找目标(小图)"
}
}
},
@ -1346,5 +1536,626 @@
"description": "选择解压使用的执行方式;不确定时保持默认。"
}
}
},
"WebBrowser_RunJsInIeBySelector_v3": {
"fields": {
"waitResultTimeout": {
"required": false
}
}
},
"StrUtil_CreateRandomStr": {
"fields": {
"special_str": {
"required": false
},
"min_max_length": {
"required": false
},
"str_contain": {
"required": false
}
}
},
"Element_SetAttribute_v2": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"Element_FileUploader": {
"fields": {
"upload_time": {
"required": false
},
"wait_dialog_time": {
"required": false
},
"designated_obj": {
"required": false
}
}
},
"WebBrowser_GetCookieNew": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_Wait": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_GetWeblistening": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_SetCookies": {
"fields": {
"web": {
"required": false
}
}
},
"Element_GetAttribute_v2": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"Trigger.Manual": {
"fields": {
"entryName": {
"required": false
}
}
},
"Keyboard_Send_V1": {
"removeFields": [
"window_title"
],
"addFields": [
{
"key": "window_title",
"label": "输入目标窗口",
"widget": "text",
"type": "string",
"displayType": "text-expression",
"required": false,
"defaultValue": "",
"placeholder": "留空则输入到当前焦点窗口",
"description": "选择目标窗口后,运行时会先把该窗口带到前台再输入内容",
"advanced": false,
"valueTypes": []
}
]
},
"Mail_ReplyMail_v2": {
"fields": {
"emails": {
"required": false
}
}
},
"WebBrowser_GoForward": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_Stop": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_GetScrollPos_V1": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_UploadDialog": {
"fields": {
"upload_time": {
"required": false
},
"web": {
"required": false
}
}
},
"Element_GetPieChartData": {
"fields": {
"v_offset": {
"required": false
},
"radius_ratio": {
"required": false
},
"designated_obj": {
"required": false
},
"h_offset": {
"required": false
}
}
},
"Mail_OperateMail": {
"fields": {
"emails": {
"required": false
}
}
},
"Element_GetButtonEnable_v3": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"StrUtil_SplitStr": {
"fields": {
"split_num": {
"required": false
}
}
},
"Element_SetValue_V6": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"WebBrowser_GoUrl": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_SetScrollPos_V1": {
"fields": {
"designated_selector": {
"required": false
},
"designated_position": {
"required": false
},
"web": {
"required": false
}
}
},
"logic_control_stop_return": {
"fields": {
"return_value": {
"required": false
}
}
},
"WebBrowser_GetScrollPos": {
"fields": {
"web": {
"required": false
}
}
},
"Element_MouseTo_V4": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"Element_GetButtonEnable_v4": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"WebBrowser_GetBrowserPicV1": {
"fields": {
"pic_name": {
"required": false
},
"web": {
"required": false
}
}
},
"Trigger.FileArrival": {
"fields": {
"stableSeconds": {
"required": false
}
}
},
"WebBrowser_GetCookies": {
"fields": {
"web": {
"required": false
}
}
},
"Bivartable_HandleDfMissValue": {
"fields": {
"row_num_list": {
"required": false
},
"col_num_list": {
"required": false
},
"row_name_list": {
"required": false
},
"col_name_list": {
"required": false
}
}
},
"WebBrowser_StopWeblistening": {
"fields": {
"web": {
"required": false
}
}
},
"Image_Find": {
"fields": {
"accuracy2": {
"required": false
}
}
},
"Element_exception_monitor_v2": {
"fields": {
"monitor_frequency": {
"required": false
},
"designated_obj": {
"required": false
}
}
},
"WebBrowser_GetDocument": {
"fields": {
"web": {
"required": false
}
}
},
"Element_GetAttribute_v3": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"WebBrowser_Switch": {
"fields": {
"web": {
"required": false
}
}
},
"Mail_DoReplyMail_v1": {
"fields": {
"emails": {
"required": false
}
}
},
"concurrent_child_process_v1": {
"fields": {
"fun_name": {
"required": false
},
"call_type": {
"required": false
},
"fun_version": {
"required": false
}
}
},
"Element_Click_V6": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"WebBrowser_SetScrollPos": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_Reload": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_GetCookie": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_GetTitle": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_GetUrl": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_Maximize": {
"fields": {
"web": {
"required": false
}
}
},
"child_modules_v1": {
"fields": {
"fun_name": {
"required": false
},
"call_type": {
"required": false
},
"fun_version": {
"required": false
}
}
},
"Element_SelectOperation_v2": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"WebBrowser_Minimize": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_Close": {
"fields": {
"web": {
"required": false
}
}
},
"Element_GetLineBarChartData": {
"fields": {
"offset": {
"required": false
},
"designated_obj": {
"required": false
}
}
},
"WebBrowser_DownloadFile_V1": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"Element_GetText_V5": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"Trigger.Schedule": {
"fields": {
"timezone": {
"required": false
}
}
},
"StrUtil_ListToString": {
"fields": {
"connectors_num": {
"required": false
}
}
},
"WebBrowser_StartWeblistening": {
"fields": {
"web": {
"required": false
}
}
},
"WebBrowser_SetCookies_V1": {
"fields": {
"web": {
"required": false
}
}
},
"Keyboard_Send": {
"removeFields": [
"window_title"
],
"addFields": [
{
"key": "window_title",
"label": "输入目标窗口",
"widget": "text",
"type": "string",
"displayType": "text-expression",
"required": false,
"defaultValue": "",
"placeholder": "留空则输入到当前焦点窗口",
"description": "选择目标窗口后,运行时会先把该窗口带到前台再输入内容",
"advanced": false,
"valueTypes": []
}
]
},
"WebBrowser_CloseWeb": {
"fields": {
"web": {
"required": false
}
}
},
"Element_CheckBoxOperation_v3": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"MessageNotification_Feishu_Message": {
"fields": {
"mentioned_list": {
"required": false
}
}
},
"WebBrowser_RunJsInIeBySelectorGround": {
"fields": {
"web": {
"required": false
}
}
},
"Word_SaveAs": {
"fields": {
"fileAddr": {
"required": false
}
}
},
"Mouse_ClickCombine_v4": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"WebBrowser_RunJsInIeBySelector_v2": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"Bivartable_DfWriteToFile": {
"fields": {
"suffix": {
"required": false
}
}
},
"Element_WaitElement_v5": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"WebBrowser_Switch_V1": {
"fields": {
"web": {
"required": false
}
}
},
"Element_Region2_v3": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"WebBrowser_GoBack": {
"fields": {
"web": {
"required": false
}
}
},
"Element_Shot_V2": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"Element_ScrollIntoView_v2": {
"fields": {
"designated_obj": {
"required": false
}
}
},
"WebBrowser_GetBrowserPic": {
"fields": {
"web": {
"required": false
},
"pic": {
"required": false
}
}
},
"Element_SelectOperation_v3": {
"fields": {
"designated_obj": {
"required": false
},
"select_value_list": {
"required": false
}
}
},
"ScreenControl_Screenlock": {
"name": "屏幕锁屏",
"removeFields": [
"username",
"pwd",
"width",
"height",
"scale_factor",
"port"
]
}
}

File diff suppressed because one or more lines are too long

View File

@ -13,6 +13,8 @@ import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { applyCatalogOverrides } from "./catalog-overrides.mjs";
// 平台标记单一源:从包内 PLATFORM_GROUPS 逐域派生(npm run catalog 用 tsx 运行)
import { buildDomainPlatformMap, PLATFORM_GROUPS } from "../packages/rpa-core/src/capabilities/register-all";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, "..");
@ -172,7 +174,17 @@ function normalizeVendorField(field) {
}
function loadCatalogSource() {
const raw = fs.readFileSync(SRC_CATALOG, "utf8");
// 优先环境变量/对面仓库路径;对面重构删除该文件后,回退到 temp/catalog-source/ 的历史恢复版
// (自 Agent-Employee commit 7bc627c8 恢复;temp/ 不入库,换机器需重新恢复)
const candidates = [SRC_CATALOG, path.join(ROOT, "temp", "catalog-source", "shizai-catalog-data.js")];
const found = candidates.find((p) => fs.existsSync(p));
if (!found) {
throw new Error(
`找不到目录源数据 shizai-catalog-data.js(尝试:${candidates.join(" ; ")})。` +
`可从 Agent-Employee 历史 commit 7bc627c8 恢复到 temp/catalog-source/`
);
}
const raw = fs.readFileSync(found, "utf8");
const start = raw.indexOf("=");
if (start < 0) throw new Error("catalog source has no assignment");
let body = raw.slice(start + 1).trim();
@ -228,10 +240,10 @@ function normalizeVendorComponent(component, vendorMap) {
const fields = (component.inputFields || []).map((f) => enrichFromVendor(normalizeVendorField(f), vendor));
const output = outputOf(component, vendor);
const risk = ["web", "app", "desktop", "message", "mobile"].includes(group) ? "high" : "low";
// 任务 3.4(决策点 7):平台标记只填 catalog 数据,渲染层置灰延后。
// Windows 专用组:desktop(user32/键鼠)、app(COM/进程)、office(Excel/Word COM)、vision(PowerShell 截屏);
// data/file/message 纯 Node,web 走 CDP,均视为跨平台(null)。
const WIN32_GROUPS = new Set(["desktop", "app", "office", "vision"]);
// 任务 3.4(决策点 7)+ 五件小事 #1:platformTag 从包内 PLATFORM_GROUPS 单一源派生
// (能力 id 命中包内注册 → win32;未实现的能力与宿主原生节点 → null 跨平台占位)。
const platforms = domainPlatformMap.get(component.id) || component.platforms;
const platformTag = component.platformTag || (platforms?.includes("win32") ? "win32" : null);
return {
id: component.id,
name: component.name,
@ -246,7 +258,7 @@ function normalizeVendorComponent(component, vendorMap) {
functionCalls: (component.functions || []).map((f) => ({ function: f.function, args: f.arguments || [], assignTo: f.assignTo || "" })),
version: component.version || "",
tags: component.tags || [],
platformTag: component.platformTag || (WIN32_GROUPS.has(group) ? "win32" : null),
platformTag,
icon: group,
risk,
fields,
@ -319,7 +331,11 @@ function normalizeTrigger(trigger) {
};
}
function main() {
async function main() {
// 平台单一源:逐域加载包内能力,得到「能力 id → 平台数组」映射(与运行时同一张 PLATFORM_GROUPS 表)
domainPlatformMap = await buildDomainPlatformMap();
console.log(`platform map: ${domainPlatformMap.size} capability ids from ${Object.keys(PLATFORM_GROUPS).length} domains`);
const source = loadCatalogSource();
const vendorMap = loadComponentsNew();
@ -362,4 +378,10 @@ function main() {
console.log(`output: ${OUT_FILE} (${(size / 1024 / 1024).toFixed(2)} MB)`);
}
main();
/** 能力 id → 平台数组(buildDomainPlatformMap 的结果,main 内填充) */
let domainPlatformMap = new Map();
main().catch((err) => {
console.error(err);
process.exit(1);
});

View File

@ -7,10 +7,17 @@ export function applyCatalogOverrides(catalog) {
for (const component of catalog.components) {
const override = overrides[component.id];
if (!override) continue;
const { fields = {}, removeFields = [], ...metadata } = override;
const { fields = {}, addFields = [], removeFields = [], ...metadata } = override;
Object.assign(component, metadata);
component.fields = component.fields.filter(field => !removeFields.includes(field.key))
.map(field => ({ ...field, ...fields[field.key] }));
// addFields:补源数据里缺失、但实现确实读取的字段(完整字段定义,key 重复则跳过)
if (addFields.length) {
const have = new Set(component.fields.map(f => f.key));
for (const f of addFields) {
if (f && f.key && !have.has(f.key)) component.fields.push(f);
}
}
}
catalog.counts.fields = catalog.components.reduce((count, component) => count + component.fields.length, 0);
return catalog;