@hanamesh/dsh-app-host 0.6.20 → 0.6.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/CONTRACT.md CHANGED
@@ -149,9 +149,9 @@ DSH 绑定必须提供同一个 `single` domain 的一次完整镜像 publish
149
149
 
150
150
  | 路由 | 方法 | 输入 | 输出 |
151
151
  |---|---|---|---|
152
- | `/hanamesh/library` | GET | `q`、`category`(缺省 `hanamesh-app`;`category=` 空 = 全部)、`cursor` | `{source, items[], categories[], installed[], plugins[], restartRequired, page}`;`items[]` 每条目在目录字段之外加 `kind`(`application` = `hanamesh-app` + npm 包;`plugin` = 其它 npm 包;`listing` = 无 npm 包,仅收录)、`application`(兼容)、`installed`(应用行或插件行,或 `null`)、`upgradeAvailable`(目录 `latestVersion` 严格新于已装 `version`;已装行处于 `uninstalled-not-unloaded` 时恒 false);`categories` = 本页条目出现过的类别(目录契约没有类别清单端点) |
152
+ | `/hanamesh/library` | GET | `q`、`category`(缺省 `hanamesh-app`;`category=` 空 = 全部)、`cursor` | `{source, items[], categories[], installed[], plugins[], restartRequired, page}`;`items[]` 每条目在目录字段之外加 `kind`(`application` = `hanamesh-app` + npm 包;`plugin` = 其它 npm 包;`listing` = 无 npm 包,仅收录)、`application`(兼容)、`installed`(应用行或插件行,或 `null`)、`upgradeAvailable`(已选 npm `installTarget.version` 严格新于已装 `version`;不读取退休目录版本;已装行处于 `uninstalled-not-unloaded` 时恒 false);`categories` = 本页条目出现过的类别(目录契约没有类别清单端点) |
153
153
  | `/hanamesh/library/installedPlugins` | GET(POST → 405) | 无参数 | `{plugins[], apps[], restartRequired}`;`plugins[]`:`{packageName, version, kind:'plugin', bundle, active, state, errorCode?}`,`state` ∈ `installed` / `installed-not-loaded`(启动后新增)/ `uninstalled-not-unloaded`(启动后移除,进程仍加载)/ `invalid`(依赖里有、`node_modules` 里读不到);`apps[]` = 原已安装应用扫描行(`registered` / `installed-not-loaded` / `runtime-missing` / `invalid`) |
154
- | `/hanamesh/library/install` | POST | `{itemId, packageName?}` | 202 `{operationId,status:'started'}`;`packageName` 给出时用它精确查目录(`q=<包名>&category=`)再按 `itemId` 匹配;应用与插件同一路径 `dsh plugin add --save-exact <包>@<registry latest>`,应用另外供给 runtime;也是升级路径 |
154
+ | `/hanamesh/library/install` | POST | `{itemId, packageName?}` | 202 `{operationId,status:'started'}`;`packageName` 给出时用它精确查目录(`q=<包名>&category=`)再按 `itemId` 匹配;应用与插件同一路径走官方 CLI;必须有 installMethods、oneClickInstall.allowed:true 并选中固定 target,npm 用 target.version,GitHub 用完整 SHA/子目录/构建名单,不在安装时解析 latest。无匹配目标拒绝 NOT_INSTALLABLE 并返回具名原因。未提供 installMethods 拒绝安装;应用另外供给 runtime;也是升级路径 |
155
155
  | `/hanamesh/library/plugins/install` | POST | `{packageName}` | 202;包必须在启用目录里能搜到(否则 404 `CATALOG_ITEM_MISSING`);目录条目是应用则走应用路径 |
156
156
  | `/hanamesh/library/plugins/uninstall` | POST | `{packageName}` | 202;`dsh plugin remove <包>`;若该包是已装应用则转应用卸载(runtime 清理 + 运行中 `INSTANCE_IN_USE` 409) |
157
157
  | `/hanamesh/library/provision` | POST | `{appId, packageName, runtimeItem}` 三者必填 | 202;缺字段/非法 → 400 `INVALID_INPUT`(`LIB-PROVISION-INPUT`) |
