@hanamesh/dsh-app-host 0.2.0-rc.2 → 0.6.12

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.
Files changed (120) hide show
  1. package/LICENSE +4 -7
  2. package/README.md +136 -13
  3. package/consistency.json +41 -6
  4. package/dist/catalog/fixtures/consumer.json +35 -0
  5. package/dist/catalog/fixtures/provider.json +107 -0
  6. package/dist/catalog/suite.d.ts +4 -0
  7. package/dist/catalog/suite.js +50 -0
  8. package/dist/client-ui.d.ts +21 -3
  9. package/dist/client-ui.js +961 -94
  10. package/dist/client.js +2 -2
  11. package/dist/control-origin.js +9 -0
  12. package/dist/descriptor.js +6 -0
  13. package/dist/dsh-compat.js +6 -0
  14. package/dist/dsh.d.ts +21 -4
  15. package/dist/dsh.js +149 -58
  16. package/dist/gateway.js +140 -11
  17. package/dist/git-process.js +64 -0
  18. package/dist/git-worker.js +50 -0
  19. package/dist/index.d.ts +9 -7
  20. package/dist/install-target/fixtures/catalog.json +58 -0
  21. package/dist/install-target/fixtures/consumer-cases.json +148 -0
  22. package/dist/install-target/fixtures/install-failure-cases.json +58 -0
  23. package/dist/install-target/fixtures/provider-cases.json +618 -0
  24. package/dist/install-target/fixtures/reference-consumer.js +37 -0
  25. package/dist/install-target/fixtures/source-profile.js +123 -0
  26. package/dist/install-target/harness.js +43 -0
  27. package/dist/install-target/run.js +28 -0
  28. package/dist/install-target/suite.d.ts +19 -0
  29. package/dist/install-target/suite.js +328 -0
  30. package/dist/installation-status.d.ts +21 -0
  31. package/dist/installation-status.js +2 -0
  32. package/dist/library/catalog.js +134 -19
  33. package/dist/library/client-identity.js +31 -0
  34. package/dist/library/desktop-cli.js +7 -0
  35. package/dist/library/domain.js +3 -1
  36. package/dist/library/handoff.js +246 -0
  37. package/dist/library/install.js +138 -32
  38. package/dist/library/installation-cli.js +48 -0
  39. package/dist/library/installation-command-worker.js +27 -0
  40. package/dist/library/installation-status-http.js +26 -0
  41. package/dist/library/installation-status.js +91 -0
  42. package/dist/library/installation-worker.js +44 -0
  43. package/dist/library/locate.js +70 -9
  44. package/dist/library/node-binding.js +11 -0
  45. package/dist/library/recovery.js +66 -0
  46. package/dist/library/routes.js +90 -14
  47. package/dist/library/service.js +123 -27
  48. package/dist/library/suite.js +130 -0
  49. package/dist/manager.js +24 -8
  50. package/dist/node-runtime/domain.js +6 -0
  51. package/dist/node-runtime/releases.json +59 -0
  52. package/dist/node-runtime/service.js +101 -0
  53. package/dist/owned-probe-diagnostic.js +53 -0
  54. package/dist/router/receipts.js +8 -11
  55. package/dist/router/routes.js +7 -7
  56. package/dist/routes.js +9 -8
  57. package/dist/runtime.js +11 -1
  58. package/dist/usage-evidence.js +18 -86
  59. package/dist/workspace-origin.js +17 -0
  60. package/docs/APP_PACKAGE.md +4 -0
  61. package/docs/CONTRACT.md +68 -10
  62. package/docs/DSH_COMPATIBILITY.md +94 -0
  63. package/docs/DSH_INTEGRATION.md +41 -0
  64. package/docs/INSTALLATION_STATUS.md +96 -0
  65. package/docs/INSTALL_TARGET.md +130 -0
  66. package/docs/LIBRARY.md +44 -3
  67. package/docs/LICENSES.json +189 -61
  68. package/docs/PROVENANCE.json +53 -12
  69. package/docs/SECURITY.md +31 -0
  70. package/node_modules/@hanamesh/lib-provision/LICENSE +15 -0
  71. package/node_modules/@hanamesh/lib-provision/README.md +134 -0
  72. package/node_modules/@hanamesh/lib-provision/THIRD_PARTY_NOTICES.md +17 -0
  73. package/node_modules/@hanamesh/lib-provision/bin/hanamesh-provision.js +13 -0
  74. package/node_modules/@hanamesh/lib-provision/consistency.json +20 -0
  75. package/node_modules/@hanamesh/lib-provision/contract/CHANGELOG.md +5 -0
  76. package/node_modules/@hanamesh/lib-provision/contract/conformance.mjs +80 -0
  77. package/node_modules/@hanamesh/lib-provision/contract/fixtures/consumer.json +9 -0
  78. package/node_modules/@hanamesh/lib-provision/contract/fixtures/provider.json +11 -0
  79. package/node_modules/@hanamesh/lib-provision/contract/lib-provision.schema.json +47 -0
  80. package/node_modules/@hanamesh/lib-provision/package.json +50 -0
  81. package/package.json +81 -33
  82. package/schemas/catalog-provider-page.schema.json +4 -2
  83. package/schemas/catalog-source.schema.json +9 -7
  84. package/schemas/install-target.schema.json +298 -0
  85. package/dist/provision/LICENSE +0 -6
  86. package/dist/provision/THIRD_PARTY_NOTICES.md +0 -13
  87. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/archive.d.ts +0 -0
  88. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/archive.js +0 -0
  89. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/check.d.ts +0 -0
  90. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/check.js +0 -0
  91. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/checkpoints.d.ts +0 -0
  92. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/checkpoints.js +0 -0
  93. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/cli.d.ts +0 -0
  94. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/cli.js +0 -0
  95. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/download.d.ts +0 -0
  96. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/download.js +0 -0
  97. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/errors.d.ts +0 -0
  98. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/errors.js +0 -0
  99. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/extract.d.ts +0 -0
  100. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/extract.js +0 -0
  101. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/fsutil.d.ts +0 -0
  102. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/fsutil.js +0 -0
  103. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/index.d.ts +0 -0
  104. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/index.js +0 -0
  105. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/ledger.d.ts +0 -0
  106. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/ledger.js +0 -0
  107. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/manifest.d.ts +0 -0
  108. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/manifest.js +0 -0
  109. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/paths.d.ts +0 -0
  110. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/paths.js +0 -0
  111. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/platform.d.ts +0 -0
  112. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/platform.js +0 -0
  113. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/provision.d.ts +0 -0
  114. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/provision.js +0 -0
  115. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/tar.d.ts +0 -0
  116. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/tar.js +0 -0
  117. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/types.d.ts +0 -0
  118. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/types.js +0 -0
  119. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/zip.d.ts +0 -0
  120. /package/{dist/provision → node_modules/@hanamesh/lib-provision/lib}/zip.js +0 -0
@@ -0,0 +1,17 @@
1
+ /** Browser-safe validation. Privileged origins do not have a WHATWG URL.origin in Node. */
2
+ export const DESKTOP_ORIGIN = 'dsh-app://app';
3
+ export function isWorkspaceOrigin(value) {
4
+ if (value === DESKTOP_ORIGIN) return true;
5
+ try {
6
+ const url = new URL(value);
7
+ return url.origin === value && url.protocol === 'http:' && url.hostname === '127.0.0.1' && Boolean(url.port);
8
+ } catch { return false; }
9
+ }
10
+ /** A document URL's origin; never turn an arbitrary opaque scheme into a trusted origin. */
11
+ export function documentOrigin(value) {
12
+ try {
13
+ const url = new URL(value);
14
+ if (url.protocol === 'dsh-app:' && url.host === 'app' && !url.username && !url.password) return DESKTOP_ORIGIN;
15
+ return url.origin;
16
+ } catch { return undefined; }
17
+ }
@@ -36,3 +36,7 @@ export async function apply(ctx, config = {}) {
36
36
  ```
37
37
 
38
38
  预发布版本只用于隔离验收且不得成为 `latest`;应用库生产路径只接受 registry `latest` 返回的稳定精确版本。发布者必须同时保留 npm 包完整性、runtime 三方来源与 sha256、以及与 app-host 精确 peer 版本对应的可复核构建记录。
39
+
40
+ ## Existing configuration and original upstream archives (D-230)
41
+
42
+ The general public AppHost config.applications API remains available for trusted registered definitions. It does not install a package, synthesize a publisher declaration or create an installed-application row. The library installer can fall back to app.json, but the installed-app scanner requires hanamesh.app. An original upstream archive with only npm main/bin is not automatically a HanaMesh application bundle. These are separate existing paths, not a new requirement that all possible upstream distributions must be impossible to integrate. Exact package/item/app/deployment/Hana readbacks and legal original presentation/success facts still need evidence; no wrapper or SDK is generated by this rule.
package/docs/CONTRACT.md CHANGED
@@ -1,3 +1,5 @@
1
+ 0.6.7 adds the explicitly consented public installation-status capability on both existing library listeners. Full request/response, strict refusals, configured official-reader support and scope/lifecycle are defined in [INSTALLATION_STATUS.md](INSTALLATION_STATUS.md); it does not authorize installation or alter recording consent.
2
+
1
3
  # 公开契约 v1(候选,0.1.0-rc.41;rc.41 卸载 runtime 时清理生成的 Python 字节码缓存、未知残留仍 fail-closed;rc.40 市场只读查询待启动实例,ready 后一次 resume;前台即时 heartbeat 与失效帧显式清理;rc.39 Market seat 内打开应用时将原有应用帧固定铺满原生窗口并覆盖 Settings,同一回执/租约/关闭动作;rc.38 投射时仅提升 shell.overlay 层级以盖过原生 Settings;rc.35 市场开关按钮即时反馈、owned 应用已退出时提前结束 TERM 等待并确认精确进程组终止;rc.34 内置市场消费 heartbeat/视图清理;rc.33 owned 回收与启动握手分阶段限时;rc.32 客户端 `market` 席位与 dshmarket 互斥、壳内重启改走 postMessage 桥(见「市场席位」);rc.28 HanaMesh 市场:插件与应用同路径装卸、已装含插件、可升级/需重启、`installedPlugins` 与 `plugins/*` 路由、`LIB-PROVISION-INPUT` 400(见「市场」);rc.27 应用使用证据经 usage 插件 record 座位上报(见「应用使用证据」);rc.26 应用库默认目录源 + 纯 DSH 位置推断(见「应用库配置」);rc.15 目录条目校验补 updatedAt;rc.16 应用库默认 category=hanamesh-app、q/cursor 透传;rc.13/rc.14 与 rc.12 契约相同:rc.13 补许可证/repository/精确 peer,rc.14 去掉客户端对 hanameshCore 的读取)
2
4
 
3
5
  冷恢复中断实例时,将对应旧活跃租约失效并在同一镜像记 `view.expired(reason=host-restart)`;旧视图仍可按原身份恢复并轮换凭据,新视图关闭不受旧租约占用。只有确认进程组退出后才持久发布 `stopped`。
@@ -128,7 +130,7 @@ DSH 绑定必须提供同一个 `single` domain 的一次完整镜像 publish
128
130
 
129
131
  ## 应用库配置(rc.26)
130
132
 
131
- `library.sources` **未配置**(undefined)→ 默认 `[{manifestUrl:"https://market.hanamesh.com/catalog-source.json",enabled:true}]`;显式 `[]` → 无来源(`CATALOG_SOURCE_REQUIRED`,由用户在「应用库来源」里添加)。默认只在存储为空的首启播种;已保存的来源永不被默认覆盖。
133
+ `library.sources` **未配置**(undefined)→ 默认 `[{manifestUrl:"https://market.hanamesh.com/catalog-source.json?view=hanamesh",enabled:true}]`;显式 `[]` → 无来源(`CATALOG_SOURCE_REQUIRED`,由用户在「应用库来源」里添加)。默认只在初始 revision=0、来源为空的首启播种;已保存的旧来源、自定义来源或手动清空的列表保持原样,不自动迁移。
132
134
 
133
135
  `library.profileDir` / `profileName` / `dshBin` 与 `nodeBinary`:显式配置永远优先。缺省时按下列规则推断,推不出即留空(安装不可用 `LIBRARY_INSTALL_UNAVAILABLE`,浏览与已安装扫描仍照常):
134
136
 
@@ -174,17 +176,73 @@ Market seat 的「打开」保留原 receipt/heartbeat/close owner;应用 ifra
174
176
 
175
177
  **壳内重启(rc.32 改)**:`hanamesh://restart` 在壳 iframe 内改走壳已有的入站 postMessage 桥 —— `window.parent.postMessage({type:'hanamesh://restart'}, '*')`,与壳 → iframe 的 `hanamesh://bound-refresh` 同一命名空间;壳按 `event.source`/`event.origin` 校验来源后调用自己的重启。原因:WKWebView 不会把 iframe 内发起的自定义 scheme 导航交给系统,`location.assign('hanamesh://restart')` 在壳内不可达。`hanamesh://restart` 这个 URL 仍是系统级入口(外部浏览器 / `open hanamesh://restart`),由壳 `deep_link.rs` 的 `restart` 分支处理。官方 DSH 直开浏览器时仍只提示「请手动重启 DSH」。
176
178
 
177
- ## 应用使用证据(rc.27,用户 2026-09-21 定,STATUS `P2-USE-EVENTS`)
179
+ ## 应用使用证据(D-221,C-USAGE-LOCAL 第1版)
178
180
 
179
- 应用的 `open` / `use` 使用证据由 **app-host** 经 usage 插件的 record 座位上报;app-host 自己不存事件、不签名、不上传。三插件互不 import:`ctx.inject(['hanameshUsage'], …)` 以**可选**服务取得 `ctx.hanameshUsage`,只鸭子类型地要求 `record(input): Promise<{disposition:'recorded'|'duplicate'|'withheld'|'rejected', eventId?, code?}>`。座位缺席、形状不符、抛错或返回 `rejected`,一律**只打 debug 日志**,不抛、不阻止应用启动或网关转发;`withheld`(用户未同意)与 `duplicate`(重复)是正常结果,同样只 debug。
181
+ `instance.ready`、成功授权/HTTP资源转发、WebSocket升级、小时桶关闭、应用停止与旧receipt恢复都不能证明实际界面呈现或成功业务操作。AppHost目前没有连接已核实的普通应用公开原事实源,因此**不自动调用 Usage record**,不以接收端时钟、运行时就绪时间或流量时间制造事实。
182
+
183
+ `createUsageEvidence({host,seat,receipts?,logger?,clock?,sweepIntervalMs?})` 保留既有生命周期接口:
184
+ - 注册时通过现有logger给出有界 `USAGE_FACT_UNAVAILABLE` 诊断;不记录应用内容、身份、token、时间或服务响应,logger失败不影响应用。
185
+ - `host.onActivity` 仅累计独立Router诊断计数;终止事件清除该实例的路由引用。原401/403过滤、路由校验、计数/去抖持久化与保留规则不变。
186
+ - `flush` / `settle` 仅等待本地诊断账本持久化,不代表Usage受理、处理或提交确认。`flush`旧筛选参数仍可传,但不会筛选生成业务事件;`sweepIntervalMs`仍校验并接受,但不启动小时上报timer。
187
+ - 启动、flush、close及恢复旧行不发送record、不自动重试、不自动置reported。遗留 `reported` 位/显式 `markReported()` 操作是账本兼容状态,不能证明业务成功或Usage确认。
188
+
189
+ 可选Usage席位仍经公开Cordis注入,不互相import。只有席位存在或返回recorded也不能使无源事实合法;缺席、不兼容、拒绝均不妨碍应用启动/转发。当前适配器不会向该席位提交运行时或流量观察。
190
+
191
+ `packageName`、`host.bindPackageName()`、`host.packageName()` 与 `list().apps[].packageName` 保持正常登记/安装读回归属能力,包名字符串或item/appId相似不证明完整映射。未来消费合法源必须先核正常安装的item/package/app/Hana关系及公开呈现/成功源与原UTC时间,按正式C-USAGE-LOCAL原键和可选归因调用record,并通过health/observeCollection区分重复、冲突、拒绝、pending/confirmed;不在AppHost复制提供方去重/签名或把pending=0当完成。当前缺面保持未知并交PM,不新造应用event/SDK/信任桥。
192
+
193
+ 本轮SOURCE/FIXTURE负例证明停止伪事实;真实普通应用的两次新结果、合法原事实及真实Usage正向结果仍须独立产品取证。
194
+
195
+ ## D-202 fixed Desktop extension
196
+
197
+ The plugin recognizes the exact upstream Desktop host entry and proves its profile link and runtime manifests before inferring paths. Explicit configuration wins; arbitrary cwd, ports or package names do not identify a Desktop. The declared standalone Node is inspected as Node, never substituted with Electron. Installation goes through the upstream public `runDesktopCli(runtimeDir, supportDir)` export with the reserved `plugin --profile desktop` arguments preserved; no upstream source is patched.
198
+
199
+ For the exact privileged workspace, the existing authenticated control transport gains `POST /apps/bootstrap-authorize` with `{viewId, leaseToken, gatewayOrigin, challenge}`, returning `{authorization}` only after complete ownership/live-generation checks. A challenge is loaded and exchanged once, valid only for its owning exact origin/view. Failures include `UNAUTHENTICATED`, `LEASE_NOT_OWNED`, `LEASE_EXPIRED`, `GATEWAY_CHALLENGE_DENIED`. The controlled page and workspace perform exact-origin and Window/source proof. AppHost returns a control bootstrap URL in the ordinary open/resume receipt, then the page naturally navigates to the bound data origin; applications receive no injected SDK or authorization.
200
+
201
+ Current C-MARKET edition 1 routes/readback and the hidden-by-default verification page remain intact. The direct C-WEB-CATALOG HanaMesh query is validated before all network access, including manifest discovery; the public market listing retains its own boundaries. Formal provider documents remain authoritative. No new Usage fields or observation calls are inferred from this implementation.
202
+
203
+ ## D-230 activation cleanup and ordinary-app admission
204
+
205
+ Every acquired route, optional-service subscription, resolver/observer, domain, ledger and host is paired immediately with a once-only release. Failed activation attempts all acquired releases in reverse order and rethrows the original activation error. A failed release is reported with the fixed APPHOST_CLEANUP_FAILED diagnostic and its internal resource label; raw errors and credentials are excluded. Success uses the same releases for Cordis unload, without stopping another plugin or signalling attached applications.
206
+
207
+ Normal installation, registration and application readback remain separate: the application installer reads the declared hanamesh.app member or falls back to app.json after exact profile/module version readback; scanInstalled requires a truthy hanamesh.app declaration. Public config.applications can register a trusted definition but does not create an installed-application row or prove item/package/app/Hana ownership. An npm main/bin alone supplies neither. Explicit definition.packageName wins over the installed scan binding; this is a lookup rule, not proof of an installation or original business fact. No admission/Usage bridge is added.
208
+
209
+
210
+ ## Plugin-managed standalone Node (2026-10-21 candidate)
211
+
212
+ Only AppHost owns `hanamesh_node_runtime` (single, v1): `{schema:1, revision, settings, active}`. Activation and settings saves never download. Saved settings override explicit plugin defaults; an existing proven/configured nodeBinary remains external until the person selects managed. A missing or invalid external binary fails visibly; it never silently switches to Electron, PATH or download.
213
+
214
+ Authenticated same-origin routes (normal workspace CSRF and connection cookie):
215
+ - GET `/hanamesh/library/node-runtime`: settings, names, supported platform, current verified binding and preparation progress/failure.
216
+ - POST `/hanamesh/library/node-runtime/settings`: exactly `{settings:{mode,version,sourceBase,storageRoot,externalBinary}}`. All five named settings are visible/editable in the plugin settings section. Source is credential/query-free HTTPS; version is one of the pinned, signed releases; storageRoot is an absolute dedicated path with no symlink components. Saving never acquires an asset.
217
+ - POST `/hanamesh/library/node-runtime/plan`: `{}`; reads platform/disk and returns exact source/version/archive bytes/extracted file bytes/space/root/SHA/license and opaque planId. No network or target directory creation. Latest plan wins; saves invalidate it.
218
+ - POST `/hanamesh/library/node-runtime/prepare`: exactly `{planId,confirm:true}`; accepts only the current unchanged plan. Starts one asynchronous operation with an operationId. Public lib-provision receives one confirmed source, immutable asset SHA, AbortSignal and visible progress; no automatic source switch or retry.
219
+ - POST `/hanamesh/library/node-runtime/cancel`: `{operationId}`; cancels only the active preparation, not accepted business operations. No new binding until file, ledger and independent version/platform/Electron checks complete, then the complete active record is persisted before ready.
220
+
221
+ Public install-target remains read-only and unchanged. Preparation never weakens authentication/CORS or guardian admission. Subsequent AppHost spawns, library CLI supervisors and installation readers capture the new standalone binding without restarting DSH. In-flight operations retain their delegate; settings/preparation reject while library/host operations are in use.
222
+
223
+ Support implementation: macOS13.5+ arm64/x64, known Node24.13.1 or24.22.0 (default24.22.0). Windows is explicitly unsupported: POSIX-owned process supervision, cancellation and reader chain need separate implementation and real-host proof. No Windows ready claim. mac x64 native proof is tracked separately from its signed archive integrity. No client or upstream files change. No npm lifecycle download.
224
+
225
+ Storage space budget is archive bytes + twice measured regular-file payload (new tree + staging); never delete another version or unknown files to make space. lib-provision alone owns staging/ledger/manifests; interrupted or cancelled downloads may leave its resumable owned cache. Another attempt requires a new visible confirmation. Existing versions remain until an explicit owned cleanup is designed; no automatic deletion or implied rollback. Offline startup verifies an existing active tree/ledger and standalone executable; failures are visible and never trigger a download.
226
+
227
+
228
+ 目录来源 URL 接受标准端口 HTTPS,另接受本机回环 `127.0.0.1`、`::1`、`localhost` 的任意端口 HTTP。使用 Node `URL` 解析后的主机名精确比较;其它来源保持原规则。凭据、fragment、查询和完整同源校验不变;不增加设置、映射或别名。目录来源自身 attribution 可使用该回环来源,普通条目展示链接仍按原 HTTPS 校验。
229
+
230
+ ## B17 套件安装包来源(2026-10-21 候选)
231
+
232
+ 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。
233
+
234
+ 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`,旧选择和其他字段保持;关闭等待已排队写入。
235
+
236
+ GET 的 `observation` 为 `{observable:false,code}` 或 `{observable:true,state}`。后者仅转发公开 Core `hanameshSuiteSource` protocol `'1'` 同步 `getState()` 的六个字段:`protocolVersion/status/selection/registry/archiveCount/lastError`。status 为 `disabled|ready|failed|disposed`,selection 为 `configured|custom|disabled`。服务缺失(含尚未 provide)为 `CORE_SUITE_SOURCE_MISSING`,不杜撰 starting/ready;旧协议、读取异常或无效 DTO 为 `CORE_SUITE_SOURCE_INVALID`。GET 仅观察,保存不触发安装或授权。
237
+
238
+ 明确选择 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,不能声称观察到两次读取间完全消失又恢复原状的事件。
239
+
240
+ 最后执行仍调用现有官方 CLI `add --save-exact --strict-peer-dependencies --registry=<声明原址>`,精确三组件;正常市场外部包路径不变。正在执行时源连接丢失由实际 CLI/输运终态报告,不靠重试/退出屏障维持源。Core B17 服务只来自候选 `c5e1a7373b9896c385f14f79a64bb277f43bde5c` 的公开能力;无 Core 运行时 import,无 Desktop hook/bootstrap、三锁或先于插件加载的服务。源码/真实 Cordis+JSON domain(AUTH_DOUBLE)与正常包验证不等同真实原生安装或 owner 验收。
241
+
242
+ 公开类型位于 `@hanamesh/dsh-app-host/dsh` 的 `SuiteSourceMode/CoreSuiteSourceState/SuiteSourceSettings/SuiteSourceSettingsInput`;页面导出 `SuiteSourceSettingsSection`。回归:`suite-source.test.mjs`、`handoff-integration.test.mjs` B17-CORDIS,变异 M84–M89(M61 检查全部后续读取被错误缓存)。
180
243
 
181
- | 事件 | 触发点 | `idempotencyKey` | `occurredAt` |
182
- |---|---|---|---|
183
- | `open` | 实例到达 `ready`(视图真被服务)——每次成功启动一次;同一实例的第二个 view 不重复 | `open:<appId>:<instanceId>` | 就绪时刻 |
184
- | `use` | 该实例的网关**真的转发**了一个已授权请求且应用回应不是 401/403(含成功的 WebSocket 升级);每应用每 UTC 小时最多一次 | `use:<appId>:<YYYYMMDDHH>` | 该小时桶内第一个被转发请求的时刻 |
185
244
 
186
- 不计入 `use` 的:引导票据 `/__hanamesh_bootstrap/*`、网关拒绝(403 `GATEWAY_*`)、应用自己的 401/403 回应、宿主的就绪探针(探针直连上游,不经网关)、`embedding:'direct'` 部署(没有网关,没有活动信号)。每实例只记一个「上次小时桶」,实例 `stopped`/`failed`/`interrupted` 即丢弃,map 以活实例数为界。
245
+ B17 review repair(同0.6.8候选):`AppHost` 未注入 `runtimeLedgerReader` 时,默认经正常依赖 `@hanamesh/lib-provision` 的公开 `ledger(root)` 读取 `<dataRoot>/runtimes/<appId>/.provision/ledger.json`。不存在的历史 `./provision/index.js` 导入已撤除;不复制账本实现或回退其它根。匹配版本与可执行检查仍 fail-closed。`AH-P05` 与正常tgz消费者均不注入 reader,使用有效真实账本启动实际 runtime、读回就绪响应与应用数据;M90检验错误导入不能回归。这仍不等于真实桌面应用的 owner 验收。
187
246
 
188
- `hanaRef` = 应用的 **npm 包名**(与 usage `validHana` 同形:`@scope/name` 或 `name`,≤128 字符),`sourcePlugin` = `@hanamesh/dsh-app-host`。包名来源按序:① 描述符可选字段 `packageName`(rc.27 新增,`validateDefinition` 校验形状;不带则完全向后兼容);② 宿主启动时的已安装扫描(`<profile>/node_modules/<pkg>/app.json` → `id`)经 `host.bindPackageName(appId, packageName)` 绑定——应用包只注册纯 `app.json`,扫描先于应用包注册发生,所以绑定不要求 appId 已注册;③ 都没有 → 该应用不产生证据(debug 日志),启动照常。`list().apps[].packageName` 带出当前解析结果(无则 `null`)。
189
247
 
190
- 新的宿主面:`host.bindPackageName(appId,name)`、`host.packageName(appId)`、`host.onActivity(listener)`(内存内活动通知 `{type:'instance.activity',instanceId,appId,deploymentId,principalId,at}`,不落 `eventsSince`);`FixedGateway({ onForward })`;`createUsageEvidence({ host, seat:()=>ctx.hanameshUsage, logger })` 是把二者接到 record 座位的适配器(`dsh.js` 已装好;`close()` 随插件卸载)。
248
+ 0.6.12:目录条目的展示/跳转链接(`sourceLinks.sourceUrl`、`homepage`、`repository.url`、`publisher.url`)接受 fragment 并原样保留;目录自身 attribution 展示链接亦保留 fragment。manifest 来源和 `transport.endpoint` 等请求 URL 校验不变。单条非法仍拒绝整个 provider page,不返回部分结果。
@@ -0,0 +1,94 @@
1
+ # Official DSH compatibility · 0.6.2 candidate
2
+
3
+ The package admits the public host ranges in `package.json`; exact development dependencies remain reproducible test
4
+ inputs. The source audit compared the installed official `/Applications/DeepSeek Harness.app` `0.2.0-rc.2` against
5
+ upstream tag `dsh-v0.2.1-alpha.2`. The installed DSH packages are inside `Contents/Resources/app.asar/dsh/node_modules`,
6
+ while the physical `Contents/Resources/runtime` contains the public command launcher and dependency payload.
7
+
8
+ ## Consumed public interfaces
9
+
10
+ | AppHost consumer | Public interface | rc.2 result |
11
+ | --- | --- | --- |
12
+ | `src/dsh.js`, domain declarations | Schemastery object/string/number/boolean/array/union/const/any/default; storage-domain `defineDomain` | Present; consumed signatures unchanged |
13
+ | `src/dsh.js` | Cordis `get`, `provide`, `effect`, `inject`, returned fiber `dispose`, logger info/warn/debug | Present; consumed signatures unchanged |
14
+ | `src/dsh.js`, store/router/library | `storageDomain.open(singleSpec)`, domain `global.get`, `global.set`, `close` | Present; whole-value publication unchanged |
15
+ | `src/dsh.js` | `webServer.port`, `register({kind:'exact',path,handler})` and disposer | Present |
16
+ | `src/dsh.js` | `connection.requestRejection(request)` → undefined / 401 / 403 | Present; AppHost delegates authentication to the host |
17
+ | `src/dsh.js`, `src/router/sources.js` | Optional credentials `describe`, `resolve`, `describeRecord`; llm `listProviders`, `listModels` | Present; consumed signatures unchanged |
18
+ | `src/library/install.js` | Optional `pluginManager.installBundle`, `removeBundle`, application outcome | Methods present; **rc.2 lacks install option `saveExact`** |
19
+ | `src/library/desktop-cli.js` | `runDesktopCli(runtimeDir,supportDir)` | Public in both versions; standalone Node cannot import ASAR |
20
+ | `src/client-ui.js` | `window.__ModuleLoader__.load({id,factory})`, React 18, Cordis effects/market seat | Present |
21
+ | `src/client-ui.js` | slots `inject`, `register`, `settings.section`, `sidebar.footer.action`, `shell.overlay` | Present; consumed registry and slot contracts unchanged |
22
+
23
+ The package's settings/locale client dependencies provide assembly ordering; AppHost calls no direct settings/locale
24
+ methods. Optional HanaMesh service seats and the legacy coding gateway endpoint are separate contracts, not official
25
+ DSH APIs. No other HanaMesh private module implementation was consulted for this audit.
26
+
27
+ Installed ASAR source evidence (paths below `dsh/node_modules/@deepseek-ai`): storage-domain `lib/index.js:61`,
28
+ `:157–172`, `:355–389`; host-webserver `lib/index.js:164–183`; client-connection `lib/index.js:586–588`;
29
+ cordis `lib/index.js:763`, `:800`, `:1169`, `:1600`; plugin-manager `lib/index.js:1749–1753`;
30
+ desktop-host `lib/cli.js:91–104`. The rc.2 public `InstallBundleOptions` is in upstream
31
+ `packages/boot/plugin-manager/src/types.ts:140–149`; alpha.2 adds `saveExact` at `:158` and forwards `--save-exact`.
32
+ Internal connection HTTPS/bind-address handling changed upstream; the local HTTP gate consumed here remains public.
33
+
34
+ ## Installation behavior
35
+
36
+ AppHost reads public package metadata resolved from the proven Desktop runtime, or the CLI/profile package anchor.
37
+ It never infers capability from the presence of a method alone or from function source text. Unknown versions do not
38
+ enable live exact installation. Compatible manager versions at least `0.2.1-alpha.2` support `saveExact`,
39
+ but neither consumed manager exposes a scoped child environment/credential monitor. Since 0.6.5 all package mutations
40
+ therefore use the public CLI under the AppHost Git supervisor, including alpha hosts. The host environment is not mutated.
41
+
42
+ For a proven packaged macOS ASAR layout, AppHost uses the executable public `runtime/cli/bin/dsh` launcher unchanged.
43
+ That launcher supports the reserved `desktop` profile and owns Electron/pnpm startup. `DSH_HOME` is derived from the
44
+ same proven `profiles/desktop`; argument construction uses an array, not a shell command. The exact dependency and
45
+ installed package version are still both read back. A CLI install reports `restart-required`; it never claims live apply.
46
+ Prepared non-ASAR layouts retain the existing public `runDesktopCli` route. Windows' `.cmd` carrier and Linux packaged
47
+ Desktop launcher have no new validation here; absent supported carriers remain unavailable rather than being guessed.
48
+
49
+ Without a usable public CLI, the probe returns `ready:false` with readable `need`;
50
+ installation refuses before touching the profile. Browsing, existing installed rows and authenticated view APIs remain.
51
+
52
+ ## Why the pins regressed
53
+
54
+ `7fc127c` introduced public host peer ranges. `b992d37` (2026-10-05) replaced the DSH ranges with exact rc.2 peers;
55
+ `b299d1e` made Cordis/Schemastery exact as well. `a00c80e` (2026-10-11) moved those exact peers to alpha.2 alongside
56
+ the fixed development runtime. Public admission had become coupled to development supply, contrary to
57
+ `laws/environment-matrix.md`. Version 0.6.2 restores that separation without modifying upstream.
58
+
59
+ ## Evidence limits
60
+
61
+ Source/fixture tests exercise version selection, pre-mutation refusal, exact readback, public launcher arguments,
62
+ isolated profile selection, restart-required and alpha live results. They are not an actual rc.2 manager or Desktop.
63
+ Real isolated official Desktop load, its browser settings/market/view flow, actual CLI installation, and package/runtime
64
+ checks need separate receipts. Other operating-system matrix cells and owner acceptance remain independent gates.
65
+
66
+ The pnpm 11 distribution consumer supplies the coherent official rc.2 host package versions from
67
+ `tests/fixtures/official-rc2-host.json` and keeps strict peer checking. A bare auto-peer experiment failed: pnpm selected
68
+ Cordis `4.0.4` alongside newly available DSH `0.2.1-alpha.3` packages that require `~4.0.5-alpha.2`. Its failure and
69
+ `pnpm peers check` output are retained in the session run's `apphost-full-test-2.log` and
70
+ `apphost-package-regression.log`. This candidate does not claim a bare package can automatically assemble an arbitrary
71
+ new DSH host. Official Desktop supplies its own coherent runtime and disables automatic peer installation.
72
+
73
+ ## I27 官方 rc.2:独立 Node 的公开配置
74
+
75
+ 官方 macOS rc.2 的 Electron-as-Node 包装脚本不等于本模块的独立 Node 监督运行时。现 guardian 明确拒绝 `process.versions.electron`,受控子进程环境也不继承宿主 Electron 启动变量;不移除该拒绝、不猜应用路径或增加客户端 hook。
76
+
77
+ 有独立 Node.js 24(24.13.1或更新24.x)时,在官方 profile 的公开插件用户配置层设置 AppHost **顶层** `nodeBinary` 为其实际绝对可执行路径;不是 library.nodeBinary,也不从PATH自动猜测。用户填写自己的真实安装路径,完全退出再重开。在网站安装交接设置和公开 install-target readback 看 ready/缺件;ready=true只代表安装前置具备,不是已安装或完整网站闭环。
78
+
79
+ ```yaml
80
+ - id: hanamesh-app-host
81
+ config:
82
+ nodeBinary: <独立Node的实际绝对路径>
83
+ ```
84
+
85
+ 这是配置示意,尖括号不是可执行路径。保留原有其他AppHost配置;不改客户端、原生品牌、其他插件设置或凭据。当前支持范围仅独立Node,未声明官方Electron-as-Node安全监督兼容。
86
+
87
+
88
+ ## R1 profile peers check (2026-10-21 candidate)
89
+
90
+ Official rc.2 supplies `ctx.slots` through `@deepseek-ai/dsh-client-ui-renderer`; the removed `dsh-client-ui-slots` peer/inject name does not exist in the official 0.2.1-rc.2 package tree. This candidate declares the actual public renderer instead, preserving the existing supported version range and exact alpha.2 development input.
91
+
92
+ The official pnpm 11.28.5 `peers check` reads the profile lock graph, which excludes installation-owned Host/Renderer supplies under the default `autoInstallPeers:false` policy. Missing lock edges are distinct from runtime injection. Its bundled range intersection also reports AppHost/Brand locale ranges as conflicting although both admit the actual 0.2.1-rc.2. The original static check remains RED, not waived; neither profile policy nor package presence is faked. Brand's finite supported ranges remain unchanged.
93
+
94
+ All client copy is registrant-owned through `ctx.locale.register/bind/subscribe`; settings labels are localized strings re-registered on change, as specified by the public settings slot contract. Styles remain within AppHost classes. The host settings column may still be clipped at390; no host selectors or client patches are used to conceal that restriction. Original provider errors retain their code/message/details under collapsed diagnostics, separate from localized explanations.
@@ -1,3 +1,37 @@
1
+ ## 0.6.2 compatibility candidate (2026-10-15)
2
+
3
+ Public host peers use the ranges recorded in `package.json`; development package versions remain exact. The installed
4
+ official Desktop rc.2 public API and alpha.2 tag were compared; details and remaining product gates are in
5
+ `DSH_COMPATIBILITY.md`. Older exact-target descriptions below are historical inputs, not current host restrictions.
6
+
7
+ The rc.2 manager cannot save an exact version through its public `installBundle` options. For the verified packaged
8
+ macOS layout, AppHost invokes the installation's executable `runtime/cli/bin/dsh` directly with ordinary
9
+ `plugin --profile desktop add --save-exact ...` arguments. The launcher owns its Electron boot details and its bundled
10
+ pnpm selection; AppHost neither extracts ASAR nor writes a replacement launcher. The inferred `DSH_HOME` selects the
11
+ same proven `profiles/desktop`; only HOME, that DSH_HOME, LANG and the launcher/system PATH enter the child environment.
12
+ The result remains `restart-required`. A verified alpha.2-or-later compatible manager retains live installation.
13
+ Missing/unproven manager capability plus missing CLI produces `ready:false` and readable `need` before mutation.
14
+
15
+ ## 0.6.1 (2026-10-15)
16
+
17
+ Review fixes. The install-target probe on the handoff listener answers only origins in `library.handoff.allowedOrigins`
18
+ (others: 403, no CORS grant, no body content); requests without an Origin are still answered. New setting
19
+ `library.handoff.pollMs` (default 1500). Removed hidden caps (port count, origin count, ttl range, preflight max-age,
20
+ plutil timeout); the remaining fixed rules are served as `rules` by `/hanamesh/library/handoff-settings` and listed on
21
+ the settings page. Expiry codes: `HANDOFF_NOT_PICKED_UP` (window never fetched it / fetched but no outcome) and
22
+ `HANDOFF_EXPIRED` (shown or confirmed, no answer); the client reports `failed` with `error.code` when the confirmation
23
+ cannot open (`HANDOFF_OPEN_FAILED`, `cause`). Live install results carry `restartBaseline`. `hanamesh-brand` is protected.
24
+
25
+ ## 0.6.0 (2026-10-15)
26
+
27
+ Website handoff listener (library/handoff.js), plugin installs through the host's public `pluginManager` (applied without restart), real `client` identity from the containing app bundle id, and readable `need` when the library cannot install. See C-MARKET 2026-10-15 addition.
28
+
29
+ ## Current D-230 candidate (0.5.9)
30
+
31
+ The current target is the fixed upstream d743267388641bc76f17c45ce8b4c231aed1d32c and official DSH 0.2.1-alpha.2, with unchanged exact host peers. Older target/version notes below are historical. Normal plugin installation consumes this package root via profile/cordis.patch.yml; upstream client discovery then loads the ./client browser condition. The separately produced 0.5.9-verification.0 client factory calls apply(ctx,{verification:true}); production calls apply(ctx) and defaults off. Both use the existing market/sidebar overlay and 11 editable contract calls, with normal installation confirmation. Config has no verification switch.
32
+
33
+ For isolated Desktop validation, supply an owned absolute DSH_HOME before activation. AppHost proves the actual host argv/profile link and primary-runtime Node before deriving paths; it does not require a hand-written global overlay or infer a candidate app from an npm bin. Exact mode inputs and artifact hashes are supplied in this card run directory. Native startup remains subject to the independently approved Desktop logging/write-isolation route. Failed activation now removes earlier routes/listeners/domains before rejecting; successful unload retains normal owned-process termination semantics.
34
+
1
35
  # DSH 集成
2
36
 
3
37
  > **NPM-APPHOST-01(2026-10-05,当前候选):** 目标为官方 `@deepseek-ai/dsh@0.2.0-rc.2`;本地 tarball 在隔离官方 profile 的 `dsh.profile.bundles` 中落座,`--dump-config` 可见 `hanamesh-app-host`。以下 rc.11 及更早说明保留为历史,不作为本候选的公开 npm 门。owner D-2026-10-05-02 已确认内联 `lib-provision` 为自有且可分发;独立许可分区见根 `LICENSE`,PUBLIC_NPM 由 PM 执行。浏览器可见性证据以 BlueMap 当前卡 REPORT 为准。
@@ -41,3 +75,10 @@
41
75
  peer 范围保留输入要求:`@deepseek-ai/cordis ^4.0.2` 与 `@deepseek-ai/schemastery ^3.18.2`。目前标 optional 仅为了核心独立安装不从网络拉未验证组件;不表示 DSH 集成可以不用它们。
42
76
 
43
77
  运行 `npm run preflight:dsh` 会输出当前缺失项并以代码2退出,这个预检是阻塞说明,不是安装器,也不是自动联网修复。
78
+
79
+
80
+ ## Plugin-local installation environment
81
+
82
+ In Settings → 安装环境, save the named source/version/storage settings if desired, then click 准备安装环境. Review the platform, exact version, URL, archive/extracted sizes, disk budget and license. Only 确认下载并准备 begins acquisition. Progress exposes download, SHA verification, extraction, independent Node check and promotion; 取消准备 aborts that one operation. Refresh failure is visible and does not silently retry. Successful preparation automatically rebinds later plugin operations; no DSH restart or manually entered path is needed. Existing configured Node is kept as an explicit advanced external mode.
83
+
84
+ New runtime acquisition is executed by the already-running plugin host using public lib-provision APIs; it does not require the missing standalone Node to bootstrap, and does not use Electron as a guardian. All native evidence must use an unchanged official rc2 app plus isolated HOME/DSH_HOME/user-data-dir. Source/component tests and signed assets alone do not establish real-host or x64 native acceptance.
@@ -0,0 +1,96 @@
1
+ # Public installation status bridge · contract 1
2
+
3
+ AppHost 0.6.7 supplies metadata discovery and a separate, explicitly consented read. Discovery never launches a reader. This is an installation observation, not permission to install/upgrade, a Usage consent change, compatibility calculation, or proof of another client.
4
+
5
+ ## Discovery and request
6
+
7
+ Both the existing website handoff listener and the DSH local library listener add optional `installationStatus` to `GET /hanamesh/library/install-target`:
8
+
9
+ ```json
10
+ {"contract":"hanamesh.installation-status","contractVersion":"1","path":"/hanamesh/library/installation-status"}
11
+ ```
12
+
13
+ The field is advertised independently of `ready` (which describes controlled installation). Consumers must use the path on that same responder only, and only when the capability/contract is present. Old providers have no capability; do not probe a guessed path.
14
+
15
+ Only after the person's explicit “检测本机安装状态” click, send `POST` to that path with `credentials:'omit'`, `Content-Type: application/json`, and exactly:
16
+
17
+ ```json
18
+ {"contractVersion":"1","consent":"read-installations"}
19
+ ```
20
+
21
+ The owning listener enforces its existing exact loopback Host. Origin is required: handoff allows only `library.handoff.allowedOrigins`; the DSH library additionally permits its actual workspace/transport origins. Null/unlisted/missing Origin is rejected, with no CORS grant for the rejected origin. Cookie or Authorization, query fields, iframe control, absolute/protocol-relative/backslash targets, wrong method/content type, invalid JSON, extra fields, unsupported contract or missing exact consent are rejected **before** any directory/CLI/Core read. The body limit is the existing `HANDOFF_BODY_MAX_BYTES` (4096), not a new configurable default. OPTIONS returns 204 with exact approved Origin, POST/content-type and Private Network Access; it performs no read. No credentials or persistent consent are accepted.
22
+
23
+ Responses use JSON, `Cache-Control:no-store`, nosniff and frame-denying CSP. After the listener's preliminary Host/target/frame checks and successful allowed-Origin admission, subsequent named refusals carry CORS; preliminary safety refusals can omit CORS. Errors use `{error:{code,message,details?},traceId}`; messages never include raw reader output, paths or exceptions.
24
+
25
+ ## Response
26
+
27
+ Public types and capability are exported at `@hanamesh/dsh-app-host/installation-status`.
28
+
29
+ ```ts
30
+ interface InstallationStatus {
31
+ schemaVersion:1;
32
+ client:'hanamesh'|'dsh';
33
+ checkedAt:string; // current read completion, ISO UTC; not an atomic whole-machine snapshot
34
+ observations:readonly InstallationObservation[];
35
+ traceId?:string;
36
+ }
37
+ interface InstallationObservation {
38
+ channel:'hanamesh'|'dsh'|'claude'|'codex';
39
+ scope:{id:'current-runtime'|'other-runtime'|'agent-cli'|'desktop-app';
40
+ kind:'responding-runtime'|'unobserved-runtime'|'official-user-profile'|'desktop-app';label:string};
41
+ status:'installed'|'not-installed'|'incomplete'|'unknown';
42
+ version:string|null;
43
+ versionKind:'core'|'agent-distribution';
44
+ components:readonly {name:string;version:string|null;active?:boolean;status?:string}[];
45
+ identity:{pluginName:string;marketplace:string}|null;
46
+ reason:{code:string;message:string;action:string}|null;
47
+ blocked?:readonly {code:string;moduleName?:string}[];
48
+ }
49
+ ```
50
+
51
+ The five distinct observations are: responding runtime, the other DSH/HanaMesh runtime (always `unknown`, not observed by this responder), Claude Code configured user-level CLI scope, Codex CLI configured local-marketplace user scope, and Codex/ChatGPT desktop App scope (always named `unknown`). CLI labels explicitly exclude project/other isolated profiles and desktop App. A valid absence is absence **only in the named configured scope**; never show it as a machine-wide uninstall. Preserve every responder/scope; ports are not durable instance identities. Conflicts between responders are unknown, never resolved by choosing one installed result.
52
+
53
+ Current runtime status/version/components come only from the optional public Core protocol `'1'` `getSuiteDeclaration()` **without an argument**, through the existing Core adapter. No registry request, health substitution, physical file-count completeness, version-range/peer/source algorithm or local upgrade selection is added. Complete maps to installed; incomplete stays incomplete; unknown stays unknown with top-level version null but known component versions and sanitized named blocked codes retained. Missing method, malformed declaration and read failure have distinct reasons. Each detection re-reads; Core removal/invalidation cannot reuse an old installed answer. Agent distribution version is independent of Core version. Missing active fields remain absent.
54
+
55
+ ## Explicit agent reader settings
56
+
57
+ `DshPluginConfig.library.installationReaders` may configure `claude` and/or `codex`. Each reader requires all these fields; **there are no inferred/default values**:
58
+
59
+ | Field | Required value |
60
+ |---|---|
61
+ | executable | Absolute path to the person's official CLI executable |
62
+ | home | Absolute explicitly selected user's home directory |
63
+ | configDir | Absolute existing official config directory for that selected profile (`CLAUDE_CONFIG_DIR` or `CODEX_HOME`) |
64
+ | pluginName / marketplace | Exact registered canonical AgentPluginDTO identity; not a guessed hanamesh@hanamesh |
65
+ | marketplaceUrl | Exact registered `installation.marketplaceUrl`, canonical credential-free HTTPS, no query/fragment |
66
+ | timeoutMs / maxOutputBytes | Required positive safe-integer read budgets; timeout at most Node's 2147483647 limit |
67
+
68
+ No website body can set these fields, executable arguments, environment or filesystem paths. No PATH/profile discovery or direct reading of private authentication/chat/config inventories is implemented. An absent/invalid reader is an actionable unknown, not “not installed”. Missing directories are not created. Windows readers are explicitly unsupported in this implementation; runtime Core reads still work. Electron hosts need the existing verified standalone `nodeBinary` for the process supervisor; absent Node is named unknown, not an Electron workaround.
69
+
70
+ Exact verified protocols (other CLI versions return `AGENT_READER_VERSION_UNSUPPORTED`):
71
+
72
+ | Channel | Verified `--version` result | Fixed subsequent read argv |
73
+ |---|---|---|
74
+ | Claude Code | `2.1.301 (Claude Code)` | `plugin list --json`; `plugin marketplace list --json` |
75
+ | Codex CLI | `codex-cli 0.162.1` | `plugin list --json --disable remote_plugin`; `plugin marketplace list --json --disable remote_plugin` |
76
+
77
+ The Codex flag was checked through this exact CLI's public `features list`; it excludes remote directory reads. A version match is necessary but not sufficient: both JSON shapes must validate. Claude uses the official installed array's `id`, user `scope`, enabled/version and load diagnostics. Other project-only target rows are unknown for this reader. Codex uses the official `installed` array and its qualified identity, installed/enabled/version. The exact target marketplace must match the registered HTTPS source in marketplace list; Codex's per-install `marketplaceSource` must also be present, supported and consistent. A local/different/unreadable source, duplicate/conflicting target or malformed/truncated/empty raw output is unknown. Only a complete valid clean inventory with no target in the configured scope is not-installed. A known install may have version null; consumers must show version unknown. Official load/payload issues produce incomplete, not successful readiness. No plugin MCP/hook definitions, unrelated inventory or CLI raw diagnostics are returned.
78
+
79
+ Commands run with an empty stdin in a disposable neutral cwd, only named HOME/config/platform paths, no inherited model/API keys, proxy/auth/askpass/Node preload, and disabled Git credential helpers. No model/install/upgrade/marketplace refresh command runs. A detached IPC reaper owns the neutral cwd outside a separate live command-group anchor. The anchor remains alive until output closes; abort, budget exhaustion, overflow, provider disposal or parent IPC disconnect makes the surviving reaper terminate only that live anchor's group. The reaper waits for group close before removing the cwd, so active writers cannot race deletion, including after caller death. No released PID/PGID or arbitrary process name is used. The caller also performs exact-path cleanup when alive. There is no retry or stale-result cache. Automatic recovery after an internal reaper/anchor itself is externally SIGKILLed or suffers a native crash is outside this lifecycle guarantee; an external intervention can leave command processes or temporary files and is never reported as an installed/absent inventory.
80
+
81
+ ## Refusals, unknown reasons, and lifecycle
82
+
83
+ HTTP refusals: `HOST_DENIED`, `ORIGIN_DENIED`, `FRAME_CONTROL_DENIED`, `CREDENTIALS_NOT_ACCEPTED` (403), `METHOD_NOT_ALLOWED` (405), `CONTENT_TYPE_REQUIRED` (415), `BODY_TOO_LARGE` (413), `BAD_TARGET`, `INVALID_JSON`, `UNKNOWN_FIELDS`, `INSTALLATION_STATUS_CONTRACT_UNSUPPORTED` (400), `INSTALLATION_STATUS_CONSENT_REQUIRED` (403), `INSTALLATION_STATUS_UNAVAILABLE` (503). A caller/provider cancellation has `INSTALLATION_STATUS_CANCELLED` (499 when still connected); disconnected clients receive no result.
84
+
85
+ Per-scope unknown reasons always include a safe code/message/action:
86
+
87
+ - `CORE_SUITE_DECLARATION_MISSING`, `CORE_SUITE_DECLARATION_INVALID`, `CORE_SUITE_READ_FAILED`, `CORE_SUITE_DECLARATION_UNKNOWN`; incomplete: `CORE_SUITE_CURRENT_INCOMPLETE`, plus Core blocked codes.
88
+ - `OTHER_RUNTIME_NOT_OBSERVED`, `CODEX_DESKTOP_SCOPE_UNOBSERVABLE`.
89
+ - `AGENT_READER_NOT_CONFIGURED`, `AGENT_READER_CONFIG_INVALID`, `AGENT_READER_VERSION_UNSUPPORTED`, `AGENT_READER_PLATFORM_UNSUPPORTED`, `AGENT_READER_NODE_REQUIRED`.
90
+ - `AGENT_PROFILE_UNAVAILABLE`, `AGENT_READER_UNAVAILABLE`, `AGENT_READER_PERMISSION_DENIED`, `AGENT_READER_TIMEOUT`, `AGENT_READER_OUTPUT_LIMIT`, `AGENT_READER_DIAGNOSTIC`, `AGENT_READER_CLEANUP_FAILED`, `AGENT_READER_FAILED`.
91
+ - `AGENT_INVENTORY_INVALID`, `AGENT_INVENTORY_CONFLICT`, `AGENT_SCOPE_UNOBSERVABLE`, `AGENT_MARKETPLACE_SOURCE_UNAVAILABLE`, `AGENT_MARKETPLACE_SOURCE_MISMATCH`.
92
+ - An installed record with unobserved version uses `AGENT_VERSION_UNAVAILABLE`; incomplete uses `AGENT_PLUGIN_LOAD_FAILED` / `AGENT_PLUGIN_PAYLOAD_MISSING`.
93
+
94
+ Start each explicit detection by clearing previous observations. Cancel fetch when the person clears/revokes/leaves, the consumer/identity changes, or the window unmounts. Discard late request generations. A failed read/capability disappearance never keeps old installed status. Do not poll, persist, forward inventory or change Core/Usage recording consent. G's website consumer owns these page lifecycle rules; the bridge cancels on disconnect and module unload and holds no cross-request observation cache.
95
+
96
+ Official command references: [Claude Code plugin commands](https://code.claude.com/docs/en/plugins/cli-reference), [Codex CLI plugin reference](https://learn.chatgpt.com/docs/cli/reference). [OpenAI local plugin scope documentation](https://developers.openai.com/plugins/build/plugins) describes multiple client scopes; CLI installation is not used here as proof of the desktop App.
@@ -0,0 +1,130 @@
1
+ # 客户端市场安装目标入口
2
+
3
+ 网站发现:DSH 本地路由与交接监听的 `/hanamesh/library/install-target` 都按当前 `library.handoff.allowedOrigins` 回答跨来源 GET / OPTIONS;只回显获准 Origin(Vary: Origin),不使用通配 CORS。未获准来源为 403 ORIGIN_DENIED,正文无客户端能力。正常工作区同源读取、无 Origin 的本机工具读取保留。其余安装/状态路由继续需要各自认证与确认。
4
+
5
+ 轮询设置:`pollMs` 写入应用库真实 storage domain;保存、卸载再加载后保持。旧记录没有该字段时使用既有配置/默认 1500 ms,不重写其它存储数据。
6
+
7
+ AppHost `0.2.0-rc.10`(安装目标入口自 rc7;合约声明与随包套件自 rc8;安装失败恢复结果自 rc10)。目标输入、目录解析、确认、安装与结果全部归本模块;调用方只传目录标识,负责呈现原市场表面。
8
+
9
+ ## Desktop 正常导航入口
10
+
11
+ 在当前已认证的同一 DSH 工作区 URL 上设置片段 `#hanamesh-install?itemId=<encoded-id>&packageName=<encoded-name>`,至少一个字段;不要变更 workspace 的 origin、路径或已有认证参数。AppHost 客户端正常加载或 hashchange 时读取这个公开片段,调用下面同一入口。重复查询字段和额外字段拒绝。无需导入另一个插件、DOM 自动点击、executeJavaScript、私有组件状态或特权 postMessage;壳只做自己的协议校验、前置窗口和正常导航。AppHost 模块需已由产品正常供应加载;旧 rc2 不认识此协议。
12
+
13
+ ## 客户端公开入口
14
+
15
+ 在 DSH 的正常客户端模块系统中取得 `@hanamesh/dsh-app-host` 的公开客户端模块,调用 `openInstallTarget({itemId?, packageName?})`。已有客户端 `market` service 也提供同名方法:`market.openInstallTarget(target)`;原 `market.render({preferredSubsectionId})` 与 `setSettingsVisible()` 保持原协议。
16
+
17
+ 至少一个标识;两者都给时必须匹配同一条目录项。拒绝所有其他字段,包括 version、url、downloadUrl。itemId 最长 160 字符;packageName 按 npm 包名语法校验、最长 214 字符。壳不得解析目录、代填版本/下载地址、点击私有 DOM、注入脚本、代按确认或访问组件状态。
18
+
19
+ 入口先显现 AppHost 自己的侧栏市场覆盖层。若宿主已用 market 席位拥有市场表面(setSettingsVisible(false)),调用方需选中/呈现现有 market.render() 表面,AppHost 不叠加第二个覆盖层。即使尚未挂载,目标确认也保存在本客户端模块内,正常挂载后读回;不跨 profile 保存。
20
+
21
+ Promise 在目录解析后返回 `{status,itemId?,packageName?}`:
22
+
23
+ | status | 意义 |
24
+ | --- | --- |
25
+ | confirmation-required | 市场已按当前目录显示名称、包名、版本和目录来源;尚未安装 |
26
+ | installing | 相同目录项已有安装进行中,复用该状态 |
27
+ | already-installed | 当前 profile 已安装且目录没有升级,不重复安装 |
28
+ | superseded | 解析过程中收到更新目标或取消,旧输入不再打开确认 |
29
+
30
+ 错误时 Promise reject,市场同时显示错误。这个回执不表示安装完成。用户按「确认安装」后,市场自己发起原 install 请求、跟踪原 events、显示结果并刷新「已安装」。取消不会发 install 请求。相同目标以目录 id 归并客户端状态,以实际包名归并宿主进行中的 install operation。安装成功后的可打开状态沿用原 AppHost 应用视图/租约;普通 DSH 插件沿原宿主入口打开,需重启时保留原提示。
31
+
32
+ ## HTTP 契约
33
+
34
+ 新增只读 `GET /hanamesh/library/target?itemId=...&packageName=...`,查询字段仍只接受上述两个标识。宿主用当前市场目录逐页解析,返回 `{item,source,traceId}`;item 含 kind、installed、upgradeAvailable 和原目录元数据,source 含市场自己的 manifestUrl;fixture 来源明确标「测试目录」。查询不会安装。
35
+
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 扫描,并与操作结果版本一致才报告读回成功。
37
+
38
+ ## 合约版本与一致性套件
39
+
40
+ 契约名 `hanamesh.install-target`,当前版本 `1`。正式协议以 C-MARKET 文档为准;随包 schema `schemas/install-target.schema.json`(出口 `@hanamesh/dsh-app-host/install-target/schema.json`)描述既有目标字段,`x-hanamesh-contract` 节记录版本、片段前缀、路由与拒绝规则,不替代正式文档。
41
+
42
+ **变更说明(rc8,小版本,只加不改):** 新增可选字段 `contractVersion`,可出现在 `openInstallTarget` 入参、`#hanamesh-install?` 片段与 `GET /hanamesh/library/target`、`POST /hanamesh/library/install` 的输入中;`GET target` 的 200 结果新增 `contractVersion:'1'`;包 `package.json` 的 `hanamesh.installTarget` 声明提供方实现的版本。itemId/packageName、字段语法、确认、取消、auth、同源与既有拒绝码都不变。影响:Desktop 现有不带版本的安装链接按 v1 照常工作,无需改动;要声明版本时由唯一宿主收尾卡改钉 rc8 后再做。Vibe 钉的应用包协议(rc2)与本契约无关,不受影响。
43
+
44
+ **变更说明(rc9,只加同实例别名):** library 路由允许同一端口的两个精确回环 Host,各自必须使用本 origin 的正常 DSH 认证。目标字段、版本 1、目录解析、明确确认、取消、安装事务与失败回滚不改;不新增依赖、配置或权限。schema 的 `loopback` 节与双方 fixture/suite 覆盖两别名及跨 Origin/未知 Host/port/iframe/CSRF/auth/版本拒绝。范围限 library HTTP 与安装目标导航;应用网关、app/router 控制路由及其他插件不变。现有 127 消费方保持,localhost 消费方保持自己的 URL/auth 参数;Desktop/Vibe 无需本轮改动。
45
+
46
+ **变更说明(rc10,只加安装失败的恢复结果):** `GET /hanamesh/library/events` 里安装操作的 `library.install-failed` 事件新增 `recovery` 对象(schema `$defs/recovery`、`$defs/installFailed`,`x-hanamesh-contract.installRecovery` 节);`code` 仍是原失败码。目标字段、版本 1、目录解析、确认、取消、版本固定、同源/auth/非 iframe、按包 operation 去重都不变;不新增依赖、peer、vendor 或配置。影响:消费方(P06 市场门、Desktop 收尾)按 rc10 读取恢复结果;不读 `recovery` 的消费方保持原行为,但不得把没有 `recovery` 的失败当作「已恢复」。Desktop 无需本轮改动。
47
+
48
+ **握手:**
49
+ - 不带 `contractVersion`:保持 rc7 的 v1 语义。
50
+ - `contractVersion:'1'`:接受,其余语义不变。
51
+ - 其他任何值(含空串):在其他字段检查之前明确拒绝 `CONTRACT_VERSION_UNSUPPORTED`(HTTP 400,`details.supported:['1']`;客户端 reject 同名码并在市场显示)。不解析目录、不安装、不静默降级。新版本调用方的额外字段也按版本不符拒绝,不当作普通字段错误。
52
+ - 调用方声明了版本时应先读提供方声明(`hanamesh.installTarget`),`supported` 不含自己的版本、或没有声明(rc7 及更早)时自己拒绝,不发送。
53
+
54
+ **随包内容:**
55
+
56
+ | 出口 | 内容 |
57
+ | --- | --- |
58
+ | `./install-target/schema.json` | schema 与契约元数据 |
59
+ | `./install-target/suite` | `runProviderSuite()`、`runConsumerSuite(consumer)`、`runChainSuite(consumer)`、`validate()`、`loadContract()` |
60
+ | `./install-target/fixtures/catalog.json` | 测试目录(市场界面标「测试目录(fixture)」) |
61
+ | `./install-target/fixtures/provider-cases.json` | 提供方用例:公开、版本匹配/不符、无效、缺目录,HTTP/客户端/片段三条入口 |
62
+ | `./install-target/fixtures/consumer-cases.json` | 消费方用例:合法目标导航、无效目标拒绝、提供方声明握手 |
63
+ | `./install-target/fixtures/reference-consumer.js` | 消费方 fixture:规则全部从 schema 读取(测试用,不是产品编码 API) |
64
+
65
+ 提供方套件在同一配置同一随机端口通过两个别名驱动本包真实的 HTTP 路由、目录服务与客户端模块(测试目录 + 计数安装器),覆盖 auth/同源/非 iframe/授权拒绝、取消不安装、只有明确确认才经原路由安装一次。链路套件把消费方生成的导航片段交给本包真实的片段入口,确认到达市场确认且不安装,并验证同一导航改报不支持的版本会被明确拒绝。
66
+
67
+ **运行(在安装了本包的消费方项目里):**
68
+
69
+ ```sh
70
+ node node_modules/@hanamesh/dsh-app-host/dist/install-target/run.js provider
71
+ node node_modules/@hanamesh/dsh-app-host/dist/install-target/run.js consumer --module ./my-consumer.mjs --export consumer
72
+ node node_modules/@hanamesh/dsh-app-host/dist/install-target/run.js consumer # 随包消费方 fixture(v1 声明)
73
+ node node_modules/@hanamesh/dsh-app-host/dist/install-target/run.js consumer --unversioned
74
+ ```
75
+
76
+ 消费方对象 `{name, contractVersion?, navigate(workspaceUrl, target) → {ok:true,url}|{ok:false,code}, handshake?(declaration) → {ok,code?}}`;导出也可以是返回该对象的无参工厂。每个套件输出一行 JSON 报告,有失败时退出码 1。
77
+
78
+ ## 安装失败后的恢复(rc10)
79
+
80
+ 一次安装(应用或插件、新装或升级)按以下顺序进行,同一宿主内同一时间只有一个 profile 写者(安装、补偿、卸载、provision 串行;`dsh plugin` 自己按每条命令加 profile 写锁):
81
+
82
+ 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>`。
84
+ 3. **读回**:profile 规格与实际解析版本都等于固定版本才继续,否则 `INSTALL_READBACK_MISMATCH`。
85
+ 4. 应用:读 `app.json` 校验定义 → 随包 lib-provision 供给声明的 runtime。
86
+
87
+ 第 2–4 步任一步失败(含 CLI 失败)都先比对基线:
88
+
89
+ | 情况 | 补偿 | 结果 |
90
+ | --- | --- | --- |
91
+ | 什么都没变(add 前失败、CLI 未写入) | 无 | `not-needed`,逐项读回相等 |
92
+ | 新装 | `dsh plugin remove <包>`(只剩残留模块/锁时 `dsh plugin install`);本次新认领的 runtime 项由 lib-provision 按 ownership 删除 | `restored`,读回等于安装前 |
93
+ | 升级 | `dsh plugin add` 原规格(精确版本带 `--save-exact`);被替换的 runtime 项按恢复后的原定义重新 provision | `restored`,读回等于原精确版本与原 runtime |
94
+ | 补偿命令失败、读回不等、并发写者改过 profile、runtime 留有未认领文件 | 不重试,不手改 profile/store/metadata | `failed`:`error`(补偿自身的错误码)、`residue`(每个不等项的 expected/actual/reason)、`originals`(安装前 `package.json`/`pnpm-lock.yaml`/runtime ledger 保存在 `<dataRoot>/library-recovery/<operationId>/`) |
95
+
96
+ `checks` 逐项列出 dependency、module、bundle、lockfile、bundles 与 `runtime:<item>` 的 expected/actual/ok。别的写者在此期间改动的其他依赖永不撤销,lockfile 因此无法证明已恢复时报 `CONCURRENT_PROFILE_WRITE`。未认领文件与 `<dataRoot>` 下的应用数据从不删除。失败不会自动重试。
97
+
98
+ 市场显示:`安装失败:<原码>(原因)` 后接 `已恢复到安装前状态(已逐项读回)` / `已恢复到原版本 x(已逐项读回)` / `安装前状态未被改动(已逐项读回)`;`failed` 显示 `恢复失败:<码>,未恢复:<项>,安装前原件:<目录>。未安装成功,也未回到安装前状态,需要人工处理`,不会显示为成功或「已回滚」。
99
+
100
+ **已知边界(不是隐藏限制):** 官方 DSH 没有公开「跨多条 `dsh plugin` 命令持有 profile 写锁」或「外部回滚整个 add」的端口,所以宿主外的并发写者只能被检出并如实报告,不能被阻止。runtime 读回以 lib-provision ledger 为准;promote 与 ledger 之间的进程崩溃由 lib-provision `verify` 负责,不在本流程内调用。provision 对无当前平台资产的项返回 `unsupported` 而不抛错,沿用 rc9,不视为失败。
101
+
102
+ **随包验证(SOURCE):** `./install-target/fixtures/install-failure-cases.json` 与 `./install-target/fixtures/source-profile.js`。提供方套件 RB01–RB15 覆盖新装/升级 × add 前/CLI/add 后校验/provision 失败及补偿自身失败、并发写者、未认领文件,RS01/RS02 成功读回,RU01/RU02 用本包真实客户端显示;消费方套件 RP01–RP05 检查消费方的失败呈现(可选方法 `presentInstallFailure(event) → {installed,restored,version?,residue?}`,没有该方法的消费方记 skipped),链路套件 CX-RB04/RB10/RB11 把真实提供方事件交给消费方。官方 CLI 与 lib-provision 在套件里是临时 profile 上的具名替身,结果只代表 SOURCE,不代表真实安装或 REAL_UI 产品门。
103
+
104
+ ## 测试版验证入口(D-180)
105
+
106
+ 测试包装只在自己的验证入口渲染 `LibraryOverlay({verification:true})`,或调用 `market.render({verification:true})`。默认 `verification:false`,正常版本不显示开发表单或合约验证面板;正常导航触发的确认框始终可见。开关不能由网站 Target 或 URL 片段开启。
107
+
108
+ 测试版「安装目标」表单:填写目录 ID/包名→查看→正常四字段确认→确认安装/取消。另一个「C-MARKET 合约验证」面板列出探测、目录、目标、安装、事件、已装列表、应用列表和 open/resume/heartbeat/close,每项可编辑 JSON 样例并查看返回。路径/方法固定,租约只使用当前授权调用的真实回执;无自动安装或打开。选择 install 仍进入同一正常确认框,不能代按确认。错误直接可见,认证/同源/非 iframe 检查不放宽;输出只在当前验证视图显示,不上传、不写公开 URL。
109
+
110
+ ## 本机公开探测与目录消费(D-180)
111
+
112
+ `GET /hanamesh/library/install-target` 无参数,精确回环 Host、非 iframe,允许跨源匿名只读探测。返回 `{client:'dsh',ready,installTarget:{contract:'hanamesh.install-target',contractVersion:'1',supported:['1'],fragmentPrefix:'#hanamesh-install?'},traceId,error?}`;缺安装器 `ready:false` 且 `error.code='INSTALL_TARGET_UNAVAILABLE'`。只这一项带 `Access-Control-Allow-Origin:*`、no-store,不发 Cookie/Allow-Credentials,不返回 profile、已装行、工作区认证地址、命令或租约。独立 DSH 默认报 dsh;没有真实 Desktop 公开证据时不推断 HanaMesh 品牌。其余控制路径仍按原 DSH 认证与准确同源授权执行。
113
+
114
+ C-WEB-CATALOG 第1版的显式 `catalog-source.json?view=hanamesh` 与 manifest 的 `/v1/plugins?view=hanamesh` 可配置为目录来源。旧无 view 源仍严格按 catalog-major-1,不混 schema 或静默降级。新 DTO 保留 id=itemId、可空 npm 身份及公开 sourceLinks;q 是子串查询,目标按精确身份匹配,空页仍有游标则续翻。只给包名时遍历确认唯一目录身份;多个身份返回409 CATALOG_TARGET_AMBIGUOUS。无 npm 包仅浏览;来源链接/分类/目录版本都不能成为启动或安装权限。CATALOG_CHANGED 中止本轮、不拼代或自动重试,其余提供方公开错误保持原 code;故障不退 fixture。
115
+
116
+ 安装事件是当前宿主内存历史。`events.resetRequired` 指游标来自重启前或已丢失的保留段;`library.eventRetention` 为正安全整数,默认512,可显式配置。客户端对不可追溯且不再进行中的操作显示结果未知并刷新已装列表,不从空事件推断取消/成功,不自动重装。终态仍须匹配 operationId;完成后的错包/错版本/缺行读回统一显示 INSTALL_READBACK_MISMATCH。
117
+
118
+ ## 本轮供给与验证边界
119
+
120
+ 本包需要通过 Desktop owning 的正常本地冻结包供应进入统一客户端;当前 Desktop rc13 的 AppHost 固定 rc2,装配尚待 Desktop 卡消费本次包。公开 npm 发布与安装包发布不在本卡范围。本轮源码/HTTP/客户端 hook harness 证据不代替正式 HanaMesh.app 的安装、小界面首步与截图。
121
+
122
+ ## D-214 正常客户端测试包装
123
+
124
+ 生产构建仍为 `pnpm run build`,Loader 调用 `apply(ctx)` 时验证默认关闭;目标 URL 或全局变量不能开启。`apply(ctx,{verification:true})` 仅为可信本方测试包装,沿正常侧栏“市场”/shell.overlay及本方注册的market席位传给既有LibraryOverlay。公开 `market.render({verification:true})` 测试用法保留。
125
+
126
+ 在本卡新的隔离目录运行 `node scripts/pack-verification.mjs --destination <本卡新目录> --version 0.5.7-verification.0`。包装器不覆盖既有目录,不复制node_modules;按冻结锁正常安装,隔离包prepack执行 `node scripts/build.mjs --verification`,再正常pnpm pack。仅该产物的自有Loader工厂包装apply传true,模块id/manifest/正常浏览器路径保留。默认源码树拒绝verification构建覆盖生产dist。
127
+
128
+ 测试版通过公开plugin add正常安装此tgz并加载,在正常侧栏市场或宿主使用的本方market席位进入“C-MARKET 合约验证”,选择11项调用、编辑JSON、明确调用,显示提供方完整公开响应及拒绝JSON。安装调用只解析目标并弹正常确认;取消无POST,确认才发一次正常安装请求。202受理仍不能当已装成功。
129
+
130
+ release与verification产物分别记录版本/来源/sha/许可/bundle及严格消费者证据;测试包验证页不证明正常release包普通应用U2或owner接受。