mirror of
https://github.com/iflytek/skillhub.git
synced 2026-11-14 09:01:16 +00:00
Merge pull request #1038 from iflytek/fix/dsh-windows-host-preparation
Some checks failed
Security / Dependency Review (push) Waiting to run
Security / CodeQL (java-kotlin) (push) Waiting to run
Security / CodeQL (javascript-typescript) (push) Waiting to run
Security / CodeQL (python) (push) Waiting to run
Deploy Docs / build (push) Has been cancelled
Deploy Docs / Deploy (push) Has been cancelled
Some checks failed
Security / Dependency Review (push) Waiting to run
Security / CodeQL (java-kotlin) (push) Waiting to run
Security / CodeQL (javascript-typescript) (push) Waiting to run
Security / CodeQL (python) (push) Waiting to run
Deploy Docs / build (push) Has been cancelled
Deploy Docs / Deploy (push) Has been cancelled
fix(dsh): prepare 0.6.1 with deduplicated Host validation
This commit is contained in:
commit
326cbd6224
10 changed files with 146 additions and 28 deletions
|
|
@ -12,14 +12,14 @@
|
|||
|
||||
### 1.1 当前候选与发布顺序
|
||||
|
||||
本轮拟发布 CLI `0.3.0`(`cli-v0.3.0`)和 DSH `0.6.0`(`dsh-v0.6.0`),范围为技能启停、统一只读检查更新、紧凑已安装卡片,以及发布前库存兼容修复。不发布服务端镜像或部署 SaaS。
|
||||
CLI `0.3.0`(`cli-v0.3.0`)已发布,本轮继续发布 DSH `0.6.1`(`dsh-v0.6.1`),范围为技能启停、统一只读检查更新、紧凑已安装卡片,以及发布前库存兼容修复。不发布服务端镜像或部署 SaaS。
|
||||
|
||||
共享库存保持严格 v2,不写入 `installationState`;启停状态从可信的 `SKILL.md` / `SKILL.md.disabled` 入口派生,不要求 CLI 用户同步升级。已发布 CLI `0.2.0` 的正常查询和普通目标管理必须继续兼容;默认更新、同步及 Suite 不得无意重新启用停用目标。旧 CLI 显式 force 重装、更新、同步仍按原覆盖语义执行,可能恢复标准入口;显式 remove 仍可删除,新版重新读取须反映真实变化。
|
||||
|
||||
1. 使用真实旧公开 CLI `0.2.0`、DSH `0.5.0` 建立基线,在同一隔离 HOME 交替操作,验证普通/停用目标混存、来源与位置隔离、默认保护和显式覆盖语义。仅证明拒写或字节未损坏不算兼容通过。
|
||||
2. 读 v1/v2 不迁移。未发布 v3 必须在锁内校验身份、入口与未完成事务,备份原始字节后安全转换 v2;普通操作日志保持旧 DSH 可读,启停日志隔离影响。并发及五阶段强杀恢复重新验收。
|
||||
3. 完成候选检查、双 RC、跨平台、真实浏览器及草稿保护;旧候选结果仅在输入等价时可复用。兼容修复正在进行,未通过前阻断发布。
|
||||
4. 通过后按专用工作流依次发布 CLI 和 DSH,核对公开字节、干净安装和升级保留。此顺序是交付安排,不是要求用户同步升级 CLI。当前不声明任何候选已发布。
|
||||
3. 完成候选检查、双 RC、跨平台、真实浏览器及草稿保护;旧候选结果仅在输入等价时可复用。兼容修复及真实旧包验收已通过;最终版本包和公开包仍按本流程单独验收。
|
||||
4. CLI 0.3.0 已完成公开字节及安装验收,不重复发布;DSH 0.6.1 通过后按专用工作流发布,核对公开字节、干净安装和升级保留。此顺序是交付安排,不是要求用户同步升级 CLI。保留已存在的 `dsh-v0.6.0` 标签,不移动到新提交;DSH 0.6.1 公开验收完成前不声明已发布。
|
||||
|
||||
旧消费者不承诺识别启停专用日志:中断启用已留下合法标准入口时,旧版仍可能执行合法管理。新恢复必须先核精确目标身份、版本、时间、指纹和归属,发现旧版变更即保留现场并返回 unknown,不能用旧意图覆盖新内容。CLI 的 v3 转换只核当前库存和入口并保留日志,不扫描全机 Host;NativeRuntime 在自己的日志域先处理恢复。
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ Host 准备允许通过显式 `--download-cache` 共享受校验临时目录中
|
|||
|
||||
### 2.3 隔离环境验证
|
||||
|
||||
本次 `0.6.0` 候选分别验证 DSH `0.2.0-rc.2` 与 `0.2.1-rc.2`,Node `>=24.2.0 <25`。Host、app-boot、核心模块及注入 Client 模块须为同一精确发行版。新 RC 仅使用 `4.0.5 / 1.0.6 / 1.0.10 / 1.1.7`;旧 RC 的 Cordis、Loader、Include、Timer 须为已验证的完整组合:`4.0.4 / 1.0.5 / 1.0.9 / 1.1.6` 或 `4.0.5 / 1.0.6 / 1.0.10 / 1.1.7`,不得交叉混装;以实际 Host 解析结果为准。已发布 `0.1.0` 尚不包含后一组合支持及历史回执修复,`0.1.1` 起包含这些修复,`0.2.0` 延续此兼容基线;发布前使用本次 tarball 验证。在独立测试账户或容器中准备 Web Profile、工作区和测试技能,连接测试 Registry。仅隔离 Profile/DSH home 不足以隔离共享 inventory。
|
||||
本次 `0.6.1` 候选分别验证 DSH `0.2.0-rc.2` 与 `0.2.1-rc.2`,Node `>=24.2.0 <25`。Host、app-boot、核心模块及注入 Client 模块须为同一精确发行版。新 RC 仅使用 `4.0.5 / 1.0.6 / 1.0.10 / 1.1.7`;旧 RC 的 Cordis、Loader、Include、Timer 须为已验证的完整组合:`4.0.4 / 1.0.5 / 1.0.9 / 1.1.6` 或 `4.0.5 / 1.0.6 / 1.0.10 / 1.1.7`,不得交叉混装;以实际 Host 解析结果为准。已发布 `0.1.0` 尚不包含后一组合支持及历史回执修复,`0.1.1` 起包含这些修复,`0.2.0` 延续此兼容基线;发布前使用本次 tarball 验证。在独立测试账户或容器中准备 Web Profile、工作区和测试技能,连接测试 Registry。仅隔离 Profile/DSH home 不足以隔离共享 inventory。
|
||||
|
||||
首次创建测试 Web Profile,然后安装本地候选包:
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ SaaS 使用其现有流水线部署;公共接口契约、测试数据规则与
|
|||
|
||||
### 4.1 独立发布流程
|
||||
|
||||
插件使用独立版本,tag 前缀固定为 `dsh-v`(本次为 `dsh-v0.6.0`),不复用 CLI 的 `cli-v*` 发布流程。专用工作流为 `.github/workflows/release-dsh-plugin.yml`;发布前仍须确认 npm 包名、兼容基线及候选包验收,本次候选为 `0.6.0`,manifest 已显式设为 `private: false`;缺省 Registry 为 `https://skill.xfyun.cn`,显式空串/空白仍断开,已有自定义来源保留。发布前分别验证缺省、断开和自定义配置,以及正式域名的固定版本安装、状态识别和本地管理。正式发布仍以最终提交、候选验收和审批结果为准。
|
||||
插件使用独立版本,tag 前缀固定为 `dsh-v`(本次为 `dsh-v0.6.1`),不复用 CLI 的 `cli-v*` 发布流程。专用工作流为 `.github/workflows/release-dsh-plugin.yml`;发布前仍须确认 npm 包名、兼容基线及候选包验收,本次候选为 `0.6.1`,manifest 已显式设为 `private: false`;缺省 Registry 为 `https://skill.xfyun.cn`,显式空串/空白仍断开,已有自定义来源保留。发布前分别验证缺省、断开和自定义配置,以及正式域名的固定版本安装、状态识别和本地管理。正式发布仍以最终提交、候选验收和审批结果为准。
|
||||
|
||||
发布流程依次完成:校验版本 → 测试与构建 → 生成并检查 tarball → 隔离 DSH 候选验收 → 发布审批 → 发布 npm → 校验已发布包。当前工作流自动执行版本校验、测试与构建、包检查、上传及完整性校验;隔离 DSH 的 UI、真实模型和平台验收按本次确认范围另行执行,未验项目须单独注明;CI 矩阵成功不等同于这些验收通过。工作流使用仓库的 `NPM_TOKEN` 发布凭证,源码及 npm 包不包含凭证;配置了环境保护规则时还须完成对应审批。
|
||||
|
||||
|
|
@ -213,7 +213,7 @@ SaaS 使用其现有流水线部署;公共接口契约、测试数据规则与
|
|||
|
||||
### 4.3 发布说明
|
||||
|
||||
`0.6.0` 的变更说明见 [release-notes.md](release-notes.md)。正式版本发布前按最终候选及验收结果更新,不沿用开发版本的支持声明。
|
||||
`0.6.1` 的变更说明见 [release-notes.md](release-notes.md)。正式版本发布前按最终候选及验收结果更新,不沿用开发版本的支持声明。
|
||||
|
||||
每个版本包含:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
# SkillHub DSH 原生插件发布说明
|
||||
|
||||
|
||||
## 0.6.0 候选:启停、检查更新与紧凑卡片
|
||||
## 0.6.1 候选:启停、检查更新与紧凑卡片
|
||||
|
||||
- **受管理技能启停**:[#1025](https://github.com/iflytek/skillhub/pull/1025) 按确认的位置将 `SKILL.md` 转换为 `SKILL.md.disabled`,启用恢复入口,保留目录和资源。更新停用安装后仍保持停用;文件修改、共享归属、目标被占用及不可信记录仍阻止写操作。停用不撤回已加载会话内容,也不停止任务。
|
||||
- **统一检查、逐项更新**:[#1030](https://github.com/iflytek/skillhub/pull/1030) 主动只读检查当前来源库存,渐进显示结果和失败重试。一次可信快照、有界并发、同来源技能共用远端 resolve;取消及上下文变化不接纳迟到结果。真正更新仍进入详情按位置确认,不提供批量自动覆盖。
|
||||
- **日常围绕使用技能**:[#1032](https://github.com/iflytek/skillhub/pull/1032) 已安装页采用桌面两列、窄屏一列的紧凑横向卡片,显示真实本地名称、一行摘要和实际安装版本;至多两个语义标签。整卡进入详情,保留使用技能主按钮,管理安装/检查更新收进更多菜单。单卡检查不清除其他卡结果,使用和菜单不误触详情。
|
||||
- **库存兼容**:共享库存保持严格 v2,不写入 `installationState`;启停状态从可信的 `SKILL.md` / `SKILL.md.disabled` 入口派生,不要求 CLI 用户同步升级。已发布 CLI `0.2.0` 的正常查询和普通目标管理必须继续兼容;默认更新、同步及 Suite 不得无意重新启用停用目标。旧 CLI 显式 force 重装、更新、同步仍按原覆盖语义执行,可能恢复标准入口;显式 remove 仍可删除,新版重新读取须反映真实变化。 未发布 v3 的安全转换、普通/启停日志兼容隔离及真实旧包交替验收正在进行;未通过前阻断发布。
|
||||
- **库存兼容**:共享库存保持严格 v2,不写入 `installationState`;启停状态从可信的 `SKILL.md` / `SKILL.md.disabled` 入口派生,不要求 CLI 用户同步升级。已发布 CLI `0.2.0` 的正常查询和普通目标管理必须继续兼容;默认更新、同步及 Suite 不得无意重新启用停用目标。旧 CLI 显式 force 重装、更新、同步仍按原覆盖语义执行,可能恢复标准入口;显式 remove 仍可删除,新版重新读取须反映真实变化。 [#1033](https://github.com/iflytek/skillhub/pull/1033) 已完成未发布 v3 的安全转换、普通/启停日志兼容隔离及真实旧包交替验收。
|
||||
- **交付边界**:保留现有双 RC、Node/Cordis 与来源边界;详情官网链接仅使用 API 的安全 `webUrl`,缺失即隐藏。该增量不包含 SaaS 部署、场景助手或技能清单导入导出。
|
||||
|
||||
上述包含已合并功能及正在实施的发布前兼容修复,不声明 `0.6.0`/CLI `0.3.0` 已发布,也不把历史功能验收等同本轮最终候选、跨平台恢复或公开包验收。发布顺序和限制见 [发布指南](release-guide.md),结果由 [验证记录](verification.md) 单独维护。
|
||||
上述功能及库存兼容修复已合并。CLI `0.3.0` 已发布;DSH `0.6.1` 仍是候选,不把历史功能验收等同最终版本包或公开包验收。发布顺序和限制见 [发布指南](release-guide.md),结果由 [验证记录](verification.md) 单独维护。
|
||||
|
||||
## 0.5.0 技能可用性诊断
|
||||
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
## 版本与交付状态
|
||||
|
||||
当前准备版本为 DSH `0.6.0` 和配套 CLI `0.3.0`,尚不据此声明已公开发布。历史版本说明保留;实际公开状态以对应 npm 版本和专用发布工作流为准。
|
||||
当前准备版本为 DSH `0.6.1`;CLI `0.3.0` 已发布并完成公开包验收,不重复发布。保留已存在的 `dsh-v0.6.0` 标签,DSH 候选使用新标签 `dsh-v0.6.1`,尚不据此声明 DSH 已公开发布。历史版本说明保留;实际公开状态以对应 npm 版本和专用发布工作流为准。
|
||||
|
||||
`@astron-team/skillhub-dsh@0.3.1` 已发布,独立标签为 `dsh-v0.3.1`。[发布准备 PR #995](https://github.com/iflytek/skillhub/pull/995) 已合并,[专用发布工作流](https://github.com/iflytek/skillhub/actions/runs/39556075587) 成功;[公开 npm 包](https://www.npmjs.com/package/@astron-team/skillhub-dsh/v/0.3.1) 与工作流产物逐字节一致,发布时 `latest` 指向 `0.3.1`。两版 Linux 的严格安装、原生探针及浏览器操作复验通过,测试工件与公开包字节一致,结果见 [验证摘要](verification.md)。历史已发布版本保持不变。
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ Node 保持 `>=24.2.0 <25`。精确兼容与验证边界见 [兼容设计](compa
|
|||
|
||||
## 安装、更新与维护
|
||||
|
||||
`0.6.0` 公开可用后,可通过 `dsh plugin --profile skillhub-test add @astron-team/skillhub-dsh@0.6.0 --ignore-scripts` 安装到已有 Web Profile;发布前使用本地候选 tarball。发布前从源码执行 `bun run check` 后生成 `.tgz`,使用固定版本 DSH 的 `plugin ... add` 安装。具体准备、命令、配置和测试流程见 [中文指南](../../../plugins/dsh/README.md)、[英文指南](../../../plugins/dsh/README.en.md) 及 [发布指南](release-guide.md)。
|
||||
`0.6.1` 公开可用后,可通过 `dsh plugin --profile skillhub-test add @astron-team/skillhub-dsh@0.6.1 --ignore-scripts` 安装到已有 Web Profile;发布前使用本地候选 tarball。发布前从源码执行 `bun run check` 后生成 `.tgz`,使用固定版本 DSH 的 `plugin ... add` 安装。具体准备、命令、配置和测试流程见 [中文指南](../../../plugins/dsh/README.md)、[英文指南](../../../plugins/dsh/README.en.md) 及 [发布指南](release-guide.md)。
|
||||
|
||||
更新或回退前保留原包和校验值、停止新操作并处理待恢复事务,然后停止 Host、安装兼容包并重新启动。浏览器旧资源可强制刷新;这不能代替 Host 依赖重载。禁用或移除包不会卸载技能,不应删除技能目录、inventory 或操作日志。
|
||||
|
||||
|
|
|
|||
|
|
@ -560,3 +560,21 @@ Windows Node 24.2.0、DSH 0.2.0-rc.2、Chrome 156 的新隔离 Profile 已通过
|
|||
兼容审查补充:日志搬迁改为先排他 link 原记录,持久化目标目录后 unlink 源记录并持久化源目录,再写扩展后继;双份需完整相等,原字节 SHA 备份不覆盖。独立定向日志/投影 15 测试、74 断言及 CLI typecheck 通过,不代表全套与真实旧包矩阵完成。无效入口投影为 unreadable,Host 不再默认 enabled,补充卡片与模型上下文测试通过。
|
||||
|
||||
旧消费者不必理解 activation 日志。中断启用后的合法标准入口仍可能被旧版正常管理;新恢复在写入前必须比较身份、版本、installedAt、指纹与归属,变化即 unknown 并保留现场。该有限保证替代“旧版必能阻止全部待恢复操作”的过度承诺;真实旧包在 moved 阶段写入后的恢复验收仍须单列。CLI 安全转换 v3 不扫描全机 Host 日志,保留原日志与技能文件;NativeRuntime 处理自己日志域的 pending 恢复。
|
||||
|
||||
|
||||
### Windows 官方 Host 准备超时诊断(2026-10-30)
|
||||
|
||||
DSH 0.6.0 发布检查在 Windows / DSH 0.2.1-rc.2 的 `install-host` 阶段两次达到 600 秒上限,均未进入技能探针;不能将该失败解释为启停断言失败。公开下载缓存存在不证明依赖解析或解包已加速。诊断摘要新增有界、白名单 npm 阶段计时及缓存观察计数,原始日志仍私有;计数不是去重请求数或完整缓存命中率。
|
||||
|
||||
本机诊断发现新版 Host 依赖树存在多份嵌套同版本 Zod。隔离 npm Registry 的最小相容范围实验中,SDK 范围 `^3.25.0 || ^4.0.0` 与十二个 exact `4.4.3` 消费方共存:默认解析为根 `4.6.7` 加十二份 `4.4.3`,`--prefer-dedupe` 则保留一份 `4.4.3`;两棵树均通过 npm 依赖约束检查。
|
||||
|
||||
候选调整仅在官方 Host 准备命令增加 `--prefer-dedupe`,不添加 Zod override、直接依赖、忽略 peer 的选项,也不改变安装脚本禁用、空白 Profile、进程清理、600 秒准备上限或业务断言。它可能在原范围内选择较旧的兼容依赖,因此仍须验证实际 Host 依赖身份和完整探针。
|
||||
|
||||
真实 Windows Node 24.2.0 / npm 11.3.0 的新版 Host lock-only 对照确认:默认 687 个安装位置、51 份 Zod,prefer-dedupe 为 636 个安装位置、1 份 Zod。本机两次完整冷安装仍达到 600 秒上限;减少重复依赖并不证明冷安装超时已解决。
|
||||
|
||||
候选提交 `74e6d93b7` 的 [CI 40996963179](https://github.com/iflytek/skillhub/actions/runs/40996963179) 中,旧版 Host 的 Windows Node 24.2.0 完整探针通过,准备耗时 348647ms;Windows Node 24 的准备完成(459541ms),但随后技能可用性探针在 user-install 的 accepted 断言失败。Linux/macOS 基础检查通过,后续官方 Host 和启停矩阵因前置失败未执行,不能算通过。安装工具返回状态与固定失败码需要补充诊断;保留现有断言。最终发布包验收仍待完成。修改验收脚本不等于改变已发布 CLI 或 DSH 产品运行逻辑。
|
||||
|
||||
|
||||
后续固定提交 `a431833b33bcf7523949537c50b9409c00c70ce3` 的 [CI 41011761831](https://github.com/iflytek/skillhub/actions/runs/41011761831) 已通过全部 14 项 DSH 检查。Windows / Host 0.2.1-rc.2 的启停与关闭监听场景准备分别耗时 348044ms、295943ms;Host 0.2.0-rc.2 分别为 291219ms、322063ms,均在全新 Profile 中通过真实探针。此前安装接受断言失败本轮未复现;新增诊断保留其固定状态和错误码,不修改断言。
|
||||
|
||||
本机部分缓存复测仍在 600787ms 超时,因此上述结果证明该固定提交的 CI 矩阵通过,不保证所有 Windows 环境在 600 秒内完成。旧 `dsh-v0.6.0` 标签保持不变;后续发布候选调整为 DSH 0.6.1,使用独立新标签。CLI 0.3.0 已发布,不重复发布,也不发布 SkillHub 应用 Release。0.6.1 发布身份、安装包及公开 npm 验收按发布流程单独验证。
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Version `0.4.0` uses the independent `dsh-v0.4.0` tag. It adds versioned example
|
|||
|
||||
`0.5.0` adds read-only skill availability diagnosis in installed cards and conversations, with separate integrity, discovery and invocation-policy results. It retains the compatibility and inventory format of `0.4.0`.
|
||||
|
||||
`0.6.0` includes managed enable/disable, unified read-only update checks, and compact horizontal installed cards with a More menu. Shared inventory remains v2 without a coordinated CLI upgrade. Compatibility fixes and real historical-package acceptance are in progress; release remains blocked until they pass. Check npm for public availability.
|
||||
`0.6.1` includes managed enable/disable, unified read-only update checks, and compact horizontal installed cards with a More menu. Shared inventory remains v2 without a coordinated CLI upgrade. Compatibility acceptance with the actual CLI 0.2.0 package has passed. Check npm for public availability.
|
||||
|
||||
## Use an installed skill
|
||||
|
||||
|
|
@ -78,8 +78,8 @@ Published `0.1.0` still requires the original Cordis `4.0.4` baseline and does n
|
|||
Query the exact npm version first and install only after confirming availability. Check DSH, Node and a complete Cordis combination listed above, stop the target Web Profile Host, then install the plugin. A verified local tarball can also be installed using the next section:
|
||||
|
||||
```sh
|
||||
npm view @astron-team/skillhub-dsh@0.6.0 version
|
||||
dsh plugin --profile skillhub-test add @astron-team/skillhub-dsh@0.6.0 --ignore-scripts
|
||||
npm view @astron-team/skillhub-dsh@0.6.1 version
|
||||
dsh plugin --profile skillhub-test add @astron-team/skillhub-dsh@0.6.1 --ignore-scripts
|
||||
dsh --profile skillhub-test --host 127.0.0.1 --port 0 --no-open
|
||||
```
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ Replace `skillhub-test` with the intended Web Profile; initial creation is docum
|
|||
|
||||
Shared inventory remains strict v2 without a persisted `installationState`; activation is derived from trusted `SKILL.md` / `SKILL.md.disabled` entries. A coordinated CLI upgrade is not required. Published CLI `0.2.0` must retain normal queries and ordinary-target management; default update, sync and Suite paths must not unintentionally enable disabled targets. Historical explicit force install, upgrade and sync retain their overwrite semantics and may restore the standard entry; explicit remove retains deletion semantics. New readers must reflect these actual file changes.
|
||||
|
||||
Reading v1/v2 does not migrate it. Any v3 inventory from unpublished builds requires identity, entry and pending-transaction validation, an original-byte backup under the same lock, and safe conversion to v2. Never restore a stale backup over newer records. Ordinary journals remain readable by historical DSH; toggle journals isolate compatibility impact. Compatibility implementation and real historical-package acceptance are in progress; release remains blocked until they pass.
|
||||
Reading v1/v2 does not migrate it. Any v3 inventory from unpublished builds requires identity, entry and pending-transaction validation, an original-byte backup under the same lock, and safe conversion to v2. Never restore a stale backup over newer records. Ordinary journals remain readable by historical DSH; toggle journals isolate compatibility impact. Compatibility acceptance using alternating operations with actual historical packages has passed; check npm for public availability.
|
||||
|
||||
Authors provide examples at the fixed `.skillhub/examples.json` path inside each skill package. The file keeps its location after installation and follows the skill's version and fingerprint; no separate global example copy is created. Skills without examples use a custom task; invalid declarations remain ordinary files without exposing invalid examples.
|
||||
|
||||
|
|
@ -223,7 +223,7 @@ Choose “管理安装 → 检查可用性” on a conversational installed-skil
|
|||
The result distinguishes local integrity, current-session discovery, and user/model invocation policies. Shadowing, local modifications and unknown Host state are separate outcomes. A name-only lookup reports Host observations without proving installation ownership or integrity. Diagnosis never modifies, removes or enables/disables a skill, edits a draft, or sends a message. A historical result is not a guarantee about a later invocation.
|
||||
|
||||
|
||||
## Managed skill enable/disable (0.6.0)
|
||||
## Managed skill enable/disable (0.6.1)
|
||||
|
||||
Open “管理安装” and choose enable or disable on the explicit location row. User and project copies are independent: disabling the user copy does not disable a project copy. UI and conversation mutations require real human confirmation. `astron_skillhub_disable` / `astron_skillhub_enable` accept fresh installation references, never arbitrary paths.
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ SkillHub 的 DSH 原生插件。在 DSH 侧栏打开 **技能中心**,匿名
|
|||
|
||||
`0.5.0` 新增技能卡片及对话中的只读可用性诊断,分别报告文件完整性、宿主发现和调用策略,保留 `0.4.0` 的兼容范围及库存格式。
|
||||
|
||||
`0.6.0` 包含受管理技能启停、统一只读检查更新,以及横向已安装卡片和更多菜单。共享库存继续使用 v2,不要求用户同步升级 CLI;兼容修复及真实旧包验收进行中,未通过前阻断发布。公开可用状态以 npm 为准。
|
||||
`0.6.1` 包含受管理技能启停、统一只读检查更新,以及横向已安装卡片和更多菜单。共享库存继续使用 v2,不要求用户同步升级 CLI;已通过与 CLI 0.2.0 的真实旧包兼容验收。公开可用状态以 npm 为准。
|
||||
|
||||
## 使用已安装技能
|
||||
|
||||
|
|
@ -78,8 +78,8 @@ npm pack --ignore-scripts --pack-destination /absolute/path/to/candidate-output
|
|||
先查询 npm 上的精确版本,确认可用后安装。核对 DSH、Node 和上表完整 Cordis 组合,停止目标 Web Profile 的 Host,再执行插件安装;也可按下一节安装已验证的本地 tarball:
|
||||
|
||||
```sh
|
||||
npm view @astron-team/skillhub-dsh@0.6.0 version
|
||||
dsh plugin --profile skillhub-test add @astron-team/skillhub-dsh@0.6.0 --ignore-scripts
|
||||
npm view @astron-team/skillhub-dsh@0.6.1 version
|
||||
dsh plugin --profile skillhub-test add @astron-team/skillhub-dsh@0.6.1 --ignore-scripts
|
||||
dsh --profile skillhub-test --host 127.0.0.1 --port 0 --no-open
|
||||
```
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ dsh --profile skillhub-test --host 127.0.0.1 --port 0 --no-open
|
|||
|
||||
共享库存保持严格 v2,不写入 `installationState`;启停状态从可信的 `SKILL.md` / `SKILL.md.disabled` 入口派生,不要求 CLI 用户同步升级。已发布 CLI `0.2.0` 的正常查询和普通目标管理必须继续兼容;默认更新、同步及 Suite 不得无意重新启用停用目标。旧 CLI 显式 force 重装、更新、同步仍按原覆盖语义执行,可能恢复标准入口;显式 remove 仍可删除,新版重新读取须反映真实变化。
|
||||
|
||||
读取 v1/v2 不迁移。未发布构建产生的 v3 必须在身份、入口与未完成事务校验后,同锁保存原字节备份并安全转换为 v2;不能恢复过时备份覆盖新记录。旧 DSH 普通管理日志保持兼容,启停日志隔离影响。兼容修复及真实旧包验收正在进行,未通过前阻断发布。
|
||||
读取 v1/v2 不迁移。未发布构建产生的 v3 必须在身份、入口与未完成事务校验后,同锁保存原字节备份并安全转换为 v2;不能恢复过时备份覆盖新记录。旧 DSH 普通管理日志保持兼容,启停日志隔离影响。已通过真实旧包交替操作的兼容验收;公开可用状态以 npm 为准。
|
||||
|
||||
案例由作者随技能包提供固定文件 `.skillhub/examples.json`,安装后保持相同位置,跟随技能版本和指纹,不创建另一个全局案例副本。无案例时使用自定义任务;无效声明按普通文件保留,不提供错误的案例入口。
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ dsh plugin --profile skillhub-test remove @astron-team/skillhub-dsh
|
|||
诊断分别核验本地文件、当前会话加载结果,以及用户/模型调用策略。同名技能生效、本地修改和宿主无法核验会给出不同结论。按技能名查询只说明当前宿主观察到的事实,不证明安装来源或内容完整性。检查不会修改、卸载、启停技能或发送消息;历史结果不保证下一次调用仍然可用。
|
||||
|
||||
|
||||
## 受管理技能启停(0.6.0)
|
||||
## 受管理技能启停(0.6.1)
|
||||
|
||||
在“管理安装”中,对明确位置选择“停用”或“启用”。用户级和项目级副本独立管理;停用用户副本不会关闭项目副本。页面与对话均需真实用户确认,取消不写文件。对话工具 `astron_skillhub_disable` / `astron_skillhub_enable` 使用新查询中的位置引用,不能提交自由路径。
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@astron-team/skillhub-dsh",
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.1",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { spawn } from 'node:child_process';
|
||||
import { createHash, randomUUID } from 'node:crypto';
|
||||
import { closeSync, openSync } from 'node:fs';
|
||||
import { access, chmod, lstat, mkdir, mkdtemp, readFile, readdir, realpath, stat, writeFile } from 'node:fs/promises';
|
||||
import { access, chmod, open, lstat, mkdir, mkdtemp, readFile, readdir, realpath, stat, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
|
@ -125,7 +125,16 @@ export function safeAvailabilityCheckpoint(value) {
|
|||
const checkpoints = ['setup', 'install', 'install-poll', 'reference', 'before-digest', 'execute', 'assertion', 'after-digest', 'mutation'];
|
||||
const assertions = ['none', 'isError', 'kind', 'schemaVersion', 'association', 'scope', 'integrity', 'discovery', 'name', 'reason', 'invocation', 'installation', 'namespace', 'slug', 'version', 'fingerprint', 'render', 'privacy', 'unchanged', 'accepted', 'completed', 'reference'];
|
||||
if (!scenarios.includes(value.scenario) || !checkpoints.includes(value.checkpoint) || !assertions.includes(value.assertion)) return undefined;
|
||||
return { scenario: value.scenario, checkpoint: value.checkpoint, assertion: value.assertion };
|
||||
const result = { scenario: value.scenario, checkpoint: value.checkpoint, assertion: value.assertion };
|
||||
const statuses = ['accepted', 'not-confirmed', 'awaiting-confirmation', 'cancelled'];
|
||||
// Mirror the public installationFailureCodes contract; never forward error messages or IDs.
|
||||
const codes = ['CONTEXT_STALE', 'REQUEST_CONFLICT', 'SOURCE_NOT_CONFIGURED', 'SOURCE_DENIED',
|
||||
'SKILL_UNAVAILABLE', 'RATE_LIMITED', 'SOURCE_UNAVAILABLE', 'TARGET_UNAVAILABLE', 'UNCONFIRMED'];
|
||||
if (statuses.includes(value.install?.status)) {
|
||||
result.install = { status: value.install.status };
|
||||
if (codes.includes(value.install.code)) result.install.code = value.install.code;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function safeToggleCheckpoint(value) {
|
||||
|
|
@ -206,6 +215,50 @@ export function safeProbeFailure(log, probe) {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
// npm emits completed timers incrementally with --timing, even when killed before
|
||||
// its final timing.json. Never expose arbitrary timer names, URLs or log lines.
|
||||
const npmPreparationLogLimit = 1024 * 1024;
|
||||
export function summarizeNpmPreparation(log, truncated = false) {
|
||||
const phases = new Set(['idealTree', 'idealTree:init', 'idealTree:userRequests', 'idealTree:buildDeps',
|
||||
'idealTree:fixDepFlags', 'reify', 'reify:loadTrees', 'reify:diffTrees', 'reify:retireShallow',
|
||||
'reify:createSparse', 'reify:loadBundles', 'reify:unpack', 'reify:unretire', 'reify:build',
|
||||
'reify:trash', 'reify:save', 'command:install']);
|
||||
const result = { completedTimingsMs: {}, lastCompletedPhase: null,
|
||||
cacheObservations: { hit: 0, miss: 0, revalidated: 0 }, truncated: truncated || log.length > npmPreparationLogLimit };
|
||||
let bounded = log.slice(-npmPreparationLogLimit);
|
||||
if (result.truncated) {
|
||||
const newline = bounded.indexOf('\n');
|
||||
bounded = newline < 0 ? '' : bounded.slice(newline + 1);
|
||||
}
|
||||
for (const line of bounded.split('\n')) {
|
||||
const timer = /^npm timing ([A-Za-z:]+) Completed in ([0-9]+)ms\r?$/.exec(line);
|
||||
if (timer && phases.has(timer[1])) {
|
||||
const elapsed = Number(timer[2]);
|
||||
if (Number.isSafeInteger(elapsed) && elapsed <= 3600000) {
|
||||
result.completedTimingsMs[timer[1]] = elapsed;
|
||||
result.lastCompletedPhase = timer[1];
|
||||
}
|
||||
}
|
||||
const request = /^npm http fetch GET [0-9]{3} \S+ [0-9]+ms \(cache (hit|miss|revalidated)\)\r?$/.exec(line);
|
||||
if (request) result.cacheObservations[request[1]]++;
|
||||
// npm also logs package-cache lookups separately from HTTP fetch responses.
|
||||
if (/^npm http cache \S+ [0-9]+ms \(cache hit\)\r?$/.test(line)) result.cacheObservations.hit++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async function readPreparationLog(path) {
|
||||
let file;
|
||||
try {
|
||||
file = await open(path, 'r');
|
||||
const { size } = await file.stat();
|
||||
const buffer = Buffer.alloc(Math.min(size, npmPreparationLogLimit));
|
||||
const { bytesRead } = await file.read(buffer, 0, buffer.length, Math.max(0, size - buffer.length));
|
||||
return { text: buffer.subarray(0, bytesRead).toString('utf8'), truncated: size > buffer.length };
|
||||
} catch { return { text: '', truncated: false }; }
|
||||
finally { await file?.close(); }
|
||||
}
|
||||
|
||||
// Only npm's public download content is shared; every Host/config/profile stays fresh.
|
||||
export async function prepareDownloadCache(path) {
|
||||
if (!isAbsolute(path)) throw new Error('Download cache must be an absolute temporary directory.');
|
||||
|
|
@ -304,13 +357,15 @@ async function main() {
|
|||
try {
|
||||
const installStarted = Date.now();
|
||||
try {
|
||||
await run('install-host', [npm, 'install', '--prefix', root, '--ignore-scripts', '--no-audit', '--no-fund',
|
||||
// Prefer reuse within the original exact/range/peer constraints to reduce duplicate files.
|
||||
await run('install-host', [npm, 'install', '--prefer-dedupe', '--timing', '--loglevel=http', '--prefix', root, '--ignore-scripts', '--no-audit', '--no-fund',
|
||||
'--registry=https://registry.npmjs.org', `@deepseek-ai/dsh@${dshVersion}`, `pnpm@${PNPM_VERSION}`], 600000);
|
||||
} finally {
|
||||
env.npm_config_cache = join(root, 'npm-cache');
|
||||
summary.preparation.elapsedMs = Date.now() - installStarted;
|
||||
const installLog = await readFile(join(root, 'logs/install-host.log'), 'utf8').catch(() => '');
|
||||
summary.preparation.observedCodes = knownHostFailureCodes(installLog);
|
||||
const installLog = await readPreparationLog(join(root, 'logs/install-host.log'));
|
||||
summary.preparation.observedCodes = knownHostFailureCodes(installLog.text);
|
||||
summary.preparation.npm = summarizeNpmPreparation(installLog.text, installLog.truncated);
|
||||
}
|
||||
const dsh = join(root, 'node_modules/@deepseek-ai/dsh/lib/bin.js');
|
||||
await access(dsh);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ async function run(ctx, root, signal, checkpoint) {
|
|||
checkpoint.operation = { deadlineReached: false };
|
||||
mark('install');
|
||||
const result = await execute('astron_skillhub_install', { namespace, slug: 'document-helper', scope });
|
||||
checkpoint.install = { status: result.value?.status, code: result.value?.code };
|
||||
mark('assertion', 'isError');
|
||||
assert.equal(result.isError, false);
|
||||
mark('assertion', 'accepted');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { expect, test } from 'bun:test';
|
||||
import { installationFailureCodes } from '../src/host/installation-failure.js';
|
||||
// @ts-expect-error The development-only Node entry has no TypeScript declarations.
|
||||
import { safeProbeFailure } from '../scripts/smoke-native-host.mjs';
|
||||
|
||||
|
|
@ -80,3 +81,20 @@ test('operation observations and Host codes whitelist every field without reveal
|
|||
expect(safeProbeFailure(JSON.stringify({ ...base, probe: 'runtime-baseline' }), 'runtime-baseline').operation).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
test('availability admission diagnostics expose only known install status and failure code', () => {
|
||||
const checkpoint = { scenario: 'user-install', checkpoint: 'assertion', assertion: 'accepted',
|
||||
install: { status: 'not-confirmed', code: 'TARGET_UNAVAILABLE', message: '/secret/token' } };
|
||||
const record = { passed: false, probe: 'skill-availability', stage: 'host-assertion',
|
||||
failureCode: 'HOST_ASSERTION_FAILED', diagnostics: { availability: checkpoint } };
|
||||
const safe = safeProbeFailure(JSON.stringify(record), 'skill-availability');
|
||||
expect(safe.availability.install).toEqual({ status: 'not-confirmed', code: 'TARGET_UNAVAILABLE' });
|
||||
expect(JSON.stringify(safe)).not.toContain('secret');
|
||||
for (const code of installationFailureCodes) {
|
||||
checkpoint.install.code = code;
|
||||
expect(safeProbeFailure(JSON.stringify(record), 'skill-availability').availability.install.code).toBe(code);
|
||||
}
|
||||
checkpoint.install.status = '/private/status';
|
||||
checkpoint.install.code = '/private/code';
|
||||
expect(safeProbeFailure(JSON.stringify(record), 'skill-availability').availability.install).toBeUndefined();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { tmpdir } from 'node:os';
|
|||
import { join } from 'node:path';
|
||||
// Node-only CI entry; keep its helpers importable without starting a Host or downloading dependencies.
|
||||
// @ts-expect-error The development-only Node entry has no TypeScript declarations.
|
||||
import { prepareDownloadCache, scenarioProbes, hostInstallManifest, selectDshVersion, supportedDshVersions, isolatedEnvironment, profileInstallManifest, retainedCordisFamily, runBounded, selectTarball } from '../scripts/smoke-native-host.mjs';
|
||||
import { summarizeNpmPreparation, prepareDownloadCache, scenarioProbes, hostInstallManifest, selectDshVersion, supportedDshVersions, isolatedEnvironment, profileInstallManifest, retainedCordisFamily, runBounded, selectTarball } from '../scripts/smoke-native-host.mjs';
|
||||
|
||||
const roots: string[] = [];
|
||||
async function root() {
|
||||
|
|
@ -283,3 +283,29 @@ test('trusted temporary base aliases accept lexical and canonical children but n
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('npm preparation diagnostics retain only bounded known timing and cache observations', () => {
|
||||
const result = summarizeNpmPreparation([
|
||||
'npm timing idealTree:init Completed in 12ms',
|
||||
'npm timing idealTree:buildDeps Completed in 345ms',
|
||||
'npm timing reify:unpack Completed in 500ms',
|
||||
'npm timing secret/token Completed in 8ms',
|
||||
'npm http fetch GET 200 https://private.invalid/token 23ms (cache miss)',
|
||||
'npm http fetch GET 200 https://private.invalid/token 4ms (cache hit)',
|
||||
'npm http fetch GET 200 https://private.invalid/token 5ms (cache revalidated)',
|
||||
'npm timing reify Completed in 99999999999999999ms',
|
||||
'npm http cache diagnostic-fixture@http://private.invalid/package.tgz 0ms (cache hit)',
|
||||
].join('\n'));
|
||||
expect(result).toEqual({ completedTimingsMs: { 'idealTree:init': 12, 'idealTree:buildDeps': 345, 'reify:unpack': 500 },
|
||||
lastCompletedPhase: 'reify:unpack', cacheObservations: { hit: 2, miss: 1, revalidated: 1 }, truncated: false });
|
||||
expect(JSON.stringify(result)).not.toMatch(/secret|token|private/);
|
||||
expect(summarizeNpmPreparation('x'.repeat(1024 * 1024 + 1)).truncated).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
test('truncated npm diagnostic tails discard the partial first line', () => {
|
||||
const result = summarizeNpmPreparation('npm timing reify Completed in 1ms\nnpm timing idealTree Completed in 2ms', true);
|
||||
expect(result.completedTimingsMs).toEqual({ idealTree: 2 });
|
||||
expect(result.lastCompletedPhase).toBe('idealTree');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue