架构与执行语义
flowchart TB
UI[WinUI 桌面端] --> Infra[Infrastructure]
Runner[独立 Runner] --> Infra
Infra --> Core[Core]
Core --> SDK[Abstractions SDK]
Runner <-->|当前用户命名管道| UI
Plugins[插件 ALC] --> SDK
Runner --> Plugins
UI -->|ServerInteractive| Space[Space.Server]
Space --> Contracts[Space.Contracts]
Contracts --> SDK
桌面端沿用 WinUIHelper 的导航、消息、主题、加载与服务注册;工作区路径由 AutoPath 创建,用户配置由 AutoConfig 保存,控制台日志由 XFEConsole 输出。Core 和 SDK 不引用 WinUI 或 Windows 自动化实现。Space 服务端单独运行,不引用桌面端和 Infrastructure。
SDK 1.7 增加插件页面元数据;可选的 XFEWorkflow.Desktop.Sdk 单独提供 WinUI 接口,Core、Runner 和 Space 仍不依赖 WinUI。导航到通用宿主页时才加载页面程序集。页面持有依赖版本租约,切换导航保留实例,禁用/卸载/退出时释放;文件仍被 ALC 占用时保留待移除并于后续启动清理。Agent 浏览器的模型请求和 Node 技能通信在后台运行,使用独立 WebView2 profile 和回环 CDP;聊天渲染器不属于 Agent 调试目标。结构与协议。
本地插件管理与在线 Space 是独立页面。Space.Contracts 定义插件/工作流的目录、发布版本与上传会话类型;同一账号、会话和串行元数据写入机制服务两类资源。Abstractions 的 WorkflowPackage 只检查便携定义的大小、ZIP、结构和插件锁定,不引用 Core、不执行用户代码。Space.Server 分开保存两类发布和包;Infrastructure.SpaceClient 负责分片、哈希、目录一致性与依赖安装,最终交给 WorkflowRepository 导入为默认关闭的新 ID。在线包不会直接交给 Runner 执行。接口和发布流程。
Space 预览接口返回已校验的工作流定义和对应版本的已上架插件清单。未公开资源由作者/管理员接口读取;图形预览只使用声明的端口元数据,不加载插件程序集。客户端复用 WorkflowCanvas 的只读模式和只读 Monaco,图形对应源码由 GraphCodeGenerator 生成;预览不会经过 WorkflowRepository、PluginManager 安装或 Runner。
WorkflowValueEditor 统一编辑字面值、数组、对象、工作流变量及默认输入;画布常量与属性使用同一控件。WorkflowLiteralValues 提供类型和精确数字解析,数值不经 double 转换;数组/对象分页并按需展开。画布数据修改通过统一的 SetNodeValue 记录撤销并同步端口与属性,不解析消息表达式。类型编辑说明。
图和变量
主图至少有一个手动“开始”入口或自动触发节点,手动入口至多一个;每个结构化子图必须有一个“开始”。自动触发节点只允许放在主图。普通执行边禁止成环、一个执行输出只连接一个后继;显式并行节点运行多个结构化子图并等待完成,然后进入汇合。数据边必须与端口声明类型兼容,来源节点必须先执行。缺失插件保留原始节点和布局,校验阻止执行。
Inputs 是当前工作流输入;Variables 是当前调用的变量。顺序子图共享变量,ForEach 临时设置 item/index 并在退出时恢复,While 每轮重新计算条件。并行分支复制变量,分支结果位于 branches,不会隐式争写父变量。子工作流有独立上下文,最大调用深度 32。
ForEach 按数组顺序逐项执行 body,index 从 0 开始,results 收集每轮子图的输出对象。进入每轮子图时清除该子图的节点输出,避免读取上一轮结果;循环体的结束节点只结束本轮。主图 next 在所有元素处理完成后执行。数组遍历与日志模板。
core.foreach.current 在子图内输出当前项 item(Any)和序号 index(Number),与消息等输入直接数据连线。执行上下文为每轮保存独立的迭代帧;结构化子图和并行分支继承最近一层帧,内层遍历退出后恢复外层,子工作流与封装方法保持自己的作用域。在遍历外访问当前项会明确报错。输出采用深拷贝,连线数据不经过参数表达式解析,不包装或修改消息内容;旧变量绑定仍兼容已有文件。
节点 ID 在整个工作流内唯一。复制节点会重写内部子图、执行边、数据边及 JSON $node 引用;代码字符串中写死的节点 ID 需要作者自行修改。
core.object.get 通过 WorkflowObjectFields.Read 读取对象/数组的点分路径或 JSON Pointer,输出字段的深拷贝 value 与存在标记 found。不存在返回空值和 false,存在的 JSON null 返回 true;文本输入需要显式解析。输出连线继续使用原始值,不解析字段里的绑定内容。通用字段经过显式转换后可连接具体类型端口。字段读取语义。
SDK 1.6 的 WorkflowBasicNodes 将 131 个纯数据操作按文本、数学、比较、数组、对象、日期和编码分文件注册,元数据与执行函数成对声明;统一合并默认值、检查输入输出类型、取消与规模限制,返回深拷贝。图形和 CapabilityRegistry.InvokeAsync 共用执行入口,字面参数不走旧绑定解析。WorkflowControlNodes 补充 10 个需要作用域的控制/变量操作,核心动作合计 164 个。节点和语言语义对照。
For/Repeat 与 ForEach 共用迭代帧,正常 body 输出收集到 results;break/continue 以带迭代作用域的内部信号传播,绕过普通 catch、重试和错误日志,finally 仍执行。只有拥有该帧的循环处理信号;并行分支禁用对外层循环的控制,分支内自己的循环创建新帧。While/Do While 支持可选 condition 子图,每轮重新执行并要求结束 result 为布尔,避免依赖文本中的变量表达式。catch 上下文携带最近的异常消息、节点 ID 与异常类型,独立调用不继承。
WorkflowJson.Display 与 SerializeDisplay 为日志、参数、调试、运行记录和 UTF-8 导出生成可读 JSON。序列化后只解码实际 JSON 中可打印的 Unicode 转义及合法代理对;反斜杠转义成对跳过,保留字面转义和 JSON 必要的引号/控制符转义,组合表情序列不变。Text 对字符串保持原文,对结构化值使用此显示路径;默认 Options 继续服务于协议、内部存储与脚本编码。凭据脱敏同时登记原文、默认编码和可读 JSON 编码的形式。
执行与取消
每次运行启动一个 Runner,并锁定所需插件版本。图形流程与 C# 共用上下文及能力调用入口。开始、完成、日志和失败事件回传桌面端;XFE 日志只呈现主动日志、节点失败和必要的异常结束提示,生命周期事件保留用于状态、高亮与记录。运行输出、日志及调试快照隐藏已管理的凭据。
编辑器通过 RunRequest.Debug 开启协作节点调试,断点在解析参数后、动作和超时计时开始前检查。Runner 的 WorkflowDebugger 经 RPC 接收继续、越过、进入、暂停及断点更新,使用暂停标识拒绝过期继续命令。结构化子图和子工作流增加调用深度,断点优先于越过;暂停时间从调试时钟扣除,外层节点超时不消耗人工等待时间。调试状态不写入运行记录 JSON;自动调度不启用调试。快捷键与能力范围。
同一工作流同时只能运行一次,重复手动或定时触发会保存 Skipped 记录。节点默认重试 0 次,超时 0 表示不限;显式重试会重新执行节点,存在外部副作用的节点需据此设置。ForEach 的次数由输入数组长度决定;While 默认最多执行 1000 次,maxIterations 限制在 1~1,000,000 范围,超过限制时报错。两者每轮均检查取消请求。
停止先向 Runner 发送取消。请求在 1 秒内确认,仍未退出的进程最多再等待 5 秒后终止进程树。退出应用先停调度、停任务、释放浏览器与输入资源并保存配置。重新启动时,将历史中 Pending/Running 的记录标为 Interrupted,不自动重放。
浏览器与输入
WebView2 对象仅在 UI 线程操作。Runner 发送能力调用,桌面端通过 DispatcherQueue 执行,再返回 JSON 或资源句柄。每个登录配置使用独立 UserDataFolder,同配置再次打开可保留 Cookie;运行拥有的标签在结束时关闭,手动标签保留。
iframe 由 CoreWebView2Frame 逐层登记,路径为 0、0/0 等,支持跨源脚本和元素操作。选择器拾取使用一次性 nonce。页面脚本通过 JavaScript return 返回同步 JSON 值。
桌面能力先取得全局输入租约,再使用 FlaUI.UIA3 定位。标准后端实现 IInputBackend,接入 XFE InputController 的组合键、Unicode 与输入序列。取消、异常和紧急停止均释放按键。UIA 定位使用物理屏幕坐标;标准绝对输入换算虚拟屏幕坐标以支持多屏。
屏幕与识别插件在 Runner 中执行 GDI 截图、Tesseract OCR 和像素容差匹配。屏幕坐标使用虚拟桌面物理像素;文件图像的区域与输出使用原图像素。框选浮层与剪贴板通过 screen.* 命名管道调用在 UI 线程完成,区域以 JSON 传回。框选冻结图像,仅确认后写入参数;取消保持旧参数,紧急停止关闭活动浮层。未解析成功的区域或图像路径绑定会报错,避免自动退回整个桌面识别。
插件边界
插件版本独立保存,每个插件创建可卸载 AssemblyLoadContext,SDK 从默认上下文共享。插件之间以能力 ID 调用,不引用对方实现 DLL。清单中的权限用于描述能力;首版执行的是本地受信任代码,不限制其操作系统访问权。
运行持有版本租约,卸载时先禁用,租约释放后删除。保存代码工作流或含 C# 节点的图时锁定可用插件集合,动态能力调用能够使用确定版本。静态子流程依赖在运行前递归合并,版本冲突直接阻止运行。