31 KiB
GoodBuddy 开发与构建
环境要求
- Node.js 24,或当前锁定依赖明确支持的 Node.js 版本
- npm
- Windows、Linux 或 macOS
安装锁定依赖:
npm ci
不要将 API Key、访问令牌、私有模型地址或本地数据库提交到仓库。
本地开发
启动开发环境:
npm run dev
预览已经生成的生产构建:
npm run build
npm start
质量验证
提交或打包前运行:
npm test
npm run typecheck
npm run lint
监听模式:
npm run test:watch
真实 Runtime 端到端测试默认关闭。配置兼容模型凭据后,显式启用:
GOODBUDDY_RUN_RUNTIME_E2E=1 npm test -- src/main/agent/runtime-e2e.manual.test.ts
OpenCode/Continue 用例默认读取 dist/harness-package-probe/win-unpacked;也可用
GOODBUDDY_E2E_PACKAGED_ROOT 指定其他已解包应用目录。该测试可能发起真实外部模型
调用。测试不会输出 API Key,文件操作在临时工作区中执行。文件包含经 Main 回环
broker 调用已分配自定义 MCP 的真实 OpenCode 和 Continue 用例。
生产构建
生成 Electron Main、Preload 和 Renderer 生产文件:
npm run build
中间构建输出位于 out。该目录为生成内容,应修改源文件后重新构建,不要直接编辑。
图标生成
应用图标源文件位于 icons。修改源图或图标处理逻辑后运行:
npm run icons
脚本会精确裁出亮色和深色圆角卡片,清理圆角外侧背景并使用适合主题的
边缘颜色生成透明抗锯齿,避免缩放后出现白边。它会统一更新 build 中的
PNG / ICO、Renderer 图标,以及官网使用的亮色和深色品牌图标。任务栏和
托盘图标保持透明背景。
平台打包
当前平台默认包
npm run dist
Windows
生成 Windows NSIS 安装包:
npm run dist:win
生成用于本机调试的 Windows 便携目录:
npm run portable
macOS
生成 x64 和 arm64 DMG:
npm run dist:mac
Linux
同时生成 Linux x64 和 arm64 包:
npm run dist:linux
只生成指定架构:
npm run dist:linux:x64
npm run dist:linux:arm64
每个 Linux 架构生成:
deb:适用于麒麟、统信 UOS 等 Debian 系桌面。rpm:适用于 Fedora、RHEL、Rocky Linux、openEuler 等 RPM 系桌面。- AppImage:适用于免安装验证和便携运行。
打包产物位于 dist。
所有桌面打包入口通过同一个 electron-builder 启动脚本运行。正常日志会隐藏
duplicate dependency references 信息摘要,因为它只表示同一已解析依赖被多条
生产依赖路径共享,并不要求构建操作者处理;调试日志仍会保留该摘要。Renderer 中
已由 Vite 打包的 @antv/g6 归类为构建期依赖,不会再作为独立 Node 模块复制进桌面
安装包。
以上 dist* 与 portable 命令用于普通本地桌面打包。它们会携带
agent-runtime-lock.json、remote-runtime-lock.json 与公开的
agent-release-keys.json,但所有桌面构建路径(包括本地 portable)都不会嵌入
可安装的远端 Agent 或远端 Runtime payload。没有使用托管 SSH 时,无需下载任何额外
组件,缺少远端包也不能阻塞普通桌面打包或发布。
“设置 > 平台功能 > 远程项目(技术预览)”列出用户数据目录中已经下载或导入的
Linux x64、Linux arm64 和 macOS arm64 复合 Agent 包。首次打开和手动刷新会从“关于与更新”所选来源读取
小型签名目录,显示本地版本、在线最新兼容版本以及 有更新 / 已是最新 / 未下载 / 校验失败;此检查不会下载 .gbagent。只有用户点击“下载”或“更新到”后才传输包,
用户也可导入或导出 .gbagent 离线包。
Main 在写入缓存前校验签名目录、目录签名、大小、SHA-256、桌面最低版本、Agent 协议、
内部 Agent/Runtime 签名和完整 payload。缓存位于 Electron userData 下的
remote-components/agent-packages,不暴露给 Renderer。
公钥 registry 的信任来自严格 schema、唯一 key ID、规范 Base64、有效 Ed25519 key、 production/test 环境和撤销列表,不来自 JSON 空白或换行。读取桌面资源、复合包和 Host 已有 registry 时接受等价 UTF-8 JSON 格式;向 Host 写入前统一重新序列化为确定性 LF JSON。签名目录、包描述符、manifest、签名和 payload 的原始字节校验保持不变,包下载 继续流式计算 SHA-256。
“SSH 主机 > 远程运行环境”只有一个按版本事实显示“安装远程环境”“更新远程环境”或 “重新安装”的主按钮。次级 SegmentedControl 选择默认且不持久化的“自动”、“Host 下载” 或“GoodBuddy 传输”:
- “Host 下载”使用签名目录中的固定 GitHub/北京镜像 URL、大小、SHA-256、 有界重定向链和固定 SSH prepare channel。GoodBuddy 桌面控制面通过该通道发送有界 one-shot installer;Host 校验目录绑定的归档大小和 SHA-256 后,由包内固定 Node 解码 并运行 installer。
- 显式“GoodBuddy 传输”在在线目录不可用,或本地已验证包版本不低于在线候选时优先使用
本地包,包括版本更高的离线导入;线上存在更新且可用时仍取得线上新包。需要下载时,
同一次用户操作会校验 SHA-256 与签名、缓存并取得 lease,再以有界流式 SFTP 上传一个
compound archive 和归档内已验证的 bootstrap Node;约 294 MiB 的整包不会一次读入
Main
Buffer,Host 仍再次校验。 - “自动”只在 operation/prepare 前执行 Host capability probe;直连明确可用才选择 Host 下载,否则选择 GoodBuddy 传输。显式选择保持有效,prepare、commit 或 adoption 失败后不跨 acquisition 自动 fallback。
两种 acquisition 都把同一签名 compound .gbagent 交付到固定 operation staging,随后
共用 control-plane prepare → commit → Agent activate/health → Runtime activate → finalize → cleanup。Main 只持久化清理 operation staging 所需的 operation ID,不保存
Agent/Runtime metadata 副本。操作中断或 adoption 失败后,下一次更新尽力清理旧暂存并
重新 prepare,不恢复或续写旧操作。确认 adoption 后显式 cleanup。Host 在 prepare
解包时完成一次完整 payload 校验;commit 只复核签名 metadata
并原子发布,Agent/Runtime 注册后的 health、capabilities 和 prompt 启动不再遍历或哈希
完整 payload。cleanup 失败不回滚已健康环境,也不阻塞下一次 fresh prepare。
进入“主机与远程执行”设置页只读取本地 Host 列表,不逐台连接。用户点击某台 Host 的
“刷新版本”后才执行远端版本和直连准备能力探测。项目切换只更新本地选择,不建立 SSH
连接;首次实际使用远程 Workspace 或 Runtime 时才建立连接。当前进程会复用该 Host 已确认
的 Agent/Runtime identity 和 Agent 连接,Host 编辑或环境更新会定向清空它们。
直连安装本身只依赖 SSH,不依赖已有 Agent。显式选择 Host 下载时由实际安装流程重新探测
并报告工具、权限、空间或来源不可达等原因;本次操作不会自动改用 GoodBuddy 传输。
新建远程项目弹窗也只读本地 Host 验证记录;目录浏览或保存才连接所选 Host。Host 卡片
在版本相同时仍允许显式重装;损坏的 GoodBuddy-owned 同 digest 目录会先隔离、替换并在
失败时恢复。直连 bootstrap 使用固定短命令 exec sh -s,脚本和有界输入经 stdin 发送,
避免 SSH command 长度限制。
新增 Host 只保存并探测,不提供自动安装开关;只有用户明确点击主按钮后才传输完整包。现有
package format v1 归档已经包含固定 Agent 和 Node,可直接用于 Host 直连;归档无需携带
agent/lib/package-installer.cjs,签名目录不提供也不检查额外的 bootstrap 能力元数据。
首次 bootstrap 不依赖已安装 Agent daemon。目标契约见
SSH Host 远程环境准备与直连下载设计。
删除 Host 或远程项目属于纯本地管理操作。Host 确认框列出全部关联项目记录并级联清理 本地数据库,但不连接 Host、不检查远端目录,也不删除远端文件;Host 设置文件和项目事务 的普通失败会回滚已写入的一侧。
Runtime 资源
桌面包会携带经过版本与完整性校验的 OpenCode、Continue 和 DSH 插件安装 Runtime:
- OpenCode 平台二进制来自
.runtime-resources/<arch>。 - Continue Runtime 来自锁定版本的
@continuedev/cli。 - DSH 插件安装使用精确锁定并从
app.asar解包的 npm CLI,通过当前 Electron 的 Node 模式运行;最终用户不需要另装 Node.js 或 npm。 - DSH 图片输入使用精确锁定的
@napi-rs/canvas完整解码 JPEG/PNG。通用包与目标平台、目标架构的 Skia 原生包必须从app.asar解包;当打包 Runner 的架构与目标架构不同时,发布脚本会根据 lockfile 的精确版本、下载地址和 integrity 临时暂存目标原生包,完成后清理。发布校验会检查版本、目标架构和 MIT 许可证。 - 打包钩子位于
build/runtime-hooks.cjs。
跨架构打包前,确认目标架构的 OpenCode 资源已经准备完成。不要用其他架构的二进制替代目标资源。
GoodBuddy 管理的本机 OpenCode 启动后会轮询已认证的 loopback health 接口,最多等待 30 秒。该窗口用于兼容 Linux ARM 等较慢环境;不要改回依赖 stdout 文案或 10 秒固定上限。 聊天中的 Runtime 切换只执行路径和模型凭据就绪检查,不为状态显示启动临时 Server。 设置中的显式连接测试、原生能力清单读取和首次实际请求仍会启动 OpenCode,并继续使用 上述完整健康检查。
GoodBuddy Agent 工件
面向用户发布的是按远端 Host 架构区分的复合 .gbagent,每个包同时包含签名
Agent、固定 Node 和当前桌面源码维护并适配的签名 OpenCode Runtime。Runtime 不建立
独立的用户版本流。远程直连所需的有界 one-shot installer 由桌面控制面打包并通过固定
SSH prepare channel 发送,不是 .gbagent 的必需条目。Host 按签名目录校验完整归档
大小和 SHA-256 后,包内固定 Node 解码并运行 installer。两种 acquisition 都在固定
operation staging 中执行同一 prepare/commit;本地 pending record 只保存清理暂存所需的
operation ID。失败或中断后的下一次更新尽力清理旧暂存并重新 prepare,不保存或恢复
Agent/Runtime metadata 副本。生产构建命令为:
node build/agent-package.cjs build \
--arch <x64|arm64> \
--minimum-desktop-version <desktop-version> \
--node-archive <locked-node-runtime.tar.gz> \
--runtime-archive <locked-opencode-package.tgz> \
--output goodbuddy-agent-<agent-version>-linux-<arch>.gbagent
底层 agent:build/import/verify 与 remote-runtime:build/import/verify 命令继续用于
开发、签名和复合包组装验证;其 .agent-resources 与
.remote-runtime-resources 输出都是不提交的生成内容,也不会被任何桌面打包命令发现
或嵌入。
Agent 原生 CI 策略
Agent 源码、共享协议、lock、bundle 工具和测试与桌面应用保持在同一仓库、同一 commit;
不维护长期分叉的 Agent 源码分支。.github/workflows/agents.yml 在相关 Pull Request、
main push 和手工触发时,分别使用 ubuntu-24.04 与 ubuntu-24.04-arm 原生构建
Linux x64/arm64 复合包,并使用 macos-15 原生构建 Darwin arm64
复合包。所有 job 从 agent-runtime-lock.json 解析官方 Node HTTPS
地址与 digest,并从 remote-runtime-lock.json 解析固定 OpenCode npm 包与 integrity;
下载后再次校验,随后使用仅存在于进程内的临时 Ed25519 测试 key 构建两次,完成内外层
测试签名验证、Agent CLI 加载 smoke 和确定性 .gbagent 对比。所有目标还验证
Koffi 原生模块加载与 OpenCode --version;Linux job 继续运行 Agent 原生回归。
Darwin arm64 的 Node 和 OpenCode 固定输入分别由同一份
agent-runtime-lock.json 与 remote-runtime-lock.json 提供,不再维护 CI 专用副本。
组包器区分 ELF 与 Mach-O,Darwin CI 产物命名为
goodbuddy-agent-<version>-darwin-arm64.gbagent,不构建 Intel Mac 目标。
macOS Host 继续通过 SSH 按需启动 detached Agent,不注册 launchd 或开机服务。
进程身份使用 macOS 原生接口,Unix Socket 使用 getpeereid;Host 下载兼容系统自带
shasum -a 256,不要求安装 GNU coreutils。真实 Host 验证记录见
远程主机技术设计。
三个目标共用 agent-catalog.json 和现有签名格式,条目按平台与架构区分。旧版 Desktop
只接受 Linux catalog 条目;首次发布包含 Darwin 的累计 catalog 前必须先发布新版
Desktop,并在发布说明中要求用户升级 Desktop。旧版的在线 Agent 目录检查会失败,
本地已验证包不被删除。不得为此改写历史包或复用发布标签。
该验证 workflow 不读取 production signing secret,不修改公开 key registry,也不上传
可安装 Agent 工件。.github/workflows/agent-release.yml 是唯一 production Agent
发布路径:只接受指向受保护 main 历史的 annotated
agent-v${agent-runtime-lock.agentVersion} 标签,在 agent-signing Environment 中
原生构建 Linux x64、Linux arm64、macOS arm64 三个目标,生成并签名累计目录,然后同步到非 Latest 的 GitHub Agent Release
与北京 OSS。该 Environment 只保护一组 GoodBuddy 通用发布变量与 Secret:
GOODBUDDY_SIGNING_KEY_ID/GOODBUDDY_SIGNING_PRIVATE_KEY
该 GoodBuddy 身份统一签署内部 Agent、固定 Runtime manifest、外层 .gbagent
描述符和累计目录,各层通过既有签名域区分用途,不再要求内部 Runtime 单独配置生产
身份。私钥只注入签名步骤;预检只读取 key ID 并确认公钥已在
resources/agent-release-keys.json 注册为 production 且未撤销。
累计目录使用 agent-catalog.json 与 agent-catalog.sig。每个条目绑定 Agent 版本、
最低桌面版本、Agent 协议、远端 OpenCode 版本/digest、架构、文件名、大小、SHA-256
以及固定 GitHub/OSS URL。同一版本和架构的字节不可改变;目录最多保留 200 个条目。
OSS 先写 agent-releases/v<version>/ 不可变对象,GitHub Release 公开且验证后才更新
单一 agent-releases/latest.json 指针。该指针只引用同一不可变版本目录中的
agent-catalog.json 与 agent-catalog.sig,避免客户端观察到跨版本组合。Agent Release 必须使用
--latest=false,不得改变桌面 Release 的 Latest 标记。
远程 OpenCode Runtime 基础
remote-runtime-lock.json 独立锁定首个远程 Runtime:OpenCode 1.18.29 的 Linux x64
baseline 与 arm64 官方包 integrity、bin/opencode 入口和固定 acp 参数。它不同于
agent-runtime-lock.json,后者锁定 Agent 自带 Node 以及用于 Linux SO_PEERCRED
的 Koffi 版本。Agent bundle 将 Koffi loader 作为 external module,并只携带目标
Linux 架构的 glibc/musl 原生 binding;构建与导入都会校验其 ELF 架构和 MIT 许可证。
远程 Runtime 工件由以下命令独立管理:
# 在对应 Linux 原生 Runner 上从 lock 指定的 npm 归档构建并签名
npm run remote-runtime:build -- --arch <x64|arm64> \
--runtime-archive <locked-opencode-package.tgz>
# 为复合 Agent 包组装导入并完整验证签名归档
npm run remote-runtime:import -- --arch x64 --archive <runtime-x64.tar>
npm run remote-runtime:import -- --arch arm64 --archive <runtime-arm64.tar>
默认生成目录为 .remote-runtime-resources/linux-<arch>/opencode/<digest>,
不提交到仓库。生产 workflow 从 remote-runtime-lock.json 解析固定包名、版本和
integrity,以 --ignore-scripts 从官方 npm registry 获取归档;bundle 工具在签名前
再次验证归档 SHA-512、包名、版本和 ELF 架构。
源码已经实现 Runtime bundle build/import/verify、Daemon 侧
manifest/Ed25519/payload/ELF/lock 校验、digest registry、ACP v3、直接进程 ownership 和
runtime/model-bridge v1。Agent 通过私有 Unix socket 按需 detached 启动,不依赖
systemd、D-Bus、Linger 或 bubblewrap。Ask 与 Execute 都直接启动签名 Runtime;两者的
差异由 OpenCode Ask 权限配置和 Agent 工具权限分发边界共同执行,Execute 不注入该
权限覆盖。
OpenCode 通过已签名 Agent helper 和每次 Prompt 的私有 Unix socket 使用 Main-only 模型 网关;Provider URL、API Key 和真实 Provider 认证头不进入远端。helper 的 loopback HTTP 入口使用每个进程随机生成的路径 capability,Anthropic/OpenAI SDK 需要的固定非秘密 本地兼容标记由 helper 核对后丢弃。GoodBuddy 自己维护会话标题,所以生成的 OpenCode 配置必须禁用 title Agent;工具循环只使用 build Agent 的模型轮次。
Ask 模式的 ACP 权限中介只可为原生 read 选择 allow_once;其他工具种类和
allow_always 请求必须拒绝。Unix 模型桥
broker 必须按 socket 当前已缓冲字节增量读取长度帧,不能等待 read(remaining) 一次返回
完整大响应。Agent 传输或权限实现变化后,除了普通测试,还要在原生 Linux 上运行包含
至少 256 KiB 响应的模型桥回归。
agent-runtime-lock.json 维护独立于桌面应用的 Agent 版本,
remote-runtime-lock.json 维护 OpenCode Runtime 版本。Agent 内容变化时必须更新
agentVersion;Runtime 更新时使用 Runtime 自身版本。签名 manifest 或 bundle digest
唯一标识对应版本的精确工件。变更后必须重新构建、签名并导入 Linux x64/arm64 两套工件;
旧版本或旧 digest 会按设计校验失败。不要手工把本机 .runtime-resources 当作远程 bundle。
桌面应用更新后不会在项目切换时下载或安装 Agent。用户先在 SSH Host 的远程运行环境 卡片显式选择直连下载或 GoodBuddy 传输。托管 SSH 项目只保存 Host ID、远端路径、 Runtime 选择和默认模式;创建或保存时验证 Host 当前环境,普通项目切换只读取这些本地 配置。首次实际使用 Workspace 或 Runtime 时解析 Host current identity;同一进程内后续 项目复用已确认的 Agent/Runtime identity 和 Agent 连接。该流程不取得安装包、不重读完整 payload,也不发布组件。
两条手动准备路径都支持阶段进度和取消;成功后定向失效该 Host 的连接与 Runtime 缓存, 引用项目下一次打开自然使用新的 current 环境。验收应覆盖两种准备方式与项目当前环境 验证入口,并确认失败或取消不会删除 Host 配置、凭据、项目设置、Workspace 文件、全局 可信元数据或仍可用的旧安装。
跨平台 CI 与 GitHub Release
.github/workflows/packages.yml 是统一发布工作流。它先验证并生成一次
out 生产 bundle,再在六个原生 Runner 上分别打包 Windows、macOS 和
Linux 的 x64、arm64 版本。生产 bundle 仅作为短期 Actions artifact
供打包任务复用,不会上传到 GitHub Release。该工作流不构建、下载、签名或嵌入
远端 Agent/OpenCode 复合包,也不依赖 agent-signing Environment;远端包缺失或
Agent 发布失败不会阻塞任何桌面目标。
本地构建单个平台目标:
npm run release:package -- --platform <windows|macos|linux> --arch <x64|arm64>
release:package 仅构建桌面产品。基础配置统一携带
agent-release-keys.json、agent-runtime-lock.json 与 remote-runtime-lock.json
供 Main 校验独立下载的复合包,但 release 配置不增加 .agent-resources 或
.remote-runtime-resources。发布校验继续检查桌面自身的 OpenCode、Continue 与
DeepSeek Harness Runtime、app.asar、目标架构和安装包签名。
在 macOS 上明确生成未签名、未公证的开发验证包:
npm run release:package -- --platform macos --arch <x64|arm64> --unsigned
默认发布产物为 Windows 的 NSIS 安装包与 portable ZIP、macOS 的 DMG 与
ZIP,以及 Linux 的 AppImage、DEB 与 RPM。Linux 原生 Runner 必须安装
rpm/rpmbuild 工具后再调用 electron-builder。Windows portable ZIP 解压后可直接
运行 GoodBuddy.exe,并包含启用便携数据目录的
.goodbuddy-portable.json。每个目标目录都包含带文件大小和 SHA-256 的
release-manifest.json。
推送 main 时只运行源码验证和 production bundle 构建,不运行六平台
打包矩阵,避免随后推送版本标签时对同一提交重复完整打包。手动触发会运行
验证和六平台打包,并保留 30 天 Actions artifacts,但不会创建 Release。
Desktop 发布顺序(强制)
Desktop 正式发布必须把候选分支推送与标签推送拆成两个阶段,不能在同一批操作中先后
推送 main 和标签:
- 完成源码、版本、双语 Release Notes 与相关文档修改,运行 Release Notes 校验、
npm test、npm run typecheck和npm run lint。常规发布准备不在本机运行 production build、打包或安装包探针。 - 提交候选,确认用户批准的准确 commit SHA 与双语 Release Notes,fetch 两个远端后,
将该候选分别推送到
origin/main和github/main。 - 等待这个准确 SHA 的
main分支Cross-platform packagesworkflow 完成。只有Run validators和Build production bundle都成功,候选才具备打标签条件。 - 如果
mainworkflow 失败,不得创建标签。修复必须形成新的 commit,重新完成本地 校验、双远端main推送,并等待新 SHA 的mainworkflow 成功;旧 SHA 的成功结果 不能替代新 SHA 的验证。 main成功后再次 fetch 两个远端,确认工作区干净、两个远端main都等于获批 SHA、 目标标签在本地和远端均未使用。此时才可在该 SHA 创建 annotated tag,并把标签分别 推送到两个远端,随后等待六平台打包和正式发布完成。
不可移动、删除、重建或复用失败的发布标签。完整审批、恢复和发布后检查以 发布 Runbook 为准。
推送 v${package.version} 标签时,工作流运行验证和六平台打包。只有在
全部目标成功后,才会严格校验并聚合所有平台产物,生成按平台重命名的
manifests、总 release-manifest.json 和 SHA256SUMS。随后工作流通过
GitHub OIDC 获取短期 STS 凭据,将发布资产和 site-release.json 上传到
北京 OSS 的不可变版本目录,并公开校验 14 个安装包。验证通过后才创建或
更新 draft GitHub Release、上传 22 个 Release 资产并正式发布,最后原子
切换官网 latest.json。任一步失败都不会提前切换官网最新版本。
同一标签重跑时,工作流会根据 resources/release-notes.json 重新生成并
覆盖 GitHub Release 正文,以 --clobber 更新已知发布资产,同时保留未知
附件。若源码与发布元数据未变化,应修正外部配置后重跑同一不可变标签;
只有必须修改代码或元数据时才递增版本并创建新标签。
Agent 发布使用另一套标签和 workflow:
agent_tag="agent-v$(node -p "require('./agent-runtime-lock.json').agentVersion")"
git tag -a "$agent_tag" -m "GoodBuddy Agent $(node -p "require('./agent-runtime-lock.json').agentVersion")"
实际创建或推送前同样必须获得用户确认。.github/workflows/agent-release.yml
原生构建两个 .gbagent、签名累计目录、上传
agent-releases/v<agent-version>/,创建 --latest=false GitHub Release,再切换
OSS 签名目录指针。生产发布需要 agent-signing 与 aliyun-oss-release 两个受保护
Environment;普通分支/PR 的 agents.yml 不使用其中任何 Secret。
Agent 标签也必须遵循与 Desktop 相同的 pre-tag 顺序。更新 Agent 生产源码或
agentVersion 后,先把候选提交推送到两个 main,等待该准确 SHA 的
GoodBuddy Agents main workflow 全部成功,再次 fetch 并确认两个远端 main、干净
工作区和未使用标签,最后才可创建和推送 annotated agent-v<version>。不得把 Agent
标签与候选 main 放在同一批推送中;任何修正 commit 都必须重新等待新 SHA 的 Agent
main workflow,旧 SHA 的成功结果不能复用。
中英文发布说明统一维护在 resources/release-notes.json。新版本按“本次
亮点 / Highlights”“功能更新 / Features”“问题修复 / Bug Fixes”“使用前
请留意 / Before You Start”四段组织,应用首次启动弹窗与 GitHub Release
正文共用该来源。旧版两段式记录会兼容读取,无需改写。提交发布候选前运行
npm run release:notes:verify 校验版本、双语条目数量并生成 Markdown。
发布标签必须与 package.json 版本完全一致。实际推送标签和触发发布前仍
需人工确认,例如当前版本应使用:
tag="v$(node -p "require('./package.json').version")"
git tag -a "$tag" -m "GoodBuddy $(node -p "require('./package.json').version")"
git push origin "$tag"
git push github "$tag"
macOS 发布 job 会先原子判断 Apple 凭据状态:以下五项 Actions Secrets 全部 存在时,使用 Developer ID Application 证书签名,并通过 App Store Connect API Key 提交 Apple notarization;五项全部缺失时,明确生成未签名、未公证的 DMG 和 ZIP,并在 Actions 日志与摘要中警告 Gatekeeper 限制;只配置一部分时 任务失败,不能静默降级为未签名包。
MACOS_CERTIFICATE_BASE64:包含证书及私钥的.p12文件经 Base64 编码后的内容。MACOS_CERTIFICATE_PASSWORD:导出.p12时设置的密码。APPLE_API_KEY_BASE64:App Store Connect API Key 的.p8文件经 Base64 编码后的内容。APPLE_API_KEY_ID:App Store Connect API Key 的 Key ID。APPLE_API_ISSUER:App Store Connect API Key 的 Issuer ID。
在 macOS 上生成适合 Secrets 的单行 Base64 内容:
base64 -i DeveloperIDApplication.p12 | tr -d '\n'
base64 -i AuthKey_XXXXXXXXXX.p8 | tr -d '\n'
证书必须是 Apple Developer 后台创建的 Developer ID Application,并在导出
.p12 的 Mac 钥匙串中同时包含对应私钥。API Key 建议使用团队级 App Store
Connect Key;.p8 只能下载一次。签名材料只放入 GitHub Secrets,不提交到仓库。
凭据完整时,macOS 打包完成后会挂载 DMG,并分别执行 codesign、Gatekeeper
spctl 和 stapler 校验;签名无效或 notarization ticket 不存在时,发布矩阵
会在上传产物前失败。完全没有凭据时,六平台矩阵和标签发布仍可完成,但 macOS
产物没有 Developer ID 签名或 Apple 公证,Gatekeeper 可能阻止首次打开。发布
工作流必须在 Actions 日志与摘要中明确这一限制;获得完整凭据后应恢复签名
发布并重新验证。
Windows 代码签名仍未配置。对外分发前还应配置 Windows 签名凭据,并重新验证 安装、升级和系统安全提示。
发布前冒烟测试
建议在每个目标系统上至少验证:
- 安装、启动、升级和卸载。
- 中文输入法、窗口缩放和高分屏显示。
- 系统密钥环和模型连接。
- 本地知识库导入、检索和知识图谱。
- Ask、Execute 的权限边界与旧版 Plan 数据兼容。
- OpenCode 与 Continue 的权限边界、取消和超时。
- DeepSeek Harness Ask 拒绝写入和第三方插件工具,可调用 Main 管理的 Web Search/Fetch;Execute 可调用已启用插件工具。文本模型在网络调用前拒绝图片,声明图片能力的模型可以实际接收 JPEG/PNG。
- OpenCode Agent/Command、原生上下文 Compact,以及 Continue Rules/Prompt 预设、结构化提问和 GoodBuddy 手动摘要压缩。
- Runtime 原生清单把 Tools 与 Commands/LSP/Formatters 分开,显示来源及 Ask/Execute 可用性,不混入 GoodBuddy 分配的 Skills/MCP;外部 OpenCode 只报告连接状态,Continue 明确标记原生 Tools 静态发现不支持;内置 MCP 的启停与 Runtime 分配会持久化并限制后续请求,DeepSeek Harness 保持不可分配;MCP 测试只读取有界 Prompt/Resource 元数据,不读取 Resource 内容。
- DSH 市场可安装、停用、重新启用和移除插件;启动失败插件不会阻止 Host,并显示为自动停用。
- 智能心跳的创建、暂停、恢复和历史记录。
- 应用退出后无残留 Runtime 子进程。
DeepSeek Harness 的 Electron Utility Host 可单独执行无模型、无凭据冒烟测试:
npm run smoke:deepseek-harness
该命令先生成 production bundle,再从 CommonJS Electron 主入口启动实际
utilityProcess,等待固定 Host 完成本地主机执行器初始化与内部 ready 握手。它不会发起
模型请求,也不会读取或传递 API Key。
Windows x64 完整打包后的 Utility Host 与内置 npm 冒烟测试:
npm run build
node node_modules/electron-builder/cli.js --win dir --x64 --publish never --config.directories.output=dist/harness-package-probe
npm run smoke:deepseek-harness:packaged
该测试启动打包后的 Host,并使用打包资源中的准确 npm 版本安装一个本地临时包,
确认 npm 依赖闭包、Electron Node 模式、node shim 和生命周期脚本都可用。它不访问
npm registry,也不运行模型请求。
真实 DSH 市场测试会访问公共 npm、运行第三方安装与插件代码,因此只在已明确授权时启用:
GOODBUDDY_DSH_MARKETPLACE_E2E=1 npm test -- src/main/agent/dsh-extension-marketplace.e2e.test.ts
该测试使用临时用户目录,经捆绑 npm 路径安装已审查的最小测试插件,并验证 Host 加载和真实工具调用;测试结束后删除临时目录。
要让真实模型同时验证插件的 Ask 拒绝与 Execute 调用,可显式提供兼容的 OpenAI Chat Completions 配置:
GOODBUDDY_DSH_MODEL_E2E=1 \
GOODBUDDY_DSH_API_KEY=... \
GOODBUDDY_DSH_BASE_URL=https://api.deepseek.com \
GOODBUDDY_DSH_MODEL=deepseek-chat \
npm test -- src/main/agent/deepseek-harness-acp-e2e.test.ts -t "rejects a real npm plugin"
如需覆盖发布包内置 npm 路径,再设置 GOODBUDDY_DSH_NPM_CLI 与
GOODBUDDY_DSH_NODE_EXECUTABLE 指向已解包应用中的 npm CLI 和应用主程序。