diff --git a/FEATURES.md b/FEATURES.md index 3f02bb44..22991dae 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -330,7 +330,7 @@ records are listed separately and do not introduce another feature status. failure, Agent `SIGKILL`/restart, and recovery from a reopened Desktop SQLite database. Successful tool START/END events appear exactly once, with no Prompt, provider, or tool replay observed. The current Agent source lock is - `0.15.0`, matching the current Desktop release candidate `0.15.0`; formal + `0.15.0`, paired with the current Desktop release candidate `0.15.1`; formal publication status follows the separate Agent and Desktop release channels. Previous macOS validation covered native package installation, detached lifecycle, Attach, real Ask/Execute, and cancellation of tools in @@ -910,14 +910,14 @@ records are listed separately and do not introduce another feature status. ### Open source, builds, and releases -- The current Desktop and Agent candidates are both `0.15.0`, with +- The current Desktop candidate is `0.15.1` and the Agent candidate is `0.15.0`, with OpenCode pinned to `1.18.29` and Continue to `1.5.47`. Publication status follows the independent Desktop and Agent release channels. -- Desktop `0.15.0` adds Obsidian integration, conversation capture into Magic +- Desktop `0.15.1` carries forward the unpublished `0.15.0` Obsidian integration, conversation capture into Magic Notes, and preview local native clients, plus knowledge, Supervisor, and UI fixes. Schema 48 adds conversation-source metadata; older clients require a complete pre-upgrade backup for rollback. Agent `0.15.0` requires Desktop - `0.15.0`, retains Node `24.19.0`, and updates remote telemetry opt-out and + `0.15.1`, retains Node `24.19.0`, and updates remote telemetry opt-out and disabled Continue update-check behavior without adding remote client shortcuts. - Desktop `0.13.15` carries forward the unpublished `0.13.14` changes to paged Supervisor reviews and resume behavior, native ripgrep searches, to-do layouts, and explicit remote follow-up after an diff --git a/FEATURES.zh-CN.md b/FEATURES.zh-CN.md index c4ef9d71..0d43af1d 100644 --- a/FEATURES.zh-CN.md +++ b/FEATURES.zh-CN.md @@ -52,7 +52,7 @@ - [x] **高效直连模型工作区工具**:直连模型使用随包 ripgrep 的原生参数发现文件和搜索内容,按行分页读取大型 UTF-8 文件,并在 Execute 中应用多文件补丁。Ask 限于工作区只读搜索和读取,Execute 按当前账号权限搜索;用户无需另行安装 ripgrep。搜索保留原生输出与退出码,大结果通过 `output_read` 完整续读,可处理的参数错误允许模型调整后继续。 - [x] **长上下文与工具输出分页**:历史消息、长回复和文档解析文本不再采用旧的固定截断限制。直连模型可分页续读已保存的搜索、命令和 Subagent 输出;所选模型的上下文窗口及传输边界仍然有效。 - [x] **OpenCode 与 Continue**:使用隔离子进程、环境变量白名单、统一配置、取消、启动与控制请求时限、有界流式输出和活动记录;共享进程回收逻辑保留 Windows 完整进程树终止,并对采用独立进程组的 POSIX 子进程执行组回收。聊天状态检查使用一次性探测 Runtime 并在返回后立即回收,不进入按模型与项目隔离的执行 Runtime 缓存;本机 GoodBuddy 管理的 OpenCode 在该被动检查中只确认所选路径和模型凭据,不启动用后即毁的 Server,设置中的显式连接测试和原生能力清单仍执行完整启动及健康检查,原生能力清单随后与首次实际请求复用同一个执行 Runtime,不再重复冷启动;执行 Runtime 仍按项目复用,但 OpenCode 配置依赖和按内容摘要生成的 Skill 快照在所有本机项目间全局复用,会话数据、工具输出及请求级 MCP 状态继续隔离。本机 GoodBuddy 管理的 OpenCode 只对同一会话内的请求保持顺序,不同会话即使属于同一项目也可并行,不同项目同样可并行;请求级动态 MCP 通过默认通配禁用和当前请求精确放行保持隔离。托管的本机和远端 OpenCode 跳过界面未使用的自动 Git 快照,避免同步差异计算拖住会话,文件工具、子代理与工作区 Git 差异保留;Continue 持续排空不用展示的宿主 stdout,防止控制台输出填满管道。这些调整随新启动的 Runtime 生效,不改写外部 OpenCode Server 配置。OpenCode 与 Continue 不再按固定工具调用数或活动数中止单次运行,已观察到的工具与 Subagent 活动全部保留在本地会话,不再在原生任务继续执行时丢弃后续详情。交互提问只由前台对话回答,定时任务、远程通道和委派等后台执行遇到提问时会立即失败并提示改为前台运行,避免无限等待。本机 OpenCode 启动时直接使用已选择的绝对路径,不预先检查文件或运行 `--version`,路径失效时由实际启动返回错误;托管 Linux ARM Host 激活 Runtime 时复用准备阶段已验证的 registry 和 manifest,不重新哈希或检查完整 OpenCode 二进制。 -- [x] **托管 SSH OpenCode 与 Continue**:该能力由“设置 > 平台功能”中的独立“远程项目”页签控制并默认关闭;关闭时不影响本地项目、普通桌面功能或桌面发布。支持 Linux x64、Linux arm64 和 macOS arm64 Host,不支持 Intel Mac;通过 SSH 按需启动 detached Agent,不要求开机服务。Ask 在 Runtime 工具边界保持只读,Execute 使用所选 SSH 账号的完整权限。Agent 持有 Prompt、模型和工具轮次、Runtime 进程及语义 transcript,Desktop 断开后重新 Attach 原操作而不重发 Prompt;模型凭据仅在当前操作内存中使用,不写入 Renderer、SSH 参数、环境或磁盘。当前 Agent 可以常驻,更新替换的旧 Agent 排空活动任务后退出。任务完成后回收空闲 Runtime,共享进程仍有任务或待答问题时保留;下一轮重建 Runtime 并恢复对话历史。设置页按平台和架构显示包状态,用户显式下载、更新或导入/导出包含 Agent、固定 Node、OpenCode 和 Continue 的签名 `.gbagent`;三个目标共用一个签名 catalog,继续校验签名和 SHA-256。Continue 需要更新后的联合包与配套 Desktop。首次发布混合 catalog 前需先升级 Desktop,旧版 Linux-only 目录读取器无法解析包含 Darwin 的目录。历史 macOS OpenCode 原生安装、生命周期、Attach、真实 Ask/Execute 和独立进程组工具取消已在真实 Host 验证,不代表 Continue 的同等实机覆盖。当前 Agent 源码锁与 Desktop 候选均为 `0.15.0`;详细运行边界和 Linux 历史回归证据见[远程主机技术设计](docs/features/remote-host/technical-design.md),正式发布状态以独立发布渠道为准。 +- [x] **托管 SSH OpenCode 与 Continue**:该能力由“设置 > 平台功能”中的独立“远程项目”页签控制并默认关闭;关闭时不影响本地项目、普通桌面功能或桌面发布。支持 Linux x64、Linux arm64 和 macOS arm64 Host,不支持 Intel Mac;通过 SSH 按需启动 detached Agent,不要求开机服务。Ask 在 Runtime 工具边界保持只读,Execute 使用所选 SSH 账号的完整权限。Agent 持有 Prompt、模型和工具轮次、Runtime 进程及语义 transcript,Desktop 断开后重新 Attach 原操作而不重发 Prompt;模型凭据仅在当前操作内存中使用,不写入 Renderer、SSH 参数、环境或磁盘。当前 Agent 可以常驻,更新替换的旧 Agent 排空活动任务后退出。任务完成后回收空闲 Runtime,共享进程仍有任务或待答问题时保留;下一轮重建 Runtime 并恢复对话历史。设置页按平台和架构显示包状态,用户显式下载、更新或导入/导出包含 Agent、固定 Node、OpenCode 和 Continue 的签名 `.gbagent`;三个目标共用一个签名 catalog,继续校验签名和 SHA-256。Continue 需要更新后的联合包与配套 Desktop。首次发布混合 catalog 前需先升级 Desktop,旧版 Linux-only 目录读取器无法解析包含 Darwin 的目录。历史 macOS OpenCode 原生安装、生命周期、Attach、真实 Ask/Execute 和独立进程组工具取消已在真实 Host 验证,不代表 Continue 的同等实机覆盖。当前 Agent 源码锁为 `0.15.0`,配套 Desktop 候选为 `0.15.1`;详细运行边界和 Linux 历史回归证据见[远程主机技术设计](docs/features/remote-host/technical-design.md),正式发布状态以独立发布渠道为准。 - [x] **SSH Host 手动环境准备源码链路**:Host Key、认证和系统探针成功后先保存 Host,并只读探测共享 Agent/Runtime;保存 Host、打开项目都不自动安装。Host 卡片只有一个按版本事实显示“安装远程环境”“更新远程环境”或“重新安装”的主按钮,次级 SegmentedControl 选择默认且不持久化的“自动”、Host 下载或 GoodBuddy 传输;“版本匹配”badge 不等同环境健康。自动模式只在 operation/prepare 前探测并择一,显式选择保持有效,任何 prepare、commit 或 adoption 失败都不跨 acquisition 自动 fallback。两种方式把同一签名 compound `.gbagent` 交付到固定 staging 后,共用 control-plane prepare、commit、Agent activate/health、Runtime activate、finalize 与显式 cleanup。GoodBuddy 路径可在同一次操作下载并验证缺失候选、缓存并取得 lease,再有界流式 SFTP 上传一个归档和其中已验证的 bootstrap Node,不把约 294 MiB 整包读入 Main `Buffer`;Host 在解包时完成一次完整 payload 校验。未完成操作只记录暂存 cleanup 所需的 operation ID;下次更新尽力清理旧暂存后重新 prepare,不保存远端 metadata 副本,也不让 cleanup 失败阻塞新更新或回滚健康环境。已有项目在实际使用 Workspace/Runtime 时按需解析 Host current identity 并执行固定 `attach-or-bootstrap`,注册后的 health、capabilities 和 prompt 启动不扫描完整 payload。详见 [设计说明](./docs/features/remote-host/environment-provisioning-technical-design.md)。 - **SSH Host 环境准备验证记录**:当前源码已在隔离 Linux x64 环境通过包安装、Ask/Execute、原生子代理工作区外写入、重连及 stop/bootstrap。现有记录未覆盖 Host 卡片的完整 GitHub、北京镜像、Linux x64/arm64、取消和离线 GoodBuddy 传输矩阵,候选 CI/原生打包与系统休眠唤醒也尚未验证;开发记录不代表版本已经发布。 - [x] **远程工作区与长任务更新**:Agent `0.11.23` 新增远程文件/Git 管理和可选模型限额,随包 Runtime 不再施加固定十分钟 Prompt 时限;无限请求时长仍保留独立连接超时。请先升级 Desktop 至 `0.12.11`,再下载并更新 Host 上的 Agent。 @@ -152,8 +152,8 @@ ### 开源、构建与发布 -- 当前 Desktop 与 Agent 候选均为 `0.15.0`,OpenCode 固定为 `1.18.29`、Continue 为 `1.5.47`;正式发布状态以独立发布渠道为准。 -- Desktop `0.15.0` 新增 Obsidian 接入、会话采集到魔法笔记和预览中的本地原生客户端,并修复知识库、监督者与界面操作。schema 48 新增会话来源元数据,回退旧客户端需使用升级前完整备份。Agent `0.15.0` 要求 Desktop `0.15.0`,保留 Node `24.19.0`,补齐远程遥测关闭配置及 Continue 跳过已关闭的更新检查,不新增远程客户端快捷入口。 +- 当前 Desktop 候选为 `0.15.1`,Agent 候选为 `0.15.0`,OpenCode 固定为 `1.18.29`、Continue 为 `1.5.47`;正式发布状态以独立发布渠道为准。 +- Desktop `0.15.1` 承接未发布的 `0.15.0` Obsidian 接入、会话采集到魔法笔记和预览中的本地原生客户端,并修复知识库、监督者与界面操作。schema 48 新增会话来源元数据,回退旧客户端需使用升级前完整备份。Agent `0.15.0` 要求 Desktop `0.15.1`,保留 Node `24.19.0`,补齐远程遥测关闭配置及 Continue 跳过已关闭的更新检查,不新增远程客户端快捷入口。 - Desktop `0.13.15` 承接未发布的 `0.13.14`,改善监督者分页回顾与续跑、ripgrep 原生搜索、待办布局和远程结果未知后的显式续聊。数据库升级至 schema 47,回退旧客户端需使用升级前完整备份。 - Desktop `0.13.16` 分离结构升级与旧历史回收,移除回顾固定时长自动暂停,并改善监督者布局。相较 `0.13.15` 不新增 schema 变更,也不要求更新 Agent。 - Agent `0.13.3` 回收空闲 Runtime,并使被替换的 Agent 等已有工作结束后退出;复合包要求 Desktop `0.13.12`,请先升级 Desktop,再更新 Host 环境。Node 保持 `24.19.0`。 diff --git a/build/native-dsh-resources.cjs b/build/native-dsh-resources.cjs index f382b71a..efe32fb6 100644 --- a/build/native-dsh-resources.cjs +++ b/build/native-dsh-resources.cjs @@ -21,7 +21,7 @@ async function prepareNativeDshResources(projectDir, platform, architecture, dow if (visited.has(directory)) return; visited.add(directory); const manifest = JSON.parse(await readFile(join(directory, 'package.json'), 'utf8')); - await cp(directory, join(target, relative(projectDir, directory)), { recursive: true }); + await cp(directory, join(target, relative(projectDir, directory)), { recursive: true, verbatimSymlinks: true }); const optional = manifest.optionalDependencies ?? {}; for (const name of Object.keys({ ...manifest.dependencies, ...manifest.peerDependencies, ...optional })) { const dependency = locate(name, directory); diff --git a/docs/development/release-preparation-0.15.0.md b/docs/development/release-preparation-0.15.0.md index 31ff3524..22d23e83 100644 --- a/docs/development/release-preparation-0.15.0.md +++ b/docs/development/release-preparation-0.15.0.md @@ -1,15 +1,17 @@ -# Desktop 与 Agent 0.15.0 发布准备 +# Desktop 0.15.1 与 Agent 0.15.0 发布准备及恢复 -日期:2026-09-27。候选验证通过,不代表已发布。 +日期:2026-09-27 至 2026-09-28。原 Desktop 0.15.0 标签构建失败, +用户已选择 Desktop 0.15.1 与 Agent 0.15.0 恢复发布;验证通过不代表已发布。 ## 范围与批准 - 用户要求 Desktop 与 GoodBuddy Agent 同步升级至 `0.15.0`,明确不构建 LoongArch。 - 源码基线:`0f8bf405db7ca21a85cc70009ad083eb837e1e03`。 - Desktop 差异基线为已发布的 `v0.13.16`,Agent 为 `agent-v0.13.4`。 -- 准备两个独立标签 `v0.15.0` 与 `agent-v0.15.0`;尚未创建或推送。 +- `v0.15.0` 已创建并推送,保持不可变;恢复使用 `v0.15.1`, + `agent-v0.15.0` 尚未创建或推送。 - 用户已批准两份完整双语说明,正式正文仅维护在 - [Desktop 发布资源](../../resources/release-notes.json) 的 `0.15.0` 条目与 + [Desktop 发布资源](../../resources/release-notes.json) 的 `0.15.1` 条目与 [Agent 发布资源](../../resources/agent-release-notes/0.15.0.md)。 已逐条核对写入文本与批准草案一致,本记录不再复制正文。 - `FEATURES.md` 与 `FEATURES.zh-CN.md` 已核对本轮能力、限制和候选版本。 @@ -195,6 +197,31 @@ Windows 原生 Runtime 聚焦用例、类型检查和 lint 通过。 冻结 tree `42a92450ad7d9ebc50167c7946bff53cff2889e6` 的 Linux 全量为 **5,371 通过、51 跳过、0 失败**,耗时 748.67 秒。 +## Desktop 0.15.1 恢复 + +`65a21390debe0dd04871417a363a9d87a29339e6` 的主分支 +[CI 36347785076](https://github.com/mesalogo/goodbuddy/actions/runs/36347785076) +验证与生产构建全部通过后,两个远端创建了不可变 `v0.15.0`。 +其正式 [CI 36348938269](https://github.com/mesalogo/goodbuddy/actions/runs/36348938269) +两个 macOS 目标在 `codesign --verify --deep --strict` 失败, +报 `invalid destination for symbolic link in bundle`;GitHub Release 未发布。 + +Host 隔离 fixture 经实际 DS 资源准备和 electron-builder 文件复制, +复现相对 npm CLI 链接变成原构建目录的绝对链接。不是签名凭据或测试超时问题。 +用户明确选择 **Desktop 0.15.1 + Agent 0.15.0** 恢复,不移动或复用旧标签。 +修正仅让复制保留相对链接;新增回归移除原安装与暂存目录后验证最终链接仍可读。 +该路径不用于 Agent,远程 Runtime 与 Agent 源码未改,不增加真实模型调用。 +未发布 Desktop 0.15.0 的双语条目迁移到 0.15.1,保留全部功能说明; +Agent 版本不变,其包元数据从新版 Desktop manifest 派生最低版本 0.15.1, +两份说明和功能矩阵同步这个升级顺序。 + +原标签最终 Windows/Linux 四个目标成功,两个 macOS 失败,发布任务跳过, +公开 Latest 仍为 `v0.13.16`。 +Windows 打包相关测试 45 通过、1 个 POSIX 链接用例跳过,类型、lint、 +发布说明校验通过。冻结 tree `99d3271174a68922fbf0a1a4cfbadbb85a6067c8` +在 Linux Host 全量 **5,372 通过、51 跳过、0 失败**,耗时 745.88 秒; +新增符号链接回归实际执行通过。提交仅追加验证记录,恢复候选 CI 与 macOS 签名待验证。 + ## 数据、兼容与验收边界 Desktop schema 48 新增魔法笔记会话来源元数据。升级前应备份完整用户数据, @@ -202,7 +229,7 @@ Desktop schema 48 新增魔法笔记会话来源元数据。升级前应备份 本次发布准备不修改数据库迁移实现,也不直接改写用户数据库。 Agent 发布工作流以候选 `package.json` 设置最低 Desktop 版本,因此 -Agent 0.15.0 包要求 Desktop 0.15.0;Node、OpenCode、Continue 锁不变。 +Agent 0.15.0 包要求 Desktop 0.15.1;Node、OpenCode、Continue 锁不变。 Agent 与 Desktop 独立发布,Agent 不替代 Desktop Latest。 原生客户端的远程 coordinator、托管标准 Node 交付整合、Continue/OpenCode diff --git a/docs/features/assistant-workbar/progress.md b/docs/features/assistant-workbar/progress.md index 61732c14..6066552c 100644 --- a/docs/features/assistant-workbar/progress.md +++ b/docs/features/assistant-workbar/progress.md @@ -1,5 +1,18 @@ # 工作栏实现与验证进度 +## 2026-09-28:DS Web 资源符号链接与 macOS 发布恢复 + +Desktop `v0.15.0` 原生构建中,两个 macOS 目标均因包外符号链接未通过签名校验。 +Linux Host 隔离 fixture 调用实际 DS 资源准备及 electron-builder 文件复制后, +确认相对 CLI 链接变成构建目录的绝对路径。复制现使用 `verbatimSymlinks`, +保留 npm 相对链接;回归移除原始安装和暂存目录后,仍须能读取最终包内链接目标。 +Windows 打包聚焦回归 45 项通过,1 项 POSIX 符号链接测试不适用于 Windows; +类型、lint 和 0.15.1 双语说明校验通过。完整冻结快照在 Linux Host 全量 +5,372 通过、51 跳过,新增符号链接用例实际执行通过;macOS 原生签名仍待发布 CI。 +这只改变 Desktop 的本地 DS Web 资源准备,不改变 Agent 的独立 Runtime 包装、 +模型桥或启动控制,模型调用 0 次。恢复版本及最终证据见 +[发布恢复记录](../../development/release-preparation-0.15.0.md)。 + ## 2026-09-27:窄栏添加入口 “+”已移出页签滚动容器;左右箭头和溢出测量使用扣除添加按钮宽度后的页签区域。 diff --git a/docs/features/assistant-workbar/runtime-native-client-proposal.md b/docs/features/assistant-workbar/runtime-native-client-proposal.md index 7f0b2728..0468a158 100644 --- a/docs/features/assistant-workbar/runtime-native-client-proposal.md +++ b/docs/features/assistant-workbar/runtime-native-client-proposal.md @@ -193,6 +193,11 @@ Renderer 仅提交当前会话或项目标识及选择标识。Main 重新解析 共享接口应返回可供界面使用的启动状态、终端标识或 DS 服务标识。外部 URL 由 Main 打开, 不允许 Renderer 提交任意程序、命令行、凭据或任意服务地址。错误保留组件、阶段和脱敏原因。 +DS Web 随包依赖通过 `build/native-dsh-resources.cjs` 保留 npm 的嵌套布局。 +复制时原样保留相对符号链接,不能将 CLI 链接改写成构建机的绝对路径; +经过 electron-builder 资源复制后,即使移除原始安装和暂存目录,链接仍须能解析到包内文件。 +这一交付约束覆盖 FR-9;不改变 Agent 的独立 Runtime 打包路径。 + ### 6.2 Continue 与 OpenCode Continue 使用已解析的标准 Node 启动固定版本 CLI,不能直接假定 Electron 的 diff --git a/package-lock.json b/package-lock.json index ef4307a1..552bdf49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "goodbuddy", - "version": "0.15.0", + "version": "0.15.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "goodbuddy", - "version": "0.15.0", + "version": "0.15.1", "license": "0BSD", "dependencies": { "@agentclientprotocol/sdk": "0.25.1", diff --git a/package.json b/package.json index 5b8ae58e..41997579 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "goodbuddy", - "version": "0.15.0", + "version": "0.15.1", "private": true, "description": "Secure desktop AI workspace with controlled Agent Runtimes", "desktopName": "GoodBuddy", diff --git a/resources/agent-release-notes/0.15.0.md b/resources/agent-release-notes/0.15.0.md index 0c82dd0b..f11a49be 100644 --- a/resources/agent-release-notes/0.15.0.md +++ b/resources/agent-release-notes/0.15.0.md @@ -7,7 +7,7 @@ ## 使用说明 -- 请先升级 **GoodBuddy Desktop 0.15.0**,再下载并更新 Host 环境。此版本不提供远程原生客户端快捷入口,已有进程不会热更新。 +- 请先升级 **GoodBuddy Desktop 0.15.1**,再下载并更新 Host 环境。此版本不提供远程原生客户端快捷入口,已有进程不会热更新。 - 提供 Linux x64、Linux arm64 和 macOS arm64 包,继续使用 Node **24.19.0**、OpenCode **1.18.29** 和 Continue **1.5.47**。 - Ask 保持只读,Execute 使用所选 SSH 账号的权限。Agent 独立发布,不替代桌面端 Latest。 @@ -20,6 +20,6 @@ ## Usage Notes -- Upgrade to **GoodBuddy Desktop 0.15.0** before downloading and updating the Host environment. This release does not provide remote native-client shortcuts, and existing processes are not hot-updated. +- Upgrade to **GoodBuddy Desktop 0.15.1** before downloading and updating the Host environment. This release does not provide remote native-client shortcuts, and existing processes are not hot-updated. - Packages cover Linux x64, Linux arm64, and macOS arm64, retaining Node **24.19.0**, OpenCode **1.18.29**, and Continue **1.5.47**. - Ask stays read-only and Execute uses the selected SSH account's permissions. Agent releases remain separate and do not replace Desktop Latest. diff --git a/resources/release-notes.json b/resources/release-notes.json index a3b465f6..8a8c99d3 100644 --- a/resources/release-notes.json +++ b/resources/release-notes.json @@ -2,7 +2,7 @@ "formatVersion": 1, "releases": [ { - "version": "0.15.0", + "version": "0.15.1", "releasedAt": "2026-09-28", "notes": { "zh-CN": { @@ -24,7 +24,7 @@ ], "notices": [ "首次启动会将数据库升级至 schema 48。升级前请备份完整用户数据,包括数据库及相邻 `notes/` 目录;旧客户端不能打开升级后的数据库,回退需使用升级前备份。", - "远程用户请先升级 Desktop 至 **0.15.0**,再下载并更新 Host 上的 Agent **0.15.0**。已有进程不会热更新,Agent 独立发布,不替代桌面端 Latest。", + "远程用户请先升级 Desktop 至 **0.15.1**,再下载并更新 Host 上的 Agent **0.15.0**。已有进程不会热更新,Agent 独立发布,不替代桌面端 Latest。", "原生客户端仍为预览,其 Skills/MCP 与 GoodBuddy 会话能力并非完全等价;Continue 和 DS Web 首次启动可能需要准备标准 Node 环境。真实模型请求可能产生费用,其他平台与安装包验收以本次发布流程结果为准。" ] }, @@ -47,7 +47,7 @@ ], "notices": [ "First launch upgrades the database to schema 48. Back up complete user data, including the database and adjacent `notes/` directory, before upgrading; older clients cannot open the upgraded database, so rollback requires a pre-upgrade backup.", - "Remote users should upgrade Desktop to **0.15.0** before downloading and updating Hosts to Agent **0.15.0**. Existing processes are not hot-updated, and the independent Agent release does not replace Desktop Latest.", + "Remote users should upgrade Desktop to **0.15.1** before downloading and updating Hosts to Agent **0.15.0**. Existing processes are not hot-updated, and the independent Agent release does not replace Desktop Latest.", "Native clients remain a preview and do not offer full parity with GoodBuddy conversation Skills/MCP capabilities; Continue and DS Web may need to prepare a standard Node environment on first launch. Real model requests may incur charges, and other-platform and packaged-app acceptance depend on this release's validation results." ] } diff --git a/tests/native-dsh-packaging.test.ts b/tests/native-dsh-packaging.test.ts index 20a5f401..1ca07523 100644 --- a/tests/native-dsh-packaging.test.ts +++ b/tests/native-dsh-packaging.test.ts @@ -1,4 +1,4 @@ -import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { mkdirSync, mkdtempSync, readFileSync, readlinkSync, rmSync, symlinkSync, writeFileSync } from 'node:fs' import { createRequire } from 'node:module' import { tmpdir } from 'node:os' import { dirname, join, resolve } from 'node:path' @@ -6,6 +6,7 @@ import { expect, it } from 'vitest' const require = createRequire(import.meta.url) const { getFileMatchers, copyFiles } = require('app-builder-lib/out/fileMatcher') +const { prepareNativeDshResources } = require('../build/native-dsh-resources.cjs') it('copies DS dependencies and nested assets through the actual builder matcher', async () => { const root = mkdtempSync(join(tmpdir(), 'goodbuddy-dsh-packaging-')) @@ -33,3 +34,49 @@ it('copies DS dependencies and nested assets through the actual builder matcher' rmSync(root, { recursive: true, force: true }) } }) + +it.skipIf(process.platform === 'win32')('keeps npm CLI symlinks inside the relocated DS bundle', async () => { + const root = mkdtempSync(join(tmpdir(), 'goodbuddy-dsh-symlinks-')) + const write = (file: string, value: unknown): void => { + const target = join(root, file) + mkdirSync(dirname(target), { recursive: true }) + writeFileSync(target, JSON.stringify(value)) + } + try { + write('node_modules/@deepseek-ai/dsh/package.json', { name: '@deepseek-ai/dsh', dependencies: { + child: '1.0.0', sharp: '1.0.0', '@koromix/koffi-linux-x64': '1.0.0' + } }) + write('node_modules/@deepseek-ai/dsh/node_modules/child/package.json', { name: 'child', version: '1.0.0' }) + write('node_modules/@deepseek-ai/dsh/node_modules/child/bin.js', 'fixture') + const sourceBin = join(root, 'node_modules/@deepseek-ai/dsh/node_modules/.bin') + mkdirSync(sourceBin, { recursive: true }) + symlinkSync('../child/bin.js', join(sourceBin, 'child')) + write('node_modules/sharp/package.json', { optionalDependencies: { + '@img/sharp-linux-x64': '1.0.0', '@img/sharp-libvips-linux-x64': '1.0.0' + } }) + write('node_modules/sharp/node_modules/@img/sharp-linux-x64/package.json', { version: '1.0.0' }) + write('node_modules/sharp/node_modules/@img/sharp-libvips-linux-x64/package.json', { version: '1.0.0' }) + write('node_modules/@koromix/koffi-linux-x64/package.json', { version: '1.0.0' }) + write('node_modules/koffi/package.json', { version: '1.0.0' }) + write('package-lock.json', { packages: {} }) + const unexpectedDownload = (): never => { throw new Error('Fixture must not download native packages') } + await prepareNativeDshResources(root, 'linux', 'x64', unexpectedDownload, unexpectedDownload) + const link = 'node_modules/@deepseek-ai/dsh/node_modules/.bin/child' + expect(readlinkSync(join(root, '.runtime-resources/dsh-x64', link))).toBe('../child/bin.js') + const config = JSON.parse(readFileSync(resolve('package.json'), 'utf8')) + const resource = config.build.extraResources.find((entry: { to: string }) => entry.to.startsWith('runtimes/dsh')) + const destination = join(root, 'output/resources') + await copyFiles(getFileMatchers({ extraResources: [resource] }, 'extraResources', destination, { + defaultSrc: root, globalOutDir: join(root, 'output'), customBuildOptions: {}, + macroExpander: (value: string) => value.replaceAll('${arch}', 'x64') + })) + // No original installation or staging directory may be needed after shipping. + rmSync(join(root, 'node_modules'), { recursive: true, force: true }) + rmSync(join(root, '.runtime-resources'), { recursive: true, force: true }) + const packagedLink = join(destination, 'runtimes/dsh', link) + expect(readlinkSync(packagedLink)).toBe('../child/bin.js') + expect(readFileSync(packagedLink, 'utf8')).toBe(JSON.stringify('fixture')) + } finally { + rmSync(root, { recursive: true, force: true }) + } +})