@@ -231,7 +231,7 @@ Storage space budget is archive bytes + twice measured regular-file payload (new
231
231
 
232
232
  ## B17 套件安装包来源(2026-10-21 候选)
233
233
 
234
- AppHost 自有设置页「套件安装包来源」,公开同源端点 `GET|POST /hanamesh/library/suite-source`。GET 无参数,返回 `{mode, configuredRegistry, observation, traceId}`;`mode` 为 `configured|core`。`configured` 是唯一新增默认:继续使用原 `library.registry`(未设仍为 `https://registry.npmjs.org`)。市场目录 `library.sources` 的未设播种、显式空数组、自定义及已保存值均独立保持;目录 manifest URL 不是 npm registry。
234
+ AppHost 自有设置页「套件安装包来源」,公开同源端点 `GET|POST /hanamesh/library/suite-source`。GET 无参数,返回 `{mode, configuredRegistry, observation, traceId}`;`mode` 为 `configured|core`。`configured` 是唯一新增默认:使用显式 `library.registry`,未设时请求Core声明实际来源。市场目录 `library.sources` 的未设播种、显式空数组、自定义及已保存值均独立保持;目录 manifest URL 不是 npm registry。
235
235
 
236
236
  POST 严格只接受 `{mode:'configured'}` 或 `{mode:'core'}`,不接受 registry、版本或其他字段/查询参数。沿用精确 Host、同源 Origin、workspace-v1、Connection admission、逐操作 authorization、JSON/65536 字节及 no-store 规则。选择存自己的 `hanamesh_library` 单镜像可选 `suiteSource:{mode}`;未存值不改旧记录。来源、目录和网站交接设置按接收顺序串行读改写整个镜像,成功持久化后才发布新内存状态;保存失败 `500/SUITE_SOURCE_SAVE_FAILED`,旧选择和其他字段保持;关闭等待已排队写入。
237
237
 
@@ -239,7 +239,7 @@ GET 的 `observation` 为 `{observable:false,code}` 或 `{observable:true,state}
239
239
 
240
240
  明确选择 core 后,预览、确认、紧接执行之前各自重新读取当前 source 和 Core `getSuiteDeclaration({registry})`,并在异步声明返回后复核来源。`ready` 仅表示来源可读;兼容性仍唯一由 Core declaration 决定。disabled/failed/disposed 或无有效 registry 为 `409/CORE_SUITE_SOURCE_NOT_READY`;不缓存 ready、不回退配置 registry、不挑最新、不重试。沿用 declarationId、精确 tuple/metadata digest/repository、物理已装 baseline 和本人 planId。URL、selection、选择模式或提供服务的对象身份变化均终止原确认;新预览仍须本人确认。服务对象身份是 AppHost 内存内 opaque token,**不是 Core 的 generation/revision**;protocol 1 没有该事实或 watch,不能声称观察到两次读取间完全消失又恢复原状的事件。
241
241
 
242
- 最后执行仍调用现有官方 CLI `add --save-exact --strict-peer-dependencies --registry=<声明原址>`,精确三组件;正常市场外部包路径不变。正在执行时源连接丢失由实际 CLI/输运终态报告,不靠重试/退出屏障维持源。Core B17 服务只来自候选 `c5e1a7373b9896c385f14f79a64bb277f43bde5c` 的公开能力;无 Core 运行时 import,无 Desktop hook/bootstrap、三锁或先于插件加载的服务。源码/真实 Cordis+JSON domain(AUTH_DOUBLE)与正常包验证不等同真实原生安装或 owner 验收。
242
+ 最后调用现有官方 CLI,完整 argv 来自Core的升级 installation,安装一个精确总包;正常市场外部包路径不变。正在执行时源连接丢失由实际 CLI/输运终态报告,不靠重试/退出屏障维持源。Core B17 服务只来自候选 `c5e1a7373b9896c385f14f79a64bb277f43bde5c` 的公开能力;无 Core 运行时 import,无 Desktop hook/bootstrap、三锁或先于插件加载的服务。源码/真实 Cordis+JSON domain(AUTH_DOUBLE)与正常包验证不等同真实原生安装或 owner 验收。
243
243
 
244
244
  公开类型位于 `@hanamesh/dsh-app-host/dsh` 的 `SuiteSourceMode/CoreSuiteSourceState/SuiteSourceSettings/SuiteSourceSettingsInput`;页面导出 `SuiteSourceSettingsSection`。回归:`suite-source.test.mjs`、`handoff-integration.test.mjs` B17-CORDIS,变异 M84–M89(M61 检查全部后续读取被错误缓存)。
245
245
 
@@ -256,6 +256,6 @@ B17 review repair(同0.6.8候选):`AppHost` 未注入 `runtimeLedgerReader
256
256
 
257
257
  ## 0.6.16 市场查询来源与诊断(原协议1)
258
258
 
259
- 普通市场安装/升级的 latest 查询消费 DSH 公开 pluginManager.registries() 与公开 registryPlan/attributeFailure,沿用用户配置源顺序和私有源隔离规则;成功后将同一源选择传入原受控CLI:显式源用--registry=<source>,pnpm自有源(null)不传--registry以保留scope/认证配置。整个配置/查询/读回共30000ms,超时abort并报REGISTRY_LOOKUP_TIMEOUT,未提供公开能力/解析源报REGISTRY_CONFIG_UNAVAILABLE;原生异常为REGISTRY_LOOKUP_FAILED并保留cause。没有新的用户配置、请求字段或协议版本。既有 details.diagnostic 带脱敏类型/消息/cause.code/实际阶段及已知CLI exit/stderr摘要,同时写宿主日志;其它错误、回滚与重启语义保留。Core套件升级路径的声明/源和缺省值单独保留,不采用普通市场的源选择。
259
+ A2 0.6.22:普通插件必须有 Indexer 已审核的 installMethods 和 oneClickInstall.allowed:true;仅执行其 npm 精确版本或完整 GitHub SHA。缺目标具名拒绝,不根据包名查可变版本标签。CLI 使用显式 library.registry(若有),否则保留宿主原生 registry 配置。删除普通插件按包名内部直装方法、元数据查询及其专用设置/规则加载。套件仅透传 Core installCommand。安装失败保留脱敏 CLI/读回诊断及补偿结果;精确版本发布后即可原生验收。
260
260
 
261
- 0.6.16最小接线:公开规则模块从宿主manager同锚点/版本守卫加载,不在AppHost声明manager依赖/peer;找不到模块或公开函数则REGISTRY_CONFIG_UNAVAILABLE,无npmjs回退或自创排序。发布依赖闭包与0.6.15一致。
261
+ A2 0.6.22:升级tuple必须带Core生成的installation(name/version/command/args),name/version与该tuple一致。AppHost传实际profile和可选配置来源;未配置来源由Core显式声明。CLI直接执行args;confirmation同时绑定该定义。installCommand是Core的可空修复展示文本,不解析命令前缀。旧Core缺定义不回退自拼。
@@ -33,7 +33,7 @@ Internal connection HTTPS/bind-address handling changed upstream; the local HTTP
33
33
 
34
34
  ## Installation behavior
35
35
 
36
- 0.6.15 retains the monitored public CLI. CLI failures carry bounded, redacted stdout and stderr through the asynchronous operation's diagnostic details; the market shows the original cause in an expandable panel. The main failure message includes the exact pnpm category. A minimum-release-age rejection lists affected package versions, explains the default 24-hour requirement while deferring to the actual cutoff, explains the known first-entry-only defect in pnpm 11.7.0 bundled with DSH 0.2.0-rc.2, and directs the person to DSH Settings to check the version and upgrade DSH, or wait until the rule is met before a manual retry. No profile configuration, dependency exclusion or supply-chain policy is changed.
36
+ 0.6.15 retains the monitored public CLI. CLI failures carry bounded, redacted stdout and stderr through the asynchronous operation's diagnostic details; the market shows the original cause in an expandable panel. The main failure message includes the exact pnpm category. A minimum-release-age rejection lists affected package versions and retains the actual cutoff in technical details. Verify the exact installation target and upgrade DSH through its Settings if necessary. No profile configuration, dependency exclusion or supply-chain policy is changed.
37
37
 
38
38
  The official 0.2.0-rc.2 bundled pnpm 11.7.0 uses only the first matching package-name rule in `minimumReleaseAgeExclude`: an earlier exact old-version entry shadows a later exact new-version entry. The existing lockfile verifier can therefore reject an unrelated installation even though the new version is visibly listed. The 11.7.0 package obtained from npm has the same entry and core bytes as the supplied Desktop copy. Official Desktop 0.2.1-rc.3's pnpm 11.28.5 unions all matching exact versions and passes the same diagnostic profile. This establishes two tested endpoints, not the first fixed version. Both Desktop CLI and in-process manager use this pnpm path and set `ELECTRON_RUN_AS_NODE=1`; switching to the manager does not correct the 11.7.0 rule evaluation.
39
39
 
@@ -1,5 +1,21 @@
1
1
  # 客户端市场安装目标入口
2
2
 
3
+ ## 0.6.21 候选 · 固定 GitHub 安装目标
4
+
5
+ 目标新增 `installTarget`,采用 C-INDEXER-CATALOG 已确认的两个形状:
6
+
7
+ ```ts
8
+ type PackageInstallTarget =
9
+ | {source:'npm';name:string;version:string;allowBuild:string[]}
10
+ | {source:'github';repo:string;ref:string;path:string|null;packageName:string;allowBuild:string[]};
11
+ ```
12
+
13
+ GitHub ref 必须是收录时固定的完整 40 位 SHA,不在安装时解析分支。结构化目标必须带 itemId;App-host 从自己的启用目录 installMethods 解析并逐字段核对,整个条目 oneClickInstall.allowed=false 时拒绝,不能换用同名 npm 包。条目必须提供 installMethods 和 oneClickInstall.allowed:true,并选中匹配的固定目标;缺 installMethods、未获准或无匹配目标时返回 NOT_INSTALLABLE / details.reason=fixed-install-target-required(或提供方具名原因),不得退回 package 展示名解析可变版本。HanaMesh 视图拒绝退休的 latestVersion/license,确认页不显示版本行,升级按钮不拼目录版本;npm 升级按 installTarget.version 与已装实际版本比较。GET target/导航片段将 installTarget JSON 编码;POST install/网站 handoff 使用对象。响应 item.installTarget 是客户端确认回传的预期目标。目录 SHA、路径或构建许可变化会拒绝 CATALOG_CHANGED,必须重新查看;不能在确认之后自动换目标。
14
+
15
+ 确认沿现有市场表面,展示完整仓库、commit、子目录;allowBuild 非空时单列「会在本机执行构建脚本」和名单,并说明 pnpm 会保存许可。取消或预览不执行 CLI。本人确认后,仅调用原受控 `dsh plugin --profile <profile> add --save-exact [--allow-build=<项>…] github:owner/repo#<SHA>[&path:<目录>]`,每段都是独立 argv,不执行来源 command。npm 目标直接使用收录时固定的精确版本,不在安装时再解析版本。
16
+
17
+ GitHub CLI exit 0 后还须核对 profile 精确保存的 Git spec、模块包名/版本、dsh.bundle 和 profile 启用列表,随后报告 restart-required;重开后由原生 DSH 加载。不自行 clone/build,不改上游,不用进程内安装器。失败补偿沿原 CLI;pnpm-workspace.yaml 若发生变化,恢复报告保留 build-permissions 残留及原文件,不擅自撤销或静默宣称恢复。handoff 事件按完整目标匹配,不能只靠同名包把另一个 SHA 报为成功。
18
+
3
19
  网站发现:DSH 本地路由与交接监听的 `/hanamesh/library/install-target` 都按当前 `library.handoff.allowedOrigins` 回答跨来源 GET / OPTIONS;只回显获准 Origin(Vary: Origin),不使用通配 CORS。未获准来源为 403 ORIGIN_DENIED,正文无客户端能力。正常工作区同源读取、无 Origin 的本机工具读取保留。其余安装/状态路由继续需要各自认证与确认。
4
20
 
5
21
  轮询设置:`pollMs` 写入应用库真实 storage domain;保存、卸载再加载后保持。旧记录没有该字段时使用既有配置/默认 1500 ms,不重写其它存储数据。
@@ -14,7 +30,7 @@ AppHost `0.2.0-rc.10`(安装目标入口自 rc7;合约声明与随包套件
14
30
 
15
31
  在 DSH 的正常客户端模块系统中取得 `@hanamesh/dsh-app-host` 的公开客户端模块,调用 `openInstallTarget({itemId?, packageName?})`。已有客户端 `market` service 也提供同名方法:`market.openInstallTarget(target)`;原 `market.render({preferredSubsectionId})` 与 `setSettingsVisible()` 保持原协议。
16
32
 
17
- 至少一个标识;两者都给时必须匹配同一条目录项。拒绝所有其他字段,包括 version、url、downloadUrl。itemId 最长 160 字符;packageName 按 npm 包名语法校验、最长 214 字符。壳不得解析目录、代填版本/下载地址、点击私有 DOM、注入脚本、代按确认或访问组件状态。
33
+ 至少一个标识;两者都给时必须匹配同一条目录项。可选 installTarget 仅为上述目录目标的预期值;其它字段(包括顶层 version、url、downloadUrl)拒绝。itemId 最长 160 字符;packageName 按 npm 包名语法校验、最长 214 字符。壳不得解析目录、代填版本/下载地址、点击私有 DOM、注入脚本、代按确认或访问组件状态。
18
34
 
19
35
  入口先显现 AppHost 自己的侧栏市场覆盖层。若宿主已用 market 席位拥有市场表面(setSettingsVisible(false)),调用方需选中/呈现现有 market.render() 表面,AppHost 不叠加第二个覆盖层。即使尚未挂载,目标确认也保存在本客户端模块内,正常挂载后读回;不跨 profile 保存。
20
36
 
@@ -22,7 +38,7 @@ Promise 在目录解析后返回 `{status,itemId?,packageName?}`:
22
38
 
23
39
  | status | 意义 |
24
40
  | --- | --- |
25
- | confirmation-required | 市场已按当前目录显示名称、包名、版本和目录来源;尚未安装 |
41
+ | confirmation-required | 市场已按当前目录显示名称、包名和目录来源;结构化 GitHub 目标另显示完整 SHA/子目录;尚未安装 |
26
42
  | installing | 相同目录项已有安装进行中,复用该状态 |
27
43
  | already-installed | 当前 profile 已安装且目录没有升级,不重复安装 |
28
44
  | superseded | 解析过程中收到更新目标或取消,旧输入不再打开确认 |
@@ -31,9 +47,9 @@ Promise 在目录解析后返回 `{status,itemId?,packageName?}`:
31
47
 
32
48
  ## HTTP 契约
33
49
 
34
- 新增只读 `GET /hanamesh/library/target?itemId=...&packageName=...`,查询字段仍只接受上述两个标识。宿主用当前市场目录逐页解析,返回 `{item,source,traceId}`;item 含 kind、installed、upgradeAvailable 和原目录元数据,source 含市场自己的 manifestUrl;fixture 来源明确标「测试目录」。查询不会安装。
50
+ 只读 `GET /hanamesh/library/target?itemId=...&packageName=...` 接受两个标识及可选 JSON 编码 installTarget。宿主用当前市场目录逐页解析,返回 `{item,source,traceId}`;item 含 kind、installed、upgradeAvailable、选定 installTarget 和原目录元数据,source 含市场自己的 manifestUrl;fixture 来源明确标「测试目录」。查询不会安装。
35
51
 
36
- 同实例同端口的精确 `127.0.0.1:<port>` 与 `localhost:<port>` Host 同时可用(同一 parentOrigin 配置,无切换)。Origin 若出现必须与本次请求 Host 的 http origin 精确一致;两个别名仍是不同的浏览器 origin,不允许跨别名请求。未知 Host、其他端口、IPv6/尾点别名、外来或 opaque Origin 明确拒绝;非 iframe、authenticate、authorize 检查保持。GET 允许同源浏览器默认不带 Origin;不接受外来 Origin。实际安装只用原 `POST /hanamesh/library/install`,要求精确同源 Origin、`X-HanaMesh-Client: workspace-v1`、资源端身份及授权;重新解析当前目录,不信任请求内版本或地址。进行中返回原 `202 {operationId,status:'started'}`;相同包共享该 operationId。已经安装且没有目录升级时返回 `200 {status:'already-installed',itemId,packageName}`。安装事件/结果与既有安装器不改。目录版本是原目录 latestVersion,既有安装器仍自行核 registry 的 latest 并固定精确版本;最终显示的已安装版本来自 profile 扫描,并与操作结果版本一致才报告读回成功。
52
+ 同实例同端口的精确 `127.0.0.1:<port>` 与 `localhost:<port>` Host 同时可用(同一 parentOrigin 配置,无切换)。Origin 若出现必须与本次请求 Host 的 http origin 精确一致;两个别名仍是不同的浏览器 origin,不允许跨别名请求。未知 Host、其他端口、IPv6/尾点别名、外来或 opaque Origin 明确拒绝;非 iframe、authenticate、authorize 检查保持。GET 允许同源浏览器默认不带 Origin;不接受外来 Origin。实际安装只用原 `POST /hanamesh/library/install`,要求精确同源 Origin、`X-HanaMesh-Client: workspace-v1`、资源端身份及授权;重新解析当前目录,不信任请求内版本或地址。进行中返回原 `202 {operationId,status:'started'}`;相同包且相同结构化目标共享该 operationId,目标不同拒绝 CATALOG_CHANGED。已经安装且没有目录升级时返回 `200 {status:'already-installed',itemId,packageName}`。安装事件/结果与既有安装器不改。结构化 npm 来源只执行收录时固定的精确版本;GitHub 只执行已确认的 SHA。最终已安装版本来自 profile 扫描,GitHub 还须读回精确来源与启用项,均与操作结果一致才报告读回成功。
37
53
 
38
54
  ## 合约版本与一致性套件
39
55
 
@@ -80,7 +96,7 @@ node node_modules/@hanamesh/dsh-app-host/dist/install-target/run.js consumer --u
80
96
  一次安装(应用或插件、新装或升级)按以下顺序进行,同一宿主内同一时间只有一个 profile 写者(安装、补偿、卸载、provision 串行;`dsh plugin` 自己按每条命令加 profile 写锁):
81
97
 
82
98
  1. **基线**:profile `package.json` 里本包的规格、`dsh.profile.bundles`、`pnpm-lock.yaml`、`node_modules` 实际解析的版本;provision 前再记 runtime 根的 lib-provision ledger。
83
- 2. 官方 `dsh plugin add --save-exact <包>@<registry latest>`。
99
+ 2. 官方 `dsh plugin add --save-exact <包>@<审核目标精确版本>`;GitHub 使用目录中的完整40位SHA和已确认构建许可。
84
100
  3. **读回**:profile 规格与实际解析版本都等于固定版本才继续,否则 `INSTALL_READBACK_MISMATCH`。
85
101
  4. 应用:读 `app.json` 校验定义 → 随包 lib-provision 供给声明的 runtime。
86
102
 
@@ -95,7 +111,7 @@ node node_modules/@hanamesh/dsh-app-host/dist/install-target/run.js consumer --u
95
111
 
96
112
  `checks` 逐项列出 dependency、module、bundle、lockfile、bundles 与 `runtime:<item>` 的 expected/actual/ok。别的写者在此期间改动的其他依赖永不撤销,lockfile 因此无法证明已恢复时报 `CONCURRENT_PROFILE_WRITE`。未认领文件与 `<dataRoot>` 下的应用数据从不删除。失败不会自动重试。
97
113
 
98
- 补偿的 `add` / `install` 沿用本次 latest 查询与正向安装选中的同一 registry:显式源继续传 `--registry=<source>`,pnpm 自有源(`null`)继续省略该参数。回滚不会重新选源、查询 latest 或退到默认公共源;`remove` 参数保持原样。恢复动作列表保留原包操作摘要,不加入 registry URL。
114
+ 补偿的 `add` / `install` 沿用本次正向安装选中的同一 registry:显式源继续传 `--registry=<source>`,pnpm 自有源(`null`)继续省略该参数。回滚不会重新选源、重新解析版本或退到默认公共源;`remove` 参数保持原样。恢复动作列表保留原包操作摘要,不加入 registry URL。
99
115
 
100
116
  市场显示:`安装失败:<原码>(原因)` 后接 `已恢复到安装前状态(已逐项读回)` / `已恢复到原版本 x(已逐项读回)` / `安装前状态未被改动(已逐项读回)`;`failed` 显示 `恢复失败:<码>,未恢复:<项>,安装前原件:<目录>。未安装成功,也未回到安装前状态,需要人工处理`,不会显示为成功或「已回滚」。
101
117
 
package/docs/LIBRARY.md CHANGED
@@ -17,8 +17,6 @@
17
17
 
18
18
  钉版本 DSH 没有向插件公开 profile 目录或 profile 名。桌面壳通过 overlay 显式给出 `library.profileDir`(绝对路径)、`library.profileName`、`library.dshBin` 与 `nodeBinary`,显式值永远优先。**纯 DSH CLI profile(rc.26)** 下缺省值按 `CONTRACT.md`「应用库配置」推断:`profileDir` 来自本包自身真实安装位置所属的 profile、`profileName` 为其目录名、`dshBin` 只取启动本进程的 `@deepseek-ai/dsh/lib/bin.js`、`nodeBinary` 只在非 Electron 进程取 `process.execPath`。Electron 下仍必须由 dsh-runtime/core 提供绝对 `nodeBinary`;应用库不会在 Electron 里回退到 `process.execPath`。
19
19
 
20
- 安装先向 registry 的 `latest` 端点复核精确稳定版本,再用显式 Node 执行 DSH 自己的 `bin.js plugin --profile <name> add --save-exact <package>@<version>`。成功后读取包内 `app.json`;如果描述符声明 runtime,则调用随包内联的锁定 `@hanamesh/lib-provision@0.1.0-rc.3`。生产默认拒绝 prerelease;隔离 Verdaccio 验收可显式设 `allowPrerelease: true`。
21
-
22
20
  rc.10:add 之后的读回、定义校验或 provision 失败按基线补偿(新装 `dsh plugin remove`、升级 `dsh plugin add` 原规格,runtime 按 ownership 回收或按原定义重供),逐项读回后在 `library.install-failed` 的 `recovery` 里报告 `not-needed`/`restored`/`failed`;详见 `INSTALL_TARGET.md`「安装失败后的恢复」。
23
21
 
24
22
  安装/卸载完成只返回 `restart-required`,绝不静默重启 DSH。卸载只删除包与 lib-provision 账本认领的 runtime 文件,保留 `<dataRoot>` 下应用数据;彻底删除需用户手动处理对应数据目录。
@@ -43,7 +41,7 @@ rc.10:add 之后的读回、定义校验或 provision 失败按基线补偿(
43
41
  ## rc.28 · 市场浏览语义
44
42
 
45
43
  - 市场页默认 `category=`(全部);筛选:全部 / 应用(`hanamesh-app`)/ 插件(客户端视图:本页非应用的 npm 条目)/ 目录页里出现过的类别(累积自 `categories`)。路由 `/hanamesh/library` 的缺省 `category=hanamesh-app` 不变(向后兼容)。
46
- - 卡片:`kind · 发布者 · latestVersion · 类别`;已装行显示状态与版本;「升级到 x.y.z」出现在 `upgradeAvailable` 时;插件「卸载」走 `plugins/uninstall`。页面底部「已安装」列出应用与插件(含 bundle 未启用为 profile 层的提示)。
44
+ - 卡片:`kind · 发布者 · 类别`;已装行显示实际状态与版本;「升级」出现在 `upgradeAvailable` 时,npm 仅按 Indexer 固定 target.version 比较;确认页不显示目录版本;插件「卸载」走 `plugins/uninstall`。页面底部「已安装」列出应用与插件(含 bundle 未启用为 profile 层的提示)。
47
45
  - 「需重新打开客户端」横幅:任一装卸操作完成后,或列表 `restartRequired` 为真时显示;所有渠道均提示完全退出 HanaMesh 客户端或 DSH 后重新打开,关闭窗口不等于退出。不提供旧壳深链或 postMessage 重启桥;只有正式宿主重启 API 供给后才另行接入。
48
46
 
49
47
  ### 0.3.0 · catalog major compatibility
@@ -56,11 +54,7 @@ Both SOURCE fixture sets and suites ship as `@hanamesh/dsh-app-host/catalog/suit
56
54
 
57
55
  市场只显示一张官方、不计分的「HanaMesh 套件」卡。完整性只显示 Core 的 `completeness`,同时展示 `installed` 的要求范围、实际版本、状态和 `blocked` 的具名原因;Core 缺席或读取失败时完整性为 unknown,并保留物理包版本供诊断。Core、使用记录、应用库不再分别作为普通可安装卡。品牌插件只属于 HanaMesh 自有客户端。
58
56
 
59
- DSH 首次安装或缺组件时,完全退出客户端后执行同一行,再重新打开:
60
-
61
- ```sh
62
- dsh plugin --profile desktop add --save-exact '@hanamesh/core@0.3.21' '@hanamesh/usage@0.3.0-rc.6' '@hanamesh/dsh-app-host@0.6.14'
63
- ```
57
+ DSH 首次安装使用公开 Core 精确发布元数据中的 `hanameshSuiteInstall.command`;已运行客户端的修复命令使用 Core 声明 `installCommand`。两者由 Core 同一格式器提供,只安装总包的精确版本,完全退出后安装并重新打开。缺命令显示具名不可用,不自造组件命令。
64
58
 
65
59
  套件版本范围、是否完整、候选来源及兼容/升级规则唯一属于 Core。App-host 只读物理安装版本,不能用它们推导兼容或安装许可;不自行读取 npm latest、计算组件组合、校验候选宿主 peers 或持有 Core 的版本范围副本。普通安装/卸载接口继续拒绝套件包。
66
60
 
@@ -87,12 +81,7 @@ HanaMesh 视图原样保留可选 `official:boolean`(包括 false)与 C-INDE
87
81
 
88
82
  ## 0.6.16 · 安装查询来源与失败诊断
89
83
 
90
- 市场通过公开 `pluginManager.registries()` 读取 DSH 的首选源、顺序 fallback 和 pnpm 已解析的源;从宿主自己的安装锚点按既有版本守卫定位 `exports['./registry']`,动态加载其公开 `registryPlan` / `attributeFailure`,不读取私有配置或发明兜底源。`library.registry` 若显式设置,沿用 DSH 对请求源的规则(外部私有源单独使用)。查询 latest 成功后,精确版本及同一源选择传入原受控CLI:显式配置源用 `add --save-exact --registry=<source>`,DSH的pnpm自有源(null)沿原生语义不传 `--registry`,保留scope源和认证配置;缺公开源能力或 pnpm 自有源无法解析,明确 `REGISTRY_CONFIG_UNAVAILABLE`,不猜 npmjs。DSH rc.2、alpha.2、rc.3 均公开这些端口。
91
-
92
- 整个查询(配置读取、配置源顺序查询、JSON 读回)共享 30000ms 上限。到期 abort 并报 `REGISTRY_LOOKUP_TIMEOUT`,不重试、不在超时后继续 fallback。只有 DSH 规则允许的网络/未找到失败才查询其已配置后继源;HTTP 认证/权限拒绝及无效 JSON 不换源。包版本稳定性策略、受控 CLI、读回、补偿、restart-required 行为保留。
93
-
94
- 原生 lookup 异常包为 `REGISTRY_LOOKUP_FAILED`,原 Error 保留于 cause;已知具名码保留。`library.*-failed.details.diagnostic` 最多4001字符,含脱敏异常类型/消息/cause.code 与实际 registry configuration/fetch/readback、CLI run、profile readback 阶段;CLI 非零另含 exit、signal 与 stderr/输出摘要。同一摘要及 operationId 写宿主 logger.warn(logger失效时安全console),UI已有可展开技术详情显示,并提示检查 DSH 源设置/网络后手动重试。不记录请求头、正文、环境、stack 或任意 details;不猜历史丢失的原错。
84
+ D1: DSH suite installation and upgrades use only @hanamesh/hanamesh. Core owns the bundle metadata and exact three dependency targets. A confirmed strict CLI add changes the bundle once; readback checks its saved version, activation and locked physical children. The suite display version is the bundle version. Ordinary market install/remove denies the bundle.
95
85
 
96
- 规则模块只由宿主公开exports提供;普通DSH用与manager版本查询相同createRequire(anchor),Desktop限已证明的runtimeDir与匹配版本。没有新增manager依赖/peer、没有生产规则副本;无法定位/加载规则或端口不完整均REGISTRY_CONFIG_UNAVAILABLE。钉SHA的rc.2规则仅位于tests/fixtures,不进入发布包。
97
86
 
98
- D1: DSH suite installation and upgrades use only @hanamesh/hanamesh. Core owns the bundle metadata and exact three dependency targets. A confirmed strict CLI add changes the bundle once; readback checks its saved version, activation and locked physical children. The suite display version is the bundle version. Ordinary market install/remove denies the bundle.
87
+ A2 0.6.22:普通插件必须有 Indexer 已审核的 installMethods 和 oneClickInstall.allowed:true;仅执行其 npm 精确版本或完整 GitHub SHA。缺目标具名拒绝,不根据包名查可变版本标签。CLI 使用显式 library.registry(若有),否则保留宿主原生 registry 配置。删除普通插件按包名内部直装方法、元数据查询及其专用设置/规则加载。套件仅透传 Core installCommand。安装失败保留脱敏 CLI/读回诊断及补偿结果;精确版本发布后即可原生验收。
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "package": {
3
3
  "name": "@hanamesh/dsh-app-host",
4
- "version": "0.6.20",
4
+ "version": "0.6.22",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "source": "git+https://github.com/yzsnstotz/hanamesh-plugin-app-host.git",
7
7
  "purpose": "Application ownership, view leases, Router forwarding, and application library for official DSH"
@@ -291,18 +291,18 @@
291
291
  "reviewDocs": "cce157612b1390b845ccca3f0632247de9fb3229"
292
292
  },
293
293
  "@hanamesh/core": {
294
- "version": "0.3.28",
294
+ "version": "0.3.30",
295
295
  "license": "MIT",
296
- "source": "https://registry.npmjs.org/@hanamesh/core/-/core-0.3.28.tgz",
296
+ "source": "https://registry.npmjs.org/@hanamesh/core/-/core-0.3.30.tgz",
297
297
  "purpose": "Development-only public Core suite declaration and provider-owned install command for clean CI; never runtime or bundled",
298
298
  "usedAs": [
299
299
  "devDependencies"
300
300
  ],
301
301
  "specs": {
302
- "devDependencies": "^0.3.28"
302
+ "devDependencies": "^0.3.30"
303
303
  },
304
- "sourceCommit": "62059ca586be2bffa054b5d392ebde41c51939ec",
305
- "archiveSha256": "9ef8c250efa3bc46de583dc82103719d71d9f3dcc2233fd49731b4467337efa9",
304
+ "sourceCommit": "11335137f909d28cd5724d6d206b770841ddb653",
305
+ "archiveSha256": "6dfa824891aa9115052473e1b27cf87811ff47a757addd812f6d6daf9c35c338",
306
306
  "licenseNotice": "MIT License\n\nCopyright (c) 2026 HanaMesh\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n",
307
307
  "licenseSha256": "cd29e05178eecb3207c494901de7d41785a7b31216e94ab37f3e104e93f3f3b1"
308
308
  }
package/docs/SECURITY.md CHANGED
@@ -8,6 +8,8 @@ D-214: verification is trusted producer/client-view configuration, never a Targe
8
8
 
9
9
  # 安全与运行边界
10
10
 
11
+ 0.6.22 固定目标安装(A2):仅消费 Indexer 的完整 SHA/包名/子目录/构建许可,且与 App-host 当前启用目录逐字段核对。条目必须有 installMethods 和 oneClickInstall.allowed:true,并选中其中的固定目标;缺方法、未获准或无匹配目标即 NOT_INSTALLABLE,不从 npm 展示名回退解析 latest。HanaMesh 视图拒绝退休的 latestVersion/license,确认页和升级按钮不显示目录版本;npm 升级只比较固定目标 version 与已装实际版本。网站或客户端的 installTarget 是预期值,不是任意执行源;未知字段、浮动 ref、参数注入/越界目录拒绝,套件保护名单同样适用于 Git 包。既有确认回传完整目标,目录变更不得偷换 SHA 或放行额外脚本。官方 CLI 用 argv,不执行采集来源的 shell command;受控独立 Node、Git helper 与监测边界不变。脚本许可由 pnpm 管理并可能持久化,安装失败保留具体 build-permissions 残留,不手改策略、不冒充恢复。仍不是 OS 沙箱。
12
+
11
13
  ## 可信边界
12
14
 
13
15
  注册描述符、DSH bridge 与宿主代码可信;浏览器请求、应用 HTTP 内容、旧租约和磁盘错误不可信。浏览器不能传命令、argv、env、cwd、dataDir 或任意上游 URL。实际 HTTP 拒绝和负向测试见 acceptance。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanamesh/dsh-app-host",
3
- "version": "0.6.20",
3
+ "version": "0.6.22",
4
4
  "description": "HanaMesh application ownership, durable view leases, and application library for official DSH 0.2.x",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -68,6 +68,28 @@
68
68
  "docs/DSH_COMPATIBILITY.md",
69
69
  "docs/INSTALLATION_STATUS.md"
70
70
  ],
71
+ "scripts": {
72
+ "build": "node scripts/build.mjs",
73
+ "test": "node scripts/safe-git.mjs node --test --test-reporter=tap --test-concurrency=1 tests/*.test.mjs",
74
+ "test:contract": "pnpm run build && node --test --test-reporter=tap --test-concurrency=1 tests/install-target-contract.test.mjs tests/catalog-major-compat.test.mjs",
75
+ "test:mutation": "node scripts/safe-git.mjs node scripts/mutations.mjs",
76
+ "test:types": "tsc --noEmit --strict --target ES2022 --module NodeNext --moduleResolution NodeNext tests/consumer.mts",
77
+ "test:native-driver": "node scripts/safe-git.mjs node --test --test-reporter=tap tests/native/driver.test.mjs",
78
+ "check": "node scripts/safe-git.mjs node scripts/check.mjs",
79
+ "prepack": "npm run build",
80
+ "demo": "node examples/demo.mjs",
81
+ "preflight:dsh": "node scripts/preflight-dsh.mjs",
82
+ "check:consistency": "node scripts/check-consistency.mjs",
83
+ "check:toolchain": "node scripts/check-toolchain.mjs",
84
+ "preflight": "node scripts/preflight.mjs",
85
+ "verify:pack": "node scripts/safe-git.mjs node scripts/verify-package.mjs",
86
+ "check:devkit-supply": "node scripts/check-devkit-consumption.mjs",
87
+ "check:provision-supply": "node scripts/check-provision-consumption.mjs",
88
+ "install:safe": "node scripts/safe-git.mjs pnpm install --frozen-lockfile",
89
+ "release:check": "hanamesh-release check --lockfile pnpm-lock.yaml",
90
+ "release:publish": "hanamesh-release publish --lockfile pnpm-lock.yaml",
91
+ "prepublishOnly": "hanamesh-release source-guard"
92
+ },
71
93
  "dsh": {
72
94
  "bundle": {
73
95
  "patch": "./profile/cordis.patch.yml"
@@ -115,7 +137,7 @@
115
137
  }
116
138
  },
117
139
  "devDependencies": {
118
- "@hanamesh/core": "^0.3.28",
140
+ "@hanamesh/core": "^0.3.30",
119
141
  "@deepseek-ai/cordis": "4.0.5-alpha.1",
120
142
  "@deepseek-ai/dsh-host-webserver": "0.2.1-alpha.2",
121
143
  "@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
@@ -132,27 +154,8 @@
132
154
  "@deepseek-ai/dsh-client-ui-renderer": "0.2.1-alpha.2",
133
155
  "@hanamesh/release-platform-gate": "file:vendor/hanamesh-release-platform-gate-0.1.10.tgz"
134
156
  },
157
+ "packageManager": "pnpm@10.33.0",
135
158
  "bundleDependencies": [
136
159
  "@hanamesh/lib-provision"
137
- ],
138
- "scripts": {
139
- "build": "node scripts/build.mjs",
140
- "test": "node scripts/safe-git.mjs node --test --test-reporter=tap --test-concurrency=1 tests/*.test.mjs",
141
- "test:contract": "pnpm run build && node --test --test-reporter=tap --test-concurrency=1 tests/install-target-contract.test.mjs tests/catalog-major-compat.test.mjs",
142
- "test:mutation": "node scripts/safe-git.mjs node scripts/mutations.mjs",
143
- "test:types": "tsc --noEmit --strict --target ES2022 --module NodeNext --moduleResolution NodeNext tests/consumer.mts",
144
- "test:native-driver": "node scripts/safe-git.mjs node --test --test-reporter=tap tests/native/driver.test.mjs",
145
- "check": "node scripts/safe-git.mjs node scripts/check.mjs",
146
- "demo": "node examples/demo.mjs",
147
- "preflight:dsh": "node scripts/preflight-dsh.mjs",
148
- "check:consistency": "node scripts/check-consistency.mjs",
149
- "check:toolchain": "node scripts/check-toolchain.mjs",
150
- "preflight": "node scripts/preflight.mjs",
151
- "verify:pack": "node scripts/safe-git.mjs node scripts/verify-package.mjs",
152
- "check:devkit-supply": "node scripts/check-devkit-consumption.mjs",
153
- "check:provision-supply": "node scripts/check-provision-consumption.mjs",
154
- "install:safe": "node scripts/safe-git.mjs pnpm install --frozen-lockfile",
155
- "release:check": "hanamesh-release check --lockfile pnpm-lock.yaml",
156
- "release:publish": "hanamesh-release publish --lockfile pnpm-lock.yaml"
157
- }
158
- }
160
+ ]
161
+ }
@@ -585,16 +585,6 @@
585
585
  "homepage": {
586
586
  "$ref": "#/$defs/hana_httpsUri"
587
587
  },
588
- "latestVersion": {
589
- "type": "string",
590
- "minLength": 1,
591
- "maxLength": 64
592
- },
593
- "license": {
594
- "type": "string",
595
- "minLength": 1,
596
- "maxLength": 80
597
- },
598
588
  "categories": {
599
589
  "type": "array",
600
590
  "maxItems": 32,
@@ -658,6 +648,32 @@
658
648
  },
659
649
  "suite": {
660
650
  "$ref": "#/$defs/hana_suite"
651
+ },
652
+ "installMethods": {
653
+ "type": "array",
654
+ "items": {
655
+ "$ref": "#/$defs/hana_installMethod"
656
+ }
657
+ },
658
+ "oneClickInstall": {
659
+ "type": "object",
660
+ "additionalProperties": false,
661
+ "required": [
662
+ "allowed",
663
+ "reason"
664
+ ],
665
+ "properties": {
666
+ "allowed": {
667
+ "type": "boolean"
668
+ },
669
+ "reason": {
670
+ "type": [
671
+ "string",
672
+ "null"
673
+ ],
674
+ "maxLength": 160
675
+ }
676
+ }
661
677
  }
662
678
  },
663
679
  "allOf": [
@@ -1231,6 +1247,77 @@
1231
1247
  "$ref": "#/$defs/hana_page"
1232
1248
  }
1233
1249
  }
1250
+ },
1251
+ "hana_installMethod": {
1252
+ "type": "object",
1253
+ "additionalProperties": false,
1254
+ "required": [
1255
+ "channel",
1256
+ "kind",
1257
+ "spec",
1258
+ "command",
1259
+ "allowBuild",
1260
+ "sourceId",
1261
+ "sourceRef"
1262
+ ],
1263
+ "properties": {
1264
+ "channel": {
1265
+ "enum": [
1266
+ "hanamesh",
1267
+ "dsh",
1268
+ "claude",
1269
+ "codex"
1270
+ ]
1271
+ },
1272
+ "kind": {
1273
+ "enum": [
1274
+ "npm",
1275
+ "github",
1276
+ "agent"
1277
+ ]
1278
+ },
1279
+ "spec": {
1280
+ "type": "string",
1281
+ "minLength": 1,
1282
+ "maxLength": 4096
1283
+ },
1284
+ "command": {
1285
+ "type": [
1286
+ "string",
1287
+ "null"
1288
+ ],
1289
+ "maxLength": 4096
1290
+ },
1291
+ "allowBuild": {
1292
+ "type": "array",
1293
+ "items": {
1294
+ "type": "string",
1295
+ "minLength": 1,
1296
+ "maxLength": 2048
1297
+ }
1298
+ },
1299
+ "sourceId": {
1300
+ "type": "string",
1301
+ "minLength": 1,
1302
+ "maxLength": 120
1303
+ },
1304
+ "sourceRef": {
1305
+ "type": "string",
1306
+ "minLength": 1,
1307
+ "maxLength": 512
1308
+ },
1309
+ "npmOwnership": {
1310
+ "type": "object"
1311
+ },
1312
+ "target": {
1313
+ "$ref": "https://hanamesh.com/schemas/app-host/install-target.schema.json#/$defs/packageInstallTarget"
1314
+ },
1315
+ "targetUnavailableReason": {
1316
+ "type": "string",
1317
+ "minLength": 1,
1318
+ "maxLength": 160
1319
+ }
1320
+ }
1234
1321
  }
1235
1322
  },
1236
1323
  "allOf": [