# OpenCU OpenCU 是 DSH 插件:新控制核心、浏览器/原生/扩展后端配合真实 REA 前端组件。AI 像使用其他工具一样调用电脑;页面、活动和监看自然呈现,没有单独的 CU 控制台。 当前源码默认适配 DSH `0.2.1-rc.2`,当前发行版本为 `2.0.1-dsh.0.2.1-rc.2`;0.2.0-rc.2/alpha.3/0.2.1-rc.1 的独立 SDK 构建入口与旧工件保留。 ## 加载 各宿主分别精确匹配 SDK 与框架:DSH `0.2.1-rc.2` 使用 Cordis `4.0.5`/Schemastery `3.18.5`;0.2.0-rc.2 使用 `4.0.4`/`3.18.4`;alpha.3 使用 `4.0.5-alpha.2`/`3.18.5-alpha.2`。插件包根入口和 `opencu/dsh` 均为 Cordis Host 插件,依赖宿主真实 `tools` 和 `attachments` 服务。不需要另开 OpenCU 服务,也不接受外部 origin/token。由 DSH profile 加载: ```yaml - insert: - id: opencu name: opencu/dsh config: dataDir: /absolute/path/to/opencu-data ``` 独立插件默认注册工具,普通会话无需额外启用 CU;集成方如需限定工具作用域,可设置 `registerTools: false` 并调用 `installTools`。第一次调用懒启动同一 controller 与所需后端。浏览器进程由插件拥有,原生 helper 由后端托管;工具取消会等待 stop 的真实输入释放。插件停用/DSH profile 退出调用 close 并等待释放和关闭,失败保留 controller 状态并报错,不清空失败冒充成功。原生 helper 缺失或权限未获批,会报告真实后端警告或错误。 数据目录优先使用 config.dataDir,其次 DSH_HOME/opencu,最后 ~/.opencu;现有数据不迁移或删除。core 使用持久目录锁,下载/批注/控制恢复状态继续保存。本候选已将新版默认内置到 OMD 并整体替换旧 CU;只装 OMD 即可加载新版,不要求另装 OpenCU。独立安装保留,共装复用同一套核心和工具。 监看与工具输入使用独立通道,捕获按目标合并;帧等待超过 12 秒返回监看错误,不改工具目标、不设置输入释放失败。原生和扩展的监看超时只结束对应请求,不断开输入连接;隐藏窗口的动画帧等待设有上限。关闭控制器先释放输入,再由后端清理捕获,不无限等待监看帧。 Chrome / Edge 扩展的 loopback bridge 由插件自动管理,监听本机 19390–19399 的首个可用端口,只服务扩展协议,不承载另一套网页或控制核心。扩展仅首次安装;AI 首次使用时自动弹「允许连接此浏览器?」窗口,单击「允许并记住」,不再复制连接信息。配对身份与凭据分别保存在私有数据目录和浏览器 local storage;固定扩展 ID,正常浏览器/DSH/电脑重启和保留身份、数据的更新后自动恢复。断开先取消并确认释放输入,然后暂停自动连接;撤销配对后需要重新允许。每次重连创建新 epoch,旧目标必须重新发现,不重放旧输入。多浏览器 profile 明确选择,不擅自连接全部。 浏览器资源使用 Playwright 的共享缓存,不捆绑 Node、Chromium 或独立应用。宿主使用 Node >=22.19。缺少 Chromium 时,可从 DSH 的“电脑与浏览器”设置安装匹配的 Playwright 浏览器资源;由插件管理安装进程,关闭时终止尚未完成的安装。 ## 能力与边界 工具提供真实目标发现、选择、观察、操作、文件交付和持久 JavaScript。已删除强制 takeover 协议;用户活动通过 userActivity 通知,不停止控制、不要求用户点恢复。stop 用于明确任务取消并确认输入释放;故障/冷恢复的 resume 核验身份,不能清状态绕过。截图保存到真实 DSH attachments 并返回持久 ImageAttachmentRef。普通批注保存不标记为 AI 操作,也不取消工具。macOS 人工活动通知使用已授权的只读输入监听,只保存所选目标的活动种类、时间与序号,不保存按键内容、不请求权限或改变控制权;缺少输入监听权限时明确报告不可检测。产品鼠标使用同一 REA 原件,承载层透明且点击穿透;可见浏览器页面和 macOS 等待到达回执后发送对应输入,隐藏页面明确延后视觉回执而不阻塞后台输入,Windows/Linux 的原生移动通过独立显示通道传给所属 DSH 窗口,不以动画回执冒充输入成功。取消会隐藏鼠标并释放实际按键。内置浏览器鼠标的位置/可见性独立于单次动作活动状态;macOS 原生鼠标不再 1.5 秒超时隐藏,在动作间隙保留最后位置,取消/释放/目标失效时移除。托管浏览器和扩展页面沿用同一 REA 鼠标,在页面存续期间不按空闲时间隐藏。Windows/Linux 显式前台输入保留系统指针,同时把真实位置接到同一 REA 鼠标;后台无障碍动作不移动系统指针;Windows 物理像素由宿主换算为 DIP,Linux X11 单屏按真实比例换算,Wayland 使用 portal 逻辑坐标与显示器原点。鼠标在动作间隙保持,取消/目标失效/helper 或所属窗口退出时清理;用户鼠标不被覆盖层捕获。 浏览器 action 支持 navigate/back/forward/reload/newTab/closeTab/activate/click/doubleClick/move/mouseDown/mouseUp/scroll/drag/keyDown/keyUp/press/type/fill/selectOption/upload/download。元素 ID 只使用最新观察;坐标遵循观察声明的坐标空间。 Windows 原生 helper 由系统 Windows PowerShell 编译随包 C#,需要 Windows 10 1703+、.NET Framework 的 UI Automation;使用物理像素,受 UIPI 和应用 UIA 支持限制,不自动提权;全局输入仅在显式前台模式运行。Linux 原生 helper 使用 `/usr/bin/python3`、X11/XTest 和可选 `python3-pyatspi`;缺少 AT-SPI 时后台语义动作不可用,XComposite 截图和显式前台坐标操作仍可按系统能力使用,但不能读取 AX 或 Unicode 输入。Wayland 使用 xdg-desktop-portal 的 RemoteDesktop/ScreenCast 和 PipeWire;在“电脑与浏览器”设置中点击“选择共享桌面”,由系统选择单个显示器并授权本次键鼠输入,不请求永久权限。依赖系统 `python3-gi`、GStreamer introspection/PipeWire/PNG 插件和 `libxkbcommon`;缺失时明确报错。支持共享显示器真实截图、逻辑/像素坐标换算、键鼠/Unicode 输入;提供 AT-SPI 和有效桌面坐标时也可绑定应用。截图范围明确为共享显示器,不冒充应用独占截图。会话撤权/退出关闭 portal 和媒体流,取消释放输入;Wayland 不提供全局人工活动通知。Windows 只读输入 hook/Linux XInput2 人工活动监听已接入,分别排除本 helper 标记的输入/XTEST 事件;只保存活动种类、时间与序号,不记录文本,不强制接管。监听依赖或权限不足时观察明确报告不可用。 ### 后台操作 选择目标与聚焦分离:`select`/`getApp`/`getTab` 不激活窗口或标签;只有显式 `focus`/`activate` 改变前台。输入默认 `inputMode: 'background'`,后台不可用时不自动降级为全局键鼠。目标的 `control` 字段(JavaScript 目标通过 `capabilities.control()`)说明路由和限制;`delivery` 仅说明提交方式,`effectVerified: false` 要求后续观察,不能当作界面变化已成功。 - 内置、Playwright 和 Chrome/Edge 扩展浏览器使用目标页 CDP 与焦点模拟;AI 新建标签不激活,内置标签不展开侧栏或切走当前会话。释放只清理 AI 记录的目标输入,并撤销焦点模拟。后台页面暂停动画帧时返回 `visualDeferred`,不伪造游标到达,也不阻塞输入。 - macOS 使用私有事件源和 `postToPid`,不移动系统鼠标;需要明确窗口。后台普通左键点击优先对支持的控件或其子元素执行 `AXPress`,禁用控件直接拒绝;否则,对已观察表格行、单元格或其文本在精确窗口内寻找最近行并使用可写 `AXSelected`,不支持时返回 `BACKGROUND_SELECTION_UNAVAILABLE`,不发送指针输入。多选表格中普通点击只保留目标行;持键、其他鼠标按钮及双击仍沿原事件路径。键盘仅发给应用内部当前键盘窗口,其他窗口用 AX 元素操作或返回 `BACKGROUND_KEYBOARD_UNAVAILABLE`。目标应用仍可能忽略事件或因自身行为激活窗口;这不是全应用无干扰保证。 - Windows 使用 UI Automation 的文本、Invoke/Toggle/SelectionItem/ExpandCollapse 和滚动模式;截图使用 `PrintWindow`,不还原最小化窗口或回退桌面截图。应用拒绝或不提供窗口像素时明确不可用。 - X11 使用 AT-SPI 动作与 XComposite 窗口像素;Wayland 使用 AT-SPI 动作,portal 仍只共享显示器,不提供被遮挡应用的独立像素,非活跃应用截图明确不可用。Windows/Linux 的后台滚动是应用定义的步长,不是精确像素。 不支持的后台操作返回 `FOREGROUND_REQUIRED` 或具体 `BACKGROUND_*_UNAVAILABLE`。经用户同意后才能显式聚焦并传 `inputMode: 'foreground'`;原有焦点、身份、权限和取消保护仍在。Windows SendInput、X11 XTest、Wayland portal 键鼠不会作为隐式兜底。内置浏览器需要重新构建匹配的宿主、双 preload 和侧栏,并声明 `backgroundInput`;只更新插件会明确拒绝旧宿主。 ### 模型输出与提示词 [运行时说明]()统一拥有 `computer_use` 主入口及 `core`/`browser`/`native` 文档,OMD 保留该契约。首次使用或重置后的第一个调用仅执行一个入口,可将返回值绑定到变量;读取返回文档和状态后,再决定下一步操作,不在该次调用中追加其他 API、等待或观察。首次自动显示基础文档,绑定应用或标签时显示对应文档及初始 AX;`getBrowser` 仅绑定浏览器并显示文档,不创建或选择标签,也不返回初始 AX。绑定和输入默认后台,不绕过目标保护或隐式回退全局键鼠。上下文压缩后调用 `cua.rewriteDocumentation()` 恢复文档与全量基线,不重置绑定。 AX 默认返回紧凑语义文本,保留元素身份、层级、值、状态和动作;`details:true` 获取完整节点与几何。身份稳定且基线完整时返回差分,`disableDiffing:true` 强制全量;截图、静默观察、目标切换、截断或失效基线会恢复全量。截图返回附件引用。内置 guest 的透明捕获在可信 surface 中按页面深浅方案补齐默认 Canvas 背景,保留显式背景及原始尺寸,不修改网页。观察自动输出一次,`emit:false` 仅供代码读取。 浏览器支持 `tab.playwright` 的角色/文本/标签/placeholder/testId/CSS 定位、过滤、顺序选择、读取及常见输入,以及 `domSnapshot()`;定位器每次按当前文档解析,保留各后端的后台输入和释放路径。支持嵌套 frame(含跨源/OOPIF)、开放 shadow DOM,以及文本/名称/标签等位置的 RegExp。定位器按 timeoutMs 自动等待目标和可操作状态,提供 waitFor 的 visible/hidden/attached/detached 状态;只有确定尚未发送输入的失败才重试,已派发或结果不明的输入不会重放。frameLocator(...).domSnapshot() 可观察子 frame。定位解析保持同步,点击稳定等待由 CDP 宿主计时并复核同一元素、盒模型及可操作状态;再次滚动后等待目标及父 frame 布局稳定,光标移动后在实际按下前重新核验位置和命中,不对移动后的旧坐标盲点;无脚本 sandbox frame 不依赖页面定时器,也不放宽 sandbox/CSP 或执行网页内联脚本。内置 guest 文本输入在嵌入视图重入后恢复原目标子 frame 的焦点,避免把子 frame 中文丢到主文档;Chrome 扩展对不透明沙盒的 `Not allowed` 限制仍如实返回,不绕过。fill 支持文本、textarea、contenteditable 与 number 控件;number 接受有限数字字符串或空值清空,非法数字在派发前拒绝,保留只读/禁用保护。角色名称忽略 aria-hidden 与视觉隐藏的后代提示,保留显式 aria-labelledby 隐藏引用;原生 label 的名称不混入被标注控件自身的选项或默认正文,文本读取仍返回原正文。内置浏览器的 automation/父子表面 RPC 以结构化信封跨越 Electron IPC 和 contextBridge,在页面侧还原错误码与输入派发状态;主/子 preload 必须声明 `structuredErrors`,旧宿主明确拒绝。旧接口保留,导航增加 `goto`,输入增加 `typeText`,拖动支持起止坐标。 [模型结果投影]()不改控制器或 UI 保存的原始状态:默认摘要化历史目标、观察与批注,保留实时库存、控制失败、所选目标能力和观察身份;`opencu_state({includeHistory:true})` 可读取完整历史。模型侧保留全部可操作节点的语义信息,去除重复文本;完整节点、几何和后端原始属性可通过 details:true 读取。只请求截图时不夹带 AX。截图附件仍按附件 ID 去重。旧会话内容不改写,本改动不会自动消除已进入上下文的历史冗余。 ## REA 前端 监看命中区域由真实 REA 帧/关闭按钮的实际布局上报,匹配宿主通过系统鼠标位置与预览自身缩放判断穿透,不依赖非激活窗口先收到鼠标事件;画面刷新不重复设置窗口层级。需要主窗口与浏览子窗口 preload 同时声明 `presentationHitRegions`,不能只更新客户端。切会话只撤下显示,不记录为主动关闭;侧栏定位的暂隐在离开所属会话后解除,切回无需新工具调用或手动显示,主动隐藏则保持。快速切换按发生时的会话快照有序发布。 电脑操作与浏览器偏好合并在同一个“电脑与浏览器”设置入口,主页面同时保留两套真实 REA 设置内容;应用权限、浏览器连接、历史/下载、站点与扩展详情在同一页内导航和返回,不另设浏览器偏好入口。 透明 REA portal 承载层不参与 Electron 拖动区域计算,实际菜单/弹层内容仍以自身边界排除拖动;浏览器原生 guest 的全窗口 carrier 同样保持中性,避免常驻透明层减掉整个 DSH 拖动区域。 原件、SHA-256 和来源保留在 [组件来源清单](src/client/rea/provenance.json),完整落位见 [组件来源](src/client/rea/provenance.json)。已撤掉错误 OpenCU 入口/控制台、截图伪网页、自绘全屏/浮窗壳及固定信号/空组件宿主适配。真实 REA 浏览组件、查找、批注/调整、图片评论、工具结果、偏好/历史/下载已接入隔离 DSH。页面查找按 Electron requestId 接收当前结果,改词、清空和关闭后丢弃旧请求回包,正常上一项/下一项保持原行为。监看使用同一 REA 堆叠组件,由所属 DSH 窗口的子窗口承载;Host 适配源码位于 `host/`,只对匹配的工作区 DSH 源码应用并构建,不修改正在运行的安装。原版监看菜单接到会话标题栏,仅在该会话已有工具目标时显示;显示/隐藏、侧栏停靠和浮窗共用同一目标与 REA 帧组件。侧栏停靠使用新增的通用 accessory 插槽,不新增 CU 标签或控制台;监看窗口接入原生坐标拖动、拖后点击抑制、悬停变色、左上阴影关闭按钮及阴影留白;不再拖后强制吸附。监看关闭不停止工具;插件释放时关闭子窗口。侧栏/悬浮监看随 AI 工具自然呈现,点击定位同一真实现场,批注/@ 不自动打断,人工操作不锁死 AI。 复制标签及 Cmd/Ctrl 返回、前进分支使用独立原生 guest,首次加载前恢复完整历史与所选索引;后台分支不切走原标签,失败回收新标签和租约,保留原表单。历史地址沿原导航策略逐项校验,不允许恢复应用内部地址;必须重建声明 `initialNavigation` 的匹配宿主和两个浏览器 preload,旧宿主明确拒绝,不在已加载页面上伪造历史。 网页进程退出时显示原版 REA 错误通知,旧元素、观察和截图快速拒绝;已退出进程的持键/持鼠状态按实际生命周期回收,不向新进程重放。刷新保留标签/工具身份,重新合成隐藏 guest 后才派发首个输入;旧文档元素必须重新观察。崩溃后直接关闭仍回收原 guest 和租约,不影响兄弟页。 浏览历史与下载历史支持按时间范围清理,下载清理只移除记录、不删除已交付文件。内置浏览器下载使用内部元数据保留名 `record.json` 或 `record.json.pending` 时,文件名添加 `download-` 前缀,避免完成记录覆盖文件正文。长下载名按 UTF-8 字节限制私有副本名称并保留正常扩展名,为唯一前缀和临时副本预留空间;托管/外接导入仍保留完整显示名和用户原件,旧下载按已保存的精确文件名打开。Cookie/站点存储和缓存通过独立 OpenCU Chromium profile 的真实 API 清理;当前宿主只支持“所有时间”,其他时间范围明确报错,不扩大删除范围。历史/下载菜单根据真实本地能力显示,不依赖 Codex 云端开关。设置中的站点权限作用于本窗口已打开页面的本次访问;相机与麦克风的单项提示分别记忆,不因同意一项自动允许另一项。媒体总开关仍明确设置整组权限,导航或关闭后重置。扩展动作弹出页在固定 4.9.0 库中先走其已有布局测量路径,避免首选尺寸事件缺失时停在 25×25 且不显示;后续原生首选尺寸事件仍正常接管。扩展管理连接内置 profile;单个已保存目录恢复失败不阻断其余扩展和管理入口,管理窗口保留具体错误并可重试,已加载扩展不重复恢复。配置读取或解析失败时保留原文件并拒绝修改,修复后可在原进程重试。扩展选择/确认等待结束后重新核验原 guest 和窗口,失效时不启动安装或移除;已提交的加载仍完成配置收尾。扩展宿主卸载后不再接收迟到订阅、发布状态或续排轮询;正常订阅和读取错误保留;状态轮询仅单次在途,期间的扩展事件合并补查,不发布事件前的旧快照。密码/联系信息入口按用户选择的已连接外部浏览器 profile 打开其自身设置,不新增密码库、不读取或复制数据。 来源入口使用原版 REA 来源面板,挂入 DSH 原侧栏,保留会话实际观察到的网页和已交付文件;不会把只打开但未观察的网页标为已读。文件条目显示公开文件名,不展示内部编码地址;图标按文件扩展名识别,点击仍以原附件身份解析持久副本。文件本地副本可编辑,内容寻址的 DSH 原附件受宿主写入保护。预览读取按实际字节执行 maxBytes(最多 256 MiB),文件检查后继续增长也不能突破限制;超限拒绝而非返回截断正文,正文版本由同一份读取字节计算。编辑保存使用独立短临时名,不因合法原文件名接近单段上限而无法保存,公开名称、权限和版本冲突检查保留。另存副本先在目标同目录完成临时复制,再原子替换确认的文件;复制或替换失败保留目标旧内容及源文件,取消不写入,已有符号链接仍指向原目标。另存目标同样拒绝覆盖或新增内容寻址原附件,检查已有文件及新文件父目录的真实路径;原附件仍可导出到普通目录。保存对话框及复制准备结束后核验原窗口,窗口失效不启动复制;已经提交的复制仍完成替换和清理。重启按现有标签记录恢复隐藏真实页面,不另开同 URL 副本;扩展注册和原生焦点同步不再反向触发切页,销毁通知不删持久标签。网页链接/window.open 新页归属原会话;隐藏或自动控制时后台创建,保留原标签选择和表单,可见且未受控页面仍正常选中新页。surface 转交 active 状态,迟到失效请求不再创建;创建失败沿原错误提示。每个标签独立保存恢复地址与自定义标题,兼容读取旧快照而不覆写;恢复保存失败保留原存档和诊断,不阻断当前网页状态与浏览历史更新。 内置浏览器自动上传在本地文件检查及输入标记等待后核验原元素和取消状态;换页、关闭、驱动取消或宿主租约释放后不再提交文件赋值,原文件和读取错误保留。普通文件选择仍由 Electron 处理。内置 guest 的快捷键保留字面加号,空键在发送前拒绝;组合键不得复用显式持有的键,避免误释放原修饰键,发送和释放失败仍保留重试记录。macOS 内置/扩展 CDP 按键同时携带原生编辑命令,支持实际全选、选区导航和删除,保留网页 preventDefault;字面键值与自动输入活动标记一致。滚轮标记精确匹配坐标和增量,在派发结算后才开始有效期,取消前未派发则移除,不把隐藏页延迟输入当成人工操作。键鼠输入标记等待结束后再检查取消状态,取消后不提交新的按下或滚轮事件,已持有输入仍正常释放。截图观察返回及坐标使用前核验真实 documentId、URL、视口和滚动位置,同地址重载不沿用旧截图,保留预览像素换算。AX 观察在元数据和无障碍树返回后检查导航代数与 guest 生命周期,导航或关闭后的迟到结果不发布,也不重新登记失效元素。坐标命中在节点查找、远端解析和描述读取后复核原文档,失效结果不登记或发布,已取得的远端对象仍释放。元素定位在滚动和盒模型等待后检查原文档,实际鼠标移动/按下/滚轮提交前再核验原元素;导航后拒绝旧坐标,抬起和释放仍可执行。定向文字输入在聚焦回执和输入标记等待后核验原元素,换页后不把旧元素的文字插入新文档;正常 type/fill 与原始错误保留。导航仅结算一次,加载事件之后迟到的 loadURL 拒绝不清除下一导航的取消入口;正常失败仍原样返回。导航停止失败不会中断已持键鼠的释放;停止与抬起错误一并报告,未确认停止保留重试入口,已释放输入不重发。内置浏览器的 Site tools 使用 Chromium 原生 WebMCP 描述与调用,不注入伪工具。`tab.webmcp.listTools()` 返回带当前文档身份的注册,`invokeTool(registrationId, input)` 再校验后执行;明确取消经独立 CDP 通道等待取消确认,失败不冒充释放成功。调用输入/结果保留到原版来源面板,最近使用数量来自实际调用记录。浏览器未提供原生 WebMCP 时明确报告不可用。 CU 工具卡保留真实 REA 组件,参数适配同时读取 alpha.3 的 `block.args` 与 rc.2 的调用 `argsRaw`/结果 `call.argsRaw`;准备阶段没有参数时仍正常显示,完成和失败沿用原卡片结算与错误详情。CU 及包含 CU 的 PTC 调用收纳进普通工具折叠组;折叠标题继续显示最新操作,展开后保留真实 REA 卡片。 应用访问许可使用真实 REA 请求卡并显示实际 DSH 身份,系统权限与逐应用访问授权相互独立;拒绝或取消结束当前等待,不自动授权、输入或重放失败动作。工具结果的系统权限错误接原版 REA 请求卡,只打开实际系统设置。卸载先停止接收新命令,再释放各页面输入、批注、下载监听、视频帧和窗口;错误集中上报,主核心释放失败保留重试状态。OMD 底部自加工作台/用量抽屉已移除,使用 DSH 原有底部布局;IUI 计数器与非 CU 页面保留。 本轮已补 REA 完整设置及子路由、偏好持久化/失败提示、网页右键原图、文本批注、图片菜单与查看器、XLSX/PPTX 保存及关闭保护、主题/焦点/地址建议、网页批注附图与草稿。回复文字批注仅接受同一 assistant 正文消息的有效选区;改选用户消息、推理、跨消息或编辑控件会清理旧入口,保留已保存的中文草稿。关闭及冷恢复不发送;明确提交后才交给原消息草稿,失败保留评论与错误以便重试。REA 动态模块遇到公开导出名与内部名碰撞时使用独立活绑定命名空间,静态消费者及延迟初始化不变;画布文本代理按深层 Shadow DOM 焦点进入/退出编辑,不抢占菜单焦点。工作簿保存中断并遇到外部文件变化时,仍可读取持久检查点和更新;冲突继续阻止写入与刷新,不丢待提交记录或覆盖外部文件。跨窗口更新按日志版本顺序交付且不重复应用,退订立即停止剩余批次;保存前重读日志确认最新已保存进度,不伪造重复增量事件。新增桌面接口需要匹配宿主重新应用并构建,不会随插件自动更新。图片 Markup 已连接原版画笔/文字/形状/评论/撤销重做和丢弃确认,合成真实 PNG 后连同原图及结构化引用进入当前消息草稿,失败保留编辑、卸载取消导出。同内容窗口迁移使用一个 WebContentsView 承载完整 REA renderer 与原 webview,分离/归还移动原 view,保留工具目标、guest lease、历史与页面状态,不新开同 URL 副本;新增主/子 renderer RPC、窗口几何/焦点及退出释放已接线。开发接续与最新包边界统一见[工作区计划](<../PLAN.md>)。 ## 构建和本地插件包 默认源码及根 `package-lock.json` 精确锁定 rc.2,标准源码安装入口为 `npm ci`。发布构建使用独立 SDK、独立 staging 和真实 REA 编译。实际 SDK 清单、完整 pnpm 锁与必要 workspace overrides 固定在 `host/build-inputs/{rc1,021rc2}/`;这些 SDK 输入保留原构建使用的字节,两个宿主均使用 Electron `44.7.0`。REA 的独立清单与完整锁固定在 `host/build-inputs/frontend/`。 `scripts/build-targets.mjs` 从公开输入创建本地 SDK 和前端工作目录,始终使用 `--frozen-lockfile --ignore-scripts`,缺锁、清单不匹配或输入 SHA-256 不匹配即失败,不自动重新解析。构建前还核对安装后的完整解析锁和全部实际 DSH 包版本,拒绝混入另一宿主版本。固定输入的 SHA-256 见 `host/build-inputs/manifest.json`;包内 `lib/host-build.json` 与输出目录 `build.json` 保存所用输入哈希、实际 DSH 包清单与安装解析锁哈希,可直接对照公开源码。 ```sh # NODE 为 Node >=22.19,PNPM 为 pnpm 11.28.5 的 pnpm.mjs 入口。 OPENCU_PNPM="$PNPM" "$NODE" scripts/build-targets.mjs ``` 默认构建当前 `rc1` 与 `021rc2` 两个宿主,也可在命令末尾只指定其中一项。旧 `rc2`(DSH 0.2.0-rc.2)与 `alpha3` 的固定 SDK 输入保留,需显式指定对应 target;不在默认构建中重新构建旧宿主。 本地复核可设置 `OPENCU_BUILD_ROOT` 和 `OPENCU_PACKAGE_OUT` 指向独立构建与输出目录,保留既有发行工件。 需要 rc.1 源码变体时,在单独源码 checkout 中执行以下命令;该入口同步宿主清单、完整 npm 根解析图、客户端目标与扩展包版本。rc.1 根 npm 锁针对其声明重新生成,与发布构建的 SDK pnpm 锁分别保存;只替换根版本字段不能得到有效变体。恢复 rc.2 时使用 `021rc2`。 ```sh "$NODE" scripts/prepare-host-source.mjs rc1 . npm ci ``` 此修复提供后续源码与构建的完整可复核输入;已发布旧 tag 的锁文件保持历史原状。 输出为工作区 `builds/opencu/021rc2/opencu-2.0.1-dsh.0.2.1-rc.2.tgz`(本次最新宿主工件;旧宿主工件分别保留,不混装)。每份包包含共用完整 core/native/browser/extension、独立重建的 REA client、真实 macOS arm64 helper 和宿主补丁;不会捆绑 SDK、node_modules、研究原件或用户数据。打包入口通过 `scripts/native-package-modes.mjs` 恢复 npm/pnpm 归一化移除的 helper 执行位;不改签名或二进制字节,实际解包后必须可执行。 桌面宿主必须在主代理拥有的匹配源码副本执行 `"$NODE" opencu/host/apply.mjs /absolute/owned/dsh-source`。补丁验证目标版本/框架,预检所有修改,拒绝只读 source cache 和安装目录;新增能力只属于显式 OpenCU lease,普通 DSH guest 权限/下载/导航策略保持原样。然后在该宿主副本安装 `electron-chrome-extensions@4.9.0`,更新自己的锁,重建受影响 Host、Client、Web、desktop main 和对应 sandboxed preloads(含 browser surface preload),再按宿主流程打包。不能只装插件或重建 desktop main:通用 accessory 槽及浏览器桥也需要相应 Client 和 preload 工件。 打包前运行 `node scripts/check-desktop-build.mjs /absolute/extracted/app`,用当前客户端兼容检查核验实际主窗口及浏览子窗口 preload;缺少能力时拒绝打包。此检查只核验编译后的桥接接口,宿主操作及前端启动仍须用匹配安装包定点验证。 包只带有效 Host 核心/后端、浏览器扩展、必要 native 来源/helper 和说明。REA 研究原件留在仓库,不把整份研究材料、开发依赖、隔离数据、token、Node 或浏览器塞进发行包。默认只运行少量明确 core 回归;真实隔离 DSH smoke 可选执行: ```sh OPENCU_DSH_SMOKE=1 node --test test/dsh.test.mjs ``` 该检查通过真实 DSH profile launcher、ToolRuntime 和 LocalAttachmentStore 调用插件;不连接预先启动的 OpenCU 服务,不修改当前工作 DSH。本仓库不执行公开发布或日常部署。